【Python】 OpenCV ~ 画像認識 ~

■ はじめに

前々からやってみたかった画像認識をやってみたくて勉強してたのだが
いい動画をみつけた。
こちらの動画では、OpenCV を使っているので、環境設定も含めて、まとめる。

勉強になった動画
https://www.youtube.com/watch?v=WSTyoaK6dPU&list=PL1P-NJggk9lbilEP-IHapQRCdYlvX43yC&index=79
https://algorithm.joho.info/programming/python/hog-svm-classifier-py/

OpenCV

* 以下の関連記事を参照のこと

https://dk521123.hatenablog.com/entry/2016/06/27/234046

■ 環境設定

pip install opencv-python
pip install opencv-contrib-python

# conda install opencv-python だとうまくいかなかった

■ 画像認識に使用する画像ファイルについて

* 画像を集めるの大変なので、
 Google画像検索をスクレイピングして、画像を集める
 ⇒ スクレイピングのコードは、以下の関連記事の例3を使用。

https://dk521123.hatenablog.com/entry/2020/06/01/000000

使用上の注意

* ファイル名が日本語の場合、以下のエラーになったので、
 ファイル名は、半角英数字で統一しておくこと

エラー内容

loading_image_path = C:\tmp\sample1\ああ_2.jpg
Traceback (most recent call last):
  File "c:/temp/sample.py", line 102, in <module>
    main()
  File "c:/temp/sample.py", line 50, in main
    image1_list = create_images(loading_image1_path)
  File "c:/temp/sample.py", line 22, in create_images
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.error: OpenCV(4.4.0) C:\Users\xxxxxxxx\AppData\Local\Temp\1\pip-req-build-sxpsnzt6\opencv\modules\imgproc\src\color.cpp:182: error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'

■ サンプル

import glob
import cv2
import numpy as np

# 画像配列の作成
def create_images(loading_image_paths):
  # Hog特徴のパラメータ
  win_size = (64, 64)
  block_size = (16, 16)
  block_stride = (4, 4)
  cell_size = (4, 4)
  bins = 10

  # 画像群の配列を生成
  images = []
  for loading_image_path in loading_image_paths:
    # 画像をロード, グレースケール変換
    # 色反転, 64*64にリサイズ, 1次元配列に変換
    print("loading_image_path = {}".format(loading_image_path))
    img = cv2.imread(loading_image_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.resize(gray, win_size)
    hog = cv2.HOGDescriptor(
      win_size, block_size, block_stride, cell_size, bins)
    image = hog.compute(gray)
    images.append(image)
  return np.array(images, np.float32)

def main():
  # 学習用の画像ファイルの格納先(1~3の3種類)
  # (今回試したのは、火付き蝋燭、火なし蝋燭、ペン)
  LOAD_TRAIN_IMAGE1_PATH = r"C:\tmp\sample1\*"
  LOAD_TRAIN_IMAGE2_PATH = r"C:\tmp\sample2\*"
  LOAD_TRAIN_IMAGE3_PATH = r"C:\tmp\sample3\*"

  # 作成した学習モデルの保存先
  SAVE_TRAINED_DATA_PATH = r"C:\tmp\svm_trained_data.xml"

  # 検証用の画像ファイルの格納先(1~3 の3種類)
  LOAD_TEST_IMAGE1_PATH = r"C:\tmp\test1\*"
  LOAD_TEST_IMAGE2_PATH = r"C:\tmp\test2\*"
  LOAD_TEST_IMAGE3_PATH = r"C:\tmp\test3\*"

  # 学習用の画像ファイルのパスを取得
  loading_image1_path = glob.glob(LOAD_TRAIN_IMAGE1_PATH)
  loading_image2_path = glob.glob(LOAD_TRAIN_IMAGE2_PATH)
  loading_image3_path = glob.glob(LOAD_TRAIN_IMAGE3_PATH)

  # 学習用の画像ファイルをロード
  image1_list = create_images(loading_image1_path)
  image2_list = create_images(loading_image2_path)
  image3_list = create_images(loading_image3_path)
  image_dataset_list = np.r_[
    image1_list, image2_list, image3_list]

  # 正解ラベルを生成
  image1_labels = np.full(len(loading_image1_path), 0, np.int32)
  image2_labels = np.full(len(loading_image2_path), 1, np.int32)
  image3_labels = np.full(len(loading_image3_path), 2, np.int32)
  image_dataset_labels = np.array([np.r_[
    image1_labels, image2_labels, image3_labels]])

  # SVMで学習モデルの作成(カーネル:LINEAR 線形, gamma:1, C:1)
  svm = cv2.ml.SVM_create()
  svm.setType(cv2.ml.SVM_C_SVC)
  svm.setKernel(cv2.ml.SVM_LINEAR)
  svm.setGamma(1)
  svm.setC(1)
  svm.setTermCriteria((cv2.TERM_CRITERIA_COUNT, 100, 1.e-06))
  svm.train(image_dataset_list, cv2.ml.ROW_SAMPLE, image_dataset_labels)

  # 学習結果を保存
  svm.save(SAVE_TRAINED_DATA_PATH)

  # 学習モデルを検証
  # 1 ~ 3のテスト用画像を入力し、画像に書かれた数字を予測
  test_image1_path = glob.glob(LOAD_TEST_IMAGE1_PATH)
  test_image2_path = glob.glob(LOAD_TEST_IMAGE2_PATH)
  test_image3_path = glob.glob(LOAD_TEST_IMAGE3_PATH)
  test_image1_list = create_images(test_image1_path)
  test_image2_list = create_images(test_image2_path)
  test_image3_list = create_images(test_image3_path)
  test_image_list = np.r_[test_image1_list, test_image2_list, test_image3_list]

  # 正解ラベルを生成
  test_image1_labels = np.full(len(test_image1_list), 0, np.int32)
  test_image2_labels = np.full(len(test_image2_list), 1, np.int32)
  test_image3_labels = np.full(len(test_image3_list), 2, np.int32)
  test_image_labels = np.array([np.r_[
    test_image1_labels, test_image2_labels, test_image3_labels]])

  svm = cv2.ml.SVM_load(SAVE_TRAINED_DATA_PATH)
  predicted = svm.predict(test_image_list)

  # 正解ラベル、予想値、正解率を表示
  print("test labels:", test_image_labels)
  print("predicted:", predicted[1].T)
  score = np.sum(test_image_labels == predicted[1].T)/len(test_image_labels[0])
  print("Score:", score)

if __name__ == '__main__':
    main()

出力結果

test labels: [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 2 2 2 2 2 2 2 2 2 2]]
predicted: [[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 2.
  0. 2. 0. 2. 0. 0. 0. 2.]]
Score: 0.71875

# いきなり、7割超になったので、思ったより高い確率なので、ちょっと驚いた

参考文献

https://qiita.com/SatoshiGachiFujimoto/items/94da93f88578b87f6a89

関連記事

scikit-learn ~ 機械学習用ライブラリ・入門編 ~
https://dk521123.hatenablog.com/entry/2020/03/02/233902
スクレイピング ~ Beautiful Soup 4 ~
https://dk521123.hatenablog.com/entry/2020/06/01/000000
JavaOpenCV ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2016/06/27/234046