Za pomocą ML Kit możesz rozpoznawać obiekty na obrazie i przypisywać im etykiety. Ten interfejs API obsługuje szeroką gamę niestandardowych modeli klasyfikacji obrazów. Wskazówki dotyczące wymagań związanych z kompatybilnością modeli, miejsc, w których można znaleźć wstępnie wytrenowane modele, oraz sposobu trenowania własnych modeli znajdziesz w artykule Modele niestandardowe w ML Kit.
Model niestandardowy można zintegrować na 2 sposoby. Model możesz dołączyć do aplikacji, umieszczając go w folderze zasobów, lub pobrać go dynamicznie z Cloud Storage. W tabeli poniżej porównaliśmy te 2 opcje.
| Model pakietowy | Model hostowany |
|---|---|
| Model jest częścią pliku APK aplikacji, co zwiększa jego rozmiar. | Model nie jest częścią pliku APK. Jest on 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 musisz mieć projektu w Firebase | Wymaga projektu w Firebase (jeśli używasz Cloud Storage dla Firebase). |
| Aby zaktualizować model, musisz ponownie opublikować aplikację | Wysyłanie aktualizacji modelu bez ponownego publikowania aplikacji |
| Brak wbudowanych testów A/B | Testy A/B za pomocą Zdalnej konfiguracji Firebase |
Wypróbuj
- Przykład użycia dołączonego modelu znajdziesz w aplikacji do szybkiego rozpoczęcia pracy z Vision, a przykład użycia hostowanego modelu – w aplikacji do szybkiego rozpoczęcia pracy z AutoML.
Zanim zaczniesz
Dodaj biblioteki ML Kit do pliku Podfile:
pod 'GoogleMLKit/ImageLabelingCustom', '8.0.0'Po zainstalowaniu lub zaktualizowaniu Pods w projekcie otwórz projekt Xcode, używając 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 dołączasz model.
1. Wczytywanie modelu
Konfigurowanie źródła modelu lokalnego
Aby połączyć model z aplikacją:
Skopiuj plik modelu (zwykle z rozszerzeniem
.tflitelub.lite) do projektu Xcode, pamiętając, aby wybraćCopy 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];
Konfigurowanie zdalnego źródła modelu
Aby użyć 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 wdrażaniu znajdziesz w przewodniku po migracji z Firebase ML do Cloud Storage.
Konfigurowanie narzędzia do etykietowania obrazów
Po skonfigurowaniu źródeł modelu utwórz z jednego z nich obiekt ImageLabeler.
Dostępne są te ustawienia:
| Opcje | |
|---|---|
confidenceThreshold
|
Minimalny poziom ufności wykrytych etykiet. Jeśli nie zostanie ustawiony, użyty zostanie dowolny 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. |
maxResultCount
|
Maksymalna liczba etykiet do zwrócenia. Jeśli nie zostanie ustawiony, użyta zostanie wartość domyślna 10. |
Jeśli masz tylko model dołączony lokalnie, utwórz narzędzie do etykietowania z obiektu LocalModel:
Swift
let options = CustomImageLabelerOptions(localModel: localModel) options.confidenceThreshold = NSNumber(value: 0.0) let imageLabeler = ImageLabeler.imageLabeler(options: options)
Objective-C
MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; options.confidenceThreshold = @(0.0); MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options];
Jeśli masz model hostowany zdalnie, przed jego uruchomieniem musisz sprawdzić, czy został pobrany.
Chociaż musisz potwierdzić to tylko przed uruchomieniem etykietowania, jeśli masz zarówno model hostowany zdalnie, jak i model dołączony lokalnie, warto przeprowadzić to sprawdzenie podczas tworzenia instancji ImageLabeler: utwórz etykietowanie na podstawie modelu zdalnego, jeśli został on pobrany, a w przeciwnym razie na podstawie 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 = CustomImageLabelerOptions(localModel: model) let imageLabeler = ImageLabeler.imageLabeler(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]; } MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:model]; MLKImageLabeler *imageLabeler = [MLKImageLabeler imageLabelerWithOptions: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.initializeLabeler(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 labeler self.initializeLabeler(with: modelURL) } } } func initializeLabeler(with modelURL: URL) { let localModel = LocalModel(path: modelURL.path) let options = CustomImageLabelerOptions(localModel: localModel) self.imageLabeler = ImageLabeler.imageLabeler(options: options) // Enable ML-related UI features here 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 initializeLabelerWithURL: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 labeler [self initializeLabelerWithURL:URL]; } }]; } - (void)initializeLabelerWithURL:(NSURL *)modelURL { MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path]; MLKCustomImageLabelerOptions *options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel]; self.imageLabeler = [MLKImageLabeler imageLabelerWithOptions:options]; // Enable ML-related UI features here [self enableMLFeatures]; }
2. Przygotowywanie obrazu wejściowego
Utwórz obiekt VisionImage za pomocą UIImage lub CMSampleBuffer.
Jeśli używasz UIImage, wykonaj te czynności:
- Utwórz obiekt
VisionImagez wartościąUIImage. Pamiętaj, aby podać prawidłowy.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, używając 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];
3. Uruchomienie narzędzia do etykietowania obrazów
Aby oznaczyć obiekty na obrazie, przekaż obiekt image do metody ImageLabelerprocess().
Asynchronicznie:
Swift
imageLabeler.process(image) { labels, error in guard error == nil, let labels = labels, !labels.isEmpty else { // Handle the error. return } // Show results. }
Objective-C
[imageLabeler processImage:image completion:^(NSArray*_Nullable labels, NSError *_Nullable error) { if (label.count == 0) { // Handle the error. return; } // Show results. }];
Synchronicznie:
Swift
var labels: [ImageLabel] do { labels = try imageLabeler.results(in: image) } catch let error { // Handle the error. return } // Show results.
Objective-C
NSError *error; NSArray*labels = [imageLabeler resultsInImage:image error:&error]; // Show results or handle the error.
4. Uzyskiwanie informacji o oznaczonych encjach
Jeśli operacja etykietowania obrazu się powiedzie, zwróci tablicę obiektówImageLabel. Każdy symbol ImageLabel reprezentuje coś, co zostało oznaczone na obrazie. Możesz uzyskać tekstowy opis każdej etykiety (jeśli jest dostępny w metadanych pliku modelu LiteRT), wskaźnik ufności i indeks. Przykład:
Swift
for label in labels { let labelText = label.text let confidence = label.confidence let index = label.index }
Objective-C
for (MLKImageLabel *label in labels) { NSString *labelText = label.text; float confidence = label.confidence; NSInteger index = label.index; }
Wskazówki dotyczące poprawy skuteczności w czasie rzeczywistym
Jeśli chcesz oznaczać obrazy w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi wytycznymi, aby uzyskać najlepszą liczbę klatek na sekundę:
- Do przetwarzania klatek wideo używaj
results(in:)synchronicznego interfejsu API detektora. Wywołaj tę metodę z funkcjiAVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput(_, didOutput:from:), aby synchronicznie uzyskać wyniki z danej klatki filmu. UstawAVCaptureVideoDataOutputalwaysDiscardsLateVideoFramesnatrue, aby ograniczyć liczbę wywołań detektora. Jeśli podczas działania detektora pojawi się nowa klatka wideo, zostanie ona odrzucona. - Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a potem w jednym kroku wyrenderuj obraz i nałóż na niego grafikę. Dzięki temu renderujesz na powierzchnię wyświetlania tylko raz dla każdej przetworzonej klatki wejściowej. Przykład znajdziesz w funkcji updatePreviewOverlayViewWithLastFrame w przykładowej aplikacji ML Kit na początek.