一.问题描述
工厂中经常需要安全生产,故需要检测目前区域内是否有动态物体进入。
但是往往因为视频设备算力或者算法性能问题,无法准确判断动态物体是人还是其他物体。
故设计了一套系统,用于实时动态检测并且判定区域内是否出现人。
二.程序以及算法
2.1程序主体流程
-
检测设备进行高斯混合模型的动态检测
-
将检测到的图片传输给云端算法
-
由云端算法yolov5判断图片中是否有人,并且返回人的坐标
-
检测设备根据云端算法返回的结果绘制图片并且按照时间保存。
2.2高斯混合模型
2.2.1算法原理讲解
2.2.2代码实现
import threading
import time
import cv2
import numpy as np
import pprint
import requests
DETECTION_URL = "http://10.101.6.64:6666/v1/object-detection/yolov5s"
def add_new_cam(rtsp_path, points):
# 创建模型
mog = cv2.createBackgroundSubtractorMOG2() # 定义高斯混合模型对象 mog
# gmg = cv2.bgsegm.createBackgroundSubtractorGMG()
# knn = cv2.createBackgroundSubtractorKNN(detectShadows=False)
# 绘制蒙版
cap = cv2.VideoCapture(rtsp_path)
ret, frame = cap.read()
mask = np.zeros(frame.shape, np.uint8)
mask = cv2.fillPoly(mask, [points], (255, 255, 255))
# 初始化计时器用于判断时间
time_now = time.time()
# cv2.imshow("mask", mask)
while 1:
ret, frame = cap.read()
frame_to_save = frame.copy()
frame_to_show = frame.copy()
frame = cv2.bitwise_and(frame, mask)
# 混合高斯模型
fgmask = mog.apply(frame) # 使用前面定义的高斯混合模型对象 mog 当前帧的运动目标检测,返回二值图像
gray_frame = fgmask.copy()
kernel = np.ones((5, 5), np.uint8)
gray_frame = cv2.morphologyEx(gray_frame, cv2.MORPH_OPEN, kernel)
# 返回值: contours,轮廓的坐标。 hierarchy,各个框之间父子关系,不常用。
contours, hierarchy = cv2.findContours(gray_frame, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 绘制每一个轮廓框到原始图像 frame 中
for contour in contours:
if cv2.contourArea(contour) < 1500: # 计算候选框的面积,如果小于1500,跳过当前候选框
continue
(x, y, w, h) = cv2.boundingRect(contour) # 根据轮廓,得到当前最佳矩形框
cv2.rectangle(frame_to_show, (x, y), (x + w, y + h), (255, 255, 0), 2) # 将该矩形框画在当前帧 frame 上
# 根据时间间隔保存图片
interval = time.time() - time_now
time_now = time.time()
if interval > 1:
print("Dynamic object detected,seding detect pic")
threading.Thread(target=send_pic, args=(frame_to_save,)).start()
cv2.imshow("gray", gray_frame)
cv2.imshow("contours", frame_to_show) # 显示当前帧
cv2.waitKey(1)
rtsp = 0
points = np.array([(0, 0), (1000, 0), (1000, 1000), (0, 1000)])
add_new_cam(rtsp, points)
其中
threading.Thread(target=send_pic, args=(frame_to_save,)).start()
为发送检测的图片到yolov5服务进行推理
2.3yolov5目标检测算法
2.3.1算法讲解
YOLOv5 is a family of object detection architectures and models pretrained on the COCO dataset, and represents Ultralytics open-source research into future vision AI methods, incorporating lessons learned and best practices evolved over thousands of hours of research and development.
2.3.2环境配置
运行yolov5需要pytorch框架,而pytorch的gpu版本需要英伟达推出的cuda环境。
因此采用了以下环节配置cuda环境:
-
nvidia 显卡驱动安装
有图形界面直接软件更新->附加驱动 就能找到nvidia驱动安装
无图形界面需要去官网下载run文件自行安装。
显卡必须是nvdia且高于一定算力
-
docker 安装
下载并安装
wget -qO- https://get.docker.com/ | sh
使非root用户可以直接运行docker
sudo usermod -aG docker 你的用户名
测试安装是否成功
docker run hello-world
-
nvidia docker 安装
Ubuntu上的Docker CE可以使用Docker的官方的语句进行设置
curl https://get.docker.com | sh sudo systemctl start docker sudo systemctl enable docker
设置稳定存储库和GPG密钥:
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \ && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \ && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
在更新包列表后安装nvidia-docker2包(和依赖项)
sudo apt-get update sudo apt-get install -y nvidia-docker2
设置默认运行时间后,重新启动Docker守护程序完成安装:
sudo systemctl restart docker
-
nvidia docker 运行
去nvidia cuda仓库下载最新的docker 并 运行容器
sudo docker run --rm --gpus all --name yolo nvidia/cuda:11.4.1-devel-ubuntu18.04
如果需要pytorch的docker,可以直接去PyTorch | NVIDIA NGC
-
torch 安装
前往PyTorch官网选择你的环境信息得到安装命令
如
pip3 install torch==1.9.1+cu111 torchvision==0.10.1+cu111 torchaudio==0.9.1 -f https://download.pytorch.org/whl/torch_stable.html
2.3.4算法搭建
git clone https://github.com/ultralytics/yolov5
cd yolov5 http://www.biyezuopin.vip
pip install -r requirements.txt
2.3.5算法调用官方示例
import torch
# 加载模型
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # or yolov5m, yolov5l, yolov5x, custom
# 图片路径
img = 'https://ultralytics.com/images/zidane.jpg' # or file, Path, PIL, OpenCV, numpy, list
# 推理接口
results = model(img)
# 结果显示
results.print() # or .show(), .save(), .crop(), .pandas(), etc.
2.3.6后台长期运行yolov5算法
安装screen
apt-get install screen -y
通过screen运行程序
screen -S yolocd yolov5/utils/flask_rest_apipython3 restapi.py
退出screen并且保持运行
crtl a d
2.4flask网络框架
2.4.1简介
flask 是基于python编写的一个轻量级网络框架
2.4.2环境配置与安装
pip install flask
2.4.3服务器主体代码
采用了yolov5项目中自带的restfulapi
更改了其中的端口号,并且加上了日志文件生成的功能。
"""
Run a rest API exposing the yolov5s object detection model
"""
import argparse
import io
import logging
import time
import torch
from PIL import Image
from flask import Flask, request
app = Flask(__name__)
DETECTION_URL = "/v1/object-detection/yolov5s"
@app.route(DETECTION_URL, methods=["POST"])
def predict():
if not request.method == "POST":
return
if request.files.get("image"):
image_file = request.files["image"]
image_bytes = image_file.read()
img = Image.open(io.BytesIO(image_bytes))
start_time = time.time()
results = model(img, size=640) # reduce size=320 for faster inference
end_time = time.time()
app.logger.info('infer time {}s'.format(str(end_time-start_time)))
return results.pandas().xyxy[0].to_json(orient="records")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Flask API exposing YOLOv5 model")
parser.add_argument("--port", default=6666, type=int, help="port number")
args = parser.parse_args()
http://www.biyezuopin.vip
handler = logging.FileHandler('flask.log')
logging_format = logging.Formatter('%(asctime)s %(message)s')
handler.setFormatter(logging_format)
app.logger.addHandler(handler)
model = torch.hub.load("ultralytics/yolov5", "yolov5l", force_reload=False ) # force_reload to recache
app.run(host="0.0.0.0", port=args.port) # debug=True causes Restarting with stat
2.4.4客户端部分代码
def send_pic(frame_to_save):
time_start_send = time.time()
# 保存图片
image_data = cv2.imencode('.png', frame_to_save)[1].tobytes()
# with open ("data.txt",'w') as f:
# f.write(str({"image": image_data}))
response = requests.post(DETECTION_URL, files={"image": image_data}).json()
time_end_send = time.time()
print("detect complete,cousumed {} s".format(str(time_end_send - time_start_send)))
# pprint.pprint(response)
save_flag = 0
for obj in response:
if obj['name'] == 'person' and obj['confidence'] > CONFIDENCE:
x1, y1, x2, y2 = int(obj['xmin']), int(obj['ymin']), int(obj['xmax']), int(obj['ymax'])
cv2.rectangle(frame_to_save, (x1, y1), (x2, y2), (255, 255, 0), 2)
save_flag += 1
if save_flag > 0:
cv2.imwrite(filename="image/" + str(time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())) + ".jpg",
img=frame_to_save)
print(str(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime())),'detected {} people'.format(str(save_flag)))
2.4.5查看日志文件得到运行情况
grep flask.log
部分结果如下
2021-09-27 03:03:43,794 infer time 0.08984136581420898s
2021-09-27 03:03:54,405 infer time 0.06490826606750488s
2021-09-27 03:04:05,195 infer time 0.08911895751953125s
2021-09-27 03:04:14,550 infer time 0.08931612968444824s
2021-09-27 03:04:23,533 infer time 0.08862948417663574s
2021-09-27 03:04:46,589 infer time 0.10284876823425293s
2021-09-27 03:05:04,673 infer time 0.06704258918762207s
2021-09-27 03:05:15,910 infer time 0.08734679222106934s
2021-09-27 03:05:38,870 infer time 0.10628700256347656s
2021-09-27 03:05:46,495 infer time 0.06697845458984375s
2021-09-27 03:06:19,265 infer time 0.10350394248962402s
2021-09-27 03:06:31,174 infer time 0.08736324310302734s
2021-09-27 03:06:32,962 infer time 0.0879511833190918s
2021-09-27 03:06:38,889 infer time 0.06657600402832031s
2021-09-27 03:06:43,692 infer time 0.0663297176361084s
2021-09-27 03:06:45,123 infer time 0.08658337593078613s
2021-09-27 03:06:49,458 infer time 0.08734583854675293s
2021-09-27 03:06:52,401 infer time 0.06574249267578125s
2021-09-27 03:07:03,808 infer time 0.08645200729370117s
2021-09-27 03:07:14,878 infer time 0.06605339050292969s
2021-09-27 03:07:42,774 infer time 0.10903763771057129s
2021-09-27 03:08:02,369 infer time 0.07390356063842773s
2021-09-27 03:08:11,318 infer time 0.1408219337463379s
2021-09-27 03:08:11,322 infer time 0.15143203735351562s
2021-09-27 03:08:13,024 infer time 0.08304357528686523s
2021-09-27 03:08:17,973 infer time 0.08363032341003418s
2021-09-27 03:08:22,080 infer time 0.06549215316772461s
2.5检测区域设定功能编写
2.5.1原理
动态检测时: 使用fillpoly方法做一个图像蒙版,其中需要检测的区域为255,其他区域都为0。之后把蒙版和原图进行与操作,把原图除了蒙版以外区域全部置零,而蒙版处不变。
目标检测时:每个通过yolov5预测到的目标,都会对其坐标中心进行判定,如果坐标中心位于代码,则保留该预测目标,否则舍弃。
2.5.2主要代码
设定参数
detect_region = [0.5, 0, 1, 1] # 检测区域划定x1,y1 ,x2,y2
cam_width, cam_height = 1280, 720 # 摄像头分辨率
detect_x1, detect_y1 = int(detect_region[0] * cam_width), int(detect_region[1] * cam_height)
detect_x2, detect_y2 = int(detect_region[2] * cam_width), int(detect_region[3] * cam_height)
points = np.array([(detect_x1, detect_y1), (detect_x2, detect_y1), (detect_x2, detect_y2), (detect_x1, detect_y2)])
# 动态检测使用mask与bitewise_and运算划定区域
ret, frame = cap.read()
mask = np.zeros(frame.shape, np.uint8)
mask = cv2.fillPoly(mask, [points], (255, 255, 255))
while (1):
ret, frame = cap.read()
frame_to_save = frame.copy()
frame_to_show = frame.copy()
frame = cv2.bitwise_and(frame, mask)
# 目标检测判断物体中心是否处于区域内的判定
if detect_x1 <= center_x <= detect_x2 and detect_y1 <= center_y <= detect_y2:
三.最终结果
3.1动态检测效果
在cpu为4800u的笔记本上可以稳定保持30fps,且cpu占用仅为8%
3.2目标检测效果
在gtx1070笔记本上搭建推理服务,可以稳定运行。
单张图片的传输与推理时间约为0.2秒
算法推理时间约为0.08秒
显卡占用为1.4g,功耗为36w左右
四.实验总结
总体效果符合预期,还有以下需要后期优化的点
1.网络传输
如果需要在嵌入式设备运行,需要注意网络传输条件,尽量采用网线传输,防止外部网络信号不佳产生设备掉线从而无法传输图片。
2.动态检测性能优化
以及代码尽量采用C++编写,保证嵌入式设备的性能占用较小。
3.目标检测性能优化
可以采用nvidia tensorrt推理服务,并且采用nginx转发提高并发量。
实验结果为640 * 640大小的图片,yolov5l在rtx3090上推理速度3毫秒一张,最大并发量约为1400张一秒。