C# EMGU人脸识别程序EigenFaceRecognizer.Predict空引用异常求助
排查C# EMGU人脸识别程序的NullReferenceException异常
核心问题定位
触发System.NullReferenceException的直接原因是recognizer对象未完成初始化:
recognizer仅在点击btnTrain执行TrainImagesFromDir方法后才会被实例化- 如果未执行训练流程,或者训练失败(比如训练目录为空、文件读取错误),
recognizer会保持null状态 - 原代码在保存人脸图像后直接调用
recognizer.Predict,此时大概率还未完成训练,导致空引用异常
修复方案
1. 增加Recognizer非空校验
在调用识别方法前先判断对象是否存在,避免异常:
// 替换原识别代码段 if (recognizer != null) { Image<Gray, Byte> grayFacesResult = resultImage.Convert<Gray, Byte>().Resize(200, 200, Inter.Cubic); var result = recognizer.Predict(grayFacesResult); // 后续绘制逻辑... } else { // 提示用户未完成训练 CvInvoke.PutText(currentFrame, "未完成模型训练", new Point(face.X - 2, face.Y - 2), FontFace.HersheyComplex, 1.0, new Bgr(Color.Red).MCvScalar); }
2. 分离保存与识别逻辑
保存人脸是为了生成训练数据,不应和识别逻辑绑定。将识别逻辑独立出来,仅当训练完成后执行:
// 修改ProcessFrame中的人脸循环逻辑 foreach (var face in faces) { // 原有的绘制人脸框、显示检测结果代码... if (EnabledSaveImage) { // 原有的保存10张人脸图像代码... // 保存完成后提示用户执行训练 MessageBox.Show("人脸图像已保存,请点击「训练」按钮完成模型训练"); EnabledSaveImage = false; // 恢复按钮状态代码... } // 独立执行识别逻辑(仅训练完成后) if (isTrained && recognizer != null) { Image<Gray, Byte> grayFacesResult = resultImage.Convert<Gray, Byte>().Resize(200, 200, Inter.Cubic); var result = recognizer.Predict(grayFacesResult); // 修复标签索引判断:原代码判断result.Label>0会跳过第一个人的识别 if (result.Label >= 0 && result.Label < PersonsNames.Count) { CvInvoke.PutText(currentFrame, PersonsNames[result.Label], new Point(face.X - 2, face.Y - 2), FontFace.HersheyComplex, 1.0, new Bgr(Color.Green).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.Red).MCvScalar, 2); } } }
3. 修复训练逻辑的标签分配问题
原训练代码中每张图像对应一个新标签,会导致同一个人的多张图像被识别为不同身份,需修改为同一人对应同一标签:
private bool TrainImagesFromDir() { int ImagesCount = 0; int Threshold = -1; TrainedFaces.Clear(); PersonsLabels.Clear(); PersonsNames.Clear(); // 新增字典映射姓名与标签 Dictionary<string, int> nameToLabel = new Dictionary<string, int>(); int currentLabel = 0; 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); string name = file.Split('\\').Last().Split('_')[0]; // 同一姓名对应同一标签 if (!nameToLabel.ContainsKey(name)) { nameToLabel[name] = currentLabel; PersonsNames.Add(name); currentLabel++; } PersonsLabels.Add(nameToLabel[name]); ImagesCount++; Debug.WriteLine(ImagesCount + ". " + name); } if (TrainedFaces.Count > 0) { recognizer = new EigenFaceRecognizer(TrainedFaces.Count, Threshold); recognizer.Train(TrainedFaces.ToArray(), PersonsLabels.ToArray()); isTrained = true; } else { MessageBox.Show("训练目录中没有人脸图像"); isTrained = false; } Debug.WriteLine(ImagesCount); Debug.WriteLine(isTrained); return isTrained; } catch (Exception ex) { isTrained = false; MessageBox.Show("训练失败: " + ex.Message); return isTrained; } }
其他潜在优化点
Thread.Sleep(1000)会阻塞UI线程导致界面卡顿,建议改用异步延时或单独线程处理图像保存- 人脸检测
DetectMultiScale可增加最小尺寸参数minSize = new Size(100,100),避免检测过小的无效区域 - 保存图像时的文件名可增加毫秒级时间戳或随机数,避免文件名重复
内容的提问来源于stack exchange,提问作者Jake
相关产品推荐
相关产品推荐

