Image Magick Not Processing All Images - image

I'm currently trying to convert a batch of images to greyscale using:
convert "* .jpg" -set colorspace Gray -separate -average "*.jpg"
Right now I'm working on a couple of hundred images. When I run the command I get copies of all the images, but only the 1st is actually converted to greyscale. Anyone know how what the issue might be? Also, if anyone has a better way of working through a very large quantity of images (ultimately I'll need to convert several thousand) at a time I'd appreciate it.
Thanks!

As pointed out in the comments, you can not have wildcards as the output. If you want to overwrite the original files with the updated colorspace, you can try the mogrify utility.
mogrify -set colorspace Gray -separate -average *.jpg
But that can be risky as your destroying originals. A simple for-loop might be easy to schedule & manage.
for filename in $(ls *.jpg)
do
convert "$filename" -set colorspace Gray -separate -average "output_${filename}"
done
ultimately I'll need to convert several thousand
If you really are facing large quantities of tasks, I would suggest spreading the tasks over multiple CPU cores. Perhaps with GNU Parallel.
parallel convert {} -set colorspace Gray -separate -average output_{.} ::: *.jpg
Of course I'm assuming your working with BASH on a *nix system. YMMV elsewhere.

Related

Batch convert white background to transparent

Seeking to set a folder of PNG line drawings to background transparent and getting lost in examples and options recommended for other cases. Essentially when I find a line of command that seems to fit I don't know how to apply it to an entire folder. Also getting lost in options like fuzz and anti-alias which I'm not even sure would benefit my images, below. I'm on a Mac running v7 of Magick and really appreciate your kind help.
Simplest Sample
Most Complex Sample
This should do what you want in Imagemagick 7. You should use magick mogrify to process a whole folder of images. To convert white to transparent in an image you can use -fuzz XX% -transparent white, where XX is a percent tolerance for nearby colors. For JPG, typically about 15% seems to work due to jpg compression change of flat colors. So I put your images into a folder called drawings and create a new empty folder called drawings2 to hold the output image, which need to be PNG (or TIFF) so use transparency.
cd
cd desktop/drawings
magick mogrify -path ../drawings2 -format png -fuzz 15% -transparent white *.jpg
Results:
See https://imagemagick.org/Usage/basics/#mogrify

Get dimensions of composition, minus whitespace

I have a series of images (10000's) that are all object-on-white-bg composition. Images are let's say 1000x1000 and objects within are any size. I want to get the actual dimensions of the object, which could be something like 400x500 or 350x520. The rest of the image is just whitespace (but there is some dirtyness to the whitespace e.g. 2% off-white)
I just need analysis of the images; I don't need to export or save data.
QUESTION: Using imagemagick, What's the most performant way to get actual object dimensions on a clean background? I just wonder is there a better imagemagick tool to use than trimImage().
Here is one working method, roughly:
(PHP/Laravel project)
$path = 'images/myTestImage.jpg';
$imagick = new \Imagick(realpath($path));
$originalDimensions = $imagick->getImageWidth()."x".$imagick->getImageHeight();
// Using imagemagick trimImage(), with fuzz value to take care of dirty white pixels.
$imagick->trimImage(5000);
$newDimensions = $imagick->getImageWidth()."x".$imagick->getImageHeight();
Example image:
Here are some ideas. I don't know enough about your environment or images to say which, if any, will work best or be appropriate for you.
You may be able to use the JPEG "shrink-on-load" feature to reduce the demand on I/O, memory and CPU cycles while loading your images - especially if the edges of your shapes are fuzzy by a couple of percent, the reduced size will not make much difference to the finding of the shape outlines. I'll demonstrate by creating a test image to trim:
convert -size 2000x2000 xc:black -fill white -draw "rectangle 100,100 200,200" image.jpg
Now get trim box:
time convert image.jpg -format %# image.jpg info:
112x112+96+96112x112+96+96
real 0m0.128s
user 0m0.803s
sys 0m0.097s
Now do same thing again but with "shrink-on-load" to 1/4 the width and height:
time convert -define jpeg:size=500x500 image.jpg -format %# image.jpg info:
28x28+24+2428x28+24+24
real 0m0.040s
user 0m0.054s
sys 0m0.014s
Note that is 3-4x faster, also that the features (including trim box) are correspondingly reduced.
You may be able to use vips, though you would have to add the corresponding tag to get John's (the author) input. If vips can do this, it may be an excellent option. I think you would need to use find_trim() which is documented here.
You may able to "shell out" using system() to get a tool such as GNU Parallel to use multiple CPU cores to process your images in parallel. It's a quick, easy install. You can just try the following in your Terminal without PHP to get an idea how fast it is:
parallel --bar 'magick -format "%f:%#\n" {} info:' ::: *.jpg
If you have too many images for your command line, you can pump the names into it this way:
find . -name \*.jpg -print0 | parallel -0 --bar 'magick -format "%f:%#\n" {} info:'
You could write some Python that takes a load of filenames to check with OpenCV which will be really fast, and then get GNU Parallel to call that Python with as many filenames as your shell can handle (-X option) to amortise the overhead of starting the Python interpreter over as many images as possible. I mean this:
parallel -X script.py ::: *.jpg
Then your Python would be of the form:
for f in sys.argv[1:]:
image = cv2.imread(... probably greyscale)
cv2.findContours ... à la Rotem https://stackoverflow.com/a/60833042/2836621
Or, rather than findContours() you can probably do it faster as suggested by Divakar here.

Compressing an image to make it less than 4KB

I have an image of a person and I want to compress it to make it less than 4KB. I need to compress it and still have the face of the person recognizable even if the image will shrink.
Here is Theresa May at 142kB:
and resized to 72x72 and converted to greyscale and reduced to 2kB with ImageMagick at the command line:
convert original.jpg -resize 72x72 -colorspace gray -define jpeg:extent=2kb result.jpg
I can still recognise her.
Here is some other guy reduced to 1kB and I can still recognise him too:
ImageMagick is installed on most Linux distros and is available for macOS and Windows. Bindings are available for Python, PHP, Ruby, Javascript, Perl etc.
If you had further knowledge about your images, or your recognition algorithm, you may be able to do better. For example, if you knew that the centre of the image was more important than the edges, you could slightly blur, or reduce contrast in relatively unimportant areas and use the available space for more details in the important areas.
Mark Setchell has the right idea. But I might suggest one potential minor improvement. Remove any meta data including profiles, EXIF data etc. You can do that by either adding -strip
convert input.jpg -strip -resize 72x72 -colorspace gray -define jpeg:extent=2kb result.jpg
or by using -thumbnail rather than -resize. The former automatically does the strip.
convert input.jpg -thumbnail 72x72 -colorspace gray -define jpeg:extent=2kb result.jpg

bash adjusting image dimensions to fit a certain size

I searched everywhere but couldn't find an answer to this.
I would like to output all images of a folder in 50Kb exactly and maintain the original aspect ratio.
I tried ImageMagick and resizing to 250x250 e.g but it is not working for me, what it does is change the first dimension and adapt the other, so output images have not the same size.
for image in `ls $#*.jpg`; do
mogrify "${image}" -resize 250x250 "${image}"
done
How do it? Thanks
Updated Answer
If you want the file size of the images to be limited to 50kB, use:
convert input.jpg -define jpeg:extent=50KB output.jpg
Original Answer
Use mogrify, but be careful it will overwrite all your images:
mogrify -resize 250x250! *.jpg
In general, when using mogrify I like to use the -path option to specify an output directory for results, like this to avoid originals getting overwritten:
mkdir results
mogrify -path results ...
Your script will work if you add the ! after the resize which forces the resize even if it distorts the shape of the picture.
If you want a way to do something similar with Python, I wrote an answer that works pretty well here. It does a binary search for a JPEG quality that satisfies a maximum size requirement.

Using ImageMagick to add whitespace to images in bulk

I have a bunch of images that are rectangular and I need them to be square, i.e. 1000x1000, so I've decided to use ImageMagick to pad the sides of the image with the amount of whitespace necessary to make the image square.
I've found the code from the following link useful (see next paragraph). But this requires me to process each image individually, to set the filename and then the -thumbnail dimensions. How can I use this same process to process an entire folder?
Here's the code and the link to it:
http://imagemagick.org/Usage/thumbnails/#pad
convert -define jpeg:size=200x200 hatching_orig.jpg -thumbnail '100x100>' \
-background white -gravity center -extent 100x100 pad_extent.gif
It is not very clear from your question what you actually have (i.e. how big your images are? what format are they?), nor what you actually want (you only provide an example that doesn't do what you want. But I'll take your question at the face-vale of the title and hope you can work out anything missing from there.
If you want to do ImageMagick in bulk, you either need to use a loop in bash, like this:
#!/bin/bash
for f in *.jpg; do
convert "%f" ...
done
or use IM's mogrify command. That is quite a powerful command, so make a backup of your images before you even think of trying to use it.
Let's assume you want to place all JPEG images in the current directory onto a white 1000x1000 pixel background. You would do this:
mogrify -background white -gravity center -extent 1000x1000 *.jpg
Updated Answer
It should be possible to do the resizing and the white padding in a single ImageMagick command without needing to use SIPS as well, like this if you now want 1200x1200 square images padded with white:
mogrify -background white -gravity center -resize 1200x1200\> -extent 1200x1200 *.jpg

Resources