استخدِم ML Kit لإضافة ميزات تقسيم المواضيع إلى تطبيقك بسهولة.
الميزة | التفاصيل |
---|---|
اسم حزمة تطوير البرامج (SDK) | play-services-mlkit-subject-segmentation |
التنفيذ | غير مجمّع: يتم تنزيل النموذج ديناميكيًا باستخدام "خدمات Google Play". |
تأثير حجم التطبيق | زيادة حجمه بمقدار 200 كيلوبايت تقريبًا |
وقت الإعداد | قد يحتاج المستخدمون إلى الانتظار إلى أن يكتمل تنزيل النموذج قبل استخدامه لأول مرة. |
جرّبه الآن
- يمكنك تجربة نموذج التطبيق لاطلاع على مثال على استخدام واجهة برمجة التطبيقات هذه.
قبل البدء
- في ملف
build.gradle
على مستوى المشروع، احرص على تضمين مستودع Maven من Google في كلّ من قسمَيbuildscript
وallprojects
. - أضِف الاعتمادية لمكتبة ML Kit لتحديد الأهداف إلى ملف Gradle على مستوى التطبيق الخاص بالوحدة، والذي يكون عادةً
app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-mlkit-subject-segmentation:16.0.0-beta1'
}
كما ذكرنا أعلاه، يتم توفير النموذج من خلال "خدمات Google Play".
يمكنك ضبط تطبيقك لتنزيل النموذج تلقائيًا على الجهاز
بعد تثبيت تطبيقك من "متجر Play". لإجراء ذلك، أضِف العبارة التالية
إلى ملف AndroidManifest.xml
في تطبيقك:
<application ...>
...
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="subject_segment" >
<!-- To use multiple models: android:value="subject_segment,model2,model3" -->
</application>
يمكنك أيضًا التحقّق صراحةً من توفّر النموذج وطلب تنزيله من خلال "خدمات Google Play" باستخدام واجهة برمجة التطبيقات ModuleInstallClient API.
في حال عدم تفعيل عمليات تنزيل النماذج في وقت التثبيت أو طلب تنزيل صريح، يتم تنزيل النموذج في المرة الأولى التي يتم فيها تشغيل أداة التقسيم. لا تؤدي الطلبات التي تقدّمها قبل اكتمال عملية التنزيل إلى أي نتائج.
1. تجهيز صورة الإدخال
لإجراء عملية تقسيم على صورة، أنشئ عنصرًا من النوع InputImage
من Bitmap
أو media.Image
أو ByteBuffer
أو صفيف بايت أو ملف على
الجهاز.
يمكنك إنشاء عنصر 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);
استخدام عنوان URL للملف
لإنشاء عنصر InputImage
، من معرّف موارد منتظم لملف، عليك تمرير سياق التطبيق ومعرّف الموارد المنتظم للملف إلى
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. إنشاء مثيل من SubjectSegmenter
تحديد خيارات أداة التقسيم
لتقسيم صورتك، عليك أولاً إنشاء مثيل من SubjectSegmenterOptions
على النحو التالي:
Kotlin
val options = SubjectSegmenterOptions.Builder() // enable options .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() // enable options .build();
في ما يلي تفاصيل كل خيار:
قناع ثقة المقدّمة
يتيح لك قناع ثقة المقدّمة تمييز الهدف في المقدّمة عن الخلفية.
يتيح لك استدعاء enableForegroundConfidenceMask()
في الخيارات استرداد
قناع المقدّمة لاحقًا من خلال استدعاء getForegroundMask()
على
SubjectSegmentationResult
العنصر الذي تم إرجاعه بعد معالجة الصورة.
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundConfidenceMask() .build();
صورة نقطية في المقدّمة
وبالمثل، يمكنك أيضًا الحصول على صورة نقطية للكائن في المقدّمة.
يتيح لك استدعاء enableForegroundBitmap()
في الخيارات استرداد
الصورة النقطية للعناصر الأمامية لاحقًا من خلال استدعاء getForegroundBitmap()
على العنصر
SubjectSegmentationResult
الذي يتم إرجاعه بعد معالجة الصورة.
Kotlin
val options = SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build()
Java
SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableForegroundBitmap() .build();
قناع ثقة متعدّد الأهداف
كما هو الحال مع خيارات المقدّمة، يمكنك استخدام SubjectResultOptions
لتفعيل
قناع الثقة لكلّ موضوع في المقدّمة على النحو التالي:
Kotlin
val subjectResultOptions = SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableConfidenceMask() .build() val options = SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Java
SubjectResultOptions subjectResultOptions = new SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableConfidenceMask() .build() SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
صورة نقطية متعددة المواضيع
وبالمثل، يمكنك تفعيل الصورة النقطية لكل موضوع:
Kotlin
val subjectResultOptions = SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableSubjectBitmap() .build() val options = SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
Java
SubjectResultOptions subjectResultOptions = new SubjectSegmenterOptions.SubjectResultOptions.Builder() .enableSubjectBitmap() .build() SubjectSegmenterOptions options = new SubjectSegmenterOptions.Builder() .enableMultipleSubjects(subjectResultOptions) .build()
إنشاء أداة تقسيم المواضيع
بعد تحديد خيارات SubjectSegmenterOptions
، أنشئ مثيلًا
SubjectSegmenter
يستدعي getClient()
ويمرّر الخيارات كمَعلمة:
Kotlin
val segmenter = SubjectSegmentation.getClient(options)
Java
SubjectSegmenter segmenter = SubjectSegmentation.getClient(options);
3- معالجة صورة
نقْل عنصر InputImage
المُعدّ إلى طريقة process
في SubjectSegmenter
:
Kotlin
segmenter.process(inputImage) .addOnSuccessListener { result -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
segmenter.process(inputImage) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(SubjectSegmentationResult result) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. الحصول على نتيجة تقسيم المواضيع
استرداد أقنعة المقدّمة وملفات الخطوط المسطّحة
بعد المعالجة، يمكنك استرداد قناع المقدّمة لصورتك بالاتّباع
getForegroundConfidenceMask()
الخطوات التالية:
Kotlin
val colors = IntArray(image.width * image.height) val foregroundMask = result.foregroundConfidenceMask for (i in 0 until image.width * image.height) { if (foregroundMask[i] > 0.5f) { colors[i] = Color.argb(128, 255, 0, 255) } } val bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 )
Java
int[] colors = new int[image.getWidth() * image.getHeight()]; FloatBuffer foregroundMask = result.getForegroundConfidenceMask(); for (int i = 0; i < image.getWidth() * image.getHeight(); i++) { if (foregroundMask.get() > 0.5f) { colors[i] = Color.argb(128, 255, 0, 255); } } Bitmap bitmapMask = Bitmap.createBitmap( colors, image.getWidth(), image.getHeight(), Bitmap.Config.ARGB_8888 );
يمكنك أيضًا استرداد صورة نقطية لمقدمة الصورة من خلال الاتصال getForegroundBitmap()
:
Kotlin
val foregroundBitmap = result.foregroundBitmap
Java
Bitmap foregroundBitmap = result.getForegroundBitmap();
استرداد الأقنعة والملفات المخصّصة للصور النقطية لكلّ موضوع
وبالمثل، يمكنك استرداد القناع للعناصر المقسّمة من خلال استدعاء
getConfidenceMask()
على كل عنصر على النحو التالي:
Kotlin
val subjects = result.subjects val colors = IntArray(image.width * image.height) for (subject in subjects) { val mask = subject.confidenceMask for (i in 0 until subject.width * subject.height) { val confidence = mask[i] if (confidence > 0.5f) { colors[image.width * (subject.startY - 1) + subject.startX] = Color.argb(128, 255, 0, 255) } } } val bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 )
Java
Listsubjects = result.getSubjects(); int[] colors = new int[image.getWidth() * image.getHeight()]; for (Subject subject : subjects) { FloatBuffer mask = subject.getConfidenceMask(); for (int i = 0; i < subject.getWidth() * subject.getHeight(); i++) { float confidence = mask.get(); if (confidence > 0.5f) { colors[width * (subject.getStartY() - 1) + subject.getStartX()] = Color.argb(128, 255, 0, 255); } } } Bitmap bitmapMask = Bitmap.createBitmap( colors, image.width, image.height, Bitmap.Config.ARGB_8888 );
يمكنك أيضًا الوصول إلى الصورة النقطية لكل موضوع مقسّم على النحو التالي:
Kotlin
val bitmaps = mutableListOf() for (subject in subjects) { bitmaps.add(subject.bitmap) }
Java
Listbitmaps = new ArrayList<>(); for (Subject subject : subjects) { bitmaps.add(subject.getBitmap()); }
نصائح لتحسين الأداء
في كل جلسة تطبيق، غالبًا ما تكون عملية الاستنتاج الأولى أبطأ من عمليات الاستنتاج اللاحقة بسبب بدء تشغيل النموذج. إذا كان وقت الاستجابة المنخفض مهمًا، ننصحك باستدعاء استنتاج "زائف" مسبقًا.
تعتمد جودة النتائج على جودة الصورة المُدخلة:
- لكي تحصل حزمة ML Kit على نتيجة تقسيم دقيقة، يجب أن تكون الصورة بدقة 512x512 بكسل على الأقل.
- يمكن أن يؤثر أيضًا عدم تركيز الصورة في الدقة. إذا لم تحصل على نتائج مقبولة، اطلب من المستخدم إعادة التقاط الصورة.