讯飞语音 Great!!!
http://www.xfyun.cn/doccenter/awd
开发集成 > Android平台
目录[隐藏]
- 1 概述
- 2 预备工作
- 3 语音听写
- 4 语音识别
- 5 语音合成
- 6 语义理解
- 7 本地功能集成
- 8 语音评测
- 9 语音唤醒
- 10 声纹识别
- 11 人脸识别
- 12 附录
- 13 常见问题
1 概述
本文档是集成科大讯飞MSC(Mobile Speech Client,移动语音终端)Android版SDK的用户指南,介绍了语音听写、语音识别、语音合成、语义理解、语音评测等接口的使用。MSC SDK的主要功能接口如下图所示:
图1 MSC主要功能接口
为了更好地理解后续内容,这里先对文档中出现的若干专有名词进行解释说明:
表1 名词解释
名词 |
解释 |
语音合成 |
将一段文字转换为成语音,可根据需要合成出不同音色、语速和语调的声音,让机器像人一样开口说话。 |
语音听写 |
将一段语音转换成文字内容,能识别常见的词汇、语句、语气并自动断句。 |
语音识别(语法识别) |
判断所说的内容是否与预定义的语法相符合,主要用于判断用户是否下达某项命令。 |
语义理解 |
分析用户语音或文字的意图,给出相应的回答,如输入“今天合肥的天气”,云端即返回今天合肥的天气信息。 |
语音评测 |
通过智能语音技术对发音水平进行评价,给出得分。 |
声纹密码 |
一种基于每个人的声音都具有唯一性的认证机制,使用声音作为密码。 |
人脸识别 |
从照片、视频流中检测出人脸,或者识别两张人脸照片是否为同一个人。 |
2 预备工作
step1 导入SDK
将开发工具包中libs目录下的Msc.jar和armeabi复制到Android工程的libs目录(如果工程无libs目录,请自行创建)中,如下图所示:
图 2 导入SDK
step2 添加用户权限
在工程AndroidManifest.xml文件中添加如下权限:
- <!--连接网络权限,用于执行云端语音能力 -->
- <uses-permission android:name="android.permission.INTERNET"/>
- <!--获取手机录音机使用权限,听写、识别、语义理解需要用到此权限 -->
- <uses-permission android:name="android.permission.RECORD_AUDIO"/>
- <!--读取网络信息状态 -->
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
- <!--获取当前wifi状态 -->
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
- <!--允许程序改变网络连接状态 -->
- <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
- <!--读取手机信息权限 -->
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
- <!--读取联系人权限,上传联系人需要用到此权限 -->
- <uses-permission android:name="android.permission.READ_CONTACTS"/>
如需使用人脸识别,还要添加:
- <!--摄相头权限,拍照需要用到 -->
- <uses-permission android:name="android.permission.CAMERA" />
注:如需在打包或者生成APK的时候进行混淆,请在proguard.cfg中添加如下代码
- -keep class com.iflytek.**{*;}
step3 初始化
初始化即创建语音配置对象,只有初始化后才可以使用MSC的各项服务。建议将初始化放在程序入口处(如Application、Activity的onCreate方法),初始化代码如下:
- // 将“12345678”替换成您申请的APPID,申请地址:http://open.voicecloud.cn
- SpeechUtility.createUtility(context, SpeechConstant.APPID +"=12345678");
注意:此接口在非主进程调用会返回null对象,如需在非主进程使用语音功能,请使用参数:SpeechConstant.APPID +"=12345678," + SpeechConstant.FORCE_LOGIN +"=true"。
3 语音听写
听写主要指将连续语音快速识别为文字的过程,科大讯飞语音听写能识别通用常见的语句、词汇,而且不限制说法。语音听写的调用方法如下:
- //1.创建SpeechRecognizer对象,第二个参数:本地听写时传InitListener
- SpeechRecognizer mIat= SpeechRecognizer.createRecognizer(context, null);
- //2.设置听写参数,详见《科大讯飞MSC API手册(Android)》SpeechConstant类
- mIat.setParameter(SpeechConstant.DOMAIN, "iat");
- mIat.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
- mIat.setParameter(SpeechConstant.ACCENT, "mandarin ");
- //3.开始听写 mIat.startListening(mRecoListener);
- //听写监听器
- private RecognizerListener mRecoListener = new RecognizerListener(){
- //听写结果回调接口(返回Json格式结果,用户可参见附录12.1);
- //一般情况下会通过onResults接口多次返回结果,完整的识别内容是多次结果的累加;
- //关于解析Json的代码可参见MscDemo中JsonParser类;
- //isLast等于true时会话结束。
- public void onResult(RecognizerResult results, boolean isLast) {
- Log.d("Result:",results.getResultString ());}
- //会话发生错误回调接口
- public void onError(SpeechError error) {
- error.getPlainDescription(true) //获取错误码描述}
- //开始录音
- public void onBeginOfSpeech() {}
- //音量值0~30
- public void onVolumeChanged(int volume){}
- //结束录音
- public void onEndOfSpeech() {}
- //扩展用接口
- public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {}
- };
另外,您还可以使用SDK提供的语音交互动画来使语音输入界面变得更加炫酷,也可以通过上传联系人和用户词表增强听写效果。
3.1 语音交互动画
为了便于快速开发,SDK还提供了一套默认的语音交互动画以及调用接口,如需使用请将SDK资源包assets路径下的资源文件拷贝至Android工程asstes目录下,如图所示:
图 3 添加动画资源
然后通过以下代码使用交互动画:
- //1.创建SpeechRecognizer对象,第二个参数:本地听写时传InitListener
- RecognizerDialog iatDialog = new RecognizerDialog(this,mInitListener);
- //2.设置听写参数,同上节
- //3.设置回调接口
- iatDialog.setListener(recognizerDialogListener);
- //4.开始听写
- iatDialog.show();
3.2 上传联系人
上传联系人可以提高联系人名称识别率,也可以提高语义理解的效果,每个用户终端设备对应一个联系人列表,联系人格式详见《科大讯飞MSC API手册(Android)》ContactManager类。
- //获取ContactManager实例化对象
- ContactManager mgr = ContactManager.createManager(context, mContactListener);
- //异步查询联系人接口,通过onContactQueryFinish接口回调
- mgr.asyncQueryAllContactsName();
- //获取联系人监听器。
- private ContactListener mContactListener = new ContactListener() {
- @Override
- public void onContactQueryFinish(String contactInfos, boolean changeFlag) {
- //指定引擎类型
- mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
- mIat.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
- ret = mIat.updateLexicon("contact", contactInfos, lexiconListener);
- if(ret != ErrorCode.SUCCESS){
- Log.d(TAG,"上传联系人失败:" + ret);
- }
- }};
- //上传联系人监听器。
- private LexiconListener lexiconListener = new LexiconListener() {
- @Override
- public void onLexiconUpdated(String lexiconId, SpeechError error) {
- if(error != null){
- Log.d(TAG,error.toString());
- }else{
- Log.d(TAG,"上传成功!");
- }
- }
- };
3.3 上传用户词表
上传用户词表可以提高词表内词汇的识别率,也可以提高语义理解的效果,每个用户终端设备对应一个词表,用户词表的格式及构造方法详见《科大讯飞MSC API手册(Android)》UserWords类。
- //上传用户词表,userwords为用户词表文件。
- String contents = "您所定义的用户词表内容";
- mIat.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
- //指定引擎类型
- mIat.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
- ret = mIat.updateLexicon("userword", contents, lexiconListener);
- if(ret != ErrorCode.SUCCESS){
- Log.d(TAG,"上传用户词表失败:" + ret);
- }
- //上传用户词表监听器。
- private LexiconListener lexiconListener = new LexiconListener() {
- @Override
- public void onLexiconUpdated(String lexiconId, SpeechError error) {
- if(error != null){
- Log.d(TAG,error.toString());
- }else{
- Log.d(TAG,"上传成功!");
- }
- }
- };
4 语音识别
即语法识别,主要指基于命令词的识别,识别指定关键词组合的词汇,或者固定说法的短句。语法识别分云端识别和本地识别,云端和本地分别采用ABNF和BNF语法格式。
语法详解见:http://club.voicecloud.cn/forum.php?mod=viewthread&tid=7595
- //云端语法识别:如需本地识别请参照本地识别
- //1.创建SpeechRecognizer对象
- SpeechRecognizer mAsr = SpeechRecognizer.createRecognizer(context, null);
- // ABNF语法示例,可以说”北京到上海”
- String mCloudGrammar = "#ABNF 1.0 UTF-8;
- languagezh-CN;
- mode voice;
- root $main;
- $main = $place1 到$place2 ;
- $place1 = 北京 | 武汉 | 南京 | 天津 | 天京 | 东京;
- $place2 = 上海 | 合肥; ";
- //2.构建语法文件
- mAsr.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
- ret = mAsr.buildGrammar("abnf", mCloudGrammar , grammarListener);
- if (ret != ErrorCode.SUCCESS){
- Log.d(TAG,"语法构建失败,错误码:" + ret);
- }else{
- Log.d(TAG,"语法构建成功");
- }
- //3.开始识别,设置引擎类型为云端
- mAsr.setParameter(SpeechConstant.ENGINE_TYPE, "cloud");
- //设置grammarId
- mAsr.setParameter(SpeechConstant.CLOUD_GRAMMAR, grammarId);
- ret = mAsr.startListening(mRecognizerListener);
- if (ret != ErrorCode.SUCCESS) {
- Log.d(TAG,"识别失败,错误码: " + ret);
- }
- //构建语法监听器
- private GrammarListener grammarListener = new GrammarListener() {
- @Override
- public void onBuildFinish(String grammarId, SpeechError error) {
- if(error == null){
- if(!TextUtils.isEmpty(grammarId)){
- //构建语法成功,请保存grammarId用于识别
- }else{
- Log.d(TAG,"语法构建失败,错误码:" + error.getErrorCode());
- }
- }};
5 语音合成
与语音听写相反,合成是将文字信息转化为可听的声音信息,让机器像人一样开口说话。合成的调用方法如下:
- //1.创建SpeechSynthesizer对象, 第二个参数:本地合成时传InitListener
- SpeechSynthesizer mTts= SpeechSynthesizer.createSynthesizer(context, null);
- //2.合成参数设置,详见《科大讯飞MSC API手册(Android)》SpeechSynthesizer 类
- mTts.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");//设置发音人
- mTts.setParameter(SpeechConstant.SPEED, "50");//设置语速
- mTts.setParameter(SpeechConstant.VOLUME, "80");//设置音量,范围0~100
- mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD); //设置云端
- //设置合成音频保存位置(可自定义保存位置),保存在“./sdcard/iflytek.pcm”
- //保存在SD卡需要在AndroidManifest.xml添加写SD卡权限
- //如果不需要保存合成音频,注释该行代码
- mTts.setParameter(SpeechConstant.TTS_AUDIO_PATH, "./sdcard/iflytek.pcm");
- //3.开始合成
- mTts.startSpeaking("科大讯飞,让世界聆听我们的声音", mSynListener);
- //合成监听器
- private SynthesizerListener mSynListener = new SynthesizerListener(){
- //会话结束回调接口,没有错误时,error为null
- public void onCompleted(SpeechError error) {}
- //缓冲进度回调
- //percent为缓冲进度0~100,beginPos为缓冲音频在文本中开始位置,endPos表示缓冲音频在文本中结束位置,info为附加信息。
- public void onBufferProgress(int percent, int beginPos, int endPos, String info) {}
- //开始播放
- public void onSpeakBegin() {}
- //暂停播放
- public void onSpeakPaused() {}
- //播放进度回调
- //percent为播放进度0~100,beginPos为播放音频在文本中开始位置,endPos表示播放音频在文本中结束位置.
- public void onSpeakProgress(int percent, int beginPos, int endPos) {}
- //恢复播放回调接口
- public void onSpeakResumed() {}
- //会话事件回调接口
- public void onEvent(int arg0, int arg1, int arg2, Bundle arg3) {}
6 语义理解
6.1 语音语义理解
您可以通过后台配置出一套您专属的语义结果,详见http://osp.voicecloud.cn/
- //1.创建文本语义理解对象
- SpeechUnderstander understander = SpeechUnderstander.createUnderstander(context, null);
- //2.设置参数,语义场景配置请登录http://osp.voicecloud.cn/
- understander.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
- //3.开始语义理解
- understander.startUnderstanding(mUnderstanderListener);
- // XmlParser为结果解析类,见SpeechDemo
- private SpeechUnderstanderListener mUnderstanderListener = new SpeechUnderstanderListener(){
- public void onResult(UnderstanderResult result) {
- String text = result.getResultString();
- }
- public void onError(SpeechError error) {}//会话发生错误回调接口
- public void onBeginOfSpeech() {}//开始录音
- public void onVolumeChanged(int volume){} //音量值0~30
- public void onEndOfSpeech() {}//结束录音
- public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {}//扩展用接口
- };
6.2 文本语义理解
用户通过输入文本获取语义结果,得到的专属语义结果和上述语音方式相同。
- //创建文本语义理解对象
- TextUnderstander mTextUnderstander = TextUnderstander.createTextUnderstander(this, null);
- //开始语义理解
- mTextUnderstander.understandText("科大讯飞", searchListener);
- //初始化监听器
- TextUnderstanderListener searchListener = new TextUnderstanderListener(){
- //语义结果回调
- public void onResult(UnderstanderResult result){}
- //语义错误回调
- public void onError(SpeechError error) {}
- };
7 本地功能集成
本地识别、合成以及唤醒功能需要通过“讯飞语音+”来实现。“讯飞语音+”是基于讯飞语音云平台开发的应用,用户安装语音+后,应用可以通过服务绑定来使用讯飞语音+的本地功能,如下图所示:
在使用本地功能之前,先检查讯飞语音+的安装情况:
- //检查语音+是否安装
- //如未安装,获取语音+下载地址进行下载。安装完成后即可使用服务。
- if(!SpeechUtility.getUtility().checkServiceInstalled ()){
- String url = SpeechUtility.getUtility().getComponentUrl();
- Uri uri = Uri.parse(url);
- Intent it = new Intent(Intent.ACTION_VIEW, uri);
- context.startActivity(it);
- }
7.1 本地识别
- //1.创建SpeechRecognizer对象,需传入初始化监听器
- SpeechRecognizer mAsr = SpeechRecognizer.createRecognizer(context, mInitListener);
- //初始化监听器,只有在使用本地语音服务时需要监听(即安装讯飞语音+,通过语音+提供本地服务),初始化成功后才可进行本地操作。
- private InitListener mInitListener = new InitListener() {
- public void onInit(int code) {
- if (code == ErrorCode.SUCCESS) {}}
- };
- //2.构建语法(本地识别引擎目前仅支持BNF语法),同在线语法识别 请参照Demo。
- //3.开始识别,设置引擎类型为本地
- mAsr.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
- //设置本地识别使用语法id(此id在语法文件中定义)、门限值
- mAsr.setParameter(SpeechConstant.LOCAL_GRAMMAR, "call");
- mAsr.setParameter(SpeechConstant.MIXED_THRESHOLD, "30");
- ret = mAsr.startListening(mRecognizerListener);
7.2 本地合成
- //1.创建SpeechSynthesizer对象
- SpeechSynthesizer mTts= SpeechSynthesizer.createSynthesizer(context, mInitListener);
- //初始化监听器,同听写初始化监听器,使用云端的情况下不需要监听即可使用,本地需要监听
- private InitListener mInitListener = new InitListener() {...};
- //2.合成参数设置
- //设置引擎类型为本地
- mTts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_LOCAL);
- //可跳转到语音+发音人设置页面进行发音人下载
- SpeechUtility.getUtility().openEngineSettings(SpeechConstant.ENG_TTS);
- //3.开始合成
- mTts.startSpeaking("科大讯飞,让世界聆听我们的声音", mSynListener);
7.3 获取语音+参数
用户可以通过语音+中的资源下载(包括:识别资源、发音人资源)来提升语音+离线能力,开发者可以通过以下接口获取当前语音+包含的离线资源列表,此接口从语音+1.032(99)版本开始支持。(通过getServiceVersion()获取版本号) 注:后续版本将支持获取语音+当前设置的发音人字段
- //1.设置所需查询的资源类型
- /**
- *1.PLUS_LOCAL_ALL: 本地所有资源
- 2.PLUS_LOCAL_ASR: 本地识别资源
- 3.PLUS_LOCAL_TTS: 本地合成资源
- */
- String type = SpeechConstant.PLUS_LOCAL_ASR;
- //2.获取当前语音+包含资源列表
- String resource = SpeechUtility.getUtility().getParameter(type);
- //3.解析json-请参见下表格式及Demo中解析方法
- {"ret":0,"result":{"version":11,
- "tts":[{"sex":"woman","language":"zh_cn","accent":"mandarin","nickname":"邻家姐姐","age":"22","name":"xiaojing"},
- {"sex":"woman","language":"zh_cn","accent":"mandarin","nickname":"王老师","age":"24","name":"xiaoyan"}],
- "asr":[{"domain":"asr","samplerate":"16000","language":"zh_cn","accent":"mandarin","name":"common"}]}}
8 语音评测
提供汉语、英语两种语言的评测,支持单字(汉语专有)、词语和句子朗读三种题型,通过简单地接口调用就可以集成到您的应用中。语音评测的使用主要有三个步骤:
1)创建对象和设置参数
- // 创建评测对象
- SpeechEvaluator mSpeechEvaluator = SpeechEvaluator.createEvaluator(
- IseDemoActivity.this, null);
- // 设置评测语种
- mSpeechEvaluator.setParameter(SpeechConstant.LANGUAGE, "en_us");
- // 设置评测题型
- mSpeechEvaluator.setParameter(SpeechConstant.ISE_CATEGORY, "read_word");
- // 设置试题编码类型
- mSpeechEvaluator.setParameter(SpeechConstant.TEXT_ENCODING, "utf-8");
- // 设置前、后端点超时
- mSpeechEvaluator.setParameter(SpeechConstant.VAD_BOS, vad_bos);
- mSpeechEvaluator.setParameter(SpeechConstant.VAD_EOS, vad_eos);
- // 设置录音超时,设置成-1则无超时限制
- mSpeechEvaluator.setParameter(SpeechConstant.KEY_SPEECH_TIMEOUT, "-1");
- // 设置结果等级,不同等级对应不同的详细程度
- mSpeechEvaluator.setParameter(SpeechConstant.RESULT_LEVEL, "complete");
可通过setParameter设置的评测相关参数说明如下:
表2 评测相关参数说明
参数 |
说明 |
是否必需 |
language |
评测语种,可选值:en_us(英语)、zh_cn(汉语) |
是 |
category |
评测题型,可选值:read_syllable(单字,汉语专有)、read_word(词语)、read_sentence(句子) |
是 |
text_encoding |
上传的试题编码格式,可选值:gb2312、utf-8。当进行汉语评测时,必须设置成utf-8,建议所有试题都使用utf-8编码 |
是 |
vad_bos |
前端点超时,默认5000ms |
否 |
vad_eos |
后端点超时,默认1800ms |
否 |
speech_timeout |
录音超时,当录音达到时限将自动触发vad停止录音,默认-1(无超时) |
否 |
result_level |
评测结果等级,可选值:plain、complete,默认为complete |
否 |
2)上传评测试题和录音
- // 首先创建一个评测监听接口
- private EvaluatorListener mEvaluatorListener = new EvaluatorListener() {
- // 结果回调,评测过程中可能会多次调用该方法,isLast为true则为最后结果
- public void onResult(EvaluatorResult result, boolean isLast) {}
- // 出错回调
- public void onError(SpeechError error) {}
- // 开始说话回调
- public void onBeginOfSpeech() {}
- // 说话结束回调
- public void onEndOfSpeech() {}
- // 音量回调
- public void onVolumeChanged(int volume) {}
- // 扩展接口,暂时没有回调
- public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {}
- };
- // 然后设置评测试题、传入监听器,开始评测录音。evaText为试题内容,试题格式详见《语音
- // 评测参数、结果说明文档》,第二个参数为扩展参数,请设置为null
- mSpeechEvaluator.startEvaluating(evaText, null, mEvaluatorListener);
调用startEvaluating即开始评测录音,读完试题内容后可以调用stopEvaluating停止录音,也可以在一段时间后由SDK自动检测vad并停止录音。当评测出错时,SDK会回调onError方法抛出SpeechError错误,通过SpeechError的getErrorCode()方法可获得错误码,常见的错误码详见第十二章附录和下表:
表3 评测错误码
错误码 |
错误值 |
含义 |
MSP_ERROR_ASE_EXCEP_SILENCE |
11401 |
无语音或音量太小 |
MSP_ERROR_ASE_EXCEP_SNRATIO |
11402 |
信噪比低或有效语音过短 |
MSP_ERROR_ASE_EXCEP_PAPERDATA |
11403 |
非试卷数据 |
MSP_ERROR_ASE_EXCEP_PAPERCONTENTS |
11404 |
试卷内容有误 |
MSP_ERROR_ASE_EXCEP_NOTMONO |
11405 |
录音格式有误 |
MSP_ERROR_ASE_EXCEP_OTHERS |
11406 |
其他评测数据异常,包括错读、漏读、恶意录入、试卷内容等错误 |
MSP_ERROR_ASE_EXCEP_PAPERFMT |
11407 |
试卷格式有误 |
MSP_ERROR_ASE_EXCEP_ULISTWORD |
11408 |
存在未登录词,即引擎中没有该词语的信息 |
3)解析评测结果
SDK通过onResult回调抛出xml格式的评测结果,结果格式及字段含义详见《语音评测参数、结果说明文档》文档,具体的解析过程可参考demo工程com.iflytek.ise.result包中的源代码。
9 语音唤醒
语音唤醒的集成主要有三个步骤:
1)导入资源文件
使用唤醒功能需要将开发包中\res\ivw\路径下的唤醒资源文件引入,引入方式有三种:Assets、Resources、SD卡;资源文件以.jet为后缀(下图以Assets方式为例)。
图4 唤醒资源文件
2)初始化
创建用户语音配置对象后才可以使用语音服务,建议在程序入口处调用。
- // 将“12345678”替换成您申请的APPID,申请地址:http://open.voicecloud.cn
- SpeechUtility.createUtility(context, SpeechConstant.APPID +"=12345678");
3)代码添加
- //1.加载唤醒词资源,resPath为唤醒资源路径
- StringBuffer param =new StringBuffer();
- String resPath = ResourceUtil.generateResourcePath(WakeDemo.this, RESOURCE_TYPE.assets, "ivw/ivModel_zhimakaimen.jet");
- param.append(ResourceUtil.IVW_RES_PATH+"="+resPath);
- param.append(","+ResourceUtil.ENGINE_START+"="+SpeechConstant.ENG_IVW);
- SpeechUtility.getUtility().setParameter(ResourceUtil.ENGINE_START,param.toString());
- //2.创建VoiceWakeuper对象
- VoiceWakeuper mIvw = VoiceWakeuper.createWakeuper(context, null);
- //3.设置唤醒参数,详见《科大讯飞MSC API手册(Android)》SpeechConstant类
- //唤醒门限值,根据资源携带的唤醒词个数按照“id:门限;id:门限”的格式传入
- mIvw.setParameter(SpeechConstant.IVW_THRESHOLD,"0:"+curThresh);
- //设置当前业务类型为唤醒
- mIvw.setParameter(SpeechConstant.IVW_SST,"wakeup");
- //设置唤醒一直保持,直到调用stopListening,传入0则完成一次唤醒后,会话立即结束(默认0)
- mIvw.setParameter(SpeechConstant.KEEP_ALIVE,"1");
- //4.开始唤醒
- mIvw.startListening(mWakeuperListener);
- //听写监听器
- private WakeuperListener mWakeuperListener = new WakeuperListener() {
- public void onResult(WakeuperResult result) {
- try {
- String text = result.getResultString();
- } catch (JSONException e) {
- e.printStackTrace();
- }}
- public void onError(SpeechError error) {}
- public void onBeginOfSpeech() {}
- public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {
- if (SpeechEvent.EVENT_IVW_RESULT == eventType) {
- //当使用唤醒+识别功能时获取识别结果
- //arg1:是否最后一个结果,1:是,0:否。
- RecognizerResult reslut = ((RecognizerResult)obj.get(SpeechEvent.KEY_EVENT_IVW_RESULT));
- }
- }};
10 声纹识别
与指纹一样,声纹也是一种独一无二的生理特征,可以用来鉴别用户的身份。声纹密码的使用包括注册、验证和模型操作。
10.1 声纹注册
现阶段语音云平台支持三种类型的声纹密码,即文本密码、自由说和数字密码,在注册之前要选择声纹的类型。
- // 首先创建SpeakerVerifier对象
- mVerify = SpeakerVerifier.createVerifier(this, null);
- // 通过setParameter设置密码类型,pwdType的取值为1、2、3,分别表示文本密码、自由说和数字密码
- mVerify.setParameter(SpeechConstant.ISV_PWDT, "" + pwdType);
pwdType的取值说明如下表所示:
表 4 pwdType取值说明
取值 |
说明 |
1 |
文本密码。用户通过读出指定的文本内容来进行声纹注册和验证,现阶段支持的文本有“芝麻开门”。 |
2 |
自由说密码。用户通过读一段文字来进行声纹注册和验证,注册时要求声音长度为20秒左右,验证时要求声音长度为15秒左右,内容不限。 |
3 |
数字密码。从云端拉取若干组特定的数字串(默认有5组,每组8位数字),用户依次读出这5组数字进行注册,在验证过程中会生成一串特定的数字,用户通过读出这串数字进行验证。 |
除自由说外,其他两种密码需调用接口从云端获取:
- // 通过调用getPasswordList方法来获取密码。mPwdListener是一个回调接口,当获取到密码后,SDK会调用其中的onBufferReceived方法对云端返回的JSON格式(具体格式见附录12.4)的密码进行处理,处理方法详见声纹Demo示例
- mVerify.getPasswordList(SpeechListener mPwdListener);
- SpeechListener mPwdListenter = new SpeechListener() {
- public void onEvent(int eventType, Bundle params) {}
- public void onBufferReceived(byte[] buffer) {}
- public void onCompleted(SpeechError error) {}
- };
获取到密码后,接下来进行声纹注册,即要求用户朗读若干次指定的内容,这一过程也称为声纹模型的训练。
- // 设置业务类型为训练
- mVerify.setParameter(SpeechConstant.ISV_SST, "train");
- // 设置密码类型
- mVerify.setParameter(SpeechConstant.ISV_PWDT, "" + pwdType);
- // 对于文本密码和数字密码,必须设置密码的文本内容,pwdText的取值为“芝麻开门”或者是从云端拉取的数字密码(每8位用“-”隔开,如“62389704-45937680-32758406-29530846-58206497”)。自由说略过此步
- mVerify.setParameter(SpeechConstant.ISV_PWD, pwdText);
- // 对于自由说,必须设置采样频率为8000,并设置ISV_RGN为1。其他密码可略过此步
- mVerify.setParameter(SpeechConstant.SAMPLE_RATE, "8000");
- mVerify.setParameter(SpeechConstant.ISV_RGN, "1");
- // 设置声纹对应的auth_id,它用于标识声纹对应的用户,为空时表示这是一个匿名用户
- mVerify.setParameter(SpeechConstant.ISV_AUTHID, auth_id);
- // 开始注册,当得到注册结果时,SDK会将其封装成VerifierResult对象,回调VerifierListener对象listener的onResult方法进行处理,处理方法详见Demo示例
- mVerify.startListening(mRegisterListener);
- VerifierListener mRegisterListener =new VerifierListener() {
- public void onVolumeChanged(int volume) {}
- public void onResult(VerifierResult result) {
- public void onEvent(int eventType, int arg1, int arg2, Bundle obj) {}
- public void onError(SpeechError error) {}
- public void onEndOfSpeech() {}
- public void onBeginOfSpeech() {}
- };
注意,当auth_id为空时(匿名用户),将使用设备的设备ID来标识注册的声纹模型。由于设备ID不能跨设备,而且不同的设备所获取到的设备ID也有可能相同,推荐的作法是在注册模型的时为app的每个用户都指定一个唯一的auth_id。auth_id的格式为:6-18个字符,为字母、数字和下划线的组合且必须以字母开头,不支持中文字符,不能包含空格。
表 5 声纹错误码
错误码 |
错误值 |
说明 |
MSS_ERROR_IVP_GENERAL |
11600 |
正常,请继续传音频 |
MSS_ERROR_IVP_EXTRA_RGN_SOPPORT |
11601 |
rgn超过最大支持次数9 |
MSS_ERROR_IVP_TRUNCATED |
11602 |
音频波形幅度太大,超出系统范围,发生截幅 |
MSS_ERROR_IVP_MUCH_NOISE |
11603 |
太多噪音 |
MSS_ERROR_IVP_TOO_LOW |
11604 |
声音太小 |
MSS_ERROR_IVP_ZERO_AUDIO |
11605 |
没检测到音频 |
MSS_ERROR_IVP_UTTER_TOO_SHORT |
11606 |
音频太短 |
MSS_ERROR_IVP_TEXT_NOT_MATCH |
11607 |
音频内容与给定文本不一致 |
MSS_ERROR_IVP_NO_ENOUGH_AUDIO |
11608 |
音频长达不到自由说的要求 |
验证结果VerifierResult类中有一个vid字段,用于标识成功注册的声注模型。结果中包含的所有字段,以及各字段的含义见第十二章附录。
10.2 声纹验证
声纹验证过程与声纹注册类似,不同之处仅在于ISV_SST需要设置为”verify”,且不用设置ISV_RGN参数,其他参数的设置、验证结果的处理过程完全可参考上节。
10.3 模型操作
声纹注册成功后,在语音云端上会生成一个对应的模型来存储声纹信息,声纹模型的操作即对模型进行查询和删除。
- // 首先设置声纹密码类型
- mVerify.setParameter(SpeechConstant.ISV_PWDT, "" + pwdType);
- // 对于文本和数字密码,必须设置声纹注册时用的密码文本,pwdText的取值为“芝麻开门”或者是从云平台拉取的数字密码。自由说略过此步
- mVerify.setParameter(SpeechConstant.ISV_PWD, pwdText);
- // 特别地,自由说一定要设置采样频率为8000,其他密码则不需要
- mVerify.setParameter(SpeechConstant.SAMPLE_RATE, “8000”);
- // 设置待操作的声纹模型的vid
- mVerify.setParameter(SpeechConstant.ISV_VID, vid);
- // 调用sendRequest方法查询或者删除模型,cmd的取值为“que”或“del”,表示查询或者删除,auth_id是声纹对应的用户标识,操作结果以异步方式回调SpeechListener类型对象listener的onBufferReceived方法进行处理,处理方法详见Demo示例
- mVerify.sendRequest(cmd, auth_id, listener);
11 人脸识别
人脸识别不仅可以检测出照片中的人脸,还可以进行人脸注册和验证。相关概的说明如下:
表 6 人脸识别概念说明
名称 |
说明 |
reg/注册 |
上传包含一张人脸的图片到云端,引擎对其进行训练,生成一个与之对应的模型,返回模型id(gid)。 |
verify/验证 |
注册成功后,上传包含一张人脸的图片到云端,引擎将其与所注册的人脸模型进行比对,验证是否为同一个人,返回验证结果。 |
detect/检测 |
上传一张图片,返回该图片中人脸的位置(支持多张人脸)。 |
align/聚焦 |
上传一张图片,返回该图片中人脸的关键点坐标(支持多张人脸)。 |
gid/模型id |
人脸模型的唯一标识,长度为32个字符,注册成功后生成。一个用户(auth_id)理论上可以注册多个人脸模型(对应gid),在进行验证时必须指定gid。 |
auth_id/用户id |
由应用传入,用于标识用户身份,长度为6-18个字符(由英文字母、数字、下划线组成,不能以数字开头)。同一个auth_id暂时仅允许注册一个模型。 |
注:人脸识别只支持PNG、JPG、BMP格式的图片。
11.1 人脸注册
- // 使用FaceRequest(Context context)构造一个FaceRequest对象
- FaceRequest face = new FaceRequest(this);
- // 设置业务类型为注册
- face.setParameter(SpeechConstant.WFR_SST, "reg");
- // 设置auth_id
- face.setParameter(SpeechConstant.WFR_AUTHID, mAuthId);
- // 调用sendRequest(byte[] img, RequestListener listener)方法发送注册请求,img为图片的二进制数据,listener为处理注册结果的回调对象
- face.sendRequest(imgData, mRequestListener);
回调对象mRequestListener的定义如下:
- RequestListener mRequestListener = new RequestListener() {
- // 获得结果时返回,JSON格式。
- public void onBufferReceived(byte[] buffer) {}
- // 流程结束时返回,error不为空则表示发生错误。
- public void onCompleted(SpeechError error) {}
- // 保留接口,扩展用。
- public void onEvent(int eventType, Bundle params) {}
- }
11.2 人脸验证
- // 设置业务类型为验证
- face.setParameter(SpeechConstant.WFR_SST, "verify");
- // 设置auth_id
- face.setParameter(SpeechConstant.WFR_AUTHID, mAuthId);
- // 设置gid,由于一个auth_id下只有一个gid,所以设置了auth_id时则可以不用设置gid。但是当
- // 没有设置auth_id时,必须设置gid
- face.setParameter(SpeechConstant.WFR_GID, gid);
- // 调用sendRequest(byte[] img, RequestListener listener)方法发送注册请求,img为图片的二进制数据,listener为处理注册结果的回调对象
- face.sendRequest(imgData, mRequestListener);
注册/验证结果中包含了是否成功、gid等信息,详细的JSON格式请参照第十二章附录,具体解析过程详见FaceDemo工程。
11.3 人脸检测
- // 设置业务类型为验证
- face.setParameter(SpeechConstant.WFR_SST, "detect");
- // 调用sendRequest(byte[] img, RequestListener listener)方法发送注册请求,img为图片的二进制数据,listener为处理注册结果的回调对象
- face.sendRequest(imgData, mRequestListener);
11.4 人脸聚焦
- // 设置业务类型为验证
- face.setParameter(SpeechConstant.WFR_SST, "align");
- // 调用sendRequest(byte[] img, RequestListener listener)方法发送注册请求,img为图片的二进制数据,listener为处理注册结果的回调对象
- face.sendRequest(imgData, mRequestListener);
12 附录
12.1 识别结果说明
JSON字段 |
英文全称 |
类型 |
说明 |
sn |
sentence |
number |
第几句 |
ls |
last sentence |
boolean |
是否最后一句 |
bg |
begin |
number |
开始 |
ed |
end |
number |
结束 |
ws |
words |
array |
词 |
cw |
chinese word |
array |
中文分词 |
w |
word |
string |
单字 |
sc |
score |
number |
分数 |
听写结果示例:
- {"sn":1,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"w":"今天","sc":0}]},{"bg":0,"cw":{"w":"的","sc":0}]},{"bg":0,"cw":[{"w":"天气","sc":0}]},{"bg":0,"cw":[{"w":"怎么样","sc":0}]},{"bg":0,"cw":[{"w":"。","sc":0}]}]}
多候选结果示例:
- {"sn":1,"ls":false,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"w":"我想听","sc":0}]},{"bg":0,"cw":[{"w":"拉德斯基进行曲","sc":0},{"w":"拉得斯进行曲","sc":0}]}]}
语法识别结果示例:
- {"sn":1,"ls":true,"bg":0,"ed":0,"ws":[{"bg":0,"cw":[{"sc":"70","gm":"0","w":"北京到上海"},{"sc":"69","gm":"0","w":"天京到上海"},{"sc":"58","gm":"0","w":"东京到上海"}]}]}
12.2 合成发音人列表
1、 语言为中英文的发音人可以支持中英文的混合朗读。
2、 英文发音人只能朗读英文,中文无法朗读。
3、 汉语发音人只能朗读中文,遇到英文会以单个字母的方式进行朗读。
4、 使用新引擎参数会获得更好的合成效果。
发音人名称 |
属性 |
语言 |
参数名称 |
新引擎参数 |
备注 |
小燕 |
青年女声 |
中英文(普通话) |
xiaoyan |
默认 |
|
小宇 |
青年男声 |
中英文(普通话) |
xiaoyu |
||
凯瑟琳 |
青年女声 |
英文 |
catherine |
||
亨利 |
青年男声 |
英文 |
henry |
||
玛丽 |
青年女声 |
英文 |
vimary |
||
小研 |
青年女声 |
中英文(普通话) |
vixy |
||
小琪 |
青年女声 |
中英文(普通话) |
vixq |
xiaoqi |
|
小峰 |
青年男声 |
中英文(普通话) |
vixf |
||
小梅 |
青年女声 |
中英文(粤语) |
vixm |
xiaomei |
|
小莉 |
青年女声 |
中英文(台湾普通话) |
vixl |
xiaolin |
|
小蓉 |
青年女声 |
汉语(四川话) |
vixr |
xiaorong |
|
小芸 |
青年女声 |
汉语(东北话) |
vixyun |
xiaoqian |
|
小坤 |
青年男声 |
汉语(河南话) |
vixk |
xiaokun |
|
小强 |
青年男声 |
汉语(湖南话) |
vixqa |
xiaoqiang |
|
小莹 |
青年女声 |
汉语(陕西话) |
vixying |
||
小新 |
童年男声 |
汉语(普通话) |
vixx |
xiaoxin |
|
楠楠 |
童年女声 |
汉语(普通话) |
vinn |
nannan |
|
老孙 |
老年男声 |
汉语(普通话) |
vils |
||
Mariane |
法语 |
Mariane |
|||
Allabent |
俄语 |
Allabent |
|||
Gabriela |
西班牙语 |
Gabriela |
|||
Abha |
印地语 |
Abha |
|||
XiaoYun |
越南语 |
XiaoYun |
12.3 错误码列表
1、10000~19999的错误码参见MSC错误码链接。
2、其它错误码参见下表:
错误码 |
错误值 |
含义 |
ERROR_NO_NETWORK |
20001 |
无有效的网络连接 |
ERROR_NETWORK_TIMEOUT |
20002 |
网络连接超时 |
ERROR_NET_EXPECTION |
20003 |
网络连接发生异常 |
ERROR_INVALID_RESULT |
20004 |
无有效的结果 |
ERROR_NO_MATCH |
20005 |
无匹配结果 |
ERROR_AUDIO_RECORD |
20006 |
录音失败 |
ERROR_NO_SPPECH |
20007 |
未检测到语音 |
ERROR_SPEECH_TIMEOUT |
20008 |
音频输入超时 |
ERROR_EMPTY_UTTERANCE |
20009 |
无效的文本输入 |
ERROR_FILE_ACCESS |
20010 |
文件读写失败 |
ERROR_PLAY_MEDIA |
20011 |
音频播放失败 |
ERROR_INVALID_PARAM |
20012 |
无效的参数 |
ERROR_TEXT_OVERFLOW |
20013 |
文本溢出 |
ERROR_INVALID_DATA |
20014 |
无效数据 |
ERROR_LOGIN |
20015 |
用户未登陆 |
ERROR_PERMISSION_DENIED |
20016 |
无效授权 |
ERROR_INTERRUPT |
20017 |
被异常打断 |
ERROR_VERSION_LOWER |
20018 |
版本过低 |
ERROR_COMPONENT_NOT_INSTALLED |
21001 |
没有安装语音组件 |
ERROR_ENGINE_NOT_SUPPORTED |
21002 |
引擎不支持 |
ERROR_ENGINE_INIT_FAIL |
21003 |
初始化失败 |
ERROR_ENGINE_CALL_FAIL |
21004 |
调用失败 |
ERROR_ENGINE_BUSY |
21005 |
引擎繁忙 |
ERROR_LOCAL_NO_INIT |
22001 |
本地引擎未初始化 |
ERROR_LOCAL_RESOURCE |
22002 |
本地引擎无资源 |
ERROR_LOCAL_ENGINE |
22003 |
本地引擎内部错误 |
ERROR_IVW_INTERRUPT |
22004 |
本地唤醒引擎被异常打断 |
ERROR_UNKNOWN |
20999 |
未知错误 |
12.4 声纹业务
文本密码JSON示例:
- {"txt_pwd":["我的地盘我做主","移动改变生活","芝麻开门"]}
数字密码JSON示例:
- {"num_pwd":["03285469","09734658","53894276","57392804","68294073"]}
声纹业务结果(VerifierResult)成员说明
成员 |
说明 |
sst |
业务类型,取值为train或verify |
ret |
返回值,0为成功,-1为失败 |
vid |
注册成功的声纹模型id |
score |
当前声纹相似度 |
suc |
本次注册已成功的训练次数 |
rgn |
本次注册需要的训练次数 |
trs |
注册完成描述信息 |
err |
注册/验证返回的错误码 |
dcs |
描述信息 |
12.5 人脸识别结果说明
JSON字段 |
类型 |
说明 |
sst |
String |
业务类型,取值为“reg”或“verify” |
ret |
int |
返回值,0为成功,-1为失败 |
rst |
String |
注册/验证成功 |
gid |
String |
注册成功的人脸模型id |
score |
double |
人脸验证的得分(验证时返回) |
sid |
String |
本次交互会话的id |
uid |
String |
返回的用户id |
注册结果示例:
- {"ret":"0","uid":"","rst":"success","gid":"wfr278b0092@hf9a6907805f269a2800","sid":"wfr278b0092@hf9a6907805f269a2800","sst":"reg"}
验证结果示例:
- {"ret":"0","uid":"","sid":"wfr27830092@hf9a6907805fb19a2800","sst":"verify","score":"100.787","rst":"success","gid":"wfr278b0092@hf9a69"}
检测结果示例:
- {"ret":"0","uid":"a12456952","rst":"success","face":[{"position":{"bottom":931,"right":766,"left":220,"top":385},"attribute":{"pose":{"pitch":1}},"tag":"","confidence":" 8.400"}],"sid":"wfr278f0004@hf9a6907bcc8c19a2800","sst":"detect"}
聚焦结果示例:
- {"ret":"0","uid":"a1316826037","rst":"success","result":[{"landmark":{"right_eye_right_corner":{"y":"98.574","x":"127.327"},"left_eye_left_corner":{"y":"101.199","x":"40.101"},"right_eye_center":{"y":"98.090","x":"113.149"},"left_eyebrow_middle":{"y":"83.169","x":"46.642"},"right_eyebrow_left_corner":{"y":"85.135","x":"96.663"},"mouth_right_corner":{"y":"164.645","x":"109.419"},"mouth_left_corner":{"y":"166.419","x":"60.044"},"left_eyebrow_left_corner":{"y":"89.283","x":"28.029"},"right_eyebrow_middle":{"y":"80.991","x":"117.417"},"left_eye_center":{"y":"99.803","x":"53.267"},"nose_left":{"y":"137.397","x":"66.491"},"mouth_lower_lip_bottom":{"y":"170.229","x":"86.013"},"nose_right":{"y":"136.968","x":"101.627"},"left_eyebrow_right_corner":{"y":"86.090","x":"68.351"},"right_eye_left_corner":{"y":"99.898","x":"100.736"},"nose_bottom":{"y":"144.465","x":"84.032"},"nose_top":{"y":"132.959","x":"83.074"},"mouth_middle":{"y":"164.466","x":"85.325"},"left_eye_right_corner":{"y":"101.043","x":"67.275"},"mouth_upper_lip_top":{"y":"159.418","x":"84.841"},"right_eyebrow_right_corner":{"y":"84.916","x":"136.423"}}}],"sid":"wfr278500ec@ch47fc07eb395d476f00","sst":"align"}
13 常见问题
1)集成语音识别功能时,程序启动后没反应?
答:请检查是否忘记使用SpeechUtility初始化。也可以在监听器的onError函数中打印错误信息,根据信息提示,查找错误源。
- public void onError(SpeechError error) {
- Log.d(error.toString());
- }
2)SDK是否支持本地语音能力?
答:Android平台SDK已经支持本地合成、本地命令词识别、本地听写语音唤醒功能了,声纹功能也即将上线。
3) Appid的使用规范?
答:申请的Appid和对应下载的SDK具有一致性,请确保在使用过程中规范传入。一个Appid对应一个平台下的一个应用,如在多个平台开发同款应用,还需申请对应平台的Appid。
更多问题,请见:
http://xfyun.cn/index.php/default/doccenter/doccenterInner?itemTitle=ZmFx&anchor=Y29udGl0bGU2Mw==
联系方式:
邮箱:msp_support@iflytek.com QQ群:91104836,153789256
讯飞语音 Great!!!相关推荐
- Android自带语音播报+讯飞语音播报封装(直接用)
一.Android自带的语音播报 1.查看是否支持中文,在测试的设备中打开'设置' -->找到 '语言和输入法'-->查看语音选项,是否支持中文,默认仅支持英文. 使用如下: public ...
- (原创)用讯飞语音实现人机交互的功能
目前在做一款车载的项目,其中有一个需求是在开车的时候实现人与手机的对话,全过程不需要用手,只用语音操控. 这个就类似于人与机器人的对话,机器人在后台一直待命,用户说话 机器人做出对应的反映. 但由于用 ...
- UNITY 接讯飞语音过程总结
UNITY 接讯飞语音过程总结 11:13 2017/3/14 1,安装问题:JDK与ECLIPSE位数一定要对应,32位对64位会出现 java was returned ....code 13的弹 ...
- iOS: 讯飞语音的使用
一.介绍: 讯飞语音做的相当不错,容错率达到90%多,如果需要做语音方面的功能,它绝对是一个不错的选择.讯飞语音的功能很多:语音听写.语音识别.语音合成等,但我们最常用的还是语音听写.讯飞语音中包含界 ...
- 搜狗语音输入提示服务器繁忙,讯飞语音输入如何提高识别率 提高方法
讯飞语音是一款语音输入软件,能够帮助用户进行通过语音说话,进行智能识别,帮助用户在繁忙的时候,直接语音输入,不过语言描述要清晰哦!小编在这里分享一下识别率提高方法,不要错过哦! 讯飞语音输入如何提高识 ...
- 讯飞语音——带你简单实现语音听写
语音听写 de 简单实现 一.前言 如果你没有在讯飞语音平台上创建应用,请先参考讯飞语音的详细配置使用 二.功能描述 语音听写和语音合成都是较为基础也是最常使用的两个基本功能. 语音合成是将文本转化为 ...
- 讯飞语音转文字 PHP demo
讯飞语音转文字PHP tp6 demo 讯飞官网没有PHP demo我是很诧异的 改成了我需要的tp6 demo 讯飞官网没有PHP demo我是很诧异的 我php天下第一就这么没牌面吗 网上找了很久 ...
- Unity与讯飞语音交互:使用aiui技能
语音交互 说到人工智能,离不开语音,大家会认为一个设备可以跟人对话是有智能的体现,在国内语音智能研发,讯飞是公认的做的最好的,在了解语音智能时,客户提的是讯飞,因为他们目标是要做一个好的语音交互.客户 ...
- 讯飞社区android 源码,android 讯飞语音 demo
[实例简介] android 讯飞语音 demo 博客地址:http://blog.csdn.net/chenshufei2/article/details/8496905 [实例截图] [核心代码] ...
- Android中集成讯飞语音,语音转文字以及文字转语音操作
讯飞语音 * 在讯飞语音开发者平台上http://www.xfyun.cn/doccenter/newer具体准备工作按照开发文档操作.注意的是创建一个项目,appid和adk是一一对应的* 在这里, ...
最新文章
- Scrapy+Scrapy-redis+Scrapyd+Gerapy 分布式爬虫框架整合
- 周休2.5天是一种奢望?互联网人自愿加班成常态?
- 编程用的记事本软件_数控常用编程软件那么多,你用哪一种?
- eclipse批量修改package、import中的包名
- ios7中让程序使用统一的status bar风格
- face++算法工程实习生面试
- 美团服务体验平台对接业务数据的最佳实践-海盗中间件
- Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext
- 小白python自学—报错问题汇总(持续更新)
- 实战百度竞价恶意点击心得
- 分布式服务的幂等性设计,值得学习!
- OpenGL ES总结(五)OpenGL 中pipeline机制
- ArcGIS Python实现Modis NDVI批量化月最大合成
- BackTrack4——利用***测试保证系统安全
- 小米4Linux刷机包,小米4官方原厂固件rom线刷刷机包下载_小米4线刷官方系统包
- 求区域的最大内接矩形
- 微信公众号授权登录,微信sdk的使用,及ios配置微信sdk失败
- 文件截取\裁剪工具软件
- 【python】【爬虫】爬取Fate Grand Order wiki所有英灵礼装图鉴
- linux下dd工具,dd 工具使用