Add a simpler image resize node.
This commit is contained in:
+6
-3
@@ -1,8 +1,11 @@
|
||||
# https://github.com/google/yapf
|
||||
[style]
|
||||
based_on_style = google
|
||||
indent_width = 2
|
||||
INDENT_WIDTH = 2
|
||||
CONTINUATION_INDENT_WIDTH = 2
|
||||
COLUMN_LIMIT = 100
|
||||
EACH_DICT_ENTRY_ON_SEPARATE_LINE = true
|
||||
INDENT_DICTIONARY_VALUE = false
|
||||
DISABLE_ENDING_COMMA_HEURISTIC = true
|
||||
DEDENT_CLOSING_BRACKETS = true
|
||||
FORCE_MULTILINE_DICT = false
|
||||
coalesce_brackets=True
|
||||
ALLOW_SPLIT_BEFORE_DICT_VALUE = false
|
||||
@@ -41,6 +41,7 @@ from .py.image_comparer import RgthreeImageComparer
|
||||
from .py.power_lora_loader import RgthreePowerLoraLoader
|
||||
from .py.power_primitive import RgthreePowerPrimitive
|
||||
from .py.image_or_latent_size import RgthreeImageOrLatentSize
|
||||
from .py.image_resize import RgthreeImageResize
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
RgthreeBigContext.NAME: RgthreeBigContext,
|
||||
@@ -65,6 +66,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
RgthreePowerLoraLoader.NAME: RgthreePowerLoraLoader,
|
||||
RgthreePowerPrimitive.NAME: RgthreePowerPrimitive,
|
||||
RgthreeImageOrLatentSize.NAME: RgthreeImageOrLatentSize,
|
||||
RgthreeImageResize.NAME: RgthreeImageResize,
|
||||
}
|
||||
|
||||
if get_config_value('unreleased.dynamic_context.enabled') is True:
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import torch
|
||||
import comfy.utils
|
||||
import nodes
|
||||
|
||||
from .constants import get_category, get_name
|
||||
|
||||
|
||||
class RgthreeImageResize:
|
||||
"""Image Resize."""
|
||||
|
||||
NAME = get_name("Image Resize")
|
||||
CATEGORY = get_category()
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls): # pylint: disable = invalid-name, missing-function-docstring
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
"measurement": (["pixels", "percentage"],),
|
||||
"width": (
|
||||
"INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": nodes.MAX_RESOLUTION,
|
||||
"step": 1,
|
||||
"tooltip": (
|
||||
"The width of the desired resize. A pixel value if measurement is 'pixels' or a"
|
||||
" 100% scale percentage value if measurement is 'percentage'. Passing '0' will"
|
||||
" calculate the dimension based on the height."
|
||||
),
|
||||
},
|
||||
),
|
||||
"height": ("INT", {
|
||||
"default": 0,
|
||||
"min": 0,
|
||||
"max": nodes.MAX_RESOLUTION,
|
||||
"step": 1
|
||||
}),
|
||||
"fit": (["crop", "pad", "contain"], {
|
||||
"tooltip": (
|
||||
"'crop' resizes so the image covers the desired width and height, and center-crops the"
|
||||
" excess, returning exactly the desired width and height."
|
||||
"\n'pad' resizes so the image fits inside the desired width and height, and fills the"
|
||||
" empty space returning exactly the desired width and height."
|
||||
"\n'contain' resizes so the image fits inside the desired width and height, and"
|
||||
" returns the image with it's new size, with one side liekly smaller than the desired."
|
||||
"\n\nNote, if either width or height is '0', the effective fit is 'contain'."
|
||||
)
|
||||
},
|
||||
),
|
||||
"method": (nodes.ImageScale.upscale_methods,),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "INT", "INT",)
|
||||
RETURN_NAMES = ("IMAGE", "WIDTH", "HEIGHT",)
|
||||
FUNCTION = "main"
|
||||
DESCRIPTION = """Resize the image."""
|
||||
|
||||
def main(self, image, measurement, width, height, method, fit):
|
||||
"""Resizes the image."""
|
||||
_, H, W, _ = image.shape
|
||||
|
||||
if measurement == "percentage":
|
||||
width = round(width * W / 100)
|
||||
height = round(height * H / 100)
|
||||
|
||||
if (width == 0 and height == 0) or (width == W and height == H):
|
||||
return (image, W, H)
|
||||
|
||||
# If one dimension is 0, then calculate the desired value from the ratio of the set dimension.
|
||||
# This also implies a 'contain' fit since the width and height will be scaled with a locked
|
||||
# aspect ratio.
|
||||
if width == 0 or height == 0:
|
||||
width = round(height / H * W) if width == 0 else width
|
||||
height = round(width / W * H) if height == 0 else height
|
||||
fit = "contain"
|
||||
|
||||
# At this point, width and height are our output height, but our resize sizes will be different.
|
||||
resized_width = width
|
||||
resized_height = height
|
||||
if fit == "crop":
|
||||
# If we resize against the opposite ratio, then choose the ratio that has the overhang.
|
||||
if (height / H * W) > width:
|
||||
resized_width = round(height / H * W)
|
||||
elif (width / W * H) > height:
|
||||
resized_height = round(width / W * H)
|
||||
elif fit == "contain" or fit == "pad":
|
||||
# If we resize against the opposite ratio, then choose the ratio that has the overhang.
|
||||
if (height / H * W) > width:
|
||||
resized_height = round(width / W * H)
|
||||
elif (width / W * H) > height:
|
||||
resized_width = round(height / H * W)
|
||||
|
||||
out_image = comfy.utils.common_upscale(
|
||||
image.clone().movedim(-1, 1), resized_width, resized_height, method, crop="disabled"
|
||||
).movedim(1, -1)
|
||||
OB, OH, OW, OC = out_image.shape
|
||||
|
||||
if fit != "contain":
|
||||
# First, we crop, then we pad; no need to check fit (other than not 'contain') since the size
|
||||
# should already be correct.
|
||||
if OW > width:
|
||||
out_image = out_image.narrow(-2, (OW - width) // 2, width)
|
||||
if OH > height:
|
||||
out_image = out_image.narrow(-3, (OH - height) // 2, height)
|
||||
|
||||
OB, OH, OW, OC = out_image.shape
|
||||
if width != OW or height != OH:
|
||||
padded_image = torch.zeros((OB, height, width, OC), dtype=image.dtype, device=image.device)
|
||||
x = (width - OW) // 2
|
||||
y = (height - OH) // 2
|
||||
for b in range(OB):
|
||||
padded_image[b, y:y + OH, x:x + OW, :] = out_image[b]
|
||||
out_image = padded_image
|
||||
|
||||
return (out_image, out_image.shape[2], out_image.shape[1])
|
||||
Reference in New Issue
Block a user