如何使用Linear Discriminant Analysis匹配ATT数据集训练测试集相似图像
解决方案实现
1. 核心功能:图像转一维向量实现
首先统一所有输入图像的分辨率(ATT人脸数据集默认分辨率为92*112,可直接使用该参数,自定义图像可按需调整),将图像转灰度后逐行拉平即可得到需求的一维向量。
首先封装人脸处理工具类,统一管理参数和训练数据:
// 人脸处理辅助类,封装向量转换、LDA训练相关数据 public class FaceProcessingHelper { // 统一图像尺寸,可根据实际数据集调整 public const int ImgWidth = 92; public const int ImgHeight = 112; // 向量总长度 = 宽*高 public const int VectorLength = ImgWidth * ImgHeight; // 存储训练集所有图像的一维向量 public List<double[]> TrainVectors { get; set; } = new List<double[]>(); // 存储训练集向量对应的人脸标签/文件名 public List<string> TrainLabels { get; set; } = new List<string>(); // 训练集均值向量,你后续计算后可存在这里 public double[] MeanVector { get; set; } }
以下是图像转一维向量的实现方法:
// 输入Bitmap图像,输出灰度化后的一维向量 public static double[] BitmapToVector(Bitmap img) { // 先把图像缩放到统一尺寸 Bitmap resizedImg = new Bitmap(img, FaceProcessingHelper.ImgWidth, FaceProcessingHelper.ImgHeight); double[] vector = new double[FaceProcessingHelper.VectorLength]; int index = 0; for (int y = 0; y < FaceProcessingHelper.ImgHeight; y++) { for (int x = 0; x < FaceProcessingHelper.ImgWidth; x++) { Color pixel = resizedImg.GetPixel(x, y); // 标准灰度转换公式,避免直接取单通道丢失信息 double grayValue = pixel.R * 0.299 + pixel.G * 0.587 + pixel.B * 0.114; vector[index] = grayValue; index++; } } return vector; }
该方法输出的数组结构和你示例的向量完全匹配,顺序为逐行从左到右读取像素灰度值。
2. 训练按钮(button1)逻辑完善
在Form1类中实例化全局工具类,补全训练逻辑:
public partial class Form1 : Form { // 全局人脸处理对象,存储训练好的数据 private FaceProcessingHelper _faceHelper = new FaceProcessingHelper(); // 数据集路径,可根据你的实际路径修改 private string _trainPath = @"C:\Users\TimothyFarCry5\Documents\visual studio 2015\Projects\LDA-FaceR\LDA-FaceR\ATTDataSet\Training"; private string _testPath = @"C:\Users\TimothyFarCry5\Documents\visual studio 2015\Projects\LDA-FaceR\LDA-FaceR\ATTDataSet\Testing"; public Form1() { InitializeComponent(); } // 训练按钮点击事件 private void button1_Click(object sender, EventArgs e) { // 清空上一次训练的缓存 _faceHelper.TrainVectors.Clear(); _faceHelper.TrainLabels.Clear(); // 遍历训练文件夹下所有jpg图像 string[] trainImgs = Directory.GetFiles(_trainPath, "*.jpg"); foreach (string imgPath in trainImgs) { Bitmap bmp = new Bitmap(Image.FromFile(imgPath)); // 转成向量存入训练集 double[] vec = BitmapToVector(bmp); _faceHelper.TrainVectors.Add(vec); // 存储文件名作为标签,也可以按人脸ID打分类标签(LDA训练需要类别信息) _faceHelper.TrainLabels.Add(Path.GetFileName(imgPath)); } // ========= 以下为你可以自行补充的LDA训练逻辑 ========= // 1. 计算所有训练向量的均值向量,存入_faceHelper.MeanVector // 2. 所有训练向量减去均值向量得到中心化向量 // 3. 计算类内散度矩阵、类间散度矩阵 // 4. 求解广义特征值,取前K个最大特征值对应的特征向量作为投影矩阵 // 5. 将所有训练向量投影到LDA特征空间,存储投影后的特征用于后续匹配 MessageBox.Show($"训练完成,共加载{_faceHelper.TrainVectors.Count}张训练图像"); // 显示第一张训练图像在original image框 pictureBox1.Image = Image.FromFile(trainImgs[0]); }
3. 识别按钮(button2)逻辑框架
private void button2_Click(object sender, EventArgs e) { if (_faceHelper.TrainVectors.Count == 0) { MessageBox.Show("请先完成训练再执行识别"); return; } // 遍历测试文件夹所有图像 string[] testImgs = Directory.GetFiles(_testPath, "*.jpg"); foreach (string testImgPath in testImgs) { Bitmap testBmp = new Bitmap(Image.FromFile(testImgPath)); // 测试图像转向量 double[] testVec = BitmapToVector(testBmp); // ========= 以下为你可以自行补充的识别逻辑 ========= // 1. 测试向量减去训练集均值向量中心化 // 2. 投影到LDA特征空间得到测试特征 // 3. 计算测试特征和所有训练集投影特征的欧式距离/余弦距离 // 4. 取距离最小的训练样本作为匹配结果,得到对应索引matchIndex int matchIndex = 0; // 这里先写示例值,替换为实际匹配结果索引即可 string matchImgPath = Path.Combine(_trainPath, _faceHelper.TrainLabels[matchIndex]); // 显示测试图像到original image框,匹配到的训练图像到recognized image框 pictureBox1.Image = testBmp; pictureBox2.Image = Image.FromFile(matchImgPath); // 可选:每识别一张停顿看效果,不需要可删除 MessageBox.Show($"当前测试图像:{Path.GetFileName(testImgPath)}\n匹配结果:{_faceHelper.TrainLabels[matchIndex]}"); } }
注意事项
- 若你使用的图像尺寸不是92*112,修改
FaceProcessingHelper里的宽高参数即可 - LDA是有监督分类算法,训练时建议按人脸ID给样本打类别标签(比如S1文件夹下的所有图像标签都为1,S2的为2),不要直接用文件名,更便于计算类间散度
- 数据集较大时可以用LockBits方法替代GetPixel加速像素读取,小数据集无需调整
内容的提问来源于stack exchange,提问作者user16612111
相关产品推荐
相关产品推荐

