帖子

[业务介绍] 【业务能力介绍系列】在线命令词识别

[复制链接]
  • TA的每日心情
    开心
    2017-12-29 09:56
  • 签到天数: 46 天

    [LV.5]常住居民I

    800117  楼主| 王水 发表于 2016-3-21 19:12:20 1#
    本帖最后由 王水 于 2016-3-21 19:26 编辑

    在之前的帖子中已经详细介绍了开放语义听写的使用和一些常见问题,今天介绍一下在线命令词识别。
    QQ截图20160****91855.jpg

    在线命令词识别又称在线语法识别或者在线识别。顾名思义,它的作用就是识别一组预先定于好的命令词,比如:北京到上海,合肥到南京,等等。
    到官网SDK下载中心(http://www.xfyun.cn/sdk/dispatcher)下载在线命令词识别SDK(这里以Android版本为例)后,可以发现压缩包里面的目录结构和听写的非常类似,这里就不过多介绍了,下面直接把demo导入到eclipse中看看。
    QQ截图20160****94716.jpg

    其中SpeechApp.java、AsrDemo.java里面就是在线命令词的代码了,grammar_sample.abnf是在线命令词需要使用到的在线语法文件。
    下面看一下demo运行的效果,今天demo后,点击“立即体验语法识别”就进入下面的界面。
    360手机助手截图0321_09_53_01.png

    可以看到上面的输入框中是读取的grammar_sample.abnf语法文件的内容,复选框选中“云端”表示当前使用的是在线命令词识别(“本地”是离线命令词识别)。
    下面分别是“构建语法”“开始识别”“停止录音”“取消按钮”,在线命令词识别是不支持“更新词典”的。
    在线命令词识别的过程主要就是先构建语法,然后启动录音开始识别。为了方便大家理解整个识别的过程,这里我详细介绍一下这里客户端和服务端都干了什么。
    • 首先是大家非常容易忘记的SpeechApp.java中的初始化:
    注意一步由于是单独放在一个java文件里面做的,所以很多开发者总是忘记这个初始化。但是这一步是必须的,如果没有做,后面的操作会报错的。
    1. // 应用程序入口处调用,避免手机内存过小,杀死后台进程后通过历史intent进入Activity造成SpeechUtility对象为null
    2. // 如在Application中调用初始化,需要在Mainifest中注册该Applicaiton
    3. // 注意:此接口在非主进程调用会返回null对象,如需在非主进程使用语音功能,请增加参数:SpeechConstant.FORCE_LOGIN+"=true"
    4. // 参数间使用半角“,”分隔。
    5. // 设置你申请的应用appid,请勿在'='与appid之间添加空格及空转义符
    6. // 注意: appid 必须和下载的SDK保持一致,否则会出现10407错误
    7. SpeechUtility.createUtility(SpeechApp.this, "appid=" + getString(R.string.app_id));
    8. // 以下语句用于设置日志开关(默认开启),设置成false时关闭语音云SDK日志打印
    复制代码
    • 然后是识别对象的创建、读取abnf语法文件:
    1. mAsr = SpeechRecognizer.createRecognizer(AsrDemo.this, mInitListener);
    2. mCloudGrammar = FucUtil.readFile(this,"grammar_sample.abnf","utf-8");
    复制代码

    • 然后开始构建语法:
    1. mContent = new String(mLocalGrammar);
    2. mAsr.setParameter(SpeechConstant.TEXT_ENCODING,"utf-8");
    3. //指定引擎类型
    4. mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
    5. ret = mAsr.buildGrammar(GRAMMAR_TYPE_BNF, mContent, mLocalGrammarListener);
    6. if(ret != ErrorCode.SUCCESS){
    7. if(ret == ErrorCode.ERROR_COMPONENT_NOT_INSTALLED){
    8. //未安装则跳转到提示安装页面
    9. mInstaller.install();
    10. }else {
    11. showTip("语法构建失败,错误码:" + ret);
    12. }
    13. }
    复制代码
    构建语法就是把abnf语法内容上传到服务器,服务器收到语法内容后,会返回一个grammarID(在mLocalGrammarListener监听器回调中返回),这个ID是你本次构建语法的唯一标示,多次构建相同的语法会覆盖之前的ID,在多个appid应用下或者多个不同的设备下构建相同的语法也会返回不同的ID。需要这一步需要注意的是,构建语法(buildGrammar)之前是需要设置TEXT_ENCODING和ENGINE_TYPE的。TEXT_ENCODING文本编码需要和语法文件的实际编码还有第一步中FucUtil.readFile(this,"grammar_sample.abnf","utf-8");的保持一致。ENGINE_TYPE是引擎类型,由于是在线命令词识别,所以这里设置成SpeechConstant.TYPE_CLOUD。
    1.         /**
    2.      * 云端构建语法监听器。
    3.      */
    4.         private GrammarListener mCloudGrammarListener = new GrammarListener() {
    5.                 @Override
    6.                 public void onBuildFinish(String grammarId, SpeechError error) {
    7.                         if(error == null){
    8.                                 String grammarID = new String(grammarId);
    9.                                 Editor editor = mSharedPreferences.edit();
    10.                                 if(!TextUtils.isEmpty(grammarId))
    11.                                         editor.putString(KEY_GRAMMAR_ABNF_ID, grammarID);
    12.                                 editor.commit();
    13.                                 showTip("语法构建成功:" + grammarId);
    14.                         }else{
    15.                                 showTip("语法构建失败,错误码:" + error.getErrorCode());
    16.                         }                        
    17.                 }
    18.         };
    复制代码
    • 构建语法成功后,就可以开始启动识别了:
    1. //设置识别引擎
    2. mAsr.setParameter(SpeechConstant.ENGINE_TYPE, mEngineType);
    3. //设置返回结果为json格式
    4. mAsr.setParameter(SpeechConstant.RESULT_TYPE, "json");
    5. //设置云端识别使用的语法id
    6. mAsr.setParameter(SpeechConstant.CLOUD_GRAMMAR, grammarId);
    7. mAsr.startListening(mRecognizerListener);
    复制代码

    注意这里一定要等构建语法回调返回成功后才能启动识别(不然你也拿不到grammarID不是?嘿嘿)。其实通俗的说,识别的过程就是把你说话的音频数据上传到服务器,服务器把你说的话与之前构建(上传)的语法进行匹配,来判断你说的可能是里面的哪个命令词,然后将判断结果(可能是多个或者零个)以json格式返回回来。

    • 然后就可以在mRecognizerListener监听器里获取返回的识别结果了。
    1.         /**
    2.      * 识别监听器。
    3.      */
    4.     private RecognizerListener mRecognizerListener = new RecognizerListener() {
    5.         
    6.         @Override
    7.         public void onVolumeChanged(int volume, byte[] data) {
    8.                 showTip("当前正在说话,音量大小:" + volume);
    9.                 Log.d(TAG, "返回音频数据:"+data.length);
    10.         }
    11.         
    12.         @Override
    13.         public void onResult(final RecognizerResult result, boolean isLast) {
    14.                 if (null != result) {
    15.                         Log.d(TAG, "recognizer result:" + result.getResultString());
    16.                         String text ;
    17.                         if("cloud".equalsIgnoreCase(mEngineType)){
    18.                                 text = JsonParser.parseGrammarResult(result.getResultString());
    19.                         }else {
    20.                                 text = JsonParser.parseLocalGrammarResult(result.getResultString());
    21.                         }
    22.                         
    23.                         // 显示
    24.                         ((EditText)findViewById(R.id.isr_text)).setText(text);               
    25.                 } else {
    26.                         Log.d(TAG, "recognizer result : null");
    27.                 }        
    28.         }
    29.         
    30.         @Override
    31.         public void onEndOfSpeech() {
    32.                 // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
    33.                 showTip("结束说话");
    34.         }
    35.         
    36.         @Override
    37.         public void onBeginOfSpeech() {
    38.                 // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
    39.                 showTip("开始说话");
    40.         }

    41.                 @Override
    42.                 public void onError(SpeechError error) {
    43.                         showTip("onError Code:"        + error.getErrorCode());
    44.                 }

    45.                 @Override
    46.                 public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
    47.                         // 以下代码用于获取与云端的会话id,当业务出错时将会话id提供给技术支持人员,可用于查询会话日志,定位出错原因
    48.                         // 若使用本地能力,会话id为null
    49.                         //        if (SpeechEvent.EVENT_SESSION_ID == eventType) {
    50.                         //                String sid = obj.getString(SpeechEvent.KEY_EVENT_SESSION_ID);
    51.                         //                Log.d(TAG, "session id =" + sid);
    52.                         //        }
    53.                 }

    54.     };
    复制代码
    json格式的结果在onResult回调中返回,通过demo中的json解析类解析后就可以得到下图中的结果了。
    360手机助手截图0321_18_59_01.png
    实际使用中一般选择置信度最高的结果。如果你说的内容不在你定义的语法范围内(比如北京北京),则会返回20005错误码。

    到这里一个完成的在线命令词识别过程就结束了,相对离线命令词识别来说要简单一些。







    评分

    参与人数 1语点 +10 收起 理由
    秋陌 + 10 很给力!

    查看全部评分

    荆小荆 发表于 2016-3-22 12:53:43
    2#
    楼主辛苦
    使用道具 举报 回复
    荆小荆 发表于 2016-3-22 12:55:14
    3#
    楼主辛苦
    使用道具 举报 回复
    荆小荆 发表于 2016-3-22 12:55:59
    4#
    楼主辛苦
    使用道具 举报 回复
    荆小荆 发表于 2016-3-22 12:57:19
    5#
    楼主辛苦
    使用道具 举报 回复
    1179856528@qq.c 发表于 2016-3-25 19:34:11
    6#
    楼主幸苦
    使用道具 举报 回复
    wangchao12138 发表于 2016-3-26 07:36:43
    7#
    没什么程例
    使用道具 举报 回复
     楼主| 王水 发表于 2016-3-28 09:49:12
    8#

    官网下载的SDK里面有demo的
    使用道具 举报 回复
    ZBT 发表于 2016-4-6 10:13:16
    9#
    置信度 如何获取??
    使用道具 举报 回复
     楼主| 王水 发表于 2016-4-6 16:24:48
    10#
    置信度是返回结果的sc字段  具体的解析见官网的demo
    使用道具 举报 回复