작업 목표
문제 및 해결
[App Crash] TFLite 파일 경로 및 의존성 충돌 문제 (SIGSEGV)
- 원인:
- 기존에 추가된
tensorflow-lite-task-vision과 MediaPipe의 내부 LiteRT 엔진 간에 네임스페이스(org.tensorflow.lite) 충돌 발생. - 앱 실행 시 네이티브 레이어(
asset_manager_util.cc)에서cat_breed_int8.tflite모델 파일을 찾지 못해 SIGSEGV (메모리 참조 오류) 발생.
- 기존에 추가된
- 해결:
TFLiteTask 종속성을 제거,MediaPipeAPI 사용(ImageClassifier)- 프로젝트 루트에 있던 모델 파일을
:ml-core모듈의src/main/assets/models/경로로 이동하여 패키징 시 정상적으로 로드되도록 조치.
외부 파일 위치
- 모듈 내
src/[main/test/...]/assets/위치

[Error] MediaPipe GPU 설정
- mediapipe 최소 Sdk 호환 문제(minSdk =23 -> 24)
// KotlinAndroid.kt internal fun Project.configureKotlinAndroid( commonExtension: CommonExtension, ) { commonExtension.apply { compileSdk = 36 defaultConfig.apply { minSdk = 24 } ... - CPU 전환 실패 오류
- GL_INVALID_VALUE 오류 발생
Build.VERSION.SDK_INT < Build.VERSION_CODES.P(API 28) : 구형 버전 CPU 사용useGpu, recursive call을 통한 에러 대응 강화
```kotlin
// TfliteBreedClassifier.kt
@Singleton
class TfliteBreedClassifier @Inject constructor(
@ApplicationContext private val context: Context
) : BreedClassifier {
private var classifier: ImageClassifier? = null
private fun setupClassifier() {
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath(“models/cat_breed_int8.tflite”)
.setDelegate(Delegate.GPU)val optionsBuilder = ImageClassifierOptions.builder() .setBaseOptions(baseOptionsBuilder.build()) .setMaxResults(3) .setScoreThreshold(0.3f) try { classifier = ImageClassifier.createFromOptions(context, optionsBuilder.build()) } catch (e: Exception) { e.printStackTrace() // Fallback to CPU if GPU fails try { val cpuOptionsBuilder = ImageClassifierOptions.builder() .setBaseOptions(BaseOptions.builder() .setModelAssetPath("models/cat_breed_int8.tflite") .setDelegate(Delegate.CPU) .build()) .setMaxResults(3) .setScoreThreshold(0.3f) classifier = ImageClassifier.createFromOptions(context, cpuOptionsBuilder.build()) } catch (fallbackException: Exception) { fallbackException.printStackTrace() } } } ... ``` --- ```kotlin @Singleton class TfliteBreedClassifier @Inject constructor( @ApplicationContext private val context: Context ) : BreedClassifier {private var classifier: ImageClassifier? = null
private var useGpu = trueprivate fun setupClassifier() {
// GPU compatibility check for older devices (Android 8.0 Galaxy S7 etc.)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
Log.w(“TfliteBreedClassifier”, “Device API level < 28. Forcing CPU for stability.”)
useGpu = false
}classifier = createClassifier(if (useGpu) Delegate.GPU else Delegate.CPU) }private fun createClassifier(delegate: Delegate): ImageClassifier? {
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath(“models/cat_breed_int8.tflite”)
.setDelegate(delegate)val optionsBuilder = ImageClassifierOptions.builder() .setBaseOptions(baseOptionsBuilder.build()) .setMaxResults(3) .setScoreThreshold(0.3f) return try { ImageClassifier.createFromOptions(context, optionsBuilder.build()) } catch (e: Exception) { Log.e("TfliteBreedClassifier", "Failed to create classifier with $delegate", e) if (delegate == Delegate.GPU) { useGpu = false createClassifier(Delegate.CPU) } else { null } } } ```
GL_INVALID_VALUE
OpenGL ES의 에러 코드 중 하나로, 숫자 인자가 허용 범위를 벗어났을 때 발생.
OpenGL ES 스펙에서 정의된 표준 에러입니다.
TFLite GL Interop 맥락에서의 의미
TFLite GPU 델리게이트는 OpenGL ES와 OpenCL 사이에서 데이터를 공유하기 위해 GL Interop을 사용합니다.
TFLite GPU Delegate
│
▼
OpenCL Kernel ──── gl_interop.cc ──── OpenGL ES Buffer
(연산 수행) (텍스처/버퍼 공유)
- 이 과정에서
glMapBufferRange를 호출할 때, 버퍼 크기나 오프셋이 기기 드라이버가 허용하는 범위와 맞지 않으면GL_INVALID_VALUE가 발생. - 즉, TFLite가 내부적으로 계산한 버퍼 크기와 실제 드라이버가 허용하는 크기가 기기마다 다를 수 있어 구형/특정 기기에서만 재현.
- OpenGL 에러는 예외(Exception)를 throw하지 않음.
// OpenGL 에러 방식 — 에러를 내부 상태로 저장 glMapBufferRange(...); // 실패해도 예외 없음 GLenum err = glGetError(); // 별도로 polling해야 에러 확인 가능 - TFLite/MediaPipe 내부에서
glGetError()로 이 에러를 감지한 뒤 로그만 출력하고 넘어가는 경우, JVM 레이어까지 예외가 전파되지 않음. - 그래서 Kotlin의 try-catch가 트리거되지 않고 폴백도 동작하지 않음.
C
Contents
