[英]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:我们必须修改一些东西才能让它按预期工作:
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 图像,以便我们可以正确地可视化轮廓并更好地了解车牌是如何定位的。getContours
as I showed above.getContours
中扭曲图像。getContours
.getContours
返回变形图像。cv2.imread
reads in images in BGR format, but Matplotlib expects images to be in RGB format.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:
因此,要解决您的问题,请执行以下操作:
four_point_transform
function by adding:four_point_transform
function: from imutils.perspective import four_point_transform
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.