ML Kit یک SDK بهینهسازی شده برای تقسیمبندی سلفی فراهم میکند.
دارایی های Selfie Segmenter به صورت ایستا به برنامه شما در زمان ساخت مرتبط هستند. با این کار اندازه دانلود برنامه شما حدود 4.5 مگابایت افزایش مییابد و تأخیر API میتواند از 25 میلیثانیه تا 65 میلیثانیه بسته به اندازه تصویر ورودی، همانطور که در پیکسل 4 اندازهگیری میشود، متفاوت باشد.
آن را امتحان کنید
- با برنامه نمونه بازی کنید تا نمونه استفاده از این API را ببینید.
قبل از شروع
- در فایل
build.gradle
در سطح پروژه خود، مطمئن شوید که مخزن Maven Google را در هر دو بخشbuildscript
وallprojects
خود قرار دهید. - وابستگی های کتابخانه های اندروید ML Kit را به فایل gradle سطح برنامه ماژول خود اضافه کنید، که معمولا
app/build.gradle
است:
dependencies {
implementation 'com.google.mlkit:segmentation-selfie:16.0.0-beta6'
}
1. یک نمونه از Segmenter ایجاد کنید
گزینه های بخش بندی
برای انجام تقسیم بندی روی یک تصویر، ابتدا با تعیین گزینه های زیر یک نمونه از Segmenter
ایجاد کنید.
حالت آشکارساز
Segmenter
در دو حالت عمل می کند. مطمئن شوید که موردی را انتخاب می کنید که با مورد استفاده شما مطابقت دارد.
STREAM_MODE (default)
این حالت برای پخش فریم ها از فیلم یا دوربین طراحی شده است. در این حالت، قطعهساز از نتایج فریمهای قبلی استفاده میکند تا نتایج تقسیمبندی نرمتر را برگرداند.
SINGLE_IMAGE_MODE
این حالت برای تصاویر تکی طراحی شده است که مرتبط نیستند. در این حالت، قطعهساز هر تصویر را بهطور مستقل پردازش میکند، بدون هموارسازی فریمها.
ماسک اندازه خام را فعال کنید
از قطعهساز میخواهد تا ماسک اندازه خام را که با اندازه خروجی مدل مطابقت دارد، برگرداند.
اندازه ماسک خام (به عنوان مثال 256x256) معمولاً کوچکتر از اندازه تصویر ورودی است. لطفاً SegmentationMask#getWidth()
و SegmentationMask#getHeight()
را صدا بزنید تا هنگام فعال کردن این گزینه اندازه ماسک را دریافت کنید.
بدون تعیین این گزینه، قطعهساز ماسک خام را تغییر مقیاس میدهد تا با اندازه تصویر ورودی مطابقت داشته باشد. اگر می خواهید منطق تغییر مقیاس سفارشی شده را اعمال کنید یا برای مورد استفاده شما نیازی به تغییر مقیاس نیست، از این گزینه استفاده کنید.
گزینه های قطعه ساز را مشخص کنید:
کاتلین
val options = SelfieSegmenterOptions.Builder() .setDetectorMode(SelfieSegmenterOptions.STREAM_MODE) .enableRawSizeMask() .build()
جاوا
SelfieSegmenterOptions options = new SelfieSegmenterOptions.Builder() .setDetectorMode(SelfieSegmenterOptions.STREAM_MODE) .enableRawSizeMask() .build();
یک نمونه از Segmenter
ایجاد کنید. گزینه هایی که مشخص کردید را پاس کنید:
کاتلین
val segmenter = Segmentation.getClient(options)
جاوا
Segmenter segmenter = Segmentation.getClient(options);
2. تصویر ورودی را آماده کنید
برای انجام بخش بندی روی یک تصویر، یک شی InputImage
از Bitmap
، media.Image
، ByteBuffer
، آرایه بایت یا یک فایل روی دستگاه ایجاد کنید.
می توانید یک شی 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
همراه با درجه چرخش نمایش داده می شود.
3. تصویر را پردازش کنید
شی InputImage
آماده شده را به متد process
Segmenter
ارسال کنید.
کاتلین
Task<SegmentationMask> result = segmenter.process(image) .addOnSuccessListener { results -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
جاوا
Task<SegmentationMask> result = segmenter.process(image) .addOnSuccessListener( new OnSuccessListener<SegmentationMask>() { @Override public void onSuccess(SegmentationMask mask) { // Task completed successfully // ... } }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
4. نتیجه تقسیم بندی را دریافت کنید
شما می توانید نتیجه تقسیم بندی را به صورت زیر بدست آورید:
کاتلین
val mask = segmentationMask.getBuffer() val maskWidth = segmentationMask.getWidth() val maskHeight = segmentationMask.getHeight() for (val y = 0; y < maskHeight; y++) { for (val x = 0; x < maskWidth; x++) { // Gets the confidence of the (x,y) pixel in the mask being in the foreground. val foregroundConfidence = mask.getFloat() } }
جاوا
ByteBuffer mask = segmentationMask.getBuffer(); int maskWidth = segmentationMask.getWidth(); int maskHeight = segmentationMask.getHeight(); for (int y = 0; y < maskHeight; y++) { for (int x = 0; x < maskWidth; x++) { // Gets the confidence of the (x,y) pixel in the mask being in the foreground. float foregroundConfidence = mask.getFloat(); } }
برای مثال کاملی از نحوه استفاده از نتایج تقسیمبندی، لطفاً نمونه راهاندازی سریع کیت ML را ببینید.
نکاتی برای بهبود عملکرد
کیفیت نتایج شما به کیفیت تصویر ورودی بستگی دارد:
- برای اینکه ML Kit به یک نتیجه تقسیم بندی دقیق دست یابد، تصویر باید حداقل 256x256 پیکسل باشد.
- فوکوس ضعیف تصویر نیز می تواند بر دقت تأثیر بگذارد. اگر نتایج قابل قبولی دریافت نکردید، از کاربر بخواهید که تصویر را دوباره بگیرد.
اگر میخواهید از بخشبندی در یک برنامه بلادرنگ استفاده کنید، این دستورالعملها را برای دستیابی به بهترین نرخ فریم دنبال کنید:
- از
STREAM_MODE
استفاده کنید. - گرفتن تصاویر با وضوح کمتر را در نظر بگیرید. با این حال، الزامات ابعاد تصویر این API را نیز در نظر داشته باشید.
- فعال کردن گزینه raw size mask و ترکیب همه منطق تغییر مقیاس با هم را در نظر بگیرید. به عنوان مثال، به جای اینکه به API اجازه دهید ابتدا ماسک را تغییر مقیاس دهد تا با اندازه تصویر ورودی شما مطابقت داشته باشد و سپس مجدداً آن را تغییر دهید تا با اندازه نمایش برای نمایش مطابقت داشته باشد، فقط ماسک اندازه خام را درخواست کنید و این دو مرحله را در یکی ترکیب کنید.
- اگر از
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
بگیرید.