OpenCV 輪廓檢測

讀入彩色3通道圖像,轉換成灰度圖像,再轉換成二值圖像,完後檢測輪廓。html

 

// cvtcolor.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include <iostream>



#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#pragma comment(lib, "opencv_highgui2410d.lib")
#pragma comment(lib, "opencv_core2410d.lib")
#pragma comment(lib, "opencv_imgproc2410d.lib")

using namespace cv;
using namespace std;

int main()
{
	string image_name = "swan.jpg";

	Mat src = imread(image_name);
	imshow("src",src);
				
	Mat gray(src.size(),CV_8U);
	cvtColor(src,gray,CV_BGR2GRAY);//轉換成灰度圖

	imshow("gray",gray);

	threshold(gray,gray,128,255,THRESH_BINARY);//轉換成2值圖像
	imshow("binary",gray);

	/
	std::vector<std::vector<cv::Point>> contours;
	cv::findContours(gray, 
		contours, // a vector of contours 
		CV_RETR_EXTERNAL, // retrieve the external contours
		CV_CHAIN_APPROX_NONE); // retrieve all pixels of each contours

	// Print contours' length
	std::cout << "Contours: " << contours.size() << std::endl;
	std::vector<std::vector<cv::Point>>::const_iterator itContours= contours.begin();
	for ( ; itContours!=contours.end(); ++itContours) 
	{

		std::cout << "Size: " << itContours->size() << std::endl;
	}

	// draw black contours on white image
	cv::Mat result(gray.size(),CV_8U,cv::Scalar(255));
	cv::drawContours(result,contours,
		-1, // draw all contours
		cv::Scalar(0), // in black
		2); // with a thickness of 2

	cv::namedWindow("Contours");
	cv::imshow("Contours",result);


	// draw contours on the original image
	cv::Mat original= cv::imread(image_name);
	cv::drawContours(original,contours,
		-1, // draw all contours
		cv::Scalar(255,255,255), // in white
		-1); // with a thickness of 2

	cv::namedWindow("Contours on Animals");
	cv::imshow("Contours on Animals",original);

	// Let's now draw black contours on white image
	result.setTo(cv::Scalar(255));
	cv::drawContours(result,contours,
		-1, // draw all contours
		cv::Scalar(0), // in black
		-1); // with a thickness of 1
	//image= cv::imread("test.png",0);




	waitKey(0);

	return 0;
}


 

 

 

實現效果:ios

 

 

 

 

添加代碼只顯示不大不小的輪廓:c++

//除去太長或者過短的輪廓   
	int cmin = 10;   
	int cmax = 500000;   
	vector<std::vector<cv::Point>>::iterator itc = contours.begin();   
	while(itc != contours.end())   
	{   
		if(itc->size() < cmin || itc->size() > cmax)   
			itc = contours.erase(itc);   
		else   
			++itc;   

	}


 

 

 

其餘相關的一些說明:算法

http://blog.sina.com.cn/s/blog_8fc98fe501017ypb.html數組

先看提取輪廓的代碼:app

  1. Mat image imread("D:/picture/images/binaryGroup.bmp",0);  
  2. if(!image.data)  
  3.     return -1;  
  4. imshow("源圖像",image);  
  5.   
  6. //獲取輪廓  
  7. std::vector> contours;  
  8. //獲取輪廓:  
  9. findContours(image,         //圖像  
  10.     contours,               //輪廓點  
  11.                     //包含圖像拓撲結構的信息(可選參數,這裏沒有選)  
  12.     CV_RETR_EXTERNAL,           //獲取輪廓的方法(這裏獲取外圍輪廓)  
  13.     CV_CHAIN_APPROX_NONE);      //輪廓近似的方法(這裏不近似,獲取所有輪廓)  
  14. //打印輪廓信息  
  15. std::cout<<"共有外圍輪廓:"<<contours.size()<<"條"<<std::endl;  
  16. std::vector>::const_iterator itContours contours.begin();  
  17. for(;itContours != contours.end();++itContours)  
  18.  
  19.     std::cout<<"每一個輪廓的長度: "<<itContours->size()<<std::endl;  
  20.  

注意到輪廓的存儲格式爲std::vector>,他說明整個輪廓是若干條輪廓按必定順序組成的,而每一個輪廓中的點也是有順序的。函數

畫出輪廓就比較簡單了:ui

  1. //畫出輪廓  
  2. Mat result(image.size(),CV_8U,Scalar(255));  
  3. //畫出輪廓,參數爲:畫板,輪廓,輪廓指示(這裏畫出全部輪廓),顏色,線粗  
  4. drawContours(result,contours,-1,Scalar(0),2);  
  5. imshow("提取外圍輪廓",result);  

還要注意提取輪廓的方法還有不少種,好比CV_RETR_LIST表明全部輪廓spa

  1. findContours(image,         //圖像  
  2.     contours,               //輪廓點  
  3.                     //包含圖像拓撲結構的信息(可選參數,這裏沒有選)  
  4.     CV_RETR_LIST,           //獲取輪廓的方法(這裏獲取全部輪廓)  
  5.     CV_CHAIN_APPROX_NONE);      //輪廓近似的方法(這裏不近似,獲取所有輪廓  
  6. //畫出輪廓  
  7. drawContours(result,contours,-1,Scalar(0),2);  
  8. imshow("提取全部輪廓",result);  

一般,這樣提取的輪廓包含一些咱們不但願的輪廓(好比一些小洞),或者假如咱們知道咱們感興趣的物體輪廓的大概範圍時,咱們就能夠用下面的辦法縮小目標範圍:.net

  1. //除去太長或者過短的輪廓  
  2. int cmin 100;  
  3. int cmax 1000;  
  4. std::vector>::const_iterator itc contours.begin();  
  5. while(itc != contours.end())  
  6.  
  7.     if(itc->size() cmin || itc->size() cmax)  
  8.         itc contours.erase(itc);  
  9.     else  
  10.         ++itc;  

  11.  




  12.   
  13. //把結果畫在源圖像上:  
  14. Mat original imread("D:/picture/images/group.jpg");  
  15. if(!original.data)  
  16.     return -1;  
  17. drawContours(original,contours,-1,Scalar(255,255,255),2);  
  18. imshow("動物的輪廓",original);  
  19.   
  20. //將輪廓重繪於白板上  
  21. result.setTo(Scalar(255));  
  22. drawContours(result,contours,-1,Scalar(0),1);  


怎麼提取輪廓的特徵呢?OpenCV提供了不少函數,咱們展現其中的幾個:

  1. //輪廓的形狀描述子  
  2. //外接矩形  
  3. Rect r0 boundingRect(Mat(contours[0]));  
  4. rectangle(result,r0,Scalar(0),2);  
  5.   
  6. //最小外接圓  
  7. float radius;  
  8. Point2f center;  
  9. minEnclosingCircle(Mat(contours[1]),center,radius);  
  10. circle(result,Point(center),static_cast<</span>int>(radius),Scalar(0),2);  
  11.   
  12. //多邊形估計  
  13. std::vector poly;  
  14. //參數爲:輸入圖像的2維點集,輸出結果,估計精度,是否閉合  
  15. approxPolyDP(Mat(contours[2]),poly,5,true);  
  16. std::cout<<"多邊形大小:"<<poly.size()<<std::endl;  
  17. //畫出結果  
  18. std::vector::const_iterator itp poly.begin();  
  19. while(itp != poly.end()-1)  
  20.  
  21.     line(result,*itp,*(itp+1),Scalar(0),2);  
  22.     ++itp;  
  23.  
  24. //將第一個點和最後一點連起來  
  25. line(result,*(poly.begin()),*(poly.end()-1),Scalar(128),2);  
  26.   
  27.   
  28. //計算凸包  
  29. std::vector hull;  
  30. convexHull(Mat(contours[3]),hull);  
  31. std::vector::const_iterator it= hull.begin();  
  32. while(it != (hull.end()-1))  
  33.  
  34.     line(result,*it,*(it+1),Scalar(0),2);  
  35.     ++it;  
  36.  
  37. line(result,*(hull.begin()),*(hull.end()-1),Scalar(0),2);  
  38.   
  39.   
  40. //計算矩信息  
  41. itc contours.begin();  
  42. while(itc != contours.end())  
  43.  
  44.     //計算全部的距  
  45.     Moments mom moments(Mat(*itc++));  
  46.     //計算並畫出質心  
  47.     circle(result,Point(mom.m10/mom.m00,mom.m01/mom.m00),2,Scalar(2),2);  
  48.  
  49. imshow("形狀描述子",result);  

咱們再次看到,輪廓的確是有順序的。值得注意的是矩信息:OpenCV提供了一個結構體Moments,它的元素就是計算好的矩信息,裏面存放了經常使用的距。

其實,OpenCV還提供了許多其餘的形狀描述子,好比函數cv::minAreaRect計算了最小外界傾斜的矩形。函數 cv::contourArea估計輪廓區域的面積(裏面的像素數)。函數cv::pointPolygonTest計算一個點是否在輪廓內,cv::matchShapes測量了2兩個輪廓的類似程度等等。這裏就不一一介紹了。

 

findContours函數,這個函數的原型爲:

void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierar-
chy, int mode, int method, Point offset=Point())

參數說明

輸入圖像image必須爲一個2值單通道圖像

contours參數爲檢測的輪廓數組,每個輪廓用一個point類型的vector表示

hiararchy參數和輪廓個數相同,每一個輪廓contours[ i ]對應4個hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,若是沒有對應項,該值設置爲負數。

mode表示輪廓的檢索模式

CV_RETR_EXTERNAL表示只檢測外輪廓

CV_RETR_LIST檢測的輪廓不創建等級關係

CV_RETR_CCOMP創建兩個等級的輪廓,上面的一層爲外邊界,裏面的一層爲內孔的邊界信息。若是內孔內還有一個連通物體,這個物體的邊界也在頂層。

CV_RETR_TREE創建一個等級樹結構的輪廓。具體參考contours.c這個demo

method爲輪廓的近似辦法

CV_CHAIN_APPROX_NONE存儲全部的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1

CV_CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來保存輪廓信息

CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

offset表示表明輪廓點的偏移量,能夠設置爲任意值。對ROI圖像中找出的輪廓,並要在整個圖像中進行分析時,這個參數仍是頗有用的。

findContours後會對輸入的2值圖像改變,因此若是不想改變該2值圖像,需建立新mat來存放,findContours後的輪廓信息contours可能過於複雜不平滑,能夠用approxPolyDP函數對該多邊形曲線作適當近似

contourArea函數能夠獲得當前輪廓包含區域的大小,方便輪廓的篩選

findContours常常與drawContours配合使用,用來將輪廓繪製出來。其中第一個參數image表示目標圖像,第二個參數contours表示輸入的輪廓組,每一組輪廓由點vector構成,第三個參數contourIdx指明畫第幾個輪廓,若是該參數爲負值,則畫所有輪廓,第四個參數color爲輪廓的顏色,第五個參數thickness爲輪廓的線寬,若是爲負值或CV_FILLED表示填充輪廓內部,第六個參數lineType爲線型,第七個參數爲輪廓結構信息,第八個參數爲maxLevel

獲得了複雜輪廓每每不適合特徵的檢測,這裏再介紹一個點集凸包絡的提取函數convexHull,輸入參數就能夠是contours組中的一個輪廓,返回外凸包絡的點集

還能夠獲得輪廓的外包絡矩形,使用函數boundingRect,若是想獲得旋轉的外包絡矩形,使用函數minAreaRect,返回值爲RotatedRect;也能夠獲得輪廓的外包絡圓,對應的函數爲minEnclosingCircle;想獲得輪廓的外包絡橢圓,對應的函數爲fitEllipse,返回值也是RotatedRect,能夠用ellipse函數畫出對應的橢圓

若是想根據多邊形的輪廓信息獲得多邊形的多階矩,可使用類moments,這個類能夠獲得多邊形和光柵形狀的3階之內的全部矩,類內有變量m00,m10,m01,m20,m11,m02,m30,m21,m12,m03,好比多邊形的質心爲 x = m10 / m00,y = m01 / m00。

若是想得到一點與多邊形封閉輪廓的信息,能夠調用pointPolygonTest函數,這個函數返回值爲該點距離輪廓最近邊界的距離,爲正值爲在輪廓內部,負值爲在輪廓外部,0表示在邊界上。

 

轉自:http://blog.sina.com.cn/s/blog_662c78590100z0rg.html

 

 

 

static int getContoursByCplus(char* Imgname, double minarea, double whRatio)
{
	cv::Mat src, dst, canny_output;
	/// Load source image and convert it to gray
	src = imread(Imgname, 0);

	if (!src.data)
	{
		std::cout << "read data error!" << std::endl;
		return -1;
	}
	blur(src, src, Size(3, 3));

	
	//the pram. for findContours,
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	/// Detect edges using canny
	Canny(src, canny_output, 80, 255, 3);
	/// Find contours
	findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
	//CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE

	double maxarea = 0;
	int maxAreaIdx = 0;

	for (int i = 0; i<contours.size(); i++)
	{

		double tmparea = fabs(contourArea(contours[i]));
		if (tmparea>maxarea)
		{
			maxarea = tmparea;
			maxAreaIdx = i;
			continue;
		}
		
		if (tmparea < minarea)
		{
			//刪除面積小於設定值的輪廓
			contours.erase(contours.begin() + i); 
			std::wcout << "delete a small area" << std::endl;
			continue;
		}
		//計算輪廓的直徑寬高
		Rect aRect =boundingRect(contours[i]);
		if ((aRect.width / aRect.height)<whRatio)
		{
			//刪除寬高比例小於設定值的輪廓
			contours.erase(contours.begin() + i); 
			std::wcout << "delete a unnomalRatio area" << std::endl;
			continue;
		}
	}
	/// Draw contours,彩色輪廓
	dst= Mat::zeros(canny_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(dst, contours, i, color, 2, 8, hierarchy, 0, Point());
	}
	// Create Window
	char* source_window = "countors";
	namedWindow(source_window, CV_WINDOW_NORMAL);
	imshow(source_window, dst);
	cv:; waitKey(0);
	
	return 0;
}


 


cvDrawContours(gray_image,c,cvScalarAll(0),cvScalarAll(0),0,CV_FILLED);

用參數CV_FILLED就能夠了 ,這樣能夠填充輪廓,進而獲得模版有點相似圖像分割了。

還有一種方法就是:

http://blog.csdn.net/augusdi/article/details/9011935

 

本文同步分享在 博客「shiter」(CSDN)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索