Fix emulator crash by adding runtime JNI loading check and fallback for MediaPipe Tasks Vision
This commit is contained in:
@@ -67,48 +67,79 @@ 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) {
|
||||||
val baseOptions = BaseOptions.builder()
|
try {
|
||||||
.setModelAssetPath("models/multiclass_segmenter.tflite")
|
val baseOptions = BaseOptions.builder()
|
||||||
.build()
|
.setModelAssetPath("models/multiclass_segmenter.tflite")
|
||||||
|
.build()
|
||||||
|
|
||||||
val options = ImageSegmenterOptions.builder()
|
val options = ImageSegmenterOptions.builder()
|
||||||
.setBaseOptions(baseOptions)
|
.setBaseOptions(baseOptions)
|
||||||
.setOutputCategoryMask(true)
|
.setOutputCategoryMask(true)
|
||||||
.setOutputConfidenceMasks(false)
|
.setOutputConfidenceMasks(false)
|
||||||
.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 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!!
|
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) {
|
||||||
val assetFileDescriptor = assets.openFd("models/midas.tflite")
|
try {
|
||||||
val inputStream = java.io.FileInputStream(assetFileDescriptor.fileDescriptor)
|
val assetFileDescriptor = assets.openFd("models/midas.tflite")
|
||||||
val fileChannel = inputStream.channel
|
val inputStream = java.io.FileInputStream(assetFileDescriptor.fileDescriptor)
|
||||||
val startOffset = assetFileDescriptor.startOffset
|
val fileChannel = inputStream.channel
|
||||||
val declaredLength = assetFileDescriptor.declaredLength
|
val startOffset = assetFileDescriptor.startOffset
|
||||||
val modelBuffer = fileChannel.map(java.nio.channels.FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
|
val declaredLength = assetFileDescriptor.declaredLength
|
||||||
|
val modelBuffer = fileChannel.map(java.nio.channels.FileChannel.MapMode.READ_ONLY, startOffset, declaredLength)
|
||||||
|
|
||||||
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 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!!
|
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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user