简介:这份资源聚焦虹膜识别中的特征提取环节,面向正在学习生物特征识别、图像处理或准备相关课程设计的学生与开发者,帮助理解如何用2D-Gabor滤波器从归一化虹膜图像中提取二进制特征编码。压缩包共10个文件,约939KB,包含6个m脚本、2个txt说明、1个mat数据文件和1个bmp示例图像,脚本覆盖Gabor滤波器构造、编码生成与系数计算等核心流程,mat与bmp文件则提供可运行的实验数据与样本。已有965人学习下载,说明其在入门与实操层面具有一定参考价值。读者可借助这些代码与数据,完整走通从滤波器设计、响应图计算到特征编码输出的链路,并对照说明文档理解各步骤参数含义,为后续模板匹配与识别实验打下基础。
1. 从一张眼睛照片到身份判定:matlab虹膜识别到底在做什么
你手头有一张人眼特写,瞳孔周围那圈像纹理一样的环状结构,就是虹膜。matlab虹膜识别要干的事,是把这张图变成一串可比较的特征码,再拿它跟库里已有的码做距离比对,判断“是不是同一个人”。它不依赖深度学习大模型,核心链路是定位、归一化、特征提取、匹配四步,全程用 MATLAB 的矩阵运算和图像工具箱就能跑通。适合谁?做课程设计的学生、想快速验证生物特征算法可行性的工程师、以及需要在嵌入式或 PC 端做轻量身份核验的开发者。我见过太多人一上来就调imfindcircles然后发现瞳孔边缘根本找不准,这篇就把这条链路拆开,把参数和翻车点讲清楚。
2. 虹膜定位:先找到圆,再谈特征
2.1 为什么直接对整图做边缘检测会翻车
虹膜定位是整个流程里最玄学的一步。很多人拿到眼睛图,第一反应是edge加imfindcircles,结果要么找到睫毛的弧线,要么把眼睑当成圆。原因在于虹膜外边界和巩膜(眼白)的灰度差并不总是明显,尤其是深色虹膜。常见做法是分两步:先定位瞳孔,因为瞳孔和虹膜的灰度差通常最大;再以瞳孔圆心为基准,向外做径向梯度搜索找外边界。MATLAB 里可以用imbinarize配合regionprops先粗定位瞳孔,再用imfindcircles限定半径范围精修。
% 读入并转灰度 img = imread('eye_sample.jpg'); gray = rgb2gray(img); % 瞳孔粗定位:自适应二值化后取最大暗区 bw = imbinarize(gray, 'adaptive', 'Sensitivity', 0.5); bw = ~bw; % 反转,让暗区变白 bw = bwareaopen(bw, 50); % 去掉小噪点 stats = regionprops(bw, 'Centroid', 'Area', 'BoundingBox'); [~, idx] = max([stats.Area]); pupilCenter = stats(idx).Centroid; % 以瞳孔中心为基准,限定半径搜索虹膜外圆 rMin = 60; rMax = 120; % 根据图像分辨率调整 [centers, radii] = imfindcircles(gray, [rMin rMax], ... 'ObjectPolarity', 'dark', 'Sensitivity', 0.92, ... 'EdgeThreshold', 0.1);这段代码的逻辑是:先通过二值化把最暗的瞳孔区域抠出来,拿到一个粗略圆心;再用imfindcircles在限定半径范围内找外圆。参数说明:Sensitivity调到 0.9 以上能提高检出率,但会引入假圆;EdgeThreshold降低到 0.1 是为了容忍模糊边缘。如果imfindcircles返回多个圆,按圆心与瞳孔中心的距离排序,取最近的那个。
2.2 用径向梯度替代霍夫变换的实操
霍夫变换在虹膜外边界上经常失灵,因为外边界不是完整圆,上边被眼睑遮、下边被下眼睑挡。我一般会改用径向梯度法:从瞳孔中心出发,沿 360 个角度方向各取一条射线,计算灰度沿射线的梯度,梯度最大值对应的半径就是边界。MATLAB 里用interp2做极坐标采样,再对每一列求diff找峰值。
angles = linspace(0, 2*pi, 360); rRange = 60:120; [theta, rho] = meshgrid(angles, rRange); % 极坐标转笛卡尔,采样 x = pupilCenter(1) + rho .* cos(theta); y = pupilCenter(2) + rho .* sin(theta); sampled = interp2(double(gray), x, y, 'linear', 0); % 沿半径方向求梯度 gradMag = abs(diff(sampled, 1, 1)); [~, maxIdx] = max(gradMag, [], 1); outerRadii = rRange(maxIdx); outerRadius = median(outerRadii); % 取中位数抗噪这里的关键是interp2的采样精度,'linear'够用,追求更稳可以换'cubic'。median比mean更抗噪,因为个别角度会被睫毛干扰。得到外半径后,虹膜环带就是瞳孔圆和外圆之间的区域。
3. 归一化与增强:把环带拉成矩形
3.1 橡皮片模型:从极坐标到矩形展开
虹膜在不同人、不同拍摄距离下大小不一,直接比对像素没意义。Daugman 的橡皮片模型把虹膜环带沿半径方向线性拉伸、沿角度方向展开成固定尺寸的矩形。MATLAB 实现就是构造一个 64×512 的网格,行对应半径(从瞳孔边缘到外边缘),列对应角度(0 到 2π),再用interp2从原图采样。
normW = 512; normH = 64; thetaNorm = linspace(0, 2*pi, normW); rNorm = linspace(pupilRadius, outerRadius, normH); [thetaGrid, rGrid] = meshgrid(thetaNorm, rNorm); xNorm = pupilCenter(1) + rGrid .* cos(thetaGrid); yNorm = pupilCenter(2) + rGrid .* sin(thetaGrid); irisNorm = interp2(double(gray), xNorm, yNorm, 'linear', 0); irisNorm = uint8(irisNorm);参数选择:宽度 512 是经验值,能保留足够角度细节;高度 64 对应半径方向,再高会引入插值模糊。pupilRadius需要从瞳孔二值化结果里用regionprops的EquivDiameter除二得到。这一步做完,你得到一张横向纹理图,后续特征提取都在它上面做。
3.2 用 CLAHE 和直方图均衡把纹理拉出来
归一化后的图往往对比度低,尤其是深色虹膜。直接做全局直方图均衡会放大噪声,我一般用 CLAHE(限制对比度自适应直方图均衡)。MATLAB 里adapthisteq就是干这个的。
irisEnhanced = adapthisteq(irisNorm, ... 'ClipLimit', 0.02, ... 'NumTiles', [8 8], ... 'Distribution', 'uniform');ClipLimit控制对比度增强上限,0.02 比较温和;NumTiles设成 8×8 意味着把图分成 64 块各自均衡,能保留局部纹理。如果发现增强后出现块状伪影,把NumTiles降到 4×4。这一步之后,虹膜的卷缩纹理(crypts)和放射状纹路会明显很多。
4. 特征提取与匹配:Gabor 滤波和汉明距离
4.1 用 Gabor 滤波器组提取相位特征
虹膜特征的核心是纹理的相位信息,Daugman 用 2D Gabor 滤波器组来提取。MATLAB 没有内置 Gabor 函数,但可以用imgaborfilt(R2016b 之后)或者自己构造。我一般用 4 个尺度、6 个方向共 24 个滤波器,对归一化图滤波后取实部和虚部的符号,量化为二进制码。
wavelength = [4 8 16 32]; orientation = 0:pi/6:pi-pi/6; featureCode = []; for w = wavelength for o = orientation gaborBank = gabor(w, o); [mag, phase] = imgaborfilt(irisEnhanced, gaborBank); % 取相位符号量化为1比特 bitPlane = phase >= 0; featureCode = [featureCode, bitPlane(:)']; end end featureCode = logical(featureCode);wavelength选 4 到 32 覆盖不同粗细纹理,orientation每 30 度一个方向。每个滤波器输出一个相位图,取符号得到 1 比特。最终特征码长度是 64×512×24,实际用的时候会降采样,常见做法是每 8×8 块取一个代表值,压到 2048 比特左右。注意imgaborfilt要求输入是灰度图且为 double 类型。
4.2 汉明距离匹配与旋转对齐
两个虹膜码比对用归一化汉明距离:异或后数 1 的个数,除以总位数。但虹膜在归一化时角度起点可能不一致,直接比会偏高。常见做法是把其中一个码循环移位若干列,取最小距离。
function dist = hammingDist(code1, code2) minDist = 1; for shift = -10:10 shifted = circshift(code2, shift, 2); d = sum(xor(code1, shifted), 'all') / numel(code1); if d < minDist minDist = d; end end dist = minDist; endshift范围取 ±10 列,对应角度偏移约 ±7 度,覆盖大多数头部倾斜。判定阈值一般设 0.32 到 0.35,低于此判为同一人。这个阈值不是绝对的,得根据你的数据集做 ROC 曲线调。如果误拒率高就往上调,误识率高就往下调。
5. 避坑与排查:那些让我重跑一整天的细节
5.1 瞳孔定位偏移导致特征码全错
现象:匹配距离普遍在 0.45 以上,同人不同图也对不上。原因:瞳孔二值化时把反光点当成了瞳孔一部分,圆心偏了。解决:在二值化前先做imfill填孔,或者用strel('disk', 3)做形态学闭运算。更稳的做法是用imfindcircles在瞳孔附近小范围搜索,半径限制在 20 到 50 像素。
5.2 归一化宽度选太小导致纹理混叠
现象:不同人的特征码区分度低,汉明距离分布重叠。原因:normW设成 128 或 256,角度方向采样不足,高频纹理被混叠。解决:宽度至少 512,如果原图分辨率高可以到 1024。代价是特征码变长,匹配变慢,但准确率提升明显。
5.3 Gabor 滤波后直接取模值丢失相位
现象:同人不同图的距离波动大。原因:只用了幅值,幅值对光照敏感。解决:一定用相位符号,相位对光照变化更鲁棒。如果imgaborfilt返回的相位在边界处跳变,可以先做unwrap再取符号。
5.4 匹配时忘记做旋转对齐
现象:同人距离 0.4,不同人 0.45,阈值没法设。原因:归一化时角度起点是图像坐标的 0 度,但每次拍摄头部旋转不同。解决:加circshift循环移位,取最小距离。移位范围根据你的采集条件定,一般 ±10 列够用。
5.5 用 RGB 三通道分别提特征再拼接
现象:特征码维度爆炸,匹配慢且没提升。原因:虹膜纹理在灰度上已经足够,三通道信息冗余。解决:统一转灰度,rgb2gray之后再做后续处理。如果原图是近红外,本身就是单通道,省去这步。
6. 进阶技巧:把等错误率压到 2% 以下的三个习惯
第一个习惯是给归一化图做掩膜。眼睑和睫毛遮挡的区域不该参与匹配,否则噪声会拉高距离。做法是在归一化图上根据灰度阈值生成一个二值掩膜,匹配时只统计掩膜为 1 的位。MATLAB 里就是mask = irisNorm > threshold,然后sum(xor(code1, code2) & mask) / sum(mask)。这个改动通常能把等错误率降 1 到 2 个百分点。
第二个习惯是特征码降采样时用块均值而不是块中心。64×512 的相位图直接每 8×8 取一个点,会丢掉块内的一致性信息。改成对每个 8×8 块取符号的多数投票,抗噪能力更强。代码上就是reshape成[8, 64, 8, 64]再对第 1、3 维求均值,最后> 0.5量化。
第三个习惯是匹配时同时算掩膜距离。如果两张图的掩膜重叠率低于 60%,直接判为不可比,不输出距离。这能避免因遮挡严重导致的误识。实现上就是maskOverlap = sum(mask1 & mask2) / sum(mask1 | mask2),低于阈值就返回NaN。
验证方法上,我一般把数据集按 7:3 拆成注册集和测试集,注册集每人留 3 张取平均码,测试集逐张比对,画 ROC 曲线找等错误率点。如果等错误率高于 5%,先回头查瞳孔定位,八成是圆心偏了。这个链路我在不同分辨率的数据上跑过十几轮,最深的教训是:虹膜识别里定位占七成,特征提取只占三成,别在 Gabor 参数上死磕,先把圆找稳。希望帮到你。
本文还有配套的精品资源,点击获取