NC Imago · Docs

Sony embedded lens corrections

The ILCE-7RM6 (A7R VI) has its own conversion, with other conventions measured against its camera JPEGs: Sony ARW lens corrections. On ILCE-7RM6 photos the formula below misses the 24–70 GM II by up to 1.43 px at 1440 px while fitting the 100 mm and 200–600 better, and no single unit fits every lens under either convention; that page's Two conventions section has the comparison and what would settle it.

Sony ARWs record three lens corrections per shot in the raw image IFD: shading (vignetting), lateral chromatic aberration (CA) and distortion, each as a camera setting and a radial table, with copies in the encrypted SR2SubIFD. Imago decodes all three wherever a file carries them and applies distortion here on two validated bodies: ILCE-7RM4A (A7R IVA) and ILCE-7CR (A7CR). The A7R IVA keeps its existing Bayer 3:2 conversion, validated with the FE 24-105mm F4 G OSS and FE 200-600mm F5.6-6.3 G OSS. The A7CR uses a separate conversion, limited to full-frame Bayer 3:2 files with distortion Auto or forced Auto. Apart from the separate ILCE-7RM6 converter, other models and shading and CA on every model are decoded only: no warp, vignetting or CA correction is added for them. The decoded record stays with the image (RawInfo::lens_metadata / RawImage::lens_metadata, types in crates/raw/src/lens_metadata.rs, decoder in crates/raw/src/vendor/arw_lens.rs) for diagnostics and for the validation work described below.

The applied distortion is converted into OpcodeList3 / WarpRectilinear, so the existing profile correction control, CPU/GPU optics path, EXIF orientation handling, and DNG export use the same correction. Linear YCbCr ARWs and non-3:2 camera crops are excluded pending validation. An aspect crop may retain the full-frame radial normalization; the decoder does not guess that relationship. Tables of other lengths and rejected tables remain uncorrected, and a table found only in the encrypted SR2SubIFD is never applied. The camera's distortion Off setting does not erase its table. A7CR Off, missing, unavailable and unknown settings produce no warp. The A7R IVA's existing behavior is unchanged. Newly imported photos use Imago's existing embedded-profile default (enabled); the profile correction control can disable or adjust it.

What is decoded

Component Raw-IFD setting / table SR2SubIFD setting / table Accepted layout
Shading 0x7031 / 0x7032 0x797c / 0x797d SHORT[1]; SSHORT[17]: 16, then 16 samples
Lateral CA 0x7034 / 0x7035 0x797f / 0x7980 SHORT[1]; SSHORT[33]: 32, then two 16-sample curves
Distortion 0x7036 / 0x7037 0x7981 / 0x7982 SHORT[1]; SSHORT[17]: 16, then 16 samples

For the A7CR, CameraCorrectionDisabled records an Off, missing, unavailable or unknown setting. APS-C, aspect crops and lossless M/S remain NotValidatedMode. The raw IFD's correction table is required.

The A7R IVA's applied path is unchanged: its conversion still receives the raw IFD's 0x7037 table exactly as the TIFF reader parsed it, with the same model, framing and fit checks, and produces the same warp. A repeated tag keeps the reader's choice and is only flagged, and a table the reader never reads (past its entry limit) is reported but never applied, so already-catalogued photos, their previews and their stored lens data stay consistent with a fresh decode.

What each component still needs

The following summarizes an investigation on the ILCE-7CR (A7CR): an owner's collection of 131 raws with four lenses (Voigtländer APO-LANTHAR 28mm F2 and 50mm F2, both manual focus; Sony FE 24–50mm F2.8 G; Tamron 35–150mm F2–2.8), plus the three public raw.pixls.us A7CR samples. No private photographs, names or derived images are part of this repository.

A7CR camera-JPEG validation

The A7CR conversion substantially reduces the corrected camera JPEG's geometry mismatch. Its empirical table unit is 15559.300457825264, fixed by the earlier four-file FE 24-50mm fit before checking other files and lenses. This is a measured approximation for the tested scope, not a Sony specification or a universal Sony constant.

For output radius rho in native crop pixels:

spacing = hypot(9504, 6336) / 30
S(r) = r * (1 + interpolate(table, r / spacing) / 15559.300457825264)
source_radius(rho) = S(rho / zoom)

Interpolation is linear. The final segment extends beyond the last knot. Positive samples increase the source radius before the fill zoom; negative samples reduce it.

The fill zoom makes the maximum of S(rho / zoom) / rho on the rectangular crop border equal to one. The optical center is the default crop's pixel-center midpoint. The measured crop is 9504 by 6336 at (32, 20).

Storage padding affects DNG normalization only, so 9600 by 6376 and 9728 by 6656 storage produce the same physical warp.

A four-term odd polynomial represents the map as WarpRectilinear. A dense 32769-point check rejects a polynomial that folds or differs from the table by more than 0.25 px at a 1440 px long edge. This approximation check is separate from agreement with the camera JPEG. The converter applies no shading or lateral CA.

Validation uses all 131 owner files numerically, nine public raw.pixls.us codings, one independent public FE 50mm file and the public issue #1300 FE 16-25mm file. Private pixels stay in memory. No private images, names or source identities are saved in the new measurement artifacts.

The nine private correction-on files cover the FE 24-50mm at 24, 49 and 50 mm. The other 122 private files have correction Off and cover the Tamron 35-150mm and the Voigtlander APO-LANTHAR 28mm and 50mm. Off references validate preservation, not correction-on optics for those lenses.

Green-channel SIFT (Scale-Invariant Feature Transform) matches at 2376 px are filtered by an independent radial fit. Residuals remove median translation only. They fit no scale, center or lens coefficient to the candidate.

High-pass correlation uses held-out pixels at 1200 px. File checks hold out the second file and then every fifth file in each lens/focal/setting group, and both public correction-on files. The private checks were also examined in the earlier study, so they are file checks rather than a fresh independent capture campaign.

The public second-lens checks do not tune the unit.

The A7R VI study uses a 0.75 px camera validation budget at 1440 px. The reporter has support to 0.933 half diagonals. Its direct table's largest radial-bin residual is 0.590 px at 1200 px (0.708 px at 1440 px).

The implemented polynomial reaches 0.638 px in a radial bin and 0.692 px in a supported edge region at 1200 px in source coordinates. Actual corrected-raster matches give 0.173/0.605 px median/p95, a 0.763 px radial-bin maximum and a 0.969 px edge-region maximum at 1200 px. That edge region is 1.162 px at 1440 px, so this does not pass a strict 0.75 px budget everywhere measured.

The unit test against the independently fitted smooth feature curve uses the combined 1.0 px table and polynomial approximation budget; its observed maximum is 0.895 px at 1440 px. These are different metrics.

The held-out private 24 mm file has weak outer-radius support. No unsupported ideal corner is counted as measured.

APS-C, aspect crops and lossless M/S are excluded. Portrait captures use the same sensor-coordinate map and the existing EXIF orientation conversion. Other lenses and the 16-25mm's tele end need more correction-on references.

No Imaging Edge On/Off pairs were measured for this body. Camera JPEGs are the reference for this change.

This branch supplies the warp for camera-look pairing in PR #1586. It does not duplicate that pairing code.

LOOK_VERSION 12 refreshes smart previews that stored no A7CR lens data; RENDER_CACHE_VERSION 69 redraws cached renders.

Existing edits are preserved. Reload from Disk reads the new lens data for old imports. Enable the profile correction control when an existing edit has it disabled.

Local inventory check

crates/raw/tests/it/sony_lens_inventory.rs is opt-in and does nothing in CI. With IMAGO_SONY_LENS_DIR naming a directory of ARWs it decodes each from its headers and prints its record (settings, sources, copies' agreement, sample ranges, statuses); IMAGO_SONY_LENS_FULL=1 also compares header and full decode. With IMAGO_SONY_LENS_INVENTORY naming a CSV inventory it checks every file's table counts, sizes, sample counts, minima and maxima, raw-IFD versus SR2SubIFD agreement, settings (numbers and labels), stored size, compression and crop tags. A file with a row must decode, and every file must have a row, whether it decodes or not. Any decode error fails the check except an unsupported variant, which is counted and must equal IMAGO_SONY_LENS_UNSUPPORTED (default 0). On the 131 A7CR raws above, all 131 inventory rows reproduce and every file decodes. On 133 public Sony ARWs (127 raw.pixls.us samples from 100 bodies, four corpus copies and two files attached to a public issue), 110 carry a record, 14 decode without one, and 9 are unsupported variants (eight packed ARWs of older bodies and one ARW without a CFA image); header and full decode agree on the 124 that decode, and no record has disagreeing copies.

Clean-room evidence

The ExifTool EXIF tag-name documentation names the raw-IFD tags 0x7031 to 0x7037 (VignettingCorrection / VignettingCorrParams, ChromaticAberrationCorrection / ChromaticAberrationCorrParams, DistortionCorrection / DistortionCorrParams) with their setting values, and gives the tables as 17 or 33 signed 16-bit integers whose first value is the count. The Sony SR2SubIFD documentation names the encrypted copies 0x797d, 0x7980 and 0x7982; the setting tags next to them (0x797c, 0x797f, 0x7981) are unnamed there and were identified by inspecting files, where they equal the raw-IFD settings. The corpus test's expected tables are the values ExifTool, run as a black-box tool, prints for the public samples. No external photo decoder implementation, lens database or the vendor lens profile was consulted. Sony Imaging Edge Edit 4.1 was used only as a black-box reference on local scratch copies.

The observed inverse mapping, with radius r normalized to the default crop's half diagonal, is:

f(r) = 1 + interpolate(table, r) / 16384
source = center + (output - center) * zoom * f(r)
zoom = 1 / max(f(r)) over radii on the rectangular output boundary

The border radii range from the shorter half edge / half diagonal to 1. Table knots span 0 through 1. Linear interpolation approximates the observed map closely; we do not claim to reproduce Sony's internal interpolator. A fixed-size weighted least-squares fit expresses source radius as r * (k0 + k1*r² + k2*r⁴ + k3*r⁶). The fit is rejected if radial error exceeds 0.0005 of the half diagonal (about 0.43 px at a 1440 px 3:2 preview), or the mapping folds. Table values are bounded and the input must have precisely the supported signed-short layout. Crop offset and normalization are converted into DNG's active-area coordinate system before creating the opcode. The warp's centre and normalization come from the standard DefaultCropOrigin / DefaultCropSize when the file has them, not from Sony's crop tags: on the tested A7R IVA files the former start at raw x=32 and the latter at x=0, and the standard origin is the one that aligns with Sony's exports. The image itself keeps the decoder's crop precedence for every Sony body (Sony's crop tags, then the default crop, then the recorded image size), so the framing is unchanged; the opcode's centre is in active-area coordinates, so the warp stays on the optical centre either way. On the A7R IVA that framing still sits 32 raw pixels left of Sony's exports (eight pixels in a 2376-pixel-wide export).

The main raw-IFD table is important: the similarly named MakerNote table has different values and must not be substituted into this formula. Main raw-IFD and SR2SubIFD values agreed in the test files, and the SR2SubIFD copy is never converted.

Validation

At 2376×1584, independent SIFT correspondences between Sony distortion On and Off TIFF exports gave:

Lens / focal length Median residual 95th percentile Maximum matched radius
FE 200–600 / 200 mm 0.043 px 0.191 px 0.97 half diagonals
FE 200–600 / 600 mm 0.055 px 0.231 px 0.95 half diagonals

These use the fixed 16384 scale and automatic border framing, not fitted per-image scale or zoom. A median translation below 0.004 px was removed from the residuals. Correspondences do not cover all pixels; these are geometric feature residuals, not photometric equality or a worst-case guarantee. Across 20 sampled RAWs (16 at 24–105 mm, four at 200–600 mm), the polynomial's maximum deviation from the interpolated file table was below 0.12 px at 1440 px. That measures approximation error, not agreement with an independent renderer. Camera JPEG and Apple Core Image comparisons support the wide-lens interpretation but are weaker evidence than same-renderer On/Off pairs.

Historical validation before restoring main's image crop precedence: Imago exports with the standard default crop framing were compared directly with Sony's corrected TIFFs. At 2376 px wide, median / 95th-percentile feature residuals were 0.35 / 1.04 px at 24 mm, 0.15 / 0.40 px at 200 mm, and 0.13 / 0.56 px at 600 mm (median translation below 0.024 px). The uncorrected medians at 200 and 600 mm were 8.63 and 10.82 px respectively. Different colour, sharpening, and demosaicing affect feature localization; these are geometry checks, not colour parity. An isolated desktop run on Apple M1 Metal detected the profile, rendered without GPU fallback or warnings, and exposed the existing Optics controls. Existing library edits are not reset: once the source has loaded, enable lens corrections if they were previously disabled.

After restoring main's crop precedence, fresh 2376×1584 exports on 2026-10-10 (main b70ae174 plus the aspect-framing guard) measured:

Focal length Corrected median / p95 residual Uncorrected median Removed translation x / y Matches / maximum radius
24 mm 0.36 / 1.12 px 5.62 px 7.98 / −0.01 px 1,442 / 0.95
200 mm 0.13 / 0.35 px 8.65 px 8.01 / 0.02 px 11,606 / 0.98
600 mm 0.10 / 0.37 px 10.33 px 8.02 / 0.00 px 12,268 / 0.97

Residuals subtract only the median feature translation; no scale or lens coefficients are fitted to these reference pairs. The roughly eight-pixel horizontal translation is the retained crop-origin difference described above, not a claim of exact Sony framing. Maximum radius is in half diagonals. The distortion geometry remains aligned while every body's image framing follows main's crop order.

Tests cover signedness/length rejection, zero and malformed tables, independent 600 mm reference geometry, barrel framing, offset crop normalization, both TIFF byte orders, header/full consistency, and preservation through DNG export. The decoder's synthetic tests (crates/raw/src/vendor/arw_lens.rs) add both byte orders and BigTIFF, exact SHORT / SSHORT types, negative samples, missing, short, long, huge and zero counts, wrong leading counts, values outside the file, a table past the TIFF reader's entry limit (not applied), repeated tags (the reader's copy kept, as before), unknown and negative settings, raw-IFD versus SR2SubIFD precedence and agreement, the correct SR2SubIFD tag numbers, a wrong decryption key, hostile entries inside the decrypted block, linear YCbCr, framing evidence (Sony's crop at x = 0 against the default crop, agreeing crops, padded storage, aspect crops, crop tags outside the frame), all eight EXIF orientations leaving the record unchanged, and neutral, out-of-range and folding tables. On a small classic-TIFF fixture in both byte orders, every truncation and four mutations of each byte outside the pixel data (set to 0x00 and 0xff, top and low bit flipped) decode or fail without a panic. tests/sr2_heap.rs measures the peak heap of decoding a 16 MiB file whose SR2Private directory holds eight fields aliasing the whole file: 37 kB, against 134 MB when those fields were decoded. No private photographs or reference TIFFs are committed.

Camera coverage and regression boundary

The model check is exact: ILCE-7RM4 (A7R IV without the A suffix), ILCE-7M3, unknown/missing model names, and every other model remain unsupported by this distortion decoder. Matching tag numbers and table lengths do not establish the scale, interpolation or crop normalization on another body. Support can expand after independent geometry validation; do not add model-name prefix matching.

The current cargo xtask corpus --download manifest contains 25 Sony CC0 samples across 18 bodies, including samples 4822 (A7R IVA / 24–105 mm) and 3989 (A9 II / 200–600 mm), each with a pinned SHA-256 that the download checks. sony_embedded_distortion_is_limited_to_validated_models checks 23 of them (the two ILCE-7CM2 codings have their own test), and sony_lens_records_of_the_public_samples verifies all 25 against their pins (SONY_SAMPLES in crates/raw/tests/it/corpus.rs, itself anchored by a pinned digest and compared with the manifest) before decoding. No CI job fetches the corpus: these fixture tests run only where it has been downloaded, and otherwise skip each absent file. IMAGO_CORPUS_REQUIRED=1 makes sony_lens_records_of_the_public_samples fail on any absent pinned file instead.

Camera Corpus samples Decoded record: tables, settings (shading / CA / distortion) Applied
ILCE-7RM4A Compressed (4822) All three tables; 257 / 1 / 17 One distortion warp
ILCE-9M2 Compressed (3989) All three; 257 / 1 / 0 None
ILCE-7M3 Compressed, uncompressed All three; 257 / 1 / 0 None
ILCE-7M4 14-bit, lossless L/M/S L/M/S: all three, 272 / 1 / 17; the 14-bit file (APS-C framing, 4608 × 3072): settings only, its 11-sample tables are not interpreted None
ILCE-7RM2 12-bit uncompressed All three; 256 / 0 / 1 None
DSC-RX100 One Shading, from the SR2SubIFD (the raw IFD has no tables); CA and distortion in the 11-sample layout None
DSC-RX100M3 One Shading; CA and distortion in the 11-sample layout None
ILCE-7CR Compressed, lossless L/M All three; 272 / 1 / 0 None
ILCE-7RM4 14-bit compressed All three; 257 / 1 / 17 None
ILCE-3500 5600 K Settings only (unavailable) None
ILCE-7S Shade An all-zero CA table; settings unavailable None
ILCE-7SM2 16:9 framing All-zero shading and CA tables; settings unavailable None
DSLR-A500, DSLR-A700, SLT-A33, DSLR-A580, DSLR-A900 One each No record None

These are model-boundary regression checks, not new geometric validation of those models. sony_lens_records_of_the_public_samples also checks the exact tables, settings, provenance and framing evidence of the three A7CR codings and the A7R IVA sample, and that across the Sony corpus only the A7R IVA's distortion is ever applied. In particular, refusing the A7 III tables resolves the unverified corner warp identified in review. The lossless M/S files are linear YCbCr and remain excluded independently of the model check. Header and full decode must agree for every sample. Synthetic tests also verify both TIFF byte orders, exact model matching, that the warp is centred on the standard default crop while the image keeps Sony's crop, and that DNG export preserves an accepted warp without adding one to rejected models. A non-3:2 Sony image crop is also rejected when the standard default crop still describes 3:2 geometry; the regression fails without the image-framing guard. The existing independent Sony geometry tests continue to cover the enabled A7R IVA path. Before restoring main's crop precedence, re-exporting the 24, 200 and 600 mm reference files after the model restriction produced byte-identical corrected PNGs to the earlier validated exports. Isolated headless app checks showed the A7R IVA profile controls present and the A7 III profile controls absent, with no notices and completed RAW renders.

Before enabling another body, compare corrected and uncorrected renders against independent references across multiple lenses/focal lengths and photographs with features near the edges and corners. Record residuals and spatial coverage, and check crop/aspect and compression variants. Embedded JPEGs are useful when the camera actually applied distortion correction; an Off JPEG cannot certify an On warp. Prefer same-renderer Sony On/Off exports when the camera setting or geometry is ambiguous. Table-fit error alone only checks our polynomial approximation, not whether the table's interpretation is correct.