بعد از اینکه مدل خودتان را با استفاده از AutoML Vision Edge آموزش دادید ، میتوانید از آن در برنامه خود برای برچسبگذاری تصاویر استفاده کنید.
قبل از اینکه شروع کنی
- اگر هنوز Firebase را به پروژه اندروید خود اضافه نکردهاید، آن را اضافه کنید.
- وابستگیهای کتابخانههای اندروید ML Kit را به فایل Gradle ماژول (سطح برنامه) خود (معمولاً
app/build.gradle) اضافه کنید:apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' implementation 'com.google.firebase:firebase-ml-vision-automl:18.0.5' }
۱. مدل را بارگذاری کنید
ML Kit مدلهای تولید شده توسط AutoML شما را روی دستگاه اجرا میکند. با این حال، میتوانید ML Kit را طوری پیکربندی کنید که مدل شما را از راه دور از Firebase، از حافظه محلی یا هر دو بارگذاری کند.
با میزبانی مدل در Firebase، میتوانید مدل را بدون انتشار نسخه جدید برنامه بهروزرسانی کنید و میتوانید Remote Config و A/B Testing برای ارائه پویای مدلهای مختلف به مجموعههای مختلف کاربران استفاده کنید.
اگر تصمیم دارید مدل را فقط با میزبانی آن در Firebase ارائه دهید و آن را با برنامه خود همراه نکنید، میتوانید حجم اولیه دانلود برنامه خود را کاهش دهید. البته به خاطر داشته باشید که اگر مدل با برنامه شما همراه نباشد، هیچ یک از عملکردهای مرتبط با مدل تا زمانی که برنامه شما برای اولین بار مدل را دانلود نکند، در دسترس نخواهد بود.
با ترکیب مدل خود با برنامهتان، میتوانید مطمئن شوید که ویژگیهای یادگیری ماشین برنامهتان حتی زمانی که مدل میزبانیشده توسط Firebase در دسترس نیست، همچنان کار میکنند.
پیکربندی یک منبع مدل میزبانیشده توسط Firebase
برای استفاده از مدل میزبانیشده از راه دور، یک شیء FirebaseAutoMLRemoteModel ایجاد کنید و نامی را که هنگام انتشار مدل به آن اختصاص دادهاید، مشخص کنید:
Java
// Specify the name you assigned in the Firebase console.
FirebaseAutoMLRemoteModel remoteModel =
new FirebaseAutoMLRemoteModel.Builder("your_remote_model").build();
Kotlin
// Specify the name you assigned in the Firebase console.
val remoteModel = FirebaseAutoMLRemoteModel.Builder("your_remote_model").build()
سپس، وظیفه دانلود مدل را آغاز کنید و شرایطی را که میخواهید تحت آن اجازه دانلود داده شود، مشخص کنید. اگر مدل روی دستگاه نباشد، یا اگر نسخه جدیدتری از مدل در دسترس باشد، وظیفه به صورت غیرهمزمان مدل را از Firebase دانلود میکند:
Java
FirebaseModelDownloadConditions conditions = new FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build();
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
// Success.
}
});
Kotlin
val conditions = FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build()
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Success.
}
بسیاری از برنامهها وظیفه دانلود را در کد مقداردهی اولیه خود شروع میکنند، اما شما میتوانید این کار را در هر زمانی قبل از نیاز به استفاده از مدل انجام دهید.
پیکربندی یک منبع مدل محلی
برای اتصال مدل به برنامه خود:
- مدل و متادیتای آن را از فایل زیپی که از کنسول Firebase دانلود کردهاید، استخراج کنید. توصیه میکنیم از فایلها به همان شکلی که دانلود کردهاید، بدون تغییر (از جمله نام فایلها) استفاده کنید.
مدل و فایلهای متادیتای آن را در بسته برنامه خود قرار دهید:
- اگر پوشه assets را در پروژه خود ندارید، با کلیک راست روی
app/folder و سپس کلیک روی New > Folder > Assets Folder، یکی ایجاد کنید. - یک زیرپوشه در زیر پوشه assets ایجاد کنید تا فایلهای مدل در آن قرار گیرند.
- فایلهای
model.tflite،dict.txtوmanifest.jsonرا در زیرپوشه کپی کنید (هر سه فایل باید در یک پوشه باشند).
- اگر پوشه assets را در پروژه خود ندارید، با کلیک راست روی
- برای اطمینان از اینکه Gradle هنگام ساخت برنامه، فایل مدل را فشرده نمیکند، موارد زیر را به فایل
build.gradleبرنامه خود اضافه کنید: فایل مدل در بسته برنامه گنجانده شده و به عنوان یک فایل خام در اختیار ML Kit قرار خواهد گرفت.android { // ... aaptOptions { noCompress "tflite" } } - یک شیء
FirebaseAutoMLLocalModelایجاد کنید و مسیر فایل مانیفست مدل را مشخص کنید:Java
FirebaseAutoMLLocalModel localModel = new FirebaseAutoMLLocalModel.Builder() .setAssetFilePath("manifest.json") .build();Kotlin
val localModel = FirebaseAutoMLLocalModel.Builder() .setAssetFilePath("manifest.json") .build()
یک برچسب تصویر از مدل خود ایجاد کنید
پس از پیکربندی منابع مدل خود، یک شیء FirebaseVisionImageLabeler از یکی از آنها ایجاد کنید.
اگر فقط یک مدل محلی دارید، کافیست یک برچسبگذار از شیء FirebaseAutoMLLocalModel خود ایجاد کنید و آستانه امتیاز اطمینان مورد نیاز خود را پیکربندی کنید ( به بخش ارزیابی مدل خود مراجعه کنید).
Java
FirebaseVisionImageLabeler labeler;
try {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions options =
new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console
// to determine an appropriate value.
.build();
labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options);
} catch (FirebaseMLException e) {
// ...
}
Kotlin
val options = FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
.setConfidenceThreshold(0) // Evaluate your model in the Firebase console
// to determine an appropriate value.
.build()
val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options)
اگر یک مدل از راه دور دارید، باید قبل از اجرای آن، بررسی کنید که آیا دانلود شده است یا خیر. میتوانید وضعیت وظیفه دانلود مدل را با استفاده از متد isModelDownloaded() در model manager بررسی کنید.
اگرچه شما فقط باید قبل از اجرای برچسبگذار این موضوع را تأیید کنید، اما اگر هم یک مدل میزبانیشده از راه دور و هم یک مدل بستهبندیشده محلی دارید، انجام این بررسی هنگام نمونهسازی برچسبگذار تصویر منطقی است: اگر مدل از راه دور دانلود شده است، یک برچسبگذار از آن و در غیر این صورت از مدل محلی ایجاد کنید.
Java
FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener(new OnSuccessListener<Boolean>() {
@Override
public void onSuccess(Boolean isDownloaded) {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder optionsBuilder;
if (isDownloaded) {
optionsBuilder = new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(remoteModel);
} else {
optionsBuilder = new FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel);
}
FirebaseVisionOnDeviceAutoMLImageLabelerOptions options = optionsBuilder
.setConfidenceThreshold(0.0f) // Evaluate your model in the Firebase console
// to determine an appropriate threshold.
.build();
FirebaseVisionImageLabeler labeler;
try {
labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options);
} catch (FirebaseMLException e) {
// Error.
}
}
});
Kotlin
FirebaseModelManager.getInstance().isModelDownloaded(remoteModel)
.addOnSuccessListener { isDownloaded ->
val optionsBuilder =
if (isDownloaded) {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(remoteModel)
} else {
FirebaseVisionOnDeviceAutoMLImageLabelerOptions.Builder(localModel)
}
// Evaluate your model in the Firebase console to determine an appropriate threshold.
val options = optionsBuilder.setConfidenceThreshold(0.0f).build()
val labeler = FirebaseVision.getInstance().getOnDeviceAutoMLImageLabeler(options)
}
اگر فقط یک مدل میزبانیشده از راه دور دارید، باید عملکردهای مرتبط با مدل را غیرفعال کنید - برای مثال، بخشی از رابط کاربری خود را خاکستری یا پنهان کنید - تا زمانی که تأیید کنید مدل دانلود شده است. میتوانید این کار را با اتصال یک شنونده به متد download() در model manager انجام دهید:
Java
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@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.
}
});
Kotlin
FirebaseModelManager.getInstance().download(remoteModel, conditions)
.addOnCompleteListener {
// Download complete. Depending on your app, you could enable the ML
// feature, or switch from the local model to the remote model, etc.
}
۲. تصویر ورودی را آماده کنید
سپس، برای هر تصویری که میخواهید برچسبگذاری کنید، با استفاده از یکی از گزینههای شرح داده شده در این بخش، یک شیء FirebaseVisionImage ایجاد کنید و آن را به یک نمونه از FirebaseVisionImageLabeler (که در بخش بعدی شرح داده شده است) منتقل کنید.
شما میتوانید یک شیء FirebaseVisionImage را از یک شیء media.Image ، یک فایل روی دستگاه، یک آرایه بایت یا یک شیء Bitmap ایجاد کنید:
برای ایجاد یک شیء
FirebaseVisionImageاز یک شیءmedia.Image، مانند زمانی که از دوربین دستگاه تصویر میگیرید، شیءmedia.Imageو چرخش تصویر را بهFirebaseVisionImage.fromMediaImage()ارسال کنید.اگر از کتابخانه CameraX استفاده میکنید، کلاسهای
OnImageCapturedListenerوImageAnalysis.Analyzerمقدار چرخش را برای شما محاسبه میکنند، بنابراین فقط کافی است قبل از فراخوانیFirebaseVisionImage.fromMediaImage()، چرخش را به یکی از ثابتهایROTATION_در ML Kit تبدیل کنید:Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } }
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } }
اگر از کتابخانه دوربینی که چرخش تصویر را به شما میدهد استفاده نمیکنید، میتوانید آن را از چرخش دستگاه و جهتگیری حسگر دوربین در دستگاه محاسبه کنید:
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * 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, Context context) 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); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; }
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * 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, context: Context): 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) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result }
سپس، شیء
media.Imageو مقدار rotation را بهFirebaseVisionImage.fromMediaImage()ارسال کنید:Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
Kotlin
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
- برای ایجاد یک شیء
FirebaseVisionImageاز یک URI فایل، متن برنامه و URI فایل را بهFirebaseVisionImage.fromFilePath()ارسال کنید. این زمانی مفید است که از یکACTION_GET_CONTENTبرای وادار کردن کاربر به انتخاب تصویر از برنامه گالری خود استفاده میکنید.Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Kotlin
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
- برای ایجاد یک شیء
FirebaseVisionImageاز یکByteBufferیا یک آرایه بایت، ابتدا چرخش تصویر را همانطور که در بالا برای ورودیmedia.Imageتوضیح داده شد، محاسبه کنید.سپس، یک شیء
FirebaseVisionImageMetadataایجاد کنید که شامل ارتفاع، عرض، فرمت کدگذاری رنگ و چرخش تصویر باشد:Java
FirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build();
Kotlin
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
از بافر یا آرایه و شیء فراداده برای ایجاد یک شیء
FirebaseVisionImageاستفاده کنید:Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
Kotlin
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
- برای ایجاد یک شیء
FirebaseVisionImageاز یک شیءBitmap:تصویر نمایش داده شده توسط شیءJava
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Kotlin
val image = FirebaseVisionImage.fromBitmap(bitmap)
Bitmapباید عمودی باشد و نیازی به چرخش اضافی نباشد.
۳. برچسبگذار تصویر را اجرا کنید
برای برچسبگذاری اشیاء در یک تصویر، شیء FirebaseVisionImage را به متد processImage() در FirebaseVisionImageLabeler ارسال کنید.
Java
labeler.processImage(image)
.addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionImageLabel>>() {
@Override
public void onSuccess(List<FirebaseVisionImageLabel> labels) {
// Task completed successfully
// ...
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// Task failed with an exception
// ...
}
});
Kotlin
labeler.processImage(image)
.addOnSuccessListener { labels ->
// Task completed successfully
// ...
}
.addOnFailureListener { e ->
// Task failed with an exception
// ...
}
اگر برچسبگذاری تصویر با موفقیت انجام شود، آرایهای از اشیاء FirebaseVisionImageLabel به شنونده موفقیت ارسال میشود. از هر شیء، میتوانید اطلاعاتی در مورد یک ویژگی شناساییشده در تصویر دریافت کنید.
برای مثال:
Java
for (FirebaseVisionImageLabel label: labels) {
String text = label.getText();
float confidence = label.getConfidence();
}
Kotlin
for (label in labels) {
val text = label.text
val confidence = label.confidence
}
نکاتی برای بهبود عملکرد در زمان واقعی
- اگر در حین کار آشکارساز، فریم ویدیویی جدیدی در دسترس قرار گرفت، فریم را حذف کنید.
- اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را از کیت ML دریافت کنید، سپس تصویر و همپوشانی را در یک مرحله رندر کنید. با انجام این کار، برای هر فریم ورودی فقط یک بار روی سطح نمایشگر رندر میکنید.
اگر از API دوربین ۲ استفاده میکنید، تصاویر را با فرمت
ImageFormat.YUV_420_888ثبت کنید.اگر از API دوربین قدیمیتر استفاده میکنید، تصاویر را با فرمت
ImageFormat.NV21ضبط کنید.