はじめに
HoloLensアドベントカレンダー2020 の4日目の記事です。
前回 に引き続き、Cognitive Services系をまとめていきまーす。
今日は、Azure Computer Vision APIの画像分析APIを用いて画像から視覚的特徴を抽出します。
例えば、下の サンプル画像 を見て、「人通りの多い道」と説明してくれます。
開発環境
- Azure
- Computer Vision API(画像分析 API)
- HoloLens2
- Unity 2019.4.1f1
- MRTK 2.5.1
- OpenCV for Unity
- Windows PC
導入
前回 の顔検出をほぼほぼ使って、異なるところだけ説明していきます。
1.AzureポータルからComputer Vision APIを作成し、エンドポイントとサブスクリプションキーをメモしておいてください。
2.Unityのプロジェクトはこんな感じ。前回の「TapToCapture」を無効にして「TapToCaptureAnalyzeAPI」を作成しました。
3.「TapToCaptureAnalyzeAPI.cs」は、エアタップしたら画像をキャプチャし、Analyze APIに投げるスクリプトになります。
1TapToCaptureAnalyzeAPI.cs
2using System.Collections;
3using System.Collections.Generic;
4using System.Linq;
5using System;
6using UnityEngine;
7using Microsoft.MixedReality.Toolkit.Utilities;
8using System.Threading.Tasks;
9using OpenCVForUnity.CoreModule;
10using OpenCVForUnity.UnityUtils;
11using OpenCVForUnity.ImgprocModule;
12
13public class TapToCaptureAnalyzeAPI : MonoBehaviour
14{
15 public GameObject quad;
16
17 [System.Serializable]
18 public class Analyze
19 {
20 public Categories[] categories;
21 public Color color;
22 public Description description;
23 public string requestId;
24 public Metadata metadata;
25 }
26
27 [System.Serializable]
28 public class Categories
29 {
30 public string name;
31 public float score;
32 }
33
34 [System.Serializable]
35 public class Color
36 {
37 public string dominantColorForeground;
38 public string dominantColorBackground;
39 public string[] dominantColors;
40 public string accentColor;
41 public bool isBwImg;
42 public bool isBWImg;
43 }
44
45 [System.Serializable]
46 public class Description
47 {
48 public string[] tags;
49 public Captions[] captions;
50 }
51
52 [System.Serializable]
53 public class Captions
54 {
55 public string text;
56 public float confidence;
57 }
58
59 [System.Serializable]
60 public class Metadata
61 {
62 public int height;
63 public int width;
64 public string format;
65 }
66
67 UnityEngine.Windows.WebCam.PhotoCapture photoCaptureObject = null;
68 Texture2D targetTexture = null;
69
70 private string endpoint = "https:///vision/v3.1/analyze";
71 private string subscription_key = "";
72 private bool waitingForCapture;
73
74 void Start(){
75 waitingForCapture = false;
76 }
77
78 public void AirTap()
79 {
80 if (waitingForCapture) return;
81 waitingForCapture = true;
82
83 Resolution cameraResolution = UnityEngine.Windows.WebCam.PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
84 targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
85
86 // PhotoCapture オブジェクトを作成します
87 UnityEngine.Windows.WebCam.PhotoCapture.CreateAsync(false, delegate (UnityEngine.Windows.WebCam.PhotoCapture captureObject) {
88 photoCaptureObject = captureObject;
89 UnityEngine.Windows.WebCam.CameraParameters cameraParameters = new UnityEngine.Windows.WebCam.CameraParameters();
90 cameraParameters.hologramOpacity = 0.0f;
91 cameraParameters.cameraResolutionWidth = cameraResolution.width;
92 cameraParameters.cameraResolutionHeight = cameraResolution.height;
93 cameraParameters.pixelFormat = UnityEngine.Windows.WebCam.CapturePixelFormat.BGRA32;
94
95 // カメラをアクティベートします
96 photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate (UnityEngine.Windows.WebCam.PhotoCapture.PhotoCaptureResult result) {
97 // 写真を撮ります
98 photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemoryAsync);
99 });
100 });
101 }
102
103 async void OnCapturedPhotoToMemoryAsync(UnityEngine.Windows.WebCam.PhotoCapture.PhotoCaptureResult result, UnityEngine.Windows.WebCam.PhotoCaptureFrame photoCaptureFrame)
104 {
105 // ターゲットテクスチャに RAW 画像データをコピーします
106 photoCaptureFrame.UploadImageDataToTexture(targetTexture);
107 byte[] bodyData = targetTexture.EncodeToJPG();
108
109 Response response = new Response();
110
111 try
112 {
113 string query = endpoint + "?visualFeatures=Categories,Description,Color";
114 var headers = new Dictionary<string, string>();
115 headers.Add("Ocp-Apim-Subscription-Key", subscription_key);
116 response = await Rest.PostAsync(query, bodyData, headers, -1, true);
117 }
118 catch (Exception e)
119 {
120 photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
121 return;
122 }
123
124 if (!response.Successful)
125 {
126 photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
127 return;
128 }
129
130 Debug.Log(response.ResponseCode);
131 Debug.Log(response.ResponseBody);
132 Analyze analyze = JsonUtility.FromJson(response.ResponseBody);
133 Debug.Log(analyze.description.captions[0].text);
134
135 // OpenCVを用いて結果をて画像に書き込み
136 Mat imgMat = new Mat(targetTexture.height, targetTexture.width, CvType.CV_8UC4);
137 Utils.texture2DToMat(targetTexture, imgMat);
138 Debug.Log("imgMat.ToString() " + imgMat.ToString());
139 Imgproc.putText(imgMat, analyze.description.captions[0].text, new Point(10, 100), Imgproc.FONT_HERSHEY_SIMPLEX, 4.0, new Scalar(255, 255, 0, 255), 4, Imgproc.LINE_AA, false);
140 Texture2D texture = new Texture2D(imgMat.cols(), imgMat.rows(), TextureFormat.RGBA32, false);
141 Utils.matToTexture2D(imgMat, texture);
142 Renderer quadRenderer = quad.GetComponent() as Renderer;
143 quadRenderer.material.SetTexture("_MainTex", texture);
144
145 // カメラを非アクティブにします
146 photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
147 }
148
149 void OnStoppedPhotoMode(UnityEngine.Windows.WebCam.PhotoCapture.PhotoCaptureResult result)
150 {
151 // photo capture のリソースをシャットダウンします
152 photoCaptureObject.Dispose();
153 photoCaptureObject = null;
154 waitingForCapture = false;
155 }
156}4.endpointとsubscription_keyにメモしておいたものを貼り付けてください。
5.クエリパラメータはCategories,Description,Colorを指定しています。
1endpoint + "?visualFeatures=Categories,Description,Color";6.MRTKのRestを用いて、分析する画像をPOSTしたら、response.ResponseBodyが次のように返ってきます。
1{'categories': [{'name': 'outdoor_', 'score': 0.00390625, 'detail': {'landmarks': []}}, {'name': 'outdoor_street', 'score': 0.33984375, 'detail': {'landmarks': []}}], 'color': {'dominantColorForeground': 'Brown', 'dominantColorBackground': 'Brown', 'dominantColors': ['Brown'], 'accentColor': 'B54316', 'isBwImg': False, 'isBWImg': False}, 'description': {'tags': ['building', 'outdoor', 'street', 'city', 'people', 'busy', 'night', 'ride', 'crowd'], 'captions': [{'text': 'a busy street with many people', 'confidence': 0.3540118634700775}]}, 'requestId': '88d8a3c6-8972-4315-8a15-b20bf14c33ce', 'metadata': {'height': 600, 'width': 450, 'format': 'Jpeg'}}7.今回はリストのJsonにはなっていないので、JsonHelperを使わずにJsonUtilityで読むことができます。
8.仕様に合わせてAnalyzeクラスとCategoriesクラス、Colorクラス、Descriptionクラス、Captionsクラス、Metadataクラスを作成しました。
9.生成された画像説明文をOpenCVを用いて、画像に張り付けてquadのテクスチャに表示します。
実行
HoloLens2にデプロイして、実行した結果がこちらになります。Editor上でもできるので試してみてください。
HoloLens2にデプロイして、実行した結果がこちらになります。Editor上でもできるので試してみてください。
“a laptop on a desk”
“a person using a laptop”
お疲れ様でした。
明日(というか遅れたので今日)は、弟子( @Horomoto-Asahi )による「Holo関連のなにか書きます」です。
参考
- Computer Vision のドキュメント
- ローカル画像の分析( C# 、 Python )