Galileoに続いてEdisonを購入しました。
購入したのは「Intel Edison Breakout Board Kit」です。
これはEdisonメインモジュール+拡張ボードの組み合わせです。
これ以外に「Intel Edison Kit for Arduino」もありますが、Galileoと大体同じようなものですね。
実際開発環境もArduinoIDEなどはGalileoと共通のようですが、BreakoutBoardの場合はGPIOなどはピンソケットなどをボード上にはんだ付け等行わないと使えません。
なのでLinuxサーバ的な使い方を色々試したいと思います。(そのうちGPIOなども使ってみるかもしれませんが)
箱の大きさはGalileoと比べて随分と小さいです。
GalileoGen2と並べてみました。左側がメインモジュール、右側がBreakoutボードです。
2014年11月2日日曜日
2014年8月22日金曜日
Intel Galileo Gen2 on Windows
今日はKINECTからちょっと離れてCPUボードの話題です。
ここ数年で1万円くらいまでで手に入るCPUボードは飛躍的に増えてきました。まぁ、増えたのはARM搭載のボードがほとんどのような気もしますけど。
実際今手元にあるのはRasberryPi,mbedボード,Arduinoなどなど。
そして今回はIntel Galileo Gen2を購入してみました。
大きな違いはCPUがx86 Pentium互換ということです。(詳しい仕様はこちら)
標準ではLinux+Arduinoが動作(この場合Linux上でArduinoをエミュレートしている?)します。
で、x86CPUでメモリもそこそこ搭載(DDR3-256MB)ならWindowsも動くかもと考えたどうかは知りませんが、MicrosoftではGalileo用Windowsの提供を開始しました。(ただし、現在はまだβ?正式版ではありません)
Galileo用Windowsを手に入れるにはこのサイトにレジストすると手に入れることができます。
実際のインストールはこちらのサイトを参考にさせていただきました。
特に問題もなく無事インストールできました。
Galileo Watcherで右クリックしてコンテキストメニュー内の「Open Network Share」を選択するとGalileoのCドライブを開くことができます。
もちろんPCからファイルの作成やコピーなども可能です。
たぶんEXE単体のコマンドラインプログラムなら普通に動きそうです。
System32フォルダ内をざっと見た感じでは基本的なコマンドラインプログラムは一通りありました。
まだ開発版なので空っぽのフォルダなんかもありますが、これから色々できそうな感じです。
USBデバイスとかつながるのかな?Webカメラでもつないでみようかと思います。
ここ数年で1万円くらいまでで手に入るCPUボードは飛躍的に増えてきました。まぁ、増えたのはARM搭載のボードがほとんどのような気もしますけど。
実際今手元にあるのはRasberryPi,mbedボード,Arduinoなどなど。
そして今回はIntel Galileo Gen2を購入してみました。
大きな違いはCPUがx86 Pentium互換ということです。(詳しい仕様はこちら)
標準ではLinux+Arduinoが動作(この場合Linux上でArduinoをエミュレートしている?)します。
で、x86CPUでメモリもそこそこ搭載(DDR3-256MB)ならWindowsも動くかもと考えたどうかは知りませんが、MicrosoftではGalileo用Windowsの提供を開始しました。(ただし、現在はまだβ?正式版ではありません)
Galileo用Windowsを手に入れるにはこのサイトにレジストすると手に入れることができます。
実際のインストールはこちらのサイトを参考にさせていただきました。
特に問題もなく無事インストールできました。
Galileo Watcherで右クリックしてコンテキストメニュー内の「Open Network Share」を選択するとGalileoのCドライブを開くことができます。
たぶんEXE単体のコマンドラインプログラムなら普通に動きそうです。
System32フォルダ内をざっと見た感じでは基本的なコマンドラインプログラムは一通りありました。
まだ開発版なので空っぽのフォルダなんかもありますが、これから色々できそうな感じです。
USBデバイスとかつながるのかな?Webカメラでもつないでみようかと思います。
2014年7月23日水曜日
KINECT V2 音声シャッター(SDK2.0β,C#)
FaceBookのグループでスナップショットを撮るときクリックするのが大変という話があったので、音声認識でスナップショットをとれるようにしてみました。
SDK2.0βのColorBasics-WPFサンプルに音声認識部分を追加しています。
今回もKinectAudioStream.csをプロジェクトに追加しています。
変更はnamespaceとMainWindow.xaml.csだけです。
MainWindow.xaml.cs
説明:
前回の音声認識サンプルとほぼ同じです。スナップショットを撮るルーチンもClickイベント部分と同じです。
慣れてくれば簡単にイベント部分を音声認識イベントに変更できそうです。
SDK2.0βのColorBasics-WPFサンプルに音声認識部分を追加しています。
今回もKinectAudioStream.csをプロジェクトに追加しています。
変更はnamespaceとMainWindow.xaml.csだけです。
MainWindow.xaml.cs
//------------------------------------------------------------------------------ // <copyright file="MainWindow.xaml.cs" company="Microsoft"> // Copyright (c) Microsoft Corporation. All rights reserved. // </copyright> //------------------------------------------------------------------------------ namespace Microsoft.Samples.Kinect.ColorBasics.speech { using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using Microsoft.Kinect; using Microsoft.Speech.AudioFormat; using Microsoft.Speech.Recognition; /// <summary> /// Interaction logic for MainWindow /// </summary> public partial class MainWindow : Window, INotifyPropertyChanged { /// <summary> /// Size of the RGB pixel in the bitmap /// </summary> private readonly uint bytesPerPixel = 0; /// <summary> /// Active Kinect sensor /// </summary> private KinectSensor kinectSensor = null; /// <summary> /// Reader for color frames /// </summary> private ColorFrameReader colorFrameReader = null; /// <summary> /// Bitmap to display /// </summary> private WriteableBitmap colorBitmap = null; /// <summary> /// Intermediate storage for receiving frame data from the sensor /// </summary> private byte[] colorPixels = null; /// <summary> /// Current status text to display /// </summary> private string statusText = null; /// <summary> /// 音声ストリーム /// </summary> KinectAudioStream convertStream = null; /// <summary> /// 音声認識エンジン /// </summary> SpeechRecognitionEngine engine=null; /// <summary> /// 日本語音声認識エンジンのID /// </summary> const string engin_id = "SR_MS_ja-JP_Kinect_11.0"; /// <summary> /// Initializes a new instance of the MainWindow class. /// </summary> public MainWindow() { // get the kinectSensor object this.kinectSensor = KinectSensor.GetDefault(); // open the reader for the color frames this.colorFrameReader = this.kinectSensor.ColorFrameSource.OpenReader(); // wire handler for frame arrival this.colorFrameReader.FrameArrived += this.Reader_ColorFrameArrived; // create the colorFrameDescription from the ColorFrameSource using Bgra format FrameDescription colorFrameDescription = this.kinectSensor.ColorFrameSource.CreateFrameDescription(ColorImageFormat.Bgra); // rgba is 4 bytes per pixel this.bytesPerPixel = colorFrameDescription.BytesPerPixel; // allocate space to put the pixels to be rendered this.colorPixels = new byte[colorFrameDescription.Width * colorFrameDescription.Height * this.bytesPerPixel]; // create the bitmap to display this.colorBitmap = new WriteableBitmap(colorFrameDescription.Width, colorFrameDescription.Height, 96.0, 96.0, PixelFormats.Bgr32, null); // set IsAvailableChanged event notifier this.kinectSensor.IsAvailableChanged += this.Sensor_IsAvailableChanged; // open the sensor this.kinectSensor.Open(); // set the status text this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText : Properties.Resources.NoSensorStatusText; // use the window object as the view model in this simple example this.DataContext = this; // initialize the components (controls) of the window this.InitializeComponent(); //////////////////////////////////////////////////////////// //音声認識関連 //////////////////////////////////////////////////////////// //音声入力設定 IReadOnlyList<AudioBeam> audioBeamList = kinectSensor.AudioSource.AudioBeams; Stream audioStream = audioBeamList[0].OpenInputStream(); //audioStreamのビット変換 convertStream = new KinectAudioStream(audioStream); //音声認識エンジン設定 engine = new SpeechRecognitionEngine(engin_id); //認識するワード var word = new Choices(); word.Add("チーズ"); word.Add("キャプチャ"); word.Add("シャッター"); //グラマービルダー var gb = new GrammarBuilder(); //言語設定 gb.Culture = engine.RecognizerInfo.Culture; //グラマービルダーに単語を登録 gb.Append(word); //グラマー作成 var g = new Grammar(gb); //認識エンジンにグラマーを登録 engine.LoadGrammar(g); //イベントの登録 engine.SpeechRecognized += engine_SpeechRecognized; engine.SpeechRecognitionRejected += engine_SpeechRecognitionRejected; //オーディオストリームの変換をアクティブにする convertStream.SpeechActive = true; //認識エンジンに入力設定 engine.SetInputToAudioStream(convertStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null)); //非同期で連続認識の開始 engine.RecognizeAsync(RecognizeMode.Multiple); } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void engine_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e) { throw new NotImplementedException(); } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void engine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e) { //認識信頼度の閾値 //(認識信頼度 -1<0<1 -1が低信頼度 0が標準 1が高信頼度 数字が大きいほど認識率が厳しくなる) const double ConfidenceThreshold = 0.6; if (e.Result.Confidence >= ConfidenceThreshold) { //認識した単語を表示 if (this.colorBitmap != null) { // create a png bitmap encoder which knows how to save a .png file BitmapEncoder encoder = new PngBitmapEncoder(); // create frame from the writable bitmap and add to encoder encoder.Frames.Add(BitmapFrame.Create(this.colorBitmap)); string time = System.DateTime.Now.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat); string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); string path = Path.Combine(myPhotos, "KinectScreenshot-Color-" + time + ".png"); // write the new file to disk try { // FileStream is IDisposable using (FileStream fs = new FileStream(path, FileMode.Create)) { encoder.Save(fs); } this.StatusText = string.Format(Properties.Resources.SavedScreenshotStatusTextFormat, path); } catch (IOException) { this.StatusText = string.Format(Properties.Resources.FailedScreenshotStatusTextFormat, path); } } } } /// <summary> /// INotifyPropertyChangedPropertyChanged event to allow window controls to bind to changeable data /// </summary> public event PropertyChangedEventHandler PropertyChanged; /// <summary> /// Gets the bitmap to display /// </summary> public ImageSource ImageSource { get { return this.colorBitmap; } } /// <summary> /// Gets or sets the current status text to display /// </summary> public string StatusText { get { return this.statusText; } set { if (this.statusText != value) { this.statusText = value; // notify any bound elements that the text has changed if (this.PropertyChanged != null) { this.PropertyChanged(this, new PropertyChangedEventArgs("StatusText")); } } } } /// <summary> /// Execute shutdown tasks /// </summary> /// <param name="sender">object sending the event</param> /// <param name="e">event arguments</param> private void MainWindow_Closing(object sender, CancelEventArgs e) { if (this.colorFrameReader != null) { // ColorFrameReder is IDisposable this.colorFrameReader.Dispose(); this.colorFrameReader = null; } if (this.kinectSensor != null) { this.kinectSensor.Close(); this.kinectSensor = null; } } /// <summary> /// Handles the user clicking on the screenshot button /// </summary> /// <param name="sender">object sending the event</param> /// <param name="e">event arguments</param> private void ScreenshotButton_Click(object sender, RoutedEventArgs e) { if (this.colorBitmap != null) { // create a png bitmap encoder which knows how to save a .png file BitmapEncoder encoder = new PngBitmapEncoder(); // create frame from the writable bitmap and add to encoder encoder.Frames.Add(BitmapFrame.Create(this.colorBitmap)); string time = System.DateTime.Now.ToString("hh'-'mm'-'ss", CultureInfo.CurrentUICulture.DateTimeFormat); string myPhotos = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures); string path = Path.Combine(myPhotos, "KinectScreenshot-Color-" + time + ".png"); // write the new file to disk try { // FileStream is IDisposable using (FileStream fs = new FileStream(path, FileMode.Create)) { encoder.Save(fs); } this.StatusText = string.Format(Properties.Resources.SavedScreenshotStatusTextFormat, path); } catch (IOException) { this.StatusText = string.Format(Properties.Resources.FailedScreenshotStatusTextFormat, path); } } } /// <summary> /// Handles the color frame data arriving from the sensor /// </summary> /// <param name="sender">object sending the event</param> /// <param name="e">event arguments</param> private void Reader_ColorFrameArrived(object sender, ColorFrameArrivedEventArgs e) { bool colorFrameProcessed = false; // ColorFrame is IDisposable using (ColorFrame colorFrame = e.FrameReference.AcquireFrame()) { if (colorFrame != null) { FrameDescription colorFrameDescription = colorFrame.FrameDescription; // verify data and write the new color frame data to the display bitmap if ((colorFrameDescription.Width == this.colorBitmap.PixelWidth) && (colorFrameDescription.Height == this.colorBitmap.PixelHeight)) { if (colorFrame.RawColorImageFormat == ColorImageFormat.Bgra) { colorFrame.CopyRawFrameDataToArray(this.colorPixels); } else { colorFrame.CopyConvertedFrameDataToArray(this.colorPixels, ColorImageFormat.Bgra); } colorFrameProcessed = true; } } } // we got a frame, render if (colorFrameProcessed) { this.RenderColorPixels(); } } /// <summary> /// Renders color pixels into the writeableBitmap. /// </summary> private void RenderColorPixels() { this.colorBitmap.WritePixels( new Int32Rect(0, 0, this.colorBitmap.PixelWidth, this.colorBitmap.PixelHeight), this.colorPixels, this.colorBitmap.PixelWidth * (int)this.bytesPerPixel, 0); } /// <summary> /// Handles the event which the sensor becomes unavailable (E.g. paused, closed, unplugged). /// </summary> /// <param name="sender">object sending the event</param> /// <param name="e">event arguments</param> private void Sensor_IsAvailableChanged(object sender, IsAvailableChangedEventArgs e) { // on failure, set the status text this.StatusText = this.kinectSensor.IsAvailable ? Properties.Resources.RunningStatusText : Properties.Resources.SensorNotAvailableStatusText; } } }
説明:
前回の音声認識サンプルとほぼ同じです。スナップショットを撮るルーチンもClickイベント部分と同じです。
慣れてくれば簡単にイベント部分を音声認識イベントに変更できそうです。
2014年7月20日日曜日
KINECT V2で音声認識(SDK2.0β C#)
7月15日からKINECT for Windows V2が発売されました。SDK2.0もパブリックベータ版が同時に公開されています。
今回はハードが大幅に変更になっているのでSDKも色々変わっています。
ColorやDepthの解説はぼちぼちブログやIT系メディアで紹介されていますが、音声認識関連はなかなか出てこないです。
取り急ぎSDKのSpeechサンプルを参考にして簡単なプログラムを作ってみました。
開発環境は
Windows8.1Pro 64bit
Visual Studio Pro 2012
です。
その他 KINECT SDK2.0 及び SpeechPlatform SDK 11.0,LanguagePack_jaJPもインストールしてください。
サンプルプログラムはC#+WPFで作成しています。
最初にプロジェクトを新しく作成してから、SDK2.0βのSpeech Basics-WPFプロジェクトよりKinectAudioStream.csファイルをインポートして、namespaceを変更してください。
MainWindow.xaml
説明:
テキストボックスを2つ配置しています。
MainWindow.xaml.cs
説明:
大きく変わったのはマイクで録音した時のオーディオフォーマットが変更になっているところです。V1の時はPCM 16bitだったのがV2ではPCM 32bit floatになっています。音声認識エンジンへの入力フォーマットはPCM 16bitの必要があるので32bitfloat->16bitへの変換が必要になります。
今回はSDKのサンプルプログラムから変換用クラスをそのままインポートして利用しています。
また、音声ストリームの指定方法が少し変更になっていますね。AudioBeam配列から選択するように変わりました。
音声認識部分についてはSpeechPlatformのバージョンは以前と変わっていないので特に変更はありません。
入力音声を配列で取得できるということは複数人の音声を識別できそうです。
現在はまだそこまで調べていないので何とも言えないのですが。できるならイントロクイズとかに使えるかもしれないですね。
今回はハードが大幅に変更になっているのでSDKも色々変わっています。
ColorやDepthの解説はぼちぼちブログやIT系メディアで紹介されていますが、音声認識関連はなかなか出てこないです。
取り急ぎSDKのSpeechサンプルを参考にして簡単なプログラムを作ってみました。
開発環境は
Windows8.1Pro 64bit
Visual Studio Pro 2012
です。
その他 KINECT SDK2.0 及び SpeechPlatform SDK 11.0,LanguagePack_jaJPもインストールしてください。
サンプルプログラムはC#+WPFで作成しています。
最初にプロジェクトを新しく作成してから、SDK2.0βのSpeech Basics-WPFプロジェクトよりKinectAudioStream.csファイルをインポートして、namespaceを変更してください。
<Window x:Class="speech_jp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" Closing="Window_Closing"> <Grid> <TextBox x:Name="tb1" HorizontalAlignment="Left" Height="30" Margin="10,82,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="497" FontSize="20"/> <TextBox x:Name="tb2" HorizontalAlignment="Left" Height="30" Margin="10,153,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="497" FontSize="20"/> <Label Content="認識した単語" HorizontalAlignment="Left" Margin="10,52,0,0" VerticalAlignment="Top" Width="107"/> <Label Content="信頼度" HorizontalAlignment="Left" Margin="10,123,0,0" VerticalAlignment="Top" Width="74"/> </Grid> </Window>
説明:
テキストボックスを2つ配置しています。
MainWindow.xaml.cs
using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Windows; using System.Windows.Documents; using System.Windows.Media; using Microsoft.Kinect; using Microsoft.Speech.AudioFormat; using Microsoft.Speech.Recognition; namespace speech_jp { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { //KINECT KinectSensor kinect=null; //AudioStream KinectAudioStream convertStream=null; //音声認識エンジン SpeechRecognitionEngine engine; //日本語音声認識エンジンのID const string engin_id = "SR_MS_ja-JP_Kinect_11.0"; //英語音声認識エンジンのID //const string engin_id = "SR_MS_en-US_Kinect_11.0"; /// <summary> /// /// </summary> public MainWindow() { InitializeComponent(); } /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Window_Loaded(object sender, RoutedEventArgs e) { //KINECTの準備 try { kinect = KinectSensor.GetDefault(); if (kinect == null) { throw new Exception("KINECTを開けません。"); } //KINECTの使用開始 kinect.Open(); //音声入力設定 IReadOnlyList<AudioBeam> audioBeamList = kinect.AudioSource.AudioBeams; Stream audioStream = audioBeamList[0].OpenInputStream(); //audioStreamのビット変換 convertStream = new KinectAudioStream(audioStream); } catch (Exception ex) { MessageBox.Show(ex.Message); Close(); } //音声認識エンジン設定 engine = new SpeechRecognitionEngine(engin_id); //認識するワード var word = new Choices(); word.Add("ハンバーグ"); word.Add("ラーメン"); word.Add("焼肉"); word.Add("すし"); word.Add("スパゲッティ"); word.Add("卵焼き"); word.Add("ぎゅう丼"); word.Add("うどん"); word.Add("からあげ"); //グラマービルダー var gb = new GrammarBuilder(); //言語設定 gb.Culture = engine.RecognizerInfo.Culture; //グラマービルダーに単語を登録 gb.Append(word); //グラマー作成 var g = new Grammar(gb); //認識エンジンにグラマーを登録 engine.LoadGrammar(g); //イベントの登録 engine.SpeechRecognized += engine_SpeechRecognized; engine.SpeechRecognitionRejected += engine_SpeechRecognitionRejected; //オーディオストリームの変換をアクティブにする convertStream.SpeechActive = true; //認識エンジンに入力設定 engine.SetInputToAudioStream(convertStream, new SpeechAudioFormatInfo(EncodingFormat.Pcm, 16000, 16, 1, 32000, 2, null)); //非同期で連続認識の開始 engine.RecognizeAsync(RecognizeMode.Multiple); } void engine_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e) { tb1.Text = "認識できません。"; tb2.Text = ""; } void engine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e) { //認識信頼度の閾値 //(認識信頼度 -1<0<1 -1が低信頼度 0が標準 1が高信頼度 数字が大きいほど認識率が厳しくなる) const double ConfidenceThreshold = 0.6; if (e.Result.Confidence >= ConfidenceThreshold) { //認識した単語を表示 tb1.Text = e.Result.Text; tb2.Text = e.Result.Confidence.ToString(); } } private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { //終了処理 if (kinect != null) { kinect.Close(); kinect = null; } } } }
説明:
大きく変わったのはマイクで録音した時のオーディオフォーマットが変更になっているところです。V1の時はPCM 16bitだったのがV2ではPCM 32bit floatになっています。音声認識エンジンへの入力フォーマットはPCM 16bitの必要があるので32bitfloat->16bitへの変換が必要になります。
今回はSDKのサンプルプログラムから変換用クラスをそのままインポートして利用しています。
また、音声ストリームの指定方法が少し変更になっていますね。AudioBeam配列から選択するように変わりました。
音声認識部分についてはSpeechPlatformのバージョンは以前と変わっていないので特に変更はありません。
入力音声を配列で取得できるということは複数人の音声を識別できそうです。
現在はまだそこまで調べていないので何とも言えないのですが。できるならイントロクイズとかに使えるかもしれないですね。
ラベル:
C#,
KINECT,
SpeechPlatform,
V2,
音声認識
2014年4月24日木曜日
TechCrunchHackathon Osakaに参加してきました。画像センシングコンポ(HVC)について
4月12-13日にかけて開催されたTechCrunchHackathon Osakaに参加してきました。
その時に作成したプロダクトに使ったオムロン製 画像センシングコンポ(HVC)について使用した感想を述べたいと思います。
まず、ボードについてはここを参照してください。
オムロンの組み込み向けライブラリOKAO Visionをプロセッサに組み込んでカメラモジュールと合わせて搭載したボード(以下HVC)です。
カメラで取り込んだ映像を解析し、結果を出力します。このボードひとつで顔検出、人体検出、性別推定、年齢推定、視線推定、、目つむり推定、顔向き推定、表情推定、手(のひら)検出、顔認証の各データが取得できます。
ボードとの入出力はUART3.3Vのシリアル通信になります。
評価ボードにはUSB変換基板も付属しているのでPCとの接続は簡単にできます。(変換基板からUSB経由で5V電源を供給しています。)
別で5V電源を供給できればマイコンボードとの接続も簡単かと思います。
今回はチームメイトがMicroUSB変換ケーブルを持っていたので、Nexus7と接続を試してみましたが問題なく接続できました。USBHost機能を持ったAndroid端末も接続できるようです。
さて、実際に使用するときにはプログラムからシリアル通信でコマンドコードを送ることになります。
コマンドコードと受信データはバイトデータなので、それらに対応した開発言語ならどれを使っても開発できます。
サンプルはWindowsデスクトップアプリでした。ハッカソン参加者で使っていた言語はC#、python、JAVA(Android)といったところでしょうか。
センシングの性能としてはボードから1.5~2mくらいまでが解析できる距離(顔認識系)のようです。これはカメラモジュールの性能にもよる可能性があるので、モジュールを交換できるならもう少し距離が延びるかもしれません。
一人分の顔認識にかかる時間は約1秒未満くらいでしょうか、瞬時とはいかないようです。
ボードからは認識時に使った画像データも取得できます。サンプルプログラムではリアルタイムで画像表示して解析データを出力できていたので、もう少し時間があれば色々試せたのかもしれません。
今回使う機会がなかったのは顔認証機能でしたが、マニュアルをちらっと読んでみただけなのですが、認証用の画像データ(もしかすると特徴点データ?)はボード上のフラッシュメモリーに登録するようになっているようです。
センシング機能をボード上ですべて行っているので非力な端末と接続しても、十分な処理速度を保てるのではないでしょうか。
いくつか希望を述べるとすると
1.10種類のアルゴリズムすべてではなくて、機能別バージョンがあってもいいかなと思います。
人体検出+顔検出、顔検出+顔認識(推定系)、全部ありバージョンなどなど・・・顔認証ありバージョンとなしバージョンでもいいかも。 用途として顔認証はなくてもいい場合も十分あると思います。
2.ぜひ企業向けだけではなく個人向けにも販売してほしいです。今回は幸運にも評価ボードを貸していただけたのでよかったのですが、結構興味を持っている個人もいると思います。開発キット7万円はちょっと高いかな・・・・少しでも安くなればいいのですが。
以上簡単ですが、HVCを使ってみた感想でした。
その時に作成したプロダクトに使ったオムロン製 画像センシングコンポ(HVC)について使用した感想を述べたいと思います。
まず、ボードについてはここを参照してください。
オムロンの組み込み向けライブラリOKAO Visionをプロセッサに組み込んでカメラモジュールと合わせて搭載したボード(以下HVC)です。
カメラで取り込んだ映像を解析し、結果を出力します。このボードひとつで顔検出、人体検出、性別推定、年齢推定、視線推定、、目つむり推定、顔向き推定、表情推定、手(のひら)検出、顔認証の各データが取得できます。
ボードとの入出力はUART3.3Vのシリアル通信になります。
評価ボードにはUSB変換基板も付属しているのでPCとの接続は簡単にできます。(変換基板からUSB経由で5V電源を供給しています。)
別で5V電源を供給できればマイコンボードとの接続も簡単かと思います。
今回はチームメイトがMicroUSB変換ケーブルを持っていたので、Nexus7と接続を試してみましたが問題なく接続できました。USBHost機能を持ったAndroid端末も接続できるようです。
さて、実際に使用するときにはプログラムからシリアル通信でコマンドコードを送ることになります。
コマンドコードと受信データはバイトデータなので、それらに対応した開発言語ならどれを使っても開発できます。
サンプルはWindowsデスクトップアプリでした。ハッカソン参加者で使っていた言語はC#、python、JAVA(Android)といったところでしょうか。
センシングの性能としてはボードから1.5~2mくらいまでが解析できる距離(顔認識系)のようです。これはカメラモジュールの性能にもよる可能性があるので、モジュールを交換できるならもう少し距離が延びるかもしれません。
一人分の顔認識にかかる時間は約1秒未満くらいでしょうか、瞬時とはいかないようです。
ボードからは認識時に使った画像データも取得できます。サンプルプログラムではリアルタイムで画像表示して解析データを出力できていたので、もう少し時間があれば色々試せたのかもしれません。
今回使う機会がなかったのは顔認証機能でしたが、マニュアルをちらっと読んでみただけなのですが、認証用の画像データ(もしかすると特徴点データ?)はボード上のフラッシュメモリーに登録するようになっているようです。
センシング機能をボード上ですべて行っているので非力な端末と接続しても、十分な処理速度を保てるのではないでしょうか。
いくつか希望を述べるとすると
1.10種類のアルゴリズムすべてではなくて、機能別バージョンがあってもいいかなと思います。
人体検出+顔検出、顔検出+顔認識(推定系)、全部ありバージョンなどなど・・・顔認証ありバージョンとなしバージョンでもいいかも。 用途として顔認証はなくてもいい場合も十分あると思います。
2.ぜひ企業向けだけではなく個人向けにも販売してほしいです。今回は幸運にも評価ボードを貸していただけたのでよかったのですが、結構興味を持っている個人もいると思います。開発キット7万円はちょっと高いかな・・・・少しでも安くなればいいのですが。
以上簡単ですが、HVCを使ってみた感想でした。
2014年4月16日水曜日
TechCrunchHackathon Osakaに参加してきました。
先週末の土曜日から日曜日にかけて大阪のグランフロントにある大阪イノベーションハブで行われたTechCrunch Hackathon Osakaに参加してきました。
ハードウェア系のハッカソンということで2日間で提供されるハードウェアやWebAPIを利用して、IoTになるプロダクトを作っていきます。
今回結成したチーム(当初6人でしたが、1人途中で用事のため抜けたので最終的に5人)ではセンサーカメラ(オムロン HVC)と音楽API(Gracenote)で何かできないかなということで色々アイデアを考えていました。
最終的には「独居老人を見守ることができるミュージックプレイヤー」を作るということになりました。
全体の構成イメージは下図のようになります。
動作内容としては
1.HVCカメラで対象者を認識し、顔の表情や年齢のパラメーターをサーバーに送信。
2.サーバーで受信したパラメーターを元に年代、ムードなどのクエリを生成してGracenote APIからプレイリスト、ジャケット画像、音源URL等のデータを取得。
3.サーバー上でGracenoteAPIから取得したデータを基にプレイヤー用HTMLを生成して、プレイヤーへ送信。
4.プレイヤーは受信したHTMLを表示。
5.動作中は特定のトリガー(今回はあらかじめ決めていたモーション)で緊急事態を認識してモバイルサービスにアラートを送信。
6.モバイルサービスから緊急受信用のアプリへアラートを送信。
このような構成になりました。
HVCとAndroidタブレットの接続(UART)は当初PocketDuinoを使う必要があるかなと思っていたのですが、HVCに付属の変換基板でそのまま接続することができました。USBホスト機能のある機種はコネクターを合わせるとそのまま接続できます。
ただし、Androidの場合はそのままではシリアル通信ができないのでライブラリとしてPocketDuinoと一緒に公開されているPhysicaloid Libraryを使っています。
HVCを使ったチームのうちAndroidと接続していたのは我々のチームだけのようでした。
次にセンサーから得られた情報を如何にしてGracenoteAPIのクエリにするのかを検討しました。最終的には15種類(年代3種類、感情5種類)のパターンで検索しています。
APIから得られたプレイリストを基にジャケット画像と音源URL(今回はyoutube)を検索してからプレイヤー向けのHTMLを生成してプレイヤーに返すようにしています。
緊急時のアラートについてはプレイヤーが特定の条件を検知した場合にAzureモバイルサービスにアラートを送信してiPhoneアプリで通知を受け取り表示およびアラート音がなるようにしました。
デモのときにアラートがならなかったのが残念です。(多分ネットワークが重く通知が届かなかったのが原因だと思います。)
今回なんとか2日間で動作するプロダクトを作れたのもチームが偶然にも、それぞれ得意分野が違っていて上手くできたのだと思います。一部Android担当された方にタスクが集まってしまった感じではありましけれども。
今回作ったプロダクトをこのままで終わるのはもったいないと思うので何らかの形で続けられたらと、チームメンバーとも話をしています。
後ほど他のチームのプロダクトについても感想を書いてみたいと思います。
ハードウェア系のハッカソンということで2日間で提供されるハードウェアやWebAPIを利用して、IoTになるプロダクトを作っていきます。
今回結成したチーム(当初6人でしたが、1人途中で用事のため抜けたので最終的に5人)ではセンサーカメラ(オムロン HVC)と音楽API(Gracenote)で何かできないかなということで色々アイデアを考えていました。
最終的には「独居老人を見守ることができるミュージックプレイヤー」を作るということになりました。
全体の構成イメージは下図のようになります。
動作内容としては
1.HVCカメラで対象者を認識し、顔の表情や年齢のパラメーターをサーバーに送信。
2.サーバーで受信したパラメーターを元に年代、ムードなどのクエリを生成してGracenote APIからプレイリスト、ジャケット画像、音源URL等のデータを取得。
3.サーバー上でGracenoteAPIから取得したデータを基にプレイヤー用HTMLを生成して、プレイヤーへ送信。
4.プレイヤーは受信したHTMLを表示。
5.動作中は特定のトリガー(今回はあらかじめ決めていたモーション)で緊急事態を認識してモバイルサービスにアラートを送信。
6.モバイルサービスから緊急受信用のアプリへアラートを送信。
このような構成になりました。
HVCとAndroidタブレットの接続(UART)は当初PocketDuinoを使う必要があるかなと思っていたのですが、HVCに付属の変換基板でそのまま接続することができました。USBホスト機能のある機種はコネクターを合わせるとそのまま接続できます。
ただし、Androidの場合はそのままではシリアル通信ができないのでライブラリとしてPocketDuinoと一緒に公開されているPhysicaloid Libraryを使っています。
HVCを使ったチームのうちAndroidと接続していたのは我々のチームだけのようでした。
次にセンサーから得られた情報を如何にしてGracenoteAPIのクエリにするのかを検討しました。最終的には15種類(年代3種類、感情5種類)のパターンで検索しています。
APIから得られたプレイリストを基にジャケット画像と音源URL(今回はyoutube)を検索してからプレイヤー向けのHTMLを生成してプレイヤーに返すようにしています。
緊急時のアラートについてはプレイヤーが特定の条件を検知した場合にAzureモバイルサービスにアラートを送信してiPhoneアプリで通知を受け取り表示およびアラート音がなるようにしました。
デモのときにアラートがならなかったのが残念です。(多分ネットワークが重く通知が届かなかったのが原因だと思います。)
今回なんとか2日間で動作するプロダクトを作れたのもチームが偶然にも、それぞれ得意分野が違っていて上手くできたのだと思います。一部Android担当された方にタスクが集まってしまった感じではありましけれども。
今回作ったプロダクトをこのままで終わるのはもったいないと思うので何らかの形で続けられたらと、チームメンバーとも話をしています。
後ほど他のチームのプロダクトについても感想を書いてみたいと思います。
2014年2月5日水曜日
SQLServerで地図データ(緯度経度)を検索
先日FaceBookでDB上の地図座標を検索したいという話題がでていたので、SQLServerでやり方を調べてみました。
実現したいのは「現在地の座標から500m(任意の範囲)内の登録地点を検索する」となります。
SQLServer2008から空間データ用にgeometry型とgeography型がサポートされるようになりました。
geometry型は平面上の座標データ(一般的なxyグラフ)を扱います。
geography型は楕円体上の座標データを扱うので緯度経度データはこちらを利用することになると思います。
まずはサンプルテーブルを作成します。
次にサンプルデータを挿入します。今回は弊社近辺の銀行の所在地にしてみました。
geography型へのデータの挿入は「geography::STGeomFromText('POINT([経度] [緯度])',4326)」となります。(「経度」と「緯度」の間は半角スペースです。間違えるとエラーになります。)
------------------------------------------
追記
------------------------------------------
geography型へのデータの挿入は「geography::POINT([緯度],[経度],SRID)」でも可能です。
こちらのほうが直接的でわかりやすいかと思います。
------------------------------------------
追記終わり
------------------------------------------
SQLServerでのgeography型データは.NET 共通言語ランタイム (CLR) のデータ型として実装されているのでgeography::STGeomFromTextメソッドで座標を変換しています。「4326」はSRID (spatial reference ID) になります。(詳しく調べていないのですが固定値として指定しています。)
これで検索する準備ができました。
登録されているデータで任意の位置から特定範囲(この場合は円内)に含まれる地点を検索するには任意の位置座標とデータの位置座標の距離を求めることによって検索することができます。
距離(単位はメートルです)はgeography型のSTDistanceメソッドで求めることができます。
次のようにクエリを発行すると任意の座標から各データの位置データまでの距離が計算されます。
上記のクエリに以下の条件を加えると任意の範囲に含まれるデータを検索できます。
以上の内容をストアドもしくはユーザー定義関数としてSQLServerに登録して呼び出せるようにしておけば任意の位置データと検索したい範囲の距離を与えて検索することができるようになります。
実現したいのは「現在地の座標から500m(任意の範囲)内の登録地点を検索する」となります。
SQLServer2008から空間データ用にgeometry型とgeography型がサポートされるようになりました。
geometry型は平面上の座標データ(一般的なxyグラフ)を扱います。
geography型は楕円体上の座標データを扱うので緯度経度データはこちらを利用することになると思います。
まずはサンプルテーブルを作成します。
CREATE TABLE T_TEST ( id int IDENTITY(1,1) NOT NULL, name nvarchar(50) , address nvarchar(50) , geo geography )
次にサンプルデータを挿入します。今回は弊社近辺の銀行の所在地にしてみました。
INSERT INTO T_TEST(name,address,geo) VALUES('りそな銀行明石支店', '明石市本町1丁目2-26', geography::STGeomFromText('POINT(134.991151 34.647505)',4326)) INSERT INTO T_TEST(name,address,geo) VALUES('みずほ銀行明石支店', '明石市大明石町1丁目5-1', geography::STGeomFromText('POINT(134.993259 34.64798)',4326)) INSERT INTO T_TEST(name,address,geo) VALUES('三井住友銀行明石支店', '明石市大明石町1丁目5-4', geography::STGeomFromText('POINT(134.993299 34.647755)',4326)) INSERT INTO T_TEST(name,address,geo) VALUES('東京三菱UFJ銀行明石支店', '明石市本町1丁目1-34', geography::STGeomFromText('POINT(134.993251 34.647223)',4326)) INSERT INTO T_TEST(name,address,geo) VALUES('百十四銀行明石支店', '明石市本町2丁目1-26', geography::STGeomFromText('POINT(134.989622 34.647607)',4326))
geography型へのデータの挿入は「geography::STGeomFromText('POINT([経度] [緯度])',4326)」となります。(「経度」と「緯度」の間は半角スペースです。間違えるとエラーになります。)
------------------------------------------
追記
------------------------------------------
geography型へのデータの挿入は「geography::POINT([緯度],[経度],SRID)」でも可能です。
こちらのほうが直接的でわかりやすいかと思います。
------------------------------------------
追記終わり
------------------------------------------
SQLServerでのgeography型データは.NET 共通言語ランタイム (CLR) のデータ型として実装されているのでgeography::STGeomFromTextメソッドで座標を変換しています。「4326」はSRID (spatial reference ID) になります。(詳しく調べていないのですが固定値として指定しています。)
これで検索する準備ができました。
登録されているデータで任意の位置から特定範囲(この場合は円内)に含まれる地点を検索するには任意の位置座標とデータの位置座標の距離を求めることによって検索することができます。
距離(単位はメートルです)はgeography型のSTDistanceメソッドで求めることができます。
次のようにクエリを発行すると任意の座標から各データの位置データまでの距離が計算されます。
--任意の位置座標を代入する変数 declare @g geography --任意の位置座標データとしてJR明石駅の位置座標を使用しています。 set @g=geography::STGeomFromText('POINT(134.993164 34.649096)',4326) select name,address,@g.STDistance(geo) as distance from T_TEST
上記のクエリに以下の条件を加えると任意の範囲に含まれるデータを検索できます。
--200m以内にある地点を検索 where @g.STDistance(geo)<200
以上の内容をストアドもしくはユーザー定義関数としてSQLServerに登録して呼び出せるようにしておけば任意の位置データと検索したい範囲の距離を与えて検索することができるようになります。
登録:
投稿 (Atom)