Adding fix
This commit is contained in:
+41
-14
@@ -4772,11 +4772,29 @@ def _measure_suit(ref: Path, filters: "list[dict]") -> "tuple[int, int, int]":
|
|||||||
return (sr // n, sg // n, sb // n) if n else (0, 0, 0)
|
return (sr // n, sg // n, sb // n) if n else (0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def _png_dims(path: Path) -> "tuple[int, int]":
|
||||||
|
"""Return (width, height) of an image/frame, or (1920, 1080) on failure."""
|
||||||
|
r = subprocess.run(
|
||||||
|
["ffprobe", "-v", "error", "-select_streams", "v:0",
|
||||||
|
"-show_entries", "stream=width,height", "-of", "csv=p=0:s=x", str(path)],
|
||||||
|
capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
w, h = r.stdout.strip().split("x")[:2]
|
||||||
|
return int(w), int(h)
|
||||||
|
except ValueError:
|
||||||
|
return (1920, 1080)
|
||||||
|
|
||||||
|
|
||||||
def _render_preview(ref: Path, filters: "list[dict]", out_png: Path) -> None:
|
def _render_preview(ref: Path, filters: "list[dict]", out_png: Path) -> None:
|
||||||
"""Save the filtered still composited over magenta (to judge the matte)."""
|
"""Save the filtered still composited over magenta (to judge the matte).
|
||||||
|
|
||||||
|
The magenta layer is sized to the frame so the full frame shows at any
|
||||||
|
source resolution — a fixed-size backdrop would crop the overlay."""
|
||||||
vf = _grade_video_filter(filters)
|
vf = _grade_video_filter(filters)
|
||||||
|
w, h = _png_dims(ref)
|
||||||
cmd = ["ffmpeg", "-y", "-v", "error",
|
cmd = ["ffmpeg", "-y", "-v", "error",
|
||||||
"-f", "lavfi", "-i", "color=c=magenta:s=1280x720",
|
"-f", "lavfi", "-i", f"color=c=magenta:s={w}x{h}",
|
||||||
"-i", str(ref), "-filter_complex",
|
"-i", str(ref), "-filter_complex",
|
||||||
f"[1]{vf},format=yuva444p10le[fg];"
|
f"[1]{vf},format=yuva444p10le[fg];"
|
||||||
f"[0][fg]overlay=shortest=1,format=rgb24",
|
f"[0][fg]overlay=shortest=1,format=rgb24",
|
||||||
@@ -4861,24 +4879,32 @@ def _stage_key(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def _stage_despill(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
def _stage_despill(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
||||||
"""Deterministic spill_suppress sweep 0.7–1.5 → candidates for a visual pick."""
|
"""Deterministic spill_suppress sweep 0.5–1.5 → candidates for a visual pick.
|
||||||
|
|
||||||
|
Reports both sides of the tradeoff: scalp green_cast (want ~0) AND the
|
||||||
|
costume's orange-ness (suit R−G, want low). Over-despilling clears the
|
||||||
|
scalp but pushes a yellow costume orange, so the balance point — not the
|
||||||
|
maximum — is usually right."""
|
||||||
base = _grade_step(filters, "gnommokey")
|
base = _grade_step(filters, "gnommokey")
|
||||||
values = [round(0.7 + (1.5 - 0.7) * i / 6, 2) for i in range(7)] # 0.7 .. 1.5
|
values = [round(0.5 + (1.5 - 0.5) * i / 8, 3) for i in range(9)] # 0.5 .. 1.5
|
||||||
print(" Sweeping spill_suppress 0.7 → 1.5 (yellow_protect held fixed)")
|
print(" Sweeping spill_suppress 0.5 → 1.5 (yellow_protect held fixed)")
|
||||||
|
suit_raw = _measure_suit(ref, _mask_and(filters, dict(base, spill_suppress=0)))
|
||||||
candidates = []
|
candidates = []
|
||||||
for i, v in enumerate(values, 1):
|
for i, v in enumerate(values, 1):
|
||||||
cfg = dict(base)
|
cfg = dict(base)
|
||||||
cfg["spill_suppress"] = v
|
cfg["spill_suppress"] = v
|
||||||
chain = _mask_and(filters, cfg)
|
chain = _mask_and(filters, cfg)
|
||||||
cast = _measure_green_cast(ref, chain)
|
cast = _measure_green_cast(ref, chain)
|
||||||
|
suit = _measure_suit(ref, chain)
|
||||||
png = out_dir / f"despill_{i}.png"
|
png = out_dir / f"despill_{i}.png"
|
||||||
_render_preview(ref, chain, png)
|
_render_preview(ref, chain, png)
|
||||||
candidates.append({
|
candidates.append({
|
||||||
"id": f"despill_{i}", "file": png.name,
|
"id": f"despill_{i}", "file": png.name,
|
||||||
"params": {"spill_suppress": v}, "hint": {"green_cast": cast},
|
"params": {"spill_suppress": v},
|
||||||
|
"hint": {"green_cast": cast, "suit_orange": suit[0] - suit[1]},
|
||||||
})
|
})
|
||||||
# Advisory only: the value nearest neutral green cast. Localized bald-head
|
# Advisory: nearest-neutral scalp cast — the balance point that clears the
|
||||||
# spill means the eye is the real judge, hence a full sweep to pick from.
|
# scalp without needlessly orange-ing the costume (over-despill goes magenta).
|
||||||
rec = min(candidates, key=lambda c: abs(c["hint"]["green_cast"]))["id"]
|
rec = min(candidates, key=lambda c: abs(c["hint"]["green_cast"]))["id"]
|
||||||
manifest = {
|
manifest = {
|
||||||
"stage": "despill", "target_step": "gnommokey",
|
"stage": "despill", "target_step": "gnommokey",
|
||||||
@@ -4886,15 +4912,16 @@ def _stage_despill(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
|||||||
"recommended": rec, "candidates": candidates,
|
"recommended": rec, "candidates": candidates,
|
||||||
}
|
}
|
||||||
mpath = _grade_write_manifest(out_dir, manifest)
|
mpath = _grade_write_manifest(out_dir, manifest)
|
||||||
print(f" {'id':>12} {'spill':>7} {'green_cast':>10} png")
|
print(f" raw costume R−G (target for suit_orange): {suit_raw[0] - suit_raw[1]}")
|
||||||
print(f" {'-'*12} {'-'*7} {'-'*10} {'-'*3}")
|
print(f" {'id':>12} {'spill':>6} {'scalp cast':>10} {'suit_orange':>11} png")
|
||||||
|
print(f" {'-'*12} {'-'*6} {'-'*10} {'-'*11} {'-'*3}")
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
star = " ◀ suggested" if c["id"] == rec else ""
|
star = " ◀ suggested" if c["id"] == rec else ""
|
||||||
print(f" {c['id']:>12} {c['params']['spill_suppress']:>7} "
|
print(f" {c['id']:>12} {c['params']['spill_suppress']:>6} "
|
||||||
f"{c['hint']['green_cast']:>10} {c['file']}{star}")
|
f"{c['hint']['green_cast']:>10} {c['hint']['suit_orange']:>11} {c['file']}{star}")
|
||||||
print("\n green_cast: >0 residual green · ~0 neutral · <0 over-despilled (magenta)")
|
print("\n scalp cast: >0 green · ~0 neutral · <0 magenta | suit_orange: lower = more yellow")
|
||||||
print(f" Manifest: {mpath}")
|
print(f" Manifest: {mpath}")
|
||||||
print(" Pick the one with a clean crown and no magenta skin:")
|
print(" Pick the balance — scalp near 0 without the suit going orange:")
|
||||||
print(f" gnommo -p {project_path.name} grade --pick despill_5")
|
print(f" gnommo -p {project_path.name} grade --pick despill_5")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user