読み込み中...

PythonでOpenCVのwaitkeyを効果的に使う方法と活用例12選

waitkey 徹底解説 Python
この記事は約35分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

本記事のサンプルコードを活用して機能追加、目的を達成できるように作ってありますので、是非ご活用ください。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を満たす現役のプログラマチームによって監修されています。

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

●PythonとOpenCVのwaitkeyって何?知らないと損する5つの理由

今日は、PythonとOpenCVを使う上で欠かせない関数、waitkeyについて解説します。

この関数、一見シンプルですが、使いこなすことで画像処理プログラムが劇的に進化します。

なぜwaitkeyが重要なのか、知らないと損する5つの理由を詳しく見ていきましょう。

○waitkeyの基本機能と重要性

waitkey関数は、OpenCVライブラリの中でも特に重要な役割を果たします。

キーボード入力を待ち受け、プログラムの流れを制御する機能を持っています。

単純そうに見えますが、この関数なしでは画像や動画の表示、処理の制御が難しくなってしまいます。

waitkeyの基本的な使い方は次のようになります。

import cv2

# 画像を表示
cv2.imshow('Image', img)

# キー入力を待つ
cv2.waitKey(0)

# ウィンドウを閉じる
cv2.destroyAllWindows()

このコードでは、画像を表示した後、ユーザーが何かキーを押すまでプログラムが待機状態になります。

キーが押されると、ウィンドウが閉じられてプログラムが終了します。

waitkeyの重要性は、次の5つの理由から明らかです。

  1. 画像表示の制御 -> 画像をただ表示するだけでなく、ユーザーが確認できる時間を確保できます。
  2. キー入力による対話的な操作 -> ユーザーの入力に応じて、プログラムの挙動を変更できます。
  3. 処理速度の調整 -> 動画処理やリアルタイム画像処理において、フレームレートを制御できます。
  4. メモリ管理の最適化 -> 適切なタイミングでリソースを解放し、メモリ使用量を抑えられます。
  5. マルチウィンドウ操作 -> 複数のウィンドウを開いている場合、それぞれの制御が可能になります。

○OpenCVでwaitkeyが必須な理由

OpenCVを使用する上で、waitkey関数はほぼ必須と言えるでしょう。

なぜなら、この関数がないと画像処理プログラムの動作が不安定になってしまうからです。

具体的には、次のような問題が発生する可能性があります。

  1. 画像が表示されない -> OpenCVは画像を表示する際、イベントループを必要とします。waitkeyがこの役割を果たしています。
  2. プログラムがすぐに終了 -> 画像を表示する暇もなく、プログラムが終了してしまいます。
  3. システムリソースの無駄遣い -> 適切なタイミングでリソースを解放できず、メモリリークの原因になります。
  4. ユーザーインタラクションの欠如 -> キー入力を受け付けられないため、対話的なプログラムが作れません。
  5. 動画再生の制御不能 -> フレームレートを制御できず、動画が高速で再生されてしまいます。

○waitkeyを使わないとどうなる?

waitkeyを使用しないプログラムを見てみましょう。

import cv2
import numpy as np

# 空の画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)

# 画像に円を描画
cv2.circle(img, (150, 150), 100, (0, 255, 0), -1)

# 画像を表示
cv2.imshow('Circle', img)

# ウィンドウを閉じる
cv2.destroyAllWindows()

このコードを実行すると、画面に何も表示されないまま、プログラムが即座に終了してしまいます。

ユーザーは緑色の円を見る機会すらありません。

一方、waitkeyを使用したバージョンを見てみましょう。

import cv2
import numpy as np

# 空の画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)

# 画像に円を描画
cv2.circle(img, (150, 150), 100, (0, 255, 0), -1)

# 画像を表示
cv2.imshow('Circle', img)

# キー入力を待つ
cv2.waitKey(0)

# ウィンドウを閉じる
cv2.destroyAllWindows()

このバージョンでは、緑色の円が表示され、ユーザーが任意のキーを押すまでプログラムが待機します。

ユーザーは十分に画像を確認できるでしょう。

waitkeyを使わないと、画像処理プログラムの動作が不安定になり、期待通りの結果が得られません。

プログラムの制御が難しくなり、ユーザビリティも低下します。

したがって、OpenCVを使用する際は、必ずwaitkeyを適切に組み込むことが重要です。

●waitkeyの使い方をマスター!コード例で学ぶ基本テク

さて、waitkeyの重要性が理解できたところで、実際の使い方を見ていきましょう。

基本的なテクニックを3つのサンプルコードを通じて学んでいきます。

○サンプルコード1:シンプルなwaitkeyの使用法

まずは、最もシンプルなwaitkeyの使用例から見ていきましょう。

import cv2
import numpy as np

# 画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img, "Press any key", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

# 画像を表示
cv2.imshow('Simple Waitkey Example', img)

# キー入力を待つ
key = cv2.waitKey(0)

# 押されたキーに応じて処理
if key == ord('q'):
    print("'q' key was pressed. Exiting...")
else:
    print(f"You pressed key code: {key}")

# ウィンドウを閉じる
cv2.destroyAllWindows()

このコードでは、黒い背景に白文字で「Press any key」と表示されます。

ユーザーが何かキーを押すまでプログラムは待機状態になります。’q’キーが押されると特別なメッセージが表示され、それ以外のキーではキーコードが表示されます。

実行結果

# 'q'キーを押した場合
'q' key was pressed. Exiting...

# その他のキー(例:'a'キー)を押した場合
You pressed key code: 97

○サンプルコード2:特定のキー入力を待つ方法

次に、特定のキー入力を待つ方法を見てみましょう。

import cv2
import numpy as np

# 画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img, "Press 'q' to quit", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)

# 画像を表示
cv2.imshow('Wait for Specific Key', img)

# 'q'キーが押されるまで待機
while True:
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break

print("'q' key was pressed. Exiting...")

# ウィンドウを閉じる
cv2.destroyAllWindows()

このコードでは、’q’キーが押されるまでループが継続します。’q’キーが押されると、ループを抜けてプログラムが終了します。

実行結果

'q' key was pressed. Exiting...

○サンプルコード3:タイムアウトを設定する技

最後に、waitkeyにタイムアウトを設定する方法を見てみましょう。

import cv2
import numpy as np

# 画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img, "Press key in 5 sec", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)

# 画像を表示
cv2.imshow('Waitkey with Timeout', img)

# 5秒間キー入力を待つ
key = cv2.waitKey(5000)

if key == -1:
    print("No key was pressed within 5 seconds.")
else:
    print(f"You pressed key code: {key}")

# ウィンドウを閉じる
cv2.destroyAllWindows()

このコードでは、waitkeyに5000ミリ秒(5秒)のタイムアウトを設定しています。

5秒以内にキーが押されればそのキーコードを表示し、押されなければタイムアウトのメッセージを表示します。

実行結果

# 5秒以内にキー(例:'a'キー)を押した場合
You pressed key code: 97

# 5秒間キーを押さなかった場合
No key was pressed within 5 seconds.

●waitkeyを活用した画像処理の神業テクニック7選

さあ、いよいよwaitkeyの真価を発揮する時がやってきました!

基本を押さえたあなたは、もう一歩先へ進む準備ができています。

画像処理の世界で「神業」と呼ばれるテクニックを、順を追って習得していきましょう。

まずは、画像切り替えの制御から始めます。単純そうに見えて奥が深い技です。

○サンプルコード4:画像切り替えの制御

画像を切り替える。

聞くと簡単そうですが、実際にプログラムで実装するとなると、ちょっとした工夫が必要になります。

waitkeyを使えば、ユーザーの操作に応じて画像を切り替えることができるんです。

import cv2
import numpy as np

# 2つの画像を用意
img1 = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img1, "Image 1", (100, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

img2 = np.ones((300, 300, 3), dtype=np.uint8) * 255
cv2.putText(img2, "Image 2", (100, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

# 現在の画像を追跡
current_img = img1

while True:
    cv2.imshow('Image Switch', current_img)

    # キー入力を待つ(1ミリ秒)
    key = cv2.waitKey(1) & 0xFF

    # スペースキーで画像を切り替え
    if key == 32:  # スペースキーのASCIIコード
        current_img = img2 if np.array_equal(current_img, img1) else img1

    # 'q'キーで終了
    elif key == ord('q'):
        break

cv2.destroyAllWindows()

実行すると、スペースキーを押すたびに、白地に黒文字の「Image 2」と黒地に白文字の「Image 1」が交互に表示されます。

‘q’キーを押すと、プログラムが終了します。

注目すべきは、waitkeyを使って常にキー入力をチェックしている点です。

1ミリ秒という短い間隔で待機することで、スムーズな画像切り替えを実現しています。

○サンプルコード5:動画再生の一時停止と再開

次は、動画プレイヤーの基本機能、一時停止と再生の実装です。

映画館のポップコーンを買いに行くときのように、好きなタイミングで動画を止められたら便利ですよね。

import cv2

# 動画ファイルを開く(自分の環境に合わせてパスを変更してください)
cap = cv2.VideoCapture('sample_video.mp4')

# 再生状態を管理するフラグ
playing = True

while cap.isOpened():
    if playing:
        ret, frame = cap.read()
        if not ret:
            break

        cv2.imshow('Video Player', frame)

    # キー入力を待つ(1ミリ秒)
    key = cv2.waitKey(1) & 0xFF

    # スペースキーで再生/一時停止を切り替え
    if key == 32:  # スペースキーのASCIIコード
        playing = not playing

    # 'q'キーで終了
    elif key == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

実行すると、動画が再生され、スペースキーを押すと一時停止します。

再度スペースキーを押すと再開します。’q’キーを押すと、プログラムが終了します。

waitkeyを1ミリ秒の間隔で呼び出すことで、スムーズな再生と素早い反応を実現しています。

playingフラグを使って再生状態を管理する技も覚えておきましょう。

○サンプルコード6:キー入力による描画モード切替

さて、お絵かきソフトの基本機能を作ってみましょう。

線を引いたり、円を描いたり。

キー入力で描画モードを切り替えられれば、使い勝手バツグンです。

import cv2
import numpy as np

# 描画用の空の画像を作成
drawing = np.zeros((512, 512, 3), dtype=np.uint8)

# マウスイベントのコールバック関数
def draw(event, x, y, flags, param):
    global mode

    if event == cv2.EVENT_LBUTTONDOWN:
        if mode == 'line':
            cv2.line(drawing, (x, y), (x+50, y+50), (0, 255, 0), 2)
        elif mode == 'circle':
            cv2.circle(drawing, (x, y), 30, (0, 0, 255), -1)

# ウィンドウを作成し、マウスコールバックを設定
cv2.namedWindow('Drawing')
cv2.setMouseCallback('Drawing', draw)

# 描画モード(初期値は線)
mode = 'line'

while True:
    cv2.imshow('Drawing', drawing)

    # キー入力を待つ(1ミリ秒)
    key = cv2.waitKey(1) & 0xFF

    # 'l'キーで線モード、'c'キーで円モード
    if key == ord('l'):
        mode = 'line'
        print("Line mode activated")
    elif key == ord('c'):
        mode = 'circle'
        print("Circle mode activated")

    # 'q'キーで終了
    elif key == ord('q'):
        break

cv2.destroyAllWindows()

実行すると、黒い画面が表示され、マウスをクリックすると緑の線が描画されます。

‘c’キーを押すと円モードに切り替わり、クリックすると赤い円が描画されます。’l’キーで線モードに戻ります。

‘q’キーを押すと、プログラムが終了します。

waitkeyを使ってキー入力を常時監視し、描画モードをリアルタイムで切り替えています。

こうすることで、ユーザーは途切れることなく作業を続けられます。

○サンプルコード7:複数ウィンドウの制御

複数の画像を同時に扱うことは珍しくありません。

例えば、元画像と処理後の画像を並べて表示したい場合。

waitkeyを使えば、複数ウィンドウを同時に制御できます。

import cv2
import numpy as np

# 2つの画像を用意
img1 = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img1, "Window 1", (80, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

img2 = np.ones((300, 300, 3), dtype=np.uint8) * 255
cv2.putText(img2, "Window 2", (80, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)

# ウィンドウを作成
cv2.namedWindow('Window 1')
cv2.namedWindow('Window 2')

while True:
    cv2.imshow('Window 1', img1)
    cv2.imshow('Window 2', img2)

    # キー入力を待つ(1ミリ秒)
    key = cv2.waitKey(1) & 0xFF

    # '1'キーでWindow 1を閉じる、'2'キーでWindow 2を閉じる
    if key == ord('1'):
        cv2.destroyWindow('Window 1')
        print("Window 1 closed")
    elif key == ord('2'):
        cv2.destroyWindow('Window 2')
        print("Window 2 closed")

    # 'q'キーで全てのウィンドウを閉じて終了
    elif key == ord('q'):
        break

cv2.destroyAllWindows()

実行すると、2つのウィンドウが表示されます。’1’キーを押すとWindow 1が閉じ、’2’キーを押すとWindow 2が閉じます。

‘q’キーを押すと、全てのウィンドウが閉じてプログラムが終了します。

waitkeyを使って1つのループで複数のウィンドウを制御できるのがポイントです。

複雑な画像処理アプリケーションを作る際の基礎となる技術です。

○サンプルコード8:画像フィルタのリアルタイム適用

写真加工アプリの核心部分、リアルタイムフィルタ適用です。

インスタグラムのようなアプリを作るときに役立つテクニックです。

import cv2
import numpy as np

# カメラをオープン
cap = cv2.VideoCapture(0)

# フィルタの種類
filters = ['normal', 'gray', 'blur']
current_filter = 0

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # 現在のフィルタを適用
    if filters[current_filter] == 'normal':
        filtered = frame
    elif filters[current_filter] == 'gray':
        filtered = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        filtered = cv2.cvtColor(filtered, cv2.COLOR_GRAY2BGR)
    elif filters[current_filter] == 'blur':
        filtered = cv2.GaussianBlur(frame, (15, 15), 0)

    # 現在のフィルタ名を表示
    cv2.putText(filtered, filters[current_filter], (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

    cv2.imshow('Filtered Camera', filtered)

    # キー入力を待つ(1ミリ秒)
    key = cv2.waitKey(1) & 0xFF

    # スペースキーでフィルタを切り替え
    if key == 32:  # スペースキーのASCIIコード
        current_filter = (current_filter + 1) % len(filters)
        print(f"Switched to {filters[current_filter]} filter")

    # 'q'キーで終了
    elif key == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

実行すると、カメラの映像が表示され、スペースキーを押すたびにノーマル→グレースケール→ぼかしの順でフィルタが切り替わります。

‘q’キーを押すと、プログラムが終了します。

waitkeyを使ってリアルタイムでフィルタを切り替えられるのがミソです。

ユーザーは即座に効果を確認できるので、使い勝手が格段に向上します。

○サンプルコード9:オブジェクト検出の開始/停止

最後に、少し高度な例を見てみましょう。

オブジェクト検出をリアルタイムで開始/停止する機能です。

AIを使った画像認識システムの基礎となる技術です。

import cv2
import numpy as np

# カメラをオープン
cap = cv2.VideoCapture(0)

# 顔検出器を読み込み
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

# 検出状態を管理するフラグ
detecting = False

while True:
    ret, frame = cap.read()
    if not ret:
        break

    if detecting:
        # グレースケールに変換
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 顔を検出
        faces = face_cascade.detectMultiScale(gray, 1.3, 5)

        # 検出された顔に矩形を描画
        for (x, y, w, h) in faces:
            cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)

    # 検出状態を表示
    status = "Detecting: ON" if detecting else "Detecting: OFF"
    cv2.putText(frame, status, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

    cv2.imshow('Face Detection', frame)

    # キー入力を待つ(1ミリ秒)
    key = cv2.waitKey(1) & 0xFF

    # スペースキーで検出のON/OFFを切り替え
    if key == 32:  # スペースキーのASCIIコード
        detecting = not detecting
        print("Detection turned", "ON" if detecting else "OFF")

    # 'q'キーで終了
    elif key == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

実行すると、カメラの映像が表示され、初期状態では顔検出がOFFになっています。

スペースキーを押すと顔検出がONになり、検出された顔に青い矩形が描画されます。

再度スペースキーを押すとOFFに戻ります。’q’キーを押すと、プログラムが終了します。

waitkeyを使って検出のON/OFFをリアルタイムで切り替えられるのがポイントです。

処理負荷の高い操作を必要なときだけ実行できるので、システムの効率化にも貢献します。

○サンプルコード10:カメラストリームのフレーム制御

最後に、カメラストリームのフレーム制御を見てみましょう。

高フレームレートのカメラを使用する際、処理が追いつかないことがあります。

waitkeyを使えば、フレームレートを調整できます。

import cv2
import time

# カメラをオープン
cap = cv2.VideoCapture(0)

# 目標フレームレート(FPS)
target_fps = 30
frame_interval = 1 / target_fps

while True:
    start_time = time.time()

    ret, frame = cap.read()
    if not ret:
        break

    # フレームを処理(ここでは単純に表示するだけ)
    cv2.imshow('Controlled FPS Camera', frame)

    # 経過時間を計算
    elapsed_time = time.time() - start_time

    # 待機時間を計算(負の場合は0に)
    wait_time = max(int((frame_interval - elapsed_time) * 1000), 1)

    # キー入力を待つ(計算された待機時間)
    key = cv2.waitKey(wait_time) & 0xFF

    # 'q'キーで終了
    if key == ord('q'):
        break

    # 実際のFPSを計算して表示
    actual_fps = 1 / (time.time() - start_time)
    print(f"Actual FPS: {actual_fps:.2f}")

cap.release()
cv2.destroyAllWindows()

実行するとカメラの映像が表示され、コンソールに実際のFPSが出力されます。

目標FPSに近い値で安定します。

‘q’キーを押すと、プログラムが終了します。

waitkeyの待機時間を動的に調整することで、フレームレートを制御しています。

処理時間が長くなっても、一定のフレームレートを保つことができます。

高速なカメラを使用する場合や、複雑な画像処理を行う場合に特に有効です。

さらに、このテクニックを応用すれば、ユーザーがリアルタイムでフレームレートを調整できるようにもなります。

例えば、上下矢印キーでFPSを変更するなど、柔軟な制御が可能になります。

import cv2
import time

# カメラをオープン
cap = cv2.VideoCapture(0)

# 初期フレームレート(FPS)
target_fps = 30

while True:
    start_time = time.time()

    ret, frame = cap.read()
    if not ret:
        break

    # 現在のFPSを表示
    cv2.putText(frame, f"FPS: {target_fps}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
    cv2.imshow('Adjustable FPS Camera', frame)

    # フレーム間隔を計算
    frame_interval = 1 / target_fps

    # 経過時間を計算
    elapsed_time = time.time() - start_time

    # 待機時間を計算(負の場合は1ミリ秒に)
    wait_time = max(int((frame_interval - elapsed_time) * 1000), 1)

    # キー入力を待つ(計算された待機時間)
    key = cv2.waitKey(wait_time) & 0xFF

    # 上矢印キーでFPS増加、下矢印キーでFPS減少
    if key == 82:  # 上矢印キー
        target_fps = min(target_fps + 5, 60)
        print(f"Target FPS increased to {target_fps}")
    elif key == 84:  # 下矢印キー
        target_fps = max(target_fps - 5, 5)
        print(f"Target FPS decreased to {target_fps}")

    # 'q'キーで終了
    elif key == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

実行するとカメラの映像が表示され、現在のFPSが画面に表示されます。

上矢印キーを押すとFPSが増加し、下矢印キーを押すとFPSが減少します。

‘q’キーを押すと、プログラムが終了します。

waitkeyを活用することで、単なるキー入力の待機だけでなく、プログラムの動作を細かく制御できることがわかります。

フレームレートの調整は、リソースの効率的な使用や、ユーザー体験の向上に直結する重要な技術です。

●waitkeyのトラブルシューティング/3大エラーと解決策。

ここでは、waitkeyを使用する際によく遭遇する3つの主要なエラーと、その解決策について詳しく解説します。

○エラー1:キー入力が反応しない場合の対処法

キー入力が反応しない。

まるで誰かに話しかけているのに、無視されているような不快な体験です。

waitkeyを使用しているのに、キー入力が全く反応しない場合、どのように対処すればよいのでしょうか。

まず、考えられる原因を探ってみましょう。

多くの場合、ウィンドウがフォーカスされていないことが原因です。

OpenCVのウィンドウがアクティブでない状態では、キー入力を受け付けません。

解決策として、次のようなアプローチが効果的です。

import cv2
import numpy as np

# 画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img, "Press any key", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

# ウィンドウを作成し、フォーカスを設定
cv2.namedWindow('Test Window')
cv2.imshow('Test Window', img)

# ウィンドウをアクティブにする
cv2.setWindowProperty('Test Window', cv2.WND_PROP_TOPMOST, 1)

while True:
    key = cv2.waitKey(1) & 0xFF
    if key != 255:
        print(f"キーが押されました: {chr(key)}")
        break

cv2.destroyAllWindows()

cv2.setWindowProperty()関数を使用してウィンドウを最前面に表示することで、確実にフォーカスを得られます。

また、waitkeyの戻り値を255と比較することで、実際にキーが押されたかどうかを確認できます。

○エラー2:プログラムが終了しない問題の解決

プログラムが終了しない。

まるで永遠に続く映画のエンドロールのように、いつまでたってもプログラムが終わらない。

こんな経験はありませんか?

waitkeyを使用する際、よく遭遇する問題の一つです。

主な原因は、無限ループ内でwaitkeyを呼び出しているにもかかわらず、適切な終了条件を設定していないことです。

解決策は、明確な終了条件を設定し、ループから抜け出す方法を用意することです。

次のコードで、この問題を解決できます。

import cv2
import numpy as np

# 画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img, "Press 'q' to quit", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)

cv2.imshow('Test Window', img)

while True:
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        print("'q'キーが押されました。プログラムを終了します。")
        break

cv2.destroyAllWindows()

このコードでは、’q’キーが押されたらループを抜け出し、プログラムを終了します。

また、cv2.destroyAllWindows()を呼び出すことで、確実にすべてのウィンドウを閉じています。

○エラー3:高CPU使用率の改善方法

CPU使用率が異常に高い。

パソコンのファンが猛烈な勢いで回り始め、まるでジェットエンジンのような騒音が部屋中に響き渡る。

waitkeyを使用したプログラムで、こんな経験をしたことはありませんか?

高CPU使用率の主な原因は、waitkeyの待機時間が短すぎることです。

待機時間が短いと、CPUが常にビジー状態になってしまいます。

解決策は、適切な待機時間を設定することです。

次のコードで、CPU使用率を大幅に改善できます。

import cv2
import numpy as np
import time

# 画像を作成
img = np.zeros((300, 300, 3), dtype=np.uint8)
cv2.putText(img, "CPU usage optimized", (30, 150), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 2)

cv2.imshow('Test Window', img)

while True:
    start_time = time.time()

    # ここに画像処理のコードを書く

    # 処理時間を計算
    process_time = time.time() - start_time

    # 待機時間を計算(最小1ミリ秒)
    wait_time = max(1, int((1/30 - process_time) * 1000))

    key = cv2.waitKey(wait_time) & 0xFF
    if key == ord('q'):
        break

cv2.destroyAllWindows()

このコードでは、処理時間を計算し、フレームレートを30FPSに保つように待機時間を動的に調整しています。

結果として、CPUの使用率を大幅に削減できます。

●waitkeyを使った驚きの応用例

waitkeyの基本を押さえ、よくあるエラーへの対処法も学びました。

ここからは、waitkeyの真価を発揮する驚きの応用例を紹介します。

この例を通じて、waitkeyが単なるキー入力待機関数ではなく、プログラムの流れを制御する重要な要素であることを実感できるでしょう。

○サンプルコード11:マルチスレッドでのwaitkey活用法

複数の処理を同時に行いたい。

例えば、画像処理をしながらキー入力も受け付けたい。

そんな要望に応えるのが、マルチスレッドプログラミングです。

waitkeyをマルチスレッド環境で使用する方法を見てみましょう。

import cv2
import numpy as np
import threading
import time

# グローバル変数
running = True
current_key = None

# キー入力を監視するスレッド
def key_monitor():
    global running, current_key
    while running:
        key = cv2.waitKey(1) & 0xFF
        if key != 255:
            current_key = key
        time.sleep(0.01)  # CPUの負荷を下げるため

# メイン処理
def main():
    global running, current_key

    # 画像を作成
    img = np.zeros((300, 300, 3), dtype=np.uint8)

    # キー入力監視スレッドを開始
    thread = threading.Thread(target=key_monitor)
    thread.start()

    try:
        while running:
            # 現在押されているキーを表示
            img.fill(0)
            if current_key:
                cv2.putText(img, f"Key: {chr(current_key)}", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)
            else:
                cv2.putText(img, "Press any key", (50, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2)

            cv2.imshow('Multithreaded Waitkey', img)

            # 'q'キーで終了
            if current_key == ord('q'):
                running = False

            time.sleep(0.03)  # 約30FPSで更新

    finally:
        running = False
        thread.join()
        cv2.destroyAllWindows()

if __name__ == "__main__":
    main()

このコードでは、キー入力の監視を別スレッドで行っています。

メインスレッドでは画像処理と表示を担当し、キー入力に応じて表示を更新します。

マルチスレッドを使用することで、複雑な処理を行いながらもスムーズにキー入力を受け付けることができます。

○サンプルコード12:GUIとの連携でインタラクティブな画像処理

最後に、waitkeyとGUIを組み合わせた高度な例を見てみましょう。

ユーザーフレンドリーなインターフェースで画像処理を行う方法です。

import cv2
import numpy as np
import tkinter as tk
from PIL import Image, ImageTk

class ImageProcessor:
    def __init__(self, window, window_title):
        self.window = window
        self.window.title(window_title)

        # OpenCVで画像を読み込む
        self.image = cv2.imread('sample_image.jpg')
        self.processed_image = self.image.copy()

        # 画像をTkinter用に変換
        self.photo = self.convert_to_tkinter(self.processed_image)

        # 画像を表示するキャンバス
        self.canvas = tk.Canvas(window, width=self.image.shape[1], height=self.image.shape[0])
        self.canvas.pack()
        self.canvas_image = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.photo)

        # ボタンを作成
        self.btn_original = tk.Button(window, text="元の画像", command=self.show_original)
        self.btn_original.pack(side=tk.LEFT)

        self.btn_gray = tk.Button(window, text="グレースケール", command=self.convert_to_gray)
        self.btn_gray.pack(side=tk.LEFT)

        self.btn_blur = tk.Button(window, text="ぼかし", command=self.apply_blur)
        self.btn_blur.pack(side=tk.LEFT)

        self.window.mainloop()

    def convert_to_tkinter(self, cv_image):
        rgb_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
        pil_image = Image.fromarray(rgb_image)
        return ImageTk.PhotoImage(image=pil_image)

    def update_image(self):
        self.photo = self.convert_to_tkinter(self.processed_image)
        self.canvas.itemconfig(self.canvas_image, image=self.photo)

    def show_original(self):
        self.processed_image = self.image.copy()
        self.update_image()

    def convert_to_gray(self):
        self.processed_image = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        self.processed_image = cv2.cvtColor(self.processed_image, cv2.COLOR_GRAY2BGR)
        self.update_image()

    def apply_blur(self):
        self.processed_image = cv2.GaussianBlur(self.image, (15, 15), 0)
        self.update_image()

# メイン処理
if __name__ == "__main__":
    root = tk.Tk()
    ImageProcessor(root, "Interactive Image Processor")

このコードでは、TkinterというPythonの標準GUIライブラリを使用して、ボタンクリックで画像処理を行うインターフェースを作成しています。

OpenCVで画像処理を行い、結果をTkinterのウィンドウに表示しています。

waitkey関数は直接使用していませんが、Tkinterのイベントループがwaitkeyと同様の役割を果たしています。

ユーザーの操作(ボタンクリック)に応じて画像処理が実行され、結果がリアルタイムで表示されます。

まとめ

PythonとOpenCVのwaitkey関数について、基本から応用まで幅広く解説しました。

waitkeyは単なるキー入力待機関数ではなく、プログラムの流れを制御する重要な要素であることが理解できたでしょう。

waitkeyを使いこなすことで、画像処理プログラムの可能性が大きく広がります。

ここで学んだ知識を活かし、独自のアイデアを形にしてみてください。

プログラミングの醍醐味は、自分のアイデアを形にすることにあります。