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.

332 lines
11 KiB

import numpy as np
import cv2
import matplotlib.pyplot as plt
from matplotlib import font_manager
import ddddocr
import threadsafe_tkinter as tk
from tkinter import filedialog
font_path = "C:/Users/Lenovo/Downloads/OPPOSans3.0/OPPOSans-Regular.ttf"
# 动态添加字体到 matplotlib 的字体列表
font = font_manager.FontProperties(fname=font_path)
class Get_license():
#图像拉伸函数
def stretch(self, img):
maxi = float(img.max())
mini = float(img.min())
for i in range(img.shape[0]):
for j in range(img.shape[1]):
img[i, j] = (255 / (maxi - mini) * img[i, j] - (255 * mini) / (maxi - mini))
return img
#二值化处理函数
def dobinaryzation(self, img):
# 使用OpenCV的自动阈值方法
ret, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return thresh
#寻找矩形的轮廓
def find_rectangle(self, contour):
y, x = [],[]
for p in contour:
y.append(p[0][0])
x.append(p[0][1])
return [min(y), min(x), max(y), max(x)]
#定位车牌号
def locate_license(self, img, afterimg):
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
#找出最大的三个区域
block = []
for c in contours:
#找出轮廓的左上点和右下点
#由此计算它的面积和长度比
r = self.find_rectangle(c)
a = (r[2] - r[0]) * (r[3] - r[1]) #面积
s = (r[2] - r[0]) * (r[3] - r[1]) #长度比
block.append([r, a, s])
#选出面积最大的3个区域
block = sorted(block, key=lambda b: b[1])[-3:]
#使用颜色识别判断找出最像车牌的区域
maxweight, maxindex = 0, -1
for i in range(len(block)):
b = afterimg[block[i][0][1]:block[i][0][3], block[i][0][0]:block[i][0][2]]
hsv = cv2.cvtColor(b, cv2.COLOR_BGR2HSV)
lower = np.array([100, 50, 50])
upper = np.array([140, 255, 255])
mask = cv2.inRange(hsv, lower, upper)
# 计算mask中白色像素即HSV范围内的像素的数量
weight = np.sum(mask == 255) # 或者简单地使用 np.count_nonzero(mask)
# 选出最大权值的区域
if weight > maxweight:
maxindex = i
maxweight = weight
return block[maxindex][0]
#预处理函数
def find_license(self, img):
m = 400 * img.shape[0] / img.shape[1]
#压缩图像
img = cv2.resize(img, (400, int(m)), interpolation=cv2.INTER_CUBIC)
#BGR转换为灰度图像
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#灰度拉伸
stretchedimg = self.stretch(gray_img)
'''进行开运算,用来去除噪声'''
r = 16
h = w = r * 2 + 1
kernel = np.zeros((h, w), np.uint8)
cv2.circle(kernel, (r, r), r, 1, -1)
openingimg = cv2.morphologyEx(stretchedimg, cv2.MORPH_OPEN, kernel)
strtimg = cv2.absdiff(stretchedimg, openingimg)
#图像二值化
binaryimg = self.dobinaryzation(strtimg)
#canny边缘检测
canny = cv2.Canny(binaryimg, binaryimg.shape[0], binaryimg.shape[1])
'''消除小的区域,保留大块的区域,从而定位车牌'''
#进行闭运算
kernel = np.ones((5, 17), np.uint8)
closingimg = cv2.morphologyEx(canny, cv2.MORPH_CLOSE, kernel)
#进行开运算
openingimg = cv2.morphologyEx(closingimg, cv2.MORPH_OPEN, kernel)
#消除小区域,定位车牌位置
rect = self.locate_license(openingimg, img)
return rect, img
#图像分割函数
def cut_license(self, afterimg, rect):
#转换为宽度和高度
rect[2] = rect[2] - rect[0]
rect[3] = rect[3] - rect[1]
rect_copy = tuple(rect.copy())
#创建掩膜
mask = np.zeros(afterimg.shape[:2], np.uint8)
#创建背景模型 大小只能为13*5行数只能为1单通道浮点型
bgdModel = np.zeros((1, 65), np.float64)
#创建前景模型
fgdModel = np.zeros((1, 65), np.float64)
#分割图像
cv2.grabCut(afterimg, mask, rect_copy, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
img_show = afterimg * mask2[:, :, np.newaxis]
return img_show
class Segmentation():
def __init__(self, cutimg):
#1、读取图像并把图像转换为灰度图像并显示
img_gray = cv2.cvtColor(cutimg, cv2.COLOR_BGR2GRAY) #转换了灰度化
#2、将灰度图像二值化设定阈值是150
self.img_thre = img_gray
cv2.threshold(img_gray, 150
, 255, cv2.THRESH_BINARY_INV, self.img_thre)
#3、保存黑白图片
cv2.imwrite('thre_res.jpg', self.img_thre)
#4、分割字符
self.white = [] #记录每一列的白色像素总和
self.black = [] #黑色
self.height = self.img_thre.shape[0]
self.width = self.img_thre.shape[1]
self.white_max = 0
self.black_max = 0
#计算每一列的黑白色像素总和
for i in range(self.width):
white_count = 0 #这一列白色总数
black_count = 0 #这一列黑色总数
for j in range(self.height):
if self.img_thre[j][i] == 255:
white_count += 1
if self.img_thre[j][i] == 0:
black_count += 1
self.white_max = max(self.white_max, white_count)
self.black_max = max(self.black_max, black_count)
self.white.append(white_count)
self.black.append(black_count)
self.arg = False #False表示白底黑字True表示黑底白字
if self.black_max > self.white_max:
self.arg = True
def heibai(self):
return self.img_thre
def find_end(self, start_):
end_ = start_ + 1
for m in range(start_ + 1, self.width - 1):
if (self.black[m] if self.arg else self.white[m]) > (
0.85 * self.black_max if self.arg else 0.85 * self.white_max):
end_ = m
break
return end_
def display(self):
#img_list = []
n = 1
plt.figure()
img_num = 0
while n < self.width - 2:
n += 1
if (self.white[n] if self.arg else self.black[n]) > (
0.15 * self.white_max if self.arg else 0.15 * self.black_max):
#上面这些判断用来辨别是白底黑字还是黑底白字
start = n
end = self.find_end(start)
n = end
if end - start > 5:
cj = self.img_thre[1:self.height, start:end]
img_num += 1
cj = cv2.cvtColor(cj, cv2.COLOR_RGB2BGR)
plt.figure(2)
plt.subplot(2, 4, img_num)
plt.title('{}'.format(img_num))
plt.imshow(cj)
plt.show()
return self.img_thre
if __name__ == '__main__':
def select_image():
# 弹出文件选择对话框,限制文件类型为图片
file_path = filedialog.askopenfilename()
if file_path:
# 将文件路径显示在标签上
img = cv2.imread(file_path)
img1 = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
# 绘图
plt.figure(1)
plt.suptitle('车牌识别', fontproperties=font)
plt.subplot(2, 3, 1)
plt.title('原始图像', fontproperties=font)
plt.imshow(img1)
# 预处理图像
license = Get_license()
rect, afterimg = license.find_license(img)
afterimg = cv2.cvtColor(afterimg, cv2.COLOR_RGB2BGR)
plt.subplot(2, 3, 2)
plt.title('预处理后图像', fontproperties=font)
plt.imshow(afterimg)
# 车牌号打框
cv2.rectangle(afterimg, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 0), 1)
x1, y1, x2, y2 = int(rect[0]), int(rect[1]), int(rect[2]), int(rect[3])
plt.subplot(2, 3, 3)
plt.title('车牌框出', fontproperties=font)
plt.imshow(afterimg)
# 背景去除
cutimg = license.cut_license(afterimg, rect)
plt.subplot(2, 3, 4)
plt.title('车牌背景去除', fontproperties=font)
plt.imshow(cutimg)
# print(int(_rect[0]), int(_rect[3]), int(_rect[2]), int(_rect[1]))
# 开始分割车牌
# cutimg = cutimg[140:165, 151:240]
cutimg = cutimg[y1 + 3:y2 - 3, x1 - 1:x2 - 3]
# cutimg = cutimg[int(_rect[0]):int(_rect[3]),int(_rect[2]):int(_rect[1])]
height, width = cutimg.shape[:2]
cutimg1 = cv2.resize(cutimg, (2 * width, 2 * height), interpolation=cv2.INTER_CUBIC)
plt.subplot(2, 3, 5)
plt.title('分割车牌与背景', fontproperties=font)
plt.imshow(cutimg)
# 字符切割
seg = Segmentation(cutimg)
plt.subplot(2, 3, 6)
img_hei = seg.heibai()
img_hei = cv2.cvtColor(img_hei, cv2.COLOR_RGB2BGR)
plt.title('车牌二值化处理', fontproperties=font)
plt.imshow(img_hei)
seg.display()
plt.show()
# 打印车牌
ocr = ddddocr.DdddOcr()
with open('thre_res.jpg', 'rb') as f:
image = f.read()
res = ocr.classification(image)
print(res)
# 创建根窗口
root = tk.Tk()
root.title('车牌识别程序')
root.geometry('600x450')
# 创建一个标签用于显示图片路径
label = tk.Label(root, text='未选择图片')
label.pack(pady=20) # 使用pack布局管理器并添加一些垂直填充
def exit_app():
# 退出应用程序的函数
root.destroy()
# 创建一个按钮点击时会调用select_image函数
button = tk.Button(root, text='选择图片', command=select_image)
button.pack()
button_exit = tk.Button(root, text='退出', command=exit_app)
button_exit.pack(pady=30)
# 启动事件循环
root.mainloop()