mirror of
https://github.com/storytold/filmcraft.git
synced 2026-10-24 10:10:05 -06:00
Merge pull request #675 from vbasky/fix/458-ultra-key-setting
Apply Ultra Key Setting as a parameter preset
This commit is contained in:
@@ -2585,15 +2585,33 @@ fn build() -> Vec<CommandSpec> {
|
||||
{
|
||||
e.params.insert(pid.clone(), filmcraft_project::Param::new(d.default.clone()));
|
||||
}
|
||||
let prm = crate::masks::target_param(e, &pq, &pid).ok_or_else(|| bad("effects.setParam", format!("no param `{pid}`")))?;
|
||||
let v = json_to_param(&prm.value, &val).ok_or_else(|| bad("effects.setParam", "value has the wrong type"))?;
|
||||
if keyframe {
|
||||
// the stopwatch and the value in one step
|
||||
prm.put_keyframe(mt, v);
|
||||
} else {
|
||||
prm.set_at(mt, v);
|
||||
let (keyframes, v) = {
|
||||
let prm = crate::masks::target_param(e, &pq, &pid).ok_or_else(|| bad("effects.setParam", format!("no param `{pid}`")))?;
|
||||
let v = json_to_param(&prm.value, &val).ok_or_else(|| bad("effects.setParam", "value has the wrong type"))?;
|
||||
let keyframes = if keyframe {
|
||||
// the stopwatch and the value in one step
|
||||
prm.put_keyframe(mt, v.clone());
|
||||
prm.keyframes.len()
|
||||
} else {
|
||||
prm.set_at(mt, v.clone());
|
||||
prm.keyframes.len()
|
||||
};
|
||||
(keyframes, v)
|
||||
};
|
||||
if e.effect == "ultra_key" && pid == "setting"
|
||||
&& let ParamValue::Choice(setting) = v
|
||||
&& let Some(rows) = filmcraft_project::effect::ultra_key_setting(setting)
|
||||
{
|
||||
for (id, value) in rows {
|
||||
if let Some(p) = e.param_mut(id) {
|
||||
p.value = ParamValue::Float(*value);
|
||||
p.keyframes.clear();
|
||||
} else {
|
||||
e.params.insert((*id).to_string(), filmcraft_project::Param::new(ParamValue::Float(*value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(prm.keyframes.len())
|
||||
Ok(keyframes)
|
||||
})?;
|
||||
// 0 keyframes: the value is static (`time` was not used)
|
||||
Ok(json!({"keyframes": keyframes}))
|
||||
|
||||
@@ -115,3 +115,16 @@ fn new_effect_params_are_settable_and_keyframable() {
|
||||
let img = s.render_program(0.125).unwrap();
|
||||
assert!(img.px.iter().all(|v| v.is_finite()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ultra_key_setting_writes_aggressive_parameters() {
|
||||
let mut s = demo();
|
||||
let clip = first_v1_clip(&s);
|
||||
s.execute("effects.apply", json!({"clips": [clip], "effect": "ultra_key"})).unwrap();
|
||||
s.execute("effects.setParam", json!({"clip": clip, "effect": "ultra_key", "param": "setting", "value": 2})).unwrap();
|
||||
let e = s.active_sequence().unwrap().find_item(filmcraft_project::ClipId(clip)).unwrap().1.effect("ultra_key").unwrap();
|
||||
assert_eq!(e.param("tolerance").unwrap().value.as_f64(), Some(90.0));
|
||||
assert_eq!(e.param("pedestal").unwrap().value.as_f64(), Some(50.0));
|
||||
assert_eq!(e.param("choke").unwrap().value.as_f64(), Some(10.0));
|
||||
assert_eq!(e.param("contrast").unwrap().value.as_f64(), Some(10.0));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::mask::Mask;
|
||||
pub use audio::{GEQ10_LABELS, GEQ20_LABELS, GEQ30_LABELS, PREMIERE_AUDIO_EFFECTS};
|
||||
|
||||
mod vfx;
|
||||
pub use vfx::{EASINGS, ECHO_OPERATORS, FRAME_LAYOUTS, LIGHT_IDS, SIMPLE_BLEND, TRACK_CHOICES, auto_point};
|
||||
pub use vfx::{EASINGS, ECHO_OPERATORS, FRAME_LAYOUTS, LIGHT_IDS, SIMPLE_BLEND, TRACK_CHOICES, auto_point, ultra_key_setting};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum EffectKind {
|
||||
|
||||
@@ -1014,6 +1014,60 @@ pub(super) fn defs() -> Vec<EffectDef> {
|
||||
v
|
||||
}
|
||||
|
||||
/// Parameter values written when Ultra Key's Setting is Default, Relaxed or Aggressive.
|
||||
/// Custom (index 3) leaves the current values. Aggressive matches the matte that cleans
|
||||
/// light and dark clothing on a typical green screen.
|
||||
pub fn ultra_key_setting(setting: u32) -> Option<&'static [(&'static str, f64)]> {
|
||||
Some(match setting {
|
||||
0 => &[
|
||||
("transparency", 45.0),
|
||||
("highlight", 10.0),
|
||||
("shadow", 50.0),
|
||||
("tolerance", 50.0),
|
||||
("pedestal", 10.0),
|
||||
("choke", 0.0),
|
||||
("soften", 0.0),
|
||||
("contrast", 0.0),
|
||||
("mid_point", 50.0),
|
||||
("desaturate", 25.0),
|
||||
("range", 50.0),
|
||||
("spill", 50.0),
|
||||
("spill_luma", 50.0),
|
||||
],
|
||||
1 => &[
|
||||
("transparency", 30.0),
|
||||
("highlight", 15.0),
|
||||
("shadow", 35.0),
|
||||
("tolerance", 70.0),
|
||||
("pedestal", 5.0),
|
||||
("choke", 0.0),
|
||||
("soften", 8.0),
|
||||
("contrast", 0.0),
|
||||
("mid_point", 50.0),
|
||||
("desaturate", 15.0),
|
||||
("range", 50.0),
|
||||
("spill", 40.0),
|
||||
("spill_luma", 50.0),
|
||||
],
|
||||
2 => &[
|
||||
("transparency", 40.0),
|
||||
("highlight", 10.0),
|
||||
("shadow", 55.0),
|
||||
("tolerance", 90.0),
|
||||
("pedestal", 50.0),
|
||||
("choke", 10.0),
|
||||
("soften", 10.0),
|
||||
("contrast", 10.0),
|
||||
("mid_point", 50.0),
|
||||
("desaturate", 50.0),
|
||||
("range", 50.0),
|
||||
("spill", 50.0),
|
||||
("spill_luma", 50.0),
|
||||
],
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Extra parameters added to core effects by the 26.x rebuilds (old instances fall back to the
|
||||
/// defaults, so projects round-trip unchanged).
|
||||
pub(super) fn extend_core(v: &mut [EffectDef]) {
|
||||
|
||||
@@ -161,17 +161,16 @@ pub fn ultra_key(img: &mut Image, e: &EffectInstance, cx: &FxCtx) {
|
||||
let k = rgb_to_ycbcr(kc[0], kc[1], kc[2], Matrix::Bt709);
|
||||
let kmag2 = (k[1] * k[1] + k[2] * k[2]).max(1e-6);
|
||||
let setting = chv(e, "setting");
|
||||
let (g_mul, ped_mul) = match setting {
|
||||
1 => (0.85, 0.7),
|
||||
2 => (1.2, 1.5),
|
||||
_ => (1.0, 1.0),
|
||||
let preset = filmcraft_project::effect::ultra_key_setting(setting);
|
||||
let pf = |id: &str, fallback: f32| {
|
||||
preset.and_then(|rows| rows.iter().find(|(k, _)| *k == id).map(|(_, v)| *v as f32)).unwrap_or(fallback)
|
||||
};
|
||||
let transparency = fv(e, "transparency", cx) / 100.0;
|
||||
let highlight = fv(e, "highlight", cx) / 100.0;
|
||||
let shadow = fv(e, "shadow", cx) / 100.0;
|
||||
let tol = 0.08 + fv(e, "tolerance", cx) / 100.0 * 0.5;
|
||||
let pedestal = (fv(e, "pedestal", cx) / 100.0 * 0.3 * ped_mul).min(0.9);
|
||||
let gain = (1.0 + 2.0 * transparency) * g_mul;
|
||||
let transparency = pf("transparency", fv(e, "transparency", cx)) / 100.0;
|
||||
let highlight = pf("highlight", fv(e, "highlight", cx)) / 100.0;
|
||||
let shadow = pf("shadow", fv(e, "shadow", cx)) / 100.0;
|
||||
let tol = 0.08 + pf("tolerance", fv(e, "tolerance", cx)) / 100.0 * 0.5;
|
||||
let pedestal = (pf("pedestal", fv(e, "pedestal", cx)) / 100.0 * 0.3).min(0.9);
|
||||
let gain = 1.0 + 2.0 * transparency;
|
||||
let output = chv(e, "output");
|
||||
let dom = if kc[1] >= kc[0] && kc[1] >= kc[2] {
|
||||
1
|
||||
@@ -199,10 +198,10 @@ pub fn ultra_key(img: &mut Image, e: &EffectInstance, cx: &FxCtx) {
|
||||
*a = ((m.clamp(0.0, 1.0) - pedestal) / (1.0 - pedestal)).clamp(0.0, 1.0);
|
||||
});
|
||||
// cleanup
|
||||
let choke = fv(e, "choke", cx) / 100.0;
|
||||
let soften = fv(e, "soften", cx) / 100.0;
|
||||
let contrast = fv(e, "contrast", cx) / 100.0;
|
||||
let mid = fv(e, "mid_point", cx) / 100.0;
|
||||
let choke = pf("choke", fv(e, "choke", cx)) / 100.0;
|
||||
let soften = pf("soften", fv(e, "soften", cx)) / 100.0;
|
||||
let contrast = pf("contrast", fv(e, "contrast", cx)) / 100.0;
|
||||
let mid = pf("mid_point", fv(e, "mid_point", cx)) / 100.0;
|
||||
if choke > 0.0 {
|
||||
let r = (choke * 4.0 * cx.px_scale).max(0.5);
|
||||
let b = blur_plane(&alpha, img.w, img.h, r);
|
||||
@@ -217,10 +216,10 @@ pub fn ultra_key(img: &mut Image, e: &EffectInstance, cx: &FxCtx) {
|
||||
alpha.par_iter_mut().for_each(|a| *a = ((*a - mid) * g + mid).clamp(0.0, 1.0));
|
||||
}
|
||||
// spill suppression + colour correction on the foreground
|
||||
let spill = fv(e, "spill", cx) / 100.0;
|
||||
let desat = fv(e, "desaturate", cx) / 100.0;
|
||||
let range = fv(e, "range", cx) / 100.0;
|
||||
let sluma = fv(e, "spill_luma", cx) / 100.0;
|
||||
let spill = pf("spill", fv(e, "spill", cx)) / 100.0;
|
||||
let desat = pf("desaturate", fv(e, "desaturate", cx)) / 100.0;
|
||||
let range = pf("range", fv(e, "range", cx)) / 100.0;
|
||||
let sluma = pf("spill_luma", fv(e, "spill_luma", cx)) / 100.0;
|
||||
let (cc_s, cc_h, cc_l) = (fv(e, "cc_saturation", cx) / 100.0, fv(e, "cc_hue", cx) / 360.0, fv(e, "cc_luminance", cx) / 100.0);
|
||||
let cc = (cc_s - 1.0).abs() > 1e-4 || cc_h.abs() > 1e-6 || (cc_l - 1.0).abs() > 1e-4;
|
||||
img.px.par_chunks_mut(4).zip(alpha.par_iter()).for_each(|(p, &a)| {
|
||||
|
||||
@@ -504,6 +504,12 @@ fn keyers_known_values() {
|
||||
set(&mut e, "output", ParamValue::Choice(1));
|
||||
let m = run("ultra_key", &e, &img, &cx());
|
||||
assert!(m.get(1, 0)[0] < 0.05 && m.get(6, 0)[0] > 0.9 && m.get(1, 0)[3] == 1.0);
|
||||
let mut aggressive = inst("ultra_key");
|
||||
set(&mut aggressive, "setting", ParamValue::Choice(2));
|
||||
let ag = run("ultra_key", &aggressive, &img, &cx());
|
||||
let def = run("ultra_key", &inst("ultra_key"), &img, &cx());
|
||||
let delta = (0..img.w * img.h).map(|i| (ag.px[i * 4 + 3] - def.px[i * 4 + 3]).abs()).fold(0.0f32, f32::max);
|
||||
assert!(delta > 1e-4, "Aggressive Setting left the Default matte unchanged");
|
||||
// Alpha Adjust: invert and opacity
|
||||
let mut a = Image::filled(2, 2, [0.1, 0.1, 0.1, 0.25]);
|
||||
let mut e = inst("alpha_adjust");
|
||||
|
||||
Reference in New Issue
Block a user