Incremental PCA の実装

Candid Covariance-free Incremental Principal Component Analysis を OpenCV で、実装してみました。結果はいかほどに。アルゴリズムはこんな感じ。

u(n) = 分析する画像。ただし、n = 1,2,...
v_1(n),v_2(n),...,v_k(n) = 主要ベクトル。
l = 忘却の値。論文中では2〜4が良いと書いてある。
そして、n = 1から画像を与えていき、以下を実行する。
1. u_1(n) = u(n) 
2. for i = 1,2,...,min(k,n)
2.1 if(i=n) v_i(n) = u_i(n) のようにi番目の主要ベクトル初期化する
2.2 otherwise
2.2.1 v_i(n) = \frac{n-1-l}{n}v_i(n-1) + \frac{1+l}{n} u_i(n) u_i^T(n) \frac{v_i(n-1)}{||v_i(n-1)||}
2.2.2 u_{i+1}(n) = u_i(n) - u_i^T(n) \frac{v_i(n)}{||v_i(n)||} \frac{v_i(n)}{||v_i(n)||}

ソースも、参考までに。

IplImage** ForeExtractUsingIPCA::m_ArrImgMain; //!<主成分画像の配列
//!<ただし、[0]には擬似平均画像が入る、[1]からが主成分。
IplImage** ForeExtractUsingIPCA::m_ArrImgTmp; //!<一時画像の配列

void ForeExtractUsingIPCA::UpdateImgMain(IplImage *imgIn) //!< GRB画像を入力
{
  ///// 初期化 uchar -> float 変換
  cvConvert( imgIn, m_ArrImgTmp[ 0 ] );

  int iterateMax = m_MainMax -1;
  if( m_MainMax > m_FrameNow ) iterateMax = m_FrameNow;

  IplImage* imgA = cvCreateImage( cvSize(m_Width, m_Height), IPL_DEPTH_32F, 3 );
  IplImage* imgB = cvCreateImage( cvSize(m_Width, m_Height), IPL_DEPTH_32F, 3 );
  IplImage* imgC = cvCreateImage( cvSize(m_Width, m_Height), IPL_DEPTH_32F, 3 );
  double scalerA, scalerB, scalerC;
  double nrmV;
  double l = m_Amnesic; //!<忘却の値、2ぐらいがよい

  for( int i=0; i<=iterateMax; i++ )
  {
    if( i == m_FrameNow )
    {
      cvConvert( m_ArrImgTmp[ m_FrameNow ], m_ArrImgMain[ m_FrameNow ] );
      continue;
    }

    ///// Vi(n) = [a= (n-1-l)/n * Vi(n-1)] + [b= (1+l)/n * Ui(n)T Vi(n-1)/|Vi(n-1)| * Ui(n) ]
    nrmV = cvNorm( m_ArrImgMain[i], NULL, CV_L2 );

    scalerA = (double)(m_FrameNow-1-l) / (double)m_FrameNow;
    cvScale( m_ArrImgMain[i], imgA, scalerA );

    double dotUV = cvDotProduct( m_ArrImgTmp[i], m_ArrImgMain[i] );
    scalerB = ((double)(1+l) * dotUV) / ((double)m_FrameNow * nrmV );
    cvScale( m_ArrImgTmp[i], imgB, scalerB );

    cvAdd( imgA, imgB, m_ArrImgMain[i] );

    ///// Ui+1(n) = Ui(n) - [c= Ui(n)T Vi(n)/|Vi(n)| * Vi(n)/|Vi(n)| ]
    if( i == iterateMax || i >= m_MainMax -1 ) continue;

    nrmV = cvNorm( m_ArrImgMain[i], NULL, CV_L2 );
    dotUV = cvDotProduct( m_ArrImgTmp[i], m_ArrImgMain[i] );
    scalerC = dotUV / (nrmV * nrmV);
    cvScale( m_ArrImgMain[i], imgC, scalerC );
    cvSub( m_ArrImgTmp[i], imgC, m_ArrImgTmp[i+1] );
  }

  cvReleaseImage( &imgA );
  cvReleaseImage( &imgB );
  cvReleaseImage( &imgC );

  m_FrameNow++;
}

PCA で得られた主要ベクトルとの距離の比較をすると以下のように。
なお、画像は10080枚与えました。

いまいち収束性がなぁ。