عند تمرير صورة إلى مجموعة أدوات تعلّم الآلة، يتم رصد ما يصل إلى خمسة كائنات في الصورة بالإضافة إلى موضع كل عنصر في الصورة. عند اكتشاف كائنات في مجموعات بث الفيديو، يكون لكل كائن معرّف فريد يمكنك استخدامه لتتبع الكائن من إطار إلى آخر.
يمكنك استخدام نموذج مخصّص لتصنيف الصور لتصنيف العناصر التي تم رصدها. يُرجى الرجوع إلى النماذج المخصّصة باستخدام ML Kit للحصول على إرشادات حول متطلبات التوافق مع النماذج، وأين يمكنك العثور على النماذج المدرَّبة مسبقًا، وكيفية تدريب نماذجك الخاصة.
هناك طريقتان لدمج نموذج مخصص. يمكنك تجميع النموذج عن طريق وضعه داخل مجلد مواد العرض لتطبيقك، أو يمكنك تنزيله ديناميكيًا من Firebase. يقارن الجدول التالي بين الخيارين.
نموذج مجمّع | نموذج مستضاف |
---|---|
النموذج هو جزء من حِزمة APK لتطبيقك، ما يؤدي إلى زيادة حجمه. | النموذج ليس جزءًا من ملف APK. تتم استضافتها من خلال تحميل المحتوى إلى Firebase Machine Learning. |
يتوفّر النموذج على الفور حتى إذا كان جهاز Android غير متصل بالإنترنت. | يتم تنزيل النموذج عند الطلب |
لا حاجة لمشروع Firebase | مطلوب مشروع Firebase |
يجب إعادة نشر تطبيقك لتحديث النموذج | نشر تحديثات النموذج بدون إعادة نشر تطبيقك |
لا يوجد اختبار A/B مدمج | إجراء اختبار أ/ب بسهولة باستخدام ميزة الإعداد عن بُعد في Firebase |
تجربة السمات والبيانات
- اطّلِع على تطبيق البدء السريع للرؤية للحصول على مثال عن استخدام النموذج المجمّع و تطبيق التشغيل السريع للإكمال التلقائي للحصول على مثال حول استخدام النموذج المستضاف.
- راجع تطبيق عرض التصميم متعدد الأبعاد للتعرّف على طريقة تنفيذ واجهة برمجة التطبيقات هذه بشكل شامل.
قبل البدء
في ملف
build.gradle
على مستوى المشروع، تأكَّد من تضمين مستودع Google Maven في كل من القسمينbuildscript
وallprojects
.أضِف التبعيات لمكتبات ML Kit في Android إلى ملف Gradle على مستوى التطبيق في وحدتك، والذي عادة ما يكون
app/build.gradle
:لتجميع نموذج مع تطبيقك:
dependencies { // ... // Object detection & tracking feature with custom bundled model implementation 'com.google.mlkit:object-detection-custom:17.0.0' }
لتنزيل نموذج ديناميكيًا من Firebase، أضف التبعية
linkFirebase
:dependencies { // ... // Object detection & tracking feature with model downloaded // from firebase implementation 'com.google.mlkit:object-detection-custom:17.0.0' implementation 'com.google.mlkit:linkfirebase:17.0.0' }
إذا كنت ترغب في تنزيل نموذج، فتأكد من إضافة Firebase إلى مشروع Android، إذا لم تكن قد فعلت ذلك، فهذا غير مطلوب عند تجميع النموذج.
1- تحميل النموذج
تهيئة مصدر نموذج محلي
لتجميع النموذج مع تطبيقك:
انسخ ملف النموذج (الذي ينتهي عادة بـ
.tflite
أو.lite
) إلى مجلدassets/
لتطبيقك. (قد تحتاج إلى إنشاء المجلد أولاً من خلال النقر بزر الماوس الأيمن على المجلدapp/
، ثم النقر على جديد > المجلد > مجلد مواد العرض.)بعد ذلك، أضِف ما يلي إلى ملف
build.gradle
لتطبيقك لضمان عدم ضغط Gradle ملف النموذج عند إنشاء التطبيق:android { // ... aaptOptions { noCompress "tflite" // or noCompress "lite" } }
سيتم تضمين ملف النموذج في حزمة التطبيق وسيتوفر لـ ML Kit باعتباره مادة عرض أولية.
إنشاء كائن
LocalModel
، مع تحديد المسار إلى ملف النموذج:Kotlin
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build()
لغة Java
LocalModel localModel = new LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build();
إعداد مصدر نموذج مستضاف على Firebase
لاستخدام النموذج المستضاف عن بُعد، أنشئ كائن CustomRemoteModel
قبل
FirebaseModelSource
، مع تحديد الاسم الذي عينته للنموذج عند
نشره:
Kotlin
// Specify the name you assigned in the Firebase console. val remoteModel = CustomRemoteModel .Builder(FirebaseModelSource.Builder("your_model_name").build()) .build()
لغة Java
// Specify the name you assigned in the Firebase console. CustomRemoteModel remoteModel = new CustomRemoteModel .Builder(new FirebaseModelSource.Builder("your_model_name").build()) .build();
بعد ذلك، ابدأ مهمة تنزيل النموذج، مع تحديد الشروط التي تريد السماح بالتنزيل بموجبها. إذا لم يكن النموذج على الجهاز، أو إذا كان إصدار أحدث من النموذج متاحًا، فسيتم تنزيل النموذج من Firebase بشكل غير متزامن:
Kotlin
val downloadConditions = DownloadConditions.Builder() .requireWifi() .build() RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener { // Success. }
لغة Java
DownloadConditions downloadConditions = new DownloadConditions.Builder() .requireWifi() .build(); RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(@NonNull Task task) { // Success. } });
تبدأ العديد من التطبيقات مهمة التنزيل في رمز التهيئة، ولكن يمكنك القيام بذلك في أي وقت قبل أن تحتاج إلى استخدام النموذج.
2. ضبط أداة رصد الكائنات
بعد ضبط مصادر النموذج، يمكنك ضبط أداة رصد الكائنات
لحالة الاستخدام باستخدام كائن CustomObjectDetectorOptions
. يمكنك تغيير
الإعدادات التالية:
إعدادات أداة كشف الكائنات | |
---|---|
وضع الاكتشاف |
STREAM_MODE (تلقائي) | SINGLE_IMAGE_MODE
في في |
اكتشاف كائنات متعددة وتتبعها |
false (تلقائي) | true
يمكنك رصد ما يصل إلى خمسة كائنات أو تتبّعها أو رصد الكائن الأبرز فقط (الإعداد التلقائي). |
تصنيف العناصر |
false (تلقائي) | true
لتحديد ما إذا كان سيتم تصنيف الكائنات المكتشَفة أم لا باستخدام نموذج المصنِّف المخصّص المقدَّم. لاستخدام نموذج التصنيف المخصّص، يجب ضبطه على |
الحد الأدنى لتصنيف التصنيف |
الحد الأدنى من درجة الثقة للتصنيفات التي تم اكتشافها. وفي حال تم ترك السياسة بدون ضبط، سيتم استخدام أي حدّ أدنى للتصنيف تحدّده البيانات الوصفية للنموذج. إذا لم يتضمّن النموذج أي بيانات وصفية أو لم تحدّد البيانات الوصفية حدًّا للتصنيف، سيتم استخدام حد تلقائي بقيمة 0.0. |
الحد الأقصى للتصنيفات لكل كائن |
الحد الأقصى لعدد التصنيفات لكل كائن ستعرضه أداة الكشف. وفي حال تم ترك السياسة بدون ضبط، سيتم استخدام القيمة التلقائية وهي 10. |
تم تحسين واجهة برمجة التطبيقات لاكتشاف العناصر وتتبع واجهة برمجة التطبيقات لهاتين حالتي الاستخدام الأساسية:
- الاكتشاف المباشر وتتبع أكثر الكائنات البارزة في عدسة الكاميرا.
- اكتشاف كائنات متعددة من صورة ثابتة.
لتهيئة واجهة برمجة التطبيقات لحالات الاستخدام هذه، باستخدام نموذج مُجمع على المستوى المحلي:
Kotlin
// Live detection and tracking val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build() // Multiple object detection in static images val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableMultipleObjects() .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build() val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions)
لغة Java
// Live detection and tracking CustomObjectDetectorOptions customObjectDetectorOptions = new CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.STREAM_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build(); // Multiple object detection in static images CustomObjectDetectorOptions customObjectDetectorOptions = new CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableMultipleObjects() .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build(); ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions);
إذا كان لديك نموذج تتم استضافته عن بُعد، يجب عليك التحقق من أنه قد تم تنزيله قبل تشغيله. يمكنك التحقق من حالة مهمة تنزيل النموذج
باستخدام طريقة مدير النموذج isModelDownloaded()
.
على الرغم من الحاجة إلى تأكيد ذلك فقط قبل تشغيل أداة الرصد، إذا كان لديك نموذج مستضاف عن بُعد ونموذج مُجمَّع محليًا، قد يكون من المنطقي إجراء هذا الفحص عند إنشاء أداة رصد الصور: يمكنك إنشاء أداة رصد من النموذج البعيد إذا تم تنزيله، ومن النموذج المحلي بخلاف ذلك.
Kotlin
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener { isDownloaded -> val optionsBuilder = if (isDownloaded) { CustomObjectDetectorOptions.Builder(remoteModel) } else { CustomObjectDetectorOptions.Builder(localModel) } val customObjectDetectorOptions = optionsBuilder .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build() val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions) }
لغة Java
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Boolean isDownloaded) { CustomObjectDetectorOptions.Builder optionsBuilder; if (isDownloaded) { optionsBuilder = new CustomObjectDetectorOptions.Builder(remoteModel); } else { optionsBuilder = new CustomObjectDetectorOptions.Builder(localModel); } CustomObjectDetectorOptions customObjectDetectorOptions = optionsBuilder .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build(); ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions); } });
إذا لم يكن لديك سوى نموذج تتم استضافته عن بُعد، يجب تعطيل الوظائف ذات الصلة بالنموذج، على سبيل المثال، الرمادي أو إخفاء جزء من واجهة المستخدم، إلى أن تؤكد أنه تم تنزيل النموذج. يمكنك تنفيذ ذلك من خلال إرفاق أداة معالجة
بأسلوب download()
في مدير النموذج:
Kotlin
RemoteModelManager.getInstance().download(remoteModel, conditions) .addOnSuccessListener { // Download complete. Depending on your app, you could enable the ML // feature, or switch from the local model to the remote model, etc. }
لغة Java
RemoteModelManager.getInstance().download(remoteModel, conditions) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Void v) { // Download complete. Depending on your app, you could enable // the ML feature, or switch from the local model to the remote // model, etc. } });
3- إعداد صورة الإدخال
أنشئ كائنInputImage
من صورتك.
يعمل جهاز رصد الكائنات مباشرةً من Bitmap
أو NV21 ByteBuffer
أو
YUV_420_888 media.Image
. ويُوصى بإنشاء InputImage
من هذه المصادر إذا كان لديك وصول مباشر إلى أحدها. في حال إنشاء InputImage
من مصادر أخرى، سنتعامل مع عملية الإحالة الناجحة داخليًا وقد تكون أقل كفاءة.
يمكنك إنشاء كائن 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
مع درجات التدوير.
4. تشغيل أداة رصد الكائنات
Kotlin
objectDetector .process(image) .addOnFailureListener(e -> {...}) .addOnSuccessListener(results -> { for (detectedObject in results) { // ... } });
لغة Java
objectDetector .process(image) .addOnFailureListener(e -> {...}) .addOnSuccessListener(results -> { for (DetectedObject detectedObject : results) { // ... } });
5- الحصول على معلومات حول الكائنات المصنّفة
إذا تم استدعاء process()
بنجاح، يتم إرسال قائمة DetectedObject
إلى المستمع الناجح.
يحتوي كل DetectedObject
على الخصائص التالية:
مربّع ربط | تمثّل هذه الخاصية Rect الذي يشير إلى موضع العنصر في الصورة. |
||||||
الرقم التعريفي للتتبع | عدد صحيح يحدد الكائن عبر الصور. قيمة فارغة في SINGLE_IMAGE_MODE. | ||||||
التصنيفات |
|
Kotlin
// The list of detected objects contains one item if multiple // object detection wasn't enabled. for (detectedObject in results) { val boundingBox = detectedObject.boundingBox val trackingId = detectedObject.trackingId for (label in detectedObject.labels) { val text = label.text val index = label.index val confidence = label.confidence } }
لغة Java
// The list of detected objects contains one item if multiple // object detection wasn't enabled. for (DetectedObject detectedObject : results) { Rect boundingBox = detectedObject.getBoundingBox(); Integer trackingId = detectedObject.getTrackingId(); for (Label label : detectedObject.getLabels()) { String text = label.getText(); int index = label.getIndex(); float confidence = label.getConfidence(); } }
ضمان تقديم تجربة رائعة للمستخدم
للحصول على أفضل تجربة للمستخدم، اتبع الإرشادات التالية في تطبيقك:
- يعتمد الاكتشاف الناجح للكائن على التعقيد المرئي للكائن. لكي يتم اكتشافه، قد تحتاج الكائنات ذات عدد صغير من الميزات المرئية إلى أن تشغل مساحة أكبر من الصورة. يجب تزويد المستخدمين بإرشادات حول التقاط المدخلات التي تعمل جيدًا مع نوع الكائنات التي تريد اكتشافها.
- عند استخدام التصنيف، إذا كنت تريد اكتشاف الكائنات التي لا تندرج ضمن الفئات المتوافقة، يمكنك تنفيذ معالجة خاصة للعناصر غير المعروفة.
يمكنك أيضًا الاطّلاع على مجموعة تطبيق عرض التصميم المتعدد الأبعاد ML Kit ومجموعتي تصميمات الميزات التي تستند إلى تعلُّم الآلة.
Improving performance
إذا كنت تريد استخدام اكتشاف الكائنات في تطبيق في الوقت الفعلي، فاتبع هذه الإرشادات لتحقيق أفضل معدلات عرض إطارات:عند استخدام وضع البث في تطبيق في الوقت الفعلي، لا تستخدم ميزة رصد كائنات متعددة لأن معظم الأجهزة لن تتمكّن من إنشاء معدلات عرض إطارات مناسبة.
- في حال استخدام واجهة برمجة التطبيقات
Camera
أوcamera2
، عليك التحكُّم في عمليات الرصد. في حال توفّر إطار فيديو جديد أثناء تشغيل أداة الرصد، أفلِت الإطار. للاطّلاع على مثال، يمكنك مراجعة الصفVisionProcessorBase
في نموذج التطبيق للبدء السريع. - إذا كنت تستخدم واجهة برمجة تطبيقات
CameraX
، تأكّد من ضبط استراتيجية الضغط العكسي على قيمتها التلقائيةImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. ويضمن ذلك تسليم صورة واحدة فقط للتحليل في كل مرة. وإذا تم إنشاء المزيد من الصور عندما تكون أداة التحليل مشغولة، سيتم إسقاطها تلقائيًا ولن يتم وضعها في قائمة الانتظار للتسليم. بعد إغلاق الصورة التي يتم تحليلها عن طريق طلب ImageProxy.close()، سيتم تسليم الصورة التالية التالية. - إذا كنت تستخدم إخراج أداة الرصد لتركيب الرسومات على
الصورة المُدخلة، عليك الحصول على النتيجة من ML Kit، ثم عرض الصورة
والتراكب في خطوة واحدة. يتم عرض هذا المحتوى على سطح العرض
مرة واحدة فقط لكل إطار إدخال. للاطّلاع على مثال، يمكنك الاطّلاع على الصفَّين
CameraSourcePreview
وGraphicOverlay
في نموذج التطبيق السريع للبدء. - إذا كنت تستخدم Camera2 API، يمكنك التقاط الصور بتنسيق
ImageFormat.YUV_420_888
. إذا كنت تستخدم إصدارًا قديمًا من واجهة برمجة تطبيقات الكاميرا، التقِط الصور بتنسيقImageFormat.NV21
.