1. 程式人生 > >從大樣本中均勻提取子集的演算法

從大樣本中均勻提取子集的演算法

在一些計算中,我們可能會遇到這樣的問題:
    我們可能會拿到一個數據量很大的樣本,但是演算法的時間對資料量很敏感而精度不太敏感,過多的資料會嚴重降低演算法的效率,此時我們可以從這個大樣本中取出一部分資料,代入演算法進行運算.比如在計算機視覺中,如果攝像機只做旋轉運動和變焦,要從兩幅檢視中的特徵點以及它們之間的單應矩陣H,非線性優化攝像機的內參數矩陣K和兩檢視之間的相對旋轉dR,必然會用到LM(Levenberg-Marquardt)優化.而LM優化對於大量的點來說,會變得很慢.這個時候,我們可以考慮從大量的特徵點中均勻地選取幾十個點作為引數傳給LM優化,以提高運算效率.同時,均勻地取樣也可以在一定程度上表徵這個大樣本,不會使精度明顯下降.

    仍然以LM優化為例,假設我們在這兩幅影象中找到了N個匹配好的特徵點對,它們構成集合S,現在我們要從這N個點對中均勻提取m個點對,m<N,構成子集S_sub.

[思路]:

    首先將這N個點對均勻分割成m份,得到每一份之間的間隔dx,然後從第一個點對開始取,每隔一個步長dx就取一個點對,直到取得m個點對.思路很簡單,但是如果dx不是整數,可能會帶來一些麻煩,需要進行一些其他處理.

[演算法]:

    1. 計算步長: dx = N/m;

    2. 計算將被取樣的第n個點對在大樣本中的位置: index(n) = n*dx = n*N/m, (0<=n<m).此時得到的index可能不是整數,所以做四捨五入,得到: index(n) = int(n*dx + 0.5), (0<=n<m);

    3. 提取子集: {S_sub(n) | S_sub(n) = S(index(n)), (0<=n<m)}.

[討論]:

    演算法其實很簡單,但是有一個問題值得討論.

    問題: 計算index時用到了四捨五入,這樣會不會導致子集中取到重複的樣本?

    我們通過計算來分析,什麼時候才可能取到重複的樣本.因為做四捨五入,只有當兩個數之差小於等於0.9的時候才可能被舍入為同一個數,所以,在做四捨五入前,只有當下列不等式成立時,index重複:

    0 <= index(n+1) - index(n) <= 0.9

因為: index(n+1) - index(n) = (n+1)*dx - n*dx = dx = N/m,

所以當 0 <= N/m <= 0.9 成立時,index重複.

又因為: m<N, 所以, N/m > 1, 故上面的不等式永遠不成立,所以這個演算法永遠不會出現index重複的情況.

[程式]:

void Sample(const vector< point2D<double> > &x1, const vector< point2D<double> > &x2,

            vector< point2D<double> > &x1_sample, vector< point2D<double> > &x1_sample,

            int sampleLen)

{

    x1_sample.clear();

    x2_sample.clear();

    int pointNum = x1.size();
    if (pointNum>sampleLen)
    {
        float dx = pointNum/(float)sampleLen;
        int nIndex = 0;
        for (int i=0; i<sampleLen; i++)
        {
            nIndex = int(i*dx + 0.5);
            x1_sample.push_back(x1[nIndex]);
            x2_sample.push_back(x2[nIndex]);
        }

    }
    else
    {
        x1_sample = x1;
        x2_sample = x2;
    }

    return;

}

    在實際應用中,我開始的時候使用全部300多個特徵點對,LM優化演算法耗時大於5秒.改用下均勻取樣之後,將點對縮減到50個左右,LM耗時約1秒.當我把取樣點降到20個時,基本可以達到實時,而計算結果並沒有明顯變差.可見從大樣本中均勻提取小樣本子集,在不影響精度的前提下,可以極大地提高運算效率.