UnityからAzure Computer Visionを利用する

# はじめに
この記事はフリューAdvent Calendar 2022の8日目の記事となります。

今回はUnityからAzure Cognitive Servicesの一つであるComputer Visionを利用する機会がありましたので記録として残しておきます。
利用する際にアセンブリが重複する部分もあったので、そのあたりの解決方法も含めて紹介します。

# Azure Cognitive Servicesとは
この記事に辿り着いてる方には説明不要とは思いますが、簡単に紹介しておきます。
Azure Cognitive Servicesはトレーニング済みのAIを簡単にアプリに組み込めるAPI形式のサービスとなっており音声、画像、言語など様々な解析が可能です。
サービスの詳細は公式サイトをご覧ください。

https://azure.microsoft.com/ja-jp/products/cognitive-services/#overview

# 今回紹介するAzure Computer Visionについて
Computer Visionは画像解析を行い視覚的情報をテキストとして返してくれるサービスとなっています。
簡単に言うと、画像には何が写っているのかを返してくれるAPIになります。

https://azure.microsoft.com/ja-jp/products/cognitive-services/computer-vision/#overview

# 動作確認した環境
　今回は以下の環境での説明となります。
　Windows10 Pro
　Unity 2021.3.21f

# Unityでの実行
この記事の内容は2022.11.30時点のものとなります。

それでは実際にUnityから呼び出しを作っていきます。
基本的には次のクイックスタートC#版に沿って手順を進めていきます。
Computer Visionのリソース作成に関してはクイックスタートにもありますし、Unityに限った話でもありませんので割愛します。

https://learn.microsoft.com/ja-jp/azure/cognitive-services/computer-vision/quickstarts-sdk/image-analysis-client-library?tabs=visual-studio%2C3-2&pivots=programming-language-csharp

## クライアントライブラリのインストール
クイックスタートにはVisual Studioでの手順は記載されていますが、Unityでの記載はありません。
順を追って説明していきます。

### 1.NuGetForUnityパッケージをインストールする
NuGetを利用してインストールできるようなのでNuGetForUnityを使用してライブラリをインストールします。
NuGetForUnityについては以下のサイトを参考にパッケージをインストールしました。

https://www.hanachiru-blog.com/entry/2021/03/26/003612

NuGetForUnityのパッケージ追加が終わればUnityのメニューにNuGetが追加されます。

### 2.Azure Computer Visionクライアントをインストールする
「NuGet」-> 「Manage NuGet Packages」を選択してNuGetを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/795eaea0-75e7-258f-db38-c6d02bfac071.png)

NuGetのウィンドウから「Computer Vision」を検索し、表示されたMicrosoft.Azure.CognitiveServices.Vision.ComputerVisionを選択し「Install」ボタンを押してクライアントライブラリをインストールします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/c4fcaa30-2543-53bc-435d-db9b8dcbdb09.png)

自動的にダウンロードとビルドが始まります。
終わってインストールが完了したと思いきや、ビルドエラーが発生します。
<font color="red">**Multiple precompiled assemblies with the same name Newtonsoft.Json.dll included on the current platform. Only one assembly with the same name is allowed per platform. (Assets/Packages/Newtonsoft.Json.10.0.3/lib/netstandard1.3/Newtonsoft.Json.dll)**</font>
冒頭で紹介したアセンブリの重複です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/a84a433d-d009-ebed-b453-3c832bc6b814.png)
この原因ですが、Newtonsoft.Json.dllはUnityのデフォルトライブラリとしてインストールされており、今回インストールしたComputer VisionのライブラリはNewtonsoft.Json.dllが依存関係であるため、一緒にインストールされ重複しエラーとなっています。

### 3.アセンブリ重複の解決

解決方法は次のようになります。
まずは、Computer Visionのクライアントライブラリが依存するライブラリの厳密なバージョンチェックを解除してバージョンが新しいUnityデフォルトのNewtonsoft.Json.dllを使用できるようにします。
AssetsのPackagesにあるMicrosoft.Azure.CognitiveServices.Vision.ComputerVison.7.0.1の中にあるDLLを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/8991676e-b92e-6ae2-2b90-d41551617f4b.png)

Inspectorから「Validate Reference」のチェックを外し「Apply」を押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/212759fb-76ff-0690-c674-14d181783d18.png)

同様にAssets/Packagesにある以下の２つのDLLに関しても「Validate Reference」のチェックを外します。
Microsoft.Rest.ClientRuntime.dll
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/f438a90d-d81a-b3e0-b21e-e71af9c78e04.png)
Microsoft.Rest.ClientRuntime.Azure.dll
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/736b662e-eaef-9e64-4df6-21344c79c875.png)

Validate Referenceは以下のUnity公式ドキュメントを参照してください。

https://docs.unity3d.com/ja/2021.3/Manual/PluginInspector.html

最後にPackagesに入っている古いバージョンのNewtonsoft.Jsonを削除することでUnityデフォルトで入っているライブラリを使用することができます。
メニューの「NuGet」から「Manage NuGet Packages」してウィンドウが開いたら「Installed」を選択し、Newtonsoft.Jsonの「Uninstall」を押せば削除されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/865a6c9f-855e-b210-d4aa-611c160482b3.png)
これでクライアントライブラリのインストールが完了しました。
後はクイックスタートにあるソースコードを使えば実行が確認できます。

## ソースコードの準備
以下はクイックスタートにあるコードをUnityスクリプトへ置き換えたものです。
したがって動作が確認できる最低限のコードになります。
コード中にあるsubscriptionKeyおよび、endpointはAzureで作成したリソースから取得したものに書き換えてください。

```cs:ComputerVision.cs
using System.Collections.Generic;
using UnityEngine;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision;
using Microsoft.Azure.CognitiveServices.Vision.ComputerVision.Models;
using System.Threading.Tasks;
using System.IO;

public class ComputerVision : MonoBehaviour
{
	// Add your Computer Vision subscription key and endpoint
	private static string subscriptionKey = "PASTE_YOUR_COMPUTER_VISION_SUBSCRIPTION_KEY_HERE";
	private static string endpoint = "PASTE_YOUR_COMPUTER_VISION_ENDPOINT_HERE";

	// URL image used for analyzing an image (image of puppy)
	private const string ANALYZE_URL_IMAGE = "https://moderatorsampleimages.blob.core.windows.net/samples/sample16.png";

	// Start is called before the first frame update
	async void Start()
    {
		Debug.Log("Azure Cognitive Services Computer Vision - .NET quickstart example");

		// Create a client
		ComputerVisionClient client = Authenticate(endpoint, subscriptionKey);

		// Analyze an image to get features and other properties.
		await AnalyzeImageUrl(client, ANALYZE_URL_IMAGE);

	}

	/*
	 * AUTHENTICATE
	 * Creates a Computer Vision client used by each example.
	 */
	private ComputerVisionClient Authenticate(string endpoint, string key)
	{
		ComputerVisionClient client =
		  new ComputerVisionClient(new ApiKeyServiceClientCredentials(key))
		  { Endpoint = endpoint };
		return client;
	}

	private async Task AnalyzeImageUrl(ComputerVisionClient client, string imageUrl)
	{
		Debug.Log("----------------------------------------------------------");
		Debug.Log("ANALYZE IMAGE - URL");
		
		// Creating a list that defines the features to be extracted from the image. 

		List<VisualFeatureTypes?> features = new List<VisualFeatureTypes?>()
			{
				VisualFeatureTypes.Tags
			};

		Debug.Log($"Analyzing the image {Path.GetFileName(imageUrl)}...");
		
		// Analyze the URL image 
		ImageAnalysis results = await client.AnalyzeImageAsync(imageUrl, visualFeatures: features);

		// Image tags and their confidence score
		Debug.Log("Tags:");
		foreach (var tag in results.Tags)
		{
			Debug.Log($"{tag.Name} {tag.Confidence}");
		}
	}
}

```
このスクリプトを適当なオブジェクトにアタッチして実行すると即座にComputer Vision APIを呼び出します。
以下のようなログが出てくれば呼び出しに成功しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/1129adce-d74a-8509-ed4f-964b893ef9f8.png)
クイックスタートの出力結果とも一致しています。
(https://learn.microsoft.com/ja-jp/azure/cognitive-services/computer-vision/quickstarts-sdk/image-analysis-client-library?tabs=visual-studio%2C3-2&pivots=programming-language-csharp#output)

これでUnityからAzure Computer Visionを利用することができました。

## 任意のテクスチャをComputer Visionで解析する
　サンプルだけではつまらないので多少アレンジしたコードも用意しておきました。
　Computer Vision APIはURLの画像解析だけではなく、画像ファイルを直接アップロードして解析することもできます。
　テクスチャをEncodeToでPNGなりJPGなりのファイルフォーマットに変換してAnalyzeImageInStreamAsyncの引数に指定するだけです。
　Webカメラからの入力画像を渡せば色々解析できて楽しいのではないかと思います。

```cs:ComputerVision.cs
	private async Task AnalyzeImageTexture(ComputerVisionClient client, Texture2D texture )
	{
		Debug.Log("----------------------------------------------------------");
		Debug.Log("ANALYZE IMAGE");
		
		// Creating a list that defines the features to be extracted from the image. 

		List<VisualFeatureTypes?> features = new List<VisualFeatureTypes?>()
			{
				VisualFeatureTypes.Tags
			};

		Debug.Log($"Analyzing the image ...");

		ImageAnalysis results = null;
		using (MemoryStream memStream = new MemoryStream(texture.EncodeToPNG()))
		{
			// Analyze the Image 
			results = await client.AnalyzeImageInStreamAsync(memStream, visualFeatures: features);

		}

		// Image tags and their confidence score
		Debug.Log("Tags:");
		foreach (var tag in results.Tags)
		{
			Debug.Log($"{tag.Name} {tag.Confidence}");
		}
	}
```
実際に手元の画像を渡したところ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/27331fd9-baa5-cbf5-c35e-95400c18de80.png)
電化製品やキーボードという結果が得られています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2743276/966246a5-3887-7244-3f43-63177400aabc.png)
**これで画像に特定の物が写っていれば何か処理を行うなどの判定が可能となりますね！**

### リファレンス
リファレンスは以下になります。
VisualFeatureTypesを追加していくことで取得できる情報が増えますので目を通してみてください。

https://learn.microsoft.com/en-us/dotnet/api/overview/azure/computer-vision?view=azure-dotnet

# 最後に
　いかがでしたでしょうか？
　今回は
　・UnityからComputer Visionを利用する方法
　・アセンブリの重複はValidate Referenceのチェックを外して古いアセンブリを削除する
　この2点の紹介でした。
　C#のライブラリが用意されており、Unityでも利用できました。
　私自身やりたいことが簡単に実現できたので非常にありがたいサービスでした。