OpenCVで飛行機を検出する初心者向け手法を解説します

eye catch opencv Python
この記事は約7分で読めます。
※記事内には広告を含む場合がございます

PythonのOpenCVを使って動画から映像を取得し、飛んでいる飛行機を検知します。

今回はピクセルを比較する方法で動体検知します。
とてもシンプルな手法ですが、飛んでいる飛行機を検知する上では意外と正確に検知出来たので記事にしました。

結果

※使用させていただいた動画のリンクは記事の最後に記載しました。

前提条件

python3.9
opencv-pytho 4.5.5

仕組み概要

1.動画を読み込む。
2.グレースケールに変換
3.比較用にフレームを保存しておく
4.ガウシアンブラーでノイズを軽減
5.画像を比較して差分の画像を生成する
6.差分の画像を2値化する(閾値処理)
7.輪郭を検知する
8.輪郭がはいった配列から、領域が小さすぎる要素は無視する
9.輪郭情報から短形で領域を囲み、描画する

コード

import cv2

filepath = "sample.mov"
cap = cv2.VideoCapture(filepath)
avg = None

while True:
    # 1フレームずつ取得する。
    ret, frame = cap.read()
    if not ret:
        break
    # グレースケールに変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    # 比較用のフレームを取得する
    if avg is None:
        avg = gray.copy().astype("float")
        continue

    # ブラーを掛けてノイズを軽減する
    blur = cv2.GaussianBlur(gray, (1, 1), 1)
    # 現在のフレームと移動平均との差を計算
    cv2.accumulateWeighted(blur, avg, 0.7)
    frameDelta = cv2.absdiff(blur, cv2.convertScaleAbs(avg))
    # デルタ画像を閾値処理を行う
    thresh = cv2.threshold(frameDelta, 3, 255, cv2.THRESH_BINARY)[1]
    # 画像の閾値に輪郭線を入れる
    contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

    for i in range(0, len(contours)):
        if len(contours[i]) > 0:
            # しきい値より小さい領域は無視する
            if cv2.contourArea(contours[i]) < 100:
                continue
            # 短形で領域を囲む
            rect = contours[i]
            x, y, w, h = cv2.boundingRect(rect)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 4)
    # 結果を出力
    cv2.imshow("Frame", frame)
    # Escキーで終わる
    key = cv2.waitKey(30)
    if key == 27:
        break
cap.release()
cv2.destroyAllWindows()

コード解説

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)


グレースケールに変換します。
OpenCVでは画像のチャンネルの並び順はRGBではなくBGRです。

blur = cv2.GaussianBlur(gray, (1, 1), 1)

ブラーを掛けてノイズを軽減します。
(1,1),1がブラーの強さになりますが、大きくする(大きくぼかす)と輪郭の情報もボケてしまって検出しにくくなります。

cv2.accumulateWeighted(blur, avg, 0.7)

2つの画像blur,avgの移動した値を求めます。0.7は入力画像の重みです。
重みは更新速度(どのくらいの早さで,以前の画像を「忘れる」か)を調節します。*参照 opencv.jp
重みを調整することでより正確に検出できることがあります。

contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

輪郭を検知します。
contoursにはオブジェクトの輪郭座標を保持している配列。
hierarchyにはオブジェクトの階層構造情報を保持している配列。
が入ります。

for i in range(0, len(contours)):
        if len(contours[i]) > 0:
            # しきい値より小さい領域は無視する
            if cv2.contourArea(contours[i]) < 100:
                continue
            # 短形で領域を囲む
            rect = contours[i]
            x, y, w, h = cv2.boundingRect(rect)
            cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 4)

forで回して順番に処理します。
cv2.contourAreaは領域の大きさを返します。しきい値100より小さいなら無視します。
cv2.boundingRect(rect)で短形の領域を計算し、
cv2.rectangleで描画します。
(0, 255, 0)は色(RGB)で、4は線の太さです。

cv2.imshow("Frame", frame) でウインドウに描画します。

欠点

動画の条件によっては上手くいかない事もあります。

↑カメラが動いていてるせいで、地表の建物も差分として検出されている

正確に検出できない条件は以下の通りです

  • 地表の構造物が写っている。なおかつ(and) 画角が大きく動いている。
  • 動画には空と飛行機しか写っていないが、雲が存在する。なおかつ(and) 画角が大きく動いている。

そして常に飛行機を1つのかたまりとして認識するのが苦手です。
窓やアンテナ、ギアやランプ等のパーツごとに認識されます。

また旅客機の様な2色以上の塗装が含まれている飛行機は色ごとに分かれて検出される傾向があります。

つまりカメラは固定されている方が確実に有利です。

発展

Webカメラの映像をソースとして使う

良く自宅から飛行機が通るのが見える方は、ラズパイ + 画角の広いWebカメラを組み合わせて実行すると面白いです。

高精度化 with 機械学習

様々な物体を検知する機械学習モデルが沢山あるそうです。
色々勉強する必要はありそうですが、完全にこのプログラムの上位互換ですね。

音声分析

風向き等もありますが、飛行機はかなりの音量があります。
エンジンによって音に特性があるので、音声分析をガチったらエンジン名を特定できるでしょう。

検証に使用した動画サイト様のURL(とても感謝!)

https://pixabay.com/ja/
ハイビジョン フリー映像素材 [Royalty Free HD Footage]

使用プログラム参照(とても感謝!)

OpenCVで手っ取り早く動体検知してみた - Qiita
自分が書いた記事の中でだいぶ前に書いたOpenCVやRaspberryPiの記事がここ最近また皆さんに読まれるようになりました。最近のトレンドになっている「ディープラーニング」、「エッジコンピューティング」に関連したキーワードになって...
オブジェクト輪郭検出 | OpenCV / findContours を使用して画像中のオブジェクトの輪郭を検出する方法
OpenCVを利用して動画(カメラ)から動体検知をする方法について | CyberAgent Developers Blog
CyberAgent Developers Advent Calendar 2017の14日目の記事 ...
OpenCVで画像の平滑化をしてみた - Qiita
はじめに 画像処理では、必ずしもキレイな画像を用意できるとは限りません。 不鮮明だったり、ノイズが入っていたりといったことも多くあります。 ノイズが入っている場合には、平滑化という手法を用いてそれを取り除くことがあります。 ある...
モーション解析と物体追跡 — opencv 2.2 documentation

~サイト支援のお願い~

以下のリンクはアフェリエイトリンクです。
この本は、私自身が実際に購入し、学習に活用した本です。

コメント

タイトルとURLをコピーしました