ImageMagick single convert command performance - php

I have a few thousand images to be processed so each millisecond counts. Each image is ~2-3Mb in size.
Source file fed to the converter:
image.jpg
Files to be generated out of the source:
orig_image.jpg // original image
1024x768_image.jpg // large image
250x250_image.jpg // thumbnail 1
174x174_image.jpg // thumbnail 2
While browsing different topics on imagemagick convert performance I got a feeling that a single command should be way faster than individual converts for each image size. Also a memory utilization was mentioned as a performance boost. (ImageMagick batch resizing performance)
Multiple command conversion (each command run via php's exec() in a loop):
convert "image.jpg" \
-coalesce -resize "1024x768>" +repage "1024x768_image.jpg"
convert "1024x768_image.jpg" \
-coalesce \
-resize "250x250>" \
+repage \
-gravity center \
-extent "250x250" "250x250_image.jpg"
convert "1024x768_image.jpg" \
-coalesce \
-resize "174x174>" \
+repage \
-gravity center \
-extent "174x174" "174x174_image.jpg"
mv image.jpg orig_image.jpg
Single command conversion incorporating ImageMagicks mpr:
convert "image.jpg" -quality 85 -colorspace rgb -coalesce \
-resize "1024x768>" \'
-write "1024x768_image.jpg" \
-write mpr:myoriginal +delete \
mpr:myoriginal -coalesce \
-resize "250x250>" \
-gravity center \
-extent "250x250" \
-write "250x250_image.jpg" +delete \
mpr:myoriginal -coalesce \'
-resize "174x174>" \
-gravity center \
-extent "174x174" \
-write "174x174_image.jpg"
After performance testing the results are somewhat unexpected. Single command convert in a loop finishes in 62 seconds while multiple command conversion executes in just 16 seconds?
# convert -version
Version: ImageMagick 7.0.2-1 Q8 i686 2017-02-03 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2016 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC HDRI OpenMP
Delegates (built-in): bzlib freetype jng jpeg lzma png tiff wmf xml zlib
Also installed libjpeg-turbo jpg processing library but I cannot tell (don't know how to check) if ImageMagic is using it or the old libjpeg.
Any ideas how to speed up image converting process?
Edit:
Don't know how to format it properly here on stackoverflow, but I just noticed that single line command had an argument "-colorspace rgb" and multiple line commands did not which actually results in such strange results where multiple commands are processed faster.
Removed the "-colorspace rgb" argument after which the MPR convert version works the best and gave additional boost in performance.
To sum it all up I ended up using this command:
// MPR
convert "orig_image.jpg" -quality 80 -coalesce \
-resize "1024x768>" \
-write 1024x768_image.jpg \
-write mpr:myoriginal +delete \
mpr:myoriginal -resize "250x250>" \
+repage -gravity center -extent "250x250" \
-write "250x250_image.jpg" \
-write mpr:myoriginal +delete \
mpr:myoriginal -coalesce -resize "174x174>" \
+repage -gravity center -extent "174x174" \
-write "174x174_image.jpg"

You're not using jpeg shrink-on-load, that'll give an easy speedup.
The jpeg library has a neat feature where it'll let you decompress at full resolution, at 1/2, 1/4 or 1/8th. 1/8th resolution is especially quick because of the way jpg works internally.
To exploit this in convert you need to hint to the jpeg loader that you need an image of a particular size. To avoid aliasing you should ask for an image at least 200% larger than your target size.
On this machine, I see:
$ vipsheader image.jpg
image.jpg: 5112x3470 uchar, 3 bands, srgb, jpegload
$ time convert image.jpg -resize 1024x768 1024x768_image.jpg
real 0m0.405s
user 0m1.896s
sys 0m0.068s
If I set the shrink-on-load hint, it's about 2x faster:
$ time convert -define jpeg:size=2048x1536 image.jpg -resize 1024x768 1024x768_image.jpg
real 0m0.195s
user 0m0.604s
sys 0m0.016s
You'll see a dramatic speedup for very large jpg files.
You could also consider another thumbnailer. vipsthumbnail, for example, is quite a bit faster again:
$ time vipsthumbnail image.jpg -s 1024x768 -o 1024x768_image.jpg
real 0m0.111s
user 0m0.132s
sys 0m0.024s
Although real time is down by only a factor of 2, user time is down by a factor of 5 or so. This makes it useful to run with gnu parallel. For example:
parallel vipsthumbnail image.jpg -s {} -o {}_image.jpg ::: \
1024x768 250x250 174x174

Eric's and John's suggestions share much wisdom, and can be mixed in with my suggestion - which is to use GNU Parallel. It will REALLY count when you have thousands of images.
I created 100 images (actually using GNU Parallel, but that's not the point) called image-0.jpg through image-99.jpg. I then did a simple resize operation just to show how to do it without getting too hung up on the ImageMagick aspect. First I did it sequentially and it took 48 seconds to resize 100 images, then I did the exact same thing with GNU Parallel and came in under 10 seconds - so there are massive time savings to be made.
#!/bin/bash
# Create a function used by both sequential and parallel versions - it's only fair
doit(){
echo Converting $1 to $2
convert -define jpeg:size=2048x1536 "$1" -resize 1024x768 "$2"
}
export -f doit
# First do them all sequentially - 48 seconds on iMac
time for img in image*.jpg; do
doit $img "seq-$img"
done
# Now do them in parallel - 10 seconds on iMac
time parallel doit {} "par-{}" ::: image*.jpg
Just for kicks - watch the CPU meter (at top right corner of the movie) and the rate the files pop out of GNU Parallel in the last 1/6th of the movie.

It is funny as I did some conversions a while ago and found mpr was slower as well.
Anyway try this:
$cmd = " convert "image.jpg" -colorspace rgb -coalesce \( -clone 0 -resize 1024x768> -write 1024x768_image.jpg +delete \)".
" \( -clone 0 -resize 250x250> -gravity center -extent 250x250 -write 250_wide.jpg +delete \) ".
" -resize 174x174> -gravity center -extent 174x174 null: ";
exec("convert $cmd 174x174_image.jpg ");
I notice you do not have a background colour for you extent.
You can also add a -define to your loop method check it out in this list: https://www.imagemagick.org/script/command-line-options.php#define
jpeg:size=geometry only reads the amount of data you need to create the image without reading the whole image. You could probably also add it into your first line. -quality is for the output and will have no effect where you put it.
$cmd = " convert "image.jpg" jpeg:size=1024x768 -colorspace rgb -coalesce \( -clone 0 -resize 1024x768> -write 1024x768_image.jpg +delete \)"..
I can never remember if it comes before or after the file name though

Try Magick Persistent Cache image file format (.mpc) over Magick Persistent Registry (.mpr).
convert "image.jpg" -quality 85 -colorspace rgb myoriginal.mpc
convert myoriginal.mpc \
-resize "1024x768>" \
-write "1024x768_image.jpg" \
-resize "250x250>" \
-gravity center \
-extent "250x250" \
-write "250x250_image.jpg" \
-resize "174x174>" \
-gravity center \
-extent "174x174" \
"174x174_image.jpg"
Which results in the following times when tested with 1.8mb jpeg.
real 0m0.051s
user 0m0.133s
sys 0m0.013s
It's true that this will take two commands (although could be simplified to one with -write ... +delete), but very little I/O cost after .mpc is loaded back into the image stack.
Or
You can probably skip .mpc all together with ...
convert "image.jpg" -quality 85 -colorspace rgb \
-resize "1024x768>" \
-write "1024x768_image.jpg" \
-resize "250x250>" \
-gravity center \
-extent "250x250" \
-write "250x250_image.jpg" \
-resize "174x174>" \
-gravity center \
-extent "174x174" \
"174x174_image.jpg"
With results...
real 0m0.061s
user 0m0.163s
sys 0m0.012s

ImageMagick has a special resize operator variation named -thumbnail <geometry> for converting very large images to small thumbnails. Internally it uses
-sample to shrink the image down to 5 times the final height which is much faster than -resize if the thumbnail is much smaller than the original image. Because this operator uses a reduced filter set, the -filter operator is ignored!
-strip to remove all profiles from the image which are usually not required for thumbnails. This also further reduces the size of the resulting image file.
-resize to finally create the requested size and ratio
When it comes to creating thumbnails from JPEG images, then the special JPEG shrink-on-load option -define jpeg:size=<size> can be used instead, as stated out by user894763. Be aware that this option has to be specified immediately after convert, e.g.:
convert -define jpeg:size=<size> input-image.jpg ...
Anyway the -thumbnail operator can be specified additionaly then because it removes all profiles from the thumbnail image and thus reduces the file size.
Detailed information can be found in the ImageMagick usage documentation.

I get about half the time using one command line in ImageMagick 6.9.10.0 Q16 Mac OSX starting with a 3 MB input JPG image.
list="image.jpg"
time for img in $list; do
convert "image.jpg" \
-coalesce -resize "1024x768>" +repage "1024x768_image.jpg"
convert "1024x768_image.jpg" \
-coalesce \
-resize "250x250>" \
+repage \
-gravity center \
-extent "250x250" "250x250_image.jpg"
convert "1024x768_image.jpg" \
-coalesce \
-resize "174x174>" \
+repage \
-gravity center \
-extent "174x174" "174x174_image.jpg"
done
Time: 0m0.952s
time convert "image.jpg" \
-resize "1024x768>" \
+write "1024x768_image.jpg" \
-resize "250x250>" \
-gravity center \
-extent "250x250" \
+write "250x250_image.jpg" \
-resize "174x174>" \
-gravity center \
-extent "174x174" \
"174x174_image.jpg"
Time: 0m0.478s
The coalesce in your multiple commands is not needed, since JPG does not support a virtual canvas. So removing that reduces the multiply command line time to 0m0.738s
Multiple commands should be longer since you have to write and read intermediate images. Since your intermediate images are JPG, you will lose more visual quality each time you write and read the intermediate images. So the quality of one long command line should be better, also.

Related

How to stylize text with PHP using GD/Imagick?

I use imagettftext() function to add a caption to the image:
example
Background and captions are separate.
How could I apply the style similar to this one, is it possible with Imagick?
I need to create the 3D effect for the letters.
required style
I was trying to use and combine letters' images, but I think it's not a good solution.
Thanks!
If you are willing to use PHP exec(), you can call my Unix Bash Imagemagick script, texteffect2 at my web site http://www.fmwconcepts.com/imagemagick/index.html. It will do bevel and other effects. Here is the script command for doing a bevel:
texteffect2 -t "bevel" -e bevel -s plain -f Ubuntu-Bold -p 200 -c red -bg none result.png
-t is the text you want to use
-e is the effect you want
-s is whether to make it plain or add an outline
-f is the font name or font file
-p is the point size for the font
-c is the text color
-bg is the background color (none means transparent)
Here is the basic Imagemagick code to do the bevel effect:
convert -background none -font Ubuntu-Bold \
-pointsize 200 -fill "red" -gravity west label:"BEVEL" \
\( +clone -alpha Extract -write mpr:alpha -blur 0x8 -shade 135x30 \
-auto-level -function polynomial 3.5,-5.05,2.05,0.25 \
mpr:alpha -compose copy_opacity -composite \) \
-compose Hardlight -composite result.png
I do not know Imagick that well. So you might look here http://us3.php.net/manual/en/book.imagick.php for the Imagick equivalents or perhaps someone else who uses Imagick can convert it for you.
the method is called Imagick::embossImage, where the text should be a separate layer, before merging the layers for output. just try to manually produce the desired effect with GIMP2, then you know the steps required to get there. the gimp-imagemagick plugin also appears useful for testing.

Merge two commands

For watermark:
exec("composite.exe -watermark 85% -gravity southeast a.jpg -depth 8 a.jpg")
For thumbnail:
exec('convert.exe a.jpg -resize 150x150 thumba.jpg');
How can I merge two commands to one command?
How can I increase speed watermark and create thumbnail?
Is this command very slow?
That method looks interesting #RomaPerekhrest I had never thought of putting multiple commands in one exec()
This untested method should also work:
exec("composite.exe -watermark 85% -gravity southeast a.jpg -depth 8 miff:- | convert.exe - -resize 150x150 thumba.jpg");
I do not know why the OP has the .exe extensions but if they want to use them I will not argue.
How do you define those commands to be "very slow"?
To "merge" two commands into single line use semicolon ; or logical AND operator &&:
exec("composite.exe -watermark 85% -gravity southeast a.jpg -depth 8 a.jpg ; convert.exe a.jpg -resize 150x150 thumba.jpg");
Also the following command-line syntax is allowed:
convert a.jpg thumba.jpg -resize 150x150 -composite -watermark 85% -gravity southeast a.jpg -depth 8 a.jpg

Conversion PDF to PNG or JPEG is very very slow using ImageMagick

I have a working PDF to PNG conversion script using PHP and ImageMagick but I am having a problem with the speed of the conversion.
I know it works because with a very small PDF the time taken to convert is not that great, but with a 250kb file (still not that large really) it takes in excess of 20 minutes to convert.
Here's the PHP:
//***** GET PATH TO IMAGEMAGICK *****
$path_to_imagemagick = trim(`which convert`);
//***** PATH TO PDF TO CONVERT *****
$path_to_pdf = getcwd() . "/pdf/myfile.pdf[0]";
//***** PATH TO OUTPUT TO *****
$output_path = getcwd() . "/pdfimage/test_converted.png";
#exec($path_to_imagemagick . " -density 72 -quality 60 -resize 150x " . $path_to_pdf . " " . $output_path);
Are there any settings I can change to make this quicker?
If it helps, the image does not need to be a PNG. If JPEG is going to be quicker I'm happy to go with that.
ImageMagick cannot convert PDF to raster images by itself at all.
ImageMagick uses a delegate for this job: that delegate is Ghostscript. If you hadn't installed Ghostscript on the same system as ImageMagick, the PDF conversion by convert wouldn't work.
To gain speed, don't use ImageMagick for PDF -> raster image conversion. Instead, rather use Ghostscript directly (also possible via PHP).
Command line for JPEG output:
gs \
-o ./pdfimage/test_converted.jpg \
-sDEVICE=jpeg \
-dJPEGQ=60 \
-r72 \
-dLastPage=1 \
pdf/myfile.pdf
Command line for PNG output:
gs \
-o ./pdfimage/test_converted.png \
-sDEVICE=pngalpha \
-dLastPage=1 \
-r72 \
pdf/myfile.pdf
Both of these commands will give you unscaled output.
To scale the output down, you may use something like
gs \
-o ./pdfimage/test_converted.png \
-sDEVICE=pngalpha \
-dLastPage=1 \
-r72 \
-dDEVICEWIDTHPOINTS=150 \
-dDEVICEHEIGHTPOINTS=150 \
-dPDFFitPage \
pdf/myfile.pdf
Also please note: You used a -quality 60 setting for your PNG outputting command. But -quality for JPEG and -quality for PNG output do have a completely different meaning with ImageMagick (and you may not be aware of it). See also this answer for some details about this.

Does Gmagick have a method like Imagick::thresholdImage?

I'm trying to create a scripts with PHP's pecl module Gmagick to remove a somewhat white background from an image. On the command line I use the following two commands for imagemagick:
convert source.jpg \( +clone -fx 'p{0,0}' \) \
-compose Difference -composite \
-modulate 100,0 -alpha off -threshold 10% source_mask.png
convert source.jpg source_mask.png \
-alpha Off -compose CopyOpacity -composite \
source_transparent.png
I already have been able to take the first few steps:
$source_gm = new Gmagick();
$source_gm->readImage($source_path);
$white_gm = new Gmagick();
$white_gm->newImage($source_gm->getImageWidth(), $source_gm->getImageHeight(), 'white', 'png');
$mask_image = $source_gm->compositeImage($white_gm, Gmagick::COMPOSITE_DIFFERENCE, 0, 0);
$mask_image->modulateImage(100, 0, 0);
The Gmagick module has some kind of constant which should be used for the threshold Gmagick::COMPOSITE_THRESHOLD. However, there is no hint on how to use it. Can someone help me with the threshold?
Thanks!
I found out that the thresholdImage function is not available in gmagick yet. The feature has been implemented in SVN, but didn't make it to a release yet.

How to use Imagemagick to create a text arc?

My command is:
system(' convert -size 320x100 xc:lightblue -font Courier -pointsize 72 \
-fill navy -annotate +25+65 \'Ernakulam1\' \
-virtual-pixel transparent -distort arc 120 \
-bordercolor lightblue font_arcnew.jpg');
This code is not working and is most likely from here:
-virtual-pixel transparent -distort arc 120
Please help me!
First of all - do You have newest ImageMagick installed on Your server?
this question has been here for a bit.
This is the specific question:
mainly this code is not working
-virtual-pixel transparent -distort arc 120
My answer:
-virtual-pixel transparent -distort Arc 360
the difference is the capital "A" in arc.
This works okay for me. Thanks to Anthony and ImageMagick.
convert -size 130x130 -font Arial -pointsize 12 -background white -fill navy label:"Murphy Springs Lodge" -virtual-pixel background -distort Arc 360 fontCircle.png
Then I use this to view my changes:
open -a Gimp fontCircle.png
fontCircle.png

Categories