Merge remote-tracking branch 'upstream/master' into sam3d_body

This commit is contained in:
kijai 2026-06-11 16:52:51 +03:00
commit dfdbed431f
2 changed files with 18 additions and 4 deletions

View File

@ -9,6 +9,7 @@ from PIL import Image
from typing_extensions import override from typing_extensions import override
import folder_paths import folder_paths
from comfy.utils import common_upscale
from comfy_api.latest import IO, ComfyExtension, Input from comfy_api.latest import IO, ComfyExtension, Input
from comfy_api_nodes.apis.openai import ( from comfy_api_nodes.apis.openai import (
InputFileContent, InputFileContent,
@ -62,7 +63,8 @@ async def validate_and_cast_response(response, timeout: int = None) -> torch.Ten
timeout: Request timeout in seconds. Defaults to None (no timeout). timeout: Request timeout in seconds. Defaults to None (no timeout).
Returns: Returns:
A torch.Tensor representing the image (1, H, W, C). A torch.Tensor of shape (N, H, W, C) with all returned images; images whose
dimensions differ from the first image's are resized to match it.
Raises: Raises:
ValueError: If the response is not valid. ValueError: If the response is not valid.
@ -89,6 +91,14 @@ async def validate_and_cast_response(response, timeout: int = None) -> torch.Ten
arr = np.asarray(pil_img).astype(np.float32) / 255.0 arr = np.asarray(pil_img).astype(np.float32) / 255.0
image_tensors.append(torch.from_numpy(arr)) image_tensors.append(torch.from_numpy(arr))
# With size="auto" the API can return images whose dimensions differ by a few pixels within a single response
# resize them to the first image's dimensions so they can be stacked into one batch.
ref_h, ref_w = image_tensors[0].shape[:2]
for i, t in enumerate(image_tensors):
if t.shape[:2] != (ref_h, ref_w):
samples = t.unsqueeze(0).movedim(-1, 1)
samples = common_upscale(samples, ref_w, ref_h, "bilinear", "center")
image_tensors[i] = samples.movedim(1, -1).squeeze(0)
return torch.stack(image_tensors, dim=0) return torch.stack(image_tensors, dim=0)

View File

@ -267,7 +267,8 @@ class SCAIL2ColoredMask(io.ComfyNode):
io.Combo.Input("sort_by", options=["none", "left_to_right", "area"], default="left_to_right", io.Combo.Input("sort_by", options=["none", "left_to_right", "area"], default="left_to_right",
tooltip="Order in which palette colors are assigned to the tracked objects (applied to both reference and pose video so each identity keeps the same color). left_to_right = leftmost object (by first-frame centroid) gets the first color; area = biggest object (by first-frame mask area) gets the first color; none = keep SAM3's order."), tooltip="Order in which palette colors are assigned to the tracked objects (applied to both reference and pose video so each identity keeps the same color). left_to_right = leftmost object (by first-frame centroid) gets the first color; area = biggest object (by first-frame mask area) gets the first color; none = keep SAM3's order."),
io.Boolean.Input("replacement_mode", default=False, io.Boolean.Input("replacement_mode", default=False,
tooltip="False = mask_video has black bg (Animation Mode). True = white bg (Replacement Mode). Set the matching replacement_mode on WanSCAILToVideo. reference_image_mask is always black-bg regardless."), tooltip="False = Animation Mode (pose_video_mask has black background, reference_image_mask has white background). "
"True = Replacement Mode (pose_video_mask has white background, reference_image_mask has black background)."),
], ],
outputs=[ outputs=[
io.Image.Output("pose_video_mask"), io.Image.Output("pose_video_mask"),
@ -296,14 +297,17 @@ class SCAIL2ColoredMask(io.ComfyNode):
return td return td
drv = _prep(driving_track_data) drv = _prep(driving_track_data)
# Animation: driving=black, ref=white. Replacement: driving=white, ref=black.
mask_video = _render_colored_masks(drv, "white" if replacement_mode else "black") mask_video = _render_colored_masks(drv, "white" if replacement_mode else "black")
ref_bg = "black" if replacement_mode else "white"
if ref_track_data is not None: if ref_track_data is not None:
ref = _prep(ref_track_data) ref = _prep(ref_track_data)
reference_image_mask = _render_colored_masks(ref, "black") reference_image_mask = _render_colored_masks(ref, ref_bg)
else: else:
H, W = drv["orig_size"] H, W = drv["orig_size"]
reference_image_mask = torch.zeros(1, H, W, 3, device=comfy.model_management.intermediate_device(), dtype=comfy.model_management.intermediate_dtype()) fill_value = 1.0 if ref_bg == "white" else 0.0
reference_image_mask = torch.full((1, H, W, 3), fill_value, device=comfy.model_management.intermediate_device(), dtype=comfy.model_management.intermediate_dtype())
return io.NodeOutput(mask_video, reference_image_mask) return io.NodeOutput(mask_video, reference_image_mask)