PHP Imagick PSD thumbs getting strange artifacts - php

We have a server that accepts PSD files. It will load it into a new Imagick object and create 4 jpg thumbs for it.
Oddly the first thumb, the largest one looks great. Each thumb after that experiences some image distortion where a layer was using a stroke effect.
Code:
$image = new Imagick($fileName);
$image->mergeImageLayers(Imagick::LAYERMETHOD_FLATTEN);
foreach ($thumbSizes as $key => $size) { //largest to smallest
if($size>$longestSide){
$size = $longestSide;
}
$this->image->thumbnailImage($size,$size,true);
$this->image->writeImage($nameBase . '-' . $key . ".$extension");
}
$image->destroy();
I'm not sure how Imagick works internally, but my intuition tells me that if the largest thumb is accurate then each one after that should be.
NOTE: I expect some image distortion when resizing an image. But if you look at the example I posted it's different than your normal resizing artifacts. It's changing the color of some text. I assume it's a problem with it resizing the stroke effect. But I would have thought since I flattened the image first, the stroke effect wouldn't exist anymore. I can pass in a jpeg representation of the same file and it resizes them all perfectly.

I think the PSD conversion is just borked by the library/special FX on the PSD. This is the first image output by similar code to yours:
The color of the number is transparent rather than black - but similarly, not correct. If the issue is being caused by the FX on that layer and you can't find a version of the library that processes the PSD that works correctly, I would suggest not using Imagick/ImageMagick to do the processing, but instead install a copy of Photoshop on the server and use the CLI processing capabilities of Photoshop itself to do the processing: https://helpx.adobe.com/photoshop/using/processing-batch-files.html .
btw an observation - you are resizing the same image repeatedly, this is less than optimal. You can avoid this by resizing a clone of the original image:
foreach ($thumbSizes as $key => $size) { //largest to smallest
$temp = clone $image;
if($size>$longestSide){
$size = $longestSide;
}
$temp->thumbnailImage($size,$size,true);
$temp->writeImage($nameBase . '-' . $key . ".$extension");
}
This should give a slightly higher output image quality.

Related

Save image on RAM and work on it using ImageMagick

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!

fastest way to convert multi page pdf to jpg images

I'm trying to find the fastest way to convert a multiple pdf in to jpeg images without quality loss. I have the following code:
$this->imagick = new \Imagick();
$this->imagick->setresolution(300,300);
$this->imagick->readimage($this->uploadPath . '/original/test.pdf');
$num_pages = $this->imagick->getNumberImages();
for($i = 0;$i < $num_pages; $i++) {
$this->imagick->setIteratorIndex($i);
$this->imagick->setImageFormat('jpeg');
$this->imagick->readimage($asset);
// i need the width, height and filename of each image to add
// to the DB
$origWidth = $this->imagick->getImageWidth();
$origHeight = $this->imagick->getImageHeight();
$filename = $this->imagick->getImageFilename();
$this->imagick->writeImage($this->uploadPath . '/large/preview-' . $this->imagick->getIteratorIndex() . '.jpg');
$this->imagick->scaleImage($origWidth / 2, $origHeight / 2);
$this->imagick->writeImage($this->uploadPath . '/normal/preview-' . $this->imagick->getIteratorIndex() . '.jpg');
//add asset to the DB with the given width, height and filename ...
}
This is very slow thou, partially because the resolution is so big but is i dont add it, the text on the images is of very poor quality. Also, the fact that i'm saving the image first, and then also saving a smaller version of the file is probably not very optimized.
So does anyone have a better method of doing this? Maybe with only ghostscript?
The minimum requirements are that i need 2 versions of the converted image. A real size version and a version at half size. And i need the width and height and filename to add to the database.
You can use Ghostscript, if you set "-sDEVICE=jpeg -sOutputFile=out%d.jpg" then each page will be written to a separate file.
Note that its not really possible to render a PDF to JPEG 'without quality loss' since JPEG is a lossy compression method. Have you considered using a lossless format instead, such as PNG or TIFF ?
To get the same images at different sizes you will need to render the file twice at different resolutions, you set resolution in Ghostscript with '-r'. The width and height can be read easily enough from the image file, or you can use pdf_info.ps to get each page size. Note that a PDF file can contain multiple pages of different sizes, I hope you aren't expecting them all to be the same....

PHP Imagick - convert image to greyscale (very bad result)

I was doing some image editing with PHP, since GD provides less functionalities, I switched to Imagick.
One of the processes is to greyscale images. Everything went fine (locally on Windows 7, Imagick 2.2.1-dev 6.5.8-7 Q16) till I uploaded the script to my web hosting server (Linux, Imagick 3.0.1, 6.2.8, 2010-10-20, Q16).
I'v tried to change the quality, but it didn't improve anything.
$img->setImageCompression(imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality(100);
Here is the results from GD, Imagick and Photoshop
I believe something's wrong with version 3.0.1. Can someone please confirm that?
Q1: Is there an alternative way to convert an image to greyscale with Imagick?
Q2: Is it possible to convert a GD resource to Imagick? So I can use imagefilter($img, IMG_FILTER_GRAYSCALE); to get the correct result and then output with Imagick.
ps: For Q2, you might suggest me to just use GD to process the image. But the problem is that imagejpeg() cannot save images with resolution preserved. and that is actually the reason I switched to Imagick.
This is my preferred way to make a B&W photo in php/imagick: $im = $im->fxImage('intensity');
That applies a function to the image, where intensity is equal to 0.299*red+0.587*green+0.114*blue.
That formula is based on how our eyes are more sensitive to different colours, and as such the difference between that and a "flat" grayscale image really is night and day.
More details here:
http://php.net/manual/en/imagick.fximage.php
http://www.imagemagick.org/script/fx.php
function ImagickToGD($imagick){
$tmpfile = tmpfile();
$imagick->writeImage($tmpfile);
return imagecreatefromstring(file_get_contents($tmpfile));
}
Note that this function does not do any cleanup (except the temp file, which PHP cleans automatically).
So, for example, your code should look like:
$img = new Imagick();
// ...
$gd = ImagickToGD($img);
unset($img); // destroy imagick
imagefilter($gd, IMG_FILTER_GRAYSCALE);
imagejpeg($gd, $target_name, 100);
imagedestroy($gd);
Also, I did not understand the part about "preserving resolution". There is nothing in these operations relating to resolution. My guess is you meant compression? If you want full quality (ie, no compression) simply use 100 as compression value (as I did).
This results in maintaining the existing quality, since opening an image of 70% quality and saving it back with 70% quality actually decreases the final quality by 49% (70% of 70%).
function GDToImagickTo($gd){
$tmpfile = tmpfile();
imagepng($tmpfile); // Png is our best image deal:
// lossless compression, transparency etc..
$imagick = new Imagick()
$imagick->readImage($tmpfile);
return $imagick;
}
Refer this website and check out the image Magick operators found here www.rubblewebs.co.uk/imagemagick/
Also go with www.fmwconcepts.com/imagemagick/ you will find some examples out here...
You can use the image class what you prefer and then use the method readImageBlob to send it to the imagick http://www.php.net/manual/en/imagick.readimageblob.php

PHP Image editing (am I doing it wrong ?)

I need to resize and stitch togehter 7 images, for that I'm using ImageMagick but it's oughly heavy and takes a long time to perform. Is there any other lighter library I can use ?
Or perhaps my code is to blame:
<?php
header('Content-type: image/jpeg');
$thumb = new Imagick();
$thumb->newImage(128*7,128, 'black');
$thumb->borderImage( 'purple', 1, 1 );
$images = new Imagick(glob('*.jpg'));
$counter =0;
foreach($images as $image) {
// Providing 0 forces thumbnailImage to maintain aspect ratio
$image->thumbnailImage(128,128, true);
$thumb->compositeImage($image,Imagick::COMPOSITE_DEFAULT, (128*$counter)+(64-$image->getImageWidth()/2),64-$image->getImageHeight()/2);//echo $image;
$counter++;
}
$thumb->setImageFormat('jpeg');
echo $thumb;
?>
UPDATE:
As it is for an iPhone app I decided to do the resizing and stitching on the iPhone itself (and then upload the result to the server for future use).
I do not use imagick but use Imagemagick command line with php exec() and you can speed up jpg resizing using define as a "hint". I belive it only reads enough of the input image to create the output image.
Anyway there was a post on the Imagemagick forum a couple of weeks ago about speeding up jpg resizing with Imagick and the answer was:
$image = new Imagick();
$image->setOption('jpeg:size', '800x532');
$image->readImage('origional.jpg');
I am not sure how this would work in your case and the original thread is:
http://www.imagemagick.org/discourse-server/viewtopic.php?f=10&t=20586&hilit=speed
As I'm writing an iPhone app and my resources on the server are low I decided to do the "hard" work on the iphone's side.
Daniel

How to compress images in CodeIgniter, yet do not change their dimensions?

I have a site where users can upload images. I process these images directly and resize them into 5 additional formats using the CodeIgniter Image Manipulation class. I do this quite efficiently as follow:
I always resize from the previous format, instead of from the original
I resize using an image quality of 90% which about halves the file size of jpegs
The above way of doing things I implemented after advise I got from another question I asked. My test case is a 1.6MB JPEG in RGB mode with a high resolution of 3872 x 2592. For that image, which is kind of borderline case, the resize process in total takes about 2 secs, which is acceptable to me.
Now, only one challenge remains. I want the original file to be compressed using that 90% quality but without resizing it. The idea being that that file too will take half the file size. I figured I could simply resize it to its' current dimensions, but that doesn't seem to do anything to the file or its size. Here's my code, somewhat simplified:
$sourceimage = "test.jpg";
$resize_settings['image_library'] = 'gd2';
$resize_settings['source_image'] = $sourceimage;
$resize_settings['maintain_ratio'] = false;
$resize_settings['quality'] = '90%';
$this->load->library('image_lib', $resize_settings);
$resize_settings['width'] = $imagefile['width'];
$resize_settings['height'] = $imagefile['height'];
$resize_settings['new_image'] = $filename;
$this->image_lib->initialize($resize_settings);
$this->image_lib->resize();
The above code works fine for all formats except the original. I tried debugging into the CI class to see why nothing happens and I noticed that the script detects that the dimensions did not change. Next, it simply makes a copy of that file without processing it at all. I commented that piece of code to force it to resize but now still nothing happens.
Does anybody know how to compress an image (any image, not just jpegs) to 90% using the CI class without changing the dimensions?
I guess you could do something like this:
$original_size = getimagesize('/path/to/original.jpg');
And then set the following options like this:
$resize_settings['width'] = $original_size[0];
$resize_settings['height'] = $original_size[1];
Ok, so that doesn't work due to CI trying to be smart, the way I see it you've three possible options:
Rotate the Image by 360ยบ
Watermark the Image (with a 1x1 Transparent Image)
Do It Yourself
The DIY approach is really simple, I know you don't want to use "custom" functions but take a look:
ImageJPEG(ImageCreateFromString(file_get_contents('/path/to/original.jpg')), '/where/to/save/optimized.jpg', 90);
As you can see, it's even more simpler than using CI.
PS: The snippet above can open any type of image (GIF, PNG and JPEG) and it always saves the image as JPEG with 90% of quality, I believe this is what you're trying to archive.

Categories