diff --git a/examples/mediapipe_vision/two-hands-upscale-workflow-batch-iterator.json b/examples/mediapipe_vision/two-hands-upscale-workflow-batch-iterator.json index 619d26e..7515195 100644 --- a/examples/mediapipe_vision/two-hands-upscale-workflow-batch-iterator.json +++ b/examples/mediapipe_vision/two-hands-upscale-workflow-batch-iterator.json @@ -1,8 +1,8 @@ { "id": "00000000-0000-0000-0000-000000000000", "revision": 0, - "last_node_id": 92, - "last_link_id": 200, + "last_node_id": 93, + "last_link_id": 203, "nodes": [ { "id": 35, @@ -2090,229 +2090,6 @@ }, "widgets_values": [] }, - { - "id": 76, - "type": "VHS_LoadVideo", - "pos": [ - -650, - 2500 - ], - "size": [ - 247.455078125, - 452.2566223144531 - ], - "flags": {}, - "order": 9, - "mode": 0, - "inputs": [ - { - "name": "meta_batch", - "shape": 7, - "type": "VHS_BatchManager", - "link": null - }, - { - "name": "vae", - "shape": 7, - "type": "VAE", - "link": null - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 199 - ] - }, - { - "name": "frame_count", - "type": "INT", - "links": null - }, - { - "name": "audio", - "type": "AUDIO", - "links": null - }, - { - "name": "video_info", - "type": "VHS_VIDEOINFO", - "links": [] - } - ], - "properties": { - "cnr_id": "comfyui-videohelpersuite", - "ver": "1.6.1", - "Node name for S&R": "VHS_LoadVideo", - "widget_ue_connectable": {} - }, - "widgets_values": { - "video": "pinching.mp4", - "force_rate": 0, - "custom_width": 0, - "custom_height": 0, - "frame_load_cap": 100, - "skip_first_frames": 0, - "select_every_nth": 1, - "format": "AnimateDiff", - "choose video to upload": "image", - "videopreview": { - "hidden": false, - "paused": false, - "params": { - "filename": "pinching.mp4", - "type": "input", - "format": "video/mp4", - "force_rate": 0, - "custom_width": 0, - "custom_height": 0, - "frame_load_cap": 100, - "skip_first_frames": 0, - "select_every_nth": 1 - } - } - } - }, - { - "id": 84, - "type": "BatchResultCollector", - "pos": [ - 8981.9296875, - 2056.58984375 - ], - "size": [ - 390.5999755859375, - 174 - ], - "flags": {}, - "order": 49, - "mode": 0, - "inputs": [ - { - "name": "image", - "type": "IMAGE", - "link": 196 - }, - { - "name": "batch_info", - "type": "BATCH_INFO", - "link": 188 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": null - } - ], - "properties": { - "aux_id": "ryanontheinside/ComfyUI_RealtimeNodes", - "ver": "91017f76d80e829738853f366c2f89b7f21f8de8", - "Node name for S&R": "BatchResultCollector", - "widget_ue_connectable": {} - }, - "widgets_values": [ - true, - "batch_video/batch", - 30, - "mp4", - false - ] - }, - { - "id": 85, - "type": "BatchImageProcessor", - "pos": [ - 181.67674255371094, - 2532.96630859375 - ], - "size": [ - 378, - 102 - ], - "flags": {}, - "order": 19, - "mode": 0, - "inputs": [ - { - "name": "images", - "type": "IMAGE", - "link": 200 - } - ], - "outputs": [ - { - "name": "image", - "type": "IMAGE", - "links": [ - 195 - ] - }, - { - "name": "batch_info", - "type": "BATCH_INFO", - "links": [ - 188 - ] - } - ], - "properties": { - "aux_id": "ryanontheinside/ComfyUI_RealtimeNodes", - "ver": "91017f76d80e829738853f366c2f89b7f21f8de8", - "Node name for S&R": "BatchImageProcessor", - "widget_ue_connectable": {} - }, - "widgets_values": [ - true, - false - ] - }, - { - "id": 92, - "type": "ImageScale", - "pos": [ - -270, - 2490 - ], - "size": [ - 315, - 130 - ], - "flags": {}, - "order": 15, - "mode": 0, - "inputs": [ - { - "name": "image", - "type": "IMAGE", - "link": 199 - } - ], - "outputs": [ - { - "name": "IMAGE", - "type": "IMAGE", - "links": [ - 200 - ] - } - ], - "properties": { - "cnr_id": "comfy-core", - "ver": "0.3.34", - "widget_ue_connectable": {}, - "Node name for S&R": "ImageScale" - }, - "widgets_values": [ - "lanczos", - 512, - 512, - "center" - ] - }, { "id": 89, "type": "ImageScale", @@ -2368,7 +2145,7 @@ 124.8360366821289 ], "flags": {}, - "order": 10, + "order": 9, "mode": 0, "inputs": [], "outputs": [], @@ -2378,6 +2155,229 @@ ], "color": "#432", "bgcolor": "#653" + }, + { + "id": 84, + "type": "BatchResultCollector", + "pos": [ + 8884.26171875, + 2620.0791015625 + ], + "size": [ + 390.5999755859375, + 174 + ], + "flags": {}, + "order": 49, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 196 + }, + { + "name": "batch_info", + "type": "BATCH_INFO", + "link": 188 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": null + } + ], + "properties": { + "aux_id": "ryanontheinside/ComfyUI_RealtimeNodes", + "ver": "91017f76d80e829738853f366c2f89b7f21f8de8", + "Node name for S&R": "BatchResultCollector", + "widget_ue_connectable": {} + }, + "widgets_values": [ + true, + "batch_video/batch", + 30, + "mp4", + false + ] + }, + { + "id": 76, + "type": "VHS_LoadVideo", + "pos": [ + -933.0139770507812, + 2530.957275390625 + ], + "size": [ + 247.455078125, + 452.2566223144531 + ], + "flags": {}, + "order": 10, + "mode": 0, + "inputs": [ + { + "name": "meta_batch", + "shape": 7, + "type": "VHS_BatchManager", + "link": null + }, + { + "name": "vae", + "shape": 7, + "type": "VAE", + "link": null + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 202 + ] + }, + { + "name": "frame_count", + "type": "INT", + "links": null + }, + { + "name": "audio", + "type": "AUDIO", + "links": null + }, + { + "name": "video_info", + "type": "VHS_VIDEOINFO", + "links": [] + } + ], + "properties": { + "cnr_id": "comfyui-videohelpersuite", + "ver": "1.6.1", + "Node name for S&R": "VHS_LoadVideo", + "widget_ue_connectable": {} + }, + "widgets_values": { + "video": "pinching.mp4", + "force_rate": 0, + "custom_width": 0, + "custom_height": 0, + "frame_load_cap": 60, + "skip_first_frames": 0, + "select_every_nth": 1, + "format": "AnimateDiff", + "choose video to upload": "image", + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "pinching.mp4", + "type": "input", + "format": "video/mp4", + "force_rate": 0, + "custom_width": 0, + "custom_height": 0, + "frame_load_cap": 60, + "skip_first_frames": 0, + "select_every_nth": 1 + } + } + } + }, + { + "id": 93, + "type": "ImageScale", + "pos": [ + -484.8045349121094, + 2515.98046875 + ], + "size": [ + 315, + 130 + ], + "flags": {}, + "order": 15, + "mode": 0, + "inputs": [ + { + "name": "image", + "type": "IMAGE", + "link": 202 + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 203 + ] + } + ], + "properties": { + "cnr_id": "comfy-core", + "ver": "0.3.34", + "widget_ue_connectable": {}, + "Node name for S&R": "ImageScale" + }, + "widgets_values": [ + "nearest-exact", + 512, + 512, + "center" + ] + }, + { + "id": 85, + "type": "BatchImageProcessor", + "pos": [ + 181.67674255371094, + 2532.96630859375 + ], + "size": [ + 378, + 102 + ], + "flags": {}, + "order": 19, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 203 + } + ], + "outputs": [ + { + "name": "image", + "type": "IMAGE", + "links": [ + 195 + ] + }, + { + "name": "batch_info", + "type": "BATCH_INFO", + "links": [ + 188 + ] + } + ], + "properties": { + "aux_id": "ryanontheinside/ComfyUI_RealtimeNodes", + "ver": "91017f76d80e829738853f366c2f89b7f21f8de8", + "Node name for S&R": "BatchImageProcessor", + "widget_ue_connectable": {} + }, + "widgets_values": [ + true, + false + ] } ], "links": [ @@ -2966,16 +2966,16 @@ "IMAGE" ], [ - 199, + 202, 76, 0, - 92, + 93, 0, "IMAGE" ], [ - 200, - 92, + 203, + 93, 0, 85, 0, @@ -3052,10 +3052,10 @@ "config": {}, "extra": { "ds": { - "scale": 0.12100000000000163, + "scale": 0.12100000000000231, "offset": [ - 2342.9131356596554, - 2264.162347066301 + 1583.2325724559685, + 1701.4048752808008 ] }, "ue_links": [], diff --git a/node_wrappers/realtimenodes/media/batch_processor.py b/node_wrappers/realtimenodes/media/batch_processor.py index 87252b3..fac9f36 100644 --- a/node_wrappers/realtimenodes/media/batch_processor.py +++ b/node_wrappers/realtimenodes/media/batch_processor.py @@ -41,8 +41,6 @@ class BatchImageProcessor(ControlNodeBase): CATEGORY = "Realtime Nodes/media/batch" DESCRIPTION = "Feeds images one by one from a batch into the workflow. Must be used in conjunction with BatchResultCollector node." def update(self, images, reset, always_execute=True, unique_id=None): - print(f"[BatchImageProcessor] Unique ID: {unique_id}") - print(f"[BatchImageProcessor] Input images tensor shape: {images.shape}") # Use class static variable to persist state if unique_id not in self.__class__._global_state: @@ -62,7 +60,6 @@ class BatchImageProcessor(ControlNodeBase): # Get current image (preserving batch dimension) current_index = state["current_index"] current_image = images[current_index:current_index+1] - print(f"[BatchImageProcessor] Current image tensor shape: {current_image.shape}") # Prepare batch info is_last_frame = current_index == state["total_frames"] - 1 @@ -75,7 +72,6 @@ class BatchImageProcessor(ControlNodeBase): # Update index for next run state["current_index"] = (current_index + 1) % state["total_frames"] print(f"[BatchImageProcessor] Processing frame {current_index+1}/{state['total_frames']} (is_last_frame: {is_last_frame})") - print(f"[BatchImageProcessor] Next frame index will be: {state['current_index']}") return (current_image, batch_info) @@ -105,10 +101,7 @@ class BatchResultCollector(ControlNodeBase): OUTPUT_NODE = True DESCRIPTION = "Collects frames from a batch and saves them as a video when the last frame is received. If the last frame is not received, the node will block execution until it is received. Must be used in conjunction with BatchImageProcessor node." def update(self, image, batch_info, filename_prefix, fps, format, reset, always_execute=True, unique_id=None): - print(f"[BatchResultCollector] Unique ID: {unique_id}") - print(f"[BatchResultCollector] Received frame {batch_info.current_index+1}/{batch_info.total_frames} (is_last_frame: {batch_info.is_last_frame})") - print(f"[BatchResultCollector] Input image tensor shape: {image.shape}") - + # Create default state structure default_state = { "collected_frames": [], @@ -147,15 +140,9 @@ class BatchResultCollector(ControlNodeBase): state["current_index"] = current_index # Ensure the array is large enough if len(state["collected_frames"]) <= current_index: - print(f"[BatchResultCollector] Extending collected_frames array from {len(state['collected_frames'])} to {current_index+1}") state["collected_frames"].extend([None] * (current_index - len(state["collected_frames"]) + 1)) # Make a copy and store the frame state["collected_frames"][current_index] = image.clone().cpu() - print(f"[BatchResultCollector] Stored frame {current_index+1}/{total_frames}, shape: {state['collected_frames'][current_index].shape}") - - # Debug info for collected frames - frame_status = ["✓" if f is not None else "✗" for f in state["collected_frames"]] - print(f"[BatchResultCollector] Frame collection status: {frame_status}") # Check if all frames collected collected_count = sum(1 for frame in state["collected_frames"] if frame is not None) @@ -176,12 +163,6 @@ class BatchResultCollector(ControlNodeBase): print(f"[BatchResultCollector] Successfully saved video. Stopping auto-queue.") raise Exception("Processing complete. Disable auto-queue.") - else: - if batch_info.is_last_frame: - print(f"[BatchResultCollector] Last frame received but not all frames collected yet") - if all_frames_collected: - print(f"[BatchResultCollector] All frames collected but not receiving last frame yet") - return (image,) def _save_frames_as_images(self, frames, filename_prefix, output_dir): @@ -312,18 +293,12 @@ class BatchResultCollector(ControlNodeBase): # Generate timestamp for unique filename timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") - # Print details of the first frame for debugging - print(f"[BatchResultCollector] First frame type: {type(frames[0])}") - print(f"[BatchResultCollector] First frame tensor shape: {frames[0].shape}") - # Get video dimensions from the first frame first_frame = frames[0] if len(first_frame.shape) == 4: # Handle [B,H,W,C] format height, width = first_frame.shape[1], first_frame.shape[2] else: # Handle [H,W,C] format height, width = first_frame.shape[0], first_frame.shape[1] - - print(f"[BatchResultCollector] Video dimensions determined: {width}x{height}") # Get save path full_output_folder, filename, counter, subfolder, _ = folder_paths.get_save_image_path( @@ -357,27 +332,16 @@ class BatchResultCollector(ControlNodeBase): if not out.isOpened(): raise Exception(f"Failed to open video writer with any codec for format {format}") - print(f"[BatchResultCollector] Created video writer with dimensions {width}x{height} at {fps} fps") - # Write frames frames_encoded = 0 for i, frame_tensor in enumerate(frames): - print(f"[BatchResultCollector] Processing frame {i+1}/{len(frames)}") - print(f"[BatchResultCollector] Frame tensor shape: {frame_tensor.shape}") - # Remove batch dimension if present (B,H,W,C) -> (H,W,C) if len(frame_tensor.shape) == 4: frame_tensor = frame_tensor.squeeze(0) - print(f"[BatchResultCollector] After removing batch dimension: {frame_tensor.shape}") # Convert tensor to numpy array with values in [0-255] frame_np = torch.clamp(frame_tensor * 255, min=0, max=255).to(torch.uint8).cpu().numpy() - print(f"[BatchResultCollector] NumPy array shape: {frame_np.shape}") - # Make sure we have the expected dimensions - if frame_np.shape[0] != height or frame_np.shape[1] != width: - print(f"[BatchResultCollector] Warning: Frame dimensions don't match expected {height}x{width}") - # Convert RGB to BGR for OpenCV frame_bgr = cv2.cvtColor(frame_np, cv2.COLOR_RGB2BGR)