1. 轮廓发现(find contour)
- 轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法。所以边缘提取的阈值选定会影响最终轮廓发现结果。
2. 步骤
- 输入图像转为灰度图像cvtColor
- 使用Canny进行边缘提取,得到二值图像
- 使用findContours寻找轮廓
- 使用drawContours绘制轮廓
3.相关API
3.1寻找轮廓findCon tours()
在二值图像上发现轮廓。
cv::findContours(
InputOutputArray binImg, // 输入图像,非0的像素被看成1,0的像素值保持不变,8-bit
OutputArrayOfArrays contours,// 全部发现的轮廓对象,每个轮廓都是以点向量的形式进行存储即使用point类型的vector表示
OutputArray, hierachy// 作为轮廓数量的表示hierarchy包含了很多元素,每个轮廓contours[i]对应hierarchy中hierarchy[i][0]~hierarchy[i][3],分别表示后一个轮廓,前一个轮廓,父轮廓,内嵌轮廓的索引,如果没有对应项,则相应的hierarchy[i]设置为负数。
int mode, // 轮廓返回的模式
int method,// 发现方法
Point offset=Point()// 轮廓像素的位移,默认(0, 0)没有位移
)
其中,
- mode包括:
- RETR_EXTERNAL:表示只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
- RETR_LIST:提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
- RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
- RETR_TREE:提取所有轮廓并重新建立网状轮廓结构
- RETR_FLOODFILL:官网没有介绍,应该是洪水填充法
- method包括:
- CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
- CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
- CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS使用Teh-Chinl链逼近算法中的一种
3.2 绘制轮廓drawContours()
在二值图像上发现轮廓使用API cv::findContours之后对发现的轮廓数据进行绘制显示。
drawContours(
InputOutputArray binImg, // 输出图像
OutputArrayOfArrays contours,// 全部发现的轮廓对象
Int contourIdx// 轮廓索引号,表示绘制第几个轮廓,如果为负值则绘制所有输入轮廓
const Scalar & color,// 绘制时候颜色
int thickness,// 绘制线宽
int lineType ,// 线的类型LINE_8
InputArray hierarchy,// 拓扑结构图
int maxlevel,// 最大层数, 0只绘制当前的,1表示绘制绘制当前及其内嵌的轮廓
Point offset=Point()// 轮廓位移,可选
4. 例程
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
using namespace cv;
using namespace std;
void CallBack(int, void*);
Mat src, dst;
int Threshold = 10;
int main() {
//输入图像并转化为灰度
src = imread("D:/resource/images/轮廓图.jpg");
if (src.empty()) printf("src couldn't be loaded...");
imshow("input", src);
cvtColor(src, src, COLOR_BGR2GRAY);
//设置阈值后执行CallBack()
namedWindow("output");
createTrackbar("Threshold", "output", &Threshold, 255, CallBack);
CallBack(0, 0);
waitKey(0);
return 0;
}
void CallBack(int, void*) {
//边缘检测
Mat CannyOutput;
Canny(src, CannyOutput, Threshold, Threshold * 2, 3, false);
//寻找边缘
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
findContours(CannyOutput, 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++)
drawContours(dst, contours, i, Scalar(rng.uniform(0,255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, LINE_8, Mat());
imshow("output", dst);
cout << contours.size()<<endl;
}