【OpenCV】覚えておくと得した気がするちょっとしたこと:cv2.WaitKey()


今まで何気にルーチンワークになってた処理が、ちょっとした工夫ですごく使いやすくなって目から鱗、みたいなことが時々あります。都度、覚え書きにしておこうと思い立ちまして。

OpenCV のウィンドウ トラックバーの動きをスムーズに

画像処理やってて、パラメータをウィンドウに付けたトラックバーで動かしながらリアルタイムに挙動を見る、というのはよくやります。この時、画像処理が重たいとトラックバーの動きがぎこちなくなって、ストレスを感じます。ちょっと工夫して改善してみました。

実行環境

2020年12月現在、一般的な環境と言っていいと思います。
Python 3.7.4
OpenCV 4.4.0
MacOS 11.0.1

トラックバー移動の遅さにストレスを感じる例

何でもいいんですが、重い画像処理の例として、Lookup Table を使わずに、forループでガンマ補正をかけるっていうのをやってみます。素材は lena.png で。
実際やらないですが、あくまで分かりやすい例として・・・

まず、画像をグレイスケールで読み込み、それを表示するためのウィンドウを作ります。

import cv2

def nothing(x):
    pass

img_gray = cv2.imread('lena.png',0)

cv2.namedWindow('lena gamma',cv2.WINDOW_NORMAL)
cv2.createTrackbar('gamma','lena gamma',200,300,nothing)

createTrackbar の引数で指定するコールバック関数をnothingと定義してます。何もしません。

次に、トラックバーで指定したガンマの値で画像にガンマ補正をかけます。

while(cv2.getWindowProperty('lena gamma',cv2.WND_PROP_VISIBLE)>0):
    gamma = cv2.getTrackbarPos('gamma','lena gamma') / 100
    if gamma == 0:
        gamma = 1/100
    img_gamma = img_gray.copy()

    for i in range(img_gamma.shape[0]):
        for j in range(img_gamma.shape[1]):
            img_gamma[i,j] = int(255 * (pow(img_gamma[i,j]/255,1/gamma)))

せっかくの numpy の高速さが台無しな感じがよく出てます。
gamma の値は、見た目をよくするため、トラックバーで指定するのは100倍値としました。なので、getTrackgbarPos で取ってきた際に、100分の1 にしてます。

そして、結果を表示します。while ループの続きですね。

    cv2.imshow('lena gamma',img_gamma)
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break

cv2.destroyAllWindows()

同じディレクトリに lena.png を置いて実行すると、こんな感じのウィンドウが出てきます。

下の方のトラックバーでガンマの値を 0.01 〜 3.00 の範囲で指定できて、リアルタイムで画像が更新されるわけですね。
ただ、画像処理が重すぎて、トラックバーがなかなかうまく動いてくれません。精神的イライラ感がつのり、使えたもんじゃないです・・・

ちょっとした改善策

この例では、根本的な改善は、ルックアップテーブルなど使えば簡単なんですが、場合によってはアルゴリズムの高速化が難しい場合だってあります。そんな時の折衷案的な改善策です。

この例では、

key = cv2.waitKey(1) & 0xFF

と、whileループを高速回転させてますが、これを低速回転にすれば、単位時間あたりの画像処理の回数が減って CPU がトラックバーの移動にさく余力ができるようです。

例えば、

key = cv2.waitKey(200) & 0xFF

としてあげると、まぁ許せる程度にはトラックバーがスムーズに動いてくれるようになりました。

  • ただし、画像の更新頻度は 0.001秒 から 0.2秒に大幅ダウンですね。でも、人間の感覚では十分許容できる範囲だと思います。

まとめ

OpenCV でウィンドウのトラックバーの動きが鈍いとき、とりあえず簡易に その場しのぎする方法でした。処理にリアルタイム性が厳しく求められる場合は使えませんが、現実解としてはありじゃあないかと思います。