Merging animated Gif into background image - php

I'm trying to merge an animated gif into a static jpeg as a background, this is being generated by html canvas and a file upload that the user uploads. I had it working great with imagemagick command line
exec("convert ".$_paths['images_dir'].$fname.".gif \( gif/cleared_dance".$dance.".gif -resize '80%' -repage 0x0-10+$imgHeight\! \) -coalesce -delete 0 -deconstruct -loop 0 ".$_paths['images_dir'].$fname.".gif");
this was taking about 15~ seconds to process which was acceptable, but then I found out I can't use the exec command and must use Imagick API, I was eventually able to recreate the process using the following code:
$data = $_POST["imgData"];
$dance = $_POST["dancer"]+1;
$spriteLeft = $_POST['left'];
$spriteTop = $_POST['top'];
$scale = $_POST['scale'];
$canvas = new Imagick();
$data = str_replace("data:image/png;base64,", "",$data);
$data = base64_decode($data);
$canvas->readImageBlob($data);
$fname = 'tmp_'.uniqid();
$gif = new Imagick();
$gif->readImage('gif/cleared_dance'.$dance.'.gif');
do {
$canvas->addImage($gif);
} while ($gif->nextImage());
foreach($canvas as $frame){
if($frame->getIteratorIndex() != 0){
$page = $frame->getImagePage();
$frame->setImagePage(200,200,$page['x']+$spriteLeft,$page['y']+$spriteTop);
}
}
This code takes about 5 seconds which is great but their is a blank frame in the gif when it loops that was super annoying so i added this after:
$canvas->setIteratorIndex(0);
$canvas->removeImage();
$canvas = $canvas->deconstructImages();
$canvas->writeImages('tmp/'.$fname.'.gif', true);
This takes out the blank frame, but adds about 20 - 30 seconds of processing. Am I doing something wrong, I've played around a good bit with the order of the code and trying different approaches. Is there a way to get the processing time back under 20 seconds like it was with the command line?

Your code is quite odd. Why are you setting the format of the Imagick object that is going to contain an animated gif to JPG? Why are you fiddling with the image page after you composited the image?
I'm pretty sure you want your code to look a lot more like
$static = new Imagick();
//$static->readImage("./testFile.png");
$inputGif->readImageBlob($data);
$inputGif = new Imagick();
//$inputGif->readImage('./testGif.gif');
$inputGif->readImage('gif/cleared_dance'.$dance.'.gif');
$gifFrames = $inputGif->coalesceImages();
$outputGif = new Imagick();
$outputGif->setFormat('gif');
foreach ($gifFrames as $gifFrame) {
$frame = clone $static;
$frame->compositeimage(
$gifFrame,
Imagick::COMPOSITE_OVER, // or maybe \Imagick::COMPOSITE_ATOP,
0, 0 // Set the offset here if you don't want the gif in the top left corner.
);
$outputGif->addImage($frame);
}
$outputGif->setImageIterations(0); //loop forever
$outputGif->deconstructImages();
$outputGif->writeImages("./outso.gif", true);

Related

Set background colour in PHP Imagick montage function

I am composing an image from 6 images using this code:
$imagick = new Imagick();
foreach ( $productImages as $productImage ) {
$imagick->addImage(new Imagick($productImage));
}
$categoryCollage = $imagick->montageImage(
$categoryImage,
"3x2+0+0",
"150x100+2+2",
Imagick::MONTAGEMODE_CONCATENATE,
"1x1+2+2"
);
Below the result when images with different size and ratio are added. The background colour is grey. How to set it to white?
According to the manual with the command line version it would be the -background parameter but I don't know how to set it in PHP:
https://legacy.imagemagick.org/Usage/montage/
I found a very trivial solution where the grey background disappears:
foreach ( $productImages as $productImage ) {
$subImage = new Imagick($productImage);
$subImage->resizeImage(150, 100, Imagick::FILTER_BOX, 1);
$imagick->addImage($subImage);
}
I just resize the image to the size which will be then anyway used in the collage and as each original image is bigger, there is no an up-scaling issue.
Using then the Imagick::MONTAGEMODE_FRAME constant it looks then quite OK.

Remove background after comparing two images with Imagick

i'm new using imagick with php and i have a problem:
I'm comparing two images with the compareImages() function. I'm using the same code as in the documentation page:
<?php
$image1 = new imagick("image1.png");
$image2 = new imagick("image2.png");
$result = $image1->compareImages($image2, Imagick::METRIC_MEANSQUAREERROR);
$result[0]->setImageFormat("png");
header("Content-Type: image/png");
echo $result[0];
?>
This code is working right, but the result image has a background with the original image with a bit of opacity.
I've been searching and I found the result that I want but with imagemagick commands:
http://www.imagemagick.org/Usage/compare/
My current result is like the image of the first command ("compare bag_frame1.gif bag_frame2.gif compare.gif") and I want a result like the shown in this command:
compare bag_frame1.gif bag_frame2.gif \
-compose Src compare_src.gif
Is there any way of doing this with the compareImages() function of imagick?
Thanks
You'll need to use Imagick::SetOption before reading the first image.
<?php
$image1 = new imagick(); // Init empty object
$image2 = new imagick("image2.png");
// Set Lowlight (resulting background) to transparent, not "original image with a bit of opacity"
$image1->setOption('lowlight-color','transparent');
// Switch the default compose operator to Src, like in example
$image1->setOption('compose', 'Src');
// Now read image
$image1->readImage("image1.png");
// Result will not have a background
$result = $image1->compareImages($image2, Imagick::METRIC_MEANSQUAREERROR);

Imagick Resize, Center, and Sparse Fill problems

My end goal here is to resize the input image to 100px width, 125px height. Some of the input images are a different Aspect Ratio, so I wish for them to be in a 100x125 container with the background sparse filled from their edge color.
Ok, so this works for the basic resize:
$image = new Imagick($imgFile);
$image->resizeImage(100,0, Imagick::FILTER_LANCZOS, 1, false);
$image->writeImage("$Dir/$game.png");
header("Content-type: ".$image->getImageFormat());
echo $image;
$image->clear();
$image->destroy();
However I've been searching for hours, and I cannot find a simple "This is how you center an image in a canvas" bit for PHP's Imagick library. Everything is for the actual ImageMagick convert application, which is not really what I'm after. I've tried compositing the resized image into an empty newImage with the set width and height, but it just seems to overwrite the dimensions regardless of the composite type, setting the Gravity to center and then the extent to 100x125 has no effect ( It always sits at 0,0, and trying to set the y offset to ((125-imageheight)/2) resulted in an offset that was way more than it should have been )
Edit:
$imageOutput = new Imagick();
$image = new Imagick($imgFile);
$image->resizeImage(100,0, Imagick::FILTER_LANCZOS, 1, false);
$imageOutput->newImage(100, 125, new ImagickPixel('black'));
$imageOutput->compositeImage($image, Imagick::COMPOSITE_ADD, 0, ((125 - $image->getImageHeight()))/2 );
$imageOutput->setImageFormat('png');
$imageOutput->writeImage("$Dir/$game.png");
header("Content-type: ".$imageOutput->getImageFormat());
echo $imageOutput;
$image->clear();
$image->destroy();
So I got my centering working, gravity apparently has no effect on actual images.
I have absolutely no idea where I would even begin to try and recreate a command line edge-in sparse fill in PHP with the library.
I ended up using a combination of Imagick and shell calls to convert itself, I'll eventually rewrite it to use entirely shell calls. I also changed my dimensions, here's the code:
$imageOutput = new Imagick(); // This will hold the resized image
$image = new Imagick($imgFile); // Open image file
$image->resizeImage(120,0, Imagick::FILTER_LANCZOS, 1, false); // Resize it width-wise
$imageOutput->newImage(120, 150, "none"); // Make the container with transparency
$imageOutput->compositeImage($image, Imagick::COMPOSITE_ADD, 0, ((150 - $image->getImageHeight())/2) ); // Center the resized image inside of the container
$imageOutput->setImageFormat('png'); // Set the format to maintain transparency
$imageOutput->writeImage("$Dir/$game.temp.png"); // Write it to disk
$image->clear(); //cleanup -v
$image->destroy();
$imageOutput->clear();
$imageOutput->destroy();
//Now the real fun
$edge = shell_exec("convert $Dir/$game.temp.png -channel A -morphology EdgeIn Diamond $Dir/$game.temp.edge.png"); // Get the edges of the box, create an image from just that
$shepards = shell_exec("convert $Dir/$game.temp.edge.png txt:- | sed '1d; / 0) /d; s/:.* /,/;'"); // get the pixel coordinates
$final = shell_exec("convert $Dir/$game.temp.edge.png -alpha off -sparse-color shepards '$shepards' png:- | convert png:- $Dir/$game.temp.png -quality 90 -composite $Dir/$game.jpg"); // Sparse fill the entire container using the edge of the other image as shepards , then composite that on top of this new image
unlink("$Dir/$game.temp.png"); // cleanup temp files
unlink("$Dir/$game.temp.edge.png");
set_header_and_serve("$Dir/$game.jpg"); // serve the newly created file

PDF to JPG Imagic page selection

Loads of answers on how to do it for a command line
convert /path/to/file/file.pdf[3] output.jpg
great... but what if I am using in memory processing, I am generating PDF with PDFlib and then output its buffer to a function that I want to generate jpg preview of selected page. How? My code :
[...]
$buf = $pdf->get_buffer();
//$buff is just a PDF stored in a string now.
$im = new Imagick();
$im->readimageblob($buf);
$im->setImageFormat("jpg");
$im->setimagecompressionquality(60);
$len = strlen($im);
header("Content-type: image/jpeg");
header("Content-Length: $len");
header("Content-Disposition: inline; filename=test.jpg");
echo $im;
This creates a jpeg but always returns last page of the PDF. I want to be able to choose which one will be converted. Is it doable without saving temporary files and using command line (exec('convert /path/to/file/file.pdf[3] output.jpg')) syntax?
Let me add that I tried
$im->readimageblob($buf[2]);
and it did not work :)
For the ones who is still searching for solution of reading specific page number from blob, please check this question Creating array populated by images from a PDF using PHP and ImageMagick
$img_array = array();
$im = new imagick();
$im->setResolution(150,150);
$im->readImageBlob($pdf_in);
$num_pages = $im->getNumberImages();
for($i = 0;$i < $num_pages; $i++)
{
$im->setIteratorIndex($i);
$im->setImageFormat('jpeg');
$img_array[$i] = $im->getImageBlob();
}
$im->destroy();
I'm loading the PDF binary into memory from Amazon S3 and then selecting the specific page I want using setIteratorIndex() followed by getImage()
function get_image_from_pdf($pdf_bytes, $page_num){
$im = new \Imagick();
$im->setResolution(150, 150);
$im->readImageBlob($pdf_bytes);
$im->setIteratorIndex($page_num);
$im = $im->getImage();
$im->setImageFormat('png');
return $im->getImageBlob();
}
version 3.0.1
being on the last image or first image in imagic object
$image_obj = new Imagick("test.pdf"); then you on last image in $image_obj
if you use
fp_pdf = fopen("test.pdf", 'rb');
$image_obj = new Imagick();
$image_obj -> readImageFile($fp_pdf);
then you on the first image in $image_obj
In second case to switch to last image you can do
fp_pdf = fopen("test.pdf", 'rb');
$image_obj = new Imagick();
$image_obj -> readImageFile($fp_pdf,2); // 2 can be any positive number?
then you on the last image in $image_obj
echo $image_obj->getNumberImages() // Returns the number of images in the object
then
if ($image_obj->hadPreviousImage)
$image_obj->previousImage() //Switch to the previous image in the object
}
or
if ($image_obj->hasNextImage()) {
$image_obj->nextImage()) //Switch to the next image in the object
}
e.g. if you have 6 images total and you need 4th then do from the end
$image_obj->previousImage()
$image_obj->previousImage()
$image_obj->setImageFormat("png");
header("Content-Type: image/png");
echo $image_obj;
EDIT: Another find is that you can
foreach($image_obj as $slides) {
echo "<br>".$Obj_img->getImageWidth();
//or wehatever you need to do.
}
EDIT 2: Very simple solution would be to use this function $image_obj->setIteratorIndex(4) count starts with zero.
It's not good news unfortunately, but I can definitively say that, as of time of writing, the ImageMagick (and PHP libraries) don't support the page notation that you're trying to use. (For people from the future finding this: I'm checking php-imagick-3.0.1 and imagemagick-6.6.0.4).
I'm trying to do the exact same thing as you, and I've just spent the last few hours trawling through the source, trying to figure out what it does and how it gets the pages, and it looks like it simply won't use it when reading from a stream (ie. the readBlob() call).
As such, I'm just going to be putting it in a temporary file and reading it from there instead. Not as elegant, but it'll work.

PHP GD Move Picture Up 1 Px

I need to take an image and move it upwards 1 px in certain situations for my code, but with what GD function would I use to do that? I couldn't find another question that asked this, so I asked it. But the middle of the picture is a number and the background is transparent, and the height and width is almost always different
Here's an example. The key part is imagecopymerge() function. play with it's 0,0,1,0 values.
<?php
$src = imagecreatefromgif($img);
list($w,$h) = getimagesize($img);
$sprite = imagecreatetruecolor($w,$h);
$trans = imagecolortransparent($sprite);
imagealphablending($sprite, false);
imagesavealpha($sprite, true);
imagepalettecopy($sprite,$src);
imagefill($sprite,0,0,imagecolortransparent($src));
imagecolortransparent($sprite,imagecolortransparent($src));
imagecopy($sprite,$src,0,0,1,0,$w,$h);
imagegif($sprite,$img);
imagedestroy($sprite);
imagedestroy($src);
?>

Categories