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

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

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

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

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

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

جرّبه الآن

قبل البدء

  1. في ملف build.gradle على مستوى المشروع، تأكَّد من تضمين مستودع Maven التابع لشركة Google في كل من 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.2'
    }
    

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

    dependencies {
      // ...
      // Object detection & tracking feature with model downloaded
      // from firebase
      implementation 'com.google.mlkit:object-detection-custom:17.0.2'
      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 تحتوي البيانات الوصفية للنموذج البسيط على أوصاف للتصنيفات.
فهرس التصنيف فهرس التسمية بين كافة التسميات التي تدعمها العلامة المصنِّف.
الثقة في التصنيف قيمة الثقة لتصنيف العنصر.

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

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

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

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

يمكنك أيضًا الاطّلاع على تطبيق عرض التصميم المتعدد الأبعاد في حزمة تعلّم الآلة التصميم المتعدد الأبعاد مجموعة أنماط الميزات المستنِدة إلى تعلُّم الآلة:

تحسين الأداء

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

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

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