MinAreaRect not taking angle into consideration

Viewed 226

I have a (gray-scale) license plate image which looks like this:

enter image description here

The threshold image from which I took contours is this:

enter image description here

Now when I draw the contours which I have taken from the threshold image onto the original image I get this result:

enter image description here

At this point, everything is OK, I have a contour that is slightly uneven (it's not a perfect rectangle) and it is tilted. To fix the tilt I try to perform perspective transformation, but the problem is that the cv2.minAreaRect(contour) when drawn seems to return this (red outline), indicating that the tilt angle is zero, which it actually shows as -0.0:

enter image description here

Here is the code:

import numpy as np
import cv2
from imutils.perspective import order_points


clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))

def clean2_plate(plate):
  equalized = clahe.apply(plate)
  blurred = cv2.bilateralFilter(equalized, 11, 75, 75)
  thresh_otsu = cv2.threshold(blurred, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]

  contours = cv2.findContours(thresh_otsu, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
  contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]

  for cnt in contours:
    x, y, w, h = cv2.boundingRect(cnt)
    final_img = plate[y:y+h, x:x+w]

    if clean_ratio_check(w, h, debug): # if certain height-width conditions are met
      return final_img, cnt
  
  print("Plate cleaning failed!")
  return plate_orig, None


def perspective_transformation(img, cnt, debug=False):
  """Perform perspective transformation for distorted license plates."""
  hull = cv2.convexHull(cnt, returnPoints=True)
  box = cv2.minAreaRect(hull)
  box = cv2.boxPoints(box)
  box = np.array(box, dtype="int")
  src_pts = order_points(box)

  # use Euclidean distance to get width & height
  width = int(np.linalg.norm(src_pts[0] - src_pts[1]))
  height = int(np.linalg.norm(src_pts[0] - src_pts[3]))

  dst_pts = np.array([[0,0], [width,0], [width,height], [0,height]], dtype=np.float32)

  M = cv2.getPerspectiveTransform(src_pts, dst_pts)
  warped_img = cv2.warpPerspective(img, M, (width, height))

  return warped_img


# MAIN
img = cv2.imread('input_img.png')
clean_plate, clean_cnt = clean2_plate(plate)

if clean_cnt is not None:
  plate_rot = perspective_transformation(clean_plate, clean_cnt)
  cv2_imshow(plate_rot)

My question is why the cv2.minAreaRect(contour) does not return an angle, as the image is visible tilted.

1 Answers

Using information from the comment of Christoph Rackwitz as a baseline, I edited my approach to the following:

def perspective_transformation_2(img, box):
  """Perform perspective transformation for distorted license plates."""
  box = np.array(box, dtype="int")
  src_pts = order_points(box)

  # use Euclidean distance to get width & height
  width = int(np.linalg.norm(src_pts[0] - src_pts[1]))
  height = int(np.linalg.norm(src_pts[0] - src_pts[3]))

  dst_pts = np.array([[0,0], [width,0], [width,height], [0,height]], dtype=np.float32)

  M = cv2.getPerspectiveTransform(src_pts, dst_pts)
  warped_img = cv2.warpPerspective(img, M, (width, height))

  return warped_img



# MAIN

img = cv2.imread('input_img.png')
clean_plate, clean_cnt = clean2_plate(plate)

if clean_cnt is not None:
  epsilon = 0.009 * cv2.arcLength(clean_cnt, True)
  approximations = cv2.approxPolyDP(clean_cnt, epsilon, True)
  approximations = approximations.reshape(4,2)
  # if len(approximations) == 4:
  plate_rot = perspective_transformation_2(clean_plate, approximations)

And the resulting image: enter image description here

Related