パーティクルフィルタ

本物のページはこちら→particle-filter

Included page "particle-filter" does not exist (create it now)

パーティクルフィルタとは?

画像の中で物体を追従するのにつかう。

アルゴリズムをおおざっぱに説明すると、

画像の中のある特定の小さな四角い領域を決めて
そこを基準に、ランダムに点(パーティクル)を置く。
そのランダムに置いた点を基準に更に、四角い領域の画素値を調べて、探したい画像をどのくらい類似しているのかを計算する。(いわゆる、類似度の計算というやつ)
各パーティクルの類似度を重みとして、次に置く四角形を決める。
この、四角形で、物体を追従することになる。
というかんじ。

類似度の計算

画像をグレースケールにしてヒストグラムを計算して比較するのが一般的。
私的にはなんでRGBまるごと計算しないんだって思うけど、
速度的な問題なのだと思う。
当時OpenCVが難しいと思っていた私は以下のようなハードコードを書いた。

void GetHistgram(CPoint& _pt, float* _histgram)
{
    memset(_histgram,0,256);
    for(int y=-TESTAREA;y<TESTAREA;y++){
        for(int x=-TESTAREA;x<TESTAREA;x++){
        int luminance=mTextureDataPointer[(TEXY-(_pt.y+y)-1)*TEXY+(_pt.x+x)];//ここでエラー
            _histgram[luminance]+=1.0f;
        }
    }
    //ヒストグラムが足して1になるよう正規化する。
    float maxvalue=0;//0より小さい値はないはず。
    for(int i=0;i<256;i++){
        if(maxvalue < _histgram[i]){
            maxvalue=_histgram[i];
        }
    }
    for(int i=0;i<256;i++){
        _histgram[i]/=maxvalue;
    }
 
}

TESTAREA
小さい四角形の領域
mTextureDataPointer
画像データ
CPoint
パーティクルの座標

ヒストグラムから類似度を算出する

比べたい画像のヒストグラム同士をかけ算して、その結果を積算していくのが一般的。
数式で書くとこうである。

(1)
\begin{align} S=\sum^{n}_{i=0} H(i) \end{align}
S
類似度 similarity
H(i)
輝度iがその画像で出現した回数 ヒストグラム関数
int CalcSimilarity(float* _srcHistgram){
        mSimilarity=0;
        for(int i=THRESHOLD;i<256;i++){
            mSimilarity+=(mHistgram[i]*_srcHistgram[i]);
        }
        return mSimilarity;
    }

パーティクルの配置

void COpenGLControl::RefineParticles(void)//平均値以下の粒子を捨て、新しい粒子を入れる
{
    mSimilarityAverage=(mSimilaritySam/(float)PARTICLENUM)/mSimilaritySam;
    std::sort(mParticles.begin(),mParticles.end());
    srand( GetTickCount(  ) );
    for(int i=PARTICLENUM/8;i<PARTICLENUM;i++){
        //if(mParticles[i].mSimilarity<mSimilarityAverage){//平均値以上のものは保存し、平均値以下のものは消して
            //新しいターゲットを中心としたランダム位置の粒子にする
            float a=rand()/(float)RAND_MAX;//0〜RAND_MAX RAND_MAXはコンパイラによって違う。
            float b=rand()/(float)RAND_MAX;
            mParticles[i].mPt.x=mTarget.mPt.x+(int)((float)TESTAREA*2*sqrt(-2.0*log(a))*cos(2.0*M_PI*b));
            mParticles[i].mPt.y=mTarget.mPt.y+(int)((float)TESTAREA*2*sqrt(-2.0*log(a))*sin(2.0*M_PI*b));
            if(mParticles[i].mPt.y<0 || mParticles[i].mPt.x<0){assert(!"0以下な数字!!");}
        //}
    }
 
}
void COpenGLControl::CalcSimilarity(void)
{
    float maximum=-FLT_MAX;
    float minimum=FLT_MAX;
     maxindex=0;
     minindex=0;
     mSimilaritySam=0;//類似度の合計値(あとで正規化するのに使用する)
     mSimilarityAverage;//類似度の平均値(あとで平均値以下を切り捨てるのに使う)
    //ランダムに散らした後、各点を中心にした8×8エリアのヒストグラムを取る。
    //ヒストグラムを利用した類似度
     for(int i=0;i<PARTICLENUM;i++){
        // mParticles[i].GenerateRects();
        // SavePartOfBitmap(mParticles[i].mRect,mParticles[i].mBitmap);//
        GetHistgram(mParticles[i].mPt,mParticles[i].mHistgram);
        mParticles[i].CalcSimilarity(mTarget.mHistgram/*,mTarget.mBitmap*/);//類似度の計算
        if(maximum<mParticles[i].mSimilarity){
            maximum=mParticles[i].mSimilarity;
            maxindex=i;
        }
        if(minimum>mParticles[i].mSimilarity){
            minimum=mParticles[i].mSimilarity;
            minindex=i;
        }
 
        mSimilaritySam+=mParticles[i].mSimilarity;
    }
 
    mTopParticle.mPt.x=mParticles[maxindex].mPt.x;//黄色
    mTopParticle.mPt.y=mParticles[maxindex].mPt.y;
    mBottomParticle.mPt.x=mParticles[minindex].mPt.x;//水色
    mBottomParticle.mPt.y=mParticles[minindex].mPt.y;
    mTopParticle.Refresh(mParticles[maxindex].mPt.x,mParticles[maxindex].mPt.y);
    mBottomParticle.Refresh(mParticles[minindex].mPt.x,mParticles[minindex].mPt.y);
 
}

サポートサイト Wikidot.com