在OpenCV滤波算法中,有两个非常重要的基本工具函数,copyMakeBorder和borderInterpolate
copyMakeBorder
函数原型
void copyMakeBorder( const Mat& src, Mat& dst,
int top, int bottom, int left, int right,
int borderType, const Scalar& value=Scalar() );
源码在utils.cpp中
功能
扩充src的边缘,将图像变大,然后以各种外插方式自动填充图像边界,这个函数实际上调用了函数cv::borderInterpolate,这个函数最重要的功能就是为了处理边界,比如均值滤波或者中值滤波中,使用copyMakeBorder将原图稍微放大,然后我们就可以处理边界的情况了
其中:
src,dst:原图与目标图像
top,bottom,left,right分别表示在原图四周扩充边缘的大小
borderType:扩充边缘的类型,就是外插的类型,OpenCV中给出以下几种方式
* BORDER_REPLICATE
* BORDER_REFLECT
* BORDER_REFLECT_101
* BORDER_WRAP
* BORDER_CONSTANT
实际中,还有其他的宏定义
//! various border interpolation methods
enum { BORDER_REPLICATE=IPL_BORDER_REPLICATE, BORDER_CONSTANT=IPL_BORDER_CONSTANT,
BORDER_REFLECT=IPL_BORDER_REFLECT, BORDER_WRAP=IPL_BORDER_WRAP,
BORDER_REFLECT_101=IPL_BORDER_REFLECT_101,
BORDER_REFLECT101=BORDER_REFLECT_101,
BORDER_TRANSPARENT=IPL_BORDER_TRANSPARENT,
BORDER_DEFAULT=BORDER_REFLECT_101, BORDER_ISOLATED=16 };
这几种方式到底什么意思呢?
OpenCV给出了解释:
代码来自源码:filter.cpp
/*
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‘
*/
个人觉得OpenCV解释的还是挺形象的
这里我们重点看下面这非常常见的几种
BORDER_REPLICATE:复制法,也就是复制最边缘像素。
如上图,红色区域为src的最边界像素,我们将边缘扩大了5个像素(right=5),蓝色区域的宽度就是5,复制了5次红色区域的值
这种方式也就是OpenCV中的中值滤波medianBlur采用的边界处理方式
BORDER_REFLECT_101:对称法,也就是以最边缘像素为轴,对称。
下面我们看图
绿色区域是src最边界的像素,蓝色区域是我们扩充的5个像素的扩充边界,而红色区域就是蓝色区域在src的对称部分
这种方式也是OpenCV边界处理的默认方式(BORDER_DEFAULT=BORDER_REFLECT_101)
也是filter2D,blur,GaussianBlur,bilateralFilter的默认处理方式,所以这种方式在边界处理中应用还是非常广泛的
BORDER_CONSTANT:常量法。
常量法就是以一个常量像素值(由参数 value给定)填充扩充的边界值,这种方式在仿射变换,透视变换中非常常见
如下图,
我们使用了默认的value,黑色填充了边界,所以红色区域的扩充的5个像素宽的边界是黑色的
在copyMakeBorder的内部,调用了函数borderInterpolate
borderInterpolate
函数原型
int borderInterpolate( int p, int len, int borderType );
源码在filter.cpp中
功能
根据不同的外插方法(borderType),如 BORDER_REPLICATE,计算外插像素对应于原图中的1D坐标,一般不单独使用,而在其他函数内部使用,如在copyMakeBorder中使用
其中
p:扩充边缘的像素的坐标(横坐标或者纵坐标)
len:src对于p所在的维的大小
borderType:与函数copyMakeBorder中的意思一样,这里就不重复了
示例:
比如我们在X方向使用BORDER_WRAP边界方式,Y方向采用BORDER_REFLECT_101方式,那么计算扩充边界像素Point(-5,100)对应原图中的位置就是
float val = img.at<float>(borderInterpolate(100, img.rows, BORDER_REFLECT_101),
borderInterpolate(-5, img.cols, BORDER_WRAP));
之前的博客中的滤波算法,没有采用上面的函数,其实,如果采用上面的函数,处理滤波将会非常方便,下面的几篇博客中,我将会修改之前的算法,采用OpenCV提供的函数处理滤波