EmguCV - 面部识别 - 使用Microsoft Access数据库中的训练集时,“对象引用未设置”异常

时间:2013-05-09 03:21:18

标签: c# ms-access nullreferenceexception emgucv face-recognition

我一直在使用EmguCV(C#)开发人脸识别应用程序。如果我将面部图像(训练集)存储在简单的Windows文件夹中,我就可以完成所有工作。但是,在我尝试迁移面部图像以存储在Microsoft Access数据库中之后,当应用程序尝试时,经常会出现“对象引用未设置为对象的实例”异常消息(并非总是如此,但大多数情况下)从视频Feed中识别脸部。

有趣的是,如果没有发生异常,识别实际上仍然可行。

这是我程序代码的片段,使用windows文件夹和数据库:

从Windows文件夹中读取存储的图像

private void FaceRecognition_Load(object sender, EventArgs e)
    {
        //if capture is not created, create it now
        if (capture == null)
        {
            try
            {
                capture = new Capture();
            }
            catch (NullReferenceException excpt)
            {
                MessageBox.Show(excpt.Message);
            }
        }

        if (capture != null)
        {
            if (captureInProgress)
            {  
                Application.Idle -= ProcessFrame;
            }
            else
            {
                Application.Idle += ProcessFrame;
            }

            captureInProgress = !captureInProgress;
        }

        #endregion
        {
            // adjust path to find your xml at loading
            haar = new HaarCascade("haarcascade_frontalface_default.xml");

            try
            {
                //Load of previus trainned faces and labels for each image
                string Labelsinfo = File.ReadAllText(Application.StartupPath + "\\TrainedFaces\\TrainedLabels.txt");
                string[] Labels = Labelsinfo.Split('%');
                NumLabels = Convert.ToInt16(Labels[0]);
                ContTrain = NumLabels;
                string LoadFaces;

                for (int tf = 1; tf < NumLabels + 1; tf++)
                {
                    LoadFaces = "face" + tf + ".bmp";
                    trainingImages.Add(new Image<Gray, byte>(Application.StartupPath + "\\TrainedFaces\\" + LoadFaces));
                    labels.Add(Labels[tf]);
                }

            }
            catch (Exception error)
            {
                //MessageBox.Show(e.ToString());
                MessageBox.Show("Nothing in binary database, please add at least a face(Simply train the prototype with the Add Face Button).", "Triained faces load", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
    }

从Microsoft Access数据库中读取存储的图像

private void connectToDatabase()
    {
        DBConnection.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=FacesDatabase.mdb";
        DBConnection.Open();
        dataAdapter = new OleDbDataAdapter("Select * from TrainingSet1", DBConnection);
        dataAdapter.Fill(localDataTable);

        if (localDataTable.Rows.Count != 0)
        {
            numOfRows = localDataTable.Rows.Count;
        }
    }

private void FaceRecognition_Load(object sender, EventArgs e)
    {
        //if capture is not created, create it now
        if (capture == null)
        {
            try
            {
                capture = new Capture();
            }
            catch (NullReferenceException excpt)
            {
                MessageBox.Show(excpt.Message);
            }
        }

        if (capture != null)
        {
            if (captureInProgress)
            {  
                Application.Idle -= ProcessFrame;
            }
            else
            {
                Application.Idle += ProcessFrame;
            }

            captureInProgress = !captureInProgress;
        }

        #endregion
        {
            // adjust path to find your xml at loading
            haar = new HaarCascade("haarcascade_frontalface_default.xml");

            connectToDatabase();

            Bitmap bmpImage;

            for (int i = 0; i < numOfRows; i++)
            {
                byte[] fetchedBytes = (byte[])localDataTable.Rows[i]["FaceImage"];
                MemoryStream stream = new MemoryStream(fetchedBytes);
                bmpImage = new Bitmap(stream);
                trainingImages.Add(new Emgu.CV.Image<Gray, Byte>(bmpImage));

                String faceName = (String)localDataTable.Rows[i]["Name"];
                labels.Add(faceName);
            }
       }
   }

导致异常的面部识别功能(使用Windows文件夹和Access数据库时完全相同):

private void ProcessFrame(object sender, EventArgs arg)
    {
        Image<Bgr, Byte> ImageFrame = capture.QueryFrame();

        Image<Gray, byte> grayframe = ImageFrame.Convert<Gray, byte>();

        MinNeighbors = int.Parse(comboBoxMinNeighbors.Text);
        WindowsSize = int.Parse(textBoxWinSiz.Text);
        ScaleIncreaseRate = Double.Parse(comboBoxMinNeighbors.Text);

        var faces = grayframe.DetectHaarCascade(haar, ScaleIncreaseRate, MinNeighbors,
                                        HAAR_DETECTION_TYPE.DO_CANNY_PRUNING,
                                        new Size(WindowsSize, WindowsSize))[0];

        if (faces.Length > 0) 
        {
            Bitmap BmpInput = grayframe.ToBitmap();

            Graphics FaceCanvas;

            foreach (var face in faces)
            {
                t = t + 1;
                result = ImageFrame.Copy(face.rect).Convert<Gray, byte>().Resize(100, 100, Emgu.CV.CvEnum.INTER.CV_INTER_CUBIC);

                ImageFrame.Draw(face.rect, new Bgr(Color.Red), 2);

                ExtractedFace = new Bitmap(face.rect.Width, face.rect.Height);

                FaceCanvas = Graphics.FromImage(ExtractedFace);

                FaceCanvas.DrawImage(BmpInput, 0, 0, face.rect, GraphicsUnit.Pixel);

                ImageFrame.Draw(face.rect, new Bgr(Color.Red), 2);

                if (trainingImages.ToArray().Length != 0)
                {

                    MCvTermCriteria termCrit = new MCvTermCriteria(ContTrain, 0.001);

                    EigenObjectRecognizer recognizer = new EigenObjectRecognizer(
                        trainingImages.ToArray(),
                        labels.ToArray(),
                        3000,
                        ref termCrit);
                    try
                    {
                        name = recognizer.Recognize(result).Label; 
                    }
                    catch (Exception error)
                    {
                        MessageBox.Show(error.ToString());
                    }

                    ImageFrame.Draw(name, ref font, new Point(face.rect.X - 2, face.rect.Y - 2), new Bgr(Color.LightGreen));
                }

            }
        }
        CamImageBox.Image = ImageFrame;
    }

以下是异常消息的屏幕截图: http://i.imgur.com/DvAhABK.jpg

发生异常的第146行是ProcessFrame函数的这一行:

name = recognizer.Recognize(result).Label;

我尝试在互联网上搜索类似的问题,并发现这些: 'Object reference not set to instance of an object' error when trying to upload image to database Object reference not set to an instance of an object #5 C# Error 'Object Reference Not Set To An Instance Of An Object' C#, "Object reference not set to an instance of an object." error

他们中的大多数建议检查所涉及的任何变量是否为空。我检查了涉及的变量,实际上当recognizer.Recognize(result)语句返回null时发生异常。

所以我的问题是,当我使用数据库中的训练图像时,为什么该语句经常返回null,而当我使用来自windows文件夹的训练图像时它永远不会返回null?

感谢任何帮助,对不起,如果问题很长,我只想清楚。 如果需要,我很乐意添加代码的其他部分:)

提前致谢!

3 个答案:

答案 0 :(得分:0)

检查您的fetchedBytes数组,看看您是否始终表示BMP图像的字节流(以0x42 0x4D开头),或者是否有“其他东西“也在那里。”

根据BMP数据如何插入Access数据库,可能包含OLE“包装器”。例如,MSPAINT.EXE就像这样保存了一个纯红色的8x8 24位BMP图像

bmpDump.png

如果我复制该文件并将其粘贴到Access窗体中的绑定对象框中,则在将BMP数据写入表之前,Access会将BMP数据包装在某些“OLE内容”中。之后,如果我尝试通过代码检索BMP图像,使用类似的东西......

Sub oleDumpTest()
    Dim rst As ADODB.Recordset, ads As ADODB.Stream
    Set rst = New ADODB.Recordset
    rst.Open "SELECT * FROM TrainingSet1 WHERE ID = 1", Application.CurrentProject.Connection
    Set ads = New ADODB.Stream
    ads.Type = adTypeBinary
    ads.Open
    ads.Write rst("FaceImage").Value
    rst.Close
    Set rst = Nothing
    ads.SaveToFile "C:\Users\Gord\Pictures\oleDump_red."
    ads.Close
    Set ads = Nothing
End Sub

...然后生成的文件还包含OLE“包装器”......

oleDump.png

...显然不是一个有效的独立BMP文件。如果我重命名该文件以赋予它.bmp扩展名并尝试在Paint中打开它,我会得到

paintError.png

因此,数据库中的[FaceImage]对象可能(某些)不是原始BMP数据,也许其他软件拒绝它们(或者根本无法理解它们)。

修改

另一个可能的问题是,当您从文件夹中的文件中获取图像时,您将Image对象包含一个包含文件路径的字符串...

trainingImages.Add(new Image<Gray, byte>(Application.StartupPath + "\\TrainedFaces\\" + LoadFaces));

...但是当您尝试从数据库中检索图像时,您将同一个对象传递给Bitmap对象

MemoryStream stream = new MemoryStream(fetchedBytes);
bmpImage = new Bitmap(stream);
trainingImages.Add(new Emgu.CV.Image<Gray, Byte>(bmpImage));

我无法知道Emgu.CV.Image对象的行为是否会有所不同,具体取决于给定对象的类型,但快速+脏的解决方法可能是将bmpImage写入临时文件,手trainingImages.Add该文件的路径,然后删除该文件。

答案 1 :(得分:0)

终于成功了!!再过一天的编码帮助我解决了问题:

adb shell monkey -p com.android.mms 1

答案 2 :(得分:-1)

我无法通过从图像所在的数据库中读取直接Stream来解决问题,但是您的解决方法是将图像保存到本地文件夹,为我工作,很多人都可以共享。这是我的演示页面。从DB加载文件:http://www.edatasoluciones.com/FaceDetection/FaceDataBase