Po wytrenowaniu własnego modelu z użyciem AutoML Vision Edge możesz go używać w aplikacji do oznaczania obrazów etykietami.
Istnieją 2 sposoby integracji modeli wytrenowanych w AutoML Vision Edge. Możesz połączyć model, kopiując jego pliki do projektu Xcode, albo pobierać go dynamicznie z Firebase.
Opcje grupowania modeli | |
---|---|
Pakiet w aplikacji |
|
Hostowane w Firebase |
|
Zanim zaczniesz
Umieść biblioteki ML Kit w pliku Podfile:
Aby dołączyć model do aplikacji:
pod 'GoogleMLKit/ImageLabelingCustom'
Aby dynamicznie pobierać model z Firebase, dodaj zależność
LinkFirebase
:pod 'GoogleMLKit/ImageLabelingCustom' pod 'GoogleMLKit/LinkFirebase'
Po zainstalowaniu lub zaktualizowaniu podów projektu otwórz projekt Xcode za pomocą zasobu
.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 swojego projektu na Androida, chyba że masz to już za sobą. Nie jest to wymagane przy pakowaniu modelu.
1. Wczytaj model
Skonfiguruj źródło modelu lokalnego
Aby połączyć model z aplikacją:
Rozpakuj model i jego metadane z archiwum ZIP pobranego z konsoli Firebase do folderu:
your_model_directory |____dict.txt |____manifest.json |____model.tflite
Wszystkie 3 pliki muszą znajdować się w tym samym folderze. Zalecamy korzystanie z plików w momencie ich pobrania, bez modyfikacji (dotyczy to również nazw plików).
Skopiuj folder do projektu Xcode, zaznaczając opcję Utwórz odwołania do folderów. Plik modelu i metadane zostaną uwzględnione w pakiecie aplikacji i 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];
Skonfiguruj źródło modelu hostowanego w Firebase
Aby używać modelu hostowanego zdalnie, utwórz obiekt CustomRemoteModel
z nazwą przypisaną do modelu podczas jego publikacji:
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 zadanie pobierania modelu, określając warunki, które muszą zostać spełnione, żeby można było pobierać dane. Jeśli modelu nie ma na urządzeniu lub jeśli jest dostępna jego nowsza wersja, zadanie pobierze go asynchronicznie 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 zadanie pobierania w kodzie inicjowania, ale możesz to zrobić w dowolnym momencie, zanim trzeba będzie użyć modelu.
Tworzenie osoby oznaczającej obrazy na podstawie modelu
Po skonfigurowaniu źródeł modeli utwórz na ich podstawie obiekt ImageLabeler
.
Jeśli masz tylko model dołączony lokalnie, po prostu utwórz osobę oznaczającą etykietami na podstawie obiektu LocalModel
i skonfiguruj próg ufności, który ma być wymagany (zobacz 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 uruchomieniem musisz sprawdzić, czy został pobrany. Stan zadania pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded(remoteModel:)
menedżera modeli.
Chociaż musisz to potwierdzić przed uruchomieniem osoby oznaczającej etykietami, jeśli masz zarówno model hostowany zdalnie, jak i model umieszczony lokalnie, warto przeprowadzić tę kontrolę podczas tworzenia wystąpienia ImageLabeler
: utwórz etykietę na podstawie modelu zdalnego (jeśli został pobrany) lub z modelu lokalnego.
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 związane z nim funkcje – na przykład wyszarzanie lub ukrycie części interfejsu użytkownika – do czasu potwierdzenia pobrania modelu.
Stan pobierania modelu możesz uzyskać, dołączając obserwatorów do domyślnego Centrum powiadomień. Pamiętaj, aby w bloku obserwatora używać słabego odniesienia do self
, ponieważ pobieranie może trochę potrwać, a obiekt źródłowy może zostać uwolniony do zakończenia pobierania. 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
za pomocą interfejsuUIImage
. Pamiętaj, by określić 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 sprawdzić 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
, korzystając z 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 oznaczania 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.
}];
Synchronnie:
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 obiektach oznaczonych etykietami
Jeśli dodanie etykiet do obrazów się powiedzie, zwróci tablicę ImageLabel
. Każdy element ImageLabel
odpowiada elementowi,
co jest oznaczone na obrazku. Możesz uzyskać opis tekstowy 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 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:
- Ogranicz wywołania do detektora. Jeśli podczas działania detektora dostępna będzie nowa klatka wideo, upuść ją.
- Jeśli używasz danych wyjściowych wzorca do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik, a potem w jednym kroku wyrenderuj obraz i nakładkę. Dzięki temu renderowanie na powierzchni wyświetlania będzie odbywać się tylko raz na każdą klatkę wejściową. Przykład znajdziesz w klasach previewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji z funkcją prezentacji.