You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

TensorFlow实时人体检测性能优化咨询(家庭多摄像头监控场景)

针对家庭监控多摄像头人体检测的性能优化方案

首先,先指出你代码里一个明显的操作不当:每次循环都重复调用get_tensor_by_name。这些张量在计算图中是固定存在的,完全不需要每次迭代都重新获取,把它们移到while循环外面,能省下不少不必要的CPU开销。

接下来,我从模型选型、硬件利用、代码细节、多线程实现这几个维度,给你具体的优化方案:

一、模型选型优化(最立竿见影)

你当前大概率选用了计算量偏大的模型(比如Faster RCNN系列),这类模型在CPU上运行效率极低。建议立刻替换为轻量级的SSD-MobileNet系列模型:

  • SSD MobileNet v2 320x320:在保证人体检测精度的前提下,推理速度远快于重型模型
  • SSD MobileNet V2 Lite:专为边缘设备优化,CPU/GPU环境下都有更出色的帧率表现

这些模型在TensorFlow Detection Model Zoo里都能找到对应的冻结推理图,直接替换即可。

二、充分利用GPU硬件加速

你有GTX960Ti,但从CPU高占用率来看,TensorFlow可能没用到GPU。请按以下步骤排查:

  1. 确认安装的是GPU版本的TensorFlow(不是仅支持CPU的版本)
  2. 在代码开头添加GPU检测代码,验证是否成功识别显卡:
import tensorflow as tf
print(tf.config.list_physical_devices('GPU'))

如果输出为空,需要检查CUDA和cuDNN的版本是否与你的TensorFlow版本匹配(比如TF2.x通常对应CUDA11.x系列)。

另外,OpenCV读取IP摄像头时,启用硬件解码能大幅降低CPU占用:

# 替换原有的摄像头初始化代码
cap = cv2.VideoCapture("你的IP摄像头URL", cv2.CAP_FFMPEG)
# 强制使用硬件解码器(适配NVIDIA显卡的nvdec)
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('H', '2', '6', '4'))

三、代码基础优化

除了把张量获取移到循环外,还有这些细节能提升性能:

  1. 缩小输入图像尺寸:将原始帧resize到模型要求的输入尺寸(比如320x320)再传入模型,能大幅降低计算量:
# 循环外提前定义目标尺寸
input_size = (320, 320)
# 循环内预处理
frame_resized = cv2.resize(frame, input_size)
frame_expanded = np.expand_dims(frame_resized, axis=0)
  1. 优化可视化环节vis_util.visualize_boxes_and_labels_on_image_array是CPU密集型操作,可做如下优化:
    • 只在检测到人体(class=1)且置信度超过阈值时才绘制框
    • 降低字体大小、框的粗细等参数,减少渲染开销
  2. 减少循环阻塞:把cv2.waitKey(25)改成cv2.waitKey(1),降低循环等待时间,提升帧率。

四、多线程实现方案(支持多摄像头)

多线程的核心是把摄像头帧抓取模型推理解耦,避免单线程中等待帧读取或推理阻塞整个流程。这里给你一个基于threadingqueue的实现框架:

1. 摄像头读取线程

每个摄像头启动独立线程,持续抓取帧并放入队列:

import threading
import queue

def camera_worker(cap, frame_queue, input_size):
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        # 预处理后放入队列(原始帧用于显示,resize后用于推理)
        frame_resized = cv2.resize(frame, input_size)
        frame_queue.put((frame, frame_resized))
        cv2.waitKey(1)  # 控制抓取频率,避免队列积压

2. 推理线程

单独线程从队列取帧,执行模型推理和可视化:

def inference_worker(frame_queue, sess, input_tensor, output_tensors, category_index):
    boxes_tensor, scores_tensor, classes_tensor, num_detections_tensor = output_tensors
    while True:
        if not frame_queue.empty():
            original_frame, resized_frame = frame_queue.get()
            frame_expanded = np.expand_dims(resized_frame, axis=0)
            # 执行推理
            boxes, scores, classes, num_detections = sess.run(
                [boxes_tensor, scores_tensor, classes_tensor, num_detections_tensor],
                feed_dict={input_tensor: frame_expanded}
            )
            # 只绘制人体检测结果(class=1)
            vis_util.visualize_boxes_and_labels_on_image_array(
                original_frame,
                np.squeeze(boxes),
                np.squeeze(classes).astype(np.int32),
                np.squeeze(scores),
                category_index,
                use_normalized_coordinates=True,
                line_thickness=2,
                min_score_thresh=0.5,
                category_filter={1}  # 过滤非人体类别
            )
            cv2.imshow("Multi-Camera Feed", original_frame)
            if cv2.waitKey(1) & 0xFF == ord("q"):
                break
        frame_queue.task_done()

3. 主函数整合

if __name__ == "__main__":
    # 初始化模型和会话
    with detection_graph.as_default():
        with tf.Session(graph=detection_graph) as sess:
            # 提前获取所有张量(关键优化!)
            input_tensor = detection_graph.get_tensor_by_name("image_tensor:0")
            output_tensors = (
                detection_graph.get_tensor_by_name("detection_boxes:0"),
                detection_graph.get_tensor_by_name("detection_scores:0"),
                detection_graph.get_tensor_by_name("detection_classes:0"),
                detection_graph.get_tensor_by_name("num_detections:0")
            )
            input_size = (320, 320)
            category_index = {1: {'id': 1, 'name': 'person'}}  # 仅关注人体

            # 初始化摄像头和队列
            cap1 = cv2.VideoCapture("摄像头1URL", cv2.CAP_FFMPEG)
            cap2 = cv2.VideoCapture("摄像头2URL", cv2.CAP_FFMPEG)
            frame_queue = queue.Queue(maxsize=10)  # 限制队列大小,避免内存溢出

            # 启动线程
            cam_thread1 = threading.Thread(target=camera_worker, args=(cap1, frame_queue, input_size))
            cam_thread2 = threading.Thread(target=camera_worker, args=(cap2, frame_queue, input_size))
            inf_thread = threading.Thread(target=inference_worker, args=(frame_queue, sess, input_tensor, output_tensors, category_index))

            # 设置为守护线程,主进程退出时自动结束
            cam_thread1.daemon = True
            cam_thread2.daemon = True
            inf_thread.daemon = True

            cam_thread1.start()
            cam_thread2.start()
            inf_thread.start()

            # 等待线程结束
            cam_thread1.join()
            cam_thread2.join()
            inf_thread.join()

            # 清理资源
            cap1.release()
            cap2.release()
            cv2.destroyAllWindows()

额外建议

  • 如果使用TensorFlow 2.x,建议迁移到SavedModel格式,用tf.saved_model.load加载模型,并结合tf.function加速推理,性能会比TF1.x的Session模式更好。
  • 可以尝试用TensorRT对模型进行优化,针对你的GTX960Ti生成专用推理引擎,进一步提升GPU推理速度。

内容的提问来源于stack exchange,提问作者simonEE

火山引擎 最新活动