هنگامی که یک تصویر را به ML Kit ارسال می کنید، حداکثر پنج شی را در تصویر به همراه موقعیت هر شی در تصویر تشخیص می دهد. هنگام شناسایی اشیاء در جریان های ویدئویی، هر شی دارای یک شناسه منحصر به فرد است که می توانید از آن برای ردیابی شی از فریم به فریم استفاده کنید.
شما می توانید از یک مدل طبقه بندی تصویر سفارشی برای طبقه بندی اشیاء شناسایی شده استفاده کنید. لطفاً برای راهنمایی در مورد الزامات سازگاری مدل، مکان یافتن مدل های از پیش آموزش دیده و نحوه آموزش مدل های خود به مدل های سفارشی با کیت ML مراجعه کنید.
دو راه برای ادغام یک مدل سفارشی وجود دارد. میتوانید با قرار دادن آن در پوشه دارایی برنامه خود، مدل را باندل کنید یا میتوانید به صورت پویا آن را از Firebase دانلود کنید. جدول زیر این دو گزینه را با هم مقایسه می کند.
مدل همراه | مدل میزبانی شده |
---|---|
این مدل بخشی از APK برنامه شما است که اندازه آن را افزایش می دهد. | مدل بخشی از APK شما نیست. با آپلود در Firebase Machine Learning میزبانی می شود. |
این مدل بلافاصله در دسترس است، حتی زمانی که دستگاه اندروید آفلاین است | مدل در صورت تقاضا دانلود می شود |
بدون نیاز به پروژه Firebase | به پروژه Firebase نیاز دارد |
برای بهروزرسانی مدل، باید برنامه خود را دوباره منتشر کنید | به روز رسانی مدل را بدون انتشار مجدد برنامه خود فشار دهید |
بدون تست A/B داخلی | تست آسان A/B با Firebase Remote Config |
آن را امتحان کنید
- برای مثالی از استفاده از مدل همراه، برنامه راه اندازی سریع چشم انداز و برای مثال استفاده از مدل میزبانی شده ، برنامه شروع سریع automl را ببینید.
- برای اجرای سرتاسر این API، به برنامه نمایشگاهی Material Design مراجعه کنید.
قبل از شروع
در فایل
build.gradle
در سطح پروژه خود، مطمئن شوید که مخزن Maven Google را در هر دو بخشbuildscript
وallprojects
خود قرار دهید.وابستگی های کتابخانه های اندروید ML Kit را به فایل 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 را به پروژه اندروید خود اضافه کرده اید ، اگر قبلاً این کار را انجام نداده اید. هنگامی که مدل را بسته بندی می کنید، این مورد نیاز نیست.
1. مدل را بارگذاری کنید
یک منبع مدل محلی را پیکربندی کنید
برای بستهبندی مدل با برنامهتان:
فایل مدل (معمولاً به
.tflite
یا.lite
ختم می شود) درassets/
پوشه برنامه خود کپی کنید. (شاید لازم باشد ابتدا با کلیک راست بر رویapp/
پوشه، سپس روی New > Folder > Assets Folder، پوشه را ایجاد کنید.)سپس موارد زیر را به فایل
build.gradle
برنامه خود اضافه کنید تا مطمئن شوید که Gradle فایل مدل را هنگام ساخت برنامه فشرده نمی کند:android { // ... aaptOptions { noCompress "tflite" // or noCompress "lite" } }
فایل مدل در بسته برنامه گنجانده شده و به عنوان دارایی خام در اختیار ML Kit قرار خواهد گرفت.
شی
LocalModel
را ایجاد کنید، مسیر فایل مدل را مشخص کنید:کاتلین
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build()
جاوا
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
ایجاد کنید و نامی را که به مدل اختصاص داده اید هنگام انتشار آن مشخص کنید:
کاتلین
// Specify the name you assigned in the Firebase console. val remoteModel = CustomRemoteModel .Builder(FirebaseModelSource.Builder("your_model_name").build()) .build()
جاوا
// Specify the name you assigned in the Firebase console. CustomRemoteModel remoteModel = new CustomRemoteModel .Builder(new FirebaseModelSource.Builder("your_model_name").build()) .build();
سپس، با مشخص کردن شرایطی که میخواهید اجازه دانلود را بدهید، کار دانلود مدل را شروع کنید. اگر مدل در دستگاه نباشد، یا اگر نسخه جدیدتری از مدل موجود باشد، این کار به صورت ناهمزمان مدل را از Firebase دانلود میکند:
کاتلین
val downloadConditions = DownloadConditions.Builder() .requireWifi() .build() RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener { // Success. }
جاوا
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 استفاده می شود. |
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);
اگر یک مدل با میزبانی از راه دور دارید، قبل از اجرای آن باید بررسی کنید که دانلود شده است. با استفاده از متد isModelDownloaded()
مدیر مدل می توانید وضعیت وظیفه دانلود مدل را بررسی کنید.
اگرچه شما فقط باید قبل از اجرای آشکارساز این موضوع را تأیید کنید، اگر هم یک مدل با میزبانی از راه دور و هم یک مدل همراه محلی دارید، ممکن است انجام این بررسی هنگام نمونهبرداری از آشکارساز تصویر منطقی باشد: اگر یک آشکارساز از مدل راه دور ایجاد کنید. دانلود شده است، و در غیر این صورت از مدل محلی.
کاتلین
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) }
جاوا
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()
مدیر مدل انجام دهید:
کاتلین
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. }
جاوا
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
یا یک media.Image
YUV_420_888 اجرا می شود.تصویر. در صورتی که به یکی از آنها دسترسی مستقیم دارید، ساخت InputImage
از آن منابع توصیه می شود. اگر یک InputImage
از منابع دیگر بسازید، ما تبدیل را به صورت داخلی برای شما انجام خواهیم داد و ممکن است کارایی کمتری داشته باشد. می توانید یک شی InputImage
از منابع مختلف ایجاد کنید که هر کدام در زیر توضیح داده شده است.
استفاده از یک media.Image
برای ایجاد یک شیء InputImage
از یک شیء media.Image
، مانند زمانی که تصویری را از دوربین دستگاه میگیرید، شیء media.Image
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
همراه با درجه چرخش نمایش داده می شود.
4. آشکارساز شی را اجرا کنید
کاتلین
objectDetector .process(image) .addOnFailureListener(e -> {...}) .addOnSuccessListener(results -> { for (detectedObject in results) { // ... } });
جاوا
objectDetector .process(image) .addOnFailureListener(e -> {...}) .addOnSuccessListener(results -> { for (DetectedObject detectedObject : results) { // ... } });
5. اطلاعاتی در مورد اشیاء برچسب دار دریافت کنید
اگر فراخوانی process()
موفق شود، لیستی از DetectedObject
به شنونده موفقیت ارسال می شود.
هر DetectedObject
دارای ویژگی های زیر است:
جعبه مرزی | Rect که موقعیت شی را در تصویر نشان می دهد. | ||||||
شناسه پیگیری | یک عدد صحیح که شی را در بین تصاویر شناسایی می کند. در SINGLE_IMAGE_MODE خالی است. | ||||||
برچسب ها |
|
کاتلین
// 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 برای مجموعه ویژگیهای مبتنی بر یادگیری ماشین را بررسی کنید.
بهبود عملکرد
اگر می خواهید از تشخیص شی در یک برنامه بلادرنگ استفاده کنید، این دستورالعمل ها را برای دستیابی به بهترین نرخ فریم دنبال کنید:وقتی از حالت پخش در یک برنامه بلادرنگ استفاده میکنید، از تشخیص چند شیء استفاده نکنید، زیرا اکثر دستگاهها قادر به تولید نرخ فریم مناسب نیستند.
- اگر از
Camera
یاcamera2
API استفاده می کنید، دریچه گاز با آشکارساز تماس می گیرد. اگر یک قاب ویدیویی جدید در حین کار کردن آشکارساز در دسترس قرار گرفت، قاب را رها کنید. برای مثال، کلاسVisionProcessorBase
را در برنامه نمونه سریع شروع کنید. - اگر از
CameraX
API استفاده میکنید، مطمئن شوید که استراتژی فشار برگشتی روی مقدار پیشفرضImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
تنظیم شده است.STRATEGY_KEEP_ONLY_LATEST. این تضمین می کند که هر بار فقط یک تصویر برای تجزیه و تحلیل تحویل داده می شود. اگر در زمانی که آنالایزر مشغول است، تصاویر بیشتری تولید شود، به طور خودکار حذف می شوند و برای تحویل در صف قرار نمی گیرند. هنگامی که تصویر مورد تجزیه و تحلیل با فراخوانی ImageProxy.close بسته شد، آخرین تصویر بعدی تحویل داده می شود. - اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را از کیت ML بگیرید، سپس تصویر را در یک مرحله رندر کنید و همپوشانی کنید. این تنها یک بار برای هر فریم ورودی به سطح نمایشگر نمایش داده می شود. برای مثال، کلاسهای
CameraSourcePreview
وGraphicOverlay
را در برنامه نمونه شروع سریع ببینید. - اگر از Camera2 API استفاده می کنید، تصاویر را با فرمت
ImageFormat.YUV_420_888
بگیرید. اگر از دوربین قدیمیتر API استفاده میکنید، تصاویر را با فرمتImageFormat.NV21
بگیرید.