Para melhorar o desempenho no Raspberry Pi, é possível utilizar a linguagem C++ e bibliotecas optimizadas para acelerar o cálculo dos modelos de deteção de objectos. É isto que o TensorFlow Lite oferece.
Um bom ponto de partida é o sítio Web QEngineering.
Hardware
- Raspberry Pi 4
- Ecrã+rato+teclado
- Cartão SD com o sistema operativo Raspbian 64bits
Configuração
Para obter o melhor desempenho, é necessário instalar a versão de 64 bits do Raspberry Pi OS. Esta versão está disponível no software Raspberry Pi Imager no menu Raspberry Pi OS (outros).
Instalando o Code::Blocks
O IDE de blocos de código é um software como o Thonny ou o Geany que permite compilar e executar código escrito em C
sudo apt-get install codeblocks
Instalando o OpenCV para Cpp
Para instalar a versão Cpp do OpenCV, usamos a ferramenta apt-get
sudo apt-get install libopencv-dev
Instalando o TensorFlow Lite no Raspberry Pi OS 64bits
Para obter o melhor desempenho no Raspberry Pi, uma solução é usar a versão Lite do TensorFlow. Por favor, siga o procedimento descrito no site da QEngineering para instalar o TensorFlow Lite no Raspberry Pi OS 64bits.
N.B.: não se esqueça de reiniciar o Raspberry Pi depois de instalar o tensorflow.
Código de deteção de objectos
Pode descarregar o código e o projeto no Github da QEngineering
O modelo é construído a partir do ficheiro tflite. Neste projeto, utilizamos o MobileNet V1
std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile("detect.tflite");
Em seguida, abra um fluxo de vídeo (ou um ficheiro de vídeo ou imagem)
VideoCapture cap(0);
O modelo é então executado em cada imagem
interpreter->Invoke();
Por fim, traçamos os resultados da deteção na imagem e apresentamo-los.
#include <stdio.h> #include <opencv2/opencv.hpp> #include <opencv2/dnn.hpp> #include <opencv2/highgui.hpp> #include <fstream> #include <iostream> #include <opencv2/core/ocl.hpp> #include "tensorflow/lite/interpreter.h" #include "tensorflow/lite/kernels/register.h" #include "tensorflow/lite/string_util.h" #include "tensorflow/lite/model.h" #include <cmath> using namespace cv; using namespace std; const size_t width = 300; const size_t height = 300; std::vector<std::string> Labels; std::unique_ptr<tflite::Interpreter> interpreter; static bool getFileContent(std::string fileName) { // Open the File std::ifstream in(fileName.c_str()); // Check if object is valid if(!in.is_open()) return false; std::string str; // Read the next line from File untill it reaches the end. while (std::getline(in, str)) { // Line contains string of length > 0 then save it in vector if(str.size()>0) Labels.push_back(str); } // Close The File in.close(); return true; } void detect_from_video(Mat &src) { Mat image; int cam_width =src.cols; int cam_height=src.rows; // copy image to input as input tensor cv::resize(src, image, Size(300,300)); memcpy(interpreter->typed_input_tensor<uchar>(0), image.data, image.total() * image.elemSize()); interpreter->SetAllowFp16PrecisionForFp32(true); interpreter->SetNumThreads(4); //quad core // cout << "tensors size: " << interpreter->tensors_size() << "\n"; // cout << "nodes size: " << interpreter->nodes_size() << "\n"; // cout << "inputs: " << interpreter->inputs().size() << "\n"; // cout << "input(0) name: " << interpreter->GetInputName(0) << "\n"; // cout << "outputs: " << interpreter->outputs().size() << "\n"; interpreter->Invoke(); // run your model const float* detection_locations = interpreter->tensor(interpreter->outputs()[0])->data.f; const float* detection_classes=interpreter->tensor(interpreter->outputs()[1])->data.f; const float* detection_scores = interpreter->tensor(interpreter->outputs()[2])->data.f; const int num_detections = *interpreter->tensor(interpreter->outputs()[3])->data.f; //there are ALWAYS 10 detections no matter how many objects are detectable // cout << "number of detections: " << num_detections << "\n"; const float confidence_threshold = 0.5; for(int i = 0; i < num_detections; i++){ if(detection_scores[i] > confidence_threshold){ int det_index = (int)detection_classes[i]+1; float y1=detection_locations[4*i ]*cam_height; float x1=detection_locations[4*i+1]*cam_width; float y2=detection_locations[4*i+2]*cam_height; float x2=detection_locations[4*i+3]*cam_width; Rect rec((int)x1, (int)y1, (int)(x2 - x1), (int)(y2 - y1)); rectangle(src,rec, Scalar(0, 0, 255), 1, 8, 0); putText(src, format("%s", Labels[det_index].c_str()), Point(x1, y1-5) ,FONT_HERSHEY_SIMPLEX,0.5, Scalar(0, 0, 255), 1, 8, 0); } } } int main(int argc,char ** argv) { float f; float FPS[16]; int i, Fcnt=0; Mat frame; chrono::steady_clock::time_point Tbegin, Tend; for(i=0;i<16;i++) FPS[i]=0.0; // Load model std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile("detect.tflite"); // Build the interpreter tflite::ops::builtin::BuiltinOpResolver resolver; tflite::InterpreterBuilder(*model.get(), resolver)(&interpreter); interpreter->AllocateTensors(); // Get the names bool result = getFileContent("COCO_labels.txt"); if(!result) { cout << "loading labels failed"; exit(-1); } VideoCapture cap("James.mp4"); if (!cap.isOpened()) { cerr << "ERROR: Unable to open the camera" << endl; return 0; } cout << "Start grabbing, press ESC on Live window to terminate" << endl; while(1){ // frame=imread("Traffic.jpg"); //need to refresh frame before dnn class detection cap >> frame; if (frame.empty()) { cerr << "ERROR: Unable to grab from the camera" << endl; break; } Tbegin = chrono::steady_clock::now(); detect_from_video(frame); Tend = chrono::steady_clock::now(); //calculate frame rate f = chrono::duration_cast <chrono::milliseconds> (Tend - Tbegin).count(); if(f>0.0) FPS[((Fcnt++)&0x0F)]=1000.0/f; for(f=0.0, i=0;i<16;i++){ f+=FPS[i]; } putText(frame, format("FPS %0.2f", f/16),Point(10,20),FONT_HERSHEY_SIMPLEX,0.6, Scalar(0, 0, 255)); //show output // cout << "FPS" << f/16 << endl; imshow("RPi 4 - 1,9 GHz - 2 Mb RAM", frame); char esc = waitKey(5); if(esc == 27) break; } cout << "Closing the camera" << endl; destroyAllWindows(); cout << "Bye!" << endl; return 0; }
Os resultados deste código mostram a deteção de objectos a uma velocidade entre 10 e 20 FPS, mas com baixa precisão neste exemplo.
É necessário realizar um estudo com outros exemplos e modelos, mas este projeto mostra o interesse do C++ e do Tensorflow Lite para a deteção de objectos no Raspberry Pi.