Android相机应用使用YOLOv5转TFLite模型启动崩溃问题排查
我用Kotlin开发一款目标检测APP,使用由YOLOv5转换而来的TensorFlow Lite模型,转换命令为:
python export.py --weights best.pt --include tflite --nms
模型输出张量详情:
Name: StatefulPartitionedCall:0
Shape: [ 1 100 4]
Type: <class 'numpy.float32'>
APP编译正常,但真机运行时闪屏后崩溃,报错信息:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.sightfulkotlin, PID: 23100
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sightfulkotlin/com.example.sightfulkotlin.MainActivity}: java.lang.IllegalStateException: Internal error: Unexpected failure when preparing tensor allocations: Regular TensorFlow ops are not supported by this interpreter. Make sure you apply/link the Flex delegate before inference.
Node number 402 (FlexCombinedNonMaxSuppression) failed to prepare.
MainActivity代码如下:
package com.example.sightfulkotlin import android.annotation.SuppressLint import android.content.Context import android.content.pm.PackageManager import android.graphics.* import android.hardware.camera2.CameraCaptureSession import android.hardware.camera2.CameraDevice import android.hardware.camera2.CameraManager import android.os.Bundle import android.os.Handler import android.os.HandlerThread import android.view.Surface import android.view.TextureView import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.example.sightfulkotlin.ml.ObjectDetection import org.tensorflow.lite.DataType import org.tensorflow.lite.support.common.FileUtil import org.tensorflow.lite.support.image.ImageProcessor import org.tensorflow.lite.support.image.TensorImage import org.tensorflow.lite.support.image.ops.ResizeOp import org.tensorflow.lite.support.tensorbuffer.TensorBuffer class MainActivity : AppCompatActivity() { var colors = listOf( Color.BLUE, Color.GREEN, Color.RED, Color.CYAN, Color.GRAY, Color.BLACK, Color.DKGRAY, Color.MAGENTA, Color.YELLOW, Color.LTGRAY, Color.WHITE) val paint = Paint() private lateinit var labels:List<String> private lateinit var cameraManager: CameraManager lateinit var cameraDevice: CameraDevice lateinit var handler: Handler lateinit var textureView: TextureView lateinit var model: ObjectDetection lateinit var bitmap: Bitmap lateinit var imageView: ImageView override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) getPermission() labels = FileUtil.loadLabels(this, "labels.txt") model = ObjectDetection.newInstance(this) var imageProcessor = ImageProcessor.Builder().add(ResizeOp(640, 640, ResizeOp.ResizeMethod.BILINEAR)).build() val handlerThread = HandlerThread("videoThread") handlerThread.start() handler = Handler(handlerThread.looper) paint.color = Color.GREEN imageView = findViewById(R.id.imageView) textureView = findViewById(R.id.textureView) textureView.surfaceTextureListener = object: TextureView.SurfaceTextureListener { override fun onSurfaceTextureAvailable(p0: SurfaceTexture, p1: Int, p2: Int) { openCamera() } override fun onSurfaceTextureSizeChanged(p0: SurfaceTexture, p1: Int, p2: Int) { } override fun onSurfaceTextureDestroyed(p0: SurfaceTexture): Boolean { return false } override fun onSurfaceTextureUpdated(p0: SurfaceTexture) { bitmap = textureView.bitmap!! var tensorImage = TensorImage(DataType.FLOAT32) tensorImage.load(bitmap) tensorImage = imageProcessor.process(tensorImage) val inputFeature0 = TensorBuffer.createFixedSize(intArrayOf(1, 640, 640, 3), DataType.FLOAT32) inputFeature0.loadBuffer(tensorImage.buffer) val outputs = model.process(inputFeature0) val locations = outputs.outputFeature0AsTensorBuffer.floatArray val scores = outputs.outputFeature1AsTensorBuffer.floatArray val classes = outputs.outputFeature2AsTensorBuffer.floatArray val numberOfDetections = outputs.outputFeature3AsTensorBuffer.floatArray val mutable = bitmap.copy(Bitmap.Config.ARGB_8888, true) val canvas = Canvas(mutable) val h = mutable.height val w = mutable.width paint.textSize = h/15f paint.strokeWidth = h/85f scores.forEachIndexed{index, fl -> var x = index x *= 4 if(fl > 0.5) { paint.color = colors[index] paint.style = Paint.Style.STROKE canvas.drawRect(RectF(locations[x+1] *w, locations[x] *h, locations[x+3] *w, locations[x+2] *h), paint) paint.style = Paint.Style.FILL canvas.drawText(labels[classes[index].toInt()] +" "+fl.toString(), locations[x+1] *w, locations[x] *h, paint) } } imageView.setImageBitmap(mutable) } } cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager } override fun onDestroy() { super.onDestroy() model.close() } @SuppressLint("MissingPermission") fun openCamera() { cameraManager.openCamera(cameraManager.cameraIdList[0], object: CameraDevice.StateCallback(){ @SuppressLint("MissingPermission") override fun onOpened(p0: CameraDevice) { cameraDevice = p0 val surfaceTexture = textureView.surfaceTexture val surface = Surface(surfaceTexture) val captureRequest = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(surface) cameraDevice.createCaptureSession(listOf(surface), object: CameraCaptureSession.StateCallback(){ override fun onConfigured(p0: CameraCaptureSession) { p0.setRepeatingRequest(captureRequest.build(), null, null) } override fun onConfigureFailed(p0: CameraCaptureSession) { } }, handler) } override fun onDisconnected(p0: CameraDevice) { } @SuppressLint("MissingPermission") override fun onError(p0: CameraDevice, p1: Int) { } },handler) } private fun getPermission() { if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)!=PackageManager.PERMISSION_GRANTED) { requestPermissions(arrayOf(android.Manifest.permission.CAMERA), 101) } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { getPermission() } } }
方法一:添加TensorFlow Lite Flex依赖并配置Delegate
报错核心是模型包含FlexCombinedNonMaxSuppression这类TensorFlow原生OP,标准TFLite不支持,需启用Flex Delegate兼容。
- 添加依赖:在app模块的
build.gradle(Module level)中加入Flex依赖:
dependencies { // 其他依赖... implementation 'org.tensorflow:tensorflow-lite-flex:2.15.0' // 版本可匹配你的TFLite版本调整 }
- 修改模型初始化逻辑:手动创建带Flex Delegate的Interpreter,替换自动生成的初始化方式:
// 替换原有的model = ObjectDetection.newInstance(this) val options = Interpreter.Options().apply { addDelegate(FlexDelegate()) } val modelFile = FileUtil.loadMappedFile(this, "model.tflite") // 替换为你的实际模型文件名 val model = ObjectDetection.newInstance(this, options)
方法二:转换模型时移除内置NMS,手动在APP端实现
若不想引入Flex依赖,可在模型转换时关闭内置NMS,将NMS逻辑移到Kotlin代码中处理:
- 重新转换模型:去掉
--nms参数:
python export.py --weights best.pt --include tflite
此时模型输出为原始候选框(形状通常为[1, 25200, 85]),而非经过NMS后的100个结果。
- 在Kotlin中实现NMS逻辑:对所有候选框进行非极大值抑制,筛选出置信度达标且不重叠的框后再绘制。
额外优化建议
- 不要在
onSurfaceTextureUpdated中同步执行模型推理,会阻塞UI线程导致卡顿或崩溃,建议将推理逻辑放到你创建的videoThread后台线程中执行。 - 检查
colors列表长度,若检测类别数量超过列表长度会触发数组越界,建议动态生成颜色或扩展列表。
内容的提问来源于stack exchange,提问作者Fatema Shawki

