2014-10-13 21:56:14 u011747781 阅读数 4244
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5927 人正在学习 去看看 杨波

最近做项目,用到离线语音识别,整了好久,查了好多方法,终于完成,网上资料有点乱,而且大部分就是那几个人写的,一群人转!下面我总结一下,也为后来人行个方便。


关于环境配置我就不多说了,我就是按照这个教程一步步来的,绝对是可行的。http://gaebolg.blog.163.com/blog/static/19826906820136232810723/



下面重点讲解一下demo的运行和语音库的配置(注意:上面那个教程的运行demo最后写的一塌糊涂,可以从拷贝声学模型和语音库那里开始看我的!)


首先,demo运行需要以下文件:pocketsphinx/model/en_US lm文件夹( .dic文件, .lm文件),以及hmm文件夹。如果觉得麻烦可以下载我提供的文件。http://download.csdn.net/detail/u011747781/8034061


下面进入重头戏,就是如何设置属于自己的中文语音库:

1、编写自己的命令集:

新建一个txt文件,输入命令,如:


百度
谷歌

点击文件,另存为,注意将编码改为UTF-8格式!(正下方编码选项)保存为txt文件。

2、利用在线工具http://www.speech.cs.cmu.edu/tools/lmtool.html将写好的txt文件生成文件。下载tgz文件即可(里面有所有文件),复制tgz压缩包中的lm文件,因为只有这个有用(如果是英语的话,dic文件可以直接用,就不用我下面介绍的方法生成dic文件了!!)


3、新建一个txt文件。在我给的data文件夹里,有个叫做standard.dic的文件,那个是标准语音库,ctrl+F 查找你需要的发音,复制到你新建的txt文件中。

格式如下:

百度 b ai d u

完事后存为UTF-8格式,改名为 .dic文件。


4、这样属于你的语音库就建好了,只需覆盖我的data文件夹中相应的dic , lm文件 即可!


最后,奉上亲测可用的完整demo  http://download.csdn.net/detail/u011747781/8034195


2019-01-11 15:56:24 weixin_44328479 阅读数 6248
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5927 人正在学习 去看看 杨波

简介

本文记录百度离线识别与讯飞离线语音识别,针对的是应用本身级别的。

百度语音离线识别集成

  1. 首先下载开发架包:bdasr_V3_20180801_d6f298a.jar,这个是离在线融合的SDK ;

  2. 导入so库:可在下载的demo中找到,复制到自己工程中同名的路径下
    在这里插入图片描述

  3. 需要百度开放平台上去申请 API Key 和 Secret Key,这是认证权限的必备信息,只有认证通过才可以继续使用语音功能,如语音唤醒,在线识别,离线识别等等。首先需要百度开放平台上去申请 API Key 和 Secret Key,这是认证权限的必备信息,只有认证通过才可以继续使用语音功能,如语音唤醒,在线识别,离线识别等等。
    在这里插入图片描述
    而鉴权认证就是要成功获取到AccessToken(Access Token 是用户身份验证和授权的凭证),而这个认证过程需要自己在应用中编写,之后会提到。
    将获取到的AppID、API Key 和 Secret Key添加到项目的配置文件中去;

  4. AndroidManifest.xml 文件的配置:
    设置权限(如果是Android 6.0 以上还需要获取动态权限,之后会说到)

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

设置APP_ID, APP_KEY, APP_SECRET

    <meta-data android:name="com.baidu.speech.APP_ID"
        android:value="9788136" />
    <meta-data
        android:name="com.baidu.speech.API_KEY"
        android:value="0GjQNO5H4pGPf9HyA3AmZEbz" />
    <meta-data
        android:name="com.baidu.speech.SECRET_KEY"
        android:value="db981ef3ec647ba8a09b599ad7447a24" />
  1. 注意事项:在申请API Key时需要设置应用包名,并且保证官网申请时的与app/bulid.gradle里的applicationId一致,否则会导致离线命令词或是唤醒报“no licenece”的错误。

离线识别实现

1.可以先准备本地词库和唤醒词库

2.基于SDK的使用:

  • 初始化EventManager类
 EventManager  asr = EventManagerFactory.create(context, "asr");//语音识别器
 EventManager  wp = EventManagerFactory.create(context, "wp");//语音唤醒器
  • 定义输出事件类
public class RecogResultManager implements EventListener{
    ...........
    
     /**
     * 回调事件
     */
    @Override
    public void onEvent(String name, String params, byte[] data, int offset, int length) {
        String logMessage = "name:" + name + "; params:" + params;
        Log.d(TAG, logMessage);
        switch (name) {
            case SpeechConstant.CALLBACK_EVENT_ASR_READY:
                // 引擎准备就绪,可以开始说话              
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_BEGIN:
                // 检测到用户的已经开始说话
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_END:
                // 检测到用户的已经停止说话
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL:
                // 临时识别结果, 长语音模式需要从此消息中取出结果
                break;
              case SpeechConstant.CALLBACK_EVENT_ASR_FINISH:
                // 识别结束, 最终识别结果或可能的错误
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_LONG_SPEECH:
                // 长语音
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_VOLUME:
                //音量值 
                break;
            case SpeechConstant.CALLBACK_EVENT_ASR_AUDIO:
                if (data.length != length) {
                 //可能出错的地方:回调返回的数据有问题
                   Log.d(TAG, "internal error: asr.audio" +
                            " callback data length is not equal to length param");
                 .....
                }
                break;
            case SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS:
                //语音唤醒成功 
                break;
            case SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR:
                //语音唤醒失败
                break;
            .......

            default:
                break;
        }
    }
}    


  • 注册自己的输出事件类
asr.registerListener(eventListener);
wp.registerListener(eventListener);
  • 加载唤醒词库与离线词库
 /**
 - 加载唤醒词库
  */
    private void loadWakeup() {
        Map<String, Object> params = new HashMap<>();
        params.put(SpeechConstant.WP_WORDS_FILE, "assets://WakeUp.bin");
        //开始识别语音唤醒
        mWakeup.start(params);
    }
    
 /**
 - 加载离线词库
  */
    private void loadWakeup() {
       Map<String, Object> params = new LinkedHashMap<>();
       //设置此参数使用与语音唤醒后进入语音识别模式
        params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
        params.put(SpeechConstant.DECODER, 2);
        params.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, "assets://baidu_speech_grammar.bsg");
       //设置唤醒到识别的停顿时间
        if (backTrackInMs > 0) {
            params.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - backTrackInMs);
        }
        mRecognizer.cancel();
        //开始进行识别
        mRecognizer.start(params);
    }
     注:对于语音唤醒到识别有两种方案:
     方案1: backTrackInMs > 0,唤醒词说完后,直接接句子,中间没有停顿,
            开启回溯,连同唤醒词一起整句识别,推荐4个字 1500ms,backTrackInMs 最大 15000,即15s.
     方案2: backTrackInMs = 0,唤醒词说完后,中间有停顿,不开启回溯。唤醒词识别回调后,正常开启识别。
            官方demo里采用默认设置的是1500,本人demo中选择的是方案2,因为测试结果方案2的识别效果好一些,详见下面测试结果。
  • 查询权限,获取AccessToken
public void check() {
        appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
        if (appId == null || appId.isEmpty()) {
              errorMessage = "appId 为空";
              fixMessage = "填写appID";
          }
        if (appKey == null || appKey.isEmpty()) {
                errorMessage = "appKey 为空";
                fixMessage = "填写appID";
          }
        if (secretKey == null || secretKey.isEmpty()) {
                 errorMessage = "secretKey 为空";
                  fixMessage = "secretKey";
         }
        try {
                checkOnline();
        } catch (UnknownHostException e) {
                infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
        } catch (Exception e) {
                errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
                fixMessage = " 重新检测appId, appKey, appSecret是否正确";
         }
 }

/**
 *  获取并校验token
 */
  public void checkOnline() throws Exception {
            String urlpath = "http://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id="
                    + appKey + "&client_secret=" + secretKey;
            URL url = new URL(urlpath);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(1000);
            InputStream is = conn.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            StringBuilder result = new StringBuilder();
            String line = "";
            do {
                line = reader.readLine();
                if (line != null) {
                    result.append(line);
                }
            } while (line != null);
           String res = result.toString();
           if (!res.contains("audio_voice_assistant_get")) {
                errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
                fixMessage = "secretKey";
                return;
            }
            appendLogMessage("openapi return " + res);
            JSONObject jsonObject = new JSONObject(res);
            String error = jsonObject.optString("error");
            if (error != null && !error.isEmpty()) {
                errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
                return;
            }
           String token = jsonObject.getString("access_token");
           if (token == null || !token.endsWith("-" + appId)) {
                errorMessage = "appId 与 appkey及 appSecret 不一致。
                appId = " + appId + " ,token = " + token;
                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
            }
        }
    }
  • 加载动态权限(Android 6.0以上需要)
    在主Activity中添加:

 private void initPermission() {
        String[] permissions = {Manifest.permission.RECORD_AUDIO,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.INTERNET,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
        };
        ArrayList<String> toApplyList = new ArrayList<>();
       for (String perm : permissions) {
            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
                toApplyList.add(perm);
                // 进入到这里代表没有权限.
            }
        }
        String[] tmpList = new String[toApplyList.size()];
        if (!toApplyList.isEmpty()) {
            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
        }
    }
    
@Override
   public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        // 此处为android 6.0以上动态授权的回调,用户自行实现。
    }
  • 控制识别/唤醒
   /**
    * 语音唤醒开始
    */
    public void start(Map<String, Object> params) {
        String json = new JSONObject(params).toString();
        wp.send(SpeechConstant.WAKEUP_START, json, null, 0, 0);
    }
    
    /**
     * 停止语音唤醒
     */
    public void stop() {
        wp.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0);
    }

    /**
     * 开始识别
     */
    public void start(Map<String, Object> params) {
        String json = new JSONObject(params).toString();
        asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
    }
    
    /**
     * 提前结束录音等待识别结果。
     */
    public void stop() {
        if (!isInited) {
            throw new RuntimeException("release() was called");
        }
        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
    }
    
    /**
     * 取消本次识别,取消后将立即停止不会返回识别结果。
     * cancel 与stop的区别是 cancel在stop的基础上,完全停止整个识别流程,
     */
    public void cancel() {
        if (!isInited) {
            throw new RuntimeException("release() was called");
        }
        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
    }
  • 事件管理器退出,回收资源,不能一直占用着mic资源。
    /**
     * 释放语音唤醒资源
     */
    public void release() {
       if (wp== null) {
            return;
        }
        stop();
        wp.unregisterListener(eventListener);
        wp = null;
        isInited = false;
    }
   
    /**
     * 释放语音识别资源
     */
      public void release() {
        if (asr == null) {
            return;
        }
        cancel();
        asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0);
        asr.unregisterListener(eventListener);
        asr = null;
        isInited = false;
    }

添加动作识别:

之前考虑是用数据库匹配识别结果,来找到相应的组件,但是demo里做得着急,只是通过管理activity,以activity和name为媒介来找到相应的组件,从而执行相应的动作。

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        VrApplication.getInstance().addActivity(this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        VrApplication.getInstance().removeActivity(this);
    }
}
public class VrApplication extends Application {

    private static final VrApplication INSTANCE = new VrApplication();

    private  List<BaseActivity> mActivities;

    private VrApplication() {}

    public static VrApplication getInstance() {
        return VrApplication.INSTANCE;
    }

    public  void addActivity(BaseActivity activity){
        if(mActivities == null){
            mActivities = new ArrayList<>();
        }
        mActivities.add(activity);
    }
    
      public  void removeActivity(BaseActivity activity){
        mActivities.remove(activity);
    }

    public List<BaseActivity> getActivities() {
        return mActivities;
    }

    /**
     * 关闭应用后需要清空管理
     */
     public  void finishAll(){
        for(BaseActivity activity : mActivities){
            if(! activity.isFinishing()){
                activity.finish();
            }
        }
    }
}

所有的界面activity需要继承BaseActivity,初始化组件时保存起来,界面销毁时clear()。

 private void initView() {
        mViews = new HashMap<>();
        EditText etCode = (EditText) findViewById(R.id.tv_point_code);
        Button action = (Button) findViewById(R.id.btn_action);
        mViews.put(etCode.getHint().toString(), etCode);
        mViews.put(action.getText().toString(), action);
    }

回调监听处理结果,进行确认组件去执行相应的动作:

public class RecogResultManager implements EventListener, IStatus {
     ...............
     
    /**
     * 回调事件
     */
    @Override
    public void onEvent(String name, String params, byte[] data, int offset, int length) {
           switch (name) {
                ........
                case SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL:
                     // 临时识别结果, 长语音模式需要从此消息中取出结果
                     handlePartial(params, data, offset, length);
                     break;
                case SpeechConstant.CALLBACK_EVENT_ASR_FINISH:
                     // 识别结束, 最终识别结果或可能的错误
                     handleFinish(params);
                     break;
                ........
                case SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS:
                     //语音唤醒成功
                     handleWpSuccess(name, params);
                     break;
                case SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR:
                     handleWpErro(name, params);
                     break;
          }
    } 
    ........
}               

处理语音唤醒,语音唤醒后,要切换开始进行语音识别:

private void handleWpMsg() {
        Map<String, Object> mParams = new LinkedHashMap<>();
        mParams.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
        mParams.put(SpeechConstant.DECODER, 2);
        mParams.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, BSGPATH);
         if (mBackTrackInMs > 0) {
            mParams.put(SpeechConstant.AUDIO_MILLS, System.currentTimeMillis() - mBackTrackInMs);
        }
        mRecognizer.cancel();
        mRecognizer.start(mParams);
 }

处理语音识别结果:

private void analysData(RecogResult recogResult) {
        String results = recogResult.getBestResult();
        //从缓存中获取当前的Activity
        BaseActivity activityInstance = UiUtil.getActivityInstance(mContext);
        if (activityInstance == null) {
            return;
        }
        Map<String, View> views = activityInstance.getViews();
        for (Map.Entry<String, View> entry : views.entrySet()) {
            if (results.contains(entry.getKey())) {
                action(entry.getValue(), results);
            }
        }
  }

执行动作:

 private void action(View value, String results) {
        if (value instanceof Button) {
            value.performClick();
        } else if (value instanceof EditText) {
            ((EditText) value).setText(results);
        }
 }

注意事项

1.即使只进行离线操作,第一次也需要进行联网进行认证授权才可以进行接下来的语音识别。
2. 不用时要及时释放,不能一直占用mic资源,影响其他使用。
3. 环境嘈杂时,识别率会很低,需要依赖降噪算法。
4.本demo针对一个应用来说,通过语音识别命令,并执行操作,跳转界面,点击操作,选择操作等,有一定的局限性,因为离线时,只能识别词库中的词,所以EditText或是TextView填写值的时候,可能没有用户所说的那个值,目前只是依赖在线识别来控制这块,还有待进一步研究。

demo下载地址:

链接: https://download.csdn.net/download/weixin_44328479/10910474.
Alt

2013-02-04 19:59:50 LoveAnnuoa 阅读数 11990
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5927 人正在学习 去看看 杨波

前言:因为在学校要做语音相关的开发,并且应用环境多数是在无网络状态下,故要用到离线语音识别。经过一番查阅,发现目前在做离线语音识别的,尤其是android环境下的离线语音识别的人确实很少。(插一句如果想要实现离线语音合成的可以看一下中科院慧声语音工作室提供的开放平台)在查阅资料期间联系过许多android方面经验丰富的前辈、朋友,但都没有接触过这个。因为是0基础,所以基本碰到一个东西就是新的,需要一点一点的学,不过即使现在能够成功运行demo,但也只是大体上的了解,主要是应用,用到什么学什么,并不深入。

下面,我将根据自己搭建环境和运行中遇到的问题,说一下自己的问题和解决方法,最后给出相关下载链接,给大家节省些时间,希望能帮助到遇到类似问题的朋友,也希望能互相学习交流。

由于许多朋友需要源代码,我便将我用过的源代码及声学模型和词典放到了csdn资源中供大家下载地址如下所示:http://download.csdn.net/detail/loveannuoa/5386461  这个工程和下面说的第二个链接代码是一样的,只是声学模型和词典不同,这个工程的数据是我自己弄的。

另外,本篇文章主要说的再linux下搭建环境,windows下的环境我也搭建完成,如果有不明白的可以先看一下我的另一篇关于windows下搭建环境的文章:http://blog.csdn.net/loveannuoa/article/details/8689478

 

重要提示:有一点之前没有说明,在整个环境配置中涉及了两个demo,一个是官方提供的即http://sourceforge.net/projects/cmusphinx/files/?source=navbar

另一个则是http://zuoshu.iteye.com/blog/1463867其中的。后者是“zuoshu”自己修改后的。根据测试,能够达到中文高识别率的是后者,即使唤作简单的中文数据词典文件,用前者的程序也没法识别。

搭建环境篇

注意:我在网上查到的几个搭建环境的帖子中都是针对Linux系统下的,一开始我没有注意到这个问题,是在windows下用cygwin模拟linux下做的,结果都没有成功。所以建议大家如果对cygwin中各种包、命令、错误不是很熟悉的情况下还是用linux系统吧,我是用的Ubuntu12.04和win7双系统,VM太卡受不了。下面指出的搭建环境的方法虽然能将demo运行起来,但识别率和识别速度实在是太难以让人满意,所以在中文识别篇中有提高识别率的方法。

我是通过两篇文章搭建的环境,分别是,

(1)http://blog.csdn.net/sununs11/article/details/8034294

         这个是CSDN中原版的android下利用pocketsphinx实现离线语音识别环境搭建方法,在myException网站和csdn其他博客中也有转载。通过这篇文章能够顺利的将demo跑起来。

(2)http://ucla.jamesyxu.com/?p=118

         这个应该是最早的讲解android离线语音识别的,而且确实不愧是老外的东西,讲解的很细致,同时也有问题分析。但这个教程中得第四步,即在demo的swig文件夹中运行”make”指令的这一步我始终没有执行过去,老是出错(错误会在下边指出)。作者貌似挺牛逼的,对网友们提出的问题都很热心的解答(但不知为什么我给他发过邮件,却一直没有点我,难道有反华倾向,呵呵)。

主要问题和注意事项集中在:

(1)    在安装sphinxbase和pocketsphinx包的时候会提示缺少其他的包(例如bison、libtool等),这个比较简单在cygwin下直接运行setup安装相应的包,如果在linux系统下直接用“sudo apt-get install “包名””就可以安装缺少的包。

(2)   在执行在swig目下make这一步中,首先,swig是需要单独安装,cygwin安装方法和(1)中类似,ubuntu下可以通过软件库下载安装。其次,无论在cygwin还是在ubuntu下都怎么弄都不成功,在cygwin下会提示问题(1)中提到的两个包未找到,这种情况可以参考http://cgwxyz.blog.163.com/blog/static/262806020105307929424/。另外,解决这个问题后还会出现.c文件中函数未定义的错误,我感觉这是cygwin本身的不足,可能就跟两个包没安装好有关。

在Ubuntu下,错误提示是缺少jni.h,这个包在jdk/include中,但无论我用“make  -I “jni.h的路径””将.h包含进来还直接将.h文件拷贝过来都依旧提示该错误。我没能解决了,如果大家有感兴趣的可以试一下,弄好了希望能通知一下,互相学习一下。

(3)    在demo的jni目录下执行ndk编译的时候,一定先修改Android.mk文件中SPHINX_PATH路径,修改时要将连同”$”在内的原来的路径都删掉,改成自己安装两个包的父目录,对于cygwin下要以“/cygdrive/….”开头,同样linux系统下也要先以”/”开头。

(4)    在更改工程属性中的swig和ndk时,要注意对于swig,在refresh选项中时选择的”thefloder…..”,而对于ndk则选择的是”theproject…..”,不要当做一样的,一定仔细按照步骤进行。


中文识别篇

我个人的理解:语音识别除了demo工程外还需要中/英文声学模型和语言模型,也就是环境搭建最后提到的hmm和lm文件夹,这两个文件夹在pocketsphinx中有,但是将这两个文件夹放入设备中测试时,不论中文还是英文识别率都很低而且识别速度也很慢。

因此以下几篇文章给出了解决办法:

(1)http://zuoshu.iteye.com/blog/1463867

这篇文章可以实现英文的识别,但是提到的中文识别目前我本人未实现,原因在于本文所给出的在线生成语音模型和字典的网站无法解析中文,只能生成英文字典。但是这篇文章给出了一个很好的生成语言模型和字典文件的网址

http://www.speech.cs.cmu.edu/tools/lmtool-new.html

 

(2)http://www.cnblogs.com/yin52133/archive/2012/07/12/2588201.html#2525875

这个是在demo跑起来之后,为解决识别中文的办法。特点:速度快,识别率很高,但是范围小。而且对于.dic字典文件来说需要自己设计,这就需要有一个比较规范字典文件模板作比照,可以用pocketsphinx/model/lm/zh_CN中得字典文件,同时也可以用上述链接中提到的zh_broadcastnews_utf8.dic,下载地址是:

http://sourceforge.net/projects/cmusphinx/files/Acoustic%20and%20Language%20Models/

 

以下是在环境搭建和demo运行中可能会用到的一些资料的下载链接:

(1)Sphinxbase和pocketsphinx的下载链接:

http://sourceforge.net/projects/cmusphinx/files/?source=navbar

(2)demo的下载地址:

http://sourceforge.net/projects/cmusphinx/files/?source=navbar

(3)一些sphinx和pocketsphinx基础知识学习的链接,涉及到一些识别引擎、识别原理的分析,挺专业的我没太看懂,以后还会继续学习。

http://blog.csdn.net/zouxy09/article/details/7941585

 

总结:

现在知识刚刚能够运行起demo来,能够简单的识别一些中文,对于识别引擎,识别原理我了解的太少,也是由于现在弄离线语音识别的太少,大多是在线的而且识别率和速度都很理想,导致几乎除了sphinx以外没有其他的语音识别引擎。

非常高兴大家能看到这里,这是我写的第一篇CSDN博文,以后希望能坚持写下去,希望与大家交流技术方面的知识,互相学习,大家如果在搭配环境或者运行demo上有什么问题,可以给我发私信,我会尽力帮忙的,互相学习共同进步。

2016-11-23 13:46:32 u014418171 阅读数 1837
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5927 人正在学习 去看看 杨波

本文将使用PocketSphinx来实现Android平台的离线语音识别

优点: 离线,不用联网 识别较准(大家都说99%  我觉得只有80%)
缺点: 自定义语音命令麻烦  只能小范围识别 

网上看了很多例子 我跟你说 那都是坑 根本就是错误的教程…
不废话 开始教程:

1.先确定要识别的词组

我的需求是实现离线识别 以下词组

中文:  向前 向后 向左 向右
英文: FORWARD BACKWARD TURNLEFT TURNRIGHT

(很多这样的教程 好像基本没教怎么识别英文来着…)

2.生成并修改lm和dic文件

先生成中文的 新建一个txt文件 命名为 text.txt
并把 向前 向后 向左 向右这四个词写上去,然后保存..
如图

注意很多文章都说这样的格式 < s>向前< /s>
这样的格式 我只想跟你说 你被坑了… 这样做出来的语音识别demo是没有任何识别反应的 真害人..

打开 http://www.speech.cs.cmu.edu/tools/lmtool-new.html 在线转换lm文件 , 点击选择文件选择刚才的text.txt文件

然后点击 COMPILE KNOWLEDGE BASE 提交
然后分别打开这两个文件 查看内容 并分别复制内容到新建的text.dic和text.lm文件里, 保存 (如果乱码 请使用谷歌浏览器 或 修改浏览器显示编码)

例如 我的 text.dic文件文本内容为:(我本来是写向前 向后 向左 向右 现在你看 顺序乱了 我不知道这样的目的 我建议保留转换后的顺序 可能与优先级有关吧)

向前
向右
向后
向左

而text.lm的内容为

Language model created by QuickLM on Tue Nov 22 03:42:55 EST 2016
Copyright (c) 1996-2010 Carnegie Mellon University and Alexander I. Rudnicky
The model is in standard ARPA format, designed by Doug Paul while he was at MITRE.
The code that was used to produce this language model is available in Open Source.
Please visit http://www.speech.cs.cmu.edu/tools/ for more information
The (fixed) discount mass is 0.5. The backoffs are computed using the ratio method.
This model based on a corpus of 4 sentences and 6 words
\data\
ngram 1=6
ngram 2=8
ngram 3=4

\1-grams:

-0.7782 -0.3010

-0.7782 -0.2218

-1.3802 向前 -0.2218

-1.3802 向右 -0.2218

-1.3802 向后 -0.2218

-1.3802 向左 -0.2218

\2-grams:

-0.9031 向前 0.0000

-0.9031 向右 0.0000

-0.9031 向后 0.0000

-0.9031 向左 0.0000

-0.3010 向前 -0.3010

-0.3010 向右 -0.3010

-0.3010 向后 -0.3010

-0.3010 向左 -0.3010

\3-grams:

-0.3010 向前

-0.3010 向右

-0.3010 向后

-0.3010 向左

\end\

还有的说有什么UTF-8编码问题 我只想说 我没遇到 我用的Sublime Text3文本编辑器 你如果有问题 你换我这个文本编辑器试试
完成上面的然后 脑残的一步来了 手动找字典(拼音)
哪里来的拼音字典? 先去这里下载一个压缩文件

https://sourceforge.net/projects/cmusphinx/files/pocketsphinx/0.7/ 

然后找到 pocketsphinx-0.7.tar.gz 点击下载(下不动用迅雷)

下载好后解压

pocketsphinx-0.7\pocketsphinx-0.7\model\hmm\zh\tdt_sc_8k //为中文语言模型文件
pocketsphinx-0.7\model\lm\zh_CN\xxxx.dic//为拼音读音字典
//同理
pocketsphinx-0.7\model\hmm\en_US\hub4wsj_sc_8k //为英文语言模型文件
pocketsphinx-0.7\model\lm\en_US\xxxx.dic //为英文读音字典

你还会发现有台湾的等等

我目录的中文语言文件字典为mandarin_notone.dic 打开 你会发现里面有很多文字对应读音(拼音)

这时 我们回到刚才的text.dic和text.lm文件 打开text.dic文件
一个词一个词 在mandarin_notone.dic字典中快捷键查找 (词找不到就单个字找) 然后复制拼音到相应的词语右边 注意拼音和词语要至少留一个空格 然后拼音和拼音之间也要留一个空格
编辑text.dic文件,我的找好了 如下:

向前  x iang q ian
向右  x iang y ou
向后  x iang h ou
向左  x iang z uo

好了 保存…

英文的也一样步骤 字典的话,要从英文字典上查 英文最好都大写 并且多个单词时最好不留空格

3.Demo修改

代码太多 略.. 但我文章下面直接放demo
网上其他教程很多都是打开demo就闪退 其实原因是
你还要手动复制模型和字典文件到sd卡上

我写的这个demo也是基于它, 但我解决了这个问题 我把文件放在 assets里 并且我把模型文件和字典都整合放到一起了 ,如下

这样 就能自动复制文件到临时路径 并让你们快速实现离线语音功能 程序员不需要担心其他操作.
实际情况下那demo遗留了一个 严重的bug 就是过100s左右会c库异常闪退 这个问题我也解决了
还有个 文字不断重复 和回调一直回调同一个识别答案这个问题也解决了…

我做了判断 , 文件都放在data/data/com.packagename.xxx/file/
并且根据手机语言切换识别中文还是英文…

public RecognizerTask(Context context) {

String dataPath = context.getFilesDir().getAbsolutePath();

File zhPath =newFile(dataPath +"/voice/zh");

if(!zhPath.exists()) {

zhPath.mkdirs();

}

File enPath =newFile(dataPath +"/voice/en");

if(!enPath.exists()) {

enPath.mkdirs();

}

pocketsphinx

.setLogfile(dataPath +"/voice/pocketsphinx.log");

String rootPath = isZh(context) ? zhPath.getPath() : enPath.getPath();//根据环境选择中英文识别

String dicPath = rootPath +"/text.dic";

String imPath = rootPath +"/text.lm";

if(!newFile(dicPath).exists()) {

releaseAssets(context,"/", dataPath);

}

Config c =newConfig();

c.setString("-hmm", rootPath);

c.setString("-dict", dicPath);

c.setString("-lm", imPath);

c.setFloat("-samprate",8000.0);

c.setInt("-maxhmmpf",2000);

c.setInt("-maxwpf",10);

c.setInt("-pl_window",2);

c.setBoolean("-backtrace",true);

c.setBoolean("-bestpath",false);

this.ps =newDecoder(c);

this.audio =null;

this.audioq =newLinkedBlockingQueue();

this.use_partials =false;

this.mailbox = Event.NONE;

}

代码很简单的 放Demo吧
—安卓离线语音识别 PocketSphinx Demo.zip—
http://download.csdn.net/detail/u014418171/9690129

安卓开发者交流群欢迎您加入
418263790

2018-10-15 20:37:57 TZYZlpx123456789 阅读数 3273
  • C++语音识别开篇

    本篇mark老师将教大家使用第三方库的调用来简单的实现语音识别。随着机器学习和人工智能的热闹,国内语音行业也可谓是百花齐放。 语音识别一个伟大的时代已在我们身边悄悄走来。

    5927 人正在学习 去看看 杨波

最近Boss让我开发一个离线语音听写功能 , 可是我发现科大讯飞的离线版本下架了 , 也看了很多地方如百度语音.......

      都没发现有离线版本 , 然而今天突然发现科大讯飞的离线听写又上架了  这就让我很开心 . 我就立马把它下载了下来,集成到我的项目中去 . 然后经过一个小时的努力 , 我把它简单的封装了一下 , 然后很方便的就可以到处调用了 !

      下面我就把我的那个类发上来你们可以直接拷过去用 . 具体有哪些不懂 , 里面传的东西不清楚可以在下方留言 , 我会给你们讲解.

    emmmm~很久没发博客了 , 现在给大家带来些福利 .

下面就是这个类:

 

public class LatYuYin {
    private static String TAG = "IatDemo";
    // 语音听写对象
    private SpeechRecognizer mIat;
    // 语音听写UI
    private RecognizerDialog mIatDialog;

    // 用HashMap存储听写结果
    private HashMap<String, String> mIatResults = new LinkedHashMap<String, String>();

    private Toast mToast;

    private Context mContext;
    private SharedPreferences mSharedPreferences;
    private boolean mTranslateEnable = false;
    private EditText mEditText;


    public LatYuYin(Context context) {
        mContext = context;
        // 初始化识别无UI识别对象
        // 使用SpeechRecognizer对象,可根据回调消息自定义界面;
        mIat = SpeechRecognizer.createRecognizer(mContext, mInitListener);
        // 初始化听写Dialog,如果只使用有UI听写功能,无需创建SpeechRecognizer
        // 使用UI听写功能,请根据sdk文件目录下的notice.txt,放置布局文件和图片资源
        mIatDialog = new RecognizerDialog(mContext, mInitListener);
        mToast = Toast.makeText(context, "", Toast.LENGTH_SHORT);
        mSharedPreferences = mContext.getSharedPreferences(IatSettings.PREFER_NAME, Activity.MODE_PRIVATE);
    }

    public void cancelmLat() {
        mIat.cancel();
        showTip("取消听写");
    }

    public void stopmLat() {
        mIat.stopListening();
        showTip("停止听写");
    }

    int ret = 0;// 函数调用返回值

    //开始听写
    public void startmLat(View view) {
        mEditText = (EditText) view.findViewById(R.id.activity_chat_et_text);
        mEditText.setText(null);
        mIatResults.clear();
        // 设置参数
        setParam();
        boolean isShowDialog = mSharedPreferences.getBoolean(mContext.getString(R.string.pref_key_iat_show), true);
        if (isShowDialog) {
            // 显示听写对话框
            mIatDialog.setListener(mRecognizerDialogListener);
            mIatDialog.show();
            showTip(mContext.getString(R.string.text_begin));
        } else {
            // 不显示听写对话框
            ret = mIat.startListening(mRecognizerListener);
            if (ret != ErrorCode.SUCCESS) {
                showTip("听写失败,错误码:" + ret);
            } else {
                showTip(mContext.getString(R.string.text_begin));
            }
        }

    }

    private void showTip(final String str) {
        Toast.makeText(mContext, str, Toast.LENGTH_SHORT).show();
    }

    /**
     * 参数设置
     *
     * @return
     */
    public void setParam() {
        // 清空参数
        mIat.setParameter(SpeechConstant.PARAMS, null);
        String lag = mSharedPreferences.getString("iat_language_preference", "mandarin");
        // 设置引擎
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
        // 设置返回结果格式
        mIat.setParameter(SpeechConstant.RESULT_TYPE, "json");

        this.mTranslateEnable = mSharedPreferences.getBoolean(mContext.getString(R.string.pref_key_translate), false);
        if (mTranslateEnable) {
            Log.i(TAG, "translate enable");
            mIat.setParameter(SpeechConstant.ASR_SCH, "1");
            mIat.setParameter(SpeechConstant.ADD_CAP, "translate");
            mIat.setParameter(SpeechConstant.TRS_SRC, "its");
        }

        if (lag.equals("en_us")) {
            // 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "en_us");
            mIat.setParameter(SpeechConstant.ACCENT, null);


            if (mTranslateEnable) {
                mIat.setParameter(SpeechConstant.ORI_LANG, "en");
                mIat.setParameter(SpeechConstant.TRANS_LANG, "cn");
            }
        } else {
            // 设置语言
            mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
            // 设置语言区域
            mIat.setParameter(SpeechConstant.ACCENT, lag);

            if (mTranslateEnable) {
                mIat.setParameter(SpeechConstant.ORI_LANG, "cn");
                mIat.setParameter(SpeechConstant.TRANS_LANG, "en");
            }
        }

        // 设置语音前端点:静音超时时间,即用户多长时间不说话则当做超时处理
        mIat.setParameter(SpeechConstant.VAD_BOS, mSharedPreferences.getString("iat_vadbos_preference", "4000"));

        // 设置语音后端点:后端点静音检测时间,即用户停止说话多长时间内即认为不再输入, 自动停止录音
        mIat.setParameter(SpeechConstant.VAD_EOS, mSharedPreferences.getString("iat_vadeos_preference", "1000"));

        // 设置标点符号,设置为"0"返回结果无标点,设置为"1"返回结果有标点
        mIat.setParameter(SpeechConstant.ASR_PTT, mSharedPreferences.getString("iat_punc_preference", "1"));

        // 设置音频保存路径,保存音频格式支持pcm、wav,设置路径为sd卡请注意WRITE_EXTERNAL_STORAGE权限
        mIat.setParameter(SpeechConstant.AUDIO_FORMAT, "wav");
        mIat.setParameter(SpeechConstant.ASR_AUDIO_PATH, Environment.getExternalStorageDirectory() + "/msc/iat.wav");
        //此处engineType为“local”
        mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
        if (SpeechConstant.TYPE_LOCAL.equals(SpeechConstant.TYPE_LOCAL)) {
            // 设置本地识别资源
            mIat.setParameter(ResourceUtil.ASR_RES_PATH, getResourcePath());
        }
    }

    private String getResourcePath() {
        StringBuffer tempBuffer = new StringBuffer();
        //识别通用资源
        tempBuffer.append(ResourceUtil.generateResourcePath(mContext, ResourceUtil.RESOURCE_TYPE.assets, "iat/common.jet"));
        tempBuffer.append(";");
        tempBuffer.append(ResourceUtil.generateResourcePath(mContext, ResourceUtil.RESOURCE_TYPE.assets, "iat/sms_16k.jet"));
        //识别8k资源-使用8k的时候请解开注释
        return tempBuffer.toString();
    }

    /**
     * 初始化监听器。
     */
    private InitListener mInitListener = new InitListener() {

        @Override
        public void onInit(int code) {
            Log.d(TAG, "SpeechRecognizer init() code = " + code);
            if (code != ErrorCode.SUCCESS) {
                showTip("初始化失败,错误码:" + code);
            }
        }
    };

    /**
     * 听写UI监听器
     */
    private RecognizerDialogListener mRecognizerDialogListener = new RecognizerDialogListener() {
        public void onResult(RecognizerResult results, boolean isLast) {
            Log.d(TAG, "recognizer result:" + results.getResultString());
            if (mTranslateEnable) {
                printTransResult(results);
            } else {
                String text = JsonParser.parseIatResult(results.getResultString());
                mEditText.append(text);
                mEditText.setSelection(mEditText.length());
            }
        }


        /**
         * 识别回调错误.
         */
        public void onError(SpeechError error) {
            if (mTranslateEnable && error.getErrorCode() == 14002) {
                showTip(error.getPlainDescription(true) + "\n请确认是否已开通翻译功能");
            } else {
                showTip(error.getPlainDescription(true));
            }
        }

    };
    /**
     * 听写监听器。
     */
    private RecognizerListener mRecognizerListener = new RecognizerListener() {

        @Override
        public void onBeginOfSpeech() {
            // 此回调表示:sdk内部录音机已经准备好了,用户可以开始语音输入
            showTip("开始说话");
        }

        @Override
        public void onError(SpeechError error) {
            // Tips:
            // 错误码:10118(您没有说话),可能是录音机权限被禁,需要提示用户打开应用的录音权限。
            if (mTranslateEnable && error.getErrorCode() == 14002) {
                showTip(error.getPlainDescription(true) + "\n请确认是否已开通翻译功能");
            } else {
                showTip(error.getPlainDescription(true));
            }
        }

        @Override
        public void onEndOfSpeech() {
            // 此回调表示:检测到了语音的尾端点,已经进入识别过程,不再接受语音输入
            showTip("结束说话");
        }

        @Override
        public void onResult(RecognizerResult results, boolean isLast) {
            if (mTranslateEnable) {
                printTransResult(results);
            } else {
                String text = JsonParser.parseIatResult(results.getResultString());
            }

            if (isLast) {
                //TODO 最后的结果
            }
        }

        @Override
        public void onVolumeChanged(int volume, byte[] data) {
            showTip("当前正在说话,音量大小:" + volume);
            Log.d(TAG, "返回音频数据:" + data.length);
        }

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

    private void printTransResult(RecognizerResult results) {
        String trans = JsonParser.parseTransResult(results.getResultString(), "dst");
        String oris = JsonParser.parseTransResult(results.getResultString(), "src");

        if (TextUtils.isEmpty(trans) || TextUtils.isEmpty(oris)) {
            showTip("解析结果失败,请确认是否已开通翻译功能。");
        }

    }
}

以上就是这个类的代码 , 基本你们拷过去就能用 .  同时你们附加了这两个类你们需要科大讯飞里面的开发包里面拷出来.

然后具体调用很简单如下:

以上就是调用的代码简单吧  然后你们不知道还要考哪些东西就把这几个文件都拷进去 , 或者看官方文档

好了  .........基本就是这些 , 如果还是没成功就在底下留言 , 我看到我会很快的回复你的!

          希望大家工作顺利!!!!!!!

语音离线识别算法

阅读数 473

没有更多推荐了,返回首页