imagemeta is a high-performance Go library for extracting EXIF and XMP metadata from images and camera RAW formats.
It is built for:
- ExifTool-aligned decoding behavior
- low-allocation parsing on hot paths
- broad support for modern and legacy camera formats
- structured MakerNote decoding (Canon, Nikon, Sony, Panasonic, Apple)
- Decode EXIF from JPEG, TIFF, CR2, CR3, DNG, NEF, ARW, RW2, HEIC/HEIF/AVIF, PNG, and CRW
- Parse XMP sidecars and embedded XMP blocks
- Read Canon/Nikon/Sony/Panasonic/Apple MakerNotes
- Extract embedded CR3 previews
- Fast image type detection
- Allocation-conscious perceptual image hashing (
imagehash)
go get github.com/evanoberholster/imagemeta- JPEG:
.jpg,.jpeg - TIFF + TIFF-based RAW:
.tif,.tiff,.cr2,.dng,.nef,.arw,.rw2 - ISO-BMFF family:
.cr3,.heic,.heif,.avif - PNG (EXIF-in-TIFF payload)
- CIFF/CRW:
.crw
package main
import (
"fmt"
"os"
"github.com/evanoberholster/imagemeta"
)
func main() {
f, err := os.Open("image.jpg")
if err != nil {
panic(err)
}
defer f.Close()
ex, err := imagemeta.Decode(f)
if err != nil {
panic(err)
}
fmt.Println("type:", ex.ImageType)
fmt.Println("make:", ex.Make)
fmt.Println("model:", ex.Model)
fmt.Println("datetime:", ex.DateTimeOriginal)
}Top-level decode entry points:
Decode(io.ReadSeeker)DecodeJPEG(io.ReadSeeker)DecodeTiff(io.ReadSeeker)DecodeCR3(io.ReadSeeker)DecodeCRW(io.ReadSeeker)DecodeHeif(io.ReadSeeker)DecodePng(io.ReadSeeker)PreviewCR3(io.ReadSeeker)
Parse a sidecar .xmp file:
x, err := xmp.ParseXmp(file)Parse embedded XMP from an image:
x, err := xmp.Parse(file)Package: github.com/evanoberholster/imagemeta/meta/xmp
Use imagetype for quick format detection before decode:
t, err := imagetype.Scan(r)Package: github.com/evanoberholster/imagemeta/imagetype
imagehash provides 64-bit and 256-bit perceptual hashing with low-allocation paths.
Package: github.com/evanoberholster/imagemeta/imagehash
- Designed for low allocation and high throughput
- Uses pooled readers and fixed-size parsing structures on hot paths
- Suitable for large-batch metadata extraction pipelines
- General benchmarks:
bench_test.go
| Benchmark | ns/op | B/op | allocs/op |
|---|---|---|---|
| Canon/CR2 | 6646 | 2440 | 20 |
| Canon/CR3 | 8015 | 3723 | 22 |
| Canon/JPG | 2058 | 176 | 9 |
| Nikon/JPG | 1747 | 104 | 7 |
| Nikon/NEF | 10306 | 736 | 19 |
| Sony/ARW | 6584 | 1512 | 12 |
Note: benchmark results vary by CPU, Go version, and sample files.
- Expand ExifTool parity coverage across MakerNote fields (Nikon, Sony, Panasonic, Apple, DJI)
- Resolve known reverse-offset parsing edge case in MakerNote decoding
- Improve malformed metadata resilience with more targeted fuzz and corpus tests
- Add more specific benchmarks to compare edge cases
- Document compatibility matrix by camera model/format
Pull requests and issue reports are welcome.
Before opening a PR, run:
go test ./...
golangci-lint runimagemeta is heavily inspired and tested on:
- Phil Harvey's ExifTool: https://exiftool.org
rwcarlsen/goexif
Additional references:
go4(BMFF/HEIF parser concepts)lclevy/canon_cr3- Lasse Heikkilä's HEIF thesis work
LLMs were used to assist in writing code, the code has been reviewed and has passed stringent checks.
MIT