Detecta objetos y hazles seguimiento con el Kit de AA en iOS

Puedes usar el Kit de AA para detectar objetos y hacerles seguimiento en todos los fotogramas de video.

Cuando pasas imágenes del Kit de AA, este muestra, para cada imagen, una lista de hasta cinco objetos detectados y su posición en la imagen. Cuando detectas objetos en transmisiones de video por Internet, cada uno de ellos tiene un ID que puedes usar para seguirlo en las imágenes. De manera opcional, puedes habilitar la clasificación ordinaria de objetos, que etiqueta los objetos con descripciones de categorías amplias.

Antes de comenzar

  1. Si aún no agregaste Firebase a tu app, sigue los pasos en la guía de introducción para hacerlo.
  2. Incluye las bibliotecas del ML Kit en tu Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    Después de instalar o actualizar los Pods de tu proyecto, asegúrate de abrir el proyecto de Xcode con su .xcworkspace.
  3. En tu app, importa Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

1. Configura el detector de objetos

Para comenzar a detectar objetos y hacerles seguimiento, primero debes crear una instancia de VisionObjectDetector que, de manera opcional, especifique cualquier configuración del detector que quieras cambiar de la configuración predeterminada.

  1. Configura el detector de objetos para tu caso práctico con un objeto VisionObjectDetectorOptions. 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 baja latencia, pero podría generar resultados incompletos (como cuadros de límite o categoría 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 a través de los fotogramas. Usa este modo cuando quieras hacer seguimiento de objetos o cuando la baja latencia sea importante, como cuando procesas transmisiones de video en Internet en tiempo real.

    En el modo de imagen individual, el detector de objetos espera hasta que el cuadro de límite y (si habilitaste la clasificación) la categoría de un objeto detectado estén disponibles antes de mostrar un resultado. En consecuencia, la latencia de detección es potencialmente más alta. Además, en el modo de imagen individual, no se asignan ID de seguimiento. Usa este modo si la latencia no es importante 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).

    Clasifica objetos false (predeterminado) | true

    Ya sea para clasificar o no objetos detectados en categorías ordinarias. Cuando se encuentra habilitado, el detector de objetos clasifica los objetos en las siguientes categorías: productos de moda, comida, productos para el hogar, lugares, plantas y desconocidos.

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

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

    Con el fin de configurar la API para estos casos prácticos, sigue estos pasos:

    Swift

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

    Objective-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. Obtén una instancia de FirebaseVisionObjectDetector:

    Swift

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

    Objective-C

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

2. Ejecuta el detector de objetos

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 varias CMSampleBufferRef.

  1. Crea un objeto VisionImage mediante una UIImage o CMSampleBufferRef.

    Para usar una UIImage, debes hacer lo siguiente:

    1. Si es necesario, rota la imagen para que la propiedad imageOrientation sea .up.
    2. Crea un objeto VisionImage mediante una UIImage que se haya rotado adecuadamente. No especifiques los metadatos de rotación. Se debe usar el valor predeterminado .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Para usar una CMSampleBufferRef, debes hacer lo siguiente:

    1. Crea un objeto VisionImageMetadata que especifique la orientación de los datos de la imagen contenidos en el búfer CMSampleBufferRef.

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

      Swift

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

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

      Luego crea el objeto de metadatos de esta manera:

      Swift

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

      Objective-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. Crea un objeto VisionImage a través del objeto CMSampleBufferRef y los metadatos de rotación:

      Swift

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

      Objective-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. Pasa la 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().

    Haz lo siguiente para detectar objetos de manera asíncrona:

    Swift

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

    Objective-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.
                        // ...
                      }];
    

    Haz lo siguiente para detectar objetos de manera síncrona:

    Swift

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

    Objective-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. Si la llamada al procesador de imágenes se ejecuta correctamente, este pasa una lista de VisionObject al controlador de finalización o la muestra, dependiendo de si llamaste a un método asíncrono o a uno síncrono.

    Cada VisionObject contiene las siguientes propiedades:

    frame 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. Nil en el modo de imagen individual.
    classificationCategory La categoría ordinaria del objeto. Si el detector de objetos no tiene habilitada la clasificación, esto es siempre .unknown.
    confidence El valor de confianza de la clasificación del objeto. Si el detector de objetos no tiene habilitada la clasificación, o el objeto se clasifica como desconocido, esto es nil.

    Swift

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

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

Mejora la usabilidad y el rendimiento

Para obtener la mejor experiencia del usuario, sigue estos lineamientos en tu aplicación:

  • La detección exitosa de objetos depende de la complejidad visual del objeto. Es posible que los objetos con una pequeña cantidad de características visuales deban ocupar un espacio mayor en la imagen para ser detectados. Debes proporcionar a los usuarios una orientación sobre cómo capturar información que funcione bien con el tipo de objetos que deseas detectar.
  • Cuando uses la clasificación, si deseas detectar objetos que no se incluyen de forma clara en las categorías compatibles, implementa un manejo especial para objetos desconocidos.

Además, consulta la [app de muestra de Material Design del Kit de AA][showcase-link]{: .external} y 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 los mejores fotogramas:

  • No utilices la detección de múltiples 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.

  • Regula las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, ignora ese fotograma.
  • Si estás usando la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado de la detección de ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, procesas la superficie de visualización solo una vez por cada fotograma de entrada. Consulta las clases previewOverlayView y FIRDetectionOverlayView en la app de muestra para ver un ejemplo.