Image Search by color (PHP) #part1
Perhaps you’ve ever come across a problem that you need to search images by their color (like it has google at images.google.com).
For this search it is necessary first to identify colors in the image. The predominant colors with the color ratio can be stored in a database and then search in this database by colors..
The problem is that in the classical picture are millions of colors that are represented in it. For image processing it is necessary to reduce the scale and work only with the basic colors.
We can reduce size the image to avoid redundant colors, which the image contains.
Image processing
If we have prepared the file, we have to go pixel by pixel and find its color.
We will create function with name getColorsFromImage that returns the array of colors and their frequency in the image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/** * Return colors array from image * @return array */ public function getColorsFromImage() { //default colors array $colors = array(); //through pixel by pixel for( $i = 0; $i < $this->image_size[1]; $i++ ){ for( $j = 0; $j < $this->image_size[0]; $j++){ //Get the index of the color of a pixel $rgb = imagecolorat($this->getImage(), $j, $i); //Get the colors for an index $_rgb = imagecolorsforindex($this->getImage(), $rgb); //reduce the number of colors $_rgb = $this->updateRGB($_rgb); //create unique color id for color // + self::increase - must be > 100 to create unique string $color_id = ( (int)( (int)$_rgb['red'] + self::increase) . (int)( $_rgb['green']+self::increase ) . (int)( $_rgb['blue']+self::increase ) ); //set new color into colors array $colors[$color_id] = $colors[$color_id] + 1; } } //return colors return ($colors); } |
The function assumes private variable with name image_size that contains the image dimensions and function getImage, which returns the modified image.
The whole class for image processing will look as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
class ColoriseException extends Exception{} class Colorise { //auxiliary variable const increase = 100; //color reduction ratio const color_reduction = 55; //maximum size of the processed image private $resize_length = 150; //px //inicalize another variables private $image, $image_name = null; //inicalize another variables private $image_size, $_r, $_g, $_b = array(); /** * Constructor */ public function __construct() {} /** * Function load image by image name. Function only set image name into private variable * @param string $image_name * @return BOOL */ public function loadImage( $image_name ) { if( $this->isImage( $image_name ) ){ $this->setImage( $image_name ); return true; } //file isn't image. throw new ColoriseException('is not an image file'); return false; } /** * Return colors array from image * @return array */ public function getColorsFromImage() { //default colors array $colors = array(); //through pixel by pixel for( $i = 0; $i < $this->image_size[1]; $i++ ){ for( $j = 0; $j < $this->image_size[0]; $j++){ //Get the index of the color of a pixel $rgb = imagecolorat($this->getImage(), $j, $i); //Get the colors for an index $_rgb = imagecolorsforindex($this->getImage(), $rgb); //reduce the number of colors $_rgb = $this->updateRGB($_rgb); //create unique color id for color // + self::increase - must be > 100 to create unique string $color_id = ( (int)( (int)$_rgb['red'] + self::increase) . (int)( $_rgb['green']+self::increase ) . (int)( $_rgb['blue']+self::increase ) ); //set new color into colors array $colors[$color_id] = $colors[$color_id] + 1; } } //return colors return ($colors); } /** * Convert string color name to RGB array * @param string $id */ public function stringToColor($id) { $r = substr($id, 0, 3); $g = substr($id, 3, 3); $b = substr($id, 6, 3); return array($r-self::increase, $g-self::increase, $b-self::increase); } /** * Adjusts the color and reduce the number of color values * @param array $_rgb */ private function updateRGB( $_rgb ) { foreach( $_rgb as &$value){ //reduction the number of colors in image $value = round(($value/self::color_reduction))*self::color_reduction; } return $_rgb; } /** * Convert RGB color into html color * @param $r * @param $g * @param $b * @return string */ public function rgb2html($r, $g=-1, $b=-1) { if (is_array($r) && sizeof($r) == 3) list($r, $g, $b) = $r; $r = intval($r); $g = intval($g); $b = intval($b); $r = dechex($r<0?0:($r>255?255:$r)); $g = dechex($g<0?0:($g>255?255:$g)); $b = dechex($b<0?0:($b>255?255:$b)); $color = (strlen($r) < 2?'0':'').$r; $color .= (strlen($g) < 2?'0':'').$g; $color .= (strlen($b) < 2?'0':'').$b; return '#'.$color; } /** * Checks whether the file is an image * @param string $image_name * @return BOOL */ private function isImage($image_name) { return (bool)($this->image_size = getimagesize($image_name)); } /** * Return image name from private variable */ private function getImageName() { return $this->image; } /** * Return image source from private variable */ private function getImage() { return $this->image; } /** * Create image source from image name * @param string $image_name */ private function setImage($image_name) { switch ($this->image_size[2]) { case 1: $image = imagecreatefromgif($image_name); break; case 2: $image = imagecreatefromjpeg($image_name); break; case 3: $image = imagecreatefrompng($image_name); break; default: throw new ColoriseException('failed image'); } //resize image $this->resizeImage($image); } /** * Resize image to maximum size * @param Image source $image */ private function resizeImage($image) { //set defautl values $width = $height = 0; //check image orientation and create new size if( $this->image_size[0] > $this->image_size[1] ){ if( $this->image_size[1] > $this->resize_length ){ $height = $this->resize_length; $width = round($this->image_size[0]/($this->image_size[1]/$this->resize_length)); } }else{ if( $this->image_size[0] > $this->resize_length ){ $width = $this->resize_length; $height = round($this->image_size[1]/($this->image_size[0]/$this->resize_length)); } } if(!$width){ $width = $this->image_size[0]; $height = $this->image_size[1]; } //create new image from image source $thumb = imagecreatetruecolor( $width, $height ); imagecopyresized( $thumb, $image, 0, 0, 0, 0, $width, $height, $this->image_size[0], $this->image_size[1] ); //setup variables $this->image = $thumb; $this->image_size = array($width, $height); } /** * Set image name into private variable * @param string $image_name */ private function setImageName( $image_name ) { $this->image_name = $image_name; $this->setImage($image_name); } } |
Use in practice
We can create a simple demo application. Pictures can be stored in the array:
1 |
$images = array('img1.jpg', 'img2.jpg', 'img3.jpg'); |
These images gradually go through and find their colors using Colorise class.
A simple example of image processing with a list of colors and frequency (code is only for demonstration):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
/** * Simple output function * @param array $colors * @param Colorise object $image */ function getOutput($colors, &$image) { //sort colors arsort($colors); //count of colors $sum = array_sum($colors); echo '<table rules=all style="border: 1px solid silver" cellpadding=5>'; echo '<tr><th colspan=5>most used colors</th></tr>'; echo '<tr><th></th><th>R</th><th>G</th><th>B</th><th>%</th></tr>'; foreach($colors as $id => $color ){ //calculation ratio of actual color in image $percent = round(($color/$sum)*100, 5 ); //report only colors that have more than two percent if( $percent < 2 ){ break; } //create html name of color $htmlcolor = $image->rgb2html($image->stringToColor($id)); echo '<tr>'; echo '<td><div style="float: left; width: 20px; height: 20px; background-color: ' . $htmlcolor . '"></div><td>'; echo implode('</td><td>', $image->stringToColor( $id ) ); echo '<td>' . $percent . '%</td>'; echo '</tr>'; } echo '</table>'; } $image = new Colorise(); $images = array('Tucan.jpg', 'fbprofile.jpg', 'full.jpg'); foreach( $images as $img ){ echo '<div style="width: 300px; border: 1px solid silver; padding: 10px; float: left; margin: 20px;">'; echo '<img src="' . $img . '" width="200px">'; $image->loadImage($img); $colors = $image->getColorsFromImage(); getOutput( $colors, $image ); echo '</div>'; } |
The output of the class will look like this:
hello
Great article! Thanks for sharing, it helps me a lot!
Just wondering, how it can be, that some RGB values are greater than 255
(e.g. sample in the middle, the color with 3,11207% has a value of 275 for red).
I have something similar for #ffffff with output as RGB of 275,275,275 by public function stringToColor($id).
Do you have a hint for me?
Thanks & best regards, Urs
very good