准备工作:

1.下载OpenCV:https://opencv.org/releases/
2.添加tess-two依赖:
建议直接在app的build.gradle下添加tess-two依赖库就可以了:

implementation 'com.rmtheis:tess-two:9.1.0'

最新版本:tess-two
也可以通过以上网址下载自行加入

3.识别库:
用到两个,一个是chi_sim 代表中文,一个是eng代表英文,资源中assets下没有识别库,需要自己添加chi_sim和eng。
识别库下载:识别库
4.图片压缩:

implementation 'top.zibin:Luban:1.1.3'

OpenCV图像处理:

 /*** 图像处理* 二值化* 腐蚀*/public void proSrc2Gray() {Mat rgbMat = new Mat();Mat grayMat = new Mat();Mat binaryMat = new Mat();Mat cannyMat = new Mat();//获取彩色图像所对应的像素数据Utils.bitmapToMat(srcBitmap, rgbMat);//图像灰度化,将彩色图像数据转换为灰度图像数据并存储到grayMat中Imgproc.cvtColor(rgbMat, grayMat, Imgproc.COLOR_RGB2GRAY);//得到边缘图,这里最后两个参数控制着选择边缘的阀值上限和下限
//        Imgproc.Canny(grayMat, cannyMat, 50, 300);//二值化 ADAPTIVE_THRESH_MEAN_C    THRESH_BINARYImgproc.threshold(grayMat, binaryMat, Imgproc.THRESH_BINARY_INV, 255, 7);//获取自定义核,参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的Mat strElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT,new Size(2, 2));//腐蚀Imgproc.erode(binaryMat,cannyMat,strElement);Imgproc.dilate(binaryMat,cannyMat,strElement);//Hough变换倾斜校正Imgproc.HoughLinesP(binaryMat,cannyMat,1,3.14/180,1);//创建一个图像mBitmap = Bitmap.createBitmap(grayMat.cols(), grayMat.rows(),Bitmap.Config.RGB_565);//将矩阵binaryMat转换为图像Utils.matToBitmap(grayMat, mBitmap);}

文字识别代码:

/*** 识别图像* @param activity* @param bitmap*/public void recognition(final Activity activity, final Bitmap bitmap) {new Thread(new Runnable() {@Overridepublic void run() {/*** 检测sd卡是否存在语言库* 若不存在,从assets获取到本地sd卡*/if (!checkTrainedDataExists()) {SDUtils.assets2SD(activity.getApplicationContext(), PathUtils.LANGUAGE_PATH, PathUtils.DEFAULT_LANGUAGE_NAME);}TessBaseAPI tessBaseAPI = new TessBaseAPI();tessBaseAPI.setDebug(true);tessBaseAPI.init(PathUtils.DATAPATH, PathUtils.DEFAULT_LANGUAGE);//识别的图片tessBaseAPI.setImage(bitmap);//获得识别后的字符串String text = "";text = "识别结果:" + "\n" + tessBaseAPI.getUTF8Text();final String finalText = text;tessBaseAPI.end();}}).start();}

以上文字识别提取的过程就完成了。
接下来就从手机相册获取照片或者拍照,然后压缩处理,识别文字。

获取图片:

权限:
Android6.0+需要动态申请权限
AndroidManifest中:

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 创建/删除文件的权限 --><uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"tools:ignore="ProtectedPermissions" /><uses-permission android:name="android.permission.CAMERA" />

java代码中:

   /*** 申请权限*/private void requestPermissions() {if (Build.VERSION.SDK_INT >= 23) {if ((ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) &&(ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) {ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, 1);}}}

由于本人比较懒,所以直接“取消严格模式”来获取图片,为什么要“取消严格模式”我就不多说了,可以自行百度。

/*** 取消严格模式 FileProvider*/private void CancelFileProvider(){if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();StrictMode.setVmPolicy( builder.build() );}}

从相册获取图片:
打开相册:

/*** 打开相册* @param activity*/public void openAlbum(Activity activity) {Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.addCategory(Intent.CATEGORY_OPENABLE);intent.setType("image/*");activity.startActivityForResult(intent, MainActivity.PICK_PHOTO);}

拿到照片裁剪:

/*** 从相册获取照片返回,裁剪* @param activity*/public void PickPhotoResult(Activity activity){Intent intent = new Intent("com.android.camera.action.CROP");intent.setDataAndType(MainActivity.imageUri, "image/*");//intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);intent.putExtra("crop", true);intent.putExtra(MediaStore.EXTRA_OUTPUT, MainActivity.imageUri);activity.startActivityForResult(intent, MainActivity.CROP_PHOTO); // 启动裁剪程序}

裁剪:

/*** 裁剪照片* @param activity*/public void CropPhotoResult(Activity activity){try {OpenCVUtils.getInstance().srcBitmap = BitmapFactory.decodeStream(activity.getContentResolver().openInputStream(MainActivity.imageUri));OpenCVUtils.getInstance().proSrc2Gray();activity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(ImageUtils.getInstance().createImageFile())));if (ImageUtils.getInstance().mBitmap != null) {ImageUtils.getInstance().showPicFileByLuban(activity);MainActivity.imgView.setImageBitmap(ImageUtils.getInstance().mBitmap); // 将裁剪后的照片显示出来MainActivity.imgView.setVisibility(View.VISIBLE);}} catch (FileNotFoundException e) {e.printStackTrace();}catch (IOException e){e.printStackTrace();}}

执行startActivityForResult后的回调函数

 @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (resultCode == RESULT_OK) {if (requestCode == PICK_PHOTO) {//将content类型的Uri转化为文件类型的UriimageUri = ImageUtils.getInstance().convertUri(this,data.getData());CameraUtils.getInstance().PickPhotoResult(this);} else if (requestCode == TAKE_PHOTO) {CameraUtils.getInstance().TakePhotoResult(this);} else if (requestCode == CROP_PHOTO) {CameraUtils.getInstance().CropPhotoResult(this);}}}

图像裁剪后,会得到一个灰度化的图像,如果直接识别拍照的图片耗费时间很长,所以我在这对裁剪后的图片进行了压缩处理。

/*** 压缩图片* @param activity*/public void showPicFileByLuban(final Activity activity) {try {Luban.with(activity).load(new File(createImageFile().getPath())).setCompressListener(new OnCompressListener() {@Overridepublic void onStart() {// TODO 压缩开始前调用,可以在方法内启动 loading UI}@Overridepublic void onSuccess(File file) {// TODO 压缩成功后调用,返回压缩后的图片文件mBitmap = BitmapFactory.decodeFile(file.getPath());Toast.makeText(activity,file.length() / 1024 + "K",Toast.LENGTH_LONG).show();}@Overridepublic void onError(Throwable e) {// TODO 当压缩过去出现问题时调用}}).launch();//启动压缩} catch (IOException e) {e.printStackTrace();}}

保存图片:

/*** 将content类型的Uri转化为文件类型的Uri* @param activity* @param uri* @return*/public Uri convertUri(Activity activity,Uri uri){InputStream is;try {//Uri ----> InputStreamis = activity.getContentResolver().openInputStream(uri);//InputStream ----> BitmapBitmap bm = BitmapFactory.decodeStream(is);//关闭流is.close();return ImageUtils.getInstance().saveBitmap(bm);} catch (FileNotFoundException e) {e.printStackTrace();return null;} catch (IOException e) {e.printStackTrace();return null;}}/*** 将Bitmap写入SD卡中的一个文件中* 并返回写入文件的Uri* @param bm* @return*/public Uri saveBitmap(Bitmap bm) {//新建文件夹用于存放裁剪后的图片File appDir = new File(PathUtils.DATAPATH + "/DCIM/Camera/");if (!appDir.exists()) {appDir.mkdirs();}try {File file = createImageFile();//打开文件输出流FileOutputStream fos = new FileOutputStream(file);//将bitmap压缩后写入输出流(参数依次为图片格式、图片质量和输出流)bm.compress(Bitmap.CompressFormat.JPEG, 100, fos);//刷新输出流fos.flush();//关闭输出流fos.close();//返回File类型的Urireturn Uri.fromFile(file);} catch (FileNotFoundException e) {e.printStackTrace();return null;} catch (IOException e) {e.printStackTrace();return null;}}

调用相机拍照:

/*** 启动相机* @param activity*/public void openCamera(Activity activity) {try {MainActivity.imageUri = Uri.fromFile(ImageUtils.getInstance().createImageFile());} catch (IOException e) {e.printStackTrace();}Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//传递你要保存的图片的路径intent.putExtra(MediaStore.EXTRA_OUTPUT, MainActivity.imageUri);activity.startActivityForResult(intent, MainActivity.TAKE_PHOTO);}

裁剪的方法和从相册获取图片的一样!!

工具类:
将assets中的识别库复制到SD卡中

public class SDUtils {/*** 将assets中的识别库复制到SD卡中** @param path 要存放在SD卡中的 完整的文件名。这里是"/storage/emulated/0/tessdata/chi_sim.traineddata"* @param name assets中的文件名 这里是 "chi_sim.traineddata"*/public static void assets2SD(Context context, 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 {//打开assets文件获得一个InputStream字节输入流is = context.getAssets().open(name);File file = new File(path);// 创建一个向指定 File 对象表示的文件中写入数据的文件输出流os = new FileOutputStream(file);byte[] bytes = new byte[4096];int len = 0;//从输入流中读取一定数量的字节,并将其存储在缓冲区数组bytes中//如果因为流位于文件末尾而没有可用的字节,则返回值-1while ((len = is.read(bytes)) != -1) {//将指定byte数组中从偏移量off开始的len个字节写入此缓冲的输出流os.write(bytes, 0, len);}//java在使用流时,都会有一个缓冲区,按一种它认为比较高效的方法来发数据:把要发的数据先放到缓冲区,//缓冲区放满以后再一次性发过去,而不是分开一次一次地发//flush()强制将缓冲区中的数据发送出去,不必等到缓冲区满os.flush();} catch (IOException e) {e.printStackTrace();} finally {try {//关闭输入流和输出流if (is != null)is.close();if (os != null)os.close();} catch (IOException e) {e.printStackTrace();}}}
}

保存的路径:

public class PathUtils {//TessBaseAPI初始化用到的第一个参数,是个目录public static final String DATAPATH = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator;//在DATAPATH中新建这个目录,TessBaseAPI初始化要求必须有这个目录public static final String tessdata = DATAPATH + File.separator + "tessdata";//TessBaseAPI初始化测第二个参数,就是识别库的名字不要后缀名。public static String DEFAULT_LANGUAGE = "chi_sim";//assets中的文件名public static String DEFAULT_LANGUAGE_NAME = DEFAULT_LANGUAGE + ".traineddata";//保存到SD卡中的完整文件名public static String LANGUAGE_PATH = tessdata + File.separator + DEFAULT_LANGUAGE_NAME;}

最后,识别按钮操作:

         case R.id.btn_rec:if (imgView.getVisibility() != View.VISIBLE) {Toast.makeText(getApplicationContext(), "请先拍照或者选一张图片", Toast.LENGTH_SHORT).show();return;} else {resultTv.setText("");txtFinal.setText("");try {ImageUtils.getInstance().mBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));} catch (Exception e) {e.printStackTrace();}OpenCVUtils.getInstance().recognition(this,ImageUtils.getInstance().mBitmap);}break;

完整demo下载
参考:https://blog.csdn.net/qq_35820350/article/details/78802276

感谢博主的分享!!

Android OpenCV实现文字识别相关推荐

  1. Android Studio实现文字识别(基于百度云OCR)

    前言:之前配置好Android Studio后,一直在参与课程的两个项目,未能及时更新,最近期末考试,可能又要断更一段时间.今天验收好,跟大家分享一下其中我负责的相机相册调用以及文字识别的部分,大家根 ...

  2. OpenCV+OCR文字识别

    需配置好OpenCV和OCR环境下运行 1.opencv简介 OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库. OpenCV用C ...

  3. opencv ocr文字识别_用OpenCV和OCR识别图片中的表格数据

    ♚ 作者: jclian,喜欢算法,热爱分享,希望能结交更多志同道合的朋友,一起在学习Python的道路上走得更远!   在很多时候,我们的数据来源形式是多种多样的,有时候数据(或表格)也会呈现在图片 ...

  4. Android 集成百度文字识别OCR身份证银行卡驾驶证识别

    SDK提供了下列百度AI开放平台RESTful接口的封装.文字识别的服务,可实现一些通用文字,网络图片文字,身份证,银行卡,驾驶证,行驶证,车牌,营业执照,通用票据等的识别需求,简化输入操作. 本篇主 ...

  5. android opencv NDK人脸识别和对比

    } env->ReleaseStringUTFChars(name, filePath); return 0; } 人脸对比 /** *人脸对比 */ extern "C" ...

  6. android opencv单机版人脸识别+比对

    原理: 通过android 系统自带的谷歌人脸识别获取响应的图片,保存在本地,然后跟先前的照片作比较,相似度大于0.8可以算为同一个人 java 部分代码: public class FaceRecA ...

  7. Android 百度文字识别(详细步骤+源码)

    运行效果图 识别到的内容: {"words_result":[{"words":"突然间有想看书的冲动"},{"words&quo ...

  8. opencv文字识别

    OpenCV(开源计算机视觉库)是一个用于实现计算机视觉和机器学习的开源库.它包含了许多预先训练的模型和算法,可以帮助开发者快速实现图像处理.对象检测和识别等功能.在文字识别方面,OpenCV也有一些 ...

  9. 基于opencv的场景文字识别

    理论基础:基于论文 "Real-Time Scene Text Localization and Recognition". 项目实现:opencv3.0 + tesseract, ...

最新文章

  1. 使用Spring-Cloud将Spring Boot应用程序部署到Cloud Foundry
  2. Java 8中的instanceof运算符和访客模式替换
  3. 1997年投稿,2021年发表!收到录用信那一刻,我即将退休……
  4. 安卓智能手机完全装机手册,让安卓拥有无限可能!
  5. 同旭堂同湿散真实效果,七大真实效果让你彻底放心
  6. unison+inotify
  7. 电脑提醒没有权限在此位置保存文件怎么办?
  8. 解决电脑软件可以上网,但是浏览器无法上网的问题
  9. 【中学】求解一元二次方程
  10. java怎么实现手机投影,在Java中投射对象
  11. 支付宝小程序生态服务商奖励发布,单个商家最高奖励5000元
  12. ThinkPHP6 API接管异常
  13. 树莓派安装AdGuard Home
  14. c语言 运行出现错误代码,运行出现 debug error
  15. 单因素试验的方差分析及其MATLAB实现
  16. Tms320c6678——多核导航模块(Multicore Navigator)
  17. Deepin系统下载安装LibreOffice6.4.1
  18. 【Python计量】Logit模型
  19. AI时代大点兵——国内外知名AI公司2018年最新盘点【完整版】
  20. 脚本引流靠谱吗,脚本引流话术连骗子都不如,你怎么引流

热门文章

  1. 这是一篇让你从早吃到晚的『台南美食攻略』
  2. 中小企业RBM结合VRRP组网
  3. 未能找到引用的组件“Microsoft.Office.Core”
  4. Java课程实验报告 实验四 Java网络编程及安全
  5. 【MySQL】字符集utf8mb4无法存储表情踩坑记录
  6. 运用人性,七情六欲聊运营
  7. 日内因子:开盘缺口探索
  8. 小程序-仿朋友圈开发问题
  9. fpu测试_浮点运算单元FPU能给电机控制带来什么?
  10. iphone邮箱看不到已发送_不看不知道 教你如何设置iPhone邮箱