![mlkit](mlkit.jpeg)
随着移动设备的普及和摄像头的高像素化,利用相机进行文本识别成为了一种流行的方式。MLKit 是 Google 提供的一款机器学习工具包,其中包含了丰富的图像和语言处理功能,包括文本识别。PreviewView 是 Android Jetpack 的一部分,它提供了一个方便的预览相机图像的视图组件。结合 MLKit 和 PreviewView,我们可以轻松构建出一个功能强大的文本识别应用程序。
添加依赖
为了使用 MLKit 和 PreviewView,我们需要在项目的 build.gradle
文件中添加相应的依赖项。以下是所需的依赖项:
1 2 3 4 5 6 7 8 9 10 11
| def camerax_version = "1.2.1" implementation "androidx.camera:camera-core:${camerax_version}" implementation "androidx.camera:camera-camera2:${camerax_version}" implementation "androidx.camera:camera-lifecycle:${camerax_version}" implementation "androidx.camera:camera-video:${camerax_version}" implementation "androidx.camera:camera-view:${camerax_version}" implementation "androidx.camera:camera-extensions:${camerax_version}"
implementation 'com.google.mlkit:text-recognition-chinese:16.0.0'
|
以上依赖项包含了与相机操作和中文文本识别相关的库。
XML 布局
在布局文件中,我们需要添加一个 PreviewView(相机预览视图),一个按钮用于开始/停止文本识别,以及一个用于显示识别结果的 TextView。以下是布局文件的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity">
<androidx.camera.view.PreviewView android:id="@+id/pre_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
<Button
android:id="@+id/btn_operation" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="点击停止文本识别" android:layout_marginHorizontal="16dp" />
<TextView android:id="@+id/tv_content" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:padding="6dp" />
</LinearLayout>
|
上述布局文件包含了一个垂直排列的 LinearLayout,其中包含了一个 PreviewView、一个按钮和一个用于显示识别结果的 TextView。
代码实现
在代码实现部分,首先检查相机权限
1 2 3
| <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.CAMERA"/> </manifest>
|
在权限被授予时初始化相机,进行文本识别。我们设置按钮的点击事件监听器,根据当前相机的状态执行相应的操作。当按钮被点击时,我们会根据相机的状态开始或停止文本识别,默认处于识别状态中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var cameraProvider: ProcessCameraProvider? = null
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) requestPermission()
binding.btnOperation.setOnClickListener { cameraProvider?.let { binding.btnOperation.text = "点击开始文本识别" cameraProvider?.unbindAll() cameraProvider = null } ?: run { binding.btnOperation.text = "点击停止文本识别" setupCamera() } } }
}
|
在 onCreate()
方法中,我们设置了按钮的点击事件监听器。当按钮被点击时,我们根据当前的相机状态执行相应的操作。如果相机已经初始化并正在运行,我们会停止文本识别并释放相机资源。如果相机未初始化或已停止,我们将开始文本识别并设置相机。
接下来,我们实现了请求相机权限的方法 requestPermission()
,并在 onCreate()
方法中调用它。在 onRequestPermissionsResult()
方法中,我们检查相机权限的授权结果。如果权限被授予,我们将调用 setupCamera()
方法初始化相机。如果权限被拒绝,我们将显示一个简短的提示消息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
private fun requestPermission() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), CAMERA_PERMISSION_CODE) } else { setupCamera() } }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == CAMERA_PERMISSION_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { setupCamera() } else { Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show() } } }
|
在 requestPermission()
方法中,我们检查相机权限并请求授权。如果权限已被授予,我们将调用 setupCamera()
方法初始化相机。
1 2 3 4 5 6 7 8 9 10
|
private fun setupCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() bindPreview(cameraProvider) }, ContextCompat.getMainExecutor(this)) }
|
在 setupCamera()
方法中,我们使用 ProcessCameraProvider 获取相机实例,并通过 bindPreview()
方法将相机与 PreviewView 绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
private fun bindPreview(cameraProvider: ProcessCameraProvider) { this.cameraProvider = cameraProvider val preview = Preview.Builder().build() val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA preview.setSurfaceProvider(binding.preView.surfaceProvider
) val analysis = ImageAnalysis.Builder() .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_YUV_420_888) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build() analysis.setAnalyzer(Executors.newSingleThreadExecutor(), this::analyzeImage) cameraProvider.bindToLifecycle(this, cameraSelector, preview, analysis) }
|
在 bindPreview()
方法中,我们创建了一个 Preview 实例,并将其与默认后置摄像头绑定。然后,我们设置 PreviewView 的 SurfaceProvider,并创建一个 ImageAnalysis 实例用于图像分析。通过设置图像分析器的回调方法,我们可以在每帧图像上执行文本识别。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
@SuppressLint("UnsafeOptInUsageError") private fun analyzeImage(imageProxy: ImageProxy) { val image = imageProxy.image ?: return val inputImage = InputImage.fromMediaImage(image, imageProxy.imageInfo.rotationDegrees) val recognizer = TextRecognition.getClient(ChineseTextRecognizerOptions.Builder().build()) recognizer.process(inputImage) .addOnSuccessListener { result -> binding.tvContent.text = result.text } .addOnCompleteListener { imageProxy.close() } .addOnFailureListener { it.printStackTrace() imageProxy.close() } }
|
通过实现了 analyzeImage()
方法,用于分析图像并执行文本识别。在该方法中,我们首先将 ImageProxy 转换为 InputImage,然后创建一个中文文本识别器。接下来,我们使用识别器对图像进行处理,并在成功完成时更新 TextView 的内容。无论成功与否,最后都会关闭 ImageProxy。这里如果我们想识别图片(Bitmap)中的文字可以调用 InputImage.fromBitmap
方法即可。
演示
Ok,到这里我们文本识别的功能 demo 就实现了, 看看效果吧:
![demo](textreconition.gif)
总结
通过结合 MLKit 和 PreviewView,我们可以轻松实现 Android 应用程序中的文本识别功能。在本篇文章中,我们详细讲解了如何使用 MLKit 和 PreviewView 实现文本识别。感兴趣的小伙伴可参考 Demo 地址:TextRecognition