mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2026-06-05 22:07:25 +08:00
[Partner Nodes] feat: add Bria Green Background node (#14277)
This commit is contained in:
parent
4d360f9c9d
commit
0a92dd9c09
@ -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(...)
|
||||||
|
|||||||
@ -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,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user