diff --git a/anime_facesaver.py b/anime_facesaver.py index 23c4c74..93fdbe1 100644 --- a/anime_facesaver.py +++ b/anime_facesaver.py @@ -119,6 +119,22 @@ def parse_arguments(): "edges. 0 (default) allows cropped faces; set >0 to reject " "faces touching the crop lines.", ) + parser.add_argument( + "--probe-stride", + type=int, + default=1, + help="Within a scene still needing a face, run detection every Nth " + "frame instead of every frame (1 = original behavior, free on " + "CUDA). For slow devices (iGPU ~5s/img) use e.g. 25: decode " + "stays frame-perfect, only the expensive inference is thinned.", + ) + parser.add_argument( + "--probe-max-per-scene", + type=int, + default=4, + help="With --probe-stride>1, probes before giving up on a scene and " + "waiting for the next scene start (default 4).", + ) parser.add_argument( "--scene-threshold", type=float, @@ -272,6 +288,17 @@ def process_video(video_path, output_dir, args, model, device, draw, face_in_scene = False frame_idx = 0 + # Probe schedule. probe_stride=1 reproduces the original every-frame + # loop (right on CUDA, where predict costs ~0.2 s). On slow devices + # (iGPU ~5 s/img) predicting every frame is hopeless: decode is ~300 fps, + # inference 0.2/s. With stride>1 we decode through frames for free and + # predict only at scene starts, retrying every stride frames up to + # probe_max attempts per scene, then waiting for the next scene start. + probe_stride = getattr(args, "probe_stride", 1) + probe_max = getattr(args, "probe_max_per_scene", 4) + next_probe = 0 + probes_used = 0 + while True: ret, frame = cap.read() if not ret: @@ -279,10 +306,20 @@ def process_video(video_path, output_dir, args, model, device, draw, if frame_idx in scene_starts: face_in_scene = False - # membership test on a set; pop matched starts to keep it cheap - # (scenes strictly increase, so set stays small either way) + next_probe = frame_idx + probes_used = 0 + + if face_in_scene: + frame_idx += 1 + continue + if probe_stride > 1 and (frame_idx < next_probe + or probes_used >= probe_max): + frame_idx += 1 + continue if not face_in_scene: + probes_used += 1 + next_probe = frame_idx + probe_stride try: results = model.predict(frame, conf=args.conf, iou=args.iou, imgsz=args.imgsz,