برچسب گذاری تصاویر با ML Kit در اندروید

می‌توانید از ML Kit برای برچسب‌گذاری اشیاء شناسایی شده در یک تصویر، با استفاده از مدل روی دستگاه یا مدل ابری استفاده کنید. برای آشنایی با مزایای هر رویکرد به نمای کلی مراجعه کنید.

قبل از اینکه شروع کنی

  1. اگر قبلاً این کار را نکرده‌اید، Firebase را به پروژه Android خود اضافه کنید .
  2. وابستگی های کتابخانه های اندروید ML Kit را به فایل 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 روی دستگاه استفاده می‌کنید، برنامه خود را طوری پیکربندی کنید که پس از نصب برنامه از فروشگاه Play، مدل ML را به‌طور خودکار در دستگاه دانلود کند.

    برای انجام این کار، اعلان زیر را به فایل 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. اگر می‌خواهید از مدل مبتنی بر Cloud استفاده کنید و قبلاً APIهای مبتنی بر ابر را برای پروژه خود فعال نکرده‌اید، اکنون این کار را انجام دهید:

    1. صفحه ML Kit APIs کنسول Firebase را باز کنید.
    2. اگر قبلاً پروژه خود را به طرح قیمت گذاری Blaze ارتقا نداده اید، برای انجام این کار، روی ارتقا کلیک کنید. (فقط اگر پروژه شما در طرح Blaze نباشد، از شما خواسته می شود که ارتقا دهید.)

      فقط پروژه های سطح Blaze می توانند از API های مبتنی بر ابر استفاده کنند.

    3. اگر APIهای مبتنی بر Cloud قبلاً فعال نشده‌اند، روی Enable Cloud-based APIs کلیک کنید.

    اگر می خواهید فقط از مدل روی دستگاه استفاده کنید، می توانید از این مرحله صرف نظر کنید.

اکنون آماده هستید که تصاویر را با استفاده از یک مدل روی دستگاه یا یک مدل مبتنی بر ابر برچسب گذاری کنید.

1. تصویر ورودی را آماده کنید

یک شی FirebaseVisionImage از تصویر خود ایجاد کنید. وقتی از Bitmap یا در صورت استفاده از camera2 API از یک media.Image با فرمت JPEG استفاده می‌کنید، برچسب‌گذار تصویر سریع‌ترین عملکرد را دارد، که در صورت امکان توصیه می‌شود.

  • برای ایجاد یک شی FirebaseVisionImage از یک شی media.Image ، مانند هنگام گرفتن تصویر از دوربین دستگاه، شی media.Image و چرخش تصویر را به FirebaseVisionImage.fromMediaImage() منتقل کنید.

    اگر از کتابخانه CameraX ، کلاس‌های OnImageCapturedListener و ImageAnalysis.Analyzer استفاده می‌کنید، مقدار چرخش را برای شما محاسبه می‌کنند، بنابراین فقط باید قبل از فراخوانی FirebaseVisionImage.fromMediaImage() چرخش را به یکی از ثابت‌های ROTATION_ ML Kit تبدیل کنید:

    Java

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

    اگر از کتابخانه دوربینی که چرخش تصویر را به شما می دهد استفاده نمی کنید، می توانید آن را از روی چرخش دستگاه و جهت سنسور دوربین در دستگاه محاسبه کنید:

    Java

    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() ارسال کنید:

    Java

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

    Kotlin+KTX

    val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
  • برای ایجاد یک شی FirebaseVisionImage از URI فایل، زمینه برنامه و فایل URI را به FirebaseVisionImage.fromFilePath() ارسال کنید. این زمانی مفید است که از یک هدف ACTION_GET_CONTENT استفاده می کنید تا از کاربر بخواهید تصویری را از برنامه گالری خود انتخاب کند.

    Java

    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 ایجاد کنید که شامل ارتفاع، عرض، فرمت کدگذاری رنگ و چرخش تصویر باشد:

    Java

    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 از بافر یا آرایه و شیء فراداده استفاده کنید:

    Java

    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 :

    Java

    FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);

    Kotlin+KTX

    val image = FirebaseVisionImage.fromBitmap(bitmap)
    تصویر نمایش داده شده توسط شی Bitmap باید عمودی باشد، بدون نیاز به چرخش اضافی.

2. برچسب تصویر را پیکربندی و اجرا کنید

برای برچسب گذاری اشیاء در یک تصویر، شی FirebaseVisionImage را به متد processImage FirebaseVisionImageLabeler ارسال کنید.

  1. ابتدا یک نمونه از FirebaseVisionImageLabeler را دریافت کنید.

    اگر می‌خواهید از برچسب‌گذار تصویر روی دستگاه استفاده کنید:

    Java

    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)
    

    اگر می خواهید از برچسب تصویر ابری استفاده کنید:

    Java

    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() منتقل کنید:

    Java

    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 چیزی را نشان می دهد که در تصویر برچسب گذاری شده است. برای هر برچسب، می‌توانید شرح متن برچسب، شناسه گراف دانش آن (در صورت موجود بودن)، و امتیاز اطمینان مطابقت را دریافت کنید. مثلا:

Java

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 دریافت کنید، سپس تصویر را رندر کنید و در یک مرحله همپوشانی کنید. با انجام این کار، برای هر فریم ورودی فقط یک بار به سطح نمایشگر رندر می دهید.
  • اگر از Camera2 API استفاده می کنید، تصاویر را با فرمت ImageFormat.YUV_420_888 بگیرید.

    اگر از دوربین قدیمی‌تر API استفاده می‌کنید، تصاویر را با فرمت ImageFormat.NV21 بگیرید.

مراحل بعدی