说到OpenCV的边界处理,少不了要先了解下什么是滤波器。很多处理算法其实都可以归为滤波操作,几乎所有的滤波都涉及到图像边界问题。

滤波器和卷积两个术语可以认为是可以互换的。信号处理倾向于使用过滤器这个名称,而数学界倾向于使用内核。经常出现的非线性核的一个例子是中值滤波器,它用内核中间的值取代了像素邻域内的值。

滤波器有很多种形状。使用最普遍的滤波器其实就是一个小的二维矩阵,用这种方式表示的滤波器也称为卷积。图1是图形方式表示的部分内核。




opencv画不填充矩形 opencv边界填充_OpenCV


(A)5×5矩形内核,(B)归一化5×5矩形内核,(C)3×3 (D)5×5归一化高斯内核

图1。

滤波器与原图进行运算,就是将滤波器覆盖在原图上,与图像上对应位置先求积再求所有值之和。将滤波器在原图上移动,就得到每个位置的滤波结果。每个滤波器或内核都有一个锚点,表明滤波器如何与原图像对齐的位置,也就是要替换原图像对应锚点位置的值。

OpenCV在图像处理方式时,一个的问题是如何处理边界。几乎所有的滤波或卷积都涉及到边界问题,如blur(), erode(), dilate()等函数。OpenCV的滤波操作会生成与输入大小相同的输出图像。为了达到这个结果,OpenCV在边界图像之外创建“虚拟”像素。对于 blur()这样的操作是必需的,它将获取某个点附近的所有像素,并对它们进行平均以确定该点的新值。

OpenCV大部分库函数都会创建边界虚拟像素。在这种情况下,只需要告诉特定的函数,你想如何创建这些像素。实现该功能的函数为copyMakeBorder()。想要放大的图像,可以用copyMakeBorder()来以某种方式填充较大图像中的所有像素。

void cv::copyMakeBorder(

cv::InputArray src, // Input image

cv::OutputArray dst, // Result image

int top, // Top side padding (pixels)

int bottom, // Bottom side padding (pixels)

int left, // Left side padding (pixels)

int right, // Right side padding (pixels)

int borderType, // Pixel extrapolation method

const cv::Scalar& value = cv::Scalar() // Used for constant borders

);

以上是copyMakeBorder()函数的定义。前两个参数是较小的原图像和较大的目标图像。接下来的四个参数指定将多少个像素添加到顶部,底部,左侧和右侧边缘的原图像中。下一个参数borderType告诉copyMakeBorder()如何填充边界像素。

例1 copyMakeBorder()函数的使用方法示意


#include "opencv2/opencv.hpp"
#include <time.h>
using namespace cv;
using namespace std;
 
int main(int, char** argv)
{
 Mat src = imread("E: /border.jpg", 1);
 namedWindow("src", 0);
 imshow("src", src);
 Mat dst;
 copyMakeBorder(src, dst, 50, 50, 50, 50, BORDER_CONSTANT);
 namedWindow("BORDER_CONSTANT", 0);
 imshow("BORDER_CONSTANT", dst);
 copyMakeBorder(src, dst, 50, 50, 50, 50, BORDER_REPLICATE);
 namedWindow("BORDER_REPLICATE", 0);
 imshow("BORDER_REPLICATE", dst);
 copyMakeBorder(src, dst, 50, 50, 50, 50, BORDER_REFLECT);
 namedWindow("BORDER_REFLECT", 0);
 imshow("BORDER_REFLECT", dst);
 copyMakeBorder(src, dst, 50, 50, 50, 50, BORDER_WRAP);
 namedWindow("BORDER_WRAP", 0);
 imshow("BORDER_WRAP", dst);
 copyMakeBorder(src, dst, 50, 50, 50, 50, BORDER_REFLECT_101);
 namedWindow("BORDER_REFLECT_101", 0);
 imshow("BORDER_REFLECT_101", dst);
 copyMakeBorder(src, dst, 50, 50, 50, 50, BORDER_DEFAULT);
 namedWindow("BORDER_DEFAULT", 0);
 imshow("BORDER_DEFAULT", dst);
 waitKey(0);
 return 0;
}


opencv画不填充矩形 opencv边界填充_opencv画不填充矩形_02


图2 原图


opencv画不填充矩形 opencv边界填充_OpenCV_03


图3。copyMakeBorder()提供的六个不同边界的图像

每种选项是完全不同的。第一个选项是BORDER_CONSTANT,它将边框区域中的所有像素设置为某个固定值。该值由copyMakeBorder()的值参数设置。在图2中,这个值是Scalar(0,0,0)。下一个选项是BORDER_WRAP,将每个像素分配一个距离从图像的边缘到边缘距离为n的像素值。复制选项BORDER_REPLICATE将从边缘开始的每个像素设置为该边缘上的像素相同的值。最后,有两种稍微不同形式: BORDER_REFLECT和BORDER_REFLECT_101。第一个分配距离图像边缘距离为n的每个像素是距同一边缘距离为n的像素值。相比之下BORDER_REFLECT_101将距离图像边缘距离为n的每个像素分配距该相同边缘距离为n + 1的像素的值。在大多数情况下, BORDER_REFLECT_101是OpenCV方法的默认选择。BORDER_DEFAULT的值为BORDER_REFLECT_101。表1是这些选项列表。

表1。 可用于copyMakeBorder()的borderType选项

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

BORDER_CONSTANT

BORDER_WRAP

BORDER_REPLICATE

BORDER_REFLECT

BORDER_REFLECT_101

BORDER_DEFAULT

在某些情况下,需要计算特定边缘像素所参照的参考像素的位置。 例如,给定宽度w和高度h的图像,可能想知道该图像中的哪个像素正在用于将值分配给虚拟像素(w + dx,h + dy)。 这个操作本质上是边界插值,函数为borderInterpolate():

int cv::borderInterpolate( // Returns coordinate of "donor" pixel

int p, // 0-based coordinate of extrapolated pixel

int len, // Length of array (on relevant axis)

int borderType // Pixel extrapolation method

);

这个函数通常在OpenCV内部使用(例如,在copy MakeBorder中),但它也可以在自己的算法中使用。 borderType的可能值与copyMakeBorder使用的值完全相同。