Spaces:
Build error
Build error
import cv2 | |
import base64 | |
import numpy as np | |
def laplacian_blending(A, B, m, num_levels=7): | |
assert A.shape == B.shape | |
assert B.shape == m.shape | |
height = m.shape[0] | |
width = m.shape[1] | |
size_list = np.array([4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192]) | |
size = size_list[np.where(size_list > max(height, width))][0] | |
GA = np.zeros((size, size, 3), dtype=np.float32) | |
GA[:height, :width, :] = A | |
GB = np.zeros((size, size, 3), dtype=np.float32) | |
GB[:height, :width, :] = B | |
GM = np.zeros((size, size, 3), dtype=np.float32) | |
GM[:height, :width, :] = m | |
gpA = [GA] | |
gpB = [GB] | |
gpM = [GM] | |
for i in range(num_levels): | |
GA = cv2.pyrDown(GA) | |
GB = cv2.pyrDown(GB) | |
GM = cv2.pyrDown(GM) | |
gpA.append(np.float32(GA)) | |
gpB.append(np.float32(GB)) | |
gpM.append(np.float32(GM)) | |
lpA = [gpA[num_levels-1]] | |
lpB = [gpB[num_levels-1]] | |
gpMr = [gpM[num_levels-1]] | |
for i in range(num_levels-1,0,-1): | |
LA = np.subtract(gpA[i-1], cv2.pyrUp(gpA[i])) | |
LB = np.subtract(gpB[i-1], cv2.pyrUp(gpB[i])) | |
lpA.append(LA) | |
lpB.append(LB) | |
gpMr.append(gpM[i-1]) | |
LS = [] | |
for la,lb,gm in zip(lpA,lpB,gpMr): | |
ls = la * gm + lb * (1.0 - gm) | |
LS.append(ls) | |
ls_ = LS[0] | |
for i in range(1,num_levels): | |
ls_ = cv2.pyrUp(ls_) | |
ls_ = cv2.add(ls_, LS[i]) | |
ls_ = ls_[:height, :width, :] | |
#ls_ = (ls_ - np.min(ls_)) * (255.0 / (np.max(ls_) - np.min(ls_))) | |
return ls_.clip(0, 255) | |
def mask_crop(mask, crop): | |
top, bottom, left, right = crop | |
shape = mask.shape | |
top = int(top) | |
bottom = int(bottom) | |
if top + bottom < shape[1]: | |
if top > 0: mask[:top, :] = 0 | |
if bottom > 0: mask[-bottom:, :] = 0 | |
left = int(left) | |
right = int(right) | |
if left + right < shape[0]: | |
if left > 0: mask[:, :left] = 0 | |
if right > 0: mask[:, -right:] = 0 | |
return mask | |
def create_image_grid(images, size=128): | |
num_images = len(images) | |
num_cols = int(np.ceil(np.sqrt(num_images))) | |
num_rows = int(np.ceil(num_images / num_cols)) | |
grid = np.zeros((num_rows * size, num_cols * size, 3), dtype=np.uint8) | |
for i, image in enumerate(images): | |
row_idx = (i // num_cols) * size | |
col_idx = (i % num_cols) * size | |
image = cv2.resize(image.copy(), (size,size)) | |
if image.dtype != np.uint8: | |
image = (image.astype('float32') * 255).astype('uint8') | |
if image.ndim == 2: | |
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) | |
grid[row_idx:row_idx + size, col_idx:col_idx + size] = image | |
return grid | |
def paste_to_whole(foreground, background, matrix, mask=None, crop_mask=(0,0,0,0), blur_amount=0.1, erode_amount = 0.15, blend_method='linear'): | |
inv_matrix = cv2.invertAffineTransform(matrix) | |
fg_shape = foreground.shape[:2] | |
bg_shape = (background.shape[1], background.shape[0]) | |
foreground = cv2.warpAffine(foreground, inv_matrix, bg_shape, borderValue=0.0, borderMode=cv2.BORDER_REPLICATE) | |
if mask is None: | |
mask = np.full(fg_shape, 1., dtype=np.float32) | |
mask = mask_crop(mask, crop_mask) | |
mask = cv2.warpAffine(mask, inv_matrix, bg_shape, borderValue=0.0) | |
else: | |
assert fg_shape == mask.shape[:2], "foreground & mask shape mismatch!" | |
mask = mask_crop(mask, crop_mask).astype('float32') | |
mask = cv2.warpAffine(mask, inv_matrix, (background.shape[1], background.shape[0]), borderValue=0.0) | |
_mask = mask.copy() | |
_mask[_mask > 0.05] = 1. | |
non_zero_points = cv2.findNonZero(_mask) | |
_, _, w, h = cv2.boundingRect(non_zero_points) | |
mask_size = int(np.sqrt(w * h)) | |
if erode_amount > 0: | |
kernel_size = max(int(mask_size * erode_amount), 1) | |
structuring_element = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size, kernel_size)) | |
mask = cv2.erode(mask, structuring_element) | |
if blur_amount > 0: | |
kernel_size = max(int(mask_size * blur_amount), 3) | |
if kernel_size % 2 == 0: | |
kernel_size += 1 | |
mask = cv2.GaussianBlur(mask, (kernel_size, kernel_size), 0) | |
mask = np.tile(np.expand_dims(mask, axis=-1), (1, 1, 3)) | |
if blend_method == 'laplacian': | |
composite_image = laplacian_blending(foreground, background, mask.clip(0,1), num_levels=4) | |
else: | |
composite_image = mask * foreground + (1 - mask) * background | |
return composite_image.astype("uint8").clip(0, 255) | |
def image_mask_overlay(img, mask): | |
img = img.astype('float32') / 255. | |
img *= (mask + 0.25).clip(0, 1) | |
img = np.clip(img * 255., 0., 255.).astype('uint8') | |
return img | |
def resize_with_padding(img, expected_size=(640, 360), color=(0, 0, 0), max_flip=False): | |
original_height, original_width = img.shape[:2] | |
if max_flip and original_height > original_width: | |
expected_size = (expected_size[1], expected_size[0]) | |
aspect_ratio = original_width / original_height | |
new_width = expected_size[0] | |
new_height = int(new_width / aspect_ratio) | |
if new_height > expected_size[1]: | |
new_height = expected_size[1] | |
new_width = int(new_height * aspect_ratio) | |
resized_img = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_AREA) | |
canvas = cv2.copyMakeBorder(resized_img, | |
top=(expected_size[1] - new_height) // 2, | |
bottom=(expected_size[1] - new_height + 1) // 2, | |
left=(expected_size[0] - new_width) // 2, | |
right=(expected_size[0] - new_width + 1) // 2, | |
borderType=cv2.BORDER_CONSTANT, value=color) | |
return canvas | |
def create_image_grid(images, size=128): | |
num_images = len(images) | |
num_cols = int(np.ceil(np.sqrt(num_images))) | |
num_rows = int(np.ceil(num_images / num_cols)) | |
grid = np.zeros((num_rows * size, num_cols * size, 3), dtype=np.uint8) | |
for i, image in enumerate(images): | |
row_idx = (i // num_cols) * size | |
col_idx = (i % num_cols) * size | |
image = cv2.resize(image.copy(), (size,size)) | |
if image.dtype != np.uint8: | |
image = (image.astype('float32') * 255).astype('uint8') | |
if image.ndim == 2: | |
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) | |
grid[row_idx:row_idx + size, col_idx:col_idx + size] = image | |
return grid | |
def image_to_html(img, size=(640, 360), extension="jpg"): | |
if img is not None: | |
img = resize_with_padding(img, expected_size=size) | |
buffer = cv2.imencode(f".{extension}", img)[1] | |
base64_data = base64.b64encode(buffer.tobytes()) | |
imgbs64 = f"data:image/{extension};base64," + base64_data.decode("utf-8") | |
html = '<div style="display: flex; justify-content: center; align-items: center; width: 100%;">' | |
html += f'<img src={imgbs64} alt="No Preview" style="max-width: 100%; max-height: 100%;">' | |
html += '</div>' | |
return html | |
return None | |
def mix_two_image(a, b, opacity=1.): | |
a_dtype = a.dtype | |
b_dtype = b.dtype | |
a = a.astype('float32') | |
b = b.astype('float32') | |
a = cv2.resize(a, (b.shape[0], b.shape[1])) | |
opacity = min(max(opacity, 0.), 1.) | |
mixed_img = opacity * b + (1 - opacity) * a | |
return mixed_img.astype(a_dtype) | |
resolution_map = { | |
"Original": None, | |
"240p": (426, 240), | |
"360p": (640, 360), | |
"480p": (854, 480), | |
"720p": (1280, 720), | |
"1080p": (1920, 1080), | |
"1440p": (2560, 1440), | |
"2160p": (3840, 2160), | |
} | |
def resize_image_by_resolution(img, quality): | |
resolution = resolution_map.get(quality, None) | |
if resolution is None: | |
return img | |
h, w = img.shape[:2] | |
if h > w: | |
ratio = resolution[0] / h | |
else: | |
ratio = resolution[0] / w | |
new_h, new_w = int(h * ratio), int(w * ratio) | |
img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA) | |
return img | |
def fast_pil_encode(pil_image): | |
image_arr = np.asarray(pil_image)[:,:,::-1] | |
buffer = cv2.imencode('.jpg', image_arr)[1] | |
base64_data = base64.b64encode(buffer.tobytes()) | |
return "data:image/jpg;base64," + base64_data.decode("utf-8") | |
def fast_numpy_encode(img_array): | |
buffer = cv2.imencode('.jpg', img_array)[1] | |
base64_data = base64.b64encode(buffer.tobytes()) | |
return "data:image/jpg;base64," + base64_data.decode("utf-8") | |
crf_quality_by_resolution = { | |
240: {"poor": 45, "low": 35, "medium": 28, "high": 23, "best": 20}, | |
360: {"poor": 35, "low": 28, "medium": 23, "high": 20, "best": 18}, | |
480: {"poor": 28, "low": 23, "medium": 20, "high": 18, "best": 16}, | |
720: {"poor": 23, "low": 20, "medium": 18, "high": 16, "best": 14}, | |
1080: {"poor": 20, "low": 18, "medium": 16, "high": 14, "best": 12}, | |
1440: {"poor": 18, "low": 16, "medium": 14, "high": 12, "best": 10}, | |
2160: {"poor": 16, "low": 14, "medium": 12, "high": 10, "best": 8} | |
} | |
def get_crf_for_resolution(resolution, quality): | |
available_resolutions = list(crf_quality_by_resolution.keys()) | |
closest_resolution = min(available_resolutions, key=lambda x: abs(x - resolution)) | |
return crf_quality_by_resolution[closest_resolution][quality] |