本篇博客是有了实时扫描识别的需求,然后看到了一篇博客,在其基础上按项目需求改进而成。感谢Si-Kang的贡献,基于交流进步的想法这里把我处理的一些问题写出来
转载地址:http://blog.youkuaiyun.com/mr_sk/article/details/72877492#comments
本篇博客Demo地址:http://download.youkuaiyun.com/detail/g_ying_jie/9883095
第一步集成:
compile 'com.rmtheis:tess-two:7.0.0'
第二步:下载tess-two的中文包,Demo中已经包含了。
第三步:声明权限,记得安卓6.0动态权限申请:
<uses-permission android:name="android.permission.CAMERA" />
<!-- 声明使用Camera意图 -->
<uses-feature android:name="android.hardware.camera" />
<!-- 声明调用Camera自动对焦功能 -->
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
第四步:获取读写权限后把字库从项目的assets目录复制到手机存储中,注意存储的路径一定要包含tessdata目录,因为在TessBaseAPI初始化的时候回去查找该目录
/**
* datapath是你传入的文字库路径,可以看到这里在传入的datapath后加了一个"tessdata"目录
* 然后验证了这个目录是否存在,如果不在,就会报错"数据目录必须包含tessdata目录"
*/
File tessdata = new File(datapath + "tessdata");
//tessdata是否存在且是个目录
if (!tessdata.exists() || !tessdata.isDirectory())
throw new IllegalArgumentException("Data path must contain subfolder tessdata!");
/**
* 将assets中的识别库复制到SD卡中
*
* @param path 要存放在SD卡中的 完整的文件名。这里是"/storage/emulated/0//tessdata/chi_sim.traineddata"
* @param name assets中的文件名 这里是 "chi_sim.traineddata"
*/
public void copyToSD(String path, String name) {
//如果存在就删掉
File f = new File(path);
if (f.exists()) {
f.delete();
}
if (!f.exists()) {
File p = new File(f.getParent());
if (!p.exists()) {
p.mkdirs();
}
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
InputStream is = null;
OutputStream os = null;
try {
is = this.getAssets().open(name);
File file = new File(path);
os = new FileOutputStream(file);
byte[] bytes = new byte[2048];
int len = 0;
while ((len = is.read(bytes)) != -1) {
os.write(bytes, 0, len);
}
os.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null)
is.close();
if (os != null)
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
第五步:利用surfaceview和camera实时预览画面,通过onPreviewFrame回调预览的帧画面,然后转化成bitmap传到TessBaseAPI进行异步识别。详细的流程请看Demo中的CameraView类。这里讲下我碰到的一些问题。
①测试需要,我想把实时预览的图片和解析后的内容通过Imageview和TextView展示出来,那么我的在CameraView中拿到CameraActivity的两个子控件,这里采用了CameraView的setTag方法,很多人可能都知道怎么去传一个,那怎么传递多个呢?
首先在strings文件加入两个key
<resources>
<string name="app_name">TestTessTwo</string>
<item name="tag_img" type="id"></item>
<item name="tag_text" type="id"></item>
</resources>
然后通过下面的方式setTag
cameraView.setTag(R.id.tag_text, tv_content);
cameraView.setTag(R.id.tag_img, img);
最后通过key获取对应的控件
imageView = (ImageView) getTag(R.id.tag_img);
textView = (TextView) getTag(R.id.tag_text);
②扫描动画TranslateAnimation的执行非常卡顿,主要是由于setPreviewCallback实时回调onPreviewFrame消耗了大量资源,并且onPreviewFrame的裁剪图片和解析内容过于耗时导致。
首先对于onPreviewFrame的图片及识别处理应该全部放到子线程中执行,可通过handler的方式回调子线程结果然后在主线程作相应处理。
然后setPreviewCallback以setPreviewCallbackWithBuffer替代,避免camera预览不断地开辟和回收内存。注意该方法要结合addCallbackBuffer使用,不然会出现只回调一次onPreviewFrame的情况,具体使用如下
开始预览前的处理,screenWidth和screenHeight是你确定的预览尺寸。可通过camera.getParameters().getPreviewSize();获取
mCamera.addCallbackBuffer(new byte[((screenWidth * screenHeight) * ImageFormat.getBitsPerPixel(ImageFormat.NV21)) / 8]);
mCamera.setPreviewCallbackWithBuffer(this);
mCamera.setPreviewDisplay(mHolder);//set the surface to be used for live preview
mCamera.startPreview();
然后在onPreviewFrame中添加camera.addCallbackBuffer(data);
/**
* Camera帧数据回调用
*/
@Override
public void onPreviewFrame(final byte[] data, final Camera camera) {
camera.addCallbackBuffer(data);
new Thread(new Runnable() {
@Override
public void run() {
好了就谈这么多吧,具体的运用在Demo中都有,有什么问题欢迎在评论留言讨论。