【OpenCV】图像滤波 Image Filtering

来源:互联网 发布:mysql 修改字段默认值 编辑:程序博客网 时间:2024/05/21 08:37


译自《The OpenCV Reference Manual Release 2.3》

CHAPTER THREE: IMGPROC. IMAGE PROCESSING  3.1 Image Filtering

转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7718831

本节描述对2D图像执行的各种线性和非线性的滤波操作。即用图像中每个像素点(x,y)临近的点进行运算。如果是线性滤波器,结果是每个像素值的加权和;如果是形态操作,结果是最小或最大值之类的。对每个坐标的像素操作的输出结果也在同一个坐标(x,y)处,这就意味着输出图像和输入图像有相同的大小。通常情况下,这些函数支持多通道图像,即对每个通道单独进行操作。因此,输出图像也与输入图像有相同的通道数。

本节描述的函数与类的另一个共同的特点是,不同于简单的算术运算,他们需要对一些不存在的像素值进行推测。例如,你想使用 3*3的高斯滤波器,处理图像每行最左侧的像素时还需要其左侧的像素,也就是图像外的像素。你可以让这些像素等于源图像最左侧的像素(“复制边(replicated border)”外推法),或者假设所有不存在的像素值为零(“恒量边(constant border)”外推法),等等。OpenCV允许你指定外推方法。详情请参阅 borderInterpolate()函数的功能及其参数描述。

BaseColumnFilter

单列核的基础滤波器。
[cpp] view plaincopy
  1. class BaseColumnFilter  
  2. {  
  3.     public:  
  4.     virtual ~BaseColumnFilter();  
  5.     // 用以被用户重写  
  6.     //  
  7.     // 对列的集合进行滤波操作  
  8.     // 输入"dstcount + ksize - 1" 行,输出"dstcount" 行,  
  9.     // 输入和输出的每行含有"width"个元素,  
  10.     // 滤波之后的行写入缓存"dst"中.  
  11.     virtual void operator()(const uchar** src, uchar* dst, int dststep,  
  12.     int dstcount, int width) = 0;  
  13.     // 重置滤波器的状态(IIR滤波器中可能用到)  
  14.     virtual void reset();  
  15.     int ksize; // 核的孔径  
  16.     int anchor; // 定位点坐标  
  17.     // 处理过程中一般不使用  
  18. };  
类 BaseColumnFilter是使用单列核对数据滤波的基础类。滤波不一定是线性滤波,表示如下:

其中 F 是滤波函数,但是用类来表示,因为类可以有其他的,如储存之前处理的数据之类的附加功能。这个类只是定义一个接口并不直接使用。作为替代,OpenCV中有一些函数(你可以添加更多)实现了特定的滤波功能并返回指向派生类的指针。这些指针通过 FilterEngine构造函数。 虽然滤波操作接口使用uchar 类型,具体实施时并限于8位数据。

BaseFilter

对2D图像滤波的基础类。
[cpp] view plaincopy
  1. class BaseFilter  
  2. {  
  3.     public:  
  4.     virtual ~BaseFilter();  
  5.     // 用以被用户重写  
  6.     //  
  7.     // 对列的集合进行滤波操作  
  8.     // 输入"dstcount + ksize.height - 1" 行,输出"dstcount" 行,  
  9.     // 输入的每行含有"(width + ksize.width-1)*cn"个元素  
  10.     // 输出的每行含有"width*cn"个元素,  
  11.     // 滤波之后的行写入缓存"dst"中.  
  12.     virtual void operator()(const uchar** src, uchar* dst, int dststep,  
  13.     int dstcount, int width, int cn) = 0;  
  14.     // 重置滤波器的状态(IIR滤波器中可能用到)  
  15.     virtual void reset();  
  16.     Size ksize;  
  17.     Point anchor;  
  18. };  
类 BaseFilter 是使用2D核对数据滤波的基础类。滤波不一定是线性的,可以表示如下:


BaseRowFilter

单列核滤波器的基础类。
[cpp] view plaincopy
  1. class BaseRowFilter  
  2. {  
  3.     public:  
  4.     virtual ~BaseRowFilter();  
  5.     // 用以被用户重写  
  6.     //  
  7.     // 对输入的单列进行滤波操作  
  8.     // 输入列有 "width"个元素, 每个元素有 "cn" 个通道.  
  9.     // 滤波之后的行写入缓存"dst"中.  
  10.     virtual void operator()(const uchar* src, uchar* dst,  
  11.     int width, int cn) = 0;  
  12.     int ksize, anchor;  
  13. };  
类 BaseRowFilter 是使用单列核对数据滤波的基础类。滤波不一定是线性的,可以表示如下:

其中 F 是滤波函数。此类只是定义了一个接口并不直接使用。这个类只是定义一个接口并不直接使用。作为替代,OpenCV中有一些函数(你可以添加更多)实现了特定的滤波功能并返回指向派生类的指针。这些指针通过 FilterEngine 构造函数。 虽然滤波操作接口使用uchar类型,具体实施时并限于8位数据。

FilterEngine

通用图像滤波类。
[cpp] view plaincopy
  1. class FilterEngine  
  2. {  
  3. public:  
  4.     // 空的构造函数  
  5.     FilterEngine();  
  6.     // 构造2D的不可分的滤波器(!_filter2D.empty())或者  
  7.     // 可分的滤波器 (!_rowFilter.empty() && !_columnFilter.empty())  
  8.     // 输入数据类型为 "srcType", 输出类型为"dstType",  
  9.     // 中间的数据类型为 "bufType".  
  10.     // _rowBorderType 何 _columnBorderType 决定图像边界如何被外推扩充  
  11.     // 只有 _rowBorderType and/or _columnBorderType  
  12.     // == BORDER_CONSTANT 时 _borderValue 才会被用到  
  13.     FilterEngine(const Ptr<BaseFilter>& _filter2D,  
  14.     const Ptr<BaseRowFilter>& _rowFilter,  
  15.     const Ptr<BaseColumnFilter>& _columnFilter,  
  16.     int srcType, int dstType, int bufType,  
  17.     int _rowBorderType=BORDER_REPLICATE,  
  18.     int _columnBorderType=-1, // 默认使用 _rowBorderType  
  19.     const Scalar& _borderValue=Scalar());  
  20.     virtual ~FilterEngine();  
  21.     // 初始引擎的分割函数  
  22.     void init(const Ptr<BaseFilter>& _filter2D,  
  23.     const Ptr<BaseRowFilter>& _rowFilter,  
  24.     const Ptr<BaseColumnFilter>& _columnFilter,  
  25.     int srcType, int dstType, int bufType,  
  26.     int _rowBorderType=BORDER_REPLICATE, int _columnBorderType=-1,  
  27.     const Scalar& _borderValue=Scalar());  
  28.     // 定义图像尺寸"wholeSize"为ROI开始滤波.  
  29.     // 返回图像开始的y-position坐标.  
  30.     virtual int start(Size wholeSize, Rect roi, int maxBufRows=-1);  
  31.     // 另一种需要图像的开始  
  32.     virtual int start(const Mat& src, const Rect& srcRoi=Rect(0,0,-1,-1),  
  33.     bool isolated=falseint maxBufRows=-1);  
  34.     // 处理源图像的另一部分  
  35.     // 从"src"到"dst"处理"srcCount" 行  
  36.     // 返回处理的行数  
  37.     virtual int proceed(const uchar* src, int srcStep, int srcCount,  
  38.     uchar* dst, int dstStep);  
  39.     // 处理整个ROI的高层调用  
  40.     virtual void apply( const Mat& src, Mat& dst,  
  41.     const Rect& srcRoi=Rect(0,0,-1,-1),  
  42.     Point dstOfs=Point(0,0),  
  43.     bool isolated=false);  
  44.     bool isSeparable() const { return filter2D.empty(); }  
  45.     // 输入图中未被处理的行数  
  46.     int remainingInputRows() const;  
  47.     // 输入中未被处理的行数  
  48.     int remainingOutputRows() const;  
  49.     // 源图的开始和结束行  
  50.     int startY, endY;  
  51.     // 指向滤波器的指针  
  52.     Ptr<BaseFilter> filter2D;  
  53.     Ptr<BaseRowFilter> rowFilter;  
  54.     Ptr<BaseColumnFilter> columnFilter;  
  55. };  
类 FilterEngine 可以被用于对任何一个图像进行滤波。它包含了所有必要的缓冲区,计算需要的图像外的“虚”像素推算值等等。通过各种创建 *Filter 的函数(见下文)可以返回指向初始化的 FilterEngine 的实例,之后可以使用这些实例中的高层接口如 filter2D() erode()dilate() 等。因此,此类在OpenCV的很多滤波函数中起着关键的作用。 
这个类使得滤波和其他函数结合更容易,如色彩空间转换,阈值,算术运算,等操作。将几个操作相结合在一起你可以得到更好的性能,因为数据都留在缓存中。例如以下是对浮点图像执行 Laplace 算子处理的简单例子,Laplacian() 函数可以简化为:
[cpp] view plaincopy
  1. void laplace_f(const Mat& src, Mat& dst)  
  2. {  
  3.     CV_Assert( src.type() == CV_32F );  
  4.     dst.create(src.size(), src.type());  
  5.     // get the derivative and smooth kernels for d2I/dx2.  
  6.     // for d2I/dy2 consider using the same kernels, just swapped  
  7.     Mat kd, ks;  
  8.     getSobelKernels( kd, ks, 2, 0, ksize, false, ktype );  
  9.     // process 10 source rows at once  
  10.     int DELTA = std::min(10, src.rows);  
  11.     Ptr<FilterEngine> Fxx = createSeparableLinearFilter(src.type(),  
  12.     dst.type(), kd, ks, Point(-1,-1), 0, borderType, borderType, Scalar() );  
  13.     Ptr<FilterEngine> Fyy = createSeparableLinearFilter(src.type(),  
  14.     dst.type(), ks, kd, Point(-1,-1), 0, borderType, borderType, Scalar() );  
  15.     int y = Fxx->start(src), dsty = 0, dy = 0;  
  16.     Fyy->start(src);  
  17.     const uchar* sptr = src.data + y*src.step;  
  18.     // allocate the buffers for the spatial image derivatives;  
  19.     // the buffers need to have more than DELTA rows, because at the  
  20.     // last iteration the output may take max(kd.rows-1,ks.rows-1)  
  21.     // rows more than the input.  
  22.     Mat Ixx( DELTA + kd.rows - 1, src.cols, dst.type() );  
  23.     Mat Iyy( DELTA + kd.rows - 1, src.cols, dst.type() );  
  24.     // inside the loop always pass DELTA rows to the filter  
  25.     // (note that the "proceed" method takes care of possibe overflow, since  
  26.     // it was given the actual image height in the "start" method)  
  27.     // on output you can get:  
  28.     // * < DELTA rows (initial buffer accumulation stage)  
  29.     // * = DELTA rows (settled state in the middle)  
  30.     // * > DELTA rows (when the input image is over, generate  
  31.     // "virtual" rows using the border mode and filter them)  
  32.     // this variable number of output rows is dy.  
  33.     // dsty is the current output row.  
  34.     // sptr is the pointer to the first input row in the portion to process  
  35.     for( ; dsty < dst.rows; sptr += DELTA*src.step, dsty += dy )  
  36.     {  
  37.         Fxx->proceed( sptr, (int)src.step, DELTA, Ixx.data, (int)Ixx.step );  
  38.         dy = Fyy->proceed( sptr, (int)src.step, DELTA, d2y.data, (int)Iyy.step );  
  39.         if( dy > 0 )  
  40.         {  
  41.             Mat dstripe = dst.rowRange(dsty, dsty + dy);  
  42.             add(Ixx.rowRange(0, dy), Iyy.rowRange(0, dy), dstripe);  
  43.         }  
  44.     }  
  45. }  
如果你不需要对滤波过程的控制,你可以简单地使用 FilterEngine:: apply方法。
[cpp] view plaincopy
  1. void FilterEngine::apply(const Mat& src, Mat& dst,  
  2.         const Rect& srcRoi, Point dstOfs, bool isolated)  
  3. {  
  4.     // check matrix types  
  5.     CV_Assert( src.type() == srcType && dst.type() == dstType );  
  6.     // handle the "whole image" case  
  7.     Rect _srcRoi = srcRoi;  
  8.     if( _srcRoi == Rect(0,0,-1,-1) )  
  9.     _srcRoi = Rect(0,0,src.cols,src.rows);  
  10.     // check if the destination ROI is inside dst.  
  11.     // and FilterEngine::start will check if the source ROI is inside src.  
  12.     CV_Assert( dstOfs.x >= 0 && dstOfs.y >= 0 &&  
  13.     dstOfs.x + _srcRoi.width <= dst.cols &&  
  14.     dstOfs.y + _srcRoi.height <= dst.rows );  
  15.     // start filtering  
  16.     int y = start(src, _srcRoi, isolated);  
  17.     // process the whole ROI. Note that "endY - startY" is the total number  
  18.     // of the source rows to process  
  19.     // (including the possible rows outside of srcRoi but inside the source image)  
  20.     proceed( src.data + y*src.step,  
  21.         (int)src.step, endY - startY,  
  22.         dst.data + dstOfs.y*dst.step +  
  23.         dstOfs.x*dst.elemSize(), (int)dst.step );  
  24. }  
不同于OpenCV的早期版本,现在的滤波操作支持图像ROI概念,也就是说,在ROI图像之外但在图像之内的像素点可以用于滤波操作。例如,你可以取单个像素作为ROI滤波。通过滤波器之后将范围特定的像素。然而,通过传递FilterEngine::startFilterEngine::apply 参数 isolated=false 它有可能仍是旧的图像。你可以明确指定传递ROI给 FilterEngine::apply 函数或者构造新的矩阵头:
[cpp] view plaincopy
  1. // compute dI/dx derivative at src(x,y)  
  2. // method 1:  
  3. // form a matrix header for a single value  
  4. float val1 = 0;  
  5. Mat dst1(1,1,CV_32F,&val1);  
  6. Ptr<FilterEngine> Fx = createDerivFilter(CV_32F, CV_32F,  
  7. 1, 0, 3, BORDER_REFLECT_101);  
  8. Fx->apply(src, Rect(x,y,1,1), Point(), dst1);  
  9. // method 2:  
  10. // form a matrix header for a single value  
  11. float val2 = 0;  
  12. Mat dst2(1,1,CV_32F,&val2);  
  13. Mat pix_roi(src, Rect(x,y,1,1));  
  14. Sobel(pix_roi, dst2, dst2.type(), 1, 0, 3, 1, 0, BORDER_REFLECT_101);  
探索中的数据类型。由于它是在 BaseFilter 描述中提到的具体的滤波器,虽然  Base*Filter::operator()  除了UCHAR的指针并其他类型的信息, 但实际它可以处理任何类型的数据。为了保证所有的函数可以运行,使用以下规则:
  • 在分离滤波的情况下,首先应用 FilterEngine::rowFilter 。它把输入图像数据(srcType类型)的中间结果存储在内部缓冲区(bufType类型)。然后,这些中间结果作为单通道数据由 FilterEngine:: columnFilter处理,结果存储在输出图像(dstType类型)中。因此,输入 RowFilter 类型是srcType 而输出类型是 bufType。输入 columnFilter 的类型是CV_MAT_DEPTH(bufType)而输出的类型为CV_MAT_DEPTH(dstType)。
  • 在非分离滤波的情况下,bufType 必须与 srcType 类型相同。如果需要,源数据会被复制到临时缓冲区之后传递给 FilterEngine:: filter2D 。也就是说,输入filter2D 类型为 scrType(= bufType),输出类型是dstType。