رصد العناصر وتتبّعها وتصنيفها باستخدام نموذج تصنيف مخصّص على نظام Android

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.
يمكنك استخدام مجموعة أدوات تعلّم الآلة لرصد الكائنات وتتبُّعها في إطارات الفيديو المتتالية.

عند تمرير صورة إلى مجموعة أدوات تعلّم الآلة، يتم رصد ما يصل إلى خمسة كائنات في الصورة بالإضافة إلى موضع كل عنصر في الصورة. عند اكتشاف كائنات في مجموعات بث الفيديو، يكون لكل كائن معرّف فريد يمكنك استخدامه لتتبع الكائن من إطار إلى آخر.

يمكنك استخدام نموذج مخصّص لتصنيف الصور لتصنيف العناصر التي تم رصدها. يُرجى الرجوع إلى النماذج المخصّصة باستخدام ML Kit للحصول على إرشادات حول متطلبات التوافق مع النماذج، وأين يمكنك العثور على النماذج المدرَّبة مسبقًا، وكيفية تدريب نماذجك الخاصة.

هناك طريقتان لدمج نموذج مخصص. يمكنك تجميع النموذج عن طريق وضعه داخل مجلد مواد العرض لتطبيقك، أو يمكنك تنزيله ديناميكيًا من Firebase. يقارن الجدول التالي بين الخيارين.

نموذج مجمّع نموذج مستضاف
النموذج هو جزء من حِزمة APK لتطبيقك، ما يؤدي إلى زيادة حجمه. النموذج ليس جزءًا من ملف APK. تتم استضافتها من خلال تحميل المحتوى إلى Firebase Machine Learning.
يتوفّر النموذج على الفور حتى إذا كان جهاز Android غير متصل بالإنترنت. يتم تنزيل النموذج عند الطلب
لا حاجة لمشروع Firebase مطلوب مشروع Firebase
يجب إعادة نشر تطبيقك لتحديث النموذج نشر تحديثات النموذج بدون إعادة نشر تطبيقك
لا يوجد اختبار A/B مدمج إجراء اختبار أ/ب بسهولة باستخدام ميزة الإعداد عن بُعد في Firebase

تجربة السمات والبيانات

قبل البدء

  1. في ملف build.gradle على مستوى المشروع، تأكَّد من تضمين مستودع Google Maven في كل من القسمين buildscript وallprojects.

  2. أضِف التبعيات لمكتبات ML Kit في Android إلى ملف Gradle على مستوى التطبيق في وحدتك، والذي عادة ما يكون app/build.gradle:

    لتجميع نموذج مع تطبيقك:

    dependencies {
      // ...
      // Object detection & tracking feature with custom bundled model
      implementation 'com.google.mlkit:object-detection-custom:17.0.0'
    }
    

    لتنزيل نموذج ديناميكيًا من Firebase، أضف التبعية linkFirebase:

    dependencies {
      // ...
      // Object detection & tracking feature with model downloaded
      // from firebase
      implementation 'com.google.mlkit:object-detection-custom:17.0.0'
      implementation 'com.google.mlkit:linkfirebase:17.0.0'
    }
    
  3. إذا كنت ترغب في تنزيل نموذج، فتأكد من إضافة Firebase إلى مشروع Android، إذا لم تكن قد فعلت ذلك، فهذا غير مطلوب عند تجميع النموذج.

1- تحميل النموذج

تهيئة مصدر نموذج محلي

لتجميع النموذج مع تطبيقك:

  1. انسخ ملف النموذج (الذي ينتهي عادة بـ .tflite أو .lite) إلى مجلد assets/ لتطبيقك. (قد تحتاج إلى إنشاء المجلد أولاً من خلال النقر بزر الماوس الأيمن على المجلد app/، ثم النقر على جديد > المجلد > مجلد مواد العرض.)

  2. بعد ذلك، أضِف ما يلي إلى ملف build.gradle لتطبيقك لضمان عدم ضغط Gradle ملف النموذج عند إنشاء التطبيق:

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

    سيتم تضمين ملف النموذج في حزمة التطبيق وسيتوفر لـ ML Kit باعتباره مادة عرض أولية.

  3. إنشاء كائن LocalModel، مع تحديد المسار إلى ملف النموذج:

    Kotlin

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

    لغة Java

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

إعداد مصدر نموذج مستضاف على Firebase

لاستخدام النموذج المستضاف عن بُعد، أنشئ كائن CustomRemoteModel قبل FirebaseModelSource، مع تحديد الاسم الذي عينته للنموذج عند نشره:

Kotlin

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

لغة Java

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

بعد ذلك، ابدأ مهمة تنزيل النموذج، مع تحديد الشروط التي تريد السماح بالتنزيل بموجبها. إذا لم يكن النموذج على الجهاز، أو إذا كان إصدار أحدث من النموذج متاحًا، فسيتم تنزيل النموذج من Firebase بشكل غير متزامن:

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

تبدأ العديد من التطبيقات مهمة التنزيل في رمز التهيئة، ولكن يمكنك القيام بذلك في أي وقت قبل أن تحتاج إلى استخدام النموذج.

2. ضبط أداة رصد الكائنات

بعد ضبط مصادر النموذج، يمكنك ضبط أداة رصد الكائنات لحالة الاستخدام باستخدام كائن CustomObjectDetectorOptions. يمكنك تغيير الإعدادات التالية:

إعدادات أداة كشف الكائنات
وضع الاكتشاف STREAM_MODE (تلقائي) | SINGLE_IMAGE_MODE

في STREAM_MODE (الإعداد التلقائي)، يتم تشغيل أداة رصد الكائنات مع وقت استجابة سريع، ولكنها قد تؤدي إلى نتائج غير مكتملة (مثل مربعات تحديد غير محدّدة أو تصنيفات فئات) عند الاستدعاءات القليلة الأولى لأداة الكشف. في STREAM_MODE أيضًا، تُحدِّد أداة الرصد أرقام تعريف التتبُّع للعناصر، والتي يمكنك استخدامها لتتبُّع العناصر على مستوى عدة إطارات. استخدِم هذا الوضع عندما تريد تتبّع العناصر أو عندما يكون وقت الاستجابة السريع مهمًا، مثلاً عند معالجة مجموعات بث الفيديو في الوقت الفعلي.

في SINGLE_IMAGE_MODE، يعرض جهاز رصد الكائنات النتيجة بعد تحديد مربع إحاطة الكائن. وفي حال تفعيل التصنيف أيضًا، سيتم عرض النتيجة بعد توفّر المربّع المحيط وفئة الفئة. ونتيجةً لذلك، من المحتمل أن يكون وقت استجابة الاكتشاف أعلى. في SINGLE_IMAGE_MODE أيضًا، لا يتم تحديد أرقام تعريف التتبّع. استخدِم هذا الوضع إذا لم يكن وقت الاستجابة مهمًا ولا تريد التعامل مع النتائج الجزئية.

اكتشاف كائنات متعددة وتتبعها false (تلقائي) | true

يمكنك رصد ما يصل إلى خمسة كائنات أو تتبّعها أو رصد الكائن الأبرز فقط (الإعداد التلقائي).

تصنيف العناصر false (تلقائي) | true

لتحديد ما إذا كان سيتم تصنيف الكائنات المكتشَفة أم لا باستخدام نموذج المصنِّف المخصّص المقدَّم. لاستخدام نموذج التصنيف المخصّص، يجب ضبطه على true.

الحد الأدنى لتصنيف التصنيف

الحد الأدنى من درجة الثقة للتصنيفات التي تم اكتشافها. وفي حال تم ترك السياسة بدون ضبط، سيتم استخدام أي حدّ أدنى للتصنيف تحدّده البيانات الوصفية للنموذج. إذا لم يتضمّن النموذج أي بيانات وصفية أو لم تحدّد البيانات الوصفية حدًّا للتصنيف، سيتم استخدام حد تلقائي بقيمة 0.0.

الحد الأقصى للتصنيفات لكل كائن

الحد الأقصى لعدد التصنيفات لكل كائن ستعرضه أداة الكشف. وفي حال تم ترك السياسة بدون ضبط، سيتم استخدام القيمة التلقائية وهي 10.

تم تحسين واجهة برمجة التطبيقات لاكتشاف العناصر وتتبع واجهة برمجة التطبيقات لهاتين حالتي الاستخدام الأساسية:

  • الاكتشاف المباشر وتتبع أكثر الكائنات البارزة في عدسة الكاميرا.
  • اكتشاف كائنات متعددة من صورة ثابتة.

لتهيئة واجهة برمجة التطبيقات لحالات الاستخدام هذه، باستخدام نموذج مُجمع على المستوى المحلي:

Kotlin

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

لغة Java

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

إذا كان لديك نموذج تتم استضافته عن بُعد، يجب عليك التحقق من أنه قد تم تنزيله قبل تشغيله. يمكنك التحقق من حالة مهمة تنزيل النموذج باستخدام طريقة مدير النموذج isModelDownloaded().

على الرغم من الحاجة إلى تأكيد ذلك فقط قبل تشغيل أداة الرصد، إذا كان لديك نموذج مستضاف عن بُعد ونموذج مُجمَّع محليًا، قد يكون من المنطقي إجراء هذا الفحص عند إنشاء أداة رصد الصور: يمكنك إنشاء أداة رصد من النموذج البعيد إذا تم تنزيله، ومن النموذج المحلي بخلاف ذلك.

Kotlin

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener { isDownloaded ->
    val optionsBuilder =
        if (isDownloaded) {
            CustomObjectDetectorOptions.Builder(remoteModel)
        } else {
            CustomObjectDetectorOptions.Builder(localModel)
        }
    val customObjectDetectorOptions = optionsBuilder
            .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
            .enableClassification()
            .setClassificationConfidenceThreshold(0.5f)
            .setMaxPerObjectLabelCount(3)
            .build()
    val objectDetector =
        ObjectDetection.getClient(customObjectDetectorOptions)
}

لغة Java

RemoteModelManager.getInstance().isModelDownloaded(remoteModel)
    .addOnSuccessListener(new OnSuccessListener() {
        @Override
        public void onSuccess(Boolean isDownloaded) {
            CustomObjectDetectorOptions.Builder optionsBuilder;
            if (isDownloaded) {
                optionsBuilder = new CustomObjectDetectorOptions.Builder(remoteModel);
            } else {
                optionsBuilder = new CustomObjectDetectorOptions.Builder(localModel);
            }
            CustomObjectDetectorOptions customObjectDetectorOptions = optionsBuilder
                .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE)
                .enableClassification()
                .setClassificationConfidenceThreshold(0.5f)
                .setMaxPerObjectLabelCount(3)
                .build();
            ObjectDetector objectDetector =
                ObjectDetection.getClient(customObjectDetectorOptions);
        }
});

إذا لم يكن لديك سوى نموذج تتم استضافته عن بُعد، يجب تعطيل الوظائف ذات الصلة بالنموذج، على سبيل المثال، الرمادي أو إخفاء جزء من واجهة المستخدم، إلى أن تؤكد أنه تم تنزيل النموذج. يمكنك تنفيذ ذلك من خلال إرفاق أداة معالجة بأسلوب 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.
            }
        });

3- إعداد صورة الإدخال

أنشئ كائن 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 قيمة التدوير لك.

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)، يجب تمرير سياق التطبيق ومعرّف الموارد المنتظم (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();
}

استخدام 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
)

لغة 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 مع درجات التدوير.

4. تشغيل أداة رصد الكائنات

Kotlin

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

لغة Java

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

5- الحصول على معلومات حول الكائنات المصنّفة

إذا تم استدعاء process() بنجاح، يتم إرسال قائمة DetectedObject إلى المستمع الناجح.

يحتوي كل DetectedObject على الخصائص التالية:

مربّع ربط تمثّل هذه الخاصية Rect الذي يشير إلى موضع العنصر في الصورة.
الرقم التعريفي للتتبع عدد صحيح يحدد الكائن عبر الصور. قيمة فارغة في SINGLE_IMAGE_MODE.
التصنيفات
وصف التصنيف تمثل هذه الخاصية الوصف النصي للتصنيف. لا يتم عرضه إلا إذا كانت البيانات الوصفية لنموذج TensorFlow Lite تحتوي على أوصاف تصنيفات.
فهرس التصنيفات فهرس التصنيفات ضمن جميع التصنيفات المتوافقة مع المصنِّف.
تصنيف الثقة قيمة الثقة لتصنيف الكائن.

Kotlin

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

لغة Java

// 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 ومجموعتي تصميمات الميزات التي تستند إلى تعلُّم الآلة.

Improving performance

إذا كنت تريد استخدام اكتشاف الكائنات في تطبيق في الوقت الفعلي، فاتبع هذه الإرشادات لتحقيق أفضل معدلات عرض إطارات:

  • عند استخدام وضع البث في تطبيق في الوقت الفعلي، لا تستخدم ميزة رصد كائنات متعددة لأن معظم الأجهزة لن تتمكّن من إنشاء معدلات عرض إطارات مناسبة.

  • في حال استخدام واجهة برمجة التطبيقات Camera أو camera2، عليك التحكُّم في عمليات الرصد. في حال توفّر إطار فيديو جديد أثناء تشغيل أداة الرصد، أفلِت الإطار. للاطّلاع على مثال، يمكنك مراجعة الصف VisionProcessorBase في نموذج التطبيق للبدء السريع.
  • إذا كنت تستخدم واجهة برمجة تطبيقات CameraX، تأكّد من ضبط استراتيجية الضغط العكسي على قيمتها التلقائية ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. ويضمن ذلك تسليم صورة واحدة فقط للتحليل في كل مرة. وإذا تم إنشاء المزيد من الصور عندما تكون أداة التحليل مشغولة، سيتم إسقاطها تلقائيًا ولن يتم وضعها في قائمة الانتظار للتسليم. بعد إغلاق الصورة التي يتم تحليلها عن طريق طلب ImageProxy.close()، سيتم تسليم الصورة التالية التالية.
  • إذا كنت تستخدم إخراج أداة الرصد لتركيب الرسومات على الصورة المُدخلة، عليك الحصول على النتيجة من ML Kit، ثم عرض الصورة والتراكب في خطوة واحدة. يتم عرض هذا المحتوى على سطح العرض مرة واحدة فقط لكل إطار إدخال. للاطّلاع على مثال، يمكنك الاطّلاع على الصفَّين CameraSourcePreview و GraphicOverlay في نموذج التطبيق السريع للبدء.
  • إذا كنت تستخدم Camera2 API، يمكنك التقاط الصور بتنسيق ImageFormat.YUV_420_888. إذا كنت تستخدم إصدارًا قديمًا من واجهة برمجة تطبيقات الكاميرا، التقِط الصور بتنسيق ImageFormat.NV21.