下面完整代码在github仓库:传送门
文章目录
- 一、图像轮廓近似估计
- 二、边界检测
- 三、Canny算子(找轮廓)
- 四、计算图像面积、周长、重心
- 五、计算轮廓面积、凸包面积
- 六、凸包和凸性检测
- 七、图像轮廓查找与绘制
- 八、Hough空间(形状检测)
- 九、分水岭算法
- 十、模版匹配
- 十一、利用对象掩码mask
- 十二、利用形态学操作寻找车牌
- 十三、图像形状匹配
一、图像轮廓近似估计
import cv2
import numpy as np
# 轮廓近似:approxPolyDP() 它主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合。
img = cv2.imread("./images/22.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 第一个参数是寻找轮廓的图像。 cv2.RETR_TREE建立一个等级树结构的轮廓。
# cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 要求传入二值图
# contours返回值首先返回一个list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示.
# hierarchy 这是一个ndarray,其中的元素个数和轮廓个数相同.
print(type(contours)) # <class 'list'>
print(type(contours[0])) # <class 'numpy.ndarray'>
print(len(contours)) # 1
print(type(hierarchy)) # <class 'numpy.ndarray'>
print(hierarchy.ndim) # 3
print(hierarchy[0].ndim) # 2
# 轮廓近似:
# approxPolyDP(curve, epsilon, closed, approxCurve=None)
# epsilon指定逼近精度的参数。这是原始曲线与其近似值之间的最大距离。
epsilon = 20 # 精确度,越小越精确
approx = cv2.approxPolyDP(contours[0], epsilon, True)
print(np.shape(approx)) # (5, 1, 2)
print(approx)
# 绘制轮廓:直接对原图进行操作
cv2.drawContours(img, [approx], -1, (0, 0, 255), 3)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
二、边界检测
import cv2
import numpy as np
# "边界检测: 边界矩形、最小(面积)矩形、最小外接圆以及椭圆拟合、直线拟合"
img = cv2.imread("./images/23.jpg")
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255, 0)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 边界矩形
x, y, w, h = cv2.boundingRect(contours[0]) # 根据轮廓点来获取边界框的坐标
img_contour = cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.imshow("img_contour", img_contour)
# 最小矩形
rect = cv2.minAreaRect(contours[0]) # 得到最小外接矩形的(中心(x,y), (宽,高), 旋转角度)
print(rect)
box = cv2.boxPoints(rect) # 获取最小外接矩形的4个顶点坐标
print(box)
print(box.dtype, box.shape)
box = np.int32(box)
print(box.dtype, box.shape)
img_contour1 = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
cv2.imshow("img_contour1", img_contour1)
# 最小外接圆
(x, y),radius = cv2.minEnclosingCircle(contours[0]) # 根据轮廓点找到最小闭合圆的中心点坐标,半径
center = (int(x), int(y))
radius = int(radius)
img_contour3 = cv2.circle(img, center, radius, (255, 0, 0), 2)
cv2.imshow("img_contour3", img_contour3)
# 椭圆拟合
ellipse = cv2.fitEllipse(contours[0]) # 根据轮廓点找到椭圆
print(ellipse)
img_contour4 = cv2.ellipse(img, ellipse, (0, 255, 255), 2)
cv2.imshow("img_contour4", img_contour4)
cv2.waitKey(0)
cv2.destroyAllWindows()
三、Canny算子(找轮廓)
import cv2
'''Canny算子:' \
'边缘:一个像素,不一定封闭,是提取轮廓的前提' \
'梯度:不一定是一个像素,有可能是多个像素' \
'轮廓:对于边缘的补充,是封闭的,属于边缘的子集'''
'''
Canny边缘检查算法步骤:
1.彩色图转化为灰度图
2.应用高斯滤波来平滑图像-->去除噪声
由于边缘检测容易受到图像中噪声的影响
3.找寻图像的强度梯度
Canny的基本思想是找寻一幅图像中强度变化最强的位置。所谓的变化最强,即指梯度方向。
平滑后的图像中每个像素点的梯度可以由Sobel算子来获得:
1)首先,利用Sobel算子得到沿x轴和y轴方向的梯度G_x和G_y。
2)由G_X和G_Y便可计算每一个像素点的梯度幅值G。
3)接着,每一个像素点用G代替。对于变化剧烈的边界处,G值越大,对应的颜色为白色。
4)然后,这些边界通常非常粗,难以标定边界的真正位置,还必须存储梯度的方向θ。
4.应用非极大抑制技术来消除边误检(本来不是边缘但检测出来是)
沿着梯度θ方向上比较该像素点,若该像素点与两侧相比最大则保留,否则抑制(置为0)。
这一步的目的是将模糊的边界变得清晰,剔除一大部分不是边缘的点。
5.双阈值边缘连接处理
规则:设定两个阈值,minVal和maxVal。
大于maxVal的边缘肯定是边缘(保留),低于minVal的边缘是非边缘(舍去)。
对于介于两者之间的值,判断是否与真正的边界(强边界)相连,相连就保留,否则丢弃。
6.二值化图像输出结果
'''
# 1.转化为灰度图
img = cv2.imread("./images/18.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2.高斯模糊
gaussian = cv2.GaussianBlur(gray, (3, 3), 3)
# 3.Canny边缘提取
# canny = cv2.Canny(gray, 50, 150)
canny = cv2.Canny(gaussian, 50, 150)
cv2.imshow("img", img)
cv2.imshow("gaussian", gaussian)
cv2.imshow("canny", canny)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import matplotlib.pyplot as plt
# 1.将图片转化为灰度图
img = cv2.imread("./images/18.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 对于对比度比较暗的图片,可进行高亮处理
abs = cv2.convertScaleAbs(gray, alpha=6, beta=0)
# 形态学操作(去除中间的黑色噪点)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
close = cv2.morphologyEx(abs, cv2.MORPH_CLOSE, kernel)
# 2.高斯平滑
gaussian = cv2.GaussianBlur(close, (5, 5), 0)
# 3.Canny算法
canny = cv2.Canny(gaussian, 50, 150) # 曲线在50-150之间且大于150,或大于150保留,否则舍弃。
titles = ['img', 'gray', 'abs', 'close', 'gaussian', 'canny']
images = [img, gray, abs, close, gaussian, canny]
# plt.figure(figsize=(10, 10))
for i in range(6):
plt.subplot(2, 3, i+1)
plt.imshow(images[i], cmap="gray")
plt.title(titles[i])
plt.xticks([])
plt.yticks([])
plt.show()
四、计算图像面积、周长、重心
import cv2
'''面积,周长,重心'''
# gray = cv2.imread("./images/21.jpg", 0)
gray = cv2.imread("./images/22.jpg", 0)
ret, binary = cv2.threshold(gray, 127, 255, 0)
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 重心
# moments(array, binaryImage=None)
print(contours[0]) # 轮廓点的坐标
M = cv2.moments(contours[0]) # 矩
print(M)
cx = int(M['m10']) / M['m00']
cy = int(M['m01']) / M['m00']
print("重心:", cx, cy)
# 面积
# contourArea(contour, oriented=None)
area = cv2.contourArea(contours[0])
print("面积:", area)
# 周长
# arcLength(curve, closed)
perimeter = cv2.arcLength(contours[0], True)
print("周长:", perimeter)
cv2.imshow("gray", gray)
cv2.imshow("binary", binary)
cv2.waitKey(0)
cv2.destroyAllWindows()
五、计算轮廓面积、凸包面积
import cv2
import numpy as np
# 轮廓性质
img = cv2.imread("./images/23.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 边界矩形
x, y, w, h = cv2.boundingRect(contours[0])
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
# 最小面积矩形
rect = cv2.minAreaRect(contours[0])
box = cv2.boxPoints(rect)
box = np.int32(box)
cv2.drawContours(img, [box], -1, (0, 255, 0), 2)
# 最小外接圆
(x, y), radius = cv2.minEnclosingCircle(contours[0])
cv2.circle(img, (int(x), int(y)), int(radius), (255, 0, 0), 2)
# 绘制轮廓
cv2.drawContours(img, contours, -1, (255, 255, 0), 2)
# 1.边界矩形的宽高比
aspect_ratio = float(w) / h
print("边界矩形的宽高比:", aspect_ratio)
# 2.轮廓面积与边界矩形面积之比
area = cv2.contourArea(contours[0])
rect_area = w*h
extent = float(area) / rect_area
print("轮廓面积与边界矩形面积之比:", extent)
# 3.轮廓面积和凸包面积之比
hull = cv2.convexHull(contours[0]) # 凸包和凸性检测
area = cv2.contourArea(contours[0])
hull_area = cv2.contourArea(hull)
solidity = float(area) / hull_area
print("轮廓面积和凸包面积之比:", solidity)
# 4.与轮廓面积相等的圆的直径
area = cv2.contourArea(contours[0])
equi_diameter = np.sqrt(4*area / np.pi)
print("与轮廓面积相等的圆的直径:", equi_diameter)
# 5.对象的方向
ellipse = cv2.fitEllipse(contours[0])
print(ellipse)
print("对象的方向angle:", ellipse[2])
# 绘制一个圆心在(150,124)、长轴78、短轴261、线宽为2的白色椭圆
# cv2.ellipse(img, ellipse, (0, 255, 255), 2)
cv2.ellipse(img, (150, 124), (78, 261), 138, 0, 300, (0, 0, 255), thickness=2)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
六、凸包和凸性检测
import cv2
# 凸包和凸性检测: convexHull()、isContourConvex()
# 函数 cv2.convexHull() 可以用来检测一个曲线是否具有凸性缺陷,并能纠正缺陷
# 函数 cv2.isContourConvex() 可以用来检测一个曲线是不是凸的。它只能返回 True 或 False。
img = cv2.imread("./images/23.jpg")
# img = cv2.imread("./images/22.jpg")
# img = cv2.imread("./images/21.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 查找轮廓
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
hull = cv2.convexHull(contours[0]) # 凸包
print(cv2.isContourConvex(contours[0]), cv2.isContourConvex(hull))
# False True
# 说明轮廓曲线是非凸的,凸包曲线是凸的
cv2.drawContours(img, [hull], -1, (0, 0, 255), 2)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
七、图像轮廓查找与绘制
import cv2
import numpy as np
'''轮廓查找与绘制: findContours(), drawContours()'''
# img = cv2.imread("./images/21.jpg")
img = cv2.imread("./images/22.jpg")
cv2.imshow("img", img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imshow("gray", gray)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
cv2.imshow("thresh", thresh)
# 查找轮廓:包括的canny算法
# findContours(image, mode, method, contours=None, hierarchy=None, offset=None)
# image:输入图像(二值化图像)
# mode:轮廓检索方式
# method:轮廓近似方法
'''
轮廓检索方式:
cv2.RETR_EXTERNAL 只检测外轮廓
cv2.RETR_LIST 检测的轮廓不建立等级关系
cv2.RETR_CCOMP 建立两个等级的轮廓,上面一层为外边界,里面一层为内孔的边界信息
cv2.RETR_TREE 建立一个等级树结构的轮廓,包含关系
'''
'''
轮廓近似方法:
cv2.CHAIN_APPROX_NONE 存储所有边界点
cv2.CHAIN_APPROX_SIMPLE 压缩垂直、水平、对角方向,只保留端点
'''
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours[0])) # 点的数量396
print(np.shape(contours)) # (1, 396, 1, 2)
print(hierarchy) # 层次树 [[[-1 -1 -1 -1]]]
cv2.imshow("thresh2", thresh)
# 绘制轮廓:直接对原图进行操作
# drawContours(image, contours, contourIdx, color, thickness=None, lineType=None, hierarchy=None, maxLevel=None, offset=None)
# contourIdx 轮廓的索引(当设置为-1时,绘制所有轮廓)
img_contour = cv2.drawContours(img, contours, -1, (0, 255, 0), 2)
cv2.imshow("img contour", img_contour)
cv2.waitKey(0)
cv2.destroyAllWindows()
八、Hough空间(形状检测)
'''
1.轮廓检测算法公式检测出轮廓:使用参数作为坐标系
2.投射到Hough空间进行形状检测
1)直线检测
lines = cv2.HoughLines(image, rho, theta, threshold)
参数:
image: 单通道的二进制图像。
rho: (ρ,θ)中ρ的精度。
theta: (ρ,θ)中θ的精度。
threshold: 阈值,(ρ,θ)对应的最低投票数。>=threshold被检测为一条线。
2)圆检测
circles = cv2.HoughCircles(image, method, dp, minDist, circles=None, param1=None, param2=None, minRadius=None, maxRadius=None)
参数:
method:定义检测图像中圆的方法。目前唯一实现的方法是HOUGH_GRADIENT。
dp:累加器分辨率与图像分辨率的反比。
dp=1,则累加器与输入图像具有相同的分辨率;dp=2,累加器有一半的宽度和高度。
minDist:该参数是让算法能明显区分的两个不同圆之间的最小距离。
param1 :用于Canny的边缘阈值上限,下限被置为上限的一半。
param2:HOUGH_GRADIENT方法的累加器阈值(最低投票数)。阈值越小,检测到的圈子越多。
minRadius :最小圆半径。
maxRadius:最大圆半径。
'''
# 1.直线检测
import cv2
import numpy as np
img = cv2.imread("./images/24.jpg")
img = cv2.GaussianBlur(img, (5, 5), 50)
# 轮廓检测算法检测出轮廓
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 150) # 提取轮廓边缘信息
# cv2.imshow("edges", edges)
# 2.投射到Hough空间进行形状检测
# 任何一条线都可以用(ρ,θ)这两个术语表示。
# 1)先定义一个累加器,(ρ,θ)对应直线,ρ和θ都分别依次增大(根据精度),计算每对(ρ,θ)的投票数。
# 其中,ρ以像素为单位,θ以弧度为单位。rho和theta是ρ和θ的精度。
# 2)然后,根据threshold(阈值,最低投票数)来判断是否归为一条直线
lines = cv2.HoughLines(edges, 1, np.pi/30, 100) # 隔一度的采样,距离
for line in lines:
rho, theta = line[0]
print(line[0])
a = np.cos(theta)
b = np.sin(theta)
x0 = rho * a
y0 = rho * b
# k1*k2=-1 ==> k2=-1/k1
# k1 = tan(θ) ==> k2 = -1/tan(θ)=-cot(θ)
x1 = int(x0 + 1000 * (b)) # 直线起点横坐标
y1= int(y0 + 1000 * (a)) # 直线起点在纵坐标
x2 = int(x0 - 1000 * (b)) # 直线终点横坐标
y2 = int(y0 - 1000 * (a)) # 直线终点纵坐标
# 画线
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 1)
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
# 圆检测
img = cv2.imread("./images/25.jpg")
# 1.轮廓检测算法检测出轮廓
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 50, 100)
cv2.imshow("edges", edges)
# 投影到Hough空间进行形状检测
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 100, # 隔1度的采样、表示两个圆之间 圆心的最小距离
param1=90, param2=20, minRadius=20, maxRadius=300) #双边阈值(断裂),最小最大半径
'''
cv2.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius)
image为输入图像,需要灰度图
method为检测方法,常用CV_HOUGH_GRADIENT
dp为检测内侧圆心的累加器图像的分辨率于输入图像之比的倒数,如dp=1,累加器和输入图像具有相同的分辨率,如果dp=2,累计器便有输入图像一半那么大的宽度和高度
minDist表示两个圆之间圆心的最小距离
param1有默认值100,它是method设置的检测方法的对应的参数,对当前唯一的方法霍夫梯度法cv2.HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半
param2有默认值100,它是method设置的检测方法的对应的参数,对当前唯一的方法霍夫梯度法cv2.HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值,它越小,就越可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了
minRadius有默认值0,圆半径的最小值
maxRadius有默认值0,圆半径的最大值
'''
print(np.uint16(np.around(circles[0, :])))
# 画圆
if not circles is None:
circle = np.uint16(np.around(circles))
for i in circle[0, :]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 255, 0), 2)
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
九、分水岭算法
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("./images/26.jpg")
cv2.imshow("img", img)
# 1.图像二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
cv2.imshow("thresh", thresh)
cv2.imshow("gray", gray)
kernel = np.ones((3, 3), dtype=np.uint8)
# 2.噪声去除
open = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)
cv2.imshow("open", open)
# 3.确定背景区域
sure_bg = cv2.dilate(open, kernel, iterations=3)
cv2.imshow("sure_bg", sure_bg)
# 4.寻找前景区域
dist_transform = cv2.distanceTransform(open, 1, 5) # 距离腐蚀,计算距离
cv2.imshow("dist transform", dist_transform)
# 根据前景像素值来确定阈值大小
ret, sure_fg = cv2.threshold(dist_transform, 0.5 * dist_transform.max(), 255, cv2.THRESH_BINARY)
cv2.imshow("sure_fg", sure_fg)
# 5.找到未知区域
sure_fg = np.uint8(sure_fg)
unknow = cv2.subtract(sure_bg, sure_fg) # 背景减去前景,去除背景
cv2.imshow("unknow", unknow)
# 6.类别标记:计算中心
ret, markers = cv2.connectedComponents(sure_fg) # ret表示前景标记数,markers表示标记的是背景
# 为所有的标记加1, 保证背景是0不是1
markers = markers + 1
markers[unknow == 255] = 0
print(markers)
# 7.分水岭算法
markers = cv2.watershed(img, markers)
img[markers == -1] = (0, 0, 255)
cv2.imshow("img_watershed", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
十、模版匹配
import cv2
import numpy as np
'模板匹配'
'''
1)模板匹配,得到匹配灰度图
res = cv2.matchTemplate(image, templ, method, result=None, mask=None)
参数:
image: 输入图像
templ: 模板图像
method: 模板匹配方法,包括:
- CV_TM_SQDIFF 平方差匹配法:该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大。
- CV_TM_SQDIFF_NORMED 相关匹配法:该方法采用乘法操作;数值越大表明匹配程度越好。
- CV_TM_CCORR 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
- CV_TM_CCORR_NORMED 归一化平方差匹配法
- CV_TM_CCOEFF 归一化相关匹配法
- CV_TM_CCOEFF_NORMED 归一化相关系数匹配法
2)获取最小和最大像素值及它们的位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
3)最后,将匹配的区域标记出来
cv2.rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None)
'''
# 1.单对象匹配:原图中仅有一个与模版匹配
img = cv2.imread("./images/16.jpg")
template = cv2.imread("./images/17.jpg")
h, w, c = template.shape
print(img.shape) # (342, 548, 3)
print(template.shape) # (48, 36, 3)
# 1.匹配模版,得到匹配灰度图
# res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF) # 匹配方法:最大值是最匹配区域
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED) # 归一化为[-1, 1], 1表示100%匹配
# res = cv2.matchTemplate(img, template, cv2.TM_CCORR) # 最大值是最匹配区域
# res = cv2.matchTemplate(img, template, cv2.TM_CCORR_NORMED) # 归一化[0, 1], 1表示100%匹配
# res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF) # 最小值是最匹配区域
# res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF_NORMED) # 归一化[0, 1], 0表示100%匹配
print(res.shape)
# 2.获取最小和最大像素值值及它们的位置
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
print(min_val) # 最小值为:-0.6616084575653076
print(max_val) # 最大值为:0.996796727180481
print(min_loc) # 最小值索引:(55, 215)
print(max_loc) # 最大值索引:(223, 85)
# 3.最后,将匹配的区域标记出来
# 匹配类型是TM_CCOEFF、TM_CCOEFF_NORMED、TM_CCORR、TM_CCORR_NORMED时,最大值是最匹配区域
cv2.rectangle(img, (max_loc[0], max_loc[1]), (max_loc[0]+w, max_loc[1]+h), color=(0, 0, 255), thickness=2)
# 匹配类型是TM_SQDIFF、TM_SQDIFF_NORMED时,最小值是最匹配区域
# cv2.rectangle(img, (min_loc[0], min_loc[1]), (max_loc[0]+w, max_loc[1]+h), color=(0, 0, 255), thickness=2)
cv2.imshow("img", img)
cv2.imshow("template", template)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
# 多对象匹配,原图中有多个模版匹配
img = cv2.imread("./images/19.jpg")
template = cv2.imread("./images/20.jpg")
h, w, c = template.shape
print(template.shape) # (35, 35, 3)
# 匹配模版,得到匹配灰度图
res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED) # 最大值为匹配区域
print(res.shape) # h, w 符合条件的有326*446个像素
# print(res) # 像素值
# 当匹配像素值>=0.95,我们认为是匹配的。
locs = np.where(res >= 0.95) # 返回匹配程度大于0.95的数组。
print(locs) # (array([230, 230, 230, 230, 230], dtype=int64), array([125, 163, 200, 310, 384], dtype=int64))
print(*locs[::-1]) # w,h -> x, y [125 163 200 310 384] [230 230 230 230 230]
for pt in zip(*locs[::-1]):
print(pt[0], pt[1])
# 最后,将所有匹配的区域标记出来
cv2.rectangle(img, (pt[0], pt[1]), (pt[0] + w, pt[1] + h), color=(0, 0, 255), thickness=1)
cv2.imshow("img", img)
cv2.imshow("template", template)
cv2.waitKey(0)
cv2.destroyAllWindows()
十一、利用对象掩码mask
import cv2
import numpy as np
# 对象掩码mask
img = cv2.imread("./images/23.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 0, 255, 0 | 8)
contours, _ = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
mask = np.zeros(img.shape, np.uint8)
cv2.drawContours(mask, contours, -1, (255, 0, 0), -1)
pixel_points = np.transpose(np.nonzero(mask))
print(pixel_points)
cv2.imshow("img", img)
cv2.imshow("mask", mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
十二、利用形态学操作寻找车牌
import cv2
# 读取图片
raw_image = cv2.imread("./images/27.jpg")
# 高斯模糊,将图片平滑化,去掉干扰的噪声
image = cv2.GaussianBlur(raw_image, (3, 3), 0)
# 图片灰度化
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Sobel算子(X方向)
Sobel_x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
Sobel_y = cv2.Sobel(image, cv2.CV_16S, 0, 1)
absX = cv2.convertScaleAbs(Sobel_x) # 转回uint8
absY = cv2.convertScaleAbs(Sobel_y)
dst = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
image = dst
cv2.imshow("image", image)
# 二值化:图像的二值化,就是将图像上的像素点的灰度值设置为0或255,图像中显示出明显的有黑和白
ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
cv2.imshow("image1", image)
# 闭操作:闭操作可以将目标区域连成一个整体,便于后续轮廓的提取
kernel_X = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel_X)
cv2.imshow("image2", image)
# 膨胀腐蚀(形态学处理)
kernel_X = cv2.getStructuringElement(cv2.MORPH_RECT, (20, 1))
kernel_Y = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 20))
image = cv2.dilate(image, kernel_X) # 在x轴上膨胀,填充车牌漏洞,会在y轴上连续
image = cv2.erode(image, kernel_X) # 在x轴上腐蚀,变回原状
image = cv2.erode(image, kernel_Y) # 在y轴上腐蚀,切断x轴上数据
image = cv2.dilate(image, kernel_Y) # 在y轴上膨胀,返回原型
image = cv2.medianBlur(image, 15) # 平滑处理,中值滤波
cv2.imshow("image3", image)
# 查找轮廓
contours, _ = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for item in contours:
rect = cv2.boundingRect(item) # 根据轮廓点来找到矩形框
x = rect[0]
y = rect[1]
w = rect[2]
h = rect[3]
if w > (h * 2):
# 裁剪区域图片
chepai = raw_image[y:y+h, x:x+w]
cv2.imshow("chepai"+str(x), chepai)
# 绘制轮廓
image = cv2.drawContours(raw_image, contours, -1, (0, 0, 255), 3)
cv2.imshow("image4", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
十三、图像形状匹配
import cv2
# 形状匹配:matchShapes()
img1 = cv2.imread("./images/21.jpg")
img2 = cv2.imread("./images/22.jpg")
gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
ret1, binary1 = cv2.threshold(gray1, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
contours1, _ = cv2.findContours(binary1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
ret2, binary2 = cv2.threshold(gray2, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
contours2, _ = cv2.findContours(binary2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
ret = cv2.matchShapes(contours1[0], contours2[0], cv2.CONTOURS_MATCH_I1, 0.0)
print(ret) # 值越小,越匹配
cv2.imshow("img1", img1)
cv2.imshow("img2", img2)
cv2.waitKey(0)
cv2.destroyAllWindows()