mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-06-28 02:39:26 +08:00
Merge branch 'master' into master
This commit is contained in:
commit
9d71cf5342
@ -174,7 +174,7 @@ class Ideogram4Transformer(nn.Module):
|
|||||||
llm = self.llm_cond_proj(llm) * text_mask
|
llm = self.llm_cond_proj(llm) * text_mask
|
||||||
h[:, :L_text] = h[:, :L_text] + llm
|
h[:, :L_text] = h[:, :L_text] + llm
|
||||||
|
|
||||||
h = h + self.embed_image_indicator((indicator == OUTPUT_IMAGE_INDICATOR).to(torch.long))
|
h = h + self.embed_image_indicator((indicator == OUTPUT_IMAGE_INDICATOR).to(torch.long), out_dtype=h.dtype)
|
||||||
|
|
||||||
# Qwen3-VL interleaved MRoPE; position_ids (B, L, 3) -> (3, L) (same across batch).
|
# Qwen3-VL interleaved MRoPE; position_ids (B, L, 3) -> (3, L) (same across batch).
|
||||||
freqs_cis = precompute_freqs_cis(
|
freqs_cis = precompute_freqs_cis(
|
||||||
@ -235,7 +235,7 @@ class Ideogram4Transformer2DModel(Ideogram4Transformer):
|
|||||||
def _run_conditional(self, x_chunk, context_chunk, attn_mask_chunk, t_chunk, gh, gw, transformer_options):
|
def _run_conditional(self, x_chunk, context_chunk, attn_mask_chunk, t_chunk, gh, gw, transformer_options):
|
||||||
B = x_chunk.shape[0]
|
B = x_chunk.shape[0]
|
||||||
device = x_chunk.device
|
device = x_chunk.device
|
||||||
img_tokens = self._img_to_tokens(x_chunk).to(self.dtype)
|
img_tokens = self._img_to_tokens(x_chunk)
|
||||||
L_img = img_tokens.shape[1]
|
L_img = img_tokens.shape[1]
|
||||||
L_text = context_chunk.shape[1]
|
L_text = context_chunk.shape[1]
|
||||||
L = L_text + L_img
|
L = L_text + L_img
|
||||||
@ -268,7 +268,7 @@ class Ideogram4Transformer2DModel(Ideogram4Transformer):
|
|||||||
def _run_image_only(self, x_chunk, t_chunk, gh, gw, transformer_options):
|
def _run_image_only(self, x_chunk, t_chunk, gh, gw, transformer_options):
|
||||||
B = x_chunk.shape[0]
|
B = x_chunk.shape[0]
|
||||||
device = x_chunk.device
|
device = x_chunk.device
|
||||||
img_tokens = self._img_to_tokens(x_chunk).to(self.dtype)
|
img_tokens = self._img_to_tokens(x_chunk)
|
||||||
L_img = img_tokens.shape[1]
|
L_img = img_tokens.shape[1]
|
||||||
|
|
||||||
position_ids = self._image_position_ids(gh, gw, device).unsqueeze(0).expand(B, L_img, 3)
|
position_ids = self._image_position_ids(gh, gw, device).unsqueeze(0).expand(B, L_img, 3)
|
||||||
|
|||||||
@ -43,6 +43,7 @@ class BFLFluxEraseRequest(BaseModel):
|
|||||||
"white (255) marks areas to remove, black (0) marks areas to preserve.",
|
"white (255) marks areas to remove, black (0) marks areas to preserve.",
|
||||||
)
|
)
|
||||||
dilate_pixels: int = Field(10)
|
dilate_pixels: int = Field(10)
|
||||||
|
seed: int | None = Field(None)
|
||||||
output_format: str = Field("png")
|
output_format: str = Field("png")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -97,3 +97,28 @@ class BriaRemoveVideoBackgroundResult(BaseModel):
|
|||||||
class BriaRemoveVideoBackgroundResponse(BaseModel):
|
class BriaRemoveVideoBackgroundResponse(BaseModel):
|
||||||
status: str = Field(...)
|
status: str = Field(...)
|
||||||
result: BriaRemoveVideoBackgroundResult | None = Field(None)
|
result: BriaRemoveVideoBackgroundResult | None = Field(None)
|
||||||
|
|
||||||
|
|
||||||
|
class BriaVideoGreenScreenRequest(BaseModel):
|
||||||
|
video: str = Field(..., description="Publicly accessible URL of the input video.")
|
||||||
|
green_shade: str = Field(
|
||||||
|
default="broadcast_green",
|
||||||
|
description="Solid chroma-key shade applied behind the foreground "
|
||||||
|
"(broadcast_green, chroma_green, or blue_screen).",
|
||||||
|
)
|
||||||
|
output_container_and_codec: str = Field(...)
|
||||||
|
preserve_audio: bool = Field(True)
|
||||||
|
seed: int = Field(...)
|
||||||
|
|
||||||
|
|
||||||
|
class BriaVideoReplaceBackgroundRequest(BaseModel):
|
||||||
|
video: str = Field(..., description="Publicly accessible URL of the input (foreground) video.")
|
||||||
|
background_url: str = Field(
|
||||||
|
...,
|
||||||
|
description="Publicly accessible URL of the background image or video to composite behind "
|
||||||
|
"the foreground. Stretched to the foreground frame; match its aspect ratio for "
|
||||||
|
"undistorted results.",
|
||||||
|
)
|
||||||
|
output_container_and_codec: str = Field(...)
|
||||||
|
preserve_audio: bool = Field(True)
|
||||||
|
seed: int = Field(...)
|
||||||
|
|||||||
@ -534,6 +534,15 @@ class FluxEraseNode(IO.ComfyNode):
|
|||||||
max=25,
|
max=25,
|
||||||
tooltip="Expands the mask boundaries to ensure clean coverage of the object's edges.",
|
tooltip="Expands the mask boundaries to ensure clean coverage of the object's edges.",
|
||||||
),
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="The random seed used for creating the noise.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
outputs=[IO.Image.Output()],
|
outputs=[IO.Image.Output()],
|
||||||
hidden=[
|
hidden=[
|
||||||
@ -553,6 +562,7 @@ class FluxEraseNode(IO.ComfyNode):
|
|||||||
image: Input.Image,
|
image: Input.Image,
|
||||||
mask: Input.Image,
|
mask: Input.Image,
|
||||||
dilate_pixels: int = 10,
|
dilate_pixels: int = 10,
|
||||||
|
seed: int = 0,
|
||||||
) -> IO.NodeOutput:
|
) -> IO.NodeOutput:
|
||||||
validate_image_dimensions(image, min_width=256, min_height=256)
|
validate_image_dimensions(image, min_width=256, min_height=256)
|
||||||
mask = resize_mask_to_image(mask, image)
|
mask = resize_mask_to_image(mask, image)
|
||||||
@ -565,6 +575,7 @@ class FluxEraseNode(IO.ComfyNode):
|
|||||||
image=tensor_to_base64_string(image[:, :, :, :3]), # make sure image will have alpha channel removed
|
image=tensor_to_base64_string(image[:, :, :, :3]), # make sure image will have alpha channel removed
|
||||||
mask=mask,
|
mask=mask,
|
||||||
dilate_pixels=dilate_pixels,
|
dilate_pixels=dilate_pixels,
|
||||||
|
seed=seed,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,8 @@ from comfy_api_nodes.apis.bria import (
|
|||||||
BriaRemoveVideoBackgroundRequest,
|
BriaRemoveVideoBackgroundRequest,
|
||||||
BriaRemoveVideoBackgroundResponse,
|
BriaRemoveVideoBackgroundResponse,
|
||||||
BriaStatusResponse,
|
BriaStatusResponse,
|
||||||
|
BriaVideoGreenScreenRequest,
|
||||||
|
BriaVideoReplaceBackgroundRequest,
|
||||||
InputModerationSettings,
|
InputModerationSettings,
|
||||||
)
|
)
|
||||||
from comfy_api_nodes.util import (
|
from comfy_api_nodes.util import (
|
||||||
@ -319,6 +321,158 @@ class BriaRemoveVideoBackground(IO.ComfyNode):
|
|||||||
return IO.NodeOutput(await download_url_to_video_output(response.result.video_url))
|
return IO.NodeOutput(await download_url_to_video_output(response.result.video_url))
|
||||||
|
|
||||||
|
|
||||||
|
class BriaVideoGreenScreen(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="BriaVideoGreenScreen",
|
||||||
|
display_name="Bria Video Green Screen",
|
||||||
|
category="partner/video/Bria",
|
||||||
|
description="Replace a video's background with a solid chroma-key screen using Bria.",
|
||||||
|
inputs=[
|
||||||
|
IO.Video.Input("video"),
|
||||||
|
IO.Combo.Input(
|
||||||
|
"green_shade",
|
||||||
|
options=["broadcast_green", "chroma_green", "blue_screen"],
|
||||||
|
tooltip="Solid chroma-key shade applied behind the foreground: "
|
||||||
|
"broadcast_green (#00B140), chroma_green (#00FF00), or blue_screen (#0000FF).",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed controls whether the node should re-run; "
|
||||||
|
"results are non-deterministic regardless of seed.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[IO.Video.Output()],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.14,"format":{"suffix":"/second"}}""",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
video: Input.Video,
|
||||||
|
green_shade: str,
|
||||||
|
seed: int,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
validate_video_duration(video, max_duration=60.0)
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path="/proxy/bria/v2/video/edit/green_screen", method="POST"),
|
||||||
|
data=BriaVideoGreenScreenRequest(
|
||||||
|
video=await upload_video_to_comfyapi(cls, video),
|
||||||
|
green_shade=green_shade,
|
||||||
|
output_container_and_codec="mp4_h264",
|
||||||
|
seed=seed,
|
||||||
|
),
|
||||||
|
response_model=BriaStatusResponse,
|
||||||
|
)
|
||||||
|
response = await poll_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path=f"/proxy/bria/v2/status/{response.request_id}"),
|
||||||
|
status_extractor=lambda r: r.status,
|
||||||
|
response_model=BriaRemoveVideoBackgroundResponse,
|
||||||
|
)
|
||||||
|
return IO.NodeOutput(await download_url_to_video_output(response.result.video_url))
|
||||||
|
|
||||||
|
|
||||||
|
class BriaVideoReplaceBackground(IO.ComfyNode):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def define_schema(cls):
|
||||||
|
return IO.Schema(
|
||||||
|
node_id="BriaVideoReplaceBackground",
|
||||||
|
display_name="Bria Video Replace Background",
|
||||||
|
category="partner/video/Bria",
|
||||||
|
description="Replace a video's background with a supplied image or video using Bria. "
|
||||||
|
"The output keeps the foreground's resolution and frame rate; a background with a "
|
||||||
|
"different aspect ratio is stretched to fit, so match it for undistorted results.",
|
||||||
|
inputs=[
|
||||||
|
IO.Video.Input("video", tooltip="Foreground video whose background is replaced."),
|
||||||
|
IO.Image.Input(
|
||||||
|
"background_image",
|
||||||
|
optional=True,
|
||||||
|
tooltip="Background image to composite behind the foreground. "
|
||||||
|
"Provide either a background image or a background video, not both.",
|
||||||
|
),
|
||||||
|
IO.Video.Input(
|
||||||
|
"background_video",
|
||||||
|
optional=True,
|
||||||
|
tooltip="Background video to composite behind the foreground. "
|
||||||
|
"Provide either a background image or a background video, not both.",
|
||||||
|
),
|
||||||
|
IO.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
display_mode=IO.NumberDisplay.number,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed controls whether the node should re-run; "
|
||||||
|
"results are non-deterministic regardless of seed.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[IO.Video.Output()],
|
||||||
|
hidden=[
|
||||||
|
IO.Hidden.auth_token_comfy_org,
|
||||||
|
IO.Hidden.api_key_comfy_org,
|
||||||
|
IO.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
price_badge=IO.PriceBadge(
|
||||||
|
expr="""{"type":"usd","usd":0.14,"format":{"suffix":"/second"}}""",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
async def execute(
|
||||||
|
cls,
|
||||||
|
video: Input.Video,
|
||||||
|
seed: int,
|
||||||
|
background_image: Input.Image | None = None,
|
||||||
|
background_video: Input.Video | None = None,
|
||||||
|
) -> IO.NodeOutput:
|
||||||
|
if (background_image is None) == (background_video is None):
|
||||||
|
raise ValueError("Provide either a background image or a background video, not both.")
|
||||||
|
validate_video_duration(video, max_duration=60.0)
|
||||||
|
if background_video is not None:
|
||||||
|
validate_video_duration(background_video, max_duration=60.0)
|
||||||
|
background_url = await upload_video_to_comfyapi(cls, background_video, wait_label="Uploading background")
|
||||||
|
else:
|
||||||
|
background_url = await upload_image_to_comfyapi(cls, background_image, wait_label="Uploading background")
|
||||||
|
response = await sync_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path="/proxy/bria/v2/video/edit/replace_background", method="POST"),
|
||||||
|
data=BriaVideoReplaceBackgroundRequest(
|
||||||
|
video=await upload_video_to_comfyapi(cls, video),
|
||||||
|
background_url=background_url,
|
||||||
|
output_container_and_codec="mp4_h264",
|
||||||
|
seed=seed,
|
||||||
|
),
|
||||||
|
response_model=BriaStatusResponse,
|
||||||
|
)
|
||||||
|
response = await poll_op(
|
||||||
|
cls,
|
||||||
|
ApiEndpoint(path=f"/proxy/bria/v2/status/{response.request_id}"),
|
||||||
|
status_extractor=lambda r: r.status,
|
||||||
|
response_model=BriaRemoveVideoBackgroundResponse,
|
||||||
|
)
|
||||||
|
return IO.NodeOutput(await download_url_to_video_output(response.result.video_url))
|
||||||
|
|
||||||
|
|
||||||
def _video_to_images_and_mask(video: Input.Video) -> tuple[Input.Image, Input.Mask]:
|
def _video_to_images_and_mask(video: Input.Video) -> tuple[Input.Image, Input.Mask]:
|
||||||
"""Decode a transparent webm (VP9 + alpha) into image frames and an alpha mask.
|
"""Decode a transparent webm (VP9 + alpha) into image frames and an alpha mask.
|
||||||
|
|
||||||
@ -416,6 +570,8 @@ class BriaExtension(ComfyExtension):
|
|||||||
BriaImageEditNode,
|
BriaImageEditNode,
|
||||||
BriaRemoveImageBackground,
|
BriaRemoveImageBackground,
|
||||||
BriaRemoveVideoBackground,
|
BriaRemoveVideoBackground,
|
||||||
|
BriaVideoGreenScreen,
|
||||||
|
# BriaVideoReplaceBackground, # server returns Status 500 when we pass background video
|
||||||
BriaTransparentVideoBackground,
|
BriaTransparentVideoBackground,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -42,9 +42,11 @@ async def _upload_image_to_krea_assets(cls: type[IO.ComfyNode], image: Input.Ima
|
|||||||
|
|
||||||
|
|
||||||
_MODEL_MEDIUM = "Krea 2 Medium"
|
_MODEL_MEDIUM = "Krea 2 Medium"
|
||||||
|
_MODEL_MEDIUM_TURBO = "Krea 2 Medium Turbo"
|
||||||
_MODEL_LARGE = "Krea 2 Large"
|
_MODEL_LARGE = "Krea 2 Large"
|
||||||
_MODEL_ENDPOINTS: dict[str, str] = {
|
_MODEL_ENDPOINTS: dict[str, str] = {
|
||||||
_MODEL_MEDIUM: "/proxy/krea/generate/image/krea/krea-2/medium",
|
_MODEL_MEDIUM: "/proxy/krea/generate/image/krea/krea-2/medium",
|
||||||
|
_MODEL_MEDIUM_TURBO: "/proxy/krea/generate/image/krea/krea-2/medium-turbo",
|
||||||
_MODEL_LARGE: "/proxy/krea/generate/image/krea/krea-2/large",
|
_MODEL_LARGE: "/proxy/krea/generate/image/krea/krea-2/large",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +59,7 @@ _UUID_RE = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F
|
|||||||
|
|
||||||
|
|
||||||
def _krea_model_inputs() -> list:
|
def _krea_model_inputs() -> list:
|
||||||
"""Nested inputs shared by both Krea 2 Medium and Large under the DynamicCombo."""
|
"""Nested inputs shared by Krea 2 Medium, Medium Turbo and Large under the DynamicCombo."""
|
||||||
return [
|
return [
|
||||||
IO.Combo.Input(
|
IO.Combo.Input(
|
||||||
"aspect_ratio",
|
"aspect_ratio",
|
||||||
@ -123,6 +125,7 @@ class Krea2ImageNode(IO.ComfyNode):
|
|||||||
"model",
|
"model",
|
||||||
options=[
|
options=[
|
||||||
IO.DynamicCombo.Option(_MODEL_MEDIUM, _krea_model_inputs()),
|
IO.DynamicCombo.Option(_MODEL_MEDIUM, _krea_model_inputs()),
|
||||||
|
IO.DynamicCombo.Option(_MODEL_MEDIUM_TURBO, _krea_model_inputs()),
|
||||||
IO.DynamicCombo.Option(_MODEL_LARGE, _krea_model_inputs()),
|
IO.DynamicCombo.Option(_MODEL_LARGE, _krea_model_inputs()),
|
||||||
],
|
],
|
||||||
tooltip="Krea 2 Medium is best for expressive illustrations; "
|
tooltip="Krea 2 Medium is best for expressive illustrations; "
|
||||||
@ -151,14 +154,15 @@ class Krea2ImageNode(IO.ComfyNode):
|
|||||||
),
|
),
|
||||||
expr="""
|
expr="""
|
||||||
(
|
(
|
||||||
$isLarge := widgets.model = "krea 2 large";
|
$rates := {
|
||||||
|
"krea 2 medium turbo": {"text": 0.015, "style": 0.0175, "moodboard": 0.02},
|
||||||
|
"krea 2 medium": {"text": 0.03, "style": 0.035, "moodboard": 0.04},
|
||||||
|
"krea 2 large": {"text": 0.06, "style": 0.065, "moodboard": 0.07}
|
||||||
|
};
|
||||||
|
$r := $lookup($rates, widgets.model);
|
||||||
$hasMoodboard := $length($lookup(widgets, "model.moodboard_id")) > 0;
|
$hasMoodboard := $length($lookup(widgets, "model.moodboard_id")) > 0;
|
||||||
$hasStyle := $lookup(inputs, "model.style_reference").connected;
|
$hasStyle := $lookup(inputs, "model.style_reference").connected;
|
||||||
$usd := $hasMoodboard
|
$usd := $hasMoodboard ? $r.moodboard : ($hasStyle ? $r.style : $r.text);
|
||||||
? ($isLarge ? 0.07 : 0.04)
|
|
||||||
: ($hasStyle
|
|
||||||
? ($isLarge ? 0.065 : 0.035)
|
|
||||||
: ($isLarge ? 0.06 : 0.03));
|
|
||||||
{"type":"usd","usd": $usd}
|
{"type":"usd","usd": $usd}
|
||||||
)
|
)
|
||||||
""",
|
""",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
comfyui-frontend-package==1.44.19
|
comfyui-frontend-package==1.45.15
|
||||||
comfyui-workflow-templates==0.9.94
|
comfyui-workflow-templates==0.9.98
|
||||||
comfyui-embedded-docs==0.5.2
|
comfyui-embedded-docs==0.5.2
|
||||||
torch
|
torch
|
||||||
torchsde
|
torchsde
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user