Focus Distance in the X2D II 3FR: The Raw Data
Full ExifTool dumps, hash tables, and render-diff numbers behind the X2D II focus-distance metadata finding. Technical appendix.
Muench Workshops runs small-group photography workshops, and I've traveled with them.
Request the free magazine →
Fast, focused culling for Hasselblad shooters. Cull off the card, then hand the keepers to Phocus.
See it for Hasselblad →Table of Contents
This page is the technical appendix to this post. It contains the raw ExifTool/tiff_walk output, tag values, and render-test results behind the claim: the Hasselblad X2D II has recorded recoverable focus distance in every 3FR file since camera firmware 1.2.7.11 (2025-11-27), and none of the three RAW converters tested (Phocus 4.2, Capture One 16.8.3, Lightroom Classic 15.4.1) read it.
One-line finding: three maker-note DOUBLE fields (tag IDs 0x0081, 0x0082, 0x0083) track focal length, magnification, and internal nodal-plane distance closely enough to recover real-world subject distance via the thin-lens relation. ExifTool has no Hasselblad module and dumps them as unnamed data. Hex-edit tests show none of the three converters change their render output when these fields are altered.
The fields
| Tag ID | Type | Inferred meaning | Verdict from capture ladder | Added in firmware |
|---|---|---|---|---|
| 0x0081 | DOUBLE (8-byte float) | Effective (breathing-adjusted) focal length, mm | Strongly supported: textbook breathing curve, 54.6 to 56.6 mm around the lens's nominal 55 mm | v1.2.7.11 (2025-11-27) |
| 0x0082 | DOUBLE (8-byte float) | Magnification ratio | Confirmed: exactly 0.0 at infinity, 0.4% agreement with ground truth at 3 m | v1.2.7.11 (2025-11-27) |
| 0x0083 | DOUBLE (8-byte float) | Internal nodal-plane / image-plane distance | Distance-correlated but NOT literal subject distance: decreases toward infinity across a ~0.65 mm range, consistent with an internal optical-state value, not meters | v1.2.7.11 (2025-11-27) |
ExifTool-invisible caveat: ExifTool 13.55 has no Hasselblad maker-note module. All three fields (and the ~59 other maker-note tags) dump under the generic [MakerUnknown] group as Unknown_0x0081, Unknown_0x0082, Unknown_0x0083, with no name, no unit, and no documentation. Every value in this page was decoded independently (tiff_walk.py, a custom TIFF-IFD parser) and cross-checked bit-for-bit against ExifTool's -a -u -U unknown-tag dump.
Name mapping caveat (probable, not confirmed): the tag-ID-to-name correspondence (0x0081 = ibisFocalLength, 0x0082 = ibisMagnificationRatio, 0x0083 = ibisNodalPlaneDistance) comes from separate reverse-engineering of the X2D II camera firmware, which independently found that firmware v1.2.7.11 added exactly three new IBIS-related maker-note fields with those names (new SnapshotMetadata accessors, populated by a new LensDataService), at the same firmware boundary, with the same field count (3) and the same data type (DOUBLE) for all three. This is strong circumstantial correlation, not a confirmed tag-ID-to-symbol-name binding: the firmware extraction artifacts needed to re-disassemble image_get_field_tuning and pin tag IDs to names directly are no longer on disk. The physical-role verdicts in the table above (focal length / magnification / nodal-plane distance) come from the capture ladder below, independent of the name mapping.
Firmware boundary, confirmed by maker-note entry count: a June 2026 capture on firmware 1.2.7.16 (specimen B0028873, mn-pristine-norm.txt) carries 61 maker-note tags including 0x0081/0x0082/0x0083. A November 2025 capture on firmware 1.1.9.2 (mn-2025-11-11-norm.txt) carries 59 tags, with 0x0081/0x0082/0x0083 entirely absent (an unrelated ASCII tag, 0x0060, is present in the older firmware and dropped in the newer one, accounting for the remainder of the count difference: 59 + 3 added β 1 dropped = 61).
The capture ladder
Shot with the XCD 2,5/55V at barrel-marked distances (no 5 m mark on this lens, so no identical-distance ground-truth pair was available), all three frames at f/8, 1/500s, ISO 25600, firmware 1.2.7.16. Capture order verified three ways: file timestamps, sequential ImageUniqueID, and the fact that magnification (0x0082) is exactly 0.0 only at the frame shot at infinity.
| Tag | B0029112 (1.5 m mark) | B0029113 (3 m mark) | B0029114 (β) |
|---|---|---|---|
| 0x0081 (focal length, mm) | 54.62803 | 55.69748 | 56.634 |
| 0x0082 (magnification) | 0.04116 | 0.01899 | 0.0 (exact) |
| 0x0083 (nodal-plane distance, mm) | 83.60891 | 83.26136 | 82.95699 |
| 0x0063 (RATIONAL, secondary candidate, refuted as focus position) | 0.001953 | 0.001953 | 0.001953 |
Distance recovered via the thin-lens relation d = f Γ (1 + 1/m), using 0x0081 as f and 0x0082 as m:
1.5 m mark (B0029112): f = 54.62803, m = 0.04116
1/m = 24.2954
1 + 1/m = 25.2954
d = 54.62803 Γ 25.2954 = 1381.84 mm = 1.382 m (vs. 1.5 m barrel mark, β7.9%; paraxial error grows at close focus and barrel marks are themselves approximate)
3 m mark (B0029113): f = 55.69748, m = 0.01899
1/m = 52.6593
1 + 1/m = 53.6593
d = 55.69748 Γ 53.6593 = 2988.8 mm = 2.989 m (vs. 3 m barrel mark, β0.36%)
Infinity (B0029114): m = 0.0 exactly, so 1/m is undefined and d is undefined. This is the physically correct output: magnification goes to zero at infinity focus, and the thin-lens relation is undefined there by construction, not by measurement error.
Residual error shrinks with distance exactly as optical theory predicts (paraxial approximation degrades faster near MFD).
Per-field verdicts (from this ladder):
- 0x0082 = magnification ratio: confirmed (exact 0.0 at infinity, 0.4% agreement at 3 m).
- 0x0081 = effective (breathing-adjusted) focal length: strongly supported (54.6 to 56.6 mm curve around the lens's nominal 55 mm).
- 0x0083 = internal nodal-plane/image-plane distance: distance-correlated, not literal subject distance (decreases toward infinity across ~0.65 mm, consistent with an internal-optical-state reading, not meters).
- 0x0063: refuted as a focus-position candidate (constant, 0.001953, across the entire ladder).
Who reads the fields
Test files: byte-identical copies of B0029113 (the 3 m frame) differing in only the two 8-byte 0x0081/0x0082 fields. edited/edited2: absurd transplant (0x0081 55.69748 β 21.0, 0x0082 0.01899 β 0.5). mild: plausible transplant of the real 1.5 m frame's own values (54.62803 / 0.04116) into the 3 m file. All edits verified by full-file byte diff (only the targeted 16 bytes changed). All rendered with straight default settings, no manual adjustments.
Capture One 16.8.3
Does not read the fields. Control-vs-edited pixel diff: max 1424/65535, mean 0.65, 25.0% of pixels differing. Same-file rerun control (same file rendered twice, no edits): max 1393/65535, mean 0.76, 26.1% of pixels differing. The edited-vs-control diff is statistically indistinguishable from C1's own render-to-render noise floor.
Spatial-signature check: both diff pairs show the same magnitude, the same edge-vs-center shape, and the same hot-cluster location (y β 0.19, x β 0.54) in normalized image coordinates. That is the signature of render noise, not a metadata-driven correction responding to the doctored values.
Have you seen the guide? I've published Essential Phocus 4.x for Mac - 85 topics across 8 sections and 246 pages covering everything from HNCS color science to HDR workflows. It's the reference manual Hasselblad hasn't updated since 3.8. It's $49, and updates are included.
Lightroom Classic 15.4.1
Does not read the fields. Protocol: fresh-named copies (control / edited / mild), Enable Profile Corrections + Remove Chromatic Aberration with the Adobe "Hasselblad XCD 2,5/55V" profile, 16-bit TIFF exports, plus a same-file rerun control.
- Rerun noise floor (no edits, same file twice): 731 px of 102M, mean 0.00006, max 447/65535. Effectively zero.
- Doctored-vs-control: edited file, 510 px differing (max 359/65535); mild file, 594 px differing (max 535/65535). Both sit at or below the rerun noise floor.
- The Adobe XCD 2,5/55V profile was verified active on every export via each file's embedded develop settings (XMP), not assumed from the UI.
First-pass confound, noted for the record: the initial LrC pass showed a large (99% of pixels) difference because Lightroom's Sync Settings did not carry "Enable Profile Corrections" to the other two copies (their exports showed LensProfileEnable: 0, no profile name in the embedded XMP). This was caught by reading each export's embedded develop settings, not by the rendered image alone. The profile was enabled manually on every copy and all three were re-exported before the clean-null numbers above were recorded. Not evidence of Adobe reading the maker-note fields; the hypothesis was raised and killed by the re-export.
Phocus 4.2
Does not read the fields. Phocus rendering is deterministic (bit-exact hash reproducible across sessions), which made hashing the discriminator rather than pixel-diff thresholds.
Across three independent test rounds (fresh Phocus sessions, fresh-named file copies, one full Cmd-Q quit between rounds two and three) and eight renders total, spanning three distinct field-value sets (real values, the absurd transplant, the plausible/mild transplant), only two distinct render hashes ever appeared:
| Hash | Label |
|---|---|
f1b36bed⦠| Default render |
626bb516β¦ | Session artifact render |
If Phocus read 0x0081/0x0082, three different field-value sets should have produced up to three different renders. They produced two, and which file got which hash tracked session/import position, not field content: round 1 the artifact hash landed on the doctored file, round 3 it landed on the authentic (pristine-value) file, the exact inverse. Bit-exact triple identity (r1-control == r2-edited2 == r2-mild, three different field-value sets producing the identical hash) is what closed the case.
0x0042 blob-swap test: the 1024-byte maker-note blob 0x0042 (a candidate distance-aware shading/vignetting table, same offset and length in every specimen) was swapped from the 1.5 m frame into a copy of the 3 m frame. Result: control == control2 == f1b36bed (default), swapped == 626bb516 (the known session-artifact hash, not a new render). A genuine distance-aware re-correction from the swapped table would have produced a new, unique hash. It produced the already-seen artifact hash instead, so 0x0042 does not determine the render either, at least not via this test.
What is proven vs. open
Proven:
- The camera has recorded per-shot data since firmware 1.2.7.11 (2025-11-27) from which real focus distance is recoverable, verified against ground-truth barrel marks (1.382 m at the 1.5 m mark, 2.989 m at the 3 m mark, undefined/0.0 at infinity as physically correct).
- Phocus 4.2, Lightroom Classic 15.4.1, and Capture One 16.8.3 do not read these specific scalar fields (0x0081, 0x0082) for their lens corrections. This was shown by hex-editing the fields and finding no render response above each tool's own noise floor (C1, LrC) or hash change (Phocus).
- ExifTool has no Hasselblad maker-note module; these fields (and the rest of the maker note) are invisible to it beyond raw unnamed-tag dumps.
Open, not proven:
- Whether Phocus's XCD lens correction is distance-aware by some other route than these two scalar fields, for example a camera-baked per-distance correction table (0x0042 or a similar blob) applied without reference to 0x0081/0x0082. The 0x0042 blob-swap test above returned a null result, but only via the same hex-edit method that is confounded by Phocus's session artifact (below); it does not rule this out cleanly. This is explicitly unresolved and should not be characterized as either proven or disproven.
- Whether other RAW converters not tested here (RawTherapee, darktable, DxO PhotoLab, and others) read these or related fields. Not tested.
- The tag-ID-to-symbol-name mapping (0x0081 =
ibisFocalLength, etc.) is circumstantial, not confirmed by disassembly. - What Hasselblad's own internal tooling, if any, does with these fields.
Method notes
Hex-edit approach. Test files were produced by editing only the target 8-byte fields at their known file offsets, then verifying with a full-file byte diff that no other byte changed. This isolates the field content as the only variable between a "control" and "edited" copy.
Same-file rerun controls. Every converter was also tested by rendering the same unedited file twice (fresh export, no changes) to establish that converter's own render-to-render noise floor. A doctored-vs-control diff is only meaningful relative to this floor; C1 in particular has nonzero noise between identical renders.
Why bit-exact hashing was the discriminator for Phocus. Phocus's renders are deterministic (same input bytes reliably produce the same output hash), which is unlike C1's noisy renders. That determinism is what let a persistent session artifact be caught and then disproven: pixel-diff thresholds alone would not have distinguished "the fields did something" from "the session state did something," but a bit-exact hash comparison across fresh, differently-named file copies in independent sessions could.
The Phocus session-artifact confound, stated plainly. During testing, Phocus attached a distinct, reproducible-but-not-metadata-driven render state (626bb516β¦, associated with different .phos sidecar defaults such as VignetteAmount and ColorTemperature) to exactly one file per session, and that state survived a full application quit. Across three independent test rounds it attached to a different file each time (by import order in round 1, by alphabetical/session position in round 3), never correlated with which file carried doctored vs. authentic field values. This makes hex-edited-metadata testing an inherently unreliable instrument for asking "does Phocus's correction depend on distance": a render-hash change could come from the field edit or from this artifact, and the two are not distinguishable by this method alone. The root cause of the artifact is not identified.
The clean path forward. Because metadata edits cannot cleanly separate "reads the field" from "session artifact," the only test that can still answer whether Phocus's XCD correction is distance-aware by some other route is optical, not metadata-based: shoot a uniform flat target at a fixed aperture at two distances (near MFD and infinity), render each with Digital Lens Correction toggled ON and OFF, and compare the ONΓ·OFF per-pixel brightness ratio (which is exposure-independent and isolates the correction Phocus chose to apply) between the two distances. Identical curves would mean the correction is generic; different curves would mean it is distance-aware by some route other than the two scalar fields tested here. This test was not run for this page.
Maven's magnetic filters are the ones on my lenses in the field.
Shop Maven Filters →