opencv4.0.0中qr码定位源码分析

概述

近期由于开发需要,需研究qr码的定位和检测,恰好opencv前端时间已经发布了4.0.0版本,就着这个机会学习一下opencv中的实现。需说明的是,opencv二维码检测模块是基于Quirc(git://github.com/dlbeer/quirc.git)这个开源库集成开发的。

qr码检测到类为QRCodeDetector,其主要成员函数有

  1. 构造函数
  2. 析构函数
  3. void setEpsX(double epsX)、void setEpsY(double epsY):这两个设置X、Y两个方向上检测出定位码的系数,默认epsX = 0.2,epsY = 0.1;
  4. bool detect(cv::InputArray img, cv::OutputArray points) const:定位qr码;
  5. string decode(cv::InputArray img, cv::InputArray points, cv::OutputArray straight_qrcode = cv::noArray()):解码;
  6. std::string detectAndDecode(cv::InputArray img, cv::OutputArray points=cv::noArray(),                                        cv::OutputArray straight_qrcode = cv::noArray()):定位和解码。

定位

我们把重点放到定位detect()上,这基本上也是qr码检测成败的关键。

bool QRCodeDetector::detect(InputArray in, OutputArray points) const
{
    Mat inarr = in.getMat();
    CV_Assert(!inarr.empty());
    CV_Assert(inarr.depth() == CV_8U);
    int incn = inarr.channels();
    if( incn == 3 || incn == 4 )
    {
        Mat gray;
        cvtColor(inarr, gray, COLOR_BGR2GRAY);
        inarr = gray;
    }

    QRDetect qrdet;
    qrdet.init(inarr, p->epsX, p->epsY);
    if (!qrdet.localization()) { return false; }
    if (!qrdet.computeTransformationPoints()) { return false; }
    vector<Point2f> pnts2f = qrdet.getTransformationPoints();
    Mat(pnts2f).convertTo(points, points.fixedType() ? points.type() : CV_32FC2);
    return true;
}

可以看到detect函数先将图片转为灰度图,然后执行调用QRDetect这个类来进行检测,我们来分析QRDetect这个类。

class QRDetect
{
public:
    void init(const Mat& src, double eps_vertical_ = 0.2, double eps_horizontal_ = 0.1);
    bool localization();
    bool computeTransformationPoints();
    Mat getBinBarcode() { return bin_barcode; }
    Mat getStraightBarcode() { return straight_barcode; }
    vector<Point2f> getTransformationPoints() { return transformation_points; }
    static Point2f intersectionLines(Point2f a1, Point2f a2, Point2f b1, Point2f b2);
protected:
    vector<Vec3d> searchHorizontalLines();
    vector<Point2f> separateVerticalLines(const vector<Vec3d> &list_lines);
    void fixationPoints(vector<Point2f> &local_point);
    vector<Point2f> getQuadrilateral(vector<Point2f> angle_list);
    bool testBypassRoute(vector<Point2f> hull, int start, int finish);
    inline double getCosVectors(Point2f a, Point2f b, Point2f c);

    Mat barcode, bin_barcode, straight_barcode;
    vector<Point2f> localization_points, transformation_points;
    double eps_vertical, eps_horizontal, coeff_expansion;
};

QRCodeDetector detect函数中的QRDetect qrdet这个对象

  1. 先执行了一次init,在init中先将宽高小于512的图片resize到512,然后执行了一个adaptiveThreshold操作,其中blocksize设为83?
  2. 然后进行localization,在其中分别使用searchHorizontalLines和separateVerticalLines来扫描行和列上的点,然后使用kmeans进行聚类,将列上的点聚类成3个,其中迭代次数为3次?

        对聚类完后3个点使用fixationPoints来重新修正,在其中先用余弦定理滤除掉任意角余弦值小于0.85的情况,后面进行了一          系列的修正(待补充);

        最后滤除相邻点距离小于10pixel的情况。

      3.在computeTransformationPoints()函数中主要有两个操作,

一个是查找对齐点,这里直接搬运别人博客上的内容(http://www.p-chao.com/2018-11-23/opencv4-0-0%E4%BA%8C%E7%BB%B4%E7%A0%81%E8%AF%86%E5%88%AB%E4%BB%A3%E7%A0%81%E7%AE%80%E6%9E%90/):

最开始会找到下图中的三个红色的特征点,这个比较容易理解,因为左上角的点到其它两个点的距离是差不多的。

然后在左下角和右上角的回字上,找离左上角红点最远的点,那么可以得到两个蓝颜色的特征点

扫描二维码关注公众号,回复: 4651503 查看本文章

根据红蓝点连线的交叉点,就可以得到第四各顶点,这样对齐需要的点我们就都找到了

第二个是进行透视变换,opencv中选取了5组点对,在上述找到的4个顶点外,还通过计算对角线交点算出了中心点,使用这5额点来计算Homograph,然后透视变换回去,就可以得到一个理想的正对我们的二维码了。

解码

opencv直接调用了quirc解码,该部分不是关注重点,本文不做赘述。

猜你喜欢

转载自blog.csdn.net/happyjume/article/details/84784035