Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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.

share|improve this question
3  
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

5 Answers 5

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:
share|improve this answer
    
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
2  
@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
share|improve this answer

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
share|improve this answer
    
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"|"
share|improve this answer
    
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
    ln -s "$file" "$slink"  
    echo "$slink" 
  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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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