SDM For Face Alignment 流程介绍及Matlab代码实现之训练篇

SDM 训练阶段的任务如下:

  1. 载入标准化的数据(包括400*400的正脸及特征点)
  2. 对每一张标准化的图片,模拟人脸检测仪,产生10个扰动的人脸框及相应的初始特征点x0
  3. 求解Δx,Φ,其中Δx=xx0,x表示true shape,Φ表示每个特征点的特征向量
  4. 求解最小二乘问题,得到一系列{Rk}

下面分别来说明:

载入数据

载入811个训练数据,按照上一章预备篇关于第一幅图片的裁剪方法裁剪这811张图片。
matlab代码如下:

function [Data] = load_single_data2 ( dbpath_img, dbpath_pts,image_index, options )%% output format
%{
DATA.
- width_orig: the width of the original image.
- height_orig: the height of the original image.
- img_gray: the crop image.
- height: the height of crop image.
- wdith: the width of crop image.
- shape_gt: ground-truth landmark.
- bbox_gt: bounding box of ground-truth.
%}
slash = options.slash;
dbname = options.datasetName;imlist = dir([dbpath_img slash '*.*g']);%% load imagesimg = im2uint8(imread([dbpath_img slash imlist(image_index).name]));Data.width_orig  = size(img,2);Data.height_orig = size(img,1);%% load shapeData.shape_gt = double(annotation_load(...[dbpath_pts slash imlist(image_index).name(1:end-3) 'pts'] , dbname));if 0figure(1); imshow(img); hold on;draw_shape(Data.shape_gt(:,1),...Data.shape_gt(:,2),'y');hold off;pause;end    %% get bounding boxData.bbox_gt = getbbox(Data.shape_gt);%% enlarge region of faceregion     = enlargingbbox(Data.bbox_gt, 2.0);region(2)  = double(max(region(2), 1));%这里主要是为了防止求出的包围盒超过图像,因此一旦超过,则region(2)必然小于0,因此此时取1即可。region(1)  = double(max(region(1), 1));bottom_y   = double(min(region(2) + region(4) - 1, ...Data.height_orig));right_x    = double(min(region(1) + region(3) - 1, ...Data.width_orig));%防止长和宽超过图片大小,因此取二者最小值img_region = img(region(2):bottom_y, region(1):right_x, :);%取人脸区域%% recalculate(重新计算) the location of groundtruth shape and bounding boxData.shape_gt = bsxfun(@minus, Data.shape_gt,...double([region(1) region(2)]));%等价于Data{iimgs}.shape_gt-repeat( double([region(1) region(2)]),size(Data{iimgs}.shape_gt,1),1)%将图像的坐标原点移到人脸包围盒的左上角,并因此得以重新计算新的特征点Data.bbox_gt = getbbox(Data.shape_gt);%新的特征点的包围盒的左上角坐标发生了改变,但是宽和高没有变化if size(img_region, 3) == 1Data.img_gray = img_region;elseData.img_gray = rgb2gray(img_region);endData.width    = size(img_region, 2);Data.height   = size(img_region, 1);if 0figure(2); imshow(Data.img_gray); hold on;draw_shape(Data.shape_gt(:,1),...Data.shape_gt(:,2),'y');hold off;pause;end%% normalized the image to the mean-shapesr = options.canvasSize(1)/Data.width;sc = options.canvasSize(2)/Data.height;Data.img_gray = imresize(Data.img_gray,options.canvasSize);Data.width    = options.canvasSize(1);Data.height   = options.canvasSize(2);Data.shape_gt = bsxfun(@times, Data.shape_gt, [sr sc]); Data.bbox_gt(1:2) = bsxfun(@times, Data.bbox_gt(1:2), [sr sc]);%补充Data.bbox_gt(3:4) = bsxfun(@times, Data.bbox_gt(3:4), [sr sc]);%补充if 0figure(3); imshow(Data.img_gray); hold on;draw_shape(Data.shape_gt(:,1),...Data.shape_gt(:,2),'r');hold on; rectangle('Position',  Data.bbox_gt, 'EdgeColor', 'k');pause;end       endfunction region = enlargingbbox(bbox, scale)
%同前面一样,初始时刻这里得到仅仅是特征点盒子,而我们如果想要包住整个人脸,就必须先将原始盒子的左上角平移一半的宽高,然后再放大两倍。这个在前面求解
%rect = get_correct_region( boxes, shape,Dataa(i).img, 1 );中也用到过
%因此这里得到的盒子是包住全部人脸的盒子。    
region(1) = floor(bbox(1) - (scale - 1)/2*bbox(3));
region(2) = floor(bbox(2) - (scale - 1)/2*bbox(4));region(3) = floor(scale*bbox(3));
region(4) = floor(scale*bbox(4));end

模拟人脸检测,产生10个初始值

事实上,每张图片都有一个ground-truth poins,因此可以求出它的包围盒,也可以通过opencv或其他的检测器可以检测出这样的框来。但两者有点不一样。如下:

,我们可以对opencv的检测盒做一些变换就可以得到近似的box gt了。
我们需要对包围盒扰动,以产生更多的盒子。怎么扰动呢?

对于一个盒子,有四个属性:x,y,width,height.因此我们只要产生10种属性即可。或者,也可以从另外一种角度来考虑这个问题。假设新的盒子已产生,那么它与原来的盒子之间就会产生4个方向的偏差,因此我们只需要对这些偏差做估计即可。

事实上,我们通过对811张图片的init shape 与ground truth shape求解偏差的均值与方差,以此可以产生两个(分别是(x,y),(width,height))二维正太分布,因此就可以产生正太分布的随机数,于是10种属性的偏差就产生了,然后加上原来盒子的属性,就产生了10个扰动的盒子。再将mean shape对齐到10个盒子上产生了10个初始值。
do_learn_variation.m:用来产生偏差的均值和方差

function do_learn_variation( options )%% loading learned shape model
load([options.modelPath options.slash options.datasetName '_ShapeModel.mat']);imgDir = options.trainingImageDataPath;
ptsDir = options.trainingTruthDataPath;%% loading data
Data = load_data( imgDir, ptsDir, options );n = length(Data);transVec   = zeros(n,2);
scaleVec   = zeros(n,2);debug = 0;%% computing the translation and scale vectors %%%%%%%%%%%%%%%%%%%%%%%%%%%%for i = 1 : n%% the information of i-th imagedisp(Data(i).img);img   = imread(Data(i).img);shape = Data(i).shape;%% if detect face using viola opencv% boxes = detect_face( img , options );%% if using ground-truthboxes = [];%% predict the face boxrect = get_correct_region( boxes, shape,img, 1 );%% predict initial location[initX,initY,width,height] = init_face_location( rect );%注意:上面算出的人脸框比较大,一般是特征点包围盒的4倍,因此上面算出的width和height分别是rect宽和高的一半,实际上从bounding_box的计算中可以看出,%特征点的包围盒分别向左上和右下延伸了一半的宽和高,导致人脸的包围盒的面积是特征点包围盒的4倍.init_shape = align_init_shape(ShapeModel.MeanShape, ...initX, initY, width, height);if debugfigure(1); imshow(img); hold on;rectangle('Position',  rect, 'EdgeColor', 'g');draw_shape(init_shape.XY(1:2:end), init_shape.XY(2:2:end), 'y');%绘制每幅人脸图上的平均人脸点hold on;plot(initX, initY, 'b*');%中心点draw_shape(shape(:,1), shape(:,2), 'r');hold off;pause;end[aligned_shape, cropIm] = align_to_mean_shape( ShapeModel, img , ...vec_2_shape(init_shape.XY) , options );%vec_2_shape将一维向量转化为二维向量,获取400*400下的图像和在此标准下的真实人脸点和初始化人脸点[aligned_true_shape] = align_shape(aligned_shape.TransM,shape_2_vec(shape));if debugfigure(1); imshow(cropIm); hold on;draw_shape(aligned_shape.XY(1:2:end), ...aligned_shape.XY(2:2:end), 'y');draw_shape(aligned_true_shape(1:2:end), ...aligned_true_shape(2:2:end), 'r');%hold off;pause;end    initVector = vec_2_shape(aligned_shape.XY);trueVector = vec_2_shape(aligned_true_shape);%compute mean and covariance matrices of translation.%计算平移的平均值和协方差矩阵meanInitVector  = mean(initVector);meanTrueVector  = mean(trueVector);%compute bounding box sizeinitLeftTop     = min(initVector);initRightBottom = max(initVector);initFaceSize = abs(initLeftTop - initRightBottom);trueLeftTop     = min(trueVector);trueRightBottom = max(trueVector);trueFaceSize = abs(trueLeftTop - trueRightBottom);transVec(i,:) = (meanInitVector - meanTrueVector)./initFaceSize;%平移要除以一个标准的人脸大小是为了消除人脸大小带来的不一致scaleVec(i,:) = initFaceSize./trueFaceSize;clear img;clear xy;%    endend%compute mean and covariance matrices of scale.%计算缩放的平均值和协方差矩阵
[mu_trans,cov_trans] = mean_covariance_of_data ( transVec );
[mu_scale,cov_scale] = mean_covariance_of_data ( scaleVec );DataVariation.mu_trans  = mu_trans;
DataVariation.cov_trans = cov_trans;
DataVariation.mu_scale  = mu_scale;
DataVariation.cov_scale = cov_scale;save([options.modelPath options.slash options.datasetName ...'_DataVariation.mat'], 'DataVariation');clear Data;end

random_init_position.m:产生10个盒子

function [rbbox] = random_init_position( bbox, ...DataVariation, nRandInit,options)rbbox(1,:) = bbox;    if nRandInit > 1center = bbox(1:2) + bbox(3:4)/2;                                            mu_trans  = DataVariation.mu_trans;
cov_trans = DataVariation.cov_trans;
mu_scale  = DataVariation.mu_scale;
cov_scale = DataVariation.cov_scale;rInit_trans = mvnrnd(mu_trans,cov_trans,nRandInit-1);
%rInit_trans = zeros(nRandInit-1,2);rCenter = repmat(center,nRandInit-1,1) + ...rInit_trans.*repmat([bbox(3) bbox(4)],nRandInit-1,1);rInit_scale = mvnrnd(mu_scale,cov_scale,nRandInit-1);%r = mvnrnd(MU,SIGMA,cases)——从均值为MU(1*d),协方差矩阵为SIGMA(d*d)的正态分布中随机抽取cases个样本,返回cases*d的矩阵r。
%rInit_scale = ones(nRandInit-1,2);rWidth  = zeros(nRandInit-1,1);
rHeight = zeros(nRandInit-1,1);for i = 1 : nRandInit - 1rWidth(i)  = bbox(3)*rInit_scale(i,1);%原始是除rHeight(i) = bbox(4)*rInit_scale(i,2);
endrbbox(2:nRandInit,1:2) = rCenter - [rWidth(:,1) rHeight(:,1)]/2;
rbbox(2:nRandInit,3:4) = [rWidth(:,1) rHeight(:,1)];
%补充项,防止扰动超过图片的边界
rbbox(1:nRandInit,1:2)=max(rbbox(1:nRandInit,1:2),1);
rbbox(1:nRandInit,1:2)=min(rbbox(1:nRandInit,1:2)+rbbox(1:nRandInit,3:4),options.canvasSize(1) )-rbbox(1:nRandInit,3:4);
endend

resetshape.m:将shape_union对齐到bbox

function [shape_initial] = resetshape(bbox, shape_union)
%RESETSHAPE Summary of this function goes here
%   Function: reset the initial shape according to the groundtruth shape and union shape for all faces
%   Detailed explanation goes here
%   Input: 
%       bbox: bbounding box of groundtruth shape
%       shape_union: uniionshape
%   Output:
%       shape_initial: reset initial shape
%       bbox: bounding box of face image% get the bounding box according to the ground truth shape
width_union = (max(shape_union(:, 1)) - min(shape_union(:, 1)));
height_union = (max(shape_union(:, 2)) - min(shape_union(:, 2)));shape_union = bsxfun(@minus, (shape_union), (min(shape_union)));shape_initial = bsxfun(@times, shape_union, [(bbox(3)/width_union) (bbox(4)/height_union)]);
shape_initial = bsxfun(@plus, shape_initial, double([bbox(1) bbox(2)]));end

求解特征点之差和特征向量

上面我们对每幅图片求得了10个初始特征点,这样我们就很容易求解Δx了。同样对于特征向量Φ,我们也可以很容易地求出来。关于特征向量,又名描述子。我们可以选择Sift特征或者Hog特征。
local_descriptors:求解特征向量

function [desc] = local_descriptors( img, xy, dsize, dbins, options )%计算描述子featType = options.descType;stage = options.current_cascade;dsize = options.descScale(stage) * size(img,1);if strcmp(featType,'raw')if size(img,3) == 3im = im2double(rgb2gray(uint8(img)));elseim = im2double(uint8(img));endfor ipts = 1 : nptsdesc(ipts,:) = raw(im,xy(ipts,:),desc_scale,desc_size);endelseif strcmp(featType,'xx_sift')%     i = randi([1 68],1,1);
%     rect = [xy(18,:) - [dsize/2 dsize/2] dsize dsize];
%     
%     if 1
%         figure(2); imshow(img); hold on;
%         rectangle('Position',  rect, 'EdgeColor', 'g');
%         hold off;
%         pause;
%     endif size(img,3) == 3im = im2double(rgb2gray(uint8(img)));elseim = im2double(uint8(img));endxy = xy - repmat(dsize/2,size(xy,1),2);desc = xx_sift(im,xy,'nsb',dbins,'winsize',dsize);elseif strcmp(featType,'hog')if size(img,3) == 3im = im2double(rgb2gray(uint8(img)));elseim = im2double(uint8(img));endnpts = size(xy,1);for ipts = 1 : npts%disp(ipts);if isempty(im)disp('empty im');endif isempty(dsize)disp('empty dsize');enddesc(ipts,:) = hog(im,xy(ipts,:),dsize);endendend

求解最小二乘问题

问题:

Min||ΔXRΦ||22

其中 ΔXR(682)×n,ΦR(12868)×n
这里68为特征点的个数,128为每个特征点的特征向量的维数,n为样本量,这里为811.
显然这是个最小二乘问题,可以直接求解。
ΔX=RΦΔXΦ=RΦΦΔXΦ(ΦΦ+λI)1=RΦΦ(ΦΦ+λI)1ΔXΦ(ΦΦ+λI)1=R

也可以通过SVM方法求解,这里我们调用了 liblinear的SVR方法来求解。
linreg.m:求解最小二乘问题

function [R,lambda] = linreg( X , Y , lambda )%X = [ones(size(X,1),1) X];%% method 1: soving linear regression using close-form solution %%%%%%%%%%%% R = (X'*X+eye(size(X,2))*lambda)\X'*Y;%先是X'*Y,再是除法%% method 2: using SVR in liblinear %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
featdim   = size(X,2);
shapedim  = size(Y,2);param = sprintf('-s 12 -p 0 -c %f -q', lambda);
%param = sprintf('-s 12 -p 0 -c 0.3 -q');
R_tmp = zeros( featdim, shapedim );
tic;
for o = 1 : shapedimdisp(['Training landmarks ' num2str(o)]);model = train(Y(:,o),sparse(X),param);R_tmp(:,o) = model.w';
end
toc;R = R_tmp;end

后续的话,我们还需要根据求解的R来更新x0,进而更新Δx,Φ,
最后求解新的最小二乘问题,得到新的R,以此下去,迭代5步即可。
这时产生的{Rk}就可以用来进行下一步的test了。如下为5次的迭代的特征点效果图:
1st step
2nd step
3rd step
4th step
5th step
我们可以看到越往后迭代,产生的新的特征点就越接近true shape.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/259260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hibernate5-多对1(n:1)-fetch=join

1.创建项目,项目名称hibernatedemo26,目录结构如图所示2.在项目中创建lib目录存储jar文件,目录结构如图所示3.在src目录中创建实体类Forum,包名(com.mycompany.demo.bean),如图所示4.实体类Forum的内容如下package com.mycompany.demo.bean;import java.util.Set;public class …

如何使用固定二级子域名公网访问多个本地Windows Web网站

文章目录 1. 下载windows版Nginx2. 配置Nginx3. 测试局域网访问4. cpolar内网穿透5. 测试公网访问6. 配置固定二级子域名7. 测试访问公网固定二级子域名 1. 下载windows版Nginx 进入官方网站(http://nginx.org/en/download.html)下载windows版的nginx 下载好后解压进入nginx目…

实验 6 数组1

//输入n个整数&#xff0c;将它们存入数组a中。输出最大值和它所对应的下标。 #include<stdio.h> int main(void) {int n,i,x;int a[10];x0;printf("enter n:");scanf("%d",&n);for(i0;i<n;i){printf("enter :");scanf("%d&qu…

初中计算机职称答辩,晋升中学语文高级教师职称答辩内容举例

晋升中学语文高级教师职称答辩内容举例 晋升中学语文高级教师职称答辩秘籍 最重要的一点&#xff1a;你要对课本上的重点篇目非常熟悉&#xff01;对于现代文来说作者、题材、课文重点、重点句子词语、中心思想等你都要明了。对于文言文来说&#xff0c;要求学生掌握的&#xf…

SDM For Face Alignment流程介绍及Matlab代码实现之测试篇

测试很简单了&#xff0c;只需要载入数据&#xff0c;然后做正则化处理&#xff0c;使用训练模型产生的{Rk},就可以预测特征点了。 face_alignment.m:用来预测特征点 function shape face_alignment( ShapeModel, DataVariation,...LearnedCascadedModel, Data, img, shape,…

计算机类公务员如何提升自己,大学毕业才发现:所学专业对考公务员如此重要,4类专业上岸率高...

导语&#xff1a;毕业季来临&#xff0c;同学们是想直接找工作积累工作经验&#xff0c;还是继续考取相关证书&#xff0c;来获得更稳定职业的入场券&#xff1f;毕业抉择很多毕业生面临的第一个问题就是未来职业规划&#xff0c;因为大学毕业之后&#xff0c;就意味着一段新的…

UVA 11401 - Triangle Counting

Problem G Triangle Counting Input: Standard Input Output: Standard Output You are given n rods of length 1, 2…, n. You have to pick any 3 of them & build a triangle. How many distinct triangles can you make? Note that, two triangles will be considere…

苏州软件测试11k工资要什么水平,3个月从机械转行软件测试,他的入职薪资是11K...

原标题&#xff1a;3个月从机械转行软件测试&#xff0c;他的入职薪资是11K只要找到适合自己的学习方式&#xff0c;成功转行只是早晚的问题&#xff01;今天汇智妹给大家介绍的这位小伙伴&#xff0c;是咱们汇学联盟平台上的一位线上学员——小周。97年的小哥哥&#xff0c;19…

TCP/IP 原理--链路层

链路层作用&#xff1a; &#xff08;1&#xff09;为IP模块发送和接收IP数据报&#xff1b; &#xff08;2&#xff09;为ARP发送ARP请求和接受ARP应答 &#xff08;3&#xff09;为RARP发送RARP请求和接受ARP应答 协议&#xff1a;以太网和SLIP协议 A.以太网协议数据封装格式…

拆解凹多边形

偶遇需要拆解凹多边形 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Media;namespace DrawPolygon {public static class Settings{public const float…

一个FORK的面试题

为什么80%的码农都做不了架构师&#xff1f;>>> #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main(void) { int i; for(i0; i<2; i){ fork(); printf("-"); } wait(NULL); wait(NULL); return 0; }/c 如果…

C++11系列学习之二-----lambda表达式

C11添加了一项名为lambda表达式的新功能&#xff0c;通过这项功能可以编写内嵌的匿名函数&#xff0c;而不必编写独立函数和函数对象&#xff0c;使得代码更容易理解。lambda表达式的语法如下所示&#xff1a;[capture_block](parameters) exceptions_specification -> retu…

GCPC2014 C Bounty Hunter

题意&#xff1a;给你一个平面上的点集&#xff08;x值各不相等&#xff09;&#xff0c;问你从最左边走到最右边&#xff08;只能以x递增的顺序&#xff09;&#xff0c;再从最右边回到最左边&#xff08;以x递减的顺序&#xff09;问你最短距离是多少。 解题思路&#xff1a;…

计算机启动时运行ccleaner,Ccleaner的使用方法

ccleaner是一款非常好用的系统优化工具&#xff0c;它可以提升电脑速度&#xff0c;可以对上网历史记录、临时文件夹、回收站垃圾清理、注册表进行垃圾项扫描和清理、软件卸载等功能&#xff0c;保护用户的个人浏览隐私&#xff0c;为Windows系统腾出更多硬盘空间。下面小编就为…

PLSQL Developer软件使用大全

PLSQL Developer软件使用大全 第一章 PLSQL Developer特性 PL/SQL Developer是一个集成开发环境&#xff0c;专门面向Oracle数据库存储程序单元的开发。如今&#xff0c;有越来越多的商业逻辑和应用逻辑转向了Oracle Server&#xff0c;因此&#xff0c;PL/SQL编程也成了整个开…

电脑显示器变色_电脑维修(看完后就可以开一家自己的电脑维修店!)

第二部分 常见故障判断本部分将计算机从开机一直到关机期间的故障进行分类。每一类的判断、定位过程都是第一部分中维修判断一节的有机组成部分&#xff0c;即不论使用什么方法或不论去判断什么内容&#xff0c;这两部分总是相互结合使用的。以下各故障类型中所列的故障现象只是…

linux运维基础篇 unit7

unit 71.进程定义进程就是cpu未完成的工作2.ps命令psa ##关于当前环境的所有进程x ##与当前环境无关的所有进程f ##显示进程从属关系e ##显示进程调用环境工具的详细信息l ##长列表显示进程的详细信息u ##显…

运行快捷指令无法连接服务器失败,快捷指令打不开怎么回事?iPhone快捷指令无法载入的解决办法...

经常会有果粉朋友反馈&#xff0c;自己的 iPhone 快捷指令打不开。具体表现是&#xff0c;在 Safari 浏览器中&#xff0c;打开快捷指令下载安装页面&#xff0c;点击“获取捷径”后&#xff0c;一直卡在快捷指令中心正在载入页面&#xff0c;等半天都无法正常载入需要安装的快…

maven搭建多模块项目和管理

在eclipse下构建maven项目&#xff0c;该项目由多个子模块组成。 1.创建一个父项目 NEW -->project-->maven-->maven Project&#xff0c;点击下一步&#xff0c;进入new maven Project的Select project name and location界面 &#xff0c;什么也不做&#xff0c;直接…

shsh验证服务器,教你从Cydia上取出SHSH并验证有效性!

原标题&#xff1a;教你从Cydia上取出SHSH并验证有效性&#xff01;今天在第一篇内容中和大家说了如何让32位设备进行降级&#xff0c;但这其中有个很重要的问题就是如何提取出对应设备的SHSH&#xff0c;虽然说本篇内容并不是对所有人都有效&#xff0c;但至少多了一个可选择的…