20210405のC#に関する記事は4件です。

Visual Studio for Mac 2019にStyleCopAnalyzersを導入する

この記事を読んでできるようになること Visual Studio for Mac 2019にStyleCopAnalyzersを導入し、C#コードの静的解析ができる。 きっかけ Unity開発をしているので、綺麗なコードを書くためにVisual Studio for Macに静的開発ツールを導入したくなった。そこで色々と調べたところStyleCopAnalyzersがヒット。 すぐに導入を試みたがNugetパッケージを導入する方法、GitHubから直接slnをもらってきて導入する方法、どれもうまくStyleCopAnalyzersが機能しなかった。 調べてもWindows版のVisual Studioしか良さそうな記事がなく困ったので これを機にVS for Macへの導入法をまとめる。 (基本的にVisual Studio for Macは記事が少ない…) 使用環境 macOS Big Sur : Version 11.2.3 Visual Studio for Mac 2019: Version 8.9.4 (build 25) 導入する 非常に簡単。 StyleCopAnalyzersのGitHubからDirectory.Build.propsをダウンロードし、これを静的解析を適用させたいhoge.csproj、fuga.slnと同じ階層に配置するだけ。 Directory.Build.propsは、複数のプロジェクトに共通の設定を一括反映するために使うらしい。自分は次のQiitaを参考にした。 C#の全プロジェクトに便利な設定を一括反映する 詳細を設定する StyleCopAnalyzersは英語圏での使用を想定されているからなのか、デフォルトでは「コメントの文末にピリオドをつけろ〜」など日本で使う上では不必要な指摘まで入る。快適に使うにはルールごとに警告かエラーか、適用外かなどを自由にカスタマイズする必要がある。 具体的にどんなルールがあるのかは、StyleCopAnalyzersのGitHubDocから確認してほしい。 カスタマイズしたいルールを決めたらVS for Macを開き、左上のVSメニューから ユーザー設定 > ソースの解析 > C# > コード規則 ここに次のようにカスタマイズしたいもののみルールを書いていく(記載のないものはデフォルトでは警告が適用される) コピペ用に下に自分が試しに用いたものを記載するが、使用するときは適宜カスタマイズしてほしい。 <RuleSet Name="Global Rules" ToolsVersion="12.0"> <Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers"> <Rule Id="SA1600" Action="Error" /> <Rule Id="UNT0001" Action="Error" /><!-- 不要なUpdate文の削除 --> <Rule Id="UNT0002" Action="Error" /><!-- tagの比較制約 --> </Rules> </RuleSet> 上の例だとSA1600をエラーとして表示する設定にしたので、コード上では次のような指摘が入る(サマリーを書いてねという指摘) ちなみにしれっと書いたが、このコード規則のところには IDEやCS、UNTから始まるコード規則も追加できる。これらも規則コードで色々検索して好みのものを追加してほしい。 最後に GitHub Actionsや、Unityエディタから静的解析の結果を確認するプラグインなど色々調べたが結局どれが一番使い心地がいいのかわからん… Unityによるチーム開発でコードを綺麗に書くために導入しているおすすめの方法があればぜひ教えてください ここまで読んでいただきありがとうございました。 この記事が何かお役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ASP .NET MVC】リアルタイムな画像キャプチャ

概要 センサやカメラなど、リアルタイムに計測したイメージデータが撮像できるという条件のとき、そのイメージデータをブラウザ上で随時更新する処理を考えるときに、imgタグでは画像が更新されないことに気づき、ちょっと詰まった。 ASP.NETによる対処法のメモを残す。 原因と対応方法 原因を考えていたところ、ブラウザ側でキャッシュが働いていて、画像を一時保存しているため。 主な方法は2つ。両者ともにメリットがあり、設計指針によって決めればよい。 (1) "Display_20210405_130903.png"などととして、キャプチャ画像をリネームして読み込む。 メリット 画像をストレージに自動的に確保でき、キャプチャした画像データを常にログとして自動で残し続けることが出来る。 時系列データと画像が残っていることから、作業や管理、デバッグがしやすい。 (2) キャッシュを持たない設定にすることで対応する。 メリット ブラウザのキャッシュの影響を考慮せずに、運用が出来る。例えば、メモリ制限があるPCで動かすことを考えて、キャッシュを持つ設定にすると、キャッシュ分だけ画像を残してしまう&そのキャッシュを読み取る処理が発生するので、メモリ効率が悪化してしまい、システム全体でのリアルタイムの動きに影響を与えるかも。 アプリの見た目上、原理的には「画像が更新されていない」という状況を防ぐことが出来る。 今回は(2)で対処することを考える。   注意 実際にセンサやカメラを使う部分はConfidentialなので、違う画像を使って差し替えます。 今のところ、オンラインで動かすことは考えていませんので、urlはローカル設定のものを使っています。ご注意を ソースコード (1) Razorページ(View)を以下のように定義 1秒ごとに、サーバサイドにGETリクエストを行ない、画像のみを変更する。 OPDebug/Index.cshtml <img src="@Url.Action( "TestLoad", "OPDebug" )" id="OpTestLoadImage" /> @section Scripts{ @Scripts.Render("~/bundles/jqueryval") <script> $(function () { setInterval( function() { $('#OpTestLoadImage').attr('src', '@Url.Action("TestLoad", "OPDebug")'); }, 1000); }); </script> } (2) Controllersでコントローラを以下のように定義 OPDebugController.cs using System.Drawing; using System.Threading.Tasks; using System.Web; using System.Web.Helpers; using System.Web.Mvc; using System.Web.Services; using OPLocal.ViewModels; namespace OPLocal.Controllers { public class OPDebugController : Controller { /* --- 既存ソースコードのため、一部省略 */ public FileContentResult TestLoad() { /* 次の行がポイント */ Response.Cache.SetNoStore(); var img = new WebImage(Server.MapPath("..") + @"\display.png"); return new FileContentResult(img.GetBytes(), string.Format("image/{0}", img.ImageFormat)); } } } Response.Cache.SetNoStore();がポイントで、これによって、ブラウザ側がキャッシュを持つのを防いでくれるようになる。 (3) ソリューション直下にdisplay.pngと、"org1.png"、"org2.png"を設定。 カメラから取り込まれたデータが、display.pngとして保存されると想定するが、とりあえずダミーで、リアルタイムのカメラの動きの代わりに、org1.bmpとorg2.bmpのどちらかをリネームして表示できるようにするものとする。 実行結果 以下、他のGET命令によるルーティングを行なわない前提で検証 display.pngがない org1.pngをdisplay.pngにリネーム org2.pngをdisplay.pngにリネーム 予想通り動いてくれた。 この後は、リアルタイム処理中は、実際にキャプチャフォルダをローカルに設定し、そのローカルに設定したファイルのうち最新のものを保存する。それを、読み取ることで解決する予定。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ランダムプラグインの実装方法

StreamRelay.NET.exe の乱数生成機能を呼び出しているプラグインの実装方法について説明する StreamRelay.NET.exe/StreamRelay.NET.x86.exeには、いくつかの機能をプラグインとして実装できるようにインターフェイスを公開している。 今回は、乱数生成機能のインターフェイスを使って、プラグインを作成する方法を記述する。 既存のプラグイン 乱数アルゴリズムのdllを呼び出すこのあたり プラグイン的には、このあたり。 Plugins\Plugin.Rei.Random.dll Plugins\Plugin.CmdRandom.dll Plugins\Plugin.BouncyCastle.Crypto.dll Plugins\Win32|64\Plugin.RdRand.x86|x64.dll Plugins\Win32|64\CRandom32|64.dll 実装したプラグインの呼び出され方 プラグインで指定された引数を渡されると、乱数生成のオブジェクトを生成して返す。 という感じ。 実装するインターフェイス 以下のインターフェイスを継承したクラスを実装すればよい IPlugin.dll 中の jp.dip.rocketeer.Plugins.IPluginRandomEntity インターフェイス IPlugin.dll 中の jp.dip.rocketeer.Plugins.IPluginRandom インターフェイス この2つのインターフェイスを実装すればよい。 jp.dip.rocketeer.Plugins.IPluginRandom インターフェイス StreamRelay.NET.exe/他のプラグインから、IPluginRandom のCreateRandomEntityメソッドが呼ばれて、IPluginRandomEntityオブジェクトを生成して返す。 そしてIPluginRandomEntityが乱数を生成する。 という感じ。 具体的には、IPluginRandomEntity CreateRandomEntity(String iRandomArgorithmName, ref String errMsg)が、StreamRelay.NET.exe/他のプラグインから呼び出されるので、IPluginRandomEntityのインスタンスを返す。 そして、IPluginRandomEntity#GetRand()/IPluginRandomEntity#GetRand(UInt32) によって、乱数が生成される。 という感じ。 jp.dip.rocketeer.Plugins.IPluginRandom インターフェイスで定義しているメソッド/プロパティ 実装する必要があるのは以下 jp.dip.rocketeer.Plugins.IPluginRandomインターフェイスで定義されているのは、以下のとおり Dictionary<String, String> ListDescription() 引数文字列などを返す IPluginRandomEntity[] CreateAllRandomEntity() 全ての乱数ライブラリをロードする IPluginRandomEntity CreateRandomEntity(String iRandomArgorithmName, ref String errMsg) 名称(ListDescription()のKeyの方だと思う)から乱数オブジェクトの実体を作成する jp.dip.rocketeer.Plugins.IPluginBaseインターフェイスで定義されている分については、インターフェイス IPluginBase についてを参照 jp.dip.rocketeer.Plugins.IPluginRandomEntity インターフェイスで定義しているメソッド/プロパティ 実装する必要があるのは以下 jp.dip.rocketeer.Plugins.IPluginRandomEntityインターフェイスで定義されているのは、以下のとおり UInt32 GetRand() 乱数を返す(UInt32の範囲) UInt32 GetRand(UInt32 LimitMax) 乱数を返す(0~LimitMax) String ArgorithmName() 乱数アルゴリズム名を返す jp.dip.rocketeer.Plugins.IPluginBaseインターフェイスで定義されている分については、インターフェイス IPluginBase についてを参照 目次へ戻る 目次というか最初の一歩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity(C#)】OculusQuestでAndroidネイティブの音声認識機能を呼び出せるのか検証

はじめに VRヘビーユーザーからすれば当たり前のことかもしれませんが、 OculusQuestにマイクが搭載されているのはご存じでしたでしょうか。 ボイスチャットに利用されることがほとんどで、 その他の用途で使われている事例をあまり見たことがありませんでした。 (たぶん世の中にはたくさんある) しかし、最近目にした記事にボイスチャット以外の用途でマイクを使った事例がありました。 【参考リンク】:Synamon、ロゼッタと「リアルタイム多言語翻訳システム装備のVRオフィス」を共同開発 一言で説明すると、翻訳VRアプリです。 マイクを音声認識の受け口として利用しています。 そこで、私も勉強がてら"OculusQuestのマイクを利用したVRアプリ作りに挑戦してみたい"と思い、実際に作りました。 勉強がてら作成していた翻訳VRできました!?OculusQuestのマイクで拾った音声を音声認識APIに渡して、認識結果を翻訳APIに渡す、、、というやり方です?次はマルチ対応していきます?#OculusQuest #Unity pic.twitter.com/k95D73gEnh— KENTO⚽️XRエンジニア?Zenn100記事マラソン挑戦中29/100 (@okprogramming) April 3, 2021 Androidネイティブの音声認識機能 Androidには音声認識の機能が搭載されており、開発者がアプリに組み込めるようにその機能が公開されています。 【参考リンク】:RecognitionListener そして、そのAndroidのネイティブの機能をUnityからネイティブプラグインとして呼び出すことができます。 OculusQuestはAndroidベースであり、Androidアプリとしてビルドすることから、 "もしかしたら、Androidのネイティブプラグインを導入すれば動くんじゃないか?"という仮説に辿り着きました。 その検証を行った結果を記していきます。 ネイティブプラグイン作成 まずは下記リンクを参考にネイティブプラグインを作成します。 【参考リンク】:Unity向けAndroidネイティブプラグインの作り方 【参考リンク】: AndroidのSpeechRecognizerをネイティブプラグイン化してUnityで使う 記事の通りでほとんど詰まることなくいけました。 なんか動かん、という場合には地味にやることが多いので原因の特定はそこそこの苦行となります。 ですので面倒かもしれませんが最初からやり直した方が早い場合もあるかもしれません。 Javaで書いたプラグイン側のコードとC#で書いたUnity側のコードだけメモ残します。 プラグイン側のコード プラグイン側のコード package com.kento.speechtest; import java.util.ArrayList; import java.util.Locale; import android.content.Context; import android.os.Bundle; import android.speech.RecognitionListener; import android.speech.RecognizerIntent; import android.speech.SpeechRecognizer; import android.content.Intent; import static com.unity3d.player.UnityPlayer.UnitySendMessage; public class Speech { static public void StartRecognizer(Context context, final String callbackTarget, final String callbackMethod) { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, Locale.JAPAN.toString()); intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, context.getPackageName()); SpeechRecognizer recognizer = SpeechRecognizer.createSpeechRecognizer(context); recognizer.setRecognitionListener(new RecognitionListener() { @Override public void onReadyForSpeech(Bundle params) { // On Ready for speech. UnitySendMessage(callbackTarget, callbackMethod, "onReadyForSpeech"); } @Override public void onBeginningOfSpeech() { // On begining of speech. UnitySendMessage(callbackTarget, callbackMethod, "onBeginningOfSpeech"); } @Override public void onRmsChanged(float rmsdB) { // On Rms changed. UnitySendMessage(callbackTarget, callbackMethod, "onRmsChanged"); } @Override public void onBufferReceived(byte[] buffer) { // On buffer received. UnitySendMessage(callbackTarget, callbackMethod, "onBufferReceived"); } @Override public void onEndOfSpeech() { // On end of speech. UnitySendMessage(callbackTarget, callbackMethod, "onEndOfSpeech"); } @Override public void onError(int error) { // On error. UnitySendMessage(callbackTarget, callbackMethod, "onError"); } @Override public void onResults(Bundle results) { ArrayList<String> list = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); String str = ""; for (String s : list) { if (str.length() > 0) { str += "\n"; } str += s; } UnitySendMessage(callbackTarget, callbackMethod, "onResults\n" + str); } @Override public void onPartialResults(Bundle partialResults) { // On partial results. UnitySendMessage(callbackTarget, callbackMethod, "onPartialResults"); } @Override public void onEvent(int eventType, Bundle params) { // On event. UnitySendMessage(callbackTarget, callbackMethod, "onEvent"); } }); recognizer.startListening(intent); } } Unity側のコード Unity側のコード using System; using UnityEngine; using UnityEngine.Android; using UnityEngine.UI; // <summary> /// Androidのネイティブ音声認識機能呼び出し /// </summary> public class AndroidNativeSpeech : MonoBehaviour { [SerializeField] private Text recText; [SerializeField] private Image microPhoneImage; private void Start() { #if UNITY_ANDROID if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) { Debug.Log("Request"); Permission.RequestUserPermission(Permission.Microphone); } #endif } private void Update() { if (Input.touchCount > 0) { Touch touch = Input.touches[0]; if (touch.phase == TouchPhase.Began) { StartSpeech(); } } } /// <summary> /// 認識開始 /// </summary> private void StartSpeech() { #if UNITY_ANDROID var nativeRecognizer = new AndroidJavaClass("com.kento.speechtest.Speech"); var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); var context = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); context.Call("runOnUiThread", new AndroidJavaRunnable(() => { nativeRecognizer.CallStatic("StartRecognizer", context,gameObject.name, "CallbackMethod"); })); #endif } /// <summary> /// 音声認識後のコールバックとして実行するメソッド /// </summary> /// <param name="message">認識したメッセージ</param> private void CallbackMethod(string message) { var messages = message.Split('\n'); //ユーザーが話すのを開始した際のコールバック if (messages[0] == "onBeginningOfSpeech") { microPhoneImage.enabled = true; } //認識した音量変化のコールバック if (messages[0] == "onRmsChanged") { recText.text = "認識中..."; } //ユーザーが話すのを終了した際のコールバック if (messages[0] == "onEndOfSpeech") { microPhoneImage.enabled = false; } //認識結果の準備が完了したコールバック if (messages[0] == "onResults") { var msg = ""; for (var i = 1; i < messages.Length; i++) { msg += messages[i] + "\n"; } Debug.Log(msg); recText.text = msg; } } } デモ 音声が無いのでアレですが、無事動作しました。 VRで検証 結果から言うと動きませんでした。 adb logcat -s Unity:*とコマンドプロンプトに入力することで 動作中のUnity製Androidアプリのログを出力できます。(教えてくださった方ありがとうございます!) その方法で実行中のVRアプリのログを確認しましたが、特にエラーを吐くことも無くただただ動いていませんでした。 同じ検証で四苦八苦している先駆者がOculusの公式コミュティに質問を記していました。 (そして、それらしい回答もなく終了していました) 【引用元】:On-device Speech Recognition on the Quest with Unity 冒頭の翻訳VRは下記リンクの手法で実現しました。 【参考リンク】:【Unity(C#)】Watson API × OculusQuestで音声認識 【参考リンク】:【Unity(C#)】Microsoft Translatorの使い方 ちなみに、海外ユーザーは音声入力や音声コマンドが利用可能なようです。 音声コマンドは「ヘイ、フェイスブック!」ってやつです。 【参考リンク】:音声コマンド・音声入力 そのうち、日本語にも対応して開発者に公開されるのを期待します。 2021/04/08 追記 下記アセットを使えばオフラインの音声認識も可能なようです。 【Asset】:Android Speech TTS ただし、日本語はまだ対応していません。 英語での認識についてデモAPKを落としてきて試しましたところなかなか良い精度でした。 通常のネイティブの音声認識機能ではなく、下記ライブラリを使用しているようです。 Kaldi Speech Recognition Toolkit (教えてくださった方、感謝です!) おわりに 今回の検証結果はOculusQuestでAndroidネイティブの音声認識機能は呼び出せないとなりました。 もし成功した方がいらっしゃったら遠慮なくツッコんでください。(あわよくば方法知りたい)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む