初识opencv03——图像预处理2
初识opencv
初识opencv01——基本api操作
初识opencv02——图像预处理1
文章目录
- 初识opencv
- 前言
- 一、插值算法
- 1.1 最近邻插值(CV2.INTER_NEAREST)
- 1.2 双线性插值(CV2.INTER_LINEAR)
- 1.3 像素区域插值(cv2.INTER_AREA)
- 1.4 双三次插值(cv2.INTER_CUBIC)
- 1.5 lanczos插值(cv2.INTER_LANCZOS4)
- 1.6 几种插值方式的对比
- 二、边缘填充
- 2.1 边界复制(BORDER_REPLICATE)
- 2.2 边界反射(BORDER_REFLECT)
- 2.3 边界常数(BORDER_CONSTANT)
- 2.4 边界包裹(BORDER_WRAP)
- 2.5 透视变换
- 三、图像掩膜
- 3.1 制作掩膜
- 3.2 补充
- 四、噪声消除
- 4.1 均值滤波
- 4.2 方框滤波
- 4.3 高斯滤波
- 4.4 中值滤波
- 4.5 双边滤波
- 4.6 各种滤波对比
- 总结
前言
这里先给出目标点与原图像点之间坐标的计算公式:
srcX=dstX∗srcWidthdstWidthsrc X=dstX*{\frac{srcWidth}{dstWidth}}srcX=dstX∗dstWidthsrcWidth
srcY=dstY∗srcHeightdstHeightsrcY=dstY*{\frac{srcHeight}{dstHeight}}srcY=dstY∗dstHeightsrcHeight
其中:
- dstXdstXdstX:目标图像中某点的xxx坐标,
- dstYdstYdstY:目标图像中某点的yyy坐标,
- srcWidthsrcWidthsrcWidth:原图的宽度,
- dstWidthdstWidthdstWidth:目标图像的宽度;
- srcHeightsrcHeightsrcHeight:原图的高度,
- dstHeightdstHeightdstHeight:目标图像的高度。
- 而srcXsrcXsrcX和srcYsrcYsrcY:目标图像中的某点对应的原图中的点的xxx和yyy的坐标。
通俗的讲,该公式就是让目标图像中的每个像素值都能找到对应的原图中的像素值,这样才能根据不同的插值方法来获取新的像素值。根据该公式,我们就可以得到每一个目标点所对应的原图像的点。
一、插值算法
- 为什么需要插值算法?
当我们对图像进行缩放、旋转或几何变换时,新像素点的位置可能不在原图像素坐标上,或者说计算出的新坐标值不为整数。那么,就需要插值算法来为我们指定新图像各个位置的像素值应该如何计算。而插值算法就是通过已知像素值估算这些新位置的值,要求能保证变换后图像的视觉连续性和质量。
cv2.warpAffine(img,M,dsize[,flags])除了必须的参数:
- img:输入图像。
- M:2x3的变换矩阵,类型为np.float32。
- dsize:输出图像的尺寸,形式为(width,height)。
还有可选参数flags,用于指定插值方法(默认为INTER_LINEAR)。
1.1 最近邻插值(CV2.INTER_NEAREST)
# 使用方法
new_img1=cv.warpAffine(img,M,(w,h),flags=cv.INTER_NEAREST)
比如一个2*2的图像放大到4*4,如下图所示,其中红色的为每个像素点的坐标,黑色的则表示该像素点的像素值:

最近邻插值的原则是:目标像素点的像素值与经过该公式计算出来的对应的像素点的像素值相同,如出现小数部分需要进行取整。
故放大后图像(1,0)点对应的原图像的(0.5,0)点,因此需要对计算出来的坐标值进行取整,取整后的结果为(0,0),也就是说放大后的图像中的(1,0)坐标处对应的像素值就是原图中(0,0)坐标处的像素值,其他像素点计算规则与此相同。
1.2 双线性插值(CV2.INTER_LINEAR)
- 线性插值
线性插值是在一维空间中,通过两点之间的直线关系估算中间值的方法。
给定两点x0x_0x0和x1x_1x1及其特征值,求xxx处的特征值f(x)f(x)f(x),通常x0<x<x1x_0<x<x_1x0<x<x1。
则目标点特征有:
f(x)=f(x0)+x−x0x1−x0∗(f(x1)−f(x0))f(x)=f(x_0)+\frac{x-x_0}{x_1-x_0}*(f(x_1)-f(x_0)) f(x)=f(x0)+x1−x0x−x0∗(f(x1)−f(x0))
或等价形式
f(x)=(1−a)f(x0)+a∗f(x1),a=x−x0x1−x0f(x)=(1-a)f(x_0)+a*f(x_1),a=\frac{x-x_0}{x_1-x_0} f(x)=(1−a)f(x0)+a∗f(x1),a=x1−x0x−x0
其中我们称aaa或其比值形式为关于点x1x_1x1的权重。之所以这样认为,是因为当aaa越大,则表明点xxx离x1x_1x1越近。
将一维线性插值扩展到二维空间,当像素落在二维空间中的四个点围成的正方形中间时,应该如何计算其特征值呢?
- 双线性插值
将一维线性插值扩展到二维空间后,只需在水平方向与垂直方向上各计算一次线性插值,即可得新像素值。

以上图为例,只需根据Q11、Q21得到R1的插值,根据Q12、Q22得到R2的插值,然后根据R1、R2得到P的插值即可,这就是双线性插值。(先计算水平方向还是垂直方向不影响最终结果)
首先计算R1和R2的插值:
f(R1)≈x2−xx2−x1f(Q11)+x−x1x2−x1f(Q21)f(R_{1})\approx\frac{x_{2}-x}{x_{2}-x_{1}}f(Q_{11})+\frac{x-x_{1}}{x_{2}-x_{1}}f(Q_{21}) f(R1)≈x2−x1x2−xf(Q11)+x2−x1x−x1f(Q21)
f(R2)≈x2−xx2−x1f(Q12)+x−x1x2−x1f(Q22)f(R_{2})\approx\frac{x_{2}-x}{x_{2}-x_{1}}f(Q_{12})+\frac{x-x_{1}}{x_{2}-x_{1}}f(Q_{22}) f(R2)≈x2−x1x2−xf(Q12)+x2−x1x−x1f(Q22)
然后根据R1和R2计算P的插值:
f(P)≈y2−yy2−y1f(R1)+y−y1y2−y1f(R2)f(P)\approx{\frac{y_{2}-y}{y_{2}-y_{1}}}f(R_{1})+{\frac{y-y_{1}}{y_{2}-y_{1}}}f(R_{2}) f(P)≈y2−y1y2−yf(R1)+y2−y1y−y1f(R2)
但在opencv中双线性插值是默认的缩放算法,我们仅需调用api即可,无需手动实现。以下为示例代码:
import cv2 as cv# 读取图像
img = cv.imread('input.jpg')# 使用双线性插值缩放
resized = cv.resize(img, (new_width, new_height),interpolation=cv2.INTER_LINEAR)# 在仿射变换中应用
M = cv.getRotationMatrix2D((width/2, height/2), 30, 1)
rotated = cv.warpAffine(img, M, (width, height),flags=cv.INTER_LINEAR)
1.3 像素区域插值(cv2.INTER_AREA)
在opencv中,像素区域插值主要分两种情况,其中缩小图像和放大图像的工作原理并不相同。
-
当使用像素区域插值方法进行缩小图像时,它就会变成一个均值滤波器(滤波器其实就是一个核,这里只做简单了解,后面实验中会介绍),其工作原理可以理解为对一个区域内的像素值取平均值。
-
当使用像素区域插值方法进行放大图像时
- 如果图像放大的比例是整数倍,那么其工作原理与最近邻插值类似;
- 如果放大的比例不是整数倍,那么就会调用双线性插值进行放大。
1.4 双三次插值(cv2.INTER_CUBIC)
与双线性插值法相同,该方法也是通过映射,在映射点的邻域内通过加权来得到放大图像中的像素值。不同的是,双三次插值法需要原图像中近邻的16个点来加权,也就是4x4的网格。
以下图为例:
图中的P点就是目标图像B在(X,Y)处根据上述公式计算出的对应于原图像A中的位置,P的坐标位置会出现小数部分,所以我们假设P点的坐标为(x+u,y+v),其中x、y表示整数部分,u、v表示小数部分,那么我们就可以得到其周围的最近的16个像素的位置,我们用a(i,j)(i,j=0,1,2,3)来表示。
然后计算出周围16个像素点与目标点P的相对距离,然后将其带入BiCubic函数:

其中:
a
一般取-0.5或-0.75,用于控制插值函数的形状。d
代表的是目标像素点与某个像素点之间的相对距离,分为dhd_hdh、dwd_wdw,即水平距离与垂直距离。
分别带入dhd_hdh、dwd_wdw,即可得到16个点中每一列的4个权重与每一行的4个权重,将其两两相乘,即可得到16个点各自的权重。
最后加权求和即可:
B(X,Y)=(a00∗wi0∗wj0)+(a01∗wi0∗wj1)+(a02∗wi0∗wj2)+(a03∗wi0∗wj3)+(a10∗wi1∗wj0)+(a11∗wi1∗wj1)+(a12∗wi1∗wj2)+(a13∗wi1∗wj3)+(a20∗wi2∗wj0)+(a21∗wi2∗wj1)+(a22∗wi2∗wj2)+(a23∗wi2∗wj3)+(a30∗wi3∗wj0)+(a31∗wi3∗wj1)+(a22∗wi3∗wj2)+(a33∗wi3∗wj3)B(X,Y) =(a_{00}*w_{i0}*w_{j0})+(a_{01}*w_{i0}*w_{j1})+(a_{02}*w_{i0}*w_{j2})+(a_{03}*w_{i0}*w_{j3}) +(a_{10}*w_{i1}*w_{j0})+(a_{11}*w_{i1}*w_{j1})+(a_{12}*w_{i1}*w_{j2})+(a_{13}*w_{i1}*w_{j3}) +(a_{20}*w_{i2}*w_{j0})+(a_{21}*w_{i2}*w_{j1})+(a_{22}*w_{i2}*w_{j2})+(a_{23}*w_{i2}*w_{j3}) +(a_{30}*w_{i3}*w_{j0})+(a_{31}*w_{i3}*w_{j1})+(a_{22}*w_{i3}*w_{j2})+(a_{33}*w_{i3}*w_{j3}) B(X,Y)=(a00∗wi0∗wj0)+(a01∗wi0∗wj1)+(a02∗wi0∗wj2)+(a03∗wi0∗wj3)+(a10∗wi1∗wj0)+(a11∗wi1∗wj1)+(a12∗wi1∗wj2)+(a13∗wi1∗wj3)+(a20∗wi2∗wj0)+(a21∗wi2∗wj1)+(a22∗wi2∗wj2)+(a23∗wi2∗wj3)+(a30∗wi3∗wj0)+(a31∗wi3∗wj1)+(a22∗wi3∗wj2)+(a33∗wi3∗wj3)
1.5 lanczos插值(cv2.INTER_LANCZOS4)
Lanczos插值方法与双三次插值的思想是一样的,不同的就是其需要的原图像周围的像素点的范围变成了8*8,并且不再使用BiCubic函数来计算权重,而是换了一个公式计算权重。
其使用的公式为:
具体计算过程这里不再赘述。
1.6 几种插值方式的对比
插值算法 | 邻域大小 | 计算复杂度 | 视觉质量 | 主要优势 | 主要劣势 | 典型应用场景 |
---|---|---|---|---|---|---|
最近邻插值 | 1×1 | 极低 | ★☆☆☆☆ | 计算简单、执行速度快 | 锯齿明显、边缘不连续 | 实时预览、像素艺术处理 |
双线性插值 | 2×2 | 低 | ★★★☆☆ | 平滑过渡、计算效率高 | 细节模糊、边缘柔化 | 通用图像缩放、网页图片适配 |
像素区域插值 | 自适应 | 中等 | ★★★★☆ (缩小) | 缩小图像时抗锯齿效果最佳 | 放大图像质量不稳定 | 图像大幅缩小、缩略图生成 |
双三次插值 | 4×4 | 较高 | ★★★★☆ | 边缘锐利、细节保留较好 | 计算量大、可能出现振铃效应 | 高质量放大、印刷图像处理 |
Lanczos插值 | 8×8 | 极高 | ★★★★★ | 细节保留最佳、高频信息损失最小 | 计算耗时长、可能产生过冲现象 | 卫星图像、医学影像、艺术摄影 |
二、边缘填充
为什么要填充边缘呢?我们以下图为例。
原图 | 旋转后的图 |
---|---|
![]() | ![]() |
可以看到,左图在逆时针旋转45度之后原图的四个顶点在右图中已经看不到了,同时,右图的四个顶点区域其实是什么都没有的,因此我们需要对空出来的区域进行一个填充。右图就是对空出来的区域进行了像素值为(0,0,0)的填充,也就是黑色像素值的填充。除此之外,后续的一些图像处理方式也会用到边缘填充,这里介绍五个常用的边缘填充方法。
2.1 边界复制(BORDER_REPLICATE)
边界复制会将边界处的像素值进行复制,然后作为边界填充的像素值,如下图所示,可以看到四周的像素值都一样。
# 使用语法如下
new_img=cv.warpAffine(img,M,(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_REPLICATE)
使用效果:
原图 | 填充后 |
---|---|
![]() | ![]() |
2.2 边界反射(BORDER_REFLECT)
# 使用语法如下
new_img=cv.warpAffine(img,M(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_REFLECT)
或
new_img=cv.warpAffine(img,M(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_REFLECT101)
原图 | 填充后 | 边界反射101 |
---|---|---|
![]() | ![]() | ![]() |
边界反射101与边界反射不同的是,不再反射边缘的像素点,具体区别如下:
边界反射 | 101 |
---|---|
![]() | ![]() |
2.3 边界常数(BORDER_CONSTANT)
当选择边界常数时,还要指定常数值是多少,默认的填充常数值为0。
# 使用语法如下
new_img=cv.warpAffine(img,M(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_CONSTANT,borderValue=(0,0,255))
原图 | 填充后 |
---|---|
![]() | ![]() |
2.4 边界包裹(BORDER_WRAP)
# 使用语法如下
new_img=cv.warpAffine(img,M,(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_WRAP)
原图 | 填充后 |
---|---|
![]() | ![]() |
2.5 透视变换
透视变换是把一个图像投影到一个新的视平面的过程,在现实世界中,我们观察到的物体在视觉上会受到透视效果的影响,即远处的物体看起来会比近处的物体小。透视投影是指将三维空间中的物体投影到二维平面上的过程,这个过程会导致物体在图像中出现形变和透视畸变。透视变换可以通过数学模型来校正这种透视畸变,使得图像中的物体看起来更符合我们的直观感受。通俗的讲,透视变换的作用其实就是改变一下图像里的目标物体的被观察的视角。
原图 | 变换后 |
---|---|
![]() | ![]() |
示例:
import cv2 as cv
import numpy as np# 读取图像文件
img=cv.imread('./imageBase/images/3.png')# 获取图像的高度和宽度
height,width=img.shape[:2]# 定义原始图像中的四个点坐标,用于透视变换
pt1=[[178,100],[487,134],[124,267],[473,308]]# 定义目标图像中的四个点坐标,对应于输出图像的四个角点
pt2=[[0,0],[width,0],[0,height],[width,height]]# 计算透视变换矩阵,将pt1中的点映射到pt2中的点
matrix=cv.getPerspectiveTransform(np.float32(pt1),np.float32(pt2))# 应用透视变换,将原始图像转换为矫正后的图像
# 使用线性插值方法,边界处理方式为常数填充
img_perspective=cv.warpPerspective(img,matrix,(width,height),cv.INTER_LINEAR,borderMode=cv.BORDER_CONSTANT)# 显示原始图像和透视变换后的图像
cv.imshow('img',img)
cv.imshow('img_perspective',img_perspective)# 等待按键事件,然后关闭所有窗口
cv.waitKey(0)
cv.destroyAllWindows()
所用到的api有:
- M=getPerspectiveTransform(src,dst)
在该函数中,需要提供两个参数:- src:原图像上需要进行透视变化的四个点的坐标,这四个点用于定义一个原图中的四边形区域。
- dst:透视变换后,src的四个点在新目标图像的四个新坐标。
该函数会返回一个透视变换矩阵,得到透视变化矩阵之后,使用warpPerspective()函数即可进行透视变化计算,并得到新的图像。
- cv2.warpPerspective(src, M, dsize, flags, borderMode)
- src:输入图像。
- M:透视变换矩阵。这个矩阵可以通过getPerspectiveTransform函数计算得到。
- dsize:输出图像的大小。它可以是一个Size对象,也可以是一个二元组。
- flags:插值方法的标记。
- borderMode:边界填充的模式。
三、图像掩膜
3.1 制作掩膜
掩膜(Mask)是一种在图像处理中常见的操作,它用于选择性地遮挡图像的某些部分,以实现特定任务的目标。掩膜通常是一个二值化图像,并且与原图像的大小相同,其中目标区域被设置为1(或白色),而其他区域被设置为0(或黑色),并且目标区域可以根据HSV的颜色范围进行修改。
示例代码:
import cv2 as cv
import numpy as npimg=cv.imread('./imageBase/images/demo.png')
img=cv.resize(img,(500,500))
# 转为HSV
hsv=cv.cvtColor(img,cv.COLOR_BGR2HSV)
# 定义颜色范围
lower_blue=np.array([35,43,46])
upper_blue=np.array([124,255,255])
# 创建掩膜
mask=cv.inRange(hsv,lower_blue,upper_blue)
res=cv.bitwise_and(img,img,mask=mask)cv.imshow('img',img)
cv.imshow('mask',mask)
cv.imshow('res',res)cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
原图 | 掩膜 | 结果 |
---|---|---|
![]() | ![]() | ![]() |
其中用到的api有:
- mask=cv.inRange(img,color_low,color_high)
用于进行多通道图像(尤其是彩色图像)的阈值操作,img是所需要操作的图像,color_low与color_high确定所需要操作的颜色值范围。 - cv2.bitwise_and(src1,src2[,mask])
其中参数为:
src1
:第一个输入数组。通常是输入的原始图像。src2
:第二个输入数组。它可以是另一个图像、一个常数值或者与src1
相同的图像。
- 当应用掩膜时,这个参数经常就是
src1
本身;即对同一个图像进行操作。- 如果对两个不同的图像执行按位与操作(例如,将两张图片的某些部分组合在一起),可以分别将它们作为
src1
和src2
输入到cv2.bitwise_and()
函数中,创建复杂的图像效果或进行图像合成。mask
:掩膜(可选)。输入数组元素只有在该掩膜非零时才被处理。是一个8位单通道的数组,尺寸必须与src1
和src2
相同。- 返回值:输出数组,应用掩膜后的图像,与输入数组大小和类型相同。
3.2 补充
-
ROI:egion of Interest,翻译过来就是感兴趣的区域。什么意思呢?比如对于一个人的照片,假如我们要检测眼睛,因为眼睛肯定在脸上,所以我们感兴趣的只有脸这部分,其他都不care,所以可以单独把脸截取出来,这样就可以大大节省计算量,提高运行速度。除了切片外,也可通过掩膜操作获取ROI。
-
添加水印
添加水印的概念其实可以理解为将一张图片中的某个物体或者图案提取出来,然后叠加到另一张图片上。具体的操作思想是通过将原始图片转换成灰度图,并进行二值化处理,去除背景部分,得到一个类似掩膜的图像。然后将这个二值化图像与另一张图片中要添加水印的区域进行“与”运算,使得目标物体的形状出现在要添加水印的区域。最后,将得到的目标物体图像与要添加水印的区域进行相加,就完成了添加水印的操作。
四、噪声消除
首先介绍一些概念:
- 噪声:
指图像中的一些干扰因素,通常是由图像采集设备、传输信道等因素造成的,表现为图像中随机的亮度,也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。高斯噪声是一种分布符合正态分布的噪声,会使图像变得模糊或有噪点。椒盐噪声则是一些黑白色的像素值分布在原图像中。
原图 | 高斯噪声 | 椒盐噪声 |
---|---|---|
![]() | ![]() | ![]() |
- 滤波器:
也可以叫做卷积核,与自适应二值化中的核一样,本身是一个小的区域,有着特定的核值,并且工作原理也是在原图上进行滑动并计算中心像素点的像素值。滤波器可分为线性滤波和非线性滤波,线性滤波对邻域中的像素进行线性运算,如在核的范围内进行加权求和,常见的线性滤波器有均值滤波、高斯滤波等。非线性滤波则是利用原始图像与模板之间的一种逻辑关系得到结果,常见的非线性滤波器中有中值滤波器、双边滤波器等。
滤波与模糊联系与区别:
- 它们都属于卷积,不同滤波方法之间只是卷积核不同(对线性滤波而言)
- 低通滤波器是模糊,高通滤波器是锐化
- 低通滤波器就是允许低频信号通过,在图像中边缘和噪点都相当于高频部分,所以低通滤波器用于去除噪点、平滑和模糊图像。高通滤波器则反之,用来增强图像边缘,进行锐化处理。
注意:椒盐噪声可以理解为斑点,随机出现在图像中的黑点或白点;高斯噪声可以理解为拍摄图片时由于光照等原因造成的噪声。
4.1 均值滤波
均值滤波是一种最简单的滤波处理,它取的是卷积核区域内元素的均值,如3×3的卷积核:
kernel=19[111111111]k e r n e l={\frac{1}{9}}{\Bigg[}\begin{array}{l l l}{1}&{1}&{1}\\{1}&{1}&{1}\\{1}&{1}&{1}\end{array}{\Bigg]} kernel=91[111111111]
示例代码:
import cv2 as cv
import numpy as nplvbo2=cv.imread('./imageBase/images/lvbo2.png')
lvbo2=cv.resize(lvbo2,(320,320))
# 均值滤波
dst1=cv.blur(lvbo2,5)cv.imshow('lvbo2',lvbo2)
cv.imshow('dst1',dst1)cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
原图 | 均值滤波 |
---|---|
![]() | ![]() |
4.2 方框滤波
方框滤波跟均值滤波很像,如3×3的滤波核如下:
kernel=a[111111111]k e r n e l={a}{\Bigg[}\begin{array}{l l l}{1}&{1}&{1}\\{1}&{1}&{1}\\{1}&{1}&{1}\end{array}{\Bigg]} kernel=a[111111111]
示例代码:
import cv2 as cv
import numpy as nplvbo2=cv.imread('./imageBase/images/lvbo2.png')
lvbo2=cv.resize(lvbo2,(320,320))
# 方框滤波
dst2=cv.boxFilter(lvbo2,-1,5)cv.imshow('lvbo2',lvbo2)
cv.imshow('dst2',dst2)cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
原图 | 方框滤波 |
---|---|
![]() | ![]() |
4.3 高斯滤波
高斯滤波是一种常用的图像处理技术,主要用于平滑图像、去除噪声。它通过使用高斯函数(正态分布)作为卷积核来对图像进行模糊处理。以3*3的卷积核为例,其核值如下所示:
kernel=[0.06250.1250.06250.1250.250.1250.06250.1250.0625]=[1161811618141811618116]\ k e r n e l=\left[\begin{array}{c}{{0.0625~~~~0.125~~~~0.0625}}\\{{0.125~~~~0.25~~~~0.125}}\\{{0.0625~~~~0.125~~~~0.0625}} \end{array}\right]=\left[\begin{array}{c c c}{\frac{1}{16}~~~\frac{1}{8}~~~\frac{1}{16}}\\{\frac{1}{8}~~~\frac{1}{4}~~~\frac{1}{8}}\\{\frac{1}{16}~~~\frac{1}{8}~~~\frac{1}{16}}\end{array}\right] kernel=0.0625 0.125 0.06250.125 0.25 0.1250.0625 0.125 0.0625=161 81 16181 41 81161 81 161
示例代码:
import cv2 as cv
import numpy as nplvbo2=cv.imread('./imageBase/images/lvbo2.png')
lvbo2=cv.resize(lvbo2,(320,320))
# 高斯滤波
dst3=cv.GaussianBlur(lvbo2,5,1)cv.imshow('lvbo2',lvbo2)
cv.imshow('dst3',dst3)cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
原图 | 高斯滤波 |
---|---|
![]() | ![]() |
4.4 中值滤波
中值又叫中位数,是所有数排序后取中间的值。中值滤波没有核值,而是在原图中从左上角开始,将卷积核区域内的像素值进行排序,并选取中值作为卷积核的中点的像素值。
示例代码:
import cv2 as cv
import numpy as nplvbo2=cv.imread('./imageBase/images/lvbo2.png')
lvbo2=cv.resize(lvbo2,(320,320))
# 中值滤波
dst4=cv.medianBlur(lvbo2,5)cv.imshow('lvbo2',lvbo2)
cv.imshow('dst4',dst4)cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
原图 | 中值滤波 |
---|---|
![]() | ![]() |
4.5 双边滤波
模糊操作基本都会损失掉图像细节信息,尤其前面介绍的线性滤波器,图像的边缘信息很难保留下来。然而,边缘(edge)信息是图像中很重要的一个特征,所以这才有了双边滤波。
双边滤波的基本思路是同时考虑将要被滤波的像素点的空域信息(周围像素点的位置的权重)和值域信息(周围像素点的像素值的权重)。双边滤波采用了两个高斯滤波的结合,一个负责计算空间邻近度的权值(也就是空域信息),也就是上面的高斯滤波器,另一个负责计算像素值相似度的权值(也就是值域信息),也是一个高斯滤波器。
示例代码:
import cv2 as cv
import numpy as nplvbo2=cv.imread('./imageBase/images/lvbo2.png')
lvbo2=cv.resize(lvbo2,(320,320))
# 双边滤波
dst5=cv.bilateralFilter(lvbo2,9,75,75)cv.imshow('lvbo2',lvbo2)
cv.imshow('dst5',dst5)cv.waitKey(0)
cv.destroyAllWindows()
运行结果:
原图 | 双边滤波 |
---|---|
![]() | ![]() |
4.6 各种滤波对比
特性 | 均值滤波 | 方框滤波 | 高斯滤波 | 中值滤波 | 双边滤波 |
---|---|---|---|---|---|
滤波类型 | 线性 | 线性 | 线性 | 非线性 | 非线性 |
计算原理 | 邻域平均值 | 邻域求和/平均值 | 高斯加权平均 | 邻域中位数 | 空间+值域双重加权 |
边缘保持 | 差(严重模糊) | 差(严重模糊) | 中等(轻微模糊) | 良好(锐利边缘) | 优秀(清晰边缘) |
噪声去除能力 | 高斯噪声:★★★☆ | 高斯噪声:★★★☆ | 高斯噪声:★★★★ | 椒盐噪声:★★★★★ | 混合噪声:★★★★☆ |
椒盐噪声:★☆☆☆ | 椒盐噪声:★☆☆☆ | 椒盐噪声:★★☆☆ | 高斯噪声:★★★☆ | ||
计算复杂度 | 极低 | 极低 | 中等 | 中等 | 高 |
主要参数 | 核大小 | 核大小、归一化 | 核大小、σ值 | 核大小 | 核大小、颜色σ、空间σ |
OpenCV函数 | cv2.blur() | cv2.boxFilter() | cv2.GaussianBlur() | cv2.medianBlur() | cv2.bilateralFilter() |
总结
本文章简要介绍了OpenCV图像预处理的四大核心技术:插值算法、边缘填充、图像掩膜和噪声消除。