426 lines
20 KiB
Python
426 lines
20 KiB
Python
""" Jovimetrix - Transform """
|
||
|
||
import sys
|
||
from enum import Enum
|
||
|
||
from comfy.utils import ProgressBar
|
||
|
||
from cozy_comfyui import \
|
||
logger, \
|
||
IMAGE_SIZE_MIN, \
|
||
InputType, RGBAMaskType, EnumConvertType, \
|
||
deep_merge, parse_param, parse_dynamic, zip_longest_fill
|
||
|
||
from cozy_comfyui.lexicon import \
|
||
Lexicon
|
||
|
||
from cozy_comfyui.node import \
|
||
COZY_TYPE_IMAGE, \
|
||
CozyImageNode, CozyBaseNode
|
||
|
||
from cozy_comfyui.image.channel import \
|
||
channel_solid
|
||
|
||
from cozy_comfyui.image.convert import \
|
||
tensor_to_cv, cv_to_tensor_full, cv_to_tensor, image_mask, image_mask_add
|
||
|
||
from cozy_comfyui.image.compose import \
|
||
EnumOrientation, EnumEdge, EnumMirrorMode, EnumScaleMode, EnumInterpolation, \
|
||
image_edge_wrap, image_mirror, image_scalefit, image_transform, \
|
||
image_crop, image_crop_center, image_crop_polygonal, image_stacker, \
|
||
image_flatten
|
||
|
||
from cozy_comfyui.image.misc import \
|
||
image_stack
|
||
|
||
from cozy_comfyui.image.mapping import \
|
||
EnumProjection, \
|
||
remap_fisheye, remap_perspective, remap_polar, remap_sphere
|
||
|
||
# ==============================================================================
|
||
# === GLOBAL ===
|
||
# ==============================================================================
|
||
|
||
JOV_CATEGORY = "TRANSFORM"
|
||
|
||
# ==============================================================================
|
||
# === ENUMERATION ===
|
||
# ==============================================================================
|
||
|
||
class EnumCropMode(Enum):
|
||
CENTER = 20
|
||
XY = 0
|
||
FREE = 10
|
||
|
||
# ==============================================================================
|
||
# === CLASS ===
|
||
# ==============================================================================
|
||
|
||
class CropNode(CozyImageNode):
|
||
NAME = "CROP (JOV) ✂️"
|
||
CATEGORY = JOV_CATEGORY
|
||
DESCRIPTION = """
|
||
Extract a portion of an input image or resize it. It supports various cropping modes, including center cropping, custom XY cropping, and free-form polygonal cropping. This node is useful for preparing image data for specific tasks or extracting regions of interest.
|
||
"""
|
||
|
||
@classmethod
|
||
def INPUT_TYPES(cls) -> InputType:
|
||
d = super().INPUT_TYPES()
|
||
d = deep_merge(d, {
|
||
"optional": {
|
||
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
||
Lexicon.FUNCTION: (EnumCropMode._member_names_, {
|
||
"default": EnumCropMode.CENTER.name}),
|
||
Lexicon.XY: ("VEC2", {
|
||
"default": (0, 0), "mij": 0, "maj": 1,
|
||
"label": ["X", "Y"]}),
|
||
Lexicon.WH: ("VEC2", {
|
||
"default": (512, 512), "mij": IMAGE_SIZE_MIN, "int": True,
|
||
"label": ["W", "H"]}),
|
||
Lexicon.TLTR: ("VEC4", {
|
||
"default": (0, 0, 0, 1), "mij": 0, "maj": 1,
|
||
"label": ["TOP", "LEFT", "TOP", "RIGHT"],}),
|
||
Lexicon.BLBR: ("VEC4", {
|
||
"default": (1, 0, 1, 1), "mij": 0, "maj": 1,
|
||
"label": ["BOTTOM", "LEFT", "BOTTOM", "RIGHT"],}),
|
||
Lexicon.MATTE: ("VEC4", {
|
||
"default": (0, 0, 0, 255), "rgb": True,})
|
||
}
|
||
})
|
||
return Lexicon._parse(d)
|
||
|
||
def run(self, **kw) -> RGBAMaskType:
|
||
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||
func = parse_param(kw, Lexicon.FUNCTION, EnumCropMode, EnumCropMode.CENTER.name)
|
||
# if less than 1 then use as scalar, over 1 = int(size)
|
||
xy = parse_param(kw, Lexicon.XY, EnumConvertType.VEC2, (0, 0,))
|
||
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)
|
||
tltr = parse_param(kw, Lexicon.TLTR, EnumConvertType.VEC4, (0, 0, 0, 1,))
|
||
blbr = parse_param(kw, Lexicon.BLBR, EnumConvertType.VEC4, (1, 0, 1, 1,))
|
||
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)
|
||
params = list(zip_longest_fill(pA, func, xy, wihi, tltr, blbr, matte))
|
||
images = []
|
||
pbar = ProgressBar(len(params))
|
||
for idx, (pA, func, xy, wihi, tltr, blbr, matte) in enumerate(params):
|
||
width, height = wihi
|
||
pA = tensor_to_cv(pA) if pA is not None else channel_solid(width, height)
|
||
alpha = None
|
||
if pA.ndim == 3 and pA.shape[2] == 4:
|
||
alpha = image_mask(pA)
|
||
|
||
if func == EnumCropMode.FREE:
|
||
x1, y1, x2, y2 = tltr
|
||
x4, y4, x3, y3 = blbr
|
||
points = (x1 * width, y1 * height), (x2 * width, y2 * height), \
|
||
(x3 * width, y3 * height), (x4 * width, y4 * height)
|
||
pA = image_crop_polygonal(pA, points)
|
||
if alpha is not None:
|
||
alpha = image_crop_polygonal(alpha, points)
|
||
pA[..., 3] = alpha[..., 0][:,:]
|
||
elif func == EnumCropMode.XY:
|
||
pA = image_crop(pA, width, height, xy)
|
||
else:
|
||
pA = image_crop_center(pA, width, height)
|
||
images.append(cv_to_tensor_full(pA, matte))
|
||
pbar.update_absolute(idx)
|
||
return image_stack(images)
|
||
|
||
class FlattenNode(CozyImageNode):
|
||
NAME = "FLATTEN (JOV) ⬇️"
|
||
CATEGORY = JOV_CATEGORY
|
||
DESCRIPTION = """
|
||
Combine multiple input images into a single image by summing their pixel values. This operation is useful for merging multiple layers or images into one composite image, such as combining different elements of a design or merging masks. Users can specify the blending mode and interpolation method to control how the images are combined. Additionally, a matte can be applied to adjust the transparency of the final composite image.
|
||
"""
|
||
|
||
@classmethod
|
||
def INPUT_TYPES(cls) -> InputType:
|
||
d = super().INPUT_TYPES()
|
||
d = deep_merge(d, {
|
||
"optional": {
|
||
Lexicon.MODE: (EnumScaleMode._member_names_, {
|
||
"default": EnumScaleMode.MATTE.name,}),
|
||
Lexicon.WH: ("VEC2", {
|
||
"default": (512, 512), "mij":1, "int": True,
|
||
"label": ["W", "H"]}),
|
||
Lexicon.SAMPLE: (EnumInterpolation._member_names_, {
|
||
"default": EnumInterpolation.LANCZOS4.name,}),
|
||
Lexicon.MATTE: ("VEC4", {
|
||
"default": (0, 0, 0, 255), "rgb": True,}),
|
||
Lexicon.OFFSET: ("VEC2", {
|
||
"default": (0, 0), "mij":0, "int": True,
|
||
"label": ["X", "Y"]}),
|
||
}
|
||
})
|
||
return Lexicon._parse(d)
|
||
|
||
def run(self, **kw) -> RGBAMaskType:
|
||
imgs = parse_dynamic(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||
if imgs is None:
|
||
logger.warning("no images to flatten")
|
||
return ()
|
||
|
||
# be less dumb when merging
|
||
pA = [tensor_to_cv(i) for i in imgs]
|
||
mode = parse_param(kw, Lexicon.MODE, EnumScaleMode, EnumScaleMode.MATTE.name)[0]
|
||
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (512, 512), 1)[0]
|
||
sample = parse_param(kw, Lexicon.SAMPLE, EnumInterpolation, EnumInterpolation.LANCZOS4.name)[0]
|
||
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)[0]
|
||
offset = parse_param(kw, Lexicon.OFFSET, EnumConvertType.VEC2INT, (0, 0), 0)[0]
|
||
w, h = wihi
|
||
x, y = offset
|
||
pA = image_flatten(pA, x, y, w, h, mode=mode, sample=sample)
|
||
pA = [cv_to_tensor_full(pA, matte)]
|
||
return image_stack(pA)
|
||
|
||
class SplitNode(CozyBaseNode):
|
||
NAME = "SPLIT (JOV) 🎭"
|
||
CATEGORY = JOV_CATEGORY
|
||
RETURN_TYPES = ("IMAGE", "IMAGE",)
|
||
RETURN_NAMES = ("IMAGEA", "IMAGEB",)
|
||
OUTPUT_TOOLTIPS = (
|
||
"Left/Top image",
|
||
"Right/Bottom image"
|
||
)
|
||
DESCRIPTION = """
|
||
Split an image into two or four images based on the percentages for width and height.
|
||
"""
|
||
|
||
@classmethod
|
||
def INPUT_TYPES(cls) -> InputType:
|
||
d = super().INPUT_TYPES()
|
||
d = deep_merge(d, {
|
||
"optional": {
|
||
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
||
Lexicon.VALUE: ("FLOAT", {
|
||
"default": 0.5, "min": 0, "max": 1, "step": 0.001
|
||
}),
|
||
Lexicon.FLIP: ("BOOLEAN", {
|
||
"default": False,
|
||
"tooltip": "Horizontal split (False) or Vertical split (True)"
|
||
}),
|
||
Lexicon.MODE: (EnumScaleMode._member_names_, {
|
||
"default": EnumScaleMode.MATTE.name,}),
|
||
Lexicon.WH: ("VEC2", {
|
||
"default": (512, 512), "mij":IMAGE_SIZE_MIN, "int": True,
|
||
"label": ["W", "H"]}),
|
||
Lexicon.SAMPLE: (EnumInterpolation._member_names_, {
|
||
"default": EnumInterpolation.LANCZOS4.name,}),
|
||
Lexicon.MATTE: ("VEC4", {
|
||
"default": (0, 0, 0, 255), "rgb": True,})
|
||
}
|
||
})
|
||
return Lexicon._parse(d)
|
||
|
||
def run(self, **kw) -> RGBAMaskType:
|
||
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||
percent = parse_param(kw, Lexicon.VALUE, EnumConvertType.FLOAT, 0.5, 0, 1)
|
||
flip = parse_param(kw, Lexicon.FLIP, EnumConvertType.BOOLEAN, False)
|
||
mode = parse_param(kw, Lexicon.MODE, EnumScaleMode, EnumScaleMode.MATTE.name)
|
||
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)
|
||
sample = parse_param(kw, Lexicon.SAMPLE, EnumInterpolation, EnumInterpolation.LANCZOS4.name)
|
||
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)
|
||
params = list(zip_longest_fill(pA, percent, flip, mode, wihi, sample, matte))
|
||
images = []
|
||
pbar = ProgressBar(len(params))
|
||
for idx, (pA, percent, flip, mode, wihi, sample, matte) in enumerate(params):
|
||
w, h = wihi
|
||
pA = channel_solid(w, h, matte) if pA is None else tensor_to_cv(pA)
|
||
|
||
if flip:
|
||
size = pA.shape[1]
|
||
percent = max(1, min(size-1, int(size * percent)))
|
||
image_a = pA[:, :percent]
|
||
image_b = pA[:, percent:]
|
||
else:
|
||
size = pA.shape[0]
|
||
percent = max(1, min(size-1, int(size * percent)))
|
||
image_a = pA[:percent, :]
|
||
image_b = pA[percent:, :]
|
||
|
||
if mode != EnumScaleMode.MATTE:
|
||
image_a = image_scalefit(image_a, w, h, mode, sample)
|
||
image_b = image_scalefit(image_b, w, h, mode, sample)
|
||
|
||
images.append([cv_to_tensor(img) for img in [image_a, image_b]])
|
||
pbar.update_absolute(idx)
|
||
return image_stack(images)
|
||
|
||
class StackNode(CozyImageNode):
|
||
NAME = "STACK (JOV) ➕"
|
||
CATEGORY = JOV_CATEGORY
|
||
DESCRIPTION = """
|
||
Merge multiple input images into a single composite image by stacking them along a specified axis.
|
||
|
||
Options include axis, stride, scaling mode, width and height, interpolation method, and matte color.
|
||
|
||
The axis parameter allows for horizontal, vertical, or grid stacking of images, while stride controls the spacing between them.
|
||
"""
|
||
|
||
@classmethod
|
||
def INPUT_TYPES(cls) -> InputType:
|
||
d = super().INPUT_TYPES()
|
||
d = deep_merge(d, {
|
||
"optional": {
|
||
Lexicon.AXIS: (EnumOrientation._member_names_, {
|
||
"default": EnumOrientation.GRID.name,}),
|
||
Lexicon.STEP: ("INT", {
|
||
"default": 1, "min": 0,
|
||
"tooltip":"How many images are placed before a new row starts (stride)"}),
|
||
Lexicon.MODE: (EnumScaleMode._member_names_, {
|
||
"default": EnumScaleMode.MATTE.name,}),
|
||
Lexicon.WH: ("VEC2", {
|
||
"default": (512, 512), "mij": IMAGE_SIZE_MIN, "int": True,
|
||
"label": ["W", "H"]}),
|
||
Lexicon.SAMPLE: (EnumInterpolation._member_names_, {
|
||
"default": EnumInterpolation.LANCZOS4.name,}),
|
||
Lexicon.MATTE: ("VEC4", {
|
||
"default": (0, 0, 0, 255), "rgb": True,})
|
||
}
|
||
})
|
||
return Lexicon._parse(d)
|
||
|
||
def run(self, **kw) -> RGBAMaskType:
|
||
images = parse_dynamic(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||
if len(images) == 0:
|
||
logger.warning("no images to stack")
|
||
return
|
||
|
||
images = [tensor_to_cv(i) for i in images]
|
||
axis = parse_param(kw, Lexicon.AXIS, EnumOrientation, EnumOrientation.GRID.name)[0]
|
||
stride = parse_param(kw, Lexicon.STEP, EnumConvertType.INT, 1, 0)[0]
|
||
mode = parse_param(kw, Lexicon.MODE, EnumScaleMode, EnumScaleMode.MATTE.name)[0]
|
||
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)[0]
|
||
sample = parse_param(kw, Lexicon.SAMPLE, EnumInterpolation, EnumInterpolation.LANCZOS4.name)[0]
|
||
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)[0]
|
||
img = image_stacker(images, axis, stride) #, matte)
|
||
if mode != EnumScaleMode.MATTE:
|
||
w, h = wihi
|
||
img = image_scalefit(img, w, h, mode, sample)
|
||
rgba, rgb, mask = cv_to_tensor_full(img, matte)
|
||
return rgba.unsqueeze(0), rgb.unsqueeze(0), mask.unsqueeze(0)
|
||
|
||
class TransformNode(CozyImageNode):
|
||
NAME = "TRANSFORM (JOV) 🏝️"
|
||
CATEGORY = JOV_CATEGORY
|
||
DESCRIPTION = """
|
||
Apply various geometric transformations to images, including translation, rotation, scaling, mirroring, tiling and perspective projection. It offers extensive control over image manipulation to achieve desired visual effects.
|
||
"""
|
||
|
||
@classmethod
|
||
def INPUT_TYPES(cls) -> InputType:
|
||
d = super().INPUT_TYPES(prompt=True, dynprompt=True)
|
||
d = deep_merge(d, {
|
||
"optional": {
|
||
Lexicon.IMAGE: (COZY_TYPE_IMAGE, {}),
|
||
Lexicon.MASK: (COZY_TYPE_IMAGE, {
|
||
"tooltip": "Override Image mask"}),
|
||
Lexicon.XY: ("VEC2", {
|
||
"default": (0, 0,), "mij": -1, "maj": 1,
|
||
"label": ["X", "Y"]}),
|
||
Lexicon.ANGLE: ("FLOAT", {
|
||
"default": 0, "min": -sys.float_info.max, "max": sys.float_info.max, "step": 0.1,}),
|
||
Lexicon.SIZE: ("VEC2", {
|
||
"default": (1, 1), "mij": 0.001,
|
||
"label": ["X", "Y"]}),
|
||
Lexicon.TILE: ("VEC2", {
|
||
"default": (1, 1), "mij": 1,
|
||
"label": ["X", "Y"]}),
|
||
Lexicon.EDGE: (EnumEdge._member_names_, {
|
||
"default": EnumEdge.CLIP.name}),
|
||
Lexicon.MIRROR: (EnumMirrorMode._member_names_, {
|
||
"default": EnumMirrorMode.NONE.name}),
|
||
Lexicon.PIVOT: ("VEC2", {
|
||
"default": (0.5, 0.5), "mij": 0, "maj": 1, "step": 0.01,
|
||
"label": ["X", "Y"]}),
|
||
Lexicon.PROJECTION: (EnumProjection._member_names_, {
|
||
"default": EnumProjection.NORMAL.name}),
|
||
Lexicon.TLTR: ("VEC4", {
|
||
"default": (0, 0, 1, 0), "mij": 0, "maj": 1, "step": 0.005,
|
||
"label": ["TOP", "LEFT", "TOP", "RIGHT"],}),
|
||
Lexicon.BLBR: ("VEC4", {
|
||
"default": (0, 1, 1, 1), "mij": 0, "maj": 1, "step": 0.005,
|
||
"label": ["BOTTOM", "LEFT", "BOTTOM", "RIGHT"],}),
|
||
Lexicon.STRENGTH: ("FLOAT", {
|
||
"default": 1, "min": 0, "max": 1, "step": 0.005}),
|
||
Lexicon.MODE: (EnumScaleMode._member_names_, {
|
||
"default": EnumScaleMode.MATTE.name,}),
|
||
Lexicon.WH: ("VEC2", {
|
||
"default": (512, 512), "mij": IMAGE_SIZE_MIN, "int": True,
|
||
"label": ["W", "H"]}),
|
||
Lexicon.SAMPLE: (EnumInterpolation._member_names_, {
|
||
"default": EnumInterpolation.LANCZOS4.name,}),
|
||
Lexicon.MATTE: ("VEC4", {
|
||
"default": (0, 0, 0, 255), "rgb": True,})
|
||
}
|
||
})
|
||
return Lexicon._parse(d)
|
||
|
||
def run(self, **kw) -> RGBAMaskType:
|
||
pA = parse_param(kw, Lexicon.IMAGE, EnumConvertType.IMAGE, None)
|
||
mask = parse_param(kw, Lexicon.MASK, EnumConvertType.IMAGE, None)
|
||
offset = parse_param(kw, Lexicon.XY, EnumConvertType.VEC2, (0, 0), -1, 1)
|
||
angle = parse_param(kw, Lexicon.ANGLE, EnumConvertType.FLOAT, 0)
|
||
size = parse_param(kw, Lexicon.SIZE, EnumConvertType.VEC2, (1, 1), 0.001)
|
||
edge = parse_param(kw, Lexicon.EDGE, EnumEdge, EnumEdge.CLIP.name)
|
||
mirror = parse_param(kw, Lexicon.MIRROR, EnumMirrorMode, EnumMirrorMode.NONE.name)
|
||
mirror_pivot = parse_param(kw, Lexicon.PIVOT, EnumConvertType.VEC2, (0.5, 0.5), 0, 1)
|
||
tile_xy = parse_param(kw, Lexicon.TILE, EnumConvertType.VEC2, (1, 1), 1)
|
||
proj = parse_param(kw, Lexicon.PROJECTION, EnumProjection, EnumProjection.NORMAL.name)
|
||
tltr = parse_param(kw, Lexicon.TLTR, EnumConvertType.VEC4, (0, 0, 1, 0), 0, 1)
|
||
blbr = parse_param(kw, Lexicon.BLBR, EnumConvertType.VEC4, (0, 1, 1, 1), 0, 1)
|
||
strength = parse_param(kw, Lexicon.STRENGTH, EnumConvertType.FLOAT, 1, 0, 1)
|
||
mode = parse_param(kw, Lexicon.MODE, EnumScaleMode, EnumScaleMode.MATTE.name)
|
||
wihi = parse_param(kw, Lexicon.WH, EnumConvertType.VEC2INT, (512, 512), IMAGE_SIZE_MIN)
|
||
sample = parse_param(kw, Lexicon.SAMPLE, EnumInterpolation, EnumInterpolation.LANCZOS4.name)
|
||
matte = parse_param(kw, Lexicon.MATTE, EnumConvertType.VEC4INT, (0, 0, 0, 255), 0, 255)
|
||
params = list(zip_longest_fill(pA, mask, offset, angle, size, edge, tile_xy, mirror, mirror_pivot, proj, strength, tltr, blbr, mode, wihi, sample, matte))
|
||
images = []
|
||
pbar = ProgressBar(len(params))
|
||
for idx, (pA, mask, offset, angle, size, edge, tile_xy, mirror, mirror_pivot, proj, strength, tltr, blbr, mode, wihi, sample, matte) in enumerate(params):
|
||
pA = tensor_to_cv(pA) if pA is not None else channel_solid()
|
||
if mask is None:
|
||
mask = image_mask(pA, 255)
|
||
else:
|
||
mask = tensor_to_cv(mask)
|
||
pA = image_mask_add(pA, mask)
|
||
|
||
h, w = pA.shape[:2]
|
||
pA = image_transform(pA, offset, angle, size, sample, edge)
|
||
pA = image_crop_center(pA, w, h)
|
||
|
||
if mirror != EnumMirrorMode.NONE:
|
||
mpx, mpy = mirror_pivot
|
||
pA = image_mirror(pA, mirror, mpx, mpy)
|
||
pA = image_scalefit(pA, w, h, EnumScaleMode.FIT, sample)
|
||
|
||
tx, ty = tile_xy
|
||
if tx != 1. or ty != 1.:
|
||
pA = image_edge_wrap(pA, tx / 2 - 0.5, ty / 2 - 0.5)
|
||
pA = image_scalefit(pA, w, h, EnumScaleMode.FIT, sample)
|
||
|
||
match proj:
|
||
case EnumProjection.PERSPECTIVE:
|
||
x1, y1, x2, y2 = tltr
|
||
x4, y4, x3, y3 = blbr
|
||
sh, sw = pA.shape[:2]
|
||
x1, x2, x3, x4 = map(lambda x: x * sw, [x1, x2, x3, x4])
|
||
y1, y2, y3, y4 = map(lambda y: y * sh, [y1, y2, y3, y4])
|
||
pA = remap_perspective(pA, [[x1, y1], [x2, y2], [x3, y3], [x4, y4]])
|
||
case EnumProjection.SPHERICAL:
|
||
pA = remap_sphere(pA, strength)
|
||
case EnumProjection.FISHEYE:
|
||
pA = remap_fisheye(pA, strength)
|
||
case EnumProjection.POLAR:
|
||
pA = remap_polar(pA)
|
||
|
||
if proj != EnumProjection.NORMAL:
|
||
pA = image_scalefit(pA, w, h, EnumScaleMode.FIT, sample)
|
||
|
||
if mode != EnumScaleMode.MATTE:
|
||
w, h = wihi
|
||
pA = image_scalefit(pA, w, h, mode, sample)
|
||
|
||
images.append(cv_to_tensor_full(pA, matte))
|
||
pbar.update_absolute(idx)
|
||
return image_stack(images)
|