基于PP-Human的人流量可视化数据大屏
【AI达人创造营三期】django+pyecharts+PP-Human实现流式视频传输与动态实时数据可视化
基于PP-Human的人流量可视化数据大屏
使用django+pyecharts+PP-Human完成动态数据大屏的开发,目前完成了人流数据的采集与入库,PP-Human的其他功能:如属性识别等信息暂未接入大屏
1. 快速开始
1.1 环境配置
整个项目可以在项目挂载的数据集中下载,暂时还没有上传github,下载解压完成后
# 切换到解压目录
cd crowd_vis
# 我已经把依赖全部写在requirements.txt中,直接pip安装即可
# paddlepaddle没有写入,没有安装的话自行安装
pip install -r requirements.txt
1.2 启动服务
命令行运行:
python manage.py runserver
出现以下内容即启动成功
System check identified no issues (0 silenced).
August 19, 2022 - 22:53:05
Django version 3.2.15, using settings 'crowd_vis.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
随后启动你的浏览器,输入http://127.0.0.1:8000/
即可访问
2. 视频流式传输解决方案
PP-Human其实目前并不支持流式传输,我这里用了一个挺憨的办法结合django的StreamingHttpResponse
实现了“伪流式”,在前端的网页上确实是流式了(
2.1 PP-Human埋点
在pipeline.py文件中的predict_video
函数的末尾加入
show_im = cv2.resize(im, (0, 0), fx=0.5, fy=0.5)
temp1, temp2 = cv2.imencode('.jpeg', show_im)
fp = open('frame.txt', 'wb')
fp.write(temp2.tobytes())
fp.close()
fp = open('records.txt', 'w')
ajax_data = records[-1]
ajax_data = [ajax_data, len(mot_res['boxes'])]
json.dump(ajax_data, fp)
fp.close()
整个PP-Human的pipeline其实就是一连串模型逐一对单个frame推理然后可视化,所以在函数的最后可以直接找到可视化结束的im
变量,将他转换tobytes()
之后,就可以传递给django处理了
2.2 Django流式传输
Django有StreamingHttpResponse
流式响应类,这个类不同于普通的HttpResponse
,他需要利用迭代器缓存数据。于是编写django视图读取之前埋点“偷”来的图片数据放入缓存其就实现了“伪”流式(在前端是真流式):以下是views.py中的流式传输部分
def pp_human_service():
# PP-Human后台进程
while True:
pp_human_path = os.path.join(BASE_DIR, "pp-human", "pipeline", "pipeline.py ")
yml_path = os.path.join(BASE_DIR, "pp-human", "pipeline", "config", "infer_cfg_pphuman.yml ")
test_video_path = os.path.join(BASE_DIR, 'test1.mp4')
# shell = r'python ' + pp_human_path + '--config ' + yml_path + r' --camera_id=0 --device=gpu --output_dir=output --do_entrance_counting'
shell = r'python ' + pp_human_path + '--config ' + yml_path + r' --video_file=' + test_video_path + ' --device=gpu --output_dir=output --do_entrance_counting'
subprocess.run(shell, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
# subprocess.run(shell)
t = threading.Thread(target=pp_human_service)
t.setDaemon(True)
t.start()
def video_display():
# 流式视频传输迭代器
txt_path = os.path.join(BASE_DIR, 'frame.txt')
while True:
fp = open(txt_path, 'rb')
info = fp.read()
fp.close()
if info:
yield b'--frame\r\n Content-Type: image/jpeg\r\n\r\n' + info + b'\r\n'
def video(request):
# 使用流传输传输视频流
return StreamingHttpResponse(video_display(), content_type='multipart/x-mixed-replace; boundary=frame')
3. 数据入库与数据动态刷新
3.1 数据入库
与前面伪流式的方法差不多,起一个定时的子进程收录从PP-Human埋点中采集的推理结果与信息。直接写在views.py里就可以,django在启动服务的时候会顺路把这些子进程都带起来。
def info_update_service():
# 数据采集入库后台进程
global context
txt_path = os.path.join(BASE_DIR, 'records.txt')
while True:
try:
fp = open(txt_path, 'r')
info = json.load(fp)
fp.close()
vis_count = info[1]
info = info[0]
info = info[info.find("Total count: ") + 13:]
total = eval(info[:info.find(',')])
info = info[info.find(":") + 2:]
in_count = eval(info[:info.find(',')])
info = info[info.find(":") + 2:]
out_count = eval(info[:-1])
count0, count1, count2, count3, count4 = total % 10, total // 10 % 10, total // 100 % 10, total // 1000 % 10, total // 10000 % 10
context = [total, vis_count, in_count, out_count, count0, count1, count2, count3, count4]
db_obj = crowdinfo(total_count=total, in_count=in_count,
out_count=out_count,
vis_count=vis_count)
db_obj.save()
sleep(2)
except Exception as e:
sleep(2)
print("db saving failed!")
print(e)
pass
t1 = threading.Thread(target=info_update_service)
t1.setDaemon(True)
t1.start()
3.2 数据动态实时刷新
数据动态刷新就是ajax的工作了,在django我们只需要写一个返回Json数据的view就可以。
这里使用pyecharts把各种数据表都整好之后一起扔给ajax处理。
def graph_vis(request):
# 人流折线图,数据表格,饼图数据更新
x_data = []
vis_data = []
in_data = []
out_data = []
table_data = []
pie_data = []
for info in crowdinfo.objects.all().order_by('-shoot_time')[:20]:
if not pie_data:
pie_data = [("滞留量", info.total_count-info.in_count-info.out_count),
("流入量", info.in_count),
("流出量", info.out_count)]
x_data.append(info.shoot_time.strftime("%Y-%m-%d %H:%M:%S"))
vis_data.append(info.vis_count)
in_data.append(info.in_count)
out_data.append(info.out_count)
table_data.append([info.shoot_time.strftime("%y-%m-%d %H:%M:%S"), info.total_count, info.vis_count, info.out_count, info.in_count])
data1 = (
Line()
.add_xaxis(x_data)
.add_yaxis("进入人流", in_data, is_smooth=True, symbol_size=10, is_connect_nones=True, color='red',
linestyle_opts=opts.series_options.LineStyleOpts(width=3))
.add_yaxis("出口人流", out_data, is_smooth=True, symbol_size=10, is_connect_nones=True, color='green',
linestyle_opts=opts.series_options.LineStyleOpts(width=3))
.set_global_opts(legend_opts=opts.LegendOpts(textstyle_opts=opts.TextStyleOpts(color='white')),
xaxis_opts=opts.AxisOpts(type_='time',
axisline_opts=opts.AxisLineOpts(linestyle_opts=opts.LineStyleOpts(
color='white'))),
yaxis_opts=opts.AxisOpts(axisline_opts=opts.AxisLineOpts(linestyle_opts=opts.LineStyleOpts(
color="white"))),
)
.dump_options_with_quotes()
)
data2 = (
Line()
.add_xaxis(x_data)
.add_yaxis("在镜人流", vis_data, is_smooth=True, symbol_size=10, is_connect_nones=True, color='red',
linestyle_opts=opts.series_options.LineStyleOpts(width=3))
.set_global_opts(legend_opts=opts.LegendOpts(textstyle_opts=opts.TextStyleOpts(color='white')),
xaxis_opts=opts.AxisOpts(type_='time',
axisline_opts=opts.AxisLineOpts(linestyle_opts=opts.LineStyleOpts(
color='white'))),
yaxis_opts=opts.AxisOpts(axisline_opts=opts.AxisLineOpts(linestyle_opts=opts.LineStyleOpts(
color="white"))),
)
.dump_options_with_quotes()
)
data3 = (
Table()
.add(headers=['采集时间', '总人流', '在镜人流', '出口人流', '进口人流'],
rows=table_data)
.render('statics/render.html')
)
data3 = open('statics/render.html', 'r', encoding='utf-8').read()
data3 = data3[data3.find("<table"):data3.find("</table>")]
data3 = data3.replace('\n', '').replace('"', "'")
data4 = (
Pie()
.add('', pie_data, center=['50%', '60%'], radius='70%')
.set_global_opts(title_opts=opts.TitleOpts(title="人流状态分布",
title_textstyle_opts=opts.TextStyleOpts(color="white")),
legend_opts=opts.LegendOpts(orient='vertical', pos_left='right',
textstyle_opts=opts.TextStyleOpts(color='white')))
.set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
.set_colors(["red", "yellow", "pink", "orange", "purple"])
.dump_options_with_quotes()
)
data1 = json.loads(data1)
data2 = json.loads(data2)
data3 = json.dumps(data3, ensure_ascii=False)
data3 = data3.replace('"', '')
data4 = json.loads(data4)
data = {
"code": 200,
"msg": "success",
"data": [data1, data2, data3, data4]
}
return JsonResponse(data)
ecarts是个JS库为什么要用python画好再传过去?因为这样更符合前后端分离的范式(不过是在给不想写Js找理由)。
一共有4个可视化数据表,前端JS代码如下:很短很方便,我不喜欢JS (
var chart1 = echarts.init(document.getElementById('graph1'),'roma');
var chart2 = echarts.init(document.getElementById('graph2'),'roma');
var chart4 = echarts.init(document.getElementById('graph4'),'light');
$(
function () {
fetchData(chart1,chart2,chart4);
setInterval(fetchData,2000);
}
);
function fetchData() {
$.ajax({
type: "get",
url: "/graph_vis",
dataType: 'json',
success: function (result) {
chart1.setOption(result.data[0]);
chart2.setOption(result.data[1]);
document.getElementById('graph3').innerHTML=result.data[2];
chart4.setOption(result.data[3]);
}
});
}
差点忘了,还有一个数字大屏
function num_count(){
// 朝后端发送ajax请求
$.ajax({
// 1.指定朝哪个后端发送ajax请求
url:'/num_count', //不写就是朝当前地址提交【与form表单的action参数相同】
// 2.请求方式
type:'get', // 不指定默认就是get,都是小写
// 3.数据
data:{},
// 4.回调函数:当后端给你返回结果的时候会自动触发,args接受后端的返回结果
success:function (args) {
document.getElementById('count1').innerHTML=args[0];
document.getElementById('count2').innerHTML=args[1];
document.getElementById('count3').innerHTML=args[2];
document.getElementById('count4').innerHTML=args[3];
document.getElementById('num_count0').innerHTML=args[4];
document.getElementById('num_count1').innerHTML=args[5];
document.getElementById('num_count2').innerHTML=args[6];
document.getElementById('num_count3').innerHTML=args[7];
document.getElementById('num_count4').innerHTML=args[8];
}
})
}
setInterval("num_count()",1000);
最终效果:
4. 未来工作
- 接入PP-Human的更多功能,如:属性识别、打架识别、摔倒识别等
- 开发报警推送功能,结合打架、摔倒、人流过多等情况进行警告推送
- 人流量数据时序相关,考虑接入机器学习的时序模型实现对人流的预警与预测
- 少画饼,多摸鱼(???)
!unzip data/data165784/crowd_vis.zip
%cd /home/aistudio/crowd_vis
!pip install -r requirements.txt
!python manage.py runserver
此文章为搬运
原项目链接
更多推荐
所有评论(0)