شناسایی، ردیابی و طبقه بندی اشیاء با یک مدل طبقه بندی سفارشی در اندروید

شما می‌توانید از کیت ML برای تشخیص و ردیابی اشیاء در فریم‌های ویدیویی متوالی استفاده کنید.

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

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

دو راه برای ادغام یک مدل سفارشی وجود دارد. می‌توانید مدل را با قرار دادن آن در پوشه asset برنامه خود، به صورت دسته‌ای (bundle) درآورید، یا می‌توانید آن را به صورت پویا از Cloud Storage دانلود کنید. جدول زیر این دو گزینه را با هم مقایسه می‌کند.

مدل بسته‌ای مدل میزبانی شده
این مدل بخشی از APK برنامه شماست که باعث افزایش حجم آن می‌شود. این مدل بخشی از APK شما نیست. با آپلود در فضای ابری میزبانی می‌شود. توصیه می‌کنیم از فضای ابری برای Firebase استفاده کنید.
این مدل بلافاصله در دسترس است، حتی زمانی که دستگاه اندروید آفلاین باشد برنامه شما باید شامل کدی باشد که مدل را بر اساس تقاضا دانلود کند
نیازی به پروژه Firebase نیست به یک پروژه Firebase نیاز دارد (در صورت استفاده از Cloud Storage برای Firebase).
برای به‌روزرسانی مدل، باید برنامه خود را دوباره منتشر کنید به‌روزرسانی‌های مدل را بدون انتشار مجدد برنامه خود، ارسال کنید
تست A/B داخلی ندارد تست A/B با پیکربندی از راه دور Firebase

امتحانش کن.

قبل از اینکه شروع کنی

۱. در فایل build.gradle.kts در سطح پروژه، مطمئن شوید که مخزن Maven گوگل را هم در بخش‌های buildscript و هم allprojects خود وارد کرده‌اید.

  1. وابستگی‌های کتابخانه‌های اندروید ML Kit را به فایل gradle سطح app ماژول خود که معمولاً app/build.gradle.kts است، اضافه کنید:

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

    dependencies {
      // ...
      // Object detection & tracking feature with custom bundled model
      implementation("com.google.mlkit:object-detection-custom:17.0.2")
    }
    
  2. اگر می‌خواهید مدلی را از فضای ذخیره‌سازی ابری برای فایربیس دانلود کنید ، اگر قبلاً فایربیس را به پروژه اندروید خود اضافه نکرده‌اید، حتماً آن را اضافه کنید . این کار هنگام بسته‌بندی مدل لازم نیست.

۱. مدل را بارگذاری کنید

می‌توانید مدل را از یک منبع محلی یا یک منبع میزبانی شده از راه دور بارگذاری کنید.

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

برای اتصال مدل به برنامه خود:

  1. فایل مدل (که معمولاً به .tflite یا .lite ختم می‌شود) را در پوشه assets/ برنامه خود کپی کنید. (ممکن است لازم باشد ابتدا با کلیک راست روی app/ پوشه و سپس کلیک روی New > Folder > Assets Folder ، پوشه را ایجاد کنید.)

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

    کاتلین

    val localModel = LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute path to model file)
            // or .setUri(URI to model file)
            .build()

    جاوا

    LocalModel localModel =
        new LocalModel.Builder()
            .setAssetFilePath("model.tflite")
            // or .setAbsoluteFilePath(absolute path to model file)
            // or .setUri(URI to model file)
            .build();

پیکربندی یک منبع مدل میزبانی شده از راه دور

برای استفاده از مدل میزبانی‌شده از راه دور، باید فایل مدل را با استفاده از منطق برنامه خود در حافظه محلی دستگاه دانلود کنید و سپس آن را به عنوان یک مدل محلی بارگذاری کنید. توصیه می‌کنیم برای میزبانی یک مدل از فضای ذخیره‌سازی ابری برای فایربیس استفاده کنید. برای جزئیات پیاده‌سازی، به راهنمای مهاجرت از فایربیس ML به فضای ذخیره‌سازی ابری مراجعه کنید.

۲. آشکارساز شیء را پیکربندی کنید

پس از پیکربندی منابع مدل خود، آشکارساز شیء را برای مورد استفاده خود با یک شیء CustomObjectDetectorOptions پیکربندی کنید. می‌توانید تنظیمات زیر را تغییر دهید:

تنظیمات آشکارساز شیء
حالت تشخیص STREAM_MODE (پیش‌فرض) | SINGLE_IMAGE_MODE

در STREAM_MODE (پیش‌فرض)، آشکارساز شیء با تأخیر کم اجرا می‌شود، اما ممکن است در چند فراخوانی اول آشکارساز، نتایج ناقصی (مانند کادرهای محدودکننده نامشخص یا برچسب‌های دسته‌بندی) تولید کند. همچنین، در STREAM_MODE ، آشکارساز شناسه‌های ردیابی را به اشیاء اختصاص می‌دهد که می‌توانید از آنها برای ردیابی اشیاء در فریم‌ها استفاده کنید. از این حالت زمانی استفاده کنید که می‌خواهید اشیاء را ردیابی کنید یا زمانی که تأخیر کم مهم است، مانند پردازش جریان‌های ویدیویی در زمان واقعی.

در SINGLE_IMAGE_MODE ، آشکارساز شیء نتیجه را پس از تعیین محدوده شیء برمی‌گرداند. اگر طبقه‌بندی را نیز فعال کنید، نتیجه را پس از در دسترس بودن محدوده و برچسب دسته‌بندی برمی‌گرداند. در نتیجه، تأخیر تشخیص به طور بالقوه بیشتر است. همچنین، در SINGLE_IMAGE_MODE ، شناسه‌های ردیابی اختصاص داده نمی‌شوند. اگر تأخیر حیاتی نیست و نمی‌خواهید با نتایج جزئی سر و کار داشته باشید، از این حالت استفاده کنید.

تشخیص و ردیابی چندین شیء false (پیش‌فرض) | true

اینکه آیا تا پنج شیء شناسایی و ردیابی شود یا فقط برجسته‌ترین شیء (پیش‌فرض).

طبقه‌بندی اشیاء false (پیش‌فرض) | true

آیا اشیاء شناسایی شده با استفاده از مدل طبقه‌بندی سفارشی ارائه شده طبقه‌بندی شوند یا خیر. برای استفاده از مدل طبقه‌بندی سفارشی خود، باید این را روی true تنظیم کنید.

آستانه اطمینان طبقه‌بندی

حداقل امتیاز اطمینان برچسب‌های شناسایی‌شده. در صورت عدم تنظیم، از هر آستانه طبقه‌بندی‌کننده‌ای که توسط فراداده مدل مشخص شده باشد، استفاده خواهد شد. اگر مدل حاوی هیچ فراداده‌ای نباشد یا فراداده آستانه طبقه‌بندی‌کننده‌ای را مشخص نکند، از آستانه پیش‌فرض ۰.۰ استفاده خواهد شد.

حداکثر برچسب برای هر شیء

حداکثر تعداد برچسب‌هایی که آشکارساز به ازای هر شیء برمی‌گرداند. اگر تنظیم نشود، مقدار پیش‌فرض ۱۰ استفاده خواهد شد.

API تشخیص و ردیابی اشیا برای این دو مورد استفاده اصلی بهینه شده است:

  • تشخیص و ردیابی زنده برجسته‌ترین شیء در منظره‌یاب دوربین.
  • تشخیص چندین شیء از یک تصویر ثابت

برای پیکربندی API برای این موارد استفاده، با یک مدل بسته‌بندی‌شده محلی:

کاتلین

// Live detection and tracking
val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

// Multiple object detection in static images
val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(localModel)
        .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
        .enableMultipleObjects()
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)

جاوا

// Live detection and tracking
CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(localModel)
                .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

// Multiple object detection in static images
CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(localModel)
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableMultipleObjects()
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

ObjectDetector objectDetector =
    ObjectDetection.getClient(customObjectDetectorOptions);

اگر مدلی دارید که از راه دور میزبانی می‌شود، باید قبل از اجرای آن، بررسی کنید که دانلود شده باشد.

اگرچه شما فقط باید قبل از اجرای آشکارساز این موضوع را تأیید کنید، اما اگر هم یک مدل میزبانی از راه دور و هم یک مدل بسته‌بندی شده محلی دارید، انجام این بررسی هنگام نمونه‌سازی آشکارساز تصویر منطقی است: اگر مدل از راه دور دانلود شده است، یک آشکارساز از آن و در غیر این صورت از مدل محلی ایجاد کنید.

کاتلین

val modelFile = File(context.cacheDir, "my_remote_model.tflite")

val model = if (modelFile.exists()) {
    // Use the downloaded model if available
    LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build()
} else {
    // Fall back to the bundled model
    LocalModel.Builder().setAssetFilePath("model.tflite").build()
}

val customObjectDetectorOptions =
        CustomObjectDetectorOptions.Builder(model)
        .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
        .enableClassification()
        .setClassificationConfidenceThreshold(0.5f)
        .setMaxPerObjectLabelCount(3)
        .build()

val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)

جاوا

File modelFile = new File(context.getCacheDir(), "my_remote_model.tflite");

LocalModel model;
if (modelFile.exists()) {
    // Use the downloaded model if available
    model = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build();
} else {
    // Fall back to the bundled model
    model = new LocalModel.Builder().setAssetFilePath("model.tflite").build();
}

CustomObjectDetectorOptions customObjectDetectorOptions =
        new CustomObjectDetectorOptions.Builder(model)
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();

ObjectDetector objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions);

اگر فقط یک مدل میزبانی‌شده از راه دور دارید، باید عملکردهای مرتبط با مدل را غیرفعال کنید - مثلاً بخشی از رابط کاربری خود را خاکستری کنید یا پنهان کنید - تا زمانی که تأیید کنید مدل دانلود شده است.

کاتلین

val localFile = File(context.cacheDir, "my_remote_model.tflite")
if (localFile.exists()) {
    // Model is already cached, initialize immediately
    initializeDetector(localFile)
} else {
    // Model is not yet available, show loading UI and start download
    showLoadingUI()
    val storage = Firebase.storage
    val modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite")
    modelRef.getFile(localFile)
        .addOnSuccessListener {
            // Download complete, initialize the detector
            hideLoadingUI()
            initializeDetector(localFile)
        }
        .addOnFailureListener {
            // Handle download error
            showErrorUI()
        }
}

private fun initializeDetector(modelFile: File) {
    val localModel = LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build()
    val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(localModel)
            .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
            .enableClassification()
            .build()
    val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions)
    // Enable ML-related UI features here
    enableMLFeatures(objectDetector)
}

جاوا

File localFile = new File(context.getCacheDir(), "my_remote_model.tflite");
if (localFile.exists()) {
    // Model is already cached, initialize immediately
    initializeDetector(localFile);
} else {
    // Model is not yet available, show loading UI and start download
    showLoadingUI();
    FirebaseStorage storage = FirebaseStorage.getInstance();
    StorageReference modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite");
    modelRef.getFile(localFile)
        .addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
                // Download complete, initialize the detector
                hideLoadingUI();
                initializeDetector(localFile);
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception exception) {
                // Handle download error
                showErrorUI();
            }
        });
}

private void initializeDetector(File modelFile) {
    LocalModel localModel = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build();
    CustomObjectDetectorOptions customObjectDetectorOptions =
            new CustomObjectDetectorOptions.Builder(localModel)
                    .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                    .enableClassification()
                    .build();
    ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions);
    // Enable ML-related UI features here
    enableMLFeatures(objectDetector);
}

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

یک شیء InputImage از تصویر خود ایجاد کنید. تشخیص‌دهنده شیء مستقیماً از Bitmap ، NV21 ByteBuffer یا YUV_420_888 media.Image اجرا می‌شود. اگر به یکی از آنها دسترسی مستقیم دارید، ساخت یک InputImage از این منابع توصیه می‌شود. اگر یک InputImage از منابع دیگر بسازید، ما تبدیل را به صورت داخلی برای شما انجام خواهیم داد و ممکن است کارایی کمتری داشته باشد.

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

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

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

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

کاتلین

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

اگر از کتابخانه دوربینی که درجه چرخش تصویر را به شما بدهد استفاده نمی‌کنید، می‌توانید آن را از درجه چرخش دستگاه و جهت سنسور دوربین در دستگاه محاسبه کنید:

کاتلین

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() ارسال کنید:

کاتلین

val image = InputImage.fromMediaImage(mediaImage, rotation)

Java

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

استفاده از یک URI فایل

برای ایجاد یک شیء InputImage از یک URI فایل، متن برنامه و URI فایل را به InputImage.fromFilePath() ارسال کنید. این زمانی مفید است که از یک ACTION_GET_CONTENT برای وادار کردن کاربر به انتخاب یک تصویر از برنامه گالری خود استفاده می‌کنید.

کاتلین

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 را با بافر یا آرایه، به همراه ارتفاع، عرض، فرمت کدگذاری رنگ و درجه چرخش تصویر ایجاد کنید:

کاتلین

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 ، تعریف زیر را انجام دهید:

کاتلین

val image = InputImage.fromBitmap(bitmap, 0)

Java

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

تصویر توسط یک شیء Bitmap به همراه درجه چرخش نمایش داده می‌شود.

۴. آشکارساز شیء را اجرا کنید

کاتلین

objectDetector
    .process(image)
    .addOnFailureListener(e -> {...})
    .addOnSuccessListener(results -> {
        for (detectedObject in results) {
          // ...
        }
    });

جاوا

objectDetector
    .process(image)
    .addOnFailureListener(e -> {...})
    .addOnSuccessListener(results -> {
        for (DetectedObject detectedObject : results) {
          // ...
        }
    });

۵. اطلاعات مربوط به اشیاء برچسب‌گذاری شده را دریافت کنید

اگر فراخوانی process() موفقیت‌آمیز باشد، فهرستی از DetectedObject ها به شنونده‌ی موفقیت ارسال می‌شود.

هر DetectedObject شامل ویژگی‌های زیر است:

جعبه محدود کننده یک Rect که موقعیت شیء را در تصویر نشان می‌دهد.
شناسه ردیابی یک عدد صحیح که شیء را در تصاویر مشخص می‌کند. در SINGLE_IMAGE_MODE مقدار null دارد.
برچسب‌ها
توضیحات برچسب توضیحات متنی برچسب. فقط در صورتی برگردانده می‌شود که فراداده‌های مدل LiteRT شامل توضیحات برچسب باشند.
فهرست برچسب شاخص برچسب در میان تمام برچسب‌های پشتیبانی‌شده توسط طبقه‌بندی‌کننده.
برچسب اعتماد به نفس مقدار اطمینان طبقه‌بندی شیء.

کاتلین

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (detectedObject in results) {
    val boundingBox = detectedObject.boundingBox
    val trackingId = detectedObject.trackingId
    for (label in detectedObject.labels) {
      val text = label.text
      val index = label.index
      val confidence = label.confidence
    }
}

جاوا

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (DetectedObject detectedObject : results) {
  Rect boundingBox = detectedObject.getBoundingBox();
  Integer trackingId = detectedObject.getTrackingId();
  for (Label label : detectedObject.getLabels()) {
    String text = label.getText();
    int index = label.getIndex();
    float confidence = label.getConfidence();
  }
}

تضمین یک تجربه کاربری عالی

برای بهترین تجربه کاربری، این دستورالعمل‌ها را در برنامه خود دنبال کنید:

  • تشخیص موفقیت‌آمیز اشیاء به پیچیدگی بصری شیء بستگی دارد. برای تشخیص، اشیاء با تعداد کمی از ویژگی‌های بصری ممکن است نیاز داشته باشند که بخش بیشتری از تصویر را اشغال کنند. شما باید کاربران را در مورد دریافت ورودی که با نوع اشیاء مورد نظر شما مطابقت دارد، راهنمایی کنید.
  • وقتی از طبقه‌بندی استفاده می‌کنید، اگر می‌خواهید اشیایی را که به طور کامل در دسته‌های پشتیبانی‌شده قرار نمی‌گیرند، شناسایی کنید، برای اشیای ناشناخته، روش‌های خاصی را پیاده‌سازی کنید.

همچنین، اپلیکیشن ویترین ML Kit Material Design و مجموعه ویژگی‌های Material Design Patterns for machine-powered را بررسی کنید.

بهبود عملکرد

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

  • وقتی از حالت استریمینگ در یک برنامه‌ی بلادرنگ استفاده می‌کنید، از تشخیص چند شیء استفاده نکنید، زیرا اکثر دستگاه‌ها قادر به تولید فریم‌ریت کافی نخواهند بود.

  • اگر از API Camera یا camera2 استفاده می‌کنید، فراخوانی‌های throttle به آشکارساز انجام می‌شود. اگر در حین اجرای آشکارساز، یک فریم ویدیویی جدید در دسترس قرار گرفت، فریم را حذف کنید. برای مثال، به کلاس VisionProcessorBase در برنامه نمونه شروع سریع مراجعه کنید.
  • اگر از API CameraX استفاده می‌کنید، مطمئن شوید که استراتژی فشار معکوس (backpressure strategy) روی مقدار پیش‌فرض خود یعنی ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST تنظیم شده است. این تضمین می‌کند که فقط یک تصویر در هر زمان برای تجزیه و تحلیل تحویل داده می‌شود. اگر تصاویر بیشتری هنگام مشغول بودن تحلیلگر تولید شوند، به طور خودکار حذف می‌شوند و برای تحویل در صف قرار نمی‌گیرند. پس از بسته شدن تصویر در حال تجزیه و تحلیل با فراخوانی ImageProxy.close()، آخرین تصویر بعدی تحویل داده می‌شود.
  • اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده می‌کنید، ابتدا نتیجه را از کیت ML دریافت کنید، سپس تصویر و همپوشانی را در یک مرحله رندر کنید. این کار فقط یک بار برای هر فریم ورودی روی سطح نمایشگر رندر می‌شود. برای مثال به کلاس‌های CameraSourcePreview و GraphicOverlay در برنامه نمونه شروع سریع مراجعه کنید.
  • اگر از API دوربین ۲ استفاده می‌کنید، تصاویر را با فرمت ImageFormat.YUV_420_888 ضبط کنید. اگر از API دوربین قدیمی‌تر استفاده می‌کنید، تصاویر را با فرمت ImageFormat.NV21 ضبط کنید.