身份证扫描主要需要用到文字识别技术(OCR)。这类技术方案已经很多了,本文介绍基于 CameraX + MLKit 的实现方式。其中 CameraX 用来实现相机的取景和预览,MLKit 用来进行图片中的文字识别。 自 5.0 开始引入了全新的相机框架 Camera2 ,相较于之前的 Camera1 对多摄像头的支持更加友好,功能更加强大,但使用成本也更高。此背景下谷歌发布了 CameraX,它基于 Camera2 封装,大大提高了 API 的易用性。我们可以用很少的代码搭建出面向特定场景的相机应用,OCR 就是一种典型的相机应用场景 。 CameraX 引入 UseCase 的概念完成各种相机能力,UseCase 有利于功能模块的解耦,聚焦特定领域进行功能开发。CameraX 默认提供了几个常用的 UseCase 实现,能够满足大多数场景下的使用 Preview : 提供相机取景和预览 ImageCapture:拍照并保存图片 ImageAnalysis:处理预览帧图片 本文 OCR 场景中将会使用到 Preview 和 ImageAnalysis 这两个 UseCase。Preview 帮助我们实现相机的取景和预览,ImageAnalysis 帮助我们将采集的图片送入 OCR 分析。 接下来让我们使用 CameraX 一步步完成相机预览功能 首先,在 Gradle 中引入 CameraX 相关库如下 另外,需要使用相机,所以在 AndroidManifest 中申请相机权限 CameraX 通过 ProcessCameraProvider 访问相机实例。顾名思义,ProcessCamera 表示每个 Application Process 期间可使用的相机服务,所以 ProcessCameraProvider 是一个进程单例,通过 getInstance 创建并获取。创建是一个异步过程,所以借助 CameraProviderFuture 异步返回: 在 Runnable 中成功获取 ProcessCameraProvider 单例,接下来可以用它来组装 UseCase ,实现相机功能了。 CameraX 的一个重要特征是 LifecycleAware,相机可以根据应用的前后台情况自动开启或关闭,降低开发者的心智负担。ProcessCameraProvider 添加 UseCase 时会关联 LifecycleOwner。 UseCase 根据 Lifecycle 调用 onStateAttached / onStateDetatched,当我们自定义 UseCase 时,可以在这里进行一些自定义前 / 后处理。 如上,ProcessCameraProvicer#bindToLifecycle 添加 Preview 。 Preview UseCase 的创建非常简单,如下: 创建 Preview 的关键是设置渲染用的 Surface,这是通过 PreviewView 获取的。 PreviewView 是 CameraX 提供的用于显示相机预览流的自定义 View,它内部可以根据需要切换 TexureView 或者 SurfaceView。 SurfaceView 有更好的性能,但在 Android 7.0 之前无法实现旋转、透明、动画等常规自定义 View 的能力,此时需要使用 TextureView 替代。PreviewView 默认使用性能优先的 SurfaceView,如果如果需要其有更好的兼容性,则可以设置 previewView.implementationMode = PreviewView.ImplementationMode.COMPATIBLE 我们可以像下面这样在 xml 中布局使用 PreviewView 如果我们使用 Compose 渲染 UI ,可以借助 AndroidView 显示 PreviewView,Compose 展示相机预览的代码大体如下所示: MLKit 是谷歌的面向移动端开发者的机器学习库,帮助移动应用在离线状态下使用各种端智能技术,例如: 智能视觉处理:二维码扫描、文字识别、人脸检测、物体捕捉等; 自然语言处理:语言识别、智能回复、自动翻译等 这些端上的技术让应用变得更加智能的同时依然保持高性能,更重要的是这一切都是免费的,且不依赖 GMS(Google Mobile Service)。 本文我们主要使用到 MLKit 的文字识别功能,只需要添加以下依赖即可: text-recognition-chinese 可以识别中文字符,另外也有其他的 Artifact 可以识别日文韩文等非拉丁系的语言。 前面我们通过 Preview 实现了相机预览,接下来我们为 CameraProvider 添加 ImageAnalysis ,它可以接收相机的预览帧用于图像分析和处理。 setBackpressureStrategy 是设置预览帧的生产消费的缓冲策略,其默认值 ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST 表示在每一帧没有分析结束之前,新的渲染帧会自动丢弃,避免排队。 ImageAnalysis#setAnalyzer 添加自定义图像分析器,这里我们定义一个 OcrAnalyzer,它基于 MLKit 实现 OCR 功能。 ImageAnalysis.Analyzer 返回的 ImageProxy 中包含了预览帧信息: imageProxy.image:图像信息 ImageInfo.rotationDegrees:根据设备情况获得的图片旋转角度。 InputImage.fromMediaImage 根据这两个参数获取具体的 InputImage,后者提交 recognition 处理。这里的 recognition 是一个可识别中文的 TextRecognition。 经过 TextRecognition 文字识别后将返回 Block / Line / Element 这样的数据结构,这种结构有利于进一步细粒度的解析。 Block 代表一个自然段落,由若干 Line(行) 组成,每一个 Line 又包含多个 Element(单词) 。 假设我们希望从身份证中获取姓名以及身份证号,虽然不确定身份证这样的排版会被识别为怎样的 Block,但是姓名和身份证号肯定处于不同 Line 中。我们定义 extractText 方法,将所有的 Block 下的 Line 聚合到一起,统一进行解析: 成功识别文字后的效果如下: 透过文字识别这样一个小的应用场景,我们切实感受到了 CameraX 以及 MLKit 开箱即用般的的易用性。作为谷歌官方工具包,它们还与 Compose 等其他 Jetpack 组件有着不错的兼容性。感谢谷歌强大的开发者生态,让开发者们可以低成本地开发自己的移动应用。 CameraX:https://developer.android.com/training/camerax MLKit:https://developers.google.com/ml-kit 本文来自微信公众号:,作者:fundroid 1. CameraX 实现相机预览
1.1 CameraX 简介
1.2 工程引入 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"
<uses-permission android:name="android.permission.CAMERA" tools:ignore="PermissionImpliesUnsupportedChromeOsHardware" />
1.3 获取 ProcessCameraProvider
// 通过 cameraProviderFuture 异步返回创建的 ProcessCameraProvider 实例 val cameraProviderFuture = ProcessCameraProvider.getInstance(context) //监听 ProcessCameraProvider 获取成功 cameraProviderFuture.addListener( Runnable { //获取 cameraProvider val cameraProvider = cameraProviderFuture.get() ... }, ContextCompat.getMainExecutor(context) // Runnable 运行的 Executor )
1.4 添加 Preview UseCase
//选择后置镜头 val cameraSelector = CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build() //添加 Preivew UseCase cameraProvider.bindToLifecycle( lifecycleOwner, cameraSelector, preview )
val preview = Preview.Builder().build().ly { setSurfaceProvider(previewView.surfaceProvider) }
1.5 布局 PreviewView
<FrameLayout android:id="@+id/container"> <androidx.camera.view.PreviewView android:id="@+id/previewView" /> </FrameLayout>
@Composable fun CameraScreen() { //获取 ProcessCameraProvider val cameraProviderFuture = remember { ProcessCameraProvider.getInstance(context) } // 显示预览 AndroidView( modifier = Modifier.fillMaxSize(), factory = { ctx -> PreviewView(ctx).ly { 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 简介
2.2 工程引入 MLKit
implementation 'com.google.mlkit:text-recognition-chinese:16.0.0-6'
2.3 CameraX 实现图像分析
val imageAnalysis = ImageAnalysis.Builder) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build() .ly //设置图像分析器 setAnalyzer Executors.newSingleThreadExecutor(), OcrAnalyzer result: String - //基于 MLKit 处理 OCR,并返回 result cameraProvider.bindToLifecycle LocalLifecycleOwner.current, cameraSelector, preview, imageAnalysis // 增加 ImageAnalysis 能力,关联 Lifecycle
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() } } } }
2.5 解析 TextBlocks
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 "$namen$id" }
结束语
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 程序员专区