第三节,滤波&边缘检测
目录
一 、色彩空间转换
OpenCV
中有数百种关于在不同色彩空间之间转换的方法。当前,在计算机中有三种常用的色彩空间:灰度,BGR
以及HSV(Hue,Saturation,Value)
。
- 灰度色彩空间是通过去除色彩信息来将其转换成灰阶,灰度色彩空间对中间处理特别有效,比如人脸检测。
BGR
,即蓝-绿-红色彩空间,每一个像素点都由一个三元数组来表示,分别代表蓝、绿、红三种颜色。网页开发者可能熟悉另一个与之相似的色彩空间:RGB
,他们只是在颜色顺序上不同。HSV,H(Hue)
是色调,S(Saturation)
是饱和度,V(Value)
表示黑暗的程度(或光谱另一端的命令程度)。
在第一次处理BGR
色彩空间的时候,可以不要其中的一个色彩分量,比如像素值[0 255 255]
(没有蓝色,绿色分量取最大值,红色分量取最大值)表示黄色。
如果读者有艺术背景,会发现绿色和红色混合产生浑浊的褐色,这是因为计算所使用的颜色模型具有可加性并且处理的是光照,而绘画不是这样的(它遵从减色模型subtractive color model
)。
计算机使用显示器发光来做颜色的媒介,因此运行在计算机上的软件所使用的色彩模型是加色模型。
二、傅里叶变换
在OpenCV
中,对图像和视频的大多数处理或多或少都会涉及到傅里叶变换的概念。Joseph Fourier
(约瑟夫.傅里叶)是以为18世纪的法国数学家,他发现并推广了很多数学概念,主要研究热学规律,在数学上,他认为一切都可以用波形来描述。具体而言,他观察到所有的波形都是由一系列简单且频率不同的正弦曲线叠加得到。
也就是说人们看到的波形都是由其他波形叠加得到的。这个概念对操作图像非常有帮助,因为这样我们可以区分图像哪些区域的信号变化特别强,哪些区域的信号变化不那么强,从而可以任意地标记噪声区域,感兴趣区域,前景和背景等。原始图像由许多频率组成,人们能够分离这些频率来处理图像和提取感兴趣的数据。
下面通过傅里叶变换来介绍图像的幅度谱(magnitude specturm
)。图像的幅度谱是另一种图像,幅度谱图像呈现了原始图像在变化方面的一种表示:把一张图像中最明亮的像素放到图像中央,然后逐渐变暗,在边缘上像素最暗。这样可以发现图像中有多少亮的像素和暗的像素,以及它们分布的比例。
傅里叶变换的概念是许多常见的图像处理操作的基础,比如边缘检测或线段和形状检测。
下面介绍两个概念:高通滤波器和低通滤波器。
2.1 高通滤波器
2.1.1 介绍
高通滤波器(HPF
)是检测图像的某个区域,然后根据像素与周围像素的亮度差值来提升该像素的亮度的滤波器。
以如下的核(kernel
),即滤波器矩阵为例:
注:核是指一组权重的集合,它会应用在源图像的一个区域,并由此生成目标图像的一个像素。比如,大小为7的核意味着每49(7x7)
个源图像的像素会产生目标图像的一个像素。
可把核看做一块覆盖在源图像上可移动的毛玻璃片,玻璃片覆盖区域的光线会按某种方式进行扩散混合后透过去。
在计算完中央像素与周围邻近像素的亮度差值之和以后,如果亮度变化很大,中央像素的亮度会增加,反之则不会。换句话说,如果一个像素比它周围的像素更突出,就会提升它的亮度。
这在边缘检测上尤为有效,它采用一种称为高频提升滤波器(high boost filter
)的高通滤波器。
高通和低通滤波器都有一个半径(radius
)的属性,它决定了多大面积的临近像素参与滤波运算。
2.1.2 示例代码
下面是一个高通滤波器的例子,代码如下:
# -*- coding: utf-8 -*-
"""
Created on Fri Apr 20 21:10:35 2018
@author: Administrator
"""
'''
OPenCV3 计算机视觉 笔记
第三章 :使用Open CV3处理图像
'''
import cv2
import numpy as np
from scipy import ndimage
'''
1.傅里叶变换
'''
'''
(1)高通滤波器
'''
kernel_3x3 = np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])
kernel_5x5 = np.array([[-1,-1,-1,-1,-1],
[-1,-1, 2, 1,-1],
[-1, 2, 4, 2,-2],
[-1, 1, 2, 2,-1],
[-1,-1,-1,-1,-1]])
#读取图像,指定格式为灰度图像
img = cv2.imread('./image/img6.jpg',cv2.IMREAD_GRAYSCALE)
#进行卷积运算
k3 = ndimage.convolve(img,kernel_3x3)
k5 = ndimage.convolve(img,kernel_5x5)
#模糊滤波
blurred = cv2.GaussianBlur(img,(11,11),0)
#作差
g_hpf = img - blurred
#显示图像
cv2.imshow('original',img)
cv2.imshow('3x3',k3)
cv2.imshow('5x5',k5)
cv2.imshow('g_hpf',g_hpf)
cv2.waitKey()
cv2.destroyAllWindows()
运行后显示如下:
导入模块后,我们定义了一个3x3
和一个5x5
的核,然后将读入的图像转换成灰度格式。
通常大多数图像处理都会用Numpy
模块来完成,但是这里的情况比较特殊,因为需要用一个给定核与图像进行卷积,但是Numpy
碰巧只接受一维数组。
上面代码用了两个自定义卷积核来实现两个高通滤波器。最后又用一种不同的方法来实现高通滤波器:通过对图像应用低通滤波器之后,与原始图像计算差值。这样得到的效果会更好。
这里注意有一点需要注意:使用卷积进行运算,并不能保证的每个像素值都在0~255
之间。对于在区间外的像素点会导致灰度图无法显示,所以还需要做归一化,然后每个值乘以255
,再将所有的值映射到这个区间内。归一化算法:$$x=(x-Min)(Max-Min)$$,这样x
的范围就在[0,1]
之间了。我们在上面调用的函数ndimage.convolve()
在内部已经做了这些处理,所以我们就不需要自己写归一化的处理过程了。
2.2 低通滤波器
高通滤波器是根据像素与邻近像素的亮度差值来提升该像素的亮度。低通滤波器(LPF
)则是在像素与周围像素的亮度差值小于一定特征值,平滑该像素的亮度。它主要用于去噪和模糊化,比如说,高斯模糊是最常用的模糊滤波器(平滑滤波器)之一,它是削弱高频信号强度的低通滤波器。
三、边缘检测
边缘在人类视觉和计算机视觉中均起着重要的作用。人类能够仅凭一张背景剪影或一个草图就能识别出物体的类型和姿态。
OpenCV
提供了许多边缘检测滤波函数,包括以下:
Laplacian() #作为边缘检测函数,他会产生明显的边缘线条,灰度图像更是如此。
Sobel()
Scharr()
Candy()
....
这些滤波函数都会将非边缘区域转换为黑色,边缘区域转换成白色或其他饱和的颜色。
但是这些函数都容易将噪声错误的识别为边缘。缓解这个问题的方法就是在找到边缘之前对图像进行模糊处理,去除噪声。
OpenCV
也提供了需要模糊滤波函数,包括以下:
blur()
medianBlur() #它对去除数字化的视频噪声特别有效,特别是去除彩色图像的噪声
GaussianBlur()
边缘检测和模糊滤波的函数的参数有很多,但总会有一个ksize
参数,它是一个奇数,表示滤波核的宽和高(以像素为单位)。
3.1 滤波处理
3.1.1 均值滤波
均值滤波是一种典型的线性滤波算法,主要是利用像素点邻域的像素值来计算像素点的值。
其具体方法是首先给出一个滤波kernel
,该核将覆盖像素点周围的其他邻域像素点,去掉像素本身,将其邻域像素点相加然后取平均值即为该像素点的新的像素值,这就是均值滤波的本质。函数原型:
cv2.blur(src,ksize[,dst[,anchor[,borderType]]])
其中参数:
src
:输入图像,图像深度是cv2.CV_8U
、cv2.CV_16U
、cv2.CV_16S
、cv2.CV_32F
以及cv2.CV_64F
其中的某一个;dst
: 输出图像,深度和类型与输入图像一致;ksize
:滤波kernel
的尺寸,元组类型;anchor
:字面意思是锚点,也就是处理的像素位于kernel
的什么位置,默认值为(-1, -1)
即位于kernel
中心点,如果没有特殊需要则不需要更改;borderType
:用于推断图像外部像素的某种边界模式,有默认值cv2.BORDER_DEFAULT
。
3.1.2 中值滤波
中值滤波是一种典型的非线性滤波,是基于排序统计理论的一种能够有效抑制噪声的非线性信号处理技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,让周围的像素值接近真实的值从而消除孤立的噪声点。该方法在取出脉冲噪声、椒盐噪声的同时能保留图像的边缘细节。这些优良特性是线性滤波所不具备的。
中值滤波首先也得生成一个滤波核,将该核内的各像素值进行排序,生成单调上升或单调下降的二维数据序列,二维中值滤波输出为:
其中f(x,y)
和g(x,y)
分别是原图像和处理后图像,w
为输入的二维模板,能够在整幅图像上滑动,通常尺寸为3*3
或5*5
区域,也可以是不同的形状如线状、圆形、十字形、圆环形等。通过从图像中的二维模板取出奇数个数据进行排序,用排序后的中值取代要处理的数据即可。
中值滤波对消除椒盐噪声非常有效,能够克服线性滤波器带来的图像细节模糊等弊端,能够有效保护图像边缘信息,是非常经典的平滑噪声处理方法。在光学测量条纹图像的相位分析处理方法中有特殊作用,但在条纹中心分析方法中作用不大。函数原型:
cv2.medianBlur(src,ksize[,dst])
其中参数:
src
: 输入图像,图像为1、3、4通道的图像,当核尺寸为3或5时,图像深度只能为cv2.CV_8U
、cv2.CV_16U
、cv2.CV_32F
中的一个,如而对于较大孔径尺寸的图片,图像深度只能是cv2.CV_8U
。dst
: 输出图像,尺寸和类型与输入图像一致。ksize
: 滤波核的尺寸大小,必须是大于1的奇数,如3、5、7……
。
3.1.3 高斯滤波
高斯滤波是一种线性平滑滤波,对于除去高斯噪声有很好的效果。在其官方文档中形容高斯滤波为Probably the most useful filter
,同时也指出高斯滤波并不是效率最高的滤波算法。
高斯算法在官方文档给出的解释是高斯滤波是通过对输入数组的每个点与输入的高斯滤波核执行卷积计算然后将这些结果一块组成了滤波后的输出数组,通俗的讲就是高斯滤波是对整幅图像进行加权平均的过程,每一个像素点的值都由其本身和邻域内的其他像素值经过加权平均后得到。
高斯滤波的具体操作是:用一个核(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
在图像处理中高斯滤波一般有两种实现方式:一种是用离散化窗口滑窗卷积,另一种是通过傅里叶变换。最常见的就是第一种滑窗实现,只有当离散化的窗口非常大,用滑窗计算量非常大的情况下会考虑基于傅里叶变换的方法。
我们在参考其他文章的时候可能会出现高斯模糊和高斯滤波两种说法,其实这两种说法是有一定区别的。我们知道滤波器分为高通、低通、带通等类型,高斯滤波和高斯模糊就是依据滤波器是低通滤波器还是高通滤波器来区分的。
比如低通滤波器,像素能量低的通过,而对于像素能量高的部分将会采取加权平均的方法重新计算像素的值,将能量像素的值编程能量较低的值,我们知道对于图像而言其高频部分展现图像细节,所以经过低通滤波器之后整幅图像变成低频造成图像模糊,这就被称为高斯模糊。
相反高通滤波是允许高频通过而过滤掉低频,这样将低频像素进行锐化操作,图像变的更加清晰,被称为高斯滤波。说白了很简单就是:高斯滤波是指用高斯函数作为滤波函数的滤波操作而高斯模糊是用高斯低通滤波器。
高斯滤波在图像处理中常用来对图像进行预处理操作,虽然耗时但是数字图像用于后期应用但是其噪声是最大的问题,噪声会造成很大的误差而误差在不同的处理操作中会累积传递,为了能够得到较好的图像,对图像进行预处理去除噪声也是针对数字图像处理的无奈之举。
高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器,高斯滤波器对于服从正太分布的噪声非常有效,一维高斯函数如下:
二维高斯函数如下:
函数原型:
cv2.GaussianBlur(src,ksize,sigmaX[,sigmaxY[,borderType]]])
其中参数:
src
:输入图像,图像深度为cv2.CV_8U
、cv2.CV_16U
、cv2.CV_16S
、cv2.CV_32F
、cv2.CV_64F
。dst
: 输出图像,与输入图像有相同的类型和尺寸。ksize
: 高斯内核大小,元组类型。sigmaX
: 高斯核函数在X
方向上的标准偏差sigmaY
: 高斯核函数在Y
方向上的标准偏差,如果sigmaY
是0,则函数会自动将sigmaY
的值设置为与sigmaX
相同的值,如果sigmaX
和sigmaY
都是0,这两个值将由ksize[0]
和ksize[1]
计算而来。具体可以参考getGaussianKernel()
函数查看具体细节。建议将size
、sigmaX
和sigmaY
都指定出来。borderType
: 推断图像外部像素的某种便捷模式,有默认值cv2.BORDER_DEFAULT
,如果没有特殊需要不用更改,具体可以参考borderInterpolate()
函数。
3.2 Laplacian
边缘检测
Laplacian
(拉普拉斯)算子是一种二阶导数算子,其具有旋转不变性,可以满足不同方向的图像边缘锐化(边缘检测)的要求。通常情况下,其算子的系数之和需要为零。常用的拉普拉斯核如下:
cv2.Laplacian()
函数用于实现Laplacian
边缘检测,函数原型::
cv2.Laplacian(src,ddepth[,ksize[,scale[,delta[,borderType]]]])
其参数说明如下:
src
:为原图像;depth
:为目标图像的深度,默认是-1,与原图深度一致;ksize
:为用于计算二阶导数滤波器的系数,必须为1、3、5、7,默认为1;scale
:为可缩放导数的比例常数,默认情况下没有伸缩系数;delta
:为添加到边缘检测结果中的可选增量值,默认情况下没有额外值;borderType
:为边界值类型,默认值为cv2.BORDER_DEFAULT
;
3.3 Sobel
边缘检测
Sobel
算子它利用像素邻近区域的梯度值来计算1个像素的梯度,然后根据一定的绝对值来取舍。

Sobel
算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
cv2.Sobel()
函数用于实现Sobel
边缘检测,函数原型:
cv2.Sobel(src,depth,dx,dy,ksize,scale,delta,borderType)
其参数说明如下:
src
:为原图像;depth
:为目标图像的深度,默认是-1,与原图深度一致;dx
:为导数x的阶数;dy
:为导数y的阶数;ksize
:为用于计算二阶导数滤波器的系数,必须为1、3、5、7,默认为1;scale
:为可缩放导数的比例常数,默认情况下没有伸缩系数;delta
:为添加到边缘检测结果中的可选增量值,默认情况下没有额外值;borderType
:为边界值类型,默认值为cv2.BORDER_DEFAULT
;
3.4 Canny
边缘检测
3.4.1 Canny
原理
Candy
边缘检测算法算法非常复杂,但是也很有趣,Canny
计算过程:
- 高斯滤波器平滑图像;
- 一阶差分偏导计算梯度值和方向;
- 对梯度值不是极大值的地方进行抑制;
- 用双阈值连接图上的联通点;
通俗说一下,
- 用高斯滤波主要是去掉图像上的噪声;
- 计算一阶差分,
OpenCV
源码中也是用Sobel
算子来算的; - 算出来的梯度值,把不是极值的点,全部置0,去掉了大部分弱的边缘。所以图像边缘会变细;
- 双阈值
t1
,t2
, 是这样的,t1 <= t2
, 大于t2
的点肯定是边缘,小于t1
的点肯定不是边缘,在t1
,t2
之间的点,通过已确定的边缘点,发起8领域方向的搜索(广搜),图中可达的是边缘,不可达的点不是边缘; - 最后得出
Canny
边缘图;
OpenCV
还提供了一个非常方便的Canny()
函数,该算法非常流行,不仅是因为它的效果,还因为在OpenCV
程序中实现时非常简单;
cv2.Canny(src,threshold1,threshold2,apertureSize,L2gradient)
cv2.Candy
函数部分参数如下:
src
:第一个参数是需要处理的原图像,该图像必须是单通道的灰度图;threshold1
:第二个参数是阈值1;threshold2
:第三个参数是阈值2;apertureSize
:为计算梯度时使用的Sobel
核大小;L2gradient
:布尔值;True
: 使用更精确的L2
范数进行计算(即两个方向的导数的平方和再开方);False
:使用L1
范数(直接将两个方向导数的绝对值相加);
其中较大的阈值2用于检测图像中明显的边缘,但是一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的。所以这时候较小的第一个阈值用于将这些断续的边缘连接起来。
3.4.2 Canny
使用
测试示例:
'''
Canny边缘检测
'''
can = cv2.Canny(img,200,300)
cv2.imshow('candy',can)
cv2.waitKey()
cv2.destroyAllWindows()
3.5 总结
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了