在 Android 上使用以 AutoML 訓練的模型為圖片加上標籤

,瞭解如何調查及移除這項存取權。 使用 AutoML Vision Edge 訓練您自己的模型後, 即可在應用程式中為圖片加上標籤 有兩種方法可以整合透過 AutoML Vision Edge 訓練的模型:您可以 將模型放置於應用程式的素材資源資料夾內 以便從 Firebase 動態下載
模型組合選項
在應用程式中封裝
  • 這個模型是應用程式 APK 的一部分
  • 可立即使用型號,即使 Android 裝置離線也沒問題
  • 不需要 Firebase 專案
由 Firebase 代管
  • 將模型上傳至 Firebase 機器學習
  • 縮減 APK 大小
  • 模型會隨選下載
  • 不必重新發布應用程式即可推送模型更新
  • 使用 Firebase 遠端設定輕鬆進行 A/B 測試
  • 需要 Firebase 專案

立即試用

事前準備

1. 請務必在專案層級的 build.gradle 檔案中納入 位於 buildscript 和 的 Google Maven 存放區 allprojects 個版面。

2.將 ML Kit Android 程式庫的依附元件新增至模組的 應用程式層級的 Gradle 檔案,通常為 app/build.gradle: 將模型與應用程式綁定:
    dependencies {
      // ...
      // Image labeling feature with bundled automl model
      implementation 'com.google.mlkit:image-labeling-automl:16.2.1'
    }
    
如要從 Firebase 動態下載模型,請新增 linkFirebase 依附元件:
    dependencies {
      // ...
      // Image labeling feature with automl model downloaded
      // from firebase
      implementation 'com.google.mlkit:image-labeling-automl:16.2.1'
      implementation 'com.google.mlkit:linkfirebase:16.0.1'
    }
    
3.如要下載模型,請務必 將 Firebase 新增至您的 Android 專案, 如果尚未建立當您要組合模型時,不必做出任何動作。

1. 載入模型

設定本機模型來源

如要將模型與應用程式組合,請按照下列步驟操作:

1.從下載的 ZIP 封存檔中,擷取模型及其中繼資料 建議你使用已下載的檔案 且未經修改 (包括檔案名稱)。

2.將模型及其中繼資料檔案加入應用程式套件:

a.如果專案沒有素材資源資料夾,請按照 在 app/ 資料夾上按一下滑鼠右鍵,然後點選 新增 >資料夾 >素材資源資料夾

b.在素材資源資料夾底下建立子資料夾,用來存放模型 檔案。

c.複製 model.tflitedict.txtmanifest.json 至子資料夾 (這三個檔案都必須位於 相同資料夾)。

3.請將以下內容新增至應用程式的 build.gradle 檔案,確保 Gradle 不會在建構應用程式時壓縮模型檔案:
    android {
        // ...
        aaptOptions {
            noCompress "tflite"
        }
    }
    
模型檔案將包含在應用程式套件中,並可供 ML Kit 使用 做為原始素材資源

注意:自 Android Gradle 外掛程式 4.1 版起,.tflite 會改為 就會預設新增至 noCompress 清單,不再需要上述方法。

4.建立 LocalModel 物件,指定模型資訊清單的路徑 檔案:

Kotlin

val localModel = AutoMLImageLabelerLocalModel.Builder()
        .setAssetFilePath("manifest.json")
        // or .setAbsoluteFilePath(absolute file path to manifest file)
        .build()

Java

AutoMLImageLabelerLocalModel localModel =
    new AutoMLImageLabelerLocalModel.Builder()
        .setAssetFilePath("manifest.json")
        // or .setAbsoluteFilePath(absolute file path to manifest file)
        .build();

設定 Firebase 託管的模型來源

如要使用遠端託管的模型,請建立 RemoteModel 物件。 請指定您在發布模型時為其指派的名稱:

Kotlin

// Specify the name you assigned in the Firebase console.
val remoteModel =
    AutoMLImageLabelerRemoteModel.Builder("your_model_name").build()

Java

// Specify the name you assigned in the Firebase console.
AutoMLImageLabelerRemoteModel remoteModel =
    new AutoMLImageLabelerRemoteModel.Builder("your_model_name").build();

接著,啟動模型下載工作,並指定在 您要允許下載的應用程式。如果裝置上沒有該型號,或者是新型號 就能以非同步方式下載該模型 建立 Vertex AI 模型

Kotlin

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

Java

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

許多應用程式會在初始化程式碼中啟動下載工作,但您 這個模型會在您需要使用模型前執行

從模型建立圖片標籤工具

設定模型來源後,請從該模型建立 ImageLabeler 物件 我們很快就會深入探討 所以目前先概略介紹

如果您只有本機組合模型,只要從 AutoMLImageLabelerLocalModel 物件,並設定可信度分數 要求的門檻 (請參閱評估模型):

Kotlin

val autoMLImageLabelerOptions = AutoMLImageLabelerOptions.Builder(localModel)
    .setConfidenceThreshold(0)  // Evaluate your model in the Firebase console
                                // to determine an appropriate value.
    .build()
val labeler = ImageLabeling.getClient(autoMLImageLabelerOptions)

Java

AutoMLImageLabelerOptions autoMLImageLabelerOptions =
        new AutoMLImageLabelerOptions.Builder(localModel)
                .setConfidenceThreshold(0.0f)  // Evaluate your model in the Firebase console
                                               // to determine an appropriate value.
                .build();
ImageLabeler labeler = ImageLabeling.getClient(autoMLImageLabelerOptions)

如果您使用的是遠端託管的模型,則須檢查該模型是否已 執行前已下載完成您可以查看模型下載狀態 工作使用模型管理員的 isModelDownloaded() 方法。

雖然您不必在執行標籤人員前確認 同時擁有遠端託管和本機封裝模型 將圖片標籤人員例項化時,要執行這項檢查:請建立 從遠端模型下載標籤人員 反之。

Kotlin

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded -> 
    val optionsBuilder =
        if (isDownloaded) {
            AutoMLImageLabelerOptions.Builder(remoteModel)
        } else {
            AutoMLImageLabelerOptions.Builder(localModel)
        }
    // Evaluate your model in the Firebase console to determine an appropriate threshold.
    val options = optionsBuilder.setConfidenceThreshold(0.0f).build()
    val labeler = ImageLabeling.getClient(options)
}

Java

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(Boolean isDownloaded) {
                AutoMLImageLabelerOptions.Builder optionsBuilder;
                if (isDownloaded) {
                    optionsBuilder = new AutoMLImageLabelerOptions.Builder(remoteModel);
                } else {
                    optionsBuilder = new AutoMLImageLabelerOptions.Builder(localModel);
                }
                AutoMLImageLabelerOptions options = optionsBuilder
                        .setConfidenceThreshold(0.0f)  // Evaluate your model in the Firebase console
                                                       // to determine an appropriate threshold.
                        .build();

                ImageLabeler labeler = ImageLabeling.getClient(options);
            }
        });

如果只有遠端託管的模型,請停用模型相關 或隱藏部分 UI,直到 您確認模型已下載完成附加監聽器即可 變更為模型管理工具的 download() 方法:

Kotlin

RemoteModelManager.getInstance().download(remoteModel, conditions)
    .addOnSuccessListener {
        // Download complete. Depending on your app, you could enable the ML
        // feature, or switch from the local model to the remote model, etc.
    }

Java

RemoteModelManager.getInstance().download(remoteModel, conditions)
        .addOnSuccessListener(new OnSuccessListener() {
            @Override
            public void onSuccess(Void v) {
              // Download complete. Depending on your app, you could enable
              // the ML feature, or switch from the local model to the remote
              // model, etc.
            }
        });

2. 準備輸入圖片

接著,為要加上標籤的每張圖片建立 InputImage。 物件中的物件名稱當您使用 Bitmap 時,圖片標籤工具執行速度最快 或者,如果您使用 camera2 API,則 YUV_420_888 media.Image,這些是 建議選取

您可以建立InputImage 不同來源的 ANR 物件,說明如下。

使用 media.Image

如要建立InputImage 物件,例如從 media.Image 物件擷取圖片 裝置的相機,請傳遞 media.Image 物件和映像檔的 旋轉為 InputImage.fromMediaImage()

如果您使用 CameraX 程式庫、OnImageCapturedListenerImageAnalysis.Analyzer 類別會計算旋轉值 不必確保憑證管理是否適當 因為 Google Cloud 會為您管理安全性

Kotlin

private class YourImageAnalyzer : ImageAnalysis.Analyzer {

    override fun analyze(imageProxy: ImageProxy) {
        val mediaImage = imageProxy.image
        if (mediaImage != null) {
            val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
}

Java

private class YourAnalyzer implements ImageAnalysis.Analyzer {

    @Override
    public void analyze(ImageProxy imageProxy) {
        Image mediaImage = imageProxy.getImage();
        if (mediaImage != null) {
          InputImage image =
                InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees());
          // Pass image to an ML Kit Vision API
          // ...
        }
    }
}

如果您沒有使用相機程式庫提供圖片的旋轉角度, 可根據裝置的旋轉角度和相機方向來計算 感應器:

Kotlin

private val ORIENTATIONS = SparseIntArray()

init {
    ORIENTATIONS.append(Surface.ROTATION_0, 0)
    ORIENTATIONS.append(Surface.ROTATION_90, 90)
    ORIENTATIONS.append(Surface.ROTATION_180, 180)
    ORIENTATIONS.append(Surface.ROTATION_270, 270)
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Throws(CameraAccessException::class)
private fun getRotationCompensation(cameraId: String, activity: Activity, isFrontFacing: Boolean): Int {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    val deviceRotation = activity.windowManager.defaultDisplay.rotation
    var rotationCompensation = ORIENTATIONS.get(deviceRotation)

    // Get the device's sensor orientation.
    val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager
    val sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION)!!

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360
    }
    return rotationCompensation
}

Java

private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
    ORIENTATIONS.append(Surface.ROTATION_0, 0);
    ORIENTATIONS.append(Surface.ROTATION_90, 90);
    ORIENTATIONS.append(Surface.ROTATION_180, 180);
    ORIENTATIONS.append(Surface.ROTATION_270, 270);
}

/**
 * Get the angle by which an image must be rotated given the device's current
 * orientation.
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private int getRotationCompensation(String cameraId, Activity activity, boolean isFrontFacing)
        throws CameraAccessException {
    // Get the device's current rotation relative to its "native" orientation.
    // Then, from the ORIENTATIONS table, look up the angle the image must be
    // rotated to compensate for the device's rotation.
    int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
    int rotationCompensation = ORIENTATIONS.get(deviceRotation);

    // Get the device's sensor orientation.
    CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE);
    int sensorOrientation = cameraManager
            .getCameraCharacteristics(cameraId)
            .get(CameraCharacteristics.SENSOR_ORIENTATION);

    if (isFrontFacing) {
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
    } else { // back-facing
        rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360;
    }
    return rotationCompensation;
}

然後,請傳遞 media.Image 物件和 將度數值旋轉為 InputImage.fromMediaImage()

Kotlin

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

InputImage image = InputImage.fromMediaImage(mediaImage, rotation);

使用檔案 URI

如要建立InputImage 物件,將應用程式結構定義與檔案 URI 傳遞至 InputImage.fromFilePath()。如果您要 使用 ACTION_GET_CONTENT 意圖提示使用者選取 取自圖片庫應用程式中的圖片。

Kotlin

val image: InputImage
try {
    image = InputImage.fromFilePath(context, uri)
} catch (e: IOException) {
    e.printStackTrace()
}

Java

InputImage image;
try {
    image = InputImage.fromFilePath(context, uri);
} catch (IOException e) {
    e.printStackTrace();
}

使用 ByteBufferByteArray

如要建立InputImage ByteBufferByteArray 的物件,請先計算圖片 與先前 media.Image 輸入中所述的旋轉角度相同。 接著,使用緩衝區或陣列建立 InputImage 物件,以及 高度、寬度、顏色編碼格式以及旋轉角度:

Kotlin

val image = InputImage.fromByteBuffer(
        byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)
// Or:
val image = InputImage.fromByteArray(
        byteArray,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
)

Java

InputImage image = InputImage.fromByteBuffer(byteBuffer,
        /* image width */ 480,
        /* image height */ 360,
        rotationDegrees,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);
// Or:
InputImage image = InputImage.fromByteArray(
        byteArray,
        /* image width */480,
        /* image height */360,
        rotation,
        InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12
);

使用 Bitmap

如要建立InputImage 物件中,Bitmap 物件,請做出以下宣告:

Kotlin

val image = InputImage.fromBitmap(bitmap, 0)

Java

InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);

圖像以 Bitmap 物件和旋轉角度表示。

3. 執行映像檔標籤工具

如要為圖片中的物件加上標籤,請將 image 物件傳遞至 ImageLabelerprocess() 方法。

Kotlin

labeler.process(image)
        .addOnSuccessListener { labels ->
            // Task completed successfully
            // ...
        }
        .addOnFailureListener { e ->
            // Task failed with an exception
            // ...
        }

Java

labeler.process(image)
        .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() {
            @Override
            public void onSuccess(List<ImageLabel> labels) {
                // Task completed successfully
                // ...
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Task failed with an exception
                // ...
            }
        });

4. 取得加上標籤的物件相關資訊

如果圖片標籤作業成功,系統會顯示 ImageLabel 清單。 會將物件傳遞到成功事件監聽器每個 ImageLabel 物件代表 也就是在映像檔中加上標籤的內容可以取得每個標籤的文字 說明、比對內容的可信度分數和相符項目索引。 例如:

Kotlin

for (label in labels) {
    val text = label.text
    val confidence = label.confidence
    val index = label.index
}

Java

for (ImageLabel label : labels) {
    String text = label.getText();
    float confidence = label.getConfidence();
    int index = label.getIndex();
}

即時效能改善訣竅

如要在即時應用程式中為圖片加上標籤,請按照下列步驟操作: 實現最佳影格速率:

  • 如果您使用 Cameracamera2 API、 限制對圖片標籤人員的呼叫如果影片有新影片 請捨棄影格,讓圖片標籤工具執行期間可供使用。詳情請參閱 VisionProcessorBase 類別的範例。
  • 如果您是使用 CameraX API, 請務必將背壓策略設為預設值 ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST。 這麼做可保證系統一次只會傳送一張圖片進行分析。如果圖片較多 會在分析器忙碌時產生,這些作業會自動遭到捨棄,不會排入佇列 廣告放送。以呼叫方式關閉要分析的圖片後 ImageProxy.close(),最新一張圖片才會放送。
  • 如果您使用圖片標籤人員的輸出內容來重疊顯示圖像 先從 ML Kit 取得結果,然後算繪圖片 並疊加單一步驟這會轉譯至顯示介面 每個輸入影格只能建立一次詳情請參閱 CameraSourcePreview 如需範例,請前往快速入門導覽課程範例應用程式中的 GraphicOverlay 類別。
  • 如果你使用 Camera2 API, ImageFormat.YUV_420_888 格式。如果使用舊版 Camera API,請以 ImageFormat.NV21 格式。