ブログ名

カート内の物体検出ブログ第2回:背景差分法を利用した物体検出

はじめに

第1回で紹介した技術を元に、第2回の記事では、カメラ、又は保存した動画を用いることによって物体検出を行なっていきます。
では、早速はじめていきましょう。

開発環境

開発環境です。
第1回の記事でも取り上げましたが、もう一度記載します。

  • macOS 10.14.6
  • python3.7.3

開発環境はmacを想定しており,言語はpythonを利用します(プログラム自体は恐らくWindowsでも動作すると思います)
pythonのバージョンはpython3です。
python2ではOpenCVの一部の関数が動作しないことがあるため、python3を利用します。

この記事で行うこと

今回の記事では以下の2点の確認を行なっていきます。

  • カメラを利用した物体検出(市販のカメラを利用)
  • 保存した動画を利用した物体検出

プログラムの作成

今回用いるプログラムは以下のようになっています。
又、このプログラムは以下の記事を参考に作成しました。

【Python/OpenCV】背景差分法で移動物体の検出 PythonとOpenCVを使って物体検出をやってみた

# -*- coding: utf-8 -*-
import cv2
import numpy as np
import time

def denoise(mask, kernel, times):
    for i in range(times):
        mask = cv2.dilate(mask,kernel,iterations = 1)
    for i in range(times):
        mask = cv2.erode(mask,kernel,iterations = 1)
    for i in range(times):
        mask = cv2.erode(mask,kernel,iterations = 1)
    for i in range(times):
        mask = cv2.dilate(mask,kernel,iterations = 1)
    return mask

def main():
    th = 40    # 差分画像の閾値 低い程検知率が上がるが,低すぎると精度が落ちる
    kernel = np.ones((5,5),np.uint8) #カーネルの設定
    count = 0
    detect = False
    last_white_pixels = 0
    item_num = 1

    # カメラのキャプチャ
    # cap = cv2.VideoCapture(1) #パソコン接続のカメラを使用する場合(数字は0や1など)
    cap = cv2.VideoCapture('動画ファイル名') #ファイルを読み込む場合
    time.sleep(3) #カメラの起動の待機


    # 最初のフレームを背景画像に設定
    ret, bg = cap.read()

    # グレースケール変換
    bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)

    # 画像サイズの取得
    height, width = bg.shape

    while(cap.isOpened()):
        # フレームの取得
        ret,frame = cap.read()
        frame_copy = frame.copy()

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

        # 差分の絶対値を計算
        mask = cv2.absdiff(gray, bg)


        # 差分画像を二値化してマスク画像を算出
        mask[mask < th] = 0
        mask[mask >= th] = 255
        mask0 = mask.copy()
        # モルフォロジー変換
        mask = denoise(mask, kernel, 2)


        # 輪郭抽出
        contours = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)[1]

        item_pics = []
        # 各輪郭に対する処理
        for i in range(0, len(contours)):

            # 輪郭の領域を計算
            area = cv2.contourArea(contours[i])

            #小さい領域と全体の輪郭(大きすぎる領域)を除外
            if area < height*width/1000 or height*width*0.8 < area:
                continue

            # 外接矩形等
            if len(contours[i]) > 0:
                detect = True
                rect = contours[i]
                x,y,w,h = cv2.boundingRect(rect)
                item_pics.append((x,y,w,h))
                cv2.rectangle(frame,(x,y),(x+w,y+h),(0,255,0),2)
                cv2.putText(frame, 'Detected', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), thickness=2)

        #白いピクセル数をカウント
        white_pixels = cv2.countNonZero(mask)

        #print(abs(white_pixels-last_white_pixels))

        if detect:
            if abs(white_pixels-last_white_pixels) < 5000:
                count += 1
            else:
                count = 0
            if count > 50:
                # 新たな背景画像の取得
                ret, bg = cap.read()
                # グレースケール変換
                bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY)
                count = 0
                for item in item_pics:
                    item_pic = frame_copy[item[1]:item[1]+item[3], item[0]:item[0]+item[2]]
                    cv2.imwrite("items1/item_" + str(item_num) + ".jpg", item_pic)
                    item_num += 1
        else:
            count = 0

        #print(count)
        # フレームとマスク画像を表示
        mask = cv2.resize(mask, (int(width*0.5), int(height*0.5)))
        frame = cv2.resize(frame, (int(width*0.5), int(height*0.5)))
        #cv2.imshow("Mask0", mask0)
        cv2.imshow("Mask1", mask)
        cv2.imshow("Flame", frame)
        detect = False
        last_white_pixels = white_pixels

        # qキーが押されたら途中終了
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()

プログラム(main関数)の流れ

プログラムの流れはこのようになっています。
1. 閾値の設定(今回は40)、countは0とする
2. カメラ、又は動画から背景画像を取得
3. 新たなフレームを取得し、背景画像との差分画像を取得する。差分画像では物体が白、背景が黒のピクセルで表される。この時、白のピクセル数をカウントする。
4. 差分画像から白の部分の輪郭を抽出し、もし輪郭があれば、各輪郭に対して外接矩形を描画し、5へ進む。なかった場合は、countを0として2に進む。
5. 前のフレームの画像の白のピクセル数と現在の画像の白のピクセル数の差をとる。その差が小さければ(=前のフレームからあまり変化がなければ)、countを1増やし、6へ進む。そうでなければ、countを0として、3に進む。
6. もしcountが50より大きければ(=モノが置かれて一定時間経過したら)、新たなフレームを取得し、それを新たな背景画像として2へ進む。また、countを0とする。

このプログラムで保存された画像はitems1というディレクトリ内に保存されます。

カメラを利用した物体検出

main関数の中のcap = cv2.VideoCapture(1)の前に付いている#を消してください。
その後、1行下のcap = cv2.VideoCapture('動画ファイル名')に#をつけてコメントアウトしてください。
実行すると以下のようにカメラが起動します。

f:id:shizuuuka0202:20200107002545p:plain

キーボードのqを押すことでプログラムを終了させることができます。

保存した動画を利用した物体検出

main関数の中のcap = cv2.VideoCapture('動画ファイル名')に、自身の動画ファイルの名前を入力して実行してください。
又、cap = cv2.VideoCapture(1)は#をつけてコメントアウトしておいてください。
実行すると下記のようなウィンドウが開かれ、プログラムの処理が行われます。

f:id:shizuuuka0202:20200107002506p:plain

デモ動画

物体検出のデモ動画です。

https://cdn.kibe.la/media/private/9725/W1siZiIsInRlYW1fOTcyNS8yMDE5LzEyLzI3LzNwb2xmcDh0dWdfZGVtby5tcDQiXV0/6409e6e9f779771e/demo.mp4

現状の課題

  • 物体を取り出した時も検知してしまう(上動画の18秒あたり)
  • 光沢がある物体は、誤検知してしまう
  • カート内が揺れた時に物体を検出してしまう

次回以降

次回以降は、物体をカートから取り出した際に検知してしまう課題を解決していきます。
又、デモ動画をとってわかった現状の課題についても解決していきたいです。

最後に

この記事では、カメラや、保存した動画で物体検出を行う方法について紹介しました。
次回の記事では、物体取り出し時の誤検知を解決していきたいと思います。


前の記事へ

目次に戻る