图像轮廓与图像分割修复

寻找并绘制轮廓

寻找轮廓:findContours()

◆ findContours() [1/2]
void cv::findContours	(	InputOutputArray 	image,
OutputArrayOfArrays 	contours,
OutputArray 	hierarchy,
int 	mode,
int 	method,
Point 	offset = Point() 
)		

该函数用于在二值图像中寻找轮廓。

Parameters
image    Source, an 8-bit single-channel image. Non-zero pixels are treated as 1's. Zero pixels remain 0's, so the image is treated as binary . You can use cv::compare, cv::inRange, cv::threshold , cv::adaptiveThreshold, cv::Canny, and others to create a binary image out of a grayscale or color one. If mode equals to cv::RETR_CCOMP or cv::RETR_FLOODFILL, the input can also be a 32-bit integer image of labels (CV_32SC1).
contours    Detected contours. Each contour is stored as a vector of points (e.g. std::vector<std::vector<cv::Point> >).

contours参数表示被检测到的轮廓,每个轮廓被存储在一个点向量中。
hierarchy    Optional output vector (e.g. std::vector<cv::Vec4i>), containing information about the image topology. It has as many elements as the number of contours. For each i-th contour contours[i], the elements hierarchy[i][0] , hierarchy[i][1] , hierarchy[i][2] , and hierarchy[i][3] are set to 0-based indices in contours of the next and previous contours at the same hierarchical level, the first child contour and the parent contour, respectively. If for the contour i there are no next, previous, parent, or nested contours, the corresponding elements of hierarchy[i] will be negative.

hierarchy包含了图像的拓扑信息。每个轮廓contours[i]对应四个hierarchy元素hierarchy[i][0]~hierarchy[i][3],分别表示后一个轮廓,前一个轮廓,父轮廓,内嵌轮廓的索引编号。如果没有对应项,对应的hierachy值设置为复数。
mode    Contour retrieval mode, see cv::RetrievalModes轮廓检索模式

有如下几种方式:

RETR_EXTERNAL 
retrieves only the extreme outer contours. It sets hierarchy[i][2]=hierarchy[i][3]=-1 for all the contours.
RETR_LIST 
retrieves all of the contours without establishing any hierarchical relationships.
RETR_CCOMP 
retrieves all of the contours and organizes them into a two-level hierarchy. At the top level, there are external boundaries of the components. At the second level, there are boundaries of the holes. If there is another contour inside a hole of a connected component, it is still put at the top level.
RETR_TREE 
retrieves all of the contours and reconstructs a full hierarchy of nested contours.

补充:对于hierarchy参数的更多内容可以参考https://docs.opencv.org/3.1.0/d9/d8b/tutorial_py_contours_hierarchy.html
method    Contour approximation method, see cv::ContourApproximationModes轮廓近似办法。

CHAIN_APPROX_NONE 
stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is, max(abs(x1-x2),abs(y2-y1))==1.
CHAIN_APPROX_SIMPLE 
compresses horizontal, vertical, and diagonal segments and leaves only their end points. For example, an up-right rectangular contour is encoded with 4 points.

offset    Optional offset by which every contour point is shifted. This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context.每个轮廓点的可选偏移量,有默认值Point()。对ROI图像中找出的轮廓,并要在整个图像中进行分析时,这个参数便可派上用场。

绘制轮廓:drawContours()

◆ drawContours()
void cv::drawContours	(	InputOutputArray 	image,
InputArrayOfArrays 	contours,
int 	contourIdx,
const Scalar & 	color,
int 	thickness = 1,
int 	lineType = LINE_8,
InputArray 	hierarchy = noArray(),
int 	maxLevel = INT_MAX,
Point 	offset = Point() 
)		

与findContours一起使用可以将找到的轮廓绘制出来。

Parameters
image    Destination image.
contours    All the input contours. Each contour is stored as a point vector.
contourIdx    Parameter indicating a contour to draw. If it is negative, all the contours are drawn.
color    Color of the contours.绘制轮廓时使用的颜色
thickness    Thickness of lines the contours are drawn with. If it is negative (for example, thickness=CV_FILLED ), the contour interiors are drawn.轮廓线条的粗细,默认值为1.如果为复数(for example, thickness=CV_FILLED ),变会绘制在轮廓内部。
lineType    Line connectivity. See cv::LineTypes.线性,默认值为8.
hierarchy    Optional information about hierarchy. It is only needed if you want to draw only some of the contours (see maxLevel ).


maxLevel    Maximal level for drawn contours. If it is 0, only the specified contour is drawn. If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This parameter is only taken into account when there is hierarchy available.
offset    Optional contour shift parameter. Shift all the drawn contours by the specified offset=(dx,dy) .

示例程序:

#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
int main()
{
	Mat srcImage = imread("1.jpg", 0);
	imshow("原始图", srcImage);

	Mat dstImage = Mat::zeros(srcImage.rows, srcImage.cols, CV_8UC3);
	srcImage = srcImage > 150;//取阈值大于150的部分
	imshow("阈值化后的原始图", srcImage);
	
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;//四维整型向量
	findContours(srcImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);//寻找所有轮廓
	
	int index = 0;
	for (; index >= 0; index = hierarchy[index][0])
	{
		Scalar color(rand() & 255, rand()&255, rand() & 255);
		drawContours(dstImage, contours, index, color, 1, 8, hierarchy);
		
	}
	imshow("轮廓图", dstImage);
	waitKey();
	destroyAllWindows();
	return 0;
}

寻找物体的凸包

简单的来说,如果给定二维平面上的点集,凸包就是将最外层的点连接起来的凸多边形。

寻找凸包:convexhull()函数

◆ convexHull()
void cv::convexHull	(	InputArray 	points,
OutputArray 	hull,
bool 	clockwise = false,
bool 	returnPoints = true 
)		

Finds the convex hull of a point set.该函数的功能是找出一个点集的凸包。

Parameters
points    Input 2D point set, stored in std::vector or Mat.存储在vector或者Mat中的二维点集。
hull    Output convex hull. It is either an integer vector of indices or vector of points. In the first case, the hull elements are 0-based indices of the convex hull points in the original array (since the set of convex hull points is a subset of the original point set). In the second case, hull elements are the convex hull points themselves.输出的凸包。
clockwise    Orientation flag. If it is true, the output convex hull is oriented clockwise. Otherwise, it is oriented counter-clockwise. The assumed coordinate system has its X axis pointing to the right, and its Y axis pointing upwards.

方向标识。当此标识符为真时,输出的凸包为顺时针方向,否则为逆时针方向。并假定坐标轴X轴指向右,Y轴指向上。
returnPoints    Operation flag. In case of a matrix, when the flag is true, the function returns convex hull points. Otherwise, it returns indices of the convex hull points. When the output array is std::vector, the flag is ignored, and the output depends on the type of the vector: std::vector<int> implies returnPoints=false, std::vector<Point> implies returnPoints=true.

操作标识符。默认值为图true,当标识符为真时,函数返回各个点的凸包。否则返回凸包各点的索引。当输出的数组是三通道std::vector时,此标志会被忽略。
Note
points and hull should be different arrays, inplace processing isn't supported.点集和凸包是不同的向量组,不支持就地操作。
Examples:
convexhull.cpp

#include <opencv2\opencv.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;

int main()
{
	Mat image(600, 600, CV_8UC3);//初始化一张画布,大小为600*600,无符号的
	RNG& rng = theRNG();//生成一个随机数生成器
	while (1)
	{
		char key;
		int count = (unsigned)rng % 100 + 3;//随机生成点的数量,若这里定义成int型下面执行会发生溢出
		vector<Point> points;//定义一个Point类型的点的集合
							 //生成随机点的坐标
		for (int i = 0; i < count; i++)
		{
			Point point;
			point.x = rng.uniform(image.cols / 4, image.cols * 3 / 4);//注意不要写成image.cols*(3/4)
			point.y = rng.uniform(image.rows / 4, image.rows * 3 / 4);
			points.push_back(point);//把新生成的点追加到points这个点集合的末尾
		}
		//检测凸包
		vector<int> hull;
		convexHull(Mat(points), hull, true);//输入一个n*2的矩阵

		image = Scalar::all(0);//给随机的坐标点上色
		for (int i = 0; i < count; i++)
			circle(image, points[i], 3, 
				Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255))
				, FILLED, LINE_AA);

		int hullcount = (int)hull.size();
		Point point0 = points[hull[hullcount - 1]];

		for (int i = 0; i < hullcount; i++)
		{
			Point point = points[hull[i]];
			line(image, point0, point, Scalar(255, 255, 255), 2, LINE_AA);
			point0 = point;
		}
		imshow("凸包检测", image);
		key = (char)waitKey();
		if (key == 27)
			break;
	}
	destroyAllWindows();
	return 0;

}

寻找物体的凸包:先找到物体的轮廓在找出物体的凸包。

示例:

#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

#define WINDOW_NAME1 "原图窗口"
#define WINDOW_NAME2 "阈值划分"
#define WINDOW_NAME3 "凸包检测"

Mat srcImage;
Mat grayImage;
int Thresh = 120;
int maxThresh = 255;
RNG rng(12345);
Mat thresholdImage;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;

void on_ThreshChange(int, void*);

int main()
{
	srcImage = imread("1.png");
	cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
	blur(grayImage, grayImage, Size(3, 3));
	namedWindow(WINDOW_NAME1,WINDOW_AUTOSIZE);
	imshow(WINDOW_NAME1, srcImage);
	createTrackbar("阈值", WINDOW_NAME1, &Thresh, maxThresh, on_ThreshChange);
	on_ThreshChange(0, 0);
	waitKey();
	destroyAllWindows();
	return 0;
}

void on_ThreshChange(int, void*)
{
	threshold(grayImage, thresholdImage, Thresh, 255, THRESH_BINARY);
	findContours(thresholdImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
	vector<vector<Point> >hull(contours.size());
	for ( int i = 0; i < contours.size(); i++)
	{
		convexHull(Mat(contours[i]), hull[i], false);
	}

	Mat drawing = Mat::zeros(thresholdImage.size(), CV_8UC3);

	for(int i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(drawing, contours, i, color, 1, 8, vector<Vec4i>(), 0, Point());
		drawContours(drawing, hull, i, color, 1, 8, vector<Vec4i>(), 0, Point());
	}

	imshow(WINDOW_NAME2, thresholdImage);
	imshow(WINDOW_NAME3, drawing);
}

使用多边形将轮廓包围

最小包围矩形minAreaRect():此函数对于给定的二维点集,寻找可旋转的最小面积的包围矩形。

minAreaRect()
RotatedRect cv::minAreaRect	(	InputArray 	points	)	

Parameters
points    Input vector of 2D points, stored in std::vector<> or Mat

示例:

#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	Mat image(600, 600, CV_8UC3);
	RNG& rng = theRNG();
	while (1)
	{
		int count = rng.uniform(3, 103);
		vector<Point> points;
		
		for (int i = 0; i < count; i++)
		{
			Point point;
			point.x = rng.uniform(image.cols / 4, image.cols * 3 / 4);
			point.y = rng.uniform(image.rows / 4, image.rows * 3 / 4);
			points.push_back(point);
		}
		RotatedRect box = minAreaRect(Mat(points));
		Point2f vertex[4];//用于接收矩形的四个顶点
		box.points(vertex);//返回矩形的四个顶点到vertex中
        //points()用于返回矩形的四个顶点
		image = Scalar::all(0);
		for (int i = 0; i < count; i++)
			circle(image, points[i], 3, Scalar(rng.uniform(0, 255), rng.uniform(0, 255),
				rng.uniform(0, 255)), FILLED, LINE_AA);

		for (int i = 0; i < 4; i++)
			line(image, vertex[i], vertex[(i + 1) % 4], Scalar(100, 200, 211),
				2, LINE_AA);
		
		imshow("矩形包围示例", image);
		char key = (char)waitKey();
		if (key == 27 || key == 'q' || key == 'Q')
			break;
		}
	destroyAllWindows();
	return 0;
	}

寻找最小包围圆形minEnclosingCircle()函数:对于给定的点集,寻找最小面积的包围圆。

示例:

#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
	Mat image(600, 600, CV_8UC3);
	RNG& rng = theRNG();
	while (1)
	{
		int count = rng.uniform(3, 103);
		vector<Point> points;

		for (int i = 0; i < count; i++)
		{
			Point point;
			point.x = rng.uniform(image.cols / 4, image.cols * 3 / 4);
			point.y = rng.uniform(image.rows / 4, image.rows * 3 / 4);
			points.push_back(point);
		}
		Point2f center;
		float radius = 0;
		minEnclosingCircle(Mat(points), center, radius);

		image = Scalar::all(0);
		for (int i = 0; i < count; i++)
			circle(image, points[i], 3, Scalar(rng.uniform(0, 255), rng.uniform(0, 255),
				rng.uniform(0, 255)), FILLED, LINE_AA);

		for (int i = 0; i < 4; i++)
			circle(image, center, cvRound(radius), Scalar(100, 200, 211),
				2, LINE_AA);

		imshow("圆形包围示例", image);
		char key = (char)waitKey();
		if (key == 27 || key == 'q' || key == 'Q')
			break;
	}
	destroyAllWindows();
	return 0;
}

综合示例:

#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;
using namespace std;

#define WINDOW_NAME1 "原始窗口"
#define WINDOW_NAME2 "效果窗口"

Mat srcImage;
Mat grayImage;
int thresh = 120;
int maxThresh = 255;
RNG rng(123450);

void on_ContoursChange(int, void*);

int main()
{
	system("color 1A");

	srcImage = imread("1.png");
	if (!srcImage.data)
	{
		printf("error!");
		return false;
	}
	cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);
	blur(grayImage, grayImage, Size(3, 3));

	namedWindow(WINDOW_NAME1, WINDOW_AUTOSIZE);
	imshow(WINDOW_NAME1, srcImage);

	createTrackbar("阈值:", WINDOW_NAME1, &thresh, maxThresh, on_ContoursChange);
	on_ContoursChange(0, 0);

	waitKey(0);
	destroyAllWindows();
	return 0;
}

void on_ContoursChange(int, void*)
{
	Mat threshold_output;
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	threshold(grayImage, threshold_output, thresh, 255, THRESH_BINARY);//阈值划分

	//找出轮廓
	findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

	//多边形逼近轮廓+矩形和圆形的边界
	vector<vector<Point> > contours_poly(contours.size());
	vector<Rect> boundRect(contours.size());
	vector<Point2f> center(contours.size());
	vector<float> radius(contours.size());

	for (int i = 0; i < contours.size(); i++)
	{
		approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);//用指定精度逼近多边形曲线
		boundRect[i] = boundingRect(Mat(contours_poly[i]));//计算并返回一个边界矩形
		minEnclosingCircle(Mat(contours_poly[i]), center[i], radius[i]);//最小面积圆
	}
	
	Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);

	for (int i = 0; i < contours.size(); i++)
	{
		Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point());
		rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);//第i个轮廓的矩形边界
		circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);//第i个轮廓的圆形边界
	}

	namedWindow(WINDOW_NAME2, WINDOW_AUTOSIZE);
	imshow(WINDOW_NAME2, drawing);

}

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值