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.
A free tool now computes this distance from the private fields and writes it into the standard tag on copies of older files: The Data Was Always in Your Older X2D II Files.
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.
July 26, 2026 re-test: the standard SubjectDistance field
Firmware 1.3.16.1 (released 2026-07-16) writes focus distance into the standard EXIF SubjectDistance tag (0x9206, RATIONAL, in the ExifIFD), in addition to the private maker-note fields, which remain present and unchanged. Reshot ladder on 1.3.16.1 with the XCD 2,5/55V: three frames (B0031107/08/09), SubjectDistance = 0.43 / 0.51 / 0.61 m, tracking focus. The tag is absent from every frame shot on 1.2.7.16 or earlier (baseline: B0030947). Cross-check against the private fields on the same frames: d = f × (1 + 1/m) gives 0.41 / 0.47 / 0.57 m, tracking together with the EXIF value a few centimeters lower, consistent with a different reference plane or derivation.
Test files: three copies of B0031107 (the 0.43 m frame). sd-control untouched; sd-absurd with the 0x9206 rational rewritten to 50/1 (50 m); sd-plausible with 61/100 (the real 0.61 m value transplanted from B0031109). The rational sits at file offset 0x5FE; the absurd edit changes exactly 2 bytes, the plausible edit exactly 1 (cmp-verified). A full ExifTool dump diff shows only SubjectDistance differing (the DepthOfField line that moves with it is an ExifTool-computed composite, not file data). The private fields 0x0081/0x0082 are untouched in all three copies, so the standard field is the only variable.
Lightroom Classic 15.4.1: reads it and uses it
All four exports carried bit-identical develop settings (full XMP-crs diff: only RawFileName differs) with the Adobe (Hasselblad XCD 2,5/55V) profile verified active per export.
- Rerun noise floor (same file exported twice): 2,978 of 305.7M samples (0.001%), mean 0.00004, max 79/65535.
- control (0.43 m) vs plausible (0.61 m): 99.95% of samples, mean 2680, max 54195.
- control (0.43 m) vs absurd (50 m): 99.95% of samples, mean 2853, max 53597.
- plausible vs absurd: 99.95% of samples, mean 2731.
Three distance values produced three distinct renders, roughly 70,000× above the rerun floor, and the mean diff is monotonic in 1/distance (smallest gap, smallest diff), the signature of a correction interpolated by focus distance rather than a binary file-changed artifact.
Capture One 16.8.4.13: reads it and uses it
The decisive evidence is geometric, immune to C1's noisy renders: sd-control and sd-plausible (close focus) render at 8519 × 11359 pixels, while sd-absurd (claiming 50 m) renders at 8742 × 11656. A 2-byte metadata edit changed the output dimensions, consistent with a distortion correction that crops less at far focus. Control vs plausible sits at C1's own rerun noise floor (rerun: 69.5% samples, mean 6.64, max 2979; control-vs-plausible: 69.0%, mean 6.15, max 4850), so C1's response between 0.43 m and 0.61 m is indistinguishable from zero; its near-range interpolation may be coarse or bucketed.
Neither application displays SubjectDistance anywhere in its metadata UI; both consume it silently. Phocus was not re-tested by this method: the session-artifact confound documented below makes hex-edit probes on Phocus unanswerable. The optical toggle test was run instead; results in the next section.
July 27, 2026: the Phocus optical toggle test
The clean instrument for Phocus never touches metadata. Flat, evenly lit wall, XCD 2,5/55V, firmware 1.3.16.1, ISO 50: frames at 0.45 m (B0032587, f/2.5), 2.95 m (B0032584, f/2.5), and infinity (B0032585, f/2.5), plus a 0.45 m frame at f/8 (B0032586) excluded from the cross-distance comparison because vignetting correction is aperture-dependent. Each frame exported from Phocus 4.2.1 as 16-bit TIFF with lens corrections off, with standard corrections, and with adaptive corrections, plus a duplicate standard export per frame as a determinism control.
Determinism gate: every duplicate export was bit-exact, zero differing pixels, all four frames. The session artifact from the hex-edit rounds never appeared.
Analysis: per-pixel ON÷OFF ratio (green channel), averaged in 40 radial annuli, curves normalized to center. Noise floor: standard vs adaptive corrections on the same frame differ by at most 0.0008.
- 2.95 m vs infinity (identical recorded aperture, 2.49666109780322 in both files): correction curves differ by up to 0.0055 of gain, single-signed across the entire outer field, the 2.95 m frame receiving less lift, which is the optically correct direction. Roughly 50x the noise floor.
- 0.45 m vs infinity (same aperture): +0.017 more lift mid-field (r ≈ 0.8) with a visibly different curve shape. The outermost annulus swings to -0.18, attributed to close-range distortion remapping displacing the wall's illumination gradient rather than to vignetting; the mid-field figure is the one to quote.
- Ordering is monotonic: the closer the focus, the more the applied correction differs from the infinity correction.
Conclusion: Phocus's lens correction is distance-aware, via Hasselblad's private per-shot data. The source cannot be EXIF SubjectDistance: both the 2.95 m and infinity frames carry only the 0xFFFFFFFF infinity sentinel there.
Side finding: the camera writes a real SubjectDistance value only at close focus. Across 373 frames on firmware 1.3.16.1, all 370 frames between 0.37 and 0.60 m (private-field derived distance) carry real values; the 2.95 m and infinity frames carry the sentinel. The cutoff lies somewhere between 0.6 m and 2.95 m and is not yet mapped.
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.
- Since firmware 1.3.16.1 (2026-07-16) the camera also writes standard EXIF SubjectDistance (0x9206), verified on three frames against the private-field cross-check (July 26 re-test above).
- On 1.3.16.1 files, Lightroom Classic 15.4.1 and Capture One 16.8.4 read SubjectDistance and use it in their lens corrections: single-field hex edits changed LrC's renders far above its rerun floor (monotonic in 1/distance) and changed C1's output pixel dimensions.
- Phocus 4.2.1's lens correction is distance-aware (optical toggle test, 2026-07-27, above), through Hasselblad's private per-shot data rather than EXIF SubjectDistance.
- The camera writes a real SubjectDistance value only at close focus: 370 frames at 0.37-0.60 m all carry one; at 2.95 m and infinity it writes the 0xFFFFFFFF sentinel. The threshold is unmapped between 0.6 m and 2.95 m.
Open, not proven:
- Which private structure Phocus reads for its distance-aware correction. Behavioral distance-awareness itself is proven (July 27 test above); the scalar fields 0x0081/0x0082 are ruled out by the hex-edit test, and the camera-baked 0x0042 table is the leading candidate, but the blob-swap test was confounded by the session artifact, so the specific source remains unidentified.
- 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.