Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions cpp/HybridTfliteModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,17 @@ std::vector<Tensor> HybridTfliteModel::getInputs() {
int count = TfLiteInterpreterGetInputTensorCount(_interpreter);
std::vector<Tensor> tensors;
tensors.reserve(count);
for (size_t i = 0; i < count; i++) {
for (int32_t i = 0; i < count; i++) {
TfLiteTensor* tensor = TfLiteInterpreterGetInputTensor(_interpreter, i);
if (tensor == nullptr) {
throw std::runtime_error("TFLite: Failed to get input tensor " + std::to_string(i) + "!");
}
int dimensions = TfLiteTensorNumDims(tensor);
std::vector<double> shape;
shape.reserve(dimensions);
for (size_t d = 0; d < dimensions; d++) {
shape.push_back(static_cast<double>(TfLiteTensorDim(tensor, d)));
for (int32_t d = 0; d < dimensions; d++) {
int32_t size = TfLiteTensorDim(tensor, d);
shape.push_back(static_cast<double>(size));
}
tensors.push_back(Tensor(std::string(TfLiteTensorName(tensor)),
dataTypeToString(TfLiteTensorType(tensor)), std::move(shape)));
Expand All @@ -57,16 +58,17 @@ std::vector<Tensor> HybridTfliteModel::getOutputs() {
int count = TfLiteInterpreterGetOutputTensorCount(_interpreter);
std::vector<Tensor> tensors;
tensors.reserve(count);
for (size_t i = 0; i < count; i++) {
for (int32_t i = 0; i < count; i++) {
const TfLiteTensor* tensor = TfLiteInterpreterGetOutputTensor(_interpreter, i);
if (tensor == nullptr) {
throw std::runtime_error("TFLite: Failed to get output tensor " + std::to_string(i) + "!");
}
int dimensions = TfLiteTensorNumDims(tensor);
std::vector<double> shape;
shape.reserve(dimensions);
for (size_t d = 0; d < dimensions; d++) {
shape.push_back(static_cast<double>(TfLiteTensorDim(tensor, d)));
for (int32_t d = 0; d < dimensions; d++) {
int32_t size = TfLiteTensorDim(tensor, d);
shape.push_back(static_cast<double>(size));
}
tensors.push_back(Tensor(std::string(TfLiteTensorName(tensor)),
dataTypeToString(TfLiteTensorType(tensor)), std::move(shape)));
Expand All @@ -82,7 +84,7 @@ void HybridTfliteModel::copyInputBuffers(const std::vector<std::shared_ptr<Array
")!");
}

for (size_t i = 0; i < inputCount; i++) {
for (int32_t i = 0; i < inputCount; i++) {
TfLiteTensor* tensor = TfLiteInterpreterGetInputTensor(_interpreter, i);
const std::shared_ptr<ArrayBuffer>& buffer = input[i];
TfLiteTensorCopyFromBuffer(tensor, buffer->data(), buffer->size());
Expand All @@ -107,7 +109,7 @@ std::vector<std::shared_ptr<ArrayBuffer>> HybridTfliteModel::copyOutputBuffers()
std::vector<std::shared_ptr<ArrayBuffer>> results;
results.reserve(outputCount);

for (size_t i = 0; i < outputCount; i++) {
for (int32_t i = 0; i < outputCount; i++) {
const TfLiteTensor* tensor = TfLiteInterpreterGetOutputTensor(_interpreter, i);
std::shared_ptr<ArrayBuffer> outputBuffer = getOutputBufferForTensor(tensor);

Expand Down
80 changes: 29 additions & 51 deletions cpp/HybridTfliteModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,36 @@
#include <tflite/c/c_api.h>
#include <tflite/delegates/gpu/delegate.h>
#include <tflite/delegates/nnapi/nnapi_delegate_c_api.h>
#else
#include <TensorFlowLiteC/TensorFlowLiteC.h>
#endif

#ifdef __APPLE__
#include <TensorFlowLiteC/TensorFlowLiteC.h>
#if FAST_TFLITE_ENABLE_CORE_ML
#include <TensorFlowLiteCCoreML/TensorFlowLiteCCoreML.h>
#endif
#endif

#define WRONG_PLATFORM_HINT \
" Make sure you are using the correct delegates for the current platform " \
"(e.g. CoreML/Metal on iOS, GPU/NNAPI on Android)."

namespace margelo::nitro::tflite {

/**
* Return a Hardware accelerated delegate, or throws
* if the given delegate type is not available.
*/
TfLiteDelegate* getDelegate(TensorflowModelDelegate delegateType) {
switch (delegateType) {
case TensorflowModelDelegate::CORE_ML:
return getCoreMLDelegate();
case TensorflowModelDelegate::METAL:
return getMetalDelegate();
case TensorflowModelDelegate::NNAPI:
return getNNAPIDelegate();
case TensorflowModelDelegate::ANDROID_GPU:
return getAndroidGPUDelegate();
}
throw std::runtime_error("Unknown Delegate \"" + std::to_string(static_cast<int>(delegateType)) +
"\"!");
}

std::shared_ptr<HybridTfliteModelSpec>
HybridTfliteModule::createModel(const std::shared_ptr<ArrayBuffer>& modelData,
const std::vector<TensorflowModelDelegate>& delegates) {
Expand All @@ -27,52 +43,14 @@ HybridTfliteModule::createModel(const std::shared_ptr<ArrayBuffer>& modelData,
throw std::runtime_error("Failed to create TFLite model from data!");
}

// Configure interpreter via options
TfLiteInterpreterOptions* options = TfLiteInterpreterOptionsCreate();

for (const TensorflowModelDelegate& delegate : delegates) {
switch (delegate) {
case TensorflowModelDelegate::CORE_ML: {
#if FAST_TFLITE_ENABLE_CORE_ML
TfLiteCoreMlDelegateOptions delegateOptions;
TfLiteDelegate* coremlDelegate = TfLiteCoreMlDelegateCreate(&delegateOptions);
TfLiteInterpreterOptionsAddDelegate(options, coremlDelegate);
break;
#else
throw std::runtime_error("CoreML Delegate is not enabled! "
"Set $EnableCoreMLDelegate to true in Podfile and rebuild.");
#endif
}
case TensorflowModelDelegate::METAL: {
throw std::runtime_error("Metal Delegate is not supported!");
}
#ifdef ANDROID
case TensorflowModelDelegate::NNAPI: {
TfLiteNnapiDelegateOptions delegateOptions = TfLiteNnapiDelegateOptionsDefault();
TfLiteDelegate* nnapiDelegate = TfLiteNnapiDelegateCreate(&delegateOptions);
TfLiteInterpreterOptionsAddDelegate(options, nnapiDelegate);
break;
}
case TensorflowModelDelegate::ANDROID_GPU: {
TfLiteGpuDelegateOptionsV2 delegateOptions = TfLiteGpuDelegateOptionsV2Default();
TfLiteDelegate* gpuDelegate = TfLiteGpuDelegateV2Create(&delegateOptions);
TfLiteInterpreterOptionsAddDelegate(options, gpuDelegate);
break;
}
#else
case TensorflowModelDelegate::NNAPI: {
throw std::runtime_error(
"NNAPI Delegate is only supported on Android!" WRONG_PLATFORM_HINT);
}
case TensorflowModelDelegate::ANDROID_GPU: {
throw std::runtime_error(
"Android-GPU Delegate is only supported on Android!" WRONG_PLATFORM_HINT);
}
#endif
default: {
// use default CPU delegate.
break;
}
}
// Add all hardware accelerated delegates (e.g. GPU, NPU, ...)
// if any. The default CPU delegate will always be available.
for (const TensorflowModelDelegate& delegateType : delegates) {
TfLiteDelegate* delegate = getDelegate(delegateType);
TfLiteInterpreterOptionsAddDelegate(options, delegate);
}

TfLiteInterpreter* interpreter = TfLiteInterpreterCreate(model, options);
Expand All @@ -87,7 +65,7 @@ HybridTfliteModule::createModel(const std::shared_ptr<ArrayBuffer>& modelData,
throw std::runtime_error("Failed to create TFLite interpreter!");
}

// Wrap in HybridTfliteModel — stores shared_ptr<ArrayBuffer> to keep bytes alive
// Wrap in HybridTfliteModel — stores shared_ptr<ArrayBuffer> to keep model data bytes alive
return std::make_shared<HybridTfliteModel>(interpreter, modelData, delegates);
}

Expand Down
70 changes: 68 additions & 2 deletions cpp/TfliteHelpers.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,21 @@
#include "TfliteHelpers.hpp"

#ifdef ANDROID
#include <tflite/c/c_api.h>
#include <tflite/delegates/gpu/delegate.h>
#include <tflite/delegates/nnapi/nnapi_delegate_c_api.h>
#endif

#ifdef __APPLE__
#include <TensorFlowLiteC/TensorFlowLiteC.h>
#if FAST_TFLITE_ENABLE_CORE_ML
#include <TensorFlowLiteCCoreML/TensorFlowLiteCCoreML.h>
#endif
#endif

namespace margelo::nitro::tflite {

// TODO: Remove this, this doesn't seem like a good idea at all.
typedef float float32_t;
typedef double float64_t;

Expand Down Expand Up @@ -29,10 +45,14 @@ std::string tfLiteStatusToString(TfLiteStatus status) {

std::string dataTypeToString(TfLiteType dataType) {
switch (dataType) {
case kTfLiteFloat16:
return "float16";
case kTfLiteFloat32:
return "float32";
case kTfLiteFloat64:
return "float64";
case kTfLiteBFloat16:
return "bfloat16";
case kTfLiteInt4:
return "int4";
case kTfLiteInt8:
Expand Down Expand Up @@ -71,6 +91,8 @@ std::string dataTypeToString(TfLiteType dataType) {

size_t getTFLTensorDataTypeSize(TfLiteType dataType) {
switch (dataType) {
case kTfLiteBool:
return sizeof(bool);
case kTfLiteFloat32:
return sizeof(float32_t);
case kTfLiteInt32:
Expand All @@ -91,17 +113,61 @@ size_t getTFLTensorDataTypeSize(TfLiteType dataType) {
return sizeof(uint32_t);
case kTfLiteUInt16:
return sizeof(uint16_t);
default:
throw std::runtime_error("Tensor DataType \"" + dataTypeToString(dataType) +
"\" is not supported!");
}
throw std::runtime_error("TFLite: Unsupported output data type! " + dataTypeToString(dataType));
}

int getTensorTotalLength(const TfLiteTensor* tensor) {
int dimensions = TfLiteTensorNumDims(tensor);
if (dimensions < 1)
return 0;
int size = 1;
for (size_t i = 0; i < dimensions; i++) {
for (int32_t i = 0; i < dimensions; i++) {
size *= TfLiteTensorDim(tensor, i);
}
return size;
}

TfLiteDelegate* getCoreMLDelegate() {
#ifdef __APPLE__
#if FAST_TFLITE_ENABLE_CORE_ML
TfLiteCoreMlDelegateOptions delegateOptions;
TfLiteDelegate* coreMlDelegate = TfLiteCoreMlDelegateCreate(&delegateOptions);
return coreMlDelegate;
#else // FAST_TFLITE_ENABLE_CORE_ML
throw std::runtime_error("The CoreML Delegate (\"core-ml\") is not enabled! "
"Set `$EnableCoreMLDelegate` to `true` in your Podfile, and rebuild.");
#endif
#else // __APPLE__
throw std::runtime_error(
"The CoreML Delegate (\"core-ml\") is only supported on Apple Platforms!");
#endif
}

TfLiteDelegate* getMetalDelegate() {
throw std::runtime_error("Metal Delegate is not yet supported!");
}

TfLiteDelegate* getNNAPIDelegate() {
#ifdef ANDROID
TfLiteNnapiDelegateOptions delegateOptions = TfLiteNnapiDelegateOptionsDefault();
TfLiteDelegate* nnapiDelegate = TfLiteNnapiDelegateCreate(&delegateOptions);
return nnapiDelegate;
#else // ANDROID
throw std::runtime_error("The NNAPI Delegate (\"nnapi\") is only supported on Android!");
#endif
}

TfLiteDelegate* getAndroidGPUDelegate() {
#ifdef ANDROID
TfLiteGpuDelegateOptionsV2 delegateOptions = TfLiteGpuDelegateOptionsV2Default();
TfLiteDelegate* gpuDelegate = TfLiteGpuDelegateV2Create(&delegateOptions);
#else // ANDROID
throw std::runtime_error(
"The Android GPU Delegate (\"android-gpu\") is only supported on Android!");
#endif
}

} // namespace margelo::nitro::tflite
9 changes: 9 additions & 0 deletions cpp/TfliteHelpers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@
#include <TensorFlowLiteC/TensorFlowLiteC.h>
#endif

namespace margelo::nitro::tflite {

std::string tfLiteStatusToString(TfLiteStatus status);
std::string dataTypeToString(TfLiteType dataType);
size_t getTFLTensorDataTypeSize(TfLiteType dataType);
int getTensorTotalLength(const TfLiteTensor* tensor);

TfLiteDelegate* getCoreMLDelegate();
TfLiteDelegate* getMetalDelegate();
TfLiteDelegate* getNNAPIDelegate();
TfLiteDelegate* getAndroidGPUDelegate();

}
12 changes: 10 additions & 2 deletions example/ios/TfliteExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-TfliteExample/Pods-TfliteExample-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-TfliteExample/Pods-TfliteExample-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TfliteExample/Pods-TfliteExample-frameworks.sh\"\n";
Expand Down Expand Up @@ -234,10 +238,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-TfliteExample/Pods-TfliteExample-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-TfliteExample/Pods-TfliteExample-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-TfliteExample/Pods-TfliteExample-resources.sh\"\n";
Expand All @@ -264,7 +272,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B3P8HDC2S6;
DEVELOPMENT_TEAM = CJW62Q77E7;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = TfliteExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
Expand Down Expand Up @@ -295,7 +303,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = B3P8HDC2S6;
DEVELOPMENT_TEAM = CJW62Q77E7;
INFOPLIST_FILE = TfliteExample/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down
12 changes: 4 additions & 8 deletions nitrogen/generated/shared/c++/TensorflowModelDelegate.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/loadTensorflowModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const tfliteModule =
*/
export async function loadTensorflowModel(
source: ModelSource,
delegates: TensorflowModelDelegate[] = ['default']
delegates: TensorflowModelDelegate[]
): Promise<TfliteModel> {
let uri: string
if (typeof source === 'number') {
Expand Down
Loading
Loading