| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_interpreter.h" |
|
|
| #include <cstdarg> |
| #include <cstddef> |
| #include <cstdint> |
|
|
| #include "edge-impulse-sdk/third_party/flatbuffers/include/flatbuffers/flatbuffers.h" |
| #include "edge-impulse-sdk/tensorflow/lite/c/c_api_types.h" |
| #include "edge-impulse-sdk/tensorflow/lite/c/common.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/flatbuffer_utils.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/memory_helpers.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_allocator.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_log.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_op_resolver.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/micro_profiler_interface.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/flatbuffer_conversions_bridge.h" |
| #include "edge-impulse-sdk/tensorflow/lite/micro/op_resolver_bridge.h" |
| #include "edge-impulse-sdk/tensorflow/lite/schema/schema_generated.h" |
| #include "edge-impulse-sdk/tensorflow/lite/schema/schema_utils.h" |
|
|
| namespace tflite { |
|
|
| MicroInterpreter::MicroInterpreter(const Model* model, |
| const MicroOpResolver& op_resolver, |
| uint8_t* tensor_arena, |
| size_t tensor_arena_size, |
| MicroResourceVariables* resource_variables, |
| MicroProfilerInterface* profiler) |
| : model_(model), |
| op_resolver_(op_resolver), |
| allocator_(*MicroAllocator::Create(tensor_arena, tensor_arena_size)), |
|
|
| graph_(&context_, model, &allocator_, resource_variables), |
| tensors_allocated_(false), |
| initialization_status_(kTfLiteError), |
| input_tensors_(nullptr), |
| output_tensors_(nullptr), |
| micro_context_(&allocator_, model_, &graph_) { |
| Init(profiler); |
| } |
|
|
| MicroInterpreter::MicroInterpreter(const Model* model, |
| const MicroOpResolver& op_resolver, |
| MicroAllocator* allocator, |
| MicroResourceVariables* resource_variables, |
| MicroProfilerInterface* profiler) |
| : model_(model), |
| op_resolver_(op_resolver), |
| allocator_(*allocator), |
| graph_(&context_, model, allocator, resource_variables), |
| tensors_allocated_(false), |
| initialization_status_(kTfLiteError), |
| input_tensors_(nullptr), |
| output_tensors_(nullptr), |
| micro_context_(&allocator_, model_, &graph_) { |
| Init(profiler); |
| } |
|
|
| MicroInterpreter::~MicroInterpreter() { |
| if (graph_.GetAllocations() != nullptr) { |
| graph_.FreeSubgraphs(); |
| } |
| #ifdef EON_COMPILER_RUN |
| if (node_and_registrations_ != nullptr) { |
| for (size_t i = 0; i < model_->subgraphs()->Get(0)->operators()->size(); ++i) { |
| TfLiteNode* node = &(node_and_registrations_[i].node); |
| const TfLiteRegistration* registration = |
| node_and_registrations_[i].registration; |
| |
| |
| if (registration != nullptr && registration->free != nullptr) { |
| registration->free(&context_, node->user_data); |
| } |
| } |
| } |
| #endif |
| } |
|
|
| void MicroInterpreter::Init(MicroProfilerInterface* profiler) { |
| context_.impl_ = static_cast<void*>(µ_context_); |
| context_.ReportError = MicroContextReportOpError; |
| context_.GetTensor = MicroContextGetTensor; |
| context_.GetEvalTensor = MicroContextGetEvalTensor; |
| context_.profiler = profiler; |
|
|
| initialization_status_ = kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroInterpreter::PrepareNodeAndRegistrationDataFromFlatbuffer() { |
| for (int subgraph_idx = 0; subgraph_idx < graph_.NumSubgraphs(); |
| subgraph_idx++) { |
| const SubGraph* subgraph = model_->subgraphs()->Get(subgraph_idx); |
| TFLITE_DCHECK(subgraph != nullptr); |
|
|
| auto* opcodes = model_->operator_codes(); |
| TfLiteBridgeBuiltinDataAllocator* builtin_data_allocator = |
| allocator_.GetBuiltinDataAllocator(); |
| uint32_t operators_size = NumSubgraphOperators(subgraph); |
| for (size_t i = 0; i < operators_size; ++i) { |
| const auto* op = subgraph->operators()->Get(i); |
| const size_t index = op->opcode_index(); |
| if (index >= opcodes->size()) { |
| MicroPrintf("Missing registration for opcode_index %d\n", index); |
| return kTfLiteError; |
| } |
| const auto* opcode = opcodes->Get(index); |
| TfLiteStatus status = |
| GetRegistrationFromOpCode(opcode, op_resolver_, |
| &(graph_.GetAllocations()[subgraph_idx] |
| .node_and_registrations[i] |
| .registration)); |
| if (status != kTfLiteOk) { |
| MicroPrintf("Failed to get registration from op code %s\n ", |
| EnumNameBuiltinOperator(GetBuiltinCode(opcode))); |
| return status; |
| } |
| const auto* registration = graph_.GetAllocations()[subgraph_idx] |
| .node_and_registrations[i] |
| .registration; |
| if (registration == nullptr) { |
| MicroPrintf("Skipping op for opcode_index %d\n", index); |
| return kTfLiteError; |
| } |
| BuiltinOperator op_type = |
| static_cast<BuiltinOperator>(registration->builtin_code); |
|
|
| const char* custom_data = nullptr; |
| size_t custom_data_size = 0; |
| unsigned char* builtin_data = nullptr; |
|
|
| if (op_type == BuiltinOperator_CUSTOM) { |
| |
| if (op->custom_options() != nullptr) { |
| custom_data = |
| reinterpret_cast<const char*>(op->custom_options()->data()); |
| custom_data_size = op->custom_options()->size(); |
| } |
| } else { |
| if (op->custom_options() != nullptr) { |
| MicroPrintf( |
| "Unsupported behavior: found builtin operator %s with custom " |
| "options.\n", |
| EnumNameBuiltinOperator(op_type)); |
| return kTfLiteError; |
| } |
|
|
| TfLiteBridgeBuiltinParseFunction parser = |
| op_resolver_.GetOpDataParser(op_type); |
| if (parser == nullptr) { |
| MicroPrintf("Did not find a parser for %s", |
| EnumNameBuiltinOperator(op_type)); |
|
|
| return kTfLiteError; |
| } |
| TF_LITE_ENSURE_STATUS(CallBuiltinParseFunction( |
| parser, op, builtin_data_allocator, (void**)(&builtin_data))); |
| } |
|
|
| TfLiteIntArray* inputs_array = |
| FlatBufferVectorToTfLiteTypeArray(op->inputs()); |
| TfLiteIntArray* outputs_array = |
| FlatBufferVectorToTfLiteTypeArray(op->outputs()); |
|
|
| TfLiteNode* node = &( |
| graph_.GetAllocations()[subgraph_idx].node_and_registrations[i].node); |
| *node = {}; |
| node->inputs = inputs_array; |
| node->outputs = outputs_array; |
| node->builtin_data = reinterpret_cast<void*>(builtin_data); |
| node->custom_initial_data = custom_data; |
| node->custom_initial_data_size = custom_data_size; |
|
|
| if (op->intermediates() && (op->intermediates()->size() > 0)) { |
| node->intermediates = |
| FlatBufferVectorToTfLiteTypeArray(op->intermediates()); |
| } |
| } |
| } |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroInterpreter::AllocateTensors(bool run_all_prep_ops) { |
| SubgraphAllocations* allocations = allocator_.StartModelAllocation(model_); |
|
|
| if (allocations == nullptr) { |
| MicroPrintf("Failed starting model allocation.\n"); |
| initialization_status_ = kTfLiteError; |
| return kTfLiteError; |
| } |
|
|
| graph_.SetSubgraphAllocations(allocations); |
|
|
| TF_LITE_ENSURE_STATUS(PrepareNodeAndRegistrationDataFromFlatbuffer()); |
|
|
| |
| context_.AllocatePersistentBuffer = MicroContextAllocatePersistentBuffer; |
| context_.RequestScratchBufferInArena = nullptr; |
| context_.GetScratchBuffer = nullptr; |
| context_.GetExternalContext = nullptr; |
| TF_LITE_ENSURE_STATUS(graph_.InitSubgraphs()); |
|
|
| |
| |
| context_.RequestScratchBufferInArena = |
| MicroContextRequestScratchBufferInArena; |
| |
| context_.GetExternalContext = MicroContextGetExternalContext; |
|
|
| TF_LITE_ENSURE_STATUS(graph_.PrepareSubgraphs(run_all_prep_ops)); |
|
|
| |
| |
| context_.AllocatePersistentBuffer = nullptr; |
| context_.RequestScratchBufferInArena = nullptr; |
| context_.GetScratchBuffer = MicroContextGetScratchBuffer; |
|
|
| TF_LITE_ENSURE_OK(&context_, allocator_.FinishModelAllocation( |
| model_, graph_.GetAllocations(), |
| &scratch_buffer_handles_)); |
|
|
| micro_context_.SetScratchBufferHandles(scratch_buffer_handles_); |
|
|
| |
| |
| input_tensors_ = |
| reinterpret_cast<TfLiteTensor**>(allocator_.AllocatePersistentBuffer( |
| sizeof(TfLiteTensor*) * inputs_size())); |
| if (input_tensors_ == nullptr) { |
| MicroPrintf( |
| "Failed to allocate memory for context->input_tensors_, " |
| "%d bytes required", |
| sizeof(TfLiteTensor*) * inputs_size()); |
| return kTfLiteError; |
| } |
|
|
| for (size_t i = 0; i < inputs_size(); ++i) { |
| input_tensors_[i] = allocator_.AllocatePersistentTfLiteTensor( |
| model_, graph_.GetAllocations(), inputs().Get(i), 0); |
| if (input_tensors_[i] == nullptr) { |
| MicroPrintf("Failed to initialize input tensor %d", i); |
| return kTfLiteError; |
| } |
| } |
|
|
| |
| |
| output_tensors_ = |
| reinterpret_cast<TfLiteTensor**>(allocator_.AllocatePersistentBuffer( |
| sizeof(TfLiteTensor*) * outputs_size())); |
| if (output_tensors_ == nullptr) { |
| MicroPrintf( |
| "Failed to allocate memory for context->output_tensors_, " |
| "%d bytes required", |
| sizeof(TfLiteTensor*) * outputs_size()); |
| return kTfLiteError; |
| } |
|
|
| for (size_t i = 0; i < outputs_size(); ++i) { |
| output_tensors_[i] = allocator_.AllocatePersistentTfLiteTensor( |
| model_, graph_.GetAllocations(), outputs().Get(i), 0); |
| if (output_tensors_[i] == nullptr) { |
| MicroPrintf("Failed to initialize output tensor %d", i); |
| return kTfLiteError; |
| } |
| } |
|
|
| TF_LITE_ENSURE_STATUS(Reset()); |
|
|
| #ifdef EON_COMPILER_RUN |
| node_and_registrations_ = allocations->node_and_registrations; |
| #endif |
|
|
| tensors_allocated_ = true; |
| return kTfLiteOk; |
| } |
|
|
| TfLiteStatus MicroInterpreter::Invoke() { |
| if (initialization_status_ != kTfLiteOk) { |
| MicroPrintf("Invoke() called after initialization failed\n"); |
| return kTfLiteError; |
| } |
|
|
| |
| |
| if (!tensors_allocated_) { |
| TF_LITE_ENSURE_OK(&context_, AllocateTensors(true)); |
| } |
| return graph_.InvokeSubgraph(0); |
| } |
|
|
| TfLiteTensor* MicroInterpreter::input(size_t index) { |
| const size_t length = inputs_size(); |
| if (index >= length) { |
| MicroPrintf("Input index %d out of range (length is %d)", index, length); |
| return nullptr; |
| } |
| return input_tensors_[index]; |
| } |
|
|
| TfLiteTensor* MicroInterpreter::output(size_t index) { |
| const size_t length = outputs_size(); |
| if (index >= length) { |
| MicroPrintf("Output index %d out of range (length is %d)", index, length); |
| return nullptr; |
| } |
| return output_tensors_[index]; |
| } |
|
|
| TfLiteTensor* MicroInterpreter::tensor(size_t index, size_t subgraph_idx) { |
| const size_t length = tensors_size(subgraph_idx); |
| if (index >= length) { |
| MicroPrintf("Tensor index %d out of range (length is %d)", index, length); |
| return nullptr; |
| } |
| return allocator_.AllocatePersistentTfLiteTensor(model_, graph_.GetAllocations(), index, subgraph_idx); |
| } |
|
|
| |
| |
| TfLiteStatus MicroInterpreter::Reset() { |
| TfLiteStatus status = graph_.FreeSubgraphs(); |
| if (status != kTfLiteOk) { |
| return status; |
| } |
| return graph_.ResetVariableTensors(); |
| } |
|
|
| TfLiteStatus MicroInterpreter::SetMicroExternalContext( |
| void* external_context_payload) { |
| return micro_context_.set_external_context(external_context_payload); |
| } |
|
|
| } |
|
|