Python,OpenCVを使って物体検出でコイン(小銭)を検出してみた

プログラミング

はじめに

画像に写っているコイン(小銭)だけを検出してみたいと思います。
最近は機械学習やディープラーニングが流行っていますが、今回は原始的な手法でやってみたいと思います。

機械学習とかディープラーニングの概要や勉強方法について知りたい人は、下記の記事もオススメです。

動作環境

・PyCharm Community Edition 2018.3.1 x64
・OpenCV 3.4.5.20

どうやってコインを検出するか

お札やコインが写っている画像の中でどうやってコインを見つけるかを考えてみます。

色々やり方があるとは思いますが、今回は輪郭を抽出して、その輪郭情報を使いたいと思います。
以下のような処理で実践してみます。

画像から輪郭を抽出

輪郭から外接矩形を算出

外接矩形の情報から円らしい物体をコイン(小銭)とする

どうやって”円らしさ”を決めるか

今回は外接矩形を用いて以下のルールで”円らしさ”を定義しました。

・アスペクト比
コインのような真円であれば外接矩形の縦と横幅のアスクペクトはほぼ1.0になります。

・コインの面積
あらかじめコインの面積を定義して、その面積との比率を使います。

・円形度
ある形から円らしさを決める指標として、「円形度」というものがあります。
式で表すと以下になります。

 円形度 = 4*(面積) / (円周率pi * (長軸)^2)

完全な円の場合は1.0になります。

スポンサーリンク

ソースコード

import cv2
import math

if __name__ == "__main__":
    # input image
    img = cv2.imread("./sample.JPG")

    # convert gray scale image
    gray_img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

    # gaussian
    gray_img = cv2.GaussianBlur(gray_img, (7,7), 0)
    #ret, bw_img = cv2.threshold(gray_img, 190, 255, cv2.THRESH_BINARY_INV)
    ret, bw_img = cv2.threshold(gray_img, 0, 255, cv2.THRESH_OTSU)

    # invert black white (when use cv2.THRESH_OTSU)
    #bw_img = cv2.bitwise_not(bw_img)

    cv2.imwrite("black_white.jpg", bw_img)

    imgEdge, contours, hierarchy = cv2.findContours(bw_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    ROUNDNESS_THRESH = 0.5
    ASPECT_THRESH = 0.90
    COIN_AREA = 100000.0
    COIN_AREA_RATIO_THRESH = 0.5
    coin_list = []
    for contour in contours:
        # get circumscribed quadrangle
        x, y, width, height = cv2.boundingRect(contour)

        # check aspect ratio
        aspect_ratio = float(width) / float(height)
        if (aspect_ratio < ASPECT_THRESH):
            continue

        # check area
        area = cv2.contourArea(contour)
        area_ratio = abs(float(1 - (area / COIN_AREA)))
        if (area != 0 and area_ratio > COIN_AREA_RATIO_THRESH):
            continue

        # detect long axis
        longAx = width
        if (width < height):
            longAx = height
        # calculate roundness value
        roundness = (4*area)/(math.pi*(longAx**2)) # it seems like a circle closer to 1.0

        if (roundness > ROUNDNESS_THRESH):
            coin_list.append(roundness)
            topleft = x
            cv2.rectangle(img, (x, y), (x+width, y+height), (0, 0, 200), 2)

    print("number of coins detected : ", len(coin_list))
    print("coin average roundness : ", sum(coin_list)/len(coin_list))
    cv2.imwrite("coin_result.jpg", img)

 

結果

 

まとめ

画像中に写っているコインを画像処理ベースの手法で検出しました。
ただ、実用という意味ではこの手法ではうまくいかないケースが多いと思います。

例えばコイン同士がくっついていたり、重なってしまった場合に対処が難しいことなどが考えられます。

また、背景によっても結果が大きく変わってくると思います。
画像処理、奥が深いですね。

参考

・OpenCVの基礎を学ぶならまずはこの一冊。


詳解 OpenCV 3 ―コンピュータビジョンライブラリを使った画像処理・認識

 

・画像処理の基礎から学ぶならまずはこの一冊。


ディジタル画像処理の基礎と応用―基本概念から顔画像認識まで (ディジタル信号処理シリーズ)

コメント

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