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

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

Gdy przekazujesz obraz do ML Kit, wykrywa on do 5 obiektów na obrazie wraz z pozycją każdego z nich. Przy wykrywaniu obiektów w strumieniach wideo każdy obiekt ma unikalny identyfikator, za pomocą którego można go śledzić od klatki do klatki. Możesz też włączyć przybliżoną klasyfikację obiektów, która oznacza obiekty szerokimi opisami kategorii.

Wypróbuj

Zanim zaczniesz

  1. W pliku Podfile uwzględnij te pody ML Kit:
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. Po zainstalowaniu lub zaktualizowaniu podów w projekcie otwórz projekt Xcode, korzystając z jego polecenia .xcworkspace. ML Kit obsługuje Xcode w wersji 12.4 lub nowszej.

1. Skonfiguruj detektor obiektów

Aby wykrywać i śledzić obiekty, najpierw utwórz instancję ObjectDetector i opcjonalnie określ ustawienia wykrywania, które chcesz zmienić z domyślnych.

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

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

    W trybie strumienia (domyślnym) wzorzec do wykrywania obiektów działa z bardzo krótkim czasem oczekiwania, ale podczas jego kilku pierwszych wywołań może generować niekompletne wyniki (takie jak nieokreślone ramki ograniczające lub kategorie). Dodatkowo w trybie strumieniowania detektor przypisuje do obiektów identyfikatory śledzenia, których możesz używać do śledzenia obiektów w ramkach. Użyj tego trybu, jeśli chcesz śledzić obiekty lub gdy ważne jest krótkie opóźnienie, np. gdy przetwarzasz strumienie wideo w czasie rzeczywistym.

    W trybie z pojedynczym obrazem detektor obiektów zwraca wynik po określeniu ramki ograniczającej obiektu. Jeśli włączysz również klasyfikację, zostanie zwrócony wynik, gdy ramka ograniczająca i etykieta kategorii są dostępne. W rezultacie czas oczekiwania na wykrywanie jest potencjalnie dłuższy. W trybie z jednym obrazem identyfikatory śledzenia nie są przypisywane. Użyj tego trybu, jeśli opóźnienie nie jest krytyczne i nie chcesz zajmować się częściowymi wynikami.

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

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

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

    Określa, czy wykryte obiekty mają być klasyfikowane w przybliżonych kategoriach. Gdy ta opcja jest włączona, detektor obiektów klasyfikuje obiekty w tych kategoriach: odzież, jedzenie, wyposażenie domu, miejsca i rośliny.

    Interfejs API wykrywania i śledzenia jest zoptymalizowany pod kątem tych 2 głównych przypadków użycia:

    • Wykrywanie na żywo i śledzenie najbardziej widocznego obiektu w wizjerze kamery.
    • 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 = 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. Pobierz instancję 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. Przygotuj obraz wejściowy

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

Utwórz obiekt VisionImage za pomocą UIImage lub CMSampleBuffer.

Jeśli używasz urządzenia UIImage, wykonaj te czynności:

  • Utwórz obiekt VisionImage za pomocą UIImage. Pamiętaj, aby podać prawidłową wartość .orientation.

    Swift

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

    Objective-C

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

Jeśli używasz urządzenia CMSampleBuffer, wykonaj te czynności:

  • Określ orientację danych obrazu w elemencie CMSampleBuffer.

    Aby pobrać orientację:

    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;
      }
    }
          
  • Utwórz obiekt VisionImage, używając obiektu i orientacji CMSampleBuffer:

    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. Przetwarzanie obrazu

Przekaż właściwość VisionImage do jednej z metod przetwarzania obrazu przez detektor obiektów. Możesz używać asynchronicznej metody process(image:) lub synchronicznej metody results().

Aby asynchronicznie wykrywać obiekty:

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

Aby synchronicznie wykrywać obiekty:

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. Pobieranie informacji o wykrytych obiektach

Jeśli wywołanie procesora obrazów się powiedzie, przekaże listę Object do modułu obsługi ukończenia lub zwróci listę w zależności od tego, czy wywołano metodę asynchroniczną czy synchroniczną.

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

frame CGRect wskazująca pozycję obiektu na obrazie.
trackingID Liczba całkowita identyfikująca obiekt na zdjęciach lub „nil” w trybie pojedynczego obrazu.
labels Tablica etykiet opisujących obiekt zwrócony przez detektor. Właściwość jest pusta, jeśli opcja wzorca shouldEnableClassification jest ustawiona na 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];
    ...
  }
}

Większa użyteczność i wydajność

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

  • Pomyślne wykrywanie obiektów zależy od złożoności wizualnej obiektu. Aby można było wykryć obiekty o niewielkiej liczbie funkcji wizualnych, może być konieczne zajmowanie większej części obrazu. Musisz udostępnić użytkownikom wskazówki dotyczące przechwytywania danych wejściowych, które sprawdzają się w przypadku danego rodzaju obiektów, które chcesz wykrywać.
  • Jeśli używasz klasyfikacji i chcesz wykrywać obiekty, które nie należą do poszczególnych kategorii, musisz wdrożyć specjalną obsługę nieznanych obiektów.

Zerknij na kolekcję wzorców dla funkcji opartych na systemach uczących się w interfejsie Material Design.

Jeśli używasz trybu strumieniowego przesyłania danych w aplikacji wyświetlającej dane w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:

  • Nie używaj wykrywania wielu obiektów w trybie strumieniowego przesyłania danych, ponieważ większość urządzeń nie będzie w stanie uzyskać wystarczającej liczby klatek.
  • Wyłącz klasyfikację, jeśli jej nie potrzebujesz.
  • Do przetwarzania klatek wideo użyj synchronicznego interfejsu API results(in:) wzorca. Wywołaj tę metodę z funkcji captureOutput(_, didOutput:from:) obiektu AVCaptureVideoDataOutputSampleBufferDelegate, aby synchronicznie pobierać wyniki z danej klatki wideo. Pozostaw alwaysDiscardsLateVideoFrames obiektu AVCaptureVideoDataOutput jako true, aby ograniczać wywołania do wzorca. Jeśli podczas działania wzorca pojawi się nowa ramka wideo, zostanie ona usunięta.
  • Jeśli używasz danych wyjściowych wzorca do nakładania grafiki na obraz wejściowy, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu na każdą przetworzoną klatkę wejściową renderujesz się tylko raz. Przykład znajdziesz w sekcji updatePreviewOverlayViewWithLastFrame w przykładowym krótkim wprowadzeniu do ML Kit.