news 2026/2/7 15:21:06

Android AI 实战:手把手教你用 Compose + CameraX 跑通 YOLOv8

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android AI 实战:手把手教你用 Compose + CameraX 跑通 YOLOv8

关键词:Android, Jetpack Compose, CameraX, TFLite, Object Detection

大家好,我是飞哥!👋

拒绝云端依赖!3MB 模型跑在手机上,TFLite 转换保姆级教程我们成功把 YOLOv8 模型“压缩”成了tflite格式。今天,我们要把它装进 Android 手机,开发一个酷炫的实时物体检测 App

想象一下,打开摄像头,对着家里的猫、杯子、电脑,手机屏幕上立马画出框框,并显示“Cat 99%”——是不是很有科技感?😎


1. 为什么要用 Jetpack Compose + CameraX?(Why)

锚定已知 ⚓️

以前做 Android 相机预览,要写 SurfaceView, TextureView,还要处理各种旋转、拉伸问题,代码又臭又长。
就像用古老的胶卷相机,拍张照片要调半天光圈快门。

生动类比 📷

Jetpack Compose + CameraX 就是 Android 界的“傻瓜数码相机”!

  • Compose:就像 Vue/React,你告诉它“我要一个相机预览窗口”,它就给你画出来。不用管底层的 XML 布局。
  • CameraX:Google 帮你把底层的脏活累活(设备兼容性、生命周期)全干了。你只需要关注两件事:“给我画面” (Preview)“给我数据” (ImageAnalysis)

提炼骨架 🦴

核心数据流如下:
摄像头 (CameraX) ➡️ 每一帧图片 (ImageAnalysis) ➡️ AI 大脑 (TFLite) ➡️ 结果坐标 ➡️ 绘制 (Compose Canvas)


2. 核心代码拆解 (How)

第一步:添加依赖与权限 (build.gradle.kts & Manifest) 📦

  1. app/build.gradle.kts(Kotlin DSL) 里加上这些:
dependencies{// 1. CameraX (相机库)valcamerax_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")// 2. TensorFlow Lite (AI 推理库)implementation("org.tensorflow:tensorflow-lite:2.14.0")implementation("org.tensorflow:tensorflow-lite-support:0.4.4")}
  1. AndroidManifest.xml中申请相机权限:
<uses-featureandroid:name="android.hardware.camera.any"/><uses-permissionandroid:name="android.permission.CAMERA"/>

⚠️ 注意:Android 6.0 以上需要动态申请权限!在 Compose 中可以使用Accompanist库或者ActivityrequestPermissions来搞定。如果不申请,App 会直接闪退。

第二步:核心分析器 (Analyzer) 🧠

这是最关键的一步。我们需要自定义一个ImageAnalysis.Analyzer,它会不断收到摄像头的图片帧。

为了简化繁琐的图片处理(YUV转RGB、缩放、归一化),强烈推荐使用TFLite Support Library

classObjectDetectorAnalyzer(privatevalcontext:Context,privatevalonResult:(List<DetectionResult>,Int,Int)->Unit// 回调:结果, 宽, 高):ImageAnalysis.Analyzer{// 1. 加载模型privatevalinterpreter:Interpreterbylazy{valmodel=FileUtil.loadMappedFile(context,"yolov8n.tflite")valoptions=Interpreter.Options()Interpreter(model,options)}// 图像预处理:调整大小 + 归一化privatevalimageProcessor=ImageProcessor.Builder().add(ResizeOp(640,640,ResizeOp.ResizeMethod.BILINEAR)).add(NormalizeOp(0f,255f)).build()@androidx.annotation.OptIn(ExperimentalGetImage::class)overridefunanalyze(imageProxy:ImageProxy){valmediaImage=imageProxy.imageif(mediaImage!=null){// 2. 转换为 Bitmapvalbitmap=imageProxy.toBitmap()// 3. 核心步骤:裁剪为正方形 (Center Crop) ✂️// 必须裁剪!因为 YOLO 模型输入是正方形,而我们为了 UI 显示不拉伸,// 也在 UI 上强制显示为正方形。这里必须保持一致。valsize=minOf(bitmap.width,bitmap.height)valleft=(bitmap.width-size)/2valtop=(bitmap.height-size)/2valsquareBitmap=Bitmap.createBitmap(bitmap,left,top,size,size)// 4. 预处理 (TensorImage)vartensorImage=TensorImage.fromBitmap(squareBitmap)tensorImage=imageProcessor.process(tensorImage)// 5. 准备输出容器 [1, 84, 8400]valoutputBuffer=TensorBuffer.createFixedSize(intArrayOf(1,84,8400),DataType.FLOAT32)// 6. 推理interpreter.run(tensorImage.buffer,outputBuffer.buffer.rewind())// 7. 后处理 (NMS)valresults=postProcess(outputBuffer.floatArray)// 8. 传回 UI (附带裁剪后的尺寸,用于坐标映射)onResult(results,size,size)}imageProxy.close()}// 💡 飞哥小贴士:// postProcess 里面要做三件事:// 1. 维度转置:把 [84, 8400] 转成 [8400, 84] 方便遍历。// 2. 阈值过滤:置信度 < 0.5 的框直接扔掉。// 3. NMS (非极大值抑制):同一个物体可能有很多重叠框,只保留分最高的那个。}

补充:关于toBitmap

虽然 CameraX 提供了toBitmap(),但性能一般。生产环境建议使用YuvToRgbConverter(Google Sample 中有提供) 实现 GPU 加速转换。

第三步:Compose UI 界面 🎨

用 Compose 写界面简直是享受。我们把相机预览和画框图层叠在一起。

@ComposablefunObjectDetectionScreen(){// 权限请求逻辑 (略) ...// 如果有权限,显示 CameraContent()CameraContent()}@ComposablefunCameraContent(){valcontext=LocalContext.currentvallifecycleOwner=LocalLifecycleOwner.currentvardetectionsbyremember{mutableStateOf(emptyList<DetectionResult>())}varimageWidthbyremember{mutableIntStateOf(640)}// 裁剪后的正方形边长varimageHeightbyremember{mutableIntStateOf(640)}// 重点:强制 UI 显示为正方形 (1:1),与模型输入保持一致Box(modifier=Modifier.fillMaxWidth().aspectRatio(1f)){// 1. 相机预览层 (AndroidView + CameraX)AndroidView(factory={ctx->valpreviewView=PreviewView(ctx)valcameraProviderFuture=ProcessCameraProvider.getInstance(ctx)cameraProviderFuture.addListener({valcameraProvider=cameraProviderFuture.get()// 配置 Previewvalpreview=Preview.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).build()preview.setSurfaceProvider(previewView.surfaceProvider)// 配置 AnalyzervalimageAnalyzer=ImageAnalysis.Builder().setTargetAspectRatio(AspectRatio.RATIO_4_3).setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST).build().also{it.setAnalyzer(Executors.newSingleThreadExecutor(),ObjectDetectorAnalyzer(ctx){results,w,h->detections=results imageWidth=w imageHeight=h})}// 关键点:配置 ViewPort (1:1 视口)// 告诉 CameraX 只输出正方形画面,自动裁剪掉多余部分valviewPort=ViewPort.Builder(Rational(1,1),Surface.ROTATION_0).build()valuseCaseGroup=UseCaseGroup.Builder().addUseCase(preview).addUseCase(imageAnalyzer).setViewPort(viewPort)// 绑定视口.build()try{cameraProvider.unbindAll()cameraProvider.bindToLifecycle(lifecycleOwner,CameraSelector.DEFAULT_BACK_CAMERA,useCaseGroup// 绑定 UseCaseGroup 而不是单独的 use cases)}catch(e:Exception){Log.e("Camera","Binding failed",e)}},ContextCompat.getMainExecutor(ctx))previewView},modifier=Modifier.fillMaxSize())// 2. 绘图层 (Canvas)Canvas(modifier=Modifier.fillMaxSize()){// 坐标映射:屏幕显示尺寸 / 图片实际尺寸valscale=size.width/imageWidth detections.forEach{result->// 将模型输出坐标映射到屏幕坐标valleft=result.x*scalevaltop=result.y*scalevalwidth=result.w*scalevalheight=result.h*scale// 画框drawRect(color=Color.Red,topLeft=Offset(left,top),size=Size(width,height),style=Stroke(width=5f))// 画文字...}}}}

效果演示:

⚠️ 飞哥避坑指南

  1. 坐标转换:模型输出的坐标是基于 640x640 的,画到屏幕上时,一定要根据屏幕实际宽高进行等比例映射,否则框会歪到姥姥家去。
  2. 线程问题analyze方法运行在子线程,更新 UI (detections = results) 一定要切回主线程
  3. YUV 转 Bitmap:CameraX 给的是 YUV_420_888 格式,直接转 Bitmap 很慢。推荐用 Google 的YuvToRgbConverter或者 TFLite Support 库里的ImageProcessor

4. 总结 📝

一句话记住它
Android 端侧推理 = CameraX 喂图 ➕ TFLite Support 预处理 ➕ YOLO 解析。

核心三要点

  1. CameraX Analyzer:每一帧图片的入口,要在这里做线程切换和数据处理。
  2. TFLite Support:Google 提供的神器,一行代码搞定缩放和归一化,别再手写 Bitmap 操作了。
  3. 后处理 (NMS):模型输出的只是候选框,必须通过 NMS 算法去重,才能得到最终结果。

🎁 示例源码

实战代码

源码包内含:

  • ✅ 完整的 Android Studio 工程
  • ✅ 已封装好的YoloV8Detector.kt(含 NMS 算法实现)
  • ✅ 预训练好的 TFLite 模型

创作不易,记得👇关注飞哥👇 ,点赞、收藏哦~~,下篇见👋

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/5 9:04:19

【SOLIDWORKS 练习题】草图专题:3.机械臂

&#x1f525;小龙报&#xff1a;个人主页 &#x1f3ac;作者简介&#xff1a;C研发&#xff0c;嵌入式&#xff0c;机器人等方向学习者 ❄️个人专栏&#xff1a;《SOLIDWORKS 练习题》 ✨ 永远相信美好的事情即将发生 文章目录前言一、机械臂1.1 题目来源1.4 绘制结果&#x…

作者头像 李华
网站建设 2026/2/4 17:40:17

【开题答辩全过程】以 基于ssm的社区留守儿童身心健康帮扶系统为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人&#xff0c;语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/2/3 17:23:41

【信创】华为昇腾NLP算法训练

1. 项目概述 目标&#xff1a;在国产信创硬件上训练长文本分类模型&#xff0c;并部署 API 提供推理服务任务类型&#xff1a;多类别/二分类 NLP 问题输入数据&#xff1a;长文本&#xff08;如 2000 token&#xff09;输出&#xff1a;文本类别预测硬件环境&#xff1a; 2 A…

作者头像 李华
网站建设 2026/2/3 16:33:49

用户态热补丁技术深度解析:构建原理、适用场景与操作指南

引言 在Linux系统运维中&#xff0c;热补丁技术因其"零中断"修复特性成为关键技术。本文聚焦用户态热补丁技术&#xff0c;结合SysCare、LibcarePlus等开源方案&#xff0c;系统解析其技术原理、实施方法及注意事项&#xff0c;为运维人员提供可落地的技术指南。 一、…

作者头像 李华
网站建设 2026/2/6 6:10:04

基于SpringBoot的网上宠物店系统毕设源码

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。 一、研究目的 本研究旨在设计并实现一个基于SpringBoot框架的网上宠物店系统&#xff0c;以满足现代电子商务环境下宠物行业的需求。具体研究目的如下&#xff1a; 提升用…

作者头像 李华
网站建设 2026/2/7 14:23:49

基于SpringBoot的课程设计选题管理系统毕业设计源码

博主介绍&#xff1a;✌ 专注于Java,python,✌关注✌私信我✌具体的问题&#xff0c;我会尽力帮助你。一、研究目的本研究旨在设计并实现一个基于SpringBoot框架的课程设计选题管理系统&#xff0c;以满足高校课程设计教学过程中的选题、申报、审核、分配以及跟踪等环节的需求。…

作者头像 李华