Python-使用OpenCV从图像和裁剪中检测QR码

问题描述 投票:1回答:3

我正在使用Python(3.7)和OpenCV进行项目,在其中我有一个文档的图像(使用相机捕获),上面带有QR码。

此QR码具有6个变量,分别为:

  1. QR码图像的大小

  2. 顶部

  3. 底部

  4. 单位

QR码的示例解码信息为:100、20、40、60、20,px

[现在,我需要从此文档图像中检测QR码,并且第一步,我需要将捕获的文档图像中QR码的大小与解码信息中提到的大小进行比较,例如,如果在捕获的图像中图像QR图像的尺寸为90X90px,解码信息的尺寸为100X100px,我们需要进行比较。

然后,在第二步中,我必须相应地使用顶部,右侧,底部和左侧变量来裁剪完整的图像。根据上面的示例,我们需要将图像从检测到的QR码位置裁剪为20px顶部,40px右侧,60px底部和20px右侧。我在下面添加了示例图片。

我已经完成了对QR码信息的解码,但是如何将检测到的QR码区域作为单独的图像,并将其尺寸与上述尺寸进行比较,然后相应地裁剪图像?

这是我到目前为止尝试过的:

import cv2

image = cv2.imread('/Users/abdul/PycharmProjects/QScanner/images/second.jpg')

qrCodeDetector = cv2.QRCodeDetector()
decodedText, points, _ = qrCodeDetector.detectAndDecode(image)
qr_data = decodedText.split(',')
qr_size = qr_data[0]
top = qr_data[1]
right = qr_data[2]
bottom = qr_data[3]
left = qr_data[4]

print(f'Size: {qr_size}' + str(qr_data[5]))
print(f'Top: {top}')
print(f'Right: {right}')
print(f'Bottom: {bottom}')
print(f'Left: {left}')
if points is not None:
    pts = len(points)
    print(pts)
    for i in range(pts):
        nextPointIndex = (i+1) % pts
        cv2.line(image, tuple(points[i][0]), tuple(points[nextPointIndex][0]), (255,0,0), 5)
        print(points[i][0])
    print(decodedText)    
    cv2.imshow("Image", image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
else:
    print("QR code not detected")

这是示例图片:

enter image description here

这是输入图像的示例:

enter image description here

python opencv computer-vision qr-code object-detection
3个回答
0
投票

我使用点获得了widthheight数据,并将其与qr_data大小进行比较。然后根据需要裁剪QR。

import cv2
import math  

image = cv2.imread('/ur/image/directory/qr.jpg')

qrCodeDetector = cv2.QRCodeDetector()
decodedText, points, _ = qrCodeDetector.detectAndDecode(image)
qr_data = decodedText.split(',')
qr_size = qr_data[0]
top = qr_data[1]
right = qr_data[2]
bottom = qr_data[3]
left = qr_data[4]

if points is not None:
    pts = len(points)
    print(pts)
    for i in range(pts):
        nextPointIndex = (i+1) % pts
        cv2.line(image, tuple(points[i][0]), tuple(points[nextPointIndex][0]), (255,0,0), 5)
        print(points[i][0])

    width = int(math.sqrt((points[0][0][0]-points[1][0][0])**2 + (points[0][0][1]-points[1][0][1])**2))
    height = int(math.sqrt((points[1][0][0]-points[2][0][0])**2 + (points[1][0][1]-points[2][0][1])**2))

    # Compare the size
    if(width==qr_data[0] and height==qr_data[0]):
        print("Sizes are equal")
    else:
        print("Width and height  " + str(width) + "x" +  str(height) + "  not equal to " 
        + str(qr_data[0]) + "x" + str(qr_data[0]))

    # Add the extension values to points and crop
    y = int(points[0][0][1]) - int(qr_data[1])
    x = int(points[0][0][0]) - int(qr_data[4])
    roi = image[y:y+height + int(qr_data[3]), x:x+width + int(qr_data[2])]
    print(decodedText)    
    cv2.imshow("Image", image)
    cv2.imshow("Crop", roi)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
else:
    print("QR code not detected")

结果:

enter image description here


0
投票

所以,您在这里主要有3个问题。

  1. 如果图像旋转角度为\ theta,
  2. 如果工作表是一个平面。 (即,在图像中,上线似乎不是线性的。但这应该没什么大不了的。)
  3. 黑色边框。您会一直拥有这些吗,还是背景不同?这很重要,因为如果没有这些内容,您将无法获得合理的结果。

我改进了您的代码,并删除了边框像素:

import cv2
import matplotlib.pyplot as plt    
import math
import numpy as np

image = cv2.imread('/Users/samettaspinar/Public/im.jpg')

qrCodeDetector = cv2.QRCodeDetector()
decodedText, points, _ = qrCodeDetector.detectAndDecode(image)
qr_data = decodedText.split(',')
qr_size = int(qr_data[0])
top = int(qr_data[1])
right = int(qr_data[2])
bottom = int(qr_data[3])
left = int(qr_data[4])

print(f'Size: {qr_size}' + str(qr_data[5]))
print(f'Top: {top}')
print(f'Right: {right}')
print(f'Bottom: {bottom}')
print(f'Left: {left}')

plt.imshow(image)
plt.show()

dists = [] #This is for estimating distances between corner points.
           #I will average them to find ratio of pixels in image vs qr_size  
           #in the optimal case, all dists should be equal

if points is not None:
    pts = len(points)
    for i in range(pts):
        p1 = points[i][0]
        p2 = points[(i+1) % pts][0]

        dists.append(math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2))

        print('line', tuple(p1), tuple(p2))
        image = cv2.line(image, tuple(p1), tuple(p2), (255,0,0), 5)
else:
    print("QR code not detected")

print('distances: ', dists)


# Remove the black border pixels. I had a simple idea for this
# Get the average intensity of the gray image
# If count the row average of the first half that are less than intensity/2. 
# It approx gives number of black borders on the left. etc.  
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
inten = np.mean(gray)

x = np.mean(gray, axis=0) # finds the vertical average
y = np.mean(gray, axis=1) # finds horizontal average

bl_left = np.sum([x[:int(col/2)] < inten/2])
bl_right = np.sum([x[int(col/2)+1:] < inten/2])

bl_top = np.sum([y[:int(row/2)] < inten/2])
bl_bottom = np.sum([y[int(row/2)+1:] < inten/2])

print('black margins: ', bl_left, bl_right, bl_top, bl_bottom)

# Estimate how many pixel you will crop out
ratio = np.mean(dists)/ int(qr_size)
print('actual px / qr_size in px: ', ratio)

row,col,dim = image.shape

top, left, right, bottom = int(top*ratio), int(left*ratio), int(right*ratio), int(bottom*ratio)
top += bl_top
left += bl_left
right += bl_right
bottom += bl_bottom

print('num pixels to be cropped: ', top, left, right, bottom)

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
image2 = image[top:row-bottom, left:col-right, :]

plt.imshow(image2)
plt.show()

注意,我忽略了轮换问题。如果存在旋转,则可以通过计算切线/弧度来计算角度,在此我可以计算出距离。


0
投票

这是使用阈值,形态学运算和轮廓滤波的简单方法。

  1. 获得二进制图像。加载图像,灰度,Gaussian blurOtsu's threshold

  2. [连接各个QR轮廓。cv2.getStructuringElement创建矩形结构内核,然后用cv2.getStructuringElement执行morphological operations

  3. QR码过滤器。cv2.MORPH_CLOSE并使用Find contourscontour approximationcontour area进行过滤。


检测到的二维码

aspect ratio

提取的QR码

enter image description here

从这里您可以将QR码与您的参考信息进行比较

代码

enter image description here
© www.soinside.com 2019 - 2024. All rights reserved.