Puedes usar ML Kit para reconocer entidades en una imagen y etiquetarlas. Esta API admite una amplia variedad de modelos personalizados de clasificación de imágenes. Consulta Modelos personalizados con ML Kit para obtener orientación sobre los requisitos de compatibilidad de los modelos, dónde encontrar modelos previamente entrenados y cómo entrenar tus propios modelos.
Existen dos formas de integrar un modelo personalizado. Puedes empaquetar el modelo colocándolo dentro de la carpeta de recursos de tu app o descargarlo de forma dinámica desde Cloud Storage. En la siguiente tabla, se comparan las dos opciones.
| Modelo agrupado | Modelo alojado |
|---|---|
| El modelo es parte del APK de la app, lo que aumenta su tamaño. | El modelo no forma parte de tu APK. Se aloja subiéndolo a Cloud Storage. Te recomendamos que uses Cloud Storage para Firebase. |
| El modelo está disponible de inmediato, incluso cuando el dispositivo Android está sin conexión | Tu app debe incluir código para descargar el modelo a pedido |
| No se necesita un proyecto de Firebase | Se requiere un proyecto de Firebase (si se usa Cloud Storage para Firebase). |
| Debes volver a publicar tu app para actualizar el modelo | El modelo de envío se actualiza sin volver a publicar la app |
| No hay pruebas A/B integradas | Pruebas A/B con Firebase Remote Config |
Probar
- Consulta la app de inicio rápido de Vision para ver un ejemplo del uso del modelo incluido y la app de inicio rápido de AutoML para ver un ejemplo del uso del modelo alojado.
Antes de comenzar
Incluye las bibliotecas de ML Kit en tu Podfile:
pod 'GoogleMLKit/ImageLabelingCustom', '8.0.0'Después de instalar o actualizar los Pods de tu proyecto, abre el proyecto de Xcode con su
.xcworkspace. ML Kit es compatible con Xcode 13.2.1 o versiones posteriores.Si quieres descargar un modelo con Cloud Storage para Firebase, asegúrate de agregar Firebase a tu proyecto de iOS, en caso de que aún no lo hayas hecho. Esto no es obligatorio cuando se agrupa un modelo.
1. Carga el modelo
Configura una fuente de modelo local
Sigue estos pasos para empaquetar el modelo con tu app:
Copia el archivo del modelo (que generalmente termina en
.tfliteo.lite) en tu proyecto de Xcode y asegúrate de seleccionarCopy bundle resourcescuando lo hagas. El archivo del modelo se incluirá en el paquete de aplicación y estará disponible para ML Kit.Crea un objeto
LocalModely especifica la ruta al archivo del modelo:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
Configura una fuente de modelo alojada de forma remota
Para usar el modelo alojado de forma remota, debes descargar el archivo del modelo en el almacenamiento local del dispositivo con la lógica de tu propia app y, luego, cargarlo como un modelo local. Te recomendamos que uses Cloud Storage para Firebase para alojar un modelo. Para obtener detalles de la implementación, consulta la guía de migración de Firebase ML a Cloud Storage.
Configura el etiquetador de imágenes
Después de configurar las fuentes de tu modelo, crea un objeto ImageLabeler a partir de una de ellas.
Están disponibles las siguientes opciones:
| Opciones | |
|---|---|
confidenceThreshold
|
Es la puntuación de confianza mínima de las etiquetas detectadas. Si no se configura, se usará cualquier umbral del clasificador especificado por los metadatos del modelo. Si el modelo no contiene metadatos o estos no especifican un umbral del clasificador, se usará un umbral predeterminado de 0.0. |
maxResultCount
|
Es la cantidad máxima de etiquetas que se devolverán. Si no se establece, se usará el valor predeterminado de 10. |
Si solo tienes un modelo empaquetado a nivel local, crea un etiquetador a partir del objeto LocalModel:
Swift
let options = CustomImageLabelerOptions(localModel: localModel) options.confidenceThreshold = NSNumber(value: 0.0) let imageLabeler = ImageLabeler.imageLabeler(options: options)
Objective-C
MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; options.confidenceThreshold = @(0.0); MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
Si tienes un modelo alojado de forma remota, comprueba si se descargó antes de ejecutarlo.
Aunque solo debes confirmar esto antes de ejecutar el etiquetador, si tienes un modelo alojado de forma remota y un modelo incluido de forma local, podría tener sentido realizar esta verificación cuando instancias ImageLabeler: crea un etiquetador a partir del modelo remoto si se descargó y a partir del modelo local en caso contrario.
Swift
// Path where your download logic saves the model let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") let model: LocalModel if FileManager.default.fileExists(atPath: localModelURL.path) { // Use the downloaded model model = LocalModel(path: localModelURL.path) } else { // Fall back to bundled model guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return } model = LocalModel(path: bundledModelPath) } let options = CustomImageLabelerOptions(localModel: model) let imageLabeler = ImageLabeler.imageLabeler(options: options)
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; MLKLocalModel *model; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Use the downloaded model model = [[MLKLocalModel alloc] initWithPath:localModelPath]; } else { // Fall back to bundled model NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"]; model = [[MLKLocalModel alloc] initWithPath:bundledModelPath]; } MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:model]; MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
Si solo tienes un modelo alojado de forma remota, debes inhabilitar la funcionalidad relacionada con el modelo, por ejemplo, oculta o inhabilita parte de tu IU, hasta que confirmes que el modelo se descargó.
Swift
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") if FileManager.default.fileExists(atPath: localModelURL.path) { // Model is already cached, initialize immediately self.initializeLabeler(with: localModelURL) } else { // Model is not yet available, show loading UI and start download self.showLoadingUI() let storage = Storage.storage() let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite") modelRef.write(toFile: localModelURL) { url, error in self.hideLoadingUI() if let error = error { // Handle download error self.showErrorUI() } else if let modelURL = url { // Download success, initialize labeler self.initializeLabeler(with: modelURL) } } } func initializeLabeler(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomImageLabelerOptions(localModel: localModel) self.imageLabeler = ImageLabeler.imageLabeler(options: options) // Enable ML-related UI features here self.enableMLFeatures() }
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath]; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Model is already cached, initialize immediately [self initializeLabelerWithURL:localModelURL]; } else { // Model is not yet available, show loading UI and start download [self showLoadingUI]; FIRStorage *storage = [FIRStorage storage]; FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"]; [modelRef writeToFile:localModelURL completion:^(NSURL * _Nullable URL, NSError * _Nullable error) { [self hideLoadingUI]; if (error != nil) { // Handle download error [self showErrorUI]; } else { // Download success, initialize labeler [self initializeLabelerWithURL:URL]; } }]; } - (void)initializeLabelerWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; self.imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options]; // Enable ML-related UI features here [self enableMLFeatures]; }
2. Prepara la imagen de entrada
Crea un objeto VisionImage con un UIImage o un CMSampleBuffer.
Si usas una UIImage, sigue estos pasos:
- Crea un objeto
VisionImageconUIImage. Asegúrate de especificar la.orientationcorrecta.Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Si usas una CMSampleBuffer, sigue estos pasos:
-
Especifica la orientación de los datos de imagen contenidos en
CMSampleBuffer.Para obtener la orientación de la imagen, haz lo siguiente:
Swift
func imageOrientation( deviceOrientation: UIDeviceOrientation, cameraPosition: AVCaptureDevice.Position ) -> UIImage.Orientation { switch deviceOrientation { case .portrait: return cameraPosition == .front ? .leftMirrored : .right case .landscapeLeft: return cameraPosition == .front ? .downMirrored : .up case .portraitUpsideDown: return cameraPosition == .front ? .rightMirrored : .left case .landscapeRight: return cameraPosition == .front ? .upMirrored : .down case .faceDown, .faceUp, .unknown: return .up } }
Objective-C
- (UIImageOrientation) imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation cameraPosition:(AVCaptureDevicePosition)cameraPosition { switch (deviceOrientation) { case UIDeviceOrientationPortrait: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored : UIImageOrientationRight; case UIDeviceOrientationLandscapeLeft: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored : UIImageOrientationUp; case UIDeviceOrientationPortraitUpsideDown: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored : UIImageOrientationLeft; case UIDeviceOrientationLandscapeRight: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored : UIImageOrientationDown; case UIDeviceOrientationUnknown: case UIDeviceOrientationFaceUp: case UIDeviceOrientationFaceDown: return UIImageOrientationUp; } }
- Crea un objeto
VisionImagecon el objetoCMSampleBuffery la orientación:Swift
let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
Objective-C
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. Ejecuta el etiquetador de imágenes
Para etiquetar objetos de una imagen, pasa el objeto image al método process() de ImageLabeler.
De forma asíncrona:
Swift
imageLabeler.process(image) { labels, error in guard error == nil, let labels = labels, !labels.isEmpty else { // Handle the error. return } // Show results. }
Objective-C
[imageLabeler processImage:image completion:^(NSArray*_Nullable labels, NSError *_Nullable error) { if (label.count == 0) { // Handle the error. return; } // Show results. }];
De forma síncrona:
Swift
var labels: [ImageLabel] do { labels = try imageLabeler.results(in: image) } catch let error { // Handle the error. return } // Show results.
Objective-C
NSError *error; NSArray*labels = [imageLabeler resultsInImage:image error:&error]; // Show results or handle the error.
4. Obtén información sobre las entidades etiquetadas
Si la operación de etiquetado de imágenes se ejecuta correctamente, se muestra un array deImageLabel. Cada ImageLabel representa un elemento que se etiquetó en la imagen. Puedes obtener la descripción del texto de cada etiqueta (si está disponible en los metadatos del archivo de modelo de LiteRT), la puntuación de confianza y el índice. Por ejemplo:
Swift
for label in labels { let labelText = label.text let confidence = label.confidence let index = label.index }
Objective-C
for (MLKImageLabel *label in labels) { NSString *labelText = label.text; float confidence = label.confidence; NSInteger index = label.index; }
Sugerencias para mejorar el rendimiento en tiempo real
Si quieres etiquetar imágenes en una aplicación en tiempo real, sigue estos lineamientos para lograr la mejor velocidad de fotogramas:
- Para procesar fotogramas de video, usa la API síncrona
results(in:)del detector. Llama a este método desde la funcióncaptureOutput(_, didOutput:from:)delAVCaptureVideoDataOutputSampleBufferDelegatepara obtener de forma síncrona los resultados del fotograma de video determinado. Mantén elAVCaptureVideoDataOutputdelalwaysDiscardsLateVideoFramescomotruepara limitar las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, se ignorará. - Si usas la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado de ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, procesas en la superficie de visualización solo una vez por cada fotograma de entrada procesado. Consulta updatePreviewOverlayViewWithLastFrame en la muestra de inicio rápido del Kit de ML para ver un ejemplo.