Android OpenCV实现文字识别
准备工作:
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实现文字识别相关推荐
- Android Studio实现文字识别(基于百度云OCR)
前言:之前配置好Android Studio后,一直在参与课程的两个项目,未能及时更新,最近期末考试,可能又要断更一段时间.今天验收好,跟大家分享一下其中我负责的相机相册调用以及文字识别的部分,大家根 ...
- OpenCV+OCR文字识别
需配置好OpenCV和OCR环境下运行 1.opencv简介 OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库. OpenCV用C ...
- opencv ocr文字识别_用OpenCV和OCR识别图片中的表格数据
♚ 作者: jclian,喜欢算法,热爱分享,希望能结交更多志同道合的朋友,一起在学习Python的道路上走得更远! 在很多时候,我们的数据来源形式是多种多样的,有时候数据(或表格)也会呈现在图片 ...
- Android 集成百度文字识别OCR身份证银行卡驾驶证识别
SDK提供了下列百度AI开放平台RESTful接口的封装.文字识别的服务,可实现一些通用文字,网络图片文字,身份证,银行卡,驾驶证,行驶证,车牌,营业执照,通用票据等的识别需求,简化输入操作. 本篇主 ...
- android opencv NDK人脸识别和对比
} env->ReleaseStringUTFChars(name, filePath); return 0; } 人脸对比 /** *人脸对比 */ extern "C" ...
- android opencv单机版人脸识别+比对
原理: 通过android 系统自带的谷歌人脸识别获取响应的图片,保存在本地,然后跟先前的照片作比较,相似度大于0.8可以算为同一个人 java 部分代码: public class FaceRecA ...
- Android 百度文字识别(详细步骤+源码)
运行效果图 识别到的内容: {"words_result":[{"words":"突然间有想看书的冲动"},{"words&quo ...
- opencv文字识别
OpenCV(开源计算机视觉库)是一个用于实现计算机视觉和机器学习的开源库.它包含了许多预先训练的模型和算法,可以帮助开发者快速实现图像处理.对象检测和识别等功能.在文字识别方面,OpenCV也有一些 ...
- 基于opencv的场景文字识别
理论基础:基于论文 "Real-Time Scene Text Localization and Recognition". 项目实现:opencv3.0 + tesseract, ...
最新文章
- 使用Spring-Cloud将Spring Boot应用程序部署到Cloud Foundry
- Java 8中的instanceof运算符和访客模式替换
- 1997年投稿,2021年发表!收到录用信那一刻,我即将退休……
- 安卓智能手机完全装机手册,让安卓拥有无限可能!
- 同旭堂同湿散真实效果,七大真实效果让你彻底放心
- unison+inotify
- 电脑提醒没有权限在此位置保存文件怎么办?
- 解决电脑软件可以上网,但是浏览器无法上网的问题
- 【中学】求解一元二次方程
- java怎么实现手机投影,在Java中投射对象
- 支付宝小程序生态服务商奖励发布,单个商家最高奖励5000元
- ThinkPHP6 API接管异常
- 树莓派安装AdGuard Home
- c语言 运行出现错误代码,运行出现 debug error
- 单因素试验的方差分析及其MATLAB实现
- Tms320c6678——多核导航模块(Multicore Navigator)
- Deepin系统下载安装LibreOffice6.4.1
- 【Python计量】Logit模型
- AI时代大点兵——国内外知名AI公司2018年最新盘点【完整版】
- 脚本引流靠谱吗,脚本引流话术连骗子都不如,你怎么引流