Wykrywanie i śledzenie obiektów za pomocą ML Kit na iOS

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

Gdy prześlesz obrazy ML Kit, ML Kit zwróci dla każdego obrazu listę maksymalnie 5 wykrytych obiektów i ich położenie na zdjęciu. Podczas wykrywania obiektów w strumieniach wideo, każdy z nich ma identyfikator, który pozwala śledzić obiekt na obrazach. Możesz też opcjonalnie włączyć obiekt przybliżony klasyfikację, która oznacza etykietami obiekty o szerokim opisie kategorii.

Zanim zaczniesz

  1. Jeśli nie masz jeszcze w aplikacji dodanej Firebase, wykonaj czynności podane w przewodniku dla początkujących.
  2. Umieść biblioteki ML Kit w pliku Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    Po zainstalowaniu lub zaktualizowaniu podów projektu otwórz Xcode projektu za pomocą jego .xcworkspace.
  3. W aplikacji zaimportuj Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

1. Konfigurowanie detektora obiektów

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

  1. Skonfiguruj detektor obiektów na potrzeby swojego przypadku użycia za pomocą VisionObjectDetectorOptions obiekt. Możesz zmienić te ustawienia: ustawienia:

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

    W trybie strumienia (domyślny) detektor obiektów działa z bardzo niską wydajnością czas oczekiwania, ale może dawać niepełne wyniki (np. nieokreślone) ramek ograniczających lub kategorii) na kilku pierwszych wywołaniach wzorca. Ponadto w trybie strumienia detektor przypisuje śledzenie Identyfikatory obiektów, które mogą służyć do śledzenia obiektów w ramkach. Użyj tego trybu, jeśli chcesz śledzić obiekty lub gdy małe opóźnienie ma duże znaczenie, np. podczas przetwarzania strumieni wideo w czasie rzeczywistym, obecnie się znajdujesz.

    W trybie pojedynczego obrazu detektor obiektów czeka, aż ramki ograniczającej obiektu i kategorię (jeśli masz włączoną klasyfikację) są dostępne przed zwróceniem wyniku. W związku z tym opóźnienie wykrywania jest potencjalnie większe. Także na jednym obrazie identyfikatorów śledzenia nie są przypisywane. Użyj tego trybu w przypadku opóźnień jest kluczowe i nie chcesz mieć do czynienia z częściowymi wyników.

    Wykrywanie i śledzenie wielu obiektów false (domyślna) | true

    Określa, czy można wykryć i śledzić do pięciu obiektów, czy tylko najbardziej. widoczny obiekt (domyślnie).

    Klasyfikowanie obiektów false (domyślna) | true

    Określa, czy należy sklasyfikować wykryte obiekty w przybliżonych kategoriach. Gdy ta opcja jest włączona, detektor obiektów klasyfikuje obiekty w następujące kategorie: moda, żywność, AGD, miejsc, roślin i nieznanych miejsc.

    Interfejs API wykrywania i śledzenia obiektów jest zoptymalizowany pod kątem tych dwóch podstawowych zastosowań przypadki:

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

    Aby skonfigurować interfejs API pod kątem tych przypadków użycia:

    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. Pobierz instancję 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. Uruchom detektor obiektów

Aby wykrywać i śledzić obiekty, wykonaj te czynności w przypadku każdego obrazu lub klatki filmu. Jeśli masz włączony tryb strumieniowania, musisz utworzyć obiekty VisionImage z CMSampleBufferRef.

  1. Utwórz obiekt VisionImage za pomocą UIImage lub CMSampleBufferRef.

    Aby użyć karty UIImage:

    1. W razie potrzeby obróć zdjęcie, tak by jego imageOrientation właściwość to .up.
    2. Utwórz obiekt VisionImage przy użyciu prawidłowo wykonanej rotacji UIImage Nie określaj żadnych metadanych rotacji – są to metadane domyślne. .topLeft.

      Swift

      let image = VisionImage(image: uiImage)

      Objective-C

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

    Aby użyć karty CMSampleBufferRef:

    1. Utwórz obiekt VisionImageMetadata, który określa orientacji danych zdjęć zawartych w Bufor CMSampleBufferRef.

      Aby sprawdzić orientację obrazu:

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

      Następnie utwórz obiekt metadanych:

      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. Utwórz obiekt VisionImage za pomocą Obiekt CMSampleBufferRef i metadane rotacji:

      Swift

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

      Objective-C

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

    Aby asynchronicznie wykrywać obiekty:

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

    Aby synchronicznie wykrywać obiekty:

    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. Jeśli wywołanie procesora zdjęć się powiedzie, przekazywana jest lista VisionObjects do modułu obsługi uzupełniania lub zwraca listę, w zależności od niezależnie od tego, czy została użyta metoda asynchroniczna czy synchroniczna.

    Każdy element VisionObject zawiera te właściwości:

    frame CGRect wskazujący położenie obiektu w .
    trackingID Liczba całkowita, która identyfikuje obiekt na obrazach. Nil w singlu trybu obrazu.
    classificationCategory Przybliżona kategoria obiektu. Jeśli detektor obiektów nie mają włączoną klasyfikację, zawsze to .unknown.
    confidence Wartość ufności klasyfikacji obiektu. Jeśli obiekt detektor nie ma włączonej klasyfikacji lub obiekt jest sklasyfikowane jako nieznane, to 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;
    }
    

Poprawa łatwości obsługi i wydajności

Aby zadbać o wygodę użytkowników, przestrzegaj tych wytycznych:

  • Pomyślne wykrycie obiektu zależy od jego złożoności wizualnej. Obiekty z niewielką liczbą funkcji wizualnych mogą zajmować większą część który ma być wykrywany. Należy zapewnić użytkownikom wskazówki dotyczące rejestrowania takich jak dane wejściowe do wykrywania obiektów.
  • Jeśli chcesz wykrywać obiekty, które nie wypadają podczas klasyfikacji, do obsługiwanych kategorii, zastosować specjalną obsługę nieznanych obiektów.

Zapoznaj się też z [ML Kit Material Design][showcase-link]{: .external } oraz Material Design Kolekcja Wzorce funkcji opartych na systemach uczących się.

Jeśli używasz trybu strumieniowania w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby: aby uzyskać najlepszą liczbę klatek:

  • Nie używaj wykrywania wielu obiektów w trybie strumieniowania, ponieważ większość urządzeń którzy są w stanie wygenerować odpowiednią liczbę klatek na sekundę.

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

  • Ogranicz wywołania do detektora. Jeśli nowa klatka wideo dostępnych, gdy detektor jest uruchomiony, upuść ramkę.
  • Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obrazu wejściowego, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładanie nakładek w jednym kroku. W ten sposób renderowanie na powierzchni tylko raz na każdą ramkę wejściową. Zobacz previewOverlayView. i FIRDetectionOverlayView w aplikacji z funkcją prezentacji.