Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -1,219 +1,217 @@
|
|
1 |
import cv2
|
2 |
import numpy as np
|
3 |
-
import scipy as sp
|
4 |
-
import scipy.sparse.linalg
|
|
|
5 |
import gradio as gr
|
6 |
-
import os
|
7 |
|
8 |
-
def get_image(
|
9 |
if mask:
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
|
19 |
-
elif img.ndim == 2:
|
20 |
-
img = np.stack((img,)*3, axis=-1)
|
21 |
-
return img.astype('double') / 255.0
|
22 |
-
|
23 |
-
def neighbours(i, j, max_i, max_j):
|
24 |
-
pairs = []
|
25 |
-
for n in [-1, 1]:
|
26 |
-
if 0 <= i+n <= max_i:
|
27 |
-
pairs.append((i+n, j))
|
28 |
-
if 0 <= j+n <= max_j:
|
29 |
-
pairs.append((i, j+n))
|
30 |
-
return pairs
|
31 |
|
32 |
-
|
|
|
|
|
33 |
img_s_h, img_s_w = img_s.shape
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
nnz = np.sum(mask > 0)
|
35 |
-
im2var = np.full(mask.shape, -1, dtype=
|
36 |
im2var[mask > 0] = np.arange(nnz)
|
37 |
|
38 |
-
ys, xs = np.
|
39 |
-
|
40 |
-
# Precompute neighbor indices
|
41 |
-
y_n = np.array([ys-1, ys+1, ys, ys])
|
42 |
-
x_n = np.array([xs, xs, xs-1, xs+1])
|
43 |
-
|
44 |
-
# Clip indices to image boundaries
|
45 |
-
y_n = np.clip(y_n, 0, img_s_h-1)
|
46 |
-
x_n = np.clip(x_n, 0, img_s_w-1)
|
47 |
|
48 |
-
|
49 |
-
d = img_s[ys, xs][:, np.newaxis] - img_s[y_n, x_n]
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
cols = np.repeat(im2var[ys, xs], 4)
|
54 |
-
data = np.ones(4*nnz)
|
55 |
|
56 |
-
A = sp.sparse.csr_matrix((data, (rows, cols)), shape=(4*nnz, nnz))
|
57 |
-
|
58 |
-
mask_n = (im2var[y_n, x_n] != -1)
|
59 |
-
cols_n = im2var[y_n, x_n][mask_n]
|
60 |
-
rows_n = np.arange(4*nnz)[mask_n.ravel()]
|
61 |
-
data_n = -np.ones(cols_n.size)
|
62 |
-
|
63 |
-
A += sp.sparse.csr_matrix((data_n, (rows_n, cols_n)), shape=(4*nnz, nnz))
|
64 |
-
|
65 |
-
b = d.ravel()
|
66 |
-
b[~mask_n.ravel()] += img_t[y_n, x_n][~mask_n]
|
67 |
-
|
68 |
-
# Solve the system
|
69 |
-
v = sp.sparse.linalg.lsqr(A, b)[0]
|
70 |
-
|
71 |
-
# Update the target image
|
72 |
img_t_out = img_t.copy()
|
73 |
-
img_t_out[
|
74 |
|
75 |
return np.clip(img_t_out, 0, 1)
|
76 |
|
77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
img_s_h, img_s_w = img_s.shape
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
nnz = np.sum(mask > 0)
|
80 |
-
im2var = np.full(mask.shape, -1, dtype=
|
81 |
im2var[mask > 0] = np.arange(nnz)
|
82 |
|
83 |
-
ys, xs = np.
|
84 |
-
|
85 |
-
# Precompute neighbor indices
|
86 |
-
y_n = np.array([ys-1, ys+1, ys, ys])
|
87 |
-
x_n = np.array([xs, xs, xs-1, xs+1])
|
88 |
-
|
89 |
-
# Clip indices to image boundaries
|
90 |
-
y_n = np.clip(y_n, 0, img_s_h-1)
|
91 |
-
x_n = np.clip(x_n, 0, img_s_w-1)
|
92 |
-
|
93 |
-
# Compute differences
|
94 |
-
ds = img_s[ys, xs][:, np.newaxis] - img_s[y_n, x_n]
|
95 |
-
dt = img_t[ys, xs][:, np.newaxis] - img_t[y_n, x_n]
|
96 |
-
|
97 |
-
# Choose larger gradient
|
98 |
-
d = np.where(np.abs(ds) > np.abs(dt), ds, dt)
|
99 |
-
|
100 |
-
# Construct sparse matrix A and vector b
|
101 |
-
rows = np.arange(4*nnz)
|
102 |
-
cols = np.repeat(im2var[ys, xs], 4)
|
103 |
-
data = np.ones(4*nnz)
|
104 |
-
|
105 |
-
A = sp.sparse.csr_matrix((data, (rows, cols)), shape=(4*nnz, nnz))
|
106 |
-
|
107 |
-
mask_n = (im2var[y_n, x_n] != -1)
|
108 |
-
cols_n = im2var[y_n, x_n][mask_n]
|
109 |
-
rows_n = np.arange(4*nnz)[mask_n.ravel()]
|
110 |
-
data_n = -np.ones(cols_n.size)
|
111 |
|
112 |
-
|
113 |
|
114 |
-
|
115 |
-
b[~mask_n.ravel()] += img_t[y_n, x_n][~mask_n]
|
116 |
|
117 |
-
|
118 |
-
v = sp.sparse.linalg.lsqr(A, b)[0]
|
119 |
|
120 |
-
# Update the target image
|
121 |
img_t_out = img_t.copy()
|
122 |
-
img_t_out[
|
123 |
|
124 |
return np.clip(img_t_out, 0, 1)
|
125 |
|
126 |
-
def
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
return gaussian_1d @ gaussian_1d.T
|
131 |
-
|
132 |
-
def _low_pass_filter(img, sigma):
|
133 |
-
return cv2.filter2D(img, -1, _2d_gaussian(sigma))
|
134 |
-
|
135 |
-
def _high_pass_filter(img, sigma):
|
136 |
-
return img - _low_pass_filter(img, sigma)
|
137 |
-
|
138 |
-
def _gaus_pyramid(img, depth, sigma):
|
139 |
-
pyramid = [img]
|
140 |
-
for _ in range(depth - 1):
|
141 |
-
img = _low_pass_filter(cv2.pyrDown(img), sigma)
|
142 |
-
pyramid.append(img)
|
143 |
-
return pyramid
|
144 |
-
|
145 |
-
def _lap_pyramid(img, depth, sigma):
|
146 |
-
pyramid = []
|
147 |
-
for d in range(depth - 1):
|
148 |
-
next_img = cv2.pyrDown(img)
|
149 |
-
lap = img - cv2.pyrUp(next_img, dstsize=img.shape[:2])
|
150 |
-
pyramid.append(lap)
|
151 |
-
img = next_img
|
152 |
-
pyramid.append(img)
|
153 |
-
return pyramid
|
154 |
-
|
155 |
-
def _blend(img1, img2, mask):
|
156 |
-
return img1 * mask + img2 * (1.0 - mask)
|
157 |
-
|
158 |
-
# Ensure mask is 3D
|
159 |
-
if mask.ndim == 2:
|
160 |
-
mask = np.repeat(mask[:, :, np.newaxis], 3, axis=2)
|
161 |
-
|
162 |
-
# Create Gaussian pyramid for mask
|
163 |
-
mask_gaus_pyramid = _gaus_pyramid(mask, depth, sigma)
|
164 |
-
|
165 |
-
# Create Laplacian pyramids for images
|
166 |
-
img1_lap_pyramid = _lap_pyramid(img1, depth, sigma)
|
167 |
-
img2_lap_pyramid = _lap_pyramid(img2, depth, sigma)
|
168 |
-
|
169 |
-
# Blend pyramids
|
170 |
-
blended_pyramid = [_blend(img1_lap, img2_lap, mask_gaus)
|
171 |
-
for img1_lap, img2_lap, mask_gaus
|
172 |
-
in zip(img1_lap_pyramid, img2_lap_pyramid, mask_gaus_pyramid)]
|
173 |
|
174 |
-
|
175 |
-
|
176 |
-
for lap in reversed(blended_pyramid[:-1]):
|
177 |
-
blended_img = cv2.pyrUp(blended_img, dstsize=lap.shape[:2])
|
178 |
-
blended_img += lap
|
179 |
|
180 |
-
|
|
|
181 |
|
182 |
-
def
|
183 |
-
|
184 |
-
bg_img = cv2.cvtColor(bg_img, cv2.COLOR_BGR2RGB)
|
185 |
-
|
186 |
-
obj_img = cv2.imread(obj_path)
|
187 |
-
obj_img = cv2.cvtColor(obj_img, cv2.COLOR_BGR2RGB)
|
188 |
-
|
189 |
-
mask_img = cv2.imread(mask_path, cv2.IMREAD_GRAYSCALE)
|
190 |
-
mask_img = np.where(mask_img > 127, 255, 0).astype(np.uint8)
|
191 |
|
192 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
# Resize mask to match object image size
|
205 |
-
mask_img = cv2.resize(mask_img, (obj_img.shape[1], obj_img.shape[0]))
|
206 |
|
207 |
-
|
208 |
-
|
209 |
-
elif blend_method == "Mixed Gradient":
|
210 |
-
blend_func = mixed_blend
|
211 |
-
else: # Laplacian
|
212 |
-
return laplacian_blend(obj_img, bg_img, np.stack((mask_img,)*3, axis=-1), 5, 25)
|
213 |
|
214 |
-
|
215 |
-
|
216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
217 |
|
218 |
return (blend_img * 255).astype(np.uint8)
|
219 |
|
|
|
1 |
import cv2
|
2 |
import numpy as np
|
3 |
+
import scipy.sparse as sp
|
4 |
+
import scipy.sparse.linalg as splin
|
5 |
+
from numba import jit
|
6 |
import gradio as gr
|
|
|
7 |
|
8 |
+
def get_image(img_path: str, mask: bool=False, scale: bool=True) -> np.array:
|
9 |
if mask:
|
10 |
+
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
|
11 |
+
_, binary_mask = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
|
12 |
+
return np.where(binary_mask == 255, 1, 0)
|
13 |
+
|
14 |
+
if scale:
|
15 |
+
return cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB).astype('double') / 255.0
|
16 |
+
|
17 |
+
return cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
|
19 |
+
@jit(nopython=True)
|
20 |
+
def build_poisson_sparse_matrix(ys, xs, im2var, img_s, img_t, mask):
|
21 |
+
nnz = len(ys)
|
22 |
img_s_h, img_s_w = img_s.shape
|
23 |
+
A_data = np.zeros(16 * nnz, dtype=np.float64)
|
24 |
+
A_rows = np.zeros(16 * nnz, dtype=np.int32)
|
25 |
+
A_cols = np.zeros(16 * nnz, dtype=np.int32)
|
26 |
+
b = np.zeros(4 * nnz, dtype=np.float64)
|
27 |
+
|
28 |
+
offsets = np.array([(0, 1), (0, -1), (1, 0), (-1, 0)])
|
29 |
+
|
30 |
+
idx = 0
|
31 |
+
for n in range(nnz):
|
32 |
+
y, x = ys[n], xs[n]
|
33 |
+
for i in range(4):
|
34 |
+
dy, dx = offsets[i]
|
35 |
+
n_y, n_x = y + dy, x + dx
|
36 |
+
e = 4 * n + i
|
37 |
+
|
38 |
+
if 0 <= n_y < img_s_h and 0 <= n_x < img_s_w:
|
39 |
+
A_data[idx] = 1
|
40 |
+
A_rows[idx] = e
|
41 |
+
A_cols[idx] = im2var[y, x]
|
42 |
+
idx += 1
|
43 |
+
|
44 |
+
b[e] = img_s[y, x] - img_s[n_y, n_x]
|
45 |
+
|
46 |
+
if im2var[n_y, n_x] != -1:
|
47 |
+
A_data[idx] = -1
|
48 |
+
A_rows[idx] = e
|
49 |
+
A_cols[idx] = im2var[n_y, n_x]
|
50 |
+
idx += 1
|
51 |
+
else:
|
52 |
+
b[e] += img_t[n_y, n_x]
|
53 |
+
|
54 |
+
return A_data[:idx], A_rows[:idx], A_cols[:idx], b
|
55 |
+
|
56 |
+
def poisson_blend_fast_jit(img_s: np.ndarray, mask: np.ndarray, img_t: np.ndarray) -> np.ndarray:
|
57 |
nnz = np.sum(mask > 0)
|
58 |
+
im2var = np.full(mask.shape, -1, dtype=np.int32)
|
59 |
im2var[mask > 0] = np.arange(nnz)
|
60 |
|
61 |
+
ys, xs = np.nonzero(mask)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
+
A_data, A_rows, A_cols, b = build_poisson_sparse_matrix(ys, xs, im2var, img_s, img_t, mask)
|
|
|
64 |
|
65 |
+
A = sp.csr_matrix((A_data, (A_rows, A_cols)), shape=(4*nnz, nnz))
|
66 |
+
v = splin.lsqr(A, b)[0]
|
|
|
|
|
67 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
img_t_out = img_t.copy()
|
69 |
+
img_t_out[mask > 0] = v[im2var[mask > 0]]
|
70 |
|
71 |
return np.clip(img_t_out, 0, 1)
|
72 |
|
73 |
+
@jit(nopython=True)
|
74 |
+
def neighbours(i: int, j: int, max_i: int, max_j: int):
|
75 |
+
pairs = []
|
76 |
+
for n in (-1, 1):
|
77 |
+
if 0 <= i+n <= max_i:
|
78 |
+
pairs.append((i+n, j))
|
79 |
+
if 0 <= j+n <= max_j:
|
80 |
+
pairs.append((i, j+n))
|
81 |
+
return pairs
|
82 |
+
|
83 |
+
@jit(nopython=True)
|
84 |
+
def build_mixed_blend_sparse_matrix(ys, xs, im2var, img_s, img_t, mask):
|
85 |
+
nnz = len(ys)
|
86 |
img_s_h, img_s_w = img_s.shape
|
87 |
+
A_data = np.zeros(8 * nnz, dtype=np.float64)
|
88 |
+
A_rows = np.zeros(8 * nnz, dtype=np.int32)
|
89 |
+
A_cols = np.zeros(8 * nnz, dtype=np.int32)
|
90 |
+
b = np.zeros(4 * nnz, dtype=np.float64)
|
91 |
+
|
92 |
+
idx = 0
|
93 |
+
e = 0
|
94 |
+
for n in range(nnz):
|
95 |
+
y, x = ys[n], xs[n]
|
96 |
+
for n_y, n_x in neighbours(y, x, img_s_h-1, img_s_w-1):
|
97 |
+
ds = img_s[y, x] - img_s[n_y, n_x]
|
98 |
+
dt = img_t[y, x] - img_t[n_y, n_x]
|
99 |
+
d = ds if abs(ds) > abs(dt) else dt
|
100 |
+
|
101 |
+
A_data[idx] = 1
|
102 |
+
A_rows[idx] = e
|
103 |
+
A_cols[idx] = im2var[y, x]
|
104 |
+
idx += 1
|
105 |
+
|
106 |
+
b[e] = d
|
107 |
+
|
108 |
+
if im2var[n_y, n_x] != -1:
|
109 |
+
A_data[idx] = -1
|
110 |
+
A_rows[idx] = e
|
111 |
+
A_cols[idx] = im2var[n_y, n_x]
|
112 |
+
idx += 1
|
113 |
+
else:
|
114 |
+
b[e] += img_t[n_y, n_x]
|
115 |
+
e += 1
|
116 |
+
|
117 |
+
return A_data[:idx], A_rows[:idx], A_cols[:idx], b[:e]
|
118 |
+
|
119 |
+
def mixed_blend_fast_jit(img_s: np.ndarray, mask: np.ndarray, img_t: np.ndarray) -> np.ndarray:
|
120 |
nnz = np.sum(mask > 0)
|
121 |
+
im2var = np.full(mask.shape, -1, dtype=np.int32)
|
122 |
im2var[mask > 0] = np.arange(nnz)
|
123 |
|
124 |
+
ys, xs = np.nonzero(mask)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
|
126 |
+
A_data, A_rows, A_cols, b = build_mixed_blend_sparse_matrix(ys, xs, im2var, img_s, img_t, mask)
|
127 |
|
128 |
+
A = sp.csr_matrix((A_data, (A_rows, A_cols)), shape=(len(b), nnz))
|
|
|
129 |
|
130 |
+
v = splin.spsolve(A.T @ A, A.T @ b)
|
|
|
131 |
|
|
|
132 |
img_t_out = img_t.copy()
|
133 |
+
img_t_out[mask > 0] = v[im2var[mask > 0]]
|
134 |
|
135 |
return np.clip(img_t_out, 0, 1)
|
136 |
|
137 |
+
def _2d_gaussian(sigma: float) -> np.ndarray:
|
138 |
+
ksize = np.int64(np.ceil(sigma)*6+1)
|
139 |
+
gaussian_1d = cv2.getGaussianKernel(ksize, sigma)
|
140 |
+
return gaussian_1d * np.transpose(gaussian_1d)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
141 |
|
142 |
+
def _low_pass_filter(img: np.ndarray, sigma: float) -> np.ndarray:
|
143 |
+
return cv2.filter2D(img, -1, _2d_gaussian(sigma))
|
|
|
|
|
|
|
144 |
|
145 |
+
def _high_pass_filter(img: np.ndarray, sigma: float) -> np.ndarray:
|
146 |
+
return img - _low_pass_filter(img, sigma)
|
147 |
|
148 |
+
def _gaus_pyramid(img: np.ndarray, depth: int, sigma: int):
|
149 |
+
_im = img.copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
|
151 |
+
pyramid = []
|
152 |
+
for d in range(depth-1):
|
153 |
+
_im = _low_pass_filter(_im.copy(), sigma)
|
154 |
+
pyramid.append(_im)
|
155 |
+
_im = cv2.pyrDown(_im)
|
156 |
+
|
157 |
+
return pyramid
|
158 |
|
159 |
+
def _lap_pyramid(img: np.ndarray, depth: int, sigma: int):
|
160 |
+
_im = img.copy()
|
161 |
+
|
162 |
+
pyramid = []
|
163 |
+
for d in range(depth-1):
|
164 |
+
lap = _high_pass_filter(_im.copy(), sigma)
|
165 |
+
pyramid.append(lap)
|
166 |
+
_im = cv2.pyrDown(_im)
|
167 |
+
|
168 |
+
return pyramid
|
|
|
|
|
169 |
|
170 |
+
def _blend(img1: np.ndarray, img2: np.ndarray, mask: np.ndarray) -> np.ndarray:
|
171 |
+
return img1 * mask + img2 * (1.0 - mask)
|
|
|
|
|
|
|
|
|
172 |
|
173 |
+
def laplacian_blend(img1: np.ndarray, img2: np.ndarray, mask: np.ndarray, depth: int, sigma: int) -> np.ndarray:
|
174 |
+
mask_gaus_pyramid = _gaus_pyramid(mask, depth, sigma)
|
175 |
+
img1_lap_pyramid, img2_lap_pyramid = _lap_pyramid(img1, depth, sigma), _lap_pyramid(img2, depth, sigma)
|
176 |
+
|
177 |
+
blended = [_blend(obj, bg, mask) for obj, bg, mask in zip(img1_lap_pyramid, img2_lap_pyramid, mask_gaus_pyramid)][::-1]
|
178 |
+
|
179 |
+
h, w = blended[0].shape[:2]
|
180 |
+
|
181 |
+
img1 = cv2.resize(img1, (w, h))
|
182 |
+
img2 = cv2.resize(img2, (w, h))
|
183 |
+
mask = cv2.resize(mask, (w, h))
|
184 |
+
|
185 |
+
blanded_img = _blend(img1, img2, mask)
|
186 |
+
blanded_img = cv2.resize(blanded_img, blended[0].shape[:2])
|
187 |
+
|
188 |
+
imgs = []
|
189 |
+
for d in range(0, depth-1):
|
190 |
+
gaussian_img = _low_pass_filter(blanded_img.copy(), sigma)
|
191 |
+
reconstructed_img = cv2.add(blended[d], gaussian_img)
|
192 |
+
|
193 |
+
imgs.append(reconstructed_img)
|
194 |
+
blanded_img = cv2.pyrUp(reconstructed_img)
|
195 |
+
|
196 |
+
return np.clip(imgs[-1], 0, 1)
|
197 |
+
|
198 |
+
def blend_images(bg_img, obj_img, mask_img, method):
|
199 |
+
# Convert images to the correct format
|
200 |
+
bg_img = cv2.cvtColor(bg_img, cv2.COLOR_RGB2BGR) / 255.0
|
201 |
+
obj_img = cv2.cvtColor(obj_img, cv2.COLOR_RGB2BGR) / 255.0
|
202 |
+
mask_img = cv2.cvtColor(mask_img, cv2.COLOR_RGB2GRAY) / 255.0
|
203 |
+
|
204 |
+
if method == "Poisson":
|
205 |
+
blend_img = np.zeros_like(bg_img)
|
206 |
+
for b in range(3):
|
207 |
+
blend_img[:,:,b] = poisson_blend_fast_jit(obj_img[:,:,b], mask_img, bg_img[:,:,b].copy())
|
208 |
+
elif method == "Mixed Gradient":
|
209 |
+
blend_img = np.zeros_like(bg_img)
|
210 |
+
for b in range(3):
|
211 |
+
blend_img[:,:,b] = mixed_blend_fast_jit(obj_img[:,:,b], mask_img, bg_img[:,:,b].copy())
|
212 |
+
elif method == "Laplacian":
|
213 |
+
mask_stack = np.stack((mask_img,) * 3, axis=-1)
|
214 |
+
blend_img = laplacian_blend(obj_img, bg_img, mask_stack, 5, 25.0)
|
215 |
|
216 |
return (blend_img * 255).astype(np.uint8)
|
217 |
|