Verified on 54 ILCE-7RM6 photos, the CA planes made the render worse: our demosaic already hides about 70 % of the lens CA (the render shows 0.29x the raw-CFA CA) while the post-demosaic warp moves R and B by 0.84x the table, so the corner R/G and B/G CA went from rms 0.032 / max 0.125 px (off) to 0.052 / 0.219 px (on) at 1440 px; only the 16-35 mm improved. warp_opcode takes a lateral_ca flag; lens_opcodes passes false, so a Sony ARW gets the one-plane distortion warp. The CA conversion stays live and tested (FF and APS-C examples, both lossless corpus files' tables). The follow-up is to warp the CFA colour planes before demosaic. The doc, tracker (docs/parity-checklist.md), docs/gaps.md, docs/raw-parity.md and whats-new say distortion applied, CA parsed and off by default, vignetting not applied; the one-plane default costs 24 -> 45 ms in the sample stage at 1920 px (three planes: 112 ms). Co-Authored-By: Claude <noreply@anthropic.com>
26 KiB
Sony ARW lens corrections
Sony cameras record three lens-correction tables in the raw IFD of every ARW: distortion, vignetting and lateral
chromatic aberration (CA). LightCraft reads them and turns distortion into a DNG WarpRectilinear opcode, so it
reaches the same Optics ▸ Enable Lens Corrections (optics.lens_profile) as a DNG's embedded warp and Panasonic's
RW2 distortion data. Lateral CA is parsed and converts to the warp's red and blue planes, but it is off by default:
applied after demosaic it made the render's CA worse (Verification). Vignetting is read but not applied (below). The
conversion is verified on the ILCE-7RM6 only; the ILCE-7RM4A has its own conversion, with other conventions
(Two conventions), and other Sony bodies' tables give no correction until they are verified.
The correction is on by default for new imports, as for any file with embedded lens data (Photo::camera_defaults).
Photos imported before this change keep their settings: Photo → Reload from Disk adds the lens data and, when the
photo is still at its import settings, turns the correction on as one undoable step; the automatic reload after an
external editor saves the file does the same. Edited photos keep their settings. The Profile Distortion slider
scales the warp as for DNG and RW2.
The tables
ExifTool's names (EXIF table, SubIFD group); each table is a count-prefixed array of signed 16-bit values
(SSHORT, 17 / 17 / 33 values), and each has an in-camera on/off tag:
| Table | Tag | Flag tag | Count (full frame / APS-C crop mode) | Flag values seen |
|---|---|---|---|---|
Distortion (DistortionCorrParams) |
0x7037 |
0x7036 |
16 / 11 | 0 (off) or 1 (on) |
Vignetting (VignettingCorrParams) |
0x7032 |
0x7031 |
16 / 11 | 2 on all 54 photos |
Lateral CA (ChromaticAberrationCorrParams) |
0x7035 |
0x7034 |
32 / 22 (red, then blue) | 1 on all 54 photos |
The first value is the knot count; only the first count values are used. Full-frame files carry 16 / 16 / 32 knots;
APS-C crop-mode files 11 / 11 / 22, padded to the same array length (distortion with its last value repeated,
vignetting and CA with zeros). The CA table holds red's count / 2 knots, then blue's. The corrections are built
whatever the flags say: the flag only records what the camera did to its own JPEG. Tables with a count outside
2–16 (CA: an odd count or outside 4–32), a count that doesn't fit the array, or non-integer values are ignored.
Measured conventions
None of this is documented; it was measured against the camera's own JPEGs (method below). Residuals are in px at a 1440 px long edge.
- Knots and centre. Knot
isits at sensor radiusi · sfrom the default-crop centre, withs = hypot(9984, 6656) / 30= 399.98 px: the full-frame default crop's half diagonal divided by 15, so knot 15 is the full-frame crop corner. The spacing is fixed on the sensor: APS-C files use the same pixel spacing (verified on CA, rms 0.004–0.010 px against 0.011–0.014 px fori/15of the APS-C half diagonal;i/10of it, 392 px, fits as well). Values are interpolated linearly between knots; beyond the last knot the last segment is extended (it matters only in the outermost ~0.4 knot spacing at the corners of a zoomed-out barrel correction). The centre is the default-crop centre: the measured translation between our render and the camera JPEG has median 0.013 px, max 0.216 px. - Distortion is relative and indexed at the corrected radius, in units of 1/15000: a corrected radius
rreads the source radiusS(r) = r · (1 + v(r) / 15000)(positive values: pincushion; negative: barrel). The camera then zooms so the output fills the default crop: output radius =z· corrected radius, withzchosen so that the largestS(ρ / z) / ρover the crop's border radiiρis 1 (binding at the corner for pincushion, at the short-edge midpoint for barrel).zwas 0.971–1.035 on these photos. Without the zoom every hypothesis fails (24.6 px); with it, the worst residual over the 34 measurable corrected photos was 0.596 px (rms 0.143; corners and edge midpoints 0.570 px), within the 0.75 px budget for a unit between about 14700 and 15350. A global least-squares fit gives 15000.7. - Lateral CA is relative to the green source radius, at that radius: red's source radius is
g · (1 + c_R(g) / 3.36·10⁶), withgthe green source radius (after the distortion correction above) andc_Rthe red half on the same knots; blue likewise with the second half. Positive values: that colour's image is larger than green's. The unit is provisional. The measurement is borderline: on the 15 photos with CA above 0.15 px that a single unit explains, the best (minimax) unit 2.26·10⁶ leaves 0.305 px against a 0.3 px budget; per-photo best units scatter from 1.5·10⁶ to 7.2·10⁶ (about ±30 % scene bias: the RawDB landscape and portrait shots of one scene, with nearly identical tables, measure 3.4–3.5·10⁶ and 4.5·10⁶). The chosen 3.36·10⁶ is the median of the 20 cleanest photos: worst 0.399 px on the 15, and the rms raw CA over all 46 measured photos falls from 0.079 to 0.052 px. Swapping the halves or an absolute form (v · norm / U) is clearly worse. Not applied by default (Verification). - Vignetting: not applied. The camera JPEGs show no vignetting compensation relative to our uncorrected render:
over the 39 photos whose measurement reaches the corners, the corner/centre ratio JPEG / render has median 0.996
(p10 0.907, p90 1.087), while the tables would predict a corner gain with median +32 % for
1 + v / 2¹⁴(+16 % forv / 2¹⁵, +63 % forv / 2¹³). Regressing the measured corner EV on the predicted one gives slope 0.009 ± 0.063. No model (1 + v/U,U/(U − v),2^(v/U)) at any unit from 2¹³ to 2¹⁸ reaches 3 %; the identity is the best of all. The raw data isn't pre-compensated either: the read noise of three dark high-ISO frames is flat from centre to corner, where a digital gain from these tables would raise it by 60–80 %. So the table probably describes the lens's natural falloff, but this camera's JPEGs don't apply it, and nothing shows its unit or model. Options: (a) skip it, so renders match the camera JPEG (the current ruling); (b) apply it with an unverified model for a Lightroom-like look; (c) shoot flat fields (a white wall at several apertures, one lens) and measure the model.
Conversion to WarpRectilinear
crates/raw/src/vendor/arw_lens_rm6.rs builds one WarpRectilinear in OpcodeList3, centred on the default-crop centre
(DNG pixel-index units relative to the active area; radius 1 = the farthest active-area corner, m):
Sfrom the distortion table and the zoomz(bisection on [0.5, 2], 4097 border radii); the green map isG(r) = S(r / z). Red and blue, when the CA planes are on:G(r) · (1 + c(G(r)) / 3.36·10⁶).- Each map is sampled at
u = i / 64, i = 0…64 (65 samples ofG(u · m) / m) and fitted by least squares with DNG's 4-term odd polynomialu · (k0 + k1 u² + k2 u⁴ + k3 u⁶)(vendor::fit_radial_warp, shared with RW2). - Fit gate: a plane is refused when its fit is worse than 0.25 px at a 1440 px long edge at a sample the crop
shows: the samples up to the first one at or beyond the crop corner. The lossless files' padded active area
(10240 × 7168 for a 9984 × 6656 crop) reaches radii that are never shown (
uup to 0.925 is shown there). Over all 54 photos the fit's worst error at all 65 samples was 0.220 px (a lossless file; ARW6 files at most 0.175 px). Between samples the error is somewhat larger: 0.071 against 0.049 px for the full-frame 16–35 mm example in the tests. - By default one plane, G, applied to all three channels:
lens_opcodescallswarp_opcode(…, lateral_ca: false). Withlateral_ca: true(a code change) the planes are[R, G, B], unless the CA table is absent or malformed, a CA value is implausible (|c| above 2·10⁻³ of the radius, i.e. 6720; the largest value in the 54 photos' tables is 1724) or a colour plane fails the gate: then G alone again. The G plane is the same either way. No opcode when the distortion table is absent or all zero (CA alone is never applied: it is relative to green), the zoom has no solution in [0.5, 2] or the G fit fails. A refused table leaves the photo uncorrected rather than wrongly warped.
The opcode then goes through the same pipeline stage as a DNG's (crates/pipeline/src/optics.rs): it rotates with
the photo (portrait files are covered) and is carried by DNG export, as RW2's is.
Which bodies get it
Only bodies listed in FULL_FRAME_CROP (arw_lens_rm6.rs) get an opcode: today ILCE-7RM6 (9984 × 6656). The unit and
the knot spacing are verified on that body alone; the spacing is defined by its full-frame crop, and a body with
another sensor size or another unit would be warped wrongly. Other bodies get no correction from this conversion; the
ILCE-7RM4A gets its own, with other conventions (below). To add a body: collect photos of it shot with distortion
correction on (several lenses and focal lengths), repeat the camera-JPEG registration below against the uncorrected
render, confirm the unit, the knot spacing and the zoom rule (or measure the body's own), then add its full-frame
default crop to the table with a test.
Two conventions
A second Sony decoder, crates/raw/src/vendor/arw_lens.rs
(Sony embedded distortion corrections, PR #498), reads the same 0x7037 table on the
ILCE-7RM4A, validated against Sony Imaging Edge exports (distortion On/Off pairs at 200 and 600 mm with the
FE 200–600 mm; corrected TIFFs at 24 mm with the FE 24–105 mm). Its conventions differ from the ones above
(r_o = output radius about the crop centre, v = the table, linear between knots):
ILCE-7RM6 (this page, arw_lens_rm6.rs) |
ILCE-7RM4A (arw_lens.rs) |
|
|---|---|---|
| Knots | i · 399.98 px, fixed on the sensor; last segment extended |
i / 15 of the crop's half diagonal; radius clamped to 1 |
| Table indexed at | the corrected radius r_o / z |
the output radius r_o |
| Unit | 15000 | 16384 |
| Zoom | fill: the largest S(ρ / z) / ρ over the border is 1 |
1 / max f over the border radii |
| Aspect crops | applied; the zoom for the recorded crop is unverified | refused (native 3:2 only) |
| Tables | distortion (16 knots; 11 in APS-C); lateral CA parsed, off by default; vignetting read, not applied | distortion, 16 entries only |
arw::decode passes both the same active area and crop (the DNG-style default crop, on the ILCE-7RM6 the same as
Sony's crop tags); their model lists don't overlap, so a photo gets at most one of them: an ILCE-7RM6 this page's
conversion, an ILCE-7RM4A the other, every other body neither.
Cross-check. The ILCE-7RM4A formula, scored on the 34 measurable corrected ILCE-7RM6 photos exactly as in Measured conventions (radial bins, px at 1440, max / rms; "c" = the worst corner or edge-midpoint point):
| Variant | Unit | All photos | Corners max / median | 24–70 GM II (23) | 100 mm Macro (8) | 200–600 G (3) |
|---|---|---|---|---|---|---|
| A: this page | 15000 | 0.596 / 0.143 | 0.570 / 0.099 | 0.596 / 0.100, c 0.474 | 0.361 / 0.135, c 0.336 | 0.588 / 0.325, c 0.570 |
| B1: the ILCE-7RM4A formula | 16384 | 1.429 / 0.319 | 1.913 / 0.204 | 1.429 / 0.385, c 1.913 | 0.289 / 0.049, c 0.234 | 0.264 / 0.115, c 0.320 |
| B2: B1 at its best single unit | 15680 | 0.940 / 0.223 | 1.202 / 0.098 | 0.940 / 0.215, c 1.202 | 0.327 / 0.100, c 0.278 | 0.683 / 0.428, c 0.637 |
| B3: A's indexing and zoom at 16384 | 16384 | 1.450 / 0.455 | 1.845 / 0.350 | 1.450 / 0.539, c 1.845 | 0.284 / 0.062, c 0.238 | 0.654 / 0.329, c 0.586 |
- B1 misses the 0.75 px budget on these photos, but only on the 24–70 GM II: at 24–28 mm it under-corrects the barrel in the outer half (up to 1.43 px at 0.7–0.85 of the half diagonal), at 45–70 mm the pincushion by a nearly uniform 0.2–0.4 px. On the 100 mm and the 200–600 it fits better than A, and on the 200–600, the one lens both validations share, its unit agrees with this body's (best unit under its indexing 16524–16602). So the data does not show the two bodies differing.
- The zoom rules agree (within 0.0035 on every photo); the unit and the table index make the difference. Under A's indexing the 24–70 GM II's barrel and pincushion ends want the same unit (14749 / 14860, 0.7 % apart); under B's they need 14792 / 15705 (6 % apart).
- APS-C: B refuses 11-entry tables. Its knots read literally on the APS-C crop (
i / 15of its half diagonal, 261.5 px) fit the APS-C RawDB files' lateral CA 4 × worse on 3 of 6 photos than the sensor-fixed 400 px;i / 10(392 px) can't be told from 400 px.
Open question: no single unit fits every lens under either convention. The 200–600 wants a 5–7 % larger unit (a
weaker correction) than the 24–70 GM II's pincushion end with similar table values (+409 … +561 against +406 … +454
at the corner), under both indexings and every knot spacing tried (0.94–1.12 ×: the ratio of the two lenses' best
units stays 1.053–1.073). A's 15000 is the compromise that keeps every photo within 0.6 px; 16384 is the 200–600's
value. That rests on 3 measurable 200–600 photos (594–600 mm). The ILCE-7RM4A's own wide-angle evidence is its
weakest: its On/Off pairs are all 200–600, and its 24 mm check (LightCraft export against Sony's TIFF) is median 0.35 /
95th percentile 1.04 px at 2376 px. On its WIDE unit-test table the two formulas differ by up to 2.49 px at 1440 at
the corner (computed from the formulas, not measured).
What would settle it: Sony Imaging Edge On/Off exports of the 24–70 GM II (or any wide zoom's wide end) on either body, which would show whether the wide-end disagreement is the body or the formula; and more lenses, and more 200–600 photos, on the ILCE-7RM6, to tell a per-lens unit from a convention. Until then each body keeps the conventions it was validated with.
Method
Photos: 54 ILCE-7RM6 ARWs: 42 private, and the 12 CC0 samples of the
dnglab RawDB ILCE-7RM6 set (ILCE-7RM6_{FF,APSC}_{land,port}_RAW{,-C,-C-HD}.ARW:
full frame and APS-C, landscape and portrait, lossless, Compressed and Compressed HQ). cargo xtask corpus --download
fetches three of them: the APS-C lossless file as arw-sony-ilce7rm6-apsc-land-lossless.arw, and the ARW 6.0
decoder's full-frame Compressed and APS-C Compressed HQ files (arw6-sony-ilce7rm6-ff-land-c.arw,
arw6-sony-ilce7rm6-apsc-land-hq.arw). The corpus test corpus_sony_lens_tables_become_opcodes checks all three, and
the full-frame lossless file where it is present.
Four lenses: FE 24–70 mm F2.8 GM II (27 photos), FE 16–35 mm F2.8 GM II (12, all RawDB), FE 100 mm F2.8 Macro GM OSS
(9), FE 200–600 mm F5.6–6.3 G OSS (6). 41 photos have in-camera distortion correction on (all private); 13 have it
off (the 12 RawDB files and one private photo). Only aggregate numbers per lens are reported. The scripts are local
(the gitignored plan/arw6/lens/), not in the repository; the steps are described so they can be redone.
- Oracle: the camera JPEG inside each ARW (
JpgFromRaw, 9984 × 6656 / 6528 × 4352, stored unrotated, turned to the photo's orientation). Tag values were read with ExifTool used as a tool. Clean room: no lens-correction code of any other project and no Adobe data was read. - Distortion (camera-JPEG block registration). Our render at half the JPEG size and the JPEG (2 × 2 box-reduced)
are both reduced to a quarter of the JPEG size; on the green channel, every 40 px a 64 px JPEG block is located in
the render by normalised cross-correlation with parabolic sub-pixel refinement. Pass 1 searches coarse-to-fine
(±28 px at 1/16 size, then ±6 px); pass 2 (used for every number) searches ±6 px around pass 1's smooth radial fit
(data-derived, needed for a macro of a periodic screen). Blocks with low contrast (std < 2), NCC < 0.8 or a
tangential displacement above 4 px are dropped. A robust fit of
p_render = g(r) · p_jpeg + t(gan even 4-term polynomial) gives the translationt; the measured radius ratio is the median of 32 radial bins (≥ 10 blocks each). A photo is measurable with ≥ 300 blocks and ≥ 15 bins reaching 0.7 of the half diagonal: 34 of the 41 corrected photos (two black frames, a near-black night frame, two low-detail frames and two whose blocks reach only 0.54 of the half diagonal are not). 108 hypotheses (radius norm, knot placement, relative or absolute, table indexed at the corrected or the source radius, zoom rule), each with its best unit, were compared. - Lateral CA (raw CFA). The demosaiced render shows only 25–60 % of the lens's CA and the JPEG's 4:2:2 chroma and noise reduction hide it, so CA was measured on the raw: from LightCraft's uncompressed CFA DNG export, the red (blue) plane against green interpolated at the same sites (4-neighbour mean), square-root encoded, 2 × 2 binned; 40 px blocks every 24 px, ±3 px NCC search, radial component, median per 16 radial bins.
- Vignetting (exposure brackets). The JPEG is resampled into the render's geometry; per pixel and channel, the exposure at which eight renders bracketed −0.3 … +1.8 EV equal the JPEG value is interpolated (smooth areas, JPEG values 12–240, monotone brackets). That cancels the fitted tone curve, since exposure is a scene-linear gain before it. Medians in 16 annuli give the JPEG's falloff relative to our uncorrected render.
Verification
Every photo rendered with Enable Lens Corrections on and off at half the JPEG size (50 of the 54 are Compressed or Compressed HQ ARW 6.0 files, so the renders used a local build of this code merged with the ARW6 decoder), then the same block registration (passes 1 and 2) against the camera JPEG, and R and B against G inside each render (80 px blocks every 128 full-resolution px, ±4 px search; per corner the median radial shift of the 12 nearest blocks). px at a 1440 px long edge; aggregates only.
Distortion, photos corrected in camera (our render with the correction on vs the camera JPEG; budget 0.75 px). "Corners + midpoints" is the 2-D displacement at the four corners and four edge midpoints, global translation included; "bins" the radial residual over 32 radial bins. A photo counts as measurable as in the method above.
| Lens | Photos (measurable) | Focal lengths | Corners + midpoints max / rms | Bins max / rms | Uncorrected, corners (per-photo max) |
|---|---|---|---|---|---|
| FE 24–70 mm F2.8 GM II | 26 (23) | 24–70 mm | 0.553 / 0.125 | 0.503 / 0.066 | 1.1–17.3 |
| FE 100 mm F2.8 Macro GM OSS | 9 (7) | 100 mm | 0.409 / 0.111 | 0.317 / 0.097 | 0.6–4.8 |
| FE 200–600 mm F5.6–6.3 G OSS | 6 (3) | 594–600 mm | 0.679 / 0.321 | 0.498 / 0.274 | 7.5–10.8 |
| All | 41 (33) | 0.679 / 0.150 (206 points) | 0.503 / 0.109 | 0.6–17.3 |
No photo is over budget; the global translation is at most 0.056 px. The 8 not measurable are black, near-black or low-detail frames, or frames whose blocks don't reach 0.7 of the half diagonal. That is one more than in the measurement (33 measurable against 34): one 100 mm photo keeps only 255 of the 300 blocks needed when it is matched on the corrected render, as block coverage differs slightly between the two renders. The 200–600 mm keeps the systematic residual the measurement found for it (its own best unit is 15659, see Limits).
Controls, correction off in camera (13 photos: the 12 RawDB files, full frame and APS-C, and one private 24–70 mm at 58 mm). Their JPEG keeps the lens's distortion, so our corrected render must differ from it by the model's own shift: it does, by up to 10.5 px (full frame) and 11.4 px (APS-C), and matches that shift within 0.639 px at the corners and midpoints (bins 0.259 px; full-frame RawDB files ≤ 0.218 px, APS-C ≤ 0.639 px). This checks that the pipeline renders the model (also for APS-C, barrel, zoom 0.984), not that the APS-C model is right.
Portrait photos: 8 corrected (orientation 6 and 8) within 0.232 px, 6 controls within 0.369 px of the model's shift: the warp turns with the photo.
Lateral CA in the render (radial R-vs-G and B-vs-G shift, median of the 12 blocks nearest each corner; 43 photos with enough detail at all four corners; budget 0.3 px):
| Lens | Photos | Off: max R / B | Off: rms | On: max R / B | On: rms | Camera JPEG: max / rms |
|---|---|---|---|---|---|---|
| FE 24–70 mm F2.8 GM II | 23 | 0.089 / 0.071 | 0.028 | 0.219 / 0.148 | 0.069 | 0.150 / 0.036 |
| FE 16–35 mm F2.8 GM II | 12 | 0.125 / 0.054 | 0.041 | 0.030 / 0.051 | 0.020 | 0.058 / 0.016 |
| FE 100 mm F2.8 Macro GM OSS | 6 | 0.020 / 0.050 | 0.013 | 0.061 / 0.157 | 0.044 | 0.021 / 0.004 |
| FE 200–600 mm F5.6–6.3 G OSS | 2 | 0.033 / 0.101 | 0.032 | 0.146 / 0.075 | 0.070 | 0.113 / 0.039 |
| All | 43 | 0.125 / 0.101 | 0.032 | 0.219 / 0.157 | 0.052 | 0.150 / 0.028 |
Within budget everywhere, but the correction makes the render's own CA larger on average (rms 0.032 → 0.052 px; smaller only for the 16–35 mm). The reason is where it runs: over the radial bins beyond 0.3 of the half diagonal (418 bins), our demosaiced render shows only 0.29 × the lens CA measured on the raw CFA (correlation 0.85), while the warp after demosaic moves R and B by 0.84 × the table (correlation 0.96). On the raw data the table halves the CA (rms 0.062 → 0.035 px); applied after a demosaic that already hid most of it, it overshoots (rms 0.021 → 0.035 px over the same bins). All of this is far below one pixel at 1440 px.
Ruling: the lateral CA planes are off by default. lens_opcodes emits the G plane alone; the conversion stays in
the code and its unit tests (warp_opcode(…, lateral_ca: true)), so it can be turned on once CA is corrected where
the table applies. Follow-up: warp the raw CFA's red and blue sites before demosaic (the table halves the raw CA,
rms 0.062 → 0.035 px), then turn the planes on and repeat this check. Optics ▸ Remove Chromatic Aberration
(optics.remove_ca, an automatic estimate from the image) remains available for visible fringes. The distortion
numbers above were measured with the three-plane warp, whose G plane is the one the default emits.
Fit gate margins (worst of R, G and B at the gated samples; gate 0.25 px): the 4 lossless files 0.160 px (margin 0.090; 0.220 px at all 65 samples, the padded radii never shown), the 46 full-frame ARW6 files 0.175 px (margin 0.075), the 4 APS-C ARW6 files 0.035 px. The largest CA value in the 54 tables is 1724 (FE 24–70 mm), a quarter of the 6720 bound. So every photo passes: with the CA planes on (the verification renders) each got the three-plane warp, and the corrected and uncorrected renders differ on all 54; by default each gets the one-plane distortion warp.
Performance
LIGHTCRAFT_PROFILE=1 renders of the full-frame lossless file and the full-frame Compressed HQ file (corpus
arw-sony-ilce7rm6-ff-land-lossless.arw, arw6-sony-ilce7rm6-ff-land-hq.arw), on a 4-core laptop shared with other
builds (so absolute times are high); the fastest of 3–5 runs. The warp runs in the sample stage:
| Render | Off | One plane (the default; measured with manual Distortion 1) | Three planes (CA planes on) |
|---|---|---|---|
| Full size 9984 × 6656, lossless | 267 ms | — | 3098 ms |
| Full size 9984 × 6656, Compressed HQ | 403 ms | 1187 ms | 4014 ms |
| 1920 px long edge (2496 × 1664 source), Compressed HQ | 23.7 ms | 45.4 ms | 112.0 ms |
The default one-plane warp takes the sample stage from 24 to 45 ms at 1920 px (inferred from the manual Distortion run: the same single-plane path, one plane for all three channels). The three colour planes cost about 2.5–3× that, as for an RW2 or DNG with three planes (the same path): the price of turning the CA planes on. The stage cache keys the resampled source by the source, framing and output size, so tone, colour and exposure drags don't re-run it.
Limits
- ILCE-7RM6 only. Other Sony bodies record the same tags; their knot spacing (defined here by the full-frame crop), unit and zoom rule are unverified, so they get no correction from this conversion (the ILCE-7RM4A's distortion has its own: Two conventions).
- APS-C distortion is unverified in camera: no APS-C photo with distortion correction on was available; the APS-C knot spacing is verified on CA only, and the APS-C zoom follows the full-frame rule.
- Aspect-ratio crops (in-camera 4:3, 16:9, 1:1) were not measured; the correction uses the crop the file records.
- One distortion unit for every lens. Fitted per lens the unit is 14826 (24–70 mm, 23 photos, very consistent), 15659 (200–600 mm, 3 photos) and 16017 (100 mm, tiny tables); with the single 15000 the 200–600 mm keeps a systematic residual of up to 0.59 px (within budget, above the method's 0.03 px noise floor). Something lens-dependent is not captured; per-lens units would need per-lens verification.
- Lateral CA off by default. Its unit is provisional (above), and a warp after demosaic, as for RW2 and DNG, overshoots: our demosaic already hides about 70 % of the lens CA, and with the planes on the rms corner CA rose from 0.032 to 0.052 px (Verification). Correcting CA before demosaic is the follow-up.
- Vignetting not applied (above).
- DNG export carries the warp in
OpcodeList3, as for RW2: a DNG reader applies it like any embedded lens correction. - Existing imports need Photo → Reload from Disk to pick up the lens data.
- The camera-JPEG colour fit still matches its pairs without the correction (docs/camera-preview-colour.md).
Sources
- ExifTool EXIF tag names: the names and types of tags
0x7031–0x7037(ExifTool 13.59 was used to read the tag values). Their meaning, units and conventions were measured as above. - dnglab RawDB, Sony ILCE-7RM6: the 12 CC0 samples (data only; dnglab's code was not consulted).