OpenCV 入门(七)

您所在的位置:网站首页 身份证背面怎么识别男女图片 OpenCV 入门(七)

OpenCV 入门(七)

2024-07-17 17:50:26| 来源: 网络整理| 查看: 265

OpenCV 入门系列:

OpenCV 入门(一)—— OpenCV 基础 OpenCV 入门(二)—— 车牌定位 OpenCV 入门(三)—— 车牌筛选 OpenCV 入门(四)—— 车牌号识别 OpenCV 入门(五)—— 人脸识别模型训练与 Windows 下的人脸识别 OpenCV 入门(六)—— Android 下的人脸识别 OpenCV 入门(七)—— 身份证识别

利用 OpenCV 实现身份证识别 Demo 效果:

2024-4-24.身份证识别Demo效果

主要步骤分为两大步:

利用 OpenCV 从完整的身份证图片中识别出身份证号码区域,并返回身份证号码的图片利用 OCR 识别工具将身份证号码图片识别成文字

实际上身份证识别、银行卡识别都是相同的思路。

1、OpenCV 图像识别 1.1 上层代码过程

在 Activity 中,点击“从相册中查找”按钮从相册中选择一张图片转换为一个 640 * 480 的 Bitmap 设置到 ImageView 中:

class MainActivity : AppCompatActivity() { private lateinit var mBinding: ActivityMainBinding private var mFullImage: Bitmap? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(mBinding.root) } /** * 从相册中选择一张图片 */ fun search(view: View) { val intent = Intent(Intent.ACTION_PICK) intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*") startActivityForResult(Intent.createChooser(intent, "选择待识别图片"), REQUEST_CODE) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) { getResult(data.data) } } private fun getResult(data: Uri?) { // 获取图片路径 var imagePath: String? = null if ("file" == data?.scheme) { Log.i(TAG, "path uri 获得图片") imagePath = data.path } else if ("content" == data?.scheme) { Log.i(TAG, "content uri 获得图片") val filePathColumns = arrayOf(MediaStore.Images.Media.DATA) val cursor = contentResolver.query(data, filePathColumns, null, null, null) if (null != cursor) { if (cursor.moveToFirst()) { val columnIndex = cursor.getColumnIndex(filePathColumns[0]) imagePath = cursor.getString(columnIndex) } cursor.close() } } // 根据图片路径生成 Bitmap 并显示 if (!TextUtils.isEmpty(imagePath)) { mFullImage?.recycle() mFullImage = toBitmap(imagePath) mBinding.tvIdNumber.text = null mBinding.ivIdCard.setImageBitmap(mFullImage) } } /** * 根据图片路径生成 Bitmap,宽高要缩放到 STANDARD_ID_CARD_WIDTH * 与 STANDARD_ID_CARD_HEIGHT 的范围内 */ private fun toBitmap(imagePath: String?): Bitmap? { if (imagePath == null) { return null } val tempOptions = BitmapFactory.Options() tempOptions.inJustDecodeBounds = true BitmapFactory.decodeFile(imagePath, tempOptions) // 计算出缩放倍数以及缩放后的宽高 var tempWidth = tempOptions.outWidth var tempHeight = tempOptions.outHeight var scale = 1 while (true) { if (tempWidth private val TAG = MainActivity::class.java.simpleName private const val REQUEST_CODE = 100 private const val STANDARD_ID_CARD_WIDTH = 640 private const val STANDARD_ID_CARD_HEIGHT = 480 } }

然后点击“查找 ID”按钮时,将完整的身份证 Bitmap 传给 ImageProcessor 交由 Native 层的 OpenCV 进行识别:

private var mResultImage: Bitmap? = null /** * 从整张图片中截取出身份证号码区域 */ fun searchIdImage(view: View) { mBinding.tvIdNumber.text = null mResultImage = ImageProcessor.getIdNumberArea(mFullImage, Bitmap.Config.ARGB_8888) mFullImage?.recycle() mBinding.ivIdCard.setImageBitmap(mResultImage) }

ImageProcessor 的内容很简单,就定义了一个 JVM 静态的 Native 方法 getIdNumberArea():

class ImageProcessor { companion object { init { System.loadLibrary("ID-Recognition") } @JvmStatic external fun getIdNumberArea(fullImage: Bitmap?, config: Bitmap.Config): Bitmap } }

该方法需要得到识别后身份证号区域的 Bitmap。

1.2 Native 识别过程

Native 层首先要解决 Bitmap 与 Mat 之间相互转换的问题。因为我们从上层传到 Native 的待识别图片是 Bitmap,但是 OpenCV 中是没有 Bitmap 对象的,类似的可以被认为是一张图片的结构是 Mat。那么在给 OpenCV 识别前,就要将 Bitmap 转化成 Mat,识别后再将 Mat 转换成 Bitmap 返回给上层。

OpenCV 提供了转换函数 nBitmapToMat2() 和 nMatToBitmap(),我们还需自己实现一个创建 Bitmap 对象的函数 createBitmap():

#include #include using namespace std; using namespace cv; extern "C" { extern JNIEXPORT void JNICALL Java_org_opencv_android_Utils_nBitmapToMat2 (JNIEnv *env, jclass, jobject bitmap, jlong m_addr, jboolean needUnPremultiplyAlpha); extern JNIEXPORT void JNICALL Java_org_opencv_android_Utils_nMatToBitmap (JNIEnv *env, jclass, jlong m_addr, jobject bitmap); /** * 反射调用上层的 Bitmap 的 createBitmap() 创建一个 Bitmap 对象,并且 * 将 srcData 的内容填充到 Bitmap 中 */ jobject createBitmap(JNIEnv *env, Mat &srcData, jobject config) { int width = srcData.cols; int height = srcData.rows; // 反射 Bitmap.createBitmap() 并调用以创建 Bitmap 对象 jclass bitmapClass = env->FindClass("android/graphics/Bitmap"); jmethodID createBitmapMethod = env->GetStaticMethodID( bitmapClass, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); jobject bitmap = env->CallStaticObjectMethod(bitmapClass, createBitmapMethod, width, height, config); // 将 srcData 转换成 bitmap Java_org_opencv_android_Utils_nMatToBitmap(env, bitmapClass, (jlong) &srcData, bitmap); return bitmap; } }

接下来再实现 OpenCV 的识别函数:

extern "C" JNIEXPORT jobject JNICALL Java_com_opencv_id_recognition_ImageProcessor_getIdNumberArea(JNIEnv *env, jclass clazz, jobject full_image, jobject config) { Mat src_img; Mat dst_img; Mat temp_img; // 1.通过 OpenCV 提供的函数,将上层传来的 Bitmap 转换为 Mat 对象 Java_org_opencv_android_Utils_nBitmapToMat2(env, clazz, full_image, (jlong) &src_img, false); // 2.将图片无损压缩至 640 * 400 resize(src_img, src_img, FIXED_ID_CARD_SIZE); // 3.灰度化 cvtColor(src_img, temp_img, COLOR_BGR2GRAY); // 4.二值化 threshold(temp_img, temp_img, 100, 255, THRESH_BINARY | THRESH_OTSU); // 5.膨胀操作 Mat eroded_img = getStructuringElement(MORPH_RECT, Size(20, 10)); erode(temp_img, temp_img, eroded_img); // 6.轮廓检测 vector contours; vector rects; findContours(temp_img, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0)); for (int i = 0; i rects.push_back(rect); rectangle(dst_img, rect, Scalar(0, 255, 255)); dst_img = src_img(rect); } } // 7.筛选结果,如果 rects 有多个元素,则挑选纵坐标靠下的 if (rects.size() == 1) { dst_img = src_img(rects[0]); } else if (rects.size() > 1) { int lowPoint = 0; Rect finalRect; for (auto &rect: rects) { if (rect.tl().y > lowPoint) { lowPoint = rect.tl().y; finalRect = rect; } } rectangle(temp_img, finalRect, Scalar(255, 255, 0)); dst_img = src_img(finalRect); } // 8. 根据最终的 Mat 创建 Bitmap 作为返回值 jobject bitmap = createBitmap(env, dst_img, config); // 9. 释放资源 src_img.release(); dst_img.release(); temp_img.release(); return bitmap; } 2、OCR 识别

上一步我们能得到一个包含身份证号码的 Bitmap,接下来需要使用 OCR 识别技术将图片中的身份证号码识别成文字。OCR 全称 Optical Character Recognition,是一个对文本资料的图像文件进行分析识别处理,获取文字及版面信息的过程。

我们使用的是 Tess-two。Tess-two 是 TesseraToolForAndroid 的一个 git 分支,它具有如下特征:

简单易用开源且支持离线使用为 Android 平台定制的 Java API

首先我们将识别模型文件 cn.traineddata 拷贝到 /src/main/assets 目录下,在 Activity 的 onCreate() 中启动协程,将该模型文件拷贝到手机中,并初始化 Tess:

override fun onCreate(savedInstanceState: Bundle?) { ... lifecycleScope.launch { initTess() } } private suspend fun initTess() { coroutineScope { // 1.显示进度 showProgress() val result = async { mTessBaseAPI = TessBaseAPI() // 2.通过流将识别模型拷贝到手机中 try { val inputStream = assets.open("$DEFAULT_LANGUAGE.traineddata") val assetFile = File("/sdcard/tess/tessdata/$DEFAULT_LANGUAGE.traineddata") if (!assetFile.exists()) { assetFile.parentFile?.mkdirs() val fos = FileOutputStream(assetFile) val buffer = ByteArray(2048) var len: Int while (inputStream.read(buffer).also { len = it } != -1) { fos.write(buffer, 0, len) } fos.close() } inputStream.close() // init 传入的 datapath 必须是包含 tessdata 的目录 return@async mTessBaseAPI?.init("/sdcard/tess", DEFAULT_LANGUAGE) ?: false } catch (e: IOException) { e.printStackTrace() } return@async false } // 3.处理异步任务结果 dismissProgress() if (!result.await()) { Toast.makeText(this@MainActivity, "load trainedData failed", Toast.LENGTH_SHORT) .show() } } } companion object { private const val DEFAULT_LANGUAGE = "cn" }

注意 TessBaseAPI.init() 的第一个参数,路径必须是包含了 tessdata 目录的父目录,否则初始化会抛异常。

最后,点击“识别文字”按钮时,将被识别的 Bitmap 设置给 Tess 然后获取文字结果即可:

fun recognition(view: View) { mTessBaseAPI?.setImage(mResultImage) mBinding.tvIdNumber.text = mTessBaseAPI?.utF8Text mTessBaseAPI?.clear() }

当然,从最终的识别结果来看,并没有达到百分百的准确率,这与训练样本的数量不够有关。Tesseract-OCR 的样本训练方法,可参考超级详细的Tesseract-OCR样本训练方法。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


    图片新闻

    实验室药品柜的特性有哪些
    实验室药品柜是实验室家具的重要组成部分之一,主要
    小学科学实验中有哪些教学
    计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
    实验室各种仪器原理动图讲
    1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
    高中化学常见仪器及实验装
    1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
    微生物操作主要设备和器具
    今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
    浅谈通风柜使用基本常识
     众所周知,通风柜功能中最主要的就是排气功能。在

    专题文章

      CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭