Apple platformlarında AutoML tarafından eğitilmiş bir modelle görüntülerdeki nesneleri algılama

AutoML Vision Edge'i kullanarak kendi modelinizi eğittikten sonra resimlerdeki nesneleri algılamak için uygulamanızda kullanabilirsiniz.

AutoML Vision Edge'den eğitilen modelleri iki şekilde entegre edebilirsiniz. Şunları yapabilirsiniz: model dosyalarını Xcode projenize kopyalayarak modeli paketleyebilir veya Firebase'den dinamik olarak indirebilir.

Model paketleme seçenekleri
Uygulamanızda paket olarak sunuluyor
  • Model, paketin bir parçasıdır
  • Model, Apple cihaz çevrimdışı olsa bile hemen kullanılabilir
  • Firebase projesine gerek yoktur
Firebase ile barındırılan
  • Modeli şu klasöre yükleyerek barındırın: Firebase Makine Öğrenimi
  • Uygulama paketi boyutunu küçültür
  • Model istek üzerine indirilir
  • Uygulamanızı yeniden yayınlamadan model güncellemelerini aktarma
  • Firebase Remote Config ile kolay A/B testi
  • Firebase projesi gerekir

Başlamadan önce

  1. Bir model indirmek istiyorsanız şunu yaptığınızdan emin olun: Firebase'i Apple projenize ekleyin, (onaylamadıysanız). model.

  2. TensorFlow ve Firebase kitaplıklarını Podfile dosyanıza ekleyin:

    Bir modeli uygulamanızla gruplandırmak için:

    Swift

    pod 'TensorFlowLiteSwift'
    

    Objective-C

    pod 'TensorFlowLiteObjC'
    

    Firebase'den bir modeli dinamik olarak indirmek için Firebase/MLModelInterpreter bağımlılık:

    Swift

    pod 'TensorFlowLiteSwift'
    pod 'Firebase/MLModelInterpreter'
    

    Objective-C

    pod 'TensorFlowLiteObjC'
    pod 'Firebase/MLModelInterpreter'
    
  3. Projenizin kapsüllerini yükledikten veya güncelledikten sonra Xcode projenizi açın (.xcworkspace) kullanılıyor.

1. Modeli yükleme

Yerel model kaynağını yapılandırma

Modeli uygulamanızla paket haline getirmek için modeli ve etiket dosyasını Xcode projenize kopyalayın. Klasör başvuruları oluşturun. Model dosyası ve etiketler dahil edilecek.

Ayrıca, tflite_metadata.json model. İki değere ihtiyacınız vardır:

  • Modelin giriş boyutları. Bu, varsayılan olarak 320x320'dir.
  • 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 bir CustomRemoteModel oluşturun. nesnesini tanımlarken modele atadığınız adı belirtin:

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, model indirme görevinizi başlatın ve çalıştırmayı istediğiniz koşulları indirmeye izin vermek istiyorsunuz. Model cihazda yoksa veya sürümü kullanılabiliyorsa görev, yeni bir sürümün yüklü olduğu modeliniz:

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 bunu, modeli kullanmaya başlamadan önce istediğiniz zaman yapabilirsiniz.

Modelinizden nesne algılayıcısı oluşturma

Model kaynaklarınızı yapılandırdıktan sonra TensorFlow Lite oluşturun Interpreter olabilir.

Yalnızca yerel olarak paketlenmiş bir modeliniz varsa, tek yapmanız gereken model dosyası:

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 bu modelin indiremezsiniz. Model indirme işleminin durumunu kontrol edebilirsiniz. model yöneticisinin isModelDownloaded(remoteModel:) yöntemini kullanarak görevi tamamlayın.

Çevirmeni çalıştırmadan önce bunu onaylamanız gerekse de hem uzaktan barındırılan hem de yerel olarak paketlenen Interpreter örneğini örneklendirirken şu kontrolü gerçekleştirmek mantıklıdır: indirilmiş olması durumunda uzak modelden ve yerel modelden aksi takdirde.

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 modelle ilgili ayarını devre dışı bırakmanız gerekir. devre dışı bırakana veya gizleyene kadar (örneğin, modelin indirildiğini onaylayın.

Varsayılan değere gözlemleyiciler ekleyerek model indirme durumunu öğrenebilirsiniz. Bildirim Merkezi. Gözlemcide self için zayıf bir referans kullandığınızdan emin olun biraz zaman alabilir ve kaynak nesne indirme tamamlandığında serbest bırakılır. Ö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, görüntülerinizi TensorFlow Lite çevirmeni için hazırlamanız gerekir.

  1. Resmi, aşağıda belirtildiği gibi modelin giriş boyutlarına göre kırpın ve ölçeklendirin tflite_metadata.json dosyası (varsayılan olarak 320x320 piksel). Şunları yapabilirsiniz: Temel Resim veya üçüncü taraf kitaplığı

  2. Resim verilerini bir Data (NSData nesne) 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ırladığınız girişi çevirmene 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, 40'lık üç çıkış dizisi olarak üretir. öğesi (veya tflite_metadata.json dosyasında belirtilen her şey) gösterilir. Her öğe bir potansiyel nesneye karşılık gelir. İlk dizi bir sınırlayıcı kutular dizisidir; ikincisi ise bir etiket dizisi; ve üçüncüsü 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 çıkış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ı

Görüntüleri gerçek zamanlı bir uygulamada etiketlemek isterseniz şu talimatları uygulayın:

  • Algılayıcıya yapılan çağrıları hızlandırın. Yeni bir video karesi kullanılabilir durumdaysa çerçeveyi bırakın.
  • Algılayıcının çıkışını üzerine grafik yerleştirmek için kullanıyorsanız önce sonucu alın, sonra da resmi oluşturun tek bir adımda yapabilirsiniz. Bu şekilde, oluşturduğunuz öğeleri ekran yüzeyinde her giriş karesi için yalnızca bir kez. Bkz. previewOverlayView ve FIRDetectionOverlayView sınıfları inceleyelim.