Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Analiza zdjęcia pod wzgledem kolorystyki
Forum PHP.pl > Forum > PHP
mhs
Witam,

chciałbym przygotować mechanizm, który automatycznie poddajcie analizie określone zdjęcia i następnie generuje dla niego 5-6 podstawowych kolorów które najtrafniej określają kolorystykę dane zdjęcia.

Coś podobne załóżmy jak tutaj (http://freszki.pl) np http://freszki.pl/freszka/548,videoegg czy http://www.dreamstime.com/royalty-free-sto...rs-image9781689 (dominant colors) czyli dla zdjęcia określone są podstawowe kolory i na bazie przypisanych tych wszystkich kolorów możliwe jest przygotowanie wyszukiwarki która znajdzie określone zdjęcia w podobnej kolorystyce.

Spotkał się ktoś z Was z takimi mechanizmami (jakimiś gotowymi bibliotekami) i ma pomysł jak "szybko" i sprawnie coś takiego przygotować?

Pozdrawiam, mhs.

kiler129
Ja właśnie walczę z identycznym zagadnieniem smile.gif
Kolor można określać na 2 sposoby - albo jako RGB (lub CMYK) albo jako wartość jasności, odcienia i nasycenia.
Myślę, że najskuteczniej będzie zbadać przy dodawaniu odcień i nasycenie; przy wybieraniu zakres +/- 30 dla odcienia (wartości możliwe od -180 do +180) oraz doświadczalnie jasność.
Wybieranie tego po RGB czy CMYK jest strasznie karkołomne - tam trudno zobaczyć które kolory są podobne.

Jeśli chcesz się pobawić i zobaczyć jak te wartości wpływają na kolor ściągnij GIMP`a (http://gimp.org), zrób nowy obrazek i kliknij w górnej belce Color > Hue-saturation a następnie baw sie suwakami.

Jak implementować to w php? Na razie nie wiem smile.gif
Mephistofeles
Określić "odległość" koloru od środka zakresu kolorów, a potem posortować i wziąć najmniejszą?
kiler129
Cytat(Mephistofeles @ 13.12.2010, 09:57:44 ) *
Określić "odległość" koloru od środka zakresu kolorów, a potem posortować i wziąć najmniejszą?

Moglbys dokladniej opisac metode znalezienia podobnych?

Zauwaz ze biorac odleglosc od srodka uwzgledniasz chyba tylko zmiane jasnosci ale nie juz odcienia wiec rozowy tym sposobem nie bedzie podobny do czerwonego, hmmmmm


EDIT:
Napisałem taką klasę:

  1. <?php
  2. class avgColor {
  3. var $pixProbX = 10; //Eg. if img is 120x120 script probe for pixel #0, 10, 20 etc
  4. var $pixProbY = 10; //See above
  5. private $imgRes; //Loaded GD2 resource image
  6. private $imgResY; //Img height [axis Y]
  7. private $imgResX; //Img width [axis X]
  8. private $colorMap; //Table of color in image
  9. private $totalProbPix; //Total probbed pixels - used for % calculation
  10.  
  11. function __construct($imgPath) {
  12. if(!is_file($imgPath) || !is_readable($imgPath)) {
  13. trigger_error("Cannot open $imgPath", E_USER_ERROR);
  14. return false;
  15. }
  16.  
  17. $imgInfo = getimagesize($imgPath);
  18. $this->imgResY = $imgInfo[1];
  19. $this->imgResX = $imgInfo[0];
  20.  
  21. switch($imgInfo["mime"]) {
  22. case "image/jpeg": $this->imgRes = imagecreatefromjpeg($imgPath); break;
  23. case "image/png": $this->imgRes = imagecreatefrompng($imgPath); break;
  24. case "image/gif": $this->imgRes = imagecreatefromgif($imgPath); break;
  25. case "image/jpeg": $this->imgRes = imagecreatefromjpeg($imgPath); break;
  26. }
  27. }
  28.  
  29. function trcArr(&$arr, $elements=10) {
  30. $i=1;
  31. foreach($arr as $key=>$val) {
  32. if($i<=$elements) $ret[$key] = $val;
  33. $i++;
  34. }
  35. $arr=$ret;
  36. }
  37.  
  38. function mapColors($el=10) { //Make colors map in image with counter.
  39. if($this->imgResY < $this->pixProbY) $this->pixProbY = $this->imgResY; //If Y axis is smaller than defined probing use maximum value
  40. if($this->imgResX < $this->pixProbX) $this->pixProbX = $this->imgResX; //Same as above for X axis
  41.  
  42. $loopsY = (int)($this->imgResY/$this->pixProbY); //Number of samples, cutted to int, Y axis
  43. $loopsX = (int)($this->imgResX/$this->pixProbX); //Same as above for X axis
  44. if($loopsY==0 || $loopsX==0) { trigger_error("Cannot make colormap for $imgPath - loops for X/Y axis == 0", E_USER_ERROR); return; } //Check psblt.
  45. $this->totalProbPix = $loopsY+$loopsX;
  46.  
  47. //echo "Y=".$this->imgResY." | X=".$this->imgResX." | prbY=".$this->pixProbY." | prbX=".$this->pixProbX."\n\n";
  48. //if($loopsX > $loopsY) { //If image contain more "rows" than "columns" typed to parsing parse by columns
  49.  
  50. for($y=0;$y<$loopsY;$y++) { //Loop for columns
  51. for($x=0;$x<$loopsX;$x++) { //Loop for pixels in column
  52. $rgb = imagecolorat($this->imgRes, ($x*$this->pixProbX), ($y*$this->pixProbY));
  53. @$this->colorMap[(($rgb >> 16) & 0xFF).".".(($rgb >> 8) & 0xFF).".".($rgb & 0xFF)]++;
  54. }
  55. }
  56. //} else { //...or if image contain more "cols" than "rows" typed to parsing parse row-by-row and then probe every pixel in row
  57.  
  58. //}
  59.  
  60. arsort($this->colorMap); //Sort arr
  61. $this->trcArr($this->colorMap, $el); //Strip array
  62. return $this->colorMap;
  63. }
  64.  
  65. function mostUsedColor(&$map) {
  66. reset($map);
  67. $muc = each($map);
  68. $muc["key"] = explode(".", $muc["key"]);
  69. $ret["color"] = dechex($muc["key"][0]).dechex($muc["key"][1]).dechex($muc["key"][2]); //Make hex notation of color
  70. $ret["percent"] = round((($muc["value"]/$this->totalProbPix)*100),2); //Calculate percen value
  71.  
  72. return $ret;
  73. }
  74. }
  75. ?>


Przykład użycia:
  1. <?php
  2. ini_set('display_errors', '1');
  3.  
  4. require_once("sim.class.php");
  5. $_sim = new avgColor("imgs/647636.jpg");
  6. var_dump($_sim->mostUsedColor($_sim->mapColors()));
  7. ?>


Klasa ładnie wygciąga najczęściej występujący kolor ale dla tapety 1280x1024 jest dość wolna gdy analizuje się każdy pixel ($pixProbX oraz $pixProbY na 1), jeśli natomiast zmniejszy się dokładność o 10x to działa szybko no ale.... smile.gif
Ma ktoś inny pomysł? Dodatkowo pozostaje kwestia porównania.

EDIT2:
Wracam z nowym kodem smile.gif Dodałem metodę avgColor która jest o 2-2.5x szybsza ale ciągle dla 4k x 3k trwa to blisko 20sekund smile.gif

  1. <?php
  2. class avgColor {
  3. var $pixProbX = 1; //Eg. if img is 120x120 script probe for pixel #0, 10, 20 etc
  4. var $pixProbY = 1; //See above
  5. private $imgRes; //Loaded GD2 resource image
  6. private $imgResY; //Img height [axis Y]
  7. private $imgResX; //Img width [axis X]
  8. private $colorMap; //Table of color in image
  9. private $totalProbPix; //Total probbed pixels - used for % calculation | value for most used color
  10. private $totalPixAvg; //Similar to above but for avg color calculation
  11.  
  12. function __construct($imgPath) {
  13. if(!is_file($imgPath) || !is_readable($imgPath)) {
  14. trigger_error("Cannot open $imgPath", E_USER_ERROR);
  15. return false;
  16. }
  17.  
  18. $imgInfo = getimagesize($imgPath);
  19. $this->imgResY = $imgInfo[1];
  20. $this->imgResX = $imgInfo[0];
  21.  
  22. switch($imgInfo["mime"]) {
  23. case "image/jpeg": $this->imgRes = imagecreatefromjpeg($imgPath); break;
  24. case "image/png": $this->imgRes = imagecreatefrompng($imgPath); break;
  25. case "image/gif": $this->imgRes = imagecreatefromgif($imgPath); break;
  26. case "image/jpeg": $this->imgRes = imagecreatefromjpeg($imgPath); break;
  27. }
  28. }
  29.  
  30. function trcArr(&$arr, $elements=10) {
  31. $i=1;
  32. foreach($arr as $key=>$val) {
  33. if($i<=$elements) $ret[$key] = $val;
  34. $i++;
  35. }
  36. $arr=$ret;
  37. }
  38.  
  39. private function mapColors($el=10) { //Make colors map in image with counter.
  40. if($this->imgResY < $this->pixProbY) $this->pixProbY = $this->imgResY; //If Y axis is smaller than defined probing use maximum value
  41. if($this->imgResX < $this->pixProbX) $this->pixProbX = $this->imgResX; //Same as above for X axis
  42.  
  43. $loopsY = (int)($this->imgResY/$this->pixProbY); //Number of samples, cutted to int, Y axis
  44. $loopsX = (int)($this->imgResX/$this->pixProbX); //Same as above for X axis
  45. if($loopsY==0 || $loopsX==0) { trigger_error("Cannot make colormap for $imgPath - loops for X/Y axis == 0", E_USER_ERROR); return; } //Check psblt.
  46. $this->totalProbPix = $loopsY+$loopsX;
  47.  
  48. //echo "Y=".$this->imgResY." | X=".$this->imgResX." | prbY=".$this->pixProbY." | prbX=".$this->pixProbX."\n\n";
  49. //if($loopsX > $loopsY) { //If image contain more "rows" than "columns" typed to parsing parse by columns
  50.  
  51. for($y=0;$y<$loopsY;$y++) { //Loop for columns
  52. for($x=0;$x<$loopsX;$x++) { //Loop for pixels in column
  53. $rgb = imagecolorat($this->imgRes, ($x*$this->pixProbX), ($y*$this->pixProbY));
  54. @$this->colorMap[(($rgb >> 16) & 0xFF).".".(($rgb >> 8) & 0xFF).".".($rgb & 0xFF)]++;
  55. }
  56. }
  57. //} else { //...or if image contain more "cols" than "rows" typed to parsing parse row-by-row and then probe every pixel in row
  58.  
  59. //}
  60.  
  61. arsort($this->colorMap); //Sort arr
  62. $this->trcArr($this->colorMap, $el); //Strip array
  63. return $this->colorMap;
  64. }
  65.  
  66. function mostUsedColor() {
  67. $this->mapColors(1); //Make color map
  68. reset($this->colorMap);
  69. $muc = each($this->colorMap);
  70. $muc["key"] = explode(".", $muc["key"]);
  71. $ret["color"] = dechex($muc["key"][0]).dechex($muc["key"][1]).dechex($muc["key"][2]); //Make hex notation of color
  72. $ret["percent"] = round((($muc["value"]/$this->totalProbPix)*100),2); //Calculate percen value
  73.  
  74. return $ret;
  75. }
  76.  
  77. function avgColor() { //Analyze every pixel and return avg color
  78. $this->totalPixAvg=0;
  79. $color=array("r"=>0,"g"=>0,"b"=>0,"rgb"=>"fff");
  80. for ($x=0;$x<imagesx($this->imgRes);$x++) {
  81. for ($y=0;$y<imagesy($this->imgRes);$y++) {
  82. $rgb = imagecolorat($this->imgRes, $x, $y);
  83. $color["r"] += (($rgb >> 16) & 0xFF);
  84. $color["g"] += (($rgb >> 8) & 0xFF);
  85. $color["b"] += $rgb & 0xFF;
  86. $this->totalPixAvg++;
  87. }
  88. }
  89.  
  90.  
  91. $color["r"] = round($color["r"]/$this->totalPixAvg);
  92. $color["g"] = round($color["g"]/$this->totalPixAvg);
  93. $color["b"] = round($color["b"]/$this->totalPixAvg);
  94. $color["rgb"] = dechex($color["r"]).dechex($color["g"]).dechex($color["b"]);
  95.  
  96. return $color;
  97. }
  98.  
  99. }
  100. ?>


  1. <?php
  2. /********************************************************************************
    */
  3. function _st() {
  4. $mtime = explode(" ", microtime());
  5. return $mtime[1] + $mtime[0];
  6. }
  7.  
  8. function _et($_st) {
  9. $mtime = explode(" ",microtime());
  10. $execT = (($mtime[1] + $mtime[0]) - $_st);
  11. echo "\n\n".str_repeat("-", 40)."\nExec time: ".(($execT<1)?($execT*1000)."ms":$execT."s")."\n\n";
  12. }
  13. /********************************************************************************
    */
  14.  
  15. ini_set('display_errors', '1');
  16.  
  17. require_once("sim.class.php");
  18. $_sim = new avgColor("imgs/647596.jpg");
  19.  
  20. $_st=_st();
  21. //var_dump($_sim->mostUsedColor());
  22. var_dump($_sim->avgColor());
  23. _et($_st);
  24. ?>
Quadina
Ja bym mimo wszystko liczył dla RGB, najpierw policzył wszystkie pixele, następnie zaniedbał 10% podobnych, posortował i wydrukował wynik. Jasność wydaje mi się tutaj trochę uszkadzaniem takiego algorytmu np. dla zdjęć szarych. Operując na RGB i procentach w odchyleniu standardowym zawsze będzie działać - przynajmniej w teorii. Zagadnienie na tyle Ciekawe, że chyba spróbuje sam coś takiego napisać i zamieszczę oczywiście rozwiązanie tutaj. Pomysł jest jeszcze jeden. Przeskalować obrazek na 10-30 razy mniejszy i dla takiego sprawdzić bez zaniedbywania. Możliwości spajania kolorów przez funkcje zmniejszające obrazki wydają mi się na tyle dobre, żeby można było je wykorzystywać również do takich celów.
kiler129
Cytat(Quadina @ 14.12.2010, 10:01:36 ) *
. Pomysł jest jeszcze jeden. Przeskalować obrazek na 10-30 razy mniejszy i dla takiego sprawdzić bez zaniedbywania. Możliwości spajania kolorów przez funkcje zmniejszające obrazki wydają mi się na tyle dobre, żeby można było je wykorzystywać również do takich celów.

Też o tym słyszałem tyle i myślałem lecz w kontekście zmniejszenia do 1x1px. Jest jednak jeden problem - różne subwersje GD generują różne wyniki dla takiej operacji przez co można sobie zrobić niezły bałagan po aktualizacji php worriedsmiley.gif

Co do rozwiązania to gdyby moją klasę zmodyfikować tak aby najpierw skalowała np. do 200x200px [gdzie obrazek wtedy jest już analizowany szybko] można zastosować metodę mostUsedColor(10) a następnie wyciągać średnią z tych 10 najczęściej użytych.
Wtedy będzie to chyba najsensowniejsze.
Quadina
Pierwsza wersja mojego pomysłu z zmniejszaniem zdjęć i zaniedbywaniem kolorów poniżej. Dużo się można jeszcze przyczepić, w każdym razie obrazek 2048x1536 przerobiła w niecałe 5 sekund.

  1. <?php
  2. class histogram{
  3. private $imgRes; //Loaded GD2 resource image
  4. private $imgResY; //Img height [axis Y]
  5. private $imgResX; //Img width [axis X]
  6. private $colorMap; //Table of color in image
  7. private $identMap; //Table of color in image
  8. function __construct($imgPath) {
  9. if(!is_file($imgPath) || !is_readable($imgPath)) {
  10. trigger_error("Cannot open $imgPath", E_USER_ERROR);
  11. return false;
  12. }
  13. $imgInfo = getimagesize($imgPath);
  14. $this->imgResY = $imgInfo[1];
  15. $this->imgResX = $imgInfo[0];
  16.  
  17. switch($imgInfo["mime"]) {
  18. case "image/jpeg": $this->imgRes = imagecreatefromjpeg($imgPath); break;
  19. case "image/png": $this->imgRes = imagecreatefrompng($imgPath); break;
  20. case "image/gif": $this->imgRes = imagecreatefromgif($imgPath); break;
  21. case "image/jpeg": $this->imgRes = imagecreatefromjpeg($imgPath); break;
  22. }
  23. }
  24. function normalize($tx=400,$ty=400){
  25. if($tx>$this->imgResX && $ty>$this->imgResY){
  26. return false;
  27. }
  28. if($tx>$this->imgResX) $tx=$this->imgResX;
  29. if($ty>$this->imgResY) $ty=$this->imgResY;
  30. $thumb = imagecreatetruecolor($tx, $ty);
  31. imagecopyresized($thumb, $this->imgRes, 0, 0, 0, 0, $tx, $ty, $this->imgResX, $this->imgResY);
  32. $this->imgRes = $thumb;
  33. $this->imgResX = $tx;
  34. $this->imgResY = $ty;
  35. return true;
  36. }
  37. function prepare($p=20){
  38. for($x=0;$x<$this->imgResX;$x++){
  39. for($y=0;$y<$this->imgResY;$y++){
  40. $rgb = imagecolorat($this->imgRes, $x, $y);
  41. $r = (($rgb >> 16) & 0xFF);
  42. $g = (($rgb >> 8) & 0xFF);
  43. $b = ($rgb & 0xFF);
  44. $ident = floor($r/$p).floor($g/$p).floor($b/$p);
  45. @$this->colorMap[$ident]++;
  46. $this->identMap[$ident] = $r.'.'.$g.'.'.$b;
  47. }
  48. }
  49. arsort($this->colorMap); //Sort arr
  50. $this->trcArr($this->colorMap, $p); //Strip array
  51. return $this->colorMap;
  52. }
  53. function trcArr(&$arr, $elements=10) {
  54. $i=1;
  55. foreach($arr as $key=>$val) {
  56. if($i<=$elements) $ret[$key] = $val;
  57. $i++;
  58. }
  59. $arr=$ret;
  60. }
  61. function mostUsedColor() {
  62. $sum = $this->imgResX * $this->imgResY;
  63. $tmp = each($this->colorMap);
  64. return array(
  65. 'color' => $this->identMap[ $tmp['key'] ],
  66. 'percent' => floor($tmp['value']/$sum*100),
  67. );
  68. }
  69. }
  70. $t1 = microtime(1);
  71. $i = new histogram('avatar.png'); // 100x100
  72. $i->normalize();
  73. $i->prepare();
  74. $t = $i->mostUsedColor();
  75. echo $t['color'].' => '.$t['percent']."\t\t". (microtime(1)-$t1)."ms\n";
  76.  
  77. $t1 = microtime(1);
  78. $i = new histogram('samochod.jpg'); // 800x592
  79. $i->normalize();
  80. $i->prepare();
  81. $t = $i->mostUsedColor();
  82. echo $t['color'].' => '.$t['percent']."\t\t". (microtime(1)-$t1)."ms\n";
  83.  
  84. $i = new histogram('IMAG0241.jpg'); // 2048x1536
  85. $i->normalize();
  86. $i->prepare();
  87. $t = $i->mostUsedColor();
  88. echo $t['color'].' => '.$t['percent']."\t\t". (microtime(1)-$t1)."ms\n";


Dostaje w zamian trochę większy czas obliczen dla małych zdjęć, ale krótszy dla większych zdjęć - porównywanie wykonałem testując twoją klasę moim testem na tych samych 3 obrazkach.
  1. // Twoja wersja, ale chyba cos procenty ci sie zle licza
  2. 383838 => 955.5 0.092159986495972ms
  3. fdfdfd => 14303.02 4.5690710544586ms
  4. 444 => 3460.27 30.853181123734ms
  5.  
  6. // moja wersja
  7. 383838 => 24 0.12960004806519ms
  8. fdfdfd => 57 2.2384610176086ms
  9. 1170 => 21 4.648444890976ms


EDIT: zauważylem, że mamy różne kolory dla największych ostatnich obrazków - zaraz sprawdzę dlaczego tak się stało, ale wydaje mi się, że jest to wynik uproszczenia, które ja stosuje i wiążącego się z nim błędu ostatniego koloru w tablicy $identmap.
kiler129
No ja obecnie zrobiłem to chyba prościej:

  1. <?php
  2. class avgColor {
  3. var $resampledSize=300; //Img scaling to X pixels [keeps aspect ratio]
  4. var $samplingAvg=10; //Number of results to averaging
  5. private $gdResImg; //Loaded GD2 resource image
  6. private $imgResY; //Img height [axis Y]
  7. private $imgResX; //Img width [axis X]
  8. var $colorMap; //Table of color in image
  9.  
  10. private function resizeResource() {
  11. $max = $this->resampledSize;
  12. $ratio = $this->imgResX/$this->imgResY;
  13. $newX=$newY=$max;
  14. if ($ratio < 1) $newX = $max*$ratio; else $newY = $max/$ratio; //Cares about ratio
  15. $tmpRes = imagecreatetruecolor($newX, $newY);
  16. imagecopyresampled($tmpRes, $this->gdResImg, 0, 0, 0, 0, $newX, $newY, $this->imgResX, $this->imgResY);
  17. $this->imgRes = $tmpRes;
  18. $this->imgResX = $newX;
  19. $this->imgResY = $newY;
  20. unset($tmpRes);
  21. }
  22.  
  23. function __construct($imgPath) {
  24. if(!is_file($imgPath) || !is_readable($imgPath)) {
  25. trigger_error("Cannot open $imgPath", E_USER_ERROR);
  26. return false;
  27. }
  28.  
  29. $imgInfo = getimagesize($imgPath);
  30. $this->imgResY = $imgInfo[1];
  31. $this->imgResX = $imgInfo[0];
  32.  
  33. switch($imgInfo["mime"]) {
  34. case "image/jpeg": $this->gdResImg = imagecreatefromjpeg($imgPath); break;
  35. case "image/png": $this->gdResImg = imagecreatefrompng($imgPath); break;
  36. case "image/gif": $this->gdResImg = imagecreatefromgif($imgPath); break;
  37. case "image/jpeg": $this->gdResImg = imagecreatefromjpeg($imgPath); break;
  38. }
  39.  
  40. $this->resizeResource(); //Resize res for sampling
  41. }
  42.  
  43. private function trcArr(&$arr, $elements=10) { //truncate array to specified nums of elements
  44. $i=1;
  45. foreach($arr as $key=>$val) {
  46. if($i<=$elements) $ret[$key] = $val;
  47. $i++;
  48. }
  49. $arr=$ret;
  50. }
  51.  
  52. private function mapColors($force=false) { //Make colors map for image; 1st param is force rerun function even if the map is already done
  53. if(!empty($this->colorMap)&&!$force) return; //If map is already done do nothing
  54.  
  55. $this->colorMap=array();
  56. for ($x=0;$x<$this->imgResX;$x++) { //Loop for "rows"
  57. for ($y=0;$y<$this->imgResY;$y++) { //Loop for every pixel in "row"
  58. $rgb = imagecolorat($this->gdResImg, $x, $y);
  59. @$this->colorMap[(($rgb >> 16) & 0xFF)."-".(($rgb >> 8) & 0xFF)."-".($rgb & 0xFF)]++; //Hide notices about index
  60. }
  61. }
  62.  
  63. arsort($this->colorMap); //Sort arr
  64. $this->trcArr($this->colorMap, $this->samplingAvg); //Strip array
  65. }
  66.  
  67.  
  68.  
  69. private function avgColor() { //Better method for avg color
  70. $this->mapColors(); //Make map w/o force
  71. $color=array("r"=>0,"g"=>0,"b"=>0,"rgb"=>"fff");
  72.  
  73. foreach($this->colorMap as $key=>$val) { //Sum colors
  74. $tmp = explode("-", $key); //Extract from R-G-B notation
  75. $color["r"] += $tmp[0]; //Red
  76. $color["g"] += $tmp[1]; //Green
  77. $color["b"] += $tmp[2]; //Blue
  78. }
  79.  
  80.  
  81. $color["r"] = $color["r"]/$this->samplingAvg;
  82. $color["g"] = $color["g"]/$this->samplingAvg;
  83. $color["b"] = $color["b"]/$this->samplingAvg;
  84. $color["rgb"] = dechex($color["r"]).dechex($color["g"]).dechex($color["b"]);
  85.  
  86. return $color;
  87. }
  88.  
  89. function get() {
  90. return $this->avgColor();
  91. }
  92.  
  93. }
  94. ?>
  95.  
  96. /************/
  97. <?php
  98. /********************************************************************************
    */
  99. function _st() {
  100. $mtime = explode(" ", microtime());
  101. return $mtime[1] + $mtime[0];
  102. }
  103.  
  104. function _et($_st) {
  105. $mtime = explode(" ",microtime());
  106. $execT = (($mtime[1] + $mtime[0]) - $_st);
  107. echo "\n\n".str_repeat("-", 40)."\nExec time: ".(($execT<1)?($execT*1000)."ms":$execT."s")."\n\n";
  108. }
  109. /********************************************************************************
    */
  110.  
  111. ini_set('display_errors', '1');
  112.  
  113. require_once("sim.class.php");
  114. $_sim = new avgColor("imgs/647636.jpg");
  115.  
  116. $_st=_st();
  117. var_dump($_sim->get());
  118. _et($_st);
  119. ?>


I obrazek 12Mpx robi w 15ms. Pokaż obrazek abyśmy testowali na tym samym winksmiley.jpg
Quadina
Przygotuje paczkę jak wrócę do domu, teraz wychodzę akurat z roboty ;-) Może odezwij się do mnie na GG to spokojnie przedyskutujemy sprawę optymalizacji - ja strasznie lubię takie tematy ;-)
Crozin
Hmmm... Imagick::getImageHistogram()?
kiler129
Cytat(Crozin @ 14.12.2010, 15:21:55 ) *

Brak dokumentacji oraz niedostępność imagemagic na sporej ilości serwerów raczej ją dyskwalifikuje smile.gif

@Quadina - napisz na moje gg: 6388911 [niewidok]
Quadina
Histogram Imagicka jest wolny, dla mojego zdjęcia tego 100x100 generował całą sekundę dalej nie sprawdzałem, bo się przeraziłem użyciem pamięci. Przy czym mój skrypt użył 1/10 z tego, więc używanie magicka chyba nie jest optymalne. Po za tym link do bardzo dobrej dokumentacji funkcji, ale w pierwszym przykładzie widzę pewien błąd ;-) Bez urazy, ale lista posortowanych pixeli nas raczej nie interesuje, bo to jesteśmy sobie w stanie zrobić sami - patrząc po czasach nawet znacznie szybciej.
Crozin
Cytat
Brak dokumentacji oraz niedostępność imagemagic na sporej ilości serwerów raczej ją dyskwalifikuje
Dokumentacji szukaj na stronach samego ImageMagicka (w końcu te PHPowe obiekty to tylko interfejs dla zewnętrznego programu). Co do dostępności - niedostępny będzie na większości serwerów współdzielonych, czyli takich na których za używanie powyższych skryptów za po krótkim okresie czasu konto zostanie zawieszone. winksmiley.jpg

Cytat
Histogram Imagicka jest wolny, dla mojego zdjęcia tego 100x100 generował całą sekundę dalej nie sprawdzałem
JPEG, 1152x864, 218 KiB - około 0.1 sekundy.
Quadina
Spoko, sprawdzimy wszystko, sprawdzimy ;-) Własnie instalujemy środowisko testowe, żeby sprawdzić wszystko na większych paczkach danych.
kiler129
/** JESLI SZUKASZ ROZWIAZANIA UZYJ KODU Z KONCA TEGO POSTU **/

Problem (chyba) rozwiązany.
Godzinę wikipedii później i pisania na stackoverflow mam taki oto kod:

  1. <?php
  2. function showColor($c1, $c2, $c3) {
  3. $hex = rgb2hex($c1,$c2,$c3);
  4. echo '<div style="background-color:#'.$hex.'; height:30px; display:inline;">'.str_repeat(" ", 25).'</div> <b>#'.$hex.'</b> (<b><font color="red">R:</font></b> '.$c1.' | <b><font color="green">G:</font></b> '.$c2.' | <b><font color="blue">B:</font></b> '.$c3.')<br><br>';
  5. }
  6.  
  7. function rgb2hex($r,$g,$b) {
  8. $colArr=array("r"=>$r,"g"=>$g,"b"=>$b);
  9. return (($colArr["r"]<16)?"0".dechex($colArr["r"]):dechex($colArr["r"])).(($colArr["g"]<16)?"0".dechex($colArr["g"]):dechex($colArr["g"])).(($colArr["b"]<16)?"0".dechex($colArr["b"]):dechex($colArr["b"]));
  10. }
  11.  
  12. function rgb2lab($r,$g,$b) {
  13. $rgb = array($r,$g,$b);
  14. $eps = 216/24389; $k = 24389/27;
  15. $xr = 0.964221; $yr = 1.0; $zr = 0.825211;
  16. $rgb[0] = $rgb[0]/255;
  17. $rgb[1] = $rgb[1]/255;
  18. $rgb[2] = $rgb[2]/255;
  19. $rgb[0] = ($rgb[0] <= 0.04045)?($rgb[0]/12.92):pow(($rgb[0]+0.055)/1.055,2.4);
  20. $rgb[1] = ($rgb[1] <= 0.04045)?($rgb[1]/12.92):pow(($rgb[1]+0.055)/1.055,2.4);
  21. $rgb[2] = ($rgb[2] <= 0.04045)?($rgb[2]/12.92):pow(($rgb[2]+0.055)/1.055,2.4);
  22. $x = 0.4360747*$rgb[0] + 0.3850649*$rgb[1] + 0.1430804 *$rgb[2];
  23. $y = 0.2225045*$rgb[0] + 0.7168786*$rgb[1] + 0.0606169 *$rgb[2];
  24. $z = 0.0139322*$rgb[0] + 0.0971045*$rgb[1] + 0.7141733 *$rgb[2];
  25. $xr = $x/$xr; $yr = $y/$yr; $zr = $z/$zr;
  26. $fx = ($xr > $eps)?pow($xr, 1/3):($fx = ($k * $xr + 16) / 116);
  27. $fy = ($yr > $eps)?pow($yr, 1/3):($fy = ($k * $yr + 16) / 116);
  28. $fz = ($zr > $eps)?pow($zr, 1/3):($fz = ($k * $zr + 16) / 116);
  29. $lab = array();
  30. $lab[] = round(( 116 * $fy ) - 16);
  31. $lab[] = round(500*($fx-$fy));
  32. $lab[] = round(200*($fy-$fz));
  33. return $lab;
  34. }
  35.  
  36. function deltaE($lab1, $lab2) {
  37. $l = 1; $c = 1;
  38. $c1 = sqrt($lab1[1]*$lab1[1]+$lab1[2]*$lab1[2]);
  39. $c2 = sqrt($lab2[1]*$lab2[1]+$lab2[2]*$lab2[2]);
  40. $h1 = (((180000000/M_PI)*atan2($lab1[1],$lab1[2])+360000000)%360000000)/1000000;
  41. $t = (164<=$h1 AND $h1<=345)?(0.56+abs(0.2*cos($h1+168))):(0.36+abs(0.4*cos($h1+35)));
  42. $f = sqrt(pow($c1,4)/(pow($c1,4)+1900));
  43. $sl = ($lab1[0]<16)?(0.511):((0.040975*$lab1[0])/(1+0.01765*$lab1[0]));
  44. $sc = (0.0638*$c1)/(1+0.0131*$c1)+0.638;
  45. $sh = $sc*($f*$t+1-$f);
  46. return sqrt(pow(($lab1[0]-$lab2[0])/($l*$sl),2)+pow(($c1-$c2)/($c*$sc),2)+pow(sqrt(($lab1[1]-$lab2[1])*($lab1[1]-$lab2[1])+($lab1[2]-$lab2[2])*($lab1[2]-$lab2[2])+($c1-$c2)*($c1-$c2))/$sh,2));
  47. }
  48.  
  49. $c1 = array(30,255,123);
  50. $c2 = array(55,250,100);
  51.  
  52. showColor($c1[0], $c1[1], $c1[2]);
  53. showColor($c2[0], $c2[1], $c2[2]);
  54.  
  55. $cl1 = rgb2lab($c1[0], $c1[1], $c1[2]);
  56. $cl2 = rgb2lab($c2[0], $c2[1], $c2[2]);
  57.  
  58. echo "<pre>";
  59. var_dump(deltaE($cl1, $cl2));
  60. ?>


Obliczy różnicę między dwoma kolorami w przestrzeni L A B - inaczej się ponoć tego dobrze nie da zrobić. Teraz tylko spiąć to z kolorami pixeli zdjęć i gotowe.

Edit
Post jest za dlugi bla bla bla: http://wklej.org/id/438798/ - poddaje sie

Edit
Ok, udało się porównać kolory, dodanie tego do fotek to już nie problem.
Kod klasy:
  1. <?php
  2. class compareColors {
  3. var $tolerance = 3; //tolerance in %
  4.  
  5. private function hex2rgb($hex) { //Convert from #FFFFFF to array(0,0,0) in eg.
  6. $hex = str_replace('#', '', $hex); //Remove #
  7. if(strlen($hex) != 6){ return array(0,0,0); }
  8. return array(
  9. hexdec(substr($hex, 0, 2)),
  10. hexdec(substr($hex, 2, 2)),
  11. hexdec(substr($hex, 4, 2))
  12. );
  13. }
  14.  
  15.  
  16. private function rgb2cmyk($rgb) { //Simple conversion from RGB namespace to CMYK namespace. (warning! colors can look diff btw rgb and cmyk, its ok for comparing but not for real conversion!)
  17. $K = min(array("r"=>(1-$rgb[0]), "g"=>(1-$rgb[1]), "b"=>(1-$rgb[2])));
  18. $C = @((1-$rgb[0]-$K)/(1-$K));
  19. $M = @((1-$rgb[1]-$K)/(1-$K));
  20. $Y = @((1-$rgb[2]-$K)/(1-$K));
  21.  
  22. return(array("C"=>$C, "M"=>$M, "Y"=>$Y, "K"=>$K));
  23. }
  24.  
  25.  
  26. function compareCMYKColors($cmyk1, $cmyk2, $returnScale=false) { //Compare 2 CMYK colors with given tolerance, it also can return "tolerane" of matching instead of bool
  27. $tolerance = ($this->tolerance/100); //Convert from % to float
  28. $diff=array();
  29. $diff[] = abs($cmyk1["C"]-$cmyk2["C"]);
  30. $diff[] = abs($cmyk1["M"]-$cmyk2["M"]);
  31. $diff[] = abs($cmyk1["Y"]-$cmyk2["Y"]);
  32. asort($diff);
  33. reset($diff);
  34. $max1 = each($diff);
  35. $max2 = each($diff);
  36.  
  37. if($returnScale) return ((($max1+$max2)/2)*100); //Convert scale to %
  38. return ($max1["value"]<=$tolerance&&$max2["value"]<=$tolerance) ? true:false;
  39. }
  40.  
  41.  
  42. function compareRGBColors($rgb1, $rgb2, $returnScale=false) { //Alias to above but converts from RGB to CMYK first
  43. return $this->compareCMYKColors($this->rgb2cmyk($rgb1), $this->rgb2cmyk($rgb2), $returnScale);
  44. }
  45.  
  46.  
  47. function compareHEXColors($hex1, $hex2, $returnScale=false) { //Alias to above
  48. return $this->compareRGBColors($this->hex2rgb($hex1), $this->hex2rgb($hex2), $returnScale);
  49. }
  50.  
  51. function bathCompare($base, $colors, $format="hex") {
  52. $out=array();
  53. if($format=="hex") { foreach($colors as $color) { if($this->compareHEXColors($base, $color)) $out[] = $color; } return $out; }
  54. if($format=="rgb") { foreach($colors as $color) { if($this->compareRGBColors($base, $color)) $out[] = $color; } return $out; }
  55. if($format=="cmyk") { foreach($colors as $color) { if($this->compareCMYKColors($base, $color)) $out[] = $color; } return $out; }
  56.  
  57. return false;
  58. }
  59.  
  60. }
  61. ?>


Kod testowy [+ dane testowe]: http://wklej.org/id/439719/
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.