You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
86 lines
3.7 KiB
86 lines
3.7 KiB
5 months ago
|
import cv2
|
||
|
import numpy as np
|
||
|
|
||
|
'''
|
||
|
基于Opencv图像处理的车牌定位和分割
|
||
|
'''
|
||
|
def car_plate_recognize(car):
|
||
|
"""=========================== 1. 定位车牌(车牌检测)==========================="""
|
||
|
# 蓝色、黄色和绿色所对应的色彩空间
|
||
|
lower_blue = np.array([100, 110, 110])
|
||
|
upper_blue = np.array([130, 255, 255])
|
||
|
lower_yellow = np.array([15, 55, 55])
|
||
|
upper_yellow = np.array([50, 255, 255])
|
||
|
lower_green = np.array([35, 100, 100])
|
||
|
upper_green = np.array([85, 255, 255])
|
||
|
|
||
|
hsv = cv2.cvtColor(car, cv2.COLOR_BGR2HSV) # 将BGR图像转化到HSV的颜色空间
|
||
|
mask_blue = cv2.inRange(hsv, lower_blue, upper_blue)
|
||
|
mask_yellow = cv2.inRange(hsv, lower_yellow, upper_yellow)
|
||
|
mask_green = cv2.inRange(hsv, lower_green, upper_green)
|
||
|
mask_plate = cv2.bitwise_or(mask_blue, mask_yellow)
|
||
|
mask_plate = cv2.bitwise_or(mask_plate, mask_green)
|
||
|
|
||
|
# 根据阈值找到对应颜色
|
||
|
mask = cv2.cvtColor(mask_plate, cv2.COLOR_GRAY2BGR)
|
||
|
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
|
||
|
Matrix = np.ones((20, 20), np.uint8)
|
||
|
mask1 = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, Matrix)
|
||
|
mask = cv2.morphologyEx(mask1, cv2.MORPH_OPEN, Matrix) # 形态学开运算
|
||
|
ret, mask = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY) # 二值化进而获取轮廓
|
||
|
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 获取轮廓 contours
|
||
|
|
||
|
# 初始化 box
|
||
|
box = None
|
||
|
|
||
|
# 寻找轮廓最大的 定位车牌
|
||
|
for i in range(len(contours)):
|
||
|
cnt = contours[i]
|
||
|
area = cv2.contourArea(cnt)
|
||
|
if area > 3000:
|
||
|
rect = cv2.minAreaRect(cnt)
|
||
|
box = cv2.boxPoints(rect)
|
||
|
box = np.int_(box)
|
||
|
break # 找到一个符合条件的就跳出循环
|
||
|
|
||
|
if box is None:
|
||
|
raise ValueError("No contours found that meet the size requirement.")
|
||
|
|
||
|
plate = cv2.drawContours(car.copy(), [box], -1, (0, 255, 0), 3)
|
||
|
|
||
|
"""=========================== 2. 分割车牌中的每个字符 ==========================="""
|
||
|
ys = [box[0, 1], box[1, 1], box[2, 1], box[3, 1]]
|
||
|
xs = [box[0, 0], box[1, 0], box[2, 0], box[3, 0]]
|
||
|
ys_sorted_index = np.argsort(ys)
|
||
|
xs_sorted_index = np.argsort(xs)
|
||
|
x1 = box[xs_sorted_index[0], 0]
|
||
|
x2 = box[xs_sorted_index[3], 0]
|
||
|
y1 = box[ys_sorted_index[0], 1]
|
||
|
y2 = box[ys_sorted_index[3], 1]
|
||
|
ROI_plate = plate[y1:y2, x1:x2]
|
||
|
ROI_plate_gray = cv2.cvtColor(ROI_plate, cv2.COLOR_BGR2GRAY) # 灰度化
|
||
|
ROI_plate_blur = cv2.GaussianBlur(ROI_plate_gray, (5, 5), 0) # 高斯滤波
|
||
|
ret, ROI_plate_Binary = cv2.threshold(ROI_plate_blur, 127, 255, cv2.THRESH_BINARY) # 二值化
|
||
|
# 形态学腐蚀 去除边框
|
||
|
kernel = np.ones((5, 5), dtype=np.uint8)
|
||
|
ROI_erode = cv2.erode(ROI_plate_Binary, kernel, iterations=1)
|
||
|
# 根据宽度 裁剪7个字符
|
||
|
width = ROI_erode.shape[1]
|
||
|
height = ROI_erode.shape[0]
|
||
|
word_0 = ROI_erode[0:height, 0:np.uint8(height / 2)]
|
||
|
word_1 = ROI_erode[0:height, np.uint8(height / 2):height]
|
||
|
size = np.uint8((width - height) / 5)
|
||
|
word_2 = ROI_erode[0:height, height + 0 * size:height + 1 * size]
|
||
|
word_3 = ROI_erode[0:height, height + 1 * size:height + 2 * size]
|
||
|
word_4 = ROI_erode[0:height, height + 2 * size:height + 3 * size]
|
||
|
word_5 = ROI_erode[0:height, height + 3 * size:height + 4 * size]
|
||
|
word_6 = ROI_erode[0:height, height + 4 * size:height + 5 * size]
|
||
|
word_all = [word_0, word_1, word_2, word_3, word_4, word_5, word_6]
|
||
|
return plate, word_all
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
car = cv2.imread(r'/mnt/data/cropped_license_plate_0.jpg', 1)
|
||
|
plate, _ = car_plate_recognize(car)
|
||
|
cv2.imwrite("plate.jpg", plate)
|