Opencv QR code positioning + keystone correction

After Opencv 4.0, Qrcode positioning and analysis was added. It can also be positioned only, and the parsing part can be realized by zBar

  • Use QRCodeDetector::detect for positioning, combined with perspective transformation for trapezoidal arrogance
#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;
}

  • phenomenon

Guess you like

Origin blog.csdn.net/pyt1234567890/article/details/109678247