-
Notifications
You must be signed in to change notification settings - Fork 1
/
poisson_image_editing.py
124 lines (92 loc) · 3.22 KB
/
poisson_image_editing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
"""Poisson image editing.
"""
import numpy as np
import cv2
import scipy.sparse
from scipy.sparse.linalg import spsolve
from os import path
def laplacian_matrix(n, m):
"""Generate the Poisson matrix.
Refer to:
https://en.wikipedia.org/wiki/Discrete_Poisson_equation
Note: it's the transpose of the wiki's matrix
"""
mat_D = scipy.sparse.lil_matrix((m, m))
mat_D.setdiag(-1, -1)
mat_D.setdiag(4)
mat_D.setdiag(-1, 1)
mat_A = scipy.sparse.block_diag([mat_D] * n).tolil()
mat_A.setdiag(-1, 1*m)
mat_A.setdiag(-1, -1*m)
return mat_A
def poisson_edit(source, target, mask, offset):
"""The poisson blending function.
Refer to:
Perez et. al., "Poisson Image Editing", 2003.
"""
# Assume:
# target is not smaller than source.
# shape of mask is same as shape of target.
y_max, x_max = target.shape[:-1]
y_min, x_min = 0, 0
x_range = x_max - x_min
y_range = y_max - y_min
M = np.float32([[1,0,offset[0]],[0,1,offset[1]]])
source = cv2.warpAffine(source,M,(x_range,y_range))
mask = mask[y_min:y_max, x_min:x_max]
mask[mask != 0] = 1
#mask = cv2.threshold(mask, 127, 1, cv2.THRESH_BINARY)
mat_A = laplacian_matrix(y_range, x_range)
# for \Delta g
laplacian = mat_A.tocsc()
# set the region outside the mask to identity
for y in range(1, y_range - 1):
for x in range(1, x_range - 1):
if mask[y, x] == 0:
k = x + y * x_range
mat_A[k, k] = 1
mat_A[k, k + 1] = 0
mat_A[k, k - 1] = 0
mat_A[k, k + x_range] = 0
mat_A[k, k - x_range] = 0
# corners
# mask[0, 0]
# mask[0, y_range-1]
# mask[x_range-1, 0]
# mask[x_range-1, y_range-1]
mat_A = mat_A.tocsc()
mask_flat = mask.flatten()
for channel in range(source.shape[2]):
source_flat = source[y_min:y_max, x_min:x_max, channel].flatten()
target_flat = target[y_min:y_max, x_min:x_max, channel].flatten()
#concat = source_flat*mask_flat + target_flat*(1-mask_flat)
# inside the mask:
# \Delta f = div v = \Delta g
alpha = 1
mat_b = laplacian.dot(source_flat)*alpha
# outside the mask:
# f = t
mat_b[mask_flat==0] = target_flat[mask_flat==0]
x = spsolve(mat_A, mat_b)
#print(x.shape)
x = x.reshape((y_range, x_range))
#print(x.shape)
x[x > 255] = 255
x[x < 0] = 0
x = x.astype('uint8')
#x = cv2.normalize(x, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX)
#print(x.shape)
target[y_min:y_max, x_min:x_max, channel] = x
return target
def main():
scr_dir = 'figs/example1'
out_dir = scr_dir
source = cv2.imread(path.join(scr_dir, "source1.jpg"))
target = cv2.imread(path.join(scr_dir, "target1.jpg"))
mask = cv2.imread(path.join(scr_dir, "mask1.png"),
cv2.IMREAD_GRAYSCALE)
offset = (0,66)
result = poisson_edit(source, target, mask, offset)
cv2.imwrite(path.join(out_dir, "possion1.png"), result)
if __name__ == '__main__':
main()