Giter VIP home page Giter VIP logo

go-exif's People

Contributors

dsoprea avatar fxkr avatar ml-bnr avatar sosiska avatar wendelhime avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

go-exif's Issues

The DQT is lost on EXIF insertion

The DQT is lost.
The eog indicates it directly.
It's also visible in the trace below.

Hence, it produces a broken JPEG:
error
Some trace:

FrameCallback(w=2448, h=2048, pt=01080001=<EPixelType:Mono8>, size=5013504, buffer=0xff...)
Loaded:
 0: OFFSET=(0x00000000          0) ID=(0x000000d8) NAME=[SOI ] SIZE=(         0) SHA1=[da39a3ee5e6b4b0d3255bfef95601890afd80709]
 1: OFFSET=(0x00000002          2) ID=(0x000000db) NAME=[DQT ] SIZE=(       130) SHA1=[f516966307b0ce1fd5996ded20032c5c0219fa47]
 2: OFFSET=(0x00000088        136) ID=(0x000000c0) NAME=[SOF0] SIZE=(         9) SHA1=[08358e300dbec36b4a3e4fb01a328d83ab36f967]
 3: OFFSET=(0x00000095        149) ID=(0x000000c4) NAME=[DHT ] SIZE=(       208) SHA1=[6d90c1133ce09c6a8908b62ace6a9ffbb66e2774]
 4: OFFSET=(0x00000169        361) ID=(0x000000da) NAME=[SOS ] SIZE=(         0) SHA1=[da39a3ee5e6b4b0d3255bfef95601890afd80709]
 5: OFFSET=(0x0000016b        363) ID=(0x00000000) NAME=[    ] SIZE=(    234452) SHA1=[b55d777adce148b5d1d719a83852194ce24aeb63]
 6: OFFSET=(0x0003953f     234815) ID=(0x000000d9) NAME=[EOI ] SIZE=(         0) SHA1=[da39a3ee5e6b4b0d3255bfef95601890afd80709]
EXIF added:
 0: OFFSET=(0x00000000          0) ID=(0x000000d8) NAME=[SOI ] SIZE=(         0) SHA1=[da39a3ee5e6b4b0d3255bfef95601890afd80709]
 1: OFFSET=(0x00000000          0) ID=(0x000000e1) NAME=[APP1] SIZE=(       516) SHA1=[97714d669e289f09fe05690e93442570e39c11f4]
 2: OFFSET=(0x00000000          0) ID=(0x000000e1) NAME=[APP1] SIZE=(       516) SHA1=[97714d669e289f09fe05690e93442570e39c11f4]
 3: OFFSET=(0x00000088        136) ID=(0x000000c0) NAME=[SOF0] SIZE=(         9) SHA1=[08358e300dbec36b4a3e4fb01a328d83ab36f967]
 4: OFFSET=(0x00000095        149) ID=(0x000000c4) NAME=[DHT ] SIZE=(       208) SHA1=[6d90c1133ce09c6a8908b62ace6a9ffbb66e2774]
 5: OFFSET=(0x00000169        361) ID=(0x000000da) NAME=[SOS ] SIZE=(         0) SHA1=[da39a3ee5e6b4b0d3255bfef95601890afd80709]
 6: OFFSET=(0x0000016b        363) ID=(0x00000000) NAME=[    ] SIZE=(    234452) SHA1=[b55d777adce148b5d1d719a83852194ce24aeb63]
 7: OFFSET=(0x0003953f     234815) ID=(0x000000d9) NAME=[EOI ] SIZE=(         0) SHA1=[da39a3ee5e6b4b0d3255bfef95601890afd80709]
194 0]
Written to "/tmp/go-basler-pylon-test/595f6491a6e9c.jpg"
FrameCallback taken 83.717249ms

I've created (using Go image/jpeg) a new JPEG image (really captured from a cam).
Then I've parsed it with go-exif, added some tags, and written next to the first one.

The "original" is viewable, the "exifed" is not.

Returning errors instead of panic

I'm quite interested in making heavy use of this package because it's the only one I can find that's pure Go and seems reasonably-recently maintained.

Unfortunately, like the other exif packages I've tried, this package does error handling very non-conventionally and makes it difficult to write stable programs that rely on this package. Instead of just return err it does this odd panic-recover flow that obscures error values and in many cases, panics aren't recovered.

Stack traces are difficult to follow back because of mulltiple, nested recovered panics (this is from the readme snippet, as fixed in PR #3 - except I use SearchFileAndExtractExif instead of SearchAndExtractExif:

panic: runtime error: index out of range [recovered]
        panic: runtime error: index out of range [recovered]
        panic: runtime error: index out of range

goroutine 21 [running]:
github.com/dsoprea/go-logging.Panic(0x45a7c00, 0xc0006c8a50)
        /Users/matt/Dev/src/github.com/dsoprea/go-logging/log.go:458 +0x79
github.com/dsoprea/go-exif.SearchFileAndExtractExif.func1()
        /Users/matt/Dev/src/github.com/dsoprea/go-exif/exif.go:100 +0x88
panic(0x45a7c00, 0xc0006c8a50)
        /usr/local/go/src/runtime/panic.go:513 +0x1b9
github.com/dsoprea/go-logging.Panic(0x45a7c00, 0xc0006c8a50)
        /Users/matt/Dev/src/github.com/dsoprea/go-logging/log.go:458 +0x79
github.com/dsoprea/go-exif.SearchAndExtractExif.func1()
        /Users/matt/Dev/src/github.com/dsoprea/go-exif/exif.go:69 +0x88
panic(0x45a7c00, 0xc0006c8a50)
        /usr/local/go/src/runtime/panic.go:513 +0x1b9
github.com/dsoprea/go-logging.Panic(0x45a7c00, 0xc0006c8a50)
        /Users/matt/Dev/src/github.com/dsoprea/go-logging/log.go:458 +0x79
github.com/dsoprea/go-exif.SearchAndExtractExif(0xc0005e4000, 0x770e8, 0x7fe00, 0x0, 0x0, 0x0, 0x0, 0x0)
        /Users/matt/Dev/src/github.com/dsoprea/go-exif/exif.go:82 +0x126
github.com/dsoprea/go-exif.SearchFileAndExtractExif(0xc00052e0c0, 0x59, 0x0, 0x0, 0x0, 0x0, 0x0)
...

I'm willing to submit pull requests, but I'll have to get it working first -- I still haven't been able to get the readme example to work quite yet.

I'm very hopeful for this package though since the only other EXIF library that (mostly) meets my needs hasn't been maintained for months. And even its forks are falling out of maintenance. But it it looks like this package has good tests for reading EXIF data and supports multiple file formats which is πŸ‘

Assuming I can get this working, is this a change you'd be willing to have merged in, if I revamp the error handling?

Test results for 200.000 images

Finished implementing go-exif into my project, and tested the first files from 200.000.

Issues found:

  • GPS coordinates are shown without minutes and seconds (or decimals). I used the exif-read-tool/main.go code only, without any extra GPSInfo parsing functions.
  • Wild west problem with big base64 MakerNote value, these fields will need to be properly parsed in the future
  • Found 2 sets of ImageHeight and ImageWidth, same ids but second entries had IfdIndex: 0 set. First entries were the pixel dimensions, second entries had smaller values and itβ€˜s not obvious what they represent

Will post examples in the next days. Stay tuned...

Cannot Extract EXIF From PNG

I have tried to add EXIF information to a PNG image, but it does not seem parseable by go-exif. I'm not sure how to confirm if this is an issue with how the data is being written to the file or with how go-exif is reading from the file.

2x2.png (After)

It's parseable by some online viewers, which say they are parsing the EXIF, such as http://metapicz.com/#landing?imgsrc=https%3A%2F%2Fuser-images.githubusercontent.com%2F469790%2F59881044-770e9300-9363-11e9-9fee-2956e4be1524.png

100% CPU usage when parsing JPEG Exif

One of our users came across JPEG files that cause the Exif() function to use 100% CPU... might be stuck in an infinite loop somewhere... couldn't find out where yet... hope you can help us with this!

GitHub issue: photoprism/photoprism#1326

Files for debugging: https://github.com/photoprism/photoprism/files/6529695/Pictures.zip

Exif() function call in our code:

https://github.com/photoprism/photoprism/blob/f6c1592cb950f51eda5e1b7e497e8f745ca445cf/internal/meta/exif_parser.go#L38

slice bounds out of range [1380994898:824795]

Trying to help by providing old pics that are causing hiccups for library.

antique shop

Hope this is welcome. If not no big deal.

Other programs on linux and mac can parse the exif data.

$ git log -1
commit 8da3881 (HEAD -> master, origin/master, origin/HEAD)
Author: Dustin Oprea [email protected]
Date: Wed May 6 01:47:03 2020 -0400

Fix GPS 2.0.0.0 test image name

tag value read incomplete

I'am trying to read and modify exif data, and store it back to jpg files. And i found that after getting the ifdBuilder, even if i did not change anything, when i store it back, the tags'value has been changed. The value lost 6 byte data which is at the very beginning of the original value.
here is my codes:

package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"log"

	exif "github.com/dsoprea/go-exif"
	jpegStruct "github.com/dsoprea/go-jpeg-image-structure"
)

// CheckExifReader sets the EXIF DateTime to the given Time unless it has
// already been defined.
func CheckExifReader(filepath string) error {
	parser := jpegStruct.NewJpegMediaParser()
	sl, err := parser.ParseFile(filepath)
	if err != nil {
		return fmt.Errorf("failed to parse JPEG file: %v", err)
	}

	rootIb, err := sl.ConstructExifBuilder()
	if err != nil {
		log.Println("No EXIF; creating it from scratch")

		//TODO what is wrong with this - no EXIF data end up in the JPEG
		im := exif.NewIfdMappingWithStandard()
		ti := exif.NewTagIndex()
		if err := exif.LoadStandardTags(ti); err != nil {
			return fmt.Errorf("failed to load standard tags: %v", err)
		}

		rootIb = exif.NewIfdBuilder(im, ti, exif.IfdPathStandard,
			exif.EncodeDefaultByteOrder)
		rootIb.AddStandardWithName("ProcessingSoftware", "photos-uploader")
	}

	// Update the exif segment.
	if err := sl.SetExif(rootIb); err != nil {
		return fmt.Errorf("failed to set EXIF to jpeg: %v", err)
	}

	// Write the modified file
	b := new(bytes.Buffer)
	if err := sl.Write(b); err != nil {
		return fmt.Errorf("failed to create JPEG data: %v", err)
	}

	filepath = "../data/output_image01.jpg"

	// Save the file
	if err := ioutil.WriteFile(filepath, b.Bytes(), 0644); err != nil {
		return fmt.Errorf("failed to write JPEG file: %v", err)
	}

	fmt.Printf("Wrote %v\n", filepath)

	return nil
}

func main() {
	err := CheckExifReader("../data/image_01.jpg")
        if err != nil {
	     fmt.Printf("error: %v", err)
        }
}

For example, the original exif Model tag's value is "Redmi Note 5A", after i run the program, it become "Note 5A".
However, If change file ifd_tag_entry.go line 139
value, err = tt.ParseBytes(addressableData[ite.ValueOffset:], byteCount)
to
value, err = tt.ParseBytes(addressableData[ite.ValueOffset-6:], byteCount)
everything will be fine, except some unit test will fail. But that not the right way to do it, and i checked some codes, still did't get a clue. Any solutions?

Extracting EXIF tags without storing all bytes in memory

Thanks for creating such a great package. I created a fork of go-exif/v2 at github.com/imclaren/go-exif that extracts the EXIF tags from files without storing all of the bytes from the start of the EXIF block in memory. I left all of the existing tests in place and they all still pass.

By default the go-exif scanner searches up to 5MB into the file for the EXIF header, and then scans up to 1MB of EXIF data. These defaults can be changed when creating a new scanner. I haven't made a pull request because of the substantial changes that I needed to make to go-exif to make this work, but I can if you like.

Hopefully you find this useful or at least interesting.

Exif DateTime is not setting correctly

I am trying to update the DateTime Exif property of an image , i used the code sample what was provided by you

It seems to not update the DateTime correctly, below is the code spinet from my code base

// Parse the image.
jmp := jpeg.NewJpegMediaParser()

sl, err := jmp.ParseFile(filepath)
es.ThrowIf(err)

// Update the UserComment tag.
rootIb, err := sl.ConstructExifBuilder()
es.ThrowIf(err)
ifdPath := "IFD0"

ifdIb, err := exif.GetOrCreateIbFromRootIb(rootIb, ifdPath)
es.ThrowIf(err)

now := time.Now().UTC()
updatedTimestampPhrase := exif.ExifFullTimestampString(now)
log.Printf("time stamp %s", updatedTimestampPhrase)

err = ifdIb.SetStandardWithName("DateTime", updatedTimestampPhrase)
es.ThrowIf(err)

// Update the exif segment.

err = sl.SetExif(rootIb)
es.ThrowIf(err)

b := new(bytes.Buffer)

err = sl.Write(b)
es.ThrowIf(err)

updatedImageBytes := b.Bytes()
//updatedImageBytes = updatedImageBytes
ioutil.WriteFile(path.Join(assetsPath, "good", "updated.jpg"), updatedImageBytes, 0644)

The original image information image

The updated image with new datetime
image

If you see the properties is seems to have corrupted the exif

Do let me know what i am doing wrong

Thanks in advance
Koushik

runtime.boundsError runtime error: slice bounds out of range [:8] with capacity 4

Ran exif-read-tool on my photo corpus. There are about 100K files. Several of them from a Canon EOS 5D Mark II that were taken in 2011 cannot be processed. They show the following error.

I can provide photo but would prefer to not attach in bug as it is a picture of some one I cannot get permission from anymore. Thanks in advance

Program error.
Stack:

runtime.boundsError runtime error: slice bounds out of range [:8] with capacity 4
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/undefined/exif_9286_user_comment.go:99 (0x58e632)
/usr/local/go/src/runtime/panic.go:969 (0x431a96)
/usr/local/go/src/runtime/panic.go:106 (0x4300c3)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/undefined/exif_9286_user_comment.go:113 (0x58a841)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/undefined/accessor.go:51 (0x587bbf)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/ifd_tag_entry.go:149 (0x5e2f84)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/exif-read-tool/main.go:120 (0x5e897a)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/ifd_enumerate.go:252 (0x5dc40d)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/ifd_enumerate.go:323 (0x5dcd20)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/ifd_enumerate.go:263 (0x5dc395)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/ifd_enumerate.go:323 (0x5dcd20)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/ifd_enumerate.go:345 (0x5dce5e)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/exif.go:192 (0x5d86f5)
/home/mhr/go/src/github.com/dsoprea/go-exif/v2/exif-read-tool/main.go:151 (0x5e7e21)
/usr/local/go/src/runtime/proc.go:203 (0x434802)
/usr/local/go/src/runtime/asm_amd64.s:1373 (0x45ee41)

What is the best way to provide version as I don't see a subcommand.
I did the "go get" last night.

Read EXIF data without loading whole file?

Hi there! (Merry Christmas, by the way)

I might be missing it (I'm new to this package), but is there a way to get the EXIF data without reading the whole file into memory? (Imagine several-hundred-MB or even multi-GB video files...)

How to set GPSLatitude and GPSLongitude values

I'm trying to set the latitude and longitude (and their refs) correctly. However I'm not really sure about what kind of data I need to convert it to.

For example I use the the following code to convert lat float64 to something like 52/1 22/1 1332/100:

latitudeRef := "N"
if lat < 0 {
	latitudeRef = "S"
	lat = -lat
}
degrees := int(math.Floor(lat))
minutes := int(math.Floor(math.Mod(lat*60, 60)))
seconds := int(math.Floor(math.Mod(lat*3600, 3600)))
val := fmt.Sprintf("%d/1 %d/1 %d/100", degrees, minutes, seconds)

Then I'm trying to update the exif data with this value like so:

ifdGPSInfo, err := exif.GetOrCreateIbFromRootIb(rootIb, "IFD/GPSInfo")
if err != nil {
	return err
}

err = ifdGPSInfo.SetStandardWithName("GPSLatitudeRef", latitudeRef)
if err != nil {
	return err
}

err = ifdGPSInfo.SetStandardWithName("GPSLatitude", val)
if err != nil {
	return err
}

But I'm getting this error:

panic: tag (0x0002) value of (19) bytes not evenly divisible by type-size (8)

Which makes sense since I guess I shouldn't be using a string value. So my question is, what should I use instead?

I notices the GpsDegrees struct, which I guess has something to do with this... However I didn't find any information on how to use it.

Getting 0x01000000 value for SceneType instead of 0x00000001

I'm not sure if the library is not parsing well the value or if I need to do some additional parsing of the value. For some reason I'm getting the following value for SceneType:

IFD-PATH=[IFD/Exif] ID=(0xa301) NAME=[SceneType] COUNT=(1) TYPE=[UNDEFINED] VALUE=[0x01000000]

For the following image:

gps

I'm getting the value 1 using other tools to get Exif data. Not sure if the parsing of the value is made wrong by go-exif or if I should get only the first byte for this specific tag.

Using v3.0.0 of go-exif.

Thank you.

SetExIf example incomplete

As a follow up to #11 - it looks like the https://godoc.org/github.com/dsoprea/go-jpeg-image-structure#example-SegmentList-SetExif example is incomplete in so far as the actual metadata is not written to the image file.

I followed the code example in #11 and updated it to your v3 of exif and v2 of jpeg image structure:

import (
	exif "github.com/dsoprea/go-exif/v3"
	exifcommon "github.com/dsoprea/go-exif/v3/common"
	jpeg "github.com/dsoprea/go-jpeg-image-structure/v2"
)

The following code will write the tag data to the image file:

func setExifTag(rootIB *exif.IfdBuilder, ifdPath, tagName, tagValue string) error {
	fmt.Printf("setTag(): ifdPath: %v, tagName: %v, tagValue: %v",
		ifdPath, tagName, tagValue)

	ifdIb, err := exif.GetOrCreateIbFromRootIb(rootIB, ifdPath)
	if err != nil {
		return fmt.Errorf("Failed to get or create IB: %v", err)
	}

	if err := ifdIb.SetStandardWithName(tagName, tagValue); err != nil {
		return fmt.Errorf("failed to set DateTime tag: %v", err)
	}

	return nil
}

func setDateIfNone(filepath string, t time.Time) error {
	parser := jpeg.NewJpegMediaParser()
	intfc, err := parser.ParseFile(filepath)
	if err != nil {
		return fmt.Errorf("Failed to parse JPEG file: %v", err)
	}

	sl := intfc.(*jpeg.SegmentList)

	rootIb, err := sl.ConstructExifBuilder()
	if err != nil {
		fmt.Println("No EXIF; creating it from scratch")

		im, err := exifcommon.NewIfdMappingWithStandard()
		if err != nil {
			return fmt.Errorf("Failed to create new IFD mapping with standard tags: %v", err)
		}
		ti := exif.NewTagIndex()
		if err := exif.LoadStandardTags(ti); err != nil {
			return fmt.Errorf("Failed to load standard tags: %v", err)
		}

		rootIb = exif.NewIfdBuilder(im, ti, exifcommon.IfdStandardIfdIdentity,
			exifcommon.EncodeDefaultByteOrder)
		rootIb.AddStandardWithName("ProcessingSoftware", "photos-uploader")
	}

	//TODO check if DateTime is already set.

	// Form our timestamp string
	ts := exifcommon.ExifFullTimestampString(t)

	// Set DateTime
	ifdPath := "IFD0"
	if err := setExifTag(rootIb, ifdPath, "DateTime", ts); err != nil {
		return fmt.Errorf("Failed to set tag %v: %v", "DateTime", err)
	}

	// Set DateTimeOriginal
	ifdPath = "IFD/Exif"
	if err := setExifTag(rootIb, ifdPath, "DateTimeOriginal", ts); err != nil {
		return fmt.Errorf("Failed to set tag %v: %v", "DateTimeOriginal", err)
	}

	// Update the exif segment.
	if err := sl.SetExif(rootIb); err != nil {
		return fmt.Errorf("Failed to set EXIF to jpeg: %v", err)
	}

	// Write the modified file
	b := new(bytes.Buffer)
	if err := sl.Write(b); err != nil {
		return fmt.Errorf("Failed to create JPEG data: %v", err)
	}

	fmt.Printf("Number of image bytes: %v\n", len(b.Bytes()))

	// Save the file
	if err := ioutil.WriteFile(filepath, b.Bytes(), 0644); err != nil {
		return fmt.Errorf("Failed to write JPEG file: %v", err)
	}

	fmt.Printf("Wrote %v\n", filepath)

	return nil
}

Canon EOS R5 missing metadata

Hi, I'm trying to use our tool to parse exif of CR3 photos and it seems to be stopped in the middle. Just a few fields is detected while we have more fields in that photo. Are CR3 photos not fully supported ? Many thanks.

PS: It's quite big and cannot attach here, you can find it in https://www.imaging-resource.com/PRODS/canon-eos-r5/canon-eos-r5A7.HTM

$ exif-read-tool -f EOSR5hSLI000050NR0.cr3 -v
2021/01/19 10:07:23 exif.exif: [DEBUG]  Found EXIF blob (320) bytes from initial position.
2021/01/19 10:07:23 main.main: [DEBUG]  EXIF blob is (60696338) bytes.
2021/01/19 10:07:23 exif.ifd_enumerate: [DEBUG]  Parsing IFD [IFD] at offset (0x0008) (scan).
2021/01/19 10:07:23 exif.ifd_enumerate: [DEBUG]  IFD [IFD] tag-count: (13)
2021/01/19 10:07:23 exif.tags: [DEBUG]  (305) tags loaded.
2021/01/19 10:07:23 exif.ifd_enumerate: [DEBUG]  Next IFD at offset: (00000000)
2021/01/19 10:07:23 exif.ifd_enumerate: [DEBUG]  Scan: It looks like the furthest offset that contained EXIF data in the EXIF blob was (250) (Scan).
IFD-PATH=[IFD] ID=(0x0100) NAME=[ImageWidth] COUNT=(1) TYPE=[SHORT] VALUE=[[8192]]
IFD-PATH=[IFD] ID=(0x0101) NAME=[ImageLength] COUNT=(1) TYPE=[SHORT] VALUE=[[5464]]
IFD-PATH=[IFD] ID=(0x0102) NAME=[BitsPerSample] COUNT=(3) TYPE=[SHORT] VALUE=[[8 8 8]]
IFD-PATH=[IFD] ID=(0x0103) NAME=[Compression] COUNT=(1) TYPE=[SHORT] VALUE=[[6]]
IFD-PATH=[IFD] ID=(0x010f) NAME=[Make] COUNT=(6) TYPE=[ASCII] VALUE=[Canon]
IFD-PATH=[IFD] ID=(0x0110) NAME=[Model] COUNT=(13) TYPE=[ASCII] VALUE=[Canon EOS R5]
IFD-PATH=[IFD] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[[1]]
IFD-PATH=[IFD] ID=(0x011a) NAME=[XResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[[72/1]]
IFD-PATH=[IFD] ID=(0x011b) NAME=[YResolution] COUNT=(1) TYPE=[RATIONAL] VALUE=[[72/1]]
IFD-PATH=[IFD] ID=(0x0128) NAME=[ResolutionUnit] COUNT=(1) TYPE=[SHORT] VALUE=[[2]]
IFD-PATH=[IFD] ID=(0x0132) NAME=[DateTime] COUNT=(20) TYPE=[ASCII] VALUE=[2020:07:22 17:52:08]
IFD-PATH=[IFD] ID=(0x013b) NAME=[Artist] COUNT=(1) TYPE=[ASCII] VALUE=[]
IFD-PATH=[IFD] ID=(0x8298) NAME=[Copyright] COUNT=(1) TYPE=[ASCII] VALUE=[]

image

Finish implementing decoding for remaining undefined-type tags

See bottom of tags_unknown.go:

go-exif/tags_unknown.go

Lines 387 to 392 in 9aa2497

// TODO(dustin): !! Still need to do:
//
// complex: 0xa302, 0xa20c, 0x8828
// long: 0xa301, 0xa300
//
// 0xa40b is device-specific and unhandled.

Tags with undefined types are size-unknown without additional handling, which means they are skipped when we construct a updated EXIF block. So, at the current moment, these tags will be dropped when an EXIF block is updated.

Where does the "IsExif" method come from?

In that example snippet, there's this

for i := 0; i < len(data); i++ {
    if e.IsExif(data[i:i + 6]) == true {
        foundAt = i
        break
    }
}

But what is e?

I've searched the source for IsExif and I can't find that function anywhere

Writer sample codes

Can you please provide one sample code for writer? I'm trying to use your tool to put EXIF into thumbnail pictures.

Thanks

panic: no exif data [recovered]

image

This image is generated with the go code, so there is no extra information?Is that possible just like a Hash-Map, and then we can just do like "get" , "put" and "delete"?

Collect goes into an infinite loop for certain exif data

I reported this earlier in #56 but the chosen solution was not accepted.

I run a service that extracts certain exif data from user supplied image files. The problem is that for some images this service goes into an infinite loop while extracting and I have traced the source of the problem to this library.

An example image file is this one (zipped to preserve exif data): broken_exif.zip.

When I run the following test code:

file, err := os.Open("broken_exif.jpg")
if err != nil {
	panic(err)
}
rawExif, err := SearchAndExtractExifWithReader(file)
if err != nil {
	panic(err)
}
ifd, err := exifcommon.NewIfdMappingWithStandard()
if err != nil {
	panic(err)
}
tag := NewTagIndex()
_, _, err = Collect(ifd, tag, rawExif)
if err != nil {
	panic(err)
}

The code never returns and remains looping, repeating the same byte offsets over and over again.

Add "tag guessing" functionality

Add a function to search for a tag-ID across our tag-index for the first match agnostic to the type of IFD it could be found in. Allow for an optional parameter that takes an ordered slice of IFD paths to search. If not provided, use a reasonable default (e.g. "EXIF IFD", "IFD0", "GPS").

Occasionally someone shows up with a picture having one or more tags in a different IFD than what is prescribed by the standard. This won't be default functionality, but at least we can provide the option. We might even suggest using it if we find some unknown tags and a preliminary check shows that they might be valid tags if found in another IFD.

One of the reasons that we don't want to make this default behavior is that if the tag-ID matches but the type and the data are not consistent, we might end-up trying to display non-printable binary.

photoprism/photoprism#304

isPrintableText() seems to return false if string contains \n or \r

This effectively converts a proper description like "Hello World\n\nWhere are you?" to "string with binary data (%d bytes)" which then shows up as image description throughout the application:

if isPrintableText(t) == false {
	phrase = fmt.Sprintf("string with binary data (%d bytes)", len(t))
	return phrase, nil
}

It seems better to allow \n, \r, and \t or return an empty string so that the value returned by Exiftool can be used as fallback. Same for Emojis if they don't qualify as printable text.

go-exif Fails Go Data Race Detector

It seems like go-logging is susceptible to race conditions which cause my tests to fail when using the Go Data Race Detector. It looks like a single logging instance is being used at a global level, and then modified with each log?

==================
WARNING: DATA RACE
Read at 0x00c0000c2230 by goroutine 21:
  github.com/dsoprea/go-logging.(*Logger).doConfigure()
      /go/pkg/mod/github.com/dsoprea/[email protected]/log.go:183 +0x55
  github.com/dsoprea/go-logging.(*Logger).Warningf()
      /go/pkg/mod/github.com/dsoprea/[email protected]/log.go:339 +0x55
  github.com/dsoprea/go-exif.ParseExifHeader()
      /go/pkg/mod/github.com/dsoprea/[email protected]/exif.go:146 +0x501
  github.com/dsoprea/go-exif.SearchAndExtractExif()
      /go/pkg/mod/github.com/dsoprea/[email protected]/exif.go:78 +0xc0
  gitlab.com/keithwoelke/parking-police/pkg/photo.parseIFDs()
      /mnt/pkg/photo/metadata.go:44 +0xa0
  gitlab.com/keithwoelke/parking-police/pkg/photo.ImageMetadata()
      /mnt/pkg/photo/metadata.go:21 +0x85
  gitlab.com/keithwoelke/parking-police/pkg/photo_test.TestGetMetadataWhenNoExifMetadata.func1()
      /mnt/pkg/photo/metadata_test.go:77 +0xe1
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:865 +0x163

Previous write at 0x00c0000c2230 by goroutine 19:
  github.com/dsoprea/go-logging.(*Logger).doConfigure()
      /go/pkg/mod/github.com/dsoprea/[email protected]/log.go:231 +0x38b
  github.com/dsoprea/go-logging.(*Logger).Warningf()
      /go/pkg/mod/github.com/dsoprea/[email protected]/log.go:339 +0x55
  github.com/dsoprea/go-exif.ParseExifHeader()
      /go/pkg/mod/github.com/dsoprea/[email protected]/exif.go:146 +0x501
  github.com/dsoprea/go-exif.SearchAndExtractExif()
      /go/pkg/mod/github.com/dsoprea/[email protected]/exif.go:78 +0xc0
  gitlab.com/keithwoelke/parking-police/pkg/photo.parseIFDs()
      /mnt/pkg/photo/metadata.go:44 +0xa0
  gitlab.com/keithwoelke/parking-police/pkg/photo.ImageMetadata()
      /mnt/pkg/photo/metadata.go:21 +0x85
  gitlab.com/keithwoelke/parking-police/pkg/photo_test.TestGetMetadataWhenNoExifMetadata.func1()
      /mnt/pkg/photo/metadata_test.go:77 +0xe1
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:865 +0x163

Goroutine 21 (running) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:916 +0x65a
  gitlab.com/keithwoelke/parking-police/pkg/photo_test.TestGetMetadataWhenNoExifMetadata()
      /mnt/pkg/photo/metadata_test.go:71 +0x156
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:865 +0x163

Goroutine 19 (finished) created at:
  testing.(*T).Run()
      /usr/local/go/src/testing/testing.go:916 +0x65a
  gitlab.com/keithwoelke/parking-police/pkg/photo_test.TestGetMetadataWhenNoExifMetadata()
      /mnt/pkg/photo/metadata_test.go:71 +0x156
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:865 +0x163
==================

Performance of Parse Exif Header

Hi Dustin, I am really enjoying your library! I can see that you have dedicated much effort to details. For a project that I am working on I am using parts of your library and did some benchmarking. .

Benchmarks are the difference between moving the whole file into a []byte (ParseExif2Header) vs using a reader (ParseExifHeader):

// JPG file with ExifData
BenchmarkParseExifHeader200-8   	  408568	      2915 ns/op	    1088 B/op	       3 allocs/op
BenchmarkParseExif2Header200-8   	    1047	    956161 ns/op	 8386637 B/op	      15 allocs/op

// JPG file with ExifData #2
BenchmarkParseExifHeader200-8   	  412591	      2883 ns/op	    1088 B/op	       3 allocs/op
BenchmarkParseExif2Header200-8   	     996	   1051139 ns/op	 8386637 B/op	      15 allocs/op

// CR2 file
BenchmarkParseExifHeader200-8   	  409156	      2654 ns/op	    1088 B/op	       3 allocs/op
BenchmarkParseExif2Header200-8   	      68	  16370650 ns/op	67106902 B/op	      18 allocs/op

// JPG file with No ExifData
BenchmarkParseExifHeader200-8   	     261	   4603787 ns/op	    1056 B/op	       2 allocs/op
BenchmarkParseExif2Header200-8   	     254	   4749712 ns/op	 1046565 B/op	      11 allocs/op

Changes are io.Reader instead of []byte, bigEndianHeaderBytes, littleEndianHeaderBytes and ExifHeader has a foundAt field for compactness.

// ParseExifHeader parses the bytes at the very top of the header.
// Benchmarked
func parseExifHeader(r io.Reader) (*ExifHeader, error) {
	bufSize := 1024 // 1kB Buffer
	data := make([]byte, bufSize)
	var err error
	var a, j int
	for {
		copy(data[:8], data[len(data)-8:])
		a, err = r.Read(data[8:])
		if err == io.EOF {
			return &ExifHeader{}, ErrNoExif
		} else if err != nil {
			panic(err)
		}
		j += a
		for i := 0; i < len(data)-8; i++ {
			if bytes.Equal([]byte{data[i], data[i+1], data[i+2], data[i+3]}, bigEndianHeaderBytes[:]) {
				return &ExifHeader{
					ByteOrder:      binary.BigEndian,
					FirstIfdOffset: binary.BigEndian.Uint32(data[i+4 : i+8]),
					foundAt:        (j + i) - bufSize,
				}, nil
			}
			if bytes.Equal([]byte{data[i], data[i+1], data[i+2], data[i+3]}, littleEndianHeaderBytes[:]) {
				return &ExifHeader{
					ByteOrder:      binary.LittleEndian,
					FirstIfdOffset: binary.LittleEndian.Uint32(data[i+4 : i+8]),
					foundAt:        (j + i) - bufSize,
				}, nil
			}
		}
	}
}

// Search for the beginning of the EXIF information. The EXIF is near the
// beginning of our/most JPEGs, so this has a very low cost.

// ParseExifHeader parses the bytes at the very top of the header.
func parseExifHeader2(r io.Reader) (*ExifHeader, error) {
	eh := &ExifHeader{
		foundAt: -1,
	}

	data, err := ioutil.ReadAll(r)
	if err != nil {
		return eh, err
	}
	for i := 0; i < len(data)-8; i++ {
		if bytes.Equal([]byte{data[i], data[i+1], data[i+2], data[i+3]}, bigEndianHeaderBytes[:]) {
			firstIfdOffset := binary.BigEndian.Uint32(data[i+4 : i+8])
			return &ExifHeader{
				ByteOrder:      binary.BigEndian,
				FirstIfdOffset: firstIfdOffset,
				foundAt:        i,
			}, nil
		}
		if bytes.Equal([]byte{data[i], data[i+1], data[i+2], data[i+3]}, littleEndianHeaderBytes[:]) {
			firstIfdOffset := binary.LittleEndian.Uint32(data[i+4 : i+8])
			return &ExifHeader{
				ByteOrder:      binary.LittleEndian,
				FirstIfdOffset: firstIfdOffset,
				foundAt:        i,
			}, nil
		}
	}

	return eh, ErrNoExif
}

var (
	bigEndianHeaderBytes    = [4]byte{'M', 'M', 0x00, 0x2a}
	littleEndianHeaderBytes = [4]byte{'I', 'I', 0x2a, 0x00}
)

// ExifHeader - The Exif header
type ExifHeader struct {
	ByteOrder      binary.ByteOrder
	FirstIfdOffset uint32
	foundAt        int
}

Bechmark Code:

var path = "../../test/img/1.jpg"

func BenchmarkParseExifHeader200(b *testing.B) {
	var err error

	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}

	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f.Seek(0, 0)
		_, _ = parseExifHeader(f)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkParseExif2Header200(b *testing.B) {
	var err error

	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}

	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f.Seek(0, 0)
		_, _ = parseExifHeader2(f)
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkCurrent200(b *testing.B) {
	var err error

	f, err := os.Open(path)
	if err != nil {
		panic(err)
	}

	b.ReportAllocs()
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		f.Seek(0, 0)
		data, _ := ioutil.ReadAll(f)
		_, _ = exif.SearchAndExtractExif(data)
		if err != nil {
			b.Fatal(err)
		}
	}
}

throw panic "no such file or directory" even if file exist

Hey, thanks for the cool project!

I have a test.gif file in the current directory. Running the following command, throws panic error:

$ exif-read-tool -filepath test.gif
*fs.PathError open ilepath: no such file or directory
/Users/furkan.turkal/go/pkg/mod/github.com/dsoprea/go-exif/[email protected]/command/exif-read-tool/main.go:92 (0x11cb177)
	main: log.PanicIf(err)
/usr/local/go/src/runtime/proc.go:225 (0x1037516)
	main: fn()
/usr/local/go/src/runtime/asm_amd64.s:1371 (0x1067c21)
	goexit: BYTE	$0x90	// NOP
$ open test.gif  # this OK

Any ideas? πŸ€”

GPSVersionID 2.0.0.0

Any reason GPSVersionID 2.0.0.0 is not supported, only 2.2.0.0 and 2.3.0.0?

Didn't find any useful information on how version 2.0 is different from 2.2 or 2.3, coordinates look the same.

See photoprism/photoprism#295

Runtime: out of memory in Collect

Something is happening with the memory in Collect. It might be a memory leak that I couldn't figure out my self.

Here is the error I'm getting with stacktrace:

fatal error: runtime: out of memory

runtime stack:
runtime.throw(0xe421fd, 0x16)
        /usr/local/go/src/runtime/panic.go:1116 +0x72
runtime.sysMap(0xc134000000, 0x4000000, 0x1649638)
        /usr/local/go/src/runtime/mem_bsd.go:73 +0xc5
runtime.(*mheap).sysAlloc(0x1634dc0, 0x400000, 0x7501a9040, 0x7501aa470)
        /usr/local/go/src/runtime/malloc.go:715 +0x1cd
runtime.(*mheap).grow(0x1634dc0, 0x1, 0x0)
        /usr/local/go/src/runtime/mheap.go:1286 +0x11c
runtime.(*mheap).allocSpan(0x1634dc0, 0x1, 0x74fd52000, 0x1649648, 0x7501aa360)
        /usr/local/go/src/runtime/mheap.go:1124 +0x65d
runtime.(*mheap).alloc.func1()
        /usr/local/go/src/runtime/mheap.go:871 +0x64
runtime.systemstack(0x7fffdd9eaf80)
        /usr/local/go/src/runtime/asm_amd64.s:370 +0x66
runtime.mstart()
        /usr/local/go/src/runtime/proc.go:1041

goroutine 315006 [running]:
runtime.systemstack_switch()
        /usr/local/go/src/runtime/asm_amd64.s:330 fp=0xc0001f2030 sp=0xc0001f2028 pc=0x4699c0
runtime.(*mheap).alloc(0x1634dc0, 0x1, 0x410120, 0x7501a6140)
        /usr/local/go/src/runtime/mheap.go:865 +0x81 fp=0xc0001f2080 sp=0xc0001f2030 pc=0x42b091
runtime.(*mcentral).grow(0x1645a18, 0x0)
        /usr/local/go/src/runtime/mcentral.go:255 +0x79 fp=0xc0001f20c0 sp=0xc0001f2080 pc=0x41d819
runtime.(*mcentral).cacheSpan(0x1645a18, 0x60)
        /usr/local/go/src/runtime/mcentral.go:106 +0x2bc fp=0xc0001f2108 sp=0xc0001f20c0 pc=0x41d34c
runtime.(*mcache).refill(0x701659560, 0x20)
        /usr/local/go/src/runtime/mcache.go:138 +0x85 fp=0xc0001f2128 sp=0xc0001f2108 pc=0x41ce35
runtime.(*mcache).nextFree(0x701659560, 0x20, 0x30, 0x30, 0xc133ffcc30)
        /usr/local/go/src/runtime/malloc.go:868 +0x87 fp=0xc0001f2160 sp=0xc0001f2128 pc=0x411a67
runtime.mallocgc(0xf0, 0xe140c0, 0xdf1c01, 0x16477e0)
        /usr/local/go/src/runtime/malloc.go:1036 +0x793 fp=0xc0001f2200 sp=0xc0001f2160 pc=0x4123a3
runtime.newobject(0xe140c0, 0x0)
        /usr/local/go/src/runtime/malloc.go:1165 +0x38 fp=0xc0001f2230 sp=0xc0001f2200 pc=0x412798
github.com/dsoprea/go-exif/v2.(*IfdEnumerate).Collect(0xc0001f2780, 0xc000000008, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /go/pkg/mod/github.com/dsoprea/go-exif/[email protected]/ifd_enumerate.go:958 +0x3ef fp=0xc0001f2718 sp=0xc0001f2230 pc=0xc4a6cf
github.com/dsoprea/go-exif/v2.Collect(0xc00064c030, 0xc0006f61a0, 0xc00053403c, 0x1ffc4, 0x3fdc4, 0x1016da0, 0x16477e0, 0x8, 0x0, 0x0, ...)
        /go/pkg/mod/github.com/dsoprea/go-exif/[email protected]/exif.go:211 +0x232 fp=0xc0001f27e8 sp=0xc0001f2718 pc=0xc41da2

Would be great if someone can have look and find out what is going on here.

Alternatives to "no exif data"

Hi @dsoprea ,

I'm having some troubles when trying to add some metadata to cropped images, there's a functionality on go-jpeg-image-structure or go-exif to deal with this before it raises the error no exif data?

I'm sending the cropped image as example (you can use any image from image.SubImage or crop an image with gimp without saving EXIF structure or any other metadata) and here's a piece of code (basically the example provided on jpegstructure)

    jmp := jpg.NewJpegMediaParser()

    intfc, err := jmp.ParseBytes(b.Bytes())
    if err != nil {
        return err
    }

    sl := intfc.(*jpg.SegmentList)
    // Throws: no exif data
    rootIb, err := sl.ConstructExifBuilder()
    if err != nil {
        return err
    }

    ifdPath := "IFD0"

    ifdIb, err := exif.GetOrCreateIbFromRootIb(rootIb, ifdPath)
    if err != nil {
        return err
    }

    err = ifdIb.SetStandardWithName("ImageDescription", description)
    if err != nil {
        return err
    }

    // Update the exif segment.
    err = sl.SetExif(rootIb)
    if err != nil {
        return err
    }

image

Failed parsing GPS coordinates

There are two example images in our test suite that still cause trouble when parsing GPS coordinates:

1. huawei-gps-error.jpg

runtime error: index out of range [1] with length 1

This seems to affect a lot of images, also some older ones taken with a Canon EOS 6D. This example was taken with a Huawei P30 phone, so probably not a vendor specific issue.

2. panorama360.jpg

runtime error: integer divide by zero

This file is a resized version (using ImageMagick) of a large panorama image attached to this feature request: photoprism/photoprism#352

Otherwise very happy with your libraries. Keep up the great work! Also added the new heic parser and noticed that an old issue with parsing width and height was fixed.

Documentation needed: writing EXIF tags

Hi, as the library is not all straightforward, (nor do I understand EXIF that well), I'd love to see some documentation / examples on how to write tag values -- more specifically, in my case, how to write the "Date and Time" tag of an photo. My photos might lack the whole EXIF data entirely as they are somewhat old.

Kudos for making this library. Was surprised to find so little in the way of EXIF in the Golang library-verse.

Failing image

Attempting to parse the exif data from the attached image causes a panic. Running the same image through exiftool does seem to work. I spent a small amount of time trying to figure this out but I didn't get too far.

exif

FQ-IFD-PATH=[IFD] ID=(0x0100) NAME=[ImageWidth] COUNT=(1) TYPE=[LONG] VALUE=[5312]
FQ-IFD-PATH=[IFD] ID=(0x0101) NAME=[ImageLength] COUNT=(1) TYPE=[LONG] VALUE=[2988]
FQ-IFD-PATH=[IFD] ID=(0x010f) NAME=[Make] COUNT=(8) TYPE=[ASCII] VALUE=[samsung]
FQ-IFD-PATH=[IFD] ID=(0x0110) NAME=[Model] COUNT=(10) TYPE=[ASCII] VALUE=[SM-G920W8]
FQ-IFD-PATH=[IFD] ID=(0x0112) NAME=[Orientation] COUNT=(1) TYPE=[SHORT] VALUE=[6]
FQ-IFD-PATH=[IFD] ID=(0x0131) NAME=[Software] COUNT=(15) TYPE=[ASCII] VALUE=[G920W8VLU6DRF1]
FQ-IFD-PATH=[IFD] ID=(0x0132) NAME=[DateTime] COUNT=(20) TYPE=[ASCII] VALUE=[2018:08:15 15:38:33]
FQ-IFD-PATH=[IFD] ID=(0x0213) NAME=[YCbCrPositioning] COUNT=(1) TYPE=[SHORT] VALUE=[1]
FQ-IFD-PATH=[IFD] ID=(0x8769) NAME=[ExifTag] COUNT=(1) TYPE=[LONG] VALUE=[175]
FQ-IFD-PATH=[IFD/Exif] ID=(0x829a) NAME=[ExposureTime] COUNT=(1) TYPE=[RATIONAL] VALUE=[1/50]
FQ-IFD-PATH=[IFD/Exif] ID=(0x829d) NAME=[FNumber] COUNT=(1) TYPE=[RATIONAL] VALUE=[19/10]
FQ-IFD-PATH=[IFD/Exif] ID=(0x8822) NAME=[ExposureProgram] COUNT=(1) TYPE=[SHORT] VALUE=[2]
FQ-IFD-PATH=[IFD/Exif] ID=(0x8827) NAME=[ISOSpeedRatings] COUNT=(1) TYPE=[SHORT] VALUE=[100]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9000) NAME=[ExifVersion] COUNT=(4) TYPE=[UNDEFINED] VALUE=[0220]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9003) NAME=[DateTimeOriginal] COUNT=(20) TYPE=[ASCII] VALUE=[2018:08:15 15:38:33]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9004) NAME=[DateTimeDigitized] COUNT=(20) TYPE=[ASCII] VALUE=[2018:08:15 15:38:33]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9201) NAME=[ShutterSpeedValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[564/100]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9202) NAME=[ApertureValue] COUNT=(1) TYPE=[RATIONAL] VALUE=[185/100]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9203) NAME=[BrightnessValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[261/100]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9204) NAME=[ExposureBiasValue] COUNT=(1) TYPE=[SRATIONAL] VALUE=[0/10]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9205) NAME=[MaxApertureValue] COUNT=(1) TYPE=[RATIONAL] VALUE=[185/100]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9207) NAME=[MeteringMode] COUNT=(1) TYPE=[SHORT] VALUE=[2]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9209) NAME=[Flash] COUNT=(1) TYPE=[SHORT] VALUE=[0]
FQ-IFD-PATH=[IFD/Exif] ID=(0x920a) NAME=[FocalLength] COUNT=(1) TYPE=[RATIONAL] VALUE=[430/100]
FQ-IFD-PATH=[IFD/Exif] ID=(0x927c) NAME=[MakerNote] COUNT=(98) TYPE=[UNDEFINED] VALUE=[MakerNote<TYPE-ID=[07 00 01 00 07 00 04 00 00 00 30 31 30 30 02 00 04 00 01 00]>]
FQ-IFD-PATH=[IFD/Exif] ID=(0x9286) NAME=[UserComment] COUNT=(21) TYPE=[UNDEFINED] VALUE=[UserComment<SIZE=(0) ENCODING=[UNDEFINED] V=[] LEN=(0)>]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa001) NAME=[ColorSpace] COUNT=(1) TYPE=[SHORT] VALUE=[1]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa002) NAME=[PixelXDimension] COUNT=(1) TYPE=[LONG] VALUE=[5312]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa003) NAME=[PixelYDimension] COUNT=(1) TYPE=[LONG] VALUE=[2988]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa402) NAME=[ExposureMode] COUNT=(1) TYPE=[LONG] VALUE=[0]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa403) NAME=[WhiteBalance] COUNT=(1) TYPE=[LONG] VALUE=[0]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa405) NAME=[FocalLengthIn35mmFilm] COUNT=(1) TYPE=[LONG] VALUE=[28]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa406) NAME=[SceneCaptureType] COUNT=(1) TYPE=[LONG] VALUE=[0]
FQ-IFD-PATH=[IFD/Exif] ID=(0xa420) NAME=[ImageUniqueID] COUNT=(30) TYPE=[ASCII] VALUE=[A16LSIA00VM A16LSJL02SM
]
panic: tag-type not valid: 0x0000

DateTimeOriginal can not be parsed: ascii not terminated with nul as expected

@linosgian originally reported this issue: photoprism/photoprism#219

Indexing did not get exif's "Date/Time Original" or "Create Date" right for some of my photos. exiftool seems to be able to parse everything correctly on all photos. As a result, the photo appears to be taken in 2020, as (I guess) indexing falls back to the file's mtime

After checking import logs, I learned many of my photos are also affected. So it must be something with how fields are parsed. Error originates from here:

log.Panicf("ascii not terminated with nul as expected")

Maybe a condition that's not clearly defined in the Exif standard? Would using ParseAsciiNoNul() instead help?

Support for IPTC and XMP metadata planned?

Is it planned to add support for IPTC and XMP metadata to the library (despite its name)? Asking because these three usually have to be dealt with in conjunction, are similar in structure and intention (at least from the end user's point of view), and tools such as exiftool support them.

GPS Coordinates Are Calculated Incorrectly

Hello! In the process of trying out this library (thank you, by the way!), I discovered the GPS coordinates are calculated slightly incorrectly.

In ifd_enumerate.go, the values are calculated by dividing the Rationals as expected. However, for some reason, they are truncated instead of stored as float64 types. This causes the value to be off slightly when calculating the final GPS coordinates in degrees.

For example, a Longitude of 77.0365 is returned as [{Numerator:77 Denominator:1} {Numerator:2 Denominator:1} {Numerator:57 Denominator:5}] which calculates as expected by hand, but after the truncating, Decimal() returns it as 77.0363888888889 instead.

missing geotiff tags

Hi, I was trying to add some geotiff tags on a image but tags like ModelPixelScaleTag, ModelTiepointTag, ModelTransformationTag aren't available (no tag found).

Looking at the lib in versions v2 and v3, there's tags_data.go with a variable tagsYaml with copy pasted value from assets. Can I add those tags to tags asset or there's another approach?

unparseable undefined tag

I'm trying to write metadata on this image (I hope github don't make changes on metadata, if it does, I'll provide another link):

IMG_20201102_110021

That's the original picture that I take from Ada with my phone. I'm using this code based on example

	jmp := jpg.NewJpegMediaParser()

	intfc, err := jmp.ParseBytes(b.Bytes())
	if err != nil {
		return err
	}

	sl := intfc.(*jpg.SegmentList)
	// Update the UserComment tag.
	rootIb, err := sl.ConstructExifBuilder()
	if err != nil {
		return err
	}

But when trying to call exif.ConstructExifBuilder, the function returns the error: unparseable undefined tag.

Somebody help me!

Here's some metadata using exiftool with the command:

exiftool -a -u -g1 IMG_20201102_110021.jpg

Output:

---- ExifTool ----
ExifTool Version Number         : 12.09
Warning                         : [minor] Unrecognized MakerNotes
Warning                         : [minor] Unrecognized MakerNotes
---- System ----
File Name                       : IMG_20201102_110021.jpg
Directory                       : ..
File Size                       : 3.1 MB
File Modification Date/Time     : 2020:11:04 11:11:19-03:00
File Access Date/Time           : 2020:11:04 11:11:31-03:00
File Inode Change Date/Time     : 2020:11:04 11:11:19-03:00
File Permissions                : rw-rw-r--
---- File ----
File Type                       : JPEG
File Type Extension             : jpg
MIME Type                       : image/jpeg
Exif Byte Order                 : Big-endian (Motorola, MM)
Image Width                     : 3456
Image Height                    : 4608
Encoding Process                : Baseline DCT, Huffman coding
Bits Per Sample                 : 8
Color Components                : 3
Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
---- IFD0 ----
Image Width                     : 3456
Image Height                    : 4608
Bits Per Sample                 : 8 8 8
Image Description               : sdr
Make                            : HUAWEI
Camera Model Name               : BND-L34
Orientation                     : Unknown (0)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Software                        : BND-L34 8.0.0.362(C567)
Modify Date                     : 2020:11:02 11:00:23
Y Cb Cr Positioning             : Centered
Device Setting Description      : (Binary data 4 bytes, use -b option to extract)
---- ExifIFD ----
Document Name                   : 
Exposure Time                   : 1/256
F Number                        : 2.2
Exposure Program                : Program AE
ISO                             : 50
Exif Version                    : 0210
Date/Time Original              : 2020:11:02 11:00:23
Create Date                     : 2020:11:02 11:00:23
Components Configuration        : Y, Cb, Cr, -
Shutter Speed Value             : 1/999963365
Aperture Value                  : 2.2
Brightness Value                : 0
Exposure Compensation           : 0
Metering Mode                   : Multi-segment
Light Source                    : Daylight
Flash                           : No Flash
Focal Length                    : 3.8 mm
Maker Note Unknown Text         : Auto
Sub Sec Time                    : 543266
Sub Sec Time Original           : 543266
Sub Sec Time Digitized          : 543266
Flashpix Version                : 0100
Color Space                     : sRGB
Exif Image Width                : 3456
Exif Image Height               : 4608
Sensing Method                  : One-chip color area
File Source                     : Digital Camera
Scene Type                      : Directly photographed
Custom Rendered                 : Custom
Exposure Mode                   : Auto
White Balance                   : Auto
Digital Zoom Ratio              : 1
Focal Length In 35mm Format     : 26 mm
Scene Capture Type              : Standard
Gain Control                    : None
Contrast                        : Normal
Saturation                      : Normal
Sharpness                       : Normal
Subject Distance Range          : Unknown
---- InteropIFD ----
Interoperability Index          : R98 - DCF basic file (sRGB)
Interoperability Version        : 0100
---- IFD1 ----
Image Width                     : 384
Image Height                    : 512
Compression                     : JPEG (old-style)
Orientation                     : Unknown (0)
X Resolution                    : 72
Y Resolution                    : 72
Resolution Unit                 : inches
Thumbnail Offset                : 8592
Thumbnail Length                : 19480
Thumbnail Image                 : (Binary data 19480 bytes, use -b option to extract)
---- JFIF ----
JFIF Version                    : 1.01
Resolution Unit                 : inches
X Resolution                    : 96
Y Resolution                    : 96
---- Composite ----
Aperture                        : 2.2
Image Size                      : 3456x4608
Megapixels                      : 15.9
Scale Factor To 35 mm Equivalent: 6.8
Shutter Speed                   : 1/256
Create Date                     : 2020:11:02 11:00:23.543266
Date/Time Original              : 2020:11:02 11:00:23.543266
Modify Date                     : 2020:11:02 11:00:23.543266
Circle Of Confusion             : 0.004 mm
Field Of View                   : 69.4 deg
Focal Length                    : 3.8 mm (35 mm equivalent: 26.0 mm)
Hyperfocal Distance             : 1.51 m
Light Value                     : 11.3

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.