عند تمرير صورة إلى أدوات تعلّم الآلة، يرصد ما يصل إلى خمسة عناصر في الصورة، بالإضافة إلى موضع كل عنصر في الصورة. عند رصد الكائنات في الفيديوهات المضمّنة، يكون لكل عنصر معرّف فريد يمكنك استخدامه لتتبُّع العنصر من إطار إلى آخر.
يمكنك استخدام نموذج تصنيف مخصّص للصور لتصنيف العناصر التي يتم رصدها. يُرجى الرجوع إلى النماذج المخصّصة باستخدام حزمة تعلّم الآلة للحصول على إرشادات حول متطلبات توافق النماذج، وأماكن العثور على نماذج مدرّبة مسبقًا، وكيفية تدريب نماذجك الخاصة.
هناك طريقتان لدمج النموذج المخصّص. يمكنك تجميع النموذج من خلال وضعه في مجلد مواد العرض في التطبيق أو تنزيله ديناميكيًا من Firebase. يقارن الجدول التالي بين الخيارين.
النموذج المجمّع | النموذج المستضاف |
---|---|
النموذج هو جزء من حزمة APK لتطبيقك، ما يزيد من حجمه. | النموذج ليس جزءًا من APK. وتتم استضافته عن طريق تحميله إلى أداة تعلُّم الآلة من Firebase. |
يتوفر الطراز على الفور، حتى عندما يكون جهاز Android غير متصل بالإنترنت. | يتم تنزيل النموذج عند الطلب |
عدم الحاجة إلى مشروع Firebase | يجب توفّر مشروع في Firebase. |
يجب إعادة نشر تطبيقك لتحديث النموذج. | إرسال تحديثات النموذج بدون إعادة نشر تطبيقك |
بدون اختبار A/B مدمج | إجراء اختبار A/B بسهولة باستخدام ميزة الإعداد عن بُعد في Firebase |
تجربة السمات والبيانات
- للاطّلاع على مثال حول استخدام النموذج المستضاف، يمكنك الاطّلاع على تطبيق vision سريع التشغيل للاطّلاع على مثال عن استخدام النموذج المجمّع وتطبيق التشغيل السريع automl للاطّلاع على مثال حول استخدام النموذج المستضاف.
- يمكنك الاطّلاع على تطبيق واجهة عرض التصميم المتعدد الأبعاد للتعرّف على عملية التنفيذ الشاملة لواجهة برمجة التطبيقات هذه.
قبل البدء
في ملف
build.gradle
على مستوى المشروع، احرص على تضمين مستودع Maven من Google في كل من القسمَينbuildscript
وallprojects
.أضِف التبعيات لمكتبات ML Kit لنظام التشغيل Android إلى ملف الدرجة على مستوى التطبيق في الوحدة، والذي يكون عادةً
app/build.gradle
:لتجميع نموذج مع تطبيقك:
dependencies { // ... // Object detection & tracking feature with custom bundled model implementation 'com.google.mlkit:object-detection-custom:17.0.1' }
لتنزيل نموذج بشكل ديناميكي من Firebase، أضِف التبعية
linkFirebase
:dependencies { // ... // Object detection & tracking feature with model downloaded // from firebase implementation 'com.google.mlkit:object-detection-custom:17.0.1' 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" } }
سيتم تضمين ملف النموذج في حزمة التطبيق وإتاحته لحزمة تعلّم الآلة كمادة عرض أولية.
إنشاء كائن
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(); } }
ضمان تجربة رائعة للمستخدم
لأفضل تجربة مستخدم، يُرجى اتّباع الإرشادات التالية في تطبيقك:
- يعتمد رصد الكائنات بدقة على مدى التعقيد البصري للكائن. لكي يتم رصدها، قد تحتاج العناصر التي تحتوي على عدد قليل من الميزات المرئية إلى أن تشغل مساحة أكبر من الصورة. يجب عليك تزويد المستخدمين بإرشادات حول التقاط المدخلات التي تعمل بشكل جيد مع نوع الكائنات التي تريد اكتشافها.
- عند استخدام التصنيف، إذا أردت رصد الكائنات التي لا تندرج ضمن الفئات المتوافقة، نفِّذ معالجة خاصة للكائنات غير المعروفة.
يمكنك أيضًا الاطّلاع على تطبيق عرض Material Design في ML Kit ومجموعة أنماط التصميم المتعدد الأبعاد للميزات المستندة إلى تعلُّم الآلة.
Improving performance
إذا أردت استخدام ميزة اكتشاف الأجسام في تطبيق في الوقت الفعلي، يُرجى اتّباع الإرشادات التالية لتحقيق أفضل عدد من اللقطات:عند استخدام وضع البث في تطبيق في الوقت الفعلي، يجب عدم استخدام ميزة رصد عناصر متعددة، لأنّ معظم الأجهزة لن تتمكّن من تسجيل عدد لقطات مناسب في الثانية.
- إذا كنت تستخدم واجهة برمجة التطبيقات
Camera
أوcamera2
، يمكنك تقليل الطلبات إلى أداة الرصد. في حال توفُّر إطار فيديو جديد أثناء تشغيل أداة الرصد، أفلِته. للاطّلاع على مثال، يمكنك الاطّلاع على صفVisionProcessorBase
في نموذج التطبيق السريع. - إذا كنت تستخدم واجهة برمجة التطبيقات
CameraX
، تأكَّد من ضبط استراتيجية الضغط العكسي على قيمتها التلقائيةImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. يضمن ذلك تسليم صورة واحدة فقط لتحليلها في كل مرة. في حال إنشاء المزيد من الصور عندما تكون أداة التحليل مشغولة، سيتم تجاهلها تلقائيًا ولن يتم وضعها في قائمة الانتظار للتسليم. بعد إغلاق الصورة التي يتم تحليلها عن طريق استدعاء ImageProxy.Close()، سيتم عرض آخر صورة تالية. - إذا كنت تستخدم نتيجة أداة الرصد لإضافة رسومات على
الصورة التي تم إدخالها، احصل أولاً على النتيجة من أدوات تعلّم الآلة، ثم اعرض الصورة
والتراكب في خطوة واحدة. ويتم عرض هذا النص على سطح الشاشة
مرة واحدة فقط لكل إطار إدخال. للاطّلاع على مثال، يمكنك الاطّلاع على صفَي
CameraSourcePreview
وGraphicOverlay
في نموذج تطبيق البدء السريع. - في حال استخدام Camera2 API، التقِط الصور بتنسيق
ImageFormat.YUV_420_888
. إذا كنت تستخدم واجهة برمجة تطبيقات الكاميرا القديمة، يجب التقاط الصور بتنسيقImageFormat.NV21
.