QR code 定位
QR code is now widely used in our daily life because it is small and can contain lots of information. QR code can also be used in positioning as it usually has three blocks to determine the position of QR code. Just like the following one, top left corner, top right corner and lower left corner are corresponding blocks to confirm the location of QR code. Of course there are other areas containing different information, but I won’t use them when positioning the location of QR code.
Contours extraction
We should do some pretreatment before contours extraction. First transfer the BGR image to gray image, then use Gaussian blur to suppress the noise, and use Canny algorithm to obtain the edge image. Let’s see the result of Canny. Result is good and we can easily distinguish the edge of QR code.
Now we are going to proceed contours extractions. As edges have been detected, contours are easily confirmed because contours are just closed loop which is made of certain edges. In OpenCV we just need to use ‘findContours’ function to do the job. But there are lots of contours in QR code and how can we just find out those three blocks to determine the location of QR code?
Position blocks extraction
To find out the position blocks from so many contours in QR code, the easiest way is to distinguish them by the nested layers. What does that mean? If there is no contour in the area which is surrounded by this contour, then we can say there is no nested layer of this contour. But if there is one contour being surrounded by another contour, then the nested layer of the latter is one. For example, the nested layers of out contour is three because there are three contours surrounded by it.
So if we observe the positioning blocks of QR code, we can find that the outmost rectangle usually have 4, 5 or 6 nested layer which is much more than other contours. We have to notice that one rectangle in findContours function is seen as two contours which means both two sides of a loop is considered as a contour. But how do know the nested layer of each contour? Well, findContours function also give us the answer. This function will return back a tree structure which contains the child and father information. The child means the surrounded contour by this contour and father means this contour is surrounded by another contour. So with this tree structure, we can easily determine the nested layer of one contour. Then we can give a threshold such as 4, 5 or 6 and determined the nested layers above this value is considered as positioning location. The exact number of threshold is influenced by the quality of edge image. If the edge image is very clear, then this threshold can be 6. On the opposite threshold can be 4 because the minimum contour may not be recognized if the image is too vague. The result is shown as below, and it meets our hope.
QR code Center positioning
Although the positioning blocks have been extracted, we still need to confirm the center of QR code to quantify the location of QR code. There are usually two ways to do that:
- Find the minimum rectangle which contains these three QR code, and if we have the information of four points of a rectangle, the center is easily obtained.
- Find the farthest distance between the points on the three positioning blocks. Then the line which is formed by corresponding pair of points is exactly diagonal of QR code, and the center is midpoint of this diagonal.
In ordinary situation these two ways can be both useful and the former solution can be more easily to be realized by OpenCV. But if the QR code is tilt with some angle, the former way can be wrong because the rectangle can be rotated. Let’s see two examples:
So I decided to take the second solution though there is no ready-made solution. It’s a classical getting maximum value problem and we can use four circulation to get our purpose. Now let’s see the result, the center is surrounded by a circle to emphasize and the corresponding two farthest points are also marked.
Some other results:
Code
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(){
Mat image_input=imread("1.jpg",IMREAD_UNCHANGED);
Mat image_gray;
Mat image_Gaussian;
Mat image_final;
cvtColor(image_input,image_gray,COLOR_BGR2GRAY);
image_final=image_input;
GaussianBlur(image_gray,image_Gaussian,Size(5,5),0,0);
imshow("image_Gaussian",image_Gaussian);
imshow("image_color",image_input);
Mat image_canny;
Canny(image_Gaussian,image_canny,10,50,3,true);
imshow("image_canny",image_canny);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
Mat image_contours=Mat::zeros(image_canny.size(),CV_8UC1);
Mat Contours=Mat::zeros(image_canny.size(),CV_8UC1);
findContours(image_canny,contours,hierarchy,RETR_TREE,CHAIN_APPROX_NONE);
//find out all outlines
int k,c;
vector<int> edge;
for(int i=0;i<contours.size();i++){
k=i;
c=1;
while(hierarchy[k][2]!=-1){
k=hierarchy[k][2];
c++;
}
if(c>=4){
edge.push_back(i);
}
}
//find out the outline whose nested layers is more than 5 which will be the location code of QR code
cout<<edge.size()<<endl;
for(int i=0;i<edge.size();i++){
cout<<edge[i]<<endl;
}
int maxsum=0,minsum=1000000,maxdis=0;
Point pt1,pt2,center;
for(int i=0;i<edge.size();i++){
for(int j=i+1;j<edge.size();j++){
for(int m=0;m<contours[edge[i]].size();m++){
for(int n=0;n<contours[edge[j]].size();n++){
int dis=pow(contours[edge[i]][m].x-contours[edge[j]][n].x,2)+pow(contours[edge[i]][m].y-contours[edge[j]][n].y,2);
cout<<dis<<endl;
if(dis>maxdis){
maxdis=dis;
pt1=contours[edge[i]][m];
pt2=contours[edge[j]][n];
cout<<pt1.x<<' '<<pt1.y<<endl;
cout<<pt2.x<<' '<<pt2.y<<endl;
}
}
}
}
}
//find out the most farthest point of location code
cout<<pt1.x<<' '<<pt1.y<<endl;
cout<<pt2.x<<' '<<pt2.y<<endl;
center.x=(pt1.x+pt2.x)/2;
center.y=(pt1.y+pt2.y)/2;
//center of two farthest points is the center of QR code
circle(image_final,pt1,5,Scalar(0,0,255),0.6,8);
circle(image_final,pt2,5,Scalar(0,0,255),0.6,8);
circle(image_final,Point(center.x,center.y),5,Scalar(0,0,255),2,8);
for(int j=0;j<edge.size();j++){
/*for(int j=0;j<contours[i].size();j++){
Point P=Point(contours[i][j].x,contours[i][j].y);
Contours.at<uchar>(P)=255;
}*/
// cout<<i<<' '<<hierarchy[i]<<endl;
int i=edge[j];
drawContours(image_final,contours,i,Scalar(0,0,255),2,8);
}
// draw the location code;
// imshow("image_contours",image_contours);
// imshow("Contours",Contours);
namedWindow("image_final",WINDOW_NORMAL);
cvResizeWindow("image_final",1000,1000);
imshow("image_final",image_final);
waitKey(0);
}