本日はチュートリアルの実施枠です。
Academyの「MR and Azure 302b: Custom vision」の実施内容をまとめます。
docs.microsoft.com
前回記事の続きです。
bluebirdofoz.hatenablog.com
今回は「Chapter 11」です。
Chapter 11:Create the ImageCapture class
最後に作成するスクリプトは ImageCapture クラスです。
このクラスは以下の処理を行います。
・HoloLensカメラを使用して画像をキャプチャし、Appフォルダに保存します。
・ユーザーからのタップジェスチャーの処理を受け取ります。
・アプリケーションを分析モードまたはトレーニングモードで実行するかどうかを決定するEnum値を保持します。
1-2.Script フォルダを開きます。
フォルダ内で右クリックして、Creapte -> C# Script を選択します。
Script の名称は ImageCapture に設定します。
3.新しいスクリプトをダブルクリックしてVisual Studioで開きます。
4-10.以下の通り、スクリプトを編集します。
・ImageCapture.cs
/// 名前空間の追加 using System; using System.IO; using System.Linq; using UnityEngine; using UnityEngine.XR.WSA.Input; using UnityEngine.XR.WSA.WebCam; public class ImageCapture : MonoBehaviour { // メンバ変数の追加 /// <summary> /// Allows this class to behave like a singleton /// クラスがシングルトンのように動作できるようにします /// </summary> public static ImageCapture Instance; /// <summary> /// Keep counts of the taps for image renaming /// イメージの名前を変更するためのタップ数を維持する /// </summary> private int captureCount = 0; /// <summary> /// Photo Capture object /// フォトキャプチャオブジェクト /// </summary> private PhotoCapture photoCaptureObject = null; /// <summary> /// Allows gestures recognition in HoloLens /// HoloLensでジェスチャー認識を可能にする /// </summary> private GestureRecognizer recognizer; /// <summary> /// Loop timer /// ループタイマ /// </summary> private float secondsBetweenCaptures = 10f; /// <summary> /// Application main functionalities switch /// アプリケーションの主な機能の切り替え /// </summary> internal enum AppModes { Analysis, Training } /// <summary> /// Local variable for current AppMode /// 現在のアプリモード /// </summary> internal AppModes AppMode { get; private set; } /// <summary> /// Flagging if the capture loop is running /// キャプチャループが実行中の場合にフラグを立てる /// </summary> internal bool captureIsActive; /// <summary> /// File path of current analysed photo /// 現在分析されている写真のファイルパス /// </summary> internal string filePath = string.Empty; /// <summary> /// Called on initialization /// 初期化処理 /// </summary> private void Awake() { Instance = this; // Change this flag to switch between Analysis Mode and Training Mode // 分析モードとトレーニングモードを切り替えるには、このフラグを変更します AppMode = AppModes.Training; } /// <summary> /// Runs at initialization right after Awake method /// StartメソッドはAwakeメソッドの直後の初期化時に実行されます /// </summary> void Start() { // Clean up the LocalState folder of this application from all photos stored // このアプリケーションのLocalStateフォルダ(保存されたすべての写真)をクリーンアップします。 DirectoryInfo info = new DirectoryInfo(Application.persistentDataPath); var fileInfo = info.GetFiles(); foreach (var file in fileInfo) { try { file.Delete(); } catch (Exception) { Debug.LogFormat("Cannot delete file: ", file.Name); } } // Subscribing to the Hololens API gesture recognizer to track user gestures // ユーザーのジェスチャーを追跡するため、Hololens APIジェスチャー認識を開始する recognizer = new GestureRecognizer(); recognizer.SetRecognizableGestures(GestureSettings.Tap); recognizer.Tapped += TapHandler; recognizer.StartCapturingGestures(); SceneOrganiser.Instance.SetCameraStatus("Ready"); } /// <summary> /// Respond to Tap Input. /// タップ入力に応答する /// タップ操作は写真キャプチャ・ループを開始または停止するためのスイッチとして作用します /// トレーニングモードでは、カメラから画像をキャプチャします /// 解析モードではカーソルが緑色の場合は、カメラが画像を撮影することができます /// カーソルが赤色のときは、カメラがビジーであることを示します /// </summary> private void TapHandler(TappedEventArgs obj) { switch (AppMode) { case AppModes.Analysis: if (!captureIsActive) { captureIsActive = true; // Set the cursor color to red // カーソルの色を赤に設定する SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red; // Update camera status to looping capture. // カメラの状態を"Looping Capture"に更新します SceneOrganiser.Instance.SetCameraStatus("Looping Capture"); // Begin the capture loop // キャプチャループを開始する InvokeRepeating("ExecuteImageCaptureAndAnalysis", 0, secondsBetweenCaptures); } else { // The user tapped while the app was analyzing // therefore stop the analysis process // アプリの分析中にユーザーがタップしたケース // 分析プロセスを停止する ResetImageCapture(); } break; case AppModes.Training: if (!captureIsActive) { captureIsActive = true; // Call the image capture // 画像キャプチャを呼び出す ExecuteImageCaptureAndAnalysis(); // Set the cursor color to red // カーソルの色を赤に設定する SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.red; // Update camera status to uploading image. // カメラのステータスを"Uploading Image"に更新します SceneOrganiser.Instance.SetCameraStatus("Uploading Image"); } break; } } /// <summary> /// Begin process of Image Capturing and send To Azure Custom Vision Service. /// 画像キャプチャのプロセスを開始し、Azure Custom Vision Service に送信します /// </summary> private void ExecuteImageCaptureAndAnalysis() { // Update camera status to analysis. // カメラのステータスを"Analysis"に更新します SceneOrganiser.Instance.SetCameraStatus("Analysis"); // Create a label in world space using the SceneOrganiser class // Invisible at this point but correctly positioned where the image was taken // SceneOrganiserクラスを使用してワールド空間にラベルを作成する // この時点では目に見えないが、画像が撮影された場所に正しく配置されている SceneOrganiser.Instance.PlaceAnalysisLabel(); // Set the camera resolution to be the highest possible // カメラの解像度を可能な限り高く設定する Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First(); Texture2D targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height); // Begin capture process, set the image format // キャプチャプロセスを開始する。イメージフォーマットの設定を行う。 PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject) { photoCaptureObject = captureObject; CameraParameters camParameters = new CameraParameters { hologramOpacity = 0.0f, cameraResolutionWidth = targetTexture.width, cameraResolutionHeight = targetTexture.height, pixelFormat = CapturePixelFormat.BGRA32 }; // Capture the image from the camera and save it in the App internal folder // カメラから画像をキャプチャして、それをAppの内部フォルダに保存します captureObject.StartPhotoModeAsync(camParameters, delegate (PhotoCapture.PhotoCaptureResult result) { string filename = string.Format(@"CapturedImage{0}.jpg", captureCount); filePath = Path.Combine(Application.persistentDataPath, filename); captureCount++; photoCaptureObject.TakePhotoAsync(filePath, PhotoCaptureFileOutputFormat.JPG, OnCapturedPhotoToDisk); }); }); } /// <summary> /// Register the full execution of the Photo Capture. /// フォトキャプチャの完了時処理を登録します /// </summary> void OnCapturedPhotoToDisk(PhotoCapture.PhotoCaptureResult result) { // Call StopPhotoMode once the image has successfully captured // イメージが正常に取得されたらStopPhotoModeを呼び出します photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode); } /// <summary> /// The camera photo mode has stopped after the capture. /// Begin the Image Analysis process. /// キャプチャ後にカメラ写真モードが停止しました /// 画像解析プロセスを開始します /// </summary> void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result) { Debug.LogFormat("Stopped Photo Mode"); // Dispose from the object in memory and request the image analysis // メモリ内のオブジェクトを破棄し、画像解析を要求する photoCaptureObject.Dispose(); photoCaptureObject = null; switch (AppMode) { case AppModes.Analysis: // Call the image analysis // 画像解析を呼び出す StartCoroutine(CustomVisionAnalyser.Instance.AnalyseLastImageCaptured(filePath)); break; case AppModes.Training: // Call training using captured image // キャプチャされた画像を使用したトレーニングを呼び出す CustomVisionTrainer.Instance.RequestTagSelection(); break; } } /// <summary> /// Stops all capture pending actions /// すべてのキャプチャ保留中のアクションを停止します /// </summary> internal void ResetImageCapture() { captureIsActive = false; // Set the cursor color to green // カーソルの色を緑に設定する SceneOrganiser.Instance.cursor.GetComponent<Renderer>().material.color = Color.green; // Update camera status to ready. // カメラの状態を準備完了に更新します SceneOrganiser.Instance.SetCameraStatus("Ready"); // Stop the capture loop if active // 処理がアクティブな場合はキャプチャループを停止する CancelInvoke(); } }
10.Visual Studio で変更を保存して Unity に戻ります。
12.これで全てのスクリプトが完成しました。
Scripts フォルダから SceneOrganiserクラスを Hierarchy パネルの MixedRealityCamera オブジェクトにアタッチします。
Chapter 11 はここまでです。
次回は Chapter 12 ~ Chapter 13 を実施します。
bluebirdofoz.hatenablog.com