Exiv2 / exiv2

Image metadata library and tools
http://www.exiv2.org/
Other
921 stars 278 forks source link

Canon AFInfo Rotation #1543

Open derselbst opened 3 years ago

derselbst commented 3 years ago

Hello,

I have noticed that Canon stores additional rotation information in the AFInfo chunk. Neither Exiv2 nor ExifTool currently seem to handle this information. I would like to share and document that information so we can add support for this.

Canon's AFInfo chunk contains a list of AF focus points that were used, selected, and in-focus when the image was taken. Depending on the camera in use, this may look like so:

image

Canon's software DPP supports rotating images by an arbitrary amount of degrees. [This is not be confused by the simple +- 90 degree rotation that you know from every other image editor!] If you rotate the image by that amount, it influences the AF Point mask. Here you see a picture of the same image rotated by 7.25 degrees in a mathematically negative (i.e. clockwise) manner:

image

I'm attaching a ZIP file that contains some example easter egg images. The applied (clockwise) rotation in degrees can be derived from the file name of the image. rotation.zip

When we search for the difference in those images, we will find that the very last two bytes of the AFInfo chunk differ between all the images.

> exiv2 -ph 1.JPG
0x0026 Canon        AFInfo                      Short     184 368
  0000  70 01 02 00 29 00 29 00 00 04 ab 02 00 04 ab 02  p...).).........
  0010  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0020  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0030  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0040  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0050  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0060  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0070  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0080  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  0090  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  00a0  2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00 2b 00  +.+.+.+.+.+.+.+.
  00b0  2b 00 2b 00 f8 ff 64 ff 23 ff 7b 00 3a 00 f9 ff  +.+...d.#.{.:...
  00c0  22 ff 4f 01 0e 01 7a 00 39 00 4e 01 0d 01 79 00  ".O...z.9.N...y.
  00d0  38 00 f7 ff 63 ff 78 00 37 00 f6 ff 62 ff 21 ff  8...c.x.7...b.!.
  00e0  61 ff 20 ff 4d 01 0c 01 4c 01 0b 01 77 00 36 00  a. .M...L...w.6.
  00f0  f5 ff 0a 01 76 00 35 00 f4 ff 60 ff 1f ff 75 00  ....v.5...`..u.
  0100  34 00 f3 ff 4b 01 8e 00 a0 00 a1 00 c1 00 c2 00  4...K...........
  0110  c3 00 63 00 97 00 98 00 8c 00 8d 00 5a 00 5b 00  ..c.........Z.[.
  0120  57 00 58 00 59 00 62 00 22 00 23 00 24 00 26 00  W.X.Y.b.".#.$.&.
  0130  28 00 eb ff ec ff 1e 00 1f 00 e2 ff e3 ff ed ff  (..............
  0140  ee ff ef ff a6 ff b8 ff b9 ff ba ff ad ff af ff  ................
  0150  83 ff 84 ff 85 ff a5 ff 00 00 04 00 00 00 00 00  ................
  0160  00 00 00 00 00 00 00 00 00 00 ff ff 00 00 3c 8c  ..............<.

Exiv2 already does a good job in parsing the AFInfo chunk and will tell you what's inside. However, the last 6 bytes are missing. While I still have no clue what the sequence ff ff 00 00 means, I can tell for sure that in this example the byte sequence 3c 8c defines the rotation. From my experiments with DPP4 I can tell, that this should be interpreted as little endian unsigned short. This yields a value of 0x8C3C = 35900. Since we are dealing with image 1.JPG (an image, that was rotated 1 deg clockwise), we receive a rotation of 35900 / 100 = 359 degrees. This is the mathematically positive (i.e. anti-clockwise) rotation that should be applied to each and every individual AF rectangle, whose locations are defined by their coordinates reported by AFXPositions and AFYPositions.

If that rotation is not applied, the AF Mask might look very strange like:

Screenshot_20210410_190816

So, it would be really great if Exiv2 could be expanded to report the correct rotation in degrees. I assume you guys are quite busy with other topics. If you let me know on with branch this should be implemented (I assume 0.27-maintenance?), I will try to get it working myself. (This may take some time though.)

alexvanderberkel commented 3 years ago

Hi Tom,

thanks for reporting the issue. We have just released RC2 of v0.27.4 and are heading towards GM this or early next month. The latest branch to work with is the maintanence branch as you assumed correctly. There has been some work on new tags from Canon cameras within this release. The file which you need to look at is canonmn_int.cpp. That is where the Canon tags are being handled.

Cheers, Alex

hassec commented 3 years ago

Hi @derselbst, I thought I could help and quickly provide a fix since I did similar work recently for the nikon maker note....

However, for some reason the CanonAFInfo is being decoded differently from what I expected from my current exiv2 knowledge. There is a special TiffDecoder implemented in tiffvisitor_int.cpp just for this tag.

My current guess is that it maybe has something to do with the dynamic size of the AFInfo tag?
It's easy to quickly add a few lines to tiffvisitor_int.cpp that will lead to exiv2 printing the info, but I'm a little hesitant to just add code to a construct that I've not yet completely understood.

Hoping @clanmills knows more and can enlighten me :blush:

derselbst commented 3 years ago

@hassec Don't worry. I already had a look into the code and found it quite easy to do. Here's my branch. I'll need a few more days to test it. Not sure why it's handled differently. Probably due to it's dynamic size.

clanmills commented 3 years ago

Welcome back, Tom @derselbst I though we worked on something like this a couple of years ago. It's tag 0x0026. My brain is a little flattened from the effort to ship v0.27.4 RC2 yesterday.

Can you submit a PR to the 'main' branch which I will set up on Sunday morning. 'main' will be the development branch for v1.00 which is scheduled for 2021-12-15.

hassec commented 3 years ago

@hassec Don't worry. I already had a look into the code and found it quite easy to do. Here's my branch. I'll need a few more days to test it. Not sure why it's handled differently. Probably due to it's dynamic size.

Ok glad to hear that you are on it! That's exactly the change I did as well. But I'm hesitant to continue to just add to the canon MakersNote construct without fully figuring out what's going on. Because, I think we should include a bit of cleanup as well, since I think the tagListAf.. functions in canonmn_int.cpp are unused.

In any case, @clanmills suggestion is probably the best, and then we can continue development once there is a PR ;)

derselbst commented 3 years ago

Can you submit a PR to the 'main' branch which I will set up on Sunday morning.

Ok, I'll do. No hurry :)

I think we should include a bit of cleanup as well, since I think the tagListAf.. functions in canonmn_int.cpp are unused.

Yes, I've seen them. I'll think about how they can be integrated.

clanmills commented 3 years ago

I've set up branch 'main' and bumped the revision numbers. When PR #1545 merges, we're good to start on Exiv2 v1.00.

@derselbst Tom. I've invited you to join Team Exiv2 in order to collaborate on this issue. If you work in our repository, It's easier for us to review your changes. @hasssec and I can update your PR and this makes life easier when adding new scripts and images to the test suite. I hope you will continue to contribute to Exiv2, however it's fine if you ask me to "uninvite you" when this work is complete.

clanmills commented 3 years ago

I was working on my book and discovered I had written the following about 6 months ago:

This decoding function decodeCanonAFInfo() added to TiffMappingInfo manufactures the new tags. Normally, tags are processed by the binary tag decoder and that approach was taken in branch fix981_canonAf. #981 and #988 However, the binary tag decoder cannot deal with AFInfo because the size of some metadata arrays cannot be determined at compile time.

We should support decoding AFInfo in v1.00, however we should NOT auto-port this PR. We can avoid having to explicitly delete tags from the metadata before writing by adding a "read-only" flag to TagInfo. This would break the Exiv2 v0.27 API and has been avoided. There is an array in decodeCanonAFInfo() which lists the "manufactured" tags such as Exif.Canon.AFNumPoints. In the Exiv2 v1.00 architecture, a way might be designed to generate that data at run-time.

More work ahead. Worth the effort.