Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NOT FOR MERGING] iOS Image Embedder Objective C++ #830

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
91 changes: 58 additions & 33 deletions tensorflow_lite_support/ios/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ load(
load(
"//tensorflow_lite_support/ios:ios.bzl",
"strip_c_api_include_path_prefix",
"strip_ios_api_include_path_prefix",
)

load(
"@build_bazel_rules_apple//apple:ios.bzl",
"ios_static_framework",
)

package(
Expand Down Expand Up @@ -37,6 +43,24 @@ strip_c_api_include_path_prefix(
],
)

strip_ios_api_include_path_prefix(
name = "strip_ios_api_include_path",
hdr_labels = [
"//tensorflow_lite_support/ios/task/vision:sources/TFLImageEmbedder.h",
"//tensorflow_lite_support/ios/task/vision:sources/TFLImageClassifier.h",
"//tensorflow_lite_support/ios/task/vision:sources/TFLObjectDetector.h",
"//tensorflow_lite_support/ios/task/vision:sources/TFLImageSegmenter.h",
"//tensorflow_lite_support/ios:sources/TFLCommon.h",
"//tensorflow_lite_support/ios/task/core:sources/TFLBaseOptions.h",
"//tensorflow_lite_support/ios/task/processor:sources/TFLClassificationOptions.h",
"//tensorflow_lite_support/ios/task/processor:sources/TFLCategory.h",
"//tensorflow_lite_support/ios/task/processor:sources/TFLClassificationResult.h",
"//tensorflow_lite_support/ios/task/processor:sources/TFLDetectionResult.h",
"//tensorflow_lite_support/ios/task/processor:sources/TFLSegmentationResult.h",
"//tensorflow_lite_support/odml/ios/image:apis/GMLImage.h",
],
)

# This target builds a monolithic static framework for the TFLite Text API,
# which includes the TFLite runtime in it.
#
Expand All @@ -59,39 +83,6 @@ tflite_ios_framework(
],
)

# Xcode 12 does not support ios fat libraries. Frameworks built for multiple
# architectures should be compiled into a .xcframework inside. Bazel currently
# does not support building .xcframework. You have to build the framework
# for the architecture you decide to test on.
# Use the below command to build for arm64 which lets you test the library on
# iOS devices.
# bazel build -c opt --config=ios_arm64 //tensorflow_lite_support/ios:TensorFlowLiteTaskVisionC_framework
tflite_ios_framework(
name = "TensorFlowLiteTaskVisionC_framework",
hdrs = [
":base_options.h",
":bounding_box.h",
":category.h",
":classification_options.h",
":classification_result.h",
":common.h",
":detection_result.h",
":frame_buffer.h",
":image_classifier.h",
":image_segmenter.h",
":object_detector.h",
":segmentation_result.h",
],
allowlist_symbols_file = ":allowlist_TensorFlowLiteTaskVision.txt",
bundle_name = "TensorFlowLiteTaskVisionC",
minimum_os_version = TFL_MINIMUM_OS_VERSION,
deps = [
"//tensorflow_lite_support/c/task/vision:image_classifier",
"//tensorflow_lite_support/c/task/vision:image_segmenter",
"//tensorflow_lite_support/c/task/vision:object_detector",
],
)

objc_library(
name = "TFLCommon",
hdrs = [
Expand Down Expand Up @@ -120,3 +111,37 @@ objc_library(
"//tensorflow_lite_support/ios:TFLCommon",
],
)

# Xcode 12 does not support ios fat libraries. Frameworks built for multiple
# architectures should be compiled into a .xcframework inside. Bazel currently
# does not support building .xcframework. You have to build the framework
# for the architecture you decide to test on.
# Use the below command to build for arm64 which lets you test the library on
# iOS devices.
# bazel build -c opt --config=ios_arm64 //tensorflow_lite_support/ios:TensorFlowLiteTaskVision_framework
ios_static_framework(
name = "TensorFlowLiteTaskVision_framework",
hdrs = [
":TFLImageEmbedder.h",
":TFLImageClassifier.h",
":TFLObjectDetector.h",
":TFLImageSegmenter.h",
":TFLCommon.h",
":TFLBaseOptions.h",
":TFLClassificationOptions.h",
":TFLCategory.h",
":TFLClassificationResult.h",
":TFLDetectionResult.h",
":TFLSegmentationResult.h",
":GMLImage.h",
],
bundle_name = "TensorFlowLiteTaskVision",
minimum_os_version = TFL_MINIMUM_OS_VERSION,
deps = [
"//tensorflow_lite_support/ios/task/vision:TFLImageEmbedder",
"//tensorflow_lite_support/ios/task/vision:TFLImageClassifier",
"//tensorflow_lite_support/ios/task/vision:TFLObjectDetector",
"//tensorflow_lite_support/ios/task/vision:TFLImageSegmenter",
],
)

Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,18 @@ Pod::Spec.new do |s|
s.authors = 'Google Inc.'
s.license = { :type => 'Apache',:file => "LICENSE"}
s.homepage = 'https://github.com/tensorflow/tflite-support'
s.source = { :http => '${TFLS_DOWNLOAD_URL}' }
s.source = { :http => 'https://dl.dropboxusercontent.com/s/kr3ngm6ji9ei8r5/TensorFlowLiteTaskVisionObjCPP-0.1.3-dev.tar.gz?dl=0' }
s.summary = 'TensorFlow Lite Task Library - Vision'
s.description = 'The Computer Vision APIs of the TFLite Task Library'

s.ios.deployment_target = '10.0'

s.module_name = 'TensorFlowLiteTaskVision'
s.static_framework = true

root_dir_name = "tensorflow_lite_support"
objc_dir = root_dir_name + '/ios/'
objc_task_dir = objc_dir + 'task/'
objc_core_dir = objc_task_dir + 'core/'
objc_processor_dir = objc_task_dir + 'processor/'
objc_vision_dir = objc_task_dir + 'vision/'
gml_image_dir = root_dir_name + '/odml/ios/image/'
s.public_header_files = [
objc_vision_dir + 'apis/*.h',
gml_image_dir + 'apis/*.h',
objc_dir + 'sources/TFLCommon.h',
objc_core_dir + 'sources/TFLBaseOptions.h',
objc_processor_dir + 'sources/{TFLClassificationOptions,TFLCategory,TFLClassificationResult,TFLDetectionResult,TFLSegmentationResult}.h',
objc_vision_dir + 'sources/*.h'
]

c_dir = root_dir_name + '/c/'
c_task_dir = c_dir + 'task/'
c_core_dir = c_task_dir + 'core/'
c_processor_dir = c_task_dir + 'processor/'
c_vision_dir = c_task_dir + 'vision/'
s.source_files = [
c_dir + '*.h',
c_core_dir + '*.h',
c_processor_dir + '{category,bounding_box,classification_options,classification_result,detection_result,segmentation_result}.h',
c_vision_dir + '*.h',
c_vision_dir + 'core/*.h',
objc_dir + 'sources/*',
objc_core_dir + 'sources/*',
objc_processor_dir + 'sources/*',
objc_vision_dir + 'apis/',
objc_vision_dir + '*/sources/*',
objc_vision_dir + 'sources/*',
gml_image_dir + 'apis/*.h',
gml_image_dir + 'sources/*'
]

s.module_map = objc_vision_dir + 'apis/framework.modulemap'
s.pod_target_xcconfig = {
'HEADER_SEARCH_PATHS' =>
'"${PODS_TARGET_SRCROOT}/' + root_dir_name + '/.."/**',
'VALID_ARCHS' => 'x86_64 armv7 arm64',
'VALID_ARCHS' => 'x86_64, arm64, armv7',
}

s.library = 'c++'
s.vendored_frameworks = 'Frameworks/TensorFlowLiteTaskVisionC.framework'
s.frameworks = 'CoreMedia', 'Accelerate'
s.vendored_frameworks = 'Frameworks/TensorFlowLiteTaskVision.framework'
end
33 changes: 31 additions & 2 deletions tensorflow_lite_support/ios/ios.bzl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""TensorFlow Lite Support Library Helper Rules for iOS"""

# When the static framework is built with bazel, the all header files are moved
# When the static framework is built with bazel, all the header files are moved
# to the "Headers" directory with no header path prefixes. This auxiliary rule
# is used for stripping the path prefix to the C API header files included by
# is used for stripping the path prefix from the C API header files included by
# other C API header files.
def strip_c_api_include_path_prefix(name, hdr_labels, prefix = ""):
"""Create modified header files with the common.h include path stripped out.
Expand All @@ -28,3 +28,32 @@ def strip_c_api_include_path_prefix(name, hdr_labels, prefix = ""):
> "$@"
""".format(prefix, hdr_label),
)

# When the static framework is built with bazel, all the header files are moved
# to the "Headers" directory with no header path prefixes. This auxiliary rule
# is used for stripping the path prefix from the iOS API header files imported by
# other iOS API header files.
def strip_ios_api_include_path_prefix(name, hdr_labels, prefix = ""):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Do we actually need this new macro?

As far as I can tell, the new iOS header files (e.g., TFLImageEmbedder.h) are all using plain header filenames in their import statements. That is, it's likely that the processed header file will be the same as the original header file.

  1. If you actually need it (which is probably not the case in my understanding..), does this actually need to be separated from the strip_c_api_include_path_prefix helper above?

Copy link
Contributor Author

@priankakariat priankakariat Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Yes stripping will be needed. Right now only one file has been added for reviewing the structure. In a full implementation the header would have a lot of imports in a hierarchical structure like TFLImageClassifier.h. We will have to strip the prefixes and build the final iOS framework like the C framework is built.
    I added a separate method since objective c files are imported using #import as opposed to c files which are included using #include.
  2. I can incorporate this check in strip_c_api_include_path_prefix and call it strip_api_include_path_prefix which will strip path prefixes irrespective of #import or #include

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. If it's needed, I'd recommend consolidating these two functions into a single one as mentioned before.

"""Create modified header files with the import path stripped out.

Args:
name: The name to be used as a prefix to the generated genrules.
hdr_labels: List of header labels to strip out the include path. Each
label must end with a colon followed by the header file name.
prefix: Optional prefix path to prepend to the header inclusion path.
"""
for hdr_label in hdr_labels:
hdr_filename = hdr_label.split(":")[-1]
hdr_filename = hdr_filename.split("/")[-1]
hdr_basename = hdr_filename.split(".")[0]
native.genrule(
name = "{}_{}".format(name, hdr_basename),
srcs = [hdr_label],
outs = [hdr_filename],
cmd = """
sed 's|#import ".*/\\([^/]\\{{1,\\}}\\.h\\)"|#import "{}\\1"|'\
"$(location {})"\
> "$@"
""".format(prefix, hdr_label),
)

16 changes: 16 additions & 0 deletions tensorflow_lite_support/ios/task/vision/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,19 @@ objc_library(
"//tensorflow_lite_support/odml/ios/image:MLImage",
],
)

objc_library(
name = "TFLImageEmbedder",
srcs = [
"sources/TFLImageEmbedder.mm",
],
hdrs = [
"sources/TFLImageEmbedder.h",
],
features = ["-layering_check"],
module_name = "TFLImageEmbedder",
deps = [
"//tensorflow_lite_support/cc/task/vision:image_embedder",
],
copts = ["-ObjC++","-std=c++14"],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the -ObjC++ option needed?

Copy link
Contributor Author

@priankakariat priankakariat Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding was that "-ObjC++" is needed when you are building with .mm files. Our source file is .mm.

)
25 changes: 0 additions & 25 deletions tensorflow_lite_support/ios/task/vision/apis/TFLTaskVision.h

This file was deleted.

This file was deleted.

29 changes: 29 additions & 0 deletions tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* Copyright 2022 The TensorFlow Authors. 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/Foundation.h>


NS_ASSUME_NONNULL_BEGIN

/**
* A TensorFlow Lite Task Image Classifiier.
*/
NS_SWIFT_NAME(ImageEmbedder)
@interface TFLImageEmbedder : NSObject

- (instancetype)initWithModelPath:(NSString *)modelPath;
@end

NS_ASSUME_NONNULL_END
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/* Copyright 2022 The TensorFlow Authors. 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 "tensorflow_lite_support/ios/task/vision/sources/TFLImageEmbedder.h"
#include "tensorflow_lite_support/cc/task/vision/image_embedder.h"

namespace {
using ImageEmbedderCpp = ::tflite::task::vision::ImageEmbedder;
using ImageEmbedderOptionsCpp =
::tflite::task::vision::ImageEmbedderOptions;
using ::tflite::support::StatusOr;
}

@implementation TFLImageEmbedder {
std::unique_ptr<ImageEmbedderCpp> cppImageEmbedder;
}

- (instancetype)initWithModelPath:(NSString *)modelPath {
self = [super init];
if (self) {
ImageEmbedderOptionsCpp cc_options;

cc_options.mutable_model_file_with_metadata()->set_file_name(modelPath.UTF8String);

StatusOr<std::unique_ptr<ImageEmbedderCpp>> embedder_status =
ImageEmbedderCpp::CreateFromOptions(cc_options);

if (embedder_status.ok()) {
cppImageEmbedder = std::move(embedder_status.value());
}
else {
Comment on lines +42 to +43
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
else {
} else {

return nil;
}
}
return self;
}

@end
36 changes: 36 additions & 0 deletions tensorflow_lite_support/ios/test/task/vision/image_embedder/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
load("@org_tensorflow//tensorflow/lite/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("@org_tensorflow//tensorflow/lite:special_rules.bzl", "tflite_ios_lab_runner")

package(
default_visibility = ["//visibility:private"],
licenses = ["notice"], # Apache 2.0
)


objc_library(
name = "TFLImageEmbedderObjcTestLibrary",
testonly = 1,
srcs = ["TFLImageEmbedderTests.m"],
data = [
"//tensorflow_lite_support/cc/test/testdata/task/vision:test_images",
"//tensorflow_lite_support/cc/test/testdata/task/vision:test_models",
],
tags = TFL_DEFAULT_TAGS,
deps = [
"//tensorflow_lite_support/ios/task/vision:TFLImageEmbedder",
"//tensorflow_lite_support/ios/task/vision/utils:GMLImageUtils",
],
)

ios_unit_test(
name = "TFLImageEmbedderObjcTest",
minimum_os_version = TFL_MINIMUM_OS_VERSION,
runner = tflite_ios_lab_runner("IOS_LATEST"),
tags = TFL_DEFAULT_TAGS + TFL_DISABLED_SANITIZER_TAGS,
deps = [
":TFLImageEmbedderObjcTestLibrary",
],
)

Loading