Skip to content

Metatadata Manipulation and More Cool Things

dragon66 edited this page Jun 3, 2019 · 2 revisions
  • Show image metadata (Support JPEG, GIF, BMP, PNG, TIFF images)

     import java.io.IOException;
     import java.util.Collection;
     import java.util.Iterator;
     import java.util.Map;
    
     import com.icafe4j.image.meta.Metadata;
     import com.icafe4j.image.meta.MetadataEntry;
     import com.icafe4j.image.meta.MetadataType;
     import com.icafe4j.image.meta.xmp.XMP;
     import com.icafe4j.string.StringUtils;
    
     public class TestMetadata {
        public static void main(String[] args) throws IOException {
           Map<MetadataType, Metadata> metadataMap = Metadata.readMetadata(args[0]);
           for(Map.Entry<MetadataType, Metadata> entry : metadataMap.entrySet()) {
              Metadata meta = entry.getValue();
              if(meta instanceof XMP) XMP.showXMP((XMP)meta);
              else {
      	    Iterator<MetadataEntry> iterator = entry.getValue().iterator();
      	    while(iterator.hasNext()) {
      	         MetadataEntry item = iterator.next();
      	         printMetadata(item, "", "     ");
                }
              }			
           }
        }
    
        private static void printMetadata(MetadataEntry entry, String indent, String increment) {
           System.out.println(indent + entry.getKey() + (StringUtils.isNullOrEmpty(entry.getValue())? "" : ": " + entry.getValue()));
           if(entry.isMetadataEntryGroup()) {
              indent += increment;
              Collection<MetadataEntry> entries = entry.getMetadataEntries();
              for(MetadataEntry e : entries) {
                  printMetadata(e, indent, increment);
              }			
           }
        }
     }
    

Run something like "java TestMetadata images\12.jpg" to test.


  • Extract thumbnail image from a JPEG image

    There are several places where you may find a JPEG thumbnail - APP0, APP1, and APP13 segments. APP1 segment often stores EXIF data along with a thumbnail. APP0 also called JFIF segment which may contain a thumbnail image but this is rather rare. APP13 also referred to as Adobe IRB (Image Resource Block) which often contains a thumbnail. We can extract JFIF, EXIF and/or APP13 Photoshop thumbnail images from JPEG image like this: import java.io.FileInputStream; import com.icafe4j.image.meta.Metadata;

      public class TestExtractThumbnails {
      	public static void main(String[] args) throws Exception {
      		FileInputStream fin = new FileInputStream(args[0]);
      		Metadata.extractThumbnails(fin, "thumbnail");
      		fin.close();
      	}	
      }
    

    We run java TestExtractThumbnails images\YCCK_color.jpg, the extracted thumbnails are "thumbnail_exif_t.jpg" and "thumbnail_photoshop_t.jpg" respectively for EXIF and Adobe APP13 segment. Here are the original and extracted thumbnail images:

example adobe_thumbnail ![exif_thumbnail] (https://github.com/dragon66/icafe/raw/master/images/thumbnail_exif_t.jpg)

Note: Theoretically, the above code could extract APP0, EXIF, and APP13 thumbnails at the same time. But I have never seen a JPEG with APP0 thumbnail.


  • Insert thumbnail image to a JPEG image

    With "icafe", We can also insert an EXIF thumbnail image into a JPEG image. This is an on-going function given the complexity of the EXIF data structure. An interesting thing is the thumbnail of a JPEG image is NOT necessarily the same content as the main image itself depending on the image manipulation software that was used to create the JPEG. Now let's see what this means by inserting a thumbnail resized from a different image to an existing image.

      import java.util.Date;
      import com.icafe4j.image.meta.Metadata;
      import com.icafe4j.image.meta.exif.Exif;
      import com.icafe4j.image.meta.exif.JpegExif;
      import com.icafe4j.image.meta.exif.ExifTag;
      import com.icafe4j.image.tiff.ASCIIField;
      import com.icafe4j.image.tiff.IFD;
      import com.icafe4j.image.tiff.RationalField;
      import com.icafe4j.image.tiff.ShortField;
      import com.icafe4j.image.tiff.TiffField;
      import com.icafe4j.image.tiff.UndefinedField;
    
      public class TestInsertThumbnail {
      	public static void main(String[] args) throws Exception {
      		FileInputStream fin = new FileInputStream(args[0]);
      		FileOutputStream fout = new FileOutputStream("thumbnailAdded.jpg");
      		Metadata.insertExif(fin, fout, populateExif());
      		fin.close();
      		fout.close();
      	}
      	
      	// This method is for testing only
          private static Exif populateExif() throws Exception {
              // Create an EXIF wrapper
              Exif exif = new JpegExif();		
              DateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
              exif.addExifField(ExifTag.EXPOSURE_TIME, FieldType.RATIONAL, new int[] {10, 600});
              exif.addExifField(ExifTag.FNUMBER, FieldType.RATIONAL, new int[] {49, 10});
              exif.addExifField(ExifTag.ISO_SPEED_RATINGS, FieldType.SHORT, new short[]{273});
              //All four bytes should be interpreted as ASCII values - represents [0220]
              exif.addExifField(ExifTag.EXIF_VERSION, FieldType.UNDEFINED, new byte[]{48, 50, 50, 48});
              exif.addExifField(ExifTag.DATE_TIME_ORIGINAL, FieldType.ASCII, formatter.format(new Date()));
              exif.addExifField(ExifTag.DATE_TIME_DIGITIZED, FieldType.ASCII, formatter.format(new Date()));
              exif.addExifField(ExifTag.FOCAL_LENGTH, FieldType.RATIONAL, new int[] {240, 10});
              // Insert ThumbNailIFD
              // Since we don't provide thumbnail image, it will be created later from the input stream
              exif.addThumbnail(null); 
      	
              return exif;
          }
      }
    

    Run "java TestInsertThumbnail images\10.jpg", the output file is "thumbnailAdded.jpg"

    thumbnailAdded

    which contains a thumbnail image "exif_thumbnail.jpg"

    exif_thumbnail


  • Remove EXIF data from JPEG image

    Using ICAFE, it's possible to remove EXIF data from JPEG or TIFF images. EXIF is a format that is a standard for storing interchange information in digital photography image files. Almost all new digital cameras use the EXIF to store information such as shutter speed, exposure compensation, F number, if a flash was used, date and time the image was taken. Some images may even store GPS information so you can easily see where the images were taken!

    The following is part of the EXIF dump of "example.jpg":

     		APP1: EXIF/XMP/ExtendedXMP
      	Byte order: Intel LITTLE_ENDIAN
      	************************************************
      	IFD 0 => offset byte 8
      	Total number of fields: 11
      	Field 0 =>
      	Tag: ImageDescription [Value: 0x010E] (Baseline)
      	Data type: ASCII
      	Field length: 32
      	Field value: 
      	Field 1 =>
      	Tag: Make [Value: 0x010F] (Baseline)
      	Data type: ASCII
      	Field length: 6
      	Field value: Canon
      	Field 2 =>
      	Tag: Model [Value: 0x0110] (Baseline)
      	Data type: ASCII
      	Field length: 24
      	Field value: Canon PowerShot SX20 IS
      	Field 3 =>
      	Tag: Orientation [Value: 0x0112] (Baseline)
      	Data type: Short
      	Field length: 1
      	Field value: [1] TopLeft
      	Field 4 =>
      	Tag: XResolution [Value: 0x011A] (Baseline)
      	Data type: Rational
      	Field length: 1
      	Field value: [180/1]
      	Field 5 =>
      	Tag: YResolution [Value: 0x011B] (Baseline)
      	Data type: Rational
      	Field length: 1
      	Field value: [180/1]
      	Field 6 =>
      	Tag: ResolutionUnit [Value: 0x0128] (Baseline)
      	Data type: Short
      	Field length: 1
      	Field value: [2] Inch
      	Field 7 =>
      	Tag: DateTime [Value: 0x0132] (Baseline)
      	Data type: ASCII
      	Field length: 20
      	Field value: 2010:04:04 08:58:03
      	Field 8 =>
      	Tag: YCbCrPositioning [Value: 0x0213] (Extended)
      	Data type: Short
      	Field length: 1
      	Field value: [2] Cosited
      	Field 9 =>
      	Tag: ExifSubIFD [Value: 0x8769] (Private)
      	Data type: Long
      	Field length: 1
      	Field value: [244] 
      	<<ExifSubIFD: offset byte 130>>
      	-----Total number of fields: 32
      	-----Field 0 =>
      	-----Tag: ExposureTime [Value: 0x829A]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [1/640]
      	-----Field 1 =>
      	-----Tag: FNumber [Value: 0x829D]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [28/10]
      	-----Field 2 =>
      	-----Tag: ISOSpeedRatings [Value: 0x8827]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [160] 
      	-----Field 3 =>
      	-----Tag: ExifVersion [Value: 0x9000]
      	-----Data type: Undefined
      	-----Field length: 4
      	-----Field value: 0221
      	-----Field 4 =>
      	-----Tag: DateTimeOriginal [Value: 0x9003]
      	-----Data type: ASCII
      	-----Field length: 20
      	-----Field value: 2010:04:04 08:58:03
      	-----Field 5 =>
      	-----Tag: DateTimeDigitized [Value: 0x9004]
      	-----Data type: ASCII
      	-----Field length: 20
      	-----Field value: 2010:04:04 08:58:03
      	-----Field 6 =>
      	-----Tag: ComponentConfiguration [Value: 0x9101]
      	-----Data type: Undefined
      	-----Field length: 4
      	-----Field value: [0x01,0x02,0x03,0x00]
      	-----Field 7 =>
      	-----Tag: CompressedBitsPerPixel [Value: 0x9102]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [3/1]
      	-----Field 8 =>
      	-----Tag: ShutterSpeedValue [Value: 0x9201]
      	-----Data type: SRational
      	-----Field length: 1
      	-----Field 9 =>
      	-----Tag: ApertureValue [Value: 0x9202]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [95/32]
      	-----Field 10 =>
      	-----Tag: ExposureBiasValue [Value: 0x9204]
      	-----Data type: SRational
      	-----Field length: 1
      	-----Field 11 =>
      	-----Tag: MaxApertureValue [Value: 0x9205]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [95/32]
      	-----Field 12 =>
      	-----Tag: MeteringMode [Value: 0x9207]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [5] 
      	-----Field 13 =>
      	-----Tag: Flash [Value: 0x9209]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [16] 
      	-----Field 14 =>
      	-----Tag: FocalLength [Value: 0x920A]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [5000/1000]
      	-----Field 15 =>
      	-----Tag: MakerNote [Value: 0x927C]
      	-----Data type: Undefined
      	-----Field length: 2284
      	-----Field value: [0x1B,0x00,0x01 ...]
      	-----Field 16 =>
      	-----Tag: UserComment [Value: 0x9286]
      	-----Data type: Undefined
      	-----Field length: 264
      	-----Field value: [0x00,0x00,0x0000 ...]
      	-----Field 17 =>
      	-----Tag: FlashPixVersion [Value: 0xA000]
      	-----Data type: Undefined
      	-----Field length: 4
      	-----Field value: 0100
      	-----Field 18 =>
      	-----Tag: ColorSpace [Value: 0xA001]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [1] 
      	-----Field 19 =>
      	-----Tag: ExifImageWidth [Value: 0xA002]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [4000] 
      	-----Field 20 =>
      	-----Tag: ExifImageHeight [Value: 0xA003]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [3000] 
      	-----Field 21 =>
      	-----Tag: ExifInteroperabilityOffset [Value: 0xA005]
      	-----Data type: Long
      	-----Field length: 1
      	-----Field value: [3310] 
      	-----<<ExifInteropSubIFD: offset byte 510>>
      	----------Total number of fields: 4
      	----------Field 0 =>
      	----------Tag: InteroperabilityIndex [Value: 0x0001]
      	----------Data type: ASCII
      	----------Field length: 4
      	----------Field value: R98
      	----------Field 1 =>
      	----------Tag: InteroperabilityVersion [Value: 0x0002]
      	----------Data type: Undefined
      	----------Field length: 4
      	----------Field value: [0x30,0x31,0x30,0x30]
      	----------Field 2 =>
      	----------Tag: RelatedImageWidth [Value: 0x1001]
      	----------Data type: Short
      	----------Field length: 1
      	----------Field value: [4000] 
      	----------Field 3 =>
      	----------Tag: RelatedImageLength [Value: 0x1002]
      	----------Data type: Short
      	----------Field length: 1
      	----------Field value: [3000] 
      	-----Field 22 =>
      	-----Tag: FocalPlanXResolution [Value: 0xA20E]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [4000000/244]
      	-----Field 23 =>
      	-----Tag: FocalPlanYResolution [Value: 0xA20F]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [3000000/183]
      	-----Field 24 =>
      	-----Tag: FocalPlanResolutionUnit [Value: 0xA210]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [2] 
      	-----Field 25 =>
      	-----Tag: SensingMethod [Value: 0xA217]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [2] 
      	-----Field 26 =>
      	-----Tag: FileSource [Value: 0xA300]
      	-----Data type: Undefined
      	-----Field length: 1
      	-----Field value: [0x03]
      	-----Field 27 =>
      	-----Tag: CustomRendered [Value: 0xA401]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [0] 
      	-----Field 28 =>
      	-----Tag: ExposureMode [Value: 0xA402]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [0] 
      	-----Field 29 =>
      	-----Tag: WhileBalence [Value: 0xA403]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [0] 
      	-----Field 30 =>
      	-----Tag: DigitalZoomRatio [Value: 0xA404]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [4000/4000]
      	-----Field 31 =>
      	-----Tag: SceneCaptureType [Value: 0xA406]
      	-----Data type: Short
      	-----Field length: 1
      	-----Field value: [0] 
      	Field 10 =>
      	Tag: GPSSubIFD [Value: 0x8825] (Private)
      	Data type: Long
      	Field length: 1
      	Field value: [3364] 
      	<<GPSSubIFD: offset byte 142>>
      	-----Total number of fields: 9
      	-----Field 0 =>
      	-----Tag: GPSVersionID [Value: 0x0000]
      	-----Data type: Byte
      	-----Field length: 4
      	-----Field value: [0x02,0x02,0x00,0x00]
      	-----Field 1 =>
      	-----Tag: GPSLatitudeRef [Value: 0x0001]
      	-----Data type: ASCII
      	-----Field length: 2
      	-----Field value: N
      	-----Field 2 =>
      	-----Tag: GPSLatitude [Value: 0x0002]
      	-----Data type: Rational
      	-----Field length: 3
      	-----Field value: [31/1,11/1,77029/1587]
      	-----Field 3 =>
      	-----Tag: GPSLongitudeRef [Value: 0x0003]
      	-----Data type: ASCII
      	-----Field length: 2
      	-----Field value: W
      	-----Field 4 =>
      	-----Tag: GPSLongitude [Value: 0x0004]
      	-----Data type: Rational
      	-----Field length: 3
      	-----Field value: [7/1,44/1,28024/507]
      	-----Field 5 =>
      	-----Tag: GPSAltitudeRef [Value: 0x0005]
      	-----Data type: Byte
      	-----Field length: 1
      	-----Field value: [0x00]
      	-----Field 6 =>
      	-----Tag: GPSAltitude [Value: 0x0006]
      	-----Data type: Rational
      	-----Field length: 1
      	-----Field value: [48350/27]
      	-----Field 7 =>
      	-----Tag: GPSTimeStamp [Value: 0x0007]
      	-----Data type: Rational
      	-----Field length: 3
      	-----Field value: [8/1,58/1,3/1]
      	-----Field 8 =>
      	-----Tag: GPSDateStamp [Value: 0x001D]
      	-----Data type: ASCII
      	-----Field length: 11
      	-----Field value: 2010:04:04
      	************************************************
      	IFD 1 => offset byte 3570
      	Total number of fields: 6
      	Field 0 =>
      	Tag: Compression [Value: 0x0103] (Baseline)
      	Data type: Short
      	Field length: 1
      	Field value: [6] JPEG ('old-style' JPEG)
      	Field 1 =>
      	Tag: XResolution [Value: 0x011A] (Baseline)
      	Data type: Rational
      	Field length: 1
      	Field value: [180/1]
      	Field 2 =>
      	Tag: YResolution [Value: 0x011B] (Baseline)
      	Data type: Rational
      	Field length: 1
      	Field value: [180/1]
      	Field 3 =>
      	Tag: ResolutionUnit [Value: 0x0128] (Baseline)
      	Data type: Short
      	Field length: 1
      	Field value: [2] Inch
      	Field 4 =>
      	Tag: JPEGInterchangeFormat/JpegIFOffset [Value: 0x0201] (Extended)
      	Data type: Long
      	Field length: 1
      	Field value: [3664] 
      	Field 5 =>
      	Tag: JPEGInterchangeFormatLength/JpegIFByteCount [Value: 0x0202] (Extended)
      	Data type: Long
      	Field length: 1
      	Field value: [5084] 
    

    You can see there are a lot information buried inside this image - apart from camera specifics, it also shows GPS information such as latitude and altitude telling you where this picture was taken making it a privacy hazard.

    Now let's see how we can remove EXIF from a JPEG image:

      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import com.icafe4j.image.meta.Metadata;
      import com.icafe4j.image.meta.MetadataType;
    
      public class TestRemoveExif {
      	public static void main(String[] args) throws Exception {
      		FileInputStream fin = new FileInputStream(args[0]);
      		FileOutputStream fout = new FileOutputStream("ExifRemoved.jpg");
      		Metadata.removeMetadata(fin, fout, MetadataType.EXIF);
      		fin.close();
      		fout.close();
      	}	
      }
    

    Run java TestRemoveExif images/example.jpg, the output is "ExifRemoved.jpg" with EXIF information removed from "example.jpg".

    Still as easy as before plus we don't even need to re-compress the original image!


  • NEW! Bulk insertion of a collection of metadata into a JPEG or TIFF image simultaneously without creating temporary file

    In the past, it was a bit clumsy if we want to insert more than one metadata types into an image by one method call. We have to keep an intermediate (temporary) file if we need to insert another type of metadata after the previous one. Now ICAFE supports insertion of a collection of metadata into JPEG and TIFF through one simple method call on Metadata as Metadata.insertMetadata().

      fin = new FileInputStream("images/exif.tif");
      fout = new FileOutputStream("exif-exif-iptc-comment- inserted.tif");
    
      List<Metadata> metaList = new ArrayList<Metadata>();
      metaList.add(populateExif(TiffExif.class));
      metaList.add(createIPTC());
      metaList.add(new Comments(Arrays.asList("Comment1", "Comment2")));
      
      Metadata.insertMetadata(metaList, fin, fout);
              
      // This method is for testing only
      private static Exif populateExif(Class<?> exifClass) throws IOException {
      	// Create an EXIF wrapper
      	Exif exif = exifClass == (TiffExif.class)?new TiffExif() : new JpegExif();
      	exif.addImageField(TiffTag.WINDOWS_XP_AUTHOR, FieldType.WINDOWSXP, "Author");
      	exif.addImageField(TiffTag.WINDOWS_XP_KEYWORDS, FieldType.WINDOWSXP, "Copyright;Author");
      	DateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
      	exif.addExifField(ExifTag.EXPOSURE_TIME, FieldType.RATIONAL, new int[] {10, 600});
      	exif.addExifField(ExifTag.FNUMBER, FieldType.RATIONAL, new int[] {49, 10});
      	exif.addExifField(ExifTag.ISO_SPEED_RATINGS, FieldType.SHORT, new short[]{273});
      	//All four bytes should be interpreted as ASCII values - represents [0220] - new byte[]{48, 50, 50, 48}
      	exif.addExifField(ExifTag.EXIF_VERSION, FieldType.UNDEFINED, "0220".getBytes());
      	exif.addExifField(ExifTag.DATE_TIME_ORIGINAL, FieldType.ASCII, formatter.format(new Date()));
      	exif.addExifField(ExifTag.DATE_TIME_DIGITIZED, FieldType.ASCII, formatter.format(new Date()));
      	exif.addExifField(ExifTag.FOCAL_LENGTH, FieldType.RATIONAL, new int[] {240, 10});		
      	// Insert ThumbNailIFD
      	// Since we don't provide thumbnail image, it will be created later from the input stream
      	exif.setThumbnailRequired(true);
      	
      	return exif;
      }
      
      private static List<IPTCDataSet> createIPTCDataSet() {
      	List<IPTCDataSet> iptcs = new ArrayList<IPTCDataSet>();
      	iptcs.add(new IPTCDataSet(IPTCApplicationTag.COPYRIGHT_NOTICE, "Copyright 2014-2016, yuwen_66@yahoo.com"));
      	iptcs.add(new IPTCDataSet(IPTCApplicationTag.CATEGORY, "ICAFE"));
      	iptcs.add(new IPTCDataSet(IPTCApplicationTag.KEY_WORDS, "Welcome 'icafe' user!"));
      	
      	return iptcs;
      }
    
      private static IPTC createIPTC() {
      	IPTC iptc = new IPTC();
      	iptc.addDataSets(createIPTCDataSet());
      	return iptc;
      }
    

  • Insert XMP and other metadata into images

    ICAFE can also insert Adobe XMP data as well as other types of metadata such ICC profile, IPTC, Adobe IRB, Exif etc into images. With XMP, for JPEG images, it can automatically spit the input XMP data if it doesn't fit into one APP1 segment. This new feature has removed the dependency on third party API to do the work. A good example can be found in TestMetadata.java from the source test folder where a image with extended XMP data is read into a XMP object and merged into one XMP document. This merged document is then inserted into a new JPEG image. As the size of the merged XMP document will exceed a normal JPEG segment size (about 65500 bytes), icafe will try to split it into one standard XMP part and one extended XMP part. The standard XMP part will fit into one segment and if the extended XMP part can fit into one segment, it will be inserted into one. Otherwise, it will be divided into roughly equal size parts and each one can fit into one segment.


  • "In place" TIFF image manipulation

    With "icafe", we could even do some "in place" TIFF image manipulation. The "TIFFImage" class is a simple wrapper for a TIFF image either single or multipage TIFF. It has methods to add to or remove fields from certain page of a TIFF image. You can also remove pages from a multipage TIFF image. Here is an example to insert a "DateTime" field to the last page of a multipage TIFF:

      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.text.DateFormat;
      import java.text.SimpleDateFormat;
      import java.util.Date;
    
      import com.icafe4j.image.tiff.ASCIIField;
      import com.icafe4j.image.tiff.TIFFImage;
      import com.icafe4j.image.tiff.TiffField;
      import com.icafe4j.image.tiff.TiffTag;
      import com.icafe4j.io.FileCacheRandomAccessInputStream;
      import com.icafe4j.io.FileCacheRandomAccessOutputStream;
      import com.icafe4j.io.RandomAccessInputStream;
      import com.icafe4j.io.RandomAccessOutputStream;
    
      public class TestTIFFImage {
      	// Test manipulate TIFF image
      	public static void main(String[] args) throws IOException {
      		FileInputStream fin = new FileInputStream(args[0]);
      		RandomAccessInputStream rin = new FileCacheRandomAccessInputStream(fin);
      		FileOutputStream fout = new FileOutputStream("NEW.tif");
      		RandomAccessOutputStream rout = new FileCacheRandomAccessOutputStream(fout);
      		TIFFImage tiffImage = new TIFFImage(rin);
      		int numOfPages = tiffImage.getNumOfPages();
      		tiffImage.setWorkingPage(numOfPages - 1); // Add something to the last page
      		DateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss z");
      		TiffField<?> tiffField = new ASCIIField(TiffTag.DATETIME.getValue(), formatter.format(new Date()) + '\0');
      		tiffImage.addField(tiffField);
      		tiffImage.write(rout);
      		rin.close(); // Release resources
      		rout.close(); // Release resources
      		fin.close(); // We need to close the stream explicitly since neither input 
      		fout.close(); // nor output random stream closes the underlying stream
      	}
      }
    

    In the above example, we create a "TIFFImage", pass in a "RandomInputStream" which is used to read the input TIFF image and a "RandomOutputStream" which is used to save as a new TIFF. We first retrieve the number of pages for the input TIFF and set the working page to the last page of it. We then create a "DateTime" field and add to the current working page. Finally we save the new "TIFFImage" as "NEW.tif". If you open the resulting page with a TIFF tool like "AsTiffTagViewer", you will find the "DateTime" field on the last page of the image.


  • Extract ICC_Profile from JPEG, TIFF and PNG images

    This is an example showing how to extract and show ICC_Profile information from a JPEG image:

      import java.io.FileInputStream;
      import com.icafe4j.image.meta.Metadata;
      import com.icafe4j.image.meta.MetadataType;
      import com.icafe4j.image.meta.icc.ICCProfile;
    
      public class TestReadICCProfile {
      	public static void main(String[] args) throws Exception {
      		FileInputStream fin = new FileInputStream(args[0]);
              Map<MetadataType, Metadata> metadataMap = Metadata.readMetadata(fin);
              ICCProfile profile = (ICCProfile)metadataMap.get(MetadataType.ICC_PROFILE);
              if(profile != null)
      			profile.showMetadata();
      		fin.close();
      	}
      }
    

    Run "java TestReadICCProfile images\exif-jpeg-thumbnail-sony-dsc-p150-inverted-colors.jpg" will produce the following output:

      *** Start of ICC_Profile Header ***
      Profile Size: 7261
      CMM Type: Lino
      Version: 2.10
      Profile/Device Class: 'mntr': display devices - CRTs and LCDs
      Color Space: RGB 
      PCS: XYZ 
      Date Created: 1998/2/9, 6:49:0
      Profile File Signature: acsp
      Primary Platform Signature: MSFT
      Flags: not embedded, used independently
      Device Manufacturer: IEC 
      Device Model: sRGB
      Device Attributes: reflective, glossy, positive, color
      Rendering Intent: perceptual
      PCS Illuminant: X = 0.9642029, Y = 1.0, Z = 0.8249054
      Profile Creator: HP  
      Profile ID: [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
      *** End of ICC_Profile Header ***
      *** Start of ICC_Profile Tag Table ***
      Tag Count: 17
      Tag# 0, Tag Name: bTRC (blueTRCTag), Data Offset: 5201, Data Length: 2060
      Tag# 1, Tag Name: bXYZ (blueMatrixColumnTag), Data Offset: 575, Data Length: 20
      Tag# 2, Tag Name: BKPT (mediaBlackPointTag), Data Offset: 515, Data Length: 20
      Tag# 3, Tag Name: cprt (copyrightTag), Data Offset: 336, Data Length: 51
      Tag# 4, Tag Name: desc (profileDescriptionTag), Data Offset: 387, Data Length: 108
      Tag# 5, Tag Name: dmdd (deviceModelDescTag), Data Offset: 707, Data Length: 136
      Tag# 6, Tag Name: dmnd (deviceMfgDescTag), Data Offset: 595, Data Length: 112
      Tag# 7, Tag Name: gTRC (greenTRCTag), Data Offset: 3141, Data Length: 2060
      Tag# 8, Tag Name: gXYZ (greenMatrixColumnTag), Data Offset: 555, Data Length: 20
      Tag# 9, Tag Name: lumi (luminanceTag), Data Offset: 1013, Data Length: 20
      Tag# 10, Tag Name: meas (measurementTag), Data Offset: 1033, Data Length: 36
      Tag# 11, Tag Name: rTRC (redTRCTag), Data Offset: 1081, Data Length: 2060
      Tag# 12, Tag Name: rXYZ (redMatrixColumnTag), Data Offset: 535, Data Length: 20
      Tag# 13, Tag Name: tech (technologyTag), Data Offset: 1069, Data Length: 12
      Tag# 14, Tag Name: view (viewingConditionsTag), Data Offset: 977, Data Length: 36
      Tag# 15, Tag Name: vued (viewingCondDescTag), Data Offset: 843, Data Length: 134
      Tag# 16, Tag Name: wtpt (mediaWhitePointTag), Data Offset: 495, Data Length: 20
      *** End of ICC_Profile Tag Table ***
    

  • Obtain text information embedded in PNG

    Quoting PNG 1.2 specification:

    The iTXt, tEXt, and zTXt chunks are used for conveying textual information associated with the image. This specification refers to them generically as "text chunks".

    Each of the text chunks contains as its first field a keyword that indicates the type of information represented by the text string. The following keywords are predefined and should be used where appropriate:

         Title            Short (one line) title or caption for image
         Author           Name of image's creator
         Description      Description of image (possibly long)
         Copyright        Copyright notice
         Creation Time    Time of original image creation
         Software         Software used to create the image
         Disclaimer       Legal disclaimer
         Warning          Warning of nature of content
         Source           Device used to create the image
         Comment          Miscellaneous comment; conversion from
      					GIF comment
    

    "icafe" is able to extract from and insert into PNG all the three kinds of textural information. Let's see an example:

      import java.io.FileInputStream;
      import java.io.FileOutputStream;
    
      import com.icafe4j.image.png.PNGTweaker;
      import com.icafe4j.image.png.Chunk;
      import com.icafe4j.image.png.ChunkType;
      import com.icafe4j.image.png.TextBuilder;
    
      public class TestPNGTweaker {
      	public static void main(String[] args) throws Exception {
      		String text = PNGTweaker.read_text_chunks(args[0]);
      		System.out.print(text);
      	   
      		TextBuilder builder = new TextBuilder(ChunkType.ITXT).keyword("Author").text("Wen Yu");
      		Chunk authorChunk = builder.build();
      		builder.keyword("Software").text("PNGTweaker 1.0");
      		Chunk softwareChunk = builder.build();
      		builder.keyword("Copyright").text("Copyright 2015 Wen Yu (yuwen_66@yahoo.com).");
      		Chunk copyrightChunk = builder.build();
      		
      		Chunk[] chunks = new Chunk[] {authorChunk, softwareChunk, copyrightChunk};
         
      		FileInputStream fi = new FileInputStream(args[0]);
      		FileOutputStream fo = new FileOutputStream("textInserted.png");
      		
      		PNGTweaker.insertChunks(chunks,	fi, fo);
      		
      		fi.close();
      		fo.close();       
      	}
      }
    

    Run "java images\ctzn0g04.png", this is the output from official png test "ctzn0g04.png":

      tEXt chunk:
      Title: PngSuite
      **********************
      tEXt chunk:
      Author: Willem A.J. van Schaik
      (willem@schaik.com)
      **********************
      zTXt chunk:
      Copyright: Copyright Willem van Schaik, Singapore 1995-96
      **********************
      zTXt chunk:
      Description: A compilation of a set of images created to test the
      various color-types of the PNG format. Included are
      black&white, color, paletted, with alpha channel, with
      transparency formats. All bit-depths allowed according
      to the spec are present.
      **********************
      zTXt chunk:
      Software: Created on a NeXTstation color using "pnmtopng".
      **********************
      zTXt chunk:
      Disclaimer: Freeware.
      **********************
      End of Image
    

    This also created a new output PNG image "textInserted.png" example


  • Extract XMP data from or insert XMP data into JPEG, TIFF, PNG, GIF etc

    Adobe sometimes uses APP1 segment to store XMP data which you can also extract with "icafe". The following are the XMP data extracted from "cmykjpg.jpg":

      <?xpacket begin="?" id="W5M0MpCehiHzreSzNTczkc9d"?>
      <x:xmpmeta x:xmptk='Adobe XMP Core 5.5-c021 79.154911, 2013/10/29-11:47:16        ' xmlns:x='adobe:ns:meta/'>
          <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
      	<rdf:Description dc:format='image/jpeg' photoshop:ColorMode='4' photoshop:ICCProfile='Uncoated FOGRA29 (ISO 12647-2:2004)' rdf:about='' xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:photoshop='http://ns.adobe.com/photoshop/1.0/' xmlns:stEvt='http://ns.adobe.com/xap/1.0/sType/ResourceEvent#' xmlns:xmp='http://ns.adobe.com/xap/1.0/' xmlns:xmpMM='http://ns.adobe.com/xap/1.0/mm/' xmp:CreateDate='2014-05-11T20:26:27-04:00' xmp:CreatorTool='Adobe Photoshop CC (Windows)' xmp:MetadataDate='2014-06-18T12:57:14-04:00' xmp:ModifyDate='2014-06-18T12:57:14-04:00' xmpMM:DocumentID='xmp.did:8b329805-62ae-f943-9fdc-8b03403c0117' xmpMM:InstanceID='xmp.iid:ac1efde9-349e-a24c-8232-280fa3a6f6d9' xmpMM:OriginalDocumentID='xmp.did:8b329805-62ae-f943-9fdc-8b03403c0117'>
      	    <xmpMM:History>
      		<rdf:Seq>
      		    <rdf:li stEvt:action='created' stEvt:instanceID='xmp.iid:8b329805-62ae-f943-9fdc-8b03403c0117' stEvt:softwareAgent='Adobe Photoshop CC (Windows)' stEvt:when='2014-05-11T20:26:27-04:00'>
      		    </rdf:li>
      		    <rdf:li stEvt:action='converted' stEvt:parameters='from image/png to image/jpeg'>
      		    </rdf:li>
      		    <rdf:li stEvt:action='saved' stEvt:changed='/' stEvt:instanceID='xmp.iid:ac1efde9-349e-a24c-8232-280fa3a6f6d9' stEvt:softwareAgent='Adobe Photoshop CC (Windows)' stEvt:when='2014-06-18T12:57:14-04:00'>
      		    </rdf:li>
      		</rdf:Seq>
      	    </xmpMM:History>
      	</rdf:Description>
          </rdf:RDF>
      </x:xmpmeta>
      <?xpacket end="w"?>
    

    ICAFE is also capable of inserting XMP data into different kinds of images which allow XMP data. It handles both standard XMP and extended XMP when inserting XMP data into JPEG images and the size of the XMP data exceeds a single JPEG segment capacity. In this case the whole XMP data is automatically split into standard and extended XMP parts and inserted into the image separately. This is transparent to the end user. No need to use third party API like Adobe XMP core to do this.

    The above examples are just part of the cool things that "icafe" could do. You can also remove ICC_Profile from JPEG, PNG, and TIFF images; manipulate PNG image trunks - inserting, deleting auxiliary chunks, merge or split IDATA chunk etc. The "TIFFTweaker" tool are able to insert pages into or remove pages from multi-page TIFF images as well.


  • Extract Google Camera's depth map and Google Cardboard Camera image and audio pair from JPEG images.

    Google has recently found some interesting usages for the old metadata embedded in Adobe XMP.

    One of the usage is Google's Depth Map data which in fact is a grayscale image hidden inside the actual image as a metadata, and in the case of JPEG, as XMP data to create blur effect.

    As the data itself is too big to fit into one JPEG APP1 block, the data is separated into a normal XMP and an extendedXMP. The normal XMP will fit into one APP1 block and the extendedXMP will be split is needed to make each part fits into one APP1. ICAFE can extract both normal XMP and extendedXMP parts to later merge them together.

    On the following composed image, the left hand side is the image as seen by normal image decoders while the right hand side shows the depth map image extracted by ICAFE from the XMP metadata.

    The other usage of XMP metadata by Google is Cardboard Camera VR which can embed both image and audio in a normal image. The normal image act as the left eye image and the embedded one as the right eye image. You can also embed an audio to enjoy while having a VR experience. The following are what extracted by ICAFE from the VR image provided here.

    Here are the image extracted from the original image:

    Unfortunately I can't figure out a way to insert the extracted MP4 audio here (Bird chirping and wave rustling)

    Things to be desired


Clone this wiki locally