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:
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)
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"
⋮ => ⋮
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"
⋮ => ⋮
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)
Below image shows the general structure of the jpeg file and EXIF data is supported in Application Marker 1:
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:
Now, let's see our image in its hexadecimal form:
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
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)
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!
wonderful! 😍👽👍🏼