Oct 29, 2009

One liner multi-image cropping

A simple entry for a simple way to solve a common problem.

I often have a lot of scanned images, documents usually, that have unnecessary white-space, and other artifacts around the edges due to limitations in the scanner software or issues with the scanner itself. Normally I use the gimp, and can correct orientation problems, colour issues and cropping very easily. But this is too much mouse work in my opinion when you have 50 images and all need a similar simple cropping. So I fiddled around in the shell a little and came up with this one-liner:
for file in *.jpg ; do echo "Cropping $file" ; \
convert -trim $file trim/$file ; \
convert -crop 1692x2190+4+4 trim/$file crop/$file ; done

OK. Not really one line, but I swear I did write it on one line :-)

What it does is three things:
  • loop over all image files, and for each:
  • trim all white-space off the edges, bringing all images to the same basic size with the top left of the real data at the top left of the image
  • trim them down to a specified size, in this case a little smaller than US letter size. I cut 2 pixels off each edge just to remove some noise commonly found on the edges of scanned images.
And here is a simple set of thumbnails of the results:

original image with many artifacts

first trim removes continuous whitespace

final cropped image at letter size with edges removed

It took about 10 minutes to figure out this script, 20 seconds to run it on all 50 images, and then 20 minutes to write the blog!


Phil H said...

A few things that might help with future scriptings. Firstly, you can pipe the two convert commands together (see http://www.imagemagick.org/script/command-line-processing.php) using the magic filename '-':

convert -trim $file - | convert -crop 1692x2190+4+4 - crop/$file

Which, of course, you can extend to more operations (might you want normalising, contrast adjustments...).

You can also get IM to work on sequences of filenames thus:

convert 'image_%03d.png[5-7]' ...

There is also some information on trimming scanned images:

Finally, I suspect that the entire convert process can become one command thus:

convert -fuzz 2% -trim -crop 1692x2190+4+4 $file processed/$file

Hope that helps.

Craig Taverner said...

Thanks Phil, clearly you know this stuff. I just read the man-page and got it working, but your solution is so much more elegant.

Now what I really wanted to do was script the gimp. If you know anything about that, drop me a line :-)

Mtheriault said...

Hello guy's

Is it possible to use your script to crop an image file before displaying it?.

I'm looking for a command line solution to display only 1 of the 4pictures in a jpf file.

Please excuse my bad English

Montreal. Canada

Mtheriault said...

No other comments, only to enable email follow-up


Craig Taverner said...

Hi Martin,

As far as I know JPF (and JPG) do not have multiple images per file, so I'm not sure I follow the 'only 1 of 4 pictures in a jpf file'. Or did you scan four together, and want to crop them? If so, then the convert -crop command can do that for you. However, for a single scan, I would rather use the gimp so I can visually position the cropping region.

I also wondered what you meant with 'before displaying it'. If you goal is to see the file after cropping, again I recommend the gimp to crop and display. If you are building a web application and want cropping server side before displaying on the web-page, then this is a much more complex topic. For that scenario I would use ruby on rails, the attachment_fu plugin and RMagick.

Craig Taverner said...

The crop size 1692x2190+4+4 were used on US letter sized scans at 200DPI. For A4 scans, I found 1656x2324+4+4 worked better.