简体   繁体   English

将车牌图像变形为正面平行

[英]Warping a license plate image to be frontal-parallel

I am trying to take an image of a license plate so that I can then do some image processing to draw contours around the plate, which I can then use to warp the perspective to then view the plate face on.我正在尝试拍摄车牌图像,以便我可以进行一些图像处理以在车牌周围绘制轮廓,然后我可以使用它来扭曲透视图,然后查看车牌表面。 Unfortunately, I am getting an error which occurs when I am trying to draw contours around an image I have processed.不幸的是,当我尝试在已处理的图像周围绘制轮廓时出现错误。 Specifically, I get an Invalid shape (4, 1, 2) for the image data error.具体来说,我得到一个Invalid shape (4, 1, 2) for the image data错误。 I am not too sure how I can go about solving this as I know that all the other images I have processed are fine.我不太确定如何 go 解决这个问题,因为我知道我处理过的所有其他图像都很好。 It's just when I try to draw contours something is going wrong.就在我尝试绘制轮廓时出了点问题。

import cv2
import numpy as np
from matplotlib import pyplot as plt

kernel = np.ones((3,3))
image = cv2.imread('NoPlate0.jpg')

def getContours(img):
    biggest = np.array([])
    maxArea = 0

    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > 500:
            cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3)
            peri = cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt,0.02*peri, True)
            if area > maxArea and len(approx) == 4:
                biggest = approx
                maxArea = area
    return biggest

imgGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray,(5,5),1)
imgCanny = cv2.Canny(imgBlur,150,200)
imgDial = cv2.dilate(imgCanny,kernel,iterations=2)
imgThres = cv2.erode(imgDial,kernel,iterations=2)
imgContour = image.copy()

titles = ['original', 'Blur', 'Canny', 'Dialte', 'Threshold', 'Contours' ]
images = [image,  imgBlur, imgCanny, imgDial, imgThres, getContours(imgThres)]

for i in range(6):
    plt.subplot(3, 3, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])

plt.show()

The exact error I am getting is this:我得到的确切错误是:

TypeError: Invalid shape (4, 1, 2) for image data

I am using the following image below as my input:我使用下面的图片作为我的输入:

车牌

Your function only returns the actual points along the contour, which you then try to call plt.imshow on.您的 function 仅返回沿轮廓的实际点,然后您尝试调用plt.imshow This is why you are getting this error.这就是您收到此错误的原因。 What you need to do is use cv2.drawContour with this contour to get what you want.您需要做的是将cv2.drawContour与此轮廓一起使用以获得您想要的。 In this case, we should restructure your getContours function so that it returns the both the coordinates (so you can use this for later) and the actual contours drawn on the image itself.在这种情况下,我们应该重组您的getContours function 以便它返回坐标(以便您以后可以使用它)和在图像本身上绘制的实际轮廓。 Instead of mutating imgContour and treating it like a global variable, only draw to this image once which will be the largest contour found in the loop:与其改变imgContour并将其视为全局变量,不如仅绘制此图像一次,这将是循环中找到的最大轮廓:

def getContours(img):
    biggest = np.array([])
    maxArea = 0
    imgContour = img.copy()  # Change - make a copy of the image to return
    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    index = None
    for i, cnt in enumerate(contours):  # Change - also provide index
        area = cv2.contourArea(cnt)
        if area > 500:
            peri = cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt,0.02*peri, True)
            if area > maxArea and len(approx) == 4:
                biggest = approx
                maxArea = area
                index = i  # Also save index to contour

    if index is not None: # Draw the biggest contour on the image
        cv2.drawContours(imgContour, contours, index, (255, 0, 0), 3)

    return biggest, imgContour  # Change - also return drawn image

Finally we can use this in your overall code in the following way:最后,我们可以通过以下方式在您的整体代码中使用它:

import cv2
import numpy as np
from matplotlib import pyplot as plt

kernel = np.ones((3,3))
image = cv2.imread('NoPlate0.jpg')

imgGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray,(5,5),1)
imgCanny = cv2.Canny(imgBlur,150,200)
imgDial = cv2.dilate(imgCanny,kernel,iterations=2)
imgThres = cv2.erode(imgDial,kernel,iterations=2)
biggest, imgContour = getContours(imgThres)  # Change

titles = ['original', 'Blur', 'Canny', 'Dilate', 'Threshold', 'Contours']
images = [image,  imgBlur, imgCanny, imgDial, imgThres, imgContour]  # Change

for i in range(6):
    plt.subplot(3, 3, i+1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])

plt.show()

As a final note, if you want to warp the license plate image so that it's parallel to the image plane, you can use cv2.getPerspectiveTransform to define a homography going from the original source image (the source points) to the warped image (the destination points), then use cv2.warpPerspective to finally warp the image.最后一点,如果你想扭曲车牌图像,使其平行于图像平面,你可以使用cv2.getPerspectiveTransform来定义从原始源图像(源点)到扭曲图像(目标点),然后使用cv2.warpPerspective最终扭曲图像。 Take note that the way the source and destination points is such that they need to be ordered so that their corresponding locations match in perspective.请注意,源点和目标点的方式需要对它们进行排序,以便它们的相应位置在视角上匹配。 That is, if the first point of the set of points defining the quadrilateral of your region was the top left, the source and destination points should both be defining the top left corner.也就是说,如果定义您所在区域的四边形的点集的第一个点是左上角,则源点和目标点都应该定义左上角。 You can do this by finding the centroid of the quadrilaterals for both the source and destination, then finding the angle subtended from the centroid to each of the corners and ordering both of them that way by sorting the angles.您可以通过为源和目标找到四边形的质心,然后找到从质心到每个角的对角,并通过对角度进行排序来对它们进行排序。

Here's the following function I wrote that does this called order_points :这是我写的以下 function ,它执行此操作称为order_points

def order_points(pts):
    # Step 1: Find centre of object
    center = np.mean(pts)

    # Step 2: Move coordinate system to centre of object
    shifted = pts - center

    # Step #3: Find angles subtended from centroid to each corner point
    theta = np.arctan2(shifted[:, 0], shifted[:, 1])

    # Step #4: Return vertices ordered by theta
    ind = np.argsort(theta)
    return pts[ind]

Finally, with the corner points you returned, try doing:最后,使用您返回的角点,尝试执行以下操作:

src = np.squeeze(biggest).astype(np.float32) # Source points
height = image.shape[0]
width = image.shape[1]
# Destination points
dst = np.float32([[0, 0], [0, height - 1], [width - 1, 0], [width - 1, height - 1]])

# Order the points correctly
src = order_points(src)
dst = order_points(dst)

# Get the perspective transform
M = cv2.getPerspectiveTransform(src, dst)

# Warp the image
img_shape = (width, height)
warped = cv2.warpPerspective(img, M, img_shape, flags=cv2.INTER_LINEAR)

src are the four corners of the source polygon that encompasses the license plate. src是包含牌照的源多边形的四个角。 Take note because they're returned from cv2.approxPolyDP , they will be a 4 x 1 x 2 NumPy array of integers.请注意,因为它们是从cv2.approxPolyDP返回的,所以它们将是一个4 x 1 x 2 NumPy 整数数组。 You will need to remove the singleton second dimension and convert these into 32-bit floating-point so that they can be used with cv2.getPerspectiveTransform .您需要删除 singleton 第二维并将它们转换为 32 位浮点数,以便它们可以与cv2.getPerspectiveTransform一起使用。 dst are the destination points where each of the corners in the source polygon get mapped to the corner points of actual output image dimensions, which will be the same size as the input image. dst是目标点,源多边形中的每个角都映射到实际 output 图像尺寸的角点,这将与输入图像大小相同。 One last thing to remember is that with cv2.warpPerspective , you specify the size of the image as (width, height) .最后要记住的是,使用cv2.warpPerspective时,您将图像的大小指定为(width, height)

If you finally want to integrate this all together and make the getContours function return the warped image, we can do this very easily.如果您最终想要将所有这些集成在一起并使getContours function 返回扭曲的图像,我们可以很容易地做到这一点。 We have to modify a few things to get this to work as intended:我们必须修改一些东西才能让它按预期工作:

  1. getContours will also take in the original RGB image so that we can properly visualise the contour and get a better perspective on how the license plate is being localised. getContours还将获取原始 RGB 图像,以便我们可以正确地可视化轮廓并更好地了解车牌是如何定位的。
  2. Add in the logic to warp the image inside getContours as I showed above.如上所示,添加逻辑以在getContours中扭曲图像。
  3. Change the plotting code to also include this warped image as well as return the warped image from getContours .更改绘图代码以也包含此变形图像并从getContours返回变形图像。
  4. Modify the plotting code slightly for showing the original image in Matplotlib, as cv2.imread reads in images in BGR format, but Matplotlib expects images to be in RGB format.稍微修改绘图代码以显示 Matplotlib 中的原始图像,因为cv2.imread以 BGR 格式读取图像,但 Matplotlib 期望图像为 RGB 格式。

Therefore:所以:

import cv2
import numpy as np
from matplotlib import pyplot as plt

def order_points(pts):
    # Step 1: Find centre of object
    center = np.mean(pts)

    # Step 2: Move coordinate system to centre of object
    shifted = pts - center

    # Step #3: Find angles subtended from centroid to each corner point
    theta = np.arctan2(shifted[:, 0], shifted[:, 1])

    # Step #4: Return vertices ordered by theta
    ind = np.argsort(theta)
    return pts[ind]

def getContours(img, orig):  # Change - pass the original image too
    biggest = np.array([])
    maxArea = 0
    imgContour = orig.copy()  # Make a copy of the original image to return
    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    index = None
    for i, cnt in enumerate(contours):  # Change - also provide index
        area = cv2.contourArea(cnt)
        if area > 500:
            peri = cv2.arcLength(cnt, True)
            approx = cv2.approxPolyDP(cnt,0.02*peri, True)
            if area > maxArea and len(approx) == 4:
                biggest = approx
                maxArea = area
                index = i  # Also save index to contour

    warped = None  # Stores the warped license plate image
    if index is not None: # Draw the biggest contour on the image
        cv2.drawContours(imgContour, contours, index, (255, 0, 0), 3)

        src = np.squeeze(biggest).astype(np.float32) # Source points
        height = image.shape[0]
        width = image.shape[1]
        # Destination points
        dst = np.float32([[0, 0], [0, height - 1], [width - 1, 0], [width - 1, height - 1]])

        # Order the points correctly
        biggest = order_points(src)
        dst = order_points(dst)

        # Get the perspective transform
        M = cv2.getPerspectiveTransform(src, dst)

        # Warp the image
        img_shape = (width, height)
        warped = cv2.warpPerspective(orig, M, img_shape, flags=cv2.INTER_LINEAR)

    return biggest, imgContour, warped  # Change - also return drawn image

kernel = np.ones((3,3))
image = cv2.imread('NoPlate0.jpg')

imgGray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray,(5,5),1)
imgCanny = cv2.Canny(imgBlur,150,200)
imgDial = cv2.dilate(imgCanny,kernel,iterations=2)
imgThres = cv2.erode(imgDial,kernel,iterations=2)
biggest, imgContour, warped = getContours(imgThres, image)  # Change

titles = ['Original', 'Blur', 'Canny', 'Dilate', 'Threshold', 'Contours', 'Warped']  # Change - also show warped image
images = [image[...,::-1],  imgBlur, imgCanny, imgDial, imgThres, imgContour, warped]  # Change

# Change - Also show contour drawn image + warped image
for i in range(5):
    plt.subplot(3, 3, i+1)
    plt.imshow(images[i], cmap='gray')
    plt.title(titles[i])

plt.subplot(3, 3, 6)
plt.imshow(images[-2])
plt.title(titles[-2])

plt.subplot(3, 3, 8)
plt.imshow(images[-1])
plt.title(titles[-1])

plt.show()

The figure I get is now:我现在得到的数字是:

最终图

You need to reshape biggest which is returned by getContours() to (4, 2).您需要将getContours()返回的biggest重塑为 (4, 2)。 And also if you want to have the warped image then you need to import imutils.而且如果你想要扭曲的图像,那么你需要导入 imutils。 So to solve your issue, please do the followings:因此,要解决您的问题,请执行以下操作:

  1. import the four_point_transform function by adding:通过添加以下内容导入four_point_transform function:

from imutils.perspective import four_point_transform

  1. And change the return statement of getContours() function like below:并更改getContours() function 的return语句,如下所示:

return four_point_transform(img, biggest.reshape(4, 2))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM