【小ネタ】[OpenCV] 動画表示の時に画面にFPSとフレーム数を表示するクラスを作ってみました

2020.07.18

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

CX事業本部の平内(SIN)です。

機械学習によって動画を処理する場合、推論等による処理時間で、フレームレートが下がってしまう場合がありま。このような時、1つの指標になるのがFPSです。

要件に適応出来ているかどうかは、FPSはいくら出ているかで表現できます。

そして、よく見られるのが下のようなものです。画面の隅に、フレーム数とFPSが表示されています。

今回は、このような表示のためのクラスを作ってみました。コードの内容は、ネット上で広く利用されているものと同じです。

2 コード

FPS表示を担う部分を、DispFpsクラスとしました。

コンストラクタで、表示の諸元(色、フォント、サイズなど)を定義しています。

dispFps.py

from timeit import default_timer as timer
import cv2

class DispFps():
    def __init__(self):
        # 表示関連定義
        self.__width = 80
        self.__height = 20
        self.__font_size = 0.4
        self.__font_width = 1
        self.__font_style = cv2.FONT_HERSHEY_COMPLEX
        self.__font_color = (255, 255, 255)
        self.__background_color = (0, 0, 0)

        # フレーム数カウント用変数
        self.__frame_count = 0

        # FPS計算用変数
        self.__accum_time = 0
        self.__curr_fps = 0
        self.__prev_time = timer()
        self.__str = "FPS: "
    
    def __calc(self):
        # フレーム数更新
        self.__frame_count += 1

        # FPS更新
        self.__curr_time = timer()
        self.__exec_time = self.__curr_time - self.__prev_time
        self.__prev_time = self.__curr_time
        self.__accum_time = self.__accum_time + self.__exec_time
        self.__curr_fps = self.__curr_fps + 1
        if self.__accum_time > 1:
            self.__accum_time = self.__accum_time - 1
            self.__str = "FPS: " + str(self.__curr_fps)
            self.__curr_fps = 0

    def __disp(self, frame, str, x1, y1, x2, y2):
        cv2.rectangle(frame, (x1, y1), (x2, y2), self.__background_color, -1)
        cv2.putText(frame, str, (x1 + 5, y2 - 5), self.__font_style, self.__font_size, self.__font_color, self.__font_width)

    def disp(self, frame):
        # 表示内容計算
        self.__calc()
        # フレーム数(左上に表示する)
        self.__disp(frame, str(self.__frame_count), 0, 0, x2 = self.__width, y2 = self.__height)
        # FPS(右上に表示する)
        screen_width = int(frame.shape[1])
        self.__disp(frame, self.__str, screen_width - self.__width, 0, screen_width, self.__height)

上記のクラスを利用している例です。

動画処理するループに入る前に、DispFpsのインスタンスを生成し、ループの中で、disp()を呼ぶだけです。

なお、ループの中で呼ばれているtime.sleep(0.8)は、推論処理などを模擬したダミーのウエイト処理です。

index.py

import cv2
import time
from dispFps import DispFps

# Webカメラ
DEVICE_ID = 0 

WIDTH = 800
HEIGHT = 600
FPS = 24

def main():
    cap = cv2.VideoCapture (DEVICE_ID)

    # フォーマット・解像度・FPSの設定
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, WIDTH)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, HEIGHT)
    cap.set(cv2.CAP_PROP_FPS, FPS)

    # フォーマット・解像度・FPSの取得
    width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
    fps = cap.get(cv2.CAP_PROP_FPS)
    print("fps:{} width:{} height:{}".format(fps, width, height))

    # [DispFps]インスタンス生成
    dispFps = DispFps()

    while True:
        
        # カメラ画像取得
        _, frame = cap.read()
        if(frame is None):
            continue
        
        # 擬似的なウエイト
        time.sleep(0.8)
        
        # [DispFps]計算及び表示
        dispFps.disp(frame)

        # 画像表示
        cv2.imshow('frame', frame)
        
        # 'q'をタイプされたらループから抜ける
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break
                
    # VideoCaptureオブジェクト破棄
    cap.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
	main()

3 実行結果

time.sleep()に与えるウエイト値を変えてみた場合の、FPS表示は、下記のようになりました。

実装上、1FPS以上遅い場合は、1となってしまうことにご注意下さい。

ウエイト FPS
0.01 24
0.05 12〜13
0.08 9〜10
0.1 6〜7
0.15 6
0.2 4
0.5 2
0.8 1
1.0 1
2.0 1

4 最後に

このクラスで軽易にFPS表示出来るようになれば、作業が捗るかも知れません・・・