Getting image exif data

Extracting and Parsing image EXIF data using Go

This article is part of a series looking at developing an image indexing and tagging solution. This sample Go code looks at how to extract and parse EXIF metadata from JPEG photographs. It will be used in the initial indexer which catalogues all the images as they are moved to an archive disk.

To make life easier for ourselves we’re going to utilise two Go packages to help with the parsing of the EXIF data. Start by installing these two dependencies.

    
go get github.com/rwcarlsen/goexif/exif
go get github.com/tidwall/gjson
    

Let’s start by defining the variables we’ll need. The err variable will hold the value of any errors that our code generates. We’ll need a pointer to a file struct to hold the image we are currently processing. We’ll also need a pointer to an Exif struct which will hold the raw metadata. The jsonByte variable will hold the byte array of the JSON representation of the EXIF metadata. We’ll also have a jsonString representation of the JSON structure to make it easier to parse the data.

    
func main() {
    var err        error
    var imgFile    *os.File
    var metaData   *exif.Exif
    var jsonByte   []byte
    var jsonString string
}
    

We start by opening the JPEG image we’re interested in checking if any errors we’re encountered. If an error was encountered then we’ll log the error and exit the program. Using Fatal from the log package is the equivalent to Print() followed by a call to os.Exit(1)

    
imgFile, err = os.Open("sample.jpg")
if err != nil {
    log.Fatal(err.Error())
}
    

If the image opened fine, then we next try to decode the EXIF metadata using the exif.Decode() function. Again checking for any errors. Once we have the metaData object we can use the MarshalJSON() function to extract the metadata as a JSON object.

    
metaData, err = exif.Decode(imgFile)
if err != nil {
    log.Fatal(err.Error())
}

jsonByte, err = metaData.MarshalJSON()
if err != nil {
    log.Fatal(err.Error())
}

jsonString = string(jsonByte)
fmt.Println(jsonString)
    

We can then convert the JSON object into a string and save that within our database linked to the image. If we print out the string version you’ll notice that all the metadata items are stored as key-value pairs.

    
{"ColorSpace":[1],"ComponentsConfiguration":"","
CompressedBitsPerPixel":["2/1"],"Contrast":[0],
"CustomRendered":[0],"DateTime":"2006:02:19 13:23:51",
"DateTimeDigitized":"2006:02:19 13:23:51",
"DateTimeOriginal":"2006:02:19 13:23:51",
"DigitalZoomRatio":["0/100"],"ExifIFDPointer":[284],
"ExifVersion":"0220","ExposureBiasValue":["0/10"],
"ExposureMode":[0],"ExposureProgram":[2],
"ExposureTime":["10/2352"],"FNumber":["56/10"],
"FileSource":"","Flash":[16],"FlashpixVersion":"0100",
"FocalLength":["58/10"],"FocalLengthIn35mmFilm":[35],
"GainControl":[0],"ISOSpeedRatings":[50],"ImageDescription":"","InteroperabilityIFDPointer":[1026],"InteroperabilityIndex":"R98",
"LightSource":[0],"Make":"NIKON","MakerNote":"",
"MaxApertureValue":["30/10"],"MeteringMode":[5],
"Model":"E4100","Orientation":[1],"PixelXDimension":[2288],
"PixelYDimension":[1712],"ResolutionUnit":[2],"Saturation":[0],
"SceneCaptureType":[1],"SceneType":"","Sharpness":[0],
"Software":"E4100v1.0","SubjectDistanceRange":[0],
"ThumbJPEGInterchangeFormat":[4596],
"ThumbJPEGInterchangeFormatLength":[5309],"UserComment":"",
"WhiteBalance":[0],"XResolution":["300/1"],"YCbCrPositioning":[2],
"YResolution":["300/1"]}
    

Now that we have the JSON object represented as a string we can use the JSON parsing package that we installed earlier. Using it’s gjson.Get() function we can pass in the JSON string and the key for the data item we’re interested in retrieving. So for example, if we wanted to list out the Camera's Make, Model and Software version we’d use the following code.

    
fmt.Println("Make: " + gjson.Get(jsonString, "Make").String())
fmt.Println("Model: " + gjson.Get(jsonString, "Model").String())
fmt.Println("Software: " + gjson.Get(jsonString, "Software").String())
    

Which would produce the following output:

    
Make: NIKON
Model: E4100
Software: E4100v1.0
    

In the next article of the series we’ll look at how to create custom thumbnails from images and then start building the indexer.


I hope you found this article useful, you can discuss it on Twitter
I create educational content for developers, hoping to inspire and teach with a deep understanding.

Paul Bradley