Opencv中的copyMakeBorder和borderInterpolate以及getRectSubPix函数讲解

转自:http://blog.csdn.net/qianqing13579/article/details/42323397#comments


在OpenCV滤波算法中,有两个非常重要的基本工具函数,copyMakeBorder和borderInterpolate

这里也会提到getRectSubPix。


1. 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,

其中:

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:复制法,也就是复制最边缘像素。

Opencv中的copyMakeBorder和borderInterpolate以及getRectSubPix函数讲解

如上图,红色区域为src的最边界像素,蓝色区域是扩充的边界,我们将边缘扩大了5个像素(right=5),蓝色区域的宽度就是5,复制了5次红色区域的值

这种方式也就是OpenCV中的中值滤波medianBlur采用的边界处理方式


BORDER_REFLECT_101:对称法,也就是以最边缘像素为轴,对称。

下面我们看图

Opencv中的copyMakeBorder和borderInterpolate以及getRectSubPix函数讲解

绿色区域是src最边界的像素,蓝色区域是我们扩充的5个像素的扩充边界,而红色区域就是蓝色区域在src的对称部分

 这种方式也是OpenCV边界处理的默认方式(BORDER_DEFAULT=BORDER_REFLECT_101)

也是filter2D,blur,GaussianBlur,bilateralFilter的默认处理方式,所以这种方式在边界处理中应用还是非常广泛的


BORDER_CONSTANT:常量法。

常量法就是以一个常量像素值(由参数 value给定)填充扩充的边界值,这种方式在仿射变换,透视变换中非常常见

如下图,

Opencv中的copyMakeBorder和borderInterpolate以及getRectSubPix函数讲解

我们使用了默认的value,黑色填充了边界,所以红色区域的扩充的5个像素宽的边界是黑色的

在copyMakeBorder的内部,调用了函数borderInterpolate


2. borderInterpolate


函数原型

int borderInterpolate( int p, int len, int borderType );

源码在filter.cpp中


功能

计算扩充的像素对应原图哪个坐标的像素

其中

p:该扩充图像的像素在原图中某一坐标轴上的坐标(注意:是在原图中的坐标!)

len:扩充像素对应的原图像的坐标轴的长度

borderType:扩充类型


示例:

[cpp] view plain copy
  1. int main(int argc,char *argv[])  
  2. {  
  3.       
  4.     Mat srcImage = imread("D:/Image/Gray/Beauty.bmp", -1); // 图像大小1000x580  
  5.     int borderType = BORDER_REFLECT_101;// 以边缘为轴,对称  
  6.     int left = 3, right = 3, top = 3, bottom = 3;  
  7.     int locationsOfSrcImage;  
  8.   
  9.     // 计算扩充像素在原图中的坐标  
  10.     printf("left:");  
  11.     for (int i = 0; i < left; ++i)  
  12.     {  
  13.         locationsOfSrcImage = borderInterpolate(i - left, srcImage.cols, borderType);  
  14.         printf("%d,", locationsOfSrcImage);  
  15.     }  
  16.     printf("\n"); // left:3,2,1,  
  17.   
  18.     printf("right:");  
  19.     for (int i = 0; i < right; ++i)  
  20.     {  
  21.         locationsOfSrcImage = borderInterpolate(i + srcImage.cols, srcImage.cols, borderType);  
  22.         printf("%d,", locationsOfSrcImage);  
  23.     }  
  24.     printf("\n");  // right:998,997,996,  
  25.   
  26.     printf("top:");  
  27.     for (int i = 0; i < top; ++i)  
  28.     {  
  29.         locationsOfSrcImage = borderInterpolate(i - top, srcImage.rows, borderType);  
  30.         printf("%d,", locationsOfSrcImage);  
  31.     }  
  32.     printf("\n"); // top:3,2,1,  
  33.   
  34.     printf("bottom:");  
  35.     for (int i = 0; i < bottom; ++i)  
  36.     {  
  37.         locationsOfSrcImage = borderInterpolate(i + srcImage.rows, srcImage.rows, borderType);  
  38.         printf("%d,", locationsOfSrcImage);  
  39.     }  
  40.     printf("\n"); // bottom:578,577,576,  
  41.   
  42.     // 如果对原图中原坐标进行计算,那么结果还是原来的坐标  
  43.     printf("原图x方向坐标:");  
  44.     for (int i = 0; i < srcImage.cols; ++i)  
  45.     {  
  46.         locationsOfSrcImage = borderInterpolate(i, srcImage.cols, BORDER_REFLECT);  
  47.         printf("%d,", locationsOfSrcImage);  
  48.     }  
  49.     printf("\n");// 输出从0~999  
  50.   
  51.     return 0;  
  52. }  

程序中使用的图像大小为1000x580(宽1000,高580)

结果:

Opencv中的copyMakeBorder和borderInterpolate以及getRectSubPix函数讲解

对上述代码稍作解释:
[cpp] view plain copy
  1. for (int i = 0; i < left; ++i)  
  2. {  
  3.     locationsOfSrcImage = borderInterpolate(i - left, srcImage.cols, borderType);  
  4.     printf("%d,", locationsOfSrcImage);  
  5. }  
  6. printf("\n"); // left:3,2,1,  

这段代码中,我们要计算左边扩充的像素在原图x方向上的坐标,由于borderInterpolate的第一个参数是扩充像素在原图中某一坐标轴上的坐标,注意,这些坐标是相对于原图来说的,由于原图最左边像素的坐标为0,所以这些扩充像素相对于原图坐标就是为负数,而left大小为3,所以i-left依次为-3,-2,-1,也就是扩充的3个像素在原图中的坐标为-3,-2,-1。borderInterpolate的第二个参数是原图像的坐标轴的长度,由于计算的x方向的坐标,对应x方向的长度,所以是cols。扩充类型是BORDER_REFLECT_101,对称法,从上面BORDER_REFLECT_101的示意图可以很容易看出,扩充的像素对应原图像素的坐标为3,2,1。


3. getRectSubPix

从图像中提取象素矩形,使用子像素精度。这里所说的子像素精度是指,如果给定的矩形坐标不是整数,可以通过双线性插值的方式获得。

在平时的使用中经常会用整数的

C++: void getRectSubPix(InputArray image, Size patchSize, Point2f center, OutputArray dst, int patchType=-1 )

C:void cvGetRectSubPix( const CvArr* src, CvArr* dst, CvPoint2D32f center );


src : 输入图像.

patchSize 是矩形的大小

dst : 提取的矩形

patchType: 提取图像的深度,默认(-1)和src相同

center : 提取的象素矩形的中心,浮点数坐标。中心必须位于图像内部.

函数 cvGetRectSubPix 从图像 src 中提取矩形:

dst(x, y) = src(x + center.x - (width(dst)-1)*0.5, y + center.y - (height(dst)-1)*0.5)

其中非整数象素点坐标采用双线性插值提取。对多通道图像,每个通道独立单独完成提取。尽管函数要求矩形的中心一定要在输入图像之中,但是有可能出现矩形的一部分超出图像边界的情况,这时,该函数复制边界的模识(replication border mode)(即用与矩形相交的图像边界线段的像素来代替矩形超越部分的像素)。

也就是说超过部分是用靠近的边界的值来填充的。