AutoML Vision Edge'i kullanarak kendi modelinizi eğittikten sonra bunu uygulamanızda görüntülerdeki nesneleri algılamak için kullanabilirsiniz.
AutoML Vision Edge'den eğitilen modelleri entegre etmenin iki yolu vardır. Modelin dosyalarını Xcode projenize kopyalayarak modeli paketleyebilir veya Firebase'den dinamik olarak indirebilirsiniz.
Model paketleme seçenekleri | |
---|---|
Uygulamanızda paketlenmiştir |
|
Firebase'de barındırılıyor |
|
Sen başlamadan önce
Bir model indirmek istiyorsanız , henüz yapmadıysanız Firebase'i Apple projenize eklediğinizden emin olun. Modeli paketlediğinizde buna gerek yoktur.
TensorFlow ve Firebase kitaplıklarını Pod dosyanıza ekleyin:
Bir modeli uygulamanızla birlikte paketlemek için:
Süratli
pod 'TensorFlowLiteSwift'
Amaç-C
pod 'TensorFlowLiteObjC'
Firebase'den dinamik olarak bir model indirmek için
Firebase/MLModelInterpreter
bağımlılığını ekleyin:Süratli
pod 'TensorFlowLiteSwift' pod 'Firebase/MLModelInterpreter'
Amaç-C
pod 'TensorFlowLiteObjC' pod 'Firebase/MLModelInterpreter'
Projenizin Pod'larını yükledikten veya güncelledikten sonra Xcode projenizi
.xcworkspace
kullanarak açın.
1. Modeli yükleyin
Yerel bir model kaynağı yapılandırma
Modeli uygulamanızla birlikte paketlemek için modeli ve etiketler dosyasını Xcode projenize kopyalayın ve bunu yaparken Klasör referansları oluştur'u seçmeye dikkat edin. Model dosyası ve etiketler uygulama paketine dahil edilecektir.
Ayrıca modelle birlikte oluşturulan tflite_metadata.json
dosyasına bakın. İki değere ihtiyacınız var:
- Modelin giriş boyutları. Bu, varsayılan olarak 320x320'dir.
- Modelin maksimum algılamaları. Bu varsayılan olarak 40'tır.
Firebase tarafından barındırılan bir model kaynağı yapılandırma
Uzaktan barındırılan modeli kullanmak için, modeli yayınlarken atadığınız adı belirterek bir CustomRemoteModel
nesnesi oluşturun:
Süratli
let remoteModel = CustomRemoteModel(
name: "your_remote_model" // The name you assigned in the Google Cloud console.
)
Amaç-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ü mevcutsa görev, modeli Firebase'den eşzamansız olarak indirir:
Süratli
let downloadProgress = ModelManager.modelManager().download(
remoteModel,
conditions: ModelDownloadConditions(
allowsCellularAccess: true,
allowsBackgroundDownloading: true
)
)
Amaç-C
FIRModelDownloadConditions *conditions =
[[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
allowsBackgroundDownloading:YES];
NSProgress *progress = [[FIRModelManager modelManager] downloadModel:remoteModel
conditions:conditions];
Çoğu uygulama, indirme görevini kendi başlatma kodunda başlatır, ancak bunu, modeli kullanmanız gerekmeden önce herhangi bir noktada yapabilirsiniz.
Modelinizden bir nesne algılayıcı oluşturun
Model kaynaklarınızı yapılandırdıktan sonra bunlardan birinden bir TensorFlow Lite Interpreter
nesnesi oluşturun.
Yalnızca yerel olarak paketlenmiş bir modeliniz varsa, model dosyasından bir yorumlayıcı oluşturmanız yeterlidir:
Süratli
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()
Amaç-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 indirilip indirilmediğini kontrol etmeniz gerekecektir. Model yöneticisinin isModelDownloaded(remoteModel:)
yöntemini kullanarak model indirme görevinin durumunu kontrol edebilirsiniz.
Her ne kadar yorumlayıcıyı çalıştırmadan önce bunu yalnızca onaylamanız gerekse de, hem uzaktan barındırılan bir modeliniz hem de yerel olarak paketlenmiş bir modeliniz varsa, Interpreter
başlatırken bu kontrolü gerçekleştirmek mantıklı olabilir: eğer uzak modelden bir yorumlayıcı oluşturun indirilmiş ve aksi takdirde yerel modelden.
Süratli
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()
Amaç-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 devre dışı bırakmalısınız (örneğin, kullanıcı arayüzünüzün bir kısmını grileştirme veya gizleme).
Gözlemcileri varsayılan Bildirim Merkezine ekleyerek model indirme durumunu alabilirsiniz. İndirme işlemleri biraz zaman alabileceğinden ve indirme işlemi tamamlandığında kaynak nesne serbest bırakılabileceğinden, gözlemci bloğunda self
zayıf bir referans kullandığınızdan emin olun. Örneğin:
Süratli
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]
// ...
}
Amaç-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ş görüntüsünü hazırlayın
Daha sonra görüntülerinizi TensorFlow Lite yorumlayıcısı için hazırlamanız gerekir.
Görüntüyü,
tflite_metadata.json
dosyasında belirtildiği gibi (varsayılan olarak 320x320 piksel) modelin giriş boyutlarına göre kırpın ve ölçeklendirin. Bunu Core Image veya üçüncü taraf bir kitaplıkla yapabilirsiniz.Görüntü verilerini bir
Data
(NSData
nesnesine) kopyalayın:Süratli
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) } }
Amaç-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 dedektörünü çalıştırın
Daha sonra hazırlanan girişi tercümana iletin:
Süratli
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
Amaç-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 alın
Nesne tespiti başarılı olursa, model çıktı olarak her biri 40 öğeden (veya tflite_metadata.json
dosyasında belirtilenlerden) oluşan üç dizi üretir. Her öğe bir potansiyel nesneye karşılık gelir. İlk dizi, sınırlayıcı kutulardan oluşan bir dizidir; ikincisi, bir dizi etiket; ve üçüncüsü, bir dizi güven değeri. Model çıktılarını almak için:
Süratli
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)
Amaç-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; }
Daha sonra etiket çıktılarını etiket sözlüğünüzle birleştirebilirsiniz:
Süratli
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))")
}
}
Amaç-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ı artırmaya yönelik ipuçları
Görüntüleri gerçek zamanlı bir uygulamada etiketlemek istiyorsanız en iyi kare hızlarına ulaşmak için şu yönergeleri izleyin:
- Gaz kelebeği dedektöre çağrı yapar. Dedektör çalışırken yeni bir video karesi kullanılabilir hale gelirse kareyi bırakın.
- Dedektörün çıkışını grafikleri giriş görüntüsüne yerleştirmek için kullanıyorsanız, önce sonucu alın, ardından tek adımda görüntüyü oluşturun ve üst üste koyun. Bunu yaparak, her giriş karesi için ekran yüzeyini yalnızca bir kez görüntüleyebilirsiniz. Örnek için vitrin örnek uygulamasındaki önizlemeOverlayView ve FIRDetectionOverlayView sınıflarına bakın.