/** * Copyright (C) 2020-2021 Savoir-faire Linux Inc. * * Author: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 * USA. */ #include "TFInference.h" // Std libraries #include <fstream> #include <iostream> #include <numeric> #include <stdlib.h> #ifdef TFLITE // Tensorflow headers #include <tensorflow/lite/builtin_op_data.h> #include <tensorflow/lite/interpreter.h> #include <tensorflow/lite/kernels/register.h> #include <tensorflow/lite/model.h> #include <tensorflow/lite/optional_debug_tools.h> #else #ifdef WIN32 #include <WinBase.h> #endif #include <tensorflow/core/graph/graph.h> //#include <tensorflow/core/graph/default_device.h> #include <tensorflow/core/platform/env.h> #endif // TFLITE #include <pluglog.h> const char sep = separator(); const std::string TAG = "FORESEG"; namespace jami { TensorflowInference::TensorflowInference(TFModel tfModel) : tfModel(tfModel) {} TensorflowInference::~TensorflowInference() {} bool TensorflowInference::isAllocated() const { return allocated_; } #ifdef TFLITE void TensorflowInference::loadModel() { Plog::log(Plog::LogPriority::INFO, TAG, "inside loadModel()"); flatbufferModel = tflite::FlatBufferModel::BuildFromFile(tfModel.modelPath.c_str()); if (!flatbufferModel) { std::runtime_error("Failed to load the model file"); } Plog::log(Plog::LogPriority::INFO, "TENSOR", "MODEL LOADED"); } void TensorflowInference::buildInterpreter() { Plog::log(Plog::LogPriority::INFO, TAG, "inside buildInterpreter()"); // Build the interpreter tflite::ops::builtin::BuiltinOpResolver resolver; tflite::InterpreterBuilder builder(*flatbufferModel, resolver); builder(&interpreter); if (interpreter) { setInterpreterSettings(); Plog::log(Plog::LogPriority::INFO, "TENSOR", "INTERPRETER BUILT"); if (tfModel.useNNAPI) { TfLiteDelegate* optionalNnApiDelegate = tflite::NnApiDelegate(); if (interpreter->ModifyGraphWithDelegate(optionalNnApiDelegate) != kTfLiteOk) { Plog::log(Plog::LogPriority::INFO, "TENSOR", "INTERPRETER ERROR!!!"); } else { Plog::log(Plog::LogPriority::INFO, "TENSOR", "INTERPRETER SET"); allocateTensors(); } } else { allocateTensors(); } } } void TensorflowInference::allocateTensors() { if (interpreter->AllocateTensors() != kTfLiteOk) { std::runtime_error("Failed to allocate tensors!"); } else { Plog::log(Plog::LogPriority::INFO, "TENSOR", "TENSORS ALLOCATED"); allocated_ = true; } } void TensorflowInference::describeModelTensors() const { // PrintInterpreterState(interpreter.get()); std::ostringstream oss; oss << "=============== inputs/outputs dimensions ===============" << "\n"; const std::vector<int> inputs = interpreter->inputs(); const std::vector<int> outputs = interpreter->outputs(); oss << "number of inputs: " << inputs.size() << std::endl; oss << "number of outputs: " << outputs.size() << std::endl; Plog::log(Plog::LogPriority::INFO, "TENSOR", oss.str()); int input = interpreter->inputs()[0]; int output = interpreter->outputs()[0]; oss << "input 0 index: " << input << std::endl; oss << "output 0 index: " << output << std::endl; oss << "=============== input dimensions ===============" << std::endl; Plog::log(Plog::LogPriority::INFO, "TENSOR", oss.str()); // get input dimension from the input tensor metadata // assuming one input only for (size_t i = 0; i < inputs.size(); i++) { std::stringstream ss; ss << "Input " << i << " ➛ "; describeTensor(ss.str(), interpreter->inputs()[i]); } oss.str(""); oss << "=============== output dimensions ===============" << "\n"; Plog::log(Plog::LogPriority::INFO, "TENSOR", oss.str()); // get input dimension from the input tensor metadata // assuming one input only for (size_t i = 0; i < outputs.size(); i++) { std::stringstream ss; ss << "Output " << i << " ➛ "; describeTensor(ss.str(), interpreter->outputs()[i]); } } void TensorflowInference::setInterpreterSettings() { // interpreter->UseNNAPI(tfModel.useNNAPI); interpreter->SetAllowFp16PrecisionForFp32(tfModel.allowFp16PrecisionForFp32); interpreter->SetNumThreads(static_cast<int>(tfModel.numberOfThreads)); } void TensorflowInference::describeTensor(std::string prefix, int index) const { std::vector<int> dimensions = getTensorDimensions(index); size_t nbDimensions = dimensions.size(); std::ostringstream tensorDescription; tensorDescription << prefix; for (size_t i = 0; i < nbDimensions; i++) { if (i == dimensions.size() - 1) { tensorDescription << dimensions[i]; } else { tensorDescription << dimensions[i] << " x "; } } tensorDescription << std::endl; Plog::log(Plog::LogPriority::INFO, "TENSOR", tensorDescription.str()); } std::vector<int> TensorflowInference::getTensorDimensions(int index) const { TfLiteIntArray* dims = interpreter->tensor(index)->dims; size_t size = static_cast<size_t>(interpreter->tensor(index)->dims->size); std::vector<int> result; result.reserve(size); for (size_t i = 0; i != size; i++) { result.push_back(dims->data[i]); } dims = nullptr; return result; } void TensorflowInference::runGraph() { for (size_t i = 0; i < tfModel.numberOfRuns; i++) { if (interpreter->Invoke() != kTfLiteOk) { Plog::log(Plog::LogPriority::INFO, "RUN GRAPH", "A problem occured when running the graph"); } } } void TensorflowInference::init() { /// Loading the model loadModel(); buildInterpreter(); describeModelTensors(); } #else // Reads a model graph definition from disk, and creates a session object you // can use to run it. void TensorflowInference::LoadGraph() { tensorflow::GraphDef graph_def; tensorflow::Status load_graph_status = tensorflow::ReadBinaryProto(tensorflow::Env::Default(), tfModel.modelPath, &graph_def); if (!load_graph_status.ok()) { allocated_ = false; Plog::log(Plog::LogPriority::INFO, "LOAD GRAPH", "A problem occured when loading the graph"); return; } Plog::log(Plog::LogPriority::INFO, "LOAD GRAPH", "graph loaded"); // Plog::log(Plog::LogPriority::INFO, "GRAPH SIZE: ", // std::to_string(graph_def.node_size())); for (auto& node : // *graph_def.mutable_node()) // { // Plog::log(Plog::LogPriority::INFO, "GRAPH NODE: ", // node.name().c_str()); // // Plog::log(Plog::LogPriority::INFO, "\tNODE SIZE: ", // node.().c_str()); // } PluginParameters* parameters = getGlobalPluginParameters(); tensorflow::SessionOptions options; if (parameters->useGPU) { options.config.mutable_gpu_options()->set_allow_growth(true); options.config.mutable_gpu_options()->set_per_process_gpu_memory_fraction(0.3); } else { #ifdef WIN32 options.config.mutable_device_count()->insert({"CPU", 1}); options.config.mutable_device_count()->insert({"GPU", 0}); #else setenv("CUDA_VISIBLE_DEVICES", "", 1); #endif } (&session)->reset(tensorflow::NewSession(options)); tensorflow::Status session_create_status = session->Create(graph_def); if (!session_create_status.ok()) { Plog::log(Plog::LogPriority::INFO, "INIT SESSION", "A problem occured when initializating session"); allocated_ = true; return; } Plog::log(Plog::LogPriority::INFO, "INIT SESSION", "session initialized"); allocated_ = true; } void TensorflowInference::runGraph() { for (size_t i = 0; i < tfModel.numberOfRuns; i++) { // Actually run the image through the model. tensorflow::Status run_status = session->Run({{tfModel.inputLayer, imageTensor}}, {tfModel.outputLayer}, {}, &outputs); if (!run_status.ok()) { Plog::log(Plog::LogPriority::INFO, "RUN GRAPH", "A problem occured when running the graph"); } } } void TensorflowInference::init() { // Loading the model LoadGraph(); } #endif } // namespace jami