七、图像金字塔与轮廓检测
1. 图像轮廓
(1)概念
cv2.findContours(img, mode, method)
mode(轮廓检测模式):
- RETR_EXTERNAL:只检测最外面的轮廓
- RETR_LIST:检测所有的轮廓,并将其保存到链表中
- RETR_CCOMP:检索所有的轮廓,并将他们组织为两层,第一层为各部分的外部边界,第二层为空洞的边界
- RETR_TREE:检索所有的轮廓,并重构嵌套轮廓的整个层次
method(轮廓逼近的方法):
- CHAIN_APPROX_NONE:以 Freeman 链码的方式输出轮廓,所有其他方法输出多边形(顶点的序列)
- CHAIN_APPROX_SIMPLE:压缩水平的、垂直的和斜的部分,即函数只保留他们的终点部分

import cv2 # OpenCV 读取的格式是 BGR
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
img = cv2.imread('ysg.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 使用二值图像提高准确率
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
plt.imshow(thresh, cmap='gray')
plt.show()
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)(2)轮廓绘制
img_cp = img.copy()
# 参数:需绘制的图像,轮廓(-1为绘制所有轮廓),颜色模式,线条厚度
res = cv2.drawContours(img_cp, contours, -1, (0, 0, 255), 1)
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()
(3)轮廓特征
cnt = contours[0]
# 面积
cv2.contourArea(cnt)
136.0# 周长,True表示闭合
cv2.arcLength(cnt, True)
49.656854152679442. 模板匹配
(1)概念
模板匹配类似于卷积原理,模板在原图像上从原点开始滑动,计算模板与(图像被覆盖的地方)的差别程度(OpenCV中共6种计算方法),然后将每一次计算结果放入一个矩阵,作为结果输出。
6种计算方法:
- TM_SQDIFF:计算平方不同,计算结果值越小,越相关
- TM_CCORR:计算相关性,计算结果值越大,越相关
- TM_CCOEFF:计算相关系数,计算结果值越大,越相关
- TM_SQDIFF_NORMED:计算归一化平方不同,计算结果值越接近0,越相关
- TM_CCORR_NORMED:计算归一化相关性,计算结果值越接近1,越相关
TM_CCOEFF_NORMED:计算归一化相关系数,计算结果值越接近1,越相关

如:原图像大小为 A x B,模板大小为 a x b,则输出结果矩阵是(A - a + 1) x (B - b + 1)
img = cv2.imread('ysg.png', 0)
template = cv2.imread('ysg_template.png', 0)
h, w = template.shape[:2]img.shape
(310, 341)template.shape
(161, 144)res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
res.shape
(150, 198)min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 最小值
min_val
0.0
# 最大值
max_val
217423616.0
# 最小值位置
min_loc
(67, 10)
# 最大值位置
max_loc
(189, 97)(2)示例
methods = ['cv2.TM_SQDIFF', 'cv2.TM_CCORR', 'cv2.TM_CCOEFF', 'cv2.TM_SQDIFF_NORMED', 'cv2.TM_CCORR_NORMED', 'cv2.TM_CCOEFF_NORMED']
for item in methods:
img_copy = img.copy()
# 匹配方法的真值
method = eval(item)
print(method)
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 如果平方差匹配TM_SQDIFF,或归一化平方差匹配TM_SQDIFF_NORMED,取最小值
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
cv2.rectangle(img_copy, top_left, bottom_right, 255, 2)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img_copy, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(item)
plt.show()





# 匹配多个对象
match = cv2.imread('ysg_match.png')
match_gray = cv2.cvtColor(match, cv2.COLOR_BGR2GRAY)
template = cv2.imread('ysg_template.png', 0)
h, w = template.shape[:2]
res = cv2.matchTemplate(match_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
# 筛选匹配程度大于等于80%的
for pt in zip(*loc[::-1]):
bottom_right = (pt[0] + w, pt[1] + h)
cv2.rectangle(match, pt, bottom_right, (0, 0, 255), 1)
plt.imshow(cv2.cvtColor(match, cv2.COLOR_BGR2RGB))
plt.show()
3. 图像金字塔
高斯金字塔

向下采样(缩小)

# 向下采样 img_down = cv2.pyrDown(img) plt.imshow(cv2.cvtColor(img_down, cv2.COLOR_BGR2RGB)) plt.show()
img.shape (310, 341, 3) img_down.shape (155, 171, 3)向上采样(放大)

# 向上采样 img = cv2.imread('ysg.png') img_up = cv2.pyrUp(img) plt.imshow(cv2.cvtColor(img_up, cv2.COLOR_BGR2RGB)) plt.show()
img.shape (310, 341, 3) img_up.shape (620, 682, 3)
拉普拉斯金字塔


img = cv2.imread('ysg.png') down = cv2.pyrDown(img) down_up = cv2.pyrUp(down) if img.shape != down_up.shape: img = cv2.resize(img, (down_up.shape[1], down_up.shape[0])) res = img - down_up plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)) plt.show()
4. 轮廓近似
outline = cv2.imread('outline.png')
gray = cv2.cvtColor(outline, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
outline_cp = outline.copy()
res = cv2.drawContours(outline_cp, [cnt], -1, (0, 0, 255), 2)
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()
# 值越小,越接近原始轮廓
epsilon = 0.1 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
outline_cp1 = outline.copy()
res = cv2.drawContours(outline_cp1, [approx], -1, (0, 0, 255), 2)
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()
5. 边界矩形
outline = cv2.imread('outline.png')
gray = cv2.cvtColor(outline, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
cnt = contours[0]
x, y, w, h = cv2.boundingRect(cnt)
res = cv2.rectangle(outline, (x, y), (x + w, y + h), (0, 0, 255), 2)
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()
# 轮廓面积与边界矩形比
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print(extent)
0.50601736972704716. 外接圆
(x, y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x), int(y))
radius = int(radius)
res = cv2.circle(res, center, radius, (0, 0, 255), 2)
plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB))
plt.show()
评论 (0)