l learnCFSS.m为核心训练文件。需要顺序运行getParametricModels;addAll; learnCFSS;
运行需要matlabpool。如果数据N<100,则不启动并行。如果没有并行工具箱,可以手工修改代码不启动并行。
至少需要约900个样本,才能通过PCA部分的数量断言。需要较大内存要求。
poseVoting.m中使用dist函数,需要神经网络工具箱。如果没有该工具箱,可以手工添加dist函数。
至少需要约2700个样本,才能通过traintestP.m中,训练SVC时的数量断言。
traintestP.m中调用了libsvm的函数svmtrain。未避免调用到VLFEAT中的同名函数,在进入这部分之前,先去除VLFEAT的路径,之后再添加上。
各种load |
|
for level = 1:stageTot 实际只用3次迭代 |
|
|
% Re-trains仅在第一次迭代调用 |
trainingSetGeneration:返回旋转对齐图像(images),生成平均脸(referenceShape),图像旋转回归器B,对齐变换T。 Pr:先验概率。,N *N,每行对应一个原始样本,表示取得每一个原始样本的概率。设定为均匀分布,对角线零,其他位置均等。 model.tpt:当前level目标形状。N*2L。用来在测试时依照概率生成样本。 |
|
主要作用:归一化样本 |
|
% from Pr to sub-region center |
|
traintestReg:生成回归模型regModel和当前形状currentPose(相当于论文中sub-region center)。 T:当前形状到平均脸(referenceShape)的变换 images:用T进一步更新图像 model.tpt:当前level目标性状,记为targetPose。 |
|
主要作用:根据概率Pr,生成扩展初始值,用回归器移动形状,得到currentPose。 |
|
% Train-test Pr |
|
traintestP:估计当前各个训练图像上,各个训练集形状的概率Pr。 |
|
|
只在第一次迭代调用
% 输入 |
||
|
||
% Loading original images |
||
Tb:每个原始图像中simple face(左右眼角左右嘴角)到标准形状的变换。N*2L。其中标准形状为target_simple_face(0-1之间)放大到priorsInfo.win_size(250*250)。 targetPose:旋转到标准simple shape之后的L个关键点。 referenceShape:平均形状。 |
||
% Label angle estimation according to targetPose |
||
Te:每一个targetPose(已经粗略旋转过)到referenceShape(平均形状)的变换。是一个较小较精细的变换。 angle:Te变换的旋转角度。 |
||
% Dataset partition |
||
set_id:N*1。把所有原始数据随机分成均等两组。用于训练两个旋转角度回归器。 |
||
% Training |
||
B:两个角度回归器。互为校验。 |
||
for s = 1:2 分别使用两组数据训练B{s} |
||
|
Tb_train:本次训练中,从原始图像到标准形状的变换。 angle_train:本次训练中,从粗略旋转到精细旋转的变化角度。 mp:用于本次训练的样本数。约为N/2。 MP:扩展后的样本数。扩展10倍(priorsInfo.augTimes)。 im:本次训练中,原始图像经过旋转得到的augment结果。N/2*扩展数。 label:扩展样本的随机旋转角度,最大旋转45°(priorsInfo.maxRoll)。 |
|
for i = 1: augTimes |
||
|
Tr_train:从现有角度到扩展后角度的变换 Tbr_train:总体旋转角度。融合了Tb_train和Tr_train。 im:用Tbr_train对原始图像进行旋转。 |
|
F:本次训练的样本特征,图像250*250,从中心部分截取一个3*3窗口,提取31维hog特征。特征长度3*3*31 = 279。 B{s}:用前述特征通过TreeBagger预测旋转角度,得到ensemble of bagged decision trees。记为B。样本随机分成两部分,s=1:2,分别计算两个B。其用意参见测试部分。 |
||
images:原始图像通过Tb变换,粗略旋转对齐到标准形状上。 |
||
% 返回 |
||
粗略旋转对齐到标准形状上的images priorModel中的平均脸referenceShape priorModle中的旋转回归器B 旋转对齐到平均形状上的变换T |
% 输入 |
|
images:原始形状 targetPose:目标形状 Pr:概率。N*N。 |
|
% 1. Sampling both for train and test 根据Pr为每个样本生成训练和测试样本 |
|
currentPose_train, currentPose_test:对于每一个原始样本,都有10个(regsInfo.trainSampleTot)用于训练和10个(regsInfo.testSampleTot)用于测试的样本,是从N个原始样本中抽取的。N*2L*10. |
|
for i = 1:m 对于每一个样本 |
|
|
第一次迭代 |
index_train:用randSampleNoReplace从原始样本中根据Pr抽取10个样本。此时Pr是除了当前样本之外的均匀分布。 index_test:用randSampleNoReplace从原始样本中根据PrI_temp抽取10个样本。其中PrI_temp是刨除10个训练样本之后的概率分布。训练和测试不能重复。 |
|
其他次迭代 |
|
tmp:将样本按照Pr降序排列 index_train, index_test:概率最大的trainSampleTot个样本。 |
|
从目标形状中根据index_XXX取出currentPose_XXX。 |
|
% 2. learn regressors to get regModel % 2A. Augmentation 把训练样本进行随机变换 |
|
M:N*训练样本数(10)。每个样本有10个训练,10个测试。 cu:当前形状。10N*2L tg:目标形状。10N*2L |
|
for i = 1:trainSampleTot 处理所有原始样本的第i个训练样本,共N个。 |
|
|
selectPoses:返回每个目标形状(targetPose)中,外眼角(regsInfo.aug_eyes_id)位置。N*4。 Ta:1*N个随机变换。是通过原始样本中随机两个样本之间的变换而得。对于每一个i,都重新随机一次。 cu:把currentPose_train中的相应的N个训练样本施加Ta中的N个变化,得到N个结果。 tg:把targetPose(共N个)施加Ta中的N个变化,得到N个结果。 im:把每个原始图像经过Ta中的N个变化,得到N个结果。 当训练样本序号i为偶数时,把cu、tg、im都镜像。 |
% 2B. Training iteration |
|
for iter = 1:iterTot 共3次,训练每一个迭代的一系列回归器 |
|
|
featOri:每一个原始样本的 关键点SIFT特征,使用自定义函数extractSIFTs_toosimple提取。维度128*L = 8704。所以至少需要871个原始样本。 featReg:对featOri做PCA之后,保留98%能量,压缩到3693维。 pca_model:PCA模型 reg_model:用trainLR_averageHalfBaggin训练一个回归器,从特征featReg预测形状偏差tg-cu。相当于原始SDM中的一级回归器。 regModel:最终返回的模型。平均值mu设为PCA的平均特征meanFeatOri,一次项A设为PCA的投影矩阵coeff和reg_model一次项的乘积,常数项定义为reg_model的常数项。 更新当前形状cu:PCA和回归合并到一步完成。 |
featOri_batch:所有样本对应测试样本的SIFT特征。N*8704*10。 currentPose_test:用前述特征通过回归器进行更新。 |
|
currentPose:从测试样本currentPose_test中,利用poseVoting(论文中的自动权重)计算。N*2L。描述一个概率分布。 |
|
% 返回 |
|
包含PCA的偏移量回归模型regModel 当前形状currentPose |
计算Pr的核心是inferenceP函数,前面的都在训练模型。
% 输入 |
|
images:原始图像 targetPose:目标形状 currentPose:当前形状 |
|
% A. Prior Prob Learning 论文中概率第一项:从偏差值推测概率 |
|
d:当前形状currentPose到目标性状targetPose偏移。N*2L d_pca:偏移量PCA结果,压缩到17维。N*17。 PModel.sigma:d_pca的协方差。测试时用高斯模型估计偏差值的概率。 PModel.pca_model:偏移量投影的PCA模型 |
|
% B. Features Prob Learning 论文中概率第二项:从特征和偏差推断概率 % 1. Sampling for training and testing |
|
s:采样个数probsInfo.probSamplings=5。扩展样本用于训练点偏移SVM。 ld:有15个semantic点,如下图红色。每个点有一个cell。存储semantic点偏移量位置。 |
|
for i = 1 : length(semantic_id) 全部样本的每个semantic点 |
|
|
dx,dy:半径0-SVCradius之间,方向0-2PI之间的随机矢量。N*5。 ld的当前cell:N*5*2。全部样本目标形状targetPose的semantic点,加上上述随机偏移。 |
% 2. Centralized feature extraction |
|
feat_all:每个尺度一级。尺度probsInfo.pyramidScale=3 |
|
for pyramid = 1:length(pyramidScale) |
|
|
pose:N*(2*15)*(5+1)。样本数*semantic点数*(采样数+1)。前5维为ld中的偏移位置targetPose+(dx,dy),最后一维是未偏移的当前形状currentPose。 feat_all:在pose上提取SIFT特征。 |
% Unroll them into training and test set |
|
F_train, L_train, F_current:每个semantic点对应一个cell, |
|
for i = 1:length(semantic_id) 每个semantic点单独训练 |
|
|
训练数据个数:N*s。 F_train{i}:(N*s)*(128*3)。3尺度SIFT。偏移的s个采样的特征。 L_train{i}:(N*s)*2。pose到targetPose的偏移量。 F_current{i}:N*(128*3)。当前形状的特征。 |
% 3. Train SVM classifiers for each landmark |
|
svc_:每个semantic点有一个svc模型。 |
|
for i = 1:semantic_id 每个semantic点训练SVC |
|
|
从N*s个原始样本中挑选出正负样本 ind_pos:偏移距离小于probsInfo.SVCthre = 0.3/0.6 ind_neg:偏移距离大于probsInfo.SVCthre 要求正负样本中较少的数量 > 8倍维度 = 3072。约需要2700个样本。 用正负样本训练当前SVC。偏差小的输出1,偏差大的输出0。(libsvm自动把+-1标记转换为0/1标记??) |
PModel.SVC赋值为刚训练出的模型。 |
|
% 4. tpt analysis |
|
PModel.representativeLocation:长度为semantic点数 |
|
for i = 1:length(semantic_id) |
|
|
location:全部目标形状中当前semantic点位置 picked_id:用analyse_tpt.m函数找出该点在不同形状中的代表位置。(详细参看analyse_tpt)从全部形状中选出probsInfo.representativ2Num1个, 从居于中心的形状中选出probsInfo.representativeNum2个,共20个。 PModel.representativeLocation{i}:20*2,20个代表形状的位置。 |
% C. Testing and training |
|
Pr:使用reference.Pm函数计算在PModel下,每一个当前形状属于每一个目标形状的概率。(详细参看inferenceP.m) |
|
% 返回 |
|
PModel:包含第一项PCA,以及第二项SVC的模型 Pr:当前形状概率。 |
其核心函数为analyse_tpt_base:从若干个location中选择出representativeNum个代表性点。
返回代表性点为picked_id,初始为空。
首先找出所有点的中心cent,距离cent最近的点放入picked_id。(knnsearch:返回距离某个点/某一组点最近的k个点序号)
而后,找出剩下的点。
对于每个点,找出所有点到现有picked_id的距离的最短距离。这个距离最大的点,认为和现有的点最不相似,放入picked_id中。下图是从100个随机点中选择10个代表点。
返回的序号是从外围到中心。最后一个点是最开始找出的cent。
% Step A: For all locations |
pA:从location中找出的representativeNum1个代表性点序号(绿圈)。 capital_id:如果一个location点到pA所有点的距离。如果距离最近的点是pA的最后一点,则返回该location的序号(红圈)。由于pA是按照从外围到中心排列,的capital_id中包含location中中心部分的点。 |
% Step B: For capital location in Step A |
pB:从capital_id的店中,找出represntativeNum2+1个代表性点。由于最后一个点返回的中心和pA的相同,所以要多找一个。 |
% Merge and get assign |
picked_id:pA和pB拼合,去除重复的中心点。 assign:location到picked_id中最近的picked_id序号。相当于把location分配给这些代表性点。 |
先计算包含纹理信息的后验概率,而后计算只由形状决定的先验概率。
% 输入 |
|
images:对齐的图像。数量为M。 model:semantic点纹理->偏移的SVC模型,semantic点代表性位置representativeLocation,点偏移量的PCA模型 currentPose:当前形状。数量为M。训练时等于为训练集样本数N。测试时为测试集样本数。 level:外层迭代次数,用于确定参数 probsInfo:参数 tpt:标定的目标形状(targetPose)。数量为训练集样本数记为N。 |
|
% Validation over currentPose |
|
feat_current:当前形状semantic点上的3尺度SIFT特征。高为M,宽为128*15*3 = 5760。 scoring_board:长度=semantic点数。每个cell中是M*N,表示第i测试样本和第j训练样本相似程度。 Pr:M*N*25,其中25为15个semantic点和10个fix点(人脸图绿色点)。概率拆解为关键点概率的连乘。 record_point:M*(2*25)。semantic点和fix点。 record_mask:M*25。没用。 |
|
for i = 1:length(semantic_id) % 处理全部当前形状的第i个semantic点。 |
|
% 靠谱的用当前形状,不靠谱的用模型中的代表性点 |
|
|
confidence_current:从feat_curretn中找出当前点对应的特征,用model中的SVC模型,预测一个置信度。第一列为结果:离真实位置近,为1;离真实位置远,为0;第二列为accuracy,删除不用。 ind:当前形状中置信度>=acceptThre=0.9的点序号。这些样本离真实值足够近。数量设为M1。 |
% for only current |
|
dX, dY:M1个当前正确形状到目标形状tpt的偏差。M1*N。 scoring_board{i}: 用M1个形状偏差dX,dY的高斯 函数计算。其方差为预设的gamma_current。填入scoring_board{i}对应的测试样本行内。 record_point:把当前形状对应点,填入record_point中,semantic部分的对应位置。 |
|
ind:当前形状中置信度<acceptThre=0.9的点序号。数量设为M2。M1+M2 = M |
|
% for considering resamping |
|
feat_search:M2个偏差形状,第i个点的representativeLocation(共20个样本)上提取的3级SIFT特征。高度M2,宽度20*3*128 = 7680。 confidence_search:代表性点的置信度。用model中的SVC模型,预测feat_search。结果小于0.5的置为0,按行归一化。M2*20。 searched_point:confidence_search(M2*20)*representativeLocation(20*2)。代表性点,用置信度加权的平均值。M2*2。 scoring_board{i}, record_point:同前处理。填入相应位置内。 |
|
Pr:把i对应的概率赋值为scoring_board{i} |
|
for i = 1:length(fix_id) |
|
|
record_point:把当前形状的对应点填入 dX, dY:当前形状到目标形状偏差 Pr:用dX, dY计算的高斯概率。 |
连乘Pr的第三维,得到M*N的矩阵。此时得到的Pr为论文中概率第二项:和纹理相关的后验概率。 |
|
% Multiply the prior |
|
for i = 1:m % 对于每个当前形状 |
|
|
d:第i个当前形状到目标形状偏差。N*(2*L) d_pca:用模型中的PCA对d进行投影。N*(2*17)。 prior:每一行(1*N) = d_pca自身数乘,而后对第二维求和。相当于在投影后空间里,到原点的距离。 |
prior:用高斯函数计算。方差为gamma_prior。 Pr:用Pr(后验)乘上prior(先验)。之后归一化。 |
|
% 返回 |
|
Pr:M*N矩阵。第i个当前形状等于第j个训练集形状的概率。 |
inferenceCFSS.m为核心训练文件。
测试单张图片相当慢,超过5s,和论文所述25fps不符。不知是否并行未启动所致。
如果更改了11行的测试数据数量,则末尾再次载入数据也要相应更改。
占用内存较大,即使是只测试2个样本,在家用PC上也会有low memory警报。
和训练流程非常相似。
载入各种数据,模型 m:测试样本数 mt:训练样本数 |
|
for level = 1:stageTot |
|
|
% 61. Re-trans |
testingsetGeneration:利用图像旋转回归器生成对齐的图像images,以及对齐的刚性形变T。 Pr:m×mt,设定为均匀分布。 |
|
% 62. from Pr to sub-region center |
|
currentPose:m×(2L)。通过inferenceReg函数从Pr估计初始形状,用回归器进行迭代。 T:记录当前形状到平均形状priorModel.referenceShape的变换 images:用上述变换继续对图像进行矫正。下图示出迭代三次图像的变化。 currentPose:用上述变换对当前形状进行矫正。 |
|
% 63. from sub-region center to Pr |
|
Pr:通过inferenceP函数,计算当前图像上,训练样本的概率分布。 |
|
estimatedPose:用各次迭代记录的变换T把currentPose反变换回去。 |
|
最后计算估计值estimatedPose和真实值data的误差,用瞳距归一化。 |
和训练流程中预处理部分很像。只在第一次迭代调用
m:测试样本数 p:m×8,平均simple_face(眼角嘴角),数量和测试样本数相同 Tb:每个原始图像中simple face到标准simple shape的变换。只有平移缩放剪切,不包含旋转。 images:原始灰度图像经过Tb的转换,进行对齐。取代人脸框作用,不算作弊。 |
F:图像中部3*3网格中的HOG特征,共279维。 predicted:样本数*2。分别用两个ensemble of bagged decision trees(记为priorModel.B),从特征F预测本图片的旋转角度。 此处的B模型有两个,各由一半训练样本生成。如果两个预测结果角度之差<180°,则将两个结果平均输出;否则,直接输出0°正脸。 另外设定了参数priorsInfo.predictedVoteThre。对预测结果绝对值进行限制。 Tr:预测角度的反旋转。 |
T:缩放平移的Tb + 预测出的旋转Tr images:经过T变换的图像 |
% 返回 |
归一化图像images,归一化变换T。 |
m:测试样本数 tpt:本次迭代的训练集样本:N*2L mt:训练集样本数 currentPose_inference:N*2L*采样数(regsInfo.samplingTot,每级都是10)。初始形状。第一次迭代时,均匀从训练样本中取;其他次迭代,先对Pr进行排序,而后取概率最大的训练样本。 |
|
% inference Iteration |
|
for iter = 1:iterTot % 4个回归,更新初始形状 |
|
|
featOri_batch:样本数*8704*采样数。使用128*L = 8704维SIFT特征。 currentPose_inference:形状变化量如下计算:(feaOri – model.reg.mu) * model.reg.A + model.reg.b。 |
currentPose:通过poseVoting从10个currentPose_inference中得到加权平均值。迭代次数为regsInfo.dominantIterTot=100。 |
和训练中的infereceP一样。计算当前形状currentPose在当前图像上,属于训练样本的概率。