From 1069f6324d88a40a33f8ce76046830bc96e3f8e1 Mon Sep 17 00:00:00 2001 From: limbicnation Date: Mon, 20 Apr 2026 06:45:32 +0200 Subject: [PATCH] fix: code review fixes - enumerate bug, dead params, output type, unused imports - Fix .index() on numpy arrays in v3 individual face loop -> enumerate() - Remove dead classifier_type param from detect_faces() (cascade already selected via CascadeCache) - Change v1 RETURN_TYPES for face_metadata from IMAGE to FLOAT (is [B,6] bbox data, not image) - Improve v3 face_metadata tooltip: NOT an image warning - Remove unused import torch.nn.functional as F - Remove unused import StringInput - Replace _tensor_to_numpy() with _get_batch_dims() - removed dead img_np return --- face_detection_node.py | 47 ++++++++++++------------------------------ 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/face_detection_node.py b/face_detection_node.py index 9248484..a962772 100644 --- a/face_detection_node.py +++ b/face_detection_node.py @@ -36,7 +36,6 @@ from typing import ClassVar, Optional import cv2 import numpy as np import torch -import torch.nn.functional as F logger = logging.getLogger(__name__) _log_level = os.getenv("COMFYUI_FACE_DETECTION_LOG_LEVEL", "INFO").upper() @@ -49,7 +48,7 @@ try: from comfy_api.v0_0_3_io import ( ComfyNode, Schema, InputBehavior, NumberDisplay, IntegerInput, FloatInput, ImageInput, ImageOutput, - ComboInput, StringInput, NodeOutput, + ComboInput, NodeOutput, ) COMFY_V3: bool = True except ImportError: @@ -171,7 +170,6 @@ def detect_faces( min_face_size: int = 64, detection_threshold: float = 0.8, auto_padding_ratio: float = 0.35, - classifier_type: str = "default", detect_all: bool = False, ) -> list[tuple[int, int, int, int]]: """ @@ -180,11 +178,10 @@ def detect_faces( Args: image_np: RGB image as uint8 numpy array (H, W, 3) - cascade: OpenCV CascadeClassifier instance + cascade: OpenCV CascadeClassifier instance (selected via CascadeCache.get() before calling) min_face_size: Minimum face dimension in pixels detection_threshold: Scale factor for detectMultiScale (≈ confidence) auto_padding_ratio: Padding as fraction of face size (0.35 = 35% face-size padding) - classifier_type: "default" or "alternative" detect_all: If True, return ALL faces. If False, return only the largest. """ gray = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY) @@ -355,7 +352,7 @@ if COMFY_V3: tooltip="Batch of cropped face tensors [B, H, W, C]"), ImageOutput("face_metadata", display_name="Face Metadata (BBoxes)", - tooltip="Face bounding box coordinates per batch item [B, 6] — x, y, w, h, score, detected"), + tooltip="Per-face [x, y, w, h, score, detected] normalized to image dims. Shape: [B, 6]. NOT an image — use for downstream bbox logic only."), ], is_output_node=True, ) @@ -384,29 +381,13 @@ if COMFY_V3: return _DEFAULT_FACE_OUTPUT_FORMAT @classmethod - def _tensor_to_numpy(cls, image: torch.Tensor) -> tuple[np.ndarray, int, int, int]: - """Convert ComfyUI tensor [B,H,W,C] float [0,1] → numpy [H,W,C] uint8 RGB.""" - if image.is_cuda: - image = image.cpu() + def _get_batch_dims(cls, image: torch.Tensor) -> tuple[int, int, int]: + """Get (B, H, W) from a ComfyUI image tensor, handling 3D→4D.""" if image.dim() == 3: - image = image.unsqueeze(0) - B, H, W, C = image.shape - - # Normalize - if image.max() <= 1.0: - image = (image * 255).round() - image = image.clip(0, 255).to(torch.uint8) - - # Take first item — full batch handled in execute() - img_np = image[0].numpy() - # Ensure RGB - if C == 1: - img_np = np.repeat(img_np, 3, axis=2) - elif C == 4: - img_np = img_np[:, :, :3] - elif C > 4: - img_np = img_np[:, :, :3] - return img_np, B, H, W + B, H, W = 1, image.shape[0], image.shape[1] + else: + B, H, W = image.shape[:3] + return B, H, W @classmethod async def execute( @@ -452,7 +433,7 @@ if COMFY_V3: temporal = cls._get_temporal(instance_id, temporal_smoothing) smoothing_active = temporal_smoothing > 0 - img_np, B, H, W = cls._tensor_to_numpy(image) + B, H, W = cls._get_batch_dims(image) # Process entire batch cropped_list: list[torch.Tensor] = [] @@ -485,7 +466,6 @@ if COMFY_V3: min_face_size=min_face_size, detection_threshold=detection_threshold, auto_padding_ratio=pad_ratio, - classifier_type=classifier_type, detect_all=detect_all, ) @@ -523,7 +503,7 @@ if COMFY_V3: # Handle face_output_format if detect_all and len(frame_crops) > 1 and resolved_format == "individual": # Individual: each face as separate batch item - for crop_np in frame_crops: + for idx, crop_np in enumerate(frame_crops): crop_t = ( torch.from_numpy(crop_np) .float() @@ -532,7 +512,7 @@ if COMFY_V3: ) cropped_list.append(crop_t) # Metadata for each face - bx, by, bw, bh = smoothed_bboxes[frame_crops.index(crop_np)] if crop_np in frame_crops else (0, 0, 0, 0) + bx, by, bw, bh = smoothed_bboxes[idx] norm_meta = torch.tensor([[ bx / max(W, 1), by / max(H, 1), bw / max(W, 1), bh / max(H, 1), @@ -668,7 +648,7 @@ class FaceDetectionNodeV1: }, } - RETURN_TYPES = ("IMAGE", "IMAGE") + RETURN_TYPES = ("IMAGE", "FLOAT") RETURN_NAMES = ("cropped_faces", "face_metadata") FUNCTION = "detect_and_crop_faces" CATEGORY = "image/processing" @@ -775,7 +755,6 @@ class FaceDetectionNodeV1: min_face_size=min_face_size, detection_threshold=detection_threshold, auto_padding_ratio=pad_ratio, - classifier_type=classifier_type, detect_all=detect_all, )