SDM人臉對齊系列一:資料預處理
人臉對齊是人臉識別系統中很重要的一個環節,SDM是傳統人臉對齊演算法中效能較為不錯的一種,在今天這個深度學習如火如荼的時代,SDM依舊具有一定的優勢。SDM相比深度網路具有模型小,速度快等優點。儘管SDM已經出現了好幾年,但是網路上對其具體的詳細講解的知識還是比較少,尤其是和訓練相關的東西。這幾天自己硬著頭皮啃了下原始碼,給出自己的理解。
1、資料集的下載
關於人臉對對齊的資料集有很多,比較常用的幾個庫可以到這裡下載:點選開啟連結。本文采用的是lfw資料集進行的實驗,下載後可以看到資料集分為testset和trainset兩個資料夾,分別包含了測試集和訓練集,各個資料集下包含了圖片和對應的標籤。
2、求平均人臉形狀
人臉對齊中,較為關鍵的第一步就是獲取訓練集的人臉關鍵點的平均形狀。因為測試資料集是基於平均臉進行偏移實現人臉對齊的。首先,正則化第一張人臉圖片:
(1)取第一張圖片包含gt points的最小矩形。
(2)以得到的最小矩形向左上角平移,平移的大小x、y方向分別為矩形寬和高,然後矩形的寬和高分別擴充套件為原來的兩倍,將這個變化後的矩形對圖片中的人臉進行裁剪,同時對裁剪出來的人臉的特徵點做相應的調整,這樣裁剪出來的人臉基本上包含了整個人臉部分。
(3)將新裁剪出來的人臉縮放到400*400的大小,同時對應的特徵點做相應的調整,當然這裡你不一定也要設定為400*400,可以根據自己的需要修改。這裡只是講解下主要涉及的函式,太具體的需要大家自己摸索,具體程式碼如下:
bounding_box.m用來獲取人臉裁剪的資訊:
function [cropmin,cropmax,offset,minshape,marginW,marginH] = ... bounding_box ( shape ) %獲取特徵點的人臉包圍盒 minshape = min(shape); maxshape = max(shape); %% calculating bounding box %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% width = maxshape(1) - minshape(1); height = maxshape(2) - minshape(2); marginW = width/2; marginH = height/2; cropmin = round(minshape - [marginW marginH]);%最左上角的點 cropmax = round(maxshape + [marginW marginH]);%最右下角的點 offset = [0 0]; if(cropmin(1)<=0) offset(1) = -cropmin(1); cropmin(1) = 1; end if(cropmin(2)<=0) offset(2) = -cropmin(2); cropmin(2) = 1; end end
normalize_first_shape.m用於正則化第一張圖片
function [shape] = normalize_first_shape( Data, options )
shape = Data.shape;
%% calculating bounding box %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
[cropmin,cropmax,offset,minshape,marginW,marginH] = bounding_box ( shape );%獲取原始圖片裁剪資訊
%% calculate scale factor %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
W_H = cropmax - cropmin;
wh1 = W_H(1);
wh2 = W_H(2);
CanvasSize = options.canvasSize;
scf = CanvasSize(1)/wh1;
if(scf*wh2 > CanvasSize(2))
scf = CanvasSize(2)/wh2;
end
%% croping image (for debug only) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
debug = 0;
if debug
img = imread(Data.img);
cropImage = img(cropmin(2):cropmax(2), cropmin(1):cropmax(1));
scaleImage = imresize(cropImage, scf);
end
%% scale shape and image %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%調整對應的人臉特徵點
shape = shape - repmat((minshape - [marginW marginH] + offset) ...
, size(shape, 1), 1);
shape = shape*scf;
if debug
% Displaying image and feature points.
figure(1);
imshow(scaleImage);
hold on;
plot(shape(:, 1), shape(:, 2), 'g*');
pause;
end
end
利用第一張正則化特徵點來正則化其他圖片的特徵點
function [shape,img] = normalize_rest_shape ( ref, data, options )
cvw = options.canvasSize(1);
cvh = options.canvasSize(2);
base = ref.shape;
shape = data.shape;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Use procrustes analysis to align shape.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%利用普氏分析法將其他圖片與第一張圖片對齊,從而獲得平均形狀
[d, z, tform] = procrustes(base, shape, 'Reflection',false);
%% normaling shape %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
debug = 0;
if debug
Trans = -1/tform.b*tform.c*tform.T';
Trans = Trans(1, :);
transM = [1/tform.b*tform.T Trans'];
cvXY = [1 cvw 1 cvw;
1 1 cvh cvh];
img = im2double(rgb2gray(imread(data.img)));
normImg = quad2Box(img, cvXY, transM);
figure(1);
imshow(normImg);
hold on;
plot(z(:, 1), z(:, 2), 'r.');
pause;
end
shape = z;
end
頂層歸一化函式
function Data = normalize_data ( Data, options )
n = length(Data);
%% noramlizing the first image
%正則化第一張圖片
shape = normalize_first_shape( Data(1) , options );
Data(1).shape = shape;
%% using the first to noramlizing others.
%根據第一張圖片正則化第二張人臉圖片
for i = 2 : n
[shape] = normalize_rest_shape( Data(1), Data(i), options );
Data(i).shape = shape;
end
end
形狀學習,獲取平均形狀
function do_learn_shape ( options )
%學習人臉的平均形狀
%% locating data folders %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
imageDataPath = options.trainingImageDataPath;%訓練資料的路徑
truthDataPath = options.trainingTruthDataPath;%訓練資料的標籤
%% loading training data %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Data = load_data( imageDataPath, truthDataPath, options );%載入圖片和標籤
%% normalizing training data %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Data = normalize_data( Data, options );%歸一化人臉形狀
%% shape model training %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ShapeModel = build_shape_model( Data );%對歸一化後的人臉特徵點求平均形狀
if ~exist( options.modelPath , 'dir' )
mkdir( options.modelPath );
end
%儲存人臉的平均形狀
save([options.modelPath options.slash options.datasetName ...
'_ShapeModel.mat'], 'ShapeModel');
clear Data;
clear ShapeModel;
下面的效果圖是通過主成分分析歸一化後的人臉形狀獲得的平均形狀
若有不當之處,請指教