Magazine Salute e Benessere

Emgu CV su Windows 7 – Facial features detection

Creato il 17 gennaio 2014 da Matteo Tosato @MatteoTosato87

EmguCV nasce con lo scopo di permettere al linguaggio .NET di chiamare le API della celebre libreria OpenCV dedicata alla visione artificiale. EmguCV è una libreria wrapper “cross-platform” in grado di girare su Windows, Linux, MAC OS X, iPad, iPhone e Andorid.

Uno degli utilizzi base e più interessanti della libreria, è la “face detection“, rilevazione dei visi all’interno di video o immagini. In questo articolo vedremo come arrivare a scrivere un semplice esempio funzionante in tempi brevi.

Avendo già introdotto a grandi linee la teoria basata sui filtri Haar (Viola-Jones [1]), in un precedente articolo su OpenCV, passiamo direttamente alla fase di impostazione dell’ambiente e allo sviluppo di un primo esempio (Con Visual Studio 2013 su Windows 7 64 Bit).

Il setup di EmguCV è reperibile sul sito http://sourceforge.net/projects/emgucv/files/latest/download/.
Nel mio caso, l’ultima versione disponibile risulta essere la 2.9.0.1922-beta.

Scaricandola ed eseguendone il setup, tutti i file verranno copiati in una cartella “EmguCV” sotto il percorso di installazione che abbiamo scelto.
La cartella “bin” contiene le dll che dovremo aggiungere al nostro progetto. Inoltre all’interno della sottocartella “x86″ e “x64″ sono presenti le librerie originali di OpenCV. A seconda della versione dobbiamo aggiungere alla PATH di sistema il percorso completo che punta rispettivamente alle versioni x86 o a 64 bit, a seconda della versione che intendiamo utilizzare.

Nel mio caso ho aggiunto:

C:\Users\matteo>PATH
( ......... )E:\Emgu\emgucv-windows-universal-cuda 2.9.0.1922\bin\x86

Siamo pronti ad aprire visual Studio, scegliamo un nuovo progetto “Console”.
Assicuriamoci di aggiungere gli assembly di openCV, nei riferimenti dovremo vedere:

riferimenti

Di seguito il listato del programma di esempio,
Per prima cosa creiamo una nuova istanza della ‘Capture’ di Emgucv.
Chiamata in questo modo, senza parametri, viene utilizzata la prima webcam disponibile sul computer, nel caso vi siano più cam installate, un parametro ‘index’ può essere passato. Dalla documentazione:

public Capture(
	int camIndex
)

camIndex
Type: System..::..Int32
The index of the camera to create capture from, starting from 0

Carico i filtri ‘HaarCascade’ pescandoli dalla directory originale di opencv. Se non sono presenti nelle cartelle di emgucv potete trovarli scaricando la versione C++ di opecv (come ho fatto).

            // ...
			// Create a Camera capture instance
            Capture cap = new Capture();
            // ...

            // Load cascade classifier configurations
            CascadeClassifier haar_face = new CascadeClassifier(
                "E:\\opencv\\data\\haarcascades\\haarcascade_frontalface_alt_tree.xml");
            CascadeClassifier haar_nose = new CascadeClassifier(
                "E:\\opencv\\data\\haarcascades\\haarcascade_mcs_nose.xml");
            CascadeClassifier haar_mouth = new CascadeClassifier(
                "E:\\opencv\\data\\haarcascades\\haarcascade_mcs_mouth.xml");
            CascadeClassifier haar_left_eye = new CascadeClassifier(
                "E:\\opencv\\data\\haarcascades\\haarcascade_lefteye_2splits.xml");
            CascadeClassifier haar_right_eye = new CascadeClassifier(
                "E:\\opencv\\data\\haarcascades\\haarcascade_righteye_2splits.xml");

La creazione degli oggetti necessari, frames, finestre di visualizzazione e oggetti utili viene fatta subito dopo:

			// ...
            // Create a Image viewers to show the frames
            ImageViewer Original_view = new ImageViewer();
            Original_view.Text = "Original";
            ImageViewer feature_search_view = new ImageViewer();
            feature_search_view.Text = "Features research areas";
            ImageViewer features_view = new ImageViewer();
            features_view.Text = "Features";
            // Create the frames
            Image<Bgr, byte> features_frame;
            Image<Bgr, byte> orig_frame;
            Image<Gray, byte> grayframe;
            // Show windows
            Original_view.Show();
            feature_search_view.Show();
            features_view.Show();
            form.Show();

            Rectangle[] faces;
            Rectangle[] detection;
            Rectangle face = new Rectangle(-1, -1, -1, -1);
            Rectangle nose = new Rectangle(-1, -1, -1, -1);
            Rectangle left_eye = new Rectangle(-1, -1, -1, -1);
            Rectangle right_eye = new Rectangle(-1, -1, -1, -1);
            Rectangle mouth = new Rectangle(-1, -1, -1, -1);
            Rectangle eyes_roi = new Rectangle(-1, -1, -1, -1);
            Rectangle mouth_roi = new Rectangle(-1, -1, -1, -1);
            Rectangle nose_roi = new Rectangle(-1, -1, -1, -1);

            Bgr red = new Bgr(Color.Red);
            Bgr blue = new Bgr(Color.Blue);
            Bgr white = new Bgr(Color.White);
            Bgr black = new Bgr(Color.Black);
            Bgr green = new Bgr(Color.Green);

A questo punto possiamo iniziare il loop principale del programma (Nel caso di un progetto Windows Form, il loop deve essere messo in un thread separato per evitare il congelamento dell’interfaccia utente).
la chiamata API: ‘DetectMultiScale‘, rileva i visi presenti nel frame passato come argomento e ritorna uno o più oggetti ‘Rectangle’, per noi coordinate, ove sono stati rilevati visi.

public Rectangle[] DetectMultiScale(
	Image<Bgr, byte> image,
	double hitThreshold,
	Size winStride,
	Size padding,
	double scale,
	int finalThreshold,
	bool useMeanshiftGrouping
)

Parameters

image
Type: Emgu.CV..::..Image<(Of <(<'Bgr, Byte>)>)>
The image to search in

hitThreshold
Type: System..::..Double
Threshold for the distance between features and SVM classifying plane. Usually it is 0 and should
be specfied in the detector coefficients (as the last free coefficient). But if the free coefficient
is omitted (which is allowed), you can specify it manually here.

winStride
Type: System.Drawing..::..Size
Window stride. Must be a multiple of block stride.

padding
Type: System.Drawing..::..Size

scale
Type: System..::..Double
Coefficient of the detection window increase.

finalThreshold
Type: System..::..Int32
After detection some objects could be covered by many rectangles. This coefficient regulates
similarity threshold. 0 means don't perform grouping. Should be an integer if not using
meanshift grouping. Use 2.0 for default

useMeanshiftGrouping
Type: System..::..Boolean
If true, it will use meanshift grouping.

Return Value
The regions where positives are found

Allo stesso tempo, mi preoccupo di disegnare sul frame quest’area rilevata e ripeto l’operazione anche per l’occhio sinistro, destro, il naso e la bocca.
Sfruttando la proprietà ‘ROI’ dell’oggetto ‘Image<>’, possiamo risparmiare tempo processore e aumentare la precisione del sistema. ‘ROI’, è un oggetto ‘Rectangle’ relativo al frame, il filtro ‘HaarCascade’ cercherà solo all’interno di quest’area. Al termine mi devo anche ricordare di resettare questo parametro tramite ‘Rectangle.Empty’.

            // For each frame captured from camera, detects face and his features.
            for (; ; )
            {
                orig_frame = cap.QueryFrame();
                grayframe = orig_frame.Convert<Gray, byte>();
                features_frame = orig_frame.Convert<Bgr, byte>();
                grayframe._EqualizeHist();

                LineSegment2D J1 = new LineSegment2D();
                LineSegment2D J2 = new LineSegment2D();
                LineSegment2D J3 = new LineSegment2D();
                LineSegment2D J4 = new LineSegment2D();
                LineSegment2D J5 = new LineSegment2D();
                LineSegment2D J6 = new LineSegment2D();
                LineSegment2D J7 = new LineSegment2D();
                LineSegment2D J8 = new LineSegment2D();

                if (orig_frame != null)
                {
                    faces = haar_face.DetectMultiScale(
                        grayframe,
                        1.1,
                        1,
                        new Size(120, 120),
                        new Size(grayframe.Width - 10, grayframe.Height - 10)
                        );

                    if (faces.Count() > 0)
                    {
                        // Features detection
                        face = nose_roi = grayframe.ROI = faces[0];

                        detection = haar_nose.DetectMultiScale(
                            grayframe,
                            1.1,
                            1,
                            new Size(15, 15),
                            new Size(grayframe.Width / 2, grayframe.Height / 2)
                            );

                        nose = (detection.Count() > 0) ? detection[0] : new Rectangle(-1, -1, -1, -1);

                        grayframe.ROI = Rectangle.Empty;
                        eyes_roi = grayframe.ROI = new Rectangle(face.X, faces[0].Y + face.Height / 5, face.Width, face.Height / 3);

                        detection = haar_left_eye.DetectMultiScale(
                            grayframe,
                            1.1,
                            1,
                            new Size(10, 10),
                            new Size(face.Width / 3, face.Height / 3)
                            );

                        left_eye = (detection.Count() > 0) ? detection[0] : new Rectangle(-1, -1, -1, -1);

                        detection = haar_right_eye.DetectMultiScale(
                            grayframe,
                            1.1,
                            1,
                            new Size(10, 10),
                            new Size(face.Width / 3, face.Height / 3)
                            );

                        right_eye = (detection.Count() > 0) ? detection[0] : new Rectangle(-1, -1, -1, -1);

                        grayframe.ROI = Rectangle.Empty;
                        mouth_roi = grayframe.ROI = new Rectangle(face.X, face.Y + face.Height * 2 / 3, face.Width, face.Height / 3);

                        detection = haar_mouth.DetectMultiScale(
                            grayframe,
                            1.1,
                            1,
                            new Size(5, 5),
                            new Size(face.Width / 2, face.Height / 2)
                            );

                        mouth = (detection.Count() > 0) ? detection[0] : new Rectangle(-1, -1, -1, -1);

                        grayframe.ROI = Rectangle.Empty;

                        // Draw ROI areas
                        grayframe.Draw(face, new Gray(), 2);
                        grayframe.Draw(new Rectangle(eyes_roi.X, eyes_roi.Y, eyes_roi.Width, eyes_roi.Height), new Gray(), 2);
                        grayframe.Draw(new Rectangle(nose_roi.X, nose_roi.Y, nose_roi.Width, nose_roi.Height), new Gray(), 2);
                        grayframe.Draw(new Rectangle(mouth_roi.X, mouth_roi.Y, mouth_roi.Width, mouth_roi.Height), new Gray(), 2);

                        // If the features have not been found then, continue with the next frame
                        if ((face.X != -1) & (left_eye.X != -1) & (right_eye.X != -1) & (mouth.X != -1) & (nose.X != -1))
                        {

                            // Draw rectangles
                            features_frame.Draw(face, red, 2);
                            features_frame.Draw(
                                new Rectangle(eyes_roi.X + left_eye.X, eyes_roi.Y + left_eye.Y, left_eye.Width, left_eye.Height),
                                blue, 1);
                            features_frame.Draw(
                                new Rectangle(eyes_roi.X + right_eye.X, eyes_roi.Y + right_eye.Y, right_eye.Width, right_eye.Height),
                                blue, 1);
                            features_frame.Draw(
                                new Rectangle(face.X + nose.X, face.Y + nose.Y, nose.Width, nose.Height),
                                white, 1);
                            features_frame.Draw(
                                new Rectangle(mouth_roi.X + mouth.X, mouth_roi.Y + mouth.Y, mouth.Width, mouth.Height),
                                green, 1);

                            grayframe.Draw(
                                new Rectangle(eyes_roi.X + left_eye.X, eyes_roi.Y + left_eye.Y, left_eye.Width, left_eye.Height),
                                new Gray(), 1);
                            grayframe.Draw(
                                new Rectangle(eyes_roi.X + right_eye.X, eyes_roi.Y + right_eye.Y, right_eye.Width, right_eye.Height),
                                new Gray(), 1);
                            grayframe.Draw(
                                new Rectangle(face.X + nose.X, face.Y + nose.Y, nose.Width, nose.Height),
                                new Gray(), 1);
                            grayframe.Draw(
                                new Rectangle(mouth_roi.X + mouth.X, mouth_roi.Y + mouth.Y, mouth.Width, mouth.Height),
                                new Gray(), 1);

                            // Draw center points
                            Point left_eye_center = new Point(eyes_roi.X + left_eye.X + left_eye.Width / 2, eyes_roi.Y + left_eye.Y + left_eye.Height / 2);
                            Point right_eye_center = new Point(eyes_roi.X + right_eye.X + right_eye.Width / 2, eyes_roi.Y + right_eye.Y + right_eye.Height / 2);
                            Point nose_center = new Point(nose_roi.X + nose.X + nose.Width / 2, nose_roi.Y + nose.Y + nose.Height / 2);
                            Point nose_left_center = new Point(nose_roi.X + nose.X, nose_center.Y);
                            Point nose_right_center = new Point(nose_roi.X + nose.X + nose.Width, nose_center.Y);
                            Point mouth_center = new Point(mouth_roi.X + mouth.X + mouth.Width / 2, mouth_roi.Y + mouth.Y + mouth.Height / 2);

                            features_frame.Draw(new CircleF(left_eye_center, 3.0f), blue, 1);
                            features_frame.Draw(new CircleF(right_eye_center, 3.0f), blue, 1);
                            features_frame.Draw(new CircleF(nose_center, 3.0f), white, 1);
                            features_frame.Draw(new CircleF(mouth_center, 3.0f), green, 1);

                            Point middle_eyes = new Point(
                                left_eye_center.X + ((right_eye_center.X - left_eye_center.X) / 2),
                                left_eye_center.Y + ((right_eye_center.Y - left_eye_center.Y) / 2)
                                );

                            // 8 Characteristic parameters
                            J1 = new LineSegment2D(left_eye_center, right_eye_center);
                            J2 = new LineSegment2D(left_eye_center, mouth_center);
                            J3 = new LineSegment2D(right_eye_center, mouth_center);
                            J4 = new LineSegment2D(left_eye_center, nose_center);
                            J5 = new LineSegment2D(right_eye_center, nose_center);
                            J6 = new LineSegment2D(mouth_center, nose_center);
                            J7 = new LineSegment2D(middle_eyes, nose_center);
                            J8 = new LineSegment2D(nose_left_center, nose_right_center);

                            features_frame.Draw(J1, black, 1);  // Distance between middles of the eyes
                            features_frame.Draw(J2, black, 1);  // Distance between middle of the left eyes and middle point of mouth
                            features_frame.Draw(J3, black, 1);  // Distance between middle of the right eyes and middle point of mouth
                            features_frame.Draw(J4, black, 1);  // Distance between middle of the left eyes and middle point of nose
                            features_frame.Draw(J5, black, 1);  // Distance between middle of the rigth eyes and middle point of nose
                            features_frame.Draw(J6, black, 1);  // Distance between middle point of mouth and middle point of nose
                            features_frame.Draw(J7, black, 1);  // Distance of middle point of middle eyes and middle of nose
                            features_frame.Draw(J8, black, 1);  // Width of nose
                        }
                    }

                    Original_view.Image = orig_frame;
                    feature_search_view.Image = grayframe;
                    features_view.Image = features_frame;
                }
                else
                    break;

                Original_view.Refresh();
                feature_search_view.Refresh();
                features_view.Refresh();

                Console.Clear();
                Console.WriteLine("Face features:");
                Console.WriteLine("---------------------------------------");
                Console.WriteLine("J1 Distance between middles of the eyes: {0:N}", J1.Length);
                Console.WriteLine("J2 Distance between middle of the left eyes and middle point of mouth: {0:N}", J2.Length);
                Console.WriteLine("J3 Distance between middle of the right eyes and middle point of mouth: {0:N}", J3.Length);
                Console.WriteLine("J4 Distance between middle of the left eyes and middle point of nose: {0:N}", J4.Length);
                Console.WriteLine("J5 Distance between middle of the right eyes and middle point of nose: {0:N}", J5.Length);
                Console.WriteLine("J6 Distance between middle point of mouth and middle point of nose: {0:N}", J6.Length);
                Console.WriteLine("J7 Distance of middle point of middle eyes and middle of nose: {0:N}", J7.Length);
                Console.WriteLine("J8 Width of nose: {0:N}", J8.Length);
            }

Nell’esempio ho creato oggetti ‘LineSegment2D’ che rappresentano le distanza fra occhi, naso e bocca.
Questi elementi sono caratteristici per persona e possono costituire la base di una serie di parametri per la face recognition.

Pur rimanendo un esempio base, la capacità di rilevare le caratteristiche principali di un viso tramite una webcam anche su piattaforme mobile si presenta come una possibilità molto interessante utile allo sviluppo di tecnologie di interoperabilità uomo-device.

Esempio di funzionamento

Esempio di funzionamento


Archiviato in:C#, Computer vision, Informatica, Programmazione Tagged: EmguCV, face detection, OpenCV

Potrebbero interessarti anche :

Ritornare alla prima pagina di Logo Paperblog

Possono interessarti anche questi articoli :

Dossier Paperblog