From feb5900ec82a34182e09603b73e021497a377f9c Mon Sep 17 00:00:00 2001 From: Julian Zahnow <54999052+Enthendor@users.noreply.github.com> Date: Fri, 23 Oct 2026 22:05:43 +0200 Subject: [PATCH 1/3] LR-EDIT-OPTICS-PROFILE: Sigma 10-18 mm exception on the ILCE-6700 The body's rule leaves 1.36-1.38 px at the Sigma 10-18 mm F2.8 DC DN's 10 mm end. A measured lens exception (Exif LensModel + FocalLength <= 10.5 mm) uses knot spacing 416 px and unit 14250 there, chosen on 63 photos at 10 mm and checked on 34 from other locations. As rendered: 0.38-0.55 px over 97 photos at 10 mm (median 0.44), 0.36 px at 10.4-10.5 mm; from 11.7 mm the body's rule stays (0.48-0.72 px on 16 photos). Co-Authored-By: Claude Opus 5.5 --- crates/raw/src/vendor/arw.rs | 3 +- crates/raw/src/vendor/arw_lens_rm6.rs | 95 +++++++++++++++++++++----- docs/gaps.md | 1 + docs/raw-parity.md | 2 +- docs/sony-ilce7rm6-lens-corrections.md | 22 +++++- 5 files changed, 101 insertions(+), 22 deletions(-) diff --git a/crates/raw/src/vendor/arw.rs b/crates/raw/src/vendor/arw.rs index 3938fa50dd..1b2e1ef46d 100644 --- a/crates/raw/src/vendor/arw.rs +++ b/crates/raw/src/vendor/arw.rs @@ -784,7 +784,8 @@ pub(crate) fn decode(bytes: &[u8], mode: Mode) -> Result { let rm6: Vec = if linear_rgb || !super::arw_lens::is_3_2(crop) { Vec::new() } else { - super::arw_lens_rm6::lens_opcodes(raw, &model, Rect::new(0, 0, w, h), reference) + let lens = metadata.lens_model.as_deref(); + super::arw_lens_rm6::lens_opcodes(raw, &model, lens, metadata.focal_length, Rect::new(0, 0, w, h), reference) }; if !rm6.is_empty() && let Some(record) = sony_lens.as_mut() diff --git a/crates/raw/src/vendor/arw_lens_rm6.rs b/crates/raw/src/vendor/arw_lens_rm6.rs index 9b8afd7773..f20263901d 100644 --- a/crates/raw/src/vendor/arw_lens_rm6.rs +++ b/crates/raw/src/vendor/arw_lens_rm6.rs @@ -62,6 +62,31 @@ const FULL_FRAME_CROP: &[(&str, (f64, f64))] = &[("ILCE-7RM6", (9984.0, 6656.0)) /// The verified APS-C bodies (Exif `Model`): their knots span the file's own default crop, the last knot at its corner. /// Measured on the ILCE-6700 (docs/sony-ilce7rm6-lens-corrections.md → ILCE-6700). const APSC_BODIES: &[&str] = &["ILCE-6700"]; + +/// A lens whose tables follow its body's convention with another knot spacing and unit, at the focal lengths up to +/// `max_focal_mm`; measured like the body, against the camera JPEG. +struct LensException { + /// Exif `Model` of the body. + body: &'static str, + /// Exif `LensModel`, as the body records it. + lens: &'static str, + max_focal_mm: f64, + knot_spacing: f64, + unit: f64, +} + +/// The measured lens exceptions (docs/sony-ilce7rm6-lens-corrections.md → ILCE-6700). +const LENS_EXCEPTIONS: &[LensException] = &[ + // Sigma 10-18 mm F2.8 DC DN at its wide end: 97 photos at 10 mm and 2 at 10.4-10.5 mm. The body's rule leaves + // 1.36-1.38 px there; from 11.7 mm on the body's rule fits better + LensException { body: "ILCE-6700", lens: "10-18mm F2.8 DC DN | Contemporary 023", max_focal_mm: 10.5, knot_spacing: 416.0, unit: 14250.0 }, +]; + +/// The exception for the body `model` with the lens `lens` at `focal_mm`, if one is measured. +fn lens_exception(model: &str, lens: Option<&str>, focal_mm: Option) -> Option<&'static LensException> { + let (lens, focal) = (lens?.trim(), focal_mm.filter(|f| f.is_finite() && *f > 0.0)?); + LENS_EXCEPTIONS.iter().find(|e| e.body == model.trim() && e.lens == lens && focal <= e.max_focal_mm) +} /// The distortion table's value of 1 (relative radius); a global least-squares fit over the 34 photos gives 15000.7. const DISTORTION_UNIT: f64 = 15000.0; /// The CA table's value of 1 (relative to the green source radius). Provisional: the median best unit of the 20 @@ -179,18 +204,19 @@ fn fill_zoom(source: &dyn Fn(f64) -> f64, x: f64, y: f64) -> Option { /// worse than 0.25 px at a 1440 px long edge at one of the fit samples within the crop (a padded active area, as in /// lossless files, reaches radii that are never shown). pub(crate) fn warp_opcode(t: &LensTables, knot_spacing: f64, active: Rect, crop: Rect, lateral_ca: bool) -> Option { - gated_warp_opcode(t, knot_spacing, active, crop, lateral_ca, FIT_ERROR_PX_AT_1440) + gated_warp_opcode(t, knot_spacing, DISTORTION_UNIT, active, crop, lateral_ca, FIT_ERROR_PX_AT_1440) } -/// [`warp_opcode`] with the fit gate `fit_error_px` (px at a 1440 px long edge) instead of 0.25 px. -fn gated_warp_opcode(t: &LensTables, knot_spacing: f64, active: Rect, crop: Rect, lateral_ca: bool, fit_error_px: f64) -> Option { +/// [`warp_opcode`] with the distortion unit `unit` instead of 15000 and the fit gate `fit_error_px` (px at a 1440 px +/// long edge) instead of 0.25 px. +fn gated_warp_opcode(t: &LensTables, knot_spacing: f64, unit: f64, active: Rect, crop: Rect, lateral_ca: bool, fit_error_px: f64) -> Option { let v = t.distortion.as_deref().filter(|v| v.iter().any(|&x| x != 0.0))?; let (aw, ah) = (active.width as f64, active.height as f64); if aw < 2.0 || ah < 2.0 || crop.width == 0 || crop.height == 0 { return None; } let (cw, ch) = (crop.width as f64, crop.height as f64); - let source = |r: f64| r * (1.0 + interpolate(v, knot_spacing, r) / DISTORTION_UNIT); + let source = |r: f64| r * (1.0 + interpolate(v, knot_spacing, r) / unit); let z = fill_zoom(&source, cw / 2.0, ch / 2.0)?; // DNG's normalisation: pixel-index units, radius 1 = the farthest active-area corner let (cx, cy) = (crop.x as f64 + (cw - 1.0) / 2.0, crop.y as f64 + (ch - 1.0) / 2.0); @@ -226,12 +252,16 @@ fn gated_warp_opcode(t: &LensTables, knot_spacing: f64, active: Rect, crop: Rect /// R and B by 0.84 × the table. Over the 43 verified photos with enough corner detail, the render's 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 a 1440 px long edge; only the 16–35 mm /// improved. Follow-up: warp the CFA colour planes before demosaic, then turn the planes on here. -pub(crate) fn lens_opcodes(raw: &Ifd, model: &str, active: Rect, crop: Rect) -> Vec { +/// +/// `lens` (Exif `LensModel`) and `focal_mm` (Exif `FocalLength`) select a measured lens exception's spacing and unit. +pub(crate) fn lens_opcodes(raw: &Ifd, model: &str, lens: Option<&str>, focal_mm: Option, active: Rect, crop: Rect) -> Vec { let tables = read_tables(raw); let knots = tables.distortion.as_ref().map_or(0, Vec::len); let Some(spacing) = knot_spacing(model, knots, crop) else { return Vec::new() }; - let op = if APSC_BODIES.contains(&model.trim()) { - gated_warp_opcode(&tables, spacing, active, crop, false, APSC_FIT_ERROR_PX_AT_1440) + let op = if let Some(e) = lens_exception(model, lens, focal_mm) { + gated_warp_opcode(&tables, e.knot_spacing, e.unit, active, crop, false, APSC_FIT_ERROR_PX_AT_1440) + } else if APSC_BODIES.contains(&model.trim()) { + gated_warp_opcode(&tables, spacing, DISTORTION_UNIT, active, crop, false, APSC_FIT_ERROR_PX_AT_1440) } else { warp_opcode(&tables, spacing, active, crop, false) }; @@ -372,7 +402,7 @@ mod tests { let t = read_tables(&ifd); assert!(t.distortion.is_none(), "{what}"); assert!(warp_opcode(&t, knots(), FF_ACTIVE, FF_CROP, false).is_none(), "{what}"); - assert!(lens_opcodes(&ifd, "ILCE-7RM6", FF_ACTIVE, FF_CROP).is_empty(), "{what}"); + assert!(lens_opcodes(&ifd, "ILCE-7RM6", None, None, FF_ACTIVE, FF_CROP).is_empty(), "{what}"); } // not an integer array, or absent for value in [Value::Ascii("16 0 2 7".into()), Value::Double(vec![2.0, 0.0, 1.0])] { @@ -406,7 +436,7 @@ mod tests { let mut table = vec![v; 17]; table[0] = 16; let ifd = ifd(vec![(DISTORTION_PARAMS, Value::SLong8(table))]); - assert!(lens_opcodes(&ifd, "ILCE-7RM6", FF_ACTIVE, FF_CROP).is_empty(), "{v}"); + assert!(lens_opcodes(&ifd, "ILCE-7RM6", None, None, FF_ACTIVE, FF_CROP).is_empty(), "{v}"); } // degenerate geometry let ff = tables_with_distortion(&values(&FF_DISTORTION)); @@ -455,7 +485,7 @@ mod tests { let t = read_tables(&ifd); assert!(t.ca.is_some() && t.distortion.is_none()); assert!(warp_opcode(&t, knots(), FF_ACTIVE, FF_CROP, true).is_none()); - assert!(lens_opcodes(&ifd, "ILCE-7RM6", FF_ACTIVE, FF_CROP).is_empty()); + assert!(lens_opcodes(&ifd, "ILCE-7RM6", None, None, FF_ACTIVE, FF_CROP).is_empty()); // an all-zero distortion table counts as absent let zero = LensTables { ca: t.ca, ..tables_with_distortion(&[0.0; 16]) }; assert!(warp_opcode(&zero, knots(), FF_ACTIVE, FF_CROP, true).is_none()); @@ -509,7 +539,7 @@ mod tests { let ifd = sony_lens_ifd(&FF_DISTORTION, &FF_VIGNETTING, &FF_HQ_CA, [1, 2, 1]); let with_ca = warp_opcode(&read_tables(&ifd), knots(), FF_ACTIVE, FF_CROP, true); assert!(matches!(with_ca, Some(Opcode::WarpRectilinear { ref planes, .. }) if planes.len() == 3), "{with_ca:?}"); - let ops = lens_opcodes(&ifd, "ILCE-7RM6", FF_ACTIVE, FF_CROP); + let ops = lens_opcodes(&ifd, "ILCE-7RM6", None, None, FF_ACTIVE, FF_CROP); let [Opcode::WarpRectilinear { planes, .. }] = ops.as_slice() else { panic!("{ops:?}") }; assert_eq!(planes.len(), 1); let green = warp_opcode(&tables_with_distortion(&values(&FF_DISTORTION)), knots(), FF_ACTIVE, FF_CROP, false); @@ -575,12 +605,12 @@ mod tests { assert_eq!(knot_spacing("ILCE-6700", 0, A6700_CROP), None); assert_eq!(knot_spacing("ILCE-6700", 11, Rect { x: 0, y: 0, width: 0, height: 0 }), None); let ifd = sony_lens_ifd(&A6700_DISTORTION, &APSC_VIGNETTING, &APSC_CA, [1, 2, 1]); - let ops = lens_opcodes(&ifd, "ILCE-6700", A6700_CROP, A6700_CROP); - let gated = gated_warp_opcode(&read_tables(&ifd), s, A6700_CROP, A6700_CROP, false, APSC_FIT_ERROR_PX_AT_1440); + let ops = lens_opcodes(&ifd, "ILCE-6700", None, None, A6700_CROP, A6700_CROP); + let gated = gated_warp_opcode(&read_tables(&ifd), s, DISTORTION_UNIT, A6700_CROP, A6700_CROP, false, APSC_FIT_ERROR_PX_AT_1440); assert_eq!(ops, gated.into_iter().collect::>()); assert_eq!(ops.len(), 1); // other APS-C bodies stay unverified - assert!(lens_opcodes(&ifd, "ILCE-6600", A6700_CROP, A6700_CROP).is_empty()); + assert!(lens_opcodes(&ifd, "ILCE-6600", None, None, A6700_CROP, A6700_CROP).is_empty()); } /// The APS-C fit gate: a table the polynomial fits within 0.44 px is applied on the ILCE-6700, while the @@ -591,17 +621,46 @@ mod tests { let t = read_tables(&ifd); let s = knot_spacing("ILCE-6700", 11, A6700_DECODED_CROP).unwrap(); assert_eq!(warp_opcode(&t, s, A6700_ACTIVE, A6700_DECODED_CROP, false), None); - let ops = lens_opcodes(&ifd, "ILCE-6700", A6700_ACTIVE, A6700_DECODED_CROP); + let ops = lens_opcodes(&ifd, "ILCE-6700", None, None, A6700_ACTIVE, A6700_DECODED_CROP); assert!(matches!(ops.as_slice(), [Opcode::WarpRectilinear { planes, .. }] if planes.len() == 1), "{ops:?}"); } + const SIGMA_10_18: &str = "10-18mm F2.8 DC DN | Contemporary 023"; + + /// The Sigma 10-18 mm exception: its spacing and unit at 10-10.5 mm, the body's rule from 11.7 mm (and without a + /// lens or focal length, on another body or another lens). + #[test] + fn lens_exception_applies_only_to_its_lens_body_and_focal_range() { + let ifd = sony_lens_ifd(&A6700_DISTORTION, &APSC_VIGNETTING, &APSC_CA, [1, 2, 1]); + let t = read_tables(&ifd); + let geometry = (A6700_ACTIVE, A6700_DECODED_CROP); + let exception = gated_warp_opcode(&t, 416.0, 14250.0, geometry.0, geometry.1, false, APSC_FIT_ERROR_PX_AT_1440); + let body = lens_opcodes(&ifd, "ILCE-6700", None, None, geometry.0, geometry.1); + assert!(exception.is_some() && body.len() == 1); + assert_ne!(body.first(), exception.as_ref()); + for focal in [10.0, 10.5] { + let ops = lens_opcodes(&ifd, "ILCE-6700", Some(SIGMA_10_18), Some(focal), geometry.0, geometry.1); + assert_eq!(ops.first(), exception.as_ref(), "{focal} mm"); + } + for (lens, focal) in [(Some(SIGMA_10_18), Some(11.7)), (Some(SIGMA_10_18), None), (None, Some(10.0)), (Some("E 10-20mm F4 PZ G"), Some(10.0))] + { + let ops = lens_opcodes(&ifd, "ILCE-6700", lens, focal, geometry.0, geometry.1); + assert_eq!(ops, body, "{lens:?} {focal:?}"); + } + // hostile focal lengths fall back to the body's rule + for focal in [f64::NAN, f64::INFINITY, -10.0, 0.0] { + assert_eq!(lens_opcodes(&ifd, "ILCE-6700", Some(SIGMA_10_18), Some(focal), geometry.0, geometry.1), body); + } + assert_eq!(lens_exception("ILCE-6600", Some(SIGMA_10_18), Some(10.0)).map(|e| e.unit), None); + } + #[test] fn unknown_model_gets_no_opcodes() { let ifd = sony_lens_ifd(&FF_DISTORTION, &FF_VIGNETTING, &FF_CA, [0, 2, 1]); - assert!(lens_opcodes(&ifd, "ILCE-7M4", FF_ACTIVE, FF_CROP).is_empty()); - assert!(lens_opcodes(&ifd, "", FF_ACTIVE, FF_CROP).is_empty()); + assert!(lens_opcodes(&ifd, "ILCE-7M4", None, None, FF_ACTIVE, FF_CROP).is_empty()); + assert!(lens_opcodes(&ifd, "", None, None, FF_ACTIVE, FF_CROP).is_empty()); // the camera's in-camera flag (0 = off here) doesn't matter: the table is applied - let ops = lens_opcodes(&ifd, " ILCE-7RM6 ", FF_ACTIVE, FF_CROP); + let ops = lens_opcodes(&ifd, " ILCE-7RM6 ", None, None, FF_ACTIVE, FF_CROP); assert_eq!(ops, warp_opcode(&read_tables(&ifd), knots(), FF_ACTIVE, FF_CROP, false).into_iter().collect::>()); assert_eq!(ops.len(), 1); } diff --git a/docs/gaps.md b/docs/gaps.md index 7389795720..bb5be27d0a 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -79,6 +79,7 @@ Beta-blocking gaps (B) total **~230–430 h**; with the stability and platform w | Date | Change | Summary | |---|---|---| +| 2026-10-23 | patch | Gap 8: the Sigma 10–18 mm on the ILCE-6700 gets a measured lens exception at 10–10.5 mm (0.55 px worst over 99 photos, was about 1.4 px) | | 2026-10-23 | minor | Gap 8 narrows: Sony ILCE-6700 (APS-C) in-file distortion through the ILCE-7RM6 conversion, knots spanning its own crop (30 photos, 3 lenses; 0.73 px worst except 1.38 px at 10 mm); the gap stays open (no lens database), hours unchanged | | 2026-10-21 | minor | Gap 28 shrinks: F8 shows / hides the right panel (`view.rightPanel`); rating `[` `]`, flag cycling and F1 help were already bound, so the entry lists only filter-bar keys, Tab / ⇧Tab and F5; 2–4 h (was 4–8) | | 2026-10-20 | minor | #2362: import destination albums use the shared hierarchy/search picker instead of a flat name list; existing import workflows retained; ranked gaps/readiness counts unchanged | diff --git a/docs/raw-parity.md b/docs/raw-parity.md index f93f98e251..59c38a3234 100644 --- a/docs/raw-parity.md +++ b/docs/raw-parity.md @@ -243,7 +243,7 @@ essentials user (the default look is what they keep). | Item | Adobe | LightCraft | |---|---|---| | Profile database | 3,635 profile files, 56 makers, auto-selected | **0** (Adobe LCPs never used; no database of our own) | -| In-file corrections | ✅ | DNG opcodes (WarpRectilinear, FixVignetteRadial, GainMap…) applied; Panasonic / Leica RW2 distortion (79 photos, 13 bodies, 11 lenses); Sony tag 0x7037 distortion for the ILCE-7RM4A (2 lenses), full-frame Bayer 3:2 ILCE-7CR files with correction Auto or forced Auto (3 lenses; see sony-lens-corrections.md for residuals and limits) the ILCE-7RM6 (3 lenses, 54 photos) and the APS-C ILCE-6700 (3 lenses, 30 photos; 1.38 px at a 10 mm wide end, 0.73 px elsewhere; `sony-ilce7rm6-lens-corrections.md`); Nikon NikonNEFInfo distortion (tag `0xc7d5`, about the sensor-shifted optical centre) for the Z 6II and Z f: F-mount lenses on the FTZ within 0.2 px of the camera JPEG, Z lenses within 0.5–1.5 px except the 24-70's wide end (7 px median; `docs/nikon-lens-corrections.md`) | +| In-file corrections | ✅ | DNG opcodes (WarpRectilinear, FixVignetteRadial, GainMap…) applied; Panasonic / Leica RW2 distortion (79 photos, 13 bodies, 11 lenses); Sony tag 0x7037 distortion for the ILCE-7RM4A (2 lenses), full-frame Bayer 3:2 ILCE-7CR files with correction Auto or forced Auto (3 lenses; see sony-lens-corrections.md for residuals and limits) the ILCE-7RM6 (3 lenses, 54 photos) and the APS-C ILCE-6700 (3 lenses, 145 photos; 0.73 px worst, with a measured Sigma 10–18 mm exception at 10–10.5 mm; `sony-ilce7rm6-lens-corrections.md`); Nikon NikonNEFInfo distortion (tag `0xc7d5`, about the sensor-shifted optical centre) for the Z 6II and Z f: F-mount lenses on the FTZ within 0.2 px of the camera JPEG, Z lenses within 0.5–1.5 px except the 24-70's wide end (7 px median; `docs/nikon-lens-corrections.md`) | | Vignetting / lateral CA from maker notes | ✅ | ⬜ | | Manual distortion, vignetting, CA, defringe | ✅ | ✅ | diff --git a/docs/sony-ilce7rm6-lens-corrections.md b/docs/sony-ilce7rm6-lens-corrections.md index 4e46637b1e..5dff832881 100644 --- a/docs/sony-ilce7rm6-lens-corrections.md +++ b/docs/sony-ilce7rm6-lens-corrections.md @@ -146,6 +146,23 @@ full-frame 0.25 px gate. Measured against the camera JPEG, the fitted polynomial 0.22–0.73 px against 0.23–0.68 px on the 27 photos, 1.35–1.38 px at 10 mm), so APS-C bodies use a 0.5 px gate (`APSC_FIT_ERROR_PX_AT_1440`). +**Lens exception: Sigma 10–18 mm F2.8 DC DN at 10–10.5 mm** (`LENS_EXCEPTIONS`, matched on Exif `LensModel` +`10-18mm F2.8 DC DN | Contemporary 023` and `FocalLength` ≤ 10.5 mm). Its wide end keeps 1.36–1.38 px under the +body's rule. On 115 further private photos of this lens (97 at 10 mm, 2 at 10.4–10.5 mm, 16 at 11.7–12.5 mm), a grid +over spacing 372–426 px and unit 14000–16500 was measured on the exact table. The photos at 10 mm were split by +shooting location: 63 to choose, 34 to check. Spacing and unit trade off along a ridge: 420 px / 14000 was best on the +63 (0.445 px worst) but put 2 of the 34 above 0.75 px, so the pick is **416 px / 14250**, within 0.51 px on both halves. +As rendered (`lightcraft-cli render`, Enable Lens Corrections on): + +| Sigma 10–18 mm | p95 at 1440 px | +|---|---| +| 10 mm, 97 photos (one night photo has too little detail to measure) | 0.38–0.55 px, median 0.44 (body's rule: about 1.4 px) | +| 10.4–10.5 mm, 2 photos | 0.36 px | +| 11.7–12.5 mm, 16 photos (body's rule, the exception does not apply) | 0.48–0.72 px, median 0.57 | + +At 11.7 mm and longer an exception of this kind fits worse than the body's rule (412 px / 14500 on the grid: 0.73–1.19 px against 0.48–0.69 px), so it stops at +10.5 mm, the longest measured focal length where it helps; 10.6–11.6 mm is unmeasured and takes the body's rule. + ## Two conventions A second Sony decoder, `crates/raw/src/vendor/arw_lens.rs` @@ -327,8 +344,9 @@ the resampled source by the source, framing and output size, so tone, colour and - **ILCE-7RM6 and ILCE-6700 only.** Other Sony bodies record the same tags; their knot spacing (defined here by the full-frame crop, or an APS-C body's own 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](#two-conventions)). -- **ILCE-6700 at strong barrel distortion:** 1.36–1.38 px at the Sigma 10–18 mm's 10 mm end (above); its vignetting - and lateral CA tables are not measured. +- **ILCE-6700 at strong barrel distortion:** the body's rule leaves 1.36–1.38 px at the Sigma 10–18 mm's 10 mm end; + that lens has a measured exception (above), other wide lenses with similarly strong tables are unmeasured. Its + vignetting and lateral CA tables are not measured. - **APS-C distortion is unverified in camera on the ILCE-7RM6:** 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. From d1b272e49654a63f49642846beffecc6ecdde20e Mon Sep 17 00:00:00 2001 From: Julian Zahnow <54999052+Enthendor@users.noreply.github.com> Date: Sat, 24 Oct 2026 15:38:20 +0200 Subject: [PATCH 2/3] docs(raw): name the Sigma choice and check sets (review: CodeRabbit on #2733) Co-Authored-By: Claude Opus 5.5 --- docs/sony-ilce7rm6-lens-corrections.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/sony-ilce7rm6-lens-corrections.md b/docs/sony-ilce7rm6-lens-corrections.md index 5dff832881..ca173b5a8c 100644 --- a/docs/sony-ilce7rm6-lens-corrections.md +++ b/docs/sony-ilce7rm6-lens-corrections.md @@ -150,8 +150,9 @@ full-frame 0.25 px gate. Measured against the camera JPEG, the fitted polynomial `10-18mm F2.8 DC DN | Contemporary 023` and `FocalLength` ≤ 10.5 mm). Its wide end keeps 1.36–1.38 px under the body's rule. On 115 further private photos of this lens (97 at 10 mm, 2 at 10.4–10.5 mm, 16 at 11.7–12.5 mm), a grid over spacing 372–426 px and unit 14000–16500 was measured on the exact table. The photos at 10 mm were split by -shooting location: 63 to choose, 34 to check. Spacing and unit trade off along a ridge: 420 px / 14000 was best on the -63 (0.445 px worst) but put 2 of the 34 above 0.75 px, so the pick is **416 px / 14250**, within 0.51 px on both halves. +shooting location: 63 to choose the values, 34 to check them. A larger spacing with a smaller unit fits about as well +(a ridge in the grid): 420 px / 14000 was best on the 63 choice photos (0.445 px worst) but put 2 of the 34 check photos +above 0.75 px, so the pick is **416 px / 14250**, within 0.51 px on both sets. As rendered (`lightcraft-cli render`, Enable Lens Corrections on): | Sigma 10–18 mm | p95 at 1440 px | From a20887e09736438b290e66f118b5b33213dc6204 Mon Sep 17 00:00:00 2001 From: Julian Zahnow <54999052+Enthendor@users.noreply.github.com> Date: Sun, 1 Nov 2026 11:54:10 +0100 Subject: [PATCH 3/3] LR-EDIT-OPTICS-PROFILE: record the LensModel strings seen for the Sigma 10-18 mm exception Co-Authored-By: Claude Opus 5.5 --- crates/raw/src/vendor/arw_lens_rm6.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/raw/src/vendor/arw_lens_rm6.rs b/crates/raw/src/vendor/arw_lens_rm6.rs index 579dcf67ab..5a215ef7a2 100644 --- a/crates/raw/src/vendor/arw_lens_rm6.rs +++ b/crates/raw/src/vendor/arw_lens_rm6.rs @@ -78,7 +78,8 @@ struct LensException { /// The measured lens exceptions (docs/sony-ilce7rm6-lens-corrections.md → ILCE-6700). const LENS_EXCEPTIONS: &[LensException] = &[ // Sigma 10-18 mm F2.8 DC DN at its wide end: 97 photos at 10 mm and 2 at 10.4-10.5 mm. The body's rule leaves - // 1.36-1.38 px there; from 11.7 mm on the body's rule fits better + // 1.36-1.38 px there; from 11.7 mm on the body's rule fits better. `lens` is the only LensModel in 336 files of + // this lens (native E-mount, no adapter); the 116 measured copies carry body firmware "ILCE-6700 v2.00" LensException { body: "ILCE-6700", lens: "10-18mm F2.8 DC DN | Contemporary 023", max_focal_mm: 10.5, knot_spacing: 416.0, unit: 14250.0 }, ];