זיהוי, מעקב וסיווג של אובייקטים באמצעות מודל סיווג בהתאמה אישית ב-Android

אפשר להשתמש ב-ML Kit כדי לזהות אובייקטים ולעקוב אחריהם בפריימים עוקבים של וידאו.

כשמעבירים תמונה לערכת ML, היא מזהה עד חמישה אובייקטים בתמונה וכן המיקום של כל אובייקט בתמונה. בעת זיהוי אובייקטים ב: וידאו בסטרימינג, לכל אובייקט יש מזהה ייחודי שאפשר להשתמש בו כדי לעקוב אחרי האובייקט ממסגרת למסגרת.

אפשר להשתמש במודל מותאם אישית לסיווג תמונות כדי לסווג את האובייקטים. זוהה. במודלים מותאמים אישית עם ערכת ML אפשר לקרוא הדרכה לגבי דרישות תאימות של מודלים, איפה למצוא מודלים שעברו אימון מראש, ואיך לאמן את המודלים שלכם.

יש שתי דרכים לשלב מודל מותאם אישית. אפשר לקבץ את המודל לפי להוסיף אותו לתיקיית הנכסים של האפליקציה, או שאפשר להוריד אותו באופן דינמי. מ-Firebase. בטבלה הבאה ניתן לראות השוואה בין שתי האפשרויות.

מודל בחבילה מודל מתארח
המודל הוא חלק מה-APK של האפליקציה, ולכן הוא מגדיל את הגודל שלו. המודל אינו חלק מה-APK שלך. כדי לארח את הסרטון, צריך להעלות אותו אל למידת מכונה ב-Firebase.
המודל זמין באופן מיידי, גם כשמכשיר Android במצב אופליין הורדת המודל מתבצעת על פי דרישה
אין צורך בפרויקט Firebase נדרש פרויקט Firebase
צריך לפרסם מחדש את האפליקציה כדי לעדכן את המודל דחיפת עדכוני מודל בלי לפרסם מחדש את האפליקציה
אין בדיקות A/B מובנות לבצע בדיקת A/B קלה ופשוטה באמצעות הגדרת תצורה מרחוק ב-Firebase

רוצה לנסות?

לפני שמתחילים

  1. בקובץ build.gradle ברמת הפרויקט, חשוב לכלול מאגר Maven של Google גם ב-buildscript וגם allprojects קטעים.

  2. הוספת יחסי התלות של ספריות ML Kit Android למודול של המודול קובץ GRid ברמת האפליקציה, שהוא בדרך כלל 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"
        }
    }
    

    קובץ המודל ייכלל בחבילת האפליקציה ויהיה זמין ל-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.

ה-API לזיהוי אובייקטים ולמעקב מותאם לשני השימושים העיקריים האלה במקרים:

  • זיהוי בזמן אמת ומעקב אחרי האובייקט הבולט ביותר במצלמה את העינית.
  • זיהוי של מספר אובייקטים מתמונה סטטית.

כדי להגדיר את ה-API לתרחישים לדוגמה האלה באמצעות מודל באריזה מקומית:

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

אם יש לך רק מודל שמתארח מרחוק, עליך להשבית את התכונה שקשורה למודלים פונקציונליות - לדוגמה, הצגה באפור או הסתרה של חלק מממשק המשתמש - עד מוודאים שבוצעה הורדה של המודל. אפשר לעשות זאת על ידי צירוף listen ל-method 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() זה שימושי כאשר צריך להשתמש ב-Intent 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 מכיל את המאפיינים (properties) הבאים:

תיבה קשורה Rect שמציין את המיקום של האובייקט תמונה.
מזהה לצורכי מעקב מספר שלם שמזהה את האובייקט בתמונות. אפס SINGLE_IMAGE_מצב.
תוויות
תיאור תווית תיאור הטקסט של התווית. מוחזר רק אם 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 Material Design עיצוב חומר איסוף תבניות לתכונות מבוססות-למידת מכונה.

Improving performance

כדי להשתמש בזיהוי אובייקטים באפליקציה בזמן אמת, צריך לפעול לפי השלבים הבאים: כדי להשיג את קצבי הפריימים הטובים ביותר:

  • כשמשתמשים במצב סטרימינג באפליקציה בזמן אמת, אין להשתמש בכמה זיהוי אובייקטים, כי רוב המכשירים לא יוכלו לייצר קצבי פריימים מתאימים.

  • אם משתמשים Camera או camera2 API, הפעלות של הגלאי באמצעות ויסות נתונים (throttle). אם מדובר בסרטון חדש הופכת לזמינה כשהגלאי פועל, משחררים את הפריים. לצפייה VisionProcessorBase באפליקציה לדוגמה של המדריך למתחילים.
  • אם אתם משתמשים ב-API של CameraX, יש לוודא שאסטרטגיית הלחץ החוזר מוגדרת לערך ברירת המחדל שלה ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST כך אפשר להבטיח שרק תמונה אחת תוצג לניתוח בכל פעם. אם עוד תמונות שנוצרות כשהכלי לניתוח נתונים עמוס, הוא יוסר באופן אוטומטי ולא ימתין בתור משלוח. לאחר שהתמונה שמנתחת נסגרת על ידי קריאה ImageProxy.close(), התמונה האחרונה הבאה תישלח.
  • אם משתמשים בפלט של הגלאי כדי להציג גרפיקה בשכבת-על מקבלים קודם את התוצאה מ-ML Kit ואז מעבדים את התמונה וליצור שכבת-על בשלב אחד. הוא מוצג לפני השטח של המסך פעם אחת בלבד לכל מסגרת קלט. לצפייה CameraSourcePreview וגם GraphicOverlay, באפליקציה לדוגמה של המדריך למתחילים.
  • אם משתמשים ב- Camera2 API, מצלמים תמונות ב פורמט של ImageFormat.YUV_420_888. אם משתמשים בגרסה הישנה של ה-API של המצלמה, מצלמים תמונות ב פורמט של ImageFormat.NV21.