LoginSignup
195
184

More than 3 years have passed since last update.

【爆速】OpenCVで複数の物体検出 -ラズパイ-

Last updated at Posted at 2019-05-29

「背景差分で物体検出をしてみた」の記事が面白くて、「複数の物体」でも
検出できるのか検証してみました。

はじめに

きっかけは、ラズパイで2つのディープラーニングモデルを動かしていたときのこと。
予想通り、速度は激遅で使いものになりませんでした。

そのため、OpenCVで物体検出できないか?と考え実装してみました。

本稿では、ディープラーニングを使わないOpenCVによる複数の物体検出を行ってみます。

OpenCVによる物体検出

まずは、背景写真を用意します。

1.jpg

そして、フィルターによる前処理を行います。

import cv2
img1 = cv2.imread("background.jpg")
img1 = cv2.GaussianBlur(img1, (5, 5), 0)

次に、物体検出させたい写真を用意します。
3.jpg

同じく前処理をしておきます。

img1 = cv2.imread("test.jpg")
img2 = cv2.GaussianBlur(img2, (5, 5), 0)

そして、OpenCVの機能で物体検出をさせます。

fgbg = cv2.bgsegm.createBackgroundSubtractorMOG()
fgmask = fgbg.apply(np.uint8(img1))
fgmask = fgbg.apply(np.uint8(img2))
cv2.imshow(fgmask)

ダウンロード.png

恐ろしく簡単に検出することができました。
しかも、爆速です。

k-means法によるクラスタリング

次に得られたモノクロマップで物体を分割します。

今回は、k-means法によるクラスタリングを行います。
本当は混合ガウス分布の方が良いのですが、処理を少しでも軽くするために
k-means法(正確にはk-means++)を使います。

得られたマップに対し、k-means法を適用します。

from sklearn.cluster import KMeans
Y, X = np.where(fgmask > 200)
y = KMeans(n_clusters=2, random_state=0).fit_predict(np.array([X,Y]).T)

これも爆速で実行することができます。

一応可視化します。

import matplotlib.pyplot as plt
plt.scatter(X, -Y+288, c=y)
plt.xlim(0,288)
plt.ylim(0,288)
plt.show()

ダウンロード (2).png

あとは、クラスタに合わせてバウンディングボックスを作ります。

def get_x_y_limit(Y, X, result, cluster):
    NO = np.where(result==cluster)
    x_max = np.max(X[NO])
    x_min = np.min(X[NO])
    y_max = np.max(Y[NO])
    y_min = np.min(Y[NO])

    x_max = int(x_max)
    x_min = int(x_min)
    y_max = int(y_max)
    y_min = int(y_min)
    return x_min, y_min, x_max, y_max

def bounding_box(img, x_min, y_min, x_max, y_max):
    img = cv2.rectangle(img, (x_min, y_min), (x_max, y_max), (0, 255, 0), 5)
    return img
x_min, y_min, x_max, y_max = get_x_y_limit(Y, X, y, 0)
img2 = bounding_box(img2, x_min, y_min, x_max, y_max)

x_min, y_min, x_max, y_max = get_x_y_limit(Y, X, y, 1)
img2 = bounding_box(img2, x_min, y_min, x_max, y_max)
cv2_imshow(img2)

ダウンロード (3).png

注目すべきは「矢印」の部分。
影が映り込んでいるのにも関わらず、検出されない点が素晴らしいです。

単に背景写真と差分をとると、どうしても影が検出されてしまうのですが、
ここは天下のOpenCV、さすがです!

制約

ただし、本手法は制約があります。
それは、「物体が一つでも必ず複数に分割されてしまう」ということです。

具体例を挙げます。

今回は手を二つ認識させたいため、クラスタリングの数を「2」に設定します。
手が二つある写真は、先ほど見ていただいたとおり、手が別々に検出されます。

ダウンロード (3).png

ところが、手が一つの写真を入れても二つの物体が検出されてしまいます。

ダウンロード.png

ここはk-means法を使っている以上、しょうがないところです。

また、k-means法は特性上、距離が近いものを同じ物体と認識します。
従って、よく言われる半月が重なったような形状ではうまく検出することが
できません。

「物体数が決まっていない」、もしくは「複雑な形状を検出したい」場合は、
DBSCANを使うと良いかもしれません。

ラズパイで実行

ラズパイでリアルタイムに動かすコードはこちらに置きました。
main_opencv.pyを実行し、「p」キーを押せば、以下のようにリアルタイムで
検出することができます。

本気を出せば、13FPSくらいになります。
終了したい場合は、「q」キーを押してください。

ただし、単にopencvをインストールしただけだと動かず、以下のようにラズパイで
contribをインストールする必要がありました。

sudo -H pip3 install opencv-contrib-python==3.4.3.18

バージョンはお好みで指定してください。

まとめ

  • ラズパイでも、OpenCVを使えば爆速で複数の物体検出ができる。
  • ただし、固定カメラ限定で、かつ事前に検出したい物体数を決めておく必要がある。
  • 本技術を応用すれば、物体検出で必要なアノテーションデータも手軽に作成することができる。
195
184
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
195
184