Package: qreader
QReaderQReaderis aRobustandStraight-Forwardsolution for readingdifficultandtrickyQRcodes within images inPython. Powered by aYOLOv8model.Behind the scenes, the library is composed by two main building blocks: AYOLOv8QR Detectormodel trained todetectandsegmentQR codes (also offered asstand-alone), and thePyzbarQR Decoder. Using the information extracted from thisQR Detector,QReadertransparently applies, on top ofPyzbar, different image preprocessing techniques that maximize thedecodingrate on difficult images.InstallationTo installQReader, simply run:pipinstallqreaderYou may need to install some additionalpyzbardependencies:OnWindows:Rarely, you can see an ugly ImportError related withlizbar-64.dll. If it happens, install thevcredist_x64.exefrom theVisual C++ Redistributable Packages for Visual Studio 2013OnLinux:sudoapt-getinstalllibzbar0OnMac OS X:brewinstallzbarNOTE:If you're runningQReaderin a server with very limited resources, you may want to install theCPUversion ofPyTorch, before installingQReader. To do so, run:pip install torch --no-cache-dir(Thanks to@cjwaltherfor his advice).UsageQReaderis a very simple and straight-forward library. For most use cases, you'll only need to calldetect_and_decode:fromqreaderimportQReaderimportcv2# Create a QReader instanceqreader=QReader()# Get the image that contains the QR codeimage=cv2.cvtColor(cv2.imread("path/to/image.png"),cv2.COLOR_BGR2RGB)# Use the detect_and_decode function to get the decoded QR datadecoded_text=qreader.detect_and_decode(image=image)detect_and_decodewill return atuplecontaining the decodedstringof everyQRfound in the image.NOTE: Some entries can beNone, it will happen when aQRhave been detected butcouldn't be decoded.API ReferenceQReader(model_size = 's', min_confidence = 0.5, reencode_to = 'shift-jis')This is the main class of the library. Please, try to instantiate it just once to avoid loading the model every time you need to detect aQRcode.model_size:str. The size of the model to use. It can be'n'(nano),'s'(small),'m'(medium) or'l'(large). Larger models are more accurate but slower. Default: 's'.min_confidence:float. The minimum confidence of the QR detection to be considered valid. Values closer to 0.0 can get moreFalse Positives, while values closer to 1.0 can lose difficult QRs. Default (and recommended): 0.5.reencode_to:str|None. The encoding to reencode theutf-8decoded QR string. If None, it won't re-encode. If you find some characters being decoded incorrectly, try to set aCode Pagethat matches your specific charset. Recommendations that have been found useful:'shift-jis' for Germanic languages'cp65001' for Asian languages (Thanks to @nguyen-viet-hung for the suggestion)QReader.detect_and_decode(image, return_detections = False)This method will decode theQRcodes in the given image and return the decodedstrings(orNone, if any of them was detected but not decoded).image:np.ndarray. The image to be read. It is expected to beRGBorBGR(uint8). Format (HxWx3).return_detections:bool. IfTrue, it will return the full detection results together with the decoded QRs. If False, it will return only the decoded content of the QR codes.is_bgr:boolean. IfTrue, the received image is expected to beBGRinstead ofRGB.Returns:tuple[str | None] | tuple[tuple[dict[str, np.ndarray | float | tuple[float | int, float | int]]], str | None]]: A tuple with all detectedQRcodes decodified. Ifreturn_detectionsisFalse, the output will look like:('Decoded QR 1', 'Decoded QR 2', None, 'Decoded QR 4', ...). Ifreturn_detectionsisTrueit will look like:(('Decoded QR 1', {'bbox_xyxy': (x1_1, y1_1, x2_1, y2_1), 'confidence': conf_1}), ('Decoded QR 2', {'bbox_xyxy': (x1_2, y1_2, x2_2, y2_2), 'confidence': conf_2, ...}), ...). LookQReader.detect()for more information about detections format.QReader.detect(image)This method detects theQRcodes in the image and returns atuple of dictionarieswith all the detection information.image:np.ndarray. The image to be read. It is expected to beRGBorBGR(uint8). Format (HxWx3).is_bgr:boolean. IfTrue, the received image is expected to beBGRinstead ofRGB.Returns:tuple[dict[str, np.ndarray|float|tuple[float|int, float|int]]]. A tuple of dictionaries containing all the information of every detection. Contains the following keys.KeyValue Desc.Value TypeValue FormconfidenceDetection confidencefloatconf.bbox_xyxyBounding boxnp.ndarray (4)[x1, y1, x2, y2]cxcyCenter of bounding boxtuple[float,float](x, y)whBounding box width and heighttuple[float,float](w, h)polygon_xyPrecise polygon that segments theQRnp.ndarray (N,2)[[x1, y1], [x2, y2], ...]quad_xyFour corners polygon that segments theQRnp.ndarray (4,2)[[x1, y1], ..., [x4, y4]]padded_quad_xyquad_xypadded to fully coverpolygon_xynp.ndarray (4,2)[[x1, y1], ..., [x4, y4]]image_shapeShape of the input imagetuple[int,int](h, w)NOTE:Allnp.ndarrayvalues are of typenp.float32All keys (exceptconfidenceandimage_shape) have a normalized ('n') version. For example,bbox_xyxyrepresents the bbox of the QR in image coordinates [[0., im_w], [0., im_h]], whilebbox_xyxyncontains the same bounding box in normalized coordinates [0., 1.].bbox_xyxy[n]andpolygon_xy[n]are clipped toimage_shape. You can use them for indexing without further managementNOTE: Is this the only method you will need? Take a look atQRDet.QReader.decode(image, detection_result)This method decodes a singleQRcode on the given image, described by a detection result.Internally, this method will run thepyzbardecoder, using the information of thedetection_result, to apply different image preprocessing techniques that heavily increase the detecoding rate.image:np.ndarray. NumPy Array with theimagethat contains theQRto decode. The image is expected to be inuint8format [HxWxC], RGB.detection_result: dict[str, np.ndarray|float|tuple[float|int, float|int]]. One of thedetection dictsreturned by thedetectmethod. Note thatQReader.detect()returns atupleof thesedict. This method expects just one of them.Returns:str | None. The decoded content of theQRcode orNoneif it couldn't be read.Usage TestsTwo sample images. At left, an image taken with a mobile phone. At right, a 64x64QRpasted over a drawing.The following code will try to decode these images containingQRs withQReader,pyzbarandOpenCV.fromqreaderimportQReaderfromcv2importQRCodeDetector,imreadfrompyzbar.pyzbarimportdecode# Initialize the three tested readers (QRReader, OpenCV and pyzbar)qreader_reader,cv2_reader,pyzbar_reader=QReader(),QRCodeDetector(),decodeforimg_pathin('test_mobile.jpeg','test_draw_64x64.jpeg'):# Read the imageimg=imread(img_path)# Try to decode the QR code with the three readersqreader_out=qreader_reader.detect_and_decode(image=img)cv2_out=cv2_reader.detectAndDecode(img=img)[0]pyzbar_out=pyzbar_reader(image=img)# Read the content of the pyzbar output (double decoding will save you from a lot of wrongly decoded characters)pyzbar_out=tuple(out.data.data.decode('utf-8').encode('shift-jis').decode('utf-8')foroutinpyzbar_out)# Print the resultsprint(f"Image:{img_path}-> QReader:{qreader_out}. OpenCV:{cv2_out}. pyzbar:{pyzbar_out}.")The output of the previous code is:Image: test_mobile.jpeg -> QReader: ('https://github.com/Eric-Canas/QReader'). OpenCV: . pyzbar: ().
Image: test_draw_64x64.jpeg -> QReader: ('https://github.com/Eric-Canas/QReader'). OpenCV: . pyzbar: ().Note thatQReaderinternally usespyzbarasdecoder. The improveddetection-decoding ratethatQReaderachieves comes from the combination of different image pre-processing techniques and theYOLOv8basedQRdetectorthat is able to detectQRcodes in harder conditions than classicalComputer Visionmethods.BenchmarkRotation TestMethodMax Rotation DegreesPyzbar17ÂºOpenCV46ÂºQReader79Âº