Replace transparent color in PHP
Asked Answered
Z

2

5

I have set of images. In this images exist green alpha color. I need replace that transparent color to another color (i want replace to white alpha color).

Its my code:

$img = imagecreatefrompng($path . $file); 
$white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
for ($y = 0; $y < imagesy($img); $y++) {
    for ($x = 0; $x < imagesx($img); $x++) {
        $rgb = imagecolorat($img, $x, $y);
        $pixel_color = imagecolorsforindex($img, $rgb);
        if ($pixel_color['alpha'] != 0 && $pixel_color['alpha'] != 127){
            imagesetpixel($img, $x, $y, $white_color_transparent);
        }
    }
}

imagealphablending($img, false);
imagesavealpha($img, true);            
imagepng($img, $path .  $file);

And my result color its the same than id source image, when i add before

imagesetpixel($img, $x, $y, $white_color_transparent);

that line:

imagesetpixel($img, $x, $y, $white_color);

i get only white color without transparency.

Zubkoff answered 20/4, 2012 at 12:28 Comment(2)
Can you provide an input sample image?Jangro
A sample image would be very useful!Tema
E
19

Without seeing your image, I'm not sure there's an easy way to do it.

EDIT 2:

One thing I noticed from my first edit is that it didn't work for an image with transparency and had no opaque layer behind it.

Here is my attempt to deal with an image that has some transparency. As you can see by the example. It didn't work out perfectly.

<?php
//These colors here are the rgba values of the green transparency I used in my test image. You will need to know this to reverse the rgb blending.
$red = 28;
$green = 198;
$blue = 72;
$alpha = .48;

$img = imagecreatefrompng('test.png');

//Adding in the image blending that Zombaya brought up. Need this so it overwrites the pixel instead of blending it
imagealphablending($img, false);
for ($y = 0; $y < imagesy($img); $y++) {
    for ($x = 0; $x < imagesx($img); $x++) {
        $rgb = imagecolorat($img, $x, $y);
        $pixel_color = imagecolorsforindex($img, $rgb);

        //If pixel color matches our alpha layer color, we change it white transparent
        //Not sure how accurate the rounding for the alpha conversion is. Maybe the reason for the green edges in this example:
        if($pixel_color['red'] == $red && 
            $pixel_color['green'] == $green &&
            $pixel_color['blue'] == $blue &&
            $pixel_color['alpha'] == round(127 - ($alpha * 127))){
            $color = imagecolorallocatealpha($img, 255, 255, 255, 127);
        } else {
            //This is the algorithm from the link. But reordered to get the old color before the transparent color went over it.
            $oldR = ($pixel_color['red'] - $alpha * $red) /  (1 - $alpha);
            $oldG = ($pixel_color['green'] - $alpha * $green ) / (1 - $alpha);
            $oldB = ($pixel_color['blue'] - $alpha * $blue) / (1 - $alpha);

            $color = imagecolorallocatealpha($img, $oldR, $oldG, $oldB, $pixel_color['alpha']);
        }
        imagesetpixel($img, $x, $y, $color);
    }
}
imagesavealpha($img, true);            
imagepng($img, 'test_result.png');

enter image description here -> Original without transparency

enter image description here-> Starting image with transparency

enter image description here -> Resulting image. Notice the green edges. And I may have got lucky with using similar opacity for the red and green. The white part is transparent

EDIT:

I thought about this some more and I came across this link: How to calculate an RGB colour by specifying an alpha blending amount?

This helped me understand how to fix your problem. Although this is dependent on if you have just a solid color that is transparent over the image. If it is a gradient it is more difficult.

<?php
//These colors here are the rgba values of the green transparency I used in my test image. You will need to know this to reverse the rgb blending.
$red = 44;
$green = 215;
$blue = 56;
$alpha = .45;

$img = imagecreatefrompng('test2.png');

for ($y = 0; $y < imagesy($img); $y++) {
    for ($x = 0; $x < imagesx($img); $x++) {
        $rgb = imagecolorat($img, $x, $y);
        $pixel_color = imagecolorsforindex($img, $rgb);
            //This is the algorithm from the link. But reordered to get the old color before the transparent color went over it.
        $oldR = ($pixel_color['red'] - $alpha * $red) /  (1 - $alpha);
        $oldG = ($pixel_color['green'] - $alpha * $green ) / (1 - $alpha);
        $oldB = ($pixel_color['blue'] - $alpha * $blue) / (1 - $alpha);

        $color = imagecolorallocate($img, $oldR, $oldG, $oldB);
        imagesetpixel($img, $x, $y, $color);
    }
}
imagesavealpha($img, true);            
imagepng($img, 'test_result.png');

enter image description here - Starting Image

enter image description here - After Image

Here is an explanation of the rgb calculation used in the code above. The link shows the basic formula:

out = alpha * new + (1 - alpha) * old

out is the resulting color after mixing the original with the new color.

So we need to rearrange the formula to get the old color, which yields this:

old = (out - alpha * new) / (1 - alpha)

OLD ANSWER:

Why this line doesn't work:

$white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);

127 means fully transparent, so that means it is invisible. Which is why the result image is the same as the starting image.

Why this line creates a fully white image:

$white_color = imagecolorallocatealpha($img, 255, 255, 255, 0); 

or

$white_color = imagecolorallocate($img, 255, 255, 255);

This basically just places a white pixel over the selected pixel (0 means no transparency), which is why the selected pixels become fully white.

Now if you do the following:

$white_color_semi_transparent = imagecolorallocatealpha($img, 255, 255, 255, 40); 

Instead of replace the transparent green with a transparent white, you know get a lighter green instead of a semi transparent white.

See my test example:

Starting Image - Starting Image

enter image description here - Resulting Image with color 255,255,255,40

So this tells us two things:

  1. First is that colors are basically being applied on top of the existing pixel's color, hence the lighter transparent green instead of a transparent white.
  2. Second, notice that the brown part is not affected. The brown part was actually an opaque red color beneath the green opacity layer. What this says is that the image doesn't take into account layers like photoshop and basically reads the brown color as brown and not a red color behind a green transparent color. And that makes sense since PNG files don't come with layer info.

I'm not sure how you'd solve your issue. It would depend on your image. I think it's possible to get the coloring you want, but it obviously the more complex your image is the more difficult it will be. For example, my two overlapped squares with two colors versus say a photo.

Excitor answered 25/5, 2012 at 18:18 Comment(0)
V
0

I used Gohn67's image as something to work width.

The trick is to turn off alpha blending before trying to transform your image. This way you can use imagesetpixel() to set the pixel to the exact value you want.

As from the manual on imagealphablending:

In non-blending mode, the drawing color is copied literally with its alpha channel information, replacing the destination pixel.

This resulted in this code:

$img = imagecreatefrompng($path.$file);
imagealphablending($img, false); // Turn off blending
$white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
for ($y = 0; $y < imagesy($img); $y++) {
    for ($x = 0; $x < imagesx($img); $x++) {
        $rgb = imagecolorat($img, $x, $y);
        $pixel_color = imagecolorsforindex($img, $rgb);
        if ($pixel_color['alpha'] != 0 && $pixel_color['alpha'] != 127){
            imagesetpixel($img, $x, $y, $white_color_transparent);
        }
    }
}
imagesavealpha($img, true);            
imagepng($img, $path.$file);

Original image

original image

Resulting image

resulting image
(The background is actually transparent, but impossible to see here)

I would like to say that right now, it replaces all transparent colors with this totally transparent white. If you would prefer to replace all colored transparency with a transparent white with the same alpha-value, you would do something like this:

imagesetpixel($img, $x, $y, imagecolorallocatealpha($img, 255, 255, 255, $pixel_color['alpha']));

And to select only the green transparent pixels, I would calculate the hue of each pixel and check if it falls in a specified range.

Voodoo answered 25/5, 2012 at 20:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.