Tello无人机ROS项目用cv2.CascadeClassifier做实时人脸检测时延迟问题求解
核心问题与优化方案
首要问题
当前代码最大的性能瓶颈是每处理一帧都重新加载一次人脸检测模型:cv2.CascadeClassifier的初始化操作被放在了帧解码循环内部,每次运行都要从磁盘读取模型文件、加载到内存,单帧开销直接增加几十毫秒,处理速度远低于Tello的视频帧率(默认30fps),导致帧队列持续积压,最终产生几十秒的延迟。
其他优化点
- 限制帧队列最大长度:当前
StandaloneVideoStream的队列无长度上限,处理速度跟不上接收速度时旧帧会持续积压,设置队列最大长度为2~3,队列满时直接丢弃最旧的帧,保证永远处理最新的视频数据。 - 降低检测计算量:Haar级联人脸检测不需要色彩信息,也不需要全720P分辨率输入。可将原图缩放到1/2大小、转灰度图后再做检测,得到的坐标等比放大回原尺寸画框即可,检测速度可提升4倍以上。
- 优化解码流程:直接使用PyAV接口将帧转换为BGR格式的numpy数组,省掉RGB转BGR的额外开销。
- 调整检测参数:适当调大
detectMultiScale的scaleFactor参数(如设为1.2)、调小minNeighbors参数(如设为3),减少检测的计算量。
优化后参考代码
#!/usr/bin/env python import rospy from sensor_msgs.msg import CompressedImage import av import cv2 import numpy import threading import traceback class StandaloneVideoStream(object): def __init__(self, max_queue_size=3): self.cond = threading.Condition() self.queue = [] self.max_queue_size = max_queue_size self.closed = False def read(self, size): self.cond.acquire() try: if len(self.queue) == 0 and not self.closed: self.cond.wait(2.0) data = bytes() while 0 < len(self.queue) and len(data) + len(self.queue[0]) < size: data = data + self.queue[0] del self.queue[0] finally: self.cond.release() return data def seek(self, offset, whence): return -1 def close(self): self.cond.acquire() self.queue = [] self.closed = True self.cond.notifyAll() self.cond.release() def add_frame(self, buf): self.cond.acquire() # 队列满时丢弃最旧的帧 if len(self.queue) >= self.max_queue_size: self.queue.pop(0) self.queue.append(buf) self.cond.notifyAll() self.cond.release() stream = StandaloneVideoStream() def callback(msg): stream.add_frame(msg.data) def main(): rospy.init_node('face_detection') rospy.Subscriber('/tello/image_raw/h264', CompressedImage, callback) # 全局仅加载一次人脸检测模型 face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml') # 开启低延迟解码配置 container = av.open(stream, options={'fflags': 'nobuffer', 'flags': 'low_delay'}) video_stream = container.streams.video[0] video_stream.thread_type = "AUTO" for frame in container.decode(video=0): # 直接转BGR格式,省掉额外颜色转换开销 img = frame.to_ndarray(format='bgr24') # 缩小+转灰度做检测,大幅提升处理速度 gray = cv2.cvtColor(cv2.resize(img, (480, 360)), cv2.COLOR_BGR2GRAY) faces = face_cascade.detectMultiScale(gray, scaleFactor=1.2, minNeighbors=3, minSize=(10, 10)) # 坐标放大回原尺寸画框 for (x, y, w, h) in faces: cv2.rectangle(img, (x*2, y*2), ((x+w)*2, (y+h)*2), (0, 255, 0), 2) cv2.imshow('Frame', img) cv2.waitKey(1) if __name__ == '__main__': try: main() except BaseException: traceback.print_exc() finally: stream.close() cv2.destroyAllWindows()
内容的提问来源于stack exchange,提问作者cv576
相关产品推荐
相关产品推荐

