本文共享自华为云社区《AI寻车》,作者:杜甫盖房子。
本案例咱们运用FairMOT进行车辆检测与盯梢、yolov5进行车牌检测、crnn进行车牌辨认,在停车场进口、出口、停车位对车辆进行盯梢与车牌辨认,不管停车场路线多杂乱,小车在你掌控之中!终究效果如下:
咱们运用ModelBox Windows SDK
进行开发,如果还没有安装SDK
,能够参考ModelBox端云协同AI开发套件(Windows)设备注册篇、ModelBox端云协同AI开发套件(Windows)SDK安装篇完结设备注册与SDK
安装。
技能开发
这个运用对应的ModelBox
版别已经做成模板放在华为云OBS中,能够用sdk中的solution.bat
东西下载,接下来咱们给出该运用在ModelBox
中的完好开发过程:
1)下载模板
履行.\solution.bat -l
可看到当时揭露的技能模板:
PS ███> .\solution.bat -l
...
Solutions name:
mask_det_yolo3
...
vehicle_plate_multi_centernet_yolov5_crnn
成果中的vehicle_plate_multi_centernet_yolov5_crnn即为AI寻车运用模板,可运用如下命令下载模板:
PS ███> .\solution.bat -s vehicle_plate_multi_centernet_yolov5_crnn
...
solution.bat
东西的参数中,-l
代表list
,即列出当时已有的模板称号;-s
代表solution-name
,即下载对应称号的模板。下载下来的模板资源,将寄存在ModelBox
中心库的solution
目录下。
2)创立工程
在ModelBox sdk
目录下运用create.bat
创立vehicle_plate
工程:
PS ███> .\create.bat -t server -n vehicle_plate -s vehicle_plate_multi_centernet_yolov5_crnn
sdk version is modelbox-xxx
success: create vehicle_plate in ███\modelbox\workspace
create.bat
东西的参数中,-t
表明创立事务的类别,包括工程(server)、Python功用单元(Python)、推理功用单元(infer)等;-n
代表name
,即创立事务的称号;-s
代表solution-name
,表明将运用后边参数值代表的模板创立工程,而不是创立空的工程。
workspace
目录下将创立出vehicle_plate
工程,工程内容如下所示:
vehicle_plate
|--bin
│ |--main.bat:运用履行进口
│ |--mock_task.toml:运用在本地履行时的输入输出装备,此运用默许运用4路本地视频文件为输入源,终究成果拼接为四宫格输出到屏幕,可根据需要修改
|--CMake:寄存一些自界说CMake函数
|--data:寄存运用运转所需要的图片、视频、文本、装备等数据
│ |--chuchang_10.mp4:停车场出口测验视频
│ |--ruchang_10.mp4:停车场进口测验视频
│ |--ruku_10.mp4:停车位1测验视频
│ |--kong_10.mp4:停车位2测验视频
│ |--plate_keys.txt:车牌字符文件
│ |--content_file.json:技能参数大局装备文件
|--dependence
│ |--modelbox_requirements.txt:运用运转依靠的外部库在此文件界说,本运用依靠pillow、scipy等东西包
|--etc
│ |--flowunit:运用所需的功用单元寄存在此目录
│ │ |--cpp:寄存C++功用单元编译后的动态链接库,此运用没有C++功用单元
│ │ |--collapse_ocr:归拢功用单元,车牌辨认后处理
│ │ |--condition:条件功用单元,判别是否检测到车辆/车牌
│ │ |--draw_full_screen:多路视频拼接输出功用单元
│ │ |--draw_plate:车牌检测成果制作
│ │ |--draw_track_bbox:车辆盯梢成果制作
│ │ |--expand_image:翻开功用单元,翻开车辆检测/车牌检测成果并行推理
│ │ |--letter_resize:车辆检测预处理功用单元
│ │ |--object_tracker:盯梢功用单元
│ │ |--plate_det_post:车牌检测后处理功用单元
│ │ |--url_cfg:流单元,多路输入解析
│ │ |--vehicle_det_post:车牌检测成果制作
|--flowunit_cpp:寄存C++功用单元的源代码,此运用没有C++功用单元
|--graph:寄存流程图
│ |--vehicle_plate.toml:默许流程图,运用本地视频文件作为输入源
│ |--modelbox.conf:modelbox相关装备
|--hilens_data_dir:寄存运用输出的成果文件、日志、功用计算信息
|--model:推理功用单元目录
│ |--vehicle_det:车辆检测推理功用单元
│ │ |--vehicle_det.toml:车辆检测推理功用单元的装备文件
│ │ |--vehicle_det_320x576.onnx:车辆检测onnx模型
│ |--plate_det:车牌检测推理功用单元
│ │ |--plate_det.toml:车牌检测推理功用单元的装备文件
│ │ |--plate_det.onnx:车牌检测onnx模型
│ |--plate_rec:车牌辨认推理功用单元
│ │ |--plate_rec.toml:车牌辨认推理功用单元的装备文件
│ │ |--plate_rec.onnx:车牌辨认onnx模型
|--build_project.sh:运用构建脚本
|--CMakeLists.txt
|--rpm:打包rpm时生成的目录,将寄存rpm包所需数据
|--rpm_copyothers.sh:rpm打包时的辅助脚本
3)检查流程图
vehicle_plate
工程graph
目录下寄存流程图,默许的流程图vehicle_plate.toml
与工程同名,其内容为(以Windows版ModelBox
为例):
# 功用单元的扫描途径,包括在[]中,多个途径运用,分隔
# ${HILENS_APP_ROOT} 表明当时运用的实践途径
# ${HILENS_MB_SDK_PATH} 表明ModelBox中心库的实践途径
[driver]
dir = [ "${HILENS_APP_ROOT}/etc/flowunit", "${HILENS_APP_ROOT}/etc/flowunit/cpp", "${HILENS_APP_ROOT}/model", "${HILENS_MB_SDK_PATH}/flowunit",]
skip-default = true
[profile]
# 通过装备profile和trace开关启用运用的功用计算
profile = false # 是否记录profile信息,每隔60s记录一次计算信息
trace = false # 是否记录trace信息,在使命履行过程中和结束时,输出计算信息
dir = "${HILENS_DATA_DIR}/mb_profile" # profile/trace信息的保存方位
[graph]
format = "graphviz" # 流程图的格式,当时仅支撑graphviz
graphconf = """digraph vehicle_plate{
node [shape=Mrecord]
queue_size = 1
batch_size = 1
input1[type=input,flowunit=input,device=cpu,deviceid=0]
data_source_parser[type=flowunit, flowunit=data_source_parser, device=cpu, deviceid=0]
url_cfg[type=flowunit, flowunit=url_cfg, device=cpu, deviceid=0]
video_demuxer[type=flowunit, flowunit=video_demuxer, device=cpu, deviceid=0]
video_decoder[type=flowunit, flowunit=video_decoder, device=cpu, deviceid=0, pix_fmt="rgb"]
letter_resize[type=flowunit, flowunit=letter_resize, device=cpu]
color_transpose[type=flowunit, flowunit=packed_planar_transpose, device=cpu, deviceid=0]
normalize[type=flowunit, flowunit=normalize, device=cpu, deviceid=0, standard_deviation_inverse="0.003921568627451, 0.003921568627451, 0.003921568627451"]
vehicle_det[type=flowunit, flowunit=vehicle_det, device=cpu, deviceid=0, batch_size=1]
vehicle_det_post[type=flowunit, flowunit=vehicle_det_post, device=cpu, deviceid=0]
object_tracker[type=flowunit, flowunit=object_tracker, device=cpu, deviceid=0]
vehicle_condition[type=flowunit, flowunit=condition, device=cpu, deviceid=0]
expand_car[type=flowunit, flowunit=expand_image, device=cpu, deviceid=0, img_h=640, img_w=640]
plate_color_transpose[type=flowunit flowunit=packed_planar_transpose device=cpu deviceid="0"]
plate_normalize[type=flowunit flowunit=normalize device=cpu deviceid=0 standard_deviation_inverse="0.003921568627451,0.003921568627451,0.003921568627451"]
plate_det[type=flowunit flowunit=plate_det device=cpu deviceid="0", batch_size=1]
plate_det_post[type=flowunit, flowunit=plate_det_post, device=cpu, deviceid=0]
plate_condition[type=flowunit, flowunit=condition, device=cpu, deviceid=0, key="plate"]
expand_plate[type=flowunit, flowunit=expand_image, device=cpu, deviceid=0, img_h=48, img_w=168, key="plate"]
ocr_color_transpose[type=flowunit flowunit=packed_planar_transpose device=cpu deviceid="0"]
ocr_mean[type=flowunit flowunit=mean device=cpu deviceid="0" mean="149.94,149.94,149.94"]
ocr_normalize[type=flowunit flowunit=normalize device=cpu deviceid=0 standard_deviation_inverse="0.020319,0.020319,0.020319"]
plate_rec[type=flowunit flowunit=plate_rec device=cpu deviceid="0", batch_size=1]
collapse_ocr[type=flowunit flowunit=collapse_ocr device=cpu deviceid="0"]
draw_plate[type=flowunit, flowunit=draw_plate, device=cpu, deviceid=0]
draw_track_bbox[type=flowunit, flowunit=draw_track_bbox, device=cpu, deviceid=0]
draw_full_screen[type=flowunit, flowunit=draw_full_screen, device=cpu, deviceid=0]
video_out[type=flowunit, flowunit=video_out, device=cpu, deviceid=0, full_screen=true]
input1:input -> data_source_parser:in_data
data_source_parser:out_video_url -> url_cfg:in_1
url_cfg:out_1 -> video_demuxer:in_video_url
video_demuxer:out_video_packet -> video_decoder:in_video_packet
video_decoder:out_video_frame -> letter_resize:in_image
letter_resize:resized_image -> color_transpose:in_image
color_transpose:out_image -> normalize:in_data
normalize:out_data -> vehicle_det:input
vehicle_det:output -> vehicle_det_post:in_feat
letter_resize:out_image -> vehicle_det_post:in_image
vehicle_det_post:out_feat -> object_tracker:in_feat
object_tracker:out_track -> vehicle_condition:in_track
video_decoder:out_video_frame -> vehicle_condition:in_image
vehicle_condition:out_track -> expand_car:in_image
expand_car:out_image -> plate_color_transpose:in_image
plate_color_transpose:out_image -> plate_normalize:in_data
plate_normalize:out_data -> plate_det:input
plate_det:output -> plate_det_post:in_feat
expand_car:out_image -> plate_det_post:in_image
plate_det_post:out_tracks -> plate_condition: in_track
vehicle_condition:out_track -> plate_condition: in_image
plate_condition:out_track -> expand_plate:in_image
expand_plate:out_image -> ocr_color_transpose:in_image
ocr_color_transpose:out_image -> ocr_mean:in_data
ocr_mean:out_data -> ocr_normalize:in_data
ocr_normalize:out_data -> plate_rec:input
plate_rec:output -> collapse_ocr:in_feat
expand_plate:out_image -> collapse_ocr:in_image
collapse_ocr:out_tracks -> draw_plate:in_feat
plate_condition:out_track -> draw_plate:in_image
draw_plate:out_image -> draw_track_bbox:in_image
plate_condition:out_image -> draw_track_bbox:in_image
draw_track_bbox:out_image -> draw_full_screen:in_image
vehicle_condition:out_image -> draw_full_screen:in_image
draw_full_screen:out_image -> video_out:in_video_frame
}"""
[flow]
desc = "vehicle_plate run in modelbox-win10-x64"
将流程图可视化:
图示中,灰色部分为预置功用单元,其余颜色为咱们完结的功用单元,其中绿色为一般通用功用单元,赤色为推理功用单元,蓝色为条件功用单元,黄色为翻开归拢功用单元。整个运用逻辑相对杂乱一些,视频解码后做图像预处理,接着是车辆检测,模型后处理得到车形框与128维车辆reid特征,送入盯梢算法进行实时盯梢,通过条件功用单元判别,检测到车辆的图送入翻开功用单元,切图进行车牌检测,车牌检测成果归拢后相同要判别是否检测到车牌,检测到车牌的帧再翻开并行进行车牌辨认,未检测到的则直接制作车辆信息。而未检测到车辆的帧则直接送入多路拼接功用单元,终究输出。
4)中心逻辑
本运用中心逻辑中的盯梢与区域判别参照客流计算实战营的运用设计,盯梢逻辑在object_tracker
功用单元中,检测与盯梢运用的是FairMOT算法,算法介绍可参考论文。
首先检查object_tracker
功用单元中回来的盯梢目标结构:
def get_tracking_objects(self, online_targets_dict):
tracking_objects = {}
for cls_id in range(self.num_classes):
online_targets = online_targets_dict[cls_id]
for t in online_targets:
obj = {}
tlwh = t.tlwh
if tlwh[2] * tlwh[3] < self.min_box_area:
continue
tid = t.track_id
obj["bbox"] = [max(0, tlwh[0]), max(0, tlwh[1]), tlwh[0] + tlwh[2], tlwh[1] + tlwh[3]]
obj["licence"] = ""
obj["licence_score"] = 0.0
obj["plate"] = np.zeros((4, 2)).tolist()
obj["plate_score"] = 0.0
obj["bbox_score"] = t.score
tracking_objects[tid] = obj
return tracking_objects
能够看到,咱们回来的盯梢目标包括车型框、车辆检测得分等已有信息以及车牌、车牌得分、车牌框、车牌框得分等包括默许数据的占位信息,便利后续功用单元获取更新。
从流程图中能够看到,object_tracker
后成果送入车辆检测条件功用单元,相同的在流程图中还包括车牌检测条件功用单元,咱们当然是希望运用同一个功用单元完结两个判别,所以在条件功用单元condition
中,咱们装备了参数key
,默许为key = "bbox"
,即对结构体中的车型框进行判别,详细完结为:
if track_result and np.any([v.get(self.key) for k, v in track_result.items()]):
buffer_img.set("track", track_json)
out_track.push_back(buffer_img)
else:
buffer_img.set("track", track_json)
out_image.push_back(buffer_img)
这样的话如果是对车型框进行判别,只需要在流程图中装备key = “plate”
即可。
相同的,图翻开功用单元expand_image
也运用了相同的办法,使车辆检测与车牌检测能够共用功用单元:
tracking_objects = json.loads(buffer_img.get("track"))
for idx, target in tracking_objects.items():
box = np.array(target.get(self.key))
...
此外,因为本运用输入为4路视频,因而需要在url_cfg
单元中进行session等级信息装备:
url_str = str(self.count) + input_meta.get_private_string("source_url")
self.count += 1
data_context.get_session_context().set_private_string("multi_source_url", url_str)
session等级的信息在功用单元之间是同步的,这样就能够在后续的功用单元中获取当时输入为哪路输入:
url = data_context.get_session_context().get_private_string("multi_source_url")
image_index = int(url[0])
相同的,关于多路输入,咱们需要在本地mock时在bin/mock_task.toml
文件中进行输入装备:
[input]
type = "url"
url = "${HILENS_APP_ROOT}/data/ruchang_10.mp4"
[input1]
type = "url"
url = "${HILENS_APP_ROOT}/data/chuchang_10.mp4"
[input2]
type = "url"
url = "${HILENS_APP_ROOT}/data/ruku_10.mp4"
[input3]
type = "url"
url = "${HILENS_APP_ROOT}/data/kong_10.mp4"
关于多路输入的感兴趣区域划定,咱们运用content_file
装备:
[common]
content_file = "../data/content_file.json"
装备文件内容为:
[ { "vehicle_area": "190,245,382,656,1265,630,956,249", "plate_area": "190,245,382,656,1265,630,956,249" }, { "vehicle_area": "663,467,228,675,994,682,1167,459", "plate_area": "663,467,228,675,994,682,1167,459" }, { "vehicle_area": "0,0,1280,0,1280,720,0,720", "plate_area": "0,0,1280,0,1280,720,0,720" }, { "vehicle_area": "0,0,1280,0,1280,720,0,720", "plate_area": "0,0,1280,0,1280,720,0,720" }]
即针对不同输入装备各自的车型车牌感兴趣区域,在后续功用单元中获取装备的参数信息进行处理,如plate_det_post
功用单元:
self.areas = json.loads(data_context.get_session_config().get_string("iva_task_common"))
url = data_context.get_session_context().get_private_string("multi_source_url")
image_index = int(url[0])
self.area = self.areas[image_index].get("plate_area")
if self.area:
self.area = np.array(list(map(int, self.area.split(",")))).reshape(-1, 1, 2).astype(np.int32)
咱们现在关于车型和车牌检测的参数装备是保持共同的,也能够装备为不同参数。
5)三方依靠库
本运用依靠scipy等东西包,ModelBox运用不需要手动安装三方依靠库,只需要装备在dependence\modelbox_requirements.txt
,运用在编译时会主动安装。
6)用启动脚本履行运用
在项目目录下履行.\bin\main.bat
运转运用:
PS ███> .\bin\main.bat
...
能够看到屏幕呈现技能画面:
白线即装备的感兴趣区域,区域外/未过线车辆根据id赋色,区域内/已过线车辆的运用灰色框,可在输入输出装备中修改划区域使命类型与坐标点。
7)技能调试
咱们1.5.0版别SDK
供给了debug
东西,在VSCode中翻开项目根目录,在创立项目时已经主动生成了调试装备文件:
如果有其他安装包,能够在装备文件中增加PYTHONPATH
参数。
在调试时,能够直接在感兴趣的代码处打断点调试即可:
启动调试后与其他程序调试操作共同:
点击关注,第一时间了解华为云新鲜技能~