【问题标题】:Warping a license plate image to be frontal-parallel将车牌图像扭曲为正面平行
【发布时间】:2020-09-29 08:57:31
【问题描述】:

我正在尝试拍摄车牌的图像,这样我就可以进行一些图像处理以在车牌周围绘制轮廓,然后我可以使用这些轮廓来扭曲透视图,然后查看车牌正面。不幸的是,当我尝试在已处理的图像周围绘制轮廓时出现错误。具体来说,我收到 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

我使用下面的图片作为我的输入:

【问题讨论】:

    标签: python opencv image-processing computer-vision homography


    【解决方案1】:

    您需要将getContours() 返回的biggest 重塑为(4, 2)。而且,如果你想拥有扭曲的图像,那么你需要导入 imutils。因此,要解决您的问题,请执行以下操作:

    1. 通过添加导入four_point_transform函数:

    from imutils.perspective import four_point_transform

    1. 并改变getContours()函数的return语句如下:

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

    【讨论】:

      【解决方案2】:

      您的函数仅返回沿轮廓的实际点,然后您尝试在其上调用plt.imshow。这就是您收到此错误的原因。你需要做的是使用cv2.drawContour 这个轮廓来得到你想要的。在这种情况下,我们应该重构您的 getContours 函数,以便它返回坐标(以便您以后可以使用它)和在图像本身上绘制的实际轮廓。与其改变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]
      

      最后,用你返回的角点,尝试做:

      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 是包围车牌的源多边形的四个角。请注意,因为它们是从 cv2.approxPolyDP 返回的,它们将是一个 4 x 1 x 2 NumPy 整数数组。您将需要删除单例第二维并将它们转换为 32 位浮点,以便它们可以与 cv2.getPerspectiveTransform 一起使用。 dst 是目标点,源多边形中的每个角都映射到实际输出图像尺寸的角点,其大小与输入图像相同。最后要记住的一件事是,使用cv2.warpPerspective,您将图像的大小指定为(width, height)

      如果您最终想将所有这些整合在一起并让getContours 函数返回扭曲的图像,我们可以很容易地做到这一点。我们必须修改一些东西才能使其按预期工作:

      1. getContours 还将获取原始 RGB 图像,以便我们可以正确地可视化轮廓并更好地了解车牌是如何定位的。
      2. 添加逻辑以扭曲getContours 中的图像,如上所示。
      3. 更改绘图代码以包含此变形图像,并从 getContours 返回变形图像。
      4. 稍微修改绘图代码以在 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()
      

      我现在得到的图是:

      【讨论】:

        猜你喜欢
        • 2023-03-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-01
        • 2014-08-13
        • 2016-03-18
        相关资源
        最近更新 更多