提取连续区域 - sumpig/OpenCV GitHub Wiki
在物体检测和识别程序中,第一步通常就是生成二值图像,找到感兴趣物体所处的位置。
下一个步骤都是从由 1 和 0 组成的像素集合中提取出物体。
OpenCV 提供了 cv::findContours 函数,可以提取出图像中连续区域的轮廓:
// 用于存储轮廓的向量
std::vector<std::vector<cv::Point>> contours;
cv::findContours(image,
contours, // 存储轮廓的向量
cv::RETR_EXTERNAL, // 检索外部轮廓
cv::CHAIN_APPROX_NONE); // 每个轮廓的全部像素
函数还指明了两个选项,第一个选项表示只检索外部轮廓,即物体内部的空穴会被忽略。
第二个选项指明了轮廓的格式。使用当前的选项,向量将列出轮廓的全部点。
用 contours.szie() 可以查看轮廓的数量。
有一个非常实用的函数可在图像(这里用白色图像)上画出那些区域的轮廓:
// 在白色图像上画黑色轮廓
cv::Mat result(image.size(), CV_8U, cv::Scalar(255));
cv::drawContours(result, contours,
-1, // 画全部轮廓
0, // 用黑色画
2); // 宽度为2
如果事先已经知道感兴趣物体的大小,就可以将部分区域删除。我们采用区域边界的最小值和最大值,具体做法是迭代遍历存放轮廓的向量,并且删除无效的轮廓:
// 删除太短或太长的轮廓
int cmin= 50; // 最小轮廓长度
int cmax= 1000; // 最大轮廓长度
std::vector<std::vector<cv::Point>>::iterator itc = contours.begin();
// 针对所有轮廓
while (itc != contours.end()) {
// 验证轮廓大小
if (itc->size() < cmin || itc->size() > cmax)
itc = contours.erase(itc);
else
++itc;
}
cv::findContours 函数也能检测二值图像中的所有闭合轮廓,包括区域内部空穴构成的轮廓。
cv::findContours(image,
contours, // 存储轮廓的向量
cv::RETR_LIST, // 检索外部轮廓
cv::CHAIN_APPROX_NONE); // 每个轮廓的全部像素
你也可以把这些轮廓分层次组织起来。主区域是父轮廓,它内部的空穴是子轮廓;如果空穴内部还有区域,那它们就是上述子轮廓的子轮廓,以此类推。使用 cv::RETR_TREE 标志可得到这个层次结构,代码为:
std::vector<cv::Vec4i> hierarchy;
cv::findContours(image, contours, // 存放轮廓的向量
hierarchy, // 层次结构
cv::RETR_TREE, // 树状结构的轮廓
cv::CHAIN_APPROX_NONE); // 每个轮廓的全部像素
层次元素由四个整数构成,前两个整数是下一个和上一个同级轮廓的序号,后两个整数是第一个子轮廓和父轮廓的序号。如果序号为负,就表示轮廓列表的末端。cv::RETR_CCOMP 标志的作用与之类似,但只允许两个层次。