前言

  OCR 是 Optical Character Recognition 的缩写,翻译为光学字符识别,指的是针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,通过识别软件将图像中的文字转换成文本格式,供文字处理软件进一步编辑加工的技术(好吧,这是我查来的)。简单的来说,OCR技术就是可以把图片上的文字识别出来,并以文本格式的形式提取出来。

  这个技术的应用方面很广泛,比如说把纸质书籍的内容转化为电子书,之前都需要人手打,但是现在只要扫描一下,将扫描出来的图片通过OCR技术转化成文本格式,效率和成本不知提升了多少倍。

  有人可能会想,这个技术听起来好高端,不懂计算机图形学,模式识别,机器学习巴拉巴拉东西的人,是不是无法接触到这样的技术,实现这样的功能,(好像是的,emmm…),不过,虽然咱们自己实现不了,但是有人把轮子造好了呀,我们只要使用人家造好的轮子,就能实现图片文字识别的功能。

  先来看一下我实现的效果吧,随手拿了办公桌上有字的东西(拿了枸杞茶和菊花茶的包装…),用自己写的demo拍了照识别了一下


  界面下方显示的图片是手机拍的照片,界面上方显示的是从照片中识别出来的文字信息。可以看出识别的正确率还是挺高的。

  我这边用的轮子是 百度文字识别 。下面我将介绍一下我是如何实现上述的文字识别功能。

请求模块定义

  百度其实有提供图片识别Android的SDK,就像其他的SDK一样,只要导入一系列包之后就可以调用识别。寻求快速开发的小伙伴可以了解一下,我看了一下文档,实现还是十分容易的。
  但是,我在demo中使用的并非是SDK,而是使用另外一种方法——以网络api的方式来进行识别。涉及到的技术有 retrofit+rxjava 进行网络请求(在之前的一篇博客中有介绍如何使用 retrofit+rxjava ,贴一下链接),Android应用动态权限的申请,FileProvider,图片的base64转码,以及热门的MVP框架。

  先看一下项目结构
  module目录下存放的是MVP架构的三个模块,bean目录下存放的是网络请求返回的数据类型,apiservice中存放的是retrofit有关网络请求的接口。
  根据百度OCR官方给出的接口
  我们定义出如下的接口方法。

 /*** 通过图片URL的形式,获取图片内的文字信息* @param accessToken 通过API Key和Secret Key获取的access_token* @param url 图片的url* @return observable对象用于rxjava,从RecognitionResultBean中可以获得图片文字识别的信息*/@POST("rest/2.0/ocr/v1/general_basic")@FormUrlEncodedObservable<RecognitionResultBean> getRecognitionResultByUrl(@Field("access_token") String accessToken, @Field("url") String url);/*** 通过图片,获取图片内的文字信息* @param accessToken 通过API Key和Secret Key获取的access_token* @param image 图像数据base64编码后进行urlencode后的String* @return  observable对象用于rxjava,从RecognitionResultBean中可以获得图片文字识别的信息*/@POST("rest/2.0/ocr/v1/general_basic")@FormUrlEncodedObservable<RecognitionResultBean> getRecognitionResultByImage(@Field("access_token") String accessToken, @Field("image") String image);

  在第一个方法中,我们需要传入两个参数,一个是access_token,需申请百度文字识别的开发者资格,得到API key和Secret Key后获取,还有一个参数是图片的网络地址url,可以直接通过这个url直接访问到图片。

  第二个方法中,第一个参数也是同上的access_token,第二个参数则是String类型,这个参数是在本地将图片base64转码之后生成。

  因为我们要实现的功能是用手机拍照,然后将照片信息传递给服务器,因此我们之后调用的是第二个方法(第一个方法只是我按照api说明随手写了一下),这些参数我们以POST的形式发送,按照百度OCRapi 的要求,需要加上@FormUrlEncode注释,我们使用@Field的方式将参数加入请求体。可以看到Observable中的是RecognitionResultBean类型,我们可以从里面拿到服务器返回的文字识别信息。

  定义好这两个方法之后,我们便可以构造retrofit对象进行调用

 Retrofit retrofit = new Retrofit.Builder().baseUrl("https://aip.baidubce.com/").addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();baiduOCRService = retrofit.create(BaiduOCRService.class);

  我们来看一下 rxjava+retrofit 在接口方法中的具体实现

  @Overridepublic void getRecognitionResultByImage(Bitmap bitmap) {String encodeResult = bitmapToString(bitmap);baiduOCRService.getRecognitionResultByImage(ACCESS_TOKEN,encodeResult).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<RecognitionResultBean>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(RecognitionResultBean recognitionResultBean) {Log.e("onnext",recognitionResultBean.toString());StringBuilder s = new StringBuilder();List<RecognitionResultBean.WordsResultBean> wordsResult = recognitionResultBean.getWords_result();for (RecognitionResultBean.WordsResultBean words:wordsResult) {s.append(words.getWords());}mView.updateUI(s.toString());}@Overridepublic void onError(Throwable e) {Log.e("onerror",e.toString());}@Overridepublic void onComplete() {}});}

  可以看到传入了一个Bitmap类型的图片参数,这个参数经过 String encodeResult = bitmapToString(bitmap); 方法转成了String类型。是因为接口要求的参数数据为String类型,所以我们对图片进行了base64转码,具体的转码方法如下:

    private String bitmapToString(Bitmap bitmap){ByteArrayOutputStream baos = new ByteArrayOutputStream();bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);byte[] bytes = baos.toByteArray();return Base64.encodeToString(bytes, Base64.DEFAULT);}

  调用此方法,便可以把图片类型转化成字符串类型,之后的操作便是对网路接口调用之后的回调方法进行定义,我们在调用成功后的onNext操作中,拿到了RecognitionResultBean类型参数,这个参数里含有图片所包含文字的信息,我们将所有的文字一一取出,用StringBuilder连接成一个字符串,返回给View层,调用View层的updateUI进行UI界面的更新,对于这个字符串我们在之后还可以进行进一步的分析操作。

  以上,百度OCR接口请求模块定义部分便已完成,接下来,我们要做的就是调用系统的相机功能,拍照得到照片,将照片传递给我们上面定义的请求接口,进行文字识别。

相机功能调用

  首先,由于要对相机功能进行调用,我们需要在AndroidManifest清单文件中写明我们需要用到的权限

    <uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.CAMERA"/>

  分别是网络请求权限,数据的读存取权限,以及相机权限。在Android 6.0 之前应用的权限在安装时全部授予,也就是说只要在AndroidManifest中申请过的权限,都会给予。而在 Android 6.0 或更高版本之后,对权限的管理作出了改变,对某些涉及到用户隐私的权限可在运行时根据用户的需要动态授予,也就是说,在AndroidManifest中申请的权限,在用户使用的过程中还得询问用户是否给予,用户给予权限了,应用才能进行相关的权限操作。因此我们需在代码中增加动态权限申请的模块(对用户安全性友好了,但是对开发者增加了不友好度….),以下是动态权限申请部分的代码:

    private boolean hasPermission() {if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED|| 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.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, PERMISSIONS_REQUEST_CODE);return false;}else {return true;}}

  代码的主要逻辑是,在程序运行的时候,检查是否有相应的权限,如果有权限,则可以进行相关操作,如果没有权限,就调用申请权限的方法。在完成这部分代码的编写之后还需重写onRequestPermissionsResult方法,对申请权限的结果进行反应。

  接下来是调用相机功能的代码

 private void takePhoto(){if (!hasPermission()) {return;}Intent intent = new Intent();intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/img";if (new File(path).exists()) {try {new File(path).createNewFile();} catch (IOException e) {e.printStackTrace();}}String filename = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());mTmpFile = new File(path, filename + ".jpg");mTmpFile.getParentFile().mkdirs();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {String authority = getPackageName() + ".provider";imageUri = FileProvider.getUriForFile(this, authority, mTmpFile);} else {imageUri = Uri.fromFile(mTmpFile);}intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);startActivityForResult(intent, CAMERA_REQUEST_CODE);}

  需要提到的是,在Android 7.0之后,如果你使用Intent携带这样的上面的imageUri去打开相机拍照,会抛出FileUriExposedException异常。这时候就需要用到google官方的解决方案——FileProvider。使用的方法可以参照 Android 7.0适配-应用之间共享文件

  调用相机之后我们需要重写onActivityResult方法,对返回拍照结果进行处理,

    @Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == CAMERA_REQUEST_CODE) {if (requestCode == RESULT_OK){Bitmap photo = BitmapFactory.decodeFile(mTmpFile.getAbsolutePath());mPresenter.getRecognitionResultByImage(photo);imageView.setImageBitmap(photo);}}}

  如果拍照成功,我们就把照片作为参数传递给之前定义好的接口方法,调用进行图片文字识别。可以看到我还把照片放入imageview中方便与识别结果进行对比。等服务器成功返回识别结构之后,就会调用VIew层的updateUI,更新textview显示识别结果。

  至此,我们就完成了从拍照到拿到识别结果的全部功能。再来识别一下公交卡

  嗯,不错。

最后

  这个demo实现了单纯的图片文字识别,就是把图片上的字读取了出来,在此之上我们可以进一步做很多有意思的事,比如用正则表达式把字符串里的一些字提取出来进行操作,像食品的营养成分表啊,发票单子啊,都可以拿来识别,将里面的有用信息提取出来,进行分析处理操作,将功能进一步扩展。
  当然,在实现这些功能之后,我们最后还是得感谢为我们提供轮子的大佬,哈哈。
  贴上本项目的github地址 reggie1996/CharacterRecognition

Android 图片文字识别DEMO(基于百度OCR)相关推荐

  1. 百度 php 图片文字识别,PHP实现百度OCR文字识别

    OCR的百度定义 (Optical Character Recognition,光学字符识别)是指电子设备(例如扫描仪或数码相机)检查纸上打印的字符,通过检测暗.亮的模式确定其形状,然后用字符识别方法 ...

  2. Python3 图片文字识别翻译——调用百度AI、百度翻译和有道翻译的API

    文章目录 Python3 图片文字识别翻译--调用百度AI.百度翻译和有道翻译的API 一.演示 二. API准备 三. 图片文字识别--调用百度AI文字识别API 四. 文字翻译 1. 百度翻译 请 ...

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

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

  4. Android图片文字识别(阿里OCR接口)

    最近使用了阿里云的OCR文字识别API 先来看看效果 我使用的是通用类文字识别,具体实现过程如下: 1.购买阿里云的通用类文字识别 目前是0元免费的,可以使用500次.购买成功后到->控制台-& ...

  5. java调用ocr识别api_Java文字识别软件-调用百度ocr实现文字识别

    java_baidu_ocr Java调用百度OCR文字识别API实现图片文字识别软件 项目源代码在文末,放到了GitHub上 - https://github.com/Ymy214/java_bai ...

  6. java ocr文字识别软件_Java文字识别软件-调用百度ocr实现文字识别

    java_baidu_ocr Java调用百度OCR文字识别API实现图片文字识别软件 这是一款小巧方便,强大的文字识别软件,由Java编写,配上了窗口界面 调用了百度ocr文字识别API 识别精度高 ...

  7. 3测试图片显示置信度_告别腾讯百度图片文字识别API调用,OCR图片文字识别就用这条代码...

    上期文章我们分享了tesseract的基本安装,本期我们来分享一下如何使用python与tesseract进行代码的编程来实现tesseract的文字识别 在开始本期文章之前,请认真阅读一下小编前期分 ...

  8. tess_two Android图片文字识别

    原文地址 http://blog.csdn.net/qq_25806863/article/details/62432918 文字识别一般都用的tesseract-ocr. GitHub:https: ...

  9. 用Python实现最简单的文字识别:基于百度云文字识别API

    Python版本:3.6.5 百度云提供的文字识别技术,准确率还是非常高的,而且每天还有5w次免费的调用量,对于用来学习或者偶尔拿来用用,已经完全足够了.文章提供一个模板,稍加修改就可以直接套用.注释 ...

最新文章

  1. 【译】Yii2 0 高级模版编写使用自定义组件(component)
  2. json 数据 生成 图表_CAPP工艺图表 / 知识重用 快速编制
  3. 如何成为SAP行业专家
  4. lhgdialogV2.37 使用
  5. Unable to locate tools.jar
  6. [译]GLUT教程 - 笔划字体
  7. 专访商汤联合创始人林达华:商汤的开源战略,从算法做起
  8. 区块链现状:最初的炒作消退,将迎来大量新应用(第一部分)
  9. 【solitidy】生成随机数算法
  10. SQL Server 2005 如何自动备份数据库
  11. 【元胞自动机】基于matlab元胞自动机车流密度不变下的双向两车道仿真(T 字形路口)【含Matlab源码 1290期】
  12. 抖音代码舞Python制作
  13. Vue登录页面源代码分享
  14. 威纶通触摸屏上传错误_威伦触摸屏程序上传方式
  15. Java 小Q 世界上最遥远的距离 解法二
  16. 未来真的可以影响过去吗?人人都能看懂的波粒二象性和延迟选择量子擦除实验解释,通俗易懂量子物理
  17. VMware Workstation创建Windows 8虚拟机
  18. 删除顺序表中区间内的元素
  19. Mapbox之栅格矢量瓦片
  20. 51万年历林贤文:做一个不“安分”的程序员

热门文章

  1. [高通MSM8953_64][Android10]解决制作差分包不生成system_manifest.xml的问题
  2. 2020五四青年节 | 青年人在美团是怎样成长的?
  3. 如何加减单元格指定数字_如何把单元格的数值每位数字进行相加?又学会一个Excel技巧...
  4. 【CVE-2021-4043】Linux本地提权漏洞复现
  5. android 获取经纬度(百度地图)
  6. Windows、Unix、Mac不同操作系统的换行问题 回车符\r和换行符\n
  7. 不要抱怨自己的能力没有「用武之地」
  8. linux调度器并发,12 Go 并发调度器模型
  9. Android 小米应用角标
  10. 大脑状态的重构与认知行为之间的映射