先程のエントリの続きとして、ジェスチャー画像のHOG特徴量を算出し、 その結果を、k近傍することで、手話の画像認識を行います。
画像処理(画像認識) - 「密なSIFT(HOG : Histogram of Oriented Gradients )」算出 - end0tknr's kipple - 新web写経開発
pythonでのk近傍法( k-nearest neighbor algorithm, k-NN ) - end0tknr's kipple - 新web写経開発
前準備 - 訓練データと、確認用データ
に記載されている通り、
http://www.idiap.ch/resource/gestures/data/shp_marcel_test.tar.gz
の uniform/ 以下にある {A,B,C,Five,Point,V} の 画像を train と test dirにcopy 。
次にのように一気に行うこともできます
$ cd ~/tmp/VISION/chap8 $ mkdir train $ mkdir test $ wget http://www.idiap.ch/resource/gestures/data/shp_marcel_test.tar.gz $ tar -xvf shp_marcel_test.tar.gz $ mv Marcel-Test/*/uniform/*uniform[0-2]?.ppm train $ mv Marcel-Test/*/uniform/*.ppm test
STEP.1 各画像の HOG特徴量を算出
#!/usr/local/bin/python # -*- coding: utf-8 -*- from PIL import Image import os import dsift import numpy ## http://www.vlfeat.org/download/vlfeat-0.9.20.tar.gz SIFT_CMD = '/home/endo/local/vlfeat/sift' def main(): d = 'test' # test用data imlist = [os.path.join(d,f) for f in os.listdir(d) if f.endswith('.ppm')] d = 'train' # 訓練用data imlist += [os.path.join(d,f) for f in os.listdir(d) if f.endswith('.ppm')] # 50x50sizeで、「密なSIFT記述子(HOG)」算出 for filename in imlist: featfile = filename[:-3]+'dsift' process_image_dsift(filename,featfile,10,5,resize=(50,50)) # 画像から「密なSIFT記述子(HOG)」を抽出し、fileに保存. # args size=特徴量size, steps=grid間隔, resize=画像resize用のタプル # force_orientation= True →最大の局所勾配の方向で記述子が正規化 # False→全て上向き def process_image_dsift(imagename, resultname, size=20, steps=10, force_orientation=False, resize=None): im = Image.open(imagename).convert('L') # file open後、グレースケール化 if resize != None: im = im.resize(resize) m, n = im.size if imagename[-3:] != 'pgm': im.save('tmp.pgm') # pgmファイルを作成 imagename = 'tmp.pgm' # frameを作成し一時fileに保存 scale = size / 3.0 x, y = numpy.meshgrid(range(steps, m, steps), range(steps, n, steps)) xx, yy = x.flatten(), y.flatten() frame = numpy.array([xx, yy, scale * numpy.ones(xx.shape[0]), numpy.zeros(xx.shape[0])]) numpy.savetxt('tmp.frame', frame.T, fmt='%03.3f') if force_orientation: cmmd = str(SIFT_CMD +" "+ imagename + " --output=" + resultname + " --read-frames=tmp.frame --orientations") else: cmmd = str(SIFT_CMD +" "+ imagename + " --output=" + resultname + " --read-frames=tmp.frame") os.system(cmmd) print 'processed', imagename, 'to', resultname if __name__ == '__main__': main()
↑こう書くと、test , train dir 以下にある画像のHOGを算出し、 結果を *.dsift ファイルに保存します
STEP.2 k-NNによる画像認識
#!/usr/local/bin/python # -*- coding: utf-8 -*- import numpy import os def main(): features, labels = read_gesture_features_labels('train/') test_features, test_labels = read_gesture_features_labels('test/') classnames = numpy.unique(labels) # k近傍法を試す k = 1 knn_classifier = KnnClassifier(labels, features) res = numpy.array([ knn_classifier.classify(test_features[i], k) for i in range(len(test_labels)) ]) # 精度 acc = sum(1.0 * (res == test_labels)) / len(test_labels) print 'Accuracy:', acc print_confusion(res, test_labels, classnames) # HOGが登録された *.dsift の filename list 作成 def read_gesture_features_labels(path): featlist = [ os.path.join(path, f) for f in os.listdir(path) if f.endswith('.dsift') ] # 特徴量を読込み features = [] for featfile in featlist: l, d = read_features_from_file(featfile) features.append(d.flatten()) features = numpy.array(features) # file名からラベルを作成 labels = [featfile.split('/')[-1][0] for featfile in featlist] # print labels return features, numpy.array(labels) # 特徴量を読込み行列形式で返す def read_features_from_file(filename): f = numpy.loadtxt(filename) return f[:, :4], f[:, 4:] # 特徴点の配置と記述子 def print_confusion(res, test_labels, classnames): n = len(classnames) # 混同行列 class_ind = dict([(classnames[i], i) for i in range(n)]) confuse = numpy.zeros((n, n)) for i in range(len(test_labels)): confuse[class_ind[res[i]], class_ind[test_labels[i]]] += 1 print 'Confusion matrix for' print classnames print confuse class KnnClassifier(object): def __init__(self,labels,samples): """ 教師データを使って分類器を初期化する """ self.labels = labels self.samples = samples def classify(self,point,k=3): """ pointを教師データ中のk個の最近傍を使って分類し、 ラベルを返す """ # 全教師データへの距離を計算する dist = numpy.array([L2dist(point,s) for s in self.samples]) # ソートする ndx = dist.argsort() # k個の最近傍を保持するのに辞書を用いる votes = {} for i in range(k): label = self.labels[ndx[i]] votes.setdefault(label,0) votes[label] += 1 return max(votes, key=lambda x: votes.get(x)) def L2dist(p1,p2): return numpy.sqrt( sum( (p1-p2)**2) ) if __name__ == '__main__': main()
↑こう書くと↓こう表示されます
$ python 0_8.1.3_2.knn_g.py Accuracy: 0.617021276596 Confusion matrix for [ 'A' 'B' 'C' 'F' 'P' 'V'] [['A' 23. 2. 2. 1. 0. 2.] ['B' 2. 20. 2. 5. 4. 1.] ['C' 0. 1. 23. 1. 0. 0.] ['F' 0. 1. 0. 20. 0. 0.] ['P' 0. 5. 3. 1. 12. 8.] ['V' 4. 0. 6. 1. 20. 18.]]
特に右から2列目の結果(12と30)から、P が Vと混同されやすいことが分かります。