source: extensions/ColorStat/cstat_colorstat.class.inc.php @ 5961

Last change on this file since 5961 was 5961, checked in by grum, 14 years ago

Add plugin files

File size: 16.6 KB
Line 
1<?php
2/* -----------------------------------------------------------------------------
3  Plugin     : ColorStat
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  * RGB
15    public functions :
16      __construct($R, $G, $B)
17      set($R, $G, $B)
18      setInt($value)
19      get($floatValue=false)
20      getHexString()
21      getInt()
22      getHSV()
23      setHSV(HSV $hsv)
24    private functions :
25      setProperty($property, $value)
26
27  * HSV
28    public functions :
29      __construct($H, $S, $V)
30      set($H, $S, $V)
31      get()
32      getRGB()
33      setRGB(RGB $rgb)
34    private functions :
35      setProperty($property, $value)
36
37  * ColorStat
38    public functions :
39    static getFileColors($fileName, $colorTable, $quality=1)
40    static RGBtoHSV(RGB $RGB)
41    static HSVtoRGB(HSV $HSV)
42    static IntToRGB($rgb)
43    static getColorTable($huePrec=10, $prec=10, $returnedType='HSV')
44    static getColorFromTable(HSV $hsvObject, $colorTable)
45
46  --------------------------------------------------------------------------- */
47
48/**
49 * The RGB class allows to read & write a RGB value
50 */
51class RGB {
52  protected $RGB = Array('R' => 0, 'G' => 0, 'B' => 0);
53
54  public function __construct($R=0, $G=0, $B=0)
55  {
56    $this->set($R, $G, $B);
57  }
58
59  public function set($R, $G, $B)
60  {
61    $this->setProperty('R', $R);
62    $this->setProperty('G', $G);
63    $this->setProperty('B', $B);
64    return($this->get());
65  }
66
67  public function setInt($value)
68  {
69    $tmp=ColorStat::IntToRGB($value);
70    $tmp2=$tmp->get();
71    $this->set($tmp2['R'], $tmp2['G'], $tmp2['B']);
72    unset($tmp2);
73    unset($tmp);
74  }
75
76  public function get($floatValue=false)
77  {
78    if($floatValue)
79    {
80      return(
81        Array(
82          'R' => $this->RGB['R']/255,
83          'G' => $this->RGB['G']/255,
84          'B' => $this->RGB['B']/255,
85        )
86      );
87    }
88    else
89    {
90      return($this->RGB);
91    }
92  }
93
94  public function getHexString()
95  {
96    return(sprintf('%02x%02x%02x', $this->RGB['R'], $this->RGB['G'], $this->RGB['B']));
97  }
98
99  public function getInt()
100  {
101    return(  $this->RGB['R']<<16 + $this->RGB['G']<<8 + $this->RGB['B']);
102  }
103
104  public function getHSV()
105  {
106    return(ColorStat::RGBtoHSV($this));
107  }
108
109  public function setHSV(HSV $hsv)
110  {
111    $tmp=ColorStat::HSVtoRGB($hsv);
112    $tmp2=$tmp->get();
113    $this->set($tmp2['R'], $tmp2['G'], $tmp2['B']);
114    unset($tmp2);
115    unset($tmp);
116  }
117
118  private function setProperty($property, $value)
119  {
120    if($property=='R' or $property=='G' or $property=='B')
121    {
122      if($value<0) $value=0;
123      if($value>255) $value=255;
124
125      $this->RGB[$property]=$value;
126      return($this->RGB[$property]);
127    }
128  }
129}
130
131
132/**
133 * The HSV class allows to read & write a HSV value
134 */
135class HSV {
136  protected $HSV = Array('H' => 0, 'S' => 0, 'V' => 0);
137
138  public function __construct($H=0, $S=0, $V=0)
139  {
140    $this->set($H, $S, $V);
141  }
142
143  public function set($H, $S, $V)
144  {
145    $this->setProperty('H', $H);
146    $this->setProperty('S', $S);
147    $this->setProperty('V', $V);
148    return($this->get());
149  }
150
151  public function get()
152  {
153    return($this->HSV);
154  }
155
156  public function getRGB()
157  {
158    return(ColorStat::HSVtoRGB($this));
159  }
160
161  public function setRGB(RGB $rgb)
162  {
163    $tmp=ColorStat::RGBtoHSV($rgb);
164    $tmp2=$tmp->get();
165    $this->set($tmp2['H'], $tmp2['S'], $tmp2['V']);
166    unset($tmp2);
167    unset($tmp);
168  }
169
170  private function setProperty($property, $value)
171  {
172    if($property=='H')
173    {
174      $this->HSV['H']=$value%360;
175      return($this->HSV['H']);
176    }
177    elseif($property=='S' or $property=='V')
178    {
179      if($value<0) $value=0;
180      if($value>100) $value=100;
181
182      $this->HSV[$property]=$value;
183      return($this->HSV[$property]);
184    }
185    return(false);
186  }
187}
188
189
190
191
192class ColorStat {
193  static public $fileColorsStat = Array(
194    'pixels' => 0,
195    'analyzed' => 0,
196    'time' => 0,
197    'colors' =>0,
198    'fileName' => "",
199    'quality' => 0,
200  );
201
202  /**
203   *
204   * @param String $fileName : the name of picture to scan
205   * @param String $colorTable : the color table model
206   * @param String $options : Array of options
207   *                            'quality' : set the quality for analyze
208   *                            'maxTime' : set the maximum time for analyze
209   *                            'pps'     : pixel per second analyzed
210   *                          if 'maxTime' and 'pps' are greater than zero, the
211   *                          quality parameter is computed automatically
212   * @return Array : an array of HSV objects
213   */
214  static function getFileColors($fileName, $colorTable, $options=array())
215  {
216    $options=self::checkOptions($options);
217
218    self::$fileColorsStat=Array(
219      'pixels'   => 0,
220      'analyzed' => 0,
221      'time' => 0,
222      'pps' =>0,
223    );
224
225    if(file_exists($fileName))
226    {
227      $time=microtime(true);
228
229      if(preg_match('/.*\.gif$/i', $fileName))
230      {
231        $image = imagecreatefromgif($fileName);
232      }
233      elseif(preg_match('/.*\.(jpg|jpeg)$/i', $fileName))
234      {
235        $image = imagecreatefromjpeg($fileName);
236      }
237      elseif(preg_match('/.*\.png$/i', $fileName))
238      {
239        $image = imagecreatefrompng($fileName);
240      }
241      else
242      {
243        return(-2);
244      }
245
246      $imageWidth=imagesx($image);
247      $imageHeight=imagesy($image);
248
249      if($options['pps']>0 && $options['maxTime']>0)
250      {
251        $quality=round(sqrt($imageWidth*$imageHeight/($options['pps']*$options['maxTime'])), 0);
252      }
253      else
254      {
255        $quality=$options['quality'];
256      }
257
258      $imageWorkWidth=round($imageWidth/$quality,0);
259      $imageWorkHeight=round($imageHeight/$quality,0);
260      $imageWork=imagecreatetruecolor($imageWorkWidth,$imageWorkHeight);
261      imagecopyresampled($imageWork, $image, 0, 0, 0, 0, $imageWorkWidth, $imageWorkHeight, $imageWidth, $imageHeight);
262      //imagecopyresized($imageWork, $image, 0, 0, 0, 0, $imageWorkWidth, $imageWorkHeight, $imageWidth, $imageHeight);
263      imagedestroy($image);
264
265      $returned=Array();
266
267      $i=0;
268      for($px=0;$px<$imageWorkWidth;$px++)
269      {
270        for($py=0;$py<$imageWorkHeight;$py++)
271        {
272          $i++;
273          $value=imagecolorat($imageWork, $px, $py);
274
275          $rgb=self::IntToRGB($value);
276          //echo sprintf("%06x", $value)." => ".$rgb->getHexString();
277
278          $color=self::getColorFromTable($rgb->getHSV(), $colorTable)->getHexString();
279          //echo " ($i) $color<br>";
280
281          if(array_key_exists($color, $returned))
282          {
283            $returned[$color]['num']=$returned[$color]['num']+1;
284          }
285          else
286          {
287            $returned[$color]=Array(
288              'hsv' => $rgb->getHSV()->get(),
289              'num' => 1,
290              'pct' => 0,
291            );
292          }
293          unset($rgb);
294        }
295      }
296
297      /*
298      $fName="q".$quality."_c".$options['numColors']."_".$fileName.".png";
299      imagepng($imageWork, $fName);
300      */
301
302      imagedestroy($imageWork);
303      uasort($returned, Array('ColorStat', 'sortTones'));
304
305      if($options['numColors']>0)
306      {
307        foreach($returned as $key=>$val)
308        {
309          $returnedColors[$key]=$val;
310          $options['numColors']--;
311          if($options['numColors']<=0) break;
312        }
313      }
314      else
315      {
316        $returnedColors=$returned;
317      }
318
319      self::$fileColorsStat=Array(
320        'pixels'   => $imageWidth*$imageHeight,
321        'analyzed' => $i,
322        'time'     => microtime(true)-$time,
323        'colors'   => count($returned),
324        'pps'      => $i/(microtime(true)-$time),
325        'quality'  => $quality,
326        //'fileName' => $fName,
327      );
328
329      unset($returned);
330
331      foreach($returnedColors as $key => $val)
332      {
333        $returnedColors[$key]['pct']=round(100*$val['num']/self::$fileColorsStat['analyzed'],2);
334      }
335
336      return($returnedColors);
337    }
338    else
339    {
340      return(-1);
341    }
342  }
343
344  /**
345   *  Calculate the HSV value from a RGB value
346   *
347   * @param RGB $RGB : RGB object
348   * @return HSV : new HSV object
349   */
350  static public function RGBtoHSV(RGB $RGB)
351  {
352    $rgbValues=$RGB->get(true);
353    $max=self::max($rgbValues);
354    $min=self::min($rgbValues);
355
356    if($max['value']==$min['value'])
357    {
358      $H=0;
359    }
360    elseif($max['key']=='R')
361    {
362      $H=(60*($rgbValues['G']-$rgbValues['B'])/($max['value']-$min['value'])+360)%360;
363    }
364    elseif($max['key']=='G')
365    {
366      $H=(60*($rgbValues['B']-$rgbValues['R'])/($max['value']-$min['value'])+120);
367    }
368    elseif($max['key']=='B')
369    {
370      $H=(60*($rgbValues['R']-$rgbValues['G'])/($max['value']-$min['value'])+240);
371    }
372
373    $S=round(100*(($max['value']==0)?0:1-$min['value']/$max['value']),0);
374    $V=round(100*$max['value'],0);
375
376    return(new HSV($H, $S, $V));
377  }
378
379  /**
380   *  Calculate the RGB value from a HSV value
381   *
382   * @param HSV $HSV : HSV object
383   * @return RGB : new RGB object
384   */
385  static public function HSVtoRGB(HSV $HSV)
386  {
387    $hsvValues=$HSV->get();
388
389    $h=abs($hsvValues['H']/60)%6;
390    $f=$hsvValues['H']/60-$h;
391    $l=round(2.55*$hsvValues['V']*(1-$hsvValues['S']/100),0);
392    $m=round(2.55*$hsvValues['V']*(1-$f*$hsvValues['S']/100),0);
393    $n=round(2.55*$hsvValues['V']*(1-(1-$f)*$hsvValues['S']/100),0);
394
395    $v=round(2.55*$hsvValues['V'],0);
396
397    switch($h)
398    {
399      case 0:
400        return(new RGB($v, $n, $l));
401        break;
402      case 1:
403        return(new RGB($m, $v, $l));
404        break;
405      case 2:
406        return(new RGB($l, $v, $n));
407        break;
408      case 3:
409        return(new RGB($l, $m, $v));
410        break;
411      case 4:
412        return(new RGB($n, $l, $v));
413        break;
414      case 5:
415        return(new RGB($v, $l, $m));
416        break;
417    }
418  }
419
420  /**
421   *
422   * @param Int $rgb : an integer &hRRGGBB
423   * @return RGB : a RGB object
424   */
425  static public function IntToRGB($rgb)
426  {
427    return(new RGB(($rgb >> 16) & 0xFF, ($rgb >> 8) & 0xFF, $rgb & 0xFF, true));
428  }
429
430  /**
431   * return a color table
432   *  $table = Array(
433   *              Array(),
434   *              Array(),
435   *              ...
436   *              Array(),
437   *              Array(),
438   *           )
439   *  $table[n]    => Array of all colors for a given hue value
440   *  $table[n][m] => HSV object or a color string RRGGBB
441   *
442   *
443   * @param Int $huePrec : degree of precision for hue [1..360]
444   * @param Float $prec    : precision step for saturation & value [1..100]
445   * @param String $returnedType : 'HSV'   => return a HSV object
446   *                               'color' => return a color string RRGGBB
447   * @return Array : the color table
448   */
449  static public function getColorTable($huePrec=10, $prec=10, $returnedType='HSV')
450  {
451    $returned=Array();
452    for($hue=0;$hue<=360;$hue+=$huePrec)
453    {
454      $hueValues=Array();
455
456      $saturation=100;
457      for($value=0;$value<100;$value=$value+=$prec)
458      {
459        $hsv=new HSV($hue, $saturation, $value);
460        if($returnedType=='HSV')
461        {
462          $hueValues[]=$hsv;
463        }
464        else
465        {
466          $hueValues[]=$hsv->getRGB()->getHexString();
467        }
468        unset($hsv);
469      }
470
471      $value=100;
472      for($saturation=100;$saturation>=0;$saturation-=$prec)
473      {
474        $hsv=new HSV($hue, $saturation, $value);
475        if($returnedType=='HSV')
476        {
477          $hueValues[]=$hsv;
478        }
479        else
480        {
481          $hueValues[]=$hsv->getRGB()->getHexString();
482        }
483        unset($hsv);
484      }
485
486
487      for($tmp=$prec;$tmp<=100;$tmp+=$prec)
488      {
489        $hsv=new HSV($hue, $tmp, 100-$tmp);
490        if($returnedType=='HSV')
491        {
492          $hueValues[]=$hsv;
493        }
494        else
495        {
496          $hueValues[]=$hsv->getRGB()->getHexString();
497        }
498        unset($hsv);
499      }
500      for($tmp=$prec;$tmp<=100;$tmp+=$prec)
501      {
502        $hsv=new HSV($hue, $tmp, $tmp);
503        if($returnedType=='HSV')
504        {
505          $hueValues[]=$hsv;
506        }
507        else
508        {
509          $hueValues[]=$hsv->getRGB()->getHexString();
510        }
511        unset($hsv);
512      }
513
514      $returned[]=$hueValues;
515      unset($hueValues);
516    }
517
518    $hue=0;
519    $saturation=0;
520    $hueValues=Array();
521    //$prec=1/(count($returned[0])-2);
522    for($value=0;$value<=100;$value+=$prec/4)
523    {
524      $hsv=new HSV($hue, $saturation, round($value,0));
525      if($returnedType=='HSV')
526      {
527        $hueValues[]=$hsv;
528      }
529      else
530      {
531        $hueValues[]=$hsv->getRGB()->getHexString();
532      }
533      unset($hsv);
534    }
535
536    $returned[]=$hueValues;
537    unset($hueValues);
538
539    return($returned);
540  }
541
542  /**
543   * @param Array : an array
544   * @return Array : an array, giving the minimum value and the related key
545   */
546  static protected function min($values)
547  {
548    $minKey="";
549    $minValue=0;
550    foreach($values as $key => $val)
551    {
552      if($minKey=='' or $val<$minValue)
553      {
554        $minKey=$key;
555        $minValue=$val;
556      }
557    }
558    return(Array('key' => $minKey, 'value' => $minValue));
559  }
560
561  /**
562   * reverted sort color
563   *
564   */
565  static protected function sortTones($a, $b)
566  {
567    if($a['num'] == $b['num'])
568    {
569      return(0);
570    }
571    return ($a['num'] > $b['num']) ? -1 : 1;
572  }
573
574  /**
575   * @param Array : an array
576   * @return Array : an array, giving the maximum value and the related key
577   */
578  static protected function max($values)
579  {
580    $maxKey="";
581    $maxValue=0;
582    foreach($values as $key => $val)
583    {
584      if($maxKey=='' or $val>$maxValue)
585      {
586        $maxKey=$key;
587        $maxValue=$val;
588      }
589    }
590    return(Array('key' => $maxKey, 'value' => $maxValue));
591  }
592
593  /**
594   * check the validity for getFileColors() options
595   * if no parameters are given, return the default options values
596   *
597   * @param Array $options : an array with given options values
598   * @return Array : an array with valid options values
599   */
600  static protected function checkOptions($options=Array())
601  {
602    if(!is_array($options))
603    {
604      $options=Array();
605    }
606
607    if(!array_key_exists('quality', $options)) $options['quality']=1;
608    if(!array_key_exists('maxTime', $options)) $options['maxTime']=0;
609    if(!array_key_exists('pps', $options)) $options['pps']=0;
610    if(!array_key_exists('numColors', $options)) $options['numColors']=0;
611
612    if($options['quality']<=0) $options['quality']=1;
613    if($options['quality']>20) $options['quality']=20;
614    if($options['maxTime']<0) $options['maxTime']=0;
615    if($options['pps']<0) $options['pps']=0;
616    if($options['numColors']<0) $options['numColors']=0;
617
618    return($options);
619  }
620
621  static public function getColorFromTable(HSV $hsvObject, $colorTable)
622  {
623    $hue=360/(count($colorTable)-2);
624    $step=count($colorTable[0]);
625//echo "*hue:$hue<br>";
626//echo "*step:$step<br>";
627
628    $hsv=$hsvObject->get();
629
630    $hueNumber=round($hsv['H']/$hue,0);
631//echo "*hueNumber:$hueNumber<br>";
632
633//echo "*H:".$hsv['H']."<br>";
634//echo "*S:".$hsv['S']."<br>";
635//echo "*V:".$hsv['V']."<br>";
636
637    if($hsv['V']==0)
638    {
639//echo "-black-<br>";
640      // black
641      return(new RGB(0,0,0));
642    }
643    elseif($hsv['S']==0 && $hsv['V']==100)
644    {
645//echo "-white-<br>";
646      // white
647      return(new RGB(255,255,255));
648    }
649    elseif($hsv['S']==0 && $hsv['V']<100 && $hsv['V']>0)
650    {
651//echo "-grey-<br>";
652      // grey
653      $stepNumber=round($hsv['V']*($step-1)/100,0);
654      return($colorTable[count($colorTable)-1][$stepNumber]->getRGB());
655    }
656    elseif($hsv['S']==100 && $hsv['V']<100 && $hsv['V']>0)
657    {
658//echo "-value-<br>";
659      //color 'value'
660      $stepNumber=round($hsv['V']*$step/400,0);
661      return($colorTable[$hueNumber][$stepNumber]->getRGB());
662    }
663    elseif($hsv['V']==100 && $hsv['S']<100 && $hsv['S']>0)
664    {
665//echo "-saturation-<br>";
666      //color 'saturation'
667      $stepNumber=round(($step/4)+(100-$hsv['S'])*($step/400),0);
668      return($colorTable[$hueNumber][$stepNumber]->getRGB());
669    }
670    elseif($hsv['V']==100 && $hsv['S']==100)
671    {
672//echo "-pure-<br>";
673      //color 'pure'
674      $stepNumber=$step/4;
675      return($colorTable[$hueNumber][$stepNumber]->getRGB());
676    }
677    elseif(
678      ($hsv['V']<100 && $hsv['V']>50 && $hsv['S']<50 && $hsv['S']>0) ||
679      ($hsv['S']<100 && $hsv['S']>50 && $hsv['V']<50 && $hsv['V']>0))
680    {
681//echo "-desaturated 1-<br>";
682      //color 'desaturated'
683      $stepNumber=($step/2)+(100-$hsv['V'])*$step/400;
684      return($colorTable[$hueNumber][$stepNumber]->getRGB());
685    }
686    elseif($hsv['V']<100 && $hsv['V']>0 && $hsv['S']<100 && $hsv['S']>0)
687    {
688//echo "-desaturated 2-<br>";
689      //color 'desaturated'
690      $stepNumber=($step*0.75)+$hsv['V']*$step/400;
691      return($colorTable[$hueNumber][$stepNumber]->getRGB());
692    }
693//echo "-none-<br>";
694    return(new RGB(0,0,0));
695  }
696
697}
698
699
700
701
702
703
704?>
Note: See TracBrowser for help on using the repository browser.