Most widely used camera focus length

Recently a collegue of mine argued that he more often needs a small camera focus length (wide-angled) than a zoom focus length (tele). To proof – or confute – this I wrote a small java program which extracts the exif data from a given directory of images and counts the focal length parameters used when taking the pictures. I used the beta version of Drew Noakes’ metadata extractor. As I haven’t coded in java for some time, the code might be a little bit dirty.

import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.iptc.IptcReader;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGDecodeParam;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.drew.metadata.*;
import com.drew.metadata.exif.ExifDirectory;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FilenameFilter;
import java.util.*;
import java.util.regex.*;

public class FocalApp {
    public FocalApp(String dirname) {
 // list of files
        File folder = new File(dirname);
        List filelist = new ArrayList();
        this.getFiles(folder, filelist);

 // key - value pairs: focallength - count
        SortedMap focalmap = new TreeMap();

 // for all found files
        for (Iterator it=filelist.iterator(); it.hasNext(); ) {
            String filename = it.next().toString();
 //System.out.println(" --- working on file: "+filename);

            File imageFile = new File(filename);
            try {
                Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
                Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);

                Double cameraFocus = 0.0;
                try {
                    String cameraFocusString = exifDirectory.getString(ExifDirectory.TAG_FOCAL_LENGTH);
                    if (cameraFocusString != null) {
 // value is present
                        cameraFocus = Double.parseDouble(cameraFocusString);
                    }
 //System.out.println("Focus lenght = " + cameraFocus);
                } catch(NumberFormatException nFE) {
 //System.out.println("Not an Integer");
                }

                Integer currentimagecount = 0;
                if(focalmap.containsKey(cameraFocus)) {
 //System.out.println("key already tgere");
                    currentimagecount = focalmap.get(cameraFocus);
                }
                currentimagecount++;
 //System.out.println("putting in map: "+currentvalue);
                focalmap.put(cameraFocus, currentimagecount);
            } catch (ImageProcessingException e) {
                System.err.println("skipping file "+filename+"due to error:"+e);
            }
        }

        Integer totalimagecount = 0;

        TreeSet set = new TreeSet(new Comparator() {
            public int compare(Object obj, Object obj1) {
                int vcomp = ((Comparable) ((Map.Entry) obj1).getValue()).compareTo(((Map.Entry)
                    obj).getValue());
                if (vcomp != 0) return vcomp;
                else return ((Comparable) ((Map.Entry) obj1).getKey()).compareTo(((Map.Entry)
                    obj).getKey());

            }
    });

        set.addAll(focalmap.entrySet());
        System.out.println("focal length in mm;number of images;");
        for (Iterator i = set.iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            Double key = (Double) entry.getKey();
            String keyname;
            if(key == 0) {
                keyname = "?";
            } else {
                keyname = key.toString();
            }
            Integer numimages = (Integer) entry.getValue();
            System.out.println(keyname + ";" + numimages+";");
            totalimagecount+=numimages;
        }

 //System.out.println("total: "+totalimagecount+" images.");
    }

    private void getFiles(File folder, List list) {
        folder.setReadOnly();

        File[] files = folder.listFiles(new ImageFileFilter());
        for(int j = 0; j < files.length; j++) {
            list.add(files[j]);
        }

        File[] subfolders = folder.listFiles();
        for(int j = 0; j  0 && args[0].length() > 0) {
            String imagepath = args[0];
            if(new File(imagepath).exists()) {
                new FocalApp(imagepath);
            } else {
                System.err.println("given image directory not found!");
                System.exit(2);
            }
        } else {
            System.err.println("please give a directory containing the images!");
            System.exit(1);
        }
    }
}

class ImageFileFilter implements FilenameFilter
{
    public boolean accept( File f, String s )
    {
        Pattern pattern = Pattern.compile("([^\s]+(?=\.(jpg|crw|cr2|nef|arw|tiff|tif))\.\2)", Pattern.CASE_INSENSITIVE);
        return pattern.matcher(s).matches();
    }
}

I used JDK 1.6. To build, you’ll need the “metadata-extractor-2.4.0-beta-1.jar” from the metadata extractor page. Then use:

javac -Xlint:unchecked -classpath .;metadata-extractor-2.4.0-beta-1.jar FocalApp.java
jar cfm FocalApp.jar Manifest.txt *.class

to build it. The Manifest.txt is just for entry method:

Manifest-Version: 1.0
Created-By: 1.6.0 (Sun Microsystems Inc.)
Class-Path: metadata-extractor-2.4.0-beta-1.jar
Main-Class: FocalApp

The run the jar, i.e. using:

java -jar FocalApp.jar "/path/to/your/images"

A tricky construct I had to google for after all the java-free months was how to sort a TreeMap by its values and not by the keys. In PERL this is simple (see Perlfaq4 for example):

my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash;

You get an array containig the hash’s keys, but sorted in the order of the hash’s values.
Now you can just iterate over the keys, displaying the values which could be something like that:

foreach my $Key (@keys) {
print "Key $Key has value $hash{$Key}";
}

In java I couldn’t find such an easy solution. First, I use a TreeMap to store the key-value pairs. This can already sort by keys using a SortedMap. But to turn the sorting around I needed put the entire content of that SortedMap into a TreeSet collection (code line 78), which uses a custom Comperator instance. I got this snippet from this forum page.

You can download the java code in a zip from here.

2 thoughts on “Most widely used camera focus length

Leave a comment