برچسبگذاری تصاویر با مدل آموزشدیده 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() مدیر مدل بررسی کنید.
اگرچه فقط باید این مورد را قبلاز اجرای برچسبزن تأیید کنید، اما اگر هم مدل از دور میزبانیشده و هم مدل بستهبندیشده محلی دارید، ممکن است منطقی باشد که این بررسی را هنگام نمونهسازی برچسبزن تصویر انجام دهید: اگر مدل از دور بارگیری شده است، برچسبزنی از مدل از دور ایجاد کنید، و درغیراینصورت از مدل محلی ایجاد کنید.
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);
استفاده از نشانی وب فایل
برای ایجاد یک شیء 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 بههمراه درجههای چرخش نشان داده میشود.
۳. اجرای برچسبزن تصویر
برای برچسبگذاری اشیا در تصویر، شیءimage را به روش ImageLabeler's
process() منتقل کنید.
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یاcamera2API استفاده میکنید، فراخوانیهای برچسبگذار تصویر را محدود کنید. اگر قاب ویدیو جدیدی درحین اجرای برچسبزن تصویر دردسترس قرار گرفت، قاب را رها کنید. برای نمونه، کلاسVisionProcessorBaseرا در برنامه نمونه شروع سریع ببینید. - اگر از
CameraXAPI استفاده میکنید، مطمئن شوید که استراتژی فشار برگشتی روی مقدار پیشفرض آن تنظیم شده باشدImageAnalysis.STRATEGY_KEEP_ONLY_LATEST. این کار تضمین میکند که در هر بار فقط یک تصویر برای تجزیهوتحلیل ارسال شود. اگر وقتی تجزیهگر مشغول است تصاویر بیشتری تولید شود، این تصاویر بهطور خودکار حذف میشوند و در صف تحویل قرار نمیگیرند. وقتی تصویری که درحال تجزیهوتحلیل است با فراخوانی ImageProxy.close() بسته شود، تصویر جدید بعدی ارائه خواهد شد. - اگر از برونداد برچسبزن تصویر برای رونهادن گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را از ML Kit دریافت کنید، سپس تصویر و رونهاد را در یک مرحله پردازش کنید. این کار فقط یکبار برای هر قاب ورودی در سطح نمایش انجام میشود. برای نمونه، کلاسهای
CameraSourcePreviewوGraphicOverlayرا در برنامه نمونه راهاندازی سریع ببینید. - اگر از Camera2 API استفاده میکنید، تصاویر را در قالب
ImageFormat.YUV_420_888ضبط کنید. اگر از Camera API قدیمیتر استفاده میکنید، تصاویر را در قالبImageFormat.NV21ضبط کنید.