onnxruntime/samples/iOS/ModelRunner/ViewController.m
Wenbing Li ed102e9d88
Add iOS test pipeline and a sample app. (#5298)
* Add iOS test pipeline and a sample app.

* clean up the unused code.

* clean up.

* revert the unknown change

* disable the shared library for iOS.

* add open source notice text.

* ignore the skipped test.

* extract the common ortenv setup
2020-09-29 13:53:11 -07:00

192 lines
5.4 KiB
Objective-C

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#import "ViewController.h"
#import "OrtSession.h"
NS_ASSUME_NONNULL_BEGIN
/**
* Safely dispatches the given `block` on the main thread. If already on the main thread, the given
* block is executed immediately; otherwise, dispatches the block asynchronously on the main thread.
*
* @param block The block to dispatch on the main thread.
*/
void TLTSafeDispatchOnMain(dispatch_block_t block) {
if (block == nil) return;
if (NSThread.isMainThread) {
block();
} else {
dispatch_async(dispatch_get_main_queue(), block);
}
}
static NSString *const kModelNameMobileNet = @"mobilenetv2-7";
static NSString *const kModelNameQuantized = @"mobilenetv2-7";
static NSString *const kModelNameNLP = @"nlp";
/** Model resource type. */
static NSString *const kModelType = @"ort";
/** The label for the serial queue for synchronizing runtime calls. */
static const char *kRuntimeSerialQueueLabel = "com.onnxruntime.testapp";
static NSString *const kNilRuntimeError =
@"Failed to invoke the runtime because the runtime was nil.";
static NSString *const kInvokeRuntimeError = @"Failed to invoke ONNX Runtime due to error: %@.";
/** Model paths. */
static NSArray *arrModelPaths;
@interface ViewController ()
/** Serial queue for synchronizing runtime calls. */
@property(nonatomic) dispatch_queue_t runtimeSerialQueue;
/** ONNXRuntime for the currently selected model. */
@property(nonatomic) OrtMobileSession *runtime;
@property(weak, nonatomic) IBOutlet UISegmentedControl *modelControl;
@property(weak, nonatomic) IBOutlet UIBarButtonItem *invokeButton;
@property(weak, nonatomic) IBOutlet UITextView *resultsTextView;
@end
@implementation ViewController
#pragma mark - NSObject
+ (void)initialize {
if (self == [ViewController self]) {
arrModelPaths = @[
[NSBundle.mainBundle pathForResource:kModelNameMobileNet ofType:kModelType],
[NSBundle.mainBundle pathForResource:kModelNameQuantized ofType:kModelType],
[NSBundle.mainBundle pathForResource:kModelNameNLP ofType:kModelType],
];
}
}
#pragma mark - UIViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.runtimeSerialQueue =
dispatch_queue_create(kRuntimeSerialQueueLabel, DISPATCH_QUEUE_SERIAL);
self.invokeButton.enabled = NO;
[self updateResultsText:[NSString stringWithFormat:@"Using ONNXRuntime runtime version %@.", @"1.5.0"]];
[self loadModel];
}
#pragma mark - IBActions
- (IBAction)modelChanged:(id)sender {
self.invokeButton.enabled = NO;
NSString *results = [NSString
stringWithFormat:@"Switched to the %@ model.",
[self.modelControl
titleForSegmentAtIndex:self.modelControl.selectedSegmentIndex]];
[self updateResultsText:results];
[self loadModel];
}
- (IBAction)invokeRuntime:(id)sender {
switch (self.modelControl.selectedSegmentIndex) {
case 0:
[self invokeMobileNet];
break;
case 1:
[self invokeQuantized];
break;
case 2:
[self invokeNLP];
}
}
#pragma mark - Private
/** Path of the currently selected model. */
- (nullable NSString *)currentModelPath {
return self.modelControl.selectedSegmentIndex == UISegmentedControlNoSegment
? nil
: arrModelPaths[self.modelControl.selectedSegmentIndex];
}
- (void)loadModel {
NSString *modelPath = [self currentModelPath];
if (modelPath.length == 0) {
[self updateResultsText:@"No model is selected."];
return;
}
__weak typeof(self) weakSelf = self;
dispatch_async(self.runtimeSerialQueue, ^{
NSError *error;
weakSelf.runtime = [[OrtMobileSession alloc] initWithModelPath:modelPath
error:&error];
if (weakSelf.runtime == nil || error != nil) {
NSString *results =
[NSString stringWithFormat:@"Failed to create the runtime due to error:%@",
error.localizedDescription];
[weakSelf updateResultsText:results];
} else {
TLTSafeDispatchOnMain(^{
weakSelf.invokeButton.enabled = YES;
});
}
});
}
- (void)invokeMobileNet {
__weak typeof(self) weakSelf = self;
dispatch_async(self.runtimeSerialQueue, ^{
if (weakSelf.runtime == nil) {
[weakSelf updateResultsText:kNilRuntimeError];
return;
}
NSError* error;
NSMutableData* data = [NSMutableData alloc];
NSString *resultMsg = [weakSelf.runtime run:data mname:@"mobilenet" error:&error];
[weakSelf updateResultsText:resultMsg];
});
}
- (void)invokeQuantized {
__weak typeof(self) weakSelf = self;
dispatch_async(self.runtimeSerialQueue, ^{
if (weakSelf.runtime == nil) {
[weakSelf updateResultsText:kNilRuntimeError];
return;
}
});
}
- (void)invokeNLP {
__weak typeof(self) weakSelf = self;
dispatch_async(self.runtimeSerialQueue, ^{
if (weakSelf.runtime == nil) {
[weakSelf updateResultsText:kNilRuntimeError];
return;
}
NSError* error;
NSMutableData* data = [NSMutableData alloc];
NSString *resultMsg = [weakSelf.runtime run:data mname:@"nlp" error:&error];
[weakSelf updateResultsText:resultMsg];
});
}
- (void)updateResultsText:(NSString *)text {
__weak typeof(self) weakSelf = self;
TLTSafeDispatchOnMain(^{
weakSelf.resultsTextView.text = text;
});
}
@end
NS_ASSUME_NONNULL_END