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

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

有趣的是,如果不发生exception,识别实际上仍然可以正常工作。

这是我的程序代码的片段,使用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(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(bmpImage)); String faceName = (String)localDataTable.Rows[i]["Name"]; labels.Add(faceName); } } } 

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

 private void ProcessFrame(object sender, EventArgs arg) { Image ImageFrame = capture.QueryFrame(); Image grayframe = ImageFrame.Convert(); 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().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; } 

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

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

 name = recognizer.Recognize(result).Label; 

我试图在互联网上搜索类似的问题,并发现这些: ‘对象引用未设置为对象的实例’错误在尝试将图像上载到数据库时 对象引用未设置为对象的实例#5 C#错误’对象引用未设置为对象’ C# 的实例 ,“对象引用未设置为对象的实例。” 错误

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

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

任何帮助表示赞赏,对不起,如果问题很长,我只想明确。 如果需要,我会很高兴添加代码的其他部分:)

提前致谢!

检查你的fetchedBytes数组,看看你是否一直得到一个代表BMP图像的字节流(从0x42 0x4D开始),或者那里也可能有“其他东西”。

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

bmpDump.png

如果我复制该文件并将其粘贴到Access窗体中的绑定对象框中,则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(Application.StartupPath + "\\TrainedFaces\\" + LoadFaces)); 

…但是当您尝试从数据库中检索图像时,您将相同的对象BitmapBitmap对象

 MemoryStream stream = new MemoryStream(fetchedBytes); bmpImage = new Bitmap(stream); trainingImages.Add(new Emgu.CV.Image(bmpImage)); 

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

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

 public void ProcessRequest(HttpContext context) { _httpContext = context; var imageid = context.Request.QueryString["Image"]; if (imageid == null || imageid == "") { imageid = "1"; } using (WebClient wc = new WebClient()) { // Handler retrieves the image from database and load it on the stream using (Stream s = wc.OpenRead("http://mypageurl/Image.ashx?Image=" + imageid)) { using (Bitmap bmp = new Bitmap(s)) { AddFace(bmp); } } } } public void AddFace(Bitmap image) { var faceImage = DetectFace(image); if (faceImage != null) { var stream = new MemoryStream(); faceImage.Save(stream, ImageFormat.Bmp); stream.Position = 0; byte[] data = new byte[stream.Length]; stream.Read(data, 0, (int)stream.Length); _httpContext.Response.Clear(); _httpContext.Response.ContentType = "image/jpeg"; _httpContext.Response.BinaryWrite(data); } } private Bitmap DetectFace(Bitmap faceImage) { var image = new Image(faceImage); var gray = image.Convert(); string filePath = HttpContext.Current.Server.MapPath("haarcascade_frontalface_default.xml"); var face = new HaarCascade(filePath); MCvAvgComp[][] facesDetected = gray.DetectHaarCascade(face, 1.1, 10, HAAR_DETECTION_TYPE.DO_CANNY_PRUNING, new Size(20, 20)); Image result = null; foreach (MCvAvgComp f in facesDetected[0]) { //draw the face detected in the 0th (gray) channel with blue color image.Draw(f.rect, new Bgr(Color.Blue), 2); result = image.Copy(f.rect).Convert(); break; } if (result != null) { result = result.Resize(200, 200, INTER.CV_INTER_CUBIC); return result.Bitmap; } return null; } public bool IsReusable { get { return false; } } 

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