FGDモデルの理解とcvbgfg_acmmm 2003ソースコードコメント(二)(
モデルを使用する場合は,必ず微視的に考慮し,各点を小さなモデルとして考慮しなければならない.いろいろな策略は最後にすべての点に応用されたからだ.この点の思考の変換は必ずあって、初めて図形の画像の方面を学んで、特に初めてopencvを使うのはいつも直接各図のすべての図から考える思考を身につけて、いつも策略を1つの図の上で実施するのが好きで、入門する時このようにして、すべて直接関数を使うためです.また,モデルを用いるだけでは,実際には図ごとに更新されるだけであり,グローバルから出発しているようである.しかし,モデルソースコードを読むときは,点の観点から考える.GMMのモデルも,この論文に基づいて構築されたモデルも同様である.
具体的にはこのモデルです.出発点は,後験確率に基づいて,一つの点が前の観光地であるかどうかをどのように判断するかの公式を導出することである.しかし、次の問題は確率をどのように表現するかです.採用した戦略はRGB値ヒストグラムを統計し,統計量を減らすためにまずRGBを階層化した.その後,前のN個のヒストグラムをカウントすると,計算量がさらに減少する.Nの数値はRGBの階層数に関係しているが,具体的にどのように計算するかは不明である.
前後のフレーム間の変化を考慮して、この点が動点に変化することは明らかであるが、背景のグラデーションの変化も考慮される.したがって、2つのテーブルを維持する必要があります.(この一節は気に入らないので,もう一度考えてから直す)
そこで、2つの差分を終えた後、前の観光地を判断し、更新戦略に基づいて、この時点の各点の確率を更新する.
このモデルは,点が背景なのか前景なのかの後験確率を算出できるのは,先験確率と尤度確率の2つの確率に設計されている.
先行確率としては,モデルが2つあるため,先行確率も2つある:すなわち,CvBGPixelStatにおけるPbcとPbccは,それぞれ2つのモデルに対応する.
さらに後験確率は統計にかかわるため、各モデルはそれぞれの表を保持している.表の数はN 2が必要であり、前N 1の量は毎回統計される.N 1-N 2は新しい背景を受け入れるときのバッファ領域である.更新の際、現在の点に一致する量が見つからない場合は、この点を末尾N 2に加えて並べ替えていく前、N 2が指すメモリは実際には変化しているので、新しい背景点の受け入れが完了する.現在の点と一致する点が見つかった場合、その点を取得する確率値が更新される.前スポットの出発角度であるか否かを判断する.まず、この点が背景点であると仮定し、背景点の変化範囲があまり大きくないと仮定し、その点を受け取ることができる背景領域を見つける確率(すなわち、その背景点が受け入れられる確率の和ごとに)、得られた式に基づいて計算することができる.
具体的にはこのモデルです.出発点は,後験確率に基づいて,一つの点が前の観光地であるかどうかをどのように判断するかの公式を導出することである.しかし、次の問題は確率をどのように表現するかです.採用した戦略はRGB値ヒストグラムを統計し,統計量を減らすためにまずRGBを階層化した.その後,前のN個のヒストグラムをカウントすると,計算量がさらに減少する.Nの数値はRGBの階層数に関係しているが,具体的にどのように計算するかは不明である.
前後のフレーム間の変化を考慮して、この点が動点に変化することは明らかであるが、背景のグラデーションの変化も考慮される.したがって、2つのテーブルを維持する必要があります.(この一節は気に入らないので,もう一度考えてから直す)
そこで、2つの差分を終えた後、前の観光地を判断し、更新戦略に基づいて、この時点の各点の確率を更新する.
このモデルは,点が背景なのか前景なのかの後験確率を算出できるのは,先験確率と尤度確率の2つの確率に設計されている.
先行確率としては,モデルが2つあるため,先行確率も2つある:すなわち,CvBGPixelStatにおけるPbcとPbccは,それぞれ2つのモデルに対応する.
さらに後験確率は統計にかかわるため、各モデルはそれぞれの表を保持している.表の数はN 2が必要であり、前N 1の量は毎回統計される.N 1-N 2は新しい背景を受け入れるときのバッファ領域である.更新の際、現在の点に一致する量が見つからない場合は、この点を末尾N 2に加えて並べ替えていく前、N 2が指すメモリは実際には変化しているので、新しい背景点の受け入れが完了する.現在の点と一致する点が見つかった場合、その点を取得する確率値が更新される.前スポットの出発角度であるか否かを判断する.まず、この点が背景点であると仮定し、背景点の変化範囲があまり大きくないと仮定し、その点を受け取ることができる背景領域を見つける確率(すなわち、その背景点が受け入れられる確率の和ごとに)、得られた式に基づいて計算することができる.
#define MIN_PV 1E-10
/* */
#define V_C(k,l) ctable[k].v[l]
#define PV_C(k) ctable[k].Pv
#define PVB_C(k) ctable[k].Pvb
#define V_CC(k,l) cctable[k].v[l]
#define PV_CC(k) cctable[k].Pv
#define PVB_CC(k) cctable[k].Pvb
// Function cvUpdateFGDStatModel updates statistical model and returns number of foreground regions
// parameters:
// curr_frame - current frame from video sequence
// p_model - pointer to CvFGDStatModel structure
/* */
static int CV_CDECL
icvUpdateFGDStatModel( IplImage* curr_frame, CvFGDStatModel* model )
{
int mask_step = model->Ftd->widthStep;
CvSeq *first_seq = NULL, *prev_seq = NULL, *seq = NULL; //
IplImage* prev_frame = model->prev_frame;
int region_count = 0;
int FG_pixels_count = 0;
//cvRound Lc=2 delta 2
// delta 2 256 64 0-7
int deltaC = cvRound(model->params.delta * 256 / model->params.Lc);
int deltaCC = cvRound(model->params.delta * 256 / model->params.Lcc);
int i, j, k, l;
//clear storages
cvClearMemStorage(model->storage);
cvZero(model->foreground);
// From foreground pixel candidates using image differencing
// with adaptive thresholding. The algorithm is from:
//
// Thresholding for Change Detection
// Paul L. Rosin 1998 6p
// http://www.cis.temple.edu/~latecki/Courses/CIS750-03/Papers/thresh-iccv.pdf
//
/* ( Ftd) ( Fbd)*/
cvChangeDetection( prev_frame, curr_frame, model->Ftd );
cvChangeDetection( model->background, curr_frame, model->Fbd );
/* */
for( i = 0; i < model->Ftd->height; i++ )
{
for( j = 0; j < model->Ftd->width; j++ )
{
if( ((uchar*)model->Fbd->imageData)[i*mask_step+j] || ((uchar*)model->Ftd->imageData)[i*mask_step+j] )//
{
float Pb = 0;
float Pv = 0;
float Pvb = 0;
/* ( ) */
CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
CvBGPixelCStatTable* ctable = stat->ctable;
CvBGPixelCCStatTable* cctable = stat->cctable;
uchar* curr_data = (uchar*)(curr_frame->imageData) + i*curr_frame->widthStep + j*3;
uchar* prev_data = (uchar*)(prev_frame->imageData) + i*prev_frame->widthStep + j*3;
int val = 0;
// Is it a motion pixel?
if( ((uchar*)model->Ftd->imageData)[i*mask_step+j] )//
{
if( !stat->is_trained_dyn_model ) {
val = 1;
} else {
// Compare with stored CCt vectors:
// 2.3 sum(p(vi|(b,s)) i from 1 to N1
// alpha2
// else
for( k = 0; PV_CC(k) > model->params.alpha2 && k < model->params.N1cc; k++ )
{
if ( abs( V_CC(k,0) - prev_data[0]) <= deltaCC &&
abs( V_CC(k,1) - prev_data[1]) <= deltaCC &&
abs( V_CC(k,2) - prev_data[2]) <= deltaCC &&
abs( V_CC(k,3) - curr_data[0]) <= deltaCC &&
abs( V_CC(k,4) - curr_data[1]) <= deltaCC &&
abs( V_CC(k,5) - curr_data[2]) <= deltaCC)
{
//
// ln
//
Pv += PV_CC(k); // p(vi|s)
Pvb += PVB_CC(k);// p(vi|b,s)
}
}
Pb = stat->Pbcc; //
if( 2 * Pvb * Pb <= Pv ) val = 1; // 2.2
}
}
else if( stat->is_trained_st_model ) //
{
// Compare with stored Ct vectors:
for( k = 0; PV_C(k) > model->params.alpha2 && k < model->params.N1c; k++ )
{
if ( abs( V_C(k,0) - curr_data[0]) <= deltaC &&
abs( V_C(k,1) - curr_data[1]) <= deltaC &&
abs( V_C(k,2) - curr_data[2]) <= deltaC )
{
Pv += PV_C(k);
Pvb += PVB_C(k);
}
}
Pb = stat->Pbc;
if( 2 * Pvb * Pb <= Pv ) val = 1; //
}
// Update foreground:
((uchar*)model->foreground->imageData)[i*mask_step+j] = (uchar)(val*255); // ( )
FG_pixels_count += val;//
} // end if( change detection...
} // for j...
} // for i...
//end BG/FG classification
// Foreground segmentation.
// Smooth foreground map:
//
//
//
//
if( model->params.perform_morphing ){
cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_OPEN, model->params.perform_morphing );
cvMorphologyEx( model->foreground, model->foreground, 0, 0, CV_MOP_CLOSE, model->params.perform_morphing );
}
//
if( model->params.minArea > 0 || model->params.is_obj_without_holes ){
// Discard under-size foreground regions:
//
cvFindContours( model->foreground, model->storage, &first_seq, sizeof(CvContour), CV_RETR_LIST );
//
for( seq = first_seq; seq; seq = seq->h_next )
{
CvContour* cnt = (CvContour*)seq;
if( cnt->rect.width * cnt->rect.height < model->params.minArea ||
(model->params.is_obj_without_holes && CV_IS_SEQ_HOLE(seq)) )
{
// Delete under-size contour:
prev_seq = seq->h_prev;
if( prev_seq )
{
prev_seq->h_next = seq->h_next;
if( seq->h_next ) seq->h_next->h_prev = prev_seq;
}
else
{
first_seq = seq->h_next;
if( seq->h_next ) seq->h_next->h_prev = NULL;
}
}
else
{
region_count++;
}
}
model->foreground_regions = first_seq;
cvZero(model->foreground);
//
cvDrawContours(model->foreground, first_seq, CV_RGB(0, 0, 255), CV_RGB(0, 0, 255), 10, -1);
} else {
model->foreground_regions = NULL;
}
// Check ALL BG update condition:
// N1 2.3 (5)
if( ((float)FG_pixels_count/(model->Ftd->width*model->Ftd->height)) > CV_BGFG_FGD_BG_UPDATE_TRESH )
{
for( i = 0; i < model->Ftd->height; i++ )
for( j = 0; j < model->Ftd->width; j++ )
{
CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
stat->is_trained_st_model = stat->is_trained_dyn_model = 1;
}
}
// Update background model:
//
for( i = 0; i < model->Ftd->height; i++ )
{
for( j = 0; j < model->Ftd->width; j++ )
{
CvBGPixelStat* stat = model->pixel_stat + i * model->Ftd->width + j;
CvBGPixelCStatTable* ctable = stat->ctable;
CvBGPixelCCStatTable* cctable = stat->cctable;
uchar *curr_data = (uchar*)(curr_frame->imageData)+i*curr_frame->widthStep+j*3;
uchar *prev_data = (uchar*)(prev_frame->imageData)+i*prev_frame->widthStep+j*3;
if( ((uchar*)model->Ftd->imageData)[i*mask_step+j] || !stat->is_trained_dyn_model ) //
{
float alpha = stat->is_trained_dyn_model ? model->params.alpha2 : model->params.alpha3;
float diff = 0;
int dist, min_dist = 2147483647, indx = -1;
//update Pb
// Pbcc = (1-alpha)*Pbcc + M*alpha M 0
// P(b|s)
stat->Pbcc *= (1.f-alpha);
if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
{
stat->Pbcc += alpha;
}
// Find best Vi match:
//
//
//PV_CC(k) = PV_CC(k)*(1-alpaha) + M*alpha
//PVB_CC(k) = PV_BCC(k)*(1-alpaha) + (M^B)*alpha ,
for(k = 0; PV_CC(k) && k < model->params.N2cc; k++ )
{
// Exponential decay of memory
PV_CC(k) *= (1-alpha);
PVB_CC(k) *= (1-alpha);
if( PV_CC(k) < MIN_PV ) // 0 continue k
{
PV_CC(k) = 0;
PVB_CC(k) = 0;
continue;
}
dist = 0;
// N2cc
// RGB , , 2*256/Lcc
for( l = 0; l < 3; l++ ) //
{
int val = abs( V_CC(k,l) - prev_data[l] );
if( val > deltaCC ) break;
dist += val;
val = abs( V_CC(k,l+3) - curr_data[l] );
if( val > deltaCC) break;
dist += val;
}
if( l == 3 && dist < min_dist )
{
min_dist = dist;
indx = k;
}
}
if( indx < 0 )
{ // Replace N2th elem in the table by new feature:
//
// cc
indx = model->params.N2cc - 1;
PV_CC(indx) = alpha;
PVB_CC(indx) = alpha;
//udate Vt
for( l = 0; l < 3; l++ )
{
V_CC(indx,l) = prev_data[l];
V_CC(indx,l+3) = curr_data[l];
}
}
else
{ // Update:
// ,
//
PV_CC(indx) += alpha;
if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
{
PVB_CC(indx) += alpha;
}
}
//re-sort CCt table by Pv
// , ,
// , , model->params.N2cc - 1 ,
// , 。 (indx N2cc),
for( k = 0; k < indx; k++ )
{
if( PV_CC(k) <= PV_CC(indx) )
{
//shift elements
CvBGPixelCCStatTable tmp1, tmp2 = cctable[indx];
for( l = k; l <= indx; l++ )
{
tmp1 = cctable[l];
cctable[l] = tmp2;
tmp2 = tmp1;
}
break;
}
}
float sum1=0, sum2=0;
//check "once-off" changes
// ,
// 3.4.1.2
// onec-off
for(k = 0; PV_CC(k) && k < model->params.N1cc; k++ )
{
sum1 += PV_CC(k);
sum2 += PVB_CC(k);
}
if( sum1 > model->params.T ) stat->is_trained_dyn_model = 1;
diff = sum1 - stat->Pbcc * sum2;
// Update stat table:
//
if( diff > model->params.T )
{
//printf("once off change at motion mode
");
//new BG features are discovered
for( k = 0; PV_CC(k) && k < model->params.N1cc; k++ )
{
PVB_CC(k) =
(PV_CC(k)-stat->Pbcc*PVB_CC(k))/(1-stat->Pbcc);
}
assert(stat->Pbcc<=1 && stat->Pbcc>=0);
}
}
// Handle "stationary" pixel:
/*
*/
if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] )
{
float alpha = stat->is_trained_st_model ? model->params.alpha2 : model->params.alpha3;
float diff = 0;
int dist, min_dist = 2147483647, indx = -1;
//update Pb
stat->Pbc *= (1.f-alpha);
if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
{
stat->Pbc += alpha;
}
//find best Vi match
for( k = 0; k < model->params.N2c; k++ )
{
// Exponential decay of memory
PV_C(k) *= (1-alpha);
PVB_C(k) *= (1-alpha);
if( PV_C(k) < MIN_PV )
{
PV_C(k) = 0;
PVB_C(k) = 0;
continue;
}
dist = 0;
for( l = 0; l < 3; l++ )
{
int val = abs( V_C(k,l) - curr_data[l] );
if( val > deltaC ) break;
dist += val;
}
if( l == 3 && dist < min_dist )
{
min_dist = dist;
indx = k;
}
}
if( indx < 0 )
{//N2th elem in the table is replaced by a new features
indx = model->params.N2c - 1;
PV_C(indx) = alpha;
PVB_C(indx) = alpha;
//udate Vt
for( l = 0; l < 3; l++ )
{
V_C(indx,l) = curr_data[l];
}
} else
{//update
PV_C(indx) += alpha;
if( !((uchar*)model->foreground->imageData)[i*mask_step+j] )
{
PVB_C(indx) += alpha;
}
}
//re-sort Ct table by Pv
for( k = 0; k < indx; k++ )
{
if( PV_C(k) <= PV_C(indx) )
{
//shift elements
CvBGPixelCStatTable tmp1, tmp2 = ctable[indx];
for( l = k; l <= indx; l++ )
{
tmp1 = ctable[l];
ctable[l] = tmp2;
tmp2 = tmp1;
}
break;
}
}
// Check "once-off" changes:
float sum1=0, sum2=0;
for( k = 0; PV_C(k) && k < model->params.N1c; k++ )
{
sum1 += PV_C(k);
sum2 += PVB_C(k);
}
diff = sum1 - stat->Pbc * sum2;
if( sum1 > model->params.T ) stat->is_trained_st_model = 1;
// Update stat table:
if( diff > model->params.T )
{
//printf("once off change at stat mode
");
//new BG features are discovered
for( k = 0; PV_C(k) && k < model->params.N1c; k++ )
{
PVB_C(k) = (PV_C(k)-stat->Pbc*PVB_C(k))/(1-stat->Pbc);
}
stat->Pbc = 1 - stat->Pbc;
}
} // if !(change detection) at pixel (i,j)
// Update the reference BG image:
if( !((uchar*)model->foreground->imageData)[i*mask_step+j])
{
uchar* ptr = ((uchar*)model->background->imageData) + i*model->background->widthStep+j*3;
if( !((uchar*)model->Ftd->imageData)[i*mask_step+j] &&
!((uchar*)model->Fbd->imageData)[i*mask_step+j] )
{
// Apply IIR filter:
for( l = 0; l < 3; l++ )
{
int a = cvRound(ptr[l]*(1 - model->params.alpha1) + model->params.alpha1*curr_data[l]);
ptr[l] = (uchar)a;
//((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l]*=(1 - model->params.alpha1);
//((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] += model->params.alpha1*curr_data[l];
}
}
else
{
// Background change detected:
for( l = 0; l < 3; l++ )
{
//((uchar*)model->background->imageData)[i*model->background->widthStep+j*3+l] = curr_data[l];
ptr[l] = curr_data[l];
}
}
}
} // j
} // i
// Keep previous frame:
cvCopy( curr_frame, model->prev_frame );
return region_count;
}
/* End of file. */