输入图像
- void Detect_Object(Mat img)
- {
- Mat gray, binaryIMG;
- Mat correct_IMG;
- cvtColor(img, gray, CV_BGR2GRAY);
- Canny(gray, binaryIMG, 90, 180);
- blur(binaryIMG, binaryIMG, Size(3, 3));
- // Find contours
- vector<vector<Point>> contours;
- vector<Vec4i> hierarchy;
- double TargetArea = 0;
- findContours(binaryIMG, contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_NONE, Point(0, 0));
- for (int i = 0; i <contours.size(); i++)
- {
- drawContours(img, contours, i, Scalar(0, 255, 0), 2, 8, hierarchy, 0, Point());
- }
- }
先上代码, 上述函数是寻找一个图像内的所有轮廓.
执行后, 利用
drawContours(img, contours, i, Scalar(0, 255, 0), 2, 8, hierarchy, 0, Point());
这个函数, 会画出所有图像内的轮廓.
执行上述代码后, 我的代码上显示 contour.size() , 即轮廓个数是 34 个. 这个轮廓个数不是绝对的, 结果由你对 canny() 选取的阈值, blur() mask 的大小选取都有影响.
Canny 和 Blurring 算法结果图像.
红色显示的是 FindContours 函数所检测到的轮廓.
你或许会觉得, 最终显示的结果像一幅捕捉边缘的图像, 即 Edge Map. 也会不解, 有了 Canny, 何须多此一举在寻找轮廓.
接下来我就要介绍本章的主角轮廓 Hierarchy 的阶层关系.
先看一段修改后的代码:
- for (int i = 0; i < contours.size(); i++)
- {
- if (hierarchy[i][3] == -1)
- {
- drawContours(img, contours, i, Scalar(0, 0, 255), 1, 8, hierarchy, 0, Point());
- }
- }
这段代码中, 多了一段条件语句, 语句里写了
hierarchy[i][3] == -1
首先, 我们确认一下结果:
这次的结果里, 只检测出了最外围轮廓. 很明显
hierarchy[i][3] == -1 起了决定性的作用.
我们先看看 OpenCV 官方文件是怎么写的.
OpenCV represents it as an array of four values : [Next, Previous, First_Child, Parent]. (Link me: https://docs.opencv.org/3.4.0/d9/d8b/tutorial_py_contours_hierarchy.html)
hierarchy[i][3] == -1, 这里的 hierarchy[i] 指的是第 i 个轮廓的阶层关系. 而 hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3] 分别指的是 Next, Previous, First_child, Parent.
我们设定的 hierarchy[i][3]== -1 的意思就是 "轮廓没有父母", 即 "这个轮廓没有上层阶级的轮廓".
同理, hierarchy[i][2]== -1 的意思就是 "此轮廓没有第一个孩子", 即 "此轮廓没有下层阶级的轮廓".
至于, hierarchy[][0],hierarchy[][1] 指的是此轮廓的后一个轮廓, 和前一个轮廓. 他们都是同一个阶级的轮廓. 这个前后顺序可能是很随意的, 至今还没找到规律. 所以还没有到怎么利用他们.
所以,
hierarchy[i][3]== -1 条件下的结果, 是选中了没有父母的轮廓, 即他的外围没有包围他的轮廓.
再看看
hierarchy[i][2]== -1 条件下的结果:
和预想的一样, 它只标出了没有 "孩子" 的轮廓, 即此轮廓内没有更小的轮廓.
对了, 我要强调一下, 这个例子里我在使用 FindContours 函数的时候, 我用了 RETR_TREE 模式. 这个模式是 "万能的", 把图像内各轮廓的亲属关系都联系上了. 简而言之, 你能知道一个轮廓的 "爷爷奶奶","孙子孙女". 甚至更深的祖辈关系.
opencv 提供了各种模式, RETR_LIST, RETR_EXTERNAL 等. 上面的官方文件有详细说明.
RETR_LIST 就是这个图像的轮廓只可能是两个阶层的其中之一, 要么你就是爹, 要么你就是儿子. 本章只讲 RETR_TREE.
这时候你可能还没领会到 Hierarchy 的魅力. 他不会马上帮你挑出你最想要的信息, 但是他确实个帮你排除 "杂质" 帮手.
例如, 我们这次测试的 Heliport 图像, 很明显这个图像里的 "H" 是叫无人机去识别, 并且在其中心降落的. 那我们怎么去识别他呢? 如果不用 Deep Learning.
我们可以在这个图像里找没有孩子的轮廓, 但没有孩子的轮廓候补也很多. 怎么办?
很简单, 选取轮廓面积最大的, 或者大于一定面积以上的, 其实方法真的很多. 只要你多加一个条件语句.
简单的看下代码:
- for (int i = 0; i < contours.size(); i++)
- {
- if (hierarchy[i][2] == -1 && contourArea(contours[i])> 9800)
- {
- drawContours(img, contours, i, Scalar(0, 0, 255), 2, 8, hierarchy, 0, Point());
- }
- }
我加了一段 面积大于 9800 像素的条件. 结果就找到了 "H".
你问我怎么算的 9800? 估算的, 但是我想说的不是面积这部分, 利用面积也只是方法之一. 想要更精确确定的方法有很多. 比如是高和宽的比例.
今天主要是为了介绍 hierarchy 的魅力, 其实利用好真的能排除很多没用的信息. hierarchy 只是 OpenCV Contours 这部分的魅力之一, 仔细翻阅官方文件你会发现, opencv 这部分还真的准备了很多宝贝.
来源: https://www.cnblogs.com/SarahPaulson/p/9607410.html