【opencv学习笔记】017之边缘处理

目录

一、前言

二、边缘处理

1、为啥处理边缘

2、默认边缘处理

三、自定义边缘处理

1、讲解

2、API

3、borderInterpolate

4、代码展示

1.使用指定颜色

2.用已知边缘

3.使用另一边像素


一、前言

继续填坑。

如果想看其他有关于OpenCV学习方法介绍、学习教程、代码实战、常见报错及解决方案等相关内容,可以直接看我的OpenCV分类:

【OpenCV系列】:https://blog.csdn.net/shuiyixin/article/category/7581855

如果你想了解更多有关于计算机视觉、OpenCV、机器学习、深度学习等相关技术的内容,想与更多大佬一起沟通,那就扫描下方二维码加入我们吧!

二、边缘处理

1、为啥处理边缘

上节课我们讲到了卷积操作和自定义线性滤波,进行卷积操作之后,图像的行列就会减少,如果卷积核为3×3,那么图像的四周就会减少一像素的边缘,如果卷积核为(2*k+1)×(2*k+1),就会减少k像素的边缘。但是我们不希望图像像素减少,我们就需要对边缘做单独的处理,也就是需要自己单独设置边缘。

至于为什么卷积核为(2*k+1)×(2*k+1),就会减少k像素的边缘,我们看下图:

在该图中,我们取卷积核为5×5,这个时候,卷积核的中心距上下左右边缘的距离为2像素,也就是图像的边缘会有两个像素得不到处理,能处理的是红色位置(核中心)包围的正方形区域。

除此之外,我们可能还需要自己独立做一些边缘。比如我们需要给图像做边框,或者我们需要对图像进行边界填充等等。

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<iostream>
#include<opencv2/opencv.hpp>

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); 

 

三、自定义边缘处理

1、讲解

通过上面的讲解我们知道了为什么要进行边缘处理,也讲了默认的边缘处理方式,但是默认的边缘处理方式,只能解决由于卷积等操作导致的图片边缘无法处理问题,也就是说,单独的一个参数无法满足我们的需求,比如我们希望处理更多位置的边缘,不仅仅是未处理位置,如果默认的方式不能满足我们的要求,我们就需要自己自定义边缘处理方式。

2、API

我们用到如下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时的边界值

注:使用这种方式会修改图像的尺寸。

 

3、borderInterpolate

我们在上面提到了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
};

 

4、代码展示

原图

对于上面的原图,我们尝试使用不同的边缘处理方式来操作。

1.使用指定颜色

如果我们用指定的颜色像素来填充,那我们就需要使用 - BORDER_CONSTANT – 。我们在图像四周分别设置5像素的红色边缘。

#include<iostream>
#include<opencv2/opencv.hpp>

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_CONSTANT

2.用已知边缘

如果我们用已知边缘来填充,那我们就需要使用 - BORDER_REPLICATE – 。最后一个参数可以忽略,为了效果更加明显,我们将边缘设置的更大一些。

#include<iostream>
#include<opencv2/opencv.hpp>

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;
}

得到的结果如下,我们能明显看到,就是用已有的边界的值向四周填充:

 
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
BORDER_REPLICATE

3.使用另一边像素

如果我们用另一边像素来填充,那我们就需要使用 - BORDER_WRAP – 。同样最后一个参数忽略不写。

#include<iostream>
#include<opencv2/opencv.hpp>

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;
}

得到的结果如下:

 
wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==
BORDER_WRAP

大家也可以自己尝试一下呀,一定要多做练习!

发布了261 篇原创文章 · 获赞 524 · 访问量 52万+

猜你喜欢

转载自blog.csdn.net/shuiyixin/article/details/104454474
今日推荐