Etiquetas: , , ,

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

raspberry-pi-imager-raspbian-os-64-bits Deteção de objectos no Raspberry Pi e TensorFlow Lite

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.

raspberry-pi-cpp-mobilenet-v1-boats Deteção de objectos no Raspberry Pi e TensorFlow Lite

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

Fontes