Gemini でプログラミング学習 #5 OpenCV でWebカメラから動体検知

現在、Pythonを勉強中です。 また、生成AIも習うより慣れよと出来る限り使って経験値を高めようとしています。 ということで Gemini を教師として Python のプログラミングを学習していく過程をブログに残していきます。

今回は前回に引き続き Web カメラの映像の動体検知です。 前回は自動車検知のカスケード分類機を作ろうとしたのですがイマイチな結果でした。 今回は自動車に限定しない動体検知のテストします。

Gemini に聞いてみる

bigriver.jp
bigriver.jp

WebCV でWebカメラの映像から動くものに矩形で囲いたい

Gemini
Gemini

実は私の最初の依頼文章で”OpenCV”を”WebCV”とキー入力を間違えています。 それに対して Gemini は

「Webカメラの映像から動くものを検出し、矩形で囲むには、OpenCVの機能を使うのが一般的です。WebCVというライブラリは存在しないため、OpenCVを用いた方法を説明します。

と、WebCVではなく OpenCVの話として説明しますとちゃんと間違いを認識し、本当は何だったのか類推し回答してくれています。 私も生成AI出始めであまり触っていなかったころは生成AIの正しい使い方みたいな情報をもとにどんな役割で、こんな状況で回答はこうこうほしいと色々工夫しないといけないと思っていました。 最近本来使わなくても良い場面でも出来る限り Gemini に聞いているのですがかなり雑に問いかけてもちゃんと答えが返ってきます。 感覚としては10あるうちの2~3の雑なINPUTで7-10、場合によっては10以上の回答が返ってくる感じです。 もちろん回答内容の正誤はありますがそんな感触です。

コード

途中色々修正していますが最終的なコードは以下。

import cv2
import numpy as np

# get background image
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
if not ret:
    print('Error: cannot capture')
    cap.release()
    cv2.destroyAllWindows()
    exit()
bg = frame.copy()

while True:
    # read a frame from the camera
    ret, frame = cap.read()
    if not ret:
        break

    # calculate the difference between the background and the frame
    diff = cv2.absdiff(frame, bg)
    diff_gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)

    # threshold
    _, thresh = cv2.threshold(diff_gray, 30, 255, cv2.THRESH_BINARY)

    # find contours
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # draw contours
    for countour in contours:
        if cv2.contourArea(countour) < 250:
            continue
        x,y,w,h = cv2.boundingRect(countour)
        cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255, 0), 2)

    # show the frame
    cv2.imshow('frame', frame)

    # 'q' key is pressed
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break   

# release the camera
cap.release()
cv2.destroyAllWindows()

実行結果はこちら。 実際にはリアルタイムに動くもの(映像は交差点なので主に自動車)について緑色の枠が追随していきます。 

また、今回はモザイク処理はいれていないためスクリーンショットを一度取得しモザイクを手動でかけています。 この後、モザイク処理を追加したプログラムも紹介します。

モザイク処理を追加

プログラムの中でモザイク処理を追加したコードはこちら。 太文字が追加部分です。

import cv2
import numpy as np

def mosaic(src, ratio=0.2):
    small = cv2.resize(src, None, fx=ratio, fy=ratio, interpolation=cv2.INTER_NEAREST)
    return cv2.resize(small, src.shape[:2][::-1], interpolation=cv2.INTER_NEAREST)

# get background image
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
if not ret:
    print('Error: cannot capture')
    cap.release()
    cv2.destroyAllWindows()
    exit()
bg = frame.copy()

while True:
    # read a frame from the camera
    ret, frame = cap.read()
    if not ret:
        break

    # calculate the difference between the background and the frame
    diff = cv2.absdiff(frame, bg)
    diff_gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)

    # threshold
    _, thresh = cv2.threshold(diff_gray, 30, 255, cv2.THRESH_BINARY)

    # find contours
    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # draw contours
    for countour in contours:
        if cv2.contourArea(countour) < 200:
            continue
        x,y,w,h = cv2.boundingRect(countour)
        cv2.rectangle(frame, (x,y), (x+w, y+h), (0, 255, 0), 2)

    # mosaic the frame
    frame = mosaic(frame)

    # show the frame
    cv2.imshow('frame', frame)

    # 'q' key is pressed
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break   

# release the camera
cap.release()
cv2.destroyAllWindows()

実行結果はこちら。 Windowsの機能でMPEGにしたものを別途GIFにしています。 サイズを小さくするために低画質にしているためちょっと見づらいですが各フレームがモザイク処理されていることがわかると思います。

functions have different types, the output array type must be explicitly specified in function ‘cv::arithm_op’

Geminiからのコードを実行時、下記のエラーが発生しました。

Traceback (most recent call last):
 File "c:\Users\XXXXXXX\OneDrive - bigriver\ドキュメント\VSC_python\opencv6.py", line 21, in <module>
  diff = cv2.absdiff(frame, bg)
      ^^^^^^^^^^^^^^^^^^^^^^
cv2.error: OpenCV(4.9.0) D:\a\opencv-python\opencv-python\opencv\modules\core\src\arithm.cpp:674: error: (-5:Bad argument) When the input arrays in add/subtract/multiply/divide functions have different types, the output array type must be explicitly specified in function 'cv::arithm_op'

”the output array type must be explicitly specified in function ‘cv::arithm_op’”がポイントとなりそうです。 従来ですと OpenCV のドキュメントを確認したり arithm_op のソースコードを確認したり、メッセージ通り array type を明示的に(explicitly)に指定されているか確認することになります。

今回は Gemini に聞いてみます。 上記のエラーコードを張り付けるだけで答えは返ってきます。

Gemini
Gemini

このエラーは、cv2.absdiff 関数で比較する2つの画像 (framebg) のデータ型が異なっているために発生しています。

具体的には、

  • frame: Webカメラから読み込まれた画像で、通常は uint8 (符号なし8ビット整数) 型です。
  • bg: 最初のフレームをコピーしたもので、float32 (単精度浮動小数点数) 型になっています。

OpenCVの算術演算関数 (cv2.absdiff, cv2.add, cv2.subtract など) では、入力画像のデータ型が異なる場合、出力画像のデータ型を明示的に指定する必要があります。

解決方法

bg のデータ型を uint8 に変換するか、cv2.absdiff の出力データ型を明示的に指定します。

解決方法

bg のデータ型を uint8 に変換するか、cv2.absdiff の出力データ型を明示的に指定します。

(省略)

今回は省略しましたが修正済のコード全体も提示してくれます。 あらゆる状況に対応出来るわけではないと思いますが初心者のプログラミングの先生として Gemini は必要十分以上の力をもっているのかなと感じました。

最終的には以下の部分を修正しエラーが解消しています。 “.astype(np.float32)” を削除しています。

#修正前
bg = frame.copy().astype(np.float32)


#修正後
bg = frame.copy() # ←修正箇所

以上