Fix emulator crash by adding runtime JNI loading check and fallback for MediaPipe Tasks Vision

This commit is contained in:
2026-07-06 17:53:04 +07:00
parent 88fc095bdc
commit 4035aefda4
@@ -67,10 +67,13 @@ class MainActivity : AppCompatActivity() {
private lateinit var backgroundOptions: List<BackgroundOption> private lateinit var backgroundOptions: List<BackgroundOption>
private val aiInferenceLock = Any() private val aiInferenceLock = Any()
private var imageSegmenter: ImageSegmenter? = null private var imageSegmenter: ImageSegmenter? = null
private var isAiSupported = true
private fun getImageSegmenter(): ImageSegmenter { private fun getImageSegmenter(): ImageSegmenter? {
synchronized(aiInferenceLock) { synchronized(aiInferenceLock) {
if (!isAiSupported) return null
if (imageSegmenter == null) { if (imageSegmenter == null) {
try {
val baseOptions = BaseOptions.builder() val baseOptions = BaseOptions.builder()
.setModelAssetPath("models/multiclass_segmenter.tflite") .setModelAssetPath("models/multiclass_segmenter.tflite")
.build() .build()
@@ -82,16 +85,31 @@ class MainActivity : AppCompatActivity() {
.build() .build()
imageSegmenter = ImageSegmenter.createFromOptions(this, options) imageSegmenter = ImageSegmenter.createFromOptions(this, options)
} catch (e: UnsatisfiedLinkError) {
isAiSupported = false
runOnUiThread {
Toast.makeText(this, "Không hỗ trợ xử lý AI trên thiết bị/emulator này (thiếu thư viện Native JNI)", Toast.LENGTH_LONG).show()
} }
return imageSegmenter!! return null
} catch (e: Exception) {
isAiSupported = false
runOnUiThread {
Toast.makeText(this, "Lỗi khởi tạo AI Segmenter: ${e.message}", Toast.LENGTH_LONG).show()
}
return null
}
}
return imageSegmenter
} }
} }
private var tfliteInterpreter: org.tensorflow.lite.Interpreter? = null private var tfliteInterpreter: org.tensorflow.lite.Interpreter? = null
private fun getTfliteInterpreter(): org.tensorflow.lite.Interpreter { private fun getTfliteInterpreter(): org.tensorflow.lite.Interpreter? {
synchronized(aiInferenceLock) { synchronized(aiInferenceLock) {
if (!isAiSupported) return null
if (tfliteInterpreter == null) { if (tfliteInterpreter == null) {
try {
val assetFileDescriptor = assets.openFd("models/midas.tflite") val assetFileDescriptor = assets.openFd("models/midas.tflite")
val inputStream = java.io.FileInputStream(assetFileDescriptor.fileDescriptor) val inputStream = java.io.FileInputStream(assetFileDescriptor.fileDescriptor)
val fileChannel = inputStream.channel val fileChannel = inputStream.channel
@@ -102,13 +120,26 @@ class MainActivity : AppCompatActivity() {
val options = org.tensorflow.lite.Interpreter.Options() val options = org.tensorflow.lite.Interpreter.Options()
options.setNumThreads(4) options.setNumThreads(4)
tfliteInterpreter = org.tensorflow.lite.Interpreter(modelBuffer, options) tfliteInterpreter = org.tensorflow.lite.Interpreter(modelBuffer, options)
} catch (e: UnsatisfiedLinkError) {
isAiSupported = false
runOnUiThread {
Toast.makeText(this, "Không hỗ trợ xử lý Depth trên thiết bị/emulator này (thiếu thư viện Native JNI)", Toast.LENGTH_LONG).show()
} }
return tfliteInterpreter!! return null
} catch (e: Exception) {
isAiSupported = false
runOnUiThread {
Toast.makeText(this, "Lỗi khởi tạo Depth Interpreter: ${e.message}", Toast.LENGTH_LONG).show()
}
return null
}
}
return tfliteInterpreter
} }
} }
private fun runDepthInference(bitmap: android.graphics.Bitmap): FloatArray { private fun runDepthInference(bitmap: android.graphics.Bitmap): FloatArray {
val interpreter = getTfliteInterpreter() val interpreter = getTfliteInterpreter() ?: return FloatArray(256 * 256)
// Input: 256x256 RGB // Input: 256x256 RGB
val scaled = android.graphics.Bitmap.createScaledBitmap(bitmap, 256, 256, true) val scaled = android.graphics.Bitmap.createScaledBitmap(bitmap, 256, 256, true)
@@ -1713,11 +1744,29 @@ class MainActivity : AppCompatActivity() {
return capturedImage return capturedImage
} }
if (!isAiSupported) {
if (blurIntensity > 0f) {
val radius = (25 * blurIntensity).toInt().coerceIn(1, 25)
return blurBitmap(capturedImage, radius)
}
return capturedImage
}
try { try {
val segmenter = getImageSegmenter()
val interpreter = getTfliteInterpreter()
if (segmenter == null || interpreter == null) {
if (blurIntensity > 0f) {
val radius = (25 * blurIntensity).toInt().coerceIn(1, 25)
return blurBitmap(capturedImage, radius)
}
return capturedImage
}
// 1. Run MediaPipe Multiclass Image Segmentation to locate subject // 1. Run MediaPipe Multiclass Image Segmentation to locate subject
val mpImage = com.google.mediapipe.framework.image.BitmapImageBuilder(capturedImage).build() val mpImage = com.google.mediapipe.framework.image.BitmapImageBuilder(capturedImage).build()
val segmentationResult = synchronized(aiInferenceLock) { val segmentationResult = synchronized(aiInferenceLock) {
getImageSegmenter().segment(mpImage) segmenter.segment(mpImage)
} }
val categoryMaskOptional = segmentationResult.categoryMask() val categoryMaskOptional = segmentationResult.categoryMask()
if (!categoryMaskOptional.isPresent) { if (!categoryMaskOptional.isPresent) {