Sample image I used, lots of EXIF info on this shot of a 1971 SL70 that I am rebuilding.
I started by looking at JAI-imageio, which is the current home of the old Sun JAI project (to be fair, that and more), but I couldn't readily figure out how to use it to write EXIF data. The capability might be there, but I couldn't find any substantive documentation or examples (apart from JavaDocs, which are useful yes, but I needed more hand holding to start out). When I ran into this basic getting started stumbling block I broadened my search.
package com.totsp.imageio;
// no warranty expressed, implied, blah-blah - back up any images before you try this
// imports omitted for brevity - see SVN below
public class SanselanDemo {
/**
* Read metadata from image file and display it.
* @param file
*/
public void readMetaData(File file) {
IImageMetadata metadata = null;
try {
metadata = Sanselan.getMetadata(file);
} catch (ImageReadException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
if (metadata instanceof JpegImageMetadata) {
JpegImageMetadata jpegMetadata = (JpegImageMetadata) metadata;
System.out.println("\nFile: " + file.getPath());
printTagValue(jpegMetadata,
TiffConstants.TIFF_TAG_XRESOLUTION);
printTagValue(jpegMetadata,
TiffConstants.TIFF_TAG_DATE_TIME);
printTagValue(jpegMetadata,
TiffConstants.EXIF_TAG_DATE_TIME_ORIGINAL);
printTagValue(jpegMetadata,
TiffConstants.EXIF_TAG_CREATE_DATE);
printTagValue(jpegMetadata,
TiffConstants.EXIF_TAG_ISO);
printTagValue(jpegMetadata,
TiffConstants.EXIF_TAG_SHUTTER_SPEED_VALUE);
printTagValue(jpegMetadata,
TiffConstants.EXIF_TAG_APERTURE_VALUE);
printTagValue(jpegMetadata,
TiffConstants.EXIF_TAG_BRIGHTNESS_VALUE);
// simple interface to GPS data
TiffImageMetadata exifMetadata = jpegMetadata.getExif();
if (exifMetadata != null) {
try {
TiffImageMetadata.GPSInfo gpsInfo = exifMetadata.getGPS();
if (null != gpsInfo) {
double longitude = gpsInfo.getLongitudeAsDegreesEast();
double latitude = gpsInfo.getLatitudeAsDegreesNorth();
System.out.println(" " +
"GPS Description: " + gpsInfo);
System.out.println(" " +
"GPS Longitude (Degrees East): " +
longitude);
System.out.println(" " +
"GPS Latitude (Degrees North): " +
latitude);
}
} catch (ImageReadException e) {
e.printStackTrace();
}
}
System.out.println("EXIF items -");
ArrayList items = jpegMetadata.getItems();
for (int i = 0; i < items.size(); i++) {
Object item = items.get(i);
System.out.println(" " + "item: " +
item);
}
System.out.println();
}
}
private static void printTagValue(
JpegImageMetadata jpegMetadata, TagInfo tagInfo) {
TiffField field = jpegMetadata.findEXIFValue(tagInfo);
if (field == null) {
System.out.println(tagInfo.name + ": " +
"Not Found.");
} else {
System.out.println(tagInfo.name + ": " +
field.getValueDescription());
}
}
/**
* Example of adding an EXIF item to metadata, in this case using ImageHistory field.
* (I have no idea if this is an appropriate use of ImageHistory, or not, just picked
* a field to update that looked like it wasn't commonly mucked with.)
* @param file
*/
public void addImageHistoryTag(File file) {
File dst = null;
IImageMetadata metadata = null;
JpegImageMetadata jpegMetadata = null;
TiffImageMetadata exif = null;
OutputStream os = null;
TiffOutputSet outputSet = new TiffOutputSet();
// establish metadata
try {
metadata = Sanselan.getMetadata(file);
} catch (ImageReadException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// establish jpegMedatadata
if (metadata != null) {
jpegMetadata = (JpegImageMetadata) metadata;
}
// establish exif
if (jpegMetadata != null) {
exif = jpegMetadata.getExif();
}
// establish outputSet
if (exif != null) {
try {
outputSet = exif.getOutputSet();
} catch (ImageWriteException e) {
e.printStackTrace();
}
}
if (outputSet != null) {
// check if field already EXISTS - if so remove
TiffOutputField imageHistoryPre = outputSet
.findField(TiffConstants.EXIF_TAG_IMAGE_HISTORY);
if (imageHistoryPre != null) {
outputSet.removeField(TiffConstants.EXIF_TAG_IMAGE_HISTORY);
}
// add field
try {
String fieldData = "ImageHistory-" + System.currentTimeMillis();
TiffOutputField imageHistory = new TiffOutputField(
ExifTagConstants.EXIF_TAG_IMAGE_HISTORY,
TiffFieldTypeConstants.FIELD_TYPE_ASCII,
fieldData.length(),
fieldData.getBytes());
TiffOutputDirectory exifDirectory = outputSet.getOrCreateExifDirectory();
exifDirectory.add(imageHistory);
} catch (ImageWriteException e) {
e.printStackTrace();
}
}
// create stream using temp file for dst
try {
dst = File.createTempFile("temp-" + System.currentTimeMillis(), ".jpeg");
os = new FileOutputStream(dst);
os = new BufferedOutputStream(os);
} catch (IOException e) {
e.printStackTrace();
}
// write/update EXIF metadata to output stream
try {
new ExifRewriter().updateExifMetadataLossless(file,
os, outputSet);
} catch (ImageReadException e) {
e.printStackTrace();
} catch (ImageWriteException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
}
}
}
// copy temp file over original file
try {
FileUtils.copyFile(dst, file);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
File bikeFile = new File("data/bike.jpg");
SanselanDemo demo = new SanselanDemo();
System.out.println("BEFORE update");
demo.readMetaData(bikeFile);
demo.addImageHistoryTag(bikeFile);
System.out.println("\nAFTER update");
demo.readMetaData(bikeFile);
}
}
BEFORE update
File: data\bike.jpg
XResolution: 180
Date Time: '2008:03:23 01:36:18'
Date Time Original: '2008:03:23 01:36:18'
Create Date: '2008:03:23 01:36:18'
ISO: 80
Shutter Speed Value: 255/32 (7.969)
Aperture Value: 4
Brightness Value: Not Found.
EXIF items -
item: Make: 'Canon'
item: Model: 'Canon PowerShot A590 IS'
item: Orientation: 1
item: XResolution: 180
item: YResolution: 180
item: Resolution Unit: 2
item: Modify Date: '2008:03:23 01:36:18'
item: YCbCr Positioning: 1
item: Exif Offset: 2384
item: Exposure Time: 1/250 (0.004)
item: FNumber: 4
item: ISO: 80
item: Exif Version: 48, 50, 50, 48
item: Date Time Original: '2008:03:23 01:36:18'
item: Create Date: '2008:03:23 01:36:18'
item: Components Configuration: 1, 2, 3, 0
item: Compressed Bits Per Pixel: 3
item: Shutter Speed Value: 255/32 (7.969)
item: Aperture Value: 4
item: Exposure Compensation: 0
item: Max Aperture Value: 88/32 (2.75)
item: Metering Mode: 5
item: Flash: 24
item: Focal Length: 5800/1000 (5.8)
item: Maker Note: 25,...)
item: UserComment: ''
item: Flashpix Version: 48, 49, 48, 48
item: Color Space: 1
item: Exif Image Width: 3264
item: Exif Image Length: 2448
item: Interop Offset: 3128
item: Focal Plane XResolution: 3264000/225 (14,506.667)
item: Focal Plane YResolution: 2448000/169 (14,485.207)
item: Focal Plane Resolution Unit: 2
item: Image History: 'ImageHistory-1207245408150'
item: Sensing Method: 2
item: File Source: 3
item: Custom Rendered: 0
item: Exposure Mode: 0
item: White Balance: 0
item: Digital Zoom Ratio: 1
item: Scene Capture Type: 0
item: Interop Index: 'R98'
item: Interop Version: 48, 49, 48, 48
item: Related Image Width: 3264
item: Related Image Length: 2448
item: Compression: 6
item: XResolution: 180
item: YResolution: 180
item: Resolution Unit: 2
item: Jpg From Raw Start: 5108
item: Jpg From Raw Length: 6322
AFTER update
File: data\bike.jpg
XResolution: 180
Date Time: '2008:03:23 01:36:18'
Date Time Original: '2008:03:23 01:36:18'
Create Date: '2008:03:23 01:36:18'
ISO: 80
Shutter Speed Value: 255/32 (7.969)
Aperture Value: 4
Brightness Value: Not Found.
EXIF items -
item: Make: 'Canon'
item: Model: 'Canon PowerShot A590 IS'
item: Orientation: 1
item: XResolution: 180
item: YResolution: 180
item: Resolution Unit: 2
item: Modify Date: '2008:03:23 01:36:18'
item: YCbCr Positioning: 1
item: Exif Offset: 2384
item: Exposure Time: 1/250 (0.004)
item: FNumber: 4
item: ISO: 80
item: Exif Version: 48, 50, 50, 48
item: Date Time Original: '2008:03:23 01:36:18'
item: Create Date: '2008:03:23 01:36:18'
item: Components Configuration: 1, 2, 3, 0
item: Compressed Bits Per Pixel: 3
item: Shutter Speed Value: 255/32 (7.969)
item: Aperture Value: 4
item: Exposure Compensation: 0
item: Max Aperture Value: 88/32 (2.75)
item: Metering Mode: 5
item: Flash: 24
item: Focal Length: 5800/1000 (5.8)
item: Maker Note: 25, ...)
item: UserComment: ''
item: Flashpix Version: 48, 49, 48, 48
item: Color Space: 1
item: Exif Image Width: 3264
item: Exif Image Length: 2448
item: Interop Offset: 3128
item: Focal Plane XResolution: 3264000/225 (14,506.667)
item: Focal Plane YResolution: 2448000/169 (14,485.207)
item: Focal Plane Resolution Unit: 2
item: Image History: 'ImageHistory-1207247905886'
item: Sensing Method: 2
item: File Source: 3
item: Custom Rendered: 0
item: Exposure Mode: 0
item: White Balance: 0
item: Digital Zoom Ratio: 1
item: Scene Capture Type: 0
item: Interop Index: 'R98'
item: Interop Version: 48, 49, 48, 48
item: Related Image Width: 3264
item: Related Image Length: 2448
item: Compression: 6
item: XResolution: 180
item: YResolution: 180
item: Resolution Unit: 2
item: Jpg From Raw Start: 5108
item: Jpg From Raw Length: 6322
Chatter
9 hours 40 min ago
13 hours 2 min ago
1 day 15 hours ago
2 days 10 hours ago
2 days 19 hours ago
3 days 5 hours ago
3 days 6 hours ago
3 days 13 hours ago
4 days 5 hours ago
4 days 8 hours ago