从二值图像提取形状信息有助于对图像进行更高级的处理和识别。下面介绍如何使用findContours()搜索图像中的轮廓,并对其进行处理和计算。
轮廓检测
输出参数:image,contours轮廓的多边形列表, hierarchy轮廓的嵌套信息
fig, axes = pl.subplots(1, 2, figsize=(10, 6))
img = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/xyz.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
binary=cv2.Canny(gray,100,100)
#ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)
#检测轮廓
image,contours, hierarchy = cv2.findContours(binary,mode=cv2.RETR_TREE,method=cv2.CHAIN_APPROX_SIMPLE)
#画出轮廓
cv2.drawContours(img,contours,-1,(0,255,0),3)
axes[0].imshow(binary,cmap='binary')
axes[1].imshow(img[:,:,::-1])
轮廓匹配
通过findContours()获取轮廓之后,可以使用approxPolyDP()对其进行简化,然后通过matchShapes()比较两个简化之后的轮廓之间的近似程度。
"要从patterns.png图像中找到与targets.png中最匹配的轮廓"
#首先获取图像的轮廓信息
img_patterns = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/patterns.png", cv2.IMREAD_GRAYSCALE)
_,patterns, _ = cv2.findContours(img_patterns, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
img_targets = cv2.imread("E:/ruanjianDM/jupyternoerbookDM/picture/targets.png", cv2.IMREAD_GRAYSCALE)
_,targets, _ = cv2.findContours(img_targets, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#并将所有轮廓的坐标最小值都修改为0
patterns = [pattern - np.min(pattern, 0, keepdims=True) for pattern in patterns]
targets = [target - np.min(target, 0, keepdims=True) for target in targets]
#然后调用approxPolyDP()对轮廓进行近似处理。它的第二个参数为近似的误差允许范围,
#该值越大,近似之后的轮廓的点数越少。第三个参数指示轮廓是否为封闭形状。
patterns_simple = [cv2.approxPolyDP(pattern, 5, True) for pattern in patterns]
targets_simple = [cv2.approxPolyDP(target, 8, True) for target in targets]
#%figonly=使用`matchShapes()`比较由`approxPolyDP()`近似之后的轮廓
fig, ax = pl.subplots(figsize=(8, 8))
ax.set_aspect("equal")
width = 180
for tidx, (target, target_simple) in enumerate(zip(targets, targets_simple)):
scores = []
texts = []
for pidx, (pattern, pattern_simple) in enumerate(zip(patterns, patterns_simple)):
index = np.s_[:, 0, :]
pattern2 = pattern[index]
target2 = target[index]
pattern_simple2 = pattern_simple[index]
target_simple2 = target_simple[index]
x0 = pidx * width + width
y0 = tidx * width + width
if tidx == 0:
pattern_poly = pl.Polygon(pattern2 + [x0, 0], color="black", alpha=0.6)
ax.add_patch(pattern_poly)
text = ax.text(x0 + width * 0.3, -50, str(pidx), fontsize=14, ha="center")
if pidx == 0:
target_poly = pl.Polygon(target2 + [0, y0], color="green", alpha=0.6)
ax.add_patch(target_poly)
text = ax.text(-50, y0 + width * 0.3, str(tidx), fontsize=14, ha="center")
pattern_simple_poly = pl.Polygon(pattern_simple2 + [x0, 0], facecolor="none", alpha=0.6)
ax.add_patch(pattern_simple_poly)
target_simple_poly = pl.Polygon(target_simple2 + [0, y0], facecolor="none", alpha=0.6)
ax.add_patch(target_simple_poly)
#比较两个形状的近似程度
score = cv2.matchShapes(target_simple, pattern_simple, 3, 0)
text = ax.text(x0 + width * 0.3, y0 + width * 0.2, "{:5.4f}".format(score),
ha="center", va="center", fontsize=16)
scores.append(score)
texts.append(text)
best_index = np.argmin(scores)
texts[best_index].set_color("red")
ax.relim()
ax.set_axis_off()
ax.autoscale();