1. 程式人生 > >face alignment by 3000 fps系列學習總結(三)

face alignment by 3000 fps系列學習總結(三)

訓練

總體目標

  • 我們需要為68個特徵點的每一個特徵點訓練5棵隨機樹,每棵樹4層深,即為所謂的隨機森林。

開始訓練

  1. 分配樣本
    事實上,對於每個特徵點,要訓練隨機森林,我們需要從現有的樣本和特徵中抽取一部分,訓練成若干個樹。
    現在,我們有N(此處N=1622)個樣本(圖片和shape)和無數個畫素差特徵。訓練時,對於每棵樹,我們從N個樣本採取有放回抽樣的方法隨機選取若干樣本,再隨機選取M個特徵點。然後使用這些素材加以訓練。這是一般的方法。不過為了簡化,我們將N個樣本平均分成5份,且允許彼此之間有重疊。然後分配好的樣本用來作為68個特徵點的共同素材。

示意圖


程式碼:

    dbsize = length(Tr_Data);

% rf = cell(1, params.max_numtrees);

overlap_ratio = params.bagging_overlap;%重疊比例

Q = floor(double(dbsize)/((1-params.bagging_overlap)*(params.max_numtrees))); %每顆樹分配的樣本個數

Data = cell(1, params.max_numtrees); %為訓練每棵樹準備的樣本資料
for t = 1:params.max_numtrees
    % calculate the number of
samples for each random tree % train t-th random tree is = max(floor((t-1)*Q - (t-1)*Q*overlap_ratio + 1), 1); ie = min(is + Q, dbsize); Data{t} = Tr_Data(is:ie); end

2.隨機森林訓練全程

程式碼:

% divide local region into grid
params.radius = ([0:1/30:1]');
params.angles = 2*pi*[0:1/36:1]'
; rfs = cell(length(params.meanshape), params.max_numtrees); %隨機森林的大小為68*5 %parfor i = 1:length(params.meanshape) for i = 1:length(params.meanshape) rf = cell(1, params.max_numtrees); disp(strcat(num2str(i), 'th landmark is processing...')); for t = 1:params.max_numtrees % disp(strcat('training', {''}, num2str(t), '-th tree for', {''}, num2str(lmarkID), '-th landmark')); % calculate the number of samples for each random tree % train t-th random tree is = max(floor((t-1)*Q - (t-1)*Q*overlap_ratio + 1), 1); %樣本的序號 ie = min(is + Q, dbsize); max_numnodes = 2^params.max_depth - 1; %最大的節點數自然是滿二叉樹的節點個數 rf{t}.ind_samples = cell(max_numnodes, 1); %節點包含的樣本序號 rf{t}.issplit = zeros(max_numnodes, 1);%是否分割 rf{t}.pnode = zeros(max_numnodes, 1); rf{t}.depth = zeros(max_numnodes, 1);%當前深度 rf{t}.cnodes = zeros(max_numnodes, 2);%當前節點的左右子節點序號 rf{t}.isleafnode = zeros(max_numnodes, 1); %判斷節點是否是葉子節點 rf{t}.feat = zeros(max_numnodes, 4); %圍繞特徵點隨機選取的2個點的座標(r1,a1,r2,a2) rf{t}.thresh = zeros(max_numnodes, 1); %分割節點的閾值 rf{t}.ind_samples{1} = 1:(ie - is + 1)*(params.augnumber); %第t棵樹的樣本序號,也是根節點包含的樣本序號 rf{t}.issplit(1) = 0; rf{t}.pnode(1) = 0; rf{t}.depth(1) = 1; rf{t}.cnodes(1, 1:2) = [0 0]; rf{t}.isleafnode(1) = 1; rf{t}.feat(1, :) = zeros(1, 4); rf{t}.thresh(1) = 0; num_nodes = 1; %num_nodes為現有的節點個數 num_leafnodes = 1;%num_leafnodes為現有的葉子節點個數 stop = 0; while(~stop) %這個迴圈用於產生隨機樹,直到沒有再可以分割的點 num_nodes_iter = num_nodes; %num_nodes為現有的節點個數 num_split = 0; %分割節點的個數 for n = 1:num_nodes_iter if ~rf{t}.issplit(n) %如果第t棵樹第n個節點已經分過,就跳過去 if rf{t}.depth(n) == params.max_depth % || length(rf{t}.ind_samples{n}) < 20 if rf{t}.depth(n) == 1 %應該去掉吧???????????????? rf{t}.depth(n) = 1; end rf{t}.issplit(n) = 1; else % separate the samples into left and right path [thresh, feat, lcind, rcind, isvalid] = splitnode(i, rf{t}.ind_samples{n}, Data{t}, params, stage); %{ if ~isvalid rf{t}.feat(n, :) = [0 0 0 0]; rf{t}.thresh(n) = 0; rf{t}.issplit(n) = 1; rf{t}.cnodes(n, :) = [0 0]; rf{t}.isleafnode(n) = 1; continue; end %} % set the threshold and featture for current node rf{t}.feat(n, :) = feat; rf{t}.thresh(n) = thresh; rf{t}.issplit(n) = 1; rf{t}.cnodes(n, :) = [num_nodes+1 num_nodes+2]; %當前節點的左右子節點序號 rf{t}.isleafnode(n) = 0; % add left and right child nodes into the random tree rf{t}.ind_samples{num_nodes+1} = lcind; rf{t}.issplit(num_nodes+1) = 0; rf{t}.pnode(num_nodes+1) = n; rf{t}.depth(num_nodes+1) = rf{t}.depth(n) + 1; rf{t}.cnodes(num_nodes+1, :) = [0 0]; rf{t}.isleafnode(num_nodes+1) = 1; rf{t}.ind_samples{num_nodes+2} = rcind; rf{t}.issplit(num_nodes+2) = 0; rf{t}.pnode(num_nodes+2) = n; rf{t}.depth(num_nodes+2) = rf{t}.depth(n) + 1; rf{t}.cnodes(num_nodes+2, :) = [0 0]; rf{t}.isleafnode(num_nodes+2) = 1; num_split = num_split + 1; %分割節點的次數,實際上一層分割節點的個數 num_leafnodes = num_leafnodes + 1; num_nodes = num_nodes + 2; end end end if num_split == 0 stop = 1; else rf{t}.num_leafnodes = num_leafnodes; rf{t}.num_nodes = num_nodes; rf{t}.id_leafnodes = find(rf{t}.isleafnode == 1); end end end % disp(strcat(num2str(i), 'th landmark is over')); rfs(i, :) = rf; end

3.分裂節點全程
流程圖:

程式碼:

function [thresh, feat, lcind, rcind, isvalid] = splitnode(lmarkID, ind_samples, Tr_Data, params, stage)

if isempty(ind_samples)
    thresh = 0;
    feat = [0 0 0 0];
    rcind = [];
    lcind = [];
    isvalid = 1;
    return;
end

% generate params.max_rand cndidate feature
% anglepairs = samplerandfeat(params.max_numfeat);
% radiuspairs = [rand([params.max_numfeat, 1]) rand([params.max_numfeat, 1])];
[radiuspairs, anglepairs] = getproposals(params.max_numfeats(stage), params.radius, params.angles);

angles_cos = cos(anglepairs);
angles_sin = sin(anglepairs);

% extract pixel difference features from pairs

pdfeats = zeros(params.max_numfeats(stage), length(ind_samples)); %所有的樣本均要提取相應階段的畫素差特徵,即比如說1000*541

shapes_residual = zeros(length(ind_samples), 2);

for i = 1:length(ind_samples)
    s = floor((ind_samples(i)-1)/(params.augnumber)) + 1; %共用樣本的序號
    k = mod(ind_samples(i)-1, (params.augnumber)) + 1; %不能共用盒子,而是對於同一張圖片的不同shape使用各自的盒子,使用餘運算,顯然小於params.augnumber,又加1,所以答案從1:params.augnumber

    % calculate the relative location under the coordinate of meanshape %x1=angles_cos(:, 1)).*radiuspairs(:, 1)
    pixel_a_x_imgcoord = (angles_cos(:, 1)).*radiuspairs(:, 1)*params.max_raio_radius(stage)*Tr_Data{s}.intermediate_bboxes{stage}(k, 3);
    pixel_a_y_imgcoord = (angles_sin(:, 1)).*radiuspairs(:, 1)*params.max_raio_radius(stage)*Tr_Data{s}.intermediate_bboxes{stage}(k, 4);

    pixel_b_x_imgcoord = (angles_cos(:, 2)).*radiuspairs(:, 2)*params.max_raio_radius(stage)*Tr_Data{s}.intermediate_bboxes{stage}(k, 3);
    pixel_b_y_imgcoord = (angles_sin(:, 2)).*radiuspairs(:, 2)*params.max_raio_radius(stage)*Tr_Data{s}.intermediate_bboxes{stage}(k, 4);

    % no transformation
    %{
    pixel_a_x_lmcoord = pixel_a_x_imgcoord;
    pixel_a_y_lmcoord = pixel_a_y_imgcoord;

    pixel_b_x_lmcoord = pixel_b_x_imgcoord;
    pixel_b_y_lmcoord = pixel_b_y_imgcoord;
    %}

    % transform the pixels from image coordinate (meanshape) to coordinate of current shape
    %以下計算出的都是中心化的座標
    [pixel_a_x_lmcoord, pixel_a_y_lmcoord] = transformPointsForward(Tr_Data{s}.meanshape2tf{k}, pixel_a_x_imgcoord', pixel_a_y_imgcoord');    
    pixel_a_x_lmcoord = pixel_a_x_lmcoord';
    pixel_a_y_lmcoord = pixel_a_y_lmcoord';

    [pixel_b_x_lmcoord, pixel_b_y_lmcoord] = transformPointsForward(Tr_Data{s}.meanshape2tf{k}, pixel_b_x_imgcoord', pixel_b_y_imgcoord');
    pixel_b_x_lmcoord = pixel_b_x_lmcoord';
    pixel_b_y_lmcoord = pixel_b_y_lmcoord';     
    %轉化為絕對座標
    pixel_a_x = int32(bsxfun(@plus, pixel_a_x_lmcoord, Tr_Data{s}.intermediate_shapes{stage}(lmarkID, 1, k)));
    pixel_a_y = int32(bsxfun(@plus, pixel_a_y_lmcoord, Tr_Data{s}.intermediate_shapes{stage}(lmarkID, 2, k)));

    pixel_b_x = int32(bsxfun(@plus, pixel_b_x_lmcoord, Tr_Data{s}.intermediate_shapes{stage}(lmarkID, 1, k)));
    pixel_b_y = int32(bsxfun(@plus, pixel_b_y_lmcoord, Tr_Data{s}.intermediate_shapes{stage}(lmarkID, 2, k)));

    width = (Tr_Data{s}.width);
    height = (Tr_Data{s}.height);

    pixel_a_x = max(1, min(pixel_a_x, width)); %意思是 pixel_a_x應該介於1和width之間
    pixel_a_y = max(1, min(pixel_a_y, height));

    pixel_b_x = max(1, min(pixel_b_x, width));
    pixel_b_y = max(1, min(pixel_b_y, height));
    %取畫素兩種方法,一是img_gray(i,j);二是img_gray(k),k是按列數第k個元素
    pdfeats(:, i) = double(Tr_Data{s}.img_gray(pixel_a_y + (pixel_a_x-1)*height)) - double(Tr_Data{s}.img_gray(pixel_b_y + (pixel_b_x-1)*height));
       %./ double(Tr_Data{s}.img_gray(pixel_a_y + (pixel_a_x-1)*height)) + double(Tr_Data{s}.img_gray(pixel_b_y + (pixel_b_x-1)*height));

    % drawshapes(Tr_Data{s}.img_gray, [pixel_a_x pixel_a_y pixel_b_x pixel_b_y]);
    % hold off;

    shapes_residual(i, :) = Tr_Data{s}.shapes_residual(lmarkID, :, k);
end

E_x_2 = mean(shapes_residual(:, 1).^2);
E_x = mean(shapes_residual(:, 1));

E_y_2 = mean(shapes_residual(:, 2).^2);
E_y = mean(shapes_residual(:, 2));
% 整體方差,其中使用了方差的經典公式Dx=Ex^2-(Ex)^2
var_overall = length(ind_samples)*((E_x_2 - E_x^2) + (E_y_2 - E_y^2));

% var_overall = length(ind_samples)*(var(shapes_residual(:, 1)) + var(shapes_residual(:, 2)));

% max_step = min(length(ind_samples), params.max_numthreshs);
% step = floor(length(ind_samples)/max_step);
max_step = 1;

var_reductions = zeros(params.max_numfeats(stage), max_step);
thresholds = zeros(params.max_numfeats(stage), max_step);

[pdfeats_sorted] = sort(pdfeats, 2); %將資料打亂順序,防止過擬合

% shapes_residual = shapes_residual(ind, :);

for i = 1:params.max_numfeats(stage) %暴力選舉法,選出最合適的feature
    % for t = 1:max_step
    t = 1;
    ind = ceil(length(ind_samples)*(0.5 + 0.9*(rand(1) - 0.5)));
        threshold = pdfeats_sorted(i, ind);  % pdfeats_sorted(i, t*step); % 
        thresholds(i, t) = threshold;
        ind_lc = (pdfeats(i, :) < threshold); %邏輯陣列
        ind_rc = (pdfeats(i, :) >= threshold);

        % figure, hold on, plot(shapes_residual(ind_lc, 1), shapes_residual(ind_lc, 2), 'r.')
        % plot(shapes_residual(ind_rc, 1), shapes_residual(ind_rc, 2), 'g.')
        % close;
        % compute 

        E_x_2_lc = mean(shapes_residual(ind_lc, 1).^2); %選出邏輯陣列中為1的那些殘差
        E_x_lc = mean(shapes_residual(ind_lc, 1));

        E_y_2_lc = mean(shapes_residual(ind_lc, 2).^2);
        E_y_lc = mean(shapes_residual(ind_lc, 2));

        var_lc = (E_x_2_lc + E_y_2_lc)- (E_x_lc^2 + E_y_lc^2);

        E_x_2_rc = (E_x_2*length(ind_samples) - E_x_2_lc*sum(ind_lc))/sum(ind_rc);
        E_x_rc = (E_x*length(ind_samples) - E_x_lc*sum(ind_lc))/sum(ind_rc);

        E_y_2_rc = (E_y_2*length(ind_samples) - E_y_2_lc*sum(ind_lc))/sum(ind_rc);
        E_y_rc = (E_y*length(ind_samples) - E_y_lc*sum(ind_lc))/sum(ind_rc);

        var_rc = (E_x_2_rc + E_y_2_rc)- (E_x_rc^2 + E_y_rc^2);

        var_reduce = var_overall - sum(ind_lc)*var_lc - sum(ind_rc)*var_rc;

        % var_reduce = var_overall - sum(ind_lc)*(var(shapes_residual(ind_lc, 1)) + var(shapes_residual(ind_lc, 2))) - sum(ind_rc)*(var(shapes_residual(ind_rc, 1)) + var(shapes_residual(ind_rc, 2)));
        var_reductions(i, t) = var_reduce;
    % end
    % plot(var_reductions(i, :));
end

[~, ind_colmax] = max(var_reductions);%尋找最大差的序號
ind_max = 1;

%{
if var_max <= 0
    isvalid = 0;
else
    isvalid = 1;
end
%}
isvalid = 1;

thresh =  thresholds(ind_colmax(ind_max), ind_max); %當前閾值

feat   = [anglepairs(ind_colmax(ind_max), :) radiuspairs(ind_colmax(ind_max), :)];

lcind = ind_samples(find(pdfeats(ind_colmax(ind_max), :) < thresh));
rcind = ind_samples(find(pdfeats(ind_colmax(ind_max), :) >= thresh));

end

問題:訓練時預設一旦可以分割節點,則必然分割成兩部分。那麼會不會出現選取一個閾值將剩餘的樣本都歸於一類呢?
說明:

如圖所示外面有一個current 座標系,裡面有mean_shape的中心化歸一化的座標。最裡面是以一個特徵點為中心取的極座標。這份程式碼取r,θ來標註在特徵點附近取到的任意兩個畫素點的座標.可以說有三個座標系(按前面順序,分別稱為座標系一、二、三)。裡面兩個座標系的尺寸一樣,但是座標原點不一樣。

假定在座標系三下,取到一畫素點座標為(x,y),而特徵點在座標系二的座標為(x0,y0),則畫素點在座標系二的座標為(x˜,y˜),則有:

(x˜,y˜)=(x,y)+(x0,y0).
又由前面一篇文章《face alignment by 3000 fps系列學習總結(二)》中間進行的相似性變換,我們知道,將當前座標由mean_shape的歸一化中心化座標轉換為current_shape的中心化座標,需要使用meanshape2tf變換。
即:
(x˜,y˜)/cR
進一步的,取中心化後得
(x˜,y˜)/cR+mean(immediateshape)=(x,y)+(x0,y0)cR+mean(immediateshape)=(x,y)cR+(x0,y0)cR+mean(immediateshape)=(x,y)cR+immediate_shape_at(x0,y0)
我們又知道:
cR=

相關推薦

face alignment by 3000 fps系列學習總結

訓練 總體目標 我們需要為68個特徵點的每一個特徵點訓練5棵隨機樹,每棵樹4層深,即為所謂的隨機森林。 開始訓練 分配樣本 事實上,對於每個特徵點,要訓練隨機森林,我們需要從現有的樣本和特徵中抽取一部分,訓練成若干個樹

敏捷開發系列學習總結10——到底什麼是敏捷開發?

1,提要 軟體開發是一個系統工程,包括最初的可行性分析、再到設計、開發、測試、維護等整個生命週期。在這個過程中某些階段的失誤或說是變化,都可能增加整個軟體專案的風險。 如何在保證效率的基礎上還能安計劃

敏捷開發系列學習總結11——Scrum敏捷開發流程的個角色、四個會議和個物件

Scrum敏捷開發流程主要包擴三個角色、四個會議和個三物件。 三個角色 Scrum團隊中包括三個角色,他們分別是產品負責人、開發團隊和 專案的直接管理者(Scrum Master)。 Scrum 團隊是自組織、跨職能的完整團隊。自組織團隊決定如何最好地完成他們的工作

敏捷開發系列學習總結2——Bug修改流程

原則,力求各司其職,簡單明瞭。 1. 測試人員提交bug ⑴ 標題: [ 模組名稱 ] 問題描述 ⑵ 內容: 問題重現步驟的描述,最好貼上圖片。 因為一圖勝萬言。 ⑶ 指定責任人: 根據bug指定責任人。如果不能確定責任人,就指定給專案負責人。 2. 責任人檢

敏捷開發系列學習總結6——你用什麼工具管理專案

在開發專案時,哪些東西需要被管理?1,當然是需求、設計說明。2,介面原型。3,專案進度。4,bug。我團隊目前就是這些資料需要被管理。讀者們有其他的好東東,就在評論裡分享吧。這些資料跟git上儲存的程式碼不同,大多是文件式的。專案進度和bug的管理,這些就屬於開發流程管理

敏捷開發系列學習總結8——創業公司研發團隊建設

小編從小就是個喜歡挑戰、喜歡折騰的人。我一直認為,寧做餓死創業狼,不做養肥打工狗。小編國內某著名重點高校計算機小碩,畢業後在世界著名500強做碼農。碼了幾年後,蘊藏於小編心底的創業激情就按捺不住了,於是小編裸辭,單槍匹馬出來闖江湖。 創業,真心是不容易的。媒體上天天看到

敏捷開發系列學習總結9——10大流行程式設計方法

過去,幾乎所有的軟體開發專案都採用瀑布模型。這種程式設計方法酷似工廠裝配線,要求開發人員完成一個開發階段,之後才能進入到下一個階段。這種方法高度結構化,但是專案需求有變化時,它就不適用了。 近些年來

JSP學習總結

vol actor time 為什麽 pso ack sta instance 9.png 四、為什麽jsp就是servlet?   打開Tomcat服務器的work目錄,找到jsp文件翻譯的java文件。類聲明如下 package org.apache.jsp; im

springMVC學習總結數據綁定

springmvc core nts 循環 ack sta attribute servle 設置 springMVC學習總結(三)數據綁定 一、springMVC的數據綁定,常用綁定類型有: 1、servlet三大域對象: HttpServletRequest Http

JavaSE學習總結——Java語言編程練習、格式化字符與常量

數據 nts 編程 () 功能 替換 pri stream 第幾天 目錄 一、變量、常量、字面量 二、銀行利率為5%,問存款100美元5年的收益細節? 三、格式化 3.1、printf格式化輸出 3.2、String.format 3.2.1、日期類型 3.2.2、

springMVC學習總結 --springMVC重定向

form mit 簡單 訪問 intern dir html isp pack 根據springMVC學習總結(一) --springMVC搭建搭建項目 在com.myl.controller包下創建一個java類WebController。 在jsp子文件夾下創建一個視

OO學習總結

簡單 manager ets types mat UC requires represent 出現 規格化設計 軟件工程的重要目標之一是實現軟件開發過程各階段的自動化,軟件自動化的前提是形式化,包括軟件需求規格的形式化、軟件設計規格的形式化和 算法描述的形式化。 Z語言由牛

python學習總結,python的變量類型

變量 對象的引用 ict asr 字符串連接 number 包括 區別 通用 1.python中每個變量的申賦值都不需要類型聲明,每個變量在內存中創建都包括變量的標識、名稱和數據等信息。 2. 每個變量在使用前都必須賦值,變量賦值後該變量才會被創建。 3. 允許同時為多個變

[學習總結] python語言學習總結

函式閉包 定義 延伸了作用域的函式(能訪問定義體之外定義的非全域性變數 作用 共享變數的時候避免使用了不安全的全域性變數 允許將函式與某些資料關聯起來,類似於簡化版面向物件程式設計 相同程式碼每次生成的閉包,其延伸的作用域都彼此獨立(計數器,登錄檔) 函式的一部分行為在編寫時無法預知

微信開發學習總結——訊息管理2-接受普通訊息和被動回覆使用者訊息

上一節內容: 微信開發學習總結(三)——訊息管理(1) https://blog.csdn.net/qq_29914837/article/details/82903594 訊息管理具有的各個子模組功能,現在我們將一個詳細介紹如何使用 一、接受普通訊息介面介紹 1.1

微信開發學習總結——訊息管理1

上一節內容: 微信開發學習總結(二)——微信開發環境準備(2) https://blog.csdn.net/qq_29914837/article/details/82896861 接收普通訊息 當普通微信使用者向公眾賬號發訊息時,微信伺服器將POST訊息的XML資料包到開

c++學習總結——類與物件

一、心得感悟     c語言的課程學習後,開始c++的學習,首先就是學習類。在學習類時,類的使用與c語言有著極大的差別,一開始學習十分別扭。c語言的學習直接定義幾個形參、函式就可以寫程式了;而到了c++學習,關於類,首先必須定義類。具有相同性質和功能的東西構成的集合,通常歸成一

JAVASE8流庫Stream學習總結

3、聚合(終止流操作) 前面我們已經看到過如何建立流和轉換流了,現在是時候讓流終止,並返回些有用的東西給我們了,這個過程就叫做聚合, 也叫約簡。 一、Optional類 講到這個,我們先從 Optional類講起,什麼是Optional類,O

Linux 學習總結

一. yum 命令 1.列出所有可更新的軟體清單命令:yum check-update 2.更新所有軟體命令:yum update 3.僅安裝指定的軟體命令:yum install <package_name> 4.僅更新指定的軟體命令:yum update <package_nam

資料庫學習總結——新增、更新與刪除資料

新增、更新與刪除資料 新增資料 更新資料 刪除資料 新增資料 insert語句中指定欄位名 insert into 表名(欄位名1,欄位名2,…) values (值1,值2,…); 注意:欄位名與欄位值的順序,型別必須互相匹