LoginSignup
14
1

More than 1 year has passed since last update.

EvisionのDNN.SegmentationModelを使ってセグメンテーション

Last updated at Posted at 2022-12-13

はじめに

本記事は Qiita AdventCalendar2022 Elixir vol9 14日目の記事です

Evisionシリーズの5つ目の記事になります
このシリーズは技術評論社の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.SegmentationModelを使ってセグメンテーションを行う方法を紹介します

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分野の多くの学習済みモデルを使用できる

セグメンテーションについて

セグメンテーション(Segmentation)とは、画像に映っているオブジェクトのクラスごとに領域を分割するタスクです。
OpenCVではじめよう ディープラーニングによる画像認識 (p.410)

使用するモジュールはDNN.DetectionMoelになります

使用するモデル

deeplab-v3
学習済みデータはこちらの7.5をダウンロードしてください

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

setup

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

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

データの準備

aliasでモジュール名を短くしています import numpy as npと同じですね
weigthsで学習済みデータのファイルパスを指定します
baseでダウンロードしたフォルダパスを貼っておくと便利です

alias Evision, as: Ev
base = "7.3をダウンロードしたフォルダの絶対パスを貼り付けてください"
weights = base <> "7.5/deeplab-v3/optimized_graph_voc.pb"

classesで推論結果のラベル名を取得するためにクラス名のリストを作ります
colorsで推論結果を囲うためにラベル毎にランダムな色のリストを作ります
色リストの一番最初はラベル外なので黒を入れておきます
またあとでNx.takeで使うので Nx.tensorでテンソル化しておきます

classes =
  (base <> "7.5/deeplab-v3/voc.names")
  |> File.read!()
  |> String.split("\n")

colors =
  Enum.with_index(classes, fn _class, index ->
    case index do
      0 -> [0, 0, 0]
      _ -> [Enum.random(0..255), Enum.random(0..255), Enum.random(0..255)]
    end
  end)
  |> Nx.tensor()

モデルの読み込み

学習済みデータを読み込んで、モデルを構築します
segmentationModel/1で先程の学習済みデータとオプションでネットワークの設定ファイルのパスを渡します

モデルの次はsetInputParams/2でハイパーパラメータを設定します
パラメーターはそれぞれ以下を設定しています

  • scale -> meanを引いた後に画像のRGB値を0~1.0の範囲に変換
  • size -> 入力画像サイズを513x513にする
  • mean -> 入力画像の各RGB値から127.5を引く
  • swapRB -> BGR形式をRGBに入れ替える
  • crop -> アスペクト比をせずにリサイズ
model =
  Ev.DNN.SegmentationModel.segmentationModel(weights)
  |> Ev.DNN.SegmentationModel.setInputParams(
    scale: 1.0 / 127.5,
    size: {513, 513},
    mean: {127.5, 127.5, 127.5},
    swapRB: true,
    crop: false
  )

推論の実行

画像を読み込みます
書籍のサンプルにあった自転車の画像を使います

image = Ev.imread(base <> "7.5/deeplab-v3/bicycle.jpg")

bicycle.jpg

segment/3でセグメンテーションを実行します

mask =
  Ev.DNN.SegmentationModel.segment(model, image)

各画素のクラスIDが返ってきますが、画像としてはRGBではなく、濃淡になってしまいます

スクリーンショット 2022-12-05 16.18.23.png

こちらを上の方で定義したランダムの色リストに変換します
流れは以下のようになっています

  1. Evision.MatからNx.tensorに変換
  2. Evision.Backendだとtakeが使えないのでNx.BinaryBackendに変換する
  3. Nx.takeでクラスの数作成した色から該当するインデックスの値だけでテンソルを作る
  4. Evisionで扱えるようにf16からu8にデータタイプを変換
  5. Evision.Mat形式に変換
mask = 
  mask
  |> Ev.Mat.to_nx(Nx.BinaryBackend)
  |> then(&Nx.take(colors, &1))
  |> Nx.as_type(:u8)
  |> Ev.Mat.from_nx_2d()

色がついてだいぶわかりやすくなりました

スクリーンショット 2022-12-05 16.36.35.png

セグメンテーション結果と画像をアルファブレンド

セグメンテーション結果から作ったマスクを半透明にして元画像に重ねます(アルファブレンド)
処理はそれぞれ以下のようになっています

  1. 元画像のサイズを取得
  2. マスクが513x513なので元画像とおなじになるようにリサイズ
  3. オプションに補間方法(interpolation)をINTER_NEAREST(最近傍補間)で指定
  4. addWeighted でアルファブレンド
{h, w, _} = Ev.Mat.shape(image)
color_mask = Ev.resize(mask, {w, h}, interpolation: Ev.cv_INTER_NEAREST())

alpha = 0.4
beta = 1.0 - alpha
blend = Ev.addWeighted(image, 0, color_mask, beta, 0.0)

スクリーンショット 2022-12-05 16.55.29.png

元画像とマスクも並べてみましょう

Kino.Layout.grid(
  [
    Ev.imencode(".png", image) |> Kino.Image.new(:png),
    Ev.imencode(".png", color_mask) |> Kino.Image.new(:png),
    Ev.imencode(".png", blend) |> Kino.Image.new(:png)
  ],
  columns: 3
)

スクリーンショット 2022-12-05 16.56.49.png

最後に

SegmentationModelを使用して簡単にセグメンテーションができました

clipで切り抜きや、各種フィルターで人をぼかす、人以外をぼかす等色々できそうですね
本記事は以上になりますありがとうございました

全コード

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

weights = base <> "7.5/deeplab-v3/optimized_graph_voc.pb"

classes =
  (base <> "7.5/deeplab-v3/voc.names")
  |> File.read!()
  |> String.split("\n")

colors =
  Enum.with_index(classes, fn _class, index ->
    case index do
      0 -> [0, 0, 0]
      _ -> [Enum.random(0..255), Enum.random(0..255), Enum.random(0..255)]
    end
  end)
  |> Nx.tensor()

model =
  Ev.DNN.SegmentationModel.segmentationModel(weights)
  |> Ev.DNN.SegmentationModel.setInputParams(
    scale: 1.0 / 127.5,
    size: {513, 513},
    mean: {127.5, 127.5, 127.5},
    swapRB: true,
    crop: false
  )

image = Ev.imread(base <> "7.5/deeplab-v3/bicycle.jpg")

mask =
  Ev.DNN.SegmentationModel.segment(model, image)
  |> Ev.Mat.to_nx(Nx.BinaryBackend)
  |> then(&Nx.take(colors, &1))
  |> Nx.as_type(:u8)
  |> Ev.Mat.from_nx_2d()

{h, w, _} = Ev.Mat.shape(image)
color_mask = Ev.resize(mask, {w, h}, interpolation: Ev.cv_INTER_NEAREST())

alpha = 0.4
beta = 1.0 - alpha
blend = Ev.addWeighted(image, alpha, color_mask, beta, 0.0)

Kino.Layout.grid(
  [
    Ev.imencode(".png", image) |> Kino.Image.new(:png),
    Ev.imencode(".png", color_mask) |> Kino.Image.new(:png),
    Ev.imencode(".png", blend) |> Kino.Image.new(:png)
  ],
  columns: 3
)
14
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
14
1