/webcam_yolov3_jetson_tx_hikvision

多个网络摄像头进行拉流以及对象检测。平台使用Jetson TX2 (ARM architecture),海康摄像头,对象检测采用YOLO v3模型。

Primary LanguagePython

Introduction

  • Python多个网络摄像头拉流
  • 拉流后使用PyTorch实现的YOLO v3做对象检测

最后需要运行在ARM架构的Jetson TX 2平台上,并且以海康摄像头作为视频源。

配合其他模型

同样的拉流代码同样可以复用在EfficientDet、YoloV5等模型上,不过模型需要二次封装。

  • EfficientDet implementation: gaodechen/EfficientDet-Webcam
  • YoloV5: YoloV5工作对性能的对比缺乏参照,但其实现依旧有启发性。推断前使用DataLoader,故而修改时去掉了拉流取图的进程。

测试环境

  • Intel CPU + NVIDA GPU
  • 视频源:RTSP/RTMP网络摄像头

使用

多网络摄像头拉流 + YOLO v3对象检测

  • clone ayooshkathuria/pytorch-yolo-v3的对象检测实现
  • 下载YOLO v3预训练模型yolov3.weights
  • 将本项目文件覆盖放入pytorch-yolo-v3文件夹当中
  • 修改settings.py,加入你的摄像头地址

运行:

python run.py                          # Default detect and display all cameras in one window
python run.py --single_window=False    # OR Detect and display in separate windows
  • single_window默认为True,表示所有画面合并显示到同一个窗口当中
  • num_cameras默认值为settings.py中的IP列表大小,表示处理所有给定摄像头

仅多摄像头拉流

去掉predict()进程的调用,并且pop_image()显示raw_q中的原图像。也就是说拉流取得的图像直接取出进行显示。

processes = [
    mp.Process(target=push_image, args=(raw_q, cam_addr)),
    # display images in raw_q instead
    mp.Process(target=pop_image, args=(raw_q, window_name)),
]

帧率

摄像头本身具有帧率,而模型检测也存在帧率。摄像头帧率可以通过cv2属性获取:

fps = cap.get(cv2.CAP_PROP_FPS)

另外此处我们采用的帧率计算方法是,直接使用predict()进程的推断时间来计算帧率,此方法包含的耗时,包含队列取图时阻塞的时间,以及模型推断时间。

文件结构

本程序文件当中主要如下:

  • settings.py:配置IP camera地址列表,画面大小
  • yolov3.pypytorch-yolo-v3模型推断的二次封装,不需要变动
  • preprocess.py:与pytorch-yolo-v3相比,只是添加了prep_frame函数,将图片输入从文件改为cv2图像

其他细节

OpenCV后端选择

系统默认给出了GStreamer进行后端解码。这里我们改为FFMPEG作为后端:

cap = cv2.VideoCapture(cam_addr, cv2.CAP_FFMPEG)

多个网络摄像头如何拉流?

Yonv1943/Python项目当中已经给出了很棒的解答。我们在此基础上进行修改。

考虑对于一个摄像头,由于H.264编码的问题使用多进程实现,多进程间的同步采用共享队列(multiprocessing.Queue)。

  • push_image(): 拉流,将图片送入raw_queue(未经推断的图片队列)
  • predict(): 将raw_queue中的图片取出,经模型推断放进pred_queue()(处理后的图片队列)
  • pop_image(): 将pred_queue中的图片通过OpenCV显示

总结起来,导致程序崩溃的原因可能有:

  • 推断速度慢,与拉流速度不一致: 即进程同步问题。raw_queue当中累积图片多,延时高,甚至队列溢出程序崩溃
  • 网络原因: 断流导致程序崩溃
  • 机器硬件性能不足: 导致创建某些进程失败

最初代码中,作者通过push_image()进程不断抛弃队首图片解决同步问题。

但是实验过程中,程序还会因为断流的原因产生崩溃,故而这里加入重连,得到最终能够长时间稳定运行的push_image()如下:

def push_image(raw_q, cam_addr):
    cap = cv2.VideoCapture(cam_addr, cv2.CAP_FFMPEG)
    while True:
        is_opened, frame = cap.read()
        if is_opened:
            raw_q.put(frame)
        else:
            # reconnect
            cap = cv2.VideoCapture(cam_addr, cv2.CAP_FFMPEG)
        if raw_q.qsize() > 1:
            # drop old images
            raw_q.get()
        else:
            # wait for streaming
            time.sleep(0.01)

经过上述修改可以长时间稳定运行,缺点是断流时重连耗时,画面卡顿1s左右。

此外,在NVIDIA Jetson TX2上运行时,出现了程序启动后,无法加载几个摄像头画面的问题,例如分开窗口显示时,有一两个摄像头的窗口一直没有创建。但是程序在本地却正常运行。

最后观察发现,由于Jetson TX2内存不足,导致进程无法创建。解决方法:创建内存交换区。目测4个海康摄像头进行YOLO对象检测时,6G交换区足够。

Jetson TX 2 ARM

运行环境

  • 硬件平台: Jetson TX2 ARM
  • 操作系统:Ubuntu 18.04
  • 视频源: 多个海康网络摄像头(RTSP TCP)

Jetson TX2 ARM PyTorch环境的搭建

git clone --recursive以及compile太耗时了!而且板子上网速也慢,读写也慢。好在最终找到了可以用的whl安装包,直接pip install一次成功。

  • PyTorch .whl downloading & installation on Jetson TX2: Nvidia Forum