Opencv 4.0之后,加入了Qrcode定位和解析。也可以只定位,解析部分用zBar等实现
- 利用QRCodeDetector ::detect进行定位,结合透视变换进行梯形骄正
#include "opencv2/objdetect.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/imgcodecs.hpp"
#include <string>
#include <iostream>
using namespace std;
using namespace cv;
/**
* @brief 解析图片,并标记二维码位置
* @note
* @param input: 输入图片
* @retval
*/
int runQR(Mat input)
{
QRCodeDetector qrcode;
vector<Point> corners;
vector<cv::String> decode_info;
corners.clear();
decode_info.clear();
// 开始解码
TickMeter timer;
timer.start();
// 多个扫描
bool result_detection = qrcode.detectAndDecodeMulti(input, decode_info, corners);
// cout << "result_detection:" << result_detection << endl;
// 单个扫描
// string code_detection = qrcode.detectAndDecode(input, corners);
// cout << "code_detection:" << code_detection << endl;
timer.stop();
cout << "Decoder time: " << timer.getTimeSec()*1000.00 << "ms" << endl;
if (result_detection > 0){
// cout << "decode_info.size():" << decode_info.size() <<endl;
// 将1维向量转为4维
vector<vector<Point>> corners2;
for (size_t i = 0; i < corners.size(); i+=4)
{
vector<Point> temp(corners.begin()+i,corners.begin()+i+4);
// cout << "temp:" << temp << endl;
corners2.push_back(temp);
}
// cout << "corners2:" << corners2.size() << endl;
// 显示二维码定位
for (size_t i = 0; i < corners2.size(); i++)
{
Mat src = input.clone();
drawContours(src,corners2,i,Scalar(0,0,255),2);
imshow("QR dst", src);
}
// 显示识别结果
for (size_t i = 0; i < decode_info.size(); i++)
{
if(!decode_info[i].empty())
cout << "Get QRcode:" << decode_info[i] <<endl;
else
cout << "err" << endl;
}
}else{
cout << "decoder err" << endl;
return -1;
}
return 0;
}
// 计算两点间距
double get_distance(Point2f pointA,Point2f pointB )
{
// 勾股定理计算距离,a方+b方=c方
double distance;
// 平方和 powf平方运算
distance = powf((pointA.x - pointB.x),2) + powf((pointA.y - pointB.y),2);
// 平方根
distance = sqrtf(distance);
return distance;
}
/**
* @brief Opencv矫正二维码
* @note
* @param input:
* @retval
*/
int runQR_dir(Mat input)
{
QRCodeDetector qrcode;
vector<Point> corners;
corners.clear();
// 开始定位
bool result_detection = qrcode.detect(input, corners);
// bool result_detection = qrcode.detectMulti(input, corners);
cout << "result_detection:" << result_detection << endl;
if(result_detection <= 0){
cout << "decoder err" << endl;
return -1;
}
// 如果用detectMulti,则只处理识别到的第一个二维码,多余的忽略。。。
Mat src = input.clone();
vector<vector<Point>> corners2;
for (size_t i = 0; i < corners.size(); i+=4){
vector<Point> temp(corners.begin()+i,corners.begin()+i+4);
corners2.push_back(temp);
}
drawContours(src,corners2,0,Scalar(0,0,255),2);
// 获取最小外接矩形
RotatedRect rect = minAreaRect(corners);//外接矩形
// 画出最小外接矩形
Point2f point_rect[4];
rect.points(point_rect);//外接矩形的4个顶点
for (int i = 0; i < 4; i++)
line(src, point_rect[i], point_rect[(i + 1) % 4], Scalar(255, 0, 0));
cout << "rect.angle:" << rect.angle << endl;
// 旋转原始图像 (此处不用旋转)
// if(rect.angle != 0){
// Point2f center = rect.center;//外接矩形中心点坐标
// // 获取旋转矩阵
// Mat M = getRotationMatrix2D(center, rect.angle, 1.0);//求旋转矩阵
// Mat warp_image;
// // 仿射变换,实现图形旋转
// warpAffine(src, warp_image, M, src.size());
// imshow("warp_image", warp_image);
// // 根据外接矩阵大小,截取图像
// Mat result = warp_image(Rect(center.x - (rect.size.width / 2), center.y - (rect.size.height/2), rect.size.width, rect.size.height));//提取ROI
// imshow("result", result);
// }
Point2f point_src[4];
// 将二维码边框顶点,和最小矩阵定点,一一对应,按照最小距离方式
for (size_t j = 0; j < 4; j++){
double mini = 99999999;
// 找出距离最近的点,对应起来
for (size_t k = 0; k < 4; k++){
double distence = get_distance(point_rect[j],corners[k]);
if( distence < mini){
mini = distence;
point_src[j] = corners[k];
}
}
}
// 透视变换 实现梯形矫正
Mat dst;
// 计算变换矩阵,由二维码顶点————>变换为最小矩阵顶点
Mat M = getPerspectiveTransform(point_src, point_rect);
warpPerspective(input, dst, M, Size(src.cols, src.rows), INTER_CUBIC);
imshow("src", src);
imshow("dst", dst);
return 0;
}
/**
* @brief 二维码检测、解析
* @note
* @param argc:
* @param *argv[]:
* @retval
*/
int QRcode_decode(int argc, char *argv[])
{
string in_file_name = "./QRcode.png";
// 加载二维码图片
Mat input = imread(in_file_name, IMREAD_COLOR);
cout << "image info: " << input.size()
<< " (" << typeToString(input.type()) << ")"
<< endl;
// runQR(input);
runQR_dir(input);
// imshow("QR src", input);
#if 0
// 打开摄像头捕获
VideoCapture cap(0);
if (!cap.isOpened()){
cout << "Cannot open a camera" << endl;
return 2;
}
for (;;)
{
Mat frame;
cap >> frame;
if (frame.empty())
{
cout << "End of video stream" << endl;
break;
}
runQR(frame);
imshow("QR src", frame);
if(waitKey(100) == 27){
cout << "'ESC' is pressed. Exiting..." << endl;
break;
}
}
#endif
cout << "=============================" << endl;
cout << "Press any key to exit ..." << endl;
waitKey(0);
cout << "Exit." << endl;
return 0;
}
- 现象