1
0

feat: add not working code

This commit is contained in:
2026-04-07 23:40:07 +08:00
parent 6a14c67f99
commit e408913281

View File

@@ -7,6 +7,37 @@ import logging
from pathlib import Path
from dataclasses import dataclass
def _uniform_car_plate(img: cv.typing.MatLike) -> cv.typing.MatLike:
"""
Uniform the image size to 512x512 while maintaining aspect ratio, padding with black.
:param img: The image in BGR format to be uniformed.
:return: The uniformed image in BGR format.
"""
UNI_HW: int = 512
# Calculate the new width and height for given image
h, w = img.shape[:2]
scale = min(UNI_HW / w, UNI_HW / h)
new_w = int(w * scale)
new_h = int(h * scale)
# Resize the image
resized_img = cv.resize(img, (new_w, new_h))
# Create a black canvas of size UNI_HW x UNI_HW
padded_img = np.zeros((UNI_HW, UNI_HW, 3), dtype=np.uint8)
# Calculate position to paste the resized image (centered)
y_offset = (UNI_HW - new_h) // 2
x_offset = (UNI_HW - new_w) // 2
# Paste the resized image onto the canvas
padded_img[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized_img
# Return the padded image
return padded_img
def extract_car_plate(img: cv.typing.MatLike) -> typing.Optional[cv.typing.MatLike]:
"""Extract the car plate part from given image.
@@ -14,27 +45,108 @@ def extract_car_plate(img: cv.typing.MatLike) -> typing.Optional[cv.typing.MatLi
:param img: The image containing car plate in BGR format.
:return: The image of binary car plate in U8 format if succeed, otherwise None.
"""
# Step 1: Convert to grayscale
# Reference: https://www.cnblogs.com/linuxAndMcu/p/19144795
# Resize the image to make following step works about finding proper contours.
img = _uniform_car_plate(img)
# Convert to grayscale image
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Histogram balance to increase contrast
hist_gray = cv.equalizeHist(gray)
# Step 2: Apply Gaussian blur to reduce noise
blurred = cv.GaussianBlur(gray, (5, 5), 0)
# Apply Gaussian blur to reduce noise
blurred = cv.GaussianBlur(hist_gray, (5, 5), 0)
# Step 3: Edge detection using Canny
# Edge detection using Canny
edges = cv.Canny(blurred, 50, 150)
# cv.imshow('contours', edges)
# k = cv.waitKey(0)
# return None
# Step 4: Morphological operations to connect edges
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
dilated = cv.dilate(edges, kernel, iterations=2)
closed = cv.morphologyEx(dilated, cv.MORPH_CLOSE, kernel)
# Morphological operations to connect broken edges
kernel_close = cv.getStructuringElement(cv.MORPH_RECT, (5, 5))
closed = cv.morphologyEx(edges, cv.MORPH_CLOSE, kernel_close)
kernel_open = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
opened = cv.morphologyEx(closed, cv.MORPH_OPEN, kernel_open)
kernel_dilate = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
dilated = cv.dilate(edges, kernel_dilate, iterations=2)
cv.imshow('contours', opened)
k = cv.waitKey(0)
return None
# Step 5: Find contours
# Find contours
contours, _ = cv.findContours(closed, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
if not contours:
logging.error("No contours found")
return None
# List all contours
logging.debug(f'Total {len(contours)} contours.')
for i, contour in enumerate(contours):
logging.debug(f'Contour[{i}] has {contour.shape[0]} points.')
cv.drawContours(img, contours, -1, (0, 0, 255), 3)
cv.imshow('contours', img)
k = cv.waitKey(0)
return None
# Filter contours
candidates: list[cv.typing.MatLike] = []
MIN_AREA: float = 2000
MAX_AREA: float = 100000
MIN_ASPECT_RATIO: float = 2.5
MAX_ASPECT_RATIO: float = 6.0
for i, contour in enumerate(contours):
# Calculate the area
area = cv.contourArea(contour)
if area < MIN_AREA or area > MAX_AREA:
logging.debug(f'Contour[{i}] failed at area limit. The area of this contour is {area}.')
continue
# Get bounding rectangle
bouding_rect = cv.boundingRect(contour)
(x, y, w, h) = bouding_rect
# Calaulate aspect ratio
aspect_ratio = w / h
if aspect_ratio < MIN_ASPECT_RATIO or aspect_ratio > MAX_ASPECT_RATIO:
logging.debug(f'Contour[{i}] failed at aspect ratio limit. The aspect ratio of this contour is {aspect_ratio}.')
continue
# Get the convex hull of contour
hull = cv.convexHull(contour)
# Compute the occupation of contour area in convex hull area
hull_area = cv.contourArea(hull)
solidity = area / hull_area
# Filter more regular contour
if solidity > 0.6:
# Extra check for the rectangle fill rate
fill_ratio = area / (w * h)
if fill_ratio > 0.3:
logging.debug(f'Contour[{i}] is perfect.')
candidates.append(contour)
continue
else:
logging.debug(f'Contour[{i}] failed at rectangle fill ratio limit. The fill ratio of this contour is {fill_ratio}')
else:
logging.debug(f'Contour[{i}] failed at solidity limit. The solidity of this contour is {solidity}.')
if len(candidates) == 0:
logging.error("No candidate contour")
return None
cv.drawContours(img, contours, -1, (0, 0, 255), 3)
cv.imshow('contours', img)
k = cv.waitKey(0)
return None
# Step 6: Find the most likely license plate contour
# License plates are typically rectangular with specific aspect ratios
max_area = 0
@@ -134,7 +246,6 @@ def extract_car_plate(img: cv.typing.MatLike) -> typing.Optional[cv.typing.MatLi
return result
@dataclass
class Cli:
input_file: Path
@@ -177,7 +288,7 @@ class Cli:
def main():
# Setup logging format
logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.INFO)
logging.basicConfig(format="[%(levelname)s] %(message)s", level=logging.DEBUG)
# Get user request
cli = Cli.from_cmdline()