OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)

本篇博客继续讲解OpenCV图像处理的基础,为最后实现OpenCV的小项目打基础。

OpenCV自学记录(3)——图像处理基础(图像平滑、边缘检测)

  • 1、图像平滑(模糊处理)
    • 1.1均值滤波
    • 1.2高斯滤波
    • 1.3中值滤波
    • 1.4双边滤波
  • 2、边缘检测
    • 2.1sobel算子
    • 2.2canny算子
    • 2.3Laplacian算子
    • 2.4scharr滤波器
  • 3、总结

1、图像平滑(模糊处理)

平滑处理(smoothing)也称模糊处理(bluring),它是一种滤波操作,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,本质上就是移除图像的高频部分(噪音,边缘等),主要是用来减少图像上的噪点或失真,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
滤波目的1、抽出对象的特征作为图像识别的特征模式。2、消除图像数字化时混入的噪声

1.1均值滤波

均值滤波又叫归一化滤波,用输出像素点核窗口内的像素均值代替输出点像素值。可以使用函数cv2.blur()cv2.boxFilter() 来完成。
代码及运算效果如下:

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('02.jpg',0)

# 参数说明:img表示输入的图片,(5, 5)表示进行均值滤波的方框大小
blur = cv2.blur(img,(5,5))
# 当normalize=True时,与均值滤波结果相同,normalize=False,表示对加和后的结果不进行平均操作,大于255的使用255表示
# -1表示输出图像深度与输入图像一致
boxFilter = cv2.boxFilter(img,-1,(5,5), normalize=True )
# 用matplotlib模块输出结果
plt.subplot(131),plt.imshow(img,'gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(132),plt.imshow(blur,'gray'),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.subplot(133),plt.imshow(boxFilter,'gray'),plt.title('boxFilter')
plt.xticks([]), plt.yticks([])
plt.show()

OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)_第1张图片

1.2高斯滤波

是实际中最常用的滤波器,高斯滤波是将输入数组的每一个像素点与高斯内核卷积将卷积和当作输出像素值。高斯核相当于对输出像素的邻域赋予不同的权值,输出像素点所在位置的权值最大(对应高斯函数的均值位置),实现的函数是cv2.GaussianBlur()高斯核可以有效的去除图像的高斯噪声当然也可以自己构造高斯核,相关函数为:cv2.GaussianKernel()。
首先介绍cv2.GaussianBlur()函数的参数意义:

cv2.GaussianBlur(src, ksize, sigmaX[, sigmaY[, borderType]]])

参数意义
src:输入输出图片,borderType为边界填充方式。
ksize:卷积核大小,即邻域大小,比如ksize为(3,3),则对以中心点为中心点3 * 3的邻域做操作;
sigmaX:高斯滤波器在X(水平)方向上的标准差;
sigmaY:高斯滤波器在Y(竖直)方向上的标准差,如果sigmaY为零,则将其设置为等于sigmaX;如果两个sigmas为零,则分别从ksize.width和ksize.height计算得出
borderType:为边界填充方式

代码实现及计算效果:

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('02.jpg',0)
# 高斯滤波,sigmaX和sigmaY设为0
GaussianBlur = cv2.GaussianBlur(img,(5,5),0)

plt.subplot(121),plt.imshow(img,'gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(GaussianBlur,'gray'),plt.title('GaussianBlur')
plt.xticks([]), plt.yticks([])
plt.show()

OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)_第2张图片

1.3中值滤波

中值滤波将图像的每个像素用邻域(以当前像素为中心的正方形区域)像素的中值代替对椒盐噪声最有效的滤波器,去除跳变点非常有效。实现函数为 cv2.medianBlur()
代码及运算效果如下:

# 中值滤波
import cv2
from matplotlib import pyplot as plt

img = cv2.imread('02.jpg',0)
# 中值滤波:img是输入图像。5为滤波器模板大小,因为模板为正方形,所以只有一个参数。
median = cv2.medianBlur(img,5)

plt.subplot(121),plt.imshow(img,'gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(median,'gray'),plt.title('median')
plt.xticks([]), plt.yticks([])
plt.show()

OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)_第3张图片

1.4双边滤波

双边滤波是一种可以保证边界清晰的去噪的滤波器。为避免滤波器平滑图像去噪的同时使边缘也模糊,这种情况下使用双边滤波器。实现函数为函数 cv2.bilateralFilter()
首先介绍一下cv2.bilateralFilter()函数:

cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]) → dst

参数解释
src:输入图像
d:过滤时周围每个像素领域的直径
sigmaColor:在color space中过滤sigma。参数越大,临近像素将会在越远的地方mix。
sigmaSpace:在coordinate space中过滤sigma。参数越大,那些颜色足够相近的的颜色的影响越大。

代码实现及计算效果:

import cv2
from matplotlib import pyplot as plt

img = cv2.imread('02.jpg',0)

#9 邻域直径,两个75 分别是空间高斯函数标准差,灰度值相似性高斯函数标准差
bilateralFilter = cv2.bilateralFilter(img,9,75,75)

plt.subplot(121),plt.imshow(img,'gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(bilateralFilter,'gray'),plt.title('bilateralFilter')
plt.xticks([]), plt.yticks([])
plt.show

OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)_第4张图片

2、边缘检测

图像边缘是图像最基本的特征之一,图像边缘是图像最基本的特征,边缘在图像分析中起着重要的用。边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。如果图像中边缘能够精确的测量和定位,那么,就意味着实际的物体能够被定位和测量,包括物体的面积、物体的直径、物体的形状等就能被测量

边缘检测的一般步骤为:
1、滤波:边缘检测算法主要是基于图像强度的一阶和二阶导数,但是导数对于噪声很敏感,因此需要采用滤波器来改善与噪声有关的边缘检测器的性能
2、增强:增强边缘的基础是确定图像各点邻域强度的变化值。增强算法可以将灰度点邻域强度值有显著变化的点凸显出来
3、检测:邻域中有很多的点的梯度值较大,但是在特定的应用中,这些点并不是要找的边缘点,需要取舍
链接

2.1sobel算子

Sobel算子是一种常用的边缘检测算子,是一阶的梯度算法,Sobel导数结合了高斯平滑和微分求导。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。Canny边缘检测用到了sobel算子来计算梯度幅值和方向
Sobel算子的计算过程:
1、分别在x和y两个方向求导
水平变化,将 I I I与一个奇数大小的内核 G x Gx Gx进行卷积。
垂直变化:将 I I I与一个奇数大小的内核 G y Gy Gy进行卷积
2、在图像的每一点用 G = √ ( G 2 x + G 2 y ) G=√(G^2x+G^2y) G=(G2x+G2y)替代: G = ∣ G x ∣ + ∣ G y ∣ G=|Gx|+|Gy| G=Gx+Gy
首先介绍实现函数cv2.sobel():

edge = cv2.Sobel(src, ddepth, dx, dy[, ksize[, scale[, delta[, borderType]]]])

参数意义:
src:输入图像
ddepth: 输出图像的深度(可以理解为数据类型),-1表示与原图像相同的深度
dx,dy:当组合为dx=1,dy=0时求x方向的一阶导数,当组合为dx=0,dy=1时求y方向的一阶导数(如果同时为1,通常得不到想要的结果)
ksize:(可选参数)Sobel算子的大小,必须是1,3,5或者7,默认为3。
scale:(可选参数)将梯度计算得到的数值放大的比例系数,效果通常使梯度图更亮,默认为1
delta:(可选参数)在将目标图像存储进多维数组前,可以将每个像素值增加delta,默认为0
borderType:(可选参数)决定图像在进行滤波操作(卷积)时边沿像素的处理方式,默认为BORDER_DEFAULT
返回值:梯度图

注意,在进行Sobel算子处理之后,还需要调用convertScaleAbs()函数计算绝对值,并将图像转换为8位图进行显示。

dst = convertScaleAbs(src[, dst[, alpha[, beta]]])

参数意义
src表示原数组
dst表示输出数组,深度为8位
alpha表示比例因子
beta表示原数组元素按比例缩放后添加的值

import cv2
img = cv2.imread("02.jpg", 0)

# 利用sobel算子计算图像的梯度
# cv2.sobel(src,ddepth,dx,dy,[ksize])
# ddepth表示图像的深度,一般设置为cv2.CV_64F,当处理为8位图像时,当梯度小于0时,会自动变成0,造成边界图像丢失
sobelx = cv2.Sobel(img, cv2.CV_64F, dx=1, dy=0)  # x方向的图像梯度
# 使cv2.convertScaleAbs()函数将结果转化为原来的uint8的形式
sobelx = cv2.convertScaleAbs(sobelx)
#使用64位有符号的数据类型,即cv2.CV_64F
sobely = cv2.Sobel(img, cv2.CV_64F, dx=0, dy=1)  # y方向的图像梯度
sobely = cv2.convertScaleAbs(sobely)

result = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)  # x方向和y方向的梯度权重叠加
cv2.imshow("Original", img)
cv2.imshow("sobelx", sobelx)
cv2.imshow("sobely", sobely)
cv2.imshow("result", result)
cv2.waitKey()
cv2.destroyAllWindows()

2.2canny算子

Canny算子是多级边缘检测算法,Canny边缘检测步骤:
(1)消除噪声:使用高斯平滑滤波器卷积降噪;
(2)计算梯度幅值和方向:此处按照Sobel滤波器的步骤;
(3)非极大值抑制:排除非边缘像素,仅仅保留一些细线条;
(4)滞后阈值:滞后阈值需要两个阈值(高阈值和低阈值)。1、若某一像素位置的幅值超过高阈值,该像素被保留为边缘像素2、若某一像素位置的幅值小于低阈值,该像素被排除3、若某一像素位置的幅值在两个阈值之间,该像素仅仅在连接一个高于高阈值的像素时被保留

首先介绍Canny边缘检测的实现函数cv2.Canny():

edge = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]])

各参数意义:
image:原始图像;
threshold1:阈值1;
threshold2:阈值2;其中,较大的阈值2用于检测图像中明显的边缘,但一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的。所以这时候用较小的第一个阈值用于将这些间断的边缘连接起来。
apertureSize:可选参数‘int类型的apertureSize,表示应用Sobel算子的孔径大小,默认值为3
L2gradient:bool类型的L2gradient,一个计算图像梯度幅值的标识,默认值false
edge:函数返回的是二值图,包含检测出的边缘

检测代码与效果图如下:

# canny边缘检测
import cv2

img = cv2.imread("02.jpg", 0)  # Canny只能处理灰度图,所以将读取的图像转成灰度图
# 高斯滤波处理原图像降噪
img = cv2.GaussianBlur(img, (3, 3), 0)
#两个阈值设置为50,150, apertureSize默认为3
canny = cv2.Canny(img, 50, 150) 

cv2.imshow('img', img)
cv2.imshow('Canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)_第5张图片

2.3Laplacian算子

拉普拉斯算子常用于图像增强领域和边缘提取。它通过灰度差分计算邻域内的像素,基本流程是:判断图像中心像素灰度值与它周围其他像素的灰度值,如果中心像素的灰度更高,则提升中心像素的灰度;反之降低中心像素的灰度,从而实现图像锐化操作。
Laplacian算子分为四邻域和八邻域,四邻域是对邻域中心像素的四方向求梯度,八邻域是对八方向求梯度。实现函数cv2.Laplacian()

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

src:输入图像
dst:输出的边缘图,其大小和通道数与输入图像相同
ddepth:目标图像所需的深度
ksize:用于计算二阶导数的滤波器的孔径大小,其值必须是正数和奇数,且默认值为1
scale:计算拉普拉斯算子值的可选比例因子。当ksize=1时,Laplacian()函数采用3×3的孔径(四邻域模板),ksize=3,Laplacian()函数采用3×3的孔径(八邻域模板)。默认值为1,
delta:将结果存入目标图像之前,添加到结果中的可选增量值,默认值为0
borderType:边框模式,更多详细信息查阅BorderTypes

注意:Laplacian算子其实主要是利用Sobel算子的运算,通过加上Sobel算子运算出的图像x方向和y方向上的导数,得到输入图像的图像锐化结果。同时,在进行Laplacian算子处理之后,还需要调用convertScaleAbs()函数计算绝对值,并将图像转换为8位图进行显示

import cv2
import matplotlib.pyplot as plt

# 读取图像
img = cv2.imread("02.jpg", 0)

# 拉普拉斯算法,8邻域模板
dst = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
Laplacian = cv2.convertScaleAbs(dst)

# 用来正常显示中文标签
plt.rcParams['font.sans-serif'] = ['SimHei']

# 显示图形
titles = [u'原始图像', u'Laplacian算子']
images = [img, Laplacian]
for i in range(2):
    plt.subplot(1, 2, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

OpenCV自学记录(3)——(一拳超人)图像处理基础(图像平滑、边缘检测)_第6张图片

2.4scharr滤波器

使用Scharr滤波器运算符计算x或y方向的图像差分。其实它的参数变量和Sobel基本上是一样的,除了没有ksize核的大小
计算图像差分:Scharr()函数,该函数仅作用于大小为3的内核。该函数的运算与Sobel函数一样快,但结果却更加精确
基本和Sobel算子一样,只是没有了超参数ksize,使用固定的3*3ksize

edge = cv2.Scharr(src, ddepth,dx, dy[, scale[, delta[, borderType]]]]);

参数意义
src:为输入图像,填Mat类型即可。
ddepth:输出图像的深度,支持如下src.depth()和ddepth的组合:
1、若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
2、若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
3、若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
4、若src.depth() = CV_64F, 取ddepth = -1/CV_64F
dx:x方向上的差分阶数。
dy:y方向上的差分阶数。
scale:计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的。
delta:表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0。
borderType:边界模式,默认值为BORDER_DEFAULT。

import cv2
img = cv2.imread("02.jpg", 0)

# 利用scharr处理计算图像的梯度
# x方向的
sobelimgx = cv2.Scharr(img,cv2.CV_16S,dx=1,dy=0)
# 使cv2.convertScaleAbs()函数将结果转化为原来的uint8的形式
sobelimgx = cv2.convertScaleAbs(sobelimgx)
# y方向的
sobelimgy = cv2.Scharr(img,cv2.CV_16S,dx=0,dy=1)
sobelimgy = cv2.convertScaleAbs(sobelimgy)

result = cv2.addWeighted(sobelimgx, 0.5, sobelimgy, 0.5, 0)  # x方向和y方向的梯度权重
cv2.imshow("Original", img)
cv2.imshow("sobelimgx", sobelimgx)
cv2.imshow("sobelimgy", sobelimgy)
cv2.imshow("result", result)
cv2.waitKey()
cv2.destroyAllWindows()

3、总结

本篇博客介绍了
1、图像平滑(模糊处理)——即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,本质上就是移除图像的高频部分(噪音,边缘等)主要是用来减少图像上的噪点或失真。介绍了4中常用的方法:均值滤波、高斯滤波、中值滤波、双边滤波。
2、边缘检测——通过求导等操作获得图像中高频部分(噪音,边缘等),边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。介绍了4种方法:sobel算子、canny算子、Laplacian算子、scharr滤波器。
下篇博客介绍OpenCV的模版匹配和图形轮廓的方法。

你可能感兴趣的:(OPenCV自学记录,python,opencv,计算机视觉)