OpenCV Recipes:Seam Carving(接缝裁剪)

In this post, we are going to learn about content-aware image resizing, which is also known as seam carving.

为什么我们关心 Seam Carving?

在我们开始讨论 Seam Carving 之前,我们首先需要了解为什么需要 Seam Carving。为什么我们应该关心图像内容?为什么我们不能只调整给定图像的大小?为了回答这些问题,让我们考虑以下图像:

现在,让我们假设我们希望缩小这个图像的宽度,同时保持高度不变。如果我们这样做,它会看起来像这样:

正如你所看到的,图像中的鸭子看起来是歪斜的,图像的整体质量有所下降。直观地说,我们可以说鸭子是图像中感兴趣部分。所以,当我们调整图像的大小时,我们希望鸭子完好无损。这就是 Seam Carving 引入的原因。使用 Seam Carving,我们可以检测这些感兴趣区域,确保它们不会退化。

它是如何工作的?

我们一直在讨论图像大小调整,以及在调整图像大小时应该如何考虑图像的内容。那么为什么要称之为 Seam Carving 呢?它更应该被称为内容感知(content-aware)图像调整,对吧?有许多用来描述这一过程的不同术语,如图像重定位、液体缩放、Seam Carving 等等。又由于我们调整图像的方式,所以它被称为 Seam Carving。该算法由 Shai Avidan 和 Ariel Shamir 提出。可以点此查阅原始文件。

我们的目标是调整给定图像的大小并保持感兴趣内容完整。我们可以通过寻找图像中最不重要的路径来做到这一点。这些路径称为接缝(seams)。一旦我们找到这些接缝,就可以将它们从图像中移除或拉伸。这种移除、拉伸或裁剪(carving)的过程最终会产生一幅被调整的图像。这就是我们称之为 Seam Carving 的原因。考虑以下图像:

在上面的图像中,我们可以看到如何将图像粗略地分为感兴趣和不感兴趣的部分。让我们考虑鸭子的图像和必须处理的约束,我们希望保持高度不变并减小宽度。这意味着我们需要在图像中找到垂直接缝并将其移除。这些接缝从顶部开始,从底部结束(反之亦然)。

如何定义感兴趣(interesting)?

在开始计算接缝之前,需要找出要使用什么指标/度量来计算接缝。我们需要一种方法来给每个像素分配重要度(importance),这样就可以识别最不重要的路径。在计算机视觉术语中,我们说我们需要给每个像素分配一个能量(energy)值,这样我们就可以找到最小能量的路径。想出一个分配能量值的好方法是非常重要的,因为它会影响输出的质量。

我们使用的度量之一是每个像素点导数值,这是其邻域活跃水平的一个很好的指标。如果活跃,那么像素值将迅速变化,因此该点的导数值将会很高。

对于每个像素位置,通过对该点的 x 和 y 导数求和来计算能量。通过计算当前像素与其邻居像素之间的差值来计算导数。一旦计算了这些值,就可以将它们存储在一个称为能量矩阵,该矩阵用于定义接缝。

如何计算接缝?

有了能量矩阵,就可以计算接缝了。我们需要找到图像中具有最少的能量路径。计算所有可能路径的代价是高昂的,因此我们需要找到一种更智能的方法来实现这一点。这时候就需要使用动态规划。事实上,Seam Carving 是动态规划的直接应用。

我们需要从第一行中的每个像素开始,找到到达最后一行的方法。为了找到能量最小的路径,我们在表格中计算并存储每个像素的最佳路径。一旦我们构建了这个表,就可以通过回溯表中的行找到特定像素的路径。

对于当前行中的每个像素,我们计算下一行中我们可以移动到的三个可能像素位置的能量,即,左下角、右下角和右下角。我们不断重复这个过程,直到我们到达最后一行。一旦我们到达底部,我们取累加值最小的那一个,然后回溯到顶部。这将会给我们一条能量最少的路径。每次我们移除一条接缝,图像的宽度会减少一个像素。因此,我们需要不断去除这些接缝,直到达到想要的图像尺寸。

首先,我们将提供一组函数来计算图像中的能量、定位其接缝、并绘制路径。

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
# Draw vertical seam on top of the image
def overlay_vertical_seam(img, seam):
img_seam_overlay = np.copy(img)

# Extract the list of points from the seam
x_coords, y_coords = np.transpose([(i,int(j)) for i,j in enumerate(seam)])

# Draw a green line on the image using the list of points
img_seam_overlay[x_coords, y_coords] = (0,255,0)

return img_seam_overlay

# Compute the energy matrix from the input image
def compute_energy_matrix(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Compute X derivative of the image
sobel_x = cv2.Sobel(gray,cv2.CV_64F, 1, 0, ksize=3)

# Compute Y derivative of the image
sobel_y = cv2.Sobel(gray,cv2.CV_64F, 0, 1, ksize=3)

abs_sobel_x = cv2.convertScaleAbs(sobel_x)
abs_sobel_y = cv2.convertScaleAbs(sobel_y)

# Return weighted summation of the two images i.e. 0.5*X + 0.5*Y
return cv2.addWeighted(abs_sobel_x, 0.5, abs_sobel_y, 0.5, 0)

# Find vertical seam in the input image
def find_vertical_seam(img, energy):
rows, cols = img.shape[:2]
# Initialize the seam vector with 0 for each element
seam = np.zeros(img.shape[0])
# Initialize distance and edge matrices
dist_to = np.zeros(img.shape[:2]) + float('inf')
dist_to[0,:] = np.zeros(img.shape[1])
edge_to = np.zeros(img.shape[:2])
# Dynamic programming; iterate using double loop and compute the paths
# efficiently
for row in range(rows-1):
for col in range(cols):
if col != 0 and (dist_to[row+1, col-1] > dist_to[row, col]
+ energy[row+1, col-1]):

dist_to[row+1, col-1] = dist_to[row, col] + energy[row+1,
col-1]
edge_to[row+1, col-1] = 1

if dist_to[row+1, col] > dist_to[row, col] + energy[row+1,
col]:

dist_to[row+1, col] = dist_to[row, col] + energy[row+1,
col]
edge_to[row+1, col] = 0

if col != cols-1 and (dist_to[row+1, col+1] > dist_to[row, col]
+ energy[row+1, col+1]):

dist_to[row+1, col+1] = dist_to[row, col] + energy[row+1,
col+1]
edge_to[row+1, col+1] = -1

# Retracing the path
# Returns the indices of the minimum values along X axis.
seam[rows-1] = np.argmin(dist_to[rows-1, :])
for i in (x for x in reversed(range(rows)) if x > 0):
seam[i-1] = seam[i] + edge_to[i, int(seam[i])]

return seam

让我们再次考虑我们的鸭子图。如果我们计算前 30 个接缝,它将如下所示:

这些绿线表示最不重要的路径。正如我们在这里看到的,它们绕过了鸭子,确保感兴趣区域没有被触及。在图像的上半部分,接缝围绕着树枝,这样质量得以保持。从技术上讲,树枝也很有趣。如果我们继续并移除前100个接缝,它将如下所示:

现在,将它与朴素调整的图像进行比较。在这个版本中鸭子看起来更好。

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
import sys
import cv2
import numpy as np


# Draw vertical seam on top of the image
def overlay_vertical_seam(img, seam):
img_seam_overlay = np.copy(img)

# Extract the list of points from the seam
x_coords, y_coords = np.transpose([(i,int(j)) for i,j in enumerate(seam)])

# Draw a green line on the image using the list of points
img_seam_overlay[x_coords, y_coords] = (0,255,0)

return img_seam_overlay

# Compute the energy matrix from the input image
def compute_energy_matrix(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Compute X derivative of the image
sobel_x = cv2.Sobel(gray,cv2.CV_64F, 1, 0, ksize=3)

# Compute Y derivative of the image
sobel_y = cv2.Sobel(gray,cv2.CV_64F, 0, 1, ksize=3)

abs_sobel_x = cv2.convertScaleAbs(sobel_x)
abs_sobel_y = cv2.convertScaleAbs(sobel_y)

# Return weighted summation of the two images i.e. 0.5*X + 0.5*Y
return cv2.addWeighted(abs_sobel_x, 0.5, abs_sobel_y, 0.5, 0)

# Find vertical seam in the input image
def find_vertical_seam(img, energy):
rows, cols = img.shape[:2]
# Initialize the seam vector with 0 for each element
seam = np.zeros(img.shape[0])
# Initialize distance and edge matrices
dist_to = np.zeros(img.shape[:2]) + float('inf')
dist_to[0,:] = np.zeros(img.shape[1])
edge_to = np.zeros(img.shape[:2])
# Dynamic programming; iterate using double loop and compute the paths
# efficiently
for row in range(rows-1):
for col in range(cols):
if col != 0 and (dist_to[row+1, col-1] > dist_to[row, col]
+ energy[row+1, col-1]):

dist_to[row+1, col-1] = dist_to[row, col] + energy[row+1,
col-1]
edge_to[row+1, col-1] = 1

if dist_to[row+1, col] > dist_to[row, col] + energy[row+1,
col]:

dist_to[row+1, col] = dist_to[row, col] + energy[row+1,
col]
edge_to[row+1, col] = 0

if col != cols-1 and (dist_to[row+1, col+1] > dist_to[row, col]
+ energy[row+1, col+1]):

dist_to[row+1, col+1] = dist_to[row, col] + energy[row+1,
col+1]
edge_to[row+1, col+1] = -1

# Retracing the path
# Returns the indices of the minimum values along X axis.
seam[rows-1] = np.argmin(dist_to[rows-1, :])
for i in (x for x in reversed(range(rows)) if x > 0):
seam[i-1] = seam[i] + edge_to[i, int(seam[i])]

return seam

# Remove the input vertical seam from the image
def remove_vertical_seam(img, seam):
rows, cols = img.shape[:2]

# To delete a point, move every point after it one step towards the
# left
for row in range(rows):
for col in range(int(seam[row]), cols-1):
img[row, col] = img[row, col+1]

# Discard the last column to create the final output image
img = img[:, 0:cols-1]
return img

if __name__=='__main__':
# Make sure the size of the input image is reasonable.
# Large images take a lot of time to be processed.
# Recommended size is 640x480.
img_input = cv2.imread(sys.argv[1])
# Use a small number to get started. Once you get an
# idea of the processing time, you can use a bigger number.
# To get started, you can set it to 20.
num_seams = int(sys.argv[2])
img = np.copy(img_input)
img_overlay_seam = np.copy(img_input)
energy = compute_energy_matrix(img)
for i in range(num_seams):
seam = find_vertical_seam(img, energy)
img_overlay_seam = overlay_vertical_seam(img_overlay_seam, seam)
img = remove_vertical_seam(img, seam)
energy = compute_energy_matrix(img)
print('Number of seams removed = ', i+1)

cv2.imshow('Input', img_input)
cv2.imshow('Seams', img_overlay_seam)
cv2.imshow('Output', img)
cv2.waitKey()

我们能扩展图像吗?

我们知道可以使用 Seam Carving 来缩小图像的宽度,而不会恶化感兴趣区域。因此,自然地产生了另一个问题,我们是否可以在不恶化感兴趣区域的情况下扩展图像。事实证明,我们可以用同样的逻辑来做这件事。当我们计算接缝时,我们只需要添加一列而不是删除一列。

如果我们朴素地扩展鸭子的图,它会是这样的:

如果我们通过使用 Seam Carving 来做,它看起来会是这样的:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
import sys
import cv2
import numpy as np


# Draw vertical seam on top of the image
def overlay_vertical_seam(img, seam):
img_seam_overlay = np.copy(img)

# Extract the list of points from the seam
x_coords, y_coords = np.transpose([(i,int(j)) for i,j in enumerate(seam)])

# Draw a green line on the image using the list of points
img_seam_overlay[x_coords, y_coords] = (0,255,0)

return img_seam_overlay

# Compute the energy matrix from the input image
def compute_energy_matrix(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Compute X derivative of the image
sobel_x = cv2.Sobel(gray,cv2.CV_64F, 1, 0, ksize=3)

# Compute Y derivative of the image
sobel_y = cv2.Sobel(gray,cv2.CV_64F, 0, 1, ksize=3)

abs_sobel_x = cv2.convertScaleAbs(sobel_x)
abs_sobel_y = cv2.convertScaleAbs(sobel_y)

# Return weighted summation of the two images i.e. 0.5*X + 0.5*Y
return cv2.addWeighted(abs_sobel_x, 0.5, abs_sobel_y, 0.5, 0)

# Find vertical seam in the input image
def find_vertical_seam(img, energy):
rows, cols = img.shape[:2]
# Initialize the seam vector with 0 for each element
seam = np.zeros(img.shape[0])
# Initialize distance and edge matrices
dist_to = np.zeros(img.shape[:2]) + float('inf')
dist_to[0,:] = np.zeros(img.shape[1])
edge_to = np.zeros(img.shape[:2])
# Dynamic programming; iterate using double loop and compute the paths
# efficiently
for row in range(rows-1):
for col in range(cols):
if col != 0 and (dist_to[row+1, col-1] > dist_to[row, col]
+ energy[row+1, col-1]):

dist_to[row+1, col-1] = dist_to[row, col] + energy[row+1,
col-1]
edge_to[row+1, col-1] = 1

if dist_to[row+1, col] > dist_to[row, col] + energy[row+1,
col]:

dist_to[row+1, col] = dist_to[row, col] + energy[row+1,
col]
edge_to[row+1, col] = 0

if col != cols-1 and (dist_to[row+1, col+1] > dist_to[row, col]
+ energy[row+1, col+1]):

dist_to[row+1, col+1] = dist_to[row, col] + energy[row+1,
col+1]
edge_to[row+1, col+1] = -1

# Retracing the path
# Returns the indices of the minimum values along X axis.
seam[rows-1] = np.argmin(dist_to[rows-1, :])
for i in (x for x in reversed(range(rows)) if x > 0):
seam[i-1] = seam[i] + edge_to[i, int(seam[i])]

return seam

# Remove the input vertical seam from the image
def remove_vertical_seam(img, seam):
rows, cols = img.shape[:2]

# To delete a point, move every point after it one step towards the
# left
for row in range(rows):
for col in range(int(seam[row]), cols-1):
img[row, col] = img[row, col+1]

# Discard the last column to create the final output image
img = img[:, 0:cols-1]
return img

# Add a vertical seam to the image
def add_vertical_seam(img, seam, num_iter):
seam = seam + num_iter
rows, cols = img.shape[:2]
zero_col_mat = np.zeros((rows,1,3), dtype=np.uint8)
img_extended = np.hstack((img, zero_col_mat))

for row in range(rows):
for col in range(cols, int(seam[row]), -1):
img_extended[row, col] = img[row, col-1]

# To insert a value between two columns, take the average
# value of the neighbors. It looks smooth this way and we
# can avoid unwanted artifacts.
for i in range(3):
v1 = img_extended[row, int(seam[row])-1, i]
v2 = img_extended[row, int(seam[row])+1, i]
img_extended[row, int(seam[row]), i] = (int(v1)+int(v2))/2

return img_extended

if __name__=='__main__':
# Make sure the size of the input image is reasonable.
# Large images take a lot of time to be processed.
# Recommended size is 640x480.
img_input = cv2.imread(sys.argv[1])
# Use a small number to get started. Once you get an
# idea of the processing time, you can use a bigger number.
# To get started, you can set it to 20.
num_seams = int(sys.argv[2])
img = np.copy(img_input)
img_output = np.copy(img_input)
img_overlay_seam = np.copy(img_input)
energy = compute_energy_matrix(img)

for i in range(num_seams):
seam = find_vertical_seam(img, energy)
img_overlay_seam = overlay_vertical_seam(img_overlay_seam, seam)
img = remove_vertical_seam(img, seam)
img_output = add_vertical_seam(img_output, seam, i)
energy = compute_energy_matrix(img)
print('Number of seams added =', i+1)

cv2.imshow('Input', img_input)
cv2.imshow('Seams', img_overlay_seam)
cv2.imshow('Output', img_output)

cv2.waitKey()

能完全移除一个物体吗?

这也许是 Seam Carving 最有趣的应用。我们可以让一个物体从图像中完全消失。让我们考虑以下图像:

用鼠标圈出要移除的区域:

移除右侧的椅子后,它会看起来像这样:

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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import sys
import cv2
import numpy as np


# Compute the energy matrix from the input image
def compute_energy_matrix(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Compute X derivative of the image
sobel_x = cv2.Sobel(gray,cv2.CV_64F, 1, 0, ksize=3)

# Compute Y derivative of the image
sobel_y = cv2.Sobel(gray,cv2.CV_64F, 0, 1, ksize=3)

abs_sobel_x = cv2.convertScaleAbs(sobel_x)
abs_sobel_y = cv2.convertScaleAbs(sobel_y)

# Return weighted summation of the two images i.e. 0.5*X + 0.5*Y
return cv2.addWeighted(abs_sobel_x, 0.5, abs_sobel_y, 0.5, 0)

# Find vertical seam in the input image
def find_vertical_seam(img, energy):
rows, cols = img.shape[:2]
# Initialize the seam vector with 0 for each element
seam = np.zeros(img.shape[0])
# Initialize distance and edge matrices
dist_to = np.zeros(img.shape[:2]) + float('inf')
dist_to[0,:] = np.zeros(img.shape[1])
edge_to = np.zeros(img.shape[:2])
# Dynamic programming; iterate using double loop and compute the paths
# efficiently
for row in range(rows-1):
for col in range(cols):
if col != 0 and (dist_to[row+1, col-1] > dist_to[row, col]
+ energy[row+1, col-1]):

dist_to[row+1, col-1] = dist_to[row, col] + energy[row+1,
col-1]
edge_to[row+1, col-1] = 1

if dist_to[row+1, col] > dist_to[row, col] + energy[row+1,
col]:

dist_to[row+1, col] = dist_to[row, col] + energy[row+1,
col]
edge_to[row+1, col] = 0

if col != cols-1 and (dist_to[row+1, col+1] > dist_to[row, col]
+ energy[row+1, col+1]):

dist_to[row+1, col+1] = dist_to[row, col] + energy[row+1,
col+1]
edge_to[row+1, col+1] = -1

# Retracing the path
# Returns the indices of the minimum values along X axis.
seam[rows-1] = np.argmin(dist_to[rows-1, :])
for i in (x for x in reversed(range(rows)) if x > 0):
seam[i-1] = seam[i] + edge_to[i, int(seam[i])]

return seam

# Remove the input vertical seam from the image
def remove_vertical_seam(img, seam):
rows, cols = img.shape[:2]

# To delete a point, move every point after it one step towards the
# left
for row in range(rows):
for col in range(int(seam[row]), cols-1):
img[row, col] = img[row, col+1]

# Discard the last column to create the final output image
img = img[:, 0:cols-1]
return img

# Add a vertical seam to the image
def add_vertical_seam(img, seam, num_iter):
seam = seam + num_iter
rows, cols = img.shape[:2]
zero_col_mat = np.zeros((rows,1,3), dtype=np.uint8)
img_extended = np.hstack((img, zero_col_mat))

for row in range(rows):
for col in range(cols, int(seam[row]), -1):
img_extended[row, col] = img[row, col-1]

# To insert a value between two columns, take the average
# value of the neighbors. It looks smooth this way and we
# can avoid unwanted artifacts.
for i in range(3):
v1 = img_extended[row, int(seam[row])-1, i]
v2 = img_extended[row, int(seam[row])+1, i]
img_extended[row, int(seam[row]), i] = (int(v1)+int(v2))/2

return img_extended

# Draw rectangle on top of the input image
def draw_rectangle(event, x, y, flags, params):
global x_init, y_init, drawing, top_left_pt, bottom_right_pt, img_orig

# Detecting a mouse click
if event == cv2.EVENT_LBUTTONDOWN:
drawing = True
x_init, y_init = x, y

# Detecting mouse movement
elif event == cv2.EVENT_MOUSEMOVE:
if drawing:
top_left_pt, bottom_right_pt = (x_init,y_init), (x,y)
img[y_init:y, x_init:x] = 255 - img_orig[y_init:y, x_init:x]
cv2.rectangle(img, top_left_pt, bottom_right_pt, (0,255,0), 2)

# Detecting the mouse button up event
elif event == cv2.EVENT_LBUTTONUP:
drawing = False
top_left_pt, bottom_right_pt = (x_init,y_init), (x,y)

# Create the "negative" film effect for the selected
# region
img[y_init:y, x_init:x] = 255 - img[y_init:y, x_init:x]

# Draw rectangle around the selected region
cv2.rectangle(img, top_left_pt, bottom_right_pt, (0,255,0), 2)
rect_final = (x_init, y_init, x-x_init, y-y_init)

# Remove the object in the selected region
remove_object(img_orig, rect_final)

# Computing the energy matrix using modified algorithm
def compute_energy_matrix_modified(img, rect_roi):
# Compute weighted summation i.e. 0.5*X + 0.5*Y
energy_matrix = compute_energy_matrix(img)
x,y,w,h = rect_roi

# We want the seams to pass through this region, so make sure the
# energy values in this region are set to 0
energy_matrix[y:y+h, x:x+w] = 0

return energy_matrix

# Remove the object from the input region of interest
def remove_object(img, rect_roi):
num_seams = rect_roi[2] + 10
energy = compute_energy_matrix_modified(img, rect_roi)

# Start a loop and rsemove one seam at a time
for i in range(num_seams):
# Find the vertical seam that can be removed
seam = find_vertical_seam(img, energy)

# Remove that vertical seam
img = remove_vertical_seam(img, seam)
x,y,w,h = rect_roi

# Compute energy matrix after removing the seam
energy = compute_energy_matrix_modified(img, (x,y,w-i,h))
print('Number of seams removed =', i+1)

img_output = np.copy(img)

# Fill up the region with surrounding values so that the size
# of the image remains unchanged
for i in range(num_seams):
seam = find_vertical_seam(img, energy)
img = remove_vertical_seam(img, seam)
img_output = add_vertical_seam(img_output, seam, i)
energy = compute_energy_matrix(img)
print('Number of seams added =', i+1)

cv2.imshow('Input', img_input)
cv2.imshow('Output', img_output)
cv2.waitKey()


if __name__=='__main__':
img_input = cv2.imread(sys.argv[1])
drawing = False
img = np.copy(img_input)
img_orig = np.copy(img_input)

cv2.namedWindow('Input')
cv2.setMouseCallback('Input', draw_rectangle)
print('Draw a rectangle with the mouse over the object to be removed')
while True:
cv2.imshow('Input', img)
c = cv2.waitKey(10)
if c == 27:
break

cv2.destroyAllWindows()

背后细节

这里基本逻辑保持不变,我们正在用 seam carving 移除物体。一旦我们选择了感兴趣区域,我们就让所有的接缝都穿过这个区域。通过在每次迭代后操纵能量矩阵来实现这一点。我们增加了一个新的函数,叫做 compute_energy_matrix_modified,一旦我们计算了能量矩阵,我们就将这个感兴趣区域赋值为零。这样,我们迫使所有接缝都穿过这个区域。移除与该区域相关的所有接缝后,我们继续添加接缝,直到将图像扩展到其原始宽度。

GreatX wechat
Subscribe to my blog by scanning my public wechat account