在 OpenCV 中优化 RTSP 视频处理:克服 FPS 差异和缓冲问题

在 OpenCV 中进行视频处理时,开发人员经常会遇到与保持帧速率一致和处理缓冲问题有关的难题。无论是处理本地视频文件,还是处理来自 RTSP 流等远程源的流媒体,确保流畅播放和准确分析都至关重要。在本篇文章中,我们将探讨 OpenCV 中的 VideoCapture() 函数所面临的常见问题,并讨论克服这些问题的有效策略。

了解问题所在

OpenCV 中的 VideoCapture() 函数提供了一种访问视频流的便捷方法,但在某些情况下可能会导致意外行为。回放本地视频时,该函数可能会尝试以比视频原始帧速率更快的速率检索帧,从而产生快进效果。另一方面,在处理 RTSP 流时,缓冲会导致帧累积,从而导致帧分析延迟。

将视频播放与原始 FPS 同步(对于本地视频):

在对本地视频文件使用 VideoCapture() 函数时,由于 FPS 存在差异,通常会出现快进效果。为了缓解这一问题,我们引入了一个延迟,使帧获取与视频的原始 FPS 同步。这将确保更流畅、更准确的播放体验。

import cv2
import time

path = r"path_of_video"

def main():
    # Open the video file
    vs = cv2.VideoCapture(path)  
    start_time2 = time.time()
    # Check if the video file opened successfully
    if not vs.isOpened():
        print("Error: Unable to open video file")
        return

    # Set desired frame rate (e.g., 15 fps)
    frame_rate =35
    fps=0
    # Get the frame rate (FPS) of the video
    fpso = vs.get(cv2.CAP_PROP_FPS)
    print(fpso)
    #vs.set(cv2.CAP_PROP_BUFFERSIZE, 0)
    # Create a named window
    cv2.namedWindow("frame", cv2.WINDOW_NORMAL)
    while True:
        # Grab a frame at a time
        ret, frame = vs.read()
        start_time = time.time()
        if not ret:
            break  # Break the loop if there are no more frames

        # Resize and display the frame on the screen
        frame = cv2.resize(frame, (800, 800))

        # Calculate FPS if loopTime is non-zero
        loopTime = time.time() - start_time

        # Adjust frame rate based on processing time
        delay = max(1, int((1 / frame_rate - loopTime) * 1000))
        key = cv2.waitKey(delay) & 0xFF

        # Wait for the user to hit 'q' for quit
        if key == ord('q'):
            break
        
        # Calculate FPS if loopTime is non-zero
        loopTime2 = time.time() - start_time
        if loopTime2 > 0:
            fps = 0.9*fps+.1 / loopTime2
            print("FPS:", fps)
        
        # Display FPS on the frame
        cv2.putText(frame, f"FPS: {fps}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2, cv2.LINE_AA)
  
        # Display the frame
        cv2.imshow("frame", frame)
    # Clean up and we're outta here.

    etime=time.time()-start_time2
    print(etime)
    cv2.destroyAllWindows()
    vs.release()

if __name__ == "__main__":
    main()

解释:

  • 设置帧速率: 我们设置所需的帧速率(frame_rate)来控制视频播放速度。该值决定了每秒应显示多少帧。
  • 打开视频文件:我们使用 cv2.VideoCapture() 打开路径指定的视频文件。
  • 循环播放帧: 我们使用 vs.read() 循环播放视频帧。如果 ret 为 False,则表示已没有剩余帧,我们将跳出循环。
  • 调整帧的大小: 我们会调整每个帧的大小,确保其适合显示窗口。在此,我们将帧的大小调整为 800×800 像素的分辨率。
  • 调整帧速率: 我们计算处理每个帧所需的时间(loopTime),并调整帧与帧之间的延迟,使其与所需帧速率(frame_rate)同步。这可确保以指定帧频流畅播放。
  • 显示 FPS:我们根据处理每帧所需的时间(loopTime2)计算每秒帧速(FPS)。我们使用 cv2.putText() 在每一帧上显示当前的 FPS。
  • 退出程序: 我们等待用户按 “q “键退出程序。一旦循环退出,我们将释放视频捕获并关闭显示窗口,从而清理资源。

RTSP 流的实时处理:

在处理 RTSP 流时,缓冲会导致帧分析延迟,尤其是当处理速度低于帧捕获速度时。为了克服这一问题,我们采用了一种多线程方法,在不同的线程中进行帧捕获和处理。我们维护一个最新帧队列,只处理最近的帧,从而确保实时分析而不会出现缓冲问题。

import cv2
import time
import threading
import queue
import cv2, queue, threading, time

# bufferless VideoCapture
class VideoCapture:
    def __init__(self, name):
        self.cap = cv2.VideoCapture(name)
        self.q = queue.Queue()
        self.lock = threading.Lock()
        self.running = True  # Flag to indicate if the thread should keep running
        self.t = threading.Thread(target=self._reader)
        self.t.daemon = True
        self.t.start()

    def _reader(self):
        while self.running:
            ret, frame = self.cap.read()
            if not ret:
                break
            if not self.q.empty():
                try:
                    self.q.get_nowait()   # Discard previous frame
                except queue.Empty:
                    pass
            self.q.put(frame)
            self.state=ret

    def read(self):
        return self.q.get(),self.state

    def stop(self):
        self.running = False
        self.t.join()  # Wait for the thread to exit


def main():
    vs = VideoCapture("rtsp://rtspstream:***************.rtsp.stream/pattern")
    start_time2 = time.time()
    frame_rate = 35
    fps = 0
    cv2.namedWindow("Live Stream", cv2.WINDOW_NORMAL)
    
    while True:
        frame,success = vs.read()
        start_time = time.time()
        if not success:
            break

        loop_time = time.time() - start_time
        delay = max(1, int((1 / frame_rate - loop_time) * 1000))
        key = cv2.waitKey(delay) & 0xFF

        if key == ord('q'):
            break

        loop_time2 = time.time() - start_time
        if loop_time2 > 0:
            fps = 0.9 * fps + 0.1 / loop_time2
            print(fps)
        cv2.putText(frame, f"FPS: {fps:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 0), 2, cv2.LINE_AA)
        cv2.imshow("Live Stream", frame)
    
    total_time = time.time() - start_time2
    print("Total time taken:", total_time, "seconds")

    cv2.destroyAllWindows()
    vs.cap.release()

if __name__ == "__main__":
    main()

解释:

  • VideoCapture 类:该类表示一个无缓冲区的 VideoCapture 对象。它初始化一个 VideoCapture 对象,启动一个线程从视频流中连续读取帧,并将帧存储在一个队列中。
  • __init__方法:使用指定的视频流 URL(名称)初始化 VideoCapture 对象。它创建一个队列 (q) 来存储帧,并启动一个线程 (t) 从视频流中连续读取帧。
  • _reader 方法:该方法由线程 (t) 执行。它从 VideoCapture 对象 (cap) 中连续读取帧并将其放入队列 (q)。
  • read 方法:从队列 (q) 中读取帧。该方法由主程序调用,用于从无缓冲区的 VideoCapture 对象中读取帧。
  • stop 方法:停止从视频流中读取帧并终止线程 (t)。它将运行标志设置为 False,并等待线程退出。
  • 主函数(main): 使用 RTSP 流 URL 初始化无缓冲区 VideoCapture 对象,并创建一个窗口用于显示实时流。程序会不断从 VideoCapture 对象中读取帧,调整帧间延迟以与所需帧频同步,并在窗口中显示帧。当用户按下 “q “键时,程序终止。
在 OpenCV 中优化 RTSP 视频处理:克服 FPS 差异和缓冲问题
帧处理流程

结论

在本实现中,我们成功创建了一个无缓冲的 VideoCapture 类,可从 RTSP 流中实时检索帧,适用于需要低延迟视频处理的应用。通过利用多线程和队列数据结构,可以从视频流中连续读取帧而无需缓冲,从而确保将帧获取和处理之间的延迟降至最低。

无缓冲 VideoCapture 类为视频监控、物体检测和实时流媒体等实时应用提供了显著优势。其高效的设计允许无缝集成到需要立即访问视频流的项目中,而不会牺牲性能。

此外,该实施方案还演示了如何使帧检索与所需帧频同步,从而促进流畅一致的视频播放。通过动态调整帧间延迟,该应用程序在优化帧处理的同时,还能保持用户定义的帧频以便显示。

总之,无缓冲 VideoCapture 类为实时视频处理应用提供了一个强大的解决方案,为开发人员提供了一个多功能工具,以最小的延迟和最佳的性能捕获和处理视频流。这种方法灵活高效,为各种领域的实时视频应用开辟了新的可能性。

作者:Vikas C

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/47826.html

(0)

相关推荐

发表回复

登录后才能评论