检测摄像头的fps

需求

项目中经常遇到不是摄像头就是网线的问题,曾经遇到一个项目算法日志一直报 warning,经过好几个小时的远程排查,发现是摄像头的 fps 不稳定,而且出现 fps 逐渐降低的情况,所以算法跑着跑着就挂了。

于是就需要开发一个测试 fps 的工具,工具倒是不复杂,主要依赖 opencv-python 库读取摄像头视频流。

代码

最基础版本

import argparse
import sys
import time

import cv2

parser = argparse.ArgumentParser()
ip = parser.add_argument("-i", "--ip", type=str)
args = parser.parse_args()

frame_id = 0

cap = cv2.VideoCapture(f"rtsp://username:password@{args.ip}:554/Streaming/Channels/101")
while True:
    try:
        ret, frame = cap.read()
        if not ret:
            break
        else:
            frame_id += 1

            if frame_id < 100:
                remainder = frame_id % 5
                if remainder == 0:
                    print(f"wait:{'-' * (frame_id // 5)}>", end="\r")
            if frame_id == 100:
                time_start = time.time()

            if frame_id > 100:
                time_end = time.time()
                print(
                    f"fps: {(frame_id - 100) / (time_end - time_start)}",
                    end="\r",
                )
    except KeyboardInterrupt:
        exit(-1)

\r 是回车符,光标会回到行首。但是只有一串数字输出,所以经过我的改良有了下面的一版:
改良版本

import argparse
import atexit
import sys
import time
from collections import OrderedDict
from multiprocessing import Event, Process, Queue
from typing import Any, List

import cv2
import keyboard

parser = argparse.ArgumentParser()
ip = parser.add_argument("-i", "--ip", type=str)
args = parser.parse_args()
alive_set = Event()

exit_msg = "\nAll processes have exited."


def test_fps(args: Any, event: Event, fps_q: Queue, name: str):
    frame_id = 0

    cap = cv2.VideoCapture(
        f"rtsp://username:password@{args.ip}:554/Streaming/Channels/101"
    )
    string = ""
    current_time = time.time()
    if cap.isOpened():
        print(f"Enter `q` to exit...\n{'-' * 40}")
    else:
        print("The camera is not opened yet.")
        exit(0)

    while True:
        try:
            ret, _ = cap.read()
            if not ret:
                break
            else:
                frame_id += 1

                if frame_id < 100:
                    remainder = frame_id % 5
                    first_100_time = time.time()

                    if remainder == 0 and current_time < first_100_time:
                        string = (
                            f"wait: {(first_100_time - current_time) * 1000:.4f} ms"
                        )
                if frame_id == 100:
                    time_start = time.time()
                    event.set()
                    string = ""

                if frame_id > 100:
                    time_end = time.time()
                    string = f"{(frame_id - 100) / (time_end - time_start):.8f}"

            fps_q.put([name, string])

        except KeyboardInterrupt:
            exit(0)


def test_time(time_start: float, event: Event, time_q: Queue, name: str):
    while True:
        try:
            if event.is_set():
                try:
                    time_now = time.time()
                    cost = f"{time_now - time_start:.3f}s."
                    time_q.put([name, cost])
                except (KeyboardInterrupt, Exception) as e:
                    exit(0)
        except (KeyboardInterrupt, Exception) as e:
            exit(0)


def print_progress(progress: "OrderedDict"):
    # sys.stdout.write("\033[2J\033[H")  # clear screen
    out_list = []
    for name, data in progress.items():
        if data:
            out_list.append(f"{name}: {data}")

    string = ""
    if len(out_list) == 1:
        string = "\r" + out_list[0].replace("fps:", "").strip()

    elif len(out_list) > 1:
        string = "\r" + " | ".join(out_list)
    else:
        string = "\r" + "waiting..."

    sys.stdout.write(string)
    sys.stdout.flush()


def kill_pid(processes: List[Process]):
    try:
        for p in processes:
            p.terminate()

    except Exception as e:
        sys.stderr.write(f"Detailed error: {e}")
        exit(0)


def main():
    print("Test the fps of camera...")
    status = Queue(maxsize=2)
    progress = OrderedDict()
    processes = []

    try:
        time_start = time.time()
        tasks = [
            Process(target=test_fps, args=(args, alive_set, status, "fps"), name="fps"),
            Process(
                target=test_time,
                args=(time_start, alive_set, status, "time"),
                name="time",
            ),
        ]

        for t in tasks:
            t.start()
            processes.append(t)
            progress[t.name] = None

        while any(i.is_alive() for i in tasks):
            while not status.empty():
                try:
                    name, data = status.get()
                    progress[name] = data
                    print_progress(progress)

                    keyboard.add_hotkey("q", kill_pid, args=(processes,))

                except KeyboardInterrupt:
                    break

    except KeyboardInterrupt:
        atexit.register(kill_pid, *(processes,))
        print(exit_msg)
        exit(0)


if __name__ == "__main__":
    main()
    print(exit_msg)

改良版使用了 多进程队列 通信和 Event 以及 keyboard 库,支持动态单行局部字符串一直刷新和 q 键退出代码的功能。

结果

$ python.exe .\test_fps.py -i 192.168.0.67
Test the fps of camera...
Enter `q` to exit...
----------------------------------------
fps: 20.03542377 | time: 43.861s.
All processes have exited.

下面有动图:

最后推荐一个好用的windows下的录屏工具,上面的gif就是这个工具录制的。ScreenToGif. 再次感叹开源的伟大。

你可能感兴趣的:(opencv,python,opencv,python)