本文章学习路径--《python opencv 从出门到精通》--明日科技
1.检测部分
图像轮廓是指图像中图形或物体外边缘线条,简单的几何图形是由线条构成的,而不规则图形是由许多点构成,所以识别起来比较困难
contours,hierarchy=cv.findContours(image, mode, method, contours, hierarchy)
image:被检测的图像,必须是8位单通道二值图像
mode:轮廓索引
cv.RETR_EXTERNAL#只检测外轮廓
cv.RETR_LIST#检测所有轮廓,但不建立层次关系
cv.RETR_CCOMP#检测所有轮廓,并建立关系
cv.RETR_TREE#检测所有外轮廓,并建立树状结构的层次关系
method:检测轮廓时用到的方法
cv.CHAIN_APPROX_NONE#存储轮廓上的所有点
cv.CHAIN_APPROX_SIMPLE#只保存水平,垂直或对角线轮廓的端点
cv.CHAIN_APPROX_TC89_L1#Ten-chinl近似算法中的一种
cv.CHAIN_APPROX_TC89_KCOS#Ten_chinl近似算法中的一种
contours:检测出来的所有轮廓list类型
hierarchy:轮廓之间的层次关系
2.绘画部分
img=drawContours(image, contours, contourIdx, color, thickness,lineType, hierarchy,maxLevel, offset)
image:被绘制的原始图像
contours:findContours()方法返回得出的轮廓列表
contourIdx:绘制轮廓索引,如果值为-1则绘制所有轮廓
color: 绘制的颜色bgr
thickness:画笔粗细程度,如果该值为-1,则绘制实心轮廓
lineType:绘制轮廓线性
hierarchy:层次关系
maxLevel:findContours:绘制轮廓的层次深度,最深绘制第maxLevel层
import cv2 as cv
img=cv.imread(r'img/jmh/cat.jpg')
gray=cv.cvtColor(img,cv.COLOR_BGR2GRAY)#转化为灰度图
t,binary=cv.threshold(gray,127,255,0,cv.THRESH_BINARY)#将灰度图转化为二值图像
t1,t2=cv.findContours(binary,cv.RETR_TREE,cv.CHAIN_APPROX_NONE)#检测所有外轮廓,并建立树状结构的层次关系,只保存水平,垂直或对角线轮廓的端点
img2=cv.drawContours(img,t1,-1,(0,0,255),5)
cv.imshow('img',img)
cv.waitKey()
详细
使用
contours, hierarchy = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) cv2.drawContours(img, contours, -1, (0, 0, 255), 5)
drawContours方法的第三个参数可以指定绘制那个索引轮廓,索引的顺序由轮廓的检索模式决定,如cv2.RETR_EXTERNAL模式下绘制索引为0时的轮廓如下所示:
cv2.RETR_CCOMP模式下绘制索引为0的轮廓的关键代码如下:
contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 0, (0, 0, 255), 5)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 1, (0, 0, 255), 5)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 2, (0, 0, 255), 5)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, 3, (0, 0, 255), 5)
花朵图像绘制轮廓,首先要降低图像中的噪 声干扰,进行滤波处理,然后将图像处理成二值灰度图像,并检测出 轮廓,最后利用绘制轮廓的方法在原始图像中绘制轮廓
import cv2
img = cv2.imread("flower.png") # 读取原图
cv2.imshow("img", img) # 显示原图
img = cv2.medianBlur(img, 5) # 使用中值滤波去除噪点
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 原图从彩图变成单通道灰度图像
t, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 灰度图像转化为二值图像
cv2.imshow("binary", binary) # 显示二值化图像
# 获取二值化图像中的轮廓极轮廓层次数据
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
cv2.drawContours(img, contours, -1, (0, 0, 255), 2) # 在原图中绘制轮廓
cv2.imshow("contours", img) # 显示绘有轮廓的图像
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
3.轮廓拟合
1.矩形包围框
矩形包围框是指图像轮廓的最小矩形边界CV提供的 boundingRect()方法可以自动计算轮廓最小矩形边界的坐标、宽和高。 boundingRect()方法的语法如下:
retval = cv2.boundingRect (array)
array:轮廓数组
retval:元组类型,包含4个整数值,分别是最小矩形包围框 的:左上角顶点的横坐标、左上角顶点的纵坐标、矩形的宽 和高。所以也可以写成x, y, w, h = cv2.boundingRect (array)的 形式。
import cv2
img = cv2.imread("shape2.png") # 读取原图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 从彩色图像变成单通道灰度图像
# 将灰度图像进行二值化阈值处理
t, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 获取二值化图像中的轮廓极轮廓层次数据
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, 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) # 绘制红色矩形
cv2.imshow("img", img) # 显示绘制结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
2.圆形包围框
圆形包围框与矩形包围框一样,是图像轮廓的最小圆形边界。
CV提供的minEnclosingCircle ()方法可以自动计算轮廓最小圆形边 界的圆心和半径。minEnclosingCircle()方法的语法如下:
center, radius = cv2.minEnclosingCircle(points)
points:轮廓数组
center:元组类型,包含2个浮点值,是最小圆形包围框圆心 的横坐标和纵坐标.
radius:浮点类型,最小圆形包围框的半径
import cv2
img = cv2.imread("shape2.png") # 读取原图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 从彩色图像变成单通道灰度图像
# 将灰度图像进行二值化阈值处理
t, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 获取二值化图像中的轮廓极轮廓层次数据
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
center, radius = cv2.minEnclosingCircle(contours[0]) # 获取最小圆形边框的圆心点和半径
x = int(round(center[0])) # 圆心点横坐标转为近似整数round用于近似操作,(四舍五入)
y = int(round(center[1])) # 圆心点纵坐标转为近似整数
cv2.circle(img, (x, y), int(radius), (0, 0, 255), 2) # 绘制圆形
cv2.imshow("img", img) # 显示绘制结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
4.凸包
矩形包围框和圆形包围框,这2种包围框虽然已经逼近 了图形的边缘,但这种包围框为了保持几何形状,与图形的真实轮廓 贴合度较差。如果能找出图形最外层的端点,将这些端点连接起来, 就可以围出一个包围图形的最小包围框,这种包围框叫凸包,凸包是最逼近轮廓的多边形,凸包的每一处都是凸出来的,也就 是任意3个点组成的内角均小于180°。
hull = cv2.convexHull(points, clockwise, returnPoints)
points:轮廓数组.
clockwise:可选参数,布尔类型。当该值为True时,凸包中 的点按顺时针排列,为False时按逆时针排列.
returnPoints:可选参数,布尔类型。当该值为True时返回点 坐标,为False时返回点索引。默认值为True.
hull:凸包的点阵数组。
实例代码如下:
import cv2
img = cv2.imread("shape2.png") # 读取原始图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转为灰度图像
ret, binary = cv2.threshold(gray, 127, 225, cv2.THRESH_BINARY) # 二值化阈值处理
# 检测图像中出现的所有轮廓
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
hull = cv2.convexHull(contours[0]) # 获取轮廓的凸包
cv2.polylines(img, [hull], True, (0, 0, 255), 2) # 绘制凸包
cv2.imshow("img", img) # 显示图像
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
4.Canny边缘检测
Canny边缘检测算法是根据像素的梯度变化寻找图像边缘,最终可以绘制 十分精细的二值边缘图像。
该方法的语法如下: edges = cv2.Canny(image, threshold1, threshold2, apertureSize, L2gradient)
image:检测的原始图像。
threshold1:计算过程中使用的第一个阈值,可以是最小阈 值,也可以是最大阈值,通常用来设置最小阈值。
threshold2:计算过程中使用的第二个阈值,通常用来设置最 大阈值。
apertureSize:可选参数,Sobel算子的孔径大小。
L2gradient:可选参数,计算图像梯度的标识,默认值为 False。值为True时采用更精准的算法进行计算。
edges:计算后得出的边缘图像,是一个二值灰度图像。
在开发过程中可以通过调整最小阈值和最大阈值控制边缘检测的 精细程度。当2个阈值都较小时,检测出较多的细节;当2个阈值都较 大时,忽略较多的细节。
import cv2
img = cv2.imread("flower.png") # 读取原图
r1 = cv2.Canny(img, 10, 50) # 使用不同的阈值进行边缘检测
r2 = cv2.Canny(img, 100, 200)
r3 = cv2.Canny(img, 400, 600)
cv2.imshow("img", img) # 显示原图
cv2.imshow("r1", r1) # 显示边缘检测结果
cv2.imshow("r2", r2)
cv2.imshow("r3", r3)
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
#阈值越小细节越多
5.霍夫变换
霍夫变换是一种特征检测,通过算法识别图像的特征,从而判断 图像中的特殊形状,例如直线和圆。
霍夫直线变换是通过霍夫坐标系的直线与笛卡儿坐标系的点之间 的映射关系来判断图像中的点是否构成直线。
OpenCV将此算法封装 成两个方法,分别是cv2.HoughLines()和cv2.HoughLinesP(),前者用于 检测无限延长的直线,后者用于检测线段。HoughLinesP()方法。 HoughLinesP()方法名称最后有一个大写的P,该方法只能检测二值灰度图像,也就是只有两种像素值的黑白图像。该方法最后把找出 的所有线段的两个端点坐标保存成一个数组。
(1).直线检测
lines = cv2.HoughLinesP(image, rho, theta, threshold, minLineLength, maxLineGap)
image:检测的原始图像。
rho:检测直线使用的半径步长,值为1时,表示检测所有可 能的半径步长。
theta:搜索直线的角度,值为π/180°时,表示检测所有角 度。
threshold:阈值,该值越小,检测出的直线就越多。
minLineLength:线段的最小长度,小于该长度的线段不记录 到结果中。
maxLineGap:线段之间的最小距离.
lines:一个数组,元素为所有检测出的线段,每条线段是一 个数组,代表线段两个端点的横、纵坐标,格式为[[[x1, y1, x2, y2], [x1, y1, x2, y2]]]。
注意:使用该方法前应该为原始图像进行降噪处理,否则会影响检测 结果。
具体代码如下:
import cv2
import numpy as np
img = cv2.imread("pen.jpg") # 读取原图
o = img.copy() # 复制原图
o = cv2.medianBlur(o, 5) # 使用中值滤波进行降噪
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY) # 从彩色图像变成单通道灰度图像
binary = cv2.Canny(o, 50, 150) # 绘制边缘图像
# 检测直线,精度为1,全角度,阈值为15,线段最短100,最小间隔为18
lines = cv2.HoughLinesP(binary, 1, np.pi / 180, 15, minLineLength=100, maxLineGap=18)
for line in lines: # 遍历所有直线
x1, y1, x2, y2 = line[0] # 读取直线两个端点的坐标
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2) # 在原始图直像上绘制线
cv2.imshow("canny", binary) # 显示二值化边缘图案
cv2.imshow("img", img) # 显示绘制结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体
(2).圆环检测
霍夫圆环变换的原理与霍夫直线变换类似。
OpenCV提供的 HoughCircles()方法用于检测图像中的圆环,该方法在检测过程中进行两轮筛选:
第一轮筛选找出可能是圆的圆心坐标,第二轮筛选计算这 些圆心坐标可能对应的半径长度。
该方法最后将圆心坐标和半径封装 成一个浮点型数组。
circles = cv2.HoughCircles(image, method, dp, minDist, param1, param2, minRadius, maxRadius)
image:检测的原始图像。
method:检测方法,OpenCV 4.0.0及以前版本仅提供了 cv2.HOUGH_GRADIENT作为唯一可用方法。
dp:累加器分辨率与原始图像分辨率之比的倒数。值为1 时,累加器与原始图像具有相同的分辨率;值为2时,累加器 的分辨率为原始图像的1/2。通常使用1作为参数。
minDist:圆心之间的最小距离。
param1:可选参数,Canny边缘检测使用的最大阈值。
param2:可选参数,检测圆环结果的投票数。第一轮筛选时 投票数超过该值的圆环才会进入第二轮筛选。值越大,检测 出的圆环越少,但越精准。
minRadius:可选参数,圆环的最小半径。
maxRadius:可选参数,圆环的最大半径。
circles:一个数组,元素为所有检测出的圆环,每个圆环也 是一个数组,内容为圆心的横、纵坐标和半径长度,格式 为:[[[x1 ,y1, r1], [x2 ,y2, r2]]]。
import cv2
import numpy as np
img = cv2.imread("coin.jpg") # 读取原图
o = img.copy() # 复制原图
o = cv2.medianBlur(o, 5) # 使用中值滤波进行降噪
gray = cv2.cvtColor(o, cv2.COLOR_BGR2GRAY) # 从彩色图像变成单通道灰度图像
# 检测圆环,圆心最小间距为70,Canny最大阈值为100,投票数超过25。最小半径为10,最大半径为50
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 70, param1=100, param2=25, minRadius=10, maxRadius=50)
circles = np.uint(np.around(circles)) # 将数组元素四舍五入成整数
for c in circles[0]: # 遍历圆环结果
x, y, r = c # 圆心横坐标、纵坐标和圆半径
cv2.circle(img, (x, y), r, (0, 0, 255), 3) # 绘制圆环
cv2.circle(img, (x, y), 2, (0, 0, 255), 3) # 绘制圆心
cv2.imshow("img", img) # 显示绘制结果
cv2.waitKey() # 按下任何键盘按键后
cv2.destroyAllWindows() # 释放所有窗体