From 2be59a5191a2d32c3e2a1e630b6335910135d7d0 Mon Sep 17 00:00:00 2001 From: "A. Unique TensorFlower" Date: Tue, 21 May 2019 22:57:55 -0700 Subject: [PATCH] Add a test app for TFLite ObjC API. PiperOrigin-RevId: 249390128 --- tensorflow/lite/experimental/objc/BUILD.apple | 47 +- tensorflow/lite/experimental/objc/README.md | 4 +- .../Configs/TensorFlowLite.tulsigen | 5 +- .../objc/apis/framework.modulemap | 5 - .../experimental/objc/apis/module.modulemap | 10 - .../experimental/objc/apps/TestApp/Podfile | 5 + .../TestApp/TestApp.xcodeproj/project.pbxproj | 352 +++++++++++++++ .../objc/apps/TestApp/TestApp/AppDelegate.h | 25 ++ .../objc/apps/TestApp/TestApp/AppDelegate.m | 22 + .../AppIcon.appiconset/Contents.json | 98 ++++ .../TestApp/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 44 ++ .../TestApp/Base.lproj/Main.storyboard | 89 ++++ .../objc/apps/TestApp/TestApp/Info.plist | 42 ++ .../apps/TestApp/TestApp/ViewController.h | 22 + .../apps/TestApp/TestApp/ViewController.m | 419 ++++++++++++++++++ .../objc/apps/TestApp/TestApp/main.m | 22 + .../objc/tests/TFLInterpreterTests.m | 19 +- .../swift/TestApp/TestApp/AppDelegate.swift | 14 + .../TestApp/Array+TensorFlowLite.swift | 14 + .../TestApp/TestApp/Data+TensorFlowLite.swift | 14 + .../TestApp/TestApp/ViewController.swift | 14 + 22 files changed, 1254 insertions(+), 38 deletions(-) delete mode 100644 tensorflow/lite/experimental/objc/apis/module.modulemap create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/Podfile create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp.xcodeproj/project.pbxproj create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.h create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.m create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/Contents.json create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/LaunchScreen.storyboard create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/Main.storyboard create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Info.plist create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.h create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.m create mode 100644 tensorflow/lite/experimental/objc/apps/TestApp/TestApp/main.m diff --git a/tensorflow/lite/experimental/objc/BUILD.apple b/tensorflow/lite/experimental/objc/BUILD.apple index e5f18059676..1b0ba65c581 100644 --- a/tensorflow/lite/experimental/objc/BUILD.apple +++ b/tensorflow/lite/experimental/objc/BUILD.apple @@ -5,7 +5,7 @@ package(default_visibility = ["//visibility:private"]) licenses(["notice"]) # Apache 2.0 load("//tensorflow/lite/experimental/ios:ios.bzl", "TFL_DEFAULT_TAGS", "TFL_DISABLED_SANITIZER_TAGS", "TFL_MINIMUM_OS_VERSION") -load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test") +load("@build_bazel_rules_apple//apple:ios.bzl", "ios_application", "ios_unit_test") SOURCES = glob([ "sources/*.h", @@ -58,7 +58,6 @@ objc_library( srcs = SOURCES, hdrs = API_HEADERS, copts = RELEASE_COPTS, - module_map = "apis/module.modulemap", tags = TFL_DEFAULT_TAGS, deps = [ "//tensorflow/lite/experimental/c:c_api", @@ -67,17 +66,17 @@ objc_library( ) ios_unit_test( - name = "TensorFlowLiteTests", + name = "Tests", size = "medium", minimum_os_version = TFL_MINIMUM_OS_VERSION, tags = TFL_DEFAULT_TAGS + TFL_DISABLED_SANITIZER_TAGS, deps = [ - ":TestsLib", + ":TestsLibrary", ], ) objc_library( - name = "TestsLib", + name = "TestsLibrary", testonly = 1, srcs = glob([ "tests/*.m", @@ -97,3 +96,41 @@ objc_library( ":TensorFlowLite", ], ) + +ios_application( + name = "TestApp", + app_icons = glob(["apps/TestApp/TestApp/Assets.xcassets/AppIcon.appiconset/**"]), + bundle_id = "com.tensorflow.lite.objc.TestApp", + families = [ + "ipad", + "iphone", + ], + infoplists = ["apps/TestApp/TestApp/Info.plist"], + minimum_os_version = TFL_MINIMUM_OS_VERSION, + sdk_frameworks = [ + "CoreGraphics", + ], + tags = TFL_DEFAULT_TAGS, + deps = [ + ":TestAppLibrary", + ], +) + +objc_library( + name = "TestAppLibrary", + srcs = glob(["apps/TestApp/TestApp/*.m"]), + hdrs = glob(["apps/TestApp/TestApp/*.h"]), + data = glob(["apps/TestApp/TestApp/Base.lproj/*.storyboard"]) + [ + "//tensorflow/lite:testdata/add.bin", + "//tensorflow/lite:testdata/add_quantized.bin", + "//tensorflow/lite:testdata/multi_add.bin", + ], + includes = [ + "apis", + ], + module_name = "TestApp", + tags = TFL_DEFAULT_TAGS + ["manual"], + deps = [ + ":TensorFlowLite", + ], +) diff --git a/tensorflow/lite/experimental/objc/README.md b/tensorflow/lite/experimental/objc/README.md index e0788e61c62..e6b30fc94fc 100644 --- a/tensorflow/lite/experimental/objc/README.md +++ b/tensorflow/lite/experimental/objc/README.md @@ -74,10 +74,10 @@ Build the `TensorFlowLite` Objective-C library target: bazel build tensorflow/lite/experimental/objc:TensorFlowLite ``` -Build the `TensorFlowLiteTests` target: +Build the `Tests` target: ```shell -bazel test tensorflow/lite/experimental/objc:TensorFlowLiteTests +bazel test tensorflow/lite/experimental/objc:Tests ``` #### Generate the Xcode project using Tulsi diff --git a/tensorflow/lite/experimental/objc/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen b/tensorflow/lite/experimental/objc/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen index 04a2a2c19cd..feacdbad8de 100644 --- a/tensorflow/lite/experimental/objc/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen +++ b/tensorflow/lite/experimental/objc/TensorFlowLite.tulsiproj/Configs/TensorFlowLite.tulsigen @@ -4,6 +4,8 @@ "tensorflow/lite/experimental/c", "tensorflow/lite/experimental/objc", "tensorflow/lite/experimental/objc/apis", + "tensorflow/lite/experimental/objc/apps/TestApp/TestApp", + "tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj", "tensorflow/lite/experimental/objc/sources", "tensorflow/lite/experimental/objc/tests", "tensorflow/lite/kernels", @@ -13,7 +15,8 @@ ], "buildTargets" : [ "//tensorflow/lite/experimental/objc:TensorFlowLite", - "//tensorflow/lite/experimental/objc:TensorFlowLiteTests", + "//tensorflow/lite/experimental/objc:TestApp", + "//tensorflow/lite/experimental/objc:Tests", ], "projectName" : "TensorFlowLite", "optionSet" : { diff --git a/tensorflow/lite/experimental/objc/apis/framework.modulemap b/tensorflow/lite/experimental/objc/apis/framework.modulemap index 57113ffab1f..c360b20ab86 100644 --- a/tensorflow/lite/experimental/objc/apis/framework.modulemap +++ b/tensorflow/lite/experimental/objc/apis/framework.modulemap @@ -1,10 +1,5 @@ framework module TFLTensorFlowLite { umbrella header "TFLTensorFlowLite.h" - header "TFLInterpreter.h" - header "TFLInterpreterOptions.h" - header "TFLQuantizationParameters.h" - header "TFLTensor.h" - export * } diff --git a/tensorflow/lite/experimental/objc/apis/module.modulemap b/tensorflow/lite/experimental/objc/apis/module.modulemap deleted file mode 100644 index 37b98478f60..00000000000 --- a/tensorflow/lite/experimental/objc/apis/module.modulemap +++ /dev/null @@ -1,10 +0,0 @@ -module TFLTensorFlowLite { - umbrella header "TFLTensorFlowLite.h" - - header "TFLInterpreter.h" - header "TFLInterpreterOptions.h" - header "TFLQuantizationParameters.h" - header "TFLTensor.h" - - export * -} diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/Podfile b/tensorflow/lite/experimental/objc/apps/TestApp/Podfile new file mode 100644 index 00000000000..7060500690d --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/Podfile @@ -0,0 +1,5 @@ +platform :ios, '9.0' + +target 'TestApp' do + pod 'TensorFlowLiteObjC' +end diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp.xcodeproj/project.pbxproj b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..9ce01df5b10 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp.xcodeproj/project.pbxproj @@ -0,0 +1,352 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + B20F5F3622937C9000A4FBD8 /* add_quantized.bin in Resources */ = {isa = PBXBuildFile; fileRef = B20F5F3322937C8D00A4FBD8 /* add_quantized.bin */; }; + B20F5F3722937C9000A4FBD8 /* add.bin in Resources */ = {isa = PBXBuildFile; fileRef = B20F5F3422937C8F00A4FBD8 /* add.bin */; }; + B20F5F3822937C9000A4FBD8 /* multi_add.bin in Resources */ = {isa = PBXBuildFile; fileRef = B20F5F3522937C8F00A4FBD8 /* multi_add.bin */; }; + B210BD922291D78D00572163 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B210BD912291D78D00572163 /* AppDelegate.m */; }; + B210BD952291D78D00572163 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B210BD942291D78D00572163 /* ViewController.m */; }; + B210BD982291D78D00572163 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B210BD962291D78D00572163 /* Main.storyboard */; }; + B210BD9A2291D78E00572163 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B210BD992291D78E00572163 /* Assets.xcassets */; }; + B210BD9D2291D78F00572163 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B210BD9B2291D78F00572163 /* LaunchScreen.storyboard */; }; + B210BDA02291D78F00572163 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B210BD9F2291D78F00572163 /* main.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + B20F5F3322937C8D00A4FBD8 /* add_quantized.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = add_quantized.bin; path = ../../../../testdata/add_quantized.bin; sourceTree = ""; }; + B20F5F3422937C8F00A4FBD8 /* add.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = add.bin; path = ../../../../testdata/add.bin; sourceTree = ""; }; + B20F5F3522937C8F00A4FBD8 /* multi_add.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = multi_add.bin; path = ../../../../testdata/multi_add.bin; sourceTree = ""; }; + B210BD8D2291D78D00572163 /* TestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B210BD902291D78D00572163 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + B210BD912291D78D00572163 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + B210BD932291D78D00572163 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + B210BD942291D78D00572163 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + B210BD972291D78D00572163 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + B210BD992291D78E00572163 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B210BD9C2291D78F00572163 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + B210BD9E2291D78F00572163 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B210BD9F2291D78F00572163 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B210BD8A2291D78D00572163 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B210BD842291D78D00572163 = { + isa = PBXGroup; + children = ( + B20F5F3322937C8D00A4FBD8 /* add_quantized.bin */, + B20F5F3422937C8F00A4FBD8 /* add.bin */, + B20F5F3522937C8F00A4FBD8 /* multi_add.bin */, + B210BD8F2291D78D00572163 /* TestApp */, + B210BD8E2291D78D00572163 /* Products */, + ); + sourceTree = ""; + }; + B210BD8E2291D78D00572163 /* Products */ = { + isa = PBXGroup; + children = ( + B210BD8D2291D78D00572163 /* TestApp.app */, + ); + name = Products; + sourceTree = ""; + }; + B210BD8F2291D78D00572163 /* TestApp */ = { + isa = PBXGroup; + children = ( + B210BD902291D78D00572163 /* AppDelegate.h */, + B210BD912291D78D00572163 /* AppDelegate.m */, + B210BD932291D78D00572163 /* ViewController.h */, + B210BD942291D78D00572163 /* ViewController.m */, + B210BD962291D78D00572163 /* Main.storyboard */, + B210BD992291D78E00572163 /* Assets.xcassets */, + B210BD9B2291D78F00572163 /* LaunchScreen.storyboard */, + B210BD9E2291D78F00572163 /* Info.plist */, + B210BD9F2291D78F00572163 /* main.m */, + ); + path = TestApp; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B210BD8C2291D78D00572163 /* TestApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = B210BDA32291D78F00572163 /* Build configuration list for PBXNativeTarget "TestApp" */; + buildPhases = ( + B210BD892291D78D00572163 /* Sources */, + B210BD8A2291D78D00572163 /* Frameworks */, + B210BD8B2291D78D00572163 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TestApp; + productName = TestApp; + productReference = B210BD8D2291D78D00572163 /* TestApp.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B210BD852291D78D00572163 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1010; + ORGANIZATIONNAME = "Google Inc"; + TargetAttributes = { + B210BD8C2291D78D00572163 = { + CreatedOnToolsVersion = 10.1; + }; + }; + }; + buildConfigurationList = B210BD882291D78D00572163 /* Build configuration list for PBXProject "TestApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B210BD842291D78D00572163; + productRefGroup = B210BD8E2291D78D00572163 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B210BD8C2291D78D00572163 /* TestApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B210BD8B2291D78D00572163 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B210BD9D2291D78F00572163 /* LaunchScreen.storyboard in Resources */, + B20F5F3622937C9000A4FBD8 /* add_quantized.bin in Resources */, + B20F5F3722937C9000A4FBD8 /* add.bin in Resources */, + B210BD9A2291D78E00572163 /* Assets.xcassets in Resources */, + B210BD982291D78D00572163 /* Main.storyboard in Resources */, + B20F5F3822937C9000A4FBD8 /* multi_add.bin in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B210BD892291D78D00572163 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B210BD952291D78D00572163 /* ViewController.m in Sources */, + B210BDA02291D78F00572163 /* main.m in Sources */, + B210BD922291D78D00572163 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + B210BD962291D78D00572163 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B210BD972291D78D00572163 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + B210BD9B2291D78F00572163 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B210BD9C2291D78F00572163 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + B210BDA12291D78F00572163 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + B210BDA22291D78F00572163 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B210BDA42291D78F00572163 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TestApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.objc.TestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + B210BDA52291D78F00572163 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = TestApp/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.tensorflow.lite.objc.TestApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B210BD882291D78D00572163 /* Build configuration list for PBXProject "TestApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B210BDA12291D78F00572163 /* Debug */, + B210BDA22291D78F00572163 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B210BDA32291D78F00572163 /* Build configuration list for PBXNativeTarget "TestApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B210BDA42291D78F00572163 /* Debug */, + B210BDA52291D78F00572163 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = B210BD852291D78D00572163 /* Project object */; +} diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.h b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.h new file mode 100644 index 00000000000..a8442869d47 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.h @@ -0,0 +1,25 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface AppDelegate : UIResponder + +@property(nonatomic) UIWindow *window; + +@end + +NS_ASSUME_NONNULL_END diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.m b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.m new file mode 100644 index 00000000000..06bb8d9b610 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/AppDelegate.m @@ -0,0 +1,22 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "AppDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation AppDelegate +@end + +NS_ASSUME_NONNULL_END diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..d8db8d65fd7 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/Contents.json b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/Contents.json new file mode 100644 index 00000000000..da4a164c918 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/LaunchScreen.storyboard b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000000..6c97d768e15 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/Main.storyboard b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/Main.storyboard new file mode 100644 index 00000000000..602ef636aa9 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Base.lproj/Main.storyboard @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Info.plist b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Info.plist new file mode 100644 index 00000000000..b16c1e9fb58 --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 0.0.1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + + diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.h b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.h new file mode 100644 index 00000000000..797c964dd4e --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.h @@ -0,0 +1,22 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface ViewController : UIViewController +@end + +NS_ASSUME_NONNULL_END diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.m b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.m new file mode 100644 index 00000000000..2b805f0d1be --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/ViewController.m @@ -0,0 +1,419 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import "ViewController.h" + +#import "TFLTensorFlowLite.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); + } +} + +/** + * Name of a float model that performs two add operations on one input tensor and returns the result + * in one output tensor. + */ +static NSString *const kModelNameAdd = @"add"; + +/** + * Name of a quantized model that performs two add operations on one input tensor and returns the + * result in one output tensor. + */ +static NSString *const kModelNameAddQuantized = @"add_quantized"; + +/** + * Name of a float model that performs three add operations on four input tensors and returns the + * results in 2 output tensors. + */ +static NSString *const kModelNameMultiAdd = @"multi_add"; + +/** Model resource type. */ +static NSString *const kModelType = @"bin"; + +/** The label for the serial queue for synchronizing interpreter calls. */ +static const char *kInterpreterSerialQueueLabel = "com.tensorflow.lite.objc.testapp.interpreter"; + +static NSString *const kNilInterpreterError = + @"Failed to invoke the interpreter because the interpreter was nil."; +static NSString *const kInvokeInterpreterError = @"Failed to invoke interpreter due to error: %@."; + +/** Model paths. */ +static NSArray *gModelPaths; + +@interface ViewController () + +/** Serial queue for synchronizing interpreter calls. */ +@property(nonatomic) dispatch_queue_t interpreterSerialQueue; + +/** TensorFlow Lite interpreter for the currently selected model. */ +@property(nonatomic) TFLInterpreter *interpreter; + +@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]) { + gModelPaths = @[ + [NSBundle.mainBundle pathForResource:kModelNameAdd ofType:kModelType], + [NSBundle.mainBundle pathForResource:kModelNameAddQuantized ofType:kModelType], + [NSBundle.mainBundle pathForResource:kModelNameMultiAdd ofType:kModelType], + ]; + } +} + +#pragma mark - UIViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + self.interpreterSerialQueue = + dispatch_queue_create(kInterpreterSerialQueueLabel, DISPATCH_QUEUE_SERIAL); + self.invokeButton.enabled = NO; + [self updateResultsText:[NSString stringWithFormat:@"Using TensorFlow Lite runtime version %@.", + TFLVersion]]; + [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)invokeInterpreter:(id)sender { + switch (self.modelControl.selectedSegmentIndex) { + case 0: + [self invokeAdd]; + break; + case 1: + [self invokeAddQuantized]; + break; + case 2: + [self invokeMultiAdd]; + } +} + +#pragma mark - Private + +/** Path of the currently selected model. */ +- (nullable NSString *)currentModelPath { + return self.modelControl.selectedSegmentIndex == UISegmentedControlNoSegment + ? nil + : gModelPaths[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.interpreterSerialQueue, ^{ + TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init]; + options.numberOfThreads = 2; + + NSError *error; + weakSelf.interpreter = [[TFLInterpreter alloc] initWithModelPath:modelPath + options:options + error:&error]; + if (weakSelf.interpreter == nil || error != nil) { + NSString *results = + [NSString stringWithFormat:@"Failed to create the interpreter due to error:%@", + error.localizedDescription]; + [weakSelf updateResultsText:results]; + } else { + TLTSafeDispatchOnMain(^{ + weakSelf.invokeButton.enabled = YES; + }); + } + }); +} + +- (void)invokeAdd { + __weak typeof(self) weakSelf = self; + dispatch_async(self.interpreterSerialQueue, ^{ + if (weakSelf.interpreter == nil) { + [weakSelf updateResultsText:kNilInterpreterError]; + return; + } + + NSArray *shape = @[@2]; + NSError *error; + + if (![weakSelf.interpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + if (![weakSelf.interpreter allocateTensorsWithError:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + TFLTensor *inputTensor = [weakSelf.interpreter inputTensorAtIndex:0 error:&error]; + if (inputTensor == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSMutableData *inputData = [NSMutableData dataWithCapacity:0]; + float one = 1.f; + float three = 3.f; + [inputData appendBytes:&one length:sizeof(float)]; + [inputData appendBytes:&three length:sizeof(float)]; + if (![inputTensor copyData:inputData error:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + if (![weakSelf.interpreter invokeWithError:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + TFLTensor *outputTensor = [weakSelf.interpreter outputTensorAtIndex:0 error:&error]; + if (outputTensor == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSData *outputData = [outputTensor dataWithError:&error]; + if (outputData == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + float output[2U]; + [outputData getBytes:output length:(sizeof(float) * 2U)]; + + [weakSelf + updateResultsText:[NSString stringWithFormat:@"Performing 2 add operations:\n\nInput = " + @"[%.1f, %.1f]\n\nOutput = [%.1f, %.1f]", + one, three, output[0], output[1]]]; + }); +} + +- (void)invokeAddQuantized { + __weak typeof(self) weakSelf = self; + dispatch_async(self.interpreterSerialQueue, ^{ + if (weakSelf.interpreter == nil) { + [weakSelf updateResultsText:kNilInterpreterError]; + return; + } + + NSArray *shape = @[@2]; + NSError *error; + + if (![weakSelf.interpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + if (![weakSelf.interpreter allocateTensorsWithError:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + TFLTensor *inputTensor = [weakSelf.interpreter inputTensorAtIndex:0 error:&error]; + if (inputTensor == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSMutableData *inputData = [NSMutableData dataWithCapacity:0]; + uint8_t one = 1U; + uint8_t three = 3U; + [inputData appendBytes:&one length:sizeof(uint8_t)]; + [inputData appendBytes:&three length:sizeof(uint8_t)]; + if (![inputTensor copyData:inputData error:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + if (![weakSelf.interpreter invokeWithError:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + TFLTensor *outputTensor = [weakSelf.interpreter outputTensorAtIndex:0 error:&error]; + if (outputTensor == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + TFLQuantizationParameters *params = outputTensor.quantizationParameters; + if (params == nil) { + [weakSelf updateResultsText: + [NSString stringWithFormat:kInvokeInterpreterError, + @"Missing qualitization parameters in the output"]]; + return; + } + + NSData *outputData = [outputTensor dataWithError:&error]; + if (outputData == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + uint8_t output[2U]; + [outputData getBytes:output length:(sizeof(uint8_t) * 2U)]; + float dequantized[2U]; + dequantized[0] = params.scale * (output[0] - params.zeroPoint); + dequantized[1] = params.scale * (output[1] - params.zeroPoint); + + [weakSelf updateResultsText: + [NSString stringWithFormat:@"Performing 2 add operations on quantized input:\n\n" + @"Input = [%d, %d]\n\nQuantized Output = [%d, %d]\n\n" + @"Dequantized Output = [%f, %f]", + one, three, output[0], output[1], dequantized[0], + dequantized[1]]]; + }); +} + +- (void)invokeMultiAdd { + __weak typeof(self) weakSelf = self; + dispatch_async(self.interpreterSerialQueue, ^{ + if (weakSelf.interpreter == nil) { + [weakSelf updateResultsText:kNilInterpreterError]; + return; + } + + NSArray *shape = @[@2]; + NSError *error; + + for (int i = 0; i < weakSelf.interpreter.inputTensorCount; ++i) { + if (![weakSelf.interpreter resizeInputTensorAtIndex:i toShape:shape error:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + } + + if (![weakSelf.interpreter allocateTensorsWithError:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSString *inputs = @""; + for (int i = 0; i < weakSelf.interpreter.inputTensorCount; ++i) { + TFLTensor *inputTensor = [weakSelf.interpreter inputTensorAtIndex:i error:&error]; + if (inputTensor == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSMutableData *inputData = [NSMutableData dataWithCapacity:0]; + float input1 = (float)(i + 1); + float input2 = (float)(i + 2); + inputs = [NSString stringWithFormat:@"%@%@[%.1f, %.1f]", inputs, + (inputs.length == 0 ? @"[" : @", "), input1, input2]; + + [inputData appendBytes:&input1 length:sizeof(float)]; + [inputData appendBytes:&input2 length:sizeof(float)]; + if (![inputTensor copyData:inputData error:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + } + inputs = [NSString stringWithFormat:@"%@]", inputs]; + + if (![weakSelf.interpreter invokeWithError:&error]) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSString *outputs = @""; + for (int i = 0; i < weakSelf.interpreter.outputTensorCount; ++i) { + TFLTensor *outputTensor = [weakSelf.interpreter outputTensorAtIndex:i error:&error]; + if (outputTensor == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + + NSData *outputData = [outputTensor dataWithError:&error]; + if (outputData == nil || error != nil) { + [weakSelf updateResultsText:[NSString stringWithFormat:kInvokeInterpreterError, + error.localizedDescription]]; + return; + } + float output[2U]; + [outputData getBytes:output length:(sizeof(float) * 2U)]; + outputs = + [NSString stringWithFormat:@"%@%@[%.1f, %.1f]", outputs, + (outputs.length == 0 ? @"[" : @", "), output[0], output[1]]; + } + outputs = [NSString stringWithFormat:@"%@]", outputs]; + + [weakSelf + updateResultsText: + [NSString + stringWithFormat:@"Performing 3 add operations:\n\nInputs = %@\n\nOutputs = %@", + inputs, outputs]]; + }); +} + +- (void)updateResultsText:(NSString *)text { + __weak typeof(self) weakSelf = self; + TLTSafeDispatchOnMain(^{ + weakSelf.resultsTextView.text = text; + }); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/main.m b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/main.m new file mode 100644 index 00000000000..b7ab1325e1b --- /dev/null +++ b/tensorflow/lite/experimental/objc/apps/TestApp/TestApp/main.m @@ -0,0 +1,22 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/tensorflow/lite/experimental/objc/tests/TFLInterpreterTests.m b/tensorflow/lite/experimental/objc/tests/TFLInterpreterTests.m index f9bebcfbaa8..c75d082fe7d 100644 --- a/tensorflow/lite/experimental/objc/tests/TFLInterpreterTests.m +++ b/tensorflow/lite/experimental/objc/tests/TFLInterpreterTests.m @@ -33,9 +33,6 @@ static NSString *const kAddQuantizedModelResourceName = @"add_quantized"; /** Model resource type. */ static NSString *const kAddModelResourceType = @"bin"; -/** Rank of the input and output tensor in the Add model. */ -static const NSUInteger kAddModelTensorRank = 1U; - /** Size of the first (and only) dimension of the input and output tensor in the Add model. */ static const NSUInteger kAddModelTensorFirstDimensionSize = 2U; @@ -100,8 +97,7 @@ static const float kTestAccuracy = 1E-5F; - (void)testSuccessfulFullRunAddFloatModel { // Shape for both input and output tensor. - NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank]; - shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize]; + NSArray *shape = @[@(kAddModelTensorFirstDimensionSize)]; // Creates the interpreter options. TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init]; @@ -184,8 +180,7 @@ static const float kTestAccuracy = 1E-5F; - (void)testSuccessfulFullRunQuantizedModel { // Shape for both input and output tensor. - NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank]; - shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize]; + NSArray *shape = @[@(kAddModelTensorFirstDimensionSize)]; // Creates the interpreter options. TFLInterpreterOptions *options = [[TFLInterpreterOptions alloc] init]; @@ -276,10 +271,6 @@ static const float kTestAccuracy = 1E-5F; } - (void)testInitWithModelPath_invalidPath { - // Shape for both input and output tensor. - NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank]; - shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize]; - // Creates the interpreter. NSError *error; TFLInterpreter *brokenInterpreter = [[TFLInterpreter alloc] initWithModelPath:@"InvalidPath" @@ -308,8 +299,7 @@ static const float kTestAccuracy = 1E-5F; } - (void)testResizeInputTensorAtIndex_invalidIndex { - NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank]; - shape[0] = [NSNumber numberWithUnsignedInteger:kAddModelTensorFirstDimensionSize]; + NSArray *shape = @[@(kAddModelTensorFirstDimensionSize)]; NSError *error; XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:kInvalidInputTensorIndex toShape:shape @@ -325,8 +315,7 @@ static const float kTestAccuracy = 1E-5F; } - (void)testResizeInputTensorAtIndex_zeroDimensionSize { - NSMutableArray *shape = [NSMutableArray arrayWithCapacity:kAddModelTensorRank]; - shape[0] = [NSNumber numberWithUnsignedInteger:0]; + NSArray *shape = @[@0]; NSError *error; XCTAssertFalse([self.interpreter resizeInputTensorAtIndex:0 toShape:shape error:&error]); XCTAssertEqual(error.code, TFLInterpreterErrorCodeInvalidShape); diff --git a/tensorflow/lite/experimental/swift/TestApp/TestApp/AppDelegate.swift b/tensorflow/lite/experimental/swift/TestApp/TestApp/AppDelegate.swift index 45fd69716df..d34b27a5c84 100644 --- a/tensorflow/lite/experimental/swift/TestApp/TestApp/AppDelegate.swift +++ b/tensorflow/lite/experimental/swift/TestApp/TestApp/AppDelegate.swift @@ -1,3 +1,17 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import UIKit @UIApplicationMain diff --git a/tensorflow/lite/experimental/swift/TestApp/TestApp/Array+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApp/TestApp/Array+TensorFlowLite.swift index e9fb026bb7b..e2853e153eb 100644 --- a/tensorflow/lite/experimental/swift/TestApp/TestApp/Array+TensorFlowLite.swift +++ b/tensorflow/lite/experimental/swift/TestApp/TestApp/Array+TensorFlowLite.swift @@ -1,3 +1,17 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import Foundation extension Array { diff --git a/tensorflow/lite/experimental/swift/TestApp/TestApp/Data+TensorFlowLite.swift b/tensorflow/lite/experimental/swift/TestApp/TestApp/Data+TensorFlowLite.swift index bc8a70c8483..0a845f6b354 100644 --- a/tensorflow/lite/experimental/swift/TestApp/TestApp/Data+TensorFlowLite.swift +++ b/tensorflow/lite/experimental/swift/TestApp/TestApp/Data+TensorFlowLite.swift @@ -1,3 +1,17 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import Foundation extension Data { diff --git a/tensorflow/lite/experimental/swift/TestApp/TestApp/ViewController.swift b/tensorflow/lite/experimental/swift/TestApp/TestApp/ViewController.swift index 33a53ca3a08..7041930a38e 100644 --- a/tensorflow/lite/experimental/swift/TestApp/TestApp/ViewController.swift +++ b/tensorflow/lite/experimental/swift/TestApp/TestApp/ViewController.swift @@ -1,3 +1,17 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + import class TensorFlowLite.Interpreter import struct TensorFlowLite.InterpreterOptions import struct TensorFlowLite.Tensor