neozng1@hnu.edu.cn
5.3.3.在图像处理中应用的滤波器:
这部分本来准备在OpenCV中地常用函数部分介绍的,但后来又想到在前面已经稍微涉及到了那些函数的作用,那么在这里就从信号处理的角度对二维信号的分析进行叙述。这一小节概括的内容可以算是真正的图像处理,即利用一定的方法增强/提取图像的特征、消除可能的干扰等,也是比赛中传统视觉算法的实现基础。
我们也可以将图像处理、机器视觉和计算机视觉进行简单的对比,图像处理更像是传统的信号处理,把图像视为空间上二维的亮度序列信号,通过逐像素操作或卷积操作等,提取图像的基本特征;机器视觉则是着重关注测量,需要得到的是严格定量的分析。如某个物体的长宽、面积、角度还有距离相机的位姿和距离等,大部分应用场景是工业自动化生产,一般需要用到图像处理的知识;计算机视觉的抽象层次最高,希望计算机拥有和人一样的理解能力,不单单是进行测量,而要抽取出高级的语义特征等。拿一张含有水杯的图片作为这三个任务的输入,图像处理输出的可能是利用canny边缘检测后得到的水杯轮廓,机器视觉算法也许会计算出水杯的真实高度,而计算机视觉程序则能告诉我们,图像中有一个水杯,甚至可以利用目标检测技术将其精确定位出来。
本节就介绍最基本的任务,图像处理。
5.3.3.1. 频域分析
对于学过信号与系统的同学来说,Fourier Transformation已经是我们的老朋友了。既然它能处理一维序列,自然也能处理二维信号,图像就是一个二维信号,我们当然可以用它来对付图像了。我们着重关注的是二维的DFT(离散傅里叶变换),希望傅里叶变换能帮我们提取出图像的频率信息。对于时间序列,频域上的幅值低对应着不同频率正弦波的强度大小,频率越高则说明变化越剧烈;那么我们直接将这种关系推广到二维空间信号,频率高低就对应着明暗过渡的缓和程度,对于物体轮廓和边缘,亮度的突变就代表了高频信号,而平缓的区块则对应了低频分量。
下图中展示了三个基本频率分量(还有一种往右边倾斜的分量),频域和空间域仍然保持着对偶关系,高频分量在频域中间隔大,低频则是集中。对于表示信号非常关键的相位信息,也一起保存在频域中。不过由于要完整的表示出频域分量,需要四个维度(时域中的每个维度都对应一个复数,这是一个
C
2
C^2
C2空间),因此一般将相位谱和频率谱分开表示。
图像的基本频率分量,以及他们在空间域和频域的对应关系
那么,回到最根本的问题,为什么我们需要傅里叶变换?因为我们想要利用变换域的一些优越的性质——比如卷积在频域只是简单的相乘操作。之前我们介绍了Sobel算子和Laplcian算子在频域的实现都非常容易(微分变成了了标量乘法),低通滤波器、高通滤波器、带通滤波器更是手到擒来。线性时不变系统(在这里则是“线性空间不变系统”)的优良性质让我们可以单独处理每个信号分量,再通过可加性结合所有成分。
对一张图片分别应用高通和低通滤波器,图源MIT opencourse 6.819
你可以设计特定的滤波器来提取需要的图像分量,在之前介绍Vision attention的时候,我们就提到一个通道的数据就相当于原图的某个“频率分量“,只不过CNN的卷积核参数是利用反向传播自我学习出来的,而不是用傅里叶变换计算出来的。从这个角度上看,傅里叶变换产生的特征(正弦分量)就要比神经网络训练出的特征更”低级“一些。同样,我们可以取二者之长,比如给予每个正弦分量一个可学习的系数,让网络通过反向传播训练此系数,得到一组组合滤波器,这相对最基本的DFT能更好地提取图像特征,又可以网络降低训练的开销不需要单独学习卷积核上的每一个参数,提高运行速度(DFT的运行速度已经被优化到极致了,打过OI或者玩信号处理的同学一定听说过/掌握了快速傅里叶变换FFT)。
眯起眼睛或取下眼镜,你将获得一个低通滤波器,看到作家的人头画像
5.3.3.2. 空域滤波
在信号与系统、控制理论中我们常常从时域和频域分析对象,那么在图像处理上我们也可以从空间域对图像进行操作。最常见的比如差分算子、二阶差分算子、高斯核等大家应该已经见怪不怪了;对于单一像素的逐个操作如阈值、双阈值、直方图归一化还有对比度增强等,也可以看作是非线性的滤波;还有之前介绍的自适应滤波,同样可以用在图像降噪去雾上。
-
降噪 因为CMOS损坏、增益过高或是在低曝光、低亮度条件下拍摄照片时画面常常伴随噪点或椒盐噪声,最简单高效的方法就是利用均值滤波、中值滤波、高斯滤波平滑/去除这种噪点。如果是每个像素上都伴随随机噪声,根据其特性可以选用定制的自适应滤波(动态改变窗口大小和参数选择)或其他的相关滤波方法处理。不过,我们需要权衡效果和时间开销,如果前处理就花费了大量的时间,对算法的实时性是非常不利的。
上图为随机噪声,下图为高斯噪声,图源MIT opencourse 6.819
-
特征提取 用于梯度计算的差分算子已经是老熟人了,提取边缘可用sobel算子和Laplace算子,比较少见的有提取倾斜边缘的roberts、只处理垂直方向的prewitt算子,上述的这些卷积核都是根据不同情况选用的,当然还有增加预处理和后处理的Canny边缘检测。这里列出他们的形状、适用情况及其优缺点。
- Roberts算子,从核的设计就可以看出,比较适合处理倾斜的边缘,对于本身边缘较为明显的图像,处理效果较好,但是边缘响应较宽而且会受到噪声影响,若要缩小响应宽度可以增加后处理,减小噪声影响则增大卷积核:
[
?
1
0
0
1
]
[
0
?
1
1
0
]
\begin{bmatrix} -1& 0 \\ 0& 1 \\ \end{bmatrix}\begin{bmatrix} 0& -1 \\ 1& 0 \\ \end{bmatrix}
[?10?01?][01??10?]
-
Prewitt算子,专注提取水平和竖直方向的边缘,鲁棒性相比前者更佳,但是提取出的倾斜边缘会呈现锯齿状,对于亮度变化不那么剧烈的图像也比较有效:
[
?
1
0
1
?
1
0
1
?
1
0
1
]
[
?
1
?
1
?
1
0
0
0
1
1
1
]
\begin{bmatrix} -1& 0 &1\\ -1& 0&1 \\ -1& 0 &1 \end{bmatrix} \begin{bmatrix} -1& -1 &-1\\ 0& 0&0 \\ 1& 1 &1 \end{bmatrix}
????1?1?1?000?111????????101??101??101???? -
Sobel算子,相当于使用了高斯插值的边缘微分算子,对于离中心更远的像素具有更小的权重(很直观,比如下方的核中,左上角对卷积核中心点的贡献应该比正上方小)。由于加入了高斯滤波,噪声容限相对prewitt有所提升,若希望得到更平滑的结果,同样可以扩大卷积核:
[
?
1
0
1
?
2
0
2
?
1
0
1
]
[
?
1
?
2
?
1
0
0
0
1
2
1
]
\begin{bmatrix} -1& 0 &1\\ -2& 0&2 \\ -1& 0 &1 \end{bmatrix} \begin{bmatrix} -1& -2 &-1\\ 0& 0&0 \\ 1& 2 &1 \end{bmatrix}
????1?2?1?000?121????????101??202??101???? -
Laplacian算子,对于离散的图像信号来说是二阶差分算子,能够更明显的凸出图像边缘:
[
0
?
1
0
?
1
4
?
1
0
?
1
0
]
\begin{bmatrix} 0& -1 &0\\ -1& 4&-1 \\ 0& -1 &0 \end{bmatrix}
???0?10??14?1?0?10???? 这是四方向的拉普拉斯算子,还有8方向(考虑两个斜45°角)的卷积核如下:
[
?
1
?
1
?
1
?
1
8
?
1
?
1
?
1
?
1
]
\begin{bmatrix} -1& -1 &-1\\ -1& 8&-1 \\ -1& -1 &-1 \end{bmatrix}
????1?1?1??18?1??1?1?1???? 如果想要凸显效果或减弱效果,读者可以自行尝试给边缘和中心设定不同的值,利用OpenCV中的filter2D() 函数进行测试。 -
Canny边缘检测在各处有非常多的教程,OpenCV documentaion中也有示例,这里就简略过一下:
-
高斯滤波,把噪声去除,显然差分运算对冲激噪声是非常敏感的 -
用梯度算子求出边缘响应 -
非极大值抑制,根据把边缘缩窄/删除误检边缘 -
双阈值操作确定响应上下界,把范围内的响应留下 -
聚类?强响应附近的弱响应也会加入,而弱响应附近的弱响应直接删除
5.3.3.3. 形态学操作
形态学操作是对二值图的轮廓和外形进行变换的运算(其实也可以运用于灰度图像,会有插值效果,但应用最多的还是二值图,直接凸显形态特征)。这部分内容是传统识别算法的老朋友了,我们手动设计一些特征并用形态学操作进行增强,或削弱噪点。
-
膨胀 将结构元素应用在某个像素时,把当前像素替换成结构元素覆盖位置上的最大值,对于二值图来说,就是变成255,扩张边缘。 -
腐蚀 和膨胀相反,替换为覆盖位置的最小值,收缩边缘。 -
开运算 先腐蚀再膨胀,消除高亮度的狭长区域(和你的结构元素设计有关)或高亮噪点。 -
闭运算 和开运算相反,消除黑色空洞区域,或将断裂的轮廓闭合。 -
顶帽 原图减去原图的开操作,消除亮度较低区域下的高亮区域。 -
黑帽 和顶帽操作相似,是原图减去闭运算,可以得到高亮部分的较暗区域。 -
形态学梯度 一般指的是膨胀和腐蚀图像的差值,前述两种“帽”操作也属于形态学梯度。 -
统一接口 OpenCV的形态学操作api为morphologyEX() ,提供了上述所有操作的接口。 -
自定义结构元素 OpenCV提供了getStructuringElement() 函数方便我们自定义结构元素,你也可以使用Mat制作更复杂的结构元素。
关于这部分内容,网络上和教材中有大量的介绍,在此不过多赘述。关键是理解形态学运算的原理, 知道算法是怎么运行的即可。
下一章将更新目标跟踪相关的内容
|