前言
刚刚学完OpenCv的基础部分,上手来一个简单的练习,定位识别二维码。
二维码有三个定位点,通过定位三个定位点来定位二维码的位置与大小。
处理步骤
- 灰度处理
- 滤波
- 二值化
- 发现轮廓,并导出子轮廓大于两个的父轮廓
- 筛选出三个轮廓,条件:有一个垂直角
-
计算倾斜角度并原图,将二维码抠出
成果展示
将原图标记出定位点:
输出图像均为
由输出可看出,任何角度的二维码,都可以正确的识别并取出。
代码展示
#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;
int threshold_value = 80;
int threshold_max = 255;
Mat src, dst;
int main()
{
src = imread("C:/Users/LBJ/Desktop/二维码识别/二维码1.png");
if (!src.data)
{
cout << "The iamge is empty" << endl;
return -1;
}
src.copyTo(dst);
namedWindow("output_image", WINDOW_AUTOSIZE);
namedWindow("Input_Image", WINDOW_AUTOSIZE);
cvtColor(src, src, CV_BGR2GRAY);
blur(src, src, Size(3, 3), Point(-1, -1));;
Mat bin_output;
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
vector<Point> convexs; //用来储存三个标记框的点集,画最小矩形,即为二维码的区域
vector<vector<Point>> Qrprint;
threshold(src, bin_output, threshold_value, threshold_max, THRESH_BINARY);
/**************************************通过轮廓信息查找二维码的标记框**********************************************/
findContours(bin_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
for (size_t i = 0; i < contours.size(); i++)
{
int j = i, temp = 0;
while (hierachy[j][2] != -1)
{
temp++;
j = hierachy[j][2];
}
if (temp >= 2)
{
Qrprint.push_back(contours[i]); //将识别到的定位点放入数组中
drawContours(dst, contours, i, Scalar(0, 0, 255), -1, 8, hierachy, 0, Point(0, 0));
cout << contours[i] << endl;
}
}
/********************************************************************************************************************/
/*************************通过两两垂直的关系,筛选出正确的三个识别框,储存子Q中************************************************/
vector<RotatedRect> Qr_Rect(Qrprint.size());
vector<Point> Center_Point(Qrprint.size());
for (size_t i = 0; i < Qrprint.size(); i++)
{
Qr_Rect[i] = minAreaRect(Qrprint[i]);
Center_Point[i] = Qr_Rect[i].center;
circle(dst, Center_Point[i], 5, Scalar(0, 255, 0), -1, 8);
}
int Q[3];
for (size_t i = 0; i < Qrprint.size(); i++)
{
for (size_t j = 0; j < Qrprint.size(); j++)
{
for (size_t k = 0; k < Qrprint.size(); k++)
{
if ( i!=j && i!=k && j!=k )
{
double k1, k2, k3;
double dx, dy;
dx = Center_Point[i].x - Center_Point[j].x;
dy = Center_Point[i].y - Center_Point[j].y;
k1 = dy / dx;
dx = Center_Point[i].x - Center_Point[k].x;
dy = Center_Point[i].y - Center_Point[k].y;
k2 = dy / dx;
dx = Center_Point[j].x - Center_Point[k].x;
dy = Center_Point[j].y - Center_Point[k].y;
k3 = dy / dx;
if ((k1*k2 > -1.1&&k1*k2 < -0.9)) {
Q[0] = i; Q[1] = j; Q[2] = k;
}
else if ((k1*k3 > -1.1&&k1*k3 < -0.9)) {
Q[0] = j; Q[1] = i; Q[2] = k;
}
else if ((k3*k2 > -1.1&&k3*k2 < -0.9)) {
Q[0] = k; Q[1] = j; Q[2] = i;
}
else { continue; }
//q1储存了直角点的信息
}
}
}
}
/**************************************在原图里标记出二维码的位置***************************************/
for (size_t i = 0; i < 3; i++)
{
convexs.insert(convexs.end(), Qrprint[Q[i]].begin(), Qrprint[Q[i]].end());
}
Point2f ptr[4];
RotatedRect rect = minAreaRect(convexs);
rect.points(ptr);
for (int i = 0; i < 4; i++)
{
line(dst, ptr[i], ptr[(i + 1) % 4], Scalar(0, 255, 0), 5, 8);
}
/**************************通过直角点位置计算旋转角度并且将二维码抠出********************************/
double angel = rect.angle;; //计算旋转角度
if (Center_Point[Q[0]].x< Center_Point[Q[1]].x && Center_Point[Q[0]].x < Center_Point[Q[2]].x)
{
}
else if (Center_Point[Q[0]].y > Center_Point[Q[1]].y && Center_Point[Q[0]].y > Center_Point[Q[2]].y)
{
angel -= 90;
}
else if (Center_Point[Q[0]].x > Center_Point[Q[1]].x && Center_Point[Q[0]].x > Center_Point[Q[2]].x)
{
angel -= 180;
}
else if (Center_Point[Q[0]].y < Center_Point[Q[1]].y && Center_Point[Q[0]].y < Center_Point[Q[2]].y)
{
angel -= 270;
}
Mat Rotation = getRotationMatrix2D(rect.center, angel, 1.0);
warpAffine(src, src, Rotation, src.size());
Mat Result = src(Rect(rect.center.x - (rect.size.width / 2), rect.center.y - (rect.size.height / 2), rect.size.width, rect.size.height));
imshow("Input_Image", dst);
imshow("output_image", Result);
waitKey(0);
return 0;
}
可优化部分
1、识别二维码三个定位点的方法是寻找子轮廓数大于二的父轮廓,改成寻找子轮廓数大于二的轮廓的最小子轮廓,效果可能会好一点。
2、对于畸变后的二维码无法识别,后期可以尝试改进。