CtrlK
BlogDocsLog inGet started
Tessl Logo

gamussa/reels-producer-skill

help quicky produce instagram reels and youtube shorts

94

2.12x
Quality

96%

Does it follow best practices?

Impact

87%

2.12x

Average score across 3 eval scenarios

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

detect_subjects.pyskills/reel-builder/scripts/

#!/usr/bin/env python3
"""Is there a person in this frame? Real detection, not an edge-density guess.

Uses Apple's Vision framework — built into macOS, no model download, hardware
accelerated. VNDetectHumanRectanglesRequest finds bodies; face detection runs
alongside because a body can be occluded while a face is clear (and vice versa:
a helmet defeats face detection but not body detection).

Why this exists: edge density does NOT separate an empty shot from a subject on
real footage. Measured on go-kart 4K, bare track scored 6.3-12.6 and helmet
close-ups 8.6-12.4 — the ranges overlap completely, because tarmac carries
gravel, kerbs, fences and a horizon. Vision separates the same frames cleanly:
0 detections on every empty frame, 1 at 0.65-0.75 confidence on every subject.

Scoring a frame returns 0.0 when nothing is found, otherwise the best detection
confidence, boosted when the subject sits near the centre of frame. That gives
build_cut_plan.py something real to rank in-point candidates by.

Requires: pip install pyobjc-framework-Vision pyobjc-framework-Quartz
macOS only. Without it, callers fall back to positional in-points.

Usage:
  detect_subjects.py frame.png [frame2.png ...]
  detect_subjects.py --video raw/take.mp4 --at 12.5 24.0 36.0
  detect_subjects.py --video raw/take.mp4 --scan --interval 2 --json
"""
import argparse, json, subprocess, sys, tempfile
from pathlib import Path

CENTRE_BONUS = 0.25    # how much a centred subject outranks an edge-of-frame one
MIN_CONFIDENCE = 0.30  # below this Vision is guessing


def _vision():
    try:
        import Vision                      # noqa: F401
        from Foundation import NSURL       # noqa: F401
        return True
    except ImportError:
        return False


def detections(path):
    """[(kind, confidence, centre_x, centre_y)] for one image, or []."""
    import Vision
    from Foundation import NSURL
    handler = Vision.VNImageRequestHandler.alloc().initWithURL_options_(
        NSURL.fileURLWithPath_(str(path)), {})
    found = []
    for kind, cls in (("human", Vision.VNDetectHumanRectanglesRequest),
                      ("face", Vision.VNDetectFaceRectanglesRequest)):
        req = cls.alloc().init()
        if kind == "human":
            try:
                req.setUpperBodyOnly_(False)
            except AttributeError:
                pass                        # older revisions have no such knob
        handler.performRequests_error_([req], None)
        for r in (req.results() or []):
            conf = float(r.confidence())
            if conf < MIN_CONFIDENCE:
                continue
            box = r.boundingBox()
            cx = float(box.origin.x + box.size.width / 2)
            cy = float(box.origin.y + box.size.height / 2)
            found.append((kind, conf, cx, cy))
    return found


def frame_score(dets):
    """0.0 when nothing is there; otherwise confidence, centre-weighted.

    Pure — the tests drive this directly rather than through Vision.
    """
    if not dets:
        return 0.0
    best = 0.0
    for _, conf, cx, cy in dets:
        # 1.0 dead centre, falling off toward the edges
        offset = max(abs(cx - 0.5), abs(cy - 0.5)) * 2
        centred = 1.0 - min(1.0, offset)
        best = max(best, conf * (1.0 + CENTRE_BONUS * centred))
    return round(best, 4)


def grab(video, t, dst):
    r = subprocess.run(
        ["ffmpeg", "-y", "-v", "error", "-skip_frame", "nokey",
         "-noaccurate_seek", "-ss", f"{t:.3f}", "-i", str(video),
         "-frames:v", "1", "-vf", "scale=640:-2", str(dst)],
        capture_output=True, text=True)
    return dst if r.returncode == 0 and dst.exists() else None


def score_at(video, times):
    """[(t, score, n_detections)] for each time in a video."""
    out = []
    with tempfile.TemporaryDirectory() as td:
        for i, t in enumerate(times):
            f = grab(video, t, Path(td) / f"f{i}.png")
            if not f:
                out.append((t, 0.0, 0))
                continue
            dets = detections(f)
            out.append((t, frame_score(dets), len(dets)))
    return out


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("images", nargs="*")
    ap.add_argument("--video")
    ap.add_argument("--at", nargs="*", type=float, default=[])
    ap.add_argument("--scan", action="store_true",
                    help="Score the whole video at --interval")
    ap.add_argument("--interval", type=float, default=2.0)
    ap.add_argument("--json", action="store_true")
    args = ap.parse_args()

    if not _vision():
        sys.exit("Apple Vision not available. Install it with:\n"
                 "  pip install pyobjc-framework-Vision pyobjc-framework-Quartz\n"
                 "macOS only — elsewhere, in-points stay positional.")

    if args.video:
        times = args.at
        if args.scan or not times:
            r = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
                                "format=duration", "-of", "csv=p=0", args.video],
                               capture_output=True, text=True)
            dur = float(r.stdout.strip() or 0)
            times = [round(t, 2) for t in
                     _frange(0.0, max(0.0, dur - 0.2), args.interval)]
        rows = score_at(args.video, times)
        if args.json:
            print(json.dumps({"video": args.video,
                              "frames": [{"t": t, "score": s, "detections": n}
                                         for t, s, n in rows]}, indent=2))
        else:
            for t, s, n in rows:
                mark = "subject" if s > 0 else "  empty"
                print(f"  {t:7.2f}s  {mark}  score={s:.2f}  detections={n}")
            hits = sum(1 for _, s, _ in rows if s > 0)
            print(f"\n{hits}/{len(rows)} frames contain a person.")
        return

    if not args.images:
        sys.exit("Pass image paths, or --video with --at/--scan.")
    for p in args.images:
        dets = detections(p)
        s = frame_score(dets)
        print(f"  {Path(p).name:24} score={s:.2f}  "
              f"{', '.join(f'{k}@{c:.2f}' for k, c, _, _ in dets) or 'nothing'}")


def _frange(start, stop, step):
    t = start
    while t <= stop:
        yield t
        t += step


if __name__ == "__main__":
    main()

.mcp.json

tile.json