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

يمكنك استخدام أدوات تعلّم الآلة لرصد العناصر وتتبّعها في إطارات الفيديو المتتالية.

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

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

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

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

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

قبل البدء

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

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

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

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

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

    dependencies {
      // ...
      // Object detection & tracking feature with model downloaded
      // from firebase
      implementation 'com.google.mlkit:object-detection-custom:17.0.1'
      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"
        }
    }
    

    سيتم تضمين ملف النموذج في حزمة التطبيق وإتاحته لحزمة تعلّم الآلة كمادة عرض أولية.

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

ضمان تجربة رائعة للمستخدم

لأفضل تجربة مستخدم، يُرجى اتّباع الإرشادات التالية في تطبيقك:

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

يمكنك أيضًا الاطّلاع على تطبيق عرض Material Design في ML Kit ومجموعة أنماط التصميم المتعدد الأبعاد للميزات المستندة إلى تعلُّم الآلة.

Improving performance

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

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

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