Search code examples
c#emgucvface-recognition

EigenFaceRecognizer.Predict(Image<Gray, Byte>) -> System.NullReferenceException: 'Object reference not set to an instance of an object.'


I am making a face recognition and identification program using C# and EMGU. I got this error that I don't know why it occurs. The error occurs on (var result = recognizer.Predict(grayFacesResult);) This code should detect face, save images of detected faces and identify a person using the saved images. Please someone help me what I missed or what was the mistake here.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.Face;
using Emgu.CV.CvEnum;

using System.IO;
using System.Threading;
using System.Diagnostics;

namespace Version1
{
    public partial class Form1 : Form
    {
        #region Variables
        // for camera capture
        private Capture videoCapture = null;
        private Image<Bgr, Byte> currentFrame = null;
        Mat frame = new Mat();

        // for toggle camera capture
        private bool facesDetectionEnabled = false;

        // for saving face images
        bool EnabledSaveImage = false;

        Image<Bgr, Byte> faceResult = null;
        List<Image<Gray, Byte>> TrainedFaces = new List<Image<Gray, byte>>();
        List<int> PersonsLabels = new List<int>();

        // ???
        private static bool isTrained = false;

        EigenFaceRecognizer recognizer;

        List<string> PersonsNames = new List<string>();

        // load cascade file
        CascadeClassifier faceCascadeClassifier = new CascadeClassifier("haarcascade_frontalface_alt.xml");
        #endregion
        public Form1()
        {
            InitializeComponent();
        }

        private void btnCapture_Click(object sender, EventArgs e)
        {
            // start camera capture
            videoCapture = new Capture();
            videoCapture.ImageGrabbed += ProcessFrame;
            videoCapture.Start();
        }

        private void ProcessFrame(object sender, EventArgs e)
        {
            // video capture
            videoCapture.Retrieve(frame, 0);
            currentFrame = frame.ToImage<Bgr, Byte>().Resize(picCapture.Width, picCapture.Height, Inter.Cubic);

            // render captured frame into the picture box picCapture2
            picCapture.Image = currentFrame.Bitmap;

            // toggle start camera capture
            if (facesDetectionEnabled)
            {
                Mat grayImage = new Mat();
                CvInvoke.CvtColor(currentFrame, grayImage, ColorConversion.Bgr2Gray);
                CvInvoke.EqualizeHist(grayImage, grayImage);
                Rectangle[] faces = faceCascadeClassifier.DetectMultiScale(grayImage, 1.1, 3, Size.Empty, Size.Empty);
                if (faces.Length > 0)
                {
                    foreach (var face in faces) {
                        // draw square around each faces
                        CvInvoke.Rectangle(currentFrame, face, new Bgr(Color.Red).MCvScalar, 2);

                        // display face on picDetected
                        Image<Bgr, Byte> resultImage = currentFrame.Convert<Bgr, Byte>();
                        resultImage.ROI = face;
                        picDetected.SizeMode = PictureBoxSizeMode.StretchImage;
                        picDetected.Image = resultImage.Bitmap;

                        if (EnabledSaveImage)
                        {
                            string path = Directory.GetCurrentDirectory() + @"\TrainedImages";

                            // check and create the directory if not exists
                            if (!Directory.Exists(path))
                            {
                                Directory.CreateDirectory(path);
                            }

                            // save 10 images
                            for (int i = 0; i < 10; i++)
                            {
                                resultImage.Resize(200, 200, Inter.Cubic).Save(path + @"\" + txtPersonName.Text + "_" + DateTime.Now.ToString("dd-mm-yyyy-hh-mm-ss") + ".jpg");
                                Thread.Sleep(1000);
                            }

                            // disable saving images
                            EnabledSaveImage = false;

                            if (btnAddPerson.InvokeRequired)
                            {
                                btnAddPerson.Invoke(new ThreadStart(delegate {
                                    btnAddPerson.Enabled = true;
                                }));
                            }

                            // recognize face
                            Image<Gray, Byte> grayFacesResult = resultImage.Convert<Gray, Byte>().Resize(200, 200, Inter.Cubic);
                            var result = recognizer.Predict(grayFacesResult); // here is the error
                            if (result.Label > 0)
                            {
                                CvInvoke.PutText(currentFrame, PersonsNames[result.Label], new Point(face.X - 2, face.Y - 2), FontFace.HersheyComplex, 1.0, new Bgr(Color.Orange).MCvScalar);
                                CvInvoke.Rectangle(currentFrame, face, new Bgr(Color.Green).MCvScalar, 2);
                            }
                            else
                            {
                                CvInvoke.PutText(currentFrame, "Unknown", new Point(face.X - 2, face.Y - 2), FontFace.HersheyComplex, 1.0, new Bgr(Color.Orange).MCvScalar);
                                CvInvoke.Rectangle(currentFrame, face, new Bgr(Color.Green).MCvScalar, 2);
                            }
                        }
                    }
                }
            }
            
            // render captured frame into the picture box picCapture2
            picCapture2.Image = currentFrame.Bitmap;
        }

        private void btnDetect_Click(object sender, EventArgs e)
        {
            facesDetectionEnabled = true;
        }

        private void btnAddPerson_Click(object sender, EventArgs e)
        {
            btnSave.Enabled = false;
            btnAddPerson.Enabled = true;
            EnabledSaveImage = false;
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            btnSave.Enabled = true;
            btnAddPerson.Enabled = false;
            EnabledSaveImage = true;
        }

        private void btnTrain_Click(object sender, EventArgs e)
        {
            TrainImagesFromDir();
        }

        private bool TrainImagesFromDir()
        {
            int ImagesCount = 0;
            int Threshold = -1;
            TrainedFaces.Clear();
            PersonsLabels.Clear();
            PersonsNames.Clear();
            try
            {
                string path = Directory.GetCurrentDirectory() + @"\TrainedImages";
                string[] files = Directory.GetFiles(path, "*.jpg", SearchOption.AllDirectories);
                foreach (var file in files)
                {
                    Image<Gray, Byte> trainedImage = new Image<Gray, byte>(file);
                    TrainedFaces.Add(trainedImage);
                    PersonsLabels.Add(ImagesCount);
                    string name = file.Split('\\').Last().Split('_')[0];
                    PersonsNames.Add(name);
                    ImagesCount++;
                    Debug.WriteLine(ImagesCount + ". " + name);
                }
                recognizer = new EigenFaceRecognizer(ImagesCount, Threshold);
                recognizer.Train(TrainedFaces.ToArray(), PersonsLabels.ToArray());
                isTrained = true;
                Debug.WriteLine(ImagesCount);
                Debug.WriteLine(isTrained);
                return isTrained;
            }
            catch (Exception ex)
            {
                isTrained = false;
                MessageBox.Show("Error in Train Images: " + ex.Message);
                return isTrained;
            }
        }
    }
}


Solution

  • I'm guessing that ProcessFrame is called before the recognizer variable is created, which would give you the null reference. Have you tried stepping through your code with breakpoints to see what happens? This would show you exactly which variable is null :)

    You could add something like this to validate your variables before running the code, it's always a good idea to ensure everything exists before trying to use it:

    private void ProcessFrame(object sender, EventArgs e)
    {
        if(recognizer == null)
            // Throw an error, log something, return, or
            // just create the recognizer and continue