استخدِم حزمة تعلّم الآلة لإضافة ميزات تصنيف المواضيع إلى تطبيقك بسهولة.
الميزة | التفاصيل |
---|---|
اسم Sdk | play-services-mlkit-subject-segmentation |
التنفيذ | غير مجمّع: يتم تنزيل النموذج ديناميكيًا باستخدام خدمات Google Play. |
تأثير حجم التطبيق | زيادة في الحجم حوالي 200 كيلوبايت. |
وقت الإعداد | وقد يحتاج المستخدمون إلى الانتظار حتى يتم تنزيل النموذج قبل استخدامه لأول مرة. |
جرّبه الآن
- يمكنك تجربة نموذج التطبيق من أجل يمكنك الاطّلاع على مثال حول استخدام واجهة برمجة التطبيقات هذه.
قبل البدء
- في ملف
build.gradle
على مستوى المشروع، تأكَّد من تضمين مستودع Maven التابع لشركة Google في كلٍّ من القسمَين "buildscript
" و"allprojects
". - أضِف الاعتمادية لمكتبة تقسيم مواضيع أدوات تعلُّم الآلة إلى ملف 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);
استخدام معرف موارد منتظم (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. إنشاء مثيل لـ 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 بكسل على الأقل.
- ويمكن أن يؤثر التركيز الضعيف للصورة أيضًا في الدقة. إذا لم تحصل على نتائج مقبولة، اطلب من المستخدم تلخيص الصورة.