אפשר להשתמש ב-Firebase ML כדי לתייג אובייקטים שזוהו בתמונה. בסקירה הכללית מפורט מידע על התכונות של ה-API הזה.
לפני שמתחילים
- אם עדיין לא עשיתם זאת, מוסיפים את Firebase לפרויקט Android.
-
בקובץ Gradle של המודול (ברמת האפליקציה)
(בדרך כלל
<project>/<app-module>/build.gradle.kts
או<project>/<app-module>/build.gradle
), מוסיפים את התלות בספריית Vision Firebase ML ל-Android. מומלץ להשתמש ב-Firebase Android BoM כדי לשלוט בגרסאות הספרייה.dependencies { // Import the BoM for the Firebase platform implementation(platform("com.google.firebase:firebase-bom:33.3.0")) // Add the dependency for the Firebase ML Vision library // When using the BoM, you don't specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-ml-vision' }
כשמשתמשים ב-Firebase Android BoM, האפליקציה תמיד תשתמש בגרסאות תואמות של ספריות Firebase ל-Android.
(חלופה) מוסיפים יחסי תלות לספריות של Firebase בלי להשתמש ב-BoM
אם בוחרים שלא להשתמש במאפיין Firebase BoM, צריך לציין כל גרסה של ספריית Firebase בשורת התלות שלו.
שימו לב: אם האפליקציה שלכם משתמשת במספר ספריות של Firebase, מומלץ מומלץ להשתמש בפקודה BoM כדי לנהל גרסאות של ספריות, וכך להבטיח שכל הגרסאות תואמת.
dependencies { // Add the dependency for the Firebase ML Vision library // When NOT using the BoM, you must specify versions in Firebase library dependencies implementation 'com.google.firebase:firebase-ml-vision:24.1.0' }
-
אם עדיין לא הפעלתם ממשקי API מבוססי-Cloud בפרויקט, עליכם לעשות זאת עכשיו:
- פותחים את Firebase ML דף ממשקי ה-API במסוף Firebase.
-
אם עדיין לא שדרגתם את הפרויקט לתוכנית התמחור והתשלומים של Blaze, לוחצים על כדי לעשות זאת, אפשר לשדרג. (תתבקש לשדרג רק אם הוא לא בתוכנית Blaze.)
רק בפרויקטים ברמת Blaze אפשר להשתמש בממשקי API מבוססי-Cloud.
- אם ממשקי ה-API מבוססי-הענן עדיין לא מופעלים, לוחצים על Enable Cloud-based APIs.
עכשיו אפשר להוסיף תוויות לתמונות.
1. הכנת תמונת הקלט
יוצרים אובייקטFirebaseVisionImage
מהתמונה.
מתייג התמונות פועל הכי מהר כשמשתמשים ב-Bitmap
או אם משתמשים
Camera2 API, media.Image
בפורמט JPEG, שמומלץ כאשר
ככל האפשר.
-
כדי ליצור אובייקט
FirebaseVisionImage
מאובייקטmedia.Image
, למשל כשיוצרים תמונה ממצלמת המכשיר, מעבירים את האובייקטmedia.Image
ואת סיבוב התמונה אלFirebaseVisionImage.fromMediaImage()
.אם משתמשים ספריית CameraX,
OnImageCapturedListener
ImageAnalysis.Analyzer
מחלקות מחשבים את ערך הסבב עבורך, אז צריך רק להמיר את הסבבFirebase MLROTATION_
קבועים לפני קריאהFirebaseVisionImage.fromMediaImage()
:Kotlin+KTX
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 Vision API // ... } } }
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 Vision API // ... } }
אם לא משתמשים בספריית מצלמה שמאפשרת סיבוב תמונה, הוא יכול לחשב אותו על סמך סיבוב המכשיר וכיוון המצלמה החיישן במכשיר:
Kotlin+KTX
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 }
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; }
לאחר מכן מעבירים את האובייקט
media.Image
ואת ערך הסיבוב אלFirebaseVisionImage.fromMediaImage()
:Kotlin+KTX
val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation)
Java
FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation);
- כדי ליצור אובייקט
FirebaseVisionImage
מ-URI של קובץ, מעבירים את ההקשר של האפליקציה ואת ה-URI של הקובץFirebaseVisionImage.fromFilePath()
. האפשרות הזו שימושית כשמשתמשים בכוונהACTION_GET_CONTENT
כדי לבקש מהמשתמש לבחור תמונה מאפליקציית הגלריה שלו.Kotlin+KTX
val image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
FirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
- כדי ליצור אובייקט
FirebaseVisionImage
מתוךByteBuffer
או מערך בייטים, מחשבים קודם את התמונה של סיבוב הנתונים כפי שמתואר למעלה עבור קלטmedia.Image
.לאחר מכן יוצרים אובייקט
FirebaseVisionImageMetadata
שמכיל את הגובה, הרוחב, פורמט קידוד הצבע והסיבוב של התמונה:Kotlin+KTX
val metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build()
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();
משתמשים במאגר הנתונים הזמני או במערך ובאובייקט המטא-נתונים כדי ליצור אובייקט
FirebaseVisionImage
:Kotlin+KTX
val image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata)
Java
FirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata);
- כדי ליצור אובייקט
FirebaseVisionImage
מאובייקטBitmap
:Kotlin+KTX
val image = FirebaseVisionImage.fromBitmap(bitmap)
Java
FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap);
Bitmap
חייבת להיות מוצבת בצורה זקופה, ללא צורך בסיבוב נוסף.
2. הגדרה והרצה של הכלי לתיוג תמונות
כדי להוסיף תוויות לאובייקטים בתמונה, צריך להעביר את האובייקטFirebaseVisionImage
אל
השיטה processImage
של FirebaseVisionImageLabeler
.
קודם כל, נקבל מופע של
FirebaseVisionImageLabeler
Kotlin+KTX
val labeler = FirebaseVision.getInstance().getCloudImageLabeler() // Or, to set the minimum confidence required: // val options = FirebaseVisionCloudImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build() // val labeler = FirebaseVision.getInstance().getCloudImageLabeler(options)
Java
FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance() .getCloudImageLabeler(); // Or, to set the minimum confidence required: // FirebaseVisionCloudImageLabelerOptions options = // new FirebaseVisionCloudImageLabelerOptions.Builder() // .setConfidenceThreshold(0.7f) // .build(); // FirebaseVisionImageLabeler labeler = FirebaseVision.getInstance() // .getCloudImageLabeler(options);
לאחר מכן, מעבירים את התמונה לשיטה
processImage()
:Kotlin+KTX
labeler.processImage(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
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 // ... } });
3. אחזור מידע על אובייקטים מתויגים
אם הפעולה של הוספת תווית לתמונות מצליחה, רשימה שלFirebaseVisionImageLabel
אובייקטים יועברו אל
הוא אוזן הצלחה. כל אובייקט FirebaseVisionImageLabel
מייצג משהו שסומן בתמונה. אפשר לראות את הטקסט של כל תווית
תיאור,
מזהה ישות ב-Knowledge Graph
(אם זמין), ואת רמת הסמך של ההתאמה. לדוגמה:
Kotlin+KTX
for (label in labels) {
val text = label.text
val entityId = label.entityId
val confidence = label.confidence
}
Java
for (FirebaseVisionImageLabel label: labels) {
String text = label.getText();
String entityId = label.getEntityId();
float confidence = label.getConfidence();
}
השלבים הבאים
- לפני שפורסים לסביבת ייצור אפליקציה שמשתמשת ב-Cloud API, צריך לבצע את הפעולות הבאות צעדים נוספים למניעה ולצמצום ההשפעה של גישה לא מורשית ל-API.