Detecte e rastreie objetos com kit de ML no iOS

Você pode usar o ML Kit para detectar e rastrear objetos em quadros de vídeo.

Quando você passa imagens do ML Kit, o ML Kit retorna, para cada imagem, uma lista de até cinco objetos detectados e sua posição na imagem. Ao detectar objetos em fluxos de vídeo, cada objeto possui um ID que você pode usar para rastreá-lo nas imagens. Opcionalmente, você também pode ativar a classificação aproximada de objetos, que rotula os objetos com descrições amplas de categorias.

Antes de você começar

  1. Se você ainda não adicionou o Firebase ao seu aplicativo, faça isso seguindo as etapas do guia de primeiros passos .
  2. Inclua as bibliotecas do ML Kit em seu Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    Depois de instalar ou atualizar os pods do seu projeto, certifique-se de abrir seu projeto Xcode usando seu .xcworkspace .
  3. No seu aplicativo, importe o Firebase:

    Rápido

    import Firebase

    Objetivo-C

    @import Firebase;

1. Configure o detector de objetos

Para começar a detectar e rastrear objetos, primeiro crie uma instância de VisionObjectDetector , especificando opcionalmente quaisquer configurações de detector que você deseja alterar do padrão.

  1. Configure o detector de objetos para seu caso de uso com um objeto VisionObjectDetectorOptions . Você pode alterar as seguintes configurações:

    Configurações do detector de objetos
    Modo de detecção .stream (padrão) | .singleImage

    No modo de fluxo (padrão), o detector de objetos é executado com latência muito baixa, mas pode produzir resultados incompletos (como caixas delimitadoras ou categorias não especificadas) nas primeiras invocações do detector. Além disso, no modo de fluxo, o detector atribui IDs de rastreamento a objetos, que você pode usar para rastrear objetos entre quadros. Use este modo quando quiser rastrear objetos ou quando a baixa latência for importante, como ao processar fluxos de vídeo em tempo real.

    No modo de imagem única, o detector de objetos aguarda até que a caixa delimitadora do objeto detectado e a categoria (se você tiver habilitado a classificação) estejam disponíveis antes de retornar um resultado. Como consequência, a latência de detecção é potencialmente maior. Além disso, no modo de imagem única, os IDs de rastreamento não são atribuídos. Use este modo se a latência não for crítica e você não quiser lidar com resultados parciais.

    Detecte e rastreie vários objetos false (padrão) | true

    Seja para detectar e rastrear até cinco objetos ou apenas o objeto mais proeminente (padrão).

    Classificar objetos false (padrão) | true

    Se deve ou não classificar os objetos detectados em categorias grosseiras. Quando ativado, o detector de objetos classifica os objetos nas seguintes categorias: produtos de moda, alimentos, produtos domésticos, lugares, plantas e desconhecidos.

    A API de detecção e rastreamento de objetos é otimizada para estes dois casos de uso principais:

    • Detecção e rastreamento ao vivo do objeto mais proeminente no visor da câmera
    • Detecção de vários objetos em uma imagem estática

    Para configurar a API para estes casos de uso:

    Rápido

    // Live detection and tracking
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .stream
    options.shouldEnableMultipleObjects = false
    options.shouldEnableClassification = true  // Optional
    
    // Multiple object detection in static images
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .singleImage
    options.shouldEnableMultipleObjects = true
    options.shouldEnableClassification = true  // Optional
    

    Objetivo-C

    // Live detection and tracking
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeStream;
    options.shouldEnableMultipleObjects = NO;
    options.shouldEnableClassification = YES;  // Optional
    
    // Multiple object detection in static images
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeSingleImage;
    options.shouldEnableMultipleObjects = YES;
    options.shouldEnableClassification = YES;  // Optional
    
  2. Obtenha uma instância do FirebaseVisionObjectDetector :

    Rápido

    let objectDetector = Vision.vision().objectDetector()
    
    // Or, to change the default settings:
    let objectDetector = Vision.vision().objectDetector(options: options)
    

    Objetivo-C

    FIRVisionObjectDetector *objectDetector = [[FIRVision vision] objectDetector];
    
    // Or, to change the default settings:
    FIRVisionObjectDetector *objectDetector = [[FIRVision vision] objectDetectorWithOptions:options];
    

2. Execute o detector de objetos

Para detectar e rastrear objetos, faça o seguinte para cada imagem ou quadro de vídeo. Se você ativou o modo de fluxo, deverá criar objetos VisionImage a partir de CMSampleBufferRef s.

  1. Crie um objeto VisionImage usando UIImage ou CMSampleBufferRef .

    Para usar uma UIImage :

    1. Se necessário, gire a imagem para que sua propriedade imageOrientation seja .up .
    2. Crie um objeto VisionImage usando o UIImage girado corretamente. Não especifique nenhum metadado de rotação — o valor padrão, .topLeft , deve ser usado.

      Rápido

      let image = VisionImage(image: uiImage)

      Objetivo-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    Para usar um CMSampleBufferRef :

    1. Crie um objeto VisionImageMetadata que 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
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      Objetivo-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      Em seguida, crie o objeto de metadados:

      Rápido

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      Objetivo-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. Crie um objeto VisionImage usando o objeto CMSampleBufferRef e os metadados de rotação:

      Rápido

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      Objetivo-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. Passe o VisionImage para um dos métodos de processamento de imagem do detector de objetos. Você pode usar o método assíncrono process(image:) ou o método síncrono results() .

    Para detectar objetos de forma assíncrona:

    Rápido

    objectDetector.process(image) { detectedObjects, error in
      guard error == nil else {
        // Error.
        return
      }
      guard let detectedObjects = detectedObjects, !detectedObjects.isEmpty else {
        // No objects detected.
        return
      }
    
      // Success. Get object info here.
      // ...
    }
    

    Objetivo-C

    [objectDetector processImage:image
                      completion:^(NSArray<FIRVisionObject *> * _Nullable objects,
                                   NSError * _Nullable error) {
                        if (error == nil) {
                          return;
                        }
                        if (objects == nil | objects.count == 0) {
                          // No objects detected.
                          return;
                        }
    
                        // Success. Get object info here.
                        // ...
                      }];
    

    Para detectar objetos de forma síncrona:

    Rápido

    var results: [VisionObject]? = nil
    do {
      results = try objectDetector.results(in: image)
    } catch let error {
      print("Failed to detect object with error: \(error.localizedDescription).")
      return
    }
    guard let detectedObjects = results, !detectedObjects.isEmpty else {
      print("Object detector returned no results.")
      return
    }
    
    // ...
    

    Objetivo-C

    NSError *error;
    NSArray<FIRVisionObject *> *objects = [objectDetector resultsInImage:image
                                                                   error:&error];
    if (error == nil) {
      return;
    }
    if (objects == nil | objects.count == 0) {
      // No objects detected.
      return;
    }
    
    // Success. Get object info here.
    // ...
    
  3. Se a chamada para o processador de imagem for bem-sucedida, ele passará uma lista de VisionObject s para o manipulador de conclusão ou retornará a lista, dependendo se você chamou o método assíncrono ou síncrono.

    Cada VisionObject contém as seguintes propriedades:

    frame Um CGRect que indica a posição do objeto na imagem.
    trackingID Um inteiro que identifica o objeto nas imagens. Nulo no modo de imagem única.
    classificationCategory A categoria grosseira do objeto. Se o detector de objetos não tiver a classificação habilitada, será sempre .unknown .
    confidence O valor de confiança da classificação do objeto. Se o detector de objetos não tiver a classificação habilitada ou o objeto for classificado como desconhecido, isso será nil .

    Rápido

    // detectedObjects contains one item if multiple object detection wasn't enabled.
    for obj in detectedObjects {
      let bounds = obj.frame
      let id = obj.trackingID
    
      // If classification was enabled:
      let category = obj.classificationCategory
      let confidence = obj.confidence
    }
    

    Objetivo-C

    // The list of detected objects contains one item if multiple
    // object detection wasn't enabled.
    for (FIRVisionObject *obj in objects) {
      CGRect bounds = obj.frame;
      if (obj.trackingID) {
        NSInteger id = obj.trackingID.integerValue;
      }
    
      // If classification was enabled:
      FIRVisionObjectCategory category = obj.classificationCategory;
      float confidence = obj.confidence.floatValue;
    }
    

Melhorando a usabilidade e o desempenho

Para obter a melhor experiência do usuário, siga estas diretrizes em seu aplicativo:

  • A detecção bem-sucedida de objetos depende da complexidade visual do objeto. Objetos com um pequeno número de características visuais podem precisar ocupar uma parte maior da imagem para serem detectados. Você deve fornecer aos usuários orientações sobre como capturar entradas que funcionem bem com o tipo de objetos que você deseja detectar.
  • Ao usar a classificação, se você quiser detectar objetos que não se enquadram perfeitamente nas categorias suportadas, implemente um tratamento especial para objetos desconhecidos.

Além disso, confira o [aplicativo de demonstração do ML Kit Material Design][showcase-link]{: .external } e a coleção de recursos do Material Design Patterns para aprendizado de máquina .

Ao usar o modo de streaming em um aplicativo em tempo real, siga estas diretrizes para obter as melhores taxas de quadros:

  • Não use a detecção de múltiplos objetos no modo de streaming, pois a maioria dos dispositivos não será capaz de produzir taxas de quadros adequadas.

  • Desative a classificação se não precisar dela.

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