phpimageimage-processinggdgdlib

Change “Saturation” of an image with PHP GD Library?


There is an excellent answer on how to change the HUE of an image using PHP-GD library. But I need to know how to change the SATURATION of an image using PHP-GD. Here is a copy of the code from the answer which successfully changes the HUE of the image.

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));
        }
    }
}

If you need to take a look at the code of helper functions rgb2hsl and hsl2rgb please check the original answer. Since Hue is one of the parameters of HSL, I thought I could modify the function somehow to get a working solution for Saturation. Despite of limited php skills I had to try but it did not work and produced bizarre results. Here is the modification that I am trying.


MODIFIED CODE: UPDATED as suggested by @mark

function imageSaturation(&$image, $saturationPercentage) {
    $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);         
            $s = $s * (100 + $saturationPercentage ) /100;
            if($s > 1) $s = 1;
            list($r, $g, $b) = hsl2rgb($h, $s, $l);            
            imagesetpixel($image, $x, $y, imagecolorallocatealpha($image, $r, $g, $b, $alpha));
        }
    }
}

header('Content-type: image/png');
$image = imagecreatefrompng('rgb.png');
imageSaturation($image, -80);//bring down current image saturation to 80%
imagepng($image);

EFFORTS UPDATE : It has been pointed out to me by @Dai that these lines help construct a color code of an individual RGB pixel. So I guess this part can remained unchanged ?

$rgb = imagecolorat($image, $x, $y);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;            
$alpha = ($rgb & 0x7F000000) >> 24;

In the next line we are simply converting the RGB values to HSL with rgb2hsl($r, $g, $b); and assigning it to list($h, $s, $l). The point I am stuck at the moment are these lines.

$s += $saturationPercentage / 100;
if($s > 1) $s--;

I understand the syntax, but not sure how I handle these or if they are even required. If not an answer helpful hints / suggestions would be great. I am trying the code on this image to bring down the saturation from 100% to 80%, but I am getting this image as result.


Solution

  • I think you need something like

    $s=$s * (100+$saturationPercentage)/100 
    

    else you are just adding a constant to each value rather than a percentage of the existing value.

    Also, where you decrement 1 if your new saturation exceeds 1, you probaly would be better just setting it to 1.0, i.e. fully saturated like this:

    if($s>1)$s=1
    

    otherwise, say the resulting saturation is 1.3 (i.e. very, very saturated) you will make that into 0.3, i.e. very undersaturated, instead of 1.0 (fully saturated).

    So, if $s is 0.7, and you add 10%, you will get

    $s = 0.7 * (100 + 10)/100
    $s = 0.7 * 1.1
    $s = 0.77