Detecta objetos y hazles seguimiento con ML Kit en iOS

Puedes usar ML Kit para detectar objetos en fotogramas de video sucesivos y hacerles seguimiento.

Cuando pasas una imagen al Kit de AA, este detecta hasta cinco objetos en la imagen, junto con la posición de cada uno de ellos. Cuando se detectan objetos en transmisiones de video por Internet, cada objeto tiene un ID único que puedes usar para seguirlo de fotograma a fotograma. De manera opcional, también puedes habilitar la clasificación aproximada de objetos, que etiqueta los objetos con descripciones de categorías amplias.

Probar

Antes de comenzar

  1. Incluye los siguientes pods del ML Kit en tu Podfile:
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. Después de instalar o actualizar los Pods de tu proyecto, abre el proyecto de Xcode con su .xcworkspace. El ML Kit es compatible con Xcode 12.4 o versiones posteriores.

1. Configura el detector de objetos

Para detectar objetos y hacerles seguimiento, primero debes crear una instancia de ObjectDetector y, de manera opcional, especifica cualquier configuración del detector que desees cambiar de la configuración predeterminada.

  1. Configura el detector de objetos para tu caso de uso con un objeto ObjectDetectorOptions. Puedes cambiar las siguientes opciones de configuración:

    Configuración del detector de objetos
    Modo de detección .stream (predeterminado) | .singleImage

    En el modo de transmisión (predeterminado), el detector de objetos se ejecuta con una latencia muy baja, pero puede generar resultados incompletos (como cuadros de límite o categorías no especificados) en las primeras invocaciones del detector. Además, en el modo de transmisión, el detector asigna ID de seguimiento a los objetos, que puedes usar para hacer seguimiento de objetos en los marcos. Usa este modo cuando quieras hacer seguimiento de objetos o si la latencia baja es importante, como cuando procesas transmisiones de video por Internet en tiempo real.

    En el modo de imagen única, el detector de objetos muestra el resultado una vez que se determina el cuadro de límite del objeto. Si también habilitas la clasificación, se muestra el resultado después de que el cuadro de límite y la etiqueta de categoría estén disponibles. En consecuencia, la latencia de detección es potencialmente más alta. Además, en el modo de imagen única, no se asignan ID de seguimiento. Usa este modo si la latencia no es crítica y no quieres lidiar con resultados parciales.

    Detecta varios objetos y hazles seguimiento false (predeterminado) | true

    Ya sea para detectar y hacer seguimiento de hasta cinco objetos o solo al más prominente (predeterminado).

    Clasificar objetos false (predeterminado) | true

    Indica si se deben clasificar o no los objetos detectados en categorías generales. Cuando está habilitado, el detector de objetos clasifica los objetos en las siguientes categorías: artículos de moda, comida, artículos para el hogar, lugares y plantas.

    La API de detección y seguimiento de objetos está optimizada para los siguientes dos casos prácticos principales:

    • Detección y seguimiento en vivo del objeto más prominente en el visor de la cámara
    • La detección de múltiples objetos en una imagen estática.

    Si deseas configurar la API para estos casos de uso, haz lo siguiente:

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. Obtén una instancia 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. Prepara la imagen de entrada

Para detectar objetos y hacerles seguimiento, haz lo siguiente con cada imagen o fotograma de video. Si habilitaste el modo de transmisión, debes crear objetos VisionImage a partir de CMSampleBuffer.

Crea un objeto VisionImage mediante una UIImage o CMSampleBuffer.

Si usas un UIImage, sigue estos pasos:

  • Crea un objeto VisionImage con la UIImage. Asegúrate de especificar el .orientation correcto.

    Swift

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

    Objective‑C

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

Si usas un CMSampleBuffer, sigue estos pasos:

  • Especifica la orientación de los datos de imagen que contiene CMSampleBuffer.

    Para obtener la orientación de la imagen, haz lo siguiente:

    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;
      }
    }
          
  • Crea un objeto VisionImage con el objeto CMSampleBuffer y la orientación:

    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. Procesa la imagen

Pasa el VisionImage a uno de los métodos de procesamiento de imágenes del detector de objetos. Puedes usar el método asíncrono process(image:) o el método síncrono results().

Para detectar objetos de forma asíncrona, haz lo siguiente:

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, haz lo siguiente:

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. Obtén información sobre los objetos detectados

Si la llamada al procesador de imágenes se ejecuta correctamente, este pasa una lista de Object al controlador de finalización o la muestra, dependiendo de si llamaste al método asíncrono o síncrono.

Cada Object contiene las siguientes propiedades:

frame Es un CGRect que indica la posición del objeto en la imagen.
trackingID Un número entero que identifica el objeto en las imágenes o "nil" en el modo de una sola imagen.
labels Un arreglo de etiquetas que describen el objeto que muestra el detector. La propiedad estará vacía si la opción del detector shouldEnableClassification se configura 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];
    ...
  }
}

Cómo mejorar la usabilidad y el rendimiento

Para obtener la mejor experiencia del usuario, sigue estos lineamientos en tu app:

  • La detección correcta de objetos depende de la complejidad visual del objeto. Para poder detectarse, es posible que los objetos con una pequeña cantidad de características visuales deban ocupar una parte más grande de la imagen. Debes proporcionar a los usuarios orientación sobre cómo capturar entradas que funcionen bien con el tipo de objetos que deseas detectar.
  • Cuando usas la clasificación, si deseas detectar objetos que no se incluyen de forma clara en las categorías admitidas, implementa un manejo especial para objetos desconocidos.

Además, consulta la colección de patrones para las funciones con tecnología de aprendizaje automático de Material Design.

Cuando uses el modo de transmisión en una aplicación en tiempo real, sigue estos lineamientos para lograr la mejor velocidad de fotogramas:

  • No uses la detección de varios objetos en el modo de transmisión, ya que la mayoría de los dispositivos no podrán producir fotogramas adecuados.
  • Inhabilita la clasificación si no la necesitas.
  • Para procesar fotogramas de video, usa la API síncrona results(in:) del detector. Llama a este método desde la función captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para obtener resultados de un fotograma de video determinado de manera síncrona. Mantén el alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput como true para limitar las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, se descartará.
  • Si usas la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado del ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, renderizas en la superficie de visualización solo una vez por cada fotograma de entrada procesado. Consulta updatePreviewOverlayViewWithLastFrame en la muestra de inicio rápido del Kit de AA para ver un ejemplo.