Unity で 3D 画像特徴可視化ツールを作ってみた

Unity を使って、画像のピクセル情報をヒートマップ上に可視化し、画像の解析に活かすことを目的としたツールを作ってみました。

01_overview

今回のコードは github 上で公開しています。
unity_3d_image_analyzer

経緯

業務で画像の解析をする際、RGB の色情報や HSV の色相・明度・彩度といった手軽に取得できる情報を見ることから始め、目的となる画像特徴の取得に向けて複雑なアルゴリズムの構築や、機械学習を行っていきます。
通常は2次元のまま画像を見ていくのですが、高さ情報を加え、3次元として見ることができれば新たな視点から画像特徴を知ることができるのではないかと思い、Unity の習作も兼ねて本ツールを作ってみることにしました。

画面仕様と簡単な使い方を決める

まず、簡単に使い方と画面を決めます。
画面は、以下のような形で、

  • Load で画像 (動画) ファイルを読み込み、3D 空間上に表示。
  • Point Cloud チェックボックスで R, G, B, H, S, V (, Off) から一つ注目したい情報を選択。
     → 選択した情報に対する点群でヒートマップ表示。
  • 動画ファイルも読み込むことが可能で、その場合は Play, Stop で再生/停止が可能。

という操作をできるようにします。

02_window

実装

メッシュ部の実装について要点を絞って解説します。

点群の描画

Unity における点群は、Mesh オブジェクトを作り、上位の GameObject の MeshFilter に設定することで描画することができます。

( ParticleManager.cs より抜粋 )

// メッシュ紐づけ用の GameObject を作って、親オブジェクトに紐づけ
GameObject meshPart = new GameObject("Mesh_" + y);
meshPart.AddComponent<MeshFilter>();
meshPart.AddComponent<MeshRenderer>();
meshPart.transform.parent = this.meshParent.transform; 

// メッシュ構築
Mesh mesh = new Mesh();
mesh.SetVertices(data.Vertics[y]);    // 点群の点毎の頂点 (Vector3)
mesh.SetIndices(data.Indices[y], MeshTopology.Points, 0);    // 点毎のインデックス番号
mesh.SetColors(data.Colors[y]);    // 点毎の色
mesh.SetTriangles(data.Triangles[y], 0);    // 面を構築する三角形集合情報

// マテリアルとメッシュを設定
meshPart.GetComponent<Renderer>().material = this.matVertex;
meshPart.GetComponent<MeshFilter>().mesh = mesh;

メッシュのオブジェクトには各頂点の座標、インデックス番号、色をそれぞれ設定することで点群を描画させることができます。
ただ、これだと点の集合の情報しかないので、面を構築するために三角形集合情報を追加で設定する必要があります。

メッシュの面を構築

メッシュの面は、三角形を基準として、複数の三角形を組み合わせることで構築することができます。
今回の場合、

  • 画像サイズ … (横) N pixel × (縦) M pixel
  • 画像左下が 0 番目、以降順に横に並ぶ

としたとき、

  1. 0 番目 → N 番目 → (N + 1) 番目 → 0 番目
  2. 0 番目 → (N + 1) 番目 → 1 番目 → 0 番目

の順でそれぞれ左回りに三角形を書くことで、一つの枠 (四角形) を描画することができます。

03_triangles

これを順に組み合わせることで、目的のメッシュの描画ができます。
なお、左回りの三角形一つでは表面だけしか描かれないため、逆回りの三角形も合わせて両面表示にしています。

( ParticleManager.cs より抜粋 )

// 横一列分
for (int midx = 0; midx < this.MeshWidth; midx++) 
{
    // 表面
    this.triangles[y][0 + midx * 6] =         0 + midx;
    this.triangles[y][1 + midx * 6] = width + 0 + midx;
    this.triangles[y][2 + midx * 6] = width + 1 + midx;
    this.triangles[y][3 + midx * 6] =         0 + midx;
    this.triangles[y][4 + midx * 6] = width + 1 + midx;
    this.triangles[y][5 + midx * 6] =         1 + midx;

    // 裏面
    this.triangles[y][0 + midx * 6 + this.MeshWidth * 6] =         1 + midx;
    this.triangles[y][1 + midx * 6 + this.MeshWidth * 6] = width + 1 + midx;
    this.triangles[y][2 + midx * 6 + this.MeshWidth * 6] =         0 + midx;
    this.triangles[y][3 + midx * 6 + this.MeshWidth * 6] = width + 1 + midx;
    this.triangles[y][4 + midx * 6 + this.MeshWidth * 6] = width + 0 + midx;
    this.triangles[y][5 + midx * 6 + this.MeshWidth * 6] =         0 + midx;
}

完成形

今回作成したコードは github 上に置きましたので、興味がある方はどうぞ。
unity_3d_image_analyzer
( 詳しい使い方等は README に書きましたのでそちらを参照ください。)

読み込んだ画像に対して、各ピクセルごと RGB もしくは HSV のうち1つに注目し、メッシュ上の色および高さに変換して描画します。

例えば、次のような花火の画像を読み込ませ、HSV の S 情報のメッシュを作ると、

01_overview

のように花火部分が奇麗に盛り上がりました。

同様に、夕景写真について、RGB の R 情報のメッシュを作ると、

04_overview_02

のようになります。

TO DO

実はこのツール、動画も読み込めるようにしてみたのですが、

05_result_movie

ベストエフォートな仕組みで読み込んでいるので、非常に更新が遅く (動画再生に追従もしていない) 、あまり実用に堪えない感じですね。
うまく動画と合わせて可視化できると面白いのですが、メッシュデータのメモリ管理など、高速化していくには課題が多いようです。。

まとめ

Unity で画像の特徴を 3D で可視化できるようなツールを作ってみました。
実際に業務で使うには色々足りていない感じですが、画像の見方が広がったので、今後も色々試していきたいと思います。