二维码(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)
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;
}
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()
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/