diff --git a/src/keep_processor.py b/src/keep_processor.py index 288bfb4..b88df32 100644 --- a/src/keep_processor.py +++ b/src/keep_processor.py @@ -1342,6 +1342,20 @@ class KEEPFaceProcessor: or np.isnan(avg_landmarks_sequence[i]).any() ): final_pasted_frame = self._run_upscaler(self.bg_upscale_model, original_frame_bgr) + # Round 11 (2026-10-31): the fix-1.1 snippet's raw + # upscaler output cannot join the IMAGE batch when + # frame x model.scale != canvas x factor (the same + # disclosed deviation as the multiface + # short-circuit's resize): the output size is the + # paste canvas, reached with the same LANCZOS4 + # resize the vendored paste applied to its no-face + # frames. + canvas_h, canvas_w = paste_mod.paste_canvas_hw( + original_frame_bgr.shape[:2], final_upscale_factor, canvas_hw) + if final_pasted_frame.shape[0] != canvas_h or final_pasted_frame.shape[1] != canvas_w: + final_pasted_frame = cv2.resize( + final_pasted_frame, (canvas_w, canvas_h), + interpolation=cv2.INTER_LANCZOS4) output_frames_cv2.append(final_pasted_frame) # the same bookkeeping as the sibling fallback rows: # the device-output tails index the fallback rows by diff --git a/tests/test_keep_processor.py b/tests/test_keep_processor.py index 939fd8a..3362775 100644 --- a/tests/test_keep_processor.py +++ b/tests/test_keep_processor.py @@ -495,6 +495,12 @@ def test_every_console_write_of_the_facelib_reaches_the_logger( patched to ``[None, valid]``) so the paste-back print keeps firing; the stub's paste return is canvas-sized (the background-only row and the vendored row must stack). + + Round 11 (2026-10-31): the short-circuit row is canvas-sized since the + short-circuit resize (the fix-1.1 snippet's raw output cannot join the + IMAGE batch at a mismatched shape), so the stub's return is the real + 512x512 canvas the vendored paste assembles from ``input_img`` - the + pin's routing assertions are unchanged. """ real_estimate = processor_module.estimate_affines @@ -513,7 +519,7 @@ def test_every_console_write_of_the_facelib_reaches_the_logger( # row and the vendored row stack; the print stays (it is the routing probe) def canvas_sized_paste(*args, **kwargs): print("Warning: Mismatch between restored_faces and inverse_affine_matrices.") - return np.zeros((16, 16, 3), dtype=np.uint8) + return np.zeros((512, 512, 3), dtype=np.uint8) face_helper.paste_faces_to_input_image = canvas_sized_paste frames = torch.rand((2, 16, 16, 3), dtype=torch.float32)