Wykrywanie i śledzenie obiektów przy użyciu ML Kit na iOS

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Możesz używać ML Kit do wykrywania i śledzenia obiektów w kolejnych klatkach wideo.

Gdy przekazujesz obraz do narzędzia ML Kit, wykrywa on maksymalnie 5 obiektów na obrazie wraz z jego położeniem. Podczas wykrywania obiektów w strumieniach wideo każdy obiekt ma unikalny identyfikator, którego można użyć do śledzenia obiektu między ramkami. Opcjonalnie możesz też włączyć przybliżoną klasyfikację obiektów, która oznacza obiekty etykietami z szerokimi opisami kategorii.

Wypróbuj

Zanim zaczniesz

  1. Uwzględnij w podfile te pody ML Kit:
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. Gdy zainstalujesz lub zaktualizujesz pody projektu, otwórz projekt Xcode, używając jego .xcworkspace. ML Kit jest obsługiwany w Xcode w wersji 12.4 lub nowszej.

1. Konfigurowanie wzorca do wykrywania obiektów

Aby wykrywać i śledzić obiekty, najpierw utwórz instancję ObjectDetector i opcjonalnie określ wszystkie ustawienia wzorca do wykrywania, które chcesz zmienić jako domyślne.

  1. Skonfiguruj wzorzec do wykrywania obiektów w swoim 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 strumieniowania (domyślny) wzorzec do wykrywania obiektów działa z niewielkim opóźnieniem, ale w przypadku kilku pierwszych wywołań detektora może uzyskać niepełne wyniki (takie jak nieokreślone ramki ograniczające lub kategorie). W trybie strumieniowania wzorzec wykrywa identyfikatory obiektów na potrzeby śledzenia obiektów w ramkach. 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 wzorzec do wykrywania obiektów zwraca wynik po określeniu ramki ograniczającej obiekt. Jeśli włączysz także klasyfikację, wyniki będą zwracane po udostępnieniu ramki ograniczającej i etykiety kategorii. W efekcie opóźnienie wykrywania może być większe. W trybie pojedynczego obrazu nie są też przypisywane identyfikatory śledzenia. Użyj tego trybu, jeśli opóźnienie nie jest krytyczne i nie chcesz uwzględniać częściowych wyników.

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

    Określa, czy chcesz wykryć 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ą zostać sklasyfikowane w ramach przybliżonych kategorii. Po włączeniu tej funkcji wykrywacz obiektów klasyfikuje obiekty w te kategorie: artykuły odzieżowe, jedzenie, artykuły gospodarstwa domowego, miejsca i rośliny.

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

    • Wykrywanie i śledzenie najsłynniejszego 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, wykonaj te czynności w przypadku każdego obrazu lub ramki filmu. Jeśli masz włączony tryb strumieniowania, musisz utworzyć obiekty VisionImage z CMSampleBuffer s.

Utwórz obiekt VisionImage za pomocą właściwości UIImage lub CMSampleBuffer.

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

  • Utwórz obiekt VisionImage z elementem UIImage. Pamiętaj o prawidłowej wartości .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 CMSampleBuffer, wykonaj te czynności:

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

    Aby uzyskać orientację obrazu:

    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 za pomocą obiektu CMSampleBuffer i orientacji:

    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. Przetwórz obraz

Przekaż VisionImage do jednej z metod przetwarzania obrazu wykrywacza obiektów. Możesz użyć metody asynchronicznego 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 wykrywać obiekty synchronicznie:

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. Uzyskaj informacje o wykrytych obiektach

Jeśli wywołanie procesora graficznego zakończy się powodzeniem, przesyła listę Object do modułu obsługi ukończenia lub zwraca listę w zależności od tego, czy została wywołana metoda asynchroniczny czy synchroniczny.

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

frame CGRect wskazujący położenie obiektu na obrazie.
trackingID Liczba całkowita określająca obiekt na obrazach lub „nil” w trybie pojedynczego obrazu.
labels Tablica etykiet opisujących obiekt, który został zwrócony przez wzorzec. Właściwość jest pusta, jeśli opcja wzorca do wykrywania treści 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];
    ...
  }
}

Poprawa użyteczności i wydajności

Aby zadbać o wygodę użytkowników, zastosuj się do tych wskazówek:

  • Pomyślne wykrywanie obiektu zależy od jego złożoności wizualnej. Aby obiekt został wykryty, obiekty o małej liczbie elementów wizualnych mogą zająć większą część obrazu. Należy przekazać użytkownikom wskazówki dotyczące przechwytywania danych wejściowych, które dobrze współgrają z obiektami, które mają być wykrywane.
  • Jeśli używasz klasyfikacji, aby wykrywać obiekty, które nie należą do obsługiwanych kategorii, zaimplementuj specjalną obsługę elementów nieznanych.

Zajrzyj też do kolekcji Wzorce funkcji wykorzystujących systemy uczące się w stylu Material Design.

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

  • Nie używaj w trybie strumieniowego wykrywania wielu obiektów, ponieważ większość urządzeń nie będzie w stanie generować odpowiedniej liczby klatek.
  • Wyłącz klasyfikację, której nie potrzebujesz.
  • Aby przetworzyć ramki wideo, użyj synchronicznego interfejsu API detektora results(in:). Wywołaj tę metodę za pomocą funkcji captureOutput(_, didOutput:from:) AVCaptureVideoDataOutputSampleBufferDelegate, aby synchronicznie uzyskać wyniki z danej klatki filmu. Aby ograniczyć liczbę wywołań wykrywania, zachowaj alwaysDiscardsLateVideoFrames AVCaptureVideoDataOutput. Jeśli nowa reguła wideo stanie się dostępna podczas działania wzorca do wykrywania treści, zostanie usunięta.
  • Jeśli używasz danych wyjściowych wzorca do nakładania grafiki na obrazie wejściowym, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu renderowanie każdej klatki będzie renderowane tylko raz dla każdej przetworzonej klatki wejściowej. Przykład znajdziesz w przykładzie updatepreviewOverlayViewWithLastFrame w przykładzie ML Kit.