Android에서 AutoML 학습 모델을 사용하여 이미지에서 객체 감지

AutoML Vision Edge를 사용하여 자체 모델을 학습시킨 후 앱에서 이 모델을 사용해 이미지에서 객체를 감지할 수 있습니다.

AutoML Vision Edge에서 학습된 모델을 통합하는 방법에는 2가지가 있습니다. 앱의 애셋 폴더에 모델을 저장하여 번들로 묶거나 Firebase에서 동적으로 다운로드하는 것입니다.

모델 번들 옵션
앱에 번들로 제공
  • 모델이 앱 APK에 포함됨
  • Android 기기가 오프라인 상태일 때도 모델을 즉시 사용할 수 있음
  • Firebase 프로젝트가 필요 없음
Firebase로 호스팅
  • Firebase 머신러닝에 모델을 업로드하여 호스팅
  • APK 크기 축소
  • 모델 요청 시 다운로드됨
  • 앱을 다시 게시하지 않고 모델 업데이트 푸시
  • Firebase 원격 구성으로 간편하게 A/B 테스트
  • Firebase 프로젝트 필요

시작하기 전에

  1. 모델을 다운로드하려면 Android 프로젝트에 Firebase를 추가(아직 추가하지 않은 경우)해야 합니다. 모델을 번들로 묶을 때는 이 작업이 필요하지 않습니다.

  2. 모듈의 앱 수준 Gradle 파일(일반적으로 app/build.gradle)에 TensorFlow Lite Task 라이브러리의 종속 항목을 추가합니다.

    모델을 앱과 함께 번들로 묶는 방법은 다음과 같습니다.

    dependencies {
      // ...
      // Object detection with a bundled Auto ML model
      implementation 'org.tensorflow:tensorflow-lite-task-vision:0.0.0-nightly-SNAPSHOT'
    }
    

    Firebase에서 모델을 동적으로 다운로드하려면 Firebase ML 종속 항목도 추가하세요.

    dependencies {
      // ...
      // Object detection with an Auto ML model deployed to Firebase
      implementation platform('com.google.firebase:firebase-bom:26.1.1')
      implementation 'com.google.firebase:firebase-ml-model-interpreter'
    
      implementation 'org.tensorflow:tensorflow-lite-task-vision:0.0.0-nightly'
    }
    

1. 모델 로드

로컬 모델 소스 구성

모델을 앱과 함께 번들로 묶는 방법은 다음과 같습니다.

  1. Google Cloud Console에서 다운로드한 zip 보관 파일에서 모델을 추출합니다.
  2. 앱 패키지에 모델을 포함합니다.
    1. 프로젝트에 애셋 폴더가 없으면 app/ 폴더를 마우스 오른쪽 버튼으로 클릭하고 새로 만들기 > 폴더 > 애셋 폴더를 클릭하여 하나 만듭니다.
    2. 메타데이터가 삽입된 tflite 모델 파일을 애셋 폴더에 복사합니다.
  3. Gradle이 앱을 빌드할 때 모델 파일을 압축하지 않도록 앱의 build.gradle 파일에 다음을 추가합니다.

    android {
        // ...
        aaptOptions {
            noCompress "tflite"
        }
    }
    

    모델 파일이 앱 패키지에 포함되며 원시 애셋으로 사용할 수 있습니다.

Firebase 호스팅 모델 소스 구성

원격 호스팅 모델을 사용하려면 모델을 게시할 때 모델에 할당한 이름을 지정하여 RemoteModel 객체를 만듭니다.

Java

// Specify the name you assigned when you deployed the model.
FirebaseCustomRemoteModel remoteModel =
        new FirebaseCustomRemoteModel.Builder("your_model").build();

Kotlin

// Specify the name you assigned when you deployed the model.
val remoteModel =
    FirebaseCustomRemoteModel.Builder("your_model_name").build()

이제 다운로드를 허용할 조건을 지정하여 모델 다운로드 작업을 시작합니다. 모델이 기기에 없거나 최신 버전의 모델을 사용할 수 있으면 모델이 Firebase에서 비동기식으로 다운로드됩니다.

Java

DownloadConditions downloadConditions = new DownloadConditions.Builder()
        .requireWifi()
        .build();
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(@NonNull Task<Void> task) {
                // Success.
            }
        });

Kotlin

val downloadConditions = DownloadConditions.Builder()
    .requireWifi()
    .build()
RemoteModelManager.getInstance().download(remoteModel, downloadConditions)
    .addOnSuccessListener {
        // Success.
    }

대부분의 앱은 초기화 코드로 다운로드 작업을 시작하지만 모델 사용이 필요한 시점 이전에는 언제든지 다운로드할 수 있습니다.

모델에서 객체 감지기 만들기

모델 소스를 구성한 후 모델 소스 중 하나에서 ObjectDetector 객체를 만듭니다.

로컬로 번들된 모델만 있는 경우 모델 파일에서 객체 감지기를 만들고 필요한 신뢰도 점수 임곗값을 구성합니다(모델 평가 참조).

자바

// Initialization
ObjectDetectorOptions options = ObjectDetectorOptions.builder()
    .setScoreThreshold(0)  // Evaluate your model in the Google Cloud Console
                           // to determine an appropriate value.
    .build();
ObjectDetector objectDetector = ObjectDetector.createFromFileAndOptions(context, modelFile, options);

Kotlin

// Initialization
val options = ObjectDetectorOptions.builder()
    .setScoreThreshold(0)  // Evaluate your model in the Google Cloud Console
                           // to determine an appropriate value.
    .build()
val objectDetector = ObjectDetector.createFromFileAndOptions(context, modelFile, options)

원격 호스팅 모델이 있다면 실행 전에 모델이 다운로드되었는지 확인해야 합니다. 모델 관리자의 isModelDownloaded() 메서드로도 모델 다운로드 작업의 상태를 확인할 수 있습니다.

이 상태는 객체 감지기를 실행하기 전에만 확인하면 되지만, 원격 호스팅 모델과 로컬로 번들된 모델이 모두 있는 경우에는 객제 감지기를 인스턴스화할 때 이 확인 작업을 수행하는 것이 합리적일 수 있으며 원격 모델이 다운로드되었으면 원격 모델에서, 그렇지 않으면 로컬 모델에서 객체 감지기를 만듭니다.

자바

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener<Boolean>() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
            }
        });

Kotlin

FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener { success ->

        }

원격 호스팅 모델만 있다면 모델 다운로드 여부가 확인될 때까지 모델 관련 기능 사용을 중지해야 합니다(예: UI 비활성화 또는 숨김). 모델 관리자의 download() 메서드에 리스너를 연결하여 관련 기능을 사용 중지할 수도 있습니다.

모델이 다운로드된 것이 확인되었으면 모델 파일에서 객체 감지기를 만듭니다.

자바

FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnCompleteListener(new OnCompleteListener<File>() {
            @Override
            public void onComplete(@NonNull Task<File> task) {
                File modelFile = task.getResult();
                if (modelFile != null) {
                    ObjectDetectorOptions options = ObjectDetectorOptions.builder()
                            .setScoreThreshold(0)
                            .build();
                    objectDetector = ObjectDetector.createFromFileAndOptions(
                            getApplicationContext(), modelFile.getPath(), options);
                }
            }
        });

Kotlin

FirebaseModelManager.getInstance().getLatestModelFile(remoteModel)
        .addOnSuccessListener { modelFile ->
            val options = ObjectDetectorOptions.builder()
                    .setScoreThreshold(0f)
                    .build()
            objectDetector = ObjectDetector.createFromFileAndOptions(
                    applicationContext, modelFile.path, options)
        }

2. 입력 이미지 준비

그런 다음 라벨을 지정할 각 이미지에서 TensorImage 객체를 만듭니다. fromBitmap 메서드를 사용하여 Bitmap에서 TensorImage 객체를 만들 수 있습니다.

자바

TensorImage image = TensorImage.fromBitmap(bitmap);

Kotlin

val image = TensorImage.fromBitmap(bitmap)

이미지 데이터가 Bitmap에 없는 경우 TensorFlow Lite 문서에 표시된 대로 픽셀 배열을 로드할 수 있습니다.

3. 객체 감지기 실행

이미지에서 객체를 감지하려면 TensorImage 객체를 ObjectDetectordetect() 메서드에 전달합니다.

자바

List<Detection> results = objectDetector.detect(image);

Kotlin

val results = objectDetector.detect(image)

4. 라벨이 지정된 객체 정보 가져오기

객체 감지 작업이 성공하면 Detection 객체 목록이 반환됩니다. 각 Detection 객체는 이미지에서 감지된 항목을 나타냅니다. 각 객체의 경계 상자와 라벨을 가져올 수 있습니다.

예를 들면 다음과 같습니다.

Java

for (Detection result : results) {
    RectF bounds = result.getBoundingBox();
    List<Category> labels = result.getCategories();
}

Kotlin

for (result in results) {
    val bounds = result.getBoundingBox()
    val labels = result.getCategories()
}

실시간 성능 향상을 위한 팁

실시간 애플리케이션에서 이미지 라벨을 지정하려는 경우 최상의 프레임 속도를 얻으려면 다음 안내를 따르세요.

  • 이미지 라벨러 호출을 제한합니다. 이미지 라벨러가 실행 중일 때 새 동영상 프레임이 제공되는 경우 해당 프레임을 삭제합니다. 관련 예시는 빠른 시작 샘플 앱에서 VisionProcessorBase 클래스를 참조하세요.
  • 이미지 라벨러 출력을 사용해 입력 이미지에서 그래픽을 오버레이하는 경우 먼저 인식 결과를 가져온 후 이미지를 렌더링하고 단일 단계로 오버레이합니다. 이렇게 하면 입력 프레임별로 한 번만 디스플레이 표면에 렌더링됩니다. 관련 예시는 빠른 시작 샘플 앱에서 CameraSourcePreviewGraphicOverlay 클래스를 참조하세요.
  • Camera2 API를 사용할 경우 ImageFormat.YUV_420_888 형식으로 이미지를 캡처합니다.

    이전 Camera API를 사용하는 경우 ImageFormat.NV21 형식으로 이미지를 캡처합니다.