Skip to content
/ exif Public

Dead simple exchangeable image file format tools for Go optimized for large image files using lazy loading.

License

Notifications You must be signed in to change notification settings

soypat/exif

Repository files navigation

exif

Exchangeable image file format tools for Go.

This library is at least 200 times faster for extracting EXIF data from a small image when compared to go-exif and can be up to thousands of times faster for images in the size of megabytes. See benchmarks below.

  • The root directory contains common EXIF functions and data types.
  • The tiff directory contains a TIFF image parser that uses lazy loading.
    • TODO: Implement the image.Image interface using a cache for low memory requirement lazy loading
  • The rational directory contains signed and unsigned 64bit rational number types

Benchmarks- comparisons with the popular go-exif library.

  • Small image: 15kB TIFF
  • Large image: 19.6MB TIFF
goos: linux
goarch: amd64
pkg: github.com/soypat/exif
cpu: 12th Gen Intel(R) Core(TM) i5-12400F
BenchmarkThisPackage_SmallImage-12    	  136064	      8411 ns/op	    2457 B/op	      57 allocs/op
BenchmarkDsoprea_SmallImage-12        	     798	   1650455 ns/op	  659437 B/op	   11252 allocs/op
BenchmarkThisPackage_LargeImage-12    	  127800	      8593 ns/op	    1977 B/op	      50 allocs/op
BenchmarkDsoprea_LargeImage-12        	      52	  23897506 ns/op	123760580 B/op	   11588 allocs/op
PASS
coverage: 41.6% of statements
Benchmark code

I have removed the following benchmark from this package since dsoprea's Go package has two high severity security issues and github's dependabot was bothering me some. You are free to run it on your computer and compare with the benchmarks under benchmarks_test.go

package exif_test

import (
	"os"
	"testing"

	dsoprea "github.com/dsoprea/go-exif/v3"
	exifcommon "github.com/dsoprea/go-exif/v3/common"
)

const (
	smallImageName = "testdata/sample1.tiff"
)

func BenchmarkDsoprea_SmallImage(b *testing.B) {
	for i := 0; i < b.N; i++ {
		rawExif, err := dsoprea.SearchFileAndExtractExif(smallImageName)
		if err != nil {
			b.Fatal(err)
		}
		mapping, _ := exifcommon.NewIfdMappingWithStandard()
		ti := dsoprea.NewTagIndex()
		_, index, err := dsoprea.Collect(mapping, ti, rawExif)
		if err != nil {
			b.Fatal(err)
		}
		err = index.RootIfd.EnumerateTagsRecursively(func(i *dsoprea.Ifd, ite *dsoprea.IfdTagEntry) error {
			return nil
		})
		if err != nil {
			b.Fatal(err)
		}
	}
}

Example

Example of usage of this library. We read a single tag value for the XResolution tag and then print out all the Exif tags in the file under the Image File Directories. In this case we only have IFD0.

	fp, err := os.Open("testdata/sample1.tiff")
	if err != nil {
		panic(err)
	}
	defer fp.Close()
	var decoder exif.LazyDecoder
	err = decoder.Decode(fp)
	if err != nil {
		panic(err)
	}

	// Read a single tag from the decoded tags.
	xResTag, err := decoder.GetTag(fp, 0, exifid.XResolution)
	if err != nil {
		panic("compression tag not found")
	}
	// One can also use the Rational, Float and Int methods to obtain 
	// statically typed tag values. The Value method returns interface{} type.
	fmt.Printf("the x resolution is %v", xResTag.Value())

	// Generate all tags of a certain constrained size using a callback.
	ifds, err := decoder.MakeIFDs(fp, func(ifd, size int, id exif.ID) bool {
		return size < 1024 // Tags less than a kilobyte in size.
	})
	if err != nil {
		panic(err)
	}
	for _, ifd := range ifds {
		fmt.Printf("%s:\n", ifd.Group.String())
		for _, tag := range ifd.Tags {
			fmt.Println("\t" + strings.Trim(tag.String(), "\x00"))
		}
	}

Outputs:

the x resolution is 2000000/10000
IFD0:
	ImageWidth (uint32): 1728
	ImageHeight (uint32): 2376
	BitsPerSample (uint16): 1
	Compression (uint16): 4
	PhotometricInterpretation (uint16): 0
	FillOrder (uint16): 2
	DocumentName (string): Standard Input
	ImageDescription (string): converted PBM file
	StripOffsets (unknown): 8
	Orientation (uint16): 1
	SamplesPerPixel (uint16): 1
	RowsPerStrip (uint32): 2376
	StripByteCounts (unknown): 18112
	XResolution (rational): 2000000/10000
	YResolution (rational): 2000000/10000
	PlanarConfiguration (uint16): 1
	ResolutionUnit (uint16): 2

About

Dead simple exchangeable image file format tools for Go optimized for large image files using lazy loading.

Topics

Resources

License

Stars

Watchers

Forks

Languages