19.7.13

Saturating Images with PHP

Saturating an image using PHP's GD is not an easy task since there's no built-in color space with a separate saturation channel. In other words, if you want to properly saturate an image, you would need to loop through each pixel and convert its RGB values to HSL or HSV or some other color space with a separate saturation channel, then convert back to rgb, and finally use imagesetpixel to piece it all together. Sounds pretty bad and it is. And that's why there's an entry on it in this blog.

Sure, you can fake your way around saturation like most iOS developers do but can you get a result like the one below? Nope. Click on the images for a bigger version and look at the tan that whitey beach bum is sporting in the saturated image. Willy Wonka would be so jealous.

Original, unfiltered image
Saturated image
























And here's the saturate function. A demo of it can be found on the Coloroid Photo Editor where it is a part of the Postcard filter.

 function saturate($filename)  
 {  
 $im = '/path_to_your_/images/'.$filename;  
      if (file_exists($im))  
      {  
           $im = imagecreatefromjpeg($im);  
           if ($im == FALSE)  
           return FALSE;   
           $width = imagesx($im);  
           $height = imagesy($im);  
           //because you really don't want to loop through more that this  
           if ($width >= 900 || $height >= 950)  
           return FALSE;  
           //let the looping begin  
           for($x = 0; $x < $width; $x++)   
           {  
                for($y = 0; $y < $height; $y++)   
                {  
                     $rgb = imagecolorat($im, $x, $y);  
                     $r = ($rgb >> 16) & 0xFF;  
                     $g = ($rgb >> 8) & 0xFF;  
                     $b = $rgb & 0xFF;  
                     $alpha = ($rgb & 0xFF000000) >> 24;  
                     //convert to hsl  
                     list($h, $s, $v) = rgb2hsv($r, $g, $b);  
                     //add a touch of saturation but not too much  
                     $s = $s * 1.5;  
                     if($s > 1) $s=1;  
                     //convert back to rgb  
                     list($r, $g, $b) = hsv2rgb($h, $s, $v);   
                     //piece everything together  
                     imagesetpixel($im, $x, $y, imagecolorallocatealpha($im, $r, $g, $b, $alpha));  
                }  
           }  
           if(imagejpeg($im, 'temp_imgs/'.$filename))  
           return TRUE;  
           imagedestroy($im);  
      }  
 return FALSE;  
 }  

And here are the standart implementations of the RGB to HSV and HSV to RGB algorithms.

 function rgb2hsv($r, $g, $b)   
 {  
      $newR = ($r / 255);  
      $newG = ($g / 255);  
      $newB = ($b / 255);  
      $rgbMin = min($newR, $newG, $newB);  
      $rgbMax = max($newR, $newG, $newB);  
      $chroma = $rgbMax - $rgbMin;  
      $v = $rgbMax;  
      if ($chroma == 0)   
      {  
        $h = 0;  
        $s = 0;  
      }   
      else   
      {  
        $s = $chroma / $rgbMax;  
        $chromaR = ((($rgbMax - $newR)/6) + ($chroma/2))/$chroma;  
        $chromaG = ((($rgbMax - $newG)/6) + ($chroma/2))/$chroma;  
        $chromaB = ((($rgbMax - $newB)/6) + ($chroma/2))/$chroma;  
        if ($newR == $rgbMax) $h = $chromaB - $chromaG;  
        else if ($newG == $rgbMax) $h = ( 1 / 3 ) + $chromaR - $chromaB;  
        else if ($newB == $rgbMax) $h = ( 2 / 3 ) + $chromaG - $chromaR;  
        if ($h < 0) $h++;  
        if ($h > 1) $h--;  
      }  
      return array($h, $s, $v);  
 }
 
 function hsv2rgb($h, $s, $v)   
 {  
      if($s == 0)   
      {  
        $r = $g = $b = $v * 255;  
      }   
      else   
      {  
        $newH = $h * 6;  
        $i = floor( $newH );  
        $var_1 = $v * ( 1 - $s );  
        $var_2 = $v * ( 1 - $s * ( $newH - $i ) );  
        $var_3 = $v * ( 1 - $s * (1 - ( $newH - $i ) ) );  
        if ($i == 0) { $newR = $v ; $newG = $var_3 ; $newB = $var_1 ; }  
        else if ($i == 1) { $newR = $var_2 ; $newG = $v ; $newB = $var_1 ; }  
        else if ($i == 2) { $newR = $var_1 ; $newG = $v ; $newB = $var_3 ; }  
        else if ($i == 3) { $newR = $var_1 ; $newG = $var_2 ; $newB = $v ; }  
        else if ($i == 4) { $newR = $var_3 ; $newG = $var_1 ; $newB = $v ; }  
        else { $newR = $v ; $newG = $var_1 ; $newB = $var_2 ; }  
        $r = $newR * 255;  
        $g = $newG * 255;  
        $b = $newB * 255;  
      }   
      return array($r, $g, $b);  
 }