【Python/OpenCV】HoG特徴+SVM識別器の自作・検証

この記事では、Python版OpenCVでHoG特徴+SVM識別器(分類器・xmlファイル)を作成する方法をソースコード付きで解説します。

HoG特徴+SVM識別器の作成

HoG SVMは、物体検出に使われる識別器の1つです。
その名の通り、HoG特徴量とSVM(サポートベクタマシン)を組み合わせて識別器を作成します。

人検出などでは明暗差よりも輪郭情報を捉えたほうが検出精度が良いとされています。
そのため、人検出はHaar-like特徴よりもHoG特徴をよく使います。

画像処理ライブラリOpenCVにも実装されているので、今回はそちらを利用して識別器を作成してみます。

関連記事
HoG SVMの詳細 HoG SVMによる人間検出の原理
HoG特徴の原理 HoG特徴量の原理・計算式
SVMの原理 サポートベクターマシンの原理・計算式

ソースコード①(Python3+OpenCV3.4)

サンプルプログラムのソースコードです。

識別器の作成

# -*- coding: utf-8 -*
import os, glob, sys
import cv2
import numpy as np

# Hog特徴量の計算
def calc_hog(img_paths, bin_n=32):
    hists = []
    for img_path in img_paths:
        # 画像をグレースケールで読み込み
        gray = cv2.imread(img_path, 0)
        # ソーベルフィルタで縦・横方向のエッジ画像を生成
        gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0)
        gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1)
        # エッジ勾配の角度と大きさを算出
        mag, ang = cv2.cartToPolar(gx, gy)
        # 勾配方向の量子化(16方向)
        bins = np.int32(bin_n*ang/(2*np.pi))
        # 勾配方向ヒストグラムを計算
        hist = np.bincount(bins.ravel(), mag.ravel(), bin_n)
        hists.append(hist)

    return np.array(hists, np.float32)

def main():
    # 正解画像・非正解画像のファイルパスを取得
    pos_img_paths = glob.glob('pos/*')
    neg_img_paths = glob.glob('neg/*')
    
    # Hog特徴量の計算
    pos_hogs = calc_hog(pos_img_paths)
    neg_hogs = calc_hog(neg_img_paths)
    hogs = np.r_[pos_hogs, neg_hogs]

    # ラベル用配列の生成(正解:1, 非正解:0)
    pos_labels = np.ones(len(pos_img_paths), np.int32)
    neg_labels = np.zeros(len(neg_img_paths), np.int32)
    labels = np.array([np.r_[pos_labels, neg_labels]])

    # Hog特徴量をSVMで学習
    svm = cv2.ml.SVM_create()
    svm.setType(cv2.ml.SVM_C_SVC)
    svm.setKernel(cv2.ml.SVM_RBF)
    svm.setGamma(5.4)
    svm.setC(2.7)
    svm.setTermCriteria((cv2.TERM_CRITERIA_COUNT, 100, 1.e-06))
    svm.train(hogs, cv2.ml.ROW_SAMPLE, labels)
    svm.save('hog_train.xml') # 自作識別器の出力


if __name__ == '__main__':
    main()

識別器のテスト

# -*- coding: utf-8 -*
import os, glob, sys
import cv2
import numpy as np

# Hog特徴量の計算
def calc_hog(img_paths, bin_n=32):
    hists = []
    for img_path in img_paths:
        # 画像をグレースケールで読み込み
        gray = cv2.imread(img_path, 0)
        # ソーベルフィルタで縦・横方向のエッジ画像を生成
        gx = cv2.Sobel(gray, cv2.CV_32F, 1, 0)
        gy = cv2.Sobel(gray, cv2.CV_32F, 0, 1)
        # エッジ勾配の角度と大きさを算出
        mag, ang = cv2.cartToPolar(gx, gy)
        # 勾配方向の量子化(16方向)
        bins = np.int32(bin_n*ang/(2*np.pi))
        # 勾配方向ヒストグラムを計算
        hist = np.bincount(bins.ravel(), mag.ravel(), bin_n)
        hists.append(hist)

    return np.array(hists, np.float32)

def main():
    # 正解画像・非正解画像のファイルパスを取得
    test_img_paths = glob.glob('test/*')

    # 自作識別器の読み込み
    svm = cv2.ml.SVM_load("hog_train.xml")
    
    # テスト画像で検証 
    hist = calc_hog(test_img_paths)
    pred = svm.predict(hist)
    result = pred[1]
    for (i, img_path) in enumerate(test_img_paths):
        print(img_path, result[i][0])

if __name__ == '__main__':
    main()

ソースコード②(Python3+OpenCV3.4)

サンプルプログラムのソースコードです。
こちらでは、Hog特徴量の計算をcv2.HOGDescriptorメソッドで行います。

cv2.HOGDescriptor

cv2.HOGDescriptorでは、グレースケール画像のHog特徴を計算できます。

hog = cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, nbins)
hog.compute(img)
パラメータ 説明
win_size 検出窓サイズ
block_size ブロックサイズ
block_stride ブロックの移動量(セルサイズの倍数にする)
cell_size セルサイズ
nbins 1セル毎のビン数
img グレースケール画像(8bit)

識別器の作成

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

# Hog特徴の計算とラベリング
def calc_hog(hog, pos_n, neg_n, win_size, cnt=1):
    train = []
    label = []
    # 正解画像・不正解画像のHog特徴を計算してラベル付け
    for i in range(cnt, pos_n):
        img = cv2.imread('pos/' + str(i) + '.jpg', 0)
        img = cv2.resize(img, (win_size))
        train.append(hog.compute(img)) # 特徴量の格納
        label.append(1)                # 正解データのラベルは1

    for i in range(cnt, neg_n):
        img = cv2.imread('neg/' + str(i) + '.jpg', 0)
        img = cv2.resize(img,(win_size))
        train.append(hog.compute(img)) # 特徴量の格納
        label.append(0)                # 非正解データのラベルは0
    
    return np.array(train), np.array(label, dtype=int)


def main():
    # Hog特徴のパラメータ
    win_size = (120, 60)
    block_size = (16, 16)
    block_stride = (4, 4)
    cell_size = (4, 4)
    bins = 9
    pos_n = 3 # 正解画像の枚数
    neg_n = 3 # 不正解画像の枚数

    # Hog特徴の計算とラベリング
    hog = cv2.HOGDescriptor(win_size, block_size, block_stride, cell_size, bins)
    train, label = calc_hog(hog, pos_n, neg_n, win_size)

    # Hog特徴からSVM識別器の作成
    svm = cv2.ml.SVM_create()
    svm.setKernel(cv2.ml.SVM_LINEAR)
    svm.setType(cv2.ml.SVM_C_SVC)
    svm.setC(0.5)
    svm.train(train, cv2.ml.ROW_SAMPLE, label)
    svm.save('hog_train.xml')

if __name__ == "__main__":
    main()

識別器のテスト

準備中…

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

def main():
    svm = cv2.ml.SVM_load("hog_train.xml")

if __name__ == "__main__":
    main()
関連記事
PythonでOpenCV入門 サンプル集
【Python】画像処理プログラミング入門
【画像処理入門】アルゴリズム&プログラミング
参考文献1 OpenCVでSVMとHOG特徴量を使って人を検出するのを試みてみる
参考文献2 HOG特徴量を用いたポケモンのアイコン画像判別
関連記事

コメント

  1. k より:

    識別機を作成した後の使い方を教えて頂けないでしょうか。
    宜しくお願い致します。

    • 管理人 より:

      ※k様

      コメントありがとうございます。
      作成した識別器の扱い方ですが、私も現在調査中でうまく行っていません。
      申し訳ないのですが、分かり次第、追記したいと思います。
      もしk様が使い方がわかれば教えてくださると嬉しいです。

      • k より:

        返信ありがとうございます。
        このサイトにはいつもお世話になっております。
        機械学習を研究に使用しておりまして、現在勉強中です。
        分かりましたらお知らせお知らせ致します。
        ご丁寧にありがとうございました。

  2. T より:

    自作識別器の出力はどこに出力されるのですか?

    • 管理人 より:

      ※T様

      コメントありがとうございます。
      自作識別器(hog_train.xml)はPythonスクリプトと同じフォルダ内に出力されます。

      • T より:

        出力されていない場合の対処法等わかりますか??

        • 管理人 より:

          ※T様
          コメントありがとうございます。
          エラーメッセージ等は出てないでしょうか。
          出ていなければ、pos、negフォルダ内の学習用画像を読み込めていないかもしれません。

          • T より:

            返信ありがとうございます。
            こちらの手違いで読み込めていませんでした。
            お手数かけました。
            ありがとうございます。

          • 管理人 より:

            ※T様
            コメントありがとうございます。
            無事解決されたようで何よりです。