本日は Unity の技術調査枠です。
今回は前回、前々回のHoloLensでカメラ画像を取得する技術を応用して、予め保存された画像をテクスチャに反映してみます。
bluebirdofoz.hatenablog.com
bluebirdofoz.hatenablog.com
ファイルから画像を取得する方法については以下を参考にします。
rikoubou.hatenablog.com
その3で作成したプロジェクトを流用します。
以下の通り、パス取得と画像バイナリ読み込みの処理をまとめました。
・UtilTest.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; public class UtilTest : MonoBehaviour, IInputClickHandler { /// <summary> /// バイナリ読み込み /// </summary> static public byte[] ReadImageFil(string path) { using (System.IO.FileStream fileStream = new System.IO.FileStream( path, System.IO.FileMode.Open, System.IO.FileAccess.Read)) { System.IO.BinaryReader bin = new System.IO.BinaryReader(fileStream); byte[] values = bin.ReadBytes((int)bin.BaseStream.Length); #if WINDOWS_UWP // ファイルを閉じる(UWPアプリではClose()メソッドは使用不可) binReader.Dispose(); #else // ファイルを閉じる binReader.Close(); #endif return values; } } /// <summary> /// バイナリ->Texture2D変換 /// </summary> static public Texture2D BinaryToTexture(byte[] bytes) { Texture2D texture = new Texture2D (1, 1); texture.LoadImage (bytes); return texture; } /// <summary> /// 画像パスの取得 /// </summary> static public string PictureFilePath() { string filename = string.Format(@"CapturedImage.jpg"); string filePath = System.IO.Path.Combine(PictureFileDirectoryPath(), filename); return filePath; } /// <summary> /// 画像保存ディレクトリパスの取得 /// 実行環境によって参照ディレクトリを変更する /// </summary> static private string PictureFileDirectoryPath() { string directorypath = ""; #if WINDOWS_UWP // HoloLens上での動作の場合、LocalAppData/AppName/LocalStateフォルダを参照する directorypath = Windows.Storage.ApplicationData.Current.LocalFolder.Path; #else // Unity上での動作の場合、Assets/StreamingAssetsフォルダを参照する directorypath = UnityEngine.Application.streamingAssetsPath; #endif return directorypath; } }
その3で作成した CaptureTest.cs を改造して以下の二つの機能を追加します。
- 開始時、既に保存されている画像ファイルがあればテクスチャに表示する。
- キャプチャ時、作成したテクスチャを jpg 出力する。
・CaptureTest.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using HoloToolkit.Unity.InputModule; using System; using System.IO; using System.Linq; using UnityEngine.XR.WSA.WebCam; public class CaptureTest : MonoBehaviour, IInputClickHandler { public GameObject targetObject = null; public Material formatMaterial = null; private PhotoCapture photoCaptureObject = null; private Material changeMaterial = null; /// <summary> /// 開始時処理 /// </summary> void Start() { // 起動時、保存済みの画像ファイルが存在すればテクスチャとして読み込む if (System.IO.File.Exists(UtilTest.PictureFilePath())) { byte[] imageBuff = UtilTest.ReadImageFile(UtilTest.PictureFilePath()); Texture2D imageTexture = UtilTest.BinaryToTexture(imageBuff); RefMaterial(imageTexture); } // AirTap時のイベントを設定する InputManager.Instance.PushFallbackInputHandler(gameObject); } /// <summary> /// キャプチャ起動 /// </summary> void OnPhotoCaptureCreated(PhotoCapture captureObject) { Debug.Log("OnPhotoCaptureCreated"); photoCaptureObject = captureObject; Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); CameraParameters c = new CameraParameters(); c.hologramOpacity = 0.0f; c.cameraResolutionWidth = cameraResolution.width; c.cameraResolutionHeight = cameraResolution.height; c.pixelFormat = CapturePixelFormat.BGRA32; captureObject.StartPhotoModeAsync(c, OnPhotoModeStarted); } /// <summary> /// キャプチャモード終了 /// </summary> void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result) { Debug.Log("OnStoppedPhotoMode"); photoCaptureObject.Dispose(); photoCaptureObject = null; } /// <summary> /// キャプチャモード開始 /// </summary> private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result) { Debug.Log("OnPhotoModeStarted"); if (result.success) { Debug.Log("OnPhotoModeStarted: success"); photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory); } else { Debug.LogError("OnPhotoModeStarted: Unable to start photo mode!"); } } /// <summary> /// キャプチャ実行時処理 /// </summary> void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame) { Debug.Log("OnCapturedPhotoToMemory"); if (result.success) { Debug.Log("OnCapturedPhotoToMemory: success"); // 使用するTexture2Dを作成し、正しい解像度を設定する // Create our Texture2D for use and set the correct resolution Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height); // 画像データをターゲットテクスチャにコピーする // Copy the raw image data into our target texture photoCaptureFrame.UploadImageDataToTexture(targetTexture); // テクスチャをマテリアルに適用する RefMaterial(targetTexture); // テクスチャをファイル出力する byte[] texByte = targetTexture.EncodeToJPG(); File.WriteAllBytes(UtilTest.PictureFilePath(), texByte); } // クリーンアップ // Clean up photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); } /// <summary> /// クリックイベント /// </summary> public void OnInputClicked(InputClickedEventData eventData) { Debug.Log("capture"); // キャプチャを開始する PhotoCapture.CreateAsync(true, OnPhotoCaptureCreated); } /// <summary> /// テクスチャをマテリアルに反映する /// </summary> public void RefMaterial(Texture2D texture) { changeMaterial = new Material(formatMaterial); changeMaterial.SetTexture("_MainTex", texture); targetObject.GetComponent<Image>().material = changeMaterial; } // Update is called once per frame void Update() { } }
プロジェクトをビルドして HoloLens 上で起動します。
アプリを初めて起動する際は、保存された画像がないため、白いパネルが表示されます。
タップ操作を行うとキャプチャが実行され、パネルにテクスチャとして反映されます。
同時に、アプリの LocalState ディレクトリにテクスチャの jpg 出力が行わています。
一旦、アプリを終了します。アプリを再起動してみると……。
先程のキャプチャ画像がパネルに反映されて表示されました。成功です。