תייג תמונות עם ערכת ML באנדרואיד

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

לפני שאתה מתחיל

  1. אם לא עשית זאת עדיין, להוסיף Firebase לפרויקט Android שלך .
  2. מוסיפים את התלות עבור ספריות אנדרואיד קיט ML למודול שלך (ברמת האפליקציה) קובץ Gradle (בדרך כלל app/build.gradle ):
    apply plugin: 'com.android.application'
    apply plugin: 'com.google.gms.google-services'
    
    dependencies {
      // ...
    
      implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
      implementation 'com.google.firebase:firebase-ml-vision-image-label-model:20.0.1'
    }
    
  3. אופציונלי אך מומלץ: אם אתה משתמש ב- API במכשיר, להגדיר את האפליקציה שלך באופן אוטומטי להוריד את מודל ML למכשיר אחרי האפליקציה שלך מותקנת מחנות Play.

    לשם כך, להוסיף את ההצהרה הבאה של האפליקציה שלך AndroidManifest.xml קובץ:

    <application ...>
      ...
      <meta-data
          android:name="com.google.firebase.ml.vision.DEPENDENCIES"
          android:value="label" />
      <!-- To use multiple models: android:value="label,model2,model3" -->
    </application>
    
    אם לא תפעיל להתקין במשרת הורדות מודל, המודל הורד בפעם הראשונה שתפעיל את הגלאי על-התקן. בקשות שתשלח לפני השלמת ההורדה לא יניבו תוצאות.
  4. אם ברצונך להשתמש במודל מבוסס ענן, ועדיין לא הפעלת את ממשקי ה- API מבוססי הענן עבור הפרויקט שלך, בצע זאת כעת:

    1. פתח את דף APIs קיט ML של קונסולת Firebase.
    2. אם לא כבר שדרג פרויקט לתכנית תמחור Blaze, לחץ שדרג לעשות זאת. (תתבקש לשדרג רק אם הפרויקט שלך אינו בתכנית Blaze.)

      רק פרויקטים ברמת Blaze יכולים להשתמש בממשקי API מבוססי ענן.

    3. אם APIs מבוסס ענן אינו כבר מופעל, לחץ אפשר ממשקים מבוססי ענן.

    אם אתה רוצה להשתמש רק בדגם המכשיר, תוכל לדלג על שלב זה.

כעת אתה מוכן לתייג תמונות באמצעות מודל בהתקן או מודל מבוסס ענן.

1. הכינו את תמונת הקלט

צור FirebaseVisionImage אובייקט מתוך התמונה שלך. מתייג התמונות רץ הכי מהר כאשר אתה משתמש Bitmap או, אם אתה משתמש ב- API camera2, של JPEG בתבנית media.Image , אשר מומלץ במידת האפשר.

  • כדי ליצור FirebaseVisionImage האובייקט מנקודת media.Image אובייקט, כגון בעת לכידת תמונה מתוך המצלמה של המכשיר, להעביר את media.Image האובייקט ואת הסיבוב של התמונה FirebaseVisionImage.fromMediaImage() .

    אם אתה משתמש CameraX הספרייה, OnImageCapturedListener ו ImageAnalysis.Analyzer הכיתות לחשב את ערך הסיבוב בשבילך, כך שאתה רק צריך להמיר את הסיבוב לאחד של ML קיט ROTATION_ קבוע לפני פניית FirebaseVisionImage.fromMediaImage() :

    ג'אווה

    private class YourAnalyzer implements ImageAnalysis.Analyzer {
    
        private int degreesToFirebaseRotation(int degrees) {
            switch (degrees) {
                case 0:
                    return FirebaseVisionImageMetadata.ROTATION_0;
                case 90:
                    return FirebaseVisionImageMetadata.ROTATION_90;
                case 180:
                    return FirebaseVisionImageMetadata.ROTATION_180;
                case 270:
                    return FirebaseVisionImageMetadata.ROTATION_270;
                default:
                    throw new IllegalArgumentException(
                            "Rotation must be 0, 90, 180, or 270.");
            }
        }
    
        @Override
        public void analyze(ImageProxy imageProxy, int degrees) {
            if (imageProxy == null || imageProxy.getImage() == null) {
                return;
            }
            Image mediaImage = imageProxy.getImage();
            int rotation = degreesToFirebaseRotation(degrees);
            FirebaseVisionImage image =
                    FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
            // Pass image to an ML Kit Vision API
            // ...
        }
    }
    

    Kotlin+KTX

    private class YourImageAnalyzer : ImageAnalysis.Analyzer {
        private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) {
            0 -> FirebaseVisionImageMetadata.ROTATION_0
            90 -> FirebaseVisionImageMetadata.ROTATION_90
            180 -> FirebaseVisionImageMetadata.ROTATION_180
            270 -> FirebaseVisionImageMetadata.ROTATION_270
            else -> throw Exception("Rotation must be 0, 90, 180, or 270.")
        }
    
        override fun analyze(imageProxy: ImageProxy?, degrees: Int) {
            val mediaImage = imageProxy?.image
            val imageRotation = degreesToFirebaseRotation(degrees)
            if (mediaImage != null) {
                val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation)
                // Pass image to an ML Kit Vision API
                // ...
            }
        }
    }
    

    אם אינך משתמש בספריית מצלמות המעניקה לך את סיבוב התמונה, תוכל לחשב אותה מתוך סיבוב המכשיר ומכיוון חיישן המצלמה במכשיר:

    ג'אווה

    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }
    
    /**
     * 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, Context context)
            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);
    
        // On most devices, the sensor orientation is 90 degrees, but for some
        // devices it is 270 degrees. For devices with a sensor orientation of
        // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
        CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
        int sensorOrientation = cameraManager
                .getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SENSOR_ORIENTATION);
        rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360;
    
        // Return the corresponding FirebaseVisionImageMetadata rotation value.
        int result;
        switch (rotationCompensation) {
            case 0:
                result = FirebaseVisionImageMetadata.ROTATION_0;
                break;
            case 90:
                result = FirebaseVisionImageMetadata.ROTATION_90;
                break;
            case 180:
                result = FirebaseVisionImageMetadata.ROTATION_180;
                break;
            case 270:
                result = FirebaseVisionImageMetadata.ROTATION_270;
                break;
            default:
                result = FirebaseVisionImageMetadata.ROTATION_0;
                Log.e(TAG, "Bad rotation value: " + rotationCompensation);
        }
        return result;
    }

    Kotlin+KTX

    private val ORIENTATIONS = SparseIntArray()
    
    init {
        ORIENTATIONS.append(Surface.ROTATION_0, 90)
        ORIENTATIONS.append(Surface.ROTATION_90, 0)
        ORIENTATIONS.append(Surface.ROTATION_180, 270)
        ORIENTATIONS.append(Surface.ROTATION_270, 180)
    }
    /**
     * 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, context: Context): 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)
    
        // On most devices, the sensor orientation is 90 degrees, but for some
        // devices it is 270 degrees. For devices with a sensor orientation of
        // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees.
        val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager
        val sensorOrientation = cameraManager
                .getCameraCharacteristics(cameraId)
                .get(CameraCharacteristics.SENSOR_ORIENTATION)!!
        rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360
    
        // Return the corresponding FirebaseVisionImageMetadata rotation value.
        val result: Int
        when (rotationCompensation) {
            0 -> result = FirebaseVisionImageMetadata.ROTATION_0
            90 -> result = FirebaseVisionImageMetadata.ROTATION_90
            180 -> result = FirebaseVisionImageMetadata.ROTATION_180
            270 -> result = FirebaseVisionImageMetadata.ROTATION_270
            else -> {
                result = FirebaseVisionImageMetadata.ROTATION_0
                Log.e(TAG, "Bad rotation value: $rotationCompensation")
            }
        }
        return result
    }

    ואז, להעביר את media.Image אובייקט וערך סיבוב כדי FirebaseVisionImage.fromMediaImage() :

    ג'אווה

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

    Kotlin+KTX

    val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
  • כדי ליצור FirebaseVisionImage אובייקט מקובץ URI, להעביר את הקשר אפליקצית קובץ אורי FirebaseVisionImage.fromFilePath() . תכונה זו שימושית כאשר אתה משתמש ACTION_GET_CONTENT כוונה מהמשתמש לבחור תמונה מאפליקציית הגלריה שלהם.

    ג'אווה

    FirebaseVisionImage image;
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri);
    } catch (IOException e) {
        e.printStackTrace();
    }

    Kotlin+KTX

    val image: FirebaseVisionImage
    try {
        image = FirebaseVisionImage.fromFilePath(context, uri)
    } catch (e: IOException) {
        e.printStackTrace()
    }
  • כדי ליצור FirebaseVisionImage אובייקט מנקודת ByteBuffer או למערך בתים, ראשון לחשב את סיבוב התמונה כמתואר לעיל media.Image קלט.

    לאחר מכן, צור FirebaseVisionImageMetadata אובייקט המכיל את פורמט גובה, רוחב, צבע קידוד של תמונה, וסיבוב:

    ג'אווה

    FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder()
            .setWidth(480)   // 480x360 is typically sufficient for
            .setHeight(360)  // image recognition
            .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
            .setRotation(rotation)
            .build();

    Kotlin+KTX

    val metadata = FirebaseVisionImageMetadata.Builder()
            .setWidth(480) // 480x360 is typically sufficient for
            .setHeight(360) // image recognition
            .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21)
            .setRotation(rotation)
            .build()

    השתמש חיץ או מערך, לבין האובייקט מטה, כדי ליצור FirebaseVisionImage אובייקט:

    ג'אווה

    FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata);
    // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);

    Kotlin+KTX

    val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata)
    // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
  • כדי ליצור FirebaseVisionImage אובייקט מנקודת Bitmap אובייקט:

    ג'אווה

    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

    Kotlin+KTX

    val image = FirebaseVisionImage.fromBitmap(bitmap)
    הדימוי המיוצג על ידי Bitmap האובייקט חייב להיות זקוף, ללא רוטציה נוספת הנדרשת.

2. הגדר והפעל את תווית התמונות

לתייג עצמים בתמונה, להעביר את FirebaseVisionImage האובייקט אל FirebaseVisionImageLabeler של processImage השיטה.

  1. ראשית, לקבל מופע של FirebaseVisionImageLabeler .

    אם ברצונך להשתמש בתווית התמונות במכשיר:

    ג'אווה

    FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
        .getOnDeviceImageLabeler();
    
    // Or, to set the minimum confidence required:
    // FirebaseVisionOnDeviceImageLabelerOptions options =
    //     new FirebaseVisionOnDeviceImageLabelerOptions.Builder()
    //         .setConfidenceThreshold(0.7f)
    //         .build();
    // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
    //     .getOnDeviceImageLabeler(options);
    

    Kotlin+KTX

    val labeler = FirebaseVision.getInstance().getOnDeviceImageLabeler()
    
    // Or, to set the minimum confidence required:
    // val options = FirebaseVisionOnDeviceImageLabelerOptions.Builder()
    //     .setConfidenceThreshold(0.7f)
    //     .build()
    // val labeler = FirebaseVision.getInstance().getOnDeviceImageLabeler(options)
    

    אם ברצונך להשתמש בתווית תמונת הענן:

    ג'אווה

    FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
        .getCloudImageLabeler();
    
    // Or, to set the minimum confidence required:
    // FirebaseVisionCloudImageLabelerOptions options =
    //     new FirebaseVisionCloudImageLabelerOptions.Builder()
    //         .setConfidenceThreshold(0.7f)
    //         .build();
    // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance()
    //     .getCloudImageLabeler(options);
    

    Kotlin+KTX

    val labeler = FirebaseVision.getInstance().getCloudImageLabeler()
    
    // Or, to set the minimum confidence required:
    // val options = FirebaseVisionCloudImageLabelerOptions.Builder()
    //     .setConfidenceThreshold(0.7f)
    //     .build()
    // val labeler = FirebaseVision.getInstance().getCloudImageLabeler(options)
    

  2. ואז, להעביר את התמונה processImage() שיטה:

    ג'אווה

    labeler.processImage(image)
        .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionImageLabel>>() {
          @Override
          public void onSuccess(List<FirebaseVisionImageLabel> labels) {
            // Task completed successfully
            // ...
          }
        })
        .addOnFailureListener(new OnFailureListener() {
          @Override
          public void onFailure(@NonNull Exception e) {
            // Task failed with an exception
            // ...
          }
        });
    

    Kotlin+KTX

    labeler.processImage(image)
        .addOnSuccessListener { labels ->
          // Task completed successfully
          // ...
        }
        .addOnFailureListener { e ->
          // Task failed with an exception
          // ...
        }
    

3. קבל מידע על אובייקטים שכותרתם

אם פעולת תיוג תמונה מצליחה, רשימת FirebaseVisionImageLabel החפץ תועבר אלי מאזין ההצלחה. כל FirebaseVisionImageLabel אובייקט מייצג משהו שהיה מסומן בתמונה. עבור כל תווית, אתה יכול לקבל את תיאור הטקסט של התווית, שלה מזהה ישות גרף הידע (אם קיים), וכן את ציון הוודאות של המשחק. לדוגמה:

ג'אווה

for (FirebaseVisionImageLabel label: labels) {
  String text = label.getText();
  String entityId = label.getEntityId();
  float confidence = label.getConfidence();
}

Kotlin+KTX

for (label in labels) {
  val text = label.text
  val entityId = label.entityId
  val confidence = label.confidence
}

טיפים לשיפור הביצועים בזמן אמת

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

  • מצערת מצערת לתווית התמונות. אם מסגרת וידאו חדשה הופכת לזמינה בזמן שתווית התמונות פועלת, שמור את המסגרת.
  • אם אתה משתמש בפלט של תווית התמונות כדי לכסות גרפיקה על תמונת הקלט, תחילה קבל את התוצאה מ- ML Kit, ולאחר מכן עיבד את התמונה ואת שכבת העל בשלב אחד. בכך אתה מעבד למשטח התצוגה רק פעם אחת עבור כל מסגרת קלט.
  • אם אתה משתמש ב- API Camera2, ללכוד תמונות ב ImageFormat.YUV_420_888 פורמט.

    אם אתה משתמש ב- API המצלמה המבוגרת, ללכוד תמונות ב ImageFormat.NV21 פורמט.

הצעדים הבאים