Die Hauptfarbe eines Bildes erhalten

countryqt30

Mitglied
Hallo,

Um bestimmte bilder zu filtern, moechte ich pruefen, ob mehr als 50% eines bildes (jpeg, png)
hauttoene sind oder nicht.

Ich habs mit ImageColorAt versucht, das ist jedoch so langsam, dass das php script abbricht (habe zumindest das gefuehl).

Mein code hier:
Code:
<?php
	
	
	$img = "a.jpg";
	$imgHand = ImageCreateFromJPEG($img);
	$imgSize = GetImageSize($img);
	$imgWidth = $imgSize[0];
	$imgHeight = $imgSize[1];
	
	$counter = 0;
	$maxCount = $imgHeight * $imgWidth;


	echo "w=$imgWidth, h=$imgHeight<br />";
	
	echo "okaay!";
	
	
	for($i=0; $i<$imgWidth; ++$i)
	{
		for($k=0; $k<$imgHeight; ++$k)
		{
			$col = ImageColorAt($imgHand, $i, $k);
			
			$colArr = html2rgb((string)$col);
			$colArrDst = array(99, 93, 33);

			echo "count(colArr)=";
			
			for($x=0; $x<3; $x++)
			{
				if(abs($colArr[$x] - $colArrDst[$x]) < 30)
				{
					$counter++;
					break;
				}
			}
			
			
		}
	}

	echo "counter=$counter<br />";
	echo "maxCount=$maxCount<br />";
	echo "=>" . $counter / $maxCount . " %";

function html2rgb($color)
{

    if (strlen($color) == 6)
        list($r, $g, $b) = array($color[0].$color[1],
                                 $color[2].$color[3],
                                 $color[4].$color[5]);
    elseif (strlen($color) == 3)
        list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]);
    else
        return false;

    $r = hexdec($r); $g = hexdec($g); $b = hexdec($b);

    return array($r, $g, $b);
}
?>
Meiner vermutung nach wird der code nach ~5 sekunden abgebrochen, weil es zu lange dauert ihn zu bearbeiten, beispiels wird das "okay" in einer der ersten zeilen gar nicht ausgegeben. Versucht den code ruhig mal bei euch :D
Mein testbild hatte eine Aufloesung von 528x705 px



Habt ihr ideen wie man das machen koennte?
 
Zuletzt bearbeitet:
Schau mal hier und zwar am Ende der Seite wo es darum geht die "Durchschnittsfarbe berechnen".

Habe es nicht getestet aber wenn du Glück hast kannst du das für dein Vorhaben auch nützen!?
 
Danke fuer deine Antwort tombe.
Funktioniert an sich richtig gut!
Leider nicht genau das, was ich gesucht habe :(.
Die dortige funktion erstellt die Durchschnittsfarbe. Ich brauche jedoch die meist vorkommende farbe.

Zahlreiche tests haben ergeben, dass das nicht durch die durchschnittsfarbe ersetzbar ist.
So kommt beispielsweise ueberdurchschnittlich oft grautoene heraus, wenn man alles zusammenmischt.

Ich habe z.b. Dieses QuellBild:
device2.png

Und moechte nun die farben analysieren:

orangetoene: 45%
blautoene: 30%
usw.
Mir ist jedoch nur die am haeufigsten vorkommende wichtig, hier wohl Orange.
2. haeufig vorkommende und 3. und so weiter sind egal.
 
Hi,
hatte nur nen paar Minuten Zeit also hab ich einfach dein Script etwas angepasst.
Achtung! es frisst Performance :D ich weiss ja ned wo du es einsetzt aber wenn einige 100 Leute gleichzeitig drauf zugreifen könnte der Server ordentlich in die Knie gehen.

PHP:
<?php

    
    
    $img = "a.jpg";
    $imgHand = ImageCreateFromJPEG($img);
    $imgSize = GetImageSize($img);
    $imgWidth = $imgSize[0];
    $imgHeight = $imgSize[1];
    
    $maxCount = $imgHeight * $imgWidth;
 
    echo "w=$imgWidth, h=$imgHeight<br />";

    $colors = array();
    
    for($i=0; $i<$imgWidth; ++$i)
    {
        for($k=0; $k<$imgHeight; ++$k)
        {
            $color = ImageColorAt($imgHand, $i, $k);
            
            $colorHexCode = rgb2hex($color);
            $colorHexCodeStr = hex2str($colorHexCode);
            
            if(!array_key_exists($colorHexCodeStr, $colors))
            {
            	$colors[$colorHexCodeStr]['colorHex'] = $colorHexCode;
            	$colors[$colorHexCodeStr]['count'] = 1;
            }
            else
            {
            	$colors[$colorHexCodeStr]['count'] += 1;
            }
        }
    }
 
//    echo "<pre>";
//    var_dump($colors);
//    echo "</pre>";

    $THE_Color = ""; // meiste Farbe :D
    $THE_Color_Arr = array(); // meiste farbe als array
    $THE_Color_Count = 0; // vorkommen der meisten Farbe
    
    foreach($colors as $colorcode => $value)
    {
    	if($value['count'] > $THE_Color_Count)
    	{
    		$THE_Color_Count = $value['count'];
    		$THE_Color = $colorcode;
    		$THE_Color_Arr = $value['colorHex'];
    	}
    }

    echo "<pre> Die am meisten Vorkommende Farbe ist:";
    var_dump($THE_Color, $THE_Color_Arr, $THE_Color_Count);
    echo "</pre>";
    
function rgb2hex($color)
{
	$r = ($color >> 16) & 0xFF;
	$g = ($color >> 8) & 0xFF;
	$b = $color & 0xFF;
	
	$r = dechex($r); $g = dechex($g); $b = dechex($b);
 
    return array($r, $g, $b);
}

function hex2str($colorhex)
{
	return $colorhex[0].$colorhex[1].$colorhex[2];
}


edit:
nochwas ^^ wenn du mehr Performance brauchst kannst du versuchen das img vorher auf sagen wir 33% zu verkleinern , sollte das Ergebnis nicht besonders beeinträchtigen.

Wenn du das ganze so ausgeben lassen möchtest:
orangetoene: 45%
blautoene: 30%
usw.

musst du ja nur für jede vorkommende Farbe im Array $colors, den Prozentsatz anhand vom index ['count'] und der Variable $maxCount berechnen.

weisst schon was ich meine :D Viel Spass dabei ^^

Greetz
Gordon
 
Zuletzt bearbeitet:
Ja, gefaellt mir schon gut.
Ich habe jedoch noch 2 Fragen:

Wie bekomme ich nun "toene" hin. Schliesslich kommt ja genau eine farbe, wie es momentan ist, praktisch nur in "kuenstlichen" bildern vor (grafiken, logos, etc, wenn es da nicht auch verlauefe gibt).
Ich handhabe aber hauptsaechlich mit "echten" bildern (wobei obiges nun vielleicht ein schlechtes beispiel war, zugegegeben), und da ist es ja ziemlich selten, dass genau eine farbe mehrmals vorkommt.
Man braucht also eine tolleranz.

Das zweite ist, ich kann ohne probleme ein 2000x1500 jpeg "parsen", ein 480x800 png (bild siehe oben) klappt jedoch nicht.
"klappt jedoch nicht" bedeutet in meinem fall eine "Diese webseite ist nicht verfuegbar, Fehler 101 (net::ERR_CONNECTION_RESET): Verbindung wurde zurückgesetzt.".
Die "Wartezeit" beim jpeg ist jedoch wesentlich laenger, ist ja klar, das bild ist auch viel groesser.

Ich habe den code hierfuer um folgende Zeile erweitert:
PHP:
	if($imgHand == FALSE)
		$imgHand = ImageCreateFromPNG($img);
 
Warum zerlegst du jeden Pixelpunkt? Es reicht wenn du die Farben durchgehst und aufsummierst. Zerlegen kannst du es am Schluss. Somit wird die Zerlegung um ein X-Faches weniger ausgeführt und das spart Performance
PHP:
<?php 

$filename = 'logo.png';

$img = imagecreatefrompng($filename);
$imgSize = GetImageSize($filename);
$imgWidth = $imgSize[0];
$imgHeight = $imgSize[1];

//Farben zählen
for($x = 0; $x < $imgWidth; $x++){
    for($y = 0; $y < $imgHeight; $y++){
        //$colors ergibt ein Array mit dem imagecolor als Index und dem Zähler als Wert
        $colors[imagecolorat($img, $x, $y)]++;        
    }
}
//Soriteren, damit die Farbe mit den meisten Punkten zuerst kommt
arsort($colors);

//Auswerten
echo "Die Grafik hat ".count($colors)." verschiedene Farben.<br />\n";
?>
<table border="1">
<tr><th>color</th><th>R</th><th>G</th><th>B</th><th>alpha</th><th>Anzahl Pixel</th></tr>
<?php 
foreach($colors as $color => $count){
    //die imagecolor in RGB und alpah aufsplitten
    $rgb = imagecolorsforindex($img, $color);
    echo <<<LINE
    <tr>
        <td>{$color}</td>
        <td>{$rgb['red']}</td>
        <td>{$rgb['green']}</td>
        <td>{$rgb['blue']}</td>
        <td>{$rgb['alpha']}</td>
        <td>{$count}</td>
    </tr>
LINE;
}
echo "/<table>";?>

Nachtrag:
Wen da das alpha weghaben willst, kannst du die rgb einfach konvertieren
PHP:
        $rgb =  imagecolorsforindex($img, imagecolorat($img, $x, $y));
        $colors[imagecolorclosest($img, $rgb['red'], $rgb['green'], $rgb['blue'])]++;
 
Schreib dir ne Funktion die den einzelnen HTML-Farbcodes einen Farbton zuweist, hast doch jetzt alles was du dazu brauchst.
 
Ist ja cool!
Ich habe die Funktion des scripts noch etwas fuer meine beduerfnisse angepasst,
sieht nun so aus

Capture.PNG

(ps: obwohl ich die IMG tags um den link gemacht habe, wird es nicht direkt eingebunden )

Eine Frage noch, was bedeuten die {}, die innerhalb des LINE sind. Ich konnte keinen Unterschied feststellen, lies ich die Klammern weg.
 
Zurück