gokaygokay commited on
Commit
3bcb760
·
verified ·
1 Parent(s): 903793a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +178 -180
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(img, mask=False):
9
  if mask:
10
- if isinstance(img, str):
11
- img = cv2.imread(img, cv2.IMREAD_GRAYSCALE)
12
- elif img.ndim == 3:
13
- img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
14
- return np.where(img > 127, 1, 0)
15
- else:
16
- if isinstance(img, str):
17
- img = cv2.imread(img)
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
- def poisson_blend(img_s, mask, img_t):
 
 
33
  img_s_h, img_s_w = img_s.shape
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  nnz = np.sum(mask > 0)
35
- im2var = np.full(mask.shape, -1, dtype='int32')
36
  im2var[mask > 0] = np.arange(nnz)
37
 
38
- ys, xs = np.where(mask == 1)
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
- # Compute differences
49
- d = img_s[ys, xs][:, np.newaxis] - img_s[y_n, x_n]
50
 
51
- # Construct sparse matrix A and vector b
52
- rows = np.arange(4*nnz)
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[ys, xs] = v
74
 
75
  return np.clip(img_t_out, 0, 1)
76
 
77
- def mixed_blend(img_s, mask, img_t):
 
 
 
 
 
 
 
 
 
 
 
 
78
  img_s_h, img_s_w = img_s.shape
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  nnz = np.sum(mask > 0)
80
- im2var = np.full(mask.shape, -1, dtype='int32')
81
  im2var[mask > 0] = np.arange(nnz)
82
 
83
- ys, xs = np.where(mask == 1)
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
- A += sp.sparse.csr_matrix((data_n, (rows_n, cols_n)), shape=(4*nnz, nnz))
113
 
114
- b = d.ravel()
115
- b[~mask_n.ravel()] += img_t[y_n, x_n][~mask_n]
116
 
117
- # Solve the system
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[ys, xs] = v
123
 
124
  return np.clip(img_t_out, 0, 1)
125
 
126
- def laplacian_blend(img1, img2, mask, depth=5, sigma=25):
127
- def _2d_gaussian(sigma):
128
- ksize = int(np.ceil(sigma) * 6 + 1)
129
- gaussian_1d = cv2.getGaussianKernel(ksize, sigma)
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
- # Reconstruct image
175
- blended_img = blended_pyramid[-1]
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
- return np.clip(blended_img, 0, 1)
 
181
 
182
- def load_example_images(bg_path, obj_path, mask_path):
183
- bg_img = cv2.imread(bg_path)
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
- return bg_img, obj_img, mask_img
 
 
 
 
 
 
193
 
194
- # Modify the blend_images function to accept numpy arrays directly
195
- def blend_images(bg_img, obj_img, mask_img, blend_method):
196
- bg_img = get_image(bg_img)
197
- obj_img = get_image(obj_img)
198
- mask_img = get_image(mask_img, mask=True)
199
-
200
- # Ensure mask is 2D
201
- if mask_img.ndim == 3:
202
- mask_img = mask_img[:,:,0] # Take the first channel if it's 3D
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
- if blend_method == "Poisson":
208
- blend_func = poisson_blend
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
- blend_img = np.zeros(bg_img.shape)
215
- for b in range(3):
216
- blend_img[:,:,b] = blend_func(obj_img[:,:,b], mask_img, bg_img[:,:,b].copy())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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