MRが楽しい

MRやVRについて学習したことを書き残す

HoloLensでカメラ画像を取得する その4(保存画像をテクスチャへ反映する)

本日は Unity の技術調査枠です。
今回は前回、前々回のHoloLensでカメラ画像を取得する技術を応用して、予め保存された画像をテクスチャに反映してみます。
bluebirdofoz.hatenablog.com
bluebirdofoz.hatenablog.com

ファイルから画像を取得する方法については以下を参考にします。
rikoubou.hatenablog.com

その3で作成したプロジェクトを流用します。
f:id:bluebirdofoz:20180320025813j:plain

以下の通り、パス取得と画像バイナリ読み込みの処理をまとめました。
・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 上で起動します。
アプリを初めて起動する際は、保存された画像がないため、白いパネルが表示されます。
f:id:bluebirdofoz:20180320025857j:plain

タップ操作を行うとキャプチャが実行され、パネルにテクスチャとして反映されます。
f:id:bluebirdofoz:20180320025904j:plain

同時に、アプリの LocalState ディレクトリにテクスチャの jpg 出力が行わています。
f:id:bluebirdofoz:20180320025912j:plain

一旦、アプリを終了します。アプリを再起動してみると……。
f:id:bluebirdofoz:20180320025920j:plain
先程のキャプチャ画像がパネルに反映されて表示されました。成功です。