Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

hex to real name 1

Status
Not open for further replies.

JamesGMills

Programmer
Aug 15, 2005
157
GB
Hi guys,

I am currently working on a website which is for artists to upload and showcase their artwork.

I have created the script to upload the images of the artwork. From these images I work out the top ten hex colours found in the image.

Now what I want to do is run those hex colour codes through a function which will cross reference with a list of colour names and return the colour which is closest.

Much like what this does:
I have found this which is a great start but unfortunately a colour I would very much expect to return Yellow for example, does not... it returns orange. Also one which i would expect to return blue returns silver.

Does anyone know of a good solution for this? Which works like the first javascript example?

Thanks in advance,

James


------------------------
 
just use the colblinder code? it's just javascript.
 
Hi,

How can I use that directly in a php page? I would have to use curl or something to call another page to get the result into my php script right?

James

------------------------
 
since it is just javascript why not disassemble the code and rewrite it in php? it would be very unusual that there was anything that could not be almost directly rewritten.
 
Hi,

Your right... unfortunately I do not have time to do this myself. It would take me days to do this... I understand its nothing too clever, in fact the code is very clean and well written ( I am going to have to think of a different way of getting what I need.

Thanks,

James


------------------------
 
Did you look at the licensing conditions on the javascript that you refer to? suggests that you may not have to write it all again!

If you want the best response to a question, please check out FAQ222-2244 first.
'If we're supposed to work in Hex, why have we only got A fingers?'
Drive a Steam Roller
 
James - adapting the code would take five minutes. maybe ten at a push.

i'd suggest that you'd spend more time looking elsewhere than just rewriting it.
 
done.

I have created the colours file and saved it on a public server. please download and use it locally. i will delete it later this evening.

this is the code. example at the bottom. it's probably not perfect as I have not tested it at all

Code:
<?php

class ntc{
	public $color; 
	public $rgb; 
	public $hsl;
	public $colorFile = '[URL unfurl="true"]http://dl.dropbox.com/u/194358/colorSerialized.php';[/URL]
	public function __construct(){
    	$this->names = unserialize(file_get_contents($this->colorFile));
		$this->shades = unserialize('a:10:{i:0;a:2:{i:0;s:6:"FF0000";i:1;s:3:"Red";}i:1;a:2:{i:0;s:6:"FFA500";i:1;s:6:"Orange";}i:2;a:2:{i:0;s:6:"FFFF00";i:1;s:6:"Yellow";}i:3;a:2:{i:0;s:6:"008000";i:1;s:5:"Green";}i:4;a:2:{i:0;s:6:"0000FF";i:1;s:4:"Blue";}i:5;a:2:{i:0;s:6:"EE82EE";i:1;s:6:"Violet";}i:6;a:2:{i:0;s:6:"A52A2A";i:1;s:5:"Brown";}i:7;a:2:{i:0;s:6:"000000";i:1;s:5:"Black";}i:8;a:2:{i:0;s:6:"808080";i:1;s:4:"Grey";}i:9;a:2:{i:0;s:6:"FFFFFF";i:1;s:5:"White";}}');
    	for($i = 0; $i < count($this->names) ; $i++):
      		$color = "#" + $this->names[$i][0];
      		$rgb = $this->html2rgb($color);
      		$hsl = $this->rgb2hsl($rgb);
      		array_push($this->names[$i], $rgb[0], $rgb[1], $rgb[2], $hsl[0], $hsl[1], $hsl[2]);
    	endfor;
  	}
	
	public function name($color){
		$color = strtoupper($color);
    	if(strlen($color) < 3 || strlen($color) > 7):
      		return array("#000000", "Invalid Color: " . $color, "#000000", "", false);
		endif;
    	if(strlen($color) % 3 == 0):
      		$color = "#" + $color;
	 	endif;
    	if(strlen($color) == 4):
    		$_color = '#';
    		for($i=0; $i<3;$i++):
      			$_color .= $color[$i];
				$_color .= $color[$i];
			endfor;
			$color = $_color;
		endif;
		
		$rgb = $this->html2rgb($color);
    	list($r, $g, $b) = $rgb;
		
    	$hsl = $this->rgb2hsl($rgb);
    	list($h,$s,$l) = $hsl;
		
    	$ndf1 = 0; $ndf2 = 0; $ndf = 0;
    	$cl = -1;
		$df = -1;

    	for($i = 0; $i < count($this->names) ; $i++):
			if($color == "#" + $this->names[$i][0]):
        		return array("#" + $this->names[$i][0], 
							$this->names[$i][1], 
							$this->shadergb($this->names[$i][2]), $this->names[$i][2], 
							true);
    		endif;
    		//print_R($this->names[$i]);
			$ndf1 = pow($r - $this->names[$i][3], 2) 
					+ pow($g - $this->names[$i][4], 2) 
					+ pow($b - $this->names[$i][5], 2);
      		$ndf2 = abs(pow($h - $this->names[$i][6], 2)) 
					+ pow($s - $this->names[$i][7], 2) 
					+ abs(pow($l - $this->names[$i][8], 2));
      		$ndf = $ndf1 + $ndf2 * 2;
			
			if($df < 0 || $df > $ndf):
				$df = $ndf;
        		$cl = $i;
      		endif;		
    	endfor;
		
		return 	$cl < 0 
				? array("#000000", "Invalid Color: " + $color, "#000000", "", false) 
				: array("#" + $this->names[$cl][0], $this->names[$cl][1], $this->shadergb($this->names[$cl][2]), $this->names[$cl][2], false);  
    }
	
	private function html2rgb($color){
    	if ($color[0] == '#')
        $color = substr($color, 1);

    	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;
		endif;
    	$r = hexdec($r); $g = hexdec($g); $b = hexdec($b);
    	return array($r, $g, $b);
	}
		
	private function rgb2hsl($rgb){
		list($clrR,$clrG,$clrB) = $rgb;
	    $clrMin = min($clrR, $clrG, $clrB);
	    $clrMax = max($clrR, $clrG, $clrB);
	    $deltaMax = $clrMax - $clrMin;
	     
	    $L = ($clrMax + $clrMin) / 510;
	     
	    if (0 == $deltaMax){
	        $H = 0;
	        $S = 0;
	    }
	    else{
	        if (0.5 > $L){
	            $S = $deltaMax / ($clrMax + $clrMin);
	        }
	        else{
	            $S = $deltaMax / (510 - $clrMax - $clrMin);
	        }
	
	        if ($clrMax == $clrR) {
	            $H = ($clrG - $clrB) / (6.0 * $deltaMax);
	        }
	        else if ($clrMax == $clrG) {
	            $H = 1/3 + ($clrB - $clrR) / (6.0 * $deltaMax);
	        }
	        else {
	            $H = 2 / 3 + ($clrR - $clrG) / (6.0 * $deltaMax);
	        }
	
	        if (0 > $H) $H += 1;
	        if (1 < $H) $H -= 1;
	    }
	    return array($H, $S,$L);
	}

  	private function shadergb($shade){
  		foreach($this->shades as $_shade):
			if($shade == $_shade[1])
        	return "#" + $_shade[0];
    	endforeach;
    	return "#000000";	
  	}
}
$ntc = new ntc;
$result = $ntc->name('#6195ED');
print_r($result);
?>
 
hmm. actually i think that the original code did not do proper conversions from hex values.
i will recast the code to use the original maths rather than the proper methods. it will take only a couple of minutes but i have to go out for half an hour. watch this proverbial space.
 
nope, i get the same results with both code implementations.

but the results are different to those provided by the javascript test site.

do they work for you?
 
yes. I gave up with that code. It seemed flawed. I wonder whether that website actually uses it.

so then I started looking into colour theory. which was quite interesting.

here is some working code. rather than work on iterative processes based on a text file I created a database table. the mysql import data is here:
and here is the code. as you can see all the diff processing is done in the database engine, which makes it much faster.

just add the db connect stuff to the top of the file and run.

Code:
<?php 

//connect to database
mysql_connect('','','')  or die (mysql_error();

mysql_select_db('colors');

class ntc {
    
	/**
     * [URL unfurl="true"]http://www.emanueleferonato.com/2009/09/08/color-difference-algorithm-part-2/[/URL]
     * @param object $color
     * @return 
     */
    private function rgb_to_xyz($color) {
        $rgb = $this->rgb($color);
        $red = $rgb[0];
        $green = $rgb[1];
        $blue = $rgb[2];
        $_red = $red / 255;
        $_green = $green / 255;
        $_blue = $blue / 255;
        if ($_red > 0.04045) {
            $_red = ($_red + 0.055) / 1.055;
            $_red = pow($_red, 2.4);
        } else {
            $_red = $_red / 12.92;
        }
        if ($_green > 0.04045) {
            $_green = ($_green + 0.055) / 1.055;
            $_green = pow($_green, 2.4);
        } else {
            $_green = $_green / 12.92;
        }
        if ($_blue > 0.04045) {
            $_blue = ($_blue + 0.055) / 1.055;
            $_blue = pow($_blue, 2.4);
        } else {
            $_blue = $_blue / 12.92;
        }
        $_red *= 100;
        $_green *= 100;
        $_blue *= 100;
        $x = $_red * 0.4124 + $_green * 0.3576 + $_blue * 0.1805;
        $y = $_red * 0.2126 + $_green * 0.7152 + $_blue * 0.0722;
        $z = $_red * 0.0193 + $_green * 0.1192 + $_blue * 0.9505;
        return (array($x, $y, $z));
    }
    /**
     * [URL unfurl="true"]http://www.emanueleferonato.com/2009/09/08/color-difference-algorithm-part-2/[/URL]
     * @param object $xyz
     * @return 
     */
    private function xyz_to_lab($xyz) {
        $x = $xyz[0];
        $y = $xyz[1];
        $z = $xyz[2];
        $_x = $x / 95.047;
        $_y = $y / 100;
        $_z = $z / 108.883;
        if ($_x > 0.008856) {
            $_x = pow($_x, 1 / 3);
        } else {
            $_x = 7.787 * $_x + 16 / 116;
        }
        if ($_y > 0.008856) {
            $_y = pow($_y, 1 / 3);
        } else {
            $_y = (7.787 * $_y) + (16 / 116);
        }
        if ($_z > 0.008856) {
            $_z = pow($_z, 1 / 3);
        } else {
            $_z = 7.787 * $_z + 16 / 116;
        }
        $l = 116 * $_y - 16;
        $a = 500 * ($_x - $_y);
        $b = 200 * ($_y - $_z);
        return (array($l, $a, $b));
    }
   
    public function color2lab($color) {
        return $this->xyz_to_lab($this->rgb_to_xyz($color));
    }
    
    public function name($color) {
        $color = strtoupper($color);
        if (strlen($color) < 3 || strlen($color) > 7):
            return array("#000000", "Invalid Color: ".$color, "#000000", "", false);
        endif;
        if (strlen($color) % 3 == 0):
            $color = "#" + $color;
        endif;
        if (strlen($color) == 4):
            $_color = '#';
            for ($i = 1; $i <= 3; $i++):
                $_color .= $color[$i];
                $_color .= $color[$i];
            endfor;
            $color = $_color;
        endif;
        
        $result = mysql_query("Select * from colors where hex = '".mysql_real_escape_string(substr($color, 1))."'");
        $row = mysql_fetch_object($result);
        if ($row) {
            $shade = $this->shadergb($row['shade']);
            $row['shadeHex'] = $shade;
            return $row;
        }
        
        $cia = $this->color2lab($color);
        list($cial, $ciaa, $ciab) = $cia;
        $sql = <<<SQL
select 	c1.*, (select c2.hex from colors c2 where c2.name = c1.shade) as shadeHex
from colors c1
order by
			sqrt(
						(
							pow((cial - $cial),2) /* first */		
						)
						+
						(
							pow(
								(
								sqrt(pow(ciaa,2) + pow(ciab,2)) /* c1 */
						 		-
								sqrt(pow($ciaa,2) + pow($ciab,2)) /*c2 */
								)
								/ (1 + (0.045 * sqrt(pow(ciaa,2) + pow(ciab,2))))	/* c1 */
							,2) /* second */	
						)
						+
						(
							pow(sqrt(
							
								pow((ciaa - $ciaa),2) /*da*/
								+
								pow((ciab - $ciab),2) /*db*/
								-
								pow(
									sqrt(pow(ciaa,2) + pow(ciab,2)) 
									-
									sqrt(pow($ciaa,2) + pow($ciab,2))
								,2)
							)	/*dh*/
							/(1 + ( 0.015 * sqrt(pow(ciaa,2) + pow(ciab,2))))
							,2)
						)
				) /* end sqrt */ ASC
LIMIT 1			
SQL;
		$result = mysql_query($sql) or die($sql);
		$row = mysql_fetch_assoc($result);
		return $row;
	}
	
	private function rgb($color) {
        if ($color[0] == '#')
            $color = substr($color, 1);
            
        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);
    }
}
$ntc = new ntc;
$result = $ntc->name('#6195ED');
print_r($result);
?>
 
I should add:
1. converting the original js to php took ten minutes. really.
2. trying to see where the original code was incorrect took about 30 mins before I gave up. it may well be a difference in the way that parseInt and intval() work but I doubt it as I circumvented those differences in some dev code I tried.
3. researching color theory took 15 minutes and locating pre-built conversion functions took 2 minutes. the research was unnecessary but quite interesting.
4. writing the code to build the database took 5 minutes.
5. testing the solution was almost immediate (but it was programmatic testing only)
6. writing the query to optimise the code took 10 minutes.

so I was not quite within my original estimate although I think the spirit was adhered to. abandoning the original js conversion and approaching it fresh took in total 7 minutes to a working prototype and less than 20 to a production level class. including the research augments those figures a bit too.
 
Gosh I think we have a winner!

Its kicking an error if I put 000000 or FFFFFF into it but for everything else it looks to be returning a good result.

I think you have proved your point!

I am going to see if I can figure out why I am getting an error for 000000 (Fatal error: Call to undefined method ntc::shadergb() hp on line 106) and then implement into the project for testing.

James

------------------------
 
that error comes about when there is an exact match. i had not recoded this to avoid the js translation. here is some fresh code

Code:
<?php 

//connect to database
$link = mysql_connect('','','');
mysql_select_db('colors');

class ntc {
    
	/**
     * [URL unfurl="true"]http://www.emanueleferonato.com/2009/09/08/color-difference-algorithm-part-2/[/URL]
     * @param object $color
     * @return 
     */
    private function rgb_to_xyz($color) {
        $rgb = $this->rgb($color);
        $red = $rgb[0];
        $green = $rgb[1];
        $blue = $rgb[2];
        $_red = $red / 255;
        $_green = $green / 255;
        $_blue = $blue / 255;
        if ($_red > 0.04045) {
            $_red = ($_red + 0.055) / 1.055;
            $_red = pow($_red, 2.4);
        } else {
            $_red = $_red / 12.92;
        }
        if ($_green > 0.04045) {
            $_green = ($_green + 0.055) / 1.055;
            $_green = pow($_green, 2.4);
        } else {
            $_green = $_green / 12.92;
        }
        if ($_blue > 0.04045) {
            $_blue = ($_blue + 0.055) / 1.055;
            $_blue = pow($_blue, 2.4);
        } else {
            $_blue = $_blue / 12.92;
        }
        $_red *= 100;
        $_green *= 100;
        $_blue *= 100;
        $x = $_red * 0.4124 + $_green * 0.3576 + $_blue * 0.1805;
        $y = $_red * 0.2126 + $_green * 0.7152 + $_blue * 0.0722;
        $z = $_red * 0.0193 + $_green * 0.1192 + $_blue * 0.9505;
        return (array($x, $y, $z));
    }
    /**
     * [URL unfurl="true"]http://www.emanueleferonato.com/2009/09/08/color-difference-algorithm-part-2/[/URL]
     * @param object $xyz
     * @return 
     */
    private function xyz_to_lab($xyz) {
        $x = $xyz[0];
        $y = $xyz[1];
        $z = $xyz[2];
        $_x = $x / 95.047;
        $_y = $y / 100;
        $_z = $z / 108.883;
        if ($_x > 0.008856) {
            $_x = pow($_x, 1 / 3);
        } else {
            $_x = 7.787 * $_x + 16 / 116;
        }
        if ($_y > 0.008856) {
            $_y = pow($_y, 1 / 3);
        } else {
            $_y = (7.787 * $_y) + (16 / 116);
        }
        if ($_z > 0.008856) {
            $_z = pow($_z, 1 / 3);
        } else {
            $_z = 7.787 * $_z + 16 / 116;
        }
        $l = 116 * $_y - 16;
        $a = 500 * ($_x - $_y);
        $b = 200 * ($_y - $_z);
        return (array($l, $a, $b));
    }
   
    public function color2lab($color) {
        return $this->xyz_to_lab($this->rgb_to_xyz($color));
    }
    
    public function name($color) {
        $color = strtoupper($color);
        if (strlen($color) < 3 || strlen($color) > 7):
            return array("#000000", "Invalid Color: ".$color, "#000000", "", false);
        endif;
        if (strlen($color) % 3 == 0):
            $color = "#" + $color;
        endif;
        if (strlen($color) == 4):
            $_color = '#';
            for ($i = 1; $i <= 3; $i++):
                $_color .= $color[$i];
                $_color .= $color[$i];
            endfor;
            $color = $_color;
        endif;
        
        $result = mysql_query("
Select *, (select c2.hex from colors c2 where c2.name = c1.shade) as shadeHex
FROM colors c1
WHERE c1.hex = '" .mysql_real_escape_string(substr($color, 1))."'");
        $row = mysql_fetch_object($result);
        if ($row) {
            return $row;
        }
        
        $cia = $this->color2lab($color);
        list($cial, $ciaa, $ciab) = $cia;
        $sql = <<<SQL
select 	c1.*, (select c2.hex from colors c2 where c2.name = c1.shade) as shadeHex
from colors c1
order by
			sqrt(
						(
							pow((cial - $cial),2) /* first */		
						)
						+
						(
							pow(
								(
								sqrt(pow(ciaa,2) + pow(ciab,2)) /* c1 */
						 		-
								sqrt(pow($ciaa,2) + pow($ciab,2)) /*c2 */
								)
								/ (1 + (0.045 * sqrt(pow(ciaa,2) + pow(ciab,2))))	/* c1 */
							,2) /* second */	
						)
						+
						(
							pow(sqrt(
							
								pow((ciaa - $ciaa),2) /*da*/
								+
								pow((ciab - $ciab),2) /*db*/
								-
								pow(
									sqrt(pow(ciaa,2) + pow(ciab,2)) 
									-
									sqrt(pow($ciaa,2) + pow($ciab,2))
								,2)
							)	/*dh*/
							/(1 + ( 0.015 * sqrt(pow(ciaa,2) + pow(ciab,2))))
							,2)
						)
				) /* end sqrt */ ASC
LIMIT 1			
SQL;
		$result = mysql_query($sql) or die($sql);
		$row = mysql_fetch_assoc($result);
		return $row;
	}
	
	private function rgb($color) {
        if ($color[0] == '#')
            $color = substr($color, 1);
            
        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);
    }
}
$ntc = new ntc;
$result = $ntc->name('#6195ED');
print_r($result);
?>
 
Hi,

Works perfectly.

I have created a Codeigniter library class file and I am calling this from my upload page.

I am uploading an image and then processing it. Part of the process I am getting the top ten colours from the image. I am then looping through and running each of these colours through this new class to get the 'nice name' and also the 'shade name' etc.

$this->load->library('image_colour_name');
$nice_colour_array = $this->image_colour_name->name('#' . $color);
echo $nice_colour_array['shade']

For some crazy reason I am getting
Fatal error: Cannot use object of type stdClass as array in C:\xampp\htdocs\pdomain_co_uk\system\application\controllers\upload.php on line 173

Does anything jump out at you why I would be getting this for some and not for others?

Thanks so much for your help. Now I have to think how to get some beers to you!

James

------------------------
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top