图像轮廓

  • 什么是图形轮廓
  • 查找轮廓
  • 绘制轮廓
  • 计算轮廓的面积和周长
    • 轮廓面积
    • 轮廓周长
  • 多边形逼近
  • 凸包
  • 轮廓拟合
    • 外接矩形
      • 最小外接矩形
      • 最大外接矩形
    • 外接圆
  • 边缘检测`Canny`
  • 霍夫变换
    • 直线检测
      • 检测笔图像中的直线
    • 圆环检测
      • 绘制硬币图像的圆环和对应圆心

什么是图形轮廓

图像轮廓是具有相同颜色或灰度的连续点的曲线,轮廓在形状分析物体检测与识别中很有用处

轮廓的作用:

  • 用于图形分析
  • 物体的识别与检测

查找轮廓

OpenCV提供的findContours()方法可以通过计算像素梯度来判断出图像的边缘,然后将边缘的点封装成数组返回

注意:

  • 为了保证检测的准确性,需要先对图像进行二值化Canny边缘检测操作
  • 画轮廓时会修改输入的图像,如果之后想继续使用原始图像,应该将原始图像存储一份到其他变量中(备份)—— img_copy = img.copy()

关键API:contours,hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

利用该函数可以获取轮廓的信息

其中:

  • image:被检测的图像,必须是8位单通道二值图像

    • 如果原始图像是彩色图像,则必须转为灰度图像cv2.cvtColor(),再经过二值化阈值处理cv2.threshold()
  • mode:查找轮廓的模式 (写模式名或数字都可以)

    • cv2.RETR_EXTERNAL / 0:表示只检测外围轮廓

    • cv2.RETR_LIST / 1:检测的轮廓不建立等级关系(属于同一轮廓线上即为同一级,),即检测所有的轮廓,从右到左,从里到外画轮廓(一般不会出现这么复杂的索引)

    • cv2.RETR_CCOMP / 2: 每层最多两级,从小到大,最里到外

    • cv2.RETR_TREE / 3:按照树型存储轮廓,从大到小,从右到左 ——> 先从外到内把右边的算完,再算左边的,!最常用!

  • method:轮廓近似方法,也叫ApproximationMode。总的来说就是如何去保存你的轮廓

    • cv2.CHAIN_APPROX_NONE:保存所有轮廓上的点,会把轮廓上所有像素点的坐标都保存下来,存储信息较大
    • cv2.CHAIN_APPROX_SIMPLE:只保存角点,比如四边形,只保存四边形的四个角,存储信息较少,比较常用

返回值:

  • contours:检测出所有轮廓的集合,list类型,可以通过contours[i](i = 0,1,2...)来调用某一个轮廓,即每一个元素都是某个轮廓的像素坐标数组
  • hierarchy:轮廓间的层级关系
# 查找轮廓import cv2
import numpy as np# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./contours.jpeg')# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 对比一下原图与灰度图
cv2.imshow('img',img)
cv2.imshow('gray',gray)# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓print(type(contours))
print(contours)# 展示
# cv2.imshow('dst',dst)
# cv2.imshow('img and dst ',np.hstack((gray,dst)))cv2.waitKey(0)
cv2.destroyAllWindows()

绘制轮廓

通过cv2.findContours()方法找到图像轮廓之后,为了方便开发人员观测,最好能把轮廓画出来,于是OpenCV提供了drawContours()方法专门用来绘制这些轮廓

关键API:img = cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset]]]]])
其中:

  • image操作的图像,由于我们可以设置轮廓的颜色color,所以就不太适合用二值化或灰度化后的图片进行操作,应该用三通道的彩图进行绘画(原图)
  • contours轮廓点,由查找轮廓的函数cv2.findContours()的返回值可得
  • contourIdx要绘制的轮廓编号-1表示绘制所有轮廓,此处是索引!因此不能写成contours[]的形式
  • color轮廓的颜色,使用BGR格式,如(0,0,255)表示红色
  • thickness线宽-1表示全部填充

返回值:

  • img:可以不使用,在执行的时候就已经在对参数中的image进行绘制了
# 绘制轮廓import cv2
import numpy as np# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./contours.jpeg')# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)cv2.imshow('img',img)
cv2.imshow('gray',gray)# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# 绘制轮廓:会直接修改原图
# 如果想保持原图不变,可以copy一份
img_copy = img.copy()
cv2.drawContours(img_copy,contours,-1,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()

结果:

如果我们想只画一个轮廓,那么可以根据目标轮廓的层级来绘制(我们使用的是cv2.RETR_TREE:从左到右、从外到内)

例如把参数中的-1改成0

cv2.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受

结果:


计算轮廓的面积和周长

轮廓面积

轮廓面积是指每个轮廓中所有的像素点围成区域的面积,单位为像素

轮廓面积是轮廓重要的统计特性之一,通过轮廓面积的大小可以进一步分析每个轮廓隐含的信息,例如通过轮廓面积区分物体大小、识别不同的物体

在查找到轮廓后,可能会有很多细小的轮廓,我们可以通过轮廓的面积进行过滤(比如小于某一个阈值,就把这个轮廓过滤掉)

关键API:cv2.contourArea(contour[, oriented])

  • 参数是countour(不加s,加了s(countours)后表示轮廓的集合)

轮廓周长

关键API:cv2.arcLength(curve, closed)

  • curve:轮廓
  • closed:是否是闭合的轮廓,为布尔类型,一般设置为closed = True
# 计算轮廓的面积和周长import cv2
import numpy as np# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./contours.jpeg')# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)cv2.imshow('img',img)
cv2.imshow('gray',gray)# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# 绘制轮廓:会直接修改原图
# 如果想保持原图不变,可以copy一份
img_copy = img.copy()
# cv2.drawContours(img_copy,contours,-1,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受
cv2.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受# 计算轮廓面积
area = cv2.contourArea(contours[1])
print('area:',area) # 单位是像素# 计算轮廓周长
length = cv2.arcLength(contours[1],closed = True)
print('length:',length) # 展示
# cv2.imshow('img',img)
# cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()

结果:

area: 115239.5
length: 1355.0710676908493

多边形逼近

cv2.findContours()后的轮廓信息contours可能过于复杂不平滑,可以用cv2.approxPolyDP()函数来对该多边形曲线做适当近似,这就是轮廓的多边形逼近

cv2.approxPolyDP()就是以多边形去逼近轮廓,采用的是Douglas_Peucker算法(方法名中的DP)

DP算法的原理比较简单,核心就是不断找多边形最远的点加入形成新的多边形,直到最短距离小于指定的精度

关键API:cv2.approxPolyDP(curve, epsilon, closed[, approxCurve])
其中:

  • curve:要近似逼近的轮廓
  • epsilon:DP算法的阈值,阈值越小,近似轮廓越贴合真是轮廓,蕴含的像素点也就越多,占用内存越大
  • closed:轮廓是否闭合
  • 返回值为一个轮廓数据approx,我们还需要再调用cv2.drawContours(img_copy,[approx],0,(0,255,0),2) 去画出轮廓!
# 多边形逼近import cv2
import numpy as np# 导入图片 该图片显示是黑白的,实际上是三通道的图像
img = cv2.imread('./hand.png')# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 进行二值化操作(注意!threshold有两个返回值!一个是阈值,一个是二值化处理后的图片)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# 绘制轮廓:会直接修改原图
# 如果想保持原图不变,可以copy一份
img_copy = img.copy()
# cv2.drawContours(img_copy,contours,-1,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受
cv2.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受#----------------------------------------------------------------------------------------------------------------------------
# 使用多边形逼近 近似模拟手的轮廓
approx = cv2.approxPolyDP(contours[0],20,closed = True) # 我们只有一个轮廓,因此写contours[0]
# approx本质上是一个轮廓数据,类型是ndarray# 画出近似逼近的轮廓 ——> 要强制类型转化为列表list
cv2.drawContours(img_copy,[approx],0,(0,255,0),2) # 会直接对操作的图像进行修改,因此可以不用接受#----------------------------------------------------------------------------------------------------------------------------# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()

结果:


凸包

多边形逼近是轮廓的高度近似,但是有时候,我们希望用一个凸包来简化它。凸包和多边形逼近很像,只不过它是物体最外层的凸多边形。

凸包指的是完全包含原有轮廓,并且仅由轮廓上的点所构成的多边形。凸包的每一处都是凸的,即在凸包内连接任意两点的直线都在凸包的内部。在凸包内,任意连续三个点的内角小于180°

关键API:hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]]])
其中:

  • points:具体的轮廓
  • hull:可选参数,逼近出来的多边形凸包,用默认值即可
  • clockwise:可选参数,布尔类型,设置凸包中的点的排序方式,用默认值即可
    • 当设置为True:凸包中的点按顺时针排列
    • 当设置为False:凸包中的点按逆时针逆时针排列

返回值:

  • hull:凸包的点阵数组
# 轮廓、多边形逼近、凸包进行对比import cv2
import numpy as np# 读入图片
img = cv2.imread('./hand.png')# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 二值化(有两个返回值)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# 如果想保持原图不变,可以copy一份
img_copy = img.copy()# 绘制轮廓:会直接修改原图
cv2.drawContours(img_copy,contours,0,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受#----------------------------------------------------------------------------------------------------------------------------
# 使用多边形逼近 近似模拟手的轮廓
approx = cv2.approxPolyDP(contours[0],20,closed = True) # 我们只有一个轮廓,因此写contours[0]
# approx本质上是一个轮廓数据,类型是ndarray# 画出近似逼近的轮廓 ——> 要强制类型转化为列表list
cv2.drawContours(img_copy,[approx],0,(0,255,0),2) # 会直接对操作的图像进行修改,因此可以不用接受
#----------------------------------------------------------------------------------------------------------------------------#----------------------------------------------------------------------------------------------------------------------------
# 计算凸包(返回一个凸包数据)
hull = cv2.convexHull(contours[0],)# 画出凸包
cv2.drawContours(img_copy,[hull],0,(255,255,0),2) # 会直接对操作的图像进行修改,因此可以不用接受
#----------------------------------------------------------------------------------------------------------------------------# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()

结果:
轮廓为红色、多边形逼近为绿色、凸包为青色


轮廓拟合

拟合是指将平面上的一系列点,用一条光滑的曲线连接起来。轮廓的拟合就是将凹凸不平的轮廓用完整的几何图形体现出来

想要完成轮廓拟合操作:

  1. 首先要判断图形的轮廓:用cv2.findContours()检索出所有的轮廓
  2. 利用不同的方法绘制拟合轮廓

外接矩形

外接矩形分为最小外接矩形最大外接矩形

下图中红色矩形是最小外接矩形,绿色矩形为最大外接矩形


最小外接矩形

关键API:cv2.minAreaRect(points)
其中:

  • point:图形的轮廓
  • 返回值为一个元组,内容是一个旋转矩形(RotatedRect)的参数:
    • 矩形的起始坐标(x,y)
    • 矩形的高度h和宽度w
    • 矩形的旋转角度(弧度制)

绘制旋转矩形:

获取旋转矩形的四个角的坐标cv2.boxPoints(box[, points])
把我们使用cv2.minAreaRect(points)返回的rect元组放入参数中,即可得到四个角的坐标,其实就是帮我们把旋转矩形的四个坐标点计算出来了

# 最小外接矩形import cv2
import numpy as np# 读入图片
img = cv2.imread('./Hello.jpeg')# 如果想保持原图不变,可以copy一份
img_copy = img.copy()# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img_copy,cv2.COLOR_BGR2GRAY)# 二值化(有两个返回值)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# 绘制轮廓:会直接修改原图 我们发现contoursIdx = 1才是矩形外框
# cv2.drawContours(img_copy,contours,1,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受# #----------------------------------------------------------------------------------------------------------------------------
# 寻找最小外接矩形
rect = cv2.minAreaRect(contours[1]) # 返回值rect是一个Rotated Rect旋转矩形,包含:矩形的起始坐标(x,y)、矩形的长宽、矩形旋转角度 # 绘画旋转矩形
box = cv2.boxPoints(rect) # 其实就是帮我们把旋转矩阵的四个坐标点计算出来了# 有了轮廓的四个角 我们直接绘制矩形即可
# cv2.drawContours(img_copy,[box]) # 由于box是二维的,参数要求是三维,因此我们再对box加一个中括号 ——> [box] # 并且由于坐标要求都是整数int,而box内部是float数据,因此需要强制类型转化
# box = np.int0(box) # 这种转化直接把小数丢掉!而不是四舍五入,因此不够准确
box = np.round(box).astype('int64') # np.round()四舍五入操作,.astype('')改成什么类型cv2.drawContours(img_copy,[box],0,(255,255,0),2) # 由于box是二维的,参数要求是三维,因此我们再对box加一个中括号 ——> [box] # #----------------------------------------------------------------------------------------------------------------------------# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()


最大外接矩形

关键API:retval = cv2.boundingRect(points)
其中:

  • point:轮廓数组

返回值:

  • retval:元组类型,包含了四个整数值(x,y,w,h),最小矩形包围框左上角顶点的横坐标、纵坐标,矩形的宽,矩形的高,所以也可以写成x,y,w,h = cv2.boundingRect(points)
# 最大外接矩形import cv2
import numpy as np# 读入图片
img = cv2.imread('./Hello.jpeg')# 如果想保持原图不变,可以copy一份
img_copy = img.copy()# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img_copy,cv2.COLOR_BGR2GRAY)# 二值化(有两个返回值)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# 绘制轮廓:会直接修改原图 我们发现contoursIdx = 1才是矩形外框
# cv2.drawContours(img_copy,contours,1,(0,0,255),2) # 会直接对操作的图像进行修改,因此可以不用接受# #----------------------------------------------------------------------------------------------------------------------------
# 寻找最大外接矩形,返回最大外接矩形的参数(x,y),(w,h)
x,y,w,h = cv2.boundingRect(contours[1]) # 返回值rect是一个Rotated Rect旋转矩形,包含:矩形的起始坐标(x,y)、矩形的长宽、矩形旋转角度 # 由于最大外界矩形就是一个方正的矩形,不需要再做旋转操作了,所以直接返回四个参数# 想要用cv2.drawContours()也行,只不过要算出四个点的坐标,有点麻烦!
cv2.rectangle(img_copy,(x,y),(x + w,y + h),(255,255,0),2) # 用矩形对角线的两个坐标即可# #----------------------------------------------------------------------------------------------------------------------------# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()

结果:

最大外接矩形都是没旋转的,因此我们直接使用cv2.boundingRect(contours[1])获取到轮廓的信息x,y,w,h后直接可以调用cv2.rectangle()画图


外接圆

外接圆与外接矩形同理,是图像轮廓的最小圆形边界

关键API:center,radius = cv2.minEnclosingCircle(points)
其中:

  • points:轮廓数组

返回值:

  • center元组类型,包含两个浮点值,是最小圆形包围框圆心的横坐标和纵坐标
  • radius浮点类型,最小圆形包围框的半径

示例代码:

# 外接圆import cv2
import numpy as np# 读入图片
img = cv2.imread('./shape2.png')# 如果想保持原图不变,可以copy一份
img_copy = img.copy()# 二值化操作是对灰度图操作的 ——> 因此我们首先要把图像变成灰度图!
gray = cv2.cvtColor(img_copy,cv2.COLOR_BGR2GRAY)# 二值化(有两个返回值)
thresh,temp = cv2.threshold(gray,127,255,cv2.THRESH_BINARY) # 最终二值化的效果会受到阈值(第二个参数thresh)的影响# 查找轮廓 返回两个结果:轮廓和层级关系
contours,hierarchy = cv2.findContours(temp,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) # (操作目标,操作类型,操作方法)
# 注意 : contours的类型是列表list,不是ndarray;列表里面才是ndarray,一个ndarray就是一个轮廓# #----------------------------------------------------------------------------------------------------------------------------
# 获取最小圆心边框的圆心点和半径
center,radius = cv2.minEnclosingCircle(contours[0]) # 返回值rect是一个Rotated Rect旋转矩形,包含:矩形的起始坐标(x,y)、矩形的长宽、矩形旋转角度 # 由于最大外界矩形就是一个方正的矩形,不需要再做旋转操作了,所以直接返回四个参数# 将圆心点的横纵坐标转化为近似整数
x = int(round(center[0]))
y = int(round(center[1]))# 绘制圆形
cv2.circle(img_copy,(x,y),int(radius),(255,255,0),2) # 用矩形对角线的两个坐标即可# #----------------------------------------------------------------------------------------------------------------------------# 展示
cv2.imshow('img',img)
cv2.imshow('img_copy',img_copy)cv2.waitKey(0)
cv2.destroyAllWindows()

结果:


边缘检测Canny

Canny边缘检测是边缘检测算法中的最优算法,最优边缘检测的三个主要评价标准是:

  • 低错误率: 标识出尽可能多的实际边缘,同时尽可能减少噪声的误报
  • 高定位性: 标识出的边缘要与图像中的边缘尽可能接近
  • 最小响应: 图像中的边缘只能标识一次

原理:

Canny边缘检测的一般步骤:

  • 去噪: 边缘检测容易受到噪声影响,在进行边缘检测前一般要进行去噪,一般用高斯滤波去除噪声
  • 计算梯度: 对平滑后的图像采用索贝尔(Sobel)算子计算梯度dx、dy和方向dy/dx(采用Sobel算子的原因是因为)

非极大值抑制(NMS算法):

  • 在获取了梯度和方向后,遍历图像,去除所有不是边界的点

  • 实现方法:逐个遍历像素点,判断当前像素点是否是周围具有相同方向梯度的像素点中的“最大值”

  • 下图中:点ABC具有相同的方向(水平向右:Gradient Direction 梯度方向),梯度方向垂直于边缘

  • 判断点A是否为ABC中的局部最大值:如果是,保留该点;否则,它被抑制(归零)

  • 更形象的例子

    第一列:7最大,取7,其他的都归零舍去;第三列:向上的梯度方向占多数,因此不管向右的梯度方向,取最大的9,其余归零

  • 滞后阈值:它会设定两个阈值maxValminVal

    • 如果我们刚刚算出该点的梯度值 > maxVal,则该点一定是边缘像素点,如点A
    • 如果我们刚刚算出该点的梯度值 < maxVal,则该点一定不是边缘像素点,抛弃掉,如点D
    • 如果minVal < 梯度值 < maxVal:如果该点与边界(点A相连,则保留,如点C;如果该点与边界不相连,则抛弃,如点D


怎么判断是否相连:在上方的方格图中相邻位置的点即相连


关键API:edges = Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]])
其中:

  • image:进行边缘检测的图片
  • threshold1:最小阈值
  • threshold2:最大阈值

返回值:

  • edges:计算后得出的边缘图像,是一个二值灰度图像

在开发过程中可以通过调整最小阈值和最大阈值来控制边缘检测的精细程度:

  • 当两个阈值都较小时,会检测出较多的细节
  • 当两个阈值都较大时,会忽略较多的细节
# Canny
import cv2
import numpy as np# 导入图片
img = cv2.imread('./lena.png')# 利用Canny边缘算法
dst1 = cv2.Canny(img,100,200)  # 阈值给的稍大一些,绘制的边缘不够精细
dst2 = cv2.Canny(img,70,150)  # 可以通过给小一点阈值,得到较为精细的边缘# 观察
# cv2.imshow(' img and dst ',np.hstack((img,dst)))    # 不能和原图一起展示!我们检测边缘时会把图像变成二维(灰度图),和原图格式不一样!
cv2.imshow('threshold:Left > Right ',np.hstack((dst1,dst2)))cv2.waitKey(0)
cv2.destroyAllWindows()

结果:
很明显右边阈值较小的图,边缘会更精细一些!但也会有许多不是边缘的地方被算进来了,有利有弊!


霍夫变换

霍夫变换是一种特征检测,它通过算法识别图像特征,从而判断出图像中的特殊形状,例如直线和圆。

本节就介绍如何检测图像中的直线和圆


直线检测

霍夫直线变换是通过霍夫坐标系的直线与笛卡尔坐标系的点之间的映射关系来判断图像中的点是否构成直线。

OpenCV将此算法封装成了两个方法:

  • cv2.HoughLines()方法:用于检测无线延长的直线
  • cv2.HoughLinesP()方法:用于检测线段**(常用)**

本节只介绍较常用的cv2.HoughLinesP()方法

cv2.HoughLinesP()方法名称最后有一个大写的P该方法只能检测二值灰度图像,也就是只有两种像素值的黑白图像。方法最后会把找出的所有线段的两个端点坐标保存成一个数组

关键API:lines = cv2.HoughLinesP(image,rho,theta,threshold,minLineLength,maxLinGap)
其中:

  • image:检测的原始图像,必须是二值灰度图像(只有两种像素值的黑白图像)
  • rho:检测直线使用的半径步长,值为1时,表示检测所有角度
  • theta:搜索直线的角度,值为pi / 180时,表示检测所有角度
  • threshold:阈值,该值越小,检测出的直线越小
  • minLineLength:线段的最小长度,小于该长度的线段不会记录到结果中
  • maxLineGap:线段之间的最小距离

返回值:

  • lines:一个数组,元素为所有检测出的线段,每个线段也是一个数组,内容为两个端点的横纵坐标,格式为[ [ [x1 , y1 , x2 , y2] , [x1^ , y1^ , x2^ , y2^] ] ]

注意:使用该方法前需要先对图像进行降噪处理,否则会影响检测的结果


检测笔图像中的直线

检测如图的中性笔照片,先将图像降噪,再对图像进行边缘检测,然后利用cv2.HoughLinesP()方法找出边缘中的直线线段,最后利用cv2.line()方法将找出的线段绘制为红色


具体代码:

import cv2
import numpy as np# 导入												

OpenCV:07图像轮廓相关推荐

  1. OpenCV检测图像轮廓

    轮廓只不过是图像中连接的曲线,或者图像中连通部分的边界,轮廓通常以图像中的边缘来计算,但是,边缘和轮廓的区别在于轮廓是闭合的,而边缘可以是任意的.边缘的概念局限于点及其邻域像素,轮廓将目标作为整体进行 ...

  2. OpenCV中图像轮廓检测

    OpenCV中图像轮廓检测 通过之前的Canny方法可以得到图像的边界,但是我们无法得到边界的数学信息.所以就有了今天的图像轮廓检测. 在OpenCV中图像轮廓检测的API: findContours ...

  3. Python+OpenCV:图像轮廓

    Python+OpenCV:图像轮廓 轮廓是什么? 轮廓可以简单地解释为一条连接所有连续点(沿边界)的曲线,具有相同的颜色和强度. 轮廓线是形状分析.目标检测和识别的重要工具. 为了获得更好的精度,可 ...

  4. opencv 利用图像轮廓函数填充圆环内部 图像轮廓函数应用小例子

    实验: import cv2 as cv import numpy as np# 1.读入圆环 img = cv.imread('circle_ring.jpg') img_gray = cv.cvt ...

  5. opencv 绘制图像轮廓

    图像轮廓概念 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形. 谈起轮廓不免想到边缘,它们确实很像.简单的说,轮廓是连续的,边缘并不全都连续(下图).其实边缘主要是作为图像的特征使用,比如可以用 ...

  6. 我的Qt作品(7)使用Qt+OpenCV实现图像轮廓提取,再用三阶贝塞尔曲线拟合成光滑线条/多边形拟合

    一.贝塞尔曲线基础知识 给一系列顶点,如果只是用直线将其中的各个点依次连接起来,最终形成一个折线图,这种很容易实现.但是现实中事物的变化往往具有连续的特性,即使是给定了一系列离散的点,基于以往的生活经 ...

  7. opencv:图像轮廓检测-细胞轮廓

    对于下面的图像,进行细胞(有彩色的都是细胞)的轮廓识别,要求分割尽可能准确,轮廓是封闭的曲线. 个人的思路: 第一:通过opencv读取图片: 第二:对原图进行灰度化处理,简化矩阵,提高处理速度: 第 ...

  8. OpenCV | OpenCV检测图像轮廓

    步骤 读取图像为灰度图像. 使用cv2.threshold()函数获取阈值图像. 使用cv2.findContours()并传递阈值图像和必要的参数. findContours()返回轮廓.您可以将其 ...

  9. OpenCV之图像梯度

    1. Sobel算子 OpenCV系列-本文底页有多个常用方法链接 1.1 Sobel介绍 cv2.Sobel(src, ddepth, dx, dy, ksize) ddepth:图像的深度 dx和 ...

最新文章

  1. 自动驾驶安全框架开发进展综述
  2. 函数和闭包之头等函数
  3. HDU1671-Phone List (trie树)
  4. 2020 年开源许可证最新趋势:67% 为宽松许可证
  5. html页面右侧滚动,HTML5页面点击和左右滑动页面滚动
  6. java代码运行linux shell操作
  7. 19/100. Subtree of Another Tree
  8. EcmaScript正則表達式( 深入淺出系列之淺出 )
  9. php 判断心跳包报错,第29问:MySQL 的复制心跳说它不想跳了
  10. 微机原理实验8254计算机钢琴,GitHub - SincereXIA/PianoMFC: 西电微机原理课设项目,键盘电子乐器演奏程序设计(电子琴),MFC...
  11. 使用VS2015编写C/C++开始步骤
  12. 《大厂内部资料》Redis 性能优化的 13 条军规!全网首发
  13. Django day 36 支付宝支付,微信推送
  14. 报错:mysqldump‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。
  15. P9:卷积神经网络的工程实践技巧
  16. 典型相关分析(cca)原理_微生物多样研究—关联分析及系统发生进化关系
  17. MDC300上激光雷达的接入
  18. 云计算技术学习书籍推荐
  19. linux scp 自动备份,linux Oracle 自动备份并scp
  20. IPU VPU GPU的关系

热门文章

  1. 蓝桥杯 stm32 DAC
  2. 无法打开模块文件“C:\Users\sq\AppData\Local\Temp\.NETFramework,Version=v4.5,AssemblyAttributes.vb”:系统找不到指定的文件
  3. 新手入门:你适合做程序员吗?
  4. 换钱问题(经典枚举样例)
  5. c语言百万富翁换钱的程序,百万富翁的换钱计划.docx
  6. 《庄子·杂篇·寓言第二十七》
  7. 物联网渐行渐近 你准备好了吗?
  8. UE4项目迁移----纯蓝图项目
  9. 自媒体账号ID应该怎么取?
  10. 自定义View实现五子棋游戏