Exif Utility Library Guide

Exif (Exchangeable image file format) is a standard specifying rules for storing metadata in digital image files, mainly those using JPEG compression.

Purpose

Exif enhances the JPEG and TIFF specifications with the addition of metadata tags to hold information such as the camera settings used to take the picture.

Exif Overview

The Exif standard defines a wide range of metadata tags, some of which are listed below:

  • Date and time information of the image captured.

  • Camera settings like camera model and make, and information that varies with each image such as orientation, aperture, shutter speed, focal length, metering mode, and film speed information.

  • A thumbnail for previewing the picture on camera LCD.

  • Copyright information.

Accessing Exif Metadata

Access to Exif metadata is provided through an abstract interface available in all the main ICL APIs. This interface can be implemented and managed through an ICL plugin, exhibiting the functionality to the client through the plugin extension mechanism.

The MExifMetaData class is a generic interface for accessing the Exif metadata. To access a specific item of a metadata, both tag ID and the primary Image File Directory [IFD] number must be provided in the MExifMetaData access method, as some tags may appear in more than one IFD. The primary IFD number may either be 0 or 1. The main image tags are grouped together under IFD 0 and the tags for the thumbnail images are grouped under IFD 1 in the Exif metadata. The tags under these IFD can be further classified into sub IFD:

For example : Tags related to the GPS can be sorted to form a group.

The MExifMetaData is compounded by two classes as mentioned below:

  • MExifMetadataReader : This provides more generic functionalitites to read the tags from the Exif Metadata. The user is expected to provide the tag id and the primary IFD for the tags that needs to be read.

  • MExifMetadataWriter : This is a generic way to write or set the tags of the Exif metadata. Here also the user is expected to render the key inputs like tag id and IFD for the data that needs to be written or modified.

The access provided by MExifMetaData is quite low level. The users are expected to know the tag IDs defined in the Exif specification in order to use it. Hence more user friendly classes as listed below are made available to access these tags.

Examples

This section contains code snippets showing how Exif metadata is accessed in several situations.

JPEG Exif Plugin

  • TExifReaderUtility : This class provides a user friendly interface for reading the metadata in Exif encoded image files. The following example code illustrates its use:

             
              
             
             void CIclExample::AccessToEXIFMetadataL(const TDesC& aFileName)
        {
        HBufC8* buffer8Bit=NULL;
        
        // If the image is not recognised or valid then the call will leave with an error
        CJPEGExifDecoder* exifdecoder =  static_cast<CJPEGExifDecoder*>(CJPEGExifDecoder::FileNewL(iFs, aFileName/*,CImageDecoder::EOptionNone, KNullUid, KNullUid, KUidICLJpegEXIFInterface*/));
           CleanupStack::PushL(exifdecoder);
        
        // Create a MExifMetadata object and Initializes to metadata associated with the CJPEGExifDecoder    
        MExifMetadata* metaData=exifdecoder->ExifMetadata();
    
        // Create a TExifReaderUtility object and pass the metadata to read it
        if(metaData != NULL)
            {
            TExifReaderUtility reader(metaData);    
            User::LeaveIfError(reader.GetImageDescription(buffer8Bit));
            }
    
        delete buffer8Bit;
        CleanupStack::PopAndDestroy(exifdecoder);
        }
            
  • TExifWriterUtility : This class provides a user friendly interface for writing the metadata in Exif encoded image files. The following example code illustrates its use:

             
              
             
             void CIclExample::AddEXIFDataToJPEGFileL()
    {
        TInt err = KErrNone;
          
        HBufC8 *encodedImageDescriptor = NULL;
       
        // Create the encoder, passing a buffer to store the encoded image
        CJPEGExifEncoder* exifencoder = static_cast<CJPEGExifEncoder*>(CImageEncoder::DataNewL(encodedImageDescriptor,CImageEncoder::EOptionNone,KImageTypeJPGUid));
        
        // Create a MExifMetadata object and initializes to metadata associated with CJPEGExifEncoder  
        MExifMetadataWriter* metaData=exifencoder->ExifMetadata(); 
        CleanupStack::PushL(exifencoder);
        
        // Create a TExifWriterUtility object to write the metadata in to the image
          TExifWriterUtility exifWriteUtility(metaData);
    
        HBufC8*    buf8ParamWriteVersion=KBuf8ParamWriteVersion().Alloc();
    
        User::LeaveIfError(exifWriteUtility.SetImageDescription(buf8ParamWriteVersion));
       
           delete encodedImageDescriptor;
          encodedImageDescriptor = NULL;
          
          delete buf8ParamWriteVersion;
          buf8ParamWriteVersion = NULL;
           
        CleanupStack::PopAndDestroy(exifencoder);
    
        }
            

The access to the Exif metadata in image decoder/encoder will be provided through the MExifMetadata interface. The extended object CJPEGExifDecoder of the public class CImageDecoder provides the mechanism to access the custom Exif interface, by allowing the client to request a reference to the MExifMetadata interface. Once the source image has been set, and the client has acquired a reference to MExifMetadata , the data associated with the Exif tags can be accessed at any time before or after a decoding process has taken place.

The client can request the main image or the thumbnail image as the source for the decode operation using the function SetImageTypeL() . These are the only arguments SetImageTypeL() will accept. Any other argument provided will cause the function to leave with error KErrArgument. The plugin assumes the main image to be the source unless otherwise specified. The plugin will ignore the function call if the image requested is unavailable.

       
        
       
       imageDecoder->SetImageTypeL(CImageDecoder::EImageTypeThumbnail);
      

Unlike decoding, encoding is a one step procedure that requires all the Exif data to be set before the event is initiated. Any unspecified parameters must be populated by the default values. The call to the SetImageTypeL() determines whether an embedded image will be included in the encoded image or not. The client must furnish an optional parameter EImageTypeThumbnail along with the EImageTypeMain included in the bitfield to denote the inclusion of a thumbnail in the encoded image. Any other parameter specified aberrant from this specification will result in the function leaving with KErrArgument error. The thumbnail generated will conform to the DCF standards.

Exif support in CImageDisplay

In this scenario, the ExifMetadata() returns a pointer to the object that implements the MExifMetadata interface. The Exif metadata can be extracted, once the source image has been set. This provides a read only access to the metadata. It is illustrated as follows:

       
        
       
       void CIclExample::AccessToEXIFMetadataL(const TDesC& aFileName)
    {
    HBufC8* buffer8Bit=NULL;
    
    // Create the decoder, passing in the image memory. The image is recognised by the 
    // Image Conversion Library, an appropriate codec plugin loaded and the image 
    // headers parsed.
    // If the image is not recognised or valid then the call will leave with an error
    CJPEGExifDecoder* exifdecoder =  static_cast<CJPEGExifDecoder*>(CJPEGExifDecoder::FileNewL(iFs, aFileName/*,CImageDecoder::EOptionNone, KNullUid, KNullUid, KUidICLJpegEXIFInterface*/));
       CleanupStack::PushL(exifdecoder);
    
    // Create a MExifMetadata object and Initializes to metadata associated with the CJPEGExifDecoder    
    MExifMetadata* metaData=exifdecoder->ExifMetadata();

    // Create a TExifReaderUtility object and pass the metadata to read it
    if(metaData != NULL)
        {
        TExifReaderUtility reader(metaData);    
        User::LeaveIfError(reader.GetImageDescription(buffer8Bit));
        }

    delete buffer8Bit;
    CleanupStack::PopAndDestroy(exifdecoder);
    }
      

Exif support in CimageTransform

In this scenario, the ExifMetadata() returns a pointer to the object that implements the MExifMetadata interface. It is illustrated as follows:

       
        
       
       void CIclExample::AddEXIFDataToJPEGFileL()
{
    TInt err = KErrNone;
      
    HBufC8 *encodedImageDescriptor = NULL;
   
    // Create the encoder, passing a buffer to store the encoded image
    CJPEGExifEncoder* exifencoder = static_cast<CJPEGExifEncoder*>(CImageEncoder::DataNewL(encodedImageDescriptor,CImageEncoder::EOptionNone,KImageTypeJPGUid));
    
    // Create a MExifMetadata object and initializes to metadata associated with CJPEGExifEncoder  
    MExifMetadataWriter* metaData=exifencoder->ExifMetadata(); 
    CleanupStack::PushL(exifencoder);
    
    // Create a TExifWriterUtility object to write the metadata in to the image
      TExifWriterUtility exifWriteUtility(metaData);

    HBufC8*    buf8ParamWriteVersion=KBuf8ParamWriteVersion().Alloc();

    User::LeaveIfError(exifWriteUtility.SetImageDescription(buf8ParamWriteVersion));
   
       delete encodedImageDescriptor;
      encodedImageDescriptor = NULL;
      
      delete buf8ParamWriteVersion;
      buf8ParamWriteVersion = NULL;
       
    CleanupStack::PopAndDestroy(exifencoder);

    }
      

The ExifMetadata() provides functions to scale still images. Additionally, it has the ability to scale only a subset of the original image by setting a clipping rectangle. This acts as if the source image outside the region does not exist causing the clipped image to be resized to the size specified.

Furthermore, a thumbnail can be added to the JPEG file as shown in the code below:

       
        
       
       void CIclExample::AddThumbnailToJPEGFileL(const TDesC& aFileName)
    {
        TInt err = KErrNone;
                 TInt soiOffset=0;
                 TInt thumbnailLength = 0;
     TInt size = 0;
     Const TInt KConstStartOffset = 12;
           HBufC8 *desData= NULL;
  
     TPtr8 imageFromFilePtr = LoadImageIntoMemoryL(aFileName);

     // Create a CImageTransform object and push it on to the cleanup stack
     CImageTransform* imageTransform=CImageTransform::NewL(iFs);
     CleanupStack::PushL(imageTransform);

     // Call SetSourceFilenameL() function of CImageTransform to set the source to file
     imageTransform->SetSourceFilenameL(aFileName);
     
     // Call SetDestDataL() function of CImageTransform to set the destination size in pixels
     imageTransform->SetDestDataL(desData);
     
     // Call SetDestSizeInPixelsL() function of CImageTransform to set the destination size in pixels
     imageTransform->SetDestSizeInPixelsL(TSize(160, 120), ETrue);
     
     // Call SetOptionsL() to add the thumbnail
     imageTransform->SetOptionsL(CImageTransform::EThumbnail);
     
     //Call SetupL() to setup the ImageTransform
     imageTransform->SetupL();
    
   // encode the image
     CActiveListener* activeListener = CreateAndInitializeActiveListenerLC();
     imageTransform->Transform(activeListener->iStatus);
     CActiveScheduler::Start();
     User::LeaveIfError(activeListener -> iStatus.Int());    
     
     // Create a CJPEGExifDecoder
  CJPEGExifDecoder* exifdecoder = static_cast<CJPEGExifDecoder*>(CJPEGExifDecoder::DataNewL(iFs, imageFromFilePtr));
  CleanupStack::PushL(exifdecoder);
     
     // Get the thumbnail from the image
     MExifMetadata* metaData=exifdecoder->ExifMetadata();        

     // Get the offset to the thumbnail image (i.e. the value of tag 0x0201)
  metaData->GetIntegerParam(0x0201, 1, soiOffset);
        
     // Get the length of the thumbnail image    (i.e. the value of tag 0x0202)
     metaData->GetIntegerParam(0x0202, 1, thumbnailLength);
     
     // Add thumbnail to image    
     RFile file;
     file.Open(iFs,KbitmapFile,EFileShareReadersOnly|EFileStream|EFileRead);
     CleanupClosePushL(file);
     file.Size(size);

     HBufC8* buffer = HBufC8::NewMaxLC(size);
     TPtr8 bufferDes(buffer->Des());
     file.Read(bufferDes);
 
  TPtrC8 thumbnailData = bufferDes.Mid(KConstStartOffset + soiOffset,thumbnailLength);
     
  // Write thumbnail data to image
     User::LeaveIfError(file.Write(thumbnailData));

     file.Close();
  delete desData;
  
     CleanupStack::PopAndDestroy(6);// buffer,exifdecoder,activeListener,imageTransform,desData,iImageInMemory
    }
      

Exif extension to support YUV image format

An extension to Exif is provided in the ICL to support encoding and decoding uncompressed YUV data images to and from the JPEG data format. The advantage of YUV format over RGB is that it reduces the amount of information required to reproduce a satisfactory video image. This is achieved by some of the components in YUV corresponding to more than one pixel.

For example, the pixel does not necessarily have its own set of Y,U,V components, but may share the U and V components with other pixels as shown below:

  • YUV 4:4:4 - Each pixel has a Y, U, and V component.

  • YUV 4:2:2 - Each horizontal pair of pixels has a Y component per pixel but only one U and V component per pair.

  • YUV 4:2:0 - Each 2*2 block of pixels has a Y component per pixel but only one U and one V component per block.

Note: The YUV bitmap layout may differ from the RGB bitmap layout in the sense that the RGB pixel components are rendered in the same buffer as opposed to YUV bitmaps which may have different layouts.

The conversion between RGB and 4:4:4 YUV is pretty straight forward. The CFbsBitmaps are capable of encapsulating YUV data presented in its basic 4:4:4 YUV interleaved format. However, the existing CFbsBitmap does not support all possible YUV bitmap formats and layouts. This limitation is overcome by the CImageFrame class which allows the image data to be stored in the memory in any format or layout as long as it is uniquely identified. This is achieved by using format code UID's.

       
        
       
       CImageFrame* imageFrame= CImageFrame::NewL(&chunk, imageSizeInBytes, KRChunkSize);