|
import torch |
|
import cv2 |
|
import numpy as np |
|
from PIL import Image, ImageDraw, ImageFilter |
|
import folder_paths |
|
from . import config |
|
|
|
LANCZOS = (Image.Resampling.LANCZOS if hasattr(Image, 'Resampling') else Image.LANCZOS) |
|
|
|
|
|
def pil2numpy(image): |
|
return (np.array(image).astype(np.float32) / 255.0)[np.newaxis, :, :, :] |
|
|
|
|
|
def pil2tensor(image): |
|
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0) |
|
|
|
|
|
def tensor2pil(image): |
|
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8)) |
|
|
|
|
|
def center_of_bbox(bbox): |
|
w, h = bbox[2] - bbox[0], bbox[3] - bbox[1] |
|
return bbox[0] + w/2, bbox[1] + h/2 |
|
|
|
|
|
def combine_masks(masks): |
|
if len(masks) == 0: |
|
return None |
|
else: |
|
initial_cv2_mask = np.array(masks[0][1]) |
|
combined_cv2_mask = initial_cv2_mask |
|
|
|
for i in range(1, len(masks)): |
|
cv2_mask = np.array(masks[i][1]) |
|
|
|
if combined_cv2_mask.shape == cv2_mask.shape: |
|
combined_cv2_mask = cv2.bitwise_or(combined_cv2_mask, cv2_mask) |
|
else: |
|
|
|
pass |
|
|
|
mask = torch.from_numpy(combined_cv2_mask) |
|
return mask |
|
|
|
|
|
def combine_masks2(masks): |
|
if len(masks) == 0: |
|
return None |
|
else: |
|
initial_cv2_mask = np.array(masks[0]).astype(np.uint8) |
|
combined_cv2_mask = initial_cv2_mask |
|
|
|
for i in range(1, len(masks)): |
|
cv2_mask = np.array(masks[i]).astype(np.uint8) |
|
|
|
if combined_cv2_mask.shape == cv2_mask.shape: |
|
combined_cv2_mask = cv2.bitwise_or(combined_cv2_mask, cv2_mask) |
|
else: |
|
|
|
pass |
|
|
|
mask = torch.from_numpy(combined_cv2_mask) |
|
return mask |
|
|
|
|
|
def bitwise_and_masks(mask1, mask2): |
|
mask1 = mask1.cpu() |
|
mask2 = mask2.cpu() |
|
cv2_mask1 = np.array(mask1) |
|
cv2_mask2 = np.array(mask2) |
|
|
|
if cv2_mask1.shape == cv2_mask2.shape: |
|
cv2_mask = cv2.bitwise_and(cv2_mask1, cv2_mask2) |
|
return torch.from_numpy(cv2_mask) |
|
else: |
|
|
|
return mask1 |
|
|
|
|
|
def to_binary_mask(mask, threshold=0): |
|
if len(mask.shape) == 3: |
|
mask = mask.squeeze(0) |
|
|
|
mask = mask.clone().cpu() |
|
mask[mask > threshold] = 1. |
|
mask[mask <= threshold] = 0. |
|
return mask |
|
|
|
|
|
def use_gpu_opencv(): |
|
return not config.get_config()['disable_gpu_opencv'] |
|
|
|
|
|
def dilate_mask(mask, dilation_factor, iter=1): |
|
if dilation_factor == 0: |
|
return mask |
|
|
|
if len(mask.shape) == 3: |
|
mask = mask.squeeze(0) |
|
|
|
kernel = np.ones((abs(dilation_factor), abs(dilation_factor)), np.uint8) |
|
|
|
if use_gpu_opencv(): |
|
mask = cv2.UMat(mask) |
|
kernel = cv2.UMat(kernel) |
|
|
|
if dilation_factor > 0: |
|
result = cv2.dilate(mask, kernel, iter) |
|
else: |
|
result = cv2.erode(mask, kernel, iter) |
|
|
|
if use_gpu_opencv(): |
|
return result.get() |
|
else: |
|
return result |
|
|
|
|
|
def dilate_masks(segmasks, dilation_factor, iter=1): |
|
if dilation_factor == 0: |
|
return segmasks |
|
|
|
dilated_masks = [] |
|
kernel = np.ones((abs(dilation_factor), abs(dilation_factor)), np.uint8) |
|
|
|
if use_gpu_opencv(): |
|
kernel = cv2.UMat(kernel) |
|
|
|
for i in range(len(segmasks)): |
|
cv2_mask = segmasks[i][1] |
|
|
|
if use_gpu_opencv(): |
|
cv2_mask = cv2.UMat(cv2_mask) |
|
|
|
if dilation_factor > 0: |
|
dilated_mask = cv2.dilate(cv2_mask, kernel, iter) |
|
else: |
|
dilated_mask = cv2.erode(cv2_mask, kernel, iter) |
|
|
|
if use_gpu_opencv(): |
|
dilated_mask = dilated_mask.get() |
|
|
|
item = (segmasks[i][0], dilated_mask, segmasks[i][2]) |
|
dilated_masks.append(item) |
|
|
|
return dilated_masks |
|
|
|
|
|
def feather_mask(mask, thickness, base_alpha=255): |
|
pil_mask = Image.fromarray(np.uint8(mask * base_alpha)) |
|
|
|
|
|
blurred_mask = pil_mask.filter(ImageFilter.GaussianBlur(thickness)) |
|
feathered_mask = Image.new("L", pil_mask.size, 0) |
|
feathered_mask.paste(blurred_mask, (0, 0), blurred_mask) |
|
return feathered_mask |
|
|
|
|
|
def subtract_masks(mask1, mask2): |
|
mask1 = mask1.cpu() |
|
mask2 = mask2.cpu() |
|
cv2_mask1 = np.array(mask1) * 255 |
|
cv2_mask2 = np.array(mask2) * 255 |
|
|
|
if cv2_mask1.shape == cv2_mask2.shape: |
|
cv2_mask = cv2.subtract(cv2_mask1, cv2_mask2) |
|
return torch.clamp(torch.from_numpy(cv2_mask) / 255.0, min=0, max=1) |
|
else: |
|
|
|
return mask1 |
|
|
|
|
|
def add_masks(mask1, mask2): |
|
mask1 = mask1.cpu() |
|
mask2 = mask2.cpu() |
|
cv2_mask1 = np.array(mask1) * 255 |
|
cv2_mask2 = np.array(mask2) * 255 |
|
|
|
if cv2_mask1.shape == cv2_mask2.shape: |
|
cv2_mask = cv2.add(cv2_mask1, cv2_mask2) |
|
return torch.clamp(torch.from_numpy(cv2_mask) / 255.0, min=0, max=1) |
|
else: |
|
|
|
return mask1 |
|
|
|
|
|
def normalize_region(limit, startp, size): |
|
if startp < 0: |
|
new_endp = min(limit, size) |
|
new_startp = 0 |
|
elif startp + size > limit: |
|
new_startp = max(0, limit - size) |
|
new_endp = limit |
|
else: |
|
new_startp = startp |
|
new_endp = min(limit, startp+size) |
|
|
|
return int(new_startp), int(new_endp) |
|
|
|
|
|
def make_crop_region(w, h, bbox, crop_factor, crop_min_size=None): |
|
x1 = bbox[0] |
|
y1 = bbox[1] |
|
x2 = bbox[2] |
|
y2 = bbox[3] |
|
|
|
bbox_w = x2 - x1 |
|
bbox_h = y2 - y1 |
|
|
|
crop_w = bbox_w * crop_factor |
|
crop_h = bbox_h * crop_factor |
|
|
|
if crop_min_size is not None: |
|
crop_w = max(crop_min_size, crop_w) |
|
crop_h = max(crop_min_size, crop_h) |
|
|
|
kernel_x = x1 + bbox_w / 2 |
|
kernel_y = y1 + bbox_h / 2 |
|
|
|
new_x1 = int(kernel_x - crop_w / 2) |
|
new_y1 = int(kernel_y - crop_h / 2) |
|
|
|
|
|
new_x1, new_x2 = normalize_region(w, new_x1, crop_w) |
|
new_y1, new_y2 = normalize_region(h, new_y1, crop_h) |
|
|
|
return [new_x1, new_y1, new_x2, new_y2] |
|
|
|
|
|
def crop_ndarray4(npimg, crop_region): |
|
x1 = crop_region[0] |
|
y1 = crop_region[1] |
|
x2 = crop_region[2] |
|
y2 = crop_region[3] |
|
|
|
cropped = npimg[:, y1:y2, x1:x2, :] |
|
|
|
return cropped |
|
|
|
|
|
def crop_ndarray2(npimg, crop_region): |
|
x1 = crop_region[0] |
|
y1 = crop_region[1] |
|
x2 = crop_region[2] |
|
y2 = crop_region[3] |
|
|
|
cropped = npimg[y1:y2, x1:x2] |
|
|
|
return cropped |
|
|
|
|
|
def crop_image(image, crop_region): |
|
return crop_ndarray4(np.array(image), crop_region) |
|
|
|
|
|
def to_latent_image(pixels, vae): |
|
x = pixels.shape[1] |
|
y = pixels.shape[2] |
|
if pixels.shape[1] != x or pixels.shape[2] != y: |
|
pixels = pixels[:, :x, :y, :] |
|
t = vae.encode(pixels[:, :, :, :3]) |
|
return {"samples": t} |
|
|
|
|
|
def scale_tensor(w, h, image): |
|
image = tensor2pil(image) |
|
scaled_image = image.resize((w, h), resample=LANCZOS) |
|
return pil2tensor(scaled_image) |
|
|
|
|
|
def scale_tensor_and_to_pil(w, h, image): |
|
image = tensor2pil(image) |
|
return image.resize((w, h), resample=LANCZOS) |
|
|
|
|
|
def empty_pil_tensor(w=64, h=64): |
|
image = Image.new("RGB", (w, h)) |
|
draw = ImageDraw.Draw(image) |
|
draw.rectangle((0, 0, w-1, h-1), fill=(0, 0, 0)) |
|
return pil2tensor(image) |
|
|
|
|
|
class NonListIterable: |
|
def __init__(self, data): |
|
self.data = data |
|
|
|
def __getitem__(self, index): |
|
return self.data[index] |
|
|
|
|
|
|
|
def add_folder_path_and_extensions(folder_name, full_folder_paths, extensions): |
|
|
|
for full_folder_path in full_folder_paths: |
|
|
|
folder_paths.add_model_folder_path(folder_name, full_folder_path) |
|
|
|
|
|
if folder_name in folder_paths.folder_names_and_paths: |
|
|
|
current_paths, current_extensions = folder_paths.folder_names_and_paths[folder_name] |
|
|
|
updated_extensions = current_extensions | extensions |
|
|
|
folder_paths.folder_names_and_paths[folder_name] = (current_paths, updated_extensions) |
|
else: |
|
|
|
|
|
|
|
folder_paths.folder_names_and_paths[folder_name] = (full_folder_paths, extensions) |
|
|
|
|
|
|
|
class AnyType(str): |
|
def __ne__(self, __value: object) -> bool: |
|
return False |
|
|
|
any_typ = AnyType("*") |
|
|