Fixing the Orientation of JPEG Photographs
I used to fix the orientation of my photographs through an application that would transpose the compressed JPEG blocks. This had the advantage of avoiding the image degradation of a decompression and a subsequent compression.
However, this works only when the photograph's size is in both dimensions an exact multiple of a block's size. When disk space at my disposal increased I configured my camera to take pictures at full resolution, and this method couldn't work anymore. A couple of months ago I found out that the media player I'm increasingly using to watch the photographs respects the so-called EXIF orientation field (as does Mac's Preview). This one-byte field specifies the orientation of the photograph (for instance, portrait or landscape). Some higher-end cameras set it automatically, but one can also modify it by hand. I started using the jpegexiforient command-line tool for this task, but even I, a command-line aficionado, found it difficult to use.
I therefore wrote jeo, a GUI-based program that allows the interactive setting of a JPEG photograph's Exif orientation field. From the following links you can download the program's source code, Windows executable, Mac OS X application, and Unix (Linux, Solaris, FreeBSD, etc) executable. In order to work jeo requires a Java virtual machine to be installed.
I wrote the program in Processing, which proved to be a good match for my requirements. I've never written a program that relied as heavily on exceptions as this one, and I often wondered on their utility. In this case they proved an invaluable debugging aid, because they allowed me to easily pinpoint the location and reason of each failure; locating the orientation field in a JPEG file is anything but trivial. I also had doubts regarding the time performance of the image rotation loop (Processing stores the images in a one-dimensional array). To reduce the code duplication I assigned the new location of each pixel inside a doubly-nested loop.
int x2, y2;
for (int x = 0; x < img.width; x++)
for (int y = 0; y < img.height; y++) {
switch (currentOrientation) {
case 8:
x2 = y;
y2 = img2.height - x - 1;
break;
case 3:
x2 = img2.width - x - 1;
y2 = img2.height - y - 1;
break;
case 6:
x2 = img2.width - y - 1;
y2 = x;
break;
default:
throw new IllegalStateException();
}
img2.pixels[y2 * img2.width + x2] = img.pixels[y * img.width + x];
}