This source file includes following definitions.
- initialize
- getHistory
- setHistory
- getNSamples
- setNSamples
- getkNNSamples
- setkNNSamples
- getDist2Threshold
- setDist2Threshold
- getDetectShadows
- setDetectShadows
- getShadowValue
- setShadowValue
- getShadowThreshold
- setShadowThreshold
- write
- read
- _cvUpdatePixelBackgroundNP
- _cvCheckPixelBackgroundNP
- icvUpdatePixelBackgroundNP
- apply
- getBackgroundImage
- createBackgroundSubtractorKNN
#include "precomp.hpp"
namespace cv
{
static const int defaultHistory2 = 500;
static const int defaultNsamples = 7;
static const float defaultDist2Threshold = 20.0f*20.0f;
static const unsigned char defaultnShadowDetection2 = (unsigned char)127;
static const float defaultfTau = 0.5f;
class BackgroundSubtractorKNNImpl : public BackgroundSubtractorKNN
{
public:
BackgroundSubtractorKNNImpl()
{
frameSize = Size(0,0);
frameType = 0;
nframes = 0;
history = defaultHistory2;
nN = defaultNsamples;
nkNN=MAX(1,cvRound(0.1*nN*3+0.40));
fTb = defaultDist2Threshold;
bShadowDetection = 1;
nShadowDetection = defaultnShadowDetection2;
fTau = defaultfTau;
name_ = "BackgroundSubtractor.KNN";
}
BackgroundSubtractorKNNImpl(int _history, float _dist2Threshold, bool _bShadowDetection=true)
{
frameSize = Size(0,0);
frameType = 0;
nframes = 0;
history = _history > 0 ? _history : defaultHistory2;
nN = defaultNsamples;
nkNN=MAX(1,cvRound(0.1*nN*3+0.40));
fTb = _dist2Threshold>0? _dist2Threshold : defaultDist2Threshold;
bShadowDetection = _bShadowDetection;
nShadowDetection = defaultnShadowDetection2;
fTau = defaultfTau;
name_ = "BackgroundSubtractor.KNN";
}
~BackgroundSubtractorKNNImpl() {}
void apply(InputArray image, OutputArray fgmask, double learningRate=-1);
virtual void getBackgroundImage(OutputArray backgroundImage) const;
void initialize(Size _frameSize, int _frameType)
{
frameSize = _frameSize;
frameType = _frameType;
nframes = 0;
int nchannels = CV_MAT_CN(frameType);
CV_Assert( nchannels <= CV_CN_MAX );
int size=frameSize.height*frameSize.width;
bgmodel.create( 1,(nN * 3) * (nchannels+1)* size,CV_8U);
aModelIndexShort.create(1,size,CV_8U);
aModelIndexMid.create(1,size,CV_8U);
aModelIndexLong.create(1,size,CV_8U);
nNextShortUpdate.create(1,size,CV_8U);
nNextMidUpdate.create(1,size,CV_8U);
nNextLongUpdate.create(1,size,CV_8U);
nShortCounter = 0;
nMidCounter = 0;
nLongCounter = 0;
aModelIndexShort = Scalar::all(0);
aModelIndexMid = Scalar::all(0);
aModelIndexLong = Scalar::all(0);
nNextShortUpdate = Scalar::all(0);
nNextMidUpdate = Scalar::all(0);
nNextLongUpdate = Scalar::all(0);
}
virtual int getHistory() const { return history; }
virtual void setHistory(int _nframes) { history = _nframes; }
virtual int getNSamples() const { return nN; }
virtual void setNSamples(int _nN) { nN = _nN; }
virtual int getkNNSamples() const { return nkNN; }
virtual void setkNNSamples(int _nkNN) { nkNN = _nkNN; }
virtual double getDist2Threshold() const { return fTb; }
virtual void setDist2Threshold(double _dist2Threshold) { fTb = (float)_dist2Threshold; }
virtual bool getDetectShadows() const { return bShadowDetection; }
virtual void setDetectShadows(bool detectshadows) { bShadowDetection = detectshadows; }
virtual int getShadowValue() const { return nShadowDetection; }
virtual void setShadowValue(int value) { nShadowDetection = (uchar)value; }
virtual double getShadowThreshold() const { return fTau; }
virtual void setShadowThreshold(double value) { fTau = (float)value; }
virtual void write(FileStorage& fs) const
{
fs << "name" << name_
<< "history" << history
<< "nsamples" << nN
<< "nKNN" << nkNN
<< "dist2Threshold" << fTb
<< "detectShadows" << (int)bShadowDetection
<< "shadowValue" << (int)nShadowDetection
<< "shadowThreshold" << fTau;
}
virtual void read(const FileNode& fn)
{
CV_Assert( (String)fn["name"] == name_ );
history = (int)fn["history"];
nN = (int)fn["nsamples"];
nkNN = (int)fn["nKNN"];
fTb = (float)fn["dist2Threshold"];
bShadowDetection = (int)fn["detectShadows"] != 0;
nShadowDetection = saturate_cast<uchar>((int)fn["shadowValue"]);
fTau = (float)fn["shadowThreshold"];
}
protected:
Size frameSize;
int frameType;
int nframes;
int history;
float fTb;
int nN;
int nkNN;
bool bShadowDetection;
unsigned char nShadowDetection;
float fTau;
int nLongCounter;
int nMidCounter;
int nShortCounter;
Mat bgmodel;
Mat aModelIndexShort;
Mat aModelIndexMid;
Mat aModelIndexLong;
Mat nNextShortUpdate;
Mat nNextMidUpdate;
Mat nNextLongUpdate;
String name_;
};
CV_INLINE void
_cvUpdatePixelBackgroundNP( long pixel,const uchar* data, int nchannels, int m_nN,
uchar* m_aModel,
uchar* m_nNextLongUpdate,
uchar* m_nNextMidUpdate,
uchar* m_nNextShortUpdate,
uchar* m_aModelIndexLong,
uchar* m_aModelIndexMid,
uchar* m_aModelIndexShort,
int m_nLongCounter,
int m_nMidCounter,
int m_nShortCounter,
int m_nLongUpdate,
int m_nMidUpdate,
int m_nShortUpdate,
uchar include
)
{
int ndata=1+nchannels;
long offsetLong = ndata * (pixel * m_nN * 3 + m_aModelIndexLong[pixel] + m_nN * 2);
long offsetMid = ndata * (pixel * m_nN * 3 + m_aModelIndexMid[pixel] + m_nN * 1);
long offsetShort = ndata * (pixel * m_nN * 3 + m_aModelIndexShort[pixel]);
if (m_nNextLongUpdate[pixel] == m_nLongCounter)
{
memcpy(&m_aModel[offsetLong],&m_aModel[offsetMid],ndata*sizeof(unsigned char));
m_aModelIndexLong[pixel] = (m_aModelIndexLong[pixel] >= (m_nN-1)) ? 0 : (m_aModelIndexLong[pixel] + 1);
};
if (m_nLongCounter == (m_nLongUpdate-1))
{
m_nNextLongUpdate[pixel] = (uchar)( rand() % m_nLongUpdate );
};
if (m_nNextMidUpdate[pixel] == m_nMidCounter)
{
memcpy(&m_aModel[offsetMid],&m_aModel[offsetShort],ndata*sizeof(unsigned char));
m_aModelIndexMid[pixel] = (m_aModelIndexMid[pixel] >= (m_nN-1)) ? 0 : (m_aModelIndexMid[pixel] + 1);
};
if (m_nMidCounter == (m_nMidUpdate-1))
{
m_nNextMidUpdate[pixel] = (uchar)( rand() % m_nMidUpdate );
};
if (m_nNextShortUpdate[pixel] == m_nShortCounter)
{
memcpy(&m_aModel[offsetShort],data,ndata*sizeof(unsigned char));
m_aModel[offsetShort+nchannels]=include;
m_aModelIndexShort[pixel] = (m_aModelIndexShort[pixel] >= (m_nN-1)) ? 0 : (m_aModelIndexShort[pixel] + 1);
};
if (m_nShortCounter == (m_nShortUpdate-1))
{
m_nNextShortUpdate[pixel] = (uchar)( rand() % m_nShortUpdate );
};
};
CV_INLINE int
_cvCheckPixelBackgroundNP(long pixel,
const uchar* data, int nchannels,
int m_nN,
uchar* m_aModel,
float m_fTb,
int m_nkNN,
float tau,
int m_nShadowDetection,
uchar& include)
{
int Pbf = 0;
int Pb = 0;
float dData[CV_CN_MAX];
include=0;
int ndata=nchannels+1;
long posPixel = pixel * ndata * m_nN * 3;
for (int n = 0; n < m_nN*3; n++)
{
uchar* mean_m = &m_aModel[posPixel + n*ndata];
float dist2;
if( nchannels == 3 )
{
dData[0] = (float)mean_m[0] - data[0];
dData[1] = (float)mean_m[1] - data[1];
dData[2] = (float)mean_m[2] - data[2];
dist2 = dData[0]*dData[0] + dData[1]*dData[1] + dData[2]*dData[2];
}
else
{
dist2 = 0.f;
for( int c = 0; c < nchannels; c++ )
{
dData[c] = (float)mean_m[c] - data[c];
dist2 += dData[c]*dData[c];
}
}
if (dist2<m_fTb)
{
Pbf++;
if(mean_m[nchannels])
{
Pb++;
if (Pb >= m_nkNN)
{
include=1;
return 1;
};
}
};
};
if (Pbf>=m_nkNN)
{
include=1;
}
int Ps = 0;
if (m_nShadowDetection)
{
for (int n = 0; n < m_nN*3; n++)
{
uchar* mean_m = &m_aModel[posPixel + n*ndata];
if(mean_m[nchannels])
{
float numerator = 0.0f;
float denominator = 0.0f;
for( int c = 0; c < nchannels; c++ )
{
numerator += (float)data[c] * mean_m[c];
denominator += (float)mean_m[c] * mean_m[c];
}
if( denominator == 0 )
return 0;
if( numerator <= denominator && numerator >= tau*denominator )
{
float a = numerator / denominator;
float dist2a = 0.0f;
for( int c = 0; c < nchannels; c++ )
{
float dD= a*mean_m[c] - data[c];
dist2a += dD*dD;
}
if (dist2a<m_fTb*a*a)
{
Ps++;
if (Ps >= m_nkNN)
return 2;
};
};
};
};
}
return 0;
};
CV_INLINE void
icvUpdatePixelBackgroundNP(const Mat& _src, Mat& _dst,
Mat& _bgmodel,
Mat& _nNextLongUpdate,
Mat& _nNextMidUpdate,
Mat& _nNextShortUpdate,
Mat& _aModelIndexLong,
Mat& _aModelIndexMid,
Mat& _aModelIndexShort,
int& _nLongCounter,
int& _nMidCounter,
int& _nShortCounter,
int _nN,
float _fAlphaT,
float _fTb,
int _nkNN,
float _fTau,
int _bShadowDetection,
uchar nShadowDetection
)
{
int nchannels = CV_MAT_CN(_src.type());
uchar* m_aModel=_bgmodel.ptr(0);
uchar* m_nNextLongUpdate=_nNextLongUpdate.ptr(0);
uchar* m_nNextMidUpdate=_nNextMidUpdate.ptr(0);
uchar* m_nNextShortUpdate=_nNextShortUpdate.ptr(0);
uchar* m_aModelIndexLong=_aModelIndexLong.ptr(0);
uchar* m_aModelIndexMid=_aModelIndexMid.ptr(0);
uchar* m_aModelIndexShort=_aModelIndexShort.ptr(0);
int m_nN=_nN;
float m_fAlphaT=_fAlphaT;
float m_fTb=_fTb;
float m_fTau=_fTau;
int m_nkNN=_nkNN;
int m_bShadowDetection=_bShadowDetection;
int Kshort,Kmid,Klong;
Kshort=(int)(log(0.7)/log(1-m_fAlphaT))+1;
Kmid=(int)(log(0.4)/log(1-m_fAlphaT))-Kshort+1;
Klong=(int)(log(0.1)/log(1-m_fAlphaT))-Kshort-Kmid+1;
int m_nShortUpdate = (Kshort/m_nN)+1;
int m_nMidUpdate = (Kmid/m_nN)+1;
int m_nLongUpdate = (Klong/m_nN)+1;
int m_nLongCounter=_nLongCounter;
int m_nMidCounter=_nMidCounter;
int m_nShortCounter=_nShortCounter;
_nShortCounter++;
_nMidCounter++;
_nLongCounter++;
if (_nShortCounter >= m_nShortUpdate) _nShortCounter = 0;
if (_nMidCounter >= m_nMidUpdate) _nMidCounter = 0;
if (_nLongCounter >= m_nLongUpdate) _nLongCounter = 0;
long i = 0;
for (long y = 0; y < _src.rows; y++)
{
for (long x = 0; x < _src.cols; x++)
{
const uchar* data = _src.ptr((int)y, (int)x);
uchar include=0;
int result= _cvCheckPixelBackgroundNP(i, data, nchannels,
m_nN, m_aModel, m_fTb,m_nkNN, m_fTau,m_bShadowDetection,include);
_cvUpdatePixelBackgroundNP(i,data,nchannels,
m_nN, m_aModel,
m_nNextLongUpdate,
m_nNextMidUpdate,
m_nNextShortUpdate,
m_aModelIndexLong,
m_aModelIndexMid,
m_aModelIndexShort,
m_nLongCounter,
m_nMidCounter,
m_nShortCounter,
m_nLongUpdate,
m_nMidUpdate,
m_nShortUpdate,
include
);
switch (result)
{
case 0:
*_dst.ptr((int)y, (int)x) = 255;
break;
case 1:
*_dst.ptr((int)y, (int)x) = 0;
break;
case 2:
*_dst.ptr((int)y, (int)x) = nShadowDetection;
break;
}
i++;
}
}
};
void BackgroundSubtractorKNNImpl::apply(InputArray _image, OutputArray _fgmask, double learningRate)
{
Mat image = _image.getMat();
bool needToInitialize = nframes == 0 || learningRate >= 1 || image.size() != frameSize || image.type() != frameType;
if( needToInitialize )
initialize(image.size(), image.type());
_fgmask.create( image.size(), CV_8U );
Mat fgmask = _fgmask.getMat();
++nframes;
learningRate = learningRate >= 0 && nframes > 1 ? learningRate : 1./std::min( 2*nframes, history );
CV_Assert(learningRate >= 0);
icvUpdatePixelBackgroundNP(image, fgmask,
bgmodel,
nNextLongUpdate,
nNextMidUpdate,
nNextShortUpdate,
aModelIndexLong,
aModelIndexMid,
aModelIndexShort,
nLongCounter,
nMidCounter,
nShortCounter,
nN,
(float)learningRate,
fTb,
nkNN,
fTau,
bShadowDetection,
nShadowDetection
);
}
void BackgroundSubtractorKNNImpl::getBackgroundImage(OutputArray backgroundImage) const
{
int nchannels = CV_MAT_CN(frameType);
Mat meanBackground(frameSize, CV_8UC3, Scalar::all(0));
int ndata=nchannels+1;
int modelstep=(ndata * nN * 3);
const uchar* pbgmodel=bgmodel.ptr(0);
for(int row=0; row<meanBackground.rows; row++)
{
for(int col=0; col<meanBackground.cols; col++)
{
for (int n = 0; n < nN*3; n++)
{
const uchar* mean_m = &pbgmodel[n*ndata];
if (mean_m[nchannels])
{
meanBackground.at<Vec3b>(row, col) = Vec3b(mean_m);
break;
}
}
pbgmodel=pbgmodel+modelstep;
}
}
switch(CV_MAT_CN(frameType))
{
case 1:
{
std::vector<Mat> channels;
split(meanBackground, channels);
channels[0].copyTo(backgroundImage);
break;
}
case 3:
{
meanBackground.copyTo(backgroundImage);
break;
}
default:
CV_Error(Error::StsUnsupportedFormat, "");
}
}
Ptr<BackgroundSubtractorKNN> createBackgroundSubtractorKNN(int _history, double _threshold2,
bool _bShadowDetection)
{
return makePtr<BackgroundSubtractorKNNImpl>(_history, (float)_threshold2, _bShadowDetection);
}
}