Simple way to resize large number of image files - shell

I have a folder which contains about 45000 jpeg images. Most of them are from 10KB - 20Kb.
Now I want to write a script to resize all of them to fixed size 256x256. I wonder if there is any simple way to do that like: for a in *.jpg do .... I am using 8-core CPU with 8GB of RAM machine running Ubuntu 14.04, so it is fine if the process requires many resources

I would use GNU Parallel, like this to make the most of all those cores:
find . -name \*.jpg | parallel -j 16 convert {} -resize 256x256 {}
If you had fewer files, you could do it like this, but the commandline would be too long for 45,000 files:
parallel -j 16 convert {} -resize 256x256 {} ::: *.jpg
Also, note that if you want the files to become EXACTLY 256x256 regardless of input dimensions and aspect ratio, you must add ! after the -resize like this -resize 256x256!
As Tom says, make a backup first!
Here is a little benchmark...
# Create 1,000 files of noisy junk #1024x1024 pixels
seq 1 1000 | parallel convert -size 1024x1024 xc:gray +noise random {}.jpg
# Resize all 1,000 files using mogrify
time mogrify -resize 256x256 *.jpg
real 1m23.324s
# Create all 1,000 input files afresh
seq 1 1000 | parallel convert -size 1024x1024 xc:gray +noise random {}.jpg
# Resize all 1,000 files using GNU Parallel
time parallel convert -resize 256x256 {} {} ::: *.jpg
real 0m22.541s
You can see that GNU Parallel is considerably faster for this example. To be fair though, it is also wasteful of resources though because a new process has to be created for each input file, whereas mogrify just uses one process that does all the files. If you knew that the files were named in a particular fashion, you may be able to optimise things better...
Finally, you may find xargs and mogrify in concert work well for you, like this:
time find . -name \*.jpg -print0 | xargs -0 -I {} -n 100 -P 8 mogrify -resize 256x256 {}
real 0m20.840s
which allows up to 8 mogrify processes to run in parallel (-P 8), and each one processes up to 100 input images (-n 100) thereby amortizing the cost of starting a process over a larger number of files.

You could use the mogrify tool provided by ImageMagick
mogrify -resize 256x256 *.jpg
This modifies all files in place, resizing them to 256x256px. Make sure to take a backup of your originals before using this command.

Related

Convert image bank using parallel

I've a bank of images and I used to use ImageMagick Convert to resize them, but I would like to take advantage of the power of multi-core by using the parallel command.
The statement I use to perform the single core conversion is as follows:
find . -type f -iname "*.jpg" -exec convert {} -quality 90 -format jpg -resize 240x160 ../small/{} \;
It works prefectly, but the problem is that, by default, convert only uses a single core to perform the process. So, how can I use parallel to use any number of cores I want to perform the same job?
Thanks!
It should be something like this:
find . -type f -iname "*.jpg" -print0 |
parallel -0 convert {} -quality 90 -resize 240x160 ../small/{}
Test it with find ... | parallel --dry-run ...
Note that I am using find ... -print0 and parallel -0 to match it so that filenames are null-terminated and spaces in filenames don't cause issues.
By default this will use all available cores. If you want to use just 2 cores, try:
parallel -j 2 ...
If you want to use all but one of your cores, try:
parallel -j -1 ...
If you want to use half your cores, try:
parallel -j 50% ...

Convert 5 Mio tiff Files in png

i am thinking about the best and fastet way to convert 5 Mio tiff Files (in Folders Subfolders and SubSubfolders) in 5 Mio png Files (same directory).
Is there any way to parallelise this job?
How could i check then if all files are converted?
ls *.tif | wc -l # compared to
ls *.png | wc -l
but for every folder.
Thanks.
Marco
Your question is very vague on details, but you can use GNU Parallel and ImageMagick like this:
find STARTDIRECTORY -iname "*.tif" -print0 | parallel -0 --dry-run magick {} {.}.png
If that looks correct, I would make a copy of a few files in a temporary location and try it for real by removing the --dry-run. If it works ok, you can add --bar for a progress bar too.
In general, GNU Parallel will keep N jobs running, where N is the number of CPU cores you have. You can change this with -j parameter.
You can set up GNU Parallel to halt on fail, or on success, or a number of failures, or after currently running jobs complete and so on. In general you will get an error message if any file fails to convert but your jobs will continue till completeion. Run man parallel and search for --halt option.
Note that the above starts a new ImageMagick process for each image which is not the most efficient although it will be pretty fast on a decent machine with good CPU, disk subsystem and RAM. You could consider using different tools such as vips if you feel like experimenting - there are a few ideas and benchmarks here.
Depending on how your files are actually laid out, you might do better using ImageMagick's mogrify command, and getting GNU Parallel to pass as many files to each invocation as your maximum command line length permits. So, for example, if you had a whole directory of TIFFs that you wanted to make into PNGs, you can do that with a single mogrify like this:
magick mogrify -format PNG *.tif
You could pair that command with a find looking for directories something like this:
find STARTDIRECTORY -type d -print0 | parallel -0 'cd {} && magick mogrify -format PNG *.tif`
Or you could find TIFF files and pass as many as possible to each mogrify something like this:
find STARTDIRECTORY -iname "*.tif" -print0 | parallel -0 -X magick mogrify -format PNG {}

Convert entire folder to greyscale using image magick?

I am trying to convert an entire folder to grayscale, using image magick.
convert *.jpg -colorspace Gray -separate -average
is met with this error :
convert: `-average' # error/convert.c/ConvertImageCommand/3290.
What is the correct command for this?
If you have lots of files to process, use mogrify:
magick mogrify -colorspace gray *.jpg
If you have tens of thousands of images and a multi-core CPU, you can get them all done in parallel with GNU Parallel:
parallel -X magick mogrify -colorspace gray ::: *.jpg
Also, the following can be used in a script - for the context menu of file managers like Dolphin, Nautilus, Nemo, Thunar etc:
for filename in "${#}"; do
name="${filename%.*}"
ext="${filename##*.}"
cp "$filename" "$name"-grayscale."$ext"
mogrify -colorspace gray "$name"-grayscale."$ext"
rm "$name"-grayscale."$ext"~
done

Shell debian reduce image size bigger than especific width

Someone knows ifbthere are some option in shell look formimages bigger than a size and reduce them to a maximum size.
Sure, use ImageMagick. Make a backup first and try on a copy of a few images as this resizes irretrievably!
find . -iname \*.jpg -exec mogrify -resize 640x480\> {} \;
Examples

Improving process for using ImageMagick to batch convert TIFF to PNG and resample

I have several folders of 600 dpi TIFFs (CCITT Group IV, so black & white) that I need to convert to screen resolution PNGs - so in ImageMagick terms, I need to convert the format and resample the images to ~80 dpi. My first approach was to perform this in a single mogrify command (this is in bash on Mac OS X):
for folder in $(find * -maxdepth 0 -type d ); \
do mogrify -path "$folder/medium" -format png -resample 31.5% "$folder/tiff/*.tif"; \
done
But the result was awful. The text in the resulting image was completely illegible. So I changed this to a two step process, (1) converting the TIFF to PNG at original resolution, then (2) downsizing the resolution:
for folder in $(find * -maxdepth 0 -type d ); \
do mogrify -path "$folder/medium" -format png "$folder/tiff/*.tif"; \
mogrify -resample 31.5% "$folder/medium/*.png"; \
done
While this process resulted in nice and crisp results at 80 dpi, the process was much slower, since I'm now writing the full resolution file to disk before downsizing the resolution.
Does anyone have a suggestion for the best way to accomplish a conversion and downsizing of resolution in a single step?
The sips tool can be used as follows:
sips -s format png -s dpiHeight 80 -s dpiWidth 80 -z 1200 1600 test.tiff --out test.png
Having said that in the resulting .png, the DPI settings don't seem to have been changed.
Also when resizing, it looks like you can only specify absolute pixel dimensions of the output image, and not a percentage of the input image. So you would have to grab the dimensions of the input image and calculate the new size explicitly:
#!/bin/bash
infile=test.tiff
outfile=test.png
pct=31 # only whole numbers for bash arithmetic
height=$(sips -g pixelHeight $infile | tail -1 | cut -d: -f2)
width=$(sips -g pixelWidth $infile | tail -1 | cut -d: -f2)
sips -s format png -s dpiHeight 180 -s dpiWidth 180 -z $((height*pct/100)) $((width*pct/100)) 1600 $infile --out $outfile
I know I am late to the party, but I was looking at this and wondered why you get poor quality when doing both setps in one go. I wondered if it was maybe down to using mogrify rather than convert, so I set about trying to improve it. So, this would be my first and best attempt:
#!/bin/bash
for f in */tiff/*.tif; do
out="${f%tif}png" # replace "tif" suffix with "png"
out=${out/tiff/medium} # replace "tiff" directory with "medium"
convert "$f" -resample 31.5% "$out"
done
And, if that still doesn't work, I could go for a second attempt which avoids writing the file to disk and then resampling it, and instead writes a PNG to stdout and then pipes that to a second convert that resamples and writes to disk - thereby avoiding the writing to disk of the big, intermediate PNG.
#!/bin/bash
for f in */tiff/*.tif; do
out="${f%tif}png" # replace "tif" suffix with "png"
out=${out/tiff/medium} # replace "tiff" directory with "medium"
convert "$f" PNG:- | convert - -resample 31.5% "$out"
done

Resources