[opencv]二维码定位C++

//
// Created by leoxae on 19-9-21.
//

#include "QRcodeLocation.h"
#include "../cv/ImgProcession.h"

RNG rng(12345);


Mat QRcodeLocation::imgtrans(Mat img){
    int width = img.cols;
    int height = img.rows;
    Mat dst = img.clone();

    //透视变换
    vector<Point2f> srcPoints = {
            Point2f(0, 0),
            Point2f(width, 0),
            Point2f(0, height),
            Point2f(width, height)
    };

    vector<Point2f> dstPoints = {
            Point2f(120, height),
            Point2f(width- 120, height),
            Point2f(0, 0),
            Point2f(width , 0)
    };

    Mat transform = getPerspectiveTransform(srcPoints, dstPoints);
    Mat transmat;
    warpPerspective(dst, transmat, transform, Size(width, height));

    return transmat;
}

Mat QRcodeLocation::imgtrans_flip(Mat img){
    int width = img.cols;
    int height = img.rows;
    Mat dst = img.clone();

    //透视变换
    vector<Point2f> srcPoints = {
            Point2f(0, 0),
            Point2f(width, 0),
            Point2f(0, height),
            Point2f(width, height)
    };

    vector<Point2f> dstPoints = {
            Point2f(0, 0),
            Point2f(width , 0),
            Point2f(120, height),
            Point2f(width- 120, height),
    };

    Mat transform = getPerspectiveTransform(srcPoints, dstPoints);
    Mat transmat;
    warpPerspective(dst, transmat, transform, Size(width, height));

    return transmat;
}

Point QRcodeLocation::Center_cal(vector<vector<Point>> contours, int i)//找到所提取轮廓的中心点
{
    int centerx = 0, centery = 0, n = contours[i].size();
    //在提取的小正方形的边界上每隔周长个像素提取一个点的坐标,求所提取四个点的平均坐标(即为小正方形的大致中心)
    centerx = (contours[i][n / 4].x + contours[i][n * 2 / 4].x + contours[i][3 * n / 4].x + contours[i][n - 1].x) / 4;
    centery = (contours[i][n / 4].y + contours[i][n * 2 / 4].y + contours[i][3 * n / 4].y + contours[i][n - 1].y) / 4;
    Point point1 = Point(centerx, centery);
    return point1;
}

Mat QRcodeLocation::FindQRcode(Mat img){

    //透视变换
    Mat transmat;
    transmat = imgtrans_flip(img);
//    transmat = imgtrans(img);
//    imshow("transmat",transmat);
//    waitKey();
    Mat hsv;
    cvtColor(transmat,hsv,COLOR_BGR2HSV);
    Scalar lower_white (0,0,0);
    Scalar upper_white (180,255,180);
    Mat mask_white;
    inRange(hsv,lower_white,upper_white,mask_white);

    Mat gray = mask_white;
    threshold(gray,gray,100,255,THRESH_OTSU + THRESH_BINARY);

    Scalar color = Scalar(1, 1, 255);
    Mat threshold_output;
    vector<vector<Point>> contours, contours2;
    vector<Vec4i> hierarchy;
    Mat drawing = Mat::zeros(img.size(), CV_8UC3);
    Mat drawing2 = Mat::zeros(img.size(), CV_8UC3);
    threshold(gray, threshold_output, 112, 255, THRESH_BINARY);
//    imshow("预处理后",threshold_output);
//    waitKey();
//    Canny(threshold_output,threshold_output,136,196,3);
//    imshow("预处理后canny:",threshold_output);
    //寻找轮廓
    //第一个参数是输入图像二值化的
    //第二个参数是内存存储器,FindContours找到的轮廓放到内存里面。
    //第三个参数是层级,**[Next, Previous, First_Child, Parent]** 的vector
    //第四个参数是类型,采用树结构
    //第五个参数是节点拟合模式,这里是全部寻找
    findContours(threshold_output, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
    //CHAIN_APPROX_NONE全体,CV_CHAIN_APPROX_SIMPLE,RETR_TREE    RETR_EXTERNAL    RETR_LIST   RETR_CCOMP
    cout << "初始轮廓数量==>" << contours.size() << endl;
    int c = 0, ic = 0, k = 0, area = 0;

    //程序的核心筛选
    int parentIdx = -1;
    if (!contours.empty()) {
        for (int i = 0; i < contours.size(); i++) {
            if (hierarchy[i][2] != -1 && ic == 0) {
                parentIdx = i;
                ic++;
            } else if (hierarchy[i][2] != -1) {
                ic++;
            } else if (hierarchy[i][2] == -1) {
                ic = 0;
                parentIdx = -1;
            }

            if (ic >= 2 && contourArea(contours[parentIdx]) < 150000 && contourArea(contours[parentIdx]) > 100) {
                contours2.push_back(contours[parentIdx]);
                drawContours(drawing, contours, parentIdx,
                   CV_RGB(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)), 1, 8);
                imshow("drawing", drawing);
                waitKey();
                ic = 0;
                parentIdx = -1;
                //得出一个二维码定位角的面积,以便计算其边长(area_side)
                area = contourArea(contours[i]);
                cout << "area==>" << area << endl;
                cout << "i= " << i << " hierarchy[i][2]= " << hierarchy[i][2]
                << " parentIdx= " << parentIdx << " ic= " << ic << endl;
            }
        }
    } else {
        //if contours为空
        cout << "[error]没有找到相应轮廓" << endl;
        return img;
    }

    for (int i = 0; i<contours2.size(); i++){
        drawContours(drawing2, contours2, i, CV_RGB(rng.uniform(100, 255), rng.uniform(100, 255), rng.uniform(100, 255)), -1, 4, hierarchy[k][2], 0, Point());
//        imshow("drawing3",drawing2);
//        waitKey();
    }
    Point point[3];
    for (int i = 0; i<contours2.size(); i++)
    {
        point[i] = Center_cal(contours2, i);
    }
    int length = sizeof(point)/sizeof(point[0]);
    cout << "length=" << length << endl;

    for (int i = 0; i < 3; i++){
        cout << "points[i]=" << point[i] << endl;
    }
    if ((point[2].x == 0 || point[2].y == 0)){
        cout << "二维码定位轮廓检测不全" << endl;
        return img;
    } else {
        //合并三个定位矩形轮廓点集
        vector<Point> pt_array = pointmerge(contours2);

//    area = contourArea(contours2[0]);
//    int area_side = cvRound(sqrt(double(area)));
//    for (int i = 0; i<contours2.size(); i++)
//    {
//        line(drawing2, point[i%contours2.size()], point[(i + 1) % contours2.size()], color, area_side / 4, 8);
//    }
//    imshow("提取后", drawing2);
        cout << "轮廓数量" << contours2.size() << endl;
        //imshow( "Contours", drawing );

        RotatedRect rct = minAreaRect(pt_array);
        Point2f pts[4];
        rct.points(pts);
        circle(transmat,pts[0],2,Scalar(0,0,255),-1);
        circle(transmat,pts[1],2,Scalar(0,255,0),-1);
        circle(transmat,pts[2],2,Scalar(255,0,0),-1);
        circle(transmat,pts[3],2,Scalar(0,255,255),-1);
//        imshow("drawcircle",transmat);
//        waitKey();

        vector<Point2f> pointlist{pts[0],pts[1],pts[2],pts[3]};
        Rect ROI = boundingRect(pointlist);

        cout << "x=" << ROI.x << "," << "y=" << ROI.y << endl;
        cout << "width=" << ROI.width << ",height=" << ROI.height << endl;
        if (ROI.width <= ROI.height){
            ROI.x = ROI.x /* 0.8*/;
            ROI.y = ROI.y /* 0.8*/;
            ROI.height = ROI.height /*2.5*/;
            ROI.width = ROI.height;
        } else {
            ROI.x = ROI.x /* 0.8*/;
            ROI.y = ROI.y /* 0.8*/;
            ROI.width = ROI.width/*2.5*/;
            ROI.height = ROI.width;
        }

        Mat rmat = transmat(ROI);
        Rect rm (ROI.x - 20, ROI.y - 20, ROI.width + 40, ROI.height + 40);
        Mat roimat = transmat(rm);

        resize(roimat,roimat,Size(roimat.cols*1.2,roimat.rows*1.2));
        imshow("roimat",roimat);
        waitKey();

        return roimat;
    }
}

/**
 * vector容器合并
 * @param contours2
 * @return
 */
vector<Point> QRcodeLocation::pointmerge(vector<vector<Point>> contours2) {

    vector<Point> ptarray;
    vector<Point> pt1 = contours2[0];
    vector<Point> pt2 = contours2[1];
    vector<Point> pt3 = contours2[2];

    ptarray.insert(ptarray.end(), pt1.begin(),pt1.end());
    ptarray.insert(ptarray.end(), pt2.begin(),pt2.end());
    ptarray.insert(ptarray.end(), pt3.begin(),pt3.end());
    return ptarray;
}
知识兔
//
// Created by leoxae on 19-9-21.
//

#ifndef KEEKOAIROBOT_QRCODELOCATION_H
#define KEEKOAIROBOT_QRCODELOCATION_H

#include "../../globals.h"
using namespace std;

class QRcodeLocation{
public:
    /**
     * 查找QRcode二维码位置
     * @param img 原图
     * @return 二维码截取后的图
     */
    static Mat FindQRcode(Mat img);

    /**
     * 二维码透视变换
     * @param img
     * @return
     */
    static Mat imgtrans(Mat img);

    /**
     * 计算轮廓中心点
     * @param contours  轮廓点集
     * @param i 索引值
     * @return 中心点坐标
     */
    static Point Center_cal(vector<vector<Point>> contours, int i);

    static vector<Point> pointmerge(vector<vector<Point>> vector);

    static Mat imgAffinetrans(Mat src, vector<Point2f> srcpts);

    static Mat img_Rotate(Mat src);

    static Mat imgtrans_flip(Mat img);
};

#endif //KEEKOAIROBOT_QRCODELOCATION_H
知识兔
计算机