看了园子里的博友们,感觉自己也应该把自己以前学习到的东西写下来,通过写下来也正好是一个回顾的过程。于是决定把今年学到了什么写下来,至于还有很多不是很清楚的地方也写下来,以便以后更好的理解。
这个写的是图像滤波。为什么要图像滤波呢,对于我们获取原始图像的时候,难免不被污染,就会有噪声的干扰,于是就有了图像滤波的操作,也就是对目标图像的噪声的抑制。这个操作也就是其他更深一层次的图像分析,图像理解来说的基础。
对于滤波的方法,分频率滤波和空域滤波,这篇主要说的是空域滤波,均值滤波,中值滤波,高斯滤波,跟双边滤波。
具体测试代码参考:
我们主要简单分析一下源码。
1、均值滤波。
均值滤波的opencv中的源码在smooth.cpp中为
void cv::blur( InputArray src, OutputArray dst,Size ksize, Point anchor, int borderType )
{
boxFilter( src, dst, -1, ksize, anchor, true, borderType );
} 调用了盒子滤波,Point anchor=Point(-1,-1),int borderType=BORDER_DEFAULT默认值
对于盒子滤波在SURF算法中提到,对于盒子滤波本人的理解就是,对于均值滤波每个像素点以1加权,而盒子滤波不一定是以1加权,不知道有没有其它差别。
void cv::boxFilter( InputArray _src, OutputArray _dst, int ddepth,
Size ksize, Point anchor,
bool normalize, int borderType )
{
Mat src = _src.getMat();//获得处理图像
//创建输出OutputArray _dst
int sdepth = src.depth(), cn = src.channels();//获取图像的深度和通道数
if( ddepth < 0 )//这边均值滤波传值过来的时候是-1小于0
ddepth = sdepth;
_dst.create( src.size(), CV_MAKETYPE(ddepth, cn) );//CV_MAKETYPE创建图像类型根据深度和通道数,这句是创建_dst
Mat dst = _dst.getMat();//通过OutputArray得到Mat类型
//BORDER_CONSTANT边界类型,opencv根据边界类型进行不同的插值运算来扩展边界
/*
不同的边界类型, '|'表示图像边界
* 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 使用特定的'i'
*/
if( borderType != BORDER_CONSTANT && normalize )//normalize均值滤波调用的时候是true,borderType=BORDER_DEFAULT默认值为
//BORDE R_REFLECT_101
{
if( src.rows == 1 )//如果源图像的行为1则滤波窗口的高度为1
ksize.height = 1;
if( src.cols == 1 )//如果源图像的列为1则滤波窗口的宽度为1
ksize.width = 1;
}
//创建盒子滤波 返回滤波引擎,不管创建什么滤波都返回滤波引擎,工厂模式是由滤波引擎可以创建各种滤波,这个是什么模式呢?
Ptr<FilterEngine> f = createBoxFilter( src.type(), dst.type(),
ksize, anchor, normalize, borderType );
f->apply( src, dst );//virtual void apply( const Mat& src, Mat& dst,const Rect& srcRoi=Rect(0,0,-1,-1),
//Point dstOfs=Point(0,0),bool isolated=false);可以控制滤波图像的大小,这边是全部滤波
}
下面是创建盒子滤波
cv::Ptr<cv::FilterEngine> cv::createBoxFilter( int srcType, int dstType, Size ksize,
Point anchor, bool normalize, int borderType )
{
int sdepth = CV_MAT_DEPTH(srcType);//由深度跟通道数可以确定图像类型,这边由类型确定深度,下面是确定通道数
int cn = CV_MAT_CN(srcType), sumType = CV_64F;//定义类型为64位浮点数
/*在types_c.h中有
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
*/
if( sdepth < CV_32S && (!normalize ||
ksize.width*ksize.height <= (sdepth == CV_8U ? (1<<23) :
sdepth == CV_16U ? (1 << 15) : (1 << 16))) )
sumType = CV_32S;//如果图像的深度不是32位或者64位的浮点型并且不归一化或者滤波窗口的面积
sumType = CV_MAKETYPE( sumType, cn );//这边也不是很清楚这么写的目的,感觉完全就是控制像素类型不是64位浮点就是32位int类型???,也就是CV_8U,CV_16U,CV_16S都对应CV_32S,其余对应CV_64F?
Ptr<BaseRowFilter> rowFilter = getRowSumFilter(srcType, sumType, ksize.width, anchor.x );
Ptr<BaseColumnFilter> columnFilter = getColumnSumFilter(sumType,
dstType, ksize.height, anchor.y, normalize ? 1./(ksize.width*ksize.height) : 1);
return Ptr<FilterEngine>(new FilterEngine(Ptr<BaseFilter>(0), rowFilter, columnFilter,
srcType, dstType, sumType, borderType ));
}
主要的就是两步getRowSumFilter跟getColumnSumFilter
cv::Ptr<cv::BaseRowFilter> cv::getRowSumFilter(int srcType, int sumType, int ksize, int anchor)
{
int sdepth = CV_MAT_DEPTH(srcType), ddepth = CV_MAT_DEPTH(sumType);
CV_Assert( CV_MAT_CN(sumType) == CV_MAT_CN(srcType) );
//要进行滤波的像素点如果这边设置小于0的话就是滤波窗口的一半,也就是滤波窗体的中心,这边滤波窗体一般设置为奇数
if( anchor < 0 )
anchor = ksize/2;
//对于不同的图像像素值类型计算一行的像素和
if( sdepth == CV_8U && ddepth == CV_32S )
return Ptr<BaseRowFilter>(new RowSum<uchar, int>(ksize, anchor));
if( sdepth == CV_8U && ddepth == CV_64F )
return Ptr<BaseRowFilter>(new RowSum<uchar, double>(ksize, anchor));
if( sdepth == CV_16U && ddepth == CV_32S )
return Ptr<BaseRowFilter>(new RowSum<ushort, int>(ksize, anchor));
if( sdepth == CV_16U && ddepth == CV_64F )
return Ptr<BaseRowFilter>(new RowSum<ushort, double>(ksize, anchor));
if( sdepth == CV_16S && ddepth == CV_32S )
return Ptr<BaseRowFilter>(new RowSum<short, int>(ksize, anchor));
if( sdepth == CV_32S && ddepth == CV_32S )
return Ptr<BaseRowFilter>(new RowSum<int, int>(ksize, anchor));
if( sdepth == CV_16S && ddepth == CV_64F )
return Ptr<BaseRowFilter>(new RowSum<short, double>(ksize, anchor));
if( sdepth == CV_32F && ddepth == CV_64F )
return Ptr<BaseRowFilter>(new RowSum<float, double>(ksize, anchor));
if( sdepth == CV_64F && ddepth == CV_64F )
return Ptr<BaseRowFilter>(new RowSum<double, double>(ksize, anchor));
CV_Error_( CV_StsNotImplemented,
("Unsupported combination of source format (=%d), and buffer format (=%d)",
srcType, sumType));
return Ptr<BaseRowFilter>(0);
}
创建一行的像素和操作如下:
这个继承对1维数据处理的基类,实现滤波操作,这边的参数const uchar* src, uchar* dst, int width, int cn怎么传的呢???望有人解答一下
void operator()(const uchar* src, uchar* dst, int width, int cn)
{
const T* S = (const T*)src;//将src转换到对应的T模板类型
ST* D = (ST*)dst;//dst转换到对应的ST模板类型
int i = 0, k, ksz_cn = ksize*cn;
width = (width - 1)*cn;
for( k = 0; k < cn; k++, S++, D++ )
{
ST s = 0;
for( i = 0; i < ksz_cn; i += cn )
s += S[i];
D[0] = s;
for( i = 0; i < width; i += cn )
{
s += S[i + ksz_cn] - S[i];
D[i+cn] = s;
}
}
}
好了,就先写到这,其实最核心的东西没有写,自己也不甚理解,望有人给予一定的解惑,谢谢。