9

I'm running the following command:

mogrify -resize '400x400>' *.png

Notice the ">". Supposedly it will ignore smaller files, but although it doesn't resize them, it edits them (modification date and file size are changed).

Is there a way to make it actually just leave the smaller files alone? I'd like to avoid several thousand unnecessary write operations.

12

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.

  • can you explain that script? Specifically the "sub" part. It's printing the file names without a space or inserting a new line. mogrify: unable to open file `22 553.png308 400 0134 2.png' @ error/png.c/ReadPNGImage/2951. I have no idea where that "308 400" comes from. I should mention the files have spaces in their names Thank you. – Mike May 18 '12 at 16:15
  • I'm getting mogrify: unable to open file `340 271 22 553.png308 400 0134 2.png' @ error/png.c/ReadPNGImage/2951. I ran the command using 200x200 on two sample files. I see that 308x400 is the size of one of the files – Mike May 18 '12 at 16:23
  • 1
    @Mike Ah, got it. Some versions of identify automatically put a newline after each record, others need to have one explicitly. Add \n at the end of the argument to -format (see my edit). – Gilles May 19 '12 at 12:10
7

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).

4

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.

2

How about using identify to find the size of your image and decide from a small script if you want to edit it or not:

identify -format "width=%w heigth=%h" bootchart.png 
width=3853 heigth=10092

Shouldn't be too hard to edit the output format for use in a simple script.

  • Considering my limited skills and the fact the images' sizes are so irregular, I need a simpler method. I'd rather process them all with mogrify. – Mike May 18 '12 at 15:13
0

I use such PHP-script, it uses ImageMagick:

<?php
$dir = ".";
$exts = array('jpg', 'jpeg', 'png', 'gif');
$max_size = is_numeric($argv[1]) ? $argv[1] : 3000;
$morgify = "mogrify -verbose -scale \"${max_size}x${max_size}>\" -quality 85";
$identify = "identify -format \"%wx%h\"";

$dh = opendir($dir);
while (($file = readdir($dh)) !== false) {
    $path = "$dir/$file";
    // skip no images
    $dot = strrpos($file, '.');
    $ext = strtolower(substr($file, $dot + 1));
    if (!in_array($ext, $exts)) continue;
    // large size?
    $size = exec("$identify \"$path\"");
    list($width, $height) = explode('x', trim($size));
    if (max($width, $height) > $max_size) {
        // scale!
        print "scale $file ${width}x${height}";
        exec("$morgify \"$path\"");
        print "\n";
    }
}
closedir($dh);
?>

It scales all images in current directory larger than 3000 on some edge.

Run command: php scale.php OR php scale.php 2000

0

Here's my take on it, incorporating ideas from @ArionKrause and @don_crissti:

#!/bin/bash
# adapted from http://unix.stackexchange.com/a/157594/110635
# and http://unix.stackexchange.com/a/220619/110635
W=1024
H=768
SIZE_TEST="%[fx:(h>$H && w>$W)]"'\n'

for f in $*; do
  if [ $(identify -format "$SIZE_TEST" "$f") = 1 ]; then
    echo "Resize: $f"
    mogrify -resize ''"$W"x"$H"'' "$f"
  else
    echo "Do not resize: $f"
  fi
done

(I needed this because my favorite batch processor Phatch does not work with Ubuntu 16.04.)

0

My functions to resize and optimize

resize_and_optimize_images () {
  resize_images 700 $PWD
  optimize_images 85 $PWD
}

resize_images () {
  max="$1"
  dir="$2"

  echo "Resizing dir $dir, max size - $max"

  shopt -s globstar

  for f in $dir/**/*.jpg $dir/**/*.jpeg $dir/**/*.png ; do
    echo "Checking $f"
    s=`identify -format "%w" $f`

    if [ $s -gt $max ]; then
      echo "Resizing..."
      mogrify -verbose -resize $max $f
    fi
    echo
  done

  echo "Done resizing dir $dir"
}

optimize_images () {
  quality="$1"
  dir="$2"

  echo "Optimizing dir $dir, quality - $quality"

  docker run -it --rm --name optimize_images_foo \
    -v $dir:/usr/src/app \
    -w /usr/src/app ruby:2.4-stretch bash -c \
    "gem install image_optim image_optim_pack && \
    (curl -L \"http://static.jonof.id.au/dl/kenutils/pngout-20150319-linux.tar.gz\" | tar -xz -C /usr/bin --strip-components 2 --wildcards \"*/x86_64/pngout\") && \
    image_optim --verbose --allow-lossy --jpegoptim-allow-lossy true --jpegoptim-max-quality $quality --pngquant-allow-lossy true --pngquant-quality 0..$quality -r ."

  echo "Done optimizing dir $dir"
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.