経緯
「スタンド能力が欲しい。」
JOJOの奇妙な冒険を知っている方なら、必ず一度は思ったことがあるはず。
自分もそうなのだから他の方もそうに違いない。
そんな思想背景を踏まえて、まずはこちらを見てほしい。
1.とある男が「スタープラチナ」と叫ぶと
2.別の男が現れて「ザワールド」と叫びつつ時間を止める
3.最初の男が「そして時は動き出す」と言うと時間が動き始める
これやりたい。今の俺ならできるはず。
そう、LUISでね。
結果
注意) 音声は撮れてないけどしゃべってます
コードは後でGitに上げる予定。
ココにコードアップしました。
ざっくりとした仕様
- 「スタープラチナ」を認識
- 別の男を画面に出す
- 「ザワールド」を認識
- 時間を止める
- 「そして時は動き出す」を認識
- 時間を動かす
こんな感じだ。細かいところは気にしない。
時間止めたりするんだからそんなこと気にしてられない。
詳細
AzureからLUISアプリを作る
LUISを使う準備をする
ここまででLUISに関する説明してなかった事に気づく。
LUISとは、Microsoftが作った
ある文が何を意味しているかいい感じに判断する機能
である。
まぁ、今から使ってゆくので何となくそんなもんだと思ってれば大丈夫なはず。
では早速行動開始。
Azureアカウントを作ったらポータルへ飛ぶ。
ポータルへの入り方は覚えておいたほうが良い。
毎回毎回存在しないログインボタンを探す羽目になる(なった)ので、ここでしっかり書いておく
トップからポータルをクリック
早速LUISのリソースを追加する。
これがLUISだ!
名前とかをいい感じに入力。
初めての方は損を考えずにとりあえずやってみる事をお勧めする。
なぜなら自分も他のCloud環境で損した事があるから。
ただ一つ注意してほしいのが、場所。これから先色々いじる予定のある方は場所を合わせとくといいらしい。
俺はかっこいいから米国西部か米国東部にしてる。
リソースが追加出来たら早速LUISのコンソールを開きにかかる
さらにログインが必要。簡単には使わせてもらえない。
LUISアプリを作ってスタープラチナとかを学習させる
アプリの新規作成
Create New Appをクリックしてアプリを作る。
japaneseを選択しないと日本語を認識しないのでそこだけ注意。
いざ作成開始
Intentを作成する
Intentとは
'文の意味を定義するもの'
だ。
要は今回で言う所の「スタープラチナ!」とか「時は動き出す」とかを表すもの。
「スタープラチナ」を意味するので「sutapura」と名付けた。
こんな感じでスタープラチナを学ばせる。
「スタープラチナ」として扱ってほしい文字列が複数になった場合はEntityを作成する。
Entityを作成する
Entityとは
揺らぎのある単語をまとめるためのもの
だ。
これ以上の詳しい説明が欲しいなら、本を読めばいいと思う。
今回のように「すたーぷらちな」や「スター・プラチナ」も「スタープラチナ」とみなしたい場合に利用する。
Intentと同じく新規作成する。
IntentとEntityを結ぶ
再びsutapura Intentを開き、どこからどこまでがstar_plutinum Entityなのかを指定する。
これやり方が非常にわかりづらいのだが、
例文の「スター」をクリックした状態でマウスを右にずらし、「プラチナ」の右側に"]"が出た状態で再度クリックする。
分かりづらいだけあって文にしてもわかりづらい。
あらかた例文を打ちまくったら、右上のTrainボタンを押す。
学習 & テスト
学習が終わったら早速Testする。
似たような感じで「ザワールド」と「時は動き出す」も作る。
作り終わったらpublishしておく。
クライアントからLUISを呼ぶ
まずはLUISを呼ぶだけ呼ぶ
LUISは下記SDKを用意しているので、好きなものを使えばよい(2018.04.15現在)。今回はC#にした。
- Android
- Windows
- Node.js
- Python
まずはVisual Studio 2017を起動して新しくプロジェクトを作る。
男を出したりするので、フォームアプリとしてプロジェクトを作成する。
JSONを使うのでパッケージを入れる。
Install-Package Newtonsoft.Json
まずはシンプルに"スタープラチナ!"を呼んで何のインテントとみなされるか判断してみる。
LuisRequest request = new LuisRequest();
Task<LuisResult> task = Task.Run(() => request.MakeRequest("スタープラチナ!"));
Task.WaitAll(task);
LuisResult result = task.Result;
Console.WriteLine(result.topScoringIntent.intent);
LUISを呼び出すクラスを作る。
注意としてはURLのVerboseをfalseにする事。
class LuisRequest
{
public async Task<LuisResult> MakeRequest(string query)
{
var client = new HttpClient();
var uri = "<Luis Endpoint>" + query;
var response = await client.GetAsync(uri);
var strResponseContent = await response.Content.ReadAsStringAsync();
LuisResult result = JsonConvert.DeserializeObject<LuisResult>(strResponseContent.ToString());
return result;
}
}
LUISのEndpointは下記の場所
LUISの戻り値を定義する
class LuisResult
{
public string query{get; set;}
public TopScoringIntent topScoringIntent{get; set;}
public Entities[] entities {get; set;}
}
class TopScoringIntent
{
public string intent {get; set; }
public double score {get; set;}
}
class Entities
{
public string entity {get; set;}
public string type {get; set;}
public int startIndex {get; set;}
public int endIndex {get; set;}
public double score {get; set;}
}
よし、sutapuraって出てる。
音声経由で呼ぶ
やはりテキストでLUIS呼んでも物足りない。ここはVUIの出番だ。
「スタープラチナ」って言いたい。
まずはLUISよろしくazure上でSpeech APIリソースを追加する。
後はココを見つつ組み込んで行く。
まずはnugetで必要なものを入れる
Microsoft.ProjectOxford.SpeechRecognition-x64
Microsoft.ProjectOxford.SpeechRecognition-x86
必要な情報をメモっておく
必要なのはサブスクリプションキー(subscriptionkey)。
コレ超不親切で、Azureのポータル上「ココがサブスクリプションキーですよ!」なんて書いてなかったりする。
AuthentificationURIなるものも必要になるが、これはsampleを追ってゆくと書いてあったりする。
では早速フォームにボタンとラベルを追加してプログラムを修正。
自分で考えるところは特にない。ほぼサンプルのまま。
public partial class Form1 : Form
{
private delegate void InvokeDelegate();
private MicrophoneRecognitionClient micClient;
public Form1()
{
InitializeComponent();
LuisRequest request = new LuisRequest();
Task<LuisResult> task = Task.Run(() => request.MakeRequest("スタープラチナ!"));
Task.WaitAll(task);
LuisResult result = task.Result;
Console.WriteLine(result.topScoringIntent.intent);
// request.MakeRequest("ザ・ワールド!");
// request.MakeRequest("そして時は動き出す");
if (this.micClient == null) {
// this.CreateMicrophoneRecoClientWithIntent();
this.CreateMicrophoneRecoClient();
}
}
private void btnSutapura_Click(object sender, EventArgs e)
{
if (this.micClient != null)
{
this.micClient.EndMicAndRecognition();
this.micClient = null;
}
if (this.micClient == null) {
// this.CreateMicrophoneRecoClientWithIntent();
this.CreateMicrophoneRecoClient();
}
this.micClient.StartMicAndRecognition();
}
private SpeechRecognitionMode Mode
{
get
{
return SpeechRecognitionMode.LongDictation;
// return SpeechRecognitionMode.ShortPhrase;
}
}
private string DefaultLocale
{
get { return "ja_JP"; }
}
public string SubscriptionKey
{
get
{
return "サブスクリプションキー";
}
}
private string AuthenticationUri
{
get
{
return "https://api.cognitive.microsoft.com/sts/v1.0/issueToken";
}
}
private string LuisEndpointUrl
{
get {
return "";
}
}
private void CreateMicrophoneRecoClient()
{
this.micClient = SpeechRecognitionServiceFactory.CreateMicrophoneClient(
this.Mode,
this.DefaultLocale,
this.SubscriptionKey);
this.micClient.AuthenticationUri = this.AuthenticationUri;
// Event handlers for speech recognition results
this.micClient.OnMicrophoneStatus += this.OnMicrophoneStatus;
this.micClient.OnPartialResponseReceived += this.OnPartialResponseReceivedHandler;
if (this.Mode == SpeechRecognitionMode.ShortPhrase)
{
this.micClient.OnResponseReceived += this.OnMicShortPhraseResponseReceivedHandler;
}
else if (this.Mode == SpeechRecognitionMode.LongDictation)
{
this.micClient.OnResponseReceived += this.OnMicDictationResponseReceivedHandler;
}
this.micClient.OnConversationError += this.OnConversationErrorHandler;
}
private void OnMicDictationResponseReceivedHandler(object sender, SpeechResponseEventArgs e)
{
Console.WriteLine("status: " + e.PhraseResponse.RecognitionStatus);
if (e.PhraseResponse.RecognitionStatus == RecognitionStatus.EndOfDictation ||
e.PhraseResponse.RecognitionStatus == RecognitionStatus.RecognitionSuccess ||
e.PhraseResponse.RecognitionStatus == RecognitionStatus.DictationEndSilenceTimeout)
{
BeginInvoke(new InvokeDelegate(() =>
{
this.Text = "stop Sutapura";
if (e.PhraseResponse.Results.Length == 0)
{
Console.WriteLine("No phrase response is available.");
}
else
{
Console.WriteLine("********* Final n-BEST Results *********");
for (int i = 0; i < e.PhraseResponse.Results.Length; i++)
{
Console.WriteLine(
string.Format("[{0}] Confidence={1}, Text=\"{2}\"",
i,
e.PhraseResponse.Results[i].Confidence,
e.PhraseResponse.Results[i].DisplayText));
}
if (e.PhraseResponse.Results.Length > 0)
{
lblStapura.Text = e.PhraseResponse.Results[0].DisplayText;
}
}
this.micClient.EndMicAndRecognition();
}));
}
}
private void CreateMicrophoneRecoClientWithIntent()
{
this.micClient =
SpeechRecognitionServiceFactory.CreateMicrophoneClientWithIntentUsingEndpointUrl(
this.DefaultLocale,
this.SubscriptionKey,
this.LuisEndpointUrl);
this.micClient.AuthenticationUri = this.AuthenticationUri;
this.micClient.OnIntent += this.OnIntentHandler;
// Event handlers for speech recognition results
this.micClient.OnMicrophoneStatus += this.OnMicrophoneStatus;
this.micClient.OnPartialResponseReceived += this.OnPartialResponseReceivedHandler;
this.micClient.OnResponseReceived += this.OnMicShortPhraseResponseReceivedHandler;
this.micClient.OnConversationError += this.OnConversationErrorHandler;
}
private void OnMicShortPhraseResponseReceivedHandler(object sender, SpeechResponseEventArgs e)
{
BeginInvoke(new InvokeDelegate(() => {
this.micClient.EndMicAndRecognition();
Console.WriteLine(e);
}));
}
private void OnMicrophoneStatus(object sender, MicrophoneEventArgs e)
{
if (e.Recording) {
BeginInvoke(new InvokeDelegate(() => {
this.Text = "Go Sutapura";
Console.WriteLine("start recording");
}));
}
}
private void OnIntentHandler(object sender, SpeechIntentEventArgs e)
{
Console.WriteLine(e.Payload);
}
private void OnPartialResponseReceivedHandler(object sender, PartialSpeechResponseEventArgs e)
{
Console.WriteLine(e.PartialResult);
}
private void OnConversationErrorHandler(object sender, SpeechErrorEventArgs e)
{
Console.WriteLine( "Error code: " + e.SpeechErrorCode.ToString() + ", Error text: " + e.SpeechErrorText);
}
}
いざ実行。
おぉ、いい感じ。
ちなみに、プログラム中に"WithIntent"なる文言があったと思う。
実はSpeech APIのSDK上、LUISとの連動もできる。
が、今回はやらない。
やりたい人はぜひともやり方をQiitaに上げてもらえると嬉しい。
男出したり時間とめたりする
さぁ、ここまでで「言葉を認識する」所ができた。
後は「認識した内容に応じてエフェクトを掛ける」部分だ。
画面にカメラ画像を出す
画面、カメラと言えばOpenCVだ。というわけでOpenCvSharp3-AnyCPU
をNugetで取得。
下記コントロールを追加する
- PictureBox
- Timer
プログラムをこそこそ書いて、カメラが映るか確認。
using Microsoft.CognitiveServices.SpeechRecognition;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using OpenCvSharp;
using OpenCvSharp.Extensions;
namespace TokiwotomeruApp
{
public partial class Form1 : Form
{
private VideoCapture video = new VideoCapture(0);
private Mat bgr;
public Form1()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr = bgr.Flip(FlipMode.Y);
pictureBox1.BackgroundImage = bgr.ToBitmap();
bgr.Release();
}
private void Form1_Shown(object sender, EventArgs e)
{
timer1.Start();
}
}
}
いい感じだ。
「スタープラチナ」で男を出す
さて、いよいよスタープラチナを組み込んでゆく。
IT業界そこそこ長いが、スタープラチナを実装するのは初めてだ。
まずは好きなスタープラチナの画像を用意してプロジェクトのbin配下(Debug/Release)にコピーする。
尚、スタープラチナは背景透過で作成する事。
カメラ画像にかぶせるのはアルファブレンドを使う。
細かな説明はOpenCVでアルファチャンネル付きpngを表示するを見てもらったほうが良いかもしれない。
実行するとやたらめったらメモリ食ってるが仕事でもないので無視することにした。
Mat frame = video.RetrieveMat();
Mat bgr = frame.Flip(FlipMode.Y);
Mat bgrTemp = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
bgr.ConvertTo(bgrTemp, MatType.CV_32FC3);
if (alphaBackground == null)
{
// create front end Image and resize
Mat stapura = Cv2.ImRead(".\\stapura.png", ImreadModes.Unchanged);
Mat resizeStapura = resizeStapura = Mat.Zeros(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC4);
Point2f[] src =
{
new Point2f(0,0),
new Point2f(stapura.Cols, 0),
new Point2f(stapura.Cols, stapura.Rows),
new Point2f(0, stapura.Rows)
};
/*
float startY = bgrTemp.Rows / 3.0f;
float startX = bgrTemp.Cols / 3.0f;
float height = bgrTemp.Rows - startY;
float width = bgrTemp.Cols - startX;
*/
float height = 290;
float width = 300;
float startY = bgrTemp.Rows - height;
float startX = bgrTemp.Cols - width;
Point2f[] dst =
{
new Point2f(startX, startY),
new Point2f(startX + width, startY),
new Point2f(startX + width, startY + height),
new Point2f(startX, startY + height),
};
Mat resizeMat = Cv2.GetPerspectiveTransform(src, dst);
Cv2.WarpPerspective(stapura, resizeStapura, resizeMat ,resizeStapura.Size(), InterpolationFlags.Cubic, BorderTypes.Transparent);
stapura.Release();
stapura = null;
resizeMat.Release();
resizeMat = null;
// split channel
Mat[] chanelSplitedImage = Cv2.Split(resizeStapura);
Mat alphaStapura1C = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_8UC1);
Mat alphaUntiStapura1C = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_8UC1);
chanelSplitedImage[3].ConvertTo(alphaStapura1C, MatType.CV_8UC1);
Cv2.BitwiseNot(alphaStapura1C, alphaUntiStapura1C);
Cv2.Normalize(alphaStapura1C, alphaStapura1C, 0.0f, 1.0f, NormTypes.MinMax);
Cv2.Normalize(alphaUntiStapura1C, alphaUntiStapura1C, 0.0f, 1.0f, NormTypes.MinMax);
Mat rgbStapuraTemp = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC3);
Mat alphaStapuraTemp = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC3);
Cv2.Merge( new Mat[]
{
chanelSplitedImage[0],
chanelSplitedImage[1],
chanelSplitedImage[2]
}
, rgbStapuraTemp);
Cv2.Merge( new Mat[]
{
alphaStapura1C,
alphaStapura1C,
alphaStapura1C
}
, alphaStapuraTemp);
Mat alphaBackgroundTemp = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC3);
Cv2.Merge( new Mat[]
{
alphaUntiStapura1C,
alphaUntiStapura1C,
alphaUntiStapura1C
}
, alphaBackgroundTemp);
rgbStapura = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC3);
alphaStapura = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC3);
alphaBackground = new Mat(bgrTemp.Rows, bgrTemp.Cols, MatType.CV_32FC3);
rgbStapuraTemp.ConvertTo(rgbStapura ,MatType.CV_32FC3);
alphaStapuraTemp.ConvertTo(alphaStapura, MatType.CV_32FC3);
alphaBackgroundTemp.ConvertTo(alphaBackground, MatType.CV_32FC3);
resizeStapura.Release();
rgbStapuraTemp.Release();
alphaStapuraTemp.Release();
alphaBackgroundTemp.Release();
chanelSplitedImage[0].Release();
chanelSplitedImage[1].Release();
chanelSplitedImage[2].Release();
chanelSplitedImage[3].Release();
alphaStapura1C.Release();
src = null;
dst = null;
rgbStapuraTemp = null;
alphaStapuraTemp = null;
alphaBackgroundTemp = null;
resizeStapura = null;
chanelSplitedImage[0] = null;;
chanelSplitedImage[1] = null;;
chanelSplitedImage[2] = null;;
chanelSplitedImage[3] = null;;
chanelSplitedImage = null;
alphaStapura1C = null;
}
Mat tmp = rgbStapura.Mul(alphaStapura) + bgrTemp.Mul(alphaBackground);
tmp.ConvertTo(bgr, MatType.CV_8UC3);
bgrTemp.Release();
tmp.Release();
bgrTemp = null;
tmp = null;
if(bgr!= null)
{
if (bitmap == null)
{
bitmap = bgr.ToBitmap();
} else {
OpenCvSharp.Extensions.BitmapConverter.ToBitmap(bgr, bitmap);
}
pictureBox1.Image = bitmap;
}
frame.Release();
frame = null;
bgr.Release();
bgr = null;
ちゃんとスタープラチナが出せた。
「ザ・ワールド」で時を止める
スタープラチナがだせたら次は時間を止める。
おそらくQiitaの読者の中でも時間を止めたことがある人は少ないんじゃなかろうかと思う。
時間を止めるとどうなるかと言うと、画面中央から放射状に青色が広がる
のだ。
頭のいい人たちはこの説明でざわつくかもしれないが、Netflixでもそうなってたんだからしょうがない。
もう少し具体的に言うと、
- 3秒ぐらいかけて青くなる
- 背景はすでに止まってる
スタープラチナの応用で丸型のアルファブレンドを使って作成する。
もっといいやり方があると思うが、そう思う人がやればいいと思うし、できたら教えてほしい。
マネする。
下記コードをタイマーで回して描画する。。
if (theWorldBase == null)
{
theWorldBase = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Mat theWorldBaseTemp = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
bgr.ConvertTo(theWorldBaseTemp, MatType.CV_32FC3);
Mat frontend = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Mat backend = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Multiply(rgbStapura, alphaStapura, frontend);
Cv2.Multiply(theWorldBaseTemp, alphaBackground, backend);
Cv2.Add(frontend, backend, theWorldBase);
theWorldBaseTemp.Release();
theWorldBaseTemp = null;
frontend.Release();
frontend = null;
backend.Release();
backend = null;
centerPosition = new OpenCvSharp.Point(bgr.Cols / 2.0f, bgr.Rows / 2.0f);
circleSize = 1;
// magic 5 px
circleMaxSize = (int)Math.Ceiling(Math.Sqrt(Math.Pow(centerPosition.X, 2) + Math.Pow(centerPosition.Y, 2))) - 5;
theWorldComplete = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
};
if (circleSize < (circleMaxSize))
{
circleSize += (int)Math.Floor(circleMaxSize / 30.0f);
if (circleSize > circleMaxSize)
{
circleSize = circleMaxSize;
}
Console.WriteLine(circleSize);
// create circle blue mask
Mat blueMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Circle(blueMask, centerPosition, circleSize, new Scalar(1.0d, 0.0d, 0.0d), -1);
// create alpha mask
Mat[] blueMaskArr = Cv2.Split(blueMask);
Mat frontAlphaMask1C = blueMaskArr[0].Clone();
Mat blueBackgroundMask = Mat.Ones(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Circle(blueBackgroundMask, centerPosition, circleSize, new Scalar(0.0d, 1.0d, 1.0d), -1);
Mat[] backgroundMaskArr = Cv2.Split(blueBackgroundMask);
Mat backAlphaMask1C = backgroundMaskArr[0];
Cv2.Normalize(frontAlphaMask1C, frontAlphaMask1C, 0.0f, 1.0f, NormTypes.MinMax);
Cv2.Normalize(backAlphaMask1C, backAlphaMask1C, 0.0f, 1.0f, NormTypes.MinMax);
Mat frontAlphaMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Mat frontAlphaMaskTemp= new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Merge(new Mat[]
{
frontAlphaMask1C,
frontAlphaMask1C,
frontAlphaMask1C
}
, frontAlphaMaskTemp);
frontAlphaMaskTemp.ConvertTo(frontAlphaMask, MatType.CV_32FC3);
Mat backAlphaMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Mat backAlphaMaskTemp = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Merge(new Mat[]
{
backAlphaMask1C,
backAlphaMask1C,
backAlphaMask1C
}
, backAlphaMaskTemp);
backAlphaMaskTemp.ConvertTo(backAlphaMask, MatType.CV_32FC3);
// create background
Mat background = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Multiply(theWorldBase, backAlphaMask, background);
// create frontend
Mat frontend = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Multiply(theWorldBase, frontAlphaMask, frontend );
Cv2.Multiply(frontend, blueMask, frontend );
// create Image
Cv2.Add(frontend, background, theWorldComplete);
theWorldComplete .ConvertTo(bgr, MatType.CV_8UC3);
blueMaskArr[0].Release();
blueMaskArr[1].Release();
blueMaskArr[2].Release();
blueBackgroundMask.Release();
backgroundMaskArr[0].Release();
backgroundMaskArr[1].Release();
backgroundMaskArr[2].Release();
backgroundMaskArr = null;
blueMaskArr = null;
frontAlphaMaskTemp.Release();
frontAlphaMaskTemp = null;
backAlphaMask1C.Release();
backAlphaMask1C = null;
backAlphaMaskTemp.Release();
backAlphaMaskTemp = null;
frontAlphaMask1C.Release();
frontAlphaMask1C = null;
frontAlphaMask.Release();
blueMask.Release();
backAlphaMask.Release();
background.Release();
frontend.Release();
} else {
Mat blueMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3, new Scalar(1,0f, 0.0f, 0.0f));
Cv2.Multiply(theWorldBase, blueMask, theWorldComplete);
theWorldComplete.ConvertTo(bgr, MatType.CV_8UC3);
blueMask.Release();
blueMask = null;
}
時間が止められるとこうなるはず。
「時は動き出す」で時を動かす
スタープラチナも出せて時間も止めれた。あとは動かすだけ。ちょろい。
時間が動き出すと画面中央に向かって青色の領域が小さくなる
だ。
ちなみに青色の領域がなくなると時間が動き出す。
特に書くことないのでタイマーで回すコードだけ
if (circleSize >= (circleMaxSize)) {
circleSize = circleMaxSize;
startTimeComplete = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3, new Scalar(1,0f, 0.0f, 0.0f));
}
if (circleSize > 5) {
circleSize -= (int)Math.Floor(circleMaxSize / 30.0f);
if (circleSize > circleMaxSize)
{
circleSize = circleMaxSize;
}
Console.WriteLine(circleSize);
// create circle blue mask
Mat blueMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Circle(blueMask, centerPosition, circleSize, new Scalar(1.0d, 0.0d, 0.0d), -1);
// create alpha mask
Mat[] blueMaskArr = Cv2.Split(blueMask);
Mat frontAlphaMask1C = blueMaskArr[0].Clone();
Mat blueBackgroundMask = Mat.Ones(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Circle(blueBackgroundMask, centerPosition, circleSize, new Scalar(0.0d, 1.0d, 1.0d), -1);
Mat[] backgroundMaskArr = Cv2.Split(blueBackgroundMask);
Mat backAlphaMask1C = backgroundMaskArr[0];
Cv2.Normalize(frontAlphaMask1C, frontAlphaMask1C, 0.0f, 1.0f, NormTypes.MinMax);
Cv2.Normalize(backAlphaMask1C, backAlphaMask1C, 0.0f, 1.0f, NormTypes.MinMax);
Mat frontAlphaMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Mat frontAlphaMaskTemp= new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Merge(new Mat[]
{
frontAlphaMask1C,
frontAlphaMask1C,
frontAlphaMask1C
}
, frontAlphaMaskTemp);
frontAlphaMaskTemp.ConvertTo(frontAlphaMask, MatType.CV_32FC3);
Mat backAlphaMask = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Mat backAlphaMaskTemp = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Merge(new Mat[]
{
backAlphaMask1C,
backAlphaMask1C,
backAlphaMask1C
}
, backAlphaMaskTemp);
backAlphaMaskTemp.ConvertTo(backAlphaMask, MatType.CV_32FC3);
// create background
Mat background = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Multiply(theWorldBase, backAlphaMask, background);
// create frontend
Mat frontend = new Mat(bgr.Rows, bgr.Cols, MatType.CV_32FC3);
Cv2.Multiply(theWorldBase, frontAlphaMask, frontend );
Cv2.Multiply(frontend, blueMask, frontend );
// create Image
Cv2.Add(frontend, background, startTimeComplete);
startTimeComplete.ConvertTo(bgr, MatType.CV_8UC3);
blueMaskArr[0].Release();
blueMaskArr[1].Release();
blueMaskArr[2].Release();
blueBackgroundMask.Release();
backgroundMaskArr[0].Release();
backgroundMaskArr[1].Release();
backgroundMaskArr[2].Release();
backgroundMaskArr = null;
blueMaskArr = null;
frontAlphaMaskTemp.Release();
frontAlphaMaskTemp = null;
backAlphaMask1C.Release();
backAlphaMask1C = null;
backAlphaMaskTemp.Release();
backAlphaMaskTemp = null;
frontAlphaMask1C.Release();
frontAlphaMask1C = null;
frontAlphaMask.Release();
blueMask.Release();
backAlphaMask.Release();
background.Release();
frontend.Release();
} else {
currentStage = stage.None;
}
まとめ
調子に乗ってスタープラチナを呼び出したり、クラウドで色々やってみようとすると、
ちゃんと請求が来る。azure賢い。