Rotule imagens com um modelo treinado em AutoML em plataformas Apple

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

Há duas maneiras de integrar modelos treinados no AutoML Vision Edge. Você pode agrupar o modelo copiando os arquivos do modelo em seu projeto Xcode ou pode baixá-lo dinamicamente do Firebase.

Opções de agrupamento de modelos
Agrupado em seu aplicativo
  • O modelo faz parte do pacote
  • O modelo está disponível imediatamente, mesmo quando o dispositivo Apple está offline
  • Não há necessidade de um projeto do Firebase
Hospedado com Firebase
  • Hospede o modelo fazendo upload para o Firebase Machine Learning
  • Reduz o tamanho do pacote de aplicativos
  • O modelo é baixado sob demanda
  • Envie atualizações de modelo sem republicar seu aplicativo
  • Testes A/B fáceis com o Firebase Remote Config
  • Requer um projeto do Firebase

Antes de você começar

  1. Inclua as bibliotecas do ML Kit em seu Podfile:

    Para agrupar um modelo com seu aplicativo:

    pod 'GoogleMLKit/ImageLabelingCustom'
    

    Para baixar dinamicamente um modelo do Firebase, adicione a dependência do LinkFirebase :

    pod 'GoogleMLKit/ImageLabelingCustom'
    pod 'GoogleMLKit/LinkFirebase'
    
  2. Depois de instalar ou atualizar os Pods do seu projeto, abra seu projeto Xcode usando seu .xcworkspace . O ML Kit é compatível com o Xcode versão 12.2 ou superior.

  3. Se você quiser fazer o download de um modelo , certifique-se de adicionar o Firebase ao seu projeto Android , caso ainda não o tenha feito. Isso não é necessário quando você agrupa o modelo.

1. Carregue o modelo

Configurar uma fonte de modelo local

Para agrupar o modelo com seu aplicativo:

  1. Extraia o modelo e seus metadados do arquivo zip que você baixou do Firebase console em uma pasta:

    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    

    Todos os três arquivos devem estar na mesma pasta. Recomendamos que você use os arquivos conforme os baixou, sem modificação (incluindo os nomes dos arquivos).

  2. Copie a pasta para o seu projeto Xcode, tendo o cuidado de selecionar Criar referências de pasta ao fazê-lo. O arquivo de modelo e os metadados serão incluídos no pacote de aplicativos e estarão disponíveis para o ML Kit.

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

    Rápido

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

    Objetivo-C

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

Configurar uma origem de modelo hospedada pelo Firebase

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

Rápido

// Initialize the model source with the name you assigned in
// the Firebase console.
let remoteModelSource = FirebaseModelSource(name: "your_remote_model")
let remoteModel = CustomRemoteModel(remoteModelSource: remoteModelSource)

Objetivo-C

// Initialize the model source with the name you assigned in
// the Firebase console.
MLKFirebaseModelSource *firebaseModelSource =
    [[MLKFirebaseModelSource alloc] initWithName:@"your_remote_model"];
MLKCustomRemoteModel *remoteModel =
    [[MLKCustomRemoteModel alloc] initWithRemoteModelSource:firebaseModelSource];

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

Rápido

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

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

Objetivo-C

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

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

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

Crie um rotulador de imagem a partir do seu modelo

Depois de configurar suas fontes de modelo, crie um objeto ImageLabeler a partir de uma delas.

Se você tiver apenas um modelo empacotado localmente, basta criar um rotulador de seu objeto LocalModel e configurar o limite de pontuação de confiança que deseja exigir (consulte Avaliar seu modelo ):

Rápido

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

Objetivo-C

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

Se você tiver um modelo hospedado remotamente, precisará verificar se foi baixado antes de executá-lo. Você pode verificar o status da tarefa de download do modelo usando o método isModelDownloaded(remoteModel:) do gerenciador de modelo.

Embora você só precise confirmar isso antes de executar o rotulador, se você tiver um modelo hospedado remotamente e um modelo empacotado localmente, pode fazer sentido realizar essa verificação ao instanciar o ImageLabeler : crie um rotulador do modelo remoto se for foi baixado e, caso contrário, do modelo local.

Rápido

var options: CustomImageLabelerOptions
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = CustomImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = CustomImageLabelerOptions(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)

Objetivo-C

MLKCustomImageLabelerOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKCustomImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = @(0.0f);  // 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, deverá desabilitar a funcionalidade relacionada ao modelo — por exemplo, acinzentado ou ocultar parte de sua interface do usuário — até confirmar que o modelo foi baixado.

Você pode obter o status de download do modelo anexando observadores ao Centro de Notificação padrão. Certifique-se de usar uma referência fraca para self no bloco do observador, pois os downloads podem levar algum tempo e o objeto de origem pode ser liberado quando o download terminar. Por exemplo:

Rápido

NotificationCenter.default.addObserver(
    forName: .mlkitMLModelDownloadDidSucceed,
    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: .mlkitMLModelDownloadDidFail,
    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]
    // ...
}

Objetivo-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. Prepare a imagem de entrada

Crie um objeto VisionImage usando um UIImage ou um CMSampleBufferRef .

Se você usar um UIImage , siga estas etapas:

  • Crie um objeto VisionImage com o UIImage . Certifique-se de especificar a .orientation correta.

    Rápido

    let image = VisionImage(image: uiImage)
    visionImage.orientation = image.imageOrientation

    Objetivo-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Se você usar um CMSampleBufferRef , siga estas etapas:

  • Especifique a orientação dos dados de imagem contidos no buffer CMSampleBufferRef .

    Para obter a orientação da imagem:

    Rápido

    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
      }
    }
          

    Objetivo-C

    - (UIImageOrientation)
      imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                             cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                          : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                          : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                          : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                          : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • Crie um objeto VisionImage usando o objeto e a orientação CMSampleBufferRef :

    Rápido

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Objetivo-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. Execute o rotulador de imagem

De forma assíncrona:

Rápido

imageLabeler.process(image) { labels, error in
    guard error == nil, let labels = labels, !labels.isEmpty else {
        // Handle the error.
        return
    }
    // Show results.
}

Objetivo-C

[imageLabeler
    processImage:image
      completion:^(NSArray<MLKImageLabel *> *_Nullable labels,
                   NSError *_Nullable error) {
        if (label.count == 0) {
            // Handle the error.
            return;
        }
        // Show results.
     }];

Sincronicamente:

Rápido

var labels: [ImageLabel]
do {
    labels = try imageLabeler.results(in: image)
} catch let error {
    // Handle the error.
    return
}
// Show results.

Objetivo-C

NSError *error;
NSArray<MLKImageLabel *> *labels =
    [imageLabeler resultsInImage:image error:&error];
// Show results or handle the error.

4. Obtenha informações sobre objetos rotulados

Se a operação de rotulagem da imagem for bem-sucedida, ela retornará uma matriz de ImageLabel . Cada ImageLabel representa algo que foi rotulado na imagem. Você pode obter a descrição de texto de cada rótulo (se disponível nos metadados do arquivo de modelo do TensorFlow Lite), pontuação de confiança e índice. Por exemplo:

Rápido

for label in labels {
  let labelText = label.text
  let confidence = label.confidence
  let index = label.index
}

Objetivo-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ê deseja rotular imagens em um aplicativo em tempo real, siga estas diretrizes para obter as melhores taxas de quadros:

  • Acelere as chamadas para o detector. Se um novo quadro de vídeo ficar disponível enquanto o detector estiver em execução, descarte o quadro.
  • Se você estiver usando a saída do detector para sobrepor gráficos na imagem de entrada, primeiro obtenha o resultado e depois renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza para a superfície de exibição apenas uma vez para cada quadro de entrada. Consulte as classes previewOverlayView e FIRDetectionOverlayView no aplicativo de amostra de demonstração para obter um exemplo.