MATLAB平台下基于PCA的人脸识别图像考勤系统及其识别原理

张开发
2026/4/5 10:42:07 15 分钟阅读

分享文章

MATLAB平台下基于PCA的人脸识别图像考勤系统及其识别原理
基于MATLAB平台的PCA的人脸识别图像考勤系统 识别原理为从一副生活照中寻找到人脸并且分割人脸图象利用PCA算法进行降维和库里图片进行对比人脸库和识别图像请自行添加今早挤完电梯冲进公司打卡——指纹考勤机“滴”的一声提示“无法识别请重试”第三次重试差点摔工牌时突然想起上周蹲实验室调的那个基于MATLAB的破刷脸考勤虽然现在还只能放正脸光亮点戴眼镜框隐形识别率差点意思后续慢慢调但至少不用跟磨平的指纹死磕。捣鼓这玩意儿的核心其实就是三步抓脸切脸、降维瘦身、比脸找身份全靠MATLAB自带的工具箱撑场子省了不少手写底层算法的头发。第一步抓脸切脸——别拿后脑勺糊弄机器首先得有个“脸库”对吧我随便从ORL人脸库里扒了40个人每人10张表情光线稍微有点小变化的正脸分辨率112×92懒癌晚期不想自己拍太多丢在项目根目录的face_database文件夹里每人建个自己的子文件夹比如person01到person40每张图命名成1.pgm到10.pgm——名字统一后续代码好遍历。考勤的测试图我单独放test_face文件夹也是正脸清晰的PGM或者JPG都行PGM是灰度图更省事儿。抓脸我用的是MATLAB自带的Viola-Jones检测器——这个玩意儿是老古董但巨好用不用自己调复杂的参数自带训练好的人脸模型。先上一段抓脸代码% 读入待考勤的测试图假设是今天摸鱼拍的摸鱼前.jpg摸鱼后的.jpg选个严肃的吧test.jpg test_img imread(test_face/test.jpg); % 要是彩色图先转成灰度Viola-Jones对灰度图友好 if size(test_img,3) 3 test_gray rgb2gray(test_img); else test_gray test_img; end % 检测器的参数第一个是训练好的人脸模型选这个最小误差的就行 face_detector vision.CascadeObjectDetector; % 检测人脸框bbox是[left top width height]四个数 bbox step(face_detector, test_gray); % 可视化一下抓脸结果万一抓的是鼻孔呢 result_img insertObjectAnnotation(test_img, rectangle, bbox, 今天来了个谁); figure;imshow(result_img);哎对了有时候检测可能会抓到多个框比如我那天把工位的鼠标垫卡通人物也放进去了就被当成“人脸”了。不过咱们场景是固定的打卡区域只有一个人对着摄像头加个简单的筛选就行——选面积最大的框if ~isempty(bbox) % 计算每个框的面积 areas bbox(:,3).*bbox(:,4); % 找面积最大的索引 [~,max_idx] max(areas); bbox bbox(max_idx,:); end抓到框之后就该切脸了切完统一缩放到和脸库一样的112×92不然后续降维没法对齐if ~isempty(bbox) % 从灰度图里切因为PCA降维用的是灰度 crop_face imcrop(test_gray, bbox); % 统一缩放 resize_face imresize(crop_face, [112 92]); % 保存或者直接用先可视化缩切后的结果 figure;imshow(resize_face);title(切好的考勤脸); else error(连脸都没抓到请把正脸对准摄像头); end刚才那段代码的imresize其实也可以换成双线性插值什么的但MATLAB默认的最近邻插值对付这个分辨率已经够了。第二步降维瘦身——别让1万多个像素点打架脸库每张图是112×9210304个像素点如果直接拿10304维的向量去比脸就像在一万层楼里找邻居——太麻烦太慢了而且很多像素点是重复的比如背景墙都是白色的或者额头都是平的没用的信息很多。基于MATLAB平台的PCA的人脸识别图像考勤系统 识别原理为从一副生活照中寻找到人脸并且分割人脸图象利用PCA算法进行降维和库里图片进行对比人脸库和识别图像请自行添加这时候就该PCA主成分分析登场了——它的核心思想就是“把有用的信息拧成一股绳没用的信息扔掉”。比如10304维的向量PCA能给你降到50维甚至20维保留90%以上的有用信息。先理理PCA在人脸识别里的流程别太复杂大概意思到就行把脸库所有图拉成列向量拼成一个“大脸矩阵”每一列是一张脸减去大脸矩阵的平均脸得到“中心化脸矩阵”计算中心化脸矩阵的协方差矩阵不对不对——脸的数量比如40×10400张远小于像素数10304直接算协方差矩阵会死人的10304×10304的矩阵所以算协方差矩阵的转置转置转置重要的事情说三遍也就是“中心化脸矩阵的转置 × 中心化脸矩阵”得到400×400的小矩阵算它的特征值和特征向量然后再还原回去得到大矩阵的特征向量也就是所谓的“特征脸”选前k个最大的特征值对应的特征向量作为投影矩阵把脸库所有的中心化脸都投影到投影矩阵上得到“脸库特征向量集”好上代码先假设我们已经把脸库遍历完拉成大脸矩阵了遍历脸库的代码后面补% ------------------ 假设已经有这些变量 ------------------ % 1. all_faces: 大脸矩阵10304行×400列每列是拉成列向量的脸double类型不然数值会溢出 % 2. labels: 标签向量400行×1列person01的10张图标1person02标2...person40标40 % ----------------------------------------------------------- % 第一步计算平均脸再中心化 avg_face mean(all_faces, 2); % 10304行×1列 centered_faces all_faces - avg_face; % 第二步转置转置转置算小矩阵的特征值特征向量再还原 small_cov centered_faces * centered_faces; % 400×400 [eig_vecs_small, eig_vals] eig(small_cov); % 特征值默认从小到大排 eig_vecs_small fliplr(eig_vecs_small); % 左右翻转变成从大到小 eig_vals diag(eig_vals); % 提取对角线上的特征值 eig_vals flipud(eig_vals); % 上下翻转从大到小 % 还原成大矩阵的特征向量特征脸 eig_vecs centered_faces * eig_vecs_small; % 归一化特征脸不然后续投影的数值会乱 eig_vecs eig_vecs ./ vecnorm(eig_vecs); % 第三步选前k个特征脸这里选能保留95%信息的k cumulative_ratio cumsum(eig_vals) / sum(eig_vals); k find(cumulative_ratio 0.95, 1, first); fprintf(保留95%%信息的特征脸数量%d\n, k); projection_matrix eig_vecs(:, 1:k); % 10304×k % 第四步把脸库所有中心化脸投影到投影矩阵上 face_database_features projection_matrix * centered_faces; % k×400 % 可视化一下前9个特征脸看看长啥样就是一堆抽象的脸 figure; for i 1:9 subplot(3,3,i); eigenface reshape(eig_vecs(:,i), [112 92]); imshow(eigenface, []); % []自动调整对比度不然全是黑的 title([特征脸, num2str(i)]); end sgtitle(前9个抽象的特征脸);这段代码里的cumsum和find组合特别好用不用自己手动数保留多少个特征脸直接让程序算到95%就行我试了ORL库大概保留40-50个超级省事儿。哦对了补一下遍历脸库的代码不然前面的all_faces和labels从哪儿来% 初始化变量 all_faces []; labels []; % 脸库根目录 database_dir face_database; % 遍历所有person子文件夹 person_dirs dir(database_dir); person_dirs person_dirs(~ismember({person_dirs.name}, {., ..})); % 去掉.和.. for i 1:length(person_dirs) person_path fullfile(database_dir, person_dirs(i).name); image_files dir(fullfile(person_path, *.pgm)); % 只找pgm格式的 for j 1:length(image_files) image_path fullfile(person_path, image_files(j).name); img imread(image_path); % 转成double拉成列向量 img_vec double(reshape(img, [], 1)); all_faces [all_faces, img_vec]; labels [labels; i]; % person01标1以此类推 end end遍历完就可以把这些变量保存成MAT文件下次直接加载不用再遍历save(face_database_data.mat, all_faces, labels, avg_face, projection_matrix, face_database_features);第三步比脸找身份——测测今天摸鱼的是谁刚才第一步我们已经把测试图切好缩好转成列向量了接下来就是和脸库比脸% 加载之前保存的MAT文件 load(face_database_data.mat); % ------------------ 假设已经有切好缩好的resize_face ------------------ % 转成double拉成列向量 test_vec double(reshape(resize_face, [], 1)); % 中心化 centered_test test_vec - avg_face; % 投影到特征脸空间 test_feature projection_matrix * centered_test; % k×1 % 计算欧氏距离找最近的脸 distances vecnorm(face_database_features - test_feature, 2, 1); % 1×400每个元素是和对应脸库图的距离 [min_dist, min_idx] min(distances); % 设置一个阈值防止拿陌生人的脸过来阈值自己调ORL库我试的5000左右合适 threshold 5000; if min_dist threshold person_id labels(min_idx); fprintf(考勤成功是Person %02d距离%.2f\n, person_id, min_dist); % 可以把名字对应上比如建个字典 name_dict {张三,李四,王五,...}; % 40个名字自己填 fprintf(真实姓名%s\n, name_dict{person_id}); else fprintf(识别失败陌生人或者脸没放正距离%.2f\n, min_dist); end欧氏距离就是最普通的距离公式PCA人脸识别里也有用余弦相似度的但我试了ORL库欧氏距离已经够用了而且好调阈值。阈值怎么调呢可以拿脸库的训练集和测试集分开比如每人拿8张当训练2张当测试然后画ROC曲线找最优阈值——但懒癌晚期的我直接拿几张陌生人的脸和几张脸库的脸试了试大概5000左右就行。最后一些小吐槽和小改进这个系统其实挺简陋的比如只能识别正脸侧脸低头戴口罩全歇菜光线影响很大要是考勤机对着窗户逆光根本抓不到脸戴隐形眼镜有时候识别率会下降可能是我扒的ORL库戴眼镜的样本少没有集成摄像头只能拿拍好的图片测试小改进的话比如抓脸的时候可以加个眼睛检测确保是正脸降维之后可以加个LDA线性判别分析提升类间距离集成MATLAB的摄像头工具箱实时抓脸实时识别加个Excel导出功能记录考勤时间不过对于我这种不想跟指纹死磕的懒癌来说现在这个版本已经够用了——下次挤完电梯掏出手机拍张正脸丢进去跑一遍就能代替考勤机打卡嘘别让老板知道。

更多文章