Rotating QR code to the correct position using Python OpenCV
Asked Answered
E

2

5

I'm a beginner in python and currently studying QR code detection and decoding. I'm having a hard time rotating the detected QR code to the right position. I already used minAreaRect() to rotate my QR code but it doesn't work. Is there any workaround or a right way to do this? thanks!

ROI2 = cv2.imread('ROI.png')
gray2 = cv2.cvtColor(ROI2, cv2.COLOR_BGR2GRAY)
blur2 = cv2.GaussianBlur(gray2, (9, 9), 0)
thresh2 = cv2.threshold(blur2, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Morph close
# kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# close2 = cv2.morphologyEx(thresh2, cv2.MORPH_CLOSE, kernel2, iterations=10)

# Find contours and filter for QR code
cnts2 = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts2 = cnts2[0] if len(cnts2) == 2 else cnts2[1]
c = sorted(cnts2, key=cv2.contourArea, reverse=True)[0]

draw = cv2.cvtColor(thresh2, cv2.COLOR_GRAY2BGR)
cv2.drawContours(draw, [c], 0, (0, 255, 0), 2)

rotrect = cv2.minAreaRect(c)
box = cv2.boxPoints(rotrect)
box = numpy.int0(box)
cv2.drawContours(draw, [box], 0, (0, 0, 255), 2)

cv2.imshow('thresh', thresh2)
cv2.imshow('ROI', ROI2)
cv2.imshow('minarearect', draw)

thresh ROI minAreaRect output

Expectant answered 28/4, 2022 at 22:59 Comment(2)
If I remeber on blog PyImageSearch there was artice - it was using loop which was rotating image and checking if it has vertical lines. But I think you could use red rectangle to calculate angle (tan(angle) = dy/dx)Bendite
detect the code (QRCodeDetector), then redraw it from the extracted modules (pixels), which is already given by the straight_qrcode value of QRCodeDetector::detectAndDecode. -- or use the detection result, which contains the corners of the code, and use that for an affine or perspective warp, but that could give you one of four possible rotations.Wicketkeeper
G
6

From my understanding, you're trying to deskew an image. To do this, we need to first compute the rotated bounding box angle then perform a linear transformation. The idea is to use cv2.minAreaRect + cv2.warpAffine. According to the documentation, cv2.minAreaRect returns

(center(x, y), (width, height), angle of rotation) = cv2.minAreaRect(...)

The third parameter gives us the angle we need to deskew the image.


Input image -> Output result

Skew angle: -39.99416732788086

Code

import cv2
import numpy as np

# Load image, grayscale, Otsu's threshold
image = cv2.imread('2.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = 255 - gray
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

# Compute rotated bounding box
coords = np.column_stack(np.where(thresh > 0))
angle = cv2.minAreaRect(coords)[-1]

if angle < -45:
    angle = -(90 + angle)
else:
    angle = -angle
print("Skew angle: ", angle)

# Rotate image to deskew
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
M = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(image, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

cv2.imshow('rotated', rotated)
cv2.waitKey()

Note: See Python OpenCV skew correction for another approach using the Projection Profile Method to correct skew.

Guinna answered 29/4, 2022 at 0:31 Comment(0)
W
2

Detect the code using QRCodeDetector::detectAndDecode and redraw it from the straight_qrcode value. The QRCodeDetector might not be able to decode all codes that you can locate using simple thresholding and contours. Especially when pieces of it are missing (including the quiet zone), the QR code detector might have trouble.

This will always show the code in its canonical orientation, with the finder patterns to the NW, NE, and SW directions.

Simple minAreaRect will only align the code's edges to the image axes, but it would not be able to tell which way is "up" in the QR code.

import cv2 as cv

im = cv.imread("OnDlO.png")

det = cv.QRCodeDetector()
(rv, points, straight_qrcode) = det.detectAndDecode(im)
# rv == 'testing123456'
# points:
# array([[[304.     ,  36.     ],
#         [415.     , 321.     ],
#         [141.55959, 428.3963 ],
#         [ 32.     , 151.     ]]], dtype=float32)

# some white padding
with_quiet_zone = cv.copyMakeBorder(straight_qrcode, 1, 1, 1, 1, borderType=cv.BORDER_CONSTANT, value=255)

# scale it up for display
larger = cv.resize(with_quiet_zone, dsize=None, fx=16, fy=16, interpolation=cv.INTER_NEAREST)

# and show it
cv.imshow("larger", larger)
cv.waitKey()

input:

enter image description here

output:

enter image description here

Wicketkeeper answered 29/4, 2022 at 8:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.