本周我主要实现了将相机的拍照功能集成到app中,拍取图片后进行图像识别与文字提取,并对提取到的英文文本翻译为中文。

一、配置 Android 项目的依赖仓库

repositories {
   maven { url=uri ("https://www.jitpack.io")}
        maven { url=uri ("https://maven.aliyun.com/repository/releases")}
        maven { url=uri ("https://maven.aliyun.com/repository/google")}
        maven { url=uri ("https://maven.aliyun.com/repository/central")}
        maven { url=uri ("https://maven.aliyun.com/repository/gradle-plugin")}
        maven { url=uri ("https://maven.aliyun.com/repository/public")}
        gradlePluginPortal()
        google()
        mavenCentral()
    }

我在settings.gradle中添加了JitPack 仓库、阿里云的 Maven 仓库、Gradle 插件门户、Maven 中央仓库和Google 仓库。通过添加这些仓库,我们可以在 Android 项目中使用各种依赖库,从而提高开发效率和项目质量。这些仓库提供了丰富的依赖库,包括 Android SDK、第三方库、Gradle 插件等。

二、添加依赖使用CameraX和ML Kit

def camerax_version = "1.3.0"
    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-view:${camerax_version}"

    implementation "androidx.compose.material:material-icons-extended:1.4.1"

 CameraX 是 Jetpack 中的一个库,旨在简化相机应用的开发,它提供了一致且易用的 API,同时解决了设备兼容性问题。在我们的项目中,我主要用CameraX库实现图片拍摄功能、实时相机预览功能并对生命周期进行绑定和管理,自动管理相机的开启/关闭,避免资源泄漏。

implementation 'com.google.mlkit:text-recognition:16.0.0'
    implementation 'com.google.mlkit:translate:17.0.1'

ML Kit 是 Google 提供的移动端机器学习套件,提供即用型(on-device)和云端(cloud)API。我选择使用的是on-device API,主要实现OCR技术以及模型自动下载与文本翻译功能。

二者运行机制数据流如下:
CameraX捕获图像 → ML Kit识别文本 → ML Kit翻译文本 → 显示结果

三、权限申请与处理提示功能实现

1.AndroidManifest.xml配置使用照相机特性和权限

 <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />

2.申请使用拍照的权限

val cameraPermissionState = rememberPermissionState(
    permission = Manifest.permission.CAMERA,
    onPermissionResult = { isGranted ->
        if (!isGranted) {
            Toast.makeText(context, "需要相机权限才能使用此功能", Toast.LENGTH_LONG).show()
        }
    }
)

使用rememberPermissionState函数申请权限,该函数的参数为permission,表示需要申请的权限。rememberPermissionState函数的返回值为PermissionState类型,表示获取权限的状态。

3.定义未授权的界面

override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera()
            } else {
                Toast.makeText(requireContext(), "需要相机权限", Toast.LENGTH_SHORT).show()
            }
        }
    }

4.Toast轻量反馈机制的使用:在Android开发中,"Toast"是一种轻量级的用户反馈机制,用于向用户显示一个浮动的提示信息。它通常用于提供操作成功、警告或错误信息等短暂的通知。Toast消息会显示在屏幕的中间,并且会在几秒钟后自动消失。

四、结合PreviewView实现预览相机画面

我使用AndroidView 嵌入原生 PreviewView的方式实现这一功能。PreviewView是Jetpack CameraX库提供的控件,它可以协助在Android应用中显示相机预览。但PreviewView并不是Compose组件,而是传统的View系统的组件。通过这种方式,可以将处于活动状态的相机中的图片预览流式传输到PreviewView中的Surface。

+------------------+
|     View         |
+------------------+
         ^
         |
+------------------+
|    ViewGroup      |
+------------------+
         ^
         |
+------------------+
|   FrameLayout     |
+------------------+
         ^
         |
+------------------+
|   PreviewView     |
+------------------+

五、拍照的处理 

CameraX提供了拍照的功能,通过调用takePicture来实现拍照。takePicture函数有两种形式:

takePicture(Executor, OnImageCapturedCallback):此方法为拍摄的图片提供内存缓冲区。

takePicture(OutputFileOptions, Executor,OnImageSavedCallback):此方法将拍摄的图片保存到提供的文件位置。 运行ImageCapture可自定义执行程序有两种类型:回调执行程序和 IO 执行程序。

我选择调用第一种方法,代码如下:

private fun captureImage(
    imageCapture: ImageCapture?,
    context: Context,
    onDetectedText: (String) -> Unit,
    onTranslatedText: (String) -> Unit
)

imageCapture1.takePicture(
    ContextCompat.getMainExecutor(context),
    object : ImageCapture.OnImageCapturedCallback() {
        override fun onCaptureSuccess(image: ImageProxy) {
            processImage(image, context, onDetectedText, onTranslatedText)
        }
        
        override fun onError(exception: ImageCaptureException) {
            Toast.makeText(context, "拍照失败", Toast.LENGTH_SHORT).show()
        }
    }
)

六、图片识别功能的实现

1. 此功能我是通过Google提供的ML Kit实现的。ML Kit的Text Recognition可以实现OCR技术。

val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
recognizer.process(image)
    .addOnSuccessListener { visionText ->
        val detectedText = visionText.text
        binding.detectedText.text = detectedText
    }

2.生命周期的绑定: 在 Android 开发中,当处理相机资源时,需要进行生命周期的绑定。这在之前的学习过程中是从未碰到过的问题,因此对于我而言也是一个提升自身技术水平与完善知识体系的好机会。

绑定到生命周期是指将某个组件或资源的生命周期与 Activity 或 Fragment 的生命周期关联起来。这样当 Activity/Fragment 的生命周期状态发生变化时(如暂停、恢复、销毁),这些组件或资源也会自动进行相应的处理,从而避免内存泄漏,节省资源。

部分代码如下:

cameraProvider.bindToLifecycle(
    lifecycleOwner,  // 生命周期拥有者
    cameraSelector,  // 相机选择器
    preview,        // 预览用例
    imageCapture    // 图像捕获用例
)
cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(
                this,
                cameraSelector,
                preview,
                imageCapture
            )

 七、文本翻译功能:

1.创建翻译器

val options = TranslatorOptions.Builder()
                        .setSourceLanguage(com.google.mlkit.nl.translate.TranslateLanguage.ENGLISH)
                        .setTargetLanguage(com.google.mlkit.nl.translate.TranslateLanguage.CHINESE)
                        .build()
                    val translator = Translation.getClient(options)
                    

2. 下载翻译模型:

translator.downloadModelIfNeeded()
                        .addOnSuccessListener {
                            // 开始翻译
                            translator.translate(detectedText)
                                .addOnSuccessListener { translatedText ->
                                    binding.translatedText.text = translatedText
                                }
                                .addOnFailureListener {
                                    Toast.makeText(requireContext(), "翻译失败", Toast.LENGTH_SHORT).show()
                                }
                        }
                        .addOnFailureListener {
                            Toast.makeText(requireContext(), "翻译模型下载失败", Toast.LENGTH_SHORT).show()
                        }

 同时添加翻译失败与模型下载失败的提示,提高代码健壮性,用户使用也更便捷。

翻译模型下载的是Google 提供的神经网络机器翻译模型(NMT模型),这是一种基于 Transformer 架构的神经网络模型,支持55种语言之间的互译,并且经过了8bits量化处理,减小了模型的参数量,可以用于移动设备。当首次下载应用或模型版本有所更新时,此代码会自动下载最新版本的模型到内存中使用。在我们的app中,我限定了只允许中英文切换,当应用卸载时会自动清除模型。

八、资源释放与回收:

1.相机资源释放

override fun onDestroyView() {
    super.onDestroyView()
    cameraExecutor.shutdown()  // 关闭相机线程池
    _binding = null  // 清除视图绑定
}

2.ML Kit识别器资源释放: 

recognizer.process(image)
    .addOnCompleteListener {
        imageProxy.close()  // 关闭ImageProxy
    }

3.图像资源释放:

这是最重要的资源释放点,ImageProxy包装了相机帧缓冲区,不释放会导致内存泄漏和相机功能异常。

.addOnCompleteListener {
    imageProxy.close()  // 必须手动关闭
}

4.线程池管理与释放:

 避免后台线程泄漏

private lateinit var cameraExecutor: ExecutorService

override fun onDestroyView() {
    cameraExecutor.shutdown()  // 有序关闭线程池
}

 5.View Binding清理:

private var _binding: LayoutImageTranslationBinding? = null
private val binding get() = _binding!!

override fun onDestroyView() {
    _binding = null  // 防止内存泄漏
}

资源释放流程图如下:

Fragment.onViewCreated()
       │
       ├─ 初始化相机 → 绑定生命周期
       │
Fragment.onDestroyView()
       │
       ├─ 1. 关闭相机线程池 (cameraExecutor.shutdown())
       ├─ 2. 清除视图绑定 (_binding = null)
       └─ 3. 自动释放: 
           - 相机资源 (通过生命周期绑定)
           - ML Kit识别器 (GC回收)
           - 翻译器实例 (GC回收)
           
图像处理流程:
takePhoto() → onCaptureSuccess()
       │
       ├─ processImage() → 识别完成
       │   │
       │   └─ .addOnCompleteListener → imageProxy.close()
       │
       └─ 错误处理 → 自动资源清理

最后效果图如下: 

更多推荐