بعد از اینکه مدل خود را با استفاده از AutoML Vision Edge آموزش دادید ، می توانید از آن در برنامه خود برای شناسایی اشیاء در تصاویر استفاده کنید.
دو راه برای ادغام مدل های آموزش دیده از AutoML Vision Edge وجود دارد. میتوانید با کپی کردن فایلهای مدل در پروژه Xcode، مدل را باندل کنید یا میتوانید آن را به صورت پویا از Firebase دانلود کنید.
گزینه های بسته بندی مدل | |
---|---|
همراه با برنامه شما |
|
میزبانی شده با Firebase |
|
قبل از شروع
اگر می خواهید مدلی را دانلود کنید ، مطمئن شوید که Firebase را به پروژه اپل خود اضافه کرده اید، اگر قبلاً این کار را نکرده اید. هنگامی که مدل را بسته بندی می کنید، این مورد نیاز نیست.
کتابخانه های TensorFlow و Firebase را در فایل پادفایل خود قرار دهید:
برای بستهبندی یک مدل با برنامهتان:
سویفت
pod 'TensorFlowLiteSwift'
هدف-C
pod 'TensorFlowLiteObjC'
برای دانلود پویا یک مدل از Firebase، وابستگی
Firebase/MLModelInterpreter
را اضافه کنید:سویفت
pod 'TensorFlowLiteSwift' pod 'Firebase/MLModelInterpreter'
هدف-C
pod 'TensorFlowLiteObjC' pod 'Firebase/MLModelInterpreter'
پس از نصب یا به روز رسانی Pods پروژه خود، پروژه Xcode خود را با استفاده از
.xcworkspace
. آن باز کنید.
1. مدل را بارگذاری کنید
یک منبع مدل محلی را پیکربندی کنید
برای بستهبندی مدل با برنامهتان، فایل مدل و برچسبها را در پروژه Xcode خود کپی کنید و مراقب باشید که هنگام انجام این کار، Create folder references را انتخاب کنید. فایل مدل و برچسبها در بسته برنامه گنجانده میشود.
همچنین به فایل tflite_metadata.json
که در کنار مدل ایجاد شده است نگاه کنید. شما به دو مقدار نیاز دارید:
- ابعاد ورودی مدل این به طور پیش فرض 320x320 است.
- حداکثر تشخیص مدل این به طور پیش فرض 40 است.
یک منبع مدل میزبانی شده توسط Firebase را پیکربندی کنید
برای استفاده از مدل میزبانی شده از راه دور، یک شی CustomRemoteModel
ایجاد کنید و نامی را که به مدل اختصاص داده اید در هنگام انتشار آن مشخص کنید:
سویفت
let remoteModel = CustomRemoteModel(
name: "your_remote_model" // The name you assigned in the Google Cloud console.
)
هدف-C
FIRCustomRemoteModel *remoteModel = [[FIRCustomRemoteModel alloc]
initWithName:@"your_remote_model"];
سپس، با مشخص کردن شرایطی که میخواهید اجازه دانلود را بدهید، کار دانلود مدل را شروع کنید. اگر مدل در دستگاه نباشد، یا اگر نسخه جدیدتری از مدل موجود باشد، این کار به صورت ناهمزمان مدل را از Firebase دانلود میکند:
سویفت
let downloadProgress = ModelManager.modelManager().download(
remoteModel,
conditions: ModelDownloadConditions(
allowsCellularAccess: true,
allowsBackgroundDownloading: true
)
)
هدف-C
FIRModelDownloadConditions *conditions =
[[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
allowsBackgroundDownloading:YES];
NSProgress *progress = [[FIRModelManager modelManager] downloadModel:remoteModel
conditions:conditions];
بسیاری از برنامهها وظیفه دانلود را در کد اولیه خود شروع میکنند، اما شما میتوانید این کار را در هر زمانی قبل از نیاز به استفاده از مدل انجام دهید.
یک آشکارساز شی از مدل خود ایجاد کنید
پس از اینکه منابع مدل خود را پیکربندی کردید، یک شیء Interpreter
TensorFlow Lite از یکی از آنها ایجاد کنید.
اگر فقط یک مدل بومی همراه دارید، فقط یک مفسر از فایل مدل ایجاد کنید:
سویفت
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()
هدف-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; }
اگر یک مدل با میزبانی از راه دور دارید، قبل از اجرای آن باید بررسی کنید که دانلود شده است. با استفاده از روش isModelDownloaded(remoteModel:)
مدیر مدل می توانید وضعیت وظیفه دانلود مدل را بررسی کنید.
اگر چه شما فقط باید قبل از اجرای مفسر این موضوع را تأیید کنید، اگر هم یک مدل میزبان از راه دور و هم یک مدل بستهبندی محلی دارید، ممکن است انجام این بررسی هنگام نمونهسازی Interpreter
منطقی باشد: اگر مفسر از مدل راه دور است، یک مفسر ایجاد کنید. دانلود شده است، و در غیر این صورت از مدل محلی.
سویفت
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()
هدف-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; }
اگر فقط یک مدل با میزبانی از راه دور دارید، باید عملکردهای مربوط به مدل را غیرفعال کنید - به عنوان مثال، خاکستری کردن یا پنهان کردن بخشی از رابط کاربری خود - تا زمانی که تأیید کنید مدل دانلود شده است.
میتوانید با پیوست کردن ناظران به مرکز اطلاع رسانی پیشفرض، وضعیت دانلود مدل را دریافت کنید. مطمئن شوید که از یک مرجع ضعیف به self
در بلوک ناظر استفاده کنید، زیرا دانلودها ممکن است مدتی طول بکشد، و شی مبدا میتواند تا پایان دانلود آزاد شود. به عنوان مثال:
سویفت
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]
// ...
}
هدف-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. تصویر ورودی را آماده کنید
در مرحله بعد، باید تصاویر خود را برای مفسر TensorFlow Lite آماده کنید.
همانطور که در فایل
tflite_metadata.json
(به طور پیش فرض 320x320 پیکسل) مشخص شده است، تصویر را به ابعاد ورودی مدل برش داده و مقیاس دهید. می توانید این کار را با Core Image یا یک کتابخانه شخص ثالث انجام دهیدداده های تصویر را در یک
Data
(شیNSData
) کپی کنید:سویفت
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) } }
هدف-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. آشکارساز شی را اجرا کنید
سپس ورودی آماده شده را به مفسر ارسال کنید:
سویفت
try interpreter.copy(inputData, toInputAt: 0)
try interpreter.invoke()
هدف-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. اطلاعاتی در مورد اشیاء شناسایی شده دریافت کنید
اگر تشخیص شی با موفقیت انجام شود، مدل سه آرایه از 40 عنصر (یا هر چیزی که در فایل tflite_metadata.json
مشخص شده بود) را به عنوان خروجی تولید می کند. هر عنصر مربوط به یک شیء بالقوه است. آرایه اول آرایه ای از جعبه های محدود کننده است. دوم، آرایه ای از برچسب ها. و سوم، مجموعه ای از مقادیر اطمینان. برای دریافت خروجی مدل:
سویفت
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)
هدف-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; }
سپس، می توانید خروجی های برچسب را با فرهنگ لغت برچسب خود ترکیب کنید:
سویفت
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))")
}
}
هدف-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);
}
}
نکاتی برای بهبود عملکرد در زمان واقعی
اگر می خواهید تصاویر را در یک برنامه بلادرنگ برچسب گذاری کنید، این دستورالعمل ها را دنبال کنید تا به بهترین نرخ فریم برسید:
- دریچه گاز به آشکارساز زنگ می زند. اگر یک قاب ویدیویی جدید در حین کار کردن آشکارساز در دسترس قرار گرفت، قاب را رها کنید.
- اگر از خروجی آشکارساز برای همپوشانی گرافیک روی تصویر ورودی استفاده میکنید، ابتدا نتیجه را دریافت کنید، سپس تصویر را در یک مرحله رندر و همپوشانی کنید. با انجام این کار، برای هر فریم ورودی فقط یک بار به سطح نمایشگر رندر می دهید. به عنوان مثال، کلاسهای previewOverlayView و FIRDetectionOverlayView را در برنامه نمونه ویترینی ببینید.