学习OpenCV 13 轮廓
Canny之类的边缘检测算法可以根据像素间差异检测出轮廓边界的像素,但它并没有将轮廓作为一个整体进行处理。而我们的任务就是要将这些边缘像素合成轮廓。
轮廓查找Contour Finding
一个轮廓对应一系列点,这些点以一定方式表示图像中的一条曲线。有多种方式可以表示一条曲线,在OpenCV中,轮廓用标准模板库(STL)向量vector<>表示,向量中每个值都包含轮廓上下一个点的位置信息,用一系列二维顶点(vector<cv::Point>或vector<cv::Point2f>)表示轮廓是最常见的方式,但不限于此。
cv::findContours()从二值图像(binary image)中计算轮廓,它处理的图像可以是从cv::Canny()函数得到的有边缘像素的图像,或是从cv::threshold()及cv::adaptiveThreshold()函数得到的图像。
轮廓层次Contour Hierarchies
下图分别是一张输入cv::findContours()函数的测试图像(左图)。图中有五块颜色区域(A,B,C,D,E),共有9条轮廓。每条轮廓都有一组输出列表表示(右上角图—轮廓参数)。也可以选择生成一组层次表达(右下角图—层次参数)。在右下角图中(轮廓树),节点表示一条轮廓,根据每个点在层次队列中的四元数组索引,连接都做了相应标记。
轮廓层次列表中四元数组
OpenCV用数组(通常vector)表示轮廓树,其中每个值都代表一条特定轮廓。在该数组中,每个值都包含一个四整数组,即层次列表中的每个节点都由四个整数组成,每个数都代表在层次列表中与当前节点有特定关系的另一个节点。
索引Index | 含义Meaning |
0 | 同级下一条轮廓Next Contour |
1 | 同级前一条轮廓Previous Contour |
2 | 下级的第一个子节点First Child |
3 | 上级的父节点Parent |
而当节点间不存在这种关系时,数组中相应元素设为-1,如根节点的3号元素的值将设为-1,因为根节点没有父节点。
使用cv::findContours()查找轮廓
void cv::findContours(
cv::InputArray image, //输入8位单通道二值图像
cv::OutputArrayOfArrays contours, //输出轮廓数组
cv::OutputArray hierarchy, //(可选项)输出轮廓树结构
int mode, //轮廓提取方式,有4种
int method, //轮廓如何被表达
cv::point offset=cv::Point() //(可选项)偏移
)
void cv::findContours(
cv::InputArray image, //输入8位单通道二值图像
cv::OutputArrayOfArrays contours, //输出轮廓数组
int mode, //轮廓提取方式,有4种
int method, //轮廓如何被表达
cv::point offset=cv::Point() //(可选项)偏移
)
第一个参数输入图像必须是8位单通道二值图像。
第二个参数是输出轮廓数组,在一个轮廓vector中,contours[i]是一条轮廓,contours[i][j]是contours[i]中的一个点。
参数hierarchy是可选项,输出轮廓的树结构。这一输出为一个数组,每个轮廓对应数组中一个值,每个值都是上面的四元数组。
参数mode告诉OpenCV轮廓提取方式,mode有4种:
- cv::RETR_EXTERNAL 只检索最外层轮廓。
- cv::RETR_LIST 检索所有轮廓并保存到表中。
- cv::RETR_CCOMP 检索所有轮廓并组织成双层结构。
- cv::RETR_TREE 检索所有轮廓并重新建立网状轮廓结构。
绘制轮廓Drawing Contours
void cv::drawContours(
cv::InputOutputArray image, //待绘制轮廓的图像
cv::InputArrayOfArrays contours, //要绘制的轮廓
int contourIdx, //正数则对应轮廓被绘制,负则所有轮廓被绘制
const cv::Scalar& color, //轮廓颜色
int thickness=1, //轮廓粗细
int lineType=8, //轮廓的线种类
cv::InputArray hierarchy=noArray(), //层次
int maxLevel=INT_MAX, //层次深度
cv::Point offset=cv::Point() //偏移
)
绘制轮廓实例
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
Mat g_gray, g_binary;//灰度图像,二值图像
int g_thresh = 100;//阈值
void on_trackbar(int, void*) {
threshold(g_gray, g_binary, g_thresh, 255, THRESH_BINARY);
vector<vector<Point>>contours;
findContours(g_binary, contours, noArray(), RETR_LIST, CHAIN_APPROX_SIMPLE);
g_binary = Scalar::all(0);
drawContours(g_binary, contours, -1, Scalar::all(255));
imshow("Coutours", g_binary);
}
int main(int argc, char**argv) {
Mat src = imread("D://somephotos//test.jpg");
namedWindow("Resource", 1);
imshow("Resource", src);
cvtColor(src, g_gray, COLOR_BGR2GRAY);
namedWindow("Contours", 1);
createTrackbar("Threshold", "Contours", &g_thresh, 255, on_trackbar);
on_trackbar(0, 0);
waitKey();
return 0;
}