day29 学习笔记


前言

  • 通过今天的学习,我掌握了OpenCV中有关图像梯度处理,图像边缘检测以及图像轮廓的基本原理和操作

一、图像梯度处理

  • 如果我们将图像想象成连续的函数(实际上是离散的),边缘部分的像素值是与旁边像素明显有区别的,所以对图片局部求极值,就可以得到整幅图片的边缘信息了。
  • cv.filter2D(src,ddepth,kernel)
  • src:指输入的图像
  • ** ddepth**:指图像的深度,也就是数据类型
  • kernel:指卷积核
  • 这里又使用到了卷积核,在处理图像边缘像素点时采用边界反射101进行边缘填充
  • 同时,该函数还允许自定义kernel
img = cv.imread(r"D:\AI\笔记课件\images\tu.png")
kernel = np.array([[-1,0,1],
                   [-2,0,2],
                   [-1,0,1]],dtype=np.float32)
kernel = kernel.T
# print(kernel)
img1 = cv.filter2D(img,-1,kernel)
cv.imshow('img',img)
cv.imshow('img1',img1)
cv.waitKey(0)
cv.destroyAllWindows()

tips:使用转置即可提取另外一个维度的边缘特征

二、图像边缘检测

  • 图像的边缘检测主要包括:取出噪声,计算图像梯度与方向,非极大值抑制以及双阈值筛选几个步骤
  • 至于为什么要进行去除噪点:主要是由于噪点和图像边缘的像素点都是像素值较周围点变化较大的点,因此在边缘检测时,噪点会影响边缘像素点的判断,需要提前处理
  • 这里讨论Canny算法背景下的图像边缘检测

1.高斯滤波

  • Canny算法中默认使用高斯滤波来对图像进行去除噪点的操作
  • 在实际操作中,这种默认的方法可能无法很好地去除噪点,这是我们可以先分析噪点类型,再手动去除噪点以获得更好的效果

2.计算图像梯度与方向

  • 上一节提到,我们主要利用卷积核来计算梯度,这里介绍两种常见的算子

(一)Sobel算子

  • 垂直方向计算梯度:
    G x = k 1 × s r c G_{x}=k_{1}\times s r c Gx=k1×src
    再在水平方向计算梯度:
    G y = k 2 × s r c G_{y}=k_{2}\times s r c Gy=k2×src
    最后求出总梯度:
    G = G x 2 + G y 2 G={\sqrt{G x^{2}+G y^{2}}} G=Gx2+Gy2

sobel_image = cv2.Sobel(src, ddepth, dx, dy, ksize)

src:这是输入图像,通常应该是一个灰度图像(单通道图像),因为 Sobel 算子是基于像素亮度梯度计算的。在彩色图像的情况下,通常需要先将其转换为灰度图像。

ddepth:这个参数代表输出图像的深度,即输出图像的数据类型。在 OpenCV 中,-1 表示输出图像的深度与输入图像相同。

dx,dy:当组合为dx=1,dy=0时求x方向的一阶导数,在这里,设置为1意味着我们想要计算图像在水平方向(x轴)的梯度。当组合为dx=0,dy=1时求y方向的一阶导数(如果同时为1,通常得不到想要的结果)

ksize:Sobel算子的大小,可选择3、5、7,默认为3。

(二)Laplacian算子

  • 对于二维函数f(x,y),两个方向的二阶差分分别是:
    ∂ 2 f ∂ x 2 = f ( x + 1 , y ) + f ( x − 1 , y ) − 2 f ( x , y ) {\frac{\partial^{2}f}{\partial x^{2}}}=f(x+1,y)+f(x-1,y)-2f(x,y) x22f=f(x+1,y)+f(x1,y)2f(x,y)

∂ 2 f ∂ y 2 = f ( x , y + 1 ) + f ( x , y − 1 ) − 2 f ( x , y ) {\frac{\partial^{2}f}{\partial y^{2}}}=f(x,y+1)+f(x,y-1)-2f(x,y) y22f=f(x,y+1)+f(x,y1)2f(x,y)

合在一起就是:
V 2 f ( x , y ) = f ( x + 1 , y ) + f ( x − 1 , y ) + f ( x , y + 1 ) + f ( x , y − 1 ) − 4 f ( x , y ) V^{2}f(x,y)=f(x+1,y)+f(x-1,y)+f(x,y+1)+f(x,y-1)-4f(x,y) V2f(x,y)=f(x+1,y)+f(x1,y)+f(x,y+1)+f(x,y1)4f(x,y)

二维的Laplacian滤波核就是:
k = [ 0 1 0 1 − 4 1 0 1 0 ] k=\left[\begin{array}{c c c}{0}&{1}&{0}\\ {1}&{-4}&{1}\\ {0}&{1}&{0}\end{array}\right] k= 010141010

有些资料中在此基础上考虑斜对角情况,将卷积核拓展为:
k = [ 1 1 1 1 − 8 1 1 1 1 ] k=\left[\begin{array}{c c c}{1}&{1}&{1}\\ {1}&{-8}&{1}\\ {1}&{1}&{1}\end{array}\right] k= 111181111
cv2.Laplacian(src, ddepth)

src:这是输入图像

ddepth:这个参数代表输出图像的深度,即输出图像的数据类型。在 OpenCV 中,-1 表示输出图像的深度与输入图像相同。

(三)非极大值抑制

  • 在计算得到梯度的方向后,如果发现梯度方向不是0°、45°、90°、135°这种特定角度,那么就要用到插值算法来计算当前像素点在其方向上进行插值的结果了,然后结合非极大值抑制进行比较并判断是否保留该像素点
  • 所谓“非极大值抑制”,就是我们需要检查每个像素点的梯度方向上的相邻像素,并保留梯度值最大的像素,将其他像素抑制为零。假设当前像素点为(x,y),其梯度方向是0°,梯度值为G(x,y),那么我们就需要比较G(x,y)与两个相邻像素的梯度值:G(x-1,y)和G(x+1,y)。如果G(x,y)是三个值里面最大的,就保留该像素值,否则将其抑制为零。
    在这里插入图片描述

(四)双阈值筛选

  • 经过非极大值抑制之后,我们还需要设置阈值来进行筛选,当阈值设的太低,就会出现假边缘,而阈值设的太高,一些较弱的边缘就会被丢掉,因此使用了双阈值来进行筛选,推荐高低阈值的比例为2:1到3:1之间

(五)API实现

  • edges = cv2.Canny(image, threshold1, threshold2),该方法封装了以上步骤

  • 可以先对图像进行噪声类型的分析,再手动去除噪点,以得到更好的效果

  • image:输入的灰度/二值化图像数据。

  • threshold1:低阈值,用于决定可能的边缘点。

  • threshold2:高阈值,用于决定强边缘点。

img = cv.imread(r"D:\AI\笔记课件\images\shudu.png",cv.IMREAD_GRAYSCALE)
_,img_binary = cv.threshold(img,127,255,cv.THRESH_BINARY)
dst = cv.Canny(img_binary,threshold1=30,threshold2=70)
cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()

三、图像轮廓

  • 轮廓是一系列相连的点组成的曲线,代表了物体的基本外形。相对于边缘,轮廓是连续的,边缘不一定连续,如下图所示。轮廓是一个闭合的、封闭的形状。
  • 寻找轮廓需要将图像做一个二值化处理,并且根据图像的不同选择不同的二值化方法来将图像中要绘制轮廓的部分置为白色,其余部分置为黑色
  • 简单来说,当一个白色像素相邻(上下左右及两条对角线)位置有黑色像素存在或者一个黑色像素相邻(上下左右及两条对角线)位置有白色像素存在时,那么该像素点就会被认定为边界像素点

1.寻找轮廓

  • contours,hierarchy = cv2.findContours(image,mode,method)

  • 返回值:[ 轮廓点坐标 ] 和 [ 层级关系 ]。

  • contours:表示获取到的轮廓点的列表。检测到有多少个轮廓,该列表就有多少子列表,每一个子列表都代表了一个轮廓中所有点的坐标。

  • hierarchy:表示轮廓之间的关系。对于第i条轮廓, h i e r a r c h y [ i ] [ 0 ] hierarchy[i][0] hierarchy[i][0], h i e r a r c h y [ i ] [ 1 ] hierarchy[i][1] hierarchy[i][1] , h i e r a r c h y [ i ] [ 2 ] hierarchy[i][2] hierarchy[i][2] ,$ hierarchy[i][3]$分别表示其后一条轮廓、前一条轮廓、(同层次的第一个)子轮廓、父轮廓的索引(如果没有相应的轮廓,则对应位置为-1)。该参数的使用情况会比较少。

  • image:表示输入的二值化图像。

  • mode:表示轮廓的检索模式。

  • method:轮廓的存储方法。

2.绘制轮廓

  • cv2.drawContours(image, contours, contourIdx, color, thickness)

  • image:原始图像,一般为单通道或三通道的 numpy 数组。

  • contours:包含多个轮廓的列表,每个轮廓本身也是一个由点坐标构成的二维数组(numpy数组)。

  • contourIdx:要绘制的轮廓索引。如果设为 -1,则会绘制所有轮廓。根据索引找到轮廓点绘制出来。默认是-1。

  • color:绘制轮廓的颜色,可以是 BGR 值或者是灰度值(对于灰度图像)。

  • thickness:轮廓线的宽度,如果是正数,则画实线;如果是负数,则填充轮廓内的区域。

3.代码实现

img1 = cv.imread(r"D:\AI\笔记课件\images\num.png")
img = cv.cvtColor(img1,cv.COLOR_BGR2GRAY)
_,img_binary = cv.threshold(img,127,255,cv.THRESH_BINARY_INV) #目标区域显示为白色,其他区域显示为黑色
contours,hierarchy = cv.findContours(img_binary,mode=cv.RETR_EXTERNAL,method=cv.CHAIN_APPROX_SIMPLE)
# print(contours,hierarchy)
cv.drawContours(img1,contours=contours,contourIdx=-1,color=(0,255,0),thickness=5)
cv.imshow('img',img1)
cv.waitKey(0)
cv.destroyAllWindows()

THE END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值