Skip to content

fix: Image.metadata() missing camera-specific Exif sub-IFD tags - #91

Merged
petercorke merged 1 commit into
mainfrom
fix/metadata-exif-subifd
Aug 16, 2026
Merged

fix: Image.metadata() missing camera-specific Exif sub-IFD tags#91
petercorke merged 1 commit into
mainfrom
fix/metadata-exif-subifd

Conversation

@petercorke

Copy link
Copy Markdown
Owner

Summary

  • Image.metadata("FocalLength") (and ExposureTime, FNumber, ISOSpeedRatings, LensModel, ...) always raised KeyError, even though the tag is genuinely present in the file.
  • Root cause: Image.getexif() (the modern public Pillow API) only returns the top-level IFD0 tags (Make, Model, Orientation, ...). Camera-specific tags live in a separate "Exif" sub-IFD that getexif() does not flatten in -- needs an explicit Exif.get_ifd(0x8769) call (0x8769 is the fixed EXIF-spec tag id for the Exif IFD pointer).
  • This is a Pillow API evolution, not a data problem: the old private Image._getexif() used to flatten everything into one dict; the public getexif() deliberately does not.
  • Fix merges the sub-IFD tags into the returned dict alongside the top-level ones.

Test plan

  • tests/test_image_io.py::TestImage::test_metadata -- was a stub (pass), now exercises both top-level (Make, Model) and sub-IFD (FocalLength, FNumber) tags against the bundled walls-l.png fixture (real iPhone 5s EXIF data)
  • Full test suite: 816 passed, 72 skipped, no regressions
  • Manually verified against RVC3-python's chap14.ipynb notebook cells (f = walls_l.metadata("FocalLength"), f = notredame.metadata("FocalLength"))

🤖 Generated with Claude Code

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Image.getexif() (the modern public Pillow API) only returns the
top-level IFD0 tags (Make, Model, Orientation, ...). Camera-specific
tags -- FocalLength, ExposureTime, FNumber, ISOSpeedRatings, LensModel,
etc. -- live in a separate "Exif" sub-IFD that getexif() does not
flatten in. metadata() never fetched that sub-IFD, so
img.metadata("FocalLength") always raised KeyError even though the
tag is genuinely present in the file (confirmed via raw PIL: it's
under Exif.get_ifd(0x8769), the fixed EXIF-spec tag id for the Exif
IFD pointer).

This is a Pillow API evolution, not a data problem: the old private
Image._getexif() used to flatten everything into one dict; the public
getexif() introduced later deliberately does not, requiring an
explicit get_ifd() call for the sub-IFD.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@petercorke
petercorke force-pushed the fix/metadata-exif-subifd branch from 358ba44 to d554ef2 Compare August 16, 2026 01:27
@petercorke
petercorke merged commit 777709e into main Aug 16, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant