تصنيف الصور باستخدام حزمة تعلّم الآلة على نظام التشغيل Android

تنظيم صفحاتك في مجموعات يمكنك حفظ المحتوى وتصنيفه حسب إعداداتك المفضّلة.

يمكنك استخدام مجموعة أدوات تعلّم الآلة لتصنيف العناصر التي تم التعرّف عليها في صورة. ويدعم النموذج الافتراضي المقدم مع ML Kit أكثر من 400 تصنيف مختلف.

الميزةغير مجمعةمجمعة
التنفيذيتم تنزيل النموذج ديناميكيًا عبر خدمات Google Play.النموذج مرتبط بشكل ثابت بـ في وقت الإنشاء.
حجم التطبيقزيادة حجم الصورة حوالي 200 كيلوبايت.زيادة الحجم حوالي 5.7 ميغابايت.
وقت الإعدادقد يتعين عليك انتظار تنزيل النموذج قبل الاستخدام الأول.يتوفّر النموذج على الفور.

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

  • يمكنك تجربة نموذج التطبيق للاطّلاع على مثال لاستخدام واجهة برمجة التطبيقات هذه.

قبل البدء

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

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

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

    dependencies {
      // ...
      // Use this dependency to bundle the model with your app
      implementation 'com.google.mlkit:image-labeling:17.0.7'
    }
    

    لاستخدام النموذج في "خدمات Google Play":

    dependencies {
      // ...
      // Use this dependency to use the dynamically downloaded model in Google Play Services
      implementation 'com.google.android.gms:play-services-mlkit-image-labeling:16.0.8'
    }
    
  3. إذا اخترت استخدام النموذج في خدمات Google Play، يمكنك تهيئة تطبيقك لتنزيل النموذج تلقائيًا على الجهاز بعد تثبيت التطبيق من متجر Play. ولإجراء ذلك، يمكنك إضافة البيان التالي إلى ملف AndroidManifest.xml لتطبيقك:

    <application ...>
          ...
          <meta-data
              android:name="com.google.mlkit.vision.DEPENDENCIES"
              android:value="ica" >
          <!-- To use multiple models: android:value="ica,model2,model3" -->
    </application>
    

    ويمكنك أيضًا التحقّق صراحةً من مدى توفّر النموذج وطلب التنزيل من خلال واجهة برمجة التطبيقات ModuleInstallClient في خدمات Google Play.

    وإذا لم يتم تمكين تنزيلات نموذج وقت التثبيت أو طلب تنزيل صريح، فسيتم تنزيل النموذج في أول مرة يتم تشغيل أداة التصنيف. ولا تؤدي الطلبات التي تجريها قبل اكتمال التنزيل إلى نتائج.

أنت الآن مستعد لتصنيف الصور.

1- إعداد صورة الإدخال

أنشئ كائن InputImage من صورتك. يتم تنفيذ تصنيف الصور بشكل أسرع عند استخدام Bitmap، أو إذا كنت تستخدم واجهة برمجة تطبيقات الكاميرا 2، YUV_420_888 media.Image، والتي يوصى بها عند الإمكان.

يمكنك إنشاء كائن 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 مع درجات التدوير.

2. تهيئة تصنيف الصور وتشغيله

لتصنيف الكائنات في صورة، ما عليك سوى تمرير الكائن InputImage إلى طريقة ImageLabeler process.

  1. أولاً، احصل على مثال من ImageLabeler.

    إذا كنت تريد استخدام أداة تصنيف الصور على الجهاز فقط، عليك إقرار ما يلي:

Kotlin

// To use default options:
val labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS)

// Or, to set the minimum confidence required:
// val options = ImageLabelerOptions.Builder()
//     .setConfidenceThreshold(0.7f)
//     .build()
// val labeler = ImageLabeling.getClient(options)

لغة Java

// To use default options:
ImageLabeler labeler = ImageLabeling.getClient(ImageLabelerOptions.DEFAULT_OPTIONS);

// Or, to set the minimum confidence required:
// ImageLabelerOptions options =
//     new ImageLabelerOptions.Builder()
//         .setConfidenceThreshold(0.7f)
//         .build();
// ImageLabeler labeler = ImageLabeling.getClient(options);
  1. ثم مرِّر الصورة إلى الطريقة process():

Kotlin

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

لغة Java

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

3- الحصول على معلومات حول الكائنات المصنّفة

في حال نجاح عملية تصنيف الصور، يتم إرسال قائمة بكائنات ImageLabel إلى أداة معالجة النجاح. يمثل كل كائن ImageLabel عنصرًا تم تصنيفه في الصورة. يدعم النموذج الأساسي أكثر من 400 تصنيف مختلف. يمكنك الحصول على وصف نصي لكل تصنيف، والفهرسة بين جميع التصنيفات المتوافقة مع النموذج، ونتيجة الثقة في المطابقة. مثلاً:

Kotlin

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

لغة Java

for (ImageLabel label : labels) {
    String text = label.getText();
    float confidence = label.getConfidence();
    int index = label.getIndex();
}

نصائح لتحسين الأداء في الوقت الفعلي

إذا أردت تصنيف الصور في تطبيق في الوقت الفعلي، اتّبِع الإرشادات التالية للحصول على أفضل معدّلات عرض إطارات:

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