Introduzione a OpenCV con Java

1. Introduzione

In questo tutorial impareremo come installare e utilizzare la libreria di visione artificiale OpenCV e applicarla al rilevamento dei volti in tempo reale.

2. Installazione

Per utilizzare la libreria OpenCV nel nostro progetto, dobbiamo aggiungere la dipendenza opencv Maven al nostro pom.xml :

 org.openpnp opencv 3.4.2-0 

Per gli utenti Gradle, dovremo aggiungere la dipendenza al nostro file build.gradle :

compile group: 'org.openpnp', name: 'opencv', version: '3.4.2-0'

Dopo aver aggiunto la libreria alle nostre dipendenze, possiamo utilizzare le funzionalità fornite da OpenCV.

3. Utilizzo della libreria

Per iniziare a utilizzare OpenCV, dobbiamo inizializzare la libreria , cosa che possiamo fare nel nostro metodo principale :

OpenCV.loadShared();

OpenCV è una classe che contiene metodi relativi al caricamento di pacchetti nativi richiesti dalla libreria OpenCV per varie piattaforme e architetture.

Vale la pena notare che la documentazione fa le cose in modo leggermente diverso:

System.loadLibrary(Core.NATIVE_LIBRARY_NAME)

Entrambe queste chiamate al metodo caricheranno effettivamente le librerie native richieste.

La differenza qui è che quest'ultimo richiede l'installazione delle librerie native . Il primo, tuttavia, può installare le librerie in una cartella temporanea se non sono disponibili su una determinata macchina. A causa di questa differenza, il metodo loadShared è solitamente il modo migliore per andare .

Ora che abbiamo inizializzato la libreria, vediamo cosa possiamo fare con essa.

4. Caricamento di immagini

Per iniziare, carichiamo l'immagine di esempio dal disco utilizzando OpenCV :

public static Mat loadImage(String imagePath) { Imgcodecs imageCodecs = new Imgcodecs(); return imageCodecs.imread(imagePath); }

Questo metodo caricherà l'immagine data come un oggetto Mat , che è una rappresentazione a matrice.

Per salvare l'immagine caricata in precedenza, possiamo utilizzare il metodo imwrite () della classe Imgcodecs :

public static void saveImage(Mat imageMatrix, String targetPath) { Imgcodecs imgcodecs = new Imgcodecs(); imgcodecs.imwrite(targetPath, imageMatrix); }

5. Haar Cascade Classifier

Prima di immergerci nel riconoscimento facciale, comprendiamo i concetti fondamentali che lo rendono possibile.

In poche parole, un classificatore è un programma che cerca di inserire una nuova osservazione in un gruppo dipendente dall'esperienza passata. I classificatori a cascata cercano di farlo utilizzando una concatenazione di diversi classificatori. Ogni classificatore successivo utilizza l'output del precedente come informazioni aggiuntive, migliorando notevolmente la classificazione.

5.1. Caratteristiche di Haar

Il rilevamento dei volti in OpenCV viene eseguito da classificatori a cascata basati su funzionalità Haar.

Le caratteristiche di Haar sono filtri utilizzati per rilevare bordi e linee sull'immagine. I filtri sono visti come quadrati con colori bianco e nero:

Questi filtri vengono applicati più volte a un'immagine, pixel per pixel, e il risultato viene raccolto come un unico valore. Questo valore è la differenza tra la somma dei pixel sotto il quadrato nero e la somma dei pixel sotto il quadrato bianco.

6. Rilevamento dei volti

In generale, il classificatore a cascata deve essere pre-addestrato per essere in grado di rilevare qualsiasi cosa.

Poiché il processo di formazione può essere lungo e richiederebbe un grande set di dati, utilizzeremo uno dei modelli pre-addestrati offerti da OpenCV. Metteremo questo file XML nella nostra cartella delle risorse per un facile accesso.

Passiamo attraverso il processo di rilevamento di un volto:

Tenteremo di rilevare il viso delineandolo con un rettangolo rosso.

Per iniziare, dobbiamo caricare l'immagine in formato Mat dal nostro percorso sorgente:

Mat loadedImage = loadImage(sourceImagePath);

Quindi, dichiareremo un oggetto MatOfRect per memorizzare le facce che troviamo:

MatOfRect facesDetected = new MatOfRect();

Successivamente, dobbiamo inizializzare CascadeClassifier per eseguire il riconoscimento:

CascadeClassifier cascadeClassifier = new CascadeClassifier(); int minFaceSize = Math.round(loadedImage.rows() * 0.1f); cascadeClassifier.load("./src/main/resources/haarcascades/haarcascade_frontalface_alt.xml"); cascadeClassifier.detectMultiScale(loadedImage, facesDetected, 1.1, 3, Objdetect.CASCADE_SCALE_IMAGE, new Size(minFaceSize, minFaceSize), new Size() );

Sopra, il parametro 1.1 denota il fattore di scala che vogliamo utilizzare, specificando di quanto viene ridotta la dimensione dell'immagine ad ogni scala dell'immagine. Il parametro successivo, 3 , è minNeighbors. Questo è il numero di vicini che un rettangolo candidato dovrebbe avere per mantenerlo.

Infine, passeremo in rassegna i volti e salveremo il risultato:

Rect[] facesArray = facesDetected.toArray(); for(Rect face : facesArray) { Imgproc.rectangle(loadedImage, face.tl(), face.br(), new Scalar(0, 0, 255), 3); } saveImage(loadedImage, targetImagePath);

Quando inseriamo la nostra immagine sorgente, dovremmo ora ricevere l'immagine di output con tutte le facce contrassegnate da un rettangolo rosso:

7. Accesso alla telecamera utilizzando OpenCV

So far, we've seen how to perform face detection on loaded images. But most of the time, we want to do it in real-time. To be able to do that, we need to access the camera.

However, to be able to show an image from a camera, we need a few additional things, apart from the obvious — a camera. To show the images, we'll use JavaFX.

Since we'll be using an ImageView to display the pictures our camera has taken, we need a way to translate an OpenCV Mat to a JavaFX Image:

public Image mat2Img(Mat mat) { MatOfByte bytes = new MatOfByte(); Imgcodecs.imencode("img", mat, bytes); InputStream inputStream = new ByteArrayInputStream(bytes.toArray()); return new Image(inputStream); }

Here, we are converting our Mat into bytes, and then converting the bytes into an Image object.

We'll start by streaming the camera view to a JavaFX Stage.

Now, let's initialize the library using the loadShared method:

OpenCV.loadShared();

Next, we'll create the stage with a VideoCapture and an ImageView to display the Image:

VideoCapture capture = new VideoCapture(0); ImageView imageView = new ImageView(); HBox hbox = new HBox(imageView); Scene scene = new Scene(hbox); stage.setScene(scene); stage.show();

Here, 0 is the ID of the camera we want to use. We also need to create an AnimationTimerto handle setting the image:

new AnimationTimer() { @Override public void handle(long l) { imageView.setImage(getCapture()); } }.start();

Finally, our getCapture method handles converting the Mat to an Image:

public Image getCapture() { Mat mat = new Mat(); capture.read(mat); return mat2Img(mat); }

The application should now create a window and then live-stream the view from the camera to the imageView window.

8. Real-Time Face Detection

Finally, we can connect all the dots to create an application that detects a face in real-time.

The code from the previous section is responsible for grabbing the image from the camera and displaying it to the user. Now, all we have to do is to process the grabbed images before showing them on screen by using our CascadeClassifier class.

Let's simply modify our getCapture method to also perform face detection:

public Image getCaptureWithFaceDetection() { Mat mat = new Mat(); capture.read(mat); Mat haarClassifiedImg = detectFace(mat); return mat2Img(haarClassifiedImg); }

Now, if we run our application, the face should be marked with the red rectangle.

We can also see a disadvantage of the cascade classifiers. If we turn our face too much in any direction, then the red rectangle disappears. This is because we've used a specific classifier that was trained only to detect the front of the face.

9. Riepilogo

In questo tutorial, abbiamo imparato come utilizzare OpenCV in Java.

Abbiamo utilizzato un classificatore a cascata pre-addestrato per rilevare i volti nelle immagini. Con l'aiuto di JavaFX, siamo riusciti a fare in modo che i classificatori rilevassero i volti in tempo reale con le immagini di una telecamera.

Come sempre tutti gli esempi di codice possono essere trovati su GitHub.