Você pode usar o Kit de ML para reconhecer e rotular entidades em uma imagem. Essa API é compatível com uma ampla variedade de modelos personalizados de classificação de imagens. Consulte Modelos personalizados com o Kit de ML para orientações sobre requisitos de compatibilidade de modelos, onde encontrar modelos pré-treinados e como treinar seus próprios modelos.
Há duas maneiras de integrar um modelo personalizado. Você pode empacotar o modelo colocando-o na pasta de recursos do app ou fazer o download dele dinamicamente do Cloud Storage. A tabela a seguir compara as duas opções.
| Modelo agrupado | Modelo hospedado |
|---|---|
| O modelo faz parte do APK do seu app, o que aumenta o tamanho dele. | O modelo não faz parte do seu APK. Ele é hospedado por upload no Cloud Storage. Recomendamos usar o Cloud Storage para Firebase. |
| O modelo estará disponível imediatamente, mesmo quando o dispositivo Android estiver off-line | O app precisa incluir código para fazer o download do modelo sob demanda. |
| Não é necessário ter um projeto do Firebase | Requer um projeto do Firebase (se você estiver usando o Cloud Storage para Firebase). |
| Você precisa republicar o app para atualizar o modelo | Enviar atualizações do modelo sem republicar o app |
| Sem teste A/B integrado | Teste A/B com a Configuração remota do Firebase |
Faça um teste
- Consulte o app de início rápido do Vision para um exemplo de uso do modelo agrupado e o app de início rápido do AutoML para um exemplo de uso do modelo hospedado.
Antes de começar
Inclua as bibliotecas do kit de ML no seu Podfile:
pod 'GoogleMLKit/ImageLabelingCustom', '8.0.0'Depois de instalar ou atualizar os pods do projeto, abra o projeto do Xcode usando o
.xcworkspace. O Kit de ML é compatível com a versão 13.2.1 ou superior do Xcode.Se você quiser fazer o download de um modelo usando o Cloud Storage para Firebase, adicione o Firebase ao seu projeto do iOS, caso ainda não tenha feito isso. Essa etapa não é necessária para empacotar o modelo.
1. Carregar o modelo
Configurar uma fonte de modelo local
Para agrupar o modelo e o app:
Copie o arquivo de modelo (geralmente terminado em
.tfliteou.lite) para seu projeto do Xcode, selecionandoCopy bundle resourcesao fazer isso. O arquivo de modelo será incluído no pacote de apps e estará disponível para o Kit de ML.Crie o objeto
LocalModel, especificando o caminho para o arquivo do modelo:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
Configurar uma fonte de modelo hospedada remotamente
Para usar o modelo hospedado remotamente, faça o download do arquivo modelo para o armazenamento local do dispositivo usando sua própria lógica de app e carregue-o como um modelo local. Recomendamos usar o Cloud Storage para Firebase para hospedar um modelo. Para detalhes da implementação, consulte o guia de migração do Firebase ML para o Cloud Storage.
Configurar o rotulador de imagens
Depois de configurar as origens do modelo, crie um objeto ImageLabeler usando uma delas.
As seguintes opções estão disponíveis:
| Opções | |
|---|---|
confidenceThreshold
|
Pontuação mínima de confiança dos rótulos detectados. Se não for definido, qualquer limite de classificador especificado pelos metadados do modelo será usado. Se o modelo não tiver metadados ou se eles não especificarem um limite de classificação, será usado um limite padrão de 0,0. |
maxResultCount
|
Número máximo de rótulos a serem retornados. Se não for definido, o valor padrão de 10 será usado. |
Se você tiver apenas um modelo agrupado localmente, basta criar um rotulador usando o 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];
Se você tiver um modelo hospedado remotamente, será necessário verificar se foi feito o download dele antes de executá-lo.
Embora você só precise confirmar isso antes de executar o rotulador, se tiver um modelo hospedado remotamente e um agrupado localmente, talvez seja interessante fazer essa verificação ao instanciar o ImageLabeler: crie um rotulador com o modelo remoto se ele tiver sido baixado e com o modelo local caso contrário.
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];
Se você tiver apenas um modelo hospedado remotamente, desative o recurso relacionado ao modelo (por exemplo, ocultando ou esmaecendo parte da IU) até confirmar que o download do modelo foi concluído.
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. Preparar a imagem de entrada
Crie um objeto VisionImage usando um UIImage ou um CMSampleBuffer.
Se você usa um UIImage, siga estas etapas:
- Crie um objeto
VisionImagecom oUIImage. Especifique a.orientationcorreta.Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Se você usa um CMSampleBuffer, siga estas etapas:
-
Especifique a orientação dos dados da imagem contidos no
CMSampleBuffer.Para ver a orientação da imagem:
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; } }
- Crie um objeto
VisionImageusando o objetoCMSampleBuffere a orientação: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. Executar o rotulador de imagens
Para rotular objetos em uma imagem, transmita o objeto image para o método process()
do ImageLabeler.
De forma assí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. Receber informações sobre entidades rotuladas
Se a operação de rotulagem de imagem for bem-sucedida, ela retornará uma matriz deImageLabel. Cada ImageLabel representa algo que foi rotulado na imagem. É possível receber a descrição de texto de cada rótulo (se disponível nos metadados do arquivo modelo do LiteRT), a pontuação de confiança e o índice. Por
exemplo:
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; }
Dicas para melhorar o desempenho em tempo real
Caso você queira rotular imagens em um aplicativo em tempo real, siga estas diretrizes para ter as melhores taxas de frames:
- Para processar frames de vídeo, use a API síncrona
results(in:)do detector. Chame esse método da funçãoAVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:)para receber resultados de forma síncrona do frame de vídeo especificado. Mantenha oAVCaptureVideoDataOutputalwaysDiscardsLateVideoFramescomotruepara limitar as chamadas ao detector. Se um novo frame de vídeo ficar disponível durante a execução do detector, ele será descartado. - Se você usar a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML. Em seguida, renderize a imagem e faça a sobreposição de uma só vez. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada quadro de entrada processado. Consulte updatePreviewOverlayViewWithLastFrame na amostra do guia de início rápido do Kit de ML para conferir um exemplo.