Commit b6fc3d81 authored by jaco's avatar jaco

Delete SuBSENSE algorithm (motion saliency) due to license issues

Part of SuBSENSE algorithm is based on ViBe method, that is patented in
Europe ans U.S.
This means that SuBSENSE can not currently be released under the BSD
license. We are trying to resolve the issue.
In waiting, I am forced to remove the algorithm from the "official"
branch on which the pull-request on opencv_contrib will be made . The
code has been moved to an opportune branch (that will not be merged) and
will remain there waiting for this issue to be clarified.
parent 4e478bc0
......@@ -99,119 +99,9 @@ class CV_EXPORTS_W StaticSaliencySpectralResidual : public StaticSaliency
/************************************ Specific Motion Saliency Specialized Classes ************************************/
/*!
Self-Balanced Sensitivity segmenTER (SuBSENSE) foreground-background segmentation algorithm.
Note: both grayscale and RGB/BGR images may be used with this extractor (parameters are adjusted automatically).
For optimal grayscale results, use CV_8UC1 frames instead of CV_8UC3.
For more details on the different parametersor on the algorithm itself, see P.-L. St-Charles et al.,
"Flexible Background Subtraction With Self-Balanced Local Sensitivity", in CVPRW 2014.
This algorithm is currently NOT thread-safe.
*/
class CV_EXPORTS_W MotionSaliencySuBSENSE : public BackgroundSubtractorLBSP, public MotionSaliency
{
public:
//! full constructor
MotionSaliencySuBSENSE( float fRelLBSPThreshold = BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, size_t nMinDescDistThreshold =
BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD,
size_t nMinColorDistThreshold = BGSSUBSENSE_DEFAULT_COLOR_DIST_THRESHOLD, size_t nBGSamples =
BGSSUBSENSE_DEFAULT_NB_BG_SAMPLES,
size_t nRequiredBGSamples = BGSSUBSENSE_DEFAULT_REQUIRED_NB_BG_SAMPLES, size_t nSamplesForMovingAvgs =
BGSSUBSENSE_DEFAULT_N_SAMPLES_FOR_MV_AVGS );
virtual ~MotionSaliencySuBSENSE();
//! (re)initiaization method; needs to be called before starting background subtraction (note: also reinitializes the keypoints vector)
virtual void initialize( const Mat& oInitImg, const std::vector<KeyPoint>& voKeyPoints );
//! refreshes all samples based on the last analyzed frame
virtual void refreshModel( float fSamplesRefreshFrac );
//! primary model update function; the learning param is used to override the internal learning thresholds (ignored when <= 0)
virtual void operator()( InputArray image, OutputArray fgmask, double learningRateOverride = 0 );
//! returns a copy of the latest reconstructed background image
void getBackgroundImage( OutputArray backgroundImage ) const;
//! turns automatic model reset on or off
void setAutomaticModelReset( bool );
void read( const FileNode& fn );
void write( FileStorage& fs ) const;
protected:
bool computeSaliencyImpl( const InputArray src, OutputArray dst );
AlgorithmInfo* info() const;
//! indicates whether internal structures have already been initialized (LBSP lookup tables, samples, etc.)
bool m_bInitializedInternalStructs;
//! absolute minimal color distance threshold ('R' or 'radius' in the original ViBe paper, used as the default/initial 'R(x)' value here, paired with BackgroundSubtractorLBSP::m_nDescDistThreshold)
const size_t m_nMinColorDistThreshold;
//! number of different samples per pixel/block to be taken from input frames to build the background model (same as 'N' in ViBe/PBAS)
const size_t m_nBGSamples;
//! number of similar samples needed to consider the current pixel/block as 'background' (same as '#_min' in ViBe/PBAS)
const size_t m_nRequiredBGSamples;
//! number of samples to use to compute the learning rate of moving averages
const size_t m_nSamplesForMovingAvgs;
//! current frame index, frame count since last model reset & model reset cooldown counters
size_t m_nFrameIndex, m_nFramesSinceLastReset, m_nModelResetCooldown;
//! last calculated non-zero desc ratio
float m_fLastNonZeroDescRatio;
//! specifies whether automatic model reset is enabled or not
bool m_bAutoModelResetEnabled;
//! specifies whether Tmin/Tmax scaling is enabled or not
bool m_bLearningRateScalingEnabled;
//! current learning rate caps
float m_fCurrLearningRateLowerCap, m_fCurrLearningRateUpperCap;
//! current kernel size for median blur post-proc filtering
int m_nMedianBlurKernelSize;
//! specifies the px update spread range
bool m_bUse3x3Spread;
//! specifies the downsampled frame size (used for cam motion analysis)
Size m_oDownSampledFrameSize;
//! background model pixel color intensity samples (equivalent to 'B(x)' in PBAS, but also paired with BackgroundSubtractorLBSP::m_voBGDescSamples to create our complete model)
std::vector<Mat> m_voBGColorSamples;
//! per-pixel update rates ('T(x)' in PBAS, which contains pixel-level 'sigmas', as referred to in ViBe)
Mat m_oUpdateRateFrame;
//! per-pixel distance thresholds (equivalent to 'R(x)' in PBAS, but used as a relative value to determine both intensity and descriptor variation thresholds)
Mat m_oDistThresholdFrame;
//! per-pixel distance variation modulators ('v(x)', relative value used to modulate 'R(x)' and 'T(x)' variations)
Mat m_oVariationModulatorFrame;
//! per-pixel mean distances between consecutive frames ('D_last(x)', used to detect ghosts and high variation regions in the sequence)
Mat m_oMeanLastDistFrame;
//! per-pixel mean minimal distances from the model ('D_min(x)' in PBAS, used to control variation magnitude and direction of 'T(x)' and 'R(x)')
Mat m_oMeanMinDistFrame_LT, m_oMeanMinDistFrame_ST;
//! per-pixel mean downsampled distances between consecutive frames (used to analyze camera movement and control max learning rates globally)
Mat m_oMeanDownSampledLastDistFrame_LT, m_oMeanDownSampledLastDistFrame_ST;
//! per-pixel mean raw segmentation results
Mat m_oMeanRawSegmResFrame_LT, m_oMeanRawSegmResFrame_ST;
//! per-pixel mean final segmentation results
Mat m_oMeanFinalSegmResFrame_LT, m_oMeanFinalSegmResFrame_ST;
//! a lookup map used to keep track of unstable regions (based on segm. noise & local dist. thresholds)
Mat m_oUnstableRegionMask;
//! per-pixel blink detection results ('Z(x)')
Mat m_oBlinksFrame;
//! pre-allocated matrix used to downsample (1/8) the input frame when needed
Mat m_oDownSampledColorFrame;
//! copy of previously used pixel intensities used to calculate 'D_last(x)'
Mat m_oLastColorFrame;
//! copy of previously used descriptors used to calculate 'D_last(x)'
Mat m_oLastDescFrame;
//! the foreground mask generated by the method at [t-1] (without post-proc, used for blinking px detection)
Mat m_oRawFGMask_last;
//! the foreground mask generated by the method at [t-1] (with post-proc)
Mat m_oFGMask_last;
//! pre-allocated CV_8UC1 matrices used to speed up morph ops
Mat m_oFGMask_PreFlood;
Mat m_oFGMask_FloodedHoles;
Mat m_oFGMask_last_dilated;
Mat m_oFGMask_last_dilated_inverted;
Mat m_oRawFGBlinkMask_curr;
Mat m_oRawFGBlinkMask_last;
};
/************************************ Specific Objectness Specialized Classes ************************************/
......
#include "BackgroundSubtractorLBSP.h"
#include "DistanceUtils.h"
#include "RandUtils.h"
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iomanip>
#include <exception>
BackgroundSubtractorLBSP::BackgroundSubtractorLBSP(float fRelLBSPThreshold, size_t nDescDistThreshold, size_t nLBSPThresholdOffset)
: nDebugCoordX(0),nDebugCoordY(0)
,m_nKeyPoints(0)
,m_nImgChannels(0)
,m_nImgType(0)
,m_nDescDistThreshold(nDescDistThreshold)
,m_nLBSPThresholdOffset(nLBSPThresholdOffset)
,m_fRelLBSPThreshold(fRelLBSPThreshold)
,m_bInitialized(false) {
CV_Assert(m_fRelLBSPThreshold>=0);
}
BackgroundSubtractorLBSP::~BackgroundSubtractorLBSP() {}
void BackgroundSubtractorLBSP::initialize(const cv::Mat& oInitImg) {
this->initialize(oInitImg,std::vector<cv::KeyPoint>());
}
//cv::AlgorithmInfo* BackgroundSubtractorLBSP::info() const {
// //return nullptr;
//}
void BackgroundSubtractorLBSP::getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const {
CV_Assert(LBSP::DESC_SIZE==2);
CV_Assert(m_bInitialized);
cv::Mat oAvgBGDesc = cv::Mat::zeros(m_oImgSize,CV_32FC((int)m_nImgChannels));
for(size_t n=0; n<m_voBGDescSamples.size(); ++n) {
for(int y=0; y<m_oImgSize.height; ++y) {
for(int x=0; x<m_oImgSize.width; ++x) {
const size_t idx_ndesc = m_voBGDescSamples[n].step.p[0]*y + m_voBGDescSamples[n].step.p[1]*x;
const size_t idx_flt32 = idx_ndesc*2;
float* oAvgBgDescPtr = (float*)(oAvgBGDesc.data+idx_flt32);
const ushort* const oBGDescPtr = (ushort*)(m_voBGDescSamples[n].data+idx_ndesc);
for(size_t c=0; c<m_nImgChannels; ++c)
oAvgBgDescPtr[c] += ((float)oBGDescPtr[c])/m_voBGDescSamples.size();
}
}
}
oAvgBGDesc.convertTo(backgroundDescImage,CV_16U);
}
std::vector<cv::KeyPoint> BackgroundSubtractorLBSP::getBGKeyPoints() const {
return m_voKeyPoints;
}
void BackgroundSubtractorLBSP::setBGKeyPoints(std::vector<cv::KeyPoint>& keypoints) {
LBSP::validateKeyPoints(keypoints,m_oImgSize);
CV_Assert(!keypoints.empty());
m_voKeyPoints = keypoints;
m_nKeyPoints = keypoints.size();
}
#pragma once
#include <opencv2/features2d.hpp>
//#include <opencv2/video/background_segm.hpp>
#include "LBSP.h"
/*!
Local Binary Similarity Pattern (LBSP) foreground-background segmentation algorithm (abstract version).
For more details on the different parameters, see P.-L. St-Charles and G.-A. Bilodeau, "Improving Background
Subtraction using Local Binary Similarity Patterns", in WACV 2014, or G.-A. Bilodeau et al, "Change Detection
in Feature Space Using Local Binary Similarity Patterns", in CRV 2013.
This algorithm is currently NOT thread-safe.
*/
class BackgroundSubtractorLBSP /* : public cv::BackgroundSubtractor */
{
public:
//! full constructor
BackgroundSubtractorLBSP( float fRelLBSPThreshold, size_t nDescDistThreshold, size_t nLBSPThresholdOffset = 0 );
//! default destructor
virtual ~BackgroundSubtractorLBSP();
//! (re)initiaization method; needs to be called before starting background subtraction
virtual void initialize( const cv::Mat& oInitImg );
//! (re)initiaization method; needs to be called before starting background subtraction (note: also reinitializes the keypoints vector)
virtual void initialize( const cv::Mat& oInitImg, const std::vector<cv::KeyPoint>& voKeyPoints )=0;
//! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0)
virtual void operator()( cv::InputArray image, cv::OutputArray fgmask, double learningRate = 0 )=0;
//! unused, always returns nullptr
cv::AlgorithmInfo* info() const;
//! returns a copy of the latest reconstructed background descriptors image
virtual void getBackgroundDescriptorsImage( cv::OutputArray backgroundDescImage ) const;
//! returns the keypoints list used for descriptor extraction (note: by default, these are generated from the DenseFeatureDetector class, and the border points are removed)
virtual std::vector<cv::KeyPoint> getBGKeyPoints() const;
//! sets the keypoints to be used for descriptor extraction, effectively setting the BGModel ROI (note: this function will remove all border keypoints)
virtual void setBGKeyPoints( std::vector<cv::KeyPoint>& keypoints );
// ######## DEBUG PURPOSES ONLY ##########
int nDebugCoordX, nDebugCoordY;
protected:
//! background model descriptors samples (tied to m_voKeyPoints but shaped like the input frames)
std::vector<cv::Mat> m_voBGDescSamples;
//! background model keypoints used for LBSP descriptor extraction (specific to the input image size)
std::vector<cv::KeyPoint> m_voKeyPoints;
//! defines the current number of used keypoints (always tied to m_voKeyPoints)
size_t m_nKeyPoints;
//! input image size
cv::Size m_oImgSize;
//! input image channel size
size_t m_nImgChannels;
//! input image type
int m_nImgType;
//! absolute descriptor distance threshold
const size_t m_nDescDistThreshold;
//! LBSP internal threshold offset value -- used to reduce texture noise in dark regions
const size_t m_nLBSPThresholdOffset;
//! LBSP relative internal threshold (kept here since we don't keep an LBSP object)
const float m_fRelLBSPThreshold;
//! pre-allocated internal LBSP threshold values for all possible 8-bit intensity values
size_t m_anLBSPThreshold_8bitLUT[UCHAR_MAX + 1];
//! defines whether or not the subtractor is fully initialized
bool m_bInitialized;
};
#pragma once
#include <opencv2/core/types_c.h>
//! computes the absolute difference of two unsigned char values
static inline size_t absdiff_uchar(uchar a, uchar b) {
return (size_t)abs((int)a-(int)b); // should return the same as (a<b?b-a:a-b), but faster when properly optimized
}
//! computes the L1 distance between two unsigned char vectors (RGB)
static inline size_t L1dist_uchar(const uchar* a, const uchar* b) {
return absdiff_uchar(a[0],b[0])+absdiff_uchar(a[1],b[1])+absdiff_uchar(a[2],b[2]);
}
//! computes the color distortion between two unsigned char vectors (RGB)
static inline size_t cdist_uchar(const uchar* curr, const uchar* bg) {
size_t curr_int_sqr = curr[0]*curr[0] + curr[1]*curr[1] + curr[2]*curr[2];
if(bg[0] || bg[1] || bg[2]) {
size_t bg_int_sqr = bg[0]*bg[0] + bg[1]*bg[1] + bg[2]*bg[2];
float mix_int_sqr = std::pow((float)(curr[0]*bg[0] + curr[1]*bg[1] + curr[2]*bg[2]),2);
return (size_t)sqrt(curr_int_sqr-(mix_int_sqr/bg_int_sqr));
}
else
return (size_t)sqrt((float)curr_int_sqr);
}
//! computes the L1 distance between two opencv unsigned char vectors (RGB)
static inline size_t L1dist_vec3b(const cv::Vec3b& a, const cv::Vec3b& b) {
const uchar a_array[3] = {a[0],a[1],a[2]};
const uchar b_array[3] = {b[0],b[1],b[2]};
return L1dist_uchar(a_array,b_array);
}
//! computes the squared L2 distance between two unsigned char vectors (RGB)
static inline size_t L2sqrdist_uchar(const uchar* a, const uchar* b) {
return (absdiff_uchar(a[0],b[0])^2)+(absdiff_uchar(a[1],b[1])^2)+(absdiff_uchar(a[2],b[2])^2);
}
//! computes the L2 distance between two unsigned char vectors (RGB)
static inline float L2dist_uchar(const uchar* a, const uchar* b) {
return sqrt((float)L2sqrdist_uchar(a,b));
}
//! computes the squared L2 distance between two opencv unsigned char vectors (RGB)
static inline size_t L2sqrdist_vec3b(const cv::Vec3b& a, const cv::Vec3b& b) {
const uchar a_array[3] = {a[0],a[1],a[2]};
const uchar b_array[3] = {b[0],b[1],b[2]};
return L2sqrdist_uchar(a_array,b_array);
}
//! computes the squared L2 distance between two opencv unsigned char vectors (RGB)
static inline float L2dist_vec3b(const cv::Vec3b& a, const cv::Vec3b& b) {
return sqrt((float)L2sqrdist_vec3b(a,b));
}
//! popcount LUT for 8bit vectors
static const uchar popcount_LUT8[256] = {
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
//! computes the population count of a 16bit vector using an 8bit popcount LUT (min=0, max=48)
static inline uchar popcount_ushort_8bitsLUT(ushort x) {
return popcount_LUT8[(uchar)x] + popcount_LUT8[(uchar)(x>>8)];
}
//! computes the population count of 3x16bit vectors using an 8bit popcount LUT (min=0, max=48)
static inline uchar popcount_ushort_8bitsLUT(const ushort* x) {
return popcount_LUT8[(uchar)x[0]] + popcount_LUT8[(uchar)(x[0]>>8)]
+ popcount_LUT8[(uchar)x[1]] + popcount_LUT8[(uchar)(x[1]>>8)]
+ popcount_LUT8[(uchar)x[2]] + popcount_LUT8[(uchar)(x[2]>>8)];
}
//! computes the hamming distance between two 16bit vectors (min=0, max=16)
static inline size_t hdist_ushort_8bitLUT(ushort a, ushort b) {
return popcount_ushort_8bitsLUT(a^b);
}
//! computes the sum of hamming distances between two 3x16 bits vectors (min=0, max=48)
static inline size_t hdist_ushort_8bitLUT(const ushort* a, const ushort* b) {
return popcount_ushort_8bitsLUT(a[0]^b[0])+popcount_ushort_8bitsLUT(a[1]^b[1])+popcount_ushort_8bitsLUT(a[2]^b[2]);
}
//! computes the gradient magnitude distance between two 16 bits vectors (min=0, max=16)
static inline size_t gdist_ushort_8bitLUT(ushort a, ushort b) {
return (size_t)abs((int)popcount_ushort_8bitsLUT(a)-(int)popcount_ushort_8bitsLUT(b));
}
//! computes the sum of gradient magnitude distances between two 3x16 bits vectors (min=0, max=48)
static inline size_t gdist_ushort_8bitLUT(const ushort* a, const ushort* b) {
return (size_t)abs((int)popcount_ushort_8bitsLUT(a)-(int)popcount_ushort_8bitsLUT(b));
}
#include "LBSP.h"
LBSP::LBSP(size_t nThreshold)
: m_bOnlyUsingAbsThreshold(true)
,m_fRelThreshold(0) // unused
,m_nThreshold(nThreshold)
,m_oRefImage() {}
LBSP::LBSP(float fRelThreshold, size_t nThresholdOffset)
: m_bOnlyUsingAbsThreshold(false)
,m_fRelThreshold(fRelThreshold)
,m_nThreshold(nThresholdOffset)
,m_oRefImage() {
CV_Assert(m_fRelThreshold>=0);
}
LBSP::~LBSP() {}
void LBSP::read(const cv::FileNode& /*fn*/) {
// ... = fn["..."];
}
void LBSP::write(cv::FileStorage& /*fs*/) const {
//fs << "..." << ...;
}
void LBSP::setReference(const cv::Mat& img) {
CV_DbgAssert(img.empty() || img.type()==CV_8UC1 || img.type()==CV_8UC3);
m_oRefImage = img;
}
int LBSP::descriptorSize() const {
return DESC_SIZE;
}
int LBSP::descriptorType() const {
return CV_16U;
}
bool LBSP::isUsingRelThreshold() const {
return !m_bOnlyUsingAbsThreshold;
}
float LBSP::getRelThreshold() const {
return m_fRelThreshold;
}
size_t LBSP::getAbsThreshold() const {
return m_nThreshold;
}
static inline void lbsp_computeImpl( const cv::Mat& oInputImg,
const cv::Mat& oRefImg,
const std::vector<cv::KeyPoint>& voKeyPoints,
cv::Mat& oDesc,
size_t _t) {
CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
const size_t nChannels = (size_t)oInputImg.channels();
const size_t _step_row = oInputImg.step.p[0];
const uchar* _data = oInputImg.data;
const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
const size_t nKeyPoints = voKeyPoints.size();
if(nChannels==1) {
oDesc.create((int)nKeyPoints,1,CV_16UC1);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar _ref = _refdata[_step_row*(_y)+_x];
ushort& _res = oDesc.at<ushort>((int)k);
#include "LBSP_16bits_dbcross_1ch.i"
}
}
else { //nChannels==3
oDesc.create((int)nKeyPoints,1,CV_16UC3);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*k));
#include "LBSP_16bits_dbcross_3ch1t.i"
}
}
}
static inline void lbsp_computeImpl( const cv::Mat& oInputImg,
const cv::Mat& oRefImg,
const std::vector<cv::KeyPoint>& voKeyPoints,
cv::Mat& oDesc,
float fThreshold,
size_t nThresholdOffset) {
CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(fThreshold>=0);
const size_t nChannels = (size_t)oInputImg.channels();
const size_t _step_row = oInputImg.step.p[0];
const uchar* _data = oInputImg.data;
const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
const size_t nKeyPoints = voKeyPoints.size();
if(nChannels==1) {
oDesc.create((int)nKeyPoints,1,CV_16UC1);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar _ref = _refdata[_step_row*(_y)+_x];
ushort& _res = oDesc.at<ushort>((int)k);
const size_t _t = (size_t)(_ref*fThreshold)+nThresholdOffset;
#include "LBSP_16bits_dbcross_1ch.i"
}
}
else { //nChannels==3
oDesc.create((int)nKeyPoints,1,CV_16UC3);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*k));
const size_t _t[3] = {(size_t)(_ref[0]*fThreshold)+nThresholdOffset,(size_t)(_ref[1]*fThreshold)+nThresholdOffset,(size_t)(_ref[2]*fThreshold)+nThresholdOffset};
#include "LBSP_16bits_dbcross_3ch3t.i"
}
}
}
static inline void lbsp_computeImpl2( const cv::Mat& oInputImg,
const cv::Mat& oRefImg,
const std::vector<cv::KeyPoint>& voKeyPoints,
cv::Mat& oDesc,
size_t _t) {
CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
const size_t nChannels = (size_t)oInputImg.channels();
const size_t _step_row = oInputImg.step.p[0];
const uchar* _data = oInputImg.data;
const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
const size_t nKeyPoints = voKeyPoints.size();
if(nChannels==1) {
oDesc.create(oInputImg.size(),CV_16UC1);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar _ref = _refdata[_step_row*(_y)+_x];
ushort& _res = oDesc.at<ushort>(_y,_x);
#include "LBSP_16bits_dbcross_1ch.i"
}
}
else { //nChannels==3
oDesc.create(oInputImg.size(),CV_16UC3);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*_y + oDesc.step.p[1]*_x));
#include "LBSP_16bits_dbcross_3ch1t.i"
}
}
}
static inline void lbsp_computeImpl2( const cv::Mat& oInputImg,
const cv::Mat& oRefImg,
const std::vector<cv::KeyPoint>& voKeyPoints,
cv::Mat& oDesc,
float fThreshold,
size_t nThresholdOffset) {
CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(fThreshold>=0);
const size_t nChannels = (size_t)oInputImg.channels();
const size_t _step_row = oInputImg.step.p[0];
const uchar* _data = oInputImg.data;
const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
const size_t nKeyPoints = voKeyPoints.size();
if(nChannels==1) {
oDesc.create(oInputImg.size(),CV_16UC1);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar _ref = _refdata[_step_row*(_y)+_x];
ushort& _res = oDesc.at<ushort>(_y,_x);
const size_t _t = (size_t)(_ref*fThreshold)+nThresholdOffset;
#include "LBSP_16bits_dbcross_1ch.i"
}
}
else { //nChannels==3
oDesc.create(oInputImg.size(),CV_16UC3);
for(size_t k=0; k<nKeyPoints; ++k) {
const int _x = (int)voKeyPoints[k].pt.x;
const int _y = (int)voKeyPoints[k].pt.y;
const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*_y + oDesc.step.p[1]*_x));
const size_t _t[3] = {(size_t)(_ref[0]*fThreshold)+nThresholdOffset,(size_t)(_ref[1]*fThreshold)+nThresholdOffset,(size_t)(_ref[2]*fThreshold)+nThresholdOffset};
#include "LBSP_16bits_dbcross_3ch3t.i"
}
}
}
void LBSP::compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
CV_Assert(!oImage.empty());
cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImage.size(),PATCH_SIZE/2);
cv::KeyPointsFilter::runByKeypointSize(voKeypoints,std::numeric_limits<float>::epsilon());
if(voKeypoints.empty()) {
oDescriptors.release();
return;
}
if(m_bOnlyUsingAbsThreshold)
lbsp_computeImpl2(oImage,m_oRefImage,voKeypoints,oDescriptors,m_nThreshold);
else
lbsp_computeImpl2(oImage,m_oRefImage,voKeypoints,oDescriptors,m_fRelThreshold,m_nThreshold);
}
void LBSP::compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const {
CV_Assert(voImageCollection.size() == vvoPointCollection.size());
voDescCollection.resize(voImageCollection.size());
for(size_t i=0; i<voImageCollection.size(); i++)
compute2(voImageCollection[i], vvoPointCollection[i], voDescCollection[i]);
}
void LBSP::computeImpl(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
CV_Assert(!oImage.empty());
cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImage.size(),PATCH_SIZE/2);
cv::KeyPointsFilter::runByKeypointSize(voKeypoints,std::numeric_limits<float>::epsilon());
if(voKeypoints.empty()) {
oDescriptors.release();
return;
}
if(m_bOnlyUsingAbsThreshold)
lbsp_computeImpl(oImage,m_oRefImage,voKeypoints,oDescriptors,m_nThreshold);
else
lbsp_computeImpl(oImage,m_oRefImage,voKeypoints,oDescriptors,m_fRelThreshold,m_nThreshold);
}
void LBSP::reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput) {
CV_DbgAssert(!voKeypoints.empty());
CV_DbgAssert(!oDescriptors.empty() && oDescriptors.cols==1);
CV_DbgAssert(oSize.width>0 && oSize.height>0);
CV_DbgAssert(DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(oDescriptors.type()==CV_16UC1 || oDescriptors.type()==CV_16UC3);
const size_t nChannels = (size_t)oDescriptors.channels();
const size_t nKeyPoints = voKeypoints.size();
if(nChannels==1) {
oOutput.create(oSize,CV_16UC1);
oOutput = cv::Scalar_<ushort>(0);
for(size_t k=0; k<nKeyPoints; ++k)
oOutput.at<ushort>(voKeypoints[k].pt) = oDescriptors.at<ushort>((int)k);
}
else { //nChannels==3
oOutput.create(oSize,CV_16UC3);
oOutput = cv::Scalar_<ushort>(0,0,0);
for(size_t k=0; k<nKeyPoints; ++k) {
ushort* output_ptr = (ushort*)(oOutput.data + oOutput.step.p[0]*(int)voKeypoints[k].pt.y);
const ushort* const desc_ptr = (ushort*)(oDescriptors.data + oDescriptors.step.p[0]*k);
const size_t idx = 3*(int)voKeypoints[k].pt.x;
for(size_t n=0; n<3; ++n)
output_ptr[idx+n] = desc_ptr[n];
}
}
}
void LBSP::calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels) {
CV_DbgAssert(oDesc1.size()==oDesc2.size() && oDesc1.type()==oDesc2.type());
CV_DbgAssert(DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(oDesc1.type()==CV_16UC1 || oDesc1.type()==CV_16UC3);
CV_DbgAssert(CV_MAT_DEPTH(oDesc1.type())==CV_16U);
CV_DbgAssert(DESC_SIZE*8<=UCHAR_MAX);
CV_DbgAssert(oDesc1.step.p[0]==oDesc2.step.p[0] && oDesc1.step.p[1]==oDesc2.step.p[1]);
const float fScaleFactor = (float)UCHAR_MAX/(DESC_SIZE*8);
const size_t nChannels = CV_MAT_CN(oDesc1.type());
const size_t _step_row = oDesc1.step.p[0];
if(nChannels==1) {
oOutput.create(oDesc1.size(),CV_8UC1);
for(int i=0; i<oDesc1.rows; ++i) {
const size_t idx = _step_row*i;
const ushort* const desc1_ptr = (ushort*)(oDesc1.data+idx);
const ushort* const desc2_ptr = (ushort*)(oDesc2.data+idx);
for(int j=0; j<oDesc1.cols; ++j)
oOutput.at<uchar>(i,j) = (uchar)(fScaleFactor*hdist_ushort_8bitLUT(desc1_ptr[j],desc2_ptr[j]));
}
}
else { //nChannels==3
if(bForceMergeChannels)
oOutput.create(oDesc1.size(),CV_8UC1);
else
oOutput.create(oDesc1.size(),CV_8UC3);
for(int i=0; i<oDesc1.rows; ++i) {
const size_t idx = _step_row*i;
const ushort* const desc1_ptr = (ushort*)(oDesc1.data+idx);
const ushort* const desc2_ptr = (ushort*)(oDesc2.data+idx);
uchar* output_ptr = oOutput.data + oOutput.step.p[0]*i;
for(int j=0; j<oDesc1.cols; ++j) {
if(bForceMergeChannels)
output_ptr[j] = (uchar)((fScaleFactor*hdist_ushort_8bitLUT(desc1_ptr+j,desc2_ptr+j))/3);
else {
for(size_t n=0;n<3; ++n) {
const size_t idx2 = 3*j+n;
output_ptr[idx2] = (uchar)(fScaleFactor*hdist_ushort_8bitLUT(desc1_ptr[idx2],desc2_ptr[idx2]));
}
}
}
}
}
}
void LBSP::validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize) {
cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImgSize,PATCH_SIZE/2);
}
#pragma once
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/features2d.hpp>
#include "DistanceUtils.h"
/*!
Local Binary Similarity Pattern (LBSP) feature extractor
Note 1: both grayscale and RGB/BGR images may be used with this extractor.
Note 2: using LBSP::compute2(...) is logically equivalent to using LBSP::compute(...) followed by LBSP::reshapeDesc(...).
For more details on the different parameters, see G.-A. Bilodeau et al, "Change Detection in Feature Space Using Local
Binary Similarity Patterns", in CRV 2013.
This algorithm is currently NOT thread-safe.
*/
class LBSP : public cv::DescriptorExtractor {
public:
//! constructor 1, threshold = absolute intensity 'similarity' threshold used when computing comparisons
LBSP(size_t nThreshold);
//! constructor 2, threshold = relative intensity 'similarity' threshold used when computing comparisons
LBSP(float fRelThreshold, size_t nThresholdOffset=0);
//! default destructor
virtual ~LBSP();
//! loads extractor params from the specified file node @@@@ not impl
virtual void read(const cv::FileNode&);
//! writes extractor params to the specified file storage @@@@ not impl
virtual void write(cv::FileStorage&) const;
//! sets the 'reference' image to be used for inter-frame comparisons (note: if no image is set or if the image is empty, the algorithm will default back to intra-frame comparisons)
virtual void setReference(const cv::Mat&);
//! returns the current descriptor size, in bytes
virtual int descriptorSize() const;
//! returns the current descriptor data type
virtual int descriptorType() const;
//! returns whether this extractor is using a relative threshold or not
virtual bool isUsingRelThreshold() const;
//! returns the current relative threshold used for comparisons (-1 = invalid/not used)
virtual float getRelThreshold() const;
//! returns the current absolute threshold used for comparisons (-1 = invalid/not used)
virtual size_t getAbsThreshold() const;
//! similar to DescriptorExtractor::compute(const cv::Mat& image, ...), but in this case, the descriptors matrix has the same shape as the input matrix (possibly slower, but the result can be displayed)
void compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const;
//! batch version of LBSP::compute2(const cv::Mat& image, ...), also similar to DescriptorExtractor::compute(const std::vector<cv::Mat>& imageCollection, ...)
void compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const;
// utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel version)
inline static void computeGrayscaleDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _t, ushort& _res) {
CV_DbgAssert(!oInputImg.empty());
CV_DbgAssert(oInputImg.type()==CV_8UC1);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
const size_t _step_row = oInputImg.step.p[0];
const uchar* const _data = oInputImg.data;
#include "LBSP_16bits_dbcross_1ch.i"
}
// utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref, const int _x, const int _y, const size_t* const _t, ushort* _res) {
CV_DbgAssert(!oInputImg.empty());
CV_DbgAssert(oInputImg.type()==CV_8UC3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
const size_t _step_row = oInputImg.step.p[0];
const uchar* const _data = oInputImg.data;
#include "LBSP_16bits_dbcross_3ch3t.i"
}
// utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref, const int _x, const int _y, const size_t _t, ushort* _res) {
CV_DbgAssert(!oInputImg.empty());
CV_DbgAssert(oInputImg.type()==CV_8UC3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
const size_t _step_row = oInputImg.step.p[0];
const uchar* const _data = oInputImg.data;
#include "LBSP_16bits_dbcross_3ch1t.i"
}
// utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel-RGB version)
inline static void computeSingleRGBDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _c, const size_t _t, ushort& _res) {
CV_DbgAssert(!oInputImg.empty());
CV_DbgAssert(oInputImg.type()==CV_8UC3 && _c<3);
CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
const size_t _step_row = oInputImg.step.p[0];
const uchar* const _data = oInputImg.data;
#include "LBSP_16bits_dbcross_s3ch.i"
}
//! utility function, used to reshape a descriptors matrix to its input image size via their keypoint locations
static void reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput);
//! utility function, used to illustrate the difference between two descriptor images
static void calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels=false);
//! utility function, used to filter out bad keypoints that would trigger out of bounds error because they're too close to the image border
static void validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize);
//! utility, specifies the pixel size of the pattern used (width and height)
static const size_t PATCH_SIZE = 5;
//! utility, specifies the number of bytes per descriptor (should be the same as calling 'descriptorSize()')
static const size_t DESC_SIZE = 2;
protected:
//! classic 'compute' implementation, based on the regular DescriptorExtractor::computeImpl arguments & expected output
virtual void computeImpl(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const;
const bool m_bOnlyUsingAbsThreshold;
const float m_fRelThreshold;
const size_t m_nThreshold;
cv::Mat m_oRefImage;
};
// note: this is the LBSP 16 bit double-cross single channel pattern as used in
// the original article by G.-A. Bilodeau et al.
//
// O O O 4 .. 3 .. 6
// O O O .. 15 8 13 ..
// O O X O O => 0 9 X 11 1
// O O O .. 12 10 14 ..
// O O O 7 .. 2 .. 5
//
// must be defined externally:
// _t (size_t, absolute threshold used for comparisons)
// _ref (uchar, 'central' value used for comparisons)
// _data (uchar*, single-channel data to be covered by the pattern)
// _y (int, pattern rows location in the image data)
// _x (int, pattern cols location in the image data)
// _step_row (size_t, step size between rows, including padding)
// _res (ushort, 16 bit result vector)
// absdiff_uchar (function, returns the absolute difference between two uchars)
#ifdef _val
#error "definitions clash detected"
#else
#define _val(x,y) _data[_step_row*(_y+y)+_x+x]
#endif
_res= ((absdiff_uchar(_val(-1, 1),_ref) > _t) << 15)
+ ((absdiff_uchar(_val( 1,-1),_ref) > _t) << 14)
+ ((absdiff_uchar(_val( 1, 1),_ref) > _t) << 13)
+ ((absdiff_uchar(_val(-1,-1),_ref) > _t) << 12)
+ ((absdiff_uchar(_val( 1, 0),_ref) > _t) << 11)
+ ((absdiff_uchar(_val( 0,-1),_ref) > _t) << 10)
+ ((absdiff_uchar(_val(-1, 0),_ref) > _t) << 9)
+ ((absdiff_uchar(_val( 0, 1),_ref) > _t) << 8)
+ ((absdiff_uchar(_val(-2,-2),_ref) > _t) << 7)
+ ((absdiff_uchar(_val( 2, 2),_ref) > _t) << 6)
+ ((absdiff_uchar(_val( 2,-2),_ref) > _t) << 5)
+ ((absdiff_uchar(_val(-2, 2),_ref) > _t) << 4)
+ ((absdiff_uchar(_val( 0, 2),_ref) > _t) << 3)
+ ((absdiff_uchar(_val( 0,-2),_ref) > _t) << 2)
+ ((absdiff_uchar(_val( 2, 0),_ref) > _t) << 1)
+ ((absdiff_uchar(_val(-2, 0),_ref) > _t));
#undef _val
\ No newline at end of file
// note: this is the LBSP 16 bit double-cross indiv RGB pattern as used in
// the original article by G.-A. Bilodeau et al.
//
// O O O 4 .. 3 .. 6
// O O O .. 15 8 13 ..
// O O X O O => 0 9 X 11 1
// O O O .. 12 10 14 ..
// O O O 7 .. 2 .. 5
// 3x 3x
//
// must be defined externally:
// _t (size_t, absolute threshold used for comparisons)
// _ref (uchar[3], 'central' values used for comparisons)
// _data (uchar*, triple-channel data to be covered by the pattern)
// _y (int, pattern rows location in the image data)
// _x (int, pattern cols location in the image data)
// _step_row (size_t, step size between rows, including padding)
// _res (ushort[3], 16 bit result vectors vector)
// absdiff_uchar (function, returns the absolute difference between two uchars)
#ifdef _val
#error "definitions clash detected"
#else
#define _val(x,y,n) _data[_step_row*(_y+y)+3*(_x+x)+n]
#endif
for(int n=0; n<3; ++n) {
_res[n] = ((absdiff_uchar(_val(-1, 1, n),_ref[n]) > _t) << 15)
+ ((absdiff_uchar(_val( 1,-1, n),_ref[n]) > _t) << 14)
+ ((absdiff_uchar(_val( 1, 1, n),_ref[n]) > _t) << 13)
+ ((absdiff_uchar(_val(-1,-1, n),_ref[n]) > _t) << 12)
+ ((absdiff_uchar(_val( 1, 0, n),_ref[n]) > _t) << 11)
+ ((absdiff_uchar(_val( 0,-1, n),_ref[n]) > _t) << 10)
+ ((absdiff_uchar(_val(-1, 0, n),_ref[n]) > _t) << 9)
+ ((absdiff_uchar(_val( 0, 1, n),_ref[n]) > _t) << 8)
+ ((absdiff_uchar(_val(-2,-2, n),_ref[n]) > _t) << 7)
+ ((absdiff_uchar(_val( 2, 2, n),_ref[n]) > _t) << 6)
+ ((absdiff_uchar(_val( 2,-2, n),_ref[n]) > _t) << 5)
+ ((absdiff_uchar(_val(-2, 2, n),_ref[n]) > _t) << 4)
+ ((absdiff_uchar(_val( 0, 2, n),_ref[n]) > _t) << 3)
+ ((absdiff_uchar(_val( 0,-2, n),_ref[n]) > _t) << 2)
+ ((absdiff_uchar(_val( 2, 0, n),_ref[n]) > _t) << 1)
+ ((absdiff_uchar(_val(-2, 0, n),_ref[n]) > _t));
}
#undef _val
// note: this is the LBSP 16 bit double-cross indiv RGB pattern as used in
// the original article by G.-A. Bilodeau et al.
//
// O O O 4 .. 3 .. 6
// O O O .. 15 8 13 ..
// O O X O O => 0 9 X 11 1
// O O O .. 12 10 14 ..
// O O O 7 .. 2 .. 5
// 3x 3x
//
// must be defined externally:
// _t (size_t[3], absolute thresholds used for comparisons)
// _ref (uchar[3], 'central' values used for comparisons)
// _data (uchar*, triple-channel data to be covered by the pattern)
// _y (int, pattern rows location in the image data)
// _x (int, pattern cols location in the image data)
// _step_row (size_t, step size between rows, including padding)
// _res (ushort[3], 16 bit result vectors vector)
// absdiff_uchar (function, returns the absolute difference between two uchars)
#ifdef _val
#error "definitions clash detected"
#else
#define _val(x,y,n) _data[_step_row*(_y+y)+3*(_x+x)+n]
#endif
for(int n=0; n<3; ++n) {
_res[n] = ((absdiff_uchar(_val(-1, 1, n),_ref[n]) > _t[n]) << 15)
+ ((absdiff_uchar(_val( 1,-1, n),_ref[n]) > _t[n]) << 14)
+ ((absdiff_uchar(_val( 1, 1, n),_ref[n]) > _t[n]) << 13)
+ ((absdiff_uchar(_val(-1,-1, n),_ref[n]) > _t[n]) << 12)
+ ((absdiff_uchar(_val( 1, 0, n),_ref[n]) > _t[n]) << 11)
+ ((absdiff_uchar(_val( 0,-1, n),_ref[n]) > _t[n]) << 10)
+ ((absdiff_uchar(_val(-1, 0, n),_ref[n]) > _t[n]) << 9)
+ ((absdiff_uchar(_val( 0, 1, n),_ref[n]) > _t[n]) << 8)
+ ((absdiff_uchar(_val(-2,-2, n),_ref[n]) > _t[n]) << 7)
+ ((absdiff_uchar(_val( 2, 2, n),_ref[n]) > _t[n]) << 6)
+ ((absdiff_uchar(_val( 2,-2, n),_ref[n]) > _t[n]) << 5)
+ ((absdiff_uchar(_val(-2, 2, n),_ref[n]) > _t[n]) << 4)
+ ((absdiff_uchar(_val( 0, 2, n),_ref[n]) > _t[n]) << 3)
+ ((absdiff_uchar(_val( 0,-2, n),_ref[n]) > _t[n]) << 2)
+ ((absdiff_uchar(_val( 2, 0, n),_ref[n]) > _t[n]) << 1)
+ ((absdiff_uchar(_val(-2, 0, n),_ref[n]) > _t[n]));
}
#undef _val
// note: this is the LBSP 16 bit double-cross indiv RGB pattern as used in
// the original article by G.-A. Bilodeau et al.
//
// O O O 4 .. 3 .. 6
// O O O .. 15 8 13 ..
// O O X O O => 0 9 X 11 1
// O O O .. 12 10 14 ..
// O O O 7 .. 2 .. 5
// (single/3x) (single/3x)
//
// must be defined externally:
// _t (size_t, absolute threshold used for comparisons)
// _ref (uchar, 'central' value used for comparisons)
// _data (uchar*, triple-channel data to be covered by the pattern)
// _y (int, pattern rows location in the image data)
// _x (int, pattern cols location in the image data)
// _c (size_t, pattern channel location in the image data)
// _step_row (size_t, step size between rows, including padding)
// _res (ushort, 16 bit result vector)
// absdiff_uchar (function, returns the absolute difference between two uchars)
#ifdef _val
#error "definitions clash detected"
#else
#define _val(x,y,n) _data[_step_row*(_y+y)+3*(_x+x)+n]
#endif
_res = ((absdiff_uchar(_val(-1, 1, _c),_ref) > _t) << 15)
+ ((absdiff_uchar(_val( 1,-1, _c),_ref) > _t) << 14)
+ ((absdiff_uchar(_val( 1, 1, _c),_ref) > _t) << 13)
+ ((absdiff_uchar(_val(-1,-1, _c),_ref) > _t) << 12)
+ ((absdiff_uchar(_val( 1, 0, _c),_ref) > _t) << 11)
+ ((absdiff_uchar(_val( 0,-1, _c),_ref) > _t) << 10)
+ ((absdiff_uchar(_val(-1, 0, _c),_ref) > _t) << 9)
+ ((absdiff_uchar(_val( 0, 1, _c),_ref) > _t) << 8)
+ ((absdiff_uchar(_val(-2,-2, _c),_ref) > _t) << 7)
+ ((absdiff_uchar(_val( 2, 2, _c),_ref) > _t) << 6)
+ ((absdiff_uchar(_val( 2,-2, _c),_ref) > _t) << 5)
+ ((absdiff_uchar(_val(-2, 2, _c),_ref) > _t) << 4)
+ ((absdiff_uchar(_val( 0, 2, _c),_ref) > _t) << 3)
+ ((absdiff_uchar(_val( 0,-2, _c),_ref) > _t) << 2)
+ ((absdiff_uchar(_val( 2, 0, _c),_ref) > _t) << 1)
+ ((absdiff_uchar(_val(-2, 0, _c),_ref) > _t));
#undef _val
\ No newline at end of file
#pragma once
/*// gaussian 3x3 pattern, based on 'floor(fspecial('gaussian', 3, 1)*256)'
static const int s_nSamplesInitPatternWidth = 3;
static const int s_nSamplesInitPatternHeight = 3;
static const int s_nSamplesInitPatternTot = 256;
static const int s_anSamplesInitPattern[s_nSamplesInitPatternHeight][s_nSamplesInitPatternWidth] = {
{19, 32, 19,},
{32, 52, 32,},
{19, 32, 19,},
};*/
// gaussian 7x7 pattern, based on 'floor(fspecial('gaussian',7,1)*4096)'
static const int s_nSamplesInitPatternWidth = 7;
static const int s_nSamplesInitPatternHeight = 7;
static const int s_nSamplesInitPatternTot = 4096;
static const int s_anSamplesInitPattern[s_nSamplesInitPatternHeight][s_nSamplesInitPatternWidth] = {
{0, 0, 4, 7, 4, 0, 0,},
{0, 11, 53, 88, 53, 11, 0,},
{4, 53, 240, 399, 240, 53, 4,},
{7, 88, 399, 660, 399, 88, 7,},
{4, 53, 240, 399, 240, 53, 4,},
{0, 11, 53, 88, 53, 11, 0,},
{0, 0, 4, 7, 4, 0, 0,},
};
//! returns a random init/sampling position for the specified pixel position; also guards against out-of-bounds values via image/border size check.
static inline void getRandSamplePosition(int& x_sample, int& y_sample, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) {
int r = 1+rand()%s_nSamplesInitPatternTot;
for(x_sample=0; x_sample<s_nSamplesInitPatternWidth; ++x_sample) {
for(y_sample=0; y_sample<s_nSamplesInitPatternHeight; ++y_sample) {
r -= s_anSamplesInitPattern[y_sample][x_sample];
if(r<=0)
goto stop;
}
}
stop:
x_sample += x_orig-s_nSamplesInitPatternWidth/2;
y_sample += y_orig-s_nSamplesInitPatternHeight/2;
if(x_sample<border)
x_sample = border;
else if(x_sample>=imgsize.width-border)
x_sample = imgsize.width-border-1;
if(y_sample<border)
y_sample = border;
else if(y_sample>=imgsize.height-border)
y_sample = imgsize.height-border-1;
}
// simple 8-connected (3x3) neighbors pattern
static const int s_anNeighborPatternSize_3x3 = 8;
static const int s_anNeighborPattern_3x3[8][2] = {
{-1, 1}, { 0, 1}, { 1, 1},
{-1, 0}, { 1, 0},
{-1,-1}, { 0,-1}, { 1,-1},
};
//! returns a random neighbor position for the specified pixel position; also guards against out-of-bounds values via image/border size check.
static inline void getRandNeighborPosition_3x3(int& x_neighbor, int& y_neighbor, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) {
int r = rand()%s_anNeighborPatternSize_3x3;
x_neighbor = x_orig+s_anNeighborPattern_3x3[r][0];
y_neighbor = y_orig+s_anNeighborPattern_3x3[r][1];
if(x_neighbor<border)
x_neighbor = border;
else if(x_neighbor>=imgsize.width-border)
x_neighbor = imgsize.width-border-1;
if(y_neighbor<border)
y_neighbor = border;
else if(y_neighbor>=imgsize.height-border)
y_neighbor = imgsize.height-border-1;
}
// 5x5 neighbors pattern
static const int s_anNeighborPatternSize_5x5 = 24;
static const int s_anNeighborPattern_5x5[24][2] = {
{-2, 2}, {-1, 2}, { 0, 2}, { 1, 2}, { 2, 2},
{-2, 1}, {-1, 1}, { 0, 1}, { 1, 1}, { 2, 1},
{-2, 0}, {-1, 0}, { 1, 0}, { 2, 0},
{-2,-1}, {-1,-1}, { 0,-1}, { 1,-1}, { 2,-1},
{-2,-2}, {-1,-2}, { 0,-2}, { 1,-2}, { 2,-2},
};
//! returns a random neighbor position for the specified pixel position; also guards against out-of-bounds values via image/border size check.
static inline void getRandNeighborPosition_5x5(int& x_neighbor, int& y_neighbor, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) {
int r = rand()%s_anNeighborPatternSize_5x5;
x_neighbor = x_orig+s_anNeighborPattern_5x5[r][0];
y_neighbor = y_orig+s_anNeighborPattern_5x5[r][1];
if(x_neighbor<border)
x_neighbor = border;
else if(x_neighbor>=imgsize.width-border)
x_neighbor = imgsize.width-border-1;
if(y_neighbor<border)
y_neighbor = border;
else if(y_neighbor>=imgsize.height-border)
y_neighbor = imgsize.height-border-1;
}
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include "DistanceUtils.h"
#include "RandUtils.h"
#include <iostream>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iomanip>
/*
*
* Intrinsic parameters for our method are defined here; tuning these for better
* performance should not be required in most cases -- although improvements in
* very specific scenarios are always possible.
*
* Note that the current configuration was used to obtain the results presented
* in our paper, in conjunction with the 2014 CVPRW on Change Detection.
*
*/
//! defines the threshold value(s) used to detect long-term ghosting and trigger the fast edge-based absorption heuristic
#define GHOSTDET_D_MAX (0.010f) // defines 'negligible' change here
#define GHOSTDET_S_MIN (0.995f) // defines the required minimum local foreground saturation value
//! parameter used to scale dynamic distance threshold adjustments ('R(x)')
#define FEEDBACK_R_VAR (0.01f)
//! parameters used to adjust the variation step size of 'v(x)'
#define FEEDBACK_V_INCR (1.000f)
#define FEEDBACK_V_DECR (0.100f)
//! parameters used to scale dynamic learning rate adjustments ('T(x)')
#define FEEDBACK_T_DECR (0.2500f)
#define FEEDBACK_T_INCR (0.5000f)
#define FEEDBACK_T_LOWER (2.0000f)
#define FEEDBACK_T_UPPER (256.00f)
//! parameters used to define 'unstable' regions, based on segm noise/bg dynamics and local dist threshold values
#define UNSTABLE_REG_RATIO_MIN 0.100f
#define UNSTABLE_REG_RDIST_MIN 3.000f
//! parameters used to scale the relative LBSP intensity threshold used for internal comparisons
#define LBSPDESC_NONZERO_RATIO_MIN 0.100f
#define LBSPDESC_NONZERO_RATIO_MAX 0.500f
//! parameters used to define model reset/learning rate boosts in our frame-level component
#define FRAMELEVEL_COLOR_DIFF_RESET_THRESHOLD 15
#define FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO 8
// local define used for debug purposes only
#define DISPLAY_SUBSENSE_DEBUG_INFO 0
// local define used to specify the default internal frame size
#define DEFAULT_FRAME_SIZE Size(320,240)
// local define used to specify the color dist threshold offset used for unstable regions
#define STAB_COLOR_DIST_OFFSET m_nMinColorDistThreshold/5
// local define used to specify the desc dist threshold offset used for unstable regions
#define UNSTAB_DESC_DIST_OFFSET m_nDescDistThreshold
// local define used to determine the median blur kernel size
#define DEFAULT_MEDIAN_BLUR_KERNEL_SIZE (9)
static const size_t s_nColorMaxDataRange_1ch = UCHAR_MAX;
static const size_t s_nDescMaxDataRange_1ch = LBSP::DESC_SIZE * 8;
static const size_t s_nColorMaxDataRange_3ch = s_nColorMaxDataRange_1ch * 3;
static const size_t s_nDescMaxDataRange_3ch = s_nDescMaxDataRange_1ch * 3;
namespace cv
{
MotionSaliencySuBSENSE::MotionSaliencySuBSENSE( float fRelLBSPThreshold, size_t nMinDescDistThreshold, size_t nMinColorDistThreshold,
size_t nBGSamples, size_t nRequiredBGSamples, size_t nSamplesForMovingAvgs )
:
BackgroundSubtractorLBSP( fRelLBSPThreshold, nMinDescDistThreshold ),
m_bInitializedInternalStructs( false ),
m_nMinColorDistThreshold( nMinColorDistThreshold ),
m_nBGSamples( nBGSamples ),
m_nRequiredBGSamples( nRequiredBGSamples ),
m_nSamplesForMovingAvgs( nSamplesForMovingAvgs ),
m_nFrameIndex( SIZE_MAX ),
m_nFramesSinceLastReset( 0 ),
m_nModelResetCooldown( 0 ),
m_fLastNonZeroDescRatio( 0.0f ),
m_bAutoModelResetEnabled( true ),
m_bLearningRateScalingEnabled( true ),
m_fCurrLearningRateLowerCap( FEEDBACK_T_LOWER ),
m_fCurrLearningRateUpperCap( FEEDBACK_T_UPPER ),
m_nMedianBlurKernelSize( DEFAULT_MEDIAN_BLUR_KERNEL_SIZE ),
m_bUse3x3Spread( true )
{
CV_Assert( m_nBGSamples > 0 && m_nRequiredBGSamples <= m_nBGSamples );
CV_Assert( m_nMinColorDistThreshold>=STAB_COLOR_DIST_OFFSET );
className = "PBAS";
}
MotionSaliencySuBSENSE::~MotionSaliencySuBSENSE() {}
void MotionSaliencySuBSENSE::read( const cv::FileNode& /*fn*/ )
{
//params.read( fn );
}
void MotionSaliencySuBSENSE::write( cv::FileStorage& /*fs*/ ) const
{
//params.write( fs );
}
bool MotionSaliencySuBSENSE::computeSaliencyImpl( const InputArray /*src*/, OutputArray /*dst*/)
{
return true;
}
void MotionSaliencySuBSENSE::initialize( const Mat& oInitImg, const std::vector<KeyPoint>& voKeyPoints )
{
// == init
CV_Assert( !oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0 );
CV_Assert( oInitImg.type()==CV_8UC3 || oInitImg.type()==CV_8UC1 );
if( oInitImg.type() == CV_8UC3 )
{
std::vector<Mat> voInitImgChannels;
split( oInitImg, voInitImgChannels );
/* bool eq = std::equal( voInitImgChannels[0].begin<uchar>(), voInitImgChannels[0].end<uchar>(), voInitImgChannels[1].begin<uchar>() )
&& std::equal( voInitImgChannels[1].begin<uchar>(), voInitImgChannels[1].end<uchar>(), voInitImgChannels[2].begin<uchar>() );
if( eq )
std::cout << std::endl
<< "\tMotionSaliencySuBSENSE : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance."
<< std::endl; */
}
std::vector<KeyPoint> voNewKeyPoints;
if( voKeyPoints.empty() )
{
DenseFeatureDetector oKPDDetector( 1.f, 1, 1.f, 1, 0, true, false );
voNewKeyPoints.reserve( oInitImg.rows * oInitImg.cols );
oKPDDetector.detect( Mat( oInitImg.size(), oInitImg.type() ), voNewKeyPoints );
}
else
voNewKeyPoints = voKeyPoints;
const size_t nOrigKeyPointsCount = voNewKeyPoints.size();
CV_Assert( nOrigKeyPointsCount > 0 );
LBSP::validateKeyPoints( voNewKeyPoints, oInitImg.size() );
CV_Assert( !voNewKeyPoints.empty() );
m_voKeyPoints = voNewKeyPoints;
m_nKeyPoints = m_voKeyPoints.size();
m_oImgSize = oInitImg.size();
m_nImgType = oInitImg.type();
m_nImgChannels = oInitImg.channels();
m_nFrameIndex = 0;
m_nFramesSinceLastReset = 0;
m_nModelResetCooldown = 0;
m_fLastNonZeroDescRatio = 0.0f;
const int nTotImgPixels = m_oImgSize.height * m_oImgSize.width;
if( (int) nOrigKeyPointsCount >= nTotImgPixels / 2 && nTotImgPixels >= DEFAULT_FRAME_SIZE.area() )
{
m_bLearningRateScalingEnabled = true;
m_bAutoModelResetEnabled = true;
m_bUse3x3Spread = ! ( nTotImgPixels > DEFAULT_FRAME_SIZE.area() * 2 );
const int nRawMedianBlurKernelSize = std::min(
(int) floor( (float) nTotImgPixels / DEFAULT_FRAME_SIZE.area() + 0.5f ) + DEFAULT_MEDIAN_BLUR_KERNEL_SIZE, 14 );
m_nMedianBlurKernelSize = ( nRawMedianBlurKernelSize % 2 ) ? nRawMedianBlurKernelSize : nRawMedianBlurKernelSize - 1;
m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER;
m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER;
}
else
{
m_bLearningRateScalingEnabled = false;
m_bAutoModelResetEnabled = false;
m_bUse3x3Spread = true;
m_nMedianBlurKernelSize = DEFAULT_MEDIAN_BLUR_KERNEL_SIZE;
m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER * 2;
m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER * 2;
}
//std::cout << m_oImgSize << " => m_nMedianBlurKernelSize=" << m_nMedianBlurKernelSize << ", with 3x3Spread=" << m_bUse3x3Spread << ", with Tscaling=" << m_bLearningRateScalingEnabled << std::endl;
m_oUpdateRateFrame.create( m_oImgSize, CV_32FC1 );
m_oUpdateRateFrame = Scalar( m_fCurrLearningRateLowerCap );
m_oDistThresholdFrame.create( m_oImgSize, CV_32FC1 );
m_oDistThresholdFrame = Scalar( 1.0f );
m_oVariationModulatorFrame.create( m_oImgSize, CV_32FC1 );
m_oVariationModulatorFrame = Scalar( 10.0f ); // should always be >= FEEDBACK_V_DECR
m_oMeanLastDistFrame.create( m_oImgSize, CV_32FC1 );
m_oMeanLastDistFrame = Scalar( 0.0f );
m_oMeanMinDistFrame_LT.create( m_oImgSize, CV_32FC1 );
m_oMeanMinDistFrame_LT = Scalar( 0.0f );
m_oMeanMinDistFrame_ST.create( m_oImgSize, CV_32FC1 );
m_oMeanMinDistFrame_ST = Scalar( 0.0f );
m_oDownSampledFrameSize = Size( m_oImgSize.width / FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO, m_oImgSize.height / FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO );
m_oMeanDownSampledLastDistFrame_LT.create( m_oDownSampledFrameSize, CV_32FC( (int )m_nImgChannels ) );
m_oMeanDownSampledLastDistFrame_LT = Scalar( 0.0f );
m_oMeanDownSampledLastDistFrame_ST.create( m_oDownSampledFrameSize, CV_32FC( (int )m_nImgChannels ) );
m_oMeanDownSampledLastDistFrame_ST = Scalar( 0.0f );
m_oMeanRawSegmResFrame_LT.create( m_oImgSize, CV_32FC1 );
m_oMeanRawSegmResFrame_LT = Scalar( 0.0f );
m_oMeanRawSegmResFrame_ST.create( m_oImgSize, CV_32FC1 );
m_oMeanRawSegmResFrame_ST = Scalar( 0.0f );
m_oMeanFinalSegmResFrame_LT.create( m_oImgSize, CV_32FC1 );
m_oMeanFinalSegmResFrame_LT = Scalar( 0.0f );
m_oMeanFinalSegmResFrame_ST.create( m_oImgSize, CV_32FC1 );
m_oMeanFinalSegmResFrame_ST = Scalar( 0.0f );
m_oUnstableRegionMask.create( m_oImgSize, CV_8UC1 );
m_oUnstableRegionMask = Scalar_<uchar>( 0 );
m_oBlinksFrame.create( m_oImgSize, CV_8UC1 );
m_oBlinksFrame = Scalar_<uchar>( 0 );
m_oDownSampledColorFrame.create( m_oDownSampledFrameSize, CV_8UC( (int )m_nImgChannels ) );
m_oDownSampledColorFrame = Scalar_<uchar>::all( 0 );
m_oLastColorFrame.create( m_oImgSize, CV_8UC( (int )m_nImgChannels ) );
m_oLastColorFrame = Scalar_<uchar>::all( 0 );
m_oLastDescFrame.create( m_oImgSize, CV_16UC( (int )m_nImgChannels ) );
m_oLastDescFrame = Scalar_<ushort>::all( 0 );
m_oRawFGMask_last.create( m_oImgSize, CV_8UC1 );
m_oRawFGMask_last = Scalar_<uchar>( 0 );
m_oFGMask_last.create( m_oImgSize, CV_8UC1 );
m_oFGMask_last = Scalar_<uchar>( 0 );
m_oFGMask_last_dilated.create( m_oImgSize, CV_8UC1 );
m_oFGMask_last_dilated = Scalar_<uchar>( 0 );
m_oFGMask_last_dilated_inverted.create( m_oImgSize, CV_8UC1 );
m_oFGMask_last_dilated_inverted = Scalar_<uchar>( 0 );
m_oFGMask_FloodedHoles.create( m_oImgSize, CV_8UC1 );
m_oFGMask_FloodedHoles = Scalar_<uchar>( 0 );
m_oFGMask_PreFlood.create( m_oImgSize, CV_8UC1 );
m_oFGMask_PreFlood = Scalar_<uchar>( 0 );
m_oRawFGBlinkMask_curr.create( m_oImgSize, CV_8UC1 );
m_oRawFGBlinkMask_curr = Scalar_<uchar>( 0 );
m_oRawFGBlinkMask_last.create( m_oImgSize, CV_8UC1 );
m_oRawFGBlinkMask_last = Scalar_<uchar>( 0 );
m_voBGColorSamples.resize( m_nBGSamples );
m_voBGDescSamples.resize( m_nBGSamples );
for ( size_t s = 0; s < m_nBGSamples; ++s )
{
m_voBGColorSamples[s].create( m_oImgSize, CV_8UC( (int )m_nImgChannels ) );
m_voBGColorSamples[s] = Scalar_<uchar>::all( 0 );
m_voBGDescSamples[s].create( m_oImgSize, CV_16UC( (int )m_nImgChannels ) );
m_voBGDescSamples[s] = Scalar_<ushort>::all( 0 );
}
if( m_nImgChannels == 1 )
{
for ( size_t t = 0; t <= UCHAR_MAX; ++t )
m_anLBSPThreshold_8bitLUT[t] = saturate_cast<uchar>( ( m_nLBSPThresholdOffset + t * m_fRelLBSPThreshold ) / 3 );
for ( size_t k = 0; k < m_nKeyPoints; ++k )
{
const int y_orig = (int) m_voKeyPoints[k].pt.y;
const int x_orig = (int) m_voKeyPoints[k].pt.x;
CV_DbgAssert(m_oLastColorFrame.step.p[0]==(size_t)m_oLastColorFrame.cols && m_oLastColorFrame.step.p[1]==1);
const size_t idx_color = m_oLastColorFrame.cols * y_orig + x_orig;
CV_DbgAssert(m_oLastDescFrame.step.p[0]==m_oLastColorFrame.step.p[0]*2 && m_oLastDescFrame.step.p[1]==m_oLastColorFrame.step.p[1]*2);
const size_t idx_desc = idx_color * 2;
m_oLastColorFrame.data[idx_color] = oInitImg.data[idx_color];
LBSP::computeGrayscaleDescriptor( oInitImg, oInitImg.data[idx_color], x_orig, y_orig, m_anLBSPThreshold_8bitLUT[oInitImg.data[idx_color]],
* ( (ushort*) ( m_oLastDescFrame.data + idx_desc ) ) );
}
}
else
{ //m_nImgChannels==3
for ( size_t t = 0; t <= UCHAR_MAX; ++t )
m_anLBSPThreshold_8bitLUT[t] = saturate_cast<uchar>( m_nLBSPThresholdOffset + t * m_fRelLBSPThreshold );
for ( size_t k = 0; k < m_nKeyPoints; ++k )
{
const int y_orig = (int) m_voKeyPoints[k].pt.y;
const int x_orig = (int) m_voKeyPoints[k].pt.x;
CV_DbgAssert(m_oLastColorFrame.step.p[0]==(size_t)m_oLastColorFrame.cols*3 && m_oLastColorFrame.step.p[1]==3);
const size_t idx_color = 3 * ( m_oLastColorFrame.cols * y_orig + x_orig );
CV_DbgAssert(m_oLastDescFrame.step.p[0]==m_oLastColorFrame.step.p[0]*2 && m_oLastDescFrame.step.p[1]==m_oLastColorFrame.step.p[1]*2);
const size_t idx_desc = idx_color * 2;
for ( size_t c = 0; c < 3; ++c )
{
const uchar nCurrBGInitColor = oInitImg.data[idx_color + c];
m_oLastColorFrame.data[idx_color + c] = nCurrBGInitColor;
LBSP::computeSingleRGBDescriptor( oInitImg, nCurrBGInitColor, x_orig, y_orig, c, m_anLBSPThreshold_8bitLUT[nCurrBGInitColor],
( (ushort*) ( m_oLastDescFrame.data + idx_desc ) )[c] );
}
}
}
m_bInitializedInternalStructs = true;
refreshModel( 1.0f );
m_bInitialized = true;
}
void MotionSaliencySuBSENSE::refreshModel( float fSamplesRefreshFrac )
{
// == refresh
CV_Assert( m_bInitializedInternalStructs );
CV_Assert( fSamplesRefreshFrac > 0.0f && fSamplesRefreshFrac <= 1.0f );
const size_t nBGSamplesToRefresh = fSamplesRefreshFrac < 1.0f ? (size_t) ( fSamplesRefreshFrac * m_nBGSamples ) : m_nBGSamples;
const size_t nRefreshStartPos = fSamplesRefreshFrac < 1.0f ? rand() % m_nBGSamples : 0;
if( m_nImgChannels == 1 )
{
for ( size_t k = 0; k < m_nKeyPoints; ++k )
{
const int y_orig = (int) m_voKeyPoints[k].pt.y;
const int x_orig = (int) m_voKeyPoints[k].pt.x;
CV_DbgAssert(m_oLastColorFrame.step.p[0]==(size_t)m_oLastColorFrame.cols && m_oLastColorFrame.step.p[1]==1);
const size_t idx_orig_color = m_oLastColorFrame.cols * y_orig + x_orig;
CV_DbgAssert(m_oLastDescFrame.step.p[0]==m_oLastColorFrame.step.p[0]*2 && m_oLastDescFrame.step.p[1]==m_oLastColorFrame.step.p[1]*2);
const size_t idx_orig_desc = idx_orig_color * 2;
for ( size_t s = nRefreshStartPos; s < nRefreshStartPos + nBGSamplesToRefresh; ++s )
{
int y_sample, x_sample;
getRandSamplePosition( x_sample, y_sample, x_orig, y_orig, LBSP::PATCH_SIZE / 2, m_oImgSize );
const size_t idx_sample_color = m_oLastColorFrame.cols * y_sample + x_sample;
const size_t idx_sample_desc = idx_sample_color * 2;
const size_t idx_sample = s % m_nBGSamples;
m_voBGColorSamples[idx_sample].data[idx_orig_color] = m_oLastColorFrame.data[idx_sample_color];
* ( (ushort*) ( m_voBGDescSamples[idx_sample].data + idx_orig_desc ) ) = * ( (ushort*) ( m_oLastDescFrame.data + idx_sample_desc ) );
}
}
}
else
{ //m_nImgChannels==3
for ( size_t k = 0; k < m_nKeyPoints; ++k )
{
const int y_orig = (int) m_voKeyPoints[k].pt.y;
const int x_orig = (int) m_voKeyPoints[k].pt.x;
CV_DbgAssert(m_oLastColorFrame.step.p[0]==(size_t)m_oLastColorFrame.cols*3 && m_oLastColorFrame.step.p[1]==3);
const size_t idx_orig_color = 3 * ( m_oLastColorFrame.cols * y_orig + x_orig );
CV_DbgAssert(m_oLastDescFrame.step.p[0]==m_oLastColorFrame.step.p[0]*2 && m_oLastDescFrame.step.p[1]==m_oLastColorFrame.step.p[1]*2);
const size_t idx_orig_desc = idx_orig_color * 2;
for ( size_t s = nRefreshStartPos; s < nRefreshStartPos + nBGSamplesToRefresh; ++s )
{
int y_sample, x_sample;
getRandSamplePosition( x_sample, y_sample, x_orig, y_orig, LBSP::PATCH_SIZE / 2, m_oImgSize );
const size_t idx_sample_color = 3 * ( m_oLastColorFrame.cols * y_sample + x_sample );
const size_t idx_sample_desc = idx_sample_color * 2;
const size_t idx_sample = s % m_nBGSamples;
uchar* bg_color_ptr = m_voBGColorSamples[idx_sample].data + idx_orig_color;
ushort* bg_desc_ptr = (ushort*) ( m_voBGDescSamples[idx_sample].data + idx_orig_desc );
const uchar* const init_color_ptr = m_oLastColorFrame.data + idx_sample_color;
const ushort* const init_desc_ptr = (ushort*) ( m_oLastDescFrame.data + idx_sample_desc );
for ( size_t c = 0; c < 3; ++c )
{
bg_color_ptr[c] = init_color_ptr[c];
bg_desc_ptr[c] = init_desc_ptr[c];
}
}
}
}
}
void MotionSaliencySuBSENSE::operator()( InputArray _image, OutputArray _fgmask, double learningRateOverride )
{
// == process
CV_DbgAssert(m_bInitialized);
Mat oInputImg = _image.getMat();
CV_DbgAssert(oInputImg.type()==m_nImgType && oInputImg.size()==m_oImgSize);
_fgmask.create( m_oImgSize, CV_8UC1 );
Mat oCurrFGMask = _fgmask.getMat();
memset( oCurrFGMask.data, 0, oCurrFGMask.cols * oCurrFGMask.rows );
size_t nNonZeroDescCount = 0;
const float fRollAvgFactor_LT = 1.0f / std::min( ++m_nFrameIndex, m_nSamplesForMovingAvgs * 4 );
const float fRollAvgFactor_ST = 1.0f / std::min( m_nFrameIndex, m_nSamplesForMovingAvgs );
if( m_nImgChannels == 1 )
{
for ( size_t k = 0; k < m_nKeyPoints; ++k )
{
const int x = (int) m_voKeyPoints[k].pt.x;
const int y = (int) m_voKeyPoints[k].pt.y;
const size_t idx_uchar = m_oImgSize.width * y + x;
const size_t idx_ushrt = idx_uchar * 2;
const size_t idx_flt32 = idx_uchar * 4;
const uchar nCurrColor = oInputImg.data[idx_uchar];
size_t nMinDescDist = s_nDescMaxDataRange_1ch;
size_t nMinSumDist = s_nColorMaxDataRange_1ch;
float* pfCurrDistThresholdFactor = (float*) ( m_oDistThresholdFrame.data + idx_flt32 );
float* pfCurrVariationFactor = (float*) ( m_oVariationModulatorFrame.data + idx_flt32 );
float* pfCurrLearningRate = ( (float*) ( m_oUpdateRateFrame.data + idx_flt32 ) );
float* pfCurrMeanLastDist = ( (float*) ( m_oMeanLastDistFrame.data + idx_flt32 ) );
float* pfCurrMeanMinDist_LT = ( (float*) ( m_oMeanMinDistFrame_LT.data + idx_flt32 ) );
float* pfCurrMeanMinDist_ST = ( (float*) ( m_oMeanMinDistFrame_ST.data + idx_flt32 ) );
float* pfCurrMeanRawSegmRes_LT = ( (float*) ( m_oMeanRawSegmResFrame_LT.data + idx_flt32 ) );
float* pfCurrMeanRawSegmRes_ST = ( (float*) ( m_oMeanRawSegmResFrame_ST.data + idx_flt32 ) );
float* pfCurrMeanFinalSegmRes_LT = ( (float*) ( m_oMeanFinalSegmResFrame_LT.data + idx_flt32 ) );
float* pfCurrMeanFinalSegmRes_ST = ( (float*) ( m_oMeanFinalSegmResFrame_ST.data + idx_flt32 ) );
ushort& nLastIntraDesc = * ( (ushort*) ( m_oLastDescFrame.data + idx_ushrt ) );
uchar& nLastColor = m_oLastColorFrame.data[idx_uchar];
const size_t nCurrColorDistThreshold = (size_t) ( ( ( *pfCurrDistThresholdFactor ) * m_nMinColorDistThreshold )
- ( ( !m_oUnstableRegionMask.data[idx_uchar] ) * STAB_COLOR_DIST_OFFSET ) ) / 2;
const size_t nCurrDescDistThreshold = ( (size_t) 1 << ( (size_t) floor( *pfCurrDistThresholdFactor + 0.5f ) ) ) + m_nDescDistThreshold
+ ( m_oUnstableRegionMask.data[idx_uchar] * UNSTAB_DESC_DIST_OFFSET );
ushort nCurrInterDesc, nCurrIntraDesc;
LBSP::computeGrayscaleDescriptor( oInputImg, nCurrColor, x, y, m_anLBSPThreshold_8bitLUT[nCurrColor], nCurrIntraDesc );
m_oUnstableRegionMask.data[idx_uchar] =
( ( *pfCurrDistThresholdFactor ) > UNSTABLE_REG_RDIST_MIN
|| ( *pfCurrMeanRawSegmRes_LT - *pfCurrMeanFinalSegmRes_LT ) > UNSTABLE_REG_RATIO_MIN
|| ( *pfCurrMeanRawSegmRes_ST - *pfCurrMeanFinalSegmRes_ST ) > UNSTABLE_REG_RATIO_MIN ) ? 1 : 0;
size_t nGoodSamplesCount = 0, nSampleIdx = 0;
while ( nGoodSamplesCount < m_nRequiredBGSamples && nSampleIdx < m_nBGSamples )
{
const uchar& nBGColor = m_voBGColorSamples[nSampleIdx].data[idx_uchar];
{
const size_t nColorDist = absdiff_uchar( nCurrColor, nBGColor );
if( nColorDist > nCurrColorDistThreshold )
goto failedcheck1ch;
const ushort& nBGIntraDesc = * ( (ushort*) ( m_voBGDescSamples[nSampleIdx].data + idx_ushrt ) );
const size_t nIntraDescDist = hdist_ushort_8bitLUT( nCurrIntraDesc, nBGIntraDesc );
LBSP::computeGrayscaleDescriptor( oInputImg, nBGColor, x, y, m_anLBSPThreshold_8bitLUT[nBGColor], nCurrInterDesc );
const size_t nInterDescDist = hdist_ushort_8bitLUT( nCurrInterDesc, nBGIntraDesc );
const size_t nDescDist = ( nIntraDescDist + nInterDescDist ) / 2;
if( nDescDist > nCurrDescDistThreshold )
goto failedcheck1ch;
const size_t nSumDist = std::min( ( nDescDist / 4 ) * ( s_nColorMaxDataRange_1ch / s_nDescMaxDataRange_1ch ) + nColorDist,
s_nColorMaxDataRange_1ch );
if( nSumDist > nCurrColorDistThreshold )
goto failedcheck1ch;
if( nMinDescDist > nDescDist )
nMinDescDist = nDescDist;
if( nMinSumDist > nSumDist )
nMinSumDist = nSumDist;
nGoodSamplesCount++;
}
failedcheck1ch : nSampleIdx++;
}
const float fNormalizedLastDist = ( (float) absdiff_uchar( nLastColor, nCurrColor ) / s_nColorMaxDataRange_1ch
+ (float) hdist_ushort_8bitLUT( nLastIntraDesc, nCurrIntraDesc ) / s_nDescMaxDataRange_1ch ) / 2;
*pfCurrMeanLastDist = ( *pfCurrMeanLastDist ) * ( 1.0f - fRollAvgFactor_ST ) + fNormalizedLastDist * fRollAvgFactor_ST;
if( nGoodSamplesCount < m_nRequiredBGSamples )
{
// == foreground
const float fNormalizedMinDist = std::min(
1.0f,
( (float) nMinSumDist / s_nColorMaxDataRange_1ch + (float) nMinDescDist / s_nDescMaxDataRange_1ch ) / 2
+ (float) ( m_nRequiredBGSamples - nGoodSamplesCount ) / m_nRequiredBGSamples );
*pfCurrMeanMinDist_LT = ( *pfCurrMeanMinDist_LT ) * ( 1.0f - fRollAvgFactor_LT ) + fNormalizedMinDist * fRollAvgFactor_LT;
*pfCurrMeanMinDist_ST = ( *pfCurrMeanMinDist_ST ) * ( 1.0f - fRollAvgFactor_ST ) + fNormalizedMinDist * fRollAvgFactor_ST;
*pfCurrMeanRawSegmRes_LT = ( *pfCurrMeanRawSegmRes_LT ) * ( 1.0f - fRollAvgFactor_LT ) + fRollAvgFactor_LT;
*pfCurrMeanRawSegmRes_ST = ( *pfCurrMeanRawSegmRes_ST ) * ( 1.0f - fRollAvgFactor_ST ) + fRollAvgFactor_ST;
oCurrFGMask.data[idx_uchar] = UCHAR_MAX;
if( m_nModelResetCooldown && ( rand() % (size_t) FEEDBACK_T_LOWER ) == 0 )
{
const size_t s_rand = rand() % m_nBGSamples;
* ( (ushort*) ( m_voBGDescSamples[s_rand].data + idx_ushrt ) ) = nCurrIntraDesc;
m_voBGColorSamples[s_rand].data[idx_uchar] = nCurrColor;
}
}
else
{
// == background
const float fNormalizedMinDist = ( (float) nMinSumDist / s_nColorMaxDataRange_1ch + (float) nMinDescDist / s_nDescMaxDataRange_1ch ) / 2;
*pfCurrMeanMinDist_LT = ( *pfCurrMeanMinDist_LT ) * ( 1.0f - fRollAvgFactor_LT ) + fNormalizedMinDist * fRollAvgFactor_LT;
*pfCurrMeanMinDist_ST = ( *pfCurrMeanMinDist_ST ) * ( 1.0f - fRollAvgFactor_ST ) + fNormalizedMinDist * fRollAvgFactor_ST;
*pfCurrMeanRawSegmRes_LT = ( *pfCurrMeanRawSegmRes_LT ) * ( 1.0f - fRollAvgFactor_LT );
*pfCurrMeanRawSegmRes_ST = ( *pfCurrMeanRawSegmRes_ST ) * ( 1.0f - fRollAvgFactor_ST );
const size_t nLearningRate = learningRateOverride > 0 ? (size_t) ceil( learningRateOverride ) : (size_t) ceil( *pfCurrLearningRate );
if( ( rand() % nLearningRate ) == 0 )
{
const size_t s_rand = rand() % m_nBGSamples;
* ( (ushort*) ( m_voBGDescSamples[s_rand].data + idx_ushrt ) ) = nCurrIntraDesc;
m_voBGColorSamples[s_rand].data[idx_uchar] = nCurrColor;
}
int x_rand, y_rand;
const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[idx_uchar];
if( bCurrUsing3x3Spread )
getRandNeighborPosition_3x3( x_rand, y_rand, x, y, LBSP::PATCH_SIZE / 2, m_oImgSize );
else
getRandNeighborPosition_5x5( x_rand, y_rand, x, y, LBSP::PATCH_SIZE / 2, m_oImgSize );
const size_t n_rand = rand();
const size_t idx_rand_uchar = m_oImgSize.width * y_rand + x_rand;
const size_t idx_rand_flt32 = idx_rand_uchar * 4;
const float fRandMeanLastDist = * ( (float*) ( m_oMeanLastDistFrame.data + idx_rand_flt32 ) );
const float fRandMeanRawSegmRes = * ( (float*) ( m_oMeanRawSegmResFrame_ST.data + idx_rand_flt32 ) );
if( ( n_rand % ( bCurrUsing3x3Spread ? nLearningRate : ( nLearningRate / 2 + 1 ) ) ) == 0
|| ( fRandMeanRawSegmRes > GHOSTDET_S_MIN && fRandMeanLastDist < GHOSTDET_D_MAX
&& ( n_rand % ( (size_t) m_fCurrLearningRateLowerCap ) ) == 0 ) )
{
const size_t idx_rand_ushrt = idx_rand_uchar * 2;
const size_t s_rand = rand() % m_nBGSamples;
* ( (ushort*) ( m_voBGDescSamples[s_rand].data + idx_rand_ushrt ) ) = nCurrIntraDesc;
m_voBGColorSamples[s_rand].data[idx_rand_uchar] = nCurrColor;
}
}
if( m_oFGMask_last.data[idx_uchar]
|| ( std::min( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) < UNSTABLE_REG_RATIO_MIN && oCurrFGMask.data[idx_uchar] ) )
{
if( ( *pfCurrLearningRate ) < m_fCurrLearningRateUpperCap )
*pfCurrLearningRate += FEEDBACK_T_INCR / ( std::max( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) * ( *pfCurrVariationFactor ) );
}
else if( ( *pfCurrLearningRate ) > m_fCurrLearningRateLowerCap )
*pfCurrLearningRate -= FEEDBACK_T_DECR * ( *pfCurrVariationFactor ) / std::max( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST );
if( ( *pfCurrLearningRate ) < m_fCurrLearningRateLowerCap )
*pfCurrLearningRate = m_fCurrLearningRateLowerCap;
else if( ( *pfCurrLearningRate ) > m_fCurrLearningRateUpperCap )
*pfCurrLearningRate = m_fCurrLearningRateUpperCap;
if( std::max( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) > UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[idx_uchar] )
( *pfCurrVariationFactor ) += FEEDBACK_V_INCR;
else if( ( *pfCurrVariationFactor ) > FEEDBACK_V_DECR )
{
( *pfCurrVariationFactor ) -= m_oFGMask_last.data[idx_uchar] ? FEEDBACK_V_DECR / 4 :
m_oUnstableRegionMask.data[idx_uchar] ? FEEDBACK_V_DECR / 2 : FEEDBACK_V_DECR;
if( ( *pfCurrVariationFactor ) < FEEDBACK_V_DECR )
( *pfCurrVariationFactor ) = FEEDBACK_V_DECR;
}
if( ( *pfCurrDistThresholdFactor ) < std::pow( 1.0f + std::min( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) * 2, 2 ) )
( *pfCurrDistThresholdFactor ) += FEEDBACK_R_VAR * ( *pfCurrVariationFactor - FEEDBACK_V_DECR );
else
{
( *pfCurrDistThresholdFactor ) -= FEEDBACK_R_VAR / ( *pfCurrVariationFactor );
if( ( *pfCurrDistThresholdFactor ) < 1.0f )
( *pfCurrDistThresholdFactor ) = 1.0f;
}
if( popcount_ushort_8bitsLUT( nCurrIntraDesc ) >= 2 )
++nNonZeroDescCount;
nLastIntraDesc = nCurrIntraDesc;
nLastColor = nCurrColor;
}
}
else
{ //m_nImgChannels==3
for ( size_t k = 0; k < m_nKeyPoints; ++k )
{
const int x = (int) m_voKeyPoints[k].pt.x;
const int y = (int) m_voKeyPoints[k].pt.y;
const size_t idx_uchar = m_oImgSize.width * y + x;
const size_t idx_flt32 = idx_uchar * 4;
const size_t idx_uchar_rgb = idx_uchar * 3;
const size_t idx_ushrt_rgb = idx_uchar_rgb * 2;
const uchar* const anCurrColor = oInputImg.data + idx_uchar_rgb;
size_t nMinTotDescDist = s_nDescMaxDataRange_3ch;
size_t nMinTotSumDist = s_nColorMaxDataRange_3ch;
float* pfCurrDistThresholdFactor = (float*) ( m_oDistThresholdFrame.data + idx_flt32 );
float* pfCurrVariationFactor = (float*) ( m_oVariationModulatorFrame.data + idx_flt32 );
float* pfCurrLearningRate = ( (float*) ( m_oUpdateRateFrame.data + idx_flt32 ) );
float* pfCurrMeanLastDist = ( (float*) ( m_oMeanLastDistFrame.data + idx_flt32 ) );
float* pfCurrMeanMinDist_LT = ( (float*) ( m_oMeanMinDistFrame_LT.data + idx_flt32 ) );
float* pfCurrMeanMinDist_ST = ( (float*) ( m_oMeanMinDistFrame_ST.data + idx_flt32 ) );
float* pfCurrMeanRawSegmRes_LT = ( (float*) ( m_oMeanRawSegmResFrame_LT.data + idx_flt32 ) );
float* pfCurrMeanRawSegmRes_ST = ( (float*) ( m_oMeanRawSegmResFrame_ST.data + idx_flt32 ) );
float* pfCurrMeanFinalSegmRes_LT = ( (float*) ( m_oMeanFinalSegmResFrame_LT.data + idx_flt32 ) );
float* pfCurrMeanFinalSegmRes_ST = ( (float*) ( m_oMeanFinalSegmResFrame_ST.data + idx_flt32 ) );
ushort* anLastIntraDesc = ( (ushort*) ( m_oLastDescFrame.data + idx_ushrt_rgb ) );
uchar* anLastColor = m_oLastColorFrame.data + idx_uchar_rgb;
const size_t nCurrColorDistThreshold = (size_t) ( ( ( *pfCurrDistThresholdFactor ) * m_nMinColorDistThreshold )
- ( ( !m_oUnstableRegionMask.data[idx_uchar] ) * STAB_COLOR_DIST_OFFSET ) );
const size_t nCurrDescDistThreshold = ( (size_t) 1 << ( (size_t) floor( *pfCurrDistThresholdFactor + 0.5f ) ) ) + m_nDescDistThreshold
+ ( m_oUnstableRegionMask.data[idx_uchar] * UNSTAB_DESC_DIST_OFFSET );
const size_t nCurrTotColorDistThreshold = nCurrColorDistThreshold * 3;
const size_t nCurrTotDescDistThreshold = nCurrDescDistThreshold * 3;
const size_t nCurrSCColorDistThreshold = nCurrTotColorDistThreshold / 2;
ushort anCurrInterDesc[3], anCurrIntraDesc[3];
const size_t anCurrIntraLBSPThresholds[3] =
{ m_anLBSPThreshold_8bitLUT[anCurrColor[0]], m_anLBSPThreshold_8bitLUT[anCurrColor[1]], m_anLBSPThreshold_8bitLUT[anCurrColor[2]] };
LBSP::computeRGBDescriptor( oInputImg, anCurrColor, x, y, anCurrIntraLBSPThresholds, anCurrIntraDesc );
m_oUnstableRegionMask.data[idx_uchar] =
( ( *pfCurrDistThresholdFactor ) > UNSTABLE_REG_RDIST_MIN
|| ( *pfCurrMeanRawSegmRes_LT - *pfCurrMeanFinalSegmRes_LT ) > UNSTABLE_REG_RATIO_MIN
|| ( *pfCurrMeanRawSegmRes_ST - *pfCurrMeanFinalSegmRes_ST ) > UNSTABLE_REG_RATIO_MIN ) ? 1 : 0;
size_t nGoodSamplesCount = 0, nSampleIdx = 0;
while ( nGoodSamplesCount < m_nRequiredBGSamples && nSampleIdx < m_nBGSamples )
{
const ushort* const anBGIntraDesc = (ushort*) ( m_voBGDescSamples[nSampleIdx].data + idx_ushrt_rgb );
const uchar* const anBGColor = m_voBGColorSamples[nSampleIdx].data + idx_uchar_rgb;
size_t nTotDescDist = 0;
size_t nTotSumDist = 0;
for ( size_t c = 0; c < 3; ++c )
{
const size_t nColorDist = absdiff_uchar( anCurrColor[c], anBGColor[c] );
if( nColorDist > nCurrSCColorDistThreshold )
goto failedcheck3ch;
size_t nIntraDescDist = hdist_ushort_8bitLUT( anCurrIntraDesc[c], anBGIntraDesc[c] );
LBSP::computeSingleRGBDescriptor( oInputImg, anBGColor[c], x, y, c, m_anLBSPThreshold_8bitLUT[anBGColor[c]], anCurrInterDesc[c] );
size_t nInterDescDist = hdist_ushort_8bitLUT( anCurrInterDesc[c], anBGIntraDesc[c] );
const size_t nDescDist = ( nIntraDescDist + nInterDescDist ) / 2;
const size_t nSumDist = std::min( ( nDescDist / 2 ) * ( s_nColorMaxDataRange_1ch / s_nDescMaxDataRange_1ch ) + nColorDist,
s_nColorMaxDataRange_1ch );
if( nSumDist > nCurrSCColorDistThreshold )
goto failedcheck3ch;
nTotDescDist += nDescDist;
nTotSumDist += nSumDist;
}
if( nTotDescDist > nCurrTotDescDistThreshold || nTotSumDist > nCurrTotColorDistThreshold )
goto failedcheck3ch;
if( nMinTotDescDist > nTotDescDist )
nMinTotDescDist = nTotDescDist;
if( nMinTotSumDist > nTotSumDist )
nMinTotSumDist = nTotSumDist;
nGoodSamplesCount++;
failedcheck3ch : nSampleIdx++;
}
const float fNormalizedLastDist = ( (float) L1dist_uchar( anLastColor, anCurrColor ) / s_nColorMaxDataRange_3ch
+ (float) hdist_ushort_8bitLUT( anLastIntraDesc, anCurrIntraDesc ) / s_nDescMaxDataRange_3ch ) / 2;
*pfCurrMeanLastDist = ( *pfCurrMeanLastDist ) * ( 1.0f - fRollAvgFactor_ST ) + fNormalizedLastDist * fRollAvgFactor_ST;
if( nGoodSamplesCount < m_nRequiredBGSamples )
{
// == foreground
const float fNormalizedMinDist = std::min(
1.0f,
( (float) nMinTotSumDist / s_nColorMaxDataRange_3ch + (float) nMinTotDescDist / s_nDescMaxDataRange_3ch ) / 2
+ (float) ( m_nRequiredBGSamples - nGoodSamplesCount ) / m_nRequiredBGSamples );
*pfCurrMeanMinDist_LT = ( *pfCurrMeanMinDist_LT ) * ( 1.0f - fRollAvgFactor_LT ) + fNormalizedMinDist * fRollAvgFactor_LT;
*pfCurrMeanMinDist_ST = ( *pfCurrMeanMinDist_ST ) * ( 1.0f - fRollAvgFactor_ST ) + fNormalizedMinDist * fRollAvgFactor_ST;
*pfCurrMeanRawSegmRes_LT = ( *pfCurrMeanRawSegmRes_LT ) * ( 1.0f - fRollAvgFactor_LT ) + fRollAvgFactor_LT;
*pfCurrMeanRawSegmRes_ST = ( *pfCurrMeanRawSegmRes_ST ) * ( 1.0f - fRollAvgFactor_ST ) + fRollAvgFactor_ST;
oCurrFGMask.data[idx_uchar] = UCHAR_MAX;
if( m_nModelResetCooldown && ( rand() % (size_t) FEEDBACK_T_LOWER ) == 0 )
{
const size_t s_rand = rand() % m_nBGSamples;
for ( size_t c = 0; c < 3; ++c )
{
* ( (ushort*) ( m_voBGDescSamples[s_rand].data + idx_ushrt_rgb + 2 * c ) ) = anCurrIntraDesc[c];
* ( m_voBGColorSamples[s_rand].data + idx_uchar_rgb + c ) = anCurrColor[c];
}
}
}
else
{
// == background
const float fNormalizedMinDist = ( (float) nMinTotSumDist / s_nColorMaxDataRange_3ch + (float) nMinTotDescDist / s_nDescMaxDataRange_3ch )
/ 2;
*pfCurrMeanMinDist_LT = ( *pfCurrMeanMinDist_LT ) * ( 1.0f - fRollAvgFactor_LT ) + fNormalizedMinDist * fRollAvgFactor_LT;
*pfCurrMeanMinDist_ST = ( *pfCurrMeanMinDist_ST ) * ( 1.0f - fRollAvgFactor_ST ) + fNormalizedMinDist * fRollAvgFactor_ST;
*pfCurrMeanRawSegmRes_LT = ( *pfCurrMeanRawSegmRes_LT ) * ( 1.0f - fRollAvgFactor_LT );
*pfCurrMeanRawSegmRes_ST = ( *pfCurrMeanRawSegmRes_ST ) * ( 1.0f - fRollAvgFactor_ST );
const size_t nLearningRate = learningRateOverride > 0 ? (size_t) ceil( learningRateOverride ) : (size_t) ceil( *pfCurrLearningRate );
if( ( rand() % nLearningRate ) == 0 )
{
const size_t s_rand = rand() % m_nBGSamples;
for ( size_t c = 0; c < 3; ++c )
{
* ( (ushort*) ( m_voBGDescSamples[s_rand].data + idx_ushrt_rgb + 2 * c ) ) = anCurrIntraDesc[c];
* ( m_voBGColorSamples[s_rand].data + idx_uchar_rgb + c ) = anCurrColor[c];
}
}
int x_rand, y_rand;
const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[idx_uchar];
if( bCurrUsing3x3Spread )
getRandNeighborPosition_3x3( x_rand, y_rand, x, y, LBSP::PATCH_SIZE / 2, m_oImgSize );
else
getRandNeighborPosition_5x5( x_rand, y_rand, x, y, LBSP::PATCH_SIZE / 2, m_oImgSize );
const size_t n_rand = rand();
const size_t idx_rand_uchar = m_oImgSize.width * y_rand + x_rand;
const size_t idx_rand_flt32 = idx_rand_uchar * 4;
const float fRandMeanLastDist = * ( (float*) ( m_oMeanLastDistFrame.data + idx_rand_flt32 ) );
const float fRandMeanRawSegmRes = * ( (float*) ( m_oMeanRawSegmResFrame_ST.data + idx_rand_flt32 ) );
if( ( n_rand % ( bCurrUsing3x3Spread ? nLearningRate : ( nLearningRate / 2 + 1 ) ) ) == 0
|| ( fRandMeanRawSegmRes > GHOSTDET_S_MIN && fRandMeanLastDist < GHOSTDET_D_MAX
&& ( n_rand % ( (size_t) m_fCurrLearningRateLowerCap ) ) == 0 ) )
{
const size_t idx_rand_uchar_rgb = idx_rand_uchar * 3;
const size_t idx_rand_ushrt_rgb = idx_rand_uchar_rgb * 2;
const size_t s_rand = rand() % m_nBGSamples;
for ( size_t c = 0; c < 3; ++c )
{
* ( (ushort*) ( m_voBGDescSamples[s_rand].data + idx_rand_ushrt_rgb + 2 * c ) ) = anCurrIntraDesc[c];
* ( m_voBGColorSamples[s_rand].data + idx_rand_uchar_rgb + c ) = anCurrColor[c];
}
}
}
if( m_oFGMask_last.data[idx_uchar]
|| ( std::min( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) < UNSTABLE_REG_RATIO_MIN && oCurrFGMask.data[idx_uchar] ) )
{
if( ( *pfCurrLearningRate ) < m_fCurrLearningRateUpperCap )
*pfCurrLearningRate += FEEDBACK_T_INCR / ( std::max( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) * ( *pfCurrVariationFactor ) );
}
else if( ( *pfCurrLearningRate ) > m_fCurrLearningRateLowerCap )
*pfCurrLearningRate -= FEEDBACK_T_DECR * ( *pfCurrVariationFactor ) / std::max( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST );
if( ( *pfCurrLearningRate ) < m_fCurrLearningRateLowerCap )
*pfCurrLearningRate = m_fCurrLearningRateLowerCap;
else if( ( *pfCurrLearningRate ) > m_fCurrLearningRateUpperCap )
*pfCurrLearningRate = m_fCurrLearningRateUpperCap;
if( std::max( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) > UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[idx_uchar] )
( *pfCurrVariationFactor ) += FEEDBACK_V_INCR;
else if( ( *pfCurrVariationFactor ) > FEEDBACK_V_DECR )
{
( *pfCurrVariationFactor ) -= m_oFGMask_last.data[idx_uchar] ? FEEDBACK_V_DECR / 4 :
m_oUnstableRegionMask.data[idx_uchar] ? FEEDBACK_V_DECR / 2 : FEEDBACK_V_DECR;
if( ( *pfCurrVariationFactor ) < FEEDBACK_V_DECR )
( *pfCurrVariationFactor ) = FEEDBACK_V_DECR;
}
if( ( *pfCurrDistThresholdFactor ) < std::pow( 1.0f + std::min( *pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST ) * 2, 2 ) )
( *pfCurrDistThresholdFactor ) += FEEDBACK_R_VAR * ( *pfCurrVariationFactor - FEEDBACK_V_DECR );
else
{
( *pfCurrDistThresholdFactor ) -= FEEDBACK_R_VAR / ( *pfCurrVariationFactor );
if( ( *pfCurrDistThresholdFactor ) < 1.0f )
( *pfCurrDistThresholdFactor ) = 1.0f;
}
if( popcount_ushort_8bitsLUT( anCurrIntraDesc ) >= 4 )
++nNonZeroDescCount;
for ( size_t c = 0; c < 3; ++c )
{
anLastIntraDesc[c] = anCurrIntraDesc[c];
anLastColor[c] = anCurrColor[c];
}
}
}
#if DISPLAY_SUBSENSE_DEBUG_INFO
std::cout << std::endl;
Point dbgpt(nDebugCoordX,nDebugCoordY);
Mat oMeanMinDistFrameNormalized; m_oMeanMinDistFrame_ST.copyTo(oMeanMinDistFrameNormalized);
circle(oMeanMinDistFrameNormalized,dbgpt,5,Scalar(1.0f));
resize(oMeanMinDistFrameNormalized,oMeanMinDistFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("d_min(x)",oMeanMinDistFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " d_min(" << dbgpt << ") = " << m_oMeanMinDistFrame_ST.at<float>(dbgpt) << std::endl;
Mat oMeanLastDistFrameNormalized; m_oMeanLastDistFrame.copyTo(oMeanLastDistFrameNormalized);
circle(oMeanLastDistFrameNormalized,dbgpt,5,Scalar(1.0f));
resize(oMeanLastDistFrameNormalized,oMeanLastDistFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("d_last(x)",oMeanLastDistFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " d_last(" << dbgpt << ") = " << m_oMeanLastDistFrame.at<float>(dbgpt) << std::endl;
Mat oMeanRawSegmResFrameNormalized; m_oMeanRawSegmResFrame_ST.copyTo(oMeanRawSegmResFrameNormalized);
circle(oMeanRawSegmResFrameNormalized,dbgpt,5,Scalar(1.0f));
resize(oMeanRawSegmResFrameNormalized,oMeanRawSegmResFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("s_avg(x)",oMeanRawSegmResFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " s_avg(" << dbgpt << ") = " << m_oMeanRawSegmResFrame_ST.at<float>(dbgpt) << std::endl;
Mat oMeanFinalSegmResFrameNormalized; m_oMeanFinalSegmResFrame_ST.copyTo(oMeanFinalSegmResFrameNormalized);
circle(oMeanFinalSegmResFrameNormalized,dbgpt,5,Scalar(1.0f));
resize(oMeanFinalSegmResFrameNormalized,oMeanFinalSegmResFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("z_avg(x)",oMeanFinalSegmResFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " z_avg(" << dbgpt << ") = " << m_oMeanFinalSegmResFrame_ST.at<float>(dbgpt) << std::endl;
Mat oDistThresholdFrameNormalized; m_oDistThresholdFrame.convertTo(oDistThresholdFrameNormalized,CV_32FC1,0.25f,-0.25f);
circle(oDistThresholdFrameNormalized,dbgpt,5,Scalar(1.0f));
resize(oDistThresholdFrameNormalized,oDistThresholdFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("r(x)",oDistThresholdFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " r(" << dbgpt << ") = " << m_oDistThresholdFrame.at<float>(dbgpt) << std::endl;
Mat oVariationModulatorFrameNormalized; normalize(m_oVariationModulatorFrame,oVariationModulatorFrameNormalized,0,255,NORM_MINMAX,CV_8UC1);
circle(oVariationModulatorFrameNormalized,dbgpt,5,Scalar(255));
resize(oVariationModulatorFrameNormalized,oVariationModulatorFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("v(x)",oVariationModulatorFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " v(" << dbgpt << ") = " << m_oVariationModulatorFrame.at<float>(dbgpt) << std::endl;
Mat oUpdateRateFrameNormalized; m_oUpdateRateFrame.convertTo(oUpdateRateFrameNormalized,CV_32FC1,1.0f/FEEDBACK_T_UPPER,-FEEDBACK_T_LOWER/FEEDBACK_T_UPPER);
circle(oUpdateRateFrameNormalized,dbgpt,5,Scalar(1.0f));
resize(oUpdateRateFrameNormalized,oUpdateRateFrameNormalized,DEFAULT_FRAME_SIZE);
imshow("t(x)",oUpdateRateFrameNormalized);
std::cout << std::fixed << std::setprecision(5) << " t(" << dbgpt << ") = " << m_oUpdateRateFrame.at<float>(dbgpt) << std::endl;
#endif //DISPLAY_SUBSENSE_DEBUG_INFO
bitwise_xor( oCurrFGMask, m_oRawFGMask_last, m_oRawFGBlinkMask_curr );
bitwise_or( m_oRawFGBlinkMask_curr, m_oRawFGBlinkMask_last, m_oBlinksFrame );
m_oRawFGBlinkMask_curr.copyTo( m_oRawFGBlinkMask_last );
oCurrFGMask.copyTo( m_oRawFGMask_last );
morphologyEx( oCurrFGMask, m_oFGMask_PreFlood, MORPH_CLOSE, Mat() );
m_oFGMask_PreFlood.copyTo( m_oFGMask_FloodedHoles );
floodFill( m_oFGMask_FloodedHoles, Point( 0, 0 ), UCHAR_MAX );
bitwise_not( m_oFGMask_FloodedHoles, m_oFGMask_FloodedHoles );
erode( m_oFGMask_PreFlood, m_oFGMask_PreFlood, Mat(), Point( -1, -1 ), 3 );
bitwise_or( oCurrFGMask, m_oFGMask_FloodedHoles, oCurrFGMask );
bitwise_or( oCurrFGMask, m_oFGMask_PreFlood, oCurrFGMask );
medianBlur( oCurrFGMask, m_oFGMask_last, m_nMedianBlurKernelSize );
dilate( m_oFGMask_last, m_oFGMask_last_dilated, Mat(), Point( -1, -1 ), 3 );
bitwise_and( m_oBlinksFrame, m_oFGMask_last_dilated_inverted, m_oBlinksFrame );
bitwise_not( m_oFGMask_last_dilated, m_oFGMask_last_dilated_inverted );
bitwise_and( m_oBlinksFrame, m_oFGMask_last_dilated_inverted, m_oBlinksFrame );
m_oFGMask_last.copyTo( oCurrFGMask );
addWeighted( m_oMeanFinalSegmResFrame_LT, ( 1.0f - fRollAvgFactor_LT ), m_oFGMask_last, ( 1.0 / UCHAR_MAX ) * fRollAvgFactor_LT, 0,
m_oMeanFinalSegmResFrame_LT, CV_32F );
addWeighted( m_oMeanFinalSegmResFrame_ST, ( 1.0f - fRollAvgFactor_ST ), m_oFGMask_last, ( 1.0 / UCHAR_MAX ) * fRollAvgFactor_ST, 0,
m_oMeanFinalSegmResFrame_ST, CV_32F );
const float fCurrNonZeroDescRatio = (float) nNonZeroDescCount / m_nKeyPoints;
if( fCurrNonZeroDescRatio < LBSPDESC_NONZERO_RATIO_MIN && m_fLastNonZeroDescRatio < LBSPDESC_NONZERO_RATIO_MIN )
{
for ( size_t t = 0; t <= UCHAR_MAX; ++t )
if( m_anLBSPThreshold_8bitLUT[t] > saturate_cast<uchar>( m_nLBSPThresholdOffset + ceil( t * m_fRelLBSPThreshold / 4 ) ) )
--m_anLBSPThreshold_8bitLUT[t];
}
else if( fCurrNonZeroDescRatio > LBSPDESC_NONZERO_RATIO_MAX && m_fLastNonZeroDescRatio > LBSPDESC_NONZERO_RATIO_MAX )
{
for ( size_t t = 0; t <= UCHAR_MAX; ++t )
if( m_anLBSPThreshold_8bitLUT[t] < saturate_cast<uchar>( m_nLBSPThresholdOffset + UCHAR_MAX * m_fRelLBSPThreshold ) )
++m_anLBSPThreshold_8bitLUT[t];
}
m_fLastNonZeroDescRatio = fCurrNonZeroDescRatio;
if( m_bLearningRateScalingEnabled )
{
resize( oInputImg, m_oDownSampledColorFrame, m_oDownSampledFrameSize, 0, 0, INTER_AREA );
accumulateWeighted( m_oDownSampledColorFrame, m_oMeanDownSampledLastDistFrame_LT, fRollAvgFactor_LT );
accumulateWeighted( m_oDownSampledColorFrame, m_oMeanDownSampledLastDistFrame_ST, fRollAvgFactor_ST );
size_t nTotColorDiff = 0;
for ( int i = 0; i < m_oMeanDownSampledLastDistFrame_ST.rows; ++i )
{
const size_t idx1 = m_oMeanDownSampledLastDistFrame_ST.step.p[0] * i;
for ( int j = 0; j < m_oMeanDownSampledLastDistFrame_ST.cols; ++j )
{
const size_t idx2 = idx1 + m_oMeanDownSampledLastDistFrame_ST.step.p[1] * j;
nTotColorDiff +=
( m_nImgChannels == 1 ) ?
(size_t) fabs(
( *(float*) ( m_oMeanDownSampledLastDistFrame_ST.data + idx2 ) )
- ( *(float*) ( m_oMeanDownSampledLastDistFrame_LT.data + idx2 ) ) ) / 2 : //(m_nImgChannels==3)
std::max(
(size_t) fabs(
( *(float*) ( m_oMeanDownSampledLastDistFrame_ST.data + idx2 ) )
- ( *(float*) ( m_oMeanDownSampledLastDistFrame_LT.data + idx2 ) ) ),
std::max(
(size_t) fabs(
( *(float*) ( m_oMeanDownSampledLastDistFrame_ST.data + idx2 + 4 ) )
- ( *(float*) ( m_oMeanDownSampledLastDistFrame_LT.data + idx2 + 4 ) ) ),
(size_t) fabs(
( *(float*) ( m_oMeanDownSampledLastDistFrame_ST.data + idx2 + 8 ) )
- ( *(float*) ( m_oMeanDownSampledLastDistFrame_LT.data + idx2 + 8 ) ) ) ) );
}
}
const float fCurrColorDiffRatio = (float) nTotColorDiff / ( m_oMeanDownSampledLastDistFrame_ST.rows * m_oMeanDownSampledLastDistFrame_ST.cols );
if( m_bAutoModelResetEnabled )
{
if( m_nFramesSinceLastReset > 1000 )
m_bAutoModelResetEnabled = false;
else if( fCurrColorDiffRatio >= FRAMELEVEL_COLOR_DIFF_RESET_THRESHOLD && m_nModelResetCooldown == 0 )
{
m_nFramesSinceLastReset = 0;
refreshModel( 0.1f ); // reset 10% of the bg model
m_nModelResetCooldown = m_nSamplesForMovingAvgs;
m_oUpdateRateFrame = Scalar( 1.0f );
}
else
++m_nFramesSinceLastReset;
}
else if( fCurrColorDiffRatio >= FRAMELEVEL_COLOR_DIFF_RESET_THRESHOLD * 2 )
{
m_nFramesSinceLastReset = 0;
m_bAutoModelResetEnabled = true;
}
if( fCurrColorDiffRatio >= FRAMELEVEL_COLOR_DIFF_RESET_THRESHOLD / 2 )
{
m_fCurrLearningRateLowerCap = (float) std::max( (int) FEEDBACK_T_LOWER >> (int) ( fCurrColorDiffRatio / 2 ), 1 );
m_fCurrLearningRateUpperCap = (float) std::max( (int) FEEDBACK_T_UPPER >> (int) ( fCurrColorDiffRatio / 2 ), 1 );
}
else
{
m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER;
m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER;
}
if( m_nModelResetCooldown > 0 )
--m_nModelResetCooldown;
}
}
void MotionSaliencySuBSENSE::getBackgroundImage( OutputArray backgroundImage ) const
{
CV_Assert( m_bInitialized );
Mat oAvgBGImg = Mat::zeros( m_oImgSize, CV_32FC( (int )m_nImgChannels ) );
for ( size_t s = 0; s < m_nBGSamples; ++s )
{
for ( int y = 0; y < m_oImgSize.height; ++y )
{
for ( int x = 0; x < m_oImgSize.width; ++x )
{
const size_t idx_nimg = m_voBGColorSamples[s].step.p[0] * y + m_voBGColorSamples[s].step.p[1] * x;
const size_t idx_flt32 = idx_nimg * 4;
float* oAvgBgImgPtr = (float*) ( oAvgBGImg.data + idx_flt32 );
const uchar* const oBGImgPtr = m_voBGColorSamples[s].data + idx_nimg;
for ( size_t c = 0; c < m_nImgChannels; ++c )
oAvgBgImgPtr[c] += ( (float) oBGImgPtr[c] ) / m_nBGSamples;
}
}
}
oAvgBGImg.convertTo( backgroundImage, CV_8U );
}
void MotionSaliencySuBSENSE::setAutomaticModelReset( bool b )
{
m_bAutoModelResetEnabled = b;
}
}/* namespace cv */
......@@ -52,7 +52,7 @@ CV_INIT_ALGORITHM(
reinterpret_cast<SizeGetter>( &StaticSaliencySpectralResidual::getWsize ),
reinterpret_cast<SizeSetter>( &StaticSaliencySpectralResidual::setWsize ) ) );
CV_INIT_ALGORITHM( MotionSaliencySuBSENSE, "SALIENCY.SuBSENSE", );
//CV_INIT_ALGORITHM( MotionSaliencySuBSENSE, "SALIENCY.SuBSENSE", );
CV_INIT_ALGORITHM(
ObjectnessBING, "SALIENCY.BING",
......@@ -62,7 +62,7 @@ bool initModule_saliency( void )
{
bool all = true;
all &= !StaticSaliencySpectralResidual_info_auto.name().empty();
all &= !MotionSaliencySuBSENSE_info_auto.name().empty();
//all &= !MotionSaliencySuBSENSE_info_auto.name().empty();
all &= !ObjectnessBING_info_auto.name().empty();
return all;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment