# -*- coding: utf-8 -*-
"""動画 → 代表フレーム抽出（静止画化）。BytePlusの「fps=1が最適」知見に沿う。
使い方:
  python common/extract_frames.py            # data/videos/*.MOV を全処理
  - Track B(静止画1枚): 中央付近の鮮明な1フレームを data/frames/<name>.jpg に
  - Track A(数枚): fps=1相当で数枚を data/frames/<name>_f00.jpg ... に
鮮明さは Laplacian分散(ピンぼけ指標)で選ぶ。白飛び率も記録(撮影品質ゲート用)。
"""
import cv2, os, glob, json, numpy as np

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
VID_DIR = os.path.join(ROOT, "data", "videos")
OUT_DIR = os.path.join(ROOT, "data", "frames")

def sharpness(img):
    return float(cv2.Laplacian(cv2.cvtColor(img, cv2.COLOR_BGR2GRAY), cv2.CV_64F).var())

def blowout_ratio(img):
    """白飛び率（高輝度ピクセル比）= 湯気/露出オーバーの代理指標。"""
    g = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    return float((g > 245).mean())

def extract(video_path, n_multi=3):
    cap = cv2.VideoCapture(video_path)
    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) or 0
    fps = cap.get(cv2.CAP_PROP_FPS) or 30
    if total <= 0:
        cap.release(); return None
    # fps=1相当のサンプリング位置
    step = max(1, int(round(fps)))
    cand = list(range(0, total, step)) or [total // 2]
    frames = []
    for f in cand:
        cap.set(cv2.CAP_PROP_POS_FRAMES, f)
        ok, img = cap.read()
        if ok: frames.append((f, img, sharpness(img), blowout_ratio(img)))
    cap.release()
    if not frames: return None
    # 中央付近かつ鮮明なフレームをTrack B用代表に
    mid = total / 2
    best = max(frames, key=lambda t: t[2] - 0.001*abs(t[0]-mid))
    # Track A用: 等間隔n枚
    multi = [frames[i] for i in np.linspace(0, len(frames)-1, min(n_multi, len(frames))).astype(int)]
    return {"best": best, "multi": multi, "n_sampled": len(frames)}

def run():
    os.makedirs(OUT_DIR, exist_ok=True)
    vids = glob.glob(os.path.join(VID_DIR, "*.*"))
    meta = {}
    for v in vids:
        name = os.path.splitext(os.path.basename(v))[0]
        r = extract(v)
        if not r:
            print("SKIP(読めない):", name); continue
        f, img, sh, bo = r["best"]
        cv2.imwrite(os.path.join(OUT_DIR, f"{name}.jpg"), img)
        for i, (ff, im, s, b) in enumerate(r["multi"]):
            cv2.imwrite(os.path.join(OUT_DIR, f"{name}_f{i:02d}.jpg"), im)
        meta[name] = {"sharpness": round(sh,1), "blowout": round(bo,3),
                       "n_sampled": r["n_sampled"]}
        print(f"OK {name}: sharp={sh:.0f} blowout={bo:.1%}")
    json.dump(meta, open(os.path.join(OUT_DIR, "_frame_meta.json"), "w", encoding="utf-8"),
              ensure_ascii=False, indent=2)
    print(f"\n{len(meta)}本処理 → {OUT_DIR}")

if __name__ == "__main__":
    run()
