برچسب‌گذاری تصاویر با مدل آموزش‌دیده AutoML در Android

پس‌از اینکه مدل خودتان را بااستفاده از AutoML Vision Edge آموزش دادید، می‌توانید از آن در برنامه‌تان برای برچسب‌گذاری تصاویر استفاده کنید. دو روش برای ادغام مدل‌های آموزش‌دیده از AutoML Vision Edge وجود دارد: می‌توانید مدل را با قرار دادن آن در پوشه دارایی برنامه خود دسته‌بندی کنید، یا می‌توانید آن را به‌صورت پویا از Firebase بارگیری کنید.
گزینه‌های بسته‌بندی مدل
در برنامه‌تان بسته‌بندی شده است
  • مدل بخشی از APK برنامه شما است
  • مدل فوراً دردسترس قرار می‌گیرد، حتی وقتی دستگاه Android آفلاین باشد
  • نیازی به پروژه Firebase نیست
میزبانی‌شده با Firebase
  • با بارگذاری مدل در Firebase Machine Learning آن را میزبانی کنید
  • اندازه APK را کاهش می‌دهد
  • مدل به‌درخواست بارگیری می‌شود
  • به‌روزرسانی‌های مدل را بدون بازانتشار برنامه خود ارسال کنید
  • آزمایش آ/ب آسان با پیکربندی از راه دور Firebase
  • به پروژه Firebase نیاز دارد

امتحان کنید

  • با برنامه نمونه بازی کنید تا نمونه‌ای از کاربرد این API را ببینید.

قبل‌از شروع

۱. در فایل build.gradle در سطح پروژه، حتماً مخزن Maven مربوط به Google را در هر دو بخش buildscript و allprojects اضافه کنید.

‫۲. وابستگی‌های کتابخانه‌های 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'
    }
    
‫۳. اگر می‌خواهید مدلی را بارگیری کنید، مطمئن شوید که Firebase را به پروژه Android خود اضافه کنید، اگر قبلاً این کار را نکرده‌اید. این کار هنگام دسته‌بندی مدل الزامی نیست.

۱. بار کردن مدل

پیکربندی منبع مدل محلی

برای بسته‌بندی مدل با برنامه‌تان:

۱. مدل و فراداده آن را از بایگانی zip که از کنسول Firebase بارگیری کرده‌اید استخراج کنید. توصیه می‌کنیم از فایل‌ها همان‌گونه که بارگیری کرده‌اید استفاده کنید و آن‌ها را تغییر ندهید (ازجمله نام فایل‌ها).

‫۲. مدل و فایل‌های فراداده آن را در بسته برنامه‌تان بگنجانید:

الف. اگر پوشه دارایی‌ها را در پروژه‌تان ندارید، با کلیک راست کردن روی پوشه app/ و سپس کلیک کردن روی جدید > پوشه > پوشه دارایی‌ها، یکی ایجاد کنید.

ب. زیرپوشه‌ای در پوشه دارایی‌ها ایجاد کنید تا فایل‌های مدل را دربرگیرد.

ج. فایل‌های model.tflite، dict.txt، و manifest.json را در زیرپوشه کپی کنید (هر سه فایل باید در یک پوشه باشند).

‫۳. برای اطمینان از اینکه Gradle هنگام ساختن برنامه فایل مدل را فشرده نمی‌کند، موارد زیر را به فایل build.gradle برنامه‌تان اضافه کنید:
    android {
        // ...
        aaptOptions {
            noCompress "tflite"
        }
    }
    
فایل مدل در بسته برنامه گنجانده می‌شود و به‌عنوان دارایی خام دراختیار ML Kit قرار می‌گیرد.

توجه: از نسخه ۴.۱ افزایه Android Gradle،‏ .tflite به‌طور پیش‌فرض به فهرست noCompress اضافه می‌شود و دیگر نیازی به موارد بالا نیست.

‫۴. ‫LocalModel شیء ایجاد کنید و مسیر فایل مانیفست مدل را مشخص کنید:

Kotlin

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

جاوا

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()

جاوا

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

سپس، کار بارگیری مدل را شروع کنید و شرایطی را که می‌خواهید بارگیری تحت آن مجاز باشد مشخص کنید. اگر مدل در دستگاه نباشد، یا اگر نسخه جدیدتری از مدل دردسترس باشد، تکلیف به‌صورت ناهمگام مدل را از Firebase بارگیری خواهد کرد:

Kotlin

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

جاوا

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)

جاوا

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)
}

جاوا

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);
            }
        });

اگر فقط مدل میزبانی‌شده از راه دور دارید، باید عملکرد مربوط به مدل را غیرفعال کنید—برای مثال، بخشی از میانای کاربر را خاکستری یا پنهان کنید—تا زمانی که تأیید کنید مدل بارگیری شده است. می‌توانید این کار را با پیوست کردن شنونده به روش 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.
    }

جاوا

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.
            }
        });

۲. آماده کردن تصویر ورودی

سپس، برای هر تصویری که می‌خواهید برچسب‌گذاری کنید، InputImage شیئی از تصویرتان بسازید. برچسب‌زن تصویر زمانی سریع‌ترین عملکرد را دارد که از Bitmap یا، اگر از camera2 API استفاده می‌کنید، از YUV_420_888 media.Image استفاده کنید که درصورت امکان توصیه می‌شود.

می‌توانید InputImage شیء را از منابع مختلف ایجاد کنید که هرکدام در زیر توضیح داده شده است.

استفاده از media.Image

برای ایجاد یک شیء InputImage از یک شیء media.Image، مثلاً وقتی تصویری را از دوربین دستگاه ضبط می‌کنید، شیء media.Image و چرخش تصویر را به InputImage.fromMediaImage() منتقل کنید.

اگر از کتابخانه CameraX استفاده می‌کنید، کلاس‌های OnImageCapturedListener و ImageAnalysis.Analyzer مقدار چرخش را برایتان محاسبه می‌کنند.

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
            // ...
        }
    }
}

جاوا

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
}

جاوا

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);

استفاده از نشانی وب فایل

برای ایجاد یک شیء InputImage از نشانی وب فایل، بافت برنامه و نشانی وب فایل را به 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();
}

استفاده از ByteBuffer یا ByteArray

برای ایجاد شیء InputImage از ByteBuffer یا ByteArray، ابتدا درجه چرخش تصویر را همان‌طور که قبلاً برای ورودی 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
)

جاوا

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 به‌همراه درجه‌های چرخش نشان داده می‌شود.

۳. اجرای برچسب‌زن تصویر

برای برچسب‌گذاری اشیا در تصویر، شیء image را به روش ImageLabeler's process() منتقل کنید.

Kotlin

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

جاوا

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
                // ...
            }
        });

۴. دریافت اطلاعات درباره اشیای برچسب‌گذاری‌شده

اگر عملیات برچسب‌گذاری تصویر موفقیت‌آمیز باشد، فهرستی از ImageLabel اشیا به شنونده موفقیت ارسال می‌شود. هر شیء ImageLabel نشان‌دهنده چیزی است که در تصویر برچسب‌گذاری شده است. می‌توانید شرح نوشتاری هر برچسب، نمره اطمینان مطابقت، و شاخص مطابقت را دریافت کنید. برای مثال:

Kotlin

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

جاوا

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

نکته‌هایی برای بهبود عملکرد هم‌زمان

اگر می‌خواهید تصاویر را در یک برنامه هم‌زمان برچسب‌گذاری کنید، این دستورالعمل‌ها را دنبال کنید تا به بهترین نرخ‌های فریم دست یابید:

  • اگر از Camera یا camera2 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 ضبط کنید.