Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

Lecture



Identify the dominant colors in the picture. Implementation in PHP.

The k-means method, also known as the k-means method, is used to distribute objects into clusters. This article describes the algorithm itself and how it can be used to find the dominant colors (the colors that occur most often) in the picture.

  Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

A bit of theory

Those who are not interested in the essence of the method can scroll to the part with the code.

A cluster in the sense we need is a group of objects with similar properties. Objects can have one property, and maybe several. By the way, these properties are numbers. The task is to split the available objects into a certain number of clusters so that the sum of deviations from the center of the clusters is minimal. The result will be the required number of "heaps" of objects.

For simplicity, consider an example with a set of objects with 2 properties. These properties will be represented as x and y coordinates on the plane. Let we have a predetermined set of points.

  Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

Further, from the current number of objects we randomly define as centroids (indicated by stars) as many points as necessary to select clusters.

  Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

Now the cycle begins. First, for each object, we determine the centroid nearest to it, thereby distributing the objects into groups.

  Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

Next, for each group of objects, we determine the center of mass and transfer the centroid of this group there.

  Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

The cycle is repeated until the total centroid shift in one cycle is less than a predetermined value. As a result, the current centrodes will turn out to be clusters.

  Determining the dominant colors in a picture in PHP using the k-means method and the method of suffering values

Application of the method for determining the dominant colors

Everything is very simple here :). The objects are pixel colors, and properties are the red, blue, and green color channel values.

  // The function that determines the "distance" between objects
 function getDistance ($ arr1, $ arr2, $ l) {
	 // Determine the "taxi car distance" - this is faster, but for a more accurate result, it is desirable to use the Euclidean metric
	 $ s = 0;
	 for ($ i = 0; $ i <$ l; ++ $ i)
		 $ s + = $ arr1 [$ i]> $ arr2 [$ i]?  ($ arr1 [$ i] - $ arr2 [$ i]): ($ arr2 [$ i] - $ arr1 [$ i]);
	 return $ s;
 }

 / *
 The function itself, which determines the dominant colors
 Values ​​passed to the function:
	 $ url - path to the image on the server
	 $ amount - the number of colors that need to be determined
	 $ sort - whether to sort colors by decreasing occurrence in the picture
	 $ maxPreSize - the maximum size of the reduced image (more details a little further)
	 $ epselon - the minimum total deviation required to exit the cycle
 * /
 function getDominantColors ($ url, $ amount = 1, $ sort = true, $ maxPreSize = 50, $ epselon = 1) {
	 // Determine whether the specified file exists.  If not, then give an error.
	 if (! file_exists ($ url))
		 return false;
	 // Determine the size of the picture, if it is a picture.
	 $ size = getimagesize ($ url);
	 if ($ size === false)
		 return false;
	 $ width = $ size [0];
	 $ height = $ size [1];
	 // Determine the format of the image from the file header and check if there is a function that can open this image.  If not, then the file is not a picture, and give an error.
	 $ format = strtolower (substr ($ size ['mime'], strpos ($ size ['mime'], '/') + 1));
	 if ($ format == 'x-ms-bmp')
		 $ format = 'wbmp';
	 $ func = 'imagecreatefrom'. $ format;
	 if (! function_exists ($ func))
		 return false;
	 $ bitmap = @ $ func ($ url);
	 if (! $ bitmap)
		 return false;

	 // Here we reduce the original image to deal not with all pixels, but with some.  This greatly increases the speed of the algorithm.  The smaller the size, the lower the accuracy.
	 $ newW = $ width> $ maxPreSize?  $ maxPreSize: $ width;
	 $ newH = $ height> $ maxPreSize?  $ maxPreSize: $ height;
	 $ bitmapNew = imagecreatetruecolor ($ newW, $ newH);
	 // For greater accuracy, you must use the imageCopyResambled function, but it spends a lot of time, so the quick and rough imageCopyResized function is used.
	 imageCopyResized ($ bitmapNew, $ bitmap, 0, 0, 0, 0, $ newW, $ newH, $ width, $ height);
	 // Put the colors of all pixels into one array
	 $ pixelsAmount = $ newW * $ newH;
	 $ pixels = Array ();
	 for ($ i = 0; $ i <$ newW; ++ $ i)
		 for ($ j = 0; $ j <$ newH; ++ $ j) {
			 $ rgb = imagecolorat ($ bitmapNew, $ i, $ j);
			 $ pixels [] = Array (
				 ($ rgb >> 16) & 0xFF,
				 ($ rgb >> 8) & 0xFF,
				 $ rgb & 0xFF
			 );
		 }
	 imagedestroy ($ bitmapNew);
	
	 // Choose random pixels to set the centroids in them
	 $ clusters = Array ();
	 $ pixelsChosen = Array ();
	 for ($ i = 0; $ i <$ amount; ++ $ i) {
		 // It is advisable not to put several centroids at one point
		 do {
			 $ id = rand (0, $ pixelsAmount - 1);
		 } while (in_array ($ id, $ pixelsChosen));
		 $ pixelsChosen [] = $ id;
		 $ clusters [] = $ pixels [$ id];
	 }
	
	 $ clustersPixels = Array ();
	 $ clustersAmounts = Array ();
	 // Start the cycle
	 do {
		 // Zero the storage of pixels belonging to the current centroid and their counter
		 for ($ i = 0; $ i <$ amount; ++ $ i) {
			 $ clustersPixels [$ i] = Array ();
			 $ clustersAmounts [$ i] = 0;
		 }
		
		 // Pass through all the pixels and determine the centroid nearest to them.	
		 for ($ i = 0; $ i <$ pixelsAmount; ++ $ i) {
			 $ distMin = -1;
			 $ id = 0;
			 for ($ j = 0; $ j <$ amount; ++ $ j) {
				 $ dist = getDistance ($ pixels [$ i], $ clusters [$ j], 3);
				 if ($ distMin == -1 or $ dist <$ distMin) {
					 $ distMin = $ dist;
					 $ id = $ j;
				 }
			 }
			 $ clustersPixels [$ id] [] = $ i;
			 ++ $ clustersAmounts [$ id];
		 }
		
		 // Move the centroid to the center of mass of the pixels belonging to it, at the same time we calculate how much it has shifted
		 $ diff = 0;
		 for ($ i = 0; $ i <$ amount; ++ $ i) {
			 if ($ clustersAmounts [$ i]> 0) {
				 $ old = $ clusters [$ i];
				 for ($ k = 0; $ k <3; ++ $ k) {
					 $ clusters [$ i] [$ k] = 0;
					 for ($ j = 0; $ j <$ clustersAmounts [$ i]; ++ $ j)
						 $ clusters [$ i] [$ k] + = $ pixels [$ clustersPixels [$ i] [$ j]] [$ k];
					 $ clusters [$ i] [$ k] / = $ clustersAmounts [$ i];
				 }
				 // We will compare the maximum deviation
				 $ dist = getDistance ($ old, $ clusters [$ i], 3);
				 $ diff = $ diff> $ dist?  $ diff: $ dist;
			 }
		 }
	 } while ($ diff> = $ epselon);
	
	 // Sort the resulting clusters (I don’t remember how the method is called)
	 if ($ sort and $ amount> 1)
		 for ($ i = 1; $ i <$ amount; ++ $ i)
			 for ($ j = $ i; $ j> = 1 and $ clustersAmounts [$ j]> $ clustersAmounts [$ j - 1]; - $ j) {
				 $ t = $ clustersAmounts [$ j - 1];
				 $ clustersAmounts [$ j - 1] = $ clustersAmounts [$ j];
				 $ clustersAmounts [$ j] = $ t;
				
				 $ t = $ clusters [$ j - 1];
				 $ clusters [$ j - 1] = $ clusters [$ j];
				 $ clusters [$ j] = $ t;
			 }
	
	 // Color value is an integer value, therefore it should be rounded
	 for ($ i = 0; $ i <$ amount; ++ $ i)
		 for ($ j = 0; $ j <3; ++ $ j)
			 $ clusters [$ i] [$ j] = floor ($ clusters [$ i] [$ j]);
	
	 // clear memory
	 imagedestroy ($ bitmap);
	
	 // Return an array that contains the dominant colors.  Each color is an array consisting of 3 elements — a red, green, and blue color channel.
	 return $ clusters;
 }

 $ colors = getDominantColors ('my_picture.jpg', 3);
 if ($ colors === false)
	 echo 'Error';
 else
	 foreach ($ colors as $ color)
		 echo '<div style = "width: 50px; height: 40px; background-color: rgba ($ color [0], $ color [1], $ color [2], 1);"> </ div>'; 



second way

<? php
/ **
* Class for Generator Image Color Palette.
This is the format for the return format.
*
* PHP versions 4 and 5
* Licensed under The GPL License
*
* Developed By Kalpeh Gamit
* E-mail Kalpa.programmer@gmail.com
*
* @filesource
* @copyright Copyright 2009, InScripter Inc. (http://www.inscripter.com)
* @link http://www.inscripter.com
* @package PHP
* @version GetImageColor-1.0.0
* @license http://www.opensource.org/licenses/gpl-2.0.php The GPL License
* /

// start of class: Generator Image Color Palette
class GeneratorImageColorPalette
{
// get image color in RGB format function
function getImageColor ($ imageFile_URL, $ numColors, $ image_granularity = 5)
{
$ image_granularity = max (1, abs ((int) $ image_granularity));
$ colors = array ();
// find image size
$ size = @getimagesize ($ imageFile_URL);
if ($ size === false)
{
user_error ("Unable to get image size data");
return false;
}
// open image
$ img = @imagecreatefromjpeg ($ imageFile_URL);
if (! $ img)
{
user_error ("Unable to open image file");
return false;
}

// fetch color in RGB format



$ startx = round ($ size [0] / 4);
$ endx = $ size [0] -round ($ size [0] / 4);

$ starty = round ($ size [1] / 4);
$ endy = $ size [1] -round ($ size [1] / 4);

for ($ x = $ startx; $ x <$ endx; $ x + = $ image_granularity)
{
for ($ y = $ starty; $ y <$ endy; $ y + = $ image_granularity)
{
$ thisColor = imagecolorat ($ img, $ x, $ y);
$ rgb = imagecolorsforindex ($ img, $ thisColor);
$ red = round (round (($ rgb ['red'] / 0x33)) * 0x33);
$ green = round (round (($ rgb ['green'] / 0x33)) * 0x33);
$ blue = round (round (($ rgb ['blue'] / 0x33)) * 0x33);
$ thisRGB = sprintf ('% 02X% 02x% 02x', $ red, $ green, $ blue);
if (array_key_exists ($ thisRGB, $ colors))
{
$ colors [$ thisRGB] ++;
}
else
{
$ colors [$ thisRGB] = 1;
}
}
}
arsort ($ colors);
// returns maximum used color like image format # C0C0C0.
return array_slice (($ colors), 0, $ numColors, true);
}

// html color R (255) G (255) B (255)
function getHtml2Rgb ($ str_color)
{
if ($ str_color [0] == '#')
$ str_color = substr ($ str_color, 1);

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

$ r = hexdec ($ r); $ g = hexdec ($ g); $ b = hexdec ($ b);
$ arr_rgb = array ($ r, $ g, $ b);
// Return colors format liek R (255) G (255) B (255)
return $ arr_rgb;
}

// end of class here: Generator Image Color Palette
}
?>

<? php
require_once "getImageColor.php"; // include the file with the script
$ img = new GeneratorImageColorPalette ();
$ colors = $ img-> getImageColor ('1.jpg', 4, $ image_granularity = 1);
print_r ($ colors);
foreach ($ colors as $ color => $ cnt)

echo "<div style = 'background: #". ($ color). "; width: 100px; height: 100px '> </ div> <br />";
?>

  the results of the first color check at the very beginning of the article 

second method

Array ([FFFFFF] => 4960 [CCCCCC] => 785 [FFFFCC] => 423 [FFCCFF] => 316)










Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Pattern recognition

Terms: Pattern recognition