Java应用OpenCV指南其五:图象滤波

来源:互联网 发布:c语言入门txt免费下载 编辑:程序博客网 时间:2024/05/02 02:19

  在这一章中我们主要讨论了图象滤波的相关知识。
  图象滤波总体来说就是对目标图象进行降噪处理,是图象处理中不可缺少的一个环节,滤波处理效果的好坏直接影响后续图象处理和分析的效果和效率。
  图象滤波分为两种:线性滤波和非线性滤波;
  线性滤波器的原始数据与滤波结果是一种算术运算,即用加减乘除等运算实现,如均值滤波器(模板内像素灰度值的平均值)、高斯滤波器(高斯加权平均值)等。由于线性滤波器是算术运算,有固定的模板,因此滤波器的转移函数是可以确定并且是唯一的(转移函数即模板的傅里叶变换)。
  非线性滤波器的原始数据与滤波结果是一种逻辑关系,即用逻辑运算实现,如最大值滤波器、最小值滤波器、中值滤波器等,是通过比较一定邻域内的灰度值大小来实现的,没有固定的模板,因而也就没有特定的转移函数(因为没有模板作傅里叶变换),另外,膨胀和腐蚀也是通过最大值、最小值滤波器实现的。

图像滤波与滤波器
  首先我们看一下图像滤波的概念。图像滤波,即在尽量保留图像细节特征的条件下对目标图像的噪声进行抑制,是图像预处理中不可缺少的操作,其处理效果的好坏将直接影响到后续图像处理和分析的有效性和可靠性。
  消除图像中的噪声成分叫作图像的平滑化或滤波操作。信号或图像的能量大部分集中在幅度谱的低频和中频段是很常见的,而在较高频段,感兴趣的信息经常被噪声淹没。因此一个能降低高频成分幅度的滤波器就能够减弱噪声的影响。
  图像滤波的目的有两个:一是抽出对象的特征作为图像识别的特征模式;另一个是为适应图像处理的要求,消除图像数字化时所混入的噪声。而对滤波处理的要求也有两条:一是不能损坏图像的轮廓及边缘等重要信息;二是使图像清晰视觉效果好。
  平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。
  空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
  关于滤波器,一种形象的比喻法是:我们可以把滤波器想象成一个包含加权系数的窗口,当使用这个滤波器平滑处理图像时,就把这个窗口放到图像之上,透过这个窗口来看我们得到的图像。
  滤波器的种类有很多, 在新版本的OpenCV中,提供了如下五种常用的图像平滑处理操作方法,且他们分别被封装在单独的函数中,使用起来非常方便:

• 方框滤波——boxblur函数
• 均值滤波(邻域平均滤波)——blur函数
• 高斯滤波——GaussianBlur函数
• 中值滤波——medianBlur函数
• 双边滤波——bilateralFilter函数

1、线性滤波:
  方框滤波、中值滤波和高斯滤波是常见的线性滤波方法。

方框滤波:

  方框滤波(box Filter)被封装在一个名为boxblur的函数中,即boxblur函数的作用是使用方框滤波器(box filter)来模糊一张图片,从src输入,从dst输出。
  函数原型如下:

Imgproc.boxFilter(src, dst, ddepth, ksize, anchor, normalize, borderType);

• 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
• 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
• 第三个参数,int类型的ddepth,输出图像的深度,-1代表使用原图深度,即src.depth()。
• 第四个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
• 第五个参数,Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
• 第六个参数,bool类型的normalize,默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了。
• 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。

方框滤波原理:
方框滤波所用的核如下:
这里写图片描述

  当normaize为true时,方框滤波也就成了均值滤波。也就是说均值滤波是方框滤波归一化后的特殊情况,比如(0,1),以便统一处理和直观量化。
  而非归一化(Unnormalized)的方框滤波用于计算每个像素邻域内的积分特性,比如密集光流算法(dense optical flow algorithms)中用到的图像倒数的协方差矩阵(covariance matrices of image derivatives)
  如果我们要在可变的窗口中计算像素总和,可以使用integral()函数。

下面是一个简单的实例:

package opencv;import org.opencv.core.*;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;public class one {    static{System.loadLibrary(Core.NATIVE_LIBRARY_NAME);};  //用来调用OpenCV库文件,必须添加    public static void main(String args[]){        Mat src = Imgcodecs.imread("F:/workspace/opencv/Filter.jpg", Imgcodecs.IMREAD_UNCHANGED);        Mat dst = new Mat();        Imgproc.boxFilter(src, dst, -1, new Size(3,3));        Imgcodecs.imwrite("F:/workspace/opencv/Filter_1.jpg", dst);    }}

处理对比图:
原图
这里写图片描述
方框滤波
这里写图片描述

均值滤波:

  均值滤波,是最简单的一种滤波操作,输出图像的每一个像素是核窗口内输入图像对应像素的像素的平均值( 所有像素加权系数相等),其实它就是归一化后的方框滤波。
  均值滤波是典型的线性滤波算法,主要方法为邻域平均法,即用一片图像区域的各个像素的均值来代替原图像中的各个像素值。一般需要在图像上对目标像素给出一个模板(内核),该模板包括了其周围的临近像素(比如以目标像素为中心的周围8(3x3-1)个像素,构成一个滤波模板,即去掉目标像素本身)。再用模板中的全体像素的平均值来代替原来像素值。即对待处理的当前像素点(x,y),选择一个模板,该模板由其近邻的若干像素组成,求模板中所有像素的均值,再把该均值赋予当前像素点(x,y),作为处理后图像在该点上的灰度个g(x,y),即个g(x,y)=1/m ∑f(x,y) ,其中m为该模板中包含当前像素在内的像素总个数。

  函数原型如下:
Imgproc.blur(src, dst_blur, ksize, anchor, borderType);

• 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
• 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
• 第三个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小
• 第四个参数,Point类型的anchor,表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
• 第五个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。

下面是一个例子:

package opencv;import org.opencv.core.*;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;public class one {    static{System.loadLibrary(Core.NATIVE_LIBRARY_NAME);};  //用来调用OpenCV库文件,必须添加    public static void main(String args[]){        Mat src = Imgcodecs.imread("F:/workspace/opencv/Filter.jpg", Imgcodecs.IMREAD_UNCHANGED);        Mat dst_blur = new Mat();        Imgproc.boxFilter(src, dst, -1, new Size(5,5));        Imgcodecs.imwrite("F:/workspace/opencv/Filter_2.jpg", dst_blur);    }}

结果:
原图
这里写图片描述
均值滤波
这里写图片描述

高斯滤波:

  高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。
  高斯滤波可以得到信噪比SNR较高的图像(反应真实信号)。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。
  高斯模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同。高斯平滑也用于计算机视觉算法中的预先处理阶段,以增强图像在不同比例大小下的图像效果(参见尺度空间表示以及尺度空间实现)。从数学的角度来看,图像的高斯模糊过程就是图像与正态分布做卷积。卷积是一个单纯的定义,本身没有什么意义可言,但是其在各个领域的应用是十分广泛的,在滤波中可以理解为一个加权平均过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到,而如何加权则是依据核函数高斯函数。
  图像与圆形方框模糊做卷积将会生成更加精确的焦外成像效果。由于高斯函数的傅立叶变换是另外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。

  函数原型如下:

Imgproc.GaussianBlur(src, dst_GaussianBlur, ksize, sigmaX, sigmaY, borderType);

• 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
• 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
• 第三个参数,Size类型的ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。
• 第四个参数,double类型的sigmaX,表示高斯核函数在X方向的的标准偏差。
• 第五个参数,double类型的sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。
• 为了结果的正确性着想,最好是把第三个参数Size,第四个参数sigmaX和第五个参数sigmaY全部指定到。
• 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT。

  在图像处理中,高斯滤波一般有两种实现方式,一是用离散化窗口滑窗卷积,另一种通过傅里叶变换。最常见的就是第一种滑窗实现,只有当离散化的窗口非常大,用滑窗计算量非常大(即使用可分离滤波器的实现)的情况下,可能会考虑基于傅里叶变化的实现方法。
  由于高斯函数可以写成可分离的形式,因此可以采用可分离滤波器实现来加速。所谓的可分离滤波器,就是可以把多维的卷积化成多个一维卷积。具体到二维的高斯滤波,就是指先对行做一维卷积,再对列做一维卷积。这样就可以将计算复杂度从O(M*M*N*N)降到O(2*M*M*N),M,N分别是图像和滤波器的窗口大小。
  高斯模糊是一个非常典型的图像卷积例子,本质上,高斯模糊就是将(灰度)图像 I 和一个高斯核进行卷积操作:
这里写图片描述

  其中 * 表示卷积操作; Gσ 是标准差为σ 的二维高斯核,定义为:
这里写图片描述

  一维高斯函数:
这里写图片描述

  中心点就是原点,μ等于0:
这里写图片描述

  对于图像处理来说,由于图像是二维的,常用二维零均值离散高斯函数作平滑滤波器。

  二维高斯函数(中心为原点):
这里写图片描述

  计算平均值的时候,我们只需要将“中心点”作为原点,其他点按照其在正态曲线上的位置,分配权重,就可以得到一个加权平均值,而这就是上述的与二维高斯核进行卷积的过程。

这是一个简单的例子:

package opencv;import org.opencv.core.*;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;public class one {    static{System.loadLibrary(Core.NATIVE_LIBRARY_NAME);};  //用来调用OpenCV库文件,必须添加    public static void main(String args[]){        Mat src = Imgcodecs.imread("F:/workspace/opencv/Filter.jpg", Imgcodecs.IMREAD_UNCHANGED);        Mat dst_GaussianBlur = new Mat();        Imgproc.GaussianBlur(src, dst_GaussianBlur, new Size(5, 5), 0);        Imgcodecs.imwrite("F:/workspace/opencv/Filter_3.jpg", dst_GaussianBlur);        }}

结果对比:
原图
这里写图片描述
高斯滤波
这里写图片描述

非线性滤波:

  线性滤波可以实现很多种不同的图像变换。然而非线性滤波,如中值滤波器和双边滤波器,有时可以达到更好的实现效果。
  非线性滤波也常用在美图软件中作为嫩肤磨皮功能的实现算法(美图秀秀!)

中值滤波:

  中值滤波(Median filter)是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值,该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像边缘细节。
  中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,其基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近的真实值,从而消除孤立的噪声点,对于斑点噪声(speckle noise)和椒盐噪声(salt-and-pepper noise)来说尤其有用,因为它不依赖于邻域内那些与典型值差别很大的值。中值滤波器在处理连续图像窗函数时与线性滤波器的工作方式类似,但滤波过程却不再是加权运算。
  中值滤波在一定的条件下可以克服常见线性滤波器如最小均方滤波、方框滤波器、均值滤波等带来的图像细节模糊,而且对滤除脉冲干扰及图像扫描噪声非常有效,也常用于保护边缘信息, 保存边缘的特性使它在不希望出现边缘模糊的场合也很有用,是非常经典的平滑噪声处理方法。
  顾名思义,中值滤波选择每个像素的邻域像素中的中值作为输出,或者说中值滤波将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值。
  例如,取3 x 3的函数窗,计算以点[i,j]为中心的函数窗像素中值步骤如下:
  (1) 按强度值大小排列像素点.
  (2) 选择排序像素集的中间值作为点[i,j]的新值.

  函数原型如下:

Imgproc.medianBlur(src, dst_GaussianBlur, ksize);

• 第一个参数,InputArray类型的src,函数的输入参数,填1、3或者4通道的Mat类型的图像;当ksize为3或者5的时候,图像深度需为CV_8U,CV_16U,或CV_32F其中之一,而对于较大孔径尺寸的图片,它只能是CV_8U。
• 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。我们可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
• 第三个参数,int类型的ksize,孔径的线性尺寸(aperture linear size),注意这个参数必须是大于1的奇数,比如:3,5,7,9 …

实例:

package opencv;import org.opencv.core.*;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;public class one {    static{System.loadLibrary(Core.NATIVE_LIBRARY_NAME);};  //用来调用OpenCV库文件,必须添加    public static void main(String args[]){        Mat src_1 = Imgcodecs.imread("F:/workspace/opencv/nonlinerFilter.jpg", Imgcodecs.IMREAD_UNCHANGED);        Mat dst_medianBlur = new Mat();        Imgproc.medianBlur(src_1, dst_medianBlur, 5);        Imgcodecs.imwrite("F:/workspace/opencv/nonlinerFilter_1.jpg", dst_medianBlur);    }}

效果对比:
原图
这里写图片描述
中值滤波
这里写图片描述

双边滤波:

  双边滤波(Bilateral filter)是一种可以保边去噪的滤波器。之所以可以达到此去噪效果,是因为滤波器是由两个函数构成。一个函数是由几何空间距离决定滤波器系数。另一个由像素差值决定滤波器系数。
  双边滤波的数学实现过程稍微有些复杂,这里只是做一个简单说明。
  双边滤波器是针对高斯平滑的提升版本,高斯平滑根据像素邻域的距离决定权重,生成权重的函数为高斯函数,所以叫高斯平滑或者高斯滤波,效果是使图像模糊,并一定程度上的保存边缘,双边滤波的改进是增加了灰度值的影响,也就是邻域的像素灰度值如果和中心像素的灰度值越接近,那么权值在高斯权值的基础上在加上一个相对较大的权值,相反,如果灰度差很大,将会给已生成的高斯模板对应的位置加上一个小的权值,以此类推,并将模板系数归一化(和为1,其目的是完全平滑的图像结果不变),因此模板的系数不再单纯的依赖位置关系,更依赖于灰度关系,因此边缘将能够被有效的保存。

函数原型:

Imgproc.bilateralFilter(src_1, dst_medianBlur, d, sigmaColor, sigmaSpace, borderType);

• 第一个参数,InputArray类型的src,输入图像,即源图像,需要为8位或者浮点型单通道、三通道的图像。
• 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
• 第三个参数,int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个值我们设其为非正数,那么OpenCV会从第五个参数sigmaSpace来计算出它来。
• 第四个参数,double类型的sigmaColor,颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有更宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
• 第五个参数,double类型的sigmaSpace坐标空间中滤波器的sigma值,坐标空间的标注方差。他的数值越大,意味着越远的像素会相互影响,从而使更大的区域足够相似的颜色获取相同的颜色。当d>0,d指定了邻域大小且与sigmaSpace无关。否则,d正比于sigmaSpace。
• 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。

例子:

package opencv;import org.opencv.core.*;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;public class one {    static{System.loadLibrary(Core.NATIVE_LIBRARY_NAME);};  //用来调用OpenCV库文件,必须添加    public static void main(String args[]){        Mat src_1 = Imgcodecs.imread("F:/workspace/opencv/nonlinerFilter.jpg", Imgcodecs.IMREAD_UNCHANGED);        Mat dst_bilateralFilter = new Mat();        Imgproc.bilateralFilter(src_1, dst_bilateralFilter, 16, 32, 8);        Imgcodecs.imwrite("F:/workspace/opencv/nonlinerFilter_2.jpg", dst_bilateralFilter);    }}

效果:
值调的不高,不是很明显,注意耳环头发和水杯
原图
这里写图片描述
双边滤波
这里写图片描述

原创粉丝点击