カメラ映像を取り込む
まずWebCamTexture
からの映像をRawImage
上に表示させます。
手順やスクリプト等は以下参考から。
MEMO
Texture2D
extendsTexture
WebCamTexture
extendsTexture
ピクセルを取り扱うGetPixels
などのメソッドは共通して使えるみたいです。
RawImage
を追加するとCanvas
の子要素になるほか、EventSystem
もHierarchyに追加されます。
このRawImage
内のコンポーネントとしてスクリプトを追加し、名前をWebCamera.cs
としておきます。
実行するとGame画面内でRawImage
が表示されないことがあるが、ここではとりあえずCanvas
のRender Modeを World Space にしておきます。
UnityのUIについては不勉強のためあとで要確認……
表示上の縦横比を変更する
位置やサイズの変更は、GameObjectはTransform
で、UIに関してはRectTransform
を用いるっぽいです。
ここではRectTransform
のWidthとHeightを変更してみます。とはいえ、映像入力そのままのサイズにしてしまうとScene内のスケールが謎なことになる(Unityの数値表示はメートル単位)ので、Heightを1としたWidth比率を設定することで縦横比をあわせてみることに。
- WebCamera.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class WebCamera : MonoBehaviour { public int width = 640; public int height = 480; private static int FPS = 30; private WebCamTexture webCamTexture; private RawImage rawImage; private RectTransform rectTransform; void Start() { // Webカメラの開始 this.webCamTexture = new WebCamTexture(this.width, this.height, FPS); // RawImageにカメラのテクスチャを貼り付ける this.rawImage = GetComponent<RawImage>(); this.rawImage.texture = this.webCamTexture; // アスペクト比 float aspect = (float)this.width / (float)this.height; // RectTransformのWidthを変えてアスペクト比をあわせる(heightは常に1.0f) this.rectTransform = GetComponent<RectTransform>(); this.rectTransform.sizeDelta = new Vector2(aspect, 1.0f); // カメラ再生開始 this.webCamTexture.Play(); } }
アスペクト比を直接RectTransform.sizeDelta
にVector2
で設定すればよさそうですね。正しい使い方かどうかはわかりません。
正方形に切り出す
最終的に機械学習系のモデルに入力することを考え、映像を縦横が同一サイズの正方形にトリミングして取り出してみます。
具体的には、RawImage
からテクスチャを正方形状態で切り出し、動的生成したメッシュに貼り付けて表示させることを目指します。
MEMO
マテリアル
=シェーダー
+テクスチャ
テクスチャは画像ファイルなどから生成できるColor
型の配列みたいなもの
シェーダーはテクスチャをいかに描画するか規定する関数のようなもの、手を出したら廃人になるってばっちゃが言ってた
切り出したあとに貼り付けて表示するためのメッシュの生成については以下を参考に。
このメッシュ生成をWebCamera.cs
内に組み込みます。
// 以下をStart()内に追加する // Mesh mesh = new Mesh(); // 頂点座標 // 第3引数(Z = -0.01f)でRawImageの少し手前側に配置 mesh.vertices = new Vector3[] { new Vector3(-0.5f, -0.5f, -0.01f), // 左下 new Vector3(-0.5f, 0.5f, -0.01f), // 左上 new Vector3(0.5f , -0.5f, -0.01f), // 右下 new Vector3(0.5f , 0.5f, -0.01f), // 右上 }; // UV座標 mesh.uv = new Vector2[] { new Vector2(0, 0), // 左下 new Vector2(0, 1), // 左上 new Vector2(1, 0), // 右下 new Vector2(1, 1), // 右上 }; // インデックス配列 mesh.triangles = new int[] { 0, 1, 2, 1, 3, 2, }; // 作成したメッシュを指定 GetComponent<MeshFilter>().sharedMesh = mesh;
前提としてRawImage
はPosZ = 0の位置に置いてあり、Main Camera
はZ = -2のところからZ+方向を向いているという構成です。
ということで、vertices
内のVector3
指定において位置を少しだけカメラ寄りの-0.01fにすることで、動的生成されたメッシュがRawImage
にオーバーレイするような形にしていきます。
テクスチャはフレームアップデート毎に切り出し → 別の貼り付け用のテクスチャオブジェクトにコピー → メッシュのマテリアルのテクスチャを置き換え、という流れで更新します。
- WebCamera.cs
void Start() { // Start()内に以下のみ追記 // メッシュに貼り付ける正方形テクスチャの準備 // カメラ映像の縦サイズ基準で作成 this.rectTexture = new Texture2D(this.height, this.height, TextureFormat.RGBA32, false); } void Update() { if (this.width >= this.height) { // カメラ映像が正方形または横長(PCやタブレット):縦サイズに合わせて横はカット int margin = (this.width - this.height) / 2; // GetPixels (x, y, width, height) で切り出し Color[] rectPixels = this.webCamTexture.GetPixels(margin, 0, this.height, this.height); this.rectTexture.SetPixels(rectPixels); } else { // カメラ映像が縦長(スマホなど):縦サイズに合わせて横は余白 int margin = (this.height - this.width) / 2; // 横方向のみマージン部を除いてコピー for (int y = 0; y < this.height; y++) { for (int x = margin; x < this.width - margin; x++) { Color c = this.webCamTexture.GetPixel(x, y); this.rectTexture.SetPixel(x, y, c); } } } // テクスチャに反映 > メッシュのマテリアルに反映 this.rectTexture.Apply(); GetComponent<MeshRenderer>().material.mainTexture = this.rectTexture; }
この時点ではマテリアルが存在していないので、インスペクタから追加します。
特に編集しなくていいのでそのままメッシュレンダラのElement 0に設定します。
シェーダーも指定しないと描画できないので、「Unlit/Texture」を選択します。
これにしておくとスクリプト上での描画がうまいこと反映できるらしいです。
「Unlit/Texture」は、テクスチャを乗せるだけの超絶シンプルシェーダーだそう。
テクスチャ描画関連として以下も参考に。
ここまで一通りで実行すると以下のようになりました。
RawImage
のColor設定でアルファを入れておくことで、元映像が少し透けて中央の正方形メッシュ部分だけが浮き出たように見せることができました。