Gdy przekażesz obraz do ML Kit, wykryje on maksymalnie 5 obiektów na obrazie wraz z ich położeniem. Podczas wykrywania obiektów w strumieniach wideo każdy obiekt ma unikalny identyfikator, którego możesz używać do śledzenia obiektu w kolejnych klatkach.
Do klasyfikowania wykrytych obiektów możesz użyć niestandardowego modelu klasyfikacji obrazów. Więcej informacji o wymaganiach dotyczących zgodności modeli, o tym, gdzie znaleźć wstępnie wytrenowane modele, i o tym, jak trenować własne modele, znajdziesz w artykule Modele niestandardowe w ML Kit.
Istnieją 2 sposoby integracji modelu niestandardowego. Możesz połączyć model, umieszczając go w folderze zasobów aplikacji, lub pobrać go dynamicznie z Cloud Storage. W tabeli poniżej porównujemy te 2 opcje.
| Model połączony | Model hostowany |
|---|---|
Model jest częścią pliku .ipa aplikacji, co
zwiększa jego rozmiar. |
Model nie jest częścią pliku .ipa aplikacji. Jest
hostowany przez przesłanie do Cloud Storage. Zalecamy korzystanie z
Cloud Storage dla
Firebase. |
| Model jest dostępny od razu, nawet gdy urządzenie z Androidem jest offline. | Aplikacja musi zawierać kod umożliwiający pobieranie modelu na żądanie. |
| Nie jest wymagany projekt w Firebase. | Wymaga projektu w Firebase (jeśli używasz Cloud Storage dla Firebase). |
| Aby zaktualizować model, musisz ponownie opublikować aplikację. | Wysyłaj aktualizacje modelu bez ponownego publikowania aplikacji. |
| Brak wbudowanych testów A/B. | Testy A/B z użyciem Zdalnej konfiguracji Firebase. |
Wypróbuj
- Przykład użycia modelu połączonego znajdziesz w aplikacji z krótkim wprowadzeniem do Vision, a przykład użycia modelu hostowanego – w aplikacji z krótkim wprowadzeniem do AutoML.
- Kompletną implementację tego interfejsu API znajdziesz w aplikacji demonstracyjnej Material Design.
Zanim zaczniesz
Dodaj biblioteki ML Kit do pliku Podfile:
pod 'GoogleMLKit/ObjectDetectionCustom', '8.0.0'Po zainstalowaniu lub zaktualizowaniu podów projektu otwórz projekt Xcode za pomocą pliku
.xcworkspace. ML Kit jest obsługiwany w Xcode w wersji 13.2.1 lub nowszej.Jeśli chcesz pobrać model za pomocą Cloud Storage dla Firebase, upewnij się, że dodasz Firebase do projektu na iOS, jeśli jeszcze tego nie zrobisz. Nie jest to wymagane, gdy łączysz model.
1. Wczytaj model
Konfigurowanie lokalnego źródła modelu
Aby połączyć model z aplikacją:
Skopiuj plik modelu (zwykle z rozszerzeniem
.tflitelub.lite) do projektu Xcode, pamiętając, aby wybrać opcjęCopy bundle resources(Kopiuj zasoby pakietu). Plik modelu zostanie uwzględniony w pakiecie aplikacji i będzie dostępny dla ML Kit.Utwórz obiekt
LocalModel, określając ścieżkę do pliku modelu:Swift
let localModel = LocalModel(path: localModelFilePath)
Objective-C
MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:localModelFilePath];
Konfigurowanie źródła modelu hostowanego zdalnie
Aby używać modelu hostowanego zdalnie, musisz pobrać plik modelu do pamięci lokalnej urządzenia za pomocą własnej logiki aplikacji, a następnie wczytać go jako model lokalny. Do hostowania modelu zalecamy używanie Cloud Storage dla Firebase. Szczegółowe informacje o implementacji znajdziesz w przewodniku po migracji z Firebase ML do Cloud Storage.
2. Konfigurowanie detektora obiektów
Po skonfigurowaniu źródeł modelu skonfiguruj detektor obiektów pod kątem swojego przypadku użycia za pomocą obiektu CustomObjectDetectorOptions. Możesz zmienić te ustawienia:
| Ustawienia detektora obiektów | |
|---|---|
| Tryb wykrywania |
STREAM_MODE (domyślny) | SINGLE_IMAGE_MODE
W trybie W trybie |
| Wykrywanie i śledzenie wielu obiektów |
false (domyślne) | true
Określa, czy wykrywać i śledzić maksymalnie 5 obiektów, czy tylko najbardziej widoczny obiekt (domyślnie). |
| Klasyfikowanie obiektów |
false (domyślne) | true
Określa, czy klasyfikować wykryte obiekty za pomocą podanego
niestandardowego modelu klasyfikatora. Aby używać niestandardowego modelu klasyfikacji
model, musisz ustawić tę wartość na |
| Próg ufności klasyfikacji |
Minimalny wynik ufności wykrytych etykiet. Jeśli nie zostanie ustawiony, będzie używany próg klasyfikatora określony w metadanych modelu. Jeśli model nie zawiera metadanych lub metadane nie określają progu klasyfikatora, zostanie użyty domyślny próg 0,0. |
| Maksymalna liczba etykiet na obiekt |
Maksymalna liczba etykiet na obiekt, które zwróci detektor. Jeśli nie zostanie ustawiony, zostanie użyta wartość domyślna 10. |
Jeśli masz tylko model połączony lokalnie, utwórz detektor obiektów z 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ł pobrany.
Chociaż musisz to potwierdzić tylko przed uruchomieniem detektora obiektów, jeśli masz zarówno model hostowany zdalnie, jak i model połączony lokalnie, warto przeprowadzić tę kontrolę podczas tworzenia instancji ObjectDetector: utwórz detektor z modelu zdalnego, jeśli został pobrany, a w przeciwnym razie z modelu lokalnego.
Swift
// Path where your download logic saves the model let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") let model: LocalModel if FileManager.default.fileExists(atPath: localModelURL.path) { // Use the downloaded model model = LocalModel(path: localModelURL.path) } else { // Fall back to bundled model guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return } model = LocalModel(path: bundledModelPath) } let options = CustomObjectDetectorOptions(localModel: model) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true options.classificationConfidenceThreshold = NSNumber(value: 0.5) options.maxPerObjectLabelCount = 3 let objectDetector = ObjectDetector.objectDetector(options: options)
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; MLKLocalModel *model; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Use the downloaded model model = [[MLKLocalModel alloc] initWithPath:localModelPath]; } else { // Fall back to bundled model NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"]; model = [[MLKLocalModel alloc] initWithPath:bundledModelPath]; } MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:model]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; options.classificationConfidenceThreshold = @(0.5); options.maxPerObjectLabelCount = 3; MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
Jeśli masz tylko model hostowany zdalnie, wyłącz funkcje związane z modelem – np. wyszarz lub ukryj część interfejsu – dopóki nie potwierdzisz, że model został pobrany.
Swift
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite") if FileManager.default.fileExists(atPath: localModelURL.path) { // Model is already cached, initialize immediately self.initializeDetector(with: localModelURL) } else { // Model is not yet available, show loading UI and start download self.showLoadingUI() let storage = Storage.storage() let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite") modelRef.write(toFile: localModelURL) { url, error in self.hideLoadingUI() if let error = error { // Handle download error self.showErrorUI() } else if let modelURL = url { // Download success, initialize detector self.initializeDetector(with: modelURL) } } } func initializeDetector(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomObjectDetectorOptions(localModel: localModel) options.detectorMode = .singleImage options.shouldEnableClassification = true options.shouldEnableMultipleObjects = true self.objectDetector = ObjectDetector.objectDetector(options: options) // Enable ML features in UI self.enableMLFeatures() }
Objective-C
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]; NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"]; NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath]; if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) { // Model is already cached, initialize immediately [self initializeDetectorWithURL:localModelURL]; } else { // Model is not yet available, show loading UI and start download [self showLoadingUI]; FIRStorage *storage = [FIRStorage storage]; FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"]; [modelRef writeToFile:localModelURL completion:^(NSURL * _Nullable URL, NSError * _Nullable error) { [self hideLoadingUI]; if (error != nil) { // Handle download error [self showErrorUI]; } else { // Download success, initialize detector [self initializeDetectorWithURL:URL]; } }]; } - (void)initializeDetectorWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel]; options.detectorMode = MLKObjectDetectorModeSingleImage; options.shouldEnableClassification = YES; options.shouldEnableMultipleObjects = YES; self.objectDetector = [MLKObjectDetector objectDetectorWithOptions:options]; // Enable ML features in UI [self enableMLFeatures]; }
Interfejs API do wykrywania i śledzenia obiektów jest zoptymalizowany pod kątem tych 2 podstawowych przypadków użycia:
- Wykrywanie i śledzenie w czasie rzeczywistym najbardziej widocznego obiektu w wizjerze aparatu.
- 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 UIImage, wykonaj te czynności:
- Utwórz obiekt
VisionImageza pomocąUIImage. Pamiętaj, aby określić 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 CMSampleBuffer, wykonaj te czynności:
-
Określ orientację danych obrazu zawartych w
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
VisionImageza pomocą obiektuCMSampleBufferi 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];
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 detektora:
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. Uzyskaj informacje o oznaczonych obiektach
Jeśli wywołanie procesora obrazów się powiedzie, przekaże on listę obiektów Object do procedury obsługi zakończenia lub zwróci listę w zależności od tego, czy wywołasz metodę asynchroniczną czy synchroniczną.
Każdy obiekt Object zawiera te właściwości:
frame |
A CGRect wskazujący położenie obiektu na
obrazie. |
||||||
trackingID |
Liczba całkowita, która identyfikuje obiekt na obrazach, lub `nil` w 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]; } }
Zapewnianie wygody użytkownikom
Aby zapewnić użytkownikom jak największą wygodę, w aplikacji stosuj te wytyczne:
- Skuteczne wykrywanie obiektów zależy od ich złożoności wizualnej. Aby można było wykryć obiekty z niewielką liczbą cech wizualnych, mogą one zajmować większą część obrazu. Powinieneś(-aś) poinformować użytkowników, jak robić zdjęcia, które dobrze sprawdzają się w przypadku wykrywanych obiektów.
- Jeśli używasz klasyfikacji i chcesz wykrywać obiekty, które nie pasują do obsługiwanych kategorii, zaimplementuj specjalną obsługę nieznanych obiektów.
Zapoznaj się też z aplikacją demonstracyjną ML Kit Material Design i zbiorem wzorców Material Design dla funkcji opartych na uczeniu maszynowym.
Zwiększanie skuteczności
Jeśli chcesz używać wykrywania obiektów w aplikacji działającej w czasie rzeczywistym, stosuj te wytyczne, aby uzyskać jak najlepszą liczbę klatek na sekundę:Jeśli używasz trybu przesyłania strumieniowego w aplikacji działającej w czasie rzeczywistym, nie używaj wykrywania wielu obiektów, ponieważ większość urządzeń nie będzie w stanie zapewnić odpowiedniej liczby klatek na sekundę.
- Do przetwarzania klatek wideo używaj synchronicznego interfejsu API detektora
results(in:). Wywołaj tę metodę z funkcjiAVCaptureVideoDataOutputSampleBufferDelegate'scaptureOutput(_, didOutput:from:), aby synchronicznie uzyskać wyniki z danej klatki wideo. Ustaw wartośćAVCaptureVideoDataOutputinterfejsualwaysDiscardsLateVideoFramesnatrue, aby ograniczyć wywołania detektora. Jeśli podczas działania detektora pojawi się nowa klatka wideo, zostanie ona pominięta. - Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a następnie w jednym kroku wyrenderuj obraz i nałóż na niego grafikę. Dzięki temu renderujesz na powierzchni wyświetlacza tylko raz dla każdej przetworzonej klatki wejściowej. Przykład znajdziesz w funkcji updatePreviewOverlayViewWithLastFrame w aplikacji z krótkim wprowadzeniem do ML Kit.