OpenCV中提供的图像滤波边沿处理方式有:
//! Various border types, image boundaries are denoted with `|`
//各种边界类型,图像边界使用“|”作为标记
//! @see borderInterpolate, copyMakeBorder
enum BorderTypes {
//!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
BORDER_CONSTANT = 0,
//!< `aaaaaa|abcdefgh|hhhhhhh`
BORDER_REPLICATE = 1,
//!< `fedcba|abcdefgh|hgfedcb`
BORDER_REFLECT = 2,
//!< `cdefgh|abcdefgh|abcdefg`
BORDER_WRAP = 3,
//!< `gfedcb|abcdefgh|gfedcba`
BORDER_REFLECT_101 = 4,
//!< `uvwxyz|abcdefgh|ijklmno`
BORDER_TRANSPARENT = 5,
//!< same as BORDER_REFLECT_101
BORDER_REFLECT101 = BORDER_REFLECT_101,
//!< same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101,
//!< do not look outside of ROI
BORDER_ISOLATED = 16
};
图像边沿的填充相关方法
borderInterpolate
说明
计算外推像素的源位置。
当使用指定的外推边框模式时,该函数计算并返回与指定的外推像素对应的像素在原图中的坐标。例如,如果您在水平方向上使用cv :: BORDER_WRAP模式,而在垂直方向上使用cv :: BORDER_REFLECT_101,并要计算目标图像img中Point(-5,100)对应原图像的坐标,看起来像:float val = img.at<float>(borderInterpolate(100, img.rows, cv::BORDER_REFLECT_101),borderInterpolate(-5, img.cols, cv::BORDER_WRAP));
通常,不直接调用该函数。它在滤波函数以及copyMakeBorder中使用。
函数声明
int borderInterpolate(int p, int len, int borderType);
函数参数
p 沿一个轴的外推像素的从0开始的坐标,可能为<0或> = len len 数组沿相应组的长度 borderType 边框类型,一个BorderTypes,除了BORDER_TRANSPARENT和BORDER_ISOLATED。当borderType == BORDER_CONSTANT时,无论p和len如何,该函数始终返回-1。
copyMakeBorder
函数说明
函数将原图像赋值到目标图像中间。被复制的原图像的上,下,左,右区域将被填充像素值。
当src已经在dst中间时,函数支持该模式。在这种情况下,函数不会复制src本身,而只是构造边界。
当源图像是较大图像的一部分(roi)时,函数将尝试使用roi区域之外的像素来形成边框。若要禁用此功能并始终进行边框填充,就像src不是roi一样,使用bordertype函数声明区域从左到右,
void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value = Scalar() );
函数参数
src 原图像 dst 与src类型相同的目标图像,图像尺寸Size(src.cols+left+right,src.rows+top+bottom) top 上方像素 bottom 下方像素 left 左侧像素 right 右侧像素,指定源图像矩形中每个方向上的像素数的填充行数。例如,top=1,bottom=1,left=1,right=1表示1像素宽的边框需要被建造。 borderType 边框类型 value 当borderType值等于BORDER_CONSTANT时,指定边填充的值。
- 首先创建一个5×5的图像矩阵:
Mat myImage(Size(5, 5), CV_8UC1); myImage.at<uchar>(0, 0) = 1; myImage.at<uchar>(0, 1) = 2; myImage.at<uchar>(0, 2) = 1; myImage.at<uchar>(0, 3) = 4; myImage.at<uchar>(0, 4) = 3; myImage.at<uchar>(1, 0) = 1; myImage.at<uchar>(1, 1) = 3; myImage.at<uchar>(1, 2) = 2; myImage.at<uchar>(1, 3) = 3; myImage.at<uchar>(1, 4) = 4; myImage.at<uchar>(2, 0) = 5; myImage.at<uchar>(2, 1) = 2; myImage.at<uchar>(2, 2) = 6; myImage.at<uchar>(2, 3) = 8; myImage.at<uchar>(2, 4) = 8; myImage.at<uchar>(3, 0) = 5; myImage.at<uchar>(3, 1) = 5; myImage.at<uchar>(3, 2) = 7; myImage.at<uchar>(3, 3) = 0; myImage.at<uchar>(3, 4) = 8; myImage.at<uchar>(4, 0) = 5; myImage.at<uchar>(4, 1) = 6; myImage.at<uchar>(4, 2) = 7; myImage.at<uchar>(4, 3) = 8; myImage.at<uchar>(4, 4) = 9; Mat dstMyImage; cout <<endl<< " 原图像灰度值:" << endl; for (int i = 0; i < 5; i++) { const uchar* p = myImage.ptr<uchar>(i); cout << " "; for (int j = 0; j < 5; j++) { if (j <= 3) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }
- BORDER_CONSTANT = 0
需要设置borderValue指定i的值。
其实质就是使用指定的值“i”代替边沿像素值进行卷积核操作。cout << endl << endl << " 当borderTypes为BORDER_CONSTANT=0时:" << endl; //当borderValue取值为2 copyMakeBorder(myImage, dstMyImage, 3, 3, 3, 3, 0,2); for (int i = 0; i < dstMyImage.rows; i++) { cout << " "; const uchar* p = dstMyImage.ptr<uchar>(i); for (int j = 0; j < dstMyImage.cols; j++) { if (j < dstMyImage.cols - 1) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }
-
BORDER_REPLICATE = 1
复制最近的一行或一列像素并一直延伸至添加边缘的宽度或高度。这种方式也就是OpenCV中的中值滤波medianBlur采用的边界处理方式。
cout << endl << endl << " 当borderTypes为BORDER_REPLICATE=1时:" << endl; copyMakeBorder(myImage, dstMyImage, 3, 3, 3, 3, 1); for (int i = 0; i < dstMyImage.rows; i++) { cout << " "; const uchar* p = dstMyImage.ptr<uchar>(i); for (int j = 0; j < dstMyImage.cols; j++) { if (j < dstMyImage.cols - 1) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }
-
BORDER_REFLECT = 2
反射复制边界cout << endl << endl << " 当borderTypes为BORDER_REFLECT=2时:" << endl; copyMakeBorder(myImage, dstMyImage, 3, 3, 3, 3, 2); for (int i = 0; i < dstMyImage.rows; i++) { cout << " "; const uchar* p = dstMyImage.ptr<uchar>(i); for (int j = 0; j < dstMyImage.cols; j++) { if (j < dstMyImage.cols - 1) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }
发现上述结果不便于查找规律,扩大边界值:copyMakeBorder(myImage, dstMyImage, 5, 5, 5, 5, 2);
-
BORDER_WRAP = 3
cout << endl << endl << " 当borderTypes为BORDER_WRAP=3时:" << endl; copyMakeBorder(myImage, dstMyImage, 3, 3, 3, 3, 3); for (int i = 0; i < dstMyImage.rows; i++) { cout << " "; const uchar* p = dstMyImage.ptr<uchar>(i); for (int j = 0; j < dstMyImage.cols; j++) { if (j < dstMyImage.cols - 1) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }
将边界填充行扩大一些看,便于观察。copyMakeBorder(myImage, dstMyImage, 5, 5, 5, 5, 3);
-
BORDER_REFLECT_101 = 4
以边界为对称轴反射复制像素,也就是以最边缘像素为轴,对称。这种方式也是OpenCV边界处理的默认方式:(BORDER_DEFAULT=BORDER_REFLECT_101)
是filter2D,blur,GaussianBlur,bilateralFilter的默认处理方式,所以这种方式在边界处理中应用还是非常广泛的。
cout << endl << endl << " 当borderTypes为BORDER_REFLECT_101=4时:" << endl; copyMakeBorder(myImage, dstMyImage, 3, 3, 3, 3, 4); for (int i = 0; i < dstMyImage.rows; i++) { cout << " "; const uchar* p = dstMyImage.ptr<uchar>(i); for (int j = 0; j < dstMyImage.cols; j++) { if (j < dstMyImage.cols - 1) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }
将边界填充行扩大一些看,便于观察。
copyMakeBorder(myImage, dstMyImage, 5, 5, 5, 5, 4);
-
BORDER_TRANSPARENT = 5
cout << endl << endl << " 当borderTypes为BORDER_TRANSPARENT=5时:" << endl; copyMakeBorder(myImage, dstMyImage, 3, 3, 3, 3, 5); for (int i = 0; i < dstMyImage.rows; i++) { cout << " "; const uchar* p = dstMyImage.ptr<uchar>(i); for (int j = 0; j < dstMyImage.cols; j++) { if (j < dstMyImage.cols - 1) { cout << int(p[j]) << " , "; } else { cout << int(p[j]) << endl; } } }