Skip to content

Commit b293f8c

Browse files
authored
[Partner Nodes] add widget for automatic upscaling for the ByteDance2Reference node (Comfy-Org#14032)
Signed-off-by: bigcat88 <bigcat88@icloud.com>
1 parent 2ca1480 commit b293f8c

3 files changed

Lines changed: 66 additions & 13 deletions

File tree

comfy_api_nodes/nodes_bytedance.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@
4343
ApiEndpoint,
4444
download_url_to_image_tensor,
4545
download_url_to_video_output,
46+
downscale_video_to_max_pixels,
4647
get_number_of_images,
4748
image_tensor_pair_to_batch,
4849
poll_op,
49-
resize_video_to_pixel_budget,
5050
sync_op,
5151
upload_audio_to_comfyapi,
5252
upload_image_to_comfyapi,
5353
upload_images_to_comfyapi,
5454
upload_video_to_comfyapi,
55+
upscale_video_to_min_pixels,
5556
validate_image_aspect_ratio,
5657
validate_image_dimensions,
5758
validate_string,
@@ -110,12 +111,13 @@ def _validate_ref_video_pixels(video: Input.Video, model_id: str, resolution: st
110111
max_px = limits.get("max")
111112
if min_px and pixels < min_px:
112113
raise ValueError(
113-
f"Reference video {index} is too small: {w}x{h} = {pixels:,}px. " f"Minimum is {min_px:,}px for this model."
114+
f"Reference video {index} is too small: {w}x{h} = {pixels:,} total pixels. "
115+
f"Minimum for this model is {min_px:,} total pixels."
114116
)
115117
if max_px and pixels > max_px:
116118
raise ValueError(
117-
f"Reference video {index} is too large: {w}x{h} = {pixels:,}px. "
118-
f"Maximum is {max_px:,}px for this model. Try downscaling the video."
119+
f"Reference video {index} is too large: {w}x{h} = {pixels:,} total pixels. "
120+
f"Maximum for this model is {max_px:,} total pixels. Try downscaling the video."
119121
)
120122

121123

@@ -1676,14 +1678,14 @@ def define_schema(cls):
16761678
"first_frame_asset_id",
16771679
default="",
16781680
tooltip="Seedance asset_id to use as the first frame. "
1679-
"Mutually exclusive with the first_frame image input.",
1681+
"Mutually exclusive with the first_frame image input.",
16801682
optional=True,
16811683
),
16821684
IO.String.Input(
16831685
"last_frame_asset_id",
16841686
default="",
16851687
tooltip="Seedance asset_id to use as the last frame. "
1686-
"Mutually exclusive with the last_frame image input.",
1688+
"Mutually exclusive with the last_frame image input.",
16871689
optional=True,
16881690
),
16891691
IO.Int.Input(
@@ -1865,11 +1867,20 @@ def _seedance2_reference_inputs(resolutions: list[str], default_ratio: str = "16
18651867
IO.Boolean.Input(
18661868
"auto_downscale",
18671869
default=False,
1868-
advanced=True,
18691870
optional=True,
18701871
tooltip="Automatically downscale reference videos that exceed the model's pixel budget "
18711872
"for the selected resolution. Aspect ratio is preserved; videos already within limits are untouched.",
18721873
),
1874+
IO.Boolean.Input(
1875+
"auto_upscale",
1876+
default=False,
1877+
advanced=True,
1878+
optional=True,
1879+
tooltip="Automatically upscale reference videos that are below the model's minimum pixel count "
1880+
"for the selected resolution. Aspect ratio is preserved; videos already meeting the minimum are "
1881+
"untouched. Note: upscaling a low-resolution source does not add real detail and may produce "
1882+
"lower-quality generations.",
1883+
),
18731884
IO.Autogrow.Input(
18741885
"reference_assets",
18751886
template=IO.Autogrow.TemplateNames(
@@ -2030,7 +2041,13 @@ async def execute(
20302041
max_px = SEEDANCE2_REF_VIDEO_PIXEL_LIMITS.get(model_id, {}).get(model["resolution"], {}).get("max")
20312042
if max_px:
20322043
for key in reference_videos:
2033-
reference_videos[key] = resize_video_to_pixel_budget(reference_videos[key], max_px)
2044+
reference_videos[key] = downscale_video_to_max_pixels(reference_videos[key], max_px)
2045+
2046+
if model.get("auto_upscale") and reference_videos:
2047+
min_px = SEEDANCE2_REF_VIDEO_PIXEL_LIMITS.get(model_id, {}).get(model["resolution"], {}).get("min")
2048+
if min_px:
2049+
for key in reference_videos:
2050+
reference_videos[key] = upscale_video_to_min_pixels(reference_videos[key], min_px)
20342051

20352052
total_video_duration = 0.0
20362053
for i, key in enumerate(reference_videos, 1):

comfy_api_nodes/util/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@
1616
convert_mask_to_image,
1717
downscale_image_tensor,
1818
downscale_image_tensor_by_max_side,
19+
downscale_video_to_max_pixels,
1920
image_tensor_pair_to_batch,
2021
pil_to_bytesio,
2122
resize_mask_to_image,
22-
resize_video_to_pixel_budget,
2323
tensor_to_base64_string,
2424
tensor_to_bytesio,
2525
tensor_to_pil,
2626
text_filepath_to_base64_string,
2727
text_filepath_to_data_uri,
2828
trim_video,
29+
upscale_video_to_min_pixels,
2930
video_to_base64_string,
3031
)
3132
from .download_helpers import (
@@ -88,16 +89,17 @@
8889
"convert_mask_to_image",
8990
"downscale_image_tensor",
9091
"downscale_image_tensor_by_max_side",
92+
"downscale_video_to_max_pixels",
9193
"image_tensor_pair_to_batch",
9294
"pil_to_bytesio",
9395
"resize_mask_to_image",
94-
"resize_video_to_pixel_budget",
9596
"tensor_to_base64_string",
9697
"tensor_to_bytesio",
9798
"tensor_to_pil",
9899
"text_filepath_to_base64_string",
99100
"text_filepath_to_data_uri",
100101
"trim_video",
102+
"upscale_video_to_min_pixels",
101103
"video_to_base64_string",
102104
# Validation utilities
103105
"get_image_dimensions",

comfy_api_nodes/util/conversions.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,14 +415,48 @@ def trim_video(video: Input.Video, duration_sec: float) -> Input.Video:
415415
raise RuntimeError(f"Failed to trim video: {str(e)}") from e
416416

417417

418-
def resize_video_to_pixel_budget(video: Input.Video, total_pixels: int) -> Input.Video:
419-
"""Downscale a video to fit within ``total_pixels`` (w * h), preserving aspect ratio.
418+
def downscale_video_to_max_pixels(video: Input.Video, max_pixels: int) -> Input.Video:
419+
"""Downscale a video to fit within ``max_pixels`` (w * h), preserving aspect ratio.
420420
421421
Returns the original video object untouched when it already fits. Preserves frame rate, duration, and audio.
422422
Aspect ratio is preserved up to a fraction of a percent (even-dim rounding).
423423
"""
424424
src_w, src_h = video.get_dimensions()
425-
scale_dims = _compute_downscale_dims(src_w, src_h, total_pixels)
425+
scale_dims = _compute_downscale_dims(src_w, src_h, max_pixels)
426+
if scale_dims is None:
427+
return video
428+
return _apply_video_scale(video, scale_dims)
429+
430+
431+
def _compute_upscale_dims(src_w: int, src_h: int, total_pixels: int) -> tuple[int, int] | None:
432+
"""Return upscaled (w, h) with even dims meeting at least ``total_pixels``, or None if already large enough.
433+
434+
Source aspect ratio is preserved; output may drift by a fraction of a percent because both dimensions
435+
are rounded up to even values (many codecs require divisible-by-2). The result is guaranteed to be at
436+
least ``total_pixels``.
437+
"""
438+
pixels = src_w * src_h
439+
if pixels >= total_pixels:
440+
return None
441+
scale = math.sqrt(total_pixels / pixels)
442+
new_w = math.ceil(src_w * scale)
443+
new_h = math.ceil(src_h * scale)
444+
if new_w % 2:
445+
new_w += 1
446+
if new_h % 2:
447+
new_h += 1
448+
return new_w, new_h
449+
450+
451+
def upscale_video_to_min_pixels(video: Input.Video, min_pixels: int) -> Input.Video:
452+
"""Upscale a video to meet at least ``min_pixels`` (w * h), preserving aspect ratio.
453+
454+
Returns the original video object untouched when it already meets the minimum. Preserves frame rate,
455+
duration, and audio. Aspect ratio is preserved up to a fraction of a percent (even-dim rounding).
456+
Note: upscaling a low-resolution source does not add real detail; downstream model quality may suffer.
457+
"""
458+
src_w, src_h = video.get_dimensions()
459+
scale_dims = _compute_upscale_dims(src_w, src_h, min_pixels)
426460
if scale_dims is None:
427461
return video
428462
return _apply_video_scale(video, scale_dims)

0 commit comments

Comments
 (0)