وقتی تصویری را به کیت ML ارسال میکنید، کیت تا پنج شیء را در تصویر به همراه موقعیت هر شیء در تصویر تشخیص میدهد. هنگام تشخیص اشیاء در جریانهای ویدیویی، هر شیء یک شناسه منحصر به فرد دارد که میتوانید از آن برای ردیابی شیء از فریم به فریم استفاده کنید.
شما میتوانید از یک مدل طبقهبندی تصویر سفارشی برای طبقهبندی اشیاء شناساییشده استفاده کنید. برای راهنمایی در مورد الزامات سازگاری مدل، محل یافتن مدلهای از پیش آموزشدیده و نحوه آموزش مدلهای خود، به مدلهای سفارشی با کیت ML مراجعه کنید.
دو راه برای ادغام یک مدل سفارشی وجود دارد. میتوانید مدل را با قرار دادن آن در پوشه asset برنامه خود، به صورت دستهای (bundle) درآورید، یا میتوانید آن را به صورت پویا از Cloud Storage دانلود کنید. جدول زیر این دو گزینه را با هم مقایسه میکند.
| مدل بستهای | مدل میزبانی شده |
|---|---|
| این مدل بخشی از APK برنامه شماست که باعث افزایش حجم آن میشود. | این مدل بخشی از APK شما نیست. با آپلود در فضای ابری میزبانی میشود. توصیه میکنیم از فضای ابری برای Firebase استفاده کنید. |
| این مدل بلافاصله در دسترس است، حتی زمانی که دستگاه اندروید آفلاین باشد | برنامه شما باید شامل کدی باشد که مدل را بر اساس تقاضا دانلود کند |
| نیازی به پروژه Firebase نیست | به یک پروژه Firebase نیاز دارد (در صورت استفاده از Cloud Storage برای Firebase). |
| برای بهروزرسانی مدل، باید برنامه خود را دوباره منتشر کنید | بهروزرسانیهای مدل را بدون انتشار مجدد برنامه خود، ارسال کنید |
| تست A/B داخلی ندارد | تست A/B با پیکربندی از راه دور Firebase |
امتحانش کن.
- برای مثالی از نحوهی استفاده از مدل همراه، به برنامهی شروع سریع vision و برای مثالی از نحوهی استفاده از مدل میزبانی شده ، به برنامهی شروع سریع automl مراجعه کنید.
- برای پیادهسازی کامل این API، به برنامهی نمایشی Material Design مراجعه کنید.
قبل از اینکه شروع کنی
۱. در فایلbuild.gradle.kts در سطح پروژه، مطمئن شوید که مخزن Maven گوگل را هم در بخشهای buildscript و هم allprojects خود وارد کردهاید.وابستگیهای کتابخانههای اندروید ML Kit را به فایل gradle سطح app ماژول خود که معمولاً
app/build.gradle.ktsاست، اضافه کنید:برای باندل کردن یک مدل با برنامهتان:
dependencies { // ... // Object detection & tracking feature with custom bundled model implementation("com.google.mlkit:object-detection-custom:17.0.2") }اگر میخواهید مدلی را از فضای ذخیرهسازی ابری برای فایربیس دانلود کنید ، اگر قبلاً فایربیس را به پروژه اندروید خود اضافه نکردهاید، حتماً آن را اضافه کنید . این کار هنگام بستهبندی مدل لازم نیست.
۱. مدل را بارگذاری کنید
میتوانید مدل را از یک منبع محلی یا یک منبع میزبانی شده از راه دور بارگذاری کنید.
پیکربندی یک منبع مدل محلی
برای اتصال مدل به برنامه خود:
فایل مدل (که معمولاً به
.tfliteیا.liteختم میشود) را در پوشهassets/برنامه خود کپی کنید. (ممکن است لازم باشد ابتدا با کلیک راست رویapp/پوشه و سپس کلیک روی New > Folder > Assets Folder ، پوشه را ایجاد کنید.)یک شیء
LocalModelایجاد کنید و مسیر فایل مدل را مشخص کنید:کاتلین
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();
پیکربندی یک منبع مدل میزبانی شده از راه دور
برای استفاده از مدل میزبانیشده از راه دور، باید فایل مدل را با استفاده از منطق برنامه خود در حافظه محلی دستگاه دانلود کنید و سپس آن را به عنوان یک مدل محلی بارگذاری کنید. توصیه میکنیم برای میزبانی یک مدل از فضای ذخیرهسازی ابری برای فایربیس استفاده کنید. برای جزئیات پیادهسازی، به راهنمای مهاجرت از فایربیس ML به فضای ذخیرهسازی ابری مراجعه کنید.
۲. آشکارساز شیء را پیکربندی کنید
پس از پیکربندی منابع مدل خود، آشکارساز شیء را برای مورد استفاده خود با یک شیء CustomObjectDetectorOptions پیکربندی کنید. میتوانید تنظیمات زیر را تغییر دهید:
| تنظیمات آشکارساز شیء | |
|---|---|
| حالت تشخیص | STREAM_MODE (پیشفرض) | SINGLE_IMAGE_MODE در در |
| تشخیص و ردیابی چندین شیء | false (پیشفرض) | trueاینکه آیا تا پنج شیء شناسایی و ردیابی شود یا فقط برجستهترین شیء (پیشفرض). |
| طبقهبندی اشیاء | false (پیشفرض) | true آیا اشیاء شناسایی شده با استفاده از مدل طبقهبندی سفارشی ارائه شده طبقهبندی شوند یا خیر. برای استفاده از مدل طبقهبندی سفارشی خود، باید این را روی |
| آستانه اطمینان طبقهبندی | حداقل امتیاز اطمینان برچسبهای شناساییشده. در صورت عدم تنظیم، از هر آستانه طبقهبندیکنندهای که توسط فراداده مدل مشخص شده باشد، استفاده خواهد شد. اگر مدل حاوی هیچ فرادادهای نباشد یا فراداده آستانه طبقهبندیکنندهای را مشخص نکند، از آستانه پیشفرض ۰.۰ استفاده خواهد شد. |
| حداکثر برچسب برای هر شیء | حداکثر تعداد برچسبهایی که آشکارساز به ازای هر شیء برمیگرداند. اگر تنظیم نشود، مقدار پیشفرض ۱۰ استفاده خواهد شد. |
API تشخیص و ردیابی اشیا برای این دو مورد استفاده اصلی بهینه شده است:
- تشخیص و ردیابی زنده برجستهترین شیء در منظرهیاب دوربین.
- تشخیص چندین شیء از یک تصویر ثابت
برای پیکربندی API برای این موارد استفاده، با یک مدل بستهبندیشده محلی:
کاتلین
// 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);
اگر مدلی دارید که از راه دور میزبانی میشود، باید قبل از اجرای آن، بررسی کنید که دانلود شده باشد.
اگرچه شما فقط باید قبل از اجرای آشکارساز این موضوع را تأیید کنید، اما اگر هم یک مدل میزبانی از راه دور و هم یک مدل بستهبندی شده محلی دارید، انجام این بررسی هنگام نمونهسازی آشکارساز تصویر منطقی است: اگر مدل از راه دور دانلود شده است، یک آشکارساز از آن و در غیر این صورت از مدل محلی ایجاد کنید.
کاتلین
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);
اگر فقط یک مدل میزبانیشده از راه دور دارید، باید عملکردهای مرتبط با مدل را غیرفعال کنید - مثلاً بخشی از رابط کاربری خود را خاکستری کنید یا پنهان کنید - تا زمانی که تأیید کنید مدل دانلود شده است.
کاتلین
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 مقدار چرخش را برای شما محاسبه میکنند.
کاتلین
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 // ... } } }
اگر از کتابخانه دوربینی که درجه چرخش تصویر را به شما بدهد استفاده نمیکنید، میتوانید آن را از درجه چرخش دستگاه و جهت سنسور دوربین در دستگاه محاسبه کنید:
کاتلین
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() ارسال کنید:
کاتلین
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
استفاده از یک URI فایل
برای ایجاد یک شیء InputImage از یک URI فایل، متن برنامه و URI فایل را به InputImage.fromFilePath() ارسال کنید. این زمانی مفید است که از یک ACTION_GET_CONTENT برای وادار کردن کاربر به انتخاب یک تصویر از برنامه گالری خود استفاده میکنید.
کاتلین
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 را با بافر یا آرایه، به همراه ارتفاع، عرض، فرمت کدگذاری رنگ و درجه چرخش تصویر ایجاد کنید:
کاتلین
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 ، تعریف زیر را انجام دهید:
کاتلین
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
تصویر توسط یک شیء Bitmap به همراه درجه چرخش نمایش داده میشود.
۴. آشکارساز شیء را اجرا کنید
کاتلین
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 که موقعیت شیء را در تصویر نشان میدهد. | ||||||
| شناسه ردیابی | یک عدد صحیح که شیء را در تصاویر مشخص میکند. در SINGLE_IMAGE_MODE مقدار null دارد. | ||||||
| برچسبها |
|
کاتلین
// 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 Material Design و مجموعه ویژگیهای Material Design Patterns for machine-powered را بررسی کنید.
بهبود عملکرد
اگر میخواهید از تشخیص اشیا در یک برنامهی بلادرنگ استفاده کنید، برای دستیابی به بهترین نرخ فریم، این دستورالعملها را دنبال کنید:وقتی از حالت استریمینگ در یک برنامهی بلادرنگ استفاده میکنید، از تشخیص چند شیء استفاده نکنید، زیرا اکثر دستگاهها قادر به تولید فریمریت کافی نخواهند بود.
- اگر از API
Cameraیاcamera2استفاده میکنید، فراخوانیهای throttle به آشکارساز انجام میشود. اگر در حین اجرای آشکارساز، یک فریم ویدیویی جدید در دسترس قرار گرفت، فریم را حذف کنید. برای مثال، به کلاسVisionProcessorBaseدر برنامه نمونه شروع سریع مراجعه کنید. - اگر از API
CameraXاستفاده میکنید، مطمئن شوید که استراتژی فشار معکوس (backpressure strategy) روی مقدار پیشفرض خود یعنیImageAnalysis.STRATEGY_KEEP_ONLY_LATESTتنظیم شده است. این تضمین میکند که فقط یک تصویر در هر زمان برای تجزیه و تحلیل تحویل داده میشود. اگر تصاویر بیشتری هنگام مشغول بودن تحلیلگر تولید شوند، به طور خودکار حذف میشوند و برای تحویل در صف قرار نمیگیرند. پس از بسته شدن تصویر در حال تجزیه و تحلیل با فراخوانی ImageProxy.close()، آخرین تصویر بعدی تحویل داده میشود. - اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را از کیت ML دریافت کنید، سپس تصویر و همپوشانی را در یک مرحله رندر کنید. این کار فقط یک بار برای هر فریم ورودی روی سطح نمایشگر رندر میشود. برای مثال به کلاسهای
CameraSourcePreviewوGraphicOverlayدر برنامه نمونه شروع سریع مراجعه کنید. - اگر از API دوربین ۲ استفاده میکنید، تصاویر را با فرمت
ImageFormat.YUV_420_888ضبط کنید. اگر از API دوربین قدیمیتر استفاده میکنید، تصاویر را با فرمتImageFormat.NV21ضبط کنید.