AutoML Vision Edge'i kullanarak kendi modelinizi eğittikten sonra, bu modeli görüntülerdeki nesneleri algılamak için uygulamanızda kullanabilirsiniz.
AutoML Vision Edge'den eğitilen modelleri iki şekilde entegre edebilirsiniz. Modelin dosyalarını Xcode projenize kopyalayarak modeli paketleyebilir veya Firebase'den dinamik olarak indirebilirsiniz.
Model paketleme seçenekleri | |
---|---|
Uygulamanızda paket halinde |
|
Firebase ile barındırılan |
|
Başlamadan önce
Model indirmek istiyorsanız henüz yapmadıysanız Firebase'i Apple projenize eklediğinizden emin olun. Modeli paket haline getirirken bu işlem gerekli değildir.
Pod dosyanıza TensorFlow ve Firebase kitaplıklarını ekleyin:
Bir modeli uygulamanızla paketlemek için:
Swift
pod 'TensorFlowLiteSwift'
Objective-C
pod 'TensorFlowLiteObjC'
Firebase'den dinamik olarak model indirmek için
Firebase/MLModelInterpreter
bağımlılığını ekleyin:Swift
pod 'TensorFlowLiteSwift' pod 'Firebase/MLModelInterpreter'
Objective-C
pod 'TensorFlowLiteObjC' pod 'Firebase/MLModelInterpreter'
Projenizin Pod'larını yükledikten veya güncelledikten sonra
.xcworkspace
kullanarak Xcode projenizi açın.
1. Modeli yükleme
Yerel model kaynağını yapılandırma
Modeli uygulamanızla birlikte paketlemek için model ve etiketler dosyasını Xcode projenize kopyalayın. Bu işlemi yaparken Klasör referansı oluştur'u seçtiğinizden emin olun. Model dosyası ve etiketler uygulama paketine dahil edilir.
Ayrıca, modelle birlikte oluşturulan tflite_metadata.json
dosyasına da bakın. İki değere ihtiyacınız vardır:
- Modelin giriş boyutları. Bu değer varsayılan olarak 320x320'tir.
- Modelin maksimum algılama sayısı. Bu değer varsayılan olarak 40'tır.
Firebase tarafından barındırılan bir model kaynağını yapılandırma
Uzaktan barındırılan modeli kullanmak için modeli yayınlarken atadığınız adı belirten bir CustomRemoteModel
nesnesi oluşturun:
Swift
let remoteModel = CustomRemoteModel(
name: "your_remote_model" // The name you assigned in the Google Cloud console.
)
Objective-C
FIRCustomRemoteModel *remoteModel = [[FIRCustomRemoteModel alloc]
initWithName:@"your_remote_model"];
Ardından, indirmeye izin vermek istediğiniz koşulları belirterek model indirme görevini başlatın. Model cihazda yoksa veya modelin daha yeni bir sürümü varsa görev, modeli Firebase'den eşzamansız olarak indirir:
Swift
let downloadProgress = ModelManager.modelManager().download(
remoteModel,
conditions: ModelDownloadConditions(
allowsCellularAccess: true,
allowsBackgroundDownloading: true
)
)
Objective-C
FIRModelDownloadConditions *conditions =
[[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
allowsBackgroundDownloading:YES];
NSProgress *progress = [[FIRModelManager modelManager] downloadModel:remoteModel
conditions:conditions];
Birçok uygulama, indirme görevini başlatma kodunda başlatır ancak modeli kullanmadan önce istediğiniz zaman bu işlemi yapabilirsiniz.
Modelinizden nesne algılayıcı oluşturma
Model kaynaklarınızı yapılandırdıktan sonra, bu kaynakların birinden TensorFlow Lite Interpreter
nesnesi oluşturun.
Yalnızca yerel olarak paketlenmiş bir modeliniz varsa model dosyasından yorumlayıcı oluşturmanız yeterlidir:
Swift
guard let modelPath = Bundle.main.path(
forResource: "model",
ofType: "tflite"
) else {
print("Failed to load the model file.")
return true
}
let interpreter = try Interpreter(modelPath: modelPath)
try interpreter.allocateTensors()
Objective-C
NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
ofType:@"tflite"];
NSError *error;
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
error:&error];
if (error != NULL) { return; }
[interpreter allocateTensorsWithError:&error];
if (error != NULL) { return; }
Uzaktan barındırılan bir modeliniz varsa çalıştırmadan önce modelin indirildiğinden emin olmanız gerekir. Model yöneticisinin isModelDownloaded(remoteModel:)
yöntemini kullanarak model indirme görevinin durumunu kontrol edebilirsiniz.
Bunu yalnızca yorumlayıcıyı çalıştırmadan önce onaylamanız gerekir. Ancak hem uzaktan barındırılan hem de yerel olarak paketlenmiş bir modeliniz varsa Interpreter
öğesini örneklendirirken bu kontrolü gerçekleştirmeniz yararlı olabilir: İndirilmişse uzak modelden, aksi takdirde yerel modelden bir yorumlayıcı oluşturun.
Swift
var modelPath: String?
if ModelManager.modelManager().isModelDownloaded(remoteModel) {
ModelManager.modelManager().getLatestModelFilePath(remoteModel) { path, error in
guard error == nil else { return }
guard let path = path else { return }
modelPath = path
}
} else {
modelPath = Bundle.main.path(
forResource: "model",
ofType: "tflite"
)
}
guard modelPath != nil else { return }
let interpreter = try Interpreter(modelPath: modelPath)
try interpreter.allocateTensors()
Objective-C
__block NSString *modelPath;
if ([[FIRModelManager modelManager] isModelDownloaded:remoteModel]) {
[[FIRModelManager modelManager] getLatestModelFilePath:remoteModel
completion:^(NSString * _Nullable filePath,
NSError * _Nullable error) {
if (error != NULL) { return; }
if (filePath == NULL) { return; }
modelPath = filePath;
}];
} else {
modelPath = [[NSBundle mainBundle] pathForResource:@"model"
ofType:@"tflite"];
}
NSError *error;
TFLInterpreter *interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath
error:&error];
if (error != NULL) { return; }
[interpreter allocateTensorsWithError:&error];
if (error != NULL) { return; }
Yalnızca uzaktan barındırılan bir modeliniz varsa modelin indirildiğini onaylayana kadar modelle ilgili işlevleri (ör. kullanıcı arayüzünüzün bir bölümünü devre dışı bırakma veya gizleme) devre dışı bırakmanız gerekir.
Varsayılan bildirim merkezine gözlemciler ekleyerek model indirme durumunu öğrenebilirsiniz. İndirmeler biraz zaman alabileceğinden ve indirme işlemi tamamlandığında kaynak nesne serbest bırakılabileceği için gözlemci blokunda self
için zayıf bir referans kullandığınızdan emin olun. Örneğin:
Swift
NotificationCenter.default.addObserver(
forName: .firebaseMLModelDownloadDidSucceed,
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: .firebaseMLModelDownloadDidFail,
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:FIRModelDownloadDidSucceedNotification
object:nil
queue:nil
usingBlock:^(NSNotification *_Nonnull note) {
if (weakSelf == nil | note.userInfo == nil) {
return;
}
__strong typeof(self) strongSelf = weakSelf;
FIRRemoteModel *model = note.userInfo[FIRModelDownloadUserInfoKeyRemoteModel];
if ([model.name isEqualToString:@"your_remote_model"]) {
// The model was downloaded and is available on the device
}
}];
[NSNotificationCenter.defaultCenter
addObserverForName:FIRModelDownloadDidFailNotification
object:nil
queue:nil
usingBlock:^(NSNotification *_Nonnull note) {
if (weakSelf == nil | note.userInfo == nil) {
return;
}
__strong typeof(self) strongSelf = weakSelf;
NSError *error = note.userInfo[FIRModelDownloadUserInfoKeyError];
}];
2. Giriş resmini hazırlama
Ardından, resimlerinizi TensorFlow Lite yorumlayıcısı için hazırlamanız gerekir.
Resmi,
tflite_metadata.json
dosyasında belirtildiği gibi modelin giriş boyutlarına göre kırpın ve ölçeklendirin (varsayılan olarak 320x320 piksel). Bunu Core Image veya üçüncü taraf kitaplığı ile yapabilirsiniz.Resim verilerini bir
Data
(NSData
nesnesi) içine kopyalayın:Swift
guard let image: CGImage = // Your input image guard let context = CGContext( data: nil, width: image.width, height: image.height, bitsPerComponent: 8, bytesPerRow: image.width * 4, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue ) else { return nil } context.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height)) guard let imageData = context.data else { return nil } var inputData = Data() for row in 0 ..< 320 { // Model takes 320x320 pixel images as input for col in 0 ..< 320 { let offset = 4 * (col * context.width + row) // (Ignore offset 0, the unused alpha channel) var red = imageData.load(fromByteOffset: offset+1, as: UInt8.self) var green = imageData.load(fromByteOffset: offset+2, as: UInt8.self) var blue = imageData.load(fromByteOffset: offset+3, as: UInt8.self) inputData.append(&red, count: 1) inputData.append(&green, count: 1) inputData.append(&blue, count: 1) } }
Objective-C
CGImageRef image = // Your input image long imageWidth = CGImageGetWidth(image); long imageHeight = CGImageGetHeight(image); CGContextRef context = CGBitmapContextCreate(nil, imageWidth, imageHeight, 8, imageWidth * 4, CGColorSpaceCreateDeviceRGB(), kCGImageAlphaNoneSkipFirst); CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image); UInt8 *imageData = CGBitmapContextGetData(context); NSMutableData *inputData = [[NSMutableData alloc] initWithCapacity:0]; for (int row = 0; row < 300; row++) { for (int col = 0; col < 300; col++) { long offset = 4 * (row * imageWidth + col); // (Ignore offset 0, the unused alpha channel) UInt8 red = imageData[offset+1]; UInt8 green = imageData[offset+2]; UInt8 blue = imageData[offset+3]; [inputData appendBytes:&red length:1]; [inputData appendBytes:&green length:1]; [inputData appendBytes:&blue length:1]; } }
3. Nesne algılayıcıyı çalıştırma
Ardından, hazırlanmış girişi yorumcuya iletin:
Swift
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Objective-C
TFLTensor *input = [interpreter inputTensorAtIndex:0 error:&error];
if (error != nil) { return; }
[input copyData:inputData error:&error];
if (error != nil) { return; }
[interpreter invokeWithError:&error];
if (error != nil) { return; }
4. Algılanan nesneler hakkında bilgi alma
Nesne algılama başarılı olursa model, çıkış olarak her biri 40 öğeden (veya tflite_metadata.json
dosyasında belirtilenden) oluşan üç dizi oluşturur.
Her öğe bir potansiyel nesneye karşılık gelir. İlk dizi bir sınırlayıcı kutu dizisi, ikincisi bir etiket dizisi ve üçüncüsü bir güven değerleri dizisidir. Model çıkışlarını almak için:
Swift
var output = try interpreter.output(at: 0)
let boundingBoxes =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 4 * 40)
output.data.copyBytes(to: boundingBoxes)
output = try interpreter.output(at: 1)
let labels =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 40)
output.data.copyBytes(to: labels)
output = try interpreter.output(at: 2)
let probabilities =
UnsafeMutableBufferPointer<Float32>.allocate(capacity: 40)
output.data.copyBytes(to: probabilities)
Objective-C
TFLTensor *output = [interpreter outputTensorAtIndex:0 error:&error];
if (error != nil) { return; }
NSData *boundingBoxes = [output dataWithError:&error];
if (error != nil) { return; }
output = [interpreter outputTensorAtIndex:1 error:&error];
if (error != nil) { return; }
NSData *labels = [output dataWithError:&error];
if (error != nil) { return; }
output = [interpreter outputTensorAtIndex:2 error:&error];
if (error != nil) { return; }
NSData *probabilities = [output dataWithError:&error];
if (error != nil) { return; }
Ardından, etiket çıktılarını etiket sözlüğünüzle birleştirebilirsiniz:
Swift
guard let labelPath = Bundle.main.path(
forResource: "dict",
ofType: "txt"
) else { return true }
let fileContents = try? String(contentsOfFile: labelPath)
guard let labelText = fileContents?.components(separatedBy: "\n") else { return true }
for i in 0 ..< 40 {
let top = boundingBoxes[0 * i]
let left = boundingBoxes[1 * i]
let bottom = boundingBoxes[2 * i]
let right = boundingBoxes[3 * i]
let labelIdx = Int(labels[i])
let label = labelText[labelIdx]
let confidence = probabilities[i]
if confidence > 0.66 {
print("Object found: \(label) (confidence: \(confidence))")
print(" Top-left: (\(left),\(top))")
print(" Bottom-right: (\(right),\(bottom))")
}
}
Objective-C
NSString *labelPath = [NSBundle.mainBundle pathForResource:@"dict"
ofType:@"txt"];
NSString *fileContents = [NSString stringWithContentsOfFile:labelPath
encoding:NSUTF8StringEncoding
error:&error];
if (error != nil || fileContents == NULL) { return; }
NSArray<NSString*> *labelText = [fileContents componentsSeparatedByString:@"\n"];
for (int i = 0; i < 40; i++) {
Float32 top, right, bottom, left;
Float32 labelIdx;
Float32 confidence;
[boundingBoxes getBytes:&top range:NSMakeRange(16 * i + 0, 4)];
[boundingBoxes getBytes:&left range:NSMakeRange(16 * i + 4, 4)];
[boundingBoxes getBytes:&bottom range:NSMakeRange(16 * i + 8, 4)];
[boundingBoxes getBytes:&right range:NSMakeRange(16 * i + 12, 4)];
[labels getBytes:&labelIdx range:NSMakeRange(4 * i, 4)];
[probabilities getBytes:&confidence range:NSMakeRange(4 * i, 4)];
if (confidence > 0.5f) {
NSString *label = labelText[(int)labelIdx];
NSLog(@"Object detected: %@", label);
NSLog(@" Confidence: %f", confidence);
NSLog(@" Top-left: (%f,%f)", left, top);
NSLog(@" Bottom-right: (%f,%f)", right, bottom);
}
}
Gerçek zamanlı performansı iyileştirmeye yönelik ipuçları
Gerçek zamanlı bir uygulamada resimleri etiketlemek istiyorsanız en iyi kare hızlarına ulaşmak için aşağıdaki yönergeleri uygulayın:
- Dedektöre yapılan çağrıları azaltın. Algılayıcı çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın.
- Giriş resmine grafik yerleştirmek için dedektörün çıkışını kullanıyorsanız önce sonucu alın, ardından resmi oluşturup tek bir adımda yer paylaşımı yapın. Böylece, her giriş çerçevesi için ekran yüzeyinde yalnızca bir kez oluşturma işlemi gerçekleştirirsiniz. Örnek olarak, vitrin örnek uygulamasındaki previewOverlayView sınıfına ve FIRDetectionOverlayView sınıfına bakın.