本篇博客是有了实时扫描识别的需求,然后看到了一篇博客,在其基础上按项目需求改进而成。感谢Si-Kang的贡献,基于交流进步的想法这里把我处理的一些问题写出来
转载地址:http://blog.csdn.net/mr_sk/article/details/72877492#comments
本篇博客Demo地址:http://download.csdn.net/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中都有,有什么问题欢迎在评论留言讨论。