Para mejorar el rendimiento en la Raspberry Pi, puede utilizar el lenguaje C++ y bibliotecas optimizadas para acelerar el cálculo de modelos de detección de objetos. Esto es lo que ofrece TensorFlow Lite.
Un buen punto de partida es el sitio web QEngineering.
Hardware
- Raspberry Pi 4
- Pantalla+ratón+teclado
- Tarjeta SD con OS Raspbian 64bits
Configuración
Para un mejor rendimiento, necesitará instalar la versión de 64 bits de Raspberry Pi OS. Esta versión está disponible en el software Raspberry Pi Imager en el menú Raspberry Pi OS (otros).
Instalación de Code::Blocks
IDE de bloques de código es un software como Thonny o Geany que permite compilar y ejecutar código escrito en C.
sudo apt-get install codeblocks
Instalación de OpenCV para Cpp
Para instalar la versión Cpp de OpenCV utilizamos la herramienta apt-get
sudo apt-get install libopencv-dev
Instalación de TensorFlow Lite en Raspberry Pi OS 64bits
Para obtener el mejor rendimiento en Raspberry Pi, una solución es utilizar la versión Lite de TensorFlow. Siga el procedimiento descrito en el sitio web de QEngineering para instalar TensorFlow Lite en Raspberry Pi OS 64bits.
Nota: no olvides reiniciar la Raspberry Pi después de instalar tensorflow.
Código de detección de objetos
Puede descargar el código y el proyecto en el Github de QEngineering
El modelo se construye a partir del archivo tflite. En este proyecto, utilizamos MobileNet V1
std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile("detect.tflite");
A continuación, abra un flujo de vídeo (o un archivo de vídeo o imagen)
VideoCapture cap(0);
A continuación, el modelo se ejecuta en cada imagen
interpreter->Invoke();
Por último, trazamos los resultados de la detección en la imagen y los mostramos.
#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; }
Los resultados de este código muestran la detección de objetos a una velocidad de entre 10 y 20 FPS, pero con una precisión baja en este ejemplo.
Es necesario realizar un estudio con otros ejemplos y modelos, pero este proyecto muestra el interés de C++ y Tensorflow Lite para la detección de objetos en Raspberry Pi.