OpenCV使用FindContours进行二维码定位
? ??我使用過FindContours,而且知道有能夠直接尋找聯(lián)通區(qū)域的函數(shù)。但是我使用的大多只是“最大輪廓”或者"輪廓數(shù)目“這些數(shù)據(jù)。其實輪廓還有另一個很重要的性質(zhì),那就是輪廓的相互包含特性。
????Mat?src?=?imread("e:/sandbox/contours.png",0);
????threshold(src,src,100,255,THRESH_OTSU);
????vector<vector<Point>?>?contours,contours2;
????vector<Vec4i>?hierarchy;
????findContours(?src,?contours,?hierarchy,??CV_RETR_TREE,?CHAIN_APPROX_NONE,?Point(0,?0)?);
????waitKey();
} 核心函數(shù)是findContours,它的幾個參數(shù)是什么意思了? 第一個參數(shù)是輸入圖像,必須是8通道的,并且應(yīng)該被轉(zhuǎn)化為二值(所有我threshold了預先),?已經(jīng)說明了,用于Contours尋找的圖片會被直接修改,如果需要它用,需要先復制一份后再保存。 第二個參數(shù)是內(nèi)存存儲器,FindContours找到的輪廓放到內(nèi)存里面。這里只是一個指針; 第三個參數(shù)是mode,可以為 /*?Contour?retrieval?modes?*/
enum
{
????CV_RETR_EXTERNAL=0,
????CV_RETR_LIST=1,
????CV_RETR_CCOMP=2,
????CV_RETR_TREE=3,
????CV_RETR_FLOODFILL=4
};
? ? 向FindContours說明需要的輪廓類型和希望的返回形式。也就是內(nèi)部輪廓的組織形式。其中顯然CV_RETR_TREE模式是最全的模式 ? 最后一個參數(shù)是輪廓如何被近似 ? //!?the?contour?approximation?algorithm
enum?{?CHAIN_APPROX_NONE??????=?1,
???????CHAIN_APPROX_SIMPLE????=?2,
???????CHAIN_APPROX_TC89_L1???=?3,
???????CHAIN_APPROX_TC89_KCOS?=?4
} ? 不過,如果我們是想獲得準確的值的話,這可能更多采用的是NONE,也就是顯示所有值。 書本內(nèi)容學完了,但是并沒有解決問題。因為我們需要的是想一個辦法,使得輪廓之間的關(guān)系能夠顯示出來。所以需要繼續(xù)挖掘文檔。 ? http://docs.opencv.org/3.1.0/d9/d8b/tutorial_py_contours_hierarchy.html Also, in the output, we got three arrays, first is the image, second is our contours, and one more output which we named as hierarchy (Please checkout the codes in previous articles). But we never used this hierarchy anywhere. Then what is this hierarchy and what is it for ? What is its relationship with the previous mentioned function argument ? Consider an example image below : 在這幅圖中(白色區(qū)域為有數(shù)據(jù)的區(qū)域,黑色為無數(shù)據(jù)),0,1,2是第一層,然后里面是3,3的里面是4和5。(2a表示是2的內(nèi)部)
?
In this image, there are a few shapes which I have numbered from 0-5. 2 and 2a denotes the external and internal contours of the outermost box.
Here, contours 0,1,2 are external or outermost. We can say, they are in hierarchy-0 or simply they are in same hierarchy level.
Next comes contour-2a. It can be considered as a child of contour-2 (or in opposite way, contour-2 is parent of contour-2a). So let it be in hierarchy-1. Similarly contour-3 is child of contour-2 and it comes in next hierarchy. Finally contours 4,5 are the children of contour-3a, and they come in the last hierarchy level. From the way I numbered the boxes, I would say contour-4 is the first child of contour-3a (It can be contour-5 also).
I mentioned these things to understand terms like same hierarchy level, external contour, child contour, parent contour, first child etc. Now let's get into OpenCV.
?Hierarchy Representation in OpenCV (層級關(guān)系)
So each contour has its own information regarding what hierarchy it is, who is its child, who is its parent etc. OpenCV represents it as an array of four values : **[Next, Previous, First_Child, Parent]**
*"Next denotes next contour at the same hierarchical level."*For eg, take contour-0 in our picture. Who is next contour in its same level ? It is contour-1. So simply put Next = 1. Similarly for Contour-1, next is contour-2. So Next = 2.
What about contour-2? There is no next contour in the same level. So simply, put Next = -1. What about contour-4? It is in same level with contour-5. So its next contour is contour-5, so Next = 5.
*"Previous denotes previous contour at the same hierarchical level."*It is same as above. Previous contour of contour-1 is contour-0 in the same level. Similarly for contour-2, it is contour-1. And for contour-0, there is no previous, so put it as -1.
*"First_Child denotes its first child contour."*There is no need of any explanation. For contour-2, child is contour-2a. So it gets the corresponding index value of contour-2a. What about contour-3a? It has two children. But we take only first child. And it is contour-4. So First_Child = 4 for contour-3a.
*"Parent denotes index of its parent contour."*It is just opposite of First_Child. Both for contour-4 and contour-5, parent contour is contour-3a. For contour-3a, it is contour-3 and so on.
Note4. RETR_TREE
And this is the final guy, Mr.Perfect. It retrieves all the contours and creates a full family hierarchy list. It even tells, who is the grandpa, father, son, grandson and even beyond... :).
For examle, I took above image, rewrite the code for cv2.RETR_TREE, reorder the contours as per the result given by OpenCV and analyze it. Again, red letters give the contour number and green letters give the hierarchy order.
imageTake contour-0 : It is in hierarchy-0. Next contour in same hierarchy is contour-7. No previous contours. Child is contour-1. And no parent. So array is [7,-1,1,-1].
Take contour-2 : It is in hierarchy-1. No contour in same level. No previous one. Child is contour-2. Parent is contour-0. So array is [-1,-1,2,0].
And remaining, try yourself. Below is the full answer:
1?>>> hierarchy 2?array([[[ 7, -1, 1, -1], 3? [-1, -1, 2, 0], 4? [-1, -1, 3, 1], 5? [-1, -1, 4, 2], 6? [-1, -1, 5, 3], 7? [ 6, -1, -1, 4], 8? [-1, 5, -1, 4], 9? [ 8, 0, -1, -1], 10? [-1, 7, -1, -1]]]) ? ? 修改后的代碼 ? #include?"stdafx.h" #include?"opencv2/highgui/highgui.hpp"#include?"opencv2/imgproc/imgproc.hpp"
#include?<iostream>
#include?<stdio.h>
#include?<stdlib.h>
#include?<math.h>
using?namespace?cv;
using?namespace?std;
//找到所提取輪廓的中心點
//在提取的中心小正方形的邊界上每隔周長個像素提取一個點的坐標,求所提取四個點的平均坐標(即為小正方形的大致中心)
Point?Center_cal(vector<vector<Point>?>?contours,int?i)
{
????int?centerx=0,centery=0,n=contours[i].size();
????centerx?=?(contours[i][n/4].x?+?contours[i][n*2/4].x?+?contours[i][3*n/4].x?+?contours[i][n-1].x)/4;
????centery?=?(contours[i][n/4].y?+?contours[i][n*2/4].y?+?contours[i][3*n/4].y?+?contours[i][n-1].y)/4;
????Point?point1=Point(centerx,centery);
????return?point1;
}
int?main(?int?argc,?char**?argv[]?)
{
????Mat?src?=?imread(?"e:/sandbox/qrcode.jpg",?1?);
????resize(src,src,Size(800,600));//標準大小
????Mat?src_gray;
????Mat?src_all=src.clone();
????Mat?threshold_output;
????vector<vector<Point>?>?contours,contours2;
????vector<Vec4i>?hierarchy;
????//預處理
????cvtColor(?src,?src_gray,?CV_BGR2GRAY?);
????blur(?src_gray,?src_gray,?Size(3,3)?);?//模糊,去除毛刺
????threshold(?src_gray,?threshold_output,?100,?255,?THRESH_OTSU?);
????//尋找輪廓?
????//第一個參數(shù)是輸入圖像?2值化的
????//第二個參數(shù)是內(nèi)存存儲器,FindContours找到的輪廓放到內(nèi)存里面。
????//第三個參數(shù)是層級,**[Next,?Previous,?First_Child,?Parent]**?的vector
????//第四個參數(shù)是類型,采用樹結(jié)構(gòu)
????//第五個參數(shù)是節(jié)點擬合模式,這里是全部尋找
????findContours(?threshold_output,?contours,?hierarchy,??CV_RETR_TREE,?CHAIN_APPROX_NONE,?Point(0,?0)?);
????//輪廓篩選
????int?c=0,ic=0,area=0;
????int?parentIdx=-1;
????for(?int?i?=?0;?i<?contours.size();?i++?)
????{
????????//hierarchy[i][2]?!=?-1?表示不是最外面的輪廓
????????if?(hierarchy[i][2]?!=?-1?&&?ic==0)
????????{
????????????parentIdx?=?i;?
????????????ic++;
????????}
????????else?if?(hierarchy[i][2]?!=?-1)
????????{
????????????ic++;
????????}
????????//最外面的清0
????????else?if(hierarchy[i][2]?==?-1)
????????{
????????????ic?=?0;
????????????parentIdx?=?-1;
????????}
????????//找到定位點信息
????????if?(?ic?>=?2)
????????{
????????????contours2.push_back(contours[parentIdx]);
????????????ic?=?0;
????????????parentIdx?=?-1;
????????}
????}
????//填充定位點
????for(int?i=0;?i<contours2.size();?i++)
????????drawContours(?src_all,?contours2,?i,??CV_RGB(0,255,0)?,?-1?);
????//連接定位點
????Point?point[3];
????for(int?i=0;?i<contours2.size();?i++)
????{
????????point[i]?=?Center_cal(?contours2,?i?);
????}
????
????line(src_all,point[0],point[1],Scalar(0,0,255),2);
????line(src_all,point[1],point[2],Scalar(0,0,255),2);
????line(src_all,point[0],point[2],Scalar(0,0,255),2);
?????
????imshow(?"結(jié)果",?src_all?);
????waitKey(0);
????return(0);
}
總結(jié)
以上是生活随笔為你收集整理的OpenCV使用FindContours进行二维码定位的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 东京见闻:快速走红日本市场 阿里云的三大
- 下一篇: 一个使用 asyncio 协程的网络爬虫