Ikuti semua informasi yang diumumkan di Firebase Summit, dan pelajari bagaimana Firebase dapat membantu Anda mempercepat pengembangan aplikasi dan menjalankan aplikasi dengan percaya diri. Pelajari Lebih Lanjut

Deteksi objek dalam gambar dengan model yang dilatih AutoML di platform Apple

Setelah melatih model Anda sendiri menggunakan AutoML Vision Edge , Anda dapat menggunakannya di aplikasi untuk mendeteksi objek dalam gambar.

Ada dua cara untuk mengintegrasikan model yang dilatih dari AutoML Vision Edge. Anda dapat memaketkan model dengan menyalin file model ke proyek Xcode Anda, atau Anda dapat mengunduhnya secara dinamis dari Firebase.

Opsi bundling model
Dibundel dalam aplikasi Anda
  • Model adalah bagian dari bundel
  • Modelnya langsung tersedia, bahkan saat perangkat Apple sedang offline
  • Tidak perlu proyek Firebase
Dihosting dengan Firebase
  • Host model dengan mengunggahnya ke Firebase Machine Learning
  • Mengurangi ukuran bundel aplikasi
  • Model diunduh sesuai permintaan
  • Dorong pembaruan model tanpa memublikasikan ulang aplikasi Anda
  • Pengujian A/B mudah dengan Firebase Remote Config
  • Memerlukan proyek Firebase

Sebelum kamu memulai

  1. Jika Anda ingin mendownload model , pastikan Anda menambahkan Firebase ke proyek Apple Anda , jika Anda belum melakukannya. Ini tidak diperlukan saat Anda memaketkan model.

  2. Sertakan pustaka TensorFlow dan Firebase di Podfile Anda:

    Untuk memaketkan model dengan aplikasi Anda:

    Cepat

    pod 'TensorFlowLiteSwift'
    

    Objective-C

    pod 'TensorFlowLiteObjC'
    

    Untuk mendownload model secara dinamis dari Firebase, tambahkan dependensi Firebase/MLModelInterpreter :

    Cepat

    pod 'TensorFlowLiteSwift'
    pod 'Firebase/MLModelInterpreter'
    

    Objective-C

    pod 'TensorFlowLiteObjC'
    pod 'Firebase/MLModelInterpreter'
    
  3. Setelah Anda menginstal atau memperbarui Pod proyek Anda, buka proyek Xcode Anda menggunakan .xcworkspace .

1. Muat model

Konfigurasikan sumber model lokal

Untuk memaketkan model dengan aplikasi Anda, salin file model dan label ke proyek Xcode Anda, berhati-hatilah untuk memilih Buat referensi folder saat Anda melakukannya. File dan label model akan disertakan dalam bundel aplikasi.

Juga, lihat file tflite_metadata.json yang dibuat di samping model. Anda memerlukan dua nilai:

  • Dimensi input model. Ini adalah 320x320 secara default.
  • Deteksi maksimum model. Ini adalah 40 secara default.

Konfigurasikan sumber model yang dihosting oleh Firebase

Untuk menggunakan model yang dihosting dari jarak jauh, buat objek CustomRemoteModel , dengan menyebutkan nama yang Anda tetapkan untuk model tersebut saat Anda memublikasikannya:

Cepat

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"];

Kemudian, mulai tugas pengunduhan model, tentukan kondisi di mana Anda ingin mengizinkan pengunduhan. Jika model tidak ada di perangkat, atau jika versi model yang lebih baru tersedia, tugas akan mengunduh model dari Firebase secara asinkron:

Cepat

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];

Banyak aplikasi memulai tugas pengunduhan dalam kode inisialisasinya, tetapi Anda dapat melakukannya kapan saja sebelum Anda perlu menggunakan model tersebut.

Buat detektor objek dari model Anda

Setelah Anda mengonfigurasi sumber model, buat objek TensorFlow Lite Interpreter dari salah satunya.

Jika Anda hanya memiliki model yang dibundel secara lokal, buat saja juru bahasa dari file model:

Cepat

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; }

Jika Anda memiliki model yang dihosting dari jarak jauh, Anda harus memeriksa apakah model tersebut telah diunduh sebelum Anda menjalankannya. Anda dapat memeriksa status tugas pengunduhan model menggunakan metode isModelDownloaded(remoteModel:) manajer model.

Meskipun Anda hanya perlu mengonfirmasi ini sebelum menjalankan juru bahasa, jika Anda memiliki model yang dihosting dari jarak jauh dan model yang dibundel secara lokal, mungkin masuk akal untuk melakukan pemeriksaan ini saat membuat instance Interpreter : buat juru bahasa dari model jarak jauh jika itu telah diunduh, dan dari model lokal sebaliknya.

Cepat

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; }

Jika Anda hanya memiliki model yang dihosting dari jarak jauh, Anda harus menonaktifkan fungsionalitas terkait model—misalnya, menghapus atau menyembunyikan bagian UI Anda—sampai Anda mengonfirmasi bahwa model telah diunduh.

Anda bisa mendapatkan status unduhan model dengan melampirkan pengamat ke Pusat Pemberitahuan default. Pastikan untuk menggunakan referensi yang lemah ke self di blok pengamat, karena pengunduhan dapat memakan waktu lama, dan objek asal dapat dibebaskan pada saat pengunduhan selesai. Sebagai contoh:

Cepat

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. Siapkan gambar masukan

Selanjutnya, Anda perlu menyiapkan gambar untuk juru bahasa TensorFlow Lite.

  1. Pangkas dan skala gambar ke dimensi input model, seperti yang ditentukan dalam file tflite_metadata.json (320x320 piksel secara default). Anda dapat melakukannya dengan Core Image atau perpustakaan pihak ketiga

  2. Salin data gambar ke Data ( objek NSData ):

    Cepat

    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. Jalankan detektor objek

Selanjutnya, berikan masukan yang sudah disiapkan ke juru bahasa:

Cepat

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. Dapatkan informasi tentang objek yang terdeteksi

Jika pendeteksian objek berhasil, model akan menghasilkan tiga larik yang terdiri dari 40 elemen (atau apa pun yang ditentukan dalam file tflite_metadata.json ) sebagai keluaran masing-masing. Setiap elemen sesuai dengan satu objek potensial. Larik pertama adalah larik kotak pembatas; yang kedua, deretan label; dan yang ketiga, serangkaian nilai kepercayaan. Untuk mendapatkan keluaran model:

Cepat

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; }

Kemudian, Anda dapat menggabungkan keluaran label dengan kamus label Anda:

Cepat

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);
    }
}

Kiat untuk meningkatkan kinerja waktu nyata

Jika Anda ingin memberi label pada gambar dalam aplikasi real-time, ikuti panduan ini untuk mendapatkan frekuensi gambar terbaik:

  • Batasi panggilan ke detektor. Jika bingkai video baru tersedia saat detektor berjalan, jatuhkan bingkai.
  • Jika Anda menggunakan keluaran detektor untuk menghamparkan grafik pada gambar masukan, dapatkan hasilnya terlebih dahulu, lalu render gambar dan lakukan hamparan dalam satu langkah. Dengan melakukannya, Anda merender ke permukaan tampilan hanya sekali untuk setiap bingkai masukan. Lihat kelas previewOverlayView dan FIRDetectionOverlayView di aplikasi contoh etalase sebagai contoh.