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

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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()