fbpixel
Etiquetas: , , ,

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).

raspberry-pi-imager-raspbian-os-64-bits Detección de objetos en Raspberry Pi y TensorFlow Lite

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.

raspberry-pi-cpp-mobilenet-v1-boats Detección de objetos en Raspberry Pi y TensorFlow Lite

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.

Fuentes