本日は Unity の技術調査枠です。
HoloLens でカメラ画像を取得する方法を試したら取っ散らかりました。
以下の公式ドキュメントに目を通し、週を通してじっくりやります。
・Locatable camera in Unity
https://developer.microsoft.com/en-us/windows/mixed-reality/locatable_camera_in_unity
フォトビデオカメラの機能を有効にする
アプリがカメラを使用するためには"WebCam"のcapabilityを宣言する必要があります。
1. Unity エディタのメニューから Edit -> Project Settings -> Player を開く。
2. WindowsStoreタブを開く。
3. PublishingSettings -> Capabilities から"WebCam"と"Microphone"を有効にする。
カメラでは一度に1つの操作だけ実行できます。
カメラが現在使用しているモード(フォト、ビデオ、または無し)を確認するには、UnityEngine.XR.WSA.WebCam.Modeをチェックします。
フォトキャプチャ
名前空間: UnityEngine.XR.WSA.WebCam
モード: PhotoCapture
PhotoCapture モードでは、カメラで写真を撮ることができます。
PhotoCapture を使用して写真を撮る一般的なパターンは次のとおりです。
1. PhotoCapture オブジェクトを作成する。
2. 必要な設定で CameraParameters オブジェクトを作成する。
3. StartPhotoModeAsync 経由でフォトモードを開始する。
4. 目的の写真を撮る。
(オプション)その写真を加工する。
5. フォトモードを停止してリソースをクリーンアップする。
PhotoCaptureの一般的なセットアップ
キャプチャする写真の用途は以下の3つがあります。
・ファイルに写真を取り込む
・テクスチャ2Dへの写真を反映する
・写真を撮影し、バイトデータとして利用する
全ての用途について、以下の同じ手順から始めます
1. まず、PhotoCaptureオブジェクトを作成します。
PhotoCapture photoCaptureObject = null; void Start() { PhotoCapture.CreateAsync(false、OnPhotoCaptureCreated); }
2. 次に、オブジェクトを保存し、パラメータを設定してフォトモードを開始します。
void OnPhotoCaptureCreated(PhotoCapture captureObject) { 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, false, OnPhotoModeStarted); }
3. 最後に使用するクリーンアップコードを作成します。
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result) { photoCaptureObject.Dispose(); photoCaptureObject = null; }
これらの手順を実行すると、キャプチャする写真のモードを選択できます。
ファイルに写真を取り込む
最も簡単な操作は、写真を直接ファイルに取り込むことです。写真はJPGまたはPNG形式で保存できます。
フォトモードを正常に開始した後、写真を撮ってディスクに保存します。
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result) { if (result.success) { string filename = string.Format(@"CapturedImage{0}_n.jpg", Time.time); string filePath = System.IO.Path.Combine(Application.persistentDataPath, filename); photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk); } else { Debug.LogError("Unable to start photo mode!"); } }
写真をディスクに保存した後、フォトモードを終了してオブジェクトをクリーンアップします
void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result) { if (result.success) { Debug.Log("Saved Photo to disk!"); photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); } else { Debug.Log("Failed to save Photo to disk"); } }
テクスチャ2Dへの写真を反映する
Texture2Dにデータを反映する場合、プロセスはディスクに保存するのと非常に似ています。
セットアッププロセスを実施し、OnPhotoModeStarted では、フレームをメモリに保存します。
private void OnPhotoModeStarted(PhotoCapture.PhotoCaptureResult result) { if (result.success) { photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory); } else { Debug.LogError("Unable to start photo mode!"); } }
次に、結果をテクスチャに適用し、一般的なクリーンアップコードを使用します。
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame) { if (result.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); // マテリアルに適用するなど、テクスチャで行いたい操作を実施します // Do as we wish with the texture such as apply it to a material, etc. } // クリーンアップ // Clean up photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); }
写真を撮影し、バイトデータとして利用する
フレームのバイトデータとやりとりするための手順です。
セットアッププロセスを実施し、OnPhotoModeStarted を使って写真をTexture2Dに反映するのと同じ手順を実行します。
違いは OnCapturedPhotoToMemory にあり、バイトデータを取得して利用することができます。
この例では、更に処理したり、SetPixels()でテクスチャに適用できる List
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame) { if (result.success) { List<byte> imageBufferList = new List<byte>(); // IMFMediaBuffer のデータを空のバイトリストにコピーします。 // Copy the raw IMFMediaBuffer data into our empty byte list. photoCaptureFrame.CopyRawImageDataIntoBuffer(imageBufferList); // この例では、BGRA32形式を使用してイメージをキャプチャしました。 // だから私たちのストライドは、各RGBAチャンネルのバイトがあるので4になります。 // 画像データも反転され、ピクセルデータに逆の順序でアクセスします。 // In this example, we captured the image using the BGRA32 format. // So our stride will be 4 since we have a byte for each rgba channel. // The raw image data will also be flipped so we access our pixel data // in the reverse order. int stride = 4; float denominator = 1.0f / 255.0f; List<Color> colorArray = new List<Color>(); for (int i = imageBufferList.Count - 1; i >= 0; i -= stride) { float a = (int)(imageBufferList[i - 0]) * denominator; float r = (int)(imageBufferList[i - 1]) * denominator; float g = (int)(imageBufferList[i - 2]) * denominator; float b = (int)(imageBufferList[i - 3]) * denominator; colorArray.Add(new Color(r, g, b, a)); } // これで、texture.SetPixels()などの配列を使って何かできるようになりました。 // Now we could do something with the array such as texture.SetPixels() or run image processing on the list } photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); }
ビデオキャプチャは一旦見送り。
以上の情報を元に HoloLens でのカメラ画像処理にトライします。