1. 程式人生 > >頭部姿態估計 - OpenCV/Dlib/Ceres

頭部姿態估計 - OpenCV/Dlib/Ceres

基本思想

通過Dlib獲得當前人臉的特徵點,然後通過旋轉平移標準模型的特徵點進行擬合,計算標準模型求得的特徵點與Dlib獲得的特徵點之間的差,使用Ceres不斷迭代優化,最終得到最佳的旋轉和平移引數。

使用環境

系統環境:Ubuntu 18.04
使用語言:C++
編譯工具:CMake

第三方工具

Dlib:用於獲得人臉特徵點
Ceres:用於進行非線性優化
CMinpack:用於進行非線性優化 (OPTIONAL)

原始碼

https://github.com/Great-Keith/head-pose-estimation

基礎概念

旋轉矩陣

頭部的任意姿態可以轉化為6個引數(yaw, roll, pitch, tx, ty, tz),前三個為旋轉引數,後三個為平移引數。

平移引數好理解,原座標加上對應的變化值即可;旋轉引數需要構成旋轉矩陣,三個引數分別對應了繞y軸旋轉的角度、繞z軸旋轉的角度和繞x軸旋轉的角度。

具體程式碼實現我們可以通過Dlib已經封裝好的API,rotate_around_x/y/z(angle)。該函式返回的型別是dlib::point_transform_affine3d,可以通過括號進行三維的變形,我們將其封裝成一個rotate函式使用如下:

void rotate(std::vector<point3f>& points, const double yaw, const double pitch, const double roll) 
{
    dlib::point_transform_affine3d around_z = rotate_around_z(roll * pi / 180);
    dlib::point_transform_affine3d around_y = rotate_around_y(yaw * pi / 180);
    dlib::point_transform_affine3d around_x = rotate_around_x(pitch * pi / 180);
    for(std::vector<point3f>::iterator iter=points.begin(); iter!=points.end(); ++iter)
        *iter = around_z(around_y(around_x(*iter)));
}

[NOTE] 其中point3f是我自己定義的一個三維點座標型別,因為Dlib中並沒有提供,而使用OpenCV中的cv::Point3f會與dlib::point定義起衝突。定義如下:

typedef dlib::vector<double, 3> point3f;

[NOTE] Dlib中的dlib::vector不是std::vector,注意二者區分。

LM演算法

這邊不進行贅訴,建議跟著推導一遍高斯牛頓法,LM演算法類似於高斯牛頓法的進階,用於迭代優化求解非線性最小二乘問題。在該程式中使用Ceres/CMinpack封裝好的API(具體使用見後文)。

三維空間到二維平面的對映

根據針孔相機模型我們可以輕鬆的得到三維座標到二維座標的對映:
\(X^{2d}=f_x(\frac{X^{3d}}{Z^{3d}})+c_x\)
\(Y^{2d}=f_y(\frac{Y^{3d}}{Z^{3d}})+c_y\)
[NOTE] 使用上角標來表示3維座標還是2維座標,下同。
其中\(f_x, f_y, c_x, c_y\)為相機的內參,我們通過OpenCV官方提供的Calibration樣例進行獲取:
例如我的電腦所獲得的結果如下:

從圖中矩陣對應關係可以獲得對應的引數值。

#define FX 1744.327628674942
#define FY 1747.838275588676
#define CX 800
#define CY 600

[NOTE] 本程式不考慮外參。

具體步驟

獲得標準模型的特徵點

該部分可見前一篇文章:BFM使用 - 獲取平均臉模型的68個特徵點座標
我們將獲得的特徵點儲存在檔案 landmarks.txt 當中。

使用Dlib獲得人臉特徵點

該部分不進行贅訴,官方有給出了詳細的樣例。
具體可以參考如下樣例:

  • https://github.com/davisking/dlib/blob/master/examples/face_landmark_detection_ex.cpp
  • https://github.com/davisking/dlib/blob/master/examples/webcam_face_pose_ex.cpp(通過這個樣例可以學習OpenCV如何呼叫攝像頭)

其中使用官方提供的預先訓練好的模型,下載地址:http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2

具體在程式碼中使用如下:

    cv::Mat temp;
    if(!cap.read(temp))
        break;
    dlib::cv_image<bgr_pixel> img(temp);
    std::vector<rectangle> dets = detector(img);
    cout << "Number of faces detected: " << dets.size() << endl;
    std::vector<full_object_detection> shapes;
    for (unsigned long j = 0; j < dets.size(); ++j) {
        /* Use dlib to get landmarks */
        full_object_detection shape = sp(img, dets[j]);
        /* ... */
    }

其中shape.part就存放著我們通過Dlib獲得的當前人臉的特徵點二維點序列。

[NOTE] 在最後CMake配置的時候,需要使用Release版本(最重要),以及增加選項USE_AVX_INSTRUCTIONSUSE_SSE2_INSTRUCTIONS/USE_SSE4_INSTRUCTIONS,否則因為Dlib的檢測耗時較長,使用攝像頭即時擬合會有嚴重的卡頓。

使用Ceres進行非線性優化

Ceres的使用官方也提供了詳細的樣例,在此我們使用的是數值差分的方法,可參考:https://github.com/ceres-solver/ceres-solver/blob/master/examples/helloworld_numeric_diff.cc

    Problem problem;
    CostFunction* cost_function = new NumericDiffCostFunction<CostFunctor, ceres::RIDDERS, LANDMARK_NUM, 6>(new CostFunctor(shape));
    problem.AddResidualBlock(cost_function, NULL, x);
    Solver::Options options;
    options.minimizer_progress_to_stdout = true;
    Solver::Summary summary;
    Solve(options, &problem, &summary);
    std::cout << summary.BriefReport() << endl;

這裡我直接使用了數值差分的方法(NumericDiffCostFunction),而不是使用自動差分(AutoDiffCostFunction),是因為自動差分的CostFunctor是通過Template實現的,利用Template來實現Jacobian矩陣的計算使用的同一個結構,這樣的話下方旋轉矩陣就不能直接通過呼叫Dlib提供的三維座標旋轉介面,而是要將整個矩陣拆解開來實現(這邊暫時沒有細想到底能不能實現),因此出於簡便,使用數值差分,在準確性上是會受到影響的。
並且注意到,具體的方法使用了Ridders(ceres::RIDDERS),而不是向前差分(ceres::FORWARD)或者中分(ceres::CENTRAL),因為用後兩者進行處理的時候,LM演算法\(\beta_{k+1}=\beta_k-(J^TJ+\lambda I)^{-1}J^Tr)\)的更新項為0,無法進行迭代,暫時沒有想到原因,之前這裡也被卡了很久。
[NOTE] 原始碼中還有使用了CMinpack的版本,該版本不可用的原因也是使用了封裝最淺的lmdif1_呼叫(返回結果INFO=4),該版本下使用的向前差分,如果改為使用lmdif_對其中的一些引數進行調整應該是可以實現的。

CostFunctor的構建

CostFunctor的構建是Ceres,也是這個程式,最重要的部分。首先我們需要先把想要計算的式子寫出來:
\(Q=\sum_i^{LANDMARK\_NUM} \|q_i^{2d}-p_i^{2d}\|^2\)
\(Q=\sum_i^{LANDMARK\_NUM} \|q_i^{2d}-Map(R(yaw, roll, pitch)p_i^{3d}+T(t_x, t_y, t_z))\|^2\)。
其中:

  • LANDMARK_NUM:該程式中為68,因為Dlib演算法獲得的特徵點數為68;;
  • \(q_i^{2d}\):通過Dlib獲得的2維特徵點座標,大小為68的vector<dlib::point>
  • \(p_i^{2d}\):經過一系列變換得到的標準模型的2維特徵點座標,大小為68的vector<dlib::point>,具體計算方法是通過\(p_i^{2d}=Map(R(yaw, roll, pitch)(p_i^{3d})+T(t_x, t_y, t_z))\);
  • \(p_i^{3d}\):標準模型的三維3維特徵點座標,大小為68的vector<point3f>;
  • \(R(yaw, roll, pitch)\):旋轉矩陣;
  • \(T(t_x, t_y, t_z)\):平移矩陣;
  • \(Map()\):3維點轉2維點的對映,如上所描述通過相機內參獲得。
  • \(\|·\|\):因為是兩個2維點的差,我們使用歐幾里得距離來作為2點的差。
    Ceres當中的CostFunctor只需要寫入平方以內的內容,因此我們如下構建:
struct CostFunctor {
public:
    CostFunctor(full_object_detection _shape){ shape = _shape; }
    bool operator()(const double* const x, double* residual) const {
        /* Init landmarks to be transformed */
        fitting_landmarks.clear();
        for(std::vector<point3f>::iterator iter=model_landmarks.begin(); iter!=model_landmarks.end(); ++iter)
            fitting_landmarks.push_back(*iter);
        transform(fitting_landmarks, x);
        std::vector<point> model_landmarks_2d;
        landmarks_3d_to_2d(fitting_landmarks, model_landmarks_2d);

        /* Calculate the energe (Euclid distance from two points) */
        for(int i=0; i<LANDMARK_NUM; i++) {
            long tmp1 = shape.part(i).x() - model_landmarks_2d.at(i).x();
            long tmp2 = shape.part(i).y() - model_landmarks_2d.at(i).y();
            residual[i] = sqrt(tmp1 * tmp1 + tmp2 * tmp2);
        }
        return true;
    }
private:
    full_object_detection shape;    /* 3d landmarks coordinates got from dlib */
};

其中的引數x是一個長度為6的陣列,對應了我們要獲得的6個引數。

初始值的選定

當前並沒有多考慮這個因素,在landmark-fitting-cam程式中除了第一幀的初始值是提前設定好的以外,後續的初始值都是前一幀的最優值。
後面的表現都很好,但這第一幀確實會存在紊亂的情況。
因此後續優化可以考慮使用一個粗估計的初始值,因為對於這些迭代優化方法,初始值的選擇決定了會不會陷入區域性最優的情況。

測試結果

臉部效果:

輸出工作環境:

相關推薦

頭部姿態估計 - OpenCV/Dlib/Ceres

基本思想 通過Dlib獲得當前人臉的特徵點,然後通過旋轉平移標準模型的特徵點進行擬合,計算標準模型求得的特徵點與Dlib獲得的特徵點之間的差,使用Ceres不斷迭代優化,最終得到最佳的旋轉和平移引數。 使用環境 系統環境:Ubuntu 18.04 使用語言:C++ 編譯工具:CMake 第三方工具 Dlib:

重磅!頭部姿態估計「原理詳解 + 實戰程式碼」來啦!

寫在前面 經過兩週的文獻和部落格閱讀,CV_Life君終於欣(dan)喜(zhan)若(xin)狂(jing)地給各位帶來head pose estimation這篇文章,因為剛剛入手這個方向,如有疏漏請各位多多包涵,並多多指教。廢話少說,先放個Demo熱熱身。 Head Pose Estimatio

頭部姿態估計——adaptive gradient methods

《Head pose estimation in the wild using Convolutional Neural Networks and adaptive gradient methods》

頭部姿態估計——multi-loss

《Fine-Grained Head Pose Estimation Without Keypoints》 2018,Nataniel Ruiz Eunji Chong James M. Rehg.

頭部姿態估計:《Fine-Grained Head Pose Estimation Without Keypoints》

《Fine-Grained Head Pose Estimation Without Keypoints》 2018,Nataniel Ruiz Eunji Chong James M. Rehg. multi-loss 程式碼連結:https://github.com/natanielruiz/d

頭部姿態估計 - Android

概括 通過Dlib獲得當前人臉的特徵點,然後通過旋轉平移標準模型的特徵點進行擬合,計算標準模型求得的特徵點與Dlib獲得的特徵點之間的差,使用Ceres不斷迭代優化,最終得到最佳的旋轉和平移引數。 Android版本在原理上同C++版本:頭部姿態估計 - OpenCV/Dlib/Ceres。 主要介紹在移植過

使用OpenCVDlib進行人頭姿態估計

       效果圖: 在本教程中我們將學習如何估計人類的姿勢使用OpenCV和Dlib照片。 在進行本教程之前,我想指出這個帖子屬於我在面部處理中編寫的一個系列。下面的一些文章有助於理解這篇文章,而其他文章補充了這一點。 2.臉部變換 3.臉平均化 4

OpenCV】 POSIT 演算法 -- 3D姿態估計

POSIT(Pose from Orthography and Scaling with Iterations), 比例正交投影迭代變換演算法. Pose : 首先這個演算法用於估計 Pose Iterations : 其次, 這個演算法採用優化迭代的演算

基於OpenCV使用OpenPose進行多個人體姿態估計

目錄 7. 結果 之前我們使用OpenPose模型對單個人體進行姿態估計。本文討論瞭如何同時對多人體進行姿態估計。 假如圖片中具有多個人體,姿態估計會生成多個獨立的關鍵點。我們需要對關鍵點分類,找出屬於同一個人的關鍵點。 我

Dlib姿態估計——旋轉矩陣與尤拉角互轉

簡介         在這篇文章中,我將分享將一個3×3旋轉矩陣轉換成尤拉角的程式碼,反之亦然。3D旋轉矩陣可以讓你的頭旋轉。 我知道這是一個壞的雙關語,但真相有時可能是非常小的!         一個旋轉矩陣有三個自由度,數學家已經行使了他們的創造

Anaconda+linux +opencv+dlib安裝

bin 手工 末尾 wid 一個 com 技術分享 get origin 準備文件Anaconda: https://www.anaconda.com/download/ 我下載的時最新的 64-Bit (x86) Installer (524 MB) 然後在下載的an

人體姿態估計在網約車風控系統中的應用

姿態估計 卷積神經網絡 網約車公司在運營過程中經常會遇到一個問題,就是如果司機不接單,卻把車開出去接乘客,相當於開黑車。那麽系統如何發現這種行為,其中就需要人體姿態估計。 相對於傳統方法,只識別人體來數人數有一個問題,就是如果乘客之間發生遮擋,或者後排乘客沒有露出面部,就會找不到乘客。 使用人體姿態估

soft-argmax將熱點圖轉換為數值坐標應用於人體姿態估計

sum 緩解 mat 漸變 圖標 bubuko 訓練 分組 參考 人體姿態估計常用預測熱點圖的方法預測x和y的坐標值,熱點圖可以理解為概率響應圖,通過求熱點圖最大值所在位置坐標,就可以得到該關鍵點的位置坐標 熱點圖法的缺點 量化產生的精度損失:卷積網絡下采樣使模型的

相機姿態估計(七)--UPnP

論文:Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation 整體來看,UPnP跟EPnP差不多,只是同時估計了焦距,因此,適合未標定場合,Uncalibrated PnP. 問題定義:

相機姿態估計(六)--EPnP

EnP演算法,論文:EPnP: Efficient Perspective-n-Point Camera Pose Estimation 直接給過程: 相機座標系用Fc,世界座標系用Fw表示,任何一點可以用四個控制點p表示: 對於相機座標系同樣成立: 對於上面的公式,

相機姿態估計(五)--DLS

論文:A Direct Least-Squares (DLS) Method for PnP 求解過程看著挺吃力的,看懂的還請多多指正。 本文針對PnP問題,採用非線性最小二乘直接計算,主要貢獻如下: 1.提出通用的n點(n≥3)姿態(all pose)計算方法,設計了獨特的非線性最

相機姿態估計(四)--AP3P

AP3P 論文:An Efficient Algebraic Solution to the Perspective-Three-Point Problem 從數學的角度,提出了經典P3P演算法的更快,更魯棒,更準確的求解演算法。根據已知3對點,可以計算相機位姿和引數。 問題定義:

【電腦科學】【2016】單目視訊三維人體姿態估計的深度學習模型

本文為立陶宛維爾紐斯格迪米納斯技術大學(作者:Agnė Grinciūnaitė)的碩士論文,共68頁。 有一種視覺系統,它可以很容易地識別、跟蹤人體的位置、運動和行為,而不需要任何額外的感知手段。這個系統擁有一個稱為大腦的處理器,只經過幾個月的訓練就能稱職地完成以上任務。通過更多

Docker Images: Centos7 + Python3.6 + Tensorflow + Opencv + Dlib

本文件主要是構建影象處理相關專案的 docker 映象,包含:基礎映象 Centos7 ,以及所涉及的 Python3.6 + Tensorflow + Opencv + Dlib 等開發環境。 Docker Images: Centos7 + Python3.6 + Te

關於論文 姿態估計和追蹤的衡量基準 official evaluation metric

1.on PoseTrack Challenge dataset. “*” means models trained on train+validation set. Top: Results on PoseTrack valida-tion set. Bottom: Results on Po