☰
Matlab人脸识别课设实战:PCA特征脸、参数调优与避坑指南
2026/10/1 3:40:01 网站建设 项目流程

简介:这是一套基于MATLAB平台的人脸识别系统实现方案与课程设计报告,面向图像处理、模式识别方向的课程设计学生和Matlab入门开发者,帮助理解从图像预处理、特征提取到模式匹配的完整工作流。资源包共136个文件,约2.43MB,以105张bmp人脸样本图像为主,配合2个m源码文件、1个fig图形文件、1份pdf和1份md课程设计文档,另有zbak备份文件,覆盖样本数据、程序实现与说明文档三大类。系统涵盖图像预处理、特征提取与模式匹配等模块,代码遵循规范的编程准则,关键算法步骤均配有详细注释,并经过反复调试,可在常规Matlab环境下稳定运行。已有26人学习,适合作为课程设计参考、算法复现练习或答辩准备资料。配套设计报告系统阐述了理论基础、算法选择依据、系统架构设计及性能评估方法,读者可结合源码对照学习,也可参考其报告组织方式完成自己的课程设计文档。

1. 基于Matlab的人脸识别系统:课设从“能跑”到“能答辩”差在哪

课程设计选人脸识别方向的人每年都很多,但答辩现场翻车的更多。不少同学从网上拼一段代码,跑ORL库报个98%识别率,老师一调摄像头,换个人、换个光照,系统直接乱认。问题不在算法不够先进,而在多数人只做到了“代码能跑”,没做到“系统能讲”。

下面这条实现链路基于Matlab把人脸识别从原理拆到落地:先说清PCA特征脸为什么能在课设里立住,再给出从ORL数据集训练到摄像头实时识别的完整代码,随后把降维维数、距离度量、数据划分三个参数逐个调给你看,避坑章收录了五个最典型的翻车现场。新手能照着敲完,熟手可以直接抄走参数表和排错思路。

2. 人脸识别原理与Matlab选型:特征脸为什么还是课设的主线

人脸识别到今天的深度学习方案确实猛,普通人脸库里随便一个CNN都能做到99%以上,但课程设计选Matlab加特征脸仍然是最稳妥的组合。原因有三:一是Matlab对传统图像处理函数的封装非常完整,几十行就能把从图像输入到分类输出的链路走通;二是PCA特征脸涉及矩阵分解、特征值、降维这些线性代数和数字图像处理的核心考点,报告能写深,答辩能展开;三是ORL这类小型人脸库上,PCA加最近邻的识别率做到90%以上并不难,加上LDA改进后可以摸到95%,和深度学习的差距没有想象中那么大,但原理完全在课设知识范围内。

先把系统拆成流程,再对比两种主流特征提取算法,最后交代Matlab环境要准备哪些工具箱。这样你写报告的时候,原理部分和实现部分不会两张皮。市面上人脸识别门禁机、考勤机的算法主干也基本是这个流程,Matlab方案正好充当嵌入式门禁方案的算法验证原型。

2.1 人脸识别四步流程拆解:检测、对齐、特征提取、分类各自干什么

一套传统人脸识别系统不管怎么改,主干都离不开四步:人脸检测、人脸对齐、特征提取、分类判决。与其把代码东一块西一块凑在一起,不如按这四步去组织你的函数和报告目录,写出来才像一套系统而不是测试脚本。

人脸检测这一步解决“人脸在哪”。Matlab里最直接的方式是调用vision.CascadeObjectDetector,它实现的是Viola-Jones检测器,基于Haar特征和AdaBoost级联。检测器返回一组边界框坐标,形式是[x y width height]。课程设计只需要用它框出人脸区域,不要求自己实现级联分类器,这部分属于Computer Vision Toolbox。

人脸对齐解决“人脸摆正没有”。理论上要检测眼睛、鼻子等关键点,再通过仿射变换把人脸转到标准位置。课设阶段很多人会省略这一步,但省略的代价是识别率对姿态和光照敏感,后面避坑章会专门讲。实现上可以用眼睛检测模型找到眼睛位置,再estimateGeometricTransform做射影变换。我一般建议课设至少做一步简单的尺寸归一化加直方图均衡,成本低、收益明显。

特征提取和分类是重头。特征提取把一张112×92、总共10304像素的人脸图像压缩成几十上百维的向量,这一步做得好坏直接决定识别效果。PCA把人脸投影到特征脸空间,LDA再投影一次让类间距离最大、类内距离最小。分类阶段最简单且有效的是最近邻,即计算测试样本与所有训练样本的距离,取最近的那个当结果。四步逻辑理清楚后,选型就顺理成章了。

2.2 特征脸(PCA)与Fisher脸(LDA)对比:什么时候选PCA、什么时候补LDA

PCA和LDA是两种特征提取策略。PCA是无监督的,只盯着全体数据的方差,投影方向让数据重构误差最小;LDA是有监督的,把类别信息拉进来,让不同人的投影尽量分开、同一个人的投影尽量聚拢。听起来LDA更划算,但LDA有一个前提:类内散度矩阵必须可逆,这对小样本高维数据极其苛刻。

课程设计用ORL这类40人乘10张的小库,每类只有10个样本,直接算类内散度矩阵,维度是10304×10304,奇异性几乎是必然的。主流做法是两段式:先用PCA把维度压到40到100,再做LDA,Fisher判别的数值稳定性就上来了。如果嫌麻烦,只做PCA加最近邻也能到90%以上,做课设完全够用。报告里把PCA基线和PCA加LDA改进两种都跑了,识别率有个提升梯度,答辩时有话讲。

对比项特征脸(PCA)Fisher脸(PCA+LDA)
监督信息无监督,只看方差有监督,利用类别标签
数值稳定性用SVD稳定小样本下易奇异
ORL库经验识别率约90%到95%约94%到98%
实现复杂度低中
课设推荐度必做基线作为改进方案

表格里的识别率是经验区间,不同数据划分差异大,别当硬指标写进报告,自己跑出来的数字才算数。如果问哪个更值得写进报告,我的答案是两个都写。原理部分对比这两种算法,实验部分给出PCA和PCA加LDA两条识别率曲线,这是很像样的课设工作量。很多高分课设报告,本质上就是把“基线、改进、对比”这套实验逻辑走完整了。

2.3 Matlab环境与工具箱:装哪些组件、什么版本能跑、硬件够不够

开始写代码之前,先把环境确认一遍。Matlab版本不是越新越好,r2018a以上其实都够用,新版r2023b、r2026b在webcam函数和App Designer上体验更好,但老版本配Computer Vision Toolbox跑人脸检测也没问题。最重要的是工具箱别漏装:Image Processing Toolbox、Statistics and Machine Learning Toolbox、Computer Vision Toolbox、Camera Support Package这四样基本是标配。安装时工具箱没勾全,运行到对应函数才报错,来回补装很耽误事。

硬件方面,课设阶段完全不需要GPU。ORL全库400张图训练PCA,普通笔记本算SVD也就是秒级到十秒级的事。如果换成自拍高清大图,1920×1080全尺寸进PCA,协方差矩阵会非常大,内存占用问题在避坑章详细展开。内存8G跑ORL绰绰有余,16G想怎么折腾都行。

有两个小习惯建议先养成。一是脚本里所有路径用相对路径或统一用fullfile拼接,避免换机器跑就报找不到文件。二是Matlab中文注释在老版本里偶尔会乱码,特别是r2018a到r2022b这一段,如果被注释问题干扰,要么用英文注释,要么直接升到r2023b以上。这些对后面几个章节的复现体验影响很大。

3. 用Matlab实现人脸识别核心代码:ORL数据集训练与摄像头实时识别

现在开始写代码。这一章按数据准备、训练与测试、摄像头实时识别的顺序推进,每段代码都给出能直接运行的函数和调用方式。ORL数据库在课设里很常见,老师一般会随课件一起发;如果手里没有,用自己拍的10个人每人10张照片组成小库也能跑通流程,只是预处理成本更高,新手不建议从自采数据开始。

3.1 数据准备:ORL人脸库读取、灰度化、尺寸归一化的标准操作

ORL库每张图像是112×92的8位灰度pgm格式,40人每人10张,目录结构一般是s1/1.pgm到s40/10.pgm。第一步是把400张图读进来、展平成列向量、组装成特征矩阵X,每一列是一张图,同时记录标签向量。

% trainFaceModel.m - 训练主函数 % dataDir: ORL数据根目录,k: 保留的主成分个数 function model = trainFaceModel(dataDir, k) numClasses = 40; % ORL库共40个人 numPerClass = 10; % 每人10张 imgRows = 112; imgCols = 92; % ORL每张图固定尺寸 X = zeros(imgRows * imgCols, numClasses * numPerClass); labels = zeros(numClasses * numPerClass, 1); col = 1; for i = 1:numClasses for j = 1:numPerClass imgPath = fullfile(dataDir, sprintf('s%d/%d.pgm', i, j)); img = imread(imgPath); img = imresize(img, [imgRows imgCols]); % 防御性统一尺寸 X(:, col) = double(img(:)); % 展平成列向量 labels(col) = i; col = col + 1; end end % 后续PCA代码见3.2节

这段代码有两点值得说明。一是X用预分配的zeros而不是循环里动态拼X(:, end+1),数据量大时后者会反复申请内存,慢且容易触发内存碎片。二是imread读进来是uint8,直接做减法运算容易溢出,所以存进X前用double转换。sprintf里%d.pgm这种路径格式依赖ORL库默认的文件名,如果你手里的库是别的命名规则,改成对应格式即可。如果你混入了彩色图,imresize会在后面矩阵运算时报维度错误,建议加一行assert(ndims(img) == 2)。

3.2 训练与测试:PCA投影加最近邻分类的完整Matlab代码

模型的核心结构很简单:均值脸、投影矩阵W、训练样本的投影特征Y和标签。用SVD而不是直接对协方差矩阵做特征分解,数值上更稳、速度也更快,避坑章会展开讲为什么不能用cov加eig。

% 训练PCA模型 meanFace = mean(X, 2); % 均值脸,用于后续中心化 Xc = X - meanFace; % 中心化 [U, S, ~] = svd(Xc, 'econ'); % 经济型SVD,只算有效分量 W = U(:, 1:k); % 取前k个特征向量作为投影矩阵 model.W = W; model.meanFace = meanFace; model.YTrain = W' * Xc; % 训练集投影到特征脸空间,k x N model.trainLabels = labels; end

svd(Xc, 'econ')返回的U是d乘min(d, N)的矩阵,d等于10304,N等于400,这里U就是10304×400。取前k列作为W,每一列对应一个最大方差方向的特征脸。训练样本被投影成k维向量,存成YTrain供测试时算距离。k是最重要的超参数,第4章专门讲怎么定。

测试函数接受一张图像矩阵,投影后和所有训练样本比距离。

% recognizeFace.m - 识别单张人脸 % model: trainFaceModel返回的结构体,img: 112x92灰度图 function label = recognizeFace(model, img) img = imresize(img, [112 92]); % 统一输入尺寸 x = double(img(:)) - model.meanFace; % 中心化 y = model.W' * x; % 投影到特征脸空间 d = sqrt(sum((model.YTrain - y) .^ 2, 1)); % 欧氏距离 [~, idx] = min(d); label = model.trainLabels(idx); % 返回最近邻的标签 end

用欧氏距离做最近邻是特征脸方案最经典的默认设置。sum(..., 1)按列求和,得到当前测试样本与全部400个训练样本的距离向量,min取最近的索引,再映射到标签。调用方式就三行:

% 主脚本 main.m model = trainFaceModel('ORL', 40); testImg = imread(fullfile('ORL', 's1', '2.pgm')); % 拿某人第2张测试 label = recognizeFace(model, testImg); fprintf('识别结果: 第%d号,真实类别: 1\n', label);

提示:第一次运行时建议先打印model.YTrain的尺寸确认投影维度,常见错误是把k设得比训练样本数还大,导致W' * Xc维度对不上。

3.3 扩展:用webcam和CascadeObjectDetector做实时人脸识别

训练完就要演示,演示就得摄像头实时识别。Matlab里摄像头采集用webcam函数,人脸检测用vision.CascadeObjectDetector,这两个函数分属不同工具箱,缺一个都会在运行时报Undefined function。

% liveRecognition.m - 实时识别演示脚本 cam = webcam(1); % 1为设备序号,只有一个摄像头时写1 detector = vision.CascadeObjectDetector('FrontalFaceCART'); model = trainFaceModel('ORL', 40); % 训练一次,后续帧只做预测 for i = 1:300 frame = snapshot(cam); % 采集一帧,uint8彩色图 bbox = detector(frame); % 检测人脸,返回Nx4矩阵 if ~isempty(bbox) % 取面积最大的人脸框,避免把背景误检当主目标 areas = bbox(:, 3) .* bbox(:, 4); [~, bi] = max(areas); face = imcrop(frame, bbox(bi, :)); % 按框裁剪 face = imresize(face, [112 92]); if size(face, 3) == 3 face = rgb2gray(face); % 彩色转灰度 end label = recognizeFace(model, face); % 识别 frame = insertObjectAnnotation(frame, 'rectangle', ... bbox(bi, :), sprintf('ID: %d', label), 'Color', 'green'); end imshow(frame); drawnow; % 强制刷新画面 end

vision.CascadeObjectDetector的构造参数'FrontalFaceCART'是预训练的人脸检测模型,比默认的FrontalFace检测率更高,误检略多。检测器对小于20×20像素的人脸直接忽略,webcam(1)的序号可以执行webcamlist查看。imcrop的bbox坐标必须严格是[x y width height],索引用错会在裁剪后取到空白或越界图像。

这个脚本跑起来后,就能在视频流里框出人脸并标出ID。两个注意点:一是每次循环新建摄像头对象是常见的性能陷阱,正确做法是循环外只开一次;二是在for循环里用imshow加drawnow虽然直观,但速度偏慢,追求流畅度可以用vision.VideoPlayer替代。实时识别对光照的敏感度比静态库大得多,建议在recognizeFace之前加一步adapthisteq直方图均衡,能明显压低侧光和逆光的影响,代价只是每帧多几毫秒。

4. 识别率调优的三个关键参数:降维维数、距离度量、数据划分

代码跑通后,识别率不一定好看,甚至可能出现训练集接近100%、测试集只有60%这种尴尬结果。问题大概率出在三个参数上:PCA保留多少维、最近邻用什么距离、训练测试集怎么划分。这三个参数相互牵连,逐个调完,识别率通常能提升5到10个百分点。

4.1 降维维数的选择:特征值能量比与识别率曲线的结合判断

PCA保留的主成分数k是最敏感的参数。k太小,丢信息太多,不同人脸在低维空间挤成一团;k太大,噪声和光照差异又混进来,等价于没有降维。常见的定法有两种:按特征值能量比,或扫识别率曲线。

SVD求出的奇异值S对应特征向量的能量,能量比直接反映当前k个主成分抓住了原始数据多大的方差比例。

% 按能量比选取k的参考代码 [~, S, ~] = svd(Xc, 'econ'); energy = cumsum(diag(S).^2) / sum(diag(S).^2); k95 = find(energy >= 0.95, 1); % 95%能量 k99 = find(energy >= 0.99, 1); % 99%能量 fprintf('k@95%% = %d, k@99%% = %d\n', k95, k99);

奇异值平方等于特征值,cumsum计算累加占比,find找到第一个超过阈值的索引。ORL上经验值通常是k95在30到50之间,k99在60到100之间。课设建议以识别率曲线为准,能量比例只作为初值参考,因为抓住方差和分类正确并不完全等价。

识别率曲线是更直接的做法:k从5扫到100,每次用固定划分做一遍测试,画识别率随k变化的曲线,曲线峰值对应的k就是最优值。你会发现曲线往往是先涨后跌,峰值后下跌就是过拟合了噪声。

% 扫描k并记录识别率 ks = 5:5:100; accs = zeros(size(ks)); for idx = 1:length(ks) model = trainFaceModel('ORL', ks(idx)); acc = testAll(model, 'ORL'); % 用测试集统计识别率 accs(idx) = acc; end plot(ks, accs, 'o-'); xlabel('降维维数k'); ylabel('识别率'); grid on;

提示:如果跑出来的曲线从某个k之后识别率剧烈震荡,先检查是不是训练集和测试集划分不稳定,见4.3节。

4.2 距离度量对比:欧氏距离、曼哈顿距离、余弦相似度的课设实验

距离度量决定了“最近邻”的“最近”是什么含义。欧氏距离对每个维度的绝对差敏感,适合特征脸空间里方差近似各向同性的情况;曼哈顿距离是各维度绝对差求和,对离群点更鲁棒;余弦相似度只看方向不看模长,对光照造成的整体亮度变化相对不敏感。

课设实验里,把三种距离都跑一遍是性价比极高的内容。实现只需要改recognizeFace里的距离计算。

% 三种距离的最近邻写法,只列距离核心行 diffMat = model.YTrain - y; % y: 测试投影 eucl = sqrt(sum(diffMat.^2, 1)); % 欧氏距离 manh = sum(abs(diffMat), 1); % 曼哈顿距离 cosSim = (model.YTrain' * y) ./ (sqrt(sum(model.YTrain.^2, 1))' * norm(y) + eps); cosDist = 1 - cosSim; % 转成距离:越小越近

cosSim计算中,model.YTrain' * y得到N×1的内积列向量,分母是各训练样本模长乘测试样本模长,加上eps防止除以0。余弦相似度越大越相近,习惯上转成1减cosSim当作距离使用。三种距离里曼哈顿的识别结果往往和欧氏非常接近,但运行时间略慢,因为abs运算比平方慢。

实测经验是:训练样本充足、光照归一化做得好的前提下,欧氏距离已经够了。如果识别率被光照明显压制,优先做adapthisteq而不是换距离度量。把三种距离的对比放进报告的实验结果表,答辩老师会觉得你做的是完整系统而不是单一脚本。

4.3 训练集与测试集划分:留一法和随机划分的利弊取舍

划分策略决定了你报出去的识别率有多少可信度。最不可取的做法是把训练集拿去当测试集,ORL上闭着眼睛都是100%,老师现场拍一张就穿帮。课设一般用两种划分:固定比例随机划分,或者留一法。

留一法的逻辑是每人10张里轮流取1张做测试、剩余9张训练,循环10次,最后平均。样本利用率最高,评估偏差小,代价是训练时间约为固定划分的10倍。ORL规模下训练PCA很快,这个代价完全可接受。

% 留一法评估框架,结构清晰优先,函数细节省略 function acc = leaveOneOut(dataDir, k) numClasses = 40; numPerClass = 10; hit = 0; total = 0; for i = 1:numClasses for testIdx = 1:numPerClass trainIdx = setdiff(1:numPerClass, testIdx); % 组装训练矩阵Xtr与标签,训练模型 model = trainFaceModelIdx(dataDir, k, i, trainIdx); % 取当前测试图识别 pred = testFaceModelIdx(model, dataDir, i, testIdx); hit = hit + (pred == i); total = total + 1; end end acc = hit / total; end

trainFaceModelIdx接受一个具体的人和一批训练索引,避免每次重新读全库。留一法跑完ORL需要400次训练,每次SVD在10304×360的规模上约零点几秒,整体一两分钟能跑完,课设完全扛得住。

随机划分,比如每人7张训练、3张测试,速度快代码简单,但小样本下每次划分结果波动可能达到两三个百分点。建议报告里以留一法作为最终识别率,随机划分用来展示划分不同结果有波动这个现象。两种都写进实验章节,也是加分点。

5. Matlab人脸识别课设避坑指南:五个翻车现场与对应修法

代码跑通只是开始,答辩前你一定会撞上几个坑。这一章写五个翻车现场,每条都按现象、原因、解决给修法,对照自己的代码逐条查,能省下大半天调试时间。

5.1 翻车现场一:图像尺寸不一致导致矩阵拼接报错

现象:运行trainFaceModel时,Matlab报Error using vertcat或dimensions must agree,断点停在对X(:, col)赋值的行。

原因:ORL库本身是整齐的112×92,但如果你把自拍图或网络图混进来,imresize只写在某个分支里,或者整库循环里只有部分样本被resize,维度不一致在拼接瞬间就炸了。

解决:在赋值前强制统一尺寸,并把类型检查写进读取函数。最常用的写法是3.1节里的imresize(img, [112 92]),同时加一句断言:

img = imread(fullfile(dataDir, sprintf('s%d/%d.pgm', i, j))); assert(ndims(img) == 2, '输入不是灰度图'); % 防止彩色图混入 img = imresize(img, [112 92]);

这样不管原始图是彩色、还是尺寸不齐,都会在训练前被纠正并明确报错。如果你用视频帧做训练集,记得先抽帧再走同一套预处理函数,不要边读边处理。

5.2 翻车现场二:大图训练时内存溢出,协方差矩阵算不动

现象:换成自拍的高清图库后,trainFaceModel直接内存不足,或者SVD算到一半Matlab卡死。

原因:如果走cov(X)加eig这条路,中间会产生d×d矩阵。d是展平后的像素数,1920×1080灰度图展平约207万维,207万乘207万乘8字节约3.4TB,任何普通电脑都扛不住。

解决:不要对d×d矩阵做特征分解,改用转置技巧或直接svd(Xc, 'econ')。svd的econ模式只生成d乘min(d, N)的结果,内存占用大幅下降。更省的做法是先算小矩阵Xc' * Xc,维度是N×N,几个MB,再做特征分解,把特征向量映射回原空间:

% 小样本PCA替代写法,N远小于d时用 G = Xc' * Xc; % N x N,N是样本数 [V, D] = eig(G); % 对NxN矩阵求特征分解 [~, order] = sort(diag(D), 'descend'); V = V(:, order); % 特征向量U = Xc * V * diag(1 ./ sqrt(diag(D) + eps)),注意特征值太小的列会放大噪声 U = Xc * V * diag(1 ./ sqrt(diag(D) + eps)); W = U(:, 1:k);

这段代码在N远小于d时非常重要,课设用ORL的话svd的econ模式就够了,但报告里能把两种做法的内存差异写清楚,属于原理加分项。实际使用建议只保留特征值大于某个小阈值的列,比如1e-6,否则数值噪声会被放大。

5.3 翻车现场三:摄像头采集的彩色帧直接喂给分类器

现象:实时识别脚本一运行就报错,常见的是Subscript dimensions mismatch或者矩阵维度对不上,识别率奇低。

原因:webcam的snapshot返回的是height×width×3的RGB uint8矩阵,而训练时的X是H×W的灰度向量。三维矩阵直接展平后维度是灰度图的3倍,和投影矩阵W的维度完全对不上,矩阵乘法第一步就垮了。

解决:按固定顺序处理:先裁剪、再灰度化、再resize,最后转double。顺序反了会导致rgb2gray作用在错误维度上,或者resize把颜色通道搅乱。

face = imcrop(frame, bbox(bi, :)); face = rgb2gray(face); % 先转灰度 face = imresize(face, [112 92]); % 再resize face = double(face(:)); % 最后展平

如果摄像头拍出来的人脸偏暗,可以在imresize后、向量化前加face = adapthisteq(face);。训练端也要做同样的处理,两边一致才不会出现特征空间偏移。

5.4 翻车现场四:同一个人两次识别结果不一样,光照和对齐问题

现象:同一个人站在摄像头前,稍微转个头或者换一盏灯,识别结果就在两个ID之间来回跳,甚至认错人。

原因:课设阶段的系统通常没有做严格的关键点对齐,人脸在框里的上下位置和旋转角度一变,特征向量就跟着变;PCA对全局亮度变化也敏感,光照一变投影坐标就跑偏。

解决:分两步。第一步做光照归一化,推荐adapthisteq,训练样本和测试样本都要加,两边一致性是关键,只给测试端加反而会拉低识别率。第二步做简单的对齐:用检测器拿到的人脸框尽量统一到同一位置和比例,比如以两眼间距为基准把脸缩放到固定尺寸再裁剪,比想象中能显著提升稳定性。如果时间不够,最低限度是框出的人脸区域要包含完整面部,不要带额头外过多的背景。

5.5 翻车现场五:课程设计报告里识别率100%反而被质疑

现象:报告写“ORL库识别率100%”,答辩老师一问数据划分,答不上来;或者老师当场拿一张库外照片测试,系统给出一个错误ID且没有拒绝能力。

原因:识别率100%往往意味着训练测试同源,或者只挑了最容易的样本做测试。另一个隐患是最近邻分类器没有拒识机制,无论来什么脸都会硬归到40个人之一,库外脸出结果只能是错。

解决:报告里识别率必须标注划分方式,“留一法平均识别率XX%”比“识别率98%”可信得多。同时给系统加一个距离阈值,当最近邻距离超过阈值时输出未注册人员:

[minDist, idx] = min(d); if minDist > threshold label = -1; % -1表示未注册 else label = model.trainLabels(idx); end

阈值怎么定?用库内样本距离分布来标定:统计所有正确匹配的最小距离,取95%分位数作为阈值。这一步写进报告,系统的完整性立刻上一个台阶,答辩时主动演示拒识,比被动被老师考倒强得多。

6. 用App Designer封装识别演示:答辩现场不翻车的加分做法

答辩最怕的不是算法讲不清,而是现场演示翻车。把系统封装成带按钮的图形界面,一是操作可重复,二是看起来像系统实现而不是脚本运行。App Designer在Matlab r2016a之后都有,布局和回调比老版GUIDE友好得多。

一个最小可用的界面只需要三个组件:一个坐标轴显示视频帧,一个“开始识别”按钮,一个文本框显示结果。回调代码的核心是把摄像头采集、检测、识别串起来:

% App Designer回调骨架,实际开发时写在对应回调函数中 function StartButtonPushed(app, event) model = load('faceModel.mat'); % 训练好的模型存成mat detector = vision.CascadeObjectDetector('FrontalFaceCART'); cam = webcam(1); for i = 1:200 frame = snapshot(cam); bbox = detector(frame); if ~isempty(bbox) [~, bi] = max(bbox(:,3) .* bbox(:,4)); face = imresize(rgb2gray(imcrop(frame, bbox(bi,:))), [112 92]); [label, minDist] = recognizeFaceWithDist(model, face); if minDist > model.threshold text = '未注册人员'; else text = sprintf('识别为第 %d 号成员', label); end app.ResultLabel.Text = text; frame = insertObjectAnnotation(frame, 'rectangle', ... bbox(bi,:), text, 'Color', 'yellow'); end imshow(app.UIAxes, frame); drawnow; end end

recognizeFaceWithDist是第5章加了阈值版本的识别函数,返回label和最近邻距离两个值。启动前把模型、模型阈值、摄像头对象都在startupFcn里初始化好,不要在每次按钮回调里重复加载,否则按钮响应会有肉眼可感的延迟。

封装完界面之后,答辩演示的顺序建议固定成三步:第一步演示训练集内识别和识别率统计,这步必然成功;第二步演示摄像头实时识别,顺便说明光照归一化处理;第三步主动演示拒识效果,拿一张不在库里的照片,系统显示未注册人员。我的教训是当年只演示了前两步,老师随手递过来一张照片,系统硬认成库里的某个人,场面一度很尴尬。后来把阈值拒识加进去,同样的场景反而成了亮点。识别系统做到能演示、能解释、能应对反例,才算真正闭环,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询