While reading up on the issues merging transparent images in PHP, it seems that everyone is in the "it works" or "it doesn't work" camps. I have found a way to demonstrate each case.
I wish to take a PNG, cut an ALPHA hole in it and then merge it on top of another image. In this case, I cut a hole in an image from google maps, and paste it over a red block. I have two images from google maps. One is of Manhattan and one is of my house. One works, and one does not. The only difference is one was "saved" via a browser.
If you run the code below, you will see the difference in outputs. Does anyone know why the same code would treat two PNG files completely differently? It must be the difference in the PNG files themselves, but then what would it be?
<?php
sometimesworks("https://maps.googleapis.com/maps/api/staticmap?center=40.714728,-73.998672&zoom=12&size=400x400&maptype=hybrid", "google");
sometimesworks("https://lh4.googleusercontent.com/UQvV_dpBa3_rVf25pvLXKD3OwzF4FtPnHBHkzdWqjCQ5mlFqcFfId9echIgDMv_xYRRYzLaKEXphw7g=w2447-h1106", "myhouse");
function sometimesworks($p_image, $p_prefix)
{
$image_top =imagecreatefrompng($p_image);
$brush = imagecolorallocate($image_top, 0, 0, 0); //create a black brush
imagefilledpolygon($image_top, explode(",", "10,10, 120,22, 80,280, 200, 191"), 4, $brush); //fill a polygon
imagecolortransparent($image_top, $brush); //turn the black to be transparent
imagepng($image_top, './'.$p_prefix.'.transparent.png'); //save the file to confirm that it is working.
//create a big red square
$image_bottom = imagecreatetruecolor(imagesx($image_top), imagesy($image_top));
$red = imagecolorallocate($image_bottom, 255, 0, 0);
imagefill($image_bottom, 0, 0, $red);
imagepng($image_bottom, './'.$p_prefix.'.red.png');
//merge the top onto the bottom.
$out = imagecreatetruecolor(imagesx($image_top), imagesy($image_top));
imagecopy($out, $image_bottom, 0, 0, 0, 0, imagesx($image_top), imagesy($image_top));
imagecopy($out, $image_top, 0, 0, 0, 0, imagesx($image_top), imagesy($image_top));
imagepng($out, './'.$p_prefix.'.out.png');
}
The issue with the above code is that the PNG files were of different type. One was a truecolor image and the other was a palette. The code to merge files differs depending on what is being done.
This is a good reference for the merging of "palette" based PNG files with transparency.
Related
I'm trying to create a photo collage in php by combining two images, a background and an image.
Certain part of my background is transparent, I want to put the image into that part of the background and merge them. The transparent part could be a square, circle, oval rectangle or any other shape. I tried the following code, but it doesn't work
$image_1 = imagecreatefrompng('images/background.png');
$image_2 = imagecreatefrompng('images/image.png');
imagealphablending($image_1, true);
imagesavealpha($image_1, true);
imagecopy($image_1, $image_2, 0, 0, 0, 0, 100, 100);
imagepng($image_1, 'images/3.png');
The code above combines the two images, but with one of them being very small, and the other take up the whole screen which is not what I wanted. I need to have the the image fit into the transparent part of the background of the newly combined image.
I would like to know and find out, how I can colorize/replace any pixel of an image, that is not (fully) transparent with an opaque pixel.
For example, having a multicolored Logo with transparent pixels, I would like to convert it in to a logo with only the color #ff0000, and not change the transparent background.
I want to achieve this with the PHP Imagick Library. I cannot find any good documentation.
I thought that Imagick::thresholdImage would be a helper, but there is no documentation about the threshold parameter.
Best results are achieved with this fragment of code. But still not working perfectly. Some pixels - i guess those with alpha > 0 and < 1 are not replaced.
$image = new \Imagick($source);
$image->setImageFormat('png');
$fill = new \ImagickPixel('#ff0000');
$image->thresholdImage(0);
$image->paintOpaqueImage('#ffffff', $fill, 1);
$image->writeImage($destination);
I would like to know and find out, how I can colorize/replace any pixel of an image, that is not (fully) transparent with an opaque pixel.
You almost certainly don't.
The code below does what you are asking (I think) and the output looks terrible. Perhaps you should give an example input image, and a hoped for example output image, that you have edited in Photoshop, to show what you were hoping for.
Input image:
Output image:
$imagick = new Imagick("fnord.png");
// Get the alpha channel of the original image.
$imagick->separateImageChannel(\Imagick::CHANNEL_ALPHA);
// Make all the colors above this pure white.
$imagick->whiteThresholdImage("rgb(254, 254, 254)");
// Make all the colors below this pure black.
$imagick->blackThresholdImage("rgb(254, 254, 254)");
// We want the mask the other way round
$imagick->negateImage(false);
$imagickCanvas = new \Imagick();
$imagickCanvas->newPseudoImage(
$imagick->getImageWidth(),
$imagick->getImageHeight(),
"xc:rgb(255, 0, 0)"
);
// Copy the mask back as the alpha channel.
$imagickCanvas->compositeImage($imagick, \Imagick::COMPOSITE_COPYOPACITY, 0, 0);
// Write out the image.
$imagickCanvas->setImageFormat('png');
$imagickCanvas->writeImage("./output.png");
This is blowing my mind. I want to merge 2 transparent PNGs.
One is a circle ($source) generated on the fly from a square image.
The second is a map marker ($marker) with a transparent circle to fit the first behind.
imagepng($source); and imagepng($marker); outputs just as one might expect.
With transparent backgrounds.
I then create an empty transparent image the size of the marker to place them both together:
//NEW BLANK TRANSPARENT IMAGE
$dest = imagecreatetruecolor(50, 61);
$transparent = imagecolorallocatealpha($dest, 0, 0, 0, 127);
imagefill($dest, 0, 0, $transparent);
imagealphablending($dest, true);
imagesavealpha($dest,true);
//COPY THE CIRCLE
imagecopy($dest, $source, 5, 5, 0, 0, 41, 41);
//AND THE MARKER ON TOP
imagecopy($dest, $marker, 0, 0, 0, 0, 50, 61);
In the result, the circle appears as a black square with the circle inside.
I tried a lot of combinations of imagealphablending and imagesavealpha both on $source and $dest and nothing seems to work.
How can I remove the black square and leave the $source transparent in the result as it is before the merge?
The problem here was:
To generate the circle on the fly, I was creating a circular mask and making everything else transparent making it red and then making red transparent.
This is incompatible with imagesavealpha(true) as it says the gd engine exactly to ignore transparent colors and save the whole alpha channel instead.
When merging both images, the color once transparent is now black.
The solution to my case was to analise it pixel per pixel and copy it to a blank image if it's inside the circle using the circular equation.
Ive been working on this project for days now and for some reason just not able to get rid of an unexpected 1px by 1px (approx) white spot that apears in each and every tile that is processed.
Summary:
I am using an original image ( say original.jpeg ) as referance to create a mosaic image ( say mosaic.jpeg which is approx 1000px by 1000px ) by merging totegher much much smaller jpeg images ( aprox 10px by 10px ).
I have a data set of approx 20,000 tile images to work with.
Process so far
I have mapped the original.jpeg image to cut it up into 5px by 5px tiles, then found the average color of each tile and saved it for further use.
I have scanned all the (10x10) tile images and stored their individual average colors as well.
i have calculated which tile image would fit closest to which tile of the original image by using Weighted ref : euclidean distance from the site mentioned.
I have managed to use PHP gd library to create a new truecolor image with all the matching tiles placed in the right position ( thus effectively creating a mosaic of the original.jpeg image )
The Problem
I am not getting a clear mosaic i had expected, for some reason the tiles do not match properly.
The Workaround
Now due to lack of time i an using a quick fix where in i take the original image, give it some 50% opacity and overlay it on top of each tile while it is being placed into the final mosaic.
NOTE: Although i am effectively overlaying the original image over the mosaic image, im NOT doing it in one go. The overlaying HAS to happen at each tile level.
So in short : Before placing each tile at the correct position of the final mosaic, i do the following
1. get that particular section of the original image ( 5x5px )
2. expand it to match the final tile size ( 10x10px )
3. set transparency for the section
4. place it over the tile that will be placed
5. merge this new tile over the final mosaic at the corresponding position.
Here is the function i have created to overlay part of the image along with transparency set to it.
public function overlay($dImg, $sImg, $opacity = null) {
// set default Opacity if not specified
$opacity = (is_null($opacity)) ? $this->opacity : $opacity;
// get width, height of sourceImage
$sWidth = imagesx($sImg);
$sHeight = imagesy($sImg);
// get width height of final image
$dWidth = imagesx($dImg);
$dHeight = imagesy($dImg);
$image = imagecreatetruecolor($dWidth, $dHeight);
imagecopyresampled($image, $sImg, 0, 0, 0, 0, $dWidth, $dHeight, $sWidth, $sHeight);
$background = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefill($image, 0, 0, $background);
imagealphablending($image, true);
imagecopymerge($dImg, $image, 0, 0, 0, 0, $dWidth, $dHeight, $opacity);
imagedestroy($image);
return $dImg;
}
THE REAL PROBLEM
in theory all of this seems perfectly fine. But the results have their own say in the matter.
I noticed an unusual almost 1x1px white patch at the start of every tile of the final mosaic.
This white patch only appears when the transparency technique above is applied. It does not happen otherwise.
I am clueless as to why this is happening, Due to this white patch the entire image looks like it has white noise all over it and degrades the overall quality immensely.
Please guide me in any direction as to why something like this could be happening.
your problem lies on these two lines:
$background = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefill($image, 0, 0, $background);
you don't need those, because imagefill is used to fill an area that have same/similar color with the color located on the supplied coordinate, in your case 0, 0 (top left), when there is no adjacent similar color, then it just change the color at the given coordinate.
you can use imagefilledrectangle instead, but i still think you don't need that, just comment out those 2 lines and see the result if it match your requirement, if not, then go ahead use imagefilledrectangle
imagefilledrectangle
Not sure why it looks so bad, but when I copy an image over another image, it looks extremely terrible, like it lost almost all its colors for some reason.
$img = imagecreate(240, 140);
$wall = imagecreatefrompng($src);
imagecopyresampled($img, $wall, 0, 0, 40, 340, 240, 140, 240, 140);
I've been trying to find a solution myself but I can't seem to find one, anybody really good with GD that can help? I can't use imagemagick.
You need to create your canvas image with imagecreatetruecolor() rather than imagecreate() as the former creates a palette based canvas with limited colour support.
I presume you are then using imagepng() to save our output the image. The third argument accepted by this function defines the quality of the image, or the compression level (0-9)