使用灵云的图像识别功能需要先在官网上进行注册应用。官网地址
注册比较简单,就不做过多介绍了,注册完应用以后,在后台创建自己的应用,创建完应用以后需要给应用开通对应的图像功能。
capKey说明:
ocr.local.bankcard 本地银行卡识别功能ocr.local.template.v6 本地身份证识别功能ocr.local.bizcard.v6 本地名片识别功能ocr.local 本地文本图片识别功能下载灵云的Android版本图像识别功能,下载地址。 如果使用在线功能,下载对应的SDK,里面有jar包和so,就可以满足需求了。如果要使用离线的图像识别功能,还需要下载灵云资源文件
资源文件列表:
Github
需要加入的so和jar包有:
libhci_curl.solibhci_sys.solibhci_sys_jni.solibhci_hwr.solibhci_hwr_hwr_jni.solibhci_hwr_local_recog.solibletter.conf.solibletter.dic.so需要使用的权限
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />加载配置类
package com.miao.util; /** * 灵云配置信息 * Created by 10048 on 2016/12/3. */ public class ConfigUtil { /** * 灵云APP_KEY */ public static final String APP_KEY = "c85d54f1"; /** * 开发者密钥 */ public static final String DEVELOPER_KEY = "712ddd892cf9163e6383aa169e0454e3"; /** * 灵云云服务的接口地址 */ public static final String CLOUD_URL = "test.api.hcicloud.com:8888"; /** * 需要运行的灵云能力 */ //云端多字识别功能 public static final String CAP_KEY_HWR_CLOUD_FREETALK = "hwr.cloud.freetalk"; //云端单字识别功能 public static final String CAP_KEY_HWR_CLOUD_LETTER = "hwr.cloud.letter"; //离线单字识别功能 public static final String CAP_KEY_HWR_LOCAL_LETTER = "hwr.local.letter"; //离线多字识别功能 public static final String CAP_KEY_HWR_LOCAL_FREESTYLUS = "hwr.local.freestylus"; //离线联想功能 public static final String CAP_KEY_HWR_LOCAL_ASSOCIATE_WORD = "hwr.local.associateword"; //离线笔形功能 public static final String CAP_KEY_HWR_LOCAL_PENSCRIPT = "hwr.local.penscript"; }封装灵云系统的初始化功能
package com.example.sinovoice.util; import android.content.Context; import android.os.Environment; import android.util.Log; import com.sinovoice.hcicloudsdk.api.HciCloudSys; import com.sinovoice.hcicloudsdk.common.AuthExpireTime; import com.sinovoice.hcicloudsdk.common.HciErrorCode; import com.sinovoice.hcicloudsdk.common.InitParam; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * Created by miaochangchun on 2016/11/28. */ public class HciCloudSysHelper { private static final String TAG = HciCloudSysHelper.class.getSimpleName(); private static HciCloudSysHelper mHciCloudSysHelper = null; private HciCloudSysHelper(){ } public static HciCloudSysHelper getInstance() { if (mHciCloudSysHelper == null) { return new HciCloudSysHelper(); } return mHciCloudSysHelper; } /** * 初始化函数 * @param context * @return */ public int init(Context context){ //配置串参数 String strConfig = getInitParam(context); int errCode = HciCloudSys.hciInit(strConfig, context); if (errCode != HciErrorCode.HCI_ERR_NONE){ Log.e(TAG, "hciInit Failed and return errcode = " + errCode); return errCode; } errCode = checkAuthAndUpdateAuth(); if (errCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "checkAuthAndUpdateAuth Failed and return errcode = " + errCode); return errCode; } return HciErrorCode.HCI_ERR_NONE; } /** * 获取授权 * @return */ private int checkAuthAndUpdateAuth() { // 获取系统授权到期时间 AuthExpireTime objExpireTime = new AuthExpireTime(); int initResult = HciCloudSys.hciGetAuthExpireTime(objExpireTime); if (initResult == HciErrorCode.HCI_ERR_NONE) { // 显示授权日期,如用户不需要关注该值,此处代码可忽略 Date date = new Date(objExpireTime.getExpireTime() * 1000); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA); Log.i(TAG, "expire time: " + sdf.format(date)); if (objExpireTime.getExpireTime() * 1000 > System.currentTimeMillis()) { // 已经成功获取了授权,并且距离授权到期有充足的时间(>7天) Log.i(TAG, "checkAuth success"); return initResult; } } // 获取过期时间失败或者已经过期 initResult = HciCloudSys.hciCheckAuth(); if (initResult == HciErrorCode.HCI_ERR_NONE) { Log.i(TAG, "checkAuth success"); return initResult; } else { Log.e(TAG, "checkAuth failed: " + initResult); return initResult; } } /** * 获取配置传参数 * @param context * @return */ private String getInitParam(Context context) { InitParam initParam = new InitParam(); //灵云云服务的接口地址,此项必填 initParam.addParam(InitParam.AuthParam.PARAM_KEY_APP_KEY, ConfigUtil.APP_KEY); //灵云云服务的接口地址,此项必填 initParam.addParam(InitParam.AuthParam.PARAM_KEY_DEVELOPER_KEY, ConfigUtil.DEVELOPER_KEY); //灵云云服务的接口地址,此项必填 initParam.addParam(InitParam.AuthParam.PARAM_KEY_CLOUD_URL, ConfigUtil.CLOUD_URL); String authPath = context.getFilesDir().getAbsolutePath(); //授权文件所在路径,此项必填 initParam.addParam(InitParam.AuthParam.PARAM_KEY_AUTH_PATH, authPath); //日志数目,默认保留多少个日志文件,超过则覆盖最旧的日志 initParam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_COUNT, "5"); String logPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "sinovoice" + File.separator + context.getPackageName() + File.separator + "log" + File.separator; File file = new File(logPath); if (!file.exists()) { file.mkdirs(); } //日志的路径,可选,如果不传或者为空则不生成日志 initParam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_PATH, logPath); Log.d(TAG, "logPath = " + logPath); //日志大小,默认一个日志文件写多大,单位为K initParam.addParam(InitParam.LogParam.PARAM_KEY_LOG_FILE_SIZE, "1024"); //日志等级,0=无,1=错误,2=警告,3=信息,4=细节,5=调试,SDK将输出小于等于logLevel的日志信息 initParam.addParam(InitParam.LogParam.PARAM_KEY_LOG_LEVEL, "5"); return initParam.getStringConfig(); } /** * 反初始化 * @return */ public int release(){ return HciCloudSys.hciRelease(); } }封装灵云手写识别功能
package com.miao.util; import android.content.Context; import android.util.Log; import com.sinovoice.hcicloudsdk.api.hwr.HciCloudHwr; import com.sinovoice.hcicloudsdk.common.HciErrorCode; import com.sinovoice.hcicloudsdk.common.Session; import com.sinovoice.hcicloudsdk.common.hwr.HwrAssociateWordsResult; import com.sinovoice.hcicloudsdk.common.hwr.HwrConfig; import com.sinovoice.hcicloudsdk.common.hwr.HwrInitParam; import com.sinovoice.hcicloudsdk.common.hwr.HwrRecogResult; import com.sinovoice.hcicloudsdk.common.hwr.HwrRecogResultItem; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Created by miaochangchun on 2016/11/1. */ public class HciCloudHwrHelper { public static final String TAG = HciCloudHwrHelper.class.getSimpleName(); private static HciCloudHwrHelper mHciCloudHwrHelper = null; private HciCloudHwrHelper(){ } public static HciCloudHwrHelper getInstance(){ if (mHciCloudHwrHelper == null) { return new HciCloudHwrHelper(); } return mHciCloudHwrHelper; } /** * 手写功能初始化 * @param context 上下文 * @param capkey 使用的capkey,手写单字识别为hwr.local.letter,多字识别为hwr.local.freestylus,联想功能为hwr.local.associateword * @return 返回0为成功,其他为失败 */ public int initHwr(Context context, String capkey){ String strConfig = getHwrInitParam(context, capkey); Log.d(TAG, "strConfig = " + strConfig); int errorCode = HciCloudHwr.hciHwrInit(strConfig); return errorCode; } /** * 手写识别函数 * @param data 笔迹坐标 * @param capkey 使用的capkey,手写单字识别为hwr.local.letter,多字识别为hwr.local.freestylus,联想功能为hwr.local.associateword * @return 返回识别的结果 */ public String recog(short[] data, String capkey){ Session session = new Session(); String sessionConfig = getHwrSessionParam(capkey); Log.d(TAG, "sessionConfig = " + sessionConfig); //sessionStart开启一个session int errorCode = HciCloudHwr.hciHwrSessionStart(sessionConfig, session); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "hciHwrSessionStart failed and return " + errorCode); HciCloudHwr.hciHwrSessionStop(session); return null; } //识别结果类进行初始化 HwrRecogResult recogResult = new HwrRecogResult(); //识别的配置串参数可以设置为空,默认就使用sessionConfig配置串参数 errorCode = HciCloudHwr.hciHwrRecog(session, data, "", recogResult); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "hciHwrRecog failed and return " + errorCode); return null; } //关闭Session errorCode = HciCloudHwr.hciHwrSessionStop(session); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "hciHwrSessionStop failed and return " + errorCode); return null; } //返回识别结果 StringBuilder sb = new StringBuilder(); List<HwrRecogResultItem> lists = recogResult.getResultItemList(); Iterator<HwrRecogResultItem> iterator = lists.iterator(); while (iterator.hasNext()) { HwrRecogResultItem item = iterator.next(); sb.append(item.getResult()).append(" , "); } return sb.toString(); // return recogResult.getResultItemList().get(0).getResult(); } /** * 联想词功能,对str进行联想,返回联想结果 * @param str 需要联想的字符串 * @param assCapkey 联想功能对应的capkey。 * @return */ public String associateWord(String str, String assCapkey){ Session session = new Session(); String sessionConfig = getAssociateWordSessionParam(assCapkey); int errorCode = HciCloudHwr.hciHwrSessionStart(sessionConfig, session); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "HciCloudHwr.hciHwrSessionStart failed and return " + errorCode); } HwrAssociateWordsResult hwrAssociateWordsResult = new HwrAssociateWordsResult(); errorCode = HciCloudHwr.hciHwrAssociateWords(session, str, "", hwrAssociateWordsResult); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "HciCloudHwr.hciHwrAssociateWords failed and return " + errorCode); } errorCode = HciCloudHwr.hciHwrSessionStop(session); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Log.e(TAG, "HciCloudHwr.hciHwrSessionStop failed and return " + errorCode); } ArrayList<String> lists = hwrAssociateWordsResult.getResultList(); StringBuilder sb = new StringBuilder(); Iterator<String> iterator = lists.iterator(); while (iterator.hasNext()) { String string = iterator.next(); sb.append(string).append(","); } return sb.toString(); } /** * 设置联想词的配置参数 * @param assCapkey 联想词功能所需的capkey,需要设置为 assCapkey=hwr.local.associateword * @return 联想词配置的字符串 */ private String getAssociateWordSessionParam(String assCapkey) { HwrConfig hwrConfig = new HwrConfig(); hwrConfig.addParam(HwrConfig.SessionConfig.PARAM_KEY_CAP_KEY, assCapkey); return hwrConfig.getStringConfig(); } /** * 获取手写识别的配置参数 * @param hwrCapkey 使用的capkey,手写单字识别为hwr.local.letter,多字识别为hwr.local.freestylus * @return 返回配置串 */ private String getHwrSessionParam(String hwrCapkey) { HwrConfig hwrConfig = new HwrConfig(); hwrConfig.addParam(HwrConfig.SessionConfig.PARAM_KEY_CAP_KEY, hwrCapkey); //设置识别结果的候选个数 hwrConfig.addParam(HwrConfig.ResultConfig.PARAM_KEY_CAND_NUM, "10"); //设置识别结果的范围 hwrConfig.addParam(HwrConfig.ResultConfig.PARAM_KEY_RECOG_RANGE, "gbk"); return hwrConfig.getStringConfig(); } /** * 获取手写的初始化配置参数 * @param context 上下文 * @param initCapkeys 使用的capkey,手写单字识别为hwr.local.letter, * 多字识别为hwr.local.freestylus, * 联想功能为hwr.local.associateword * 可以设置多个,中间以分号隔开 * @return 返回配置串 */ private String getHwrInitParam(Context context, String initCapkeys) { HwrInitParam hwrInitParam = new HwrInitParam(); hwrInitParam.addParam(HwrInitParam.PARAM_KEY_INIT_CAP_KEYS, initCapkeys); hwrInitParam.addParam(HwrInitParam.PARAM_KEY_FILE_FLAG, "android_so"); String dataPath = context.getFilesDir().getAbsolutePath().replace("files", "lib"); hwrInitParam.addParam(HwrInitParam.PARAM_KEY_DATA_PATH, dataPath); return hwrInitParam.getStringConfig(); } /** * 手写反初始化功能 * @return 返回0为成功,其他为失败 */ public int releaseHwr(){ return HciCloudHwr.hciHwrRelease(); } }在MainActivity中使用手写识别的功能
package com.miao.hwrtest; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.PorterDuff; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.Window; import android.widget.EditText; import android.widget.Toast; import com.miao.util.ConfigUtil; import com.miao.util.HciCloudHwrHelper; import com.miao.util.HciCloudSysHelper; import com.sinovoice.hcicloudsdk.common.HciErrorCode; public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{ private static final int MAX_POINT = 2048; private SurfaceHolder surfaceHolder; private SurfaceView surface; private Paint paint; private Path path; private int top; private int bottom; private boolean isRunning = true; private Canvas canvas; private String TAG = MainActivity.class.getSimpleName(); private boolean start = true; private long init; private long now; private int mCurX, mCurY; private int mCurIndex; private short[] mPoints = null; private boolean mEnd; private HciCloudSysHelper mHciCloudSysHelper; private HciCloudHwrHelper mHciCloudHwrHelper; private EditText myEditText; private Handler myHandler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.arg1) { case 0: Bundle bundle = msg.getData(); String result = bundle.getString("result"); myEditText.setText(result); break; default: break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); initView(); initSinovoice(); } private void initView() { surface = (SurfaceView) findViewById(R.id.surface); myEditText = (EditText) findViewById(R.id.text); surfaceHolder = surface.getHolder(); // 获得SurfaceHolder对象 surface.setZOrderOnTop(true); //使surface可见 surfaceHolder.setFormat(PixelFormat.TRANSPARENT); //设置背景透明 surfaceHolder.addCallback(this); // 为SurfaceView添加状态监听 paint = new Paint(); // 创建一个画笔对象 path = new Path(); mPoints = new short[MAX_POINT * 2]; mCurIndex = 0; } /** * 灵云系统初始化 */ private void initSinovoice() { mHciCloudSysHelper = HciCloudSysHelper.getInstance(); mHciCloudHwrHelper = HciCloudHwrHelper.getInstance(); int errorCode = mHciCloudSysHelper.init(this); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Toast.makeText(this, "系统初始化失败,错误码=" + errorCode, Toast.LENGTH_SHORT).show(); return; } errorCode = mHciCloudHwrHelper.initHwr(this, ConfigUtil.CAP_KEY_HWR_LOCAL_LETTER); if (errorCode != HciErrorCode.HCI_ERR_NONE) { Toast.makeText(this, "手写初始化失败,错误码=" + errorCode, Toast.LENGTH_SHORT).show(); return ; } } /** * 把坐标添加到short数组 * @param x * @param y * @return */ private boolean addStroke(short x, short y) { if (mCurX >= 0 && mCurY >= 0) { if ((mCurIndex / 2) < (MAX_POINT - 2)) { mPoints[mCurIndex] = x; mCurIndex++; mPoints[mCurIndex] = y; mCurIndex++; return true; } else if ((mCurIndex / 2) == (MAX_POINT - 2)) { mPoints[mCurIndex] = -1; mCurIndex++; mPoints[mCurIndex] = 0; mCurIndex++; return true; } } return false; } /** * 最后一笔添加坐标(-1,-1) */ public void addLastStrokePoint() { if(mCurIndex < 2 ||(mPoints[mCurIndex-1] == -1 && mPoints[mCurIndex-2] == -1)) { return; } mPoints[mCurIndex] = -1; mCurIndex++; mPoints[mCurIndex] = -1; mCurIndex++; } /** * 每次抬笔添加坐标(-1,0) */ private void addStrokeEnd() { mPoints[mCurIndex] = -1; mCurIndex++; mPoints[mCurIndex] = 0; mCurIndex++; } /** * 重置笔迹点数据 */ private void resetStroke() { mPoints = new short[MAX_POINT * 2]; mCurIndex = 0; } /** * 获取笔迹点数组 * @return 笔迹点数组 */ public short[] getStroke() { mEnd = true; addStrokeEnd(); addLastStrokePoint(); short[] stroke = new short[mCurIndex]; System.arraycopy(mPoints, 0, stroke, 0, mCurIndex); return stroke; } /** * 当SurfaceView创建的时候,调用此函数 * @param holder */ @Override public void surfaceCreated(SurfaceHolder holder) { int[] location = new int[2]; surface.getLocationOnScreen(location); top = location[1]; Log.d(TAG, "top = " + top); bottom = top + surface.getHeight(); Log.d(TAG, "bottom = " + bottom); //开启一个绘画线程 new Thread(wlineThread).start(); } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getY() >= top && event.getY() <= bottom) { start = true; init = now; now = System.currentTimeMillis(); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (now - init >= 100 && now - init <= 1000) { //抬笔操作,加上(-1,0) // Log.d(TAG, "X坐标=" + "-1" + "\tY坐标=" + "0"); addStrokeEnd(); } path.moveTo(event.getX(), event.getY() - top); // Log.d(TAG, "X坐标=" + event.getX() + "\tY坐标=" + event.getY()); // addStroke((short) event.getX(), (short) event.getY()); break; case MotionEvent.ACTION_MOVE: path.lineTo(event.getX(), event.getY() - top); // Log.d(TAG, "X坐标=" + event.getX() + "\tY坐标=" + event.getY()); addStroke((short) event.getX(), (short) event.getY()); break; default: break; } } return true; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { isRunning = false; } return super.onKeyDown(keyCode, event); } /** * 当SurfaceView的视图发生改变的时候,调用此函数 * @param holder * @param format * @param width * @param height */ @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * 当SurfaceView销毁的时候,调用此函数 * @param holder */ @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "surface Destroyed"); isRunning = false; } Runnable wlineThread =new Runnable(){ @Override public void run() { while (isRunning) { drawView(); if (start) { long temp = System.currentTimeMillis() - now; // Log.d(TAG, "temp=" + temp); if (temp > 1000 && temp < 100000) { //抬笔时间超过1秒,加上坐标(-1,-1),过滤第一次时间的计算 // Log.d(TAG, "X坐标=" + "-1" + "\tY坐标=" + "-1"); short[] data = getStroke(); String result = mHciCloudHwrHelper.recog(data, ConfigUtil.CAP_KEY_HWR_LOCAL_LETTER); Message message = new Message(); message.arg1 = 0; Bundle bundle = new Bundle(); bundle.putString("result", result); message.setData(bundle); myHandler.sendMessage(message); start = false; clearCanvas(); } } } } }; /** * 清空Canvas上的笔迹 */ private void clearCanvas() { for (int i = 0; i < 4; i++) { try { if (surfaceHolder != null) { canvas = surfaceHolder.lockCanvas(); canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); path.reset(); resetStroke(); mEnd = false; } } catch (Exception e) { e.printStackTrace(); } finally { if (canvas != null){ surfaceHolder.unlockCanvasAndPost(canvas); } } } } /** * 再Canvas上画 */ private void drawView() { try { if (surfaceHolder != null) { canvas = surfaceHolder.lockCanvas(); canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG)); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(5); canvas.drawPath(path, paint); } } catch (Exception e) { e.printStackTrace(); } finally { if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } } @Override protected void onDestroy() { releaseSinovoice(); super.onDestroy(); } /** * 灵云系统反初始化 */ private void releaseSinovoice() { if (mHciCloudHwrHelper != null) { mHciCloudHwrHelper.releaseHwr(); } if (mHciCloudSysHelper != null) { mHciCloudSysHelper.release(); } } }注:灵云的离线手写识别能力,第一次使用的时候也是需要有一个联网授权的过程,授权成功以后,即可在授权期内使用离线手写能力。