Anamorphic source fix: pred-scale na square pixel (PAL DV 720x576 SAR 64:45 \u2192 1024x576) preden track/center/blur filter dela. Brez tega so se reels iz starih MPG-2 (npr. Global Kryner Like a Virgin) razteg-nili horizontalno.
This commit is contained in:
parent
0fe1a47295
commit
46ec0cec9c
@ -25,7 +25,12 @@ import numpy as np
|
||||
|
||||
|
||||
def get_video_info(path):
|
||||
"""Vrni dict z width, height, fps, duration."""
|
||||
"""Vrni dict z width, height, fps, duration.
|
||||
|
||||
width/height so SQUARE-PIXEL dimenzije (popravljeno za anamorphic SAR).
|
||||
Pri broadcast 720x576 SAR 64:45 (PAL DV) bo vrnil 1024x576 (square pixel).
|
||||
Pri standardnem source z SAR 1:1 ostane nespremenjeno.
|
||||
"""
|
||||
cmd = [
|
||||
"ffprobe", "-v", "quiet", "-print_format", "json",
|
||||
"-show_streams", "-show_format", str(path)
|
||||
@ -35,9 +40,38 @@ def get_video_info(path):
|
||||
fps_str = vstream["r_frame_rate"]
|
||||
num, den = fps_str.split("/")
|
||||
fps = float(num) / float(den)
|
||||
|
||||
raw_w = int(vstream["width"])
|
||||
raw_h = int(vstream["height"])
|
||||
|
||||
# SAR (sample aspect ratio) — razmerje stranic pikslja
|
||||
sar = vstream.get("sample_aspect_ratio", "1:1")
|
||||
try:
|
||||
sar_n, sar_d = sar.split(":")
|
||||
sar_n, sar_d = int(sar_n), int(sar_d)
|
||||
if sar_n == 0 or sar_d == 0:
|
||||
sar_n, sar_d = 1, 1
|
||||
except (ValueError, AttributeError):
|
||||
sar_n, sar_d = 1, 1
|
||||
|
||||
# Square-pixel dimenzije: če SAR != 1:1, popravi širino
|
||||
if sar_n != sar_d:
|
||||
sq_w = int(round(raw_w * sar_n / sar_d))
|
||||
# Zaokroži na sodo (libx264 ima rad sode dimenzije)
|
||||
sq_w = sq_w + (sq_w % 2)
|
||||
sq_h = raw_h
|
||||
print(f"📐 Anamorphic source: {raw_w}x{raw_h} SAR {sar_n}:{sar_d} → {sq_w}x{sq_h} (square pixel)", file=sys.stderr)
|
||||
else:
|
||||
sq_w = raw_w
|
||||
sq_h = raw_h
|
||||
|
||||
return {
|
||||
"width": int(vstream["width"]),
|
||||
"height": int(vstream["height"]),
|
||||
"width": sq_w,
|
||||
"height": sq_h,
|
||||
"raw_width": raw_w,
|
||||
"raw_height": raw_h,
|
||||
"sar_n": sar_n,
|
||||
"sar_d": sar_d,
|
||||
"fps": fps,
|
||||
"duration": float(data["format"]["duration"]),
|
||||
}
|
||||
@ -250,7 +284,7 @@ def build_center_filter(info, target_w, target_h):
|
||||
return f"scale={scale_w}:{scale_h},crop={target_w}:{target_h}:(in_w-{target_w})/2:0"
|
||||
|
||||
|
||||
def build_blur_filter(info, target_w, target_h):
|
||||
def build_blur_filter(info, target_w, target_h, anamorphic_prefix=""):
|
||||
"""
|
||||
9:16 platno: spodaj/zgoraj blur kopija, v sredini originalni 16:9.
|
||||
"""
|
||||
@ -258,10 +292,11 @@ def build_blur_filter(info, target_w, target_h):
|
||||
src_w = info["width"]
|
||||
src_h = info["height"]
|
||||
fg_h = int(target_w * src_h / src_w)
|
||||
pre = (anamorphic_prefix + ",") if anamorphic_prefix else ""
|
||||
return (
|
||||
f"[0:v]scale={target_w}:{target_h}:force_original_aspect_ratio=increase,"
|
||||
f"[0:v]{pre}scale={target_w}:{target_h}:force_original_aspect_ratio=increase,"
|
||||
f"crop={target_w}:{target_h},gblur=sigma=30[bg];"
|
||||
f"[0:v]scale={target_w}:{fg_h}[fg];"
|
||||
f"[0:v]{pre}scale={target_w}:{fg_h}[fg];"
|
||||
f"[bg][fg]overlay=0:(H-h)/2"
|
||||
)
|
||||
|
||||
@ -364,17 +399,29 @@ def main():
|
||||
info = get_video_info(work_input)
|
||||
print(f"📹 Vhod: {info['width']}x{info['height']} @ {info['fps']:.2f}fps, {info['duration']:.1f}s")
|
||||
|
||||
# Anamorphic correction prefix: če je SAR != 1:1, najprej scale-amo na square-pixel
|
||||
# in resetiramo SAR=1, šele potem track/crop filter dela na pravilnih dimenzijah.
|
||||
# Brez tega: 720x576 SAR 64:45 (DAR 16:9) PAL DV se pri scale na 1920 razteguje
|
||||
# nepravilno (ker filter misli da je 720 širina, dejansko prikazana širina je 1024).
|
||||
if info.get("sar_n", 1) != info.get("sar_d", 1):
|
||||
anamorphic_prefix = f"scale={info['width']}:{info['height']}:flags=lanczos,setsar=1,"
|
||||
print(f"🔧 Anamorphic prefix: {anamorphic_prefix.rstrip(',')}", file=sys.stderr)
|
||||
else:
|
||||
anamorphic_prefix = ""
|
||||
|
||||
if args.mode == "track":
|
||||
print("🔍 Detektiram obraze (OpenCV)...")
|
||||
samples, _, _, _, _ = detect_face_centers(work_input, sample_fps=5)
|
||||
n_with_face = sum(1 for _, x in samples if x is not None)
|
||||
print(f" {n_with_face}/{len(samples)} vzorcev z obrazom")
|
||||
x_at = smooth_track(samples, info["duration"], smoothing_window=4.0)
|
||||
vfilter = build_track_filter(info, x_at, args.target_width, args.target_height, info["fps"])
|
||||
vfilter = anamorphic_prefix + build_track_filter(info, x_at, args.target_width, args.target_height, info["fps"])
|
||||
elif args.mode == "center":
|
||||
vfilter = build_center_filter(info, args.target_width, args.target_height)
|
||||
vfilter = anamorphic_prefix + build_center_filter(info, args.target_width, args.target_height)
|
||||
elif args.mode == "blur":
|
||||
vfilter = build_blur_filter(info, args.target_width, args.target_height)
|
||||
# blur uporablja filter_complex z [0:v] referenco — anamorphic prefix gre v posebni veji
|
||||
vfilter = build_blur_filter(info, args.target_width, args.target_height,
|
||||
anamorphic_prefix=anamorphic_prefix.rstrip(","))
|
||||
|
||||
preset = {"fast": "veryfast", "medium": "medium", "high": "slow"}[args.quality]
|
||||
crf = {"fast": "26", "medium": "21", "high": "18"}[args.quality]
|
||||
@ -389,10 +436,6 @@ def main():
|
||||
audio_filter.append(f"afade=t=out:st={fade_start}:d={args.fade_out}")
|
||||
audio_filter_str = ",".join(audio_filter) if audio_filter else None
|
||||
|
||||
# Force kvadraten pixel (SAR=1:1) — fix za broadcast .MPG ki ima 64:45 anamorphic
|
||||
if not vfilter.endswith(",setsar=1"):
|
||||
vfilter = vfilter + ",setsar=1"
|
||||
|
||||
if args.mode == "blur":
|
||||
# blur uporablja filter_complex
|
||||
cmd = [
|
||||
|
||||
Loading…
Reference in New Issue
Block a user