[8509] | 1 | <?php |
---|
| 2 | /* ----------------------------------------------------------------------------- |
---|
| 3 | Plugin : Histogram |
---|
| 4 | Author : Grum |
---|
| 5 | email : grum@grum.fr |
---|
| 6 | website : http://photos.grum.fr |
---|
| 7 | PWG user : http://forum.piwigo.org/profile.php?id=3706 |
---|
| 8 | |
---|
| 9 | << May the Little SpaceFrog be with you ! >> |
---|
| 10 | ------------------------------------------------------------------------------ |
---|
| 11 | See main.inc.php for release information |
---|
| 12 | |
---|
| 13 | Provided classes : |
---|
| 14 | |
---|
| 15 | |
---|
| 16 | --------------------------------------------------------------------------- */ |
---|
| 17 | |
---|
| 18 | if(!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!'); |
---|
| 19 | |
---|
[15346] | 20 | define('MAX_PIXELS_ANALYZED', 1000000); |
---|
[8509] | 21 | |
---|
| 22 | |
---|
| 23 | class Histogram { |
---|
| 24 | static public $fileHistogram = Array( |
---|
| 25 | 'pixels' => 0, |
---|
| 26 | 'analyzed' => 0, |
---|
| 27 | 'time' => 0, |
---|
| 28 | 'fileName' => "", |
---|
| 29 | ); |
---|
| 30 | |
---|
| 31 | /** |
---|
| 32 | * returns histogram values for an image |
---|
| 33 | * |
---|
| 34 | * |
---|
| 35 | * @param String $fileName : the picture's filename |
---|
| 36 | * @return : -1 if file doesn't exist |
---|
| 37 | * -2 if file is not a PNG, a JPEG or a GIF file |
---|
| 38 | * -3 if a fatal error occurs |
---|
| 39 | * Array of L, R, G, and B values (% for values from 0 to 255) |
---|
| 40 | */ |
---|
| 41 | static function read($fileName) |
---|
| 42 | { |
---|
| 43 | self::$fileHistogram=Array( |
---|
| 44 | 'pixels' => 0, |
---|
| 45 | 'analyzed' => 0, |
---|
| 46 | 'time' => 0, |
---|
| 47 | 'pps' =>0, |
---|
| 48 | ); |
---|
| 49 | |
---|
| 50 | $returned=array( |
---|
| 51 | 'L' => array(), |
---|
| 52 | 'A' => array(), |
---|
| 53 | 'M' => array(), |
---|
| 54 | 'R' => array(), |
---|
| 55 | 'G' => array(), |
---|
| 56 | 'B' => array() |
---|
| 57 | ); |
---|
| 58 | |
---|
| 59 | for($i=0;$i<256;$i++) |
---|
| 60 | { |
---|
| 61 | $returned['L'][$i]=0; |
---|
| 62 | $returned['A'][$i]=0; |
---|
| 63 | $returned['M'][$i]=0; |
---|
| 64 | $returned['R'][$i]=0; |
---|
| 65 | $returned['G'][$i]=0; |
---|
| 66 | $returned['B'][$i]=0; |
---|
| 67 | } |
---|
| 68 | |
---|
| 69 | if(file_exists($fileName)) |
---|
| 70 | { |
---|
| 71 | $time=microtime(true); |
---|
| 72 | |
---|
| 73 | try |
---|
| 74 | { |
---|
| 75 | if(preg_match('/.*\.gif$/i', $fileName)) |
---|
| 76 | { |
---|
| 77 | $image = imagecreatefromgif($fileName); |
---|
| 78 | } |
---|
| 79 | elseif(preg_match('/.*\.(jpg|jpeg)$/i', $fileName)) |
---|
| 80 | { |
---|
| 81 | $image = imagecreatefromjpeg($fileName); |
---|
| 82 | } |
---|
| 83 | elseif(preg_match('/.*\.png$/i', $fileName)) |
---|
| 84 | { |
---|
| 85 | $image = imagecreatefrompng($fileName); |
---|
| 86 | } |
---|
| 87 | else |
---|
| 88 | { |
---|
| 89 | return(-2); |
---|
| 90 | } |
---|
| 91 | |
---|
| 92 | $imageWidth=imagesx($image); |
---|
| 93 | $imageHeight=imagesy($image); |
---|
| 94 | |
---|
[15346] | 95 | |
---|
| 96 | $ratio=1; |
---|
| 97 | $nbPixels=$imageWidth*$imageHeight; |
---|
| 98 | if($nbPixels>MAX_PIXELS_ANALYZED) $ratio=$nbPixels/MAX_PIXELS_ANALYZED; |
---|
| 99 | |
---|
| 100 | $imageWorkWidth=round($imageWidth/$ratio,0); |
---|
| 101 | $imageWorkHeight=round($imageHeight/$ratio,0); |
---|
| 102 | $imageWork=imagecreatetruecolor($imageWorkWidth,$imageWorkHeight); |
---|
| 103 | imagecopyresampled($imageWork, $image, 0, 0, 0, 0, $imageWorkWidth, $imageWorkHeight, $imageWidth, $imageHeight); |
---|
| 104 | //imagecopyresized($imageWork, $image, 0, 0, 0, 0, $imageWorkWidth, $imageWorkHeight, $imageWidth, $imageHeight); |
---|
| 105 | imagedestroy($image); |
---|
| 106 | |
---|
[8509] | 107 | $i=0; |
---|
[15346] | 108 | for($px=0;$px<$imageWorkWidth;$px++) |
---|
[8509] | 109 | { |
---|
[15346] | 110 | for($py=0;$py<$imageWorkHeight;$py++) |
---|
[8509] | 111 | { |
---|
| 112 | $i++; |
---|
[15346] | 113 | $value=imagecolorat($imageWork, $px, $py); |
---|
[8509] | 114 | |
---|
| 115 | $rgb=self::IntToRGB($value); |
---|
| 116 | $l=self::RGBtoL($rgb); |
---|
| 117 | $a=self::RGBtoA($rgb); |
---|
| 118 | $m=self::RGBtoM($rgb); |
---|
| 119 | |
---|
| 120 | $returned['L'][$l]++; |
---|
| 121 | $returned['A'][$a]++; |
---|
| 122 | $returned['M'][$m]++; |
---|
| 123 | $returned['R'][$rgb['R']]++; |
---|
| 124 | $returned['G'][$rgb['G']]++; |
---|
| 125 | $returned['B'][$rgb['B']]++; |
---|
| 126 | |
---|
| 127 | unset($rgb); |
---|
| 128 | } |
---|
| 129 | } |
---|
| 130 | |
---|
[15346] | 131 | imagedestroy($imageWork); |
---|
[8509] | 132 | |
---|
| 133 | self::$fileHistogram=Array( |
---|
[15346] | 134 | 'pixels' => $imageWorkWidth*$imageWorkHeight, |
---|
[8509] | 135 | 'analyzed' => $i, |
---|
| 136 | 'time' => microtime(true)-$time, |
---|
| 137 | 'pps' => $i/(microtime(true)-$time), |
---|
| 138 | ); |
---|
| 139 | |
---|
| 140 | ksort($returned['L']); |
---|
| 141 | ksort($returned['A']); |
---|
| 142 | ksort($returned['M']); |
---|
| 143 | ksort($returned['R']); |
---|
| 144 | ksort($returned['G']); |
---|
| 145 | ksort($returned['B']); |
---|
| 146 | |
---|
| 147 | foreach($returned as $key => $val) |
---|
| 148 | { |
---|
| 149 | $m=max($returned[$key]); |
---|
| 150 | foreach($returned[$key] as $key2 => $val2) |
---|
| 151 | { |
---|
| 152 | $returned[$key][$key2]=round($val2/$m,4); |
---|
| 153 | } |
---|
| 154 | } |
---|
| 155 | |
---|
| 156 | return($returned); |
---|
| 157 | } |
---|
| 158 | catch (Exception $e) |
---|
| 159 | { |
---|
| 160 | //echo "ERROR!<br>".print_r($e, true); |
---|
| 161 | return(-3); |
---|
| 162 | } |
---|
| 163 | } |
---|
| 164 | else |
---|
| 165 | { |
---|
| 166 | return(-1); |
---|
| 167 | } |
---|
| 168 | } |
---|
| 169 | |
---|
| 170 | |
---|
| 171 | /** |
---|
| 172 | * build the histogram and save it in an image file |
---|
| 173 | * |
---|
| 174 | * @param String $fileName : the histogram filename |
---|
| 175 | * @param Array $values : array('L', 'R', 'G', 'B') of values |
---|
| 176 | * @param Array $options : array of options for histogram rendering |
---|
| 177 | * 'color_bg' : background color |
---|
| 178 | * 'color_l' : color for luminance drawing |
---|
| 179 | * 'color_r' : color for red drawing |
---|
| 180 | * 'color_g' : color for green drawing |
---|
| 181 | * 'color_b' : color for blue drawing |
---|
| 182 | * 'color_ticks' : color for ticks drawing |
---|
| 183 | * 'mode_l' : drawing mode for luminance |
---|
| 184 | * 'mode_r' : drawing mode for luminance |
---|
| 185 | * 'mode_g' : drawing mode for luminance |
---|
| 186 | * 'mode_b' : drawing mode for luminance |
---|
| 187 | * => modes can takes 'line' or 'bar' or 'surface' or 'none' values |
---|
| 188 | * 'mode_ticks_h' : drawing mode for horizontal ticks |
---|
| 189 | * 'mode_ticks_v' : drawing mode for vertical ticks |
---|
| 190 | * => can takes 'none' or 'dot' or 'solid' |
---|
| 191 | * 'histo_width' : histogram width |
---|
| 192 | * 'histo_height' : histogram height |
---|
| 193 | */ |
---|
| 194 | static function build($fileName, $values, $options=array()) |
---|
| 195 | { |
---|
| 196 | $options=self::checkHistoOptions($options); |
---|
| 197 | |
---|
| 198 | $image=imagecreatetruecolor($options['histo_width'], $options['histo_height']); |
---|
| 199 | $options['color_bg']=self::colorAllocate($image, $options['color_bg']); |
---|
[8511] | 200 | $options['color_v']=self::colorAllocate($image, $options['color_v']); |
---|
[8509] | 201 | $options['color_r']=self::colorAllocate($image, $options['color_r']); |
---|
| 202 | $options['color_g']=self::colorAllocate($image, $options['color_g']); |
---|
| 203 | $options['color_b']=self::colorAllocate($image, $options['color_b']); |
---|
| 204 | |
---|
| 205 | imagefilledrectangle($image,0,0,$options['histo_width'], $options['histo_height'], $options['color_bg']); |
---|
| 206 | |
---|
| 207 | |
---|
[8511] | 208 | $histoList=array('v', 'r', 'g', 'b'); |
---|
[8509] | 209 | |
---|
| 210 | |
---|
| 211 | foreach($histoList as $histo) |
---|
| 212 | { |
---|
| 213 | switch($options['mode_'.$histo]) |
---|
| 214 | { |
---|
| 215 | case 'line': |
---|
| 216 | case 'surface': |
---|
| 217 | $points=array(0,$options['histo_height']); |
---|
[8511] | 218 | if($histo=='v') |
---|
[8509] | 219 | { |
---|
| 220 | $histoKey=$options['histo_method']; |
---|
| 221 | } |
---|
| 222 | else |
---|
| 223 | { |
---|
| 224 | $histoKey=$histo; |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | foreach($values[strtoupper($histoKey)] as $key => $histoValue) |
---|
| 228 | { |
---|
| 229 | $points[]=$key * $options['histo_width']/255; |
---|
| 230 | $points[]=$options['histo_height'] - $histoValue * $options['histo_height']; |
---|
| 231 | } |
---|
| 232 | $points[]=$options['histo_width']; |
---|
| 233 | $points[]=$options['histo_height']; |
---|
| 234 | |
---|
| 235 | if($options['mode_'.$histo]=='line') |
---|
| 236 | { |
---|
| 237 | imagepolygon($image,$points, count($points)/2, $options['color_'.$histo]); |
---|
| 238 | } |
---|
| 239 | else |
---|
| 240 | { |
---|
| 241 | imagefilledpolygon($image,$points, count($points)/2, $options['color_'.$histo]); |
---|
| 242 | } |
---|
| 243 | break; |
---|
| 244 | case 'bar': |
---|
| 245 | break; |
---|
| 246 | } |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | imagepng($image, $fileName); |
---|
| 250 | imagedestroy($image); |
---|
| 251 | } |
---|
| 252 | |
---|
| 253 | |
---|
| 254 | /** |
---|
| 255 | * Calculate the Luminance value from a RGB value |
---|
| 256 | * |
---|
| 257 | * @param Array $RGB : RGB object |
---|
| 258 | * @return Integer : luminance color |
---|
| 259 | */ |
---|
| 260 | static public function RGBtoL($RGB) |
---|
| 261 | { |
---|
| 262 | return(round($RGB['R']*0.299+$RGB['G']*0.587+$RGB['B']*0.114,0)); |
---|
| 263 | } |
---|
| 264 | |
---|
| 265 | /** |
---|
| 266 | * Calculate the average grey value from a RGB value |
---|
| 267 | * |
---|
| 268 | * @param Array $RGB : RGB object |
---|
| 269 | * @return Integer : average grey color |
---|
| 270 | */ |
---|
| 271 | static public function RGBtoA($RGB) |
---|
| 272 | { |
---|
| 273 | return(round(($RGB['R']+$RGB['G']+$RGB['B'])/3,0)); |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | |
---|
| 277 | /** |
---|
| 278 | * Calculate the max value from a RGB value |
---|
| 279 | * |
---|
| 280 | * @param Array $RGB : RGB object |
---|
| 281 | * @return Integer : the max value |
---|
| 282 | */ |
---|
| 283 | static public function RGBtoM($RGB) |
---|
| 284 | { |
---|
| 285 | return(max($RGB)); |
---|
| 286 | } |
---|
| 287 | |
---|
| 288 | |
---|
| 289 | /** |
---|
| 290 | * |
---|
| 291 | * @param Int $rgb : an integer &hRRGGBB |
---|
| 292 | * @return RGB : a RGB object |
---|
| 293 | */ |
---|
| 294 | static public function IntToRGB($rgb) |
---|
| 295 | { |
---|
| 296 | return( |
---|
| 297 | Array( |
---|
| 298 | 'R' => ($rgb >> 16) & 0xFF, |
---|
| 299 | 'G' => ($rgb >> 8) & 0xFF, |
---|
| 300 | 'B' => $rgb & 0xFF |
---|
| 301 | ) |
---|
| 302 | ); |
---|
| 303 | } |
---|
| 304 | |
---|
| 305 | /** |
---|
| 306 | * convert a string like #rrggbb into a RGB array |
---|
| 307 | * |
---|
| 308 | * @param String $rgb : a #rrggbb string |
---|
| 309 | * @return Array |
---|
| 310 | */ |
---|
| 311 | static public function StringToRGB($rgb) |
---|
| 312 | { |
---|
| 313 | $returned=array( |
---|
| 314 | 'R' => 0, |
---|
| 315 | 'G' => 0, |
---|
| 316 | 'B' => 0 |
---|
| 317 | ); |
---|
| 318 | |
---|
| 319 | if(preg_match_all('/^#([A-F0-9]{2})([A-F0-9]{2})([A-F0-9]{2})$/i', $rgb, $values, PREG_SET_ORDER)>0) |
---|
| 320 | { |
---|
| 321 | $returned['R']=intval('0x'.$values[0][1],0); |
---|
| 322 | $returned['G']=intval('0x'.$values[0][2],0); |
---|
| 323 | $returned['B']=intval('0x'.$values[0][3],0); |
---|
| 324 | } |
---|
| 325 | |
---|
| 326 | return($returned); |
---|
| 327 | } |
---|
| 328 | |
---|
| 329 | /** |
---|
| 330 | * convert a RGB array ('R'=>r, 'G'=>g, 'B'=>b ) into integer value |
---|
| 331 | * |
---|
| 332 | * @param Array $rgb : rgb array |
---|
| 333 | * @return Integer |
---|
| 334 | */ |
---|
| 335 | static public function RGBToInt($rgb) |
---|
| 336 | { |
---|
| 337 | return($rgb['R']*65536 + $rgb['G']*256 + $rgb['B']); |
---|
| 338 | } |
---|
| 339 | |
---|
| 340 | /** |
---|
| 341 | * convert a RGB array ('R'=>r, 'G'=>g, 'B'=>b ) into string #rrggbb value |
---|
| 342 | * |
---|
| 343 | * @param Array $rgb : rgb array |
---|
| 344 | * @return String |
---|
| 345 | */ |
---|
| 346 | static public function RGBToString($rgb) |
---|
| 347 | { |
---|
| 348 | return( |
---|
| 349 | sprintf('#%02X%02X%02X', $rgb['R'], $rgb['G'], $rgb['B']) |
---|
| 350 | ); |
---|
| 351 | } |
---|
| 352 | |
---|
| 353 | static public function checkHistoOptions($options) |
---|
| 354 | { |
---|
| 355 | if(!isset($options['color_bg']) or !is_numeric($options['color_bg'])) $options['color_bg']=0xffffff; |
---|
[8511] | 356 | if(!isset($options['color_v']) or !is_numeric($options['color_v'])) $options['color_v']=0x808080; |
---|
[8509] | 357 | if(!isset($options['color_r']) or !is_numeric($options['color_r'])) $options['color_r']=0xA00000; |
---|
| 358 | if(!isset($options['color_g']) or !is_numeric($options['color_g'])) $options['color_g']=0x00A000; |
---|
| 359 | if(!isset($options['color_b']) or !is_numeric($options['color_b'])) $options['color_b']=0x0000A0; |
---|
| 360 | |
---|
| 361 | $modes=array('surface', 'line', 'bar', 'none'); |
---|
[8511] | 362 | if(!isset($options['mode_v']) or !in_array($options['mode_v'], $modes)) $options['mode_v']='surface'; |
---|
| 363 | if(!isset($options['mode_r']) or !in_array($options['mode_r'], $modes)) $options['mode_r']='line'; |
---|
| 364 | if(!isset($options['mode_g']) or !in_array($options['mode_g'], $modes)) $options['mode_g']='line'; |
---|
| 365 | if(!isset($options['mode_b']) or !in_array($options['mode_b'], $modes)) $options['mode_b']='line'; |
---|
[8509] | 366 | |
---|
| 367 | if(!isset($options['histo_width']) or !is_numeric($options['histo_width']) or $options['histo_width']<=0) $options['histo_width']=256; |
---|
| 368 | if(!isset($options['histo_height']) or !is_numeric($options['histo_height']) or $options['histo_height']<=0) $options['histo_height']=100; |
---|
| 369 | if(!isset($options['histo_method']) or !in_array($options['histo_method'], array('L', 'A', 'M'))) $options['histo_method']='L'; |
---|
| 370 | |
---|
| 371 | return($options); |
---|
| 372 | } |
---|
| 373 | |
---|
| 374 | |
---|
| 375 | static public function colorAllocate($im, $color) |
---|
| 376 | { |
---|
| 377 | $rgb=self::IntToRGB($color); |
---|
| 378 | return(imagecolorallocate($im, $rgb['R'], $rgb['G'], $rgb['B'])); |
---|
| 379 | } |
---|
| 380 | |
---|
| 381 | static public function colorAllocateA($im, $color, $alpha) |
---|
| 382 | { |
---|
| 383 | $rgb=self::IntToRGB($color); |
---|
| 384 | return(imagecolorallocatealpha($im, $rgb['R'], $rgb['G'], $rgb['B'], $alpha)); |
---|
| 385 | } |
---|
| 386 | |
---|
| 387 | |
---|
| 388 | } |
---|
| 389 | |
---|
| 390 | |
---|
| 391 | ?> |
---|