虽然您可以在应用程序中使用通用听写语言模型,但是很快您将遇到大量应用程序开发困难,它们是关于如何处理识别结果的。例如,以比萨饼定购系统为例。用户可能说“I'd like a pepperoni pizza”,结果将包含该字符串。但是它也可能包含“I'd like pepper on a plaza”,或者很多发音类似的语句,这取决于用户的发音差别或背景噪音情况。同样,用户可能说“Mary had a little lamb”,而结果将包含它,即使它对于比萨饼定购系统毫无意义。所有这些错误的结果对于应用程序而言毫无用处。因此,应用程序应该始终提供专门描述应用程序所要听到内容的语法。
在图 8 中,
using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using System.Speech.Recognition;
namespace Reco_Sample_1
{
public partial class Form1 : Form
{
//create a recognizer
SpeechRecognizer _recognizer = new SpeechRecognizer();
public Form1() { InitializeComponent(); }
private void Form1_Load(object sender, EventArgs e)
{
//Create a pizza grammar
Choices pizzaChoices = new Choices();
pizzaChoices.AddPhrase("I'd like a cheese pizza");
pizzaChoices.AddPhrase("I'd like a pepperoni pizza");
pizzaChoices.AddPhrase("I'd like a large pepperoni pizza");
pizzaChoices.AddPhrase(
"I'd like a small thin crust vegetarian pizza");
Grammar pizzaGrammar =
new Grammar(new GrammarBuilder(pizzaChoices));
//Attach an event handler
pizzaGrammar.SpeechRecognized +=
new EventHandler<RecognitionEventArgs>(
PizzaGrammar_SpeechRecognized);
_recognizer.LoadGrammar(pizzaGrammar);
}
void PizzaGrammar_SpeechRecognized(
object sender, RecognitionEventArgs e)
{
MessageBox.Show(e.Result.Text);
}
}
}
我使用了一个普通的 Windows 窗体应用程序,并添加了若干行代码以实现基本的语音识别。首先,我引入 System.Speech.Recognition 命名空间,然后实例化一个 SpeechRecognizer 对象。然后,我在 Form1_Load 中执行三个操作:生成一个语法,将一个事件处理程序附加到该语法,以便从该语法接收 SpeechRecognized 事件,然后将该语法加载到识别器。此时,识别器将开始听取符合该语法定义的模式的语音。当它识别出符合该语法的内容时,将调用该语法的 SpeechRecognized 事件处理程序。该事件处理程序本身访问 Result 对象并使用识别的文本。
System.Speech.Recognition API 支持 W3C 语音识别语法规范 (SRGS) — 位于 www.w3.org/TR/speech-grammar。该 API 甚至提供一组用于创建和使用 SRGS XML 文档的类。但多数情况下,使用 SRGS 有些过了,因此 API 也提供了 GrammarBuilder 类,它能够很好地满足比萨饼定购系统的需要。
GrammarBuilder 允许您从一组短语和选项中组成语法。在图 8中,我已经排除了不关注的问题(“Mary had a little lamb”),并对引擎进行了限制,以便它可以在模糊的声音之间进行更好的选择。当用户将“pizza”错误发音为“plaza”时,它甚至不会考虑单词“plaza”。因此通过这几行代码,我已经极大地增强了该系统的准确性。但是,该语法仍然存在一些问题。
详尽列出用户可能说的话语的方法很单调、易于出错且难于维护,而且实际上只能对很少的语法实现这一点。比较可取的做法是定义一个能够定义单词组合方式的语法。此外,如果应用程序关注比萨饼的尺寸、陷料和类型,开发人员还需大量的工作来分析结果字符串中的这些值。如果该识别系统可以在结果中识别这些语义属性,就会方便得多。使用 System.Speech.Recognition 和 Windows Vista 识别引擎会使该操作非常容易。
图 9 显示在用户从备选列表中说出一些内容时,如何使用 Choices 类组成语法。在该代码中,每个 Choices 实例的内容在构造函数中作为一个字符串参数序列指定。但是您有其他很多用于填充 Choices 的选项:您可以迭代添加新短语,从一个数组构造 Choices,将 Choices 添加到 Choices 以生成用户能够理解的复杂组合规则,或者将 GrammarBuilder 实例添加到 Choices 以生成更为灵活的语法(如本示例的 Permutations 部分中演示的那样)。
private void Form1_Load(object sender, EventArgs e)
{
//[I'd like] a [<size>] [<crust>] [<topping>] pizza [please]
//build the core set of choices
Choices sizes = new Choices("small", "regular", "large");
Choices crusts = new Choices("thin crust", "thick crust");
Choices toppings = new Choices("vegetarian", "pepperoni", "cheese");
//build the permutations of choices...
//choose all three
GrammarBuilder sizeCrustTopping = new GrammarBuilder();
sizeCrustTopping.AppendChoices(sizes, "size");
sizeCrustTopping.AppendChoices(crusts, "crust");
sizeCrustTopping.AppendChoices(toppings, "topping");
//choose size and topping, and assume thick crust
GrammarBuilder sizeAndTopping = new GrammarBuilder();
sizeAndTopping.AppendChoices(sizes, "size");
sizeAndTopping.AppendChoices(toppings, "topping");
sizeAndTopping.AppendResultKeyValue("crust", "thick crust");
//choose topping only, and assume the rest
GrammarBuilder toppingOnly = new GrammarBuilder();
toppingOnly.AppendChoices(toppings, "topping");
toppingOnly.AppendResultKeyValue("size", "regular");
toppingOnly.AppendResultKeyValue("crust", "thick crust");
//assemble the permutations
Choices permutations = new Choices();
permutations.AddGrammarBuilders(sizeCrustTopping);
permutations.AddGrammarBuilders(sizeAndTopping);
permutations.AddGrammarBuilders(toppingOnly);
//now build the complete pattern...
GrammarBuilder pizzaRequest = new GrammarBuilder();
//pre-amble "[I'd like] a"
pizzaRequest.AppendChoices(new Choices("I'd like a", "a"));
//permutations "[<size>] [<crust>] [<topping>]"
pizzaRequest.AppendChoices(permutations);
//post-amble "pizza [please]"
pizzaRequest.AppendChoices(new Choices("pizza", "pizza please"));
//create the pizza grammar
Grammar pizzaGrammar = new Grammar(pizzaRequest);
//attach the event handler
pizzaGrammar.SpeechRecognized +=
new EventHandler<RecognitionEventArgs>(
PizzaGrammar_SpeechRecognized);
//load the grammar into the recognizer
_recognizer.LoadGrammar(pizzaGrammar);
}
void PizzaGrammar_SpeechRecognized(object sender, RecognitionEventArgs e)
{
StringBuilder resultString = new StringBuilder();
resultString.Append("Raw text result: ");
resultString.AppendLine(e.Result.Text);
resultString.Append("Size: ");
resultString.AppendLine(e.Result.Semantics["size"].Value.ToString());
resultString.Append("Crust: ");
resultString.AppendLine(e.Result.Semantics["crust"].Value.ToString());
resultString.Append("Topping: ");
resultString.AppendLine(
e.Result.Semantics["topping"].Value.ToString());
MessageBox.Show(resultString.ToString());
}
图 9还显示如何使用语义值标记结果。当使用 GrammarBuilder 时,您可以将 Choices 附加到该语法,并将一个语义值附加到该选项,如以下语句示例所示:
AppendChoices(toppings, "topping");
有时一个特定的话语将具有从未公开的隐含语义值。例如,如果用户不指定比萨饼的尺寸,该语法可以将尺寸指定为“常规”,如以下语句所示:
AppendResultKeyValue("size", "regular");
从结果中获取语义值是通过访问 RecognitionEventArgs.Result.Semantics[] 来完成的。