techno_memo

個人用の技術メモ。python・ROS・AI系のソフトウェア・ツールなどの情報を記載

画像認識①テンプレートマッチング/色に基づく物体認識/エッジ形状に基づく形状認識

やりたいこと

 画像から下記の手段で物体を認識する

  • テンプレートマッチング (正解画像との類似度比較)

  • 色に基づく物体検出 (HSV色空間マスクと輪郭抽出)

  • エッジ形状に基づく物体検出 (cannyエッジ検出とハフ変換)

f:id:sd08419ttic:20190331220930p:plain

画像認識の簡単な説明

『画像認識』として扱われるタスクは、主に下記のようなものがある。

タスク名 説明 応用例
クラス分類(classification) 画像全体を表すラベルを識別する 画像のWEB検索システム
位置推定(localization) 物体のラベルを分類し、それが画像中のどこにあるかを特定する 工場の生産設備(部品の位置決めなど)
物体検出(detection) 画面に映るすべての物体のラベルを分類し、それが画像中のどこにあるかを特定する 自動運転の物体認識(歩行者・対向車など)・防犯カメラの画像認識
領域分割(segmentation) 物体検出した結果をピクセル単位で切り分ける 自動運転の道路領域認識/医療画像の分析(異常箇所の切り分け)

参考サイト

starpentagon.net

大量かつ複雑な問題を解くためには機械学習/DeepLearningを用いた学習・検出が不可欠となるが、本記事ではそれを使うまでもない簡易的なパターン検出アルゴリズムの基本手法を紹介する。

テンプレートマッチング

*正解画像(テンプレート)と入力画像の画素値を直接比較し、類似度の高い箇所を特定する手法

*各画素について絶対値差分2乗和(SQDIFF)、相互相関(CCORR)などで類似度を計算し、閾値で同一とみなせるかを判定する

詳細は下記サイトが詳しい

実装例

labs.eecs.tottori-u.ac.jp

数式の説明

isl.sist.chukyo-u.ac.jp

実装例は下記のようになる。 テンプレート画像はあらかじめ画像ファイルとして用意しておく。 下記例では相関係数の正規化された値を指標として使っている。

def templete_matching(src_img,temp_img,threshold):
    '''
    テンプレートマッチング関数
    src_img:入力画像, temp_img:検出対象画像, threshold:検出敷居値(0-1)
    '''
    img_gray = cv2.cvtColor(src_img, cv2.COLOR_BGR2GRAY)        #入力画像をグレースケール変換する
    temp_gray = cv2.cvtColor(temp_img, cv2.COLOR_BGR2GRAY)      #テンプレート画像をグレースケール変換する
    w= template.shape[1]    #テンプレート画像幅
    h= template.shape[0]    #テンプレート画像高さ

    res = cv2.matchTemplate(img_gray,temp_gray,cv2.TM_CCOEFF_NORMED)    #テンプレートマッチング,相関係数の正規化指標を利用

    res_vis = res.copy()
    res_vis[res_vis<0] = 0.0
    res_vis = np.uint8((res_vis)*100)
    cv2.imshow("score_map",res_vis)    #スコア表示
    cv2.waitKey(0)
    loc = np.where( res >= threshold)   #閾値判定
    for pt in zip(*loc[::-1]):
        cv2.rectangle(src_img, pt, (pt[0] + w, pt[1] + h), (0,0,255), 2)   #結果の描画
    cv2.imshow("result",src_img)    #画面表示
    cv2.waitKey(0)

色に基づく物体検出 (HSV色空間マスクと輪郭抽出)

*検出したい物体の色がわかっているという前提であれば、特定の色の塊を抽出することで物体を検出できる

*通常の画像ファイルはRGB系で扱われることが多いが、色による検出をしやすくすためHSV系に変換して特定の色を閾値判定して抽出する

*色の抽出結果に輪郭検出アルゴリズムを適用してある程度の大きさを持つ物体領域を検出する。

RGBとHSVの変換については下記サイトを利用できる。 (検出用の閾値設定など)

www.peko-step.com

輪郭抽出用にopencvのfindContours関数を利用している。関数のオプションなどについては下記サイトが詳しい。

今回のサンプルコードでは最も外側の輪郭を抽出する設定を用いている。

また、そのまま適用するとノイズが含まれるため抽出できた輪郭から一定の長さ以下のものを削除し、各輪郭の頂点を 検出して長方形で表示するようにしている。

pynote.hatenablog.com

def color_cluster(src_img):
    '''
    色に基づく物体検出
    '''
    hsv = cv2.cvtColor(src_img, cv2.COLOR_BGR2HSV_FULL)             #hsv座標系への変換
    mask = np.zeros((hsv.shape[0],hsv.shape[1],1), dtype=np.uint8)  #画像マスクの生成
    h = hsv[:, :, 0]    #色相
    s = hsv[:, :, 1]    #彩度
    mask[((h < 20) | (h > 200)) & (s > 128)] = 255  #hsv座標系での色マスク(赤色)
    #他の色を設定する場合下記サイトなどで閾値設定する
    #https://www.peko-step.com/tool/hsvrgb.html

    #色マスクに対する輪郭抽出
    contours, hierarchy =cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    filtered_contour = []

    result_img = src_img.copy()

    #輪郭抽出結果のフィルタ (非常に小さい輪郭はノイズとみなして除去))
    for indx in range(len(contours)):
        if (len(contours[indx])>20):    #輪郭が20pixel異常の長さとなる場合
            filtered_contour.append(contours[indx])
            np_contour = np.array(contours[indx]).reshape(len(contours[indx]),2)
            left_x = min(np_contour[:,0])    #輪郭の一番左となるX座標
            right_x = max(np_contour[:,0])   #輪郭の一番右となるX座標
            top_y = min(np_contour[:,1])     #輪郭の一番上となるY座標
            bottom_y = max(np_contour[:,1])  #輪郭の一番下となるY座標
            cv2.rectangle(result_img, (left_x,top_y), (right_x, bottom_y), (0,0,255), 2)    #長方形で物体の領域を表示
            pass
    result_img = cv2.drawContours(result_img, filtered_contour, -1, (0,255,0), 3)
    cv2.imshow("result",result_img)
    cv2.waitKey(0)

エッジ形状に基づく物体検出 (cannyエッジ検出とハフ変換)

検出したい物体の形状が決まっている(線・円など)場合、エッジ検出結果(Canny エッジ検出)+ハフ変換を利用できる。

*元画像に対してエッジ検出フィルタ(Canny エッジ検出など)でエッジ特徴量を抽出する。

*エッジ特徴に対してハフ変換を用いて特定の形状となる成分を抽出する。

cannyエッジ検出フィルタのアルゴリズムについては下記のopencvリファレンスに説明が記載されている。

labs.eecs.tottori-u.ac.jp

上記で抽出したcannyエッジに対してハフ変換を利用して形状を検出する。

ハフ変換については下記のopencvリファレンスに説明が記載されている。

opencvでは直線検出はcv2.HoughLines()、円検出はcv2.HoughCircles()で容易に行うことができる。

labs.eecs.tottori-u.ac.jp

def edge_cluster(src_img):
    '''
    エッジ形状に基づく物体認識
    src_img:入力画像
    '''

    #canny edge http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_imgproc/py_canny/py_canny.html
    edges = cv2.Canny(src_img,100,200)

    #ハフ変換


    #線の検出と描画
    lines = cv2.HoughLines(edges,1,np.pi/180,50)
    for indx in range(len(lines)):
        for rho,theta in lines[indx]:
            a = np.cos(theta)
            b = np.sin(theta)
            x0 = a*rho
            y0 = b*rho
            x1 = int(x0 + 1000*(-b))
            y1 = int(y0 + 1000*(a))
            x2 = int(x0 - 1000*(-b))
            y2 = int(y0 - 1000*(a))
            cv2.line(src_img,(x1,y1),(x2,y2),(0,255,0),2)

    #円の検出と描画
    circles = cv2.HoughCircles(edges,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)
    circles = np.uint16(np.around(circles))
    
    for i in circles[0,:]:
        # draw the outer circle
        cv2.circle(src_img,(i[0],i[1]),i[2],(255,0,0),2)
        # draw the center of the circle
        cv2.circle(src_img,(i[0],i[1]),2,(0,0,255),3)

    cv2.imshow("result",src_img)
    cv2.waitKey(0)

実装結果のまとめは下記のgithubに保存している。

github.com

より複雑な特徴量計算(HOG/harr-likeなど)によるもの、DeepLearningを用いたものについても今後記載する。