Manipulating JPEG Images
Using ExifData Class to Read and Modify JPEG EXIF Tags
Almost all digital cameras (including smartphones), scanners and other systems handling image save images with EXIF (Exchangeable Image File) information. Camera settings and scene information are recorded by the camera into the image file. EXIF data also include shutter speed, date and time a photo was taken, focal length, exposure compensation, metering pattern and if a flash was used. Aspose.Imaging APIs has made possible to extract the EXIF information from a given image in a very easy and simple manner. Developers may also write EXIF data to the images or modify the existing information as per their requirement. Aspose.Imaging has provided ExifData class for reading, writing and modifying the EXIF data, where as Aspose.PSD.Exif.Enums namespace contains the relevant enumerations used in the process.
Reading EXIF Data
Aspose.PSD APIs provide means to read EXIF data from a given image. Below provided steps illustrate the usage of ExifData class to read the EXIF information from an image.
- Load PSD Image using the factory method Load.
- Find JPEG thumbnail among PSD resources.
- Extract an instance of ExifData class.
Fetch the required information and write it to console.
Alternatively, developers may also get the specific information using the following code snippet.
Writing and Modifying EXIF Data
Using Aspose.PSD APIs, developers can write new EXIF information and modify existing EXIF data of an image. Both processes (Writing & Modifying) requires loading of an image and getting the EXIF data into an instance of ExifData class. Then one can access properties exposed by ExifData class to set them accordingly. Please note that images for manipulation should be JPEG or TIFF images that are usually PSD thumbnails. Sample code to demonstrate the usage is as follow:
String dataDir = Utils.getDataDir(WritingAndModifyingEXIFData.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Extract exif data and print to the console. | |
JpegExifData exif = ((ThumbnailResource) image.getImageResources()[i]).getJpegOptions().getExifData(); | |
if (exif != null) { | |
// Set LensMake, WhiteBalance, Flash information Save the image | |
exif.setLensMake("Sony"); | |
exif.setWhiteBalance(ExifWhiteBalance.Auto); | |
exif.setFlash(ExifFlash.Fired); | |
} | |
} | |
} | |
image.save(dataDir + "aspose_out.psd"); | |
} |
Extracting Thumbnails from PSD Resources
Thumbnails are reduced-size versions of pictures, used to display a significant part of the picture instead of the full frame. Some image files (especially the ones shot with a digital camera) have a thumbnail image embedded in the file. Aspose.PSD API allows you to extract PSD resource thumbnails and store it separately on disk. Thumbnail resources contain ExifData.Thumbnail property that can retrieve the thumbnail data. The code snippet provided below demonstrates how to use it.
String dataDir = Utils.getDataDir(ExtractThumbnailFromPSD.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Extract thumbnail data and store it as a separate image file. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
int[] data = ((ThumbnailResource) image.getImageResources()[i]).getThumbnailArgb32Data(); | |
try (PsdImage extractedThumnailImage = new PsdImage(thumbnail.getWidth(), thumbnail.getHeight())) { | |
extractedThumnailImage.saveArgb32Pixels(extractedThumnailImage.getBounds(), data); | |
extractedThumnailImage.save(dataDir + "extracted_thumbnail.jpg", new JpegOptions()); | |
} | |
} | |
} | |
} |
Use the approach discussed above to store the thumbnail to other supported file formats. If you wish to export thumbnail data to other image formats such as BMP & PNG, please use other image export options.
Extracting Thumbnails from JFIF Segments
It is also possible to extract thumbnails from the ExifData or JFIF segment of PSD thumbnail resources. The following code shows how to perform the extraction of thumbnail data from JFIF or ExifData segment:
String dataDir = Utils.getDataDir(ExtractThumbnailFromPSD.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Extract thumbnail data and store it as a separate image file. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
int[] data = ((ThumbnailResource) image.getImageResources()[i]).getThumbnailArgb32Data(); | |
try (PsdImage extractedThumnailImage = new PsdImage(thumbnail.getWidth(), thumbnail.getHeight())) { | |
extractedThumnailImage.saveArgb32Pixels(extractedThumnailImage.getBounds(), data); | |
extractedThumnailImage.save(dataDir + "extracted_thumbnail.jpg", new JpegOptions()); | |
} | |
} | |
} | |
} |
Use the approach discussed above to store the thumbnail to other supported file formats. If you wish to export thumbnail data to other image formats such as BMP & PNG, please use other image export options.
Add Thumbnail to JFIF Segment
The code snippet below demonstrates how to use the JFIF.Thumbnail property to add a thumbnail image to the JFIF segment of a loaded PSD image.
String dataDir = Utils.getDataDir(AddThumbnailToJFIFSegment.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Adjust thumbnail data. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
JpegExifData exifData = new JpegExifData(); | |
PsdImage thumbnailImage = new PsdImage(100, 100); | |
try { | |
// Fill thumbnail data. | |
int[] pixels = new int[thumbnailImage.getWidth() * thumbnailImage.getHeight()]; | |
for (int j = 0; j < pixels.length; j++) { | |
pixels[j] = j; | |
} | |
// Assign thumbnail data. | |
thumbnailImage.saveArgb32Pixels(thumbnailImage.getBounds(), pixels); | |
exifData.setThumbnail(thumbnailImage); | |
thumbnail.getJpegOptions().setExifData(exifData); | |
} catch (Exception e) { | |
thumbnailImage.dispose(); | |
} | |
} | |
} | |
image.save(); | |
} |
Thumbnail images with other segment data cannot occupy more than 65,545 bytes because of the JPEG format specifications. In cases where large images are to be set as a thumbnail, exception may arise.
Add Thumbnail to EXIF Segment
The code snippet below demonstrate how to use the ExifData.Thumbnail property to add a thumbnail image to the EXIF segment of a loaded PSD image.
String dataDir = Utils.getDataDir(AddThumbnailToEXIFSegment.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Adjust thumbnail data. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
JpegExifData exifData = new JpegExifData(); | |
PsdImage thumbnailImage = new PsdImage(100, 100); | |
try { | |
// Fill thumbnail data. | |
int[] pixels = new int[thumbnailImage.getWidth() * thumbnailImage.getHeight()]; | |
for (int j = 0; j < pixels.length; j++) { | |
pixels[j] = j; | |
} | |
// Assign thumbnail data. | |
thumbnailImage.saveArgb32Pixels(thumbnailImage.getBounds(), pixels); | |
exifData.setThumbnail(thumbnailImage); | |
thumbnail.getJpegOptions().setExifData(exifData); | |
} catch (Exception e) { | |
thumbnailImage.dispose(); | |
} | |
} | |
} | |
image.save(); | |
} |
In this case, the Aspose.PSD API cannot estimate the thumbnail image size, but it can check the size of the entire EXIF data segment. This cannot be bigger than 65,535 bytes.
Using JpegExifData Class to Read and Modify JPEG EXIF Tags
Aspose.PSD APIs provide JpegExifData class that is exclusive to JPEG image formats to retrieve & update EXIF information. This article demonstrates the usage of JpegExifData class to achieve the same. Aspose.PSD.Exif.JpegExifData class serves as EXIF data container for JPEG images, and provide means to retrieve standard JPEG EXIF tags as demonstrated below:
String dataDir = Utils.getDataDir(AddThumbnailToEXIFSegment.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Adjust thumbnail data. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
JpegExifData exifData = new JpegExifData(); | |
PsdImage thumbnailImage = new PsdImage(100, 100); | |
try { | |
// Fill thumbnail data. | |
int[] pixels = new int[thumbnailImage.getWidth() * thumbnailImage.getHeight()]; | |
for (int j = 0; j < pixels.length; j++) { | |
pixels[j] = j; | |
} | |
// Assign thumbnail data. | |
thumbnailImage.saveArgb32Pixels(thumbnailImage.getBounds(), pixels); | |
exifData.setThumbnail(thumbnailImage); | |
thumbnail.getJpegOptions().setExifData(exifData); | |
} catch (Exception e) { | |
thumbnailImage.dispose(); | |
} | |
} | |
} | |
image.save(); | |
} |
Complete List of EXIF Tags
The above code snippet reads a few EXIF Tags using the properties offered by Aspose.PSD.Exif.JpegExifData class. Complete list of these properties is available here. Following code will read all the EXIF tags using the System.Reflection.PropertyInfo class.
String dataDir = Utils.getDataDir(ReadAllEXIFTagList.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Extract thumbnail data and store it as a separate image file. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
JpegExifData exifData = thumbnail.getJpegOptions().getExifData(); | |
if (exifData != null) { | |
for (int j = 0; j < exifData.getProperties().length; j++) { | |
System.out.println(exifData.getProperties()[j].getId() + ":" + exifData.getProperties()[j].getValue()); | |
} | |
} | |
} | |
} | |
} |
Auto Correct Orientation of JPEG Images
Photos can be shot with a camera rotated at 90°, 180°, 270°, or none (normal orientation). Most digital cameras stores the orientation information along with the image data as EXIF tags of the JPEG images. This information can be used to perform the auto rotation on the images to correct the orientation. Aspose.PSD APIs provides the AutoRotate method for JpegImage class to auto correct the orientation of JPEG images. Here is how you can use the AutoRotate method with Aspose.PSD for Java API.
String dataDir = Utils.getDataDir(AutoCorrectOrientationOfJPEGImages.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "1280px-Zebras_Serengeti.psd")) { | |
// Iterate over resources. | |
for (int i = 0; i < image.getImageResources().length; i++) { | |
// Find thumbnail resource. Typically they are in the Jpeg file format. | |
if (image.getImageResources()[i] instanceof ThumbnailResource || image.getImageResources()[i] instanceof Thumbnail4Resource) { | |
// Adjust thumbnail data. | |
ThumbnailResource thumbnail = (ThumbnailResource) image.getImageResources()[i]; | |
JpegExifData exifData = thumbnail.getJpegOptions().getExifData(); | |
if (exifData != null && exifData.getThumbnail() != null) { | |
// If there is thumbnail stored then auto-rotate it. | |
JpegImage jpegImage = (JpegImage) exifData.getThumbnail(); | |
if (jpegImage != null) { | |
jpegImage.autoRotate(); | |
} | |
} | |
} | |
} | |
// Save image. | |
image.save(); | |
} |
Support for JPEG-LS with CMYK and YCCK
Aspose.PSD for Java API provides support for CMYK and YCCK color models with JPEG-LS. The code snippet below demonstrates how to use that support for JPEG-LS.
String dataDir = Utils.getDataDir(SupportForJPEGLSWithCMYK.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "PsdImage.psd")) { | |
JpegOptions options = new JpegOptions(); | |
//Just replace one line given below in examples to use YCCK instead of CMYK | |
//options.ColorType = JpegCompressionColorMode.Cmyk; | |
options.setColorType(JpegCompressionColorMode.Cmyk); | |
options.setCompressionType(JpegCompressionMode.JpegLs); | |
// The default profiles will be used. | |
options.setRgbColorProfile(null); | |
options.setCmykColorProfile(null); | |
image.save(dataDir + "output.jpg", options); | |
} | |
try (PsdImage image1 = (PsdImage) Image.load(dataDir + "PsdImage.psd")) { | |
JpegOptions options1 = new JpegOptions(); | |
//Just replace one line given below in examples to use YCCK instead of CMYK | |
//options.ColorType = JpegCompressionColorMode.Cmyk; | |
options1.setColorType(JpegCompressionColorMode.Cmyk); | |
options1.setCompressionType(JpegCompressionMode.Lossless); | |
// The default profiles will be used. | |
options1.setRgbColorProfile(null); | |
options1.setCmykColorProfile(null); | |
image1.save(dataDir + "output2.jpg", options1); | |
} |
Support for 2-7 bits per sample in JPEG-LS images
Aspose.PSD for Java API provides support for 2-7 bits per sample JPEG-LS images. The code snippet below demonstrates how to use that support for JPEG-LS.
String dataDir = Utils.getDataDir(SupportFor2And7BitsJPEG.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "PsdImage.psd")) { | |
JpegOptions options = new JpegOptions(); | |
// Set 2 bits per sample to see the difference in size and quality | |
byte bpp = 2; | |
//Just replace one line given below in examples to use YCCK instead of CMYK | |
//options.ColorType = JpegCompressionColorMode.Cmyk; | |
options.setColorType(JpegCompressionColorMode.Cmyk); | |
options.setCompressionType(JpegCompressionMode.JpegLs); | |
options.setBitsPerChannel(bpp); | |
// The default profiles will be used. | |
options.setRgbColorProfile(null); | |
options.setCmykColorProfile(null); | |
image.save(dataDir + "2_7BitsJPEG_output.jpg", options); | |
} |
Setting ColorType and CompressionType for JPEG images
Aspose.PSD for Java API provides support for Color Type and compression Type and set them as gray scale and progressive for JPEG images. The code snippet below demonstrates how to use that support.
String dataDir = Utils.getDataDir(ColorTypeAndCompressionType.class) + "ModifyingAndConvertingImages/"; | |
try (PsdImage image = (PsdImage) Image.load(dataDir + "PsdImage.psd")) { | |
JpegOptions options = new JpegOptions(); | |
options.setColorType(JpegCompressionColorMode.Grayscale); | |
options.setCompressionType(JpegCompressionMode.Progressive); | |
image.save(dataDir + "ColorTypeAndCompressionType_output.jpg", options); | |
} |