Gdy przekazujesz obraz do narzędzia ML Kit, wykrywa on do 5 obiektów razem 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 początku do klatki.
Do sklasyfikowania wykrytych obiektów możesz użyć niestandardowego modelu klasyfikacji obrazów. W artykule Modele niestandardowe z użyciem ML Kit dowiesz się, jakie są wymagania dotyczące zgodności modeli, gdzie znaleźć wytrenowane modele i jak trenować własne modele.
Model niestandardowy można zintegrować na 2 sposoby. Możesz połączyć model, umieszczając go w folderze zasobów aplikacji lub pobrać dynamicznie z Firebase. Tabela poniżej zawiera porównanie obu opcji.
Model w pakiecie | Model hostowany |
---|---|
Model jest częścią pliku .ipa aplikacji, który zwiększa swój rozmiar. |
Model nie jest częścią pliku .ipa aplikacji. Jest hostowany przez przesłanie go do systemów uczących się Firebase. |
Model jest dostępny natychmiast, nawet jeśli urządzenie z Androidem jest offline. | Model jest pobierany na żądanie |
Nie potrzebujesz projektu Firebase | Wymaga projektu Firebase |
Aby zaktualizować model, musisz ponownie opublikować aplikację | Przesyłaj aktualizacje modelu bez ponownego publikowania aplikacji |
Brak wbudowanych testów A/B | łatwe testy A/B dzięki Zdalnej konfiguracji Firebase. |
Wypróbuj
- W krótkim wprowadzeniu do aplikacji Vision znajdziesz przykład użycia modelu w pakiecie, a w aplikacji z krótkim wprowadzeniem do Automl – przykład użycia hostowanego modelu.
- Pełną implementację tego interfejsu API znajdziesz w aplikacji Material Design z funkcją prezentacji.
Zanim zaczniesz
Dodaj biblioteki ML Kit do pliku Podfile:
Aby dodać model do pakietu z aplikacją:
pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
Aby dynamicznie pobierać model z Firebase, dodaj zależność
LinkFirebase
:pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0' pod 'GoogleMLKit/LinkFirebase', '3.2.0'
Po zainstalowaniu lub zaktualizowaniu podów w projekcie otwórz projekt Xcode, korzystając z
.xcworkspace
. ML Kit obsługuje Xcode w wersji 13.2.1 lub nowszej.Jeśli chcesz pobrać model, dodaj Firebase do swojego projektu iOS, jeśli jeszcze tego nie zrobiłeś. Nie jest to wymagane podczas pakowania modelu.
1. Wczytywanie modelu
Skonfiguruj źródło modelu lokalnego
Aby połączyć model z aplikacją:
Skopiuj do projektu Xcode plik modelu (zwykle kończący się na
.tflite
lub.lite
), wybierając przy tymCopy bundle resources
. Plik modelu zostanie dołączony do pakietu aplikacji i będzie dostępny dla ML Kit.Utwórz obiekt
LocalModel
, podając ścieżkę do pliku modelu:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
Skonfiguruj źródło modelu hostowane w Firebase
Aby użyć modelu hostowanego zdalnie, utwórz obiekt CustomRemoteModel
z nazwą przypisaną do modelu w chwili jego opublikowania:
Swift
let firebaseModelSource = FirebaseModelSource( name: "your_remote_model") // The name you assigned in // the Firebase console. let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource)
Objective-C
MLKFirebaseModelSource *firebaseModelSource = [[MLKFirebaseModelSource alloc] initWithName:@"your_remote_model"]; // The name you assigned in // the Firebase console. MLKCustomRemoteModel *remoteModel = [[MLKCustomRemoteModel alloc] initWithRemoteModelSource:firebaseModelSource];
Następnie rozpocznij zadanie pobierania modelu, określając warunki, które mają mieć możliwość pobierania. Jeśli modelu nie ma na urządzeniu lub dostępna jest jego nowsza wersja, zadanie pobierze asynchronicznie model z Firebase:
Swift
let downloadConditions = ModelDownloadConditions( allowsCellularAccess: true, allowsBackgroundDownloading: true ) let downloadProgress = ModelManager.modelManager().download( remoteModel, conditions: downloadConditions )
Objective-C
MLKModelDownloadConditions *downloadConditions = [[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES allowsBackgroundDownloading:YES]; NSProgress *downloadProgress = [[MLKModelManager modelManager] downloadModel:remoteModel conditions:downloadConditions];
Wiele aplikacji rozpoczyna zadanie pobierania w kodzie inicjowania, ale możesz to zrobić w dowolnym momencie, zanim zajdzie potrzeba używania modelu.
2. Skonfiguruj detektor obiektów
Po skonfigurowaniu źródeł modelu skonfiguruj detektor obiektów na potrzeby danego przypadku użycia z użyciem obiektu CustomObjectDetectorOptions
. Możesz zmienić
te ustawienia:
Ustawienia funkcji wykrywania obiektów | |
---|---|
Tryb wykrywania |
STREAM_MODE (domyślnie) | SINGLE_IMAGE_MODE
W W |
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 klasyfikować wykryte obiekty przy użyciu dostarczonego modelu klasyfikatora niestandardowego. Aby użyć własnego modelu klasyfikacji, ustaw wartość |
Próg ufności klasyfikacji |
Minimalny wskaźnik ufności wykrytych etykiet. Jeśli nie skonfigurujesz tej zasady, zostanie użyty dowolny próg klasyfikatora określony przez metadane modelu. Jeśli model nie zawiera żadnych metadanych lub metadane nie określają progu klasyfikatora, zostanie użyty domyślny próg równy 0,0. |
Maksymalna liczba etykiet na obiekt |
Maksymalna liczba etykiet na obiekt, które może zwrócić detektor. Jeśli zasada nie jest skonfigurowana, używana jest wartość domyślna, czyli 10. |
Jeśli masz tylko model dołączony lokalnie, utwórz wzorzec do wykrywania obiektów na podstawie obiektu LocalModel
:
Swift
let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3
Objective-C
MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3;
Jeśli masz model hostowany zdalnie, przed jego uruchomieniem musisz sprawdzić, czy został on pobrany. Stan zadania pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded(remoteModel:)
menedżera modeli.
Musisz to potwierdzić przed uruchomieniem detektora obiektów, ale jeśli używasz zarówno modelu hostowanego zdalnie, jak i modelu dołączonego lokalnie, podczas tworzenia wystąpienia obiektu ObjectDetector
warto wykonać tę procedurę sprawdzania: utwórz detektor z modelu zdalnego (jeśli został pobrany, a w przeciwnym razie z modelu lokalnego).
Swift
var options: CustomObjectDetectorOptions! if (ModelManager.modelManager().isModelDownloaded(remoteModel)) { options = CustomObjectDetectorOptions(remoteModel: remoteModel) } else { options = CustomObjectDetectorOptions(localModel: localModel) } options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3
Objective-C
MLKCustomObjectDetectorOptions *options; if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) { options = [[MLKCustomObjectDetectorOptions alloc] initWithRemoteModel:remoteModel]; } else { options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; } options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3;
Jeśli masz tylko model hostowany zdalnie, wyłącz funkcje związane z modelem – na przykład wyszarzanie lub ukrycie części interfejsu użytkownika do czasu potwierdzenia, że model został pobrany.
Stan pobierania modelu możesz uzyskać, dołączając obserwatorów do domyślnego Centrum powiadomień. Pamiętaj, aby w bloku obserwacyjnym używać słabego odniesienia do self
, ponieważ pobieranie może trochę potrwać, a obiekt źródłowy może zostać uwolniony do momentu zakończenia pobierania. Na przykład:
Swift
NotificationCenter.default.addObserver( forName: .mlkitModelDownloadDidSucceed, object: nil, queue: nil ) { [weak self] notification in guard let strongSelf = self, let userInfo = notification.userInfo, let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel, model.name == "your_remote_model" else { return } // The model was downloaded and is available on the device } NotificationCenter.default.addObserver( forName: .mlkitModelDownloadDidFail, object: nil, queue: nil ) { [weak self] notification in guard let strongSelf = self, let userInfo = notification.userInfo, let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue] as? RemoteModel else { return } let error = userInfo[ModelDownloadUserInfoKey.error.rawValue] // ... }
Objective-C
__weak typeof(self) weakSelf = self; [NSNotificationCenter.defaultCenter addObserverForName:MLKModelDownloadDidSucceedNotification object:nil queue:nil usingBlock:^(NSNotification *_Nonnull note) { if (weakSelf == nil | note.userInfo == nil) { return; } __strong typeof(self) strongSelf = weakSelf; MLKRemoteModel *model = note.userInfo[MLKModelDownloadUserInfoKeyRemoteModel]; if ([model.name isEqualToString:@"your_remote_model"]) { // The model was downloaded and is available on the device } }]; [NSNotificationCenter.defaultCenter addObserverForName:MLKModelDownloadDidFailNotification object:nil queue:nil usingBlock:^(NSNotification *_Nonnull note) { if (weakSelf == nil | note.userInfo == nil) { return; } __strong typeof(self) strongSelf = weakSelf; NSError *error = note.userInfo[MLKModelDownloadUserInfoKeyError]; }];
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 = CustomObjectDetectorOptions(localModel: localModel) options.shouldEnableClassification = true options.maxPerObjectLabelCount = 3 // Multiple object detection in static images let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableMultipleObjects = true options.shouldEnableClassification = true options.maxPerObjectLabelCount = 3
Objective-C
// Live detection and tracking MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.shouldEnableClassification = YES; options.maxPerObjectLabelCount = 3; // Multiple object detection in static images MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableMultipleObjects = YES; options.shouldEnableClassification = YES; options.maxPerObjectLabelCount = 3;
3. Przygotuj obraz wejściowy
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 orientacjiCMSampleBuffer
: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];
4. Utwórz i uruchom detektor obiektów
Utwórz nowy detektor obiektów:
Swift
let objectDetector = ObjectDetector.objectDetector(options: options)
Objective-C
MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
Następnie użyj wzorca do wykrywania treści:
Asynchronicznie:
Swift
objectDetector.process(image) { objects, error in guard error == nil, let objects = objects, !objects.isEmpty else { // Handle the error. return } // Show results. }
Objective-C
[objectDetector processImage:image completion:^(NSArray
*_Nullable objects, NSError *_Nullable error) { if (objects.count == 0) { // Handle the error. return; } // Show results. }]; Synchronicznie:
Swift
var objects: [Object] do { objects = try objectDetector.results(in: image) } catch let error { // Handle the error. return } // Show results.
Objective-C
NSError *error; NSArray
*objects = [objectDetector resultsInImage:image error:&error]; // Show results or handle the error.
5. Pobieranie informacji o obiektach z etykietami
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 określająca obiekt na obrazach lub „nil” w trybie SINGLE_IMAGE_mode. | ||||||
labels |
|
Swift
// objects contains one item if multiple object detection wasn't enabled. for object in objects { let frame = object.frame let trackingID = object.trackingID let description = object.labels.enumerated().map { (index, label) in "Label \(index): \(label.text), \(label.confidence), \(label.index)" }.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]; } }
Dbanie o wygodę użytkowników
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.
Obejrzyj też [aplikację prezentującą ML Kit Material Design][showcase-link]{: .external } oraz kolekcję Material Design wzorców dla funkcji opartych na systemach uczących się.
Improving performance
Jeśli chcesz używać wykrywania obiektów w aplikacji w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:W przypadku korzystania z trybu strumieniowego przesyłania danych w aplikacji w czasie rzeczywistym nie używaj wykrywania wielu obiektów. Większość urządzeń nie jest w stanie uzyskać odpowiedniej liczby klatek.
- Do przetwarzania klatek wideo użyj synchronicznego interfejsu API
results(in:)
wzorca. Wywołaj tę metodę z funkcjicaptureOutput(_, didOutput:from:)
obiektuAVCaptureVideoDataOutputSampleBufferDelegate
, aby synchronicznie pobierać wyniki z danej klatki wideo. PozostawalwaysDiscardsLateVideoFrames
obiektuAVCaptureVideoDataOutput
jakotrue
, 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.