mchehab / zbar

ZBar is an open source software suite for reading bar codes from various sources, including webcams. As its development stopped in 2012, I took the task of keeping it updated with the V4L2 API. This is the main repository for it. There's a clone at at LinuxTV.org, and another one at gitlab.
https://linuxtv.org/downloads/zbar/
GNU Lesser General Public License v2.1
911 stars 189 forks source link

Swiss QR-bills not recognized #216

Open drtrigon opened 2 years ago

drtrigon commented 2 years ago

In Switzerland we have a new standard for invoices according to ISO Standard 20022. See this example:

61488adaa4506140398ea38e_barcode (1) 2swissqr

(from here)

The issue is that zbarimg is not able to find or decode this QR code. I also tried to invert the image.

timlegge commented 2 years ago

zbarcam does read it fine but zbarimg does not

drtrigon commented 2 years ago

What I found is that rescaling helps if I scale the screenshot of this webpage to 90% or the screenshot of the image alone (when clicking the link) to 40% it works for me also using zbarimg.

IMHO this scaling should be done by zbar itself, correct?

drtrigon commented 2 years ago

(also I do have the issue, that zbar messes up the char encoding and is not able to represent the ü properly)

timlegge commented 2 years ago

I did add a line to zbarimg.c to scale the image to approx 40% which did allow it to work. However, I believe the fix is likely somewhere in the QR interpreting code but I am not familiar enough with it to say where.

timlegge commented 2 years ago

The issue with the char encoding can be fixed with:

diff --git a/zbar/qrcode/qrdectxt.c b/zbar/qrcode/qrdectxt.c
index 977d552..47e24ce 100644
--- a/zbar/qrcode/qrdectxt.c
+++ b/zbar/qrcode/qrdectxt.c
@@ -45,6 +45,7 @@ static int text_is_big5(const unsigned char *_text, int _len){
         return 0;
       }
     }else{ // normal ascii encoding, it's okay
+      return 0;
     }
   }
   return 1;

This looks to be the correct fix but I don't have a barcode containing big5 char encoding to be usre

hash-eol commented 1 year ago

I have an identical problem as drtrigon with my bills (sometimes even the zbarcam program can not read the QR code or reads garbage data resulting for example in the reference number being wrong), and I suspect it is the white cross in the middle. Trying to get the data from the image in the 1st comment fails with the "barcode data was not detected". However, just starting up gimp to spray some black over the white cross in the center and zbarimg can find the QR code! Edited QR code blacking out the white cross

Now, I don't know if it is trivial to fix or not in zbar?

maglub commented 2 months ago

In python, using the pyzbar library, I work around this by using open CV, masking the cross before sending it to zbar:

import cv2
import numpy as np

def mask_swiss_cross(img):
  #-----------------------------------------
  # Since most QR decoders have issues with the swiss cros in the middle of the qrcode
  #-----------------------------------------
  logging.info(f"  - Masking swiss cross")
  qcd = cv2.QRCodeDetector()
  retval, points = qcd.detect(img)

  logging.debug(points)

  poly_points = points[0]

  # Calculate the centroid of the polygon
  centroid = np.mean(poly_points, axis=0).astype(int)

  #--- draw edges around the qrcode
  #img = cv2.polylines(img, points.astype(int), True, (0, 255, 0), 3)

  # Calculate the total width spanned by the poly-points
  min_x = np.min(poly_points[:, 0])
  max_x = np.max(poly_points[:, 0])
  total_width = max_x - min_x

  # Calculate the rectangle's width as 1/8 of the total width
  #rect_width = total_width / 8
  rect_width = total_width / 10

  # Decide on the rectangle's height (arbitrary decision or based on specific criteria)
  # For demonstration, let's say we keep the height equal to the rectangle's width for a square shape
  rect_height = rect_width

  # Calculate the top-left and bottom-right points of the rectangle
  top_left = (int(centroid[0] - rect_width / 2), int(centroid[1] - rect_height / 2))
  bottom_right = (int(centroid[0] + rect_width / 2), int(centroid[1] + rect_height / 2))

  # Draw the filled rectangle in black
  cv2.rectangle(img, top_left, bottom_right, (0, 0, 0), -1) # -1 thickness fills the rectangle
  return img

Then I just call zbar with:

img_masked = mask_swiss_cross(img)
res = zbar_decode(img_masked)

I also created a quick and dirty "swiss cross masker" in python, see here: https://gist.github.com/maglub/1954a916844c7b0118d9c16f1a6d3e9b