Use mogrify to resize large files while ignoring small ones

I think mogrify systematically rewrites the file, so your only hope is to filter the list first, as suggested by jippie. Here's how you might do it (untested): print out a list of image files with a size indication, keep only the names whose associated size is within range, and process that list.

identify -format '%w %h %i\n' ./*.png |
awk '$1 > 400 || $2 > 400 {sub(/^[^ ]* [^ ]* /, ""); print}' |
tr '\n' '\0' |
xargs -0 mogrify -resize '400x400'

Script explanation:

  • For each file, print a line with the width, a space, the height, a space, and the file name. Depending on your version of identify, the \n to add a final newline may either be necessary (ImageMagick 6.6.0) or superfluous but harmless (GraphicsMagick 1.1.11).
  • (awk) On each line, if the width ($1) and height ($2) match the required conditions, then:
    • Remove all text up to the second space character. This strips the width and height.
    • Print what remains of the line, which is the file name.
  • Replace newlines by null characters.
  • Call xargs -0 to execute the mogrify command on the file names. (We can't use plain xargs because it can't deal with input containing whitespace or \'".)

The file names may contain any character except newlines.


I faced the same problem you described. Here's my solution:

#!/bin/bash
files=*.jpg
minimumWidth=640
minimumHeight=640

for f in $files
do
    imageWidth=$(identify -format "%w" "$f")
    imageHeight=$(identify -format "%h" "$f")

    if [ "$imageWidth" -gt "$minimumWidth" ] || [ "$imageHeight" -gt "$minimumHeight" ]; then
        mogrify -resize ''"$minimumWidth"x"$minimumHeight"'' $f
    fi
done

I tested it on several JPEG images on a virtualized CentOS 6.5 machine. The script only resized and compressed the images whose width or height was larger than 640 pixels. This made it work for images with dimensions like 800 x 600 (landscape, resizing it to 640 x 480) and dimensions like 600 x 800 (portrait, resizing it to 480 x 640).

PS: A note on the 400x400 parameter: mogrify will process the file even if its dimensions are equal or smaller than 400x400, but will resize only if its dimensions are larger than 400x400. That's why files' modification time and size are changed (in my case, mogrify made these files even larger than they were).


You could also use the fx operator to filter images based on height/width e.g.

identify -format '%[fx:(h>400 && w>400)]\n' image.png

will output 1 if the image is bigger than 400x400 and 0 if it's equal or smaller than 400x400...


Assuming sane files names (no newlines/spaces/tabs etc) you could use identify to print image names preceded by either 1: or 0:, process the output deleting lines that start with 0: and removing the leading 1: on the rest of lines so only the file names remain, one per line, then pipe that list to mogrify ... @- (the @ syntax was added in imagemagick v6.5.2):

identify -format '%[fx:(h>400 && w>400)]:%i\n' ./*.png | \
sed '/^1:/!d;//s///' | mogrify -resize '400x400' -- @-

Otherwise, with find you could print only the files with size > 400x400 and then pipe the result to xargs + mogrify (it's less efficient as it runs a shell for each file but it should work with all kind of file names):

find . -maxdepth 1 -type f -name '*.png' -exec sh -c \
'identify -format "%[fx:(h>400 && w>400)]\n" "$0" | grep -q 1' {} \; -print0 \
| xargs -0 mogrify -resize '400x400'

If you're a zsh user see also this answer.

Tags:

Find

Mogrify