I've been having some trouble generating an image with the Imagick PHP extension. Everything works fine, except my following "montage" has a white background and therefore I cannot overlay it on top of something else. How can I generate a montage with a transparent background?
$Montage = $Icons->montageImage(new imagickdraw(), "3x2+0+0", "34x34+3+3", imagick::MONTAGEMODE_UNFRAME, "0x0+0+0");
$Canvas->compositeImage($Montage, $Montage->getImageCompose(), 5, 5);
Thanks!!
I know this is an old question, but I found another way to do this using Imagick's montageImage function. After you create your Imagick object you must declare the background as transparent like this:
$Icons->setBackgroundColor( new ImagickPixel('transparent') );
Once that is set, you can run the object through montageImage which will create a montageImage with a transparent background:
$Montage = $Icons->montageImage(new imagickdraw(), "3x2+0+0", "34x34+3+3", imagick::MONTAGEMODE_UNFRAME, "0x0+0+0");
Then you can add the new montage image to your composite image making sure to use the predefined Imagick composite constant COMPOSITE_ATOP or your desired constant(s) like this:
$Canvas->compositeImage($Montage, imagick::COMPOSITE_ATOP, 5, 5);
Just ran across this question and decided to post another solution in case someone else wants another way to do this without a manual loop.
I had the same problem and discovered that the MagickWand C API, which powers imagick), doesn't support the option for montage.
I ended up montaging it manually like this:
// Add them to an array of Imagick objects, instead of using addImage().
$images = new Array();
// Make a transparent canvas.
$target = new Imagick();
$target->newImage($width, $height * count(images), 'none');
$i = 0;
foreach ($images as $image) {
$target->compositeImage($image, imagick::COMPOSITE_COPY, 0, $height * $i++);
}
Related
I am working on a project where the client can create dynamic image by merging different layers (images) into single image and then show that generated image to client. Currently my code is something like this
$output = uniqid().'.jpg';
exec('convert -size 600x800 xc:white '.$output);
$layers = array(//array of about hundreds of PNG images);
foreach($layers as $key => $singleLayer)
{
$layerIs = $singleLayer['image'];
$positionsXY = explode(',', $singleLayer['xy']);
exec('composite -geometry +'.$positionsXY[0].'+'.$positionsXY[1].' '.$layerIs.' '.$output.' '.$output);
}
$base64 = base64_encode(file_get_contents($output));
unlink($output);
return 'data:image/jpeg;base64,'.$base64;
In my above code, I am generating a white background image and then merging multiple PNG images on it these images are more than 100. While also saving this image every time a single layer is merged and then output the final image to client after deleting the generated image. This all takes about 15 seconds to do.
So, My Question is if ImageMagick has an option which can allow me to save image on ram instead of saving image on harddisk which in turn can speed up my processing?
Although I found this http://www.imagemagick.org/discourse-server/viewtopic.php?t=20295 but could not understand much of it that how can I implement it.
The easiest way to use ImageMagick in php is to use the ImageMagick extension & class. I would not recommend using the exec function for it.
So first off, converting the image and store it in a variable.
You say you are not used to variabled, but by your code it seems like you are, you might just not know it.
For example, your $output is a variabe. The variable contains the image after the output is made (if it succeeds).
Now, I'm not 100% sure what your layers array contains, if its images or paths to images, but I expect that its paths, so I'll go with that assumption.
Your first exec call does a convert, I don't think that is nessesery, cause you have no input, all you need is a "white image" from what I can see.
To do that, create a new image object with and create a new image which is the correct size and bg color:
$image = new Imagick();
$image->newImage(600, 800, "white");
You then loop through the layers as you do, but instead of exec you use the imagemagick image you created above:
foreach($layers as $key => $singleLayer) {
$layerIs = $singleLayer['image'];
$positionsXY = explode(',', $singleLayer['xy']);
// Each loop should load the image into a new imagemagick object, but we release it when the scope is exited.
$layer = new IMagick($layerIs);
$image->compositeImage($layer, Imagick::COMPOSITE_DEFAULT, $positionsXY[0], $positionsXY[1]);
}
When the loop is complete, the $image variable will contain the composited image, which you can return as you wish.
$image->setImageFormat('jpg');
return "data:image/jpeg;base64,{base64_encode($image)}";
Note: Code is not tested and written directly in browser. So it might need to be rewritten!
Let's suppose I want to create a face generator, where I would design several pictures for face form, ears, noses, mouth, hair. Given a combination of those pictures how can I create a new picture in PHP? For instance, a simplified version:
There is face1.png, face2.png, nose1.png and nose2.png. How could I programmatically merge face1.png with nose2.png, so the result picture would hold content from both picture?
You've basically got three options: GD, Cairo or ImageMagic. I'd recommend using the Imagick class if it's available. If not, ImageMagick through PHP system calls. If that's not available, GD will probably suffice.
It depends on your server configuration, which of these are available and which would require additional packages to be installed.
There's a very simple example in the Imagick documentation of combining images: https://secure.php.net/manual/en/imagick.compositeimage.php
I also found this example for GD:
Merge two PNG images with PHP GD library
There is a function named imagecopy. This function overrides a part of the destination image using a source image. The given part is specified as parameters. Before you tell me that this does not solve your problem, I have to add that the pixels in the destination picture will not be overriden by the pixels of the source picture if the pixels are transparent. You need to use imagealphablending and imagesavealpha on the source picture, like this:
public static function experimental($images, $width, $height, $dest = null) {
$index = 0;
if ($dest === null) {
$dest = $images[$index++];
}
while ($index < count($images)) {
imagealphablending($images[$index], true);
imagesavealpha($images[$index], true );
imagecopy($dest, $images[$index++], 0, 0, 0, 0, $width, $height);
}
return $dest;
}
If we have these two pictures:
The result will be this:
You really want make it with PHP?
Ok.
1) You can use GD library for image processing.
2) You can use imagemagic PHP wrapper -- imagic.
But I think you should use canvas on client side. And for saving result you can send base64 png image representation of canvas (or separated layers/pictures) to backend.
I have an instance of PHP Imagine:
$imagine = new \Imagine\Imagick\Imagine();
Is there a way to get the wrapped Imagick instance? I need this to remove transparency from an image. If there is no way to get the instance, how can I remove transparency from an image using PHP Imagine?
I got it. The documentation of Imagine is so poor, it's ridiculous...
I had to go into the source code of Imagine to find what I needed:
$imagine = new \Imagine\Imagick\Imagine();
$image = $imagine->open($this->getFile()->getRealPath());
$imagick = $image->getImagick();
Is it save to use the imagick instance?
Here is the working code:
$imagine = new \Imagine\Imagick\Imagine();
$image = $imagine->open($source);
$imagick = $image->getImagick();
if($imagick->getImageAlphaChannel()) { #only if it has transparency
$imagick->setImageBackgroundColor("white");
$imagick = $imagick->flattenImages();
$imagick->writeImage();
$image = $imagine->open($source);
}
$image->strip(); //remove GPS, EXIF, comments, ....
$image->interlace(\Imagine\Image\ImageInterface::INTERLACE_LINE);
$image->save($dest);
This is not the optimal solution, I think. I would need a way to replace the imagick instance of Imagine, but there is no such method. So I am using a workaround:
$imagick->writeImage();
$image = $imagine->open($source);
After removing transparency, I override the original file. Then I reopen the same file with imagine, which means that Imagine is then using the right image (the one without transparency)
I tested it and it works. But I think it is not really the best solution.
How can I remove transparency in Imagick without getting a new imagick instance? This would do the trick, I think.
I have a php script to create jpg thumbnail of pdf as follows;
<?php
$file ="test.pdf";
$im = new imagick(realpath($file).'[0]');
$im->setImageFormat("jpg");
$im->resizeImage(200,200,1,0);
// start buffering
ob_start();
$thumbnail = $im->getImageBlob();
$contents = ob_get_contents();
ob_end_clean();
echo "<img src='data:image/jpg;base64,".base64_encode($thumbnail)."' />";
?>
But the resulting jpg have black background instead of white.. How can I fix this??
None of the previously posted answers worked for me however the below did:
$image = new Imagick;
$image->setResolution(300, 300);
$image->readImage("{$originalPath}[0]");
$image->setImageFormat('jpg');
$image->scaleImage(500, 500, true);
$image->setImageAlphaChannel(Imagick::VIRTUALPIXELMETHOD_WHITE);
As I'm using the Laravel framework I then take the converted image and store it using Laravels filesystem.
Storage::put($storePath, $image->getImageBlob());
Update: So I recently changed OS and whereas this previously worked on my Ubuntu machine on my Mac certain images were still coming out black.
I had to change the script to the below:
$image = new Imagick;
$image->setResolution(300, 300);
$image->setBackgroundColor('white');
$image->readImage("{$originalPath}[0]");
$image->setImageFormat('jpg');
$image->scaleImage(500, 500, true);
$image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
$image->setImageAlphaChannel(Imagick::ALPHACHANNEL_REMOVE);
Seems important to set the background colour before reading in the image. I also flatten any possible layers and remove the alpha channel. I feel like I tried ALPHACHANNEL_REMOVE on my Ubuntu machine and it didn't work so hopefully between these answers readers can find something that works for them.
If your version of Imagick is not up to date, the setImageBackgroundColor may be wrong.
Swap the following line
$im->setImageBackgroundColor("red");
to this (Imagick version >= 2.1.0)
$im->setBackgroundColor(new ImagickPixel("red"));
or (Imagick version < 2.1.0)
$im->setBackgroundColor("red");
I solved it by;
$im = new imagick(realpath($file).'[0]');
$im->setCompression(Imagick::COMPRESSION_JPEG);
$im->setCompressionQuality(100);
$im->setImageFormat("jpeg");
$im->writeImage("imagename.jpg");
Simply adding this prevents the JPG to be created with a black background
-alpha off
change this code $im->setimageformat("jpg"); to this code
$im->setimageformat("png"); if you face a background colour issue.
Just use flattenImages() right after creating a new imagick():
$im = new Imagick('file.pdf[0]');
$im = $im->flattenImages();
Edit: The flattenImages method has been deprecated & removed. Use
$im = $im->mergeImageLayers( imagick::LAYERMETHOD_FLATTEN );
After endless attempts to append a pdf file with a jpeg image without getting black areas, I found the solution: the function transformImageColorspace
Used in this order works perfectly:
$im = new Imagick();
$im->readImage("file.pdf");
$im->transformImageColorspace(Imagick::COLORSPACE_SRGB);
$im->setImageFormat('jpeg');
$im->writeImage('image.jpg');
I would like to add to the excellent and helpful answers by saying that a slightly different approach is required if your PDF has multiple pages.
Something I was surprised to discover is that the Imagick class implements Iterable, which means you can run it through a foreach loop and operate on each page in turn (which it turns out is necessary because the layer, colour, and alpha channel changes appear to only take effect on the last page) which will be presented to you as a separate Imagick object:
$im = new Imagick('path/to/file.pdf');
foreach ($im as $c => $page)
{
// do what you need to do with each page and then...
$im->writeImage('path/to/image-'.$c.'.jpg');
}
Is it possible to take this image:
And apply this mask:
And turn it into this:
Using either GD or Imagick? I know it's possible to mask an image using shapes but I'm not sure how to go on about doing it with a pre-created alphatransparent image. :s
Using Imagick and ImageMagick version > 6 (I don't know if it will work on older versions):
// Set image path
$path = '/path/to/your/images/';
// Create new objects from png's
$dude = new Imagick($path . 'dude.png');
$mask = new Imagick($path . 'dudemask.png');
// IMPORTANT! Must activate the opacity channel
// See: http://www.php.net/manual/en/function.imagick-setimagematte.php
$dude->setImageMatte(1);
// Create composite of two images using DSTIN
// See: http://www.imagemagick.org/Usage/compose/#dstin
$dude->compositeImage($mask, Imagick::COMPOSITE_DSTIN, 0, 0);
// Write image to a file.
$dude->writeImage($path . 'newimage.png');
// And/or output image directly to browser
header("Content-Type: image/png");
echo $dude;
I think you are looking for imagealphablending. I use it for watermarks, and I believe it will do the effect you are looking for.
Great work with (ImageMagick) NOT GD .. I see the tags of this question is GD!!
Here is a GD version at this link:
PHP GD Use one image to mask another image, including transparency