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