EXIF Data

0
171

EXIF stands for Exchangeable Image File. EXIF data is low-level information stored in JPEG images. EXIF was created by the Japan Electronic Industries Development Association (JEIDA) and became a convention adopted across camera manufacturers, including on mobile devices. You can read about the EXIF format at http://en.wikipedia.org/wiki/Exchangeable_image_file_format and http://www.exif.org/Exif2-2.PDF.

EXIF data can include the date and time the image was created, the camera manufacturer and camera settings, location information, and even a thumbnail image. Visit Jeffrey Friedl’s website at http://regex.info/exif.cgi and load a JPEG image to see the information it contains.

In AIR for Android, you could use the geolocation API to get location information and associate it with the photo you just shot, but it is more efficient to get this information directly from the image if it is available. To store image location on an Android device when taking a picture, the user must have Location & Security→Use GPS Satellites selected and then turn on the camera’s Store Location option.

Several open source AS3 libraries are available for reading EXIF data. I chose the one by Kenichi Ishibashi. You can download his library using Subversion at http://code.shichiseki.jp/as3/ExifInfo/. Ishibashi’s Loader class uses the loadBytes function and passes its data as a ByteArray to access the raw data information. Import his package to your class.

Our first example loads an image from the Gallery, reads its thumbnail data, and displays it. Note that thumbnail creation varies among devices and is not always available. Check that it exists before trying to display it:

import flash.display.Loader;
import flash.display.MovieClip;
import flash.media.CameraRoll;
import flash.media.MediaPromise;
import flash.events.MediaEvent;
import flash.events.Event;
import flash.net.URLRequest
import jp.shichiseki.exif.*;
var loader:ExifLoader;
var cameraRoll:CameraRoll;
function Exif1() {
if (CameraRoll.supportsBrowseForImage) {
init();
}
}
function init():void {
cameraRoll = new CameraRoll();
cameraRoll.addEventListener(MediaEvent.SELECT, onSelect);
cameraRoll.browseForImage();
}
function onSelect(event:MediaEvent):void {
var promise:MediaPromise = event.data as MediaPromise;
loader = new ExifLoader();
loader.addEventListener(Event.COMPLETE, imageLoaded);
loader.load(new URLRequest(promise.file.url));
}
function imageLoaded(event:Event):void {
var exif:ExifInfo = loader.exif as ExifInfo;
if (exif.thumbnailData) {
var thumbLoader:Loader = new Loader();
thumbLoader.loadBytes(exif.thumbnailData);
addChild(thumbLoader);
}
}

The next example also lets you choose an image from the device’s Gallery and display its geographic information. The user must have GPS enabled and must have authorized the camera to save the location when the picture was taken:

import flash.display.Loader;
import flash.display.MovieClip;
import flash.media.CameraRoll;
import flash.media.MediaPromise;
import flash.events.MediaEvent;
import flash.events.Event;
import flash.net.URLRequest
import flash.text.TextField;
import flash.text.TextFormat;
import flash.text.TextFieldAutoSize;
import jp.shichiseki.exif.*;
var cameraRoll:CameraRoll;
var loader:ExifLoader;
if (CameraRoll.supportsBrowseForImage) {
cameraRoll = new CameraRoll();
cameraRoll.addEventListener(MediaEvent.SELECT, onSelect);
cameraRoll.browseForImage();
}
function onSelect(event:MediaEvent):void {
var promise:MediaPromise = event.data as MediaPromise;
loader = new ExifLoader();
loader.addEventListener(Event.COMPLETE, onImageLoaded);
loader.load(new URLRequest(promise.file.url));
}
function onImageLoaded(event:Event):void {
var exif:ExifInfo = loader.exif as ExifInfo;
var textFormat:TextFormat = new TextFormat();
textFormat.size = 40;
textFormat.color = 0x66CC99;
var where:TextField = new TextField();
where.x = 50;
where.y = 200;
where.defaultTextFormat = textFormat;
where.autoSize = TextFieldAutoSize.LEFT;
addChild(where);
if (exif.ifds.gps) {
var gpsIfd:IFD = exif.ifds.gps;
var exifLat:Array = gpsIfd[“GPSLatitude”] as Array;
var latitude:Number = shorten(exifLat, gpsIfd[“GPSLatitudeRef”]);
var exifLon:Array = gpsIfd[“GPSLongitude”] as Array;
var longitude:Number = shorten(exifLon, gpsIfd[“GPSLongitudeRef”]);
where.text = latitude + “n” + longitude;
} else {
where.text = “No geographic information”;
}
}
function shorten(info:Array, reference:String):Number {
var degree:Number = info[0] + (info[1]/60) + (info[2]/3600);
// position from Greenwich and equator
if (reference == “S” || reference == “E”) {
degree * -1;
}
return degree;
}

Base 60 is commonly used to store geographic coordinates in degrees. Degrees, minutes, and seconds are stored separately. Put them back together and sign them depending on whether they are south of the equator and east of Greenwich Mean Time.

Displaying latitude and longitude is not very helpful, nor is it interesting for most users. But you can render a static map using latitude and longitude or retrieve an address.