Skip to content

RafDecoder: read the aspect ratio dialed in on the camera - #986

Closed
hennikul wants to merge 3 commits into
darktable-org:developfrom
hennikul:fuji-aspect-ratio
Closed

RafDecoder: read the aspect ratio dialed in on the camera#986
hennikul wants to merge 3 commits into
darktable-org:developfrom
hennikul:fuji-aspect-ratio

Conversation

@hennikul

Copy link
Copy Markdown

AI disclosure

Per AGENTS.md, this change was written with AI assistance (Claude Code
with Claude Opus 5). I have read and reviewed it, it builds clean, and I
bear responsibility for it. I am happy to argue any part of it.

What

Fujifilm bodies with an aspect ratio setting record the choice but leave the
raw data uncropped. Consumers therefore see the full sensor readout and the
framing chosen while shooting is lost, where other raw processors reproduce
it and let the user take it back.

The value is tag 0x0115 in the same Fujifilm proprietary directory that
RafDecoder::getDefaultCrop() already reads 0x0110 and 0x0111 from, so
this is a small addition rather than new parsing:

  • FUJI_RAWIMAGEASPECTRATIO = 0x0115 in TiffTag.h, aliasing
    SAMPLESPERPIXEL the same way FUJI_RAWIMAGECROPTOPLEFT aliases MODEL
  • ImageMetaData::cameraAspectRatio, an Optional<std::array<int, 2>>
  • read it in RafDecoder::decodeMetaDataInternal()

Why a ratio and not a rectangle

The box a vendor describes is relative to that vendor's own output area,
which is not the area rawspeed hands out. A consumer given a rectangle would
have to track that difference per vendor; a consumer given a ratio can just
center it on whatever it actually received. It is also the only thing some
vendors record - Fujifilm's own RawImageCroppedSize beside this tag holds
the native area, not the selected one, so there is no usable rectangle here
in the first place.

The field is named for the concept rather than for Fujifilm, so the other
decoders can fill it as and when someone does that work.

Untested - a Fujifilm owner is needed

I have no Fujifilm body and no raf file to try this on. What I did check:

  • the tag number and its two-entry layout come from ExifTool's
    FujiFilm::RAF table
  • cross-checked against LibRaw, which reads the same tag in
    src/metadata/fuji.cpp and maps it to raw_aspect
  • the height-then-width order matches FUJI_RAWIMAGECROPPEDSIZE, read a few
    lines away in getDefaultCrop() with the same ordering
  • hasEntryRecursive() guards the lookup, since getIFDWithTag() throws and
    not every raf carries that directory
  • builds clean

None of that substitutes for running it on a real file. If someone with a
Fujifilm body that has an aspect ratio setting could verify
that a raf shot
at 1:1 or 16:9 comes out with cameraAspectRatio set to the matching pair,
and that ordinary raf files are unaffected, I would be grateful. I am happy
to add a test if you can point me at where a sample would live.

Context

This is the missing half of darktable-org/darktable#22088, which reproduces
this framing as an editable crop. That side reads the ratio through Exiv2,
which reaches Canon but cannot see Fujifilm's - the value is in the raf
container rather than the Exif makernote, which is why it wants doing here.
Consuming this field in darktable is a follow-up and not part of this PR.

Fujifilm bodies with an aspect ratio setting record the choice but leave
the raw data uncropped, so the framing chosen while shooting is otherwise
lost to consumers. The value sits in the same proprietary directory as the
crop tags getDefaultCrop() already reads, two entries along, stored height
then width like the FUJI_RAWIMAGECROPPEDSIZE beside it.

Expose it as ImageMetaData::cameraAspectRatio rather than as a rectangle.
The box a vendor describes is relative to that vendor's own output area,
which is not the area we hand out, so a consumer that centers a ratio
itself does not have to track the difference. It is also all some vendors
record.

UNTESTED: I have no Fujifilm body and no raf sample to try this on. The
tag number and its layout are taken from ExifTool's FujiFilm::RAF table and
cross-checked against LibRaw's fuji.cpp, and the byte order matches the
sibling tag already read a few lines away in getDefaultCrop(), but none of
that is a substitute for running it. Verification by someone with a
Fujifilm body would be very welcome.

AI disclosure, per AGENTS.md: this change was written with AI assistance
(Claude Code with Claude Opus 5). I have reviewed it, it builds clean, and
I take responsibility for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
hennikul and others added 2 commits August 31, 2026 18:10
Without this the entry falls to the default UNDEFINED case and its count
comes out as the byte length rather than the number of shorts, so
RafDecoder cannot read it and the ratio is silently dropped.

Found by running the previous commit against Fujifilm X-T200 sample
DSCF0074.RAF from raw.pixls.us, which is shot at 16:9. It now reads as
{16, 9}, and a 3:2 sample from the same database still reads as absent
rather than as a ratio.

AI disclosure, per AGENTS.md: written with AI assistance (Claude Code with
Claude Opus 5). I have reviewed it and take responsibility for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The tag is written whether or not a ratio was picked on the camera, so a
plain 3:2 frame carries one too. Reporting that would have consumers crop
every raf to an exact 3:2, trimming the sensor margins off images nobody
chose a ratio for.

Compare it against the cropped size in the same directory, which stays at
the native shape whatever ratio is selected, and only report a ratio that
differs from it.

Checked against raw.pixls.us samples: Fujifilm X-T200 DSCF0074.RAF, shot at
16:9, reports {16, 9}; Fujifilm X100V DSCF0734.RAF, shot at the native 3:2,
reports nothing.

AI disclosure, per AGENTS.md: written with AI assistance (Claude Code with
Claude Opus 5). I have reviewed it and take responsibility for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hennikul hennikul closed this Aug 31, 2026
@hennikul

Copy link
Copy Markdown
Author

It is no longer untested. @gi-man pointed me at raw.pixls.us on the darktable
side, and there is a suitable sample there: Fujifilm X-T200 DSCF0074.RAF,
shot at 16:9.

Two things were wrong with what I first pushed, both of which only running it
could have shown.

The entry was parsed as UNDEFINED. FiffParser has an allow-list of raf
tags treated as SHORT, and I had not added the new one, so its count came
out as the byte length and RafDecoder skipped it. As submitted the first
commit read nothing at all.

The tag is present even at the native ratio. A plain 3:2 frame carries
RawImageAspectRatio too, so reporting it unconditionally would have had
consumers crop every raf to an exact 3:2 and trim the sensor margins off
images nobody chose a ratio for. It is now compared against
RawImageCroppedSize in the same directory, which stays at the native shape
whatever ratio is selected, and only a ratio that differs is reported.

Verified end to end through darktable with the field consumed:

Sample Ratio Result
X-T200 DSCF0074.RAF 16:9 reports {16, 9}, exports 16:9
X100V DSCF0734.RAF native 3:2 reports nothing, exports full frame

The samples also settle the byte order I had inferred rather than observed:
the X100V pair reads (2, 3) against a cropped size of (4160, 6240), so
height first, as getDefaultCrop() already assumes for the sibling tag.

One thing I could not test: fuji_xe4_32_lossy.RAF fails to decompress on
current develop with "compressed RAF header check". That is unrelated to this
change and happens before it, but it means lossy compressed rafs are
unverified here.

Happy to add a test if you point me at where a sample would live.

Disclosure, per AGENTS.md: written with AI assistance (Claude Code with
Claude Opus 5). I have reviewed it and take responsibility for it.

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