A photo tagged to be turned is stored turned (#1079) #1086
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/1079-exif-orientation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #1079.
Summary
The re-encode stripped a picture's EXIF without applying its orientation first, so a phone photo held as landscape pixels plus a "rotate" tag was stored on its side. Reproduced first: a 400×300 JPEG with Orientation 6 came out 400×300.
getexifis not called (see "Found on the way").One change to every still picture, tagged or not
_reencodenow collects its output in pieces and joins them after the image is closed, in place ofio.BytesIO. Output bytes are identical (a test compares against a direct Pillow save). Without it the turned worst file cost +74 MB in a fresh process, becauseBytesIOis copied as it grows.Measured
docker run -m 512m, the real validator, peak over the state before the call. All files tagged Orientation 6.transpose, original closedexif_transpose(in_place=True)The spill file is page cache, not process memory: up to 38 MB in the backend and 151 MB in the worker. Under pressure it is reclaimed: the photo passed in a 113 MiB container with
oom_kill 0, wheretransposewas killed in 130 MiB.Through nginx on a warm backend (512 MB, two processes):
oom_kill 0.oom_kill 0.Limits
eXIfchunk follows its pixel data is turned on store, but the header check reports its unturned size. Stored dimensions are right.Found on the way, not fixed here
On main today,
Image.openparses a JPEG's EXIF and reads the data of every directory entry. A 6.3 MB JPEG with 60 entries each claiming the same 6 MB tookImage.openalone to 429 MB. That runs before the pixel check and outside the lock. It gets its own issue.Changelog
changelog.d/1079-exif-orientation.fixed.mdTest plan
tests/wiki/test_exif_orientation.py, 222 tests. Against main'simage_service.py:61 failed, 50 passed, 96 errors, the reproduction among the failures.308 passed.7627 passed, 14 skipped, 107 deselected. It ran before the last test-only edits;image_service.pydid not change after it.🤖 Generated with Claude Code