preface
This article will use OpenCV C + + for QR code detection.
1, QR code detection
Firstly, we must preprocess the image and extract the image contour through gray, filtering, binarization and other operations. Here I also added morphological operations to eliminate noise and effectively connect rectangular areas.
Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat blur; GaussianBlur(gray, blur, Size(3, 3), 0); Mat bin; threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); //Eliminate edge burr through Size (5,1) open operation Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1)); Mat open; morphologyEx(bin, open, MORPH_OPEN, kernel); //Through the Size (21,1) closed operation, the rectangular regions can be effectively connected to facilitate the extraction of rectangular regions Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1)); Mat close; morphologyEx(open, close, MORPH_CLOSE, kernel1);
The figure shows the effect obtained after a series of image processing. Then we need to extract the contour of the image to find the rectangular area where the QR code is located.
//Use RETR_EXTERNAL find outermost contour vector<vector<Point>>MaxContours; findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i < MaxContours.size(); i++) { Mat mask = Mat::zeros(src.size(), CV_8UC3); mask = Scalar::all(255); double area = contourArea(MaxContours[i]); //Find the rectangular area where the QR code is located through the area threshold if (area > 6000 && area < 100000) { //Calculate minimum circumscribed rectangle RotatedRect MaxRect = minAreaRect(MaxContours[i]); //Calculate the minimum external rectangle aspect ratio double ratio = MaxRect.size.width / MaxRect.size.height; if (ratio > 0.8 && ratio < 1.2) { Rect MaxBox = MaxRect.boundingRect(); //rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2); //Cut out the rectangular area from the original image Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br())); ROI.copyTo(mask(MaxBox)); ROI_Rect.push_back(mask); } } }
From the following code snippet, we can find out the rectangular area where the QR code is located, and pull out these areas and save them for the following identification work.
//Find the rectangular area where the QR code is located void Find_QR_Rect(Mat src, vector<Mat>&ROI_Rect) { Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat blur; GaussianBlur(gray, blur, Size(3, 3), 0); Mat bin; threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); //Eliminate edge burr through Size (5,1) open operation Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1)); Mat open; morphologyEx(bin, open, MORPH_OPEN, kernel); //Through the Size (21,1) closed operation, the rectangular regions can be effectively connected to facilitate the extraction of rectangular regions Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1)); Mat close; morphologyEx(open, close, MORPH_CLOSE, kernel1); //Use RETR_EXTERNAL find outermost contour vector<vector<Point>>MaxContours; findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i < MaxContours.size(); i++) { Mat mask = Mat::zeros(src.size(), CV_8UC3); mask = Scalar::all(255); double area = contourArea(MaxContours[i]); //Find the rectangular area where the QR code is located through the area threshold if (area > 6000 && area < 100000) { //Calculate minimum circumscribed rectangle RotatedRect MaxRect = minAreaRect(MaxContours[i]); //Calculate the minimum external rectangle aspect ratio double ratio = MaxRect.size.width / MaxRect.size.height; if (ratio > 0.8 && ratio < 1.2) { Rect MaxBox = MaxRect.boundingRect(); //rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2); //Cut out the rectangular area from the original image Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br())); ROI.copyTo(mask(MaxBox)); ROI_Rect.push_back(mask); } } } }
As shown in the figure, this is the QR code rectangle found. Only one of them is shown here.
2, QR code recognition
1. Find the contour hierarchy through findContours
//Used to store the detected QR code vector<vector<Point>>QR_Rect; //Traverse all found rectangular areas for (int i = 0; i < ROI_Rect.size(); i++) { Mat gray; cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY); Mat bin; threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); //Through hierarchy, RETR_TREE finds the hierarchical relationship between contours vector<vector<Point>>contours; vector<Vec4i>hierarchy; findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); //Parent profile index int ParentIndex = -1; int cn = 0; //Three "loops" for storing QR code rectangles vector<Point>rect_points; for (int i = 0; i < contours.size(); i++) { //hierarchy[i][2] != -1 indicates that the contour has sub contour cn, which is used to count the contour in "back" if (hierarchy[i][2] != -1 && cn == 0) { ParentIndex = i; cn++; } else if (hierarchy[i][2] != -1 && cn == 1) { cn++; } else if (hierarchy[i][2] == -1) { //initialization ParentIndex = -1; cn = 0; } //If the contour has sub contours and there are level 2 sub contours, it is deemed that 'back' is found if (hierarchy[i][2] != -1 && cn == 2) { drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1); RotatedRect rect; rect = minAreaRect(contours[ParentIndex]); rect_points.push_back(rect.center); } } }
The overall idea of the above code segment is: firstly, contour detection is carried out through image preprocessing,
Through hierarchy, RETR_TREE finds the hierarchical relationship between contours. Judge whether the contour has sub contour according to whether hierarchy[i][2] is - 1. If the contour has sub contours, several sub contours are counted. If the contour has sub contours and there are level 2 sub contours, it is considered that 'back' is found. On the hierarchical relationship of the outline, you can find the information on Baidu and understand the principle.
//Identify whether the found rectangular area is a QR code int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect) { //Used to store the detected QR code vector<vector<Point>>QR_Rect; //Traverse all found rectangular areas for (int i = 0; i < ROI_Rect.size(); i++) { Mat gray; cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY); Mat bin; threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); //Through hierarchy, RETR_TREE finds the hierarchical relationship between contours vector<vector<Point>>contours; vector<Vec4i>hierarchy; findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); //Parent profile index int ParentIndex = -1; int cn = 0; //Three "loops" for storing QR code rectangles vector<Point>rect_points; for (int i = 0; i < contours.size(); i++) { //hierarchy[i][2] != -1 indicates that the contour has sub contour cn, which is used to count the contour in "back" if (hierarchy[i][2] != -1 && cn == 0) { ParentIndex = i; cn++; } else if (hierarchy[i][2] != -1 && cn == 1) { cn++; } else if (hierarchy[i][2] == -1) { //initialization ParentIndex = -1; cn = 0; } //If the contour has sub contours and there are level 2 sub contours, it is deemed that 'back' is found if (hierarchy[i][2] != -1 && cn == 2) { drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1); RotatedRect rect; rect = minAreaRect(contours[ParentIndex]); rect_points.push_back(rect.center); } } //Connect the 'return' found for (int i = 0; i < rect_points.size(); i++) { line(canvas, rect_points[i], rect_points[(i + 1) % rect_points.size()], Scalar::all(255), 5); } QR_Rect.push_back(rect_points); } return QR_Rect.size(); }
From the above code segment, we can identify the QR code. The effect is shown in the figure.
3, 2D code drawing
//Frame the location of the QR code Mat gray; cvtColor(canvas, gray, COLOR_BGR2GRAY); vector<vector<Point>>contours; findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); Point2f points[4]; for (int i = 0; i < contours.size(); i++) { RotatedRect rect = minAreaRect(contours[i]); rect.points(points); for (int j = 0; j < 4; j++) { line(src, points[j], points[(j + 1) % 4], Scalar(0, 255, 0), 2); } }
The final effect is shown in the figure.
#include<iostream> #include<opencv2/core.hpp> #include<opencv2/imgproc.hpp> #include<opencv2/highgui.hpp> using namespace std; using namespace cv; //Find the rectangular area where the QR code is located void Find_QR_Rect(Mat src, vector<Mat>&ROI_Rect) { Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY); Mat blur; GaussianBlur(gray, blur, Size(3, 3), 0); Mat bin; threshold(blur, bin, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); //Eliminate edge burr through Size (5,1) open operation Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 1)); Mat open; morphologyEx(bin, open, MORPH_OPEN, kernel); //Through the Size (21,1) closed operation, the rectangular regions can be effectively connected to facilitate the extraction of rectangular regions Mat kernel1 = getStructuringElement(MORPH_RECT, Size(21, 1)); Mat close; morphologyEx(open, close, MORPH_CLOSE, kernel1); //Use RETR_EXTERNAL find outermost contour vector<vector<Point>>MaxContours; findContours(close, MaxContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); for (int i = 0; i < MaxContours.size(); i++) { Mat mask = Mat::zeros(src.size(), CV_8UC3); mask = Scalar::all(255); double area = contourArea(MaxContours[i]); //Find the rectangular area where the QR code is located through the area threshold if (area > 6000 && area < 100000) { //Calculate minimum circumscribed rectangle RotatedRect MaxRect = minAreaRect(MaxContours[i]); //Calculate the minimum external rectangle aspect ratio double ratio = MaxRect.size.width / MaxRect.size.height; if (ratio > 0.8 && ratio < 1.2) { Rect MaxBox = MaxRect.boundingRect(); //rectangle(src, Rect(MaxBox.tl(), MaxBox.br()), Scalar(255, 0, 255), 2); //Cut out the rectangular area from the original image Mat ROI = src(Rect(MaxBox.tl(), MaxBox.br())); ROI.copyTo(mask(MaxBox)); ROI_Rect.push_back(mask); } } } } //Identify whether the found rectangular area is a QR code int Dectect_QR_Rect(Mat src,Mat &canvas,vector<Mat>&ROI_Rect) { //Used to store the detected QR code vector<vector<Point>>QR_Rect; //Traverse all found rectangular areas for (int i = 0; i < ROI_Rect.size(); i++) { Mat gray; cvtColor(ROI_Rect[i], gray, COLOR_BGR2GRAY); Mat bin; threshold(gray, bin, 0, 255, THRESH_BINARY_INV|THRESH_OTSU); //Through hierarchy, RETR_TREE finds the hierarchical relationship between contours vector<vector<Point>>contours; vector<Vec4i>hierarchy; findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE); //Parent profile index int ParentIndex = -1; int cn = 0; //Three "loops" for storing QR code rectangles vector<Point>rect_points; for (int i = 0; i < contours.size(); i++) { //hierarchy[i][2] != -1 indicates that the contour has sub contour cn, which is used to count the contour in "back" if (hierarchy[i][2] != -1 && cn == 0) { ParentIndex = i; cn++; } else if (hierarchy[i][2] != -1 && cn == 1) { cn++; } else if (hierarchy[i][2] == -1) { //initialization ParentIndex = -1; cn = 0; } //If the contour has sub contours and there are level 2 sub contours, it is deemed that 'back' is found if (hierarchy[i][2] != -1 && cn == 2) { drawContours(canvas, contours, ParentIndex, Scalar::all(255), -1); RotatedRect rect; rect = minAreaRect(contours[ParentIndex]); rect_points.push_back(rect.center); } } //Connect the 'return' found for (int i = 0; i < rect_points.size(); i++) { line(canvas, rect_points[i], rect_points[(i + 1) % rect_points.size()], Scalar::all(255), 5); } QR_Rect.push_back(rect_points); } return QR_Rect.size(); } int main() { Mat src = imread("6.png"); if (src.empty()) { cout << "No image data!" << endl; system("pause"); return 0; } vector<Mat>ROI_Rect; Find_QR_Rect(src, ROI_Rect); Mat canvas = Mat::zeros(src.size(), src.type()); int flag = Dectect_QR_Rect(src, canvas, ROI_Rect); //imshow("canvas", canvas); if (flag <= 0) { cout << "Can not detect QR code!" << endl; system("pause"); return 0; } cout << "Detected" << flag << "A QR code." << endl; //Frame the location of the QR code Mat gray; cvtColor(canvas, gray, COLOR_BGR2GRAY); vector<vector<Point>>contours; findContours(gray, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); Point2f points[4]; for (int i = 0; i < contours.size(); i++) { RotatedRect rect = minAreaRect(contours[i]); rect.points(points); for (int j = 0; j < 4; j++) { line(src, points[j], points[(j + 1) % 4], Scalar(0, 255, 0), 2); } } imshow("source", src); waitKey(0); destroyAllWindows(); system("pause"); return 0; }
summary
In this paper, OpenCV C + + is used for QR code detection. The key steps are as follows.
1. Image preprocessing, filter out the rectangular area where the QR code is located, and pick out the area for subsequent recognition.
2. The contour of the screened rectangular area is detected to judge their previous hierarchical relationship, so as to identify the QR code.
3. Finally, according to the detected QR code "back" word, it can be drawn.
The above is my overall idea of QR code detection and recognition. Welcome to exchange and learn together. If there are deficiencies, you are welcome to correct.