news 2026/9/11 16:37:13

基于小波矩的MATLAB人脸识别算法源码与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于小波矩的MATLAB人脸识别算法源码与实现

简介:这是一份基于MATLAB的小波矩人脸识别算法项目源码,适合计算机、电子信息等专业学生用于课程设计、期末大作业或毕业设计参考。项目实现了从人脸检测、分割到小波矩特征提取与匹配的完整流程,包含YCL.m、Humoment.m、detectFaceParts.m等多个核心脚本,并配有README说明,便于理解算法思路和运行方式。压缩包共30个文件,以MATLAB的.m源文件为主,辅以bmp、jpg格式的测试人像和.mat格式的预存特征数据,整体大小仅1.44MB,轻量易用。目前已有56人学习下载,可作为入门级人脸识别算法研究的参考资料。资源注重动手实践,代码结构清晰、注释与说明到位,适合能看懂代码并愿意自行调试的读者在此基础上扩展功能。

1. 为什么人脸识别要用小波矩:多尺度形状特征的取舍

做了一整年 MATLAB 人脸识别的离线项目,我慢慢发现,真正能被复现和调试的往往不是深度学习那套端到端模型,而是像小波矩这样特征明确、依赖少的算法。小波矩把人脸图像变换到极坐标,在径向方向用小波基做多分辨率分解,在角向方向再保留分量信息,最终得到一组同时对平移、旋转、尺度变化不敏感的特征。它与传统 Hu 矩最大的差别是:Hu 矩只能给出全局平均量,而小波矩能捕捉眼睛边缘、下颌曲线这些局部的细节差异。这个题目里的“源码+项目说明”,指的是一套完整的 MATLAB 实现,适合小样本、算力有限或需要把识别过程拆给其他模块解释的教学场景。如果你不想被显卡和设备驱动绑架,想在一台普通笔记本上把人脸识别跑起来,这条路值得试。

2. 小波矩的数学结构与MATLAB离散化

2.1 从几何矩到小波矩:两个缺陷的补法

人脸识别本质上是在做形状描述。最早大家用几何矩,比如图像的零阶面积矩、一阶重心矩和二阶中心矩,再由 Hu 在 1962 年提出 7 个不变矩。这些矩的优点是计算简单,但存在两个明显的坏毛病:第一,它是全局积分,把整张脸的平均灰度和轮廓“揉”成一团,对鼻子、嘴、颧骨这些局部组织的辨识力不够;第二,高阶矩容易受噪声和量化误差影响,实际工程中经常出现同一个人的两个样本比不同人的样本还远的情况。

小波矩的思路是用小波基替换几何矩里的多项式核,步骤如下:先把原始灰度图做极坐标变换,得到 f(r,θ),其中径向坐标 r 归一化到 0 到 1,角向坐标 θ 取 0 到 2π。接着在 r 方向用小波族 ψ_m,n(r) = 2^(m/2) ψ(2^m r − n) 分解,在 θ 方向保留傅里叶核 e^(−jkθ)。于是小波矩写成:

F_mnk = ∫∫ f(r,θ) ψ_m,n(r) e^(−jkθ) r dr dθ

这个公式看起来复杂,实际只做了两件事:径向小波变换取多尺度局部特征;角向傅里叶分解取圆上谐波特征。旋转时,图像在 θ 方向平移,会让 F_mnk 的相位乘上 e^(jkα),但幅度不变,所以取绝对值abs(coeff)就能获得旋转不变性。平移不变性靠预处理阶段把脸的中心搬到极坐标原点实现;尺度不变性靠对径向坐标归一化实现。

2.2 小波基函数与矩的离散化实现

选择径向小波就像选择特征粒度的放大镜。不同基函数生成的矩向量分布完全不同,直接影响最终识别率。下表给出我在 MATLAB 里用过的三种小波基:

基函数MATLAB名称波形特点适用场景
Haarhaar分段常数,有跳变快速原型、光照均匀的证件照
Mexican Hatmexh平滑,带通特性脸部轮廓和眼睛区域的分层描述
Morletmorl振荡明显,频域局部好纹理较多、有表情变化的人脸表面

实际项目里我更倾向于 Mexican Hat,原因是它对重采样噪声不敏感,而且和小波矩的积分核配合时不需要额外做滤噪。Haar 虽然快,但径向基在 r 轴上有阶跃,会在极坐标网格的边界产生伪高峰。离散化时,把积分替换成累加:先对图像做极坐标重采样,得到一个 Nr × Ntheta 的矩阵,然后对每个 m、n、k 做加权求和。权重必须包含雅可比因子 r,否则小波矩在原点附近会被过分放大。exp(−1jkθ) 的计算可以用 MATLAB 的复数广播完成,不需要逐个像素循环。

2.3 构造特征向量的尺度与维数选择

特征向量不是把所有 m、n、k 的系数全塞进去就好。维度膨胀会让分类器的正则化非常难受。常见做法是限制尺度 m 不超过 3,位移 n 不超过 2,k 取 0 到 3,这时特征个数为 4×3×4 = 48 维。在 ORL 这种 92×112 的人脸图上,48 维已经能支撑 5 类以上的分类。如果你只区分两个人,m 到 2、n 到 1 就够了。

我一般把特征组织成行向量,形式如下:

function Feat = waveletMoments(polarIm, mMax, nMax, kMax) % waveletMoments 小波矩离散化计算 % 输入:polarIm - 极坐标重采样后的图像,尺寸 Nr x Ntheta % mMax, nMax, kMax - 尺度、位移、角频率上限 % 输出:Feat - 行向量,每段按 m-n-k 次序排列 [Nr, Nth] = size(polarIm); r = (0:Nr-1)' / Nr; % 径向坐标归一化到 [0,1) theta = (0:Nth-1) / Nth * 2 * pi; % 角向坐标 [R, TH] = meshgrid(r, theta); % 生成坐标网格 polarIm = polarIm'; % 调整成 Nr x Nth 对齐 rWeight = repmat(r(:), 1, Nth); % 雅可比权重 r Feat = []; for m = 0:mMax for n = 0:nMax % 用 mexh 母小波生成径向基 psi = 2^(m/2) * wavefun('mexh', 8); psi = interp1(linspace(0, 1, length(psi)), psi, r, 'linear', 0); psi = psi(:); for k = 0:kMax % 角向傅里叶核:与 theta 相关 basis = repmat(psi, 1, Nth) .* exp(-1j * k * theta); coeff = sum(sum(polarIm .* basis .* rWeight)); Feat = [Feat, abs(coeff)]; % 取模消除旋转相位 end end end end

这段代码的核心是先用wavefun生成母小波在连续轴上的近似值,再用interp1采样到径向网格上。rWeight对应公式里的 r dr 权重;abs(coeff)去掉复相位,得到旋转不变特征。有一个容易错的地方:Nth采样点如果太少,高频角向分量会相互混叠,建议Nth至少取 64。

3. 在MATLAB中搭一条能跑通的小波矩特征提取管线

光有核心公式还不够。我接触不少基于这个思路的项目,问题大多出在特征提取前的人脸归一化和极坐标变换上。这一章给出一条能在普通电脑上跑通的管线,并且每一步都留参数检查点。

3.1 人脸检测与预处理:从 RGB 到极坐标网格

先读取图片,用 MATLAB 自带的级联检测器定位人脸。读取后转为灰度,截取人脸框,还要把两只眼睛对齐到固定位置,否则后续小波矩对左右脸的旋转变化会混入特征。最保险的预处理不做缩放,直接以两眼连线作为水平参考,围绕人脸中心裁剪一个正方形区域。

function [gray, box] = preprocessFace(imgPath) % 返回对齐后的灰度图和人脸框位置 detector = vision.CascadeObjectDetector('FrontalFaceCART'); img = imread(imgPath); box = step(detector, img); if isempty(box) error('未检测到人脸'); end % 使用第一张脸 face = imcrop(img, box(1,:)); gray = rgb2gray(face); gray = imresize(gray, [128, 128]); % 统一尺寸 end

这里只做尺寸统一还不够。人脸检测给出的框是矩形,但眼睛不一定水平。更严谨的做法是把左眼和右眼位置作为参照,计算旋转变换。我一般会在项目说明里附一个alignFace.m脚本,内部用fitgeotrans生成相似变换矩阵,把两只眼睛映射到固定位置。检测框最好外扩 20%,以免脸贴边时极坐标圆缺了一块,影响后面的角向分解。

3.2 极坐标变换与重采样参数

得到 128×128 灰度图后,下一步是把笛卡尔坐标网格映射到极坐标。这里不能直接调用cart2pol就算完,还要用interp2把像素值搬到规则极坐标网格上,否则后续矩阵运算是错位的。常见做法是建立两套网格:一套是笛卡尔坐标的meshgrid,另一套是径向采样点 r 和角向采样点 θ。

function polarIm = convertToPolar(gray, center, radius, Nr, Nth) % 将128x128灰度图转换为Nr x Nth的极坐标图 x = 1:size(gray,2); y = 1:size(gray,1); [xx, yy] = meshgrid(x, y); % 生成极坐标采样点 r = linspace(0.1, 1, Nr) * radius; % 跳开中心奇异点 theta = linspace(0, 2*pi, Nth+1); theta(end) = []; % 去掉端点,避免重复 [TH, R] = meshgrid(theta, r); % 转换到笛卡尔坐标 px = center(1) + R .* cos(TH); py = center(2) + R .* sin(TH); % 双线性插值,边界填充0 polarIm = interp2(xx, yy, double(gray), px, py, 'linear', 0); end

这里的r从 0.1 而不是 0 开始,是为了避开光轴上的单像素点,否则插值会在中心产生异常高值。Nth+1去掉最后一个点,是为了保证 0 和 2π 不会重复计入,否则再做角向傅里叶时会产生直流偏置。这个点我排错排了整整一个下午,最后用sum(abs(diff(polarIm,[],2)))检查各行边界才发现。

3.3 验证平移、旋转、尺度不变性的参数表

特征提取之后不要急着送分类器,先做一个不变性自检:把同一张脸旋转 30°、平移 5 像素、缩放 0.8 倍,再分别提取小波矩,看两两之间的欧氏距离。我常用下面这组参数配合自检:

检查项输入改动允许特征距离需要微调的参数
旋转不变性顺时针旋转 30°< 0.15kMax是否过高,Nth是否足够
平移不变性上下移动 5 像素< 0.20center是否真的在质心,检测框是否偏移
尺度不变性缩放 0.8 倍< 0.25半径radius是否包含整个脸

如果旋转误差过大,把角向采样Nth从 64 提到 128;如果平移误差过大,说明预处理阶段的人脸中心校准不稳定,应该在imcrop前先做眼睛对齐。如果尺度误差大,确认radius是根据人脸框一半宽度算出来的,而不是固定 64。

4. 识别引擎:特征训练、分类与效果验证

特征提取只是把每个人的脸转成向量。接下来需要做的是训练分类器和识别流程。在 MATLAB 里,常见的选择是把小波矩特征放到支撑向量机或者 K 近邻里。不要一上来就上深度网络,小样本任务中这些经典分类器往往更稳。

4.1 小样本下的分类器选型

我一般会把项目里的样本做划分:每个人取 4 张做训练,2 张做验证。当每人的样本数少于 6 时,RBF 核 SVM 容易过拟合,优先用线性 SVM 或者 KNN。KNN 对小波矩这种低维特征非常友好,因为特征本身已经有旋转不变性,距离计算稳定。但 KNN 需要保存全部训练向量,预测时复杂度是 O(N),在门禁这种几千人的场景会慢。线性 SVM 保存的是支持向量,输出只是符号函数,速度快很多。

实际项目里我偏好线性 SVM,配合fitcsvmpredict。多分类问题用fitcecoc把二分类扩展成“一对一”或“一对多”。这个题目中的“算法源码+项目说明”一般用于学生项目或算法验证,所以可以放一个简单的一对一 ECOC 做说明,训练时间和内存都可控。

4.2 特征维数与交叉验证参数设置

48 维小波矩在人脸库上并不是直接最好用。有的维度受噪声影响大,需要先用主成分分析把维度压到 20 到 30。PCA 虽然会损失一点可解释性,但能明显稳定 SVM 的间隔,避免高维空间的退化。交叉验证我建议用cvpartition,不要手动打乱样本,否则会出现同一个人的不同照片同时落进训练集和验证集,导致评估虚高。

参数建议值说明
mMax2~3超过 3 时小波基频率过高,容易混入噪声
nMax1~2代表径向位移,太大特征维数爆炸
kMax0~3角向分量,0 是整体轮廓,1~3 是边缘梯度
PCA 保留贡献率0.95低于 0.9 丢失局部细节,高于 0.98 噪声多
CV 折数5样本少时用留一法

4.3 一个可运行的训练识别脚本框架

这里给出一个完整的脚本骨架,可以直接放到项目根目录运行。它假设你在features.mat里已经存放了小波矩特征和对应标签。

load('features.mat'); % featMat: Nxd, labels: Nx1 rng(42); % 5折交叉验证 cv = cvpartition(labels, 'KFold', 5); acc = zeros(cv.NumTestSets, 1); for i = 1:cv.NumTestSets trIdx = cv.training(i); teIdx = cv.test(i); % 先用训练集做PCA,再把测试集映射过去 [coeff, ~, ~] = pca(featMat(trIdx,:)); trX = featMat(trIdx,:) * coeff(:,1:20); teX = featMat(teIdx,:) * coeff(:,1:20); mdl = fitcecoc(trX, labels(trIdx), ... 'Learners', templateSVM('KernelFunction','linear')); pred = predict(mdl, teX); acc(i) = sum(pred == labels(teIdx)) / length(pred); end fprintf('平均识别率: %.2f%%\n', mean(acc)*100);

这段代码的要点有三个:pca的变换矩阵只由训练集计算,不能在测试集上重新算;templateSVM里明确用线性核,避免 RBF 在小样本上过拟合;fitcecoc自动处理多分类。如果你的标签是字符串而不是数字,先做grp2idx转成索引。另外,SVM 对特征尺度敏感,小波矩各维度范围差异不大,但如果做了多分辨率拼接,建议先做zscore

5. 源码调试技巧:三个容易翻车的细节

很多项目说明只写了运行顺序,没有写参数为什么这样给,这是最让人头疼的。真正处理问题时,以下三个点最容易被忽略,也最能解释“为什么照着代码跑识别率总比别人低”。建议先在调试前设定一个简单基线:把一张正面照旋转 90°,要求识别结果不变;选两个人各 5 张图,要求训练准确率 100%。

5.1 重采样导致的对齐错误

极坐标变换中心如果不设在脸的质心,而是设在裁剪框的几何中心,那么同一张脸在旋转后重采样坐标系会发生偏移。最简单的检查方法是显示polarIm,看脸上两只眼睛是否在极坐标图的同一水平线上。如果不是,应该用center = [xEyeMean, yEyeMean],而不是center = size(gray)/2

5.2 特征维度过高时的降维策略

小波矩特征的维数会随着nMax增大而线性上升。我见过有人把 m、n、k 全调到 4,特征直接到 125 维,而验证集只有 20 张,SVM 变成无解问题。建议在写完特征函数后立刻打印特征维数:size(featMat, 2)。如果大于 60,就用 PCA 或 LASSO 筛特征。检查筛选后的特征矩阵是否出现全零列,部分小波基在位移 n 超出径向范围时会返回全零。

5.3 用旋转等价性验证源码是否正确

一个最有效的验证手段是“旋转重建检查”:把一张图旋转 45° 后再提取特征,看它和原图的特征余弦相似度是否大于 0.95;如果只有 0.7,问题多半在角向采样不均匀。

img1 = preprocessFace('face1.jpg'); img2 = imrotate(img1, 45, 'bilinear', 'crop'); p1 = convertToPolar(img1, [64 64], 50, 64, 64); p2 = convertToPolar(img2, [64 64], 50, 64, 64); f1 = waveletMoments(p1, 2, 1, 2); f2 = waveletMoments(p2, 2, 1, 2); cosSim = dot(f1, f2) / (norm(f1) * norm(f2)); fprintf('旋转前后小波矩余弦相似度: %.4f\n', cosSim);

如果相似度低于 0.9,先检查imrotatecrop参数是否保持了图像尺寸,再检查convertToPolar里的半径是否给得过大,导致旋转后部分背景被拉入。另外,小波矩的取模会丢失相位信息,不同旋转角度的特征距离理论上不应当为零,所以阈值不要期望等于 1。这个检查通过之后,再接分类器做交叉验证,得到的结果才值得写进项目说明。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 16:36:19

Android串口调试实战:权限处理、JNI封装与收发链路解析

简介&#xff1a;面向Android串口通信开发场景&#xff0c;这份实例项目基于Android Studio构建&#xff0c;并已通过实际测试&#xff0c;适合物联网应用开发者、硬件交互工程师&#xff0c;以及有一定Android基础的学习者参考。项目完整实现了串口参数设置、打开与关闭、数据…

作者头像 李华
网站建设 2026/9/11 16:34:14

curl_escape 详解:libcurl 中 URL 编码的遗留接口与正确替代方案

curl_escape 详解&#xff1a;libcurl 中 URL 编码的遗留接口与正确替代方案 【免费下载链接】curl A command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQ…

作者头像 李华
网站建设 2026/9/11 16:32:30

Qt SVGViewer解析:QSvgRenderer与QGraphicsView构建可交互视口

简介&#xff1a;基于Qt框架的SVG查看器示例工程&#xff0c;面向需要学习Qt图形视图框架、SVG渲染与交互开发的C开发者。项目通过QSvgRenderer、QSvgWidget、QGraphicsScene/View等核心组件&#xff0c;演示了从加载SVG文件到缩放、平移显示的关键流程&#xff0c;同时涉及信号…

作者头像 李华