Python - Detect a QR code from an image and crop using OpenCV

后端 未结 3 778
不思量自难忘°
不思量自难忘° 2020-12-18 13:37

I\'m working on a project using Python(3.7) and OpenCV in which I have an Image(captured using the camera) of a document with a QR code placed on it.

This QR code ha

3条回答
  •  太阳男子
    2020-12-18 13:53

    So, you mainly have 3 problems here.

    1. If the image is rotated with an angle \theta,
    2. If the sheet is one a plane. (i.e., in the images, the upper line doesn't seem to be linear. But it should not be a big deal.)
    3. The black borders. Will you always have those or may it be a different background? This is important because without cropping out those, you won't be able to get a reasonable result.

    I improved your code a little bit and removed the border pixels:

    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()
    

    Notice that I ignored the rotation issue. If there is rotation, you can find the angle by calculating the tangents/arctan where I calculated the distances.

提交回复
热议问题