Puedes usar el Kit de AA para reconocer entidades en una imagen y etiquetarlas. Esta API admite una amplia variedad de modelos personalizados de clasificación de imágenes. Por favor, consulta Modelos personalizados con el Kit de AA para obtener orientación sobre requisitos de compatibilidad de modelos, dónde encontrar modelos previamente entrenados, y cómo entrenar tus propios modelos.
Hay dos formas de integrar el etiquetado de imágenes en los modelos personalizados: a través de la agrupación la canalización como parte de tu app o mediante una canalización sin agrupar que depende en los Servicios de Google Play. Si seleccionas la canalización sin empaquetar, se usará más pequeño. Consulta la siguiente tabla para obtener más detalles.
Red de Búsqueda y Red de Display | Sin agrupar | |
---|---|---|
Nombre de la biblioteca | com.google.mlkit:image-labeling-custom | com.google.android.gms:play-services-mlkit-image-labeling-custom |
Implementación | La canalización se vincula de forma estática a tu app en el tiempo de compilación. | La canalización se descarga de forma dinámica a través de los Servicios de Google Play. |
Tamaño de la app | Se aumentó el tamaño de aproximadamente 3.8 MB. | Aumento de tamaño aproximado de 200 KB. |
Hora de inicialización | La canalización está disponible de inmediato. | Es posible que debas esperar a que se descargue la canalización antes de usarla por primera vez. |
Etapa del ciclo de vida de la API | Disponibilidad general (DG) | Beta |
Hay dos formas de integrar un modelo personalizado: empaquetar el modelo colocarla dentro de la carpeta de recursos de la app o descargarla de forma dinámica desde Firebase. En la siguiente tabla, se comparan estas dos opciones.
Modelo empaquetado | Modelo alojado |
---|---|
El modelo es parte del APK de tu app, que aumenta su tamaño. | El modelo no forma parte de tu APK. Se aloja subiendo a Aprendizaje automático de Firebase. |
El modelo está disponible de inmediato, incluso cuando el dispositivo Android está sin conexión | El modelo se descarga a pedido |
No se necesita un proyecto de Firebase | Requiere un proyecto de Firebase |
Debes volver a publicar tu app para actualizar el modelo | Envía actualizaciones del modelo sin volver a publicar tu app |
Sin pruebas A/B integradas | Pruebas A/B sencillas con Firebase Remote Config |
Probar
- Consulta la app de inicio rápido de Vision. para ver un ejemplo de uso del modelo empaquetado la app de inicio rápido de AutoML para un ejemplo de uso del modelo alojado.
Antes de comenzar
En tu archivo
build.gradle
de nivel de proyecto, asegúrate de incluir lo siguiente: Puedes acceder al repositorio Maven de Google en tubuildscript
yallprojects
.Agrega las dependencias para las bibliotecas de Android del ML Kit al archivo archivo de Gradle a nivel de la app, que suele ser
app/build.gradle
. Elige uno de los las siguientes dependencias según tus necesidades:Para empaquetar la canalización con tu aplicación, sigue estos pasos:
dependencies { // ... // Use this dependency to bundle the pipeline with your app implementation 'com.google.mlkit:image-labeling-custom:17.0.3' }
Para usar la canalización en los Servicios de Google Play:
dependencies { // ... // Use this dependency to use the dynamically downloaded pipeline in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-image-labeling-custom:16.0.0-beta5' }
Si eliges usar la canalización en los Servicios de Google Play, puedes configura tu app para descargar automáticamente la canalización al dispositivo después tu app se instala desde Play Store. Para ello, agrega lo siguiente al archivo
AndroidManifest.xml
de tu app:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="custom_ica" /> <!-- To use multiple downloads: android:value="custom_ica,download2,download3" --> </application>
También puedes verificar de forma explícita la disponibilidad de la canalización y solicitar su descarga a través de API de ModuleInstallClient de los Servicios de Google Play
Si no habilitas las descargas de canalizaciones durante la instalación o solicitas una descarga explícita, la canalización se descarga la primera vez que ejecutas el etiquetador. Solicitudes que realizas antes de que se complete la descarga no producirá resultados.
Agrega la dependencia
linkFirebase
si deseas descargar de forma dinámica un de Firebase:Para descargar un modelo de Firebase de forma dinámica, agrega
linkFirebase
. dependencia:dependencies { // ... // Image labeling feature with model downloaded from Firebase implementation 'com.google.mlkit:image-labeling-custom:17.0.3' // Or use the dynamically downloaded pipeline in Google Play Services // implementation 'com.google.android.gms:play-services-mlkit-image-labeling-custom:16.0.0-beta5' implementation 'com.google.mlkit:linkfirebase:17.0.0' }
Si quieres descargar un modelo, asegúrate de agregar Firebase a tu proyecto de Android si aún no lo has hecho. Esto no es necesario cuando agrupas el modelo.
1. Carga el modelo
Configura una fuente de modelo local
Para empaquetar el modelo con tu app, haz lo siguiente:
Copia el archivo de modelo (que generalmente termina en
.tflite
o.lite
) en el archivoassets/
carpeta (Es posible que primero debas crear la carpeta haz clic con el botón derecho en la carpetaapp/
y, luego, en Nuevo > Carpeta > Recursos).Luego, agrega lo siguiente al archivo
build.gradle
de tu app para asegurarte de que Gradle no comprime el archivo del modelo cuando se compila la app:android { // ... aaptOptions { noCompress "tflite" // or noCompress "lite" } }
El archivo del modelo se incluirá en el paquete de la app y estará disponible para el Kit de AA como activo sin procesar.
Crea un objeto
LocalModel
y especifica la ruta de acceso al archivo de modelo:Kotlin
val localModel = LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build()
Java
LocalModel localModel = new LocalModel.Builder() .setAssetFilePath("model.tflite") // or .setAbsoluteFilePath(absolute file path to model file) // or .setUri(URI to model file) .build();
Configura una fuente de modelo alojada en Firebase
Para usar el modelo alojado de forma remota, crea un objeto RemoteModel
. Para ello, haz lo siguiente:
FirebaseModelSource
, que especifica el nombre que le asignaste al modelo cuando
lo publicó:
Kotlin
// Specify the name you assigned in the Firebase console. val remoteModel = CustomRemoteModel .Builder(FirebaseModelSource.Builder("your_model_name").build()) .build()
Java
// Specify the name you assigned in the Firebase console. CustomRemoteModel remoteModel = new CustomRemoteModel .Builder(new FirebaseModelSource.Builder("your_model_name").build()) .build();
Luego, inicia la tarea de descarga del modelo y especifica las condiciones que deseas permitir la descarga. Si el modelo no está en el dispositivo o si es del modelo está disponible, la tarea descargará de forma asíncrona la de Firebase:
Kotlin
val downloadConditions = DownloadConditions.Builder() .requireWifi() .build() RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener { // Success. }
Java
DownloadConditions downloadConditions = new DownloadConditions.Builder() .requireWifi() .build(); RemoteModelManager.getInstance().download(remoteModel, downloadConditions) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(@NonNull Task task) { // Success. } });
Muchas apps comienzan la tarea de descarga en su código de inicialización, pero tú puedes hacerlo en cualquier momento antes de usar el modelo.
Configura el etiquetador de imágenes
Después de configurar las fuentes de tu modelo, crea un objeto ImageLabeler
a partir del
una de ellas.
Están disponibles las siguientes opciones:
Opciones | |
---|---|
confidenceThreshold
|
Puntuación de confianza mínima de las etiquetas detectadas. Si no se establece, de clasificación especificado por los metadatos del modelo. Si el modelo no contiene metadatos o si estos no especificas un umbral del clasificador, se establecerá un umbral predeterminado de 0.0 que se usan. |
maxResultCount
|
Cantidad máxima de etiquetas que se mostrarán. Si no está establecido, el valor predeterminado de Se usará 10. |
Si solo tienes un modelo empaquetado a nivel local, crea un etiquetador desde tu
Objeto LocalModel
:
Kotlin
val customImageLabelerOptions = CustomImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build() val labeler = ImageLabeling.getClient(customImageLabelerOptions)
Java
CustomImageLabelerOptions customImageLabelerOptions = new CustomImageLabelerOptions.Builder(localModel) .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build(); ImageLabeler labeler = ImageLabeling.getClient(customImageLabelerOptions);
Si tienes un modelo alojado de forma remota, deberás verificar que se haya
descargado antes de ejecutarlo. Puedes verificar el estado de la descarga del modelo
tarea con el método isModelDownloaded()
del administrador de modelos.
Aunque solo tienes que confirmarlo antes de ejecutar el etiquetador, si tener un modelo alojado de forma remota y uno empaquetado localmente, podría hacer sentido realizar esta verificación cuando se crea una instancia del etiquetador de imágenes: crea un del etiquetador del modelo remoto, si se descargó, y del sistema de lo contrario.
Kotlin
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener { isDownloaded -> val optionsBuilder = if (isDownloaded) { CustomImageLabelerOptions.Builder(remoteModel) } else { CustomImageLabelerOptions.Builder(localModel) } val options = optionsBuilder .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build() val labeler = ImageLabeling.getClient(options) }
Java
RemoteModelManager.getInstance().isModelDownloaded(remoteModel) .addOnSuccessListener(new OnSuccessListener() { @Override public void onSuccess(Boolean isDownloaded) { CustomImageLabelerOptions.Builder optionsBuilder; if (isDownloaded) { optionsBuilder = new CustomImageLabelerOptions.Builder(remoteModel); } else { optionsBuilder = new CustomImageLabelerOptions.Builder(localModel); } CustomImageLabelerOptions options = optionsBuilder .setConfidenceThreshold(0.5f) .setMaxResultCount(5) .build(); ImageLabeler labeler = ImageLabeling.getClient(options); } });
Si solo tienes un modelo alojado de forma remota, debes inhabilitar las funciones
(por ejemplo, ocultar o inhabilitar parte de la IU) hasta que
confirmas que el modelo se descargó. Puedes hacerlo adjuntando un objeto de escucha
al método download()
del administrador de modelos:
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. }
Java
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. } });
2. Prepara la imagen de entrada
Luego, para cada imagen que quieras etiquetar, crea unInputImage
.
objeto de tu imagen. El etiquetador de imágenes se ejecuta de forma más rápida cuando usas un Bitmap
o, si usas la API de camera2, un media.Image
de YUV_420_888, que son
se recomienda siempre que sea posible.
Puedes crear un InputImage
objeto de diferentes fuentes, cada uno se explica a continuación.
Usa un media.Image
Para crear un elemento InputImage
, sigue estos pasos:
objeto de un objeto media.Image
, como cuando capturas una imagen de una
la cámara del dispositivo, pasa el objeto media.Image
y el
rotación a InputImage.fromMediaImage()
.
Si usas
biblioteca de CameraX, los elementos OnImageCapturedListener
y
Las clases ImageAnalysis.Analyzer
calculan el valor de rotación
por ti.
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 // ... } } }
Java
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 // ... } } }
Si no usas una biblioteca de cámaras que indique el grado de rotación de la imagen, calcularlo a partir del grado de rotación del dispositivo y la orientación de la cámara sensor en el dispositivo:
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 }
Java
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; }
Luego, pasa el objeto media.Image
y el
valor de grado de rotación a InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Usa un URI de archivo
Para crear un elemento InputImage
, sigue estos pasos:
objeto de un URI de archivo, pasa el contexto de la app y el URI del archivo a
InputImage.fromFilePath()
Esto es útil cuando
usa un intent ACTION_GET_CONTENT
para solicitarle al usuario que seleccione
una imagen de su app de galería.
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(); }
Usa un objeto ByteBuffer
o ByteArray
Para crear un elemento InputImage
, sigue estos pasos:
objeto de una ByteBuffer
o ByteArray
, primero calcula la imagen
grado de rotación como se describió anteriormente para la entrada media.Image
.
Luego, crea el objeto InputImage
con el búfer o array, junto con los atributos
El alto, el ancho, el formato de codificación de color y el grado de rotación:
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 )
Java
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 );
Usa un Bitmap
Para crear un elemento InputImage
, sigue estos pasos:
objeto a partir de un objeto Bitmap
, realiza la siguiente declaración:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
La imagen se representa con un objeto Bitmap
junto con los grados de rotación.
3. Ejecuta el etiquetador de imágenes
Para etiquetar objetos de una imagen, pasa el objeto image
al de ImageLabeler
process()
.
Kotlin
labeler.process(image) .addOnSuccessListener { labels -> // Task completed successfully // ... } .addOnFailureListener { e -> // Task failed with an exception // ... }
Java
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 // ... } });
4. Obtén información sobre las entidades etiquetadas
Si la operación de etiquetado de imágenes se ejecuta correctamente, se mostrará una lista deImageLabel
.
se pasan al objeto de escucha que detecta el resultado correcto. Cada objeto ImageLabel
representa un elemento etiquetado en la imagen. Puedes obtener el texto de cada etiqueta
(si está disponible en los metadatos del archivo de modelo de TensorFlow Lite), la puntuación de confianza y el índice. Por ejemplo:
Kotlin
for (label in labels) { val text = label.text val confidence = label.confidence val index = label.index }
Java
for (ImageLabel label : labels) { String text = label.getText(); float confidence = label.getConfidence(); int index = label.getIndex(); }
Sugerencias para mejorar el rendimiento en tiempo real
Si quieres etiquetar imágenes en una aplicación en tiempo real, sigue estos pasos: pautas para lograr la mejor velocidad de fotogramas:
- Si usas
Camera
o API decamera2
, limitar las llamadas al etiquetador de imágenes Si un video nuevo marco esté disponible mientras se ejecuta el etiquetador de imágenes, suéltalo. Consulta laVisionProcessorBase
en la app de muestra de inicio rápido para ver un ejemplo. - Si usas la API de
CameraX
, asegúrate de que la estrategia de contrapresión se haya establecido en su valor predeterminadoImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
De esta forma, se garantiza que solo se entregará una imagen a la vez para su análisis. Si hay más imágenes que se producen cuando el analizador está ocupado, se eliminarán automáticamente y no se agregarán a la cola y la entrega de modelos. Una vez que la imagen que se está analizando se cierra con una llamada a ImageProxy.close(), se publicará la siguiente imagen más reciente. - Si usas la salida del etiquetador de imágenes para superponer gráficos
la imagen de entrada, primero obtén el resultado del ML Kit y, luego, renderiza la imagen
y superponerla en un solo paso. Se renderiza en la superficie de visualización.
solo una vez para cada fotograma de entrada. Consulta la
CameraSourcePreview
yGraphicOverlay
en la app de muestra de inicio rápido para ver un ejemplo. - Si usas la API de Camera2, captura imágenes en
ImageFormat.YUV_420_888
. Si usas la API de Camera, captura imágenes enImageFormat.NV21
.