Rotular imagens com um modelo treinado pelo AutoML no iOS

Depois de treinar seu próprio modelo usando o AutoML Vision Edge, você pode usá-lo no seu app para rotular imagens.

Há duas maneiras de integrar modelos treinados pelo AutoML Vision Edge. Você pode agrupe o modelo copiando os arquivos dele em seu projeto Xcode ou pode fazer o download dele dinamicamente do Firebase.

Opções de empacotamento de modelos
Incluído no seu app
  • O modelo faz parte do pacote
  • O modelo estará disponível imediatamente, mesmo quando o dispositivo iOS estiver off-line
  • Não é necessário ter um projeto do Firebase
Hospedado com o Firebase

Faça um teste

Antes de começar

1. Inclua as bibliotecas do Kit de ML no seu Podfile:

Para agrupar um modelo e seu app:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    
Para fazer o download dinâmico de um modelo do Firebase, adicione LinkFirebase dependência:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    pod 'GoogleMLKit/LinkFirebase'
    
2: Depois de instalar ou atualizar os pods do seu projeto, abra o projeto Xcode usando o código .xcworkspace. O Kit de ML é compatível com o Xcode versão 13.2.1 ou superior. 3. Se você quiser fazer o download de um modelo, verifique se adicione o Firebase ao seu projeto do iOS, caso ainda não tenha feito isso. Isso não é necessário quando você agrupa o um modelo de machine learning.

1. Carregar o modelo

Configurar uma fonte de modelo local

Para agrupar o modelo e o app, faça o seguinte:

1: Extraia o modelo e os metadados dele do arquivo ZIP que você baixou. do Console do Firebase para uma pasta:
    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    
Os três arquivos precisam estar na mesma pasta. Recomendamos o uso dos arquivos à medida que foram baixados, sem modificação (incluindo os nomes dos arquivos).

2: Copie a pasta para seu projeto Xcode, selecionando Crie referências de pasta quando fizer isso. O arquivo do modelo e os metadados serão incluídos no pacote de apps e estarão disponíveis para o Kit de ML.

3: Crie o objeto AutoMLImageLabelerLocalModel, especificando o caminho para o arquivo de manifesto do modelo:

Swift

guard let manifestPath = Bundle.main.path(
    forResource: "manifest",
    ofType: "json",
    inDirectory: "your_model_directory"
) else { return }
let localModel = AutoMLImageLabelerLocalModel(manifestPath: manifestPath)

Objective-C

NSString *manifestPath =
    [NSBundle.mainBundle pathForResource:@"manifest"
                                  ofType:@"json"
                             inDirectory:@"your_model_directory"];
MLKAutoMLImageLabelerLocalModel *localModel =
    [[MLKAutoMLImageLabelerLocalModel alloc] initWithManifestPath:manifestPath];

Configurar uma fonte de modelo hospedada no Firebase

Para usar o modelo hospedado remotamente, crie um AutoMLImageLabelerRemoteModel especificando o nome que você atribuiu ao modelo quando o publicou:

Swift

let remoteModel = AutoMLImageLabelerRemoteModel(
    name: "your_remote_model"  // The name you assigned in
                               // the Firebase console.
)

Objective-C

MLKAutoMLImageLabelerRemoteModel *remoteModel =
    [[MLKAutoMLImageLabelerRemoteModel alloc]
        initWithName:@"your_remote_model"];  // The name you assigned in
                                             // the Firebase console.

Em seguida, inicie a tarefa de download do modelo, especificando as condições sob as quais do qual você quer permitir o download. Se o modelo não estiver no dispositivo ou se um modelo mais recente versão do modelo estiver disponível, a tarefa fará o download do arquivo do Firebase:

Swift

let downloadConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)

let downloadProgress = ModelManager.modelManager().download(
  remoteModel,
  conditions: downloadConditions
)

Objective-C

MLKModelDownloadConditions *downloadConditions =
    [[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];

NSProgress *downloadProgress =
    [[MLKModelManager modelManager] downloadModel:remoteModel
                                       conditions:downloadConditions];

Muitos apps iniciam a tarefa de download no código de inicialização, mas você pode fazer isso a qualquer momento antes de precisar usar o modelo.

Criar um rotulador de imagens a partir do modelo

Depois de configurar as origens do modelo, crie um objeto ImageLabeler usando uma um deles.

Se você tiver apenas um modelo agrupado localmente, basta criar um rotulador AutoMLImageLabelerLocalModel e configurar a pontuação de confiança o limite que você quer exigir. Consulte Avaliar seu modo:

Swift

let options = AutoMLImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Firebase console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKAutoMLImageLabelerOptions *options =
    [[MLKAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0);  // Evaluate your model in the Firebase console
                                       // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

Se você tiver um modelo hospedado remotamente, será necessário verificar se ele foi antes de executá-lo. É possível verificar o status do download do modelo tarefa usando o método isModelDownloaded(remoteModel:) do gerenciador de modelos.

Embora isso só precise ser confirmado antes de executar o rotulador, se você mas tem um modelo hospedado remotamente e um modelo agrupado localmente, isso pode tornar para fazer essa verificação ao instanciar o ImageLabeler: crie um rotulador a partir do modelo remoto, caso tenha feito o download, e do modelo local caso contrário.

Swift

var options: AutoMLImageLabelerOptions!
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = AutoMLImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = AutoMLImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Firebase console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKAutoMLImageLabelerOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKAutoMLImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = @(0.0);  // Evaluate your model in the Firebase console
                                       // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

Se você tiver apenas um modelo hospedado remotamente, desative o recurso da interface, por exemplo, esmaecer ou ocultar parte da interface, até confirme se o download do modelo foi concluído.

É possível receber o status de download do modelo anexando observadores ao arquivo Central de Notificações. Use uma referência fraca a self no observador. já que os downloads podem levar algum tempo e o objeto de origem pode ser liberado no momento em que o download é concluído. Exemplo:

Swift

NotificationCenter.default.addObserver(
    forName: .mlkitModelDownloadDidSucceed,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel,
        model.name == "your_remote_model"
        else { return }
    // The model was downloaded and is available on the device
}

NotificationCenter.default.addObserver(
    forName: .mlkitModelDownloadDidFail,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel
        else { return }
    let error = userInfo[ModelDownloadUserInfoKey.error.rawValue]
    // ...
}

Objective-C

__weak typeof(self) weakSelf = self;

[NSNotificationCenter.defaultCenter
    addObserverForName:MLKModelDownloadDidSucceedNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              MLKRemoteModel *model = note.userInfo[MLKModelDownloadUserInfoKeyRemoteModel];
              if ([model.name isEqualToString:@"your_remote_model"]) {
                // The model was downloaded and is available on the device
              }
            }];

[NSNotificationCenter.defaultCenter
    addObserverForName:MLKModelDownloadDidFailNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              NSError *error = note.userInfo[MLKModelDownloadUserInfoKeyError];
            }];

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 VisionImage com o UIImage. Especifique o .orientation correto.

    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 saber qual é 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 VisionImage usando o Objeto e orientação CMSampleBuffer:

    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

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 (labels.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. Conseguir informações sobre os objetos rotulados

Se a operação de rotulagem de imagem for bem-sucedida, ela retornará uma matriz de ImageLabel Cada ImageLabel representa algo que foi rotuladas na imagem. Você pode obter a descrição de texto de cada rótulo (se disponível no metadados do arquivo de modelo do TensorFlow Lite), pontuação de confiança e índice. 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

Se você quiser rotular imagens em um aplicativo em tempo real, siga estas instruções diretrizes para obter as melhores taxas de quadros:

  • Para processar frames de vídeo, use a API síncrona results(in:) do detector. Ligação esse método da AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:) para receber resultados do vídeo fornecido de forma síncrona frame. Manter de AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames como true para limitar as chamadas ao detector. Se um novo frame de vídeo ficar disponível enquanto o detector estiver em execução, ele será descartado.
  • Se você usar a saída do detector para sobrepor elementos gráficos a imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada processado. Veja a classe updatePreviewOverlayViewWithLastFrame na amostra do guia de início rápido do Kit de ML.