OpenCV-图像处理(28、轮廓发现(find contour in your image))

时间:2024-04-04 11:45:35

OpenCV-图像处理(28、轮廓发现(find contour in your image))

  • 轮廓:轮廓即是以某种方式表示图像中的曲线的点的列表。这种表示可以根据实际的情形不同而不同。表示一条曲线的方式有很多种。OpenCV中,轮廓是由STL风格的vector<>模板对象表示的,其中vector中的每个元素都编码了曲线上,下一点的位置信息。
  • 拓扑:就是把实体抽象成与其大小、形状无关的“点”,而把连接实体的线路抽象成“线”,进而以图的形式来表示这些点与线之间关系的方法,其目的在于研究这些点、线之间的相连关系。表示点和线之间关系的图被称为拓扑结构图。

轮廓发现(find contour)

  • 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓发现结果
  • API介绍
    findContours 发现轮廓
    drawContours 绘制轮廓

相关API

  • 在二值图像上发现轮廓使用API: cv::findContours()的具体调用有两种方式,具体如下:
  1. void cv::findContours(
    InputOutputArray binImg, // 输入图像,非0的像素被看成1,0的像素值保持不变,8-bit
    OutputArrayOfArrays contours,// 全部发现的轮廓对象, 包含points的vectors的vector
    OutputArray, hierachy// 图该的拓扑结构,可选,该轮廓发现算法正是基于图像拓扑结构实现。
    int mode, // 轮廓返回的模式
    int method,// 发现方法
    Point offset=Point()// 轮廓像素的位移,默认(0, 0)没有位移
    )

  2. void cv::findContours(
    InputOutputArray image, // 输入的8位单通道“二值”图像
    OutputArrayOfArrays contours, // 包含points的vectors的vector
    int mode, // 轮廓检索模式
    int method, // 近似方法
    cv::Point offset = cv::Point() // (可选) 所有点的偏移
    );

int mode, // mode轮廓检索模式,可以通过cv::RetrievalModes()查看详细信息

  1. RETR_EXTERNAL:表示只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
  2. RETR_LIST:提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
  3. RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
  4. RETR_TREE:提取所有轮廓并重新建立网状轮廓结构
  5. RETR_FLOODFILL:官网没有介绍,应该是洪水填充法

int method,// method:轮廓近似方法可以通过cv::ContourApproximationModes()查看详细信息

  1. CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
  2. CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
  3. CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS使用Teh-Chinl链逼近算法中的一种
  • 对发现的轮廓数据进行绘制显示
    drawContours(// 绘制轮廓,一次只能根据轮廓索引号绘制一次,需要循环所有发现的轮廓
    InputOutputArray binImg, // 输出图像
    OutputArrayOfArrays contours,// 全部发现的轮廓对象,包含points的vectors的vector
    Int contourIdx// 轮廓索引号
    const Scalar & color,// 绘制时候颜色
    int thickness,// 绘制线宽,如果传 -1 表示填充轮廓
    int lineType ,// 线的类型LINE_8
    InputArray hierarchy,// 拓扑结构图
    int maxlevel,// 最大层数, 0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓
    Point offset=Point()// 轮廓位移,可选
    )

步骤

  1. 输入图像转为灰度图像cvtColor
  2. 使用Canny进行边缘提取,得到二值图像
  3. 使用findContours寻找轮廓
  4. 使用drawContours绘制轮廓

程序代码

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
 
Mat src,src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
 
void thresh_callback(int, void* );
 
int main( int argc, char** argv )
{
	// 1. 加载源图像
	src = imread("E:/Experiment/OpenCV/Pictures/happyfish.jpg");
	
	// 2. 转成灰度并模糊化降噪
	cvtColor( src, src_gray, CV_BGR2GRAY );
	blur( src_gray, src_gray, Size(3,3) );
 
	// 创建窗体
	char* source_window = "Source";
	namedWindow( source_window, CV_WINDOW_AUTOSIZE );
	imshow( source_window, src );
 
	createTrackbar( " Canny thresh:", "Source", &thresh, max_thresh, thresh_callback );
	thresh_callback( 0, 0 );
 
	waitKey(0);
	return(0);
}
 
void thresh_callback(int, void* )
{
	Mat canny_output;
	vector<vector<Point> > contours;//每个轮廓由一系列点组成
	vector<Vec4i> hierarchy;
 
	// 用Canny算子检测边缘
	Canny( src_gray, canny_output, thresh, thresh*2, 3 );
	// 寻找轮廓
	findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
 
	// 发现轮廓
	//因为要用不同颜色区分轮廓,所以这里用的CV_8UC3 3通道彩色图,如果是CV_8UC1 绘制的就是灰度图,感官上不好区分
	Mat drawing = 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( drawing, contours, i, color, 2, 8, hierarchy, 0, Point() );//颜色一致的就属同一发现的轮廓
	}
 
	// 在窗体中显示结果
	namedWindow( "Contours", CV_WINDOW_AUTOSIZE );
	imshow( "Contours", drawing );
}

运行截图

OpenCV-图像处理(28、轮廓发现(find contour in your image))

参考博客:

  1. https://blog.csdn.net/guduruyu/article/details/69220296
  2. https://blog.csdn.net/huanghuangjin/article/details/81182562
  3. https://blog.csdn.net/LYKymy/article/details/83210433