13
1

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 1 year has passed since last update.

EvisionのDNN.ClassificationModelを使ってクラス分類をする

Last updated at Posted at 2022-12-08

はじめに

本記事は Qiita AdventCalendar2022 Elixir vol2 9日目の記事です

Evisionシリーズの2つ目の記事になります
このシリーズは技術評論社のOpenCVではじめよう ディープラーニングによる画像認識の7章の内容を参考にElixirとEvisionで書き換えて行っています

  1. Livebook + Evision基本編
  2. EvisionのCascadeClassifierで顔認識
  3. EvisionのDNN.ClassificationModelを使ってクラス分類
  4. EvisionのDNN.TextDetectionModelDBでテキスト検出
  5. EvisionのDNN.DetectionModelでYOLOv4を使って物体検知 12/14公開
  6. EvisionのDNN.SegmentationModelでセグメンテーション 12/14公開
  7. YOLOv4の結果を切り取ってEfficientnetで更に分類するシステムをEvisionで書く 12/18公開

Livebook上で画像処理ライブラリOpenCVのElixirラッパーのEvisionのDNN.ClassificationModelを使ってクラス分類を行う方法を紹介します

Livebookについて

Livebook is a web application for writing interactive and collaborative code notebooks.

LivebookはコラボレーションもできるElixir対話的実行環境を提供するWebアプリケーションです

Evisionについて

  • OpenCVのElixirラッパー
  • Port等を使わずに直にElixirから使うことができる
  • Nxのバックエンドとして使用でき、行列演算の高速化(CPU,GPU)ができる
  • Nxデータに相互に変換できる
  • 膨大な画像処理の関数を使用できる
  • DNNモジュールでCV分野の多くの学習済みモデルを使用できる

クラス分類について

クラス分類(Classfication)とは、画像に何が映っているのかをクラスごとに信頼度を出して分類するタスクです。
by OpenCVではじめよう ディープラーニングによる画像認識 (p.380)

使用するモデル

Efficientnet-b7.onnx を使用します
クラスリストと学習済みデータはこちらの7.4をダウンロードしてください

直リンク
https://gihyo.jp/assets/files/book/2022/978-4-297-12775-6/download/7.4.zip

setup

livebookは公式サイトを参考にインストールしてください
livebookを起動してノートブックを作成したらsetupセルに以下を追加して実行してください

Mix.install([
  {:evision, "~> 0.1.21"},
  {:kino, "~> 0.7.0"},
])

データの準備

aliasでモジュール名を短くしています import numpy as npと同じですね
weigthsで学習済みデータのファイルパスを定義します
baseでダウンロードしたフォルダパスを貼っておくと便利です
classesでクラス分類の推論結果は学習時に使用した正解リストのインデックスを返すので、クラス名がわかるように正解リストをリストにします

alias Evision, as: Ev
base = "7.4をダウンロードしたフォルダの絶対パスを貼り付けてください"

weights = base <> "7.4/efficientnet/efficientnet-b7.onnx"

classes =
  (base <> "7.4/efficientnet/imagenet.names")
  |> File.read!()
  |> String.split("\n")

モデルの読み込み

学習済みデータを読み込んで、モデルを構築します
classificationModel/1で先程の学習済みデータのパスを渡します
次にsetInputParams/2でハイパーパラメータを設定します
パラメーターはそれぞれ以下を設定しています

  • scale -> 画像のRGB値を0~1.0の範囲に変換
  • size -> 入力画像サイズを600x600にする
  • mean -> 各RGB値から指定した平均値を引いて照度変化に強くする
  • swapRB -> BGR形式をRGBに入れ替える
  • crop -> アスペクト比を保持してリサイズする
model =
  Ev.DNN.ClassificationModel.classificationModel(weights)
  |> Ev.DNN.ClassificationModel.setInputParams(
    scale: 1.0 / 255.0,
    size: {600, 600},
    mean: {124.675, 116.28, 103.53},
    swapRB: true,
    crop: true
  )

推論の実行

画像を読み込みます
使うのはYOLOでおなじみの犬と自転車が写っているアレです

image = Ev.imread(base <> "images/dog.jpg")

dog.jpg

classify/2で推論を実行します

{class_id, confidence} = Ev.DNN.ClassificationModel.classify(model, image)

実行するとクラスIDと正当確率が返ってきます

{249, 8.626869201660156}

推論結果を画像に描画

推論結果をクラスIDからクラス名に変換し、確率も小数点以下3まで表示するようにして、putText/6で元画像に描画します

putTextの引数はそれぞれ以下のようになっています

  • 画像
  • 書き込むテキスト
  • 書き込む座標(左下)
  • フォント
  • 文字の大きさ
  • 文字の色
Ev.putText(
  image,
  "#{Enum.at(classes, class_id)}(#{Float.round(confidence, 3)})",
  {30, 30},
  Ev.cv_FONT_HERSHEY_TRIPLEX(),
  1.0,
  {255, 255, 255}
)

スクリーンショット 2022-12-02 13.34.52.png

おまけ上位5つを表示

他の候補も見たい場合は predict/2 を使います、
処理は上から

  1. 推論実行
  2. 結果がMatのリスト([Mat])で返ってくるので1つ目の要素を取得
  3. Matだと行列処理がやりにくいのでNxに変換
  4. shapeが{1, 1000} なので squueeze で {1000}に変換
  5. 出力されたデータを0~1の範囲の確率の値に変換(Softmax)

となっています

confidences =
  Ev.DNN.ClassificationModel.predict(model, image)
  |> List.first()
  |> Ev.Mat.to_nx(Nx.BinaryBackend)
  |> Nx.squeeze()
  |> then(&Nx.divide(
    Nx.exp(&1), 
    Nx.sum(Nx.exp(&1))
  ))

上記のデータはクラス毎の確率になっているので、以下の処理を行います

  1. Nx.argsort direction: :descで大きい順のインデックスに並び替えます
  2. Enum.take(5)で先頭から5つでリストを作ります
  3. Enum.mapで クラス名と確率のタプルに変換
top5 =
  Nx.argsort(confidences, direction: :desc)
  |> Nx.to_flat_list()
  |> Enum.take(5)
  |> Enum.map(fn c ->
    {Enum.at(classes, c), Nx.to_number(confidences[c])}
  end)

実行すると以下のような結果を得られます

[
  {"malamute, malemute, Alaskan malamute", 0.7833796739578247},
  {"Tibetan mastiff", 0.017758667469024658},
  {"mountain bike, all-terrain bike, off-roader", 0.015341623686254025},
  {"Eskimo dog, husky", 0.006293073762208223},
  {"Siberian husky", 0.0028612350579351187}
]

最後に

ClassificationModelを使用して簡単にクラス分類を行うことができました
他にも試してみたい方はmodel zooやOpenCV zoo で色々試してみてください
本記事は以上になりますありがとうございました

全コード

alias Evision, as: Ev
base = "/Users/shou/livebook_samples/"

weights = base <> "7.4/efficientnet/efficientnet-b7.onnx"

classes =
  (base <> "7.4/efficientnet/imagenet.names")
  |> File.read!()
  |> String.split("\n")

model =
  Ev.DNN.ClassificationModel.classificationModel(weights)
  |> Ev.DNN.ClassificationModel.setInputParams(
    scale: 1.0 / 255.0,
    size: {600, 600},
    mean: {124.675, 116.28, 103.53},
    swapRB: true,
    crop: true
  )

image = Ev.imread(base <> "images/dog.jpg")
{class_id, confidence} = Ev.DNN.ClassificationModel.classify(model, image)

Ev.putText(
  image,
  "#{Enum.at(classes, class_id)}(#{Float.round(confidence, 3)})",
  {30, 30},
  Ev.cv_FONT_HERSHEY_TRIPLEX(),
  0.5,
  {255, 255, 255}
)
13
1
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
13
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?