【香橙派 Orange pi AIpro】| 搭建部署基于Yolov5的车牌识别系统
最近刚好想研究一下嵌入式方面的知识就看到了香橙派新出的板子 Orange pi AIpro,果断搞来研究一下
一、香橙派 Orange pi AIpro 开发板介绍及实物开箱
1.1 开发板介绍
昇腾AI技术路线:高性能,低功耗 4核64位处理器 + AI处理器,速度更快,功耗更低,可为各类AI应用场景带来卓越的性能表现。
多通道输出,支持多屏异显:支持双HDMI视频输出,支持双4K高清输出,支持一个MIPI DSI屏输出,支持两个MIPI接口摄像头输入。
丰富接口,易于扩展:汇聚了MIPI DSI、MIPI CSI、USB 3.0、Type-C 3.0、HDMI 2.0、千兆以太网、支持SATA/NVMe SSD 2280的M.2插槽等各类流行的接口,可应用于外部设备控制和扩展。
应用场景广泛 覆盖 AIoT各行各业:可广泛适用于AI教学实训、AI算法验证、智能小车、机械臂、边缘计算、无人机、人工智能、云计算、AR/VR、智能安防、智能家居、智能交通等领域。
可运行openEuler系统:高效、稳定、安全。可运行openEuler系统,支持多种硬件架构和虚拟化技术,可广泛应用于企业级云计算、边缘计算场景等。
1.2 产品详情图
正面
背面
1.3 开箱实物
物品清单
正面图
背面图
在提交申请后,也是很快就收到了快递包裹,开箱后,有一个快充头和数据线,板子同标准的盒子装着,同时用两块薄海绵包裹着,可以预防静电对板子造成损害
实物颜值非常高,且已经自带了一块32G的TF卡,里面也已经烧录了openeuler镜像系统
二、开发部署预先准备
2.1 镜像介绍与烧录
开发板默认支持 ubuntu系统和 openeuler系统,由于 TF卡中已经预先烧录好了 openeuler系统,所以下面我就直接使用该系统进行搭建和部署了,熟悉和习惯 ubuntu系统的用户也可以格式化,重新烧录一个 ubuntu系统。
官方也提供了烧录工具
2.2 启动开发板
连接电源后,开发板会自动启动
2.3 连接开发板
由于手边没有 HDMI 线,我就直接用远程连接的方式连接吧
-
拿到开发板的 ip
-
使用 MobaXterm 远程连接
三、基于Yolov5的车牌识别系统
3.1 项目介绍
该项目是一个基于深度学习与yolov5 的车牌检测 、车牌识别、中文车牌识别与检测、支持12种中文车牌、支持双层车牌的目标识别系统。
项目开源地址:Chinese_license_plate_detection_recognition
3.2 拉取项目
- 先创建一个文件夹用来存放我们的项目
mkdir yolov5
- 开发板系统中已经预先安装好了 git,我们直接使用 git 将项目拉取下来就可以了
git clone https://github.com/we0091234/Chinese_license_plate_detection_recognition.git
- 进入到刚刚拉取的项目中
cd Chinese_license_plate_detection_recognition
- 刚刚拉取的项目,需要众多的依赖,这里需要下载一下,使用下面的命令下载就可以了
pip install -r ./requirements.txt -i https://mirrors.aliyun.com/pypi/simple
需要下载的依赖比较多,这里可能需要多等待一下
出现绿色的 Successfully 就代表依赖下载成功了
- 到此为止,项目就算拉取成功了,我们看一下,项目的目录文件结构
可以看到文件还是很多的
3.3 部分核心代码
# -*- coding: UTF-8 -*-
import argparse
import time
from pathlib import Path
import os
import cv2
import torch
import torch.backends.cudnn as cudnn
from numpy import random
import copy
import numpy as np
from models.experimental import attempt_load
from utils.datasets import letterbox
from utils.general import check_img_size, non_max_suppression_face, apply_classifier, scale_coords, xyxy2xywh, \
strip_optimizer, set_logging, increment_path
from utils.plots import plot_one_box
from utils.torch_utils import select_device, load_classifier, time_synchronized
from utils.cv_puttext import cv2ImgAddText
from plate_recognition.plate_rec import get_plate_result,allFilePath,init_model,cv_imread
# from plate_recognition.plate_cls import cv_imread
from plate_recognition.double_plate_split_merge import get_split_merge
clors = [(255,0,0),(0,255,0),(0,0,255),(255,255,0),(0,255,255)]
danger=['危','险']
def order_points(pts): #四个点按照左上 右上 右下 左下排列
rect = np.zeros((4, 2), dtype = "float32")
s = pts.sum(axis = 1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
diff = np.diff(pts, axis = 1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
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 load_model(weights, device): #加载检测模型
model = attempt_load(weights, map_location=device) # load FP32 model
return model
def scale_coords_landmarks(img1_shape, coords, img0_shape, ratio_pad=None): #返回到原图坐标
# Rescale coords (xyxy) from img1_shape to img0_shape
if ratio_pad is None: # calculate from img0_shape
gain = min(img1_shape[0] / img0_shape[0], img1_shape[1] / img0_shape[1]) # gain = old / new
pad = (img1_shape[1] - img0_shape[1] * gain) / 2, (img1_shape[0] - img0_shape[0] * gain) / 2 # wh padding
else:
gain = ratio_pad[0][0]
pad = ratio_pad[1]
coords[:, [0, 2, 4, 6]] -= pad[0] # x padding
coords[:, [1, 3, 5, 7]] -= pad[1] # y padding
coords[:, :8] /= gain
#clip_coords(coords, img0_shape)
coords[:, 0].clamp_(0, img0_shape[1]) # x1
coords[:, 1].clamp_(0, img0_shape[0]) # y1
coords[:, 2].clamp_(0, img0_shape[1]) # x2
coords[:, 3].clamp_(0, img0_shape[0]) # y2
coords[:, 4].clamp_(0, img0_shape[1]) # x3
coords[:, 5].clamp_(0, img0_shape[0]) # y3
coords[:, 6].clamp_(0, img0_shape[1]) # x4
coords[:, 7].clamp_(0, img0_shape[0]) # y4
# coords[:, 8].clamp_(0, img0_shape[1]) # x5
# coords[:, 9].clamp_(0, img0_shape[0]) # y5
return coords
def get_plate_rec_landmark(img, xyxy, conf, landmarks, class_num,device,plate_rec_model,is_color=False): #获取车牌坐标以及四个角点坐标并获取车牌号
h,w,c = img.shape
result_dict={}
tl = 1 or round(0.002 * (h + w) / 2) + 1 # line/font thickness
x1 = int(xyxy[0])
y1 = int(xyxy[1])
x2 = int(xyxy[2])
y2 = int(xyxy[3])
height=y2-y1
landmarks_np=np.zeros((4,2))
rect=[x1,y1,x2,y2]
for i in range(4):
point_x = int(landmarks[2 * i])
point_y = int(landmarks[2 * i + 1])
landmarks_np[i]=np.array([point_x,point_y])
class_label= int(class_num) #车牌的的类型0代表单牌,1代表双层车牌
roi_img = four_point_transform(img,landmarks_np) #透视变换得到车牌小图
if class_label: #判断是否是双层车牌,是双牌的话进行分割后然后拼接
roi_img=get_split_merge(roi_img)
if not is_color:
plate_number,rec_prob = get_plate_result(roi_img,device,plate_rec_model,is_color=is_color) #对车牌小图进行识别
else:
plate_number,rec_prob,plate_color,color_conf=get_plate_result(roi_img,device,plate_rec_model,is_color=is_color)
# cv2.imwrite("roi.jpg",roi_img)
result_dict['rect']=rect #车牌roi区域
result_dict['detect_conf']=conf #检测区域得分
result_dict['landmarks']=landmarks_np.tolist() #车牌角点坐标
result_dict['plate_no']=plate_number #车牌号
result_dict['rec_conf']=rec_prob #每个字符的概率
result_dict['roi_height']=roi_img.shape[0] #车牌高度
result_dict['plate_color']=""
if is_color:
result_dict['plate_color']=plate_color #车牌颜色
result_dict['color_conf']=color_conf #颜色得分
result_dict['plate_type']=class_label #单双层 0单层 1双层
return result_dict
def detect_Recognition_plate(model, orgimg, device,plate_rec_model,img_size,is_color=False):#获取车牌信息
# Load model
# img_size = opt_img_size
conf_thres = 0.3 #得分阈值
iou_thres = 0.5 #nms的iou值
dict_list=[]
# orgimg = cv2.imread(image_path) # BGR
img0 = copy.deepcopy(orgimg)
assert orgimg is not None, 'Image Not Found '
h0, w0 = orgimg.shape[:2] # orig hw
r = img_size / max(h0, w0) # resize image to img_size
if r != 1: # always resize down, only resize up if training with augmentation
interp = cv2.INTER_AREA if r < 1 else cv2.INTER_LINEAR
img0 = cv2.resize(img0, (int(w0 * r), int(h0 * r)), interpolation=interp)
imgsz = check_img_size(img_size, s=model.stride.max()) # check img_size
img = letterbox(img0, new_shape=imgsz)[0] #检测前处理,图片长宽变为32倍数,比如变为640X640
# img =process_data(img0)
# Convert
img = img[:, :, ::-1].transpose(2, 0, 1).copy() # BGR to RGB, to 3x416x416 图片的BGR排列转为RGB,然后将图片的H,W,C排列变为C,H,W排列
# Run inference
t0 = time.time()
img = torch.from_numpy(img).to(device)
img = img.float() # uint8 to fp16/32
img /= 255.0 # 0 - 255 to 0.0 - 1.0
if img.ndimension() == 3:
img = img.unsqueeze(0)
# Inference
# t1 = time_synchronized()/
pred = model(img)[0]
# t2=time_synchronized()
# print(f"infer time is {(t2-t1)*1000} ms")
# Apply NMS
pred = non_max_suppression_face(pred, conf_thres, iou_thres)
# print('img.shape: ', img.shape)
# print('orgimg.shape: ', orgimg.shape)
# Process detections
for i, det in enumerate(pred): # detections per image
if len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = scale_coords(img.shape[2:], det[:, :4], orgimg.shape).round()
# Print results
for c in det[:, -1].unique():
n = (det[:, -1] == c).sum() # detections per class
det[:, 5:13] = scale_coords_landmarks(img.shape[2:], det[:, 5:13], orgimg.shape).round()
for j in range(det.size()[0]):
xyxy = det[j, :4].view(-1).tolist()
conf = det[j, 4].cpu().numpy()
landmarks = det[j, 5:13].view(-1).tolist()
class_num = det[j, 13].cpu().numpy()
result_dict = get_plate_rec_landmark(orgimg, xyxy, conf, landmarks, class_num,device,plate_rec_model,is_color=is_color)
dict_list.append(result_dict)
return dict_list
# cv2.imwrite('result.jpg', orgimg)
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)>=1:
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
def get_second(capture):
if capture.isOpened():
rate = capture.get(5) # 帧速率
FrameNumber = capture.get(7) # 视频文件的帧数
duration = FrameNumber/rate # 帧速率/视频总帧数 是时间,除以60之后单位是分钟
return int(rate),int(FrameNumber),int(duration)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--detect_model', nargs='+', type=str, default='weights/plate_detect.pt', help='model.pt path(s)') #检测模型
parser.add_argument('--rec_model', type=str, default='weights/plate_rec_color.pth', help='model.pt path(s)')#车牌识别+颜色识别模型
parser.add_argument('--is_color',type=bool,default=True,help='plate color') #是否识别颜色
parser.add_argument('--image_path', type=str, default='imgs', help='source') #图片路径
parser.add_argument('--img_size', type=int, default=640, help='inference size (pixels)') #网络输入图片大小
parser.add_argument('--output', type=str, default='result', help='source') #图片结果保存的位置
parser.add_argument('--video', type=str, default='', help='source') #视频的路径
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") #使用gpu还是cpu进行识别
# device =torch.device("cpu")
opt = parser.parse_args()
print(opt)
save_path = opt.output
count=0
if not os.path.exists(save_path):
os.mkdir(save_path)
detect_model = load_model(opt.detect_model, device) #初始化检测模型
plate_rec_model=init_model(device,opt.rec_model,is_color=opt.is_color) #初始化识别模型
#算参数量
total = sum(p.numel() for p in detect_model.parameters())
total_1 = sum(p.numel() for p in plate_rec_model.parameters())
print("detect params: %.2fM,rec params: %.2fM" % (total/1e6,total_1/1e6))
# plate_color_model =init_color_model(opt.color_model,device)
time_all = 0
time_begin=time.time()
if not opt.video: #处理图片
if not os.path.isfile(opt.image_path): #目录
file_list=[]
allFilePath(opt.image_path,file_list) #将这个目录下的所有图片文件路径读取到file_list里面
for img_path in file_list: #遍历图片文件
print(count,img_path,end=" ")
time_b = time.time() #开始时间
img =cv_imread(img_path) #opencv 读取图片
if img is None:
continue
if img.shape[-1]==4: #图片如果是4个通道的,将其转为3个通道
img=cv2.cvtColor(img,cv2.COLOR_BGRA2BGR)
# detect_one(model,img_path,device)
dict_list=detect_Recognition_plate(detect_model, img, device,plate_rec_model,opt.img_size,is_color=opt.is_color)#检测以及识别车牌
ori_img=draw_result(img,dict_list) #将结果画在图上
img_name = os.path.basename(img_path)
save_img_path = os.path.join(save_path,img_name) #图片保存的路径
time_e=time.time()
time_gap = time_e-time_b #计算单个图片识别耗时
if count:
time_all+=time_gap
cv2.imwrite(save_img_path,ori_img) #opencv将识别的图片保存
count+=1
print(f"sumTime time is {time.time()-time_begin} s, average pic time is {time_all/(len(file_list)-1)}")
else: #单个图片
print(count,opt.image_path,end=" ")
img =cv_imread(opt.image_path)
if img.shape[-1]==4:
img=cv2.cvtColor(img,cv2.COLOR_BGRA2BGR)
# detect_one(model,img_path,device)
dict_list=detect_Recognition_plate(detect_model, img, device,plate_rec_model,opt.img_size,is_color=opt.is_color)
ori_img=draw_result(img,dict_list)
img_name = os.path.basename(opt.image_path)
save_img_path = os.path.join(save_path,img_name)
cv2.imwrite(save_img_path,ori_img)
else: #处理视频
video_name = opt.video
capture=cv2.VideoCapture(video_name)
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
fps = capture.get(cv2.CAP_PROP_FPS) # 帧数
width, height = int(capture.get(cv2.CAP_PROP_FRAME_WIDTH)), int(capture.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 宽高
out = cv2.VideoWriter('result.mp4', fourcc, fps, (width, height)) # 写入视频
frame_count = 0
fps_all=0
rate,FrameNumber,duration=get_second(capture)
if capture.isOpened():
while True:
t1 = cv2.getTickCount()
frame_count+=1
print(f"第{frame_count} 帧",end=" ")
ret,img=capture.read()
if not ret:
break
# if frame_count%rate==0:
img0 = copy.deepcopy(img)
dict_list=detect_Recognition_plate(detect_model, img, device,plate_rec_model,opt.img_size,is_color=opt.is_color)
ori_img=draw_result(img,dict_list)
t2 =cv2.getTickCount()
infer_time =(t2-t1)/cv2.getTickFrequency()
fps=1.0/infer_time
fps_all+=fps
str_fps = f'fps:{fps:.4f}'
cv2.putText(ori_img,str_fps,(20,20),cv2.FONT_HERSHEY_SIMPLEX,1,(0,255,0),2)
# cv2.imshow("haha",ori_img)
# cv2.waitKey(1)
out.write(ori_img)
# current_time = int(frame_count/FrameNumber*duration)
# sec = current_time%60
# minute = current_time//60
# for result_ in result_list:
# plate_no = result_['plate_no']
# if not is_car_number(pattern_str,plate_no):
# continue
# print(f'车牌号:{plate_no},时间:{minute}分{sec}秒')
# time_str =f'{minute}分{sec}秒'
# writer.writerow({"车牌":plate_no,"时间":time_str})
# out.write(ori_img)
else:
print("失败")
capture.release()
out.release()
cv2.destroyAllWindows()
print(f"all frame is {frame_count},average fps is {fps_all/frame_count} fps")
3.4 关键点分析
- 初始化参数:
- 从输入图像img获取其高度h、宽度w和通道数c。
- 初始化一个空字典
result_dict
用于存储结果。 - 计算线条/字体粗细tl,这里基于图像尺寸动态计算。
- 解析车牌位置和角点坐标:
- 从xyxy(车牌边界框的坐标,格式为[x1, y1, x2, y2])中提取车牌的左上角和右下角坐标。
- 初始化一个
NumPy
数组landmarks_np
用于存储车牌的四个角点坐标。 - 遍历
landmarks
(车牌角点坐标列表),将每个角点坐标(x, y)存入landmarks_np
。
- 获取车牌类型:
- 从
class_num
获取车牌类型(0代表单牌,1代表双层车牌)。
- 透视变换获取车牌小图:
- 使用
four_point_transform
函数(需要预先定义或导入)对车牌区域进行透视变换,得到车牌的正面视图roi_img
。
- 处理双层车牌:
- 如果车牌类型为双层(
class_label
为1),则调用get_split_merge
函数(需要预先定义或导入)对车牌小图进行分割和合并处理。
- 车牌识别:
- 根据
is_color
参数决定是否需要识别车牌颜色。 - 调用
get_plate_result
函数(需要预先定义或导入)对车牌小图roi_img进行识别,获取车牌号plate_number
、识别概率rec_prob
,以及(如果is_color
为True
)车牌颜色plate_color
和颜色置信度color_conf
。
- 构建结果字典:
- 将车牌的边界框rect、检测置信度conf、角点坐标landmarks、车牌号
plate_number
、识别概率rec_prob
、车牌高度roi_img.shape[0]
、车牌颜色(如果is_color
为True)以及车牌类型class_label
等信息存入result_dict。
- 返回结果:
- 返回包含车牌识别结果的字典
result_dict
。
3.5 车牌检测模型训练
- 下载一定量的数据集
需要下载一定量的数据集,项目的作者有提供,需要的可以联系项目作者,格式为 yolov5 格式
label x y w h pt1x pt1y pt2x pt2y pt3x pt3y pt4x pt4y
自己的数据集可以通过lablme 软件,create polygons标注车牌四个点即可,然后通过json2yolo.py 将数据集转为yolo格式,也可进行训练
- 修改
data/widerface.yaml
train
和val
路径,换成你的数据路径
train: /your/train/path #修改成你的训练集路径
val: /your/val/path #修改成你的验证集路径
# number of classes
nc: 2 #这里用的是2分类,0 单层车牌 1 双层车牌
# class names
names: [ 'single','double']
- 训练命令
python3 train.py --data data/widerface.yaml --cfg models/yolov5n-0.5.yaml --weights weights/plate_detect.pt --epoch 120
3.6 车牌识别模型训练
-
下载数据集
数据集可以自己收集,或者找项目作者提供 -
数据集打上标签,生成train.txt和val.txt
图片命名如上图:车牌号_序号.jpg 然后执行如下命令,得到 train.txt
和 val.txt
python plateLabel.py --image_path your/train/img/path/ --label_file datasets/train.txt
python plateLabel.py --image_path your/val/img/path/ --label_file datasets/val.txt
数据格式如下:
train.txt
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_ALL/冀BAJ731_3.jpg 5 53 52 60 49 45 43
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_ALL/冀BD387U_2454.jpg 5 53 55 45 50 49 70
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_ALL/冀BG150C_3.jpg 5 53 58 43 47 42 54
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_OTHER_ALL/皖A656V3_8090.jpg 13 52 48 47 48 71 45
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_OTHER_ALL/皖C91546_7979.jpg 13 54 51 43 47 46 48
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_OTHER_ALL/皖G88950_1540.jpg 13 58 50 50 51 47 42
/mnt/Gu/trainData/plate/new_git_train/CCPD_CRPD_OTHER_ALL/皖GX9Y56_2113.jpg 13 58 73 51 74 47 48
- 将
train.txt
val.txt
路径写入lib/config/360CC_config.yaml
中
DATASET:
DATASET: 360CC
ROOT: ""
CHAR_FILE: 'lib/dataset/txt/plate2.txt'
JSON_FILE: {'train': 'datasets/train.txt', 'val': 'datasets/val.txt'}
- 执行训练命令
python train.py --cfg lib/config/360CC_config.yaml
3.7 图片识别测试
-
项目基础图片集
项目中已经预先存放了一些车牌图片,我们可以使用这些图片来测试一下 -
执行图片检测命令
python detect_plate.py --detect_model weights/plate_detect.pt --rec_model weights/plate_rec_color.pth --image_path imgs --output result
输入完命令后,大约在3秒后,程序持续输出识别结果,可以看到程序的启动速度还是挺快了,总共的识别时间在8.5秒左右,平均一张图片的识别速度在0.5秒左右,这速度已经是相当的快了
- 图片识别结果
3.8 视频识别测试
- 项目基础识别视频
视频:
yolov2车牌识别视频
视频截图:
视频时间长度在 54 秒左右,视频类型为 mp4,视频内容主要是以行驶中的车尾视角拍摄马路上的车辆。
-
上传视频到项目文件中
我连接软件用的 MobaXterm,不知道为什么,上传文件总是会卡住,没办法,只能用 Xftp 再连接一次然后上传视频了
-
执行视频检测命令
python detect_plate.py --detect_model weights/plate_detect.pt --rec_model weights/plate_rec_color.pth --video 2.mp4
输入执行命令后,系统大约在 5 秒后启动了,然后将视频分帧处理
处理过程中ing
视频的后半部分就已经没有车了,处理显示的也都是空帧,就没有任何意义了,这里就不进行展示了。
可以看到,54 秒的视频,共划分了 1501 帧,香橙派 Orange pi AIpro 这块板子,处理速度在 2.7 fps 左右,相对比其他开发板子来说,速度已经可以了。
四. 踩坑问题
- 烧录报错
错误提示
出了点问题。如果源镜像曾被压缩过,请检查它是否已损坏。
The writer process ended unexpectedly
直接点击跳过即可
- ping 命令不能使用
解决办法:
sudo setcap cap_net_raw+p /bin/ping
成功解决
五. 使用体验与总结
使用下来体验感还挺好的,板子在通电启动后,5秒内散热风扇的噪音还是比较大的,5秒后,声音就变的很小了,后续即使在跑项目的时候,声音也没有较大的变化,散热做的也非常的好,整体体验下来板子的温度一直维持在一个正常的温度,没有出现温度过高的情况。
除了个人体验外,OrangePi AIpro 凭借其卓越的即插即用特性,极大地加速了开发进程。通过简单的配置步骤,省去了繁琐的硬件调试环节。在软件生态方面,香橙派社区构建了一个资源丰富、文档详尽的生态系统,对初学者来说可以轻松访问并选用适合的文字识别模型和框架,通过直观易懂的指南,快速部署至开发板上。