mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
LR-EYE-RED: red eye correction with automatic pupil detection
pipeline::redeye: inside the user's ellipse the pupil is found on the source image (a fixed grid in normalized coordinates, so previews and exports agree): weighted centroid and area of red-dominant pixels (or, for pet eyes, of the brightest ones). Pupil Size scales the detected disc; rendering in scene-linear light after white balance neutralizes the red where it is red (iris and catchlights stay) and darkens it. CPU (lin stage) and GPU kernel redeye_k with an equivalence test (both the GPU and the CPU scene-linear paths). Indexed controls redEye.<i>.pupilSize/darken, redeye.add/delete commands, the Red Eye panel (selected eye's sliders, delete) and the loupe tool (drag an ellipse, click to select). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
8b7fb306dc
commit
ebcc41825b
14 files changed
+614
-12
No files matched your search
@@ -27,6 +27,7 @@ pub enum Section {
|
||||
Profile,
|
||||
Calibration,
|
||||
PointColor,
|
||||
RedEye,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
@@ -47,6 +48,7 @@ impl Section {
|
||||
Section::Profile => "Profile",
|
||||
Section::Calibration => "Calibration",
|
||||
Section::PointColor => "Point Color",
|
||||
Section::RedEye => "Red Eye",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,6 +337,28 @@ pub static INDEXED: &[ControlSpec] = &[
|
||||
decimals: 0,
|
||||
track: Gradient { from: "#202020", to: "#f0f0f0" },
|
||||
},
|
||||
ControlSpec {
|
||||
id: "redEye.pupilSize",
|
||||
label: "Pupil Size",
|
||||
section: Section::RedEye,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
default: 50.0,
|
||||
step: 1.0,
|
||||
decimals: 0,
|
||||
track: Plain,
|
||||
},
|
||||
ControlSpec {
|
||||
id: "redEye.darken",
|
||||
label: "Darken",
|
||||
section: Section::RedEye,
|
||||
min: 0.0,
|
||||
max: 100.0,
|
||||
default: 50.0,
|
||||
step: 1.0,
|
||||
decimals: 0,
|
||||
track: Gradient { from: "#d0d0d0", to: "#101010" },
|
||||
},
|
||||
];
|
||||
|
||||
/// `pointColor.3.hueShift` → (`pointColor`, 3, `hueShift`).
|
||||
@@ -361,13 +385,21 @@ fn indexed_field<'a>(s: &'a mut DevelopSettings, id: &str) -> Option<&'a mut f64
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
"redEye" => {
|
||||
let e = s.red_eye.get_mut(i)?;
|
||||
Some(match field {
|
||||
"pupilSize" => &mut e.pupil_size,
|
||||
"darken" => &mut e.darken,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn indexed_get(s: &DevelopSettings, id: &str) -> Option<f64> {
|
||||
// (a clone keeps one accessor for get and set; these lists are tiny)
|
||||
let mut c = DevelopSettings { point_colors: s.point_colors.clone(), ..DevelopSettings::default() };
|
||||
let mut c = DevelopSettings { point_colors: s.point_colors.clone(), red_eye: s.red_eye.clone(), ..DevelopSettings::default() };
|
||||
indexed_field(&mut c, id).map(|v| *v)
|
||||
}
|
||||
|
||||
@@ -398,6 +430,7 @@ pub fn indexed_instances(s: &DevelopSettings) -> Vec<(String, &'static ControlSp
|
||||
let Some((fam, field)) = spec.id.split_once('.') else { continue };
|
||||
let n = match fam {
|
||||
"pointColor" => s.point_colors.len(),
|
||||
"redEye" => s.red_eye.len(),
|
||||
_ => 0,
|
||||
};
|
||||
for i in 0..n {
|
||||
|
||||
@@ -842,14 +842,27 @@ impl Default for Spot {
|
||||
}
|
||||
}
|
||||
|
||||
/// A red eye / pet eye correction: the user's ellipse (centre normalized, radii as fractions of
|
||||
/// the long edge); the pupil inside it is found automatically.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct RedEye {
|
||||
pub center: Point,
|
||||
pub rx: f64,
|
||||
pub ry: f64,
|
||||
/// 0..100 (50 = the detected pupil).
|
||||
pub pupil_size: f64,
|
||||
/// 0..100.
|
||||
pub darken: f64,
|
||||
pub pet: bool,
|
||||
/// Pet eye catchlight: offset of its centre from the pupil centre, in pupil radii.
|
||||
#[serde(default)]
|
||||
pub catchlight: Option<Point>,
|
||||
}
|
||||
|
||||
impl Default for RedEye {
|
||||
fn default() -> Self {
|
||||
Self { center: Point::new(0.5, 0.5), rx: 0.02, ry: 0.015, pupil_size: 50.0, darken: 50.0, pet: false, catchlight: None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
Reference in new issue
Block a user