Wykrywaj i śledź obiekty za pomocą ML Kit na iOS

Za pomocą zestawu ML Kit można wykrywać i śledzić obiekty w klatkach wideo.

Kiedy przekazujesz obrazy ML Kit, ML Kit zwraca dla każdego obrazu listę maksymalnie pięciu wykrytych obiektów i ich położenie na obrazie. Podczas wykrywania obiektów w strumieniach wideo każdy obiekt ma identyfikator, którego można użyć do śledzenia obiektu na obrazach. Opcjonalnie możesz także włączyć zgrubną klasyfikację obiektów, która oznacza obiekty z szerokimi opisami kategorii.

Zanim zaczniesz

  1. Jeśli nie dodałeś jeszcze Firebase do swojej aplikacji, zrób to, wykonując czynności opisane w przewodniku wprowadzającym .
  2. Dołącz biblioteki ML Kit do swojego Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    Po zainstalowaniu lub zaktualizowaniu Podów swojego projektu pamiętaj o otwarciu projektu Xcode przy użyciu jego .xcworkspace .
  3. W swojej aplikacji zaimportuj Firebase:

    Szybki

    import Firebase

    Cel C

    @import Firebase;

1. Skonfiguruj detektor obiektów

Aby rozpocząć wykrywanie i śledzenie obiektów, najpierw utwórz instancję VisionObjectDetector , opcjonalnie określając ustawienia detektora, które chcesz zmienić z domyślnych.

  1. Skonfiguruj detektor obiektów dla swojego przypadku użycia za pomocą obiektu VisionObjectDetectorOptions . Możesz zmienić następujące ustawienia:

    Ustawienia detektora obiektów
    Tryb wykrywania .stream (domyślny) | .singleImage

    W trybie strumieniowym (domyślnym) detektor obiektów działa z bardzo małym opóźnieniem, ale może generować niekompletne wyniki (takie jak nieokreślone ramki ograniczające lub kategoria) przy kilku pierwszych wywołaniach detektora. Ponadto w trybie strumieniowym detektor przypisuje obiektom identyfikatory śledzenia, których można używać do śledzenia obiektów w klatkach. Użyj tego trybu, jeśli chcesz śledzić obiekty lub gdy ważne jest małe opóźnienie, na przykład podczas przetwarzania strumieni wideo w czasie rzeczywistym.

    W trybie pojedynczego obrazu detektor obiektów czeka, aż obwiednia i kategoria wykrytego obiektu (jeśli włączono klasyfikację) będą dostępne, zanim zwróci wynik. W rezultacie opóźnienie wykrywania jest potencjalnie większe. Ponadto w trybie pojedynczego obrazu identyfikatory śledzenia nie są przypisywane. Użyj tego trybu, jeśli opóźnienie nie jest krytyczne i nie chcesz mieć do czynienia z częściowymi wynikami.

    Wykrywaj i śledź wiele obiektów false (domyślnie) | true

    Określa, czy wykrywać i śledzić maksymalnie pięć obiektów, czy tylko najbardziej widoczny obiekt (domyślnie).

    Klasyfikuj obiekty false (domyślnie) | true

    Określa, czy klasyfikować wykryte obiekty do ogólnych kategorii. Po włączeniu detektor obiektów klasyfikuje obiekty w następujące kategorie: towary modowe, żywność, artykuły gospodarstwa domowego, miejsca, rośliny i nieznane.

    Interfejs API wykrywania i śledzenia obiektów jest zoptymalizowany pod kątem dwóch podstawowych przypadków użycia:

    • Wykrywanie na żywo i śledzenie najbardziej widocznego obiektu w wizjerze aparatu
    • Wykrywanie wielu obiektów na statycznym obrazie

    Aby skonfigurować interfejs API dla tych przypadków użycia:

    Szybki

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

    Cel 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. Uzyskaj instancję FirebaseVisionObjectDetector :

    Szybki

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

    Cel C

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

2. Uruchom detektor obiektów

Aby wykryć i śledzić obiekty, wykonaj poniższe czynności dla każdego obrazu lub klatki wideo. Jeśli włączyłeś tryb strumieniowy, musisz utworzyć obiekty VisionImage z CMSampleBufferRef s.

  1. Utwórz obiekt VisionImage przy użyciu UIImage lub CMSampleBufferRef .

    Aby użyć UIImage :

    1. W razie potrzeby obróć obraz tak, aby jego właściwość imageOrientation miała .up .
    2. Utwórz obiekt VisionImage , używając poprawnie obróconego UIImage . Nie określaj żadnych metadanych rotacji — należy użyć wartości domyślnej .topLeft .

      Szybki

      let image = VisionImage(image: uiImage)

      Cel C

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

    Aby użyć CMSampleBufferRef :

    1. Utwórz obiekt VisionImageMetadata , który określa orientację danych obrazu zawartych w buforze CMSampleBufferRef .

      Aby uzyskać orientację obrazu:

      Szybki

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

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

      Następnie utwórz obiekt metadanych:

      Szybki

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

      Cel 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. Utwórz obiekt VisionImage przy użyciu obiektu CMSampleBufferRef i metadanych rotacji:

      Szybki

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

      Cel C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. Przekaż VisionImage do jednej z metod przetwarzania obrazu detektora obiektów. Można użyć metody asynchronicznej process(image:) lub metody synchronicznej results() .

    Aby wykryć obiekty asynchronicznie:

    Szybki

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

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

    Aby wykryć obiekty synchronicznie:

    Szybki

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

    Cel 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. Jeśli wywołanie procesora obrazu powiedzie się, przekazuje on listę obiektów VisionObject do procedury obsługi uzupełniania lub zwraca listę, w zależności od tego, czy wywołano metodę asynchroniczną, czy synchroniczną.

    Każdy VisionObject zawiera następujące właściwości:

    frame CGRect wskazujący położenie obiektu na obrazie.
    trackingID Liczba całkowita identyfikująca obiekt na obrazach. Zero w trybie pojedynczego obrazu.
    classificationCategory Gruba kategoria obiektu. Jeśli detektor obiektów nie ma włączonej klasyfikacji, jest to zawsze .unknown .
    confidence Wartość ufności klasyfikacji obiektu. Jeśli detektor obiektów nie ma włączonej klasyfikacji lub obiekt jest sklasyfikowany jako nieznany, wartość ta wynosi nil .

    Szybki

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

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

Poprawa użyteczności i wydajności

Aby zapewnić najlepszą wygodę użytkowania, postępuj zgodnie z tymi wskazówkami w swojej aplikacji:

  • Skuteczne wykrycie obiektu zależy od jego złożoności wizualnej. Aby obiekty o niewielkiej liczbie cech wizualnych mogły zostać wykryte, konieczne może być zajęcie większej części obrazu. Powinieneś zapewnić użytkownikom wskazówki dotyczące przechwytywania danych wejściowych, które sprawdzają się w przypadku obiektów, które chcesz wykryć.
  • Jeśli podczas korzystania z klasyfikacji chcesz wykryć obiekty, które nie mieszczą się w obsługiwanych kategoriach, zaimplementuj specjalną obsługę nieznanych obiektów.

Zapoznaj się także z [aplikacją prezentacyjną ML Kit Material Design] [showcase-link]{: .external } i kolekcją Material Design Patterns na potrzeby funkcji opartych na uczeniu maszynowym .

Korzystając z trybu przesyłania strumieniowego w aplikacji czasu rzeczywistego, postępuj zgodnie z poniższymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:

  • Nie używaj wykrywania wielu obiektów w trybie przesyłania strumieniowego, ponieważ większość urządzeń nie będzie w stanie wygenerować odpowiedniej liczby klatek na sekundę.

  • Wyłącz klasyfikację, jeśli jej nie potrzebujesz.

  • Przepustnica wzywa do detektora. Jeśli w czasie działania detektora pojawi się nowa klatka wideo, usuń ją.
  • Jeśli używasz wyjścia detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. W ten sposób renderujesz na powierzchnię wyświetlacza tylko raz dla każdej klatki wejściowej. Zobacz przykładowe klasy PreviewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji prezentacyjnej.