- 💂 个人主页:风间琉璃
- 🤟 版权: 本文由【风间琉璃】原创、在CSDN首发、需要转载请联系博主
- 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦
目录
一、识别原理
1.二维码定位
2.颜色识别
二、部分源码
一、识别原理
二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的、黑白相间的、记录数据符号信息的图形;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理.
它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。常见的二维码为QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。深色模块表示二进制"1",浅色模块表示二进制"0"。
健康码是申请人通过填报个人信息健康状况、旅游史、居住地、及是否接触过疑似或确诊肺炎病患等问题自动生成二维码,动态显示个人疫情风险等级。健康码又在二维码的基础上发展而来,健康码分红、黄、绿三种颜色,包含这三种颜色的色块表示二进制’’1”。
健康码的识别一般分为两步:健康码定位和健康码颜色识别。首先判断图片中是否含有二维码,若有则找到健康码的位置。然后将含有健康码的部分进行颜色识别,即可判断健康码是红码、黄码、绿码中的哪一种。
1.二维码定位
健康码码有三个形状相同的位置探测图形, 在没有旋转的情况下, 这三个位置探测图形分别位于健康码码符号的左上角、 右上角和左下角(如下图所示)。 三个位置探测图形共同组成图像图形。
每个位置探测图形可以看作是由3个重叠的同心的正方形组成,它们分别为77个深色模块、55个浅模块和3*3个深色模块。位置探测图形的模块宽度比为1:1:3:1:1。这种1:1:3:1:1 的宽度比例特征在图像的其他位置出现的可能性很小,故可以将此作为位置探测图形的扫描特征。基于此特征,当一条直线上被黑白相间地截为1:1:3:1:1时,可以认为该直线穿过了位置探测图形。并且该扫描特征不受图像倾斜的影响。
寻找健康码的三个角的定位角点,需要对图片进行平滑滤波,二值化,寻找轮廓,筛选轮廓中有两个子轮廓的特征,从筛选后的轮廓中找到面积最接近的3个即是二维码的定位角点。然后判断3个角点处于什么位置,主要用来对图片进行透视校正或者仿射校正。需要判断三个角点围成的三角形的最大的角就是二维码左上角的点。然后根据这个角的两个边的角度差确定另外两个角点的左下和右上位置。根据这些特征识别二维码的范围。
二维码定位流程图如下图所示:
2.颜色识别
找到健康码的位置后,裁剪该区域的图片,得到只含有健康码的图片进行颜色识别,可以避免因为图片中其他部分颜色从而导致识别结果错误。这里的颜色识别不是识别经过灰度化、二值化处理的图片,而是从健康码定位得到的四个顶点坐标,在原图进行裁剪得到的。由于裁剪后的健康码只有绿、黄、红、白色(衬底)这四种颜色。针对这种两者颜色组合而言,最简单粗暴的颜色识别是计算出健康码中三种颜色的像素个数,那个颜色像素点最多便是那种颜色的健康码(白色这里直接忽略不计)。
在OpenCV中图像颜色的处理一般不在RGB域,因为各个颜色连接程度比较大,不易区分。而图片处理一般采用HSV域,有利于图片分割。相对于RGB空间,HSV空间能够非常直观的表达色彩的明暗,色调,以及鲜艳程度,方便进行颜色之间的对比。所以在颜色检测时,选用HSV图像。HSV是相对RGB的另一种颜色表示方式,它相对RGB而言,是一种比较直观的颜色模型。其中颜色的参数分别是:色调(H),饱和度(S),明度(V)。一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对应的HSV分量需要给定一个严格的范围,如下表所示。
黑 | 灰 | 白 | 红 | 橙 | 黄 | 绿 | 青 | 蓝 | 紫 | ||
hmin | 0 | 0 | 0 | 0 | 156 | 11 | 26 | 35 | 78 | 100 | 125 |
hamx | 180 | 180 | 180 | 10 | 180 | 25 | 34 | 77 | 99 | 124 | 155 |
smin | 0 | 0 | 0 | 43 | 43 | 43 | 43 | 43 | 43 | 43 | |
smax | 255 | 43 | 30 | 255 | 255 | 255 | 255 | 255 | 255 | 255 | |
vmin | 0 | 46 | 221 | 46 | 46 | 46 | 46 | 46 | 46 | 46 | |
vmax | 46 | 220 | 255 | 255 | 255 | 255 | 255 | 255 | 255 | 255 |
确定了各个颜色在HSV中的范围后,只需要遍历健康码中每一个像素点,判断属于那个颜色范围即可。针对健康码这种双颜色的,红、黄、绿那个像素点的数量多,便基本可以确定健康码的颜色。
二、部分源码
二维码定位主要使用OpenCV在对象检测模块中QRCodeDetector有关API分别实现二维码检测。
①定位结果的detect()函数
bool cv::QRCodeDetector::detect(InputArray img, OutputArray points)
img:待检测是否含有QR二维码的灰度图像或者彩色图像
points:包含QR二维码的最小区域四边形的4个顶点坐标,即二维码的4个顶点
②定位结果解码的decode()函数
std::string cv::QRCodeDetector::decode(InputArray img, InputArray points, OutputArray straight_qrcode = noArray())
img:含有QR二维码的图像
points:包含QR二维码的最小区域的四边形的四个顶点
straight_qrcode:经过校正和二值化的QR二维码
③识别并解码的detectAndDecode()函数
std::string cv::QRCodeDetector::decode(InputArray img, OutputArray points = noArray(), OutputArray straight_qrcode = noArray())
img:含有QR二维码的图像
points:包含QR二维码的最小区域的四边形的四个顶点
straight_qrcode:经过校正和二值化的QR二维码
以下是定位二维码,并且绘制二维码的边框,为后面图片裁剪作准备。
Qrcode::Qrcode(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Qrcode)
{
ui->setupUi(this);
int red=0,green=0,yellow=0;
Mat img = imread("/home/qt/qr/6.jpg");
if (img.empty())
{
cout << "image error" << endl;
}
Mat gray;
cvtColor(img, gray, COLOR_BGR2GRAY);
QRCodeDetector QRdetecter;
vector<Point> points;
bool isQRcode;
string color;
Mat roi; //颜色识别区域
isQRcode = QRdetecter.detect(gray, points); //识别二维码
if (isQRcode)
{
//绘制二维码的边框
for (int i = 0; i < points.size(); i++)
{
if (i == points.size() - 1)
{
line(img, points[i], points[0], Scalar(0, 0, 0), 2, 8);
break;
}
line(img, points[i], points[i + 1], Scalar(0, 0, 0), 2, 8);
}
}
else
{
qDebug()<<"无法识别二维码,请确认图像时候含有二维码";
}
}
以下是裁剪出二维码,然后进行颜色识别。
//颜色识别
Rect m_select = Rect(points[0].x,points[0].y,points[1].x-points[0].x,points[1].x-points[0].x); //提取二维码
roi= img(m_select);
Mat matHsv;
cvtColor(roi, matHsv, COLOR_BGR2HSV);
for (int i = 0; i < roi.rows; i++)
{
for (int j = 0; j < roi.cols; j++)
{
vector<int> colorVec;
colorVec.push_back(matHsv.at<Vec3b>(i,j)[0]);
colorVec.push_back(matHsv.at<Vec3b>(i,j)[1]);
colorVec.push_back(matHsv.at<Vec3b>(i,j)[2]);
if (((colorVec[0] >= 0 && colorVec[0] <= 10) || (colorVec[0] >= 156 && colorVec[0] <= 180)) && (colorVec[1] >= 43 && colorVec[1] <= 255) && (colorVec[2] >= 46 && colorVec[2] <= 255))
{
red += 1;
}
else if ((colorVec[0] >= 26 && colorVec[0] <= 34) && (colorVec[1] >= 43 && colorVec[1] <= 255) && (colorVec[2] >= 46 && colorVec[2] <= 255))
{
yellow += 1;
}
else if ((colorVec[0] >= 35 && colorVec[0] <= 77) && (colorVec[1] >= 43 && colorVec[1] <= 255) && (colorVec[2] >= 46 && colorVec[2] <= 255))
{
green += 1;
}
}
}
int max = maxThree(red, yellow, green);
if (yellow == max)
{
color = "yellow";
}
else if (green == max)
{
color = "green";
}
else if (red == max)
{
color = "red";
}
else
{
color = " ";
}
实际上就是遍历健康码中每一个像素点并判断该颜色属于那个颜色范围,然后红、黄、绿那个像素点的数量多,便基本可以确定健康码的颜色。
结束语
感谢你观看我的文章呐~本次航班到这里就结束啦 🛬
希望本篇文章有对你带来帮助 🎉,有学习到一点知识~
躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。
最后,博主要一下你们的三连呀(点赞、评论、收藏),不要钱的还是可以搞一搞的嘛~
不知道评论啥的,即使扣个666也是对博主的鼓舞吖 💞 感谢 💐