برچسبگذاری تصاویر با مدل آموزشدیده AutoML در Android
پساز اینکه مدل خودتان را بااستفاده از AutoML Vision Edge آموزش دادید، میتوانید از آن در برنامهتان برای برچسبگذاری تصاویر استفاده کنید. دو روش برای ادغام مدلهای آموزشدیده از AutoML Vision Edge وجود دارد: میتوانید مدل را با قرار دادن آن در پوشه دارایی برنامهتان بستهبندی کنید، یا میتوانید آن را بهصورت پویا از Firebase بارگیری کنید.| گزینههای بستهبندی مدل | |
|---|---|
| در برنامهتان دستهبندی شده است |
|
| میزبانیشده با Firebase |
|
امتحان کنید
- با برنامه نمونه کار کنید تا نمونهای از کاربرد این API را ببینید.
قبلاز شروع
۱. در فایلbuild.gradle سطح پروژه، حتماً مخزن Maven متعلق به Google را در هر دو بخش buildscript و allprojects اضافه کنید.۲. وابستگیهای کتابخانههای ML Kit Android را به فایل gradle سطح برنامه ماژولتان اضافه کنید که معمولاً
app/build.gradle است:
برای بستهبندی مدل با برنامهتان:
dependencies {
// ...
// Image labeling feature with bundled automl model
implementation 'com.google.mlkit:image-labeling-automl:16.2.1'
}
linkFirebase را اضافه کنید:
dependencies {
// ...
// Image labeling feature with automl model downloaded
// from firebase
implementation 'com.google.mlkit:image-labeling-automl:16.2.1'
implementation 'com.google.mlkit:linkfirebase:16.0.1'
}
۱. بار کردن مدل
پیکربندی منبع مدل محلی
برای بستهبندی مدل با برنامهتان:۱. مدل و فراداده آن را از بایگانی zip که از کنسول Firebase بارگیری کردهاید استخراج کنید. توصیه میکنیم از فایلها همانطور که بارگیری کردهاید استفاده کنید، بدون اینکه آنها را اصلاح کنید (ازجمله نام فایلها).
۲. مدل و فایلهای فراداده آن را در بسته برنامهتان قرار دهید:
الف. اگر پوشه داراییها را در پروژهتان ندارید، با کلیک راست کردن روی پوشه
app/ و سپس کلیک کردن روی جدید > پوشه > پوشه داراییها، آن را ایجاد کنید.ب. زیرپوشهای در پوشه داراییها ایجاد کنید تا فایلهای مدل را دربرگیرد.
ج. فایلهای
model.tflite، dict.txt، و
manifest.json را در زیرپوشه کپی کنید (هر سه فایل باید در
یک پوشه باشند).۳. برای اطمینان از اینکه Gradle هنگام ساختن برنامه، فایل مدل را فشرده نمیکند، موارد زیر را به فایل
build.gradle برنامهتان اضافه کنید:
android {
// ...
aaptOptions {
noCompress "tflite"
}
}
توجه: از نسخه ۴.۱ افزایه Android Gradle، tflite. بهطور پیشفرض به فهرست noCompress اضافه خواهد شد و دیگر به موارد بالا نیازی نیست.
۴. شیء
LocalModel را ایجاد کنید و مسیر فایل مانیفست مدل را مشخص کنید:
Kotlin
val localModel = AutoMLImageLabelerLocalModel.Builder() .setAssetFilePath("manifest.json") // or .setAbsoluteFilePath(absolute file path to manifest file) .build()
جاوا
AutoMLImageLabelerLocalModel localModel = new AutoMLImageLabelerLocalModel.Builder() .setAssetFilePath("manifest.json") // or .setAbsoluteFilePath(absolute file path to manifest file) .build();
پیکربندی منبع مدل میزبانیشده در Firebase
برای استفاده از مدل میزبانیشده ازراهدور، یک شیء RemoteModel ایجاد کنید،
و نامی را که هنگام انتشار مدل به آن اختصاص دادهاید مشخص کنید:
Kotlin
// Specify the name you assigned in the Firebase console. val remoteModel = AutoMLImageLabelerRemoteModel.Builder("your_model_name").build()
جاوا
// Specify the name you assigned in the Firebase console. AutoMLImageLabelerRemoteModel remoteModel = new AutoMLImageLabelerRemoteModel.Builder("your_model_name").build();
سپس، تکلیف بارگیری مدل را شروع کنید و شرایطی را که میخواهید بارگیری تحت آن مجاز باشد مشخص کنید. اگر مدل در دستگاه نباشد، یا اگر نسخه جدیدتری از مدل دردسترس باشد، تکلیف بهصورت ناهمگام مدل را از Firebase بارگیری خواهد کرد:
Kotlin
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. } });
بسیاری از برنامهها تکلیف بارگیری را در کد مقداردهی اولیه خود شروع میکنند، اما شما میتوانید این کار را در هر نقطهای قبلاز نیاز به استفاده از مدل انجام دهید.
ایجاد برچسبزن تصویر از مدل شما
پساز پیکربندی منابع مدل، یک شیء ImageLabeler از یکی از آنها ایجاد کنید.
اگر فقط مدل بستهبندیشده محلی دارید، کافی است برچسبزن را از شیء AutoMLImageLabelerLocalModel خود ایجاد کنید و آستانه امتیاز اطمینان موردنظرتان را پیکربندی کنید (به ارزیابی مدل مراجعه کنید):
Kotlin
val autoMLImageLabelerOptions = AutoMLImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0) // Evaluate your model in the Firebase console // to determine an appropriate value. .build() val labeler = ImageLabeling.getClient(autoMLImageLabelerOptions)
جاوا
AutoMLImageLabelerOptions autoMLImageLabelerOptions = new AutoMLImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console // to determine an appropriate value. .build(); ImageLabeler labeler = ImageLabeling.getClient(autoMLImageLabelerOptions)
اگر مدل با میزبانی از راه دور دارید، باید قبلاز اجرای آن بررسی کنید که بارگیری شده باشد. میتوانید وضعیت تکلیف بارگیری مدل را بااستفاده از روش isModelDownloaded() مدیر مدل بررسی کنید.
Although you only have to confirm this before running the labeler, if you have both a remotely-hosted model and a locally-bundled model, it might make sense to perform this check when instantiating the image labeler: create a labeler from the remote model if it's been downloaded, and from the local model otherwise.
Kotlin
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener { isDownloaded -> val optionsBuilder = if (isDownloaded) { AutoMLImageLabelerOptions.Builder(remoteModel) } else { AutoMLImageLabelerOptions.Builder(localModel) } // Evaluate your model in the Firebase console to determine an appropriate threshold. val options = optionsBuilder.setConfidenceThreshold(0.0f).build() val labeler = ImageLabeling.getClient(options) }
جاوا
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Boolean isDownloaded) { AutoMLImageLabelerOptions.Builder optionsBuilder; if (isDownloaded) { optionsBuilder = new AutoMLImageLabelerOptions.Builder(remoteModel); } else { optionsBuilder = new AutoMLImageLabelerOptions.Builder(localModel); } AutoMLImageLabelerOptions options = optionsBuilder .setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console // to determine an appropriate threshold. .build(); ImageLabeler labeler = ImageLabeling.getClient(options); } });
اگر فقط مدل میزبانیشده از راه دور دارید، باید عملکرد مربوط به مدل را غیرفعال کنید—برای مثال، بخشی از میانای کاربر را خاکستری یا پنهان کنید—تا زمانی که تأیید کنید مدل بارگیری شده است. میتوانید این کار را با پیوست کردن شنونده
به روش 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. }
جاوا
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. } });
۲. آماده کردن تصویر ورودی
سپس، برای هر تصویری که میخواهید برچسبگذاری کنید، یک شیء InputImage
از تصویرتان ایجاد کنید. برچسبزن تصویر زمانی سریعترین عملکرد را دارد که از Bitmap
یا، اگر از camera2 API استفاده میکنید، از YUV_420_888 media.Image استفاده کنید که
درصورت امکان توصیه میشود.
میتوانید شیء 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);
استفاده از URI فایل
برای ایجاد شیء InputImage
از URI فایل، بافت برنامه و URI فایل را به
InputImage.fromFilePath() ارسال کنید. این کار زمانی مفید است که
از ACTION_GET_CONTENT intent برای درخواست از کاربر برای انتخاب
تصویری از برنامه گالریاش استفاده میکنید.
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 بههمراه درجه چرخش نشان داده میشود.
۳. اجرای برچسبزن تصویر
برای برچسبگذاری اشیاء در تصویر، شیءimage را به روش process() مربوط به ImageLabeler ارسال کنید.
Kotlin
labeler.process(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
جاوا
labeler.process(image) .addOnSuccessListener(new OnSuccessListener<List<ImageLabel>>() { @Override public void onSuccess(List<ImageLabel> labels) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
۴. دریافت اطلاعات درباره اشیای برچسبگذاریشده
اگر عملیات برچسبگذاری تصویر موفقیتآمیز باشد، فهرستی از اشیاء ImageLabel
به شنونده موفقیت ارسال میشود. هر شیء ImageLabel نشاندهنده
چیزی است که در تصویر برچسبگذاری شده است. میتوانید شرح نوشتاری هر برچسب، امتیاز اطمینان مطابقت، و نمایه مطابقت را دریافت کنید.
برای مثال:
Kotlin
for (label in labels) { val text = label.text val confidence = label.confidence val index = label.index }
جاوا
for (ImageLabel label : labels) { String text = label.getText(); float confidence = label.getConfidence(); int index = label.getIndex(); }
نکاتی برای بهبود عملکرد همزمان
اگر میخواهید تصاویر را در یک برنامه همزمان برچسبگذاری کنید، این دستورالعملها را دنبال کنید تا به بهترین نرخ فریم دست یابید:
- اگر از میانای برنامهسازی کاربردی
Cameraیاcamera2استفاده میکنید، فراخوانیهای برچسبزن تصویر را محدود کنید. اگر فریم ویدیو جدیدی در حین اجرای برچسبزن تصویر دردسترس قرار گرفت، فریم را رها کنید. برای دیدن نمونه، کلاسVisionProcessorBaseرا در برنامه نمونه شروع سریع ببینید. - اگر از
CameraXAPI استفاده میکنید، مطمئن شوید که استراتژی فشار برگشتی روی مقدار پیشفرض آنImageAnalysis.STRATEGY_KEEP_ONLY_LATESTتنظیم شده باشد. این کار تضمین میکند که در هر بار فقط یک تصویر برای تجزیهوتحلیل ارسال شود. اگر وقتی تحلیلگر مشغول است تصاویر بیشتری تولید شود، این تصاویر بهطور خودکار حذف میشوند و برای تحویل در صف قرار نمیگیرند. وقتی تصویری که درحال تجزیهوتحلیل است با فراخوانی ImageProxy.close() بسته شود، تصویر جدید بعدی ارائه خواهد شد. - اگر از خروجی برچسبزن تصویر برای همپوشانی گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را از ML Kit دریافت کنید، سپس تصویر و همپوشانی را در یک مرحله رندر کنید. این مورد فقط یکبار برای هر فریم ورودی
در سطح نمایشگر پرداز میشود. برای دیدن نمونه، کلاسهای
CameraSourcePreviewوGraphicOverlayرا در برنامه نمونه شروع سریع ببینید. - اگر از Camera2 API استفاده میکنید، تصاویر را در قالب
ImageFormat.YUV_420_888ضبط کنید. اگر از Camera API قدیمیتر استفاده میکنید، تصاویر را در قالبImageFormat.NV21ضبط کنید.