opencv学习笔记(二十)cvFilter2D()卷积以及卷积边界的处理

时间:2022-04-11 12:14:11

20.1**cvFilter2D()卷积:**

void cvFilter2D(
const CvArr* src,
CvArr* dst,
const CvMat* kernel,
CvPoint anchor=cvPoint(-1,-1)
);
src
输入图像

dst
输出图像

kernel
卷积核, 单通道浮点矩阵。 如果想要应用不同的核于不同的通道,先用 cvSplit 函数分解图像到单个色彩通道上,然后单独处理。

anchor
核的锚点表示一个被滤波的点在核内的位置。 锚点应该处于核内部。默认值 (-1,-1) 表示锚点在核中心。

这里我们创建一个适当大小的矩阵,将系数连同源图像和目标图像一起传递给cvFilter2D()。我们还可以有选择地输人一个CvPoint指出核的中心位置,默认值(cvPoint (-1, -1))会设参考点为核的中心。如果定义了参考点,核的大小可以是偶数,否则只能是奇数。
源图像src和目标图像dst大小应该相同,有些人可能认为考虑到卷积核的额外的长和宽,源图像src应该大于目标图像dst。但是在OpenCV里源图像src和目标图像dst的大小是可以一样的,因为在默认情况下,在卷积之前,OpenCV通过复制源图像src的边界创建了虚拟像素,这样以便于目标图像dst边界的像素可以被填充。
这里我们所讨论的卷积核的系数应该是浮点类型的,这就意味着我们必须用CV_32F来初始化矩阵。

20.2卷积模板及程序实例:

下面这篇文章详细介绍了卷积的应用:
http://blog.sina.com.cn/s/blog_6ac784290101e47s.html
常用卷积模板
opencv学习笔记(二十)cvFilter2D()卷积以及卷积边界的处理

20.3OpenCV图像卷积(图像滤波)的程序代码:

#include "cv.h"
#include "highgui.h"
int main(int argc,char**argv)
{
IplImage* src, *dst;
float low[9] ={ 1.0/16, 2.0/16, 1.0/16, 2.0/16, 4.0/16, 2.0/16, 1.0/16, 2.0/16, 1.0/16 };//低通滤波核
float high[9]={-1,-1,-1,-1,9,-1,-1,-1,-1};
//高通滤波核——这个模板自己设,这里用的是常用的核。
CvMat km = cvMat( 3, 3, CV_32FC1, low);
//构造单通道浮点矩阵,将图像IplImage结构转换为图像数组
//上面这个矩阵就是在cvFilter2D里面要用到的核
src = cvLoadImage( "b.jpg" );
dst = cvCreateImage( cvGetSize(src), IPL_DEPTH_8U, 3 );
cvFilter2D( src, dst, &km, cvPoint( -1, -1 ) );
//设参考点为核的中心
cvNamedWindow( "src", CV_WINDOW_AUTOSIZE );
cvNamedWindow( "filtering", CV_WINDOW_AUTOSIZE );
cvShowImage( "src", src );
cvShowImage( "filtering", dst );
cvWaitKey(0);
cvReleaseImage( &src );
cvReleaseImage( &dst );
return 0;
}

20.4低通滤波和高通滤波:

低通滤波:边缘平滑
高通滤波:边缘提取与增强

滤波是信号处理机图像处理中的一个基本操作。滤波去除图像中的噪声,提取感兴趣的特征,允许图像重采样。
图像中的频域和空域:空间域指用图像的灰度值来描述一幅图像;而频域指用图像灰度值的变化来描述一幅图像。而低通滤波器和高通滤波器的概念就是在频域中产生的。
低通滤波器指去除图像中的高频成分,而高通滤波器指去除图像中的低频成分。

20.5卷积边界

对于卷积,一个很自然的问题是如何处理卷积边界。例如,在使用刚才所讨论的卷积核时,当卷积点在图像边界时会发生什么?许多使用cvFilter2D()的OpenCV内置函数必须用各种方式来解决这个问题。同样在做卷积时,有必要知道如何有效解决这个问题。
这个解决方法就是使用cvCopyMakeBorder()函数,它可以将特定的图像轻微变大,然后以各种方式自动填充图像边界。
复制图像并且制作边界。

20.5.1cvCopyMakeBorder()
定义:
void cvCopyMakeBorder(
const CvArr* src,
CvArr* dst,
CvPoint offset,
int bordertype,
CvScalar value=cvScalarAll(0)
);
参数:
src
输入图像。

dst
输出图像。(根据offset而相应改变大小)

offset
输入图像(或者其ROI)欲拷贝到的输出图像长方形的左上角坐标(或者左下角坐标,如果以左下角为原点)。长方形的尺寸要和原图像的尺寸的ROI分之一匹配。
——指的是输出图像上指定哪个点为原点坐标,拷贝图像。例子中选择了cvPoint(5,5),cvPoint(25,25)这两个点分别为图像的原点,则输出图像要对应的扩大cvSize(img->width+10,img->height+10),cvSize(img->width+50,img->height+50)。
——这里输出图像至少要在边界上加上原点坐标值,当然最好是两倍(不然就是只有两条边会有边框)。所以才是+10和50。

bordertype已拷贝的原图像长方形的边界的类型:
IPL_BORDER_CONSTANT——填充边界为固定值,值由函数最后一个参数指定。(默认黑色填充)
IPL_BORDER_REPLICATE——边界用上下行或者左右列来复制填充。(其他两种IPL边界类型, IPL_BORDER_REFLECT 和IPL_BORDER_WRAP现已不支持)。

value如果边界类型为IPL_BORDER_CONSTANT的话,那么此为边界像素的值。

我们在前面已经提到,当调用OpenCV库函数中的卷积功能时,cvCopyMakeBorder()函数就会被调用。在大多数情况下,边界类型为
IPL_BORDER_REPLICATE,但有时并不希望用它。所以在另一种场合,可能用到cvCopyMakeBorder()。你可以创建一幅具有比想要得到的边界稍微大一些的图像,无论调用任何常规操作,接下来就可以剪切到对源图像所感兴趣的部分。这样一来,OpenCV的自动加边就不会影响所关心的像素。

20.5.2程序实例

#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
#include <iostream>
int main(int argc,char** argv)
{
IplImage* src=cvLoadImage("b.jpg");
IplImage* dst1=cvCreateImage(cvSize(src->width+40,src->height+40),src->depth,src->nChannels);
//因为cvPoint(20,20),所以长宽必须是+(20*2=)40
IplImage* dst2=cvCreateImage(cvSize(src->width+40,src->height+40),src->depth,src->nChannels);
cvZero(dst1);
cvZero(dst2);
cvCopyMakeBorder(src,dst1,cvPoint(20,20),IPL_BORDER_REPLICATE); //用边界像素的值填充
cvCopyMakeBorder(src,dst2,cvPoint(20,20),IPL_BORDER_CONSTANT); //用黑色填充
cvNamedWindow("dst1");
cvNamedWindow("dst2");
cvShowImage("src",src);
cvShowImage("dst1",dst1);
cvShowImage("dst2",dst2);
cvWaitKey(0);
cvDestroyWindow("src");
cvDestroyWindow("dst1");
cvDestroyWindow("dst2");
cvReleaseImage(&src);
cvReleaseImage(&dst1);
cvReleaseImage(&dst2);
return 0;
}