2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

OpenCV SVMで顔認識を試してみた

Last updated at Posted at 2021-03-09

はじめに

OpenCV SVMを使って、入力顔写真からトランプかプーチンかを判別する処理を実装して試してみましたので、備忘として残します。

開発環境

Ubuntu 18.04.4 LTS
Python 3.6.9
opencv 4.5.1
dlib 19.21.1

入力画像

こちらの方法で、トランプとプーチンの顔写真を収集しました。
学習用にトランプとプーチンの画像それぞれ100枚を使いました。

まず結果から

テストデータとして別に分類しておいた、トランプ、プーチンそれぞれの画像10枚の画像でテストしました。
以下の10枚の画像の内、9枚を正しくトランプと認識しました。
trum-test.png
プーチンと誤認識をしてしまった1枚は、上記赤枠内の画像です。
こちらは、やや横向きの画像となっていますので、機械学習では難しいのかもしれません。

プーチンについても以下の10枚の画像の内、9枚を正しくプーチンと認識しました。
putin-test.png
トランプと誤認識した1枚は、上記赤枠内の画像です。
どうも、少し若い頃の写真なような気がします。

学習処理

入力画像からSIFTで特徴量を抽出し、BoW VocabularyをKmeansで学習します。Vocabulary(クラスタ)数は40にしました。
Vocabularyを学習したら、BoW Vocabularyで表現した特徴量を抽出し、それを使ってSVMで学習します。

以下が該当ソースコードになります。

def train(file_manager, voc_file, svm_file):
    # create a sift and a flann
    sift = cv2.SIFT_create()
    flann = MyMatcher.createFlann()

    # create a BoW KMeans Trainer
    bow_trainer = MyBoW(sift, flann, BOW_NUM_CLUSTERS)

    # add samples to the trainer
    for i in range(BOW_NUM_TRAINING_SAMPLES_PER_CLASS):
        pos_path, neg_path = file_manager.getFile(i)
        bow_trainer.addSample(pos_path)
        bow_trainer.addSample(neg_path)

    # create vocabulary
    bow_trainer.createVocAndSave(voc_file)

    # prepare training data with BoW decriptors
    training_data = util.TrainingData()
    for i in range(SVM_NUM_TRAINING_SAMPLES_PER_CLASS):
        pos_path, neg_path = file_manager.getFile(i)
        pos_img = cv2.imread(pos_path, cv2.IMREAD_GRAYSCALE)
        neg_img = cv2.imread(neg_path, cv2.IMREAD_GRAYSCALE)
        pos_descriptors = bow_trainer.extractDescriptors(pos_img)
        neg_descriptors = bow_trainer.extractDescriptors(neg_img)
        training_data.set(pos_descriptors, 1)
        training_data.set(neg_descriptors, -1)

    # create a SVM and train it
    svm = MySVM()
    svm.config(100)
    data, labels = training_data.get()
    svm.train(data, labels, svm_file)

予測処理

予測はSVMのpredictを呼ぶだけです。学習と予測は別々に呼び出せるようにしたため、予測前に学習データの読み込みを行っています。

def predict(file_manager, voc_file, svm_file):
    # create a sift and a flann
    sift = cv2.SIFT_create()
    flann = MyMatcher.createFlann()

    # create a Bow KMeans Trainer and the load vocaabulary
    bow_trainer = MyBoW(sift, flann, BOW_NUM_CLUSTERS)
    bow_trainer.loadVoc(voc_file)

    # create a SVM and load the data
    svm = MySVM()
    svm.load(svm_file)

    # predict
    for i in range(file_manager.getNumFiles()):
        path = file_manager.getFile(i)
        img = cv2.imread(path)
        gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        descriptors = bow_trainer.extractDescriptors(gray_img)
        result = svm.predict(descriptors)
        logger.debug('file={0}, result={1}'.format(path, result))

その他のソースコード

上記コードでは、OpenCVのオブジェクトをラップしたクラスを作って処理していますので、参考までにそのクラスのコードも下記に載せておきます。

BOW_NUM_TRAINING_SAMPLES_PER_CLASS = 30
SVM_NUM_TRAINING_SAMPLES_PER_CLASS = 100
BOW_NUM_CLUSTERS = 40

class MyBoW:
    def __init__(self, dextractor, dmatcher, cluster_count):
        self._dextractor = dextractor
        self._trainer = cv2.BOWKMeansTrainer(cluster_count)
        self._extractor = cv2.BOWImgDescriptorExtractor(dextractor, dmatcher)

    def addSample(self, path):
        img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
        keypoints, descriptors = self._dextractor.detectAndCompute(img, None)
        if descriptors is None:
            logger.debug('No descriptor genearted')
        else:
            self._trainer.add(descriptors)

    def createVocAndSave(self, data_file=None):
        voc = self._trainer.cluster()
        self._extractor.setVocabulary(voc)
        if data_file is not None:
            with open(data_file, 'wb') as f:
                pickle.dump(voc, f)

    def loadVoc(self, data_file):
        if data_file is not None:
            with open(data_file, 'rb') as f:
                voc = pickle.load(f)
            self._extractor.setVocabulary(voc)

    def extractDescriptors(self, img):
        keypoints = self._dextractor.detect(img)
        return self._extractor.compute(img, keypoints)

class MySVM:
    def __init__(self):
        self._svm = cv2.ml.SVM_create()

    def train(self, training_data, training_labels, data_file):
        aa = np.array(training_data)
        self._svm.train(np.array(training_data), cv2.ml.ROW_SAMPLE,
                        np.array(training_labels))
        if data_file is not None:
            self._svm.save(data_file)

    def config(self, count):
        self._svm.setType(cv2.ml.SVM_C_SVC)
        self._svm.setKernel(cv2.ml.SVM_LINEAR)
        self._svm.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, count, 1e-6))

    def load(self, data_file):
        new_svm = self._svm.load(data_file)
        self._svm = new_svm

    def predict(self, descriptros):
        return self._svm.predict(descriptros)

class MyMatcher:
    def createFlann():
        FLANN_INDEX_KDTREE = 1
        index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
        search_params = {}
        return cv2.FlannBasedMatcher(index_params, search_params)

所感

confidence scoreも取得するように修正しようと思います。

2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?