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 |
|
Dihosting dengan Firebase |
|
Sebelum kamu memulai
Jika Anda ingin mendownload model , pastikan Anda menambahkan Firebase ke proyek Apple Anda , jika Anda belum melakukannya. Ini tidak diperlukan saat Anda memaketkan model.
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'
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.
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 ketigaSalin data gambar ke
Data
( objekNSData
):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.