How to make thumbnails using Imagick::resizeImage appear sharper - PHP? - php

I am using Imagick::resizeImage to create a thumbnail PNG image of each page of a pdf file. However, the image I am getting back is really blurry. How can I make it a little sharper? Any pointers would be really appreciated.
I have tried adjusting the 'blur' paramter of Imagick::resizeImage between 0.1 - 1, without success.
$pdfPage = '1Mpublic.pdf[0]';
$im = new imagick($pdfPage);
$im->setImageFormat('png');
// Resize thumbnail image
$imgHeight = $im -> getImageHeight();
$imgWidth = $im -> getImageWidth();
$desiredWidth = 200;
$desiredHeight = resizedImageHeight($imgWidth, $imgHeight, $desiredWidth);
$im -> setResolution(1500, 1500);
$im -> resizeImage($desiredWidth, $desiredHeight, imagick::STYLE_NORMAL, 0.1);
/* Resize image */
function resizedImageHeight($imgWidth, $imgHeight, $desiredImgWidth){
$quoient = $imgWidth/$imgHeight;
$height = $desiredImgWidth/$quoient;
return $height;
}
original pdf link:
https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4905263/pdf/ksgt-06-04-1091539.pdf

Rather than rendering and then resizing the raster, it might be better to render the PDF to the right number of pixels in the first place. It'll be faster, and you can be sure that the amount of sharpness is correct for the content.
For example:
$ time convert -density 50 ksgt-06-04-1091539.pdf[0] x2.png
real 0m0.325s
user 0m0.299s
sys 0m0.024s
Makes:
The -density 50 makes a page about the same number of pixels across as your sample, 425.
In imagick you could do it like this (as #fmw42's excellent answer already says):
#!/usr/bin/env php
<?php
$target_width = 400;
# get natural size, calculate density we need for target width
$im = new imagick();
$im->pingImage($argv[1]);
$geo = $im->getImageGeometry();
$natural_width = $geo['width'];
$density = 72.0 * $target_width / $natural_width;
# load at correct resolution for target_width
$im = new imagick();
$im->setResolution($density, $density);
$im->readImage($argv[1]);
# and write back
$im->writeImage($argv[2]);
Doing both the ping and the read is a little slow in imagick, unfortunately:
$ time ./pdfthumb.php ksgt-06-04-1091539.pdf x.png
real 0m2.773s
user 0m2.737s
sys 0m0.036s
It's not imagick, but vipsthumbnail can do the ping and read in one operation:
$ time vipsthumbnail ksgt-06-04-1091539.pdf -s 400x -o x2.png
real 0m0.064s
user 0m0.064s
sys 0m0.011s
It might be worth considering if speed is important. libvips has a php binding so you can call it directly, but if you do that you'll run into awful licensing problems because it uses the GPL library poppler for PDF rendering, sigh. ImageMagick uses GhostScript and shells out to that for the same reason.

Unfortunately, I do not know Imagick that well. But in Imagemagick command line, I would do what is sometimes called supersampling. That is use a large density to read the PDF, then resize down by the inverse scale factor.
For example, nominal density is 72 dpi. I would read the input PDF at 4*72=288 dpi. Then after rasterizing, I would resize by 1/4=25% or for a larger result by something larger than 25%, say 50%. Here is your first page done both ways:
convert -density 288 ksgt-06-04-1091539.pdf[0] -resize 25% result1.png
convert -density 288 ksgt-06-04-1091539.pdf[0] -resize 50% result2.png
In Imagick, the key is something like:
$imagick = new Imagick();
$imagick->setImageResolution(288, 288);
$imagick->readImage('myfile.pdf');

Related

My php image server using Imagick produces much larger png8 files after cropping / scaling down

I'm trying to set up a simple PHP image server to allow me to add just large file for each of my images and then scale and crop them as needed. For my test file I start with a png8 exported via "save for web" from illustrator of size 2400 x 1200, which has a filesize of 21.6KB.
When I use Imagick's cropThumbnailImage function to reduce it to 600 x 600 the resulting file is 62.1KB (three times the size for a substantially smaller image). A 600 x 600 crop of the same image saved from illustrator clocks in at about 8.2KB. I can accept a modest file size increase for the added convenience, but an ~8x increase is just too much.
When saving the file I make sure to force the output to png8 so it doesn't default to a lossless png format, but other than that I'm clueless as to how to resolve it.
Here is my processing code:
//create working image
$image = new Imagick( $this->orig_file );
// Set Compression
$image->setImageCompressionQuality( 9 );
//scale image to height
$image->cropThumbnailImage ( $this->w, $this->h );
// strip extra data
$image->stripImage();
// save file
$image->writeImage( 'png8:'.$this->output_file );
Here are my test files:
Original Full scale image outputted by illustrator.
Cropped 600 x 600 image generated by imagick.
[EDIT: As per Mark's suggestion below I added the following changes]
// replacing cropThumbnailImage with:
$image->resizeImage(0, $this->h, imagick::FILTER_TRIANGLE, 1);
// crop
$start = ($image->getImageWidth() / 2) - ($this->w / 2);
$image->cropimage($this->w, $this->h, $start, 0);
// reduce colors to 20
$image->quantizeImage($this->q, 1, 0, true, false); // using 20 as $this->q
The end result goes from 62.1KB to 50.4KB, better but still over double the size of the fullsized image, and many times larger that the illustrator save for web version at that size.
600x600 image reduced to 20 colors and resized not thumbnailed
Your original image has 33 colours and weighs in at 22kB.
If you resize like this (albeit at the command line):
convert jabba.png -resize 600x600 -strip png8:result.png
the output file will be 6.6kB.
If you resize like I suggested with -scale:
convert jabba.png -scale 600x600 -strip png8:result.png
the output file will be 5.0kB.
If you retain -quality 9 in there, you will end up with > 25kB.

Imagemagick resize images dynamically

I have created a App wehere the user can upload an Image and choose a region to crop. Afterward the image should be resized and cropped with the chosen coordinates.
The problem is, that those image are printed afterwards, so the quality should be as close to the original image as possible(without artifacts, or visible blur).
Anyway resizing causes a loss of quality and using unsharpmask() after resizing makes it a lot better, but it still differs from image to image.
Oh and sometimes the colours don't even match with the original :(
I know this function's parameters depend on things like the size and saturation of the image, but is there any way to calculate the best parameters?
Here's what I have so far:
$origPath = "ThePathToTheFileOnTheServer.jpg";
move_uploaded_file( $_FILES['imagefile']['tmp_name'],$origPath);
$img = new Imagick();
$file_handle_for_viewing_image = fopen($origPath, 'a+');
$img->readImageFile($file_handle_for_viewing_image);
fclose($file_handle_for_viewing_image);
$img->setImageFormat('jpg');
$img->setImageCompression(Imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality(100);
$img->resizeImage ( 0, $targ_h, Imagick::FILTER_LANCZOS, 1);
$img->unsharpMaskImage(0 , 0.5 , 1 , 0.05);
$img->cropImage($targ_w, $targ_h, $xPos, 0);
$newimg = new Imagick();
$newimg->newImage($targ_w,$targ_h,new ImagickPixel('white'));
$newimg->setImageFormat('jpg');
$newimg->setImageCompression(Imagick::COMPRESSION_JPEG);
$newimg->setImageCompressionQuality(100);
$newimg->compositeImage($img,Imagick::COMPOSITE_OVER,0,0);
$newimg->writeImage("TheNewImage.jpg");
It does what it should, but can I somehow calculate the parameters of $img->unsharpMaskImage(0 , 0.5 , 1 , 0.05); to fix the current Image?
Thanks for your help!
Here's an example:
original:
after resize:
You could try resizing in the Lab colourspace and then converting back to sRGB afterwards. At the command-line, that would be
convert input.jpg -colorspace Lab -resize ... -colorspace sRGB result.jpg

How to increase quality of image through imagemagick binary convert?

I'm working in an image conversion using imageMagick binary convert. When i resize a small image into a larger image and also increase the quality of image.
Here's my sample code:
$img = 'old_image.png';
$path1= 'new_img.png';
exec("convert $img -quality 100% -density 600 -resize 2480x3508 -depth 400 $path1");
When i used this command its working fine and it convert large image with loss of quality.
When i need to increase quality by using sharpen 50% code in exec command it doesn't create a proper image and no response in exec command.
$img = 'old_image.png';
$path1= 'new_img.png';
exec("convert $img -sharpen 99% -quality 100% -density 600 -resize 2480x3508 -depth 400 $path1");
Here I'm using image magick binary convert. How to achieve this image quality. Any help would be appreciated.
Think sharpen works like blur and takes an argument of {radius}x{sigma}
What version are you using? Maybe look at some of the docs that correspond to it...
http://www.imagemagick.org/Usage/blur

Image file size differences between Imagemagick and GD library

I have been doing some tests and found out that Imagemagick creates larger file sized images compared to GD library.
I have tried using thumbnailImage method and also resizeImage method (with different filters) of Imagemagick for creating an image of max dimension 1024x680 jpeg with JPEG compression and quality 80 and at 72 pixels per inch resolution and am also using stripImage method to remove unneeded meta data. The file size created by Imagemagick is always in the range of 700KB to 800KB depending upon various filters. On the other hand GD library produces an image of size 1024x680 which is only 41KB in size.
Can anyone please explain the difference in file sizes. I opened up the 2 files in Photo shop and checked to see any differences but could not find any (DPI, color profile, 8 bit channel etc), but still cant explain the difference in file sizes.
$srgbPath = "pathTosrgbColorProfile";
$srgb = file_get_contents($srgbPath);
$image->profileImage('icc', $srgb);
$image->stripImage();
$image->setImageResolution(72,72);
$image->setImageUnits(1);
$image->setInterlaceScheme(Imagick::INTERLACE_JPEG);
$image->setImageCompression(imagick::COMPRESSION_JPEG);
$image->setImageCompressionQuality(80);
$image->setColorspace(imagick::COLORSPACE_SRGB);
$image->resizeImage($thumb_width,$thumb_nheight,imagick::FILTER_CATROM,1);
$image->writeImage($destination);
The size went down by 40KB giving an output of 711KB which is still pretty big. The Hi-res original file I am testing on is a jpeg of size 3008x2000 (4.2MB).
Edit:
I think I figured it out, the method setCompression() does it for the Object and not the image, instead I used setImageCompression() and setImageCompressionQuality() and now the size has reduced to 100KB.. All good now!
Maybe the Quality settings of GD and ImageMagick aren't easily comparable, 80% in one does not mean the same as 80% in the other. I found the following note in an article form Smashing Magazine:
It turns out that JPEG quality scales are not defined in a specification or standard, and they are not uniform across encoders. A quality of 60 in Photoshop might be the same as a quality of 40 in one program, quality B+ in another and quality fantastico in a third. In my tests, I found that Photoshop’s 60 is closest to -quality 82 in ImageMagick.
So you may pay more attention on quality when comparing the different result files. Maybe the colors differ or the gd image has more artifacts.
The differnce seems rather large; when I did some tests a couple of years ago the file size of IM was about 5x the size of GD.
It would be interesting to see your actual code used.
I am at work at the moment but have a photo resized to 592 x 592 and the filesize is 50.3KB I know it is not the same size as yours but it was saved at quality 100
You can run this and see what IM says about the output files:
convert image -verbose -identify
EDIT:
You must be doing something wrong I have just run a test and the results are below - For some reason the thumbnail size is the same as the resize size! Maybe a bug.
Original file size: 4700 x 3178 2.31MB
-resize dimensions = 1021 x 680 186kb
-thumbnail dimensions = 1021 x 680 186kb
GD dimensions = 1024 x 682 100kb
$original = 'IMG_4979_1.CR2';
// Convert to jpg as GD will not work with CR2 files
exec("convert $original image.jpg");
$image = "image.jpg";
exec("convert $image -resize 1024x680 output1.jpg");
exec("convert $image -thumbnail 1024x680 -strip output2.jpg");
// Set the path to the image to resize
$input_image = 'image.jpg';
// Get the size of the original image into an array
$size = getimagesize( $input_image );
// Set the new width of the image
$thumb_width = "1024";
// Calculate the height of the new image to keep the aspect ratio
$thumb_height = ( int )(( $thumb_width/$size[0] )*$size[1] );
// Create a new true color image in the memory
$thumbnail = ImageCreateTrueColor( $thumb_width, $thumb_height );
// Create a new image from file
$src_img = ImageCreateFromJPEG( $input_image );
// Create the resized image
ImageCopyResampled( $thumbnail, $src_img, 0, 0, 0, 0, $thumb_width, $thumb_height, $size[0], $size[1] );
// Save the image as resized.jpg
ImageJPEG( $thumbnail, "output3.jpg" );
// Clear the memory of the tempory image
ImageDestroy( $thumbnail );

How can I trim just the left and right side of an image using Imagemagick in PHP?

I'm trying to trim a variable amount of whitespace in an image only the left and right side using ImageMagick and PHP. Does anyone know how to do this (perhaps using something other than imagemagick?)?
Here's an example.
I have these two images:
Each has a variable amount of text that is dynamically created in a fixed width image.
What I need to do is trim the background off the right and left side so the images come out like this:
If ImageMagick can't do it, I am willing to use something else, but I will need help on how exactly because I am not much of a programmer. Thanks!
Here's my current code that trims all sides of an image:
<?php
/* Create the object and read the image in */
$i = '3';
$im = new Imagick("test".$i.".png");
/* Trim the image. */
$im->trimImage(0);
/* Ouput the image */
//header("Content-Type: image/" . $im->getImageFormat());
//echo $im;
/*** Write the trimmed image to disk ***/
$im->writeImage(dirname(__FILE__) . '/test'.$i.'.png');
/*Display Image*/
echo $img = "<img src=\"test".$i.".png\">";
?>
I think you are on the right track with ImageMagick's -trim operator 1), but the trick would be to get it tell you what it would do without actually doing it, and then modify that to do what you really want...
So, to get the trim-box ImageMagick calculates for your first image, you do this:
convert -fuzz 10% image.jpg -format "%#" info:
60x29+21+31
That is a 60x29 pixel rectangle, offset 21 across and 31 down from the top left corner. Now, we want to get these values into bash variables, so I set the IFS (Input Field Separator) to split fields on spaces, x and also + signs:
#!/bin/bash
IFS=" x+" read a b c d < <(convert -fuzz 10% image.jpg -format "%#" info:)
echo $a $b $c $d
60 29 21 31
Now I can ignore the 29 and the 31 because we are only interested in cropping the width, and crop like this:
convert image.jpg -crop "${a}x+${c}+0" out.jpg
So, for your 2 images, I get these:
and the full procedure is this:
#!/bin/bash
IFS=" x+" read a b c d < <(convert -fuzz 10% image.jpg -format "%#" info:)
convert image.jpg -crop "${a}x+${c}+0" out.jpg
Notes
1) The -format %# is just a shorthand for the -trim operator, which would be this in full
convert image.jpg -trim info:
image.jpg JPEG 72x40 200x100+16+24 8-bit sRGB 0.000u 0:00.000
From what I can see in the ImageMagick docs on cropping and borders, it doesn't seem to be possible.
you can't specify an edge for "intelligent" cropping (known as-trim on the command line), and all the cropping methods that accept a geometry argument need a fixed number for cropping.
The only idea that comes to mind is to get the colour of the shaved area in a separate call, run trimImage, and add the lost areas back using -border.
Edit: The IM manual is suggesting something similar. Check out Trimming Just One Side of an Image. I'm not familiar with IM's PHP extension to translate the code into PHP calls but it should be half-way straightforward.
The GD based library WideImage has something similar. It's called autoCrop, by default it works on all four sides.
However, you could just add another parameter and based on it only crop top/bottom or left/right.
autoCrop code
It's pretty well documented. $img is a WideImage_Image type. There is also an interactive online demo of it.
Related question: Removing black bars off video thumbnail.
Using GD:
function imageautocrop( &$img) {
$emptycol = function ( $img, $x, $min, $max) {
for( $y=$min; $y<$max; $y++) {
$col = imagecolorsforindex( $img, imagecolorat( $img, $x, $y));
if( $col['alpha'] != 127) return false;
}
return true;
}
$trim = Array('top'=>0,'bot'=>0,'lft'=>0,'rgt'=>0);
$size = Array('x'=>imagesx($img)-1,'y'=>imagesy($img)-1);
// code for affecting rows removed due to question asked
while( $emptycol( $img, $trim['lft'], $trim['top'], $size['y']-$trim['bot'])) $trim['lft']++;
while( $emptycol( $img, $size['x']-$trim['rgt'], $trim['top'], $size['y']-$trim['bot'])) $trim['rgt']++;
$newimg = imagecreate( $size['x']-$trim['lft']-$trim['rgt']+1, $size['y']-$trim['top']-$trim['bot']+1);
imagecopy( $newimg, $img, 0, 0, $trim['lft'], $trim['top'], imagesx($newimg)+1, imagesy($newimg)+1);
imagedestroy($img);
$img = $newimg;
}
It's very old code of mine, so probably not optimal, but it does the job.
It is a two step process as text is dynamically generated
Generate the text image, determine width(image)
Overlay text image into background, determine width(background)
Use one tool mentioned above, crop (width(background)-width(image)/2 on either side
The trick is figuring out the width(image). See: How can I auto adjust the width of a GD-generated image to fit the text?
Then again, if you know width(image), you can crop the width(background) first before overlay
Use cropImage() instead. Something like this, perhaps:
$img_x_size = 800; // Set these to relevant values
$img_y_size = 600;
$crop_pixels = 20; // How many pixels to crop
// cropImage(XsizeOfCrop, YsizeOfCrop, CropXPos, CropYPos)
$im->cropImage($img_x_size - $crop_pixels, $img_y_size, 0, $crop_pixels / 2);

Categories