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

    xiaoxiao2021-03-25  164

    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 /*  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'  */  int cv::borderInterpolate( int p, int len, int borderType ) // p是扩展边界的位置,len是原图宽度  {      if( (unsigned)p < (unsigned)len )     // 转换为无符号类型,左边界和上边界:p一般是负数,右边界和下边界,p一般是大于len的。          ;      else if( borderType == BORDER_REPLICATE ) // 重复类型,每次对应原图的位置是0或len-1          p = p < 0 ? 0 : len - 1;      else if( borderType == BORDER_REFLECT || borderType == BORDER_REFLECT_101 ) // 反射/映射      {          int delta = borderType == BORDER_REFLECT_101;          if( len == 1 )              return 0;          do          {              if( p < 0 )    // 反射:左边界或101:右边界                  p = -p - 1 + delta;              else                  p = len - 1 - (p - len) - delta;          }          while( (unsigned)p >= (unsigned)len );      }      else if( borderType == BORDER_WRAP )  // 包装      {          if( p < 0 )  // 左边界              p -= ((p-len+1)/len)*len;          if( p >= len )  // 右边界              p %= len;      }      else if( borderType == BORDER_CONSTANT )  // 常量,另外处理          p = -1;      else          CV_Error( CV_StsBadArg, "Unknown/unsupported border type" );      return p;  }   非常量类型边界扩展: [cpp] view plain copy static void copyMakeBorder_8u( const uchar* src, size_t srcstep, Size srcroi, // 原图 参数:数据,step,大小                                 uchar* dst, size_t dststep, Size dstroi,  // 目的图像参数                                 int top, int left, int cn, int borderType )  {      const int isz = (int)sizeof(int);      int i, j, k, elemSize = 1;      bool intMode = false;        if( (cn | srcstep | dststep | (size_t)src | (size_t)dst) % isz == 0 )      {          cn /= isz;          elemSize = isz;          intMode = true;      }        AutoBuffer<int> _tab((dstroi.width - srcroi.width)*cn);  // 大小是 扩展的左右边界之和,仅用于存放 扩展的边界 在原图中的位置      int* tab = _tab;      int right = dstroi.width - srcroi.width - left;      int bottom = dstroi.height - srcroi.height - top;            for( i = 0; i < left; i++ ) // 左边界      {          j = borderInterpolate(i - left, srcroi.width, borderType)*cn;  // 计算出原图中对应的位置          for( k = 0; k < cn; k++ )  // 每个通道的处理              tab[i*cn + k] = j + k;      }            for( i = 0; i < right; i++ )  // 右边界      {          j = borderInterpolate(srcroi.width + i, srcroi.width, borderType)*cn;          for( k = 0; k < cn; k++ )              tab[(i+left)*cn + k] = j + k;      }        srcroi.width *= cn;      dstroi.width *= cn;      left *= cn;      right *= cn;            uchar* dstInner = dst + dststep*top + left*elemSize;        for( i = 0; i < srcroi.height; i++, dstInner += dststep, src += srcstep ) // 从原图中复制数据到扩展的边界中      {          if( dstInner != src )              memcpy(dstInner, src, srcroi.width*elemSize);                    if( intMode )          {              const int* isrc = (int*)src;              int* idstInner = (int*)dstInner;              for( j = 0; j < left; j++ )                  idstInner[j - left] = isrc[tab[j]];              for( j = 0; j < right; j++ )                  idstInner[j + srcroi.width] = isrc[tab[j + left]];          }          else          {              for( j = 0; j < left; j++ )                  dstInner[j - left] = src[tab[j]];              for( j = 0; j < right; j++ )                  dstInner[j + srcroi.width] = src[tab[j + left]];          }      }            dstroi.width *= elemSize;      dst += dststep*top;            for( i = 0; i < top; i++ )  // 上边界      {          j = borderInterpolate(i - top, srcroi.height, borderType);          memcpy(dst + (i - top)*dststep, dst + j*dststep, dstroi.width); // 进行整行的复制      }            for( i = 0; i < bottom; i++ ) // 先边界      {          j = borderInterpolate(i + srcroi.height, srcroi.height, borderType);          memcpy(dst + (i + srcroi.height)*dststep, dst + j*dststep, dstroi.width); // 进行整行的复制      }  }   常量类型的扩展就更简单了: [cpp] view plain copy static void copyMakeConstBorder_8u( const uchar* src, size_t srcstep, Size srcroi,                                      uchar* dst, size_t dststep, Size dstroi,                                      int top, int left, int cn, const uchar* value )  {      int i, j;      AutoBuffer<uchar> _constBuf(dstroi.width*cn);      uchar* constBuf = _constBuf;      int right = dstroi.width - srcroi.width - left;      int bottom = dstroi.height - srcroi.height - top;            for( i = 0; i < dstroi.width; i++ ) // 初始化 常量buf的值      {          for( j = 0; j < cn; j++ )              constBuf[i*cn + j] = value[j];      }            srcroi.width *= cn;      dstroi.width *= cn;      left *= cn;      right *= cn;            uchar* dstInner = dst + dststep*top + left;            for( i = 0; i < srcroi.height; i++, dstInner += dststep, src += srcstep ) // 复制原图数据和扩展左右边界      {          if( dstInner != src )              memcpy( dstInner, src, srcroi.width );          memcpy( dstInner - left, constBuf, left );          memcpy( dstInner + srcroi.width, constBuf, right );      }            dst += dststep*top;            for( i = 0; i < top; i++ )          memcpy(dst + (i - top)*dststep, constBuf, dstroi.width); // 扩展上边界            for( i = 0; i < bottom; i++ ) // 扩展下边界          memcpy(dst + (i + srcroi.height)*dststep, constBuf, dstroi.width);  }  

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

    转载请注明原文地址: https://ju.6miu.com/read-5756.html

    最新回复(0)