Detectar e rastrear objetos com o Kit de ML no iOS

É possível usar o Kit de ML para detectar e rastrear objetos em frames de vídeo sucessivos.

Quando você transmite uma imagem para o Kit de ML, ele detecta até cinco objetos junto com a posição de cada objeto na imagem. Ao detectar objetos em streams de vídeo, cada objeto tem um ID exclusivo que pode ser usado para rastreá-lo de frame a frame. Também é possível ativar a classificação aproximada de objetos, que rotula objetos com descrições de categorias amplas.

Testar

Antes de começar

  1. Inclua os seguintes pods do Kit de ML no seu Podfile:
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. Depois de instalar ou atualizar os pods do projeto, abra o projeto Xcode usando o .xcworkspace. O Kit de ML é compatível com a versão 12.4 ou mais recente do Xcode.

1. Configurar o detector de objetos

Para detectar e rastrear objetos, primeiro crie uma instância de ObjectDetector e, se quiser, especifique as configurações do detector que você quer alterar do padrão.

  1. Configure o detector de objetos para seu caso de uso com um objeto ObjectDetectorOptions. É possível alterar as seguintes configurações:

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

    No modo de stream (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 stream, o detector atribui IDs de rastreamento a objetos, que podem ser usados para rastrear objetos em frames. Use esse modo quando quiser rastrear objetos ou quando a baixa latência for importante, como ao processar streams de vídeo em tempo real.

    No modo de imagem única, o detector de objetos retorna o resultado depois que a caixa delimitadora do objeto é determinada. Se você também ativar a classificação, ela retornará o resultado depois que a caixa delimitadora e o rótulo da categoria estiverem disponíveis. Como consequência, a latência de detecção é potencialmente maior. Além disso, no modo de imagem única, os IDs de acompanhamento não são atribuídos. Use esse modo se a latência não for crítica e você não quiser lidar com resultados parciais.

    Detectar e rastrear vários objetos false (padrão) | true

    Define se é preciso detectar e rastrear até cinco objetos ou apenas o objeto mais proeminente (padrão).

    Classificar objetos false (padrão) | true

    Se os objetos detectados serão ou não classificados em categorias gerais. Quando ativado, o detector de objetos os classifica nas seguintes categorias: artigos de moda, alimentos, artigos domésticos, locais e plantas.

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

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

    Se quiser configurar a API para esses casos de uso:

Swift

// Live detection and tracking
let options = ObjectDetectorOptions()
options.shouldEnableClassification = true

// Multiple object detection in static images
let options = ObjectDetectorOptions()
options.detectorMode = .singleImage
options.shouldEnableMultipleObjects = true
options.shouldEnableClassification = true

Objective-C

// Live detection and tracking
MLKObjectDetectorOptions *options = [[MLKObjectDetectorOptions alloc] init];
options.shouldEnableClassification = YES;

// Multiple object detection in static images
MLKObjectDetectorOptions *options = [[MLKOptions alloc] init];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableMultipleObjects = YES;
options.shouldEnableClassification = YES;
  1. Consiga uma instância de ObjectDetector:

Swift

let objectDetector = ObjectDetector.objectDetector()

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

Objective-C

MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetector];

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

2. Preparar a imagem de entrada

Para detectar e rastrear objetos, faça o seguinte para cada imagem ou frame de vídeo. Se você ativou o modo de stream, é necessário criar objetos VisionImage a partir de CMSampleBuffers.

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 em 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 VisionImage usando o objeto CMSampleBuffer e 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. Processar a imagem

Transmita VisionImage para um dos métodos de processamento de imagem do detector de objetos. É possível usar o método process(image:) assíncrono ou o método síncrono results().

Para detectar objetos de forma assíncrona:

Swift

objectDetector.process(image) { objects, error in
  guard error == nil else {
    // Error.
    return
  }
  guard !objects.isEmpty else {
    // No objects detected.
    return
  }

  // Success. Get object info here.
  // ...
}

Objective-C

[objectDetector processImage:image
                  completion:^(NSArray * _Nullable objects,
                               NSError * _Nullable error) {
                    if (error == nil) {
                      return;
                    }
                    if (objects.count == 0) {
                      // No objects detected.
                      return;
                    }

                    // Success. Get object info here.
                  }];

Para detectar objetos de forma síncrona:

Swift

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

// Success. Get object info here.

Objective-C

NSError *error;
NSArray *objects = [objectDetector resultsInImage:image error:&error];
if (error == nil) {
  return;
}
if (objects.count == 0) {
  // No objects detected.
  return;
}

// Success. Get object info here.

4. Receber informações sobre os objetos detectados

Se a chamada para o processador de imagem for bem-sucedida, ela transmite uma lista de Objects para o gerenciador de conclusão ou retorna a lista, dependendo se você chamou o método assíncrono ou síncrono.

Cada Object contém as seguintes propriedades:

frame Um CGRect que indica a posição do objeto na imagem.
trackingID Um número inteiro que identifica o objeto em imagens ou "nil" no modo de imagem única.
labels Uma matriz de rótulos que descrevem o objeto retornado pelo detector. A propriedade ficará vazia se a opção de detector shouldEnableClassification estiver definida como false.

Swift

// objects contains one item if multiple object detection wasn't enabled.
for object in objects {
  let frame = object.frame
  let trackingID = object.trackingID

  // If classification was enabled:
  let description = object.labels.enumerated().map { (index, label) in
    "Label \(index): \(label.text), \(label.confidence)"
    }.joined(separator:"\n")

}

Objective-C

// The list of detected objects contains one item if multiple
// object detection wasn't enabled.
for (MLKObject *object in objects) {
  CGRect frame = object.frame;
  NSNumber *trackingID = object.trackingID;
  for (MLKObjectLabel *label in object.labels) {
    NSString *labelString = [NSString stringWithFormat: @"%@, %f, %lu",
      label.text, label.confidence, (unsigned long)label.index];
    ...
  }
}

Como melhorar a usabilidade e o desempenho

Para ter a melhor experiência do usuário, siga estas diretrizes no seu app:

  • A detecção bem-sucedida de objetos depende da complexidade visual do objeto. Para serem detectados, objetos com um pequeno número de recursos visuais podem precisar ocupar uma parte maior da imagem. Forneça aos usuários orientações sobre como capturar entradas que funcionem bem com o tipo de objeto que você quer detectar.
  • Ao usar a classificação, se você quiser detectar objetos que não se enquadrem nas categorias compatíveis, implemente o tratamento especial para objetos desconhecidos.

Além disso, confira a coleção de Padrões para recursos com tecnologia de machine learning do Material Design.

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

  • Não use a detecção de vários objetos no modo de streaming, porque a maioria dos dispositivos não será capaz de produzir taxas de frames adequadas.
  • Desative a classificação se ela não for necessária.
  • Para processar frames de vídeo, use a API síncrona results(in:) do detector. Chame esse método na função captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para receber resultados de forma síncrona do frame de vídeo especificado. Mantenha o alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput 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 na imagem de entrada, primeiro acesse 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 a superfície de exibição apenas uma vez para cada frame de entrada processado. Consulte updatePreviewOverlayViewWithLastFrame no exemplo do guia de início rápido do Kit de ML.