画像の分散・共分散行列を求める

教師付き分類を行うときに最尤法を用いる場合、
マハラノビス距離を計算するために教師データの分散・共分散行列が必要になります。

チャンネル数Kの画像のチャンネル間の分散・共分散行列は以下の式で表されます。


ここで、対角成分はそれぞれのチャンネルの分散になります。
Mは各チャンネルの平均ベクトルです。
また、分散・共分散行列は対称行列です。

これを用いて入力画像の分散・共分散行列を求めるには以下のようにします。


/**
 * 入力画像の分散・共分散行列を求める
 *
 * @param btImg 入力画像
 * @param nRow  画像の縦のピクセル数
 * @param nCol  画像の横のピクセル数
 * @param nCh   画像のチャンネル数
 * @param dMeanVect 各チャンネルの平均ベクトル、あらかじめ確保済み
 * @param dCovMat   分散・共分散行列、確保済み
 */

void calc_cov( BYTE *btImg, int nRow, int nCol, int nCh,
        double *dMeanVect, double *dCovMat )
{
  int i, j, m, n;
  double dV;
  double *pdImg;

  // 初期化
  for ( i = 0; i < nCh; i++ )
  {
    dMeanVect[i] = 0.0;
    for ( j = 0; j < nCh; j++ )
      dCovMat[i*nCh+j] = 0.0;
  }

  // まず、平均ベクトルを求める
  for ( i = 0, n = 1; i < nRow*nCol; i++, n++ )
  {
    for ( j = 0; j < nCh; j++ )
    {
      dV = btImg[i*nCh+j];
      dV -= dMeanVect[j];
      dMeanVect[j] += dV / n;
    }
  }

  // 分散共分散行列を求める
  pdImg = new double[nRow*nCol*nCh];
  for ( i = 0; i < nRow*nCol; i++ )
  {
    for ( j = 0; j < nCh; j++ )
    {
      pdImg[i*nCh+j] = (double)btImg[i*nCh+j] - dMeanVect[j];
    }
  }

  for ( i = 0; i < nRow*nCol; i++ )
  {
    for ( m = 0; m < nCh; m++ )
    {
      for ( n = m; n < nCh; n++ )
      {
        dCovMat[m*nCh+n] += pdImg[i*nCh+m] * pdImg[i*nCh+n] / (nRow*nCol);
      }
    }
  }

  delete[] pdImg;

  // 一応転置位置の成分を埋める
  for ( m = 0; m < nCh; m++ )
  {
    for ( n = m+1; n < nCh; n++ )
    {
      dCovMat[n*nCh+m] = dCovMat[m*nCh+n];
    }
  }
}

参考文献

アーカイブ