轮廓
基本概念
轮廓可以简单认为成将连续的点(连着边界)连在一起的曲线,具有相同的颜色或者灰度,提取轮廓就是提取这些具有相同颜色或者灰度的曲线,或者说是连通域,轮廓在形状分析和物体的检测和识别中非常有用。
注意事项
①为了更加准确,要使用二值化图像。在寻找轮廓之前,要进行阈值化处理或者 Canny 边界检测
②查找轮廓的函数会修改原始图像。如果你在找到轮廓之后还想使用原始图像的话,你应该将原始图像存储到其他变量中(clone(), copyTo())
③在OpenCV 中,查找轮廓就像在黑色背景中找白色物体。你应该记住, 要找的物体应该是白色而背景应该是黑色。
常用函数
findContours()—–查找轮廓
drawContours()—–绘制轮廓
实例
查找轮廓—findContours()
函数原型
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode,
int method, Point offset = Point());
/** @overload */
CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours,
int mode, int method, Point offset = Point());
- image: 输入图像, Mat类型8位单通道图像(一般为二值图)
- contours: 检测到的轮廓, 每个轮廓存储为一个点向量, 即Point类型的vector表示
- hierarchy: 可选的输出向量, 包含图像的拓扑信息。其作为轮廓数量的表示, 包含了许多元素, 每个轮廓contours[i]对应4个
hierarchy元素hierarchy[i][0]~hierarchy[i][3], 分别表示后一轮廓、前一轮廓、父轮廓、内嵌轮廓的索引编号, 如果没有对应项, 设置为负数 mode: 轮廓检索模式, 取值如下:
CV_RETR_EXTERNAL=0—–表示只检测最外层轮廓
CV_RETR_LIST=1——提取所有轮廓并放置在list中, 轮廓不建立等级关系
CV_RETR_CCOMP=2——提取所有轮廓并组织为双层结构
CV_RETR_TREE=3——提取所有轮廓并重新建立网状轮廓结构
method: 轮廓的近似方法, 取值见图2
- offset: 每个轮廓的可选偏移量, 默认值Point()
绘制轮廓—drawContours()
函数原型
CV_EXPORTS_W void 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() );
- image: 目标图像, Mat类型对象即可
- contours: 所有的输入轮廓, 每个轮廓存储为一个点向量
- contourIdx: 轮廓绘制指示变量(索引), 若为负值, 则表示绘制所有轮廓
- color: 绘制轮廓的颜色
- thickness: 轮廓线条的粗细, 默认值1, 如果为负值, 则绘制轮廓内部, 可选宏 CV_FILLED
- lineType: 线条类型, 默认值8
- hierarcy: 可选的层次结构信息, 默认值noArray()
- maxLevel: 表示用于绘制轮廓的最大等级, 默认值INT_MAX
- offset: 可选的轮廓偏移参数, 默认值Point()
轮廓查找与轮廓绘制代码
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
Mat srcImg = imread("D:\\1\\01.png");
Mat tempImg = srcImg.clone();//进行备份
Mat draw(srcImg.rows, srcImg.cols, CV_8UC3);
cvtColor(srcImg, srcImg, CV_BGR2GRAY); //转为灰度图
threshold(srcImg, srcImg, 50, 255, CV_THRESH_BINARY);//二值化
imshow("srcImg", srcImg); //轮廓查找前
vector<vector<Point>> contours;//两层结构,第一层表示轮廓数,第二层表示轮廓中的点
vector<Vec4i> hierarchy;
//findContours(srcImg, contours, hierarchy,RETR_EXTERNAL, CHAIN_APPROX_SIMPLE ); //查找轮廓
//findContours(srcImg, contours, hierarchy,RETR_LIST, CHAIN_APPROX_SIMPLE ); //查找轮廓
findContours(srcImg, contours, hierarchy,CV_RETR_CCOMP, CHAIN_APPROX_SIMPLE ); //查找轮廓
//findContours(srcImg, contours, hierarchy,RETR_TREE, CHAIN_APPROX_NONE ); //查找轮廓
imshow("cont", srcImg); //轮廓查找后
drawContours(tempImg, contours, -1, Scalar(0, 255, 0), 2, 8); //绘制轮廓
cout<<"num="<<contours.size()<<endl; //输出轮廓个数
imshow("contours", tempImg);
waitKey(0);
}
MainWindow::~MainWindow()
{
}
发现墙面的裂缝
Mat Img=img.clone();
Mat gray;
Mat element0=getStructuringElement(MORPH_RECT,Size(5,5));
Mat element1=getStructuringElement(MORPH_RECT,Size(7,7));
cvtColor(Img,gray,COLOR_RGB2GRAY);
GaussianBlur(gray,gray,Size(255,255),0,0);
adaptiveThreshold(gray,gray,255,ADAPTIVE_THRESH_GAUSSIAN_C,THRESH_BINARY_INV,15,3);//自适应滤波
erode(gray,gray,element0);
dialate(gray,gray,element1);
vector<vector<Point>> contours;
vector<vec4i> hierarchy;
findContours(gray,contours,CV_PETE_TREE,CV_CHAIN_APPROX_SIMPLE,Point(0,0));
drawContours(img,contours,-1,Scalar(0,0,255),3,8,hierarchy);
if(!(img.empty())){
resize(img,img,Size(rect.width(),rect.Height()));
imshow("PIC",img);
}
访问每一个轮廓以及轮廓上的所有点
访问每一个轮廓
访问每一个像素
代码一—绘制轮廓
代码1
drawContours(tempImg, contours, -1, Scalar(0, 255, 0), 2, 8); //绘制轮廓
代码2
for(int i=0; i<contours.size(); i++)
{
drawContours(tempImg, contours, i, Scalar(0, 255, 0), 2, 8); //绘制轮廓
}
代码1和代码2实现的功能相同
代码二
for(int i=0; i<contours.size(); i++)
{
for(int j=0; j<contours[i].size(); j++)
{
circle(tempImg, Point(contours[i][j].x, contours[i][j].y), 3, Scalar(0, 255, 0), 2, 8); //画出每个轮廓的每个点
imshow("contours", tempImg);
}
代码3
for(int i=0; i<contours.size(); i++)
{
//drawContours(tempImg, contours, i, Scalar(0, 255, 0), 2, 8); //绘制轮廓
for(int j=0; j<contours[i].size(); j++)
{
line(tempImg, Point(3,3), Point(contours[i][j].x, contours[i][j].y), Scalar(0, 0, 255), 1, 8);
cout<<"("<<contours[i][j].x<<","<<contours[i][j].y<<")"<<endl;
waitKey(100);
imshow("contours", tempImg);
}
}
查找轮廓参数说明
简单孔洞填充
findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //查找轮廓
drawContours(tempImg, contours, -1, Scalar(0, 255, 0), -1, 8); //绘制轮廓
简单连通域标记
findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //查找轮廓
cout<<"num="<<contours.size()<<endl; //输出轮廓(连通域)个数
for(int i=0; i<contours.size(); i++)
{
drawContours(tempImg, contours, i, Scalar(rand()%255, rand()%255, rand()%255), -1, 8); //绘制轮廓
}
imshow("contours", tempImg);
总体代码
#include "mainwindow.h"
#include<opencv2/opencv.hpp>
#include<opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
Mat srcImg = imread("D:\\1\\33.png");
imshow("src", srcImg);
//Mat tempImg(srcImg.rows, srcImg.cols, CV_8UC3, Scalar(255, 255, 255));
Mat tempImg = srcImg.clone(); //原图备份
cvtColor(srcImg, srcImg, CV_BGR2GRAY); //转为灰度图
threshold(srcImg, srcImg, 100, 255, CV_THRESH_BINARY); //二值化
imshow("threshold", srcImg);
vector<vector<Point>> contours; //轮廓定义
vector<Vec4i> hierarcy;
findContours(srcImg, contours, hierarcy, CV_RETR_TREE, CV_LINK_RUNS); //查找轮廓
//drawContours(tempImg, contours, -1, Scalar(0, 255, 0), 2, 8); //绘制轮廓
for(int i=0; i<contours.size(); i++)
{
//drawContours(tempImg, contours, i, Scalar(0, 255, 0), 2, 8); //绘制轮廓
for(int j=0; j<contours[i].size(); j++)
{
circle(tempImg, Point(contours[i][j].x, contours[i][j].y), 3, Scalar(0, 255, 0), 2, 8); //画出每个轮廓的每个点
//line(tempImg, Point(3,3), Point(contours[i][j].x, contours[i][j].y), Scalar(0, 0, 255), 1, 8);
//cout<<"("<<contours[i][j].x<<","<<contours[i][j].y<<")"<<endl;
//waitKey(100);
imshow("contours", tempImg);
}
}
//孔洞填充
findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //查找轮廓
drawContours(tempImg, contours, -1, Scalar(0, 255, 0), -1, 8); //绘制轮廓
//标记连通域
findContours(srcImg, contours, hierarcy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); //查找轮廓
cout<<"num="<<contours.size()<<endl; //输出轮廓(连通域)个数
for(int i=0; i<contours.size(); i++)
{
drawContours(tempImg, contours, i, Scalar(rand()%255, rand()%255, rand()%255), -1, 8); //绘制轮廓
}
imshow("contours", tempImg);
waitKey(0);
}
MainWindow::~MainWindow()
{
}