Po wytrenowaniu własnego modelu za pomocą AutoML Vision Edge możesz używać go w aplikacji do oznaczania obrazów etykietami.
Modele wytrenowane w AutoML Vision Edge można zintegrować na 2 sposoby. Model możesz spakować, kopiując pliki modelu do projektu Xcode, lub pobrać go dynamicznie z Firebase.
Opcje pakietów modeli | |
---|---|
Dołączone do aplikacji |
|
Hostowane w Firebase |
|
Zanim zaczniesz
Dodaj biblioteki ML Kit do pliku Podfile:
W przypadku łączenia modelu z aplikacją:
pod 'GoogleMLKit/ImageLabelingCustom'
Aby dynamicznie pobrać model z Firebase, dodaj zależność
LinkFirebase
:pod 'GoogleMLKit/ImageLabelingCustom' pod 'GoogleMLKit/LinkFirebase'
Po zainstalowaniu lub zaktualizowaniu Pods w projekcie otwórz projekt Xcode za pomocą ikony
.xcworkspace
. ML Kit jest obsługiwany w Xcode w wersji 12.2 lub nowszej.Jeśli chcesz pobrać model, pamiętaj, aby dodać Firebase do projektu na Androida, 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ą:
Wyodrębnij model i jego metadane z pobranego z Firebase konsoli archiwum ZIP do folderu:
your_model_directory |____dict.txt |____manifest.json |____model.tflite
Wszystkie 3 pliki muszą znajdować się w tym samym folderze. Zalecamy używanie pobranych plików bez modyfikacji (w tym bez zmiany nazw plików).
Skopiuj folder do projektu Xcode, pamiętając, aby wybrać opcję Create folder references (Utwórz odwołania do folderu). Plik modelu i metadane zostaną uwzględnione w pakiecie aplikacji i będą dostępne dla ML Kit.
Utwórz obiekt
LocalModel
, podając ścieżkę do pliku manifestu modelu:Swift
guard let manifestPath = Bundle.main.path( forResource: "manifest", ofType: "json", inDirectory: "your_model_directory" ) else { return true } let localModel = LocalModel(manifestPath: manifestPath)
Objective-C
NSString *manifestPath = [NSBundle.mainBundle pathForResource:@"manifest" ofType:@"json" inDirectory:@"your_model_directory"]; MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithManifestPath:manifestPath];
Konfigurowanie źródła modelu hostowanego w Firebase
Aby użyć modelu hostowanego zdalnie, utwórz obiekt CustomRemoteModel
, podając nazwę, którą przypisano do modelu podczas publikowania:
Swift
// Initialize the model source with the name you assigned in
// the Firebase console.
let remoteModelSource = FirebaseModelSource(name: "your_remote_model")
let remoteModel = CustomRemoteModel(remoteModelSource: remoteModelSource)
Objective-C
// Initialize the model source with the name you assigned in
// the Firebase console.
MLKFirebaseModelSource *firebaseModelSource =
[[MLKFirebaseModelSource alloc] initWithName:@"your_remote_model"];
MLKCustomRemoteModel *remoteModel =
[[MLKCustomRemoteModel alloc] initWithRemoteModelSource:firebaseModelSource];
Następnie rozpocznij pobieranie modelu, określając warunki, w których chcesz zezwolić na pobieranie. Jeśli modelu nie ma na urządzeniu lub dostępna jest nowsza wersja, zadanie asynchronicznie pobierze 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] downloadRemoteModel:remoteModel
conditions:downloadConditions];
Wiele aplikacji rozpoczyna pobieranie w kodzie inicjującym, ale możesz to zrobić w dowolnym momencie przed użyciem modelu.
Tworzenie narzędzia do oznaczania obrazów na podstawie modelu
Po skonfigurowaniu źródeł modelu utwórz z jednego z nich obiekt ImageLabeler
.
Jeśli masz tylko model dołączony lokalnie, utwórz narzędzie do etykietowania z obiektu LocalModel
i skonfiguruj próg wyniku ufności, który chcesz wymagać (patrz Ocena modelu):
Swift
let options = CustomImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0) // Evaluate your model in the Cloud console
// to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options)
Objective-C
CustomImageLabelerOptions *options =
[[CustomImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0f); // Evaluate your model in the Cloud console
// to determine an appropriate value.
MLKImageLabeler *imageLabeler =
[MLKImageLabeler imageLabelerWithOptions:options];
Jeśli masz model hostowany zdalnie, przed jego uruchomieniem musisz sprawdzić, czy został pobrany. Stan zadania pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded(remoteModel:)
menedżera modeli.
Chociaż musisz potwierdzić to tylko przed uruchomieniem narzędzia do etykietowania, jeśli masz zarówno model hostowany zdalnie, jak i model dołączony lokalnie, warto przeprowadzić to sprawdzenie podczas tworzenia instancji narzędzia do etykietowania: utwórz narzędzie do etykietowania na podstawie modelu zdalnego, jeśli został on pobrany, a w przeciwnym razie na podstawie modelu lokalnego.ImageLabeler
Swift
var options: CustomImageLabelerOptions
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
options = CustomImageLabelerOptions(remoteModel: remoteModel)
} else {
options = CustomImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = NSNumber(value: 0.0) // Evaluate your model in the Firebase console
// to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options: options)
Objective-C
MLKCustomImageLabelerOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
options = [[MLKCustomImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = @(0.0f); // Evaluate your model in the Firebase console
// to determine an appropriate value.
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.
Stan pobierania modelu możesz uzyskać, dołączając obserwatorów do domyślnego Centrum powiadomień. W bloku obserwatora używaj słabego odwołania do self
, ponieważ pobieranie może potrwać, a obiekt źródłowy może zostać zwolniony, zanim pobieranie się zakończy. Przykład:
Swift
NotificationCenter.default.addObserver(
forName: .mlkitMLModelDownloadDidSucceed,
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: .mlkitMLModelDownloadDidFail,
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];
}];
2. Przygotowywanie obrazu wejściowego
Utwórz obiekt VisionImage
za pomocą UIImage
lub CMSampleBufferRef
.
Jeśli używasz UIImage
, wykonaj te czynności:
- Utwórz obiekt
VisionImage
z właściwoś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 CMSampleBufferRef
, wykonaj te czynności:
-
Określ orientację danych obrazu zawartych w buforze
CMSampleBufferRef
.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 position == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored : UIImageOrientationRight; case UIDeviceOrientationLandscapeLeft: return position == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored : UIImageOrientationUp; case UIDeviceOrientationPortraitUpsideDown: return position == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored : UIImageOrientationLeft; case UIDeviceOrientationLandscapeRight: return position == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored : UIImageOrientationDown; case UIDeviceOrientationUnknown: case UIDeviceOrientationFaceUp: case UIDeviceOrientationFaceDown: return UIImageOrientationUp; } }
- Utwórz obiekt
VisionImage
, używając obiektuCMSampleBufferRef
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. Uruchamianie narzędzia do etykietowania obrazów
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<MLKImageLabel *> *_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<MLKImageLabel *> *labels =
[imageLabeler resultsInImage:image error:&error];
// Show results or handle the error.
4. Uzyskiwanie informacji o oznaczonych obiektach
Jeśli operacja etykietowania obrazu się powiedzie, zwróci tablicę obiektów
ImageLabel
. 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 TensorFlow Lite), 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 zwiększania 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ę:
- Ograniczanie liczby połączeń z wykrywaczem. Jeśli podczas działania detektora stanie się dostępna nowa klatka wideo, odrzuć ją.
- Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik, a potem w jednym kroku wyrenderuj obraz i nałóż na niego grafikę. Dzięki temu renderowanie na powierzchnię wyświetlania odbywa się tylko raz dla każdej ramki wejściowej. Przykład znajdziesz w klasach previewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji demonstracyjnej.