Julia Community 🟣

Cover image for Release of ExifViewer.jl for Image Metadata : GSOC'22 Work Product
Ashwani Rathee
Ashwani Rathee

Posted on

Release of ExifViewer.jl for Image Metadata : GSOC'22 Work Product

In this post, we discuss Exif metadata and the launch of ExifViewer.jl to work with Exif metadata in image files in Julia.

Introduction

EXIF is short for Exchangeable Image File, a format that is a standard for storing interchange information in digital photography image files using JPEG compression. ExifViewer.jl provides support for encoding and decoding metadata from image files like jpeg, png, tiff. Let's take the example of the below shown image as our image with metadata:

Image description

Let's first try to load using Images.jl

julia> using Images, FileIO
julia> filepath = "exifviewer-banner.jpeg"
julia> img = load(filepath)
960×2360 Array{RGB{N0f8},2} with eltype RGB{N0f8}:
 RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)    RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)
 RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)     RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)
                                                                          
 RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)     RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)
 RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)     RGB{N0f8}(1.0,1.0,1.0)  RGB{N0f8}(1.0,1.0,1.0)
Enter fullscreen mode Exit fullscreen mode

FileIO successfully loads the image and returns an AbstractArray of size 960×2360 but doesn't return associated metadata that gives information about the artist, camera settings, or image's description.

We lose out on a lot of important details about the image. ExifViewer.jl solves this problem by reading the exif-metadata from the image, which we hope in the future will be integrated with the load function to return both image array and its associated metadata.

Let's read image's metadata with ExifViewer.jl:

julia> using ExifViewer
julia> filepath = "exifviewer-banner.jpeg"
julia> tags = read_tags(filepath)
Dict{String, Any} with 15 entries:
  "EXIF_TAG_YCBCR_POSITIONING"          => "Centered"
  "EXIF_TAG_FLASH_PIX_VERSION"          => "FlashPix Version 1.0"
  "EXIF_TAG_EXIF_VERSION"               => "Exif Version 2.1"
  "EXIF_TAG_RESOLUTION_UNIT"            => "Inch"
  "EXIF_TAG_COLOR_SPACE"                => "sRGB"
  "EXIF_TAG_COMPONENTS_CONFIGURATION"   => "Y Cb Cr -"
  "EXIF_TAG_PHOTOMETRIC_INTERPRETATION" => "YCbCr"
  "EXIF_TAG_Y_RESOLUTION"               => "960"
  "EXIF_TAG_ARTIST"                     => "Ash :)"
  "EXIF_TAG_MAKE"                       => "Ash :)"
  "EXIF_TAG_X_RESOLUTION"               => "2360"
                                      => 
Enter fullscreen mode Exit fullscreen mode

We can also update using write_tags, let's update EXIF_TAG_MAKE to Julia as it will be all done in Julia this time.

julia> using Images, FileIO, ExifViewer
julia> filepath = "exifviewer-banner.jpeg"
julia> img = load(filepath)
julia> tags = read_tags(filepath);
julia> tags["EXIF_TAG_MAKE"] = "Julia"
julia> write_tags(filepath; img=img, tags=tags)
julia> tags = read_tags(filepath)
Dict{String, Any} with 15 entries:
  "EXIF_TAG_YCBCR_POSITIONING"          => "Centered"
  "EXIF_TAG_FLASH_PIX_VERSION"          => "FlashPix Version 1.0"
  "EXIF_TAG_EXIF_VERSION"               => "Exif Version 2.1"
  "EXIF_TAG_RESOLUTION_UNIT"            => "Inch"
  "EXIF_TAG_COLOR_SPACE"                => "sRGB"
  "EXIF_TAG_COMPONENTS_CONFIGURATION"   => "Y Cb Cr -"
  "EXIF_TAG_PHOTOMETRIC_INTERPRETATION" => "YCbCr"
  "EXIF_TAG_Y_RESOLUTION"               => "960"
  "EXIF_TAG_ARTIST"                     => "Ash :)"
  "EXIF_TAG_MAKE"                       => "Julia"
  "EXIF_TAG_X_RESOLUTION"               => "2360"
                                      => 
Enter fullscreen mode Exit fullscreen mode

Hurray!!!!!! We have updated the metadata of the image file.

There is a very interesting question: where is this data exactly stored in a file?

Let's take an example of a .jpeg file, let's generate a very simple jpeg file to understand its structure.

julia> using Images, FileIO, ExifViewer
julia> filepath = "sample.jpeg"
julia> img = Gray{N0f8}.(rand(1,1))
julia> tags = Dict{String,Any}("EXIF_TAG_ARTIST"=>"Ash")
julia> write_tags(filepath; img=img, tags=tags)

Enter fullscreen mode Exit fullscreen mode

Below image shows the general structure of the jpeg file and EXIF data is supported in Application Marker 1:

JPEG File Structure

Exif data will be stored in APP1 whose start is marked by 0xFFE1 as we will see soon.
and some details about JPEG file structure, which tell where the image starts and ends, what markers mark the start of APP1 data, and where the compressed data is etc:

Various markers in JPEG file

Now, let's see our image in its hexadecimal form:

Sample.jpeg

Our EXIF data is stored in APP1 which we try to decode and encode using EXIFViewer.jl.

Note!!!!

ExifViewer.jl is now available for the community's use in Julia's General Registry and can be simply installed using:

# Enter ']' from the REPL to enter Pkg mode.
pkg> add ExifViewer
Enter fullscreen mode Exit fullscreen mode

Future Work

Currently a limited number of tags(around 40) are supported for writing to file that need to be extended and there are some known issues related to encoding. Also, encoding is limited to JPEG and JPG files right now which needs to be eventually extended to all available formats that can use exif specification.

Acknowledgments

Thank you @johnnychen94 for being constant support as a mentor and a friend over the years!!

Top comments (2)

Collapse
 
fortunewalla profile image
Fortune Walla

Not sure this can be applicable to your package or not but there is a software called IrfanView and it detects whether the filename extension matches the EXIF name or not.

Example would be an image that has webp tag internally but .png in the filename.

Here IrfanView offers to change the filename extension from .png to .webp

Nice effort! All the best!

Collapse
 
fabiorsodre profile image
Fábio Sodré

wonderful! 😍👽👍🏼