Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameter to enable return of corrected zbar polygons #33

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 66 additions & 2 deletions qreader.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ class DecodeQRResult:
image: np.ndarray
result: Decoded

cropped_bbox: np.ndarray | None = None
cropped_quad: np.ndarray | None = None
corrected_perspective_values: tuple[np.ndarray, np.ndarray] | None = None


def wrap(
scale_factor: float,
Expand All @@ -62,6 +66,10 @@ def wrap(
blur_kernel_sizes: tuple[tuple[int, int], ...] | None,
image: np.ndarray,
results: typing.List[Decoded],

cropped_bbox: np.ndarray | None = None,
cropped_quad: np.ndarray | None = None,
corrected_perspective_values: tuple[np.ndarray, np.ndarray] | None = None
) -> list[DecodeQRResult]:

return [
Expand All @@ -72,6 +80,10 @@ def wrap(
blur_kernel_sizes=blur_kernel_sizes,
image=image,
result=result,

cropped_bbox=cropped_bbox,
cropped_quad=cropped_quad,
corrected_perspective_values = corrected_perspective_values,
)
for result in results
]
Expand Down Expand Up @@ -147,6 +159,7 @@ def detect(
bounding box in absolute coordinates, while 'bbox_xyxyn' is the bounding box in normalized coordinates
(from 0. to 1.).
"""

return self.detector.detect(image=image, is_bgr=is_bgr)

def decode(
Expand All @@ -155,6 +168,7 @@ def decode(
detection_result: dict[
str, np.ndarray | float | tuple[float | int, float | int]
],
return_zbar_poly: bool = False,
) -> str | None:
"""
This method decodes a single QR code on the given image, described by a detection_result.
Expand Down Expand Up @@ -188,11 +202,41 @@ def decode(
f"Double decoding failed for {self.reencode_to}. Returning utf-8 decoded string."
)

if return_zbar_poly:
polygon = decodeQRResult.result.polygon
if decodeQRResult.corrections == "cropped_bbox":
bbox_xyxy = decodeQRResult.cropped_bbox
x_offset, y_offset = bbox_xyxy[0], bbox_xyxy[1]
scale_factor = decodeQRResult.scale_factor
corrected_polygon = [
(point.x / scale_factor + x_offset, point.y / scale_factor + y_offset)
for point in polygon
]
return decoded_str, corrected_polygon
elif decodeQRResult.corrections == "corrected_perspective":
padded_quad_xy, dst_pts = decodeQRResult.corrected_perspective_values
M_inv = cv2.getPerspectiveTransform(dst_pts, padded_quad_xy)
corrected_polygon = cv2.perspectiveTransform(np.array([[(point.x, point.y) for point in polygon]], dtype=np.float32), M_inv)[0]
scale_factor = decodeQRResult.scale_factor
corrected_polygon = [
(point[0] / scale_factor, point[1] / scale_factor) for point in corrected_polygon
]
return decoded_str, corrected_polygon

else:
return decoded_str, None

return decoded_str
if return_zbar_poly:
return None, None
return None

def detect_and_decode(
self, image: np.ndarray, return_detections: bool = False, is_bgr: bool = False
self,
image: np.ndarray,
return_detections: bool = False,
is_bgr: bool = False,
return_zbar_poly: bool = False,
) -> (
tuple[
dict[str, np.ndarray | float | tuple[float | int, float | int]], str | None
Expand All @@ -217,7 +261,7 @@ def detect_and_decode(
"""
detections = self.detect(image=image, is_bgr=is_bgr)
decoded_qrs = tuple(
self.decode(image=image, detection_result=detection)
self.decode(image=image, detection_result=detection, return_zbar_poly=return_zbar_poly)
for detection in detections
)

Expand Down Expand Up @@ -313,6 +357,7 @@ def _decode_qr_zbar(
):
continue


rescaled_image = cv2.resize(
src=image,
dsize=None,
Expand All @@ -329,7 +374,11 @@ def _decode_qr_zbar(
blur_kernel_sizes=None,
image=rescaled_image,
results=decodedQR,

cropped_bbox=detection_result[BBOX_XYXY] if label == "cropped_bbox" else None,
corrected_perspective_values=(detection_result[PADDED_QUAD_XY], self.__get_dst_pts(corrected_perspective)) if label == "corrected_perspective" else None,
)

# For QRs with black background and white foreground, try to invert the image
inverted_image = image = 255 - rescaled_image
decodedQR = decodeQR(inverted_image, symbols=[ZBarSymbol.QRCODE])
Expand All @@ -341,6 +390,9 @@ def _decode_qr_zbar(
blur_kernel_sizes=None,
image=inverted_image,
results=decodedQR,

cropped_bbox=detection_result[BBOX_XYXY] if label == "cropped_bbox" else None,
corrected_perspective_values=(detection_result[PADDED_QUAD_XY], self.__get_dst_pts(corrected_perspective)) if label == "corrected_perspective" else None,
)

# If it not works, try to parse to grayscale (if it is not already)
Expand All @@ -362,6 +414,9 @@ def _decode_qr_zbar(
blur_kernel_sizes=((5, 5), (7, 7)),
image=gray,
results=decodedQR,

cropped_bbox=detection_result[BBOX_XYXY] if label == "cropped_bbox" else None,
corrected_perspective_values=(detection_result[PADDED_QUAD_XY], self.__get_dst_pts(corrected_perspective)) if label == "corrected_perspective" else None,
)

if len(rescaled_image.shape) == 3:
Expand All @@ -387,6 +442,9 @@ def _decode_qr_zbar(
blur_kernel_sizes=((3, 3),),
image=sharpened_gray,
results=decodedQR,

cropped_bbox=detection_result[BBOX_XYXY] if label == "cropped_bbox" else None,
corrected_perspective_values=(detection_result[PADDED_QUAD_XY], self.__get_dst_pts(corrected_perspective)) if label == "corrected_perspective" else None,
)

return []
Expand Down Expand Up @@ -436,6 +494,12 @@ def __correct_perspective(
dst_img = cv2.warpPerspective(image, M, (N, N))

return dst_img

def __get_dst_pts(self, corrected_perspective: np.ndarray) -> np.ndarray:
N = corrected_perspective.shape[0]
return np.array(
[[0, 0], [N - 1, 0], [N - 1, N - 1], [0, N - 1]], dtype=np.float32
)

def __threshold_and_blur_decodings(
self,
Expand Down
Loading