Spaces:
Running
on
Zero
Running
on
Zero
| import math | |
| import cv2 | |
| from PIL import Image | |
| from comfy.utils import common_upscale | |
| from .utils.image_convert import mask2tensor, np2tensor, tensor2mask, tensor2np | |
| from .utils.mask_utils import solid_mask | |
| from .utils.utils import make_even | |
| _CATEGORY = 'fnodes/image scale' | |
| UPSCALE_METHODS = ['lanczos', 'nearest-exact', 'bilinear', 'area', 'bicubic'] | |
| class GetImageSize: | |
| def INPUT_TYPES(cls): | |
| return { | |
| 'required': { | |
| 'image': ('IMAGE',), | |
| } | |
| } | |
| RETURN_TYPES = ( | |
| 'INT', | |
| 'INT', | |
| 'INT', | |
| ) | |
| RETURN_NAMES = ( | |
| 'width', | |
| 'height', | |
| 'count', | |
| ) | |
| FUNCTION = 'execute' | |
| CATEGORY = _CATEGORY | |
| def execute(self, image): | |
| return { | |
| 'ui': { | |
| 'width': (image.shape[2],), | |
| 'height': (image.shape[1],), | |
| 'count': (image.shape[0],), | |
| }, | |
| 'result': ( | |
| image.shape[2], | |
| image.shape[1], | |
| image.shape[0], | |
| ), | |
| } | |
| class BaseImageScaler: | |
| def INPUT_TYPES(cls): | |
| return { | |
| 'required': { | |
| 'image': ('IMAGE',), | |
| 'upscale_method': (UPSCALE_METHODS,), | |
| }, | |
| 'optional': { | |
| 'mask': ('MASK',), | |
| }, | |
| } | |
| RETURN_TYPES = ('IMAGE', 'MASK', 'INT', 'INT', 'INT') | |
| RETURN_NAMES = ('image', 'mask', 'width', 'height', 'min_dimension') | |
| CATEGORY = _CATEGORY | |
| def scale_image(self, image, width, height, upscale_method, mask=None): | |
| image_tensor = image.movedim(-1, 1) | |
| scaled_image = common_upscale(image_tensor, width, height, upscale_method, 'disabled') | |
| scaled_image = scaled_image.movedim(1, -1) | |
| result_mask = solid_mask(width, height) | |
| if mask is not None: | |
| mask_image = mask2tensor(mask) | |
| mask_image = mask_image.movedim(-1, 1) | |
| mask_image = common_upscale(mask_image, width, height, upscale_method, 'disabled') | |
| mask_image = mask_image.movedim(1, -1) | |
| result_mask = tensor2mask(mask_image) | |
| return scaled_image, result_mask | |
| def prepare_result(self, scaled_image, result_mask, width, height): | |
| return { | |
| 'ui': { | |
| 'width': (width,), | |
| 'height': (height,), | |
| }, | |
| 'result': ( | |
| scaled_image, | |
| result_mask, | |
| width, | |
| height, | |
| min(width, height), | |
| ), | |
| } | |
| class ImageScalerForSDModels(BaseImageScaler): | |
| def INPUT_TYPES(cls): | |
| base_inputs = super().INPUT_TYPES() | |
| base_inputs['required']['sd_model_type'] = (['sdxl', 'sd15', 'sd15+', 'sdxl+'],) | |
| return base_inputs | |
| FUNCTION = 'execute' | |
| DESCRIPTION = """ | |
| 根据SD模型类型缩放图片到指定像素数,sd15为512x512,sd15+为512x768,sdxl为1024x1024,sdxl+为1024x1280 | |
| """ | |
| def execute(self, image, upscale_method, sd_model_type, mask=None): | |
| sd_dimensions = {'sd15': (512, 512), 'sd15+': (512, 768), 'sdxl': (1024, 1024), 'sdxl+': (1024, 1280)} | |
| target_width, target_height = sd_dimensions.get(sd_model_type, (1024, 1024)) | |
| total_pixels = target_width * target_height | |
| scale_by = math.sqrt(total_pixels / (image.shape[2] * image.shape[1])) | |
| width = round(image.shape[2] * scale_by) | |
| height = round(image.shape[1] * scale_by) | |
| scaled_image, result_mask = self.scale_image(image, width, height, upscale_method, mask) | |
| return self.prepare_result(scaled_image, result_mask, width, height) | |
| class ImageScaleBySpecifiedSide(BaseImageScaler): | |
| def INPUT_TYPES(cls): | |
| base_inputs = super().INPUT_TYPES() | |
| base_inputs['required'].update( | |
| { | |
| 'size': ('INT', {'default': 512, 'min': 0, 'step': 1, 'max': 99999}), | |
| 'shorter': ('BOOLEAN', {'default': False}), | |
| } | |
| ) | |
| return base_inputs | |
| FUNCTION = 'execute' | |
| DESCRIPTION = """ | |
| 根据指定边长缩放图片,shorter为True时参照短边,否则参照长边 | |
| """ | |
| def execute(self, image, size, upscale_method, shorter, mask=None): | |
| if shorter: | |
| reference_side_length = min(image.shape[2], image.shape[1]) | |
| else: | |
| reference_side_length = max(image.shape[2], image.shape[1]) | |
| scale_by = reference_side_length / size | |
| width = make_even(round(image.shape[2] / scale_by)) | |
| height = make_even(round(image.shape[1] / scale_by)) | |
| scaled_image, result_mask = self.scale_image(image, width, height, upscale_method, mask) | |
| return self.prepare_result(scaled_image, result_mask, width, height) | |
| class ComputeImageScaleRatio: | |
| def INPUT_TYPES(cls): | |
| return { | |
| 'required': { | |
| 'image': ('IMAGE',), | |
| 'target_max_size': ( | |
| 'INT', | |
| {'default': 1920, 'min': 0, 'step': 1, 'max': 99999}, | |
| ), | |
| }, | |
| } | |
| RETURN_TYPES = ( | |
| 'FLOAT', | |
| 'INT', | |
| 'INT', | |
| ) | |
| RETURN_NAMES = ( | |
| 'rescale_ratio', | |
| 'width', | |
| 'height', | |
| ) | |
| FUNCTION = 'execute' | |
| CATEGORY = _CATEGORY | |
| DESCRIPTION = '根据引用图片的大小和目标最大尺寸,返回缩放比例和缩放后的宽高' | |
| def execute(self, image, target_max_size): | |
| samples = image.movedim(-1, 1) | |
| width, height = samples.shape[3], samples.shape[2] | |
| rescale_ratio = target_max_size / max(width, height) | |
| new_width = make_even(round(width * rescale_ratio)) | |
| new_height = make_even(round(height * rescale_ratio)) | |
| return { | |
| 'ui': { | |
| 'rescale_ratio': (rescale_ratio,), | |
| 'width': (new_width,), | |
| 'height': (new_height,), | |
| }, | |
| 'result': ( | |
| rescale_ratio, | |
| new_width, | |
| new_height, | |
| ), | |
| } | |
| class ImageRotate: | |
| def INPUT_TYPES(cls): | |
| return { | |
| 'required': { | |
| 'image_from': ('IMAGE',), | |
| 'angle': ( | |
| 'FLOAT', | |
| {'default': 0.1, 'min': -14096, 'max': 14096, 'step': 0.01}, | |
| ), | |
| 'expand': ('BOOLEAN', {'default': True}), | |
| }, | |
| } | |
| RETURN_TYPES = ('IMAGE',) | |
| RETURN_NAMES = ('rotated_image',) | |
| FUNCTION = 'run' | |
| CATEGORY = _CATEGORY | |
| def run(self, image_from, angle, expand): | |
| image_np = tensor2np(image_from[0]) | |
| height, width = image_np.shape[:2] | |
| center = (width / 2, height / 2) | |
| if expand: | |
| # 计算新图像的尺寸 | |
| rot_mat = cv2.getRotationMatrix2D(center, angle, 1.0) | |
| abs_cos = abs(rot_mat[0, 0]) | |
| abs_sin = abs(rot_mat[0, 1]) | |
| new_width = int(height * abs_sin + width * abs_cos) | |
| new_height = int(height * abs_cos + width * abs_sin) | |
| # 调整旋转矩阵 | |
| rot_mat[0, 2] += (new_width / 2) - center[0] | |
| rot_mat[1, 2] += (new_height / 2) - center[1] | |
| # 执行旋转 | |
| rotated_image = cv2.warpAffine(image_np, rot_mat, (new_width, new_height), flags=cv2.INTER_CUBIC) | |
| else: | |
| # 不扩展图像尺寸的旋转 | |
| rot_mat = cv2.getRotationMatrix2D(center, angle, 1.0) | |
| rotated_image = cv2.warpAffine(image_np, rot_mat, (width, height), flags=cv2.INTER_CUBIC) | |
| # 转换回tensor格式 | |
| rotated_tensor = np2tensor(rotated_image).unsqueeze(0) | |
| return (rotated_tensor,) | |
| class TrimImageBorders: | |
| def INPUT_TYPES(cls): | |
| return { | |
| 'required': { | |
| 'image': ('IMAGE',), | |
| 'threshold': ( | |
| 'INT', | |
| {'default': 10, 'min': 0, 'max': 14096, 'step': 1}, | |
| ), | |
| }, | |
| } | |
| RETURN_TYPES = ('IMAGE',) | |
| FUNCTION = 'run' | |
| CATEGORY = _CATEGORY | |
| DESCRIPTION = '图片去黑边' | |
| def run(self, image, threshold): | |
| img = tensor2np(image[0]) | |
| img = Image.fromarray(img) | |
| gray_image = img.convert('L') | |
| binary_image = gray_image.point(lambda x: 255 if x > threshold else 0) | |
| bbox = binary_image.getbbox() | |
| if bbox: | |
| cropped_image = img.crop(bbox) | |
| else: | |
| cropped_image = img | |
| cropped_image = np2tensor(cropped_image).unsqueeze(0) | |
| return (cropped_image,) | |
| IMAGE_SCALE_CLASS_MAPPINGS = { | |
| 'GetImageSize-': GetImageSize, | |
| 'ImageScalerForSDModels-': ImageScalerForSDModels, | |
| 'ImageScaleBySpecifiedSide-': ImageScaleBySpecifiedSide, | |
| 'ComputeImageScaleRatio-': ComputeImageScaleRatio, | |
| 'ImageRotate-': ImageRotate, | |
| 'TrimImageBorders-': TrimImageBorders, | |
| } | |
| IMAGE_SCALE_NAME_MAPPINGS = { | |
| 'GetImageSize-': 'Get Image Size', | |
| 'ImageScalerForSDModels-': 'Image Scaler for SD Models', | |
| 'ImageScaleBySpecifiedSide-': 'Image Scale By Specified Side', | |
| 'ComputeImageScaleRatio-': 'Compute Image Scale Ratio', | |
| 'ImageRotate-': 'Image Rotate', | |
| 'TrimImageBorders-': 'Trim Image Borders', | |
| } | |