How to batch resize images in Ubuntu recursively within the terminal?
Asked Answered
A

10

44

I have multiple images stored in a set of organized folders. I need to re-size those images to a specific percentage recursively from their parent directory. I am running Ubuntu 11.10 and i prefer learning how to do that directly from the terminal.

Askins answered 29/5, 2012 at 15:55 Comment(0)
F
93

You could use imagemagick. For instance, for resizing all the JPG images under the current directory to 50% of their original size, you could do:

for f in `find . -name "*.jpg"`
do
    convert $f -resize 50% $f.resized.jpg
done

The resulting files will have ".jpg" twice in their names. If that is an issue, you can check the following alternatives.

For traversing/finding the files to resize, you can use xargs too. Example:

find . -name "*.jpg" | xargs convert -resize 50%

This will create copies of the images. If you just want to convert them in place, you can use:

find . -name "*.jpg" | xargs mogrify -resize 50%
Fariss answered 29/5, 2012 at 16:1 Comment(11)
Is this a recursive process? f is a variable and $FILES is the path for the file right?Askins
The question mentions "a set of organized folders". Need a "find".Allegorical
@Askins I changed the answer so that it handles recursion in the subfolders of the current folder, looking for all jpg images.Fariss
@Allegorical thanks, I realize now that a way to deal with recursion was explicitly asked by the OP. I fixed by answer to include recursion.Fariss
Great script. If using mogrify take care to do: mogrify -resize 50% $f and not mogrify $f -resize 50%Salonika
The for ...; do ... done solution doesn't seem to work as is (it looks for .jpg.jpg files inside the loop). This modified version works as expected : <pre>for f in find . -name "*.jpg"; do convert basename $f .jpg.jpg -resize x600 basename $f .jpg_resized.png; done</pre>Guck
Sorry for the bad formatting, I wasn't able to find a way to disable the code markup within 5 minutes, but you get the idea ...Guck
@ThibaudRuelle Thanks for spotting that :)Fariss
Abs great answer @Fariss but I have also handled spaces in the modified version and posted it as an alternate answer for those dealing with spaces in file names and folder names. ThanksLiebig
Imagemagick is a great tool for this, although it is quite resource demanding. I am currently having performance problems when trying to resize and optimize images. It can take as much as up to 15 minutes to resize a 2.4 MB size image, which is unacceptable. See this page for more info about the approach I am usingDisappointed
Is it possible to do this with images of type X11 cursor?Plovdiv
K
14

You can also use

sudo apt install imagemagick
sudo apt-get install nautilus-image-converter
nautilus -q

For resizing/rotating images in the current folder. You just install and then right click on an image or multiple ones and choose the size you want and that's it. The nautilus -q is to stop nautilus. Just start nautilus again, and you'll be able to use the image converter.

Knar answered 19/11, 2013 at 9:23 Comment(2)
Hey! This should be the accepted answer. It's so convenient to use it. Similar answer here: askubuntu.com/questions/1053081/…Substantive
I'm not sure why this is not upvoted more. I'm a programmer, so comfortable with the command line, but when I just want to resize some pictures, having a UI is way easier than looking up the imagemagick commands every timeTheone
L
10

Extending the answer from @betabandido

Incase there are spaces in filenames or folder names in which the images are, then one should use -print0 with find and -0 with xargs to avoid any parsing errors.

find . -name "*.jpg" -print0 | xargs -0 convert -resize 50%
find . -name "*.jpg" -print0 | xargs -0 mogrify -resize 50%
Liebig answered 29/10, 2015 at 19:13 Comment(1)
With quality works too: find . -name "*.jpg" -print0 | xargs -0 mogrify -quality 70%Bailar
C
3

Old question but I find which I consider the simplest method that also deals with whitespaces and non "standard" chars.

find -iname "*.jpg" -exec convert {} -resize 1280x1280 {} \;

(that's my solution with max dimension, but if you want to resize to percentage just adapt)

find -iname "*.jpg" -exec convert {} -resize 50% {} \;
Compensation answered 20/12, 2022 at 11:58 Comment(1)
This approach is much better as it runs convert for each file separtely using -exec. So if one image fails for any reason, others are still converted. Using xargs runs in batch and will terminate on first error (e.g., if you have one corrupted image file then all the remaining batch is terminated).Monoicous
E
1

It's also works if you give the new resize resolution :

convert $f.jpg -size 1024x768 $f.resized.png
Edmonson answered 30/5, 2013 at 13:42 Comment(0)
V
1

You can use imagemagick tool for batch resize.

It will maintain the aspect ratio

$ convert dragon.gif    -resize 64x64  resize_dragon.gif

It will not maintain the aspect ratio

$ convert dragon.gif    -resize 64x64\!  exact_dragon.gif

$ cat resize.sh 
#!/bin/bash
for f in `find . -name "*.jpg"`
do
    convert $f -resize 45x60\!  $f.resize.jpg
done

It will resize the image to 45x60 without maintaining the aspect ratio in current directory.

Vibratory answered 28/5, 2014 at 13:0 Comment(0)
T
1

I modified the code of the accepted answer a little bit to include png files and add a prefix instead of a postfix to the file name to make it easier to select all resized files at once.

for p in `find . -name "*.jpg" -o -name "*.png"`
do
    d=${p%/*}
    f=${p##*/}
    b=${f%.*}
    e=${f##*.}
    convert $p -resize 33% $d/thumb.$b.$e
done

With a little modification it is possible to recreate the directory structure to a separate directory and have only resized files with the same directory structure. Another option is flattening the directory structure and having randomly generated file names and collect the path mapping to a CSV file by each thumb file with append.

True answered 26/7, 2022 at 11:33 Comment(0)
W
0

there are a few answers like:

find . -name "*.jpg" | xargs convert -resize 50%

this won't work as it will expand the list like this: convert -resize 50% a.jpg b.jpg c.jpg which will resize a.jpg in c-0.jpg, b.jpg in c-1.jpg and let c.jpg untouched.

So you have to execute the resize command for each match, and give both input file name and output file name, with something like:

find . -name "*.jpg" | xargs -n 1 sh -c 'convert -resize 50% $0 $(echo $0 | sed 's/\.jpg/-th\.jpg/')'

each match of find is individually passed by xargs -n 1 to the resize script: sh -c 'convert -resize 50% $0 $(echo $0 | sed 's/\.jpg/-th\.jpg/')'. This script receives the file name in argument $0, uses sed to make an output file name by substitution of the original .jpg suffix by a -th.jpg one. And it runs the convert command with those two file names.

Here is the version without xargs but find -exec:

find -name '*.jpg' -exec sh -c 'convert -resize 50% $0 $(echo $0 | sed 's/\.jpg/-th\.jpg/')' {} \;
Wirephoto answered 12/11, 2015 at 0:10 Comment(3)
Mmmm... with this method, you are going to have to create a new process to execute sh for every single JPEG, and a new process to execute sed for every single JPEG and a new process to execute convert for every single JPEG and that is going to hurt if you have lots of images. I would suggest using mogrify which you only invoke once and it does all your images from a single process, and/or using GNU Parallel, or, at the very least working out the new filename using shell parameter substitution rather than sed.Idempotent
Absolutely; with mogrify, you don't need to work-out a second file name and all the complexity is gone. My point was that what works with mogrify doesn't with convert, as written in a couple of answers.Wirephoto
My point was that mogrify and convert are not simply interchangeable, as written in a couple of answers. Sure; mogrify does it all if you are fine with overwriting the original file. If you don't, you then want to use convert, and it gets a bit more messy. Get rid of sed, good idea: find -name '*.jpg' -exec sh -c 'convert -resize 50% $0 "${0%\.jpg}-th.jpg"' {} \; or simply by prefixing: "th-${0}" IMHO, I am not sure that the cost of creating processes matters much compared to the task of image processing... but I agree; why the hell create a new process when it is not needed?Wirephoto
H
0

Without XARGS and replacing the original:

After many hours struggling with my Linux ignorance (first year around), I've finally got the best solution for me:

  • using ImageMagick;
  • without XARGS;
  • replacing the original file;
  • setting a specific max for width and heigt keeping aspect ratio (700px in the following code);
  • inclusion of lettercase specific variants to avoid omissions;
  • recursively searching in all subfolders.

This is the code for the terminal:

for f in `find . -name "*.jpg" -or -name "*.JPG" -or -name "*.jpeg" -or -name "*.JPEG"` ; do convert "$f" -resize 700 "$f" ; done

I hope it helps many of you; anyways it's already saved for my future needs.

Heracliteanism answered 16/5, 2023 at 6:32 Comment(0)
C
0

The script to resize all .png files in a folder to 30%. The original file can either be removed (if delete_original is true), or stays, in that case the resized file would get a name postfix rename_postfix:

#!/bin/bash

rename_postfix="-small"
delete_original=true
substr=".png"
for str in *.png; do
    #reverse strings
    reverse_str=$(echo $str | rev)
    reverse_substr=$(echo $substr | rev)
    
    #find index of reversed substring in reversed string
    prefix=${reverse_str%%$reverse_substr*}
    reverse_index=${#prefix}
    
    #calculate last index
    index=$(( ${#str} - ${#substr} - $reverse_index ))
    
    # Extract the filename without extension
    filename="${str:0:index}"
    if $delete_original; then
        # the name can remain, because the original will be removed
        new_filename="${str}"
    else
        # need a new name, two files will remain
        new_filename="${filename}${rename_postfix}${substr}"
    fi

    echo "extension = ${substr}, original = ${str}, filename = ${filename}, new = ${new_filename}"
    
    # Resize and convert the file to .jpg
    convert -resize 30% "${str}" "${new_filename}"
done

I think you should be able to write a function with the above body that would accept a directory argument, etc (if you want to make it go into subfolders)

The convert function is from the imagemagick library:

sudo apt install imagemagick
Cymar answered 2/4, 2024 at 20:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.