Adding fixes to the grading
This commit is contained in:
+67
-45
@@ -81,7 +81,7 @@ Examples:
|
|||||||
gnommo -p video1 grade --set screen_gain=200 Preview with a gnommokey override
|
gnommo -p video1 grade --set screen_gain=200 Preview with a gnommokey override
|
||||||
gnommo -p video1 grade --stage key Auto-tune the matte key → candidate + manifest (then --pick key_1)
|
gnommo -p video1 grade --stage key Auto-tune the matte key → candidate + manifest (then --pick key_1)
|
||||||
gnommo -p video1 grade --stage despill Sweep spill_suppress 0.7–1.5 → stills to pick from
|
gnommo -p video1 grade --stage despill Sweep spill_suppress 0.7–1.5 → stills to pick from
|
||||||
gnommo -p video1 grade --stage grade Sweep paleness → color-grade stills to pick from
|
gnommo -p video1 grade --stage grade Sweep centered grade (5=camera vibrance, <5 paler, >5 more saturated)
|
||||||
gnommo -p video1 grade --pick despill_5 Apply a chosen candidate to project.json
|
gnommo -p video1 grade --pick despill_5 Apply a chosen candidate to project.json
|
||||||
gnommo -p video1 description Generate YouTube description file
|
gnommo -p video1 description Generate YouTube description file
|
||||||
gnommo -p video1 archive Copy project to connected external drive
|
gnommo -p video1 archive Copy project to connected external drive
|
||||||
@@ -1465,28 +1465,38 @@ def _import_narration_segments(narration_dir: Path, config, verbose: bool) -> No
|
|||||||
# so the project can be rendered from raw before the preprocess stage runs.
|
# so the project can be rendered from raw before the preprocess stage runs.
|
||||||
migrated_count = 0
|
migrated_count = 0
|
||||||
for seg_id, entry in existing_narration.items():
|
for seg_id, entry in existing_narration.items():
|
||||||
|
changed = False
|
||||||
|
|
||||||
|
# Fold the legacy "output_file" key into "processed_file".
|
||||||
|
if "output_file" in entry:
|
||||||
|
entry.setdefault("processed_file", entry.pop("output_file"))
|
||||||
|
changed = True
|
||||||
|
|
||||||
|
# Older runs stored the processed output in source_file itself.
|
||||||
src = entry.get("source_file", "")
|
src = entry.get("source_file", "")
|
||||||
if not (src.startswith("processed/") or "_processed." in src):
|
if src.startswith("processed/") or "_processed." in src:
|
||||||
continue
|
# Preserve the processed path under processed_file (don't clobber an
|
||||||
# Preserve the processed path under processed_file (don't clobber an
|
# explicit one the user already set).
|
||||||
# explicit one the user already set).
|
entry.setdefault("processed_file", src)
|
||||||
entry.setdefault("processed_file", src)
|
# Repoint source_file at the raw recording when we can find it.
|
||||||
# Repoint source_file at the raw recording when we can find it.
|
raw_match = next(
|
||||||
raw_match = next(
|
(
|
||||||
(
|
f
|
||||||
f
|
for f in (raw_dir.iterdir() if raw_dir.exists() else [])
|
||||||
for f in (raw_dir.iterdir() if raw_dir.exists() else [])
|
if f.is_file()
|
||||||
if f.is_file()
|
and f.suffix.lower() in _raw_video_exts_set
|
||||||
and f.suffix.lower() in _raw_video_exts_set
|
and f.stem.lower() == seg_id.lower()
|
||||||
and f.stem.lower() == seg_id.lower()
|
),
|
||||||
),
|
None,
|
||||||
None,
|
)
|
||||||
)
|
if raw_match:
|
||||||
if raw_match:
|
entry["source_file"] = f"raw_mov/{raw_match.name}"
|
||||||
entry["source_file"] = f"raw_mov/{raw_match.name}"
|
changed = True
|
||||||
|
# else: no raw available — leave source_file as the processed file so
|
||||||
|
# the segment still renders; it just can't be re-preprocessed from raw.
|
||||||
|
|
||||||
|
if changed:
|
||||||
migrated_count += 1
|
migrated_count += 1
|
||||||
# else: no raw available — leave source_file as the processed file so the
|
|
||||||
# segment still renders; it simply can't be re-preprocessed from raw.
|
|
||||||
|
|
||||||
default_filters = config.default_filters if config else {}
|
default_filters = config.default_filters if config else {}
|
||||||
added_count = 0
|
added_count = 0
|
||||||
@@ -4992,27 +5002,35 @@ def _stage_despill(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
|||||||
|
|
||||||
|
|
||||||
def _stage_grade(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
def _stage_grade(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
||||||
"""Deterministic look × yellow-tint grid → punchy color_grade candidates.
|
"""Deterministic centered-grade × yellow-tint grid → color_grade candidates.
|
||||||
|
|
||||||
Two axes: 'look' ramps the auto-levels/vividness punch (flat → strong), and
|
Two axes. 'grade' is a centered 1–9 dial: 5 keeps the camera's native
|
||||||
'yellow_tint' hue-selectively pulls the yellow costume back from the orange
|
vibrance as-shot (no saturation/contrast/punch change), below 5 is paler
|
||||||
that punch introduces (negative = greener/preserve, 0 = as-is). Skin (reds)
|
than default (desaturated, flatter), above 5 is more saturated with a
|
||||||
is untouched by the tint, so you can crank vibrance and keep the suit yellow."""
|
growing auto-levels punch. 'yellow_tint' hue-selectively pulls the yellow
|
||||||
|
costume back from the orange that punch introduces (negative = greener/
|
||||||
|
preserve, 0 = as-is). Skin (reds) is untouched by the tint, so you can crank
|
||||||
|
vibrance and keep the suit yellow."""
|
||||||
key = _grade_step(filters, "gnommokey")
|
key = _grade_step(filters, "gnommokey")
|
||||||
looks = [0.4, 0.7, 1.0]
|
grades = [1, 3, 5, 7, 9] # centered on 5 = camera vibrance
|
||||||
tints = [0.0, -0.4, -0.8] # 0 = as-is, negative = pull yellows back from orange
|
tints = [0.0, -0.4, -0.8] # 0 = as-is, negative = pull yellows back from orange
|
||||||
print(" Grid: look {0.4, 0.7, 1.0} × yellow_tint {0.0, -0.4, -0.8}")
|
print(" Grid: grade {1, 3, 5, 7, 9} (5 = camera vibrance) × yellow_tint {0.0, -0.4, -0.8}")
|
||||||
candidates = []
|
candidates = []
|
||||||
|
recommended = None
|
||||||
i = 0
|
i = 0
|
||||||
for p in looks:
|
for g in grades:
|
||||||
|
# d ∈ [-1, +1] around the centered midpoint (grade 5 → d = 0).
|
||||||
|
d = (g - 5) / 4.0
|
||||||
for yt in tints:
|
for yt in tints:
|
||||||
i += 1
|
i += 1
|
||||||
cg = {
|
cg = {
|
||||||
"type": "color_grade",
|
"type": "color_grade",
|
||||||
"auto_levels": round(p, 3),
|
# Punch only ramps ABOVE center; at/below 5 there is none.
|
||||||
"saturation": round(1.0 + 0.5 * p, 3),
|
"auto_levels": round(0.4 * max(0.0, d), 3),
|
||||||
"contrast": round(1.0 + 0.15 * p, 3),
|
# Symmetric around 1.0: <5 paler, 5 as-shot, >5 more saturated.
|
||||||
"brightness": round(0.02 * p, 3),
|
"saturation": round(1.0 + 0.25 * d, 3),
|
||||||
|
"contrast": round(1.0 + 0.08 * d, 3),
|
||||||
|
"brightness": round(0.02 * max(0.0, d), 3),
|
||||||
"yellow_tint": yt,
|
"yellow_tint": yt,
|
||||||
}
|
}
|
||||||
chain = _mask_and(filters, key, cg)
|
chain = _mask_and(filters, key, cg)
|
||||||
@@ -5020,28 +5038,32 @@ def _stage_grade(project_path, filters, ref, out_dir, source_name, ss) -> int:
|
|||||||
png = out_dir / f"grade_{i}.png"
|
png = out_dir / f"grade_{i}.png"
|
||||||
_render_preview(ref, chain, png)
|
_render_preview(ref, chain, png)
|
||||||
params = {k: cg[k] for k in ("auto_levels", "saturation", "contrast", "brightness", "yellow_tint")}
|
params = {k: cg[k] for k in ("auto_levels", "saturation", "contrast", "brightness", "yellow_tint")}
|
||||||
params["look"] = p
|
params["grade"] = g
|
||||||
candidates.append({
|
candidates.append({
|
||||||
"id": f"grade_{i}", "file": png.name, "params": params,
|
"id": f"grade_{i}", "file": png.name, "params": params,
|
||||||
"hint": {"suit_rgb": list(suit), "orange": suit[0] - suit[1]},
|
"hint": {"suit_rgb": list(suit), "orange": suit[0] - suit[1]},
|
||||||
})
|
})
|
||||||
|
# Default suggestion: the centered, true-to-camera look (grade 5, no tint).
|
||||||
|
if g == 5 and yt == 0.0:
|
||||||
|
recommended = f"grade_{i}"
|
||||||
manifest = {
|
manifest = {
|
||||||
"stage": "grade", "target_step": "color_grade",
|
"stage": "grade", "target_step": "color_grade",
|
||||||
"source": source_name, "ss": round(ss, 2), "swept": ["look", "yellow_tint"],
|
"source": source_name, "ss": round(ss, 2), "swept": ["grade", "yellow_tint"],
|
||||||
"recommended": "grade_8", "candidates": candidates, # look 1.0, tint -0.4
|
"recommended": recommended, "candidates": candidates, # grade 5 = camera vibrance
|
||||||
}
|
}
|
||||||
mpath = _grade_write_manifest(out_dir, manifest)
|
mpath = _grade_write_manifest(out_dir, manifest)
|
||||||
rec = manifest["recommended"]
|
rec = manifest["recommended"]
|
||||||
print(f" {'id':>10} {'look':>5} {'tint':>5} {'suit RGB':>17} {'orange':>6} png")
|
print(f" {'id':>10} {'grade':>5} {'tint':>5} {'suit RGB':>17} {'orange':>6} png")
|
||||||
print(f" {'-'*10} {'-'*5} {'-'*5} {'-'*17} {'-'*6} {'-'*3}")
|
print(f" {'-'*10} {'-'*5} {'-'*5} {'-'*17} {'-'*6} {'-'*3}")
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
star = " ◀ suggested" if c["id"] == rec else ""
|
star = " ◀ suggested (camera vibrance)" if c["id"] == rec else ""
|
||||||
print(f" {c['id']:>10} {c['params']['look']:>5} {c['params']['yellow_tint']:>5} "
|
print(f" {c['id']:>10} {c['params']['grade']:>5} {c['params']['yellow_tint']:>5} "
|
||||||
f"{str(tuple(c['hint']['suit_rgb'])):>17} {c['hint']['orange']:>6} {c['file']}{star}")
|
f"{str(tuple(c['hint']['suit_rgb'])):>17} {c['hint']['orange']:>6} {c['file']}{star}")
|
||||||
print("\n orange = suit R−G: lower is more yellow, higher is more orange")
|
print("\n grade: 5 = camera vibrance, <5 paler than default, >5 more saturated")
|
||||||
|
print(" orange = suit R−G: lower is more yellow, higher is more orange")
|
||||||
print(f" Manifest: {mpath}")
|
print(f" Manifest: {mpath}")
|
||||||
print(" Pick punch (look) + a tint that keeps the suit yellow:")
|
print(" Pick a grade (paler <5 / punchier >5) + a tint that keeps the suit yellow:")
|
||||||
print(f" gnommo -p {project_path.name} grade --pick grade_8")
|
print(f" gnommo -p {project_path.name} grade --pick {rec}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -5068,9 +5090,9 @@ def _grade_pick(project_path, stage_hint, pick) -> int:
|
|||||||
if cand is None:
|
if cand is None:
|
||||||
print(f" ERROR: candidate '{cand_id}' not in {mpath.name}.")
|
print(f" ERROR: candidate '{cand_id}' not in {mpath.name}.")
|
||||||
return 1
|
return 1
|
||||||
# 'look'/'paleness'/'aggressiveness' are UI-only dials, not real fields.
|
# 'grade'/'look'/'paleness'/'aggressiveness' are UI-only dials, not real fields.
|
||||||
params = {k: v for k, v in cand["params"].items()
|
params = {k: v for k, v in cand["params"].items()
|
||||||
if k not in ("look", "paleness", "aggressiveness")}
|
if k not in ("grade", "look", "paleness", "aggressiveness")}
|
||||||
_apply_candidate_to_project(project_path, manifest["target_step"], params)
|
_apply_candidate_to_project(project_path, manifest["target_step"], params)
|
||||||
print(f" Applied {cand_id} → project.json ({manifest['target_step']}): "
|
print(f" Applied {cand_id} → project.json ({manifest['target_step']}): "
|
||||||
+ ", ".join(f"{k}={v}" for k, v in params.items()))
|
+ ", ".join(f"{k}={v}" for k, v in params.items()))
|
||||||
|
|||||||
Executable
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
|
||||||
|
./gnommo.sh -p video0 grade --stage key
|
||||||
|
./gnommo.sh -p video1 grade --stage key
|
||||||
|
./gnommo.sh -p video2 grade --stage key
|
||||||
|
./gnommo.sh -p video3 grade --stage key
|
||||||
|
./gnommo.sh -p video4 grade --stage key
|
||||||
|
./gnommo.sh -p video5 grade --stage key
|
||||||
|
./gnommo.sh -p video6 grade --stage key
|
||||||
|
|
||||||
|
|
||||||
|
./gnommo.sh -p video0 grade --pick key_5
|
||||||
|
./gnommo.sh -p video1 grade --pick key_5
|
||||||
|
./gnommo.sh -p video2 grade --pick key_5
|
||||||
|
./gnommo.sh -p video3 grade --pick key_5
|
||||||
|
./gnommo.sh -p video4 grade --pick key_5
|
||||||
|
./gnommo.sh -p video5 grade --pick key_5
|
||||||
|
./gnommo.sh -p video6 grade --pick key_5
|
||||||
|
|
||||||
|
./gnommo.sh -p video0 grade --stage grade
|
||||||
|
./gnommo.sh -p video1 grade --stage grade
|
||||||
|
./gnommo.sh -p video2 grade --stage grade
|
||||||
|
./gnommo.sh -p video3 grade --stage grade
|
||||||
|
./gnommo.sh -p video4 grade --stage grade
|
||||||
|
./gnommo.sh -p video5 grade --stage grade
|
||||||
|
./gnommo.sh -p video6 grade --stage grade
|
||||||
|
|
||||||
|
./gnommo.sh -p video0 grade --pick grade_5
|
||||||
|
./gnommo.sh -p video1 grade --pick grade_5
|
||||||
|
./gnommo.sh -p video2 grade --pick grade_5
|
||||||
|
./gnommo.sh -p video3 grade --pick grade_5
|
||||||
|
./gnommo.sh -p video4 grade --pick grade_5
|
||||||
|
./gnommo.sh -p video5 grade --pick grade_5
|
||||||
|
./gnommo.sh -p video6 grade --pick grade_5
|
||||||
Reference in New Issue
Block a user