OpenCV creare una libreria per LabVIEW [1/2]

Aos
Se ti trovi a leggere questo post, sei senza dubbio interessato, a usare le funzioni di OpenCV insieme alle grandi potenzialità fornite da LabVIEW. Per i curiosi, e gli utenti meno esperti, OpenCV è una libreria di visione artificiale, ben fornita, ed efficiente a livello computazionale in quanto sviluppata in C, LabVIEW d’altra parte attraverso il suo linguaggio G, semplice ed intuitivo, permette di realizzare applicazioni in poco tempo.

L’idea è quella di sviluppare applicazioni in ambito di visione artificiale, senza entrare nei dettagli implementativi degli algoritmi di visione, e quindi sfruttando le funzioni messe a disposizione da OpenCV.

In questo post, vedremo come creare una libreria per OpenCV e testarla con Visual Studio utilizzando un main program che richiami la libreria stessa.

Il passo successivo consiste nell’importare la libreria in LabVIEW e creare un semplice VI che partendo da un’immagine in ingresso, individua i cerchi al suo interno e mostra il risultato sul front panel.

Per lo sviluppo della libreria, occorre installare i seguenti programmi (queste sono le versioni da me usate):

Visual Studio 2013 Professional
LabVIEW 2013 SP1
OpenCV 2.4.9

Adesso che abbiamo tutti gli strumenti, creiamo una dll, che trova i cerchi presenti in un’immagine per mezzo dell’algoritmo dell’hough circle, le cui funzioni sono messe a disposizione da OpenCV.

– CREAZIONE DELLA DLL-

creazione del progetto in VisualStudio (VS)
-File->New->Project
-nella tendina a destra, selezionare template->Visual C++ -> Win32
-selezionare nel pannello centrale Win32 project
-dare un nome al progetto (hough_aos), e click su Ok
-click su Next
-nel campo application type, selezionare DLL, e in additional options, mettere la spunta su empty project
-click su finish

1

2

configurazione del progetto
Per prima cosa occorre configurare Visual Studio con OpenCV, ecco un tutorial

 

-CREAZIONE DEL FILE .H-

Per creare l’header file, all’interno della finestra solution explorer, click col tasto destro sulla cartella Header files e click su Add->New Item, inserire il nome del file .h e fare click su Add.

3

Nell’editor, inseriamo le inclusioni degli header di OpenCV, (in questo caso highgui e imgproc). Prima di salvare e passare alla definizione del file .cpp, occorre definire i prototipi delle funzioni messe a disposizione dalla dll, i quali sono riconosciuti come funzioni di libreria per mezzo del costrutto _declspec (dllexport), da anteporre al nome della funzione.

Ultimo accorgimento è quello di racchiudere i prototipi all’interno di “extern C”{}. In questo modo il compilatore “non orla”i prototipi, i quali vengono trattati come funzioni in C, e quindi utilizzabili in ambiente LabVIEW.

[cpp] #include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace std;
using namespace cv;

class HoughDLL {
public:

vector<Vec3f> circles;

double minDistance, param1, param2;
int minRadius, maxRadius;

public:

int Hough(Mat &image) {

if (!image.data) {
return -1;
}

GaussianBlur(image, image, Size(9, 9), 2, 2);

HoughCircles(image, circles, CV_HOUGH_GRADIENT, 1, minDistance, param1, param2, minRadius, maxRadius);

return 0;
}
};

extern "C"{
_declspec (dllexport) int calcola_cerchi(uchar *imdata, int cols, int rows, double minDistance_, double param1_, double param2_, int minRadius_, int maxRadius_, int *result_size);
_declspec (dllexport) int ottieni_cerchi(int *x_out, int *y_out, int *r_out);
}
[/cpp]

Il file .h si divide essenzialmente in tre parti:

a) definzione delle librerie da includere, e definizione dei namespaces

[cpp] #include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace std;
using namespace cv;
[/cpp]

b) definzione della classe HoughDLL, all’interno della quale inseriamo come attributi, quelli necessari alla funzione del calcolo dei cerchi di opencv. Dichiarazione e definizione della funzione “Hough”, la quale al suo interno usa i metodi messi a disposizione da opencv, in particolare, ad essa viene passata l’immagine da elaborare, alla quale viene applicata un GaussianBlur (al fine di migliorare la ricerca dei cerchi). L’immagine con blur viene infine passata all’HoughCircles di opencv, la quale riempie l’attributo circle della classe stessa.

[cpp] class HoughDLL {
public:

vector<Vec3f> circles;

double minDistance, param1, param2;
int minRadius, maxRadius;

public:

int Hough(Mat &image) {

if (!image.data) {
return -1;
}

GaussianBlur(image, image, Size(9, 9), 2, 2);

HoughCircles(image, circles, CV_HOUGH_GRADIENT, 1, minDistance, param1, param2, minRadius, maxRadius);

return 0;
}
};
[/cpp]

c) dichiarazione dei prototipi di funzione della libreria all’interno del costrutto extern “C”. In questo caso le funzioni define sono due:

– calcola_cerchi, individua i cerchi all’interno dell’immagine e li salva nella variabile “circles” la quale è un vector, ovvero un vettore di oggetti appartenenti alla classe Vec3f
– ottieni_cerchi, dal vettore circles vengono estratte le coordinate x,y e il raggio i quali vengono salvati in 3 vettori differenti di interi.

[cpp] extern "C"{
_declspec (dllexport) int calcola_cerchi(uchar *imdata, int cols, int rows, double minDistance_, double param1_, double param2_, int minRadius_, int maxRadius_, int *result_size);
_declspec (dllexport) int ottieni_cerchi(int *x_out, int *y_out, int *r_out);
}
[/cpp]

-CREAZIONE DEL FILE .CPP-

La creazione del file sorgente è uguale al caso precedente, tasto destro sulla cartella Source Files e click su Add->New Item. All’interno dell’editor di testo copiare il seguente codice.

[cpp] #include "hough_aos.h"
#include <iostream>

HoughDLL detect;

_declspec (dllexport) int calcola_cerchi(
uchar *imdata,
int cols,
int rows,
double minDistance_,
double param1_,
double param2_,
int minRadius_,
int maxRadius_,
int *result_size)
{

Mat inputImage(rows, cols, CV_8U, &imdata[0]);

detect.minDistance = minDistance_;
detect.param1 = param1_;
detect.param2 = param2_;
detect.minRadius = minRadius_;
detect.maxRadius = maxRadius_;

int error = detect.Hough(inputImage);
if (error != 0) {
return -1;
}

(*result_size) = detect.circles.size();
return 0;
}

_declspec (dllexport) int ottieni_cerchi(int *x_out, int *y_out, int *r_out)
{
if (detect.circles.size() == 0)
return -2;

for (int i = 0; i<detect.circles.size(); i++)
{
*(x_out + i) = cvRound(detect.circles[i][0]);
*(y_out + i) = cvRound(detect.circles[i][1]);
*(r_out + i) = cvRound(detect.circles[i][2]);
}
return 0;
}
[/cpp]

Spiegazione

Per prima cosa, richiamiamo il hough_aos.h in modo da includere nel file sorgente le librerie di OpenCv e creiamo l’oggetto detect della classe HougDLL.

[cpp] #include "hough_aos.h"
#include <iostream>

HoughDLL detect;
[/cpp]

FUNZIONE calcola_cerchi

A questo punto entriamo nella definizione della funzione calcola_cerchi, il cui prototipo è il seguente:

[cpp] _declspec (dllexport) int calcola_cerchi(
uchar *imdata,
int cols,
int rows,
double minDistance_,
double param1_,
double param2_,
int minRadius_,
int maxRadius_,
int *result_size)
{

Mat inputImage(rows, cols, CV_8U, &imdata[0]);

detect.minDistance = minDistance_;
detect.param1 = param1_;
detect.param2 = param2_;
detect.minRadius = minRadius_;
detect.maxRadius = maxRadius_;

int error = detect.Hough(inputImage);
if (error != 0) {
return -1;
}

(*result_size) = detect.circles.size();
return 0;
}
[/cpp]

il tipo di ritorno della funzione è un int, verrà utilizzato come codice di errore nel caso in cui si presenti un problema nell’esecuzione della routine. I parametri della funzione possono essere divisi in, parametri di input e di output:

parametri di input:

– *imdata, corrisponde alla matrice in cui è contenuta l’immagine da processare;
– cols, rows, corrispondono a larghezza e altezza dell’immagine da processare;
– minDistance, param1, param2, minRadius, maxRadius, sono parametri propri della funzione HoughCircles di OpenCV, i quali definiscono i tipi di cerchi da cercare all’interno dell’immagine;

parametri di output:

-int *result_size, corrisponde al numero di cerchi individuati. Da notare come il result_size è un puntatore a int. Dato che vogliamo che il risultato sia disponibile al di fuori della funzione, andiamo a scrivere all’interno della memoria puntata dalla variabile che passiamo alla funzione;

creazione dell’immagine Mat (classe matrice di OpenCV), al suo interno inseriamo i parametri, di altezza (rows), larghezza(cols), data (&imdata[0]) e tipo di immagine, in questo caso in scala di grigi (CV_8U)

[cpp] Mat inputImage(rows, cols, CV_8U, &imdata[0]);
[/cpp]

definizione degli attributi di classe, i quali verrano poi utilizzati dal metodo “Hough” della classe stessa.

[cpp] detect.minDistance = minDistance_;
detect.param1 = param1_;
detect.param2 = param2_;
detect.minRadius = minRadius_;
detect.maxRadius = maxRadius_;
[/cpp]

detection dei cerchi passando al metodo “Hough” l’immagine Mat creata precedentemente

[cpp] int error = detect.Hough(inputImage);
if (error != 0) {
return -1;
}
[/cpp]

salvataggio del numero di cerchi trovati, se l’esecuzione della funzione va a buon fine, il valore di ritorno è 0.

[cpp] (*result_size) = detect.circles.size();
return 0;
[/cpp]

FUNZIONE ottieni_cerchi

prototipo della funzione:

[cpp] _declspec (dllexport) int ottieni_cerchi(int *x_out, int *y_out, int *r_out)
[/cpp]

come nel caso di “calcola_cerchi”, il parametro di ritorno è un intero, il quale codifica la buona riuscita o meno dell’esecuzione stessa.

I parametri in questo caso sono 3, tutti di “uscita”:

-*x_out *y_out *r_out, questi tre puntatori, corrispondo al dato in uscita, nella memoria da essi puntata verranno salvati il centro (x_out,y_out) e il raggio (r_out) di tutti i cerchi individuati, i quali saranno successivamente visualizzati all’interno dell’immagine originale.

controllo sui cerchi individuati, nel caso la funzione precedente non avesse trovato nessun cerchio, oppure l’attributo circles fosse vuoto, la funzione ritorna -2, il quale può essere interpretato dalla funzione chiamante come un codice di errore.

[cpp] if (detect.circles.size() == 0)
return -2;
[/cpp]

salvataggio dei parametri dei cerchi individuati nelle variabili x_out, y_out, r_out. In caso di esecuzione senza errori, il valore di ritorno è 0

[cpp] for (int i = 0; i<detect.circles.size(); i++)
{
*(x_out + i) = cvRound(detect.circles[i][0]);
*(y_out + i) = cvRound(detect.circles[i][1]);
*(r_out + i) = cvRound(detect.circles[i][2]);
}
return 0;
[/cpp]

COMPILAZIONE

sul solution explorer, click destro sul nome del progetto, a questo punto selezionare Build. Se non ci sono errori, il compilatore effettua il build della libreria con successo.

4

– CREAZIONE DI UNA MAIN FUNCTION-

L’obiettivo della main function è quello di testare la libreria appena creata, ovvero al suo interno verranno richiamati “calcola_cerchi” e “ottieni_cerchi”

-Aggiunta della main function all’attuale progetto.  Sulla barra del menu di visual studio, file->new project, selezionare win32 project e nel menu a tendina solution (in basso), selezionare “add to solution”, in questo modo manteniamo il progetto relativo alla libreria, aperto.

5

-click su ok. Nella finestra che appare click su next, spuntare “console application” e “empty project” e infine click su finish.

7

-a questo punto nel solution explorer abbiamo due progetti

8

-configurare il progetto MAIN, i passi sono uguali alla sezione “configurazione del progetto”
– sul solution explorer click destro su MAIN, e selezionare “Set as StartUp project”
– di nuovo sul solution explorer click destro su MAIN, selezionare Properties, sul menu che si apre, nella colonna a sinistra, espandere la voce “common properties”, click sulla voce References. Click sul tasto Add New Reference… e successivamente spunta su hough_aos, infine click su ok.

7

10

-ultimo passo della configurazione è inserire nelle cartella da include anche quella in cui è definito il file.h della libreria hough_aos. Sul progetto MAIN->properties->C/c -> General-> additional include directories. Aggiungere una nuova istanza in cui inserire la path del file hough_aos.h

-aggiungiamo un item main.cpp nella cartella source file del progetto MAIN e incolliamo il seguente codice

[cpp] #include "hough_aos.h"
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
int numero_cerchi = 0;
int x, y, r;

const char* filename = "C:\\Users\\Francesco\\Desktop\\cc.png";
Mat img = imread(filename, 0);

if (!img.data) // Check for invalid input
{
cout << "impossibile aprire immagine" << endl;

return -1;
}

imshow("Immagine sorgente", img);
waitKey();

int a = calcola_cerchi(img.data, img.cols, img.rows, 20, 150, 40, 0, 0, &numero_cerchi);
if (a != 0) {
return -1;
}

int *xM = new int[numero_cerchi];
int *yM = new int[numero_cerchi];
int *rM = new int[numero_cerchi];

int b = ottieni_cerchi(xM, yM, rM);

if (b != 0) {
return -2;
}

cvtColor(img, img, CV_GRAY2BGR);

for (int i = 0; i < numero_cerchi; i++)
{

x = *(xM + i);
y = *(yM + i);
r = *(rM + i);

circle(img, Point(x, y), r, Scalar(0, 0, 255), 3, CV_AA); // cerchio
circle(img, Point(x, y), 2, Scalar(0, 255, 0), 3, CV_AA); // centro del cerchio
}

imshow("Cerchi individuati", img);
waitKey();

return 0;
}
[/cpp]

SPIEGAZIONE

La main function si divide essenzialmente in 5 parti

a) acquisizione dell’immagine da path, creazione dell’oggetto della classe Mat, controllo della presenza del campo data del Mat appena creato. Infine visualizzazione dell’immagine.

[cpp] const char* filename = "C:\\Users\\Francesco\\Desktop\\cc.png";
Mat img = imread(filename, 0);

if (!img.data) // Check for invalid input
{
cout << "impossibile aprire immagine" << endl;

return -1;
}

imshow("Immagine sorgente", img);
waitKey();
[/cpp]

b) estrazione delle informazioni dal Mat (in labview verrà passato direttamente la matrice relativa all’immagine), ovvero, rows, cols e data. Infine esecuzione della funzione di libreria calcola_cerchi.

[cpp] int a = calcola_cerchi(img.data, img.cols, img.rows, 20, 150, 40, 0, 0, &numero_cerchi);
if (a != 0) {
return -1;
}
[/cpp]

c) dichiarazione e inizializzazione dei tre puntatori a vettore, x_out, y_out e r_out di dimensione pari al numero di cerchi trovati da calcola_cerchi.

[cpp] int *xM = new int[numero_cerchi];
int *yM = new int[numero_cerchi];
int *rM = new int[numero_cerchi];
[/cpp]

d) passaggio dei tre puntatori

[cpp] int b = ottieni_cerchi(xM, yM, rM);
if (b != 0) {
return -2;
}
[/cpp]

e) conversione dell’immagine da scala di grigi in BGR (per visualizzare il colore dei cerchi) e ciclo for per la scrittura dei cerchi sull’immagine. La funzione di OpenCV che disegna cerchi è “Circle”, nel main viene richiamata due volte,una per disegnare il centro, l’altra per disegnare il cerchio stesso. Una volta disegnati i cerchi nell’immagine, passiamo a visualizzarla.

[cpp] cvtColor(img, img, CV_GRAY2BGR);

for (int i = 0; i < numero_cerchi; i++)
{

x = *(xM + i);
y = *(yM + i);
r = *(rM + i);

circle(img, Point(x, y), r, Scalar(0, 0, 255), 3, CV_AA); // cerchio
circle(img, Point(x, y), 2, Scalar(0, 255, 0), 3, CV_AA); // centro del cerchio
}
[/cpp]

COMPILAZIONE

compilare entrambi i progetti. Infine premi F5 per eseguire.

6

Nel prossimo post, l’algoritmo eseguito dalla main function verrà codificato in LabVIEW.

Francesco Celiberti
Ciao a tutti,

mi chiamo Francesco, sono laureato in Ing. Informatica e dell'Automazione. Sono attualmente coinvolto in un progetto di ricerca Europeo, MOTORIST. www.motorist-ptw.eu
Tags: , ,
By Francesco Celiberti | ottobre 6th, 2014 | SHOW COMMENT(1)

One Response

  1. OpenCV creare una libreria per LabVIEW [2/2] | Automazione Open Source says

    […] abbiamo visto nel post precedente, la creazione di una libreria di OpenCV, è una procedura semplice e immediata basata su 3 […]