CameraX + MLKit 打造超简单 OCR 方案

news2025/2/26 9:43:45

前言

疫情期间隔三差五就要做核酸,随时都要准备身份证给大白扫描识别。写一篇文章聊聊其中用到的技术吧,顺道缅怀这段难忘的经历,希望大家的生活彻底告别的疫情影响,早日回归正常!

身份证扫描主要用到是文字识别技术(OCR)。这类技术方案已经很多了,本文介绍基于 CameraX + MLKit 的实现方式。其中 CameraX 用来实现相机的取景和预览,MLKit 用来进行图片中的文字识别。

1. CameraX 实现相机预览

1.1 CameraX 简介

Android 自 5.0 开始引入了全新的相机框架 Camera2 ,相较于之前的 Camera1 对多摄像头的支持更加友好,功能更加强大,但使用成本也更高。此背景下谷歌发布了 CameraX,它基于 Camera2 封装,大大提高了 API 的易用性。我们可以用很少的代码搭建出面向特定场景的相机应用,OCR 就是一种典型的相机应用场景 。

CameraX 引入 UseCase 的概念完成各种相机能力,UseCase 有利于功能模块的解耦,聚焦特定领域进行功能开发。CameraX 默认提供了几个常用的 UseCase 实现,能够满足大多数场景下的使用

  • Preview : 提供相机取景和预览
  • ImageCapture:拍照并保存图片
  • ImageAnalysis:处理预览帧图片

本文 OCR 场景中将会使用到 PreviewImageAnalysis 这两个 UseCase。Preview 帮助我们实现相机的取景和预览,ImageAnalysis 帮助我们将采集的图片送入 OCR 分析。

接下来让我们使用 CameraX 一步步完成相机预览功能

1.2 工程引入 CameraX

首先,在 Gradle 中引入 CameraX 相关库如下

implementation "androidx.camera:camera-lifecycle:1.2.0"
implementation "androidx.camera:camera-view:1.2.0"
implementation "androidx.camera:camera-camera2:1.2.0"

另外,需要使用相机,所以在 AndroidManifest 中申请相机权限

<uses-permission android:name="android.permission.CAMERA"
    tools:ignore="PermissionImpliesUnsupportedChromeOsHardware" />

1.3 获取 ProcessCameraProvider

CameraX 通过 ProcessCameraProvider 访问相机实例。顾名思义,ProcessCamera 表示每个 Application Process 期间可使用的相机服务,所以 ProcessCameraProvider 是一个进程单例,通过 getInstance 创建并获取。创建是一个异步过程,所以借助 CameraProviderFuture 异步返回:

// 通过 cameraProviderFuture 异步返回创建的 ProcessCameraProvider 实例
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)

//监听 ProcessCameraProvider 获取成功
cameraProviderFuture.addListener(
    Runnable {
        //获取 cameraProvider
        val cameraProvider = cameraProviderFuture.get()
        ...
    }, 
    ContextCompat.getMainExecutor(context) // Runnable 运行的 Executor
)

在 Runnable 中成功获取 ProcessCameraProvider 单例,接下来可以用它来组装 UseCase ,实现相机功能了。

CameraX 的一个重要特征是 LifecycleAware,相机可以根据应用的前后台情况自动开启或关闭,降低开发者的心智负担。ProcessCameraProvider 添加 UseCase 时会关联 LifecycleOwner

UseCase 根据 Lifecycle 调用 onStateAttached/onStateDetatched,当我们自定义 UseCase 时,可以在这里进行一些自定义前/后处理。

1.4 添加 Preview UseCase

//选择后置镜头
val cameraSelector =
    CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build()

//添加 Preivew UseCase 
cameraProvider.bindToLifecycle( 
    lifecycleOwner,  
    cameraSelector, 
    preview
)

如上, ProcessCameraProvicer#bindToLifecycle 添加 Preview 。

Preview UseCase 的创建非常简单,如下:

val preview = Preview.Builder().build().apply {
    setSurfaceProvider(previewView.surfaceProvider)
}

创建 Preview 的关键是设置渲染用的 Surface,这是通过 PreviewView 获取的。

PreviewView 是 CameraX 提供的用于显示相机预览流的自定义 View,它内部可以根据需要切换 TexureView 或者 SurfaceView。

SurfaceView 有更好的性能,但在 Android 7.0 之前无法实现旋转、透明、动画等常规自定义 View 的功能,需要使用 TextureView 替代。PreviewView 默认使用性能优先的 SurfaceView,如果希望其有更好的兼容性,可以设置 previewView.implementationMode = PreviewView.ImplementationMode.COMPATIBLE

1.5 布局 PreviewView

我们可以像下面这样在 xml 中布局使用 PreviewView

<FrameLayout
    android:id="@+id/container">
        <androidx.camera.view.PreviewView
            android:id="@+id/previewView" />
</FrameLayout>

如果我们使用 Compose 渲染 UI ,可以借助 AndroidView 显示 PreviewView,Compose 展示相机预览的代码大体如下所示:

@Composable
fun CameraScreen() {

    //获取 ProcessCameraProvider
    val cameraProviderFuture = remember {
        ProcessCameraProvider.getInstance(context)
    }
    
    // 显示预览
    AndroidView(
        modifier = Modifier.fillMaxSize(),
        factory = { ctx ->
            PreviewView(ctx).apply {
                cameraProviderFuture.addListener({
                    val cameraProvider = cameraProviderFuture.get()
                    val preview = //略
                    val cameraSelector = //略
                   
                    cameraProvider.unbindAll()
                    cameraProvider.bindToLifecycle(
                        LocalLifecycleOwner.current,
                        cameraSelector,
                        preview
                    )
                
            }, ContextCompat.getMainExecutor(previewView.context))
        }
    })
    
}

2. MLKit 实现文字识别

2.1 MLKit 简介

MLKit 是谷歌的面向移动端开发者的机器学习库,帮助移动应用在离线状态下使用各种端智能技术,例如:

  • 智能视觉处理:二维码扫描、文字识别、人脸检测、物体捕捉等;
  • 自然语言处理:语言识别、智能回复、自动翻译等

这些端上的技术让应用变得更加智能的同时依然保持高性能,更重要的是这一切都是免费的,且不依赖 GMS(Google Mobile Service)。

2.2 工程引入 MLKit

本文我们主要使用到 MLKit 的文字识别功能,只需要添加以下依赖即可:

implementation 'com.google.mlkit:text-recognition-chinese:16.0.0-beta6'

text-recognition-chinese 可以识别中文字符,另外也有其他的 Artifact 可以识别日文韩文等非拉丁系的语言。

2.3 CameraX 实现图像分析

前面我们通过 Preview 实现了相机预览,接下来我们为 CameraProvider 添加 ImageAnalysis ,它可以接收相机的预览帧用于图像分析和处理。

val imageAnalysis = ImageAnalysis.Builder()
    .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build()
    .apply {
        //设置图像分析器
        setAnalyzer(
            Executors.newSingleThreadExecutor(),
            OcrAnalyzer { result: String ->
                //基于 MLKit 处理 OCR,并返回 result
            } 
        )
    }

...

cameraProvider.bindToLifecycle(
    LocalLifecycleOwner.current,
    cameraSelector,
    preview,
    imageAnalysis // 增加 ImageAnalysis 能力,关联 Lifecycle

setBackpressureStrategy 是设置预览帧的生产消费的缓冲策略,其默认值 ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST 表示在每一帧没有分析结束之前,新的渲染帧会自动丢弃,避免排队。

ImageAnalysis#setAnalyzer 添加自定义图像分析器,这里我们定义一个 OcrAnalyzer,它基于 MLKit 实现 OCR 功能。

2.4 自定义 OcrAnalyzer

class OcrAnalyzer(
    private val onRecognized : (result: String) -> Unit
) : ImageAnalysis.Analyzer {

    // 获取可识别中文的 TextRecognition
    private val recognition = 
        TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build())
        
    // 对 Image 进行处理
    override fun analyze(imageProxy: ImageProxy) {
        val image = imageProxy.image

        if (image != null) {
            val imageRotation = imageProxy.imageInfo.rotationDegrees
            val inputImage = InputImage.fromMediaImage(image, imageRotation)
            recognition.process(inputImage)
                .addOnSuccessListener { recognizedText ->
                    val textBlocks = recognizedText.textBlocks
                    //解析 textBlocks 获取所需的信息并返回
                    extractText(textBlocks)?.let { onRecognized(it) }
                    imageProxy.close()
                }.addOnFailureListener {
                    imageProxy.close()
                }
        } 
    }
}

ImageAnalysis.Analyzer 返回的 ImageProxy 中包含了预览帧信息:

  • imageProxy.image: 图像信息
  • ImageInfo.rotationDegrees:根据设备情况获得的图片旋转角度。

InputImage.fromMediaImage 根据这两个参数获取具体的 InputImage,后者提交 recognition 处理。这里的 recognition 是一个可识别中文的 TextRecognition

2.5 解析 TextBlocks

经过 TextRecognition 文字识别后将返回 Block/Line/Element 这样的数据结构,这种结构有利于进一步细粒度的解析。

Block 代表一个自然段落,由若干 Line(行) 组成,每一个 Line 又包含多个 Element(单词) 。

假设我们希望从身份证中获取姓名以及身份证号,虽然不确定身份证这样的排版会被识别为怎样的 Block,但是姓名和身份证号肯定处于不同 Line 中。我们定义 extractText 方法,将所有的 Block 下的 Line 聚合到一起,统一进行解析:

private fun extractText(textBlocks: List<Text.TextBlock>): String {
    val lines = textBlocks.flatMap { it.lines }
    var name = "unknown"
    var id = "unknown"
    lines.forEach {
        val lineText = it.elements.joinToString { it.text }
        if (lineText.contains("姓名")) {
            name = lineText.substringAfter("姓名")
        }
        if (lineText.contains("公民身份证号码")) {
            id = lineText.substringAfter("公民身份证号码")
        }
    }
    return "$name\n$id"
}

成功识别文字后的效果如下:

结束语

透过文字识别这样一个小的应用场景,我们切实感受到了 CameraX 以及 MLKit 开箱即用般的的易用性。作为谷歌官方工具包,它们还与 Compose 等其他 Jetpack 组件有着不错的兼容性。感谢谷歌强大地开发者生态,让开发者们可以低成本的开发自己的移动应用。

  • CameraX:https://developer.android.com/training/camerax
  • MLKit:https://developers.google.com/ml-kit

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/190799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java程序设计实验3 | 面向对象(上)

*本文是博主对Java各种实验的再整理与详解&#xff0c;除了代码部分和解析部分&#xff0c;一些题目还增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人自己的补充&#xff0c;以方便大家额外学习、参考。 目录 一、实验…

2023软考软件设计师易混淆知识点~(5)

将2023上半年软考《软件设计师》易混淆知识点&#xff0c;分享给大家&#xff0c;快来跟着一起打卡学习吧&#xff01;易混淆点4:软件维护类型1、更正性维护:针对真实存在并已经发生的错误进行的维护行为。2、预防性维护:针对真实存在但还未发生的错误进行的维护。3、适应性维护…

物联网毕业设计项目选题:harmonyos鸿蒙系统开发智能小车案例-hi3861v100开发板-留言领源码

华清远见FS-Hi3861开发套件&#xff0c;支持HarmonyOS 3.0系统。开发板主控Hi3861芯片内置WiFi功能&#xff0c;开发板板载资源丰富&#xff0c;包括传感器、执行器、NFC、显示屏等&#xff0c;同时还配套丰富的拓展模块。开发板配套丰富的学习资料&#xff0c;包括全套开发教程…

洛谷 P2983 [USACO10FEB]Chocolate Buying S

这题其实不难&#xff0c;但时间复杂度是大难点 题目描述 Bessie and the herd love chocolate so Farmer John is buying them some. The Bovine Chocolate Store features N (1 < N < 100,000) kinds of chocolate in essentially unlimited quantities. Each type i o…

网络电缆有哪些类型,网络工程师必知!

在 21 世纪&#xff0c;理想的目标是完全消除网络电缆的使用&#xff0c;并生活在一个一切都以无线方式管理的世界中。没有电缆的困扰&#xff0c;管理服务器会容易得多&#xff0c;我们也不必浪费时间标记和跟踪数百条电缆。 在那个时候到来之前&#xff0c;我们一直坚持使用…

F623光猫超级管理员密码获取

中兴F623光猫超级管理员密码获取 ⚠️事前说明 如果你不熟悉光猫的操作&#xff0c;请勿修改或拆解任何与光猫有关的命令、零件、网线等。操作有风险&#xff0c;运行需谨慎。 光猫型号&#xff1a;ZXHN F623 所属运营商&#xff1a;中国移动 硬件版本号&#xff1a;V6.0 …

算法刷题打卡第77天:解密消息

解密消息 难度&#xff1a;简单 给你字符串 key 和 message &#xff0c;分别表示一个加密密钥和一段加密消息。解密 message 的步骤如下&#xff1a; 使用 key 中 26 个英文小写字母第一次出现的顺序作为替换表中的字母 顺序 。 将替换表与普通英文字母表对齐&#xff0c;…

模型量化I—基础概念

最近接了个模型量化的任务&#xff0c;读了几篇比较新的论文&#xff0c;并跑通了几个模型。因为第一次接触这个领域&#xff0c;没有理论基础打底&#xff0c;直接读“大成”性的论文&#xff0c;常常有种云山雾罩的感觉&#xff0c;如空中楼阁&#xff0c;不知道每个结论的来…

java常用类: String,StringBuffer和StringBuilder

java常用类型: Ineteger等包装类 String类&#xff0c;StringBuffer类和StringBuilder类 Math类及常用方法 System类及常用方法 Arrays类及常用方法 BigInteger类和BigDecimal类及常用方法 日期类Date类,Calender类和LocalDateTime类 文章目录String介绍String创建细节String s…

转投高通平台,大众展露主导自动驾驶的野心

/ 导读 /2022年&#xff0c;英特尔几乎以亏本的价格推动Mobileye上市&#xff0c;成为市场上的一大看点。出现这种情况的原因&#xff0c;除了整个美国自动驾驶大环境不好&#xff0c;和Mobileye接连失去几个大客户不无关系。其中尤以大众这样体量的客户对于Mobileye的影响最为…

Ubuntu 系统如何以 root 用户SSH登录实例

文章目录前言总结前言 出了点小插曲&#xff0c;心情烦躁&#xff0c;搞得Ubuntu都连不上了&#xff0c;心态炸裂。 不过后面冷静下来以后还是找到了解决办法&#xff0c;可以用ssh成功连上root权限的Ubuntu。 因为很少接触Ubuntu&#xff0c;所以不知道即使root的密码正确…

09技术太卷我学APEX-定制页面及导航菜单权限

09技术太卷我学APEX-定制页面及导航菜单权限 0 始终没搞明白APEX的角色如果分配页面的权限&#xff0c;只能自己定制一个 APEX现学现卖开发了个《5217仓库管理》&#xff0c;功能在春节前就搞定了&#xff0c;卡在对页面的权限控制&#xff0c;经过翻阅牛人的博客&#xff0c…

使用vue3,vite,less从零开始学习硅谷外卖.docx

严正声明&#xff01; 重要的事情说三遍&#xff0c;本文章仅供分享&#xff0c;文章和代码都是开源的&#xff0c;严禁以此牟利&#xff0c;严禁侵犯尚硅谷原作视频的任何权益&#xff0c;我知道学习编程的人各种各样的心思都有&#xff0c;但这不是你对开源社区侵权的理由&am…

读书:《高效的秘密》

#《高效的秘密》的作者是《纽约时报》商业调查记者查尔斯都希格&#xff0c;他的另一本畅销书是《习惯的力量》。 高效不是超额工作、拼命工作&#xff0c;甚至牺牲自己的个人生活。高效的秘密取决于你做选择的方式。 一、激发动力&#xff0c;重新审视效率产生的源头 掌控力…

1.7 Cubemx STM32F429_RTX FATFS 库函数讲解(三)

文章目录1、打开文件夹2、读取文件夹3、打开\新建一个文件5、读取文件1、打开文件夹 FRESULT f_opendir ( DIR* DirObject, /* Pointer to the blank directory object structure */ const TCHAR* DirName /* Pointer to the directory name */ ) 函数说明: 此函数可以打开…

React中如何使用Mobx

一、Mobx前端状态管理框架 基础概念&#xff1f; 1. 什么是Mobx Mobx是一个简单、可扩展的状态管理库 2. 什么是状态管理&#xff1f; 状态管理就是将分布在各个组件、各个模块中的状态的变化&#xff0c;按照一定的规则&#xff0c;进行统一的管理。 3. 为什么需要状态管…

NX二开ufun函数创建块/基准平面/凸台/垫块/腔体

本节主要讲述通过ufun函数直接创建块、基准平面、凸台、垫块、腔体&#xff0c;涉及ufun函数如下&#xff1a; 1、创建块 UF_MODL_create_block 2、创建基准平面 UF_MODL_create_fixed_dplane 3、创建凸台 UF_MODL_create_boss 4、创建垫块 UF_MODL_create_rect_pad5 5、创…

聊聊如何成为更好的架构师?

之前有小伙伴咨询架构方向需要学习什么&#xff0c;需要什么技术储备&#xff0c;今天就来深入聊聊这个话题。 就此探讨下必备技能、经验&#xff0c;以及储备相关知识所需的时间和精力。除此之外&#xff0c;我也回顾了自己走过的路、使用或尝试过的技术&#xff0c;以及我从…

开学季,送什么给小学生实用?学生最实用的护眼好物

伴随着“立春”&#xff0c;又到新的春季开学季&#xff0c;不管是家长还是孩子面对每个学期的循环&#xff0c;每一次都有新的故事和发现&#xff0c;很快就要开学了&#xff0c;给学生的小礼物准备好了吗&#xff0c;作为父母、长辈送给孩子最好的礼物&#xff0c;我认为莫过…

大数据舆情分析软件实时监控,TOOM大数据处理与舆情监控简介

舆情数据分析处理是指通过使用大数据技术、人工智能、自然语言处理等&#xff0c;从舆情数据中提取信息&#xff0c;进行模型建立、模式识别、情感分析等&#xff0c;从而了解舆论情况。舆情数据分析处理的目的是了解舆论趋势、话题热点、网民情绪等&#xff0c;从而便于企业、…