Change "HUE" of an image with PHP GD Library?
Asked Answered
F

2

14

I'd like to change the "hue" of an image using the PHP image processing functions. Which is the correct filter to apply?

Note: I'm from a photoshop background, so in the event my interpretation of "hue" is different from others, here's what I mean...

In photoshop, you can use the "Hue" filter to change the color of an image without actually affecting the design of the image.

I'm currently using the function below to recolor my images, but the function fails to meet my needs due to the fact that it completely repaints the image, losing the design.

    function set_theme_color_header($hex)
    {
        $info = hexToRGB($hex); //utility function that converts hex to rgb
        $token = "header.gif";
        $img = imagecreatefromgif("header-template.gif";
        $color = imagecolorallocate($img, $info["red"], $info["green"], $info["blue"]);
        imagecolorset($img, 0, $info["red"], $info["green"], $info["blue"]);
        imagegif($img, $token);
    }
    ?>
Firman answered 11/12, 2009 at 19:39 Comment(2)
what function do you believe is 'completely repainting the image' ?? what do you mean by 'loses the design' ?Urge
How many questions are you going to post and delete regarding the set_theme_color_header() function?Elias
S
36

I'm pretty sure there are no functions in PHP that can handle the change of hue. But you can write your own function to do this, here are the steps to change the image's hue:

  1. Traverse the image pixel by pixel
  2. Get the RGB color at pixel using imagecolorat()
  3. Transform the RGB value into a HSL value
  4. Change the hue value, leave saturation and lightness alone (Hue is a value from 0 to 360)
  5. Transform the new HSL value back to RGB
  6. Change the color at current pixel

Actually this seems quite an interesting thing to try out, so I might just do this myself and post it here if you don't find any other solution.

EDIT

Wrote the function. To change hue in PHP, you'll need functions to transform between RGB and HSL color spaces and a function that does the traversing of the image. This might be a bit ugly but it works nicely. Quite slow on larger images.

function imagehue(&$image, $angle) {
    if($angle % 360 == 0) return;
    $width = imagesx($image);
    $height = imagesy($image);

    for($x = 0; $x < $width; $x++) {
        for($y = 0; $y < $height; $y++) {
            $rgb = imagecolorat($image, $x, $y);
            $r = ($rgb >> 16) & 0xFF;
            $g = ($rgb >> 8) & 0xFF;
            $b = $rgb & 0xFF;            
            $alpha = ($rgb & 0x7F000000) >> 24;
            list($h, $s, $l) = rgb2hsl($r, $g, $b);
            $h += $angle / 360;
            if($h > 1) $h--;
            list($r, $g, $b) = hsl2rgb($h, $s, $l);            
            imagesetpixel($image, $x, $y, imagecolorallocatealpha($image, $r, $g, $b, $alpha));
        }
    }
}

Here's the required helper functions for converting color space, shamelessly copied from http://www.actionscript.org/forums/showthread.php3?t=50746 with minor alterations:

function rgb2hsl($r, $g, $b) {
   $var_R = ($r / 255);
   $var_G = ($g / 255);
   $var_B = ($b / 255);

   $var_Min = min($var_R, $var_G, $var_B);
   $var_Max = max($var_R, $var_G, $var_B);
   $del_Max = $var_Max - $var_Min;

   $v = $var_Max;

   if ($del_Max == 0) {
      $h = 0;
      $s = 0;
   } else {
      $s = $del_Max / $var_Max;

      $del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
      $del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
      $del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;

      if      ($var_R == $var_Max) $h = $del_B - $del_G;
      else if ($var_G == $var_Max) $h = ( 1 / 3 ) + $del_R - $del_B;
      else if ($var_B == $var_Max) $h = ( 2 / 3 ) + $del_G - $del_R;

      if ($h < 0) $h++;
      if ($h > 1) $h--;
   }

   return array($h, $s, $v);
}

function hsl2rgb($h, $s, $v) {
    if($s == 0) {
        $r = $g = $B = $v * 255;
    } else {
        $var_H = $h * 6;
        $var_i = floor( $var_H );
        $var_1 = $v * ( 1 - $s );
        $var_2 = $v * ( 1 - $s * ( $var_H - $var_i ) );
        $var_3 = $v * ( 1 - $s * (1 - ( $var_H - $var_i ) ) );

        if       ($var_i == 0) { $var_R = $v     ; $var_G = $var_3  ; $var_B = $var_1 ; }
        else if  ($var_i == 1) { $var_R = $var_2 ; $var_G = $v      ; $var_B = $var_1 ; }
        else if  ($var_i == 2) { $var_R = $var_1 ; $var_G = $v      ; $var_B = $var_3 ; }
        else if  ($var_i == 3) { $var_R = $var_1 ; $var_G = $var_2  ; $var_B = $v     ; }
        else if  ($var_i == 4) { $var_R = $var_3 ; $var_G = $var_1  ; $var_B = $v     ; }
        else                   { $var_R = $v     ; $var_G = $var_1  ; $var_B = $var_2 ; }

        $r = $var_R * 255;
        $g = $var_G * 255;
        $B = $var_B * 255;
    }    
    return array($r, $g, $B);
}

And finally, an usage example. This example opens an image, shifts its hue by 180° and outputs to the browser:

header('Content-type: image/png');
$image = imagecreatefrompng('image.png');
imagehue($image, 180);
imagepng($image);

As the angle of hue is set in degrees, giving 0, 360, 720 or any multiple of 360 will result in no change to the image.

Sandhurst answered 11/12, 2009 at 19:48 Comment(6)
Wow! Took you long enough! :-) Thanks a ton Tatu.Firman
Tatu, one thing I'm missing, and perhaps this is not possible, but, let's say, I just want to change the hue of the image from gray (the default) to something very close to #cc0000. Is this possible? Since I can't supply a hex in your function, How do I define the angle needed to achieve that?Firman
Also, in your example you start with a png, then create a jpeg from that. Will this work if I use an indexed gif for each step instead?Firman
If you have a gray image, changing the hue will not do anything (as the saturation is 0, any hue you give will result in a gray image). You might want to look at imagefilter and especially its IMG_FILTER_COLORIZE filter. And yes, you can output any filetype you want, so change the imagejpeg to imagegif for example.Sandhurst
These calculations don't seem to be correct. For RGB->HSL, Hue seems correct but running it through with {123, 21, 31} As the RGB values and checking with various sites results in incorrect lightness and saturation values.Barnsley
One line to add prior to the call to imagepng when saving out the result: imagesavealpha($image, true); Without it the alpha transparency layer is discarded.Woof
H
0

You'd normally convert from RGB to HSL color space, at which point you can manipulate the hue directly. Once you've done what you want, you convert back to RGB.

Hamilton answered 11/12, 2009 at 19:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.