在OpenCV中图像边界扩展 copyMakeBorder 的实现

1. 边界处理的类型

2. opencv的实现


在图像处理中,经常需要空域或频域的滤波处理,在进入真正的处理程序前,需要考虑图像边界情况。

通常的处理方法是为图像增加一定的边缘,以适应 卷积核 在原图像边界的操作。


1. 增加边界的类型有以下4个类型:

以一行图像数据为例,abcdefgh是原图数据,|是图像边界,为原图加边

aaaaaa|abcdefgh|hhhhhhh     重复

fedcba|abcdefgh|hgfedcb    反射

gfedcb|abcdefgh|gfedcba  反射101,相当于上一行的左右互换

cdefgh|abcdefgh|abcdefg  外包装

iiiiii|abcdefgh|iiiiiii  with some specified 'i'  常量

2. opencv的实现

opencv中有几处增加边界的实现,其源码分别散布在Utils.cpp,Filter.cpp,Ts_func.cpp中,功能和实现都基本相同。

以Utils的copyMakeBorder,及Filter中的borderInterpolate为例,这两种的代码风格比较通俗易懂。

边界处理的步骤:

   首先,为目的图像(结果图像)分配内存,图像大小为size(src.rows + top + bottom, src.cols + left + right)

    然后,以原图为基准,逐行处理,先扩展左边界,复制原图数据到目的图像,再扩展右边界。

    最后,扩展上边界,以及下边界。


其中,每扩展一个边界像素,都需要计算出对应的原图中的位置,这个功能被提炼出来,就是borderInterpolate

[cpp]  view plain copy
  1. /* 
  2.  Various border types, image boundaries are denoted with '|' 
  3.   
  4.  * BORDER_REPLICATE:     aaaaaa|abcdefgh|hhhhhhh 
  5.  * BORDER_REFLECT:       fedcba|abcdefgh|hgfedcb 
  6.  * BORDER_REFLECT_101:   gfedcb|abcdefgh|gfedcba 
  7.  * BORDER_WRAP:          cdefgh|abcdefgh|abcdefg         
  8.  * BORDER_CONSTANT:      iiiiii|abcdefgh|iiiiiii  with some specified 'i' 
  9.  */  
  10. int cv::borderInterpolate( int p, int len, int borderType ) // p是扩展边界的位置,len是原图宽度  
  11. {  
  12.     if( (unsigned)p < (unsigned)len )     // 转换为无符号类型,左边界和上边界:p一般是负数,右边界和下边界,p一般是大于len的。  
  13.         ;  
  14.     else if( borderType == BORDER_REPLICATE ) // 重复类型,每次对应原图的位置是0或len-1  
  15.         p = p < 0 ? 0 : len - 1;  
  16.     else if( borderType == BORDER_REFLECT || borderType == BORDER_REFLECT_101 ) // 反射/映射  
  17.     {  
  18.         int delta = borderType == BORDER_REFLECT_101;  
  19.         if( len == 1 )  
  20.             return 0;  
  21.         do  
  22.         {  
  23.             if( p < 0 )    // 反射:左边界或101:右边界  
  24.                 p = -p - 1 + delta;  
  25.             else  
  26.                 p = len - 1 - (p - len) - delta;  
  27.         }  
  28.         while( (unsigned)p >= (unsigned)len );  
  29.     }  
  30.     else if( borderType == BORDER_WRAP )  // 包装  
  31.     {  
  32.         if( p < 0 )  // 左边界  
  33.             p -= ((p-len+1)/len)*len;  
  34.         if( p >= len )  // 右边界  
  35.             p %= len;  
  36.     }  
  37.     else if( borderType == BORDER_CONSTANT )  // 常量,另外处理  
  38.         p = -1;  
  39.     else  
  40.         CV_Error( CV_StsBadArg, "Unknown/unsupported border type" );  
  41.     return p;  
  42. }  

非常量类型边界扩展:

[cpp]  view plain copy
  1. static void copyMakeBorder_8u( const uchar* src, size_t srcstep, Size srcroi, // 原图 参数:数据,step,大小  
  2.                                uchar* dst, size_t dststep, Size dstroi,  // 目的图像参数  
  3.                                int top, int left, int cn, int borderType )  
  4. {  
  5.     const int isz = (int)sizeof(int);  
  6.     int i, j, k, elemSize = 1;  
  7.     bool intMode = false;  
  8.   
  9.     if( (cn | srcstep | dststep | (size_t)src | (size_t)dst) % isz == 0 )  
  10.     {  
  11.         cn /= isz;  
  12.         elemSize = isz;  
  13.         intMode = true;  
  14.     }  
  15.   
  16.     AutoBuffer<int> _tab((dstroi.width - srcroi.width)*cn);  // 大小是 扩展的左右边界之和,仅用于存放 扩展的边界 在原图中的位置  
  17.     int* tab = _tab;  
  18.     int right = dstroi.width - srcroi.width - left;  
  19.     int bottom = dstroi.height - srcroi.height - top;  
  20.       
  21.     for( i = 0; i < left; i++ ) // 左边界  
  22.     {  
  23.         j = borderInterpolate(i - left, srcroi.width, borderType)*cn;  // 计算出原图中对应的位置  
  24.         for( k = 0; k < cn; k++ )  // 每个通道的处理  
  25.             tab[i*cn + k] = j + k;  
  26.     }  
  27.       
  28.     for( i = 0; i < right; i++ )  // 右边界  
  29.     {  
  30.         j = borderInterpolate(srcroi.width + i, srcroi.width, borderType)*cn;  
  31.         for( k = 0; k < cn; k++ )  
  32.             tab[(i+left)*cn + k] = j + k;  
  33.     }  
  34.   
  35.     srcroi.width *= cn;  
  36.     dstroi.width *= cn;  
  37.     left *= cn;  
  38.     right *= cn;  
  39.       
  40.     uchar* dstInner = dst + dststep*top + left*elemSize;  
  41.   
  42.     for( i = 0; i < srcroi.height; i++, dstInner += dststep, src += srcstep ) // 从原图中复制数据到扩展的边界中  
  43.     {  
  44.         if( dstInner != src )  
  45.             memcpy(dstInner, src, srcroi.width*elemSize);  
  46.           
  47.         if( intMode )  
  48.         {  
  49.             const int* isrc = (int*)src;  
  50.             int* idstInner = (int*)dstInner;  
  51.             for( j = 0; j < left; j++ )  
  52.                 idstInner[j - left] = isrc[tab[j]];  
  53.             for( j = 0; j < right; j++ )  
  54.                 idstInner[j + srcroi.width] = isrc[tab[j + left]];  
  55.         }  
  56.         else  
  57.         {  
  58.             for( j = 0; j < left; j++ )  
  59.                 dstInner[j - left] = src[tab[j]];  
  60.             for( j = 0; j < right; j++ )  
  61.                 dstInner[j + srcroi.width] = src[tab[j + left]];  
  62.         }  
  63.     }  
  64.       
  65.     dstroi.width *= elemSize;  
  66.     dst += dststep*top;  
  67.       
  68.     for( i = 0; i < top; i++ )  // 上边界  
  69.     {  
  70.         j = borderInterpolate(i - top, srcroi.height, borderType);  
  71.         memcpy(dst + (i - top)*dststep, dst + j*dststep, dstroi.width); // 进行整行的复制  
  72.     }  
  73.       
  74.     for( i = 0; i < bottom; i++ ) // 先边界  
  75.     {  
  76.         j = borderInterpolate(i + srcroi.height, srcroi.height, borderType);  
  77.         memcpy(dst + (i + srcroi.height)*dststep, dst + j*dststep, dstroi.width); // 进行整行的复制  
  78.     }  
  79. }  

常量类型的扩展就更简单了:

[cpp]  view plain copy
  1. static void copyMakeConstBorder_8u( const uchar* src, size_t srcstep, Size srcroi,  
  2.                                     uchar* dst, size_t dststep, Size dstroi,  
  3.                                     int top, int left, int cn, const uchar* value )  
  4. {  
  5.     int i, j;  
  6.     AutoBuffer<uchar> _constBuf(dstroi.width*cn);  
  7.     uchar* constBuf = _constBuf;  
  8.     int right = dstroi.width - srcroi.width - left;  
  9.     int bottom = dstroi.height - srcroi.height - top;  
  10.       
  11.     for( i = 0; i < dstroi.width; i++ ) // 初始化 常量buf的值  
  12.     {  
  13.         for( j = 0; j < cn; j++ )  
  14.             constBuf[i*cn + j] = value[j];  
  15.     }  
  16.       
  17.     srcroi.width *= cn;  
  18.     dstroi.width *= cn;  
  19.     left *= cn;  
  20.     right *= cn;  
  21.       
  22.     uchar* dstInner = dst + dststep*top + left;  
  23.       
  24.     for( i = 0; i < srcroi.height; i++, dstInner += dststep, src += srcstep ) // 复制原图数据和扩展左右边界  
  25.     {  
  26.         if( dstInner != src )  
  27.             memcpy( dstInner, src, srcroi.width );  
  28.         memcpy( dstInner - left, constBuf, left );  
  29.         memcpy( dstInner + srcroi.width, constBuf, right );  
  30.     }  
  31.       
  32.     dst += dststep*top;  
  33.       
  34.     for( i = 0; i < top; i++ )  
  35.         memcpy(dst + (i - top)*dststep, constBuf, dstroi.width); // 扩展上边界  
  36.       
  37.     for( i = 0; i < bottom; i++ ) // 扩展下边界  
  38.         memcpy(dst + (i + srcroi.height)*dststep, constBuf, dstroi.width);  
  39. }  


对于medianBlur( InputArray _src0, OutputArray _dst, int ksize )的边界扩展方式是 重复复制最边缘像素 BORDER_REPLICATE。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值