I have the following code to generate an animated GIF from a list of images. It's working fine but I would like to compress it. I tried a lot of different strategies but the resulting size is always exactly the same.
What am I doing wrong?
<?php
namespace AppBundle\Service;
class GifCreator
{
public function createGifFromImages(array $images)
{
// https://stackoverflow.com/a/10095594
$gif = new \Imagick();
$gif->setFormat("gif");
// adds all the images
foreach ($images as $image) {
$frame = new \Imagick();
$frame->readImage($image->getRealPath());
// THE FOLLOWING 2 LINES ARE NOT WORKING
//$frame->setImageCompression(\Imagick::COMPRESSION_JPEG);
//$frame->setImageCompressionQuality(5);
$frame->setImageDelay(50);
$gif->addImage($frame);
}
// optimizes the GIF
$gif->optimizeImageLayers();
// https://hotexamples.com/examples/-/Imagick/optimizeImageLayers/php-imagick-optimizeimagelayers-method-examples.html
// THE FOLLOWING 2 LINES ARE NOT WORKING
//$gif->setImageCompression(\Imagick::COMPRESSION_LZW);
//$gif->setImageCompressionQuality(30);
// writes the GIF
$tempPath = tempnam(sys_get_temp_dir(), 'GIF');
$result = $gif->writeImages($tempPath, true);
return $tempPath;
}
}
It seems that addImage isn't applying the proper filters.
Related
I am Using Imagick to Convert PDF to Images
Uploading PDF to Laravel
public function uploadPdf( Request $request )
{
$model = new Attachment();
if ( $request->hasFile('pdf_file_from_request') ) {
Storage::deleteDirectory('3pagerpdf/pdf');
$pdf = $request->file('pdf_file_from_request');
$fileName = time() . '.' . $pdf->getClientOriginalName();
Storage::putFileAs('3pagerpdf/pdf', $pdf, $fileName);
$model->pdf_file_name_in_datebase = $fileName;
$model->save();
flash()->addSuccess('Pdf Added');
return back();
}
}
Converting that PDF to Images
public function convertpdftoimages()
{
$model = Attachment::first();
set_time_limit(300);
// Get the PDF file from the storage folder
$pdfPath = storage_path('app/3pagerpdf/pdf/'.$model->pdf_file_name_in_datebase);
// Create an Imagick object
$imagick = new Imagick();
// Set the resolution and output format
$imagick->setResolution(300, 300);
$imagick->setFormat('jpeg');
// Read the PDF file into the Imagick object
$imagick->readImage($pdfPath);
// Convert each page of the PDF to an image
foreach ( $imagick as $page ) {
$page->writeImage(public_path("/temp_images/page{$page->getImageIndex()}.jpg"));
}
flash()->addSuccess('Convertion Successfull');
return back();
}
Adding bar code to those images
public function add_bar_code_to_images2()
{
$files = File::files('temp_images');
foreach ( $files as $file ) {
// Generate a barcode image for the file
$barcodeGenerator = new BarcodeGeneratorPNG();
$data = '';
for ($i = 0; $i < 10; $i++) {
$data .= rand(0, 9);
}
$barcodeString = $barcodeGenerator->getBarcode($data, $barcodeGenerator::TYPE_CODE_128);
// Create an image resource from the barcode string
$barcodeImage = imagecreatefromstring($barcodeString);
// Load the original image
$originalImage = imagecreatefromjpeg($file->getPathname());
// Merge the barcode image with the original image
imagecopy($originalImage, $barcodeImage, imagesx($originalImage) - imagesx($barcodeImage) - 10, 10, 0, 0, imagesx($barcodeImage), imagesy($barcodeImage));
// Save the modified image to a new path
imagejpeg($originalImage, 'modified_images/' . $file->getFilename());
}
flash()->addSuccess('Bar Code Added Succesfuully');
return back();
}
Converting those images Back to pdf
public function convertimagestopdf( )
{
// Create a new Imagick object
$imagick = new Imagick();
// Set the resolution of the PDF
$imagick->setResolution(300, 300);
// Iterate over the images in the "modified_images" directory
foreach (glob("modified_images/*.jpg") as $image) {
// Read the image file
$imagick->readImage($image);
}
// Set the format of the PDF to "application/pdf"
$imagick->setImageFormat('pdf');
// Save the PDF to a file
$imagick->writeImages('modified_pdf/document.pdf', true);
flash()->addSuccess('pdf compileed');
return back();
}
What I want is
I want to add bar code to every single page of a pdf but I dont want to use imagick extenstion it needs to be installed on a system to make it work .
I tried the above code
it is taking too long to execute for small pdf's that code it fine but for the pdf with 60 pages and above it starts to give me time out error
Note I am not generating this pdf
the pdf will be uploaded by random user of my application and it will be already a pdf I just need to add barcode to every single page of the pdf .
I will take any alternative solution
in which i dont have to install any application on my computer
like ghostscript
Basicly I'm trying to merge a static image into a gif with Imagick. I think I'm close to it, but the resulting gif so far implements a black image into the gif instead of the image I expect. The dimensions are correct though. This is what I have so far:
<?php
if(!empty($_POST['url'])){
$gif = new Imagick('original.gif');
$img = new Imagick($_POST['url']);
$file_dst = 'result.gif';
$gif = $gif->coalesceImages();
foreach($gif as $frame){
$frame->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
$frame->setImageArtifact('compose:args', "1,0,-0.5,0.5");
$frame->compositeImage($img, Imagick::COMPOSITE_MATHEMATICS, 0, 0);
}
$gif = $gif->deconstructImages();
$gif->writeImages($file_dst, true);
echo "done";
}
?>
Am I missing something that's causing this issue? Or am I doing something wrong?
Hello I am using imagick to morph two images but i am getting the output as GIF with n number of frames. Here is the code
morphImages();
function morphImages()
{
$images = [
"1.png",
"2.png",
];
$imagick = new \Imagick(realpath($images[count($images) - 1]));
foreach ($images as $image) {
$nextImage = new \Imagick(realpath($image));
$imagick->addImage($nextImage);
}
$imagick->resetIterator();
$morphed = $imagick->morphImages(25);
header("Content-Type: image/gif");
$morphed->setImageFormat('gif');
echo $morphed->getImagesBlob();
}
The output i'm getting is this - http://applekaka.com/test.php
Now i want the output as JPEG at 13th frame, How do i do that?
What I want to do is to save only the first frame of an animated GIF in static image (non animated gif).
Using Gmagick 1.1.2RC1 and GraphicMagick 3.1.18
I read heaps of posts talking about it. Some says that it was working in the previous version of Gmagick but not in the new one.
Here is my current test code:
public function testAnimatedGifFrame()
{
$testAnimGif = $this->assets.'/animated.gif';
$im = new \Gmagick($testAnimGif);
$frameNumber = $im->getNumberImages();
$this->assertEquals(47, $frameNumber, 'The number of frame for the animated GIF should be 47');
// Select the first frame and save it
$frameIndex = 0;
$img = $im->coalesceImages();
foreach ($img as $frame) {
die('here');
$frameName = ++$frameIndex . '.gif';
$frame->writeImage( $frameName );
}
}
The animated GIF is composed of 47 frames, but when using coalesceImages(), the script is never getting inside the foreach loop (it's never dying).
Not sure what I've missed here.
For those looking to do it using Imagick or Gmagick like I was.
Just save it as a JPG and BAM!
public function testAnimatedGifFrame()
{
$testAnimGif = $this->assets.'/animated.gif';
$singleFrame = $this->assets.'/test_single_frame.jpg';
$im = new \Gmagick($testAnimGif);
$frameNumber = $im->getNumberImages();
$this->assertEquals(47, $frameNumber, 'The number of frame for the animated GIF should be 47');
// Save the image as JPG to disable the animation
$im->setImageFormat('JPG');
$im->writeImage($singleFrame);
$im->destroy();
}
If you want to save the last image of the animation instead of the first one, you just need to add $im = $im->flattenImages(); before $im->setImageFormat('JPG');
I hope this will help some of you ;)
I am trying to build a script that retrieves a list of thumbnail images from an external link, much like Facebook does when you share a link and can choose the thumbnail image that is associated with that post.
My script currently works like this:
file_get_contents on the URL
preg_match_all to match any <img src="" in the contents
Works out the full URL to each image and stores it in an array
If there are < 10 images it loops through and uses getimagesize to find width and height
If there are > 10 images it loops through and uses fread and imagecreatefromstring to find width and height (for speed)
Once all width and heights are worked out it loops through and only adds the images to a new array that have a minimum width and height (so only larger images are shown, smaller images are less likely to be descriptive of the URL)
Each image has its new dimensions worked out (scaled down proportionally) and are returned...
<img src="'.$image[0].'" width="'.$image[1].'" height="'.$image[2].'"><br><br>
At the moment this works fine, but there are a number of problems I can potentially have:
SPEED! If the URL has many images on the page it will take considerably longer to process
MEMORY! Using getimagesize or fread & imagecreatefromstring will store the whole image in memory, any large images on the page could eat up the server's memory and kill my script (and server)
One solution I have found is being able to retrieve the image width and height from the header of the image without having to download the whole image, though I have only found some code to do this for JPG's (it would need to support GIF & PNG).
Can anyone make any suggestions to help me with either problem mentioned above, or perhaps you can suggest another way of doing this I am open to ideas... Thanks!
** Edit: Code below:
// Example images array
$images = array('http://blah.com/1.jpg', 'http://blah.com/2.jpg');
// Find the image sizes
$image_sizes = $this->image_sizes($images);
// Find the images that meet the minimum size
for ($i = 0; $i < count($image_sizes); $i++) {
if ($image_sizes[$i][0] >= $min || $image_sizes[$i][1] >= $min) {
// Scale down the original image size
$dimensions = $this->resize_dimensions($scale_width, $scale_height, $image_sizes[$i][0], $image_sizes[$i][1]);
$img[] = array($images[$i], $dimensions['width'], $dimensions['height']);
}
}
// Output the images
foreach ($img as $image) echo '<img src="'.$image[0].'" width="'.$image[1].'" height="'.$image[2].'"><br><br>';
/**
* Retrieves the image sizes
* Uses the getimagesize() function or the filesystem for speed increases
*/
public function image_sizes($images) {
$out = array();
if (count($images) < 10) {
foreach ($images as $image) {
list($width, $height) = #getimagesize($image);
if (is_numeric($width) && is_numeric($height)) {
$out[] = array($width, $height);
}
else {
$out[] = array(0, 0);
}
}
}
else {
foreach ($images as $image) {
$handle = #fopen($image, "rb");
$contents = "";
if ($handle) {
while(true) {
$data = fread($handle, 8192);
if (strlen($data) == 0) break;
$contents .= $data;
}
fclose($handle);
$im = #imagecreatefromstring($contents);
if ($im) {
$out[] = array(imagesx($im), imagesy($im));
}
else {
$out[] = array(0, 0);
}
#imagedestroy($im);
}
else {
$out[] = array(0, 0);
}
}
}
return $out;
}
/**
* Calculates restricted dimensions with a maximum of $goal_width by $goal_height
*/
public function resize_dimensions($goal_width, $goal_height, $width, $height) {
$return = array('width' => $width, 'height' => $height);
// If the ratio > goal ratio and the width > goal width resize down to goal width
if ($width/$height > $goal_width/$goal_height && $width > $goal_width) {
$return['width'] = floor($goal_width);
$return['height'] = floor($goal_width/$width * $height);
}
// Otherwise, if the height > goal, resize down to goal height
else if ($height > $goal_height) {
$return['width'] = floor($goal_height/$height * $width);
$return['height'] = floor($goal_height);
}
return $return;
}
getimagesize reads only header, but imagecreatefromstring reads whole image. Image read by GD, ImageMagick or GraphicsMagic is stored as bitmap so it consumes widthheight(3 or 4) bytes, and there's nothing you can do about it.
The best possible solution for your problem is to make curl multi-request (see http://ru.php.net/manual/en/function.curl-multi-select.php ), and then one by one process recieved images with GD or any other library. And to make memory consumption a bit lower, you can store image files on disk, not in memory.
The only idea that comes to mind for your current approach (which is impressive) is to check the HTML for existing width and height attributes and skip the file read process altogether if they exist.