将车牌图像变形为正面平行
Warping a license plate image to be frontal-parallel
我正在尝试拍摄一张车牌图像,这样我就可以进行一些图像处理以绘制车牌周围的轮廓,然后我可以使用它来扭曲视角,然后查看车牌的正面。不幸的是,当我尝试在已处理的图像周围绘制轮廓时出现错误。具体来说,我收到 Invalid shape (4, 1, 2) for the image data
错误。我不太确定如何解决这个问题,因为我知道我处理过的所有其他图像都很好。只是当我尝试绘制轮廓时出现问题。
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()
我得到的确切错误是:
TypeError: Invalid shape (4, 1, 2) for image data
我正在使用下图作为我的输入:
您需要将 getContours()
返回的 biggest
重塑为 (4, 2)。而且如果你想要扭曲的图像,那么你需要导入 imutils。因此,要解决您的问题,请执行以下操作:
- 通过添加以下内容导入
four_point_transform
函数:
from imutils.perspective import four_point_transform
- 并更改
getContours()
函数的 return
语句,如下所示:
return four_point_transform(img, biggest.reshape(4, 2))
您的函数仅 return 沿着轮廓的实际点,然后您尝试调用 plt.imshow
。这就是您收到此错误的原因。你需要做的是使用 cv2.drawContour
和这个轮廓来得到你想要的。在这种情况下,我们应该重构您的 getContours
函数,使其 return 既是坐标(以便您以后可以使用它),又是在图像本身上绘制的实际轮廓。不要改变 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
最后我们可以通过以下方式在您的整体代码中使用它:
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()
最后一点,如果您想扭曲车牌图像,使其与图像平面平行,您可以使用 cv2.getPerspectiveTransform
定义从原始源图像(源点) 到扭曲图像(目标点),然后使用 cv2.warpPerspective
最终扭曲图像。请注意,源点和目标点的方式需要对它们进行排序,以便它们的相应位置在视角上匹配。也就是说,如果定义您所在区域的四边形的点集的第一个点是左上角,则源点和目标点都应该定义左上角。您可以通过为源和目标找到四边形的质心,然后找到从质心到每个角的对向角并通过对角度排序来对它们进行排序来做到这一点。
这是我编写的以下函数,它调用 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]
最后,使用您 return 编辑的角点,尝试执行以下操作:
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
是包含牌照的源多边形的四个角。请注意,因为它们是 return 从 cv2.approxPolyDP
编辑而来,它们将是一个 4 x 1 x 2
NumPy 整数数组。您将需要删除单例第二维并将它们转换为 32 位浮点数,以便它们可以与 cv2.getPerspectiveTransform
一起使用。 dst
是目标点,源多边形中的每个角都映射到实际输出图像尺寸的角点,这将与输入图像大小相同。最后要记住的是,使用 cv2.warpPerspective
,您将图像的大小指定为 (width, height)
。
如果您最终想将所有这些整合在一起并使 getContours
函数 return 变形图像,我们可以很容易地做到这一点。我们必须修改一些东西才能让它按预期工作:
getContours
还将采用原始 RGB 图像,以便我们可以正确地可视化轮廓并更好地了解车牌是如何定位的。
- 添加逻辑以在
getContours
内扭曲图像,如上所示。
- 更改绘图代码以包含此变形图像以及 return 来自
getContours
的变形图像。
- 稍微修改绘图代码以在 Matplotlib 中显示原始图像,因为
cv2.imread
以 BGR 格式读取图像,但 Matplotlib 期望图像为 RGB 格式。
因此:
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()
我现在得到的数字是:
我正在尝试拍摄一张车牌图像,这样我就可以进行一些图像处理以绘制车牌周围的轮廓,然后我可以使用它来扭曲视角,然后查看车牌的正面。不幸的是,当我尝试在已处理的图像周围绘制轮廓时出现错误。具体来说,我收到 Invalid shape (4, 1, 2) for the image data
错误。我不太确定如何解决这个问题,因为我知道我处理过的所有其他图像都很好。只是当我尝试绘制轮廓时出现问题。
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()
我得到的确切错误是:
TypeError: Invalid shape (4, 1, 2) for image data
我正在使用下图作为我的输入:
您需要将 getContours()
返回的 biggest
重塑为 (4, 2)。而且如果你想要扭曲的图像,那么你需要导入 imutils。因此,要解决您的问题,请执行以下操作:
- 通过添加以下内容导入
four_point_transform
函数:
from imutils.perspective import four_point_transform
- 并更改
getContours()
函数的return
语句,如下所示:
return four_point_transform(img, biggest.reshape(4, 2))
您的函数仅 return 沿着轮廓的实际点,然后您尝试调用 plt.imshow
。这就是您收到此错误的原因。你需要做的是使用 cv2.drawContour
和这个轮廓来得到你想要的。在这种情况下,我们应该重构您的 getContours
函数,使其 return 既是坐标(以便您以后可以使用它),又是在图像本身上绘制的实际轮廓。不要改变 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
最后我们可以通过以下方式在您的整体代码中使用它:
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()
最后一点,如果您想扭曲车牌图像,使其与图像平面平行,您可以使用 cv2.getPerspectiveTransform
定义从原始源图像(源点) 到扭曲图像(目标点),然后使用 cv2.warpPerspective
最终扭曲图像。请注意,源点和目标点的方式需要对它们进行排序,以便它们的相应位置在视角上匹配。也就是说,如果定义您所在区域的四边形的点集的第一个点是左上角,则源点和目标点都应该定义左上角。您可以通过为源和目标找到四边形的质心,然后找到从质心到每个角的对向角并通过对角度排序来对它们进行排序来做到这一点。
这是我编写的以下函数,它调用 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]
最后,使用您 return 编辑的角点,尝试执行以下操作:
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
是包含牌照的源多边形的四个角。请注意,因为它们是 return 从 cv2.approxPolyDP
编辑而来,它们将是一个 4 x 1 x 2
NumPy 整数数组。您将需要删除单例第二维并将它们转换为 32 位浮点数,以便它们可以与 cv2.getPerspectiveTransform
一起使用。 dst
是目标点,源多边形中的每个角都映射到实际输出图像尺寸的角点,这将与输入图像大小相同。最后要记住的是,使用 cv2.warpPerspective
,您将图像的大小指定为 (width, height)
。
如果您最终想将所有这些整合在一起并使 getContours
函数 return 变形图像,我们可以很容易地做到这一点。我们必须修改一些东西才能让它按预期工作:
getContours
还将采用原始 RGB 图像,以便我们可以正确地可视化轮廓并更好地了解车牌是如何定位的。- 添加逻辑以在
getContours
内扭曲图像,如上所示。 - 更改绘图代码以包含此变形图像以及 return 来自
getContours
的变形图像。 - 稍微修改绘图代码以在 Matplotlib 中显示原始图像,因为
cv2.imread
以 BGR 格式读取图像,但 Matplotlib 期望图像为 RGB 格式。
因此:
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()
我现在得到的数字是: