وقتی تصویری را به ML Kit میدهید، این کیت حداکثر پنج شیء را در تصویر بههمراه موقعیت هر شیء در تصویر تشخیص میدهد. هنگام تشخیص اشیا در جاریسازیهای ویدیو، هر شیء شناسه یکتایی دارد که میتوانید از آن برای ردیابی شیء از فریمی به فریم دیگر استفاده کنید.
میتوانید از مدل طبقهبندی تصویر سفارشی برای طبقهبندی کردن اشیایی که شناسایی میشوند استفاده کنید. برای راهنمایی درباره الزامات سازگاری مدل، مکان یافتن مدلهای ازپیش آموزشدیده، و نحوه آموزش مدلهای خودتان، به مدلهای سفارشی با ML Kit مراجعه کنید.
دو روش برای ادغام مدل سفارشی وجود دارد. میتوانید مدل را با قرار دادن آن در پوشه دارایی برنامه دستهبندی کنید، یا میتوانید آن را بهصورت پویا از Cloud Storage بارگیری کنید. جدول زیر این دو گزینه را مقایسه میکند.
| مدل بستهای | مدل میزبانیشده |
|---|---|
| این مدل بخشی از فایل APK برنامه شما است که باعث افزایش حجم آن میشود. | مدل بخشی از APK شما نیست. با بارگذاری در Cloud Storage میزبانی میشود. توصیه میکنیم از Cloud Storage for Firebase استفاده کنید. |
| این مدل بلافاصله دردسترس است، حتی وقتی دستگاه Android آفلاین باشد | برنامهتان باید حاوی کد برای بارگیری مدل درصورت نیاز باشد |
| نیازی به پروژه Firebase نیست | به پروژه Firebase نیاز دارد (درصورت استفاده از Cloud Storage for Firebase). |
| برای بهروز کردن مدل باید برنامهتان را دوباره منتشر کنید | بهروزرسانیهای مدل را بدون انتشار مجدد برنامهتان ارسال کنید |
| آزمون آ/ب داخلی ندارد | آزمایش آ/ب با پیکربندی از راه دور Firebase |
امتحان کنید
- برای نمونهای از استفاده از مدل دستهای و برنامه شروع سریع automl برای نمونهای از استفاده از مدل میزبانیشده، برنامه شروع سریع دیداری را ببینید.
- برای پیادهسازی سرتاسری این API، به برنامه ویترین Material Design مراجعه کنید.
قبلاز شروع
۱. در فایلbuild.gradle.kts در سطح پروژه، حتماً مخزن Maven مربوط به Google را در هر دو بخش buildscript و allprojects اضافه کنید.
وابستگیهای کتابخانههای ML Kit Android را به فایل gradle سطح برنامه واحدتان اضافه کنید، که معمولاً
app/build.gradle.ktsاست:برای دستهبندی مدل با برنامه:
dependencies { // ... // Object detection & tracking feature with custom bundled model implementation("com.google.mlkit:object-detection-custom:17.0.2") }اگر میخواهید مدلی را از «فضای ذخیرهسازی ابری ویژه Firebase» بارگیری کنید، مطمئن شوید که Firebase را به پروژه Android خود اضافه کردهاید، اگر قبلاً این کار را نکردهاید. این کار هنگام بستهبندی مدل لازم نیست.
۱. بار کردن مدل
میتوانید مدل را از منبعی که بهصورت محلی بستهبندی شده است یا منبعی که از راه دور میزبانی میشود بار کنید.
پیکربندی منبع مدل محلی
برای بستهبندی کردن مدل با برنامهتان:
فایل مدل (معمولاً با
.tfliteیا.liteتمام میشود) را در پوشهassets/برنامه کپی کنید. (ممکن است ابتدا لازم باشد پوشه را با کلیک راست روی پوشهapp/و سپس کلیک روی جدید > پوشه > پوشه داراییها ایجاد کنید.)یک شیء
LocalModelایجاد کنید و مسیر فایل مدل را مشخص کنید:Kotlin
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute path to model file) // or .setUri(URI to model file) .build()
جاوا
LocalModel localModel = new LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute path to model file) // or .setUri(URI to model file) .build();
پیکربندی منبع مدل میزبانیشده از راه دور
برای استفاده از مدل میزبانیشده از دور، باید فایل مدل را بااستفاده از منطق برنامه خودتان در فضای ذخیرهسازی محلی دستگاه بارگیری کنید و سپس آن را بهعنوان مدل محلی بار کنید. توصیه میکنیم برای میزبانی مدل از «فضای ذخیرهسازی ابری ویژه Firebase» استفاده کنید. برای جزئیات پیادهسازی، راهنمای انتقال Firebase ML به Cloud Storage را ببینید.
۲. پیکربندی آشکارساز شیء
پساز پیکربندی منابع مدل، آشکارساز شیء را برای مورد استفادهتان با شیء CustomObjectDetectorOptions پیکربندی کنید. میتوانید تنظیمات زیر را تغییر دهید:
| تنظیمات تشخیص اشیا | |
|---|---|
| حالت تشخیص |
STREAM_MODE (پیشفرض) | SINGLE_IMAGE_MODE
در در |
| شناسایی و ردیابی چند شیء |
false (پیشفرض) | true
اینکه حداکثر پنج شیء یا فقط برجستهترین شیء (پیشفرض) شناسایی و ردیابی شود. |
| طبقهبندی اشیاء |
false (پیشفرض) | true
اینکه آیا اشیای شناساییشده بااستفاده از مدل طبقهبند سفارشی ارائهشده طبقهبندی شوند یا نه. برای استفاده از مدل طبقهبندی سفارشیتان،
باید این را روی |
| آستانه اطمینان طبقهبندی |
حداقل امتیاز اطمینان برچسبهای شناساییشده. اگر تنظیم نشده باشد، از هر آستانه طبقهبندیکنندهای که در فراداده مدل مشخص شده است استفاده خواهد شد. اگر مدل هیچ فرادادهای نداشته باشد یا فراداده آستانه طبقهبندی را مشخص نکند، از آستانه پیشفرض ۰٫۰ استفاده خواهد شد. |
| حداکثر برچسب برای هر شیء |
حداکثر تعداد برچسبها برای هر شیء که آشکارساز برمیگرداند. اگر تنظیم نشود، از مقدار پیشفرض ۱۰ استفاده خواهد شد. |
API تشخیص و ردیابی شیء برای این دو مورد استفاده اصلی بهینهسازی شده است:
- تشخیص و ردیابی زنده برجستهترین شیء در منظرهیاب دوربین.
- تشخیص چند شیء از یک تصویر ثابت.
برای پیکربندی API برای این موارد استفاده، با مدل بستهبندیشده محلی:
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)
جاوا
// 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);
اگر مدل از دور میزبانی میشود، باید بررسی کنید که قبلاز اجرا شدن بارگیری شده باشد.
اگرچه فقط باید این موضوع را قبلاز اجرای آشکارساز تأیید کنید، اما اگر هم مدل میزبانیشده از راه دور و هم مدل بستهبندیشده محلی دارید، ممکن است منطقی باشد که این بررسی را هنگام نمونهسازی آشکارساز تصویر انجام دهید: اگر مدل از راه دور بارگیری شده است، آشکارساز را از آن مدل ایجاد کنید و درغیراینصورت از مدل محلی ایجاد کنید.
Kotlin
val modelFile = File(context.cacheDir, "my_remote_model.tflite") val model = if (modelFile.exists()) { // Use the downloaded model if available LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build() } else { // Fall back to the bundled model LocalModel.Builder().setAssetFilePath("model.tflite").build() } val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(model) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build() val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions)
جاوا
File modelFile = new File(context.getCacheDir(), "my_remote_model.tflite"); LocalModel model; if (modelFile.exists()) { // Use the downloaded model if available model = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build(); } else { // Fall back to the bundled model model = new LocalModel.Builder().setAssetFilePath("model.tflite").build(); } CustomObjectDetectorOptions customObjectDetectorOptions = new CustomObjectDetectorOptions.Builder(model) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .setClassificationConfidenceThreshold(0.5f) .setMaxPerObjectLabelCount(3) .build(); ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions);
اگر فقط مدل میزبانی از دور دارید، باید عملکرد مربوط به مدل را غیرفعال کنید—برای مثال، بخشی از رابط کاربریتان را خاکستری کنید یا پنهان کنید—تا زمانی که تأیید کنید مدل بارگیری شده است.
Kotlin
val localFile = File(context.cacheDir, "my_remote_model.tflite") if (localFile.exists()) { // Model is already cached, initialize immediately initializeDetector(localFile) } else { // Model is not yet available, show loading UI and start download showLoadingUI() val storage = Firebase.storage val modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite") modelRef.getFile(localFile) .addOnSuccessListener { // Download complete, initialize the detector hideLoadingUI() initializeDetector(localFile) } .addOnFailureListener { // Handle download error showErrorUI() } } private fun initializeDetector(modelFile: File) { val localModel = LocalModel.Builder().setAbsoluteFilePath(modelFile.absolutePath).build() val customObjectDetectorOptions = CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .build() val objectDetector = ObjectDetection.getClient(customObjectDetectorOptions) // Enable ML-related UI features here enableMLFeatures(objectDetector) }
جاوا
File localFile = new File(context.getCacheDir(), "my_remote_model.tflite"); if (localFile.exists()) { // Model is already cached, initialize immediately initializeDetector(localFile); } else { // Model is not yet available, show loading UI and start download showLoadingUI(); FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference modelRef = storage.getReferenceFromUrl("gs://YOUR_BUCKET/path/to/model.tflite"); modelRef.getFile(localFile) .addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() { @Override public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { // Download complete, initialize the detector hideLoadingUI(); initializeDetector(localFile); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception exception) { // Handle download error showErrorUI(); } }); } private void initializeDetector(File modelFile) { LocalModel localModel = new LocalModel.Builder().setAbsoluteFilePath(modelFile.getAbsolutePath()).build(); CustomObjectDetectorOptions customObjectDetectorOptions = new CustomObjectDetectorOptions.Builder(localModel) .setDetectorMode(CustomObjectDetectorOptions.SINGLE_IMAGE_MODE) .enableClassification() .build(); ObjectDetector objectDetector = ObjectDetection.getClient(customObjectDetectorOptions); // Enable ML-related UI features here enableMLFeatures(objectDetector); }
۳. آماده کردن تصویر ورودی
شیء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 // ... } } }
جاوا
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 }
جاوا
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);
استفاده از شناسه منبع یکسان فایل
برای ایجاد شیء 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 )
جاوا
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 بههمراه درجه چرخش نشان داده میشود.
۴. اجرای شناساگر شیء
Kotlin
objectDetector .process(image) .addOnFailureListener(e -> {...}) .addOnSuccessListener(results -> { for (detectedObject in results) { // ... } });
جاوا
objectDetector .process(image) .addOnFailureListener(e -> {...}) .addOnSuccessListener(results -> { for (DetectedObject detectedObject : results) { // ... } });
۵. دریافت اطلاعات درباره اشیای برچسبگذاریشده
اگر تماس با process() موفقیتآمیز باشد، فهرستی از DetectedObject به شنونده موفقیت ارسال میشود.
هر DetectedObject شامل ویژگیهای زیر است:
| چارگوش محصورکننده | Rect که موقعیت شیء را در تصویر نشان میدهد. |
||||||
| شناسه رهگیری | عدد صحیحی که شیء را در سراسر تصاویر شناسایی میکند. Null در 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 } }
جاوا
// 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» و مجموعه الگوهای ویژگیهای با بهرهگیری از یادگیری ماشین را بررسی کنید.
بهبود عملکرد
اگر میخواهید از تشخیص اشیا در برنامه همزمان استفاده کنید، این رهنمودها را دنبال کنید تا به بهترین نرخ فریم دست یابید:وقتی از حالت جاریسازی در برنامه همزمان استفاده میکنید، از تشخیص چندگانه شیء استفاده نکنید، زیرا اکثر دستگاهها نمیتوانند نرخهای قاب کافی تولید کنند.
- اگر از
Cameraیاcamera2API استفاده میکنید، فراخوانیهای آشکارساز را محدود کنید. اگر قاب ویدیو جدیدی درحالیکه آشکارساز درحال اجرا است دردسترس قرار گرفت، قاب را رها کنید. برای نمونه، کلاسVisionProcessorBaseرا در برنامه نمونه شروع سریع ببینید. - اگر از
CameraXAPI استفاده میکنید، مطمئن شوید که استراتژی فشار برگشتی روی مقدار پیشفرض آنImageAnalysis.STRATEGY_KEEP_ONLY_LATESTتنظیم شده باشد. این کار تضمین میکند که در هر بار فقط یک تصویر برای تجزیهوتحلیل ارسال شود. اگر وقتی تجزیهگر مشغول است تصاویر بیشتری تولید شود، این تصاویر بهطور خودکار حذف میشوند و در صف تحویل قرار نمیگیرند. وقتی تصویری که درحال تجزیهوتحلیل است با فراخوانی ImageProxy.close() بسته شود، تصویر جدید بعدی ارائه خواهد شد. - اگر از برونداد آشکارساز برای رونهادن گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را از ML Kit دریافت کنید، سپس تصویر را پرداز و در یک مرحله رونهاد کنید. این کار فقط یکبار برای هر قاب ورودی در سطح نمایش انجام میشود. برای نمونه، کلاسهای
CameraSourcePreviewوGraphicOverlayرا در برنامه نمونه راهاندازی سریع ببینید. - اگر از Camera2 API استفاده میکنید، تصاویر را در قالب
ImageFormat.YUV_420_888ضبط کنید. اگر از Camera API قدیمیتر استفاده میکنید، تصاویر را در قالبImageFormat.NV21ضبط کنید.