在 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 “键时,程序终止。
结论
在本实现中,我们成功创建了一个无缓冲的 VideoCapture 类,可从 RTSP 流中实时检索帧,适用于需要低延迟视频处理的应用。通过利用多线程和队列数据结构,可以从视频流中连续读取帧而无需缓冲,从而确保将帧获取和处理之间的延迟降至最低。
无缓冲 VideoCapture 类为视频监控、物体检测和实时流媒体等实时应用提供了显著优势。其高效的设计允许无缝集成到需要立即访问视频流的项目中,而不会牺牲性能。
此外,该实施方案还演示了如何使帧检索与所需帧频同步,从而促进流畅一致的视频播放。通过动态调整帧间延迟,该应用程序在优化帧处理的同时,还能保持用户定义的帧频以便显示。
总之,无缓冲 VideoCapture 类为实时视频处理应用提供了一个强大的解决方案,为开发人员提供了一个多功能工具,以最小的延迟和最佳的性能捕获和处理视频流。这种方法灵活高效,为各种领域的实时视频应用开辟了新的可能性。
作者:Vikas C
本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/47826.html