Opencv边缘检测、轮廓发现、绘制轮廓
提取图像轮廓的2个步骤
1、 findContours函数找轮廓,
2、 drawContours函数画轮廓
轮廓的查找——cv::findContours()
函数cv::findContour是从二值图像中来计算轮廓的,它可以使用cv::Canny()函数处理的图像,因为这样的图像含有边缘像素;也可以使用cv::threshold()或者cv::adaptiveThreshold()处理后的图像,其边缘隐含在正负区域的交界处。
轮廓的层级结构
下图所示了cv::findCountour()的基本功能,图的上部是一幅测试图像,其背景为白色,并含有数个彩色的的区域(标签A到E)。图中也绘制出了由cv::findContours()所确定的轮廓。这些轮廓被标记为cX或hX,其中c代表“contour(轮廓)”,h代表“洞(hole)”,而X是一些数字。有些轮廓使用虚线表示的,他们表示白色区域(即非零区域)的外部边界。OpenCV和cv::findContour()对这些外部边界和图中的点线,即内部边界或者是洞的外部边界,进行区分的。
如图的下半部分,OpenCV可以将找到的轮廓组织成轮廓树,表示其轮廓结构的包围关系。对于测试图像中的轮廓,我们将根节点处的轮廓称为c0,而“洞”h00和h01是其子节点。反过来这些子节点又会包含新的子节点以此类推。
表示这种树的方式有很多种,OpenCV中使用数组(尤其是vectors)来表示这种树,其中数组中的每个条目都代表一个特定的轮廓,每个条目包含一个由4个整数组成的集合(通常表示为cv :: Vec4i类型的元素,就像四通道数组中的条目一样)。对于每个节点来说,四个元素所表示的含义分别如下:0号元素表示下一个轮廓(同一层级);1号元素表示前一个轮廓(同一层级);2号元素表示第一个子轮廓(下一层级);3号元素表示父轮廓(上一层级)
drawContours函数的作用
主要用于画出图像的轮廓
函数的调用形式
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
函数参数详解:
其中第一个参数image表示目标图像,
第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,
第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
第四个参数color为轮廓的颜色,
第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
第六个参数lineType为线型,
第七个参数为轮廓结构信息,
第八个参数为maxLevel
代码演示
/*
* 轮廓发现
* 1:输入图像转为灰度图像
* 2使用Canny进行边缘提取,得到二值图像
* 3使用findContours寻找轮廓
* 4使用drawContours绘制轮廓
*
*/
#include <opencv.hpp>
#include<iostream>
#include <string>
#include<conio.h>
#include<time.h>
using namespace std;
using namespace cv;
Mat src, dst;
const char* output_win = "findcontours demo";
int threshold_value = 100;
int threshold_max = 255;
RNG rng;
void Demo_Contours(int, void*);
int main()
{
src = imread("E:\\Users\\opencvCoder\\image\\niu.jpg");
if (src.empty())
{
printf("could not load image...\n");
return -1;
}
imshow("input_image", src);
namedWindow("input_image", 0);
namedWindow(output_win, 1);
cvtColor(src, src, COLOR_BGR2GRAY);
imshow("gray_image", src);
const char* trackbar_title = "Threshold Value";
createTrackbar(trackbar_title, output_win, &threshold_value, threshold_max, Demo_Contours);
Demo_Contours(0, 0);
waitKey(0);
return 0;
}
void Demo_Contours(int, void*)
{
Mat canny_output;
vector<vector<Point>>contours;
vector<Vec4i>hierachy;
/*
*Canny边缘检测的步骤:
*1、去噪。噪声会影响边缘检测的准确性,因此首先要将噪声过滤掉。
*2、计算梯度大小和方向。
*3、非极大值抑制。就是适当的让边缘'变瘦'。
*4、确定边缘。使用双阈值法确定最终的边缘信息。
*/
Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false);
findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
dst = Mat::zeros(src.size(), CV_8UC3);
RNG rng(12345);
for (size_t 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, hierachy, 0, Point(0, 0));
}
imshow(output_win, dst);
//canny_output 为图像掩码,将非零值复制给dst
src.copyTo(dst, canny_output);
//图像取反输出
//imshow(output_win, ~dst);
//正常输出
imshow("mask image", dst);
}