F# + KINECT SDK Beta2で音声認識 #kinectsdk_ac

この記事はKINECT SDK Advent Calendar 2011 : ATNDの12月16日分です。


今回はKINECT SDKに同梱されているサンプルプログラム「Speech」と同等のものをF#で書いてみました。
このサンプルはred、blue、greenの3単語を判別するコードで、KINECT SDKでの音声認識プログラムとしては一番わかりやすいと思います。

環境

中身

今回はコンソールアプリケーションです。

open System
open System.IO
open System.Linq
open Microsoft.Research.Kinect.Audio
open Microsoft.Speech.AudioFormat
open Microsoft.Speech.Recognition

let getKinectRecognizer () =
  let matchingFunc (r:RecognizerInfo) =
    let mutable value = ""
    r.AdditionalInfo.TryGetValue("Kinect", &value) |> ignore
    "True".Equals(value, StringComparison.InvariantCultureIgnoreCase) && "en".Equals(r.Culture.Name, StringComparison.InvariantCultureIgnoreCase)
  SpeechRecognitionEngine.InstalledRecognizers().Where(matchingFunc).FirstOrDefault()

let dumpRecordedAudio (audio:RecognizedAudio) =
  let rec findNextFileNumber num =
    let filename = "RetainedAudio_" + (string num) + ".wav"
    if filename |> (File.Exists >> not) then filename
    else findNextFileNumber (num+1)

  if audio = null then ()

  let filename = findNextFileNumber 0
  Console.WriteLine("\nWriting file: {0}", filename)
  use file = new FileStream(filename, System.IO.FileMode.CreateNew)
  audio.WriteToWaveStream(file)

let sreSpeechRecognized (sre:SpeechRecognitionEngine) =
  sre.SpeechRecognized
  |> Observable.subscribe (fun e -> Console.WriteLine("\nSpeech Recognized: \t{0}", e.Result.Text) )
  |> ignore

let sreSpeechHypothesized (sre:SpeechRecognitionEngine) =
  sre.SpeechHypothesized
  |> Observable.subscribe (fun e -> Console.Write("\rSpeech Hypothesized: \t{0}", e.Result.Text) )
  |> ignore

let sreSpeechRecognitionRejected (sre:SpeechRecognitionEngine) =
  sre.SpeechRecognitionRejected
  |> Observable.subscribe begin
      fun e ->
        Console.WriteLine("\nSpeech Rejected")
        if e.Result <> null then dumpRecordedAudio(e.Result.Audio)
    end
  |> ignore

let main () =
  use source = new KinectAudioSource()
  source.FeatureMode <- true
  source.AutomaticGainControl <- false
  source.SystemMode <- SystemMode.OptibeamArrayOnly

  let ri = getKinectRecognizer()

  if ri = null then 
    Console.WriteLine("Could not find Kinect speech recognizer. Please refer to the sample requirements.")
  
  else
    Console.WriteLine("Using: {0}", ri.Name)

    use sre = new SpeechRecognitionEngine(ri.Id)             
    let colors = new Choices()
    [| "red";"green"; "blue" |] |> colors.Add

    let gb = new GrammarBuilder()
    gb.Culture <- ri.Culture
    colors |> gb.Append
                     
    let g = new Grammar(gb)                    

    sre.LoadGrammar(g)
    sre |> sreSpeechRecognized
    sre |> sreSpeechHypothesized
    sre |> sreSpeechRecognitionRejected
     
    use s = source.Start()
    sre.SetInputToAudioStream(s,
      new SpeechAudioFormatInfo(
        EncodingFormat.Pcm, 16000, 16, 1,
          32000, 2, null))

    Console.WriteLine("Recognizing. Say: 'red', 'green' or 'blue'. Press ENTER to stop")
     
    sre.RecognizeAsync(RecognizeMode.Multiple)
    Console.ReadLine() |> ignore
    Console.WriteLine("Stopping recognizer ...")
    sre.RecognizeAsyncStop()
  
[<EntryPoint>]
main ()
Kinectのオーディオ・ソースを利用

Kinectのオーディオ・ソースの準備とKinectAudioSource.SystemModeプロパティの設定、オートマティック・ゲイン・コントロールの無効化設定を行っています。

  use source = new KinectAudioSource()
  source.FeatureMode <- true
  source.AutomaticGainControl <- false
  source.SystemMode <- SystemMode.OptibeamArrayOnly

F#ではuseキーワードを使うことで、含まれているコードブロックの最後でリソースを自動開放してくれます。

認識エンジンの準備

まず、SpeechRecognitionEngineオブジェクトを生成します。

  let ri = getKinectRecognizer()

  if ri = null then 
    Console.WriteLine("Could not find Kinect speech recognizer. Please refer to the sample requirements.")
  
  Console.WriteLine("Using: {0}", ri.Name)

  use sre = new SpeechRecognitionEngine(ri.Id) 

次に、構文を構築しSpeechRecognitionEngineオブジェクトに読み込ませる作業です。

  use sre = new SpeechRecognitionEngine(ri.Id)             
  let colors = new Choices()
  [| "red";"green"; "blue" |] |> colors.Add

  let gb = new GrammarBuilder()
  gb.Culture <- ri.Culture
  colors |> gb.Append
                     
  let g = new Grammar(gb)                    

  sre.LoadGrammar(g)
音声認識機能の開始、停止

ストリームを開き、認識エンジンに渡してから認識機能を開始させます。

  use s = source.Start()
  sre.SetInputToAudioStream(s,
    new SpeechAudioFormatInfo(
      EncodingFormat.Pcm, 16000, 16, 1,
        32000, 2, null))

  Console.WriteLine("Recognizing. Say: 'red', 'green' or 'blue'. Press ENTER to stop")

  sre.RecognizeAsync(RecognizeMode.Multiple)
  Console.ReadLine() |> ignore
  Console.WriteLine("Stopping recognizer ...")
  sre.RecognizeAsyncStop()

コンソールから一行入力が行われたら認識機能を停止させます。

音声認識通知

信頼性の高い 1 つ以上の句を検索したときはSpeechRecognized、音声の一部が一時的に認識されたときはSpeechHypothesized、信頼性の低い候補句しか返せなかったときはSpeechRecognitionRejected経由で通知が行われるようです。
今回はSpeechRecognitionRejectedのときにdumpRecordedAudioを呼び出して音声を記録するようにしています。

let dumpRecordedAudio (audio:RecognizedAudio) =
  let rec findNextFileNumber num =
    let filename = "RetainedAudio_" + (string num) + ".wav"
    if filename |> (File.Exists >> not) then filename
    else findNextFileNumber (num+1)

  if audio = null then ()

  let filename = findNextFileNumber 0
  Console.WriteLine("\nWriting file: {0}", filename)
  use file = new FileStream(filename, System.IO.FileMode.CreateNew)
  audio.WriteToWaveStream(file)



実行結果はSpeechサンプルプログラムと同じになります。
音声の認識も簡単に行えるのはすばらしいですね。