Android,百度,云知声tts总结
最近在做Android语音播报功能(TTS),现总结如下:(ps:demo代码地址:https://github.com/giserlong/TTS_DEMO)
一.Android原生接口
用Android原生接口TextToSpeech,简单易用,但是一般情况下不支持中文,需自己下载讯飞语音+ 等中文引擎,并设置为系统默认tts,方可正常播报中文,关键代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_native);
//初始化TTS
tts = new TextToSpeech(this, this);
//获取控件
speechText = (EditText)findViewById(R.id.speechTextView); speechButton = (Button)findViewById(R.id.speechButton);
//为button添加监听
speechButton.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
// TODO Auto-generated method stub
tts.speak(speechText.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
}
});
} @Override
public void onInit(int status){
// 判断是否转化成功
if (status == TextToSpeech.SUCCESS){
//tts.getCurrentEngine();
//默认设定语言为中文,原生的android貌似不支持中文。
int result = tts.setLanguage(Locale.CHINA);
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED){
tts.setLanguage(Locale.US);
Toast.makeText(this,"不支持中文,已自动设置为英文",Toast.LENGTH_SHORT).show();
Log.d("ss","");
}else{
Toast.makeText(this,"已自动设置为中文",Toast.LENGTH_SHORT).show();
Log.d("ss","");
}
}
}
二.百度离在线融合SDK
注册百度智能云开发者账号后,添加语音合成应用,填写包名等相关信息后,生成key及APPID等信息:
激活SDK需此关键信息,还需下载对应SDK,并添加至项目中,引用相关jar包,添加对应so库至asset
关键代码如下:
package com.yupont.www.myapplication; import android.content.Context;
import android.os.Environment; import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.TtsMode; import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream; /**
* <p>文件描述:<p>
* <p>作者:Mark<p>
* <p>创建时间:2019/5/23<p>
* <p>更改时间:2019/5/23<p>
* <p>版本号:1<p>
*/
public class BaiDuSpeechUtil {
private final String TAG = this.getClass().getSimpleName();
private SpeechSynthesizer mSpeechSynthesizer;
private String mSampleDirPath;
private static final String SAMPLE_DIR_NAME = "baiduTTS";
//-------以下全是在assets下的文件,使用离线时必须全部copy到手机中方可使用----start--
private static final String SPEECH_FEMALE_MODEL_NAME = "bd_etts_common_speech_f7_mand_eng_high_am-mix_v3.0.0_20170512.dat";
private static final String SPEECH_MALE_MODEL_NAME = "bd_etts_common_speech_m15_mand_eng_high_am-mix_v3.0.0_20170505.dat";
private static final String TEXT_MODEL_NAME = "bd_etts_text.dat";
private static final String ENGLISH_SPEECH_FEMALE_MODEL_NAME = "bd_etts_speech_female_en.dat";
private static final String ENGLISH_SPEECH_MALE_MODEL_NAME = "bd_etts_speech_male_en.dat";
private static final String ENGLISH_TEXT_MODEL_NAME = "bd_etts_text_en.dat"; //--------end------------------------------------------------------------- private static BaiDuSpeechUtil baiDuSpeechUtil = null;
public static BaiDuSpeechUtil getInstance(){
if(baiDuSpeechUtil == null) {
synchronized (BaiDuSpeechUtil.class) {
if(baiDuSpeechUtil == null) {
baiDuSpeechUtil = new BaiDuSpeechUtil();
}
}
}
return baiDuSpeechUtil;
} /**
* 初始化百度语音资源
* @param context
*/
public void setInitialEnv(Context context) {
initialEnv(context);
}
/**
* 初始化百度语音播报相关
* @param context
*/
public void setInitialTts(Context context, SpeechSynthesizerListener speechSynthesizerListener){
initialTts(context,speechSynthesizerListener);
} private void initialEnv(Context context) {
// long start_time= System.currentTimeMillis();
if (mSampleDirPath == null) {
String sdcardPath = Environment.getExternalStorageDirectory().toString();
mSampleDirPath = sdcardPath + "/" + SAMPLE_DIR_NAME;
}
makeDir(mSampleDirPath);
copyFromAssetsToSdcard(context,false, SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_FEMALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/" + SPEECH_MALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, TEXT_MODEL_NAME, mSampleDirPath + "/" + TEXT_MODEL_NAME);
copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME, mSampleDirPath + "/"
+ ENGLISH_SPEECH_FEMALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_SPEECH_MALE_MODEL_NAME, mSampleDirPath + "/"
+ ENGLISH_SPEECH_MALE_MODEL_NAME);
copyFromAssetsToSdcard(context,false, "english/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + "/"
+ ENGLISH_TEXT_MODEL_NAME); // Log.d(TAG,"initialEnv cost:"+ (System.currentTimeMillis()-start_time));
} private void makeDir(String dirPath) {
File file = new File(dirPath);
if (!file.exists()) {
file.mkdirs();
}
} /**
* 将sample工程需要的资源文件拷贝到SD卡中使用(授权文件为临时授权文件,请注册正式授权)
* 主要是在离线时候用到,只需执行一次即可,这里写的不严谨,应该去判断一下离线用的那些文件,sd卡是否存在,如果不存在,则copy,如果存在则无需在copy,可在子线程操作
* @param isCover 是否覆盖已存在的目标文件
* @param source
* @param dest
*/
private void copyFromAssetsToSdcard(Context context, boolean isCover, String source, String dest) {
File file = new File(dest);
if (isCover || (!isCover && !file.exists())) {
InputStream is = null;
FileOutputStream fos = null;
try {
is = context.getAssets().open(source);
String path = dest;
fos = new FileOutputStream(path);
byte[] buffer = new byte[1024];
int size = 0;
while ((size = is.read(buffer, 0, 1024)) != -1) {
fos.write(buffer, 0, size);
}
fos.flush(); } catch (Exception e) {
e.printStackTrace();
} finally { if (is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
} }
if (fos != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
} }
} }
//此方法可在子线程中操作,由于这个初始化过程比较费时,大概在1s左右,看项目需求而定,如果是进入界面就必须播放(伴随UI改变的)的,在UI线程,如无其他特殊要求,放在子线程中即可
private void initialTts(Context context,SpeechSynthesizerListener speechSynthesizerListener) {
// long start_time= System.currentTimeMillis();
mSpeechSynthesizer = SpeechSynthesizer.getInstance();
mSpeechSynthesizer.setContext(context);
mSpeechSynthesizer.setSpeechSynthesizerListener(speechSynthesizerListener);
mSpeechSynthesizer.setApiKey(Config.appKey_baidu, Config.secret_baidu);
mSpeechSynthesizer.setAppId(Config.appID_baidu); // 文本模型文件路径 (离线引擎使用)
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, mSampleDirPath + "/"
+ TEXT_MODEL_NAME);
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, mSampleDirPath + "/"
+ SPEECH_FEMALE_MODEL_NAME);
// 本地授权文件路径,如未设置将使用默认路径.设置临时授权文件路径,LICENCE_FILE_NAME请替换成临时授权文件的实际路径,仅在使用临时license文件时需要进行设置,如果在[应用管理]中开通了正式离线授权,不需要设置该参数,建议将该行代码删除(离线引擎)
// 如果合成结果出现临时授权文件将要到期的提示,说明使用了临时授权文件,请删除临时授权即可。 // 发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档,以文档说明为准。0--普通女声,1--普通男声,2--特别男声,3--情感男声。。。)
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
// 设置Mix模式的合成策略, //mix模式下,wifi使用在线合成,非wifi使用离线合成)
mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_HIGH_SPEED_SYNTHESIZE_WIFI);
// if(SystemUtil.isNetWorkConnected(getCurrentActivity())) {
// // AuthInfo接口用于测试开发者是否成功申请了在线或者离线授权,如果测试授权成功了,可以删除AuthInfo部分的代码(该接口首次验证时比较耗时),不会影响正常使用(合成使用时
// AuthInfo authInfo=this.mSpeechSynthesizer.auth(TtsMode.MIX);
//
// if (authInfo.isSuccess()){
// toPrint("auth success");
// }else{
// String errorMsg=authInfo.getTtsError().getDetailMessage();
// toPrint("auth failed errorMsg=" + errorMsg);
// }
// } // 初始化tts
mSpeechSynthesizer.initTts(TtsMode.MIX);
// 加载离线英文资源(提供离线英文合成功能)
//int result = mSpeechSynthesizer.loadEnglishModel(mSampleDirPath + "/" + ENGLISH_TEXT_MODEL_NAME, mSampleDirPath + mSampleDirPath + "/" + ENGLISH_SPEECH_FEMALE_MODEL_NAME);
// Log.d(TAG,"initialTts cost:"+ (System.currentTimeMillis()-start_time));
int result = mSpeechSynthesizer.loadModel(mSampleDirPath + "/" + TEXT_MODEL_NAME, mSampleDirPath + mSampleDirPath + "/" + SPEECH_FEMALE_MODEL_NAME);
if(result<0){
result++;
}
} /**
* 播报的文字
* @param content
*/
public void speakText(String content) {
try{
if(mSpeechSynthesizer != null) {
int result = mSpeechSynthesizer.speak(content);
if (result < 0) {
// Log.d(TAG,"error,please look up error code in doc or URL:http://yuyin.baidu.com/docs/tts/122 ");
}
}
}catch (Exception e) {
e.printStackTrace();
} } /**
* 暂停
*/
public void pauseSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.pause();
}
} /**
* 停止播放
*/
public void stopSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.stop();
}
} /**
* 接着停止后的地方播放
*/ public void resumeSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.resume();
}
} /**
* 释放mSpeechSynthesizer,在使用完之后必须调用,确保下个界面使用的时候资源已经释放掉了,否则下个界面将无法正常播放
*/
public void releaseSpeechSynthesizer(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer.release();
}
} public void setSpeechSynthesizerNull(){
if(mSpeechSynthesizer != null) {
mSpeechSynthesizer = null;
}
} public void endSpeechSynthesizer(){
pauseSpeechSynthesizer();
stopSpeechSynthesizer();
releaseSpeechSynthesizer();
setSpeechSynthesizerNull();
} }
首次需联网,自动下载授权文件,以后离线也能用
三.云知声离线SDK
同百度,注册账号后,下载sdk,做好引用与配置
gradle中配置:
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
关键代码:
package com.yupont.www.myapplication; import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream; import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast; import com.unisound.client.SpeechConstants;
import com.unisound.client.SpeechSynthesizer;
import com.unisound.client.SpeechSynthesizerListener; public class yzsTTSOfflineActivity extends Activity { private static boolean TTS_PLAY_FLAGE = false; private EditText mTTSText;
private TextView mTextViewTip;
private TextView mTextViewStatus;
private Button mTTSPlayBtn;
private SpeechSynthesizer mTTSPlayer;
private final String mFrontendModel= Environment.getExternalStorageDirectory().toString()+"/Yupont/UAV/OfflineTTSModels/frontend_model";
private final String mBackendModel = Environment.getExternalStorageDirectory().toString()+"/Yupont/UAV/OfflineTTSModels/backend_lzl";
// private final String mFrontendModel= getClass().getClassLoader().getResource("assets/OfflineTTSModels/frontend_model").getPath().substring(5);
// private final String mBackendModel = getClass().getClassLoader().getResource("assets/OfflineTTSModels/backend_lzl").getPath();
//
@Override
public void onCreate(Bundle savedInstanceState) {
// requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_yzs_offline_tts);
copyFilesFassets(this,"OfflineTTSModels", Environment.getExternalStorageDirectory().toString()+"/Yupont/UAV/OfflineTTSModels"); //getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.status_bar_main); mTTSText = (EditText) findViewById(R.id.textViewResult);
//mTextViewStatus = (TextView) findViewById(R.id.textViewStatus);
//mTextViewTip = (TextView) findViewById(R.id.textViewTip);
mTTSPlayBtn = (Button) findViewById(R.id.recognizer_btn);
mTTSPlayBtn.setEnabled(false);
mTTSPlayBtn.setOnClickListener(new OnClickListener() { @Override
public void onClick(View arg0) {
TTSPlay();
}
}); // 初始化本地TTS播报
initTts();
} /**
* 从assets目录中复制整个文件夹内容
* @param context Context 使用CopyFiles类的Activity
* @param oldPath String 原文件路径 如:/aa
* @param newPath String 复制后路径 如:xx:/bb/cc
*/
public void copyFilesFassets(Context context, String oldPath, String newPath) {
try {
String fileNames[] = context.getAssets().list(oldPath);//获取assets目录下的所有文件及目录名
if (fileNames.length > 0) {//如果是目录
File file = new File(newPath);
file.mkdirs();//如果文件夹不存在,则递归
for (String fileName : fileNames) {
copyFilesFassets(context,oldPath + "/" + fileName,newPath+"/"+fileName);
}
} else {//如果是文件
if(new File(newPath).exists()){
return;
}
InputStream is = context.getAssets().open(oldPath);
FileOutputStream fos = new FileOutputStream(new File(newPath));
byte[] buffer = new byte[1024];
int byteCount=0;
while((byteCount=is.read(buffer))!=-1) {//循环从输入流读取 buffer字节
fos.write(buffer, 0, byteCount);//将读取的输入流写入到输出流
}
fos.flush();//刷新缓冲区
is.close();
fos.close();
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
//如果捕捉到错误则通知UI线程
//MainActivity.handler.sendEmptyMessage(COPY_FALSE);
}
} /**
* 初始化本地离线TTS
*/
private void initTts() { // 初始化语音合成对象
try {
mTTSPlayer = new SpeechSynthesizer(this, Config.appKey, Config.secret); // 设置本地合成
mTTSPlayer.setOption(SpeechConstants.TTS_SERVICE_MODE, SpeechConstants.TTS_SERVICE_MODE_LOCAL);
File _FrontendModelFile = new File(mFrontendModel);
if (!_FrontendModelFile.exists()) {
toastMessage("文件:" + mFrontendModel + "不存在,请将assets下相关文件拷贝到SD卡指定目录!");
}
File _BackendModelFile = new File(mBackendModel);
if (!_BackendModelFile.exists()) {
toastMessage("文件:" + mBackendModel + "不存在,请将assets下相关文件拷贝到SD卡指定目录!");
}
// 设置前端模型
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_FRONTEND_MODEL_PATH, mFrontendModel);
// 设置后端模型
mTTSPlayer.setOption(SpeechConstants.TTS_KEY_BACKEND_MODEL_PATH, mBackendModel);
// 设置回调监听
mTTSPlayer.setTTSListener(new SpeechSynthesizerListener() { @Override
public void onEvent(int type) {
switch (type) {
case SpeechConstants.TTS_EVENT_INIT:
// 初始化成功回调
log_i("onInitFinish");
mTTSPlayBtn.setEnabled(true);
break;
case SpeechConstants.TTS_EVENT_SYNTHESIZER_START:
// 开始合成回调
log_i("beginSynthesizer");
break;
case SpeechConstants.TTS_EVENT_SYNTHESIZER_END:
// 合成结束回调
log_i("endSynthesizer");
break;
case SpeechConstants.TTS_EVENT_BUFFER_BEGIN:
// 开始缓存回调
log_i("beginBuffer");
break;
case SpeechConstants.TTS_EVENT_BUFFER_READY:
// 缓存完毕回调
log_i("bufferReady");
break;
case SpeechConstants.TTS_EVENT_PLAYING_START:
// 开始播放回调
log_i("onPlayBegin");
break;
case SpeechConstants.TTS_EVENT_PLAYING_END:
// 播放完成回调
log_i("onPlayEnd");
setTTSButtonReady();
break;
case SpeechConstants.TTS_EVENT_PAUSE:
// 暂停回调
log_i("pause");
break;
case SpeechConstants.TTS_EVENT_RESUME:
// 恢复回调
log_i("resume");
break;
case SpeechConstants.TTS_EVENT_STOP:
// 停止回调
log_i("stop");
break;
case SpeechConstants.TTS_EVENT_RELEASE:
// 释放资源回调
log_i("release");
break;
default:
break;
} } @Override
public void onError(int type, String errorMSG) {
// 语音合成错误回调
log_i("onError");
toastMessage(errorMSG);
setTTSButtonReady();
}
});
// 初始化合成引擎
mTTSPlayer.init("");
} catch (Exception e) {
e.printStackTrace();
}
} private void TTSPlay() {
if (!TTS_PLAY_FLAGE) {
mTTSPlayer.playText(mTTSText.getText().toString());
setTTSButtonStop();
} else {
mTTSPlayer.stop();
setTTSButtonReady();
} } private void setTTSButtonStop() {
TTS_PLAY_FLAGE = true;
mTTSPlayBtn.setText(R.string.stop_tts);
} private void setTTSButtonReady() {
mTTSPlayBtn.setText(R.string.start_tts);
TTS_PLAY_FLAGE = false;
} protected void setTipText(String tip) { mTextViewTip.setText(tip);
} protected void setStatusText(String status) { mTextViewStatus.setText(getString(R.string.lable_status) + "(" + status + ")");
} @Override
public void onPause() {
super.onPause();
// 主动停止识别
if (mTTSPlayer != null) {
mTTSPlayer.stop();
}
} private void log_i(String log) {
Log.i("demo", log);
} @Override
protected void onDestroy() {
// 主动释放离线引擎
if (mTTSPlayer != null) {
mTTSPlayer.release(SpeechConstants.TTS_RELEASE_ENGINE, null);
}
super.onDestroy();
} private void toastMessage(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
}
Android,百度,云知声tts总结的更多相关文章
- Android 百度云推送
http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/sdk/clientsdk. public class DemoAppl ...
- Android 百度云媒体 等播放器播放4:3等多种比例的视频 大小配置的问题
视频播放宽度大小各不一样,就需要根据视频的尺寸去适应屏幕的宽度和高度进行缩放. 思路是在onPrepared方法中,或者切换屏幕后,获取video的高度和宽度.以及屏幕展示区域的高度的宽度.并对比二者 ...
- Android 高仿微信实时聊天 基于百度云推送
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:[张鸿洋的博客] 一直在仿微信界面,今天终于有幸利用百 ...
- Android 高仿微信即时聊天 百度云为基础的推
转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:[张鸿洋的博客] 一直在仿微信界面,今天最终有幸利用百 ...
- Android推送 百度云推送 入门篇
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/27231237 现在app基本都有推送的功能,于是看了下百度云的推送,官方文档和D ...
- 消息推送之百度云推送Android集成与使用方法
这两天由于项目需要,研究了一下百度云推送,本来这事没什么多大工作量的,但注册百度开发者账户创建应用令我蛋疼菊紧了好一阵,这些东西做了对技术没啥提升,不做又不行,必经之路.好在我耗费了N多个毫毫秒秒后稀 ...
- android开发(25) - 推送的实现,使用百度云推送
什么叫推送? 中文名称:推送 英文名称:push 定义:描述因特网内容提供者和因特网用户之间工作方式的术语.“推送”指因特网内容提供者定期向预订用户“提供”数据. 项目中有可能会用到推送.如果自己写一 ...
- 消息推送之百度云推送Android集成与用法
这两天因为项目须要.研究了一下百度云推送,本来这事没什么多大工作量的,但注冊百度开发人员账户创建应用令我蛋疼菊紧了好一阵,这些东西做了对技术没啥提升,不做又不行,必经之路. 好在我耗费了N多个毫毫秒秒 ...
- Android推送服务——百度云推送
一.推送服务简介 消息推送,顾名思义,是由一方主动发起,而另一方与发起方以某一种方式建立连接并接收消息.在Android开发中,这里的发起方我们把它叫做推送服务器(Push Server),接收方叫做 ...
随机推荐
- SSM框架之Mybatis(7)延迟加载、缓存及注解
Mybatis(7)延迟加载.缓存及注解 1.延迟加载 延迟加载: 就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据.延迟加载也称懒加载. **好处:**先从单表查询,需要时再从关联表去关 ...
- ArcGIS api for JavaScript 3.27 在线浏览的一些小部件
var navOption; var navToolbar;// 当前选择的操作 require( [ "esri/toolbars/navigation", "esri ...
- Data Management Technology(1) -- Introduction
1.Database concepts (1)Data & Information Information Is any kind of event that affects the stat ...
- skip-broken to work around the problem rpm -Va --nofiles --nodigest
清除yum缓存 yum clean all 重新安装,见结尾[root@localhost ~]# yum install libstdc++.so.6Loaded plugins: fastestm ...
- Codeforces Round #584
传送门 A. Paint the Numbers 签到. Code #include <bits/stdc++.h> using namespace std; typedef long l ...
- Leetcode103_Binary-tree-zigzag-level-order-traversal
原题: https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/ 思路,如果无从下手,先要了解二叉树的广度优 ...
- 职位画像分析(pandas/ matplotlib)
一.数据分析的步骤 二.提出问题 (1) 分析数据分析师主要的技能排名? (2) 分析数据分析师薪资和岗位地点.学历.工作年限的关系? (3) 数据分析师的学历需求? (4) 不同城市数据分析师的需求 ...
- 第05组 Alpha事后诸葛亮
组长博客链接(2分) 组长博客链接 现代软件工程 项目Postmortem 模板(27分) 设想和目标(2分) 1.我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? ...
- IDEA2019 Win10 Tomcat Server控制台中文乱码的快速解决办法
原理 Windows10的控制台使用GBK编码,而Tomcat使用UTF-8编码,导致乱码 解决办法 修改$tomcat/conf/logging.properties文件 # 注释这行 java.u ...
- matlab练习程序(计算图像旋转角度)
比如有图像1,将其旋转n度得到图像2,问如何比较两张图像得到旋转的度数n. 算法思路参考logpolar变换: 1.从图像中心位置向四周引出射线. 2.计算每根射线所打到图像上的像素累计和,得到极坐标 ...