1 Star 0 Fork 0

xiyan-100/yolov8-plate

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
detect_rec_plate.py 10.51 KB
一键复制 编辑 原始数据 按行查看 历史
记忆之城 提交于 2024-02-28 13:20 . yolov8
import torch
import cv2
import numpy as np
import argparse
import copy
import time
import os
from ultralytics.nn.tasks import attempt_load_weights
from plate_recognition.plate_rec import get_plate_result,init_model,cv_imread
from plate_recognition.double_plate_split_merge import get_split_merge
from fonts.cv_puttext import cv2ImgAddText
def allFilePath(rootPath,allFIleList):# 读取文件夹内的文件,放到list
fileList = os.listdir(rootPath)
for temp in fileList:
if os.path.isfile(os.path.join(rootPath,temp)):
allFIleList.append(os.path.join(rootPath,temp))
else:
allFilePath(os.path.join(rootPath,temp),allFIleList)
def four_point_transform(image, pts): #透视变换得到车牌小图
# rect = order_points(pts)
rect = pts.astype('float32')
(tl, tr, br, bl) = rect
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
dst = np.array([
[0, 0],
[maxWidth - 1, 0],
[maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype = "float32")
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
def letter_box(img,size=(640,640)): #yolo 前处理 letter_box操作
h,w,_=img.shape
r=min(size[0]/h,size[1]/w)
new_h,new_w=int(h*r),int(w*r)
new_img = cv2.resize(img,(new_w,new_h))
left= int((size[1]-new_w)/2)
top=int((size[0]-new_h)/2)
right = size[1]-left-new_w
bottom=size[0]-top-new_h
img =cv2.copyMakeBorder(new_img,top,bottom,left,right,cv2.BORDER_CONSTANT,value=(114,114,114))
return img,r,left,top
def load_model(weights, device): #加载yolov8 模型
model = attempt_load_weights(weights,device=device) # load FP32 model
return model
def xywh2xyxy(det): #xywh转化为xyxy
y = det.clone()
y[:,0]=det[:,0]-det[0:,2]/2
y[:,1]=det[:,1]-det[0:,3]/2
y[:,2]=det[:,0]+det[0:,2]/2
y[:,3]=det[:,1]+det[0:,3]/2
return y
def my_nums(dets,iou_thresh): #nms操作
y = dets.clone()
y_box_score = y[:,:5]
index = torch.argsort(y_box_score[:,-1],descending=True)
keep = []
while index.size()[0]>0:
i = index[0].item()
keep.append(i)
x1=torch.maximum(y_box_score[i,0],y_box_score[index[1:],0])
y1=torch.maximum(y_box_score[i,1],y_box_score[index[1:],1])
x2=torch.minimum(y_box_score[i,2],y_box_score[index[1:],2])
y2=torch.minimum(y_box_score[i,3],y_box_score[index[1:],3])
zero_=torch.tensor(0).to(device)
w=torch.maximum(zero_,x2-x1)
h=torch.maximum(zero_,y2-y1)
inter_area = w*h
nuion_area1 =(y_box_score[i,2]-y_box_score[i,0])*(y_box_score[i,3]-y_box_score[i,1]) #计算交集
union_area2 =(y_box_score[index[1:],2]-y_box_score[index[1:],0])*(y_box_score[index[1:],3]-y_box_score[index[1:],1])#计算并集
iou = inter_area/(nuion_area1+union_area2-inter_area)#计算iou
idx = torch.where(iou<=iou_thresh)[0] #保留iou小于iou_thresh的
index=index[idx+1]
return keep
def restore_box(dets,r,left,top): #坐标还原到原图上
dets[:,[0,2]]=dets[:,[0,2]]-left
dets[:,[1,3]]= dets[:,[1,3]]-top
dets[:,:4]/=r
# dets[:,5:13]/=r
return dets
# pass
def post_processing(prediction,conf,iou_thresh,r,left,top): #后处理
prediction = prediction.permute(0,2,1).squeeze(0)
xc = prediction[:, 4:6].amax(1) > conf #过滤掉小于conf的框
x = prediction[xc]
if not len(x):
return []
boxes = x[:,:4] #框
boxes = xywh2xyxy(boxes) #中心点 宽高 变为 左上 右下两个点
score,index = torch.max(x[:,4:6],dim=-1,keepdim=True) #找出得分和所属类别
x = torch.cat((boxes,score,x[:,6:14],index),dim=1) #重新组合
score = x[:,4]
keep =my_nums(x,iou_thresh)
x=x[keep]
x=restore_box(x,r,left,top)
return x
def pre_processing(img,opt,device): #前处理
img, r,left,top= letter_box(img,(opt.img_size,opt.img_size))
# print(img.shape)
img=img[:,:,::-1].transpose((2,0,1)).copy() #bgr2rgb hwc2chw
img = torch.from_numpy(img).to(device)
img = img.float()
img = img/255.0
img =img.unsqueeze(0)
return img ,r,left,top
def det_rec_plate(img,img_ori,detect_model,plate_rec_model):
result_list=[]
img,r,left,top = pre_processing(img,opt,device) #前处理
predict = detect_model(img)[0]
outputs=post_processing(predict,0.3,0.5,r,left,top) #后处理
for output in outputs:
result_dict={}
output = output.squeeze().cpu().numpy().tolist()
rect=output[:4]
rect = [int(x) for x in rect]
label = output[-1]
roi_img = img_ori[rect[1]:rect[3],rect[0]:rect[2]]
# land_marks=np.array(output[5:13],dtype='int64').reshape(4,2)
# roi_img = four_point_transform(img_ori,land_marks) #透视变换得到车牌小图
if int(label): #判断是否是双层车牌,是双牌的话进行分割后然后拼接
roi_img=get_split_merge(roi_img)
plate_number,rec_prob,plate_color,color_conf=get_plate_result(roi_img,device,plate_rec_model,is_color=True)
result_dict['plate_no']=plate_number #车牌号
result_dict['plate_color']=plate_color #车牌颜色
result_dict['rect']=rect #车牌roi区域
result_dict['detect_conf']=output[4] #检测区域得分
# result_dict['landmarks']=land_marks.tolist() #车牌角点坐标
# result_dict['rec_conf']=rec_prob #每个字符的概率
result_dict['roi_height']=roi_img.shape[0] #车牌高度
# result_dict['plate_color']=plate_color
# if is_color:
result_dict['color_conf']=color_conf #颜色得分
result_dict['plate_type']=int(label) #单双层 0单层 1双层
result_list.append(result_dict)
return result_list
def draw_result(orgimg,dict_list,is_color=False): # 车牌结果画出来
result_str =""
for result in dict_list:
rect_area = result['rect']
x,y,w,h = rect_area[0],rect_area[1],rect_area[2]-rect_area[0],rect_area[3]-rect_area[1]
padding_w = 0.05*w
padding_h = 0.11*h
rect_area[0]=max(0,int(x-padding_w))
rect_area[1]=max(0,int(y-padding_h))
rect_area[2]=min(orgimg.shape[1],int(rect_area[2]+padding_w))
rect_area[3]=min(orgimg.shape[0],int(rect_area[3]+padding_h))
height_area = result['roi_height']
# landmarks=result['landmarks']
result_p = result['plate_no']
if result['plate_type']==0:#单层
result_p+=" "+result['plate_color']
else: #双层
result_p+=" "+result['plate_color']+"双层"
result_str+=result_p+" "
# for i in range(4): #关键点
# cv2.circle(orgimg, (int(landmarks[i][0]), int(landmarks[i][1])), 5, clors[i], -1)
cv2.rectangle(orgimg,(rect_area[0],rect_area[1]),(rect_area[2],rect_area[3]),(0,0,255),2) #画框
labelSize = cv2.getTextSize(result_p,cv2.FONT_HERSHEY_SIMPLEX,0.5,1) #获得字体的大小
if rect_area[0]+labelSize[0][0]>orgimg.shape[1]: #防止显示的文字越界
rect_area[0]=int(orgimg.shape[1]-labelSize[0][0])
orgimg=cv2.rectangle(orgimg,(rect_area[0],int(rect_area[1]-round(1.6*labelSize[0][1]))),(int(rect_area[0]+round(1.2*labelSize[0][0])),rect_area[1]+labelSize[1]),(255,255,255),cv2.FILLED)#画文字框,背景白色
if len(result)>=6:
orgimg=cv2ImgAddText(orgimg,result_p,rect_area[0],int(rect_area[1]-round(1.6*labelSize[0][1])),(0,0,0),21)
# orgimg=cv2ImgAddText(orgimg,result_p,rect_area[0]-height_area,rect_area[1]-height_area-10,(0,255,0),height_area)
print(result_str)
return orgimg
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--detect_model', nargs='+', type=str, default=r'weights/yolov8s.pt', help='model.pt path(s)') #yolov8检测模型
parser.add_argument('--rec_model', type=str, default=r'weights/plate_rec_color.pth', help='model.pt path(s)')#车牌字符识别模型
parser.add_argument('--image_path', type=str, default=r'imgs', help='source') #待识别图片路径
parser.add_argument('--img_size', type=int, default=640, help='inference size (pixels)') #yolov8 网络模型输入大小
parser.add_argument('--output', type=str, default='result', help='source') #结果保存的文件夹
device =torch.device("cuda" if torch.cuda.is_available() else "cpu")
clors = [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(0,255,255)]
opt = parser.parse_args()
save_path = opt.output
if not os.path.exists(save_path):
os.mkdir(save_path)
detect_model = load_model(opt.detect_model, device) #初始化yolov8识别模型
plate_rec_model=init_model(device,opt.rec_model,is_color=True) #初始化识别模型
#算参数量
total = sum(p.numel() for p in detect_model.parameters())
total_1 = sum(p.numel() for p in plate_rec_model.parameters())
print("yolov8 detect params: %.2fM,rec params: %.2fM" % (total/1e6,total_1/1e6))
detect_model.eval()
# print(detect_model)
file_list = []
allFilePath(opt.image_path,file_list)
count=0
time_all = 0
time_begin=time.time()
for pic_ in file_list:
print(count,pic_,end=" ")
time_b = time.time() #开始时间
img = cv2.imread(pic_)
img_ori = copy.deepcopy(img)
result_list=det_rec_plate(img,img_ori,detect_model,plate_rec_model)
time_e=time.time()
ori_img=draw_result(img,result_list) #将结果画在图上
img_name = os.path.basename(pic_)
save_img_path = os.path.join(save_path,img_name) #图片保存的路径
time_gap = time_e-time_b #计算单个图片识别耗时
if count:
time_all+=time_gap
count+=1
cv2.imwrite(save_img_path,ori_img) #op
# print(result_list)
print(f"sumTime time is {time.time()-time_begin} s, average pic time is {time_all/(len(file_list)-1)}")
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Python
1
https://gitee.com/xiyan100/yolov8-plate.git
git@gitee.com:xiyan100/yolov8-plate.git
xiyan100
yolov8-plate
yolov8-plate
master

搜索帮助

23e8dbc6 1850385 7e0993f3 1850385