OpenCV2马拉松第5圈——线性滤波

收入囊中

这里的很多内容其实在我的Computer Vision: Algorithms and ApplicationsのImage processing中都有讲过
  • 相关和卷积工作原理
  • 边界处理
  • 滤波器的工作原理
  • 会使用均值滤波,高斯滤波
  • 使用自己创造的核函数进行双线性滤波
  • 可分离的滤波(加速)

葵花宝典

相关:  g = f h

卷积:  g = f
OpenCV2马拉松第5圈——线性滤波_第1张图片
暂时不考虑边缘,所以8*8的图形进行相关或卷积操作后就得到6*6的图形
因为我们的h(有时叫做核函数)是中心对称的,所以相关和卷积得到的结果是一样的
那不一样呢?看下面的例子,用个一维的例子,{x,y}是核函数,{a,b,c,d,e}是数据
这里构造核   与数据列表的卷积.
In[1]:=
Click for copyable input
Out[1]=
这里构造相关.
In[2]:=
Click for copyable input
Out[2]=
Padding(border effects)
之前提到过,8*8的图像用3*3的核处理会成6*6,那么边界要怎么处理呢?
OpenCV2马拉松第5圈——线性滤波_第2张图片
  • 0填充,很简单的处理方式
  • 常数填充
  • 夹取填塞(clamp),不断地复制边缘像素的值
  • 重叠填塞(wrap),以环状形态环绕图像进行循环
  • 镜像填塞(mirror),像素环绕图像边界进行镜像反射
  • 延长(extend),通过在边缘像素值中减去镜像信号的方式延长信号
下面是来自OpenCV的例子,边界处理没有CVAA上面那么丰富
/*
 Various border types, image boundaries are denoted with '|'

 * BORDER_REPLICATE:     aaaaaa|abcdefgh|hhhhhhh
 * BORDER_REFLECT:       fedcba|abcdefgh|hgfedcb
 * BORDER_REFLECT_101:   gfedcb|abcdefgh|gfedcba
 * BORDER_WRAP:          cdefgh|abcdefgh|abcdefg
 * BORDER_CONSTANT:      iiiiii|abcdefgh|iiiiiii  with some specified 'i'
 */


滤波器
今天讲的高斯低通滤波器,均值滤波器,双线性滤波器都是起到模糊的作用
低通滤波器抑制了图像的高频部分,使得低频分量畅通
今天是4月28日,在Coursera上有数字信号处理这门课开课,还有在网易公开课有斯坦福的傅立叶变换
如果你想非常深入了解,可以去学一下这两门课
滤波函数有时候又叫核函数,也可以叫算子
OpenCV2马拉松第5圈——线性滤波_第3张图片

初识API

均值滤波
C++: void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
 
  • src – 原始图像
  • dst – 输出图像
  • ksize – 核函数大小
  • anchor – 锚点,一般情况下默认为(-1,-1),意味着在中心进行卷积
  • borderType – 边界类型

The function smoothes an image using the kernel:


高斯滤波,对去除正态分布的噪声很有用

C++: void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )
 
  • src – 输入图像
  • dst – 输出图像
  • ksize – 核大小
  • sigmaX – 控制幅度的参数(大家应该都学过或看过高斯函数吧,比如在正态分布中),如果sigmaX,sigmaY都为0,则由核的高度宽度自己计算
  • sigmaY – 二维高斯函数有两个方向可以控制幅度,或这个不设置则和X一样
  • borderType – 边界类型


用自己的核函数进行滤波

C++: void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, intborderType=BORDER_DEFAULT )
 
  • src – 输入图像.
  • dst – 输出图像.
  • depth – ddepth=-1,输出图像具有和输入图像一样的depth
  • kernel – 核函数,单通道浮点矩阵
  • anchor – 同之前
  • delta – 可选,直接加到输出图像
  • borderType – 边界类型

荷枪实弹

使用均值滤波
OpenCV2马拉松第5圈——线性滤波_第4张图片
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;

Mat src,dst;
int i = 1;
static void change_dst(int, void*)
{
	if(i%2 == 0)i++;
	blur( src, dst, Size( i, i ), Point(-1,-1));
    imshow("dstImage", dst);
}

int main( int, char** argv )
{
   	src = imread( argv[1] );
   	namedWindow("srcImage", 1);
   	namedWindow("dstImage", 1);

	createTrackbar( "mean filter:", "dstImage", &i, 20, change_dst);
	change_dst(0, 0);
	
	imshow("srcImage", src);
	waitKey();
	return 0;
}


使用高斯滤波
OpenCV2马拉松第5圈——线性滤波_第5张图片
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;

Mat src,dst;
int i = 1;
static void change_dst(int, void*)
{
	if(i%2 == 0)i++;
	GaussianBlur( src, dst, Size( i, i ), 0, 0 );
    imshow("dstImage", dst);
}

int main( int, char** argv )
{
   	src = imread( argv[1] );
   	namedWindow("srcImage", 1);
   	namedWindow("dstImage", 1);

	createTrackbar( "gauss filter:", "dstImage", &i, 20, change_dst);
	change_dst(0, 0);
	
	imshow("srcImage", src);
	waitKey();
	return 0;
}

使用自定义线性滤波
OpenCV2马拉松第5圈——线性滤波_第6张图片
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;

Mat src,dst,Kernel;

int main( int, char** argv )
{
   	src = imread( argv[1] );
   	namedWindow("srcImage", 1);
   	namedWindow("dstImage", 1);

	Kernel = (Mat_<double>(3,3) << 1, 2, 1, 2, 4, 2, 1, 2, 1)/16;

    filter2D(src, dst, -1 , Kernel, Point(-1,-1));
    imshow("dstImage", dst);
	
	imshow("srcImage", src);
	waitKey();
	return 0;
}

举一反三

可分离的滤波
二维高斯函数卷积可以分两步来进行,首先将图像与一维高斯函数进行卷积,然后将卷积结果与方向垂直的相同一维高斯函数卷积.因此,二维高斯滤波的计算量随滤波模板宽度成线性增长而不是成平方增长。

二维卷积运算,更新一个像素点肯定需要 K 次运算(K是核函数的大小)
文中提出了一种加速的方法,先用一维行向量进行卷积,再用一维列向量进行卷积,如果一个卷积核可以采用这种方法计算,就是可分离的。(这样子就只有2K次操作,很神奇吧)

=vhT  

将卷积核K拆分成列向量v和行向量h
当然,并不是所有K都能被拆分,我在上面的图片中的3个例子都是可以拆分的,一维向量已经列在二维下面

最简单的平均滤波,[1,1,1......,1]* [1,1,1......,1] T   = K
再看第3个高斯核,[1,4,6,4,1]* [1,4,6,4,1] T  = K
那么如何判断核函数是不是可分离的呢?CVAA说用奇异值分解的办法
我的想法是,必须要满足中心对称,比如高斯函数,sigmax和sigmay相等的时候就可以分解

OpenCV帮我们实现了
C++:  void  sepFilter2D (InputArray  src, OutputArray  dst, int  ddepth, InputArray  kernelX, InputArray  kernelY, Point  anchor=Point(-1,-1), double delta=0, int  borderType=BORDER_DEFAULT  )
 
  • src – Source image.
  • dst – Destination image of the same size and the same number of channels as src .
  • ddepth –
    Destination image depth. The following combination of  src.depth() and  ddepth are supported:
    • src.depth() = CV_8Uddepth = -1/CV_16S/CV_32F/CV_64F
    • src.depth() = CV_16U/CV_16Sddepth = -1/CV_32F/CV_64F
    • src.depth() = CV_32Fddepth = -1/CV_32F/CV_64F
    • src.depth() = CV_64Fddepth = -1/CV_64F

    when ddepth=-1, the destination image will have the same depth as the source.

  • kernelX – Coefficients for filtering each row.
  • kernelY – Coefficients for filtering each column.
  • anchor – Anchor position within the kernel. The default value  means that the anchor is at the kernel center.
  • delta – Value added to the filtered results before storing them.
  • borderType – Pixel extrapolation method. See borderInterpolate() for details.
参数基本没什么好讲,都是一样的,下面我们用这个函数来进行高斯卷积(滤波)
因为效果和之前高斯滤波是一样的,就不贴图了,但是要注意虽然效果一样,但是速度可是大大提升!!
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;

Mat src,dst,kernelX,kernelY;

int main( int, char** argv )
{
   	src = imread( argv[1] );
   	namedWindow("srcImage", 1);
   	namedWindow("dstImage", 1);

	kernelX = (Mat_<double>(1,5) << 1,4,6,4,1)/16;
	kernelY = (Mat_<double>(1,5) << 1,4,6,4,1)/16;

    sepFilter2D(src, dst, -1, kernelX, kernelY);
    imshow("dstImage", dst);
	
	imshow("srcImage", src);
	waitKey();
	return 0;
}


计算机视觉讨论群162501053
转载请注明:http://blog.csdn.net/abcd1992719g

你可能感兴趣的:(可分离滤波,线性滤波,opencv2)