Graduation project - Title: image correction based on machine vision (taking license plate recognition as an example) - image distortion correction

Keywords: Deep Learning image processing machine vision

0 Introduction

Today, the senior introduces a machine vision project to you

Image correction based on machine vision (taking license plate recognition as an example)

Bi design help, problem opening guidance, technical solutions
🇶746876041

1 Introduction to ideas

At present, the license plate recognition system can be seen everywhere at the door of each community, and the recognition effect seems to be OK. After consulting the data, it is found that the whole process can be subdivided into four parts: license plate location, distortion correction, license plate segmentation and content recognition. This essay mainly introduces two parts: license plate location and distortion correction, which are realized by opencv in python environment.

1.1 license plate location

At present, the mainstream license plate location methods can be divided into two categories: one is based on the background color feature of license plate; Another method is based on the contour shape feature of license plate. Based on color features can be divided into two categories: one in RGB space and the other in HSV space. After testing, it is found that the effect of using any method alone is not ideal. At present, it is common to use several positioning methods at the same time, or use one recognition and the other verification. In this paper, the license plate is located mainly through color features, mainly based on the H component of HSV space, supplemented by the R component and B component of RGB space, and then the length width ratio of the license plate is used to eliminate interference.

1.2 distortion correction

In the process of license plate image acquisition, the camera lens is usually not perpendicular to the license plate, so the license plate in the image to be recognized will be distorted to a certain extent, which brings some difficulties to the subsequent license plate content recognition. Therefore, it is necessary to correct the distortion of the license plate to eliminate the adverse effects of distortion.

2 code implementation

2.1 license plate location

2.1.1 select suspicious areas through color features

The images of the license plate under different lighting environments are taken, the background color is intercepted, and the channel separation and color space conversion are carried out by opencv. After the test, the following characteristics of the background color of the license plate are summarized:

  • (1) In HSV space, the value of H component usually hovers around 115, and the s component and V component vary greatly due to different illumination (the value range of H component in opencv is 0 to 179, not 0 to 360 in iconography; the value range of S component and V component is 0 to 255);

  • (2) In RGB space, the R component is usually small, generally below 30, the B component is usually large, generally above 80, and the G component fluctuates greatly;

  • (3) In the HSV space, the image is filled with light and saturated, that is, the S component and V component of the image are set to 255, and then the color space is converted from HSV space to RGB space. It is found that all R components become 0 and all B components become 255 (this operation will introduce large interference and will not be used later).

According to the above features, the suspicious license plate area can be preliminarily screened. Then, the gray image is operated, and the pixel value of the suspicious position is set to 255 and the pixel value of other positions is set to 0, that is, the image is binarized according to the features. In the binarized image, the suspicious area is represented by white and other areas are black. Then, the image can be further processed through expansion and corrosion .

for i in range(img_h):
    for j in range(img_w):
        # Ordinary blue license plate, while excluding the interference of transparent reflective materials
        if ((img_HSV[:, :, 0][i, j]-115)**2 < 15**2) and (img_B[i, j] > 70) and (img_R[i, j] < 40):
            img_gray[i, j] = 255
        else:
            img_gray[i, j] = 0

2.1.2 find the peripheral contour of the license plate

After selecting the suspicious area and binarizing the image, generally, only the pixel color of the license plate position in the image is white, but there will be some noise in some special cases. As shown in the above figure, because there is a blue bracket in the upper right corner of the image, which is consistent with the color characteristics of the license plate, it is also recognized as a license plate, which introduces noise.

Through observation, it can be found that there are great differences between the license plate area and noise, and the characteristics of the license plate area are obvious:

  • (1) According to the shape of China's conventional license plate, the shape of the license plate is a flat rectangle, and the length width ratio is about 3:1;

  • (2) The area of the license plate is much larger than the noise area, which is generally the largest white area in the image.

You can find the outline of the white area in the binarized image through the cv2.findContours() function.

Note: in opencv2 and opencv4, there are two return values of cv2.findContours(), while in opencv3, there are three return values. Depending on the opencv version, the code writing method will also be different.

# Detect all outer contours, leaving only four vertices of the rectangle
# opencv4.0, opencv2.x
contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# opencv3.x
_, contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

Here, because there are three white areas (license plate and two noises) in the binary image, the return value contours is a list with a length of 3. The list contains three arrays, and each array stores the contour information of a white area. The shape of each array is (n, 1, 2), that is, each array stores the coordinates of n points on the contour of the white area.

At present, we have obtained three array s, that is, three groups of contour information, but we do not know which is the group of contour information corresponding to the license plate area. At this time, we can filter the contour of the license plate area according to the above characteristics of the license plate.

#Shape and size screening verification
det_x_max = 0
det_y_max = 0
num = 0
for i in range(len(contours)):
    x_min = np.min(contours[i][ :, :, 0])
    x_max = np.max(contours[i][ :, :, 0])
    y_min = np.min(contours[i][ :, :, 1])
    y_max = np.max(contours[i][ :, :, 1])
    det_x = x_max - x_min
    det_y = y_max - y_min
    if (det_x / det_y > 1.8) and (det_x > det_x_max ) and (det_y > det_y_max ):
        det_y_max = det_y
        det_x_max = det_x
        num = i
# Get the contour point set of the most suspicious area
points = np.array(contours[num][:, 0])

The shape of the final points is (n, 2), which stores the coordinates of n points, which are distributed on the edge of the license plate

2.1.3 location of license plate area

After obtaining the point set on the contour of the license plate, you can use CV2. Minaarearect() to obtain the minimum circumscribed rectangle of the point set. The return value rect contains the center point coordinates, height, width, inclination angle and other information of the rectangle. You can use cv2.boxPoints() to obtain the coordinates of the four vertices of the rectangle.

# Obtain the minimum circumscribed matrix, center point coordinates, width, height and rotation angle
rect = cv2.minAreaRect(points)
# Gets the four vertices of a rectangle, floating point
box = cv2.boxPoints(rect)
# Rounding
box = np.int0(box)

However, we do not know which vertex of the rectangle these four coordinate points correspond to, so we can not make full use of these coordinate information.

Starting from the size characteristics of coordinate values, the four coordinates can be matched with the four vertices of the rectangle: in the opencv coordinate system, the smallest ordinate is top_point, the largest ordinate is bottom_point, the smallest abscissa is left_point, and the largest abscissa is right_point.

# Get four vertex coordinates
left_point_x = np.min(box[:, 0])
right_point_x = np.max(box[:, 0])
top_point_y = np.min(box[:, 1])
bottom_point_y = np.max(box[:, 1])

left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
# Up, down, left and right coordinates
vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y], [right_point_x, right_point_y]])


2.2 distortion correction

2.2.1 vertex location of license plate after distortion

In order to realize the distortion correction of license plate, the position relationship of corresponding points before and after distortion must be found.

It can be seen that the rectangular license plate becomes a parallelogram after distortion, so the license plate contour does not match the obtained rectangular contour. However, with the four vertex coordinates of the rectangle, the four vertex coordinates of the parallelogram license plate can be obtained through a simple geometric similarity relationship.

In this example, the four vertices of the parallelogram and the four vertices of the rectangle have the following relationship: rectangular vertex Top_Point,Bottom_Point and parallelogram vertex new_top_point,new_bottom_point coincident, rectangular vertex top_ Abscissa of point and parallelogram vertex NEW_ right_ The abscissa of point is the same, and the rectangular vertex bottom_ Abscissa of point and parallelogram vertex NEW_ left_ The abscissa of point is the same.

But in fact, due to different shooting angles, there may be two different distortions. The specific distortion can be judged according to the different inclination angles of the rectangle.

After judging the specific distortion, the coordinates of the four vertices of the parallelogram can be easily obtained by selecting the corresponding geometric similarity relationship, that is, the coordinates of the four vertices of the distorted license plate can be obtained.

In order to correct the license plate, the coordinates of the four vertices of the license plate before distortion need to be obtained. Because the standard size of the license plate in China is 440X140, the four vertex coordinates of the license plate before distortion can be specified as: (0,0), (440,0), (0140), (440140). The order should correspond to the four vertex coordinates after distortion.

# Distortion condition 1
if rect[2] > -45:
    new_right_point_x = vertices[0, 0]
    new_right_point_y = int(vertices[1, 1] - (vertices[0, 0]- vertices[1, 0]) / (vertices[3, 0] - vertices[1, 0]) * (vertices[1, 1] - vertices[3, 1]))
    new_left_point_x = vertices[1, 0]
    new_left_point_y = int(vertices[0, 1] + (vertices[0, 0] - vertices[1, 0]) / (vertices[0, 0] - vertices[2, 0]) * (vertices[2, 1] - vertices[0, 1]))
    # Corrected four vertex coordinates
    point_set_1 = np.float32([[440, 0],[0, 0],[0, 140],[440, 140]])
# Distortion condition 2
elif rect[2] < -45:
    new_right_point_x = vertices[1, 0]
    new_right_point_y = int(vertices[0, 1] + (vertices[1, 0] - vertices[0, 0]) / (vertices[3, 0] - vertices[0, 0]) * (vertices[3, 1] - vertices[0, 1]))
    new_left_point_x = vertices[0, 0]
    new_left_point_y = int(vertices[1, 1] - (vertices[1, 0] - vertices[0, 0]) / (vertices[1, 0] - vertices[2, 0]) * (vertices[1, 1] - vertices[2, 1]))
    # Corrected four vertex coordinates
    point_set_1 = np.float32([[0, 0],[0, 140],[440, 140],[440, 0]])

# Coordinates of four vertices of parallelogram before correction
new_box = np.array([(vertices[0, 0], vertices[0, 1]), (new_left_point_x, new_left_point_y), (vertices[1, 0], vertices[1, 1]), (new_right_point_x, new_right_point_y)])
point_set_0 = np.float32(new_box)

2.2.2 correction

This distortion is caused by the projection caused by the camera not perpendicular to the license plate, so it can be corrected by CV2. Warpprective().

# Transformation matrix
mat = cv2.getPerspectiveTransform(point_set_0, point_set_1)
# Projection transformation
lic = cv2.warpPerspective(img, mat, (440, 140))

# Created by Dan Cheng senior: Q746876041
import cv2
import numpy as np

# Pretreatment
def imgProcess(path):
    img = cv2.imread(path)
    # Uniform size
    img = cv2.resize(img, (640,480))
    # Gaussian blur
    img_Gas = cv2.GaussianBlur(img,(5,5),0)
    # RGB channel separation
    img_B = cv2.split(img_Gas)[0]
    img_G = cv2.split(img_Gas)[1]
    img_R = cv2.split(img_Gas)[2]
    # Read gray image and HSV space image
    img_gray = cv2.cvtColor(img_Gas, cv2.COLOR_BGR2GRAY)
    img_HSV = cv2.cvtColor(img_Gas, cv2.COLOR_BGR2HSV)
    return img, img_Gas, img_B, img_G, img_R, img_gray, img_HSV

# Preliminary identification
def preIdentification(img_gray, img_HSV, img_B, img_R):
    for i in range(480):
        for j in range(640):
            # Ordinary blue license plate, while excluding the interference of transparent reflective materials
            if ((img_HSV[:, :, 0][i, j]-115)**2 < 15**2) and (img_B[i, j] > 70) and (img_R[i, j] < 40):
                img_gray[i, j] = 255
            else:
                img_gray[i, j] = 0
    # Define core
    kernel_small = np.ones((3, 3))
    kernel_big = np.ones((7, 7))

    img_gray = cv2.GaussianBlur(img_gray, (5, 5), 0) # Gaussian smoothing
    img_di = cv2.dilate(img_gray, kernel_small, iterations=5) # Corrosion for 5 times
    img_close = cv2.morphologyEx(img_di, cv2.MORPH_CLOSE, kernel_big) # Closed operation
    img_close = cv2.GaussianBlur(img_close, (5, 5), 0) # Gaussian smoothing
    _, img_bin = cv2.threshold(img_close, 100, 255, cv2.THRESH_BINARY) # Binarization
    return img_bin

# location
def fixPosition(img, img_bin):
    # Detect all outer contours, leaving only four vertices of the rectangle
    contours, _ = cv2.findContours(img_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    #Shape and size screening verification
    det_x_max = 0
    det_y_max = 0
    num = 0
    for i in range(len(contours)):
        x_min = np.min(contours[i][ :, :, 0])
        x_max = np.max(contours[i][ :, :, 0])
        y_min = np.min(contours[i][ :, :, 1])
        y_max = np.max(contours[i][ :, :, 1])
        det_x = x_max - x_min
        det_y = y_max - y_min
        if (det_x / det_y > 1.8) and (det_x > det_x_max ) and (det_y > det_y_max ):
            det_y_max = det_y
            det_x_max = det_x
            num = i
    # Get the contour point set of the most suspicious area
    points = np.array(contours[num][:, 0])
    return points


#img_lic_canny = cv2.Canny(img_lic_bin, 100, 200)


def findVertices(points):
    # Obtain the minimum circumscribed matrix, center point coordinates, width, height and rotation angle
    rect = cv2.minAreaRect(points)
    # Gets the four vertices of a rectangle, floating point
    box = cv2.boxPoints(rect)
    # Rounding
    box = np.int0(box)
    # Get four vertex coordinates
    left_point_x = np.min(box[:, 0])
    right_point_x = np.max(box[:, 0])
    top_point_y = np.min(box[:, 1])
    bottom_point_y = np.max(box[:, 1])

    left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
    right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
    top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
    bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
    # Up, down, left and right coordinates
    vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y], [right_point_x, right_point_y]])
    return vertices, rect

def tiltCorrection(vertices, rect):
    # Distortion condition 1
    if rect[2] > -45:
        new_right_point_x = vertices[0, 0]
        new_right_point_y = int(vertices[1, 1] - (vertices[0, 0]- vertices[1, 0]) / (vertices[3, 0] - vertices[1, 0]) * (vertices[1, 1] - vertices[3, 1]))
        new_left_point_x = vertices[1, 0]
        new_left_point_y = int(vertices[0, 1] + (vertices[0, 0] - vertices[1, 0]) / (vertices[0, 0] - vertices[2, 0]) * (vertices[2, 1] - vertices[0, 1]))
        # Corrected four vertex coordinates
        point_set_1 = np.float32([[440, 0],[0, 0],[0, 140],[440, 140]])
    # Distortion condition 2
    elif rect[2] < -45:
        new_right_point_x = vertices[1, 0]
        new_right_point_y = int(vertices[0, 1] + (vertices[1, 0] - vertices[0, 0]) / (vertices[3, 0] - vertices[0, 0]) * (vertices[3, 1] - vertices[0, 1]))
        new_left_point_x = vertices[0, 0]
        new_left_point_y = int(vertices[1, 1] - (vertices[1, 0] - vertices[0, 0]) / (vertices[1, 0] - vertices[2, 0]) * (vertices[1, 1] - vertices[2, 1]))
        # Corrected four vertex coordinates
        point_set_1 = np.float32([[0, 0],[0, 140],[440, 140],[440, 0]])

    # Coordinates of four vertices of parallelogram before correction
    new_box = np.array([(vertices[0, 0], vertices[0, 1]), (new_left_point_x, new_left_point_y), (vertices[1, 0], vertices[1, 1]), (new_right_point_x, new_right_point_y)])
    point_set_0 = np.float32(new_box)
    return point_set_0, point_set_1, new_box

def transform(img, point_set_0, point_set_1):
    # Transformation matrix
    mat = cv2.getPerspectiveTransform(point_set_0, point_set_1)
    # Projection transformation
    lic = cv2.warpPerspective(img, mat, (440, 140))
    return lic

def main():
    path = 'F:\\Python\\license_plate\\test\\9.jpg'
    # Image preprocessing
    img, img_Gas, img_B, img_G, img_R, img_gray, img_HSV = imgProcess(path)
    # Preliminary identification
    img_bin  = preIdentification(img_gray, img_HSV, img_B, img_R)
    points = fixPosition(img, img_bin)
    vertices, rect = findVertices(points)
    point_set_0, point_set_1, new_box = tiltCorrection(vertices, rect)
    img_draw = cv2.drawContours(img.copy(), [new_box], -1, (0,0,255), 3)
    lic = transform(img, point_set_0, point_set_1)
    # The license plate is framed on the original drawing
    cv2.namedWindow("Image")
    cv2.imshow("Image", img_draw)
    # Binary image
    cv2.namedWindow("Image_Bin")
    cv2.imshow("Image_Bin", img_bin)
    # Display corrected license plate
    cv2.namedWindow("Lic")
    cv2.imshow("Lic", lic)
    # Pause, close window
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == '__main__':
    main()

7 finally - design help

Bi design help, problem opening guidance, technical solutions
🇶746876041

Posted by lancia on Wed, 01 Dec 2021 03:33:31 -0800