kitti数据集_基于KITTI数据集的无人驾驶感知相关功能实现(2)

二     图像的梯度空间特征提取

fe7586a469729a1fdf3cc41bc8234f43.png 2.1  什么是图像梯度:

图像的梯度就是描述图像中灰度的变化,微积分就是求函数的变化率,即导数(梯度)。

图像的梯度相当于2个相邻像素之间的差值。图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导:在x方向,选取某个像素,假设其像素值是100,沿x方向的相邻像素分别是90,90,90,则根据上面的计算其x方向梯度分别是10,0,0。这里只取变化率的绝对值,表明变化的大小即可。

ea62f1db1a5499fec3a1f1ada70f1f45.png灰度值100和90之间亮度相差10,并不是很明显,与一大群90的连续灰度值在一起,轮廓必然是模糊的。我们注意到,如果相邻像素灰度值有变化,那么梯度就有值,如果相邻像素灰度值没有变化,那么梯度就为0。如果我们把梯度值与对应的像素相加,那么灰度值没有变化的,像素值不变,而有梯度值的,灰度值变大了,那么图像的边缘更加明显。

2ce86ecdcbefed031d1e97543a5d85c4.png 2.2  OpenCV中的图像梯度应用(检测边缘):

OpenCV提供三种类型的梯度滤波器或高通滤波器,Sobel,Scharr和Laplacian. 高通滤波器(英语:High-pass filter)是容许高频信号通过、但减弱(或减少)频率低于截止频率信号通过的滤波器。对于不同滤波器而言,每个频率的信号的减弱程度不同。它有时被称为低频剪切滤波器;Sober 基本数学原理:

cdfabc79d69a130f952d87e3622049ec.png

2.2.1  Sober 算子

Sober 算子是离散微分算子(discrete differentiation operator),用来计算图像灰度的近似梯度。

cv2.Sobel(src, ddepth, dx, dy, dst=None, ksize=None, scale=None, delta=None, borderType=None)

  • src : 原图像
  • ddepth : 目标图像的深度,填写 -1 则保存与原图像的深度一致;图像深度是单个像素点的色彩详细度,往往把像素深度说成是图像深度。表示一个像素的位数越多,它能表达的颜色数目就越多,而它的深度就越深。

src.depth() 为原图像的深度,则一般对应的ddepth为:

sec.depth()ddepth
CV_8U-1/CV_16S/CV_32F/CV_64F
CV_16U/CV_16S-1/CV_32F/CV_64F
CV_32F-1/CV_32F/CV_64F
CV_64F-1/CV_64F
  • dx:表示在x方向上求导的阶数
  • dy:表示在y方向上求导的阶数 如果是0则表示在该方向上不求导(不能2个方向都是0) 一阶导数可以用来描述原函数的增减性。二阶导数可以用来判断函数在一段区间上的凹凸性,,则是凹的,则是凸的。
  • dst : 表示输出图像
  • ksize :内核大小 如果不填写或者 ,那么使用scharr内核过滤因子。scharr的内核过滤因子大小为3。,
c5532f1230d73c0e2464a472f715612b.png
  • scale : scale是缩放导数的比例常数,默认情况下没有伸缩系数;
  • delta :在将目标图像存储进多维数组前,可以将每个像素值增加delta,默认为0;
  • borderType :是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
27ae0845d4fc217ab1abe995ab44473f.png
    import cv2
img = cv2.imread("first.jpg")
GRAYImg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
SoberImg = cv2.Sobel(GRAYImg,-1,0,1)
cv2.imshow("img",img)
cv2.imshow("GRAYimg",GRAYImg)
cv2.imshow("Sober",SoberImg)
cv2.waitKey()
cv2.destroyAllWindows()
e035235fa95bb32bdfbd1cb9294b765b.png

Sobel算子算法的优点是计算简单,速度快。但是由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,因此这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。该算法认为:凡灰度新值大于或等于阈值的像素点时都是边缘点。这种判断有点欠合理,可能会造成边缘点的误判,因为许多噪声点的灰度值也很大。

2.2.2  Scharr 算子

cv2.Scharr(src, ddepth, dx, dy, dst=None, scale=None, delta=None, borderType=None)

  • 和: dx >= 0 && dy >= 0 && dx+dy == 1 $
  • 会看到参数和Sobel算子一致,不过,该函数与Sobel的区别在于,Scharr仅作用于大小为3的内核。但结果更为精确
  • scharr算子与Sobel的不同点是在平滑部分,这里所用的平滑算子是 ,相比于 ,中心元素占的权重更重
349e5a264d4950955473f09d732fba5d.png

大小一样,故计算量一样。scharr算子临近像素的权重更大,故精确度更高。对比两种算子的处理效果。发现scharr算子能计算出更小的梯度变化 Scharr:

46d15166156271b1d186884fffd044e5.png
  
import cv2
img = cv2.imread("first.jpg")
GRAYImg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
Scharr = cv2.Scharr(GRAYImg,-1,0,1)
cv2.imshow("img",img)
cv2.imshow("GRAYimg",GRAYImg)
cv2.imshow("Scharr",Scharr)
cv2.waitKey()
cv2.destroyAllWindows()

2.2.3  Laplacian 算子

Laplacian算子属于二阶微分算子。  二阶微分的边缘定位能力更强,锐化效果更好

102e5907f087fea8373c542f7bc68b68.png

f592be6c9658de610292bfe2e415283d.png   算子进行边缘检测并没有像的平滑过程,所以它会对噪声产生较大的响应,并且无法分别得到水平方向、垂直方向或者其他固定方向的的边缘。但是它只有一个卷积核,所以计算成本会更低。

ce48684e3149ebaaa22e8a374179c089.png

从模板形式容易看出,如果在图像中一个较暗的区域中出现了一个亮点,那么用拉普拉斯运算就会使这个亮点变得更亮。因为图像中的边缘就是那些灰度发生跳变的区域,所以拉普拉斯锐化模板在边缘检测中很有用。一般增强技术对于陡峭的边缘和缓慢变化的边缘很难确定其边缘线的位置。但此算子却可用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、孤立线或线端点为目的的场合。同梯度算子一样,拉普拉斯算子(Laplacian)也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。图像锐化处理的作用是使灰度反差增强,从而使模糊图像变得更加清晰。图像模糊的实质就是图像受到平均运算或积分运算 。因此可以对图像进行逆运算,如微分运算能够突出图像细节,使图像变得更为清晰。由于拉普拉斯是一种微分算子,它的应用可增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。

cv2.Laplacian(src, ddepth, dst=None, ksize=None, scale=None, delta=None, borderType=None)

  • src :原图像
  • ddepth:输出图像深度,-1则为保持原图像一致
  • src.depth() 为原图像的深度,则一般对应的ddepth为:
深度取值范围
CV_8U0–255
CV_8S-128–127
CV_16U0–65535
CV_16S-32768–32767
CV_32S0–65535
CV_32F(单精度浮点数)0.0–1.0
CV_64F(双精度浮点数 )0.0–1.0
src.depth()ddepth
CV_8U-1/CV_16S/CV_32F/CV_64F
CV_16S-1/CV_32F/CV_64F
CV_32F-1/CV_32F/CV_64F
CV_64F-1/CV_64F

转换深度后的图像可能需要归一化;

  • dst:处理后的图像
  • ksize:内核大小(大小必须为正奇数)(一般为1、3、5、7。默认为1)
  • scale是缩放导数的比例常数,默认情况下没有伸缩系数;
  • delta是一个可选的增量,将会加到最终的dst中,同样,默认情况下没有额外的值加到dst中;
  • borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。
27ae0845d4fc217ab1abe995ab44473f.png
import cv2
img = cv2.imread("first.jpg",0) #灰度模式读取图像
LaplacianImg = cv2.Laplacian(img,-1)
cv2.imshow("img",img)
cv2.imshow("Laplacian",LaplacianImg)
cv2.waitKey()
cv2.destroyAllWindows()
fba351667998731fcdcc20b3f141bbb9.png 2.3 &nbsp 边缘检测:

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括(i)深度上的不连续、(ii)表面方向不连续、(iii)物质属性变化和(iv)场景照明变化。边缘检测是图像处理和计算机视觉中,尤其是特征提取中的一个研究领域。图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。 有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。边缘检测的一般步骤:

  • 滤波:边缘检测的算法主要是基于图像强度的一阶和二阶导数,但导数通常对噪声很敏感,因此必须采用滤波器来改善与噪声有关的边缘检测器的性能。常见的滤波方法主要有高斯滤波,即采用离散化的高斯函数产生一组归一化的高斯核(具体见“高斯滤波原理及其编程离散化实现方法”一文),然后基于高斯核函数对图像灰度矩阵的每一点进行加权求和(具体程序实现见下文)。
  • 增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将图像灰度点邻域强度值有显著变化的点凸显出来。在具体编程实现时,可通过计算梯度幅值来确定。
  • 检测:经过增强的图像,往往邻域中有很多点的梯度值比较大,而在特定的应用中,这些点并不是我们要找的边缘点,所以应该采用某种方法来对这些点进行取舍。实际工程中,常用的方法是通过阈值化方法来检测。

Canny 边缘检测 :

原理:

  • 去噪:先经过高斯平滑

  • 增强:寻找图像强度梯度

  • 非极大抑制:这一步的目的是将模糊(blurred)的边界变得清晰(sharp)。通俗的讲,就是保留了每个像素点上梯度强度的极大值,而删掉其他的值。对于每个像素点,进行如下操作:将其梯度方向近似为以下值中的一个(0,45,90,135,180,225,270,315)(即上下左右和45度方向)

c2cb746cacb4dd935b6dbb3378e44e28.png比较该像素点,和其梯度方向正负方向的像素点的梯度强度 如果该像素点梯度强度最大则保留,否则抑制(删除,即置为0)

cc74d6fa6372e49574d176d488a44921.png
  • 双阈值

经过非极大抑制后图像中仍然有很多噪声点。Canny算法中应用了一种叫双阈值的技术。即设定一个阈值上界和阈值下界(opencv中通常由人为指定的),图像中的像素点如果大于阈值上界则认为必然是边界(称为强边界,strong edge),小于阈值下界则认为必然不是边界,两者之间的则认为是候选项(称为弱边界,weak edge)。

  • 利用滞后的边界跟踪

思想:和强边界相连的弱边界认为是边界,其他的弱边界则被抑制

滞后阈值:现在需要确定哪些边界是真正的边界,需要两个阈值,minVal和maxVal。图像灰度梯度 高于maxVal被认为是真正的边界,低于minVal的舍弃。两者之间的值要判断是否与真正的边界相连,相连就保留,不相连舍弃。

  • Ⅰ.如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。

  • Ⅱ.如果某一像素位置的幅值小于 低 阈值, 该像素被排除。

  • Ⅲ.如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。

7be003fa60dbc0abc2eeb723c99e5292.png

cv2.Canny(image, threshold1, threshold2, edges=None, apertureSize=None, L2gradient=None)

  • image : 原图像
  • threshold1:阈值1
  • threshold2:阈值2
  •  低于阈值1的像素点会被认为不是边缘;
  •  高于阈值2的像素点会被认为是边缘;
  •  在阈值1和阈值2之间的像素点,若与处理后得到的边缘像素点相邻,则被认为是边缘,否则被认为不是边缘。
  •  对于Canny函数的使用,推荐的高低阈值比在2:1到3:1之间。
  • edges:输出边缘映射; 默认:单通道8位图像,与图像大小相同。
  • apertureSize:Sobel算子内核大小 (默认为3)
  • L2gredient:是否采用更精确的方式计算图像梯度(默认为 False)
import cv2
img = cv2.imread("first.jpg")
GRAYImg = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #转换为灰度图像
GauBurlGrayImg = cv2.GaussianBlur(GRAYImg,(3,3),1) #进行高斯滤波
imgCanny = cv2.Canny(GauBurlGrayImg,100,200) #边缘检测
cv2.imshow("img",img)
cv2.imshow("Canny",imgCanny)
cv2.waitKey()
cv2.destroyAllWindows()

2033203f0ee8e80a51a745f7ba9cda14.pngCanny 的特点:

  • 1.低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
  • 2.高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
  • 3.最小响应: 图像中的边缘只能标识一次,并且可能存在的图像噪声不因标识为边缘。

扩展结束

项目代码

  
import numpy as np
import cv2
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import pickle

#读入图片
image = mpimg.imread('work/test_picture/0000000086.png')

#定义Sobel函数,因为做边缘检测时只考虑梯度的大小,所以对返回结果做绝对值处理
def sobel(img, orient = 'x', sobel_kernel=3, sobel_thresh = (30, 100)):
#转换为单通道灰度图,像素值做归一化
gray = cv2.cvtColor(img, cv2.COLOR_RGBA2GRAY)
#按方向进行梯度计算
if orient == 'x':
sobel = cv2.Sobel(gray,cv2.CV_64F, 1, 0,ksize=sobel_kernel)
if orient == 'y':
sobel = cv2.Sobel(gray,cv2.CV_64F, 0, 1,ksize=sobel_kernel)
abs_sobel = np.absolute(sobel)

#转换到 8-bit (0 - 255) 并转换为type = np.uint8
scaled_sobel = (abs_sobel/(np.max(abs_sobel)/255)).astype(np.uint8)

#根据梯度阈值,创建一个二进制掩膜
binary_output = np.zeros_like(scaled_sobel)

#将原图中高于阈值的像素点在掩膜中的对应位置置为1
binary_output[(scaled_sobel>=sobel_thresh[0]) & (scaled_sobel<=sobel_thresh[1])]=1

return binary_output

#定义像素点梯度总和函数
def mag_thresh(img, sobel_kernel=3, mag_thresh=(30, 100)):
#转换为单通道灰度图
gray = cv2.cvtColor(img, cv2.COLOR_RGBA2GRAY)

#分别计算x,y方向梯度
sobelx = cv2.Sobel(gray,cv2.CV_64F, 1, 0,ksize=sobel_kernel)
sobely = cv2.Sobel(gray,cv2.CV_64F, 0, 1,ksize=sobel_kernel)

#计算梯度和
gradmag = np.sqrt(np.absolute(sobelx) ** 2 + np.absolute(sobely) ** 2)

#计算梯度角
theta = np.arctan2(np.absolute(sobely),np.absolute((sobelx)))

#转换到 8-bit (0 - 255) 并转换为type = np.uint8
scaled_gradmag = (gradmag/(np.max(gradmag)/255)).astype(np.uint8)

#根据梯度阈值,创建一个二进制掩膜
binary_output = np.zeros_like(scaled_gradmag)

#将原图中高于阈值的像素点在掩膜中的对应位置置为1
binary_output[(scaled_gradmag>=mag_thresh[0]) & (scaled_gradmag<=mag_thresh[1])]=1

return binary_output

#对比输出
Sx_binary = sobel(img=image,orient='x',sobel_kernel=3, sobel_thresh = (30, 100))
Sx_binary_wider_thresh = sobel(img=image,orient='x',sobel_kernel=3, sobel_thresh = (30, 100))
Sx_binary_Larger_kernel = sobel(img=image,orient='x',sobel_kernel=9, sobel_thresh = (30, 100))

Sy_binary = sobel(img=image,orient='y',sobel_kernel=3, sobel_thresh = (150, 200))
Sy_binary_wider_thresh = sobel(img=image,orient='y',sobel_kernel=3, sobel_thresh = (15, 200))

mag_binary = mag_thresh(img=image, sobel_kernel=3, mag_thresh=(30, 100))
mag_binary_larger_kernel = mag_thresh(img=image, sobel_kernel=9, mag_thresh=(30, 100))
mag_binary_wider_thresh = mag_thresh(img=image, sobel_kernel=3, mag_thresh=(15, 200))
#可视化结果
f, (ax1, ax2, ax3) = plt.subplots(3, 3, figsize=(20, 10))
f.tight_layout()

ax1[0].imshow(image)
ax1[0].set_title('Original Image', fontsize=30)
ax2[0].imshow(mag_binary, cmap='gray')
ax2[0].set_title('Thresholded Magnitude', fontsize=30)

ax1[1].imshow(Sx_binary, cmap='gray')
ax1[1].set_title('Sobelx', fontsize=30)
ax3[0].imshow(Sx_binary_wider_thresh, cmap='gray')
ax3[0].set_title('Sx_wider_thresh', fontsize=30)
ax3[2].imshow(Sx_binary_Larger_kernel, cmap='gray')
ax3[2].set_title('Sx_Larger_kernel', fontsize=30)

ax1[2].imshow(Sy_binary, cmap='gray')
ax1[2].set_title('Sobely', fontsize=30)
ax3[1].imshow(Sy_binary_wider_thresh, cmap='gray')
ax3[1].set_title('Sy_wider_thresh', fontsize=30)

ax2[1].imshow(mag_binary_larger_kernel, cmap='gray')
ax2[1].set_title('mag_Larger_kernel', fontsize=30)
ax2[2].imshow(mag_binary_wider_thresh, cmap='gray')
ax2[2].set_title('mag_wider_thresh', fontsize=30)
plt.subplots_adjust(left=0., right=1., top=0.9, bottom=0.,wspace=0.2,hspace=0.1)
plt.show()
````

81e8a9bf9e438b247744911386dc75c1.png由以上图像处理结果发现,Sx的边缘检测效果比Sy要更明显,y方向需要更宽的threshold范围才能显示的比较明显;x,y方向的梯度和可以更清晰的检测出图像中的所有轮廓,所以可以根据实际应用的不同来决定使用x,y或者梯度和的方式进行边缘检测;调节参数kernel_size和梯度threshold,可发现更宽的threshold会检测出更多的目标点,同时也包含了较多噪点,而kernel_size加大会使图片保留更大尺度的边缘,降低对小尺度边缘的敏感性,一定程度抑制了噪点

part2 end...
作者:joker-wt

来AI Studio互粉吧~等你哦~( https://aistudio.baidu.com/aistudio/personalcenter/thirdview/276642)

编辑不易,欢迎三连!!!❤❤❤

9b91e40801e05c93c3f9c382493ed100.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值