# Sort images by aspect ratio

I am trying to select a few art images to fit some new frames. I would like to be able to sort these images by proximity to a particular aspect ratio and then view them in decreasing order of proximity through something like feh.

Is there some glorious one-line Unix expression? Otherwise a few dozen lines of Python should clear this up.

-
You can pull the aspect ratio via ImageMagick, e.g. `identify -format "%[fx:w/h]:%M\n" *.jpg`. I dunno how to easily sort by proximity to an arbitrary value. `sort` does not seem to support such sorting. – N.N. Oct 8 '12 at 16:54
Well, basically, you'd want to convert those numbers to an integer distance metric, and then you could pass it to sort -n. It appears you can actually do all the math with ImageMagick. – derobert Oct 8 '12 at 17:31

Using N.N.'s ImageMagick comment as a lead, it turns out you can have it do arbitrary computations as part of its `-format` output. So, one possible way:

``````identify -format "%[fx:round(100000*abs((4/3)-(w/h)))]:%M\n" *.jpg | \
sort -n -k1 -t:
``````

should do what you want. Note that 4/3 in there is a constant, the desired aspect ratio. You could of course find that as the output of a previous `identify` (to matches "closest to this image").

To explain, we're taking the plain difference between the desired and actual aspect ration. Then multiplying by a large number, and rounding. The multiply and round is because sort only handles integers, not rationals.

## edit

It turns out `sort` does appear to do rationals, not just integers. Its even specified to in the Single Unix Specification (v4). So, you can get rid of that workaround:

``````identify -format "%[fx:abs((4/3)-(w/h))]:%M\n" *.jpg | \
sort -n -k1 -t:
``````
-
Where did you find the documentation on identify's -format option? I saw nothing in the man page and spent quite some time trying to figure the darn thing out :). – terdon Oct 8 '12 at 17:57
Oh, and on my system your command does not seem to work. I get `33333:cover.jpg` where cover.jpg is 500x500. – terdon Oct 8 '12 at 17:58
@terdon well, that's because it has a ratio of 1/1 not 4/3. 4/3-1 = 1/3 = .333…. Just change that 4/3 to the ratio you want (which sounds like its 1/1). The docs are at imagemagick.org/script/fx.php in a typically confusing manner. Took me a while to find them, too. – derobert Oct 8 '12 at 18:01
Thanks for the link. As for the rest, I was being an idiot. I only had one jpg in my test folder (the rest are pngs) so since I saw only one output line I assumed that was the desired result and that identify was claiming that image had a 4/3 aspect ratio. That's what I get for trying to think on Monday evenings... +1 for an elegant solution. – terdon Oct 8 '12 at 18:07

Well, the easiest way to do this would be to use ImageMagick. It should be in the repositories of your Linux distribution, for debian based systems, run:

``````sudo apt-get install imagemagick
``````

One of the programs of the ImageMagick suite is `identify`, this will print the characteristics of a list of input image files. Combining it with `sort` will give you a list of images sorted by size (you can change png for whatever extension(s) you have:

``````identify *png | sort -gk 3
``````

If you actually need the aspect ratio and not just the size, try something like this:

1. Simple option, assumes your image names have no spaces:

``````identify *png *jpg *gif | \
gawk '{split(\$3,sizes,"x"); print \$1,sizes[1]/sizes[2]}' | \
sed 's/\[.\]//' | sort -gk 3
``````

The gawk command splits the 3rd field (the image size which has the format LxH) into the array "sizes" and then prints the 1st field (the image name) and the result of dividing the image's length by its height. The `sed` command is just beautifying the output and the `sort` command sorts the result according to image size ratio.

2. More complex, this one can deal with spaces in file names:

``````find . \( -iname  "*png" -o -iname "*jpg" -o -iname "*gif" \) -exec identify {} \; |\
perl -ne '/(.+?)\s+[A-Z]{3}\s+(\d+)x(\d+)/; print "\$1 ", \$2/\$3, "\n"' | \
sort -gk 2
``````

Here we are using `find` to identify the files we are interested in and run the `identify` command, and then piping its output through a little PERL script. The regular expression looks for three capital letters (`[A-Z]{3}`) which should be the image format. Once we have found that, it is easy to identify the image name and dimensions.

I am not using gawk here because the presence of spaces in the input file names will confuse the field numbers. Finally, the script will print out the image's name and the result of the length/height division which we `sort` numerically.

If simply browsing the available aspect ratios is not enough, if you have at least one image with the desired aspect ratio, just use grep to extract those images whose ratio is closest:

``````identify *png *jpg *gif | \
gawk '{split(\$3,sizes,"x"); print \$1,sizes[1]/sizes[2]}' |\
sed 's/\[.\]//' | sort -gk 3 | grep -C 10 GOOD_IMAGE.jpg
``````
-

You can compute the difference of each aspect ratio with the reference. Looks like derobert's answer, but simpler:

``````ref=4/3
identify -format "%[fx:abs(w/h - \$ref)] %M\n" *.jpg | sort -n -k1
``````
-
Hmm, apparently sort does do rationals. I was under the impression it did not... – derobert Oct 8 '12 at 18:22

My usual line of reasoning is to use squared error distance metric wherever I hear words "degree of proximity". One can surely use absolute values, and it would be faster. The script takes as an argument the ideal aspect ratio, processes all *.jpg files in the current directory and outputs them sorted in the increasing order of divergence from the ideal ratio.

`head` removes trailing end-of-line character inserted by ImageMagick.

The script contains a gawk command which sets output field separator to an unlikely character, computes squared difference and prints back the pair difference|file name. Sort is performed and unnecessary information (squared aspect ratio difference) is culled by `cut`.

``````#!/bin/sh
if [ x"\$1" = "x" ] ; then
echo "Usage: \$0 TargetAspectRatio" >&2
exit 2
fi
ASPECT_CMD="BEGIN{OFS=\"|\";}\
{\
print (\\$1-\$1)^2, \\$2;\
}"
identify -format "%[fx:w/h]:%M\n" *.jpg | head -n-1 | gawk -F":" "\$ASPECT_CMD" | sort -k1 -t"|" | cut -f2 -d"|"
``````
-
Please note that it uses quadratic distance and computations within gawk/awk :) (Thanks to @N.N. for the initial shortcut) – Deer Hunter Oct 8 '12 at 18:19
Could you please add an explanation of the script to your answer? It would make it better. – N.N. Oct 8 '12 at 20:10
Done, thanks for the nudge. – Deer Hunter Oct 8 '12 at 20:27

An aspect ratio is just a number, so you can browse the images according to their names, if the name is prefixed with the aspect-ratio. Creating symbolic links with the aspect-ratio pre-pended allows you to browse the images sorted by aspect-ratio.

The following viewers work as expected. (feh doesn't forward/back, even for real images - on my system)

• comix
• eog (Eye of Gnome / Image Viewer)
• gwenview

``````picd='/media/dat_ext4/pictures/jpg/misc'
srtd="\$picd/ar-sort"   # directory to hold "sorted" symbolic links
mkdir -p "\$srtd"

find "\$picd" -maxdepth 1 -type f -name "*" -print0 |
while IFS= read -d \$'\0' -r file ; do
[[ \$(file -ib "\$file") != image* ]] && continue  # skip non-image files
ar="\$(identify -format "%[fx:w/h]" "\$file")"
slink="\$(printf '%s/%06.4f %s' "\$srtd" "\$ar" "\$(basename "\$file")")"
# make symbolic link in 'ar-sort' directory
done
``````

Go into the `ar-sort` directory and just browse from whatever starting point (aspect-ratio) you are interested in.

The symbolic-link names look like this:

``````0.6732 Gold Bars.jpg
0.7242 Light Bulb.jpg
0.8022 Escher - Waterfall.jpg
1.3696 Old Typewriter.jpg
1.6000 King Tut.jpg
``````
-