Pour améliorer les performances sur Raspberry Pi vous pouvez utiliser le langage C++ ainsi que des librairies optimisées afin d’accélérer la vitesse de calcul des modèles de détection d’objets. C’est ce que propose TensorFlow Lite.
Une bonne référence pour commencer sur le sujet est le site QEngineering.
Matériel
- Raspberry Pi 4
- Écran+souris+clavier
- Carte SD avec OS Raspbian 64bits
Configuration
Pour de meilleures performances, il vous faudra installer la version 64bits de Raspberry Pi OS. Cette version est disponible dans le logiciel Raspberry Pi Imager dans le menu Raspberry Pi OS (others)
Installation de Code::Blocks
Code blocks IDE est un logiciel comme Thonny ou Geany qui vous permettra de compiler et de lancer des codes écrits en C/C++
sudo apt-get install codeblocks
Installation d’OpenCV pour Cpp
Pour installer la version Cpp d’OpenCV nous passons par l’outil apt-get
sudo apt-get install libopencv-dev
Installation de TensorFlow Lite sur Raspberry Pi OS 64bits
Pour de meilleures performances sur Raspberry Pi, une solution est d’utiliser la version Lite de TensorFlow. Veuillez suivre la procédure décrite sur le site QEngineering pour installer TensorFlow Lite sur Raspberry Pi OS 64bits
N.B.: n’oubliez pas de redémarrer le Raspberry Pi après l’installation de tensorflow
Code pour la détection d’objet
Vous pouvez récupérer le code et projet sur le Github de QEngineering
On construit le modèle à partir du fichier tflite. Dans ce projet, nous utilisons MobileNet V1
std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile("detect.tflite");
Puis on ouvre un flux vidéo (où un fichier vidéo ou une image)
VideoCapture cap(0);
On exécute ensuite le modèle sur chaque image
interpreter->Invoke();
Enfin nous traçons les résultats de la détections sur l’image et l’affichons
#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; }
Les résultats de ce code montre une détection d’objet à une vitesse entre 10 et 20 FPS mais avec une faible précision sur cet exemple.
Une étude avec d’autres exemples et modèles doit être mené mais ce projet montre l’intérêt de C++ et Tensorflow Lite pour la détection d’objet sur Raspberry Pi