コードの評価、最適化に役立つ処理時間計測方法

※当サイトではアフィリエイト広告を利用しています

Python プログラミング 画像処理

【Python・OpenCV】コードの評価、最適化に役立つ処理時間計測方法

2023-03-14

はじめに

画像処理のプログラムは、場合によって処理に時間がかかることがあります。
その処理時間を正確に計測することで、処理の最適化や異なるアルゴリズムの比較などが可能になるため、時間の計測を知っておくととても便利です。
この記事では、OpenCVで用意されている時間計測の機能について紹介します。

cv2.getTickCount関数とcv2.getTickFrequency関数

getTickCount関数とgetTickFrequency関数を使用した時間計測の方法を紹介します。
以下に例を示します。

'''
getTickCount(), getTickFrequency()を使った時間の計測
'''

import cv2

start_time = cv2.getTickCount()

# ----- 時間計測したい処理(ここから) -----
# 画像を読み込む
image = cv2.imread('image.jpg')
# 画像のサイズを取得
h, w = image.shape[:2]
# 画像サイズを変更
result = cv2.resize(image, (w * 2, h))
# ----- 時間計測したい処理(ここまで) -----

end_time = cv2.getTickCount()
elapsed_time = (end_time - start_time) / cv2.getTickFrequency()
print("Elapsed time:", elapsed_time, "seconds")

上のコードでは時間の掛かる処理として画像ファイルの読み込み、画像サイズの変更を行っています。
これらの方法についてはこちら。

getTickCount関数を使用して処理を開始する前(7行目)と終了した後(18行目)の時間を取得し、それらの差分から処理時間を計算しています。
OpenCVのgetTickCount()関数は、内部的にシステム時計のチック数を返します。getTickFrequency()関数は、1秒あたりのチック数を返します(19行目)。これらの関数を使用することで、処理時間をチック数で計測し、それを秒単位の時間に変換することができます。
計測された時間はgetTickFrequency関数によって取得するチック数あたりの秒数で割って正確な時間に変換されます。
最後に、計測された時間を秒単位で表示しています。

「チック数」とは時刻の単位を表すカウント数のことです。
チック数はコンピュータの内部時計やタイマーが一定時間ごとに発行する信号の回数を表し、その時間単位はプラットフォームによって異なります。

TickMeterクラス

時間計測機能

OpenCVのTickMeterクラスは、時間計測用のクラスです。TickMeterオブジェクトを使用することで、処理の開始時点から終了時点までの時間を簡単に計測することができます。
以下はTickMeterクラスを使用した時間計測の例です。

'''
TickMeterクラスを使った時間の計測
'''

import cv2

# TickMeterオブジェクトを作成
tm = cv2.TickMeter()

# 計測開始
tm.start()

# ----- 時間計測したい処理(ここから) -----
# 画像を読み込む
image = cv2.imread('image.jpg')
# 画像のサイズを取得
h, w = image.shape[:2]
# 画像サイズを変更
result = cv2.resize(image, (w * 2, h))
# ----- 時間計測したい処理(ここまで) -----

# 計測終了
tm.stop()

elapsed_time = tm.getTimeMicro()
print("Elapsed time:", elapsed_time, "micro seconds")

elapsed_time = tm.getTimeMilli()
print("Elapsed time:", elapsed_time, "milli seconds")

elapsed_time = tm.getTimeSec()
print("Elapsed time:", elapsed_time, "seconds")

elapsed_time = tm.getTimeTicks()
print("Elapsed time:", elapsed_time, "ticks")

TickMeterオブジェクトを作成(8行目)し、start()メソッドを呼び出し計測を開始します(11行目)。
計測を終了するには、stop()メソッドを呼び出します(23行目)。
getTimeMicro()メソッドを使用(25行目)することで、計測した時間を秒単位で取得することができます。

時間の取得はgetTimeMicro()メソッドを含め下に示す4種類があります。

  • getTimeMicro():マイクロ秒で計測時間を取得
  • getTimeMilli():ミリ秒で計測時間を取得
  • getTimeSec():秒で計測時間を取得
  • getTimeTicks():チック数で計測時間を取得

TickMeterクラスには平均時間を計算するメソッドも用意されています。
以下はTickMeterクラスを使用した平均時間計測の例です。

'''
TickMeterクラスを使った1000回の平均時間の計測
'''

import cv2

# TickMeterオブジェクトを作成
tm = cv2.TickMeter()

# ----- 時間計測したい処理(ここから) -----
for num in range(1000):
    # 計測開始
    tm.start()
    # 画像を読み込む
    image = cv2.imread('image.jpg')
    # 画像のサイズを取得
    h, w = image.shape[:2]
    # 画像サイズを変更
    result = cv2.resize(image, (w * 2, h))
    # 計測終了
    tm.stop()
# ----- 時間計測したい処理(ここまで) -----

elapsed_time = tm.getAvgTimeMilli()
print("Average time:", elapsed_time, "milli seconds")

elapsed_time = tm.getAvgTimeSec()
print("Average time:", elapsed_time, "seconds")

上のコードでは時間の掛かる処理として画像ファイルの読み込み、画像サイズの変更を1000回行った平均値を計測します。
TickMeterオブジェクトを作成(8行目)し、1000回のループの中でstart()メソッドを呼び出し計測を開始します(13行目)。
計測を終了するには、同様にループの中でstop()メソッドを呼び出します(21行目)。
getAvgTimeMilli()メソッドを使用(24行目)することで、1000回計測した平均時間をミリ秒単位で取得することができます。

時間の取得はgetAvgTimeMilli()メソッドを含め下に示す2種類があります。

  • getAvgTimeMilli():ミリ秒で平均計測時間を取得
  • getAvgTimeSec():秒で平均計測時間を取得

計測結果のリセット

連続して複数回の時間計測する場合、TickMeterオブジェクトを再利用することが出来ます。
reset()メソッドはTickMeterオブジェクトの状態を計測前の状態に戻します。
つまり、計測された時間をゼロにリセットし計測が停止されている状態にします。
これにより、再びstart()メソッドを呼び出すことで、新しい計測を開始することができます。
以下にreset()メソッドを使用したコードの例を示します。

'''
TickMeterクラスのインスタンスをリセットして複数回の時間の計測を行う
'''

import cv2

# TickMeterオブジェクトを作成
tm = cv2.TickMeter()

# ----- 1回目の計測(ここから) -----
# 計測開始
tm.start()
# 画像を読み込む
image = cv2.imread('image1.jpg')
# 画像のサイズを取得
h, w = image.shape[:2]
# 画像サイズを変更
result = cv2.resize(image, (w * 2, h))
# 計測終了
tm.stop()
# ----- 1回目の計測(ここまで) -----

# 1回目の計測結果を表示
elapsed_time = tm.getTimeSec()
print("Elapsed time 1:", elapsed_time, "seconds")

# TickMeterインスタンスをリセット
tm.reset()

# ----- 2回目の計測(ここから) -----
# 計測開始
tm.start()
# 画像を読み込む
image = cv2.imread('image2.png')
# 画像のサイズを取得
h, w = image.shape[:2]
# 画像サイズを変更
result = cv2.resize(image, (w * 2, h))
# 計測終了
tm.stop()
# ----- 2回目の計測(ここまで) -----

# 2回目の計測結果を表示
elapsed_time = tm.getTimeSec()
print("Elapsed time 2:", elapsed_time, "seconds")

このコードでは、TickMeterクラスのインスタンスを2回使用して、異なる画像の読み込み処理を計測しています。
1回目の計測後に、reset()メソッドを呼び出してリセットし(28行目)、2回目の計測を行っています。

おわりに

時間計測を行うことは、アルゴリズムの高速化や処理時間の短縮において重要な役割を果たします。
getTickCount関数とgetTickFrequency関数やTickMeterクラスの時間計測メソッドの使い方を覚えておくと、役に立つ時があると思います。
特にTickMeterクラスは平均時間を計測することも容易なので、getTickCount関数とgetTickFrequency関数を使用するよりもおすすめです。
また、OpenCVの機能はmacOS, Windows, Linuxなどのプラットフォームに依存せずに同じコードで正確に時間計測が可能となります。
最後にTickMeterクラスには動画のFPS(フレームレート)を計測する機能も備わっているので、別の記事で紹介したいと思います。

■(広告)OpenCVの参考書としてどうぞ!■

参考リンク

-Python, プログラミング, 画像処理
-