目录
一、前言
二、边缘处理
1、为啥处理边缘
2、默认边缘处理
三、自定义边缘处理
1、讲解
2、API
3、borderInterpolate
4、代码展示
1.使用指定颜色
2.用已知边缘
3.使用另一边像素
继续填坑。
如果想看其他有关于OpenCV学习方法介绍、学习教程、代码实战、常见报错及解决方案等相关内容,可以直接看我的OpenCV分类:
【OpenCV系列】:https://blog.csdn.net/shuiyixin/article/category/7581855
如果你想了解更多有关于计算机视觉、OpenCV、机器学习、深度学习等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!
上节课我们讲到了卷积操作和自定义线性滤波,进行卷积操作之后,图像的行列就会减少,如果卷积核为3×3,那么图像的四周就会减少一像素的边缘,如果卷积核为(2*k+1)×(2*k+1),就会减少k像素的边缘。但是我们不希望图像像素减少,我们就需要对边缘做单独的处理,也就是需要自己单独设置边缘。
至于为什么卷积核为(2*k+1)×(2*k+1),就会减少k像素的边缘,我们看下图:
在该图中,我们取卷积核为5×5,这个时候,卷积核的中心距上下左右边缘的距离为2像素,也就是图像的边缘会有两个像素得不到处理,能处理的是红色位置(核中心)包围的正方形区域。
除此之外,我们可能还需要自己独立做一些边缘。比如我们需要给图像做边框,或者我们需要对图像进行边界填充等等。
对于上面提到的由于卷积操作导致的边界无法处理的问题,我们可以使用上节课我们讲到了自定义线性滤波使用的的API的最后一个参数,borderType。通过这个参数,我们可以有一些比较常用的处理方式,常用的边界类型如下:
enum BorderTypes {
BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb`
BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg`
BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
BORDER_TRANSPARENT = 5, //!< `uvwxyz|absdefgh|ijklmno`
BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_ISOLATED = 16 //!< do not look outside of ROI
};
除了默认的BORDER_DEFAULT。最常用的还有:
- BORDER_CONSTANT – 填充边缘用指定像素值
- BORDER_REPLICATE – 填充边缘像素用已知的边缘像素值。
- BORDER_WRAP – 用另外一边的像素来补偿填充
我们可以通过如下代码去观看效果:
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat img, src, kernel;
img = imread("E:/image/girl2.png");
if (!img.data)
{
cout << "could not load image !";
return -1;
}
imshow("【输入图像】", img);
kernel = Mat::ones(Size(3, 3),CV_32F)/9;
//对于下面的函数,如果最后一个参数不写,就是默认为BORDER_DEFAULT。
filter2D(img, src, -1, kernel, Point(-1, -1),0);
//filter2D(img, src, -1, kernel, Point(-1, -1),0,BORDER_DEFAULT);
cout << kernel << endl;
imshow("【输出图像】", src);
waitKey(0);
return 0;
}
想观看不同的效果,我们需要修改如下函数:
//对于下面的函数,如果最后一个参数不写,就是默认为BORDER_DEFAULT。
filter2D(img, src, -1, kernel, Point(-1, -1),0);
//filter2D(img, src, -1, kernel, Point(-1, -1),0,BORDER_DEFAULT);
//filter2D(img, src, -1, kernel, Point(-1, -1),0,BORDER_CONSTANT);
//filter2D(img, src, -1, kernel, Point(-1, -1),0,BORDER_REPLICATE);
//filter2D(img, src, -1, kernel, Point(-1, -1),0,BORDER_WRAP);
通过上面的讲解我们知道了为什么要进行边缘处理,也讲了默认的边缘处理方式,但是默认的边缘处理方式,只能解决由于卷积等操作导致的图片边缘无法处理问题,也就是说,单独的一个参数无法满足我们的需求,比如我们希望处理更多位置的边缘,不仅仅是未处理位置,如果默认的方式不能满足我们的要求,我们就需要自己自定义边缘处理方式。
我们用到如下API:
void copyMakeBorder(
InputArray src,
OutputArray dst,
int top,
int bottom,
int left,
int right,
int borderType,
const Scalar& value = Scalar()
);
函数参数含义如下:
(1)InputArray类型的src ,输入图像。
(2)OutputArray类型的dst ,输出图像,图像的类型和输入图像相同,尺寸为:Size(src.cols+left+right, src.rows+top+bottom)。
(3)int类型的top,
(4)int类型的bottom,
(5)int类型的left,
(6)int类型的right,上面四个参数是图像要增加的边缘的大小。
(7)int类型的borderType,边缘的类型。参照 borderInterpolate。
(8)Scalar类型的value,borderType==BORDER_CONSTANT时的边界值
注:使用这种方式会修改图像的尺寸。
我们在上面提到了borderInterpolate,这个是一个函数,具体内容如下:
int borderInterpolate(int p, int len, int borderType);
函数参数含义如下:
@param borderType Border type, one of the cv::BorderTypes, except for cv::BORDER_TRANSPARENT and cv::BORDER_ISOLATED . When borderType==cv::BORDER_CONSTANT , the function always returns -1, regardless of p and len.
(1)int类型的p,沿其中一个轴外推像素的基于0的坐标,可能小于0或等于len。
(2)int类型的len,数组沿相应轴的长度。
(3)int类型的borderType,边缘的类型。除了cv::BORDER_TRANSPARENT 和 cv::BORDER_ISOLATED,当borderType==cv::BORDER_CONSTANT时,不管p和len为多少,函数总是返回-1。
而这里面提到的borderType,就是我们之前讲到的:
enum BorderTypes {
BORDER_CONSTANT = 0, //!< `iiiiii|abcdefgh|iiiiiii` with some specified `i`
BORDER_REPLICATE = 1, //!< `aaaaaa|abcdefgh|hhhhhhh`
BORDER_REFLECT = 2, //!< `fedcba|abcdefgh|hgfedcb`
BORDER_WRAP = 3, //!< `cdefgh|abcdefgh|abcdefg`
BORDER_REFLECT_101 = 4, //!< `gfedcb|abcdefgh|gfedcba`
BORDER_TRANSPARENT = 5, //!< `uvwxyz|absdefgh|ijklmno`
BORDER_REFLECT101 = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_DEFAULT = BORDER_REFLECT_101, //!< same as BORDER_REFLECT_101
BORDER_ISOLATED = 16 //!< do not look outside of ROI
};
对于上面的原图,我们尝试使用不同的边缘处理方式来操作。
如果我们用指定的颜色像素来填充,那我们就需要使用 - BORDER_CONSTANT – 。我们在图像四周分别设置5像素的红色边缘。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat img, src, kernel;
Scalar color = Scalar(0, 0, 255);
img = imread("E:/image/girl5.png");
if (!img.data)
{
cout << "could not load image !";
return -1;
}
imshow("【输入图像】", img);
copyMakeBorder(img,src,5,5,5,5, BORDER_CONSTANT,color);
imshow("【输出图像】", src);
waitKey(0);
return 0;
}
得到的结果如下:
如果我们用已知边缘来填充,那我们就需要使用 - BORDER_REPLICATE – 。最后一个参数可以忽略,为了效果更加明显,我们将边缘设置的更大一些。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat img, src, kernel;
Scalar color = Scalar(0, 0, 255);
img = imread("E:/image/girl5.png");
if (!img.data)
{
cout << "could not load image !";
return -1;
}
imshow("【输入图像】", img);
copyMakeBorder(img, src, 55, 55, 55, 55, BORDER_REPLICATE);
imshow("【输出图像】", src);
waitKey(0);
return 0;
}
得到的结果如下,我们能明显看到,就是用已有的边界的值向四周填充:
如果我们用另一边像素来填充,那我们就需要使用 - BORDER_WRAP – 。同样最后一个参数忽略不写。
#include
#include
using namespace std;
using namespace cv;
int main()
{
Mat img, src, kernel;
Scalar color = Scalar(0, 0, 255);
img = imread("E:/image/girl5.png");
if (!img.data)
{
cout << "could not load image !";
return -1;
}
imshow("【输入图像】", img);
copyMakeBorder(img, src, 55, 55, 55, 55, BORDER_WRAP);
imshow("【输出图像】", src);
waitKey(0);
return 0;
}
得到的结果如下:
大家也可以自己尝试一下呀,一定要多做练习!