OpenCV学习之路(三)——轮廓查找与多边形绘制

版权声明:谢谢你那么厉害还看了我的文章,欢迎转载交流学习~ https://blog.csdn.net/kilotwo/article/details/88866279

查找与绘制轮廓

一个轮廓一般对应一系列点,即一条曲线。在OpenCV中,可以用findContours()函数从二值图像查找轮廓。

  1. image:输入图像。8-bit的单通道二值图像,非零的像素都会被当作1。
  2. contours:检测到的轮廓。是一个向量,向量的每个元素都是一个轮廓。因此,这个向量的每个元素仍是一个向量。vector<vector > contours;
  3. hierarchy:各个轮廓的继承关系。hierarchy也是一个向量,长度和contours相等,每个元素和contours的元素对应。hierarchy的每个元素是一个包含四个整型数的向量。即:
    vector hierarchy; // Vec4i is a vector contains four number of int
    hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3],分别表示的是第i条轮廓(contours[i])的下一条,前一条,包含的第一条轮廓(第一条子轮廓)和包含他的轮廓(父轮廓)。
  4. mod: 检测轮廓的方法。有四种方法。
    method:表示一条轮廓的方法。
  5. offset:可选的偏移,就是简单的平移,特别是在做了ROI步骤之后有用

检测轮廓方法(mod):

  • CV_RETR_EXTERNAL:只检测外轮廓。忽略轮廓内部的洞。

  • CV_RETR_LIST:检测所有轮廓,但不建立继承(包含)关系。

  • CV_RETR_TREE:检测所有轮廓,并且建立所有的继承(包含)关系。用CV_RETR_EXTERNAL和CV_RETR_LIST方法hierarchy变量是没用的,因为前者没有包含关系,找到的都是外轮廓,后者仅仅是找到所有的轮廓但并不把包含关系区分。用TREE这种检测方法的时候我们的hierarchy这个参数才是有意义的。事实上,应用前两种方法的时候,我们就用findContours这个函数的第二种声明了。

  • CV_RETR_CCOMP:检测所有轮廓,但是仅仅建立两层包含关系。外轮廓放到顶层,外轮廓包含的第一层内轮廓放到底层,如果内轮廓还包含轮廓,那就把这些内轮廓放到顶层去。

表示一条轮廓的方法(method):

  • CV_CHAIN_APPROX_NONE:把轮廓上所有的点存储。

  • CV_CHAIN_APPROX_SIMPLE:只存储水平,垂直,对角直线的起始点。对drawContours函数来说,这两种方法没有区别。

  • CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:实现的“Teh-Chin chain approximation algorithm.

查找findContours()函数

findContours(srcImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

  • 第一个参数,输入图像,即原图像,填Mat单通道图像。可使用compare()、inrange()、threshold()、adaptivethreshold()、canny()等函数或彩色图创建二进制图像。
  • 第二个参数,检测到的轮廓、函数调用后的运算结果。每个轮廓存储为一个点向量,即用point类型的vector表示。
  • 第三个参数,可选的输出向量,包含图像的拓扑信息。每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0]~hierarchy[i][3],分别为后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号。
  • 第四个参数,轮廓检索模式。只检测最外层、提取所有轮廓、提取所有轮廓并组织双层结构、提取所有轮廓并重新建立网状轮廓结构。
  • 第五个参数 轮廓的近似方法。

绘制drawContours()函数

drawContours(dstImage, contours, index, color, FILLED, 8, hierarchy);

  • 第一个轮廓,目标图像,填Mat对象
  • 第二个参数,所有的输入轮廓,表示输入的轮廓组,每一组轮廓由点vector构成.
  • 第三个参数,轮廓绘制的指示变量,如果其为负值,则绘制所有轮廓。
  • 第四个参数,轮廓的颜色
  • 第五个参数,粗细度
  • 第六个参数,线条类型
  • 第七个参数,hierarchy

查找并绘制轮廓


//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;

//-----------------------------------【main( )函数】--------------------------------------------

//		描述:控制台应用程序的入口函数,我们的程序从这里开始
//-------------------------------------------------------------------------------------------------
int main(int argc, char** argv)
{
	// 【1】载入原始图,且必须以二值图模式载入
	Mat srcImage = imread("1.jpg", 0);
	imshow("原始图", srcImage);

	//【2】初始化结果图
	Mat dstImage = Mat::zeros(srcImage.rows, srcImage.cols, CV_8UC3);

	//【3】srcImage取大于阈值119的那部分
	srcImage = srcImage > 119;
	imshow("取阈值后的原始图", srcImage);

	//【4】定义轮廓和层次结构
	vector<vector<Point> > contours;
	vector<Vec4i> hierarchy;

	//【5】查找轮廓
	//OpenCV3版为:
	findContours(srcImage, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

	// 【6】遍历所有顶层的轮廓, 以随机颜色绘制出每个连接组件颜色
	int index = 0;
	for (; index >= 0; index = hierarchy[index][0])
	{
		Scalar color(rand() & 255, rand() & 255, rand() & 255);
		//此句代码的OpenCV3版为:
		drawContours(dstImage, contours, index, color, FILLED, 8, hierarchy);
	}

	//【7】显示最后的轮廓图
	imshow("轮廓图", dstImage);

	waitKey(0);

}

使用多边形把轮廓包围

在实际应用中,常常会有将检测到的轮廓用多边形表示出来的需求。比如在一个全家福中,我想用一个矩形框将我自己的头像框出来,这样就需要这方面的知识了。

OpenCv这方面的函数总结如下:

  1. 逼近多边形曲线:approxPolyDP():基于RDP算法实现,目的是减少多边形轮廓点数,方面秒点。输入二维点集,输出多边形逼近结果,
  2. 返回指定点集轮廓最小矩形边界:boundingRect()
  3. 寻找给定的点集可旋转的最小包围矩形:minAreaRect()
  4. 寻找最小包围圆形:minEnclosingCircle()
  5. 用椭圆拟合二维点集:fitEllipse()

步骤如下:

  • 首先将图像变为二值图像
  • 发现轮廓,找到图像轮廓
  • 通过API在轮廓点上找到最小包含矩形和圆,旋转矩形与椭圆
  • 绘制它们

下面给出这些函数用法的综合案例。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

using namespace cv;
using namespace std;

Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);

/// 函数声明
void thresh_callback(int, void*);

/** @主函数 */
int main(int argc, char** argv)
{
    /// 载入原图像, 返回3通道图像
    src = imread("123.jpg", 1);

    /// 转化成灰度图像并进行平滑
    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(" Threshold:", "Source", &thresh, max_thresh, thresh_callback);
    thresh_callback(0, 0);

    waitKey(0);
    return(0);
}

/** @thresh_callback 函数 */
void thresh_callback(int, void*)
{
    Mat threshold_output;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    /// 使用Threshold检测边缘
    threshold(src_gray, threshold_output, thresh, 255, THRESH_BINARY);
    /// 找到轮廓
    findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_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(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);
        //画圆
        circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);
    }

    /// 显示在一个窗口
    namedWindow("Contours", CV_WINDOW_AUTOSIZE);
    imshow("Contours", drawing);
}

猜你喜欢

转载自blog.csdn.net/kilotwo/article/details/88866279