3D手勢識別(三)使用PCA尋找手的主方向:PCA的原理和實現

爲了確定指尖位置,首先將手的3D點雲進行PCA,再在主方向上找最前點。


1、PCA原理:

PCA (主成分分析)算法提供了一種壓縮數據的方式。我們也可以將 PCA 視爲學習數據表示的無監督學習算法。 PCA 學習一種比原始輸入維數更低的表示。它也學習了一種元素之間彼此沒有線性相關的表示。這是學習表示中元素統計獨立標準的第一步。要實現完全獨立性,表示學習算法也必須去掉變量間的非線性關係。

PCA 將輸入 x 投影表示成 z,學習數據的正交線性變換,這種表示其實對應着數據的第一個主要成分。因此,我們可以用 PCA 作爲保留數據儘可能多信息的降維方法(再次就最小重構誤差平方而言)。

PCA 學習一種線性投影,使最大方差的方向和新空間的軸對齊。(左) 原始數據包含了x的樣本。在這個空間中,方差的方向與軸的方向並不是對齊的。(右) 變換過的數據 z = x^{^{T}} W 在軸 z 1 的方向上有最大的變化。第二大變化方差的方向沿着軸 z 2 。

主成分元素特點:彼此無關。將數據 x 投影到 z 時,得到的數據表示的協方差矩陣是對角的(證明略),立刻可得 z 中的元素是彼此無關的。

2、PCA實現

使用了Eigen矩陣相關函數和深度圖與世界座標轉化函數(見上上節),代碼如下:

//depth爲輸入三維深度圖,silhouette爲二維掩碼,選取該x,y範圍的深度圖

//hand_dir爲返回主成分向量,已轉化到世界座標系下,第一主成分爲hand_dir.x(),第二主成分爲hand_dir.y(),第三主成分爲hand_dir.z()

Eigen::Vector3f CircleActionImpl::CalcHandDirFromPCA(const cv::Mat &depth, const cv::Mat silhouette, Eigen::Vector3f &hand_dir) {
    const unsigned short *dep_ptr;
    const unsigned char *sil_ptr;
    int width = depth.cols;
    int height = depth.rows;
    static std::vector<Eigen::Vector3f> points_pca;
    points_pca.clear();
    Eigen::Vector3f hand_centre;
    hand_centre.setZero();
    for (int i = 0; i < height; i++) {
        dep_ptr = depth.ptr<unsigned short>(i);
        sil_ptr = silhouette.ptr<unsigned char>(i);
        for (int j = 0; j < width; j++) {
            if (*(sil_ptr++) == 255) {
                points_pca.push_back(Depth2World(j, i, *(dep_ptr + j)));
                hand_centre += points_pca.back();
            }
        }
    }

    if (points_pca.size() == 0) return Eigen::Vector3f::Zero();
    hand_centre /= points_pca.size();
    Eigen::Map<Eigen::Matrix3Nf> points_mat(points_pca[0].data(), 3, points_pca.size());
    Eigen::Matrix3f cov = points_mat * points_mat.adjoint();
    Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eig(cov);

    hand_dir = eig.eigenvectors().col(1);
    if (hand_dir[0] < 0)
        hand_dir = -hand_dir;

    return hand_centre;

}

得到的手的主方向實驗結果正確,正負不確定,假設手指衝攝像頭方向,則在主方向上找最前點可得指尖座標。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章