Android:基于OpenCV实现身份证识别(C++)——移植图像算法
系列文章目录
第一章 Android:基于OpenCV实现身份证识别(C++)——图像处理
第二章 Android:基于OpenCV实现身份证识别(C++)——移植图像算法
文章目录
- 系列文章目录
- 前言
- 一、Android Studio中配置OpenCV
- 1.创建项目
- 2.下载并解压opencv-android-sdk
- 3.配置OpenCV SDK路径
- 4.配置CMakeLists.txt文件
- 二、添加选择图片功能
- 1.自定义协定
- 2.调用registerForActivityResult
- 三、移植C++图像处理程序
- 1.定义原生方法
- 2.Mat与Bitmap数据格式转换
- 3.移植图像处理逻辑
- 四、效果展示
- 总结
前言
我们要做一个Android上的身份证号码识别功能,在上一篇用OpenCV做了图像处理,本文目标是将我们的C++程序移植到Android程序中。
【本文源码下载】
软件环境:
- Android Studio Chipmunk | 2021.2.1
- opencv-4.5.5-android-sdk
一、Android Studio中配置OpenCV
1.创建项目
新建项目——选择Native C++——点击Next;
修改名称和包名——点击Next;
这里可以选择C++的标准库版本,我这里保持默认,点击Finish。
2.下载并解压opencv-android-sdk
在官网下载opencv-4.5.5-android-sdk.zip 并解压至磁盘,后面我们要把此SDK目录配置到项目中。
解压后效果如下:
3.配置OpenCV SDK路径
修改gradle.properties文件,加入opencvsdk路径;
opencvsdk=D\:\\Soft\\Dev\\OpenCV-android-sdk
修改build.gradle(:app)文件 ,加入cmake配置;
android {defaultConfig {externalNativeBuild {cmake {cppFlags "-frtti -fexceptions"abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'arguments "-DOpenCV_DIR=" + opencvsdk + "/sdk/native"arguments "-DANDROID_STL=c++_shared"}}}
}
如果没有配置 arguments “-DANDROID_STL=c++_shared”,可能会在运行时闪退,并报以下错误。
E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.example.idrec, PID: 28414java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not foundat java.lang.Runtime.loadLibrary0(Runtime.java:1071)at java.lang.Runtime.loadLibrary0(Runtime.java:1007)at java.lang.System.loadLibrary(System.java:1667)at com.example.idrec.MainActivity.<clinit>(MainActivity.kt:69)at java.lang.Class.newInstance(Native Method)
4.配置CMakeLists.txt文件
修改CMakeLists.txt文件,加入以下配置;
include_directories(${OpenCV_DIR}/jni/include)
add_library( lib_opencv SHARED IMPORTED )
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${OpenCV_DIR}/libs/${ANDROID_ABI}/libopencv_java4.so)
并在target_link_libraries内加入lib_opencv,效果如下;
target_link_libraries( # Specifies the target library.idreclib_opencv# Links the target library to the log library# included in the NDK.${log-lib})
二、添加选择图片功能
要实现从手机选择图片,传统的startActivityForResult 官方已经废弃了,如果想启动一个 Activity 并获取返回的结果,推荐使用 registerForActivityResult 来代替。
1.自定义协定
关于如何自定义协定?可参考官方文档:自定义协定
选择手机图片的协定如下:
/*** 选择照片的协定*/
class SelectPhotoContract : ActivityResultContract<Unit, Bitmap?>() {private lateinit var context: Contextoverride fun createIntent(context: Context, input: Unit?): Intent {this.context = contextreturn Intent(Intent.ACTION_PICK).setType("image/*")}override fun parseResult(resultCode: Int, intent: Intent?): Bitmap? {if (intent == null || resultCode != Activity.RESULT_OK) return null// Uri转-》Bitmapintent.data?.let {val input = context.contentResolver.openInputStream(it)val bitmap = BitmapFactory.decodeStream(input)input?.close()return bitmap}return null}
}
这是一个输出Bitmap的协定,我们需要在parseResult方法中通过结果Uri获取Bitmap;
2.调用registerForActivityResult
然后在Activity中注册registerForActivityResult 并显示图片结果,代码如下:
class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingprivate val mSelectPhoto = selectPhoto()/** 选择的图片 */private var mSrcBitmap: Bitmap? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.btChooseImage.setOnClickListener {mSelectPhoto.launch(Unit)}binding.btReadId.setOnClickListener {readID()}}/*** 选择图片*/private fun selectPhoto() = registerForActivityResult(SelectPhotoContract()) {it?.let {mSrcBitmap = itbinding.ivImage.setImageBitmap(it)return@registerForActivityResult}Toast.makeText(this, "没有选择图片", Toast.LENGTH_SHORT).show()}
}
注意:private val mSelectPhoto = selectPhoto() 不要写成private val mSelectPhoto1 by lazy { selectPhoto() }懒加载的形式,registerForActivityResult是不支持这样初始化的。
三、移植C++图像处理程序
1.定义原生方法
定义原生方法getIdNumber(),点击“识别ID”按钮时,通过readID()调用getIdNumber()来获取识别后的Bitmap图像,并显示出来;
class MainActivity : AppCompatActivity() {.../*** 读取身份证号码*/private fun readID(){if(mSrcBitmap == null){Toast.makeText(this,"请先选择图片", Toast.LENGTH_SHORT).show()}else{mSrcBitmap?.let {val idBitmap = getIdNumber(it, Bitmap.Config.ARGB_8888)binding.ivImage.setImageBitmap(idBitmap)}}}external fun getIdNumber(src: Bitmap, config: Bitmap.Config): Bitmapcompanion object {// Used to load the 'idrec' library on application startup.init {System.loadLibrary("idrec")}}
}
2.Mat与Bitmap数据格式转换
在C++中我们使用的Mat格式,其实它就相当于Android中的Bitmap格式;
如果我们要在C++中做图像处理,就面临两个问题:
(1)Bitmap如何转为Mat格式?
(2)Mat又如何转为Bitmap格式?
这个在OpenCV-android-sdk\sdk\java\src\org\opencv\android\Utils.java中已经帮我们提供了格式转换方法,现在需要在cpp文件中声明一下,就可以使用了。
修改main/cpp/native-lib.cpp文件,添加以下代码:
#include <jni.h>
#include <string>
#include <opencv2/opencv.hpp>#define CARD_SIZE Size(640, 400)using namespace cv;
using namespace std;extern "C" JNIEXPORT void JNICALL
Java_org_opencv_android_Utils_nBitmapToMat2(JNIEnv *env, jclass clazz, jobject bitmap, jlong m_addr,jboolean un_premultiply_alpha);
extern "C" JNIEXPORT void JNICALL
Java_org_opencv_android_Utils_nMatToBitmap(JNIEnv *env, jclass, jlong m_addr, jobject bitmap);/*** Mat格式转——》Bitmap* @param env* @param srcData* @param config* @return*/
jobject createBitmap(JNIEnv *env, Mat srcData, jobject config){int imgWidth = srcData.cols;int imgHeight = srcData.rows;// 利用反射创建Bitmap对象jclass bmpCls = env->FindClass("android/graphics/Bitmap");jmethodID createBitmapMid = env->GetStaticMethodID(bmpCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");jobject jBmpObj = env->CallStaticObjectMethod(bmpCls, createBitmapMid, imgWidth, imgHeight, config);Java_org_opencv_android_Utils_nMatToBitmap(env, nullptr, (jlong) &srcData, jBmpObj);return jBmpObj;
}/*** 获取身份证号码*/
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_idrec_MainActivity_getIdNumber(JNIEnv *env, jobject thiz, jobject src,jobject config) {Mat src_img;Mat dst_img;// bitmap转——》MatJava_org_opencv_android_Utils_nBitmapToMat2(env, (jclass)thiz, src, (jlong)&src_img, 0);Mat dst;...图像处理逻辑...// 创建Bitmap对象jobject bitmap = createBitmap(env, dst_img, config);// 释放资源src_img.release();dst_img.release();dst.release();return bitmap;
}
3.移植图像处理逻辑
现在将上一篇的图像处理逻辑复制到getIdNumber方法中,代码如下:
/*** 获取身份证号码*/
extern "C"
JNIEXPORT jobject JNICALL
Java_com_example_idrec_MainActivity_getIdNumber(JNIEnv *env, jobject thiz, jobject src,jobject config) {Mat src_img;Mat dst_img;// bitmap转——》MatJava_org_opencv_android_Utils_nBitmapToMat2(env, (jclass)thiz, src, (jlong)&src_img, 0);Mat dst;// 无损压缩 640*400resize(src_img, src_img, CARD_SIZE);// 灰度化cvtColor(src_img, dst, COLOR_BGR2GRAY);// 二值化threshold(dst, dst, 100, 255, THRESH_BINARY);// 膨胀Mat erodeElement = getStructuringElement(MORPH_RECT, Size(20, 10));erode(dst, dst, erodeElement);// 轮廓检测vector<vector<Point>> contours;vector<Rect> rects;findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));for (auto & contour : contours) {Rect rect = boundingRect(contour);rectangle(dst, rect, Scalar(0, 255));// 筛选轮廓图片if (rect.width > rect.height * 9) {rects.push_back(rect);rectangle(dst, rect, Scalar(0, 0, 255));}}if (rects.size() == 1) {dst_img = src_img(rects.at(0));}else {Rect rectTmp = rects.at(0);// 遍历查找Y最大的轮廓for (auto rect : rects) {if (rect.tl().y > rectTmp.tl().y) {rectTmp = rect;}}rectangle(dst, rectTmp, Scalar(255, 255, 0));dst_img = src_img(rectTmp);}// 创建Bitmap对象jobject bitmap = createBitmap(env, dst_img, config);// 释放资源src_img.release();dst_img.release();dst.release();return bitmap;
}
四、效果展示
先从相册选择图片后,点击“识别ID”按钮,开始识别身份证号码。
总结
以上就是本文要讲的内容,从中我们学到了如何在Android 引入OpenCV?如何Mat与Bitmap格式转换?如何在Android中调用C++图像计算?
【本文源码下载】
Android:基于OpenCV实现身份证识别(C++)——移植图像算法相关推荐
- 基于opencv的身份证识别(KNN与OCR两种算法)
KNN算法主程序 # -*-coding:utf-8-*- # @Author: Phantom # @编译环境:windows 10 + python3.8 # @IDE:Pycharm2021.1 ...
- Python 基于 opencv 的车牌识别系统, 可以准确识别车牌号
大家好,我是程序员徐师兄,6 年大厂程序员经验,点击关注我 简介 毕业设计基于Opencv的车牌识别系统 车牌搜索识别找出某个车牌号 对比识别车牌系统 车牌数据库认证系统 车牌图文搜索系统 车牌数据库 ...
- Android平台上实现身份证识别(通过阿里云Api-印刷文字识别_身份证识别)
Android平台上实现身份证识别(通过阿里云Api-印刷文字识别_身份证识别) 一: 前言 继上一篇文章有段时间了,上一篇文章的身份证和银行卡的识别时通过本地的opencv库,tess-two库识别 ...
- 基于OpenCV实现人脸识别--Python
目录 前言 第一章 OpenCV介绍 第二章 功能描述 2.1 对已有的数据进行检测 2.2 陌生人检测并发出警告 2.3 保存陌生人的视频 2.4 输入图片进行检测 2.5 现场录用信息 第三章 功 ...
- 基于OpenCV的人脸识别自助商店(源码&部署视频)
1.模块功能介绍 实现人脸识别模块.人脸登录与注册功能.商店显示和用户余额页显示功能 用GUl图形界面实现(pyqt)语言python windows下软件pycharm 1.用户登录模块:刷脸登录 ...
- 树莓派 --- 基于OpenCV实现人脸识别
目录 参考博客 调百度人脸识别的API也能达到目的,我这里是基于OpenCV进行人脸识别 OpenCV(Open source Computer Vision Library) 是一个开源的计算机视觉 ...
- 基于OpenCV的车牌识别的设计与实现
随着大数据和互联网技术的快速发展,利用人工智能技术实现车牌信息的自动识别推荐成为研究的热门话题.通过对基于OpenCV的车牌识别系统的网站功能需要进行讨论研究,这种跨平台计算机视觉和机器学习非常适用于 ...
- Python基于OpenCV的人脸识别自助商店(源码&部署视频)
1.模块功能介绍 实现人脸识别模块.人脸登录与注册功能.商店显示和用户余额页显示功能 用GUl图形界面实现(pyqt)语言python windows下软件pycharm 1.用户登录模块:刷脸登录 ...
- 【优秀毕设】基于OpenCV的人脸识别打卡/签到/考勤管理系统(最简基本库开发、可基于树莓派)
[优秀毕设]基于OpenCV的人脸识别打卡/签到/考勤管理系统(最简基本库开发.可基于树莓派) 该系统利用Harr级联检测和LPBH进行人脸检测和训练.识别 利用Tkinter完成界面搭建 利用Fla ...
最新文章
- 微众银行殷磊:AI+卫星,从上帝视角洞察资产管理|BDTC 2019
- SpringBoot中使用FastJson解析Json数据
- 24继承父类并实现多个接口
- Scala父类构造方法
- 2022年软件评测师考试大纲
- HTML5期末大作业:家乡网站设计——我的家乡-获奖第二(6页) HTML+CSS+JavaScript 关于我的家乡HTML网页设计--
- 科技文献检索(九)——检索步骤和策略
- matlab程序复制出现乱码,matlab程序复制到Word文档里变成乱码,该如何改?
- Android性能优化之利用LeakCanary检测内存泄漏及解决办法
- android9手机电池管家,腾讯电池管家APP
- 2016-2017-2 《程序设计与数据结构》课程总结
- Matlab中fprintf函数使用
- 讲解MySQL8.0备份与还原工具(mysqlbackup)
- 数据结构基础— How Long Does It Take
- 在计算机病毒组成结构中,计算机病毒的结构中有哪三种机制组成?
- JavaScript中if嵌套
- 自动祝福程序(定时发送消息)
- RN:metro缓存以及如何清除缓存
- 通过icon hash查IP地址
- 矩阵分析L1 线性空间基础
热门文章
- matlab 三角分解法 解线性方程组的直接方法
- 从街机到抓娃娃机,硬币经济也将被移动支付取代?
- YouTube DNN论文精读
- TCP/IP 详解(第 2 版) 笔记 / 3 链路层 / 3.2 以太网与 IEEE 802 LAN/MAN 标准 / 3.2.2 以太网帧格式
- 直播预告 | NeurIPS 专场一 青年科学家专场
- 树莓派 zero 通过 max31865 连接 PT100 热电阻 测量温度
- KAL 推出 Kalignite Hypervisor 解决方案—引领 ATM 硬件更新创新模式
- 20175208 实验一 Java开发环境的熟悉
- 新浪服务器mysql_php新浪云链接mysql与storage
- 修改php-fpm监听端口,php-fpm配置详解