二维码(QR码)是一种矩阵条形码,是一种机器可读的光学标签,其中包含有关其所附着物品的信息。实际上,二维码(QR码)码通常包含指向网站或应用程序的定位器,标识符或跟踪器的数据。

最近,OpenCV 4.0 发布了许多改进和新功能。其中之一是二维码扫描仪。我们在之前的文章中已经写过关于条码和二维码扫描器的文章,它使用了一个外部库——ZBar。因此,我们想检查 OpenCV 扫描仪的工作原理,以及它是否比 Zbar 扫描仪更好。在这篇文章中,我们将看到如何使用来自 OpenCV 的这个新的二维码扫描器。如果您想选择使用哪一种,我们还将它与基于 ZBar 的扫描仪进行比较。

链接:https://pan.baidu.com/s/1SjOnoxUic6wG8Fh0nB50kw 
提取码:123a

1.使用qrcode库生成QR码

pip install opencv-python qrcode pillow pyzbar
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
@File    : generateQRCode.py
@Time    : 2021/10/12 14:13
"""
# 生成二维码
import qrcode

# 二维码包含的示例数据
data = ""
# 生成的二维码图片名称
filename = "CSDNBlog.png"
# 生成二维码
img = qrcode.make(data)
# 保存成图片输出
img.save(filename)

opencv 获取 图片 二维码 位置 opencv读取二维码_opencv 获取 图片 二维码 位置

2.基于OpenCV读取QR码

(1)Python

import cv2
import numpy as np
import sys
import time

if len(sys.argv) > 1:
    inputImage = cv2.imread(sys.argv[1])
else:
    inputImage = cv2.imread("CSDNBlog.png")


# 显示条码和二维码位置
def display(im, bbox):
	# bbox:Nx4x2
    n = len(bbox)
    for j in range(n):
        cv2.line(im, tuple(bbox[j][0]), tuple(bbox[j][1]), (255, 0, 0), 1)
        cv2.line(im, tuple(bbox[j][1]), tuple(bbox[j][2]), (255, 0, 0), 1)
        cv2.line(im, tuple(bbox[j][2]), tuple(bbox[j][3]), (255, 0, 0), 1)
        cv2.line(im, tuple(bbox[j][3]), tuple(bbox[j][0]), (255, 0, 0), 1)

    # 显示
    cv2.imshow("Results", im)


# 创建一个 qrCodeDetector 对象
qrDecoder = cv2.QRCodeDetector()

# 检测和解码二维码
t = time.time()
data, bbox, rectifiedImage = qrDecoder.detectAndDecode(inputImage)
print("Time Taken for Detect and Decode : {:.3f} seconds".format(time.time() - t))
if len(data) > 0:
    print("Decoded Data : {}".format(data))
    display(inputImage, bbox)
    rectifiedImage = np.uint8(rectifiedImage);
    cv2.imshow("Rectified QRCode", rectifiedImage);
else:
    print("QR Code not detected")
    cv2.imshow("Results", inputImage)
cv2.imwrite("output.jpg", inputImage)
cv2.waitKey(0)
cv2.destroyAllWindows()

(2)C++

// #include <opencv2/opencv.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>

using namespace cv;
using namespace std;

void display(Mat &im, Mat &bbox)
{
  int n = bbox.rows;
  for(int i = 0 ; i < n ; i++)
  {
  	// 下面有点小问题,自己画线
    line(im, Point2i(bbox.at<float>(i,0),bbox.at<float>(i,1)), Point2i(bbox.at<float>(i+1) % n,0), bbox.at<float>((i+1) % n,1)), Scalar(255,0,0), 3);
  }
  imshow("Result", im);
}

int main(int argc, char* argv[])
{
  // Read image
  Mat inputImage;
  if(argc>1)
    inputImage = imread(argv[1]);
  else
    inputImage = imread("qrcode-learnopencv.jpg");

  QRCodeDetector qrDecoder;

  Mat bbox, rectifiedImage;

  std::string data = qrDecoder.detectAndDecode(inputImage, bbox, rectifiedImage);
  if(data.length()>0)
  {
    cout << "Decoded Data : " << data << endl;

    display(inputImage, bbox);
    rectifiedImage.convertTo(rectifiedImage, CV_8UC3);
    imshow("Rectified QRCode", rectifiedImage);

    waitKey(0);
  }
  else
    cout << "QR Code not detected" << endl;
}

opencv 获取 图片 二维码 位置 opencv读取二维码_OpenCV_02

3.Zbar与OpenCV读取QR码比较

import cv2
import numpy as np
import sys
import time
import pyzbar.pyzbar as pyzbar

cap = cv2.VideoCapture("qr-code_animated.gif")
hasFrame, frame = cap.read()
vid_writer = cv2.VideoWriter('output.avi', cv2.VideoWriter_fourcc('M', 'J', 'P', 'G'), 10,
                             (frame.shape[1], frame.shape[0]))


# 显示条码和二维码位置
def display(im, decodedObjects):
    # 遍历所有已解码的对象
    for decodedObject in decodedObjects:
        points = decodedObject.polygon

        # 如果点不形成四边形,请找到凸包
        if len(points) > 4:
            hull = cv2.convexHull(np.array([point for point in points], dtype=np.float32))
            hull = list(map(tuple, np.squeeze(hull)))
        else:
            hull = points;

        # 凸包中的点数
        n = len(hull)

        # 绘制凸包
        for j in range(0, n):
            cv2.line(im, hull[j], hull[(j + 1) % n], (255, 0, 0), 3)

    # 显示结果
    # cv2.imshow("Results", im);


# 创建一个 qrCodeDetector 对象
qrDecoder = cv2.QRCodeDetector()

# 检测和解码二维码
t = time.time()
while (1):
    hasFrame, inputImage = cap.read()
    if not hasFrame:
        break
    decodedObjects = pyzbar.decode(inputImage)
    if len(decodedObjects):
        zbarData = decodedObjects[0].data
    else:
        zbarData = ''
    opencvData, bbox, rectifiedImage = qrDecoder.detectAndDecode(inputImage)
    if zbarData:
        cv2.putText(inputImage, "ZBAR : {}".format(zbarData.decode()), (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1,
                    (0, 255, 0), 2, cv2.LINE_AA)
    else:
        cv2.putText(inputImage, "ZBAR : QR Code NOT Detected", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2,
                    cv2.LINE_AA)
    if opencvData:
        cv2.putText(inputImage, "OpenCV:{}".format(opencvData), (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2,
                    cv2.LINE_AA)
    else:
        cv2.putText(inputImage, "OpenCV:QR Code NOT Detected", (10, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2,
                    cv2.LINE_AA)

    display(inputImage, decodedObjects)
    cv2.imshow("Result", inputImage)
    vid_writer.write(inputImage)
    k = cv2.waitKey(20)
    if k == 27:
        break
cv2.destroyAllWindows()
vid_writer.release()

opencv 获取 图片 二维码 位置 opencv读取二维码_opencv_03

opencv 获取 图片 二维码 位置 opencv读取二维码_opencv 获取 图片 二维码 位置_04

4.将视频转为GIF代码

#!/usr/bin/python3
# -*- encoding: utf-8 -*-
"""
@File    : toGIF.py
"""
import cv2
import imageio


def read_video(video_path):
    video_cap = cv2.VideoCapture(video_path)
    frame_count = 0
    all_frames = []
    while True:
        ret, frame = video_cap.read()
        if ret is False:
            break
        frame = cv2.resize(frame, (320, 280))
        frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        all_frames.append(frame)
        cv2.imshow('frame', frame)
        cv2.waitKey(1)
        frame_count += 1
        if frame_count > 100:
            break
        print(frame_count)
    video_cap.release()
    cv2.destroyAllWindows()
    print('===>', len(all_frames))

    return all_frames


def frame_to_gif(frame_list):
    gif = imageio.mimsave('./qrCode.gif', frame_list, 'GIF')


if __name__ == "__main__":
    frame_list = read_video('output.avi')
    frame_to_gif(frame_list)

5.对比结果分析

现在,让我们基于以下理由比较这两种实现。令人惊讶的是,基于 ZBar 的扫描仪在各个方面都优于 OpenCV 的二维码。

5.1速度

ZBar 库的速度几乎是 OpenCV 二维码检测器的两倍。

5.2稳健性

与 OpenCV 相比,ZBar 库在以下因素上产生了更可靠的结果,如上述视频所示:

  • ZBar 在各种旋转下都更好或具有可比性
  • 从视频中的不同缩放级别可以看出,ZBar 在不同图像尺寸下效果更好
  • ZBar 更擅长处理透视失真(当图像与相机不垂直时。

5.3特征

ZBar 库也提供对条码的支持,而 OpenCV 中还没有。

总的来说,我们可以说 QR Code 是最近在 OpenCV 中推出的,它可能会在未来的版本中变得更好。到那时之前,如果您想在您的应用程序中使用条形码或二维码,请坚持使用 ZBar。

BONUS

  • Python代码
from pyzbar.pyzbar import decode
import cv2
import numpy as np

cap = cv2.VideoCapture(0)


def get_qr_data(input_frame):
    try:
        return decode(input_frame)
    except:
        return []


def draw_polygon(f_in, qro):
    if len(qro) == 0:
        return f_in
    else:
        for obj in qro:
            text = obj.data.decode('utf-8')
            pts = np.array([obj.polygon], np.int32)
            # print("Before Reshape::", pts.shape)
            pts = pts.reshape((4, 1, 2))
            # print("After Reshape::",pts.shape)
            cv2.polylines(f_in, [pts], True, (255, 100, 5), 2)
            cv2.putText(f_in, text, (50, 50), cv2.FONT_HERSHEY_PLAIN,1.5,(255,100,5),2)
        return f_in


while True:
    _, frame = cap.read()
    qr_obj = get_qr_data(frame)
    frame = draw_polygon(frame, qr_obj)
    cv2.imshow("DD", frame)
    # cv2.imshow("DD2", frame2)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()
  • C++代码
//Reference:https://www.learnopencv.com/opencv-qr-code-scanner-c-and-python/

#include <iostream>
#include <vector>
#include <zbar.h>

#include <opencv2/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;
using namespace zbar;

typedef struct
{
    string type;
    string data;
    vector <Point> location;
}decodedObject;


// Find and decode barcodes and QR codes
void decodewithlocation(Mat &im, vector<decodedObject>&decodedObjects)
{

    // Create zbar scanner
    ImageScanner scanner;
    //Clear decoded Object to keep only one QR code in memory at a time.
    decodedObjects.clear();
    // Configure scanner
    scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
//    scanner.set_config(ZBAR_QRCODE, ZBAR_CFG_ENABLE, 1);

    // Convert image to grayscale
    Mat imGray;
    cvtColor(im, imGray,COLOR_BGR2GRAY);

    // Wrap image data in a zbar image
    Image image(im.cols, im.rows, "Y800", (uchar *)imGray.data, im.cols * im.rows);

    // Scan the image for barcodes and QRCodes
    int n = scanner.scan(image);

    // Print results
    for(Image::SymbolIterator symbol = image.symbol_begin(); symbol != image.symbol_end(); ++symbol)
    {
        decodedObject obj;

        obj.type = symbol->get_type_name();
        obj.data = symbol->get_data();

        // Print type and data
        cout << "Type : " << obj.type << endl;
        cout << "Data : " << obj.data << endl << endl;

        // Obtain location
        for(int i = 0; i< symbol->get_location_size(); i++)
        {
            obj.location.push_back(Point(symbol->get_location_x(i),symbol->get_location_y(i)));
        }

      decodedObjects.push_back(obj);
    }
}



int main(int argc, char *argv[])
{

    vector<decodedObject> decodedObjects;

    VideoCapture *cam = new VideoCapture(0);

    while(cam->isOpened()){
        Mat image;
        cam->read(image);
//        cvtColor(image, image,COLOR_BGR2GRAY);
        decodewithlocation(image, decodedObjects);

        cout<<decodedObjects.size()<<endl;
        for (int i=0; i<decodedObjects.size(); i++){
            //QR Object location in image stored in points variable
            vector<Point> points = decodedObjects[i].location;
            //Convex Hull is created if points are more than 4
            vector<Point> hull;
            if(points.size() > 4){
                convexHull(points, hull);
            }else{
                hull=points;
            }
            int n = hull.size();
            for(int j = 0; j < n; j++)
            {
                line(image, hull[j], hull[ (j+1) % n], Scalar(0,255,90), 3);
            }

        }

        imshow("image", image);


        if(waitKeyEx(1)==99){
            break;
        }
    }

    // Variable for decoded objects

    // Find and decode barcodes and QR codes


    return 0;
}

参考代码

https://learnopencv.com/opencv-qr-code-scanner-c-and-python/