1. 程式人生 > >影象輪廓與影象分割修復

影象輪廓與影象分割修復

尋找並繪製輪廓

尋找輪廓: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);

}