diff --git a/gui/qt/bgslibrary_gui.pro b/gui/qt/bgslibrary_gui.pro index 53caf04b50b841cedfaa92d8725824370ccabef7..615e30dcf23acded22c6842363c40bd1ebb7d35c 100644 --- a/gui/qt/bgslibrary_gui.pro +++ b/gui/qt/bgslibrary_gui.pro @@ -127,7 +127,7 @@ SOURCES += bgslibrary_gui.cpp\ ../../src/algorithms/MultiLayer/LocalBinaryPattern.cpp \ ../../src/algorithms/PBAS/PBAS.cpp \ ../../src/algorithms/SigmaDelta/sdLaMa091.cpp \ - ../../src/algorithms/T2F/FuzzyUtils.cpp \ + ../../src/tools/FuzzyUtils.cpp \ ../../src/algorithms/T2F/MRF.cpp \ ../../src/algorithms/T2F/T2FGMM.cpp \ ../../src/algorithms/T2F/T2FMRF.cpp \ @@ -239,7 +239,7 @@ HEADERS += mainwindow.h \ ../../src/algorithms/MultiLayer/OpenCvLegacyIncludes.h \ ../../src/algorithms/PBAS/PBAS.h \ ../../src/algorithms/SigmaDelta/sdLaMa091.h \ - ../../src/algorithms/T2F/FuzzyUtils.h \ + ../../src/tools/FuzzyUtils.h \ ../../src/algorithms/T2F/MRF.h \ ../../src/algorithms/T2F/T2FGMM.h \ ../../src/algorithms/T2F/T2FMRF.h \ @@ -249,7 +249,7 @@ HEADERS += mainwindow.h \ ../../src/algorithms/VuMeter/TBackgroundVuMeter.h \ ../../src/algorithms/AdaptiveBackgroundLearning.h \ ../../src/algorithms/AdaptiveSelectiveBackgroundLearning.h \ - ../../src/algorithms/bgslibrary.h \ + ../../src/algorithms/algorithms.h \ ../../src/algorithms/DPAdaptiveMedian.h \ ../../src/algorithms/DPEigenbackground.h \ ../../src/algorithms/DPGrimsonGMM.h \ diff --git a/src/FrameProcessor.cpp b/src/FrameProcessor.cpp index ab47cda08804cb56772d6a3ce939fce3fecb9b26..38eb71f989b0852abc85281cfde80507e558a832 100644 --- a/src/FrameProcessor.cpp +++ b/src/FrameProcessor.cpp @@ -159,7 +159,7 @@ namespace bgslibrary codeBook = std::make_shared<CodeBook>(); if (enableForegroundMaskAnalysis) - foregroundMaskAnalysis = std::make_shared<ForegroundMaskAnalysis>(); + foregroundMaskAnalysis = std::make_shared<tools::ForegroundMaskAnalysis>(); } void FrameProcessor::process(const std::string name, const std::shared_ptr<IBGS> &bgs, const cv::Mat &img_input, cv::Mat &img_bgs) diff --git a/src/FrameProcessor.h b/src/FrameProcessor.h index 8c0b10f1c9d7db0ff6f95c20b189669a62f2d3a2..1eec6d601918556f62e1926d8b5735a996778e58 100644 --- a/src/FrameProcessor.h +++ b/src/FrameProcessor.h @@ -202,7 +202,7 @@ namespace bgslibrary std::shared_ptr<CodeBook> codeBook; bool enableCodeBook = false; - std::shared_ptr<ForegroundMaskAnalysis> foregroundMaskAnalysis; + std::shared_ptr<tools::ForegroundMaskAnalysis> foregroundMaskAnalysis; bool enableForegroundMaskAnalysis = false; public: diff --git a/src/algorithms/CodeBook.h b/src/algorithms/CodeBook.h index 3fc8ff90f6e23bd56b0700d257c45ab18f44403d..512d59bdbebfae780509f6ecfe27229aa835299b 100644 --- a/src/algorithms/CodeBook.h +++ b/src/algorithms/CodeBook.h @@ -8,18 +8,28 @@ namespace bgslibrary { namespace algorithms { - struct codeword { - float min; - float max; - float f; - float l; - int first; - int last; - bool isStale; - }; + namespace codebook { + struct codeword { + float min; + float max; + float f; + float l; + int first; + int last; + bool isStale; + }; + } class CodeBook : public IBGS { + public: + typedef codebook::codeword codeword; + + CodeBook(); + ~CodeBook(); + + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + private: static const int Tdel = 200; static const int Tadd = 150; @@ -34,13 +44,6 @@ namespace bgslibrary std::vector<codeword> **cbMain; std::vector<codeword> **cbCache; - public: - CodeBook(); - ~CodeBook(); - - void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); - - private: void initializeCodebook(int w, int h); void update_cb(const cv::Mat& frame); void fg_cb(const cv::Mat& frame, cv::Mat& fg); diff --git a/src/algorithms/DPAdaptiveMedian.h b/src/algorithms/DPAdaptiveMedian.h index 953f35fe73c8d4b17a2d515c51832973e582670f..fbad2da636f3553c842ee5ff5ebe9d0beca80f56 100644 --- a/src/algorithms/DPAdaptiveMedian.h +++ b/src/algorithms/DPAdaptiveMedian.h @@ -6,8 +6,6 @@ #include "IBGS.h" #include "dp/AdaptiveMedianBGS.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -16,15 +14,15 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - AdaptiveMedianParams params; - AdaptiveMedianBGS bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; int threshold; int samplingRate; int learningFrames; + IplImage* frame; + dp::RgbImage frame_data; + dp::AdaptiveMedianParams params; + dp::AdaptiveMedianBGS bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPAdaptiveMedian(); diff --git a/src/algorithms/DPEigenbackground.h b/src/algorithms/DPEigenbackground.h index 34b1457c6e98e70b39a268a8a7d1f1e07d680403..0d9b18dff20b25c6448e64429cfa5cb9a50fce71 100644 --- a/src/algorithms/DPEigenbackground.h +++ b/src/algorithms/DPEigenbackground.h @@ -7,8 +7,6 @@ #include "dp/Eigenbackground.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -17,17 +15,15 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - EigenbackgroundParams params; - Eigenbackground bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - int threshold; int historySize; int embeddedDim; + IplImage* frame; + dp::RgbImage frame_data; + dp::EigenbackgroundParams params; + dp::Eigenbackground bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPEigenbackground(); diff --git a/src/algorithms/DPGrimsonGMM.h b/src/algorithms/DPGrimsonGMM.h index 2fa5bff3d3a7cb48f6caf597d450c8a84373f581..6d3d3e3edc5133ebc654cd14898e0a8fb6824ef1 100644 --- a/src/algorithms/DPGrimsonGMM.h +++ b/src/algorithms/DPGrimsonGMM.h @@ -7,8 +7,6 @@ #include "dp/GrimsonGMM.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -17,17 +15,15 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - GrimsonParams params; - GrimsonGMM bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; int gaussians; + IplImage* frame; + dp::RgbImage frame_data; + dp::GrimsonParams params; + dp::GrimsonGMM bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPGrimsonGMM(); diff --git a/src/algorithms/DPMean.h b/src/algorithms/DPMean.h index 6c3cdd7f5572c29cef7f5af0256fbdb39cceb09c..311906f13329ca0f4a987f5ff59def959104427e 100644 --- a/src/algorithms/DPMean.h +++ b/src/algorithms/DPMean.h @@ -7,8 +7,6 @@ #include "dp/MeanBGS.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -17,17 +15,15 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - MeanParams params; - MeanBGS bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - int threshold; double alpha; int learningFrames; + IplImage* frame; + dp::RgbImage frame_data; + dp::MeanParams params; + dp::MeanBGS bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPMean(); diff --git a/src/algorithms/DPPratiMediod.h b/src/algorithms/DPPratiMediod.h index 8833016a406865b41edfbb59f07c0ea10aba1470..eb93683ac93acee4fa32ab4db18c8d88cd1813bb 100644 --- a/src/algorithms/DPPratiMediod.h +++ b/src/algorithms/DPPratiMediod.h @@ -7,8 +7,6 @@ #include "dp/PratiMediodBGS.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -17,18 +15,16 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - PratiParams params; - PratiMediodBGS bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - int threshold; int samplingRate; int historySize; int weight; + IplImage* frame; + dp::RgbImage frame_data; + dp::PratiParams params; + dp::PratiMediodBGS bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPPratiMediod(); diff --git a/src/algorithms/DPTexture.cpp b/src/algorithms/DPTexture.cpp index 0448a797497165e9e23f4ee33edd438f77582fac..e56ca87090125d1510aa2abfe30f97383f1a64fa 100644 --- a/src/algorithms/DPTexture.cpp +++ b/src/algorithms/DPTexture.cpp @@ -4,9 +4,9 @@ using namespace bgslibrary::algorithms; -DPTexture::DPTexture(): - IBGS(quote(DPTexture)), - // enableFiltering(true) +DPTexture::DPTexture() : + IBGS(quote(DPTexture)) + //, enableFiltering(true) { debug_construction(DPTexture); initLoadSaveConfig(algorithmName); @@ -29,7 +29,8 @@ void DPTexture::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & { init(img_input, img_output, img_bgmodel); - frame = new IplImage(img_input); + //frame = new IplImage(img_input); + frame = cvCloneImage(&(IplImage)img_input); if (firstTime) { width = img_input.size().width; @@ -47,20 +48,20 @@ void DPTexture::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & cvZero(tempMask.Ptr()); // create background model - bgModel = new TextureArray[size]; + bgModel = new dp::TextureArray[size]; texture = cvCreateImage(cvSize(width, height), 8, 3); cvZero(texture.Ptr()); modeArray = new unsigned char[size]; - curTextureHist = new TextureHistogram[size]; + curTextureHist = new dp::TextureHistogram[size]; // initialize background model bgs.LBP(image, texture); bgs.Histogram(texture, curTextureHist); - for (int y = REGION_R + TEXTURE_R; y < height - REGION_R - TEXTURE_R; ++y) - for (int x = REGION_R + TEXTURE_R; x < width - REGION_R - TEXTURE_R; ++x) { + for (int y = dp::REGION_R + dp::TEXTURE_R; y < height - dp::REGION_R - dp::TEXTURE_R; ++y) { + for (int x = dp::REGION_R + dp::TEXTURE_R; x < width - dp::REGION_R - dp::TEXTURE_R; ++x) { int index = x + y*width; - for (int m = 0; m < NUM_MODES; ++m) { - for (int i = 0; i < NUM_BINS; ++i) { + for (int m = 0; m < dp::NUM_MODES; ++m) { + for (int i = 0; i < dp::NUM_BINS; ++i) { bgModel[index].mode[m].r[i] = curTextureHist[index].r[i]; bgModel[index].mode[m].g[i] = curTextureHist[index].g[i]; bgModel[index].mode[m].b[i] = curTextureHist[index].b[i]; @@ -79,7 +80,7 @@ void DPTexture::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & // perform background subtraction bgs.LBP(image, texture); bgs.Histogram(texture, curTextureHist); - bgs.BgsCompare(bgModel, curTextureHist, modeArray, THRESHOLD, fgMask); + bgs.BgsCompare(bgModel, curTextureHist, modeArray, dp::THRESHOLD, fgMask); //if(enableFiltering) //{ diff --git a/src/algorithms/DPTexture.h b/src/algorithms/DPTexture.h index a908687a138ba1d4cf151a0a950db02b04f17f7c..b78dffeca593a7fae41486d9262b4248eb0ffc9e 100644 --- a/src/algorithms/DPTexture.h +++ b/src/algorithms/DPTexture.h @@ -5,6 +5,11 @@ #include "opencv2/core/version.hpp" #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 +// opencv legacy includes +#include "opencv2/core/core_c.h" +#include "opencv2/core/types_c.h" +#include "opencv2/imgproc/imgproc_c.h" + #include "dp/TextureBGS.h" //#include "ConnectedComponents.h" @@ -18,15 +23,15 @@ namespace bgslibrary int width; int height; int size; - TextureBGS bgs; - IplImage* frame; - RgbImage image; - BwImage fgMask; - BwImage tempMask; - TextureArray* bgModel; - RgbImage texture; unsigned char* modeArray; - TextureHistogram* curTextureHist; + IplImage* frame; + dp::TextureBGS bgs; + dp::RgbImage image; + dp::BwImage fgMask; + dp::BwImage tempMask; + dp::TextureArray* bgModel; + dp::RgbImage texture; + dp::TextureHistogram* curTextureHist; //ConnectedComponents cc; //CBlobResult largeBlobs; //IplConvKernel* dilateElement; diff --git a/src/algorithms/DPWrenGA.h b/src/algorithms/DPWrenGA.h index 0fe1e6c7a8f8b87fd21146b5d8beb8195032cb33..4be0d8ca907677df681ef9ece231c5f4ceb41d09 100644 --- a/src/algorithms/DPWrenGA.h +++ b/src/algorithms/DPWrenGA.h @@ -7,8 +7,6 @@ #include "dp/WrenGA.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -17,17 +15,15 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - WrenParams params; - WrenGA bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; int learningFrames; + IplImage* frame; + dp::RgbImage frame_data; + dp::WrenParams params; + dp::WrenGA bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPWrenGA(); diff --git a/src/algorithms/DPZivkovicAGMM.h b/src/algorithms/DPZivkovicAGMM.h index 4100ee63521190e234e5fd6288b5dcfd4db8c800..ca2c1a6e2e210f42de1a8bff3fa8f3c43cc141fa 100644 --- a/src/algorithms/DPZivkovicAGMM.h +++ b/src/algorithms/DPZivkovicAGMM.h @@ -7,8 +7,6 @@ #include "dp/ZivkovicAGMM.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -17,17 +15,15 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - ZivkovicParams params; - ZivkovicAGMM bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; int gaussians; + IplImage* frame; + dp::RgbImage frame_data; + dp::ZivkovicParams params; + dp::ZivkovicAGMM bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: DPZivkovicAGMM(); diff --git a/src/algorithms/FuzzyChoquetIntegral.cpp b/src/algorithms/FuzzyChoquetIntegral.cpp index b9140bfd17296af8d98ed483a8df5f05ba3f535d..90dfe6eaee5f6cc8f90f44f14de280dc26ee862e 100644 --- a/src/algorithms/FuzzyChoquetIntegral.cpp +++ b/src/algorithms/FuzzyChoquetIntegral.cpp @@ -128,10 +128,9 @@ void FuzzyChoquetIntegral::process(const cv::Mat &img_input, cv::Mat &img_output #ifndef MEX_COMPILE_FLAG if (showOutput) { - cvShowImage(algorithmName + "_LBP_IN", lbp_input_f1); - cvShowImage(algorithmName + "_LBP_BG", lbp_background_f1); - cvShowImage(algorithmName + "_FG_PROB", integral_choquet_f1); - + cv::imshow(algorithmName + "_LBP_IN", cv::cvarrToMat(lbp_input_f1)); + cv::imshow(algorithmName + "_LBP_BG", cv::cvarrToMat(lbp_background_f1)); + cv::imshow(algorithmName + "_FG_PROB", cv::cvarrToMat(integral_choquet_f1)); cv::imshow(algorithmName + "_BG", img_background); cv::imshow(algorithmName + "_FG", img_foreground); } diff --git a/src/algorithms/FuzzyChoquetIntegral.h b/src/algorithms/FuzzyChoquetIntegral.h index ba348dbeaebd1daaf9fd3bd983373bf56be432a9..992f65148b61a15579b020a3f69aa6679920a3e0 100644 --- a/src/algorithms/FuzzyChoquetIntegral.h +++ b/src/algorithms/FuzzyChoquetIntegral.h @@ -5,7 +5,7 @@ #include "opencv2/core/version.hpp" #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -#include "T2F/FuzzyUtils.h" +#include "../tools/FuzzyUtils.h" namespace bgslibrary { @@ -13,9 +13,15 @@ namespace bgslibrary { class FuzzyChoquetIntegral : public IBGS { + public: + FuzzyChoquetIntegral(); + ~FuzzyChoquetIntegral(); + + typedef bgslibrary::tools::FuzzyUtils FuzzyUtils; + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + private: - long long frameNumber; - + long frameNumber; int framesToLearn; double alphaLearn; double alphaUpdate; @@ -23,17 +29,9 @@ namespace bgslibrary int option; bool smooth; double threshold; - FuzzyUtils fu; cv::Mat img_background_f3; - - public: - FuzzyChoquetIntegral(); - ~FuzzyChoquetIntegral(); - - void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); - - private: + void save_config(cv::FileStorage &fs); void load_config(cv::FileStorage &fs); }; diff --git a/src/algorithms/FuzzySugenoIntegral.cpp b/src/algorithms/FuzzySugenoIntegral.cpp index 03f63fa57ef71cb954235f3b52f1d2c223f03ab5..5ac009af330383cec02cedacf156c84708949908 100644 --- a/src/algorithms/FuzzySugenoIntegral.cpp +++ b/src/algorithms/FuzzySugenoIntegral.cpp @@ -127,10 +127,9 @@ void FuzzySugenoIntegral::process(const cv::Mat &img_input, cv::Mat &img_output, #ifndef MEX_COMPILE_FLAG if (showOutput) { - cvShowImage(algorithmName + "_LBP_IN", lbp_input_f1); - cvShowImage(algorithmName + "_LBP_BG", lbp_background_f1); - cvShowImage(algorithmName + "_FG_PROB", integral_sugeno_f1); - + cv::imshow(algorithmName + "_LBP_IN", cv::cvarrToMat(lbp_input_f1)); + cv::imshow(algorithmName + "_LBP_BG", cv::cvarrToMat(lbp_background_f1)); + cv::imshow(algorithmName + "_FG_PROB", cv::cvarrToMat(integral_sugeno_f1)); cv::imshow(algorithmName + "_BG", img_background); cv::imshow(algorithmName + "_FG", img_foreground); } diff --git a/src/algorithms/FuzzySugenoIntegral.h b/src/algorithms/FuzzySugenoIntegral.h index ed680a3cfc753a53c6f5029e0eeb11af2c75d59e..fefd11be42bcea2e44d7045971d1ec155d1c3827 100644 --- a/src/algorithms/FuzzySugenoIntegral.h +++ b/src/algorithms/FuzzySugenoIntegral.h @@ -5,7 +5,7 @@ #include "opencv2/core/version.hpp" #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -#include "T2F/FuzzyUtils.h" +#include "../tools/FuzzyUtils.h" namespace bgslibrary { @@ -13,9 +13,15 @@ namespace bgslibrary { class FuzzySugenoIntegral : public IBGS { - private: - long long frameNumber; + public: + FuzzySugenoIntegral(); + ~FuzzySugenoIntegral(); + typedef bgslibrary::tools::FuzzyUtils FuzzyUtils; + void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); + + private: + long frameNumber; int framesToLearn; double alphaLearn; double alphaUpdate; @@ -23,17 +29,9 @@ namespace bgslibrary int option; bool smooth; double threshold; - - FuzzyUtils fu; cv::Mat img_background_f3; + FuzzyUtils fu; - public: - FuzzySugenoIntegral(); - ~FuzzySugenoIntegral(); - - void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel); - - private: void save_config(cv::FileStorage &fs); void load_config(cv::FileStorage &fs); }; diff --git a/src/algorithms/IMBS/IMBS.cpp b/src/algorithms/IMBS/IMBS.cpp index acf3c5b6a08345f6154b33b83fe875077a79ee67..bab74da8deb60b83f84b49a64d6869c085ca45c5 100644 --- a/src/algorithms/IMBS/IMBS.cpp +++ b/src/algorithms/IMBS/IMBS.cpp @@ -1,5 +1,6 @@ #include "IMBS.hpp" +using namespace bgslibrary::algorithms::imbs; using namespace std; using namespace cv; diff --git a/src/algorithms/IMBS/IMBS.hpp b/src/algorithms/IMBS/IMBS.hpp index 7a89949e6a2a8d62e5b72e10bafef3f2d800f2e9..9ddc66fac1b27bdf7ec92b9ad73b8cbb677419a6 100644 --- a/src/algorithms/IMBS/IMBS.hpp +++ b/src/algorithms/IMBS/IMBS.hpp @@ -12,169 +12,173 @@ #include <opencv2/imgproc/imgproc_c.h> #include <opencv2/highgui/highgui_c.h> -using namespace cv; -using namespace std; - -class BackgroundSubtractorIMBS +namespace bgslibrary { -public: - //! the default constructor - BackgroundSubtractorIMBS(); - //! the full constructor - BackgroundSubtractorIMBS(double fps, - unsigned int fgThreshold = 15, - unsigned int associationThreshold = 5, - double samplingPeriod = 500., - unsigned int minBinHeight = 2, - unsigned int numSamples = 30, - double alpha = 0.65, - double beta = 1.15, - double tau_s = 60., - double tau_h = 40., - double minArea = 30., - double persistencePeriod = 10000., - bool morphologicalFiltering = false - ); - //! the destructor - ~BackgroundSubtractorIMBS(); - //! the update operator - void apply(InputArray image, OutputArray fgmask, double learningRate = -1.); - - //! computes a background image which shows only the highest bin for each pixel - void getBackgroundImage(OutputArray backgroundImage) const; - - //! re-initiaization method - void initialize(Size frameSize, int frameType); - -private: - //method for creating the background model - void createBg(unsigned int bg_sample_number); - //method for updating the background model - void updateBg(); - //method for computing the foreground mask - void getFg(); - //method for suppressing shadows and highlights - void hsvSuppression(); - //method for refining foreground mask - void filterFg(); - //method for filtering out blobs smaller than a given area - void areaThresholding(); - //method for getting the current time - double getTimestamp(); - //method for converting from RGB to HSV - Mat convertImageRGBtoHSV(const Mat& imageRGB); - //method for changing the bg in case of sudden changes - void changeBg(); - - //current input RGB frame - Mat frame; - vector<Mat> frameBGR; - //frame size - Size frameSize; - //frame type - int frameType; - //total number of pixels in frame - unsigned int numPixels; - //current background sample - Mat bgSample; - vector<Mat> bgSampleBGR; - //current background image which shows only the highest bin for each pixel - //(just for displaying purposes) - Mat bgImage; - //current foreground mask - Mat fgmask; - - Mat fgfiltered; - - //number of fps - double fps; - //time stamp in milliseconds (ms) - double timestamp; - //previous time stamp in milliseconds (ms) - double prev_timestamp; - double initial_tick_count; - //initial message to be shown until the first bg model is ready - Mat initialMsgGray; - Mat initialMsgRGB; - - //struct for modeling the background values for a single pixel - typedef struct Bins { - void initialize(unsigned int numSamples) { - binValues = new Vec3b[numSamples]; - binHeights = new uchar[numSamples]; - isFg = new bool[numSamples]; - } - ~Bins() { - if (binValues) { delete[] binValues; } - if (binHeights) { delete[] binHeights; } - if (isFg) { delete[] isFg; } + namespace algorithms + { + namespace imbs + { + class BackgroundSubtractorIMBS + { + public: + //! the default constructor + BackgroundSubtractorIMBS(); + //! the full constructor + BackgroundSubtractorIMBS(double fps, + unsigned int fgThreshold = 15, + unsigned int associationThreshold = 5, + double samplingPeriod = 500., + unsigned int minBinHeight = 2, + unsigned int numSamples = 30, + double alpha = 0.65, + double beta = 1.15, + double tau_s = 60., + double tau_h = 40., + double minArea = 30., + double persistencePeriod = 10000., + bool morphologicalFiltering = false + ); + //! the destructor + ~BackgroundSubtractorIMBS(); + //! the update operator + void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRate = -1.); + + //! computes a background image which shows only the highest bin for each pixel + void getBackgroundImage(cv::OutputArray backgroundImage) const; + + //! re-initiaization method + void initialize(cv::Size frameSize, int frameType); + + private: + //method for creating the background model + void createBg(unsigned int bg_sample_number); + //method for updating the background model + void updateBg(); + //method for computing the foreground mask + void getFg(); + //method for suppressing shadows and highlights + void hsvSuppression(); + //method for refining foreground mask + void filterFg(); + //method for filtering out blobs smaller than a given area + void areaThresholding(); + //method for getting the current time + double getTimestamp(); + //method for converting from RGB to HSV + cv::Mat convertImageRGBtoHSV(const cv::Mat& imageRGB); + //method for changing the bg in case of sudden changes + void changeBg(); + + //current input RGB frame + cv::Mat frame; + std::vector<cv::Mat> frameBGR; + //frame size + cv::Size frameSize; + //frame type + int frameType; + //total number of pixels in frame + unsigned int numPixels; + //current background sample + cv::Mat bgSample; + std::vector<cv::Mat> bgSampleBGR; + //current background image which shows only the highest bin for each pixel + //(just for displaying purposes) + cv::Mat bgImage; + //current foreground mask + cv::Mat fgmask; + cv::Mat fgfiltered; + //number of fps + double fps; + //time stamp in milliseconds (ms) + double timestamp; + //previous time stamp in milliseconds (ms) + double prev_timestamp; + double initial_tick_count; + //initial message to be shown until the first bg model is ready + cv::Mat initialMsgGray; + cv::Mat initialMsgRGB; + + //struct for modeling the background values for a single pixel + typedef struct Bins { + void initialize(unsigned int numSamples) { + binValues = new cv::Vec3b[numSamples]; + binHeights = new uchar[numSamples]; + isFg = new bool[numSamples]; + } + ~Bins() { + if (binValues) { delete[] binValues; } + if (binHeights) { delete[] binHeights; } + if (isFg) { delete[] isFg; } + } + cv::Vec3b* binValues; + uchar* binHeights; + bool* isFg; + } Bins; + Bins* bgBins; + + public: + //struct for modeling the background values for the entire frame + typedef struct BgModel { + void initialize(unsigned int maxBgBins) { + values = new cv::Vec3b[maxBgBins]; + isValid = new bool[maxBgBins]; + isValid[0] = false; + isFg = new bool[maxBgBins]; + counter = new uchar[maxBgBins]; + } + ~BgModel() { + if (values) { delete[] values; } + if (isValid) { delete[] isValid; } + if (isFg) { delete[] isFg; } + if (counter) { delete[] counter; } + } + cv::Vec3b* values; + bool* isValid; + bool* isFg; + uchar* counter; + } BgModel; + private: + BgModel* bgModel; + + //SHADOW SUPPRESSION PARAMETERS + float alpha; + float beta; + uchar tau_s; + uchar tau_h; + + unsigned int minBinHeight; + unsigned int numSamples; + unsigned int samplingPeriod; + unsigned long prev_bg_frame_time; + unsigned int bg_frame_counter; + unsigned int associationThreshold; + unsigned int maxBgBins; + unsigned int nframes; + + double minArea; + bool bg_reset; + unsigned int persistencePeriod; + bool prev_area; + bool sudden_change; + unsigned int fgThreshold; + uchar SHADOW_LABEL; + uchar PERSISTENCE_LABEL; + uchar FOREGROUND_LABEL; + //persistence map + unsigned int* persistenceMap; + cv::Mat persistenceImage; + + bool morphologicalFiltering; + + public: + unsigned int getMaxBgBins() { + return maxBgBins; + } + unsigned int getFgThreshold() { + return fgThreshold; + } + void getBgModel(BgModel bgModel_copy[], unsigned int size); + }; } - Vec3b* binValues; - uchar* binHeights; - bool* isFg; - } Bins; - Bins* bgBins; - -public: - //struct for modeling the background values for the entire frame - typedef struct BgModel { - void initialize(unsigned int maxBgBins) { - values = new Vec3b[maxBgBins]; - isValid = new bool[maxBgBins]; - isValid[0] = false; - isFg = new bool[maxBgBins]; - counter = new uchar[maxBgBins]; - } - ~BgModel() { - if (values) { delete[] values; } - if (isValid) { delete[] isValid; } - if (isFg) { delete[] isFg; } - if (counter) { delete[] counter; } - } - Vec3b* values; - bool* isValid; - bool* isFg; - uchar* counter; - } BgModel; -private: - BgModel* bgModel; - - //SHADOW SUPPRESSION PARAMETERS - float alpha; - float beta; - uchar tau_s; - uchar tau_h; - - unsigned int minBinHeight; - unsigned int numSamples; - unsigned int samplingPeriod; - unsigned long prev_bg_frame_time; - unsigned int bg_frame_counter; - unsigned int associationThreshold; - unsigned int maxBgBins; - unsigned int nframes; - - double minArea; - bool bg_reset; - unsigned int persistencePeriod; - bool prev_area; - bool sudden_change; - unsigned int fgThreshold; - uchar SHADOW_LABEL; - uchar PERSISTENCE_LABEL; - uchar FOREGROUND_LABEL; - //persistence map - unsigned int* persistenceMap; - Mat persistenceImage; - - bool morphologicalFiltering; - -public: - unsigned int getMaxBgBins() { - return maxBgBins; - } - unsigned int getFgThreshold() { - return fgThreshold; } - void getBgModel(BgModel bgModel_copy[], unsigned int size); -}; +} diff --git a/src/algorithms/IndependentMultimodal.cpp b/src/algorithms/IndependentMultimodal.cpp index e5a69701ab0f98c7e6c024426bc03c4917ba05df..10d3ccaba2497d565aeec55facdd710bfd14ae3f 100644 --- a/src/algorithms/IndependentMultimodal.cpp +++ b/src/algorithms/IndependentMultimodal.cpp @@ -9,7 +9,7 @@ IndependentMultimodal::IndependentMultimodal() : { debug_construction(IndependentMultimodal); initLoadSaveConfig(algorithmName); - pIMBS = new BackgroundSubtractorIMBS(fps); + pIMBS = new imbs::BackgroundSubtractorIMBS(fps); } IndependentMultimodal::~IndependentMultimodal() { diff --git a/src/algorithms/IndependentMultimodal.h b/src/algorithms/IndependentMultimodal.h index 2bbdbcfbb81642c563092aee0d6bffcab54e605a..6dc654da5e02dcca98f2121a36090cbbf7bb06a0 100644 --- a/src/algorithms/IndependentMultimodal.h +++ b/src/algorithms/IndependentMultimodal.h @@ -14,7 +14,7 @@ namespace bgslibrary class IndependentMultimodal : public IBGS { private: - BackgroundSubtractorIMBS* pIMBS; + imbs::BackgroundSubtractorIMBS* pIMBS; int fps; public: diff --git a/src/algorithms/KDE.cpp b/src/algorithms/KDE.cpp index e438ee7568dc577a4101de8107e7e2c4533a942f..ac6832a1dcf63dec35f79c8901ca42dbd97c9229 100644 --- a/src/algorithms/KDE.cpp +++ b/src/algorithms/KDE.cpp @@ -12,7 +12,7 @@ KDE::KDE() : { debug_construction(KDE); initLoadSaveConfig(algorithmName); - p = new NPBGSubtractor; + p = new kde::NPBGSubtractor; } KDE::~KDE() { @@ -65,11 +65,11 @@ void KDE::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bg } // Now, we can subtract the background - ((NPBGSubtractor*)p)->NBBGSubtraction(img_input.data, FGImage, FilteredFGImage, DisplayBuffers); + ((kde::NPBGSubtractor*)p)->NBBGSubtraction(img_input.data, FGImage, FilteredFGImage, DisplayBuffers); // At each frame also you can call the update function to adapt the bg // here you pass a mask where pixels with true value will be masked out of the update. - ((NPBGSubtractor*)p)->Update(FGImage); + ((kde::NPBGSubtractor*)p)->Update(FGImage); img_foreground.data = FGImage; } diff --git a/src/algorithms/KDE.h b/src/algorithms/KDE.h index 91e9fa181e325dc7925a2778c6edf9ca595ff70d..d0eee8ab46be6fd7a5acd3536013e8f6da205ca0 100644 --- a/src/algorithms/KDE.h +++ b/src/algorithms/KDE.h @@ -14,7 +14,7 @@ namespace bgslibrary class KDE : public IBGS { private: - NPBGSubtractor *p; + kde::NPBGSubtractor *p; int rows; int cols; int color_channels; diff --git a/src/algorithms/KDE/KernelTable.cpp b/src/algorithms/KDE/KernelTable.cpp index 0ab243b50e9d1ab5848def743fcbccfb390f07d2..0b796556458185b3aa0dfb2f37201bdc7b31ef92 100644 --- a/src/algorithms/KDE/KernelTable.cpp +++ b/src/algorithms/KDE/KernelTable.cpp @@ -2,7 +2,11 @@ #include "KernelTable.h" -#define PI 3.14159 +#ifndef PI +#define PI 3.141592653589793f +#endif + +using namespace bgslibrary::algorithms::kde; KernelLUTable::KernelLUTable() { diff --git a/src/algorithms/KDE/KernelTable.h b/src/algorithms/KDE/KernelTable.h index cf18a3e89e163ee1b47f652cdd440eedc8f02e7d..98d6c67adde1060f74c494b3f2b3b32429835234 100644 --- a/src/algorithms/KDE/KernelTable.h +++ b/src/algorithms/KDE/KernelTable.h @@ -2,19 +2,28 @@ #include <iostream> -class KernelLUTable +namespace bgslibrary { -public: - double minsegma; - double maxsegma; - int segmabins; - int tablehalfwidth; - double *kerneltable; - double *kernelsums; + namespace algorithms + { + namespace kde + { + class KernelLUTable + { + public: + double minsegma; + double maxsegma; + int segmabins; + int tablehalfwidth; + double *kerneltable; + double *kernelsums; -public: - KernelLUTable(); - ~KernelLUTable(); + public: + KernelLUTable(); + ~KernelLUTable(); - KernelLUTable(int KernelHalfWidth, double Segmamin, double Segmamax, int Segmabins); -}; + KernelLUTable(int KernelHalfWidth, double Segmamin, double Segmamax, int Segmabins); + }; + } + } +} diff --git a/src/algorithms/KDE/NPBGSubtractor.cpp b/src/algorithms/KDE/NPBGSubtractor.cpp index bcc4b0908fd841771c98a3255172847b5d59fc77..812fffcb19147a9a79a88d0e55136b582b56828d 100644 --- a/src/algorithms/KDE/NPBGSubtractor.cpp +++ b/src/algorithms/KDE/NPBGSubtractor.cpp @@ -9,1101 +9,1111 @@ //static char THIS_FILE[]=__FILE__; //#define new DEBUG_NEW //#endif +//using namespace bgslibrary::algorithms::kde; -void BGR2SnGnRn(unsigned char * in_image, - unsigned char * out_image, - unsigned int rows, - unsigned int cols) +namespace bgslibrary { - unsigned int i; - unsigned int r2, r3; - unsigned int r, g, b; - double s; - - for (i = 0; i < rows*cols * 3; i += 3) + namespace algorithms { - b = in_image[i]; - g = in_image[i + 1]; - r = in_image[i + 2]; - - // calculate color ratios - s = (double)255 / (double)(b + g + r + 30); + namespace kde + { + void BGR2SnGnRn(unsigned char * in_image, + unsigned char * out_image, + unsigned int rows, + unsigned int cols) + { + unsigned int i; + unsigned int r2, r3; + unsigned int r, g, b; + double s; - r2 = (unsigned int)((g + 10) * s); - r3 = (unsigned int)((r + 10) * s); + for (i = 0; i < rows*cols * 3; i += 3) + { + b = in_image[i]; + g = in_image[i + 1]; + r = in_image[i + 2]; - out_image[i] = (unsigned char)(((unsigned int)b + g + r) / 3); - out_image[i + 1] = (unsigned char)(r2 > 255 ? 255 : r2); - out_image[i + 2] = (unsigned char)(r3 > 255 ? 255 : r3); - } -} + // calculate color ratios + s = (double)255 / (double)(b + g + r + 30); -void UpdateDiffHist(unsigned char * image1, unsigned char * image2, DynamicMedianHistogram * pHist) -{ - unsigned int j; - int bin, diff; + r2 = (unsigned int)((g + 10) * s); + r3 = (unsigned int)((r + 10) * s); - unsigned int imagesize = pHist->imagesize; - unsigned char histbins = pHist->histbins; - unsigned char *pAbsDiffHist = pHist->Hist; - - int histbins_1 = histbins - 1; + out_image[i] = (unsigned char)(((unsigned int)b + g + r) / 3); + out_image[i + 1] = (unsigned char)(r2 > 255 ? 255 : r2); + out_image[i + 2] = (unsigned char)(r3 > 255 ? 255 : r3); + } + } - for (j = 0; j < imagesize; j++) - { - diff = (int)image1[j] - (int)image2[j]; - diff = abs(diff); - // update histogram - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[j*histbins + bin]++; - } -} + void UpdateDiffHist(unsigned char * image1, unsigned char * image2, DynamicMedianHistogram * pHist) + { + unsigned int j; + int bin, diff; -void FindHistMedians(DynamicMedianHistogram * pAbsDiffHist) -{ - unsigned char * Hist = pAbsDiffHist->Hist; - unsigned char * MedianBins = pAbsDiffHist->MedianBins; - unsigned char * AccSum = pAbsDiffHist->AccSum; - unsigned char histsum = pAbsDiffHist->histsum; - unsigned char histbins = pAbsDiffHist->histbins; - unsigned int imagesize = pAbsDiffHist->imagesize; - - int sum; - int bin; - unsigned int histindex; - unsigned char medianCount = histsum / 2; - unsigned int j; - - // find medians - for (j = 0; j < imagesize; j++) - { - // find the median - bin = 0; - sum = 0; + unsigned int imagesize = pHist->imagesize; + unsigned char histbins = pHist->histbins; + unsigned char *pAbsDiffHist = pHist->Hist; - histindex = j*histbins; + int histbins_1 = histbins - 1; - while (sum < medianCount) - { - sum += Hist[histindex + bin]; - bin++; - } + for (j = 0; j < imagesize; j++) + { + diff = (int)image1[j] - (int)image2[j]; + diff = abs(diff); + // update histogram + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[j*histbins + bin]++; + } + } - bin--; + void FindHistMedians(DynamicMedianHistogram * pAbsDiffHist) + { + unsigned char * Hist = pAbsDiffHist->Hist; + unsigned char * MedianBins = pAbsDiffHist->MedianBins; + unsigned char * AccSum = pAbsDiffHist->AccSum; + unsigned char histsum = pAbsDiffHist->histsum; + unsigned char histbins = pAbsDiffHist->histbins; + unsigned int imagesize = pAbsDiffHist->imagesize; + + int sum; + int bin; + unsigned int histindex; + unsigned char medianCount = histsum / 2; + unsigned int j; + + // find medians + for (j = 0; j < imagesize; j++) + { + // find the median + bin = 0; + sum = 0; - MedianBins[j] = bin; - AccSum[j] = sum; - } -} + histindex = j*histbins; -DynamicMedianHistogram BuildAbsDiffHist(unsigned char * pSequence, - unsigned int rows, - unsigned int cols, - unsigned int color_channels, - unsigned int SequenceLength, - unsigned int histbins) -{ + while (sum < medianCount) + { + sum += Hist[histindex + bin]; + bin++; + } - unsigned int imagesize = rows*cols*color_channels; - unsigned int i; + bin--; - DynamicMedianHistogram Hist; + MedianBins[j] = bin; + AccSum[j] = sum; + } + } - unsigned char *pAbsDiffHist = new unsigned char[rows*cols*color_channels*histbins]; - unsigned char *pMedianBins = new unsigned char[rows*cols*color_channels]; - unsigned char *pMedianFreq = new unsigned char[rows*cols*color_channels]; - unsigned char *pAccSum = new unsigned char[rows*cols*color_channels]; + DynamicMedianHistogram BuildAbsDiffHist(unsigned char * pSequence, + unsigned int rows, + unsigned int cols, + unsigned int color_channels, + unsigned int SequenceLength, + unsigned int histbins) + { - memset(pAbsDiffHist, 0, rows*cols*color_channels*histbins); + unsigned int imagesize = rows*cols*color_channels; + unsigned int i; - Hist.Hist = pAbsDiffHist; - Hist.MedianBins = pMedianBins; - Hist.MedianFreq = pMedianFreq; - Hist.AccSum = pAccSum; - Hist.histbins = histbins; - Hist.imagesize = rows*cols*color_channels; - Hist.histsum = SequenceLength - 1; + DynamicMedianHistogram Hist; - unsigned char *image1, *image2; - for (i = 1; i < SequenceLength; i++) - { - // find diff between frame i,i-1; - image1 = pSequence + (i - 1)*imagesize; - image2 = pSequence + (i)*imagesize; + unsigned char *pAbsDiffHist = new unsigned char[rows*cols*color_channels*histbins]; + unsigned char *pMedianBins = new unsigned char[rows*cols*color_channels]; + unsigned char *pMedianFreq = new unsigned char[rows*cols*color_channels]; + unsigned char *pAccSum = new unsigned char[rows*cols*color_channels]; - UpdateDiffHist(image1, image2, &Hist); - } + memset(pAbsDiffHist, 0, rows*cols*color_channels*histbins); - FindHistMedians(&Hist); + Hist.Hist = pAbsDiffHist; + Hist.MedianBins = pMedianBins; + Hist.MedianFreq = pMedianFreq; + Hist.AccSum = pAccSum; + Hist.histbins = histbins; + Hist.imagesize = rows*cols*color_channels; + Hist.histsum = SequenceLength - 1; - return Hist; -} + unsigned char *image1, *image2; + for (i = 1; i < SequenceLength; i++) + { + // find diff between frame i,i-1; + image1 = pSequence + (i - 1)*imagesize; + image2 = pSequence + (i)*imagesize; -void EstimateSDsFromAbsDiffHist(DynamicMedianHistogram * pAbsDiffHist, - unsigned char * pSDs, - unsigned int imagesize, - double MinSD, - double MaxSD, - unsigned int kernelbins) -{ - double v; - double kernelbinfactor = (kernelbins - 1) / (MaxSD - MinSD); - int medianCount; - int sum; - int bin; - unsigned int histindex; - unsigned int j; - unsigned int x1, x2; - - unsigned char *Hist = pAbsDiffHist->Hist; - unsigned char *MedianBins = pAbsDiffHist->MedianBins; - unsigned char *AccSum = pAbsDiffHist->AccSum; - unsigned char histsum = pAbsDiffHist->histsum; - unsigned char histbins = pAbsDiffHist->histbins; - - medianCount = (histsum) / 2; - - for (j = 0; j < imagesize; j++) - { - histindex = j*histbins; + UpdateDiffHist(image1, image2, &Hist); + } - bin = MedianBins[j]; - sum = AccSum[j]; + FindHistMedians(&Hist); - x1 = sum - Hist[histindex + bin]; - x2 = sum; + return Hist; + } - // interpolate to get the median - // x1 < 50 % < x2 + void EstimateSDsFromAbsDiffHist(DynamicMedianHistogram * pAbsDiffHist, + unsigned char * pSDs, + unsigned int imagesize, + double MinSD, + double MaxSD, + unsigned int kernelbins) + { + double v; + double kernelbinfactor = (kernelbins - 1) / (MaxSD - MinSD); + int medianCount; + int sum; + int bin; + unsigned int histindex; + unsigned int j; + unsigned int x1, x2; + + unsigned char *Hist = pAbsDiffHist->Hist; + unsigned char *MedianBins = pAbsDiffHist->MedianBins; + unsigned char *AccSum = pAbsDiffHist->AccSum; + unsigned char histsum = pAbsDiffHist->histsum; + unsigned char histbins = pAbsDiffHist->histbins; + + medianCount = (histsum) / 2; + + for (j = 0; j < imagesize; j++) + { + histindex = j*histbins; - v = 1.04 * ((double)bin - (double)(x2 - medianCount) / (double)(x2 - x1)); - v = (v <= MinSD ? MinSD : v); + bin = MedianBins[j]; + sum = AccSum[j]; - // convert sd to kernel table bin + x1 = sum - Hist[histindex + bin]; + x2 = sum; - bin = (int)(v >= MaxSD ? kernelbins - 1 : floor((v - MinSD)*kernelbinfactor + .5)); + // interpolate to get the median + // x1 < 50 % < x2 - assert(bin >= 0 && bin < kernelbins); + v = 1.04 * ((double)bin - (double)(x2 - medianCount) / (double)(x2 - x1)); + v = (v <= MinSD ? MinSD : v); - pSDs[j] = bin; - } -} + // convert sd to kernel table bin -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// + bin = (int)(v >= MaxSD ? kernelbins - 1 : floor((v - MinSD)*kernelbinfactor + .5)); -NPBGSubtractor::NPBGSubtractor() {} + assert(bin >= 0 && bin < kernelbins); -NPBGSubtractor::~NPBGSubtractor() -{ - delete AbsDiffHist.Hist; - delete AbsDiffHist.MedianBins; - delete AbsDiffHist.MedianFreq; - delete AbsDiffHist.AccSum; - delete KernelTable; - delete BGModel->SDbinsImage; - delete BGModel; - delete Pimage1; - delete Pimage2; - delete tempFrame; - delete imageindex->List; - delete imageindex; -} + pSDs[j] = bin; + } + } -int NPBGSubtractor::Intialize(unsigned int prows, - unsigned int pcols, - unsigned int pcolor_channels, - unsigned int SequenceLength, - unsigned int pTimeWindowSize, - unsigned char pSDEstimationFlag, - unsigned char pUseColorRatiosFlag) -{ + ////////////////////////////////////////////////////////////////////// + // Construction/Destruction + ////////////////////////////////////////////////////////////////////// - rows = prows; - cols = pcols; - color_channels = pcolor_channels; - imagesize = rows*cols*color_channels; - SdEstimateFlag = pSDEstimationFlag; - UseColorRatiosFlag = pUseColorRatiosFlag; - //SampleSize = SequenceLength; + NPBGSubtractor::NPBGSubtractor() {} - AdaptBGFlag = FALSE; - // - SubsetFlag = TRUE; + NPBGSubtractor::~NPBGSubtractor() + { + delete AbsDiffHist.Hist; + delete AbsDiffHist.MedianBins; + delete AbsDiffHist.MedianFreq; + delete AbsDiffHist.AccSum; + delete KernelTable; + delete BGModel->SDbinsImage; + delete BGModel; + delete Pimage1; + delete Pimage2; + delete tempFrame; + delete imageindex->List; + delete imageindex; + } - UpdateSDRate = 0; + int NPBGSubtractor::Intialize(unsigned int prows, + unsigned int pcols, + unsigned int pcolor_channels, + unsigned int SequenceLength, + unsigned int pTimeWindowSize, + unsigned char pSDEstimationFlag, + unsigned char pUseColorRatiosFlag) + { - BGModel = new NPBGmodel(rows, cols, color_channels, SequenceLength, pTimeWindowSize, 500); + rows = prows; + cols = pcols; + color_channels = pcolor_channels; + imagesize = rows*cols*color_channels; + SdEstimateFlag = pSDEstimationFlag; + UseColorRatiosFlag = pUseColorRatiosFlag; + //SampleSize = SequenceLength; - Pimage1 = new double[rows*cols]; - Pimage2 = new double[rows*cols]; + AdaptBGFlag = FALSE; + // + SubsetFlag = TRUE; - tempFrame = new unsigned char[rows*cols * 3]; + UpdateSDRate = 0; - imageindex = new ImageIndex; - imageindex->List = new unsigned int[rows*cols]; + BGModel = new NPBGmodel(rows, cols, color_channels, SequenceLength, pTimeWindowSize, 500); - // error checking - if (BGModel == NULL) - return 0; + Pimage1 = new double[rows*cols]; + Pimage2 = new double[rows*cols]; - return 1; -} + tempFrame = new unsigned char[rows*cols * 3]; -void NPBGSubtractor::AddFrame(unsigned char *ImageBuffer) -{ - if (UseColorRatiosFlag && color_channels == 3) - BGR2SnGnRn(ImageBuffer, ImageBuffer, rows, cols); + imageindex = new ImageIndex; + imageindex->List = new unsigned int[rows*cols]; - BGModel->AddFrame(ImageBuffer); -} + // error checking + if (BGModel == NULL) + return 0; -void NPBGSubtractor::Estimation() -{ - int SampleSize = BGModel->SampleSize; + return 1; + } - memset(BGModel->TemporalMask, 0, rows*cols*BGModel->TemporalBufferLength); + void NPBGSubtractor::AddFrame(unsigned char *ImageBuffer) + { + if (UseColorRatiosFlag && color_channels == 3) + BGR2SnGnRn(ImageBuffer, ImageBuffer, rows, cols); - //BGModel->AccMask= new unsigned int [rows*cols]; - memset(BGModel->AccMask, 0, rows*cols * sizeof(unsigned int)); + BGModel->AddFrame(ImageBuffer); + } - unsigned char *pSDs = new unsigned char[rows*cols*color_channels]; + void NPBGSubtractor::Estimation() + { + int SampleSize = BGModel->SampleSize; - //DynamicMedianHistogram AbsDiffHist; + memset(BGModel->TemporalMask, 0, rows*cols*BGModel->TemporalBufferLength); - int Abshistbins = 20; + //BGModel->AccMask= new unsigned int [rows*cols]; + memset(BGModel->AccMask, 0, rows*cols * sizeof(unsigned int)); - TimeIndex = 0; + unsigned char *pSDs = new unsigned char[rows*cols*color_channels]; - // estimate standard deviations + //DynamicMedianHistogram AbsDiffHist; - if (SdEstimateFlag) - { - AbsDiffHist = BuildAbsDiffHist(BGModel->Sequence, rows, cols, color_channels, SampleSize, Abshistbins); - EstimateSDsFromAbsDiffHist(&AbsDiffHist, pSDs, imagesize, SEGMAMIN, SEGMAMAX, SEGMABINS); - } - else - { - unsigned int bin; - bin = (unsigned int)floor(((DEFAULTSEGMA - SEGMAMIN)*SEGMABINS) / (SEGMAMAX - SEGMAMIN)); - memset(pSDs, bin, rows*cols*color_channels * sizeof(unsigned char)); - } + int Abshistbins = 20; - BGModel->SDbinsImage = pSDs; + TimeIndex = 0; - // Generate the Kernel - KernelTable = new KernelLUTable(KERNELHALFWIDTH, SEGMAMIN, SEGMAMAX, SEGMABINS); -} + // estimate standard deviations -/*********************************************************************/ + if (SdEstimateFlag) + { + AbsDiffHist = BuildAbsDiffHist(BGModel->Sequence, rows, cols, color_channels, SampleSize, Abshistbins); + EstimateSDsFromAbsDiffHist(&AbsDiffHist, pSDs, imagesize, SEGMAMIN, SEGMAMAX, SEGMABINS); + } + else + { + unsigned int bin; + bin = (unsigned int)floor(((DEFAULTSEGMA - SEGMAMIN)*SEGMABINS) / (SEGMAMAX - SEGMAMIN)); + memset(pSDs, bin, rows*cols*color_channels * sizeof(unsigned char)); + } -void BuildImageIndex(unsigned char * Image, - ImageIndex * imageIndex, - unsigned int rows, - unsigned int cols) -{ - unsigned int i, j; - unsigned int r, c; - unsigned int * image_list; + BGModel->SDbinsImage = pSDs; - j = cols + 1; - i = 0; - image_list = imageIndex->List; + // Generate the Kernel + KernelTable = new KernelLUTable(KERNELHALFWIDTH, SEGMAMIN, SEGMAMAX, SEGMABINS); + } - for (r = 1; r < rows - 1; r++) - { - for (c = 1; c < cols - 1; c++) - { - if (Image[j]) - image_list[i++] = j; + /*********************************************************************/ - j++; - } - j += 2; - } + void BuildImageIndex(unsigned char * Image, + ImageIndex * imageIndex, + unsigned int rows, + unsigned int cols) + { + unsigned int i, j; + unsigned int r, c; + unsigned int * image_list; - imageIndex->cnt = i; -} + j = cols + 1; + i = 0; + image_list = imageIndex->List; -/*********************************************************************/ + for (r = 1; r < rows - 1; r++) + { + for (c = 1; c < cols - 1; c++) + { + if (Image[j]) + image_list[i++] = j; -void HystExpandOperatorIndexed(unsigned char * inImage, - ImageIndex * inIndex, - double * Pimage, - double hyst_th, - unsigned char * outImage, - ImageIndex * outIndex, - unsigned int urows, - unsigned int ucols) -{ - unsigned int * in_list; - unsigned int in_cnt; - unsigned int * out_list; + j++; + } + j += 2; + } - int rows, cols; + imageIndex->cnt = i; + } - int Nbr[9]; - unsigned int i, j; - unsigned int k; - unsigned int idx; + /*********************************************************************/ - rows = (int)urows; - cols = (int)ucols; + void HystExpandOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + double * Pimage, + double hyst_th, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) + { + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; - in_cnt = inIndex->cnt; - in_list = inIndex->List; + int rows, cols; - Nbr[0] = -cols - 1; - Nbr[1] = -cols; - Nbr[2] = -cols + 1; - Nbr[3] = -1; - Nbr[4] = 0; - Nbr[5] = 1; - Nbr[6] = cols - 1; - Nbr[7] = cols; - Nbr[8] = cols + 1; + int Nbr[9]; + unsigned int i, j; + unsigned int k; + unsigned int idx; - memset(outImage, 0, rows*cols); + rows = (int)urows; + cols = (int)ucols; - out_list = outIndex->List; - k = 0; + in_cnt = inIndex->cnt; + in_list = inIndex->List; - for (i = 0; i < in_cnt; i++) - { - for (j = 0; j < 9; j++) - { - idx = in_list[i] + Nbr[j]; + Nbr[0] = -cols - 1; + Nbr[1] = -cols; + Nbr[2] = -cols + 1; + Nbr[3] = -1; + Nbr[4] = 0; + Nbr[5] = 1; + Nbr[6] = cols - 1; + Nbr[7] = cols; + Nbr[8] = cols + 1; - if (Pimage[idx] < hyst_th) - outImage[idx] = 255; - } - } + memset(outImage, 0, rows*cols); - // build index for out image - BuildImageIndex(outImage, outIndex, urows, ucols); -} + out_list = outIndex->List; + k = 0; -/*********************************************************************/ + for (i = 0; i < in_cnt; i++) + { + for (j = 0; j < 9; j++) + { + idx = in_list[i] + Nbr[j]; -void HystShrinkOperatorIndexed(unsigned char * inImage, - ImageIndex * inIndex, - double * Pimage, - double hyst_th, - unsigned char * outImage, - ImageIndex * outIndex, - unsigned int urows, - unsigned int ucols) -{ - unsigned int * in_list; - unsigned int in_cnt; - unsigned int * out_list; + if (Pimage[idx] < hyst_th) + outImage[idx] = 255; + } + } - int rows, cols; + // build index for out image + BuildImageIndex(outImage, outIndex, urows, ucols); + } - int Nbr[9]; - unsigned int i, j; - unsigned int k, idx; + /*********************************************************************/ - rows = (int)urows; - cols = (int)ucols; + void HystShrinkOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + double * Pimage, + double hyst_th, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) + { + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; - in_cnt = inIndex->cnt; - in_list = inIndex->List; + int rows, cols; - Nbr[0] = -cols - 1; - Nbr[1] = -cols; - Nbr[2] = -cols + 1; - Nbr[3] = -1; - Nbr[4] = 0; - Nbr[5] = 1; - Nbr[6] = cols - 1; - Nbr[7] = cols; - Nbr[8] = cols + 1; + int Nbr[9]; + unsigned int i, j; + unsigned int k, idx; - memset(outImage, 0, rows*cols); + rows = (int)urows; + cols = (int)ucols; - out_list = outIndex->List; - k = 0; + in_cnt = inIndex->cnt; + in_list = inIndex->List; - for (i = 0; i < in_cnt; i++) - { - idx = in_list[i]; - j = 0; + Nbr[0] = -cols - 1; + Nbr[1] = -cols; + Nbr[2] = -cols + 1; + Nbr[3] = -1; + Nbr[4] = 0; + Nbr[5] = 1; + Nbr[6] = cols - 1; + Nbr[7] = cols; + Nbr[8] = cols + 1; - while (j < 9 && inImage[idx + Nbr[j]]) - j++; + memset(outImage, 0, rows*cols); - if (j >= 9 || Pimage[idx] <= hyst_th) - outImage[idx] = 255; - } + out_list = outIndex->List; + k = 0; - BuildImageIndex(outImage, outIndex, rows, cols); -} + for (i = 0; i < in_cnt; i++) + { + idx = in_list[i]; + j = 0; -/*********************************************************************/ + while (j < 9 && inImage[idx + Nbr[j]]) + j++; -void ExpandOperatorIndexed(unsigned char * inImage, - ImageIndex * inIndex, - unsigned char * outImage, - ImageIndex * outIndex, - unsigned int urows, - unsigned int ucols) -{ - unsigned int * in_list; - unsigned int in_cnt; - unsigned int * out_list; + if (j >= 9 || Pimage[idx] <= hyst_th) + outImage[idx] = 255; + } - int rows, cols; + BuildImageIndex(outImage, outIndex, rows, cols); + } - int Nbr[9]; - unsigned int i, j; - unsigned int k; - unsigned int idx; + /*********************************************************************/ - rows = (int)urows; - cols = (int)ucols; + void ExpandOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) + { + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; - in_cnt = inIndex->cnt; - in_list = inIndex->List; + int rows, cols; - Nbr[0] = -cols - 1; - Nbr[1] = -cols; - Nbr[2] = -cols + 1; - Nbr[3] = -1; - Nbr[4] = 0; - Nbr[5] = 1; - Nbr[6] = cols - 1; - Nbr[7] = cols; - Nbr[8] = cols + 1; + int Nbr[9]; + unsigned int i, j; + unsigned int k; + unsigned int idx; + rows = (int)urows; + cols = (int)ucols; - memset(outImage, 0, rows*cols); + in_cnt = inIndex->cnt; + in_list = inIndex->List; + Nbr[0] = -cols - 1; + Nbr[1] = -cols; + Nbr[2] = -cols + 1; + Nbr[3] = -1; + Nbr[4] = 0; + Nbr[5] = 1; + Nbr[6] = cols - 1; + Nbr[7] = cols; + Nbr[8] = cols + 1; - out_list = outIndex->List; - k = 0; - for (i = 0; i < in_cnt; i++) - for (j = 0; j < 9; j++) { - idx = in_list[i] + Nbr[j]; - outImage[idx] = 255; - } + memset(outImage, 0, rows*cols); - // build index for out image - BuildImageIndex(outImage, outIndex, rows, cols); + out_list = outIndex->List; + k = 0; + for (i = 0; i < in_cnt; i++) + for (j = 0; j < 9; j++) { + idx = in_list[i] + Nbr[j]; + outImage[idx] = 255; + } -} -/*********************************************************************/ + // build index for out image -void ShrinkOperatorIndexed(unsigned char * inImage, - ImageIndex * inIndex, - unsigned char * outImage, - ImageIndex * outIndex, - unsigned int urows, - unsigned int ucols) -{ + BuildImageIndex(outImage, outIndex, rows, cols); - unsigned int * in_list; - unsigned int in_cnt; - unsigned int * out_list; + } - int rows, cols; + /*********************************************************************/ - int Nbr[9]; - unsigned int i, j; - unsigned int k, idx; + void ShrinkOperatorIndexed(unsigned char * inImage, + ImageIndex * inIndex, + unsigned char * outImage, + ImageIndex * outIndex, + unsigned int urows, + unsigned int ucols) + { - rows = (int)urows; - cols = (int)ucols; + unsigned int * in_list; + unsigned int in_cnt; + unsigned int * out_list; - in_cnt = inIndex->cnt; - in_list = inIndex->List; + int rows, cols; - Nbr[0] = -cols - 1; - Nbr[1] = -cols; - Nbr[2] = -cols + 1; - Nbr[3] = -1; - Nbr[4] = 0; - Nbr[5] = 1; - Nbr[6] = cols - 1; - Nbr[7] = cols; - Nbr[8] = cols + 1; + int Nbr[9]; + unsigned int i, j; + unsigned int k, idx; + rows = (int)urows; + cols = (int)ucols; - memset(outImage, 0, rows*cols); + in_cnt = inIndex->cnt; + in_list = inIndex->List; - out_list = outIndex->List; - k = 0; - for (i = 0; i < in_cnt; i++) { - idx = in_list[i]; - j = 0; + Nbr[0] = -cols - 1; + Nbr[1] = -cols; + Nbr[2] = -cols + 1; + Nbr[3] = -1; + Nbr[4] = 0; + Nbr[5] = 1; + Nbr[6] = cols - 1; + Nbr[7] = cols; + Nbr[8] = cols + 1; - while (j < 9 && inImage[idx + Nbr[j]]) { - j++; - } - if (j >= 9) { - outImage[idx] = 255; - } - } + memset(outImage, 0, rows*cols); - BuildImageIndex(outImage, outIndex, rows, cols); -} + out_list = outIndex->List; + k = 0; + for (i = 0; i < in_cnt; i++) { + idx = in_list[i]; + j = 0; -/*********************************************************************/ + while (j < 9 && inImage[idx + Nbr[j]]) { + j++; + } -void NoiseFilter_o(unsigned char * Image, - unsigned char * ResultIm, - int rows, - int cols, - unsigned char th) -{ - /* assuming input is 1 for on, 0 for off */ + if (j >= 9) { + outImage[idx] = 255; + } + } + BuildImageIndex(outImage, outIndex, rows, cols); + } - int r, c; - unsigned char *p, *n, *nw, *ne, *e, *w, *s, *sw, *se; - unsigned int v; - unsigned int TH; + /*********************************************************************/ - unsigned char * ResultPtr; + void NoiseFilter_o(unsigned char * Image, + unsigned char * ResultIm, + int rows, + int cols, + unsigned char th) + { + /* assuming input is 1 for on, 0 for off */ - TH = 255 * th; - memset(ResultIm, 0, rows*cols); + int r, c; + unsigned char *p, *n, *nw, *ne, *e, *w, *s, *sw, *se; + unsigned int v; + unsigned int TH; - p = Image + cols + 1; - ResultPtr = ResultIm + cols + 1; + unsigned char * ResultPtr; - for (r = 1; r < rows - 1; r++) - { - for (c = 1; c < cols - 1; c++) - { - if (*p) - { - n = p - cols; - ne = n + 1; - nw = n - 1; - e = p + 1; - w = p - 1; - s = p + cols; - se = s + 1; - sw = s - 1; - - v = (unsigned int)*nw + *n + *ne + *w + *e + *sw + *s + *se; - - if (v >= TH) - *ResultPtr = 255; - else - *ResultPtr = 0; - } - p++; - ResultPtr++; - } - p += 2; - ResultPtr += 2; - } -} + TH = 255 * th; -/*********************************************************************/ + memset(ResultIm, 0, rows*cols); -void NPBGSubtractor::SequenceBGUpdate_Pairs(unsigned char * image, - unsigned char * Mask) -{ - unsigned int i, ic; - unsigned char * pSequence = BGModel->Sequence; - unsigned char * PixelQTop = BGModel->PixelQTop; - //unsigned int Top = BGModel->Top; - unsigned int rate; + p = Image + cols + 1; + ResultPtr = ResultIm + cols + 1; - int TemporalBufferTop = (int)BGModel->TemporalBufferTop; - unsigned char * pTemporalBuffer = BGModel->TemporalBuffer; - unsigned char * pTemporalMask = BGModel->TemporalMask; - int TemporalBufferLength = BGModel->TemporalBufferLength; + for (r = 1; r < rows - 1; r++) + { + for (c = 1; c < cols - 1; c++) + { + if (*p) + { + n = p - cols; + ne = n + 1; + nw = n - 1; + e = p + 1; + w = p - 1; + s = p + cols; + se = s + 1; + sw = s - 1; + + v = (unsigned int)*nw + *n + *ne + *w + *e + *sw + *s + *se; + + if (v >= TH) + *ResultPtr = 255; + else + *ResultPtr = 0; + } + p++; + ResultPtr++; + } + p += 2; + ResultPtr += 2; + } + } - unsigned int * AccMask = BGModel->AccMask; - unsigned int ResetMaskTh = BGModel->ResetMaskTh; + /*********************************************************************/ - unsigned char *pAbsDiffHist = AbsDiffHist.Hist; - unsigned char histbins = AbsDiffHist.histbins; - int histbins_1 = histbins - 1; + void NPBGSubtractor::SequenceBGUpdate_Pairs(unsigned char * image, + unsigned char * Mask) + { + unsigned int i, ic; + unsigned char * pSequence = BGModel->Sequence; + unsigned char * PixelQTop = BGModel->PixelQTop; + //unsigned int Top = BGModel->Top; + unsigned int rate; - int TimeWindowSize = BGModel->TimeWindowSize; - int SampleSize = BGModel->SampleSize; + int TemporalBufferTop = (int)BGModel->TemporalBufferTop; + unsigned char * pTemporalBuffer = BGModel->TemporalBuffer; + unsigned char * pTemporalMask = BGModel->TemporalMask; + int TemporalBufferLength = BGModel->TemporalBufferLength; - int TemporalBufferNext; + unsigned int * AccMask = BGModel->AccMask; + unsigned int ResetMaskTh = BGModel->ResetMaskTh; - unsigned int imagebuffersize = rows*cols*color_channels; - unsigned int imagespatialsize = rows*cols; + unsigned char *pAbsDiffHist = AbsDiffHist.Hist; + unsigned char histbins = AbsDiffHist.histbins; + int histbins_1 = histbins - 1; - unsigned char mask; + int TimeWindowSize = BGModel->TimeWindowSize; + int SampleSize = BGModel->SampleSize; - unsigned int histindex; - unsigned char diff; - unsigned char bin; + int TemporalBufferNext; - static int TBCount = 0; + unsigned int imagebuffersize = rows*cols*color_channels; + unsigned int imagespatialsize = rows*cols; - unsigned char * pTBbase1, *pTBbase2; - unsigned char * pModelbase1, *pModelbase2; + unsigned char mask; - rate = TimeWindowSize / SampleSize; - rate = (rate > 2) ? rate : 2; + unsigned int histindex; + unsigned char diff; + unsigned char bin; + static int TBCount = 0; - TemporalBufferNext = (TemporalBufferTop + 1) - % TemporalBufferLength; + unsigned char * pTBbase1, *pTBbase2; + unsigned char * pModelbase1, *pModelbase2; - // pointers to Masks : Top and Next - unsigned char * pTMaskTop = pTemporalMask + TemporalBufferTop*imagespatialsize; - unsigned char * pTMaskNext = pTemporalMask + TemporalBufferNext*imagespatialsize; + rate = TimeWindowSize / SampleSize; + rate = (rate > 2) ? rate : 2; - // pointers to TB frames: Top and Next - unsigned char * pTBTop = pTemporalBuffer + TemporalBufferTop*imagebuffersize; - unsigned char * pTBNext = pTemporalBuffer + TemporalBufferNext*imagebuffersize; - if (((TimeIndex) % rate == 0) && TBCount >= TemporalBufferLength) - { - for (i = 0, ic = 0; i < imagespatialsize; i++, ic += color_channels) - { - mask = *(pTMaskTop + i) || *(pTMaskNext + i); + TemporalBufferNext = (TemporalBufferTop + 1) + % TemporalBufferLength; - if (!mask) - { - // pointer to TB pixels to be added to the model - pTBbase1 = pTBTop + ic; - pTBbase2 = pTBNext + ic; + // pointers to Masks : Top and Next + unsigned char * pTMaskTop = pTemporalMask + TemporalBufferTop*imagespatialsize; + unsigned char * pTMaskNext = pTemporalMask + TemporalBufferNext*imagespatialsize; - // pointers to Model pixels to be replaced - pModelbase1 = pSequence + PixelQTop[i] * imagebuffersize + ic; - pModelbase2 = pSequence + ((PixelQTop[i] + 1) % SampleSize)*imagebuffersize + ic; + // pointers to TB frames: Top and Next + unsigned char * pTBTop = pTemporalBuffer + TemporalBufferTop*imagebuffersize; + unsigned char * pTBNext = pTemporalBuffer + TemporalBufferNext*imagebuffersize; - // update Deviation Histogram - if (SdEstimateFlag) + if (((TimeIndex) % rate == 0) && TBCount >= TemporalBufferLength) { - if (color_channels == 1) + for (i = 0, ic = 0; i < imagespatialsize; i++, ic += color_channels) { - histindex = i*histbins; - - // add new pair from temporal buffer - diff = (unsigned char)abs((int)*pTBbase1 - (int)*pTBbase2); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]++; - - - // remove old pair from the model - diff = (unsigned char)abs((int)*pModelbase1 - (int)*pModelbase2); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]--; + mask = *(pTMaskTop + i) || *(pTMaskNext + i); + + if (!mask) + { + // pointer to TB pixels to be added to the model + pTBbase1 = pTBTop + ic; + pTBbase2 = pTBNext + ic; + + // pointers to Model pixels to be replaced + pModelbase1 = pSequence + PixelQTop[i] * imagebuffersize + ic; + pModelbase2 = pSequence + ((PixelQTop[i] + 1) % SampleSize)*imagebuffersize + ic; + + // update Deviation Histogram + if (SdEstimateFlag) + { + if (color_channels == 1) + { + histindex = i*histbins; + + // add new pair from temporal buffer + diff = (unsigned char)abs((int)*pTBbase1 - (int)*pTBbase2); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]++; + + + // remove old pair from the model + diff = (unsigned char)abs((int)*pModelbase1 - (int)*pModelbase2); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]--; + } + else + { + // color + + // add new pair from temporal buffer + histindex = ic*histbins; + diff = abs(*pTBbase1 - + *pTBbase2); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]++; + + histindex += histbins; + diff = abs(*(pTBbase1 + 1) - + *(pTBbase2 + 1)); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]++; + + histindex += histbins; + diff = abs(*(pTBbase1 + 2) - + *(pTBbase2 + 2)); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]++; + + // remove old pair from the model + histindex = ic*histbins; + + diff = abs(*pModelbase1 - + *pModelbase2); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]--; + + histindex += histbins; + diff = abs(*(pModelbase1 + 1) - + *(pModelbase2 + 1)); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]--; + + histindex += histbins; + diff = abs(*(pModelbase1 + 2) - + *(pModelbase2 + 2)); + bin = (diff < histbins ? diff : histbins_1); + pAbsDiffHist[histindex + bin]--; + } + } + + // add new pair into the model + memcpy(pModelbase1, pTBbase1, color_channels * sizeof(unsigned char)); + + memcpy(pModelbase2, pTBbase2, color_channels * sizeof(unsigned char)); + + PixelQTop[i] = (PixelQTop[i] + 2) % SampleSize; + } } - else - { - // color - - // add new pair from temporal buffer - histindex = ic*histbins; - diff = abs(*pTBbase1 - - *pTBbase2); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]++; - - histindex += histbins; - diff = abs(*(pTBbase1 + 1) - - *(pTBbase2 + 1)); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]++; - - histindex += histbins; - diff = abs(*(pTBbase1 + 2) - - *(pTBbase2 + 2)); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]++; - - // remove old pair from the model - histindex = ic*histbins; - - diff = abs(*pModelbase1 - - *pModelbase2); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]--; - - histindex += histbins; - diff = abs(*(pModelbase1 + 1) - - *(pModelbase2 + 1)); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]--; - - histindex += histbins; - diff = abs(*(pModelbase1 + 2) - - *(pModelbase2 + 2)); - bin = (diff < histbins ? diff : histbins_1); - pAbsDiffHist[histindex + bin]--; - } - } + } // end if (sampling event) - // add new pair into the model - memcpy(pModelbase1, pTBbase1, color_channels * sizeof(unsigned char)); + // update temporal buffer + // add new frame to Temporal buffer. + memcpy(pTBTop, image, imagebuffersize); - memcpy(pModelbase2, pTBbase2, color_channels * sizeof(unsigned char)); + // update AccMask + // update new Mask with information in AccMask - PixelQTop[i] = (PixelQTop[i] + 2) % SampleSize; - } - } - } // end if (sampling event) - - // update temporal buffer - // add new frame to Temporal buffer. - memcpy(pTBTop, image, imagebuffersize); - - // update AccMask - // update new Mask with information in AccMask - - for (i = 0; i < rows*cols; i++) - { - if (Mask[i]) - AccMask[i]++; - else - AccMask[i] = 0; - - if (AccMask[i] > ResetMaskTh) - Mask[i] = 0; - } - - // add new mask - memcpy(pTMaskTop, Mask, imagespatialsize); - - // advance Temporal buffer pointer - TemporalBufferTop = (TemporalBufferTop + 1) % TemporalBufferLength; - - BGModel->TemporalBufferTop = TemporalBufferTop; - - TBCount++; + for (i = 0; i < rows*cols; i++) + { + if (Mask[i]) + AccMask[i]++; + else + AccMask[i] = 0; - // estimate SDs + if (AccMask[i] > ResetMaskTh) + Mask[i] = 0; + } - if (SdEstimateFlag && UpdateSDRate && ((TimeIndex) % UpdateSDRate == 0)) - { - double MaxSD = KernelTable->maxsegma; - double MinSD = KernelTable->minsegma; - int KernelBins = KernelTable->segmabins; + // add new mask + memcpy(pTMaskTop, Mask, imagespatialsize); - unsigned char * pSDs = BGModel->SDbinsImage; + // advance Temporal buffer pointer + TemporalBufferTop = (TemporalBufferTop + 1) % TemporalBufferLength; - FindHistMedians(&(AbsDiffHist)); - EstimateSDsFromAbsDiffHist(&(AbsDiffHist), pSDs, imagebuffersize, MinSD, MaxSD, KernelBins); - } + BGModel->TemporalBufferTop = TemporalBufferTop; - TimeIndex++; -} + TBCount++; -/*********************************************************************/ + // estimate SDs -void DisplayPropabilityImageWithThresholding(double * Pimage, - unsigned char * DisplayImage, - double Threshold, - unsigned int rows, - unsigned int cols) -{ - double p; + if (SdEstimateFlag && UpdateSDRate && ((TimeIndex) % UpdateSDRate == 0)) + { + double MaxSD = KernelTable->maxsegma; + double MinSD = KernelTable->minsegma; + int KernelBins = KernelTable->segmabins; - for (unsigned int i = 0; i < rows*cols; i++) - { - p = Pimage[i]; + unsigned char * pSDs = BGModel->SDbinsImage; - DisplayImage[i] = (p > Threshold) ? 0 : 255; - } -} + FindHistMedians(&(AbsDiffHist)); + EstimateSDsFromAbsDiffHist(&(AbsDiffHist), pSDs, imagebuffersize, MinSD, MaxSD, KernelBins); + } -/*********************************************************************/ + TimeIndex++; + } -void NPBGSubtractor::NPBGSubtraction_Subset_Kernel( - unsigned char * image, - unsigned char * FGImage, - unsigned char * FilteredFGImage) -{ - unsigned int i, j; - unsigned char *pSequence = BGModel->Sequence; + /*********************************************************************/ - unsigned int SampleSize = BGModel->SampleSize; + void DisplayPropabilityImageWithThresholding(double * Pimage, + unsigned char * DisplayImage, + double Threshold, + unsigned int rows, + unsigned int cols) + { + double p; - double *kerneltable = KernelTable->kerneltable; - int KernelHalfWidth = KernelTable->tablehalfwidth; - //double *KernelSum = KernelTable->kernelsums; - double KernelMaxSigma = KernelTable->maxsegma; - double KernelMinSigma = KernelTable->minsegma; - int KernelBins = KernelTable->segmabins; - unsigned char * SDbins = BGModel->SDbinsImage; + for (unsigned int i = 0; i < rows*cols; i++) + { + p = Pimage[i]; - //unsigned char * SaturationImage = FilteredFGImage; + DisplayImage[i] = (p > Threshold) ? 0 : 255; + } + } - // default sigmas .. to be removed. - double sigma1; - double sigma2; - double sigma3; + /*********************************************************************/ - sigma1 = 2.25; - sigma2 = 2.25; - sigma3 = 2.25; + void NPBGSubtractor::NPBGSubtraction_Subset_Kernel( + unsigned char * image, + unsigned char * FGImage, + unsigned char * FilteredFGImage) + { + unsigned int i, j; + unsigned char *pSequence = BGModel->Sequence; - double p; - double th; + unsigned int SampleSize = BGModel->SampleSize; - double alpha; + double *kerneltable = KernelTable->kerneltable; + int KernelHalfWidth = KernelTable->tablehalfwidth; + //double *KernelSum = KernelTable->kernelsums; + double KernelMaxSigma = KernelTable->maxsegma; + double KernelMinSigma = KernelTable->minsegma; + int KernelBins = KernelTable->segmabins; + unsigned char * SDbins = BGModel->SDbinsImage; - alpha = AlphaValue; + //unsigned char * SaturationImage = FilteredFGImage; - /* intialize FG image */ + // default sigmas .. to be removed. + double sigma1; + double sigma2; + double sigma3; - memset(FGImage, 0, rows*cols); + sigma1 = 2.25; + sigma2 = 2.25; + sigma3 = 2.25; - //Threshold=1; - th = Threshold * SampleSize; + double p; + double th; - double sum = 0, kernel1, kernel2, kernel3; - int k, g; + double alpha; + alpha = AlphaValue; - if (color_channels == 1) - { - // gray scale + /* intialize FG image */ - int kernelbase; + memset(FGImage, 0, rows*cols); - for (i = 0; i < rows*cols; i++) - { - kernelbase = SDbins[i] * (2 * KernelHalfWidth + 1); - sum = 0; - j = 0; + //Threshold=1; + th = Threshold * SampleSize; - while (j < SampleSize && sum < th) - { - g = pSequence[j*imagesize + i]; - k = g - image[i] + KernelHalfWidth; - sum += kerneltable[kernelbase + k]; - j++; - } + double sum = 0, kernel1, kernel2, kernel3; + int k, g; - p = sum / j; - Pimage1[i] = p; - } - } - else if (UseColorRatiosFlag && SubsetFlag) - { - // color ratios - unsigned int ig; - int base; + if (color_channels == 1) + { + // gray scale - int kernelbase1; - int kernelbase2; - int kernelbase3; + int kernelbase; - unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1; + for (i = 0; i < rows*cols; i++) + { + kernelbase = SDbins[i] * (2 * KernelHalfWidth + 1); + sum = 0; + j = 0; + + while (j < SampleSize && sum < th) + { + g = pSequence[j*imagesize + i]; + k = g - image[i] + KernelHalfWidth; + sum += kerneltable[kernelbase + k]; + j++; + } + + p = sum / j; + Pimage1[i] = p; + } + } + else if (UseColorRatiosFlag && SubsetFlag) + { + // color ratios - double beta = 3.0; // minimum bound on the range. - double betau = 100.0; + unsigned int ig; + int base; - double beta_over_alpha = beta / alpha; - double betau_over_alpha = betau / alpha; + int kernelbase1; + int kernelbase2; + int kernelbase3; + unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1; - double brightness_lowerbound = 1 - alpha; - double brightness_upperbound = 1 + alpha; - int x1, x2; - unsigned int SubsampleCount; + double beta = 3.0; // minimum bound on the range. + double betau = 100.0; - for (i = 0, ig = 0; i < imagesize; i += 3, ig++) - { - kernelbase1 = SDbins[i] * kerneltablewidth; - kernelbase2 = SDbins[i + 1] * kerneltablewidth; - kernelbase3 = SDbins[i + 2] * kerneltablewidth; + double beta_over_alpha = beta / alpha; + double betau_over_alpha = betau / alpha; - sum = 0; - j = 0; - SubsampleCount = 0; - while (j < SampleSize && sum < th) - { - base = j*imagesize + i; - g = pSequence[base]; + double brightness_lowerbound = 1 - alpha; + double brightness_upperbound = 1 + alpha; + int x1, x2; + unsigned int SubsampleCount; - if (g < beta_over_alpha) - { - x1 = (int)(g - beta); - x2 = (int)(g + beta); - } - else if (g > betau_over_alpha) - { - x1 = (int)(g - betau); - x2 = (int)(g + betau); + for (i = 0, ig = 0; i < imagesize; i += 3, ig++) + { + kernelbase1 = SDbins[i] * kerneltablewidth; + kernelbase2 = SDbins[i + 1] * kerneltablewidth; + kernelbase3 = SDbins[i + 2] * kerneltablewidth; + + sum = 0; + j = 0; + SubsampleCount = 0; + + while (j < SampleSize && sum < th) + { + base = j*imagesize + i; + g = pSequence[base]; + + if (g < beta_over_alpha) + { + x1 = (int)(g - beta); + x2 = (int)(g + beta); + } + else if (g > betau_over_alpha) + { + x1 = (int)(g - betau); + x2 = (int)(g + betau); + } + else + { + x1 = (int)(g*brightness_lowerbound + 0.5); + x2 = (int)(g*brightness_upperbound + 0.5); + } + + if (x1 < image[i] && image[i] < x2) + { + g = pSequence[base + 1]; + k = (g - image[i + 1]) + KernelHalfWidth; + kernel2 = kerneltable[kernelbase2 + k]; + + g = pSequence[base + 2]; + k = (g - image[i + 2]) + KernelHalfWidth; + kernel3 = kerneltable[kernelbase3 + k]; + + sum += kernel2*kernel3; + + SubsampleCount++; + } + j++; + } + + p = sum / j; + Pimage1[ig] = p; + } } - else + else if (UseColorRatiosFlag && !SubsetFlag) { - x1 = (int)(g*brightness_lowerbound + 0.5); - x2 = (int)(g*brightness_upperbound + 0.5); - } + // color ratios - if (x1 < image[i] && image[i] < x2) - { - g = pSequence[base + 1]; - k = (g - image[i + 1]) + KernelHalfWidth; - kernel2 = kerneltable[kernelbase2 + k]; + unsigned int ig; + int base; + int bin; - g = pSequence[base + 2]; - k = (g - image[i + 2]) + KernelHalfWidth; - kernel3 = kerneltable[kernelbase3 + k]; + int kernelbase1; + int kernelbase2; + int kernelbase3; - sum += kernel2*kernel3; + unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1; - SubsampleCount++; - } - j++; - } + int gmin, gmax; + double gfactor; - p = sum / j; - Pimage1[ig] = p; - } - } - else if (UseColorRatiosFlag && !SubsetFlag) - { - // color ratios + gmax = 200; + gmin = 10; - unsigned int ig; - int base; - int bin; + gfactor = (KernelMaxSigma - KernelMinSigma) / (double)(gmax - gmin); - int kernelbase1; - int kernelbase2; - int kernelbase3; + for (i = 0, ig = 0; i < imagesize; i += 3, ig++) + { - unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1; + bin = (int)floor(((alpha * 16 - KernelMinSigma)*KernelBins) / (KernelMaxSigma - KernelMinSigma)); - int gmin, gmax; - double gfactor; + kernelbase1 = bin*kerneltablewidth; + kernelbase2 = SDbins[i + 1] * kerneltablewidth; + kernelbase3 = SDbins[i + 2] * kerneltablewidth; - gmax = 200; - gmin = 10; + sum = 0; + j = 0; - gfactor = (KernelMaxSigma - KernelMinSigma) / (double)(gmax - gmin); + while (j < SampleSize && sum < th) + { + base = j*imagesize + i; + g = pSequence[base]; - for (i = 0, ig = 0; i < imagesize; i += 3, ig++) - { + if (g < gmin) + bin = 0; + else if (g > gmax) + bin = KernelBins - 1; + else + bin = (int)((g - gmin) * gfactor + 0.5); - bin = (int)floor(((alpha * 16 - KernelMinSigma)*KernelBins) / (KernelMaxSigma - KernelMinSigma)); + kernelbase1 = bin*kerneltablewidth; - kernelbase1 = bin*kerneltablewidth; - kernelbase2 = SDbins[i + 1] * kerneltablewidth; - kernelbase3 = SDbins[i + 2] * kerneltablewidth; + k = (g - image[i]) + KernelHalfWidth; + kernel1 = kerneltable[kernelbase1 + k]; - sum = 0; - j = 0; + g = pSequence[base + 1]; + k = (g - image[i + 1]) + KernelHalfWidth; + kernel2 = kerneltable[kernelbase2 + k]; - while (j < SampleSize && sum < th) - { - base = j*imagesize + i; - g = pSequence[base]; - - if (g < gmin) - bin = 0; - else if (g > gmax) - bin = KernelBins - 1; - else - bin = (int)((g - gmin) * gfactor + 0.5); + g = pSequence[base + 2]; + k = (g - image[i + 2]) + KernelHalfWidth; + kernel3 = kerneltable[kernelbase3 + k]; - kernelbase1 = bin*kerneltablewidth; + sum += kernel1*kernel2*kernel3; + j++; + } - k = (g - image[i]) + KernelHalfWidth; - kernel1 = kerneltable[kernelbase1 + k]; + p = sum / j; + Pimage1[ig] = p; + } + } + else // RGB color + { + unsigned int ig; + int base; - g = pSequence[base + 1]; - k = (g - image[i + 1]) + KernelHalfWidth; - kernel2 = kerneltable[kernelbase2 + k]; + int kernelbase1; + int kernelbase2; + int kernelbase3; + unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1; - g = pSequence[base + 2]; - k = (g - image[i + 2]) + KernelHalfWidth; - kernel3 = kerneltable[kernelbase3 + k]; + for (i = 0, ig = 0; i < imagesize; i += 3, ig++) + { + // used extimated kernel width to access the right kernel + kernelbase1 = SDbins[i] * kerneltablewidth; + kernelbase2 = SDbins[i + 1] * kerneltablewidth; + kernelbase3 = SDbins[i + 2] * kerneltablewidth; + + sum = 0; + j = 0; + while (j < SampleSize && sum < th) + { + base = j*imagesize + i; + g = pSequence[base]; + k = (g - image[i]) + KernelHalfWidth; + kernel1 = kerneltable[kernelbase1 + k]; + + g = pSequence[base + 1]; + k = (g - image[i + 1]) + KernelHalfWidth; + kernel2 = kerneltable[kernelbase2 + k]; + + g = pSequence[base + 2]; + k = (g - image[i + 2]) + KernelHalfWidth; + kernel3 = kerneltable[kernelbase3 + k]; + + sum += kernel1*kernel2*kernel3; + j++; + } + + p = sum / j; + Pimage1[ig] = p; + } + } - sum += kernel1*kernel2*kernel3; - j++; + DisplayPropabilityImageWithThresholding(Pimage1, FGImage, Threshold, rows, cols); } - p = sum / j; - Pimage1[ig] = p; - } - } - else // RGB color - { - unsigned int ig; - int base; - - int kernelbase1; - int kernelbase2; - int kernelbase3; - unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1; + /*********************************************************************/ - for (i = 0, ig = 0; i < imagesize; i += 3, ig++) - { - // used extimated kernel width to access the right kernel - kernelbase1 = SDbins[i] * kerneltablewidth; - kernelbase2 = SDbins[i + 1] * kerneltablewidth; - kernelbase3 = SDbins[i + 2] * kerneltablewidth; - - sum = 0; - j = 0; - while (j < SampleSize && sum < th) + void NPBGSubtractor::NBBGSubtraction(unsigned char * Frame, + unsigned char * FGImage, + unsigned char * FilteredFGImage, + unsigned char ** DisplayBuffers) { - base = j*imagesize + i; - g = pSequence[base]; - k = (g - image[i]) + KernelHalfWidth; - kernel1 = kerneltable[kernelbase1 + k]; + if (UseColorRatiosFlag) + BGR2SnGnRn(Frame, tempFrame, rows, cols); + else + memcpy(tempFrame, Frame, rows*cols*color_channels); - g = pSequence[base + 1]; - k = (g - image[i + 1]) + KernelHalfWidth; - kernel2 = kerneltable[kernelbase2 + k]; + NPBGSubtraction_Subset_Kernel(tempFrame, FGImage, FilteredFGImage); + /*NoiseFilter_o(FGImage,DisplayBuffers[3],rows,cols,4); + BuildImageIndex(DisplayBuffers[3],imageindex,rows,cols); - g = pSequence[base + 2]; - k = (g - image[i + 2]) + KernelHalfWidth; - kernel3 = kerneltable[kernelbase3 + k]; + ExpandOperatorIndexed(DisplayBuffers[3],imageindex,DisplayBuffers[4],imageindex,rows,cols); + ShrinkOperatorIndexed(DisplayBuffers[4],imageindex,FilteredFGImage,imageindex,rows,cols); - sum += kernel1*kernel2*kernel3; - j++; + memset(DisplayBuffers[3],0,rows*cols);*/ } - p = sum / j; - Pimage1[ig] = p; + void NPBGSubtractor::Update(unsigned char * FGMask) + { + if (UpdateBGFlag) + SequenceBGUpdate_Pairs(tempFrame, FGMask); + } } } - - DisplayPropabilityImageWithThresholding(Pimage1, FGImage, Threshold, rows, cols); -} - -/*********************************************************************/ - -void NPBGSubtractor::NBBGSubtraction(unsigned char * Frame, - unsigned char * FGImage, - unsigned char * FilteredFGImage, - unsigned char ** DisplayBuffers) -{ - if (UseColorRatiosFlag) - BGR2SnGnRn(Frame, tempFrame, rows, cols); - else - memcpy(tempFrame, Frame, rows*cols*color_channels); - - NPBGSubtraction_Subset_Kernel(tempFrame, FGImage, FilteredFGImage); - /*NoiseFilter_o(FGImage,DisplayBuffers[3],rows,cols,4); - BuildImageIndex(DisplayBuffers[3],imageindex,rows,cols); - - ExpandOperatorIndexed(DisplayBuffers[3],imageindex,DisplayBuffers[4],imageindex,rows,cols); - ShrinkOperatorIndexed(DisplayBuffers[4],imageindex,FilteredFGImage,imageindex,rows,cols); - - memset(DisplayBuffers[3],0,rows*cols);*/ -} - -void NPBGSubtractor::Update(unsigned char * FGMask) -{ - if (UpdateBGFlag) - SequenceBGUpdate_Pairs(tempFrame, FGMask); } diff --git a/src/algorithms/KDE/NPBGSubtractor.h b/src/algorithms/KDE/NPBGSubtractor.h index fbe8664e96b7bb46c25abe443a333d5ab13eef7a..837fc85cb36a4aaa353ee9396ccfa6404e73ec5f 100644 --- a/src/algorithms/KDE/NPBGSubtractor.h +++ b/src/algorithms/KDE/NPBGSubtractor.h @@ -3,93 +3,102 @@ #include "NPBGmodel.h" #include "KernelTable.h" -#define FALSE 0 -#define TRUE 1 +namespace bgslibrary +{ + namespace algorithms + { + namespace kde + { + const int FALSE = 0; + const int TRUE = 1; -// kernal look up table settings -#define KERNELHALFWIDTH 255 -#define SEGMAMAX 36.5 -#define SEGMAMIN 0.5 -#define SEGMABINS 80 -#define DEFAULTSEGMA 1.0 + // kernal look up table settings + const int KERNELHALFWIDTH = 255; + const float SEGMAMAX = 36.5; + const float SEGMAMIN = 0.5; + const int SEGMABINS = 80; + const float DEFAULTSEGMA = 1.0; -typedef struct -{ - unsigned char *Hist; - unsigned char *MedianBins; - unsigned char *MedianFreq; - unsigned char *AccSum; - unsigned char histbins; - unsigned char histsum; - unsigned int imagesize; -} DynamicMedianHistogram; + typedef struct + { + unsigned char *Hist; + unsigned char *MedianBins; + unsigned char *MedianFreq; + unsigned char *AccSum; + unsigned char histbins; + unsigned char histsum; + unsigned int imagesize; + } DynamicMedianHistogram; -typedef struct -{ - unsigned int cnt; - unsigned int *List; -} ImageIndex; + typedef struct + { + unsigned int cnt; + unsigned int *List; + } ImageIndex; -class NPBGSubtractor -{ -private: - unsigned int rows; - unsigned int cols; - unsigned int color_channels; - unsigned int imagesize; - // flags - unsigned char UpdateBGFlag; - unsigned char SdEstimateFlag; - unsigned char UseColorRatiosFlag; - unsigned char AdaptBGFlag; - unsigned char SubsetFlag; - // - int UpdateSDRate; - double Threshold; - double AlphaValue; - unsigned int TimeIndex; - ImageIndex *imageindex; - unsigned char *tempFrame; - KernelLUTable *KernelTable; - NPBGmodel *BGModel; - DynamicMedianHistogram AbsDiffHist; - double *Pimage1; - double *Pimage2; - // - void NPBGSubtraction_Subset_Kernel(unsigned char * image, unsigned char * FGImage, unsigned char * FilteredFGImage); - void SequenceBGUpdate_Pairs(unsigned char * image, unsigned char * Mask); + class NPBGSubtractor + { + private: + unsigned int rows; + unsigned int cols; + unsigned int color_channels; + unsigned int imagesize; + // flags + unsigned char UpdateBGFlag; + unsigned char SdEstimateFlag; + unsigned char UseColorRatiosFlag; + unsigned char AdaptBGFlag; + unsigned char SubsetFlag; + // + int UpdateSDRate; + double Threshold; + double AlphaValue; + unsigned int TimeIndex; + ImageIndex *imageindex; + unsigned char *tempFrame; + KernelLUTable *KernelTable; + NPBGmodel *BGModel; + DynamicMedianHistogram AbsDiffHist; + double *Pimage1; + double *Pimage2; + // + void NPBGSubtraction_Subset_Kernel(unsigned char * image, unsigned char * FGImage, unsigned char * FilteredFGImage); + void SequenceBGUpdate_Pairs(unsigned char * image, unsigned char * Mask); -public: - NPBGSubtractor(); - virtual ~NPBGSubtractor(); - //~NPBGSubtractor(); + public: + NPBGSubtractor(); + virtual ~NPBGSubtractor(); + //~NPBGSubtractor(); - int Intialize(unsigned int rows, - unsigned int cols, - unsigned int color_channels, - unsigned int SequenceLength, - unsigned int TimeWindowSize, - unsigned char SDEstimationFlag, - unsigned char UseColorRatiosFlag); + int Intialize(unsigned int rows, + unsigned int cols, + unsigned int color_channels, + unsigned int SequenceLength, + unsigned int TimeWindowSize, + unsigned char SDEstimationFlag, + unsigned char UseColorRatiosFlag); - void AddFrame(unsigned char * ImageBuffer); + void AddFrame(unsigned char * ImageBuffer); - void Estimation(); + void Estimation(); - void NBBGSubtraction(unsigned char *Frame, - unsigned char *FGImage, - unsigned char *FilteredFGImage, - unsigned char **DisplayBuffers); + void NBBGSubtraction(unsigned char *Frame, + unsigned char *FGImage, + unsigned char *FilteredFGImage, + unsigned char **DisplayBuffers); - void Update(unsigned char *); + void Update(unsigned char *); - void SetThresholds(double th, double alpha) - { - Threshold = th; - AlphaValue = alpha; - }; + void SetThresholds(double th, double alpha) + { + Threshold = th; + AlphaValue = alpha; + }; - void SetUpdateFlag(unsigned int bgflag) { - UpdateBGFlag = bgflag; - }; -}; + void SetUpdateFlag(unsigned int bgflag) { + UpdateBGFlag = bgflag; + }; + }; + } + } +} diff --git a/src/algorithms/KDE/NPBGmodel.cpp b/src/algorithms/KDE/NPBGmodel.cpp index f4bd2728cc90c186993d9fd24c02a9504ddbd5de..4e5b3cc9a37850d6c72bcd0dc575ab60caa8118a 100644 --- a/src/algorithms/KDE/NPBGmodel.cpp +++ b/src/algorithms/KDE/NPBGmodel.cpp @@ -2,26 +2,23 @@ #include "NPBGmodel.h" -#ifdef _DEBUG -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -//#define new DEBUG_NEW -#endif +//#ifdef _DEBUG +//#undef THIS_FILE +//static char THIS_FILE[] = __FILE__; +////#define new DEBUG_NEW +//#endif -NPBGmodel::NPBGmodel() -{ - std::cout << "NPBGmodel()" << std::endl; -} +using namespace bgslibrary::algorithms::kde; -NPBGmodel::~NPBGmodel() -{ +NPBGmodel::NPBGmodel() {} + +NPBGmodel::~NPBGmodel() { delete Sequence; delete PixelQTop; delete TemporalBuffer; delete TemporalMask; delete AccMask; //delete SDbinsImage; - std::cout << "~NPBGmodel()" << std::endl; } NPBGmodel::NPBGmodel(unsigned int Rows, @@ -31,8 +28,6 @@ NPBGmodel::NPBGmodel(unsigned int Rows, unsigned int pTimeWindowSize, unsigned int bg_suppression_time) { - std::cout << "NPBGmodel()" << std::endl; - imagesize = Rows*Cols*ColorChannels; rows = Rows; diff --git a/src/algorithms/KDE/NPBGmodel.h b/src/algorithms/KDE/NPBGmodel.h index fc95adf67644c783c40ba7abec77d6a933815c7c..02a1f731b83e19adaa714c546fda6dc6e62655c5 100644 --- a/src/algorithms/KDE/NPBGmodel.h +++ b/src/algorithms/KDE/NPBGmodel.h @@ -2,51 +2,60 @@ #include <iostream> -class NPBGmodel +namespace bgslibrary { -private: - unsigned char *Sequence; - unsigned int SampleSize; - unsigned int TimeWindowSize; - - unsigned int rows, cols, color_channels; - unsigned int imagesize; - - unsigned int Top; - unsigned char *PixelQTop; - - //unsigned int *PixelUpdateCounter; - - unsigned char *SDbinsImage; - - unsigned char *TemporalBuffer; - unsigned char TemporalBufferLength; - unsigned char TemporalBufferTop; - unsigned char *TemporalBufferMask; - - unsigned char *TemporalMask; - unsigned char TemporalMaskLength; - unsigned char TemporalMaskTop; - - unsigned int *AccMask; - unsigned int ResetMaskTh; // Max continous duration a pixel can be detected before - // it is forced to be updated... - - double *weights; - -public: - NPBGmodel(); - //~NPBGmodel(); - virtual ~NPBGmodel(); - - NPBGmodel(unsigned int Rows, - unsigned int Cols, - unsigned int ColorChannels, - unsigned int Length, - unsigned int pTimeWindowSize, - unsigned int bg_suppression_time); - - void AddFrame(unsigned char *ImageBuffer); - - friend class NPBGSubtractor; -}; + namespace algorithms + { + namespace kde + { + class NPBGmodel + { + private: + unsigned char *Sequence; + unsigned int SampleSize; + unsigned int TimeWindowSize; + + unsigned int rows, cols, color_channels; + unsigned int imagesize; + + unsigned int Top; + unsigned char *PixelQTop; + + //unsigned int *PixelUpdateCounter; + + unsigned char *SDbinsImage; + + unsigned char *TemporalBuffer; + unsigned char TemporalBufferLength; + unsigned char TemporalBufferTop; + unsigned char *TemporalBufferMask; + + unsigned char *TemporalMask; + unsigned char TemporalMaskLength; + unsigned char TemporalMaskTop; + + unsigned int *AccMask; + unsigned int ResetMaskTh; // Max continous duration a pixel can be detected before + // it is forced to be updated... + + double *weights; + + public: + NPBGmodel(); + //~NPBGmodel(); + virtual ~NPBGmodel(); + + NPBGmodel(unsigned int Rows, + unsigned int Cols, + unsigned int ColorChannels, + unsigned int Length, + unsigned int pTimeWindowSize, + unsigned int bg_suppression_time); + + void AddFrame(unsigned char *ImageBuffer); + + friend class NPBGSubtractor; + }; + } + } +} diff --git a/src/algorithms/LBAdaptiveSOM.cpp b/src/algorithms/LBAdaptiveSOM.cpp index a43e55acbc03a7316d75b20a94b29fd0fcf4ec48..b051ed54e035be32f5ce9d84cc68acb44fb72411 100644 --- a/src/algorithms/LBAdaptiveSOM.cpp +++ b/src/algorithms/LBAdaptiveSOM.cpp @@ -29,7 +29,7 @@ void LBAdaptiveSOM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::M int w = cvGetSize(frame).width; int h = cvGetSize(frame).height; - m_pBGModel = new BGModelSom(w, h); + m_pBGModel = new lb::BGModelSom(w, h); m_pBGModel->InitModel(frame); } diff --git a/src/algorithms/LBAdaptiveSOM.h b/src/algorithms/LBAdaptiveSOM.h index e1648960b2cd3104936eb952bd8726c8f1f6bd21..7141f91e0fcda5cd0b831c28883835a111774cb7 100644 --- a/src/algorithms/LBAdaptiveSOM.h +++ b/src/algorithms/LBAdaptiveSOM.h @@ -7,9 +7,6 @@ #include "lb/BGModelSom.h" -using namespace lb_library; -using namespace lb_library::AdaptiveSOM; - namespace bgslibrary { namespace algorithms @@ -17,7 +14,7 @@ namespace bgslibrary class LBAdaptiveSOM : public IBGS { private: - BGModel* m_pBGModel; + lb::BGModel* m_pBGModel; int sensitivity; int trainingSensitivity; int learningRate; diff --git a/src/algorithms/LBFuzzyAdaptiveSOM.cpp b/src/algorithms/LBFuzzyAdaptiveSOM.cpp index 1eadf750568ff78c6a8b736f31cf62f6a9442039..a4feed6d131ed665184b36bc64ca5dc2aeac9544 100644 --- a/src/algorithms/LBFuzzyAdaptiveSOM.cpp +++ b/src/algorithms/LBFuzzyAdaptiveSOM.cpp @@ -28,7 +28,7 @@ void LBFuzzyAdaptiveSOM::process(const cv::Mat &img_input, cv::Mat &img_output, int w = cvGetSize(frame).width; int h = cvGetSize(frame).height; - m_pBGModel = new BGModelFuzzySom(w, h); + m_pBGModel = new lb::BGModelFuzzySom(w, h); m_pBGModel->InitModel(frame); } diff --git a/src/algorithms/LBFuzzyAdaptiveSOM.h b/src/algorithms/LBFuzzyAdaptiveSOM.h index 6c9b4421de0d2fefe20d4b797f53f119455b4cfd..9c45382913d35ca941b05f2cc29be89b1216fb6c 100644 --- a/src/algorithms/LBFuzzyAdaptiveSOM.h +++ b/src/algorithms/LBFuzzyAdaptiveSOM.h @@ -7,9 +7,6 @@ #include "lb/BGModelFuzzySom.h" -using namespace lb_library; -using namespace lb_library::FuzzyAdaptiveSOM; - namespace bgslibrary { namespace algorithms @@ -17,7 +14,7 @@ namespace bgslibrary class LBFuzzyAdaptiveSOM : public IBGS { private: - BGModel* m_pBGModel; + lb::BGModel* m_pBGModel; int sensitivity; int trainingSensitivity; int learningRate; diff --git a/src/algorithms/LBFuzzyGaussian.cpp b/src/algorithms/LBFuzzyGaussian.cpp index 95171913a037280b14984c358497c171140d9368..c501848815ad4b6ae9a717d2658e37d3785c8dc4 100644 --- a/src/algorithms/LBFuzzyGaussian.cpp +++ b/src/algorithms/LBFuzzyGaussian.cpp @@ -28,7 +28,7 @@ void LBFuzzyGaussian::process(const cv::Mat &img_input, cv::Mat &img_output, cv: int w = cvGetSize(frame).width; int h = cvGetSize(frame).height; - m_pBGModel = new BGModelFuzzyGauss(w, h); + m_pBGModel = new lb::BGModelFuzzyGauss(w, h); m_pBGModel->InitModel(frame); } diff --git a/src/algorithms/LBFuzzyGaussian.h b/src/algorithms/LBFuzzyGaussian.h index 7a48b71b650353c69ffebe86577c04def7df7a8b..3725fda48aff1e7641d85e28d4d514a2883f36a6 100644 --- a/src/algorithms/LBFuzzyGaussian.h +++ b/src/algorithms/LBFuzzyGaussian.h @@ -7,9 +7,6 @@ #include "lb/BGModelFuzzyGauss.h" -using namespace lb_library; -using namespace lb_library::FuzzyGaussian; - namespace bgslibrary { namespace algorithms @@ -17,7 +14,7 @@ namespace bgslibrary class LBFuzzyGaussian : public IBGS { private: - BGModel* m_pBGModel; + lb::BGModel* m_pBGModel; int sensitivity; int bgThreshold; int learningRate; diff --git a/src/algorithms/LBMixtureOfGaussians.cpp b/src/algorithms/LBMixtureOfGaussians.cpp index b04e0f63fb166584605aaf5b3e6d9b7efc56f257..219cd1b9f5dabcb370abfa618b3f3dc78430c4e4 100644 --- a/src/algorithms/LBMixtureOfGaussians.cpp +++ b/src/algorithms/LBMixtureOfGaussians.cpp @@ -28,7 +28,7 @@ void LBMixtureOfGaussians::process(const cv::Mat &img_input, cv::Mat &img_output int w = cvGetSize(frame).width; int h = cvGetSize(frame).height; - m_pBGModel = new BGModelMog(w, h); + m_pBGModel = new lb::BGModelMog(w, h); m_pBGModel->InitModel(frame); } diff --git a/src/algorithms/LBMixtureOfGaussians.h b/src/algorithms/LBMixtureOfGaussians.h index 0a5cf76c88a14a17c5fe2c109b0d73c459452fc3..b4211561ba04442c6b5ed0a6effd43b4fa6821bb 100644 --- a/src/algorithms/LBMixtureOfGaussians.h +++ b/src/algorithms/LBMixtureOfGaussians.h @@ -7,9 +7,6 @@ #include "lb/BGModelMog.h" -using namespace lb_library; -using namespace lb_library::MixtureOfGaussians; - namespace bgslibrary { namespace algorithms @@ -17,7 +14,7 @@ namespace bgslibrary class LBMixtureOfGaussians : public IBGS { private: - BGModel* m_pBGModel; + lb::BGModel* m_pBGModel; int sensitivity; int bgThreshold; int learningRate; diff --git a/src/algorithms/LBP_MRF.cpp b/src/algorithms/LBP_MRF.cpp index 1b2266e1adb3a49e06d4e234f2a1bb8696f490ff..e0d4c64977688cccf415075cb1f0c2ca27682b95 100644 --- a/src/algorithms/LBP_MRF.cpp +++ b/src/algorithms/LBP_MRF.cpp @@ -10,8 +10,8 @@ LBP_MRF::LBP_MRF() : { debug_construction(LBP_MRF); initLoadSaveConfig(algorithmName); - Detector = new MotionDetection(); - Detector->SetMode(MotionDetection::md_LBPHistograms); + Detector = new lbp_mrf::MotionDetection(); + Detector->SetMode(lbp_mrf::MotionDetection::md_LBPHistograms); } LBP_MRF::~LBP_MRF() { @@ -25,8 +25,8 @@ void LBP_MRF::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &im init(img_input, img_output, img_bgmodel); IplImage TempImage(img_input); - MEImage InputImage(img_input.cols, img_input.rows, img_input.channels()); - MEImage OutputImage(img_input.cols, img_input.rows, img_input.channels()); + lbp_mrf::MEImage InputImage(img_input.cols, img_input.rows, img_input.channels()); + lbp_mrf::MEImage OutputImage(img_input.cols, img_input.rows, img_input.channels()); InputImage.SetIplImage((void*)&TempImage); diff --git a/src/algorithms/LBP_MRF.h b/src/algorithms/LBP_MRF.h index 5e199a1c3211c30ec72b8c2ba6a9d7bd790fbc1f..0edb3873155940fa683ba053a095a0f12380f479 100644 --- a/src/algorithms/LBP_MRF.h +++ b/src/algorithms/LBP_MRF.h @@ -14,7 +14,7 @@ namespace bgslibrary class LBP_MRF : public IBGS { private: - MotionDetection* Detector; + lbp_mrf::MotionDetection* Detector; cv::Mat img_segmentation; public: diff --git a/src/algorithms/LBP_MRF/MEDefs.cpp b/src/algorithms/LBP_MRF/MEDefs.cpp index 07486dc8c4024a75493456208816b4552f0f73cb..726221fe68e96b81cdcd0c8562b8064e10f39295 100644 --- a/src/algorithms/LBP_MRF/MEDefs.cpp +++ b/src/algorithms/LBP_MRF/MEDefs.cpp @@ -2,19 +2,28 @@ #include "MEDefs.hpp" -float MERound(float number) -{ - double FracPart = 0.0; - double IntPart = 0.0; - float Ret = 0.0; +//using namespace bgslibrary::algorithms::lbp_mrf; - FracPart = modf((double)number, &IntPart); - if (number >= 0) +namespace bgslibrary +{ + namespace algorithms { - Ret = (float)(FracPart >= 0.5 ? IntPart + 1 : IntPart); - } - else { - Ret = (float)(FracPart <= -0.5 ? IntPart - 1 : IntPart); + namespace lbp_mrf + { + float MERound(float number) + { + double FracPart = 0.0; + double IntPart = 0.0; + float Ret = 0.0; + + FracPart = modf((double)number, &IntPart); + if (number >= 0) + Ret = (float)(FracPart >= 0.5 ? IntPart + 1 : IntPart); + else + Ret = (float)(FracPart <= -0.5 ? IntPart - 1 : IntPart); + + return Ret; + } + } } - return Ret; } diff --git a/src/algorithms/LBP_MRF/MEDefs.hpp b/src/algorithms/LBP_MRF/MEDefs.hpp index 0c955e17560ff1ccbfeff55715f51dee0e21b1ee..5092f87f0a07e2c9180fb8c718e9d55aa52974cc 100644 --- a/src/algorithms/LBP_MRF/MEDefs.hpp +++ b/src/algorithms/LBP_MRF/MEDefs.hpp @@ -1,54 +1,60 @@ #pragma once - /// Pi value -#ifndef ME_PI_VALUE -#define ME_PI_VALUE 3.14159265 -#endif - -/*! Process state */ -typedef enum { - ps_Min = 0, /*!< Minimum value */ - ps_Uninitialized = ps_Min, /*!< Uninitialized state */ - ps_Initialized, /*!< Initialized state */ - ps_InProgress, /*!< In progress state */ - ps_Successful, /*!< Successful state */ - ps_Max = ps_Successful /*!< Maximum value */ -} MEProcessStateType; - -template <typename T> -const T& MEMin(const T& a, const T& b) -{ - if (a < b) - return a; - return b; -} - -template <typename T> -const T& MEMax(const T& a, const T& b) +namespace bgslibrary { - if (a < b) - return b; - return a; + namespace algorithms + { + namespace lbp_mrf + { + const double ME_PI_VALUE = 3.14159265; + + /*! Process state */ + typedef enum { + ps_Min = 0, /*!< Minimum value */ + ps_Uninitialized = ps_Min, /*!< Uninitialized state */ + ps_Initialized, /*!< Initialized state */ + ps_InProgress, /*!< In progress state */ + ps_Successful, /*!< Successful state */ + ps_Max = ps_Successful /*!< Maximum value */ + } MEProcessStateType; + + template <typename T> + const T& MEMin(const T& a, const T& b) + { + if (a < b) + return a; + return b; + } + + template <typename T> + const T& MEMax(const T& a, const T& b) + { + if (a < b) + return b; + return a; + } + + template <typename T> + const T& MEBound(const T& min, const T& val, const T& max) + { + return MEMax(min, MEMin(max, val)); + } + + /*! + * @brief Round a float number + * + * @param number number to round + * + * @return New float number + * + * This method rounds a float number, if the fraction is .5 or lower + * then it rounds down, otherwise up. + * + */ + + float MERound(float number); + + /** @} */ + } + } } - -template <typename T> -const T& MEBound(const T& min, const T& val, const T& max) -{ - return MEMax(min, MEMin(max, val)); -} - -/*! - * @brief Round a float number - * - * @param number number to round - * - * @return New float number - * - * This method rounds a float number, if the fraction is .5 or lower - * then it rounds down, otherwise up. - * - */ - -float MERound(float number); - -/** @} */ diff --git a/src/algorithms/LBP_MRF/MEHistogram.cpp b/src/algorithms/LBP_MRF/MEHistogram.cpp index ec5ce1a533e493ec549da8e4fca56ccbf54a98a0..8e5cde351ec3141f412527575dc73a605020059b 100644 --- a/src/algorithms/LBP_MRF/MEHistogram.cpp +++ b/src/algorithms/LBP_MRF/MEHistogram.cpp @@ -1,4 +1,5 @@ #include <opencv2/opencv.hpp> +// opencv legacy includes #include <opencv2/core/core_c.h> #include <opencv2/imgproc/types_c.h> #include <opencv2/imgproc/imgproc_c.h> @@ -7,17 +8,15 @@ #include "MEDefs.hpp" #include "MEImage.hpp" -MEHistogram::MEHistogram() -{ +using namespace bgslibrary::algorithms::lbp_mrf; + +MEHistogram::MEHistogram() { Clear(); } -MEHistogram::~MEHistogram() -{ -} +MEHistogram::~MEHistogram(){} -void MEHistogram::Clear() -{ +void MEHistogram::Clear() { memset(&HistogramData, 0, 256 * sizeof(int)); } diff --git a/src/algorithms/LBP_MRF/MEHistogram.hpp b/src/algorithms/LBP_MRF/MEHistogram.hpp index be8453e503b383727e6801bc71a8cf7f7dbc3359..9ae3a022bf743db172845dde550cb06baa663d39 100644 --- a/src/algorithms/LBP_MRF/MEHistogram.hpp +++ b/src/algorithms/LBP_MRF/MEHistogram.hpp @@ -1,317 +1,326 @@ #pragma once -class MEImage; - -/** - * MEHistogram - * @brief The class provides basic histogram operations - */ -class MEHistogram -{ -public: - - /// Types of histogram calculation - typedef enum { - h_Min = 0, /*!< Minimum value */ - h_Overwrite = h_Min, /*!< Overwrite */ - h_Add, /*!< Add */ - h_Max = h_Add /*!< Maximum value */ - } HistogramType; - - /// Types of histogram stretching - typedef enum { - s_Min = 0, /*!< Minimum value */ - s_OwnMode = s_Min, /*!< Own mode */ - s_GimpMode, /*!< Gimp mode */ - s_Max = s_GimpMode /*!< Maximum value */ - } StretchType; - - /// Constructor of class - MEHistogram(); - /// Destructor of class - ~MEHistogram(); - - /*! - * @brief Clear histogram data - * - * Clear histogram data. - * - */ - - void Clear(); - - /*! - * @brief Equality (==) operator - * - * @param histogram Histogram to be compared - * - * @return True if the two histograms are equal. - * - * Compare two histograms. - * - */ - - bool operator==(MEHistogram& histogram) const; - - /*! - * @brief Calculate the histogram of one color channel - * - * @param image Given image for the calculations - * @param channel Selected color channel for calculation (Range: 1..x) - * @param mode The mode of calculation. - * - * The method calculates the histograms of a color channel. - * There is two different type of the function: - * - * - h_Add: Add the data to the existing histogram. - * - h_Overwrite: Clear the histogram data before the - * calculation. - * - */ - - void Calculate(MEImage& image, int channel, HistogramType mode); - - /*! - * @brief Calculate the average histogram of an image - * - * @param image Given image for the calculations - * @param mode Histogram calculation mode - * - * The method calculates the average histogram of an image. - * - */ - - void Calculate(MEImage& image, HistogramType mode = h_Overwrite); - - /*! - * @brief Calculate the histogram of an image region - * - * @param image Given image for the calculations - * @param channel Selected color channel for calculation (Range: 1..x) - * @param x0 x0 coordinate of the region - * @param y0 y0 coordinate of the region - * @param x1 x1 coordinate of the region - * @param y1 y1 coordinate of the region - * - * The method calculates the average histogram of an image region - * (x0,y0)-(x1,y1). - * - */ - - void Calculate(MEImage& image, int channel, int x0, int y0, int x1, int y1); - - /*! - * @brief Get the index of maximum value of the histogram - * - * @return Index number - * - * Function gives an index value back where is the highest - * peak of the histogram. - * - */ - - int GetPeakIndex() const; - - /*! - * @brief Get the lowest histogram index with an threshold value - * - * @param threshold Specified threshold (in percent: 0..100 %) - * - * @return Index number - * - * Function gives the lowest index back whose value reaches - * an threshold value calculated by (counted pixel number / - * 10*threshold / 100). - * - */ - - int GetLowestLimitIndex(int threshold) const; - - /*! - * @brief Get the highest histogram index with an threshold value - * - * @param threshold Specified threshold (in percent: 0..100 %) - * - * @return Index number - * - * Function gives the highest index back whose value reaches - * an threshold value calculated by (counted pixel number / - * 10*threshold / 100). - * - */ - - int GetHighestLimitIndex(int threshold) const; - - /*! - * @brief Get the amount of the histogram values in an interval - * - * @param minindex Minimal index of the interval - * @param maxindex Maximal index of the interval - * - * @return Amount of the values - * - * Function calculates the amount of the histogram values - * in a given interval. - * - */ - - int GetPowerAmount(int min_index, int max_index) const; - - /*! - * @brief Get index value of the centroid point of the histogram - * - * @return Index number - * - * Function calculates the centre of area of the histogram and - * gives the index number back. - * - */ - - int GetCentroidIndex() const; - - /*! - * @brief Stretch the histogram - * - * @param mode Mode of the histogram stretching - * - * @return True if successful, otherwise false. - * - * The function selects and stretches the main power - * interval of the histogram. The following calculation - * modes are available: - * - * - s_OwnMode: The calculation of the power - * interval is selected by functions Histogram::GetHistogramLowestLimitIndex() - * and Histogram::GetHistogramHighestLimitIndex() where the - * threshold is 20, 10, 5, 2, 1 in order. The power range will - * be selected if the length is at least 52 long or the used - * threshold reaches the 1 value. - * - s_GimpMode: The minimum index of power interval is - * specified by the first fulfilled abs(percentage[i]-0.006) < - * fabs(percentage[i+1]-0.006) where the percentage[i] means - * the amount of the histogram values in the interval [0, i]. - * The maximum index is specified by the first fulfilled - * (from the end of the histogram) abs(percentage[i]-0.006) < - * fabs(percentage[i-1]-0.006) where the percentage[i] means - * the amount of the histogram values in the interval [i, 255]. - * - * The stretch operation is rejected if the power interval is - * less than 10 or less than 20 and the percentage[min_index, max_index] - * / percentage[0, 255] < 0.2. - * - */ - - bool Stretch(StretchType mode); - - /// Histogram spectrum - int HistogramData[256]; -}; - - -/** - * MEHistogramTransform - * @brief The class provides histogram operations - */ -class MEHistogramTransform +namespace bgslibrary { -public: - /// Types of histogram processing - typedef enum { - p_Min = 0, /*!< Minimum value */ - p_SeparateChannels = p_Min, /*!< Separate channels */ - p_Average, /*!< Average */ - p_Max = p_Average /*!< Maximum value */ - } ProcessingType; - - /// Types of histogram transformations - typedef enum { - t_Min = 0, /*!< Minimum value */ - t_Continuous = t_Min, /*!< Continuous */ - t_Discrete, /*!< Discrete */ - t_Max = t_Discrete /*!< Maximum value */ - } TransformType; - - /// Constructor of class - MEHistogramTransform(); - /// Destructor of class - ~MEHistogramTransform(); - - /*! - * @brief Histogram stretching an image - * - * @param image Source image to stretch - * - * The function stretches the histogram of the given image with - * default parameters: process the color channels separately - * and continuously. - * - */ - - void HistogramStretch(MEImage& image); - - /*! - * @brief Histogram stretching with specified parameters - * - * @param image Source image to stretch - * @param time_mode Mode of the histogram stretching - * - * The function transformations the histogram of the image. - * There is some different possibilities to make the operation: - * - * - t_Continuous: The function always stretches the - * image at each call of the method. - * - t_Discrete: A histogram is calculated at the first - * call of the function and all further images will be - * stretched by this initial histogram. - * - */ - - void HistogramStretch(MEImage& image, TransformType time_mode); - - /*! - * @brief Histogram equalization on an image - * - * @param image Source image to equalize - * - * The source image is transformed by histogram - * equalization. - * - */ - - void HistogramEqualize(MEImage& image); - - /*! - * @brief Set the process mode of the histogram transformation - * - * @param new_channel_mode New mode of processing channels - * @param new_stretch_mode New mode of histogram stretching - * - * The process mode of histogram transformation can be - * set by this method. Two process modes are available for - * processing channels: - * - * - p_SeparateChannels: The class processes the color channels - * separately. - * - p_Average: The color channels are averaged - * in the histogram operations. - * - * Two process modes are usable for histogram stretching: - * s_OwnMode and s_GimpMode. See Histogram::Stretch() - * for more details. - * - */ - - void SetStretchProcessingMode(ProcessingType new_channel_mode, MEHistogram::StretchType new_stretch_mode); - -private: - /// Type of the process of histograms - ProcessingType ChannelMode; - /// Stretch mode - MEHistogram::StretchType StretchMode; - /// Histograms for red, green and blue color channels - MEHistogram RedChannel, GreenChannel, BlueChannel; - /// Histogram for average calculation - MEHistogram AverageChannel; - /// Continuous histogram stretch is done already - bool DiscreteStretchingDone; -}; + namespace algorithms + { + namespace lbp_mrf + { + class MEImage; + + /** + * MEHistogram + * @brief The class provides basic histogram operations + */ + class MEHistogram + { + public: + + /// Types of histogram calculation + typedef enum { + h_Min = 0, /*!< Minimum value */ + h_Overwrite = h_Min, /*!< Overwrite */ + h_Add, /*!< Add */ + h_Max = h_Add /*!< Maximum value */ + } HistogramType; + + /// Types of histogram stretching + typedef enum { + s_Min = 0, /*!< Minimum value */ + s_OwnMode = s_Min, /*!< Own mode */ + s_GimpMode, /*!< Gimp mode */ + s_Max = s_GimpMode /*!< Maximum value */ + } StretchType; + + /// Constructor of class + MEHistogram(); + /// Destructor of class + ~MEHistogram(); + + /*! + * @brief Clear histogram data + * + * Clear histogram data. + * + */ + + void Clear(); + + /*! + * @brief Equality (==) operator + * + * @param histogram Histogram to be compared + * + * @return True if the two histograms are equal. + * + * Compare two histograms. + * + */ + + bool operator==(MEHistogram& histogram) const; + + /*! + * @brief Calculate the histogram of one color channel + * + * @param image Given image for the calculations + * @param channel Selected color channel for calculation (Range: 1..x) + * @param mode The mode of calculation. + * + * The method calculates the histograms of a color channel. + * There is two different type of the function: + * + * - h_Add: Add the data to the existing histogram. + * - h_Overwrite: Clear the histogram data before the + * calculation. + * + */ + + void Calculate(MEImage& image, int channel, HistogramType mode); + + /*! + * @brief Calculate the average histogram of an image + * + * @param image Given image for the calculations + * @param mode Histogram calculation mode + * + * The method calculates the average histogram of an image. + * + */ + + void Calculate(MEImage& image, HistogramType mode = h_Overwrite); + + /*! + * @brief Calculate the histogram of an image region + * + * @param image Given image for the calculations + * @param channel Selected color channel for calculation (Range: 1..x) + * @param x0 x0 coordinate of the region + * @param y0 y0 coordinate of the region + * @param x1 x1 coordinate of the region + * @param y1 y1 coordinate of the region + * + * The method calculates the average histogram of an image region + * (x0,y0)-(x1,y1). + * + */ + + void Calculate(MEImage& image, int channel, int x0, int y0, int x1, int y1); + + /*! + * @brief Get the index of maximum value of the histogram + * + * @return Index number + * + * Function gives an index value back where is the highest + * peak of the histogram. + * + */ + + int GetPeakIndex() const; + + /*! + * @brief Get the lowest histogram index with an threshold value + * + * @param threshold Specified threshold (in percent: 0..100 %) + * + * @return Index number + * + * Function gives the lowest index back whose value reaches + * an threshold value calculated by (counted pixel number / + * 10*threshold / 100). + * + */ + + int GetLowestLimitIndex(int threshold) const; + + /*! + * @brief Get the highest histogram index with an threshold value + * + * @param threshold Specified threshold (in percent: 0..100 %) + * + * @return Index number + * + * Function gives the highest index back whose value reaches + * an threshold value calculated by (counted pixel number / + * 10*threshold / 100). + * + */ + + int GetHighestLimitIndex(int threshold) const; + + /*! + * @brief Get the amount of the histogram values in an interval + * + * @param minindex Minimal index of the interval + * @param maxindex Maximal index of the interval + * + * @return Amount of the values + * + * Function calculates the amount of the histogram values + * in a given interval. + * + */ + + int GetPowerAmount(int min_index, int max_index) const; + + /*! + * @brief Get index value of the centroid point of the histogram + * + * @return Index number + * + * Function calculates the centre of area of the histogram and + * gives the index number back. + * + */ + + int GetCentroidIndex() const; + + /*! + * @brief Stretch the histogram + * + * @param mode Mode of the histogram stretching + * + * @return True if successful, otherwise false. + * + * The function selects and stretches the main power + * interval of the histogram. The following calculation + * modes are available: + * + * - s_OwnMode: The calculation of the power + * interval is selected by functions Histogram::GetHistogramLowestLimitIndex() + * and Histogram::GetHistogramHighestLimitIndex() where the + * threshold is 20, 10, 5, 2, 1 in order. The power range will + * be selected if the length is at least 52 long or the used + * threshold reaches the 1 value. + * - s_GimpMode: The minimum index of power interval is + * specified by the first fulfilled abs(percentage[i]-0.006) < + * fabs(percentage[i+1]-0.006) where the percentage[i] means + * the amount of the histogram values in the interval [0, i]. + * The maximum index is specified by the first fulfilled + * (from the end of the histogram) abs(percentage[i]-0.006) < + * fabs(percentage[i-1]-0.006) where the percentage[i] means + * the amount of the histogram values in the interval [i, 255]. + * + * The stretch operation is rejected if the power interval is + * less than 10 or less than 20 and the percentage[min_index, max_index] + * / percentage[0, 255] < 0.2. + * + */ + + bool Stretch(StretchType mode); + + /// Histogram spectrum + int HistogramData[256]; + }; + + + /** + * MEHistogramTransform + * @brief The class provides histogram operations + */ + class MEHistogramTransform + { + public: + /// Types of histogram processing + typedef enum { + p_Min = 0, /*!< Minimum value */ + p_SeparateChannels = p_Min, /*!< Separate channels */ + p_Average, /*!< Average */ + p_Max = p_Average /*!< Maximum value */ + } ProcessingType; + + /// Types of histogram transformations + typedef enum { + t_Min = 0, /*!< Minimum value */ + t_Continuous = t_Min, /*!< Continuous */ + t_Discrete, /*!< Discrete */ + t_Max = t_Discrete /*!< Maximum value */ + } TransformType; + + /// Constructor of class + MEHistogramTransform(); + /// Destructor of class + ~MEHistogramTransform(); + + /*! + * @brief Histogram stretching an image + * + * @param image Source image to stretch + * + * The function stretches the histogram of the given image with + * default parameters: process the color channels separately + * and continuously. + * + */ + + void HistogramStretch(MEImage& image); + + /*! + * @brief Histogram stretching with specified parameters + * + * @param image Source image to stretch + * @param time_mode Mode of the histogram stretching + * + * The function transformations the histogram of the image. + * There is some different possibilities to make the operation: + * + * - t_Continuous: The function always stretches the + * image at each call of the method. + * - t_Discrete: A histogram is calculated at the first + * call of the function and all further images will be + * stretched by this initial histogram. + * + */ + + void HistogramStretch(MEImage& image, TransformType time_mode); + + /*! + * @brief Histogram equalization on an image + * + * @param image Source image to equalize + * + * The source image is transformed by histogram + * equalization. + * + */ + + void HistogramEqualize(MEImage& image); + + /*! + * @brief Set the process mode of the histogram transformation + * + * @param new_channel_mode New mode of processing channels + * @param new_stretch_mode New mode of histogram stretching + * + * The process mode of histogram transformation can be + * set by this method. Two process modes are available for + * processing channels: + * + * - p_SeparateChannels: The class processes the color channels + * separately. + * - p_Average: The color channels are averaged + * in the histogram operations. + * + * Two process modes are usable for histogram stretching: + * s_OwnMode and s_GimpMode. See Histogram::Stretch() + * for more details. + * + */ + + void SetStretchProcessingMode(ProcessingType new_channel_mode, MEHistogram::StretchType new_stretch_mode); + + private: + /// Type of the process of histograms + ProcessingType ChannelMode; + /// Stretch mode + MEHistogram::StretchType StretchMode; + /// Histograms for red, green and blue color channels + MEHistogram RedChannel, GreenChannel, BlueChannel; + /// Histogram for average calculation + MEHistogram AverageChannel; + /// Continuous histogram stretch is done already + bool DiscreteStretchingDone; + }; + } + } +} diff --git a/src/algorithms/LBP_MRF/MEImage.cpp b/src/algorithms/LBP_MRF/MEImage.cpp index 71d66debc89083bcf2b49c6433301250774d862d..0928871d3d4bb6be0bd4a2df26ca9c42949f447c 100644 --- a/src/algorithms/LBP_MRF/MEImage.cpp +++ b/src/algorithms/LBP_MRF/MEImage.cpp @@ -1,1391 +1,1403 @@ #include <opencv2/opencv.hpp> +// opencv legacy includes #include <opencv2/imgproc/types_c.h> #include <opencv2/imgproc/imgproc_c.h> #include "MEImage.hpp" #include "MEDefs.hpp" +//using namespace bgslibrary::algorithms::lbp_mrf; + #define ME_CAST_TO_IPLIMAGE(image_ptr) ((IplImage*)image_ptr) #define ME_RELEASE_IPLIMAGE(image_ptr) \ cvReleaseImage((IplImage**)&image_ptr); \ image_ptr = NULL; - // RGB to YUV transform -const float RGBtoYUVMatrix[3][3] = -{ { 0.299, 0.587, 0.114 }, - { -0.147, -0.289, 0.436 }, - { 0.615, -0.515, -0.100 } }; - -// RGB to YIQ transform -const float RGBtoYIQMatrix[3][3] = -{ { 0.299, 0.587, 0.114 }, - { 0.596, -0.274, -0.322 }, - { 0.212, -0.523, 0.311 } }; - -MEImage::MEImage(int width, int height, int layers) : cvImg(NULL) -{ - _Init(width, height, layers); -} - -MEImage::MEImage(const MEImage& other) : cvImg(NULL) +namespace bgslibrary { - _Copy(other); -} - -MEImage::~MEImage() -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)) + namespace algorithms { - ME_RELEASE_IPLIMAGE(cvImg); - } -} - -void MEImage::Clear() -{ - cvSetZero(ME_CAST_TO_IPLIMAGE(cvImg)); -} - -void MEImage::GetLayer(MEImage& new_layer, int layer_number) const -{ - int LayerNumber = layer_number; + namespace lbp_mrf + { + // RGB to YUV transform + const float RGBtoYUVMatrix[3][3] = + { { 0.299, 0.587, 0.114 }, + { -0.147, -0.289, 0.436 }, + { 0.615, -0.515, -0.100 } }; + + // RGB to YIQ transform + const float RGBtoYIQMatrix[3][3] = + { { 0.299, 0.587, 0.114 }, + { 0.596, -0.274, -0.322 }, + { 0.212, -0.523, 0.311 } }; + + MEImage::MEImage(int width, int height, int layers) : cvImg(NULL) + { + _Init(width, height, layers); + } - if ((new_layer.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width) || - (new_layer.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) || - (new_layer.GetLayers() != 1)) - { - new_layer.Realloc(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height, 1); - } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels < LayerNumber) - { - printf("The given layer number is too large (%d > %d)\n", - LayerNumber, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + MEImage::MEImage(const MEImage& other) : cvImg(NULL) + { + _Copy(other); + } - LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; - } - if (LayerNumber <= 0) - { - printf("The given layer number is too small (%d <= 0)\n", LayerNumber); - LayerNumber = 1; - } + MEImage::~MEImage() + { + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + } - cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), LayerNumber); - cvCopy(ME_CAST_TO_IPLIMAGE(cvImg), (IplImage*)new_layer.GetIplImage(), NULL); - cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), 0); -} + void MEImage::Clear() + { + cvSetZero(ME_CAST_TO_IPLIMAGE(cvImg)); + } -void MEImage::SetLayer(MEImage& layer, int layer_number) -{ - int LayerNumber = layer_number; + void MEImage::GetLayer(MEImage& new_layer, int layer_number) const + { + int LayerNumber = layer_number; - if (layer.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - layer.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) - { - printf("The dimensions of the layer and " - "destination image is different (%dx%d <> %dx%d)\n", - layer.GetWidth(), layer.GetHeight(), ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height); - return; - } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels < LayerNumber) - { - printf("The given layer number is too large (%d > %d)\n", - LayerNumber, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; - } - if (LayerNumber <= 0) - { - printf("The given layer number is too small (%d <= 0)\n", LayerNumber); - LayerNumber = 1; - } - if (layer.GetLayers() != 1) - { - printf("The layer image has not one color channel (1 != %d)\n", - layer.GetLayers()); - return; - } - cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), LayerNumber); - cvCopy((IplImage*)layer.GetIplImage(), ME_CAST_TO_IPLIMAGE(cvImg), NULL); - cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), 0); -} + if ((new_layer.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width) || + (new_layer.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) || + (new_layer.GetLayers() != 1)) + { + new_layer.Realloc(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height, 1); + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels < LayerNumber) + { + printf("The given layer number is too large (%d > %d)\n", + LayerNumber, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -void MEImage::CopyImageData(unsigned char* data) -{ - memcpy(ME_CAST_TO_IPLIMAGE(cvImg)->imageData, data, ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -} + LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + } + if (LayerNumber <= 0) + { + printf("The given layer number is too small (%d <= 0)\n", LayerNumber); + LayerNumber = 1; + } -void* MEImage::GetIplImage() const -{ - return (void*)ME_CAST_TO_IPLIMAGE(cvImg); -} + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), LayerNumber); + cvCopy(ME_CAST_TO_IPLIMAGE(cvImg), (IplImage*)new_layer.GetIplImage(), NULL); + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), 0); + } -void MEImage::SetIplImage(void* image) -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)) - { - ME_RELEASE_IPLIMAGE(cvImg); - } - cvImg = cvCloneImage((IplImage*)image); - // Correct the origin of the image - if (ME_CAST_TO_IPLIMAGE(cvImg)->origin == 1) - { - MirrorVertical(); - ME_CAST_TO_IPLIMAGE(cvImg)->origin = 0; - } -} + void MEImage::SetLayer(MEImage& layer, int layer_number) + { + int LayerNumber = layer_number; -bool MEImage::operator==(const MEImage& image) -{ - return Equal(image); -} + if (layer.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + layer.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) + { + printf("The dimensions of the layer and " + "destination image is different (%dx%d <> %dx%d)\n", + layer.GetWidth(), layer.GetHeight(), ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height); + return; + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels < LayerNumber) + { + printf("The given layer number is too large (%d > %d)\n", + LayerNumber, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + } + if (LayerNumber <= 0) + { + printf("The given layer number is too small (%d <= 0)\n", LayerNumber); + LayerNumber = 1; + } + if (layer.GetLayers() != 1) + { + printf("The layer image has not one color channel (1 != %d)\n", + layer.GetLayers()); + return; + } + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), LayerNumber); + cvCopy((IplImage*)layer.GetIplImage(), ME_CAST_TO_IPLIMAGE(cvImg), NULL); + cvSetImageCOI(ME_CAST_TO_IPLIMAGE(cvImg), 0); + } -bool MEImage::operator!=(const MEImage& image) -{ - return !operator==(image); -} + void MEImage::CopyImageData(unsigned char* data) + { + memcpy(ME_CAST_TO_IPLIMAGE(cvImg)->imageData, data, ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + } -MEImage& MEImage::operator=(const MEImage& other_image) -{ - if (&other_image == this) - return *this; + void* MEImage::GetIplImage() const + { + return (void*)ME_CAST_TO_IPLIMAGE(cvImg); + } - _Copy(other_image); - return *this; -} + void MEImage::SetIplImage(void* image) + { + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + cvImg = cvCloneImage((IplImage*)image); + // Correct the origin of the image + if (ME_CAST_TO_IPLIMAGE(cvImg)->origin == 1) + { + MirrorVertical(); + ME_CAST_TO_IPLIMAGE(cvImg)->origin = 0; + } + } -int MEImage::GetWidth() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : 0; -} + bool MEImage::operator==(const MEImage& image) + { + return Equal(image); + } -int MEImage::GetRowWidth() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->widthStep : 0; -} + bool MEImage::operator!=(const MEImage& image) + { + return !operator==(image); + } -int MEImage::GetHeight() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : 0; -} + MEImage& MEImage::operator=(const MEImage& other_image) + { + if (&other_image == this) + return *this; -int MEImage::GetLayers() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->nChannels : 0; -} + _Copy(other_image); + return *this; + } -int MEImage::GetPixelDataNumber() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? GetWidth()*GetHeight()*GetLayers() : 0; -} + int MEImage::GetWidth() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : 0; + } -unsigned char* MEImage::GetImageData() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData : NULL; -} + int MEImage::GetRowWidth() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->widthStep : 0; + } -void MEImage::SetData(unsigned char* image_data, int width, int height, int channels) -{ - _Init(width, height, channels); + int MEImage::GetHeight() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : 0; + } - for (int y = height - 1; y >= 0; --y) - { - int Start = GetRowWidth()*y; - int Start2 = width*channels*y; + int MEImage::GetLayers() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->nChannels : 0; + } - memcpy(&ME_CAST_TO_IPLIMAGE(cvImg)->imageData[Start], &image_data[Start2], width*channels); - } -} + int MEImage::GetPixelDataNumber() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? GetWidth()*GetHeight()*GetLayers() : 0; + } -float MEImage::GetRatio() const -{ - return ME_CAST_TO_IPLIMAGE(cvImg) ? (float)ME_CAST_TO_IPLIMAGE(cvImg)->height / (float)ME_CAST_TO_IPLIMAGE(cvImg)->width : 0.0; -} + unsigned char* MEImage::GetImageData() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData : NULL; + } -void MEImage::Realloc(int width, int height) -{ - Realloc(width, height, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -} + void MEImage::SetData(unsigned char* image_data, int width, int height, int channels) + { + _Init(width, height, channels); -void MEImage::Realloc(int width, int height, int layers) -{ - _Init(width, height, layers); -} + for (int y = height - 1; y >= 0; --y) + { + int Start = GetRowWidth()*y; + int Start2 = width*channels*y; -void MEImage::Resize(int new_width, int new_height) -{ - if (new_height < 1) - { - printf("Invalid new height: %d < 1\n", new_height); - return; - } - if (new_width < 1) - { - printf("Invalid new width: %d < 1\n", new_width); - return; - } - IplImage* TempImg = cvCreateImage(cvSize(new_width, new_height), 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + memcpy(&ME_CAST_TO_IPLIMAGE(cvImg)->imageData[Start], &image_data[Start2], width*channels); + } + } - cvResize(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_INTER_NN); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + float MEImage::GetRatio() const + { + return ME_CAST_TO_IPLIMAGE(cvImg) ? (float)ME_CAST_TO_IPLIMAGE(cvImg)->height / (float)ME_CAST_TO_IPLIMAGE(cvImg)->width : 0.0; + } + void MEImage::Realloc(int width, int height) + { + Realloc(width, height, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + } -void MEImage::ResizeScaleX(int new_width) -{ - if (new_width < 1) - { - printf("Invalid new width: %d < 1\n", new_width); - return; - } - Resize(new_width, (int)((float)new_width*GetRatio())); -} + void MEImage::Realloc(int width, int height, int layers) + { + _Init(width, height, layers); + } -void MEImage::ResizeScaleY(int new_height) -{ - if (new_height < 1) - { - printf("Invalid new height: %d < 1\n", new_height); - return; - } - Resize((int)((float)new_height * 1 / GetRatio()), new_height); -} + void MEImage::Resize(int new_width, int new_height) + { + if (new_height < 1) + { + printf("Invalid new height: %d < 1\n", new_height); + return; + } + if (new_width < 1) + { + printf("Invalid new width: %d < 1\n", new_width); + return; + } + IplImage* TempImg = cvCreateImage(cvSize(new_width, new_height), 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -void MEImage::MirrorHorizontal() -{ - cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 1); -} + cvResize(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_INTER_NN); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } -void MEImage::MirrorVertical() -{ - cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 0); -} -void MEImage::Crop(int x1, int y1, int x2, int y2) -{ - int NewX1 = x1; - int NewY1 = y1; - int NewX2 = x2; - int NewY2 = y2; - - NewX1 = (NewX1 < 0) ? 0 : NewX1; - NewX1 = (NewX1 > ME_CAST_TO_IPLIMAGE(cvImg)->width) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : NewX1; - NewY1 = (NewY1 < 0) ? 0 : NewY1; - NewY1 = (NewY1 > ME_CAST_TO_IPLIMAGE(cvImg)->height) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : NewY1; - - NewX2 = (NewX2 < 0) ? 0 : NewX2; - NewX2 = (NewX2 > ME_CAST_TO_IPLIMAGE(cvImg)->width) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : NewX2; - NewY2 = (NewY2 < 0) ? 0 : NewY2; - NewY2 = (NewY2 > ME_CAST_TO_IPLIMAGE(cvImg)->height) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : NewY2; - - if ((NewX2 - NewX1) <= 0) - { - printf("Invalid new width: %d <= 0\n", NewX2 - NewX1); - return; - } - if ((NewY2 - NewY1) <= 0) - { - printf("Invalid new height: %d <= 0\n", NewY2 - NewY1); - return; - } - IplImage* TempImg = cvCreateImage(cvSize(NewX2 - NewX1, NewY2 - NewY1), 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + void MEImage::ResizeScaleX(int new_width) + { + if (new_width < 1) + { + printf("Invalid new width: %d < 1\n", new_width); + return; + } + Resize(new_width, (int)((float)new_width*GetRatio())); + } - cvSetImageROI(ME_CAST_TO_IPLIMAGE(cvImg), cvRect(NewX1, NewY1, NewX2 - NewX1, NewY2 - NewY1)); - cvCopy(ME_CAST_TO_IPLIMAGE(cvImg), TempImg); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + void MEImage::ResizeScaleY(int new_height) + { + if (new_height < 1) + { + printf("Invalid new height: %d < 1\n", new_height); + return; + } + Resize((int)((float)new_height * 1 / GetRatio()), new_height); + } -void MEImage::CopyImageInside(int x, int y, MEImage& source_image) -{ - int NewX = x; - int NewY = y; - int PasteLengthX = source_image.GetWidth(); - int PasteLengthY = source_image.GetHeight(); + void MEImage::MirrorHorizontal() + { + cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 1); + } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != source_image.GetLayers()) - { - if (source_image.GetLayers() == 1 && ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 3) - { - source_image.ConvertGrayscaleToRGB(); - } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1 && source_image.GetLayers() == 3) - { - source_image.ConvertToGrayscale(g_OpenCV); - } - } - if (NewX < 0) - NewX = 0; - if (NewX > ME_CAST_TO_IPLIMAGE(cvImg)->width) - NewX = ME_CAST_TO_IPLIMAGE(cvImg)->width; - if (NewY < 0) - NewY = 0; - if (NewY > ME_CAST_TO_IPLIMAGE(cvImg)->height) - NewY = ME_CAST_TO_IPLIMAGE(cvImg)->height; - if (NewX + PasteLengthX > ME_CAST_TO_IPLIMAGE(cvImg)->width) - PasteLengthX = ME_CAST_TO_IPLIMAGE(cvImg)->width - NewX; - if (NewY + PasteLengthY > ME_CAST_TO_IPLIMAGE(cvImg)->height) - PasteLengthY = ME_CAST_TO_IPLIMAGE(cvImg)->height - NewY; - - if (PasteLengthX != source_image.GetWidth() || - PasteLengthY != source_image.GetHeight()) - { - source_image.Resize(PasteLengthX, PasteLengthY); - } - cvSetImageROI(ME_CAST_TO_IPLIMAGE(cvImg), cvRect(NewX, NewY, PasteLengthX, PasteLengthY)); - cvCopy((IplImage*)source_image.GetIplImage(), ME_CAST_TO_IPLIMAGE(cvImg)); - cvResetImageROI(ME_CAST_TO_IPLIMAGE(cvImg)); -} + void MEImage::MirrorVertical() + { + cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 0); + } -void MEImage::Erode(int iterations) -{ - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), - 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + void MEImage::Crop(int x1, int y1, int x2, int y2) + { + int NewX1 = x1; + int NewY1 = y1; + int NewX2 = x2; + int NewY2 = y2; + + NewX1 = (NewX1 < 0) ? 0 : NewX1; + NewX1 = (NewX1 > ME_CAST_TO_IPLIMAGE(cvImg)->width) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : NewX1; + NewY1 = (NewY1 < 0) ? 0 : NewY1; + NewY1 = (NewY1 > ME_CAST_TO_IPLIMAGE(cvImg)->height) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : NewY1; + + NewX2 = (NewX2 < 0) ? 0 : NewX2; + NewX2 = (NewX2 > ME_CAST_TO_IPLIMAGE(cvImg)->width) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : NewX2; + NewY2 = (NewY2 < 0) ? 0 : NewY2; + NewY2 = (NewY2 > ME_CAST_TO_IPLIMAGE(cvImg)->height) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : NewY2; + + if ((NewX2 - NewX1) <= 0) + { + printf("Invalid new width: %d <= 0\n", NewX2 - NewX1); + return; + } + if ((NewY2 - NewY1) <= 0) + { + printf("Invalid new height: %d <= 0\n", NewY2 - NewY1); + return; + } + IplImage* TempImg = cvCreateImage(cvSize(NewX2 - NewX1, NewY2 - NewY1), 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvErode(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + cvSetImageROI(ME_CAST_TO_IPLIMAGE(cvImg), cvRect(NewX1, NewY1, NewX2 - NewX1, NewY2 - NewY1)); + cvCopy(ME_CAST_TO_IPLIMAGE(cvImg), TempImg); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } -void MEImage::Dilate(int iterations) -{ - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), - 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + void MEImage::CopyImageInside(int x, int y, MEImage& source_image) + { + int NewX = x; + int NewY = y; + int PasteLengthX = source_image.GetWidth(); + int PasteLengthY = source_image.GetHeight(); - cvDilate(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != source_image.GetLayers()) + { + if (source_image.GetLayers() == 1 && ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 3) + { + source_image.ConvertGrayscaleToRGB(); + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1 && source_image.GetLayers() == 3) + { + source_image.ConvertToGrayscale(g_OpenCV); + } + } + if (NewX < 0) + NewX = 0; + if (NewX > ME_CAST_TO_IPLIMAGE(cvImg)->width) + NewX = ME_CAST_TO_IPLIMAGE(cvImg)->width; + if (NewY < 0) + NewY = 0; + if (NewY > ME_CAST_TO_IPLIMAGE(cvImg)->height) + NewY = ME_CAST_TO_IPLIMAGE(cvImg)->height; + if (NewX + PasteLengthX > ME_CAST_TO_IPLIMAGE(cvImg)->width) + PasteLengthX = ME_CAST_TO_IPLIMAGE(cvImg)->width - NewX; + if (NewY + PasteLengthY > ME_CAST_TO_IPLIMAGE(cvImg)->height) + PasteLengthY = ME_CAST_TO_IPLIMAGE(cvImg)->height - NewY; + + if (PasteLengthX != source_image.GetWidth() || + PasteLengthY != source_image.GetHeight()) + { + source_image.Resize(PasteLengthX, PasteLengthY); + } + cvSetImageROI(ME_CAST_TO_IPLIMAGE(cvImg), cvRect(NewX, NewY, PasteLengthX, PasteLengthY)); + cvCopy((IplImage*)source_image.GetIplImage(), ME_CAST_TO_IPLIMAGE(cvImg)); + cvResetImageROI(ME_CAST_TO_IPLIMAGE(cvImg)); + } -void MEImage::Smooth() -{ - SmoothAdvanced(s_Median, 3); -} + void MEImage::Erode(int iterations) + { + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), + 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -void MEImage::SmoothAdvanced(SmoothType filtermode, int filtersize) -{ - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvErode(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } - switch (filtermode) - { - case s_Blur: - cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_BLUR, filtersize, filtersize, 0); - break; - case s_Median: - cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_MEDIAN, filtersize, 0, 0); - break; - case s_Gaussian: - cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GAUSSIAN, filtersize, filtersize, 0); - break; - default: - cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_MEDIAN, filtersize, 0, 0); - break; - } - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + void MEImage::Dilate(int iterations) + { + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), + 8, ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -void MEImage::Canny() -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) - { - ConvertToGrayscale(g_OpenCV); - } + cvDilate(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCanny(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 800, 1100, 5); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + void MEImage::Smooth() + { + SmoothAdvanced(s_Median, 3); + } -void MEImage::Laplace() -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) - { - ConvertToGrayscale(g_OpenCV); - } - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), - IPL_DEPTH_16S, 1); - cvLaplace(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 3); - cvConvertScale(TempImg, ME_CAST_TO_IPLIMAGE(cvImg), 1, 0); - ME_RELEASE_IPLIMAGE(cvImg); -} + void MEImage::SmoothAdvanced(SmoothType filtermode, int filtersize) + { + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -void MEImage::Quantize(int levels) -{ - if (levels <= 0) - { - printf("Level number is too small (%d <= 0)\n", levels); - return; - } - if (levels > 256) - { - printf("Level number is too large (%d > 256)\n", levels); - return; - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + switch (filtermode) + { + case s_Blur: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_BLUR, filtersize, filtersize, 0); + break; + case s_Median: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_MEDIAN, filtersize, 0, 0); + break; + case s_Gaussian: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GAUSSIAN, filtersize, filtersize, 0); + break; + default: + cvSmooth(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_MEDIAN, filtersize, 0, 0); + break; + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; --i) - { - ImageData[i] = ImageData[i] / (256 / levels)*(256 / levels); - } -} + void MEImage::Canny() + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } -void MEImage::Threshold(int threshold_limit) -{ - if (threshold_limit < 0) - { - printf("Threshold number is too small (%d <= 0)\n", threshold_limit); - return; - } - if (threshold_limit > 255) - { - printf("Threshold number is too large (%d > 255)\n", threshold_limit); - return; - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCanny(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 800, 1100, 5); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; --i) - { - if (ImageData[i] < threshold_limit) - { - ImageData[i] = 0; - } - } -} + void MEImage::Laplace() + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) + { + ConvertToGrayscale(g_OpenCV); + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), + IPL_DEPTH_16S, 1); + cvLaplace(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 3); + cvConvertScale(TempImg, ME_CAST_TO_IPLIMAGE(cvImg), 1, 0); + ME_RELEASE_IPLIMAGE(cvImg); + } -void MEImage::AdaptiveThreshold() -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) - { - ConvertToGrayscale(g_OpenCV); - } - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvAdaptiveThreshold(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 25, - CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, -7); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + void MEImage::Quantize(int levels) + { + if (levels <= 0) + { + printf("Level number is too small (%d <= 0)\n", levels); + return; + } + if (levels > 256) + { + printf("Level number is too large (%d > 256)\n", levels); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; -void MEImage::ThresholdByMask(MEImage& mask_image) -{ - if (mask_image.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - mask_image.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) - { - printf("Image properties are different\n"); - return; - } - if (mask_image.GetLayers() != 3 && ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 3) - { - mask_image.ConvertGrayscaleToRGB(); - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* MaskImageData = mask_image.GetImageData(); + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; --i) + { + ImageData[i] = ImageData[i] / (256 / levels)*(256 / levels); + } + } - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; --i) - { - if (MaskImageData[i] == 0) - { - ImageData[i] = 0; - } - } -} + void MEImage::Threshold(int threshold_limit) + { + if (threshold_limit < 0) + { + printf("Threshold number is too small (%d <= 0)\n", threshold_limit); + return; + } + if (threshold_limit > 255) + { + printf("Threshold number is too large (%d > 255)\n", threshold_limit); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; -void MEImage::ColorSpace(ColorSpaceConvertType mode) -{ - IplImage* TempImg = NULL; - unsigned char* ImageData = NULL; - int WidthStep = 0; - int RowStart = 0; + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; --i) + { + if (ImageData[i] < threshold_limit) + { + ImageData[i] = 0; + } + } + } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1) - { - printf("No sense to convert: source image is greyscale\n"); - ConvertGrayscaleToRGB(); - } - switch (mode) - { - case csc_RGBtoXYZCIED65: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2XYZ); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_XYZCIED65toRGB: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_XYZ2RGB); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_RGBtoHSV: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2HSV); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_HSVtoRGB: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, - ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_HSV2RGB); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_RGBtoHLS: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2HLS); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_HLStoRGB: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_HLS2RGB); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_RGBtoCIELab: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2Lab); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_CIELabtoRGB: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_Lab2RGB); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_RGBtoCIELuv: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2Luv); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_CIELuvtoRGB: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_Luv2RGB); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case csc_RGBtoYUV: - ComputeColorSpace(csc_RGBtoYUV); - break; - - case csc_RGBtoYIQ: - ComputeColorSpace(csc_RGBtoYIQ); - break; - - case csc_RGBtorgI: - ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - RowStart = 0; - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = (ME_CAST_TO_IPLIMAGE(cvImg)->width - 1) * 3; x >= 0; x -= 3) + void MEImage::AdaptiveThreshold() { - int r = 0; - int g = 0; - int I = 0; - - I = (int)ImageData[RowStart + x] + (int)ImageData[RowStart + x + 1] + (int)ImageData[RowStart + x + 2]; - r = (int)((float)ImageData[RowStart + x] / I * 255); - g = (int)((float)ImageData[RowStart + x + 1] / I * 255); - ImageData[RowStart + x] = (unsigned char)r; - ImageData[RowStart + x + 1] = (unsigned char)g; - ImageData[RowStart + x + 2] = (unsigned char)(I / 3); + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) + { + ConvertToGrayscale(g_OpenCV); + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvAdaptiveThreshold(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 25, + CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, -7); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; } - RowStart += WidthStep; - } - break; - default: - break; - } -} + void MEImage::ThresholdByMask(MEImage& mask_image) + { + if (mask_image.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + mask_image.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height) + { + printf("Image properties are different\n"); + return; + } + if (mask_image.GetLayers() != 3 && ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 3) + { + mask_image.ConvertGrayscaleToRGB(); + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* MaskImageData = mask_image.GetImageData(); -void MEImage::ConvertToGrayscale(GrayscaleType grayscale_mode) -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1) - { - printf("Image is already grayscale\n"); - return; - } - IplImage* TempImg = NULL; - unsigned char* ImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* ImageData = NULL; + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; --i) + { + if (MaskImageData[i] == 0) + { + ImageData[i] = 0; + } + } + } - switch (grayscale_mode) - { - case g_Average: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); - ImageData = (unsigned char*)TempImg->imageData; + void MEImage::ColorSpace(ColorSpaceConvertType mode) + { + IplImage* TempImg = NULL; + unsigned char* ImageData = NULL; + int WidthStep = 0; + int RowStart = 0; - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 3; i >= 0; i -= 3) - { - ImageData[i / 3] = (ImgData[i] + ImgData[i + 1] + ImgData[i + 2]) / 3; - } - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - case g_OpenCV: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2GRAY); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; - - default: - break; - } -} + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1) + { + printf("No sense to convert: source image is greyscale\n"); + ConvertGrayscaleToRGB(); + } + switch (mode) + { + case csc_RGBtoXYZCIED65: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2XYZ); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_XYZCIED65toRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_XYZ2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoHSV: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2HSV); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_HSVtoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, + ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_HSV2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoHLS: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2HLS); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_HLStoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_HLS2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoCIELab: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2Lab); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_CIELabtoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_Lab2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoCIELuv: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2Luv); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_CIELuvtoRGB: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_Luv2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case csc_RGBtoYUV: + ComputeColorSpace(csc_RGBtoYUV); + break; + + case csc_RGBtoYIQ: + ComputeColorSpace(csc_RGBtoYIQ); + break; + + case csc_RGBtorgI: + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + RowStart = 0; + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = (ME_CAST_TO_IPLIMAGE(cvImg)->width - 1) * 3; x >= 0; x -= 3) + { + int r = 0; + int g = 0; + int I = 0; + + I = (int)ImageData[RowStart + x] + (int)ImageData[RowStart + x + 1] + (int)ImageData[RowStart + x + 2]; + r = (int)((float)ImageData[RowStart + x] / I * 255); + g = (int)((float)ImageData[RowStart + x + 1] / I * 255); + ImageData[RowStart + x] = (unsigned char)r; + ImageData[RowStart + x + 1] = (unsigned char)g; + ImageData[RowStart + x + 2] = (unsigned char)(I / 3); + } + RowStart += WidthStep; + } + break; -void MEImage::ConvertGrayscaleToRGB() -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) - { - return; - } - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 3); + default: + break; + } + } - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GRAY2RGB); - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + void MEImage::ConvertToGrayscale(GrayscaleType grayscale_mode) + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels == 1) + { + printf("Image is already grayscale\n"); + return; + } + IplImage* TempImg = NULL; + unsigned char* ImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* ImageData = NULL; -void MEImage::ConvertBGRToRGB() -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 3) - { - return; - } - cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), ME_CAST_TO_IPLIMAGE(cvImg), CV_RGB2BGR); -} + switch (grayscale_mode) + { + case g_Average: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); + ImageData = (unsigned char*)TempImg->imageData; -void MEImage::LBP(LBPType mode) -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) - { - ConvertToGrayscale(g_OpenCV); - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); - unsigned char* TempImgData = (unsigned char*)TempImg->imageData; - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int WidthStep_2 = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep * 2; - - cvSetZero(TempImg); - switch (mode) - { - case lbp_Normal: - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*(ME_CAST_TO_IPLIMAGE(cvImg)->height - 2) - 1; i >= ME_CAST_TO_IPLIMAGE(cvImg)->widthStep + 1; --i) - { - TempImgData[i] = - (ImageData[i] <= ImageData[i - ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1]) + - ((ImageData[i] <= ImageData[i - ME_CAST_TO_IPLIMAGE(cvImg)->widthStep]) * 2) + - ((ImageData[i] <= ImageData[i - ME_CAST_TO_IPLIMAGE(cvImg)->widthStep + 1]) * 4) + - ((ImageData[i] <= ImageData[i - 1]) * 8) + - ((ImageData[i] <= ImageData[i + 1]) * 16) + - ((ImageData[i] <= ImageData[i + ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1]) * 32) + - ((ImageData[i] <= ImageData[i + ME_CAST_TO_IPLIMAGE(cvImg)->widthStep]) * 64) + - ((ImageData[i] <= ImageData[i + ME_CAST_TO_IPLIMAGE(cvImg)->widthStep + 1]) * 128); - } - break; + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 3; i >= 0; i -= 3) + { + ImageData[i / 3] = (ImgData[i] + ImgData[i + 1] + ImgData[i + 2]) / 3; + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + case g_OpenCV: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_RGB2GRAY); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + default: + break; + } + } - case lbp_Special: - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*(ME_CAST_TO_IPLIMAGE(cvImg)->height - 3) - 2; i >= ME_CAST_TO_IPLIMAGE(cvImg)->widthStep * 2 + 2; --i) - { - int CenterPixel = (ImageData[i + 1] + ImageData[i - 1] + - ImageData[i - WidthStep] + ImageData[i + WidthStep]) / 4; - TempImgData[i] = ((CenterPixel <= (ImageData[i - (WidthStep_2)-2] + - ImageData[i - (WidthStep_2)-1] + - ImageData[i - WidthStep - 2] + - ImageData[i - WidthStep - 1]) / 4)) + - ((CenterPixel <= (ImageData[i - WidthStep] + - ImageData[i - (WidthStep_2)]) / 2) * 2) + - ((CenterPixel <= ((ImageData[i - (WidthStep_2)+2] + - ImageData[i - (WidthStep_2)+1] + - ImageData[i - WidthStep + 2] + - ImageData[i - WidthStep + 1]) / 4)) * 4) + - ((CenterPixel <= (ImageData[i - 1] + - ImageData[i - 2]) / 2) * 8) + - ((CenterPixel <= (ImageData[i + 1] + - ImageData[i + 2]) / 2) * 16) + - ((CenterPixel <= ((ImageData[i + (WidthStep_2)-2] + - ImageData[i + (WidthStep_2)-1] + - ImageData[i + WidthStep - 2] + - ImageData[i + WidthStep - 1]) / 4)) * 32) + - ((CenterPixel <= (ImageData[i + WidthStep] + - ImageData[i - WidthStep_2]) / 2) * 64) + - ((CenterPixel <= ((ImageData[i + (WidthStep_2)+2] + - ImageData[i + (WidthStep_2)+1] + - ImageData[i + WidthStep + 2] + - ImageData[i + WidthStep + 1]) / 4)) * 128); - } - break; + void MEImage::ConvertGrayscaleToRGB() + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 1) + { + return; + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 3); - default: - break; - } - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GRAY2RGB); + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } -void MEImage::Binarize(int threshold) -{ - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + void MEImage::ConvertBGRToRGB() + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 3) + { + return; + } + cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), ME_CAST_TO_IPLIMAGE(cvImg), CV_RGB2BGR); + } - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) - { - if (ImageData[i] >= threshold) - { - ImageData[i] = 255; - } - else { - ImageData[i] = 0; - } - } -} + void MEImage::LBP(LBPType mode) + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, 1); + unsigned char* TempImgData = (unsigned char*)TempImg->imageData; + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int WidthStep_2 = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep * 2; + + cvSetZero(TempImg); + switch (mode) + { + case lbp_Normal: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*(ME_CAST_TO_IPLIMAGE(cvImg)->height - 2) - 1; i >= ME_CAST_TO_IPLIMAGE(cvImg)->widthStep + 1; --i) + { + TempImgData[i] = + (ImageData[i] <= ImageData[i - ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1]) + + ((ImageData[i] <= ImageData[i - ME_CAST_TO_IPLIMAGE(cvImg)->widthStep]) * 2) + + ((ImageData[i] <= ImageData[i - ME_CAST_TO_IPLIMAGE(cvImg)->widthStep + 1]) * 4) + + ((ImageData[i] <= ImageData[i - 1]) * 8) + + ((ImageData[i] <= ImageData[i + 1]) * 16) + + ((ImageData[i] <= ImageData[i + ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1]) * 32) + + ((ImageData[i] <= ImageData[i + ME_CAST_TO_IPLIMAGE(cvImg)->widthStep]) * 64) + + ((ImageData[i] <= ImageData[i + ME_CAST_TO_IPLIMAGE(cvImg)->widthStep + 1]) * 128); + } + break; -void MEImage::Subtract(MEImage& source, SubtractModeType mode) -{ - if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || - source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) - { - printf("Image properties are different.\n"); - return; - } - unsigned char* ImageData = NULL; - unsigned char* DstData = NULL; - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int RowStart = 0; + case lbp_Special: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*(ME_CAST_TO_IPLIMAGE(cvImg)->height - 3) - 2; i >= ME_CAST_TO_IPLIMAGE(cvImg)->widthStep * 2 + 2; --i) + { + int CenterPixel = (ImageData[i + 1] + ImageData[i - 1] + + ImageData[i - WidthStep] + ImageData[i + WidthStep]) / 4; + TempImgData[i] = ((CenterPixel <= (ImageData[i - (WidthStep_2)-2] + + ImageData[i - (WidthStep_2)-1] + + ImageData[i - WidthStep - 2] + + ImageData[i - WidthStep - 1]) / 4)) + + ((CenterPixel <= (ImageData[i - WidthStep] + + ImageData[i - (WidthStep_2)]) / 2) * 2) + + ((CenterPixel <= ((ImageData[i - (WidthStep_2)+2] + + ImageData[i - (WidthStep_2)+1] + + ImageData[i - WidthStep + 2] + + ImageData[i - WidthStep + 1]) / 4)) * 4) + + ((CenterPixel <= (ImageData[i - 1] + + ImageData[i - 2]) / 2) * 8) + + ((CenterPixel <= (ImageData[i + 1] + + ImageData[i + 2]) / 2) * 16) + + ((CenterPixel <= ((ImageData[i + (WidthStep_2)-2] + + ImageData[i + (WidthStep_2)-1] + + ImageData[i + WidthStep - 2] + + ImageData[i + WidthStep - 1]) / 4)) * 32) + + ((CenterPixel <= (ImageData[i + WidthStep] + + ImageData[i - WidthStep_2]) / 2) * 64) + + ((CenterPixel <= ((ImageData[i + (WidthStep_2)+2] + + ImageData[i + (WidthStep_2)+1] + + ImageData[i + WidthStep + 2] + + ImageData[i + WidthStep + 1]) / 4)) * 128); + } + break; - switch (mode) - { - case sub_Normal: - ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - DstData = source.GetImageData(); - RowStart = 0; + default: + break; + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + void MEImage::Binarize(int threshold) { - ImageData[RowStart + x] = - ImageData[RowStart + x] - DstData[RowStart + x] < 0 ? 0 : - ImageData[RowStart + x] - DstData[RowStart + x]; - } - RowStart += WidthStep; - } - break; + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - case sub_Absolut: - ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - DstData = source.GetImageData(); - RowStart = 0; + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) + { + if (ImageData[i] >= threshold) + { + ImageData[i] = 255; + } + else { + ImageData[i] = 0; + } + } + } - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + void MEImage::Subtract(MEImage& source, SubtractModeType mode) { - ImageData[RowStart + x] = ImageData[RowStart + x] - - DstData[RowStart + x] < 0 ? -ImageData[RowStart + x] + - DstData[RowStart + x] : ImageData[RowStart + x] - DstData[RowStart + x]; - } - RowStart += WidthStep; - } - break; + if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different.\n"); + return; + } + unsigned char* ImageData = NULL; + unsigned char* DstData = NULL; + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; - default: - break; - } -} + switch (mode) + { + case sub_Normal: + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + RowStart = 0; -void MEImage::Multiple(MEImage& source, MultiplicationType mode) -{ - if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || - source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) - { - printf("Image properties are different.\n"); - return; - } - float Result = 0.0; - IplImage* TempImg = NULL; - unsigned char* ImageData = NULL; - unsigned char* ImageData2 = NULL; - unsigned char* ImageData3 = NULL; - unsigned char* DstData = NULL; - - switch (mode) - { - case m_Normal: - Result = 0; - ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - DstData = source.GetImageData(); + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + ImageData[RowStart + x] = + ImageData[RowStart + x] - DstData[RowStart + x] < 0 ? 0 : + ImageData[RowStart + x] - DstData[RowStart + x]; + } + RowStart += WidthStep; + } + break; - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) - { - if ((ImageData[i] >= 128) && (DstData[i] >= 128)) - { - Result = (float)ImageData[i] / 128 * (float)DstData[i] / 128; + case sub_Absolut: + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + RowStart = 0; - if (Result >= 1) - { - ImageData[i] = 255; - } - else { - ImageData[i] = 0; + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + ImageData[RowStart + x] = ImageData[RowStart + x] - + DstData[RowStart + x] < 0 ? -ImageData[RowStart + x] + + DstData[RowStart + x] : ImageData[RowStart + x] - DstData[RowStart + x]; + } + RowStart += WidthStep; + } + break; + + default: + break; } } - else { - ImageData[i] = 0; - } - } - break; - - case m_Neighbourhood: - TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - ImageData2 = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - DstData = source.GetImageData(); - ImageData3 = (unsigned char*)TempImg->imageData; - - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width - 1; x >= 0; --x) - for (int l = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; l >= 0; --l) - { - if (((DstData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + - x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] == 255) || - (ImageData2[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + - x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] == 255)) && - (NeighbourhoodCounter(x - 2, y - 2, n_5x5) > 3) && - (source.NeighbourhoodCounter(x - 2, y - 2, n_5x5) > 3)) + + void MEImage::Multiple(MEImage& source, MultiplicationType mode) + { + if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different.\n"); + return; + } + float Result = 0.0; + IplImage* TempImg = NULL; + unsigned char* ImageData = NULL; + unsigned char* ImageData2 = NULL; + unsigned char* ImageData3 = NULL; + unsigned char* DstData = NULL; + + switch (mode) + { + case m_Normal: + Result = 0; + ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) { - ImageData3[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + - x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] = 255; + if ((ImageData[i] >= 128) && (DstData[i] >= 128)) + { + Result = (float)ImageData[i] / 128 * (float)DstData[i] / 128; + + if (Result >= 1) + { + ImageData[i] = 255; + } + else { + ImageData[i] = 0; + } + } + else { + ImageData[i] = 0; + } } + break; + + case m_Neighbourhood: + TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + ImageData2 = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + DstData = source.GetImageData(); + ImageData3 = (unsigned char*)TempImg->imageData; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width - 1; x >= 0; --x) + for (int l = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; l >= 0; --l) + { + if (((DstData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] == 255) || + (ImageData2[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] == 255)) && + (NeighbourhoodCounter(x - 2, y - 2, n_5x5) > 3) && + (source.NeighbourhoodCounter(x - 2, y - 2, n_5x5) > 3)) + { + ImageData3[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] = 255; + } + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + break; + + default: + break; } - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; - break; + } - default: - break; - } -} + void MEImage::Addition(MEImage& source, AdditionType mode) + { + if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different.\n"); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* DstData = source.GetImageData(); -void MEImage::Addition(MEImage& source, AdditionType mode) -{ - if (source.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - source.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || - source.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) - { - printf("Image properties are different.\n"); - return; - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* DstData = source.GetImageData(); + switch (mode) + { + case a_Average: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) + { + ImageData[i] = (ImageData[i] + DstData[i]) / 2; + } + break; - switch (mode) - { - case a_Average: - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) - { - ImageData[i] = (ImageData[i] + DstData[i]) / 2; - } - break; + case a_Union: + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) + { + if (DstData[i] > ImageData[i]) + { + ImageData[i] = DstData[i]; + } + } + break; - case a_Union: - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep - 1; i >= 0; --i) - { - if (DstData[i] > ImageData[i]) - { - ImageData[i] = DstData[i]; + default: + break; + } } - } - break; - default: - break; - } -} - -void MEImage::EliminateSinglePixels() -{ - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* DstData = (unsigned char*)TempImg->imageData; - int sum = 0; - int xy = 0; - int ywidth = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width - 1; x >= 0; --x) - { - xy = y*ywidth + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; - - for (int l = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; l >= 0; --l) + void MEImage::EliminateSinglePixels() { - if ((ImageData[xy + l] > 0) && (x > 0) && (y > 0) && (x < ME_CAST_TO_IPLIMAGE(cvImg)->width - 1) && (y < ME_CAST_TO_IPLIMAGE(cvImg)->height - 1)) - { - sum = (ImageData[xy - ywidth - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + - (ImageData[xy - ywidth + l] > 0) + - (ImageData[xy - ywidth + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + - (ImageData[xy - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + - (ImageData[xy + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + - (ImageData[xy + ywidth - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + - (ImageData[xy + ywidth + l] > 0) + - (ImageData[xy + ywidth + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0); - - if (sum > 3) + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* DstData = (unsigned char*)TempImg->imageData; + int sum = 0; + int xy = 0; + int ywidth = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width - 1; x >= 0; --x) { - DstData[xy + l] = 255; - } - else { - DstData[xy + l] = 0; + xy = y*ywidth + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + + for (int l = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; l >= 0; --l) + { + if ((ImageData[xy + l] > 0) && (x > 0) && (y > 0) && (x < ME_CAST_TO_IPLIMAGE(cvImg)->width - 1) && (y < ME_CAST_TO_IPLIMAGE(cvImg)->height - 1)) + { + sum = (ImageData[xy - ywidth - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + + (ImageData[xy - ywidth + l] > 0) + + (ImageData[xy - ywidth + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + + (ImageData[xy - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + + (ImageData[xy + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + + (ImageData[xy + ywidth - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0) + + (ImageData[xy + ywidth + l] > 0) + + (ImageData[xy + ywidth + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l] > 0); + + if (sum > 3) + { + DstData[xy + l] = 255; + } + else { + DstData[xy + l] = 0; + } + } + else { + DstData[xy + l] = 0; + } + } } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } + + float MEImage::DifferenceAreas(MEImage& reference, int difference) const + { + if (reference.GetWidth() != GetWidth() || + reference.GetHeight() != GetHeight() || + reference.GetLayers() != GetLayers()) + { + printf("Image dimensions or channels are different\n"); + return -1.0; } - else { - DstData[xy + l] = 0; + float PixelDiff = 0.0; + int Pixels = 0; + unsigned char* OrigImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* RefImgData = reference.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + if (abs(OrigImgData[RowStart + x] - RefImgData[RowStart + x]) > difference) + Pixels++; + } + RowStart += WidthStep; } + PixelDiff = (float)Pixels / (ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep) * 100; + return PixelDiff; } - } - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; -} - -float MEImage::DifferenceAreas(MEImage& reference, int difference) const -{ - if (reference.GetWidth() != GetWidth() || - reference.GetHeight() != GetHeight() || - reference.GetLayers() != GetLayers()) - { - printf("Image dimensions or channels are different\n"); - return -1.0; - } - float PixelDiff = 0.0; - int Pixels = 0; - unsigned char* OrigImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* RefImgData = reference.GetImageData(); - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int RowStart = 0; - - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) - { - if (abs(OrigImgData[RowStart + x] - RefImgData[RowStart + x]) > difference) - Pixels++; - } - RowStart += WidthStep; - } - PixelDiff = (float)Pixels / (ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep) * 100; - return PixelDiff; -} -int MEImage::AverageDifference(MEImage& reference) const -{ - if (reference.GetWidth() != GetWidth() || - reference.GetHeight() != GetHeight() || - reference.GetLayers() != GetLayers()) - { - printf("Image dimensions or channels are different\n"); - return -1; - } - int Difference = 0; - unsigned char* OrigImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* RefImgData = reference.GetImageData(); - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int RowStart = 0; + int MEImage::AverageDifference(MEImage& reference) const + { + if (reference.GetWidth() != GetWidth() || + reference.GetHeight() != GetHeight() || + reference.GetLayers() != GetLayers()) + { + printf("Image dimensions or channels are different\n"); + return -1; + } + int Difference = 0; + unsigned char* OrigImgData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* RefImgData = reference.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) - { - Difference += abs(OrigImgData[RowStart + x] - RefImgData[RowStart + x]); - } - RowStart += WidthStep; - } - Difference = Difference / (ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep); - return Difference; -} + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + Difference += abs(OrigImgData[RowStart + x] - RefImgData[RowStart + x]); + } + RowStart += WidthStep; + } + Difference = Difference / (ME_CAST_TO_IPLIMAGE(cvImg)->height*ME_CAST_TO_IPLIMAGE(cvImg)->widthStep); + return Difference; + } -void MEImage::Minimum(MEImage& image) -{ - if (image.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - image.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || - image.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) - { - printf("Image properties are different\n"); - return; - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* SecData = image.GetImageData(); - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int RowStart = 0; + void MEImage::Minimum(MEImage& image) + { + if (image.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + image.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + image.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different\n"); + return; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* SecData = image.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) - { - ImageData[RowStart + x] = ImageData[RowStart + x] > SecData[RowStart + x] ? - SecData[RowStart + x] : ImageData[RowStart + x]; - } - RowStart += WidthStep; - } -} + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + ImageData[RowStart + x] = ImageData[RowStart + x] > SecData[RowStart + x] ? + SecData[RowStart + x] : ImageData[RowStart + x]; + } + RowStart += WidthStep; + } + } -float MEImage::AverageBrightnessLevel() const -{ - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int RowStart = 0; - int BrightnessLevel = 0; + float MEImage::AverageBrightnessLevel() const + { + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; + int BrightnessLevel = 0; - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) - { - BrightnessLevel += (int)ImageData[RowStart + x]; - } - RowStart += WidthStep; - } - return BrightnessLevel / (GetWidth()*GetHeight()*GetLayers()); -} + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + BrightnessLevel += (int)ImageData[RowStart + x]; + } + RowStart += WidthStep; + } + return BrightnessLevel / (GetWidth()*GetHeight()*GetLayers()); + } -bool MEImage::Equal(const MEImage& reference) const -{ - return Equal(reference, 1); -} + bool MEImage::Equal(const MEImage& reference) const + { + return Equal(reference, 1); + } -bool MEImage::Equal(const MEImage& reference, int maxabsdiff) const -{ - bool Ret = true; + bool MEImage::Equal(const MEImage& reference, int maxabsdiff) const + { + bool Ret = true; - if (reference.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || - reference.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || - reference.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) - { - printf("Image properties are different\n"); - return false; - } - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* RefData = reference.GetImageData(); - int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; - int RowStart = 0; + if (reference.GetWidth() != ME_CAST_TO_IPLIMAGE(cvImg)->width || + reference.GetHeight() != ME_CAST_TO_IPLIMAGE(cvImg)->height || + reference.GetLayers() != ME_CAST_TO_IPLIMAGE(cvImg)->nChannels) + { + printf("Image properties are different\n"); + return false; + } + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* RefData = reference.GetImageData(); + int WidthStep = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep; + int RowStart = 0; - for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) - { - for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) - { - if (abs(ImageData[RowStart + x] - RefData[RowStart + x]) >= maxabsdiff) - { - Ret = false; + for (int y = ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; y >= 0; --y) + { + for (int x = ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels - 1; x >= 0; --x) + { + if (abs(ImageData[RowStart + x] - RefData[RowStart + x]) >= maxabsdiff) + { + Ret = false; + return Ret; + } + } + RowStart += WidthStep; + } return Ret; } - } - RowStart += WidthStep; - } - return Ret; -} -unsigned char MEImage::GrayscalePixel(int x, int y) const -{ - int NewX = x; - int NewY = y; + unsigned char MEImage::GrayscalePixel(int x, int y) const + { + int NewX = x; + int NewY = y; - NewX = NewX < 0 ? 0 : NewX; - NewX = NewX > ME_CAST_TO_IPLIMAGE(cvImg)->width - 1 ? ME_CAST_TO_IPLIMAGE(cvImg)->width - 1 : NewX; - NewY = NewY < 0 ? 0 : NewY; - NewY = NewY > ME_CAST_TO_IPLIMAGE(cvImg)->height - 1 ? ME_CAST_TO_IPLIMAGE(cvImg)->height - 1 : NewY; + NewX = NewX < 0 ? 0 : NewX; + NewX = NewX > ME_CAST_TO_IPLIMAGE(cvImg)->width - 1 ? ME_CAST_TO_IPLIMAGE(cvImg)->width - 1 : NewX; + NewY = NewY < 0 ? 0 : NewY; + NewY = NewY > ME_CAST_TO_IPLIMAGE(cvImg)->height - 1 ? ME_CAST_TO_IPLIMAGE(cvImg)->height - 1 : NewY; - float Sum = 0; - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + float Sum = 0; + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - for (int l = 0; l < ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; l++) - { - Sum = Sum + (int)ImageData[NewY*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + NewX*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l]; - } - Sum = Sum / ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; - return (unsigned char)(Sum); -} + for (int l = 0; l < ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; l++) + { + Sum = Sum + (int)ImageData[NewY*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + NewX*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + l]; + } + Sum = Sum / ME_CAST_TO_IPLIMAGE(cvImg)->nChannels; + return (unsigned char)(Sum); + } -int MEImage::NeighbourhoodCounter(int startx, int starty, - NeighbourhoodType neighbourhood) const -{ - int IterX = 0; - int IterY = 0; - int Counter = 0; + int MEImage::NeighbourhoodCounter(int startx, int starty, + NeighbourhoodType neighbourhood) const + { + int IterX = 0; + int IterY = 0; + int Counter = 0; - // Determine the iteration numbers - switch (neighbourhood) - { - case n_2x2: - IterX = 2; - IterY = 2; - break; - - case n_3x3: - IterX = 3; - IterY = 3; - break; - - case n_3x2: - IterX = 2; - IterY = 3; - break; - - case n_5x5: - IterX = 5; - IterY = 5; - break; - - case n_7x7: - IterX = 7; - IterY = 7; - break; - - default: - IterX = 3; - IterY = 3; - break; - } + // Determine the iteration numbers + switch (neighbourhood) + { + case n_2x2: + IterX = 2; + IterY = 2; + break; + + case n_3x3: + IterX = 3; + IterY = 3; + break; + + case n_3x2: + IterX = 2; + IterY = 3; + break; + + case n_5x5: + IterX = 5; + IterY = 5; + break; + + case n_7x7: + IterX = 7; + IterY = 7; + break; + + default: + IterX = 3; + IterY = 3; + break; + } - int NewStartX = startx; - int NewStartY = starty; + int NewStartX = startx; + int NewStartY = starty; - NewStartX = startx < 0 ? 0 : startx; - NewStartX = startx >= ME_CAST_TO_IPLIMAGE(cvImg)->width - IterX ? ME_CAST_TO_IPLIMAGE(cvImg)->width - IterX - 1 : startx; - NewStartY = starty < 0 ? 0 : starty; - NewStartY = starty >= ME_CAST_TO_IPLIMAGE(cvImg)->height - IterY ? ME_CAST_TO_IPLIMAGE(cvImg)->height - IterY - 1 : starty; + NewStartX = startx < 0 ? 0 : startx; + NewStartX = startx >= ME_CAST_TO_IPLIMAGE(cvImg)->width - IterX ? ME_CAST_TO_IPLIMAGE(cvImg)->width - IterX - 1 : startx; + NewStartY = starty < 0 ? 0 : starty; + NewStartY = starty >= ME_CAST_TO_IPLIMAGE(cvImg)->height - IterY ? ME_CAST_TO_IPLIMAGE(cvImg)->height - IterY - 1 : starty; - int Value = 0; - unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + int Value = 0; + unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - for (int x = NewStartX; x < NewStartX + IterX; x++) - for (int y = NewStartY; y < NewStartY + IterY; y++) - { - Value = ((int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels] + - (int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + 1] + - (int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + 2]) / 3; + for (int x = NewStartX; x < NewStartX + IterX; x++) + for (int y = NewStartY; y < NewStartY + IterY; y++) + { + Value = ((int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels] + + (int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + 1] + + (int)ImageData[y*ME_CAST_TO_IPLIMAGE(cvImg)->width*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + x*ME_CAST_TO_IPLIMAGE(cvImg)->nChannels + 2]) / 3; + + if (Value == 255) + { + Counter++; + } + } + return Counter; + } - if (Value == 255) + void MEImage::GradientVector(bool smooth, int x, int y, int mask_size, int& result_x, int& result_y) { - Counter++; - } - } - return Counter; -} + int Results[8]; + int DiagonalMaskSize = (int)((float)mask_size / sqrtf(2)); -void MEImage::GradientVector(bool smooth, int x, int y, int mask_size, int& result_x, int& result_y) -{ - int Results[8]; - int DiagonalMaskSize = (int)((float)mask_size / sqrtf(2)); + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } + if (smooth) + { + SmoothAdvanced(s_Gaussian, mask_size * 3 - (mask_size * 3 - 1) % 2); + } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) - { - ConvertToGrayscale(g_OpenCV); - } - if (smooth) - { - SmoothAdvanced(s_Gaussian, mask_size * 3 - (mask_size * 3 - 1) % 2); - } + Results[0] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x, y - mask_size); + Results[1] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + DiagonalMaskSize, y - DiagonalMaskSize); + Results[2] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + mask_size, y); + Results[3] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + DiagonalMaskSize, y + DiagonalMaskSize); + Results[4] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x, y + mask_size); + Results[5] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x - DiagonalMaskSize, y + DiagonalMaskSize); + Results[6] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x - mask_size, y); + Results[7] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + DiagonalMaskSize, y - DiagonalMaskSize); + + result_x = (DiagonalMaskSize*Results[1] + mask_size*Results[2] + + DiagonalMaskSize*Results[3] - DiagonalMaskSize*Results[5] - + mask_size*Results[6] + DiagonalMaskSize*Results[7]) / 256; + result_y = (-mask_size*Results[0] - DiagonalMaskSize*Results[1] + + DiagonalMaskSize*Results[3] + mask_size*Results[4] + + DiagonalMaskSize*Results[5] - DiagonalMaskSize*Results[7]) / 256; + } - Results[0] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x, y - mask_size); - Results[1] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + DiagonalMaskSize, y - DiagonalMaskSize); - Results[2] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + mask_size, y); - Results[3] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + DiagonalMaskSize, y + DiagonalMaskSize); - Results[4] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x, y + mask_size); - Results[5] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x - DiagonalMaskSize, y + DiagonalMaskSize); - Results[6] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x - mask_size, y); - Results[7] = (int)GrayscalePixel(x, y) - (int)GrayscalePixel(x + DiagonalMaskSize, y - DiagonalMaskSize); - - result_x = (DiagonalMaskSize*Results[1] + mask_size*Results[2] + - DiagonalMaskSize*Results[3] - DiagonalMaskSize*Results[5] - - mask_size*Results[6] + DiagonalMaskSize*Results[7]) / 256; - result_y = (-mask_size*Results[0] - DiagonalMaskSize*Results[1] + - DiagonalMaskSize*Results[3] + mask_size*Results[4] + - DiagonalMaskSize*Results[5] - DiagonalMaskSize*Results[7]) / 256; -} + void MEImage::GradientVisualize(int vector_x, int vector_y) + { + if (vector_x <= 0) + { + printf("vectorx: wrong parameter (%d <= 0)\n", vector_x); + return; + } + if (vector_y <= 0) + { + printf("vectory: wrong parameter (%d <= 0)\n", vector_y); + return; + } + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) + { + ConvertToGrayscale(g_OpenCV); + } -void MEImage::GradientVisualize(int vector_x, int vector_y) -{ - if (vector_x <= 0) - { - printf("vectorx: wrong parameter (%d <= 0)\n", vector_x); - return; - } - if (vector_y <= 0) - { - printf("vectory: wrong parameter (%d <= 0)\n", vector_y); - return; - } - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels > 1) - { - ConvertToGrayscale(g_OpenCV); - } + int masksize = (ME_CAST_TO_IPLIMAGE(cvImg)->width < ME_CAST_TO_IPLIMAGE(cvImg)->height) ? + ME_CAST_TO_IPLIMAGE(cvImg)->width / (vector_x + 1) : + ME_CAST_TO_IPLIMAGE(cvImg)->height / (vector_y + 1); - int masksize = (ME_CAST_TO_IPLIMAGE(cvImg)->width < ME_CAST_TO_IPLIMAGE(cvImg)->height) ? - ME_CAST_TO_IPLIMAGE(cvImg)->width / (vector_x + 1) : - ME_CAST_TO_IPLIMAGE(cvImg)->height / (vector_y + 1); + SmoothAdvanced(s_Gaussian, masksize * 2 - 1); + for (int i = 1; i < vector_x; i++) + for (int i1 = 1; i1 < vector_y; i1++) + { + int Resultx = 0, Resulty = 0; + int x = (int)(((float)ME_CAST_TO_IPLIMAGE(cvImg)->width*i / (vector_x))); + int y = (int)(((float)ME_CAST_TO_IPLIMAGE(cvImg)->height*i1 / (vector_y))); - SmoothAdvanced(s_Gaussian, masksize * 2 - 1); - for (int i = 1; i < vector_x; i++) - for (int i1 = 1; i1 < vector_y; i1++) - { - int Resultx = 0, Resulty = 0; - int x = (int)(((float)ME_CAST_TO_IPLIMAGE(cvImg)->width*i / (vector_x))); - int y = (int)(((float)ME_CAST_TO_IPLIMAGE(cvImg)->height*i1 / (vector_y))); + GradientVector(false, x, y, (int)(0.707*masksize), Resultx, Resulty); - GradientVector(false, x, y, (int)(0.707*masksize), Resultx, Resulty); + CvPoint Point1; + CvPoint Point2; - CvPoint Point1; - CvPoint Point2; + Point1.x = x - Resultx / 2; + Point1.y = y - Resulty / 2; + Point2.x = x + Resultx / 2; + Point2.y = y + Resulty / 2; + cvLine(ME_CAST_TO_IPLIMAGE(cvImg), Point1, Point2, CV_RGB(255, 255, 255), 1, 8); + } + } - Point1.x = x - Resultx / 2; - Point1.y = y - Resulty / 2; - Point2.x = x + Resultx / 2; - Point2.y = y + Resulty / 2; - cvLine(ME_CAST_TO_IPLIMAGE(cvImg), Point1, Point2, CV_RGB(255, 255, 255), 1, 8); - } -} + bool MEImage::_Copy(const MEImage& other_image) + { + if (&other_image == this) + return true; -bool MEImage::_Copy(const MEImage& other_image) -{ - if (&other_image == this) - return true; + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + cvImg = cvCloneImage((IplImage*)other_image.GetIplImage()); + return true; + } - if (ME_CAST_TO_IPLIMAGE(cvImg)) - { - ME_RELEASE_IPLIMAGE(cvImg); - } - cvImg = cvCloneImage((IplImage*)other_image.GetIplImage()); - return true; -} + void MEImage::_Init(int width, int height, int layers) + { + if (width < 1) + { + printf("Given width for the new image is too small (%d <= 0)\n", width); + return; + } + if (height < 1) + { + printf("Given height for the new image is (%d <= 0)\n", height); + return; + } + if ((layers != 1) && (layers != 3)) + { + printf("Only one or three (%d != 1 or 3) layer allowed\n", layers); + return; + } -void MEImage::_Init(int width, int height, int layers) -{ - if (width < 1) - { - printf("Given width for the new image is too small (%d <= 0)\n", width); - return; - } - if (height < 1) - { - printf("Given height for the new image is (%d <= 0)\n", height); - return; - } - if ((layers != 1) && (layers != 3)) - { - printf("Only one or three (%d != 1 or 3) layer allowed\n", layers); - return; - } + if (ME_CAST_TO_IPLIMAGE(cvImg)) + { + ME_RELEASE_IPLIMAGE(cvImg); + } + cvImg = cvCreateImage(cvSize(width, height), 8, layers); + } - if (ME_CAST_TO_IPLIMAGE(cvImg)) - { - ME_RELEASE_IPLIMAGE(cvImg); - } - cvImg = cvCreateImage(cvSize(width, height), 8, layers); -} + void MEImage::ComputeColorSpace(ColorSpaceConvertType mode) + { + if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 3) + { + printf("Image has to have three color channels (%d != 3)\n", ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + return; + } + IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, + ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); -void MEImage::ComputeColorSpace(ColorSpaceConvertType mode) -{ - if (ME_CAST_TO_IPLIMAGE(cvImg)->nChannels != 3) - { - printf("Image has to have three color channels (%d != 3)\n", ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); - return; - } - IplImage* TempImg = cvCreateImage(cvSize(ME_CAST_TO_IPLIMAGE(cvImg)->width, ME_CAST_TO_IPLIMAGE(cvImg)->height), 8, - ME_CAST_TO_IPLIMAGE(cvImg)->nChannels); + for (int i = 0; i < 3; i++) + for (int i1 = 0; i1 < 3; i1++) + { + if (mode == csc_RGBtoYUV) + TransformMatrix[i][i1] = RGBtoYUVMatrix[i][i1]; + if (mode == csc_RGBtoYIQ) + TransformMatrix[i][i1] = RGBtoYIQMatrix[i][i1]; + } + float x = 0.0; + float y = 0.0; + float z = 0.0; + float xmin = 0.0; + float xmax = 0.0; + float ymin = 0.0; + float ymax = 0.0; + float zmin = 0.0; + float zmax = 0.0; + + if (mode == csc_RGBtoYUV) + { + xmin = 0.0; + xmax = 255.0; + ymin = -111.18; + ymax = 111.18; + zmin = -156.825; + zmax = 156.825; + } + if (mode == csc_RGBtoYIQ) + { + xmin = 0.0; + xmax = 255.0; + ymin = -151.98; + ymax = 151.98; + zmin = -133.365; + zmax = 133.365; + } + unsigned char* SrcData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; + unsigned char* DstData = (unsigned char*)TempImg->imageData; - for (int i = 0; i < 3; i++) - for (int i1 = 0; i1 < 3; i1++) - { - if (mode == csc_RGBtoYUV) - TransformMatrix[i][i1] = RGBtoYUVMatrix[i][i1]; - if (mode == csc_RGBtoYIQ) - TransformMatrix[i][i1] = RGBtoYIQMatrix[i][i1]; + for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; i -= 3) + { + x = (float)SrcData[i] * TransformMatrix[0][0] + + (float)SrcData[i + 1] * TransformMatrix[0][1] + + (float)SrcData[i + 2] * TransformMatrix[0][2]; + y = (float)SrcData[i] * TransformMatrix[1][0] + + (float)SrcData[i + 1] * TransformMatrix[1][1] + + (float)SrcData[i + 2] * TransformMatrix[1][2]; + z = (float)SrcData[i] * TransformMatrix[2][0] + + (float)SrcData[i + 1] * TransformMatrix[2][1] + + (float)SrcData[i + 2] * TransformMatrix[2][2]; + + x = xmax - xmin != 0.0 ? 255.0 : (x - xmin) / (xmax - xmin)*255.0; + y = ymax - ymin != 0.0 ? 255.0 : (y - xmin) / (ymax - ymin)*255.0; + z = zmax - zmin != 0.0 ? 255.0 : (z - xmin) / (zmax - zmin)*255.0; + + DstData[i] = (unsigned char)MEBound(0, (int)x, 255); + DstData[i + 1] = (unsigned char)MEBound(0, (int)y, 255); + DstData[i + 2] = (unsigned char)MEBound(0, (int)z, 255); + } + ME_RELEASE_IPLIMAGE(cvImg); + cvImg = TempImg; + } } - float x = 0.0; - float y = 0.0; - float z = 0.0; - float xmin = 0.0; - float xmax = 0.0; - float ymin = 0.0; - float ymax = 0.0; - float zmin = 0.0; - float zmax = 0.0; - - if (mode == csc_RGBtoYUV) - { - xmin = 0.0; - xmax = 255.0; - ymin = -111.18; - ymax = 111.18; - zmin = -156.825; - zmax = 156.825; - } - if (mode == csc_RGBtoYIQ) - { - xmin = 0.0; - xmax = 255.0; - ymin = -151.98; - ymax = 151.98; - zmin = -133.365; - zmax = 133.365; - } - unsigned char* SrcData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData; - unsigned char* DstData = (unsigned char*)TempImg->imageData; - - for (int i = ME_CAST_TO_IPLIMAGE(cvImg)->widthStep*ME_CAST_TO_IPLIMAGE(cvImg)->height - 1; i >= 0; i -= 3) - { - x = (float)SrcData[i] * TransformMatrix[0][0] + - (float)SrcData[i + 1] * TransformMatrix[0][1] + - (float)SrcData[i + 2] * TransformMatrix[0][2]; - y = (float)SrcData[i] * TransformMatrix[1][0] + - (float)SrcData[i + 1] * TransformMatrix[1][1] + - (float)SrcData[i + 2] * TransformMatrix[1][2]; - z = (float)SrcData[i] * TransformMatrix[2][0] + - (float)SrcData[i + 1] * TransformMatrix[2][1] + - (float)SrcData[i + 2] * TransformMatrix[2][2]; - - x = xmax - xmin != 0.0 ? 255.0 : (x - xmin) / (xmax - xmin)*255.0; - y = ymax - ymin != 0.0 ? 255.0 : (y - xmin) / (ymax - ymin)*255.0; - z = zmax - zmin != 0.0 ? 255.0 : (z - xmin) / (zmax - zmin)*255.0; - - DstData[i] = (unsigned char)MEBound(0, (int)x, 255); - DstData[i + 1] = (unsigned char)MEBound(0, (int)y, 255); - DstData[i + 2] = (unsigned char)MEBound(0, (int)z, 255); } - ME_RELEASE_IPLIMAGE(cvImg); - cvImg = TempImg; } diff --git a/src/algorithms/LBP_MRF/MEImage.hpp b/src/algorithms/LBP_MRF/MEImage.hpp index 7094912f368ab0d0a85ab88872e80867b71aa2de..b9ba1fccf8b855629d4098ddb6f7ccdc9f4e410b 100644 --- a/src/algorithms/LBP_MRF/MEImage.hpp +++ b/src/algorithms/LBP_MRF/MEImage.hpp @@ -1,968 +1,977 @@ #pragma once -/** - * MEImage - * @brief Basic image functions - */ -class MEImage +namespace bgslibrary { -public: - /// Types of LBP operator - typedef enum { - lbp_Min = 0, /*!< Minimum value */ - lbp_Normal = lbp_Min, /*!< Normal LBP pattern */ - lbp_Special, /*!< Special LBP pattern */ - lbp_Max = lbp_Special /*!< Maximum value */ - } LBPType; - - /// Types of image subtraction - typedef enum { - sub_Min = 0, /*!< Minimum value */ - sub_Normal = sub_Min, /*!< Normal */ - sub_Absolut, /*!< Absolut */ - sub_Max = sub_Absolut /*!< Maximum value */ - } SubtractModeType; - - /// Types of image addition - typedef enum { - a_Min = 0, /*!< Minimum value */ - a_Average = a_Min, /*!< Average */ - a_Union, /*!< Union */ - a_Max = a_Union /*!< Maximum value */ - } AdditionType; - - /// Types of image multiplication - typedef enum { - m_Min = 0, /*!< Minimum value */ - m_Normal = m_Min, /*!< Normal */ - m_Neighbourhood, /*!< Neighbourhood */ - m_Max = m_Neighbourhood /*!< Maximum value */ - } MultiplicationType; - - /// Types of grayscale conversation - typedef enum { - g_Min = 0, /*!< Minimum value */ - g_Average = g_Min, /*!< Average */ - g_OpenCV, /*!< OpenCV */ - g_Max = g_OpenCV /*!< Maximum value */ - } GrayscaleType; - - /// Types of pixel neighbourhoods - typedef enum { - n_Min = 0, /*!< Minimum value */ - n_2x2 = n_Min, /*!< 2x2 */ - n_3x2, /*!< 3x2 */ - n_3x3, /*!< 3x3 */ - n_5x5, /*!< 5x5 */ - n_7x7, /*!< 7x7 */ - n_Max = n_7x7 /*!< Maximum value */ - } NeighbourhoodType; - - /// Types of special pixels - typedef enum { - p_Min = 0, /*!< Minimum value */ - p_Minimum = p_Min, /*!< Minimum */ - p_Maximum, /*!< Maximum */ - p_Counter, /*!< Counter */ - p_Max = p_Counter /*!< Maximum value */ - } PixelType; - - /// Types of smooth operation - typedef enum { - s_Min = 0, /*!< Minimum value */ - s_Blur = s_Min, /*!< Blur */ - s_Gaussian, /*!< Gaussian */ - s_Median, /*!< Medium */ - s_Max = s_Median /*!< Maximum value */ - } SmoothType; - - /// Types of color space conversions - typedef enum { - csc_Min = 0, /*!< Minimum value */ - csc_RGBtoXYZCIED65 = csc_Min, /*!< RGB to XYZCIED65 */ - csc_XYZCIED65toRGB, /*!< XYZCIED65 to RGB */ - csc_RGBtoHSV, /*!< RGB to HSV */ - csc_HSVtoRGB, /*!< HSV to RGB */ - csc_RGBtoHLS, /*!< RGB to HLS */ - csc_HLStoRGB, /*!< HLS to RGB */ - csc_RGBtoCIELab, /*!< RGB to CIELab */ - csc_CIELabtoRGB, /*!< CIELab to RGB */ - csc_RGBtoCIELuv, /*!< RGB to CIELuv */ - csc_CIELuvtoRGB, /*!< CIELuv to RGB */ - csc_RGBtoYUV, /*!< RGB to YUV */ - csc_RGBtoYIQ, /*!< RGB to YIQ */ - csc_RGBtorgI, /*!< RGB to rgI */ - csc_Max = csc_RGBtorgI /*!< Maximum value */ - } ColorSpaceConvertType; - - /*! - * @brief Class constructor - * - * @param width Image width - * @param height Image height - * @param layers Layers - * - * Class constructor with the possibility to specify the image width, - * height and the layers. The default options are 16x16x1. - * - */ - - MEImage(int width = 16, int height = 16, int layers = 1); - - /*! - * @brief Class constructor - * - * @param other Other image - * - * Class constructor with the possibility to specify the image width, - * height and the layers. The default options are 16x16x1. - * - */ - - MEImage(const MEImage& other); - /// Destructor of class - ~MEImage(); - - /* - ------------------------------------------------------------------- - Basic functions - ------------------------------------------------------------------- - */ - - /*! - * @brief Clear image - * - * This function clears image by filling all image data with zero - * value. - * - */ - - void Clear(); - - /*! - * @brief Get an color layer of image - * - * @param new_layer new image of layer - * @param layernumber number of layer which will be copied - * - * Copy an image layer (R, G or B) to @a new_layer image. @a new_layer has to - * have only one color layer (greyscale). If @a new_layer is not - * greyscale or it has got different width or height like source image - * than function reallocates it with appropriate features before - * copying image data. - * - */ - - void GetLayer(MEImage& new_layer, int layernumber) const; - - /*! - * @brief Copy a new color layer to image - * - * @param new_layer image data of new color layer - * @param layernumber number of layer where image data will copy - * - * Copy a new image layer from @a new_layer image. @a new_layer has to - * have only one color layer (greyscale). If @a new_layer is not - * greyscale or it has got different width or height like source image - * than function halts with an error message. - * - */ - - void SetLayer(MEImage& new_layer, int layernumber); - - /*! - * @brief Copy image data to a pointer - * - * @param data pointer where image data will be copied - * - * Function in order to acquire image data to an external - * (unsigned char*) pointer. - * - */ - - void CopyImageData(unsigned char* data); - - /*! - * @brief Get a pointer to the internal IplImage - * - * @return Pointer to the IplImage - * - * This function returns the internal IplImage of the class. The - * image data can not be modified. - * - */ - - void* GetIplImage() const; - - /*! - * @brief Set the internal IplImage - * - * @param image Pointer to the IplImage - * - * This function sets the internal IplImage of the class. - * - */ - - void SetIplImage(void* image); - - /*! - * @brief Handle operator == for MEImage - * - * @param image image to check - * - * @return true if the images are equal otherwise false. - * - * The operator checks the equality of two images. - * - */ - - bool operator==(const MEImage& image); - - /*! - * @brief Handle operator != for MEImage - * - * @param image image to check - * - * @return true if the images are not equal otherwise false. - * - * The operator checks the non-equality of two images. - * - */ - - bool operator!=(const MEImage& image); - - /*! - * @brief Handle operator = for MEImage - * - * @param other_image image to copy operation - * - * @return Reference to the actual instance. - * - * Copy image data to @a other_image image. Function calls only - * _Copy() directly. - * - */ - - MEImage& operator=(const MEImage& other_image); - - /*! - * @brief Get the width of the image - * - * @return Width of the image - * - * Get the width of the image. - * - */ - - int GetWidth() const; - - /*! - * @brief Get the height of the image - * - * @return Height of the image - * - * Get the height of the image. - */ - - int GetHeight() const; - - /*! - * @brief Get the length of a pixel row of the image - * - * @return Length of a pixel row - * - * Get the row width of the image. - * - */ - - int GetRowWidth() const; - - /*! - * @brief Get the number of color layers of the image - * - * @return Number of color layer of the image - * - * Get the number of color layer of the image. - * - */ - - int GetLayers() const; - - /*! - * @brief Get the number of the image pixel data - * - * @return Number of the image pixel data - * - * Get the number of the image pixel data. - * - */ - - int GetPixelDataNumber() const; - - /*! - * @brief Get the image data - * - * @return Pointer to the image data - * - * Get a pointer to the image. - * - */ - - unsigned char* GetImageData() const; - - /*! - * @brief Set the image data - * - * @param image_data New image data - * @param width New image width - * @param height New image height - * @param channels New image color channels - * - * Get a pointer to the image. - * - */ - - void SetData(unsigned char* image_data, int width, int height, int channels); - - /*! - * @brief Get ratio of image width and height - * - * @return float ratio of image dimensions - * - * Function calculates ratio of image width and height with - * following equation: ratio = height / width. - */ - - float GetRatio() const; - - /* - ------------------------------------------------------------------- - Basic image manipulation - ------------------------------------------------------------------- - */ - - /*! - * @brief Reallocate image data - * - * @param width New width of the image - * @param height New height of the image - * - * Image data will be reallocated with new dimensions @a width - * and @a height. Number of color channels is not changed. - * - */ - - void Realloc(int width, int height); - - /*! - * @brief Reallocate image data - * - * @param width New width of the image - * @param height New height of the image - * @param layers Number of color channels of the image - * - * Image data will be reallocated with new dimensions @a width, - * @a height and new number of color channels @a layers. - * - */ - - void Realloc(int width, int height, int layers); - - /*! - * @brief Resize image - * - * @param newwidth new width of image - * @param newheight new height of image - * - * Resize image to @a newwidth width and @a newheight - * height dimensions. - * - */ - - void Resize(int newwidth, int newheight); - - /*! - * @brief Resize image with new width - * - * @param newwidth new width of image - * - * Image is resized with only new width information therefore - * fit to original ratio. - * - */ - - void ResizeScaleX(int newwidth); - - /*! - * @brief Resize image with new height - * - * @param newheight new height of image - * - * Image is resized with only new height information therefore - * fit to original ratio. - * - */ - - void ResizeScaleY(int newheight); - - /*! - * @brief Reverse image in horizontal direction - * - * Function makes a mirror transformation on image in horizontal - * direction. - * - */ - - void MirrorHorizontal(); - - /*! - * @brief Reverse image in vertical direction - * - * Function makes a mirror transformation on image in vertical - * direction. - * - */ - - void MirrorVertical(); - - /*! - * @brief Crop image - * - * @param x1, y1 coordinates of top-left point of rectangle - * @param x2, y2 coordinates of bottom-right point of rectangle - * - * Crop the image in a smaller piece whose dimensions are - * specified as a rectangle. Top-left and bottom-right - * coordinates of rectangle are (x1, y1) and (x2, y2) wherefrom - * comes that the width of the new image is x2-x1 and height is - * y2-y1. - * - */ - - void Crop(int x1, int y1, int x2, int y2); - - /*! - * @brief Copy all image data from an other picture - * - * @param x0 x coordinate to paste the new image data - * @param y0 y coordinate to paste the new image data - * @param source_image source image - * - * Function copies all image data from @a source_image - * to the given coordinate (x0,y0). - * - */ - - void CopyImageInside(int x0, int y0, MEImage& source_image); - - /* - ------------------------------------------------------------------- - Image processing functions - ------------------------------------------------------------------- - */ - - /*! - * @brief Erode function - * - * @param iterations iterations of erode method - * - * Method makes an erode filter on an image @a iterations - * times with standard 3x3 matrix size. - * - */ - - void Erode(int iterations); - - /*! - * @brief Dilate function - * - * @param iterations iterations of dilate method - * - * Method makes an dilate filter on an image - * @a iterations times with standard 3x3 matrix size. - * - */ - - void Dilate(int iterations); - - /*! - * @brief Smooth function - * - * Method smooths with median filter and standard 3x3 matrix size. - * (Median filter works fine and fast.) - * - */ - - void Smooth(); - - /*! - * @brief Smooth function with defined parameters - * - * @param filtermode type of smooth method - * @param filtersize the size of the convolution matrix - * - * Method smooths with median filter and the given matrix - * size (@a filtersize x @a filtersize). There are more - * types of smooth function (@a filtermode): - * - * - s_Blur: Blur filter. - * - s_Gaussian: Gaussian filter. - * - s_Median: Median filter. - * - */ - - void SmoothAdvanced(SmoothType filtermode, int filtersize); - - /*! - * @brief Canny function - * - * Canny operator is usable for edge detection. Function makes - * this operation with standard 3x3 matrix - * size. Canny has two threshold value which are set to zero - * in this function by default. - * - */ - - void Canny(); - - /*! - * @brief Laplace function - * - * Laplace operator is usable for edge detection like Canny. - * This function makes a laplace filter with - * standard 3x3 matrix size. After calculating destination image will - * be converted from 16 bit back to 8 bit. - * - */ - - void Laplace(); - - /*! - * @brief Image quantisation - * - * @param levels level of quantisation - * - * Quantize an image with @a levels level. It means by 16 - * level color range 0-255 quantizes to 0-15, by 4 level to 0-63 etc. - * - */ - - void Quantize(int levels); - - /*! - * @brief Threshold a picture - * - * @param threshold_limit limit for threshold - * - * Threshold an image with @a threshold_limit limit. Value range - * of @a threshold_limit is between 0-255. E.g. by value 160 functions - * will eliminate all color values under 160 with black color - * (color value zero). - * - */ - - void Threshold(int threshold_limit); - - /*! - * @brief Adaptive threshold function - * - * This function does adaptive threshold function. - * - */ - - void AdaptiveThreshold(); - - /*! - * @brief Threshold a picture by a mask image - * - * @param mask_image mask image for thresholding - * - * Threshold an image with a mask image @a mask_image. - * - */ - - void ThresholdByMask(MEImage& mask_image); - - /*! - * @brief Convert an image into a new color space - * - * @param transformation Definition of color transformation - * - * This function converts an image from a specified color space - * to an other. - * Current supported conversions (@a transformation): - * - csc_RGBtoXYZCIED65: RGB to XYZ (D65 reference light), - * - csc_XYZCIED65toRGB: XYZ to RGB (D65 reference light), - * - csc_RGBtoHSV: RGB to HSV, - * - csc_HSVtoRGB: HSV to RGB, - * - csc_RGBtoHLS: RGB to HSV, - * - csc_HLStoRGB: HSV to RGB, - * - csc_RGBtoCIELab: RGB to CIELab, - * - csc_CIELabtoRGB: CIELuv to RGB, - * - csc_RGBtoCIELuv: RGB to CIELuv, - * - csc_CIELuvtoRGB: CIELuv to RGB, - * - csc_RGBtoYUV: RGB to YUV color space, - * - csc_RGBtoYIQ: RGB to YIQ color space. - * - */ - - void ColorSpace(ColorSpaceConvertType transformation); - - /*! - * @brief Convert an image to grayscale - * - * @param grayscale_mode mode of grayscale conversation - * - * The function converts the image to grayscale version - * (one color channel after the conversion). There is four - * different ways to convert the image to grayscale what we - * can define with @a grayscale_mode: - * - * - g_Average: It computes the average grayscale - * values of the pixels with arithmetical average. - * - g_OpenCV: It computes the average grayscale - * values by help of the values of the Y channel. - * - */ - - void ConvertToGrayscale(GrayscaleType grayscale_mode = g_OpenCV); - - /*! - * @brief Convert a grayscale image to RGB - * - * The function converts the grayscale image to RGB version. - * (It copies the info from a single color channel to - * three color channel.) - * - */ - - void ConvertGrayscaleToRGB(); - - /*! - * @brief Change the red and blue components of every pixels - * - * Function changes the red component with the blue of - * every pixels. (Simple conversion from RGB->BGR.) - * - */ - - void ConvertBGRToRGB(); - - /*! - * @brief Compute an LBP filter on the image - * - * @param mode The LBP operator type - * - * The function converts the image to binary version over the - * threshold value. - * - */ - - void LBP(LBPType mode = lbp_Special); - - /*! - * @brief Binarize an image - * - * @param threshold Threshold value - * - * The function converts the image to binary version over the - * threshold value. - * - */ - - void Binarize(int threshold); - - /*! - * @brief Subtract an image from the internal picture - * - * @param source Source image for subtraction - * @param mode Calculation mode of difference feature - * - * Function generates a difference image between two image: - * the internal picture of this class and @a source_image. - * The calculation mode is determined by @a mode parameter. - * Function supports the following modes: - * - * - sub_Normal: Simple subtraction between each - * correspondent pixel (per color channels). The result values - * are converted to absolute value and normalized to - * range 0-255. - * - */ - - void Subtract(MEImage& source, SubtractModeType mode); - - /*! - * @brief Multiple an image with the internal picture - * - * @param source Second source image for multiplication - * @param mode Multiplication mode - * - * Function multiples an image with the internal image of this class and - * the result is stored in the internal image. The implemented calculation - * modes: - * - * - m_Normal: It multiples the corresponding pixel values - * of the two images. The original pixel values are divided by 128 and - * multiplied together. If the result is at least 1 then the new pixel value - * is 255 otherwise 0. - * - m_Neighbourhood: It multiples all pixel values of its - * 3x3 neighbourhood separately (see the method at MULTIPLICATION_NORMAL) - * and the new pixel value is 255 if at least two pixel is active in the - * 3x3 neighbourhood otherwise 0. - * - */ - - void Multiple(MEImage& source, MultiplicationType mode); - - /*! - * @brief Addition of an image and the internal picture - * - * @param source second source image for addition method - * @param mode the declaration of the used addition mode - * - * Function makes an addition operation between an image and the internal - * image of this class and the result is stored in the internal image. - * Supported modes: - * - * - a_Average: It sums the average of the corresponding pixels - * of each pictures. - * - a_Union: It sums the union of the corresponding pixels - * of each pictures. - * - */ - - void Addition(MEImage& source, AdditionType mode); - - /*! - * @brief Eliminate the single pixels from a binary image - * - * Function eliminates such a pixels which do not have neighbour pixels with - * 255 value in a 3x3 neighbourhood. The image should be converted to binary - * version. - * - */ - - void EliminateSinglePixels(); - - /*! - * @brief Calculate an area difference feature between two images - * - * @param reference Reference image - * @param difference Difference - * - * @return The percentage of image areas representing the conditions - * - * Function calculates a similarity feature between two pictures. - * Counts the number of the pixels whose intensity difference is - * higher than @a difference. (Range: 0..100) - * - */ - - float DifferenceAreas(MEImage& reference, int difference) const; - - /*! - * @brief Calculate an average difference between two images - * - * @param reference Reference image - * - * @return Average difference of the pixels - * - * Function calculates a similarity feature between - * two images. It returns a simple sum of the absolute difference - * of each pixel in the two images and averaged by the pixel number. - * (Range: 0..255) - * - */ - - int AverageDifference(MEImage& reference) const; - - /*! - * @brief Calculate minimum of image data - * - * @param image Second image - * - * Function calculates the minimum of current and given image. - * - */ - - void Minimum(MEImage& image); - - /*! - * @brief Calculate average brightness level - * - * @return Brightness level in range 0-255. - * - * Function calculates the average brightness level of the image. - * - */ - - float AverageBrightnessLevel() const; - - /*! - * @brief Check the equalization with a reference image - * - * @param reference Reference image - * - * @return true in case of binary equalization, otherwise false. - * - * Function calculates the binary difference between - * the image and the reference image. - * - */ - - bool Equal(const MEImage& reference) const; - - /*! - * @brief Check the equalization with a reference image - * - * @param reference Reference image - * @param maxabsdiff Maximal absolute difference - * - * @return true in case of equalization, otherwise false. - * - * Function checks the difference between the image and - * the reference image. Two pixels are equal in a range of - * a maximal absolute difference. - * - */ - - bool Equal(const MEImage& reference, int maxabsdiff) const; - - /*! - * @brief Get the grayscale value of a pixel - * - * @param x X coordinate of the pixel - * @param y Y coordinate of the pixel - * - * @return grayscale value of the pixel - * - * The method gives the grayscale value of a pixel back. If - * the image has 3 color channels (e.g. RGB) then Y value of - * YIQ/YUV color space will be calculated otherwise normal - * averaged grayscale value. - * - */ - - unsigned char GrayscalePixel(int x, int y) const; - - /*! - * @brief Count the number of neighbourhood pixels with maximum intensity - * - * @param startx X coordinate of the top-left pixel - * @param starty Y coordinate of the top-left pixel - * @param neighbourhood Specific subset of pixels - * - * @return number of the pixels with maximum intensity. - * - * The method counts the number of the pixels with maximum - * intensity (255) in a specified subset of pixels. - * The grayscale values of the pixels are used in the counter - * process. The following neighbourhood forms are allowed with - * the @a neighbourhood parameter: - * - * - n_2X2: Simple 2x2 matrix. - * - n_3X3: Simple 3x3 matrix. - * - n_3x2: Simple 3x2 matrix. - * - */ - - int NeighbourhoodCounter(int startx, int starty, NeighbourhoodType neighbourhood) const; - - /*! - * @brief Calculate the gradient vector in a point - * - * @param smooth compute smooth filter - * @param x X coordinate of the point - * @param y Y coordinate of the point - * @param mask_size The mask size to calculate the gradient - * - * @param result_x X component of the calculated vector - * @param result_y Y component of the calculated vector - * - * The method calculates the gradient vector in a given point. - * The image is preprocessed with a Gauss filter to smooth the - * image content. The filter size of the Gauss filter depends on - * mask size of the gradient vector: filter size = mask size*3. - * Eight points are assigned to the initial point to compute - * a vector sum: (x, y-mask_size), (x+mask_size/√2, y-mask_size/√2), - * (x+mask_size, y), (x+mask_size/√2, y+mask_size/√2), (x, y+mask_size), - * (x-mask_size/√2, y+mask_size/√2), (x-mask_size, y), (x-mask_size/√2, y-mask_size/√2). - * The lengths of all vectors equalize with the mask size. - * After that each vector is multiplied with the gradient difference between - * its two end points. The results are summarized and normalized by - * the mask size. - * - */ - - void GradientVector(bool smooth, int x, int y, int mask_size, int& result_x, int& result_y); - - /*! - * @brief Visualize gradient vectors - * - * @param vector_x Number of points horizontally - * @param vector_y Number of points vertically - * - * This function draws a wire (@a vector_x * @a vector_y) with - * gradient vectors. - * - */ - - void GradientVisualize(int vector_x, int vector_y); - -private: - - /* - ------------------------------------------------------------------- - Internal methods - ------------------------------------------------------------------- - */ - - /*! - * @brief Copy image data - * - * @param other_image Input image with new image data - * - * @return true if it is successful, otherwise false. - * - * Copy image data from @a other_image to MEImage image data. - * - */ - - bool _Copy(const MEImage& other_image); - - /*! - * @brief Inherent initialization function - * - * @param width Width of the image - * @param height Height of the image - * @param layer Number of color channels of the image - * - * Initialization function of MEImage class which allocates - * memory to internal MEImage image and sets its properties. - * - */ - - void _Init(int width, int height, int layer); - - /*! - * @brief Compute an image to a different color space - * - * @param mode Mode of the conversion - * - * Currently, the internal function allows to use a few - * mode to convert an image between color spaces. - * Current supported conversions (@a mode): - * - RGBtoYUV: RGB to YUV color space, - * - RGBtoYIQ: RGB to YIQ color space. - * - */ - - void ComputeColorSpace(ColorSpaceConvertType mode); - -private: - /// This matrix stores the matrix of the actual color space transform - float TransformMatrix[3][3]; - /// The OpenCV image which contains the image data - void* cvImg; -}; + namespace algorithms + { + namespace lbp_mrf + { + /** + * MEImage + * @brief Basic image functions + */ + class MEImage + { + public: + /// Types of LBP operator + typedef enum { + lbp_Min = 0, /*!< Minimum value */ + lbp_Normal = lbp_Min, /*!< Normal LBP pattern */ + lbp_Special, /*!< Special LBP pattern */ + lbp_Max = lbp_Special /*!< Maximum value */ + } LBPType; + + /// Types of image subtraction + typedef enum { + sub_Min = 0, /*!< Minimum value */ + sub_Normal = sub_Min, /*!< Normal */ + sub_Absolut, /*!< Absolut */ + sub_Max = sub_Absolut /*!< Maximum value */ + } SubtractModeType; + + /// Types of image addition + typedef enum { + a_Min = 0, /*!< Minimum value */ + a_Average = a_Min, /*!< Average */ + a_Union, /*!< Union */ + a_Max = a_Union /*!< Maximum value */ + } AdditionType; + + /// Types of image multiplication + typedef enum { + m_Min = 0, /*!< Minimum value */ + m_Normal = m_Min, /*!< Normal */ + m_Neighbourhood, /*!< Neighbourhood */ + m_Max = m_Neighbourhood /*!< Maximum value */ + } MultiplicationType; + + /// Types of grayscale conversation + typedef enum { + g_Min = 0, /*!< Minimum value */ + g_Average = g_Min, /*!< Average */ + g_OpenCV, /*!< OpenCV */ + g_Max = g_OpenCV /*!< Maximum value */ + } GrayscaleType; + + /// Types of pixel neighbourhoods + typedef enum { + n_Min = 0, /*!< Minimum value */ + n_2x2 = n_Min, /*!< 2x2 */ + n_3x2, /*!< 3x2 */ + n_3x3, /*!< 3x3 */ + n_5x5, /*!< 5x5 */ + n_7x7, /*!< 7x7 */ + n_Max = n_7x7 /*!< Maximum value */ + } NeighbourhoodType; + + /// Types of special pixels + typedef enum { + p_Min = 0, /*!< Minimum value */ + p_Minimum = p_Min, /*!< Minimum */ + p_Maximum, /*!< Maximum */ + p_Counter, /*!< Counter */ + p_Max = p_Counter /*!< Maximum value */ + } PixelType; + + /// Types of smooth operation + typedef enum { + s_Min = 0, /*!< Minimum value */ + s_Blur = s_Min, /*!< Blur */ + s_Gaussian, /*!< Gaussian */ + s_Median, /*!< Medium */ + s_Max = s_Median /*!< Maximum value */ + } SmoothType; + + /// Types of color space conversions + typedef enum { + csc_Min = 0, /*!< Minimum value */ + csc_RGBtoXYZCIED65 = csc_Min, /*!< RGB to XYZCIED65 */ + csc_XYZCIED65toRGB, /*!< XYZCIED65 to RGB */ + csc_RGBtoHSV, /*!< RGB to HSV */ + csc_HSVtoRGB, /*!< HSV to RGB */ + csc_RGBtoHLS, /*!< RGB to HLS */ + csc_HLStoRGB, /*!< HLS to RGB */ + csc_RGBtoCIELab, /*!< RGB to CIELab */ + csc_CIELabtoRGB, /*!< CIELab to RGB */ + csc_RGBtoCIELuv, /*!< RGB to CIELuv */ + csc_CIELuvtoRGB, /*!< CIELuv to RGB */ + csc_RGBtoYUV, /*!< RGB to YUV */ + csc_RGBtoYIQ, /*!< RGB to YIQ */ + csc_RGBtorgI, /*!< RGB to rgI */ + csc_Max = csc_RGBtorgI /*!< Maximum value */ + } ColorSpaceConvertType; + + /*! + * @brief Class constructor + * + * @param width Image width + * @param height Image height + * @param layers Layers + * + * Class constructor with the possibility to specify the image width, + * height and the layers. The default options are 16x16x1. + * + */ + + MEImage(int width = 16, int height = 16, int layers = 1); + + /*! + * @brief Class constructor + * + * @param other Other image + * + * Class constructor with the possibility to specify the image width, + * height and the layers. The default options are 16x16x1. + * + */ + + MEImage(const MEImage& other); + /// Destructor of class + ~MEImage(); + + /* + ------------------------------------------------------------------- + Basic functions + ------------------------------------------------------------------- + */ + + /*! + * @brief Clear image + * + * This function clears image by filling all image data with zero + * value. + * + */ + + void Clear(); + + /*! + * @brief Get an color layer of image + * + * @param new_layer new image of layer + * @param layernumber number of layer which will be copied + * + * Copy an image layer (R, G or B) to @a new_layer image. @a new_layer has to + * have only one color layer (greyscale). If @a new_layer is not + * greyscale or it has got different width or height like source image + * than function reallocates it with appropriate features before + * copying image data. + * + */ + + void GetLayer(MEImage& new_layer, int layernumber) const; + + /*! + * @brief Copy a new color layer to image + * + * @param new_layer image data of new color layer + * @param layernumber number of layer where image data will copy + * + * Copy a new image layer from @a new_layer image. @a new_layer has to + * have only one color layer (greyscale). If @a new_layer is not + * greyscale or it has got different width or height like source image + * than function halts with an error message. + * + */ + + void SetLayer(MEImage& new_layer, int layernumber); + + /*! + * @brief Copy image data to a pointer + * + * @param data pointer where image data will be copied + * + * Function in order to acquire image data to an external + * (unsigned char*) pointer. + * + */ + + void CopyImageData(unsigned char* data); + + /*! + * @brief Get a pointer to the internal IplImage + * + * @return Pointer to the IplImage + * + * This function returns the internal IplImage of the class. The + * image data can not be modified. + * + */ + + void* GetIplImage() const; + + /*! + * @brief Set the internal IplImage + * + * @param image Pointer to the IplImage + * + * This function sets the internal IplImage of the class. + * + */ + + void SetIplImage(void* image); + + /*! + * @brief Handle operator == for MEImage + * + * @param image image to check + * + * @return true if the images are equal otherwise false. + * + * The operator checks the equality of two images. + * + */ + + bool operator==(const MEImage& image); + + /*! + * @brief Handle operator != for MEImage + * + * @param image image to check + * + * @return true if the images are not equal otherwise false. + * + * The operator checks the non-equality of two images. + * + */ + + bool operator!=(const MEImage& image); + + /*! + * @brief Handle operator = for MEImage + * + * @param other_image image to copy operation + * + * @return Reference to the actual instance. + * + * Copy image data to @a other_image image. Function calls only + * _Copy() directly. + * + */ + + MEImage& operator=(const MEImage& other_image); + + /*! + * @brief Get the width of the image + * + * @return Width of the image + * + * Get the width of the image. + * + */ + + int GetWidth() const; + + /*! + * @brief Get the height of the image + * + * @return Height of the image + * + * Get the height of the image. + */ + + int GetHeight() const; + + /*! + * @brief Get the length of a pixel row of the image + * + * @return Length of a pixel row + * + * Get the row width of the image. + * + */ + + int GetRowWidth() const; + + /*! + * @brief Get the number of color layers of the image + * + * @return Number of color layer of the image + * + * Get the number of color layer of the image. + * + */ + + int GetLayers() const; + + /*! + * @brief Get the number of the image pixel data + * + * @return Number of the image pixel data + * + * Get the number of the image pixel data. + * + */ + + int GetPixelDataNumber() const; + + /*! + * @brief Get the image data + * + * @return Pointer to the image data + * + * Get a pointer to the image. + * + */ + + unsigned char* GetImageData() const; + + /*! + * @brief Set the image data + * + * @param image_data New image data + * @param width New image width + * @param height New image height + * @param channels New image color channels + * + * Get a pointer to the image. + * + */ + + void SetData(unsigned char* image_data, int width, int height, int channels); + + /*! + * @brief Get ratio of image width and height + * + * @return float ratio of image dimensions + * + * Function calculates ratio of image width and height with + * following equation: ratio = height / width. + */ + + float GetRatio() const; + + /* + ------------------------------------------------------------------- + Basic image manipulation + ------------------------------------------------------------------- + */ + + /*! + * @brief Reallocate image data + * + * @param width New width of the image + * @param height New height of the image + * + * Image data will be reallocated with new dimensions @a width + * and @a height. Number of color channels is not changed. + * + */ + + void Realloc(int width, int height); + + /*! + * @brief Reallocate image data + * + * @param width New width of the image + * @param height New height of the image + * @param layers Number of color channels of the image + * + * Image data will be reallocated with new dimensions @a width, + * @a height and new number of color channels @a layers. + * + */ + + void Realloc(int width, int height, int layers); + + /*! + * @brief Resize image + * + * @param newwidth new width of image + * @param newheight new height of image + * + * Resize image to @a newwidth width and @a newheight + * height dimensions. + * + */ + + void Resize(int newwidth, int newheight); + + /*! + * @brief Resize image with new width + * + * @param newwidth new width of image + * + * Image is resized with only new width information therefore + * fit to original ratio. + * + */ + + void ResizeScaleX(int newwidth); + + /*! + * @brief Resize image with new height + * + * @param newheight new height of image + * + * Image is resized with only new height information therefore + * fit to original ratio. + * + */ + + void ResizeScaleY(int newheight); + + /*! + * @brief Reverse image in horizontal direction + * + * Function makes a mirror transformation on image in horizontal + * direction. + * + */ + + void MirrorHorizontal(); + + /*! + * @brief Reverse image in vertical direction + * + * Function makes a mirror transformation on image in vertical + * direction. + * + */ + + void MirrorVertical(); + + /*! + * @brief Crop image + * + * @param x1, y1 coordinates of top-left point of rectangle + * @param x2, y2 coordinates of bottom-right point of rectangle + * + * Crop the image in a smaller piece whose dimensions are + * specified as a rectangle. Top-left and bottom-right + * coordinates of rectangle are (x1, y1) and (x2, y2) wherefrom + * comes that the width of the new image is x2-x1 and height is + * y2-y1. + * + */ + + void Crop(int x1, int y1, int x2, int y2); + + /*! + * @brief Copy all image data from an other picture + * + * @param x0 x coordinate to paste the new image data + * @param y0 y coordinate to paste the new image data + * @param source_image source image + * + * Function copies all image data from @a source_image + * to the given coordinate (x0,y0). + * + */ + + void CopyImageInside(int x0, int y0, MEImage& source_image); + + /* + ------------------------------------------------------------------- + Image processing functions + ------------------------------------------------------------------- + */ + + /*! + * @brief Erode function + * + * @param iterations iterations of erode method + * + * Method makes an erode filter on an image @a iterations + * times with standard 3x3 matrix size. + * + */ + + void Erode(int iterations); + + /*! + * @brief Dilate function + * + * @param iterations iterations of dilate method + * + * Method makes an dilate filter on an image + * @a iterations times with standard 3x3 matrix size. + * + */ + + void Dilate(int iterations); + + /*! + * @brief Smooth function + * + * Method smooths with median filter and standard 3x3 matrix size. + * (Median filter works fine and fast.) + * + */ + + void Smooth(); + + /*! + * @brief Smooth function with defined parameters + * + * @param filtermode type of smooth method + * @param filtersize the size of the convolution matrix + * + * Method smooths with median filter and the given matrix + * size (@a filtersize x @a filtersize). There are more + * types of smooth function (@a filtermode): + * + * - s_Blur: Blur filter. + * - s_Gaussian: Gaussian filter. + * - s_Median: Median filter. + * + */ + + void SmoothAdvanced(SmoothType filtermode, int filtersize); + + /*! + * @brief Canny function + * + * Canny operator is usable for edge detection. Function makes + * this operation with standard 3x3 matrix + * size. Canny has two threshold value which are set to zero + * in this function by default. + * + */ + + void Canny(); + + /*! + * @brief Laplace function + * + * Laplace operator is usable for edge detection like Canny. + * This function makes a laplace filter with + * standard 3x3 matrix size. After calculating destination image will + * be converted from 16 bit back to 8 bit. + * + */ + + void Laplace(); + + /*! + * @brief Image quantisation + * + * @param levels level of quantisation + * + * Quantize an image with @a levels level. It means by 16 + * level color range 0-255 quantizes to 0-15, by 4 level to 0-63 etc. + * + */ + + void Quantize(int levels); + + /*! + * @brief Threshold a picture + * + * @param threshold_limit limit for threshold + * + * Threshold an image with @a threshold_limit limit. Value range + * of @a threshold_limit is between 0-255. E.g. by value 160 functions + * will eliminate all color values under 160 with black color + * (color value zero). + * + */ + + void Threshold(int threshold_limit); + + /*! + * @brief Adaptive threshold function + * + * This function does adaptive threshold function. + * + */ + + void AdaptiveThreshold(); + + /*! + * @brief Threshold a picture by a mask image + * + * @param mask_image mask image for thresholding + * + * Threshold an image with a mask image @a mask_image. + * + */ + + void ThresholdByMask(MEImage& mask_image); + + /*! + * @brief Convert an image into a new color space + * + * @param transformation Definition of color transformation + * + * This function converts an image from a specified color space + * to an other. + * Current supported conversions (@a transformation): + * - csc_RGBtoXYZCIED65: RGB to XYZ (D65 reference light), + * - csc_XYZCIED65toRGB: XYZ to RGB (D65 reference light), + * - csc_RGBtoHSV: RGB to HSV, + * - csc_HSVtoRGB: HSV to RGB, + * - csc_RGBtoHLS: RGB to HSV, + * - csc_HLStoRGB: HSV to RGB, + * - csc_RGBtoCIELab: RGB to CIELab, + * - csc_CIELabtoRGB: CIELuv to RGB, + * - csc_RGBtoCIELuv: RGB to CIELuv, + * - csc_CIELuvtoRGB: CIELuv to RGB, + * - csc_RGBtoYUV: RGB to YUV color space, + * - csc_RGBtoYIQ: RGB to YIQ color space. + * + */ + + void ColorSpace(ColorSpaceConvertType transformation); + + /*! + * @brief Convert an image to grayscale + * + * @param grayscale_mode mode of grayscale conversation + * + * The function converts the image to grayscale version + * (one color channel after the conversion). There is four + * different ways to convert the image to grayscale what we + * can define with @a grayscale_mode: + * + * - g_Average: It computes the average grayscale + * values of the pixels with arithmetical average. + * - g_OpenCV: It computes the average grayscale + * values by help of the values of the Y channel. + * + */ + + void ConvertToGrayscale(GrayscaleType grayscale_mode = g_OpenCV); + + /*! + * @brief Convert a grayscale image to RGB + * + * The function converts the grayscale image to RGB version. + * (It copies the info from a single color channel to + * three color channel.) + * + */ + + void ConvertGrayscaleToRGB(); + + /*! + * @brief Change the red and blue components of every pixels + * + * Function changes the red component with the blue of + * every pixels. (Simple conversion from RGB->BGR.) + * + */ + + void ConvertBGRToRGB(); + + /*! + * @brief Compute an LBP filter on the image + * + * @param mode The LBP operator type + * + * The function converts the image to binary version over the + * threshold value. + * + */ + + void LBP(LBPType mode = lbp_Special); + + /*! + * @brief Binarize an image + * + * @param threshold Threshold value + * + * The function converts the image to binary version over the + * threshold value. + * + */ + + void Binarize(int threshold); + + /*! + * @brief Subtract an image from the internal picture + * + * @param source Source image for subtraction + * @param mode Calculation mode of difference feature + * + * Function generates a difference image between two image: + * the internal picture of this class and @a source_image. + * The calculation mode is determined by @a mode parameter. + * Function supports the following modes: + * + * - sub_Normal: Simple subtraction between each + * correspondent pixel (per color channels). The result values + * are converted to absolute value and normalized to + * range 0-255. + * + */ + + void Subtract(MEImage& source, SubtractModeType mode); + + /*! + * @brief Multiple an image with the internal picture + * + * @param source Second source image for multiplication + * @param mode Multiplication mode + * + * Function multiples an image with the internal image of this class and + * the result is stored in the internal image. The implemented calculation + * modes: + * + * - m_Normal: It multiples the corresponding pixel values + * of the two images. The original pixel values are divided by 128 and + * multiplied together. If the result is at least 1 then the new pixel value + * is 255 otherwise 0. + * - m_Neighbourhood: It multiples all pixel values of its + * 3x3 neighbourhood separately (see the method at MULTIPLICATION_NORMAL) + * and the new pixel value is 255 if at least two pixel is active in the + * 3x3 neighbourhood otherwise 0. + * + */ + + void Multiple(MEImage& source, MultiplicationType mode); + + /*! + * @brief Addition of an image and the internal picture + * + * @param source second source image for addition method + * @param mode the declaration of the used addition mode + * + * Function makes an addition operation between an image and the internal + * image of this class and the result is stored in the internal image. + * Supported modes: + * + * - a_Average: It sums the average of the corresponding pixels + * of each pictures. + * - a_Union: It sums the union of the corresponding pixels + * of each pictures. + * + */ + + void Addition(MEImage& source, AdditionType mode); + + /*! + * @brief Eliminate the single pixels from a binary image + * + * Function eliminates such a pixels which do not have neighbour pixels with + * 255 value in a 3x3 neighbourhood. The image should be converted to binary + * version. + * + */ + + void EliminateSinglePixels(); + + /*! + * @brief Calculate an area difference feature between two images + * + * @param reference Reference image + * @param difference Difference + * + * @return The percentage of image areas representing the conditions + * + * Function calculates a similarity feature between two pictures. + * Counts the number of the pixels whose intensity difference is + * higher than @a difference. (Range: 0..100) + * + */ + + float DifferenceAreas(MEImage& reference, int difference) const; + + /*! + * @brief Calculate an average difference between two images + * + * @param reference Reference image + * + * @return Average difference of the pixels + * + * Function calculates a similarity feature between + * two images. It returns a simple sum of the absolute difference + * of each pixel in the two images and averaged by the pixel number. + * (Range: 0..255) + * + */ + + int AverageDifference(MEImage& reference) const; + + /*! + * @brief Calculate minimum of image data + * + * @param image Second image + * + * Function calculates the minimum of current and given image. + * + */ + + void Minimum(MEImage& image); + + /*! + * @brief Calculate average brightness level + * + * @return Brightness level in range 0-255. + * + * Function calculates the average brightness level of the image. + * + */ + + float AverageBrightnessLevel() const; + + /*! + * @brief Check the equalization with a reference image + * + * @param reference Reference image + * + * @return true in case of binary equalization, otherwise false. + * + * Function calculates the binary difference between + * the image and the reference image. + * + */ + + bool Equal(const MEImage& reference) const; + + /*! + * @brief Check the equalization with a reference image + * + * @param reference Reference image + * @param maxabsdiff Maximal absolute difference + * + * @return true in case of equalization, otherwise false. + * + * Function checks the difference between the image and + * the reference image. Two pixels are equal in a range of + * a maximal absolute difference. + * + */ + + bool Equal(const MEImage& reference, int maxabsdiff) const; + + /*! + * @brief Get the grayscale value of a pixel + * + * @param x X coordinate of the pixel + * @param y Y coordinate of the pixel + * + * @return grayscale value of the pixel + * + * The method gives the grayscale value of a pixel back. If + * the image has 3 color channels (e.g. RGB) then Y value of + * YIQ/YUV color space will be calculated otherwise normal + * averaged grayscale value. + * + */ + + unsigned char GrayscalePixel(int x, int y) const; + + /*! + * @brief Count the number of neighbourhood pixels with maximum intensity + * + * @param startx X coordinate of the top-left pixel + * @param starty Y coordinate of the top-left pixel + * @param neighbourhood Specific subset of pixels + * + * @return number of the pixels with maximum intensity. + * + * The method counts the number of the pixels with maximum + * intensity (255) in a specified subset of pixels. + * The grayscale values of the pixels are used in the counter + * process. The following neighbourhood forms are allowed with + * the @a neighbourhood parameter: + * + * - n_2X2: Simple 2x2 matrix. + * - n_3X3: Simple 3x3 matrix. + * - n_3x2: Simple 3x2 matrix. + * + */ + + int NeighbourhoodCounter(int startx, int starty, NeighbourhoodType neighbourhood) const; + + /*! + * @brief Calculate the gradient vector in a point + * + * @param smooth compute smooth filter + * @param x X coordinate of the point + * @param y Y coordinate of the point + * @param mask_size The mask size to calculate the gradient + * + * @param result_x X component of the calculated vector + * @param result_y Y component of the calculated vector + * + * The method calculates the gradient vector in a given point. + * The image is preprocessed with a Gauss filter to smooth the + * image content. The filter size of the Gauss filter depends on + * mask size of the gradient vector: filter size = mask size*3. + * Eight points are assigned to the initial point to compute + * a vector sum: (x, y-mask_size), (x+mask_size/√2, y-mask_size/√2), + * (x+mask_size, y), (x+mask_size/√2, y+mask_size/√2), (x, y+mask_size), + * (x-mask_size/√2, y+mask_size/√2), (x-mask_size, y), (x-mask_size/√2, y-mask_size/√2). + * The lengths of all vectors equalize with the mask size. + * After that each vector is multiplied with the gradient difference between + * its two end points. The results are summarized and normalized by + * the mask size. + * + */ + + void GradientVector(bool smooth, int x, int y, int mask_size, int& result_x, int& result_y); + + /*! + * @brief Visualize gradient vectors + * + * @param vector_x Number of points horizontally + * @param vector_y Number of points vertically + * + * This function draws a wire (@a vector_x * @a vector_y) with + * gradient vectors. + * + */ + + void GradientVisualize(int vector_x, int vector_y); + + private: + + /* + ------------------------------------------------------------------- + Internal methods + ------------------------------------------------------------------- + */ + + /*! + * @brief Copy image data + * + * @param other_image Input image with new image data + * + * @return true if it is successful, otherwise false. + * + * Copy image data from @a other_image to MEImage image data. + * + */ + + bool _Copy(const MEImage& other_image); + + /*! + * @brief Inherent initialization function + * + * @param width Width of the image + * @param height Height of the image + * @param layer Number of color channels of the image + * + * Initialization function of MEImage class which allocates + * memory to internal MEImage image and sets its properties. + * + */ + + void _Init(int width, int height, int layer); + + /*! + * @brief Compute an image to a different color space + * + * @param mode Mode of the conversion + * + * Currently, the internal function allows to use a few + * mode to convert an image between color spaces. + * Current supported conversions (@a mode): + * - RGBtoYUV: RGB to YUV color space, + * - RGBtoYIQ: RGB to YIQ color space. + * + */ + + void ComputeColorSpace(ColorSpaceConvertType mode); + + private: + /// This matrix stores the matrix of the actual color space transform + float TransformMatrix[3][3]; + /// The OpenCV image which contains the image data + void* cvImg; + }; + } + } +} diff --git a/src/algorithms/LBP_MRF/MotionDetection.cpp b/src/algorithms/LBP_MRF/MotionDetection.cpp index 59dde1583bacc2979f55bedff492891d06a53e4a..b434806ad248ddfce5a28d5abe809a00c337ab24 100644 --- a/src/algorithms/LBP_MRF/MotionDetection.cpp +++ b/src/algorithms/LBP_MRF/MotionDetection.cpp @@ -9,1454 +9,1461 @@ #include "MEHistogram.hpp" #include "MEImage.hpp" -using namespace ck; - -// Pyramid picture for the tracking -IplImage *HUOFPyramid; -// Pyramid picture for the tracking -IplImage *HUOFPrevPyramid; - -// Struct for histogram update data of a pixel -struct MEPixelDataType -{ - float BackgroundRate; - int LifeCycle; - float *Weights; - bool *BackgroundHistogram; - float **Histograms; - float *PreviousHistogram; -}; - -MotionDetection::MotionDetection(DetectorType mode) : - MDMode(md_NotDefined), MDDataState(ps_Uninitialized), Frames(0), ReadyMask(false), - HUColorSpace(MEImage::csc_RGBtoCIELuv), HULBPMode(MEImage::lbp_Special), - HUHistogramsPerPixel(3), HUHistogramArea(5), HUHistogramBins(8), - HUImageWidth(-1), HUImageHeight(-1), HULBPPixelData(NULL), - HUPrThres(0.75), HUBackgrThres(0.95), HUHistLRate(0.01), HUWeightsLRate(0.01), - HUSamplePixels(-1), HUDesiredSamplePixels(-1), HUMinCutWeight(8.0), - HUOFDataState(ps_Uninitialized), HUOFPointsNumber(-1), - HUOFCamMovementX(0), MaxTrackedPoints(0), HUOFFrames(-1), - HUOFCamMovement(false) -{ - HUOFPyramid = NULL; - HUOFPrevPyramid = NULL; - HUOFPoints[0] = NULL; - HUOFPoints[1] = NULL; - SetMode(mode); -} - -MotionDetection::~MotionDetection() +namespace bgslibrary { - if (MDMode != md_NotDefined) + namespace algorithms { - ReleaseData(); - } -} + namespace lbp_mrf + { + // Pyramid picture for the tracking + IplImage *HUOFPyramid; + // Pyramid picture for the tracking + IplImage *HUOFPrevPyramid; -void MotionDetection::SetMode(DetectorType newmode) -{ - if (MDMode != md_NotDefined && MDMode != newmode) - { - ReleaseData(); - Frames = 0; - HUOFFrames = -1; - HUOFCamMovement = false; - HUOFCamMovementX = 0; - ReadyMask = false; - } + // Struct for histogram update data of a pixel + struct MEPixelDataType + { + float BackgroundRate; + int LifeCycle; + float *Weights; + bool *BackgroundHistogram; + float **Histograms; + float *PreviousHistogram; + }; + + MotionDetection::MotionDetection(DetectorType mode) : + MDMode(md_NotDefined), MDDataState(ps_Uninitialized), Frames(0), ReadyMask(false), + HUColorSpace(MEImage::csc_RGBtoCIELuv), HULBPMode(MEImage::lbp_Special), + HUHistogramsPerPixel(3), HUHistogramArea(5), HUHistogramBins(8), + HUImageWidth(-1), HUImageHeight(-1), HULBPPixelData(NULL), + HUPrThres(0.75), HUBackgrThres(0.95), HUHistLRate(0.01), HUWeightsLRate(0.01), + HUSamplePixels(-1), HUDesiredSamplePixels(-1), HUMinCutWeight(8.0), + HUOFDataState(ps_Uninitialized), HUOFPointsNumber(-1), + HUOFCamMovementX(0), MaxTrackedPoints(0), HUOFFrames(-1), + HUOFCamMovement(false) + { + HUOFPyramid = NULL; + HUOFPrevPyramid = NULL; + HUOFPoints[0] = NULL; + HUOFPoints[1] = NULL; + SetMode(mode); + } - switch (newmode) - { - case md_LBPHistograms: - MDMode = md_LBPHistograms; - break; + MotionDetection::~MotionDetection() + { + if (MDMode != md_NotDefined) + { + ReleaseData(); + } + } - case md_DLBPHistograms: - MDMode = md_DLBPHistograms; - break; + void MotionDetection::SetMode(DetectorType newmode) + { + if (MDMode != md_NotDefined && MDMode != newmode) + { + ReleaseData(); + Frames = 0; + HUOFFrames = -1; + HUOFCamMovement = false; + HUOFCamMovementX = 0; + ReadyMask = false; + } - default: - MDMode = md_LBPHistograms; - break; - } -} + switch (newmode) + { + case md_LBPHistograms: + MDMode = md_LBPHistograms; + break; -float MotionDetection::GetParameter(ParametersType param) const -{ - float ret = 0.0; + case md_DLBPHistograms: + MDMode = md_DLBPHistograms; + break; - switch (param) - { - case mdp_HUProximityThreshold: - ret = (float)HUPrThres; - break; + default: + MDMode = md_LBPHistograms; + break; + } + } - case mdp_HUBackgroundThreshold: - ret = (float)HUBackgrThres; - break; + float MotionDetection::GetParameter(ParametersType param) const + { + float ret = 0.0; - case mdp_HUHistogramLearningRate: - ret = (float)HUHistLRate; - break; + switch (param) + { + case mdp_HUProximityThreshold: + ret = (float)HUPrThres; + break; - case mdp_HUWeightsLearningRate: - ret = (float)HUWeightsLRate; - break; + case mdp_HUBackgroundThreshold: + ret = (float)HUBackgrThres; + break; - case mdp_HUMinCutWeight: - ret = (float)HUMinCutWeight; - break; + case mdp_HUHistogramLearningRate: + ret = (float)HUHistLRate; + break; - case mdp_HUDesiredSamplePixels: - ret = (float)HUDesiredSamplePixels; - break; + case mdp_HUWeightsLearningRate: + ret = (float)HUWeightsLRate; + break; - case mdp_HUHistogramsPerPixel: - ret = (float)HUHistogramsPerPixel; - break; + case mdp_HUMinCutWeight: + ret = (float)HUMinCutWeight; + break; - case mdp_HUHistogramArea: - ret = (float)HUHistogramArea; - break; + case mdp_HUDesiredSamplePixels: + ret = (float)HUDesiredSamplePixels; + break; - case mdp_HUHistogramBins: - ret = (float)HUHistogramBins; - break; + case mdp_HUHistogramsPerPixel: + ret = (float)HUHistogramsPerPixel; + break; - case mdp_HUColorSpace: - ret = (float)HUColorSpace; - break; + case mdp_HUHistogramArea: + ret = (float)HUHistogramArea; + break; - case mdp_HULBPMode: - ret = (float)HULBPMode; - break; + case mdp_HUHistogramBins: + ret = (float)HUHistogramBins; + break; - default: - break; - } - return ret; -} + case mdp_HUColorSpace: + ret = (float)HUColorSpace; + break; -void MotionDetection::SetParameter(ParametersType param, float value) -{ - switch (param) - { - case mdp_HUProximityThreshold: - HUPrThres = (float)value; - break; + case mdp_HULBPMode: + ret = (float)HULBPMode; + break; - case mdp_HUBackgroundThreshold: - HUBackgrThres = (float)value; - break; + default: + break; + } + return ret; + } - case mdp_HUHistogramLearningRate: - HUHistLRate = (float)value; - break; + void MotionDetection::SetParameter(ParametersType param, float value) + { + switch (param) + { + case mdp_HUProximityThreshold: + HUPrThres = (float)value; + break; - case mdp_HUWeightsLearningRate: - HUWeightsLRate = (float)value; - break; + case mdp_HUBackgroundThreshold: + HUBackgrThres = (float)value; + break; - case mdp_HUMinCutWeight: - HUMinCutWeight = (float)value; - break; + case mdp_HUHistogramLearningRate: + HUHistLRate = (float)value; + break; - case mdp_HUDesiredSamplePixels: - HUDesiredSamplePixels = (int)value; - break; + case mdp_HUWeightsLearningRate: + HUWeightsLRate = (float)value; + break; - case mdp_HUHistogramsPerPixel: - HUHistogramsPerPixel = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramsPerPixel; - break; + case mdp_HUMinCutWeight: + HUMinCutWeight = (float)value; + break; - case mdp_HUHistogramArea: - HUHistogramArea = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramArea; - break; + case mdp_HUDesiredSamplePixels: + HUDesiredSamplePixels = (int)value; + break; - case mdp_HUHistogramBins: - HUHistogramBins = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramBins; - break; + case mdp_HUHistogramsPerPixel: + HUHistogramsPerPixel = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramsPerPixel; + break; - case mdp_HUColorSpace: - HUColorSpace = (MDDataState == ps_Uninitialized) ? (int)value : HUColorSpace; - break; + case mdp_HUHistogramArea: + HUHistogramArea = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramArea; + break; - case mdp_HULBPMode: - HULBPMode = (MDDataState == ps_Uninitialized) ? (int)value : HULBPMode; - break; + case mdp_HUHistogramBins: + HUHistogramBins = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramBins; + break; - default: - break; - } -} + case mdp_HUColorSpace: + HUColorSpace = (MDDataState == ps_Uninitialized) ? (int)value : HUColorSpace; + break; -void MotionDetection::DetectMotions(MEImage& image) -{ - switch (MDMode) - { - case md_LBPHistograms: - case md_DLBPHistograms: - DetectMotionsHU(image); - break; + case mdp_HULBPMode: + HULBPMode = (MDDataState == ps_Uninitialized) ? (int)value : HULBPMode; + break; - default: - break; - } -} + default: + break; + } + } -void MotionDetection::GetMotionsMask(MEImage& mask_image) -{ - if (ReadyMask) - { - mask_image = MaskImage; - } + void MotionDetection::DetectMotions(MEImage& image) + { + switch (MDMode) + { + case md_LBPHistograms: + case md_DLBPHistograms: + DetectMotionsHU(image); + break; - switch (MDMode) - { - case md_LBPHistograms: - case md_DLBPHistograms: - GetMotionsMaskHU(MaskImage); - break; + default: + break; + } + } - default: - break; - } + void MotionDetection::GetMotionsMask(MEImage& mask_image) + { + if (ReadyMask) + { + mask_image = MaskImage; + } - ReadyMask = true; - mask_image = MaskImage; -} + switch (MDMode) + { + case md_LBPHistograms: + case md_DLBPHistograms: + GetMotionsMaskHU(MaskImage); + break; -void MotionDetection::CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives, - int& ttnegatives, int& ttpositives) -{ - if (MDDataState != ps_Successful) - { - printf("No data for calculation.\n"); - return; - } + default: + break; + } - if (referenceimage.GetLayers() != 1) - referenceimage.ConvertToGrayscale(MEImage::g_OpenCV); + ReadyMask = true; + mask_image = MaskImage; + } - referenceimage.Binarize(1); + void MotionDetection::CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives, + int& ttnegatives, int& ttpositives) + { + if (MDDataState != ps_Successful) + { + printf("No data for calculation.\n"); + return; + } - MEImage mask_image; + if (referenceimage.GetLayers() != 1) + referenceimage.ConvertToGrayscale(MEImage::g_OpenCV); - GetMotionsMask(mask_image); + referenceimage.Binarize(1); - if ((mask_image.GetWidth() != referenceimage.GetWidth()) || - (mask_image.GetHeight() != referenceimage.GetHeight())) - { - printf("Different resolutions of mask<->reference image.\n"); - return; - } + MEImage mask_image; - unsigned char* RefMaskImgData = referenceimage.GetImageData(); - unsigned char* MaskImgData = mask_image.GetImageData(); - int RowStart = 0; - int RowWidth = referenceimage.GetRowWidth(); + GetMotionsMask(mask_image); - int TrueNegatives = 0; - int TruePositives = 0; - int TotalTrueNegatives = 0; - int TotalTruePositives = 0; + if ((mask_image.GetWidth() != referenceimage.GetWidth()) || + (mask_image.GetHeight() != referenceimage.GetHeight())) + { + printf("Different resolutions of mask<->reference image.\n"); + return; + } - int ImageFrame = 0; + unsigned char* RefMaskImgData = referenceimage.GetImageData(); + unsigned char* MaskImgData = mask_image.GetImageData(); + int RowStart = 0; + int RowWidth = referenceimage.GetRowWidth(); - if (MDMode == md_LBPHistograms || md_DLBPHistograms) - { - ImageFrame = HUHistogramArea / 2; - } + int TrueNegatives = 0; + int TruePositives = 0; + int TotalTrueNegatives = 0; + int TotalTruePositives = 0; - for (int y = referenceimage.GetHeight() - ImageFrame - 1; y >= ImageFrame; --y) - { - for (int x = referenceimage.GetWidth() - ImageFrame - 1; x >= ImageFrame; --x) - { - TrueNegatives += - (RefMaskImgData[RowStart + x] == 0) && - (MaskImgData[RowStart + x] == 0); - TotalTrueNegatives += (RefMaskImgData[RowStart + x] == 0); - TruePositives += - (RefMaskImgData[RowStart + x] == 255) && - (MaskImgData[RowStart + x] == 255); - TotalTruePositives += (RefMaskImgData[RowStart + x] == 255); - } - RowStart += RowWidth; - } + int ImageFrame = 0; - tnegatives = TrueNegatives; - ttnegatives = TotalTrueNegatives; - tpositives = TruePositives; - ttpositives = TotalTruePositives; -} + if (MDMode == md_LBPHistograms || md_DLBPHistograms) + { + ImageFrame = HUHistogramArea / 2; + } -void MotionDetection::ReleaseData() -{ - if (MDMode == md_LBPHistograms || MDMode == md_DLBPHistograms) - { - ReleaseHUData(); - } -} + for (int y = referenceimage.GetHeight() - ImageFrame - 1; y >= ImageFrame; --y) + { + for (int x = referenceimage.GetWidth() - ImageFrame - 1; x >= ImageFrame; --x) + { + TrueNegatives += + (RefMaskImgData[RowStart + x] == 0) && + (MaskImgData[RowStart + x] == 0); + TotalTrueNegatives += (RefMaskImgData[RowStart + x] == 0); + TruePositives += + (RefMaskImgData[RowStart + x] == 255) && + (MaskImgData[RowStart + x] == 255); + TotalTruePositives += (RefMaskImgData[RowStart + x] == 255); + } + RowStart += RowWidth; + } -void MotionDetection::InitHUData(int imagewidth, int imageheight) -{ - if ((HUImageWidth != imagewidth - HUHistogramArea + 1) || - (HUImageHeight != imageheight - HUHistogramArea + 1) || - (MDDataState == ps_Uninitialized)) - { - if (MDDataState != ps_Uninitialized) - { - ReleaseHUData(); - } + tnegatives = TrueNegatives; + ttnegatives = TotalTrueNegatives; + tpositives = TruePositives; + ttpositives = TotalTruePositives; + } - MDDataState = ps_Initialized; + void MotionDetection::ReleaseData() + { + if (MDMode == md_LBPHistograms || MDMode == md_DLBPHistograms) + { + ReleaseHUData(); + } + } - HUImageWidth = imagewidth - HUHistogramArea + 1; - HUImageHeight = imageheight - HUHistogramArea + 1; + void MotionDetection::InitHUData(int imagewidth, int imageheight) + { + if ((HUImageWidth != imagewidth - HUHistogramArea + 1) || + (HUImageHeight != imageheight - HUHistogramArea + 1) || + (MDDataState == ps_Uninitialized)) + { + if (MDDataState != ps_Uninitialized) + { + ReleaseHUData(); + } - HULBPPixelData = new MEPixelDataType**[HUImageWidth / 2]; + MDDataState = ps_Initialized; - for (int i = 0; i < HUImageWidth / 2; ++i) - { - HULBPPixelData[i] = new MEPixelDataType*[HUImageHeight]; - } + HUImageWidth = imagewidth - HUHistogramArea + 1; + HUImageHeight = imageheight - HUHistogramArea + 1; - for (int i = 0; i < HUImageWidth / 2; ++i) - for (int i1 = 0; i1 < HUImageHeight; ++i1) - { - HULBPPixelData[i][i1] = new MEPixelDataType; - HULBPPixelData[i][i1]->Weights = new float[HUHistogramsPerPixel]; - HULBPPixelData[i][i1]->BackgroundHistogram = new bool[HUHistogramsPerPixel]; - HULBPPixelData[i][i1]->Histograms = new float*[HUHistogramsPerPixel]; - for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) - HULBPPixelData[i][i1]->Histograms[i2] = new float[HUHistogramBins]; - HULBPPixelData[i][i1]->PreviousHistogram = new float[HUHistogramBins]; - } + HULBPPixelData = new MEPixelDataType**[HUImageWidth / 2]; - // Allocate auxiliary variables - HUMaskColumnAddDel = new int*[HUHistogramArea]; - for (int i = 0; i < HUHistogramArea; ++i) - HUMaskColumnAddDel[i] = new int[2]; + for (int i = 0; i < HUImageWidth / 2; ++i) + { + HULBPPixelData[i] = new MEPixelDataType*[HUImageHeight]; + } - HUMaskRowAddDel = new int*[HUHistogramArea]; - for (int i = 0; i < HUHistogramArea; ++i) - HUMaskRowAddDel[i] = new int[2]; + for (int i = 0; i < HUImageWidth / 2; ++i) + for (int i1 = 0; i1 < HUImageHeight; ++i1) + { + HULBPPixelData[i][i1] = new MEPixelDataType; + HULBPPixelData[i][i1]->Weights = new float[HUHistogramsPerPixel]; + HULBPPixelData[i][i1]->BackgroundHistogram = new bool[HUHistogramsPerPixel]; + HULBPPixelData[i][i1]->Histograms = new float*[HUHistogramsPerPixel]; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + HULBPPixelData[i][i1]->Histograms[i2] = new float[HUHistogramBins]; + HULBPPixelData[i][i1]->PreviousHistogram = new float[HUHistogramBins]; + } - // Generate sample mask - SetSampleMaskHU(sm_Circle, HUDesiredSamplePixels); + // Allocate auxiliary variables + HUMaskColumnAddDel = new int*[HUHistogramArea]; + for (int i = 0; i < HUHistogramArea; ++i) + HUMaskColumnAddDel[i] = new int[2]; - // Init HU optical flow data - if (MDMode == md_DLBPHistograms) - InitHUOFData(imagewidth, imageheight); + HUMaskRowAddDel = new int*[HUHistogramArea]; + for (int i = 0; i < HUHistogramArea; ++i) + HUMaskRowAddDel[i] = new int[2]; - ClearHUData(); - } -} + // Generate sample mask + SetSampleMaskHU(sm_Circle, HUDesiredSamplePixels); -void MotionDetection::InitHUOFData(int imagewidth, int imageheight) -{ - if (HUOFDataState != ps_Uninitialized) - { - ReleaseHUOFData(); - } + // Init HU optical flow data + if (MDMode == md_DLBPHistograms) + InitHUOFData(imagewidth, imageheight); - if (HUOFDataState == ps_Uninitialized) - { - HUOFPointsNumber = imagewidth*imageheight / 1000; - HUOFPyramid = cvCreateImage(cvSize(imagewidth, imageheight), 8, 1); - HUOFPrevPyramid = cvCreateImage(cvSize(imagewidth, imageheight), 8, 1); - HUOFPoints[0] = (CvPoint2D32f*)cvAlloc(HUOFPointsNumber * sizeof(HUOFPoints[0][0])); - HUOFPoints[1] = (CvPoint2D32f*)cvAlloc(HUOFPointsNumber * sizeof(HUOFPoints[1][0])); - } -} + ClearHUData(); + } + } -void MotionDetection::ReleaseHUData() -{ - if (MDDataState != ps_Uninitialized) - { - for (int i = 0; i < HUImageWidth / 2; i++) - for (int i1 = 0; i1 < HUImageHeight; i1++) + void MotionDetection::InitHUOFData(int imagewidth, int imageheight) { - delete[] HULBPPixelData[i][i1]->PreviousHistogram; - for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) - delete[] HULBPPixelData[i][i1]->Histograms[i2]; - delete[] HULBPPixelData[i][i1]->Histograms; - delete[] HULBPPixelData[i][i1]->BackgroundHistogram; - delete[] HULBPPixelData[i][i1]->Weights; - delete HULBPPixelData[i][i1]; + if (HUOFDataState != ps_Uninitialized) + { + ReleaseHUOFData(); + } + + if (HUOFDataState == ps_Uninitialized) + { + HUOFPointsNumber = imagewidth*imageheight / 1000; + HUOFPyramid = cvCreateImage(cvSize(imagewidth, imageheight), 8, 1); + HUOFPrevPyramid = cvCreateImage(cvSize(imagewidth, imageheight), 8, 1); + HUOFPoints[0] = (CvPoint2D32f*)cvAlloc(HUOFPointsNumber * sizeof(HUOFPoints[0][0])); + HUOFPoints[1] = (CvPoint2D32f*)cvAlloc(HUOFPointsNumber * sizeof(HUOFPoints[1][0])); + } } - for (int i = 0; i < HUImageWidth / 2; i++) - { - delete[] HULBPPixelData[i]; - } - delete[] HULBPPixelData; + void MotionDetection::ReleaseHUData() + { + if (MDDataState != ps_Uninitialized) + { + for (int i = 0; i < HUImageWidth / 2; i++) + for (int i1 = 0; i1 < HUImageHeight; i1++) + { + delete[] HULBPPixelData[i][i1]->PreviousHistogram; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + delete[] HULBPPixelData[i][i1]->Histograms[i2]; + delete[] HULBPPixelData[i][i1]->Histograms; + delete[] HULBPPixelData[i][i1]->BackgroundHistogram; + delete[] HULBPPixelData[i][i1]->Weights; + delete HULBPPixelData[i][i1]; + } - if (MDMode == md_DLBPHistograms) - ReleaseHUOFData(); + for (int i = 0; i < HUImageWidth / 2; i++) + { + delete[] HULBPPixelData[i]; + } + delete[] HULBPPixelData; - HUImageWidth = -1; - HUImageHeight = -1; - HULBPPixelData = NULL; - MDDataState = ps_Uninitialized; + if (MDMode == md_DLBPHistograms) + ReleaseHUOFData(); - // Release auxiliary variables - for (int i = 0; i < HUHistogramArea; ++i) - delete[] HUMaskColumnAddDel[i]; - delete[] HUMaskColumnAddDel; + HUImageWidth = -1; + HUImageHeight = -1; + HULBPPixelData = NULL; + MDDataState = ps_Uninitialized; - for (int i = 0; i < HUHistogramArea; ++i) - delete[] HUMaskRowAddDel[i]; - delete[] HUMaskRowAddDel; + // Release auxiliary variables + for (int i = 0; i < HUHistogramArea; ++i) + delete[] HUMaskColumnAddDel[i]; + delete[] HUMaskColumnAddDel; - HUMaskColumnAddDel = NULL; - HUMaskRowAddDel = NULL; - } -} + for (int i = 0; i < HUHistogramArea; ++i) + delete[] HUMaskRowAddDel[i]; + delete[] HUMaskRowAddDel; -void MotionDetection::ReleaseHUOFData() -{ - if (MDDataState != ps_Uninitialized) - { - if (HUOFPyramid) - { - cvReleaseImage(&HUOFPyramid); - HUOFPyramid = NULL; - } - if (HUOFPrevPyramid) - { - cvReleaseImage(&HUOFPrevPyramid); - HUOFPrevPyramid = NULL; - } - if (HUOFPoints[0]) - { - cvFree(&HUOFPoints[0]); - HUOFPoints[0] = NULL; - } - if (HUOFPoints[1]) - { - cvFree(&HUOFPoints[1]); - HUOFPoints[1] = NULL; - } - HUOFDataState = ps_Uninitialized; - } -} + HUMaskColumnAddDel = NULL; + HUMaskRowAddDel = NULL; + } + } -void MotionDetection::ClearHUData() -{ - if (MDDataState != ps_Uninitialized) - { - for (int i = (HUImageWidth / 2) - 1; i >= 0; --i) - for (int i1 = HUImageHeight - 1; i1 >= 0; --i1) + void MotionDetection::ReleaseHUOFData() { - for (int i2 = HUHistogramsPerPixel - 1; i2 >= 0; --i2) + if (MDDataState != ps_Uninitialized) { - memset(HULBPPixelData[i][i1]->Histograms[i2], 0, - HUHistogramBins * sizeof(float)); - HULBPPixelData[i][i1]->Weights[i2] = 1.0 / HUHistogramsPerPixel; - HULBPPixelData[i][i1]->BackgroundHistogram[i2] = true; + if (HUOFPyramid) + { + cvReleaseImage(&HUOFPyramid); + HUOFPyramid = NULL; + } + if (HUOFPrevPyramid) + { + cvReleaseImage(&HUOFPrevPyramid); + HUOFPrevPyramid = NULL; + } + if (HUOFPoints[0]) + { + cvFree(&HUOFPoints[0]); + HUOFPoints[0] = NULL; + } + if (HUOFPoints[1]) + { + cvFree(&HUOFPoints[1]); + HUOFPoints[1] = NULL; + } + HUOFDataState = ps_Uninitialized; } - HULBPPixelData[i][i1]->BackgroundRate = 1.0; - HULBPPixelData[i][i1]->LifeCycle = 0; } - MDDataState = ps_Initialized; - } -} -void MotionDetection::DetectMotionsHU(MEImage& image) -{ - unsigned char *ImgData = NULL; - MEImage newimage = image; - float DiffAreas = 0; - - // Init the histogram update data structures if needs be - if ((MDDataState == ps_Uninitialized) || - (HUImageWidth != newimage.GetWidth() - HUHistogramArea + 1) || - (HUImageHeight != newimage.GetHeight() - HUHistogramArea + 1)) - { - InitHUData(newimage.GetWidth(), newimage.GetHeight()); - } + void MotionDetection::ClearHUData() + { + if (MDDataState != ps_Uninitialized) + { + for (int i = (HUImageWidth / 2) - 1; i >= 0; --i) + for (int i1 = HUImageHeight - 1; i1 >= 0; --i1) + { + for (int i2 = HUHistogramsPerPixel - 1; i2 >= 0; --i2) + { + memset(HULBPPixelData[i][i1]->Histograms[i2], 0, + HUHistogramBins * sizeof(float)); + HULBPPixelData[i][i1]->Weights[i2] = 1.0 / HUHistogramsPerPixel; + HULBPPixelData[i][i1]->BackgroundHistogram[i2] = true; + } + HULBPPixelData[i][i1]->BackgroundRate = 1.0; + HULBPPixelData[i][i1]->LifeCycle = 0; + } + MDDataState = ps_Initialized; + } + } - if (newimage.GetLayers() == 1) - { - newimage.ConvertGrayscaleToRGB(); - } + void MotionDetection::DetectMotionsHU(MEImage& image) + { + unsigned char *ImgData = NULL; + MEImage newimage = image; + float DiffAreas = 0; + + // Init the histogram update data structures if needs be + if ((MDDataState == ps_Uninitialized) || + (HUImageWidth != newimage.GetWidth() - HUHistogramArea + 1) || + (HUImageHeight != newimage.GetHeight() - HUHistogramArea + 1)) + { + InitHUData(newimage.GetWidth(), newimage.GetHeight()); + } - MEImage blueimage = newimage; - blueimage.ColorSpace(MEImage::csc_RGBtoCIELuv); + if (newimage.GetLayers() == 1) + { + newimage.ConvertGrayscaleToRGB(); + } - if (HUColorSpace != -1) - { - newimage.ColorSpace((MEImage::ColorSpaceConvertType)HUColorSpace); - } + MEImage blueimage = newimage; + blueimage.ColorSpace(MEImage::csc_RGBtoCIELuv); - if (Frames == 0) - { - MEImage BlueLayer; - blueimage.GetLayer(BlueLayer, 1); - BlueLayer.Resize(32, 24); - PreviousBlueLayer = BlueLayer; - } + if (HUColorSpace != -1) + { + newimage.ColorSpace((MEImage::ColorSpaceConvertType)HUColorSpace); + } - Frames++; + if (Frames == 0) + { + MEImage BlueLayer; + blueimage.GetLayer(BlueLayer, 1); + BlueLayer.Resize(32, 24); + PreviousBlueLayer = BlueLayer; + } - // Detect the fast, big changes in the scene - MEImage BlueLayer; - blueimage.GetLayer(BlueLayer, 1); - BlueLayer.Resize(32, 24); - DiffAreas = BlueLayer.DifferenceAreas(PreviousBlueLayer, 12); + Frames++; - if (DiffAreas > 80) - { - MDDataState = ps_Initialized; - if (MDMode == md_DLBPHistograms) - HUOFDataState = ps_Initialized; - printf("Frame: %d - big changes in the scene (%f)", Frames, DiffAreas); - Frames = 1; - HUOFFrames = -1; - } - PreviousBlueLayer = BlueLayer; + // Detect the fast, big changes in the scene + MEImage BlueLayer; + blueimage.GetLayer(BlueLayer, 1); + BlueLayer.Resize(32, 24); + DiffAreas = BlueLayer.DifferenceAreas(PreviousBlueLayer, 12); - if (Frames == 1) - { - CurrentImage = image; - PreviousImage = CurrentImage; - } - else - if (Frames > 1) - { - PreviousImage = CurrentImage; - CurrentImage = image; - // Optical flow correction of the camera movements - if (MDMode == md_DLBPHistograms) - { - OpticalFlowCorrection(); - } - } + if (DiffAreas > 80) + { + MDDataState = ps_Initialized; + if (MDMode == md_DLBPHistograms) + HUOFDataState = ps_Initialized; + printf("Frame: %d - big changes in the scene (%f)", Frames, DiffAreas); + Frames = 1; + HUOFFrames = -1; + } + PreviousBlueLayer = BlueLayer; - newimage.ConvertToGrayscale(MEImage::g_OpenCV); + if (Frames == 1) + { + CurrentImage = image; + PreviousImage = CurrentImage; + } + else + if (Frames > 1) + { + PreviousImage = CurrentImage; + CurrentImage = image; + // Optical flow correction of the camera movements + if (MDMode == md_DLBPHistograms) + { + OpticalFlowCorrection(); + } + } - if (HULBPMode != -1) - { - newimage.LBP((MEImage::LBPType)HULBPMode); - } + newimage.ConvertToGrayscale(MEImage::g_OpenCV); - // Set some auxiliary variables - ImgData = newimage.GetImageData(); - int DivisionOperator = (int)(log((double)256 / HUHistogramBins) / log((double) 2.)) + 1; + if (HULBPMode != -1) + { + newimage.LBP((MEImage::LBPType)HULBPMode); + } - // Downscale the image - for (int i = newimage.GetRowWidth()*newimage.GetHeight() - 1; i >= 0; --i) - { - ImgData[i] >>= DivisionOperator; - } + // Set some auxiliary variables + ImgData = newimage.GetImageData(); + int DivisionOperator = (int)(log((double)256 / HUHistogramBins) / log((double) 2.)) + 1; - UpdateModelHU(newimage, HULBPPixelData); + // Downscale the image + for (int i = newimage.GetRowWidth()*newimage.GetHeight() - 1; i >= 0; --i) + { + ImgData[i] >>= DivisionOperator; + } - // Change the state of the HU data structures - if (MDDataState == ps_Initialized) - { - MDDataState = ps_Successful; - } - HUOFCamMovement = false; - ReadyMask = false; -} + UpdateModelHU(newimage, HULBPPixelData); -void MotionDetection::UpdateModelHU(MEImage& image, MEPixelDataType*** model) -{ - float *CurrentHistogram = new float[HUHistogramBins]; - float *CurrentHistogram2 = new float[HUHistogramBins]; - unsigned char *ImgData = image.GetImageData(); - int RowWidth = image.GetRowWidth(); - int RowStart = (HUImageHeight - 1)*RowWidth; - - memset(CurrentHistogram, 0, HUHistogramBins * sizeof(float)); - // Calculate the first histogram - for (int y = HUHistogramArea - 1; y >= 0; --y) - { - for (int x = HUHistogramArea - 1; x >= 0; --x) - { - if ((HUMaskRowAddDel[y][1] > x) && (HUMaskRowAddDel[y][0] <= x) && - (HUMaskColumnAddDel[x][1] > y) && (HUMaskColumnAddDel[x][0] <= y)) - { - CurrentHistogram[ImgData[RowStart + HUImageWidth - 1 + x]]++; - } - } - RowStart += RowWidth; - } - - // This cycle generates the last row of histograms - for (int y = HUImageHeight - 1; y >= 0; --y) - { - if (HUImageHeight - 1 > y) - { - // Delete and add a pixel column from the histogram data - for (int i = HUHistogramArea - 1; i >= 0; --i) - { - if (HUMaskColumnAddDel[i][0] != -1) - CurrentHistogram[ImgData[RowWidth*(y + HUMaskColumnAddDel[i][0]) + HUImageWidth - 1 + i]]++; - if (HUMaskColumnAddDel[i][1] != -1) - CurrentHistogram[ImgData[RowWidth*(y + HUMaskColumnAddDel[i][1]) + HUImageWidth - 1 + i]]--; - } - } - - if (y % 2 == HUImageWidth % 2) - { - MEPixelDataType* PixelData = model[(HUImageWidth - 1) / 2][y]; - - // Allocate and initialize the pixel data if needs be - if (!PixelData) - { - // Memory allocation - PixelData = new MEPixelDataType; - PixelData->Weights = new float[HUHistogramsPerPixel]; - PixelData->BackgroundHistogram = new bool[HUHistogramsPerPixel]; - PixelData->Histograms = new float*[HUHistogramsPerPixel]; - for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) - PixelData->Histograms[i2] = new float[HUHistogramBins]; - PixelData->PreviousHistogram = new float[HUHistogramBins]; - - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + // Change the state of the HU data structures + if (MDDataState == ps_Initialized) { - memcpy(PixelData->Histograms[i], CurrentHistogram, HUHistogramBins * sizeof(float)); - PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; - PixelData->BackgroundHistogram[i] = true; + MDDataState = ps_Successful; } - PixelData->BackgroundRate = 1.0; - PixelData->LifeCycle = 0; - memcpy(PixelData->PreviousHistogram, CurrentHistogram, HUHistogramBins * sizeof(float)); - - model[(HUImageWidth - 1) / 2][y] = PixelData; + HUOFCamMovement = false; + ReadyMask = false; } - else { - bool InitHistograms = (MDDataState == ps_Initialized); - if (MDDataState != ps_Initialized && HUOFCamMovement) + void MotionDetection::UpdateModelHU(MEImage& image, MEPixelDataType*** model) + { + float *CurrentHistogram = new float[HUHistogramBins]; + float *CurrentHistogram2 = new float[HUHistogramBins]; + unsigned char *ImgData = image.GetImageData(); + int RowWidth = image.GetRowWidth(); + int RowStart = (HUImageHeight - 1)*RowWidth; + + memset(CurrentHistogram, 0, HUHistogramBins * sizeof(float)); + // Calculate the first histogram + for (int y = HUHistogramArea - 1; y >= 0; --y) { - // Histogram intersection between the previous and the current histogram - float Difference = 0.0; - for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) + for (int x = HUHistogramArea - 1; x >= 0; --x) { - Difference += (float)(CurrentHistogram[i1] < PixelData->PreviousHistogram[i1] ? - CurrentHistogram[i1] : PixelData->PreviousHistogram[i1]); + if ((HUMaskRowAddDel[y][1] > x) && (HUMaskRowAddDel[y][0] <= x) && + (HUMaskColumnAddDel[x][1] > y) && (HUMaskColumnAddDel[x][0] <= y)) + { + CurrentHistogram[ImgData[RowStart + HUImageWidth - 1 + x]]++; + } } - Difference /= HUSamplePixels; - - if (Difference < HUBackgrThres) - InitHistograms = true; + RowStart += RowWidth; } - if (InitHistograms) + + // This cycle generates the last row of histograms + for (int y = HUImageHeight - 1; y >= 0; --y) { - // Copy the histogram data to the HU data structures - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + if (HUImageHeight - 1 > y) { - memcpy(PixelData->Histograms[i], CurrentHistogram, HUHistogramBins * sizeof(float)); - PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; - PixelData->BackgroundHistogram[i] = true; + // Delete and add a pixel column from the histogram data + for (int i = HUHistogramArea - 1; i >= 0; --i) + { + if (HUMaskColumnAddDel[i][0] != -1) + CurrentHistogram[ImgData[RowWidth*(y + HUMaskColumnAddDel[i][0]) + HUImageWidth - 1 + i]]++; + if (HUMaskColumnAddDel[i][1] != -1) + CurrentHistogram[ImgData[RowWidth*(y + HUMaskColumnAddDel[i][1]) + HUImageWidth - 1 + i]]--; + } } - memcpy(PixelData->PreviousHistogram, CurrentHistogram, HUHistogramBins * sizeof(float)); - PixelData->BackgroundRate = 1.0; - PixelData->LifeCycle = 0; - } - else { - // Update the HU data structures - UpdateHUPixelData(PixelData, CurrentHistogram); - if (MDMode == md_DLBPHistograms) + if (y % 2 == HUImageWidth % 2) { - memcpy(PixelData->PreviousHistogram, CurrentHistogram, HUHistogramBins * sizeof(float)); - } - } - } - } - - // Copy the histogram - memcpy(CurrentHistogram2, CurrentHistogram, HUHistogramBins * sizeof(float)); + MEPixelDataType* PixelData = model[(HUImageWidth - 1) / 2][y]; - // This cycle generates a column of histograms - for (int x = HUImageWidth - 2; x >= 0; --x) - { - RowStart = RowWidth*y; + // Allocate and initialize the pixel data if needs be + if (!PixelData) + { + // Memory allocation + PixelData = new MEPixelDataType; + PixelData->Weights = new float[HUHistogramsPerPixel]; + PixelData->BackgroundHistogram = new bool[HUHistogramsPerPixel]; + PixelData->Histograms = new float*[HUHistogramsPerPixel]; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + PixelData->Histograms[i2] = new float[HUHistogramBins]; + PixelData->PreviousHistogram = new float[HUHistogramBins]; - // Delete and add a pixel column from the histogram data - for (int i = HUHistogramArea - 1; i >= 0; --i) - { - if (HUMaskRowAddDel[i][0] != -1) - CurrentHistogram2[ImgData[RowStart + x + HUMaskRowAddDel[i][0]]]++; - if (HUMaskRowAddDel[i][1] != -1) - CurrentHistogram2[ImgData[RowStart + x + HUMaskRowAddDel[i][1]]]--; + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram, HUHistogramBins * sizeof(float)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + memcpy(PixelData->PreviousHistogram, CurrentHistogram, HUHistogramBins * sizeof(float)); - RowStart += RowWidth; - } - if (x % 2 == 0) - { - MEPixelDataType* PixelData = model[x / 2][y]; + model[(HUImageWidth - 1) / 2][y] = PixelData; + } + else { + bool InitHistograms = (MDDataState == ps_Initialized); - // Allocate and initialize the pixel data if needs be - if (!PixelData) - { - // Memory allocation - PixelData = new MEPixelDataType; - PixelData->Weights = new float[HUHistogramsPerPixel]; - PixelData->BackgroundHistogram = new bool[HUHistogramsPerPixel]; - PixelData->Histograms = new float*[HUHistogramsPerPixel]; - for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) - PixelData->Histograms[i2] = new float[HUHistogramBins]; - PixelData->PreviousHistogram = new float[HUHistogramBins]; - - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) - { - memcpy(PixelData->Histograms[i], CurrentHistogram2, sizeof(CurrentHistogram2)); - PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; - PixelData->BackgroundHistogram[i] = true; + if (MDDataState != ps_Initialized && HUOFCamMovement) + { + // Histogram intersection between the previous and the current histogram + float Difference = 0.0; + for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) + { + Difference += (float)(CurrentHistogram[i1] < PixelData->PreviousHistogram[i1] ? + CurrentHistogram[i1] : PixelData->PreviousHistogram[i1]); + } + Difference /= HUSamplePixels; + + if (Difference < HUBackgrThres) + InitHistograms = true; + } + if (InitHistograms) + { + // Copy the histogram data to the HU data structures + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram, HUHistogramBins * sizeof(float)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + memcpy(PixelData->PreviousHistogram, CurrentHistogram, HUHistogramBins * sizeof(float)); + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + } + else { + // Update the HU data structures + UpdateHUPixelData(PixelData, CurrentHistogram); + + if (MDMode == md_DLBPHistograms) + { + memcpy(PixelData->PreviousHistogram, CurrentHistogram, HUHistogramBins * sizeof(float)); + } + } + } } - PixelData->BackgroundRate = 1.0; - PixelData->LifeCycle = 0; - model[x / 2][y] = PixelData; - memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); - } - else { - bool InitHistograms = (MDDataState == ps_Initialized); - if (MDDataState != ps_Initialized && HUOFCamMovement) - { - // Histogram intersection between the previous and the current histogram - float Difference = 0.0; - for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) - { - Difference += (float)(CurrentHistogram2[i1] < PixelData->PreviousHistogram[i1] ? - CurrentHistogram2[i1] : PixelData->PreviousHistogram[i1]); - } - Difference /= HUSamplePixels; + // Copy the histogram + memcpy(CurrentHistogram2, CurrentHistogram, HUHistogramBins * sizeof(float)); - if (Difference < HUBackgrThres) - InitHistograms = true; - } - if (InitHistograms) + // This cycle generates a column of histograms + for (int x = HUImageWidth - 2; x >= 0; --x) { - // Copy the histogram data to the HU data structures - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + RowStart = RowWidth*y; + + // Delete and add a pixel column from the histogram data + for (int i = HUHistogramArea - 1; i >= 0; --i) { - memcpy(PixelData->Histograms[i], CurrentHistogram2, sizeof(CurrentHistogram2)); - PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; - PixelData->BackgroundHistogram[i] = true; - } - memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); - PixelData->BackgroundRate = 1.0; - PixelData->LifeCycle = 0; - } - else { - // Update the HU data structures - UpdateHUPixelData(PixelData, CurrentHistogram2); + if (HUMaskRowAddDel[i][0] != -1) + CurrentHistogram2[ImgData[RowStart + x + HUMaskRowAddDel[i][0]]]++; + if (HUMaskRowAddDel[i][1] != -1) + CurrentHistogram2[ImgData[RowStart + x + HUMaskRowAddDel[i][1]]]--; - if (MDMode == md_DLBPHistograms) + RowStart += RowWidth; + } + if (x % 2 == 0) { - memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + MEPixelDataType* PixelData = model[x / 2][y]; + + // Allocate and initialize the pixel data if needs be + if (!PixelData) + { + // Memory allocation + PixelData = new MEPixelDataType; + PixelData->Weights = new float[HUHistogramsPerPixel]; + PixelData->BackgroundHistogram = new bool[HUHistogramsPerPixel]; + PixelData->Histograms = new float*[HUHistogramsPerPixel]; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + PixelData->Histograms[i2] = new float[HUHistogramBins]; + PixelData->PreviousHistogram = new float[HUHistogramBins]; + + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram2, sizeof(CurrentHistogram2)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + model[x / 2][y] = PixelData; + memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + } + else { + bool InitHistograms = (MDDataState == ps_Initialized); + + if (MDDataState != ps_Initialized && HUOFCamMovement) + { + // Histogram intersection between the previous and the current histogram + float Difference = 0.0; + for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) + { + Difference += (float)(CurrentHistogram2[i1] < PixelData->PreviousHistogram[i1] ? + CurrentHistogram2[i1] : PixelData->PreviousHistogram[i1]); + } + Difference /= HUSamplePixels; + + if (Difference < HUBackgrThres) + InitHistograms = true; + } + if (InitHistograms) + { + // Copy the histogram data to the HU data structures + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + memcpy(PixelData->Histograms[i], CurrentHistogram2, sizeof(CurrentHistogram2)); + PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel; + PixelData->BackgroundHistogram[i] = true; + } + memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + PixelData->BackgroundRate = 1.0; + PixelData->LifeCycle = 0; + } + else { + // Update the HU data structures + UpdateHUPixelData(PixelData, CurrentHistogram2); + + if (MDMode == md_DLBPHistograms) + { + memcpy(PixelData->PreviousHistogram, CurrentHistogram2, sizeof(CurrentHistogram2)); + } + } + } } + } } + delete[] CurrentHistogram; + delete[] CurrentHistogram2; } - } - } - delete[] CurrentHistogram; - delete[] CurrentHistogram2; -} - -void MotionDetection::UpdateHUPixelData(MEPixelDataType* PixelData, const float *histogram) -{ - int MaxIndex = 0; - float MaxValue = -1; - bool Replace = true; - float *IntersectionResults = new float[HUHistogramsPerPixel]; + void MotionDetection::UpdateHUPixelData(MEPixelDataType* PixelData, const float *histogram) + { + int MaxIndex = 0; + float MaxValue = -1; + bool Replace = true; + float *IntersectionResults = new float[HUHistogramsPerPixel]; - PixelData->LifeCycle++; - PixelData->BackgroundRate = 0.0; + PixelData->LifeCycle++; + PixelData->BackgroundRate = 0.0; - // Compute intersection between the currect and older histograms - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) - { - // Histogram intersection - float Difference = 0.0; - for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) - { - Difference += (float)histogram[i1] < PixelData->Histograms[i][i1] ? - (float)histogram[i1] : PixelData->Histograms[i][i1]; - } + // Compute intersection between the currect and older histograms + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + // Histogram intersection + float Difference = 0.0; + for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) + { + Difference += (float)histogram[i1] < PixelData->Histograms[i][i1] ? + (float)histogram[i1] : PixelData->Histograms[i][i1]; + } - IntersectionResults[i] = (float)Difference / (float)(HUSamplePixels); + IntersectionResults[i] = (float)Difference / (float)(HUSamplePixels); - if (PixelData->BackgroundHistogram[i] && - IntersectionResults[i] > PixelData->BackgroundRate) - { - PixelData->BackgroundRate = IntersectionResults[i]; - } + if (PixelData->BackgroundHistogram[i] && + IntersectionResults[i] > PixelData->BackgroundRate) + { + PixelData->BackgroundRate = IntersectionResults[i]; + } - if (MaxValue < IntersectionResults[i]) - { - MaxValue = IntersectionResults[i]; - MaxIndex = i; - } + if (MaxValue < IntersectionResults[i]) + { + MaxValue = IntersectionResults[i]; + MaxIndex = i; + } - Replace = Replace && (IntersectionResults[i] < HUPrThres); - } + Replace = Replace && (IntersectionResults[i] < HUPrThres); + } - // Replace the histogram with the lowest weight - if (Replace) - { - // Find the histogram with minimal weight - int MinIndex = 0; - float MinValue = PixelData->Weights[0]; - for (int i1 = HUHistogramsPerPixel - 1; i1 > 0; --i1) - { - if (MinValue > PixelData->Weights[i1]) - { - MinValue = PixelData->Weights[i1]; - MinIndex = i1; - } - } + // Replace the histogram with the lowest weight + if (Replace) + { + // Find the histogram with minimal weight + int MinIndex = 0; + float MinValue = PixelData->Weights[0]; + for (int i1 = HUHistogramsPerPixel - 1; i1 > 0; --i1) + { + if (MinValue > PixelData->Weights[i1]) + { + MinValue = PixelData->Weights[i1]; + MinIndex = i1; + } + } - PixelData->Weights[MinIndex] = 0.01; - for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) - PixelData->Histograms[MinIndex][i1] = (float)histogram[i1]; - PixelData->BackgroundHistogram[MinIndex] = 0; + PixelData->Weights[MinIndex] = 0.01; + for (int i1 = HUHistogramBins - 1; i1 >= 0; --i1) + PixelData->Histograms[MinIndex][i1] = (float)histogram[i1]; + PixelData->BackgroundHistogram[MinIndex] = 0; - // Normalize the weights - float sum = 0; - for (int i1 = HUHistogramsPerPixel - 1; i1 >= 0; --i1) - sum += PixelData->Weights[i1]; + // Normalize the weights + float sum = 0; + for (int i1 = HUHistogramsPerPixel - 1; i1 >= 0; --i1) + sum += PixelData->Weights[i1]; - for (int i1 = HUHistogramsPerPixel - 1; i1 >= 0; --i1) - PixelData->Weights[i1] = PixelData->Weights[i1] / sum; + for (int i1 = HUHistogramsPerPixel - 1; i1 >= 0; --i1) + PixelData->Weights[i1] = PixelData->Weights[i1] / sum; - return; - } + return; + } - float LearningRate = HUHistLRate; + float LearningRate = HUHistLRate; - if (PixelData->LifeCycle < 100) - LearningRate += (float)(100 - PixelData->LifeCycle) / 100; - else - if (MDMode == md_DLBPHistograms && HUOFFrames != -1 && HUOFFrames < 40) - LearningRate += (HUOFFrames < 80 ? 0.05 : 0); + if (PixelData->LifeCycle < 100) + LearningRate += (float)(100 - PixelData->LifeCycle) / 100; + else + if (MDMode == md_DLBPHistograms && HUOFFrames != -1 && HUOFFrames < 40) + LearningRate += (HUOFFrames < 80 ? 0.05 : 0); - // Match was found -> Update the histogram of the best match - for (int i = HUHistogramBins - 1; i >= 0; --i) - { - PixelData->Histograms[MaxIndex][i] *= (1.0 - LearningRate); - PixelData->Histograms[MaxIndex][i] += LearningRate*(float)histogram[i]; - } + // Match was found -> Update the histogram of the best match + for (int i = HUHistogramBins - 1; i >= 0; --i) + { + PixelData->Histograms[MaxIndex][i] *= (1.0 - LearningRate); + PixelData->Histograms[MaxIndex][i] += LearningRate*(float)histogram[i]; + } - LearningRate = HUWeightsLRate; - if (PixelData->LifeCycle < 100) - LearningRate += (float)(100 - PixelData->LifeCycle) / 100; - else - if (MDMode == md_DLBPHistograms && HUOFFrames != -1 && HUOFFrames < 40) - LearningRate += (HUOFFrames < 80 ? 0.05 : 0); + LearningRate = HUWeightsLRate; + if (PixelData->LifeCycle < 100) + LearningRate += (float)(100 - PixelData->LifeCycle) / 100; + else + if (MDMode == md_DLBPHistograms && HUOFFrames != -1 && HUOFFrames < 40) + LearningRate += (HUOFFrames < 80 ? 0.05 : 0); - // Update the weights of the histograms - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) - { - PixelData->Weights[i] = - (LearningRate*(i == MaxIndex) + (1.0 - LearningRate)*PixelData->Weights[i]); - } + // Update the weights of the histograms + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + PixelData->Weights[i] = + (LearningRate*(i == MaxIndex) + (1.0 - LearningRate)*PixelData->Weights[i]); + } - // Order and select the background histograms - float **Weights = new float*[HUHistogramsPerPixel]; - for (int i = 0; i < HUHistogramsPerPixel; ++i) - Weights[i] = new float[2]; + // Order and select the background histograms + float **Weights = new float*[HUHistogramsPerPixel]; + for (int i = 0; i < HUHistogramsPerPixel; ++i) + Weights[i] = new float[2]; - for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) - { - Weights[i][0] = (float)i; - Weights[i][1] = PixelData->Weights[i]; - } + for (int i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + Weights[i][0] = (float)i; + Weights[i][1] = PixelData->Weights[i]; + } - for (int i1 = HUHistogramsPerPixel - 1; i1 >= 2; --i1) - for (int i = i1; i >= 1; --i) - { - if (Weights[i][1] <= Weights[i - 1][1]) - { - float tmp = Weights[i][0]; - float tmp2 = Weights[i][1]; + for (int i1 = HUHistogramsPerPixel - 1; i1 >= 2; --i1) + for (int i = i1; i >= 1; --i) + { + if (Weights[i][1] <= Weights[i - 1][1]) + { + float tmp = Weights[i][0]; + float tmp2 = Weights[i][1]; - Weights[i][0] = Weights[i - 1][0]; - Weights[i][1] = Weights[i - 1][1]; + Weights[i][0] = Weights[i - 1][0]; + Weights[i][1] = Weights[i - 1][1]; - Weights[i - 1][0] = tmp; - Weights[i - 1][1] = tmp2; - } - } + Weights[i - 1][0] = tmp; + Weights[i - 1][1] = tmp2; + } + } - float Sum = 0; - int i = 0; + float Sum = 0; + int i = 0; - for (i = HUHistogramsPerPixel - 1; i >= 0; --i) - { - Sum += Weights[i][1]; - PixelData->BackgroundHistogram[(int)Weights[i][0]] = true; + for (i = HUHistogramsPerPixel - 1; i >= 0; --i) + { + Sum += Weights[i][1]; + PixelData->BackgroundHistogram[(int)Weights[i][0]] = true; - if (Sum > HUBackgrThres) - break; - } - for (int i1 = i - 1; i1 >= 0; --i1) - { - PixelData->BackgroundHistogram[(int)Weights[i1][0]] = false; - } - delete[] IntersectionResults; - for (int i = 0; i < HUHistogramsPerPixel; ++i) - delete[] Weights[i]; - delete[] Weights; -} + if (Sum > HUBackgrThres) + break; + } + for (int i1 = i - 1; i1 >= 0; --i1) + { + PixelData->BackgroundHistogram[(int)Weights[i1][0]] = false; + } + delete[] IntersectionResults; + for (int i = 0; i < HUHistogramsPerPixel; ++i) + delete[] Weights[i]; + delete[] Weights; + } -void MotionDetection::OpticalFlowCorrection() -{ - IplImage *PreviousGray = NULL, *CurrentGray = NULL; - char* PointsStatus = (char*)cvAlloc(HUOFPointsNumber); - int i = 0, i1 = 0; + void MotionDetection::OpticalFlowCorrection() + { + IplImage *PreviousGray = NULL, *CurrentGray = NULL; + char* PointsStatus = (char*)cvAlloc(HUOFPointsNumber); + int i = 0, i1 = 0; - if (HUOFFrames != -1) - HUOFFrames++; + if (HUOFFrames != -1) + HUOFFrames++; - // Convert the images into grayscale - if (CurrentImage.GetLayers() > 1) - { - CurrentGray = cvCreateImage(cvGetSize(CurrentImage.GetIplImage()), IPL_DEPTH_8U, 1); - cvCvtColor(CurrentImage.GetIplImage(), CurrentGray, CV_BGR2GRAY); - } - else - CurrentGray = (IplImage*)CurrentImage.GetIplImage(); - if (PreviousImage.GetLayers() > 1) - { - PreviousGray = cvCreateImage(cvGetSize(CurrentImage.GetIplImage()), IPL_DEPTH_8U, 1); - cvCvtColor(PreviousImage.GetIplImage(), PreviousGray, CV_BGR2GRAY); - } - else - PreviousGray = (IplImage*)PreviousImage.GetIplImage(); + // Convert the images into grayscale + if (CurrentImage.GetLayers() > 1) + { + CurrentGray = cvCreateImage(cvGetSize(CurrentImage.GetIplImage()), IPL_DEPTH_8U, 1); + cvCvtColor(CurrentImage.GetIplImage(), CurrentGray, CV_BGR2GRAY); + } + else + CurrentGray = (IplImage*)CurrentImage.GetIplImage(); + if (PreviousImage.GetLayers() > 1) + { + PreviousGray = cvCreateImage(cvGetSize(CurrentImage.GetIplImage()), IPL_DEPTH_8U, 1); + cvCvtColor(PreviousImage.GetIplImage(), PreviousGray, CV_BGR2GRAY); + } + else + PreviousGray = (IplImage*)PreviousImage.GetIplImage(); - if (HUOFDataState != ps_Successful) - { - printf("Search new corners\n"); - IplImage* TempEig = cvCreateImage(cvGetSize(CurrentGray), 32, 1); - IplImage* Temp = cvCreateImage(cvGetSize(CurrentGray), 32, 1); - double MinDistance = (CurrentImage.GetWidth() + CurrentImage.GetHeight()) / 20; - HUOFPointsNumber = MaxTrackedPoints = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; - - // Search good trackable points - cvGoodFeaturesToTrack(PreviousGray, TempEig, Temp, - HUOFPoints[0], &HUOFPointsNumber, - 0.01, MinDistance, NULL, 3); - MaxTrackedPoints = HUOFPointsNumber; - // Release temporary images - cvReleaseImage(&TempEig); - cvReleaseImage(&Temp); - // Realloc the point status array - if (PointsStatus) - { - cvFree(&PointsStatus); - PointsStatus = NULL; - } + if (HUOFDataState != ps_Successful) + { + printf("Search new corners\n"); + IplImage* TempEig = cvCreateImage(cvGetSize(CurrentGray), 32, 1); + IplImage* Temp = cvCreateImage(cvGetSize(CurrentGray), 32, 1); + double MinDistance = (CurrentImage.GetWidth() + CurrentImage.GetHeight()) / 20; + HUOFPointsNumber = MaxTrackedPoints = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; + + // Search good trackable points + cvGoodFeaturesToTrack(PreviousGray, TempEig, Temp, + (CvPoint2D32f*)HUOFPoints[0], &HUOFPointsNumber, + 0.01, MinDistance, NULL, 3); + MaxTrackedPoints = HUOFPointsNumber; + // Release temporary images + cvReleaseImage(&TempEig); + cvReleaseImage(&Temp); + // Realloc the point status array + if (PointsStatus) + { + cvFree(&PointsStatus); + PointsStatus = NULL; + } - if (MaxTrackedPoints < 2) - { - HUOFDataState = ps_Initialized; - HUOFPointsNumber = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; - return; - } - else - HUOFDataState = ps_Successful; - PointsStatus = (char*)cvAlloc(HUOFPointsNumber); - } + if (MaxTrackedPoints < 2) + { + HUOFDataState = ps_Initialized; + HUOFPointsNumber = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; + return; + } + else + HUOFDataState = ps_Successful; + PointsStatus = (char*)cvAlloc(HUOFPointsNumber); + } - cvCalcOpticalFlowPyrLK(PreviousGray, CurrentGray, HUOFPrevPyramid, HUOFPyramid, - HUOFPoints[0], HUOFPoints[1], HUOFPointsNumber, - cvSize(10, 10), 3, PointsStatus, NULL, - cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 5, 1), 0); + cvCalcOpticalFlowPyrLK(PreviousGray, CurrentGray, HUOFPrevPyramid, HUOFPyramid, + HUOFPoints[0], HUOFPoints[1], HUOFPointsNumber, + cvSize(10, 10), 3, PointsStatus, NULL, + cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 5, 1), 0); - // Count the distances of the tracked points - int **Distances = new int*[HUOFPointsNumber]; - for (int i = 0; i < HUOFPointsNumber; ++i) - Distances[i] = new int[3]; + // Count the distances of the tracked points + int **Distances = new int*[HUOFPointsNumber]; + for (int i = 0; i < HUOFPointsNumber; ++i) + Distances[i] = new int[3]; - int DistanceMax = 0; - for (i = 0; i < HUOFPointsNumber; ++i) - { - int DiffX = (int)MERound(HUOFPoints[1][i].x - HUOFPoints[0][i].x); - int DiffY = (int)MERound(HUOFPoints[1][i].y - HUOFPoints[0][i].y); - if ((PointsStatus[i] == 1) && !((DiffX == 0) && (DiffY == 0))) - { - bool found = false; - // Create a list from the differences to count them - for (i1 = 0; i1 < DistanceMax; ++i1) - { - if ((Distances[i1][0] == DiffX) && - (Distances[i1][1] == DiffY)) + int DistanceMax = 0; + for (i = 0; i < HUOFPointsNumber; ++i) { - Distances[i1][2]++; - found = true; - break; + int DiffX = (int)MERound(HUOFPoints[1][i].x - HUOFPoints[0][i].x); + int DiffY = (int)MERound(HUOFPoints[1][i].y - HUOFPoints[0][i].y); + if ((PointsStatus[i] == 1) && !((DiffX == 0) && (DiffY == 0))) + { + bool found = false; + // Create a list from the differences to count them + for (i1 = 0; i1 < DistanceMax; ++i1) + { + if ((Distances[i1][0] == DiffX) && + (Distances[i1][1] == DiffY)) + { + Distances[i1][2]++; + found = true; + break; + } + } + if ((!found) && !((DiffX == 0) && (DiffY == 0))) + { + Distances[DistanceMax][0] = (int)MERound(HUOFPoints[1][i].x - HUOFPoints[0][i].x); + Distances[DistanceMax][1] = (int)MERound(HUOFPoints[1][i].y - HUOFPoints[0][i].y); + Distances[DistanceMax][2] = 1; + DistanceMax++; + } + } } - } - if ((!found) && !((DiffX == 0) && (DiffY == 0))) - { - Distances[DistanceMax][0] = (int)MERound(HUOFPoints[1][i].x - HUOFPoints[0][i].x); - Distances[DistanceMax][1] = (int)MERound(HUOFPoints[1][i].y - HUOFPoints[0][i].y); - Distances[DistanceMax][2] = 1; - DistanceMax++; - } - } - } - // Sort the results - for (int i1 = DistanceMax - 1; i1 >= 2; --i1) - { - for (int i = i1; i >= 1; --i) - { - if ((Distances[i][2] > Distances[i - 1][2]) || - ((Distances[i][2] == Distances[i - 1][2]) && - (abs(Distances[i][0]) + abs(Distances[i][1]) < - abs(Distances[i - 1][0]) + abs(Distances[i - 1][1])))) - { - int tmp = Distances[i][0]; - int tmp2 = Distances[i][1]; - int tmp3 = Distances[i][2]; - - Distances[i][0] = Distances[i - 1][0]; - Distances[i][1] = Distances[i - 1][1]; - Distances[i][2] = Distances[i - 1][2]; - - Distances[i - 1][0] = tmp; - Distances[i - 1][1] = tmp2; - Distances[i - 1][2] = tmp3; - } - } - } - - float MoveX = 0.0; - float MoveY = 0.0; - int SampleNums = 0; - float DistanceMeasure = 0.0; + // Sort the results + for (int i1 = DistanceMax - 1; i1 >= 2; --i1) + { + for (int i = i1; i >= 1; --i) + { + if ((Distances[i][2] > Distances[i - 1][2]) || + ((Distances[i][2] == Distances[i - 1][2]) && + (abs(Distances[i][0]) + abs(Distances[i][1]) < + abs(Distances[i - 1][0]) + abs(Distances[i - 1][1])))) + { + int tmp = Distances[i][0]; + int tmp2 = Distances[i][1]; + int tmp3 = Distances[i][2]; - // Calculate the final camera movement - for (i = 0; i < DistanceMax; ++i) - { - if ((Distances[i][2] <= MaxTrackedPoints / 10)) - break; + Distances[i][0] = Distances[i - 1][0]; + Distances[i][1] = Distances[i - 1][1]; + Distances[i][2] = Distances[i - 1][2]; - if (i > 0) - { - DistanceMeasure += (Distances[i][0] - Distances[i - 1][0])*(Distances[i][0] - Distances[i - 1][0]); - DistanceMeasure += (Distances[i][1] - Distances[i - 1][1])*(Distances[i][1] - Distances[i - 1][1]); - } + Distances[i - 1][0] = tmp; + Distances[i - 1][1] = tmp2; + Distances[i - 1][2] = tmp3; + } + } + } - MoveX += Distances[i][0] * Distances[i][2]; - MoveY += Distances[i][1] * Distances[i][2]; - SampleNums += Distances[i][2]; - } + float MoveX = 0.0; + float MoveY = 0.0; + int SampleNums = 0; + float DistanceMeasure = 0.0; - if (SampleNums > 0) - { - MoveX = MERound(MoveX / SampleNums); - MoveY = MERound(MoveY / SampleNums); - } + // Calculate the final camera movement + for (i = 0; i < DistanceMax; ++i) + { + if ((Distances[i][2] <= MaxTrackedPoints / 10)) + break; - if (!((MoveX == 0) && (MoveY == 0)) && - (SampleNums > MaxTrackedPoints / 2)) - { - HUOFCamMovementX += (int)MoveX; - int HUOFCamMovementY = (int)MoveY; - int MaxX = (HUImageWidth / 2) - 1; - int MaxY = HUImageHeight - 1; - /* - printf("-----------\n"); - - for (i = 0; i < DistanceMax; ++i) - printf("%d: %d,%d\n", Distances[i][2], Distances[i][0], Distances[i][1]); - - printf("FINAL: %d,%d,%1.2f\n", (int)MoveX, (int)MoveY, DistanceMeasure); - printf("-----------\n"); - printf("Camera movement: %d,%d,%d (max: %d, current: %d)\n", - SampleNums, HUOFCamMovementX, HUOFCamMovementY, MaxTrackedPoints, HUOFPointsNumber); - */ - HUOFFrames = 0; - HUOFCamMovement = true; - - if (!(HUOFCamMovementY == 0 && HUOFCamMovementX >= -1 && HUOFCamMovementX <= 1)) - { - MEPixelDataType ***PreviousData = new MEPixelDataType**[MaxX + 1]; + if (i > 0) + { + DistanceMeasure += (Distances[i][0] - Distances[i - 1][0])*(Distances[i][0] - Distances[i - 1][0]); + DistanceMeasure += (Distances[i][1] - Distances[i - 1][1])*(Distances[i][1] - Distances[i - 1][1]); + } - for (int i = 0; i < MaxX + 1; ++i) - PreviousData[i] = new MEPixelDataType*[MaxY + 1]; + MoveX += Distances[i][0] * Distances[i][2]; + MoveY += Distances[i][1] * Distances[i][2]; + SampleNums += Distances[i][2]; + } - // Camera movement being happened - for (int y = MaxY; y >= 0; --y) - { - for (int x = MaxX; x >= 0; --x) + if (SampleNums > 0) { - PreviousData[x][y] = NULL; + MoveX = MERound(MoveX / SampleNums); + MoveY = MERound(MoveY / SampleNums); } - } - // Move the LBP data to new locations - for (int y = MaxY; y >= 0; --y) - { - for (int x = MaxX; x >= 0; --x) + if (!((MoveX == 0) && (MoveY == 0)) && + (SampleNums > MaxTrackedPoints / 2)) { - int NewX = x + (HUOFCamMovementX / 2); - int NewY = y + HUOFCamMovementY; - - if (NewX >= 0 && NewX <= MaxX && - NewY >= 0 && NewY <= MaxY) + HUOFCamMovementX += (int)MoveX; + int HUOFCamMovementY = (int)MoveY; + int MaxX = (HUImageWidth / 2) - 1; + int MaxY = HUImageHeight - 1; + /* + printf("-----------\n"); + + for (i = 0; i < DistanceMax; ++i) + printf("%d: %d,%d\n", Distances[i][2], Distances[i][0], Distances[i][1]); + + printf("FINAL: %d,%d,%1.2f\n", (int)MoveX, (int)MoveY, DistanceMeasure); + printf("-----------\n"); + printf("Camera movement: %d,%d,%d (max: %d, current: %d)\n", + SampleNums, HUOFCamMovementX, HUOFCamMovementY, MaxTrackedPoints, HUOFPointsNumber); + */ + HUOFFrames = 0; + HUOFCamMovement = true; + + if (!(HUOFCamMovementY == 0 && HUOFCamMovementX >= -1 && HUOFCamMovementX <= 1)) { - if (HULBPPixelData[NewX][NewY]) + MEPixelDataType ***PreviousData = new MEPixelDataType**[MaxX + 1]; + + for (int i = 0; i < MaxX + 1; ++i) + PreviousData[i] = new MEPixelDataType*[MaxY + 1]; + + // Camera movement being happened + for (int y = MaxY; y >= 0; --y) { - PreviousData[NewX][NewY] = HULBPPixelData[NewX][NewY]; - HULBPPixelData[NewX][NewY] = NULL; - if (PreviousData[x][y]) + for (int x = MaxX; x >= 0; --x) { - HULBPPixelData[NewX][NewY] = PreviousData[x][y]; PreviousData[x][y] = NULL; } - else - { - HULBPPixelData[NewX][NewY] = HULBPPixelData[x][y]; - HULBPPixelData[x][y] = NULL; - } } - else + + // Move the LBP data to new locations + for (int y = MaxY; y >= 0; --y) { - if (PreviousData[x][y]) + for (int x = MaxX; x >= 0; --x) { - HULBPPixelData[NewX][NewY] = PreviousData[x][y]; - PreviousData[x][y] = NULL; + int NewX = x + (HUOFCamMovementX / 2); + int NewY = y + HUOFCamMovementY; + + if (NewX >= 0 && NewX <= MaxX && + NewY >= 0 && NewY <= MaxY) + { + if (HULBPPixelData[NewX][NewY]) + { + PreviousData[NewX][NewY] = HULBPPixelData[NewX][NewY]; + HULBPPixelData[NewX][NewY] = NULL; + if (PreviousData[x][y]) + { + HULBPPixelData[NewX][NewY] = PreviousData[x][y]; + PreviousData[x][y] = NULL; + } + else + { + HULBPPixelData[NewX][NewY] = HULBPPixelData[x][y]; + HULBPPixelData[x][y] = NULL; + } + } + else + { + if (PreviousData[x][y]) + { + HULBPPixelData[NewX][NewY] = PreviousData[x][y]; + PreviousData[x][y] = NULL; + } + else + { + HULBPPixelData[NewX][NewY] = HULBPPixelData[x][y]; + HULBPPixelData[x][y] = NULL; + } + } + } + else + { + if (HULBPPixelData[x][y]) + { + delete[] HULBPPixelData[x][y]->PreviousHistogram; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + delete[] HULBPPixelData[x][y]->Histograms[i2]; + delete[] HULBPPixelData[x][y]->Histograms; + delete[] HULBPPixelData[x][y]->BackgroundHistogram; + delete[] HULBPPixelData[x][y]->Weights; + delete HULBPPixelData[x][y]; + HULBPPixelData[x][y] = NULL; + } + } } - else + } + + // Release unused data + for (int y = MaxY; y >= 0; --y) + { + for (int x = MaxX; x >= 0; --x) { - HULBPPixelData[NewX][NewY] = HULBPPixelData[x][y]; - HULBPPixelData[x][y] = NULL; + if (PreviousData[x][y]) + { + delete[] PreviousData[x][y]->PreviousHistogram; + for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) + delete[] PreviousData[x][y]->Histograms[i2]; + delete[] PreviousData[x][y]->Histograms; + delete[] PreviousData[x][y]->BackgroundHistogram; + delete[] PreviousData[x][y]->Weights; + delete PreviousData[x][y]; + PreviousData[x][y] = NULL; + } } } + + HUOFCamMovementX = HUOFCamMovementX % 1; + + for (int i = 0; i < MaxX + 1; ++i) + delete[] PreviousData[i]; + delete[] PreviousData; } - else + } + + i1 = 0; + // Throw the missed points away + for (i = 0; i < HUOFPointsNumber; ++i) + { + if (PointsStatus[i] == 1) { - if (HULBPPixelData[x][y]) - { - delete[] HULBPPixelData[x][y]->PreviousHistogram; - for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) - delete[] HULBPPixelData[x][y]->Histograms[i2]; - delete[] HULBPPixelData[x][y]->Histograms; - delete[] HULBPPixelData[x][y]->BackgroundHistogram; - delete[] HULBPPixelData[x][y]->Weights; - delete HULBPPixelData[x][y]; - HULBPPixelData[x][y] = NULL; - } + HUOFPoints[0][i1] = HUOFPoints[1][i]; + i1++; } } + HUOFPointsNumber -= i + 1 - i1; + + if (HUOFPointsNumber < MaxTrackedPoints / 2) + { + printf("Re-init the optical flow\n"); + HUOFDataState = ps_Initialized; + HUOFPointsNumber = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; + } + // Free memory + if (PreviousGray != PreviousImage.GetIplImage()) + cvReleaseImage(&PreviousGray); + if (CurrentGray != CurrentImage.GetIplImage()) + cvReleaseImage(&CurrentGray); + cvFree(&PointsStatus); + + for (int i = 0; i < HUOFPointsNumber; ++i) + delete[] Distances[i]; + delete[] Distances; } - // Release unused data - for (int y = MaxY; y >= 0; --y) + void MotionDetection::GetMotionsMaskHU(MEImage& mask_image) { - for (int x = MaxX; x >= 0; --x) + if (MDDataState != ps_Successful) + { + mask_image.Clear(); + return; + } + + // Reallocate the mask image if needs be + if ((HUImageWidth + HUHistogramArea - 1 != mask_image.GetWidth()) || + (HUImageHeight + HUHistogramArea - 1 != mask_image.GetHeight()) || + (mask_image.GetLayers() != 1)) { - if (PreviousData[x][y]) + mask_image.Realloc(HUImageWidth + HUHistogramArea - 1, + HUImageHeight + HUHistogramArea - 1, 1); + } + mask_image.Clear(); + // Generate the mask image + unsigned char* MaskImgData = mask_image.GetImageData(); + int RowStart = (mask_image.GetHeight() - HUHistogramArea / 2)*mask_image.GetRowWidth(); + int RowWidth = mask_image.GetRowWidth(); + + // Generate a graph about the histogram data + Graph::node_id **Nodes = new Graph::node_id*[HUImageWidth / 2]; + for (int i = 0; i < HUImageWidth / 2; ++i) + Nodes[i] = new Graph::node_id[HUImageHeight]; + Graph *LBPGraph = new Graph(); + + for (int x = (HUImageWidth / 2) - 1; x >= 0; --x) + { + for (int y = HUImageHeight - 1; y >= 0; --y) { - delete[] PreviousData[x][y]->PreviousHistogram; - for (int i2 = 0; i2 < HUHistogramsPerPixel; ++i2) - delete[] PreviousData[x][y]->Histograms[i2]; - delete[] PreviousData[x][y]->Histograms; - delete[] PreviousData[x][y]->BackgroundHistogram; - delete[] PreviousData[x][y]->Weights; - delete PreviousData[x][y]; - PreviousData[x][y] = NULL; + Nodes[x][y] = LBPGraph->add_node(); } } - } - HUOFCamMovementX = HUOFCamMovementX % 1; - - for (int i = 0; i < MaxX + 1; ++i) - delete[] PreviousData[i]; - delete[] PreviousData; - } - } + for (int x = (HUImageWidth / 2) - 1; x >= 0; --x) + { + for (int y = HUImageHeight - 1; y >= 0; --y) + { + LBPGraph->set_tweights(Nodes[x][y], 1, + (short int)(HUMinCutWeight*(1 - HULBPPixelData[x][y]->BackgroundRate))); - i1 = 0; - // Throw the missed points away - for (i = 0; i < HUOFPointsNumber; ++i) - { - if (PointsStatus[i] == 1) - { - HUOFPoints[0][i1] = HUOFPoints[1][i]; - i1++; - } - } - HUOFPointsNumber -= i + 1 - i1; + if (x > 0 && y > 0) + { + LBPGraph->add_edge(Nodes[x][y], Nodes[x - 1][y], 1, 1); + LBPGraph->add_edge(Nodes[x][y], Nodes[x][y - 1], 1, 1); + } + } + } - if (HUOFPointsNumber < MaxTrackedPoints / 2) - { - printf("Re-init the optical flow\n"); - HUOFDataState = ps_Initialized; - HUOFPointsNumber = CurrentImage.GetWidth()*CurrentImage.GetHeight() / 1000; - } - // Free memory - if (PreviousGray != PreviousImage.GetIplImage()) - cvReleaseImage(&PreviousGray); - if (CurrentGray != CurrentImage.GetIplImage()) - cvReleaseImage(&CurrentGray); - cvFree(&PointsStatus); - - for (int i = 0; i < HUOFPointsNumber; ++i) - delete[] Distances[i]; - delete[] Distances; -} + LBPGraph->maxflow(); -void MotionDetection::GetMotionsMaskHU(MEImage& mask_image) -{ - if (MDDataState != ps_Successful) - { - mask_image.Clear(); - return; - } + for (int x = (HUImageWidth / 2) - 1; x >= 0; --x) + { + for (int y = HUImageHeight - 1; y >= 0; --y) + { + if (LBPGraph->what_segment(Nodes[x][y]) == Graph::SINK) + HULBPPixelData[x][y]->BackgroundRate = 0.0; + else + HULBPPixelData[x][y]->BackgroundRate = 1.0; + } + } - // Reallocate the mask image if needs be - if ((HUImageWidth + HUHistogramArea - 1 != mask_image.GetWidth()) || - (HUImageHeight + HUHistogramArea - 1 != mask_image.GetHeight()) || - (mask_image.GetLayers() != 1)) - { - mask_image.Realloc(HUImageWidth + HUHistogramArea - 1, - HUImageHeight + HUHistogramArea - 1, 1); - } - mask_image.Clear(); - // Generate the mask image - unsigned char* MaskImgData = mask_image.GetImageData(); - int RowStart = (mask_image.GetHeight() - HUHistogramArea / 2)*mask_image.GetRowWidth(); - int RowWidth = mask_image.GetRowWidth(); - - // Generate a graph about the histogram data - Graph::node_id **Nodes = new Graph::node_id*[HUImageWidth / 2]; - for (int i = 0; i < HUImageWidth / 2; ++i) - Nodes[i] = new Graph::node_id[HUImageHeight]; - Graph *LBPGraph = new Graph(); - - for (int x = (HUImageWidth / 2) - 1; x >= 0; --x) - { - for (int y = HUImageHeight - 1; y >= 0; --y) - { - Nodes[x][y] = LBPGraph->add_node(); - } - } + delete LBPGraph; + LBPGraph = NULL; + for (int y = HUImageHeight - 1; y >= 0; --y) + { + for (int x = HUImageWidth - 1; x >= 0; --x) + { + if (y % 2 == (x + 1) % 2) + MaskImgData[RowStart + x + (HUHistogramArea / 2)] = + (HULBPPixelData[x / 2][y]->BackgroundRate == 0.0) ? 255 : 0; + else + { + MaskImgData[RowStart + x + (HUHistogramArea / 2)] = + ((int)(x > 1 && HULBPPixelData[(x / 2) - 1][y]->BackgroundRate == 0.0) + + (int)(x < mask_image.GetWidth() - HUHistogramArea - 1 && + HULBPPixelData[(x / 2) + 1][y]->BackgroundRate == 0.0) + + (int)(y > 0 && HULBPPixelData[x / 2][y - 1]->BackgroundRate == 0.0) + + (int)(y < mask_image.GetHeight() - HUHistogramArea && + HULBPPixelData[x / 2][y + 1]->BackgroundRate == 0.0) > 1) + ? 255 : 0; + } + } + RowStart -= RowWidth; + } - for (int x = (HUImageWidth / 2) - 1; x >= 0; --x) - { - for (int y = HUImageHeight - 1; y >= 0; --y) - { - LBPGraph->set_tweights(Nodes[x][y], 1, - (short int)(HUMinCutWeight*(1 - HULBPPixelData[x][y]->BackgroundRate))); + cvFloodFill(mask_image.GetIplImage(), cvPoint(0, 0), cvScalar(128, 128, 128, 128), + cvScalar(0, 0, 0, 0), cvScalar(0, 0, 0, 0)); + for (int i = ((IplImage*)mask_image.GetIplImage())->widthStep*((IplImage*)mask_image.GetIplImage())->height - 1; i >= 0; --i) + { + if (MaskImgData[i] == 128) + { + MaskImgData[i] = 0; + } + else + { + if (MaskImgData[i] == 0) + { + MaskImgData[i] = 255; + } + } + } + // Apply an erode operator + mask_image.Erode(1); - if (x > 0 && y > 0) - { - LBPGraph->add_edge(Nodes[x][y], Nodes[x - 1][y], 1, 1); - LBPGraph->add_edge(Nodes[x][y], Nodes[x][y - 1], 1, 1); + for (int i = 0; i < HUImageWidth / 2; ++i) + delete[] Nodes[i]; + delete[] Nodes; } - } - } - LBPGraph->maxflow(); + void MotionDetection::SetSampleMaskHU(SampleMaskType mask_type, int desiredarea) + { + if (HUMaskColumnAddDel == NULL || HUMaskRowAddDel == NULL) + { + printf("Auxiliary variables are NULL\n"); + return; + } - for (int x = (HUImageWidth / 2) - 1; x >= 0; --x) - { - for (int y = HUImageHeight - 1; y >= 0; --y) - { - if (LBPGraph->what_segment(Nodes[x][y]) == Graph::SINK) - HULBPPixelData[x][y]->BackgroundRate = 0.0; - else - HULBPPixelData[x][y]->BackgroundRate = 1.0; - } - } + // Generate a mask for computing the histograms + IplImage *MaskImage = cvCreateImage(cvSize(HUHistogramArea, HUHistogramArea), 8, 1); + int DesiredArea = desiredarea <= 0 ? HUHistogramBins * 2 : desiredarea; - delete LBPGraph; - LBPGraph = NULL; - for (int y = HUImageHeight - 1; y >= 0; --y) - { - for (int x = HUImageWidth - 1; x >= 0; --x) - { - if (y % 2 == (x + 1) % 2) - MaskImgData[RowStart + x + (HUHistogramArea / 2)] = - (HULBPPixelData[x / 2][y]->BackgroundRate == 0.0) ? 255 : 0; - else - { - MaskImgData[RowStart + x + (HUHistogramArea / 2)] = - ((int)(x > 1 && HULBPPixelData[(x / 2) - 1][y]->BackgroundRate == 0.0) + - (int)(x < mask_image.GetWidth() - HUHistogramArea - 1 && - HULBPPixelData[(x / 2) + 1][y]->BackgroundRate == 0.0) + - (int)(y > 0 && HULBPPixelData[x / 2][y - 1]->BackgroundRate == 0.0) + - (int)(y < mask_image.GetHeight() - HUHistogramArea && - HULBPPixelData[x / 2][y + 1]->BackgroundRate == 0.0) > 1) - ? 255 : 0; - } - } - RowStart -= RowWidth; - } + int **CalculationMask = new int*[HUHistogramArea]; + for (int i = 0; i < HUHistogramArea; ++i) + CalculationMask[i] = new int[HUHistogramArea]; - cvFloodFill(mask_image.GetIplImage(), cvPoint(0, 0), cvScalar(128, 128, 128, 128), - cvScalar(0, 0, 0, 0), cvScalar(0, 0, 0, 0)); - for (int i = ((IplImage*)mask_image.GetIplImage())->widthStep*((IplImage*)mask_image.GetIplImage())->height - 1; i >= 0; --i) - { - if (MaskImgData[i] == 128) - { - MaskImgData[i] = 0; - } - else - { - if (MaskImgData[i] == 0) - { - MaskImgData[i] = 255; - } - } - } - // Apply an erode operator - mask_image.Erode(1); + int SquareSide = (int)MERound(sqrt((float)DesiredArea)); + int CircleRadius = (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)); + int EllipseA = (int)MERound(HUHistogramArea / 2 + 1); + int EllipseB = (int)MERound(DesiredArea / (EllipseA*1.2*ME_PI_VALUE)); - for (int i = 0; i < HUImageWidth / 2; ++i) - delete[] Nodes[i]; - delete[] Nodes; -} + cvSetZero(MaskImage); -void MotionDetection::SetSampleMaskHU(SampleMaskType mask_type, int desiredarea) -{ - if (HUMaskColumnAddDel == NULL || HUMaskRowAddDel == NULL) - { - printf("Auxiliary variables are NULL\n"); - return; - } + switch (mask_type) + { + case sm_Circle: + cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), + CircleRadius, CV_RGB(1, 1, 1), -1); + break; - // Generate a mask for computing the histograms - IplImage *MaskImage = cvCreateImage(cvSize(HUHistogramArea, HUHistogramArea), 8, 1); - int DesiredArea = desiredarea <= 0 ? HUHistogramBins * 2 : desiredarea; + case sm_Square: + cvRectangle(MaskImage, + cvPoint(HUHistogramArea / 2 - SquareSide / 2, HUHistogramArea / 2 - SquareSide / 2), + cvPoint(HUHistogramArea / 2 + SquareSide / 2, HUHistogramArea / 2 + SquareSide / 2), + CV_RGB(1, 1, 1), -1); + break; - int **CalculationMask = new int*[HUHistogramArea]; - for (int i = 0; i < HUHistogramArea; ++i) - CalculationMask[i] = new int[HUHistogramArea]; + case sm_Ellipse: + cvEllipse(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), + cvSize(EllipseA, EllipseB), 45, 0, 360, + CV_RGB(1, 1, 1), -1); + break; - int SquareSide = (int)MERound(sqrt((float)DesiredArea)); - int CircleRadius = (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)); - int EllipseA = (int)MERound(HUHistogramArea / 2 + 1); - int EllipseB = (int)MERound(DesiredArea / (EllipseA*1.2*ME_PI_VALUE)); + case sm_RandomPixels: + HUSamplePixels = 0; + while (HUSamplePixels != DesiredArea) + { + int i = rand() % HUHistogramArea; + int j = rand() % HUHistogramArea; - cvSetZero(MaskImage); + if (MaskImage->imageData[i*MaskImage->widthStep + j] == 0) + { + MaskImage->imageData[i*MaskImage->widthStep + j] = 1; + HUSamplePixels++; + } + } + break; - switch (mask_type) - { - case sm_Circle: - cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), - CircleRadius, CV_RGB(1, 1, 1), -1); - break; - - case sm_Square: - cvRectangle(MaskImage, - cvPoint(HUHistogramArea / 2 - SquareSide / 2, HUHistogramArea / 2 - SquareSide / 2), - cvPoint(HUHistogramArea / 2 + SquareSide / 2, HUHistogramArea / 2 + SquareSide / 2), - CV_RGB(1, 1, 1), -1); - break; - - case sm_Ellipse: - cvEllipse(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), - cvSize(EllipseA, EllipseB), 45, 0, 360, - CV_RGB(1, 1, 1), -1); - break; - - case sm_RandomPixels: - HUSamplePixels = 0; - while (HUSamplePixels != DesiredArea) - { - int i = rand() % HUHistogramArea; - int j = rand() % HUHistogramArea; + default: + cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), + (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)), CV_RGB(1, 1, 1), -1); + break; + } - if (MaskImage->imageData[i*MaskImage->widthStep + j] == 0) - { - MaskImage->imageData[i*MaskImage->widthStep + j] = 1; - HUSamplePixels++; - } - } - break; + HUSamplePixels = 0; + //memset(CalculationMask, 0, sizeof(CalculationMask)); - default: - cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2), - (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)), CV_RGB(1, 1, 1), -1); - break; - } + for (int i = 0; i < HUHistogramArea; ++i) + { + for (int i1 = 0; i1 < HUHistogramArea; ++i1) + { + if (MaskImage->imageData[i*MaskImage->widthStep + i1] != 0) + { + HUSamplePixels++; + CalculationMask[i][i1] = 1; + } + else { + CalculationMask[i][i1] = 0; + } + } + } - HUSamplePixels = 0; - //memset(CalculationMask, 0, sizeof(CalculationMask)); + // Fill an auxiliary variable for fast computing with data + for (int i = 0; i < HUHistogramArea; ++i) + { + HUMaskColumnAddDel[i][0] = -1; + for (int i1 = 0; i1 < HUHistogramArea; ++i1) + { + if (CalculationMask[i][i1] != 0) + { + HUMaskColumnAddDel[i][0] = i1; + break; + } + } + HUMaskColumnAddDel[i][1] = -1; + for (int i1 = HUHistogramArea - 1; i1 >= 0; --i1) + { + if (CalculationMask[i][i1] != 0) + { + HUMaskColumnAddDel[i][1] = i1 + 1; + break; + } + } + } - for (int i = 0; i < HUHistogramArea; ++i) - { - for (int i1 = 0; i1 < HUHistogramArea; ++i1) - { - if (MaskImage->imageData[i*MaskImage->widthStep + i1] != 0) - { - HUSamplePixels++; - CalculationMask[i][i1] = 1; - } - else { - CalculationMask[i][i1] = 0; - } - } - } + // Fill an auxiliary variable for fast computing with data + for (int i = 0; i < HUHistogramArea; ++i) + { + HUMaskRowAddDel[i][0] = -1; + for (int i1 = 0; i1 < HUHistogramArea; ++i1) + { + if (CalculationMask[i1][i] != 0) + { + HUMaskRowAddDel[i][0] = i1; + break; + } + } + HUMaskRowAddDel[i][1] = -1; + for (int i1 = HUHistogramArea - 1; i1 >= 0; --i1) + { + if (CalculationMask[i1][i] != 0) + { + HUMaskRowAddDel[i][1] = i1 + 1; + break; + } + } + } - // Fill an auxiliary variable for fast computing with data - for (int i = 0; i < HUHistogramArea; ++i) - { - HUMaskColumnAddDel[i][0] = -1; - for (int i1 = 0; i1 < HUHistogramArea; ++i1) - { - if (CalculationMask[i][i1] != 0) - { - HUMaskColumnAddDel[i][0] = i1; - break; - } - } - HUMaskColumnAddDel[i][1] = -1; - for (int i1 = HUHistogramArea - 1; i1 >= 0; --i1) - { - if (CalculationMask[i][i1] != 0) - { - HUMaskColumnAddDel[i][1] = i1 + 1; - break; - } - } - } + // Freeing memory + cvReleaseImage(&MaskImage); - // Fill an auxiliary variable for fast computing with data - for (int i = 0; i < HUHistogramArea; ++i) - { - HUMaskRowAddDel[i][0] = -1; - for (int i1 = 0; i1 < HUHistogramArea; ++i1) - { - if (CalculationMask[i1][i] != 0) - { - HUMaskRowAddDel[i][0] = i1; - break; - } - } - HUMaskRowAddDel[i][1] = -1; - for (int i1 = HUHistogramArea - 1; i1 >= 0; --i1) - { - if (CalculationMask[i1][i] != 0) - { - HUMaskRowAddDel[i][1] = i1 + 1; - break; + for (int i = 0; i < HUHistogramArea; ++i) + delete[] CalculationMask[i]; + delete[] CalculationMask; } } } - - // Freeing memory - cvReleaseImage(&MaskImage); - - for (int i = 0; i < HUHistogramArea; ++i) - delete[] CalculationMask[i]; - delete[] CalculationMask; } #endif diff --git a/src/algorithms/LBP_MRF/MotionDetection.hpp b/src/algorithms/LBP_MRF/MotionDetection.hpp index ff47011c211449f39529200999be93dd7fc9219c..f7720e5d2198275130c20c7762760fa91b129d81 100644 --- a/src/algorithms/LBP_MRF/MotionDetection.hpp +++ b/src/algorithms/LBP_MRF/MotionDetection.hpp @@ -3,373 +3,384 @@ #include "opencv2/core/version.hpp" #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 +#include <opencv2/imgproc/types_c.h> + #include "MEDefs.hpp" #include "MEImage.hpp" -class CvBGStatModel; -struct CvPoint2D32f; - -// Struct for histogram update data of a pixel -struct MEPixelDataType; - -/** - * MotionDetection - * @brief Extract moving objects from image sequence - */ -class MotionDetection +namespace bgslibrary { -public: - - /// Types of motion detection - typedef enum - { - md_Min = 0, /*!< Minimum value */ - md_NotDefined = md_Min, /*!< Not defined */ - md_DLBPHistograms, /*!< Dynamic LBP */ - md_LBPHistograms, /*!< Normal LBP */ - md_Max = md_LBPHistograms /*!< Maximum value */ - } DetectorType; - - /// Types of sample mask - typedef enum - { - sm_Min = 0, /*!< Minimum value */ - sm_Circle = sm_Min, /*!< Circle */ - sm_Square, /*!< Square */ - sm_Ellipse, /*!< Ellipse */ - sm_RandomPixels, /*!< Random pixels */ - sm_Max = sm_RandomPixels /*!< Maximum value */ - } SampleMaskType; - - /// Types of motion detection parameters - typedef enum + namespace algorithms { - mdp_Min = 0, /*!< Minimum value */ - mdp_HUProximityThreshold = mdp_Min, /*!< Proximity threshold */ - mdp_HUBackgroundThreshold, /*!< Background threshold */ - mdp_HUHistogramLearningRate, /*!< Histogram learning rate */ - mdp_HUWeightsLearningRate, /*!< Weights learning rate */ - mdp_HUMinCutWeight, /*!< Minimum cut weight */ - mdp_HUDesiredSamplePixels, /*!< Desired sample pixels */ - mdp_HUHistogramsPerPixel, /*!< Histogram per pixel */ - mdp_HUHistogramArea, /*!< Histogram area */ - mdp_HUHistogramBins, /*!< Histogram bins */ - mdp_HUColorSpace, /*!< Color space */ - mdp_HULBPMode, /*!< LBP mode */ - mdp_Max = mdp_HULBPMode /*!< Maximum value */ - } ParametersType; - - /*! - * @brief Class constructor - * - * @param mode Detection mode - * - * Class constructor with the possibility to specify the detection mode. - * The default is dynamic LBP. - * - */ - - MotionDetection(DetectorType mode = md_DLBPHistograms); - /// Destructor of class - ~MotionDetection(); - - /* - ------------------------------------------------------------------- - Motion methods - ------------------------------------------------------------------- - */ - - /*! - * @brief Set the mode of the motion detection - * - * @param newmode New mode of detection - * - * Set the mode of the motion detection. - * - */ - - void SetMode(DetectorType newmode); - - /*! - * @brief Get a parameter value of the motion detection - * - * @param param Parameter of the detection - * - * @return Queried value - * - * Get the value of a parameter of the motion detection. - * - */ - - float GetParameter(ParametersType param) const; - - /*! - * @brief Set a parameter of the motion detection - * - * @param param Parameter of the detection - * @param value New value - * - * Set a new value to a parameter of the motion detection. - * - */ - - void SetParameter(ParametersType param, float value); - - /*! - * @brief Detect the motions on an image - * - * @param image Image to process - * - * The function designed to search motions in image streams - * thus it needs to process the image sequence frame by frame. - * It processes an image from this sequence and searches moving blobs - * on that. - * - */ - - void DetectMotions(MEImage& image); - - /*! - * @brief Get mask image with detected motions - * - * @param mask_image Result mask image - * - * The function creates a mask image on which the objects are - * indicated by white blobs. - * - */ - - void GetMotionsMask(MEImage& mask_image); - - /*! - * @brief Calculate results of the motion detection - * - * @param referenceimage Reference mask image - * @param tnegatives True negative pixels - * @param tpositives True positive pixels - * @param ttnegatives Total true negative pixels - * @param ttpositives Total true positive pixels - * - * The function calculates the results of the motion detection - * between the current motion mask and a given reference mask - * image. - * - */ - - void CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives, - int& ttnegatives, int& ttpositives); - -private: - - /*! - * @brief Release data structures - * - * Function releases the data structures. - * - */ - - void ReleaseData(); - - /* - ------------------------------------------------------------------- - Histogram update methods - ------------------------------------------------------------------- - */ - - /*! - * @brief Init HU data structures - * - * @param imagewidth Image width for HU to process - * @param imageheight Image height for HU to process - * - * Function allocates/re-allocates the HU data structures and they - * are cleared if needs be. - * - */ - - void InitHUData(int imagewidth, int imageheight); - - /*! - * @brief Init HU optical flow data structures - * - * @param imagewidth Image width for HU to process - * @param imageheight Image height for HU to process - * - * Function allocates/re-allocates the HU optical flow - * data structures. - * - */ - - void InitHUOFData(int imagewidth, int imageheight); - - /*! - * @brief Release HU data structures - * - * Function releases the HU data structures. - * - */ - - void ReleaseHUData(); - - /*! - * @brief Release HU optical flow data structures - * - * Function releases the HU optical flow data structures. - * - */ - - void ReleaseHUOFData(); - - /*! - * @brief Clear HU data structures - * - * Function clears the HU data structures. - * - */ - - void ClearHUData(); - - /*! - * @brief Get mask image with detected motions by histogram update - * - * @param mask_image Result mask image - * - * The function creates a mask image on which the objects are - * indicated by white blobs. - * - */ - - void GetMotionsMaskHU(MEImage& mask_image); - - /*! - * @brief Set the sample mask - * - * @param mask_type Type of the mask - * @param desiredarea The desired area size of the mask - * - * The function creates a sample mask with a desired form - * (square, circle, ellipse, random pixels) and size. - * - */ - - void SetSampleMaskHU(SampleMaskType mask_type, int desiredarea); - - /*! - * @brief Detect the motions on an image with histogram update - * - * @param image Image to process - * - * The function designed to search motions in image streams - * thus it needs to process the image sequence frame by frame. - * It processes an image from this sequence and searches moving blobs - * on that. It uses histogram update method. - * - */ - - void DetectMotionsHU(MEImage& image); - - /*! - * @brief Update a model - * - * @param image Image to process - * @param model Model to update - * - * The function updates a histogram model of the image. - * - */ - - void UpdateModelHU(MEImage& image, MEPixelDataType*** model); - - /*! - * @brief Update the HU data structure for one pixel - * - * @param pixeldata Pixel data - * @param histogram Current histogram - * - * This method updates the HU data for one pixel. - * - */ - - void UpdateHUPixelData(MEPixelDataType* pixeldata, const float *histogram); - - /*! - * @brief Optical flow correction of the camera movements - * - * The function trackes some points on the scene if a camera movement is - * detected, then the LBP pixel data is corrected. - * - */ - - void OpticalFlowCorrection(); - -private: - // GENERAL VARIABLES - /// Motion detection type - DetectorType MDMode; - /// State of the data structures - MEProcessStateType MDDataState; - /// Processed number in the image sequence - int Frames; - /// Store the current image - MEImage CurrentImage; - /// Store the previous image - MEImage PreviousImage; - /// Store the current mask image - MEImage MaskImage; - /// Store the current mask image - bool ReadyMask; - // HISTOGRAM UPDATE VARIABLES - /// Color space (-1 = no conversion) - int HUColorSpace; - /// LBP calculation mode (-1 = no conversion) - int HULBPMode; - /// Histograms per pixel - int HUHistogramsPerPixel; - /// Histogram area - int HUHistogramArea; - /// Histogram bins - int HUHistogramBins; - /// Image width for histogram update - int HUImageWidth; - /// Image height for histogram update - int HUImageHeight; - /// Data of the LBP histograms - MEPixelDataType ***HULBPPixelData; - /// Store the previous blue layer - MEImage PreviousBlueLayer; - /// Histogram proximity threshold - float HUPrThres; - /// Background selection threshold - float HUBackgrThres; - /// Histogram learning rate - float HUHistLRate; - /// Weights learning rate - float HUWeightsLRate; - /// Pixel number used to calculate the histograms - int HUSamplePixels; - /// The desired pixel number used to calculate the histograms (-1 = Auto) - int HUDesiredSamplePixels; - /// Min cut weight - float HUMinCutWeight; - /// Auxiliary variable for computing the histograms in a column - int **HUMaskColumnAddDel; - /// Auxiliary variable for computing the histograms in a row - int **HUMaskRowAddDel; - // OPTICAL FLOW VARIABLES - /// State of the optical flow - MEProcessStateType HUOFDataState; - /// Number of the tracked points with optical flow - int HUOFPointsNumber; - /// Tracked points - CvPoint2D32f* HUOFPoints[2]; - /// The rest x component of previous camera movement - int HUOFCamMovementX; - /// Maximum tracked points detected in one cycle - int MaxTrackedPoints; - /// Processed frame number with optical flow in the image sequence - int HUOFFrames; - /// Indicator of a new camera movement - bool HUOFCamMovement; -}; + namespace lbp_mrf + { + class CvBGStatModel; + //struct CvPoint2D32f; + + // Struct for histogram update data of a pixel + struct MEPixelDataType; + + /** + * MotionDetection + * @brief Extract moving objects from image sequence + */ + class MotionDetection + { + public: + + /// Types of motion detection + typedef enum + { + md_Min = 0, /*!< Minimum value */ + md_NotDefined = md_Min, /*!< Not defined */ + md_DLBPHistograms, /*!< Dynamic LBP */ + md_LBPHistograms, /*!< Normal LBP */ + md_Max = md_LBPHistograms /*!< Maximum value */ + } DetectorType; + + /// Types of sample mask + typedef enum + { + sm_Min = 0, /*!< Minimum value */ + sm_Circle = sm_Min, /*!< Circle */ + sm_Square, /*!< Square */ + sm_Ellipse, /*!< Ellipse */ + sm_RandomPixels, /*!< Random pixels */ + sm_Max = sm_RandomPixels /*!< Maximum value */ + } SampleMaskType; + + /// Types of motion detection parameters + typedef enum + { + mdp_Min = 0, /*!< Minimum value */ + mdp_HUProximityThreshold = mdp_Min, /*!< Proximity threshold */ + mdp_HUBackgroundThreshold, /*!< Background threshold */ + mdp_HUHistogramLearningRate, /*!< Histogram learning rate */ + mdp_HUWeightsLearningRate, /*!< Weights learning rate */ + mdp_HUMinCutWeight, /*!< Minimum cut weight */ + mdp_HUDesiredSamplePixels, /*!< Desired sample pixels */ + mdp_HUHistogramsPerPixel, /*!< Histogram per pixel */ + mdp_HUHistogramArea, /*!< Histogram area */ + mdp_HUHistogramBins, /*!< Histogram bins */ + mdp_HUColorSpace, /*!< Color space */ + mdp_HULBPMode, /*!< LBP mode */ + mdp_Max = mdp_HULBPMode /*!< Maximum value */ + } ParametersType; + + /*! + * @brief Class constructor + * + * @param mode Detection mode + * + * Class constructor with the possibility to specify the detection mode. + * The default is dynamic LBP. + * + */ + + MotionDetection(DetectorType mode = md_DLBPHistograms); + /// Destructor of class + ~MotionDetection(); + + /* + ------------------------------------------------------------------- + Motion methods + ------------------------------------------------------------------- + */ + + /*! + * @brief Set the mode of the motion detection + * + * @param newmode New mode of detection + * + * Set the mode of the motion detection. + * + */ + + void SetMode(DetectorType newmode); + + /*! + * @brief Get a parameter value of the motion detection + * + * @param param Parameter of the detection + * + * @return Queried value + * + * Get the value of a parameter of the motion detection. + * + */ + + float GetParameter(ParametersType param) const; + + /*! + * @brief Set a parameter of the motion detection + * + * @param param Parameter of the detection + * @param value New value + * + * Set a new value to a parameter of the motion detection. + * + */ + + void SetParameter(ParametersType param, float value); + + /*! + * @brief Detect the motions on an image + * + * @param image Image to process + * + * The function designed to search motions in image streams + * thus it needs to process the image sequence frame by frame. + * It processes an image from this sequence and searches moving blobs + * on that. + * + */ + + void DetectMotions(MEImage& image); + + /*! + * @brief Get mask image with detected motions + * + * @param mask_image Result mask image + * + * The function creates a mask image on which the objects are + * indicated by white blobs. + * + */ + + void GetMotionsMask(MEImage& mask_image); + + /*! + * @brief Calculate results of the motion detection + * + * @param referenceimage Reference mask image + * @param tnegatives True negative pixels + * @param tpositives True positive pixels + * @param ttnegatives Total true negative pixels + * @param ttpositives Total true positive pixels + * + * The function calculates the results of the motion detection + * between the current motion mask and a given reference mask + * image. + * + */ + + void CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives, + int& ttnegatives, int& ttpositives); + + private: + + /*! + * @brief Release data structures + * + * Function releases the data structures. + * + */ + + void ReleaseData(); + + /* + ------------------------------------------------------------------- + Histogram update methods + ------------------------------------------------------------------- + */ + + /*! + * @brief Init HU data structures + * + * @param imagewidth Image width for HU to process + * @param imageheight Image height for HU to process + * + * Function allocates/re-allocates the HU data structures and they + * are cleared if needs be. + * + */ + + void InitHUData(int imagewidth, int imageheight); + + /*! + * @brief Init HU optical flow data structures + * + * @param imagewidth Image width for HU to process + * @param imageheight Image height for HU to process + * + * Function allocates/re-allocates the HU optical flow + * data structures. + * + */ + + void InitHUOFData(int imagewidth, int imageheight); + + /*! + * @brief Release HU data structures + * + * Function releases the HU data structures. + * + */ + + void ReleaseHUData(); + + /*! + * @brief Release HU optical flow data structures + * + * Function releases the HU optical flow data structures. + * + */ + + void ReleaseHUOFData(); + + /*! + * @brief Clear HU data structures + * + * Function clears the HU data structures. + * + */ + + void ClearHUData(); + + /*! + * @brief Get mask image with detected motions by histogram update + * + * @param mask_image Result mask image + * + * The function creates a mask image on which the objects are + * indicated by white blobs. + * + */ + + void GetMotionsMaskHU(MEImage& mask_image); + + /*! + * @brief Set the sample mask + * + * @param mask_type Type of the mask + * @param desiredarea The desired area size of the mask + * + * The function creates a sample mask with a desired form + * (square, circle, ellipse, random pixels) and size. + * + */ + + void SetSampleMaskHU(SampleMaskType mask_type, int desiredarea); + + /*! + * @brief Detect the motions on an image with histogram update + * + * @param image Image to process + * + * The function designed to search motions in image streams + * thus it needs to process the image sequence frame by frame. + * It processes an image from this sequence and searches moving blobs + * on that. It uses histogram update method. + * + */ + + void DetectMotionsHU(MEImage& image); + + /*! + * @brief Update a model + * + * @param image Image to process + * @param model Model to update + * + * The function updates a histogram model of the image. + * + */ + + void UpdateModelHU(MEImage& image, MEPixelDataType*** model); + + /*! + * @brief Update the HU data structure for one pixel + * + * @param pixeldata Pixel data + * @param histogram Current histogram + * + * This method updates the HU data for one pixel. + * + */ + + void UpdateHUPixelData(MEPixelDataType* pixeldata, const float *histogram); + + /*! + * @brief Optical flow correction of the camera movements + * + * The function trackes some points on the scene if a camera movement is + * detected, then the LBP pixel data is corrected. + * + */ + + void OpticalFlowCorrection(); + + private: + // GENERAL VARIABLES + /// Motion detection type + DetectorType MDMode; + /// State of the data structures + MEProcessStateType MDDataState; + /// Processed number in the image sequence + int Frames; + /// Store the current image + MEImage CurrentImage; + /// Store the previous image + MEImage PreviousImage; + /// Store the current mask image + MEImage MaskImage; + /// Store the current mask image + bool ReadyMask; + // HISTOGRAM UPDATE VARIABLES + /// Color space (-1 = no conversion) + int HUColorSpace; + /// LBP calculation mode (-1 = no conversion) + int HULBPMode; + /// Histograms per pixel + int HUHistogramsPerPixel; + /// Histogram area + int HUHistogramArea; + /// Histogram bins + int HUHistogramBins; + /// Image width for histogram update + int HUImageWidth; + /// Image height for histogram update + int HUImageHeight; + /// Data of the LBP histograms + MEPixelDataType ***HULBPPixelData; + /// Store the previous blue layer + MEImage PreviousBlueLayer; + /// Histogram proximity threshold + float HUPrThres; + /// Background selection threshold + float HUBackgrThres; + /// Histogram learning rate + float HUHistLRate; + /// Weights learning rate + float HUWeightsLRate; + /// Pixel number used to calculate the histograms + int HUSamplePixels; + /// The desired pixel number used to calculate the histograms (-1 = Auto) + int HUDesiredSamplePixels; + /// Min cut weight + float HUMinCutWeight; + /// Auxiliary variable for computing the histograms in a column + int **HUMaskColumnAddDel; + /// Auxiliary variable for computing the histograms in a row + int **HUMaskRowAddDel; + // OPTICAL FLOW VARIABLES + /// State of the optical flow + MEProcessStateType HUOFDataState; + /// Number of the tracked points with optical flow + int HUOFPointsNumber; + /// Tracked points + CvPoint2D32f* HUOFPoints[2]; + /// The rest x component of previous camera movement + int HUOFCamMovementX; + /// Maximum tracked points detected in one cycle + int MaxTrackedPoints; + /// Processed frame number with optical flow in the image sequence + int HUOFFrames; + /// Indicator of a new camera movement + bool HUOFCamMovement; + }; + } + } +} #endif diff --git a/src/algorithms/LBP_MRF/block.h b/src/algorithms/LBP_MRF/block.h index 16c86b7248dfe0c357f5af16eaa1aad62e259582..7f5c3d25889725f326d07b297e45acad5bed7a65 100644 --- a/src/algorithms/LBP_MRF/block.h +++ b/src/algorithms/LBP_MRF/block.h @@ -3,170 +3,176 @@ #include <stdlib.h> #include <stdio.h> -namespace ck +namespace bgslibrary { - template <class Type> class Block + namespace algorithms { - public: - /* Constructor. Arguments are the block size and - (optionally) the pointer to the function which - will be called if allocation failed; the message - passed to this function is "Not enough memory!" */ - Block(int size, void(*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } - - /* Destructor. Deallocates all items added so far */ - ~Block() { while (first) { block *next = first->next; delete first; first = next; } } - - /* Allocates 'num' consecutive items; returns pointer - to the first item. 'num' cannot be greater than the - block size since items must fit in one block */ - Type *New(int num = 1) + namespace lbp_mrf { - Type *t; - - if (!last || last->current + num > last->last) + template <class Type> class Block { - if (last && last->next) last = last->next; - else + public: + /* Constructor. Arguments are the block size and + (optionally) the pointer to the function which + will be called if allocation failed; the message + passed to this function is "Not enough memory!" */ + Block(int size, void(*err_function)(char *) = NULL) { first = last = NULL; block_size = size; error_function = err_function; } + + /* Destructor. Deallocates all items added so far */ + ~Block() { while (first) { block *next = first->next; delete first; first = next; } } + + /* Allocates 'num' consecutive items; returns pointer + to the first item. 'num' cannot be greater than the + block size since items must fit in one block */ + Type *New(int num = 1) { - block *next = (block *) new char[sizeof(block) + (block_size - 1)*sizeof(Type)]; - if (!next) { fprintf(stderr, "Not enough memory!"); exit(1); } - if (last) last->next = next; - else first = next; - last = next; - last->current = &(last->data[0]); - last->last = last->current + block_size; - last->next = NULL; + Type *t; + + if (!last || last->current + num > last->last) + { + if (last && last->next) last = last->next; + else + { + block *next = (block *) new char[sizeof(block) + (block_size - 1)*sizeof(Type)]; + if (!next) { fprintf(stderr, "Not enough memory!"); exit(1); } + if (last) last->next = next; + else first = next; + last = next; + last->current = &(last->data[0]); + last->last = last->current + block_size; + last->next = NULL; + } + } + + t = last->current; + last->current += num; + return t; } - } - - t = last->current; - last->current += num; - return t; - } - /* Returns the first item (or NULL, if no items were added) */ - Type *ScanFirst() - { - scan_current_block = first; - if (!scan_current_block) return NULL; - scan_current_data = &(scan_current_block->data[0]); - return scan_current_data++; - } - - /* Returns the next item (or NULL, if all items have been read) - Can be called only if previous ScanFirst() or ScanNext() - call returned not NULL. */ - Type *ScanNext() - { - if (scan_current_data >= scan_current_block->current) - { - scan_current_block = scan_current_block->next; - if (!scan_current_block) return NULL; - scan_current_data = &(scan_current_block->data[0]); - } - return scan_current_data++; - } + /* Returns the first item (or NULL, if no items were added) */ + Type *ScanFirst() + { + scan_current_block = first; + if (!scan_current_block) return NULL; + scan_current_data = &(scan_current_block->data[0]); + return scan_current_data++; + } - /* Marks all elements as empty */ - void Reset() - { - block *b; - if (!first) return; - for (b = first;; b = b->next) - { - b->current = &(b->data[0]); - if (b == last) break; - } - last = first; - } + /* Returns the next item (or NULL, if all items have been read) + Can be called only if previous ScanFirst() or ScanNext() + call returned not NULL. */ + Type *ScanNext() + { + if (scan_current_data >= scan_current_block->current) + { + scan_current_block = scan_current_block->next; + if (!scan_current_block) return NULL; + scan_current_data = &(scan_current_block->data[0]); + } + return scan_current_data++; + } - /***********************************************************************/ + /* Marks all elements as empty */ + void Reset() + { + block *b; + if (!first) return; + for (b = first;; b = b->next) + { + b->current = &(b->data[0]); + if (b == last) break; + } + last = first; + } - private: + /***********************************************************************/ - typedef struct block_st - { - Type *current, *last; - struct block_st *next; - Type data[1]; - } block; + private: - int block_size; - block *first; - block *last; + typedef struct block_st + { + Type *current, *last; + struct block_st *next; + Type data[1]; + } block; - block *scan_current_block; - Type *scan_current_data; + int block_size; + block *first; + block *last; - void(*error_function)(char *); - }; + block *scan_current_block; + Type *scan_current_data; - /***********************************************************************/ - /***********************************************************************/ - /***********************************************************************/ + void(*error_function)(char *); + }; - template <class Type> class DBlock - { - public: - /* Constructor. Arguments are the block size and - (optionally) the pointer to the function which - will be called if allocation failed; the message - passed to this function is "Not enough memory!" */ - DBlock(int size, void(*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } - - /* Destructor. Deallocates all items added so far */ - ~DBlock() { while (first) { block *next = first->next; delete first; first = next; } } - - /* Allocates one item */ - Type *New() - { - block_item *item; + /***********************************************************************/ + /***********************************************************************/ + /***********************************************************************/ - if (!first_free) + template <class Type> class DBlock { - block *next = first; - first = (block *) new char[sizeof(block) + (block_size - 1)*sizeof(block_item)]; - if (!first) { fprintf(stderr, "Not enough memory!"); exit(1); } - first_free = &(first->data[0]); - for (item = first_free; item < first_free + block_size - 1; item++) - item->next_free = item + 1; - item->next_free = NULL; - first->next = next; - } - - item = first_free; - first_free = item->next_free; - return (Type *)item; - } + public: + /* Constructor. Arguments are the block size and + (optionally) the pointer to the function which + will be called if allocation failed; the message + passed to this function is "Not enough memory!" */ + DBlock(int size, void(*err_function)(char *) = NULL) { first = NULL; first_free = NULL; block_size = size; error_function = err_function; } + + /* Destructor. Deallocates all items added so far */ + ~DBlock() { while (first) { block *next = first->next; delete first; first = next; } } + + /* Allocates one item */ + Type *New() + { + block_item *item; + + if (!first_free) + { + block *next = first; + first = (block *) new char[sizeof(block) + (block_size - 1)*sizeof(block_item)]; + if (!first) { fprintf(stderr, "Not enough memory!"); exit(1); } + first_free = &(first->data[0]); + for (item = first_free; item < first_free + block_size - 1; item++) + item->next_free = item + 1; + item->next_free = NULL; + first->next = next; + } + + item = first_free; + first_free = item->next_free; + return (Type *)item; + } - /* Deletes an item allocated previously */ - void Delete(Type *t) - { - ((block_item *)t)->next_free = first_free; - first_free = (block_item *)t; - } + /* Deletes an item allocated previously */ + void Delete(Type *t) + { + ((block_item *)t)->next_free = first_free; + first_free = (block_item *)t; + } - /***********************************************************************/ + /***********************************************************************/ - private: + private: - typedef union block_item_st - { - Type t; - block_item_st *next_free; - } block_item; + typedef union block_item_st + { + Type t; + block_item_st *next_free; + } block_item; - typedef struct block_st - { - struct block_st *next; - block_item data[1]; - } block; + typedef struct block_st + { + struct block_st *next; + block_item data[1]; + } block; - int block_size; - block *first; - block_item *first_free; + int block_size; + block *first; + block_item *first_free; - void(*error_function)(char *); - }; + void(*error_function)(char *); + }; + } + } } diff --git a/src/algorithms/LBP_MRF/graph.cpp b/src/algorithms/LBP_MRF/graph.cpp index cdc137932f8ab2b45bf453b3ba5183d7bdc07a5e..541efb3669ebd8b732fdd26bda47d560c471fcd2 100644 --- a/src/algorithms/LBP_MRF/graph.cpp +++ b/src/algorithms/LBP_MRF/graph.cpp @@ -2,64 +2,70 @@ #include "graph.h" -namespace ck +namespace bgslibrary { - Graph::Graph(void(*err_function)(char *)) + namespace algorithms { - error_function = err_function; - node_block = new Block<node>(NODE_BLOCK_SIZE, error_function); - arc_block = new Block<arc>(NODE_BLOCK_SIZE, error_function); - flow = 0; - } + namespace lbp_mrf + { + Graph::Graph(void(*err_function)(char *)) + { + error_function = err_function; + node_block = new Block<node>(NODE_BLOCK_SIZE, error_function); + arc_block = new Block<arc>(NODE_BLOCK_SIZE, error_function); + flow = 0; + } - Graph::~Graph() - { - delete node_block; - delete arc_block; - } + Graph::~Graph() + { + delete node_block; + delete arc_block; + } - Graph::node_id Graph::add_node() - { - node *i = node_block->New(); + Graph::node_id Graph::add_node() + { + node *i = node_block->New(); - i->first = NULL; - i->tr_cap = 0; + i->first = NULL; + i->tr_cap = 0; - return (node_id)i; - } + return (node_id)i; + } - void Graph::add_edge(node_id from, node_id to, captype cap, captype rev_cap) - { - arc *a, *a_rev; + void Graph::add_edge(node_id from, node_id to, captype cap, captype rev_cap) + { + arc *a, *a_rev; - a = arc_block->New(2); - a_rev = a + 1; + a = arc_block->New(2); + a_rev = a + 1; - a->sister = a_rev; - a_rev->sister = a; - a->next = ((node*)from)->first; - ((node*)from)->first = a; - a_rev->next = ((node*)to)->first; - ((node*)to)->first = a_rev; - a->head = (node*)to; - a_rev->head = (node*)from; - a->r_cap = cap; - a_rev->r_cap = rev_cap; - } + a->sister = a_rev; + a_rev->sister = a; + a->next = ((node*)from)->first; + ((node*)from)->first = a; + a_rev->next = ((node*)to)->first; + ((node*)to)->first = a_rev; + a->head = (node*)to; + a_rev->head = (node*)from; + a->r_cap = cap; + a_rev->r_cap = rev_cap; + } - void Graph::set_tweights(node_id i, captype cap_source, captype cap_sink) - { - flow += (cap_source < cap_sink) ? cap_source : cap_sink; - ((node*)i)->tr_cap = cap_source - cap_sink; - } + void Graph::set_tweights(node_id i, captype cap_source, captype cap_sink) + { + flow += (cap_source < cap_sink) ? cap_source : cap_sink; + ((node*)i)->tr_cap = cap_source - cap_sink; + } - void Graph::add_tweights(node_id i, captype cap_source, captype cap_sink) - { - //register - captype delta = ((node*)i)->tr_cap; // 'register' storage class specifier is deprecated and incompatible with C++17 - if (delta > 0) cap_source += delta; - else cap_sink -= delta; - flow += (cap_source < cap_sink) ? cap_source : cap_sink; - ((node*)i)->tr_cap = cap_source - cap_sink; + void Graph::add_tweights(node_id i, captype cap_source, captype cap_sink) + { + //register + captype delta = ((node*)i)->tr_cap; // 'register' storage class specifier is deprecated and incompatible with C++17 + if (delta > 0) cap_source += delta; + else cap_sink -= delta; + flow += (cap_source < cap_sink) ? cap_source : cap_sink; + ((node*)i)->tr_cap = cap_source - cap_sink; + } + } } } diff --git a/src/algorithms/LBP_MRF/graph.h b/src/algorithms/LBP_MRF/graph.h index a30e938e73702f685ee44711b50fb10a95ec1fd8..257b0a77d35e71952154eaec1be8df0ed3bf883c 100644 --- a/src/algorithms/LBP_MRF/graph.h +++ b/src/algorithms/LBP_MRF/graph.h @@ -2,136 +2,142 @@ #include "block.h" -/* - Nodes, arcs and pointers to nodes are - added in blocks for memory and time efficiency. - Below are numbers of items in blocks - */ -#define NODE_BLOCK_SIZE 512 -#define ARC_BLOCK_SIZE 1024 -#define NODEPTR_BLOCK_SIZE 128 - -namespace ck +namespace bgslibrary { - class Graph + namespace algorithms { - public: - typedef enum + namespace lbp_mrf { - SOURCE = 0, - SINK = 1 - } termtype; /* terminals */ - - /* Type of edge weights. - Can be changed to char, int, float, double, ... */ - typedef short captype; - /* Type of total flow */ - typedef int flowtype; - - typedef void * node_id; - - /* interface functions */ - - /* Constructor. Optional argument is the pointer to the - function which will be called if an error occurs; - an error message is passed to this function. If this - argument is omitted, exit(1) will be called. */ - Graph(void(*err_function)(char *) = NULL); - - /* Destructor */ - ~Graph(); - - /* Adds a node to the graph */ - node_id add_node(); - - /* Adds a bidirectional edge between 'from' and 'to' - with the weights 'cap' and 'rev_cap' */ - void add_edge(node_id from, node_id to, captype cap, captype rev_cap); - - /* Sets the weights of the edges 'SOURCE->i' and 'i->SINK' - Can be called at most once for each node before any call to 'add_tweights'. - Weights can be negative */ - void set_tweights(node_id i, captype cap_source, captype cap_sink); - - /* Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights - Can be called multiple times for each node. - Weights can be negative */ - void add_tweights(node_id i, captype cap_source, captype cap_sink); - - /* After the maxflow is computed, this function returns to which - segment the node 'i' belongs (Graph::SOURCE or Graph::SINK) */ - termtype what_segment(node_id i); - - /* Computes the maxflow. Can be called only once. */ - flowtype maxflow(); - - /***********************************************************************/ - /***********************************************************************/ - /***********************************************************************/ - - private: - /* internal variables and functions */ - - struct arc_st; - - /* node structure */ - typedef struct node_st - { - arc_st *first; /* first outcoming arc */ - - arc_st *parent; /* node's parent */ - node_st *next; /* pointer to the next active node - (or to itself if it is the last node in the list) */ - int TS; /* timestamp showing when DIST was computed */ - int DIST; /* distance to the terminal */ - short is_sink; /* flag showing whether the node is in the source or in the sink tree */ - - captype tr_cap; /* if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node - otherwise -tr_cap is residual capacity of the arc node->SINK */ - } node; - - /* arc structure */ - typedef struct arc_st - { - node_st *head; /* node the arc points to */ - arc_st *next; /* next arc with the same originating node */ - arc_st *sister; /* reverse arc */ - - captype r_cap; /* residual capacity */ - } arc; - - /* 'pointer to node' structure */ - typedef struct nodeptr_st - { - node_st *ptr; - nodeptr_st *next; - } nodeptr; - - Block<node> *node_block; - Block<arc> *arc_block; - DBlock<nodeptr> *nodeptr_block; - - void(*error_function)(char *); /* this function is called if a error occurs, - with a corresponding error message - (or exit(1) is called if it's NULL) */ - - flowtype flow; /* total flow */ - - /***********************************************************************/ - - node *queue_first[2], *queue_last[2]; /* list of active nodes */ - nodeptr *orphan_first, *orphan_last; /* list of pointers to orphans */ - int TIME; /* monotonically increasing global counter */ - - /***********************************************************************/ - - /* functions for processing active list */ - void set_active(node *i); - node *next_active(); - - void maxflow_init(); - void augment(arc *middle_arc); - void process_source_orphan(node *i); - void process_sink_orphan(node *i); - }; + /* + Nodes, arcs and pointers to nodes are + added in blocks for memory and time efficiency. + Below are numbers of items in blocks + */ + const int NODE_BLOCK_SIZE = 512; + const int ARC_BLOCK_SIZE = 1024; + const int NODEPTR_BLOCK_SIZE = 128; + + class Graph + { + public: + typedef enum + { + SOURCE = 0, + SINK = 1 + } termtype; /* terminals */ + + /* Type of edge weights. + Can be changed to char, int, float, double, ... */ + typedef short captype; + /* Type of total flow */ + typedef int flowtype; + + typedef void * node_id; + + /* interface functions */ + + /* Constructor. Optional argument is the pointer to the + function which will be called if an error occurs; + an error message is passed to this function. If this + argument is omitted, exit(1) will be called. */ + Graph(void(*err_function)(char *) = NULL); + + /* Destructor */ + ~Graph(); + + /* Adds a node to the graph */ + node_id add_node(); + + /* Adds a bidirectional edge between 'from' and 'to' + with the weights 'cap' and 'rev_cap' */ + void add_edge(node_id from, node_id to, captype cap, captype rev_cap); + + /* Sets the weights of the edges 'SOURCE->i' and 'i->SINK' + Can be called at most once for each node before any call to 'add_tweights'. + Weights can be negative */ + void set_tweights(node_id i, captype cap_source, captype cap_sink); + + /* Adds new edges 'SOURCE->i' and 'i->SINK' with corresponding weights + Can be called multiple times for each node. + Weights can be negative */ + void add_tweights(node_id i, captype cap_source, captype cap_sink); + + /* After the maxflow is computed, this function returns to which + segment the node 'i' belongs (Graph::SOURCE or Graph::SINK) */ + termtype what_segment(node_id i); + + /* Computes the maxflow. Can be called only once. */ + flowtype maxflow(); + + /***********************************************************************/ + /***********************************************************************/ + /***********************************************************************/ + + private: + /* internal variables and functions */ + + struct arc_st; + + /* node structure */ + typedef struct node_st + { + arc_st *first; /* first outcoming arc */ + + arc_st *parent; /* node's parent */ + node_st *next; /* pointer to the next active node + (or to itself if it is the last node in the list) */ + int TS; /* timestamp showing when DIST was computed */ + int DIST; /* distance to the terminal */ + short is_sink; /* flag showing whether the node is in the source or in the sink tree */ + + captype tr_cap; /* if tr_cap > 0 then tr_cap is residual capacity of the arc SOURCE->node + otherwise -tr_cap is residual capacity of the arc node->SINK */ + } node; + + /* arc structure */ + typedef struct arc_st + { + node_st *head; /* node the arc points to */ + arc_st *next; /* next arc with the same originating node */ + arc_st *sister; /* reverse arc */ + + captype r_cap; /* residual capacity */ + } arc; + + /* 'pointer to node' structure */ + typedef struct nodeptr_st + { + node_st *ptr; + nodeptr_st *next; + } nodeptr; + + Block<node> *node_block; + Block<arc> *arc_block; + DBlock<nodeptr> *nodeptr_block; + + void(*error_function)(char *); /* this function is called if a error occurs, + with a corresponding error message + (or exit(1) is called if it's NULL) */ + + flowtype flow; /* total flow */ + + /***********************************************************************/ + + node *queue_first[2], *queue_last[2]; /* list of active nodes */ + nodeptr *orphan_first, *orphan_last; /* list of pointers to orphans */ + int TIME; /* monotonically increasing global counter */ + + /***********************************************************************/ + + /* functions for processing active list */ + void set_active(node *i); + node *next_active(); + + void maxflow_init(); + void augment(arc *middle_arc); + void process_source_orphan(node *i); + void process_sink_orphan(node *i); + }; + } + } } diff --git a/src/algorithms/LBP_MRF/maxflow.cpp b/src/algorithms/LBP_MRF/maxflow.cpp index 05b3ceffc7c1a2f934fecfdc5c054fa4df35c297..660de7ba78293768815fd2df9d8d3c04d7998533 100644 --- a/src/algorithms/LBP_MRF/maxflow.cpp +++ b/src/algorithms/LBP_MRF/maxflow.cpp @@ -24,474 +24,479 @@ the front of the first queue. If the first queue is empty, it is replaced by the second queue (and the second queue becomes empty). */ -namespace ck +namespace bgslibrary { - inline void Graph::set_active(node *i) + namespace algorithms { - if (!i->next) + namespace lbp_mrf { - /* it's not in the list yet */ - if (queue_last[1]) queue_last[1]->next = i; - else queue_first[1] = i; - queue_last[1] = i; - i->next = i; - } - } - - /* - Returns the next active node. - If it is connected to the sink, it stays in the list, - otherwise it is removed from the list - */ - inline Graph::node * Graph::next_active() - { - node *i; - - while (1) - { - if (!(i = queue_first[0])) + inline void Graph::set_active(node *i) { - queue_first[0] = i = queue_first[1]; - queue_last[0] = queue_last[1]; - queue_first[1] = NULL; - queue_last[1] = NULL; - if (!i) return NULL; + if (!i->next) + { + /* it's not in the list yet */ + if (queue_last[1]) queue_last[1]->next = i; + else queue_first[1] = i; + queue_last[1] = i; + i->next = i; + } } - /* remove it from the active list */ - if (i->next == i) queue_first[0] = queue_last[0] = NULL; - else queue_first[0] = i->next; - i->next = NULL; - - /* a node in the list is active iff it has a parent */ - if (i->parent) return i; - } - } - - /***********************************************************************/ - - void Graph::maxflow_init() - { - node *i; - - queue_first[0] = queue_last[0] = NULL; - queue_first[1] = queue_last[1] = NULL; - orphan_first = NULL; - - for (i = node_block->ScanFirst(); i; i = node_block->ScanNext()) - { - i->next = NULL; - i->TS = 0; - if (i->tr_cap > 0) - { - /* i is connected to the source */ - i->is_sink = 0; - i->parent = TERMINAL; - set_active(i); - i->TS = 0; - i->DIST = 1; - } - else if (i->tr_cap < 0) + /* + Returns the next active node. + If it is connected to the sink, it stays in the list, + otherwise it is removed from the list + */ + inline Graph::node * Graph::next_active() { - /* i is connected to the sink */ - i->is_sink = 1; - i->parent = TERMINAL; - set_active(i); - i->TS = 0; - i->DIST = 1; - } - else - { - i->parent = NULL; - } - } - TIME = 0; - } + node *i; - /***********************************************************************/ - - void Graph::augment(arc *middle_arc) - { - node *i; - arc *a; - captype bottleneck; - nodeptr *np; - - - /* 1. Finding bottleneck capacity */ - /* 1a - the source tree */ - bottleneck = middle_arc->r_cap; - for (i = middle_arc->sister->head;; i = a->head) - { - a = i->parent; - if (a == TERMINAL) break; - if (bottleneck > a->sister->r_cap) bottleneck = a->sister->r_cap; - } - if (bottleneck > i->tr_cap) bottleneck = i->tr_cap; - /* 1b - the sink tree */ - for (i = middle_arc->head;; i = a->head) - { - a = i->parent; - if (a == TERMINAL) break; - if (bottleneck > a->r_cap) bottleneck = a->r_cap; - } - if (bottleneck > -i->tr_cap) bottleneck = -i->tr_cap; + while (1) + { + if (!(i = queue_first[0])) + { + queue_first[0] = i = queue_first[1]; + queue_last[0] = queue_last[1]; + queue_first[1] = NULL; + queue_last[1] = NULL; + if (!i) return NULL; + } + /* remove it from the active list */ + if (i->next == i) queue_first[0] = queue_last[0] = NULL; + else queue_first[0] = i->next; + i->next = NULL; - /* 2. Augmenting */ - /* 2a - the source tree */ - middle_arc->sister->r_cap += bottleneck; - middle_arc->r_cap -= bottleneck; - for (i = middle_arc->sister->head;; i = a->head) - { - a = i->parent; - if (a == TERMINAL) break; - a->r_cap += bottleneck; - a->sister->r_cap -= bottleneck; - if (!a->sister->r_cap) - { - /* add i to the adoption list */ - i->parent = ORPHAN; - np = nodeptr_block->New(); - np->ptr = i; - np->next = orphan_first; - orphan_first = np; - } - } - i->tr_cap -= bottleneck; - if (!i->tr_cap) - { - /* add i to the adoption list */ - i->parent = ORPHAN; - np = nodeptr_block->New(); - np->ptr = i; - np->next = orphan_first; - orphan_first = np; - } - /* 2b - the sink tree */ - for (i = middle_arc->head;; i = a->head) - { - a = i->parent; - if (a == TERMINAL) break; - a->sister->r_cap += bottleneck; - a->r_cap -= bottleneck; - if (!a->r_cap) - { - /* add i to the adoption list */ - i->parent = ORPHAN; - np = nodeptr_block->New(); - np->ptr = i; - np->next = orphan_first; - orphan_first = np; + /* a node in the list is active iff it has a parent */ + if (i->parent) return i; + } } - } - i->tr_cap += bottleneck; - if (!i->tr_cap) - { - /* add i to the adoption list */ - i->parent = ORPHAN; - np = nodeptr_block->New(); - np->ptr = i; - np->next = orphan_first; - orphan_first = np; - } + /***********************************************************************/ - flow += bottleneck; - } + void Graph::maxflow_init() + { + node *i; - /***********************************************************************/ + queue_first[0] = queue_last[0] = NULL; + queue_first[1] = queue_last[1] = NULL; + orphan_first = NULL; - void Graph::process_source_orphan(node *i) - { - node *j; - arc *a0, *a0_min = NULL, *a; - nodeptr *np; - int d, d_min = INFINITE_D; - - /* trying to find a new parent */ - for (a0 = i->first; a0; a0 = a0->next) - if (a0->sister->r_cap) - { - j = a0->head; - if (!j->is_sink && (a = j->parent)) + for (i = node_block->ScanFirst(); i; i = node_block->ScanNext()) { - /* checking the origin of j */ - d = 0; - while (1) + i->next = NULL; + i->TS = 0; + if (i->tr_cap > 0) { - if (j->TS == TIME) - { - d += j->DIST; - break; - } - a = j->parent; - d++; - if (a == TERMINAL) - { - j->TS = TIME; - j->DIST = 1; - break; - } - if (a == ORPHAN) { d = INFINITE_D; break; } - j = a->head; + /* i is connected to the source */ + i->is_sink = 0; + i->parent = TERMINAL; + set_active(i); + i->TS = 0; + i->DIST = 1; } - if (d < INFINITE_D) /* j originates from the source - done */ + else if (i->tr_cap < 0) { - if (d < d_min) - { - a0_min = a0; - d_min = d; - } - /* set marks along the path */ - for (j = a0->head; j->TS != TIME; j = j->parent->head) - { - j->TS = TIME; - j->DIST = d--; - } + /* i is connected to the sink */ + i->is_sink = 1; + i->parent = TERMINAL; + set_active(i); + i->TS = 0; + i->DIST = 1; + } + else + { + i->parent = NULL; } } + TIME = 0; } - if ((i->parent = a0_min)) - { - i->TS = TIME; - i->DIST = d_min + 1; - } - else - { - /* no parent is found */ - i->TS = 0; + /***********************************************************************/ - /* process neighbors */ - for (a0 = i->first; a0; a0 = a0->next) + void Graph::augment(arc *middle_arc) { - j = a0->head; - if (!j->is_sink && (a = j->parent)) + node *i; + arc *a; + captype bottleneck; + nodeptr *np; + + + /* 1. Finding bottleneck capacity */ + /* 1a - the source tree */ + bottleneck = middle_arc->r_cap; + for (i = middle_arc->sister->head;; i = a->head) { - if (a0->sister->r_cap) set_active(j); - if (a != TERMINAL && a != ORPHAN && a->head == i) - { - /* add j to the adoption list */ - j->parent = ORPHAN; - np = nodeptr_block->New(); - np->ptr = j; - if (orphan_last) orphan_last->next = np; - else orphan_first = np; - orphan_last = np; - np->next = NULL; - } + a = i->parent; + if (a == TERMINAL) break; + if (bottleneck > a->sister->r_cap) bottleneck = a->sister->r_cap; } - } - } - } + if (bottleneck > i->tr_cap) bottleneck = i->tr_cap; + /* 1b - the sink tree */ + for (i = middle_arc->head;; i = a->head) + { + a = i->parent; + if (a == TERMINAL) break; + if (bottleneck > a->r_cap) bottleneck = a->r_cap; + } + if (bottleneck > -i->tr_cap) bottleneck = -i->tr_cap; - void Graph::process_sink_orphan(node *i) - { - node *j; - arc *a0, *a0_min = NULL, *a; - nodeptr *np; - int d, d_min = INFINITE_D; - - /* trying to find a new parent */ - for (a0 = i->first; a0; a0 = a0->next) - if (a0->r_cap) - { - j = a0->head; - if (j->is_sink && (a = j->parent)) + + /* 2. Augmenting */ + /* 2a - the source tree */ + middle_arc->sister->r_cap += bottleneck; + middle_arc->r_cap -= bottleneck; + for (i = middle_arc->sister->head;; i = a->head) { - /* checking the origin of j */ - d = 0; - while (1) - { - if (j->TS == TIME) - { - d += j->DIST; - break; - } - a = j->parent; - d++; - if (a == TERMINAL) - { - j->TS = TIME; - j->DIST = 1; - break; - } - if (a == ORPHAN) { d = INFINITE_D; break; } - j = a->head; - } - if (d < INFINITE_D) /* j originates from the sink - done */ + a = i->parent; + if (a == TERMINAL) break; + a->r_cap += bottleneck; + a->sister->r_cap -= bottleneck; + if (!a->sister->r_cap) { - if (d < d_min) - { - a0_min = a0; - d_min = d; - } - /* set marks along the path */ - for (j = a0->head; j->TS != TIME; j = j->parent->head) - { - j->TS = TIME; - j->DIST = d--; - } + /* add i to the adoption list */ + i->parent = ORPHAN; + np = nodeptr_block->New(); + np->ptr = i; + np->next = orphan_first; + orphan_first = np; } } - } - - if ((i->parent = a0_min)) - { - i->TS = TIME; - i->DIST = d_min + 1; - } - else - { - /* no parent is found */ - i->TS = 0; - - /* process neighbors */ - for (a0 = i->first; a0; a0 = a0->next) - { - j = a0->head; - if (j->is_sink && (a = j->parent)) + i->tr_cap -= bottleneck; + if (!i->tr_cap) + { + /* add i to the adoption list */ + i->parent = ORPHAN; + np = nodeptr_block->New(); + np->ptr = i; + np->next = orphan_first; + orphan_first = np; + } + /* 2b - the sink tree */ + for (i = middle_arc->head;; i = a->head) { - if (a0->r_cap) set_active(j); - if (a != TERMINAL && a != ORPHAN && a->head == i) + a = i->parent; + if (a == TERMINAL) break; + a->sister->r_cap += bottleneck; + a->r_cap -= bottleneck; + if (!a->r_cap) { - /* add j to the adoption list */ - j->parent = ORPHAN; + /* add i to the adoption list */ + i->parent = ORPHAN; np = nodeptr_block->New(); - np->ptr = j; - if (orphan_last) orphan_last->next = np; - else orphan_first = np; - orphan_last = np; - np->next = NULL; + np->ptr = i; + np->next = orphan_first; + orphan_first = np; } } - } - } - } - - /***********************************************************************/ - - Graph::flowtype Graph::maxflow() - { - node *i, *j, *current_node = NULL; - arc *a; - nodeptr *np, *np_next; + i->tr_cap += bottleneck; + if (!i->tr_cap) + { + /* add i to the adoption list */ + i->parent = ORPHAN; + np = nodeptr_block->New(); + np->ptr = i; + np->next = orphan_first; + orphan_first = np; + } - maxflow_init(); - nodeptr_block = new DBlock<nodeptr>(NODEPTR_BLOCK_SIZE, error_function); - while (1) - { - if ((i = current_node)) - { - i->next = NULL; /* remove active flag */ - if (!i->parent) i = NULL; - } - if (!i) - { - if (!(i = next_active())) break; + flow += bottleneck; } - /* growth */ - if (!i->is_sink) + /***********************************************************************/ + + void Graph::process_source_orphan(node *i) { - /* grow source tree */ - for (a = i->first; a; a = a->next) - if (a->r_cap) + node *j; + arc *a0, *a0_min = NULL, *a; + nodeptr *np; + int d, d_min = INFINITE_D; + + /* trying to find a new parent */ + for (a0 = i->first; a0; a0 = a0->next) + if (a0->sister->r_cap) { - j = a->head; - if (!j->parent) + j = a0->head; + if (!j->is_sink && (a = j->parent)) { - j->is_sink = 0; - j->parent = a->sister; - j->TS = i->TS; - j->DIST = i->DIST + 1; - set_active(j); + /* checking the origin of j */ + d = 0; + while (1) + { + if (j->TS == TIME) + { + d += j->DIST; + break; + } + a = j->parent; + d++; + if (a == TERMINAL) + { + j->TS = TIME; + j->DIST = 1; + break; + } + if (a == ORPHAN) { d = INFINITE_D; break; } + j = a->head; + } + if (d < INFINITE_D) /* j originates from the source - done */ + { + if (d < d_min) + { + a0_min = a0; + d_min = d; + } + /* set marks along the path */ + for (j = a0->head; j->TS != TIME; j = j->parent->head) + { + j->TS = TIME; + j->DIST = d--; + } + } } - else if (j->is_sink) break; - else if (j->TS <= i->TS && - j->DIST > i->DIST) + } + + if ((i->parent = a0_min)) + { + i->TS = TIME; + i->DIST = d_min + 1; + } + else + { + /* no parent is found */ + i->TS = 0; + + /* process neighbors */ + for (a0 = i->first; a0; a0 = a0->next) + { + j = a0->head; + if (!j->is_sink && (a = j->parent)) { - /* heuristic - trying to make the distance from j to the source shorter */ - j->parent = a->sister; - j->TS = i->TS; - j->DIST = i->DIST + 1; + if (a0->sister->r_cap) set_active(j); + if (a != TERMINAL && a != ORPHAN && a->head == i) + { + /* add j to the adoption list */ + j->parent = ORPHAN; + np = nodeptr_block->New(); + np->ptr = j; + if (orphan_last) orphan_last->next = np; + else orphan_first = np; + orphan_last = np; + np->next = NULL; + } } } + } } - else + + void Graph::process_sink_orphan(node *i) { - /* grow sink tree */ - for (a = i->first; a; a = a->next) - if (a->sister->r_cap) + node *j; + arc *a0, *a0_min = NULL, *a; + nodeptr *np; + int d, d_min = INFINITE_D; + + /* trying to find a new parent */ + for (a0 = i->first; a0; a0 = a0->next) + if (a0->r_cap) { - j = a->head; - if (!j->parent) + j = a0->head; + if (j->is_sink && (a = j->parent)) { - j->is_sink = 1; - j->parent = a->sister; - j->TS = i->TS; - j->DIST = i->DIST + 1; - set_active(j); + /* checking the origin of j */ + d = 0; + while (1) + { + if (j->TS == TIME) + { + d += j->DIST; + break; + } + a = j->parent; + d++; + if (a == TERMINAL) + { + j->TS = TIME; + j->DIST = 1; + break; + } + if (a == ORPHAN) { d = INFINITE_D; break; } + j = a->head; + } + if (d < INFINITE_D) /* j originates from the sink - done */ + { + if (d < d_min) + { + a0_min = a0; + d_min = d; + } + /* set marks along the path */ + for (j = a0->head; j->TS != TIME; j = j->parent->head) + { + j->TS = TIME; + j->DIST = d--; + } + } } - else if (!j->is_sink) { a = a->sister; break; } - else if (j->TS <= i->TS && - j->DIST > i->DIST) + } + + if ((i->parent = a0_min)) + { + i->TS = TIME; + i->DIST = d_min + 1; + } + else + { + /* no parent is found */ + i->TS = 0; + + /* process neighbors */ + for (a0 = i->first; a0; a0 = a0->next) + { + j = a0->head; + if (j->is_sink && (a = j->parent)) { - /* heuristic - trying to make the distance from j to the sink shorter */ - j->parent = a->sister; - j->TS = i->TS; - j->DIST = i->DIST + 1; + if (a0->r_cap) set_active(j); + if (a != TERMINAL && a != ORPHAN && a->head == i) + { + /* add j to the adoption list */ + j->parent = ORPHAN; + np = nodeptr_block->New(); + np->ptr = j; + if (orphan_last) orphan_last->next = np; + else orphan_first = np; + orphan_last = np; + np->next = NULL; + } } } + } } - TIME++; + /***********************************************************************/ - if (a) + Graph::flowtype Graph::maxflow() { - i->next = i; /* set active flag */ - current_node = i; + node *i, *j, *current_node = NULL; + arc *a; + nodeptr *np, *np_next; - /* augmentation */ - augment(a); - /* augmentation end */ + maxflow_init(); + nodeptr_block = new DBlock<nodeptr>(NODEPTR_BLOCK_SIZE, error_function); - /* adoption */ - while ((np = orphan_first)) + while (1) { - np_next = np->next; - np->next = NULL; + if ((i = current_node)) + { + i->next = NULL; /* remove active flag */ + if (!i->parent) i = NULL; + } + if (!i) + { + if (!(i = next_active())) break; + } - while ((np = orphan_first)) + /* growth */ + if (!i->is_sink) { - orphan_first = np->next; - i = np->ptr; - nodeptr_block->Delete(np); - if (!orphan_first) orphan_last = NULL; - if (i->is_sink) process_sink_orphan(i); - else process_source_orphan(i); + /* grow source tree */ + for (a = i->first; a; a = a->next) + if (a->r_cap) + { + j = a->head; + if (!j->parent) + { + j->is_sink = 0; + j->parent = a->sister; + j->TS = i->TS; + j->DIST = i->DIST + 1; + set_active(j); + } + else if (j->is_sink) break; + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the source shorter */ + j->parent = a->sister; + j->TS = i->TS; + j->DIST = i->DIST + 1; + } + } } + else + { + /* grow sink tree */ + for (a = i->first; a; a = a->next) + if (a->sister->r_cap) + { + j = a->head; + if (!j->parent) + { + j->is_sink = 1; + j->parent = a->sister; + j->TS = i->TS; + j->DIST = i->DIST + 1; + set_active(j); + } + else if (!j->is_sink) { a = a->sister; break; } + else if (j->TS <= i->TS && + j->DIST > i->DIST) + { + /* heuristic - trying to make the distance from j to the sink shorter */ + j->parent = a->sister; + j->TS = i->TS; + j->DIST = i->DIST + 1; + } + } + } + + TIME++; + + if (a) + { + i->next = i; /* set active flag */ + current_node = i; - orphan_first = np_next; + /* augmentation */ + augment(a); + /* augmentation end */ + + /* adoption */ + while ((np = orphan_first)) + { + np_next = np->next; + np->next = NULL; + + while ((np = orphan_first)) + { + orphan_first = np->next; + i = np->ptr; + nodeptr_block->Delete(np); + if (!orphan_first) orphan_last = NULL; + if (i->is_sink) process_sink_orphan(i); + else process_source_orphan(i); + } + + orphan_first = np_next; + } + /* adoption end */ + } + else current_node = NULL; } - /* adoption end */ - } - else current_node = NULL; - } - delete nodeptr_block; + delete nodeptr_block; - return flow; - } + return flow; + } - /***********************************************************************/ + /***********************************************************************/ - Graph::termtype Graph::what_segment(node_id i) - { - if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE; - return SINK; + Graph::termtype Graph::what_segment(node_id i) + { + if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE; + return SINK; + } + } } - } diff --git a/src/algorithms/LBSP/BackgroundSubtractorLBSP.cpp b/src/algorithms/LBSP/BackgroundSubtractorLBSP.cpp index 6344b6dc8d4c8a280380288c61a3284fe6a8b280..53dc0bedfd3bd3f446a0ed37334d087f51c34976 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorLBSP.cpp +++ b/src/algorithms/LBSP/BackgroundSubtractorLBSP.cpp @@ -9,6 +9,8 @@ #include "DistanceUtils.h" #include "RandUtils.h" +//using namespace bgslibrary::algorithms::lbsp; + #ifndef SIZE_MAX # if __WORDSIZE == 64 # define SIZE_MAX (18446744073709551615UL) @@ -17,55 +19,64 @@ # endif #endif -// local define used to determine the default median blur kernel size -#define DEFAULT_MEDIAN_BLUR_KERNEL_SIZE (9) +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + // local define used to determine the default median blur kernel size + const int DEFAULT_MEDIAN_BLUR_KERNEL_SIZE = 9; -BackgroundSubtractorLBSP::BackgroundSubtractorLBSP(float fRelLBSPThreshold, size_t nLBSPThresholdOffset) - : m_nImgChannels(0) - , m_nImgType(0) - , m_nLBSPThresholdOffset(nLBSPThresholdOffset) - , m_fRelLBSPThreshold(fRelLBSPThreshold) - , m_nTotPxCount(0) - , m_nTotRelevantPxCount(0) - , m_nFrameIndex(SIZE_MAX) - , m_nFramesSinceLastReset(0) - , m_nModelResetCooldown(0) - , m_aPxIdxLUT(nullptr) - , m_aPxInfoLUT(nullptr) - , m_nDefaultMedianBlurKernelSize(DEFAULT_MEDIAN_BLUR_KERNEL_SIZE) - , m_bInitialized(false) - , m_bAutoModelResetEnabled(true) - , m_bUsingMovingCamera(false) - , nDebugCoordX(0), nDebugCoordY(0) { - CV_Assert(m_fRelLBSPThreshold >= 0); -} + BackgroundSubtractorLBSP::BackgroundSubtractorLBSP(float fRelLBSPThreshold, size_t nLBSPThresholdOffset) + : m_nImgChannels(0) + , m_nImgType(0) + , m_nLBSPThresholdOffset(nLBSPThresholdOffset) + , m_fRelLBSPThreshold(fRelLBSPThreshold) + , m_nTotPxCount(0) + , m_nTotRelevantPxCount(0) + , m_nFrameIndex(SIZE_MAX) + , m_nFramesSinceLastReset(0) + , m_nModelResetCooldown(0) + , m_aPxIdxLUT(nullptr) + , m_aPxInfoLUT(nullptr) + , m_nDefaultMedianBlurKernelSize(DEFAULT_MEDIAN_BLUR_KERNEL_SIZE) + , m_bInitialized(false) + , m_bAutoModelResetEnabled(true) + , m_bUsingMovingCamera(false) + , nDebugCoordX(0), nDebugCoordY(0) { + CV_Assert(m_fRelLBSPThreshold >= 0); + } -BackgroundSubtractorLBSP::~BackgroundSubtractorLBSP() {} + BackgroundSubtractorLBSP::~BackgroundSubtractorLBSP() {} -void BackgroundSubtractorLBSP::initialize(const cv::Mat& oInitImg) { - this->initialize(oInitImg, cv::Mat()); -} + void BackgroundSubtractorLBSP::initialize(const cv::Mat& oInitImg) { + this->initialize(oInitImg, cv::Mat()); + } -/*cv::AlgorithmInfo* BackgroundSubtractorLBSP::info() const { - return nullptr; -}*/ + /*cv::AlgorithmInfo* BackgroundSubtractorLBSP::info() const { + return nullptr; + }*/ -cv::Mat BackgroundSubtractorLBSP::getROICopy() const { - return m_oROI.clone(); -} + cv::Mat BackgroundSubtractorLBSP::getROICopy() const { + return m_oROI.clone(); + } -void BackgroundSubtractorLBSP::setROI(cv::Mat& oROI) { - LBSP::validateROI(oROI); - CV_Assert(cv::countNonZero(oROI) > 0); - if (m_bInitialized) { - cv::Mat oLatestBackgroundImage; - getBackgroundImage(oLatestBackgroundImage); - initialize(oLatestBackgroundImage, oROI); - } - else - m_oROI = oROI.clone(); -} + void BackgroundSubtractorLBSP::setROI(cv::Mat& oROI) { + LBSP::validateROI(oROI); + CV_Assert(cv::countNonZero(oROI) > 0); + if (m_bInitialized) { + cv::Mat oLatestBackgroundImage; + getBackgroundImage(oLatestBackgroundImage); + initialize(oLatestBackgroundImage, oROI); + } + else + m_oROI = oROI.clone(); + } -void BackgroundSubtractorLBSP::setAutomaticModelReset(bool bVal) { - m_bAutoModelResetEnabled = bVal; + void BackgroundSubtractorLBSP::setAutomaticModelReset(bool bVal) { + m_bAutoModelResetEnabled = bVal; + } + } + } } diff --git a/src/algorithms/LBSP/BackgroundSubtractorLBSP.h b/src/algorithms/LBSP/BackgroundSubtractorLBSP.h index e22a10aaf49b68312e27dc38b70580f0c1a0adcc..3744b8c3358a58c2265644e4bed5a5ec3b0d014b 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorLBSP.h +++ b/src/algorithms/LBSP/BackgroundSubtractorLBSP.h @@ -5,82 +5,90 @@ #include "LBSP.h" -/*! - Local Binary Similarity Pattern (LBSP)-based change detection algorithm (abstract version/base class). +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /*! + Local Binary Similarity Pattern (LBSP)-based change detection algorithm (abstract version/base class). - 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. + 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 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 - virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) = 0; - //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) - virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRate = 0) = 0; - //! unused, always returns nullptr - //virtual cv::AlgorithmInfo* info() const; - //! returns a copy of the ROI used for descriptor extraction - virtual cv::Mat getROICopy() const; - //! sets the ROI to be used for descriptor extraction (note: this function will reinit the model and return the usable ROI) - virtual void setROI(cv::Mat& oROI); - //! turns automatic model reset on or off - void setAutomaticModelReset(bool); + This algorithm is currently NOT thread-safe. + */ + class BackgroundSubtractorLBSP : public cv::BackgroundSubtractor { + public: + //! full constructor + BackgroundSubtractorLBSP(float fRelLBSPThreshold, 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 + virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) = 0; + //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) + virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRate = 0) = 0; + //! unused, always returns nullptr + //virtual cv::AlgorithmInfo* info() const; + //! returns a copy of the ROI used for descriptor extraction + virtual cv::Mat getROICopy() const; + //! sets the ROI to be used for descriptor extraction (note: this function will reinit the model and return the usable ROI) + virtual void setROI(cv::Mat& oROI); + //! turns automatic model reset on or off + void setAutomaticModelReset(bool); -protected: - struct PxInfoBase { - int nImgCoord_Y; - int nImgCoord_X; - size_t nModelIdx; - }; - //! background model ROI used for LBSP descriptor extraction (specific to the input image size) - cv::Mat m_oROI; - //! input image size - cv::Size m_oImgSize; - //! input image channel size - size_t m_nImgChannels; - //! input image type - int m_nImgType; - //! 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; - //! total number of pixels (depends on the input frame size) & total number of relevant pixels - size_t m_nTotPxCount, m_nTotRelevantPxCount; - //! current frame index, frame count since last model reset & model reset cooldown counters - size_t m_nFrameIndex, m_nFramesSinceLastReset, m_nModelResetCooldown; - //! pre-allocated internal LBSP threshold values LUT for all possible 8-bit intensities - size_t m_anLBSPThreshold_8bitLUT[UCHAR_MAX + 1]; - //! internal pixel index LUT for all relevant analysis regions (based on the provided ROI) - size_t* m_aPxIdxLUT; - //! internal pixel info LUT for all possible pixel indexes - PxInfoBase* m_aPxInfoLUT; - //! default kernel size for median blur post-proc filtering - const int m_nDefaultMedianBlurKernelSize; - //! specifies whether the algorithm is fully initialized or not - bool m_bInitialized; - //! specifies whether automatic model resets are enabled or not - bool m_bAutoModelResetEnabled; - //! specifies whether the camera is considered moving or not - bool m_bUsingMovingCamera; - //! copy of latest pixel intensities (used when refreshing model) - cv::Mat m_oLastColorFrame; - //! copy of latest descriptors (used when refreshing model) - cv::Mat m_oLastDescFrame; - //! the foreground mask generated by the method at [t-1] - cv::Mat m_oLastFGMask; - -public: - // ######## DEBUG PURPOSES ONLY ########## - int nDebugCoordX, nDebugCoordY; - std::string sDebugName; -}; + protected: + struct PxInfoBase { + int nImgCoord_Y; + int nImgCoord_X; + size_t nModelIdx; + }; + //! background model ROI used for LBSP descriptor extraction (specific to the input image size) + cv::Mat m_oROI; + //! input image size + cv::Size m_oImgSize; + //! input image channel size + size_t m_nImgChannels; + //! input image type + int m_nImgType; + //! 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; + //! total number of pixels (depends on the input frame size) & total number of relevant pixels + size_t m_nTotPxCount, m_nTotRelevantPxCount; + //! current frame index, frame count since last model reset & model reset cooldown counters + size_t m_nFrameIndex, m_nFramesSinceLastReset, m_nModelResetCooldown; + //! pre-allocated internal LBSP threshold values LUT for all possible 8-bit intensities + size_t m_anLBSPThreshold_8bitLUT[UCHAR_MAX + 1]; + //! internal pixel index LUT for all relevant analysis regions (based on the provided ROI) + size_t* m_aPxIdxLUT; + //! internal pixel info LUT for all possible pixel indexes + PxInfoBase* m_aPxInfoLUT; + //! default kernel size for median blur post-proc filtering + const int m_nDefaultMedianBlurKernelSize; + //! specifies whether the algorithm is fully initialized or not + bool m_bInitialized; + //! specifies whether automatic model resets are enabled or not + bool m_bAutoModelResetEnabled; + //! specifies whether the camera is considered moving or not + bool m_bUsingMovingCamera; + //! copy of latest pixel intensities (used when refreshing model) + cv::Mat m_oLastColorFrame; + //! copy of latest descriptors (used when refreshing model) + cv::Mat m_oLastDescFrame; + //! the foreground mask generated by the method at [t-1] + cv::Mat m_oLastFGMask; + public: + // ######## DEBUG PURPOSES ONLY ########## + int nDebugCoordX, nDebugCoordY; + std::string sDebugName; + }; + } + } +} diff --git a/src/algorithms/LBSP/BackgroundSubtractorLBSP_.cpp b/src/algorithms/LBSP/BackgroundSubtractorLBSP_.cpp index d5324bdebf4682617eba6139647624ee7255965f..4bcdfb1a85f78b334b8e66bfa4316ec089449f20 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorLBSP_.cpp +++ b/src/algorithms/LBSP/BackgroundSubtractorLBSP_.cpp @@ -9,57 +9,68 @@ #include "DistanceUtils.h" #include "RandUtils.h" -// local define used to determine the default median blur kernel size -#define DEFAULT_MEDIAN_BLUR_KERNEL_SIZE (9) +//using namespace bgslibrary::algorithms::lbsp; -BackgroundSubtractorLBSP_::BackgroundSubtractorLBSP_(float fRelLBSPThreshold, size_t nLBSPThresholdOffset) - : m_nImgChannels(0) - , m_nImgType(0) - , m_nLBSPThresholdOffset(nLBSPThresholdOffset) - , m_fRelLBSPThreshold(fRelLBSPThreshold) - , m_nTotPxCount(0) - , m_nTotRelevantPxCount(0) - , m_nFrameIndex(SIZE_MAX) - , m_nFramesSinceLastReset(0) - , m_nModelResetCooldown(0) - , m_aPxIdxLUT(nullptr) - , m_aPxInfoLUT(nullptr) - , m_nDefaultMedianBlurKernelSize(DEFAULT_MEDIAN_BLUR_KERNEL_SIZE) - , m_bInitialized(false) - , m_bAutoModelResetEnabled(true) - , m_bUsingMovingCamera(false) - , m_nDebugCoordX(0) - , m_nDebugCoordY(0) - , m_pDebugFS(nullptr) { - CV_Assert(m_fRelLBSPThreshold >= 0); -} +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + // local define used to determine the default median blur kernel size + const int DEFAULT_MEDIAN_BLUR_KERNEL_SIZE = 9; -BackgroundSubtractorLBSP_::~BackgroundSubtractorLBSP_() {} + BackgroundSubtractorLBSP_::BackgroundSubtractorLBSP_(float fRelLBSPThreshold, size_t nLBSPThresholdOffset) + : m_nImgChannels(0) + , m_nImgType(0) + , m_nLBSPThresholdOffset(nLBSPThresholdOffset) + , m_fRelLBSPThreshold(fRelLBSPThreshold) + , m_nTotPxCount(0) + , m_nTotRelevantPxCount(0) + , m_nFrameIndex(SIZE_MAX) + , m_nFramesSinceLastReset(0) + , m_nModelResetCooldown(0) + , m_aPxIdxLUT(nullptr) + , m_aPxInfoLUT(nullptr) + , m_nDefaultMedianBlurKernelSize(DEFAULT_MEDIAN_BLUR_KERNEL_SIZE) + , m_bInitialized(false) + , m_bAutoModelResetEnabled(true) + , m_bUsingMovingCamera(false) + , m_nDebugCoordX(0) + , m_nDebugCoordY(0) + , m_pDebugFS(nullptr) { + CV_Assert(m_fRelLBSPThreshold >= 0); + } -void BackgroundSubtractorLBSP_::initialize(const cv::Mat& oInitImg) { - this->initialize(oInitImg, cv::Mat()); -} -/* -cv::AlgorithmInfo* BackgroundSubtractorLBSP_::info() const { - return nullptr; -} -*/ -cv::Mat BackgroundSubtractorLBSP_::getROICopy() const { - return m_oROI.clone(); -} + BackgroundSubtractorLBSP_::~BackgroundSubtractorLBSP_() {} -void BackgroundSubtractorLBSP_::setROI(cv::Mat& oROI) { - LBSP_::validateROI(oROI); - CV_Assert(cv::countNonZero(oROI) > 0); - if (m_bInitialized) { - cv::Mat oLatestBackgroundImage; - getBackgroundImage(oLatestBackgroundImage); - initialize(oLatestBackgroundImage, oROI); - } - else - m_oROI = oROI.clone(); -} + void BackgroundSubtractorLBSP_::initialize(const cv::Mat& oInitImg) { + this->initialize(oInitImg, cv::Mat()); + } + /* + cv::AlgorithmInfo* BackgroundSubtractorLBSP_::info() const { + return nullptr; + } + */ + cv::Mat BackgroundSubtractorLBSP_::getROICopy() const { + return m_oROI.clone(); + } -void BackgroundSubtractorLBSP_::setAutomaticModelReset(bool bVal) { - m_bAutoModelResetEnabled = bVal; + void BackgroundSubtractorLBSP_::setROI(cv::Mat& oROI) { + LBSP_::validateROI(oROI); + CV_Assert(cv::countNonZero(oROI) > 0); + if (m_bInitialized) { + cv::Mat oLatestBackgroundImage; + getBackgroundImage(oLatestBackgroundImage); + initialize(oLatestBackgroundImage, oROI); + } + else + m_oROI = oROI.clone(); + } + + void BackgroundSubtractorLBSP_::setAutomaticModelReset(bool bVal) { + m_bAutoModelResetEnabled = bVal; + } + } + } } diff --git a/src/algorithms/LBSP/BackgroundSubtractorLBSP_.h b/src/algorithms/LBSP/BackgroundSubtractorLBSP_.h index d828e107cf3db6bb948a182340e20852f45a3a6f..79d6df927c4071bdd1e48ed63d62ca2dcf25793e 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorLBSP_.h +++ b/src/algorithms/LBSP/BackgroundSubtractorLBSP_.h @@ -5,88 +5,97 @@ #include "LBSP_.h" -/*! - Local Binary Similarity Pattern (LBSP)-based change detection algorithm (abstract version/base class). +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /*! + Local Binary Similarity Pattern (LBSP)-based change detection algorithm (abstract version/base class). - 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. + 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 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 - virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) = 0; - //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) - virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRate = 0) = 0; - //! unused, always returns nullptr - //virtual cv::AlgorithmInfo* info() const; - //! returns a copy of the ROI used for descriptor extraction - virtual cv::Mat getROICopy() const; - //! sets the ROI to be used for descriptor extraction (note: this function will reinit the model and return the usable ROI) - virtual void setROI(cv::Mat& oROI); - //! turns automatic model reset on or off - void setAutomaticModelReset(bool); + This algorithm is currently NOT thread-safe. + */ + class BackgroundSubtractorLBSP_ : public cv::BackgroundSubtractor { + public: + //! full constructor + BackgroundSubtractorLBSP_(float fRelLBSPThreshold, 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 + virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) = 0; + //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) + virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRate = 0) = 0; + //! unused, always returns nullptr + //virtual cv::AlgorithmInfo* info() const; + //! returns a copy of the ROI used for descriptor extraction + virtual cv::Mat getROICopy() const; + //! sets the ROI to be used for descriptor extraction (note: this function will reinit the model and return the usable ROI) + virtual void setROI(cv::Mat& oROI); + //! turns automatic model reset on or off + void setAutomaticModelReset(bool); -protected: - struct PxInfoBase { - int nImgCoord_Y; - int nImgCoord_X; - size_t nModelIdx; - }; - //! background model ROI used for LBSP descriptor extraction (specific to the input image size) - cv::Mat m_oROI; - //! input image size - cv::Size m_oImgSize; - //! input image channel size - size_t m_nImgChannels; - //! input image type - int m_nImgType; - //! 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; - //! total number of pixels (depends on the input frame size) & total number of relevant pixels - size_t m_nTotPxCount, m_nTotRelevantPxCount; - //! current frame index, frame count since last model reset & model reset cooldown counters - size_t m_nFrameIndex, m_nFramesSinceLastReset, m_nModelResetCooldown; - //! pre-allocated internal LBSP threshold values LUT for all possible 8-bit intensities - size_t m_anLBSPThreshold_8bitLUT[UCHAR_MAX + 1]; - //! internal pixel index LUT for all relevant analysis regions (based on the provided ROI) - size_t* m_aPxIdxLUT; - //! internal pixel info LUT for all possible pixel indexes - PxInfoBase* m_aPxInfoLUT; - //! default kernel size for median blur post-proc filtering - const int m_nDefaultMedianBlurKernelSize; - //! specifies whether the algorithm is fully initialized or not - bool m_bInitialized; - //! specifies whether automatic model resets are enabled or not - bool m_bAutoModelResetEnabled; - //! specifies whether the camera is considered moving or not - bool m_bUsingMovingCamera; - //! copy of latest pixel intensities (used when refreshing model) - cv::Mat m_oLastColorFrame; - //! copy of latest descriptors (used when refreshing model) - cv::Mat m_oLastDescFrame; - //! the foreground mask generated by the method at [t-1] - cv::Mat m_oLastFGMask; + protected: + struct PxInfoBase { + int nImgCoord_Y; + int nImgCoord_X; + size_t nModelIdx; + }; + //! background model ROI used for LBSP descriptor extraction (specific to the input image size) + cv::Mat m_oROI; + //! input image size + cv::Size m_oImgSize; + //! input image channel size + size_t m_nImgChannels; + //! input image type + int m_nImgType; + //! 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; + //! total number of pixels (depends on the input frame size) & total number of relevant pixels + size_t m_nTotPxCount, m_nTotRelevantPxCount; + //! current frame index, frame count since last model reset & model reset cooldown counters + size_t m_nFrameIndex, m_nFramesSinceLastReset, m_nModelResetCooldown; + //! pre-allocated internal LBSP threshold values LUT for all possible 8-bit intensities + size_t m_anLBSPThreshold_8bitLUT[UCHAR_MAX + 1]; + //! internal pixel index LUT for all relevant analysis regions (based on the provided ROI) + size_t* m_aPxIdxLUT; + //! internal pixel info LUT for all possible pixel indexes + PxInfoBase* m_aPxInfoLUT; + //! default kernel size for median blur post-proc filtering + const int m_nDefaultMedianBlurKernelSize; + //! specifies whether the algorithm is fully initialized or not + bool m_bInitialized; + //! specifies whether automatic model resets are enabled or not + bool m_bAutoModelResetEnabled; + //! specifies whether the camera is considered moving or not + bool m_bUsingMovingCamera; + //! copy of latest pixel intensities (used when refreshing model) + cv::Mat m_oLastColorFrame; + //! copy of latest descriptors (used when refreshing model) + cv::Mat m_oLastDescFrame; + //! the foreground mask generated by the method at [t-1] + cv::Mat m_oLastFGMask; -private: - //! copy constructor -- disabled since this class (and its children) use lots of dynamic structs based on raw pointers - BackgroundSubtractorLBSP_(const BackgroundSubtractorLBSP_&); - //! assignment operator -- disabled since this class (and its children) use lots of dynamic structs based on raw pointers - BackgroundSubtractorLBSP_& operator=(const BackgroundSubtractorLBSP_&); + private: + //! copy constructor -- disabled since this class (and its children) use lots of dynamic structs based on raw pointers + BackgroundSubtractorLBSP_(const BackgroundSubtractorLBSP_&); + //! assignment operator -- disabled since this class (and its children) use lots of dynamic structs based on raw pointers + BackgroundSubtractorLBSP_& operator=(const BackgroundSubtractorLBSP_&); -public: - // ######## DEBUG PURPOSES ONLY ########## - int m_nDebugCoordX, m_nDebugCoordY; - std::string m_sDebugName; - cv::FileStorage* m_pDebugFS; -}; + public: + // ######## DEBUG PURPOSES ONLY ########## + int m_nDebugCoordX, m_nDebugCoordY; + std::string m_sDebugName; + cv::FileStorage* m_pDebugFS; + }; + } + } +} diff --git a/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.cpp b/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.cpp index 1b67ae6ca3859b3ffce2ba2487dcd94225141cb4..330a1ac50889d908942ce98d35085b37b45e6ac6 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.cpp +++ b/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.cpp @@ -7,6 +7,8 @@ #include "BackgroundSubtractorLOBSTER.h" #include "RandUtils.h" +using namespace bgslibrary::algorithms::lbsp; + BackgroundSubtractorLOBSTER::BackgroundSubtractorLOBSTER(float fRelLBSPThreshold , size_t nLBSPThresholdOffset , size_t nDescDistThreshold diff --git a/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.h b/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.h index d229e52ae2b87e9499829364a70bc0faf1960fad..b7860a188628fdb3ec7c35a0bd703910ddeed9a5 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.h +++ b/src/algorithms/LBSP/BackgroundSubtractorLOBSTER.h @@ -2,66 +2,74 @@ #include "BackgroundSubtractorLBSP.h" -//! defines the default value for BackgroundSubtractorLBSP::m_fRelLBSPThreshold -#define BGSLOBSTER_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD (0.365f) -//! defines the default value for BackgroundSubtractorLBSP::m_nLBSPThresholdOffset -#define BGSLOBSTER_DEFAULT_LBSP_OFFSET_SIMILARITY_THRESHOLD (0) -//! defines the default value for BackgroundSubtractorLOBSTER::m_nDescDistThreshold -#define BGSLOBSTER_DEFAULT_DESC_DIST_THRESHOLD (4) -//! defines the default value for BackgroundSubtractorLOBSTER::m_nColorDistThreshold -#define BGSLOBSTER_DEFAULT_COLOR_DIST_THRESHOLD (30) -//! defines the default value for BackgroundSubtractorLOBSTER::m_nBGSamples -#define BGSLOBSTER_DEFAULT_NB_BG_SAMPLES (35) -//! defines the default value for BackgroundSubtractorLOBSTER::m_nRequiredBGSamples -#define BGSLOBSTER_DEFAULT_REQUIRED_NB_BG_SAMPLES (2) -//! defines the default value for the learning rate passed to BackgroundSubtractorLOBSTER::operator() -#define BGSLOBSTER_DEFAULT_LEARNING_RATE (16) +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + //! defines the default value for BackgroundSubtractorLBSP::m_fRelLBSPThreshold + const float BGSLOBSTER_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD = 0.365f; + //! defines the default value for BackgroundSubtractorLBSP::m_nLBSPThresholdOffset + const int BGSLOBSTER_DEFAULT_LBSP_OFFSET_SIMILARITY_THRESHOLD = 0; + //! defines the default value for BackgroundSubtractorLOBSTER::m_nDescDistThreshold + const int BGSLOBSTER_DEFAULT_DESC_DIST_THRESHOLD = 4; + //! defines the default value for BackgroundSubtractorLOBSTER::m_nColorDistThreshold + const int BGSLOBSTER_DEFAULT_COLOR_DIST_THRESHOLD = 30; + //! defines the default value for BackgroundSubtractorLOBSTER::m_nBGSamples + const int BGSLOBSTER_DEFAULT_NB_BG_SAMPLES = 35; + //! defines the default value for BackgroundSubtractorLOBSTER::m_nRequiredBGSamples + const int BGSLOBSTER_DEFAULT_REQUIRED_NB_BG_SAMPLES = 2; + //! defines the default value for the learning rate passed to BackgroundSubtractorLOBSTER::operator() + const int BGSLOBSTER_DEFAULT_LEARNING_RATE = 16; -/*! - LOcal Binary Similarity segmenTER (LOBSTER) change detection algorithm. + /*! + LOcal Binary Similarity segmenTER (LOBSTER) change detection 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. + 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 parameters or on the algorithm itself, see P.-L. St-Charles and - G.-A. Bilodeau, "Improving Background Subtraction using Local Binary Similarity Patterns", in WACV 2014. + For more details on the different parameters or on the algorithm itself, see P.-L. St-Charles and + G.-A. Bilodeau, "Improving Background Subtraction using Local Binary Similarity Patterns", in WACV 2014. - This algorithm is currently NOT thread-safe. - */ -class BackgroundSubtractorLOBSTER : public BackgroundSubtractorLBSP { -public: - //! full constructor - BackgroundSubtractorLOBSTER(float fRelLBSPThreshold = BGSLOBSTER_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, - size_t nLBSPThresholdOffset = BGSLOBSTER_DEFAULT_LBSP_OFFSET_SIMILARITY_THRESHOLD, - size_t nDescDistThreshold = BGSLOBSTER_DEFAULT_DESC_DIST_THRESHOLD, - size_t nColorDistThreshold = BGSLOBSTER_DEFAULT_COLOR_DIST_THRESHOLD, - size_t nBGSamples = BGSLOBSTER_DEFAULT_NB_BG_SAMPLES, - size_t nRequiredBGSamples = BGSLOBSTER_DEFAULT_REQUIRED_NB_BG_SAMPLES); - //! default destructor - virtual ~BackgroundSubtractorLOBSTER(); - //! (re)initiaization method; needs to be called before starting background subtraction - virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI); - //! refreshes all samples based on the last analyzed frame - virtual void refreshModel(float fSamplesRefreshFrac, bool bForceFGUpdate = false); - //! primary model update function; the learning param is reinterpreted as an integer and should be > 0 (smaller values == faster adaptation) - virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = BGSLOBSTER_DEFAULT_LEARNING_RATE); - //! returns a copy of the latest reconstructed background image - void getBackgroundImage(cv::OutputArray backgroundImage) const; - //! returns a copy of the latest reconstructed background descriptors image - virtual void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; - -protected: - //! absolute color distance threshold - const size_t m_nColorDistThreshold; - //! absolute descriptor distance threshold - const size_t m_nDescDistThreshold; - //! number of different samples per pixel/block to be taken from input frames to build the background model - const size_t m_nBGSamples; - //! number of similar samples needed to consider the current pixel/block as 'background' - const size_t m_nRequiredBGSamples; - //! background model pixel intensity samples - std::vector<cv::Mat> m_voBGColorSamples; - //! background model descriptors samples - std::vector<cv::Mat> m_voBGDescSamples; -}; + This algorithm is currently NOT thread-safe. + */ + class BackgroundSubtractorLOBSTER : public BackgroundSubtractorLBSP { + public: + //! full constructor + BackgroundSubtractorLOBSTER(float fRelLBSPThreshold = BGSLOBSTER_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, + size_t nLBSPThresholdOffset = BGSLOBSTER_DEFAULT_LBSP_OFFSET_SIMILARITY_THRESHOLD, + size_t nDescDistThreshold = BGSLOBSTER_DEFAULT_DESC_DIST_THRESHOLD, + size_t nColorDistThreshold = BGSLOBSTER_DEFAULT_COLOR_DIST_THRESHOLD, + size_t nBGSamples = BGSLOBSTER_DEFAULT_NB_BG_SAMPLES, + size_t nRequiredBGSamples = BGSLOBSTER_DEFAULT_REQUIRED_NB_BG_SAMPLES); + //! default destructor + virtual ~BackgroundSubtractorLOBSTER(); + //! (re)initiaization method; needs to be called before starting background subtraction + virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI); + //! refreshes all samples based on the last analyzed frame + virtual void refreshModel(float fSamplesRefreshFrac, bool bForceFGUpdate = false); + //! primary model update function; the learning param is reinterpreted as an integer and should be > 0 (smaller values == faster adaptation) + virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = BGSLOBSTER_DEFAULT_LEARNING_RATE); + //! returns a copy of the latest reconstructed background image + void getBackgroundImage(cv::OutputArray backgroundImage) const; + //! returns a copy of the latest reconstructed background descriptors image + virtual void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; + protected: + //! absolute color distance threshold + const size_t m_nColorDistThreshold; + //! absolute descriptor distance threshold + const size_t m_nDescDistThreshold; + //! number of different samples per pixel/block to be taken from input frames to build the background model + const size_t m_nBGSamples; + //! number of similar samples needed to consider the current pixel/block as 'background' + const size_t m_nRequiredBGSamples; + //! background model pixel intensity samples + std::vector<cv::Mat> m_voBGColorSamples; + //! background model descriptors samples + std::vector<cv::Mat> m_voBGDescSamples; + }; + } + } +} diff --git a/src/algorithms/LBSP/BackgroundSubtractorPAWCS.cpp b/src/algorithms/LBSP/BackgroundSubtractorPAWCS.cpp index 8a1415d2d5fecfcc526afe452f0b871e42cbfba0..acb1a8f4b522fda388fcfe4d51873dc9c989d705 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorPAWCS.cpp +++ b/src/algorithms/LBSP/BackgroundSubtractorPAWCS.cpp @@ -7,1328 +7,1339 @@ #include "BackgroundSubtractorPAWCS.h" #include "RandUtils.h" -/* - * - * Intrinsic configuration parameters are defined here; tuning these for better - * performance should not be required in most cases -- although improvements in - * very specific scenarios are always possible. - * - */ - //! parameter used for 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 (1.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_RATIO_MIN (0.100f) -#define LBSPDESC_RATIO_MAX (0.500f) -//! parameters used to trigger auto model resets in our frame-level component -#define FRAMELEVEL_MIN_L1DIST_THRES (45) -#define FRAMELEVEL_MIN_CDIST_THRES (FRAMELEVEL_MIN_L1DIST_THRES/10) -#define FRAMELEVEL_DOWNSAMPLE_RATIO (8) -//! parameters used to downscale gword maps & scale thresholds to make comparisons easier -#define GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO (2) -#define GWORD_DEFAULT_NB_INIT_SAMPL_PASSES (2) -#define GWORD_DESC_THRES_BITS_MATCH_FACTOR (4) +//using namespace bgslibrary::algorithms::lbsp; -// local define used to specify the default frame size (320x240 = QVGA) -#define DEFAULT_FRAME_SIZE cv::Size(320,240) -// local define used to specify the default lword/gword update rate (16 = like vibe) -#define DEFAULT_RESAMPLING_RATE (16) -// local define used to specify the bootstrap window size for faster model stabilization -#define DEFAULT_BOOTSTRAP_WIN_SIZE (500) -// local define for the amount of weight offset to apply to words, making sure new words aren't always better than old ones -#define DEFAULT_LWORD_WEIGHT_OFFSET (DEFAULT_BOOTSTRAP_WIN_SIZE*2) -// local define used to set the default local word occurrence increment -#define DEFAULT_LWORD_OCC_INCR 1 -// local define for the maximum weight a word can achieve before cutting off occ incr (used to make sure model stays good for long-term uses) -#define DEFAULT_LWORD_MAX_WEIGHT (1.0f) -// local define for the initial weight of a new word (used to make sure old words aren't worse off than new seeds) -#define DEFAULT_LWORD_INIT_WEIGHT (1.0f/m_nLocalWordWeightOffset) -// local define used to specify the desc dist threshold offset used for unstable regions -#define UNSTAB_DESC_DIST_OFFSET (m_nDescDistThresholdOffset) -// local define used to specify the min descriptor bit count for flat regions -#define FLAT_REGION_BIT_COUNT (s_nDescMaxDataRange_1ch/8) +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /* + * + * Intrinsic configuration parameters are defined here; tuning these for better + * performance should not be required in most cases -- although improvements in + * very specific scenarios are always possible. + * + */ + //! parameter used for dynamic distance threshold adjustments ('R(x)') + const float FEEDBACK_R_VAR = 0.01f; + //! parameters used to adjust the variation step size of 'v(x)' + const float FEEDBACK_V_INCR = 1.000f; + const float FEEDBACK_V_DECR = 0.100f; + //! parameters used to scale dynamic learning rate adjustments ('T(x)') + const float FEEDBACK_T_DECR = 0.2500f; + const float FEEDBACK_T_INCR = 0.5000f; + const float FEEDBACK_T_LOWER = 1.0000f; + const float FEEDBACK_T_UPPER = 256.00f; + //! parameters used to define 'unstable' regions, based on segm noise/bg dynamics and local dist threshold values + const float UNSTABLE_REG_RATIO_MIN = 0.100f; + const float UNSTABLE_REG_RDIST_MIN = 3.000f; + //! parameters used to scale the relative LBSP intensity threshold used for internal comparisons + const float LBSPDESC_RATIO_MIN = 0.100f; + const float LBSPDESC_RATIO_MAX = 0.500f; + //! parameters used to trigger auto model resets in our frame-level component + const int FRAMELEVEL_MIN_L1DIST_THRES = 45; + const float FRAMELEVEL_MIN_CDIST_THRES = (FRAMELEVEL_MIN_L1DIST_THRES / 10); + const int FRAMELEVEL_DOWNSAMPLE_RATIO = 8; + //! parameters used to downscale gword maps & scale thresholds to make comparisons easier + const int GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO = 2; + const int GWORD_DEFAULT_NB_INIT_SAMPL_PASSES = 2; + const int GWORD_DESC_THRES_BITS_MATCH_FACTOR = 4; -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; + // local define used to specify the default frame size (320x240 = QVGA) + #define DEFAULT_FRAME_SIZE cv::Size(320,240) + // local define used to specify the default lword/gword update rate (16 = like vibe) + const int DEFAULT_RESAMPLING_RATE = 16; + // local define used to specify the bootstrap window size for faster model stabilization + const int DEFAULT_BOOTSTRAP_WIN_SIZE = 500; + // local define for the amount of weight offset to apply to words, making sure new words aren't always better than old ones + const float DEFAULT_LWORD_WEIGHT_OFFSET = (DEFAULT_BOOTSTRAP_WIN_SIZE * 2); + // local define used to set the default local word occurrence increment + const int DEFAULT_LWORD_OCC_INCR = 1; + // local define for the maximum weight a word can achieve before cutting off occ incr (used to make sure model stays good for long-term uses) + const float DEFAULT_LWORD_MAX_WEIGHT = 1.0f; + // local define for the initial weight of a new word (used to make sure old words aren't worse off than new seeds) + const double DEFAULT_LWORD_INIT_WEIGHT = (1.0f / DEFAULT_LWORD_WEIGHT_OFFSET); + // local define used to specify the desc dist threshold offset used for unstable regions + #define UNSTAB_DESC_DIST_OFFSET (m_nDescDistThresholdOffset) + // local define used to specify the min descriptor bit count for flat regions + #define FLAT_REGION_BIT_COUNT (s_nDescMaxDataRange_1ch/8) -BackgroundSubtractorPAWCS::BackgroundSubtractorPAWCS(float fRelLBSPThreshold - , size_t nDescDistThresholdOffset - , size_t nMinColorDistThreshold - , size_t nMaxNbWords - , size_t nSamplesForMovingAvgs) - : BackgroundSubtractorLBSP_(fRelLBSPThreshold) - , m_nMinColorDistThreshold(nMinColorDistThreshold) - , m_nDescDistThresholdOffset(nDescDistThresholdOffset) - , m_nMaxLocalWords(nMaxNbWords) - , m_nCurrLocalWords(0) - , m_nMaxGlobalWords(nMaxNbWords / 2) - , m_nCurrGlobalWords(0) - , m_nSamplesForMovingAvgs(nSamplesForMovingAvgs) - , m_fLastNonFlatRegionRatio(0.0f) - , m_nMedianBlurKernelSize(m_nDefaultMedianBlurKernelSize) - , m_nDownSampledROIPxCount(0) - , m_nLocalWordWeightOffset(DEFAULT_LWORD_WEIGHT_OFFSET) - , m_apLocalWordDict(nullptr) - , m_aLocalWordList_1ch(nullptr) - , m_pLocalWordListIter_1ch(nullptr) - , m_aLocalWordList_3ch(nullptr) - , m_pLocalWordListIter_3ch(nullptr) - , m_apGlobalWordDict(nullptr) - , m_aGlobalWordList_1ch(nullptr) - , m_pGlobalWordListIter_1ch(nullptr) - , m_aGlobalWordList_3ch(nullptr) - , m_pGlobalWordListIter_3ch(nullptr) - , m_aPxInfoLUT_PAWCS(nullptr) { - CV_Assert(m_nMaxLocalWords > 0 && m_nMaxGlobalWords > 0); -} - -BackgroundSubtractorPAWCS::~BackgroundSubtractorPAWCS() { - CleanupDictionaries(); -} + 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; -void BackgroundSubtractorPAWCS::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) { - // == init - CV_Assert(!oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0); - CV_Assert(oInitImg.isContinuous()); - CV_Assert(oInitImg.type() == CV_8UC3 || oInitImg.type() == CV_8UC1); - if (oInitImg.type() == CV_8UC3) { - std::vector<cv::Mat> voInitImgChannels; - cv::split(oInitImg, voInitImgChannels); - if (!cv::countNonZero((voInitImgChannels[0] != voInitImgChannels[1]) | (voInitImgChannels[2] != voInitImgChannels[1]))) - std::cout << std::endl << "\tBackgroundSubtractorPAWCS : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl; - } - cv::Mat oNewBGROI; - if (oROI.empty() && (m_oROI.empty() || oROI.size() != oInitImg.size())) { - oNewBGROI.create(oInitImg.size(), CV_8UC1); - oNewBGROI = cv::Scalar_<uchar>(UCHAR_MAX); - } - else if (oROI.empty()) - oNewBGROI = m_oROI; - else { - CV_Assert(oROI.size() == oInitImg.size() && oROI.type() == CV_8UC1); - CV_Assert(cv::countNonZero((oROI < UCHAR_MAX)&(oROI > 0)) == 0); - oNewBGROI = oROI.clone(); - cv::Mat oTempROI; - cv::dilate(oNewBGROI, oTempROI, cv::Mat(), cv::Point(-1, -1), LBSP_::PATCH_SIZE / 2); - cv::bitwise_or(oNewBGROI, oTempROI / 2, oNewBGROI); - } - const size_t nOrigROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nOrigROIPxCount > 0); - LBSP_::validateROI(oNewBGROI); - const size_t nFinalROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nFinalROIPxCount > 0); - CleanupDictionaries(); - m_oROI = oNewBGROI; - m_oImgSize = oInitImg.size(); - m_nImgType = oInitImg.type(); - m_nImgChannels = oInitImg.channels(); - m_nTotPxCount = m_oImgSize.area(); - m_nTotRelevantPxCount = nFinalROIPxCount; - m_nFrameIndex = 0; - m_nFramesSinceLastReset = 0; - m_nModelResetCooldown = 0; - m_bUsingMovingCamera = false; - m_oDownSampledFrameSize_MotionAnalysis = cv::Size(m_oImgSize.width / FRAMELEVEL_DOWNSAMPLE_RATIO, m_oImgSize.height / FRAMELEVEL_DOWNSAMPLE_RATIO); - m_oDownSampledFrameSize_GlobalWordLookup = cv::Size(m_oImgSize.width / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO, m_oImgSize.height / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO); - cv::resize(m_oROI, m_oDownSampledROI_MotionAnalysis, m_oDownSampledFrameSize_MotionAnalysis, 0, 0, cv::INTER_AREA); - m_fLastNonFlatRegionRatio = 0.0f; - m_nCurrLocalWords = m_nMaxLocalWords; - if (nOrigROIPxCount >= m_nTotPxCount / 2 && (int)m_nTotPxCount >= DEFAULT_FRAME_SIZE.area()) { - const float fRegionSizeScaleFactor = (float)m_nTotPxCount / DEFAULT_FRAME_SIZE.area(); - const int nRawMedianBlurKernelSize = std::min((int)floor(0.5f + fRegionSizeScaleFactor) + m_nDefaultMedianBlurKernelSize, m_nDefaultMedianBlurKernelSize + 4); - m_nMedianBlurKernelSize = (nRawMedianBlurKernelSize % 2) ? nRawMedianBlurKernelSize : nRawMedianBlurKernelSize - 1; - m_nCurrGlobalWords = m_nMaxGlobalWords; - m_oDownSampledROI_MotionAnalysis |= UCHAR_MAX / 2; - } - else { - const float fRegionSizeScaleFactor = (float)nOrigROIPxCount / DEFAULT_FRAME_SIZE.area(); - const int nRawMedianBlurKernelSize = std::min((int)floor(0.5f + m_nDefaultMedianBlurKernelSize*fRegionSizeScaleFactor * 2) + (m_nDefaultMedianBlurKernelSize - 4), m_nDefaultMedianBlurKernelSize); - m_nMedianBlurKernelSize = (nRawMedianBlurKernelSize % 2) ? nRawMedianBlurKernelSize : nRawMedianBlurKernelSize - 1; - m_nCurrGlobalWords = std::min((size_t)std::pow(m_nMaxGlobalWords*fRegionSizeScaleFactor, 2) + 1, m_nMaxGlobalWords); - } - if (m_nImgChannels == 1) { - m_nCurrLocalWords = std::max(m_nCurrLocalWords / 2, (size_t)1); - m_nCurrGlobalWords = std::max(m_nCurrGlobalWords / 2, (size_t)1); - } - m_nDownSampledROIPxCount = (size_t)cv::countNonZero(m_oDownSampledROI_MotionAnalysis); - m_nLocalWordWeightOffset = DEFAULT_LWORD_WEIGHT_OFFSET; - m_oIllumUpdtRegionMask.create(m_oImgSize, CV_8UC1); - m_oIllumUpdtRegionMask = cv::Scalar_<uchar>(0); - m_oUpdateRateFrame.create(m_oImgSize, CV_32FC1); - m_oUpdateRateFrame = cv::Scalar(FEEDBACK_T_LOWER); - m_oDistThresholdFrame.create(m_oImgSize, CV_32FC1); - m_oDistThresholdFrame = cv::Scalar(2.0f); - m_oDistThresholdVariationFrame.create(m_oImgSize, CV_32FC1); - m_oDistThresholdVariationFrame = cv::Scalar(FEEDBACK_V_INCR * 10); - m_oMeanMinDistFrame_LT.create(m_oImgSize, CV_32FC1); - m_oMeanMinDistFrame_LT = cv::Scalar(0.0f); - m_oMeanMinDistFrame_ST.create(m_oImgSize, CV_32FC1); - m_oMeanMinDistFrame_ST = cv::Scalar(0.0f); - m_oMeanDownSampledLastDistFrame_LT.create(m_oDownSampledFrameSize_MotionAnalysis, CV_32FC((int)m_nImgChannels)); - m_oMeanDownSampledLastDistFrame_LT = cv::Scalar(0.0f); - m_oMeanDownSampledLastDistFrame_ST.create(m_oDownSampledFrameSize_MotionAnalysis, CV_32FC((int)m_nImgChannels)); - m_oMeanDownSampledLastDistFrame_ST = cv::Scalar(0.0f); - m_oMeanRawSegmResFrame_LT.create(m_oImgSize, CV_32FC1); - m_oMeanRawSegmResFrame_LT = cv::Scalar(0.0f); - m_oMeanRawSegmResFrame_ST.create(m_oImgSize, CV_32FC1); - m_oMeanRawSegmResFrame_ST = cv::Scalar(0.0f); - m_oMeanFinalSegmResFrame_LT.create(m_oImgSize, CV_32FC1); - m_oMeanFinalSegmResFrame_LT = cv::Scalar(0.0f); - m_oMeanFinalSegmResFrame_ST.create(m_oImgSize, CV_32FC1); - m_oMeanFinalSegmResFrame_ST = cv::Scalar(0.0f); - m_oUnstableRegionMask.create(m_oImgSize, CV_8UC1); - m_oUnstableRegionMask = cv::Scalar_<uchar>(0); - m_oBlinksFrame.create(m_oImgSize, CV_8UC1); - m_oBlinksFrame = cv::Scalar_<uchar>(0); - m_oDownSampledFrame_MotionAnalysis.create(m_oDownSampledFrameSize_MotionAnalysis, CV_8UC((int)m_nImgChannels)); - m_oDownSampledFrame_MotionAnalysis = cv::Scalar_<uchar>::all(0); - m_oLastColorFrame.create(m_oImgSize, CV_8UC((int)m_nImgChannels)); - m_oLastColorFrame = cv::Scalar_<uchar>::all(0); - m_oLastDescFrame.create(m_oImgSize, CV_16UC((int)m_nImgChannels)); - m_oLastDescFrame = cv::Scalar_<ushort>::all(0); - m_oLastRawFGMask.create(m_oImgSize, CV_8UC1); - m_oLastRawFGMask = cv::Scalar_<uchar>(0); - m_oLastFGMask.create(m_oImgSize, CV_8UC1); - m_oLastFGMask = cv::Scalar_<uchar>(0); - m_oLastFGMask_dilated.create(m_oImgSize, CV_8UC1); - m_oLastFGMask_dilated = cv::Scalar_<uchar>(0); - m_oLastFGMask_dilated_inverted.create(m_oImgSize, CV_8UC1); - m_oLastFGMask_dilated_inverted = cv::Scalar_<uchar>(0); - m_oFGMask_FloodedHoles.create(m_oImgSize, CV_8UC1); - m_oFGMask_FloodedHoles = cv::Scalar_<uchar>(0); - m_oFGMask_PreFlood.create(m_oImgSize, CV_8UC1); - m_oFGMask_PreFlood = cv::Scalar_<uchar>(0); - m_oCurrRawFGBlinkMask.create(m_oImgSize, CV_8UC1); - m_oCurrRawFGBlinkMask = cv::Scalar_<uchar>(0); - m_oLastRawFGBlinkMask.create(m_oImgSize, CV_8UC1); - m_oLastRawFGBlinkMask = cv::Scalar_<uchar>(0); - m_oTempGlobalWordWeightDiffFactor.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); - m_oTempGlobalWordWeightDiffFactor = cv::Scalar(-0.1f); - m_oMorphExStructElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); - m_aPxIdxLUT = new size_t[m_nTotRelevantPxCount]; - memset(m_aPxIdxLUT, 0, sizeof(size_t)*m_nTotRelevantPxCount); - m_aPxInfoLUT_PAWCS = new PxInfo_PAWCS[m_nTotPxCount]; - memset(m_aPxInfoLUT_PAWCS, 0, sizeof(PxInfo_PAWCS)*m_nTotPxCount); - m_aPxInfoLUT = m_aPxInfoLUT_PAWCS; - m_apLocalWordDict = new LocalWordBase*[m_nTotRelevantPxCount*m_nCurrLocalWords]; - memset(m_apLocalWordDict, 0, sizeof(LocalWordBase*)*m_nTotRelevantPxCount*m_nCurrLocalWords); - m_apGlobalWordDict = new GlobalWordBase*[m_nCurrGlobalWords]; - memset(m_apGlobalWordDict, 0, sizeof(GlobalWordBase*)*m_nCurrGlobalWords); - if (m_nImgChannels == 1) { - CV_DbgAssert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width && m_oLastColorFrame.step.p[1] == 1); - CV_DbgAssert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); - m_aLocalWordList_1ch = new LocalWord_1ch[m_nTotRelevantPxCount*m_nCurrLocalWords]; - memset(m_aLocalWordList_1ch, 0, sizeof(LocalWord_1ch)*m_nTotRelevantPxCount*m_nCurrLocalWords); - m_pLocalWordListIter_1ch = m_aLocalWordList_1ch; - m_aGlobalWordList_1ch = new GlobalWord_1ch[m_nCurrGlobalWords]; - m_pGlobalWordListIter_1ch = m_aGlobalWordList_1ch; - for (size_t t = 0; t <= UCHAR_MAX; ++t) - m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>((m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold) / 3); - for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { - if (m_oROI.data[nPxIter]) { - m_aPxIdxLUT[nModelIter] = nPxIter; - m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; - m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; - m_aPxInfoLUT_PAWCS[nPxIter].nModelIdx = nModelIter; - m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx = (size_t)((m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)*m_oDownSampledFrameSize_GlobalWordLookup.width + (m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)) * 4; - m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT = new GlobalWordBase*[m_nCurrGlobalWords]; - for (size_t nGlobalWordIdxIter = 0; nGlobalWordIdxIter < m_nCurrGlobalWords; ++nGlobalWordIdxIter) - m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordIdxIter] = &(m_aGlobalWordList_1ch[nGlobalWordIdxIter]); - m_oLastColorFrame.data[nPxIter] = oInitImg.data[nPxIter]; - const size_t nDescIter = nPxIter * 2; - LBSP_::computeGrayscaleDescriptor(oInitImg, oInitImg.data[nPxIter], m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxIter]], *((ushort*)(m_oLastDescFrame.data + nDescIter))); - ++nModelIter; + BackgroundSubtractorPAWCS::BackgroundSubtractorPAWCS(float fRelLBSPThreshold + , size_t nDescDistThresholdOffset + , size_t nMinColorDistThreshold + , size_t nMaxNbWords + , size_t nSamplesForMovingAvgs) + : BackgroundSubtractorLBSP_(fRelLBSPThreshold) + , m_nMinColorDistThreshold(nMinColorDistThreshold) + , m_nDescDistThresholdOffset(nDescDistThresholdOffset) + , m_nMaxLocalWords(nMaxNbWords) + , m_nCurrLocalWords(0) + , m_nMaxGlobalWords(nMaxNbWords / 2) + , m_nCurrGlobalWords(0) + , m_nSamplesForMovingAvgs(nSamplesForMovingAvgs) + , m_fLastNonFlatRegionRatio(0.0f) + , m_nMedianBlurKernelSize(m_nDefaultMedianBlurKernelSize) + , m_nDownSampledROIPxCount(0) + , m_nLocalWordWeightOffset(DEFAULT_LWORD_WEIGHT_OFFSET) + , m_apLocalWordDict(nullptr) + , m_aLocalWordList_1ch(nullptr) + , m_pLocalWordListIter_1ch(nullptr) + , m_aLocalWordList_3ch(nullptr) + , m_pLocalWordListIter_3ch(nullptr) + , m_apGlobalWordDict(nullptr) + , m_aGlobalWordList_1ch(nullptr) + , m_pGlobalWordListIter_1ch(nullptr) + , m_aGlobalWordList_3ch(nullptr) + , m_pGlobalWordListIter_3ch(nullptr) + , m_aPxInfoLUT_PAWCS(nullptr) { + CV_Assert(m_nMaxLocalWords > 0 && m_nMaxGlobalWords > 0); } - } - } - else { //m_nImgChannels==3 - CV_DbgAssert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width * 3 && m_oLastColorFrame.step.p[1] == 3); - CV_DbgAssert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); - m_aLocalWordList_3ch = new LocalWord_3ch[m_nTotRelevantPxCount*m_nCurrLocalWords]; - memset(m_aLocalWordList_3ch, 0, sizeof(LocalWord_3ch)*m_nTotRelevantPxCount*m_nCurrLocalWords); - m_pLocalWordListIter_3ch = m_aLocalWordList_3ch; - m_aGlobalWordList_3ch = new GlobalWord_3ch[m_nCurrGlobalWords]; - m_pGlobalWordListIter_3ch = m_aGlobalWordList_3ch; - for (size_t t = 0; t <= UCHAR_MAX; ++t) - m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold); - for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { - if (m_oROI.data[nPxIter]) { - m_aPxIdxLUT[nModelIter] = nPxIter; - m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; - m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; - m_aPxInfoLUT_PAWCS[nPxIter].nModelIdx = nModelIter; - m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx = (size_t)((m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)*m_oDownSampledFrameSize_GlobalWordLookup.width + (m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)) * 4; - m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT = new GlobalWordBase*[m_nCurrGlobalWords]; - for (size_t nGlobalWordIdxIter = 0; nGlobalWordIdxIter < m_nCurrGlobalWords; ++nGlobalWordIdxIter) - m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordIdxIter] = &(m_aGlobalWordList_3ch[nGlobalWordIdxIter]); - const size_t nPxRGBIter = nPxIter * 3; - const size_t nDescRGBIter = nPxRGBIter * 2; - for (size_t c = 0; c < 3; ++c) { - m_oLastColorFrame.data[nPxRGBIter + c] = oInitImg.data[nPxRGBIter + c]; - LBSP_::computeSingleRGBDescriptor(oInitImg, oInitImg.data[nPxRGBIter + c], m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxRGBIter + c]], ((ushort*)(m_oLastDescFrame.data + nDescRGBIter))[c]); - } - ++nModelIter; + + BackgroundSubtractorPAWCS::~BackgroundSubtractorPAWCS() { + CleanupDictionaries(); } - } - } - m_bInitialized = true; - refreshModel(1, 0); -} -void BackgroundSubtractorPAWCS::refreshModel(size_t nBaseOccCount, float fOccDecrFrac, bool bForceFGUpdate) { - // == refresh - CV_Assert(m_bInitialized); - CV_Assert(fOccDecrFrac >= 0.0f && fOccDecrFrac <= 1.0f); - if (m_nImgChannels == 1) { - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { - const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; - const size_t nFloatIter = nPxIter * 4; - uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; - const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); - const size_t nCurrColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) / 2; - const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET); - // == refresh: local decr - if (fOccDecrFrac > 0.0f) { - for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - if (pCurrLocalWord) - pCurrLocalWord->nOccurrences -= (size_t)(fOccDecrFrac*pCurrLocalWord->nOccurrences); - } + void BackgroundSubtractorPAWCS::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) { + // == init + CV_Assert(!oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0); + CV_Assert(oInitImg.isContinuous()); + CV_Assert(oInitImg.type() == CV_8UC3 || oInitImg.type() == CV_8UC1); + if (oInitImg.type() == CV_8UC3) { + std::vector<cv::Mat> voInitImgChannels; + cv::split(oInitImg, voInitImgChannels); + if (!cv::countNonZero((voInitImgChannels[0] != voInitImgChannels[1]) | (voInitImgChannels[2] != voInitImgChannels[1]))) + std::cout << std::endl << "\tBackgroundSubtractorPAWCS : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl; } - const size_t nCurrWordOccIncr = DEFAULT_LWORD_OCC_INCR; - const size_t nTotLocalSamplingIterCount = (s_nSamplesInitPatternWidth*s_nSamplesInitPatternHeight) * 2; - for (size_t nLocalSamplingIter = 0; nLocalSamplingIter < nTotLocalSamplingIterCount; ++nLocalSamplingIter) { - // == refresh: local resampling - int nSampleImgCoord_Y, nSampleImgCoord_X; - getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); - const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nSamplePxIdx]) { - const uchar nSampleColor = m_oLastColorFrame.data[nSamplePxIdx]; - const size_t nSampleDescIdx = nSamplePxIdx * 2; - const ushort nSampleIntraDesc = *((ushort*)(m_oLastDescFrame.data + nSampleDescIdx)); - bool bFoundUninitd = false; - size_t nLocalWordIdx; - for (nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - if (pCurrLocalWord - && L1dist(nSampleColor, pCurrLocalWord->oFeature.anColor[0]) <= nCurrColorDistThreshold - && hdist(nSampleIntraDesc, pCurrLocalWord->oFeature.anDesc[0]) <= nCurrDescDistThreshold) { - pCurrLocalWord->nOccurrences += nCurrWordOccIncr; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - break; - } - else if (!pCurrLocalWord) - bFoundUninitd = true; - } - if (nLocalWordIdx == m_nCurrLocalWords) { - nLocalWordIdx = m_nCurrLocalWords - 1; - LocalWord_1ch* pCurrLocalWord = bFoundUninitd ? m_pLocalWordListIter_1ch++ : (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - pCurrLocalWord->oFeature.anColor[0] = nSampleColor; - pCurrLocalWord->oFeature.anDesc[0] = nSampleIntraDesc; - pCurrLocalWord->nOccurrences = nBaseOccCount; - pCurrLocalWord->nFirstOcc = m_nFrameIndex; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; - } - while (nLocalWordIdx > 0 && (!m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1] || GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset) > GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1], m_nFrameIndex, m_nLocalWordWeightOffset))) { - std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); - --nLocalWordIdx; + cv::Mat oNewBGROI; + if (oROI.empty() && (m_oROI.empty() || oROI.size() != oInitImg.size())) { + oNewBGROI.create(oInitImg.size(), CV_8UC1); + oNewBGROI = cv::Scalar_<uchar>(UCHAR_MAX); + } + else if (oROI.empty()) + oNewBGROI = m_oROI; + else { + CV_Assert(oROI.size() == oInitImg.size() && oROI.type() == CV_8UC1); + CV_Assert(cv::countNonZero((oROI < UCHAR_MAX)&(oROI > 0)) == 0); + oNewBGROI = oROI.clone(); + cv::Mat oTempROI; + cv::dilate(oNewBGROI, oTempROI, cv::Mat(), cv::Point(-1, -1), LBSP_::PATCH_SIZE / 2); + cv::bitwise_or(oNewBGROI, oTempROI / 2, oNewBGROI); + } + const size_t nOrigROIPxCount = (size_t)cv::countNonZero(oNewBGROI); + CV_Assert(nOrigROIPxCount > 0); + LBSP_::validateROI(oNewBGROI); + const size_t nFinalROIPxCount = (size_t)cv::countNonZero(oNewBGROI); + CV_Assert(nFinalROIPxCount > 0); + CleanupDictionaries(); + m_oROI = oNewBGROI; + m_oImgSize = oInitImg.size(); + m_nImgType = oInitImg.type(); + m_nImgChannels = oInitImg.channels(); + m_nTotPxCount = m_oImgSize.area(); + m_nTotRelevantPxCount = nFinalROIPxCount; + m_nFrameIndex = 0; + m_nFramesSinceLastReset = 0; + m_nModelResetCooldown = 0; + m_bUsingMovingCamera = false; + m_oDownSampledFrameSize_MotionAnalysis = cv::Size(m_oImgSize.width / FRAMELEVEL_DOWNSAMPLE_RATIO, m_oImgSize.height / FRAMELEVEL_DOWNSAMPLE_RATIO); + m_oDownSampledFrameSize_GlobalWordLookup = cv::Size(m_oImgSize.width / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO, m_oImgSize.height / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO); + cv::resize(m_oROI, m_oDownSampledROI_MotionAnalysis, m_oDownSampledFrameSize_MotionAnalysis, 0, 0, cv::INTER_AREA); + m_fLastNonFlatRegionRatio = 0.0f; + m_nCurrLocalWords = m_nMaxLocalWords; + if (nOrigROIPxCount >= m_nTotPxCount / 2 && (int)m_nTotPxCount >= DEFAULT_FRAME_SIZE.area()) { + const float fRegionSizeScaleFactor = (float)m_nTotPxCount / DEFAULT_FRAME_SIZE.area(); + const int nRawMedianBlurKernelSize = std::min((int)floor(0.5f + fRegionSizeScaleFactor) + m_nDefaultMedianBlurKernelSize, m_nDefaultMedianBlurKernelSize + 4); + m_nMedianBlurKernelSize = (nRawMedianBlurKernelSize % 2) ? nRawMedianBlurKernelSize : nRawMedianBlurKernelSize - 1; + m_nCurrGlobalWords = m_nMaxGlobalWords; + m_oDownSampledROI_MotionAnalysis |= UCHAR_MAX / 2; + } + else { + const float fRegionSizeScaleFactor = (float)nOrigROIPxCount / DEFAULT_FRAME_SIZE.area(); + const int nRawMedianBlurKernelSize = std::min((int)floor(0.5f + m_nDefaultMedianBlurKernelSize*fRegionSizeScaleFactor * 2) + (m_nDefaultMedianBlurKernelSize - 4), m_nDefaultMedianBlurKernelSize); + m_nMedianBlurKernelSize = (nRawMedianBlurKernelSize % 2) ? nRawMedianBlurKernelSize : nRawMedianBlurKernelSize - 1; + m_nCurrGlobalWords = std::min((size_t)std::pow(m_nMaxGlobalWords*fRegionSizeScaleFactor, 2) + 1, m_nMaxGlobalWords); + } + if (m_nImgChannels == 1) { + m_nCurrLocalWords = std::max(m_nCurrLocalWords / 2, (size_t)1); + m_nCurrGlobalWords = std::max(m_nCurrGlobalWords / 2, (size_t)1); + } + m_nDownSampledROIPxCount = (size_t)cv::countNonZero(m_oDownSampledROI_MotionAnalysis); + m_nLocalWordWeightOffset = DEFAULT_LWORD_WEIGHT_OFFSET; + m_oIllumUpdtRegionMask.create(m_oImgSize, CV_8UC1); + m_oIllumUpdtRegionMask = cv::Scalar_<uchar>(0); + m_oUpdateRateFrame.create(m_oImgSize, CV_32FC1); + m_oUpdateRateFrame = cv::Scalar(FEEDBACK_T_LOWER); + m_oDistThresholdFrame.create(m_oImgSize, CV_32FC1); + m_oDistThresholdFrame = cv::Scalar(2.0f); + m_oDistThresholdVariationFrame.create(m_oImgSize, CV_32FC1); + m_oDistThresholdVariationFrame = cv::Scalar(FEEDBACK_V_INCR * 10); + m_oMeanMinDistFrame_LT.create(m_oImgSize, CV_32FC1); + m_oMeanMinDistFrame_LT = cv::Scalar(0.0f); + m_oMeanMinDistFrame_ST.create(m_oImgSize, CV_32FC1); + m_oMeanMinDistFrame_ST = cv::Scalar(0.0f); + m_oMeanDownSampledLastDistFrame_LT.create(m_oDownSampledFrameSize_MotionAnalysis, CV_32FC((int)m_nImgChannels)); + m_oMeanDownSampledLastDistFrame_LT = cv::Scalar(0.0f); + m_oMeanDownSampledLastDistFrame_ST.create(m_oDownSampledFrameSize_MotionAnalysis, CV_32FC((int)m_nImgChannels)); + m_oMeanDownSampledLastDistFrame_ST = cv::Scalar(0.0f); + m_oMeanRawSegmResFrame_LT.create(m_oImgSize, CV_32FC1); + m_oMeanRawSegmResFrame_LT = cv::Scalar(0.0f); + m_oMeanRawSegmResFrame_ST.create(m_oImgSize, CV_32FC1); + m_oMeanRawSegmResFrame_ST = cv::Scalar(0.0f); + m_oMeanFinalSegmResFrame_LT.create(m_oImgSize, CV_32FC1); + m_oMeanFinalSegmResFrame_LT = cv::Scalar(0.0f); + m_oMeanFinalSegmResFrame_ST.create(m_oImgSize, CV_32FC1); + m_oMeanFinalSegmResFrame_ST = cv::Scalar(0.0f); + m_oUnstableRegionMask.create(m_oImgSize, CV_8UC1); + m_oUnstableRegionMask = cv::Scalar_<uchar>(0); + m_oBlinksFrame.create(m_oImgSize, CV_8UC1); + m_oBlinksFrame = cv::Scalar_<uchar>(0); + m_oDownSampledFrame_MotionAnalysis.create(m_oDownSampledFrameSize_MotionAnalysis, CV_8UC((int)m_nImgChannels)); + m_oDownSampledFrame_MotionAnalysis = cv::Scalar_<uchar>::all(0); + m_oLastColorFrame.create(m_oImgSize, CV_8UC((int)m_nImgChannels)); + m_oLastColorFrame = cv::Scalar_<uchar>::all(0); + m_oLastDescFrame.create(m_oImgSize, CV_16UC((int)m_nImgChannels)); + m_oLastDescFrame = cv::Scalar_<ushort>::all(0); + m_oLastRawFGMask.create(m_oImgSize, CV_8UC1); + m_oLastRawFGMask = cv::Scalar_<uchar>(0); + m_oLastFGMask.create(m_oImgSize, CV_8UC1); + m_oLastFGMask = cv::Scalar_<uchar>(0); + m_oLastFGMask_dilated.create(m_oImgSize, CV_8UC1); + m_oLastFGMask_dilated = cv::Scalar_<uchar>(0); + m_oLastFGMask_dilated_inverted.create(m_oImgSize, CV_8UC1); + m_oLastFGMask_dilated_inverted = cv::Scalar_<uchar>(0); + m_oFGMask_FloodedHoles.create(m_oImgSize, CV_8UC1); + m_oFGMask_FloodedHoles = cv::Scalar_<uchar>(0); + m_oFGMask_PreFlood.create(m_oImgSize, CV_8UC1); + m_oFGMask_PreFlood = cv::Scalar_<uchar>(0); + m_oCurrRawFGBlinkMask.create(m_oImgSize, CV_8UC1); + m_oCurrRawFGBlinkMask = cv::Scalar_<uchar>(0); + m_oLastRawFGBlinkMask.create(m_oImgSize, CV_8UC1); + m_oLastRawFGBlinkMask = cv::Scalar_<uchar>(0); + m_oTempGlobalWordWeightDiffFactor.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); + m_oTempGlobalWordWeightDiffFactor = cv::Scalar(-0.1f); + m_oMorphExStructElement = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3)); + m_aPxIdxLUT = new size_t[m_nTotRelevantPxCount]; + memset(m_aPxIdxLUT, 0, sizeof(size_t)*m_nTotRelevantPxCount); + m_aPxInfoLUT_PAWCS = new PxInfo_PAWCS[m_nTotPxCount]; + memset(m_aPxInfoLUT_PAWCS, 0, sizeof(PxInfo_PAWCS)*m_nTotPxCount); + m_aPxInfoLUT = m_aPxInfoLUT_PAWCS; + m_apLocalWordDict = new LocalWordBase*[m_nTotRelevantPxCount*m_nCurrLocalWords]; + memset(m_apLocalWordDict, 0, sizeof(LocalWordBase*)*m_nTotRelevantPxCount*m_nCurrLocalWords); + m_apGlobalWordDict = new GlobalWordBase*[m_nCurrGlobalWords]; + memset(m_apGlobalWordDict, 0, sizeof(GlobalWordBase*)*m_nCurrGlobalWords); + if (m_nImgChannels == 1) { + CV_DbgAssert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width && m_oLastColorFrame.step.p[1] == 1); + CV_DbgAssert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); + m_aLocalWordList_1ch = new LocalWord_1ch[m_nTotRelevantPxCount*m_nCurrLocalWords]; + memset(m_aLocalWordList_1ch, 0, sizeof(LocalWord_1ch)*m_nTotRelevantPxCount*m_nCurrLocalWords); + m_pLocalWordListIter_1ch = m_aLocalWordList_1ch; + m_aGlobalWordList_1ch = new GlobalWord_1ch[m_nCurrGlobalWords]; + m_pGlobalWordListIter_1ch = m_aGlobalWordList_1ch; + for (size_t t = 0; t <= UCHAR_MAX; ++t) + m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>((m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold) / 3); + for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { + if (m_oROI.data[nPxIter]) { + m_aPxIdxLUT[nModelIter] = nPxIter; + m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; + m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; + m_aPxInfoLUT_PAWCS[nPxIter].nModelIdx = nModelIter; + m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx = (size_t)((m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)*m_oDownSampledFrameSize_GlobalWordLookup.width + (m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)) * 4; + m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT = new GlobalWordBase*[m_nCurrGlobalWords]; + for (size_t nGlobalWordIdxIter = 0; nGlobalWordIdxIter < m_nCurrGlobalWords; ++nGlobalWordIdxIter) + m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordIdxIter] = &(m_aGlobalWordList_1ch[nGlobalWordIdxIter]); + m_oLastColorFrame.data[nPxIter] = oInitImg.data[nPxIter]; + const size_t nDescIter = nPxIter * 2; + LBSP_::computeGrayscaleDescriptor(oInitImg, oInitImg.data[nPxIter], m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxIter]], *((ushort*)(m_oLastDescFrame.data + nDescIter))); + ++nModelIter; } } } - CV_Assert(m_apLocalWordDict[nLocalDictIdx]); - for (size_t nLocalWordIdx = 1; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - // == refresh: local random resampling - LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - if (!pCurrLocalWord) { - const size_t nRandLocalWordIdx = (rand() % nLocalWordIdx); - const LocalWord_1ch* pRefLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nRandLocalWordIdx]; - const int nRandColorOffset = (rand() % (nCurrColorDistThreshold + 1)) - (int)nCurrColorDistThreshold / 2; - pCurrLocalWord = m_pLocalWordListIter_1ch++; - pCurrLocalWord->oFeature.anColor[0] = cv::saturate_cast<uchar>((int)pRefLocalWord->oFeature.anColor[0] + nRandColorOffset); - pCurrLocalWord->oFeature.anDesc[0] = pRefLocalWord->oFeature.anDesc[0]; - pCurrLocalWord->nOccurrences = std::max((size_t)(pRefLocalWord->nOccurrences*((float)(m_nCurrLocalWords - nLocalWordIdx) / m_nCurrLocalWords)), (size_t)1); - pCurrLocalWord->nFirstOcc = m_nFrameIndex; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; + else { //m_nImgChannels==3 + CV_DbgAssert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width * 3 && m_oLastColorFrame.step.p[1] == 3); + CV_DbgAssert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); + m_aLocalWordList_3ch = new LocalWord_3ch[m_nTotRelevantPxCount*m_nCurrLocalWords]; + memset(m_aLocalWordList_3ch, 0, sizeof(LocalWord_3ch)*m_nTotRelevantPxCount*m_nCurrLocalWords); + m_pLocalWordListIter_3ch = m_aLocalWordList_3ch; + m_aGlobalWordList_3ch = new GlobalWord_3ch[m_nCurrGlobalWords]; + m_pGlobalWordListIter_3ch = m_aGlobalWordList_3ch; + for (size_t t = 0; t <= UCHAR_MAX; ++t) + m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold); + for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { + if (m_oROI.data[nPxIter]) { + m_aPxIdxLUT[nModelIter] = nPxIter; + m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; + m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; + m_aPxInfoLUT_PAWCS[nPxIter].nModelIdx = nModelIter; + m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx = (size_t)((m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)*m_oDownSampledFrameSize_GlobalWordLookup.width + (m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X / GWORD_LOOKUP_MAPS_DOWNSAMPLE_RATIO)) * 4; + m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT = new GlobalWordBase*[m_nCurrGlobalWords]; + for (size_t nGlobalWordIdxIter = 0; nGlobalWordIdxIter < m_nCurrGlobalWords; ++nGlobalWordIdxIter) + m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordIdxIter] = &(m_aGlobalWordList_3ch[nGlobalWordIdxIter]); + const size_t nPxRGBIter = nPxIter * 3; + const size_t nDescRGBIter = nPxRGBIter * 2; + for (size_t c = 0; c < 3; ++c) { + m_oLastColorFrame.data[nPxRGBIter + c] = oInitImg.data[nPxRGBIter + c]; + LBSP_::computeSingleRGBDescriptor(oInitImg, oInitImg.data[nPxRGBIter + c], m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxRGBIter + c]], ((ushort*)(m_oLastDescFrame.data + nDescRGBIter))[c]); + } + ++nModelIter; + } } } + m_bInitialized = true; + refreshModel(1, 0); } - } - CV_Assert(m_aLocalWordList_1ch == (m_pLocalWordListIter_1ch - m_nTotRelevantPxCount*m_nCurrLocalWords)); - cv::Mat oGlobalDictPresenceLookupMap(m_oImgSize, CV_8UC1, cv::Scalar_<uchar>(0)); - size_t nPxIterIncr = std::max(m_nTotPxCount / m_nCurrGlobalWords, (size_t)1); - for (size_t nSamplingPasses = 0; nSamplingPasses < GWORD_DEFAULT_NB_INIT_SAMPL_PASSES; ++nSamplingPasses) { - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - // == refresh: global resampling - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - if ((nPxIter%nPxIterIncr) == 0) { // <=(m_nCurrGlobalWords) gwords from (m_nCurrGlobalWords) equally spaced pixels - if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { - const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; - const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; - const size_t nFloatIter = nPxIter * 4; - uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; - const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); - const size_t nCurrColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) / 2; - const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET); - CV_Assert(m_apLocalWordDict[nLocalDictIdx]); - const LocalWord_1ch* pRefBestLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx]; - const float fRefBestLocalWordWeight = GetLocalWordWeight(pRefBestLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - const uchar nRefBestLocalWordDescBITS = (uchar)popcount(pRefBestLocalWord->oFeature.anDesc[0]); - bool bFoundUninitd = false; - size_t nGlobalWordIdx; - for (nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { - GlobalWord_1ch* pCurrGlobalWord = (GlobalWord_1ch*)m_apGlobalWordDict[nGlobalWordIdx]; - if (pCurrGlobalWord - && L1dist(pCurrGlobalWord->oFeature.anColor[0], pRefBestLocalWord->oFeature.anColor[0]) <= nCurrColorDistThreshold - && L1dist(nRefBestLocalWordDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR) - break; - else if (!pCurrGlobalWord) - bFoundUninitd = true; + + void BackgroundSubtractorPAWCS::refreshModel(size_t nBaseOccCount, float fOccDecrFrac, bool bForceFGUpdate) { + // == refresh + CV_Assert(m_bInitialized); + CV_Assert(fOccDecrFrac >= 0.0f && fOccDecrFrac <= 1.0f); + if (m_nImgChannels == 1) { + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { + const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; + const size_t nFloatIter = nPxIter * 4; + uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; + const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + const size_t nCurrColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) / 2; + const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET); + // == refresh: local decr + if (fOccDecrFrac > 0.0f) { + for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + if (pCurrLocalWord) + pCurrLocalWord->nOccurrences -= (size_t)(fOccDecrFrac*pCurrLocalWord->nOccurrences); + } + } + const size_t nCurrWordOccIncr = DEFAULT_LWORD_OCC_INCR; + const size_t nTotLocalSamplingIterCount = (s_nSamplesInitPatternWidth*s_nSamplesInitPatternHeight) * 2; + for (size_t nLocalSamplingIter = 0; nLocalSamplingIter < nTotLocalSamplingIterCount; ++nLocalSamplingIter) { + // == refresh: local resampling + int nSampleImgCoord_Y, nSampleImgCoord_X; + getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); + const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nSamplePxIdx]) { + const uchar nSampleColor = m_oLastColorFrame.data[nSamplePxIdx]; + const size_t nSampleDescIdx = nSamplePxIdx * 2; + const ushort nSampleIntraDesc = *((ushort*)(m_oLastDescFrame.data + nSampleDescIdx)); + bool bFoundUninitd = false; + size_t nLocalWordIdx; + for (nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + if (pCurrLocalWord + && L1dist(nSampleColor, pCurrLocalWord->oFeature.anColor[0]) <= nCurrColorDistThreshold + && hdist(nSampleIntraDesc, pCurrLocalWord->oFeature.anDesc[0]) <= nCurrDescDistThreshold) { + pCurrLocalWord->nOccurrences += nCurrWordOccIncr; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + break; + } + else if (!pCurrLocalWord) + bFoundUninitd = true; + } + if (nLocalWordIdx == m_nCurrLocalWords) { + nLocalWordIdx = m_nCurrLocalWords - 1; + LocalWord_1ch* pCurrLocalWord = bFoundUninitd ? m_pLocalWordListIter_1ch++ : (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + pCurrLocalWord->oFeature.anColor[0] = nSampleColor; + pCurrLocalWord->oFeature.anDesc[0] = nSampleIntraDesc; + pCurrLocalWord->nOccurrences = nBaseOccCount; + pCurrLocalWord->nFirstOcc = m_nFrameIndex; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; + } + while (nLocalWordIdx > 0 && (!m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1] || GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset) > GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1], m_nFrameIndex, m_nLocalWordWeightOffset))) { + std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); + --nLocalWordIdx; + } + } + } + CV_Assert(m_apLocalWordDict[nLocalDictIdx]); + for (size_t nLocalWordIdx = 1; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + // == refresh: local random resampling + LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + if (!pCurrLocalWord) { + const size_t nRandLocalWordIdx = (rand() % nLocalWordIdx); + const LocalWord_1ch* pRefLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nRandLocalWordIdx]; + const int nRandColorOffset = (rand() % (nCurrColorDistThreshold + 1)) - (int)nCurrColorDistThreshold / 2; + pCurrLocalWord = m_pLocalWordListIter_1ch++; + pCurrLocalWord->oFeature.anColor[0] = cv::saturate_cast<uchar>((int)pRefLocalWord->oFeature.anColor[0] + nRandColorOffset); + pCurrLocalWord->oFeature.anDesc[0] = pRefLocalWord->oFeature.anDesc[0]; + pCurrLocalWord->nOccurrences = std::max((size_t)(pRefLocalWord->nOccurrences*((float)(m_nCurrLocalWords - nLocalWordIdx) / m_nCurrLocalWords)), (size_t)1); + pCurrLocalWord->nFirstOcc = m_nFrameIndex; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; + } + } } - if (nGlobalWordIdx == m_nCurrGlobalWords) { - nGlobalWordIdx = m_nCurrGlobalWords - 1; - GlobalWord_1ch* pCurrGlobalWord = bFoundUninitd ? m_pGlobalWordListIter_1ch++ : (GlobalWord_1ch*)m_apGlobalWordDict[nGlobalWordIdx]; - pCurrGlobalWord->oFeature.anColor[0] = pRefBestLocalWord->oFeature.anColor[0]; - pCurrGlobalWord->oFeature.anDesc[0] = pRefBestLocalWord->oFeature.anDesc[0]; - pCurrGlobalWord->nDescBITS = nRefBestLocalWordDescBITS; + } + CV_Assert(m_aLocalWordList_1ch == (m_pLocalWordListIter_1ch - m_nTotRelevantPxCount*m_nCurrLocalWords)); + cv::Mat oGlobalDictPresenceLookupMap(m_oImgSize, CV_8UC1, cv::Scalar_<uchar>(0)); + size_t nPxIterIncr = std::max(m_nTotPxCount / m_nCurrGlobalWords, (size_t)1); + for (size_t nSamplingPasses = 0; nSamplingPasses < GWORD_DEFAULT_NB_INIT_SAMPL_PASSES; ++nSamplingPasses) { + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + // == refresh: global resampling + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + if ((nPxIter%nPxIterIncr) == 0) { // <=(m_nCurrGlobalWords) gwords from (m_nCurrGlobalWords) equally spaced pixels + if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { + const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; + const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; + const size_t nFloatIter = nPxIter * 4; + uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; + const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + const size_t nCurrColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) / 2; + const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET); + CV_Assert(m_apLocalWordDict[nLocalDictIdx]); + const LocalWord_1ch* pRefBestLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx]; + const float fRefBestLocalWordWeight = GetLocalWordWeight(pRefBestLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + const uchar nRefBestLocalWordDescBITS = (uchar)popcount(pRefBestLocalWord->oFeature.anDesc[0]); + bool bFoundUninitd = false; + size_t nGlobalWordIdx; + for (nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { + GlobalWord_1ch* pCurrGlobalWord = (GlobalWord_1ch*)m_apGlobalWordDict[nGlobalWordIdx]; + if (pCurrGlobalWord + && L1dist(pCurrGlobalWord->oFeature.anColor[0], pRefBestLocalWord->oFeature.anColor[0]) <= nCurrColorDistThreshold + && L1dist(nRefBestLocalWordDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR) + break; + else if (!pCurrGlobalWord) + bFoundUninitd = true; + } + if (nGlobalWordIdx == m_nCurrGlobalWords) { + nGlobalWordIdx = m_nCurrGlobalWords - 1; + GlobalWord_1ch* pCurrGlobalWord = bFoundUninitd ? m_pGlobalWordListIter_1ch++ : (GlobalWord_1ch*)m_apGlobalWordDict[nGlobalWordIdx]; + pCurrGlobalWord->oFeature.anColor[0] = pRefBestLocalWord->oFeature.anColor[0]; + pCurrGlobalWord->oFeature.anDesc[0] = pRefBestLocalWord->oFeature.anDesc[0]; + pCurrGlobalWord->nDescBITS = nRefBestLocalWordDescBITS; + pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); + pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); + pCurrGlobalWord->fLatestWeight = 0.0f; + m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; + } + float& fCurrGlobalWordLocalWeight = *(float*)(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fCurrGlobalWordLocalWeight < fRefBestLocalWordWeight) { + m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight += fRefBestLocalWordWeight; + fCurrGlobalWordLocalWeight += fRefBestLocalWordWeight; + } + oGlobalDictPresenceLookupMap.data[nPxIter] = UCHAR_MAX; + while (nGlobalWordIdx > 0 && (!m_apGlobalWordDict[nGlobalWordIdx - 1] || m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > m_apGlobalWordDict[nGlobalWordIdx - 1]->fLatestWeight)) { + std::swap(m_apGlobalWordDict[nGlobalWordIdx], m_apGlobalWordDict[nGlobalWordIdx - 1]); + --nGlobalWordIdx; + } + } + } + } + nPxIterIncr = std::max(nPxIterIncr / 3, (size_t)1); + } + for (size_t nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { + GlobalWord_1ch* pCurrGlobalWord = (GlobalWord_1ch*)m_apGlobalWordDict[nGlobalWordIdx]; + if (!pCurrGlobalWord) { + pCurrGlobalWord = m_pGlobalWordListIter_1ch++; + pCurrGlobalWord->oFeature.anColor[0] = 0; + pCurrGlobalWord->oFeature.anDesc[0] = 0; + pCurrGlobalWord->nDescBITS = 0; pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); pCurrGlobalWord->fLatestWeight = 0.0f; m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; } - float& fCurrGlobalWordLocalWeight = *(float*)(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fCurrGlobalWordLocalWeight < fRefBestLocalWordWeight) { - m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight += fRefBestLocalWordWeight; - fCurrGlobalWordLocalWeight += fRefBestLocalWordWeight; - } - oGlobalDictPresenceLookupMap.data[nPxIter] = UCHAR_MAX; - while (nGlobalWordIdx > 0 && (!m_apGlobalWordDict[nGlobalWordIdx - 1] || m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > m_apGlobalWordDict[nGlobalWordIdx - 1]->fLatestWeight)) { - std::swap(m_apGlobalWordDict[nGlobalWordIdx], m_apGlobalWordDict[nGlobalWordIdx - 1]); - --nGlobalWordIdx; - } } + CV_Assert((size_t)(m_pGlobalWordListIter_1ch - m_aGlobalWordList_1ch) == m_nCurrGlobalWords && m_aGlobalWordList_1ch == (m_pGlobalWordListIter_1ch - m_nCurrGlobalWords)); } - } - nPxIterIncr = std::max(nPxIterIncr / 3, (size_t)1); - } - for (size_t nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { - GlobalWord_1ch* pCurrGlobalWord = (GlobalWord_1ch*)m_apGlobalWordDict[nGlobalWordIdx]; - if (!pCurrGlobalWord) { - pCurrGlobalWord = m_pGlobalWordListIter_1ch++; - pCurrGlobalWord->oFeature.anColor[0] = 0; - pCurrGlobalWord->oFeature.anDesc[0] = 0; - pCurrGlobalWord->nDescBITS = 0; - pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); - pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); - pCurrGlobalWord->fLatestWeight = 0.0f; - m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; - } - } - CV_Assert((size_t)(m_pGlobalWordListIter_1ch - m_aGlobalWordList_1ch) == m_nCurrGlobalWords && m_aGlobalWordList_1ch == (m_pGlobalWordListIter_1ch - m_nCurrGlobalWords)); - } - else { //m_nImgChannels==3 - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { - const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; - const size_t nFloatIter = nPxIter * 4; - uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; - const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); - const size_t nCurrTotColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) * 3; - const size_t nCurrTotDescDistThreshold = (((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET)) * 3; - // == refresh: local decr - if (fOccDecrFrac > 0.0f) { - for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - if (pCurrLocalWord) - pCurrLocalWord->nOccurrences -= (size_t)(fOccDecrFrac*pCurrLocalWord->nOccurrences); + else { //m_nImgChannels==3 + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { + const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; + const size_t nFloatIter = nPxIter * 4; + uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; + const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + const size_t nCurrTotColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) * 3; + const size_t nCurrTotDescDistThreshold = (((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET)) * 3; + // == refresh: local decr + if (fOccDecrFrac > 0.0f) { + for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + if (pCurrLocalWord) + pCurrLocalWord->nOccurrences -= (size_t)(fOccDecrFrac*pCurrLocalWord->nOccurrences); + } + } + const size_t nCurrWordOccIncr = DEFAULT_LWORD_OCC_INCR; + const size_t nTotLocalSamplingIterCount = (s_nSamplesInitPatternWidth*s_nSamplesInitPatternHeight) * 2; + for (size_t nLocalSamplingIter = 0; nLocalSamplingIter < nTotLocalSamplingIterCount; ++nLocalSamplingIter) { + // == refresh: local resampling + int nSampleImgCoord_Y, nSampleImgCoord_X; + getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); + const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nSamplePxIdx]) { + const size_t nSamplePxRGBIdx = nSamplePxIdx * 3; + const size_t nSampleDescRGBIdx = nSamplePxRGBIdx * 2; + const uchar* const anSampleColor = m_oLastColorFrame.data + nSamplePxRGBIdx; + const ushort* const anSampleIntraDesc = ((ushort*)(m_oLastDescFrame.data + nSampleDescRGBIdx)); + bool bFoundUninitd = false; + size_t nLocalWordIdx; + for (nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + if (pCurrLocalWord + && cmixdist<3>(anSampleColor, pCurrLocalWord->oFeature.anColor) <= nCurrTotColorDistThreshold + && hdist<3>(anSampleIntraDesc, pCurrLocalWord->oFeature.anDesc) <= nCurrTotDescDistThreshold) { + pCurrLocalWord->nOccurrences += nCurrWordOccIncr; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + break; + } + else if (!pCurrLocalWord) + bFoundUninitd = true; + } + if (nLocalWordIdx == m_nCurrLocalWords) { + nLocalWordIdx = m_nCurrLocalWords - 1; + LocalWord_3ch* pCurrLocalWord = bFoundUninitd ? m_pLocalWordListIter_3ch++ : (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + for (size_t c = 0; c < 3; ++c) { + pCurrLocalWord->oFeature.anColor[c] = anSampleColor[c]; + pCurrLocalWord->oFeature.anDesc[c] = anSampleIntraDesc[c]; + } + pCurrLocalWord->nOccurrences = nBaseOccCount; + pCurrLocalWord->nFirstOcc = m_nFrameIndex; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; + } + while (nLocalWordIdx > 0 && (!m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1] || GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset) > GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1], m_nFrameIndex, m_nLocalWordWeightOffset))) { + std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); + --nLocalWordIdx; + } + } + } + CV_Assert(m_apLocalWordDict[nLocalDictIdx]); + for (size_t nLocalWordIdx = 1; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + // == refresh: local random resampling + LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + if (!pCurrLocalWord) { + const size_t nRandLocalWordIdx = (rand() % nLocalWordIdx); + const LocalWord_3ch* pRefLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nRandLocalWordIdx]; + const int nRandColorOffset = (rand() % (nCurrTotColorDistThreshold / 3 + 1)) - (int)(nCurrTotColorDistThreshold / 6); + pCurrLocalWord = m_pLocalWordListIter_3ch++; + for (size_t c = 0; c < 3; ++c) { + pCurrLocalWord->oFeature.anColor[c] = cv::saturate_cast<uchar>((int)pRefLocalWord->oFeature.anColor[c] + nRandColorOffset); + pCurrLocalWord->oFeature.anDesc[c] = pRefLocalWord->oFeature.anDesc[c]; + } + pCurrLocalWord->nOccurrences = std::max((size_t)(pRefLocalWord->nOccurrences*((float)(m_nCurrLocalWords - nLocalWordIdx) / m_nCurrLocalWords)), (size_t)1); + pCurrLocalWord->nFirstOcc = m_nFrameIndex; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; + } + } + } } - } - const size_t nCurrWordOccIncr = DEFAULT_LWORD_OCC_INCR; - const size_t nTotLocalSamplingIterCount = (s_nSamplesInitPatternWidth*s_nSamplesInitPatternHeight) * 2; - for (size_t nLocalSamplingIter = 0; nLocalSamplingIter < nTotLocalSamplingIterCount; ++nLocalSamplingIter) { - // == refresh: local resampling - int nSampleImgCoord_Y, nSampleImgCoord_X; - getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X, m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); - const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nSamplePxIdx]) { - const size_t nSamplePxRGBIdx = nSamplePxIdx * 3; - const size_t nSampleDescRGBIdx = nSamplePxRGBIdx * 2; - const uchar* const anSampleColor = m_oLastColorFrame.data + nSamplePxRGBIdx; - const ushort* const anSampleIntraDesc = ((ushort*)(m_oLastDescFrame.data + nSampleDescRGBIdx)); - bool bFoundUninitd = false; - size_t nLocalWordIdx; - for (nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - if (pCurrLocalWord - && cmixdist<3>(anSampleColor, pCurrLocalWord->oFeature.anColor) <= nCurrTotColorDistThreshold - && hdist<3>(anSampleIntraDesc, pCurrLocalWord->oFeature.anDesc) <= nCurrTotDescDistThreshold) { - pCurrLocalWord->nOccurrences += nCurrWordOccIncr; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - break; + CV_Assert(m_aLocalWordList_3ch == (m_pLocalWordListIter_3ch - m_nTotRelevantPxCount*m_nCurrLocalWords)); + cv::Mat oGlobalDictPresenceLookupMap(m_oImgSize, CV_8UC1, cv::Scalar_<uchar>(0)); + size_t nPxIterIncr = std::max(m_nTotPxCount / m_nCurrGlobalWords, (size_t)1); + for (size_t nSamplingPasses = 0; nSamplingPasses < GWORD_DEFAULT_NB_INIT_SAMPL_PASSES; ++nSamplingPasses) { + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + // == refresh: global resampling + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + if ((nPxIter%nPxIterIncr) == 0) { // <=(m_nCurrGlobalWords) gwords from (m_nCurrGlobalWords) equally spaced pixels + if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { + const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; + const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; + const size_t nFloatIter = nPxIter * 4; + uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; + const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + const size_t nCurrTotColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) * 3; + const size_t nCurrTotDescDistThreshold = (((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET)) * 3; + CV_Assert(m_apLocalWordDict[nLocalDictIdx]); + const LocalWord_3ch* pRefBestLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx]; + const float fRefBestLocalWordWeight = GetLocalWordWeight(pRefBestLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + const uchar nRefBestLocalWordDescBITS = (uchar)popcount<3>(pRefBestLocalWord->oFeature.anDesc); + bool bFoundUninitd = false; + size_t nGlobalWordIdx; + for (nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { + GlobalWord_3ch* pCurrGlobalWord = (GlobalWord_3ch*)m_apGlobalWordDict[nGlobalWordIdx]; + if (pCurrGlobalWord + && L1dist(nRefBestLocalWordDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrTotDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR + && cmixdist<3>(pRefBestLocalWord->oFeature.anColor, pCurrGlobalWord->oFeature.anColor) <= nCurrTotColorDistThreshold) + break; + else if (!pCurrGlobalWord) + bFoundUninitd = true; + } + if (nGlobalWordIdx == m_nCurrGlobalWords) { + nGlobalWordIdx = m_nCurrGlobalWords - 1; + GlobalWord_3ch* pCurrGlobalWord = bFoundUninitd ? m_pGlobalWordListIter_3ch++ : (GlobalWord_3ch*)m_apGlobalWordDict[nGlobalWordIdx]; + for (size_t c = 0; c < 3; ++c) { + pCurrGlobalWord->oFeature.anColor[c] = pRefBestLocalWord->oFeature.anColor[c]; + pCurrGlobalWord->oFeature.anDesc[c] = pRefBestLocalWord->oFeature.anDesc[c]; + } + pCurrGlobalWord->nDescBITS = nRefBestLocalWordDescBITS; + pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); + pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); + pCurrGlobalWord->fLatestWeight = 0.0f; + m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; + } + float& fCurrGlobalWordLocalWeight = *(float*)(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fCurrGlobalWordLocalWeight < fRefBestLocalWordWeight) { + m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight += fRefBestLocalWordWeight; + fCurrGlobalWordLocalWeight += fRefBestLocalWordWeight; + } + oGlobalDictPresenceLookupMap.data[nPxIter] = UCHAR_MAX; + while (nGlobalWordIdx > 0 && (!m_apGlobalWordDict[nGlobalWordIdx - 1] || m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > m_apGlobalWordDict[nGlobalWordIdx - 1]->fLatestWeight)) { + std::swap(m_apGlobalWordDict[nGlobalWordIdx], m_apGlobalWordDict[nGlobalWordIdx - 1]); + --nGlobalWordIdx; + } + } } - else if (!pCurrLocalWord) - bFoundUninitd = true; } - if (nLocalWordIdx == m_nCurrLocalWords) { - nLocalWordIdx = m_nCurrLocalWords - 1; - LocalWord_3ch* pCurrLocalWord = bFoundUninitd ? m_pLocalWordListIter_3ch++ : (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + nPxIterIncr = std::max(nPxIterIncr / 3, (size_t)1); + } + for (size_t nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { + GlobalWord_3ch* pCurrGlobalWord = (GlobalWord_3ch*)m_apGlobalWordDict[nGlobalWordIdx]; + if (!pCurrGlobalWord) { + pCurrGlobalWord = m_pGlobalWordListIter_3ch++; for (size_t c = 0; c < 3; ++c) { - pCurrLocalWord->oFeature.anColor[c] = anSampleColor[c]; - pCurrLocalWord->oFeature.anDesc[c] = anSampleIntraDesc[c]; + pCurrGlobalWord->oFeature.anColor[c] = 0; + pCurrGlobalWord->oFeature.anDesc[c] = 0; } - pCurrLocalWord->nOccurrences = nBaseOccCount; - pCurrLocalWord->nFirstOcc = m_nFrameIndex; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; - } - while (nLocalWordIdx > 0 && (!m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1] || GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset) > GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1], m_nFrameIndex, m_nLocalWordWeightOffset))) { - std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); - --nLocalWordIdx; + pCurrGlobalWord->nDescBITS = 0; + pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); + pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); + pCurrGlobalWord->fLatestWeight = 0.0f; + m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; } } + CV_Assert((size_t)(m_pGlobalWordListIter_3ch - m_aGlobalWordList_3ch) == m_nCurrGlobalWords && m_aGlobalWordList_3ch == (m_pGlobalWordListIter_3ch - m_nCurrGlobalWords)); } - CV_Assert(m_apLocalWordDict[nLocalDictIdx]); - for (size_t nLocalWordIdx = 1; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - // == refresh: local random resampling - LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - if (!pCurrLocalWord) { - const size_t nRandLocalWordIdx = (rand() % nLocalWordIdx); - const LocalWord_3ch* pRefLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nRandLocalWordIdx]; - const int nRandColorOffset = (rand() % (nCurrTotColorDistThreshold / 3 + 1)) - (int)(nCurrTotColorDistThreshold / 6); - pCurrLocalWord = m_pLocalWordListIter_3ch++; - for (size_t c = 0; c < 3; ++c) { - pCurrLocalWord->oFeature.anColor[c] = cv::saturate_cast<uchar>((int)pRefLocalWord->oFeature.anColor[c] + nRandColorOffset); - pCurrLocalWord->oFeature.anDesc[c] = pRefLocalWord->oFeature.anDesc[c]; - } - pCurrLocalWord->nOccurrences = std::max((size_t)(pRefLocalWord->nOccurrences*((float)(m_nCurrLocalWords - nLocalWordIdx) / m_nCurrLocalWords)), (size_t)1); - pCurrLocalWord->nFirstOcc = m_nFrameIndex; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx] = pCurrLocalWord; + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + // == refresh: per-px global word sort + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; + float fLastGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[0]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + for (size_t nGlobalWordLUTIdx = 1; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { + const float fCurrGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fCurrGlobalWordLocalWeight > fLastGlobalWordLocalWeight) + std::swap(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx], m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx - 1]); + else + fLastGlobalWordLocalWeight = fCurrGlobalWordLocalWeight; } } } - } - CV_Assert(m_aLocalWordList_3ch == (m_pLocalWordListIter_3ch - m_nTotRelevantPxCount*m_nCurrLocalWords)); - cv::Mat oGlobalDictPresenceLookupMap(m_oImgSize, CV_8UC1, cv::Scalar_<uchar>(0)); - size_t nPxIterIncr = std::max(m_nTotPxCount / m_nCurrGlobalWords, (size_t)1); - for (size_t nSamplingPasses = 0; nSamplingPasses < GWORD_DEFAULT_NB_INIT_SAMPL_PASSES; ++nSamplingPasses) { - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - // == refresh: global resampling - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - if ((nPxIter%nPxIterIncr) == 0) { // <=(m_nCurrGlobalWords) gwords from (m_nCurrGlobalWords) equally spaced pixels - if (bForceFGUpdate || !m_oLastFGMask_dilated.data[nPxIter]) { + + void BackgroundSubtractorPAWCS::apply(cv::InputArray _image, cv::OutputArray _fgmask, double learningRateOverride) { + // == process + CV_Assert(m_bInitialized); + cv::Mat oInputImg = _image.getMat(); + CV_Assert(oInputImg.type() == m_nImgType && oInputImg.size() == m_oImgSize); + CV_Assert(oInputImg.isContinuous()); + _fgmask.create(m_oImgSize, CV_8UC1); + cv::Mat oCurrFGMask = _fgmask.getMat(); + memset(oCurrFGMask.data, 0, oCurrFGMask.cols*oCurrFGMask.rows); + const bool bBootstrapping = ++m_nFrameIndex <= DEFAULT_BOOTSTRAP_WIN_SIZE; + const size_t nCurrSamplesForMovingAvg_LT = bBootstrapping ? m_nSamplesForMovingAvgs / 2 : m_nSamplesForMovingAvgs; + const size_t nCurrSamplesForMovingAvg_ST = nCurrSamplesForMovingAvg_LT / 4; + const float fRollAvgFactor_LT = 1.0f / std::min(m_nFrameIndex, nCurrSamplesForMovingAvg_LT); + const float fRollAvgFactor_ST = 1.0f / std::min(m_nFrameIndex, nCurrSamplesForMovingAvg_ST); + const size_t nCurrGlobalWordUpdateRate = bBootstrapping ? DEFAULT_RESAMPLING_RATE / 2 : DEFAULT_RESAMPLING_RATE; + size_t nFlatRegionCount = 0; + if (m_nImgChannels == 1) { + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const size_t nDescIter = nPxIter * 2; + const size_t nFloatIter = nPxIter * 4; const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; + const uchar nCurrColor = oInputImg.data[nPxIter]; + uchar& nLastColor = m_oLastColorFrame.data[nPxIter]; + ushort& nLastIntraDesc = *((ushort*)(m_oLastDescFrame.data + nDescIter)); + size_t nMinColorDist = s_nColorMaxDataRange_1ch; + size_t nMinDescDist = s_nDescMaxDataRange_1ch; + float& fCurrMeanRawSegmRes_LT = *(float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter); + float& fCurrMeanRawSegmRes_ST = *(float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter); + float& fCurrMeanFinalSegmRes_LT = *(float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter); + float& fCurrMeanFinalSegmRes_ST = *(float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter); + float& fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + float& fCurrDistThresholdVariationFactor = *(float*)(m_oDistThresholdVariationFrame.data + nFloatIter); + float& fCurrLearningRate = *(float*)(m_oUpdateRateFrame.data + nFloatIter); + float& fCurrMeanMinDist_LT = *(float*)(m_oMeanMinDistFrame_LT.data + nFloatIter); + float& fCurrMeanMinDist_ST = *(float*)(m_oMeanMinDistFrame_ST.data + nFloatIter); + const float fBestLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx], m_nFrameIndex, m_nLocalWordWeightOffset); + const float fLocalWordsWeightSumThreshold = fBestLocalWordWeight / (fCurrDistThresholdFactor * 2); + uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; + uchar& nCurrRegionIllumUpdtVal = m_oIllumUpdtRegionMask.data[nPxIter]; + uchar& nCurrRegionSegmVal = oCurrFGMask.data[nPxIter]; + const bool bCurrRegionIsROIBorder = m_oROI.data[nPxIter] < UCHAR_MAX; + const int nCurrImgCoord_X = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X; + const int nCurrImgCoord_Y = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y; + ushort nCurrInterDesc, nCurrIntraDesc; + LBSP_::computeGrayscaleDescriptor(oInputImg, nCurrColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nCurrColor], nCurrIntraDesc); + const uchar nCurrIntraDescBITS = (uchar)popcount(nCurrIntraDesc); + const bool bCurrRegionIsFlat = nCurrIntraDescBITS < FLAT_REGION_BIT_COUNT; + if (bCurrRegionIsFlat) + ++nFlatRegionCount; + const size_t nCurrWordOccIncr = (DEFAULT_LWORD_OCC_INCR + m_nModelResetCooldown) << int(bCurrRegionIsFlat || bBootstrapping); + const size_t nCurrLocalWordUpdateRate = learningRateOverride > 0 ? (size_t)ceil(learningRateOverride) : bCurrRegionIsFlat ? (size_t)ceil(fCurrLearningRate + FEEDBACK_T_LOWER) / 2 : (size_t)ceil(fCurrLearningRate); + const size_t nCurrColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) / 2; + const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET); + size_t nLocalWordIdx = 0; + float fPotentialLocalWordsWeightSum = 0.0f; + float fLastLocalWordWeight = FLT_MAX; + while (nLocalWordIdx < m_nCurrLocalWords && fPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { + LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + const float fCurrLocalWordWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + { + const size_t nColorDist = L1dist(nCurrColor, pCurrLocalWord->oFeature.anColor[0]); + const size_t nIntraDescDist = hdist(nCurrIntraDesc, pCurrLocalWord->oFeature.anDesc[0]); + LBSP_::computeGrayscaleDescriptor(oInputImg, pCurrLocalWord->oFeature.anColor[0], nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[0]], nCurrInterDesc); + const size_t nInterDescDist = hdist(nCurrInterDesc, pCurrLocalWord->oFeature.anDesc[0]); + const size_t nDescDist = (nIntraDescDist + nInterDescDist) / 2; + if ((!bCurrRegionIsUnstable || bCurrRegionIsFlat || bCurrRegionIsROIBorder) + && nColorDist <= nCurrColorDistThreshold + && nColorDist >= nCurrColorDistThreshold / 2 + && nIntraDescDist <= nCurrDescDistThreshold / 2 + && (rand() % (nCurrRegionIllumUpdtVal ? (nCurrLocalWordUpdateRate / 2 + 1) : nCurrLocalWordUpdateRate)) == 0) { + // == illum updt + pCurrLocalWord->oFeature.anColor[0] = nCurrColor; + pCurrLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; + m_oIllumUpdtRegionMask.data[nPxIter - 1] = 1 & m_oROI.data[nPxIter - 1]; + m_oIllumUpdtRegionMask.data[nPxIter + 1] = 1 & m_oROI.data[nPxIter + 1]; + m_oIllumUpdtRegionMask.data[nPxIter] = 2; + } + if (nDescDist <= nCurrDescDistThreshold && nColorDist <= nCurrColorDistThreshold) { + fPotentialLocalWordsWeightSum += fCurrLocalWordWeight; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + if ((!m_oLastFGMask.data[nPxIter] || m_bUsingMovingCamera) && fCurrLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pCurrLocalWord->nOccurrences += nCurrWordOccIncr; + nMinColorDist = std::min(nMinColorDist, nColorDist); + nMinDescDist = std::min(nMinDescDist, nDescDist); + } + } + if (fCurrLocalWordWeight > fLastLocalWordWeight) { + std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); + } + else + fLastLocalWordWeight = fCurrLocalWordWeight; + ++nLocalWordIdx; + } + while (nLocalWordIdx < m_nCurrLocalWords) { + const float fCurrLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset); + if (fCurrLocalWordWeight > fLastLocalWordWeight) { + std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); + } + else + fLastLocalWordWeight = fCurrLocalWordWeight; + ++nLocalWordIdx; + } + if (fPotentialLocalWordsWeightSum >= fLocalWordsWeightSumThreshold || bCurrRegionIsROIBorder) { + // == background + const float fNormalizedMinDist = std::max((float)nMinColorDist / s_nColorMaxDataRange_1ch, (float)nMinDescDist / s_nDescMaxDataRange_1ch); + fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; + fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; + fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT); + fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST); + if ((rand() % nCurrLocalWordUpdateRate) == 0) { + size_t nGlobalWordLUTIdx; + GlobalWord_1ch* pCurrGlobalWord = nullptr; + for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { + pCurrGlobalWord = (GlobalWord_1ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; + if (L1dist(pCurrGlobalWord->oFeature.anColor[0], nCurrColor) <= nCurrColorDistThreshold + && L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR) + break; + } + if (nGlobalWordLUTIdx != m_nCurrGlobalWords || (rand() % (nCurrLocalWordUpdateRate * 2)) == 0) { + if (nGlobalWordLUTIdx == m_nCurrGlobalWords) { + pCurrGlobalWord = (GlobalWord_1ch*)m_apGlobalWordDict[m_nCurrGlobalWords - 1]; + pCurrGlobalWord->oFeature.anColor[0] = nCurrColor; + pCurrGlobalWord->oFeature.anDesc[0] = nCurrIntraDesc; + pCurrGlobalWord->nDescBITS = nCurrIntraDescBITS; + pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); + pCurrGlobalWord->fLatestWeight = 0.0f; + } + float& fCurrGlobalWordLocalWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fCurrGlobalWordLocalWeight < fPotentialLocalWordsWeightSum) { + pCurrGlobalWord->fLatestWeight += fPotentialLocalWordsWeightSum; + fCurrGlobalWordLocalWeight += fPotentialLocalWordsWeightSum; + } + } + } + } + else { + // == foreground + const float fNormalizedMinDist = std::max(std::max((float)nMinColorDist / s_nColorMaxDataRange_1ch, (float)nMinDescDist / s_nDescMaxDataRange_1ch), (fLocalWordsWeightSumThreshold - fPotentialLocalWordsWeightSum) / fLocalWordsWeightSumThreshold); + fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; + fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; + fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT) + fRollAvgFactor_LT; + fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST) + fRollAvgFactor_ST; + if (bCurrRegionIsFlat || (rand() % nCurrLocalWordUpdateRate) == 0) { + size_t nGlobalWordLUTIdx; + GlobalWord_1ch* pCurrGlobalWord; + for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { + pCurrGlobalWord = (GlobalWord_1ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; + if (L1dist(pCurrGlobalWord->oFeature.anColor[0], nCurrColor) <= nCurrColorDistThreshold + && L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR) + break; + } + if (nGlobalWordLUTIdx == m_nCurrGlobalWords) + nCurrRegionSegmVal = UCHAR_MAX; + else { + const float fGlobalWordLocalizedWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fPotentialLocalWordsWeightSum + fGlobalWordLocalizedWeight / (bCurrRegionIsFlat ? 2 : 4) < fLocalWordsWeightSumThreshold) + nCurrRegionSegmVal = UCHAR_MAX; + } + } + else + nCurrRegionSegmVal = UCHAR_MAX; + if (fPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { + const size_t nNewLocalWordIdx = m_nCurrLocalWords - 1; + LocalWord_1ch* pNewLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nNewLocalWordIdx]; + pNewLocalWord->oFeature.anColor[0] = nCurrColor; + pNewLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; + pNewLocalWord->nOccurrences = nCurrWordOccIncr; + pNewLocalWord->nFirstOcc = m_nFrameIndex; + pNewLocalWord->nLastOcc = m_nFrameIndex; + } + } + // == neighb updt + if ((!nCurrRegionSegmVal && (rand() % nCurrLocalWordUpdateRate) == 0) || bCurrRegionIsROIBorder || m_bUsingMovingCamera) { + //if((!nCurrRegionSegmVal && (rand()%(nCurrRegionIllumUpdtVal?(nCurrLocalWordUpdateRate/2+1):nCurrLocalWordUpdateRate))==0) || bCurrRegionIsROIBorder) { + int nSampleImgCoord_Y, nSampleImgCoord_X; + if (bCurrRegionIsFlat || bCurrRegionIsROIBorder || m_bUsingMovingCamera) + getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); + else + getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); + const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + if (m_oROI.data[nSamplePxIdx]) { + const size_t nNeighborLocalDictIdx = m_aPxInfoLUT_PAWCS[nSamplePxIdx].nModelIdx*m_nCurrLocalWords; + size_t nNeighborLocalWordIdx = 0; + float fNeighborPotentialLocalWordsWeightSum = 0.0f; + while (nNeighborLocalWordIdx < m_nCurrLocalWords && fNeighborPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { + LocalWord_1ch* pNeighborLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; + const size_t nNeighborColorDist = L1dist(nCurrColor, pNeighborLocalWord->oFeature.anColor[0]); + const size_t nNeighborIntraDescDist = hdist(nCurrIntraDesc, pNeighborLocalWord->oFeature.anDesc[0]); + const bool bNeighborRegionIsFlat = popcount(pNeighborLocalWord->oFeature.anDesc[0]) < FLAT_REGION_BIT_COUNT; + const size_t nNeighborWordOccIncr = bNeighborRegionIsFlat ? nCurrWordOccIncr * 2 : nCurrWordOccIncr; + if (nNeighborColorDist <= nCurrColorDistThreshold && nNeighborIntraDescDist <= nCurrDescDistThreshold) { + const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; + } + else if (!oCurrFGMask.data[nSamplePxIdx] && bCurrRegionIsFlat && (bBootstrapping || (rand() % nCurrLocalWordUpdateRate) == 0)) { + const size_t nSampleDescIdx = nSamplePxIdx * 2; + ushort& nNeighborLastIntraDesc = *((ushort*)(m_oLastDescFrame.data + nSampleDescIdx)); + const size_t nNeighborLastIntraDescDist = hdist(nCurrIntraDesc, nNeighborLastIntraDesc); + if (nNeighborColorDist <= nCurrColorDistThreshold && nNeighborLastIntraDescDist <= nCurrDescDistThreshold / 2) { + const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; + pNeighborLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; + } + } + ++nNeighborLocalWordIdx; + } + if (fNeighborPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { + nNeighborLocalWordIdx = m_nCurrLocalWords - 1; + LocalWord_1ch* pNeighborLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; + pNeighborLocalWord->oFeature.anColor[0] = nCurrColor; + pNeighborLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; + pNeighborLocalWord->nOccurrences = nCurrWordOccIncr; + pNeighborLocalWord->nFirstOcc = m_nFrameIndex; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + } + } + } + if (nCurrRegionIllumUpdtVal) + nCurrRegionIllumUpdtVal -= 1; + // == feedback adj + bCurrRegionIsUnstable = fCurrDistThresholdFactor > UNSTABLE_REG_RDIST_MIN || (fCurrMeanRawSegmRes_LT - fCurrMeanFinalSegmRes_LT) > UNSTABLE_REG_RATIO_MIN || (fCurrMeanRawSegmRes_ST - fCurrMeanFinalSegmRes_ST) > UNSTABLE_REG_RATIO_MIN; + if (m_oLastFGMask.data[nPxIter] || (std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && nCurrRegionSegmVal)) + fCurrLearningRate = std::min(fCurrLearningRate + FEEDBACK_T_INCR / (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST)*fCurrDistThresholdVariationFactor), FEEDBACK_T_UPPER); + else + fCurrLearningRate = std::max(fCurrLearningRate - FEEDBACK_T_DECR*fCurrDistThresholdVariationFactor / std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST), FEEDBACK_T_LOWER); + if (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) > UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[nPxIter]) + (fCurrDistThresholdVariationFactor) += bBootstrapping ? FEEDBACK_V_INCR * 2 : FEEDBACK_V_INCR; + else + fCurrDistThresholdVariationFactor = std::max(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR*((bBootstrapping || bCurrRegionIsFlat) ? 2 : m_oLastFGMask.data[nPxIter] ? 0.5f : 1), FEEDBACK_V_DECR); + if (fCurrDistThresholdFactor < std::pow(1.0f + std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) * 2, 2)) + fCurrDistThresholdFactor += FEEDBACK_R_VAR*(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR); + else + fCurrDistThresholdFactor = std::max(fCurrDistThresholdFactor - FEEDBACK_R_VAR / fCurrDistThresholdVariationFactor, 1.0f); + nLastIntraDesc = nCurrIntraDesc; + nLastColor = nCurrColor; + } + } + else { //m_nImgChannels==3 + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const size_t nPxRGBIter = nPxIter * 3; + const size_t nDescRGBIter = nPxRGBIter * 2; const size_t nFloatIter = nPxIter * 4; + const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; + const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; + const uchar* const anCurrColor = oInputImg.data + nPxRGBIter; + uchar* anLastColor = m_oLastColorFrame.data + nPxRGBIter; + ushort* anLastIntraDesc = ((ushort*)(m_oLastDescFrame.data + nDescRGBIter)); + size_t nMinTotColorDist = s_nColorMaxDataRange_3ch; + size_t nMinTotDescDist = s_nDescMaxDataRange_3ch; + float& fCurrMeanRawSegmRes_LT = *(float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter); + float& fCurrMeanRawSegmRes_ST = *(float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter); + float& fCurrMeanFinalSegmRes_LT = *(float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter); + float& fCurrMeanFinalSegmRes_ST = *(float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter); + float& fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + float& fCurrDistThresholdVariationFactor = *(float*)(m_oDistThresholdVariationFrame.data + nFloatIter); + float& fCurrLearningRate = *(float*)(m_oUpdateRateFrame.data + nFloatIter); + float& fCurrMeanMinDist_LT = *(float*)(m_oMeanMinDistFrame_LT.data + nFloatIter); + float& fCurrMeanMinDist_ST = *(float*)(m_oMeanMinDistFrame_ST.data + nFloatIter); + const float fBestLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx], m_nFrameIndex, m_nLocalWordWeightOffset); + const float fLocalWordsWeightSumThreshold = fBestLocalWordWeight / (fCurrDistThresholdFactor * 2); uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; - const float fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); + uchar& nCurrRegionIllumUpdtVal = m_oIllumUpdtRegionMask.data[nPxIter]; + uchar& nCurrRegionSegmVal = oCurrFGMask.data[nPxIter]; + const bool bCurrRegionIsROIBorder = m_oROI.data[nPxIter] < UCHAR_MAX; + const int nCurrImgCoord_X = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X; + const int nCurrImgCoord_Y = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y; + 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, nCurrImgCoord_X, nCurrImgCoord_Y, anCurrIntraLBSPThresholds, anCurrIntraDesc); + const uchar nCurrIntraDescBITS = (uchar)popcount<3>(anCurrIntraDesc); + const bool bCurrRegionIsFlat = nCurrIntraDescBITS < FLAT_REGION_BIT_COUNT * 2; + if (bCurrRegionIsFlat) + ++nFlatRegionCount; + const size_t nCurrWordOccIncr = (DEFAULT_LWORD_OCC_INCR + m_nModelResetCooldown) << int(bCurrRegionIsFlat || bBootstrapping); + const size_t nCurrLocalWordUpdateRate = learningRateOverride > 0 ? (size_t)ceil(learningRateOverride) : bCurrRegionIsFlat ? (size_t)ceil(fCurrLearningRate + FEEDBACK_T_LOWER) / 2 : (size_t)ceil(fCurrLearningRate); const size_t nCurrTotColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) * 3; const size_t nCurrTotDescDistThreshold = (((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET)) * 3; - CV_Assert(m_apLocalWordDict[nLocalDictIdx]); - const LocalWord_3ch* pRefBestLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx]; - const float fRefBestLocalWordWeight = GetLocalWordWeight(pRefBestLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - const uchar nRefBestLocalWordDescBITS = (uchar)popcount<3>(pRefBestLocalWord->oFeature.anDesc); - bool bFoundUninitd = false; - size_t nGlobalWordIdx; - for (nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { - GlobalWord_3ch* pCurrGlobalWord = (GlobalWord_3ch*)m_apGlobalWordDict[nGlobalWordIdx]; - if (pCurrGlobalWord - && L1dist(nRefBestLocalWordDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrTotDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR - && cmixdist<3>(pRefBestLocalWord->oFeature.anColor, pCurrGlobalWord->oFeature.anColor) <= nCurrTotColorDistThreshold) - break; - else if (!pCurrGlobalWord) - bFoundUninitd = true; + size_t nLocalWordIdx = 0; + float fPotentialLocalWordsWeightSum = 0.0f; + float fLastLocalWordWeight = FLT_MAX; + while (nLocalWordIdx < m_nCurrLocalWords && fPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { + LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + const float fCurrLocalWordWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + { + const size_t nTotColorL1Dist = L1dist<3>(anCurrColor, pCurrLocalWord->oFeature.anColor); + const size_t nColorDistortion = cdist<3>(anCurrColor, pCurrLocalWord->oFeature.anColor); + const size_t nTotColorMixDist = cmixdist(nTotColorL1Dist, nColorDistortion); + const size_t nTotIntraDescDist = hdist<3>(anCurrIntraDesc, pCurrLocalWord->oFeature.anDesc); + const size_t anCurrInterLBSPThresholds[3] = { m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[0]],m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[1]],m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[2]] }; + LBSP_::computeRGBDescriptor(oInputImg, pCurrLocalWord->oFeature.anColor, nCurrImgCoord_X, nCurrImgCoord_Y, anCurrInterLBSPThresholds, anCurrInterDesc); + const size_t nTotInterDescDist = hdist<3>(anCurrInterDesc, pCurrLocalWord->oFeature.anDesc); + const size_t nTotDescDist = (nTotIntraDescDist + nTotInterDescDist) / 2; + if ((!bCurrRegionIsUnstable || bCurrRegionIsFlat || bCurrRegionIsROIBorder) + && nTotColorMixDist <= nCurrTotColorDistThreshold + && nTotColorL1Dist >= nCurrTotColorDistThreshold / 2 + && nTotIntraDescDist <= nCurrTotDescDistThreshold / 2 + && (rand() % (nCurrRegionIllumUpdtVal ? (nCurrLocalWordUpdateRate / 2 + 1) : nCurrLocalWordUpdateRate)) == 0) { + // == illum updt + for (size_t c = 0; c < 3; ++c) { + pCurrLocalWord->oFeature.anColor[c] = anCurrColor[c]; + pCurrLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + } + m_oIllumUpdtRegionMask.data[nPxIter - 1] = 1 & m_oROI.data[nPxIter - 1]; + m_oIllumUpdtRegionMask.data[nPxIter + 1] = 1 & m_oROI.data[nPxIter + 1]; + m_oIllumUpdtRegionMask.data[nPxIter] = 2; + } + if (nTotDescDist <= nCurrTotDescDistThreshold && nTotColorMixDist <= nCurrTotColorDistThreshold) { + fPotentialLocalWordsWeightSum += fCurrLocalWordWeight; + pCurrLocalWord->nLastOcc = m_nFrameIndex; + if ((!m_oLastFGMask.data[nPxIter] || m_bUsingMovingCamera) && fCurrLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pCurrLocalWord->nOccurrences += nCurrWordOccIncr; + nMinTotColorDist = std::min(nMinTotColorDist, nTotColorMixDist); + nMinTotDescDist = std::min(nMinTotDescDist, nTotDescDist); + } + } + if (fCurrLocalWordWeight > fLastLocalWordWeight) { + std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); + } + else + fLastLocalWordWeight = fCurrLocalWordWeight; + ++nLocalWordIdx; } - if (nGlobalWordIdx == m_nCurrGlobalWords) { - nGlobalWordIdx = m_nCurrGlobalWords - 1; - GlobalWord_3ch* pCurrGlobalWord = bFoundUninitd ? m_pGlobalWordListIter_3ch++ : (GlobalWord_3ch*)m_apGlobalWordDict[nGlobalWordIdx]; - for (size_t c = 0; c < 3; ++c) { - pCurrGlobalWord->oFeature.anColor[c] = pRefBestLocalWord->oFeature.anColor[c]; - pCurrGlobalWord->oFeature.anDesc[c] = pRefBestLocalWord->oFeature.anDesc[c]; + while (nLocalWordIdx < m_nCurrLocalWords) { + const float fCurrLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset); + if (fCurrLocalWordWeight > fLastLocalWordWeight) { + std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); + } + else + fLastLocalWordWeight = fCurrLocalWordWeight; + ++nLocalWordIdx; + } + if (fPotentialLocalWordsWeightSum >= fLocalWordsWeightSumThreshold || bCurrRegionIsROIBorder) { + // == background + const float fNormalizedMinDist = std::max((float)nMinTotColorDist / s_nColorMaxDataRange_3ch, (float)nMinTotDescDist / s_nDescMaxDataRange_3ch); + fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; + fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; + fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT); + fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST); + if ((rand() % nCurrLocalWordUpdateRate) == 0) { + size_t nGlobalWordLUTIdx; + GlobalWord_3ch* pCurrGlobalWord = nullptr; + for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { + pCurrGlobalWord = (GlobalWord_3ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; + if (L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrTotDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR + && cmixdist<3>(anCurrColor, pCurrGlobalWord->oFeature.anColor) <= nCurrTotColorDistThreshold) + break; + } + if (nGlobalWordLUTIdx != m_nCurrGlobalWords || (rand() % (nCurrLocalWordUpdateRate * 2)) == 0) { + if (nGlobalWordLUTIdx == m_nCurrGlobalWords) { + pCurrGlobalWord = (GlobalWord_3ch*)m_apGlobalWordDict[m_nCurrGlobalWords - 1]; + for (size_t c = 0; c < 3; ++c) { + pCurrGlobalWord->oFeature.anColor[c] = anCurrColor[c]; + pCurrGlobalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + } + pCurrGlobalWord->nDescBITS = nCurrIntraDescBITS; + pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); + pCurrGlobalWord->fLatestWeight = 0.0f; + } + float& fCurrGlobalWordLocalWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fCurrGlobalWordLocalWeight < fPotentialLocalWordsWeightSum) { + pCurrGlobalWord->fLatestWeight += fPotentialLocalWordsWeightSum; + fCurrGlobalWordLocalWeight += fPotentialLocalWordsWeightSum; + } + } } - pCurrGlobalWord->nDescBITS = nRefBestLocalWordDescBITS; - pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); - pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); - pCurrGlobalWord->fLatestWeight = 0.0f; - m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; } - float& fCurrGlobalWordLocalWeight = *(float*)(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fCurrGlobalWordLocalWeight < fRefBestLocalWordWeight) { - m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight += fRefBestLocalWordWeight; - fCurrGlobalWordLocalWeight += fRefBestLocalWordWeight; + else { + // == foreground + const float fNormalizedMinDist = std::max(std::max((float)nMinTotColorDist / s_nColorMaxDataRange_3ch, (float)nMinTotDescDist / s_nDescMaxDataRange_3ch), (fLocalWordsWeightSumThreshold - fPotentialLocalWordsWeightSum) / fLocalWordsWeightSumThreshold); + fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; + fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; + fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT) + fRollAvgFactor_LT; + fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST) + fRollAvgFactor_ST; + if (bCurrRegionIsFlat || (rand() % nCurrLocalWordUpdateRate) == 0) { + size_t nGlobalWordLUTIdx; + GlobalWord_3ch* pCurrGlobalWord; + for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { + pCurrGlobalWord = (GlobalWord_3ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; + if (L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrTotDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR + && cmixdist<3>(anCurrColor, pCurrGlobalWord->oFeature.anColor) <= nCurrTotColorDistThreshold) + break; + } + if (nGlobalWordLUTIdx == m_nCurrGlobalWords) + nCurrRegionSegmVal = UCHAR_MAX; + else { + const float fGlobalWordLocalizedWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fPotentialLocalWordsWeightSum + fGlobalWordLocalizedWeight / (bCurrRegionIsFlat ? 2 : 4) < fLocalWordsWeightSumThreshold) + nCurrRegionSegmVal = UCHAR_MAX; + } + } + else + nCurrRegionSegmVal = UCHAR_MAX; + if (fPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { + const size_t nNewLocalWordIdx = m_nCurrLocalWords - 1; + LocalWord_3ch* pNewLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nNewLocalWordIdx]; + for (size_t c = 0; c < 3; ++c) { + pNewLocalWord->oFeature.anColor[c] = anCurrColor[c]; + pNewLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + } + pNewLocalWord->nOccurrences = nCurrWordOccIncr; + pNewLocalWord->nFirstOcc = m_nFrameIndex; + pNewLocalWord->nLastOcc = m_nFrameIndex; + } } - oGlobalDictPresenceLookupMap.data[nPxIter] = UCHAR_MAX; - while (nGlobalWordIdx > 0 && (!m_apGlobalWordDict[nGlobalWordIdx - 1] || m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > m_apGlobalWordDict[nGlobalWordIdx - 1]->fLatestWeight)) { - std::swap(m_apGlobalWordDict[nGlobalWordIdx], m_apGlobalWordDict[nGlobalWordIdx - 1]); - --nGlobalWordIdx; + // == neighb updt + if ((!nCurrRegionSegmVal && (rand() % nCurrLocalWordUpdateRate) == 0) || bCurrRegionIsROIBorder || m_bUsingMovingCamera) { + //if((!nCurrRegionSegmVal && (rand()%(nCurrRegionIllumUpdtVal?(nCurrLocalWordUpdateRate/2+1):nCurrLocalWordUpdateRate))==0) || bCurrRegionIsROIBorder) { + int nSampleImgCoord_Y, nSampleImgCoord_X; + if (bCurrRegionIsFlat || bCurrRegionIsROIBorder || m_bUsingMovingCamera) + getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); + else + getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); + const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + if (m_oROI.data[nSamplePxIdx]) { + const size_t nNeighborLocalDictIdx = m_aPxInfoLUT_PAWCS[nSamplePxIdx].nModelIdx*m_nCurrLocalWords; + size_t nNeighborLocalWordIdx = 0; + float fNeighborPotentialLocalWordsWeightSum = 0.0f; + while (nNeighborLocalWordIdx < m_nCurrLocalWords && fNeighborPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { + LocalWord_3ch* pNeighborLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; + const size_t nNeighborTotColorL1Dist = L1dist<3>(anCurrColor, pNeighborLocalWord->oFeature.anColor); + const size_t nNeighborColorDistortion = cdist<3>(anCurrColor, pNeighborLocalWord->oFeature.anColor); + const size_t nNeighborTotColorMixDist = cmixdist(nNeighborTotColorL1Dist, nNeighborColorDistortion); + const size_t nNeighborTotIntraDescDist = hdist<3>(anCurrIntraDesc, pNeighborLocalWord->oFeature.anDesc); + const bool bNeighborRegionIsFlat = popcount<3>(pNeighborLocalWord->oFeature.anDesc) < FLAT_REGION_BIT_COUNT * 2; + const size_t nNeighborWordOccIncr = bNeighborRegionIsFlat ? nCurrWordOccIncr * 2 : nCurrWordOccIncr; + if (nNeighborTotColorMixDist <= nCurrTotColorDistThreshold && nNeighborTotIntraDescDist <= nCurrTotDescDistThreshold) { + const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; + } + else if (!oCurrFGMask.data[nSamplePxIdx] && bCurrRegionIsFlat && (bBootstrapping || (rand() % nCurrLocalWordUpdateRate) == 0)) { + const size_t nSamplePxRGBIdx = nSamplePxIdx * 3; + const size_t nSampleDescRGBIdx = nSamplePxRGBIdx * 2; + ushort* anNeighborLastIntraDesc = ((ushort*)(m_oLastDescFrame.data + nSampleDescRGBIdx)); + const size_t nNeighborTotLastIntraDescDist = hdist<3>(anCurrIntraDesc, anNeighborLastIntraDesc); + if (nNeighborTotColorMixDist <= nCurrTotColorDistThreshold && nNeighborTotLastIntraDescDist <= nCurrTotDescDistThreshold / 2) { + const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; + for (size_t c = 0; c < 3; ++c) + pNeighborLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + } + else { + const bool bNeighborLastRegionIsFlat = popcount<3>(anNeighborLastIntraDesc) < FLAT_REGION_BIT_COUNT * 2; + if (bNeighborLastRegionIsFlat && bCurrRegionIsFlat && + nNeighborTotLastIntraDescDist + nNeighborTotIntraDescDist <= nCurrTotDescDistThreshold && + nNeighborColorDistortion <= nCurrTotColorDistThreshold / 4) { + const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) + pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; + for (size_t c = 0; c < 3; ++c) + pNeighborLocalWord->oFeature.anColor[c] = anCurrColor[c]; + } + } + } + ++nNeighborLocalWordIdx; + } + if (fNeighborPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { + nNeighborLocalWordIdx = m_nCurrLocalWords - 1; + LocalWord_3ch* pNeighborLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; + for (size_t c = 0; c < 3; ++c) { + pNeighborLocalWord->oFeature.anColor[c] = anCurrColor[c]; + pNeighborLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + } + pNeighborLocalWord->nOccurrences = nCurrWordOccIncr; + pNeighborLocalWord->nFirstOcc = m_nFrameIndex; + pNeighborLocalWord->nLastOcc = m_nFrameIndex; + } + } + } + if (nCurrRegionIllumUpdtVal) + nCurrRegionIllumUpdtVal -= 1; + // == feedback adj + bCurrRegionIsUnstable = fCurrDistThresholdFactor > UNSTABLE_REG_RDIST_MIN || (fCurrMeanRawSegmRes_LT - fCurrMeanFinalSegmRes_LT) > UNSTABLE_REG_RATIO_MIN || (fCurrMeanRawSegmRes_ST - fCurrMeanFinalSegmRes_ST) > UNSTABLE_REG_RATIO_MIN; + if (m_oLastFGMask.data[nPxIter] || (std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && nCurrRegionSegmVal)) + fCurrLearningRate = std::min(fCurrLearningRate + FEEDBACK_T_INCR / (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST)*fCurrDistThresholdVariationFactor), FEEDBACK_T_UPPER); + else + fCurrLearningRate = std::max(fCurrLearningRate - FEEDBACK_T_DECR*fCurrDistThresholdVariationFactor / std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST), FEEDBACK_T_LOWER); + if (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) > UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[nPxIter]) + (fCurrDistThresholdVariationFactor) += bBootstrapping ? FEEDBACK_V_INCR * 2 : FEEDBACK_V_INCR; + else + fCurrDistThresholdVariationFactor = std::max(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR*((bBootstrapping || bCurrRegionIsFlat) ? 2 : m_oLastFGMask.data[nPxIter] ? 0.5f : 1), FEEDBACK_V_DECR); + if (fCurrDistThresholdFactor < std::pow(1.0f + std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) * 2, 2)) + fCurrDistThresholdFactor += FEEDBACK_R_VAR*(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR); + else + fCurrDistThresholdFactor = std::max(fCurrDistThresholdFactor - FEEDBACK_R_VAR / fCurrDistThresholdVariationFactor, 1.0f); + for (size_t c = 0; c < 3; ++c) { + anLastIntraDesc[c] = anCurrIntraDesc[c]; + anLastColor[c] = anCurrColor[c]; } } } - } - nPxIterIncr = std::max(nPxIterIncr / 3, (size_t)1); - } - for (size_t nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { - GlobalWord_3ch* pCurrGlobalWord = (GlobalWord_3ch*)m_apGlobalWordDict[nGlobalWordIdx]; - if (!pCurrGlobalWord) { - pCurrGlobalWord = m_pGlobalWordListIter_3ch++; - for (size_t c = 0; c < 3; ++c) { - pCurrGlobalWord->oFeature.anColor[c] = 0; - pCurrGlobalWord->oFeature.anDesc[c] = 0; - } - pCurrGlobalWord->nDescBITS = 0; - pCurrGlobalWord->oSpatioOccMap.create(m_oDownSampledFrameSize_GlobalWordLookup, CV_32FC1); - pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); - pCurrGlobalWord->fLatestWeight = 0.0f; - m_apGlobalWordDict[nGlobalWordIdx] = pCurrGlobalWord; - } - } - CV_Assert((size_t)(m_pGlobalWordListIter_3ch - m_aGlobalWordList_3ch) == m_nCurrGlobalWords && m_aGlobalWordList_3ch == (m_pGlobalWordListIter_3ch - m_nCurrGlobalWords)); - } - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - // == refresh: per-px global word sort - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; - float fLastGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[0]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - for (size_t nGlobalWordLUTIdx = 1; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { - const float fCurrGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fCurrGlobalWordLocalWeight > fLastGlobalWordLocalWeight) - std::swap(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx], m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx - 1]); - else - fLastGlobalWordLocalWeight = fCurrGlobalWordLocalWeight; - } - } -} - -void BackgroundSubtractorPAWCS::apply(cv::InputArray _image, cv::OutputArray _fgmask, double learningRateOverride) { - // == process - CV_Assert(m_bInitialized); - cv::Mat oInputImg = _image.getMat(); - CV_Assert(oInputImg.type() == m_nImgType && oInputImg.size() == m_oImgSize); - CV_Assert(oInputImg.isContinuous()); - _fgmask.create(m_oImgSize, CV_8UC1); - cv::Mat oCurrFGMask = _fgmask.getMat(); - memset(oCurrFGMask.data, 0, oCurrFGMask.cols*oCurrFGMask.rows); - const bool bBootstrapping = ++m_nFrameIndex <= DEFAULT_BOOTSTRAP_WIN_SIZE; - const size_t nCurrSamplesForMovingAvg_LT = bBootstrapping ? m_nSamplesForMovingAvgs / 2 : m_nSamplesForMovingAvgs; - const size_t nCurrSamplesForMovingAvg_ST = nCurrSamplesForMovingAvg_LT / 4; - const float fRollAvgFactor_LT = 1.0f / std::min(m_nFrameIndex, nCurrSamplesForMovingAvg_LT); - const float fRollAvgFactor_ST = 1.0f / std::min(m_nFrameIndex, nCurrSamplesForMovingAvg_ST); - const size_t nCurrGlobalWordUpdateRate = bBootstrapping ? DEFAULT_RESAMPLING_RATE / 2 : DEFAULT_RESAMPLING_RATE; - size_t nFlatRegionCount = 0; - if (m_nImgChannels == 1) { - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const size_t nDescIter = nPxIter * 2; - const size_t nFloatIter = nPxIter * 4; - const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; - const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; - const uchar nCurrColor = oInputImg.data[nPxIter]; - uchar& nLastColor = m_oLastColorFrame.data[nPxIter]; - ushort& nLastIntraDesc = *((ushort*)(m_oLastDescFrame.data + nDescIter)); - size_t nMinColorDist = s_nColorMaxDataRange_1ch; - size_t nMinDescDist = s_nDescMaxDataRange_1ch; - float& fCurrMeanRawSegmRes_LT = *(float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter); - float& fCurrMeanRawSegmRes_ST = *(float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter); - float& fCurrMeanFinalSegmRes_LT = *(float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter); - float& fCurrMeanFinalSegmRes_ST = *(float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter); - float& fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); - float& fCurrDistThresholdVariationFactor = *(float*)(m_oDistThresholdVariationFrame.data + nFloatIter); - float& fCurrLearningRate = *(float*)(m_oUpdateRateFrame.data + nFloatIter); - float& fCurrMeanMinDist_LT = *(float*)(m_oMeanMinDistFrame_LT.data + nFloatIter); - float& fCurrMeanMinDist_ST = *(float*)(m_oMeanMinDistFrame_ST.data + nFloatIter); - const float fBestLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx], m_nFrameIndex, m_nLocalWordWeightOffset); - const float fLocalWordsWeightSumThreshold = fBestLocalWordWeight / (fCurrDistThresholdFactor * 2); - uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; - uchar& nCurrRegionIllumUpdtVal = m_oIllumUpdtRegionMask.data[nPxIter]; - uchar& nCurrRegionSegmVal = oCurrFGMask.data[nPxIter]; - const bool bCurrRegionIsROIBorder = m_oROI.data[nPxIter] < UCHAR_MAX; - const int nCurrImgCoord_X = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X; - const int nCurrImgCoord_Y = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y; - ushort nCurrInterDesc, nCurrIntraDesc; - LBSP_::computeGrayscaleDescriptor(oInputImg, nCurrColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nCurrColor], nCurrIntraDesc); - const uchar nCurrIntraDescBITS = (uchar)popcount(nCurrIntraDesc); - const bool bCurrRegionIsFlat = nCurrIntraDescBITS < FLAT_REGION_BIT_COUNT; - if (bCurrRegionIsFlat) - ++nFlatRegionCount; - const size_t nCurrWordOccIncr = (DEFAULT_LWORD_OCC_INCR + m_nModelResetCooldown) << int(bCurrRegionIsFlat || bBootstrapping); - const size_t nCurrLocalWordUpdateRate = learningRateOverride > 0 ? (size_t)ceil(learningRateOverride) : bCurrRegionIsFlat ? (size_t)ceil(fCurrLearningRate + FEEDBACK_T_LOWER) / 2 : (size_t)ceil(fCurrLearningRate); - const size_t nCurrColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) / 2; - const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET); - size_t nLocalWordIdx = 0; - float fPotentialLocalWordsWeightSum = 0.0f; - float fLastLocalWordWeight = FLT_MAX; - while (nLocalWordIdx < m_nCurrLocalWords && fPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { - LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - const float fCurrLocalWordWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - { - const size_t nColorDist = L1dist(nCurrColor, pCurrLocalWord->oFeature.anColor[0]); - const size_t nIntraDescDist = hdist(nCurrIntraDesc, pCurrLocalWord->oFeature.anDesc[0]); - LBSP_::computeGrayscaleDescriptor(oInputImg, pCurrLocalWord->oFeature.anColor[0], nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[0]], nCurrInterDesc); - const size_t nInterDescDist = hdist(nCurrInterDesc, pCurrLocalWord->oFeature.anDesc[0]); - const size_t nDescDist = (nIntraDescDist + nInterDescDist) / 2; - if ((!bCurrRegionIsUnstable || bCurrRegionIsFlat || bCurrRegionIsROIBorder) - && nColorDist <= nCurrColorDistThreshold - && nColorDist >= nCurrColorDistThreshold / 2 - && nIntraDescDist <= nCurrDescDistThreshold / 2 - && (rand() % (nCurrRegionIllumUpdtVal ? (nCurrLocalWordUpdateRate / 2 + 1) : nCurrLocalWordUpdateRate)) == 0) { - // == illum updt - pCurrLocalWord->oFeature.anColor[0] = nCurrColor; - pCurrLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; - m_oIllumUpdtRegionMask.data[nPxIter - 1] = 1 & m_oROI.data[nPxIter - 1]; - m_oIllumUpdtRegionMask.data[nPxIter + 1] = 1 & m_oROI.data[nPxIter + 1]; - m_oIllumUpdtRegionMask.data[nPxIter] = 2; + const bool bRecalcGlobalWords = !(m_nFrameIndex % (nCurrGlobalWordUpdateRate << 5)); + const bool bUpdateGlobalWords = !(m_nFrameIndex % (nCurrGlobalWordUpdateRate)); + cv::Mat oLastFGMask_dilated_inverted_downscaled; + if (bUpdateGlobalWords) + cv::resize(m_oLastFGMask_dilated_inverted, oLastFGMask_dilated_inverted_downscaled, m_oDownSampledFrameSize_GlobalWordLookup, 0, 0, cv::INTER_NEAREST); + for (size_t nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { + if (bRecalcGlobalWords && m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > 0.0f) { + m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight = GetGlobalWordWeight(m_apGlobalWordDict[nGlobalWordIdx]); + if (m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight < 1.0f) { + m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight = 0.0f; + m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap = cv::Scalar(0.0f); + } } - if (nDescDist <= nCurrDescDistThreshold && nColorDist <= nCurrColorDistThreshold) { - fPotentialLocalWordsWeightSum += fCurrLocalWordWeight; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - if ((!m_oLastFGMask.data[nPxIter] || m_bUsingMovingCamera) && fCurrLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pCurrLocalWord->nOccurrences += nCurrWordOccIncr; - nMinColorDist = std::min(nMinColorDist, nColorDist); - nMinDescDist = std::min(nMinDescDist, nDescDist); + if (bUpdateGlobalWords && m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > 0.0f) { + cv::accumulateProduct(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, m_oTempGlobalWordWeightDiffFactor, m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, oLastFGMask_dilated_inverted_downscaled); + m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight *= 0.9f; + cv::blur(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, cv::Size(3, 3), cv::Point(-1, -1), cv::BORDER_REPLICATE); } + if (nGlobalWordIdx > 0 && m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > m_apGlobalWordDict[nGlobalWordIdx - 1]->fLatestWeight) + std::swap(m_apGlobalWordDict[nGlobalWordIdx], m_apGlobalWordDict[nGlobalWordIdx - 1]); } - if (fCurrLocalWordWeight > fLastLocalWordWeight) { - std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); - } - else - fLastLocalWordWeight = fCurrLocalWordWeight; - ++nLocalWordIdx; - } - while (nLocalWordIdx < m_nCurrLocalWords) { - const float fCurrLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset); - if (fCurrLocalWordWeight > fLastLocalWordWeight) { - std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); - } - else - fLastLocalWordWeight = fCurrLocalWordWeight; - ++nLocalWordIdx; - } - if (fPotentialLocalWordsWeightSum >= fLocalWordsWeightSumThreshold || bCurrRegionIsROIBorder) { - // == background - const float fNormalizedMinDist = std::max((float)nMinColorDist / s_nColorMaxDataRange_1ch, (float)nMinDescDist / s_nDescMaxDataRange_1ch); - fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; - fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; - fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT); - fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST); - if ((rand() % nCurrLocalWordUpdateRate) == 0) { - size_t nGlobalWordLUTIdx; - GlobalWord_1ch* pCurrGlobalWord = nullptr; - for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { - pCurrGlobalWord = (GlobalWord_1ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; - if (L1dist(pCurrGlobalWord->oFeature.anColor[0], nCurrColor) <= nCurrColorDistThreshold - && L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR) - break; - } - if (nGlobalWordLUTIdx != m_nCurrGlobalWords || (rand() % (nCurrLocalWordUpdateRate * 2)) == 0) { - if (nGlobalWordLUTIdx == m_nCurrGlobalWords) { - pCurrGlobalWord = (GlobalWord_1ch*)m_apGlobalWordDict[m_nCurrGlobalWords - 1]; - pCurrGlobalWord->oFeature.anColor[0] = nCurrColor; - pCurrGlobalWord->oFeature.anDesc[0] = nCurrIntraDesc; - pCurrGlobalWord->nDescBITS = nCurrIntraDescBITS; - pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); - pCurrGlobalWord->fLatestWeight = 0.0f; - } - float& fCurrGlobalWordLocalWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fCurrGlobalWordLocalWeight < fPotentialLocalWordsWeightSum) { - pCurrGlobalWord->fLatestWeight += fPotentialLocalWordsWeightSum; - fCurrGlobalWordLocalWeight += fPotentialLocalWordsWeightSum; + if (bUpdateGlobalWords) { + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; + float fLastGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[0]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + for (size_t nGlobalWordLUTIdx = 1; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { + const float fCurrGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); + if (fCurrGlobalWordLocalWeight > fLastGlobalWordLocalWeight) + std::swap(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx], m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx - 1]); + else + fLastGlobalWordLocalWeight = fCurrGlobalWordLocalWeight; } } } - } - else { - // == foreground - const float fNormalizedMinDist = std::max(std::max((float)nMinColorDist / s_nColorMaxDataRange_1ch, (float)nMinDescDist / s_nDescMaxDataRange_1ch), (fLocalWordsWeightSumThreshold - fPotentialLocalWordsWeightSum) / fLocalWordsWeightSumThreshold); - fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; - fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; - fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT) + fRollAvgFactor_LT; - fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST) + fRollAvgFactor_ST; - if (bCurrRegionIsFlat || (rand() % nCurrLocalWordUpdateRate) == 0) { - size_t nGlobalWordLUTIdx; - GlobalWord_1ch* pCurrGlobalWord; - for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { - pCurrGlobalWord = (GlobalWord_1ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; - if (L1dist(pCurrGlobalWord->oFeature.anColor[0], nCurrColor) <= nCurrColorDistThreshold - && L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR) - break; - } - if (nGlobalWordLUTIdx == m_nCurrGlobalWords) - nCurrRegionSegmVal = UCHAR_MAX; - else { - const float fGlobalWordLocalizedWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fPotentialLocalWordsWeightSum + fGlobalWordLocalizedWeight / (bCurrRegionIsFlat ? 2 : 4) < fLocalWordsWeightSumThreshold) - nCurrRegionSegmVal = UCHAR_MAX; - } + cv::bitwise_xor(oCurrFGMask, m_oLastRawFGMask, m_oCurrRawFGBlinkMask); + cv::bitwise_or(m_oCurrRawFGBlinkMask, m_oLastRawFGBlinkMask, m_oBlinksFrame); + m_oCurrRawFGBlinkMask.copyTo(m_oLastRawFGBlinkMask); + oCurrFGMask.copyTo(m_oLastRawFGMask); + cv::morphologyEx(oCurrFGMask, m_oFGMask_PreFlood, cv::MORPH_CLOSE, m_oMorphExStructElement); + m_oFGMask_PreFlood.copyTo(m_oFGMask_FloodedHoles); + cv::floodFill(m_oFGMask_FloodedHoles, cv::Point(0, 0), UCHAR_MAX); + cv::bitwise_not(m_oFGMask_FloodedHoles, m_oFGMask_FloodedHoles); + cv::erode(m_oFGMask_PreFlood, m_oFGMask_PreFlood, cv::Mat(), cv::Point(-1, -1), 3); + cv::bitwise_or(oCurrFGMask, m_oFGMask_FloodedHoles, oCurrFGMask); + cv::bitwise_or(oCurrFGMask, m_oFGMask_PreFlood, oCurrFGMask); + cv::medianBlur(oCurrFGMask, m_oLastFGMask, m_nMedianBlurKernelSize); + cv::dilate(m_oLastFGMask, m_oLastFGMask_dilated, cv::Mat(), cv::Point(-1, -1), 3); + cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); + cv::bitwise_not(m_oLastFGMask_dilated, m_oLastFGMask_dilated_inverted); + cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); + m_oLastFGMask.copyTo(oCurrFGMask); + cv::addWeighted(m_oMeanFinalSegmResFrame_LT, (1.0f - fRollAvgFactor_LT), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_LT, 0, m_oMeanFinalSegmResFrame_LT, CV_32F); + cv::addWeighted(m_oMeanFinalSegmResFrame_ST, (1.0f - fRollAvgFactor_ST), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_ST, 0, m_oMeanFinalSegmResFrame_ST, CV_32F); + const float fCurrNonFlatRegionRatio = (float)(m_nTotRelevantPxCount - nFlatRegionCount) / m_nTotRelevantPxCount; + if (fCurrNonFlatRegionRatio < LBSPDESC_RATIO_MIN && m_fLastNonFlatRegionRatio < LBSPDESC_RATIO_MIN) { + for (size_t t = 0; t <= UCHAR_MAX; ++t) + if (m_anLBSPThreshold_8bitLUT[t] > cv::saturate_cast<uchar>((m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold) / 4)) + --m_anLBSPThreshold_8bitLUT[t]; } - else - nCurrRegionSegmVal = UCHAR_MAX; - if (fPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { - const size_t nNewLocalWordIdx = m_nCurrLocalWords - 1; - LocalWord_1ch* pNewLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nNewLocalWordIdx]; - pNewLocalWord->oFeature.anColor[0] = nCurrColor; - pNewLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; - pNewLocalWord->nOccurrences = nCurrWordOccIncr; - pNewLocalWord->nFirstOcc = m_nFrameIndex; - pNewLocalWord->nLastOcc = m_nFrameIndex; + else if (fCurrNonFlatRegionRatio > LBSPDESC_RATIO_MAX && m_fLastNonFlatRegionRatio > LBSPDESC_RATIO_MAX) { + for (size_t t = 0; t <= UCHAR_MAX; ++t) + if (m_anLBSPThreshold_8bitLUT[t] < cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + UCHAR_MAX*m_fRelLBSPThreshold)) + ++m_anLBSPThreshold_8bitLUT[t]; } - } - // == neighb updt - if ((!nCurrRegionSegmVal && (rand() % nCurrLocalWordUpdateRate) == 0) || bCurrRegionIsROIBorder || m_bUsingMovingCamera) { - //if((!nCurrRegionSegmVal && (rand()%(nCurrRegionIllumUpdtVal?(nCurrLocalWordUpdateRate/2+1):nCurrLocalWordUpdateRate))==0) || bCurrRegionIsROIBorder) { - int nSampleImgCoord_Y, nSampleImgCoord_X; - if (bCurrRegionIsFlat || bCurrRegionIsROIBorder || m_bUsingMovingCamera) - getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); - else - getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); - const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - if (m_oROI.data[nSamplePxIdx]) { - const size_t nNeighborLocalDictIdx = m_aPxInfoLUT_PAWCS[nSamplePxIdx].nModelIdx*m_nCurrLocalWords; - size_t nNeighborLocalWordIdx = 0; - float fNeighborPotentialLocalWordsWeightSum = 0.0f; - while (nNeighborLocalWordIdx < m_nCurrLocalWords && fNeighborPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { - LocalWord_1ch* pNeighborLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; - const size_t nNeighborColorDist = L1dist(nCurrColor, pNeighborLocalWord->oFeature.anColor[0]); - const size_t nNeighborIntraDescDist = hdist(nCurrIntraDesc, pNeighborLocalWord->oFeature.anDesc[0]); - const bool bNeighborRegionIsFlat = popcount(pNeighborLocalWord->oFeature.anDesc[0]) < FLAT_REGION_BIT_COUNT; - const size_t nNeighborWordOccIncr = bNeighborRegionIsFlat ? nCurrWordOccIncr * 2 : nCurrWordOccIncr; - if (nNeighborColorDist <= nCurrColorDistThreshold && nNeighborIntraDescDist <= nCurrDescDistThreshold) { - const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; - if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; + m_fLastNonFlatRegionRatio = fCurrNonFlatRegionRatio; + cv::resize(oInputImg, m_oDownSampledFrame_MotionAnalysis, m_oDownSampledFrameSize_MotionAnalysis, 0, 0, cv::INTER_AREA); + cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, m_oMeanDownSampledLastDistFrame_LT, fRollAvgFactor_LT); + cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, m_oMeanDownSampledLastDistFrame_ST, fRollAvgFactor_ST); + const float fCurrMeanL1DistRatio = L1dist((float*)m_oMeanDownSampledLastDistFrame_LT.data, (float*)m_oMeanDownSampledLastDistFrame_ST.data, m_oMeanDownSampledLastDistFrame_LT.total(), m_nImgChannels, m_oDownSampledROI_MotionAnalysis.data) / m_nDownSampledROIPxCount; + if (!m_bAutoModelResetEnabled && fCurrMeanL1DistRatio >= FRAMELEVEL_MIN_L1DIST_THRES * 2) + m_bAutoModelResetEnabled = true; + if (m_bAutoModelResetEnabled || m_bUsingMovingCamera) { + if ((m_nFrameIndex%DEFAULT_BOOTSTRAP_WIN_SIZE) == 0) { + cv::Mat oCurrBackgroundImg, oDownSampledBackgroundImg; + getBackgroundImage(oCurrBackgroundImg); + cv::resize(oCurrBackgroundImg, oDownSampledBackgroundImg, m_oDownSampledFrameSize_MotionAnalysis, 0, 0, cv::INTER_AREA); + cv::Mat oDownSampledBackgroundImg_32F; oDownSampledBackgroundImg.convertTo(oDownSampledBackgroundImg_32F, CV_32F); + const float fCurrModelL1DistRatio = L1dist((float*)m_oMeanDownSampledLastDistFrame_LT.data, (float*)oDownSampledBackgroundImg_32F.data, m_oMeanDownSampledLastDistFrame_LT.total(), m_nImgChannels, cv::Mat(m_oDownSampledROI_MotionAnalysis == UCHAR_MAX).data) / m_nDownSampledROIPxCount; + const float fCurrModelCDistRatio = cdist((float*)m_oMeanDownSampledLastDistFrame_LT.data, (float*)oDownSampledBackgroundImg_32F.data, m_oMeanDownSampledLastDistFrame_LT.total(), m_nImgChannels, cv::Mat(m_oDownSampledROI_MotionAnalysis == UCHAR_MAX).data) / m_nDownSampledROIPxCount; + if (m_bUsingMovingCamera && fCurrModelL1DistRatio < FRAMELEVEL_MIN_L1DIST_THRES / 4 && fCurrModelCDistRatio < FRAMELEVEL_MIN_CDIST_THRES / 4) { + m_nLocalWordWeightOffset = DEFAULT_LWORD_WEIGHT_OFFSET; + m_bUsingMovingCamera = false; + refreshModel(1, 1, true); } - else if (!oCurrFGMask.data[nSamplePxIdx] && bCurrRegionIsFlat && (bBootstrapping || (rand() % nCurrLocalWordUpdateRate) == 0)) { - const size_t nSampleDescIdx = nSamplePxIdx * 2; - ushort& nNeighborLastIntraDesc = *((ushort*)(m_oLastDescFrame.data + nSampleDescIdx)); - const size_t nNeighborLastIntraDescDist = hdist(nCurrIntraDesc, nNeighborLastIntraDesc); - if (nNeighborColorDist <= nCurrColorDistThreshold && nNeighborLastIntraDescDist <= nCurrDescDistThreshold / 2) { - const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; - if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; - pNeighborLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; - } + else if (bBootstrapping && !m_bUsingMovingCamera && (fCurrModelL1DistRatio >= FRAMELEVEL_MIN_L1DIST_THRES || fCurrModelCDistRatio >= FRAMELEVEL_MIN_CDIST_THRES)) { + m_nLocalWordWeightOffset = 5; + m_bUsingMovingCamera = true; + refreshModel(1, 1, true); } - ++nNeighborLocalWordIdx; } - if (fNeighborPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { - nNeighborLocalWordIdx = m_nCurrLocalWords - 1; - LocalWord_1ch* pNeighborLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; - pNeighborLocalWord->oFeature.anColor[0] = nCurrColor; - pNeighborLocalWord->oFeature.anDesc[0] = nCurrIntraDesc; - pNeighborLocalWord->nOccurrences = nCurrWordOccIncr; - pNeighborLocalWord->nFirstOcc = m_nFrameIndex; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; + if (m_nFramesSinceLastReset > DEFAULT_BOOTSTRAP_WIN_SIZE * 2) + m_bAutoModelResetEnabled = false; + else if (fCurrMeanL1DistRatio >= FRAMELEVEL_MIN_L1DIST_THRES && m_nModelResetCooldown == 0) { + m_nFramesSinceLastReset = 0; + refreshModel(m_nLocalWordWeightOffset / 8, 0, true); + m_nModelResetCooldown = nCurrSamplesForMovingAvg_ST; + m_oUpdateRateFrame = cv::Scalar(1.0f); } + else if (!bBootstrapping) + ++m_nFramesSinceLastReset; } + if (m_nModelResetCooldown > 0) + --m_nModelResetCooldown; } - if (nCurrRegionIllumUpdtVal) - nCurrRegionIllumUpdtVal -= 1; - // == feedback adj - bCurrRegionIsUnstable = fCurrDistThresholdFactor > UNSTABLE_REG_RDIST_MIN || (fCurrMeanRawSegmRes_LT - fCurrMeanFinalSegmRes_LT) > UNSTABLE_REG_RATIO_MIN || (fCurrMeanRawSegmRes_ST - fCurrMeanFinalSegmRes_ST) > UNSTABLE_REG_RATIO_MIN; - if (m_oLastFGMask.data[nPxIter] || (std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && nCurrRegionSegmVal)) - fCurrLearningRate = std::min(fCurrLearningRate + FEEDBACK_T_INCR / (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST)*fCurrDistThresholdVariationFactor), FEEDBACK_T_UPPER); - else - fCurrLearningRate = std::max(fCurrLearningRate - FEEDBACK_T_DECR*fCurrDistThresholdVariationFactor / std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST), FEEDBACK_T_LOWER); - if (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) > UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[nPxIter]) - (fCurrDistThresholdVariationFactor) += bBootstrapping ? FEEDBACK_V_INCR * 2 : FEEDBACK_V_INCR; - else - fCurrDistThresholdVariationFactor = std::max(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR*((bBootstrapping || bCurrRegionIsFlat) ? 2 : m_oLastFGMask.data[nPxIter] ? 0.5f : 1), FEEDBACK_V_DECR); - if (fCurrDistThresholdFactor < std::pow(1.0f + std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) * 2, 2)) - fCurrDistThresholdFactor += FEEDBACK_R_VAR*(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR); - else - fCurrDistThresholdFactor = std::max(fCurrDistThresholdFactor - FEEDBACK_R_VAR / fCurrDistThresholdVariationFactor, 1.0f); - nLastIntraDesc = nCurrIntraDesc; - nLastColor = nCurrColor; - } - } - else { //m_nImgChannels==3 - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const size_t nPxRGBIter = nPxIter * 3; - const size_t nDescRGBIter = nPxRGBIter * 2; - const size_t nFloatIter = nPxIter * 4; - const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; - const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; - const uchar* const anCurrColor = oInputImg.data + nPxRGBIter; - uchar* anLastColor = m_oLastColorFrame.data + nPxRGBIter; - ushort* anLastIntraDesc = ((ushort*)(m_oLastDescFrame.data + nDescRGBIter)); - size_t nMinTotColorDist = s_nColorMaxDataRange_3ch; - size_t nMinTotDescDist = s_nDescMaxDataRange_3ch; - float& fCurrMeanRawSegmRes_LT = *(float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter); - float& fCurrMeanRawSegmRes_ST = *(float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter); - float& fCurrMeanFinalSegmRes_LT = *(float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter); - float& fCurrMeanFinalSegmRes_ST = *(float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter); - float& fCurrDistThresholdFactor = *(float*)(m_oDistThresholdFrame.data + nFloatIter); - float& fCurrDistThresholdVariationFactor = *(float*)(m_oDistThresholdVariationFrame.data + nFloatIter); - float& fCurrLearningRate = *(float*)(m_oUpdateRateFrame.data + nFloatIter); - float& fCurrMeanMinDist_LT = *(float*)(m_oMeanMinDistFrame_LT.data + nFloatIter); - float& fCurrMeanMinDist_ST = *(float*)(m_oMeanMinDistFrame_ST.data + nFloatIter); - const float fBestLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx], m_nFrameIndex, m_nLocalWordWeightOffset); - const float fLocalWordsWeightSumThreshold = fBestLocalWordWeight / (fCurrDistThresholdFactor * 2); - uchar& bCurrRegionIsUnstable = m_oUnstableRegionMask.data[nPxIter]; - uchar& nCurrRegionIllumUpdtVal = m_oIllumUpdtRegionMask.data[nPxIter]; - uchar& nCurrRegionSegmVal = oCurrFGMask.data[nPxIter]; - const bool bCurrRegionIsROIBorder = m_oROI.data[nPxIter] < UCHAR_MAX; - const int nCurrImgCoord_X = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X; - const int nCurrImgCoord_Y = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y; - 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, nCurrImgCoord_X, nCurrImgCoord_Y, anCurrIntraLBSPThresholds, anCurrIntraDesc); - const uchar nCurrIntraDescBITS = (uchar)popcount<3>(anCurrIntraDesc); - const bool bCurrRegionIsFlat = nCurrIntraDescBITS < FLAT_REGION_BIT_COUNT * 2; - if (bCurrRegionIsFlat) - ++nFlatRegionCount; - const size_t nCurrWordOccIncr = (DEFAULT_LWORD_OCC_INCR + m_nModelResetCooldown) << int(bCurrRegionIsFlat || bBootstrapping); - const size_t nCurrLocalWordUpdateRate = learningRateOverride > 0 ? (size_t)ceil(learningRateOverride) : bCurrRegionIsFlat ? (size_t)ceil(fCurrLearningRate + FEEDBACK_T_LOWER) / 2 : (size_t)ceil(fCurrLearningRate); - const size_t nCurrTotColorDistThreshold = (size_t)(sqrt(fCurrDistThresholdFactor)*m_nMinColorDistThreshold) * 3; - const size_t nCurrTotDescDistThreshold = (((size_t)1 << ((size_t)floor(fCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (bCurrRegionIsUnstable*UNSTAB_DESC_DIST_OFFSET)) * 3; - size_t nLocalWordIdx = 0; - float fPotentialLocalWordsWeightSum = 0.0f; - float fLastLocalWordWeight = FLT_MAX; - while (nLocalWordIdx < m_nCurrLocalWords && fPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { - LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - const float fCurrLocalWordWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - { - const size_t nTotColorL1Dist = L1dist<3>(anCurrColor, pCurrLocalWord->oFeature.anColor); - const size_t nColorDistortion = cdist<3>(anCurrColor, pCurrLocalWord->oFeature.anColor); - const size_t nTotColorMixDist = cmixdist(nTotColorL1Dist, nColorDistortion); - const size_t nTotIntraDescDist = hdist<3>(anCurrIntraDesc, pCurrLocalWord->oFeature.anDesc); - const size_t anCurrInterLBSPThresholds[3] = { m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[0]],m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[1]],m_anLBSPThreshold_8bitLUT[pCurrLocalWord->oFeature.anColor[2]] }; - LBSP_::computeRGBDescriptor(oInputImg, pCurrLocalWord->oFeature.anColor, nCurrImgCoord_X, nCurrImgCoord_Y, anCurrInterLBSPThresholds, anCurrInterDesc); - const size_t nTotInterDescDist = hdist<3>(anCurrInterDesc, pCurrLocalWord->oFeature.anDesc); - const size_t nTotDescDist = (nTotIntraDescDist + nTotInterDescDist) / 2; - if ((!bCurrRegionIsUnstable || bCurrRegionIsFlat || bCurrRegionIsROIBorder) - && nTotColorMixDist <= nCurrTotColorDistThreshold - && nTotColorL1Dist >= nCurrTotColorDistThreshold / 2 - && nTotIntraDescDist <= nCurrTotDescDistThreshold / 2 - && (rand() % (nCurrRegionIllumUpdtVal ? (nCurrLocalWordUpdateRate / 2 + 1) : nCurrLocalWordUpdateRate)) == 0) { - // == illum updt - for (size_t c = 0; c < 3; ++c) { - pCurrLocalWord->oFeature.anColor[c] = anCurrColor[c]; - pCurrLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + + void BackgroundSubtractorPAWCS::getBackgroundImage(cv::OutputArray backgroundImage) const { // @@@ add option to reconstruct from gwords? + CV_Assert(m_bInitialized); + cv::Mat oAvgBGImg = cv::Mat::zeros(m_oImgSize, CV_32FC((int)m_nImgChannels)); + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; + const int nCurrImgCoord_X = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X; + const int nCurrImgCoord_Y = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y; + if (m_nImgChannels == 1) { + float fTotWeight = 0.0f; + float fTotColor = 0.0f; + for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + float fCurrWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + fTotColor += (float)pCurrLocalWord->oFeature.anColor[0] * fCurrWeight; + fTotWeight += fCurrWeight; } - m_oIllumUpdtRegionMask.data[nPxIter - 1] = 1 & m_oROI.data[nPxIter - 1]; - m_oIllumUpdtRegionMask.data[nPxIter + 1] = 1 & m_oROI.data[nPxIter + 1]; - m_oIllumUpdtRegionMask.data[nPxIter] = 2; + oAvgBGImg.at<float>(nCurrImgCoord_Y, nCurrImgCoord_X) = fTotColor / fTotWeight; } - if (nTotDescDist <= nCurrTotDescDistThreshold && nTotColorMixDist <= nCurrTotColorDistThreshold) { - fPotentialLocalWordsWeightSum += fCurrLocalWordWeight; - pCurrLocalWord->nLastOcc = m_nFrameIndex; - if ((!m_oLastFGMask.data[nPxIter] || m_bUsingMovingCamera) && fCurrLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pCurrLocalWord->nOccurrences += nCurrWordOccIncr; - nMinTotColorDist = std::min(nMinTotColorDist, nTotColorMixDist); - nMinTotDescDist = std::min(nMinTotDescDist, nTotDescDist); + else { //m_nImgChannels==3 + float fTotWeight = 0.0f; + float fTotColor[3] = { 0.0f,0.0f,0.0f }; + for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { + LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; + float fCurrWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); + for (size_t c = 0; c < 3; ++c) + fTotColor[c] += (float)pCurrLocalWord->oFeature.anColor[c] * fCurrWeight; + fTotWeight += fCurrWeight; + } + oAvgBGImg.at<cv::Vec3f>(nCurrImgCoord_Y, nCurrImgCoord_X) = cv::Vec3f(fTotColor[0] / fTotWeight, fTotColor[1] / fTotWeight, fTotColor[2] / fTotWeight); } } - if (fCurrLocalWordWeight > fLastLocalWordWeight) { - std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); - } - else - fLastLocalWordWeight = fCurrLocalWordWeight; - ++nLocalWordIdx; - } - while (nLocalWordIdx < m_nCurrLocalWords) { - const float fCurrLocalWordWeight = GetLocalWordWeight(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_nFrameIndex, m_nLocalWordWeightOffset); - if (fCurrLocalWordWeight > fLastLocalWordWeight) { - std::swap(m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx], m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx - 1]); - } - else - fLastLocalWordWeight = fCurrLocalWordWeight; - ++nLocalWordIdx; + oAvgBGImg.convertTo(backgroundImage, CV_8U); } - if (fPotentialLocalWordsWeightSum >= fLocalWordsWeightSumThreshold || bCurrRegionIsROIBorder) { - // == background - const float fNormalizedMinDist = std::max((float)nMinTotColorDist / s_nColorMaxDataRange_3ch, (float)nMinTotDescDist / s_nDescMaxDataRange_3ch); - fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; - fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; - fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT); - fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST); - if ((rand() % nCurrLocalWordUpdateRate) == 0) { - size_t nGlobalWordLUTIdx; - GlobalWord_3ch* pCurrGlobalWord = nullptr; - for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { - pCurrGlobalWord = (GlobalWord_3ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; - if (L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrTotDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR - && cmixdist<3>(anCurrColor, pCurrGlobalWord->oFeature.anColor) <= nCurrTotColorDistThreshold) - break; - } - if (nGlobalWordLUTIdx != m_nCurrGlobalWords || (rand() % (nCurrLocalWordUpdateRate * 2)) == 0) { - if (nGlobalWordLUTIdx == m_nCurrGlobalWords) { - pCurrGlobalWord = (GlobalWord_3ch*)m_apGlobalWordDict[m_nCurrGlobalWords - 1]; - for (size_t c = 0; c < 3; ++c) { - pCurrGlobalWord->oFeature.anColor[c] = anCurrColor[c]; - pCurrGlobalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; + + void BackgroundSubtractorPAWCS::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)); + // @@@@@@ TO BE REWRITTEN FOR WORD-BASED RECONSTRUCTION + /*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 nDescIter = m_voBGDescSamples[n].step.p[0]*y + m_voBGDescSamples[n].step.p[1]*x; + const size_t nFloatIter = nDescIter*2; + float* oAvgBgDescPtr = (float*)(oAvgBGDesc.data+nFloatIter); + const ushort* const oBGDescPtr = (ushort*)(m_voBGDescSamples[n].data+nDescIter); + for(size_t c=0; c<m_nImgChannels; ++c) + oAvgBgDescPtr[c] += ((float)oBGDescPtr[c])/m_voBGDescSamples.size(); + } } - pCurrGlobalWord->nDescBITS = nCurrIntraDescBITS; - pCurrGlobalWord->oSpatioOccMap = cv::Scalar(0.0f); - pCurrGlobalWord->fLatestWeight = 0.0f; - } - float& fCurrGlobalWordLocalWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fCurrGlobalWordLocalWeight < fPotentialLocalWordsWeightSum) { - pCurrGlobalWord->fLatestWeight += fPotentialLocalWordsWeightSum; - fCurrGlobalWordLocalWeight += fPotentialLocalWordsWeightSum; - } - } - } + }*/ + oAvgBGDesc.convertTo(backgroundDescImage, CV_16U); } - else { - // == foreground - const float fNormalizedMinDist = std::max(std::max((float)nMinTotColorDist / s_nColorMaxDataRange_3ch, (float)nMinTotDescDist / s_nDescMaxDataRange_3ch), (fLocalWordsWeightSumThreshold - fPotentialLocalWordsWeightSum) / fLocalWordsWeightSumThreshold); - fCurrMeanMinDist_LT = fCurrMeanMinDist_LT*(1.0f - fRollAvgFactor_LT) + fNormalizedMinDist*fRollAvgFactor_LT; - fCurrMeanMinDist_ST = fCurrMeanMinDist_ST*(1.0f - fRollAvgFactor_ST) + fNormalizedMinDist*fRollAvgFactor_ST; - fCurrMeanRawSegmRes_LT = fCurrMeanRawSegmRes_LT*(1.0f - fRollAvgFactor_LT) + fRollAvgFactor_LT; - fCurrMeanRawSegmRes_ST = fCurrMeanRawSegmRes_ST*(1.0f - fRollAvgFactor_ST) + fRollAvgFactor_ST; - if (bCurrRegionIsFlat || (rand() % nCurrLocalWordUpdateRate) == 0) { - size_t nGlobalWordLUTIdx; - GlobalWord_3ch* pCurrGlobalWord; - for (nGlobalWordLUTIdx = 0; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { - pCurrGlobalWord = (GlobalWord_3ch*)m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]; - if (L1dist(nCurrIntraDescBITS, pCurrGlobalWord->nDescBITS) <= nCurrTotDescDistThreshold / GWORD_DESC_THRES_BITS_MATCH_FACTOR - && cmixdist<3>(anCurrColor, pCurrGlobalWord->oFeature.anColor) <= nCurrTotColorDistThreshold) - break; - } - if (nGlobalWordLUTIdx == m_nCurrGlobalWords) - nCurrRegionSegmVal = UCHAR_MAX; - else { - const float fGlobalWordLocalizedWeight = *(float*)(pCurrGlobalWord->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fPotentialLocalWordsWeightSum + fGlobalWordLocalizedWeight / (bCurrRegionIsFlat ? 2 : 4) < fLocalWordsWeightSumThreshold) - nCurrRegionSegmVal = UCHAR_MAX; - } + + void BackgroundSubtractorPAWCS::CleanupDictionaries() { + if (m_aLocalWordList_1ch) { + delete[] m_aLocalWordList_1ch; + m_aLocalWordList_1ch = nullptr; + m_pLocalWordListIter_1ch = nullptr; } - else - nCurrRegionSegmVal = UCHAR_MAX; - if (fPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { - const size_t nNewLocalWordIdx = m_nCurrLocalWords - 1; - LocalWord_3ch* pNewLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nNewLocalWordIdx]; - for (size_t c = 0; c < 3; ++c) { - pNewLocalWord->oFeature.anColor[c] = anCurrColor[c]; - pNewLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; - } - pNewLocalWord->nOccurrences = nCurrWordOccIncr; - pNewLocalWord->nFirstOcc = m_nFrameIndex; - pNewLocalWord->nLastOcc = m_nFrameIndex; + else if (m_aLocalWordList_3ch) { + delete[] m_aLocalWordList_3ch; + m_aLocalWordList_3ch = nullptr; + m_pLocalWordListIter_3ch = nullptr; } - } - // == neighb updt - if ((!nCurrRegionSegmVal && (rand() % nCurrLocalWordUpdateRate) == 0) || bCurrRegionIsROIBorder || m_bUsingMovingCamera) { - //if((!nCurrRegionSegmVal && (rand()%(nCurrRegionIllumUpdtVal?(nCurrLocalWordUpdateRate/2+1):nCurrLocalWordUpdateRate))==0) || bCurrRegionIsROIBorder) { - int nSampleImgCoord_Y, nSampleImgCoord_X; - if (bCurrRegionIsFlat || bCurrRegionIsROIBorder || m_bUsingMovingCamera) - getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); - else - getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP_::PATCH_SIZE / 2, m_oImgSize); - const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - if (m_oROI.data[nSamplePxIdx]) { - const size_t nNeighborLocalDictIdx = m_aPxInfoLUT_PAWCS[nSamplePxIdx].nModelIdx*m_nCurrLocalWords; - size_t nNeighborLocalWordIdx = 0; - float fNeighborPotentialLocalWordsWeightSum = 0.0f; - while (nNeighborLocalWordIdx < m_nCurrLocalWords && fNeighborPotentialLocalWordsWeightSum < fLocalWordsWeightSumThreshold) { - LocalWord_3ch* pNeighborLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; - const size_t nNeighborTotColorL1Dist = L1dist<3>(anCurrColor, pNeighborLocalWord->oFeature.anColor); - const size_t nNeighborColorDistortion = cdist<3>(anCurrColor, pNeighborLocalWord->oFeature.anColor); - const size_t nNeighborTotColorMixDist = cmixdist(nNeighborTotColorL1Dist, nNeighborColorDistortion); - const size_t nNeighborTotIntraDescDist = hdist<3>(anCurrIntraDesc, pNeighborLocalWord->oFeature.anDesc); - const bool bNeighborRegionIsFlat = popcount<3>(pNeighborLocalWord->oFeature.anDesc) < FLAT_REGION_BIT_COUNT * 2; - const size_t nNeighborWordOccIncr = bNeighborRegionIsFlat ? nCurrWordOccIncr * 2 : nCurrWordOccIncr; - if (nNeighborTotColorMixDist <= nCurrTotColorDistThreshold && nNeighborTotIntraDescDist <= nCurrTotDescDistThreshold) { - const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; - if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; - } - else if (!oCurrFGMask.data[nSamplePxIdx] && bCurrRegionIsFlat && (bBootstrapping || (rand() % nCurrLocalWordUpdateRate) == 0)) { - const size_t nSamplePxRGBIdx = nSamplePxIdx * 3; - const size_t nSampleDescRGBIdx = nSamplePxRGBIdx * 2; - ushort* anNeighborLastIntraDesc = ((ushort*)(m_oLastDescFrame.data + nSampleDescRGBIdx)); - const size_t nNeighborTotLastIntraDescDist = hdist<3>(anCurrIntraDesc, anNeighborLastIntraDesc); - if (nNeighborTotColorMixDist <= nCurrTotColorDistThreshold && nNeighborTotLastIntraDescDist <= nCurrTotDescDistThreshold / 2) { - const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; - if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; - for (size_t c = 0; c < 3; ++c) - pNeighborLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; - } - else { - const bool bNeighborLastRegionIsFlat = popcount<3>(anNeighborLastIntraDesc) < FLAT_REGION_BIT_COUNT * 2; - if (bNeighborLastRegionIsFlat && bCurrRegionIsFlat && - nNeighborTotLastIntraDescDist + nNeighborTotIntraDescDist <= nCurrTotDescDistThreshold && - nNeighborColorDistortion <= nCurrTotColorDistThreshold / 4) { - const float fNeighborLocalWordWeight = GetLocalWordWeight(pNeighborLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - fNeighborPotentialLocalWordsWeightSum += fNeighborLocalWordWeight; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; - if (fNeighborLocalWordWeight < DEFAULT_LWORD_MAX_WEIGHT) - pNeighborLocalWord->nOccurrences += nNeighborWordOccIncr; - for (size_t c = 0; c < 3; ++c) - pNeighborLocalWord->oFeature.anColor[c] = anCurrColor[c]; - } - } - } - ++nNeighborLocalWordIdx; - } - if (fNeighborPotentialLocalWordsWeightSum < DEFAULT_LWORD_INIT_WEIGHT) { - nNeighborLocalWordIdx = m_nCurrLocalWords - 1; - LocalWord_3ch* pNeighborLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nNeighborLocalDictIdx + nNeighborLocalWordIdx]; - for (size_t c = 0; c < 3; ++c) { - pNeighborLocalWord->oFeature.anColor[c] = anCurrColor[c]; - pNeighborLocalWord->oFeature.anDesc[c] = anCurrIntraDesc[c]; - } - pNeighborLocalWord->nOccurrences = nCurrWordOccIncr; - pNeighborLocalWord->nFirstOcc = m_nFrameIndex; - pNeighborLocalWord->nLastOcc = m_nFrameIndex; - } + if (m_apLocalWordDict) { + delete[] m_apLocalWordDict; + m_apLocalWordDict = nullptr; + } + if (m_aGlobalWordList_1ch) { + delete[] m_aGlobalWordList_1ch; + m_aGlobalWordList_1ch = nullptr; + m_pGlobalWordListIter_1ch = nullptr; + } + else if (m_aGlobalWordList_3ch) { + delete[] m_aGlobalWordList_3ch; + m_aGlobalWordList_3ch = nullptr; + m_pGlobalWordListIter_3ch = nullptr; + } + if (m_apGlobalWordDict) { + delete[] m_apGlobalWordDict; + m_apGlobalWordDict = nullptr; + } + if (m_aPxInfoLUT_PAWCS) { + for (size_t nPxIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) + delete[] m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT; + delete[] m_aPxInfoLUT_PAWCS; + m_aPxInfoLUT = nullptr; + m_aPxInfoLUT_PAWCS = nullptr; + } + if (m_aPxIdxLUT) { + delete[] m_aPxIdxLUT; + m_aPxIdxLUT = nullptr; } } - if (nCurrRegionIllumUpdtVal) - nCurrRegionIllumUpdtVal -= 1; - // == feedback adj - bCurrRegionIsUnstable = fCurrDistThresholdFactor > UNSTABLE_REG_RDIST_MIN || (fCurrMeanRawSegmRes_LT - fCurrMeanFinalSegmRes_LT) > UNSTABLE_REG_RATIO_MIN || (fCurrMeanRawSegmRes_ST - fCurrMeanFinalSegmRes_ST) > UNSTABLE_REG_RATIO_MIN; - if (m_oLastFGMask.data[nPxIter] || (std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && nCurrRegionSegmVal)) - fCurrLearningRate = std::min(fCurrLearningRate + FEEDBACK_T_INCR / (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST)*fCurrDistThresholdVariationFactor), FEEDBACK_T_UPPER); - else - fCurrLearningRate = std::max(fCurrLearningRate - FEEDBACK_T_DECR*fCurrDistThresholdVariationFactor / std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST), FEEDBACK_T_LOWER); - if (std::max(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) > UNSTABLE_REG_RATIO_MIN && m_oBlinksFrame.data[nPxIter]) - (fCurrDistThresholdVariationFactor) += bBootstrapping ? FEEDBACK_V_INCR * 2 : FEEDBACK_V_INCR; - else - fCurrDistThresholdVariationFactor = std::max(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR*((bBootstrapping || bCurrRegionIsFlat) ? 2 : m_oLastFGMask.data[nPxIter] ? 0.5f : 1), FEEDBACK_V_DECR); - if (fCurrDistThresholdFactor < std::pow(1.0f + std::min(fCurrMeanMinDist_LT, fCurrMeanMinDist_ST) * 2, 2)) - fCurrDistThresholdFactor += FEEDBACK_R_VAR*(fCurrDistThresholdVariationFactor - FEEDBACK_V_DECR); - else - fCurrDistThresholdFactor = std::max(fCurrDistThresholdFactor - FEEDBACK_R_VAR / fCurrDistThresholdVariationFactor, 1.0f); - for (size_t c = 0; c < 3; ++c) { - anLastIntraDesc[c] = anCurrIntraDesc[c]; - anLastColor[c] = anCurrColor[c]; - } - } - } - const bool bRecalcGlobalWords = !(m_nFrameIndex % (nCurrGlobalWordUpdateRate << 5)); - const bool bUpdateGlobalWords = !(m_nFrameIndex % (nCurrGlobalWordUpdateRate)); - cv::Mat oLastFGMask_dilated_inverted_downscaled; - if (bUpdateGlobalWords) - cv::resize(m_oLastFGMask_dilated_inverted, oLastFGMask_dilated_inverted_downscaled, m_oDownSampledFrameSize_GlobalWordLookup, 0, 0, cv::INTER_NEAREST); - for (size_t nGlobalWordIdx = 0; nGlobalWordIdx < m_nCurrGlobalWords; ++nGlobalWordIdx) { - if (bRecalcGlobalWords && m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > 0.0f) { - m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight = GetGlobalWordWeight(m_apGlobalWordDict[nGlobalWordIdx]); - if (m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight < 1.0f) { - m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight = 0.0f; - m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap = cv::Scalar(0.0f); - } - } - if (bUpdateGlobalWords && m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > 0.0f) { - cv::accumulateProduct(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, m_oTempGlobalWordWeightDiffFactor, m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, oLastFGMask_dilated_inverted_downscaled); - m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight *= 0.9f; - cv::blur(m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, m_apGlobalWordDict[nGlobalWordIdx]->oSpatioOccMap, cv::Size(3, 3), cv::Point(-1, -1), cv::BORDER_REPLICATE); - } - if (nGlobalWordIdx > 0 && m_apGlobalWordDict[nGlobalWordIdx]->fLatestWeight > m_apGlobalWordDict[nGlobalWordIdx - 1]->fLatestWeight) - std::swap(m_apGlobalWordDict[nGlobalWordIdx], m_apGlobalWordDict[nGlobalWordIdx - 1]); - } - if (bUpdateGlobalWords) { - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const size_t nGlobalWordMapLookupIdx = m_aPxInfoLUT_PAWCS[nPxIter].nGlobalWordMapLookupIdx; - float fLastGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[0]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - for (size_t nGlobalWordLUTIdx = 1; nGlobalWordLUTIdx < m_nCurrGlobalWords; ++nGlobalWordLUTIdx) { - const float fCurrGlobalWordLocalWeight = *(float*)(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx]->oSpatioOccMap.data + nGlobalWordMapLookupIdx); - if (fCurrGlobalWordLocalWeight > fLastGlobalWordLocalWeight) - std::swap(m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx], m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT[nGlobalWordLUTIdx - 1]); - else - fLastGlobalWordLocalWeight = fCurrGlobalWordLocalWeight; - } - } - } - cv::bitwise_xor(oCurrFGMask, m_oLastRawFGMask, m_oCurrRawFGBlinkMask); - cv::bitwise_or(m_oCurrRawFGBlinkMask, m_oLastRawFGBlinkMask, m_oBlinksFrame); - m_oCurrRawFGBlinkMask.copyTo(m_oLastRawFGBlinkMask); - oCurrFGMask.copyTo(m_oLastRawFGMask); - cv::morphologyEx(oCurrFGMask, m_oFGMask_PreFlood, cv::MORPH_CLOSE, m_oMorphExStructElement); - m_oFGMask_PreFlood.copyTo(m_oFGMask_FloodedHoles); - cv::floodFill(m_oFGMask_FloodedHoles, cv::Point(0, 0), UCHAR_MAX); - cv::bitwise_not(m_oFGMask_FloodedHoles, m_oFGMask_FloodedHoles); - cv::erode(m_oFGMask_PreFlood, m_oFGMask_PreFlood, cv::Mat(), cv::Point(-1, -1), 3); - cv::bitwise_or(oCurrFGMask, m_oFGMask_FloodedHoles, oCurrFGMask); - cv::bitwise_or(oCurrFGMask, m_oFGMask_PreFlood, oCurrFGMask); - cv::medianBlur(oCurrFGMask, m_oLastFGMask, m_nMedianBlurKernelSize); - cv::dilate(m_oLastFGMask, m_oLastFGMask_dilated, cv::Mat(), cv::Point(-1, -1), 3); - cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); - cv::bitwise_not(m_oLastFGMask_dilated, m_oLastFGMask_dilated_inverted); - cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); - m_oLastFGMask.copyTo(oCurrFGMask); - cv::addWeighted(m_oMeanFinalSegmResFrame_LT, (1.0f - fRollAvgFactor_LT), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_LT, 0, m_oMeanFinalSegmResFrame_LT, CV_32F); - cv::addWeighted(m_oMeanFinalSegmResFrame_ST, (1.0f - fRollAvgFactor_ST), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_ST, 0, m_oMeanFinalSegmResFrame_ST, CV_32F); - const float fCurrNonFlatRegionRatio = (float)(m_nTotRelevantPxCount - nFlatRegionCount) / m_nTotRelevantPxCount; - if (fCurrNonFlatRegionRatio < LBSPDESC_RATIO_MIN && m_fLastNonFlatRegionRatio < LBSPDESC_RATIO_MIN) { - for (size_t t = 0; t <= UCHAR_MAX; ++t) - if (m_anLBSPThreshold_8bitLUT[t] > cv::saturate_cast<uchar>((m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold) / 4)) - --m_anLBSPThreshold_8bitLUT[t]; - } - else if (fCurrNonFlatRegionRatio > LBSPDESC_RATIO_MAX && m_fLastNonFlatRegionRatio > LBSPDESC_RATIO_MAX) { - for (size_t t = 0; t <= UCHAR_MAX; ++t) - if (m_anLBSPThreshold_8bitLUT[t] < cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + UCHAR_MAX*m_fRelLBSPThreshold)) - ++m_anLBSPThreshold_8bitLUT[t]; - } - m_fLastNonFlatRegionRatio = fCurrNonFlatRegionRatio; - cv::resize(oInputImg, m_oDownSampledFrame_MotionAnalysis, m_oDownSampledFrameSize_MotionAnalysis, 0, 0, cv::INTER_AREA); - cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, m_oMeanDownSampledLastDistFrame_LT, fRollAvgFactor_LT); - cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, m_oMeanDownSampledLastDistFrame_ST, fRollAvgFactor_ST); - const float fCurrMeanL1DistRatio = L1dist((float*)m_oMeanDownSampledLastDistFrame_LT.data, (float*)m_oMeanDownSampledLastDistFrame_ST.data, m_oMeanDownSampledLastDistFrame_LT.total(), m_nImgChannels, m_oDownSampledROI_MotionAnalysis.data) / m_nDownSampledROIPxCount; - if (!m_bAutoModelResetEnabled && fCurrMeanL1DistRatio >= FRAMELEVEL_MIN_L1DIST_THRES * 2) - m_bAutoModelResetEnabled = true; - if (m_bAutoModelResetEnabled || m_bUsingMovingCamera) { - if ((m_nFrameIndex%DEFAULT_BOOTSTRAP_WIN_SIZE) == 0) { - cv::Mat oCurrBackgroundImg, oDownSampledBackgroundImg; - getBackgroundImage(oCurrBackgroundImg); - cv::resize(oCurrBackgroundImg, oDownSampledBackgroundImg, m_oDownSampledFrameSize_MotionAnalysis, 0, 0, cv::INTER_AREA); - cv::Mat oDownSampledBackgroundImg_32F; oDownSampledBackgroundImg.convertTo(oDownSampledBackgroundImg_32F, CV_32F); - const float fCurrModelL1DistRatio = L1dist((float*)m_oMeanDownSampledLastDistFrame_LT.data, (float*)oDownSampledBackgroundImg_32F.data, m_oMeanDownSampledLastDistFrame_LT.total(), m_nImgChannels, cv::Mat(m_oDownSampledROI_MotionAnalysis == UCHAR_MAX).data) / m_nDownSampledROIPxCount; - const float fCurrModelCDistRatio = cdist((float*)m_oMeanDownSampledLastDistFrame_LT.data, (float*)oDownSampledBackgroundImg_32F.data, m_oMeanDownSampledLastDistFrame_LT.total(), m_nImgChannels, cv::Mat(m_oDownSampledROI_MotionAnalysis == UCHAR_MAX).data) / m_nDownSampledROIPxCount; - if (m_bUsingMovingCamera && fCurrModelL1DistRatio < FRAMELEVEL_MIN_L1DIST_THRES / 4 && fCurrModelCDistRatio < FRAMELEVEL_MIN_CDIST_THRES / 4) { - m_nLocalWordWeightOffset = DEFAULT_LWORD_WEIGHT_OFFSET; - m_bUsingMovingCamera = false; - refreshModel(1, 1, true); - } - else if (bBootstrapping && !m_bUsingMovingCamera && (fCurrModelL1DistRatio >= FRAMELEVEL_MIN_L1DIST_THRES || fCurrModelCDistRatio >= FRAMELEVEL_MIN_CDIST_THRES)) { - m_nLocalWordWeightOffset = 5; - m_bUsingMovingCamera = true; - refreshModel(1, 1, true); - } - } - if (m_nFramesSinceLastReset > DEFAULT_BOOTSTRAP_WIN_SIZE * 2) - m_bAutoModelResetEnabled = false; - else if (fCurrMeanL1DistRatio >= FRAMELEVEL_MIN_L1DIST_THRES && m_nModelResetCooldown == 0) { - m_nFramesSinceLastReset = 0; - refreshModel(m_nLocalWordWeightOffset / 8, 0, true); - m_nModelResetCooldown = nCurrSamplesForMovingAvg_ST; - m_oUpdateRateFrame = cv::Scalar(1.0f); - } - else if (!bBootstrapping) - ++m_nFramesSinceLastReset; - } - if (m_nModelResetCooldown > 0) - --m_nModelResetCooldown; -} -void BackgroundSubtractorPAWCS::getBackgroundImage(cv::OutputArray backgroundImage) const { // @@@ add option to reconstruct from gwords? - CV_Assert(m_bInitialized); - cv::Mat oAvgBGImg = cv::Mat::zeros(m_oImgSize, CV_32FC((int)m_nImgChannels)); - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const size_t nLocalDictIdx = nModelIter*m_nCurrLocalWords; - const int nCurrImgCoord_X = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_X; - const int nCurrImgCoord_Y = m_aPxInfoLUT_PAWCS[nPxIter].nImgCoord_Y; - if (m_nImgChannels == 1) { - float fTotWeight = 0.0f; - float fTotColor = 0.0f; - for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - LocalWord_1ch* pCurrLocalWord = (LocalWord_1ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - float fCurrWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - fTotColor += (float)pCurrLocalWord->oFeature.anColor[0] * fCurrWeight; - fTotWeight += fCurrWeight; + float BackgroundSubtractorPAWCS::GetLocalWordWeight(const LocalWordBase* w, size_t nCurrFrame, size_t nOffset) { + return (float)(w->nOccurrences) / ((w->nLastOcc - w->nFirstOcc) + (nCurrFrame - w->nLastOcc) * 2 + nOffset); } - oAvgBGImg.at<float>(nCurrImgCoord_Y, nCurrImgCoord_X) = fTotColor / fTotWeight; - } - else { //m_nImgChannels==3 - float fTotWeight = 0.0f; - float fTotColor[3] = { 0.0f,0.0f,0.0f }; - for (size_t nLocalWordIdx = 0; nLocalWordIdx < m_nCurrLocalWords; ++nLocalWordIdx) { - LocalWord_3ch* pCurrLocalWord = (LocalWord_3ch*)m_apLocalWordDict[nLocalDictIdx + nLocalWordIdx]; - float fCurrWeight = GetLocalWordWeight(pCurrLocalWord, m_nFrameIndex, m_nLocalWordWeightOffset); - for (size_t c = 0; c < 3; ++c) - fTotColor[c] += (float)pCurrLocalWord->oFeature.anColor[c] * fCurrWeight; - fTotWeight += fCurrWeight; + + float BackgroundSubtractorPAWCS::GetGlobalWordWeight(const GlobalWordBase* w) { + return (float)cv::sum(w->oSpatioOccMap).val[0]; } - oAvgBGImg.at<cv::Vec3f>(nCurrImgCoord_Y, nCurrImgCoord_X) = cv::Vec3f(fTotColor[0] / fTotWeight, fTotColor[1] / fTotWeight, fTotColor[2] / fTotWeight); } } - oAvgBGImg.convertTo(backgroundImage, CV_8U); -} - -void BackgroundSubtractorPAWCS::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)); - // @@@@@@ TO BE REWRITTEN FOR WORD-BASED RECONSTRUCTION - /*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 nDescIter = m_voBGDescSamples[n].step.p[0]*y + m_voBGDescSamples[n].step.p[1]*x; - const size_t nFloatIter = nDescIter*2; - float* oAvgBgDescPtr = (float*)(oAvgBGDesc.data+nFloatIter); - const ushort* const oBGDescPtr = (ushort*)(m_voBGDescSamples[n].data+nDescIter); - for(size_t c=0; c<m_nImgChannels; ++c) - oAvgBgDescPtr[c] += ((float)oBGDescPtr[c])/m_voBGDescSamples.size(); - } - } - }*/ - oAvgBGDesc.convertTo(backgroundDescImage, CV_16U); -} - -void BackgroundSubtractorPAWCS::CleanupDictionaries() { - if (m_aLocalWordList_1ch) { - delete[] m_aLocalWordList_1ch; - m_aLocalWordList_1ch = nullptr; - m_pLocalWordListIter_1ch = nullptr; - } - else if (m_aLocalWordList_3ch) { - delete[] m_aLocalWordList_3ch; - m_aLocalWordList_3ch = nullptr; - m_pLocalWordListIter_3ch = nullptr; - } - if (m_apLocalWordDict) { - delete[] m_apLocalWordDict; - m_apLocalWordDict = nullptr; - } - if (m_aGlobalWordList_1ch) { - delete[] m_aGlobalWordList_1ch; - m_aGlobalWordList_1ch = nullptr; - m_pGlobalWordListIter_1ch = nullptr; - } - else if (m_aGlobalWordList_3ch) { - delete[] m_aGlobalWordList_3ch; - m_aGlobalWordList_3ch = nullptr; - m_pGlobalWordListIter_3ch = nullptr; - } - if (m_apGlobalWordDict) { - delete[] m_apGlobalWordDict; - m_apGlobalWordDict = nullptr; - } - if (m_aPxInfoLUT_PAWCS) { - for (size_t nPxIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) - delete[] m_aPxInfoLUT_PAWCS[nPxIter].apGlobalDictSortLUT; - delete[] m_aPxInfoLUT_PAWCS; - m_aPxInfoLUT = nullptr; - m_aPxInfoLUT_PAWCS = nullptr; - } - if (m_aPxIdxLUT) { - delete[] m_aPxIdxLUT; - m_aPxIdxLUT = nullptr; - } -} - -float BackgroundSubtractorPAWCS::GetLocalWordWeight(const LocalWordBase* w, size_t nCurrFrame, size_t nOffset) { - return (float)(w->nOccurrences) / ((w->nLastOcc - w->nFirstOcc) + (nCurrFrame - w->nLastOcc) * 2 + nOffset); -} - -float BackgroundSubtractorPAWCS::GetGlobalWordWeight(const GlobalWordBase* w) { - return (float)cv::sum(w->oSpatioOccMap).val[0]; } diff --git a/src/algorithms/LBSP/BackgroundSubtractorPAWCS.h b/src/algorithms/LBSP/BackgroundSubtractorPAWCS.h index b32d5b786a64028a028f6a2cd9a7048446c50100..e81736d37618108540ae33033b971932c2907fb4 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorPAWCS.h +++ b/src/algorithms/LBSP/BackgroundSubtractorPAWCS.h @@ -2,152 +2,161 @@ #include "BackgroundSubtractorLBSP_.h" -//! defines the default value for BackgroundSubtractorLBSP_::m_fRelLBSPThreshold -#define BGSPAWCS_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD (0.333f) -//! defines the default value for BackgroundSubtractorPAWCS::m_nDescDistThresholdOffset -#define BGSPAWCS_DEFAULT_DESC_DIST_THRESHOLD_OFFSET (2) -//! defines the default value for BackgroundSubtractorPAWCS::m_nMinColorDistThreshold -#define BGSPAWCS_DEFAULT_MIN_COLOR_DIST_THRESHOLD (20) -//! defines the default value for BackgroundSubtractorPAWCS::m_nMaxLocalWords and m_nMaxGlobalWords -#define BGSPAWCS_DEFAULT_MAX_NB_WORDS (50) -//! defines the default value for BackgroundSubtractorPAWCS::m_nSamplesForMovingAvgs -#define BGSPAWCS_DEFAULT_N_SAMPLES_FOR_MV_AVGS (100) +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + //! defines the default value for BackgroundSubtractorLBSP_::m_fRelLBSPThreshold + const float BGSPAWCS_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD = 0.333f; + //! defines the default value for BackgroundSubtractorPAWCS::m_nDescDistThresholdOffset + const int BGSPAWCS_DEFAULT_DESC_DIST_THRESHOLD_OFFSET = 2; + //! defines the default value for BackgroundSubtractorPAWCS::m_nMinColorDistThreshold + const int BGSPAWCS_DEFAULT_MIN_COLOR_DIST_THRESHOLD = 20; + //! defines the default value for BackgroundSubtractorPAWCS::m_nMaxLocalWords and m_nMaxGlobalWords + const int BGSPAWCS_DEFAULT_MAX_NB_WORDS = 50; + //! defines the default value for BackgroundSubtractorPAWCS::m_nSamplesForMovingAvgs + const int BGSPAWCS_DEFAULT_N_SAMPLES_FOR_MV_AVGS = 100; -/*! - Pixel-based Adaptive Word Consensus Segmenter (PAWCS) change detection algorithm. + /*! + Pixel-based Adaptive Word Consensus Segmenter (PAWCS) change detection 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. + 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 parameters or on the algorithm itself, see P.-L. St-Charles et al., - "A Self-Adjusting Approach to Change Detection Based on Background Word Consensus", in WACV 2015. + For more details on the different parameters or on the algorithm itself, see P.-L. St-Charles et al., + "A Self-Adjusting Approach to Change Detection Based on Background Word Consensus", in WACV 2015. - This algorithm is currently NOT thread-safe. - */ -class BackgroundSubtractorPAWCS : public BackgroundSubtractorLBSP_ { -public: - //! full constructor - BackgroundSubtractorPAWCS(float fRelLBSPThreshold = BGSPAWCS_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, - size_t nDescDistThresholdOffset = BGSPAWCS_DEFAULT_DESC_DIST_THRESHOLD_OFFSET, - size_t nMinColorDistThreshold = BGSPAWCS_DEFAULT_MIN_COLOR_DIST_THRESHOLD, - size_t nMaxNbWords = BGSPAWCS_DEFAULT_MAX_NB_WORDS, - size_t nSamplesForMovingAvgs = BGSPAWCS_DEFAULT_N_SAMPLES_FOR_MV_AVGS); - //! default destructor - virtual ~BackgroundSubtractorPAWCS(); - //! (re)initiaization method; needs to be called before starting background subtraction - virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI); - //! refreshes all local (+ global) dictionaries based on the last analyzed frame - virtual void refreshModel(size_t nBaseOccCount, float fOccDecrFrac, bool bForceFGUpdate = false); - //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) - virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = 0); - //! returns a copy of the latest reconstructed background image - virtual void getBackgroundImage(cv::OutputArray backgroundImage) const; - //! returns a copy of the latest reconstructed background descriptors image - virtual void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; + This algorithm is currently NOT thread-safe. + */ + class BackgroundSubtractorPAWCS : public BackgroundSubtractorLBSP_ { + public: + //! full constructor + BackgroundSubtractorPAWCS(float fRelLBSPThreshold = BGSPAWCS_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, + size_t nDescDistThresholdOffset = BGSPAWCS_DEFAULT_DESC_DIST_THRESHOLD_OFFSET, + size_t nMinColorDistThreshold = BGSPAWCS_DEFAULT_MIN_COLOR_DIST_THRESHOLD, + size_t nMaxNbWords = BGSPAWCS_DEFAULT_MAX_NB_WORDS, + size_t nSamplesForMovingAvgs = BGSPAWCS_DEFAULT_N_SAMPLES_FOR_MV_AVGS); + //! default destructor + virtual ~BackgroundSubtractorPAWCS(); + //! (re)initiaization method; needs to be called before starting background subtraction + virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI); + //! refreshes all local (+ global) dictionaries based on the last analyzed frame + virtual void refreshModel(size_t nBaseOccCount, float fOccDecrFrac, bool bForceFGUpdate = false); + //! primary model update function; the learning param is used to override the internal learning speed (ignored when <= 0) + virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = 0); + //! returns a copy of the latest reconstructed background image + virtual void getBackgroundImage(cv::OutputArray backgroundImage) const; + //! returns a copy of the latest reconstructed background descriptors image + virtual void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; -protected: - template<size_t nChannels> - struct ColorLBSPFeature { - uchar anColor[nChannels]; - ushort anDesc[nChannels]; - }; - struct LocalWordBase { - size_t nFirstOcc; - size_t nLastOcc; - size_t nOccurrences; - }; - template<typename T> - struct LocalWord : LocalWordBase { - T oFeature; - }; - struct GlobalWordBase { - float fLatestWeight; - cv::Mat oSpatioOccMap; - uchar nDescBITS; - }; - template<typename T> - struct GlobalWord : GlobalWordBase { - T oFeature; - }; - typedef LocalWord<ColorLBSPFeature<1>> LocalWord_1ch; - typedef LocalWord<ColorLBSPFeature<3>> LocalWord_3ch; - typedef GlobalWord<ColorLBSPFeature<1>> GlobalWord_1ch; - typedef GlobalWord<ColorLBSPFeature<3>> GlobalWord_3ch; - struct PxInfo_PAWCS : PxInfoBase { - size_t nGlobalWordMapLookupIdx; - GlobalWordBase** apGlobalDictSortLUT; - }; - //! absolute minimal color distance threshold ('R' or 'radius' in the original ViBe paper, used as the default/initial 'R(x)' value here) - const size_t m_nMinColorDistThreshold; - //! absolute descriptor distance threshold offset - const size_t m_nDescDistThresholdOffset; - //! max/curr number of local words used to build background submodels (for a single pixel, similar to 'N' in ViBe/PBAS, may vary based on img/channel size) - size_t m_nMaxLocalWords, m_nCurrLocalWords; - //! max/curr number of global words used to build the global background model (may vary based on img/channel size) - size_t m_nMaxGlobalWords, m_nCurrGlobalWords; - //! number of samples to use to compute the learning rate of moving averages - const size_t m_nSamplesForMovingAvgs; - //! last calculated non-flat region ratio - float m_fLastNonFlatRegionRatio; - //! current kernel size for median blur post-proc filtering - int m_nMedianBlurKernelSize; - //! specifies the downsampled frame size used for cam motion analysis & gword lookup maps - cv::Size m_oDownSampledFrameSize_MotionAnalysis, m_oDownSampledFrameSize_GlobalWordLookup; - //! downsampled version of the ROI used for cam motion analysis - cv::Mat m_oDownSampledROI_MotionAnalysis; - //! total pixel count for the downsampled ROIs - size_t m_nDownSampledROIPxCount; - //! current local word weight offset - size_t m_nLocalWordWeightOffset; + protected: + template<size_t nChannels> + struct ColorLBSPFeature { + uchar anColor[nChannels]; + ushort anDesc[nChannels]; + }; + struct LocalWordBase { + size_t nFirstOcc; + size_t nLastOcc; + size_t nOccurrences; + }; + template<typename T> + struct LocalWord : LocalWordBase { + T oFeature; + }; + struct GlobalWordBase { + float fLatestWeight; + cv::Mat oSpatioOccMap; + uchar nDescBITS; + }; + template<typename T> + struct GlobalWord : GlobalWordBase { + T oFeature; + }; + typedef LocalWord<ColorLBSPFeature<1>> LocalWord_1ch; + typedef LocalWord<ColorLBSPFeature<3>> LocalWord_3ch; + typedef GlobalWord<ColorLBSPFeature<1>> GlobalWord_1ch; + typedef GlobalWord<ColorLBSPFeature<3>> GlobalWord_3ch; + struct PxInfo_PAWCS : PxInfoBase { + size_t nGlobalWordMapLookupIdx; + GlobalWordBase** apGlobalDictSortLUT; + }; + //! absolute minimal color distance threshold ('R' or 'radius' in the original ViBe paper, used as the default/initial 'R(x)' value here) + const size_t m_nMinColorDistThreshold; + //! absolute descriptor distance threshold offset + const size_t m_nDescDistThresholdOffset; + //! max/curr number of local words used to build background submodels (for a single pixel, similar to 'N' in ViBe/PBAS, may vary based on img/channel size) + size_t m_nMaxLocalWords, m_nCurrLocalWords; + //! max/curr number of global words used to build the global background model (may vary based on img/channel size) + size_t m_nMaxGlobalWords, m_nCurrGlobalWords; + //! number of samples to use to compute the learning rate of moving averages + const size_t m_nSamplesForMovingAvgs; + //! last calculated non-flat region ratio + float m_fLastNonFlatRegionRatio; + //! current kernel size for median blur post-proc filtering + int m_nMedianBlurKernelSize; + //! specifies the downsampled frame size used for cam motion analysis & gword lookup maps + cv::Size m_oDownSampledFrameSize_MotionAnalysis, m_oDownSampledFrameSize_GlobalWordLookup; + //! downsampled version of the ROI used for cam motion analysis + cv::Mat m_oDownSampledROI_MotionAnalysis; + //! total pixel count for the downsampled ROIs + size_t m_nDownSampledROIPxCount; + //! current local word weight offset + size_t m_nLocalWordWeightOffset; - //! word lists & dictionaries - LocalWordBase** m_apLocalWordDict; - LocalWord_1ch* m_aLocalWordList_1ch, *m_pLocalWordListIter_1ch; - LocalWord_3ch* m_aLocalWordList_3ch, *m_pLocalWordListIter_3ch; - GlobalWordBase** m_apGlobalWordDict; - GlobalWord_1ch* m_aGlobalWordList_1ch, *m_pGlobalWordListIter_1ch; - GlobalWord_3ch* m_aGlobalWordList_3ch, *m_pGlobalWordListIter_3ch; - PxInfo_PAWCS* m_aPxInfoLUT_PAWCS; + //! word lists & dictionaries + LocalWordBase** m_apLocalWordDict; + LocalWord_1ch* m_aLocalWordList_1ch, *m_pLocalWordListIter_1ch; + LocalWord_3ch* m_aLocalWordList_3ch, *m_pLocalWordListIter_3ch; + GlobalWordBase** m_apGlobalWordDict; + GlobalWord_1ch* m_aGlobalWordList_1ch, *m_pGlobalWordListIter_1ch; + GlobalWord_3ch* m_aGlobalWordList_3ch, *m_pGlobalWordListIter_3ch; + PxInfo_PAWCS* m_aPxInfoLUT_PAWCS; - //! a lookup map used to keep track of regions where illumination recently changed - cv::Mat m_oIllumUpdtRegionMask; - //! per-pixel update rates ('T(x)' in PBAS, which contains pixel-level 'sigmas', as referred to in ViBe) - cv::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) - cv::Mat m_oDistThresholdFrame; - //! per-pixel distance threshold variation modulators ('v(x)', relative value used to modulate 'R(x)' and 'T(x)' variations) - cv::Mat m_oDistThresholdVariationFrame; - //! 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)') - cv::Mat m_oMeanMinDistFrame_LT, m_oMeanMinDistFrame_ST; - //! per-pixel mean downsampled distances between consecutive frames (used to analyze camera movement and force global model resets automatically) - cv::Mat m_oMeanDownSampledLastDistFrame_LT, m_oMeanDownSampledLastDistFrame_ST; - //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) - cv::Mat m_oMeanRawSegmResFrame_LT, m_oMeanRawSegmResFrame_ST; - //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) - cv::Mat m_oMeanFinalSegmResFrame_LT, m_oMeanFinalSegmResFrame_ST; - //! a lookup map used to keep track of unstable regions (based on segm. noise & local dist. thresholds) - cv::Mat m_oUnstableRegionMask; - //! per-pixel blink detection map ('Z(x)') - cv::Mat m_oBlinksFrame; - //! pre-allocated matrix used to downsample the input frame when needed - cv::Mat m_oDownSampledFrame_MotionAnalysis; - //! the foreground mask generated by the method at [t-1] (without post-proc, used for blinking px detection) - cv::Mat m_oLastRawFGMask; + //! a lookup map used to keep track of regions where illumination recently changed + cv::Mat m_oIllumUpdtRegionMask; + //! per-pixel update rates ('T(x)' in PBAS, which contains pixel-level 'sigmas', as referred to in ViBe) + cv::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) + cv::Mat m_oDistThresholdFrame; + //! per-pixel distance threshold variation modulators ('v(x)', relative value used to modulate 'R(x)' and 'T(x)' variations) + cv::Mat m_oDistThresholdVariationFrame; + //! 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)') + cv::Mat m_oMeanMinDistFrame_LT, m_oMeanMinDistFrame_ST; + //! per-pixel mean downsampled distances between consecutive frames (used to analyze camera movement and force global model resets automatically) + cv::Mat m_oMeanDownSampledLastDistFrame_LT, m_oMeanDownSampledLastDistFrame_ST; + //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) + cv::Mat m_oMeanRawSegmResFrame_LT, m_oMeanRawSegmResFrame_ST; + //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) + cv::Mat m_oMeanFinalSegmResFrame_LT, m_oMeanFinalSegmResFrame_ST; + //! a lookup map used to keep track of unstable regions (based on segm. noise & local dist. thresholds) + cv::Mat m_oUnstableRegionMask; + //! per-pixel blink detection map ('Z(x)') + cv::Mat m_oBlinksFrame; + //! pre-allocated matrix used to downsample the input frame when needed + cv::Mat m_oDownSampledFrame_MotionAnalysis; + //! the foreground mask generated by the method at [t-1] (without post-proc, used for blinking px detection) + cv::Mat m_oLastRawFGMask; - //! pre-allocated CV_8UC1 matrices used to speed up morph ops - cv::Mat m_oFGMask_PreFlood; - cv::Mat m_oFGMask_FloodedHoles; - cv::Mat m_oLastFGMask_dilated; - cv::Mat m_oLastFGMask_dilated_inverted; - cv::Mat m_oCurrRawFGBlinkMask; - cv::Mat m_oLastRawFGBlinkMask; - cv::Mat m_oTempGlobalWordWeightDiffFactor; - cv::Mat m_oMorphExStructElement; + //! pre-allocated CV_8UC1 matrices used to speed up morph ops + cv::Mat m_oFGMask_PreFlood; + cv::Mat m_oFGMask_FloodedHoles; + cv::Mat m_oLastFGMask_dilated; + cv::Mat m_oLastFGMask_dilated_inverted; + cv::Mat m_oCurrRawFGBlinkMask; + cv::Mat m_oLastRawFGBlinkMask; + cv::Mat m_oTempGlobalWordWeightDiffFactor; + cv::Mat m_oMorphExStructElement; - //! internal cleanup function for the dictionary structures - void CleanupDictionaries(); - //! internal weight lookup function for local words - static float GetLocalWordWeight(const LocalWordBase* w, size_t nCurrFrame, size_t nOffset); - //! internal weight lookup function for global words - static float GetGlobalWordWeight(const GlobalWordBase* w); -}; + //! internal cleanup function for the dictionary structures + void CleanupDictionaries(); + //! internal weight lookup function for local words + static float GetLocalWordWeight(const LocalWordBase* w, size_t nCurrFrame, size_t nOffset); + //! internal weight lookup function for global words + static float GetGlobalWordWeight(const GlobalWordBase* w); + }; + } + } +} diff --git a/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.cpp b/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.cpp index f965b048bb19f950ee51349ed0e463b23932ec95..04a084acc18d7930f19ab209bc989cf010c1f89c 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.cpp +++ b/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.cpp @@ -7,732 +7,743 @@ #include "BackgroundSubtractorSuBSENSE.h" #include "RandUtils.h" -/* - * - * 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. - * - */ - //! 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 +//using namespace bgslibrary::algorithms::lbsp; + +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /* + * + * 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. + * + */ + //! defines the threshold value(s) used to detect long-term ghosting and trigger the fast edge-based absorption heuristic + const float GHOSTDET_D_MAX = 0.010f; // defines 'negligible' change here + const float GHOSTDET_S_MIN = 0.995f; // defines the required minimum local foreground saturation value + //! parameter used to scale dynamic distance threshold adjustments ('R(x)') + const float FEEDBACK_R_VAR = 0.01f; + //! parameters used to adjust the variation step size of 'v(x)' + const float FEEDBACK_V_INCR = 1.000f; + const float FEEDBACK_V_DECR = 0.100f; + //! parameters used to scale dynamic learning rate adjustments ('T(x)') + const float FEEDBACK_T_DECR = 0.2500f; + const float FEEDBACK_T_INCR = 0.5000f; + const float FEEDBACK_T_LOWER = 2.0000f; + const float FEEDBACK_T_UPPER = 256.00f; + //! parameters used to define 'unstable' regions, based on segm noise/bg dynamics and local dist threshold values + const float UNSTABLE_REG_RATIO_MIN = 0.100f; + const float UNSTABLE_REG_RDIST_MIN = 3.000f; + //! parameters used to scale the relative LBSP intensity threshold used for internal comparisons + const float LBSPDESC_NONZERO_RATIO_MIN = 0.100f; + const float LBSPDESC_NONZERO_RATIO_MAX = 0.500f; + //! parameters used to define model reset/learning rate boosts in our frame-level component #define FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD (m_nMinColorDistThreshold/2) -#define FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO (8) + const int FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO = 8; -// local define used to display debug information -#define DISPLAY_SUBSENSE_DEBUG_INFO 0 -// local define used to specify the default frame size (320x240 = QVGA) + // local define used to display debug information + const int DISPLAY_SUBSENSE_DEBUG_INFO = 0; + // local define used to specify the default frame size (320x240 = QVGA) #define DEFAULT_FRAME_SIZE cv::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_nDescDistThresholdOffset) -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; - -BackgroundSubtractorSuBSENSE::BackgroundSubtractorSuBSENSE(float fRelLBSPThreshold - , size_t nDescDistThresholdOffset - , size_t nMinColorDistThreshold - , size_t nBGSamples - , size_t nRequiredBGSamples - , size_t nSamplesForMovingAvgs) - : BackgroundSubtractorLBSP(fRelLBSPThreshold) - , m_nMinColorDistThreshold(nMinColorDistThreshold) - , m_nDescDistThresholdOffset(nDescDistThresholdOffset) - , m_nBGSamples(nBGSamples) - , m_nRequiredBGSamples(nRequiredBGSamples) - , m_nSamplesForMovingAvgs(nSamplesForMovingAvgs) - , m_fLastNonZeroDescRatio(0.0f) - , m_bLearningRateScalingEnabled(true) - , m_fCurrLearningRateLowerCap(FEEDBACK_T_LOWER) - , m_fCurrLearningRateUpperCap(FEEDBACK_T_UPPER) - , m_nMedianBlurKernelSize(m_nDefaultMedianBlurKernelSize) - , m_bUse3x3Spread(true) { - CV_Assert(m_nBGSamples > 0 && m_nRequiredBGSamples <= m_nBGSamples); - CV_Assert(m_nMinColorDistThreshold >= STAB_COLOR_DIST_OFFSET); -} + 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; -BackgroundSubtractorSuBSENSE::~BackgroundSubtractorSuBSENSE() { - if (m_aPxIdxLUT) - delete[] m_aPxIdxLUT; - if (m_aPxInfoLUT) - delete[] m_aPxInfoLUT; -} - -void BackgroundSubtractorSuBSENSE::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) { - // == init - CV_Assert(!oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0); - CV_Assert(oInitImg.isContinuous()); - CV_Assert(oInitImg.type() == CV_8UC3 || oInitImg.type() == CV_8UC1); - if (oInitImg.type() == CV_8UC3) { - std::vector<cv::Mat> voInitImgChannels; - cv::split(oInitImg, voInitImgChannels); - if (!cv::countNonZero((voInitImgChannels[0] != voInitImgChannels[1]) | (voInitImgChannels[2] != voInitImgChannels[1]))) - std::cout << std::endl << "\tBackgroundSubtractorSuBSENSE : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl; - } - cv::Mat oNewBGROI; - if (oROI.empty() && (m_oROI.empty() || oROI.size() != oInitImg.size())) { - oNewBGROI.create(oInitImg.size(), CV_8UC1); - oNewBGROI = cv::Scalar_<uchar>(UCHAR_MAX); - } - else if (oROI.empty()) - oNewBGROI = m_oROI; - else { - CV_Assert(oROI.size() == oInitImg.size() && oROI.type() == CV_8UC1); - CV_Assert(cv::countNonZero((oROI < UCHAR_MAX)&(oROI > 0)) == 0); - oNewBGROI = oROI.clone(); - cv::Mat oTempROI; - cv::dilate(oNewBGROI, oTempROI, cv::Mat(), cv::Point(-1, -1), LBSP::PATCH_SIZE / 2); - cv::bitwise_or(oNewBGROI, oTempROI / 2, oNewBGROI); - } - const size_t nOrigROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nOrigROIPxCount > 0); - LBSP::validateROI(oNewBGROI); - const size_t nFinalROIPxCount = (size_t)cv::countNonZero(oNewBGROI); - CV_Assert(nFinalROIPxCount > 0); - m_oROI = oNewBGROI; - m_oImgSize = oInitImg.size(); - m_nImgType = oInitImg.type(); - m_nImgChannels = oInitImg.channels(); - m_nTotPxCount = m_oImgSize.area(); - m_nTotRelevantPxCount = nFinalROIPxCount; - m_nFrameIndex = 0; - m_nFramesSinceLastReset = 0; - m_nModelResetCooldown = 0; - m_fLastNonZeroDescRatio = 0.0f; - const int nTotImgPixels = m_oImgSize.height*m_oImgSize.width; - if (nOrigROIPxCount >= m_nTotPxCount / 2 && (int)m_nTotPxCount >= 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) + m_nDefaultMedianBlurKernelSize, 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 = m_nDefaultMedianBlurKernelSize; - m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER * 2; - m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER * 2; - } - m_oUpdateRateFrame.create(m_oImgSize, CV_32FC1); - m_oUpdateRateFrame = cv::Scalar(m_fCurrLearningRateLowerCap); - m_oDistThresholdFrame.create(m_oImgSize, CV_32FC1); - m_oDistThresholdFrame = cv::Scalar(1.0f); - m_oVariationModulatorFrame.create(m_oImgSize, CV_32FC1); - m_oVariationModulatorFrame = cv::Scalar(10.0f); // should always be >= FEEDBACK_V_DECR - m_oMeanLastDistFrame.create(m_oImgSize, CV_32FC1); - m_oMeanLastDistFrame = cv::Scalar(0.0f); - m_oMeanMinDistFrame_LT.create(m_oImgSize, CV_32FC1); - m_oMeanMinDistFrame_LT = cv::Scalar(0.0f); - m_oMeanMinDistFrame_ST.create(m_oImgSize, CV_32FC1); - m_oMeanMinDistFrame_ST = cv::Scalar(0.0f); - m_oDownSampledFrameSize = cv::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 = cv::Scalar(0.0f); - m_oMeanDownSampledLastDistFrame_ST.create(m_oDownSampledFrameSize, CV_32FC((int)m_nImgChannels)); - m_oMeanDownSampledLastDistFrame_ST = cv::Scalar(0.0f); - m_oMeanRawSegmResFrame_LT.create(m_oImgSize, CV_32FC1); - m_oMeanRawSegmResFrame_LT = cv::Scalar(0.0f); - m_oMeanRawSegmResFrame_ST.create(m_oImgSize, CV_32FC1); - m_oMeanRawSegmResFrame_ST = cv::Scalar(0.0f); - m_oMeanFinalSegmResFrame_LT.create(m_oImgSize, CV_32FC1); - m_oMeanFinalSegmResFrame_LT = cv::Scalar(0.0f); - m_oMeanFinalSegmResFrame_ST.create(m_oImgSize, CV_32FC1); - m_oMeanFinalSegmResFrame_ST = cv::Scalar(0.0f); - m_oUnstableRegionMask.create(m_oImgSize, CV_8UC1); - m_oUnstableRegionMask = cv::Scalar_<uchar>(0); - m_oBlinksFrame.create(m_oImgSize, CV_8UC1); - m_oBlinksFrame = cv::Scalar_<uchar>(0); - m_oDownSampledFrame_MotionAnalysis.create(m_oDownSampledFrameSize, CV_8UC((int)m_nImgChannels)); - m_oDownSampledFrame_MotionAnalysis = cv::Scalar_<uchar>::all(0); - m_oLastColorFrame.create(m_oImgSize, CV_8UC((int)m_nImgChannels)); - m_oLastColorFrame = cv::Scalar_<uchar>::all(0); - m_oLastDescFrame.create(m_oImgSize, CV_16UC((int)m_nImgChannels)); - m_oLastDescFrame = cv::Scalar_<ushort>::all(0); - m_oLastRawFGMask.create(m_oImgSize, CV_8UC1); - m_oLastRawFGMask = cv::Scalar_<uchar>(0); - m_oLastFGMask.create(m_oImgSize, CV_8UC1); - m_oLastFGMask = cv::Scalar_<uchar>(0); - m_oLastFGMask_dilated.create(m_oImgSize, CV_8UC1); - m_oLastFGMask_dilated = cv::Scalar_<uchar>(0); - m_oLastFGMask_dilated_inverted.create(m_oImgSize, CV_8UC1); - m_oLastFGMask_dilated_inverted = cv::Scalar_<uchar>(0); - m_oFGMask_FloodedHoles.create(m_oImgSize, CV_8UC1); - m_oFGMask_FloodedHoles = cv::Scalar_<uchar>(0); - m_oFGMask_PreFlood.create(m_oImgSize, CV_8UC1); - m_oFGMask_PreFlood = cv::Scalar_<uchar>(0); - m_oCurrRawFGBlinkMask.create(m_oImgSize, CV_8UC1); - m_oCurrRawFGBlinkMask = cv::Scalar_<uchar>(0); - m_oLastRawFGBlinkMask.create(m_oImgSize, CV_8UC1); - m_oLastRawFGBlinkMask = cv::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] = cv::Scalar_<uchar>::all(0); - m_voBGDescSamples[s].create(m_oImgSize, CV_16UC((int)m_nImgChannels)); - m_voBGDescSamples[s] = cv::Scalar_<ushort>::all(0); - } - if (m_aPxIdxLUT) - delete[] m_aPxIdxLUT; - if (m_aPxInfoLUT) - delete[] m_aPxInfoLUT; - m_aPxIdxLUT = new size_t[m_nTotRelevantPxCount]; - m_aPxInfoLUT = new PxInfoBase[m_nTotPxCount]; - if (m_nImgChannels == 1) { - CV_Assert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width && m_oLastColorFrame.step.p[1] == 1); - CV_Assert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); - for (size_t t = 0; t <= UCHAR_MAX; ++t) - m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>((m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold) / 3); - for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { - if (m_oROI.data[nPxIter]) { - m_aPxIdxLUT[nModelIter] = nPxIter; - m_aPxInfoLUT[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; - m_aPxInfoLUT[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; - m_aPxInfoLUT[nPxIter].nModelIdx = nModelIter; - m_oLastColorFrame.data[nPxIter] = oInitImg.data[nPxIter]; - const size_t nDescIter = nPxIter * 2; - LBSP::computeGrayscaleDescriptor(oInitImg, oInitImg.data[nPxIter], m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxIter]], *((ushort*)(m_oLastDescFrame.data + nDescIter))); - ++nModelIter; + BackgroundSubtractorSuBSENSE::BackgroundSubtractorSuBSENSE(float fRelLBSPThreshold + , size_t nDescDistThresholdOffset + , size_t nMinColorDistThreshold + , size_t nBGSamples + , size_t nRequiredBGSamples + , size_t nSamplesForMovingAvgs) + : BackgroundSubtractorLBSP(fRelLBSPThreshold) + , m_nMinColorDistThreshold(nMinColorDistThreshold) + , m_nDescDistThresholdOffset(nDescDistThresholdOffset) + , m_nBGSamples(nBGSamples) + , m_nRequiredBGSamples(nRequiredBGSamples) + , m_nSamplesForMovingAvgs(nSamplesForMovingAvgs) + , m_fLastNonZeroDescRatio(0.0f) + , m_bLearningRateScalingEnabled(true) + , m_fCurrLearningRateLowerCap(FEEDBACK_T_LOWER) + , m_fCurrLearningRateUpperCap(FEEDBACK_T_UPPER) + , m_nMedianBlurKernelSize(m_nDefaultMedianBlurKernelSize) + , m_bUse3x3Spread(true) { + CV_Assert(m_nBGSamples > 0 && m_nRequiredBGSamples <= m_nBGSamples); + CV_Assert(m_nMinColorDistThreshold >= STAB_COLOR_DIST_OFFSET); } - } - } - else { //m_nImgChannels==3 - CV_Assert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width * 3 && m_oLastColorFrame.step.p[1] == 3); - CV_Assert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); - for (size_t t = 0; t <= UCHAR_MAX; ++t) - m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold); - for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { - if (m_oROI.data[nPxIter]) { - m_aPxIdxLUT[nModelIter] = nPxIter; - m_aPxInfoLUT[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; - m_aPxInfoLUT[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; - m_aPxInfoLUT[nPxIter].nModelIdx = nModelIter; - const size_t nPxRGBIter = nPxIter * 3; - const size_t nDescRGBIter = nPxRGBIter * 2; - for (size_t c = 0; c < 3; ++c) { - m_oLastColorFrame.data[nPxRGBIter + c] = oInitImg.data[nPxRGBIter + c]; - LBSP::computeSingleRGBDescriptor(oInitImg, oInitImg.data[nPxRGBIter + c], m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxRGBIter + c]], ((ushort*)(m_oLastDescFrame.data + nDescRGBIter))[c]); - } - ++nModelIter; + + BackgroundSubtractorSuBSENSE::~BackgroundSubtractorSuBSENSE() { + if (m_aPxIdxLUT) + delete[] m_aPxIdxLUT; + if (m_aPxInfoLUT) + delete[] m_aPxInfoLUT; } - } - } - m_bInitialized = true; - refreshModel(1.0f); -} -void BackgroundSubtractorSuBSENSE::refreshModel(float fSamplesRefreshFrac, bool bForceFGUpdate) { - // == refresh - CV_Assert(m_bInitialized); - CV_Assert(fSamplesRefreshFrac > 0.0f && fSamplesRefreshFrac <= 1.0f); - const size_t nModelsToRefresh = 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 nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - if (bForceFGUpdate || !m_oLastFGMask.data[nPxIter]) { - for (size_t nCurrModelIdx = nRefreshStartPos; nCurrModelIdx < nRefreshStartPos + nModelsToRefresh; ++nCurrModelIdx) { - int nSampleImgCoord_Y, nSampleImgCoord_X; - getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); - const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - if (bForceFGUpdate || !m_oLastFGMask.data[nSamplePxIdx]) { - const size_t nCurrRealModelIdx = nCurrModelIdx%m_nBGSamples; - m_voBGColorSamples[nCurrRealModelIdx].data[nPxIter] = m_oLastColorFrame.data[nSamplePxIdx]; - *((ushort*)(m_voBGDescSamples[nCurrRealModelIdx].data + nPxIter * 2)) = *((ushort*)(m_oLastDescFrame.data + nSamplePxIdx * 2)); - } + void BackgroundSubtractorSuBSENSE::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) { + // == init + CV_Assert(!oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0); + CV_Assert(oInitImg.isContinuous()); + CV_Assert(oInitImg.type() == CV_8UC3 || oInitImg.type() == CV_8UC1); + if (oInitImg.type() == CV_8UC3) { + std::vector<cv::Mat> voInitImgChannels; + cv::split(oInitImg, voInitImgChannels); + if (!cv::countNonZero((voInitImgChannels[0] != voInitImgChannels[1]) | (voInitImgChannels[2] != voInitImgChannels[1]))) + std::cout << std::endl << "\tBackgroundSubtractorSuBSENSE : Warning, grayscale images should always be passed in CV_8UC1 format for optimal performance." << std::endl; } - } - } - } - else { //m_nImgChannels==3 - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - if (bForceFGUpdate || !m_oLastFGMask.data[nPxIter]) { - for (size_t nCurrModelIdx = nRefreshStartPos; nCurrModelIdx < nRefreshStartPos + nModelsToRefresh; ++nCurrModelIdx) { - int nSampleImgCoord_Y, nSampleImgCoord_X; - getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); - const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - if (bForceFGUpdate || !m_oLastFGMask.data[nSamplePxIdx]) { - const size_t nCurrRealModelIdx = nCurrModelIdx%m_nBGSamples; - for (size_t c = 0; c < 3; ++c) { - m_voBGColorSamples[nCurrRealModelIdx].data[nPxIter * 3 + c] = m_oLastColorFrame.data[nSamplePxIdx * 3 + c]; - *((ushort*)(m_voBGDescSamples[nCurrRealModelIdx].data + (nPxIter * 3 + c) * 2)) = *((ushort*)(m_oLastDescFrame.data + (nSamplePxIdx * 3 + c) * 2)); - } - } + cv::Mat oNewBGROI; + if (oROI.empty() && (m_oROI.empty() || oROI.size() != oInitImg.size())) { + oNewBGROI.create(oInitImg.size(), CV_8UC1); + oNewBGROI = cv::Scalar_<uchar>(UCHAR_MAX); } - } - } - } -} - -void BackgroundSubtractorSuBSENSE::apply(cv::InputArray _image, cv::OutputArray _fgmask, double learningRateOverride) { - // == process - CV_Assert(m_bInitialized); - cv::Mat oInputImg = _image.getMat(); - CV_Assert(oInputImg.type() == m_nImgType && oInputImg.size() == m_oImgSize); - CV_Assert(oInputImg.isContinuous()); - _fgmask.create(m_oImgSize, CV_8UC1); - cv::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); - const float fRollAvgFactor_ST = 1.0f / std::min(m_nFrameIndex, m_nSamplesForMovingAvgs / 4); - if (m_nImgChannels == 1) { - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const size_t nDescIter = nPxIter * 2; - const size_t nFloatIter = nPxIter * 4; - const int nCurrImgCoord_X = m_aPxInfoLUT[nPxIter].nImgCoord_X; - const int nCurrImgCoord_Y = m_aPxInfoLUT[nPxIter].nImgCoord_Y; - const uchar nCurrColor = oInputImg.data[nPxIter]; - size_t nMinDescDist = s_nDescMaxDataRange_1ch; - size_t nMinSumDist = s_nColorMaxDataRange_1ch; - float* pfCurrDistThresholdFactor = (float*)(m_oDistThresholdFrame.data + nFloatIter); - float* pfCurrVariationFactor = (float*)(m_oVariationModulatorFrame.data + nFloatIter); - float* pfCurrLearningRate = ((float*)(m_oUpdateRateFrame.data + nFloatIter)); - float* pfCurrMeanLastDist = ((float*)(m_oMeanLastDistFrame.data + nFloatIter)); - float* pfCurrMeanMinDist_LT = ((float*)(m_oMeanMinDistFrame_LT.data + nFloatIter)); - float* pfCurrMeanMinDist_ST = ((float*)(m_oMeanMinDistFrame_ST.data + nFloatIter)); - float* pfCurrMeanRawSegmRes_LT = ((float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter)); - float* pfCurrMeanRawSegmRes_ST = ((float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter)); - float* pfCurrMeanFinalSegmRes_LT = ((float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter)); - float* pfCurrMeanFinalSegmRes_ST = ((float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter)); - ushort& nLastIntraDesc = *((ushort*)(m_oLastDescFrame.data + nDescIter)); - uchar& nLastColor = m_oLastColorFrame.data[nPxIter]; - const size_t nCurrColorDistThreshold = (size_t)(((*pfCurrDistThresholdFactor)*m_nMinColorDistThreshold) - ((!m_oUnstableRegionMask.data[nPxIter])*STAB_COLOR_DIST_OFFSET)) / 2; - const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(*pfCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (m_oUnstableRegionMask.data[nPxIter] * UNSTAB_DESC_DIST_OFFSET); - ushort nCurrInterDesc, nCurrIntraDesc; - LBSP::computeGrayscaleDescriptor(oInputImg, nCurrColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nCurrColor], nCurrIntraDesc); - m_oUnstableRegionMask.data[nPxIter] = ((*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[nPxIter]; - { - const size_t nColorDist = L1dist(nCurrColor, nBGColor); - if (nColorDist > nCurrColorDistThreshold) - goto failedcheck1ch; - const ushort& nBGIntraDesc = *((ushort*)(m_voBGDescSamples[nSampleIdx].data + nDescIter)); - const size_t nIntraDescDist = hdist(nCurrIntraDesc, nBGIntraDesc); - LBSP::computeGrayscaleDescriptor(oInputImg, nBGColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nBGColor], nCurrInterDesc); - const size_t nInterDescDist = hdist(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++; + else if (oROI.empty()) + oNewBGROI = m_oROI; + else { + CV_Assert(oROI.size() == oInitImg.size() && oROI.type() == CV_8UC1); + CV_Assert(cv::countNonZero((oROI < UCHAR_MAX)&(oROI > 0)) == 0); + oNewBGROI = oROI.clone(); + cv::Mat oTempROI; + cv::dilate(oNewBGROI, oTempROI, cv::Mat(), cv::Point(-1, -1), LBSP::PATCH_SIZE / 2); + cv::bitwise_or(oNewBGROI, oTempROI / 2, oNewBGROI); } - failedcheck1ch: - nSampleIdx++; - } - const float fNormalizedLastDist = ((float)L1dist(nLastColor, nCurrColor) / s_nColorMaxDataRange_1ch + (float)hdist(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[nPxIter] = 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 + nDescIter)) = nCurrIntraDesc; - m_voBGColorSamples[s_rand].data[nPxIter] = nCurrColor; + const size_t nOrigROIPxCount = (size_t)cv::countNonZero(oNewBGROI); + CV_Assert(nOrigROIPxCount > 0); + LBSP::validateROI(oNewBGROI); + const size_t nFinalROIPxCount = (size_t)cv::countNonZero(oNewBGROI); + CV_Assert(nFinalROIPxCount > 0); + m_oROI = oNewBGROI; + m_oImgSize = oInitImg.size(); + m_nImgType = oInitImg.type(); + m_nImgChannels = oInitImg.channels(); + m_nTotPxCount = m_oImgSize.area(); + m_nTotRelevantPxCount = nFinalROIPxCount; + m_nFrameIndex = 0; + m_nFramesSinceLastReset = 0; + m_nModelResetCooldown = 0; + m_fLastNonZeroDescRatio = 0.0f; + const int nTotImgPixels = m_oImgSize.height*m_oImgSize.width; + if (nOrigROIPxCount >= m_nTotPxCount / 2 && (int)m_nTotPxCount >= 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) + m_nDefaultMedianBlurKernelSize, 14); + m_nMedianBlurKernelSize = (nRawMedianBlurKernelSize % 2) ? nRawMedianBlurKernelSize : nRawMedianBlurKernelSize - 1; + m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER; + m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER; } - } - 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 + nDescIter)) = nCurrIntraDesc; - m_voBGColorSamples[s_rand].data[nPxIter] = nCurrColor; + else { + m_bLearningRateScalingEnabled = false; + m_bAutoModelResetEnabled = false; + m_bUse3x3Spread = true; + m_nMedianBlurKernelSize = m_nDefaultMedianBlurKernelSize; + m_fCurrLearningRateLowerCap = FEEDBACK_T_LOWER * 2; + m_fCurrLearningRateUpperCap = FEEDBACK_T_UPPER * 2; } - int nSampleImgCoord_Y, nSampleImgCoord_X; - const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[nPxIter]; - if (bCurrUsing3x3Spread) - getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); - else - getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); - const size_t n_rand = rand(); - const size_t idx_rand_uchar = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - 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; + m_oUpdateRateFrame.create(m_oImgSize, CV_32FC1); + m_oUpdateRateFrame = cv::Scalar(m_fCurrLearningRateLowerCap); + m_oDistThresholdFrame.create(m_oImgSize, CV_32FC1); + m_oDistThresholdFrame = cv::Scalar(1.0f); + m_oVariationModulatorFrame.create(m_oImgSize, CV_32FC1); + m_oVariationModulatorFrame = cv::Scalar(10.0f); // should always be >= FEEDBACK_V_DECR + m_oMeanLastDistFrame.create(m_oImgSize, CV_32FC1); + m_oMeanLastDistFrame = cv::Scalar(0.0f); + m_oMeanMinDistFrame_LT.create(m_oImgSize, CV_32FC1); + m_oMeanMinDistFrame_LT = cv::Scalar(0.0f); + m_oMeanMinDistFrame_ST.create(m_oImgSize, CV_32FC1); + m_oMeanMinDistFrame_ST = cv::Scalar(0.0f); + m_oDownSampledFrameSize = cv::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 = cv::Scalar(0.0f); + m_oMeanDownSampledLastDistFrame_ST.create(m_oDownSampledFrameSize, CV_32FC((int)m_nImgChannels)); + m_oMeanDownSampledLastDistFrame_ST = cv::Scalar(0.0f); + m_oMeanRawSegmResFrame_LT.create(m_oImgSize, CV_32FC1); + m_oMeanRawSegmResFrame_LT = cv::Scalar(0.0f); + m_oMeanRawSegmResFrame_ST.create(m_oImgSize, CV_32FC1); + m_oMeanRawSegmResFrame_ST = cv::Scalar(0.0f); + m_oMeanFinalSegmResFrame_LT.create(m_oImgSize, CV_32FC1); + m_oMeanFinalSegmResFrame_LT = cv::Scalar(0.0f); + m_oMeanFinalSegmResFrame_ST.create(m_oImgSize, CV_32FC1); + m_oMeanFinalSegmResFrame_ST = cv::Scalar(0.0f); + m_oUnstableRegionMask.create(m_oImgSize, CV_8UC1); + m_oUnstableRegionMask = cv::Scalar_<uchar>(0); + m_oBlinksFrame.create(m_oImgSize, CV_8UC1); + m_oBlinksFrame = cv::Scalar_<uchar>(0); + m_oDownSampledFrame_MotionAnalysis.create(m_oDownSampledFrameSize, CV_8UC((int)m_nImgChannels)); + m_oDownSampledFrame_MotionAnalysis = cv::Scalar_<uchar>::all(0); + m_oLastColorFrame.create(m_oImgSize, CV_8UC((int)m_nImgChannels)); + m_oLastColorFrame = cv::Scalar_<uchar>::all(0); + m_oLastDescFrame.create(m_oImgSize, CV_16UC((int)m_nImgChannels)); + m_oLastDescFrame = cv::Scalar_<ushort>::all(0); + m_oLastRawFGMask.create(m_oImgSize, CV_8UC1); + m_oLastRawFGMask = cv::Scalar_<uchar>(0); + m_oLastFGMask.create(m_oImgSize, CV_8UC1); + m_oLastFGMask = cv::Scalar_<uchar>(0); + m_oLastFGMask_dilated.create(m_oImgSize, CV_8UC1); + m_oLastFGMask_dilated = cv::Scalar_<uchar>(0); + m_oLastFGMask_dilated_inverted.create(m_oImgSize, CV_8UC1); + m_oLastFGMask_dilated_inverted = cv::Scalar_<uchar>(0); + m_oFGMask_FloodedHoles.create(m_oImgSize, CV_8UC1); + m_oFGMask_FloodedHoles = cv::Scalar_<uchar>(0); + m_oFGMask_PreFlood.create(m_oImgSize, CV_8UC1); + m_oFGMask_PreFlood = cv::Scalar_<uchar>(0); + m_oCurrRawFGBlinkMask.create(m_oImgSize, CV_8UC1); + m_oCurrRawFGBlinkMask = cv::Scalar_<uchar>(0); + m_oLastRawFGBlinkMask.create(m_oImgSize, CV_8UC1); + m_oLastRawFGBlinkMask = cv::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] = cv::Scalar_<uchar>::all(0); + m_voBGDescSamples[s].create(m_oImgSize, CV_16UC((int)m_nImgChannels)); + m_voBGDescSamples[s] = cv::Scalar_<ushort>::all(0); } - } - if (m_oLastFGMask.data[nPxIter] || (std::min(*pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && oCurrFGMask.data[nPxIter])) { - 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[nPxIter]) - (*pfCurrVariationFactor) += FEEDBACK_V_INCR; - else if ((*pfCurrVariationFactor) > FEEDBACK_V_DECR) { - (*pfCurrVariationFactor) -= m_oLastFGMask.data[nPxIter] ? FEEDBACK_V_DECR / 4 : m_oUnstableRegionMask.data[nPxIter] ? 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(nCurrIntraDesc) >= 2) - ++nNonZeroDescCount; - nLastIntraDesc = nCurrIntraDesc; - nLastColor = nCurrColor; - } - } - else { //m_nImgChannels==3 - for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { - const size_t nPxIter = m_aPxIdxLUT[nModelIter]; - const int nCurrImgCoord_X = m_aPxInfoLUT[nPxIter].nImgCoord_X; - const int nCurrImgCoord_Y = m_aPxInfoLUT[nPxIter].nImgCoord_Y; - const size_t nPxIterRGB = nPxIter * 3; - const size_t nDescIterRGB = nPxIterRGB * 2; - const size_t nFloatIter = nPxIter * 4; - const uchar* const anCurrColor = oInputImg.data + nPxIterRGB; - size_t nMinTotDescDist = s_nDescMaxDataRange_3ch; - size_t nMinTotSumDist = s_nColorMaxDataRange_3ch; - float* pfCurrDistThresholdFactor = (float*)(m_oDistThresholdFrame.data + nFloatIter); - float* pfCurrVariationFactor = (float*)(m_oVariationModulatorFrame.data + nFloatIter); - float* pfCurrLearningRate = ((float*)(m_oUpdateRateFrame.data + nFloatIter)); - float* pfCurrMeanLastDist = ((float*)(m_oMeanLastDistFrame.data + nFloatIter)); - float* pfCurrMeanMinDist_LT = ((float*)(m_oMeanMinDistFrame_LT.data + nFloatIter)); - float* pfCurrMeanMinDist_ST = ((float*)(m_oMeanMinDistFrame_ST.data + nFloatIter)); - float* pfCurrMeanRawSegmRes_LT = ((float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter)); - float* pfCurrMeanRawSegmRes_ST = ((float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter)); - float* pfCurrMeanFinalSegmRes_LT = ((float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter)); - float* pfCurrMeanFinalSegmRes_ST = ((float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter)); - ushort* anLastIntraDesc = ((ushort*)(m_oLastDescFrame.data + nDescIterRGB)); - uchar* anLastColor = m_oLastColorFrame.data + nPxIterRGB; - const size_t nCurrColorDistThreshold = (size_t)(((*pfCurrDistThresholdFactor)*m_nMinColorDistThreshold) - ((!m_oUnstableRegionMask.data[nPxIter])*STAB_COLOR_DIST_OFFSET)); - const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(*pfCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (m_oUnstableRegionMask.data[nPxIter] * 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, nCurrImgCoord_X, nCurrImgCoord_Y, anCurrIntraLBSPThresholds, anCurrIntraDesc); - m_oUnstableRegionMask.data[nPxIter] = ((*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 + nDescIterRGB); - const uchar* const anBGColor = m_voBGColorSamples[nSampleIdx].data + nPxIterRGB; - size_t nTotDescDist = 0; - size_t nTotSumDist = 0; - for (size_t c = 0; c < 3; ++c) { - const size_t nColorDist = L1dist(anCurrColor[c], anBGColor[c]); - if (nColorDist > nCurrSCColorDistThreshold) - goto failedcheck3ch; - const size_t nIntraDescDist = hdist(anCurrIntraDesc[c], anBGIntraDesc[c]); - LBSP::computeSingleRGBDescriptor(oInputImg, anBGColor[c], nCurrImgCoord_X, nCurrImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[anBGColor[c]], anCurrInterDesc[c]); - const size_t nInterDescDist = hdist(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 (m_aPxIdxLUT) + delete[] m_aPxIdxLUT; + if (m_aPxInfoLUT) + delete[] m_aPxInfoLUT; + m_aPxIdxLUT = new size_t[m_nTotRelevantPxCount]; + m_aPxInfoLUT = new PxInfoBase[m_nTotPxCount]; + if (m_nImgChannels == 1) { + CV_Assert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width && m_oLastColorFrame.step.p[1] == 1); + CV_Assert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); + for (size_t t = 0; t <= UCHAR_MAX; ++t) + m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>((m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold) / 3); + for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { + if (m_oROI.data[nPxIter]) { + m_aPxIdxLUT[nModelIter] = nPxIter; + m_aPxInfoLUT[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; + m_aPxInfoLUT[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; + m_aPxInfoLUT[nPxIter].nModelIdx = nModelIter; + m_oLastColorFrame.data[nPxIter] = oInitImg.data[nPxIter]; + const size_t nDescIter = nPxIter * 2; + LBSP::computeGrayscaleDescriptor(oInitImg, oInitImg.data[nPxIter], m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxIter]], *((ushort*)(m_oLastDescFrame.data + nDescIter))); + ++nModelIter; + } + } } - 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<3>(anLastColor, anCurrColor) / s_nColorMaxDataRange_3ch + (float)hdist<3>(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[nPxIter] = 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 + nDescIterRGB + 2 * c)) = anCurrIntraDesc[c]; - *(m_voBGColorSamples[s_rand].data + nPxIterRGB + c) = anCurrColor[c]; + else { //m_nImgChannels==3 + CV_Assert(m_oLastColorFrame.step.p[0] == (size_t)m_oImgSize.width * 3 && m_oLastColorFrame.step.p[1] == 3); + CV_Assert(m_oLastDescFrame.step.p[0] == m_oLastColorFrame.step.p[0] * 2 && m_oLastDescFrame.step.p[1] == m_oLastColorFrame.step.p[1] * 2); + for (size_t t = 0; t <= UCHAR_MAX; ++t) + m_anLBSPThreshold_8bitLUT[t] = cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + t*m_fRelLBSPThreshold); + for (size_t nPxIter = 0, nModelIter = 0; nPxIter < m_nTotPxCount; ++nPxIter) { + if (m_oROI.data[nPxIter]) { + m_aPxIdxLUT[nModelIter] = nPxIter; + m_aPxInfoLUT[nPxIter].nImgCoord_Y = (int)nPxIter / m_oImgSize.width; + m_aPxInfoLUT[nPxIter].nImgCoord_X = (int)nPxIter%m_oImgSize.width; + m_aPxInfoLUT[nPxIter].nModelIdx = nModelIter; + const size_t nPxRGBIter = nPxIter * 3; + const size_t nDescRGBIter = nPxRGBIter * 2; + for (size_t c = 0; c < 3; ++c) { + m_oLastColorFrame.data[nPxRGBIter + c] = oInitImg.data[nPxRGBIter + c]; + LBSP::computeSingleRGBDescriptor(oInitImg, oInitImg.data[nPxRGBIter + c], m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[oInitImg.data[nPxRGBIter + c]], ((ushort*)(m_oLastDescFrame.data + nDescRGBIter))[c]); + } + ++nModelIter; + } } } + m_bInitialized = true; + refreshModel(1.0f); } - 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 + nDescIterRGB + 2 * c)) = anCurrIntraDesc[c]; - *(m_voBGColorSamples[s_rand].data + nPxIterRGB + c) = anCurrColor[c]; + + void BackgroundSubtractorSuBSENSE::refreshModel(float fSamplesRefreshFrac, bool bForceFGUpdate) { + // == refresh + CV_Assert(m_bInitialized); + CV_Assert(fSamplesRefreshFrac > 0.0f && fSamplesRefreshFrac <= 1.0f); + const size_t nModelsToRefresh = 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 nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + if (bForceFGUpdate || !m_oLastFGMask.data[nPxIter]) { + for (size_t nCurrModelIdx = nRefreshStartPos; nCurrModelIdx < nRefreshStartPos + nModelsToRefresh; ++nCurrModelIdx) { + int nSampleImgCoord_Y, nSampleImgCoord_X; + getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); + const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + if (bForceFGUpdate || !m_oLastFGMask.data[nSamplePxIdx]) { + const size_t nCurrRealModelIdx = nCurrModelIdx%m_nBGSamples; + m_voBGColorSamples[nCurrRealModelIdx].data[nPxIter] = m_oLastColorFrame.data[nSamplePxIdx]; + *((ushort*)(m_voBGDescSamples[nCurrRealModelIdx].data + nPxIter * 2)) = *((ushort*)(m_oLastDescFrame.data + nSamplePxIdx * 2)); + } + } + } } } - int nSampleImgCoord_Y, nSampleImgCoord_X; - const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[nPxIter]; - if (bCurrUsing3x3Spread) - getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); - else - getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); - const size_t n_rand = rand(); - const size_t idx_rand_uchar = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; - 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]; + else { //m_nImgChannels==3 + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + if (bForceFGUpdate || !m_oLastFGMask.data[nPxIter]) { + for (size_t nCurrModelIdx = nRefreshStartPos; nCurrModelIdx < nRefreshStartPos + nModelsToRefresh; ++nCurrModelIdx) { + int nSampleImgCoord_Y, nSampleImgCoord_X; + getRandSamplePosition(nSampleImgCoord_X, nSampleImgCoord_Y, m_aPxInfoLUT[nPxIter].nImgCoord_X, m_aPxInfoLUT[nPxIter].nImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); + const size_t nSamplePxIdx = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + if (bForceFGUpdate || !m_oLastFGMask.data[nSamplePxIdx]) { + const size_t nCurrRealModelIdx = nCurrModelIdx%m_nBGSamples; + for (size_t c = 0; c < 3; ++c) { + m_voBGColorSamples[nCurrRealModelIdx].data[nPxIter * 3 + c] = m_oLastColorFrame.data[nSamplePxIdx * 3 + c]; + *((ushort*)(m_voBGDescSamples[nCurrRealModelIdx].data + (nPxIter * 3 + c) * 2)) = *((ushort*)(m_oLastDescFrame.data + (nSamplePxIdx * 3 + c) * 2)); + } + } + } + } } } } - if (m_oLastFGMask.data[nPxIter] || (std::min(*pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && oCurrFGMask.data[nPxIter])) { - 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[nPxIter]) - (*pfCurrVariationFactor) += FEEDBACK_V_INCR; - else if ((*pfCurrVariationFactor) > FEEDBACK_V_DECR) { - (*pfCurrVariationFactor) -= m_oLastFGMask.data[nPxIter] ? FEEDBACK_V_DECR / 4 : m_oUnstableRegionMask.data[nPxIter] ? 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<3>(anCurrIntraDesc) >= 4) - ++nNonZeroDescCount; - for (size_t c = 0; c < 3; ++c) { - anLastIntraDesc[c] = anCurrIntraDesc[c]; - anLastColor[c] = anCurrColor[c]; - } - } - } + + void BackgroundSubtractorSuBSENSE::apply(cv::InputArray _image, cv::OutputArray _fgmask, double learningRateOverride) { + // == process + CV_Assert(m_bInitialized); + cv::Mat oInputImg = _image.getMat(); + CV_Assert(oInputImg.type() == m_nImgType && oInputImg.size() == m_oImgSize); + CV_Assert(oInputImg.isContinuous()); + _fgmask.create(m_oImgSize, CV_8UC1); + cv::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); + const float fRollAvgFactor_ST = 1.0f / std::min(m_nFrameIndex, m_nSamplesForMovingAvgs / 4); + if (m_nImgChannels == 1) { + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const size_t nDescIter = nPxIter * 2; + const size_t nFloatIter = nPxIter * 4; + const int nCurrImgCoord_X = m_aPxInfoLUT[nPxIter].nImgCoord_X; + const int nCurrImgCoord_Y = m_aPxInfoLUT[nPxIter].nImgCoord_Y; + const uchar nCurrColor = oInputImg.data[nPxIter]; + size_t nMinDescDist = s_nDescMaxDataRange_1ch; + size_t nMinSumDist = s_nColorMaxDataRange_1ch; + float* pfCurrDistThresholdFactor = (float*)(m_oDistThresholdFrame.data + nFloatIter); + float* pfCurrVariationFactor = (float*)(m_oVariationModulatorFrame.data + nFloatIter); + float* pfCurrLearningRate = ((float*)(m_oUpdateRateFrame.data + nFloatIter)); + float* pfCurrMeanLastDist = ((float*)(m_oMeanLastDistFrame.data + nFloatIter)); + float* pfCurrMeanMinDist_LT = ((float*)(m_oMeanMinDistFrame_LT.data + nFloatIter)); + float* pfCurrMeanMinDist_ST = ((float*)(m_oMeanMinDistFrame_ST.data + nFloatIter)); + float* pfCurrMeanRawSegmRes_LT = ((float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter)); + float* pfCurrMeanRawSegmRes_ST = ((float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter)); + float* pfCurrMeanFinalSegmRes_LT = ((float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter)); + float* pfCurrMeanFinalSegmRes_ST = ((float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter)); + ushort& nLastIntraDesc = *((ushort*)(m_oLastDescFrame.data + nDescIter)); + uchar& nLastColor = m_oLastColorFrame.data[nPxIter]; + const size_t nCurrColorDistThreshold = (size_t)(((*pfCurrDistThresholdFactor)*m_nMinColorDistThreshold) - ((!m_oUnstableRegionMask.data[nPxIter])*STAB_COLOR_DIST_OFFSET)) / 2; + const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(*pfCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (m_oUnstableRegionMask.data[nPxIter] * UNSTAB_DESC_DIST_OFFSET); + ushort nCurrInterDesc, nCurrIntraDesc; + LBSP::computeGrayscaleDescriptor(oInputImg, nCurrColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nCurrColor], nCurrIntraDesc); + m_oUnstableRegionMask.data[nPxIter] = ((*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[nPxIter]; + { + const size_t nColorDist = L1dist(nCurrColor, nBGColor); + if (nColorDist > nCurrColorDistThreshold) + goto failedcheck1ch; + const ushort& nBGIntraDesc = *((ushort*)(m_voBGDescSamples[nSampleIdx].data + nDescIter)); + const size_t nIntraDescDist = hdist(nCurrIntraDesc, nBGIntraDesc); + LBSP::computeGrayscaleDescriptor(oInputImg, nBGColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nBGColor], nCurrInterDesc); + const size_t nInterDescDist = hdist(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)L1dist(nLastColor, nCurrColor) / s_nColorMaxDataRange_1ch + (float)hdist(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[nPxIter] = 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 + nDescIter)) = nCurrIntraDesc; + m_voBGColorSamples[s_rand].data[nPxIter] = 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 + nDescIter)) = nCurrIntraDesc; + m_voBGColorSamples[s_rand].data[nPxIter] = nCurrColor; + } + int nSampleImgCoord_Y, nSampleImgCoord_X; + const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[nPxIter]; + if (bCurrUsing3x3Spread) + getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); + else + getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); + const size_t n_rand = rand(); + const size_t idx_rand_uchar = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + 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_oLastFGMask.data[nPxIter] || (std::min(*pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && oCurrFGMask.data[nPxIter])) { + 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[nPxIter]) + (*pfCurrVariationFactor) += FEEDBACK_V_INCR; + else if ((*pfCurrVariationFactor) > FEEDBACK_V_DECR) { + (*pfCurrVariationFactor) -= m_oLastFGMask.data[nPxIter] ? FEEDBACK_V_DECR / 4 : m_oUnstableRegionMask.data[nPxIter] ? 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(nCurrIntraDesc) >= 2) + ++nNonZeroDescCount; + nLastIntraDesc = nCurrIntraDesc; + nLastColor = nCurrColor; + } + } + else { //m_nImgChannels==3 + for (size_t nModelIter = 0; nModelIter < m_nTotRelevantPxCount; ++nModelIter) { + const size_t nPxIter = m_aPxIdxLUT[nModelIter]; + const int nCurrImgCoord_X = m_aPxInfoLUT[nPxIter].nImgCoord_X; + const int nCurrImgCoord_Y = m_aPxInfoLUT[nPxIter].nImgCoord_Y; + const size_t nPxIterRGB = nPxIter * 3; + const size_t nDescIterRGB = nPxIterRGB * 2; + const size_t nFloatIter = nPxIter * 4; + const uchar* const anCurrColor = oInputImg.data + nPxIterRGB; + size_t nMinTotDescDist = s_nDescMaxDataRange_3ch; + size_t nMinTotSumDist = s_nColorMaxDataRange_3ch; + float* pfCurrDistThresholdFactor = (float*)(m_oDistThresholdFrame.data + nFloatIter); + float* pfCurrVariationFactor = (float*)(m_oVariationModulatorFrame.data + nFloatIter); + float* pfCurrLearningRate = ((float*)(m_oUpdateRateFrame.data + nFloatIter)); + float* pfCurrMeanLastDist = ((float*)(m_oMeanLastDistFrame.data + nFloatIter)); + float* pfCurrMeanMinDist_LT = ((float*)(m_oMeanMinDistFrame_LT.data + nFloatIter)); + float* pfCurrMeanMinDist_ST = ((float*)(m_oMeanMinDistFrame_ST.data + nFloatIter)); + float* pfCurrMeanRawSegmRes_LT = ((float*)(m_oMeanRawSegmResFrame_LT.data + nFloatIter)); + float* pfCurrMeanRawSegmRes_ST = ((float*)(m_oMeanRawSegmResFrame_ST.data + nFloatIter)); + float* pfCurrMeanFinalSegmRes_LT = ((float*)(m_oMeanFinalSegmResFrame_LT.data + nFloatIter)); + float* pfCurrMeanFinalSegmRes_ST = ((float*)(m_oMeanFinalSegmResFrame_ST.data + nFloatIter)); + ushort* anLastIntraDesc = ((ushort*)(m_oLastDescFrame.data + nDescIterRGB)); + uchar* anLastColor = m_oLastColorFrame.data + nPxIterRGB; + const size_t nCurrColorDistThreshold = (size_t)(((*pfCurrDistThresholdFactor)*m_nMinColorDistThreshold) - ((!m_oUnstableRegionMask.data[nPxIter])*STAB_COLOR_DIST_OFFSET)); + const size_t nCurrDescDistThreshold = ((size_t)1 << ((size_t)floor(*pfCurrDistThresholdFactor + 0.5f))) + m_nDescDistThresholdOffset + (m_oUnstableRegionMask.data[nPxIter] * 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, nCurrImgCoord_X, nCurrImgCoord_Y, anCurrIntraLBSPThresholds, anCurrIntraDesc); + m_oUnstableRegionMask.data[nPxIter] = ((*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 + nDescIterRGB); + const uchar* const anBGColor = m_voBGColorSamples[nSampleIdx].data + nPxIterRGB; + size_t nTotDescDist = 0; + size_t nTotSumDist = 0; + for (size_t c = 0; c < 3; ++c) { + const size_t nColorDist = L1dist(anCurrColor[c], anBGColor[c]); + if (nColorDist > nCurrSCColorDistThreshold) + goto failedcheck3ch; + const size_t nIntraDescDist = hdist(anCurrIntraDesc[c], anBGIntraDesc[c]); + LBSP::computeSingleRGBDescriptor(oInputImg, anBGColor[c], nCurrImgCoord_X, nCurrImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[anBGColor[c]], anCurrInterDesc[c]); + const size_t nInterDescDist = hdist(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<3>(anLastColor, anCurrColor) / s_nColorMaxDataRange_3ch + (float)hdist<3>(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[nPxIter] = 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 + nDescIterRGB + 2 * c)) = anCurrIntraDesc[c]; + *(m_voBGColorSamples[s_rand].data + nPxIterRGB + 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 + nDescIterRGB + 2 * c)) = anCurrIntraDesc[c]; + *(m_voBGColorSamples[s_rand].data + nPxIterRGB + c) = anCurrColor[c]; + } + } + int nSampleImgCoord_Y, nSampleImgCoord_X; + const bool bCurrUsing3x3Spread = m_bUse3x3Spread && !m_oUnstableRegionMask.data[nPxIter]; + if (bCurrUsing3x3Spread) + getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); + else + getRandNeighborPosition_5x5(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize); + const size_t n_rand = rand(); + const size_t idx_rand_uchar = m_oImgSize.width*nSampleImgCoord_Y + nSampleImgCoord_X; + 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_oLastFGMask.data[nPxIter] || (std::min(*pfCurrMeanMinDist_LT, *pfCurrMeanMinDist_ST) < UNSTABLE_REG_RATIO_MIN && oCurrFGMask.data[nPxIter])) { + 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[nPxIter]) + (*pfCurrVariationFactor) += FEEDBACK_V_INCR; + else if ((*pfCurrVariationFactor) > FEEDBACK_V_DECR) { + (*pfCurrVariationFactor) -= m_oLastFGMask.data[nPxIter] ? FEEDBACK_V_DECR / 4 : m_oUnstableRegionMask.data[nPxIter] ? 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<3>(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; - cv::Point dbgpt(nDebugCoordX, nDebugCoordY); - cv::Mat oMeanMinDistFrameNormalized; m_oMeanMinDistFrame_ST.copyTo(oMeanMinDistFrameNormalized); - cv::circle(oMeanMinDistFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); - cv::resize(oMeanMinDistFrameNormalized, oMeanMinDistFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("d_min(x)", oMeanMinDistFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " d_min(" << dbgpt << ") = " << m_oMeanMinDistFrame_ST.at<float>(dbgpt) << std::endl; - cv::Mat oMeanLastDistFrameNormalized; m_oMeanLastDistFrame.copyTo(oMeanLastDistFrameNormalized); - cv::circle(oMeanLastDistFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); - cv::resize(oMeanLastDistFrameNormalized, oMeanLastDistFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("d_last(x)", oMeanLastDistFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " d_last(" << dbgpt << ") = " << m_oMeanLastDistFrame.at<float>(dbgpt) << std::endl; - cv::Mat oMeanRawSegmResFrameNormalized; m_oMeanRawSegmResFrame_ST.copyTo(oMeanRawSegmResFrameNormalized); - cv::circle(oMeanRawSegmResFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); - cv::resize(oMeanRawSegmResFrameNormalized, oMeanRawSegmResFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("s_avg(x)", oMeanRawSegmResFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " s_avg(" << dbgpt << ") = " << m_oMeanRawSegmResFrame_ST.at<float>(dbgpt) << std::endl; - cv::Mat oMeanFinalSegmResFrameNormalized; m_oMeanFinalSegmResFrame_ST.copyTo(oMeanFinalSegmResFrameNormalized); - cv::circle(oMeanFinalSegmResFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); - cv::resize(oMeanFinalSegmResFrameNormalized, oMeanFinalSegmResFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("z_avg(x)", oMeanFinalSegmResFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " z_avg(" << dbgpt << ") = " << m_oMeanFinalSegmResFrame_ST.at<float>(dbgpt) << std::endl; - cv::Mat oDistThresholdFrameNormalized; m_oDistThresholdFrame.convertTo(oDistThresholdFrameNormalized, CV_32FC1, 0.25f, -0.25f); - cv::circle(oDistThresholdFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); - cv::resize(oDistThresholdFrameNormalized, oDistThresholdFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("r(x)", oDistThresholdFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " r(" << dbgpt << ") = " << m_oDistThresholdFrame.at<float>(dbgpt) << std::endl; - cv::Mat oVariationModulatorFrameNormalized; cv::normalize(m_oVariationModulatorFrame, oVariationModulatorFrameNormalized, 0, 255, cv::NORM_MINMAX, CV_8UC1); - cv::circle(oVariationModulatorFrameNormalized, dbgpt, 5, cv::Scalar(255)); - cv::resize(oVariationModulatorFrameNormalized, oVariationModulatorFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("v(x)", oVariationModulatorFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " v(" << dbgpt << ") = " << m_oVariationModulatorFrame.at<float>(dbgpt) << std::endl; - cv::Mat oUpdateRateFrameNormalized; m_oUpdateRateFrame.convertTo(oUpdateRateFrameNormalized, CV_32FC1, 1.0f / FEEDBACK_T_UPPER, -FEEDBACK_T_LOWER / FEEDBACK_T_UPPER); - cv::circle(oUpdateRateFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); - cv::resize(oUpdateRateFrameNormalized, oUpdateRateFrameNormalized, DEFAULT_FRAME_SIZE); - cv::imshow("t(x)", oUpdateRateFrameNormalized); - std::cout << std::fixed << std::setprecision(5) << " t(" << dbgpt << ") = " << m_oUpdateRateFrame.at<float>(dbgpt) << std::endl; + std::cout << std::endl; + cv::Point dbgpt(nDebugCoordX, nDebugCoordY); + cv::Mat oMeanMinDistFrameNormalized; m_oMeanMinDistFrame_ST.copyTo(oMeanMinDistFrameNormalized); + cv::circle(oMeanMinDistFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); + cv::resize(oMeanMinDistFrameNormalized, oMeanMinDistFrameNormalized, DEFAULT_FRAME_SIZE); + cv::imshow("d_min(x)", oMeanMinDistFrameNormalized); + std::cout << std::fixed << std::setprecision(5) << " d_min(" << dbgpt << ") = " << m_oMeanMinDistFrame_ST.at<float>(dbgpt) << std::endl; + cv::Mat oMeanLastDistFrameNormalized; m_oMeanLastDistFrame.copyTo(oMeanLastDistFrameNormalized); + cv::circle(oMeanLastDistFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); + cv::resize(oMeanLastDistFrameNormalized, oMeanLastDistFrameNormalized, DEFAULT_FRAME_SIZE); + cv::imshow("d_last(x)", oMeanLastDistFrameNormalized); + std::cout << std::fixed << std::setprecision(5) << " d_last(" << dbgpt << ") = " << m_oMeanLastDistFrame.at<float>(dbgpt) << std::endl; + cv::Mat oMeanRawSegmResFrameNormalized; m_oMeanRawSegmResFrame_ST.copyTo(oMeanRawSegmResFrameNormalized); + cv::circle(oMeanRawSegmResFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); + cv::resize(oMeanRawSegmResFrameNormalized, oMeanRawSegmResFrameNormalized, DEFAULT_FRAME_SIZE); + cv::imshow("s_avg(x)", oMeanRawSegmResFrameNormalized); + std::cout << std::fixed << std::setprecision(5) << " s_avg(" << dbgpt << ") = " << m_oMeanRawSegmResFrame_ST.at<float>(dbgpt) << std::endl; + cv::Mat oMeanFinalSegmResFrameNormalized; m_oMeanFinalSegmResFrame_ST.copyTo(oMeanFinalSegmResFrameNormalized); + cv::circle(oMeanFinalSegmResFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); + cv::resize(oMeanFinalSegmResFrameNormalized, oMeanFinalSegmResFrameNormalized, DEFAULT_FRAME_SIZE); + cv::imshow("z_avg(x)", oMeanFinalSegmResFrameNormalized); + std::cout << std::fixed << std::setprecision(5) << " z_avg(" << dbgpt << ") = " << m_oMeanFinalSegmResFrame_ST.at<float>(dbgpt) << std::endl; + cv::Mat oDistThresholdFrameNormalized; m_oDistThresholdFrame.convertTo(oDistThresholdFrameNormalized, CV_32FC1, 0.25f, -0.25f); + cv::circle(oDistThresholdFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); + cv::resize(oDistThresholdFrameNormalized, oDistThresholdFrameNormalized, DEFAULT_FRAME_SIZE); + cv::imshow("r(x)", oDistThresholdFrameNormalized); + std::cout << std::fixed << std::setprecision(5) << " r(" << dbgpt << ") = " << m_oDistThresholdFrame.at<float>(dbgpt) << std::endl; + cv::Mat oVariationModulatorFrameNormalized; cv::normalize(m_oVariationModulatorFrame, oVariationModulatorFrameNormalized, 0, 255, cv::NORM_MINMAX, CV_8UC1); + cv::circle(oVariationModulatorFrameNormalized, dbgpt, 5, cv::Scalar(255)); + cv::resize(oVariationModulatorFrameNormalized, oVariationModulatorFrameNormalized, DEFAULT_FRAME_SIZE); + cv::imshow("v(x)", oVariationModulatorFrameNormalized); + std::cout << std::fixed << std::setprecision(5) << " v(" << dbgpt << ") = " << m_oVariationModulatorFrame.at<float>(dbgpt) << std::endl; + cv::Mat oUpdateRateFrameNormalized; m_oUpdateRateFrame.convertTo(oUpdateRateFrameNormalized, CV_32FC1, 1.0f / FEEDBACK_T_UPPER, -FEEDBACK_T_LOWER / FEEDBACK_T_UPPER); + cv::circle(oUpdateRateFrameNormalized, dbgpt, 5, cv::Scalar(1.0f)); + cv::resize(oUpdateRateFrameNormalized, oUpdateRateFrameNormalized, DEFAULT_FRAME_SIZE); + cv::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 - cv::bitwise_xor(oCurrFGMask, m_oLastRawFGMask, m_oCurrRawFGBlinkMask); - cv::bitwise_or(m_oCurrRawFGBlinkMask, m_oLastRawFGBlinkMask, m_oBlinksFrame); - m_oCurrRawFGBlinkMask.copyTo(m_oLastRawFGBlinkMask); - oCurrFGMask.copyTo(m_oLastRawFGMask); - cv::morphologyEx(oCurrFGMask, m_oFGMask_PreFlood, cv::MORPH_CLOSE, cv::Mat()); - m_oFGMask_PreFlood.copyTo(m_oFGMask_FloodedHoles); - cv::floodFill(m_oFGMask_FloodedHoles, cv::Point(0, 0), UCHAR_MAX); - cv::bitwise_not(m_oFGMask_FloodedHoles, m_oFGMask_FloodedHoles); - cv::erode(m_oFGMask_PreFlood, m_oFGMask_PreFlood, cv::Mat(), cv::Point(-1, -1), 3); - cv::bitwise_or(oCurrFGMask, m_oFGMask_FloodedHoles, oCurrFGMask); - cv::bitwise_or(oCurrFGMask, m_oFGMask_PreFlood, oCurrFGMask); - cv::medianBlur(oCurrFGMask, m_oLastFGMask, m_nMedianBlurKernelSize); - cv::dilate(m_oLastFGMask, m_oLastFGMask_dilated, cv::Mat(), cv::Point(-1, -1), 3); - cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); - cv::bitwise_not(m_oLastFGMask_dilated, m_oLastFGMask_dilated_inverted); - cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); - m_oLastFGMask.copyTo(oCurrFGMask); - cv::addWeighted(m_oMeanFinalSegmResFrame_LT, (1.0f - fRollAvgFactor_LT), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_LT, 0, m_oMeanFinalSegmResFrame_LT, CV_32F); - cv::addWeighted(m_oMeanFinalSegmResFrame_ST, (1.0f - fRollAvgFactor_ST), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_ST, 0, m_oMeanFinalSegmResFrame_ST, CV_32F); - const float fCurrNonZeroDescRatio = (float)nNonZeroDescCount / m_nTotRelevantPxCount; - 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] > cv::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] < cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + UCHAR_MAX*m_fRelLBSPThreshold)) - ++m_anLBSPThreshold_8bitLUT[t]; - } - m_fLastNonZeroDescRatio = fCurrNonZeroDescRatio; - if (m_bLearningRateScalingEnabled) { - cv::resize(oInputImg, m_oDownSampledFrame_MotionAnalysis, m_oDownSampledFrameSize, 0, 0, cv::INTER_AREA); - cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, m_oMeanDownSampledLastDistFrame_LT, fRollAvgFactor_LT); - cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, 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_MIN_COLOR_DIFF_THRESHOLD && m_nModelResetCooldown == 0) { - m_nFramesSinceLastReset = 0; - refreshModel(0.1f); // reset 10% of the bg model - m_nModelResetCooldown = m_nSamplesForMovingAvgs / 4; - m_oUpdateRateFrame = cv::Scalar(1.0f); + cv::bitwise_xor(oCurrFGMask, m_oLastRawFGMask, m_oCurrRawFGBlinkMask); + cv::bitwise_or(m_oCurrRawFGBlinkMask, m_oLastRawFGBlinkMask, m_oBlinksFrame); + m_oCurrRawFGBlinkMask.copyTo(m_oLastRawFGBlinkMask); + oCurrFGMask.copyTo(m_oLastRawFGMask); + cv::morphologyEx(oCurrFGMask, m_oFGMask_PreFlood, cv::MORPH_CLOSE, cv::Mat()); + m_oFGMask_PreFlood.copyTo(m_oFGMask_FloodedHoles); + cv::floodFill(m_oFGMask_FloodedHoles, cv::Point(0, 0), UCHAR_MAX); + cv::bitwise_not(m_oFGMask_FloodedHoles, m_oFGMask_FloodedHoles); + cv::erode(m_oFGMask_PreFlood, m_oFGMask_PreFlood, cv::Mat(), cv::Point(-1, -1), 3); + cv::bitwise_or(oCurrFGMask, m_oFGMask_FloodedHoles, oCurrFGMask); + cv::bitwise_or(oCurrFGMask, m_oFGMask_PreFlood, oCurrFGMask); + cv::medianBlur(oCurrFGMask, m_oLastFGMask, m_nMedianBlurKernelSize); + cv::dilate(m_oLastFGMask, m_oLastFGMask_dilated, cv::Mat(), cv::Point(-1, -1), 3); + cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); + cv::bitwise_not(m_oLastFGMask_dilated, m_oLastFGMask_dilated_inverted); + cv::bitwise_and(m_oBlinksFrame, m_oLastFGMask_dilated_inverted, m_oBlinksFrame); + m_oLastFGMask.copyTo(oCurrFGMask); + cv::addWeighted(m_oMeanFinalSegmResFrame_LT, (1.0f - fRollAvgFactor_LT), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_LT, 0, m_oMeanFinalSegmResFrame_LT, CV_32F); + cv::addWeighted(m_oMeanFinalSegmResFrame_ST, (1.0f - fRollAvgFactor_ST), m_oLastFGMask, (1.0 / UCHAR_MAX)*fRollAvgFactor_ST, 0, m_oMeanFinalSegmResFrame_ST, CV_32F); + const float fCurrNonZeroDescRatio = (float)nNonZeroDescCount / m_nTotRelevantPxCount; + 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] > cv::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] < cv::saturate_cast<uchar>(m_nLBSPThresholdOffset + UCHAR_MAX*m_fRelLBSPThreshold)) + ++m_anLBSPThreshold_8bitLUT[t]; + } + m_fLastNonZeroDescRatio = fCurrNonZeroDescRatio; + if (m_bLearningRateScalingEnabled) { + cv::resize(oInputImg, m_oDownSampledFrame_MotionAnalysis, m_oDownSampledFrameSize, 0, 0, cv::INTER_AREA); + cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, m_oMeanDownSampledLastDistFrame_LT, fRollAvgFactor_LT); + cv::accumulateWeighted(m_oDownSampledFrame_MotionAnalysis, 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_MIN_COLOR_DIFF_THRESHOLD && m_nModelResetCooldown == 0) { + m_nFramesSinceLastReset = 0; + refreshModel(0.1f); // reset 10% of the bg model + m_nModelResetCooldown = m_nSamplesForMovingAvgs / 4; + m_oUpdateRateFrame = cv::Scalar(1.0f); + } + else + ++m_nFramesSinceLastReset; + } + else if (fCurrColorDiffRatio >= FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD * 2) { + m_nFramesSinceLastReset = 0; + m_bAutoModelResetEnabled = true; + } + if (fCurrColorDiffRatio >= FRAMELEVEL_MIN_COLOR_DIFF_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; + } } - else - ++m_nFramesSinceLastReset; - } - else if (fCurrColorDiffRatio >= FRAMELEVEL_MIN_COLOR_DIFF_THRESHOLD * 2) { - m_nFramesSinceLastReset = 0; - m_bAutoModelResetEnabled = true; - } - if (fCurrColorDiffRatio >= FRAMELEVEL_MIN_COLOR_DIFF_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 BackgroundSubtractorSuBSENSE::getBackgroundImage(cv::OutputArray backgroundImage) const { - CV_Assert(m_bInitialized); - cv::Mat oAvgBGImg = cv::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 nFloatIter = idx_nimg * 4; - float* oAvgBgImgPtr = (float*)(oAvgBGImg.data + nFloatIter); - 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; + void BackgroundSubtractorSuBSENSE::getBackgroundImage(cv::OutputArray backgroundImage) const { + CV_Assert(m_bInitialized); + cv::Mat oAvgBGImg = cv::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 nFloatIter = idx_nimg * 4; + float* oAvgBgImgPtr = (float*)(oAvgBGImg.data + nFloatIter); + 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); } - } - } - oAvgBGImg.convertTo(backgroundImage, CV_8U); -} -void BackgroundSubtractorSuBSENSE::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 nFloatIter = idx_ndesc * 2; - float* oAvgBgDescPtr = (float*)(oAvgBGDesc.data + nFloatIter); - 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(); + void BackgroundSubtractorSuBSENSE::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 nFloatIter = idx_ndesc * 2; + float* oAvgBgDescPtr = (float*)(oAvgBGDesc.data + nFloatIter); + 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); } } } - oAvgBGDesc.convertTo(backgroundDescImage, CV_16U); } diff --git a/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.h b/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.h index 48c0a0d89312820db330b17c08c4a11997a7e42f..9ec95e1e0825a24102ebbf34e2dff12470604b2b 100644 --- a/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.h +++ b/src/algorithms/LBSP/BackgroundSubtractorSuBSENSE.h @@ -2,112 +2,120 @@ #include "BackgroundSubtractorLBSP.h" -//! defines the default value for BackgroundSubtractorLBSP::m_fRelLBSPThreshold -#define BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD (0.333f) -//! defines the default value for BackgroundSubtractorSuBSENSE::m_nDescDistThresholdOffset -#define BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD_OFFSET (3) -//! defines the default value for BackgroundSubtractorSuBSENSE::m_nMinColorDistThreshold -#define BGSSUBSENSE_DEFAULT_MIN_COLOR_DIST_THRESHOLD (30) -//! defines the default value for BackgroundSubtractorSuBSENSE::m_nBGSamples -#define BGSSUBSENSE_DEFAULT_NB_BG_SAMPLES (50) -//! defines the default value for BackgroundSubtractorSuBSENSE::m_nRequiredBGSamples -#define BGSSUBSENSE_DEFAULT_REQUIRED_NB_BG_SAMPLES (2) -//! defines the default value for BackgroundSubtractorSuBSENSE::m_nSamplesForMovingAvgs -#define BGSSUBSENSE_DEFAULT_N_SAMPLES_FOR_MV_AVGS (100) +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + //! defines the default value for BackgroundSubtractorLBSP::m_fRelLBSPThreshold + const float BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD = 0.333f; + //! defines the default value for BackgroundSubtractorSuBSENSE::m_nDescDistThresholdOffset + const int BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD_OFFSET = 3; + //! defines the default value for BackgroundSubtractorSuBSENSE::m_nMinColorDistThreshold + const int BGSSUBSENSE_DEFAULT_MIN_COLOR_DIST_THRESHOLD = 30; + //! defines the default value for BackgroundSubtractorSuBSENSE::m_nBGSamples + const int BGSSUBSENSE_DEFAULT_NB_BG_SAMPLES = 50; + //! defines the default value for BackgroundSubtractorSuBSENSE::m_nRequiredBGSamples + const int BGSSUBSENSE_DEFAULT_REQUIRED_NB_BG_SAMPLES = 2; + //! defines the default value for BackgroundSubtractorSuBSENSE::m_nSamplesForMovingAvgs + const int BGSSUBSENSE_DEFAULT_N_SAMPLES_FOR_MV_AVGS = 100; -/*! - Self-Balanced Sensitivity segmenTER (SuBSENSE) change detection algorithm. + /*! + Self-Balanced Sensitivity segmenTER (SuBSENSE) change detection 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. + 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 parameters or on the algorithm itself, see P.-L. St-Charles et al., - "Flexible Background Subtraction With Self-Balanced Local Sensitivity", in CVPRW 2014. + For more details on the different parameters or 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 BackgroundSubtractorSuBSENSE : public BackgroundSubtractorLBSP { -public: - //! full constructor - BackgroundSubtractorSuBSENSE(float fRelLBSPThreshold = BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, - size_t nDescDistThresholdOffset = BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD_OFFSET, - size_t nMinColorDistThreshold = BGSSUBSENSE_DEFAULT_MIN_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); - //! default destructor - virtual ~BackgroundSubtractorSuBSENSE(); - //! (re)initiaization method; needs to be called before starting background subtraction - virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI); - //! refreshes all samples based on the last analyzed frame - virtual void refreshModel(float fSamplesRefreshFrac, bool bForceFGUpdate = false); - //! primary model update function; the learning param is used to override the internal learning thresholds (ignored when <= 0) - virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = 0); - //! returns a copy of the latest reconstructed background image - void getBackgroundImage(cv::OutputArray backgroundImage) const; - //! returns a copy of the latest reconstructed background descriptors image - void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; + This algorithm is currently NOT thread-safe. + */ + class BackgroundSubtractorSuBSENSE : public BackgroundSubtractorLBSP { + public: + //! full constructor + BackgroundSubtractorSuBSENSE(float fRelLBSPThreshold = BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD, + size_t nDescDistThresholdOffset = BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD_OFFSET, + size_t nMinColorDistThreshold = BGSSUBSENSE_DEFAULT_MIN_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); + //! default destructor + virtual ~BackgroundSubtractorSuBSENSE(); + //! (re)initiaization method; needs to be called before starting background subtraction + virtual void initialize(const cv::Mat& oInitImg, const cv::Mat& oROI); + //! refreshes all samples based on the last analyzed frame + virtual void refreshModel(float fSamplesRefreshFrac, bool bForceFGUpdate = false); + //! primary model update function; the learning param is used to override the internal learning thresholds (ignored when <= 0) + virtual void apply(cv::InputArray image, cv::OutputArray fgmask, double learningRateOverride = 0); + //! returns a copy of the latest reconstructed background image + void getBackgroundImage(cv::OutputArray backgroundImage) const; + //! returns a copy of the latest reconstructed background descriptors image + void getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const; -protected: - //! absolute minimal color distance threshold ('R' or 'radius' in the original ViBe paper, used as the default/initial 'R(x)' value here) - const size_t m_nMinColorDistThreshold; - //! absolute descriptor distance threshold offset - const size_t m_nDescDistThresholdOffset; - //! 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; - //! last calculated non-zero desc ratio - float m_fLastNonZeroDescRatio; - //! 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 - cv::Size m_oDownSampledFrameSize; + protected: + //! absolute minimal color distance threshold ('R' or 'radius' in the original ViBe paper, used as the default/initial 'R(x)' value here) + const size_t m_nMinColorDistThreshold; + //! absolute descriptor distance threshold offset + const size_t m_nDescDistThresholdOffset; + //! 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; + //! last calculated non-zero desc ratio + float m_fLastNonZeroDescRatio; + //! 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 + cv::Size m_oDownSampledFrameSize; - //! background model pixel color intensity samples (equivalent to 'B(x)' in PBAS) - std::vector<cv::Mat> m_voBGColorSamples; - //! background model descriptors samples - std::vector<cv::Mat> m_voBGDescSamples; + //! background model pixel color intensity samples (equivalent to 'B(x)' in PBAS) + std::vector<cv::Mat> m_voBGColorSamples; + //! background model descriptors samples + std::vector<cv::Mat> m_voBGDescSamples; - //! per-pixel update rates ('T(x)' in PBAS, which contains pixel-level 'sigmas', as referred to in ViBe) - cv::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) - cv::Mat m_oDistThresholdFrame; - //! per-pixel distance variation modulators ('v(x)', relative value used to modulate 'R(x)' and 'T(x)' variations) - cv::Mat m_oVariationModulatorFrame; - //! per-pixel mean distances between consecutive frames ('D_last(x)', used to detect ghosts and high variation regions in the sequence) - cv::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)') - cv::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) - cv::Mat m_oMeanDownSampledLastDistFrame_LT, m_oMeanDownSampledLastDistFrame_ST; - //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) - cv::Mat m_oMeanRawSegmResFrame_LT, m_oMeanRawSegmResFrame_ST; - //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) - cv::Mat m_oMeanFinalSegmResFrame_LT, m_oMeanFinalSegmResFrame_ST; - //! a lookup map used to keep track of unstable regions (based on segm. noise & local dist. thresholds) - cv::Mat m_oUnstableRegionMask; - //! per-pixel blink detection map ('Z(x)') - cv::Mat m_oBlinksFrame; - //! pre-allocated matrix used to downsample the input frame when needed - cv::Mat m_oDownSampledFrame_MotionAnalysis; - //! the foreground mask generated by the method at [t-1] (without post-proc, used for blinking px detection) - cv::Mat m_oLastRawFGMask; - - //! pre-allocated CV_8UC1 matrices used to speed up morph ops - cv::Mat m_oFGMask_PreFlood; - cv::Mat m_oFGMask_FloodedHoles; - cv::Mat m_oLastFGMask_dilated; - cv::Mat m_oLastFGMask_dilated_inverted; - cv::Mat m_oCurrRawFGBlinkMask; - cv::Mat m_oLastRawFGBlinkMask; -}; + //! per-pixel update rates ('T(x)' in PBAS, which contains pixel-level 'sigmas', as referred to in ViBe) + cv::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) + cv::Mat m_oDistThresholdFrame; + //! per-pixel distance variation modulators ('v(x)', relative value used to modulate 'R(x)' and 'T(x)' variations) + cv::Mat m_oVariationModulatorFrame; + //! per-pixel mean distances between consecutive frames ('D_last(x)', used to detect ghosts and high variation regions in the sequence) + cv::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)') + cv::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) + cv::Mat m_oMeanDownSampledLastDistFrame_LT, m_oMeanDownSampledLastDistFrame_ST; + //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) + cv::Mat m_oMeanRawSegmResFrame_LT, m_oMeanRawSegmResFrame_ST; + //! per-pixel mean raw segmentation results (used to detect unstable segmentation regions) + cv::Mat m_oMeanFinalSegmResFrame_LT, m_oMeanFinalSegmResFrame_ST; + //! a lookup map used to keep track of unstable regions (based on segm. noise & local dist. thresholds) + cv::Mat m_oUnstableRegionMask; + //! per-pixel blink detection map ('Z(x)') + cv::Mat m_oBlinksFrame; + //! pre-allocated matrix used to downsample the input frame when needed + cv::Mat m_oDownSampledFrame_MotionAnalysis; + //! the foreground mask generated by the method at [t-1] (without post-proc, used for blinking px detection) + cv::Mat m_oLastRawFGMask; + //! pre-allocated CV_8UC1 matrices used to speed up morph ops + cv::Mat m_oFGMask_PreFlood; + cv::Mat m_oFGMask_FloodedHoles; + cv::Mat m_oLastFGMask_dilated; + cv::Mat m_oLastFGMask_dilated_inverted; + cv::Mat m_oCurrRawFGBlinkMask; + cv::Mat m_oLastRawFGBlinkMask; + }; + } + } +} diff --git a/src/algorithms/LBSP/DistanceUtils.h b/src/algorithms/LBSP/DistanceUtils.h index 124d4bcc73009bce8a31a1ed16e0bed826792b81..59a0fe1b96cd248b7811ab7a2857227fed6a2e9a 100644 --- a/src/algorithms/LBSP/DistanceUtils.h +++ b/src/algorithms/LBSP/DistanceUtils.h @@ -1,316 +1,326 @@ #pragma once +// opencv legacy includes #include <opencv2/core/types_c.h> -//! computes the L1 distance between two integer values -template<typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type L1dist(T a, T b) { - return (size_t)abs((int)a-b); -} - -//! computes the L1 distance between two float values -template<typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type L1dist(T a, T b) { - return fabs((float)a-(float)b); -} - -//! computes the L1 distance between two generic arrays -template<size_t nChannels, typename T> static inline auto L1dist(const T* a, const T* b) -> decltype(L1dist(*a,*b)) { - decltype(L1dist(*a,*b)) oResult = 0; - for(size_t c=0; c<nChannels; ++c) - oResult += L1dist(a[c],b[c]); - return oResult; -} - -//! computes the L1 distance between two generic arrays -template<size_t nChannels, typename T> static inline auto L1dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L1dist<nChannels>(a,b)) { - decltype(L1dist<nChannels>(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) - if(m[i]) - oResult += L1dist<nChannels>(a+n,b+n); - } - else { - for(size_t n=0; n<nTotElements; n+=nChannels) - oResult += L1dist<nChannels>(a+n,b+n); - } - return oResult; -} - -//! computes the L1 distance between two generic arrays -template<typename T> static inline auto L1dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L1dist<3>(a,b,nElements,m)) { - CV_Assert(nChannels>0 && nChannels<=4); - switch(nChannels) { - case 1: return L1dist<1>(a,b,nElements,m); - case 2: return L1dist<2>(a,b,nElements,m); - case 3: return L1dist<3>(a,b,nElements,m); - case 4: return L1dist<4>(a,b,nElements,m); - default: return 0; +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + //! computes the L1 distance between two integer values + template<typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type L1dist(T a, T b) { + return (size_t)abs((int)a-b); + } + + //! computes the L1 distance between two float values + template<typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type L1dist(T a, T b) { + return fabs((float)a-(float)b); + } + + //! computes the L1 distance between two generic arrays + template<size_t nChannels, typename T> static inline auto L1dist(const T* a, const T* b) -> decltype(L1dist(*a,*b)) { + decltype(L1dist(*a,*b)) oResult = 0; + for(size_t c=0; c<nChannels; ++c) + oResult += L1dist(a[c],b[c]); + return oResult; + } + + //! computes the L1 distance between two generic arrays + template<size_t nChannels, typename T> static inline auto L1dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L1dist<nChannels>(a,b)) { + decltype(L1dist<nChannels>(a,b)) oResult = 0; + size_t nTotElements = nElements*nChannels; + if(m) { + for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) + if(m[i]) + oResult += L1dist<nChannels>(a+n,b+n); + } + else { + for(size_t n=0; n<nTotElements; n+=nChannels) + oResult += L1dist<nChannels>(a+n,b+n); + } + return oResult; + } + + //! computes the L1 distance between two generic arrays + template<typename T> static inline auto L1dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L1dist<3>(a,b,nElements,m)) { + CV_Assert(nChannels>0 && nChannels<=4); + switch(nChannels) { + case 1: return L1dist<1>(a,b,nElements,m); + case 2: return L1dist<2>(a,b,nElements,m); + case 3: return L1dist<3>(a,b,nElements,m); + case 4: return L1dist<4>(a,b,nElements,m); + default: return 0; + } + } + + //! computes the L1 distance between two opencv vectors + template<size_t nChannels, typename T> static inline auto L1dist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(L1dist<nChannels,T>((T*)(0),(T*)(0))) { + T a_array[nChannels], b_array[nChannels]; + for(size_t c=0; c<nChannels; ++c) { + a_array[c] = a[(int)c]; + b_array[c] = b[(int)c]; + } + return L1dist<nChannels>(a_array,b_array); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + + //! computes the squared L2 distance between two generic variables + template<typename T> static inline auto L2sqrdist(T a, T b) -> decltype(L1dist(a,b)) { + auto oResult = L1dist(a,b); + return oResult*oResult; + } + + //! computes the squared L2 distance between two generic arrays + template<size_t nChannels, typename T> static inline auto L2sqrdist(const T* a, const T* b) -> decltype(L2sqrdist(*a,*b)) { + decltype(L2sqrdist(*a,*b)) oResult = 0; + for(size_t c=0; c<nChannels; ++c) + oResult += L2sqrdist(a[c],b[c]); + return oResult; + } + + //! computes the squared L2 distance between two generic arrays + template<size_t nChannels, typename T> static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L2sqrdist<nChannels>(a,b)) { + decltype(L2sqrdist<nChannels>(a,b)) oResult = 0; + size_t nTotElements = nElements*nChannels; + if(m) { + for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) + if(m[i]) + oResult += L2sqrdist<nChannels>(a+n,b+n); + } + else { + for(size_t n=0; n<nTotElements; n+=nChannels) + oResult += L2sqrdist<nChannels>(a+n,b+n); + } + return oResult; + } + + //! computes the squared L2 distance between two generic arrays + template<typename T> static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L2sqrdist<3>(a,b,nElements,m)) { + CV_Assert(nChannels>0 && nChannels<=4); + switch(nChannels) { + case 1: return L2sqrdist<1>(a,b,nElements,m); + case 2: return L2sqrdist<2>(a,b,nElements,m); + case 3: return L2sqrdist<3>(a,b,nElements,m); + case 4: return L2sqrdist<4>(a,b,nElements,m); + default: return 0; + } + } + + //! computes the squared L2 distance between two opencv vectors + template<size_t nChannels, typename T> static inline auto L2sqrdist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(L2sqrdist<nChannels,T>((T*)(0),(T*)(0))) { + T a_array[nChannels], b_array[nChannels]; + for(size_t c=0; c<nChannels; ++c) { + a_array[c] = a[(int)c]; + b_array[c] = b[(int)c]; + } + return L2sqrdist<nChannels>(a_array,b_array); + } + + //! computes the L2 distance between two generic arrays + template<size_t nChannels, typename T> static inline float L2dist(const T* a, const T* b) { + decltype(L2sqrdist(*a,*b)) oResult = 0; + for(size_t c=0; c<nChannels; ++c) + oResult += L2sqrdist(a[c],b[c]); + return sqrt((float)oResult); + } + + //! computes the L2 distance between two generic arrays + template<size_t nChannels, typename T> static inline float L2dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) { + decltype(L2sqrdist<nChannels>(a,b)) oResult = 0; + size_t nTotElements = nElements*nChannels; + if(m) { + for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) + if(m[i]) + oResult += L2sqrdist<nChannels>(a+n,b+n); + } + else { + for(size_t n=0; n<nTotElements; n+=nChannels) + oResult += L2sqrdist<nChannels>(a+n,b+n); + } + return sqrt((float)oResult); + } + + //! computes the squared L2 distance between two generic arrays + template<typename T> static inline float L2dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) { + CV_Assert(nChannels>0 && nChannels<=4); + switch(nChannels) { + case 1: return L2dist<1>(a,b,nElements,m); + case 2: return L2dist<2>(a,b,nElements,m); + case 3: return L2dist<3>(a,b,nElements,m); + case 4: return L2dist<4>(a,b,nElements,m); + default: return 0; + } + } + + //! computes the L2 distance between two opencv vectors + template<size_t nChannels, typename T> static inline float L2dist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) { + T a_array[nChannels], b_array[nChannels]; + for(size_t c=0; c<nChannels; ++c) { + a_array[c] = a[(int)c]; + b_array[c] = b[(int)c]; + } + return L2dist<nChannels>(a_array,b_array); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + + //! computes the color distortion between two integer arrays + template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type cdist(const T* curr, const T* bg) { + static_assert(nChannels>1,"cdist: requires more than one channel"); + size_t curr_sqr = 0; + bool bSkip = true; + for(size_t c=0; c<nChannels; ++c) { + curr_sqr += curr[c]*curr[c]; + bSkip = bSkip&(bg[c]<=0); + } + if(bSkip) + return (size_t)sqrt((float)curr_sqr); + size_t bg_sqr = 0; + size_t mix = 0; + for(size_t c=0; c<nChannels; ++c) { + bg_sqr += bg[c]*bg[c]; + mix += curr[c]*bg[c]; + } + return (size_t)sqrt(curr_sqr-((float)(mix*mix)/bg_sqr)); + } + + //! computes the color distortion between two float arrays + template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type cdist(const T* curr, const T* bg) { + static_assert(nChannels>1,"cdist: requires more than one channel"); + float curr_sqr = 0; + bool bSkip = true; + for(size_t c=0; c<nChannels; ++c) { + curr_sqr += (float)curr[c]*curr[c]; + bSkip = bSkip&(bg[c]<=0); + } + if(bSkip) + return sqrt(curr_sqr); + float bg_sqr = 0; + float mix = 0; + for(size_t c=0; c<nChannels; ++c) { + bg_sqr += (float)bg[c]*bg[c]; + mix += (float)curr[c]*bg[c]; + } + return sqrt(curr_sqr-((mix*mix)/bg_sqr)); + } + + //! computes the color distortion between two generic arrays + template<size_t nChannels, typename T> static inline auto cdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(cdist<nChannels>(a,b)) { + decltype(cdist<nChannels>(a,b)) oResult = 0; + size_t nTotElements = nElements*nChannels; + if(m) { + for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) + if(m[i]) + oResult += cdist<nChannels>(a+n,b+n); + } + else { + for(size_t n=0; n<nTotElements; n+=nChannels) + oResult += cdist<nChannels>(a+n,b+n); + } + return oResult; + } + + //! computes the color distortion between two generic arrays + template<typename T> static inline auto cdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(cdist<3>(a,b,nElements,m)) { + CV_Assert(nChannels>1 && nChannels<=4); + switch(nChannels) { + case 2: return cdist<2>(a,b,nElements,m); + case 3: return cdist<3>(a,b,nElements,m); + case 4: return cdist<4>(a,b,nElements,m); + default: return 0; + } + } + + //! computes the color distortion between two opencv vectors + template<size_t nChannels, typename T> static inline auto cdist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(cdist<nChannels,T>((T*)(0),(T*)(0))) { + T a_array[nChannels], b_array[nChannels]; + for(size_t c=0; c<nChannels; ++c) { + a_array[c] = a[(int)c]; + b_array[c] = b[(int)c]; + } + return cdist<nChannels>(a_array,b_array); + } + + //! computes a color distortion-distance mix using two generic distances + template<typename T> static inline T cmixdist(T oL1Distance, T oCDistortion) { + return (oL1Distance/2+oCDistortion*4); + } + + //! computes a color distoirtion-distance mix using two generic arrays + template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type cmixdist(const T* curr, const T* bg) { + return cmixdist(L1dist<nChannels>(curr,bg),cdist<nChannels>(curr,bg)); + } + + template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type cmixdist(const T* curr, const T* bg) { + return cmixdist(L1dist<nChannels>(curr,bg),cdist<nChannels>(curr,bg)); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////// + + //! popcount LUT for 8-bit 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 an N-byte vector using an 8-bit popcount LUT + template<typename T> static inline size_t popcount(T x) { + size_t nBytes = sizeof(T); + size_t nResult = 0; + for(size_t l=0; l<nBytes; ++l) + nResult += popcount_LUT8[(uchar)(x>>l*8)]; + return nResult; + } + + //! computes the hamming distance between two N-byte vectors using an 8-bit popcount LUT + template<typename T> static inline size_t hdist(T a, T b) { + return popcount(a^b); + } + + //! computes the gradient magnitude distance between two N-byte vectors using an 8-bit popcount LUT + template<typename T> static inline size_t gdist(T a, T b) { + return L1dist(popcount(a),popcount(b)); + } + + //! computes the population count of a (nChannels*N)-byte vector using an 8-bit popcount LUT + template<size_t nChannels, typename T> static inline size_t popcount(const T* x) { + size_t nBytes = sizeof(T); + size_t nResult = 0; + for(size_t c=0; c<nChannels; ++c) + for(size_t l=0; l<nBytes; ++l) + nResult += popcount_LUT8[(uchar)(*(x+c)>>l*8)]; + return nResult; + } + + //! computes the hamming distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT + template<size_t nChannels, typename T> static inline size_t hdist(const T* a, const T* b) { + T xor_array[nChannels]; + for(size_t c=0; c<nChannels; ++c) + xor_array[c] = a[c]^b[c]; + return popcount<nChannels>(xor_array); + } + + //! computes the gradient magnitude distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT + template<size_t nChannels, typename T> static inline size_t gdist(const T* a, const T* b) { + return L1dist(popcount<nChannels>(a),popcount<nChannels>(b)); + } + } } } - -//! computes the L1 distance between two opencv vectors -template<size_t nChannels, typename T> static inline auto L1dist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(L1dist<nChannels,T>((T*)(0),(T*)(0))) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c<nChannels; ++c) { - a_array[c] = a[(int)c]; - b_array[c] = b[(int)c]; - } - return L1dist<nChannels>(a_array,b_array); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//! computes the squared L2 distance between two generic variables -template<typename T> static inline auto L2sqrdist(T a, T b) -> decltype(L1dist(a,b)) { - auto oResult = L1dist(a,b); - return oResult*oResult; -} - -//! computes the squared L2 distance between two generic arrays -template<size_t nChannels, typename T> static inline auto L2sqrdist(const T* a, const T* b) -> decltype(L2sqrdist(*a,*b)) { - decltype(L2sqrdist(*a,*b)) oResult = 0; - for(size_t c=0; c<nChannels; ++c) - oResult += L2sqrdist(a[c],b[c]); - return oResult; -} - -//! computes the squared L2 distance between two generic arrays -template<size_t nChannels, typename T> static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(L2sqrdist<nChannels>(a,b)) { - decltype(L2sqrdist<nChannels>(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) - if(m[i]) - oResult += L2sqrdist<nChannels>(a+n,b+n); - } - else { - for(size_t n=0; n<nTotElements; n+=nChannels) - oResult += L2sqrdist<nChannels>(a+n,b+n); - } - return oResult; -} - -//! computes the squared L2 distance between two generic arrays -template<typename T> static inline auto L2sqrdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(L2sqrdist<3>(a,b,nElements,m)) { - CV_Assert(nChannels>0 && nChannels<=4); - switch(nChannels) { - case 1: return L2sqrdist<1>(a,b,nElements,m); - case 2: return L2sqrdist<2>(a,b,nElements,m); - case 3: return L2sqrdist<3>(a,b,nElements,m); - case 4: return L2sqrdist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the squared L2 distance between two opencv vectors -template<size_t nChannels, typename T> static inline auto L2sqrdist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(L2sqrdist<nChannels,T>((T*)(0),(T*)(0))) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c<nChannels; ++c) { - a_array[c] = a[(int)c]; - b_array[c] = b[(int)c]; - } - return L2sqrdist<nChannels>(a_array,b_array); -} - -//! computes the L2 distance between two generic arrays -template<size_t nChannels, typename T> static inline float L2dist(const T* a, const T* b) { - decltype(L2sqrdist(*a,*b)) oResult = 0; - for(size_t c=0; c<nChannels; ++c) - oResult += L2sqrdist(a[c],b[c]); - return sqrt((float)oResult); -} - -//! computes the L2 distance between two generic arrays -template<size_t nChannels, typename T> static inline float L2dist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) { - decltype(L2sqrdist<nChannels>(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) - if(m[i]) - oResult += L2sqrdist<nChannels>(a+n,b+n); - } - else { - for(size_t n=0; n<nTotElements; n+=nChannels) - oResult += L2sqrdist<nChannels>(a+n,b+n); - } - return sqrt((float)oResult); -} - -//! computes the squared L2 distance between two generic arrays -template<typename T> static inline float L2dist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) { - CV_Assert(nChannels>0 && nChannels<=4); - switch(nChannels) { - case 1: return L2dist<1>(a,b,nElements,m); - case 2: return L2dist<2>(a,b,nElements,m); - case 3: return L2dist<3>(a,b,nElements,m); - case 4: return L2dist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the L2 distance between two opencv vectors -template<size_t nChannels, typename T> static inline float L2dist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c<nChannels; ++c) { - a_array[c] = a[(int)c]; - b_array[c] = b[(int)c]; - } - return L2dist<nChannels>(a_array,b_array); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//! computes the color distortion between two integer arrays -template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type cdist(const T* curr, const T* bg) { - static_assert(nChannels>1,"cdist: requires more than one channel"); - size_t curr_sqr = 0; - bool bSkip = true; - for(size_t c=0; c<nChannels; ++c) { - curr_sqr += curr[c]*curr[c]; - bSkip = bSkip&(bg[c]<=0); - } - if(bSkip) - return (size_t)sqrt((float)curr_sqr); - size_t bg_sqr = 0; - size_t mix = 0; - for(size_t c=0; c<nChannels; ++c) { - bg_sqr += bg[c]*bg[c]; - mix += curr[c]*bg[c]; - } - return (size_t)sqrt(curr_sqr-((float)(mix*mix)/bg_sqr)); -} - -//! computes the color distortion between two float arrays -template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type cdist(const T* curr, const T* bg) { - static_assert(nChannels>1,"cdist: requires more than one channel"); - float curr_sqr = 0; - bool bSkip = true; - for(size_t c=0; c<nChannels; ++c) { - curr_sqr += (float)curr[c]*curr[c]; - bSkip = bSkip&(bg[c]<=0); - } - if(bSkip) - return sqrt(curr_sqr); - float bg_sqr = 0; - float mix = 0; - for(size_t c=0; c<nChannels; ++c) { - bg_sqr += (float)bg[c]*bg[c]; - mix += (float)curr[c]*bg[c]; - } - return sqrt(curr_sqr-((mix*mix)/bg_sqr)); -} - -//! computes the color distortion between two generic arrays -template<size_t nChannels, typename T> static inline auto cdist(const T* a, const T* b, size_t nElements, const uchar* m=NULL) -> decltype(cdist<nChannels>(a,b)) { - decltype(cdist<nChannels>(a,b)) oResult = 0; - size_t nTotElements = nElements*nChannels; - if(m) { - for(size_t n=0,i=0; n<nTotElements; n+=nChannels,++i) - if(m[i]) - oResult += cdist<nChannels>(a+n,b+n); - } - else { - for(size_t n=0; n<nTotElements; n+=nChannels) - oResult += cdist<nChannels>(a+n,b+n); - } - return oResult; -} - -//! computes the color distortion between two generic arrays -template<typename T> static inline auto cdist(const T* a, const T* b, size_t nElements, size_t nChannels, const uchar* m=NULL) -> decltype(cdist<3>(a,b,nElements,m)) { - CV_Assert(nChannels>1 && nChannels<=4); - switch(nChannels) { - case 2: return cdist<2>(a,b,nElements,m); - case 3: return cdist<3>(a,b,nElements,m); - case 4: return cdist<4>(a,b,nElements,m); - default: return 0; - } -} - -//! computes the color distortion between two opencv vectors -template<size_t nChannels, typename T> static inline auto cdist_(const cv::Vec<T,nChannels>& a, const cv::Vec<T,nChannels>& b) -> decltype(cdist<nChannels,T>((T*)(0),(T*)(0))) { - T a_array[nChannels], b_array[nChannels]; - for(size_t c=0; c<nChannels; ++c) { - a_array[c] = a[(int)c]; - b_array[c] = b[(int)c]; - } - return cdist<nChannels>(a_array,b_array); -} - -//! computes a color distortion-distance mix using two generic distances -template<typename T> static inline T cmixdist(T oL1Distance, T oCDistortion) { - return (oL1Distance/2+oCDistortion*4); -} - -//! computes a color distoirtion-distance mix using two generic arrays -template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_integral<T>::value,size_t>::type cmixdist(const T* curr, const T* bg) { - return cmixdist(L1dist<nChannels>(curr,bg),cdist<nChannels>(curr,bg)); -} - -template<size_t nChannels, typename T> static inline typename std::enable_if<std::is_floating_point<T>::value,float>::type cmixdist(const T* curr, const T* bg) { - return cmixdist(L1dist<nChannels>(curr,bg),cdist<nChannels>(curr,bg)); -} - -/////////////////////////////////////////////////////////////////////////////////////////////////// - -//! popcount LUT for 8-bit 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 an N-byte vector using an 8-bit popcount LUT -template<typename T> static inline size_t popcount(T x) { - size_t nBytes = sizeof(T); - size_t nResult = 0; - for(size_t l=0; l<nBytes; ++l) - nResult += popcount_LUT8[(uchar)(x>>l*8)]; - return nResult; -} - -//! computes the hamming distance between two N-byte vectors using an 8-bit popcount LUT -template<typename T> static inline size_t hdist(T a, T b) { - return popcount(a^b); -} - -//! computes the gradient magnitude distance between two N-byte vectors using an 8-bit popcount LUT -template<typename T> static inline size_t gdist(T a, T b) { - return L1dist(popcount(a),popcount(b)); -} - -//! computes the population count of a (nChannels*N)-byte vector using an 8-bit popcount LUT -template<size_t nChannels, typename T> static inline size_t popcount(const T* x) { - size_t nBytes = sizeof(T); - size_t nResult = 0; - for(size_t c=0; c<nChannels; ++c) - for(size_t l=0; l<nBytes; ++l) - nResult += popcount_LUT8[(uchar)(*(x+c)>>l*8)]; - return nResult; -} - -//! computes the hamming distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT -template<size_t nChannels, typename T> static inline size_t hdist(const T* a, const T* b) { - T xor_array[nChannels]; - for(size_t c=0; c<nChannels; ++c) - xor_array[c] = a[c]^b[c]; - return popcount<nChannels>(xor_array); -} - -//! computes the gradient magnitude distance between two (nChannels*N)-byte vectors using an 8-bit popcount LUT -template<size_t nChannels, typename T> static inline size_t gdist(const T* a, const T* b) { - return L1dist(popcount<nChannels>(a),popcount<nChannels>(b)); -} diff --git a/src/algorithms/LBSP/LBSP.cpp b/src/algorithms/LBSP/LBSP.cpp index 5f5a57a68f76e9ed0b03222d5989c4bd938acf37..b965112cde62d8e1990e8f8adad914eedbfc3fff 100644 --- a/src/algorithms/LBSP/LBSP.cpp +++ b/src/algorithms/LBSP/LBSP.cpp @@ -1,318 +1,329 @@ #include "LBSP.h" -LBSP::LBSP(size_t nThreshold) - : m_bOnlyUsingAbsThreshold(true) - , m_fRelThreshold(0) // unused - , m_nThreshold(nThreshold) - , m_oRefImage() {} +//using namespace bgslibrary::algorithms::lbsp; -LBSP::LBSP(float fRelThreshold, size_t nThresholdOffset) - : m_bOnlyUsingAbsThreshold(false) - , m_fRelThreshold(fRelThreshold) - , m_nThreshold(nThresholdOffset) - , m_oRefImage() { - CV_Assert(m_fRelThreshold >= 0); -} +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + LBSP::LBSP(size_t nThreshold) + : m_bOnlyUsingAbsThreshold(true) + , m_fRelThreshold(0) // unused + , m_nThreshold(nThreshold) + , m_oRefImage() {} -LBSP::~LBSP() {} + LBSP::LBSP(float fRelThreshold, size_t nThresholdOffset) + : m_bOnlyUsingAbsThreshold(false) + , m_fRelThreshold(fRelThreshold) + , m_nThreshold(nThresholdOffset) + , m_oRefImage() { + CV_Assert(m_fRelThreshold >= 0); + } -void LBSP::read(const cv::FileNode& /*fn*/) { - // ... = fn["..."]; -} + LBSP::~LBSP() {} -void LBSP::write(cv::FileStorage& /*fs*/) const { - //fs << "..." << ...; -} + void LBSP::read(const cv::FileNode& /*fn*/) { + // ... = fn["..."]; + } -void LBSP::setReference(const cv::Mat& img) { - CV_DbgAssert(img.empty() || img.type() == CV_8UC1 || img.type() == CV_8UC3); - m_oRefImage = img; -} + void LBSP::write(cv::FileStorage& /*fs*/) const { + //fs << "..." << ...; + } -int LBSP::descriptorSize() const { - return DESC_SIZE; -} + void LBSP::setReference(const cv::Mat& img) { + CV_DbgAssert(img.empty() || img.type() == CV_8UC1 || img.type() == CV_8UC3); + m_oRefImage = img; + } -int LBSP::descriptorType() const { - return CV_16U; -} + int LBSP::descriptorSize() const { + return DESC_SIZE; + } -bool LBSP::isUsingRelThreshold() const { - return !m_bOnlyUsingAbsThreshold; -} + int LBSP::descriptorType() const { + return CV_16U; + } -float LBSP::getRelThreshold() const { - return m_fRelThreshold; -} + bool LBSP::isUsingRelThreshold() const { + return !m_bOnlyUsingAbsThreshold; + } -size_t LBSP::getAbsThreshold() const { - return m_nThreshold; -} + float LBSP::getRelThreshold() const { + return m_fRelThreshold; + } -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); + 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)); + } + } + 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; + 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 }; + } + } + 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); + 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)); + } + } + 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; + 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 }; + } + } + 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 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::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::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::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); - oOutput = cv::Scalar(0); - 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(desc1_ptr[j], desc2_ptr[j])); - } - } - else { //nChannels==3 - if (bForceMergeChannels) - oOutput.create(oDesc1.size(), CV_8UC1); - else - oOutput.create(oDesc1.size(), CV_8UC3); - oOutput = cv::Scalar::all(0); - 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) { - for (size_t n = 0; n < 3; ++n) { - const size_t idx2 = 3 * j + 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); + oOutput = cv::Scalar(0); + 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(desc1_ptr[j], desc2_ptr[j])); + } + } + else { //nChannels==3 if (bForceMergeChannels) - output_ptr[j] += (uchar)((fScaleFactor*hdist(desc1_ptr[idx2], desc2_ptr[idx2])) / 3); + oOutput.create(oDesc1.size(), CV_8UC1); else - output_ptr[idx2] = (uchar)(fScaleFactor*hdist(desc1_ptr[idx2], desc2_ptr[idx2])); + oOutput.create(oDesc1.size(), CV_8UC3); + oOutput = cv::Scalar::all(0); + 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) { + for (size_t n = 0; n < 3; ++n) { + const size_t idx2 = 3 * j + n; + if (bForceMergeChannels) + output_ptr[j] += (uchar)((fScaleFactor*hdist(desc1_ptr[idx2], desc2_ptr[idx2])) / 3); + else + output_ptr[idx2] = (uchar)(fScaleFactor*hdist(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); -} + void LBSP::validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize) { + cv::KeyPointsFilter::runByImageBorder(voKeypoints, oImgSize, PATCH_SIZE / 2); + } -void LBSP::validateROI(cv::Mat& oROI) { - CV_Assert(!oROI.empty() && oROI.type() == CV_8UC1); - cv::Mat oROI_new(oROI.size(), CV_8UC1, cv::Scalar_<uchar>(0)); - const size_t nBorderSize = PATCH_SIZE / 2; - const cv::Rect nROI_inner(nBorderSize, nBorderSize, oROI.cols - nBorderSize * 2, oROI.rows - nBorderSize * 2); - cv::Mat(oROI, nROI_inner).copyTo(cv::Mat(oROI_new, nROI_inner)); - oROI = oROI_new; + void LBSP::validateROI(cv::Mat& oROI) { + CV_Assert(!oROI.empty() && oROI.type() == CV_8UC1); + cv::Mat oROI_new(oROI.size(), CV_8UC1, cv::Scalar_<uchar>(0)); + const size_t nBorderSize = PATCH_SIZE / 2; + const cv::Rect nROI_inner(nBorderSize, nBorderSize, oROI.cols - nBorderSize * 2, oROI.rows - nBorderSize * 2); + cv::Mat(oROI, nROI_inner).copyTo(cv::Mat(oROI_new, nROI_inner)); + oROI = oROI_new; + } + } + } } diff --git a/src/algorithms/LBSP/LBSP.h b/src/algorithms/LBSP/LBSP.h index cab6a60206cfb897a88eadab2c64ae2911250233..6c8eb80256a0383f729ed8adfb5f909d7c809399 100644 --- a/src/algorithms/LBSP/LBSP.h +++ b/src/algorithms/LBSP/LBSP.h @@ -6,114 +6,123 @@ #include "DistanceUtils.h" -/*! - Local Binary Similarity Pattern (LBSP) feature extractor +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /*! + 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(...). + 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. + 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; + 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; + //! 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 (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* 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 (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, 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 function, used to filter out bad pixels in a ROI that would trigger out of bounds error because they're too close to the image border - static void validateROI(cv::Mat& oROI); - //! 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; + //! 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 function, used to filter out bad pixels in a ROI that would trigger out of bounds error because they're too close to the image border + static void validateROI(cv::Mat& oROI); + //! 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; + 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; -}; + const bool m_bOnlyUsingAbsThreshold; + const float m_fRelThreshold; + const size_t m_nThreshold; + cv::Mat m_oRefImage; + }; + } + } +} diff --git a/src/algorithms/LBSP/LBSP_.cpp b/src/algorithms/LBSP/LBSP_.cpp index 2cf2eb65bd6b1d060e79b76398fb61bfc93a881a..0116dbbeec54cafafc3200ce65072216b72951f9 100644 --- a/src/algorithms/LBSP/LBSP_.cpp +++ b/src/algorithms/LBSP/LBSP_.cpp @@ -1,318 +1,329 @@ #include "LBSP_.h" -LBSP_::LBSP_(size_t nThreshold) - : m_bOnlyUsingAbsThreshold(true) - , m_fRelThreshold(0) // unused - , m_nThreshold(nThreshold) - , m_oRefImage() {} +//using namespace bgslibrary::algorithms::lbsp; -LBSP_::LBSP_(float fRelThreshold, size_t nThresholdOffset) - : m_bOnlyUsingAbsThreshold(false) - , m_fRelThreshold(fRelThreshold) - , m_nThreshold(nThresholdOffset) - , m_oRefImage() { - CV_Assert(m_fRelThreshold >= 0); -} +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + LBSP_::LBSP_(size_t nThreshold) + : m_bOnlyUsingAbsThreshold(true) + , m_fRelThreshold(0) // unused + , m_nThreshold(nThreshold) + , m_oRefImage() {} -LBSP_::~LBSP_() {} + LBSP_::LBSP_(float fRelThreshold, size_t nThresholdOffset) + : m_bOnlyUsingAbsThreshold(false) + , m_fRelThreshold(fRelThreshold) + , m_nThreshold(nThresholdOffset) + , m_oRefImage() { + CV_Assert(m_fRelThreshold >= 0); + } -void LBSP_::read(const cv::FileNode& /*fn*/) { - // ... = fn["..."]; -} + LBSP_::~LBSP_() {} -void LBSP_::write(cv::FileStorage& /*fs*/) const { - //fs << "..." << ...; -} + void LBSP_::read(const cv::FileNode& /*fn*/) { + // ... = fn["..."]; + } -void LBSP_::setReference(const cv::Mat& img) { - CV_DbgAssert(img.empty() || img.type() == CV_8UC1 || img.type() == CV_8UC3); - m_oRefImage = img; -} + void LBSP_::write(cv::FileStorage& /*fs*/) const { + //fs << "..." << ...; + } -int LBSP_::descriptorSize() const { - return DESC_SIZE; -} + void LBSP_::setReference(const cv::Mat& img) { + CV_DbgAssert(img.empty() || img.type() == CV_8UC1 || img.type() == CV_8UC3); + m_oRefImage = img; + } -int LBSP_::descriptorType() const { - return CV_16U; -} + int LBSP_::descriptorSize() const { + return DESC_SIZE; + } -bool LBSP_::isUsingRelThreshold() const { - return !m_bOnlyUsingAbsThreshold; -} + int LBSP_::descriptorType() const { + return CV_16U; + } -float LBSP_::getRelThreshold() const { - return m_fRelThreshold; -} + bool LBSP_::isUsingRelThreshold() const { + return !m_bOnlyUsingAbsThreshold; + } -size_t LBSP_::getAbsThreshold() const { - return m_nThreshold; -} + float LBSP_::getRelThreshold() const { + return m_fRelThreshold; + } -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); + 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)); + } + } + 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; + 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 }; + } + } + 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); + 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)); + } + } + 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; + 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 }; + } + } + 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 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_::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_::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_::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); - oOutput = cv::Scalar(0); - 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(desc1_ptr[j], desc2_ptr[j])); - } - } - else { //nChannels==3 - if (bForceMergeChannels) - oOutput.create(oDesc1.size(), CV_8UC1); - else - oOutput.create(oDesc1.size(), CV_8UC3); - oOutput = cv::Scalar::all(0); - 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) { - for (size_t n = 0; n < 3; ++n) { - const size_t idx2 = 3 * j + 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); + oOutput = cv::Scalar(0); + 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(desc1_ptr[j], desc2_ptr[j])); + } + } + else { //nChannels==3 if (bForceMergeChannels) - output_ptr[j] += (uchar)((fScaleFactor*hdist(desc1_ptr[idx2], desc2_ptr[idx2])) / 3); + oOutput.create(oDesc1.size(), CV_8UC1); else - output_ptr[idx2] = (uchar)(fScaleFactor*hdist(desc1_ptr[idx2], desc2_ptr[idx2])); + oOutput.create(oDesc1.size(), CV_8UC3); + oOutput = cv::Scalar::all(0); + 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) { + for (size_t n = 0; n < 3; ++n) { + const size_t idx2 = 3 * j + n; + if (bForceMergeChannels) + output_ptr[j] += (uchar)((fScaleFactor*hdist(desc1_ptr[idx2], desc2_ptr[idx2])) / 3); + else + output_ptr[idx2] = (uchar)(fScaleFactor*hdist(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); -} + void LBSP_::validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize) { + cv::KeyPointsFilter::runByImageBorder(voKeypoints, oImgSize, PATCH_SIZE / 2); + } -void LBSP_::validateROI(cv::Mat& oROI) { - CV_Assert(!oROI.empty() && oROI.type() == CV_8UC1); - cv::Mat oROI_new(oROI.size(), CV_8UC1, cv::Scalar_<uchar>(0)); - const size_t nBorderSize = PATCH_SIZE / 2; - const cv::Rect nROI_inner(nBorderSize, nBorderSize, oROI.cols - nBorderSize * 2, oROI.rows - nBorderSize * 2); - cv::Mat(oROI, nROI_inner).copyTo(cv::Mat(oROI_new, nROI_inner)); - oROI = oROI_new; + void LBSP_::validateROI(cv::Mat& oROI) { + CV_Assert(!oROI.empty() && oROI.type() == CV_8UC1); + cv::Mat oROI_new(oROI.size(), CV_8UC1, cv::Scalar_<uchar>(0)); + const size_t nBorderSize = PATCH_SIZE / 2; + const cv::Rect nROI_inner(nBorderSize, nBorderSize, oROI.cols - nBorderSize * 2, oROI.rows - nBorderSize * 2); + cv::Mat(oROI, nROI_inner).copyTo(cv::Mat(oROI_new, nROI_inner)); + oROI = oROI_new; + } + } + } } diff --git a/src/algorithms/LBSP/LBSP_.h b/src/algorithms/LBSP/LBSP_.h index 25fd831eee97a8c6b791832c793613e24914ba6d..3b89a3736407413e726b228ba067c498edf37d5a 100644 --- a/src/algorithms/LBSP/LBSP_.h +++ b/src/algorithms/LBSP/LBSP_.h @@ -6,114 +6,123 @@ #include "DistanceUtils.h" -/*! - Local Binary Similarity Pattern (LBSP) feature extractor +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /*! + 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(...). + 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. + 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::Feature2D { -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; + This algorithm is currently NOT thread-safe. + */ + class LBSP_ : public cv::Feature2D { + 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; + //! 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 (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* 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 (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, 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 function, used to filter out bad pixels in a ROI that would trigger out of bounds error because they're too close to the image border - static void validateROI(cv::Mat& oROI); - //! 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; + //! 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 function, used to filter out bad pixels in a ROI that would trigger out of bounds error because they're too close to the image border + static void validateROI(cv::Mat& oROI); + //! 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; + 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; -}; + const bool m_bOnlyUsingAbsThreshold; + const float m_fRelThreshold; + const size_t m_nThreshold; + cv::Mat m_oRefImage; + }; + } + } +} diff --git a/src/algorithms/LBSP/RandUtils.h b/src/algorithms/LBSP/RandUtils.h index 69f4432a42ac46f8c964a83a93525d4fbfd6eba4..570d333f30c10bef209d0cb665af143e32752d1f 100644 --- a/src/algorithms/LBSP/RandUtils.h +++ b/src/algorithms/LBSP/RandUtils.h @@ -1,96 +1,105 @@ #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,}, -};*/ +namespace bgslibrary +{ + namespace algorithms + { + namespace lbsp + { + /*// 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,2)*512)' -static const int s_nSamplesInitPatternWidth = 7; -static const int s_nSamplesInitPatternHeight = 7; -static const int s_nSamplesInitPatternTot = 512; -static const int s_anSamplesInitPattern[s_nSamplesInitPatternHeight][s_nSamplesInitPatternWidth] = { - {2, 4, 6, 7, 6, 4, 2,}, - {4, 8, 12, 14, 12, 8, 4,}, - {6, 12, 21, 25, 21, 12, 6,}, - {7, 14, 25, 28, 25, 14, 7,}, - {6, 12, 21, 25, 21, 12, 6,}, - {4, 8, 12, 14, 12, 8, 4,}, - {2, 4, 6, 7, 6, 4, 2,}, -}; + // gaussian 7x7 pattern, based on 'floor(fspecial('gaussian',7,2)*512)' + static const int s_nSamplesInitPatternWidth = 7; + static const int s_nSamplesInitPatternHeight = 7; + static const int s_nSamplesInitPatternTot = 512; + static const int s_anSamplesInitPattern[s_nSamplesInitPatternHeight][s_nSamplesInitPatternWidth] = { + {2, 4, 6, 7, 6, 4, 2,}, + {4, 8, 12, 14, 12, 8, 4,}, + {6, 12, 21, 25, 21, 12, 6,}, + {7, 14, 25, 28, 25, 14, 7,}, + {6, 12, 21, 25, 21, 12, 6,}, + {4, 8, 12, 14, 12, 8, 4,}, + {2, 4, 6, 7, 6, 4, 2,}, + }; -//! 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; -} + //! 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}, -}; + // 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; -} + //! 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}, -}; + // 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; + //! 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; + } + } + } } diff --git a/src/algorithms/LBSimpleGaussian.cpp b/src/algorithms/LBSimpleGaussian.cpp index 368af80c8102faec5659f008cdf49b4e8891f236..6586833be501a0bb2e6fb557c48dae89dd876eec 100644 --- a/src/algorithms/LBSimpleGaussian.cpp +++ b/src/algorithms/LBSimpleGaussian.cpp @@ -27,7 +27,7 @@ void LBSimpleGaussian::process(const cv::Mat &img_input, cv::Mat &img_output, cv int w = cvGetSize(frame).width; int h = cvGetSize(frame).height; - m_pBGModel = new BGModelGauss(w, h); + m_pBGModel = new lb::BGModelGauss(w, h); m_pBGModel->InitModel(frame); } diff --git a/src/algorithms/LBSimpleGaussian.h b/src/algorithms/LBSimpleGaussian.h index 990751cf5214771b3f2daa0694687f7f776d7dd3..b41d54d7be4287d30464105ccee56bfc7b95ad45 100644 --- a/src/algorithms/LBSimpleGaussian.h +++ b/src/algorithms/LBSimpleGaussian.h @@ -7,9 +7,6 @@ #include "lb/BGModelGauss.h" -using namespace lb_library; -using namespace lb_library::SimpleGaussian; - namespace bgslibrary { namespace algorithms @@ -17,7 +14,7 @@ namespace bgslibrary class LBSimpleGaussian : public IBGS { private: - BGModel* m_pBGModel; + lb::BGModel* m_pBGModel; int sensitivity; int noiseVariance; int learningRate; diff --git a/src/algorithms/LOBSTER.cpp b/src/algorithms/LOBSTER.cpp index 937c31e75a10b214921a21861607714f519199d0..2a03c1086caf687a391ccd609a500324502e7c7a 100644 --- a/src/algorithms/LOBSTER.cpp +++ b/src/algorithms/LOBSTER.cpp @@ -5,12 +5,12 @@ using namespace bgslibrary::algorithms; LOBSTER::LOBSTER() : IBGS(quote(LOBSTER)), pLOBSTER(nullptr), - fRelLBSPThreshold(BGSLOBSTER_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD), - nLBSPThresholdOffset(BGSLOBSTER_DEFAULT_LBSP_OFFSET_SIMILARITY_THRESHOLD), - nDescDistThreshold(BGSLOBSTER_DEFAULT_DESC_DIST_THRESHOLD), - nColorDistThreshold(BGSLOBSTER_DEFAULT_COLOR_DIST_THRESHOLD), - nBGSamples(BGSLOBSTER_DEFAULT_NB_BG_SAMPLES), - nRequiredBGSamples(BGSLOBSTER_DEFAULT_REQUIRED_NB_BG_SAMPLES) + fRelLBSPThreshold(lbsp::BGSLOBSTER_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD), + nLBSPThresholdOffset(lbsp::BGSLOBSTER_DEFAULT_LBSP_OFFSET_SIMILARITY_THRESHOLD), + nDescDistThreshold(lbsp::BGSLOBSTER_DEFAULT_DESC_DIST_THRESHOLD), + nColorDistThreshold(lbsp::BGSLOBSTER_DEFAULT_COLOR_DIST_THRESHOLD), + nBGSamples(lbsp::BGSLOBSTER_DEFAULT_NB_BG_SAMPLES), + nRequiredBGSamples(lbsp::BGSLOBSTER_DEFAULT_REQUIRED_NB_BG_SAMPLES) { debug_construction(LOBSTER); initLoadSaveConfig(algorithmName); @@ -27,7 +27,7 @@ void LOBSTER::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &im init(img_input, img_output, img_bgmodel); if (firstTime) { - pLOBSTER = new BackgroundSubtractorLOBSTER( + pLOBSTER = new lbsp::BackgroundSubtractorLOBSTER( fRelLBSPThreshold, nLBSPThresholdOffset, nDescDistThreshold, nColorDistThreshold, nBGSamples, nRequiredBGSamples); diff --git a/src/algorithms/LOBSTER.h b/src/algorithms/LOBSTER.h index 324cd5230010e7796fa6015010a90ba56f7ff7a0..505fd779de190b12ea6554c4386ac0ead960ff40 100644 --- a/src/algorithms/LOBSTER.h +++ b/src/algorithms/LOBSTER.h @@ -11,7 +11,7 @@ namespace bgslibrary class LOBSTER : public IBGS { private: - BackgroundSubtractorLOBSTER* pLOBSTER; + lbsp::BackgroundSubtractorLOBSTER* pLOBSTER; float fRelLBSPThreshold; int nLBSPThresholdOffset; diff --git a/src/algorithms/MultiCue.cpp b/src/algorithms/MultiCue.cpp index a961678e32f645455668348fef05586fbdb6df33..72fbab1a8904b5c5f8b1ed9cefab613c1a7fea16 100644 --- a/src/algorithms/MultiCue.cpp +++ b/src/algorithms/MultiCue.cpp @@ -2,9 +2,24 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace bgslibrary::algorithms::libMultiCue; +using namespace bgslibrary::algorithms::multiCue; using namespace bgslibrary::algorithms; +#define MIN3(x,y,z) ((y) <= (z) ? ((x) <= (y) ? (x) : (y)) : ((x) <= (z) ? (x) : (z))) +#define MAX3(x,y,z) ((y) >= (z) ? ((x) >= (y) ? (x) : (y)) : ((x) >= (z) ? (x) : (z))) + +#ifndef PI +#define PI 3.141592653589793f +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + MultiCue::MultiCue() : IBGS(quote(MultiCue)) { diff --git a/src/algorithms/MultiCue.h b/src/algorithms/MultiCue.h index 31e31b16b82df8af9cfa6bb3925627e831038557..ca75a50eff404f33b9d4ae924114e47149b84f24 100644 --- a/src/algorithms/MultiCue.h +++ b/src/algorithms/MultiCue.h @@ -3,23 +3,6 @@ #include "opencv2/core/version.hpp" #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -#define MIN3(x,y,z) ((y) <= (z) ? ((x) <= (y) ? (x) : (y)) : ((x) <= (z) ? (x) : (z))) -#define MAX3(x,y,z) ((y) >= (z) ? ((x) >= (y) ? (x) : (y)) : ((x) >= (z) ? (x) : (z))) - -#ifndef PI -#define PI 3.14159 -#endif - -typedef int BOOL; - -#ifndef FALSE -#define FALSE 0 -#endif - -#ifndef TRUE -#define TRUE 1 -#endif - #if !defined(__APPLE__) #include <malloc.h> #endif @@ -36,8 +19,10 @@ namespace bgslibrary { namespace algorithms { - namespace libMultiCue + namespace multiCue { + typedef int BOOL; + struct point { short m_nX; short m_nY; @@ -106,7 +91,7 @@ namespace bgslibrary { namespace algorithms { - using namespace bgslibrary::algorithms::libMultiCue; + //using namespace bgslibrary::algorithms::multiCue; class MultiCue : public IBGS { @@ -115,10 +100,14 @@ namespace bgslibrary void load_config(cv::FileStorage &fs); public: + typedef bgslibrary::algorithms::multiCue::point point; + typedef bgslibrary::algorithms::multiCue::TextureModel TextureModel; + typedef bgslibrary::algorithms::multiCue::BoundingBoxInfo BoundingBoxInfo; + typedef bgslibrary::algorithms::multiCue::ColorModel ColorModel; + typedef bgslibrary::algorithms::multiCue::BOOL BOOL; + MultiCue(); ~MultiCue(); - - public: //---------------------------------------------------- // APIs and User-Adjustable Parameters //---------------------------------------------------- @@ -136,7 +125,6 @@ namespace bgslibrary short g_nTextureTrainVolRange; //the codebook size factor for texture models. (The parameter k in the paper) short g_nColorTrainVolRange; //the codebook size factor for color models. (The parameter eta_1 in the paper) - public: //---------------------------------------------------- // Implemented Function Lists //---------------------------------------------------- @@ -185,7 +173,7 @@ namespace bgslibrary void C_ClearNonEssentialEntries(short nClearNum, ColorModel* pModel); void C_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short nReferredIdx, short nClearNum, ColorModel* pCachebook); void C_Absorption(int iAbsorbCnt, point pos, short** aContinuCnt, short** aRefferedIndex, ColorModel* pModel, ColorModel* pCache); - public: + //---------------------------------------------------- // Implemented Variable Lists //---------------------------------------------------- diff --git a/src/algorithms/MultiLayer.cpp b/src/algorithms/MultiLayer.cpp index 89aa30a2fc2e60d1afbe5652225f38b2993b7f4a..1914785815ed7780af4925ab6fcd40f045a8b4c4 100644 --- a/src/algorithms/MultiLayer.cpp +++ b/src/algorithms/MultiLayer.cpp @@ -26,7 +26,6 @@ void MultiLayer::setStatus(Status _status) { void MultiLayer::finish() { if (bg_model_preload.empty()) { bg_model_preload = "./" + algorithmName + ".yml"; - saveConfig(); } if (status == MLBGS_LEARN && saveModel == true) { @@ -68,7 +67,7 @@ void MultiLayer::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat fg_prob_img3 = cvCreateImage(img_size, org_img->depth, org_img->nChannels); merged_img = cvCreateImage(cvSize(img_size.width * 2, img_size.height * 2), org_img->depth, org_img->nChannels); - BGS = new CMultiLayerBGS(); + BGS = new multilayer::CMultiLayerBGS(); BGS->Init(img_size.width, img_size.height); BGS->SetForegroundMaskImage(fg_mask_img); BGS->SetForegroundProbImage(fg_prob_img); diff --git a/src/algorithms/MultiLayer.h b/src/algorithms/MultiLayer.h index 26e33286d592c7e921f57f4f497f06f194a90bac..b33d72fd076c929afe569ce33ea8721d914a137d 100644 --- a/src/algorithms/MultiLayer.h +++ b/src/algorithms/MultiLayer.h @@ -21,13 +21,13 @@ namespace bgslibrary }; private: - long long frameNumber; + long frameNumber; cv::Mat img_merged; bool saveModel; bool disableDetectMode; bool disableLearning; int detectAfter; - CMultiLayerBGS* BGS; + multilayer::CMultiLayerBGS* BGS; Status status; //IplImage* img; IplImage* org_img; diff --git a/src/algorithms/MultiLayer/BGS.h b/src/algorithms/MultiLayer/BGS.h index 2ae1280a827f33b097c8e30ba1688398fd6e988e..97eeaf4cc71e2195d835fd9dec1ae2be8fa3b0a7 100644 --- a/src/algorithms/MultiLayer/BGS.h +++ b/src/algorithms/MultiLayer/BGS.h @@ -3,19 +3,6 @@ // opencv legacy includes #include "OpenCvLegacyIncludes.h" -#define MAX_LBP_MODE_NUM 5 -#define ROBUST_COLOR_OFFSET 6.0f -#define LOW_INITIAL_MODE_WEIGHT 0.01f -#define MODE_UPDATING_LEARN_RATE 0.01f -#define WEIGHT_UPDATING_LEARN_RATE 0.01f -#define COLOR_MAX_MIN_OFFSET 5 -#define BACKGROUND_MODEL_PERCENT 0.6f -#define PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD 0.2f -#define PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE 6 -#define PATTERN_DIST_CONV_GAUSSIAN_SIGMA 2.5f -#define ROBUST_SHADOW_RATE 0.6f -#define ROBUST_HIGHLIGHT_RATE 1.20f - #define BINARY_PATTERM_ELEM(c1, c2, offset) \ ((float)(c2)-(float)(c1)+offset > 0) @@ -28,134 +15,156 @@ #define PI 3.141592653589793f #endif -/************************************************************************/ -/* some data structures for multi-level LBP (local binary pattern) */ -/* texture feature for background subtraction */ -/************************************************************************/ -typedef struct _LBP -{ - float* bg_pattern; /* the average local binary pattern of background mode */ - float* bg_intensity; /* the average color intensity of background mode */ - float* max_intensity; /* the maximal color intensity of background mode */ - float* min_intensity; /* the minimal color intensity of background mode */ - float weight; /* the weight of background mode, i.e. probability that the background mode belongs to background */ - float max_weight; /* the maximal weight of background mode */ - int bg_layer_num; /* the background layer number of background mode */ - unsigned long first_time; /* the first time of background mode appearing */ - unsigned long last_time; /* the last time of background model appearing */ - int freq; /* the appearing frequency */ - //long mnrl; /* maximum negative run-length */ - unsigned long layer_time; /* the first time of background mode becoming a background layer */ -} -LBPStruct; - -typedef struct _PixelLBP -{ - LBPStruct* LBPs; /* the background modes */ - unsigned short* lbp_idxes; /* the indices of background modes */ - unsigned int cur_bg_layer_no; - unsigned int num; /* the total number of background modes */ - unsigned int bg_num; /* the number of the first background modes for foreground detection */ - unsigned char* cur_intensity; /* the color intensity of current pixel */ - float* cur_pattern; /* the local binary pattern of current pixel */ - float matched_mode_first_time; /* the index of currently matched pixel mode */ -} -PixelLBPStruct; - -/*********************************************************************************/ -/* should replace the above structure using class in the future (not finished) */ -/*********************************************************************************/ - -class BG_PIXEL_MODE +namespace bgslibrary { -public: - float* bg_lbp_pattern; /* the average local binary pattern of background mode */ - float* bg_intensity; /* the average color intensity of background mode */ - float* max_intensity; /* the maximal color intensity of background mode */ - float* min_intensity; /* the minimal color intensity of background mode */ - float weight; /* the weight of background mode, i.e. probability that the background mode belongs to background */ - float max_weight; /* the maximal weight of background mode */ - int bg_layer_num; /* the background layer number of background mode */ - - int lbp_pattern_length; - int color_channel; - - BG_PIXEL_MODE(int _lbp_pattern_length, int _color_channel = 3) { - lbp_pattern_length = _lbp_pattern_length; - color_channel = _color_channel; - - bg_lbp_pattern = new float[lbp_pattern_length]; - bg_intensity = new float[color_channel]; - max_intensity = new float[color_channel]; - min_intensity = new float[color_channel]; - }; - - virtual ~BG_PIXEL_MODE() { - delete[] bg_lbp_pattern; - delete[] bg_intensity; - delete[] max_intensity; - delete[] min_intensity; - }; -}; - -class BG_PIXEL_PATTERN -{ -public: - BG_PIXEL_MODE** pixel_MODEs; /* the background modes */ - unsigned short* lbp_pattern_idxes; /* the indices of background modes */ - unsigned int cur_bg_layer_no; - unsigned int num; /* the total number of background modes */ - unsigned int bg_num; /* the number of the first background modes for foreground detection */ - unsigned char* cur_intensity; /* the color intensity of current pixel */ - float* cur_lbp_pattern; /* the local binary pattern of current pixel */ - - int lbp_pattern_length; - int color_channel; - int pixel_mode_num; - - BG_PIXEL_PATTERN(int _pixel_mode_num, int _lbp_pattern_length, int _color_channel = 3) { - pixel_mode_num = _pixel_mode_num; - lbp_pattern_length = _lbp_pattern_length; - color_channel = _color_channel; - - pixel_MODEs = new BG_PIXEL_MODE*[pixel_mode_num]; - - for (int i = 0; i < pixel_mode_num; i++) { - pixel_MODEs[i] = new BG_PIXEL_MODE(_lbp_pattern_length, _color_channel); + namespace algorithms + { + namespace multilayer + { + const int MAX_LBP_MODE_NUM = 5; + const float ROBUST_COLOR_OFFSET = 6.0f; + const float LOW_INITIAL_MODE_WEIGHT = 0.01f; + const float MODE_UPDATING_LEARN_RATE = 0.01f; + const float WEIGHT_UPDATING_LEARN_RATE = 0.01f; + const int COLOR_MAX_MIN_OFFSET = 5; + const float BACKGROUND_MODEL_PERCENT = 0.6f; + const float PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD = 0.2f; + const float PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE = 6; + const float PATTERN_DIST_CONV_GAUSSIAN_SIGMA = 2.5f; + const float ROBUST_SHADOW_RATE = 0.6f; + const float ROBUST_HIGHLIGHT_RATE = 1.20f; + + /************************************************************************/ + /* some data structures for multi-level LBP (local binary pattern) */ + /* texture feature for background subtraction */ + /************************************************************************/ + typedef struct _LBP + { + float* bg_pattern; /* the average local binary pattern of background mode */ + float* bg_intensity; /* the average color intensity of background mode */ + float* max_intensity; /* the maximal color intensity of background mode */ + float* min_intensity; /* the minimal color intensity of background mode */ + float weight; /* the weight of background mode, i.e. probability that the background mode belongs to background */ + float max_weight; /* the maximal weight of background mode */ + int bg_layer_num; /* the background layer number of background mode */ + unsigned long first_time; /* the first time of background mode appearing */ + unsigned long last_time; /* the last time of background model appearing */ + int freq; /* the appearing frequency */ + //long mnrl; /* maximum negative run-length */ + unsigned long layer_time; /* the first time of background mode becoming a background layer */ + } + LBPStruct; + + typedef struct _PixelLBP + { + LBPStruct* LBPs; /* the background modes */ + unsigned short* lbp_idxes; /* the indices of background modes */ + unsigned int cur_bg_layer_no; + unsigned int num; /* the total number of background modes */ + unsigned int bg_num; /* the number of the first background modes for foreground detection */ + unsigned char* cur_intensity; /* the color intensity of current pixel */ + float* cur_pattern; /* the local binary pattern of current pixel */ + float matched_mode_first_time; /* the index of currently matched pixel mode */ + } + PixelLBPStruct; + + /*********************************************************************************/ + /* should replace the above structure using class in the future (not finished) */ + /*********************************************************************************/ + + class BG_PIXEL_MODE + { + public: + float* bg_lbp_pattern; /* the average local binary pattern of background mode */ + float* bg_intensity; /* the average color intensity of background mode */ + float* max_intensity; /* the maximal color intensity of background mode */ + float* min_intensity; /* the minimal color intensity of background mode */ + float weight; /* the weight of background mode, i.e. probability that the background mode belongs to background */ + float max_weight; /* the maximal weight of background mode */ + int bg_layer_num; /* the background layer number of background mode */ + + int lbp_pattern_length; + int color_channel; + + BG_PIXEL_MODE(int _lbp_pattern_length, int _color_channel = 3) { + lbp_pattern_length = _lbp_pattern_length; + color_channel = _color_channel; + + bg_lbp_pattern = new float[lbp_pattern_length]; + bg_intensity = new float[color_channel]; + max_intensity = new float[color_channel]; + min_intensity = new float[color_channel]; + }; + + virtual ~BG_PIXEL_MODE() { + delete[] bg_lbp_pattern; + delete[] bg_intensity; + delete[] max_intensity; + delete[] min_intensity; + }; + }; + + class BG_PIXEL_PATTERN + { + public: + BG_PIXEL_MODE** pixel_MODEs; /* the background modes */ + unsigned short* lbp_pattern_idxes; /* the indices of background modes */ + unsigned int cur_bg_layer_no; + unsigned int num; /* the total number of background modes */ + unsigned int bg_num; /* the number of the first background modes for foreground detection */ + unsigned char* cur_intensity; /* the color intensity of current pixel */ + float* cur_lbp_pattern; /* the local binary pattern of current pixel */ + + int lbp_pattern_length; + int color_channel; + int pixel_mode_num; + + BG_PIXEL_PATTERN(int _pixel_mode_num, int _lbp_pattern_length, int _color_channel = 3) { + pixel_mode_num = _pixel_mode_num; + lbp_pattern_length = _lbp_pattern_length; + color_channel = _color_channel; + + pixel_MODEs = new BG_PIXEL_MODE*[pixel_mode_num]; + + for (int i = 0; i < pixel_mode_num; i++) { + pixel_MODEs[i] = new BG_PIXEL_MODE(_lbp_pattern_length, _color_channel); + } + + lbp_pattern_idxes = new unsigned short[pixel_mode_num]; + cur_intensity = new unsigned char[color_channel]; + cur_lbp_pattern = new float[lbp_pattern_length]; + }; + + virtual ~BG_PIXEL_PATTERN() { + delete[] lbp_pattern_idxes; + delete[] cur_intensity; + delete[] cur_lbp_pattern; + + for (int i = 0; i < pixel_mode_num; i++) + delete pixel_MODEs[i]; + delete[] pixel_MODEs; + }; + }; + + class IMAGE_BG_MODEL + { + int pixel_length; + + BG_PIXEL_PATTERN** pixel_PATTERNs; + + IMAGE_BG_MODEL(int _pixel_length, int _pixel_mode_num, int _lbp_pattern_length, int _color_channel = 3) { + pixel_length = _pixel_length; + + pixel_PATTERNs = new BG_PIXEL_PATTERN*[pixel_length]; + for (int i = 0; i < pixel_length; i++) + pixel_PATTERNs[i] = new BG_PIXEL_PATTERN(_pixel_mode_num, _lbp_pattern_length, _color_channel); + } + virtual ~IMAGE_BG_MODEL() { + for (int i = 0; i < pixel_length; i++) + delete pixel_PATTERNs[i]; + delete[] pixel_PATTERNs; + } + }; } - - lbp_pattern_idxes = new unsigned short[pixel_mode_num]; - cur_intensity = new unsigned char[color_channel]; - cur_lbp_pattern = new float[lbp_pattern_length]; - }; - - virtual ~BG_PIXEL_PATTERN() { - delete[] lbp_pattern_idxes; - delete[] cur_intensity; - delete[] cur_lbp_pattern; - - for (int i = 0; i < pixel_mode_num; i++) - delete pixel_MODEs[i]; - delete[] pixel_MODEs; - }; -}; - -class IMAGE_BG_MODEL -{ - int pixel_length; - - BG_PIXEL_PATTERN** pixel_PATTERNs; - - IMAGE_BG_MODEL(int _pixel_length, int _pixel_mode_num, int _lbp_pattern_length, int _color_channel = 3) { - pixel_length = _pixel_length; - - pixel_PATTERNs = new BG_PIXEL_PATTERN*[pixel_length]; - for (int i = 0; i < pixel_length; i++) - pixel_PATTERNs[i] = new BG_PIXEL_PATTERN(_pixel_mode_num, _lbp_pattern_length, _color_channel); } - virtual ~IMAGE_BG_MODEL() { - for (int i = 0; i < pixel_length; i++) - delete pixel_PATTERNs[i]; - delete[] pixel_PATTERNs; - } -}; +} diff --git a/src/algorithms/MultiLayer/BackgroundSubtractionAPI.h b/src/algorithms/MultiLayer/BackgroundSubtractionAPI.h index 39223986b73c3710cf1bd3c966ee6415771cdb82..b44cdd7ec583b6f6b9a7dee99929bcede1df049d 100644 --- a/src/algorithms/MultiLayer/BackgroundSubtractionAPI.h +++ b/src/algorithms/MultiLayer/BackgroundSubtractionAPI.h @@ -3,90 +3,99 @@ // opencv legacy includes #include "OpenCvLegacyIncludes.h" -class CBackgroundSubtractionAPI +namespace bgslibrary { -public: - //CBackgroundSubtractionAPI(){}; - //virtual ~CBackgroundSubtractionAPI(){}; + namespace algorithms + { + namespace multilayer + { + class CBackgroundSubtractionAPI + { + public: + //CBackgroundSubtractionAPI(){}; + //virtual ~CBackgroundSubtractionAPI(){}; - //------------------------------------------------------------- - // TO CALL AT INITIALISATION: DEFINES THE SIZE OF THE INPUT IMAGES - // NORMALLY, UNNECESSARY IF A CONFIGURATION FILE IS LOADED - void Init(int width, int height); + //------------------------------------------------------------- + // TO CALL AT INITIALISATION: DEFINES THE SIZE OF THE INPUT IMAGES + // NORMALLY, UNNECESSARY IF A CONFIGURATION FILE IS LOADED + void Init(int width, int height); - //------------------------------------------------------------- - // PROVIDE A MASK TO DEFINE THE SET OF POINTS WHERE BACKGROUND - // SUBTRACTION DOES NOT NEED TO BE PERFORMED - // - // mode is useful to specify if the points to remove from - // processing are in addition to the ones potentially - // removed according to the configuration file, - // or if they are the only ones to be removed - // - // mode=0 : provided points need to be removed - // in addition to those already removed - // mode=1 : the provided points are the only one to remove - // from processing - // Note: maskImage(li,co)=0 indicate the points to remove - // from background processing - void SetValidPointMask(IplImage* maskImage, int mode); + //------------------------------------------------------------- + // PROVIDE A MASK TO DEFINE THE SET OF POINTS WHERE BACKGROUND + // SUBTRACTION DOES NOT NEED TO BE PERFORMED + // + // mode is useful to specify if the points to remove from + // processing are in addition to the ones potentially + // removed according to the configuration file, + // or if they are the only ones to be removed + // + // mode=0 : provided points need to be removed + // in addition to those already removed + // mode=1 : the provided points are the only one to remove + // from processing + // Note: maskImage(li,co)=0 indicate the points to remove + // from background processing + void SetValidPointMask(IplImage* maskImage, int mode); - //------------------------------------------------------------- - // - // set the frame rate, to adjust the update parameters - // to the actual frame rate. - // Can be called only once at initialisation, - // but in online cases, can be used to indicate - // the time interval during the last processed frame - // - // frameDuration is in millisecond - void SetFrameRate(float frameDuration); + //------------------------------------------------------------- + // + // set the frame rate, to adjust the update parameters + // to the actual frame rate. + // Can be called only once at initialisation, + // but in online cases, can be used to indicate + // the time interval during the last processed frame + // + // frameDuration is in millisecond + void SetFrameRate(float frameDuration); - //------------------------------------------------------------- - // PROVIDE A POINTER TO THE INPUT IMAGE - // -> INDICATE WHERE THE NEW IMAGE TO PROCESS IS STORED - // - // Here assumes that the input image will contain RGB images. - // The memory of this image is handled by the caller. - // - // The return value indicate whether the actual Background - // Subtraction algorithm handles RGB images (1) or not (0). - // - int SetRGBInputImage(IplImage * inputImage); + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE INPUT IMAGE + // -> INDICATE WHERE THE NEW IMAGE TO PROCESS IS STORED + // + // Here assumes that the input image will contain RGB images. + // The memory of this image is handled by the caller. + // + // The return value indicate whether the actual Background + // Subtraction algorithm handles RGB images (1) or not (0). + // + int SetRGBInputImage(IplImage * inputImage); - //------------------------------------------------------------- - // PROVIDE A POINTER TO THE RESULT IMAGE - // INDICATE WHERE THE BACKGROUND RESULT NEED TO BE STORED - // - // The return value is 1 if correct image format is provided, - // otherwise the return value is 0. - // e.g. fg_mask_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1); - int SetForegroundMaskImage(IplImage *fg_mask_img); + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE RESULT IMAGE + // INDICATE WHERE THE BACKGROUND RESULT NEED TO BE STORED + // + // The return value is 1 if correct image format is provided, + // otherwise the return value is 0. + // e.g. fg_mask_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1); + int SetForegroundMaskImage(IplImage *fg_mask_img); - // The return value is 1 if the function is implemented - // with correct format, otherwise the return value is 0 - // e.g. fg_prob_img = cvCreateImage(imgSize, IPL_DEPTH_32F, 1); - int SetForegroundProbImage(IplImage *fg_prob_img); + // The return value is 1 if the function is implemented + // with correct format, otherwise the return value is 0 + // e.g. fg_prob_img = cvCreateImage(imgSize, IPL_DEPTH_32F, 1); + int SetForegroundProbImage(IplImage *fg_prob_img); - //------------------------------------------------------------- - // This function should be called each time a new image is - // available in the input image. - // - // The return value is 1 if everything goes well, - // otherwise the return value is 0. - // - int Process(); + //------------------------------------------------------------- + // This function should be called each time a new image is + // available in the input image. + // + // The return value is 1 if everything goes well, + // otherwise the return value is 0. + // + int Process(); - //------------------------------------------------------------- - // this function should save parameters and information of the model - // (e.g. after a training of the model, or in such a way - // that the model can be reload to process the next frame - // type of save: - void Save(char *bg_model_fn); + //------------------------------------------------------------- + // this function should save parameters and information of the model + // (e.g. after a training of the model, or in such a way + // that the model can be reload to process the next frame + // type of save: + void Save(char *bg_model_fn); - //------------------------------------------------------------- - // this function should load the parameters necessary - // for the processing of the background subtraction or - // load background model information - void Load(char *bg_model_fn); -}; + //------------------------------------------------------------- + // this function should load the parameters necessary + // for the processing of the background subtraction or + // load background model information + void Load(char *bg_model_fn); + }; + } + } +} diff --git a/src/algorithms/MultiLayer/BlobExtraction.cpp b/src/algorithms/MultiLayer/BlobExtraction.cpp index 97d1e9a5294e6ffc64160ca4ff0e06e6e12ce980..8ef878a93c0113e9c6cb5e02d5afbf4010b18e7a 100644 --- a/src/algorithms/MultiLayer/BlobExtraction.cpp +++ b/src/algorithms/MultiLayer/BlobExtraction.cpp @@ -1,1440 +1,1449 @@ -//! Indica si la connectivitat es a 8 (si es desactiva es a 4) -#define B_CONNECTIVITAT_8 - -//! si la imatge és cÃclica verticalment (els blobs que toquen -//! les vores superior i inferior no es consideren externs) -#define IMATGE_CICLICA_VERTICAL 1 -//! si la imatge és cÃclica horitzontalment (els blobs que toquen -//! les vores dreta i esquerra no es consideren externs) -#define IMATGE_CICLICA_HORITZONTAL 0 - -#define PERIMETRE_DIAGONAL (1.41421356237310 - 2) -#define SQRT2 1.41421356237310 -// color dels pÃxels de la mà scara per ser exteriors -#define PIXEL_EXTERIOR 0 - #include "BlobResult.h" #include "BlobExtraction.h" #include "OpenCvLegacyIncludes.h" -namespace Blob +//! Indica si la connectivitat es a 8 (si es desactiva es a 4) +#define B_CONNECTIVITAT_8 + +namespace bgslibrary { - /** - - FUNCIÓ: BlobAnalysis - - FUNCIONALITAT: Extreu els blobs d'una imatge d'un sol canal - - PARÀMETRES: - - inputImage: Imatge d'entrada. Ha de ser d'un sol canal - - threshold: Nivell de gris per considerar un pixel blanc o negre - - maskImage: Imatge de mà scara fora de la cual no es calculen els blobs. A més, - els blobs que toquen els pixels de la mà scara a 0, són considerats - externs - - borderColor: Color del marc de la imatge (0=black or 1=white) - - findmoments: calcula els moments dels blobs o no - - RegionData: on es desarà el resultat - - RESULTAT: - - retorna true si tot ha anat bé, false si no. Deixa el resultat a blobs. - - RESTRICCIONS: - - La imatge d'entrada ha de ser d'un sol canal - - AUTOR: dgrossman@cdr.stanford.edu - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - - fpinyol@cvc.uab.es, rborras@cvc.uab.es: adaptació a les OpenCV - */ - bool BlobAnalysis(IplImage* inputImage, - uchar threshold, - IplImage* maskImage, - bool borderColor, - bool findmoments, - blob_vector &RegionData) + namespace algorithms { - // dimensions of input image taking in account the ROI - int Cols, Rows, startCol, startRow; - - if (inputImage->roi) - { - CvRect imageRoi = cvGetImageROI(inputImage); - startCol = imageRoi.x; - startRow = imageRoi.y; - Cols = imageRoi.width; - Rows = imageRoi.height; - } - else + namespace multilayer { - startCol = 0; - startRow = 0; - Cols = inputImage->width; - Rows = inputImage->height; - } + namespace blob + { + //! si la imatge és cÃclica verticalment (els blobs que toquen + //! les vores superior i inferior no es consideren externs) + const int IMATGE_CICLICA_VERTICAL = 1; + //! si la imatge és cÃclica horitzontalment (els blobs que toquen + //! les vores dreta i esquerra no es consideren externs) + const int IMATGE_CICLICA_HORITZONTAL = 0; + + const double SQRT2 = 1.41421356237310; + const double PERIMETRE_DIAGONAL = (SQRT2 - 2); + + // color dels pÃxels de la mà scara per ser exteriors + const int PIXEL_EXTERIOR = 0; + + /** + - FUNCIÓ: BlobAnalysis + - FUNCIONALITAT: Extreu els blobs d'una imatge d'un sol canal + - PARÀMETRES: + - inputImage: Imatge d'entrada. Ha de ser d'un sol canal + - threshold: Nivell de gris per considerar un pixel blanc o negre + - maskImage: Imatge de mà scara fora de la cual no es calculen els blobs. A més, + els blobs que toquen els pixels de la mà scara a 0, són considerats + externs + - borderColor: Color del marc de la imatge (0=black or 1=white) + - findmoments: calcula els moments dels blobs o no + - RegionData: on es desarà el resultat + - RESULTAT: + - retorna true si tot ha anat bé, false si no. Deixa el resultat a blobs. + - RESTRICCIONS: + - La imatge d'entrada ha de ser d'un sol canal + - AUTOR: dgrossman@cdr.stanford.edu + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + - fpinyol@cvc.uab.es, rborras@cvc.uab.es: adaptació a les OpenCV + */ + bool BlobAnalysis(IplImage* inputImage, + uchar threshold, + IplImage* maskImage, + bool borderColor, + bool findmoments, + blob_vector &RegionData) + { + // dimensions of input image taking in account the ROI + int Cols, Rows, startCol, startRow; - int Trans = Cols; // MAX trans in any row - char* pMask = NULL; - char* pImage; + if (inputImage->roi) + { + CvRect imageRoi = cvGetImageROI(inputImage); + startCol = imageRoi.x; + startRow = imageRoi.y; + Cols = imageRoi.width; + Rows = imageRoi.height; + } + else + { + startCol = 0; + startRow = 0; + Cols = inputImage->width; + Rows = inputImage->height; + } - // Convert image array into transition array. In each row - // the transition array tells which columns have a color change - int iCol, iRow, iTran, Tran; // Data for a given run - bool ThisCell, LastCell; // Contents (colors (0 or 1)) within this row - int TransitionOffset = 0; // Performance booster to avoid multiplication + int Trans = Cols; // MAX trans in any row + char* pMask = NULL; + char* pImage; - // row 0 and row Rows+1 represent the border - int i; - int *Transition; // Transition Matrix + // Convert image array into transition array. In each row + // the transition array tells which columns have a color change + int iCol, iRow, iTran, Tran; // Data for a given run + bool ThisCell, LastCell; // Contents (colors (0 or 1)) within this row + int TransitionOffset = 0; // Performance booster to avoid multiplication - int nombre_pixels_mascara = 0; - //! Imatge amb el perimetre extern de cada pixel - IplImage *imatgePerimetreExtern; + // row 0 and row Rows+1 represent the border + int i; + int *Transition; // Transition Matrix - // input images must have only 1-channel and be an image - if (!CV_IS_IMAGE(inputImage) || (inputImage->nChannels != 1)) - { - return false; - } - if (maskImage != NULL) - { - // input image and mask are a valid image? - if (!CV_IS_IMAGE(inputImage) || !CV_IS_IMAGE(maskImage)) - return false; + int nombre_pixels_mascara = 0; + //! Imatge amb el perimetre extern de cada pixel + IplImage *imatgePerimetreExtern; - // comprova que la mà scara tingui les mateixes dimensions que la imatge - if (inputImage->width != maskImage->width || inputImage->height != maskImage->height) - { - return false; - } + // input images must have only 1-channel and be an image + if (!CV_IS_IMAGE(inputImage) || (inputImage->nChannels != 1)) + { + return false; + } + if (maskImage != NULL) + { + // input image and mask are a valid image? + if (!CV_IS_IMAGE(inputImage) || !CV_IS_IMAGE(maskImage)) + return false; - // comprova que la mà scara sigui una imatge d'un sol canal (grayscale) - if (maskImage->nChannels != 1) - { - return false; - } + // comprova que la mà scara tingui les mateixes dimensions que la imatge + if (inputImage->width != maskImage->width || inputImage->height != maskImage->height) + { + return false; + } - } + // comprova que la mà scara sigui una imatge d'un sol canal (grayscale) + if (maskImage->nChannels != 1) + { + return false; + } - // Initialize Transition array - Transition = new int[(Rows + 2)*(Cols + 2)]; - memset(Transition, 0, (Rows + 2) * (Cols + 2) * sizeof(int)); - Transition[0] = Transition[(Rows + 1) * (Cols + 2)] = Cols + 2; + } - // Start at the beginning of the image (startCol, startRow) - pImage = inputImage->imageData + startCol - 1 + startRow * inputImage->widthStep; + // Initialize Transition array + Transition = new int[(Rows + 2)*(Cols + 2)]; + memset(Transition, 0, (Rows + 2) * (Cols + 2) * sizeof(int)); + Transition[0] = Transition[(Rows + 1) * (Cols + 2)] = Cols + 2; - /* - Paral·lelització del cà lcul de la matriu de transicions - Fem que cada iteració del for el faci un thread o l'altre ( tenim 2 possibles threads ) - */ - if (maskImage == NULL) - { - imatgePerimetreExtern = NULL; + // Start at the beginning of the image (startCol, startRow) + pImage = inputImage->imageData + startCol - 1 + startRow * inputImage->widthStep; - //Fill Transition array - for (iRow = 1; iRow < Rows + 1; iRow++) // Choose a row of Bordered image - { - TransitionOffset = iRow*(Cols + 2); //per a que sigui paral·litzable - iTran = 0; // Index into Transition array - Tran = 0; // No transitions at row start - LastCell = borderColor; + /* + Paral·lelització del cà lcul de la matriu de transicions + Fem que cada iteració del for el faci un thread o l'altre ( tenim 2 possibles threads ) + */ + if (maskImage == NULL) + { + imatgePerimetreExtern = NULL; - for (iCol = 0; iCol < Cols + 2; iCol++) // Scan that row of Bordered image - { - if (iCol == 0 || iCol == Cols + 1) - ThisCell = borderColor; - else - ThisCell = ((unsigned char)*(pImage)) > threshold; + //Fill Transition array + for (iRow = 1; iRow < Rows + 1; iRow++) // Choose a row of Bordered image + { + TransitionOffset = iRow*(Cols + 2); //per a que sigui paral·litzable + iTran = 0; // Index into Transition array + Tran = 0; // No transitions at row start + LastCell = borderColor; - if (ThisCell != LastCell) - { - Transition[TransitionOffset + iTran] = Tran; // Save completed Tran - iTran++; // Prepare new index - LastCell = ThisCell; // With this color + for (iCol = 0; iCol < Cols + 2; iCol++) // Scan that row of Bordered image + { + if (iCol == 0 || iCol == Cols + 1) + ThisCell = borderColor; + else + ThisCell = ((unsigned char)*(pImage)) > threshold; + + if (ThisCell != LastCell) + { + Transition[TransitionOffset + iTran] = Tran; // Save completed Tran + iTran++; // Prepare new index + LastCell = ThisCell; // With this color + } + + Tran++; // Tran continues + pImage++; + } + + Transition[TransitionOffset + iTran] = Tran; // Save completed run + if ((TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2)) + { + Transition[TransitionOffset + iTran + 1] = -1; + } + //jump to next row (beginning from (startCol, startRow)) + pImage = inputImage->imageData - 1 + startCol + (iRow + startRow)*inputImage->widthStep; + } } + else + { + //maskImage not NULL: Cal recòrrer la mà scara també per calcular la matriu de transicions - Tran++; // Tran continues - pImage++; - } + char perimeter; + char *pPerimetre; - Transition[TransitionOffset + iTran] = Tran; // Save completed run - if ((TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2)) - { - Transition[TransitionOffset + iTran + 1] = -1; - } - //jump to next row (beginning from (startCol, startRow)) - pImage = inputImage->imageData - 1 + startCol + (iRow + startRow)*inputImage->widthStep; - } - } - else - { - //maskImage not NULL: Cal recòrrer la mà scara també per calcular la matriu de transicions + // creem la imatge que contindrà el perimetre extern de cada pixel + imatgePerimetreExtern = cvCreateImage(cvSize(maskImage->width, maskImage->height), IPL_DEPTH_8U, 1); + cvSetZero(imatgePerimetreExtern); - char perimeter; - char *pPerimetre; + pMask = maskImage->imageData - 1; - // creem la imatge que contindrà el perimetre extern de cada pixel - imatgePerimetreExtern = cvCreateImage(cvSize(maskImage->width, maskImage->height), IPL_DEPTH_8U, 1); - cvSetZero(imatgePerimetreExtern); + //Fill Transition array + for (iRow = 1; iRow < Rows + 1; iRow++) // Choose a row of Bordered image + { + TransitionOffset = iRow*(Cols + 2); + iTran = 0; // Index into Transition array + Tran = 0; // No transitions at row start + LastCell = borderColor; - pMask = maskImage->imageData - 1; + pPerimetre = imatgePerimetreExtern->imageData + (iRow - 1) * imatgePerimetreExtern->widthStep; + //pMask = maskImage->imageData + (iRow-1) * maskImage->widthStep; - //Fill Transition array - for (iRow = 1; iRow < Rows + 1; iRow++) // Choose a row of Bordered image - { - TransitionOffset = iRow*(Cols + 2); - iTran = 0; // Index into Transition array - Tran = 0; // No transitions at row start - LastCell = borderColor; + for (iCol = 0; iCol < Cols + 2; iCol++) // Scan that row of Bordered image + { + if (iCol == 0 || iCol == Cols + 1 || ((unsigned char)*pMask) == PIXEL_EXTERIOR) + ThisCell = borderColor; + else + ThisCell = ((unsigned char)*(pImage)) > threshold; - pPerimetre = imatgePerimetreExtern->imageData + (iRow - 1) * imatgePerimetreExtern->widthStep; - //pMask = maskImage->imageData + (iRow-1) * maskImage->widthStep; + if (ThisCell != LastCell) + { + Transition[TransitionOffset + iTran] = Tran; // Save completed Tran + iTran++; // Prepare new index + LastCell = ThisCell; // With this color + } - for (iCol = 0; iCol < Cols + 2; iCol++) // Scan that row of Bordered image - { - if (iCol == 0 || iCol == Cols + 1 || ((unsigned char)*pMask) == PIXEL_EXTERIOR) - ThisCell = borderColor; - else - ThisCell = ((unsigned char)*(pImage)) > threshold; + /*//////////////////////////////////////////////////////////////////////// + Calcul de la imatge amb els pixels externs + ////////////////////////////////////////////////////////////////////////*/ + // pels pixels externs no cal calcular res pq no hi accedir-hem + if ((iCol > 0) && (iCol < Cols)) + { + if (*pMask == PIXEL_EXTERIOR) + { + *pPerimetre = 0; + } + else + { + perimeter = 0; - if (ThisCell != LastCell) - { - Transition[TransitionOffset + iTran] = Tran; // Save completed Tran - iTran++; // Prepare new index - LastCell = ThisCell; // With this color - } + // pixels al nord de l'actual + if (iRow > 1) + { + if (*(pMask - maskImage->widthStep) == PIXEL_EXTERIOR) perimeter++; + } - /*//////////////////////////////////////////////////////////////////////// - Calcul de la imatge amb els pixels externs - ////////////////////////////////////////////////////////////////////////*/ - // pels pixels externs no cal calcular res pq no hi accedir-hem - if ((iCol > 0) && (iCol < Cols)) - { - if (*pMask == PIXEL_EXTERIOR) - { - *pPerimetre = 0; - } - else - { - perimeter = 0; + // pixels a l'est i oest de l'actual + if (iRow < imatgePerimetreExtern->height) + { + if ((iCol > 0) && (*(pMask - 1) == PIXEL_EXTERIOR)) perimeter++; - // pixels al nord de l'actual - if (iRow > 1) - { - if (*(pMask - maskImage->widthStep) == PIXEL_EXTERIOR) perimeter++; - } + if ((iCol < imatgePerimetreExtern->width - 1) && (*(pMask + 1) == PIXEL_EXTERIOR)) perimeter++; + } - // pixels a l'est i oest de l'actual - if (iRow < imatgePerimetreExtern->height) - { - if ((iCol > 0) && (*(pMask - 1) == PIXEL_EXTERIOR)) perimeter++; + // pixels al sud de l'actual + if (iRow < imatgePerimetreExtern->height - 1) + { + if (*(pMask + maskImage->widthStep) == PIXEL_EXTERIOR) perimeter++; + } + + *pPerimetre = perimeter; + } + } - if ((iCol < imatgePerimetreExtern->width - 1) && (*(pMask + 1) == PIXEL_EXTERIOR)) perimeter++; + Tran++; // Tran continues + pImage++; + pMask++; + pPerimetre++; } + Transition[TransitionOffset + iTran] = Tran; // Save completed run - // pixels al sud de l'actual - if (iRow < imatgePerimetreExtern->height - 1) + if ((TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2)) { - if (*(pMask + maskImage->widthStep) == PIXEL_EXTERIOR) perimeter++; + Transition[TransitionOffset + iTran + 1] = -1; } - *pPerimetre = perimeter; + + //jump to next row (beginning from (startCol, startRow)) + pImage = inputImage->imageData - 1 + startCol + (iRow + startRow)*inputImage->widthStep; + //the mask should be the same size as image Roi, so don't take into account the offset + pMask = maskImage->imageData - 1 + iRow*maskImage->widthStep; } } - Tran++; // Tran continues - pImage++; - pMask++; - pPerimetre++; - } - Transition[TransitionOffset + iTran] = Tran; // Save completed run + // Process transition code depending on Last row and This row + // + // Last ---++++++--+++++++++++++++-----+++++++++++++++++++-----++++++-------+++--- + // This -----+++-----++++----+++++++++----+++++++---++------------------++++++++-- + // + // There are various possibilities: + // + // Case 1 2 3 4 5 6 7 8 + // Last |xxx |xxxxoo |xxxxxxx|xxxxxxx|ooxxxxx|ooxxx |ooxxxxx| xxx| + // This | yyy| yyy| yyyy | yyyyy|yyyyyyy|yyyyyyy|yyyy |yyyy | + // Here o is optional + // + // Here are the primitive tests to distinguish these 6 cases: + // A) Last end < This start - 1 OR NOT Note: -1 + // B) This end < Last start OR NOT + // C) Last start < This start OR NOT + // D) This end < Last end OR NOT + // E) This end = Last end OR NOT + // + // Here is how to use these tests to determine the case: + // Case 1 = A [=> NOT B AND C AND NOT D AND NOT E] + // Case 2 = C AND NOT D AND NOT E [AND NOT A AND NOT B] + // Case 3 = C AND D [=> NOT E] [AND NOT A AND NOT B] + // Case 4 = C AND NOT D AND E [AND NOT A AND NOT B] + // Case 5 = NOT C AND E [=> NOT D] [AND NOT A AND NOT B] + // Case 6 = NOT C AND NOT D AND NOT E [AND NOT A AND NOT B] + // Case 7 = NOT C AND D [=> NOT E] [AND NOT A AND NOT B] + // Case 8 = B [=> NOT A AND NOT C AND D AND NOT E] + // + // In cases 2,3,4,5,6,7 the following additional test is needed: + // Match) This color = Last color OR NOT + // + // In cases 5,6,7 the following additional test is needed: + // Known) This region was already matched OR NOT + // + // Here are the main tests and actions: + // Case 1: LastIndex++; + // Case 2: if(Match) {y = x;} + // LastIndex++; + // Case 3: if(Match) {y = x;} + // else {y = new} + // ThisIndex++; + // Case 4: if(Match) {y = x;} + // else {y = new} + // LastIndex++; + // ThisIndex++; + // Case 5: if(Match AND NOT Known) {y = x} + // else if(Match AND Known) {Subsume(x,y)} + // LastIndex++;ThisIndex++ + // Case 6: if(Match AND NOT Known) {y = x} + // else if(Match AND Known) {Subsume(x,y)} + // LastIndex++; + // Case 7: if(Match AND NOT Known) {y = x} + // else if(Match AND Known) {Subsume(x,y)} + // ThisIndex++; + // Case 8: ThisIndex++; + + int *SubsumedRegion = NULL; + + double ThisParent; // These data can change when the line is current + double ThisArea; + double ThisPerimeter; + double ThisSumX = 0; + double ThisSumY = 0; + double ThisSumXX = 0; + double ThisSumYY = 0; + double ThisSumXY = 0; + double ThisMinX; + double ThisMaxX; + double ThisMinY; + double ThisMaxY; + double LastPerimeter; // This is the only data for retroactive change + double ThisExternPerimeter; + + int HighRegionNum = 0; + //int RegionNum = 0; + int ErrorFlag = 0; + + int LastRow, ThisRow; // Row number + int LastStart, ThisStart; // Starting column of run + int LastEnd, ThisEnd; // Ending column of run + int LastColor, ThisColor; // Color of run + + int LastIndex, ThisIndex; // Which run are we up to + int LastIndexCount, ThisIndexCount; // Out of these runs + int LastRegionNum, ThisRegionNum; // Which assignment + int *LastRegion; // Row assignment of region number + int *ThisRegion; // Row assignment of region number + + int LastOffset = -(Trans + 2); // For performance to avoid multiplication + int ThisOffset = 0; // For performance to avoid multiplication + int ComputeData; + + CvPoint actualedge; + uchar imagevalue; + bool CandidatExterior = false; + + // apuntadors als blobs de la regió actual i last + CBlob *regionDataThisRegion, *regionDataLastRegion; + + LastRegion = new int[Cols + 2]; + ThisRegion = new int[Cols + 2]; + + for (i = 0; i < Cols + 2; i++) // Initialize result arrays + { + LastRegion[i] = -1; + ThisRegion[i] = -1; + } - if ((TransitionOffset + iTran + 1) < (Rows + 1)*(Cols + 2)) - { - Transition[TransitionOffset + iTran + 1] = -1; - } + //create the external blob + RegionData.push_back(new CBlob()); + SubsumedRegion = NewSubsume(SubsumedRegion, 0); + RegionData[0]->parent = -1; + RegionData[0]->area = (double)Transition[0]; + RegionData[0]->perimeter = (double)(2 + 2 * Transition[0]); + ThisIndexCount = 1; + ThisRegion[0] = 0; // Border region - //jump to next row (beginning from (startCol, startRow)) - pImage = inputImage->imageData - 1 + startCol + (iRow + startRow)*inputImage->widthStep; - //the mask should be the same size as image Roi, so don't take into account the offset - pMask = maskImage->imageData - 1 + iRow*maskImage->widthStep; - } - } + // beginning of the image + // en cada linia, pimage apunta al primer pixel de la fila + pImage = inputImage->imageData - 1 + startCol + startRow * inputImage->widthStep; + //the mask should be the same size as image Roi, so don't take into account the offset + if (maskImage != NULL) pMask = maskImage->imageData - 1; - // Process transition code depending on Last row and This row - // - // Last ---++++++--+++++++++++++++-----+++++++++++++++++++-----++++++-------+++--- - // This -----+++-----++++----+++++++++----+++++++---++------------------++++++++-- - // - // There are various possibilities: - // - // Case 1 2 3 4 5 6 7 8 - // Last |xxx |xxxxoo |xxxxxxx|xxxxxxx|ooxxxxx|ooxxx |ooxxxxx| xxx| - // This | yyy| yyy| yyyy | yyyyy|yyyyyyy|yyyyyyy|yyyy |yyyy | - // Here o is optional - // - // Here are the primitive tests to distinguish these 6 cases: - // A) Last end < This start - 1 OR NOT Note: -1 - // B) This end < Last start OR NOT - // C) Last start < This start OR NOT - // D) This end < Last end OR NOT - // E) This end = Last end OR NOT - // - // Here is how to use these tests to determine the case: - // Case 1 = A [=> NOT B AND C AND NOT D AND NOT E] - // Case 2 = C AND NOT D AND NOT E [AND NOT A AND NOT B] - // Case 3 = C AND D [=> NOT E] [AND NOT A AND NOT B] - // Case 4 = C AND NOT D AND E [AND NOT A AND NOT B] - // Case 5 = NOT C AND E [=> NOT D] [AND NOT A AND NOT B] - // Case 6 = NOT C AND NOT D AND NOT E [AND NOT A AND NOT B] - // Case 7 = NOT C AND D [=> NOT E] [AND NOT A AND NOT B] - // Case 8 = B [=> NOT A AND NOT C AND D AND NOT E] - // - // In cases 2,3,4,5,6,7 the following additional test is needed: - // Match) This color = Last color OR NOT - // - // In cases 5,6,7 the following additional test is needed: - // Known) This region was already matched OR NOT - // - // Here are the main tests and actions: - // Case 1: LastIndex++; - // Case 2: if(Match) {y = x;} - // LastIndex++; - // Case 3: if(Match) {y = x;} - // else {y = new} - // ThisIndex++; - // Case 4: if(Match) {y = x;} - // else {y = new} - // LastIndex++; - // ThisIndex++; - // Case 5: if(Match AND NOT Known) {y = x} - // else if(Match AND Known) {Subsume(x,y)} - // LastIndex++;ThisIndex++ - // Case 6: if(Match AND NOT Known) {y = x} - // else if(Match AND Known) {Subsume(x,y)} - // LastIndex++; - // Case 7: if(Match AND NOT Known) {y = x} - // else if(Match AND Known) {Subsume(x,y)} - // ThisIndex++; - // Case 8: ThisIndex++; - - int *SubsumedRegion = NULL; - - double ThisParent; // These data can change when the line is current - double ThisArea; - double ThisPerimeter; - double ThisSumX = 0; - double ThisSumY = 0; - double ThisSumXX = 0; - double ThisSumYY = 0; - double ThisSumXY = 0; - double ThisMinX; - double ThisMaxX; - double ThisMinY; - double ThisMaxY; - double LastPerimeter; // This is the only data for retroactive change - double ThisExternPerimeter; - - int HighRegionNum = 0; - //int RegionNum = 0; - int ErrorFlag = 0; - - int LastRow, ThisRow; // Row number - int LastStart, ThisStart; // Starting column of run - int LastEnd, ThisEnd; // Ending column of run - int LastColor, ThisColor; // Color of run - - int LastIndex, ThisIndex; // Which run are we up to - int LastIndexCount, ThisIndexCount; // Out of these runs - int LastRegionNum, ThisRegionNum; // Which assignment - int *LastRegion; // Row assignment of region number - int *ThisRegion; // Row assignment of region number - - int LastOffset = -(Trans + 2); // For performance to avoid multiplication - int ThisOffset = 0; // For performance to avoid multiplication - int ComputeData; - - CvPoint actualedge; - uchar imagevalue; - bool CandidatExterior = false; - - // apuntadors als blobs de la regió actual i last - CBlob *regionDataThisRegion, *regionDataLastRegion; - - LastRegion = new int[Cols + 2]; - ThisRegion = new int[Cols + 2]; - - for (i = 0; i < Cols + 2; i++) // Initialize result arrays - { - LastRegion[i] = -1; - ThisRegion[i] = -1; - } + char *pImageAux, *pMaskAux = NULL; - //create the external blob - RegionData.push_back(new CBlob()); - SubsumedRegion = NewSubsume(SubsumedRegion, 0); - RegionData[0]->parent = -1; - RegionData[0]->area = (double)Transition[0]; - RegionData[0]->perimeter = (double)(2 + 2 * Transition[0]); + // Loop over all rows + for (ThisRow = 1; ThisRow < Rows + 2; ThisRow++) + { + //cout << "========= THIS ROW = " << ThisRow << endl; // for debugging + ThisOffset += Trans + 2; + ThisIndex = 0; + LastOffset += Trans + 2;; + LastRow = ThisRow - 1; + LastIndexCount = ThisIndexCount; + LastIndex = 0; + + int EndLast = 0; + int EndThis = 0; + + for (int j = 0; j < Trans + 2; j++) + { + int Index = ThisOffset + j; + int TranVal = Transition[Index]; + if (TranVal > 0) ThisIndexCount = j + 1; // stop at highest - ThisIndexCount = 1; - ThisRegion[0] = 0; // Border region + if (ThisRegion[j] == -1) { EndLast = 1; } + if (TranVal < 0) { EndThis = 1; } - // beginning of the image - // en cada linia, pimage apunta al primer pixel de la fila - pImage = inputImage->imageData - 1 + startCol + startRow * inputImage->widthStep; - //the mask should be the same size as image Roi, so don't take into account the offset - if (maskImage != NULL) pMask = maskImage->imageData - 1; + if (EndLast > 0 && EndThis > 0) { break; } - char *pImageAux, *pMaskAux = NULL; + LastRegion[j] = ThisRegion[j]; + ThisRegion[j] = -1; // Flag indicates region is not initialized + } - // Loop over all rows - for (ThisRow = 1; ThisRow < Rows + 2; ThisRow++) - { - //cout << "========= THIS ROW = " << ThisRow << endl; // for debugging - ThisOffset += Trans + 2; - ThisIndex = 0; - LastOffset += Trans + 2;; - LastRow = ThisRow - 1; - LastIndexCount = ThisIndexCount; - LastIndex = 0; - - int EndLast = 0; - int EndThis = 0; - - for (int j = 0; j < Trans + 2; j++) - { - int Index = ThisOffset + j; - int TranVal = Transition[Index]; - if (TranVal > 0) ThisIndexCount = j + 1; // stop at highest + int MaxIndexCount = LastIndexCount; + if (ThisIndexCount > MaxIndexCount) MaxIndexCount = ThisIndexCount; - if (ThisRegion[j] == -1) { EndLast = 1; } - if (TranVal < 0) { EndThis = 1; } + // Main loop over runs within Last and This rows + while (LastIndex < LastIndexCount && ThisIndex < ThisIndexCount) + { + ComputeData = 0; + + if (LastIndex == 0) LastStart = 0; + else LastStart = Transition[LastOffset + LastIndex - 1]; + LastEnd = Transition[LastOffset + LastIndex] - 1; + LastColor = LastIndex - 2 * (LastIndex / 2); + LastRegionNum = LastRegion[LastIndex]; + + regionDataLastRegion = RegionData[LastRegionNum]; + + + if (ThisIndex == 0) ThisStart = 0; + else ThisStart = Transition[ThisOffset + ThisIndex - 1]; + ThisEnd = Transition[ThisOffset + ThisIndex] - 1; + ThisColor = ThisIndex - 2 * (ThisIndex / 2); + ThisRegionNum = ThisRegion[ThisIndex]; + + if (ThisRegionNum >= 0) + regionDataThisRegion = RegionData[ThisRegionNum]; + else + regionDataThisRegion = NULL; + + + // blobs externs + CandidatExterior = false; + if ( + #if !IMATGE_CICLICA_VERTICAL + ThisRow == 1 || ThisRow == Rows || + #endif + #if !IMATGE_CICLICA_HORITZONTAL + ThisStart <= 1 || ThisEnd >= Cols || + #endif + GetExternPerimeter(ThisStart, ThisEnd, ThisRow, inputImage->width, inputImage->height, imatgePerimetreExtern) + ) + { + CandidatExterior = true; + } - if (EndLast > 0 && EndThis > 0) { break; } + int TestA = (LastEnd < ThisStart - 1); // initially false + int TestB = (ThisEnd < LastStart); // initially false + int TestC = (LastStart < ThisStart); // initially false + int TestD = (ThisEnd < LastEnd); + int TestE = (ThisEnd == LastEnd); - LastRegion[j] = ThisRegion[j]; - ThisRegion[j] = -1; // Flag indicates region is not initialized - } + int TestMatch = (ThisColor == LastColor); // initially true + int TestKnown = (ThisRegion[ThisIndex] >= 0); // initially false - int MaxIndexCount = LastIndexCount; - if (ThisIndexCount > MaxIndexCount) MaxIndexCount = ThisIndexCount; + int Case = 0; + if (TestA) Case = 1; + else if (TestB) Case = 8; + else if (TestC) + { + if (TestD) Case = 3; + else if (!TestE) Case = 2; + else Case = 4; + } + else + { + if (TestE) Case = 5; + else if (TestD) Case = 7; + else Case = 6; + } - // Main loop over runs within Last and This rows - while (LastIndex < LastIndexCount && ThisIndex < ThisIndexCount) - { - ComputeData = 0; - - if (LastIndex == 0) LastStart = 0; - else LastStart = Transition[LastOffset + LastIndex - 1]; - LastEnd = Transition[LastOffset + LastIndex] - 1; - LastColor = LastIndex - 2 * (LastIndex / 2); - LastRegionNum = LastRegion[LastIndex]; - - regionDataLastRegion = RegionData[LastRegionNum]; - - - if (ThisIndex == 0) ThisStart = 0; - else ThisStart = Transition[ThisOffset + ThisIndex - 1]; - ThisEnd = Transition[ThisOffset + ThisIndex] - 1; - ThisColor = ThisIndex - 2 * (ThisIndex / 2); - ThisRegionNum = ThisRegion[ThisIndex]; - - if (ThisRegionNum >= 0) - regionDataThisRegion = RegionData[ThisRegionNum]; - else - regionDataThisRegion = NULL; - - - // blobs externs - CandidatExterior = false; - if ( -#if !IMATGE_CICLICA_VERTICAL - ThisRow == 1 || ThisRow == Rows || -#endif -#if !IMATGE_CICLICA_HORITZONTAL - ThisStart <= 1 || ThisEnd >= Cols || -#endif - GetExternPerimeter(ThisStart, ThisEnd, ThisRow, inputImage->width, inputImage->height, imatgePerimetreExtern) - ) - { - CandidatExterior = true; - } + // Initialize common variables + ThisArea = (float) 0.0; - int TestA = (LastEnd < ThisStart - 1); // initially false - int TestB = (ThisEnd < LastStart); // initially false - int TestC = (LastStart < ThisStart); // initially false - int TestD = (ThisEnd < LastEnd); - int TestE = (ThisEnd == LastEnd); + if (findmoments) + { + ThisSumX = ThisSumY = (float) 0.0; + ThisSumXX = ThisSumYY = ThisSumXY = (float) 0.0; + } + ThisMinX = ThisMinY = (float) 1000000.0; + ThisMaxX = ThisMaxY = (float)-1.0; - int TestMatch = (ThisColor == LastColor); // initially true - int TestKnown = (ThisRegion[ThisIndex] >= 0); // initially false + LastPerimeter = ThisPerimeter = (float) 0.0; + ThisParent = (float)-1; + ThisExternPerimeter = 0.0; - int Case = 0; - if (TestA) Case = 1; - else if (TestB) Case = 8; - else if (TestC) - { - if (TestD) Case = 3; - else if (!TestE) Case = 2; - else Case = 4; - } - else - { - if (TestE) Case = 5; - else if (TestD) Case = 7; - else Case = 6; - } + // Determine necessary action and take it + switch (Case) + { + case 1: //|xxx | + //| yyy| - // Initialize common variables - ThisArea = (float) 0.0; + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + LastIndex++; - if (findmoments) - { - ThisSumX = ThisSumY = (float) 0.0; - ThisSumXX = ThisSumYY = ThisSumXY = (float) 0.0; - } - ThisMinX = ThisMinY = (float) 1000000.0; - ThisMaxX = ThisMaxY = (float)-1.0; + //afegim la cantonada a LastRegion + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); - LastPerimeter = ThisPerimeter = (float) 0.0; - ThisParent = (float)-1; - ThisExternPerimeter = 0.0; + //afegim la cantonada a ThisRegion + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); - // Determine necessary action and take it - switch (Case) - { - case 1: //|xxx | - //| yyy| + break; - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - LastIndex++; - //afegim la cantonada a LastRegion - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); + case 2: //|xxxxoo | + //| yyy| - //afegim la cantonada a ThisRegion - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); + if (TestMatch) // Same color + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; - break; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = LastEnd - ThisStart + 1; // to subtract + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL * 2; + if (CandidatExterior) + { + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); + ThisExternPerimeter += PERIMETRE_DIAGONAL * 2; + } + ComputeData = 1; + } - case 2: //|xxxxoo | - //| yyy| + //afegim la cantonada a ThisRegion + if (ThisRegionNum != -1) + { + // afegim dos vertexs si són diferents, només + if (ThisStart - 1 != ThisEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + //afegim la cantonada a ThisRegion + if (LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + // afegim dos vertexs si són diferents, només + if (ThisStart - 1 != ThisEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + } + } - if (TestMatch) // Same color - { - ThisRegionNum = LastRegionNum; - regionDataThisRegion = regionDataLastRegion; + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + LastIndex++; + break; - ThisArea = ThisEnd - ThisStart + 1; - LastPerimeter = LastEnd - ThisStart + 1; // to subtract - ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + - PERIMETRE_DIAGONAL * 2; - if (CandidatExterior) - { - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); - ThisExternPerimeter += PERIMETRE_DIAGONAL * 2; - } - ComputeData = 1; - } + case 3: //|xxxxxxx| + //| yyyy | - //afegim la cantonada a ThisRegion - if (ThisRegionNum != -1) - { - // afegim dos vertexs si són diferents, només - if (ThisStart - 1 != ThisEnd) - { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - //afegim la cantonada a ThisRegion - if (LastRegionNum != -1 && LastRegionNum != ThisRegionNum) - { - // afegim dos vertexs si són diferents, només - if (ThisStart - 1 != ThisEnd) - { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - } - } + if (TestMatch) // Same color + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - LastIndex++; - break; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = ThisArea; // to subtract + ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL * 2; + if (CandidatExterior) + { + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); + ThisExternPerimeter += PERIMETRE_DIAGONAL * 2; + } + } + else // Different color => New region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back(new CBlob()); + regionDataThisRegion = RegionData.back(); + + SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); + if (CandidatExterior) + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); - case 3: //|xxxxxxx| - //| yyyy | + } - if (TestMatch) // Same color - { - ThisRegionNum = LastRegionNum; - regionDataThisRegion = regionDataLastRegion; + if (ThisRegionNum != -1) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + // si hem creat un nou blob, afegim tb a l'anterior + if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + } - ThisArea = ThisEnd - ThisStart + 1; - LastPerimeter = ThisArea; // to subtract - ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL * 2; - if (CandidatExterior) - { - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ComputeData = 1; + ThisIndex++; + break; - ThisExternPerimeter += PERIMETRE_DIAGONAL * 2; - } - } - else // Different color => New region - { - ThisParent = LastRegionNum; - ThisRegionNum = ++HighRegionNum; - ThisArea = ThisEnd - ThisStart + 1; - ThisPerimeter = 2 + 2 * ThisArea; - RegionData.push_back(new CBlob()); - regionDataThisRegion = RegionData.back(); - - SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); - if (CandidatExterior) - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); - } + case 4: //|xxxxxxx| + //| yyyyy| - if (ThisRegionNum != -1) - { - //afegim la cantonada a la regio - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - //afegim la cantonada a la regio - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - // si hem creat un nou blob, afegim tb a l'anterior - if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) - { - //afegim la cantonada a la regio - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - //afegim la cantonada a la regio - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - } + if (TestMatch) // Same color + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = ThisArea; // to subtract + ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL; + if (CandidatExterior) + { + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - ComputeData = 1; - ThisIndex++; - break; + ThisExternPerimeter += PERIMETRE_DIAGONAL; + } + } + else // Different color => New region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back(new CBlob()); + regionDataThisRegion = RegionData.back(); + SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); + if (CandidatExterior) + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); + } - case 4: //|xxxxxxx| - //| yyyyy| + if (ThisRegionNum != -1) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + // si hem creat un nou blob, afegim tb a l'anterior + if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + } - if (TestMatch) // Same color - { - ThisRegionNum = LastRegionNum; - regionDataThisRegion = regionDataLastRegion; - ThisArea = ThisEnd - ThisStart + 1; - LastPerimeter = ThisArea; // to subtract - ThisPerimeter = 2 + ThisArea + PERIMETRE_DIAGONAL; - if (CandidatExterior) - { - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ComputeData = 1; - ThisExternPerimeter += PERIMETRE_DIAGONAL; - } - } - else // Different color => New region - { - ThisParent = LastRegionNum; - ThisRegionNum = ++HighRegionNum; - ThisArea = ThisEnd - ThisStart + 1; - ThisPerimeter = 2 + 2 * ThisArea; - RegionData.push_back(new CBlob()); - regionDataThisRegion = RegionData.back(); - SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); - if (CandidatExterior) - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + #ifdef B_CONNECTIVITAT_8 + if (TestMatch) + { + LastIndex++; + ThisIndex++; + } + else + { + LastIndex++; + } + #else + LastIndex++; + ThisIndex++; + #endif + break; - } - if (ThisRegionNum != -1) - { - //afegim la cantonada a la regio - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - // si hem creat un nou blob, afegim tb a l'anterior - if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) - { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - } + case 5: //|ooxxxxx| + //|yyyyyyy| - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - ComputeData = 1; + if (!TestMatch && !TestKnown) // Different color and unknown => new region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back(new CBlob()); + regionDataThisRegion = RegionData.back(); + SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); + if (CandidatExterior) + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); -#ifdef B_CONNECTIVITAT_8 - if (TestMatch) - { - LastIndex++; - ThisIndex++; - } - else - { - LastIndex++; - } -#else - LastIndex++; - ThisIndex++; -#endif - break; + } + else if (TestMatch && !TestKnown) // Same color and unknown + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = LastEnd - LastStart + 1; // to subtract + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL * (LastStart != ThisStart); + if (CandidatExterior) + { + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); - case 5: //|ooxxxxx| - //|yyyyyyy| + ThisExternPerimeter += PERIMETRE_DIAGONAL * (LastStart != ThisStart); + } + ComputeData = 1; + } + else if (TestMatch && TestKnown) // Same color and known + { + LastPerimeter = LastEnd - LastStart + 1; // to subtract + //ThisPerimeter = - LastPerimeter; + ThisPerimeter = -2 * LastPerimeter + + PERIMETRE_DIAGONAL * (LastStart != ThisStart); - if (!TestMatch && !TestKnown) // Different color and unknown => new region - { - ThisParent = LastRegionNum; - ThisRegionNum = ++HighRegionNum; - ThisArea = ThisEnd - ThisStart + 1; - ThisPerimeter = 2 + 2 * ThisArea; - RegionData.push_back(new CBlob()); - regionDataThisRegion = RegionData.back(); - SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); - if (CandidatExterior) - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + if (ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if (ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum); + + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + } - } - else if (TestMatch && !TestKnown) // Same color and unknown - { - ThisRegionNum = LastRegionNum; - regionDataThisRegion = regionDataLastRegion; - ThisArea = ThisEnd - ThisStart + 1; - LastPerimeter = LastEnd - LastStart + 1; // to subtract - ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter - + PERIMETRE_DIAGONAL * (LastStart != ThisStart); - if (CandidatExterior) - { - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + if (ThisRegionNum != -1) + { + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); - ThisExternPerimeter += PERIMETRE_DIAGONAL * (LastStart != ThisStart); - } - ComputeData = 1; - } - else if (TestMatch && TestKnown) // Same color and known - { - LastPerimeter = LastEnd - LastStart + 1; // to subtract - //ThisPerimeter = - LastPerimeter; - ThisPerimeter = -2 * LastPerimeter - + PERIMETRE_DIAGONAL * (LastStart != ThisStart); + if (ThisStart - 1 != LastEnd) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + } + // si hem creat un nou blob, afegim tb a l'anterior + if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + } - if (ThisRegionNum > LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, - findmoments, ThisRegionNum, LastRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) - { - if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; - if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; - } - ThisRegionNum = LastRegionNum; - } - else if (ThisRegionNum < LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, - findmoments, LastRegionNum, ThisRegionNum); + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; - for (int iOld = 0; iOld < MaxIndexCount; iOld++) - { - if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; - if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; - } - LastRegionNum = ThisRegionNum; - } - } + #ifdef B_CONNECTIVITAT_8 + if (TestMatch) + { + LastIndex++; + ThisIndex++; + } + else + { + LastIndex++; + } + #else + LastIndex++; + ThisIndex++; + #endif + break; - if (ThisRegionNum != -1) - { - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); + case 6: //|ooxxx | + //|yyyyyyy| - if (ThisStart - 1 != LastEnd) - { - //afegim la cantonada a la regio - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - } - // si hem creat un nou blob, afegim tb a l'anterior - if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) - { - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - } + if (TestMatch && !TestKnown) + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + LastPerimeter = LastEnd - LastStart + 1; // to subtract + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + if (CandidatExterior) + { + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; -#ifdef B_CONNECTIVITAT_8 - if (TestMatch) - { - LastIndex++; - ThisIndex++; - } - else - { - LastIndex++; - } -#else - LastIndex++; - ThisIndex++; -#endif - break; + ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + } + ComputeData = 1; + } + else if (TestMatch && TestKnown) + { + LastPerimeter = LastEnd - LastStart + 1; // to subtract + //ThisPerimeter = - LastPerimeter; + ThisPerimeter = -2 * LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + if (ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if (ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + } - case 6: //|ooxxx | - //|yyyyyyy| - if (TestMatch && !TestKnown) - { - ThisRegionNum = LastRegionNum; - regionDataThisRegion = regionDataLastRegion; - ThisArea = ThisEnd - ThisStart + 1; - LastPerimeter = LastEnd - LastStart + 1; // to subtract - ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter - + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); - if (CandidatExterior) - { - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + if (ThisRegionNum != -1) + { + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + if (ThisStart - 1 != LastEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + } + // si hem creat un nou blob, afegim tb a l'anterior + if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + //afegim la cantonada a la regio + if (ThisStart - 1 != LastEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + } + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + LastIndex++; + break; - ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); - } - ComputeData = 1; - } - else if (TestMatch && TestKnown) - { - LastPerimeter = LastEnd - LastStart + 1; // to subtract - //ThisPerimeter = - LastPerimeter; - ThisPerimeter = -2 * LastPerimeter - + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); - if (ThisRegionNum > LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, - findmoments, ThisRegionNum, LastRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) - { - if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; - if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; - } - ThisRegionNum = LastRegionNum; - } - else if (ThisRegionNum < LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, - findmoments, LastRegionNum, ThisRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) - { - if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; - if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; - } - LastRegionNum = ThisRegionNum; - } - } + case 7: //|ooxxxxx| + //|yyyy | + if (!TestMatch && !TestKnown) // Different color and unknown => new region + { + ThisParent = LastRegionNum; + ThisRegionNum = ++HighRegionNum; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + 2 * ThisArea; + RegionData.push_back(new CBlob()); + regionDataThisRegion = RegionData.back(); + SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); + if (CandidatExterior) + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); - if (ThisRegionNum != -1) - { - //afegim la cantonada a la regio - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - if (ThisStart - 1 != LastEnd) - { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - } - // si hem creat un nou blob, afegim tb a l'anterior - if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) - { - //afegim la cantonada a la regio - if (ThisStart - 1 != LastEnd) - { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } - } + } + else if (TestMatch && !TestKnown) + { + ThisRegionNum = LastRegionNum; + regionDataThisRegion = regionDataLastRegion; + ThisArea = ThisEnd - ThisStart + 1; + ThisPerimeter = 2 + ThisArea; + LastPerimeter = ThisEnd - LastStart + 1; + ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + if (CandidatExterior) + { + ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, + inputImage->width, inputImage->height, + imatgePerimetreExtern); - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - LastIndex++; - break; + ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + } + ComputeData = 1; + } + else if (TestMatch && TestKnown) + { + LastPerimeter = ThisEnd - LastStart + 1; // to subtract + //ThisPerimeter = - LastPerimeter; + ThisPerimeter = -2 * LastPerimeter + + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + if (ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if (ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } + } - case 7: //|ooxxxxx| - //|yyyy | + if (ThisRegionNum != -1) + { + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + if (ThisStart - 1 != LastEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + } + // si hem creat un nou blob, afegim tb a l'anterior + if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + //afegim la cantonada a la regio + actualedge.x = ThisEnd; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + if (ThisStart - 1 != LastEnd) + { + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + } - if (!TestMatch && !TestKnown) // Different color and unknown => new region - { - ThisParent = LastRegionNum; - ThisRegionNum = ++HighRegionNum; - ThisArea = ThisEnd - ThisStart + 1; - ThisPerimeter = 2 + 2 * ThisArea; - RegionData.push_back(new CBlob()); - regionDataThisRegion = RegionData.back(); - SubsumedRegion = NewSubsume(SubsumedRegion, HighRegionNum); - if (CandidatExterior) - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ThisIndex++; + break; - } - else if (TestMatch && !TestKnown) - { - ThisRegionNum = LastRegionNum; - regionDataThisRegion = regionDataLastRegion; - ThisArea = ThisEnd - ThisStart + 1; - ThisPerimeter = 2 + ThisArea; - LastPerimeter = ThisEnd - LastStart + 1; - ThisPerimeter = 2 + 2 * ThisArea - LastPerimeter - + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); - if (CandidatExterior) - { - ThisExternPerimeter = GetExternPerimeter(ThisStart, ThisEnd, ThisRow, - inputImage->width, inputImage->height, - imatgePerimetreExtern); + case 8: //| xxx| + //|yyyy | - ThisExternPerimeter += PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); - } - ComputeData = 1; - } - else if (TestMatch && TestKnown) - { - LastPerimeter = ThisEnd - LastStart + 1; // to subtract - //ThisPerimeter = - LastPerimeter; - ThisPerimeter = -2 * LastPerimeter - + PERIMETRE_DIAGONAL + PERIMETRE_DIAGONAL * (ThisStart != LastStart); + #ifdef B_CONNECTIVITAT_8 + // fusionem blobs + if (TestMatch) + { + if (ThisRegionNum > LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, + findmoments, ThisRegionNum, LastRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; + if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + } + ThisRegionNum = LastRegionNum; + } + else if (ThisRegionNum < LastRegionNum) + { + Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, + findmoments, LastRegionNum, ThisRegionNum); + for (int iOld = 0; iOld < MaxIndexCount; iOld++) + { + if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; + if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + } + LastRegionNum = ThisRegionNum; + } - if (ThisRegionNum > LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, - findmoments, ThisRegionNum, LastRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) + regionDataThisRegion->perimeter = regionDataThisRegion->perimeter + PERIMETRE_DIAGONAL * 2; + } + #endif + + if (ThisRegionNum != -1) + { + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataThisRegion->edges, &actualedge); + } + #ifdef B_CONNECTIVITAT_8 + // si hem creat un nou blob, afegim tb a l'anterior + if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + { + #endif + //afegim la cantonada a la regio + actualedge.x = ThisStart - 1; + actualedge.y = ThisRow - 1; + cvSeqPush(regionDataLastRegion->edges, &actualedge); + #ifdef B_CONNECTIVITAT_8 + } + #endif + + ThisRegion[ThisIndex] = ThisRegionNum; + LastRegion[LastIndex] = LastRegionNum; + ThisIndex++; + #ifdef B_CONNECTIVITAT_8 + LastIndex--; + #endif + break; + + default: + ErrorFlag = -1; + } // end switch case + + // calculate the blob moments and mean gray level of the current blob (ThisRegionNum) + if (ComputeData > 0) { - if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; - if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + // compute blob moments if necessary + if (findmoments) + { + float ImageRow = (float)(ThisRow - 1); + + for (int k = ThisStart; k <= ThisEnd; k++) + { + ThisSumX += (float)(k - 1); + ThisSumXX += (float)(k - 1) * (k - 1); + } + + ThisSumXY = ThisSumX * ImageRow; + ThisSumY = ThisArea * ImageRow; + ThisSumYY = ThisSumY * ImageRow; + + } + + // compute the mean gray level and its std deviation + if (ThisRow <= Rows) + { + pImageAux = pImage + ThisStart; + if (maskImage != NULL) pMaskAux = pMask + ThisStart; + for (int k = ThisStart; k <= ThisEnd; k++) + { + if ((k > 0) && (k <= Cols)) + { + if (maskImage != NULL) + { + // només es té en compte el valor del pÃxel de la + // imatge que queda dins de la mà scara + // (de pas, comptem el nombre de pÃxels de la mà scara) + if (((unsigned char)*pMaskAux) != PIXEL_EXTERIOR) + { + imagevalue = (unsigned char)(*pImageAux); + regionDataThisRegion->mean += imagevalue; + regionDataThisRegion->stddev += imagevalue*imagevalue; + } + else + { + nombre_pixels_mascara++; + } + } + else + { + imagevalue = (unsigned char)(*pImageAux); + regionDataThisRegion->mean += imagevalue; + regionDataThisRegion->stddev += imagevalue*imagevalue; + + } + } + pImageAux++; + if (maskImage != NULL) pMaskAux++; + } + } + + // compute the min and max values of X and Y + if (ThisStart - 1 < (int)ThisMinX) ThisMinX = (float)(ThisStart - 1); + if (ThisMinX < (float) 0.0) ThisMinX = (float) 0.0; + if (ThisEnd > (int) ThisMaxX) ThisMaxX = (float)ThisEnd; + + if (ThisRow - 1 < ThisMinY) ThisMinY = ThisRow - 1; + if (ThisMinY < (float) 0.0) ThisMinY = (float) 0.0; + if (ThisRow > ThisMaxY) ThisMaxY = ThisRow; } - ThisRegionNum = LastRegionNum; - } - else if (ThisRegionNum < LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, - findmoments, LastRegionNum, ThisRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) + + // put the current results into RegionData + if (ThisRegionNum >= 0) { - if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; - if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; + if (ThisParent >= 0) { regionDataThisRegion->parent = (int)ThisParent; } + regionDataThisRegion->etiqueta = ThisRegionNum; + regionDataThisRegion->area += ThisArea; + regionDataThisRegion->perimeter += ThisPerimeter; + regionDataThisRegion->externPerimeter += ThisExternPerimeter; + + if (ComputeData > 0) + { + if (findmoments) + { + regionDataThisRegion->sumx += ThisSumX; + regionDataThisRegion->sumy += ThisSumY; + regionDataThisRegion->sumxx += ThisSumXX; + regionDataThisRegion->sumyy += ThisSumYY; + regionDataThisRegion->sumxy += ThisSumXY; + } + regionDataThisRegion->perimeter -= LastPerimeter; + regionDataThisRegion->minx = MIN(regionDataThisRegion->minx, ThisMinX); + regionDataThisRegion->maxx = MAX(regionDataThisRegion->maxx, ThisMaxX); + regionDataThisRegion->miny = MIN(regionDataThisRegion->miny, ThisMinY); + regionDataThisRegion->maxy = MAX(regionDataThisRegion->maxy, ThisMaxY); + } + // blobs externs + if (CandidatExterior) + { + regionDataThisRegion->exterior = true; + } + } - LastRegionNum = ThisRegionNum; - } - } + } // end Main loop - if (ThisRegionNum != -1) - { - //afegim la cantonada a la regio - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - if (ThisStart - 1 != LastEnd) - { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); + if (ErrorFlag != 0) { + delete[] Transition; + delete[] ThisRegion; + delete[] LastRegion; + return false; } - } - // si hem creat un nou blob, afegim tb a l'anterior - if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) + // ens situem al primer pixel de la seguent fila + pImage = inputImage->imageData - 1 + startCol + (ThisRow + startRow) * inputImage->widthStep; + + if (maskImage != NULL) + pMask = maskImage->imageData - 1 + ThisRow * maskImage->widthStep; + } // end Loop over all rows + + // eliminem l'à rea del marc + // i també els pÃxels de la mà scara + // ATENCIO: PERFER: el fet de restar el nombre_pixels_mascara del + // blob 0 només serà cert si la mà scara té contacte amb el marc. + // Si no, s'haurà de trobar quin és el blob que conté més pÃxels del + // compte. + RegionData[0]->area -= (Rows + 1 + Cols + 1) * 2 + nombre_pixels_mascara; + + // eliminem el perÃmetre de més: + // - sense marc: 2m+2n (perÃmetre extern) + // - amb marc: 2(m+2)+2(n+2) = 2m+2n + 8 + // (segurament no és del tot acurat) + // (i amb les mà scares encara menys...) + RegionData[0]->perimeter -= 8.0; + + // Condense the list + blob_vector::iterator itNew, itOld, iti; + CBlob *blobActual; + + itNew = RegionData.begin(); + itOld = RegionData.begin(); + int iNew = 0; + for (int iOld = 0; iOld <= HighRegionNum; iOld++, itOld++) { - //afegim la cantonada a la regio - actualedge.x = ThisEnd; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); - if (ThisStart - 1 != LastEnd) + if (SubsumedRegion[iOld] < 1) // This number not subsumed { - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); + // Move data from old region number to new region number + //*RegionData[iNew] = *RegionData[iOld]; + **itNew = **itOld; + + // Update and parent pointer if necessary + iti = RegionData.begin(); + for (int i = 0; i <= HighRegionNum; i++) + { + //if(RegionData[i]->parent == iOld) { RegionData[i]->parent = iNew; } + if ((*iti)->parent == iOld) { (*iti)->parent = iNew; } + + ++iti; + } + iNew++; + ++itNew; } } - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - ThisIndex++; - break; - case 8: //| xxx| - //|yyyy | + HighRegionNum = iNew - 1; // Update where the data ends + RegionData[HighRegionNum]->parent = -1; // and set end of array flag -#ifdef B_CONNECTIVITAT_8 - // fusionem blobs - if (TestMatch) + + if (findmoments) { - if (ThisRegionNum > LastRegionNum) + iti = RegionData.begin(); + // Normalize summation fields into moments + for (ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++) { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataThisRegion, regionDataLastRegion, - findmoments, ThisRegionNum, LastRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) + blobActual = *iti; + + // Get averages + blobActual->sumx /= blobActual->area; + blobActual->sumy /= blobActual->area; + blobActual->sumxx /= blobActual->area; + blobActual->sumyy /= blobActual->area; + blobActual->sumxy /= blobActual->area; + + // Create moments + blobActual->sumxx -= blobActual->sumx * blobActual->sumx; + blobActual->sumyy -= blobActual->sumy * blobActual->sumy; + blobActual->sumxy -= blobActual->sumx * blobActual->sumy; + if (blobActual->sumxy > -1.0E-14 && blobActual->sumxy < 1.0E-14) { - if (ThisRegion[iOld] == ThisRegionNum) ThisRegion[iOld] = LastRegionNum; - if (LastRegion[iOld] == ThisRegionNum) LastRegion[iOld] = LastRegionNum; + blobActual->sumxy = (float) 0.0; // Eliminate roundoff error } - ThisRegionNum = LastRegionNum; - } - else if (ThisRegionNum < LastRegionNum) - { - Subsume(RegionData, HighRegionNum, SubsumedRegion, regionDataLastRegion, regionDataThisRegion, - findmoments, LastRegionNum, ThisRegionNum); - for (int iOld = 0; iOld < MaxIndexCount; iOld++) - { - if (ThisRegion[iOld] == LastRegionNum) ThisRegion[iOld] = ThisRegionNum; - if (LastRegion[iOld] == LastRegionNum) LastRegion[iOld] = ThisRegionNum; - } - LastRegionNum = ThisRegionNum; - } - regionDataThisRegion->perimeter = regionDataThisRegion->perimeter + PERIMETRE_DIAGONAL * 2; + } } -#endif - if (ThisRegionNum != -1) - { - //afegim la cantonada a la regio - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataThisRegion->edges, &actualedge); - } -#ifdef B_CONNECTIVITAT_8 - // si hem creat un nou blob, afegim tb a l'anterior - if (!TestMatch && LastRegionNum != -1 && LastRegionNum != ThisRegionNum) - { -#endif - //afegim la cantonada a la regio - actualedge.x = ThisStart - 1; - actualedge.y = ThisRow - 1; - cvSeqPush(regionDataLastRegion->edges, &actualedge); -#ifdef B_CONNECTIVITAT_8 - } -#endif - - ThisRegion[ThisIndex] = ThisRegionNum; - LastRegion[LastIndex] = LastRegionNum; - ThisIndex++; -#ifdef B_CONNECTIVITAT_8 - LastIndex--; -#endif - break; - - default: - ErrorFlag = -1; - } // end switch case - - // calculate the blob moments and mean gray level of the current blob (ThisRegionNum) - if (ComputeData > 0) - { - // compute blob moments if necessary - if (findmoments) + //Get the real mean and std deviation + iti = RegionData.begin(); + for (ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++) { - float ImageRow = (float)(ThisRow - 1); - - for (int k = ThisStart; k <= ThisEnd; k++) + blobActual = *iti; + if (blobActual->area > 1) { - ThisSumX += (float)(k - 1); - ThisSumXX += (float)(k - 1) * (k - 1); + blobActual->stddev = + sqrt( + ( + blobActual->stddev * blobActual->area - + blobActual->mean * blobActual->mean + ) / + (blobActual->area*(blobActual->area - 1)) + ); } + else + blobActual->stddev = 0; - ThisSumXY = ThisSumX * ImageRow; - ThisSumY = ThisArea * ImageRow; - ThisSumYY = ThisSumY * ImageRow; + if (blobActual->area > 0) + blobActual->mean /= blobActual->area; + else + blobActual->mean = 0; } - - // compute the mean gray level and its std deviation - if (ThisRow <= Rows) + // eliminem els blobs subsumats + blob_vector::iterator itBlobs = RegionData.begin() + HighRegionNum + 1; + while (itBlobs != RegionData.end()) { - pImageAux = pImage + ThisStart; - if (maskImage != NULL) pMaskAux = pMask + ThisStart; - for (int k = ThisStart; k <= ThisEnd; k++) - { - if ((k > 0) && (k <= Cols)) - { - if (maskImage != NULL) - { - // només es té en compte el valor del pÃxel de la - // imatge que queda dins de la mà scara - // (de pas, comptem el nombre de pÃxels de la mà scara) - if (((unsigned char)*pMaskAux) != PIXEL_EXTERIOR) - { - imagevalue = (unsigned char)(*pImageAux); - regionDataThisRegion->mean += imagevalue; - regionDataThisRegion->stddev += imagevalue*imagevalue; - } - else - { - nombre_pixels_mascara++; - } - } - else - { - imagevalue = (unsigned char)(*pImageAux); - regionDataThisRegion->mean += imagevalue; - regionDataThisRegion->stddev += imagevalue*imagevalue; - - } - } - pImageAux++; - if (maskImage != NULL) pMaskAux++; - } + delete *itBlobs; + //RegionData.erase( itBlobs ); + ++itBlobs; } + RegionData.erase(RegionData.begin() + HighRegionNum + 1, RegionData.end()); - // compute the min and max values of X and Y - if (ThisStart - 1 < (int)ThisMinX) ThisMinX = (float)(ThisStart - 1); - if (ThisMinX < (float) 0.0) ThisMinX = (float) 0.0; - if (ThisEnd > (int) ThisMaxX) ThisMaxX = (float)ThisEnd; + //free(RegionData); + free(SubsumedRegion); + delete[] Transition; + delete[] ThisRegion; + delete[] LastRegion; - if (ThisRow - 1 < ThisMinY) ThisMinY = ThisRow - 1; - if (ThisMinY < (float) 0.0) ThisMinY = (float) 0.0; - if (ThisRow > ThisMaxY) ThisMaxY = ThisRow; + if (imatgePerimetreExtern) cvReleaseImage(&imatgePerimetreExtern); + + return true; } - // put the current results into RegionData - if (ThisRegionNum >= 0) - { - if (ThisParent >= 0) { regionDataThisRegion->parent = (int)ThisParent; } - regionDataThisRegion->etiqueta = ThisRegionNum; - regionDataThisRegion->area += ThisArea; - regionDataThisRegion->perimeter += ThisPerimeter; - regionDataThisRegion->externPerimeter += ThisExternPerimeter; - if (ComputeData > 0) + int *NewSubsume(int *subsumed, int index_subsume) + { + if (index_subsume == 0) { - if (findmoments) - { - regionDataThisRegion->sumx += ThisSumX; - regionDataThisRegion->sumy += ThisSumY; - regionDataThisRegion->sumxx += ThisSumXX; - regionDataThisRegion->sumyy += ThisSumYY; - regionDataThisRegion->sumxy += ThisSumXY; - } - regionDataThisRegion->perimeter -= LastPerimeter; - regionDataThisRegion->minx = MIN(regionDataThisRegion->minx, ThisMinX); - regionDataThisRegion->maxx = MAX(regionDataThisRegion->maxx, ThisMaxX); - regionDataThisRegion->miny = MIN(regionDataThisRegion->miny, ThisMinY); - regionDataThisRegion->maxy = MAX(regionDataThisRegion->maxy, ThisMaxY); + subsumed = (int*)malloc(sizeof(int)); } - // blobs externs - if (CandidatExterior) + else { - regionDataThisRegion->exterior = true; + subsumed = (int*)realloc(subsumed, (index_subsume + 1) * sizeof(int)); } - + subsumed[index_subsume] = 0; + return subsumed; } - } // end Main loop - if (ErrorFlag != 0) { - delete[] Transition; - delete[] ThisRegion; - delete[] LastRegion; - return false; - } - // ens situem al primer pixel de la seguent fila - pImage = inputImage->imageData - 1 + startCol + (ThisRow + startRow) * inputImage->widthStep; - - if (maskImage != NULL) - pMask = maskImage->imageData - 1 + ThisRow * maskImage->widthStep; - } // end Loop over all rows - - // eliminem l'à rea del marc - // i també els pÃxels de la mà scara - // ATENCIO: PERFER: el fet de restar el nombre_pixels_mascara del - // blob 0 només serà cert si la mà scara té contacte amb el marc. - // Si no, s'haurà de trobar quin és el blob que conté més pÃxels del - // compte. - RegionData[0]->area -= (Rows + 1 + Cols + 1) * 2 + nombre_pixels_mascara; - - // eliminem el perÃmetre de més: - // - sense marc: 2m+2n (perÃmetre extern) - // - amb marc: 2(m+2)+2(n+2) = 2m+2n + 8 - // (segurament no és del tot acurat) - // (i amb les mà scares encara menys...) - RegionData[0]->perimeter -= 8.0; - - // Condense the list - blob_vector::iterator itNew, itOld, iti; - CBlob *blobActual; - - itNew = RegionData.begin(); - itOld = RegionData.begin(); - int iNew = 0; - for (int iOld = 0; iOld <= HighRegionNum; iOld++, itOld++) - { - if (SubsumedRegion[iOld] < 1) // This number not subsumed - { - // Move data from old region number to new region number - //*RegionData[iNew] = *RegionData[iOld]; - **itNew = **itOld; - - // Update and parent pointer if necessary - iti = RegionData.begin(); - for (int i = 0; i <= HighRegionNum; i++) + /** + Fusiona dos blobs i afegeix el blob les caracterÃstiques del blob RegionData[HiNum] + al blob RegionData[LoNum]. Al final allibera el blob de RegionData[HiNum] + */ + void Subsume(blob_vector &RegionData, + int HighRegionNum, + int* SubsumedRegion, + CBlob* blobHi, + CBlob* blobLo, + bool findmoments, + int HiNum, + int LoNum) { - //if(RegionData[i]->parent == iOld) { RegionData[i]->parent = iNew; } - if ((*iti)->parent == iOld) { (*iti)->parent = iNew; } - - ++iti; - } - iNew++; - ++itNew; - } - } - + // cout << "\nSubsuming " << HiNum << " into " << LoNum << endl; // for debugging - HighRegionNum = iNew - 1; // Update where the data ends - RegionData[HighRegionNum]->parent = -1; // and set end of array flag + int i; + blobLo->minx = MIN(blobHi->minx, blobLo->minx); + blobLo->miny = MIN(blobHi->miny, blobLo->miny); + blobLo->maxx = MAX(blobHi->maxx, blobLo->maxx); + blobLo->maxy = MAX(blobHi->maxy, blobLo->maxy); + blobLo->area += blobHi->area; + blobLo->perimeter += blobHi->perimeter; + blobLo->externPerimeter += blobHi->externPerimeter; + blobLo->exterior = blobLo->exterior || blobHi->exterior; + blobLo->mean += blobHi->mean; + blobLo->stddev += blobHi->stddev; - if (findmoments) - { - iti = RegionData.begin(); - // Normalize summation fields into moments - for (ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++) - { - blobActual = *iti; - - // Get averages - blobActual->sumx /= blobActual->area; - blobActual->sumy /= blobActual->area; - blobActual->sumxx /= blobActual->area; - blobActual->sumyy /= blobActual->area; - blobActual->sumxy /= blobActual->area; - - // Create moments - blobActual->sumxx -= blobActual->sumx * blobActual->sumx; - blobActual->sumyy -= blobActual->sumy * blobActual->sumy; - blobActual->sumxy -= blobActual->sumx * blobActual->sumy; - if (blobActual->sumxy > -1.0E-14 && blobActual->sumxy < 1.0E-14) - { - blobActual->sumxy = (float) 0.0; // Eliminate roundoff error - } - - } - } - - //Get the real mean and std deviation - iti = RegionData.begin(); - for (ThisRegionNum = 0; ThisRegionNum <= HighRegionNum; ThisRegionNum++, iti++) - { - blobActual = *iti; - if (blobActual->area > 1) - { - blobActual->stddev = - sqrt( - ( - blobActual->stddev * blobActual->area - - blobActual->mean * blobActual->mean - ) / - (blobActual->area*(blobActual->area - 1)) - ); - } - else - blobActual->stddev = 0; - - if (blobActual->area > 0) - blobActual->mean /= blobActual->area; - else - blobActual->mean = 0; - - } - // eliminem els blobs subsumats - blob_vector::iterator itBlobs = RegionData.begin() + HighRegionNum + 1; - while (itBlobs != RegionData.end()) - { - delete *itBlobs; - //RegionData.erase( itBlobs ); - ++itBlobs; - } - RegionData.erase(RegionData.begin() + HighRegionNum + 1, RegionData.end()); - - //free(RegionData); - free(SubsumedRegion); - delete[] Transition; - delete[] ThisRegion; - delete[] LastRegion; - - if (imatgePerimetreExtern) cvReleaseImage(&imatgePerimetreExtern); - - return true; - } - - - int *NewSubsume(int *subsumed, int index_subsume) - { - if (index_subsume == 0) - { - subsumed = (int*)malloc(sizeof(int)); - } - else - { - subsumed = (int*)realloc(subsumed, (index_subsume + 1) * sizeof(int)); - } - subsumed[index_subsume] = 0; - return subsumed; - } + if (findmoments) + { + blobLo->sumx += blobHi->sumx; + blobLo->sumy += blobHi->sumy; + blobLo->sumxx += blobHi->sumxx; + blobLo->sumyy += blobHi->sumyy; + blobLo->sumxy += blobHi->sumxy; + } + // Make sure no region still has subsumed region as parent + blob_vector::iterator it = (RegionData.begin() + HiNum + 1); - /** - Fusiona dos blobs i afegeix el blob les caracterÃstiques del blob RegionData[HiNum] - al blob RegionData[LoNum]. Al final allibera el blob de RegionData[HiNum] - */ - void Subsume(blob_vector &RegionData, - int HighRegionNum, - int* SubsumedRegion, - CBlob* blobHi, - CBlob* blobLo, - bool findmoments, - int HiNum, - int LoNum) - { - // cout << "\nSubsuming " << HiNum << " into " << LoNum << endl; // for debugging - - int i; - - blobLo->minx = MIN(blobHi->minx, blobLo->minx); - blobLo->miny = MIN(blobHi->miny, blobLo->miny); - blobLo->maxx = MAX(blobHi->maxx, blobLo->maxx); - blobLo->maxy = MAX(blobHi->maxy, blobLo->maxy); - blobLo->area += blobHi->area; - blobLo->perimeter += blobHi->perimeter; - blobLo->externPerimeter += blobHi->externPerimeter; - blobLo->exterior = blobLo->exterior || blobHi->exterior; - blobLo->mean += blobHi->mean; - blobLo->stddev += blobHi->stddev; - - if (findmoments) - { - blobLo->sumx += blobHi->sumx; - blobLo->sumy += blobHi->sumy; - blobLo->sumxx += blobHi->sumxx; - blobLo->sumyy += blobHi->sumyy; - blobLo->sumxy += blobHi->sumxy; - } - // Make sure no region still has subsumed region as parent - blob_vector::iterator it = (RegionData.begin() + HiNum + 1); + for (i = HiNum + 1; i <= HighRegionNum; i++, it++) + { + if ((*it)->parent == (float)HiNum) { (*it)->parent = LoNum; } + } - for (i = HiNum + 1; i <= HighRegionNum; i++, it++) - { - if ((*it)->parent == (float)HiNum) { (*it)->parent = LoNum; } - } + // Mark dead region number for future compression + SubsumedRegion[HiNum] = 1; + // marquem el blob com a lliure + blobHi->etiqueta = -1; - // Mark dead region number for future compression - SubsumedRegion[HiNum] = 1; - // marquem el blob com a lliure - blobHi->etiqueta = -1; + // Atenció!!!! abans d'eliminar els edges + // s'han de traspassar del blob HiNum al blob LoNum + blobHi->CopyEdges(*blobLo); + blobHi->ClearEdges(); + } - // Atenció!!!! abans d'eliminar els edges - // s'han de traspassar del blob HiNum al blob LoNum - blobHi->CopyEdges(*blobLo); - blobHi->ClearEdges(); - } + /** + - FUNCIÓ: GetExternPerimeter + - FUNCIONALITAT: Retorna el perimetre extern d'una run lenght + - PARÀMETRES: + - start: columna d'inici del run + - end: columna final del run + - row: fila del run + - maskImage: mà scara pels pixels externs + - RESULTAT: + - quantitat de perimetre extern d'un run, suposant que és un blob + d'una única fila d'alçada + - RESTRICCIONS: + - AUTOR: + - DATA DE CREACIÓ: 2006/02/27 + - MODIFICACIÓ: Data. Autor. Descripció. + */ + double GetExternPerimeter(int start, int end, int row, int width, int height, IplImage *imatgePerimetreExtern) + { + double perimeter = 0.0f; + char *pPerimetre; - /** - - FUNCIÓ: GetExternPerimeter - - FUNCIONALITAT: Retorna el perimetre extern d'una run lenght - - PARÀMETRES: - - start: columna d'inici del run - - end: columna final del run - - row: fila del run - - maskImage: mà scara pels pixels externs - - RESULTAT: - - quantitat de perimetre extern d'un run, suposant que és un blob - d'una única fila d'alçada - - RESTRICCIONS: - - AUTOR: - - DATA DE CREACIÓ: 2006/02/27 - - MODIFICACIÓ: Data. Autor. Descripció. - */ - double GetExternPerimeter(int start, int end, int row, int width, int height, IplImage *imatgePerimetreExtern) - { - double perimeter = 0.0f; - char *pPerimetre; + // comprovem les dimensions de la imatge + perimeter += (start <= 0) + (end >= width - 1); + if (row <= 1) perimeter += start - end; + if (row >= height - 1) perimeter += start - end; - // comprovem les dimensions de la imatge - perimeter += (start <= 0) + (end >= width - 1); - if (row <= 1) perimeter += start - end; - if (row >= height - 1) perimeter += start - end; + // comprovem els pixels que toquen a la mà scara (si s'escau) + if (imatgePerimetreExtern != NULL) + { + if (row <= 0 || row >= height) return perimeter; - // comprovem els pixels que toquen a la mà scara (si s'escau) - if (imatgePerimetreExtern != NULL) - { - if (row <= 0 || row >= height) return perimeter; + if (start < 0) start = 1; + if (end >= width) end = width - 2; - if (start < 0) start = 1; - if (end >= width) end = width - 2; + pPerimetre = imatgePerimetreExtern->imageData + (row - 1) * imatgePerimetreExtern->widthStep + start; + for (int x = start - 1; x <= end; x++) + { + perimeter += *pPerimetre; + pPerimetre++; + } + } - pPerimetre = imatgePerimetreExtern->imageData + (row - 1) * imatgePerimetreExtern->widthStep + start; - for (int x = start - 1; x <= end; x++) - { - perimeter += *pPerimetre; - pPerimetre++; + return perimeter; + } } } - - return perimeter; } - } diff --git a/src/algorithms/MultiLayer/BlobExtraction.h b/src/algorithms/MultiLayer/BlobExtraction.h index 6a9dc94cbb5f471c4a4ae234efe79f5ced610ddd..dcd0dfe552d7e7aa4245323f39277149d928f2fa 100644 --- a/src/algorithms/MultiLayer/BlobExtraction.h +++ b/src/algorithms/MultiLayer/BlobExtraction.h @@ -1,17 +1,26 @@ #pragma once -namespace Blob +namespace bgslibrary { - //! Extreu els blobs d'una imatge - bool BlobAnalysis(IplImage* inputImage, uchar threshold, IplImage* maskImage, - bool borderColor, bool findmoments, blob_vector &RegionData); + namespace algorithms + { + namespace multilayer + { + namespace blob + { + //! Extreu els blobs d'una imatge + bool BlobAnalysis(IplImage* inputImage, uchar threshold, IplImage* maskImage, + bool borderColor, bool findmoments, blob_vector &RegionData); - // FUNCIONS AUXILIARS + // FUNCIONS AUXILIARS - //! Fusiona dos blobs - void Subsume(blob_vector &RegionData, int, int*, CBlob*, CBlob*, bool, int, int); - //! Reallocata el vector auxiliar de blobs subsumats - int *NewSubsume(int *SubSumedRegion, int elems_inbuffer); - //! Retorna el perimetre extern d'una run lenght - double GetExternPerimeter(int start, int end, int row, int width, int height, IplImage *maskImage); + //! Fusiona dos blobs + void Subsume(blob_vector &RegionData, int, int*, CBlob*, CBlob*, bool, int, int); + //! Reallocata el vector auxiliar de blobs subsumats + int *NewSubsume(int *SubSumedRegion, int elems_inbuffer); + //! Retorna el perimetre extern d'una run lenght + double GetExternPerimeter(int start, int end, int row, int width, int height, IplImage *maskImage); + } + } + } } diff --git a/src/algorithms/MultiLayer/BlobResult.cpp b/src/algorithms/MultiLayer/BlobResult.cpp index f5b2f757d5c20a5a2d01d707f4460f12f41fa906..10aafbfcb293c6fd6fb0dfd3071aafa213420a74 100644 --- a/src/algorithms/MultiLayer/BlobResult.cpp +++ b/src/algorithms/MultiLayer/BlobResult.cpp @@ -6,791 +6,795 @@ #include "BlobResult.h" #include "BlobExtraction.h" -/************************************************************************** -Constructors / Destructors -**************************************************************************/ - -namespace Blob +namespace bgslibrary { - /** - - FUNCIÓ: CBlobResult - - FUNCIONALITAT: Constructor estandard. - - PARÀMETRES: - - RESULTAT: - - Crea un CBlobResult sense cap blob - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobResult - - FUNCTIONALITY: Standard constructor - - PARAMETERS: - - RESULT: - - creates an empty set of blobs - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlobResult::CBlobResult() - { - m_blobs = blob_vector(); - } - - /** - - FUNCIÓ: CBlobResult - - FUNCIONALITAT: Constructor a partir d'una imatge. Inicialitza la seqüència de blobs - amb els blobs resultants de l'anà lisi de blobs de la imatge. - - PARÀMETRES: - - source: imatge d'on s'extreuran els blobs - - mask: mà scara a aplicar. Només es calcularan els blobs on la mà scara sigui - diferent de 0. Els blobs que toquin a un pixel 0 de la mà scara seran - considerats exteriors. - - threshold: llindar que s'aplicarà a la imatge source abans de calcular els blobs - - findmoments: indica si s'han de calcular els moments de cada blob - - RESULTAT: - - objecte CBlobResult amb els blobs de la imatge source - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlob - - FUNCTIONALITY: Constructor from an image. Fills an object with all the blobs in - the image - - PARAMETERS: - - source: image to extract the blobs from - - mask: optional mask to apply. The blobs will be extracted where the mask is - not 0. All the neighbouring blobs where the mask is 0 will be extern blobs - - threshold: threshold level to apply to the image before computing blobs - - findmoments: true to calculate the blob moments (slower) - - RESULT: - - object with all the blobs in the image. It throws an EXCEPCIO_CALCUL_BLOBS - if some error appears in the BlobAnalysis function - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlobResult::CBlobResult(IplImage *source, IplImage *mask, int threshold, bool findmoments) - { - bool success; - - try - { - // cridem la funció amb el marc a true=1=blanc (aixà no unirà els blobs externs) - success = BlobAnalysis(source, (uchar)threshold, mask, true, findmoments, m_blobs); - } - catch (...) - { - success = false; - } - - if (!success) throw EXCEPCIO_CALCUL_BLOBS; - } - - /** - - FUNCIÓ: CBlobResult - - FUNCIONALITAT: Constructor de còpia. Inicialitza la seqüència de blobs - amb els blobs del parà metre. - - PARÀMETRES: - - source: objecte que es copiarà - - RESULTAT: - - objecte CBlobResult amb els blobs de l'objecte source - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobResult - - FUNCTIONALITY: Copy constructor - - PARAMETERS: - - source: object to copy - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlobResult::CBlobResult(const CBlobResult &source) - { - m_blobs = blob_vector(source.GetNumBlobs()); - - // creem el nou a partir del passat com a parà metre - m_blobs = blob_vector(source.GetNumBlobs()); - // copiem els blobs de l'origen a l'actual - blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); - blob_vector::iterator pBlobsDst = m_blobs.begin(); - - while (pBlobsSrc != source.m_blobs.end()) - { - // no podem cridar a l'operador = ja que blob_vector és un - // vector de CBlob*. Per tant, creem un blob nou a partir del - // blob original - *pBlobsDst = new CBlob(**pBlobsSrc); - ++pBlobsSrc; - ++pBlobsDst; - } - } - - - - /** - - FUNCIÓ: ~CBlobResult - - FUNCIONALITAT: Destructor estandard. - - PARÀMETRES: - - RESULTAT: - - Allibera la memòria reservada de cadascun dels blobs de la classe - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: ~CBlobResult - - FUNCTIONALITY: Destructor - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlobResult::~CBlobResult() + namespace algorithms { - ClearBlobs(); - } - - /************************************************************************** - Operadors / Operators - **************************************************************************/ - - - /** - - FUNCIÓ: operador = - - FUNCIONALITAT: Assigna un objecte source a l'actual - - PARÀMETRES: - - source: objecte a assignar - - RESULTAT: - - Substitueix els blobs actuals per els de l'objecte source - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: Assigment operator - - FUNCTIONALITY: - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlobResult& CBlobResult::operator=(const CBlobResult& source) - { - // si ja són el mateix, no cal fer res - if (this != &source) + namespace multilayer { - // alliberem el conjunt de blobs antic - for (int i = 0; i < GetNumBlobs(); i++) + namespace blob { - delete m_blobs[i]; - } - m_blobs.clear(); - // creem el nou a partir del passat com a parà metre - m_blobs = blob_vector(source.GetNumBlobs()); - // copiem els blobs de l'origen a l'actual - blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); - blob_vector::iterator pBlobsDst = m_blobs.begin(); - - while (pBlobsSrc != source.m_blobs.end()) - { - // no podem cridar a l'operador = ja que blob_vector és un - // vector de CBlob*. Per tant, creem un blob nou a partir del - // blob original - *pBlobsDst = new CBlob(**pBlobsSrc); - ++pBlobsSrc; - ++pBlobsDst; - } - } - return *this; - } - + /** + - FUNCIÓ: CBlobResult + - FUNCIONALITAT: Constructor estandard. + - PARÀMETRES: + - RESULTAT: + - Crea un CBlobResult sense cap blob + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobResult + - FUNCTIONALITY: Standard constructor + - PARAMETERS: + - RESULT: + - creates an empty set of blobs + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlobResult::CBlobResult() + { + m_blobs = blob_vector(); + } - /** - - FUNCIÓ: operador + - - FUNCIONALITAT: Concatena els blobs de dos CBlobResult - - PARÀMETRES: - - source: d'on s'agafaran els blobs afegits a l'actual - - RESULTAT: - - retorna un nou CBlobResult amb els dos CBlobResult concatenats - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - NOTA: per la implementació, els blobs del parà metre es posen en ordre invers - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: + operator - - FUNCTIONALITY: Joins the blobs in source with the current ones - - PARAMETERS: - - source: object to copy the blobs - - RESULT: - - object with the actual blobs and the source blobs - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlobResult CBlobResult::operator+(const CBlobResult& source) - { - //creem el resultat a partir dels blobs actuals - CBlobResult resultat(*this); + /** + - FUNCIÓ: CBlobResult + - FUNCIONALITAT: Constructor a partir d'una imatge. Inicialitza la seqüència de blobs + amb els blobs resultants de l'anà lisi de blobs de la imatge. + - PARÀMETRES: + - source: imatge d'on s'extreuran els blobs + - mask: mà scara a aplicar. Només es calcularan els blobs on la mà scara sigui + diferent de 0. Els blobs que toquin a un pixel 0 de la mà scara seran + considerats exteriors. + - threshold: llindar que s'aplicarà a la imatge source abans de calcular els blobs + - findmoments: indica si s'han de calcular els moments de cada blob + - RESULTAT: + - objecte CBlobResult amb els blobs de la imatge source + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlob + - FUNCTIONALITY: Constructor from an image. Fills an object with all the blobs in + the image + - PARAMETERS: + - source: image to extract the blobs from + - mask: optional mask to apply. The blobs will be extracted where the mask is + not 0. All the neighbouring blobs where the mask is 0 will be extern blobs + - threshold: threshold level to apply to the image before computing blobs + - findmoments: true to calculate the blob moments (slower) + - RESULT: + - object with all the blobs in the image. It throws an EXCEPCIO_CALCUL_BLOBS + if some error appears in the BlobAnalysis function + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlobResult::CBlobResult(IplImage *source, IplImage *mask, int threshold, bool findmoments) + { + bool success; + + try + { + // cridem la funció amb el marc a true=1=blanc (aixà no unirà els blobs externs) + success = BlobAnalysis(source, (uchar)threshold, mask, true, findmoments, m_blobs); + } + catch (...) + { + success = false; + } + + if (!success) throw EXCEPCIO_CALCUL_BLOBS; + } - // reservem memòria per als nous blobs - resultat.m_blobs.resize(resultat.GetNumBlobs() + source.GetNumBlobs()); + /** + - FUNCIÓ: CBlobResult + - FUNCIONALITAT: Constructor de còpia. Inicialitza la seqüència de blobs + amb els blobs del parà metre. + - PARÀMETRES: + - source: objecte que es copiarà + - RESULTAT: + - objecte CBlobResult amb els blobs de l'objecte source + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobResult + - FUNCTIONALITY: Copy constructor + - PARAMETERS: + - source: object to copy + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlobResult::CBlobResult(const CBlobResult &source) + { + m_blobs = blob_vector(source.GetNumBlobs()); + + // creem el nou a partir del passat com a parà metre + m_blobs = blob_vector(source.GetNumBlobs()); + // copiem els blobs de l'origen a l'actual + blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); + blob_vector::iterator pBlobsDst = m_blobs.begin(); + + while (pBlobsSrc != source.m_blobs.end()) + { + // no podem cridar a l'operador = ja que blob_vector és un + // vector de CBlob*. Per tant, creem un blob nou a partir del + // blob original + *pBlobsDst = new CBlob(**pBlobsSrc); + ++pBlobsSrc; + ++pBlobsDst; + } + } - // declarem els iterador per recòrrer els blobs d'origen i desti - blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); - blob_vector::iterator pBlobsDst = resultat.m_blobs.end(); - // insertem els blobs de l'origen a l'actual - while (pBlobsSrc != source.m_blobs.end()) - { - --pBlobsDst; - *pBlobsDst = new CBlob(**pBlobsSrc); - ++pBlobsSrc; - } - return resultat; - } + /** + - FUNCIÓ: ~CBlobResult + - FUNCIONALITAT: Destructor estandard. + - PARÀMETRES: + - RESULTAT: + - Allibera la memòria reservada de cadascun dels blobs de la classe + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: ~CBlobResult + - FUNCTIONALITY: Destructor + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlobResult::~CBlobResult() + { + ClearBlobs(); + } - /************************************************************************** - Operacions / Operations - **************************************************************************/ - - /** - - FUNCIÓ: AddBlob - - FUNCIONALITAT: Afegeix un blob al conjunt - - PARÀMETRES: - - blob: blob a afegir - - RESULTAT: - - modifica el conjunt de blobs actual - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 2006/03/01 - - MODIFICACIÓ: Data. Autor. Descripció. - */ - void CBlobResult::AddBlob(CBlob *blob) - { - if (blob != NULL) - m_blobs.push_back(new CBlob(blob)); - } + /************************************************************************** + Operadors / Operators + **************************************************************************/ + + + /** + - FUNCIÓ: operador = + - FUNCIONALITAT: Assigna un objecte source a l'actual + - PARÀMETRES: + - source: objecte a assignar + - RESULTAT: + - Substitueix els blobs actuals per els de l'objecte source + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: Assigment operator + - FUNCTIONALITY: + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlobResult& CBlobResult::operator=(const CBlobResult& source) + { + // si ja són el mateix, no cal fer res + if (this != &source) + { + // alliberem el conjunt de blobs antic + for (int i = 0; i < GetNumBlobs(); i++) + { + delete m_blobs[i]; + } + m_blobs.clear(); + // creem el nou a partir del passat com a parà metre + m_blobs = blob_vector(source.GetNumBlobs()); + // copiem els blobs de l'origen a l'actual + blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); + blob_vector::iterator pBlobsDst = m_blobs.begin(); + + while (pBlobsSrc != source.m_blobs.end()) + { + // no podem cridar a l'operador = ja que blob_vector és un + // vector de CBlob*. Per tant, creem un blob nou a partir del + // blob original + *pBlobsDst = new CBlob(**pBlobsSrc); + ++pBlobsSrc; + ++pBlobsDst; + } + } + return *this; + } - /** - - FUNCIÓ: GetSTLResult - - FUNCIONALITAT: Calcula el resultat especificat sobre tots els blobs de la classe - - PARÀMETRES: - - evaluador: Qualsevol objecte derivat de COperadorBlob - - RESULTAT: - - Retorna un array de double's STL amb el resultat per cada blob - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: GetResult - - FUNCTIONALITY: Computes the function evaluador on all the blobs of the class - and returns a vector with the result - - PARAMETERS: - - evaluador: function to apply to each blob (any object derived from the - COperadorBlob class ) - - RESULT: - - vector with all the results in the same order as the blobs - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double_stl_vector CBlobResult::GetSTLResult(funcio_calculBlob *evaluador) const - { - if (GetNumBlobs() <= 0) - { - return double_stl_vector(); - } + /** + - FUNCIÓ: operador + + - FUNCIONALITAT: Concatena els blobs de dos CBlobResult + - PARÀMETRES: + - source: d'on s'agafaran els blobs afegits a l'actual + - RESULTAT: + - retorna un nou CBlobResult amb els dos CBlobResult concatenats + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - NOTA: per la implementació, els blobs del parà metre es posen en ordre invers + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: + operator + - FUNCTIONALITY: Joins the blobs in source with the current ones + - PARAMETERS: + - source: object to copy the blobs + - RESULT: + - object with the actual blobs and the source blobs + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlobResult CBlobResult::operator+(const CBlobResult& source) + { + //creem el resultat a partir dels blobs actuals + CBlobResult resultat(*this); - // definim el resultat - double_stl_vector result = double_stl_vector(GetNumBlobs()); - // i iteradors sobre els blobs i el resultat - double_stl_vector::iterator itResult = result.begin(); - blob_vector::const_iterator itBlobs = m_blobs.begin(); + // reservem memòria per als nous blobs + resultat.m_blobs.resize(resultat.GetNumBlobs() + source.GetNumBlobs()); - // avaluem la funció en tots els blobs - while (itBlobs != m_blobs.end()) - { - *itResult = (*evaluador)(**itBlobs); - ++itBlobs; - ++itResult; - } - return result; - } + // declarem els iterador per recòrrer els blobs d'origen i desti + blob_vector::const_iterator pBlobsSrc = source.m_blobs.begin(); + blob_vector::iterator pBlobsDst = resultat.m_blobs.end(); - /** - - FUNCIÓ: GetNumber - - FUNCIONALITAT: Calcula el resultat especificat sobre un únic blob de la classe - - PARÀMETRES: - - evaluador: Qualsevol objecte derivat de COperadorBlob - - indexblob: número de blob del que volem calcular el resultat. - - RESULTAT: - - Retorna un double amb el resultat - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: GetNumber - - FUNCTIONALITY: Computes the function evaluador on a blob of the class - - PARAMETERS: - - indexBlob: index of the blob to compute the function - - evaluador: function to apply to each blob (any object derived from the - COperadorBlob class ) - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobResult::GetNumber(int indexBlob, funcio_calculBlob *evaluador) const - { - if (indexBlob < 0 || indexBlob >= GetNumBlobs()) - RaiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS); - return (*evaluador)(*m_blobs[indexBlob]); - } + // insertem els blobs de l'origen a l'actual + while (pBlobsSrc != source.m_blobs.end()) + { + --pBlobsDst; + *pBlobsDst = new CBlob(**pBlobsSrc); + ++pBlobsSrc; + } - /////////////////////////// FILTRAT DE BLOBS //////////////////////////////////// - - /** - - FUNCIÓ: Filter - - FUNCIONALITAT: Filtra els blobs de la classe i deixa el resultat amb només - els blobs que han passat el filtre. - El filtrat es basa en especificar condicions sobre un resultat dels blobs - i seleccionar (o excloure) aquells blobs que no compleixen una determinada - condicio - - PARÀMETRES: - - dst: variable per deixar els blobs filtrats - - filterAction: acció de filtrat. Incloure els blobs trobats (B_INCLUDE), - o excloure els blobs trobats (B_EXCLUDE) - - evaluador: Funció per evaluar els blobs (qualsevol objecte derivat de COperadorBlob - - Condition: tipus de condició que ha de superar la mesura (FilterType) - sobre cada blob per a ser considerat. - B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL, - B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE - - LowLimit: valor numèric per a la comparació (Condition) de la mesura (FilterType) - - HighLimit: valor numèric per a la comparació (Condition) de la mesura (FilterType) - (només té sentit per a aquelles condicions que tenen dos valors - (B_INSIDE, per exemple). - - RESULTAT: - - Deixa els blobs resultants del filtrat a destination - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: Filter - - FUNCTIONALITY: Get some blobs from the class based on conditions on measures - of the blobs. - - PARAMETERS: - - dst: where to store the selected blobs - - filterAction: B_INCLUDE: include the blobs which pass the filter in the result - B_EXCLUDE: exclude the blobs which pass the filter in the result - - evaluador: Object to evaluate the blob - - Condition: How to decide if the result returned by evaluador on each blob - is included or not. It can be: - B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL, - B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE - - LowLimit: numerical value to evaluate the Condition on evaluador(blob) - - HighLimit: numerical value to evaluate the Condition on evaluador(blob). - Only useful for B_INSIDE and B_OUTSIDE - - RESULT: - - It returns on dst the blobs that accomplish (B_INCLUDE) or discards (B_EXCLUDE) - the Condition on the result returned by evaluador on each blob - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlobResult::Filter(CBlobResult &dst, - int filterAction, - funcio_calculBlob *evaluador, - int condition, - double lowLimit, double highLimit /*=0*/) + return resultat; + } - { - int i, numBlobs; - bool resultavaluacio; - double_stl_vector avaluacioBlobs; - double_stl_vector::iterator itavaluacioBlobs; - - if (GetNumBlobs() <= 0) return; - if (!evaluador) return; - //avaluem els blobs amb la funció pertinent - avaluacioBlobs = GetSTLResult(evaluador); - itavaluacioBlobs = avaluacioBlobs.begin(); - numBlobs = GetNumBlobs(); - switch (condition) - { - case B_EQUAL: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = *itavaluacioBlobs == lowLimit; - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + /************************************************************************** + Operacions / Operations + **************************************************************************/ + + /** + - FUNCIÓ: AddBlob + - FUNCIONALITAT: Afegeix un blob al conjunt + - PARÀMETRES: + - blob: blob a afegir + - RESULTAT: + - modifica el conjunt de blobs actual + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 2006/03/01 + - MODIFICACIÓ: Data. Autor. Descripció. + */ + void CBlobResult::AddBlob(CBlob *blob) { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + if (blob != NULL) + m_blobs.push_back(new CBlob(blob)); } - } - break; - case B_NOT_EQUAL: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = *itavaluacioBlobs != lowLimit; - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + + + /** + - FUNCIÓ: GetSTLResult + - FUNCIONALITAT: Calcula el resultat especificat sobre tots els blobs de la classe + - PARÀMETRES: + - evaluador: Qualsevol objecte derivat de COperadorBlob + - RESULTAT: + - Retorna un array de double's STL amb el resultat per cada blob + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: GetResult + - FUNCTIONALITY: Computes the function evaluador on all the blobs of the class + and returns a vector with the result + - PARAMETERS: + - evaluador: function to apply to each blob (any object derived from the + COperadorBlob class ) + - RESULT: + - vector with all the results in the same order as the blobs + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double_stl_vector CBlobResult::GetSTLResult(funcio_calculBlob *evaluador) const { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + if (GetNumBlobs() <= 0) + { + return double_stl_vector(); + } + + // definim el resultat + double_stl_vector result = double_stl_vector(GetNumBlobs()); + // i iteradors sobre els blobs i el resultat + double_stl_vector::iterator itResult = result.begin(); + blob_vector::const_iterator itBlobs = m_blobs.begin(); + + // avaluem la funció en tots els blobs + while (itBlobs != m_blobs.end()) + { + *itResult = (*evaluador)(**itBlobs); + ++itBlobs; + ++itResult; + } + return result; } - } - break; - case B_GREATER: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = *itavaluacioBlobs > lowLimit; - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + + /** + - FUNCIÓ: GetNumber + - FUNCIONALITAT: Calcula el resultat especificat sobre un únic blob de la classe + - PARÀMETRES: + - evaluador: Qualsevol objecte derivat de COperadorBlob + - indexblob: número de blob del que volem calcular el resultat. + - RESULTAT: + - Retorna un double amb el resultat + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: GetNumber + - FUNCTIONALITY: Computes the function evaluador on a blob of the class + - PARAMETERS: + - indexBlob: index of the blob to compute the function + - evaluador: function to apply to each blob (any object derived from the + COperadorBlob class ) + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobResult::GetNumber(int indexBlob, funcio_calculBlob *evaluador) const { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + if (indexBlob < 0 || indexBlob >= GetNumBlobs()) + RaiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS); + return (*evaluador)(*m_blobs[indexBlob]); } - } - break; - case B_LESS: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = *itavaluacioBlobs < lowLimit; - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + + /////////////////////////// FILTRAT DE BLOBS //////////////////////////////////// + + /** + - FUNCIÓ: Filter + - FUNCIONALITAT: Filtra els blobs de la classe i deixa el resultat amb només + els blobs que han passat el filtre. + El filtrat es basa en especificar condicions sobre un resultat dels blobs + i seleccionar (o excloure) aquells blobs que no compleixen una determinada + condicio + - PARÀMETRES: + - dst: variable per deixar els blobs filtrats + - filterAction: acció de filtrat. Incloure els blobs trobats (B_INCLUDE), + o excloure els blobs trobats (B_EXCLUDE) + - evaluador: Funció per evaluar els blobs (qualsevol objecte derivat de COperadorBlob + - Condition: tipus de condició que ha de superar la mesura (FilterType) + sobre cada blob per a ser considerat. + B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL, + B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE + - LowLimit: valor numèric per a la comparació (Condition) de la mesura (FilterType) + - HighLimit: valor numèric per a la comparació (Condition) de la mesura (FilterType) + (només té sentit per a aquelles condicions que tenen dos valors + (B_INSIDE, per exemple). + - RESULTAT: + - Deixa els blobs resultants del filtrat a destination + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: Filter + - FUNCTIONALITY: Get some blobs from the class based on conditions on measures + of the blobs. + - PARAMETERS: + - dst: where to store the selected blobs + - filterAction: B_INCLUDE: include the blobs which pass the filter in the result + B_EXCLUDE: exclude the blobs which pass the filter in the result + - evaluador: Object to evaluate the blob + - Condition: How to decide if the result returned by evaluador on each blob + is included or not. It can be: + B_EQUAL,B_NOT_EQUAL,B_GREATER,B_LESS,B_GREATER_OR_EQUAL, + B_LESS_OR_EQUAL,B_INSIDE,B_OUTSIDE + - LowLimit: numerical value to evaluate the Condition on evaluador(blob) + - HighLimit: numerical value to evaluate the Condition on evaluador(blob). + Only useful for B_INSIDE and B_OUTSIDE + - RESULT: + - It returns on dst the blobs that accomplish (B_INCLUDE) or discards (B_EXCLUDE) + the Condition on the result returned by evaluador on each blob + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlobResult::Filter(CBlobResult &dst, + int filterAction, + funcio_calculBlob *evaluador, + int condition, + double lowLimit, double highLimit /*=0*/) + { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + int i, numBlobs; + bool resultavaluacio; + double_stl_vector avaluacioBlobs; + double_stl_vector::iterator itavaluacioBlobs; + + if (GetNumBlobs() <= 0) return; + if (!evaluador) return; + //avaluem els blobs amb la funció pertinent + avaluacioBlobs = GetSTLResult(evaluador); + itavaluacioBlobs = avaluacioBlobs.begin(); + numBlobs = GetNumBlobs(); + switch (condition) + { + case B_EQUAL: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs == lowLimit; + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_NOT_EQUAL: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs != lowLimit; + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_GREATER: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs > lowLimit; + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_LESS: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs < lowLimit; + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_GREATER_OR_EQUAL: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs >= lowLimit; + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_LESS_OR_EQUAL: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = *itavaluacioBlobs <= lowLimit; + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_INSIDE: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = (*itavaluacioBlobs >= lowLimit) && (*itavaluacioBlobs <= highLimit); + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + case B_OUTSIDE: + for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) + { + resultavaluacio = (*itavaluacioBlobs < lowLimit) || (*itavaluacioBlobs > highLimit); + if ((resultavaluacio && filterAction == B_INCLUDE) || + (!resultavaluacio && filterAction == B_EXCLUDE)) + { + dst.m_blobs.push_back(new CBlob(GetBlob(i))); + } + } + break; + } + + + // en cas de voler filtrar un CBlobResult i deixar-ho en el mateix CBlobResult + // ( operacio inline ) + if (&dst == this) + { + // esborrem els primers blobs ( que són els originals ) + // ja que els tindrem replicats al final si passen el filtre + blob_vector::iterator itBlobs = m_blobs.begin(); + for (int i = 0; i < numBlobs; i++) + { + delete *itBlobs; + ++itBlobs; + } + m_blobs.erase(m_blobs.begin(), itBlobs); + } } - } - break; - case B_GREATER_OR_EQUAL: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = *itavaluacioBlobs >= lowLimit; - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + + + /** + - FUNCIÓ: GetBlob + - FUNCIONALITAT: Retorna un blob si aquest existeix (index != -1) + - PARÀMETRES: + - indexblob: index del blob a retornar + - RESULTAT: + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /* + - FUNCTION: GetBlob + - FUNCTIONALITY: Gets the n-th blob (without ordering the blobs) + - PARAMETERS: + - indexblob: index in the blob array + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlob CBlobResult::GetBlob(int indexblob) const { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + if (indexblob < 0 || indexblob >= GetNumBlobs()) + RaiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS); + + return *m_blobs[indexblob]; } - } - break; - case B_LESS_OR_EQUAL: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = *itavaluacioBlobs <= lowLimit; - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + CBlob *CBlobResult::GetBlob(int indexblob) { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + if (indexblob < 0 || indexblob >= GetNumBlobs()) + RaiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS); + + return m_blobs[indexblob]; } - } - break; - case B_INSIDE: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = (*itavaluacioBlobs >= lowLimit) && (*itavaluacioBlobs <= highLimit); - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + + /** + - FUNCIÓ: GetNthBlob + - FUNCIONALITAT: Retorna l'enèssim blob segons un determinat criteri + - PARÀMETRES: + - criteri: criteri per ordenar els blobs (objectes derivats de COperadorBlob) + - nBlob: index del blob a retornar + - dst: on es retorna el resultat + - RESULTAT: + - retorna el blob nBlob a dst ordenant els blobs de la classe segons el criteri + en ordre DESCENDENT. Per exemple, per obtenir el blob major: + GetNthBlob( CBlobGetArea(), 0, blobMajor ); + GetNthBlob( CBlobGetArea(), 1, blobMajor ); (segon blob més gran) + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /* + - FUNCTION: GetNthBlob + - FUNCTIONALITY: Gets the n-th blob ordering first the blobs with some criteria + - PARAMETERS: + - criteri: criteria to order the blob array + - nBlob: index of the returned blob in the ordered blob array + - dst: where to store the result + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlobResult::GetNthBlob(funcio_calculBlob *criteri, int nBlob, CBlob &dst) const { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + // verifiquem que no estem accedint fora el vector de blobs + if (nBlob < 0 || nBlob >= GetNumBlobs()) + { + //RaiseError( EXCEPTION_BLOB_OUT_OF_BOUNDS ); + dst = CBlob(); + return; + } + + double_stl_vector avaluacioBlobs, avaluacioBlobsOrdenat; + double valorEnessim; + + //avaluem els blobs amb la funció pertinent + avaluacioBlobs = GetSTLResult(criteri); + + avaluacioBlobsOrdenat = double_stl_vector(GetNumBlobs()); + + // obtenim els nBlob primers resultats (en ordre descendent) + std::partial_sort_copy(avaluacioBlobs.begin(), + avaluacioBlobs.end(), + avaluacioBlobsOrdenat.begin(), + avaluacioBlobsOrdenat.end(), + std::greater<double>()); + + valorEnessim = avaluacioBlobsOrdenat[nBlob]; + + // busquem el primer blob que té el valor n-ssim + double_stl_vector::const_iterator itAvaluacio = avaluacioBlobs.begin(); + + bool trobatBlob = false; + int indexBlob = 0; + while (itAvaluacio != avaluacioBlobs.end() && !trobatBlob) + { + if (*itAvaluacio == valorEnessim) + { + trobatBlob = true; + dst = CBlob(GetBlob(indexBlob)); + } + ++itAvaluacio; + indexBlob++; + } } - } - break; - case B_OUTSIDE: - for (i = 0; i < numBlobs; i++, itavaluacioBlobs++) - { - resultavaluacio = (*itavaluacioBlobs < lowLimit) || (*itavaluacioBlobs > highLimit); - if ((resultavaluacio && filterAction == B_INCLUDE) || - (!resultavaluacio && filterAction == B_EXCLUDE)) + + /** + - FUNCIÓ: ClearBlobs + - FUNCIONALITAT: Elimina tots els blobs de l'objecte + - PARÀMETRES: + - RESULTAT: + - Allibera tota la memòria dels blobs + - RESTRICCIONS: + - AUTOR: Ricard Borrà s Navarra + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /* + - FUNCTION: ClearBlobs + - FUNCTIONALITY: Clears all the blobs from the object and releases all its memory + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlobResult::ClearBlobs() { - dst.m_blobs.push_back(new CBlob(GetBlob(i))); + /*for( int i = 0; i < GetNumBlobs(); i++ ) + { + delete m_blobs[i]; + }*/ + blob_vector::iterator itBlobs = m_blobs.begin(); + while (itBlobs != m_blobs.end()) + { + delete *itBlobs; + ++itBlobs; + } + + m_blobs.clear(); } - } - break; - } - - - // en cas de voler filtrar un CBlobResult i deixar-ho en el mateix CBlobResult - // ( operacio inline ) - if (&dst == this) - { - // esborrem els primers blobs ( que són els originals ) - // ja que els tindrem replicats al final si passen el filtre - blob_vector::iterator itBlobs = m_blobs.begin(); - for (int i = 0; i < numBlobs; i++) - { - delete *itBlobs; - ++itBlobs; - } - m_blobs.erase(m_blobs.begin(), itBlobs); - } - } - - - /** - - FUNCIÓ: GetBlob - - FUNCIONALITAT: Retorna un blob si aquest existeix (index != -1) - - PARÀMETRES: - - indexblob: index del blob a retornar - - RESULTAT: - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /* - - FUNCTION: GetBlob - - FUNCTIONALITY: Gets the n-th blob (without ordering the blobs) - - PARAMETERS: - - indexblob: index in the blob array - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlob CBlobResult::GetBlob(int indexblob) const - { - if (indexblob < 0 || indexblob >= GetNumBlobs()) - RaiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS); - - return *m_blobs[indexblob]; - } - CBlob *CBlobResult::GetBlob(int indexblob) - { - if (indexblob < 0 || indexblob >= GetNumBlobs()) - RaiseError(EXCEPTION_BLOB_OUT_OF_BOUNDS); - return m_blobs[indexblob]; - } - - /** - - FUNCIÓ: GetNthBlob - - FUNCIONALITAT: Retorna l'enèssim blob segons un determinat criteri - - PARÀMETRES: - - criteri: criteri per ordenar els blobs (objectes derivats de COperadorBlob) - - nBlob: index del blob a retornar - - dst: on es retorna el resultat - - RESULTAT: - - retorna el blob nBlob a dst ordenant els blobs de la classe segons el criteri - en ordre DESCENDENT. Per exemple, per obtenir el blob major: - GetNthBlob( CBlobGetArea(), 0, blobMajor ); - GetNthBlob( CBlobGetArea(), 1, blobMajor ); (segon blob més gran) - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /* - - FUNCTION: GetNthBlob - - FUNCTIONALITY: Gets the n-th blob ordering first the blobs with some criteria - - PARAMETERS: - - criteri: criteria to order the blob array - - nBlob: index of the returned blob in the ordered blob array - - dst: where to store the result - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlobResult::GetNthBlob(funcio_calculBlob *criteri, int nBlob, CBlob &dst) const - { - // verifiquem que no estem accedint fora el vector de blobs - if (nBlob < 0 || nBlob >= GetNumBlobs()) - { - //RaiseError( EXCEPTION_BLOB_OUT_OF_BOUNDS ); - dst = CBlob(); - return; - } - - double_stl_vector avaluacioBlobs, avaluacioBlobsOrdenat; - double valorEnessim; - - //avaluem els blobs amb la funció pertinent - avaluacioBlobs = GetSTLResult(criteri); - - avaluacioBlobsOrdenat = double_stl_vector(GetNumBlobs()); + /** + - FUNCIÓ: RaiseError + - FUNCIONALITAT: Funció per a notificar errors al l'usuari (en debug) i llença + les excepcions + - PARÀMETRES: + - errorCode: codi d'error + - RESULTAT: + - Ensenya un missatge a l'usuari (en debug) i llença una excepció + - RESTRICCIONS: + - AUTOR: Ricard Borrà s Navarra + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /* + - FUNCTION: RaiseError + - FUNCTIONALITY: Error handling function + - PARAMETERS: + - errorCode: reason of the error + - RESULT: + - in _DEBUG version, shows a message box with the error. In release is silent. + In both cases throws an exception with the error. + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlobResult::RaiseError(const int errorCode) const + { + throw errorCode; + } - // obtenim els nBlob primers resultats (en ordre descendent) - std::partial_sort_copy(avaluacioBlobs.begin(), - avaluacioBlobs.end(), - avaluacioBlobsOrdenat.begin(), - avaluacioBlobsOrdenat.end(), - std::greater<double>()); - valorEnessim = avaluacioBlobsOrdenat[nBlob]; - // busquem el primer blob que té el valor n-ssim - double_stl_vector::const_iterator itAvaluacio = avaluacioBlobs.begin(); + /************************************************************************** + Auxiliars / Auxiliary functions + **************************************************************************/ + + + /** + - FUNCIÓ: PrintBlobs + - FUNCIONALITAT: Escriu els parà metres (à rea, perÃmetre, exterior, mitjana) + de tots els blobs a un fitxer. + - PARÀMETRES: + - nom_fitxer: path complet del fitxer amb el resultat + - RESULTAT: + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /* + - FUNCTION: PrintBlobs + - FUNCTIONALITY: Prints some blob features in an ASCII file + - PARAMETERS: + - nom_fitxer: full path + filename to generate + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlobResult::PrintBlobs(char *nom_fitxer) const + { + double_stl_vector area, /*perimetre,*/ exterior, mitjana, compacitat, longitud, + externPerimeter, perimetreConvex, perimetre; + int i; + FILE *fitxer_sortida; + + area = GetSTLResult(CBlobGetArea()); + perimetre = GetSTLResult(CBlobGetPerimeter()); + exterior = GetSTLResult(CBlobGetExterior()); + mitjana = GetSTLResult(CBlobGetMean()); + compacitat = GetSTLResult(CBlobGetCompactness()); + longitud = GetSTLResult(CBlobGetLength()); + externPerimeter = GetSTLResult(CBlobGetExternPerimeter()); + perimetreConvex = GetSTLResult(CBlobGetHullPerimeter()); + + fitxer_sortida = fopen(nom_fitxer, "w"); + + for (i = 0; i < GetNumBlobs(); i++) + { + fprintf(fitxer_sortida, "blob %d ->\t a=%7.0f\t p=%8.2f (%8.2f extern)\t pconvex=%8.2f\t ext=%.0f\t m=%7.2f\t c=%3.2f\t l=%8.2f\n", + i, area[i], perimetre[i], externPerimeter[i], perimetreConvex[i], exterior[i], mitjana[i], compacitat[i], longitud[i]); + } + fclose(fitxer_sortida); - bool trobatBlob = false; - int indexBlob = 0; - while (itAvaluacio != avaluacioBlobs.end() && !trobatBlob) - { - if (*itAvaluacio == valorEnessim) - { - trobatBlob = true; - dst = CBlob(GetBlob(indexBlob)); + } } - ++itAvaluacio; - indexBlob++; } } - - /** - - FUNCIÓ: ClearBlobs - - FUNCIONALITAT: Elimina tots els blobs de l'objecte - - PARÀMETRES: - - RESULTAT: - - Allibera tota la memòria dels blobs - - RESTRICCIONS: - - AUTOR: Ricard Borrà s Navarra - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /* - - FUNCTION: ClearBlobs - - FUNCTIONALITY: Clears all the blobs from the object and releases all its memory - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlobResult::ClearBlobs() - { - /*for( int i = 0; i < GetNumBlobs(); i++ ) - { - delete m_blobs[i]; - }*/ - blob_vector::iterator itBlobs = m_blobs.begin(); - while (itBlobs != m_blobs.end()) - { - delete *itBlobs; - ++itBlobs; - } - - m_blobs.clear(); - } - - /** - - FUNCIÓ: RaiseError - - FUNCIONALITAT: Funció per a notificar errors al l'usuari (en debug) i llença - les excepcions - - PARÀMETRES: - - errorCode: codi d'error - - RESULTAT: - - Ensenya un missatge a l'usuari (en debug) i llença una excepció - - RESTRICCIONS: - - AUTOR: Ricard Borrà s Navarra - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /* - - FUNCTION: RaiseError - - FUNCTIONALITY: Error handling function - - PARAMETERS: - - errorCode: reason of the error - - RESULT: - - in _DEBUG version, shows a message box with the error. In release is silent. - In both cases throws an exception with the error. - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlobResult::RaiseError(const int errorCode) const - { - throw errorCode; - } - - - - /************************************************************************** - Auxiliars / Auxiliary functions - **************************************************************************/ - - - /** - - FUNCIÓ: PrintBlobs - - FUNCIONALITAT: Escriu els parà metres (à rea, perÃmetre, exterior, mitjana) - de tots els blobs a un fitxer. - - PARÀMETRES: - - nom_fitxer: path complet del fitxer amb el resultat - - RESULTAT: - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /* - - FUNCTION: PrintBlobs - - FUNCTIONALITY: Prints some blob features in an ASCII file - - PARAMETERS: - - nom_fitxer: full path + filename to generate - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlobResult::PrintBlobs(char *nom_fitxer) const - { - double_stl_vector area, /*perimetre,*/ exterior, mitjana, compacitat, longitud, - externPerimeter, perimetreConvex, perimetre; - int i; - FILE *fitxer_sortida; - - area = GetSTLResult(CBlobGetArea()); - perimetre = GetSTLResult(CBlobGetPerimeter()); - exterior = GetSTLResult(CBlobGetExterior()); - mitjana = GetSTLResult(CBlobGetMean()); - compacitat = GetSTLResult(CBlobGetCompactness()); - longitud = GetSTLResult(CBlobGetLength()); - externPerimeter = GetSTLResult(CBlobGetExternPerimeter()); - perimetreConvex = GetSTLResult(CBlobGetHullPerimeter()); - - fitxer_sortida = fopen(nom_fitxer, "w"); - - for (i = 0; i < GetNumBlobs(); i++) - { - fprintf(fitxer_sortida, "blob %d ->\t a=%7.0f\t p=%8.2f (%8.2f extern)\t pconvex=%8.2f\t ext=%.0f\t m=%7.2f\t c=%3.2f\t l=%8.2f\n", - i, area[i], perimetre[i], externPerimeter[i], perimetreConvex[i], exterior[i], mitjana[i], compacitat[i], longitud[i]); - } - fclose(fitxer_sortida); - - } - } diff --git a/src/algorithms/MultiLayer/BlobResult.h b/src/algorithms/MultiLayer/BlobResult.h index 62ec48551cac4c82d2ae9e96e3c13a7142fe462d..96d92648ef881d0a44a4c5a6f5af84d7d90c3f6c 100644 --- a/src/algorithms/MultiLayer/BlobResult.h +++ b/src/algorithms/MultiLayer/BlobResult.h @@ -9,135 +9,137 @@ #include "OpenCvLegacyIncludes.h" #include "blob.h" -typedef std::vector<double> double_stl_vector; - -/************************************************************************** - Filtres / Filters - **************************************************************************/ - - //! accions que es poden fer amb els filtres - //! Actions performed by a filter (include or exclude blobs) -#define B_INCLUDE 1L -#define B_EXCLUDE 2L - -//! condicions sobre els filtres -//! Conditions to apply the filters -#define B_EQUAL 3L -#define B_NOT_EQUAL 4L -#define B_GREATER 5L -#define B_LESS 6L -#define B_GREATER_OR_EQUAL 7L -#define B_LESS_OR_EQUAL 8L -#define B_INSIDE 9L -#define B_OUTSIDE 10L - - -/************************************************************************** - Excepcions / Exceptions - **************************************************************************/ - - //! Excepcions llençades per les funcions: -#define EXCEPTION_BLOB_OUT_OF_BOUNDS 1000 -#define EXCEPCIO_CALCUL_BLOBS 1001 - -namespace Blob +namespace bgslibrary { - - //! definició de que es un vector de blobs - typedef std::vector<CBlob*> blob_vector; - - /** - Classe que conté un conjunt de blobs i permet extreure'n propietats - o filtrar-los segons determinats criteris. - Class to calculate the blobs of an image and calculate some properties - on them. Also, the class provides functions to filter the blobs using - some criteria. - */ - class CBlobResult + namespace algorithms { - public: - - //! constructor estandard, crea un conjunt buit de blobs - //! Standard constructor, it creates an empty set of blobs - CBlobResult(); - //! constructor a partir d'una imatge - //! Image constructor, it creates an object with the blobs of the image - CBlobResult(IplImage *source, IplImage *mask, int threshold, bool findmoments); - //! constructor de còpia - //! Copy constructor - CBlobResult(const CBlobResult &source); - //! Destructor - virtual ~CBlobResult(); - - //! operador = per a fer assignacions entre CBlobResult - //! Assigment operator - CBlobResult& operator=(const CBlobResult& source); - //! operador + per concatenar dos CBlobResult - //! Addition operator to concatenate two sets of blobs - CBlobResult operator+(const CBlobResult& source); - - //! Afegeix un blob al conjunt - //! Adds a blob to the set of blobs - void AddBlob(CBlob *blob); - -#ifdef MATRIXCV_ACTIU - //! Calcula un valor sobre tots els blobs de la classe retornant una MatrixCV - //! Computes some property on all the blobs of the class - double_vector GetResult(funcio_calculBlob *evaluador) const; -#endif - //! Calcula un valor sobre tots els blobs de la classe retornant un std::vector<double> - //! Computes some property on all the blobs of the class - double_stl_vector GetSTLResult(funcio_calculBlob *evaluador) const; - - //! Calcula un valor sobre un blob de la classe - //! Computes some property on one blob of the class - double GetNumber(int indexblob, funcio_calculBlob *evaluador) const; - - //! Retorna aquells blobs que compleixen les condicions del filtre en el destination - //! Filters the blobs of the class using some property - void Filter(CBlobResult &dst, - int filterAction, funcio_calculBlob *evaluador, - int condition, double lowLimit, double highLimit = 0); - - //! Retorna l'enèssim blob segons un determinat criteri - //! Sorts the blobs of the class acording to some criteria and returns the n-th blob - void GetNthBlob(funcio_calculBlob *criteri, int nBlob, CBlob &dst) const; - - //! Retorna el blob enèssim - //! Gets the n-th blob of the class ( without sorting ) - CBlob GetBlob(int indexblob) const; - CBlob *GetBlob(int indexblob); - - //! Elimina tots els blobs de l'objecte - //! Clears all the blobs of the class - void ClearBlobs(); - - //! Escriu els blobs a un fitxer - //! Prints some features of all the blobs in a file - void PrintBlobs(char *nom_fitxer) const; - - - //Metodes GET/SET - - //! Retorna el total de blobs - //! Gets the total number of blobs - int GetNumBlobs() const + namespace multilayer { - return(m_blobs.size()); + namespace blob + { + typedef std::vector<double> double_stl_vector; + + /************************************************************************** + Filtres / Filters + **************************************************************************/ + + //! accions que es poden fer amb els filtres + //! Actions performed by a filter (include or exclude blobs) + const long B_INCLUDE = 1L; + const long B_EXCLUDE = 2L; + + //! condicions sobre els filtres + //! Conditions to apply the filters + const long B_EQUAL = 3L; + const long B_NOT_EQUAL = 4L; + const long B_GREATER = 5L; + const long B_LESS = 6L; + const long B_GREATER_OR_EQUAL = 7L; + const long B_LESS_OR_EQUAL = 8L; + const long B_INSIDE = 9L; + const long B_OUTSIDE = 10L; + + /************************************************************************** + Excepcions / Exceptions + **************************************************************************/ + + //! Excepcions llençades per les funcions: + const int EXCEPTION_BLOB_OUT_OF_BOUNDS = 1000; + const int EXCEPCIO_CALCUL_BLOBS = 1001; + + //! definició de que es un vector de blobs + typedef std::vector<CBlob*> blob_vector; + + /** + Classe que conté un conjunt de blobs i permet extreure'n propietats + o filtrar-los segons determinats criteris. + Class to calculate the blobs of an image and calculate some properties + on them. Also, the class provides functions to filter the blobs using + some criteria. + */ + class CBlobResult + { + public: + //! constructor estandard, crea un conjunt buit de blobs + //! Standard constructor, it creates an empty set of blobs + CBlobResult(); + //! constructor a partir d'una imatge + //! Image constructor, it creates an object with the blobs of the image + CBlobResult(IplImage *source, IplImage *mask, int threshold, bool findmoments); + //! constructor de còpia + //! Copy constructor + CBlobResult(const CBlobResult &source); + //! Destructor + virtual ~CBlobResult(); + + //! operador = per a fer assignacions entre CBlobResult + //! Assigment operator + CBlobResult& operator=(const CBlobResult& source); + //! operador + per concatenar dos CBlobResult + //! Addition operator to concatenate two sets of blobs + CBlobResult operator+(const CBlobResult& source); + + //! Afegeix un blob al conjunt + //! Adds a blob to the set of blobs + void AddBlob(CBlob *blob); + + #ifdef MATRIXCV_ACTIU + //! Calcula un valor sobre tots els blobs de la classe retornant una MatrixCV + //! Computes some property on all the blobs of the class + double_vector GetResult(funcio_calculBlob *evaluador) const; + #endif + //! Calcula un valor sobre tots els blobs de la classe retornant un std::vector<double> + //! Computes some property on all the blobs of the class + double_stl_vector GetSTLResult(funcio_calculBlob *evaluador) const; + + //! Calcula un valor sobre un blob de la classe + //! Computes some property on one blob of the class + double GetNumber(int indexblob, funcio_calculBlob *evaluador) const; + + //! Retorna aquells blobs que compleixen les condicions del filtre en el destination + //! Filters the blobs of the class using some property + void Filter(CBlobResult &dst, + int filterAction, funcio_calculBlob *evaluador, + int condition, double lowLimit, double highLimit = 0); + + //! Retorna l'enèssim blob segons un determinat criteri + //! Sorts the blobs of the class acording to some criteria and returns the n-th blob + void GetNthBlob(funcio_calculBlob *criteri, int nBlob, CBlob &dst) const; + + //! Retorna el blob enèssim + //! Gets the n-th blob of the class ( without sorting ) + CBlob GetBlob(int indexblob) const; + CBlob *GetBlob(int indexblob); + + //! Elimina tots els blobs de l'objecte + //! Clears all the blobs of the class + void ClearBlobs(); + + //! Escriu els blobs a un fitxer + //! Prints some features of all the blobs in a file + void PrintBlobs(char *nom_fitxer) const; + + + //Metodes GET/SET + + //! Retorna el total de blobs + //! Gets the total number of blobs + int GetNumBlobs() const + { + return(m_blobs.size()); + } + + private: + //! Funció per gestionar els errors + //! Function to manage the errors + void RaiseError(const int errorCode) const; + + protected: + //! Vector amb els blobs + //! Vector with all the blobs + blob_vector m_blobs; + }; + } } - - - private: - - //! Funció per gestionar els errors - //! Function to manage the errors - void RaiseError(const int errorCode) const; - - protected: - - //! Vector amb els blobs - //! Vector with all the blobs - blob_vector m_blobs; - }; - + } } diff --git a/src/algorithms/MultiLayer/CMultiLayerBGS.cpp b/src/algorithms/MultiLayer/CMultiLayerBGS.cpp index 0708e57c2910af7e3c270914af1aaeb5636572b9..48e2b42473224ca2a1c8d4ed39bd17a67e6ef7c5 100644 --- a/src/algorithms/MultiLayer/CMultiLayerBGS.cpp +++ b/src/algorithms/MultiLayer/CMultiLayerBGS.cpp @@ -9,11 +9,8 @@ #include "CMultiLayerBGS.h" #include "OpenCvLegacyIncludes.h" -using namespace Blob; - -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// +using namespace bgslibrary::algorithms::multilayer; +using namespace bgslibrary::algorithms::multilayer::blob; CMultiLayerBGS::CMultiLayerBGS() { m_nMaxLBPModeNum = MAX_LBP_MODE_NUM; @@ -1639,7 +1636,7 @@ void CMultiLayerBGS::GetFloatEdgeImage(IplImage *src, IplImage *dst) { void CMultiLayerBGS::ExportLogMessage(char *msg) { const char *log_fn = "log_message.txt"; - ofstream fout(log_fn, ios::app); + std::ofstream fout(log_fn, std::ios::app); if (fout.fail()) { printf("Error opening log output file %s.\n", log_fn); fout.close(); @@ -1826,7 +1823,7 @@ void CMultiLayerBGS::Save(const char *bg_model_fn, int save_type) { } bool CMultiLayerBGS::Load(const char *bg_model_fn) { - ifstream fin(bg_model_fn, ios::in); + std::ifstream fin(bg_model_fn, std::ios::in); if (fin.fail()) { printf("Error opening background model file %s.\n", bg_model_fn); fin.close(); diff --git a/src/algorithms/MultiLayer/CMultiLayerBGS.h b/src/algorithms/MultiLayer/CMultiLayerBGS.h index 3043a804104c1cc170af8dd6814317da253f9f58..302a19a56a9b690f7026bbe8b850765feaf1612b 100644 --- a/src/algorithms/MultiLayer/CMultiLayerBGS.h +++ b/src/algorithms/MultiLayer/CMultiLayerBGS.h @@ -23,243 +23,249 @@ step. If you compile it under Linux, please uncomment it. #include "LocalBinaryPattern.h" #include "BlobResult.h" #include "OpenCvDataConversion.h" - #include "BackgroundSubtractionAPI.h" #ifdef LINUX_BILATERAL_FILTER #include "CrossBilateralFilter.h" #endif -using namespace std; - -class CMultiLayerBGS : public CBackgroundSubtractionAPI +namespace bgslibrary { -public: - //------------------------------------------------------------- - // TO CALL AT INITIALISATION: DEFINES THE SIZE OF THE INPUT IMAGES - // NORMALLY, UNNECESSARY IF A CONFIGURATION FILE IS LOADED - void Init(int width, int height); - - //------------------------------------------------------------- - // PROVIDE A MASK TO DEFINE THE SET OF POINTS WHERE BACKGROUND - // SUBTRACTION DOES NOT NEED TO BE PERFORMED - // - // mode is useful to specify if the points to remove from - // processing are in addition to the ones potentially - // removed according to the configuration file, - // or if they are the only ones to be removed - // - // mode=0 : provided points need to be removed - // in addition to those already removed - // mode=1 : the provided points are the only one to remove - // from processing - // Note: maskImage(li,co)=0 indicate the points to remove - // from background processing - void SetValidPointMask(IplImage* maskImage, int mode); - - //------------------------------------------------------------- - // - // set the frame rate, to adjust the update parameters - // to the actual frame rate. - // Can be called only once at initialisation, - // but in online cases, can be used to indicate - // the time interval during the last processed frame - // - // frameDuration is in millisecond - void SetFrameRate(float frameDuration); - - //------------------------------------------------------------- - // - // set some main parameters for background model learning. - // in general, we can set large updating rates for background - // model learning and set small updating rates in foreground - // detection - void SetParameters(int max_lbp_mode_num, // maximal LBP mode number - float mode_updating_learn_rate_per_second, // background mode updating learning rate per second - float weight_updating_learn_rate_per_second, // mode's weight updating learning rate per second - float low_init_mode_weight); // the low initial mode weight - -//------------------------------------------------------------- -// PROVIDE A POINTER TO THE INPUT IMAGE -// -> INDICATE WHERE THE NEW IMAGE TO PROCESS IS STORED -// -// Here assumes that the input image will contain RGB images. -// The memory of this image is handled by the caller. -// -// The return value indicate whether the actual Background -// Subtraction algorithm handles RGB images (1) or not (0). -// - int SetRGBInputImage(IplImage * inputImage, CvRect *roi = NULL); - - //------------------------------------------------------------- - // PROVIDE A POINTER TO THE RESULT IMAGE - // INDICATE WHERE THE BACKGROUND RESULT NEED TO BE STORED - // - int SetForegroundMaskImage(IplImage* fg_mask_img); - int SetForegroundProbImage(IplImage* fg_prob_img); - - //------------------------------------------------------------- - // This function should be called each time a new image is - // available in the input image. - // - // The return value is 0 if everything goes well, a non-zero value - // otherwise. - // - int Process(); - - //------------------------------------------------------------- - // this function should save parameters and information of the model - // (e.g. after a training of the model, or in such a way - // that the model can be reload to process the next frame - // type of save: - // 0 - background model information (pixel by pixel) - // 1 - background model parameters - // 2 - both background information (pixel by pixel) and parameters - void Save(const char *bg_model_fn, int save_type); - void Save(const char* bg_model_fn); - - //------------------------------------------------------------- - // this function should load the parameters necessary - // for the processing of the background subtraction or - // load background model information - bool Load(const char *bg_model_fn); - - - void SetCurrentFrameNumber(unsigned long cur_frame_no); - - void GetForegroundMaskImage(IplImage *fg_mask_img); - void GetForegroundImage(IplImage *fg_img, CvScalar bg_color = CV_RGB(0, 255, 0)); - void GetBackgroundImage(IplImage *bk_img); - void GetForegroundProbabilityImage(IplImage* fg_prob_img); - - void GetBgLayerNoImage(IplImage *bg_layer_no_img, CvScalar* layer_colors = NULL, int layer_num = 0); - void GetLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, CvScalar empty_color = CV_RGB(0, 0, 0)); - void GetCurrentLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, IplImage *layered_fg_img = NULL, - CvScalar layered_bg_bk_color = CV_RGB(0, 0, 0), CvScalar layered_fg_color = CV_RGB(255, 0, 0), - int smooth_win = 13, float smooth_sigma = 3.0f, float below_layer_noise = 0.5f, float above_layer_noise = 0.3f, int min_blob_size = 50); - float DistLBP(LBPStruct *LBP1, LBPStruct *LBP2); - void GetColoredBgMultiLayeredImage(IplImage *bg_multi_layer_img, CvScalar *layer_colors); - void UpdatePatternColorDistWeights(float *cur_pattern, float *bg_pattern); - void ExportLogMessage(char* msg); - void Postprocessing(); - void GetFloatEdgeImage(IplImage *src, IplImage *dst); - void RemoveBackgroundLayers(PixelLBPStruct *PLBP, bool *removed_modes = NULL); - float CalColorRangeDist(unsigned char *cur_intensity, float *bg_intensity, float *max_intensity, - float *min_intensity, float shadow_rate, float highlight_rate); - float CalVectorsAngle(float *c1, unsigned char *c2, int length); - float CalVectorsNoisedAngle(float *bg_color, unsigned char *noised_color, float offset, int length); - void ComputeGradientImage(IplImage *src, IplImage *dst, bool bIsFloat); - float CalColorBgDist(uchar *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity); - float CalPatternBgDist(float *cur_pattern, float *bg_pattern); - - void GetForegroundMaskMap(CvMat *fg_mask_mat); - void Initialization(IplImage *first_img, int lbp_level_num, float *radiuses, int *neig_pt_nums); - void GetCurrentBackgroundDistMap(CvMat *bk_dist_map); - void BackgroundSubtractionProcess(); - void SetBkMaskImage(IplImage *mask_img); - void SetNewImage(IplImage *new_img, CvRect *roi = NULL); - - void ResetAllParameters(); - void QuickSort(float *pData, unsigned short *pIdxes, long low, long high, bool bAscent); - void UpdateBgPixelPattern(float *cur_pattern, float *bg_bg_pattern); - void UpdateBgPixelColor(unsigned char* cur_intensity, float* bg_intensity); - void Update_MAX_MIN_Intensity(unsigned char *cur_intensity, float *max_intensity, float *min_intensity); - void MergeImages(int num, ...); - - int m_nChannel; /* most of opencv functions support 1,2,3 or 4 channels, for the input images */ - - PixelLBPStruct* m_pPixelLBPs; /* the LBP texture patterns for each image */ - int m_nMaxLBPModeNum; /* the maximal number for the used LBP pattern models */ - float m_fModeUpdatingLearnRate; /* the background mode learning rate */ - float m_fWeightUpdatingLearnRate; /* the background mode weight updating rate */ - float m_f1_ModeUpdatingLearnRate; /* 1 - background_mode_learning_rate */ - float m_f1_WeightUpdatingLearnRate; /* 1 - background_mode_weight_updating_rate */ - float m_fRobustColorOffset; /* the intensity offset robust to noise */ - float m_fLowInitialModeWeight; /* the lowest weight of initial background mode */ - int m_nLBPLength; /* the length of texture LBP operator */ - float m_fPatternColorDistBgThreshold; /* the threshold value used to classify background and foreground */ - float m_fPatternColorDistBgUpdatedThreshold; /* the threshold value used to update the background modeling */ - float m_fMinBgLayerWeight; /* the minimal weight to remove background layers */ - - int m_nPatternDistSmoothNeigHalfSize; /* the neighboring half size of gaussian window to remove the noise - on the distance map */ - float m_fPatternDistConvGaussianSigma; /* the gaussian sigma used to remove the noise on the distance map */ - - float m_fBackgroundModelPercent; /* the background mode percent, the first several background modes - with high mode weights should be regarded as reliable background modes */ - - float m_fRobustShadowRate; /* the minimal shadow rate, [0.4, 0.7] */ - float m_fRobustHighlightRate; /* the maximal highlight rate, [1.1, 1.4] */ - - int m_nLBPImgNum; /* the number of images used for texture LBP feature */ - - float m_fMinLBPBinaryProb; /* the minimal LBP binary probability */ - float m_f1_MinLBPBinaryProb; /* 1 - minimal_LBP_binary_probability */ - - CvSize m_cvImgSize; /* the image size (width, height) */ - - unsigned long m_nCurImgFrameIdx; /* the frame index of current image */ - - bool m_bUsedGradImage; /* the boolean variable signaling whether the gradient image is used - or not for computing LBP operator */ - - bool m_bUsedColorLBP; /* true - multi-channel color image for LBP operator, - false - gray-scale image for LBP operator */ - - CLocalBinaryPattern m_cLBP; /* the class instant for computing LBP (local binary pattern) texture feature */ - - IplImage* m_pBkMaskImg; /* the mask image corresponding to the input image, - i.e. all the masked pixels should be processed */ - - IplImage* m_pOrgImg; /* the original image */ - IplImage** m_ppOrgLBPImgs; /* the multi-layer images used for LBP feature extraction */ - IplImage* m_pFgImg; /* the foreground image */ - IplImage* m_pBgImg; /* the background image */ - IplImage* m_pFgMaskImg; /* the foreground mask image */ - IplImage* m_pBgDistImg; /* the background distance image (float) */ - IplImage* m_pEdgeImg; /* the edge image used for cross bilateral filter */ - IplImage* m_pFgProbImg; /* the foreground probability image (uchar) */ - - IplImage* m_pFirstAppearingTimeMap; - -#ifdef LINUX_BILATERAL_FILTER - CCrossBilateralFilter m_cCrossBF; /* the class instant for cross bilateral filter - which should be used to remove noise on the distance map */ -#endif - - bool m_disableLearning; - float m_fSigmaS; /* sigma in the spatial domain for cross bilateral filter */ - float m_fSigmaR; /* sigma in the normalized intensity domain for cross bilateral filter */ - - float m_fTextureWeight; /* the weight value of texture LBP feature - for background modeling & foreground detection */ - - float m_fColorWeight; /* the weight value of color invariant feature - for background modeling & foreground detection */ - - float m_fWeightUpdatingConstant; /* the constant ( >= 1 ) for 'hysteries' weight updating scheme - (increase when matched, decrease when un-matched */ - - float m_fReliableBackgroundModeWeight; /* the weight value for background mode - which should be regarded as a reliable background mode, - which is useful for multi-layer scheme */ - - float m_fMinNoisedAngle; /* the minimal angle value between the background color - and the noised observed color */ - - float m_fMinNoisedAngleSine; /* the minimal angle sine value between the background color - and the noised observed color */ + namespace algorithms + { + namespace multilayer + { + class CMultiLayerBGS : public CBackgroundSubtractionAPI + { + public: + //------------------------------------------------------------- + // TO CALL AT INITIALISATION: DEFINES THE SIZE OF THE INPUT IMAGES + // NORMALLY, UNNECESSARY IF A CONFIGURATION FILE IS LOADED + void Init(int width, int height); + + //------------------------------------------------------------- + // PROVIDE A MASK TO DEFINE THE SET OF POINTS WHERE BACKGROUND + // SUBTRACTION DOES NOT NEED TO BE PERFORMED + // + // mode is useful to specify if the points to remove from + // processing are in addition to the ones potentially + // removed according to the configuration file, + // or if they are the only ones to be removed + // + // mode=0 : provided points need to be removed + // in addition to those already removed + // mode=1 : the provided points are the only one to remove + // from processing + // Note: maskImage(li,co)=0 indicate the points to remove + // from background processing + void SetValidPointMask(IplImage* maskImage, int mode); + + //------------------------------------------------------------- + // + // set the frame rate, to adjust the update parameters + // to the actual frame rate. + // Can be called only once at initialisation, + // but in online cases, can be used to indicate + // the time interval during the last processed frame + // + // frameDuration is in millisecond + void SetFrameRate(float frameDuration); + + //------------------------------------------------------------- + // + // set some main parameters for background model learning. + // in general, we can set large updating rates for background + // model learning and set small updating rates in foreground + // detection + void SetParameters(int max_lbp_mode_num, // maximal LBP mode number + float mode_updating_learn_rate_per_second, // background mode updating learning rate per second + float weight_updating_learn_rate_per_second, // mode's weight updating learning rate per second + float low_init_mode_weight); // the low initial mode weight + + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE INPUT IMAGE + // -> INDICATE WHERE THE NEW IMAGE TO PROCESS IS STORED + // + // Here assumes that the input image will contain RGB images. + // The memory of this image is handled by the caller. + // + // The return value indicate whether the actual Background + // Subtraction algorithm handles RGB images (1) or not (0). + // + int SetRGBInputImage(IplImage * inputImage, CvRect *roi = NULL); + + //------------------------------------------------------------- + // PROVIDE A POINTER TO THE RESULT IMAGE + // INDICATE WHERE THE BACKGROUND RESULT NEED TO BE STORED + // + int SetForegroundMaskImage(IplImage* fg_mask_img); + int SetForegroundProbImage(IplImage* fg_prob_img); + + //------------------------------------------------------------- + // This function should be called each time a new image is + // available in the input image. + // + // The return value is 0 if everything goes well, a non-zero value + // otherwise. + // + int Process(); + + //------------------------------------------------------------- + // this function should save parameters and information of the model + // (e.g. after a training of the model, or in such a way + // that the model can be reload to process the next frame + // type of save: + // 0 - background model information (pixel by pixel) + // 1 - background model parameters + // 2 - both background information (pixel by pixel) and parameters + void Save(const char *bg_model_fn, int save_type); + void Save(const char* bg_model_fn); + + //------------------------------------------------------------- + // this function should load the parameters necessary + // for the processing of the background subtraction or + // load background model information + bool Load(const char *bg_model_fn); + + + void SetCurrentFrameNumber(unsigned long cur_frame_no); + + void GetForegroundMaskImage(IplImage *fg_mask_img); + void GetForegroundImage(IplImage *fg_img, CvScalar bg_color = CV_RGB(0, 255, 0)); + void GetBackgroundImage(IplImage *bk_img); + void GetForegroundProbabilityImage(IplImage* fg_prob_img); + + void GetBgLayerNoImage(IplImage *bg_layer_no_img, CvScalar* layer_colors = NULL, int layer_num = 0); + void GetLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, CvScalar empty_color = CV_RGB(0, 0, 0)); + void GetCurrentLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, IplImage *layered_fg_img = NULL, + CvScalar layered_bg_bk_color = CV_RGB(0, 0, 0), CvScalar layered_fg_color = CV_RGB(255, 0, 0), + int smooth_win = 13, float smooth_sigma = 3.0f, float below_layer_noise = 0.5f, float above_layer_noise = 0.3f, int min_blob_size = 50); + float DistLBP(LBPStruct *LBP1, LBPStruct *LBP2); + void GetColoredBgMultiLayeredImage(IplImage *bg_multi_layer_img, CvScalar *layer_colors); + void UpdatePatternColorDistWeights(float *cur_pattern, float *bg_pattern); + void ExportLogMessage(char* msg); + void Postprocessing(); + void GetFloatEdgeImage(IplImage *src, IplImage *dst); + void RemoveBackgroundLayers(PixelLBPStruct *PLBP, bool *removed_modes = NULL); + float CalColorRangeDist(unsigned char *cur_intensity, float *bg_intensity, float *max_intensity, + float *min_intensity, float shadow_rate, float highlight_rate); + float CalVectorsAngle(float *c1, unsigned char *c2, int length); + float CalVectorsNoisedAngle(float *bg_color, unsigned char *noised_color, float offset, int length); + void ComputeGradientImage(IplImage *src, IplImage *dst, bool bIsFloat); + float CalColorBgDist(uchar *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity); + float CalPatternBgDist(float *cur_pattern, float *bg_pattern); + + void GetForegroundMaskMap(CvMat *fg_mask_mat); + void Initialization(IplImage *first_img, int lbp_level_num, float *radiuses, int *neig_pt_nums); + void GetCurrentBackgroundDistMap(CvMat *bk_dist_map); + void BackgroundSubtractionProcess(); + void SetBkMaskImage(IplImage *mask_img); + void SetNewImage(IplImage *new_img, CvRect *roi = NULL); + + void ResetAllParameters(); + void QuickSort(float *pData, unsigned short *pIdxes, long low, long high, bool bAscent); + void UpdateBgPixelPattern(float *cur_pattern, float *bg_bg_pattern); + void UpdateBgPixelColor(unsigned char* cur_intensity, float* bg_intensity); + void Update_MAX_MIN_Intensity(unsigned char *cur_intensity, float *max_intensity, float *min_intensity); + void MergeImages(int num, ...); + + int m_nChannel; /* most of opencv functions support 1,2,3 or 4 channels, for the input images */ + + PixelLBPStruct* m_pPixelLBPs; /* the LBP texture patterns for each image */ + int m_nMaxLBPModeNum; /* the maximal number for the used LBP pattern models */ + float m_fModeUpdatingLearnRate; /* the background mode learning rate */ + float m_fWeightUpdatingLearnRate; /* the background mode weight updating rate */ + float m_f1_ModeUpdatingLearnRate; /* 1 - background_mode_learning_rate */ + float m_f1_WeightUpdatingLearnRate; /* 1 - background_mode_weight_updating_rate */ + float m_fRobustColorOffset; /* the intensity offset robust to noise */ + float m_fLowInitialModeWeight; /* the lowest weight of initial background mode */ + int m_nLBPLength; /* the length of texture LBP operator */ + float m_fPatternColorDistBgThreshold; /* the threshold value used to classify background and foreground */ + float m_fPatternColorDistBgUpdatedThreshold; /* the threshold value used to update the background modeling */ + float m_fMinBgLayerWeight; /* the minimal weight to remove background layers */ + + int m_nPatternDistSmoothNeigHalfSize; /* the neighboring half size of gaussian window to remove the noise + on the distance map */ + float m_fPatternDistConvGaussianSigma; /* the gaussian sigma used to remove the noise on the distance map */ + + float m_fBackgroundModelPercent; /* the background mode percent, the first several background modes + with high mode weights should be regarded as reliable background modes */ + + float m_fRobustShadowRate; /* the minimal shadow rate, [0.4, 0.7] */ + float m_fRobustHighlightRate; /* the maximal highlight rate, [1.1, 1.4] */ + + int m_nLBPImgNum; /* the number of images used for texture LBP feature */ + + float m_fMinLBPBinaryProb; /* the minimal LBP binary probability */ + float m_f1_MinLBPBinaryProb; /* 1 - minimal_LBP_binary_probability */ + + CvSize m_cvImgSize; /* the image size (width, height) */ + + unsigned long m_nCurImgFrameIdx; /* the frame index of current image */ + + bool m_bUsedGradImage; /* the boolean variable signaling whether the gradient image is used + or not for computing LBP operator */ + + bool m_bUsedColorLBP; /* true - multi-channel color image for LBP operator, + false - gray-scale image for LBP operator */ + + CLocalBinaryPattern m_cLBP; /* the class instant for computing LBP (local binary pattern) texture feature */ + + IplImage* m_pBkMaskImg; /* the mask image corresponding to the input image, + i.e. all the masked pixels should be processed */ + + IplImage* m_pOrgImg; /* the original image */ + IplImage** m_ppOrgLBPImgs; /* the multi-layer images used for LBP feature extraction */ + IplImage* m_pFgImg; /* the foreground image */ + IplImage* m_pBgImg; /* the background image */ + IplImage* m_pFgMaskImg; /* the foreground mask image */ + IplImage* m_pBgDistImg; /* the background distance image (float) */ + IplImage* m_pEdgeImg; /* the edge image used for cross bilateral filter */ + IplImage* m_pFgProbImg; /* the foreground probability image (uchar) */ + + IplImage* m_pFirstAppearingTimeMap; + + #ifdef LINUX_BILATERAL_FILTER + CCrossBilateralFilter m_cCrossBF; /* the class instant for cross bilateral filter + which should be used to remove noise on the distance map */ + #endif + + bool m_disableLearning; + float m_fSigmaS; /* sigma in the spatial domain for cross bilateral filter */ + float m_fSigmaR; /* sigma in the normalized intensity domain for cross bilateral filter */ + + float m_fTextureWeight; /* the weight value of texture LBP feature + for background modeling & foreground detection */ + + float m_fColorWeight; /* the weight value of color invariant feature + for background modeling & foreground detection */ + + float m_fWeightUpdatingConstant; /* the constant ( >= 1 ) for 'hysteries' weight updating scheme + (increase when matched, decrease when un-matched */ + + float m_fReliableBackgroundModeWeight; /* the weight value for background mode + which should be regarded as a reliable background mode, + which is useful for multi-layer scheme */ + + float m_fMinNoisedAngle; /* the minimal angle value between the background color + and the noised observed color */ + + float m_fMinNoisedAngleSine; /* the minimal angle sine value between the background color + and the noised observed color */ - float m_fFrameDuration; /* frame duration */ + float m_fFrameDuration; /* frame duration */ - float m_fModeUpdatingLearnRatePerSecond; - float m_fWeightUpdatingLearnRatePerSecond; + float m_fModeUpdatingLearnRatePerSecond; + float m_fWeightUpdatingLearnRatePerSecond; - int m_nLBPLevelNum; - float m_pLBPRadiuses[10]; - int m_pLBPMeigPointNums[10]; + int m_nLBPLevelNum; + float m_pLBPRadiuses[10]; + int m_pLBPMeigPointNums[10]; - CvRect* m_pROI; - CMultiLayerBGS(); - virtual ~CMultiLayerBGS(); -}; + CvRect* m_pROI; + CMultiLayerBGS(); + virtual ~CMultiLayerBGS(); + }; + } + } +} diff --git a/src/algorithms/MultiLayer/LocalBinaryPattern.cpp b/src/algorithms/MultiLayer/LocalBinaryPattern.cpp index d781d4eecfa224b59b8080220fb77fe2ea9c9502..7ff5f17a520e4b02b28d61e76b420377deff4074 100644 --- a/src/algorithms/MultiLayer/LocalBinaryPattern.cpp +++ b/src/algorithms/MultiLayer/LocalBinaryPattern.cpp @@ -1,11 +1,8 @@ #include "LocalBinaryPattern.h" -////////////////////////////////////////////////////////////////////// -// Construction/Destruction -////////////////////////////////////////////////////////////////////// +using namespace bgslibrary::algorithms::multilayer; -CLocalBinaryPattern::CLocalBinaryPattern() -{ +CLocalBinaryPattern::CLocalBinaryPattern() { m_ppOrgImgs = NULL; m_pRadiuses = NULL; m_fRobustWhiteNoise = 3.0f; @@ -14,26 +11,20 @@ CLocalBinaryPattern::CLocalBinaryPattern() m_pShiftedImg = NULL; } -CLocalBinaryPattern::~CLocalBinaryPattern() -{ +CLocalBinaryPattern::~CLocalBinaryPattern() { FreeMemories(); } void CLocalBinaryPattern::Initialization(IplImage **first_imgs, int imgs_num, int level_num, float *radius, int *neig_pt_num, float robust_white_noise, int type) { - m_nImgsNum = imgs_num; - m_nLBPLevelNum = level_num; - m_pRadiuses = new float[m_nLBPLevelNum]; m_pNeigPointsNums = new int[m_nLBPLevelNum]; m_ppOrgImgs = first_imgs; - int a, b; for (a = 0; a < m_nImgsNum; a++) { m_cvImgSize = cvGetSize(first_imgs[a]); - if (first_imgs[a]->nChannels > 1) { printf("Input image channel must be 1!"); exit(1); @@ -52,9 +43,7 @@ void CLocalBinaryPattern::Initialization(IplImage **first_imgs, int imgs_num, in } m_pShiftedImg = cvCloneImage(m_ppOrgImgs[0]); - m_pXYShifts = new CvPoint[tot_neig_pts_num]; - m_nMaxShift.x = 0; m_nMaxShift.y = 0; int shift_idx = 0; diff --git a/src/algorithms/MultiLayer/LocalBinaryPattern.h b/src/algorithms/MultiLayer/LocalBinaryPattern.h index d02aa6769202ffa82763134fece607befad00a5e..30e17e98d53e22362d28f2fdaddb45224b1fd093 100644 --- a/src/algorithms/MultiLayer/LocalBinaryPattern.h +++ b/src/algorithms/MultiLayer/LocalBinaryPattern.h @@ -5,46 +5,55 @@ #include "BGS.h" #include "OpenCvDataConversion.h" -/************************************************************************/ -/* two types of computing the LBP operators but currently GENERAL_LBP */ -/* has been implemented. */ -/************************************************************************/ -#define GENERAL_LBP 0 -#define SYMMETRIC_LBP 1 - -class CLocalBinaryPattern +namespace bgslibrary { -public: - void CalImageDifferenceMap(IplImage *cent_img, IplImage *neig_img, float *pattern, CvRect *roi = NULL); - void CalNeigPixelOffset(float radius, int tot_neig_pts_num, int neig_pt_idx, int &offset_x, int &offset_y); - void CalShiftedImage(IplImage *src, int offset_x, int offset_y, IplImage *dst, CvRect *roi = NULL); - void FreeMemories(); - void ComputeLBP(PixelLBPStruct *PLBP, CvRect *roi = NULL); - void SetNewImages(IplImage **new_imgs); - - IplImage** m_ppOrgImgs; /* the original images used for computing the LBP operators */ - - void Initialization(IplImage **first_imgs, int imgs_num, - int level_num, float *radius, int *neig_pt_num, - float robust_white_noise = 3.0f, int type = GENERAL_LBP); - - CLocalBinaryPattern(); - virtual ~CLocalBinaryPattern(); - - float m_fRobustWhiteNoise; /* the robust noise value for computing the LBP operator in each channel */ - -private: - void SetShiftedMeshGrid(CvSize img_size, float offset_x, float offset_y, CvMat *grid_map_x, CvMat *grid_map_y); - - float* m_pRadiuses; /* the circle radiuses for the LBP operator */ - //int m_nLBPType; /* the type of computing LBP operator */ - int* m_pNeigPointsNums; /* the numbers of neighboring pixels on multi-level circles */ - int m_nImgsNum; /* the number of multi-channel image */ - int m_nLBPLevelNum; /* the number of multi-level LBP operator */ - CvSize m_cvImgSize; /* the image size (width, height) */ - - CvPoint* m_pXYShifts; - CvPoint m_nMaxShift; - - IplImage* m_pShiftedImg; -}; + namespace algorithms + { + namespace multilayer + { + /************************************************************************/ + /* two types of computing the LBP operators but currently GENERAL_LBP */ + /* has been implemented. */ + /************************************************************************/ + const int GENERAL_LBP = 0; + const int SYMMETRIC_LBP = 1; + + class CLocalBinaryPattern + { + public: + void CalImageDifferenceMap(IplImage *cent_img, IplImage *neig_img, float *pattern, CvRect *roi = NULL); + void CalNeigPixelOffset(float radius, int tot_neig_pts_num, int neig_pt_idx, int &offset_x, int &offset_y); + void CalShiftedImage(IplImage *src, int offset_x, int offset_y, IplImage *dst, CvRect *roi = NULL); + void FreeMemories(); + void ComputeLBP(PixelLBPStruct *PLBP, CvRect *roi = NULL); + void SetNewImages(IplImage **new_imgs); + + IplImage** m_ppOrgImgs; /* the original images used for computing the LBP operators */ + + void Initialization(IplImage **first_imgs, int imgs_num, + int level_num, float *radius, int *neig_pt_num, + float robust_white_noise = 3.0f, int type = GENERAL_LBP); + + CLocalBinaryPattern(); + virtual ~CLocalBinaryPattern(); + + float m_fRobustWhiteNoise; /* the robust noise value for computing the LBP operator in each channel */ + + private: + void SetShiftedMeshGrid(CvSize img_size, float offset_x, float offset_y, CvMat *grid_map_x, CvMat *grid_map_y); + + float* m_pRadiuses; /* the circle radiuses for the LBP operator */ + //int m_nLBPType; /* the type of computing LBP operator */ + int* m_pNeigPointsNums; /* the numbers of neighboring pixels on multi-level circles */ + int m_nImgsNum; /* the number of multi-channel image */ + int m_nLBPLevelNum; /* the number of multi-level LBP operator */ + CvSize m_cvImgSize; /* the image size (width, height) */ + + CvPoint* m_pXYShifts; + CvPoint m_nMaxShift; + + IplImage* m_pShiftedImg; + }; + } + } +} diff --git a/src/algorithms/MultiLayer/OpenCvDataConversion.h b/src/algorithms/MultiLayer/OpenCvDataConversion.h index 5280b8ad93955f49a3885f1a87be93b6760e194e..4da11a5597f01cb9292ce828640d05871db4d2ef 100644 --- a/src/algorithms/MultiLayer/OpenCvDataConversion.h +++ b/src/algorithms/MultiLayer/OpenCvDataConversion.h @@ -4,172 +4,181 @@ // opencv legacy includes #include "OpenCvLegacyIncludes.h" -template <class TI, class TM> /* class TI - the type of image data, class TM - the type of matrix data */ -class COpencvDataConversion +namespace bgslibrary { -public: - - /* get the image data */ - TI * GetImageData(IplImage *img) - { - if (!img->roi) { /* no ROI used, i.e. the whole image */ - int y; //, x; - TI* img_data = new TI[img->width*img->height*img->nChannels]; - TI* temp = img_data; - TI* x_data; - - for (y = 0; y < img->height; y++) { - x_data = (TI*)(img->imageData + img->widthStep*y); - int row_length = img->width*img->nChannels; - memcpy(temp, x_data, sizeof(TI)*row_length); - temp += row_length; - /* - for ( x = 0 ; x < img->width*img->nChannels ; x++ ) - *temp++ = *x_data++; - */ - } - - return img_data; - } - else { /* get image data only in ROI */ - int y;//, x; - TI* img_data = new TI[img->roi->width*img->roi->height*img->nChannels]; - TI* temp = img_data; - TI* x_data; - for (y = img->roi->yOffset; y < img->roi->yOffset + img->roi->height; y++) { - x_data = (TI*)(img->imageData + img->widthStep*y + img->roi->xOffset*sizeof(TI)*img->nChannels); - int row_length = img->roi->width*img->nChannels; - memcpy(temp, x_data, sizeof(TI)*row_length); - temp += row_length; - /* - for ( x = 0 ; x < img->roi->width*img->nChannels ; x++ ) - *temp++ = *x_data++; - */ - } - return img_data; - } - }; - - /* set the image data */ - void SetImageData(IplImage *img, TI *img_data) - { - if (!img->roi) { /* no ROI used, i.e. the whole image */ - int y;//, x; - TI* temp = img_data; - TI* x_data; - for (y = 0; y < img->height; y++) { - x_data = (TI*)(img->imageData + img->widthStep*y); - int row_length = img->width*img->nChannels; - memcpy(x_data, temp, sizeof(TI)*row_length); - temp += row_length; - /* - for ( x = 0 ; x < img->width*img->nChannels ; x++ ) - *x_data++ = *temp++; - */ - } - } - else { /* set image data only in ROI */ - int y;//, x; - TI* temp = img_data; - TI* x_data; - for (y = img->roi->yOffset; y < img->roi->yOffset + img->roi->height; y++) { - x_data = (TI*)(img->imageData + img->widthStep*y + img->roi->xOffset*sizeof(TI)*img->nChannels); - int row_length = img->roi->width*img->nChannels; - memcpy(x_data, temp, sizeof(TI)*row_length); - temp += row_length; - /* - for ( x = 0 ; x < img->roi->width*img->nChannels ; x++ ) - *x_data++ = *temp++; - */ - } - } - } - - /* get the matrix data */ - TM * GetMatData(CvMat *mat) - { - TM* mat_data = new TM[mat->width*mat->height]; - memcpy(mat_data, mat->data.ptr, sizeof(TM)*mat->width*mat->height); - return mat_data; - - /* - int y, x; - TM* mat_data = new TM[mat->width*mat->height]; - TM* temp = mat_data; - TM* x_data; - for ( y = 0 ; y < mat->height ; y++ ) { - x_data = (TM*)(mat->data.ptr + mat->step*y); - for ( x = 0 ; x < mat->width ; x++ ) - *temp++ = *x_data++; - } - return mat_data; - */ - }; - - /* set the matrix data */ - void SetMatData(CvMat *mat, TM *mat_data) + namespace algorithms { - memcpy(mat->data.ptr, mat_data, sizeof(TM)*mat->width*mat->height); - - /* - int y, x; - TM* temp = mat_data; - TM* x_data; - for ( y = 0 ; y < mat->height ; y++ ) { - x_data = (TM*)(mat->data.ptr + mat->step*y); - for ( x = 0 ; x < mat->width ; x++ ) - *x_data++ = *temp++; + namespace multilayer + { + template <class TI, class TM> /* class TI - the type of image data, class TM - the type of matrix data */ + class COpencvDataConversion + { + public: + + /* get the image data */ + TI * GetImageData(IplImage *img) + { + if (!img->roi) { /* no ROI used, i.e. the whole image */ + int y; //, x; + TI* img_data = new TI[img->width*img->height*img->nChannels]; + TI* temp = img_data; + TI* x_data; + + for (y = 0; y < img->height; y++) { + x_data = (TI*)(img->imageData + img->widthStep*y); + int row_length = img->width*img->nChannels; + memcpy(temp, x_data, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->width*img->nChannels ; x++ ) + *temp++ = *x_data++; + */ + } + + return img_data; + } + else { /* get image data only in ROI */ + int y;//, x; + TI* img_data = new TI[img->roi->width*img->roi->height*img->nChannels]; + TI* temp = img_data; + TI* x_data; + for (y = img->roi->yOffset; y < img->roi->yOffset + img->roi->height; y++) { + x_data = (TI*)(img->imageData + img->widthStep*y + img->roi->xOffset*sizeof(TI)*img->nChannels); + int row_length = img->roi->width*img->nChannels; + memcpy(temp, x_data, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->roi->width*img->nChannels ; x++ ) + *temp++ = *x_data++; + */ + } + return img_data; + } + }; + + /* set the image data */ + void SetImageData(IplImage *img, TI *img_data) + { + if (!img->roi) { /* no ROI used, i.e. the whole image */ + int y;//, x; + TI* temp = img_data; + TI* x_data; + for (y = 0; y < img->height; y++) { + x_data = (TI*)(img->imageData + img->widthStep*y); + int row_length = img->width*img->nChannels; + memcpy(x_data, temp, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->width*img->nChannels ; x++ ) + *x_data++ = *temp++; + */ + } + } + else { /* set image data only in ROI */ + int y;//, x; + TI* temp = img_data; + TI* x_data; + for (y = img->roi->yOffset; y < img->roi->yOffset + img->roi->height; y++) { + x_data = (TI*)(img->imageData + img->widthStep*y + img->roi->xOffset*sizeof(TI)*img->nChannels); + int row_length = img->roi->width*img->nChannels; + memcpy(x_data, temp, sizeof(TI)*row_length); + temp += row_length; + /* + for ( x = 0 ; x < img->roi->width*img->nChannels ; x++ ) + *x_data++ = *temp++; + */ + } + } + } + + /* get the matrix data */ + TM * GetMatData(CvMat *mat) + { + TM* mat_data = new TM[mat->width*mat->height]; + memcpy(mat_data, mat->data.ptr, sizeof(TM)*mat->width*mat->height); + return mat_data; + + /* + int y, x; + TM* mat_data = new TM[mat->width*mat->height]; + TM* temp = mat_data; + TM* x_data; + for ( y = 0 ; y < mat->height ; y++ ) { + x_data = (TM*)(mat->data.ptr + mat->step*y); + for ( x = 0 ; x < mat->width ; x++ ) + *temp++ = *x_data++; + } + return mat_data; + */ + }; + + /* set the matrix data */ + void SetMatData(CvMat *mat, TM *mat_data) + { + memcpy(mat->data.ptr, mat_data, sizeof(TM)*mat->width*mat->height); + + /* + int y, x; + TM* temp = mat_data; + TM* x_data; + for ( y = 0 ; y < mat->height ; y++ ) { + x_data = (TM*)(mat->data.ptr + mat->step*y); + for ( x = 0 ; x < mat->width ; x++ ) + *x_data++ = *temp++; + } + */ + } + + /* convert the image data to the matrix data */ + void ConvertData(IplImage *img_src, CvMat *mat_dst) + { + if (img_src->nChannels > 1) { + printf("Must be one-channel image for ConvertImageData!\n"); + exit(1); + } + + TI* _img_data = GetImageData(img_src); + TM* _mat_data = new TM[img_src->width*img_src->height]; + + TI* img_data = _img_data; + TM* mat_data = _mat_data; + int i; + for (i = 0; i < img_src->width*img_src->height; i++) + *mat_data++ = (TM)(*img_data++); + + SetMatData(mat_dst, _mat_data); + + delete[] _img_data; + delete[] _mat_data; + } + + /* convert the matrix data to the image data */ + void ConvertData(CvMat *mat_src, IplImage *img_dst) + { + if (img_dst->nChannels > 1) { + printf("Must be one-channel image for ConvertImageData!\n"); + exit(1); + } + + TM* _mat_data = GetMatData(mat_src); + TI* _img_data = new TI[mat_src->width*mat_src->height]; + + TM* mat_data = _mat_data; + TI* img_data = _img_data; + + int i; + for (i = 0; i < mat_src->width*mat_src->height; i++) + *img_data++ = (TI)(*mat_data++); + + SetImageData(img_dst, _img_data); + + delete[] _img_data; + delete[] _mat_data; + } + + COpencvDataConversion() {}; + virtual ~COpencvDataConversion() {}; + }; } - */ } - - /* convert the image data to the matrix data */ - void ConvertData(IplImage *img_src, CvMat *mat_dst) - { - if (img_src->nChannels > 1) { - printf("Must be one-channel image for ConvertImageData!\n"); - exit(1); - } - - TI* _img_data = GetImageData(img_src); - TM* _mat_data = new TM[img_src->width*img_src->height]; - - TI* img_data = _img_data; - TM* mat_data = _mat_data; - int i; - for (i = 0; i < img_src->width*img_src->height; i++) - *mat_data++ = (TM)(*img_data++); - - SetMatData(mat_dst, _mat_data); - - delete[] _img_data; - delete[] _mat_data; - } - - /* convert the matrix data to the image data */ - void ConvertData(CvMat *mat_src, IplImage *img_dst) - { - if (img_dst->nChannels > 1) { - printf("Must be one-channel image for ConvertImageData!\n"); - exit(1); - } - - TM* _mat_data = GetMatData(mat_src); - TI* _img_data = new TI[mat_src->width*mat_src->height]; - - TM* mat_data = _mat_data; - TI* img_data = _img_data; - - int i; - for (i = 0; i < mat_src->width*mat_src->height; i++) - *img_data++ = (TI)(*mat_data++); - - SetImageData(img_dst, _img_data); - - delete[] _img_data; - delete[] _mat_data; - } - - COpencvDataConversion() {}; - virtual ~COpencvDataConversion() {}; -}; +} diff --git a/src/algorithms/MultiLayer/blob.cpp b/src/algorithms/MultiLayer/blob.cpp index 62e879ac12060f685c1a700e2562c2695d5e6a9f..e44f98d126905ff06b6a6cb08e7b530f3862970d 100644 --- a/src/algorithms/MultiLayer/blob.cpp +++ b/src/algorithms/MultiLayer/blob.cpp @@ -1,1089 +1,1094 @@ #include <limits.h> #include "blob.h" -namespace Blob +namespace bgslibrary { - /** - - FUNCIÓ: CBlob - - FUNCIONALITAT: Constructor està ndard - - PARÀMETRES: - - RESULTAT: - - inicialització de totes les variables internes i de l'storage i la sequencia - per a les cantonades del blob - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlob - - FUNCTIONALITY: Standard constructor - - PARAMETERS: - - RESULT: - - memory allocation for the blob edges and initialization of member variables - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlob::CBlob() + namespace algorithms { - etiqueta = -1; // Flag indicates null region - exterior = 0; - area = 0.0f; - perimeter = 0.0f; - parent = -1; - minx = LONG_MAX; - maxx = 0; - miny = LONG_MAX; - maxy = 0; - sumx = 0; - sumy = 0; - sumxx = 0; - sumyy = 0; - sumxy = 0; - mean = 0; - stddev = 0; - externPerimeter = 0; - - m_storage = cvCreateMemStorage(0); - edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, - sizeof(CvContour), - sizeof(CvPoint), m_storage); - } - - /** - - FUNCIÓ: CBlob - - FUNCIONALITAT: Constructor de còpia - - PARÀMETRES: - - RESULTAT: - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlob - - FUNCTIONALITY: Copy constructor - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlob::CBlob(const CBlob &src) - { - // copiem les propietats del blob origen a l'actual - etiqueta = src.etiqueta; - exterior = src.exterior; - area = src.Area(); - perimeter = src.Perimeter(); - parent = src.parent; - minx = src.minx; - maxx = src.maxx; - miny = src.miny; - maxy = src.maxy; - sumx = src.sumx; - sumy = src.sumy; - sumxx = src.sumxx; - sumyy = src.sumyy; - sumxy = src.sumxy; - mean = src.mean; - stddev = src.stddev; - externPerimeter = src.externPerimeter; - - // copiem els edges del blob origen a l'actual - CvSeqReader reader; - CvSeqWriter writer; - CvPoint edgeactual; - - // creem una sequencia buida per als edges - m_storage = cvCreateMemStorage(0); - edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, - sizeof(CvContour), - sizeof(CvPoint), m_storage); - - cvStartReadSeq(src.Edges(), &reader); - cvStartAppendToSeq(edges, &writer); - - for (int i = 0; i < src.Edges()->total; i++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - CV_WRITE_SEQ_ELEM(edgeactual, writer); - } - - cvEndWriteSeq(&writer); - } - CBlob::CBlob(const CBlob *src) - { - // copiem les propietats del blob origen a l'actual - etiqueta = src->etiqueta; - exterior = src->exterior; - area = src->Area(); - perimeter = src->Perimeter(); - parent = src->parent; - minx = src->minx; - maxx = src->maxx; - miny = src->miny; - maxy = src->maxy; - sumx = src->sumx; - sumy = src->sumy; - sumxx = src->sumxx; - sumyy = src->sumyy; - sumxy = src->sumxy; - mean = src->mean; - stddev = src->stddev; - externPerimeter = src->externPerimeter; - - // copiem els edges del blob origen a l'actual - CvSeqReader reader; - CvSeqWriter writer; - CvPoint edgeactual; - - // creem una sequencia buida per als edges - m_storage = cvCreateMemStorage(0); - edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, - sizeof(CvContour), - sizeof(CvPoint), m_storage); - - cvStartReadSeq(src->Edges(), &reader); - cvStartAppendToSeq(edges, &writer); - - for (int i = 0; i < src->Edges()->total; i++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - CV_WRITE_SEQ_ELEM(edgeactual, writer); - } - - cvEndWriteSeq(&writer); - } - - /** - - FUNCIÓ: ~CBlob - - FUNCIONALITAT: Destructor està ndard - - PARÀMETRES: - - RESULTAT: - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlob - - FUNCTIONALITY: Standard destructor - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlob::~CBlob() - { - // Eliminar vèrtexs del blob - cvClearSeq(edges); - // i la zona de memòria on són - cvReleaseMemStorage(&m_storage); - } - - /** - - FUNCIÓ: operator= - - FUNCIONALITAT: Operador d'assignació - - PARÀMETRES: - - src: blob a assignar a l'actual - - RESULTAT: - - Substitueix el blob actual per el src - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: Assigment operator - - FUNCTIONALITY: Assigns a blob to the current - - PARAMETERS: - - src: blob to assign - - RESULT: - - the current blob is replaced by the src blob - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CBlob& CBlob::operator=(const CBlob &src) - { - // si ja són el mateix, no cal fer res - if (this != &src) - { - // Eliminar vèrtexs del blob - cvClearSeq(edges); - // i la zona de memòria on són - cvReleaseMemStorage(&m_storage); - - // creem una sequencia buida per als edges - m_storage = cvCreateMemStorage(0); - edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, - sizeof(CvContour), - sizeof(CvPoint), m_storage); - - // copiem les propietats del blob origen a l'actual - etiqueta = src.etiqueta; - exterior = src.exterior; - area = src.Area(); - perimeter = src.Perimeter(); - parent = src.parent; - minx = src.minx; - maxx = src.maxx; - miny = src.miny; - maxy = src.maxy; - sumx = src.sumx; - sumy = src.sumy; - sumxx = src.sumxx; - sumyy = src.sumyy; - sumxy = src.sumxy; - mean = src.mean; - stddev = src.stddev; - externPerimeter = src.externPerimeter; - - // copiem els edges del blob origen a l'actual - CvSeqReader reader; - CvSeqWriter writer; - CvPoint edgeactual; - - cvStartReadSeq(src.Edges(), &reader); - cvStartAppendToSeq(edges, &writer); - - for (int i = 0; i < src.Edges()->total; i++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - CV_WRITE_SEQ_ELEM(edgeactual, writer); - } - - cvEndWriteSeq(&writer); - } - return *this; - } - - /** - - FUNCIÓ: FillBlob - - FUNCIONALITAT: Pinta l'interior d'un blob amb el color especificat - - PARÀMETRES: - - imatge: imatge on es vol pintar el el blob - - color: color amb que es vol pintar el blob - - RESULTAT: - - retorna la imatge d'entrada amb el blob pintat - - RESTRICCIONS: - - AUTOR: - - Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: FillBlob - - FUNCTIONALITY: - - Fills the blob with a specified colour - - PARAMETERS: - - imatge: where to paint - - color: colour to paint the blob - - RESULT: - - modifies input image and returns the seed point used to fill the blob - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlob::FillBlob(IplImage *imatge, CvScalar color, int offsetX /*=0*/, int offsetY /*=0*/) const - { - - //verifiquem que existeixi el blob i que tingui cantonades - if (edges == NULL || edges->total == 0) return; - - CvPoint edgeactual, pt1, pt2; - CvSeqReader reader; - vectorPunts vectorEdges = vectorPunts(edges->total); - vectorPunts::iterator itEdges, itEdgesSeguent; - bool dinsBlob; - int yActual; - - // passem els punts del blob a un vector de punts de les STL - cvStartReadSeq(edges, &reader); - itEdges = vectorEdges.begin(); - while (itEdges != vectorEdges.end()) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - *itEdges = edgeactual; - ++itEdges; - } - // ordenem el vector per les Y's i les X's d'esquerra a dreta - std::sort(vectorEdges.begin(), vectorEdges.end(), comparaCvPoint()); - - // recorrem el vector ordenat i fem linies entre punts consecutius - itEdges = vectorEdges.begin(); - itEdgesSeguent = vectorEdges.begin() + 1; - dinsBlob = true; - while (itEdges != (vectorEdges.end() - 1)) + namespace multilayer { - yActual = (*itEdges).y; - - if (((*itEdges).x != (*itEdgesSeguent).x) && - ((*itEdgesSeguent).y == yActual) - ) + namespace blob { - if (dinsBlob) + /** + - FUNCIÓ: CBlob + - FUNCIONALITAT: Constructor està ndard + - PARÀMETRES: + - RESULTAT: + - inicialització de totes les variables internes i de l'storage i la sequencia + per a les cantonades del blob + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlob + - FUNCTIONALITY: Standard constructor + - PARAMETERS: + - RESULT: + - memory allocation for the blob edges and initialization of member variables + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlob::CBlob() { - pt1 = *itEdges; - pt1.x += offsetX; - pt1.y += offsetY; - - pt2 = *itEdgesSeguent; - pt2.x += offsetX; - pt2.y += offsetY; - - cvLine(imatge, pt1, pt2, color); + etiqueta = -1; // Flag indicates null region + exterior = 0; + area = 0.0f; + perimeter = 0.0f; + parent = -1; + minx = LONG_MAX; + maxx = 0; + miny = LONG_MAX; + maxy = 0; + sumx = 0; + sumy = 0; + sumxx = 0; + sumyy = 0; + sumxy = 0; + mean = 0; + stddev = 0; + externPerimeter = 0; + + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint), m_storage); } - dinsBlob = !dinsBlob; - } - ++itEdges; - ++itEdgesSeguent; - if ((*itEdges).y != yActual) dinsBlob = true; - } - vectorEdges.clear(); - } - /** - - FUNCIÓ: CopyEdges - - FUNCIONALITAT: Afegeix els vèrtexs del blob al blob destination - - PARÀMETRES: - - destination: blob al que volem afegir els vèrtexs - - RESULTAT: - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CopyEdges - - FUNCTIONALITY: Adds the blob edges to destination - - PARAMETERS: - - destination: where to add the edges - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlob::CopyEdges(CBlob &destination) const - { - CvSeqReader reader; - CvSeqWriter writer; - CvPoint edgeactual; + /** + - FUNCIÓ: CBlob + - FUNCIONALITAT: Constructor de còpia + - PARÀMETRES: + - RESULTAT: + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlob + - FUNCTIONALITY: Copy constructor + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlob::CBlob(const CBlob &src) + { + // copiem les propietats del blob origen a l'actual + etiqueta = src.etiqueta; + exterior = src.exterior; + area = src.Area(); + perimeter = src.Perimeter(); + parent = src.parent; + minx = src.minx; + maxx = src.maxx; + miny = src.miny; + maxy = src.maxy; + sumx = src.sumx; + sumy = src.sumy; + sumxx = src.sumxx; + sumyy = src.sumyy; + sumxy = src.sumxy; + mean = src.mean; + stddev = src.stddev; + externPerimeter = src.externPerimeter; + + // copiem els edges del blob origen a l'actual + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + // creem una sequencia buida per als edges + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint), m_storage); + + cvStartReadSeq(src.Edges(), &reader); + cvStartAppendToSeq(edges, &writer); + + for (int i = 0; i < src.Edges()->total; i++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + CV_WRITE_SEQ_ELEM(edgeactual, writer); + } + + cvEndWriteSeq(&writer); + } + CBlob::CBlob(const CBlob *src) + { + // copiem les propietats del blob origen a l'actual + etiqueta = src->etiqueta; + exterior = src->exterior; + area = src->Area(); + perimeter = src->Perimeter(); + parent = src->parent; + minx = src->minx; + maxx = src->maxx; + miny = src->miny; + maxy = src->maxy; + sumx = src->sumx; + sumy = src->sumy; + sumxx = src->sumxx; + sumyy = src->sumyy; + sumxy = src->sumxy; + mean = src->mean; + stddev = src->stddev; + externPerimeter = src->externPerimeter; + + // copiem els edges del blob origen a l'actual + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + // creem una sequencia buida per als edges + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint), m_storage); + + cvStartReadSeq(src->Edges(), &reader); + cvStartAppendToSeq(edges, &writer); + + for (int i = 0; i < src->Edges()->total; i++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + CV_WRITE_SEQ_ELEM(edgeactual, writer); + } + + cvEndWriteSeq(&writer); + } - cvStartReadSeq(edges, &reader); - cvStartAppendToSeq(destination.Edges(), &writer); + /** + - FUNCIÓ: ~CBlob + - FUNCIONALITAT: Destructor està ndard + - PARÀMETRES: + - RESULTAT: + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlob + - FUNCTIONALITY: Standard destructor + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlob::~CBlob() + { + // Eliminar vèrtexs del blob + cvClearSeq(edges); + // i la zona de memòria on són + cvReleaseMemStorage(&m_storage); + } - for (int i = 0; i < edges->total; i++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - CV_WRITE_SEQ_ELEM(edgeactual, writer); - } + /** + - FUNCIÓ: operator= + - FUNCIONALITAT: Operador d'assignació + - PARÀMETRES: + - src: blob a assignar a l'actual + - RESULTAT: + - Substitueix el blob actual per el src + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: Assigment operator + - FUNCTIONALITY: Assigns a blob to the current + - PARAMETERS: + - src: blob to assign + - RESULT: + - the current blob is replaced by the src blob + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CBlob& CBlob::operator=(const CBlob &src) + { + // si ja són el mateix, no cal fer res + if (this != &src) + { + // Eliminar vèrtexs del blob + cvClearSeq(edges); + // i la zona de memòria on són + cvReleaseMemStorage(&m_storage); + + // creem una sequencia buida per als edges + m_storage = cvCreateMemStorage(0); + edges = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2, + sizeof(CvContour), + sizeof(CvPoint), m_storage); + + // copiem les propietats del blob origen a l'actual + etiqueta = src.etiqueta; + exterior = src.exterior; + area = src.Area(); + perimeter = src.Perimeter(); + parent = src.parent; + minx = src.minx; + maxx = src.maxx; + miny = src.miny; + maxy = src.maxy; + sumx = src.sumx; + sumy = src.sumy; + sumxx = src.sumxx; + sumyy = src.sumyy; + sumxy = src.sumxy; + mean = src.mean; + stddev = src.stddev; + externPerimeter = src.externPerimeter; + + // copiem els edges del blob origen a l'actual + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; + + cvStartReadSeq(src.Edges(), &reader); + cvStartAppendToSeq(edges, &writer); + + for (int i = 0; i < src.Edges()->total; i++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + CV_WRITE_SEQ_ELEM(edgeactual, writer); + } + + cvEndWriteSeq(&writer); + } + return *this; + } - cvEndWriteSeq(&writer); - } + /** + - FUNCIÓ: FillBlob + - FUNCIONALITAT: Pinta l'interior d'un blob amb el color especificat + - PARÀMETRES: + - imatge: imatge on es vol pintar el el blob + - color: color amb que es vol pintar el blob + - RESULTAT: + - retorna la imatge d'entrada amb el blob pintat + - RESTRICCIONS: + - AUTOR: + - Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: FillBlob + - FUNCTIONALITY: + - Fills the blob with a specified colour + - PARAMETERS: + - imatge: where to paint + - color: colour to paint the blob + - RESULT: + - modifies input image and returns the seed point used to fill the blob + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlob::FillBlob(IplImage *imatge, CvScalar color, int offsetX /*=0*/, int offsetY /*=0*/) const + { - /** - - FUNCIÓ: ClearEdges - - FUNCIONALITAT: Elimina els vèrtexs del blob - - PARÀMETRES: - - RESULTAT: - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: ClearEdges - - FUNCTIONALITY: Delete current blob edges - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - void CBlob::ClearEdges() - { - // Eliminar vèrtexs del blob eliminat - cvClearSeq(edges); - } + //verifiquem que existeixi el blob i que tingui cantonades + if (edges == NULL || edges->total == 0) return; + + CvPoint edgeactual, pt1, pt2; + CvSeqReader reader; + vectorPunts vectorEdges = vectorPunts(edges->total); + vectorPunts::iterator itEdges, itEdgesSeguent; + bool dinsBlob; + int yActual; + + // passem els punts del blob a un vector de punts de les STL + cvStartReadSeq(edges, &reader); + itEdges = vectorEdges.begin(); + while (itEdges != vectorEdges.end()) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + *itEdges = edgeactual; + ++itEdges; + } + // ordenem el vector per les Y's i les X's d'esquerra a dreta + std::sort(vectorEdges.begin(), vectorEdges.end(), comparaCvPoint()); + + // recorrem el vector ordenat i fem linies entre punts consecutius + itEdges = vectorEdges.begin(); + itEdgesSeguent = vectorEdges.begin() + 1; + dinsBlob = true; + while (itEdges != (vectorEdges.end() - 1)) + { + yActual = (*itEdges).y; + + if (((*itEdges).x != (*itEdgesSeguent).x) && + ((*itEdgesSeguent).y == yActual) + ) + { + if (dinsBlob) + { + pt1 = *itEdges; + pt1.x += offsetX; + pt1.y += offsetY; + + pt2 = *itEdgesSeguent; + pt2.x += offsetX; + pt2.y += offsetY; + + cvLine(imatge, pt1, pt2, color); + } + dinsBlob = !dinsBlob; + } + ++itEdges; + ++itEdgesSeguent; + if ((*itEdges).y != yActual) dinsBlob = true; + } + vectorEdges.clear(); + } - /** - - FUNCIÓ: GetConvexHull - - FUNCIONALITAT: Retorna el poligon convex del blob - - PARÀMETRES: - - dst: sequencia on desarem el resultat (no ha d'estar inicialitzada) - - RESULTAT: - - true si tot ha anat bé - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: GetConvexHull - - FUNCTIONALITY: Calculates the convex hull polygon of the blob - - PARAMETERS: - - dst: where to store the result - - RESULT: - - true if no error ocurred - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - bool CBlob::GetConvexHull(CvSeq **dst) const - { - if (edges != NULL && edges->total > 0) - { - *dst = cvConvexHull2(edges, 0, CV_CLOCKWISE, 0); - return true; - } - return false; - } + /** + - FUNCIÓ: CopyEdges + - FUNCIONALITAT: Afegeix els vèrtexs del blob al blob destination + - PARÀMETRES: + - destination: blob al que volem afegir els vèrtexs + - RESULTAT: + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CopyEdges + - FUNCTIONALITY: Adds the blob edges to destination + - PARAMETERS: + - destination: where to add the edges + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlob::CopyEdges(CBlob &destination) const + { + CvSeqReader reader; + CvSeqWriter writer; + CvPoint edgeactual; - /** - - FUNCIÓ: GetEllipse - - FUNCIONALITAT: Retorna l'ellipse que s'ajusta millor a les cantonades del blob - - PARÀMETRES: - - RESULTAT: - - estructura amb l'ellipse - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 25-05-2005. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: GetEllipse - - FUNCTIONALITY: Calculates the ellipse that best fits the edges of the blob - - PARAMETERS: - - RESULT: - - CvBox2D struct with the calculated ellipse - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - CvBox2D CBlob::GetEllipse() const - { - CvBox2D elipse; - // necessitem 6 punts per calcular l'elipse - if (edges != NULL && edges->total > 6) - { - elipse = cvFitEllipse2(edges); - } - else - { - elipse.center.x = 0.0; - elipse.center.y = 0.0; - elipse.size.width = 0.0; - elipse.size.height = 0.0; - elipse.angle = 0.0; - } - return elipse; - } + cvStartReadSeq(edges, &reader); + cvStartAppendToSeq(destination.Edges(), &writer); + for (int i = 0; i < edges->total; i++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + CV_WRITE_SEQ_ELEM(edgeactual, writer); + } + cvEndWriteSeq(&writer); + } - /*************************************************************************** - Implementació de les classes per al cà lcul de caracterÃstiques sobre el blob - - Implementation of the helper classes to perform operations on blobs - **************************************************************************/ - - /** - - FUNCIÓ: Moment - - FUNCIONALITAT: Calcula el moment pq del blob - - RESULTAT: - - retorna el moment pq especificat o 0 si el moment no està implementat - - RESTRICCIONS: - - Implementats els moments pq: 00, 01, 10, 20, 02 - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: Moment - - FUNCTIONALITY: Calculates the pq moment of the blob - - PARAMETERS: - - RESULT: - - returns the pq moment or 0 if the moment it is not implemented - - RESTRICTIONS: - - Currently, only implemented the 00, 01, 10, 20, 02 pq moments - - AUTHOR: Ricard Borrà s - - CREATION DATE: 20-07-2004. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetMoment::operator()(const CBlob &blob) const - { - //Moment 00 - if ((m_p == 0) && (m_q == 0)) - return blob.Area(); + /** + - FUNCIÓ: ClearEdges + - FUNCIONALITAT: Elimina els vèrtexs del blob + - PARÀMETRES: + - RESULTAT: + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: ClearEdges + - FUNCTIONALITY: Delete current blob edges + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + void CBlob::ClearEdges() + { + // Eliminar vèrtexs del blob eliminat + cvClearSeq(edges); + } - //Moment 10 - if ((m_p == 1) && (m_q == 0)) - return blob.SumX(); + /** + - FUNCIÓ: GetConvexHull + - FUNCIONALITAT: Retorna el poligon convex del blob + - PARÀMETRES: + - dst: sequencia on desarem el resultat (no ha d'estar inicialitzada) + - RESULTAT: + - true si tot ha anat bé + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: GetConvexHull + - FUNCTIONALITY: Calculates the convex hull polygon of the blob + - PARAMETERS: + - dst: where to store the result + - RESULT: + - true if no error ocurred + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + bool CBlob::GetConvexHull(CvSeq **dst) const + { + if (edges != NULL && edges->total > 0) + { + *dst = cvConvexHull2(edges, 0, CV_CLOCKWISE, 0); + return true; + } + return false; + } - //Moment 01 - if ((m_p == 0) && (m_q == 1)) - return blob.SumY(); + /** + - FUNCIÓ: GetEllipse + - FUNCIONALITAT: Retorna l'ellipse que s'ajusta millor a les cantonades del blob + - PARÀMETRES: + - RESULTAT: + - estructura amb l'ellipse + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 25-05-2005. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: GetEllipse + - FUNCTIONALITY: Calculates the ellipse that best fits the edges of the blob + - PARAMETERS: + - RESULT: + - CvBox2D struct with the calculated ellipse + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + CvBox2D CBlob::GetEllipse() const + { + CvBox2D elipse; + // necessitem 6 punts per calcular l'elipse + if (edges != NULL && edges->total > 6) + { + elipse = cvFitEllipse2(edges); + } + else + { + elipse.center.x = 0.0; + elipse.center.y = 0.0; + elipse.size.width = 0.0; + elipse.size.height = 0.0; + elipse.angle = 0.0; + } + return elipse; + } - //Moment 20 - if ((m_p == 2) && (m_q == 0)) - return blob.SumXX(); - //Moment 02 - if ((m_p == 0) && (m_q == 2)) - return blob.SumYY(); - return 0; - } + /*************************************************************************** + Implementació de les classes per al cà lcul de caracterÃstiques sobre el blob + + Implementation of the helper classes to perform operations on blobs + **************************************************************************/ + + /** + - FUNCIÓ: Moment + - FUNCIONALITAT: Calcula el moment pq del blob + - RESULTAT: + - retorna el moment pq especificat o 0 si el moment no està implementat + - RESTRICCIONS: + - Implementats els moments pq: 00, 01, 10, 20, 02 + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: Moment + - FUNCTIONALITY: Calculates the pq moment of the blob + - PARAMETERS: + - RESULT: + - returns the pq moment or 0 if the moment it is not implemented + - RESTRICTIONS: + - Currently, only implemented the 00, 01, 10, 20, 02 pq moments + - AUTHOR: Ricard Borrà s + - CREATION DATE: 20-07-2004. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetMoment::operator()(const CBlob &blob) const + { + //Moment 00 + if ((m_p == 0) && (m_q == 0)) + return blob.Area(); - /** - - FUNCIÓ: HullPerimeter - - FUNCIONALITAT: Calcula la longitud del perimetre convex del blob. - Fa servir la funció d'OpenCV cvConvexHull2 per a - calcular el perimetre convex. - - - PARÀMETRES: - - RESULTAT: - - retorna la longitud del perÃmetre convex del blob. Si el blob no té coordenades - associades retorna el perÃmetre normal del blob. - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobGetHullPerimeter - - FUNCTIONALITY: Calculates the convex hull perimeter of the blob - - PARAMETERS: - - RESULT: - - returns the convex hull perimeter of the blob or the perimeter if the - blob edges could not be retrieved - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetHullPerimeter::operator()(const CBlob &blob) const - { - if (blob.Edges() != NULL && blob.Edges()->total > 0) - { - CvSeq *hull = cvConvexHull2(blob.Edges(), 0, CV_CLOCKWISE, 1); - return fabs(cvArcLength(hull, CV_WHOLE_SEQ, 1)); - } - return blob.Perimeter(); - } + //Moment 10 + if ((m_p == 1) && (m_q == 0)) + return blob.SumX(); - double CBlobGetHullArea::operator()(const CBlob &blob) const - { - if (blob.Edges() != NULL && blob.Edges()->total > 0) - { - CvSeq *hull = cvConvexHull2(blob.Edges(), 0, CV_CLOCKWISE, 1); - return fabs(cvContourArea(hull)); - } - return blob.Perimeter(); - } + //Moment 01 + if ((m_p == 0) && (m_q == 1)) + return blob.SumY(); - /** - - FUNCIÓ: MinX_at_MinY - - FUNCIONALITAT: Calcula el valor MinX a MinY. - - PARÀMETRES: - - blob: blob del que volem calcular el valor - - RESULTAT: - - retorna la X minima en la Y minima. - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobGetMinXatMinY - - FUNCTIONALITY: Calculates the minimum X on the minimum Y - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetMinXatMinY::operator()(const CBlob &blob) const - { - double MinX_at_MinY = LONG_MAX; + //Moment 20 + if ((m_p == 2) && (m_q == 0)) + return blob.SumXX(); - CvSeqReader reader; - CvPoint edgeactual; + //Moment 02 + if ((m_p == 0) && (m_q == 2)) + return blob.SumYY(); - cvStartReadSeq(blob.Edges(), &reader); + return 0; + } - for (int j = 0; j < blob.Edges()->total; j++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - if ((edgeactual.y == blob.MinY()) && (edgeactual.x < MinX_at_MinY)) - { - MinX_at_MinY = edgeactual.x; - } - } + /** + - FUNCIÓ: HullPerimeter + - FUNCIONALITAT: Calcula la longitud del perimetre convex del blob. + Fa servir la funció d'OpenCV cvConvexHull2 per a + calcular el perimetre convex. + + - PARÀMETRES: + - RESULTAT: + - retorna la longitud del perÃmetre convex del blob. Si el blob no té coordenades + associades retorna el perÃmetre normal del blob. + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobGetHullPerimeter + - FUNCTIONALITY: Calculates the convex hull perimeter of the blob + - PARAMETERS: + - RESULT: + - returns the convex hull perimeter of the blob or the perimeter if the + blob edges could not be retrieved + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetHullPerimeter::operator()(const CBlob &blob) const + { + if (blob.Edges() != NULL && blob.Edges()->total > 0) + { + CvSeq *hull = cvConvexHull2(blob.Edges(), 0, CV_CLOCKWISE, 1); + return fabs(cvArcLength(hull, CV_WHOLE_SEQ, 1)); + } + return blob.Perimeter(); + } - return MinX_at_MinY; - } + double CBlobGetHullArea::operator()(const CBlob &blob) const + { + if (blob.Edges() != NULL && blob.Edges()->total > 0) + { + CvSeq *hull = cvConvexHull2(blob.Edges(), 0, CV_CLOCKWISE, 1); + return fabs(cvContourArea(hull)); + } + return blob.Perimeter(); + } - /** - - FUNCIÓ: MinY_at_MaxX - - FUNCIONALITAT: Calcula el valor MinX a MaxX. - - PARÀMETRES: - - blob: blob del que volem calcular el valor - - RESULTAT: - - retorna la Y minima en la X maxima. - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobGetMinXatMinY - - FUNCTIONALITY: Calculates the minimum Y on the maximum X - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetMinYatMaxX::operator()(const CBlob &blob) const - { - double MinY_at_MaxX = LONG_MAX; + /** + - FUNCIÓ: MinX_at_MinY + - FUNCIONALITAT: Calcula el valor MinX a MinY. + - PARÀMETRES: + - blob: blob del que volem calcular el valor + - RESULTAT: + - retorna la X minima en la Y minima. + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobGetMinXatMinY + - FUNCTIONALITY: Calculates the minimum X on the minimum Y + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetMinXatMinY::operator()(const CBlob &blob) const + { + double MinX_at_MinY = LONG_MAX; - CvSeqReader reader; - CvPoint edgeactual; + CvSeqReader reader; + CvPoint edgeactual; - cvStartReadSeq(blob.Edges(), &reader); + cvStartReadSeq(blob.Edges(), &reader); - for (int j = 0; j < blob.Edges()->total; j++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - if ((edgeactual.x == blob.MaxX()) && (edgeactual.y < MinY_at_MaxX)) - { - MinY_at_MaxX = edgeactual.y; - } - } + for (int j = 0; j < blob.Edges()->total; j++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + if ((edgeactual.y == blob.MinY()) && (edgeactual.x < MinX_at_MinY)) + { + MinX_at_MinY = edgeactual.x; + } + } - return MinY_at_MaxX; - } + return MinX_at_MinY; + } - /** - - FUNCIÓ: MaxX_at_MaxY - - FUNCIONALITAT: Calcula el valor MaxX a MaxY. - - PARÀMETRES: - - blob: blob del que volem calcular el valor - - RESULTAT: - - retorna la X maxima en la Y maxima. - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobGetMaxXatMaxY - - FUNCTIONALITY: Calculates the maximum X on the maximum Y - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetMaxXatMaxY::operator()(const CBlob &blob) const - { - double MaxX_at_MaxY = LONG_MIN; + /** + - FUNCIÓ: MinY_at_MaxX + - FUNCIONALITAT: Calcula el valor MinX a MaxX. + - PARÀMETRES: + - blob: blob del que volem calcular el valor + - RESULTAT: + - retorna la Y minima en la X maxima. + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobGetMinXatMinY + - FUNCTIONALITY: Calculates the minimum Y on the maximum X + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetMinYatMaxX::operator()(const CBlob &blob) const + { + double MinY_at_MaxX = LONG_MAX; - CvSeqReader reader; - CvPoint edgeactual; + CvSeqReader reader; + CvPoint edgeactual; - cvStartReadSeq(blob.Edges(), &reader); + cvStartReadSeq(blob.Edges(), &reader); - for (int j = 0; j < blob.Edges()->total; j++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - if ((edgeactual.y == blob.MaxY()) && (edgeactual.x > MaxX_at_MaxY)) - { - MaxX_at_MaxY = edgeactual.x; - } - } + for (int j = 0; j < blob.Edges()->total; j++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + if ((edgeactual.x == blob.MaxX()) && (edgeactual.y < MinY_at_MaxX)) + { + MinY_at_MaxX = edgeactual.y; + } + } - return MaxX_at_MaxY; - } + return MinY_at_MaxX; + } - /** - - FUNCIÓ: MaxY_at_MinX - - FUNCIONALITAT: Calcula el valor MaxY a MinX. - - PARÀMETRES: - - blob: blob del que volem calcular el valor - - RESULTAT: - - retorna la Y maxima en la X minima. - - RESTRICCIONS: - - AUTOR: Ricard Borrà s - - DATA DE CREACIÓ: 20-07-2004. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: CBlobGetMaxYatMinX - - FUNCTIONALITY: Calculates the maximum Y on the minimum X - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetMaxYatMinX::operator()(const CBlob &blob) const - { - double MaxY_at_MinX = LONG_MIN; + /** + - FUNCIÓ: MaxX_at_MaxY + - FUNCIONALITAT: Calcula el valor MaxX a MaxY. + - PARÀMETRES: + - blob: blob del que volem calcular el valor + - RESULTAT: + - retorna la X maxima en la Y maxima. + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobGetMaxXatMaxY + - FUNCTIONALITY: Calculates the maximum X on the maximum Y + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetMaxXatMaxY::operator()(const CBlob &blob) const + { + double MaxX_at_MaxY = LONG_MIN; - CvSeqReader reader; - CvPoint edgeactual; + CvSeqReader reader; + CvPoint edgeactual; - cvStartReadSeq(blob.Edges(), &reader); + cvStartReadSeq(blob.Edges(), &reader); - for (int j = 0; j < blob.Edges()->total; j++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - if ((edgeactual.x == blob.MinY()) && (edgeactual.y > MaxY_at_MinX)) - { - MaxY_at_MinX = edgeactual.y; - } - } + for (int j = 0; j < blob.Edges()->total; j++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + if ((edgeactual.y == blob.MaxY()) && (edgeactual.x > MaxX_at_MaxY)) + { + MaxX_at_MaxY = edgeactual.x; + } + } - return MaxY_at_MinX; - } + return MaxX_at_MaxY; + } - /** - Retorna l'elongació del blob (longitud/amplada) - */ - /** - - FUNCTION: CBlobGetElongation - - FUNCTIONALITY: Calculates the elongation of the blob ( length/breadth ) - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - See below to see how the lenght and the breadth are aproximated - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetElongation::operator()(const CBlob &blob) const - { - double ampladaC, longitudC, amplada, longitud; + /** + - FUNCIÓ: MaxY_at_MinX + - FUNCIONALITAT: Calcula el valor MaxY a MinX. + - PARÀMETRES: + - blob: blob del que volem calcular el valor + - RESULTAT: + - retorna la Y maxima en la X minima. + - RESTRICCIONS: + - AUTOR: Ricard Borrà s + - DATA DE CREACIÓ: 20-07-2004. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: CBlobGetMaxYatMinX + - FUNCTIONALITY: Calculates the maximum Y on the minimum X + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetMaxYatMinX::operator()(const CBlob &blob) const + { + double MaxY_at_MinX = LONG_MIN; - ampladaC = (double)(blob.Perimeter() + sqrt(pow(blob.Perimeter(), 2) - 16 * blob.Area())) / 4; - if (ampladaC <= 0.0) return 0; - longitudC = (double)blob.Area() / ampladaC; + CvSeqReader reader; + CvPoint edgeactual; - longitud = MAX(longitudC, ampladaC); - amplada = MIN(longitudC, ampladaC); + cvStartReadSeq(blob.Edges(), &reader); - return (double)longitud / amplada; - } + for (int j = 0; j < blob.Edges()->total; j++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + if ((edgeactual.x == blob.MinY()) && (edgeactual.y > MaxY_at_MinX)) + { + MaxY_at_MinX = edgeactual.y; + } + } - /** - Retorna la compacitat del blob - */ - /** - - FUNCTION: CBlobGetCompactness - - FUNCTIONALITY: Calculates the compactness of the blob - ( maximum for circle shaped blobs, minimum for the rest) - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetCompactness::operator()(const CBlob &blob) const - { - if (blob.Area() != 0.0) - return (double)pow(blob.Perimeter(), 2) / (4 * CV_PI*blob.Area()); - else - return 0.0; - } + return MaxY_at_MinX; + } - /** - Retorna la rugositat del blob - */ - /** - - FUNCTION: CBlobGetRoughness - - FUNCTIONALITY: Calculates the roughness of the blob - ( ratio between perimeter and convex hull perimeter) - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetRoughness::operator()(const CBlob &blob) const - { - CBlobGetHullPerimeter getHullPerimeter = CBlobGetHullPerimeter(); + /** + Retorna l'elongació del blob (longitud/amplada) + */ + /** + - FUNCTION: CBlobGetElongation + - FUNCTIONALITY: Calculates the elongation of the blob ( length/breadth ) + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - See below to see how the lenght and the breadth are aproximated + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetElongation::operator()(const CBlob &blob) const + { + double ampladaC, longitudC, amplada, longitud; - double hullPerimeter = getHullPerimeter(blob); + ampladaC = (double)(blob.Perimeter() + sqrt(pow(blob.Perimeter(), 2) - 16 * blob.Area())) / 4; + if (ampladaC <= 0.0) return 0; + longitudC = (double)blob.Area() / ampladaC; - if (hullPerimeter != 0.0) - return blob.Perimeter() / hullPerimeter;//HullPerimeter(); + longitud = MAX(longitudC, ampladaC); + amplada = MIN(longitudC, ampladaC); - return 0.0; - } + return (double)longitud / amplada; + } - /** - Retorna la longitud del blob - */ - /** - - FUNCTION: CBlobGetLength - - FUNCTIONALITY: Calculates the lenght of the blob (the biggest axis of the blob) - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - The lenght is an aproximation to the real lenght - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetLength::operator()(const CBlob &blob) const - { - double ampladaC, longitudC; - double tmp; + /** + Retorna la compacitat del blob + */ + /** + - FUNCTION: CBlobGetCompactness + - FUNCTIONALITY: Calculates the compactness of the blob + ( maximum for circle shaped blobs, minimum for the rest) + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetCompactness::operator()(const CBlob &blob) const + { + if (blob.Area() != 0.0) + return (double)pow(blob.Perimeter(), 2) / (4 * CV_PI*blob.Area()); + else + return 0.0; + } - tmp = blob.Perimeter()*blob.Perimeter() - 16 * blob.Area(); + /** + Retorna la rugositat del blob + */ + /** + - FUNCTION: CBlobGetRoughness + - FUNCTIONALITY: Calculates the roughness of the blob + ( ratio between perimeter and convex hull perimeter) + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetRoughness::operator()(const CBlob &blob) const + { + CBlobGetHullPerimeter getHullPerimeter = CBlobGetHullPerimeter(); - if (tmp > 0.0) - ampladaC = (double)(blob.Perimeter() + sqrt(tmp)) / 4; - // error intrÃnsec en els cà lculs de l'à rea i el perÃmetre - else - ampladaC = (double)(blob.Perimeter()) / 4; + double hullPerimeter = getHullPerimeter(blob); - if (ampladaC <= 0.0) return 0; - longitudC = (double)blob.Area() / ampladaC; + if (hullPerimeter != 0.0) + return blob.Perimeter() / hullPerimeter;//HullPerimeter(); - return MAX(longitudC, ampladaC); - } + return 0.0; + } - /** - Retorna l'amplada del blob - */ - /** - - FUNCTION: CBlobGetBreadth - - FUNCTIONALITY: Calculates the breadth of the blob (the smallest axis of the blob) - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - The breadth is an aproximation to the real breadth - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetBreadth::operator()(const CBlob &blob) const - { - double ampladaC, longitudC; - double tmp; + /** + Retorna la longitud del blob + */ + /** + - FUNCTION: CBlobGetLength + - FUNCTIONALITY: Calculates the lenght of the blob (the biggest axis of the blob) + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - The lenght is an aproximation to the real lenght + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetLength::operator()(const CBlob &blob) const + { + double ampladaC, longitudC; + double tmp; - tmp = blob.Perimeter()*blob.Perimeter() - 16 * blob.Area(); + tmp = blob.Perimeter()*blob.Perimeter() - 16 * blob.Area(); - if (tmp > 0.0) - ampladaC = (double)(blob.Perimeter() + sqrt(tmp)) / 4; - // error intrÃnsec en els cà lculs de l'à rea i el perÃmetre - else - ampladaC = (double)(blob.Perimeter()) / 4; + if (tmp > 0.0) + ampladaC = (double)(blob.Perimeter() + sqrt(tmp)) / 4; + // error intrÃnsec en els cà lculs de l'à rea i el perÃmetre + else + ampladaC = (double)(blob.Perimeter()) / 4; - if (ampladaC <= 0.0) return 0; - longitudC = (double)blob.Area() / ampladaC; + if (ampladaC <= 0.0) return 0; + longitudC = (double)blob.Area() / ampladaC; - return MIN(longitudC, ampladaC); - } + return MAX(longitudC, ampladaC); + } - /** - Calcula la distà ncia entre un punt i el centre del blob - */ - /** - - FUNCTION: CBlobGetDistanceFromPoint - - FUNCTIONALITY: Calculates the euclidean distance between the blob center and - the point specified in the constructor - - PARAMETERS: - - RESULT: - - RESTRICTIONS: - - AUTHOR: Ricard Borrà s - - CREATION DATE: 25-05-2005. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetDistanceFromPoint::operator()(const CBlob &blob) const - { - double xmitjana, ymitjana; - CBlobGetXCenter getXCenter; - CBlobGetYCenter getYCenter; + /** + Retorna l'amplada del blob + */ + /** + - FUNCTION: CBlobGetBreadth + - FUNCTIONALITY: Calculates the breadth of the blob (the smallest axis of the blob) + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - The breadth is an aproximation to the real breadth + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetBreadth::operator()(const CBlob &blob) const + { + double ampladaC, longitudC; + double tmp; - xmitjana = m_x - getXCenter(blob); - ymitjana = m_y - getYCenter(blob); + tmp = blob.Perimeter()*blob.Perimeter() - 16 * blob.Area(); - return sqrt((xmitjana*xmitjana) + (ymitjana*ymitjana)); - } + if (tmp > 0.0) + ampladaC = (double)(blob.Perimeter() + sqrt(tmp)) / 4; + // error intrÃnsec en els cà lculs de l'à rea i el perÃmetre + else + ampladaC = (double)(blob.Perimeter()) / 4; - /** - - FUNCIÓ: BlobGetXYInside - - FUNCIONALITAT: Calcula si un punt cau dins de la capsa rectangular - del blob - - RESULTAT: - - retorna 1 si hi està ; 0 si no - - RESTRICCIONS: - - AUTOR: Francesc Pinyol Margalef - - DATA DE CREACIÓ: 16-01-2006. - - MODIFICACIÓ: Data. Autor. Descripció. - */ - /** - - FUNCTION: BlobGetXYInside - - FUNCTIONALITY: Calculates whether a point is inside the - rectangular bounding box of a blob - - PARAMETERS: - - RESULT: - - returns 1 if it is inside; o if not - - RESTRICTIONS: - - AUTHOR: Francesc Pinyol Margalef - - CREATION DATE: 16-01-2006. - - MODIFICATION: Date. Author. Description. - */ - double CBlobGetXYInside::operator()(const CBlob &blob) const - { - if (blob.Edges() == NULL || blob.Edges()->total == 0) return 0.0; + if (ampladaC <= 0.0) return 0; + longitudC = (double)blob.Area() / ampladaC; - // passem els punts del blob a un vector de punts de les STL - CvSeqReader reader; - CBlob::vectorPunts vectorEdges; - CBlob::vectorPunts::iterator itEdges, itEdgesSeguent; - CvPoint edgeactual; - bool dinsBlob; + return MIN(longitudC, ampladaC); + } - // agafem tots els punts amb la mateixa y que l'actual - cvStartReadSeq(blob.Edges(), &reader); + /** + Calcula la distà ncia entre un punt i el centre del blob + */ + /** + - FUNCTION: CBlobGetDistanceFromPoint + - FUNCTIONALITY: Calculates the euclidean distance between the blob center and + the point specified in the constructor + - PARAMETERS: + - RESULT: + - RESTRICTIONS: + - AUTHOR: Ricard Borrà s + - CREATION DATE: 25-05-2005. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetDistanceFromPoint::operator()(const CBlob &blob) const + { + double xmitjana, ymitjana; + CBlobGetXCenter getXCenter; + CBlobGetYCenter getYCenter; - for (int i = 0; i < blob.Edges()->total; i++) - { - CV_READ_SEQ_ELEM(edgeactual, reader); - if (edgeactual.y == m_p.y) - vectorEdges.push_back(edgeactual); - } + xmitjana = m_x - getXCenter(blob); + ymitjana = m_y - getYCenter(blob); - if (vectorEdges.empty()) return 0.0; + return sqrt((xmitjana*xmitjana) + (ymitjana*ymitjana)); + } - // ordenem el vector per les Y's i les X's d'esquerra a dreta - std::sort(vectorEdges.begin(), vectorEdges.end(), CBlob::comparaCvPoint()); + /** + - FUNCIÓ: BlobGetXYInside + - FUNCIONALITAT: Calcula si un punt cau dins de la capsa rectangular + del blob + - RESULTAT: + - retorna 1 si hi està ; 0 si no + - RESTRICCIONS: + - AUTOR: Francesc Pinyol Margalef + - DATA DE CREACIÓ: 16-01-2006. + - MODIFICACIÓ: Data. Autor. Descripció. + */ + /** + - FUNCTION: BlobGetXYInside + - FUNCTIONALITY: Calculates whether a point is inside the + rectangular bounding box of a blob + - PARAMETERS: + - RESULT: + - returns 1 if it is inside; o if not + - RESTRICTIONS: + - AUTHOR: Francesc Pinyol Margalef + - CREATION DATE: 16-01-2006. + - MODIFICATION: Date. Author. Description. + */ + double CBlobGetXYInside::operator()(const CBlob &blob) const + { + if (blob.Edges() == NULL || blob.Edges()->total == 0) return 0.0; + + // passem els punts del blob a un vector de punts de les STL + CvSeqReader reader; + CBlob::vectorPunts vectorEdges; + CBlob::vectorPunts::iterator itEdges, itEdgesSeguent; + CvPoint edgeactual; + bool dinsBlob; + + // agafem tots els punts amb la mateixa y que l'actual + cvStartReadSeq(blob.Edges(), &reader); + + for (int i = 0; i < blob.Edges()->total; i++) + { + CV_READ_SEQ_ELEM(edgeactual, reader); + if (edgeactual.y == m_p.y) + vectorEdges.push_back(edgeactual); + } + + if (vectorEdges.empty()) return 0.0; + + // ordenem el vector per les Y's i les X's d'esquerra a dreta + std::sort(vectorEdges.begin(), vectorEdges.end(), CBlob::comparaCvPoint()); + + // recorrem el punts del blob de la mateixa fila que el punt d'entrada + // i mirem si la X del punt d'entrada està entre dos coordenades "plenes" + // del blob + itEdges = vectorEdges.begin(); + itEdgesSeguent = vectorEdges.begin() + 1; + dinsBlob = true; + + while (itEdges != (vectorEdges.end() - 1)) + { + if ((*itEdges).x <= m_p.x && (*itEdgesSeguent).x >= m_p.x && dinsBlob) + { + vectorEdges.clear(); + return 1.0; + } + + ++itEdges; + ++itEdgesSeguent; + dinsBlob = !dinsBlob; + } + + vectorEdges.clear(); + return 0.0; + } - // recorrem el punts del blob de la mateixa fila que el punt d'entrada - // i mirem si la X del punt d'entrada està entre dos coordenades "plenes" - // del blob - itEdges = vectorEdges.begin(); - itEdgesSeguent = vectorEdges.begin() + 1; - dinsBlob = true; + #ifdef BLOB_OBJECT_FACTORY + /** + - FUNCIÓ: RegistraTotsOperadors + - FUNCIONALITAT: Registrar tots els operadors definits a blob.h + - PARÀMETRES: + - fabricaOperadorsBlob: fà brica on es registraran els operadors + - RESULTAT: + - Modifica l'objecte fabricaOperadorsBlob + - RESTRICCIONS: + - Només es registraran els operadors de blob.h. Si se'n volen afegir, cal afegir-los amb + el mètode Register de la fà brica. + - AUTOR: rborras + - DATA DE CREACIÓ: 2006/05/18 + - MODIFICACIÓ: Data. Autor. Descripció. + */ + void RegistraTotsOperadors(t_OperadorBlobFactory &fabricaOperadorsBlob) + { + // blob shape + fabricaOperadorsBlob.Register(CBlobGetArea().GetNom(), Type2Type<CBlobGetArea>()); + fabricaOperadorsBlob.Register(CBlobGetBreadth().GetNom(), Type2Type<CBlobGetBreadth>()); + fabricaOperadorsBlob.Register(CBlobGetCompactness().GetNom(), Type2Type<CBlobGetCompactness>()); + fabricaOperadorsBlob.Register(CBlobGetElongation().GetNom(), Type2Type<CBlobGetElongation>()); + fabricaOperadorsBlob.Register(CBlobGetExterior().GetNom(), Type2Type<CBlobGetExterior>()); + fabricaOperadorsBlob.Register(CBlobGetLength().GetNom(), Type2Type<CBlobGetLength>()); + fabricaOperadorsBlob.Register(CBlobGetPerimeter().GetNom(), Type2Type<CBlobGetPerimeter>()); + fabricaOperadorsBlob.Register(CBlobGetRoughness().GetNom(), Type2Type<CBlobGetRoughness>()); + + // extern pixels + fabricaOperadorsBlob.Register(CBlobGetExternPerimeterRatio().GetNom(), Type2Type<CBlobGetExternPerimeterRatio>()); + fabricaOperadorsBlob.Register(CBlobGetExternHullPerimeterRatio().GetNom(), Type2Type<CBlobGetExternHullPerimeterRatio>()); + fabricaOperadorsBlob.Register(CBlobGetExternPerimeter().GetNom(), Type2Type<CBlobGetExternPerimeter>()); + + + // hull + fabricaOperadorsBlob.Register(CBlobGetHullPerimeter().GetNom(), Type2Type<CBlobGetHullPerimeter>()); + fabricaOperadorsBlob.Register(CBlobGetHullArea().GetNom(), Type2Type<CBlobGetHullArea>()); + + + // elipse info + fabricaOperadorsBlob.Register(CBlobGetMajorAxisLength().GetNom(), Type2Type<CBlobGetMajorAxisLength>()); + fabricaOperadorsBlob.Register(CBlobGetMinorAxisLength().GetNom(), Type2Type<CBlobGetMinorAxisLength>()); + fabricaOperadorsBlob.Register(CBlobGetAxisRatio().GetNom(), Type2Type<CBlobGetAxisRatio>()); + fabricaOperadorsBlob.Register(CBlobGetOrientation().GetNom(), Type2Type<CBlobGetOrientation>()); + fabricaOperadorsBlob.Register(CBlobGetOrientationCos().GetNom(), Type2Type<CBlobGetOrientationCos>()); + fabricaOperadorsBlob.Register(CBlobGetAreaElipseRatio().GetNom(), Type2Type<CBlobGetAreaElipseRatio>()); + + // min an max + fabricaOperadorsBlob.Register(CBlobGetMaxX().GetNom(), Type2Type<CBlobGetMaxX>()); + fabricaOperadorsBlob.Register(CBlobGetMaxY().GetNom(), Type2Type<CBlobGetMaxY>()); + fabricaOperadorsBlob.Register(CBlobGetMinX().GetNom(), Type2Type<CBlobGetMinX>()); + fabricaOperadorsBlob.Register(CBlobGetMinY().GetNom(), Type2Type<CBlobGetMinY>()); + + fabricaOperadorsBlob.Register(CBlobGetMaxXatMaxY().GetNom(), Type2Type<CBlobGetMaxXatMaxY>()); + fabricaOperadorsBlob.Register(CBlobGetMaxYatMinX().GetNom(), Type2Type<CBlobGetMaxYatMinX>()); + fabricaOperadorsBlob.Register(CBlobGetMinXatMinY().GetNom(), Type2Type<CBlobGetMinXatMinY>()); + fabricaOperadorsBlob.Register(CBlobGetMinYatMaxX().GetNom(), Type2Type<CBlobGetMinYatMaxX>()); + + // grey level stats + fabricaOperadorsBlob.Register(CBlobGetMean().GetNom(), Type2Type<CBlobGetMean>()); + fabricaOperadorsBlob.Register(CBlobGetStdDev().GetNom(), Type2Type<CBlobGetStdDev>()); + + // coordinate info + fabricaOperadorsBlob.Register(CBlobGetXYInside().GetNom(), Type2Type<CBlobGetXYInside>()); + fabricaOperadorsBlob.Register(CBlobGetDiffY().GetNom(), Type2Type<CBlobGetDiffY>()); + fabricaOperadorsBlob.Register(CBlobGetDiffX().GetNom(), Type2Type<CBlobGetDiffX>()); + fabricaOperadorsBlob.Register(CBlobGetXCenter().GetNom(), Type2Type<CBlobGetXCenter>()); + fabricaOperadorsBlob.Register(CBlobGetYCenter().GetNom(), Type2Type<CBlobGetYCenter>()); + fabricaOperadorsBlob.Register(CBlobGetDistanceFromPoint().GetNom(), Type2Type<CBlobGetDistanceFromPoint>()); + + // moments + fabricaOperadorsBlob.Register(CBlobGetMoment().GetNom(), Type2Type<CBlobGetMoment>()); - while (itEdges != (vectorEdges.end() - 1)) - { - if ((*itEdges).x <= m_p.x && (*itEdgesSeguent).x >= m_p.x && dinsBlob) - { - vectorEdges.clear(); - return 1.0; + } + #endif } - - ++itEdges; - ++itEdgesSeguent; - dinsBlob = !dinsBlob; } - - vectorEdges.clear(); - return 0.0; - } - -#ifdef BLOB_OBJECT_FACTORY - - /** - - FUNCIÓ: RegistraTotsOperadors - - FUNCIONALITAT: Registrar tots els operadors definits a blob.h - - PARÀMETRES: - - fabricaOperadorsBlob: fà brica on es registraran els operadors - - RESULTAT: - - Modifica l'objecte fabricaOperadorsBlob - - RESTRICCIONS: - - Només es registraran els operadors de blob.h. Si se'n volen afegir, cal afegir-los amb - el mètode Register de la fà brica. - - AUTOR: rborras - - DATA DE CREACIÓ: 2006/05/18 - - MODIFICACIÓ: Data. Autor. Descripció. - */ - void RegistraTotsOperadors(t_OperadorBlobFactory &fabricaOperadorsBlob) - { - // blob shape - fabricaOperadorsBlob.Register(CBlobGetArea().GetNom(), Type2Type<CBlobGetArea>()); - fabricaOperadorsBlob.Register(CBlobGetBreadth().GetNom(), Type2Type<CBlobGetBreadth>()); - fabricaOperadorsBlob.Register(CBlobGetCompactness().GetNom(), Type2Type<CBlobGetCompactness>()); - fabricaOperadorsBlob.Register(CBlobGetElongation().GetNom(), Type2Type<CBlobGetElongation>()); - fabricaOperadorsBlob.Register(CBlobGetExterior().GetNom(), Type2Type<CBlobGetExterior>()); - fabricaOperadorsBlob.Register(CBlobGetLength().GetNom(), Type2Type<CBlobGetLength>()); - fabricaOperadorsBlob.Register(CBlobGetPerimeter().GetNom(), Type2Type<CBlobGetPerimeter>()); - fabricaOperadorsBlob.Register(CBlobGetRoughness().GetNom(), Type2Type<CBlobGetRoughness>()); - - // extern pixels - fabricaOperadorsBlob.Register(CBlobGetExternPerimeterRatio().GetNom(), Type2Type<CBlobGetExternPerimeterRatio>()); - fabricaOperadorsBlob.Register(CBlobGetExternHullPerimeterRatio().GetNom(), Type2Type<CBlobGetExternHullPerimeterRatio>()); - fabricaOperadorsBlob.Register(CBlobGetExternPerimeter().GetNom(), Type2Type<CBlobGetExternPerimeter>()); - - - // hull - fabricaOperadorsBlob.Register(CBlobGetHullPerimeter().GetNom(), Type2Type<CBlobGetHullPerimeter>()); - fabricaOperadorsBlob.Register(CBlobGetHullArea().GetNom(), Type2Type<CBlobGetHullArea>()); - - - // elipse info - fabricaOperadorsBlob.Register(CBlobGetMajorAxisLength().GetNom(), Type2Type<CBlobGetMajorAxisLength>()); - fabricaOperadorsBlob.Register(CBlobGetMinorAxisLength().GetNom(), Type2Type<CBlobGetMinorAxisLength>()); - fabricaOperadorsBlob.Register(CBlobGetAxisRatio().GetNom(), Type2Type<CBlobGetAxisRatio>()); - fabricaOperadorsBlob.Register(CBlobGetOrientation().GetNom(), Type2Type<CBlobGetOrientation>()); - fabricaOperadorsBlob.Register(CBlobGetOrientationCos().GetNom(), Type2Type<CBlobGetOrientationCos>()); - fabricaOperadorsBlob.Register(CBlobGetAreaElipseRatio().GetNom(), Type2Type<CBlobGetAreaElipseRatio>()); - - // min an max - fabricaOperadorsBlob.Register(CBlobGetMaxX().GetNom(), Type2Type<CBlobGetMaxX>()); - fabricaOperadorsBlob.Register(CBlobGetMaxY().GetNom(), Type2Type<CBlobGetMaxY>()); - fabricaOperadorsBlob.Register(CBlobGetMinX().GetNom(), Type2Type<CBlobGetMinX>()); - fabricaOperadorsBlob.Register(CBlobGetMinY().GetNom(), Type2Type<CBlobGetMinY>()); - - fabricaOperadorsBlob.Register(CBlobGetMaxXatMaxY().GetNom(), Type2Type<CBlobGetMaxXatMaxY>()); - fabricaOperadorsBlob.Register(CBlobGetMaxYatMinX().GetNom(), Type2Type<CBlobGetMaxYatMinX>()); - fabricaOperadorsBlob.Register(CBlobGetMinXatMinY().GetNom(), Type2Type<CBlobGetMinXatMinY>()); - fabricaOperadorsBlob.Register(CBlobGetMinYatMaxX().GetNom(), Type2Type<CBlobGetMinYatMaxX>()); - - // grey level stats - fabricaOperadorsBlob.Register(CBlobGetMean().GetNom(), Type2Type<CBlobGetMean>()); - fabricaOperadorsBlob.Register(CBlobGetStdDev().GetNom(), Type2Type<CBlobGetStdDev>()); - - // coordinate info - fabricaOperadorsBlob.Register(CBlobGetXYInside().GetNom(), Type2Type<CBlobGetXYInside>()); - fabricaOperadorsBlob.Register(CBlobGetDiffY().GetNom(), Type2Type<CBlobGetDiffY>()); - fabricaOperadorsBlob.Register(CBlobGetDiffX().GetNom(), Type2Type<CBlobGetDiffX>()); - fabricaOperadorsBlob.Register(CBlobGetXCenter().GetNom(), Type2Type<CBlobGetXCenter>()); - fabricaOperadorsBlob.Register(CBlobGetYCenter().GetNom(), Type2Type<CBlobGetYCenter>()); - fabricaOperadorsBlob.Register(CBlobGetDistanceFromPoint().GetNom(), Type2Type<CBlobGetDistanceFromPoint>()); - - // moments - fabricaOperadorsBlob.Register(CBlobGetMoment().GetNom(), Type2Type<CBlobGetMoment>()); - } - -#endif - } - diff --git a/src/algorithms/MultiLayer/blob.h b/src/algorithms/MultiLayer/blob.h index acad398e188d643eb71c6c6915938a176f5fb71e..6cce6c8ec3b8d9e539b7a27e7fde2e9cf535f333 100644 --- a/src/algorithms/MultiLayer/blob.h +++ b/src/algorithms/MultiLayer/blob.h @@ -8,777 +8,785 @@ // opencv legacy includes #include "OpenCvLegacyIncludes.h" -//! Factor de conversió de graus a radians -#define DEGREE2RAD (CV_PI / 180.0) - -namespace Blob +namespace bgslibrary { - /** - Classe que representa un blob, entés com un conjunt de pixels del - mateix color contigus en una imatge binaritzada. - - Class to represent a blob, a group of connected pixels in a binary image - */ - class CBlob + namespace algorithms { - public: - //! Constructor està ndard - //! Standard constructor - CBlob(); - //! Constructor de còpia - //! Copy constructor - CBlob(const CBlob &src); - CBlob(const CBlob *src); - - //! Destructor està ndard - //! Standard Destructor - ~CBlob(); - - //! Operador d'assignació - //! Assigment operator - CBlob& operator=(const CBlob &src); - - //! Indica si el blob està buit ( no té cap info associada ) - //! Shows if the blob has associated information - bool IsEmpty() const + namespace multilayer { - return (area == 0.0 && perimeter == 0.0); - }; - - //! Neteja les cantonades del blob - //! Clears the edges of the blob - void ClearEdges(); - //! Copia les cantonades del blob a un altre (les afegeix al destÃ) - //! Adds the blob edges to another blob - void CopyEdges(CBlob &destination) const; - //! Retorna el poligon convex del blob - //! Calculates the convex hull of the blob - bool GetConvexHull(CvSeq **dst) const; - //! Calcula l'elipse que s'adapta als vèrtexs del blob - //! Fits an ellipse to the blob edges - CvBox2D GetEllipse() const; - - //! Pinta l'interior d'un blob d'un color determinat - //! Paints the blob in an image - void FillBlob(IplImage *imatge, CvScalar color, int offsetX = 0, int offsetY = 0) const; - - //! Funcions GET sobre els valors dels blobs - //! Get functions - - inline int Label() const { return etiqueta; } - inline int Parent() const { return parent; } - inline double Area() const { return area; } - inline double Perimeter() const { return perimeter; } - inline double ExternPerimeter() const { return externPerimeter; } - inline int Exterior() const { return exterior; } - inline double Mean() const { return mean; } - inline double StdDev() const { return stddev; } - inline double MinX() const { return minx; } - inline double MinY() const { return miny; } - inline double MaxX() const { return maxx; } - inline double MaxY() const { return maxy; } - inline CvSeq *Edges() const { return edges; } - inline double SumX() const { return sumx; } - inline double SumY() const { return sumy; } - inline double SumXX() const { return sumxx; } - inline double SumYY() const { return sumyy; } - inline double SumXY() const { return sumxy; } - - //! etiqueta del blob - //! label of the blob - int etiqueta; - //! flag per indicar si es exterior o no - //! true for extern blobs - int exterior; - //! area del blob - //! Blob area - double area; - //! perimetre del blob - //! Blob perimeter - double perimeter; - //! quantitat de perimetre del blob extern - //! amount of blob perimeter which is exterior - double externPerimeter; - //! etiqueta del blob pare - //! label of the parent blob - int parent; - //! moments - double sumx; - double sumy; - double sumxx; - double sumyy; - double sumxy; - //! Bounding rect - double minx; - double maxx; - double miny; - double maxy; - - //! mitjana - //! mean of the grey scale values of the blob pixels - double mean; - //! desviació standard - //! standard deviation of the grey scale values of the blob pixels - double stddev; - - //! à rea de memòria on es desaran els punts de contorn del blob - //! storage which contains the edges of the blob - CvMemStorage *m_storage; - //! Sequència de punts del contorn del blob - //! Sequence with the edges of the blob - CvSeq *edges; - - - //! Point datatype for plotting (FillBlob) - typedef std::vector<CvPoint> vectorPunts; - - //! Helper class to compare two CvPoints (for sorting in FillBlob) - struct comparaCvPoint : public std::binary_function<CvPoint, CvPoint, bool> - { - //! Definim que un punt és menor com més amunt a la dreta estigui - bool operator()(CvPoint a, CvPoint b) + namespace blob { - if (a.y == b.y) - return a.x < b.x; - else - return a.y < b.y; + //! Factor de conversió de graus a radians + const double DEGREE2RAD = (CV_PI / 180.0); + + /** + Classe que representa un blob, entés com un conjunt de pixels del + mateix color contigus en una imatge binaritzada. + + Class to represent a blob, a group of connected pixels in a binary image + */ + class CBlob + { + public: + //! Constructor està ndard + //! Standard constructor + CBlob(); + //! Constructor de còpia + //! Copy constructor + CBlob(const CBlob &src); + CBlob(const CBlob *src); + + //! Destructor està ndard + //! Standard Destructor + ~CBlob(); + + //! Operador d'assignació + //! Assigment operator + CBlob& operator=(const CBlob &src); + + //! Indica si el blob està buit ( no té cap info associada ) + //! Shows if the blob has associated information + bool IsEmpty() const + { + return (area == 0.0 && perimeter == 0.0); + }; + + //! Neteja les cantonades del blob + //! Clears the edges of the blob + void ClearEdges(); + //! Copia les cantonades del blob a un altre (les afegeix al destÃ) + //! Adds the blob edges to another blob + void CopyEdges(CBlob &destination) const; + //! Retorna el poligon convex del blob + //! Calculates the convex hull of the blob + bool GetConvexHull(CvSeq **dst) const; + //! Calcula l'elipse que s'adapta als vèrtexs del blob + //! Fits an ellipse to the blob edges + CvBox2D GetEllipse() const; + + //! Pinta l'interior d'un blob d'un color determinat + //! Paints the blob in an image + void FillBlob(IplImage *imatge, CvScalar color, int offsetX = 0, int offsetY = 0) const; + + //! Funcions GET sobre els valors dels blobs + //! Get functions + + inline int Label() const { return etiqueta; } + inline int Parent() const { return parent; } + inline double Area() const { return area; } + inline double Perimeter() const { return perimeter; } + inline double ExternPerimeter() const { return externPerimeter; } + inline int Exterior() const { return exterior; } + inline double Mean() const { return mean; } + inline double StdDev() const { return stddev; } + inline double MinX() const { return minx; } + inline double MinY() const { return miny; } + inline double MaxX() const { return maxx; } + inline double MaxY() const { return maxy; } + inline CvSeq *Edges() const { return edges; } + inline double SumX() const { return sumx; } + inline double SumY() const { return sumy; } + inline double SumXX() const { return sumxx; } + inline double SumYY() const { return sumyy; } + inline double SumXY() const { return sumxy; } + + //! etiqueta del blob + //! label of the blob + int etiqueta; + //! flag per indicar si es exterior o no + //! true for extern blobs + int exterior; + //! area del blob + //! Blob area + double area; + //! perimetre del blob + //! Blob perimeter + double perimeter; + //! quantitat de perimetre del blob extern + //! amount of blob perimeter which is exterior + double externPerimeter; + //! etiqueta del blob pare + //! label of the parent blob + int parent; + //! moments + double sumx; + double sumy; + double sumxx; + double sumyy; + double sumxy; + //! Bounding rect + double minx; + double maxx; + double miny; + double maxy; + + //! mitjana + //! mean of the grey scale values of the blob pixels + double mean; + //! desviació standard + //! standard deviation of the grey scale values of the blob pixels + double stddev; + + //! à rea de memòria on es desaran els punts de contorn del blob + //! storage which contains the edges of the blob + CvMemStorage *m_storage; + //! Sequència de punts del contorn del blob + //! Sequence with the edges of the blob + CvSeq *edges; + + + //! Point datatype for plotting (FillBlob) + typedef std::vector<CvPoint> vectorPunts; + + //! Helper class to compare two CvPoints (for sorting in FillBlob) + struct comparaCvPoint : public std::binary_function<CvPoint, CvPoint, bool> + { + //! Definim que un punt és menor com més amunt a la dreta estigui + bool operator()(CvPoint a, CvPoint b) + { + if (a.y == b.y) + return a.x < b.x; + else + return a.y < b.y; + } + }; + }; + + + + /************************************************************************** + Definició de les classes per a fer operacions sobre els blobs + + Helper classes to perform operations on blobs + **************************************************************************/ + + + //! Classe d'on derivarem totes les operacions sobre els blobs + //! Interface to derive all blob operations + class COperadorBlob + { + public: + virtual ~COperadorBlob() {}; + + //! Aplica l'operació al blob + virtual double operator()(const CBlob &blob) const = 0; + //! Obté el nom de l'operador + virtual const char *GetNom() const = 0; + + operator COperadorBlob*() const + { + return (COperadorBlob*)this; + } + }; + + typedef COperadorBlob funcio_calculBlob; + + #ifdef BLOB_OBJECT_FACTORY + /** + Funció per comparar dos identificadors dins de la fà brica de COperadorBlobs + */ + struct functorComparacioIdOperador + { + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } + }; + + //! Definition of Object factory type for COperadorBlob objects + typedef ObjectFactory<COperadorBlob, const char *, functorComparacioIdOperador > t_OperadorBlobFactory; + + //! Funció global per a registrar tots els operadors definits a blob.h + void RegistraTotsOperadors(t_OperadorBlobFactory &fabricaOperadorsBlob); + + #endif + + //! Classe per calcular l'à rea d'un blob + //! Class to get the area of a blob + class CBlobGetArea : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.Area(); + } + const char *GetNom() const + { + return "CBlobGetArea"; + } + }; + + //! Classe per calcular el perimetre d'un blob + //! Class to get the perimeter of a blob + class CBlobGetPerimeter : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.Perimeter(); + } + const char *GetNom() const + { + return "CBlobGetPerimeter"; + } + }; + + //! Classe que diu si un blob és extern o no + //! Class to get the extern flag of a blob + class CBlobGetExterior : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.Exterior(); + } + const char *GetNom() const + { + return "CBlobGetExterior"; + } + }; + + //! Classe per calcular la mitjana de nivells de gris d'un blob + //! Class to get the mean grey level of a blob + class CBlobGetMean : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.Mean(); + } + const char *GetNom() const + { + return "CBlobGetMean"; + } + }; + + //! Classe per calcular la desviació està ndard dels nivells de gris d'un blob + //! Class to get the standard deviation of the grey level values of a blob + class CBlobGetStdDev : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.StdDev(); + } + const char *GetNom() const + { + return "CBlobGetStdDev"; + } + }; + + //! Classe per calcular la compacitat d'un blob + //! Class to calculate the compactness of a blob + class CBlobGetCompactness : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetCompactness"; + } + }; + + //! Classe per calcular la longitud d'un blob + //! Class to calculate the length of a blob + class CBlobGetLength : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetLength"; + } + }; + + //! Classe per calcular l'amplada d'un blob + //! Class to calculate the breadth of a blob + class CBlobGetBreadth : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetBreadth"; + } + }; + + //! Classe per calcular la diferència en X del blob + class CBlobGetDiffX : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.maxx - blob.minx; + } + const char *GetNom() const + { + return "CBlobGetDiffX"; + } + }; + + //! Classe per calcular la diferència en X del blob + class CBlobGetDiffY : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.maxy - blob.miny; + } + const char *GetNom() const + { + return "CBlobGetDiffY"; + } + }; + + //! Classe per calcular el moment PQ del blob + //! Class to calculate the P,Q moment of a blob + class CBlobGetMoment : public COperadorBlob + { + public: + //! Constructor està ndard + //! Standard constructor (gets the 00 moment) + CBlobGetMoment() + { + m_p = m_q = 0; + } + //! Constructor: indiquem el moment p,q a calcular + //! Constructor: gets the PQ moment + CBlobGetMoment(int p, int q) + { + m_p = p; + m_q = q; + }; + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMoment"; + } + + private: + //! moment que volem calcular + int m_p, m_q; + }; + + //! Classe per calcular el perimetre del poligon convex d'un blob + //! Class to calculate the convex hull perimeter of a blob + class CBlobGetHullPerimeter : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetHullPerimeter"; + } + }; + + //! Classe per calcular l'à rea del poligon convex d'un blob + //! Class to calculate the convex hull area of a blob + class CBlobGetHullArea : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetHullArea"; + } + }; + + //! Classe per calcular la x minima en la y minima + //! Class to calculate the minimum x on the minimum y + class CBlobGetMinXatMinY : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMinXatMinY"; + } + }; + + //! Classe per calcular la y minima en la x maxima + //! Class to calculate the minimum y on the maximum x + class CBlobGetMinYatMaxX : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMinYatMaxX"; + } + }; + + //! Classe per calcular la x maxima en la y maxima + //! Class to calculate the maximum x on the maximum y + class CBlobGetMaxXatMaxY : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMaxXatMaxY"; + } + }; + + //! Classe per calcular la y maxima en la x minima + //! Class to calculate the maximum y on the minimum y + class CBlobGetMaxYatMinX : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetMaxYatMinX"; + } + }; + + //! Classe per a calcular la x mÃnima + //! Class to get the minimum x + class CBlobGetMinX : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.MinX(); + } + const char *GetNom() const + { + return "CBlobGetMinX"; + } + }; + + //! Classe per a calcular la x mà xima + //! Class to get the maximum x + class CBlobGetMaxX : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.MaxX(); + } + const char *GetNom() const + { + return "CBlobGetMaxX"; + } + }; + + //! Classe per a calcular la y mÃnima + //! Class to get the minimum y + class CBlobGetMinY : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.MinY(); + } + const char *GetNom() const + { + return "CBlobGetMinY"; + } + }; + + //! Classe per a calcular la y mà xima + //! Class to get the maximum y + class CBlobGetMaxY : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.MaxY(); + } + const char *GetNom() const + { + return "CBlobGetMax"; + } + }; + + + //! Classe per calcular l'elongacio d'un blob + //! Class to calculate the elongation of the blob + class CBlobGetElongation : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetElongation"; + } + }; + + //! Classe per calcular la rugositat d'un blob + //! Class to calculate the roughness of the blob + class CBlobGetRoughness : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetRoughness"; + } + }; + + //! Classe per calcular la distà ncia entre el centre del blob i un punt donat + //! Class to calculate the euclidean distance between the center of a blob and a given point + class CBlobGetDistanceFromPoint : public COperadorBlob + { + public: + //! Standard constructor (distance to point 0,0) + CBlobGetDistanceFromPoint() + { + m_x = m_y = 0.0; + } + //! Constructor (distance to point x,y) + CBlobGetDistanceFromPoint(const double x, const double y) + { + m_x = x; + m_y = y; + } + + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetDistanceFromPoint"; + } + + private: + // coordenades del punt on volem calcular la distà ncia + double m_x, m_y; + }; + + //! Classe per calcular el nombre de pixels externs d'un blob + //! Class to get the number of extern pixels of a blob + class CBlobGetExternPerimeter : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.ExternPerimeter(); + } + const char *GetNom() const + { + return "CBlobGetExternPerimeter"; + } + }; + + //! Classe per calcular el ratio entre el perimetre i nombre pixels externs + //! valors propers a 0 indiquen que la majoria del blob és intern + //! valors propers a 1 indiquen que la majoria del blob és extern + //! Class to calculate the ratio between the perimeter and the number of extern pixels + class CBlobGetExternPerimeterRatio : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + if (blob.Perimeter() != 0) + return blob.ExternPerimeter() / blob.Perimeter(); + else + return blob.ExternPerimeter(); + } + const char *GetNom() const + { + return "CBlobGetExternPerimeterRatio"; + } + }; + + //! Classe per calcular el ratio entre el perimetre convex i nombre pixels externs + //! valors propers a 0 indiquen que la majoria del blob és intern + //! valors propers a 1 indiquen que la majoria del blob és extern + //! Class to calculate the ratio between the perimeter and the number of extern pixels + class CBlobGetExternHullPerimeterRatio : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + CBlobGetHullPerimeter getHullPerimeter; + double hullPerimeter; + + if ((hullPerimeter = getHullPerimeter(blob)) != 0) + return blob.ExternPerimeter() / hullPerimeter; + else + return blob.ExternPerimeter(); + } + const char *GetNom() const + { + return "CBlobGetExternHullPerimeterRatio"; + } + }; + + //! Classe per calcular el centre en el eix X d'un blob + //! Class to calculate the center in the X direction + class CBlobGetXCenter : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.MinX() + ((blob.MaxX() - blob.MinX()) / 2.0); + } + const char *GetNom() const + { + return "CBlobGetXCenter"; + } + }; + + //! Classe per calcular el centre en el eix Y d'un blob + //! Class to calculate the center in the Y direction + class CBlobGetYCenter : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + return blob.MinY() + ((blob.MaxY() - blob.MinY()) / 2.0); + } + const char *GetNom() const + { + return "CBlobGetYCenter"; + } + }; + + //! Classe per calcular la longitud de l'eix major d'un blob + //! Class to calculate the length of the major axis of the ellipse that fits the blob edges + class CBlobGetMajorAxisLength : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + return elipse.size.width; + } + const char *GetNom() const + { + return "CBlobGetMajorAxisLength"; + } + }; + + //! Classe per calcular el ratio entre l'area de la elipse i la de la taca + //! Class + class CBlobGetAreaElipseRatio : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + if (blob.Area() == 0.0) return 0.0; + + CvBox2D elipse = blob.GetEllipse(); + double ratioAreaElipseAreaTaca = ((elipse.size.width / 2.0) + * + (elipse.size.height / 2.0) + *CV_PI + ) + / + blob.Area(); + + return ratioAreaElipseAreaTaca; + } + const char *GetNom() const + { + return "CBlobGetAreaElipseRatio"; + } + }; + + //! Classe per calcular la longitud de l'eix menor d'un blob + //! Class to calculate the length of the minor axis of the ellipse that fits the blob edges + class CBlobGetMinorAxisLength : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + return elipse.size.height; + } + const char *GetNom() const + { + return "CBlobGetMinorAxisLength"; + } + }; + + //! Classe per calcular l'orientació de l'ellipse del blob en radians + //! Class to calculate the orientation of the ellipse that fits the blob edges in radians + class CBlobGetOrientation : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + if (elipse.angle > 180.0) + return ((elipse.angle - 180.0)* DEGREE2RAD); + else + return (elipse.angle * DEGREE2RAD); + + } + const char *GetNom() const + { + return "CBlobGetOrientation"; + } + }; + + //! Classe per calcular el cosinus de l'orientació de l'ellipse del blob + //! Class to calculate the cosinus of the orientation of the ellipse that fits the blob edges + class CBlobGetOrientationCos : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + CBlobGetOrientation getOrientation; + return fabs(cos(getOrientation(blob))); + } + const char *GetNom() const + { + return "CBlobGetOrientationCos"; + } + }; + + + //! Classe per calcular el ratio entre l'eix major i menor de la el·lipse + //! Class to calculate the ratio between both axes of the ellipse + class CBlobGetAxisRatio : public COperadorBlob + { + public: + double operator()(const CBlob &blob) const + { + CvBox2D elipse = blob.GetEllipse(); + + return elipse.size.height / elipse.size.width; + } + const char *GetNom() const + { + return "CBlobGetAxisRatio"; + } + }; + + + //! Classe per calcular si un punt cau dins del blob + //! Class to calculate whether a point is inside a blob + class CBlobGetXYInside : public COperadorBlob + { + public: + //! Constructor està ndard + //! Standard constructor + CBlobGetXYInside() + { + m_p = cvPoint(0, 0); + } + //! Constructor: indiquem el punt + //! Constructor: sets the point + CBlobGetXYInside(CvPoint p) + { + m_p = p; + }; + double operator()(const CBlob &blob) const; + const char *GetNom() const + { + return "CBlobGetXYInside"; + } + + private: + //! punt que considerem + //! point to be considered + CvPoint m_p; + }; } - }; - }; - - - - /************************************************************************** - Definició de les classes per a fer operacions sobre els blobs - - Helper classes to perform operations on blobs - **************************************************************************/ - - - //! Classe d'on derivarem totes les operacions sobre els blobs - //! Interface to derive all blob operations - class COperadorBlob - { - public: - virtual ~COperadorBlob() {}; - - //! Aplica l'operació al blob - virtual double operator()(const CBlob &blob) const = 0; - //! Obté el nom de l'operador - virtual const char *GetNom() const = 0; - - operator COperadorBlob*() const - { - return (COperadorBlob*)this; - } - }; - - typedef COperadorBlob funcio_calculBlob; - -#ifdef BLOB_OBJECT_FACTORY - /** - Funció per comparar dos identificadors dins de la fà brica de COperadorBlobs - */ - struct functorComparacioIdOperador - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - //! Definition of Object factory type for COperadorBlob objects - typedef ObjectFactory<COperadorBlob, const char *, functorComparacioIdOperador > t_OperadorBlobFactory; - - //! Funció global per a registrar tots els operadors definits a blob.h - void RegistraTotsOperadors(t_OperadorBlobFactory &fabricaOperadorsBlob); - -#endif - - //! Classe per calcular l'à rea d'un blob - //! Class to get the area of a blob - class CBlobGetArea : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.Area(); - } - const char *GetNom() const - { - return "CBlobGetArea"; - } - }; - - //! Classe per calcular el perimetre d'un blob - //! Class to get the perimeter of a blob - class CBlobGetPerimeter : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.Perimeter(); - } - const char *GetNom() const - { - return "CBlobGetPerimeter"; - } - }; - - //! Classe que diu si un blob és extern o no - //! Class to get the extern flag of a blob - class CBlobGetExterior : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.Exterior(); - } - const char *GetNom() const - { - return "CBlobGetExterior"; - } - }; - - //! Classe per calcular la mitjana de nivells de gris d'un blob - //! Class to get the mean grey level of a blob - class CBlobGetMean : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.Mean(); - } - const char *GetNom() const - { - return "CBlobGetMean"; - } - }; - - //! Classe per calcular la desviació està ndard dels nivells de gris d'un blob - //! Class to get the standard deviation of the grey level values of a blob - class CBlobGetStdDev : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.StdDev(); - } - const char *GetNom() const - { - return "CBlobGetStdDev"; - } - }; - - //! Classe per calcular la compacitat d'un blob - //! Class to calculate the compactness of a blob - class CBlobGetCompactness : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetCompactness"; - } - }; - - //! Classe per calcular la longitud d'un blob - //! Class to calculate the length of a blob - class CBlobGetLength : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetLength"; - } - }; - - //! Classe per calcular l'amplada d'un blob - //! Class to calculate the breadth of a blob - class CBlobGetBreadth : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetBreadth"; - } - }; - - //! Classe per calcular la diferència en X del blob - class CBlobGetDiffX : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.maxx - blob.minx; - } - const char *GetNom() const - { - return "CBlobGetDiffX"; - } - }; - - //! Classe per calcular la diferència en X del blob - class CBlobGetDiffY : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.maxy - blob.miny; - } - const char *GetNom() const - { - return "CBlobGetDiffY"; - } - }; - - //! Classe per calcular el moment PQ del blob - //! Class to calculate the P,Q moment of a blob - class CBlobGetMoment : public COperadorBlob - { - public: - //! Constructor està ndard - //! Standard constructor (gets the 00 moment) - CBlobGetMoment() - { - m_p = m_q = 0; - } - //! Constructor: indiquem el moment p,q a calcular - //! Constructor: gets the PQ moment - CBlobGetMoment(int p, int q) - { - m_p = p; - m_q = q; - }; - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetMoment"; - } - - private: - //! moment que volem calcular - int m_p, m_q; - }; - - //! Classe per calcular el perimetre del poligon convex d'un blob - //! Class to calculate the convex hull perimeter of a blob - class CBlobGetHullPerimeter : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetHullPerimeter"; - } - }; - - //! Classe per calcular l'à rea del poligon convex d'un blob - //! Class to calculate the convex hull area of a blob - class CBlobGetHullArea : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetHullArea"; - } - }; - - //! Classe per calcular la x minima en la y minima - //! Class to calculate the minimum x on the minimum y - class CBlobGetMinXatMinY : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetMinXatMinY"; - } - }; - - //! Classe per calcular la y minima en la x maxima - //! Class to calculate the minimum y on the maximum x - class CBlobGetMinYatMaxX : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetMinYatMaxX"; - } - }; - - //! Classe per calcular la x maxima en la y maxima - //! Class to calculate the maximum x on the maximum y - class CBlobGetMaxXatMaxY : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetMaxXatMaxY"; - } - }; - - //! Classe per calcular la y maxima en la x minima - //! Class to calculate the maximum y on the minimum y - class CBlobGetMaxYatMinX : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetMaxYatMinX"; - } - }; - - //! Classe per a calcular la x mÃnima - //! Class to get the minimum x - class CBlobGetMinX : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.MinX(); - } - const char *GetNom() const - { - return "CBlobGetMinX"; - } - }; - - //! Classe per a calcular la x mà xima - //! Class to get the maximum x - class CBlobGetMaxX : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.MaxX(); } - const char *GetNom() const - { - return "CBlobGetMaxX"; - } - }; - - //! Classe per a calcular la y mÃnima - //! Class to get the minimum y - class CBlobGetMinY : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.MinY(); - } - const char *GetNom() const - { - return "CBlobGetMinY"; - } - }; - - //! Classe per a calcular la y mà xima - //! Class to get the maximum y - class CBlobGetMaxY : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.MaxY(); - } - const char *GetNom() const - { - return "CBlobGetMax"; - } - }; - - - //! Classe per calcular l'elongacio d'un blob - //! Class to calculate the elongation of the blob - class CBlobGetElongation : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetElongation"; - } - }; - - //! Classe per calcular la rugositat d'un blob - //! Class to calculate the roughness of the blob - class CBlobGetRoughness : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetRoughness"; - } - }; - - //! Classe per calcular la distà ncia entre el centre del blob i un punt donat - //! Class to calculate the euclidean distance between the center of a blob and a given point - class CBlobGetDistanceFromPoint : public COperadorBlob - { - public: - //! Standard constructor (distance to point 0,0) - CBlobGetDistanceFromPoint() - { - m_x = m_y = 0.0; - } - //! Constructor (distance to point x,y) - CBlobGetDistanceFromPoint(const double x, const double y) - { - m_x = x; - m_y = y; - } - - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetDistanceFromPoint"; - } - - private: - // coordenades del punt on volem calcular la distà ncia - double m_x, m_y; - }; - - //! Classe per calcular el nombre de pixels externs d'un blob - //! Class to get the number of extern pixels of a blob - class CBlobGetExternPerimeter : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.ExternPerimeter(); - } - const char *GetNom() const - { - return "CBlobGetExternPerimeter"; - } - }; - - //! Classe per calcular el ratio entre el perimetre i nombre pixels externs - //! valors propers a 0 indiquen que la majoria del blob és intern - //! valors propers a 1 indiquen que la majoria del blob és extern - //! Class to calculate the ratio between the perimeter and the number of extern pixels - class CBlobGetExternPerimeterRatio : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - if (blob.Perimeter() != 0) - return blob.ExternPerimeter() / blob.Perimeter(); - else - return blob.ExternPerimeter(); - } - const char *GetNom() const - { - return "CBlobGetExternPerimeterRatio"; - } - }; - - //! Classe per calcular el ratio entre el perimetre convex i nombre pixels externs - //! valors propers a 0 indiquen que la majoria del blob és intern - //! valors propers a 1 indiquen que la majoria del blob és extern - //! Class to calculate the ratio between the perimeter and the number of extern pixels - class CBlobGetExternHullPerimeterRatio : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - CBlobGetHullPerimeter getHullPerimeter; - double hullPerimeter; - - if ((hullPerimeter = getHullPerimeter(blob)) != 0) - return blob.ExternPerimeter() / hullPerimeter; - else - return blob.ExternPerimeter(); - } - const char *GetNom() const - { - return "CBlobGetExternHullPerimeterRatio"; - } - }; - - //! Classe per calcular el centre en el eix X d'un blob - //! Class to calculate the center in the X direction - class CBlobGetXCenter : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.MinX() + ((blob.MaxX() - blob.MinX()) / 2.0); - } - const char *GetNom() const - { - return "CBlobGetXCenter"; - } - }; - - //! Classe per calcular el centre en el eix Y d'un blob - //! Class to calculate the center in the Y direction - class CBlobGetYCenter : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - return blob.MinY() + ((blob.MaxY() - blob.MinY()) / 2.0); - } - const char *GetNom() const - { - return "CBlobGetYCenter"; - } - }; - - //! Classe per calcular la longitud de l'eix major d'un blob - //! Class to calculate the length of the major axis of the ellipse that fits the blob edges - class CBlobGetMajorAxisLength : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - CvBox2D elipse = blob.GetEllipse(); - - return elipse.size.width; - } - const char *GetNom() const - { - return "CBlobGetMajorAxisLength"; - } - }; - - //! Classe per calcular el ratio entre l'area de la elipse i la de la taca - //! Class - class CBlobGetAreaElipseRatio : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - if (blob.Area() == 0.0) return 0.0; - - CvBox2D elipse = blob.GetEllipse(); - double ratioAreaElipseAreaTaca = ((elipse.size.width / 2.0) - * - (elipse.size.height / 2.0) - *CV_PI - ) - / - blob.Area(); - - return ratioAreaElipseAreaTaca; - } - const char *GetNom() const - { - return "CBlobGetAreaElipseRatio"; - } - }; - - //! Classe per calcular la longitud de l'eix menor d'un blob - //! Class to calculate the length of the minor axis of the ellipse that fits the blob edges - class CBlobGetMinorAxisLength : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - CvBox2D elipse = blob.GetEllipse(); - - return elipse.size.height; - } - const char *GetNom() const - { - return "CBlobGetMinorAxisLength"; - } - }; - - //! Classe per calcular l'orientació de l'ellipse del blob en radians - //! Class to calculate the orientation of the ellipse that fits the blob edges in radians - class CBlobGetOrientation : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - CvBox2D elipse = blob.GetEllipse(); - - if (elipse.angle > 180.0) - return ((elipse.angle - 180.0)* DEGREE2RAD); - else - return (elipse.angle * DEGREE2RAD); - - } - const char *GetNom() const - { - return "CBlobGetOrientation"; - } - }; - - //! Classe per calcular el cosinus de l'orientació de l'ellipse del blob - //! Class to calculate the cosinus of the orientation of the ellipse that fits the blob edges - class CBlobGetOrientationCos : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - CBlobGetOrientation getOrientation; - return fabs(cos(getOrientation(blob))); - } - const char *GetNom() const - { - return "CBlobGetOrientationCos"; - } - }; - - - //! Classe per calcular el ratio entre l'eix major i menor de la el·lipse - //! Class to calculate the ratio between both axes of the ellipse - class CBlobGetAxisRatio : public COperadorBlob - { - public: - double operator()(const CBlob &blob) const - { - CvBox2D elipse = blob.GetEllipse(); - - return elipse.size.height / elipse.size.width; - } - const char *GetNom() const - { - return "CBlobGetAxisRatio"; - } - }; - - - //! Classe per calcular si un punt cau dins del blob - //! Class to calculate whether a point is inside a blob - class CBlobGetXYInside : public COperadorBlob - { - public: - //! Constructor està ndard - //! Standard constructor - CBlobGetXYInside() - { - m_p = cvPoint(0, 0); - } - //! Constructor: indiquem el punt - //! Constructor: sets the point - CBlobGetXYInside(CvPoint p) - { - m_p = p; - }; - double operator()(const CBlob &blob) const; - const char *GetNom() const - { - return "CBlobGetXYInside"; - } - - private: - //! punt que considerem - //! point to be considered - CvPoint m_p; - }; - + } } diff --git a/src/algorithms/PAWCS.cpp b/src/algorithms/PAWCS.cpp index e9f82d2fa322fea5177228339bf6fde334c16f98..aab37c7f239485bc1aa38923de494e4f3a4e7463 100644 --- a/src/algorithms/PAWCS.cpp +++ b/src/algorithms/PAWCS.cpp @@ -5,11 +5,11 @@ using namespace bgslibrary::algorithms; PAWCS::PAWCS() : IBGS(quote(PAWCS)), pPAWCS(nullptr), - fRelLBSPThreshold(BGSPAWCS_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD), - nDescDistThresholdOffset(BGSPAWCS_DEFAULT_DESC_DIST_THRESHOLD_OFFSET), - nMinColorDistThreshold(BGSPAWCS_DEFAULT_MIN_COLOR_DIST_THRESHOLD), - nMaxNbWords(BGSPAWCS_DEFAULT_MAX_NB_WORDS), - nSamplesForMovingAvgs(BGSPAWCS_DEFAULT_N_SAMPLES_FOR_MV_AVGS) + fRelLBSPThreshold(lbsp::BGSPAWCS_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD), + nDescDistThresholdOffset(lbsp::BGSPAWCS_DEFAULT_DESC_DIST_THRESHOLD_OFFSET), + nMinColorDistThreshold(lbsp::BGSPAWCS_DEFAULT_MIN_COLOR_DIST_THRESHOLD), + nMaxNbWords(lbsp::BGSPAWCS_DEFAULT_MAX_NB_WORDS), + nSamplesForMovingAvgs(lbsp::BGSPAWCS_DEFAULT_N_SAMPLES_FOR_MV_AVGS) { debug_construction(PAWCS); initLoadSaveConfig(algorithmName); @@ -26,7 +26,7 @@ void PAWCS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_ init(img_input, img_output, img_bgmodel); if (firstTime) { - pPAWCS = new BackgroundSubtractorPAWCS( + pPAWCS = new lbsp::BackgroundSubtractorPAWCS( fRelLBSPThreshold, nDescDistThresholdOffset, nMinColorDistThreshold, nMaxNbWords, nSamplesForMovingAvgs); diff --git a/src/algorithms/PAWCS.h b/src/algorithms/PAWCS.h index 48d8286a2fb5f9875445d769d70c541e5a557d12..737d5db961d1af9429013ad67faafad5b5ce1e99 100644 --- a/src/algorithms/PAWCS.h +++ b/src/algorithms/PAWCS.h @@ -10,7 +10,7 @@ namespace bgslibrary class PAWCS : public IBGS { private: - BackgroundSubtractorPAWCS* pPAWCS; + lbsp::BackgroundSubtractorPAWCS* pPAWCS; float fRelLBSPThreshold; int nDescDistThresholdOffset; diff --git a/src/algorithms/PBAS/PBAS.cpp b/src/algorithms/PBAS/PBAS.cpp index 36ed4596b1130c43c7a60c34d6c136b6c8634620..c024792d7117e58610e772dd741a1303c959019a 100644 --- a/src/algorithms/PBAS/PBAS.cpp +++ b/src/algorithms/PBAS/PBAS.cpp @@ -1,9 +1,12 @@ #include "PBAS.h" -PBAS::PBAS(void) : N(20), R_lower(18), Raute_min(2), T_lower(2), T_upper(200), R_scale(5), R_incdec(0.05), T_dec(0.05), T_inc(1) -{ - std::cout << "PBAS()" << std::endl; +using namespace bgslibrary::algorithms::pbas; +PBAS::PBAS() : + N(20), R_lower(18), Raute_min(2), + T_lower(2), T_upper(200), R_scale(5), + R_incdec(0.05), T_dec(0.05), T_inc(1) +{ //feature vector alpha = 7.0; beta = 1.0; @@ -61,8 +64,6 @@ void PBAS::newInitialization() PBAS::~PBAS(void) { - std::cout << "~PBAS()" << std::endl; - randomN.clear(); randomX.clear(); randomY.clear(); diff --git a/src/algorithms/PBAS/PBAS.h b/src/algorithms/PBAS/PBAS.h index d58dceb7668175a6103ea19f0fee77ebe2c6add1..b1c88310066bb82512fbc34b0a7ecf2ded8f589c 100644 --- a/src/algorithms/PBAS/PBAS.h +++ b/src/algorithms/PBAS/PBAS.h @@ -4,148 +4,154 @@ #include <opencv2/opencv.hpp> //#include <highgui.h> -class PBAS +namespace bgslibrary { -public: - PBAS(void); - ~PBAS(void); - bool process(cv::Mat* input, cv::Mat* output); - - void setN(int); - void setRaute_min(int); - void setR_lower(double); - void setR_incdec(double); - void setR_scale(double); - void setT_init(double); - void setT_lower(double); - void setT_upper(double); - void setT_dec(double); - void setT_inc(double); - void setAlpha(double); - void setBeta(double); - - bool isMovement(); - -private: - void calculateFeatures(std::vector<cv::Mat>* feature, cv::Mat* inputImage); - void checkValid(int *x, int *y); - void decisionThresholdRegulator(float* pt, float* meanDistArr); - void learningRateRegulator(float* pt, float* meanDist, uchar* isFore); - void init(cv::Mat*); - void newInitialization(); - - cv::Mat meanMinDist; - float* meanMinDist_Pt; - - - - int width, height; - int chans; - - //balance of feature pixel value to conture value - double alpha, beta; - //################################################################################## - - double formerMeanNorm; - - //define value of foreground/background pixels - int foregroundValue, backgroundValue; - - //################################################################################## - //random number parameters - - //random number generator - cv::RNG randomGenerator; - - //length of random array initialization - long countOfRandomNumb; - - //pre - initialize the randomNumbers for better performance - std::vector<int> randomN, randomMinDist, randomX, randomY, randomT, randomTN; - - //################################################################################### - - //check if something is moving - bool isMove; - - //for init, count number of runs - int runs; - - - cv::Mat* resultMap; - std::vector<cv::Mat> currentFeatures; - - std::vector<float*> currentFeaturesM_Pt; - std::vector<uchar*> currentFeaturesC_Pt; - uchar* resultMap_Pt; - - std::vector<std::vector<float*>>B_Mag_Pts; - std::vector<std::vector<uchar*>>B_Col_Pts; - - double sumMagnitude; - double formerMeanMag; - //float formerDistanceBack; - - //#################################################################################### - //N - Number: Defining the size of the background-history-model - // number of samples per pixel - //size of background history B(x_i) - int N; - // background model - std::vector<std::vector<cv::Mat>> backgroundModel; - //#################################################################################### - //#################################################################################### - //R-Threshhold - Variables - //minimal Threshold for foreground and initial value of R(x_i) - // radius of the sphere -> lower border boundary - double R_lower; - - //factor which defines new threshold of R(x_i) together with meanMinDist(x_i) - // scale for the sphere threshhold to define pixel-based Thresholds - double R_scale; - - //decreasing / increasing factor of R(x_i) - // increasing/decreasing factor for the r-Threshold based on the result of rTreshScale * meanMinDistBackground - double R_incdec; - - cv::Mat actualR; - float* actualR_Pt; //new pixel-based r-threshhold -> pointer to arrays - //##################################################################################### - //#################################################################################### - //counter for minimal distance to background - // Defining the number of background-model-images, which have a lowerDIstance to the current Image than defined by the R-Thresholds, that are necessary - // to decide that this pixel is background - int Raute_min; - //##################################################################################### - //#################################################################################### - //initial value of inverse update factor T(x_i) - // Initialize the background-model update rate - double T_init; - - //increasing Factor of the update rate 1/T(x_i) - // scale that defines the increasing of the update rate of the background model, if the current pixel is background - //--> more frequently updates if pixel is background because, there shouln't be any change - double T_inc; - - //upper boundary of T(x_i) - // defining an upper value, that nrSubsampling can achieve, thus it doesn't reach to an infinite value, where actually no update is possible - // at all - double T_upper; - - //lower boundary of T(x_i) - // defining a minimum value for nrSubsampling --> base value 2.0 - double T_lower; - - //decreasing factor of the update rate 1/T(x_i) - // opposite scale to increasingRateScale, for decreasing the update rate of the background model, if the current pixel is foreground - //--> Thesis: Our Foreground is a real moving object -> thus the background-model is good, so don't update it - double T_dec; - - //holds update rate of current pixel - cv::Mat actualT; - float* actualT_Pt; - - //##################################################################################### - - cv::Mat sobelX, sobelY; -}; + namespace algorithms + { + namespace pbas + { + class PBAS + { + public: + PBAS(void); + ~PBAS(void); + bool process(cv::Mat* input, cv::Mat* output); + + void setN(int); + void setRaute_min(int); + void setR_lower(double); + void setR_incdec(double); + void setR_scale(double); + void setT_init(double); + void setT_lower(double); + void setT_upper(double); + void setT_dec(double); + void setT_inc(double); + void setAlpha(double); + void setBeta(double); + + bool isMovement(); + + private: + void calculateFeatures(std::vector<cv::Mat>* feature, cv::Mat* inputImage); + void checkValid(int *x, int *y); + void decisionThresholdRegulator(float* pt, float* meanDistArr); + void learningRateRegulator(float* pt, float* meanDist, uchar* isFore); + void init(cv::Mat*); + void newInitialization(); + + cv::Mat meanMinDist; + float* meanMinDist_Pt; + + int width, height; + int chans; + + //balance of feature pixel value to conture value + double alpha, beta; + //################################################################################## + + double formerMeanNorm; + + //define value of foreground/background pixels + int foregroundValue, backgroundValue; + + //################################################################################## + //random number parameters + + //random number generator + cv::RNG randomGenerator; + + //length of random array initialization + long countOfRandomNumb; + + //pre - initialize the randomNumbers for better performance + std::vector<int> randomN, randomMinDist, randomX, randomY, randomT, randomTN; + + //################################################################################### + + //check if something is moving + bool isMove; + + //for init, count number of runs + int runs; + + cv::Mat* resultMap; + std::vector<cv::Mat> currentFeatures; + + std::vector<float*> currentFeaturesM_Pt; + std::vector<uchar*> currentFeaturesC_Pt; + uchar* resultMap_Pt; + + std::vector<std::vector<float*>>B_Mag_Pts; + std::vector<std::vector<uchar*>>B_Col_Pts; + + double sumMagnitude; + double formerMeanMag; + //float formerDistanceBack; + + //#################################################################################### + //N - Number: Defining the size of the background-history-model + // number of samples per pixel + //size of background history B(x_i) + int N; + // background model + std::vector<std::vector<cv::Mat>> backgroundModel; + //#################################################################################### + //#################################################################################### + //R-Threshhold - Variables + //minimal Threshold for foreground and initial value of R(x_i) + // radius of the sphere -> lower border boundary + double R_lower; + + //factor which defines new threshold of R(x_i) together with meanMinDist(x_i) + // scale for the sphere threshhold to define pixel-based Thresholds + double R_scale; + + //decreasing / increasing factor of R(x_i) + // increasing/decreasing factor for the r-Threshold based on the result of rTreshScale * meanMinDistBackground + double R_incdec; + + cv::Mat actualR; + float* actualR_Pt; //new pixel-based r-threshhold -> pointer to arrays + //##################################################################################### + //#################################################################################### + //counter for minimal distance to background + // Defining the number of background-model-images, which have a lowerDIstance to the current Image than defined by the R-Thresholds, that are necessary + // to decide that this pixel is background + int Raute_min; + //##################################################################################### + //#################################################################################### + //initial value of inverse update factor T(x_i) + // Initialize the background-model update rate + double T_init; + + //increasing Factor of the update rate 1/T(x_i) + // scale that defines the increasing of the update rate of the background model, if the current pixel is background + //--> more frequently updates if pixel is background because, there shouln't be any change + double T_inc; + + //upper boundary of T(x_i) + // defining an upper value, that nrSubsampling can achieve, thus it doesn't reach to an infinite value, where actually no update is possible + // at all + double T_upper; + + //lower boundary of T(x_i) + // defining a minimum value for nrSubsampling --> base value 2.0 + double T_lower; + + //decreasing factor of the update rate 1/T(x_i) + // opposite scale to increasingRateScale, for decreasing the update rate of the background model, if the current pixel is foreground + //--> Thesis: Our Foreground is a real moving object -> thus the background-model is good, so don't update it + double T_dec; + + //holds update rate of current pixel + cv::Mat actualT; + float* actualT_Pt; + + //##################################################################################### + + cv::Mat sobelX, sobelY; + }; + } + } +} diff --git a/src/algorithms/PixelBasedAdaptiveSegmenter.h b/src/algorithms/PixelBasedAdaptiveSegmenter.h index 65209b010823aebf5665a293963b9ff84c0747e7..02687194013941b9c0dcea056404b9e5bbba45b5 100644 --- a/src/algorithms/PixelBasedAdaptiveSegmenter.h +++ b/src/algorithms/PixelBasedAdaptiveSegmenter.h @@ -10,7 +10,7 @@ namespace bgslibrary class PixelBasedAdaptiveSegmenter : public IBGS { private: - PBAS pbas; + pbas::PBAS pbas; bool enableInputBlur; bool enableOutputBlur; diff --git a/src/algorithms/SigmaDelta.cpp b/src/algorithms/SigmaDelta.cpp index 72aae943af06b928015369e0b52a928cfba33c53..1e17b374c32d8eb0f6b65d4889412259471b7c1e 100644 --- a/src/algorithms/SigmaDelta.cpp +++ b/src/algorithms/SigmaDelta.cpp @@ -5,7 +5,7 @@ using namespace bgslibrary::algorithms; SigmaDelta::SigmaDelta() : IBGS(quote(SigmaDelta)), ampFactor(1), minVar(15), maxVar(255), - algorithm(sdLaMa091New()) + algorithm(sigmadelta::sdLaMa091New()) { debug_construction(SigmaDelta); initLoadSaveConfig(algorithmName); @@ -14,7 +14,7 @@ SigmaDelta::SigmaDelta() : SigmaDelta::~SigmaDelta() { debug_destruction(SigmaDelta); - sdLaMa091Free(algorithm); + sigmadelta::sdLaMa091Free(algorithm); } void SigmaDelta::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) @@ -22,14 +22,14 @@ void SigmaDelta::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat init(img_input, img_output, img_bgmodel); if (firstTime) { - sdLaMa091AllocInit_8u_C3R(algorithm, img_input.data, img_input.cols, img_input.rows, img_input.step); + sigmadelta::sdLaMa091AllocInit_8u_C3R(algorithm, img_input.data, img_input.cols, img_input.rows, img_input.step); img_foreground = cv::Mat(img_input.size(), CV_8UC1); img_background = cv::Mat(img_input.size(), CV_8UC3); firstTime = false; } else { cv::Mat img_output_tmp(img_input.rows, img_input.cols, CV_8UC3); - sdLaMa091Update_8u_C3R(algorithm, img_input.data, img_output_tmp.data); + sigmadelta::sdLaMa091Update_8u_C3R(algorithm, img_input.data, img_output_tmp.data); unsigned char* tmpBuffer = (unsigned char*)img_output_tmp.data; unsigned char* outBuffer = (unsigned char*)img_foreground.data; @@ -65,7 +65,7 @@ void SigmaDelta::load_config(cv::FileStorage &fs) { } void SigmaDelta::applyParams() { - sdLaMa091SetAmplificationFactor(algorithm, ampFactor); - sdLaMa091SetMinimalVariance(algorithm, minVar); - sdLaMa091SetMaximalVariance(algorithm, maxVar); + sigmadelta::sdLaMa091SetAmplificationFactor(algorithm, ampFactor); + sigmadelta::sdLaMa091SetMinimalVariance(algorithm, minVar); + sigmadelta::sdLaMa091SetMaximalVariance(algorithm, maxVar); } diff --git a/src/algorithms/SigmaDelta.h b/src/algorithms/SigmaDelta.h index f1fb89073296a120a084a1e3e43c025ee8d039c0..adb2a9a7a84d9574294f0f43df1a1473a1a45aca 100644 --- a/src/algorithms/SigmaDelta.h +++ b/src/algorithms/SigmaDelta.h @@ -16,7 +16,7 @@ namespace bgslibrary int ampFactor; int minVar; int maxVar; - sdLaMa091_t* algorithm; + sigmadelta::sdLaMa091_t* algorithm; public: SigmaDelta(); diff --git a/src/algorithms/SigmaDelta/sdLaMa091.cpp b/src/algorithms/SigmaDelta/sdLaMa091.cpp index 6a8c9a50b037f2e2ae1906fbbcaa435b766a8253..feb8754563311f5ed9ffa919d9fe390506d658dd 100644 --- a/src/algorithms/SigmaDelta/sdLaMa091.cpp +++ b/src/algorithms/SigmaDelta/sdLaMa091.cpp @@ -1,641 +1,651 @@ #include "sdLaMa091.h" -#define DEFAULT_N 1 -#define DEFAULT_VMIN 2 -#define DEFAULT_VMAX 255 - -#define LIB "sdLaMa091 - " -#define RED 0 -#define GREEN 1 -#define BLUE 2 -#define CHANNELS 3 - -typedef enum { - UNKNOWN, - C1R, - C3R -} image_t; - -struct sdLaMa091 { - image_t imageType; - - uint32_t width; - uint32_t rgbWidth; - uint32_t height; - uint32_t stride; - uint32_t numBytes; - uint32_t unusedBytes; - uint32_t rgbUnusedBytes; - - uint32_t N; - uint32_t Vmin; - uint32_t Vmax; - - uint8_t* Mt; - uint8_t* Ot; - uint8_t* Vt; -}; - -#if defined(DEFENSIVE_ALLOC) || defined(DEFENSIVE_POINTER) || \ - defined(DEFENSIVE_PARAM) -static inline void outputError(char* error); -#endif - -static inline uint8_t absVal(int8_t num); -static inline uint8_t min(uint8_t a, uint8_t b); -static inline uint8_t max(uint8_t a, uint8_t b); - -#if defined(DEFENSIVE_ALLOC) || defined(DEFENSIVE_POINTER) || \ - defined(DEFENSIVE_PARAM) - -static inline void outputError(char* error) { - fprintf(stderr, "%s%s\n", LIB, error); -} -#endif +//using namespace bgslibrary::algorithms::sigmadelta; +namespace bgslibrary +{ + namespace algorithms + { + namespace sigmadelta + { + const int DEFAULT_N = 1; + const int DEFAULT_VMIN = 2; + const int DEFAULT_VMAX = 255; + + const char* LIB = "sdLaMa091 - "; + const int RED = 0; + const int GREEN = 1; + const int BLUE = 2; + const int CHANNELS = 3; + + typedef enum { + UNKNOWN, + C1R, + C3R + } image_t; + + struct sdLaMa091 { + image_t imageType; + + uint32_t width; + uint32_t rgbWidth; + uint32_t height; + uint32_t stride; + uint32_t numBytes; + uint32_t unusedBytes; + uint32_t rgbUnusedBytes; + + uint32_t N; + uint32_t Vmin; + uint32_t Vmax; + + uint8_t* Mt; + uint8_t* Ot; + uint8_t* Vt; + }; + + #if defined(DEFENSIVE_ALLOC) || defined(DEFENSIVE_POINTER) || \ + defined(DEFENSIVE_PARAM) + static inline void outputError(char* error); + #endif + + static inline uint8_t absVal(int8_t num); + static inline uint8_t min(uint8_t a, uint8_t b); + static inline uint8_t max(uint8_t a, uint8_t b); + + #if defined(DEFENSIVE_ALLOC) || defined(DEFENSIVE_POINTER) || \ + defined(DEFENSIVE_PARAM) + + static inline void outputError(char* error) { + fprintf(stderr, "%s%s\n", LIB, error); + } + #endif -static inline uint8_t absVal(int8_t num) { - return (num < 0) ? (uint8_t)-num : (uint8_t)num; -} + static inline uint8_t absVal(int8_t num) { + return (num < 0) ? (uint8_t)-num : (uint8_t)num; + } -static inline uint8_t min(uint8_t a, uint8_t b) { - return (a < b) ? a : b; -} + static inline uint8_t min(uint8_t a, uint8_t b) { + return (a < b) ? a : b; + } -static inline uint8_t max(uint8_t a, uint8_t b) { - return (a > b) ? a : b; -} + static inline uint8_t max(uint8_t a, uint8_t b) { + return (a > b) ? a : b; + } -sdLaMa091_t* sdLaMa091New(void) { - sdLaMa091_t* sdLaMa091 = (sdLaMa091_t*)malloc(sizeof(*sdLaMa091)); + sdLaMa091_t* sdLaMa091New(void) { + sdLaMa091_t* sdLaMa091 = (sdLaMa091_t*)malloc(sizeof(*sdLaMa091)); -#ifdef DEFENSIVE_ALLOC - if (sdLaMa091 == NULL) { - outputError("Cannot allocate sdLaMa091 structure"); - return NULL; - } -#endif + #ifdef DEFENSIVE_ALLOC + if (sdLaMa091 == NULL) { + outputError("Cannot allocate sdLaMa091 structure"); + return NULL; + } + #endif - sdLaMa091->imageType = UNKNOWN; + sdLaMa091->imageType = UNKNOWN; - sdLaMa091->width = 0; - sdLaMa091->rgbWidth = 0; - sdLaMa091->height = 0; - sdLaMa091->stride = 0; - sdLaMa091->numBytes = 0; - sdLaMa091->unusedBytes = 0; - sdLaMa091->rgbUnusedBytes = 0; + sdLaMa091->width = 0; + sdLaMa091->rgbWidth = 0; + sdLaMa091->height = 0; + sdLaMa091->stride = 0; + sdLaMa091->numBytes = 0; + sdLaMa091->unusedBytes = 0; + sdLaMa091->rgbUnusedBytes = 0; - sdLaMa091->N = DEFAULT_N; - sdLaMa091->Vmin = DEFAULT_VMIN; - sdLaMa091->Vmax = DEFAULT_VMAX; + sdLaMa091->N = DEFAULT_N; + sdLaMa091->Vmin = DEFAULT_VMIN; + sdLaMa091->Vmax = DEFAULT_VMAX; - sdLaMa091->Mt = NULL; - sdLaMa091->Ot = NULL; - sdLaMa091->Vt = NULL; + sdLaMa091->Mt = NULL; + sdLaMa091->Ot = NULL; + sdLaMa091->Vt = NULL; - return sdLaMa091; -} + return sdLaMa091; + } -int32_t sdLaMa091AllocInit_8u_C1R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - const uint32_t width, - const uint32_t height, - const uint32_t stride) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot initialize a NULL structure"); - return EXIT_FAILURE; - } + int32_t sdLaMa091AllocInit_8u_C1R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + const uint32_t width, + const uint32_t height, + const uint32_t stride) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot initialize a NULL structure"); + return EXIT_FAILURE; + } - if (image_data == NULL) { - outputError("Cannot allocate ressources for a NULL image"); - return EXIT_FAILURE; - } -#endif + if (image_data == NULL) { + outputError("Cannot allocate ressources for a NULL image"); + return EXIT_FAILURE; + } + #endif -#ifdef DEFENSIVE_PARAM - if (width == 0 || height == 0 || stride == 0) { - outputError("Cannot allocate ressources for zero values"); - return EXIT_FAILURE; - } + #ifdef DEFENSIVE_PARAM + if (width == 0 || height == 0 || stride == 0) { + outputError("Cannot allocate ressources for zero values"); + return EXIT_FAILURE; + } - if (stride < width) { - outputError("Cannot allocate ressources for a stride lower than the width"); - return EXIT_FAILURE; - } -#endif + if (stride < width) { + outputError("Cannot allocate ressources for a stride lower than the width"); + return EXIT_FAILURE; + } + #endif - sdLaMa091->imageType = C1R; + sdLaMa091->imageType = C1R; - sdLaMa091->width = width; - sdLaMa091->height = height; - sdLaMa091->stride = stride; - sdLaMa091->numBytes = stride * height; - sdLaMa091->unusedBytes = stride - sdLaMa091->width; + sdLaMa091->width = width; + sdLaMa091->height = height; + sdLaMa091->stride = stride; + sdLaMa091->numBytes = stride * height; + sdLaMa091->unusedBytes = stride - sdLaMa091->width; - sdLaMa091->Mt = (uint8_t*)malloc(sdLaMa091->numBytes); -#ifdef DEFENSIVE_ALLOC - if (sdLaMa091->Mt == NULL) { - outputError("Cannot allocate sdLaMa091->Mt table"); - return EXIT_FAILURE; - } -#endif - memcpy(sdLaMa091->Mt, image_data, sdLaMa091->numBytes); - - sdLaMa091->Ot = (uint8_t*)malloc(sdLaMa091->numBytes); -#ifdef DEFENSIVE_ALLOC - if (sdLaMa091->Ot == NULL) { - outputError("Cannot allocate sdLaMa091->Ot table"); - return EXIT_FAILURE; - } -#endif - uint8_t* workOt = sdLaMa091->Ot; + sdLaMa091->Mt = (uint8_t*)malloc(sdLaMa091->numBytes); + #ifdef DEFENSIVE_ALLOC + if (sdLaMa091->Mt == NULL) { + outputError("Cannot allocate sdLaMa091->Mt table"); + return EXIT_FAILURE; + } + #endif + memcpy(sdLaMa091->Mt, image_data, sdLaMa091->numBytes); + + sdLaMa091->Ot = (uint8_t*)malloc(sdLaMa091->numBytes); + #ifdef DEFENSIVE_ALLOC + if (sdLaMa091->Ot == NULL) { + outputError("Cannot allocate sdLaMa091->Ot table"); + return EXIT_FAILURE; + } + #endif + uint8_t* workOt = sdLaMa091->Ot; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workOt) - *workOt = 0; + for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workOt) + *workOt = 0; - if (sdLaMa091->unusedBytes > 0) - workOt += sdLaMa091->unusedBytes; - } + if (sdLaMa091->unusedBytes > 0) + workOt += sdLaMa091->unusedBytes; + } - sdLaMa091->Vt = (uint8_t*)malloc(sdLaMa091->numBytes); -#ifdef DEFENSIVE_ALLOC - if (sdLaMa091->Vt == NULL) { - outputError("Cannot allocate sdLaMa091->Vt table"); - return EXIT_FAILURE; - } -#endif - uint8_t* workVt = sdLaMa091->Vt; + sdLaMa091->Vt = (uint8_t*)malloc(sdLaMa091->numBytes); + #ifdef DEFENSIVE_ALLOC + if (sdLaMa091->Vt == NULL) { + outputError("Cannot allocate sdLaMa091->Vt table"); + return EXIT_FAILURE; + } + #endif + uint8_t* workVt = sdLaMa091->Vt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workVt) - *workVt = sdLaMa091->Vmin; + for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workVt) + *workVt = sdLaMa091->Vmin; - if (sdLaMa091->unusedBytes > 0) - workVt += sdLaMa091->unusedBytes; - } + if (sdLaMa091->unusedBytes > 0) + workVt += sdLaMa091->unusedBytes; + } - return EXIT_SUCCESS; -} + return EXIT_SUCCESS; + } -int32_t sdLaMa091AllocInit_8u_C3R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - const uint32_t width, - const uint32_t height, - const uint32_t stride) { - int32_t success = sdLaMa091AllocInit_8u_C1R(sdLaMa091, image_data, width, - height, stride); - - if (success == EXIT_SUCCESS) { - sdLaMa091->imageType = C3R; - sdLaMa091->rgbWidth = sdLaMa091->width * CHANNELS; - sdLaMa091->rgbUnusedBytes = stride - sdLaMa091->rgbWidth; - sdLaMa091->width = 0; - sdLaMa091->unusedBytes = 0; - } + int32_t sdLaMa091AllocInit_8u_C3R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + const uint32_t width, + const uint32_t height, + const uint32_t stride) { + int32_t success = sdLaMa091AllocInit_8u_C1R(sdLaMa091, image_data, width, + height, stride); + + if (success == EXIT_SUCCESS) { + sdLaMa091->imageType = C3R; + sdLaMa091->rgbWidth = sdLaMa091->width * CHANNELS; + sdLaMa091->rgbUnusedBytes = stride - sdLaMa091->rgbWidth; + sdLaMa091->width = 0; + sdLaMa091->unusedBytes = 0; + } - return success; -} + return success; + } -int32_t sdLaMa091SetAmplificationFactor(sdLaMa091_t* sdLaMa091, - const uint32_t amplificationFactor) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot set a parameter of a NULL structure"); - return EXIT_FAILURE; - } -#endif + int32_t sdLaMa091SetAmplificationFactor(sdLaMa091_t* sdLaMa091, + const uint32_t amplificationFactor) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot set a parameter of a NULL structure"); + return EXIT_FAILURE; + } + #endif -#ifdef DEFENSIVE_PARAM - if (amplificationFactor == 0) { - outputError("Cannot set a parameter with a zero value"); - return EXIT_FAILURE; - } -#endif + #ifdef DEFENSIVE_PARAM + if (amplificationFactor == 0) { + outputError("Cannot set a parameter with a zero value"); + return EXIT_FAILURE; + } + #endif - sdLaMa091->N = amplificationFactor; + sdLaMa091->N = amplificationFactor; - return EXIT_SUCCESS; -} + return EXIT_SUCCESS; + } -uint32_t sdLaMa091GetAmplificationFactor(const sdLaMa091_t* sdLaMa091) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot get a parameter of a NULL structure"); - errno = ERROR_OCCURED; + uint32_t sdLaMa091GetAmplificationFactor(const sdLaMa091_t* sdLaMa091) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot get a parameter of a NULL structure"); + errno = ERROR_OCCURED; - return EXIT_FAILURE; - } -#endif + return EXIT_FAILURE; + } + #endif - return sdLaMa091->N; -} + return sdLaMa091->N; + } -int32_t sdLaMa091SetMaximalVariance(sdLaMa091_t* sdLaMa091, - const uint32_t maximalVariance) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot set a parameter of a NULL structure"); - return EXIT_FAILURE; - } -#endif + int32_t sdLaMa091SetMaximalVariance(sdLaMa091_t* sdLaMa091, + const uint32_t maximalVariance) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot set a parameter of a NULL structure"); + return EXIT_FAILURE; + } + #endif -#ifdef DEFENSIVE_PARAM - if (maximalVariance == 0) { - outputError("Cannot set a parameter with a zero value"); - return EXIT_FAILURE; - } -#endif + #ifdef DEFENSIVE_PARAM + if (maximalVariance == 0) { + outputError("Cannot set a parameter with a zero value"); + return EXIT_FAILURE; + } + #endif - sdLaMa091->Vmax = maximalVariance; + sdLaMa091->Vmax = maximalVariance; - return EXIT_SUCCESS; -} + return EXIT_SUCCESS; + } -uint32_t sdLaMa091GetMaximalVariance(const sdLaMa091_t* sdLaMa091) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot get a parameter of a NULL structure"); - errno = ERROR_OCCURED; + uint32_t sdLaMa091GetMaximalVariance(const sdLaMa091_t* sdLaMa091) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot get a parameter of a NULL structure"); + errno = ERROR_OCCURED; - return EXIT_FAILURE; - } -#endif + return EXIT_FAILURE; + } + #endif - return sdLaMa091->Vmax; -} + return sdLaMa091->Vmax; + } -int32_t sdLaMa091SetMinimalVariance(sdLaMa091_t* sdLaMa091, - const uint32_t minimalVariance) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot set a parameter of a NULL structure"); - return EXIT_FAILURE; - } -#endif + int32_t sdLaMa091SetMinimalVariance(sdLaMa091_t* sdLaMa091, + const uint32_t minimalVariance) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot set a parameter of a NULL structure"); + return EXIT_FAILURE; + } + #endif - sdLaMa091->Vmin = minimalVariance; + sdLaMa091->Vmin = minimalVariance; - return EXIT_SUCCESS; -} + return EXIT_SUCCESS; + } -uint32_t sdLaMa091GetMinimalVariance(const sdLaMa091_t* sdLaMa091) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot get a parameter of a NULL structure"); - errno = ERROR_OCCURED; + uint32_t sdLaMa091GetMinimalVariance(const sdLaMa091_t* sdLaMa091) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot get a parameter of a NULL structure"); + errno = ERROR_OCCURED; - return EXIT_FAILURE; - } -#endif + return EXIT_FAILURE; + } + #endif - return sdLaMa091->Vmin; -} + return sdLaMa091->Vmin; + } -int32_t sdLaMa091Update_8u_C1R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - uint8_t* segmentation_map) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot update a NULL structure"); - return EXIT_FAILURE; - } + int32_t sdLaMa091Update_8u_C1R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + uint8_t* segmentation_map) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot update a NULL structure"); + return EXIT_FAILURE; + } - if (image_data == NULL) { - outputError("Cannot update a structure with a NULL image"); - return EXIT_FAILURE; - } + if (image_data == NULL) { + outputError("Cannot update a structure with a NULL image"); + return EXIT_FAILURE; + } - if (segmentation_map == NULL) { - outputError("Cannot update a structure with a NULL segmentation map"); - return EXIT_FAILURE; - } + if (segmentation_map == NULL) { + outputError("Cannot update a structure with a NULL segmentation map"); + return EXIT_FAILURE; + } - if (sdLaMa091->Mt == NULL) { - outputError("Cannot update a structure with a NULL Mt table"); - return EXIT_FAILURE; - } + if (sdLaMa091->Mt == NULL) { + outputError("Cannot update a structure with a NULL Mt table"); + return EXIT_FAILURE; + } - if (sdLaMa091->Ot == NULL) { - outputError("Cannot update a structure with a NULL Ot table"); - return EXIT_FAILURE; - } + if (sdLaMa091->Ot == NULL) { + outputError("Cannot update a structure with a NULL Ot table"); + return EXIT_FAILURE; + } - if (sdLaMa091->Vt == NULL) { - outputError("Cannot update a structure with a NULL Vt table"); - return EXIT_FAILURE; - } -#endif + if (sdLaMa091->Vt == NULL) { + outputError("Cannot update a structure with a NULL Vt table"); + return EXIT_FAILURE; + } + #endif -#ifdef DEFENSIVE_PARAM - if (sdLaMa091->imageType != C1R) { - outputError("Cannot update a structure which is not C1R"); - return EXIT_FAILURE; - } + #ifdef DEFENSIVE_PARAM + if (sdLaMa091->imageType != C1R) { + outputError("Cannot update a structure which is not C1R"); + return EXIT_FAILURE; + } - if (sdLaMa091->width == 0 || sdLaMa091->height == 0 || - sdLaMa091->stride == 0) { - outputError("Cannot update a structure with zero values"); - return EXIT_FAILURE; - } + if (sdLaMa091->width == 0 || sdLaMa091->height == 0 || + sdLaMa091->stride == 0) { + outputError("Cannot update a structure with zero values"); + return EXIT_FAILURE; + } - if (sdLaMa091->stride < sdLaMa091->width) { - outputError("Cannot update a structure with a stride lower than the width"); - return EXIT_FAILURE; - } + if (sdLaMa091->stride < sdLaMa091->width) { + outputError("Cannot update a structure with a stride lower than the width"); + return EXIT_FAILURE; + } - if (sdLaMa091->Vmax < sdLaMa091->Vmin) { - outputError("Cannot update a structure with Vmax inferior to Vmin"); - return EXIT_FAILURE; - } -#endif + if (sdLaMa091->Vmax < sdLaMa091->Vmin) { + outputError("Cannot update a structure with Vmax inferior to Vmin"); + return EXIT_FAILURE; + } + #endif - const uint8_t* workImage = image_data; - uint8_t* workMt = sdLaMa091->Mt; + const uint8_t* workImage = image_data; + uint8_t* workMt = sdLaMa091->Mt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workImage, ++workMt) { - if (*workMt < *workImage) - ++(*workMt); - else if (*workMt > *workImage) - --(*workMt); - } + for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workImage, ++workMt) { + if (*workMt < *workImage) + ++(*workMt); + else if (*workMt > *workImage) + --(*workMt); + } - if (sdLaMa091->unusedBytes > 0) { - workImage += sdLaMa091->unusedBytes; - workMt += sdLaMa091->unusedBytes; - } - } + if (sdLaMa091->unusedBytes > 0) { + workImage += sdLaMa091->unusedBytes; + workMt += sdLaMa091->unusedBytes; + } + } - workImage = image_data; - workMt = sdLaMa091->Mt; - uint8_t* workOt = sdLaMa091->Ot; + workImage = image_data; + workMt = sdLaMa091->Mt; + uint8_t* workOt = sdLaMa091->Ot; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workImage, ++workMt, - ++workOt) - *workOt = absVal(*workMt - *workImage); + for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workImage, ++workMt, + ++workOt) + *workOt = absVal(*workMt - *workImage); - if (sdLaMa091->unusedBytes > 0) { - workImage += sdLaMa091->unusedBytes; - workMt += sdLaMa091->unusedBytes; - workOt += sdLaMa091->unusedBytes; - } - } + if (sdLaMa091->unusedBytes > 0) { + workImage += sdLaMa091->unusedBytes; + workMt += sdLaMa091->unusedBytes; + workOt += sdLaMa091->unusedBytes; + } + } - workOt = sdLaMa091->Ot; - uint8_t* workVt = sdLaMa091->Vt; + workOt = sdLaMa091->Ot; + uint8_t* workVt = sdLaMa091->Vt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workOt, ++workVt) { - uint32_t ampOt = sdLaMa091->N * *workOt; + for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workOt, ++workVt) { + uint32_t ampOt = sdLaMa091->N * *workOt; - if (*workVt < ampOt) - ++(*workVt); - else if (*workVt > ampOt) - --(*workVt); + if (*workVt < ampOt) + ++(*workVt); + else if (*workVt > ampOt) + --(*workVt); - *workVt = max(min(*workVt, sdLaMa091->Vmax), sdLaMa091->Vmin); - } + *workVt = max(min(*workVt, sdLaMa091->Vmax), sdLaMa091->Vmin); + } - if (sdLaMa091->unusedBytes > 0) { - workOt += sdLaMa091->unusedBytes; - workVt += sdLaMa091->unusedBytes; - } - } + if (sdLaMa091->unusedBytes > 0) { + workOt += sdLaMa091->unusedBytes; + workVt += sdLaMa091->unusedBytes; + } + } - workOt = sdLaMa091->Ot; - workVt = sdLaMa091->Vt; + workOt = sdLaMa091->Ot; + workVt = sdLaMa091->Vt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++segmentation_map, - ++workOt, ++workVt) { + for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++segmentation_map, + ++workOt, ++workVt) { - if (*workOt < *workVt) - *segmentation_map = BACKGROUND; - else - *segmentation_map = FOREGROUND; - } + if (*workOt < *workVt) + *segmentation_map = BACKGROUND; + else + *segmentation_map = FOREGROUND; + } - if (sdLaMa091->unusedBytes > 0) { - segmentation_map += sdLaMa091->unusedBytes; - workOt += sdLaMa091->unusedBytes; - workVt += sdLaMa091->unusedBytes; - } - } + if (sdLaMa091->unusedBytes > 0) { + segmentation_map += sdLaMa091->unusedBytes; + workOt += sdLaMa091->unusedBytes; + workVt += sdLaMa091->unusedBytes; + } + } - return EXIT_SUCCESS; -} + return EXIT_SUCCESS; + } -int32_t sdLaMa091Update_8u_C3R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - uint8_t* segmentation_map) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot update a NULL structure"); - return EXIT_FAILURE; - } + int32_t sdLaMa091Update_8u_C3R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + uint8_t* segmentation_map) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot update a NULL structure"); + return EXIT_FAILURE; + } - if (image_data == NULL) { - outputError("Cannot update a structure with a NULL image"); - return EXIT_FAILURE; - } + if (image_data == NULL) { + outputError("Cannot update a structure with a NULL image"); + return EXIT_FAILURE; + } - if (segmentation_map == NULL) { - outputError("Cannot update a structure with a NULL segmentation map"); - return EXIT_FAILURE; - } + if (segmentation_map == NULL) { + outputError("Cannot update a structure with a NULL segmentation map"); + return EXIT_FAILURE; + } - if (sdLaMa091->Mt == NULL) { - outputError("Cannot update a structure with a NULL Mt table"); - return EXIT_FAILURE; - } + if (sdLaMa091->Mt == NULL) { + outputError("Cannot update a structure with a NULL Mt table"); + return EXIT_FAILURE; + } - if (sdLaMa091->Ot == NULL) { - outputError("Cannot update a structure with a NULL Ot table"); - return EXIT_FAILURE; - } + if (sdLaMa091->Ot == NULL) { + outputError("Cannot update a structure with a NULL Ot table"); + return EXIT_FAILURE; + } - if (sdLaMa091->Vt == NULL) { - outputError("Cannot update a structure with a NULL Vt table"); - return EXIT_FAILURE; - } -#endif + if (sdLaMa091->Vt == NULL) { + outputError("Cannot update a structure with a NULL Vt table"); + return EXIT_FAILURE; + } + #endif -#ifdef DEFENSIVE_PARAM - if (sdLaMa091->imageType != C3R) { - outputError("Cannot update a structure which is not C3R"); - return EXIT_FAILURE; - } + #ifdef DEFENSIVE_PARAM + if (sdLaMa091->imageType != C3R) { + outputError("Cannot update a structure which is not C3R"); + return EXIT_FAILURE; + } - if (sdLaMa091->rgbWidth == 0 || sdLaMa091->height == 0 || - sdLaMa091->stride == 0) { - outputError("Cannot update a structure with zero values"); - return EXIT_FAILURE; - } + if (sdLaMa091->rgbWidth == 0 || sdLaMa091->height == 0 || + sdLaMa091->stride == 0) { + outputError("Cannot update a structure with zero values"); + return EXIT_FAILURE; + } - if (sdLaMa091->stride < sdLaMa091->rgbWidth) { - outputError("Cannot update a structure with a stride lower than the width"); - return EXIT_FAILURE; - } + if (sdLaMa091->stride < sdLaMa091->rgbWidth) { + outputError("Cannot update a structure with a stride lower than the width"); + return EXIT_FAILURE; + } - if (sdLaMa091->Vmax < sdLaMa091->Vmin) { - outputError("Cannot update a structure with Vmax inferior to Vmin"); - return EXIT_FAILURE; - } -#endif + if (sdLaMa091->Vmax < sdLaMa091->Vmin) { + outputError("Cannot update a structure with Vmax inferior to Vmin"); + return EXIT_FAILURE; + } + #endif - const uint8_t* workImage = image_data; - uint8_t* workMt = sdLaMa091->Mt; + const uint8_t* workImage = image_data; + uint8_t* workMt = sdLaMa091->Mt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workImage, ++workMt) { - if (*workMt < *workImage) - ++(*workMt); - else if (*workMt > *workImage) - --(*workMt); - } + for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workImage, ++workMt) { + if (*workMt < *workImage) + ++(*workMt); + else if (*workMt > *workImage) + --(*workMt); + } - if (sdLaMa091->rgbUnusedBytes > 0) { - workImage += sdLaMa091->rgbUnusedBytes; - workMt += sdLaMa091->rgbUnusedBytes; - } - } + if (sdLaMa091->rgbUnusedBytes > 0) { + workImage += sdLaMa091->rgbUnusedBytes; + workMt += sdLaMa091->rgbUnusedBytes; + } + } - workImage = image_data; - workMt = sdLaMa091->Mt; - uint8_t* workOt = sdLaMa091->Ot; + workImage = image_data; + workMt = sdLaMa091->Mt; + uint8_t* workOt = sdLaMa091->Ot; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workImage, ++workMt, - ++workOt) - *workOt = absVal(*workMt - *workImage); + for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workImage, ++workMt, + ++workOt) + *workOt = absVal(*workMt - *workImage); - if (sdLaMa091->rgbUnusedBytes > 0) { - workImage += sdLaMa091->rgbUnusedBytes; - workMt += sdLaMa091->rgbUnusedBytes; - workOt += sdLaMa091->rgbUnusedBytes; - } - } + if (sdLaMa091->rgbUnusedBytes > 0) { + workImage += sdLaMa091->rgbUnusedBytes; + workMt += sdLaMa091->rgbUnusedBytes; + workOt += sdLaMa091->rgbUnusedBytes; + } + } - workOt = sdLaMa091->Ot; - uint8_t* workVt = sdLaMa091->Vt; + workOt = sdLaMa091->Ot; + uint8_t* workVt = sdLaMa091->Vt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workOt, ++workVt) { - uint32_t ampOt = sdLaMa091->N * *workOt; + for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workOt, ++workVt) { + uint32_t ampOt = sdLaMa091->N * *workOt; - if (*workVt < ampOt) - ++(*workVt); - else if (*workVt > ampOt) - --(*workVt); + if (*workVt < ampOt) + ++(*workVt); + else if (*workVt > ampOt) + --(*workVt); - *workVt = max(min(*workVt, sdLaMa091->Vmax), sdLaMa091->Vmin); - } + *workVt = max(min(*workVt, sdLaMa091->Vmax), sdLaMa091->Vmin); + } - if (sdLaMa091->rgbUnusedBytes > 0) { - workOt += sdLaMa091->rgbUnusedBytes; - workVt += sdLaMa091->rgbUnusedBytes; - } - } + if (sdLaMa091->rgbUnusedBytes > 0) { + workOt += sdLaMa091->rgbUnusedBytes; + workVt += sdLaMa091->rgbUnusedBytes; + } + } - workOt = sdLaMa091->Ot; - workVt = sdLaMa091->Vt; + workOt = sdLaMa091->Ot; + workVt = sdLaMa091->Vt; - for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { + for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) { - uint32_t numColor = 0; + uint32_t numColor = 0; - bool isForeground = false; + bool isForeground = false; - for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workOt, ++workVt) { - if (*workOt >= *workVt) - isForeground = true; + for (uint32_t j = 0; j < sdLaMa091->rgbWidth; ++j, ++workOt, ++workVt) { + if (*workOt >= *workVt) + isForeground = true; - if (numColor == BLUE) { - if (isForeground) { - *segmentation_map = FOREGROUND; - *(++segmentation_map) = FOREGROUND; - *(++segmentation_map) = FOREGROUND; - ++segmentation_map; - } - else { - *segmentation_map = BACKGROUND; - *(++segmentation_map) = BACKGROUND; - *(++segmentation_map) = BACKGROUND; - ++segmentation_map; - } + if (numColor == BLUE) { + if (isForeground) { + *segmentation_map = FOREGROUND; + *(++segmentation_map) = FOREGROUND; + *(++segmentation_map) = FOREGROUND; + ++segmentation_map; + } + else { + *segmentation_map = BACKGROUND; + *(++segmentation_map) = BACKGROUND; + *(++segmentation_map) = BACKGROUND; + ++segmentation_map; + } - isForeground = false; - } + isForeground = false; + } - numColor = (numColor + 1) % CHANNELS; - } + numColor = (numColor + 1) % CHANNELS; + } - if (sdLaMa091->rgbUnusedBytes > 0) { - segmentation_map += sdLaMa091->rgbUnusedBytes; - workOt += sdLaMa091->rgbUnusedBytes; - workVt += sdLaMa091->rgbUnusedBytes; - } - } + if (sdLaMa091->rgbUnusedBytes > 0) { + segmentation_map += sdLaMa091->rgbUnusedBytes; + workOt += sdLaMa091->rgbUnusedBytes; + workVt += sdLaMa091->rgbUnusedBytes; + } + } - return EXIT_SUCCESS; -} + return EXIT_SUCCESS; + } -int32_t sdLaMa091Free(sdLaMa091_t* sdLaMa091) { -#ifdef DEFENSIVE_POINTER - if (sdLaMa091 == NULL) { - outputError("Cannot free a NULL strucutre"); - return EXIT_FAILURE; - } -#endif + int32_t sdLaMa091Free(sdLaMa091_t* sdLaMa091) { + #ifdef DEFENSIVE_POINTER + if (sdLaMa091 == NULL) { + outputError("Cannot free a NULL strucutre"); + return EXIT_FAILURE; + } + #endif - if (sdLaMa091->Mt != NULL) - free(sdLaMa091->Mt); - if (sdLaMa091->Ot != NULL) - free(sdLaMa091->Ot); - if (sdLaMa091->Vt != NULL) - free(sdLaMa091->Vt); + if (sdLaMa091->Mt != NULL) + free(sdLaMa091->Mt); + if (sdLaMa091->Ot != NULL) + free(sdLaMa091->Ot); + if (sdLaMa091->Vt != NULL) + free(sdLaMa091->Vt); - free(sdLaMa091); + free(sdLaMa091); - return EXIT_SUCCESS; + return EXIT_SUCCESS; + } + } + } } diff --git a/src/algorithms/SigmaDelta/sdLaMa091.h b/src/algorithms/SigmaDelta/sdLaMa091.h index 996c1b9405cb14a17407e7327764562128dc9fe2..84eeecb82c725586a758e8b27055f46707dbe6b5 100644 --- a/src/algorithms/SigmaDelta/sdLaMa091.h +++ b/src/algorithms/SigmaDelta/sdLaMa091.h @@ -6,47 +6,56 @@ #include <stdlib.h> #include <string.h> -#define BACKGROUND 0 -#define FOREGROUND 255 -#define ERROR_OCCURED 1 +namespace bgslibrary +{ + namespace algorithms + { + namespace sigmadelta + { + const int BACKGROUND = 0; + const int FOREGROUND = 255; + const int ERROR_OCCURED = 1; -typedef struct sdLaMa091 sdLaMa091_t; + typedef struct sdLaMa091 sdLaMa091_t; -sdLaMa091_t* sdLaMa091New(void); + sdLaMa091_t* sdLaMa091New(void); -int32_t sdLaMa091AllocInit_8u_C1R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - const uint32_t width, - const uint32_t height, - const uint32_t stride); + int32_t sdLaMa091AllocInit_8u_C1R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + const uint32_t width, + const uint32_t height, + const uint32_t stride); -int32_t sdLaMa091AllocInit_8u_C3R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - const uint32_t width, - const uint32_t height, - const uint32_t stride); + int32_t sdLaMa091AllocInit_8u_C3R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + const uint32_t width, + const uint32_t height, + const uint32_t stride); -int32_t sdLaMa091SetAmplificationFactor(sdLaMa091_t* sdLaMa091, - const uint32_t amplificationFactor); + int32_t sdLaMa091SetAmplificationFactor(sdLaMa091_t* sdLaMa091, + const uint32_t amplificationFactor); -uint32_t sdLaMa091GetAmplificationFactor(const sdLaMa091_t* sdLaMa091); + uint32_t sdLaMa091GetAmplificationFactor(const sdLaMa091_t* sdLaMa091); -int32_t sdLaMa091SetMaximalVariance(sdLaMa091_t* sdLaMa091, - const uint32_t maximalVariance); + int32_t sdLaMa091SetMaximalVariance(sdLaMa091_t* sdLaMa091, + const uint32_t maximalVariance); -uint32_t sdLaMa091GetMaximalVariance(const sdLaMa091_t* sdLaMa091); + uint32_t sdLaMa091GetMaximalVariance(const sdLaMa091_t* sdLaMa091); -int32_t sdLaMa091SetMinimalVariance(sdLaMa091_t* sdLaMa091, - const uint32_t minimalVariance); + int32_t sdLaMa091SetMinimalVariance(sdLaMa091_t* sdLaMa091, + const uint32_t minimalVariance); -uint32_t sdLaMa091GetMinimalVariance(const sdLaMa091_t* sdLaMa091); + uint32_t sdLaMa091GetMinimalVariance(const sdLaMa091_t* sdLaMa091); -int32_t sdLaMa091Update_8u_C1R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - uint8_t* segmentation_map); + int32_t sdLaMa091Update_8u_C1R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + uint8_t* segmentation_map); -int32_t sdLaMa091Update_8u_C3R(sdLaMa091_t* sdLaMa091, - const uint8_t* image_data, - uint8_t* segmentation_map); + int32_t sdLaMa091Update_8u_C3R(sdLaMa091_t* sdLaMa091, + const uint8_t* image_data, + uint8_t* segmentation_map); -int32_t sdLaMa091Free(sdLaMa091_t* sdLaMa091); + int32_t sdLaMa091Free(sdLaMa091_t* sdLaMa091); + } + } +} diff --git a/src/algorithms/SuBSENSE.cpp b/src/algorithms/SuBSENSE.cpp index f753a8dbb67a4a540f5000a641875235c204a7a7..c9429023ecc53f9e266ba959ca143192b2311258 100644 --- a/src/algorithms/SuBSENSE.cpp +++ b/src/algorithms/SuBSENSE.cpp @@ -5,12 +5,12 @@ using namespace bgslibrary::algorithms; SuBSENSE::SuBSENSE() : IBGS(quote(SuBSENSE)), pSubsense(0), - fRelLBSPThreshold(BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD), - nDescDistThresholdOffset(BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD_OFFSET), - nMinColorDistThreshold(BGSSUBSENSE_DEFAULT_MIN_COLOR_DIST_THRESHOLD), - nBGSamples(BGSSUBSENSE_DEFAULT_NB_BG_SAMPLES), - nRequiredBGSamples(BGSSUBSENSE_DEFAULT_REQUIRED_NB_BG_SAMPLES), - nSamplesForMovingAvgs(BGSSUBSENSE_DEFAULT_N_SAMPLES_FOR_MV_AVGS) + fRelLBSPThreshold(lbsp::BGSSUBSENSE_DEFAULT_LBSP_REL_SIMILARITY_THRESHOLD), + nDescDistThresholdOffset(lbsp::BGSSUBSENSE_DEFAULT_DESC_DIST_THRESHOLD_OFFSET), + nMinColorDistThreshold(lbsp::BGSSUBSENSE_DEFAULT_MIN_COLOR_DIST_THRESHOLD), + nBGSamples(lbsp::BGSSUBSENSE_DEFAULT_NB_BG_SAMPLES), + nRequiredBGSamples(lbsp::BGSSUBSENSE_DEFAULT_REQUIRED_NB_BG_SAMPLES), + nSamplesForMovingAvgs(lbsp::BGSSUBSENSE_DEFAULT_N_SAMPLES_FOR_MV_AVGS) { debug_construction(SuBSENSE); initLoadSaveConfig(algorithmName); @@ -27,7 +27,7 @@ void SuBSENSE::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &i init(img_input, img_output, img_bgmodel); if (firstTime) { - pSubsense = new BackgroundSubtractorSuBSENSE( + pSubsense = new lbsp::BackgroundSubtractorSuBSENSE( fRelLBSPThreshold, nDescDistThresholdOffset, nMinColorDistThreshold, nBGSamples, nRequiredBGSamples, nSamplesForMovingAvgs); diff --git a/src/algorithms/SuBSENSE.h b/src/algorithms/SuBSENSE.h index 73edc268061553410046b6684e946b7799ba023f..e309776da5a607d8f8c10ecf5fc4402a94b54526 100644 --- a/src/algorithms/SuBSENSE.h +++ b/src/algorithms/SuBSENSE.h @@ -10,7 +10,7 @@ namespace bgslibrary class SuBSENSE : public IBGS { private: - BackgroundSubtractorSuBSENSE* pSubsense; + lbsp::BackgroundSubtractorSuBSENSE* pSubsense; float fRelLBSPThreshold; int nDescDistThresholdOffset; diff --git a/src/algorithms/T2F/FuzzyUtils.h b/src/algorithms/T2F/FuzzyUtils.h deleted file mode 100644 index 43918535957ddb34827eb8f8ecfad3b8c7c62663..0000000000000000000000000000000000000000 --- a/src/algorithms/T2F/FuzzyUtils.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "../../tools/PixelUtils.h" - -class FuzzyUtils -{ -public: - FuzzyUtils(void); - ~FuzzyUtils(void); - - void LBP(IplImage* InputImage, IplImage* LBP); - void getBinValue(float* neighberGrayPixel, float* BinaryValue, int m, int n); - - void SimilarityDegreesImage(IplImage* CurrentImage, IplImage* BGImage, IplImage* DeltaImage, int n, int color_space); - void RatioPixels(float* CurrentPixel, float* BGPixel, float* DeltaPixel, int n); - - void getFuzzyIntegralSugeno(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage); - void getFuzzyIntegralChoquet(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage); - void FuzzyMeasureG(float g1, float g2, float g3, float *G); - void Trier(float* g, int n, int* index); - float min(float *a, float *b); - float max(float *g, int n); - void gDeDeux(float* a, float* b, float* lambda); - void getLambda(float* g); - - void AdaptativeSelectiveBackgroundModelUpdate(IplImage* CurrentImage, IplImage* BGImage, IplImage* OutputImage, IplImage* Integral, float seuil, float alpha); -}; diff --git a/src/algorithms/T2F/MRF.cpp b/src/algorithms/T2F/MRF.cpp index aeaa0d19ea0a7d49e50404e741489e023deb2b25..2d6d18b4f5c306de93922582c39bbf9a6daf6d69 100644 --- a/src/algorithms/T2F/MRF.cpp +++ b/src/algorithms/T2F/MRF.cpp @@ -2,7 +2,7 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; //init the basic MRF MRF::MRF() diff --git a/src/algorithms/T2F/MRF.h b/src/algorithms/T2F/MRF.h index 82fd283d6f2713d0355eed1dbc12c0a5ffb48746..2f773c86cb47311d0508e48bfece28c84ade9074 100644 --- a/src/algorithms/T2F/MRF.h +++ b/src/algorithms/T2F/MRF.h @@ -5,88 +5,91 @@ #include "T2FMRF.h" -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { - // base class - class MRF + namespace dp { - public: - IplImage *in_image, *out_image; - //image's width and height - int width, height; + // base class + class MRF + { + public: + IplImage *in_image, *out_image; + //image's width and height + int width, height; - public: - MRF(); + public: + MRF(); - protected: + protected: - ////////////////////////////////////////////////////////////////////////// - //the number of labeling - int no_regions; - //potential of Space Constraint - double beta; - //terminal condition when (deltaE < t) - double t; + ////////////////////////////////////////////////////////////////////////// + //the number of labeling + int no_regions; + //potential of Space Constraint + double beta; + //terminal condition when (deltaE < t) + double t; - ////////////////////////////////////////////////////////////////////////// - //for gibbs - double T0; - //current temperature - double T; - double c; + ////////////////////////////////////////////////////////////////////////// + //for gibbs + double T0; + //current temperature + double T; + double c; - ////////////////////////////////////////////////////////////////////////// - // alpha value for MMD - double alpha; + ////////////////////////////////////////////////////////////////////////// + // alpha value for MMD + double alpha; - ////////////////////////////////////////////////////////////////////////// - //current global energy - double E; - //old global energy - double E_old; - //number of iteration - int K; + ////////////////////////////////////////////////////////////////////////// + //current global energy + double E; + //old global energy + double E_old; + //number of iteration + int K; - ////////////////////////////////////////////////////////////////////////// - //labeling image - int **classes; - //input image - int **in_image_data; - //evidence - float ** local_evidence; - }; + ////////////////////////////////////////////////////////////////////////// + //labeling image + int **classes; + //input image + int **in_image_data; + //evidence + float ** local_evidence; + }; - /************************************************************************/ - /* the Markov Random Field with time constraints for T2FGMM */ - /************************************************************************/ - class MRF_TC : public MRF - { - private: - double beta_time; + /************************************************************************/ + /* the Markov Random Field with time constraints for T2FGMM */ + /************************************************************************/ + class MRF_TC : public MRF + { + private: + double beta_time; - public: - IplImage *background2; - RgbImage background; - int **old_labeling; + public: + IplImage *background2; + RgbImage background; + int **old_labeling; - public: - MRF_TC(); - ~MRF_TC(); - double TimeEnergy2(int i, int j, int label); - void OnIterationOver2(void); - void Build_Classes_OldLabeling_InImage_LocalEnergy(); - void InitEvidence2(GMM *gmm, HMM *hmm, IplImage *labeling); - void CreateOutput2(); - double CalculateEnergy2(); - double LocalEnergy2(int i, int j, int label); - double Doubleton2(int i, int j, int label); + public: + MRF_TC(); + ~MRF_TC(); + double TimeEnergy2(int i, int j, int label); + void OnIterationOver2(void); + void Build_Classes_OldLabeling_InImage_LocalEnergy(); + void InitEvidence2(GMM *gmm, HMM *hmm, IplImage *labeling); + void CreateOutput2(); + double CalculateEnergy2(); + double LocalEnergy2(int i, int j, int label); + double Doubleton2(int i, int j, int label); - void Gibbs2(); - void ICM2(); - void Metropolis2(bool mmd); - }; + void Gibbs2(); + void ICM2(); + void Metropolis2(bool mmd); + }; + } } } diff --git a/src/algorithms/T2F/T2FGMM.cpp b/src/algorithms/T2F/T2FGMM.cpp index abba7129770d8aec7496873f723f026c7217a775..7d7b35e4f95d6e1a53258cefbf52c1e9b1d6d618 100644 --- a/src/algorithms/T2F/T2FGMM.cpp +++ b/src/algorithms/T2F/T2FGMM.cpp @@ -2,311 +2,320 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +//using namespace bgslibrary::algorithms::dp; -int compareT2FGMM(const void* _gmm1, const void* _gmm2) +namespace bgslibrary { - GMM gmm1 = *(GMM*)_gmm1; - GMM gmm2 = *(GMM*)_gmm2; - - if (gmm1.significants < gmm2.significants) - return 1; - else if (gmm1.significants == gmm2.significants) - return 0; - else - return -1; -} - -T2FGMM::T2FGMM() -{ - m_modes = NULL; -} - -T2FGMM::~T2FGMM() -{ - delete[] m_modes; -} - -void T2FGMM::Initalize(const BgsParams& param) -{ - m_params = (T2FGMMParams&)param; - - // Tbf - the threshold - m_bg_threshold = 0.75f; // 1-cf from the paper - - // Tgenerate - the threshold - m_variance = 36.0f; // sigma for the new mode - - // GMM for each pixel - m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; - - // used modes per pixel - m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); - - m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + namespace algorithms + { + namespace dp + { + int compareT2FGMM(const void* _gmm1, const void* _gmm2) + { + GMM gmm1 = *(GMM*)_gmm1; + GMM gmm2 = *(GMM*)_gmm2; - // Factor control for the T2FGMM-UM [0,3] - //km = (float) 1.5; - km = (float)m_params.KM(); + if (gmm1.significants < gmm2.significants) + return 1; + else if (gmm1.significants == gmm2.significants) + return 0; + else + return -1; + } - // Factor control for the T2FGMM-UV [0.3,1] - //kv = (float) 0.6; - kv = (float)m_params.KV(); -} + T2FGMM::T2FGMM() + { + m_modes = NULL; + } -RgbImage* T2FGMM::Background() -{ - return &m_background; -} + T2FGMM::~T2FGMM() + { + delete[] m_modes; + } -void T2FGMM::InitModel(const RgbImage& data) -{ - m_modes_per_pixel.Clear(); + void T2FGMM::Initalize(const BgsParams& param) + { + m_params = (T2FGMMParams&)param; - for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) - { - m_modes[i].weight = 0; - m_modes[i].variance = 0; - m_modes[i].muR = 0; - m_modes[i].muG = 0; - m_modes[i].muB = 0; - m_modes[i].significants = 0; - } -} + // Tbf - the threshold + m_bg_threshold = 0.75f; // 1-cf from the paper -void T2FGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) -{ - // it doesn't make sense to have conditional updates in the GMM framework -} + // Tgenerate - the threshold + m_variance = 36.0f; // sigma for the new mode -void T2FGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, - unsigned char& low_threshold, unsigned char& high_threshold) -{ - // calculate distances to the modes (+ sort???) - // here we need to go in descending order!!! - long pos; - bool bFitsPDF = false; - bool bBackgroundLow = false; - bool bBackgroundHigh = false; - - float fOneMinAlpha = 1 - m_params.Alpha(); - float totalWeight = 0.0f; - - // calculate number of Gaussians to include in the background model - int backgroundGaussians = 0; - double sum = 0.0; - for (int i = 0; i < numModes; ++i) - { - if (sum < m_bg_threshold) - { - backgroundGaussians++; - sum += m_modes[posPixel + i].weight; - } - else - break; - } + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; - // update all distributions and check for match with current pixel - for (int iModes = 0; iModes < numModes; iModes++) - { - pos = posPixel + iModes; - float weight = m_modes[pos].weight; + // used modes per pixel + m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); - // fit not found yet - if (!bFitsPDF) - { - //check if it belongs to some of the modes - //calculate distance - float var = m_modes[pos].variance; - float muR = m_modes[pos].muR; - float muG = m_modes[pos].muG; - float muB = m_modes[pos].muB; - - //float km = 2; - //float kv = 0.9; - - float dR = fabs(muR - pixel(0)); - float dG = fabs(muG - pixel(1)); - float dB = fabs(muB - pixel(2)); - - // calculate the squared distance - float HR; - float HG; - float HB; - - // T2FGMM-UM - if (m_params.Type() == TYPE_T2FGMM_UM) - { - if ((pixel(0) < muR - km*var) || (pixel(0) > muR + km*var)) - HR = 2 * km*dR / var; - else - HR = dR*dR / (2 * var*var) + km*dR / var + km*km / 2; + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); - if ((pixel(1) < muG - km*var) || (pixel(1) > muG + km*var)) - HG = 2 * km*dG / var; - else - HG = dG*dG / (2 * var*var) + km*dG / var + km*km / 2; + // Factor control for the T2FGMM-UM [0,3] + //km = (float) 1.5; + km = (float)m_params.KM(); - if ((pixel(2) < muB - km*var) || (pixel(2) > muB + km*var)) - HB = 2 * km*dB / var; - else - HB = dB*dB / (2 * var*var) + km*dB / var + km*km / 2; + // Factor control for the T2FGMM-UV [0.3,1] + //kv = (float) 0.6; + kv = (float)m_params.KV(); } - // T2FGMM-UV - if (m_params.Type() == TYPE_T2FGMM_UV) + RgbImage* T2FGMM::Background() { - HR = (1 / (kv*kv) - kv*kv) * (pixel(0) - muR) * (pixel(0) - muR) / (2 * var); - HG = (1 / (kv*kv) - kv*kv) * (pixel(1) - muG) * (pixel(1) - muG) / (2 * var); - HB = (1 / (kv*kv) - kv*kv) * (pixel(2) - muB) * (pixel(2) - muB) / (2 * var); + return &m_background; } - // calculate the squared distance - float dist = (HR*HR + HG*HG + HB*HB); + void T2FGMM::InitModel(const RgbImage& data) + { + m_modes_per_pixel.Clear(); - if (dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) - bBackgroundHigh = true; + for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + { + m_modes[i].weight = 0; + m_modes[i].variance = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + m_modes[i].significants = 0; + } + } - // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution - if (dist < m_params.LowThreshold()*var) + void T2FGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) { - bFitsPDF = true; - - // check if this Gaussian is part of the background model - if (iModes < backgroundGaussians) - bBackgroundLow = true; - - //update distribution - float k = m_params.Alpha() / weight; - weight = fOneMinAlpha*weight + m_params.Alpha(); - m_modes[pos].weight = weight; - m_modes[pos].muR = muR - k*(dR); - m_modes[pos].muG = muG - k*(dG); - m_modes[pos].muB = muB - k*(dB); - - //limit the variance - float sigmanew = var + k*(dist - var); - m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + // it doesn't make sense to have conditional updates in the GMM framework } - else + + void T2FGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& low_threshold, unsigned char& high_threshold) { - weight = fOneMinAlpha*weight; - if (weight < 0.0) + // calculate distances to the modes (+ sort???) + // here we need to go in descending order!!! + long pos; + bool bFitsPDF = false; + bool bBackgroundLow = false; + bool bBackgroundHigh = false; + + float fOneMinAlpha = 1 - m_params.Alpha(); + float totalWeight = 0.0f; + + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for (int i = 0; i < numModes; ++i) { - weight = 0.0; - numModes--; + if (sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posPixel + i].weight; + } + else + break; } - m_modes[pos].weight = weight; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); - } - } - else - { - weight = fOneMinAlpha*weight; - if (weight < 0.0) - { - weight = 0.0; - numModes--; - } - m_modes[pos].weight = weight; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); - } - - totalWeight += weight; - } + // update all distributions and check for match with current pixel + for (int iModes = 0; iModes < numModes; iModes++) + { + pos = posPixel + iModes; + float weight = m_modes[pos].weight; + + // fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].variance; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + //float km = 2; + //float kv = 0.9; + + float dR = fabs(muR - pixel(0)); + float dG = fabs(muG - pixel(1)); + float dB = fabs(muB - pixel(2)); + + // calculate the squared distance + float HR; + float HG; + float HB; + + // T2FGMM-UM + if (m_params.Type() == TYPE_T2FGMM_UM) + { + if ((pixel(0) < muR - km*var) || (pixel(0) > muR + km*var)) + HR = 2 * km*dR / var; + else + HR = dR*dR / (2 * var*var) + km*dR / var + km*km / 2; + + if ((pixel(1) < muG - km*var) || (pixel(1) > muG + km*var)) + HG = 2 * km*dG / var; + else + HG = dG*dG / (2 * var*var) + km*dG / var + km*km / 2; + + if ((pixel(2) < muB - km*var) || (pixel(2) > muB + km*var)) + HB = 2 * km*dB / var; + else + HB = dB*dB / (2 * var*var) + km*dB / var + km*km / 2; + } + + // T2FGMM-UV + if (m_params.Type() == TYPE_T2FGMM_UV) + { + HR = (1 / (kv*kv) - kv*kv) * (pixel(0) - muR) * (pixel(0) - muR) / (2 * var); + HG = (1 / (kv*kv) - kv*kv) * (pixel(1) - muG) * (pixel(1) - muG) / (2 * var); + HB = (1 / (kv*kv) - kv*kv) * (pixel(2) - muB) * (pixel(2) - muB) / (2 * var); + } + + // calculate the squared distance + float dist = (HR*HR + HG*HG + HB*HB); + + if (dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution + if (dist < m_params.LowThreshold()*var) + { + bFitsPDF = true; + + // check if this Gaussian is part of the background model + if (iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha() / weight; + weight = fOneMinAlpha*weight + m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit the variance + float sigmanew = var + k*(dist - var); + m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight = 0.0; + numModes--; + } + + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight = 0.0; + numModes--; + } + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + + totalWeight += weight; + } - // renormalize weights so they add to one - double invTotalWeight = 1.0 / totalWeight; - for (int iLocal = 0; iLocal < numModes; iLocal++) - { - m_modes[posPixel + iLocal].weight *= (float)invTotalWeight; - m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight - / sqrt(m_modes[posPixel + iLocal].variance); - } + // renormalize weights so they add to one + double invTotalWeight = 1.0 / totalWeight; + for (int iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invTotalWeight; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight + / sqrt(m_modes[posPixel + iLocal].variance); + } - // Sort significance values so they are in desending order. - qsort(&m_modes[posPixel], numModes, sizeof(GMM), compareT2FGMM); + // Sort significance values so they are in desending order. + qsort(&m_modes[posPixel], numModes, sizeof(GMM), compareT2FGMM); - // make new mode if needed and exit - if (!bFitsPDF) - { - if (numModes < m_params.MaxModes()) - numModes++; - //else - // the weakest mode will be replaced - - pos = posPixel + numModes - 1; - - m_modes[pos].muR = pixel.ch[0]; - m_modes[pos].muG = pixel.ch[1]; - m_modes[pos].muB = pixel.ch[2]; - m_modes[pos].variance = m_variance; - m_modes[pos].significants = 0; // will be set below - - if (numModes == 1) - m_modes[pos].weight = 1; - else - m_modes[pos].weight = m_params.Alpha(); - - //renormalize weights - int iLocal; - float sum = 0.0; - for (iLocal = 0; iLocal < numModes; iLocal++) - sum += m_modes[posPixel + iLocal].weight; - - double invSum = 1.0 / sum; - for (iLocal = 0; iLocal < numModes; iLocal++) - { - m_modes[posPixel + iLocal].weight *= (float)invSum; - m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight / sqrt(m_modes[posPixel + iLocal].variance); - } - } + // make new mode if needed and exit + if (!bFitsPDF) + { + if (numModes < m_params.MaxModes()) + numModes++; + //else + // the weakest mode will be replaced + + pos = posPixel + numModes - 1; + + m_modes[pos].muR = pixel.ch[0]; + m_modes[pos].muG = pixel.ch[1]; + m_modes[pos].muB = pixel.ch[2]; + m_modes[pos].variance = m_variance; + m_modes[pos].significants = 0; // will be set below + + if (numModes == 1) + m_modes[pos].weight = 1; + else + m_modes[pos].weight = m_params.Alpha(); + + //renormalize weights + int iLocal; + float sum = 0.0; + for (iLocal = 0; iLocal < numModes; iLocal++) + sum += m_modes[posPixel + iLocal].weight; + + double invSum = 1.0 / sum; + for (iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invSum; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight / sqrt(m_modes[posPixel + iLocal].variance); + } + } - // Sort significance values so they are in desending order. - qsort(&(m_modes[posPixel]), numModes, sizeof(GMM), compareT2FGMM); + // Sort significance values so they are in desending order. + qsort(&(m_modes[posPixel]), numModes, sizeof(GMM), compareT2FGMM); - if (bBackgroundLow) - low_threshold = BACKGROUND; - else - low_threshold = FOREGROUND; + if (bBackgroundLow) + low_threshold = BACKGROUND; + else + low_threshold = FOREGROUND; - if (bBackgroundHigh) - high_threshold = BACKGROUND; - else - high_threshold = FOREGROUND; -} + if (bBackgroundHigh) + high_threshold = BACKGROUND; + else + high_threshold = FOREGROUND; + } -/////////////////////////////////////////////////////////////////////////////// -//Input: -// data - a pointer to the data of a RGB image of the same size -//Output: -// output - a pointer to the data of a gray value image of the same size -// (the memory should already be reserved) -// values: 255-foreground, 125-shadow, 0-background -/////////////////////////////////////////////////////////////////////////////// -void T2FGMM::Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask) -{ - unsigned char low_threshold, high_threshold; - long posPixel; + /////////////////////////////////////////////////////////////////////////////// + //Input: + // data - a pointer to the data of a RGB image of the same size + //Output: + // output - a pointer to the data of a gray value image of the same size + // (the memory should already be reserved) + // values: 255-foreground, 125-shadow, 0-background + /////////////////////////////////////////////////////////////////////////////// + void T2FGMM::Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask) + { + unsigned char low_threshold, high_threshold; + long posPixel; - // update each pixel of the image - for (unsigned int r = 0; r < m_params.Height(); ++r) - { - for (unsigned int c = 0; c < m_params.Width(); ++c) - { - // update model + background subtract - posPixel = (r*m_params.Width() + c) * m_params.MaxModes(); + // update each pixel of the image + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for (unsigned int c = 0; c < m_params.Width(); ++c) + { + // update model + background subtract + posPixel = (r*m_params.Width() + c) * m_params.MaxModes(); - SubtractPixel(posPixel, data(r, c), m_modes_per_pixel(r, c), low_threshold, high_threshold); + SubtractPixel(posPixel, data(r, c), m_modes_per_pixel(r, c), low_threshold, high_threshold); - low_threshold_mask(r, c) = low_threshold; - high_threshold_mask(r, c) = high_threshold; + low_threshold_mask(r, c) = low_threshold; + high_threshold_mask(r, c) = high_threshold; - m_background(r, c, 0) = (unsigned char)m_modes[posPixel].muR; - m_background(r, c, 1) = (unsigned char)m_modes[posPixel].muG; - m_background(r, c, 2) = (unsigned char)m_modes[posPixel].muB; + m_background(r, c, 0) = (unsigned char)m_modes[posPixel].muR; + m_background(r, c, 1) = (unsigned char)m_modes[posPixel].muG; + m_background(r, c, 2) = (unsigned char)m_modes[posPixel].muB; + } + } + } } } } diff --git a/src/algorithms/T2F/T2FGMM.h b/src/algorithms/T2F/T2FGMM.h index 5c13069911be09f185c3d9c961e24cca6e09f352..4a7cfc38414fec28e34deaf442d90910b6df2608 100644 --- a/src/algorithms/T2F/T2FGMM.h +++ b/src/algorithms/T2F/T2FGMM.h @@ -6,101 +6,104 @@ #include "../dp/Bgs.h" #include "../dp/GrimsonGMM.h" -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { - const int TYPE_T2FGMM_UM = 0; - const int TYPE_T2FGMM_UV = 1; - - // --- User adjustable parameters used by the T2F GMM BGS algorithm --- - class T2FGMMParams : public BgsParams - { - public: - float &LowThreshold() { return m_low_threshold; } - float &HighThreshold() { return m_high_threshold; } - - float &Alpha() { return m_alpha; } - int &MaxModes() { return m_max_modes; } - int &Type() { return m_type; } - float &KM() { return m_km; } - float &KV() { return m_kv; } - - private: - // Threshold on the squared dist. to decide when a sample is close to an existing - // components. If it is not close to any a new component will be generated. - // Smaller threshold values lead to more generated components and higher threshold values - // lead to a small number of components but they can grow too large. - // - // It is usual easiest to think of these thresholds as being the number of variances away - // from the mean of a pixel before it is considered to be from the foreground. - float m_low_threshold; - float m_high_threshold; - - // alpha - speed of update - if the time interval you want to average over is T - // set alpha=1/T. - float m_alpha; - - // Maximum number of modes (Gaussian components) that will be used per pixel - int m_max_modes; - - // T2FGMM_UM / T2FGMM_UV - int m_type; - - // Factor control for the T2FGMM-UM - float m_km; - - // Factor control for the T2FGMM-UV - float m_kv; - }; - - // --- T2FGMM BGS algorithm --- - class T2FGMM : public Bgs + namespace dp { - public: - T2FGMM(); - ~T2FGMM(); - - void Initalize(const BgsParams& param); - - void InitModel(const RgbImage& data); - void Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask); - void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); - - RgbImage* Background(); - - private: - void SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, unsigned char& lowThreshold, unsigned char& highThreshold); - - // User adjustable parameters - T2FGMMParams m_params; - - // Threshold when the component becomes significant enough to be included into - // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 - // For alpha=0.001 it means that the mode should exist for approximately 105 frames before - // it is considered foreground - float m_bg_threshold; //1-cf from the paper - - // Initial variance for the newly generated components. - // It will will influence the speed of adaptation. A good guess should be made. - // A simple way is to estimate the typical standard deviation from the images. - float m_variance; - - // Dynamic array for the mixture of Gaussians - GMM* m_modes; - - // Number of Gaussian components per pixel - BwImage m_modes_per_pixel; - - // Current background model - RgbImage m_background; - - // Factor control for the T2FGMM-UM - float km; - - // Factor control for the T2FGMM-UV - float kv; - }; + const int TYPE_T2FGMM_UM = 0; + const int TYPE_T2FGMM_UV = 1; + + // --- User adjustable parameters used by the T2F GMM BGS algorithm --- + class T2FGMMParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &MaxModes() { return m_max_modes; } + int &Type() { return m_type; } + float &KM() { return m_km; } + float &KV() { return m_kv; } + + private: + // Threshold on the squared dist. to decide when a sample is close to an existing + // components. If it is not close to any a new component will be generated. + // Smaller threshold values lead to more generated components and higher threshold values + // lead to a small number of components but they can grow too large. + // + // It is usual easiest to think of these thresholds as being the number of variances away + // from the mean of a pixel before it is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + // alpha - speed of update - if the time interval you want to average over is T + // set alpha=1/T. + float m_alpha; + + // Maximum number of modes (Gaussian components) that will be used per pixel + int m_max_modes; + + // T2FGMM_UM / T2FGMM_UV + int m_type; + + // Factor control for the T2FGMM-UM + float m_km; + + // Factor control for the T2FGMM-UV + float m_kv; + }; + + // --- T2FGMM BGS algorithm --- + class T2FGMM : public Bgs + { + public: + T2FGMM(); + ~T2FGMM(); + + void Initalize(const BgsParams& param); + + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background(); + + private: + void SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, unsigned char& lowThreshold, unsigned char& highThreshold); + + // User adjustable parameters + T2FGMMParams m_params; + + // Threshold when the component becomes significant enough to be included into + // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + float m_bg_threshold; //1-cf from the paper + + // Initial variance for the newly generated components. + // It will will influence the speed of adaptation. A good guess should be made. + // A simple way is to estimate the typical standard deviation from the images. + float m_variance; + + // Dynamic array for the mixture of Gaussians + GMM* m_modes; + + // Number of Gaussian components per pixel + BwImage m_modes_per_pixel; + + // Current background model + RgbImage m_background; + + // Factor control for the T2FGMM-UM + float km; + + // Factor control for the T2FGMM-UV + float kv; + }; + } } } diff --git a/src/algorithms/T2F/T2FMRF.cpp b/src/algorithms/T2F/T2FMRF.cpp index 417aa27dc84e92851a0c42732bd7d4c932054b1b..3a264c14b88c8543f5d78fd97dcfc2703f5bd45c 100644 --- a/src/algorithms/T2F/T2FMRF.cpp +++ b/src/algorithms/T2F/T2FMRF.cpp @@ -2,402 +2,411 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +//using namespace bgslibrary::algorithms::dp; -int compareT2FMRF(const void* _gmm1, const void* _gmm2) +namespace bgslibrary { - GMM gmm1 = *(GMM*)_gmm1; - GMM gmm2 = *(GMM*)_gmm2; - - if (gmm1.significants < gmm2.significants) - return 1; - else if (gmm1.significants == gmm2.significants) - return 0; - else - return -1; -} - -GMM* T2FMRF::gmm() -{ - return m_modes; -} -HMM* T2FMRF::hmm() -{ - return m_state; -} - -T2FMRF::T2FMRF() -{ - m_modes = NULL; -} - -T2FMRF::~T2FMRF() -{ - delete[] m_modes; -} - -void T2FMRF::Initalize(const BgsParams& param) -{ - m_params = (T2FMRFParams&)param; - - // Tbf - the threshold - m_bg_threshold = 0.75f; // 1-cf from the paper - - // Tgenerate - the threshold - m_variance = 36.0f; // sigma for the new mode - - // GMM for each pixel - m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; - - //HMM for each pixel - m_state = new HMM[m_params.Size()]; - - // used modes per pixel - m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); - - m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); - - // Factor control for the T2FGMM-UM [0,3] - // km = (float) 2; //1.5; - km = (float)m_params.KM(); - - // Factor control for the T2FGMM-UV [0.3,1] - // kv = (float) 0.9; //0.6; - kv = (float)m_params.KV(); -} - -RgbImage* T2FMRF::Background() -{ - return &m_background; -} - -void T2FMRF::InitModel(const RgbImage& data) -{ - m_modes_per_pixel.Clear(); - - for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + namespace algorithms { - m_modes[i].weight = 0; - m_modes[i].variance = 0; - m_modes[i].muR = 0; - m_modes[i].muG = 0; - m_modes[i].muB = 0; - m_modes[i].significants = 0; - } - - for (unsigned int j = 0; j < m_params.Size(); ++j) - { - m_state[j].State = background; - m_state[j].Ab2b = 0.7f; - m_state[j].Ab2f = 0.3f; - m_state[j].Af2b = 0.4f; - m_state[j].Af2f = 0.6f; - m_state[j].T = 0.7f; - } -} - -void T2FMRF::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) -{ - // it doesn't make sense to have conditional updates in the GMM framework -} - -void T2FMRF::SubtractPixel(long posPixel, long posGMode, const RgbPixel& pixel, unsigned char& numModes, - unsigned char& low_threshold, unsigned char& high_threshold) -{ - // calculate distances to the modes (+ sort???) - // here we need to go in descending order!!! - long pos; - bool bFitsPDF = false; - bool bBackgroundLow = false; - bool bBackgroundHigh = false; - - HiddenState CurrentState = m_state[posPixel].State; - float Ab2b = m_state[posPixel].Ab2b; - float Ab2f = m_state[posPixel].Ab2f; - float Af2b = m_state[posPixel].Af2b; - float Af2f = m_state[posPixel].Af2f; - //float T = m_state[posPixel].T; - - float fOneMinAlpha = 1 - m_params.Alpha(); - float totalWeight = 0.0f; - - // calculate number of Gaussians to include in the background model - int backgroundGaussians = 0; - double sum = 0.0; - for (int i = 0; i < numModes; ++i) - { - if (sum < m_bg_threshold) + namespace dp { - backgroundGaussians++; - sum += m_modes[posGMode + i].weight; - } - else - break; - } - - // update all distributions and check for match with current pixel - for (int iModes = 0; iModes < numModes; iModes++) - { - pos = posGMode + iModes; - float weight = m_modes[pos].weight; - - // fit not found yet - if (!bFitsPDF) - { - //check if it belongs to some of the modes - //calculate distance - float var = m_modes[pos].variance; - float muR = m_modes[pos].muR; - float muG = m_modes[pos].muG; - float muB = m_modes[pos].muB; - - //float km = 2; - //float kv = 0.9; - - float dR = fabs(muR - pixel(0)); - float dG = fabs(muG - pixel(1)); - float dB = fabs(muB - pixel(2)); - - // calculate the squared distance - float HR; - float HG; - float HB; - - // T2FMRF-UM - if (m_params.Type() == TYPE_T2FMRF_UM) + int compareT2FMRF(const void* _gmm1, const void* _gmm2) { - if ((pixel(0) < muR - km*var) || (pixel(0) > muR + km*var)) - HR = 2 * km*dR / var; - else - HR = dR*dR / (2 * var*var) + km*dR / var + km*km / 2; - - if ((pixel(1) < muG - km*var) || (pixel(1) > muG + km*var)) - HG = 2 * km*dG / var; - else - HG = dG*dG / (2 * var*var) + km*dG / var + km*km / 2; + GMM gmm1 = *(GMM*)_gmm1; + GMM gmm2 = *(GMM*)_gmm2; - if ((pixel(2) < muB - km*var) || (pixel(2) > muB + km*var)) - HB = 2 * km*dB / var; + if (gmm1.significants < gmm2.significants) + return 1; + else if (gmm1.significants == gmm2.significants) + return 0; else - HB = dB*dB / (2 * var*var) + km*dB / var + km*km / 2; + return -1; } - // T2FGMM-UV - if (m_params.Type() == TYPE_T2FMRF_UV) + GMM* T2FMRF::gmm() { - HR = (1 / (kv*kv) - kv*kv) * (pixel(0) - muR) * (pixel(0) - muR) / (2 * var); - HG = (1 / (kv*kv) - kv*kv) * (pixel(1) - muG) * (pixel(1) - muG) / (2 * var); - HB = (1 / (kv*kv) - kv*kv) * (pixel(2) - muB) * (pixel(2) - muB) / (2 * var); + return m_modes; + } + HMM* T2FMRF::hmm() + { + return m_state; } - float ro; - if (CurrentState == background) + T2FMRF::T2FMRF() { - if (Ab2b != 0) ro = (Ab2f / Ab2b); - else ro = 10; + m_modes = NULL; } - else + + T2FMRF::~T2FMRF() { - if (Af2b != 0) ro = (Af2f / Af2b); - else ro = 10; + delete[] m_modes; } - // calculate the squared distance - float dist = (HR*HR + HG*HG + HB*HB); + void T2FMRF::Initalize(const BgsParams& param) + { + m_params = (T2FMRFParams&)param; + + // Tbf - the threshold + m_bg_threshold = 0.75f; // 1-cf from the paper + + // Tgenerate - the threshold + m_variance = 36.0f; // sigma for the new mode + + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; + + //HMM for each pixel + m_state = new HMM[m_params.Size()]; + + // used modes per pixel + m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); + + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); - if (dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) - bBackgroundHigh = true; + // Factor control for the T2FGMM-UM [0,3] + // km = (float) 2; //1.5; + km = (float)m_params.KM(); - // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution - if (dist < m_params.LowThreshold()*var) + // Factor control for the T2FGMM-UV [0.3,1] + // kv = (float) 0.9; //0.6; + kv = (float)m_params.KV(); + } + + RgbImage* T2FMRF::Background() { - bFitsPDF = true; - - // check if this Gaussian is part of the background model - if (iModes < backgroundGaussians) - bBackgroundLow = true; - - //update distribution - float k = m_params.Alpha() / weight; - weight = fOneMinAlpha*weight + m_params.Alpha(); - m_modes[pos].weight = weight; - m_modes[pos].muR = muR - k*(dR); - m_modes[pos].muG = muG - k*(dG); - m_modes[pos].muB = muB - k*(dB); - - //limit the variance - float sigmanew = var + k*(dist - var); - m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + return &m_background; } - else + + void T2FMRF::InitModel(const RgbImage& data) { - weight = fOneMinAlpha*weight; - if (weight < 0.0) + m_modes_per_pixel.Clear(); + + for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) { - weight = 0.0; - numModes--; + m_modes[i].weight = 0; + m_modes[i].variance = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + m_modes[i].significants = 0; } - m_modes[pos].weight = weight; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + for (unsigned int j = 0; j < m_params.Size(); ++j) + { + m_state[j].State = background; + m_state[j].Ab2b = 0.7f; + m_state[j].Ab2f = 0.3f; + m_state[j].Af2b = 0.4f; + m_state[j].Af2f = 0.6f; + m_state[j].T = 0.7f; + } } - } - else - { - weight = fOneMinAlpha*weight; - if (weight < 0.0) + + void T2FMRF::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) { - weight = 0.0; - numModes--; + // it doesn't make sense to have conditional updates in the GMM framework } - m_modes[pos].weight = weight; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); - } - - totalWeight += weight; - } - - // renormalize weights so they add to one - double invTotalWeight = 1.0 / totalWeight; - for (int iLocal = 0; iLocal < numModes; iLocal++) - { - m_modes[posGMode + iLocal].weight *= (float)invTotalWeight; - m_modes[posGMode + iLocal].significants = m_modes[posGMode + iLocal].weight / sqrt(m_modes[posGMode + iLocal].variance); - } - // Sort significance values so they are in desending order. - qsort(&m_modes[posGMode], numModes, sizeof(GMM), compareT2FMRF); - - // make new mode if needed and exit - if (!bFitsPDF) - { - if (numModes < m_params.MaxModes()) - numModes++; - //else - // the weakest mode will be replaced - - pos = posGMode + numModes - 1; - - m_modes[pos].muR = pixel.ch[0]; - m_modes[pos].muG = pixel.ch[1]; - m_modes[pos].muB = pixel.ch[2]; - m_modes[pos].variance = m_variance; - m_modes[pos].significants = 0; // will be set below - - if (numModes == 1) - m_modes[pos].weight = 1; - else - m_modes[pos].weight = m_params.Alpha(); - - //renormalize weights - int iLocal; - float sum = 0.0; - for (iLocal = 0; iLocal < numModes; iLocal++) - sum += m_modes[posGMode + iLocal].weight; - - double invSum = 1.0 / sum; - for (iLocal = 0; iLocal < numModes; iLocal++) - { - m_modes[posGMode + iLocal].weight *= (float)invSum; - m_modes[posGMode + iLocal].significants = m_modes[posPixel + iLocal].weight / sqrt(m_modes[posGMode + iLocal].variance); - } - } - - // Sort significance values so they are in desending order. - qsort(&(m_modes[posGMode]), numModes, sizeof(GMM), compareT2FMRF); - - if (bBackgroundLow) - { - low_threshold = BACKGROUND; - m_state[posPixel].State = background; + void T2FMRF::SubtractPixel(long posPixel, long posGMode, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& low_threshold, unsigned char& high_threshold) + { + // calculate distances to the modes (+ sort???) + // here we need to go in descending order!!! + long pos; + bool bFitsPDF = false; + bool bBackgroundLow = false; + bool bBackgroundHigh = false; + + HiddenState CurrentState = m_state[posPixel].State; + float Ab2b = m_state[posPixel].Ab2b; + float Ab2f = m_state[posPixel].Ab2f; + float Af2b = m_state[posPixel].Af2b; + float Af2f = m_state[posPixel].Af2f; + //float T = m_state[posPixel].T; + + float fOneMinAlpha = 1 - m_params.Alpha(); + float totalWeight = 0.0f; + + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for (int i = 0; i < numModes; ++i) + { + if (sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posGMode + i].weight; + } + else + break; + } - if (CurrentState == background) - { - float b2b = fOneMinAlpha*Ab2b + m_params.Alpha(); - float b2f = fOneMinAlpha*Ab2f; + // update all distributions and check for match with current pixel + for (int iModes = 0; iModes < numModes; iModes++) + { + pos = posGMode + iModes; + float weight = m_modes[pos].weight; + + // fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].variance; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + //float km = 2; + //float kv = 0.9; + + float dR = fabs(muR - pixel(0)); + float dG = fabs(muG - pixel(1)); + float dB = fabs(muB - pixel(2)); + + // calculate the squared distance + float HR; + float HG; + float HB; + + // T2FMRF-UM + if (m_params.Type() == TYPE_T2FMRF_UM) + { + if ((pixel(0) < muR - km*var) || (pixel(0) > muR + km*var)) + HR = 2 * km*dR / var; + else + HR = dR*dR / (2 * var*var) + km*dR / var + km*km / 2; + + if ((pixel(1) < muG - km*var) || (pixel(1) > muG + km*var)) + HG = 2 * km*dG / var; + else + HG = dG*dG / (2 * var*var) + km*dG / var + km*km / 2; + + if ((pixel(2) < muB - km*var) || (pixel(2) > muB + km*var)) + HB = 2 * km*dB / var; + else + HB = dB*dB / (2 * var*var) + km*dB / var + km*km / 2; + } + + // T2FGMM-UV + if (m_params.Type() == TYPE_T2FMRF_UV) + { + HR = (1 / (kv*kv) - kv*kv) * (pixel(0) - muR) * (pixel(0) - muR) / (2 * var); + HG = (1 / (kv*kv) - kv*kv) * (pixel(1) - muG) * (pixel(1) - muG) / (2 * var); + HB = (1 / (kv*kv) - kv*kv) * (pixel(2) - muB) * (pixel(2) - muB) / (2 * var); + } + + float ro; + if (CurrentState == background) + { + if (Ab2b != 0) ro = (Ab2f / Ab2b); + else ro = 10; + } + else + { + if (Af2b != 0) ro = (Af2f / Af2b); + else ro = 10; + } + + // calculate the squared distance + float dist = (HR*HR + HG*HG + HB*HB); + + if (dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution + if (dist < m_params.LowThreshold()*var) + { + bFitsPDF = true; + + // check if this Gaussian is part of the background model + if (iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha() / weight; + weight = fOneMinAlpha*weight + m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit the variance + float sigmanew = var + k*(dist - var); + m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight = 0.0; + numModes--; + } + + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight = 0.0; + numModes--; + } + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + + totalWeight += weight; + } - float b = b2b + b2f; - m_state[posPixel].Ab2b = b2b / b; - m_state[posPixel].Ab2f = b2f / b; - m_state[posPixel].T = m_state[posPixel].Ab2b; - } - else - { - float f2b = fOneMinAlpha*Af2b + m_params.Alpha(); - float f2f = fOneMinAlpha*Af2f; + // renormalize weights so they add to one + double invTotalWeight = 1.0 / totalWeight; + for (int iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posGMode + iLocal].weight *= (float)invTotalWeight; + m_modes[posGMode + iLocal].significants = m_modes[posGMode + iLocal].weight / sqrt(m_modes[posGMode + iLocal].variance); + } - float f = f2b + f2f; - m_state[posPixel].Af2b = f2b / f; - m_state[posPixel].Af2f = f2f / f; - m_state[posPixel].T = m_state[posPixel].Af2b; - } - } - else - { - low_threshold = FOREGROUND; - m_state[posPixel].State = foreground; + // Sort significance values so they are in desending order. + qsort(&m_modes[posGMode], numModes, sizeof(GMM), compareT2FMRF); - if (CurrentState == background) - { - float b2b = fOneMinAlpha*Ab2b; - float b2f = fOneMinAlpha*Ab2f + m_params.Alpha(); + // make new mode if needed and exit + if (!bFitsPDF) + { + if (numModes < m_params.MaxModes()) + numModes++; + //else + // the weakest mode will be replaced + + pos = posGMode + numModes - 1; + + m_modes[pos].muR = pixel.ch[0]; + m_modes[pos].muG = pixel.ch[1]; + m_modes[pos].muB = pixel.ch[2]; + m_modes[pos].variance = m_variance; + m_modes[pos].significants = 0; // will be set below + + if (numModes == 1) + m_modes[pos].weight = 1; + else + m_modes[pos].weight = m_params.Alpha(); + + //renormalize weights + int iLocal; + float sum = 0.0; + for (iLocal = 0; iLocal < numModes; iLocal++) + sum += m_modes[posGMode + iLocal].weight; + + double invSum = 1.0 / sum; + for (iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posGMode + iLocal].weight *= (float)invSum; + m_modes[posGMode + iLocal].significants = m_modes[posPixel + iLocal].weight / sqrt(m_modes[posGMode + iLocal].variance); + } + } - float b = b2b + b2f; - m_state[posPixel].Ab2b = b2b / b; - m_state[posPixel].Ab2f = b2f / b; - m_state[posPixel].T = m_state[posPixel].Ab2b; - } - else - { - float f2b = fOneMinAlpha*Af2b; - float f2f = fOneMinAlpha*Af2f + m_params.Alpha(); + // Sort significance values so they are in desending order. + qsort(&(m_modes[posGMode]), numModes, sizeof(GMM), compareT2FMRF); - float f = f2b + f2f; - m_state[posPixel].Af2b = f2b / f; - m_state[posPixel].Af2f = f2f / f; - m_state[posPixel].T = m_state[posPixel].Af2b; - } - } + if (bBackgroundLow) + { + low_threshold = BACKGROUND; + m_state[posPixel].State = background; + + if (CurrentState == background) + { + float b2b = fOneMinAlpha*Ab2b + m_params.Alpha(); + float b2f = fOneMinAlpha*Ab2f; + + float b = b2b + b2f; + m_state[posPixel].Ab2b = b2b / b; + m_state[posPixel].Ab2f = b2f / b; + m_state[posPixel].T = m_state[posPixel].Ab2b; + } + else + { + float f2b = fOneMinAlpha*Af2b + m_params.Alpha(); + float f2f = fOneMinAlpha*Af2f; + + float f = f2b + f2f; + m_state[posPixel].Af2b = f2b / f; + m_state[posPixel].Af2f = f2f / f; + m_state[posPixel].T = m_state[posPixel].Af2b; + } + } + else + { + low_threshold = FOREGROUND; + m_state[posPixel].State = foreground; + + if (CurrentState == background) + { + float b2b = fOneMinAlpha*Ab2b; + float b2f = fOneMinAlpha*Ab2f + m_params.Alpha(); + + float b = b2b + b2f; + m_state[posPixel].Ab2b = b2b / b; + m_state[posPixel].Ab2f = b2f / b; + m_state[posPixel].T = m_state[posPixel].Ab2b; + } + else + { + float f2b = fOneMinAlpha*Af2b; + float f2f = fOneMinAlpha*Af2f + m_params.Alpha(); + + float f = f2b + f2f; + m_state[posPixel].Af2b = f2b / f; + m_state[posPixel].Af2f = f2f / f; + m_state[posPixel].T = m_state[posPixel].Af2b; + } + } - if (bBackgroundHigh) - high_threshold = BACKGROUND; - else - high_threshold = FOREGROUND; -} + if (bBackgroundHigh) + high_threshold = BACKGROUND; + else + high_threshold = FOREGROUND; + } -/////////////////////////////////////////////////////////////////////////////// -//Input: -// data - a pointer to the data of a RGB image of the same size -//Output: -// output - a pointer to the data of a gray value image of the same size -// (the memory should already be reserved) -// values: 255-foreground, 125-shadow, 0-background -/////////////////////////////////////////////////////////////////////////////// -void T2FMRF::Subtract(int frame_num, const RgbImage& data, - BwImage& low_threshold_mask, BwImage& high_threshold_mask) -{ - unsigned char low_threshold, high_threshold; - long posPixel; - long posGMode; + /////////////////////////////////////////////////////////////////////////////// + //Input: + // data - a pointer to the data of a RGB image of the same size + //Output: + // output - a pointer to the data of a gray value image of the same size + // (the memory should already be reserved) + // values: 255-foreground, 125-shadow, 0-background + /////////////////////////////////////////////////////////////////////////////// + void T2FMRF::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) + { + unsigned char low_threshold, high_threshold; + long posPixel; + long posGMode; - // update each pixel of the image - for (unsigned int r = 0; r < m_params.Height(); ++r) - { - for (unsigned int c = 0; c < m_params.Width(); ++c) - { - // update model + background subtract - posPixel = r*m_params.Width() + c; - posGMode = (r*m_params.Width() + c) * m_params.MaxModes(); + // update each pixel of the image + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for (unsigned int c = 0; c < m_params.Width(); ++c) + { + // update model + background subtract + posPixel = r*m_params.Width() + c; + posGMode = (r*m_params.Width() + c) * m_params.MaxModes(); - SubtractPixel(posPixel, posGMode, data(r, c), m_modes_per_pixel(r, c), low_threshold, high_threshold); + SubtractPixel(posPixel, posGMode, data(r, c), m_modes_per_pixel(r, c), low_threshold, high_threshold); - low_threshold_mask(r, c) = low_threshold; - high_threshold_mask(r, c) = high_threshold; + low_threshold_mask(r, c) = low_threshold; + high_threshold_mask(r, c) = high_threshold; - m_background(r, c, 0) = (unsigned char)m_modes[posGMode].muR; - m_background(r, c, 1) = (unsigned char)m_modes[posGMode].muG; - m_background(r, c, 2) = (unsigned char)m_modes[posGMode].muB; + m_background(r, c, 0) = (unsigned char)m_modes[posGMode].muR; + m_background(r, c, 1) = (unsigned char)m_modes[posGMode].muG; + m_background(r, c, 2) = (unsigned char)m_modes[posGMode].muB; + } + } + } } } } diff --git a/src/algorithms/T2F/T2FMRF.h b/src/algorithms/T2F/T2FMRF.h index a3a3a17317f57d0498528f3adc5e408082eab622..8f79e802634b7655041d2fb4f9dc1d3a198514b7 100644 --- a/src/algorithms/T2F/T2FMRF.h +++ b/src/algorithms/T2F/T2FMRF.h @@ -6,130 +6,133 @@ #include "../dp/Bgs.h" #include "../dp/GrimsonGMM.h" -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { - const int TYPE_T2FMRF_UM = 0; - const int TYPE_T2FMRF_UV = 1; - - enum HiddenState { background, foreground }; - - typedef struct HMMState - { - float T; - //Hidden State - HiddenState State; - //transition probability - float Ab2b; - float Ab2f; - float Af2f; - float Af2b; - } HMM; - - //typedef struct GMMGaussian - //{ - // float variance; - // float muR; - // float muG; - // float muB; - // float weight; - // float significants; // this is equal to weight / standard deviation and is used to - // // determine which Gaussians should be part of the background model - //} GMM; - - // --- User adjustable parameters used by the T2F GMM BGS algorithm --- - class T2FMRFParams : public BgsParams - { - public: - float &LowThreshold() { return m_low_threshold; } - float &HighThreshold() { return m_high_threshold; } - - float &Alpha() { return m_alpha; } - int &MaxModes() { return m_max_modes; } - int &Type() { return m_type; } - float &KM() { return m_km; } - float &KV() { return m_kv; } - - private: - // Threshold on the squared dist. to decide when a sample is close to an existing - // components. If it is not close to any a new component will be generated. - // Smaller threshold values lead to more generated components and higher threshold values - // lead to a small number of components but they can grow too large. - // - // It is usual easiest to think of these thresholds as being the number of variances away - // from the mean of a pixel before it is considered to be from the foreground. - float m_low_threshold; - float m_high_threshold; - - // alpha - speed of update - if the time interval you want to average over is T - // set alpha=1/T. - float m_alpha; - - // Maximum number of modes (Gaussian components) that will be used per pixel - int m_max_modes; - - // T2FMRF_UM / T2FMRF_UV - int m_type; - - // Factor control for the T2FMRF-UM - float m_km; - - // Factor control for the T2FMRF-UV - float m_kv; - }; - - // --- T2FGMM BGS algorithm --- - class T2FMRF : public Bgs + namespace dp { - public: - T2FMRF(); - ~T2FMRF(); - - void Initalize(const BgsParams& param); - void InitModel(const RgbImage& data); - void Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask); - void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); - - RgbImage* Background(); - - GMM *gmm(void); - HMM *hmm(void); - - private: - void SubtractPixel(long posPixel, long posGMode, const RgbPixel& pixel, unsigned char& numModes, unsigned char& lowThreshold, unsigned char& highThreshold); - - // User adjustable parameters - T2FMRFParams m_params; - - // Threshold when the component becomes significant enough to be included into - // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 - // For alpha=0.001 it means that the mode should exist for approximately 105 frames before - // it is considered foreground - float m_bg_threshold; //1-cf from the paper - - // Initial variance for the newly generated components. - // It will will influence the speed of adaptation. A good guess should be made. - // A simple way is to estimate the typical standard deviation from the images. - float m_variance; - - // Dynamic array for the mixture of Gaussians - GMM* m_modes; - - //Dynamic array for the hidden state - HMM* m_state; - - // Number of Gaussian components per pixel - BwImage m_modes_per_pixel; - - // Current background model - RgbImage m_background; - - // Factor control for the T2FGMM-UM - float km; - // Factor control for the T2FGMM-UV - float kv; - }; + const int TYPE_T2FMRF_UM = 0; + const int TYPE_T2FMRF_UV = 1; + + enum HiddenState { background, foreground }; + + typedef struct HMMState + { + float T; + //Hidden State + HiddenState State; + //transition probability + float Ab2b; + float Ab2f; + float Af2f; + float Af2b; + } HMM; + + //typedef struct GMMGaussian + //{ + // float variance; + // float muR; + // float muG; + // float muB; + // float weight; + // float significants; // this is equal to weight / standard deviation and is used to + // // determine which Gaussians should be part of the background model + //} GMM; + + // --- User adjustable parameters used by the T2F GMM BGS algorithm --- + class T2FMRFParams : public BgsParams + { + public: + float &LowThreshold() { return m_low_threshold; } + float &HighThreshold() { return m_high_threshold; } + + float &Alpha() { return m_alpha; } + int &MaxModes() { return m_max_modes; } + int &Type() { return m_type; } + float &KM() { return m_km; } + float &KV() { return m_kv; } + + private: + // Threshold on the squared dist. to decide when a sample is close to an existing + // components. If it is not close to any a new component will be generated. + // Smaller threshold values lead to more generated components and higher threshold values + // lead to a small number of components but they can grow too large. + // + // It is usual easiest to think of these thresholds as being the number of variances away + // from the mean of a pixel before it is considered to be from the foreground. + float m_low_threshold; + float m_high_threshold; + + // alpha - speed of update - if the time interval you want to average over is T + // set alpha=1/T. + float m_alpha; + + // Maximum number of modes (Gaussian components) that will be used per pixel + int m_max_modes; + + // T2FMRF_UM / T2FMRF_UV + int m_type; + + // Factor control for the T2FMRF-UM + float m_km; + + // Factor control for the T2FMRF-UV + float m_kv; + }; + + // --- T2FGMM BGS algorithm --- + class T2FMRF : public Bgs + { + public: + T2FMRF(); + ~T2FMRF(); + + void Initalize(const BgsParams& param); + void InitModel(const RgbImage& data); + void Subtract(int frame_num, const RgbImage& data, BwImage& low_threshold_mask, BwImage& high_threshold_mask); + void Update(int frame_num, const RgbImage& data, const BwImage& update_mask); + + RgbImage* Background(); + + GMM *gmm(void); + HMM *hmm(void); + + private: + void SubtractPixel(long posPixel, long posGMode, const RgbPixel& pixel, unsigned char& numModes, unsigned char& lowThreshold, unsigned char& highThreshold); + + // User adjustable parameters + T2FMRFParams m_params; + + // Threshold when the component becomes significant enough to be included into + // the background model. It is the TB = 1-cf from the paper. So I use cf=0.1 => TB=0.9 + // For alpha=0.001 it means that the mode should exist for approximately 105 frames before + // it is considered foreground + float m_bg_threshold; //1-cf from the paper + + // Initial variance for the newly generated components. + // It will will influence the speed of adaptation. A good guess should be made. + // A simple way is to estimate the typical standard deviation from the images. + float m_variance; + + // Dynamic array for the mixture of Gaussians + GMM* m_modes; + + //Dynamic array for the hidden state + HMM* m_state; + + // Number of Gaussian components per pixel + BwImage m_modes_per_pixel; + + // Current background model + RgbImage m_background; + + // Factor control for the T2FGMM-UM + float km; + // Factor control for the T2FGMM-UV + float kv; + }; + } } } diff --git a/src/algorithms/T2FGMM_UM.cpp b/src/algorithms/T2FGMM_UM.cpp index 44904da8079e30cb3a74b1407ff5f6d9a4204606..7c95dfe58cab47bd1f9f2c36c50bc6776bb62fa2 100644 --- a/src/algorithms/T2FGMM_UM.cpp +++ b/src/algorithms/T2FGMM_UM.cpp @@ -41,7 +41,7 @@ void T2FGMM_UM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & params.HighThreshold() = 2 * params.LowThreshold(); params.Alpha() = alpha; params.MaxModes() = gaussians; - params.Type() = TYPE_T2FGMM_UM; + params.Type() = dp::TYPE_T2FGMM_UM; params.KM() = km; // Factor control for the T2FGMM-UM [0,3] default: 1.5 params.KV() = kv; // Factor control for the T2FGMM-UV [0.3,1] default: 0.6 diff --git a/src/algorithms/T2FGMM_UM.h b/src/algorithms/T2FGMM_UM.h index c5a6ebd0e91c0024ee147816cae203dc23a11c2b..0f3185a042faff634afa03d7661f050a4eea1b21 100644 --- a/src/algorithms/T2FGMM_UM.h +++ b/src/algorithms/T2FGMM_UM.h @@ -6,8 +6,6 @@ #include "IBGS.h" #include "T2F/T2FGMM.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -16,19 +14,17 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - T2FGMMParams params; - T2FGMM bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; float km; float kv; int gaussians; + IplImage* frame; + dp::RgbImage frame_data; + dp::T2FGMMParams params; + dp::T2FGMM bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: T2FGMM_UM(); diff --git a/src/algorithms/T2FGMM_UV.cpp b/src/algorithms/T2FGMM_UV.cpp index 3b411351b7fd26697f08810ded00ad44e3916be3..cff817bd82f0266bedea0cb809edbcbdc1a21084 100644 --- a/src/algorithms/T2FGMM_UV.cpp +++ b/src/algorithms/T2FGMM_UV.cpp @@ -41,7 +41,7 @@ void T2FGMM_UV::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & params.HighThreshold() = 2 * params.LowThreshold(); params.Alpha() = alpha; params.MaxModes() = gaussians; - params.Type() = TYPE_T2FGMM_UV; + params.Type() = dp::TYPE_T2FGMM_UV; params.KM() = km; // Factor control for the T2FGMM-UM [0,3] default: 1.5 params.KV() = kv; // Factor control for the T2FGMM-UV [0.3,1] default: 0.6 diff --git a/src/algorithms/T2FGMM_UV.h b/src/algorithms/T2FGMM_UV.h index 75f931f4aa94ffa57eba17bc59b266a5fbaeafc1..6c00688642637f9e66274058c393f51e26359661 100644 --- a/src/algorithms/T2FGMM_UV.h +++ b/src/algorithms/T2FGMM_UV.h @@ -6,8 +6,6 @@ #include "IBGS.h" #include "T2F/T2FGMM.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -16,19 +14,17 @@ namespace bgslibrary { private: long frameNumber; - IplImage* frame; - RgbImage frame_data; - - T2FGMMParams params; - T2FGMM bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; float km; float kv; int gaussians; + IplImage* frame; + dp::RgbImage frame_data; + dp::T2FGMMParams params; + dp::T2FGMM bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; public: T2FGMM_UV(); diff --git a/src/algorithms/T2FMRF_UM.cpp b/src/algorithms/T2FMRF_UM.cpp index 487b72e6b973c1da899a38ae91707e7b8006513d..208be5d63d5fa7199e79885fd76fd2a44d6fac40 100644 --- a/src/algorithms/T2FMRF_UM.cpp +++ b/src/algorithms/T2FMRF_UM.cpp @@ -41,7 +41,7 @@ void T2FMRF_UM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & params.HighThreshold() = 2 * params.LowThreshold(); params.Alpha() = alpha; params.MaxModes() = gaussians; - params.Type() = TYPE_T2FMRF_UM; + params.Type() = dp::TYPE_T2FMRF_UM; params.KM() = km; // Factor control for the T2FMRF-UM [0,3] default: 2 params.KV() = kv; // Factor control for the T2FMRF-UV [0.3,1] default: 0.9 diff --git a/src/algorithms/T2FMRF_UM.h b/src/algorithms/T2FMRF_UM.h index ba27e6e67f8e4c67f8f1088e1b1a4d6145079f7c..9761ae6b15025516f0773df390681922bff492e2 100644 --- a/src/algorithms/T2FMRF_UM.h +++ b/src/algorithms/T2FMRF_UM.h @@ -6,8 +6,6 @@ #include "IBGS.h" #include "T2F/MRF.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -16,26 +14,22 @@ namespace bgslibrary { private: long frameNumber; - IplImage *frame; - RgbImage frame_data; - - IplImage *old_labeling; - IplImage *old; - - T2FMRFParams params; - T2FMRF bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; float km; float kv; int gaussians; - - MRF_TC mrf; - GMM *gmm; - HMM *hmm; + IplImage *frame; + IplImage *old_labeling; + IplImage *old; + dp::RgbImage frame_data; + dp::T2FMRFParams params; + dp::T2FMRF bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; + dp::MRF_TC mrf; + dp::GMM *gmm; + dp::HMM *hmm; public: T2FMRF_UM(); diff --git a/src/algorithms/T2FMRF_UV.cpp b/src/algorithms/T2FMRF_UV.cpp index 5c96be89b3a49fac9650afd0787b12885ce46e2e..c0a1c5ff65c2f13f3427f44178c43bfdb807934d 100644 --- a/src/algorithms/T2FMRF_UV.cpp +++ b/src/algorithms/T2FMRF_UV.cpp @@ -41,7 +41,7 @@ void T2FMRF_UV::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & params.HighThreshold() = 2 * params.LowThreshold(); params.Alpha() = alpha; params.MaxModes() = gaussians; - params.Type() = TYPE_T2FMRF_UV; + params.Type() = dp::TYPE_T2FMRF_UV; params.KM() = km; // Factor control for the T2FMRF-UM [0,3] default: 2 params.KV() = kv; // Factor control for the T2FMRF-UV [0.3,1] default: 0.9 diff --git a/src/algorithms/T2FMRF_UV.h b/src/algorithms/T2FMRF_UV.h index f9b6e0f9a6075a618ef59f642059b76b27f7af92..ef2519b6d3f1db68e7b5f2caa4b844fd0540bd9d 100644 --- a/src/algorithms/T2FMRF_UV.h +++ b/src/algorithms/T2FMRF_UV.h @@ -6,8 +6,6 @@ #include "IBGS.h" #include "T2F/MRF.h" -using namespace Algorithms::BackgroundSubtraction; - namespace bgslibrary { namespace algorithms @@ -16,26 +14,22 @@ namespace bgslibrary { private: long frameNumber; - IplImage *frame; - RgbImage frame_data; - - IplImage *old_labeling; - IplImage *old; - - T2FMRFParams params; - T2FMRF bgs; - BwImage lowThresholdMask; - BwImage highThresholdMask; - double threshold; double alpha; float km; float kv; int gaussians; - - MRF_TC mrf; - GMM *gmm; - HMM *hmm; + IplImage *frame; + IplImage *old_labeling; + IplImage *old; + dp::RgbImage frame_data; + dp::T2FMRFParams params; + dp::T2FMRF bgs; + dp::BwImage lowThresholdMask; + dp::BwImage highThresholdMask; + dp::MRF_TC mrf; + dp::GMM *gmm; + dp::HMM *hmm; public: T2FMRF_UV(); diff --git a/src/algorithms/TwoPoints.cpp b/src/algorithms/TwoPoints.cpp index ec2c134e1222e56712fbdd926c7fd105c39cce5e..6b554ea8eb11d7feec1309f84885b848d558f3ab 100644 --- a/src/algorithms/TwoPoints.cpp +++ b/src/algorithms/TwoPoints.cpp @@ -10,12 +10,12 @@ TwoPoints::TwoPoints() : debug_construction(TwoPoints); initLoadSaveConfig(algorithmName); //model = static_cast<twopointsModel_t*>(libtwopointsModel_New()); - model = libtwopointsModel_New(); + model = twopoints::libtwopointsModel_New(); } TwoPoints::~TwoPoints() { debug_destruction(TwoPoints); - libtwopointsModel_Free(model); + twopoints::libtwopointsModel_Free(model); } void TwoPoints::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) @@ -36,14 +36,14 @@ void TwoPoints::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & //img_output = Mat(img_input.rows, img_input.cols, CV_8UC1); // Initialization of the ViBe model. - libtwopointsModel_AllocInit_8u_C1R(model, img_input_grayscale.data, img_input.cols, img_input.rows); + twopoints::libtwopointsModel_AllocInit_8u_C1R(model, img_input_grayscale.data, img_input.cols, img_input.rows); // Sets default model values. - // libvibeModel_Sequential_SetMatchingThreshold(model, matchingThreshold); - // libvibeModel_Sequential_SetUpdateFactor(model, updateFactor); + // twopoints::libvibeModel_Sequential_SetMatchingThreshold(model, matchingThreshold); + // twopoints::libvibeModel_Sequential_SetUpdateFactor(model, updateFactor); } - libtwopointsModel_Segmentation_8u_C1R(model, img_input_grayscale.data, img_output.data); + twopoints::libtwopointsModel_Segmentation_8u_C1R(model, img_input_grayscale.data, img_output.data); updatingMask = cv::Mat(img_input.rows, img_input.cols, CV_8UC1); // Work on the output and define the updating mask @@ -58,7 +58,7 @@ void TwoPoints::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat & } } - libtwopointsModel_Update_8u_C1R(model, img_input_grayscale.data, updatingMask.data); + twopoints::libtwopointsModel_Update_8u_C1R(model, img_input_grayscale.data, updatingMask.data); #ifndef MEX_COMPILE_FLAG if (showOutput) diff --git a/src/algorithms/TwoPoints.h b/src/algorithms/TwoPoints.h index 65cff47eb97cab995c34972a10a60883cb4f8d6a..1a1ef25a73c36ec63129a68856c7760442ce8a2d 100644 --- a/src/algorithms/TwoPoints.h +++ b/src/algorithms/TwoPoints.h @@ -14,7 +14,7 @@ namespace bgslibrary static const int DEFAULT_UPDATE_FACTOR = 16; int matchingThreshold; int updateFactor; - twopointsModel_t* model; + twopoints::twopointsModel_t* model; public: TwoPoints(); diff --git a/src/algorithms/TwoPoints/two_points.cpp b/src/algorithms/TwoPoints/two_points.cpp index 1c0c121fe109cce30b656e96605b9a474e6d1056..2ba29ed822d67411eb3ce8f3416431ce08a265b3 100644 --- a/src/algorithms/TwoPoints/two_points.cpp +++ b/src/algorithms/TwoPoints/two_points.cpp @@ -2,377 +2,387 @@ #include "two_points.h" -static unsigned int abs_uint(const int i) +//using namespace bgslibrary::algorithms::twopoints; +namespace bgslibrary { - return (i >= 0) ? i : -i; -} - -struct twopointsModel -{ - /* Parameters. */ - uint32_t width; - uint32_t height; - uint32_t numberOfSamples; - uint32_t matchingThreshold; - uint32_t matchingNumber; - uint32_t updateFactor; - - /* Storage for the history. */ - uint8_t *historyImage1; - uint8_t *historyImage2; - - /* Buffers with random values. */ - uint32_t *jump; - int *neighbor; -}; - -// ----------------------------------------------------------------------------- -// Creates the data structure -// ----------------------------------------------------------------------------- -twopointsModel_t *libtwopointsModel_New() -{ - /* Model structure alloc. */ - twopointsModel_t *model = NULL; - model = (twopointsModel_t*)calloc(1, sizeof(*model)); - assert(model != NULL); - - /* Default parameters values. */ - model->matchingThreshold = 20; - model->updateFactor = 16; - - /* Storage for the history. */ - model->historyImage1 = NULL; - model->historyImage2 = NULL; - - /* Buffers with random values. */ - model->jump = NULL; - model->neighbor = NULL; - - return(model); -} - -// ---------------------------------------------------------------------------- -// Frees the structure -// ---------------------------------------------------------------------------- -int32_t libtwopointsModel_Free(twopointsModel_t *model) -{ - if (model == NULL) - return(-1); - - if (model->historyImage1 != NULL) { - free(model->historyImage1); - model->historyImage1 = NULL; - } - if (model->historyImage2 != NULL) { - free(model->historyImage2); - model->historyImage2 = NULL; - } - if (model->jump != NULL) { - free(model->jump); - model->jump = NULL; - } - if (model->neighbor != NULL) { - free(model->neighbor); - model->neighbor = NULL; - } - free(model); - - return(0); -} - -// ----------------------------------------------------------------------------- -// Allocates and initializes a C1R model structure -// ----------------------------------------------------------------------------- -int32_t libtwopointsModel_AllocInit_8u_C1R( - twopointsModel_t *model, - const uint8_t *image_data, - const uint32_t width, - const uint32_t height -) -{ - // Some basic checks. */ - assert((image_data != NULL) && (model != NULL)); - assert((width > 0) && (height > 0)); - - /* Finish model alloc - parameters values cannot be changed anymore. */ - model->width = width; - model->height = height; - - /* Creates the historyImage structure. */ - model->historyImage1 = NULL; - model->historyImage1 = (uint8_t*)malloc(width * height * sizeof(uint8_t)); - model->historyImage2 = NULL; - model->historyImage2 = (uint8_t*)malloc(width * height * sizeof(uint8_t)); - - assert(model->historyImage1 != NULL); - assert(model->historyImage2 != NULL); - - for (int index = width * height - 1; index >= 0; --index) { - uint8_t value = image_data[index]; - - int value_plus_noise = value - 10; - if (value_plus_noise < 0) { value_plus_noise = 0; } - if (value_plus_noise > 255) { value_plus_noise = 255; } - model->historyImage1[index] = value_plus_noise; - - value_plus_noise = value + 10; - if (value_plus_noise < 0) { value_plus_noise = 0; } - if (value_plus_noise > 255) { value_plus_noise = 255; } - model->historyImage2[index] = value_plus_noise; - - // Swaps the two values if needed - if (model->historyImage1[index] > model->historyImage2[index]) { - uint8_t val = model->historyImage1[index]; - model->historyImage1[index] = model->historyImage2[index]; - model->historyImage2[index] = val; - } - } - - /* Fills the buffers with random values. */ - int size = (width > height) ? 2 * width + 1 : 2 * height + 1; - - model->jump = (uint32_t*)malloc(size * sizeof(*(model->jump))); - assert(model->jump != NULL); - - model->neighbor = (int*)malloc(size * sizeof(*(model->neighbor))); - assert(model->neighbor != NULL); - - - for (int i = 0; i < size; ++i) { - model->jump[i] = (rand() % (2 * model->updateFactor)) + 1; // Values between 1 and 2 * updateFactor. - model->neighbor[i] = ((rand() % 3) - 1) + ((rand() % 3) - 1) * width; // Values between { -width - 1, ... , width + 1 }. - } - - return(0); -} - -// ----------------------------------------------------------------------------- -// Segmentation of a C1R model -// ----------------------------------------------------------------------------- -int32_t libtwopointsModel_Segmentation_8u_C1R( - twopointsModel_t *model, - const uint8_t *image_data, - uint8_t *segmentation_map -) -{ - assert((image_data != NULL) && (model != NULL) && (segmentation_map != NULL)); - assert((model->width > 0) && (model->height > 0)); - assert((model->jump != NULL) && (model->neighbor != NULL)); - - /* Some variables. */ - uint32_t width = model->width; - uint32_t height = model->height; - uint32_t matchingThreshold = model->matchingThreshold; - - uint8_t *historyImage1 = model->historyImage1; - uint8_t *historyImage2 = model->historyImage2; - - /* Segmentation. */ - memset(segmentation_map, 0, width * height); - - uint8_t *first = historyImage1; - for (int index = width * height - 1; index >= 0; --index) { - // We adapt the threshold - matchingThreshold = model->matchingThreshold; - if (matchingThreshold < abs_uint(historyImage2[index] - historyImage1[index])) { - matchingThreshold = abs_uint(historyImage2[index] - historyImage1[index]); - } - if (abs_uint(image_data[index] - first[index]) <= matchingThreshold) - segmentation_map[index]++; - } - - first = historyImage2; - for (int index = width * height - 1; index >= 0; --index) { - // We adapt the threshold - matchingThreshold = model->matchingThreshold; - if (matchingThreshold < abs_uint(historyImage2[index] - historyImage1[index])) { - matchingThreshold = abs_uint(historyImage2[index] - historyImage1[index]); - } - if (abs_uint(image_data[index] - first[index]) <= matchingThreshold) - segmentation_map[index]++; - } - - return(0); -} - -// ---------------------------------------------------------------------------- -// Update a C1R model -// ---------------------------------------------------------------------------- -int doUpdate(const uint8_t value) -{ - if (value == 0) return 0; - else return 1; -} + namespace algorithms + { + namespace twopoints + { + static unsigned int abs_uint(const int i) + { + return (i >= 0) ? i : -i; + } + struct twopointsModel + { + /* Parameters. */ + uint32_t width; + uint32_t height; + uint32_t numberOfSamples; + uint32_t matchingThreshold; + uint32_t matchingNumber; + uint32_t updateFactor; + + /* Storage for the history. */ + uint8_t *historyImage1; + uint8_t *historyImage2; + + /* Buffers with random values. */ + uint32_t *jump; + int *neighbor; + }; + + // ----------------------------------------------------------------------------- + // Creates the data structure + // ----------------------------------------------------------------------------- + twopointsModel_t *libtwopointsModel_New() + { + /* Model structure alloc. */ + twopointsModel_t *model = NULL; + model = (twopointsModel_t*)calloc(1, sizeof(*model)); + assert(model != NULL); + + /* Default parameters values. */ + model->matchingThreshold = 20; + model->updateFactor = 16; + + /* Storage for the history. */ + model->historyImage1 = NULL; + model->historyImage2 = NULL; + + /* Buffers with random values. */ + model->jump = NULL; + model->neighbor = NULL; + + return(model); + } -int32_t libtwopointsModel_Update_8u_C1R( - twopointsModel_t *model, - const uint8_t *image_data, - uint8_t *updating_mask -) -{ - assert((image_data != NULL) && (model != NULL) && (updating_mask != NULL)); - assert((model->width > 0) && (model->height > 0)); - assert((model->jump != NULL) && (model->neighbor != NULL)); - - // Some variables. - uint32_t width = model->width; - uint32_t height = model->height; - - uint8_t *historyImage1 = model->historyImage1; - uint8_t *historyImage2 = model->historyImage2; - - // Updating. - uint32_t *jump = model->jump; - int *neighbor = model->neighbor; - - // All the frame, except the border. - uint32_t shift, indX, indY; - unsigned int x, y; - - for (y = 1; y < height - 1; ++y) { - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). - - while (indX < width - 1) { - int index = indX + y * width; - - if (doUpdate(updating_mask[index])) { - uint8_t value = image_data[index]; - // In-place substitution. - // if (2*value < (historyImage1[index]+historyImage2[index]) ) { - if (rand() % 2 == 0) { - historyImage1[index] = value; + // ---------------------------------------------------------------------------- + // Frees the structure + // ---------------------------------------------------------------------------- + int32_t libtwopointsModel_Free(twopointsModel_t *model) + { + if (model == NULL) + return(-1); + + if (model->historyImage1 != NULL) { + free(model->historyImage1); + model->historyImage1 = NULL; } - else { - historyImage2[index] = value; + if (model->historyImage2 != NULL) { + free(model->historyImage2); + model->historyImage2 = NULL; } - - // Propagation - int index_neighbor = index + neighbor[shift]; - if (2 * value < (historyImage1[index_neighbor] + historyImage2[index_neighbor])) { - // if (rand()%2 == 0 ) { - historyImage1[index_neighbor] = value; + if (model->jump != NULL) { + free(model->jump); + model->jump = NULL; } - else { - historyImage2[index_neighbor] = value; + if (model->neighbor != NULL) { + free(model->neighbor); + model->neighbor = NULL; } - } + free(model); - ++shift; - indX += jump[shift]; - } - } + return(0); + } - // First row. - y = 0; - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). + // ----------------------------------------------------------------------------- + // Allocates and initializes a C1R model structure + // ----------------------------------------------------------------------------- + int32_t libtwopointsModel_AllocInit_8u_C1R( + twopointsModel_t *model, + const uint8_t *image_data, + const uint32_t width, + const uint32_t height + ) + { + // Some basic checks. */ + assert((image_data != NULL) && (model != NULL)); + assert((width > 0) && (height > 0)); + + /* Finish model alloc - parameters values cannot be changed anymore. */ + model->width = width; + model->height = height; + + /* Creates the historyImage structure. */ + model->historyImage1 = NULL; + model->historyImage1 = (uint8_t*)malloc(width * height * sizeof(uint8_t)); + model->historyImage2 = NULL; + model->historyImage2 = (uint8_t*)malloc(width * height * sizeof(uint8_t)); + + assert(model->historyImage1 != NULL); + assert(model->historyImage2 != NULL); + + for (int index = width * height - 1; index >= 0; --index) { + uint8_t value = image_data[index]; + + int value_plus_noise = value - 10; + if (value_plus_noise < 0) { value_plus_noise = 0; } + if (value_plus_noise > 255) { value_plus_noise = 255; } + model->historyImage1[index] = value_plus_noise; + + value_plus_noise = value + 10; + if (value_plus_noise < 0) { value_plus_noise = 0; } + if (value_plus_noise > 255) { value_plus_noise = 255; } + model->historyImage2[index] = value_plus_noise; + + // Swaps the two values if needed + if (model->historyImage1[index] > model->historyImage2[index]) { + uint8_t val = model->historyImage1[index]; + model->historyImage1[index] = model->historyImage2[index]; + model->historyImage2[index] = val; + } + } - while (indX <= width - 1) { - int index = indX + y * width; + /* Fills the buffers with random values. */ + int size = (width > height) ? 2 * width + 1 : 2 * height + 1; - if (doUpdate(updating_mask[index])) { - uint8_t value = image_data[index]; - // In-place substitution. - // if (2*value < (historyImage1[index]+historyImage2[index]) ) { - if (rand() % 2 == 0) { - historyImage1[index] = value; - } - else { - historyImage2[index] = value; - } - } + model->jump = (uint32_t*)malloc(size * sizeof(*(model->jump))); + assert(model->jump != NULL); - ++shift; - indX += jump[shift]; - } + model->neighbor = (int*)malloc(size * sizeof(*(model->neighbor))); + assert(model->neighbor != NULL); - // Last row. - y = height - 1; - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). - while (indX <= width - 1) { - int index = indX + y * width; + for (int i = 0; i < size; ++i) { + model->jump[i] = (rand() % (2 * model->updateFactor)) + 1; // Values between 1 and 2 * updateFactor. + model->neighbor[i] = ((rand() % 3) - 1) + ((rand() % 3) - 1) * width; // Values between { -width - 1, ... , width + 1 }. + } - if (doUpdate(updating_mask[index])) { - uint8_t value = image_data[index]; - // In-place substitution. - // if (2*value < (historyImage1[index]+historyImage2[index]) ) { - if (rand() % 2 == 0) { - historyImage1[index] = value; - } - else { - historyImage2[index] = value; + return(0); } - } - - ++shift; - indX += jump[shift]; - } - // First column. - x = 0; - shift = rand() % height; - indY = jump[shift]; // index_jump should never be zero (> 1). + // ----------------------------------------------------------------------------- + // Segmentation of a C1R model + // ----------------------------------------------------------------------------- + int32_t libtwopointsModel_Segmentation_8u_C1R( + twopointsModel_t *model, + const uint8_t *image_data, + uint8_t *segmentation_map + ) + { + assert((image_data != NULL) && (model != NULL) && (segmentation_map != NULL)); + assert((model->width > 0) && (model->height > 0)); + assert((model->jump != NULL) && (model->neighbor != NULL)); + + /* Some variables. */ + uint32_t width = model->width; + uint32_t height = model->height; + uint32_t matchingThreshold = model->matchingThreshold; + + uint8_t *historyImage1 = model->historyImage1; + uint8_t *historyImage2 = model->historyImage2; + + /* Segmentation. */ + memset(segmentation_map, 0, width * height); + + uint8_t *first = historyImage1; + for (int index = width * height - 1; index >= 0; --index) { + // We adapt the threshold + matchingThreshold = model->matchingThreshold; + if (matchingThreshold < abs_uint(historyImage2[index] - historyImage1[index])) { + matchingThreshold = abs_uint(historyImage2[index] - historyImage1[index]); + } + if (abs_uint(image_data[index] - first[index]) <= matchingThreshold) + segmentation_map[index]++; + } - while (indY <= height - 1) { - int index = x + indY * width; + first = historyImage2; + for (int index = width * height - 1; index >= 0; --index) { + // We adapt the threshold + matchingThreshold = model->matchingThreshold; + if (matchingThreshold < abs_uint(historyImage2[index] - historyImage1[index])) { + matchingThreshold = abs_uint(historyImage2[index] - historyImage1[index]); + } + if (abs_uint(image_data[index] - first[index]) <= matchingThreshold) + segmentation_map[index]++; + } - if (doUpdate(updating_mask[index])) { - uint8_t value = image_data[index]; - // In-place substitution. - // if (2*value < (historyImage1[index]+historyImage2[index]) ) { - if (rand() % 2 == 0) { - historyImage1[index] = value; + return(0); } - else { - historyImage2[index] = value; + + // ---------------------------------------------------------------------------- + // Update a C1R model + // ---------------------------------------------------------------------------- + int doUpdate(const uint8_t value) + { + if (value == 0) return 0; + else return 1; } - } - ++shift; - indY += jump[shift]; - } - // Last column. - x = width - 1; - shift = rand() % height; - indY = jump[shift]; // index_jump should never be zero (> 1). + int32_t libtwopointsModel_Update_8u_C1R( + twopointsModel_t *model, + const uint8_t *image_data, + uint8_t *updating_mask + ) + { + assert((image_data != NULL) && (model != NULL) && (updating_mask != NULL)); + assert((model->width > 0) && (model->height > 0)); + assert((model->jump != NULL) && (model->neighbor != NULL)); + + // Some variables. + uint32_t width = model->width; + uint32_t height = model->height; + + uint8_t *historyImage1 = model->historyImage1; + uint8_t *historyImage2 = model->historyImage2; + + // Updating. + uint32_t *jump = model->jump; + int *neighbor = model->neighbor; + + // All the frame, except the border. + uint32_t shift, indX, indY; + unsigned int x, y; + + for (y = 1; y < height - 1; ++y) { + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX < width - 1) { + int index = indX + y * width; + + if (doUpdate(updating_mask[index])) { + uint8_t value = image_data[index]; + // In-place substitution. + // if (2*value < (historyImage1[index]+historyImage2[index]) ) { + if (rand() % 2 == 0) { + historyImage1[index] = value; + } + else { + historyImage2[index] = value; + } + + // Propagation + int index_neighbor = index + neighbor[shift]; + if (2 * value < (historyImage1[index_neighbor] + historyImage2[index_neighbor])) { + // if (rand()%2 == 0 ) { + historyImage1[index_neighbor] = value; + } + else { + historyImage2[index_neighbor] = value; + } + } + + ++shift; + indX += jump[shift]; + } + } - while (indY <= height - 1) { - int index = x + indY * width; + // First row. + y = 0; + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX <= width - 1) { + int index = indX + y * width; + + if (doUpdate(updating_mask[index])) { + uint8_t value = image_data[index]; + // In-place substitution. + // if (2*value < (historyImage1[index]+historyImage2[index]) ) { + if (rand() % 2 == 0) { + historyImage1[index] = value; + } + else { + historyImage2[index] = value; + } + } + + ++shift; + indX += jump[shift]; + } - if (doUpdate(updating_mask[index])) { - uint8_t value = image_data[index]; - // In-place substitution. - // if (2*value < (historyImage1[index]+historyImage2[index]) ) { - if (rand() % 2 == 0) { - historyImage1[index] = value; - } - else { - historyImage2[index] = value; - } - } + // Last row. + y = height - 1; + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX <= width - 1) { + int index = indX + y * width; + + if (doUpdate(updating_mask[index])) { + uint8_t value = image_data[index]; + // In-place substitution. + // if (2*value < (historyImage1[index]+historyImage2[index]) ) { + if (rand() % 2 == 0) { + historyImage1[index] = value; + } + else { + historyImage2[index] = value; + } + } + + ++shift; + indX += jump[shift]; + } - ++shift; - indY += jump[shift]; - } + // First column. + x = 0; + shift = rand() % height; + indY = jump[shift]; // index_jump should never be zero (> 1). + + while (indY <= height - 1) { + int index = x + indY * width; + + if (doUpdate(updating_mask[index])) { + uint8_t value = image_data[index]; + // In-place substitution. + // if (2*value < (historyImage1[index]+historyImage2[index]) ) { + if (rand() % 2 == 0) { + historyImage1[index] = value; + } + else { + historyImage2[index] = value; + } + } + + ++shift; + indY += jump[shift]; + } - // The first pixel! - if (rand() % model->updateFactor == 0) { - if (doUpdate(updating_mask[0])) { - uint8_t value = image_data[0]; - // In-place substitution. - if (rand() % 2 == 0) { - historyImage1[0] = value; - } - else { - historyImage2[0] = value; + // Last column. + x = width - 1; + shift = rand() % height; + indY = jump[shift]; // index_jump should never be zero (> 1). + + while (indY <= height - 1) { + int index = x + indY * width; + + if (doUpdate(updating_mask[index])) { + uint8_t value = image_data[index]; + // In-place substitution. + // if (2*value < (historyImage1[index]+historyImage2[index]) ) { + if (rand() % 2 == 0) { + historyImage1[index] = value; + } + else { + historyImage2[index] = value; + } + } + + ++shift; + indY += jump[shift]; + } + + // The first pixel! + if (rand() % model->updateFactor == 0) { + if (doUpdate(updating_mask[0])) { + uint8_t value = image_data[0]; + // In-place substitution. + if (rand() % 2 == 0) { + historyImage1[0] = value; + } + else { + historyImage2[0] = value; + } + } + } + + return(0); } } } - - return(0); } diff --git a/src/algorithms/TwoPoints/two_points.h b/src/algorithms/TwoPoints/two_points.h index ecf8b6f419de4197c1d3b4bdc5068aea11433850..f249fd3dd60dfc1ed4d3a21c52564723cb9c0fd1 100644 --- a/src/algorithms/TwoPoints/two_points.h +++ b/src/algorithms/TwoPoints/two_points.h @@ -5,30 +5,39 @@ #include <stdio.h> #include <string.h> -#define COLOR_BACKGROUND 0 /*!< Default label for background pixels */ -#define COLOR_FOREGROUND 255 /*!< Default label for foreground pixels. Note that some authors chose any value different from 0 instead */ - -typedef struct twopointsModel twopointsModel_t; - -twopointsModel_t *libtwopointsModel_New(); - -int32_t libtwopointsModel_Free(twopointsModel_t *model); - -int32_t libtwopointsModel_AllocInit_8u_C1R( - twopointsModel_t *model, - const uint8_t *image_data, - const uint32_t width, - const uint32_t height -); - -int32_t libtwopointsModel_Segmentation_8u_C1R( - twopointsModel_t *model, - const uint8_t *image_data, - uint8_t *segmentation_map -); - -int32_t libtwopointsModel_Update_8u_C1R( - twopointsModel_t *model, - const uint8_t *image_data, - uint8_t *updating_mask -); +namespace bgslibrary +{ + namespace algorithms + { + namespace twopoints + { + const int COLOR_BACKGROUND = 0; /*!< Default label for background pixels */ + const int COLOR_FOREGROUND = 255; /*!< Default label for foreground pixels. Note that some authors chose any value different from 0 instead */ + + typedef struct twopointsModel twopointsModel_t; + + twopointsModel_t *libtwopointsModel_New(); + + int32_t libtwopointsModel_Free(twopointsModel_t *model); + + int32_t libtwopointsModel_AllocInit_8u_C1R( + twopointsModel_t *model, + const uint8_t *image_data, + const uint32_t width, + const uint32_t height + ); + + int32_t libtwopointsModel_Segmentation_8u_C1R( + twopointsModel_t *model, + const uint8_t *image_data, + uint8_t *segmentation_map + ); + + int32_t libtwopointsModel_Update_8u_C1R( + twopointsModel_t *model, + const uint8_t *image_data, + uint8_t *updating_mask + ); + } + } +} diff --git a/src/algorithms/ViBe.cpp b/src/algorithms/ViBe.cpp index 16b9df1ccfb90283baa21fc593155d7f4518c30e..460b92673f62ddca29df0b8830c7a1a4aaeb1497 100644 --- a/src/algorithms/ViBe.cpp +++ b/src/algorithms/ViBe.cpp @@ -1,6 +1,7 @@ #include "ViBe.h" using namespace bgslibrary::algorithms; +//using namespace bgslibrary::algorithms::vibe; ViBe::ViBe() : IBGS(quote(ViBe)), @@ -12,12 +13,12 @@ ViBe::ViBe() : { debug_construction(ViBe); initLoadSaveConfig(algorithmName); - model = libvibeModel_Sequential_New(); + model = vibe::libvibeModel_Sequential_New(); } ViBe::~ViBe() { debug_destruction(ViBe); - libvibeModel_Sequential_Free(model); + vibe::libvibeModel_Sequential_Free(model); } void ViBe::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel) @@ -32,18 +33,18 @@ void ViBe::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_b //img_output = cv::Mat(img_input.rows, img_input.cols, CV_8UC1); /* Initialization of the ViBe model. */ - libvibeModel_Sequential_AllocInit_8u_C3R(model, img_input.data, img_input.cols, img_input.rows); + vibe::libvibeModel_Sequential_AllocInit_8u_C3R(model, img_input.data, img_input.cols, img_input.rows); /* Sets default model values. */ - //libvibeModel_Sequential_SetNumberOfSamples(model, numberOfSamples); - libvibeModel_Sequential_SetMatchingThreshold(model, matchingThreshold); - libvibeModel_Sequential_SetMatchingNumber(model, matchingNumber); - libvibeModel_Sequential_SetUpdateFactor(model, updateFactor); + //vibe::libvibeModel_Sequential_SetNumberOfSamples(model, numberOfSamples); + vibe::libvibeModel_Sequential_SetMatchingThreshold(model, matchingThreshold); + vibe::libvibeModel_Sequential_SetMatchingNumber(model, matchingNumber); + vibe::libvibeModel_Sequential_SetUpdateFactor(model, updateFactor); } - libvibeModel_Sequential_Segmentation_8u_C3R(model, img_input.data, img_output.data); - //libvibeModel_Sequential_Update_8u_C3R(model, model_img_input.data, img_output.data); - libvibeModel_Sequential_Update_8u_C3R(model, img_input.data, img_output.data); + vibe::libvibeModel_Sequential_Segmentation_8u_C3R(model, img_input.data, img_output.data); + //vibe::libvibeModel_Sequential_Update_8u_C3R(model, model_img_input.data, img_output.data); + vibe::libvibeModel_Sequential_Update_8u_C3R(model, img_input.data, img_output.data); #ifndef MEX_COMPILE_FLAG if (showOutput) diff --git a/src/algorithms/ViBe.h b/src/algorithms/ViBe.h index 84f000e3c3aa543edce3469b7fe9e80c8ee6a631..067d0834b11ec0dca559173a2603c6de1068d6a4 100644 --- a/src/algorithms/ViBe.h +++ b/src/algorithms/ViBe.h @@ -20,7 +20,7 @@ namespace bgslibrary int matchingThreshold; int matchingNumber; int updateFactor; - vibeModel_Sequential_t* model; + vibe::vibeModel_Sequential_t* model; public: ViBe(); diff --git a/src/algorithms/ViBe/vibe-background-sequential.cpp b/src/algorithms/ViBe/vibe-background-sequential.cpp index 6210733f646cc393ebb08e26f6ce9576839d3955..4bec4e6d0b197ae2542b50fbf2e532b5e76c0950 100644 --- a/src/algorithms/ViBe/vibe-background-sequential.cpp +++ b/src/algorithms/ViBe/vibe-background-sequential.cpp @@ -3,905 +3,913 @@ #include "vibe-background-sequential.h" -#define NUMBER_OF_HISTORY_IMAGES 2 - -uint32_t distance_Han2014Improved(uint8_t pixel, uint8_t bg) -{ - uint8_t min, max; - - // Computes R = 0.13 min{ max[bg,26], 230} - max = 26; - if (bg > max) { max = bg; } - - min = 230; - if (min > max) { min = max; } - - return (uint32_t)(0.13*min); -} - -static int abs_uint(const int i) -{ - return (i >= 0) ? i : -i; -} - -static int32_t distance_is_close_8u_C3R(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2, uint32_t threshold) -{ - return (abs_uint(r1 - r2) + abs_uint(g1 - g2) + abs_uint(b1 - b2) <= 4.5 * threshold); -} - -struct vibeModel_Sequential -{ - /* Parameters. */ - uint32_t width; - uint32_t height; - uint32_t numberOfSamples; - uint32_t matchingThreshold; - uint32_t matchingNumber; - uint32_t updateFactor; - - /* Storage for the history. */ - uint8_t *historyImage; - uint8_t *historyBuffer; - uint32_t lastHistoryImageSwapped; - - /* Buffers with random values. */ - uint32_t *jump; - int *neighbor; - uint32_t *position; -}; - -// ----------------------------------------------------------------------------- -// Print parameters -// ----------------------------------------------------------------------------- -uint32_t libvibeModel_Sequential_PrintParameters(const vibeModel_Sequential_t *model) -{ - printf( - "Using ViBe background subtraction algorithm\n" - " - Number of samples per pixel: %03d\n" - " - Number of matches needed: %03d\n" - " - Matching threshold: %03d\n" - " - Model update subsampling factor: %03d\n", - libvibeModel_Sequential_GetNumberOfSamples(model), - libvibeModel_Sequential_GetMatchingNumber(model), - libvibeModel_Sequential_GetMatchingThreshold(model), - libvibeModel_Sequential_GetUpdateFactor(model) - ); - - return(0); -} - -// ----------------------------------------------------------------------------- -// Creates the data structure -// ----------------------------------------------------------------------------- -vibeModel_Sequential_t *libvibeModel_Sequential_New() -{ - /* Model structure alloc. */ - vibeModel_Sequential_t *model = NULL; - model = (vibeModel_Sequential_t*)calloc(1, sizeof(*model)); - assert(model != NULL); - - /* Default parameters values. */ - model->numberOfSamples = 20; - model->matchingThreshold = 20; - model->matchingNumber = 2; - model->updateFactor = 16; - - /* Storage for the history. */ - model->historyImage = NULL; - model->historyBuffer = NULL; - model->lastHistoryImageSwapped = 0; - - /* Buffers with random values. */ - model->jump = NULL; - model->neighbor = NULL; - model->position = NULL; - - return(model); -} - -// ----------------------------------------------------------------------------- -// Some "Get-ers" -// ----------------------------------------------------------------------------- -uint32_t libvibeModel_Sequential_GetNumberOfSamples(const vibeModel_Sequential_t *model) +//using namespace bgslibrary::algorithms::vibe; +namespace bgslibrary { - assert(model != NULL); return(model->numberOfSamples); -} - -uint32_t libvibeModel_Sequential_GetMatchingNumber(const vibeModel_Sequential_t *model) -{ - assert(model != NULL); return(model->matchingNumber); -} - -uint32_t libvibeModel_Sequential_GetMatchingThreshold(const vibeModel_Sequential_t *model) -{ - assert(model != NULL); return(model->matchingThreshold); -} - -uint32_t libvibeModel_Sequential_GetUpdateFactor(const vibeModel_Sequential_t *model) -{ - assert(model != NULL); return(model->updateFactor); -} - -// ----------------------------------------------------------------------------- -// Some "Set-ers" -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_SetMatchingThreshold( - vibeModel_Sequential_t *model, - const uint32_t matchingThreshold -) { - assert(model != NULL); - assert(matchingThreshold > 0); - - model->matchingThreshold = matchingThreshold; - - return(0); -} - -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_SetMatchingNumber( - vibeModel_Sequential_t *model, - const uint32_t matchingNumber -) { - assert(model != NULL); - assert(matchingNumber > 0); - - model->matchingNumber = matchingNumber; - - return(0); -} - -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_SetUpdateFactor( - vibeModel_Sequential_t *model, - const uint32_t updateFactor -) { - assert(model != NULL); - assert(updateFactor > 0); - - model->updateFactor = updateFactor; - - /* We also need to change the values of the jump buffer ! */ - assert(model->jump != NULL); - - /* Shifts. */ - int size = (model->width > model->height) ? 2 * model->width + 1 : 2 * model->height + 1; - - for (int i = 0; i < size; ++i) - model->jump[i] = (updateFactor == 1) ? 1 : (rand() % (2 * model->updateFactor)) + 1; // 1 or values between 1 and 2 * updateFactor. - - return(0); -} - -// ---------------------------------------------------------------------------- -// Frees the structure -// ---------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_Free(vibeModel_Sequential_t *model) -{ - if (model == NULL) - return(-1); - - if (model->historyBuffer == NULL) { - free(model); - return(0); - } - - free(model->historyImage); - free(model->historyBuffer); - free(model->jump); - free(model->neighbor); - free(model->position); - free(model); - - return(0); -} - -// ----------------------------------------------------------------------------- -// Allocates and initializes a C1R model structure -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_AllocInit_8u_C1R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - const uint32_t width, - const uint32_t height -) { - // Some basic checks. */ - assert((image_data != NULL) && (model != NULL)); - assert((width > 0) && (height > 0)); - - /* Finish model alloc - parameters values cannot be changed anymore. */ - model->width = width; - model->height = height; - - /* Creates the historyImage structure. */ - model->historyImage = NULL; - model->historyImage = (uint8_t*)malloc(NUMBER_OF_HISTORY_IMAGES * width * height * sizeof(*(model->historyImage))); - - assert(model->historyImage != NULL); - - for (int i = 0; i < NUMBER_OF_HISTORY_IMAGES; ++i) { - for (int index = width * height - 1; index >= 0; --index) - model->historyImage[i * width * height + index] = image_data[index]; - } - - /* Now creates and fills the history buffer. */ - model->historyBuffer = (uint8_t*)malloc(width * height * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) * sizeof(uint8_t)); - assert(model->historyBuffer != NULL); - - for (int index = width * height - 1; index >= 0; --index) { - uint8_t value = image_data[index]; - - for (int x = 0; x < model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES; ++x) { - int value_plus_noise = value + rand() % 20 - 10; - - if (value_plus_noise < 0) { value_plus_noise = 0; } - if (value_plus_noise > 255) { value_plus_noise = 255; } - - model->historyBuffer[index * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x] = value_plus_noise; - } - } - - /* Fills the buffers with random values. */ - int size = (width > height) ? 2 * width + 1 : 2 * height + 1; - - model->jump = (uint32_t*)malloc(size * sizeof(*(model->jump))); - assert(model->jump != NULL); - - model->neighbor = (int*)malloc(size * sizeof(*(model->neighbor))); - assert(model->neighbor != NULL); - - model->position = (uint32_t*)malloc(size * sizeof(*(model->position))); - assert(model->position != NULL); - - for (int i = 0; i < size; ++i) { - model->jump[i] = (rand() % (2 * model->updateFactor)) + 1; // Values between 1 and 2 * updateFactor. - model->neighbor[i] = ((rand() % 3) - 1) + ((rand() % 3) - 1) * width; // Values between { -width - 1, ... , width + 1 }. - model->position[i] = rand() % (model->numberOfSamples); // Values between 0 and numberOfSamples - 1. - } - - return(0); -} - -// ----------------------------------------------------------------------------- -// Segmentation of a C1R model -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_Segmentation_8u_C1R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *segmentation_map -) { - /* Basic checks. */ - assert((image_data != NULL) && (model != NULL) && (segmentation_map != NULL)); - assert((model->width > 0) && (model->height > 0)); - assert(model->historyBuffer != NULL); - assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); - - /* Some variables. */ - uint32_t width = model->width; - uint32_t height = model->height; - uint32_t matchingNumber = model->matchingNumber; - //uint32_t matchingThreshold = model->matchingThreshold; - - uint8_t *historyImage = model->historyImage; - uint8_t *historyBuffer = model->historyBuffer; - - /* Segmentation. */ - memset(segmentation_map, matchingNumber - 1, width * height); - - /* First history Image structure. */ - for (int index = width * height - 1; index >= 0; --index) { - //if (abs_uint(image_data[index] - historyImage[index]) > matchingThreshold) - if (abs_uint(image_data[index] - historyImage[index]) > distance_Han2014Improved(image_data[index], historyImage[index])) - segmentation_map[index] = matchingNumber; - } - - /* Next historyImages. */ - for (int i = 1; i < NUMBER_OF_HISTORY_IMAGES; ++i) { - uint8_t *pels = historyImage + i * width * height; - - for (int index = width * height - 1; index >= 0; --index) { - // if (abs_uint(image_data[index] - pels[index]) <= matchingThreshold) - if (abs_uint(image_data[index] - pels[index]) <= distance_Han2014Improved(image_data[index], pels[index])) - --segmentation_map[index]; - } - } - - /* For swapping. */ - model->lastHistoryImageSwapped = (model->lastHistoryImageSwapped + 1) % NUMBER_OF_HISTORY_IMAGES; - uint8_t *swappingImageBuffer = historyImage + (model->lastHistoryImageSwapped) * width * height; - - /* Now, we move in the buffer and leave the historyImages. */ - int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); - - for (int index = width * height - 1; index >= 0; --index) { - if (segmentation_map[index] > 0) { - /* We need to check the full border and swap values with the first or second historyImage. - * We still need to find a match before we can stop our search. - */ - uint32_t indexHistoryBuffer = index * numberOfTests; - uint8_t currentValue = image_data[index]; - - for (int i = numberOfTests; i > 0; --i, ++indexHistoryBuffer) { - // if (abs_uint(currentValue - historyBuffer[indexHistoryBuffer]) <= matchingThreshold) { - if (abs_uint(currentValue - historyBuffer[indexHistoryBuffer]) <= distance_Han2014Improved(currentValue, historyBuffer[indexHistoryBuffer])) { - --segmentation_map[index]; - - /* Swaping: Putting found value in history image buffer. */ - uint8_t temp = swappingImageBuffer[index]; - swappingImageBuffer[index] = historyBuffer[indexHistoryBuffer]; - historyBuffer[indexHistoryBuffer] = temp; - - /* Exit inner loop. */ - if (segmentation_map[index] <= 0) break; - } - } // for - } // if - } // for + namespace algorithms + { + namespace vibe + { + uint32_t distance_Han2014Improved(uint8_t pixel, uint8_t bg) + { + uint8_t min, max; + + // Computes R = 0.13 min{ max[bg,26], 230} + max = 26; + if (bg > max) { max = bg; } + + min = 230; + if (min > max) { min = max; } + + return (uint32_t)(0.13*min); + } - /* Produces the output. Note that this step is application-dependent. */ - for (uint8_t *mask = segmentation_map; mask < segmentation_map + (width * height); ++mask) - if (*mask > 0) *mask = COLOR_FOREGROUND; + static int abs_uint(const int i) + { + return (i >= 0) ? i : -i; + } - return(0); -} + static int32_t distance_is_close_8u_C3R(uint8_t r1, uint8_t g1, uint8_t b1, uint8_t r2, uint8_t g2, uint8_t b2, uint32_t threshold) + { + return (abs_uint(r1 - r2) + abs_uint(g1 - g2) + abs_uint(b1 - b2) <= 4.5 * threshold); + } -// ---------------------------------------------------------------------------- -// Update a C1R model -// ---------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_Update_8u_C1R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *updating_mask -) { - /* Basic checks . */ - assert((image_data != NULL) && (model != NULL) && (updating_mask != NULL)); - assert((model->width > 0) && (model->height > 0)); - assert(model->historyBuffer != NULL); - assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); - - /* Some variables. */ - uint32_t width = model->width; - uint32_t height = model->height; - - uint8_t *historyImage = model->historyImage; - uint8_t *historyBuffer = model->historyBuffer; - - /* Some utility variable. */ - int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); - - /* Updating. */ - uint32_t *jump = model->jump; - int *neighbor = model->neighbor; - uint32_t *position = model->position; - - /* All the frame, except the border. */ - uint32_t shift, indX, indY; - unsigned int x, y; - - for (y = 1; y < height - 1; ++y) { - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). - - while (indX < width - 1) { - int index = indX + y * width; - - if (updating_mask[index] == COLOR_BACKGROUND) { - /* In-place substitution. */ - uint8_t value = image_data[index]; - int index_neighbor = index + neighbor[shift]; - - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { - historyImage[index + position[shift] * width * height] = value; - historyImage[index_neighbor + position[shift] * width * height] = value; - } - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[index * numberOfTests + pos] = value; - historyBuffer[index_neighbor * numberOfTests + pos] = value; - } + struct vibeModel_Sequential + { + /* Parameters. */ + uint32_t width; + uint32_t height; + uint32_t numberOfSamples; + uint32_t matchingThreshold; + uint32_t matchingNumber; + uint32_t updateFactor; + + /* Storage for the history. */ + uint8_t *historyImage; + uint8_t *historyBuffer; + uint32_t lastHistoryImageSwapped; + + /* Buffers with random values. */ + uint32_t *jump; + int *neighbor; + uint32_t *position; + }; + + // ----------------------------------------------------------------------------- + // Print parameters + // ----------------------------------------------------------------------------- + uint32_t libvibeModel_Sequential_PrintParameters(const vibeModel_Sequential_t *model) + { + printf( + "Using ViBe background subtraction algorithm\n" + " - Number of samples per pixel: %03d\n" + " - Number of matches needed: %03d\n" + " - Matching threshold: %03d\n" + " - Model update subsampling factor: %03d\n", + libvibeModel_Sequential_GetNumberOfSamples(model), + libvibeModel_Sequential_GetMatchingNumber(model), + libvibeModel_Sequential_GetMatchingThreshold(model), + libvibeModel_Sequential_GetUpdateFactor(model) + ); + + return(0); } - ++shift; - indX += jump[shift]; - } - } + // ----------------------------------------------------------------------------- + // Creates the data structure + // ----------------------------------------------------------------------------- + vibeModel_Sequential_t *libvibeModel_Sequential_New() + { + /* Model structure alloc. */ + vibeModel_Sequential_t *model = NULL; + model = (vibeModel_Sequential_t*)calloc(1, sizeof(*model)); + assert(model != NULL); + + /* Default parameters values. */ + model->numberOfSamples = 20; + model->matchingThreshold = 20; + model->matchingNumber = 2; + model->updateFactor = 16; + + /* Storage for the history. */ + model->historyImage = NULL; + model->historyBuffer = NULL; + model->lastHistoryImageSwapped = 0; + + /* Buffers with random values. */ + model->jump = NULL; + model->neighbor = NULL; + model->position = NULL; + + return(model); + } - /* First row. */ - y = 0; - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). + // ----------------------------------------------------------------------------- + // Some "Get-ers" + // ----------------------------------------------------------------------------- + uint32_t libvibeModel_Sequential_GetNumberOfSamples(const vibeModel_Sequential_t *model) + { + assert(model != NULL); return(model->numberOfSamples); + } - while (indX <= width - 1) { - int index = indX + y * width; + uint32_t libvibeModel_Sequential_GetMatchingNumber(const vibeModel_Sequential_t *model) + { + assert(model != NULL); return(model->matchingNumber); + } - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) - historyImage[index + position[shift] * width * height] = image_data[index]; - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[index * numberOfTests + pos] = image_data[index]; + uint32_t libvibeModel_Sequential_GetMatchingThreshold(const vibeModel_Sequential_t *model) + { + assert(model != NULL); return(model->matchingThreshold); } - } - ++shift; - indX += jump[shift]; - } + uint32_t libvibeModel_Sequential_GetUpdateFactor(const vibeModel_Sequential_t *model) + { + assert(model != NULL); return(model->updateFactor); + } - /* Last row. */ - y = height - 1; - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). + // ----------------------------------------------------------------------------- + // Some "Set-ers" + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_SetMatchingThreshold( + vibeModel_Sequential_t *model, + const uint32_t matchingThreshold + ) { + assert(model != NULL); + assert(matchingThreshold > 0); - while (indX <= width - 1) { - int index = indX + y * width; + model->matchingThreshold = matchingThreshold; - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) - historyImage[index + position[shift] * width * height] = image_data[index]; - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[index * numberOfTests + pos] = image_data[index]; + return(0); } - } - - ++shift; - indX += jump[shift]; - } - /* First column. */ - x = 0; - shift = rand() % height; - indY = jump[shift]; // index_jump should never be zero (> 1). + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_SetMatchingNumber( + vibeModel_Sequential_t *model, + const uint32_t matchingNumber + ) { + assert(model != NULL); + assert(matchingNumber > 0); - while (indY <= height - 1) { - int index = x + indY * width; + model->matchingNumber = matchingNumber; - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) - historyImage[index + position[shift] * width * height] = image_data[index]; - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[index * numberOfTests + pos] = image_data[index]; + return(0); } - } - ++shift; - indY += jump[shift]; - } + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_SetUpdateFactor( + vibeModel_Sequential_t *model, + const uint32_t updateFactor + ) { + assert(model != NULL); + assert(updateFactor > 0); - /* Last column. */ - x = width - 1; - shift = rand() % height; - indY = jump[shift]; // index_jump should never be zero (> 1). + model->updateFactor = updateFactor; - while (indY <= height - 1) { - int index = x + indY * width; + /* We also need to change the values of the jump buffer ! */ + assert(model->jump != NULL); - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) - historyImage[index + position[shift] * width * height] = image_data[index]; - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[index * numberOfTests + pos] = image_data[index]; - } - } + /* Shifts. */ + int size = (model->width > model->height) ? 2 * model->width + 1 : 2 * model->height + 1; - ++shift; - indY += jump[shift]; - } + for (int i = 0; i < size; ++i) + model->jump[i] = (updateFactor == 1) ? 1 : (rand() % (2 * model->updateFactor)) + 1; // 1 or values between 1 and 2 * updateFactor. - /* The first pixel! */ - if (rand() % model->updateFactor == 0) { - if (updating_mask[0] == 0) { - int position = rand() % model->numberOfSamples; - - if (position < NUMBER_OF_HISTORY_IMAGES) - historyImage[position * width * height] = image_data[0]; - else { - int pos = position - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[pos] = image_data[0]; + return(0); } - } - } - return(0); -} + // ---------------------------------------------------------------------------- + // Frees the structure + // ---------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_Free(vibeModel_Sequential_t *model) + { + if (model == NULL) + return(-1); + + if (model->historyBuffer == NULL) { + free(model); + return(0); + } -// ---------------------------------------------------------------------------- -// -------------------------- The same for C3R models ------------------------- -// ---------------------------------------------------------------------------- - -// ----------------------------------------------------------------------------- -// Allocates and initializes a C3R model structure -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_AllocInit_8u_C3R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - const uint32_t width, - const uint32_t height -) { - /* Some basic checks. */ - assert((image_data != NULL) && (model != NULL)); - assert((width > 0) && (height > 0)); - - /* Finish model alloc - parameters values cannot be changed anymore. */ - model->width = width; - model->height = height; - - /* Creates the historyImage structure. */ - model->historyImage = NULL; - model->historyImage = (uint8_t*)malloc(NUMBER_OF_HISTORY_IMAGES * (3 * width) * height * sizeof(uint8_t)); - assert(model->historyImage != NULL); - - for (int i = 0; i < NUMBER_OF_HISTORY_IMAGES; ++i) { - for (int index = (3 * width) * height - 1; index >= 0; --index) - model->historyImage[i * (3 * width) * height + index] = image_data[index]; - } + free(model->historyImage); + free(model->historyBuffer); + free(model->jump); + free(model->neighbor); + free(model->position); + free(model); - assert(model->historyImage != NULL); + return(0); + } - /* Now creates and fills the history buffer. */ - model->historyBuffer = (uint8_t *)malloc((3 * width) * height * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) * sizeof(uint8_t)); - assert(model->historyBuffer != NULL); + // ----------------------------------------------------------------------------- + // Allocates and initializes a C1R model structure + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_AllocInit_8u_C1R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + const uint32_t width, + const uint32_t height + ) { + // Some basic checks. */ + assert((image_data != NULL) && (model != NULL)); + assert((width > 0) && (height > 0)); + + /* Finish model alloc - parameters values cannot be changed anymore. */ + model->width = width; + model->height = height; + + /* Creates the historyImage structure. */ + model->historyImage = NULL; + model->historyImage = (uint8_t*)malloc(NUMBER_OF_HISTORY_IMAGES * width * height * sizeof(*(model->historyImage))); + + assert(model->historyImage != NULL); + + for (int i = 0; i < NUMBER_OF_HISTORY_IMAGES; ++i) { + for (int index = width * height - 1; index >= 0; --index) + model->historyImage[i * width * height + index] = image_data[index]; + } - for (int index = (3 * width) * height - 1; index >= 0; --index) { - uint8_t value = image_data[index]; + /* Now creates and fills the history buffer. */ + model->historyBuffer = (uint8_t*)malloc(width * height * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) * sizeof(uint8_t)); + assert(model->historyBuffer != NULL); - for (int x = 0; x < model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES; ++x) { - int value_plus_noise = value + rand() % 20 - 10; + for (int index = width * height - 1; index >= 0; --index) { + uint8_t value = image_data[index]; - if (value_plus_noise < 0) { value_plus_noise = 0; } - if (value_plus_noise > 255) { value_plus_noise = 255; } + for (int x = 0; x < model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES; ++x) { + int value_plus_noise = value + rand() % 20 - 10; - model->historyBuffer[index * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x] = value_plus_noise; - } - } + if (value_plus_noise < 0) { value_plus_noise = 0; } + if (value_plus_noise > 255) { value_plus_noise = 255; } - /* Fills the buffers with random values. */ - int size = (width > height) ? 2 * width + 1 : 2 * height + 1; + model->historyBuffer[index * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x] = value_plus_noise; + } + } - model->jump = (uint32_t*)malloc(size * sizeof(*(model->jump))); - assert(model->jump != NULL); + /* Fills the buffers with random values. */ + int size = (width > height) ? 2 * width + 1 : 2 * height + 1; - model->neighbor = (int*)malloc(size * sizeof(*(model->neighbor))); - assert(model->neighbor != NULL); + model->jump = (uint32_t*)malloc(size * sizeof(*(model->jump))); + assert(model->jump != NULL); - model->position = (uint32_t*)malloc(size * sizeof(*(model->position))); - assert(model->position != NULL); + model->neighbor = (int*)malloc(size * sizeof(*(model->neighbor))); + assert(model->neighbor != NULL); - for (int i = 0; i < size; ++i) { - model->jump[i] = (rand() % (2 * model->updateFactor)) + 1; // Values between 1 and 2 * updateFactor. - model->neighbor[i] = ((rand() % 3) - 1) + ((rand() % 3) - 1) * width; // Values between { width - 1, ... , width + 1 }. - model->position[i] = rand() % (model->numberOfSamples); // Values between 0 and numberOfSamples - 1. - } + model->position = (uint32_t*)malloc(size * sizeof(*(model->position))); + assert(model->position != NULL); - return(0); -} + for (int i = 0; i < size; ++i) { + model->jump[i] = (rand() % (2 * model->updateFactor)) + 1; // Values between 1 and 2 * updateFactor. + model->neighbor[i] = ((rand() % 3) - 1) + ((rand() % 3) - 1) * width; // Values between { -width - 1, ... , width + 1 }. + model->position[i] = rand() % (model->numberOfSamples); // Values between 0 and numberOfSamples - 1. + } -// ----------------------------------------------------------------------------- -// Segmentation of a C3R model -// ----------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_Segmentation_8u_C3R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *segmentation_map -) { - /* Basic checks. */ - assert((image_data != NULL) && (model != NULL) && (segmentation_map != NULL)); - assert((model->width > 0) && (model->height > 0)); - assert(model->historyBuffer != NULL); - assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); - - /* Some variables. */ - uint32_t width = model->width; - uint32_t height = model->height; - uint32_t matchingNumber = model->matchingNumber; - uint32_t matchingThreshold = model->matchingThreshold; - - uint8_t *historyImage = model->historyImage; - uint8_t *historyBuffer = model->historyBuffer; - - /* Segmentation. */ - memset(segmentation_map, matchingNumber - 1, width * height); - - /* First history Image structure. */ - uint8_t *first = historyImage; - - for (int index = width * height - 1; index >= 0; --index) { - if ( - !distance_is_close_8u_C3R( - image_data[3 * index], image_data[3 * index + 1], image_data[3 * index + 2], - first[3 * index], first[3 * index + 1], first[3 * index + 2], matchingThreshold - ) - ) - segmentation_map[index] = matchingNumber; - } + return(0); + } - /* Next historyImages. */ - for (int i = 1; i < NUMBER_OF_HISTORY_IMAGES; ++i) { - uint8_t *pels = historyImage + i * (3 * width) * height; - - for (int index = width * height - 1; index >= 0; --index) { - if ( - distance_is_close_8u_C3R( - image_data[3 * index], image_data[3 * index + 1], image_data[3 * index + 2], - pels[3 * index], pels[3 * index + 1], pels[3 * index + 2], matchingThreshold - ) - ) - --segmentation_map[index]; - } - } + // ----------------------------------------------------------------------------- + // Segmentation of a C1R model + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_Segmentation_8u_C1R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *segmentation_map + ) { + /* Basic checks. */ + assert((image_data != NULL) && (model != NULL) && (segmentation_map != NULL)); + assert((model->width > 0) && (model->height > 0)); + assert(model->historyBuffer != NULL); + assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); + + /* Some variables. */ + uint32_t width = model->width; + uint32_t height = model->height; + uint32_t matchingNumber = model->matchingNumber; + //uint32_t matchingThreshold = model->matchingThreshold; + + uint8_t *historyImage = model->historyImage; + uint8_t *historyBuffer = model->historyBuffer; + + /* Segmentation. */ + memset(segmentation_map, matchingNumber - 1, width * height); + + /* First history Image structure. */ + for (int index = width * height - 1; index >= 0; --index) { + //if (abs_uint(image_data[index] - historyImage[index]) > matchingThreshold) + if (abs_uint(image_data[index] - historyImage[index]) > distance_Han2014Improved(image_data[index], historyImage[index])) + segmentation_map[index] = matchingNumber; + } - // For swapping - model->lastHistoryImageSwapped = (model->lastHistoryImageSwapped + 1) % NUMBER_OF_HISTORY_IMAGES; - uint8_t *swappingImageBuffer = historyImage + (model->lastHistoryImageSwapped) * (3 * width) * height; - - // Now, we move in the buffer and leave the historyImages - int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); - - for (int index = width * height - 1; index >= 0; --index) { - if (segmentation_map[index] > 0) { - /* We need to check the full border and swap values with the first or second historyImage. - * We still need to find a match before we can stop our search. - */ - uint32_t indexHistoryBuffer = (3 * index) * numberOfTests; - - for (int i = numberOfTests; i > 0; --i, indexHistoryBuffer += 3) { - if ( - distance_is_close_8u_C3R( - image_data[(3 * index)], image_data[(3 * index) + 1], image_data[(3 * index) + 2], - historyBuffer[indexHistoryBuffer], historyBuffer[indexHistoryBuffer + 1], historyBuffer[indexHistoryBuffer + 2], - matchingThreshold - ) - ) - --segmentation_map[index]; - - /* Swaping: Putting found value in history image buffer. */ - uint8_t temp_r = swappingImageBuffer[(3 * index)]; - uint8_t temp_g = swappingImageBuffer[(3 * index) + 1]; - uint8_t temp_b = swappingImageBuffer[(3 * index) + 2]; - - swappingImageBuffer[(3 * index)] = historyBuffer[indexHistoryBuffer]; - swappingImageBuffer[(3 * index) + 1] = historyBuffer[indexHistoryBuffer + 1]; - swappingImageBuffer[(3 * index) + 2] = historyBuffer[indexHistoryBuffer + 2]; - - historyBuffer[indexHistoryBuffer] = temp_r; - historyBuffer[indexHistoryBuffer + 1] = temp_g; - historyBuffer[indexHistoryBuffer + 2] = temp_b; - - /* Exit inner loop. */ - if (segmentation_map[index] <= 0) break; - } // for - } // if - } // for - - /* Produces the output. Note that this step is application-dependent. */ - for (uint8_t *mask = segmentation_map; mask < segmentation_map + (width * height); ++mask) - if (*mask > 0) *mask = COLOR_FOREGROUND; - - return(0); -} + /* Next historyImages. */ + for (int i = 1; i < NUMBER_OF_HISTORY_IMAGES; ++i) { + uint8_t *pels = historyImage + i * width * height; -// ---------------------------------------------------------------------------- -// Update a C3R model -// ---------------------------------------------------------------------------- -int32_t libvibeModel_Sequential_Update_8u_C3R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *updating_mask -) { - /* Basic checks. */ - assert((image_data != NULL) && (model != NULL) && (updating_mask != NULL)); - assert((model->width > 0) && (model->height > 0)); - assert(model->historyBuffer != NULL); - assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); - - /* Some variables. */ - uint32_t width = model->width; - uint32_t height = model->height; - - uint8_t *historyImage = model->historyImage; - uint8_t *historyBuffer = model->historyBuffer; - - /* Some utility variable. */ - int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); - - /* Updating. */ - uint32_t *jump = model->jump; - int *neighbor = model->neighbor; - uint32_t *position = model->position; - - /* All the frame, except the border. */ - uint32_t shift, indX, indY; - int x, y; - - for (y = 1; y < height - 1; ++y) { - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). - - while (indX < width - 1) { - int index = indX + y * width; - - if (updating_mask[index] == COLOR_BACKGROUND) { - /* In-place substitution. */ - uint8_t r = image_data[3 * index]; - uint8_t g = image_data[3 * index + 1]; - uint8_t b = image_data[3 * index + 2]; - - int index_neighbor = 3 * (index + neighbor[shift]); - - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { - historyImage[3 * index + position[shift] * (3 * width) * height] = r; - historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; - historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; - - historyImage[index_neighbor + position[shift] * (3 * width) * height] = r; - historyImage[index_neighbor + position[shift] * (3 * width) * height + 1] = g; - historyImage[index_neighbor + position[shift] * (3 * width) * height + 2] = b; + for (int index = width * height - 1; index >= 0; --index) { + // if (abs_uint(image_data[index] - pels[index]) <= matchingThreshold) + if (abs_uint(image_data[index] - pels[index]) <= distance_Han2014Improved(image_data[index], pels[index])) + --segmentation_map[index]; + } } - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + /* For swapping. */ + model->lastHistoryImageSwapped = (model->lastHistoryImageSwapped + 1) % NUMBER_OF_HISTORY_IMAGES; + uint8_t *swappingImageBuffer = historyImage + (model->lastHistoryImageSwapped) * width * height; + + /* Now, we move in the buffer and leave the historyImages. */ + int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); + + for (int index = width * height - 1; index >= 0; --index) { + if (segmentation_map[index] > 0) { + /* We need to check the full border and swap values with the first or second historyImage. + * We still need to find a match before we can stop our search. + */ + uint32_t indexHistoryBuffer = index * numberOfTests; + uint8_t currentValue = image_data[index]; + + for (int i = numberOfTests; i > 0; --i, ++indexHistoryBuffer) { + // if (abs_uint(currentValue - historyBuffer[indexHistoryBuffer]) <= matchingThreshold) { + if (abs_uint(currentValue - historyBuffer[indexHistoryBuffer]) <= distance_Han2014Improved(currentValue, historyBuffer[indexHistoryBuffer])) { + --segmentation_map[index]; + + /* Swaping: Putting found value in history image buffer. */ + uint8_t temp = swappingImageBuffer[index]; + swappingImageBuffer[index] = historyBuffer[indexHistoryBuffer]; + historyBuffer[indexHistoryBuffer] = temp; + + /* Exit inner loop. */ + if (segmentation_map[index] <= 0) break; + } + } // for + } // if + } // for + + /* Produces the output. Note that this step is application-dependent. */ + for (uint8_t *mask = segmentation_map; mask < segmentation_map + (width * height); ++mask) + if (*mask > 0) *mask = COLOR_FOREGROUND; + + return(0); + } - historyBuffer[index_neighbor * numberOfTests + 3 * pos] = r; - historyBuffer[index_neighbor * numberOfTests + 3 * pos + 1] = g; - historyBuffer[index_neighbor * numberOfTests + 3 * pos + 2] = b; + // ---------------------------------------------------------------------------- + // Update a C1R model + // ---------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_Update_8u_C1R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *updating_mask + ) { + /* Basic checks . */ + assert((image_data != NULL) && (model != NULL) && (updating_mask != NULL)); + assert((model->width > 0) && (model->height > 0)); + assert(model->historyBuffer != NULL); + assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); + + /* Some variables. */ + uint32_t width = model->width; + uint32_t height = model->height; + + uint8_t *historyImage = model->historyImage; + uint8_t *historyBuffer = model->historyBuffer; + + /* Some utility variable. */ + int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); + + /* Updating. */ + uint32_t *jump = model->jump; + int *neighbor = model->neighbor; + uint32_t *position = model->position; + + /* All the frame, except the border. */ + uint32_t shift, indX, indY; + unsigned int x, y; + + for (y = 1; y < height - 1; ++y) { + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX < width - 1) { + int index = indX + y * width; + + if (updating_mask[index] == COLOR_BACKGROUND) { + /* In-place substitution. */ + uint8_t value = image_data[index]; + int index_neighbor = index + neighbor[shift]; + + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { + historyImage[index + position[shift] * width * height] = value; + historyImage[index_neighbor + position[shift] * width * height] = value; + } + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[index * numberOfTests + pos] = value; + historyBuffer[index_neighbor * numberOfTests + pos] = value; + } + } + + ++shift; + indX += jump[shift]; + } } - } - ++shift; - indX += jump[shift]; - } - } + /* First row. */ + y = 0; + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX <= width - 1) { + int index = indX + y * width; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) + historyImage[index + position[shift] * width * height] = image_data[index]; + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[index * numberOfTests + pos] = image_data[index]; + } + } + + ++shift; + indX += jump[shift]; + } - /* First row. */ - y = 0; - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). + /* Last row. */ + y = height - 1; + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX <= width - 1) { + int index = indX + y * width; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) + historyImage[index + position[shift] * width * height] = image_data[index]; + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[index * numberOfTests + pos] = image_data[index]; + } + } + + ++shift; + indX += jump[shift]; + } - while (indX <= width - 1) { - int index = indX + y * width; + /* First column. */ + x = 0; + shift = rand() % height; + indY = jump[shift]; // index_jump should never be zero (> 1). + + while (indY <= height - 1) { + int index = x + indY * width; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) + historyImage[index + position[shift] * width * height] = image_data[index]; + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[index * numberOfTests + pos] = image_data[index]; + } + } + + ++shift; + indY += jump[shift]; + } - uint8_t r = image_data[3 * index]; - uint8_t g = image_data[3 * index + 1]; - uint8_t b = image_data[3 * index + 2]; + /* Last column. */ + x = width - 1; + shift = rand() % height; + indY = jump[shift]; // index_jump should never be zero (> 1). + + while (indY <= height - 1) { + int index = x + indY * width; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) + historyImage[index + position[shift] * width * height] = image_data[index]; + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[index * numberOfTests + pos] = image_data[index]; + } + } + + ++shift; + indY += jump[shift]; + } - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { - historyImage[3 * index + position[shift] * (3 * width) * height] = r; - historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; - historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; - } - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + /* The first pixel! */ + if (rand() % model->updateFactor == 0) { + if (updating_mask[0] == 0) { + int position = rand() % model->numberOfSamples; + + if (position < NUMBER_OF_HISTORY_IMAGES) + historyImage[position * width * height] = image_data[0]; + else { + int pos = position - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[pos] = image_data[0]; + } + } + } - historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + return(0); } - } - ++shift; - indX += jump[shift]; - } + // ---------------------------------------------------------------------------- + // -------------------------- The same for C3R models ------------------------- + // ---------------------------------------------------------------------------- + + // ----------------------------------------------------------------------------- + // Allocates and initializes a C3R model structure + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_AllocInit_8u_C3R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + const uint32_t width, + const uint32_t height + ) { + /* Some basic checks. */ + assert((image_data != NULL) && (model != NULL)); + assert((width > 0) && (height > 0)); + + /* Finish model alloc - parameters values cannot be changed anymore. */ + model->width = width; + model->height = height; + + /* Creates the historyImage structure. */ + model->historyImage = NULL; + model->historyImage = (uint8_t*)malloc(NUMBER_OF_HISTORY_IMAGES * (3 * width) * height * sizeof(uint8_t)); + assert(model->historyImage != NULL); + + for (int i = 0; i < NUMBER_OF_HISTORY_IMAGES; ++i) { + for (int index = (3 * width) * height - 1; index >= 0; --index) + model->historyImage[i * (3 * width) * height + index] = image_data[index]; + } - /* Last row. */ - y = height - 1; - shift = rand() % width; - indX = jump[shift]; // index_jump should never be zero (> 1). + assert(model->historyImage != NULL); - while (indX <= width - 1) { - int index = indX + y * width; + /* Now creates and fills the history buffer. */ + model->historyBuffer = (uint8_t *)malloc((3 * width) * height * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) * sizeof(uint8_t)); + assert(model->historyBuffer != NULL); - uint8_t r = image_data[3 * index]; - uint8_t g = image_data[3 * index + 1]; - uint8_t b = image_data[3 * index + 2]; + for (int index = (3 * width) * height - 1; index >= 0; --index) { + uint8_t value = image_data[index]; - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { - historyImage[3 * index + position[shift] * (3 * width) * height] = r; - historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; - historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; - } - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + for (int x = 0; x < model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES; ++x) { + int value_plus_noise = value + rand() % 20 - 10; - historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; - } - } + if (value_plus_noise < 0) { value_plus_noise = 0; } + if (value_plus_noise > 255) { value_plus_noise = 255; } - ++shift; - indX += jump[shift]; - } + model->historyBuffer[index * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x] = value_plus_noise; + } + } - /* First column. */ - x = 0; - shift = rand() % height; - indY = jump[shift]; // index_jump should never be zero (> 1). + /* Fills the buffers with random values. */ + int size = (width > height) ? 2 * width + 1 : 2 * height + 1; - while (indY <= height - 1) { - int index = x + indY * width; + model->jump = (uint32_t*)malloc(size * sizeof(*(model->jump))); + assert(model->jump != NULL); - uint8_t r = image_data[3 * index]; - uint8_t g = image_data[3 * index + 1]; - uint8_t b = image_data[3 * index + 2]; + model->neighbor = (int*)malloc(size * sizeof(*(model->neighbor))); + assert(model->neighbor != NULL); - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { - historyImage[3 * index + position[shift] * (3 * width) * height] = r; - historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; - historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; - } - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; - } - } + model->position = (uint32_t*)malloc(size * sizeof(*(model->position))); + assert(model->position != NULL); - ++shift; - indY += jump[shift]; - } + for (int i = 0; i < size; ++i) { + model->jump[i] = (rand() % (2 * model->updateFactor)) + 1; // Values between 1 and 2 * updateFactor. + model->neighbor[i] = ((rand() % 3) - 1) + ((rand() % 3) - 1) * width; // Values between { width - 1, ... , width + 1 }. + model->position[i] = rand() % (model->numberOfSamples); // Values between 0 and numberOfSamples - 1. + } - /* Last column. */ - x = width - 1; - shift = rand() % height; - indY = jump[shift]; // index_jump should never be zero (> 1). + return(0); + } - while (indY <= height - 1) { - int index = x + indY * width; + // ----------------------------------------------------------------------------- + // Segmentation of a C3R model + // ----------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_Segmentation_8u_C3R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *segmentation_map + ) { + /* Basic checks. */ + assert((image_data != NULL) && (model != NULL) && (segmentation_map != NULL)); + assert((model->width > 0) && (model->height > 0)); + assert(model->historyBuffer != NULL); + assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); + + /* Some variables. */ + uint32_t width = model->width; + uint32_t height = model->height; + uint32_t matchingNumber = model->matchingNumber; + uint32_t matchingThreshold = model->matchingThreshold; + + uint8_t *historyImage = model->historyImage; + uint8_t *historyBuffer = model->historyBuffer; + + /* Segmentation. */ + memset(segmentation_map, matchingNumber - 1, width * height); + + /* First history Image structure. */ + uint8_t *first = historyImage; + + for (int index = width * height - 1; index >= 0; --index) { + if ( + !distance_is_close_8u_C3R( + image_data[3 * index], image_data[3 * index + 1], image_data[3 * index + 2], + first[3 * index], first[3 * index + 1], first[3 * index + 2], matchingThreshold + ) + ) + segmentation_map[index] = matchingNumber; + } - uint8_t r = image_data[3 * index]; - uint8_t g = image_data[3 * index + 1]; - uint8_t b = image_data[3 * index + 2]; + /* Next historyImages. */ + for (int i = 1; i < NUMBER_OF_HISTORY_IMAGES; ++i) { + uint8_t *pels = historyImage + i * (3 * width) * height; + + for (int index = width * height - 1; index >= 0; --index) { + if ( + distance_is_close_8u_C3R( + image_data[3 * index], image_data[3 * index + 1], image_data[3 * index + 2], + pels[3 * index], pels[3 * index + 1], pels[3 * index + 2], matchingThreshold + ) + ) + --segmentation_map[index]; + } + } - if (updating_mask[index] == COLOR_BACKGROUND) { - if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { - historyImage[3 * index + position[shift] * (3 * width) * height] = r; - historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; - historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; + // For swapping + model->lastHistoryImageSwapped = (model->lastHistoryImageSwapped + 1) % NUMBER_OF_HISTORY_IMAGES; + uint8_t *swappingImageBuffer = historyImage + (model->lastHistoryImageSwapped) * (3 * width) * height; + + // Now, we move in the buffer and leave the historyImages + int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); + + for (int index = width * height - 1; index >= 0; --index) { + if (segmentation_map[index] > 0) { + /* We need to check the full border and swap values with the first or second historyImage. + * We still need to find a match before we can stop our search. + */ + uint32_t indexHistoryBuffer = (3 * index) * numberOfTests; + + for (int i = numberOfTests; i > 0; --i, indexHistoryBuffer += 3) { + if ( + distance_is_close_8u_C3R( + image_data[(3 * index)], image_data[(3 * index) + 1], image_data[(3 * index) + 2], + historyBuffer[indexHistoryBuffer], historyBuffer[indexHistoryBuffer + 1], historyBuffer[indexHistoryBuffer + 2], + matchingThreshold + ) + ) + --segmentation_map[index]; + + /* Swaping: Putting found value in history image buffer. */ + uint8_t temp_r = swappingImageBuffer[(3 * index)]; + uint8_t temp_g = swappingImageBuffer[(3 * index) + 1]; + uint8_t temp_b = swappingImageBuffer[(3 * index) + 2]; + + swappingImageBuffer[(3 * index)] = historyBuffer[indexHistoryBuffer]; + swappingImageBuffer[(3 * index) + 1] = historyBuffer[indexHistoryBuffer + 1]; + swappingImageBuffer[(3 * index) + 2] = historyBuffer[indexHistoryBuffer + 2]; + + historyBuffer[indexHistoryBuffer] = temp_r; + historyBuffer[indexHistoryBuffer + 1] = temp_g; + historyBuffer[indexHistoryBuffer + 2] = temp_b; + + /* Exit inner loop. */ + if (segmentation_map[index] <= 0) break; + } // for + } // if + } // for + + /* Produces the output. Note that this step is application-dependent. */ + for (uint8_t *mask = segmentation_map; mask < segmentation_map + (width * height); ++mask) + if (*mask > 0) *mask = COLOR_FOREGROUND; + + return(0); } - else { - int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; - historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; - historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; - } - } + // ---------------------------------------------------------------------------- + // Update a C3R model + // ---------------------------------------------------------------------------- + int32_t libvibeModel_Sequential_Update_8u_C3R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *updating_mask + ) { + /* Basic checks. */ + assert((image_data != NULL) && (model != NULL) && (updating_mask != NULL)); + assert((model->width > 0) && (model->height > 0)); + assert(model->historyBuffer != NULL); + assert((model->jump != NULL) && (model->neighbor != NULL) && (model->position != NULL)); + + /* Some variables. */ + uint32_t width = model->width; + uint32_t height = model->height; + + uint8_t *historyImage = model->historyImage; + uint8_t *historyBuffer = model->historyBuffer; + + /* Some utility variable. */ + int numberOfTests = (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES); + + /* Updating. */ + uint32_t *jump = model->jump; + int *neighbor = model->neighbor; + uint32_t *position = model->position; + + /* All the frame, except the border. */ + uint32_t shift, indX, indY; + int x, y; + + for (y = 1; y < height - 1; ++y) { + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX < width - 1) { + int index = indX + y * width; + + if (updating_mask[index] == COLOR_BACKGROUND) { + /* In-place substitution. */ + uint8_t r = image_data[3 * index]; + uint8_t g = image_data[3 * index + 1]; + uint8_t b = image_data[3 * index + 2]; + + int index_neighbor = 3 * (index + neighbor[shift]); + + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { + historyImage[3 * index + position[shift] * (3 * width) * height] = r; + historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; + historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; + + historyImage[index_neighbor + position[shift] * (3 * width) * height] = r; + historyImage[index_neighbor + position[shift] * (3 * width) * height + 1] = g; + historyImage[index_neighbor + position[shift] * (3 * width) * height + 2] = b; + } + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + + historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + + historyBuffer[index_neighbor * numberOfTests + 3 * pos] = r; + historyBuffer[index_neighbor * numberOfTests + 3 * pos + 1] = g; + historyBuffer[index_neighbor * numberOfTests + 3 * pos + 2] = b; + } + } + + ++shift; + indX += jump[shift]; + } + } - ++shift; - indY += jump[shift]; - } + /* First row. */ + y = 0; + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX <= width - 1) { + int index = indX + y * width; + + uint8_t r = image_data[3 * index]; + uint8_t g = image_data[3 * index + 1]; + uint8_t b = image_data[3 * index + 2]; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { + historyImage[3 * index + position[shift] * (3 * width) * height] = r; + historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; + historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; + } + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + + historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + } + } + + ++shift; + indX += jump[shift]; + } - /* The first pixel! */ - if (rand() % model->updateFactor == 0) { - if (updating_mask[0] == 0) { - int position = rand() % model->numberOfSamples; + /* Last row. */ + y = height - 1; + shift = rand() % width; + indX = jump[shift]; // index_jump should never be zero (> 1). + + while (indX <= width - 1) { + int index = indX + y * width; + + uint8_t r = image_data[3 * index]; + uint8_t g = image_data[3 * index + 1]; + uint8_t b = image_data[3 * index + 2]; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { + historyImage[3 * index + position[shift] * (3 * width) * height] = r; + historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; + historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; + } + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + + historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + } + } + + ++shift; + indX += jump[shift]; + } - uint8_t r = image_data[0]; - uint8_t g = image_data[1]; - uint8_t b = image_data[2]; + /* First column. */ + x = 0; + shift = rand() % height; + indY = jump[shift]; // index_jump should never be zero (> 1). + + while (indY <= height - 1) { + int index = x + indY * width; + + uint8_t r = image_data[3 * index]; + uint8_t g = image_data[3 * index + 1]; + uint8_t b = image_data[3 * index + 2]; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { + historyImage[3 * index + position[shift] * (3 * width) * height] = r; + historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; + historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; + } + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + } + } + + ++shift; + indY += jump[shift]; + } - if (position < NUMBER_OF_HISTORY_IMAGES) { - historyImage[position * (3 * width) * height] = r; - historyImage[position * (3 * width) * height + 1] = g; - historyImage[position * (3 * width) * height + 2] = b; - } - else { - int pos = position - NUMBER_OF_HISTORY_IMAGES; + /* Last column. */ + x = width - 1; + shift = rand() % height; + indY = jump[shift]; // index_jump should never be zero (> 1). + + while (indY <= height - 1) { + int index = x + indY * width; + + uint8_t r = image_data[3 * index]; + uint8_t g = image_data[3 * index + 1]; + uint8_t b = image_data[3 * index + 2]; + + if (updating_mask[index] == COLOR_BACKGROUND) { + if (position[shift] < NUMBER_OF_HISTORY_IMAGES) { + historyImage[3 * index + position[shift] * (3 * width) * height] = r; + historyImage[3 * index + position[shift] * (3 * width) * height + 1] = g; + historyImage[3 * index + position[shift] * (3 * width) * height + 2] = b; + } + else { + int pos = position[shift] - NUMBER_OF_HISTORY_IMAGES; + + historyBuffer[(3 * index) * numberOfTests + 3 * pos] = r; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 1] = g; + historyBuffer[(3 * index) * numberOfTests + 3 * pos + 2] = b; + } + } + + ++shift; + indY += jump[shift]; + } + + /* The first pixel! */ + if (rand() % model->updateFactor == 0) { + if (updating_mask[0] == 0) { + int position = rand() % model->numberOfSamples; + + uint8_t r = image_data[0]; + uint8_t g = image_data[1]; + uint8_t b = image_data[2]; + + if (position < NUMBER_OF_HISTORY_IMAGES) { + historyImage[position * (3 * width) * height] = r; + historyImage[position * (3 * width) * height + 1] = g; + historyImage[position * (3 * width) * height + 2] = b; + } + else { + int pos = position - NUMBER_OF_HISTORY_IMAGES; + + historyBuffer[3 * pos] = r; + historyBuffer[3 * pos + 1] = g; + historyBuffer[3 * pos + 2] = b; + } + } + } - historyBuffer[3 * pos] = r; - historyBuffer[3 * pos + 1] = g; - historyBuffer[3 * pos + 2] = b; + return(0); } } } - - return(0); } diff --git a/src/algorithms/ViBe/vibe-background-sequential.h b/src/algorithms/ViBe/vibe-background-sequential.h index c119be4cbd9a9e49c013cbc067edcff40df4b9af..e68ba0aecaf3c845262ee82dd7d2ce451c115a98 100644 --- a/src/algorithms/ViBe/vibe-background-sequential.h +++ b/src/algorithms/ViBe/vibe-background-sequential.h @@ -5,239 +5,249 @@ #include <stdio.h> #include <string.h> -#define COLOR_BACKGROUND 0 /*!< Default label for background pixels */ -#define COLOR_FOREGROUND 255 /*!< Default label for foreground pixels. Note that some authors chose any value different from 0 instead */ - -/** - * \typedef struct vibeModel_Sequential_t - * \brief Data structure for the background subtraction model. - * - * This data structure contains the background model as well as some paramaters value. - * The code is designed to hide all the implementation details to the user to ease its use. - */ -typedef struct vibeModel_Sequential vibeModel_Sequential_t; - -/** - * Allocation of a new data structure where the background model will be stored. - * Please note that this function only creates the structure to host the data. - * This data structures will only be filled with a call to \ref libvibeModel_Sequential_AllocInit_8u_C1R. - * - * \result A pointer to a newly allocated \ref vibeModel_Sequential_t - * structure, or <tt>NULL</tt> in the case of an error. - */ -vibeModel_Sequential_t *libvibeModel_Sequential_New(); - -/** - * ViBe uses several parameters. - * You can print and change some of them if you want. However, default - * value should meet your needs for most videos. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @return - */ -uint32_t libvibeModel_Sequential_PrintParameters(const vibeModel_Sequential_t *model); - -/** - * Setter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param numberOfSamples - * @return - */ -int32_t libvibeModel_Sequential_SetNumberOfSamples( - vibeModel_Sequential_t *model, - const uint32_t numberOfSamples -); - -/** - * Setter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @return - */ -uint32_t libvibeModel_Sequential_GetNumberOfSamples(const vibeModel_Sequential_t *model); - -/** - * Setter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param matchingThreshold - * @return - */ -int32_t libvibeModel_Sequential_SetMatchingThreshold( - vibeModel_Sequential_t *model, - const uint32_t matchingThreshold -); - -/** - * Setter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @return - */ -uint32_t libvibeModel_Sequential_GetMatchingThreshold(const vibeModel_Sequential_t *model); - -/** - * Setter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param matchingNumber - * @return - */ -int32_t libvibeModel_Sequential_SetMatchingNumber( - vibeModel_Sequential_t *model, - const uint32_t matchingNumber -); - -/** - * Setter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param updateFactor New value for the update factor. Please note that the update factor is to be understood as a probability of updating. More specifically, an update factor of 16 means that 1 out of every 16 background pixels is updated. Likewise, an update factor of 1 means that every background pixel is updated. - * @return - */ -int32_t libvibeModel_Sequential_SetUpdateFactor( - vibeModel_Sequential_t *model, - const uint32_t updateFactor -); - -/** - * Getter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @return - */ -uint32_t libvibeModel_Sequential_GetMatchingNumber(const vibeModel_Sequential_t *model); - - -/** - * Getter. - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @return - */ -uint32_t libvibeModel_Sequential_GetUpdateFactor(const vibeModel_Sequential_t *model); - -/** - * \brief Frees all the memory used by the <tt>model</tt> and deallocates the structure. - * - * This function frees all the memory allocated by \ref libvibeModel_SequentialNew and - * \ref libvibeModel_Sequential_AllocInit_8u_C1R or \ref libvibeModel_Sequential_AllocInit_8u_C3R. - * @param model The data structure with ViBe's background subtraction model and parameters. - * @return - */ -int32_t libvibeModel_Sequential_Free(vibeModel_Sequential_t *model); - -/** - * The two following functions allocate the required memory according to the - * model parameters and the dimensions of the input images. - * You must use the "C1R" function for grayscale images and the "C3R" for color - * images. - * These 2 functions also initialize the background model using the content - * of *image_data which is the pixel buffer of the first image of your stream. - */ - // ------------------------- Single channel images ---------------------------- - /** - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param image_data - * @param width - * @param height - * @return - */ -int32_t libvibeModel_Sequential_AllocInit_8u_C1R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - const uint32_t width, - const uint32_t height -); - -/* These 2 functions perform 2 operations: - * - they classify the pixels *image_data using the provided model and store - * the results in *segmentation_map. - * - they update *model according to these results and the content of - * *image_data. - * You must use the "C1R" function for grayscale images and the "C3R" for color - * images. - */ - /** - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param image_data - * @param segmentation_map - * @return - */ -int32_t libvibeModel_Sequential_Segmentation_8u_C1R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *segmentation_map -); - -/** - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param image_data - * @param updating_mask - * @return - */ -int32_t libvibeModel_Sequential_Update_8u_C1R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *updating_mask -); - -// ------------------------- Three channel images ----------------------------- -/** - * The pixel values of color images are arranged in the following order - * RGBRGBRGB... (or HSVHSVHSVHSVHSVHSV...) - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param image_data - * @param width - * @param height - * @return - */ -int32_t libvibeModel_Sequential_AllocInit_8u_C3R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - const uint32_t width, - const uint32_t height -); - -/* These 2 functions perform 2 operations: - * - they classify the pixels *image_data using the provided model and store - * the results in *segmentation_map. - * - they update *model according to these results and the content of - * *image_data. - * You must use the "C1R" function for grayscale images and the "C3R" for color - * images. - */ - /** - * The pixel values of color images are arranged in the following order - * RGBRGBRGB... (or HSVHSVHSVHSVHSVHSV...) - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param image_data - * @param segmentation_map - * @return - */ -int32_t libvibeModel_Sequential_Segmentation_8u_C3R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *segmentation_map -); - -/** - * The pixel values of color images are arranged in the following order - * RGBRGBRGB... (or HSVHSVHSVHSVHSVHSV...) - * - * @param model The data structure with ViBe's background subtraction model and parameters. - * @param image_data - * @param updating_mask - * @return - */ -int32_t libvibeModel_Sequential_Update_8u_C3R( - vibeModel_Sequential_t *model, - const uint8_t *image_data, - uint8_t *updating_mask -); +namespace bgslibrary +{ + namespace algorithms + { + namespace vibe + { + const int COLOR_BACKGROUND = 0; // Default label for background pixels + const int COLOR_FOREGROUND = 255; // Default label for foreground pixels. Note that some authors chose any value different from 0 instead + const int NUMBER_OF_HISTORY_IMAGES = 2; + + /** + * \typedef struct vibeModel_Sequential_t + * \brief Data structure for the background subtraction model. + * + * This data structure contains the background model as well as some paramaters value. + * The code is designed to hide all the implementation details to the user to ease its use. + */ + typedef struct vibeModel_Sequential vibeModel_Sequential_t; + + /** + * Allocation of a new data structure where the background model will be stored. + * Please note that this function only creates the structure to host the data. + * This data structures will only be filled with a call to \ref libvibeModel_Sequential_AllocInit_8u_C1R. + * + * \result A pointer to a newly allocated \ref vibeModel_Sequential_t + * structure, or <tt>NULL</tt> in the case of an error. + */ + vibeModel_Sequential_t *libvibeModel_Sequential_New(); + + /** + * ViBe uses several parameters. + * You can print and change some of them if you want. However, default + * value should meet your needs for most videos. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @return + */ + uint32_t libvibeModel_Sequential_PrintParameters(const vibeModel_Sequential_t *model); + + /** + * Setter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param numberOfSamples + * @return + */ + int32_t libvibeModel_Sequential_SetNumberOfSamples( + vibeModel_Sequential_t *model, + const uint32_t numberOfSamples + ); + + /** + * Setter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @return + */ + uint32_t libvibeModel_Sequential_GetNumberOfSamples(const vibeModel_Sequential_t *model); + + /** + * Setter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param matchingThreshold + * @return + */ + int32_t libvibeModel_Sequential_SetMatchingThreshold( + vibeModel_Sequential_t *model, + const uint32_t matchingThreshold + ); + + /** + * Setter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @return + */ + uint32_t libvibeModel_Sequential_GetMatchingThreshold(const vibeModel_Sequential_t *model); + + /** + * Setter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param matchingNumber + * @return + */ + int32_t libvibeModel_Sequential_SetMatchingNumber( + vibeModel_Sequential_t *model, + const uint32_t matchingNumber + ); + + /** + * Setter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param updateFactor New value for the update factor. Please note that the update factor is to be understood as a probability of updating. More specifically, an update factor of 16 means that 1 out of every 16 background pixels is updated. Likewise, an update factor of 1 means that every background pixel is updated. + * @return + */ + int32_t libvibeModel_Sequential_SetUpdateFactor( + vibeModel_Sequential_t *model, + const uint32_t updateFactor + ); + + /** + * Getter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @return + */ + uint32_t libvibeModel_Sequential_GetMatchingNumber(const vibeModel_Sequential_t *model); + + + /** + * Getter. + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @return + */ + uint32_t libvibeModel_Sequential_GetUpdateFactor(const vibeModel_Sequential_t *model); + + /** + * \brief Frees all the memory used by the <tt>model</tt> and deallocates the structure. + * + * This function frees all the memory allocated by \ref libvibeModel_SequentialNew and + * \ref libvibeModel_Sequential_AllocInit_8u_C1R or \ref libvibeModel_Sequential_AllocInit_8u_C3R. + * @param model The data structure with ViBe's background subtraction model and parameters. + * @return + */ + int32_t libvibeModel_Sequential_Free(vibeModel_Sequential_t *model); + + /** + * The two following functions allocate the required memory according to the + * model parameters and the dimensions of the input images. + * You must use the "C1R" function for grayscale images and the "C3R" for color + * images. + * These 2 functions also initialize the background model using the content + * of *image_data which is the pixel buffer of the first image of your stream. + */ + // ------------------------- Single channel images ---------------------------- + /** + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param image_data + * @param width + * @param height + * @return + */ + int32_t libvibeModel_Sequential_AllocInit_8u_C1R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + const uint32_t width, + const uint32_t height + ); + + /* These 2 functions perform 2 operations: + * - they classify the pixels *image_data using the provided model and store + * the results in *segmentation_map. + * - they update *model according to these results and the content of + * *image_data. + * You must use the "C1R" function for grayscale images and the "C3R" for color + * images. + */ + /** + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param image_data + * @param segmentation_map + * @return + */ + int32_t libvibeModel_Sequential_Segmentation_8u_C1R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *segmentation_map + ); + + /** + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param image_data + * @param updating_mask + * @return + */ + int32_t libvibeModel_Sequential_Update_8u_C1R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *updating_mask + ); + + // ------------------------- Three channel images ----------------------------- + /** + * The pixel values of color images are arranged in the following order + * RGBRGBRGB... (or HSVHSVHSVHSVHSVHSV...) + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param image_data + * @param width + * @param height + * @return + */ + int32_t libvibeModel_Sequential_AllocInit_8u_C3R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + const uint32_t width, + const uint32_t height + ); + + /* These 2 functions perform 2 operations: + * - they classify the pixels *image_data using the provided model and store + * the results in *segmentation_map. + * - they update *model according to these results and the content of + * *image_data. + * You must use the "C1R" function for grayscale images and the "C3R" for color + * images. + */ + /** + * The pixel values of color images are arranged in the following order + * RGBRGBRGB... (or HSVHSVHSVHSVHSVHSV...) + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param image_data + * @param segmentation_map + * @return + */ + int32_t libvibeModel_Sequential_Segmentation_8u_C3R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *segmentation_map + ); + + /** + * The pixel values of color images are arranged in the following order + * RGBRGBRGB... (or HSVHSVHSVHSVHSVHSV...) + * + * @param model The data structure with ViBe's background subtraction model and parameters. + * @param image_data + * @param updating_mask + * @return + */ + int32_t libvibeModel_Sequential_Update_8u_C3R( + vibeModel_Sequential_t *model, + const uint8_t *image_data, + uint8_t *updating_mask + ); + } + } +} diff --git a/src/algorithms/VuMeter.h b/src/algorithms/VuMeter.h index d10591400ecf1ff04929deb70633c9d4c2ae6908..50f62382940346b47852384654c2519b906e369a 100644 --- a/src/algorithms/VuMeter.h +++ b/src/algorithms/VuMeter.h @@ -13,13 +13,11 @@ namespace bgslibrary class VuMeter : public IBGS { private: - TBackgroundVuMeter bgs; - + vumeter::TBackgroundVuMeter bgs; IplImage *frame; IplImage *gray; IplImage *background; IplImage *mask; - bool enableFilter; int binSize; double alpha; diff --git a/src/algorithms/VuMeter/TBackground.cpp b/src/algorithms/VuMeter/TBackground.cpp index e961ee8912dd002d58ac3d25b81812c9b6b1bee9..19f26f8ee7b9c8f10cfa247056f46778ca19fa47 100644 --- a/src/algorithms/VuMeter/TBackground.cpp +++ b/src/algorithms/VuMeter/TBackground.cpp @@ -1,46 +1,37 @@ #include "TBackground.h" -TBackground::TBackground(void) -{ +using namespace bgslibrary::algorithms::vumeter; + +TBackground::TBackground(){ std::cout << "TBackground()" << std::endl; } -TBackground::~TBackground(void) -{ +TBackground::~TBackground(){ Clear(); std::cout << "~TBackground()" << std::endl; } -void TBackground::Clear(void) -{ -} +void TBackground::Clear(){} -void TBackground::Reset(void) -{ -} +void TBackground::Reset(){} -int TBackground::GetParameterCount(void) -{ +int TBackground::GetParameterCount(void){ return 0; } -std::string TBackground::GetParameterName(int nInd) -{ +std::string TBackground::GetParameterName(int nInd){ return ""; } -std::string TBackground::GetParameterValue(int nInd) -{ +std::string TBackground::GetParameterValue(int nInd){ return ""; } -int TBackground::SetParameterValue(int nInd, std::string csNew) -{ +int TBackground::SetParameterValue(int nInd, std::string csNew){ return 0; } -int TBackground::Init(IplImage * pSource) -{ +int TBackground::Init(IplImage * pSource){ return 0; } @@ -67,8 +58,7 @@ bool TBackground::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage * return bResult; } -int TBackground::UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask) -{ +int TBackground::UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask){ return 0; } diff --git a/src/algorithms/VuMeter/TBackground.h b/src/algorithms/VuMeter/TBackground.h index 8f66fe2506e72f5db94cc41f7a959928c7b01599..612060da9ca22ab744b1f654261d72271fd6e518 100644 --- a/src/algorithms/VuMeter/TBackground.h +++ b/src/algorithms/VuMeter/TBackground.h @@ -6,25 +6,34 @@ #include <opencv2/core/core_c.h> #include <opencv2/imgproc/imgproc_c.h> -class TBackground +namespace bgslibrary { -public: - TBackground(void); - virtual ~TBackground(void); + namespace algorithms + { + namespace vumeter + { + class TBackground + { + public: + TBackground(); + virtual ~TBackground(); - virtual void Clear(void); - virtual void Reset(void); + virtual void Clear(); + virtual void Reset(); - virtual int UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); - virtual int UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd); - virtual IplImage *CreateTestImg(); + virtual int UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); + virtual int UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd); + virtual IplImage *CreateTestImg(); - virtual int GetParameterCount(void); - virtual std::string GetParameterName(int nInd); - virtual std::string GetParameterValue(int nInd); - virtual int SetParameterValue(int nInd, std::string csNew); + virtual int GetParameterCount(); + virtual std::string GetParameterName(int nInd); + virtual std::string GetParameterValue(int nInd); + virtual int SetParameterValue(int nInd, std::string csNew); -protected: - virtual int Init(IplImage * pSource); - virtual bool isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); -}; + protected: + virtual int Init(IplImage * pSource); + virtual bool isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); + }; + } + } +} diff --git a/src/algorithms/VuMeter/TBackgroundVuMeter.cpp b/src/algorithms/VuMeter/TBackgroundVuMeter.cpp index b0a0d4c5e01e375b79c38d4511cb9a6028ec5c70..5e5f4db349e399e352882c4b63ef7a552f80d6bc 100644 --- a/src/algorithms/VuMeter/TBackgroundVuMeter.cpp +++ b/src/algorithms/VuMeter/TBackgroundVuMeter.cpp @@ -1,349 +1,360 @@ #include "TBackgroundVuMeter.h" -#define PROCESS_PAR_COUNT 3 - -TBackgroundVuMeter::TBackgroundVuMeter(void) - : m_pHist(NULL) - , m_nBinCount(0) - , m_nBinSize(8) - , m_nCount(0) - , m_fAlpha(0.995) - , m_fThreshold(0.03) -{ - std::cout << "TBackgroundVuMeter()" << std::endl; -} +//using namespace bgslibrary::algorithms::vumeter; -TBackgroundVuMeter::~TBackgroundVuMeter(void) +namespace bgslibrary { - Clear(); - std::cout << "~TBackgroundVuMeter()" << std::endl; -} - -void TBackgroundVuMeter::Clear(void) -{ - TBackground::Clear(); - - if (m_pHist != NULL) + namespace algorithms { - for (int i = 0; i < m_nBinCount; ++i) + namespace vumeter { - if (m_pHist[i] != NULL) - cvReleaseImage(&m_pHist[i]); - } - - delete[] m_pHist; - m_pHist = NULL; - m_nBinCount = 0; - } - - m_nCount = 0; -} - -void TBackgroundVuMeter::Reset(void) -{ - float fVal = 0.0; - - TBackground::Reset(); - - if (m_pHist != NULL) - { - // fVal = (m_nBinCount != 0) ? (float)(1.0 / (double)m_nBinCount) : (float)0.0; - fVal = 0.0; - - for (int i = 0; i < m_nBinCount; ++i) - { - if (m_pHist[i] != NULL) + const int PROCESS_PAR_COUNT = 3; + + TBackgroundVuMeter::TBackgroundVuMeter(void) + : m_pHist(NULL) + , m_nBinCount(0) + , m_nBinSize(8) + , m_nCount(0) + , m_fAlpha(0.995) + , m_fThreshold(0.03) { - cvSetZero(m_pHist[i]); - cvAddS(m_pHist[i], cvScalar(fVal), m_pHist[i]); + std::cout << "TBackgroundVuMeter()" << std::endl; } - } - } - - m_nCount = 0; -} - -int TBackgroundVuMeter::GetParameterCount(void) -{ - return TBackground::GetParameterCount() + PROCESS_PAR_COUNT; -} - -std::string TBackgroundVuMeter::GetParameterName(int nInd) -{ - std::string csResult; - int nNb; - nNb = TBackground::GetParameterCount(); + TBackgroundVuMeter::~TBackgroundVuMeter(void) + { + Clear(); + std::cout << "~TBackgroundVuMeter()" << std::endl; + } - if (nInd >= nNb) - { - nInd -= nNb; + void TBackgroundVuMeter::Clear(void) + { + TBackground::Clear(); + + if (m_pHist != NULL) + { + for (int i = 0; i < m_nBinCount; ++i) + { + if (m_pHist[i] != NULL) + cvReleaseImage(&m_pHist[i]); + } + + delete[] m_pHist; + m_pHist = NULL; + m_nBinCount = 0; + } + + m_nCount = 0; + } - switch (nInd) - { - case 0: csResult = "Bin size"; break; - case 1: csResult = "Alpha"; break; - case 2: csResult = "Threshold"; break; - } - } - else - csResult = TBackground::GetParameterName(nInd); + void TBackgroundVuMeter::Reset(void) + { + float fVal = 0.0; + + TBackground::Reset(); + + if (m_pHist != NULL) + { + // fVal = (m_nBinCount != 0) ? (float)(1.0 / (double)m_nBinCount) : (float)0.0; + fVal = 0.0; + + for (int i = 0; i < m_nBinCount; ++i) + { + if (m_pHist[i] != NULL) + { + cvSetZero(m_pHist[i]); + cvAddS(m_pHist[i], cvScalar(fVal), m_pHist[i]); + } + } + } + + m_nCount = 0; + } - return csResult; -} + int TBackgroundVuMeter::GetParameterCount(void) + { + return TBackground::GetParameterCount() + PROCESS_PAR_COUNT; + } -std::string TBackgroundVuMeter::GetParameterValue(int nInd) -{ - std::string csResult; - int nNb; + std::string TBackgroundVuMeter::GetParameterName(int nInd) + { + std::string csResult; + int nNb; + + nNb = TBackground::GetParameterCount(); + + if (nInd >= nNb) + { + nInd -= nNb; + + switch (nInd) + { + case 0: csResult = "Bin size"; break; + case 1: csResult = "Alpha"; break; + case 2: csResult = "Threshold"; break; + } + } + else + csResult = TBackground::GetParameterName(nInd); + + return csResult; + } - nNb = TBackground::GetParameterCount(); + std::string TBackgroundVuMeter::GetParameterValue(int nInd) + { + std::string csResult; + int nNb; - if (nInd >= nNb) - { - nInd -= nNb; + nNb = TBackground::GetParameterCount(); - char buff[100]; + if (nInd >= nNb) + { + nInd -= nNb; - switch (nInd) - { - case 0: sprintf(buff, "%d", m_nBinSize); break; - case 1: sprintf(buff, "%.3f", m_fAlpha); break; - case 2: sprintf(buff, "%.2f", m_fThreshold); break; - } + char buff[100]; - csResult = buff; - } - else - csResult = TBackground::GetParameterValue(nInd); + switch (nInd) + { + case 0: sprintf_s(buff, "%d", m_nBinSize); break; + case 1: sprintf_s(buff, "%.3f", m_fAlpha); break; + case 2: sprintf_s(buff, "%.2f", m_fThreshold); break; + } - return csResult; -} + csResult = buff; + } + else + csResult = TBackground::GetParameterValue(nInd); -int TBackgroundVuMeter::SetParameterValue(int nInd, std::string csNew) -{ - int nErr = 0; + return csResult; + } - int nNb; + int TBackgroundVuMeter::SetParameterValue(int nInd, std::string csNew) + { + int nErr = 0; - nNb = TBackground::GetParameterCount(); + int nNb; - if (nInd >= nNb) - { - nInd -= nNb; + nNb = TBackground::GetParameterCount(); - switch (nInd) - { - case 0: SetBinSize(atoi(csNew.c_str())); break; - case 1: SetAlpha(atof(csNew.c_str())); break; - case 2: SetThreshold(atof(csNew.c_str())); break; - default: nErr = 1; - } - } - else - nErr = TBackground::SetParameterValue(nInd, csNew); + if (nInd >= nNb) + { + nInd -= nNb; - return nErr; -} + switch (nInd) + { + case 0: SetBinSize(atoi(csNew.c_str())); break; + case 1: SetAlpha(atof(csNew.c_str())); break; + case 2: SetThreshold(atof(csNew.c_str())); break; + default: nErr = 1; + } + } + else + nErr = TBackground::SetParameterValue(nInd, csNew); -int TBackgroundVuMeter::Init(IplImage * pSource) -{ - int nErr = 0; - int nbl, nbc; + return nErr; + } - Clear(); + int TBackgroundVuMeter::Init(IplImage * pSource) + { + int nErr = 0; + int nbl, nbc; + + Clear(); + + nErr = TBackground::Init(pSource); + + if (pSource == NULL) + nErr = 1; + + // calcul le nb de bin + if (!nErr) + { + nbl = pSource->height; + nbc = pSource->width; + m_nBinCount = (m_nBinSize != 0) ? 256 / m_nBinSize : 0; + + if (m_nBinCount <= 0 || m_nBinCount > 256) + nErr = 1; + } + + // creation du tableau de pointeur + if (!nErr) + { + m_pHist = new IplImage *[m_nBinCount]; + + if (m_pHist == NULL) + nErr = 1; + } + + // creation des images + if (!nErr) + { + for (int i = 0; i < m_nBinCount; ++i) + { + m_pHist[i] = cvCreateImage(cvSize(nbc, nbl), IPL_DEPTH_32F, 1); + + if (m_pHist[i] == NULL) + nErr = 1; + } + } + + if (!nErr) + Reset(); + else + Clear(); + + return nErr; + } - nErr = TBackground::Init(pSource); + bool TBackgroundVuMeter::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask) + { + bool bResult = true; + int i; - if (pSource == NULL) - nErr = 1; + bResult = TBackground::isInitOk(pSource, pBackground, pMotionMask); - // calcul le nb de bin - if (!nErr) - { - nbl = pSource->height; - nbc = pSource->width; - m_nBinCount = (m_nBinSize != 0) ? 256 / m_nBinSize : 0; + if (pSource == NULL) + bResult = false; - if (m_nBinCount <= 0 || m_nBinCount > 256) - nErr = 1; - } + if (m_nBinSize == 0) + bResult = false; - // creation du tableau de pointeur - if (!nErr) - { - m_pHist = new IplImage *[m_nBinCount]; + if (bResult) + { + i = (m_nBinSize != 0) ? 256 / m_nBinSize : 0; - if (m_pHist == NULL) - nErr = 1; - } + if (i != m_nBinCount || m_pHist == NULL) + bResult = false; + } - // creation des images - if (!nErr) - { - for (int i = 0; i < m_nBinCount; ++i) - { - m_pHist[i] = cvCreateImage(cvSize(nbc, nbl), IPL_DEPTH_32F, 1); + if (bResult) + { + int nbl = pSource->height; + int nbc = pSource->width; - if (m_pHist[i] == NULL) - nErr = 1; - } - } + for (i = 0; i < m_nBinCount; ++i) + { + if (m_pHist[i] == NULL || m_pHist[i]->width != nbc || m_pHist[i]->height != nbl) + bResult = false; + } + } - if (!nErr) - Reset(); - else - Clear(); + return bResult; + } - return nErr; -} + int TBackgroundVuMeter::UpdateBackground(IplImage *pSource, IplImage *pBackground, IplImage *pMotionMask) + { + int nErr = 0; + unsigned char *ptrs, *ptrb, *ptrm; + float *ptr1, *ptr2; -bool TBackgroundVuMeter::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask) -{ - bool bResult = true; - int i; + if (!isInitOk(pSource, pBackground, pMotionMask)) + nErr = Init(pSource); - bResult = TBackground::isInitOk(pSource, pBackground, pMotionMask); + if (!nErr) + { + m_nCount++; + int nbc = pSource->width; + int nbl = pSource->height; + unsigned char v = m_nBinSize; - if (pSource == NULL) - bResult = false; + // multiplie tout par alpha + for (int i = 0; i < m_nBinCount; ++i) + cvConvertScale(m_pHist[i], m_pHist[i], m_fAlpha, 0.0); - if (m_nBinSize == 0) - bResult = false; + for (int l = 0; l < nbl; ++l) + { + ptrs = (unsigned char *)(pSource->imageData + pSource->widthStep * l); + ptrm = (unsigned char *)(pMotionMask->imageData + pMotionMask->widthStep * l); + ptrb = (unsigned char *)(pBackground->imageData + pBackground->widthStep * l); - if (bResult) - { - i = (m_nBinSize != 0) ? 256 / m_nBinSize : 0; + for (int c = 0; c < nbc; ++c, ptrs++, ptrb++, ptrm++) + { + // recherche le bin à augmenter + int i = *ptrs / v; - if (i != m_nBinCount || m_pHist == NULL) - bResult = false; - } + if (i < 0 || i >= m_nBinCount) + i = 0; - if (bResult) - { - int nbl = pSource->height; - int nbc = pSource->width; + ptr1 = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * l); + ptr1 += c; - for (i = 0; i < m_nBinCount; ++i) - { - if (m_pHist[i] == NULL || m_pHist[i]->width != nbc || m_pHist[i]->height != nbl) - bResult = false; - } - } + *ptr1 += (float)(1.0 - m_fAlpha); + *ptrm = (*ptr1 < m_fThreshold) ? 255 : 0; - return bResult; -} + // recherche le bin du fond actuel + i = *ptrb / v; -int TBackgroundVuMeter::UpdateBackground(IplImage *pSource, IplImage *pBackground, IplImage *pMotionMask) -{ - int nErr = 0; - unsigned char *ptrs, *ptrb, *ptrm; - float *ptr1, *ptr2; + if (i < 0 || i >= m_nBinCount) + i = 0; - if (!isInitOk(pSource, pBackground, pMotionMask)) - nErr = Init(pSource); + ptr2 = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * l); + ptr2 += c; - if (!nErr) - { - m_nCount++; - int nbc = pSource->width; - int nbl = pSource->height; - unsigned char v = m_nBinSize; + if (*ptr2 < *ptr1) + *ptrb = *ptrs; + } + } - // multiplie tout par alpha - for (int i = 0; i < m_nBinCount; ++i) - cvConvertScale(m_pHist[i], m_pHist[i], m_fAlpha, 0.0); + if (m_nCount < 5) + cvSetZero(pMotionMask); + } - for (int l = 0; l < nbl; ++l) - { - ptrs = (unsigned char *)(pSource->imageData + pSource->widthStep * l); - ptrm = (unsigned char *)(pMotionMask->imageData + pMotionMask->widthStep * l); - ptrb = (unsigned char *)(pBackground->imageData + pBackground->widthStep * l); + return nErr; + } - for (int c = 0; c < nbc; ++c, ptrs++, ptrb++, ptrm++) + IplImage *TBackgroundVuMeter::CreateTestImg() { - // recherche le bin à augmenter - int i = *ptrs / v; - - if (i < 0 || i >= m_nBinCount) - i = 0; - - ptr1 = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * l); - ptr1 += c; + IplImage *pImage = NULL; - *ptr1 += (float)(1.0 - m_fAlpha); - *ptrm = (*ptr1 < m_fThreshold) ? 255 : 0; + if (m_nBinCount > 0) + pImage = cvCreateImage(cvSize(m_nBinCount, 100), IPL_DEPTH_8U, 3); - // recherche le bin du fond actuel - i = *ptrb / v; + if (pImage != NULL) + cvSetZero(pImage); - if (i < 0 || i >= m_nBinCount) - i = 0; - - ptr2 = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * l); - ptr2 += c; - - if (*ptr2 < *ptr1) - *ptrb = *ptrs; + return pImage; } - } - - if (m_nCount < 5) - cvSetZero(pMotionMask); - } - - return nErr; -} -IplImage *TBackgroundVuMeter::CreateTestImg() -{ - IplImage *pImage = NULL; + int TBackgroundVuMeter::UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd) + { + int nErr = 0; + float *ptrf; - if (m_nBinCount > 0) - pImage = cvCreateImage(cvSize(m_nBinCount, 100), IPL_DEPTH_8U, 3); + if (pTest == NULL || !isInitOk(pSource, pBackground, pSource)) + nErr = 1; - if (pImage != NULL) - cvSetZero(pImage); + if (!nErr) + { + int nbl = pTest->height; + int nbc = pTest->width; - return pImage; -} + if (nbl != 100 || nbc != m_nBinCount) + nErr = 1; -int TBackgroundVuMeter::UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd) -{ - int nErr = 0; - float *ptrf; + if (nX < 0 || nX >= pSource->width || nY < 0 || nY >= pSource->height) + nErr = 1; + } - if (pTest == NULL || !isInitOk(pSource, pBackground, pSource)) - nErr = 1; + if (!nErr) + { + cvSetZero(pTest); - if (!nErr) - { - int nbl = pTest->height; - int nbc = pTest->width; + for (int i = 0; i < m_nBinCount; ++i) + { + ptrf = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * nY); + ptrf += nX; - if (nbl != 100 || nbc != m_nBinCount) - nErr = 1; + if (*ptrf >= 0 || *ptrf <= 1.0) { + cvLine(pTest, cvPoint(i, 100), cvPoint(i, (int)(100.0 * (1.0 - *ptrf))), cvScalar(0, 255, 0)); + } + } - if (nX < 0 || nX >= pSource->width || nY < 0 || nY >= pSource->height) - nErr = 1; - } + cvLine(pTest, cvPoint(0, (int)(100.0 * (1.0 - m_fThreshold))), cvPoint(m_nBinCount, (int)(100.0 * (1.0 - m_fThreshold))), cvScalar(0, 128, 0)); + } - if (!nErr) - { - cvSetZero(pTest); - - for (int i = 0; i < m_nBinCount; ++i) - { - ptrf = (float *)(m_pHist[i]->imageData + m_pHist[i]->widthStep * nY); - ptrf += nX; - - if (*ptrf >= 0 || *ptrf <= 1.0) { - cvLine(pTest, cvPoint(i, 100), cvPoint(i, (int)(100.0 * (1.0 - *ptrf))), cvScalar(0, 255, 0)); + return nErr; } } - - cvLine(pTest, cvPoint(0, (int)(100.0 * (1.0 - m_fThreshold))), cvPoint(m_nBinCount, (int)(100.0 * (1.0 - m_fThreshold))), cvScalar(0, 128, 0)); } - - return nErr; } diff --git a/src/algorithms/VuMeter/TBackgroundVuMeter.h b/src/algorithms/VuMeter/TBackgroundVuMeter.h index 1d63aecab2d0867bafe492fdb27bcf8481dbc435..89cfc22351d9659d7c6697cd97aca4d6a5174eaa 100644 --- a/src/algorithms/VuMeter/TBackgroundVuMeter.h +++ b/src/algorithms/VuMeter/TBackgroundVuMeter.h @@ -2,43 +2,52 @@ #include "TBackground.h" -class TBackgroundVuMeter : public TBackground +namespace bgslibrary { -public: - TBackgroundVuMeter(void); - virtual ~TBackgroundVuMeter(void); - - virtual void Clear(void); - virtual void Reset(void); - - virtual int UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); - - virtual IplImage *CreateTestImg(); - virtual int UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd); - - virtual int GetParameterCount(void); - virtual std::string GetParameterName(int nInd); - virtual std::string GetParameterValue(int nInd); - virtual int SetParameterValue(int nInd, std::string csNew); - - inline void SetBinSize(int nNew) { m_nBinSize = (nNew > 0 && nNew < 255) ? nNew : 8; } - inline double GetBinSize() { return m_nBinSize; } - - inline void SetAlpha(double fNew) { m_fAlpha = (fNew > 0.0 && fNew < 1.0) ? fNew : 0.995; } - inline double GetAlpha() { return m_fAlpha; } - - inline void SetThreshold(double fNew) { m_fThreshold = (fNew > 0.0 && fNew < 1.0) ? fNew : 0.03; } - inline double GetThreshold() { return m_fThreshold; } - -protected: - IplImage **m_pHist; - - int m_nBinCount; - int m_nBinSize; - int m_nCount; - double m_fAlpha; - double m_fThreshold; - - virtual int Init(IplImage * pSource); - virtual bool isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); -}; + namespace algorithms + { + namespace vumeter + { + class TBackgroundVuMeter : public TBackground + { + public: + TBackgroundVuMeter(void); + virtual ~TBackgroundVuMeter(void); + + virtual void Clear(void); + virtual void Reset(void); + + virtual int UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); + + virtual IplImage *CreateTestImg(); + virtual int UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd); + + virtual int GetParameterCount(void); + virtual std::string GetParameterName(int nInd); + virtual std::string GetParameterValue(int nInd); + virtual int SetParameterValue(int nInd, std::string csNew); + + inline void SetBinSize(int nNew) { m_nBinSize = (nNew > 0 && nNew < 255) ? nNew : 8; } + inline double GetBinSize() { return m_nBinSize; } + + inline void SetAlpha(double fNew) { m_fAlpha = (fNew > 0.0 && fNew < 1.0) ? fNew : 0.995; } + inline double GetAlpha() { return m_fAlpha; } + + inline void SetThreshold(double fNew) { m_fThreshold = (fNew > 0.0 && fNew < 1.0) ? fNew : 0.03; } + inline double GetThreshold() { return m_fThreshold; } + + protected: + IplImage **m_pHist; + + int m_nBinCount; + int m_nBinSize; + int m_nCount; + double m_fAlpha; + double m_fThreshold; + + virtual int Init(IplImage * pSource); + virtual bool isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask); + }; + } + } +} diff --git a/src/algorithms/dp/AdaptiveMedianBGS.cpp b/src/algorithms/dp/AdaptiveMedianBGS.cpp index 231a6b59ceb2669dc0cdf275aed6bd533a2ab184..2609fb0906cc11900502b8fa6afb650b64db5471 100644 --- a/src/algorithms/dp/AdaptiveMedianBGS.cpp +++ b/src/algorithms/dp/AdaptiveMedianBGS.cpp @@ -2,12 +2,11 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; void AdaptiveMedianBGS::Initalize(const BgsParams& param) { m_params = (AdaptiveMedianParams&)param; - m_median = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); cvSet(m_median.Ptr(), CV_RGB(BACKGROUND, BACKGROUND, BACKGROUND)); } diff --git a/src/algorithms/dp/AdaptiveMedianBGS.h b/src/algorithms/dp/AdaptiveMedianBGS.h index 55a68190d7295eceb0297b85e16589b388f76723..0a9d1c7f2fff306ac1fb6534ed70de9d27506120 100644 --- a/src/algorithms/dp/AdaptiveMedianBGS.h +++ b/src/algorithms/dp/AdaptiveMedianBGS.h @@ -9,10 +9,12 @@ #include "Bgs.h" -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { // --- Parameters used by the Adaptive Median BGS algorithm --- class AdaptiveMedianParams : public BgsParams { @@ -55,6 +57,7 @@ namespace Algorithms RgbImage m_median; }; + } } } diff --git a/src/algorithms/dp/Bgs.h b/src/algorithms/dp/Bgs.h index b083d5f318c2ef8565d576e061bc9c9b20bfc6c5..7f1c1ea93be7b7e361f54cecab64407df5ce5ff3 100644 --- a/src/algorithms/dp/Bgs.h +++ b/src/algorithms/dp/Bgs.h @@ -6,10 +6,12 @@ #include "Image.h" #include "BgsParams.h" -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { class Bgs { public: @@ -37,6 +39,7 @@ namespace Algorithms // Return the current background model. virtual RgbImage *Background() = 0; }; + } } } diff --git a/src/algorithms/dp/BgsParams.h b/src/algorithms/dp/BgsParams.h index 79539b7f19285231815c8ab6669811c5e340c0a3..5f1ed6b0960fbfd82405ef7a540a52d789952d63 100644 --- a/src/algorithms/dp/BgsParams.h +++ b/src/algorithms/dp/BgsParams.h @@ -1,9 +1,11 @@ #pragma once -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { class BgsParams { public: @@ -25,5 +27,6 @@ namespace Algorithms unsigned int m_height; unsigned int m_size; }; + } } } diff --git a/src/algorithms/dp/Eigenbackground.cpp b/src/algorithms/dp/Eigenbackground.cpp index c207fbb1199c8548c18d294cbe7f7417524f7fda..e8c3aea034baed42b7a65a8402b35c7f08335fb6 100644 --- a/src/algorithms/dp/Eigenbackground.cpp +++ b/src/algorithms/dp/Eigenbackground.cpp @@ -2,7 +2,7 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; Eigenbackground::Eigenbackground() { diff --git a/src/algorithms/dp/Eigenbackground.h b/src/algorithms/dp/Eigenbackground.h index 4808493f652f7519d52c9c93362414abf960a4cd..9f44e42846b3d33aa6d0b6e505bae27120d7a6e9 100644 --- a/src/algorithms/dp/Eigenbackground.h +++ b/src/algorithms/dp/Eigenbackground.h @@ -5,10 +5,12 @@ #include "Bgs.h" -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { // --- Parameters used by the Mean BGS algorithm --- class EigenbackgroundParams : public BgsParams { @@ -57,6 +59,7 @@ namespace Algorithms RgbImage m_background; }; + } } } diff --git a/src/algorithms/dp/Error.cpp b/src/algorithms/dp/Error.cpp index 0ccb18091022eb582b0b795c7810b0e8830320eb..f2958cd91ae1863c767106f2ff2c067c6c445578 100644 --- a/src/algorithms/dp/Error.cpp +++ b/src/algorithms/dp/Error.cpp @@ -3,29 +3,37 @@ #include "Error.h" -using namespace std; +//using namespace bgslibrary::algorithms::dp; -ofstream traceFile; - -bool Error(const char* msg, const char* code, int data) -{ - cerr << code << ": " << msg << endl; - - return false; -} - -bool TraceInit(const char* filename) -{ - traceFile.open(filename); - return traceFile.is_open(); -} - -void Trace(const char* msg) -{ - traceFile << msg << endl; -} - -void TraceClose() +namespace bgslibrary { - traceFile.close(); + namespace algorithms + { + namespace dp + { + std::ofstream traceFile; + + bool Error(const char* msg, const char* code, int data) + { + std::cerr << code << ": " << msg << std::endl; + return false; + } + + bool TraceInit(const char* filename) + { + traceFile.open(filename); + return traceFile.is_open(); + } + + void Trace(const char* msg) + { + traceFile << msg << std::endl; + } + + void TraceClose() + { + traceFile.close(); + } + } + } } diff --git a/src/algorithms/dp/Error.h b/src/algorithms/dp/Error.h index a499e00534ebf6b992634f1d34ca11699494755b..3695f8e0410a74ddf519d2e4ee88cc0e6fd9d345 100644 --- a/src/algorithms/dp/Error.h +++ b/src/algorithms/dp/Error.h @@ -1,6 +1,15 @@ #pragma once -bool Error(const char* msg, const char* code, int data); -bool TraceInit(const char* filename); -void Trace(const char* msg); -void TraceClose(); +namespace bgslibrary +{ + namespace algorithms + { + namespace dp + { + bool Error(const char* msg, const char* code, int data); + bool TraceInit(const char* filename); + void Trace(const char* msg); + void TraceClose(); + } + } +} diff --git a/src/algorithms/dp/GrimsonGMM.cpp b/src/algorithms/dp/GrimsonGMM.cpp index b60e0f63d01234f16db8265de9fbc4537a1acf17..cc6111fcbc41779f9c5990aac0c775c79e70ddac 100644 --- a/src/algorithms/dp/GrimsonGMM.cpp +++ b/src/algorithms/dp/GrimsonGMM.cpp @@ -2,288 +2,297 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +//using namespace bgslibrary::algorithms::dp; -int compareGMM(const void* _gmm1, const void* _gmm2) +namespace bgslibrary { - GMM gmm1 = *(GMM*)_gmm1; - GMM gmm2 = *(GMM*)_gmm2; - - if (gmm1.significants < gmm2.significants) - return 1; - else if (gmm1.significants == gmm2.significants) - return 0; - else - return -1; -} - -GrimsonGMM::GrimsonGMM() -{ - m_modes = NULL; -} - -GrimsonGMM::~GrimsonGMM() -{ - delete[] m_modes; -} - -void GrimsonGMM::Initalize(const BgsParams& param) -{ - m_params = (GrimsonParams&)param; - - // Tbf - the threshold - m_bg_threshold = 0.75f; // 1-cf from the paper - - // Tgenerate - the threshold - m_variance = 36.0f; // sigma for the new mode - - // GMM for each pixel - m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; - - // used modes per pixel - m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); - - m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); -} - -RgbImage* GrimsonGMM::Background() -{ - return &m_background; -} - -void GrimsonGMM::InitModel(const RgbImage& data) -{ - m_modes_per_pixel.Clear(); - - for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) + namespace algorithms { - m_modes[i].weight = 0; - m_modes[i].variance = 0; - m_modes[i].muR = 0; - m_modes[i].muG = 0; - m_modes[i].muB = 0; - m_modes[i].significants = 0; - } -} - -void GrimsonGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) -{ - // it doesn't make sense to have conditional updates in the GMM framework -} - -void GrimsonGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, - unsigned char& low_threshold, unsigned char& high_threshold) -{ - // calculate distances to the modes (+ sort???) - // here we need to go in descending order!!! - long pos; - bool bFitsPDF = false; - bool bBackgroundLow = false; - bool bBackgroundHigh = false; + namespace dp + { + int compareGMM(const void* _gmm1, const void* _gmm2) + { + GMM gmm1 = *(GMM*)_gmm1; + GMM gmm2 = *(GMM*)_gmm2; + + if (gmm1.significants < gmm2.significants) + return 1; + else if (gmm1.significants == gmm2.significants) + return 0; + else + return -1; + } - float fOneMinAlpha = 1 - m_params.Alpha(); + GrimsonGMM::GrimsonGMM() + { + m_modes = NULL; + } - float totalWeight = 0.0f; + GrimsonGMM::~GrimsonGMM() + { + delete[] m_modes; + } - // calculate number of Gaussians to include in the background model - int backgroundGaussians = 0; - double sum = 0.0; - for (int i = 0; i < numModes; ++i) - { - if (sum < m_bg_threshold) - { - backgroundGaussians++; - sum += m_modes[posPixel + i].weight; - } - else - { - break; - } - } + void GrimsonGMM::Initalize(const BgsParams& param) + { + m_params = (GrimsonParams&)param; - // update all distributions and check for match with current pixel - for (int iModes = 0; iModes < numModes; iModes++) - { - pos = posPixel + iModes; - float weight = m_modes[pos].weight; + // Tbf - the threshold + m_bg_threshold = 0.75f; // 1-cf from the paper - // fit not found yet - if (!bFitsPDF) - { - //check if it belongs to some of the modes - //calculate distance - float var = m_modes[pos].variance; - float muR = m_modes[pos].muR; - float muG = m_modes[pos].muG; - float muB = m_modes[pos].muB; + // Tgenerate - the threshold + m_variance = 36.0f; // sigma for the new mode - float dR = muR - pixel(0); - float dG = muG - pixel(1); - float dB = muB - pixel(2); + // GMM for each pixel + m_modes = new GMM[m_params.Size()*m_params.MaxModes()]; - // calculate the squared distance - float dist = (dR*dR + dG*dG + dB*dB); + // used modes per pixel + m_modes_per_pixel = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1); - if (dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) - bBackgroundHigh = true; + m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3); + } - // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution - if (dist < m_params.LowThreshold()*var) + RgbImage* GrimsonGMM::Background() { - bFitsPDF = true; - - // check if this Gaussian is part of the background model - if (iModes < backgroundGaussians) - bBackgroundLow = true; - - //update distribution - float k = m_params.Alpha() / weight; - weight = fOneMinAlpha*weight + m_params.Alpha(); - m_modes[pos].weight = weight; - m_modes[pos].muR = muR - k*(dR); - m_modes[pos].muG = muG - k*(dG); - m_modes[pos].muB = muB - k*(dB); - - //limit the variance - float sigmanew = var + k*(dist - var); - m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + return &m_background; } - else + + void GrimsonGMM::InitModel(const RgbImage& data) { - weight = fOneMinAlpha*weight; - if (weight < 0.0) + m_modes_per_pixel.Clear(); + + for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i) { - weight = 0.0; - numModes--; + m_modes[i].weight = 0; + m_modes[i].variance = 0; + m_modes[i].muR = 0; + m_modes[i].muG = 0; + m_modes[i].muB = 0; + m_modes[i].significants = 0; } - - m_modes[pos].weight = weight; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); } - } - else - { - weight = fOneMinAlpha*weight; - if (weight < 0.0) + + void GrimsonGMM::Update(int frame_num, const RgbImage& data, const BwImage& update_mask) { - weight = 0.0; - numModes--; + // it doesn't make sense to have conditional updates in the GMM framework } - m_modes[pos].weight = weight; - m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); - } - totalWeight += weight; - } + void GrimsonGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char& numModes, + unsigned char& low_threshold, unsigned char& high_threshold) + { + // calculate distances to the modes (+ sort???) + // here we need to go in descending order!!! + long pos; + bool bFitsPDF = false; + bool bBackgroundLow = false; + bool bBackgroundHigh = false; - // renormalize weights so they add to one - double invTotalWeight = 1.0 / totalWeight; - for (int iLocal = 0; iLocal < numModes; iLocal++) - { - m_modes[posPixel + iLocal].weight *= (float)invTotalWeight; - m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight - / sqrt(m_modes[posPixel + iLocal].variance); - } + float fOneMinAlpha = 1 - m_params.Alpha(); - // Sort significance values so they are in desending order. - qsort(&m_modes[posPixel], numModes, sizeof(GMM), compareGMM); + float totalWeight = 0.0f; - // make new mode if needed and exit - if (!bFitsPDF) - { - if (numModes < m_params.MaxModes()) - { - numModes++; - } - else - { - // the weakest mode will be replaced - } - - pos = posPixel + numModes - 1; - - m_modes[pos].muR = pixel.ch[0]; - m_modes[pos].muG = pixel.ch[1]; - m_modes[pos].muB = pixel.ch[2]; - m_modes[pos].variance = m_variance; - m_modes[pos].significants = 0; // will be set below + // calculate number of Gaussians to include in the background model + int backgroundGaussians = 0; + double sum = 0.0; + for (int i = 0; i < numModes; ++i) + { + if (sum < m_bg_threshold) + { + backgroundGaussians++; + sum += m_modes[posPixel + i].weight; + } + else + { + break; + } + } - if (numModes == 1) - m_modes[pos].weight = 1; - else - m_modes[pos].weight = m_params.Alpha(); + // update all distributions and check for match with current pixel + for (int iModes = 0; iModes < numModes; iModes++) + { + pos = posPixel + iModes; + float weight = m_modes[pos].weight; + + // fit not found yet + if (!bFitsPDF) + { + //check if it belongs to some of the modes + //calculate distance + float var = m_modes[pos].variance; + float muR = m_modes[pos].muR; + float muG = m_modes[pos].muG; + float muB = m_modes[pos].muB; + + float dR = muR - pixel(0); + float dG = muG - pixel(1); + float dB = muB - pixel(2); + + // calculate the squared distance + float dist = (dR*dR + dG*dG + dB*dB); + + if (dist < m_params.HighThreshold()*var && iModes < backgroundGaussians) + bBackgroundHigh = true; + + // a match occurs when the pixel is within sqrt(fTg) standard deviations of the distribution + if (dist < m_params.LowThreshold()*var) + { + bFitsPDF = true; + + // check if this Gaussian is part of the background model + if (iModes < backgroundGaussians) + bBackgroundLow = true; + + //update distribution + float k = m_params.Alpha() / weight; + weight = fOneMinAlpha*weight + m_params.Alpha(); + m_modes[pos].weight = weight; + m_modes[pos].muR = muR - k*(dR); + m_modes[pos].muG = muG - k*(dG); + m_modes[pos].muB = muB - k*(dB); + + //limit the variance + float sigmanew = var + k*(dist - var); + m_modes[pos].variance = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight = 0.0; + numModes--; + } + + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + } + else + { + weight = fOneMinAlpha*weight; + if (weight < 0.0) + { + weight = 0.0; + numModes--; + } + m_modes[pos].weight = weight; + m_modes[pos].significants = m_modes[pos].weight / sqrt(m_modes[pos].variance); + } + + totalWeight += weight; + } - //renormalize weights - int iLocal; - float sum = 0.0; - for (iLocal = 0; iLocal < numModes; iLocal++) - { - sum += m_modes[posPixel + iLocal].weight; - } + // renormalize weights so they add to one + double invTotalWeight = 1.0 / totalWeight; + for (int iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invTotalWeight; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight + / sqrt(m_modes[posPixel + iLocal].variance); + } - double invSum = 1.0 / sum; - for (iLocal = 0; iLocal < numModes; iLocal++) - { - m_modes[posPixel + iLocal].weight *= (float)invSum; - m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight - / sqrt(m_modes[posPixel + iLocal].variance); + // Sort significance values so they are in desending order. + qsort(&m_modes[posPixel], numModes, sizeof(GMM), compareGMM); - } - } + // make new mode if needed and exit + if (!bFitsPDF) + { + if (numModes < m_params.MaxModes()) + { + numModes++; + } + else + { + // the weakest mode will be replaced + } + + pos = posPixel + numModes - 1; + + m_modes[pos].muR = pixel.ch[0]; + m_modes[pos].muG = pixel.ch[1]; + m_modes[pos].muB = pixel.ch[2]; + m_modes[pos].variance = m_variance; + m_modes[pos].significants = 0; // will be set below + + if (numModes == 1) + m_modes[pos].weight = 1; + else + m_modes[pos].weight = m_params.Alpha(); + + //renormalize weights + int iLocal; + float sum = 0.0; + for (iLocal = 0; iLocal < numModes; iLocal++) + { + sum += m_modes[posPixel + iLocal].weight; + } + + double invSum = 1.0 / sum; + for (iLocal = 0; iLocal < numModes; iLocal++) + { + m_modes[posPixel + iLocal].weight *= (float)invSum; + m_modes[posPixel + iLocal].significants = m_modes[posPixel + iLocal].weight + / sqrt(m_modes[posPixel + iLocal].variance); + + } + } - // Sort significance values so they are in desending order. - qsort(&(m_modes[posPixel]), numModes, sizeof(GMM), compareGMM); + // Sort significance values so they are in desending order. + qsort(&(m_modes[posPixel]), numModes, sizeof(GMM), compareGMM); - if (bBackgroundLow) - { - low_threshold = BACKGROUND; - } - else - { - low_threshold = FOREGROUND; - } + if (bBackgroundLow) + { + low_threshold = BACKGROUND; + } + else + { + low_threshold = FOREGROUND; + } - if (bBackgroundHigh) - { - high_threshold = BACKGROUND; - } - else - { - high_threshold = FOREGROUND; - } -} + if (bBackgroundHigh) + { + high_threshold = BACKGROUND; + } + else + { + high_threshold = FOREGROUND; + } + } -/////////////////////////////////////////////////////////////////////////////// -//Input: -// data - a pointer to the data of a RGB image of the same size -//Output: -// output - a pointer to the data of a gray value image of the same size -// (the memory should already be reserved) -// values: 255-foreground, 125-shadow, 0-background -/////////////////////////////////////////////////////////////////////////////// -void GrimsonGMM::Subtract(int frame_num, const RgbImage& data, - BwImage& low_threshold_mask, BwImage& high_threshold_mask) -{ - unsigned char low_threshold, high_threshold; - long posPixel; + /////////////////////////////////////////////////////////////////////////////// + //Input: + // data - a pointer to the data of a RGB image of the same size + //Output: + // output - a pointer to the data of a gray value image of the same size + // (the memory should already be reserved) + // values: 255-foreground, 125-shadow, 0-background + /////////////////////////////////////////////////////////////////////////////// + void GrimsonGMM::Subtract(int frame_num, const RgbImage& data, + BwImage& low_threshold_mask, BwImage& high_threshold_mask) + { + unsigned char low_threshold, high_threshold; + long posPixel; - // update each pixel of the image - for (unsigned int r = 0; r < m_params.Height(); ++r) - { - for (unsigned int c = 0; c < m_params.Width(); ++c) - { - // update model + background subtract - posPixel = (r*m_params.Width() + c)*m_params.MaxModes(); + // update each pixel of the image + for (unsigned int r = 0; r < m_params.Height(); ++r) + { + for (unsigned int c = 0; c < m_params.Width(); ++c) + { + // update model + background subtract + posPixel = (r*m_params.Width() + c)*m_params.MaxModes(); - SubtractPixel(posPixel, data(r, c), m_modes_per_pixel(r, c), low_threshold, high_threshold); + SubtractPixel(posPixel, data(r, c), m_modes_per_pixel(r, c), low_threshold, high_threshold); - low_threshold_mask(r, c) = low_threshold; - high_threshold_mask(r, c) = high_threshold; + low_threshold_mask(r, c) = low_threshold; + high_threshold_mask(r, c) = high_threshold; - m_background(r, c, 0) = (unsigned char)m_modes[posPixel].muR; - m_background(r, c, 1) = (unsigned char)m_modes[posPixel].muG; - m_background(r, c, 2) = (unsigned char)m_modes[posPixel].muB; + m_background(r, c, 0) = (unsigned char)m_modes[posPixel].muR; + m_background(r, c, 1) = (unsigned char)m_modes[posPixel].muG; + m_background(r, c, 2) = (unsigned char)m_modes[posPixel].muB; + } + } + } } } } diff --git a/src/algorithms/dp/GrimsonGMM.h b/src/algorithms/dp/GrimsonGMM.h index d07e4da489bf3f25d22da9910de221adffb2aee8..a89a0eacfef97edfdac3f62a26a9510fe4868aea 100644 --- a/src/algorithms/dp/GrimsonGMM.h +++ b/src/algorithms/dp/GrimsonGMM.h @@ -4,10 +4,12 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { typedef struct GMMGaussian { float variance; @@ -91,6 +93,7 @@ namespace Algorithms // Current background model RgbImage m_background; }; + } } } diff --git a/src/algorithms/dp/Image.cpp b/src/algorithms/dp/Image.cpp index d54587eb997b7a36ead1110ade9eae9be7dc76ec..8089ef42cb73062e8fee5f20e9bae778199e22d3 100644 --- a/src/algorithms/dp/Image.cpp +++ b/src/algorithms/dp/Image.cpp @@ -2,47 +2,58 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -ImageBase::~ImageBase() -{ - if (imgp != NULL && m_bReleaseMemory) - cvReleaseImage(&imgp); - imgp = NULL; -} +//using namespace bgslibrary::algorithms::dp; -void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue) +namespace bgslibrary { - for (int r = 1; r < image.Ptr()->height - 1; ++r) + namespace algorithms { - for (int c = 1; c < image.Ptr()->width - 1; ++c) + namespace dp { - int count = 0; - if (image(r, c) == fgValue) + ImageBase::~ImageBase() { - if (image(r - 1, c - 1) == fgValue) - count++; - if (image(r - 1, c) == fgValue) - count++; - if (image(r - 1, c + 1) == fgValue) - count++; - if (image(r, c - 1) == fgValue) - count++; - if (image(r, c + 1) == fgValue) - count++; - if (image(r + 1, c - 1) == fgValue) - count++; - if (image(r + 1, c) == fgValue) - count++; - if (image(r + 1, c + 1) == fgValue) - count++; - - if (count < minDensity) - filtered(r, c) = 0; - else - filtered(r, c) = fgValue; + if (imgp != NULL && m_bReleaseMemory) + cvReleaseImage(&imgp); + imgp = NULL; } - else + + void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue) { - filtered(r, c) = 0; + for (int r = 1; r < image.Ptr()->height - 1; ++r) + { + for (int c = 1; c < image.Ptr()->width - 1; ++c) + { + int count = 0; + if (image(r, c) == fgValue) + { + if (image(r - 1, c - 1) == fgValue) + count++; + if (image(r - 1, c) == fgValue) + count++; + if (image(r - 1, c + 1) == fgValue) + count++; + if (image(r, c - 1) == fgValue) + count++; + if (image(r, c + 1) == fgValue) + count++; + if (image(r + 1, c - 1) == fgValue) + count++; + if (image(r + 1, c) == fgValue) + count++; + if (image(r + 1, c + 1) == fgValue) + count++; + + if (count < minDensity) + filtered(r, c) = 0; + else + filtered(r, c) = fgValue; + } + else + { + filtered(r, c) = 0; + } + } + } } } } diff --git a/src/algorithms/dp/Image.h b/src/algorithms/dp/Image.h index de20d421a32184c4722fdb92ff9c20b06d0ab14a..f923cbd4ca46b11bd56594d6b4ebab5efa6cdd10 100644 --- a/src/algorithms/dp/Image.h +++ b/src/algorithms/dp/Image.h @@ -4,329 +4,338 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -template <class T> -class ImageIterator +namespace bgslibrary { -public: - ImageIterator(IplImage* image, int x = 0, int y = 0, int dx = 0, int dy = 0) : - i(x), j(y), i0(0) + namespace algorithms { - data = reinterpret_cast<T*>(image->imageData); - step = image->widthStep / sizeof(T); - - nl = image->height; - if ((y + dy) > 0 && (y + dy) < nl) - nl = y + dy; - - if (y < 0) - j = 0; - - data += step*j; - - nc = image->width; - if ((x + dx) > 0 && (x + dx) < nc) - nc = x + dx; - - nc *= image->nChannels; - if (x > 0) - i0 = x*image->nChannels; - i = i0; - - nch = image->nChannels; - } - - /* has next ? */ - bool operator!() const { return j < nl; } - - /* next pixel */ - ImageIterator& operator++() - { - i++; - if (i >= nc) + namespace dp { - i = i0; - j++; - data += step; + template <class T> + class ImageIterator + { + public: + ImageIterator(IplImage* image, int x = 0, int y = 0, int dx = 0, int dy = 0) : + i(x), j(y), i0(0) + { + data = reinterpret_cast<T*>(image->imageData); + step = image->widthStep / sizeof(T); + + nl = image->height; + if ((y + dy) > 0 && (y + dy) < nl) + nl = y + dy; + + if (y < 0) + j = 0; + + data += step*j; + + nc = image->width; + if ((x + dx) > 0 && (x + dx) < nc) + nc = x + dx; + + nc *= image->nChannels; + if (x > 0) + i0 = x*image->nChannels; + i = i0; + + nch = image->nChannels; + } + + /* has next ? */ + bool operator!() const { return j < nl; } + + /* next pixel */ + ImageIterator& operator++() + { + i++; + if (i >= nc) + { + i = i0; + j++; + data += step; + } + return *this; + } + + ImageIterator& operator+=(int s) + { + i += s; + if (i >= nc) + { + i = i0; + j++; + data += step; + } + return *this; + } + + /* pixel access */ + T& operator*() { return data[i]; } + + const T operator*() const { return data[i]; } + + const T neighbor(int dx, int dy) const + { + return *(data + dy*step + i + dx); + } + + T* operator&() const { return data + i; } + + /* current pixel coordinates */ + int column() const { return i / nch; } + int line() const { return j; } + + private: + int i, i0, j; + T* data; + int step; + int nl, nc; + int nch; + }; + + // --- Constants -------------------------------------------------------------- + + const unsigned char NUM_CHANNELS = 3; + + // --- Pixel Types ------------------------------------------------------------ + + class RgbPixel + { + public: + RgbPixel() { ; } + RgbPixel(unsigned char _r, unsigned char _g, unsigned char _b) + { + ch[0] = _r; ch[1] = _g; ch[2] = _b; + } + + RgbPixel& operator=(const RgbPixel& rhs) + { + ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2]; + return *this; + } + + inline unsigned char& operator()(const int _ch) + { + return ch[_ch]; + } + + inline unsigned char operator()(const int _ch) const + { + return ch[_ch]; + } + + unsigned char ch[3]; + }; + + class RgbPixelFloat + { + public: + RgbPixelFloat() { ; } + RgbPixelFloat(float _r, float _g, float _b) + { + ch[0] = _r; ch[1] = _g; ch[2] = _b; + } + + RgbPixelFloat& operator=(const RgbPixelFloat& rhs) + { + ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2]; + return *this; + } + + inline float& operator()(const int _ch) + { + return ch[_ch]; + } + + inline float operator()(const int _ch) const + { + return ch[_ch]; + } + + float ch[3]; + }; + + // --- Image Types ------------------------------------------------------------ + + class ImageBase + { + public: + ImageBase(IplImage* img = NULL) { imgp = img; m_bReleaseMemory = true; } + ~ImageBase(); + + void ReleaseMemory(bool b) { m_bReleaseMemory = b; } + + IplImage* Ptr() { return imgp; } + const IplImage* Ptr() const { return imgp; } + + void ReleaseImage() + { + cvReleaseImage(&imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // copy-constructor + ImageBase(const ImageBase& rhs) + { + // it is very inefficent if this copy-constructor is called + assert(false); + } + + // assignment operator + ImageBase& operator=(const ImageBase& rhs) + { + // it is very inefficent if operator= is called + assert(false); + + return *this; + } + + virtual void Clear() = 0; + + protected: + IplImage* imgp; + bool m_bReleaseMemory; + }; + + class RgbImage : public ImageBase + { + public: + RgbImage(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // channel-level access using image(row, col, channel) + inline unsigned char& operator()(const int r, const int c, const int ch) + { + return (unsigned char &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels + ch]; + } + + inline const unsigned char& operator()(const int r, const int c, const int ch) const + { + return (unsigned char &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels + ch]; + } + + // RGB pixel-level access using image(row, col) + inline RgbPixel& operator()(const int r, const int c) + { + return (RgbPixel &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels]; + } + + inline const RgbPixel& operator()(const int r, const int c) const + { + return (RgbPixel &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels]; + } + }; + + class RgbImageFloat : public ImageBase + { + public: + RgbImageFloat(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // channel-level access using image(row, col, channel) + inline float& operator()(const int r, const int c, const int ch) + { + return (float &)imgp->imageData[r*imgp->widthStep + (c*imgp->nChannels + ch) * sizeof(float)]; + } + + inline float operator()(const int r, const int c, const int ch) const + { + return (float)imgp->imageData[r*imgp->widthStep + (c*imgp->nChannels + ch) * sizeof(float)]; + } + + // RGB pixel-level access using image(row, col) + inline RgbPixelFloat& operator()(const int r, const int c) + { + return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels * sizeof(float)]; + } + + inline const RgbPixelFloat& operator()(const int r, const int c) const + { + return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels * sizeof(float)]; + } + }; + + class BwImage : public ImageBase + { + public: + BwImage(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // pixel-level access using image(row, col) + inline unsigned char& operator()(const int r, const int c) + { + return (unsigned char &)imgp->imageData[r*imgp->widthStep + c]; + } + + inline unsigned char operator()(const int r, const int c) const + { + return (unsigned char)imgp->imageData[r*imgp->widthStep + c]; + } + }; + + class BwImageFloat : public ImageBase + { + public: + BwImageFloat(IplImage* img = NULL) : ImageBase(img) { ; } + + virtual void Clear() + { + cvZero(imgp); + } + + void operator=(IplImage* img) + { + imgp = img; + } + + // pixel-level access using image(row, col) + inline float& operator()(const int r, const int c) + { + return (float &)imgp->imageData[r*imgp->widthStep + c * sizeof(float)]; + } + + inline float operator()(const int r, const int c) const + { + return (float)imgp->imageData[r*imgp->widthStep + c * sizeof(float)]; + } + }; + + // --- Image Functions -------------------------------------------------------- + + //void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue); } - return *this; - } - - ImageIterator& operator+=(int s) - { - i += s; - if (i >= nc) - { - i = i0; - j++; - data += step; - } - return *this; - } - - /* pixel access */ - T& operator*() { return data[i]; } - - const T operator*() const { return data[i]; } - - const T neighbor(int dx, int dy) const - { - return *(data + dy*step + i + dx); - } - - T* operator&() const { return data + i; } - - /* current pixel coordinates */ - int column() const { return i / nch; } - int line() const { return j; } - -private: - int i, i0, j; - T* data; - int step; - int nl, nc; - int nch; -}; - -// --- Constants -------------------------------------------------------------- - -const unsigned char NUM_CHANNELS = 3; - -// --- Pixel Types ------------------------------------------------------------ - -class RgbPixel -{ -public: - RgbPixel() { ; } - RgbPixel(unsigned char _r, unsigned char _g, unsigned char _b) - { - ch[0] = _r; ch[1] = _g; ch[2] = _b; - } - - RgbPixel& operator=(const RgbPixel& rhs) - { - ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2]; - return *this; - } - - inline unsigned char& operator()(const int _ch) - { - return ch[_ch]; - } - - inline unsigned char operator()(const int _ch) const - { - return ch[_ch]; - } - - unsigned char ch[3]; -}; - -class RgbPixelFloat -{ -public: - RgbPixelFloat() { ; } - RgbPixelFloat(float _r, float _g, float _b) - { - ch[0] = _r; ch[1] = _g; ch[2] = _b; - } - - RgbPixelFloat& operator=(const RgbPixelFloat& rhs) - { - ch[0] = rhs.ch[0]; ch[1] = rhs.ch[1]; ch[2] = rhs.ch[2]; - return *this; - } - - inline float& operator()(const int _ch) - { - return ch[_ch]; - } - - inline float operator()(const int _ch) const - { - return ch[_ch]; - } - - float ch[3]; -}; - -// --- Image Types ------------------------------------------------------------ - -class ImageBase -{ -public: - ImageBase(IplImage* img = NULL) { imgp = img; m_bReleaseMemory = true; } - ~ImageBase(); - - void ReleaseMemory(bool b) { m_bReleaseMemory = b; } - - IplImage* Ptr() { return imgp; } - const IplImage* Ptr() const { return imgp; } - - void ReleaseImage() - { - cvReleaseImage(&imgp); - } - - void operator=(IplImage* img) - { - imgp = img; - } - - // copy-constructor - ImageBase(const ImageBase& rhs) - { - // it is very inefficent if this copy-constructor is called - assert(false); - } - - // assignment operator - ImageBase& operator=(const ImageBase& rhs) - { - // it is very inefficent if operator= is called - assert(false); - - return *this; } - - virtual void Clear() = 0; - -protected: - IplImage* imgp; - bool m_bReleaseMemory; -}; - -class RgbImage : public ImageBase -{ -public: - RgbImage(IplImage* img = NULL) : ImageBase(img) { ; } - - virtual void Clear() - { - cvZero(imgp); - } - - void operator=(IplImage* img) - { - imgp = img; - } - - // channel-level access using image(row, col, channel) - inline unsigned char& operator()(const int r, const int c, const int ch) - { - return (unsigned char &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels + ch]; - } - - inline const unsigned char& operator()(const int r, const int c, const int ch) const - { - return (unsigned char &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels + ch]; - } - - // RGB pixel-level access using image(row, col) - inline RgbPixel& operator()(const int r, const int c) - { - return (RgbPixel &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels]; - } - - inline const RgbPixel& operator()(const int r, const int c) const - { - return (RgbPixel &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels]; - } -}; - -class RgbImageFloat : public ImageBase -{ -public: - RgbImageFloat(IplImage* img = NULL) : ImageBase(img) { ; } - - virtual void Clear() - { - cvZero(imgp); - } - - void operator=(IplImage* img) - { - imgp = img; - } - - // channel-level access using image(row, col, channel) - inline float& operator()(const int r, const int c, const int ch) - { - return (float &)imgp->imageData[r*imgp->widthStep + (c*imgp->nChannels + ch) * sizeof(float)]; - } - - inline float operator()(const int r, const int c, const int ch) const - { - return (float)imgp->imageData[r*imgp->widthStep + (c*imgp->nChannels + ch) * sizeof(float)]; - } - - // RGB pixel-level access using image(row, col) - inline RgbPixelFloat& operator()(const int r, const int c) - { - return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels * sizeof(float)]; - } - - inline const RgbPixelFloat& operator()(const int r, const int c) const - { - return (RgbPixelFloat &)imgp->imageData[r*imgp->widthStep + c*imgp->nChannels * sizeof(float)]; - } -}; - -class BwImage : public ImageBase -{ -public: - BwImage(IplImage* img = NULL) : ImageBase(img) { ; } - - virtual void Clear() - { - cvZero(imgp); - } - - void operator=(IplImage* img) - { - imgp = img; - } - - // pixel-level access using image(row, col) - inline unsigned char& operator()(const int r, const int c) - { - return (unsigned char &)imgp->imageData[r*imgp->widthStep + c]; - } - - inline unsigned char operator()(const int r, const int c) const - { - return (unsigned char)imgp->imageData[r*imgp->widthStep + c]; - } -}; - -class BwImageFloat : public ImageBase -{ -public: - BwImageFloat(IplImage* img = NULL) : ImageBase(img) { ; } - - virtual void Clear() - { - cvZero(imgp); - } - - void operator=(IplImage* img) - { - imgp = img; - } - - // pixel-level access using image(row, col) - inline float& operator()(const int r, const int c) - { - return (float &)imgp->imageData[r*imgp->widthStep + c * sizeof(float)]; - } - - inline float operator()(const int r, const int c) const - { - return (float)imgp->imageData[r*imgp->widthStep + c * sizeof(float)]; - } -}; - -// --- Image Functions -------------------------------------------------------- - -void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue); +} #endif diff --git a/src/algorithms/dp/MeanBGS.cpp b/src/algorithms/dp/MeanBGS.cpp index 95aa72f1371b940c87d17f4ee9eb1f64aa10c32a..8b419b0d3de93496af764e55a6749c1e1c22f063 100644 --- a/src/algorithms/dp/MeanBGS.cpp +++ b/src/algorithms/dp/MeanBGS.cpp @@ -2,7 +2,7 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; void MeanBGS::Initalize(const BgsParams& param) { diff --git a/src/algorithms/dp/MeanBGS.h b/src/algorithms/dp/MeanBGS.h index 7ed8baf86cbdc34c7c480b60c25c9e906e6302a8..11238353497f3e9a216b597cfc2ce49e3a967b58 100644 --- a/src/algorithms/dp/MeanBGS.h +++ b/src/algorithms/dp/MeanBGS.h @@ -4,10 +4,12 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { // --- Parameters used by the Mean BGS algorithm --- class MeanParams : public BgsParams { @@ -53,6 +55,7 @@ namespace Algorithms RgbImageFloat m_mean; RgbImage m_background; }; + } } } diff --git a/src/algorithms/dp/PratiMediodBGS.cpp b/src/algorithms/dp/PratiMediodBGS.cpp index 984240c01074d9704e0039dc1f27b2cb8ecba38c..8c9ee81e522da8073c3de684434739bd33dc0edf 100644 --- a/src/algorithms/dp/PratiMediodBGS.cpp +++ b/src/algorithms/dp/PratiMediodBGS.cpp @@ -2,7 +2,7 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; PratiMediodBGS::PratiMediodBGS() { diff --git a/src/algorithms/dp/PratiMediodBGS.h b/src/algorithms/dp/PratiMediodBGS.h index 8a38515e23a321b732b9b8a3bdde55421d88ce8f..203746f5075ba82b832e6ce4a64876a73649637e 100644 --- a/src/algorithms/dp/PratiMediodBGS.h +++ b/src/algorithms/dp/PratiMediodBGS.h @@ -5,10 +5,12 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { // --- Parameters used by the Prati Mediod BGS algorithm --- class PratiParams : public BgsParams { @@ -82,6 +84,7 @@ namespace Algorithms BwImage m_mask_low_threshold; BwImage m_mask_high_threshold; }; + } } } diff --git a/src/algorithms/dp/TextureBGS.cpp b/src/algorithms/dp/TextureBGS.cpp index faa466af51af476162eed650e0da7012b98ea15a..b0adc222207e7c397b9882c1ca1f8bd9db13fdd6 100644 --- a/src/algorithms/dp/TextureBGS.cpp +++ b/src/algorithms/dp/TextureBGS.cpp @@ -2,6 +2,8 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 +using namespace bgslibrary::algorithms::dp; + TextureBGS::TextureBGS() {} TextureBGS::~TextureBGS() {} diff --git a/src/algorithms/dp/TextureBGS.h b/src/algorithms/dp/TextureBGS.h index 93328b5907edff28d8efd78b1f2270f651a6c0ca..da850fdad30083496f2ccfd7c0c9a316221a7c9f 100644 --- a/src/algorithms/dp/TextureBGS.h +++ b/src/algorithms/dp/TextureBGS.h @@ -5,41 +5,50 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -const int REGION_R = 5; // Note: the code currently assumes this value is <= 7 -const int TEXTURE_POINTS = 6; // Note: the code currently assumes this value is 6 -const int TEXTURE_R = 2; // Note: the code currently assumes this value is 2 -const int NUM_BINS = 64; // 2^TEXTURE_POINTS -const int HYSTERSIS = 3; -const double ALPHA = 0.05f; -const double THRESHOLD = 0.5*(REGION_R + REGION_R + 1)*(REGION_R + REGION_R + 1)*NUM_CHANNELS; -const int NUM_MODES = 1; // The paper describes how multiple modes can be maintained, -// but this implementation does not fully support more than one - -struct TextureHistogram +namespace bgslibrary { - unsigned char r[NUM_BINS]; // histogram for red channel - unsigned char g[NUM_BINS]; // histogram for green channel - unsigned char b[NUM_BINS]; // histogram for blue channel -}; - -struct TextureArray -{ - TextureHistogram mode[NUM_MODES]; -}; - -class TextureBGS -{ -public: - TextureBGS(); - ~TextureBGS(); - - void LBP(RgbImage& image, RgbImage& texture); - void Histogram(RgbImage& texture, TextureHistogram* curTextureHist); - int ProximityMeasure(TextureHistogram& bgTexture, TextureHistogram& curTextureHist); - void BgsCompare(TextureArray* bgModel, TextureHistogram* curTextureHist, - unsigned char* modeArray, float threshold, BwImage& fgMask); - void UpdateModel(BwImage& fgMask, TextureArray* bgModel, - TextureHistogram* curTextureHist, unsigned char* modeArray); -}; + namespace algorithms + { + namespace dp + { + const int REGION_R = 5; // Note: the code currently assumes this value is <= 7 + const int TEXTURE_POINTS = 6; // Note: the code currently assumes this value is 6 + const int TEXTURE_R = 2; // Note: the code currently assumes this value is 2 + const int NUM_BINS = 64; // 2^TEXTURE_POINTS + const int HYSTERSIS = 3; + const double ALPHA = 0.05f; + const double THRESHOLD = 0.5*(REGION_R + REGION_R + 1)*(REGION_R + REGION_R + 1)*NUM_CHANNELS; + const int NUM_MODES = 1; // The paper describes how multiple modes can be maintained, + // but this implementation does not fully support more than one + + struct TextureHistogram + { + unsigned char r[NUM_BINS]; // histogram for red channel + unsigned char g[NUM_BINS]; // histogram for green channel + unsigned char b[NUM_BINS]; // histogram for blue channel + }; + + struct TextureArray + { + TextureHistogram mode[NUM_MODES]; + }; + + class TextureBGS + { + public: + TextureBGS(); + ~TextureBGS(); + + void LBP(RgbImage& image, RgbImage& texture); + void Histogram(RgbImage& texture, TextureHistogram* curTextureHist); + int ProximityMeasure(TextureHistogram& bgTexture, TextureHistogram& curTextureHist); + void BgsCompare(TextureArray* bgModel, TextureHistogram* curTextureHist, + unsigned char* modeArray, float threshold, BwImage& fgMask); + void UpdateModel(BwImage& fgMask, TextureArray* bgModel, + TextureHistogram* curTextureHist, unsigned char* modeArray); + }; + } + } +} #endif diff --git a/src/algorithms/dp/WrenGA.cpp b/src/algorithms/dp/WrenGA.cpp index 17a67b7736e9231d54ecadf59dba684ce00cec0c..d29b2061d8ddab3c0c95e75d4f4261b7af7af46c 100644 --- a/src/algorithms/dp/WrenGA.cpp +++ b/src/algorithms/dp/WrenGA.cpp @@ -2,7 +2,7 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; WrenGA::WrenGA() { diff --git a/src/algorithms/dp/WrenGA.h b/src/algorithms/dp/WrenGA.h index dc3d70fa47852aa2a4597dd13308fe9833ded038..ede4cf5cbce4fd9de1c01345781f48ef1565d469 100644 --- a/src/algorithms/dp/WrenGA.h +++ b/src/algorithms/dp/WrenGA.h @@ -4,10 +4,12 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { // --- Parameters used by the Mean BGS algorithm --- class WrenParams : public BgsParams { @@ -65,6 +67,7 @@ namespace Algorithms RgbImage m_background; }; + } } } diff --git a/src/algorithms/dp/ZivkovicAGMM.cpp b/src/algorithms/dp/ZivkovicAGMM.cpp index bcbc8272b2ce31082291daf7d0174d99260875dd..382fbb330c84fe7495a0afdf05cc79c880071eae 100644 --- a/src/algorithms/dp/ZivkovicAGMM.cpp +++ b/src/algorithms/dp/ZivkovicAGMM.cpp @@ -2,7 +2,7 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -using namespace Algorithms::BackgroundSubtraction; +using namespace bgslibrary::algorithms::dp; ZivkovicAGMM::ZivkovicAGMM() { diff --git a/src/algorithms/dp/ZivkovicAGMM.h b/src/algorithms/dp/ZivkovicAGMM.h index 9566d019b288196d587dad71df71f0f1c5a6c99d..b06293531ad1b0df43390227eb7764c9e51419f0 100644 --- a/src/algorithms/dp/ZivkovicAGMM.h +++ b/src/algorithms/dp/ZivkovicAGMM.h @@ -4,10 +4,12 @@ #if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 -namespace Algorithms +namespace bgslibrary { - namespace BackgroundSubtraction + namespace algorithms { + namespace dp + { // --- User adjustable parameters used by the Grimson GMM BGS algorithm --- class ZivkovicParams : public BgsParams { @@ -96,6 +98,7 @@ namespace Algorithms //number of Gaussian components per pixel unsigned char* m_modes_per_pixel; }; + } } } diff --git a/src/algorithms/lb/BGModel.cpp b/src/algorithms/lb/BGModel.cpp index 26d97125bbc98e281a4ac73294d682dae11e3807..b4f71a8f4e22218f54e5e6fb575388d5222eef2f 100644 --- a/src/algorithms/lb/BGModel.cpp +++ b/src/algorithms/lb/BGModel.cpp @@ -1,51 +1,50 @@ #include "BGModel.h" -namespace lb_library +using namespace bgslibrary::algorithms::lb; + +BGModel::BGModel(int width, int height) : m_width(width), m_height(height) +{ + m_SrcImage = cvCreateImage(cvSize(m_width, m_height), IPL_DEPTH_8U, 3); + m_BGImage = cvCreateImage(cvSize(m_width, m_height), IPL_DEPTH_8U, 3); + m_FGImage = cvCreateImage(cvSize(m_width, m_height), IPL_DEPTH_8U, 3); + + cvZero(m_SrcImage); + cvZero(m_BGImage); + cvZero(m_FGImage); +} + +BGModel::~BGModel() +{ + if (m_SrcImage != NULL) cvReleaseImage(&m_SrcImage); + if (m_BGImage != NULL) cvReleaseImage(&m_BGImage); + if (m_FGImage != NULL) cvReleaseImage(&m_FGImage); +} + +IplImage* BGModel::GetSrc() +{ + return m_SrcImage; +} + +IplImage* BGModel::GetFG() +{ + return m_FGImage; +} + +IplImage* BGModel::GetBG() +{ + return m_BGImage; +} + +void BGModel::InitModel(IplImage* image) +{ + cvCopy(image, m_SrcImage); + Init(); + return; +} + +void BGModel::UpdateModel(IplImage* image) { - BGModel::BGModel(int width, int height) : m_width(width), m_height(height) - { - m_SrcImage = cvCreateImage(cvSize(m_width, m_height), IPL_DEPTH_8U, 3); - m_BGImage = cvCreateImage(cvSize(m_width, m_height), IPL_DEPTH_8U, 3); - m_FGImage = cvCreateImage(cvSize(m_width, m_height), IPL_DEPTH_8U, 3); - - cvZero(m_SrcImage); - cvZero(m_BGImage); - cvZero(m_FGImage); - } - - BGModel::~BGModel() - { - if (m_SrcImage != NULL) cvReleaseImage(&m_SrcImage); - if (m_BGImage != NULL) cvReleaseImage(&m_BGImage); - if (m_FGImage != NULL) cvReleaseImage(&m_FGImage); - } - - IplImage* BGModel::GetSrc() - { - return m_SrcImage; - } - - IplImage* BGModel::GetFG() - { - return m_FGImage; - } - - IplImage* BGModel::GetBG() - { - return m_BGImage; - } - - void BGModel::InitModel(IplImage* image) - { - cvCopy(image, m_SrcImage); - Init(); - return; - } - - void BGModel::UpdateModel(IplImage* image) - { - cvCopy(image, m_SrcImage); - Update(); - return; - } + cvCopy(image, m_SrcImage); + Update(); + return; } diff --git a/src/algorithms/lb/BGModel.h b/src/algorithms/lb/BGModel.h index abe43afb7fddfb90b28d87d7c27ec6c25f02844e..fb0bb4c09b1838564300c6cbf9c29ddfc2046b95 100644 --- a/src/algorithms/lb/BGModel.h +++ b/src/algorithms/lb/BGModel.h @@ -7,34 +7,40 @@ #include "Types.h" -namespace lb_library +namespace bgslibrary { - class BGModel + namespace algorithms { - public: + namespace lb + { + class BGModel + { + public: - BGModel(int width, int height); - virtual ~BGModel(); + BGModel(int width, int height); + virtual ~BGModel(); - void InitModel(IplImage* image); - void UpdateModel(IplImage* image); + void InitModel(IplImage* image); + void UpdateModel(IplImage* image); - virtual void setBGModelParameter(int id, int value) {}; + virtual void setBGModelParameter(int id, int value) {}; - virtual IplImage* GetSrc(); - virtual IplImage* GetFG(); - virtual IplImage* GetBG(); + virtual IplImage* GetSrc(); + virtual IplImage* GetFG(); + virtual IplImage* GetBG(); - protected: + protected: - IplImage* m_SrcImage; - IplImage* m_BGImage; - IplImage* m_FGImage; + IplImage* m_SrcImage; + IplImage* m_BGImage; + IplImage* m_FGImage; - const unsigned int m_width; - const unsigned int m_height; + const unsigned int m_width; + const unsigned int m_height; - virtual void Init() = 0; - virtual void Update() = 0; - }; + virtual void Init() = 0; + virtual void Update() = 0; + }; + } + } } diff --git a/src/algorithms/lb/BGModelFuzzyGauss.cpp b/src/algorithms/lb/BGModelFuzzyGauss.cpp index 46c793fa5bac27e211352d771f7ca5d996b15a27..3ead2406ff00a3afe14a85fddde795a1ebc0114b 100644 --- a/src/algorithms/lb/BGModelFuzzyGauss.cpp +++ b/src/algorithms/lb/BGModelFuzzyGauss.cpp @@ -1,174 +1,171 @@ #include "BGModelFuzzyGauss.h" -namespace lb_library +using namespace bgslibrary::algorithms::lb; +using namespace bgslibrary::algorithms::lb::BGModelFuzzyGaussParams; + +BGModelFuzzyGauss::BGModelFuzzyGauss(int width, int height) : BGModel(width, height) { - namespace FuzzyGaussian + m_alphamax = ALPHAFUZZYGAUSS; + m_threshold = THRESHOLDFUZZYGAUSS * THRESHOLDFUZZYGAUSS; + m_threshBG = THRESHOLDBG; + m_noise = NOISEFUZZYGAUSS; + + m_pMu = new DBLRGB[m_width * m_height]; + m_pVar = new DBLRGB[m_width * m_height]; + + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; + + for (unsigned int k = 0; k < (m_width * m_height); k++) { - BGModelFuzzyGauss::BGModelFuzzyGauss(int width, int height) : BGModel(width, height) - { - m_alphamax = ALPHAFUZZYGAUSS; - m_threshold = THRESHOLDFUZZYGAUSS * THRESHOLDFUZZYGAUSS; - m_threshBG = THRESHOLDBG; - m_noise = NOISEFUZZYGAUSS; - - m_pMu = new DBLRGB[m_width * m_height]; - m_pVar = new DBLRGB[m_width * m_height]; - - DBLRGB *pMu = m_pMu; - DBLRGB *pVar = m_pVar; - - for (unsigned int k = 0; k < (m_width * m_height); k++) - { - pMu->Red = 0.0; - pMu->Green = 0.0; - pMu->Blue = 0.0; - - pVar->Red = m_noise; - pVar->Green = m_noise; - pVar->Blue = m_noise; - - pMu++; - pVar++; - } - } + pMu->Red = 0.0; + pMu->Green = 0.0; + pMu->Blue = 0.0; - BGModelFuzzyGauss::~BGModelFuzzyGauss() - { - delete[] m_pMu; - delete[] m_pVar; - } + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; - void BGModelFuzzyGauss::setBGModelParameter(int id, int value) - { - double dvalue = (double)value / 255.0; + pMu++; + pVar++; + } +} - switch (id) - { - case 0: - m_threshold = 100.0*dvalue*dvalue; - break; +BGModelFuzzyGauss::~BGModelFuzzyGauss() +{ + delete[] m_pMu; + delete[] m_pVar; +} + +void BGModelFuzzyGauss::setBGModelParameter(int id, int value) +{ + double dvalue = (double)value / 255.0; - case 1: - m_threshBG = dvalue; - break; + switch (id) + { + case 0: + m_threshold = 100.0*dvalue*dvalue; + break; - case 2: - m_alphamax = dvalue*dvalue*dvalue; - break; + case 1: + m_threshBG = dvalue; + break; - case 3: - m_noise = 100.0*dvalue; - break; - } + case 2: + m_alphamax = dvalue*dvalue*dvalue; + break; - return; - } + case 3: + m_noise = 100.0*dvalue; + break; + } - void BGModelFuzzyGauss::Init() - { - DBLRGB *pMu = m_pMu; - DBLRGB *pVar = m_pVar; + return; +} - Image<BYTERGB> prgbSrc(m_SrcImage); +void BGModelFuzzyGauss::Init() +{ + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; - for (unsigned int i = 0; i < m_height; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - pMu->Red = prgbSrc[i][j].Red; - pMu->Green = prgbSrc[i][j].Green; - pMu->Blue = prgbSrc[i][j].Blue; + Image<BYTERGB> prgbSrc(m_SrcImage); - pVar->Red = m_noise; - pVar->Green = m_noise; - pVar->Blue = m_noise; + for (unsigned int i = 0; i < m_height; i++) + { + for (unsigned int j = 0; j < m_width; j++) + { + pMu->Red = prgbSrc[i][j].Red; + pMu->Green = prgbSrc[i][j].Green; + pMu->Blue = prgbSrc[i][j].Blue; - pMu++; - pVar++; - } - } + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; - return; + pMu++; + pVar++; } + } - void BGModelFuzzyGauss::Update() - { - DBLRGB *pMu = m_pMu; - DBLRGB *pVar = m_pVar; + return; +} - Image<BYTERGB> prgbSrc(m_SrcImage); - Image<BYTERGB> prgbBG(m_BGImage); - Image<BYTERGB> prgbFG(m_FGImage); +void BGModelFuzzyGauss::Update() +{ + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; - for (unsigned int i = 0; i < m_height; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - double srcR = (double)prgbSrc[i][j].Red; - double srcG = (double)prgbSrc[i][j].Green; - double srcB = (double)prgbSrc[i][j].Blue; + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); - // Fuzzy background subtraction (Mahalanobis distance) + for (unsigned int i = 0; i < m_height; i++) + { + for (unsigned int j = 0; j < m_width; j++) + { + double srcR = (double)prgbSrc[i][j].Red; + double srcG = (double)prgbSrc[i][j].Green; + double srcB = (double)prgbSrc[i][j].Blue; - double dr = srcR - pMu->Red; - double dg = srcG - pMu->Green; - double db = srcB - pMu->Blue; + // Fuzzy background subtraction (Mahalanobis distance) - double d2 = dr*dr / pVar->Red + dg*dg / pVar->Green + db*db / pVar->Blue; + double dr = srcR - pMu->Red; + double dg = srcG - pMu->Green; + double db = srcB - pMu->Blue; - double fuzzyBG = 1.0; + double d2 = dr*dr / pVar->Red + dg*dg / pVar->Green + db*db / pVar->Blue; - if (d2 < m_threshold) - fuzzyBG = d2 / m_threshold; + double fuzzyBG = 1.0; - // Fuzzy running average + if (d2 < m_threshold) + fuzzyBG = d2 / m_threshold; - double alpha = m_alphamax*exp(FUZZYEXP*fuzzyBG); + // Fuzzy running average - if (dr*dr > DBL_MIN) - pMu->Red += alpha*dr; + double alpha = m_alphamax*exp(FUZZYEXP*fuzzyBG); - if (dg*dg > DBL_MIN) - pMu->Green += alpha*dg; + if (dr*dr > DBL_MIN) + pMu->Red += alpha*dr; - if (db*db > DBL_MIN) - pMu->Blue += alpha*db; + if (dg*dg > DBL_MIN) + pMu->Green += alpha*dg; - double d; + if (db*db > DBL_MIN) + pMu->Blue += alpha*db; - d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red; - if (d*d > DBL_MIN) - pVar->Red += alpha*d; + double d; - d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green; - if (d*d > DBL_MIN) - pVar->Green += alpha*d; + d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red; + if (d*d > DBL_MIN) + pVar->Red += alpha*d; - d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue; - if (d*d > DBL_MIN) - pVar->Blue += alpha*d; + d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green; + if (d*d > DBL_MIN) + pVar->Green += alpha*d; - pVar->Red = (std::max)(pVar->Red, m_noise); - pVar->Green = (std::max)(pVar->Green, m_noise); - pVar->Blue = (std::max)(pVar->Blue, m_noise); + d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue; + if (d*d > DBL_MIN) + pVar->Blue += alpha*d; - // Set foreground and background + pVar->Red = (std::max)(pVar->Red, m_noise); + pVar->Green = (std::max)(pVar->Green, m_noise); + pVar->Blue = (std::max)(pVar->Blue, m_noise); - if (fuzzyBG >= m_threshBG) - prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; - else - prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + // Set foreground and background - prgbBG[i][j].Red = (unsigned char)pMu->Red; - prgbBG[i][j].Green = (unsigned char)pMu->Green; - prgbBG[i][j].Blue = (unsigned char)pMu->Blue; + if (fuzzyBG >= m_threshBG) + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; + else + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; - pMu++; - pVar++; - } - } + prgbBG[i][j].Red = (unsigned char)pMu->Red; + prgbBG[i][j].Green = (unsigned char)pMu->Green; + prgbBG[i][j].Blue = (unsigned char)pMu->Blue; - return; + pMu++; + pVar++; } } + + return; } diff --git a/src/algorithms/lb/BGModelFuzzyGauss.h b/src/algorithms/lb/BGModelFuzzyGauss.h index 0d473b2e5b4dbc43322fc949b60669af3f1a2936..1d4070e4501e820852ec5dcf2777894b4808c1c4 100644 --- a/src/algorithms/lb/BGModelFuzzyGauss.h +++ b/src/algorithms/lb/BGModelFuzzyGauss.h @@ -2,35 +2,40 @@ #include "BGModel.h" -namespace lb_library +namespace bgslibrary { - namespace FuzzyGaussian + namespace algorithms { - const float ALPHAFUZZYGAUSS = 0.02f; - const float THRESHOLDFUZZYGAUSS = 3.5f; - const float THRESHOLDBG = 0.5f; - const float NOISEFUZZYGAUSS = 50.0f; - const float FUZZYEXP = -5.0f; - - class BGModelFuzzyGauss : public BGModel + namespace lb { - public: - BGModelFuzzyGauss(int width, int height); - ~BGModelFuzzyGauss(); + namespace BGModelFuzzyGaussParams { + const float ALPHAFUZZYGAUSS = 0.02f; + const float THRESHOLDFUZZYGAUSS = 3.5f; + const float THRESHOLDBG = 0.5f; + const float NOISEFUZZYGAUSS = 50.0f; + const float FUZZYEXP = -5.0f; + } + + class BGModelFuzzyGauss : public BGModel + { + public: + BGModelFuzzyGauss(int width, int height); + ~BGModelFuzzyGauss(); - void setBGModelParameter(int id, int value); + void setBGModelParameter(int id, int value); - protected: - double m_alphamax; - double m_threshold; - double m_threshBG; - double m_noise; + protected: + double m_alphamax; + double m_threshold; + double m_threshBG; + double m_noise; - DBLRGB* m_pMu; - DBLRGB* m_pVar; + DBLRGB* m_pMu; + DBLRGB* m_pVar; - void Init(); - void Update(); - }; + void Init(); + void Update(); + }; + } } } diff --git a/src/algorithms/lb/BGModelFuzzySom.cpp b/src/algorithms/lb/BGModelFuzzySom.cpp index 2d85c50fcd12615c501debc0ce386bda6ca0ac74..74eef2cf875665391c7c40700954fe767c1797f2 100644 --- a/src/algorithms/lb/BGModelFuzzySom.cpp +++ b/src/algorithms/lb/BGModelFuzzySom.cpp @@ -1,262 +1,259 @@ #include "BGModelFuzzySom.h" -namespace lb_library +using namespace bgslibrary::algorithms::lb; +using namespace bgslibrary::algorithms::lb::BGModelFuzzySomParams; + +BGModelFuzzySom::BGModelFuzzySom(int width, int height) : BGModel(width, height) { - namespace FuzzyAdaptiveSOM - { - BGModelFuzzySom::BGModelFuzzySom(int width, int height) : BGModel(width, height) - { - m_offset = (KERNEL - 1) / 2; + m_offset = (KERNEL - 1) / 2; - if (SPAN_NEIGHBORS) - m_pad = 0; - else - m_pad = m_offset; + if (SPAN_NEIGHBORS) + m_pad = 0; + else + m_pad = m_offset; - // SOM models + // SOM models - m_widthSOM = m_width*M + 2 * m_offset + (m_width - 1)*m_pad; - m_heightSOM = m_height*N + 2 * m_offset + (m_height - 1)*m_pad; + m_widthSOM = m_width*M + 2 * m_offset + (m_width - 1)*m_pad; + m_heightSOM = m_height*N + 2 * m_offset + (m_height - 1)*m_pad; - m_ppSOM = new DBLRGB*[m_heightSOM]; - for (int n = 0; n < m_heightSOM; n++) - m_ppSOM[n] = new DBLRGB[m_widthSOM]; + m_ppSOM = new DBLRGB*[m_heightSOM]; + for (int n = 0; n < m_heightSOM; n++) + m_ppSOM[n] = new DBLRGB[m_widthSOM]; - for (int j = 0; j < m_heightSOM; j++) - { - for (int i = 0; i < m_widthSOM; i++) - { - m_ppSOM[j][i].Red = 0.0; - m_ppSOM[j][i].Green = 0.0; - m_ppSOM[j][i].Blue = 0.0; - } - } + for (int j = 0; j < m_heightSOM; j++) + { + for (int i = 0; i < m_widthSOM; i++) + { + m_ppSOM[j][i].Red = 0.0; + m_ppSOM[j][i].Green = 0.0; + m_ppSOM[j][i].Blue = 0.0; + } + } - // Create weights + // Create weights - m_ppW = new double*[KERNEL]; - for (int n = 0; n < KERNEL; n++) - m_ppW[n] = new double[KERNEL]; + m_ppW = new double*[KERNEL]; + for (int n = 0; n < KERNEL; n++) + m_ppW[n] = new double[KERNEL]; - // Construct Gaussian kernel using Pascal's triangle + // Construct Gaussian kernel using Pascal's triangle - int cM; - int cN; - m_Wmax = DBL_MIN; + int cM; + int cN; + m_Wmax = DBL_MIN; - cN = 1; - for (int j = 0; j < KERNEL; j++) - { - cM = 1; + cN = 1; + for (int j = 0; j < KERNEL; j++) + { + cM = 1; - for (int i = 0; i < KERNEL; i++) - { - m_ppW[j][i] = cN*cM; + for (int i = 0; i < KERNEL; i++) + { + m_ppW[j][i] = cN*cM; - if (m_ppW[j][i] > m_Wmax) - m_Wmax = m_ppW[j][i]; + if (m_ppW[j][i] > m_Wmax) + m_Wmax = m_ppW[j][i]; - cM = cM * (KERNEL - 1 - i) / (i + 1); - } + cM = cM * (KERNEL - 1 - i) / (i + 1); + } - cN = cN * (KERNEL - 1 - j) / (j + 1); - } + cN = cN * (KERNEL - 1 - j) / (j + 1); + } - // Parameters + // Parameters - m_epsilon1 = EPS1*EPS1; - m_epsilon2 = EPS2*EPS2; + m_epsilon1 = EPS1*EPS1; + m_epsilon2 = EPS2*EPS2; - m_alpha1 = C1 / m_Wmax; - m_alpha2 = C2 / m_Wmax; + m_alpha1 = C1 / m_Wmax; + m_alpha2 = C2 / m_Wmax; - m_K = 0; - m_TSteps = TRAINING_STEPS; - } + m_K = 0; + m_TSteps = TRAINING_STEPS; +} - BGModelFuzzySom::~BGModelFuzzySom() - { - for (int n = 0; n < m_heightSOM; n++) - delete[] m_ppSOM[n]; +BGModelFuzzySom::~BGModelFuzzySom() +{ + for (int n = 0; n < m_heightSOM; n++) + delete[] m_ppSOM[n]; - delete[] m_ppSOM; + delete[] m_ppSOM; - for (int n = 0; n < KERNEL; n++) - delete[] m_ppW[n]; + for (int n = 0; n < KERNEL; n++) + delete[] m_ppW[n]; - delete[] m_ppW; - } + delete[] m_ppW; +} - void BGModelFuzzySom::setBGModelParameter(int id, int value) - { - double dvalue = (double)value / 255.0; +void BGModelFuzzySom::setBGModelParameter(int id, int value) +{ + double dvalue = (double)value / 255.0; - switch (id) - { - case 0: - m_epsilon2 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; - break; + switch (id) + { + case 0: + m_epsilon2 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; - case 1: - m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; - break; + case 1: + m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; - case 2: - m_alpha2 = dvalue*dvalue*dvalue / m_Wmax; - break; + case 2: + m_alpha2 = dvalue*dvalue*dvalue / m_Wmax; + break; - case 3: - m_alpha1 = dvalue*dvalue*dvalue / m_Wmax; - break; + case 3: + m_alpha1 = dvalue*dvalue*dvalue / m_Wmax; + break; - case 5: - m_TSteps = (int)(255.0*dvalue); - break; - } + case 5: + m_TSteps = (int)(255.0*dvalue); + break; + } - return; - } + return; +} + +void BGModelFuzzySom::Init() +{ + Image<BYTERGB> prgbSrc(m_SrcImage); + + for (unsigned int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); - void BGModelFuzzySom::Init() + for (unsigned int i = 0; i < m_width; i++) { - Image<BYTERGB> prgbSrc(m_SrcImage); + int ii = m_offset + i*(M + m_pad); - for (unsigned int j = 0; j < m_height; j++) + for (int l = 0; l < N; l++) { - int jj = m_offset + j*(N + m_pad); - - for (unsigned int i = 0; i < m_width; i++) + for (int k = 0; k < M; k++) { - int ii = m_offset + i*(M + m_pad); - - for (int l = 0; l < N; l++) - { - for (int k = 0; k < M; k++) - { - m_ppSOM[jj + l][ii + k].Red = (double)prgbSrc[j][i].Red; - m_ppSOM[jj + l][ii + k].Green = (double)prgbSrc[j][i].Green; - m_ppSOM[jj + l][ii + k].Blue = (double)prgbSrc[j][i].Blue; - } - } + m_ppSOM[jj + l][ii + k].Red = (double)prgbSrc[j][i].Red; + m_ppSOM[jj + l][ii + k].Green = (double)prgbSrc[j][i].Green; + m_ppSOM[jj + l][ii + k].Blue = (double)prgbSrc[j][i].Blue; } } + } + } - m_K = 0; + m_K = 0; - return; - } + return; +} - void BGModelFuzzySom::Update() - { - double alpha, a; - double epsilon; +void BGModelFuzzySom::Update() +{ + double alpha, a; + double epsilon; - // calibration phase - if (m_K <= m_TSteps) - { - epsilon = m_epsilon1; - alpha = (m_alpha1 - m_K * (m_alpha1 - m_alpha2) / m_TSteps); - m_K++; - } - else // online phase - { - epsilon = m_epsilon2; - alpha = m_alpha2; - } + // calibration phase + if (m_K <= m_TSteps) + { + epsilon = m_epsilon1; + alpha = (m_alpha1 - m_K * (m_alpha1 - m_alpha2) / m_TSteps); + m_K++; + } + else // online phase + { + epsilon = m_epsilon2; + alpha = m_alpha2; + } - Image<BYTERGB> prgbSrc(m_SrcImage); - Image<BYTERGB> prgbBG(m_BGImage); - Image<BYTERGB> prgbFG(m_FGImage); + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); - for (unsigned int j = 0; j < m_height; j++) - { - int jj = m_offset + j*(N + m_pad); + for (unsigned int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); - for (unsigned int i = 0; i < m_width; i++) - { - int ii = m_offset + i*(M + m_pad); + for (unsigned int i = 0; i < m_width; i++) + { + int ii = m_offset + i*(M + m_pad); - double srcR = (double)prgbSrc[j][i].Red; - double srcG = (double)prgbSrc[j][i].Green; - double srcB = (double)prgbSrc[j][i].Blue; + double srcR = (double)prgbSrc[j][i].Red; + double srcG = (double)prgbSrc[j][i].Green; + double srcB = (double)prgbSrc[j][i].Blue; - // Find BMU + // Find BMU - double d2min = DBL_MAX; - int iiHit = ii; - int jjHit = jj; + double d2min = DBL_MAX; + int iiHit = ii; + int jjHit = jj; - for (int l = 0; l < N; l++) - { - for (int k = 0; k < M; k++) - { - double dr = srcR - m_ppSOM[jj + l][ii + k].Red; - double dg = srcG - m_ppSOM[jj + l][ii + k].Green; - double db = srcB - m_ppSOM[jj + l][ii + k].Blue; - - double d2 = dr*dr + dg*dg + db*db; - - if (d2 < d2min) - { - d2min = d2; - iiHit = ii + k; - jjHit = jj + l; - } - } - } + for (int l = 0; l < N; l++) + { + for (int k = 0; k < M; k++) + { + double dr = srcR - m_ppSOM[jj + l][ii + k].Red; + double dg = srcG - m_ppSOM[jj + l][ii + k].Green; + double db = srcB - m_ppSOM[jj + l][ii + k].Blue; - double fuzzyBG = 1.0; + double d2 = dr*dr + dg*dg + db*db; - if (d2min < epsilon) - fuzzyBG = d2min / epsilon; + if (d2 < d2min) + { + d2min = d2; + iiHit = ii + k; + jjHit = jj + l; + } + } + } - // Update SOM + double fuzzyBG = 1.0; - double alphamax = alpha*exp(FUZZYEXP*fuzzyBG); + if (d2min < epsilon) + fuzzyBG = d2min / epsilon; - for (int l = (jjHit - m_offset); l <= (jjHit + m_offset); l++) - { - for (int k = (iiHit - m_offset); k <= (iiHit + m_offset); k++) - { - a = alphamax * m_ppW[l - jjHit + m_offset][k - iiHit + m_offset]; + // Update SOM - // speed hack.. avoid very small increment values. abs() is sloooow. + double alphamax = alpha*exp(FUZZYEXP*fuzzyBG); - double d; + for (int l = (jjHit - m_offset); l <= (jjHit + m_offset); l++) + { + for (int k = (iiHit - m_offset); k <= (iiHit + m_offset); k++) + { + a = alphamax * m_ppW[l - jjHit + m_offset][k - iiHit + m_offset]; - d = srcR - m_ppSOM[l][k].Red; - if (d*d > DBL_MIN) - m_ppSOM[l][k].Red += a*d; + // speed hack.. avoid very small increment values. abs() is sloooow. - d = srcG - m_ppSOM[l][k].Green; - if (d*d > DBL_MIN) - m_ppSOM[l][k].Green += a*d; + double d; - d = srcB - m_ppSOM[l][k].Blue; - if (d*d > DBL_MIN) - m_ppSOM[l][k].Blue += a*d; - } - } + d = srcR - m_ppSOM[l][k].Red; + if (d*d > DBL_MIN) + m_ppSOM[l][k].Red += a*d; - if (fuzzyBG >= FUZZYTHRESH) - { - // Set foreground image - prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 255; - } - else - { - // Set background image - prgbBG[j][i].Red = m_ppSOM[jjHit][iiHit].Red; - prgbBG[j][i].Green = m_ppSOM[jjHit][iiHit].Green; - prgbBG[j][i].Blue = m_ppSOM[jjHit][iiHit].Blue; + d = srcG - m_ppSOM[l][k].Green; + if (d*d > DBL_MIN) + m_ppSOM[l][k].Green += a*d; - // Set foreground image - prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0; - } + d = srcB - m_ppSOM[l][k].Blue; + if (d*d > DBL_MIN) + m_ppSOM[l][k].Blue += a*d; } } - return; + if (fuzzyBG >= FUZZYTHRESH) + { + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 255; + } + else + { + // Set background image + prgbBG[j][i].Red = m_ppSOM[jjHit][iiHit].Red; + prgbBG[j][i].Green = m_ppSOM[jjHit][iiHit].Green; + prgbBG[j][i].Blue = m_ppSOM[jjHit][iiHit].Blue; + + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0; + } } } + + return; } diff --git a/src/algorithms/lb/BGModelFuzzySom.h b/src/algorithms/lb/BGModelFuzzySom.h index 8469ae6e39017018abb1ce1a31482dc8445fa4ad..58a477a890703c956c2d026a8cae622f63cf5179 100644 --- a/src/algorithms/lb/BGModelFuzzySom.h +++ b/src/algorithms/lb/BGModelFuzzySom.h @@ -2,54 +2,55 @@ #include "BGModel.h" -namespace lb_library +namespace bgslibrary { - namespace FuzzyAdaptiveSOM + namespace algorithms { - // SOM parameters - const int M = 3; // width SOM (per pixel) - const int N = 3; // height SOM (per pixel) - const int KERNEL = 3; // size Gaussian kernel - - const bool SPAN_NEIGHBORS = false; // true if update neighborhood spans different pixels // - const int TRAINING_STEPS = 100; // number of training steps - - const double EPS1 = 100.0; // model match distance during training - const double EPS2 = 20.0; // model match distance - const double C1 = 1.0; // learning rate during training - const double C2 = 0.05; // learning rate - - const double FUZZYEXP = -5.0; - const double FUZZYTHRESH = 0.8; - - class BGModelFuzzySom : public BGModel + namespace lb { - public: - BGModelFuzzySom(int width, int height); - ~BGModelFuzzySom(); - - void setBGModelParameter(int id, int value); - - protected: - int m_widthSOM; - int m_heightSOM; - int m_offset; - int m_pad; - int m_K; - int m_TSteps; - - double m_Wmax; - - double m_epsilon1; - double m_epsilon2; - double m_alpha1; - double m_alpha2; - - DBLRGB** m_ppSOM; // SOM grid - double** m_ppW; // Weights - - void Init(); - void Update(); - }; + namespace BGModelFuzzySomParams { + const int M = 3; // width SOM (per pixel) + const int N = 3; // height SOM (per pixel) + const int KERNEL = 3; // size Gaussian kernel + const bool SPAN_NEIGHBORS = false; // true if update neighborhood spans different pixels // + const int TRAINING_STEPS = 100; // number of training steps + const double EPS1 = 100.0; // model match distance during training + const double EPS2 = 20.0; // model match distance + const double C1 = 1.0; // learning rate during training + const double C2 = 0.05; // learning rate + const double FUZZYEXP = -5.0; + const double FUZZYTHRESH = 0.8; + } + + class BGModelFuzzySom : public BGModel + { + public: + BGModelFuzzySom(int width, int height); + ~BGModelFuzzySom(); + + void setBGModelParameter(int id, int value); + + protected: + int m_widthSOM; + int m_heightSOM; + int m_offset; + int m_pad; + int m_K; + int m_TSteps; + + double m_Wmax; + + double m_epsilon1; + double m_epsilon2; + double m_alpha1; + double m_alpha2; + + DBLRGB** m_ppSOM; // SOM grid + double** m_ppW; // Weights + + void Init(); + void Update(); + }; + } } } diff --git a/src/algorithms/lb/BGModelGauss.cpp b/src/algorithms/lb/BGModelGauss.cpp index 6a4b4233efdeb45e68f0d4dcf3e08a444cb94ca7..fbb006a0bc2cd69b719960e5685946df568bca16 100644 --- a/src/algorithms/lb/BGModelGauss.cpp +++ b/src/algorithms/lb/BGModelGauss.cpp @@ -1,164 +1,161 @@ #include "BGModelGauss.h" -namespace lb_library -{ - namespace SimpleGaussian - { - BGModelGauss::BGModelGauss(int width, int height) : BGModel(width, height) - { - m_alpha = ALPHAGAUSS; - m_threshold = THRESHGAUSS*THRESHGAUSS; - m_noise = NOISEGAUSS; +using namespace bgslibrary::algorithms::lb; +using namespace bgslibrary::algorithms::lb::BGModelGaussParams; - m_pMu = new DBLRGB[m_width * m_height]; - m_pVar = new DBLRGB[m_width * m_height]; +BGModelGauss::BGModelGauss(int width, int height) : BGModel(width, height) +{ + m_alpha = ALPHAGAUSS; + m_threshold = THRESHGAUSS*THRESHGAUSS; + m_noise = NOISEGAUSS; - DBLRGB *pMu = m_pMu; - DBLRGB *pVar = m_pVar; + m_pMu = new DBLRGB[m_width * m_height]; + m_pVar = new DBLRGB[m_width * m_height]; - for (unsigned int k = 0; k < (m_width * m_height); k++) - { - pMu->Red = 0.0; - pMu->Green = 0.0; - pMu->Blue = 0.0; + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; - pVar->Red = m_noise; - pVar->Green = m_noise; - pVar->Blue = m_noise; + for (unsigned int k = 0; k < (m_width * m_height); k++) + { + pMu->Red = 0.0; + pMu->Green = 0.0; + pMu->Blue = 0.0; - pMu++; - pVar++; - } - } + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; - BGModelGauss::~BGModelGauss() - { - delete[] m_pMu; - delete[] m_pVar; - } + pMu++; + pVar++; + } +} - void BGModelGauss::setBGModelParameter(int id, int value) - { - double dvalue = (double)value / 255.0; +BGModelGauss::~BGModelGauss() +{ + delete[] m_pMu; + delete[] m_pVar; +} - switch (id) - { - case 0: - m_threshold = 100.0*dvalue*dvalue; - break; +void BGModelGauss::setBGModelParameter(int id, int value) +{ + double dvalue = (double)value / 255.0; - case 1: - m_noise = 100.0*dvalue; - break; + switch (id) + { + case 0: + m_threshold = 100.0*dvalue*dvalue; + break; - case 2: - m_alpha = dvalue*dvalue*dvalue; - break; - } + case 1: + m_noise = 100.0*dvalue; + break; - return; - } + case 2: + m_alpha = dvalue*dvalue*dvalue; + break; + } - void BGModelGauss::Init() - { - DBLRGB *pMu = m_pMu; - DBLRGB *pVar = m_pVar; + return; +} - Image<BYTERGB> prgbSrc(m_SrcImage); +void BGModelGauss::Init() +{ + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; - for (unsigned int i = 0; i < m_height; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - pMu->Red = prgbSrc[i][j].Red; - pMu->Green = prgbSrc[i][j].Green; - pMu->Blue = prgbSrc[i][j].Blue; + Image<BYTERGB> prgbSrc(m_SrcImage); - pVar->Red = m_noise; - pVar->Green = m_noise; - pVar->Blue = m_noise; + for (unsigned int i = 0; i < m_height; i++) + { + for (unsigned int j = 0; j < m_width; j++) + { + pMu->Red = prgbSrc[i][j].Red; + pMu->Green = prgbSrc[i][j].Green; + pMu->Blue = prgbSrc[i][j].Blue; - pMu++; - pVar++; - } - } + pVar->Red = m_noise; + pVar->Green = m_noise; + pVar->Blue = m_noise; - return; + pMu++; + pVar++; } + } - void BGModelGauss::Update() - { - DBLRGB *pMu = m_pMu; - DBLRGB *pVar = m_pVar; + return; +} - Image<BYTERGB> prgbSrc(m_SrcImage); - Image<BYTERGB> prgbBG(m_BGImage); - Image<BYTERGB> prgbFG(m_FGImage); +void BGModelGauss::Update() +{ + DBLRGB *pMu = m_pMu; + DBLRGB *pVar = m_pVar; - for (unsigned int i = 0; i < m_height; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - double srcR = (double)prgbSrc[i][j].Red; - double srcG = (double)prgbSrc[i][j].Green; - double srcB = (double)prgbSrc[i][j].Blue; + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); - // Mahalanobis distance + for (unsigned int i = 0; i < m_height; i++) + { + for (unsigned int j = 0; j < m_width; j++) + { + double srcR = (double)prgbSrc[i][j].Red; + double srcG = (double)prgbSrc[i][j].Green; + double srcB = (double)prgbSrc[i][j].Blue; - double dr = srcR - pMu->Red; - double dg = srcG - pMu->Green; - double db = srcB - pMu->Blue; + // Mahalanobis distance - double d2 = dr*dr / pVar->Red + dg*dg / pVar->Green + db*db / pVar->Blue; + double dr = srcR - pMu->Red; + double dg = srcG - pMu->Green; + double db = srcB - pMu->Blue; - // Classify + double d2 = dr*dr / pVar->Red + dg*dg / pVar->Green + db*db / pVar->Blue; - if (d2 < m_threshold) - prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; - else - prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; + // Classify - // Update parameters + if (d2 < m_threshold) + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + else + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; - if (dr*dr > DBL_MIN) - pMu->Red += m_alpha*dr; + // Update parameters - if (dg*dg > DBL_MIN) - pMu->Green += m_alpha*dg; + if (dr*dr > DBL_MIN) + pMu->Red += m_alpha*dr; - if (db*db > DBL_MIN) - pMu->Blue += m_alpha*db; + if (dg*dg > DBL_MIN) + pMu->Green += m_alpha*dg; - double d; + if (db*db > DBL_MIN) + pMu->Blue += m_alpha*db; - d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red; - if (d*d > DBL_MIN) - pVar->Red += m_alpha*d; + double d; - d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green; - if (d*d > DBL_MIN) - pVar->Green += m_alpha*d; + d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red; + if (d*d > DBL_MIN) + pVar->Red += m_alpha*d; - d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue; - if (d*d > DBL_MIN) - pVar->Blue += m_alpha*d; + d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green; + if (d*d > DBL_MIN) + pVar->Green += m_alpha*d; - pVar->Red = (std::min)(pVar->Red, m_noise); - pVar->Green = (std::min)(pVar->Green, m_noise); - pVar->Blue = (std::min)(pVar->Blue, m_noise); + d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue; + if (d*d > DBL_MIN) + pVar->Blue += m_alpha*d; - // Set background + pVar->Red = (std::min)(pVar->Red, m_noise); + pVar->Green = (std::min)(pVar->Green, m_noise); + pVar->Blue = (std::min)(pVar->Blue, m_noise); - prgbBG[i][j].Red = (unsigned char)pMu->Red; - prgbBG[i][j].Green = (unsigned char)pMu->Green; - prgbBG[i][j].Blue = (unsigned char)pMu->Blue; + // Set background - pMu++; - pVar++; - } - } + prgbBG[i][j].Red = (unsigned char)pMu->Red; + prgbBG[i][j].Green = (unsigned char)pMu->Green; + prgbBG[i][j].Blue = (unsigned char)pMu->Blue; - return; + pMu++; + pVar++; } } + + return; } diff --git a/src/algorithms/lb/BGModelGauss.h b/src/algorithms/lb/BGModelGauss.h index 5dffa0948ac16436a0debdd4947778e8a3a9ab29..42945c14209741bef773ca95d1a3a9642f71b131 100644 --- a/src/algorithms/lb/BGModelGauss.h +++ b/src/algorithms/lb/BGModelGauss.h @@ -2,33 +2,37 @@ #include "BGModel.h" -namespace lb_library +namespace bgslibrary { - namespace SimpleGaussian + namespace algorithms { - // Parameters - const double THRESHGAUSS = 2.5; // Threshold - const double ALPHAGAUSS = 0.0001; // Learning rate - const double NOISEGAUSS = 50.0; // Minimum variance (noise) - - class BGModelGauss : public BGModel + namespace lb { - public: - BGModelGauss(int width, int height); - ~BGModelGauss(); + namespace BGModelGaussParams { + const double THRESHGAUSS = 2.5; // Threshold + const double ALPHAGAUSS = 0.0001; // Learning rate + const double NOISEGAUSS = 50.0; // Minimum variance (noise) + } + + class BGModelGauss : public BGModel + { + public: + BGModelGauss(int width, int height); + ~BGModelGauss(); - void setBGModelParameter(int id, int value); + void setBGModelParameter(int id, int value); - protected: - double m_alpha; - double m_threshold; - double m_noise; + protected: + double m_alpha; + double m_threshold; + double m_noise; - DBLRGB* m_pMu; - DBLRGB* m_pVar; + DBLRGB* m_pMu; + DBLRGB* m_pVar; - void Init(); - void Update(); - }; + void Init(); + void Update(); + }; + } } } diff --git a/src/algorithms/lb/BGModelMog.cpp b/src/algorithms/lb/BGModelMog.cpp index e52c6fb62d065c774e6d2cfeb60e2fbf2d2713e5..c23055f281ec3cd083cf7c1196792134f80bd95c 100644 --- a/src/algorithms/lb/BGModelMog.cpp +++ b/src/algorithms/lb/BGModelMog.cpp @@ -1,273 +1,270 @@ #include "BGModelMog.h" -namespace lb_library -{ - namespace MixtureOfGaussians - { - BGModelMog::BGModelMog(int width, int height) : BGModel(width, height) - { - m_alpha = LEARNINGRATEMOG; - m_threshold = THRESHOLDMOG*THRESHOLDMOG; - m_noise = INITIALVARMOG; +using namespace bgslibrary::algorithms::lb; +using namespace bgslibrary::algorithms::lb::BGModelMogParams; - m_T = BGTHRESHOLDMOG; +BGModelMog::BGModelMog(int width, int height) : BGModel(width, height) +{ + m_alpha = LEARNINGRATEMOG; + m_threshold = THRESHOLDMOG*THRESHOLDMOG; + m_noise = INITIALVARMOG; - m_pMOG = new MOGDATA[NUMBERGAUSSIANS*m_width*m_height]; - m_pK = new int[m_width*m_height]; + m_T = BGTHRESHOLDMOG; - MOGDATA *pMOG = m_pMOG; - int *pK = m_pK; + m_pMOG = new MOGDATA[NUMBERGAUSSIANS*m_width*m_height]; + m_pK = new int[m_width*m_height]; - for (unsigned int i = 0; i < (m_width * m_height); i++) - { - for (unsigned int k = 0; k < NUMBERGAUSSIANS; k++) - { - pMOG->mu.Red = 0.0; - pMOG->mu.Green = 0.0; - pMOG->mu.Blue = 0.0; + MOGDATA *pMOG = m_pMOG; + int *pK = m_pK; - pMOG->var.Red = 0.0; - pMOG->var.Green = 0.0; - pMOG->var.Blue = 0.0; + for (unsigned int i = 0; i < (m_width * m_height); i++) + { + for (unsigned int k = 0; k < NUMBERGAUSSIANS; k++) + { + pMOG->mu.Red = 0.0; + pMOG->mu.Green = 0.0; + pMOG->mu.Blue = 0.0; - pMOG->w = 0.0; - pMOG->sortKey = 0.0; + pMOG->var.Red = 0.0; + pMOG->var.Green = 0.0; + pMOG->var.Blue = 0.0; - pMOG++; - } + pMOG->w = 0.0; + pMOG->sortKey = 0.0; - pK[i] = 0; - } + pMOG++; } - BGModelMog::~BGModelMog() - { - delete[] m_pMOG; - delete[] m_pK; - } + pK[i] = 0; + } +} - void BGModelMog::setBGModelParameter(int id, int value) - { - double dvalue = (double)value / 255.0; +BGModelMog::~BGModelMog() +{ + delete[] m_pMOG; + delete[] m_pK; +} - switch (id) - { - case 0: - m_threshold = 100.0*dvalue*dvalue; - break; +void BGModelMog::setBGModelParameter(int id, int value) +{ + double dvalue = (double)value / 255.0; - case 1: - m_T = dvalue; - break; + switch (id) + { + case 0: + m_threshold = 100.0*dvalue*dvalue; + break; - case 2: - m_alpha = dvalue*dvalue*dvalue; - break; + case 1: + m_T = dvalue; + break; - case 3: - m_noise = 100.0*dvalue;; - break; - } + case 2: + m_alpha = dvalue*dvalue*dvalue; + break; - return; - } + case 3: + m_noise = 100.0*dvalue;; + break; + } - void BGModelMog::Init() - { - MOGDATA *pMOG = m_pMOG; - int *pK = m_pK; + return; +} - Image<BYTERGB> prgbSrc(m_SrcImage); +void BGModelMog::Init() +{ + MOGDATA *pMOG = m_pMOG; + int *pK = m_pK; - int n = 0; - for (unsigned int i = 0; i < m_height; i++) - { - for (unsigned int j = 0; j < m_width; j++) - { - pMOG[0].mu.Red = prgbSrc[i][j].Red; - pMOG[0].mu.Green = prgbSrc[i][j].Green; - pMOG[0].mu.Blue = prgbSrc[i][j].Blue; + Image<BYTERGB> prgbSrc(m_SrcImage); - pMOG[0].var.Red = m_noise; - pMOG[0].var.Green = m_noise; - pMOG[0].var.Blue = m_noise; + int n = 0; + for (unsigned int i = 0; i < m_height; i++) + { + for (unsigned int j = 0; j < m_width; j++) + { + pMOG[0].mu.Red = prgbSrc[i][j].Red; + pMOG[0].mu.Green = prgbSrc[i][j].Green; + pMOG[0].mu.Blue = prgbSrc[i][j].Blue; - pMOG[0].w = 1.0; - pMOG[0].sortKey = pMOG[0].w / sqrt(pMOG[0].var.Red + pMOG[0].var.Green + pMOG[0].var.Blue); + pMOG[0].var.Red = m_noise; + pMOG[0].var.Green = m_noise; + pMOG[0].var.Blue = m_noise; - pK[n] = 1; - n++; + pMOG[0].w = 1.0; + pMOG[0].sortKey = pMOG[0].w / sqrt(pMOG[0].var.Red + pMOG[0].var.Green + pMOG[0].var.Blue); - pMOG += NUMBERGAUSSIANS; - } - } + pK[n] = 1; + n++; - return; + pMOG += NUMBERGAUSSIANS; } + } + + return; +} + +void BGModelMog::Update() +{ + int kBG; + + MOGDATA *pMOG = m_pMOG; + int *pK = m_pK; + + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); - void BGModelMog::Update() + int n = 0; + for (unsigned int i = 0; i < m_height; i++) + { + for (unsigned int j = 0; j < m_width; j++) { - int kBG; + double srcR = (double)prgbSrc[i][j].Red; + double srcG = (double)prgbSrc[i][j].Green; + double srcB = (double)prgbSrc[i][j].Blue; - MOGDATA *pMOG = m_pMOG; - int *pK = m_pK; + // Find matching distribution - Image<BYTERGB> prgbSrc(m_SrcImage); - Image<BYTERGB> prgbBG(m_BGImage); - Image<BYTERGB> prgbFG(m_FGImage); + int kHit = -1; - int n = 0; - for (unsigned int i = 0; i < m_height; i++) + for (int k = 0; k < pK[n]; k++) { - for (unsigned int j = 0; j < m_width; j++) - { - double srcR = (double)prgbSrc[i][j].Red; - double srcG = (double)prgbSrc[i][j].Green; - double srcB = (double)prgbSrc[i][j].Blue; + // Mahalanobis distance + double dr = srcR - pMOG[k].mu.Red; + double dg = srcG - pMOG[k].mu.Green; + double db = srcB - pMOG[k].mu.Blue; + double d2 = dr*dr / pMOG[k].var.Red + dg*dg / pMOG[k].var.Green + db*db / pMOG[k].var.Blue; - // Find matching distribution + if (d2 < m_threshold) + { + kHit = k; + break; + } + } - int kHit = -1; + // Adjust parameters - for (int k = 0; k < pK[n]; k++) + // matching distribution found + if (kHit != -1) + { + for (int k = 0; k < pK[n]; k++) + { + if (k == kHit) { - // Mahalanobis distance - double dr = srcR - pMOG[k].mu.Red; - double dg = srcG - pMOG[k].mu.Green; - double db = srcB - pMOG[k].mu.Blue; - double d2 = dr*dr / pMOG[k].var.Red + dg*dg / pMOG[k].var.Green + db*db / pMOG[k].var.Blue; - - if (d2 < m_threshold) - { - kHit = k; - break; - } - } + pMOG[k].w = pMOG[k].w + m_alpha*(1.0f - pMOG[k].w); - // Adjust parameters + double d; - // matching distribution found - if (kHit != -1) - { - for (int k = 0; k < pK[n]; k++) - { - if (k == kHit) - { - pMOG[k].w = pMOG[k].w + m_alpha*(1.0f - pMOG[k].w); - - double d; - - d = srcR - pMOG[k].mu.Red; - if (d*d > DBL_MIN) - pMOG[k].mu.Red += m_alpha*d; - - d = srcG - pMOG[k].mu.Green; - if (d*d > DBL_MIN) - pMOG[k].mu.Green += m_alpha*d; - - d = srcB - pMOG[k].mu.Blue; - if (d*d > DBL_MIN) - pMOG[k].mu.Blue += m_alpha*d; - - d = (srcR - pMOG[k].mu.Red)*(srcR - pMOG[k].mu.Red) - pMOG[k].var.Red; - if (d*d > DBL_MIN) - pMOG[k].var.Red += m_alpha*d; - - d = (srcG - pMOG[k].mu.Green)*(srcG - pMOG[k].mu.Green) - pMOG[k].var.Green; - if (d*d > DBL_MIN) - pMOG[k].var.Green += m_alpha*d; - - d = (srcB - pMOG[k].mu.Blue)*(srcB - pMOG[k].mu.Blue) - pMOG[k].var.Blue; - if (d*d > DBL_MIN) - pMOG[k].var.Blue += m_alpha*d; - - pMOG[k].var.Red = (std::max)(pMOG[k].var.Red, m_noise); - pMOG[k].var.Green = (std::max)(pMOG[k].var.Green, m_noise); - pMOG[k].var.Blue = (std::max)(pMOG[k].var.Blue, m_noise); - } - else - pMOG[k].w = (1.0 - m_alpha)*pMOG[k].w; - } - } - // no match found... create new one - else - { - if (pK[n] < NUMBERGAUSSIANS) - pK[n]++; + d = srcR - pMOG[k].mu.Red; + if (d*d > DBL_MIN) + pMOG[k].mu.Red += m_alpha*d; + + d = srcG - pMOG[k].mu.Green; + if (d*d > DBL_MIN) + pMOG[k].mu.Green += m_alpha*d; - kHit = pK[n] - 1; + d = srcB - pMOG[k].mu.Blue; + if (d*d > DBL_MIN) + pMOG[k].mu.Blue += m_alpha*d; - if (pK[n] == 1) - pMOG[kHit].w = 1.0; - else - pMOG[kHit].w = LEARNINGRATEMOG; + d = (srcR - pMOG[k].mu.Red)*(srcR - pMOG[k].mu.Red) - pMOG[k].var.Red; + if (d*d > DBL_MIN) + pMOG[k].var.Red += m_alpha*d; - pMOG[kHit].mu.Red = srcR; - pMOG[kHit].mu.Green = srcG; - pMOG[kHit].mu.Blue = srcB; + d = (srcG - pMOG[k].mu.Green)*(srcG - pMOG[k].mu.Green) - pMOG[k].var.Green; + if (d*d > DBL_MIN) + pMOG[k].var.Green += m_alpha*d; - pMOG[kHit].var.Red = m_noise; - pMOG[kHit].var.Green = m_noise; - pMOG[kHit].var.Blue = m_noise; + d = (srcB - pMOG[k].mu.Blue)*(srcB - pMOG[k].mu.Blue) - pMOG[k].var.Blue; + if (d*d > DBL_MIN) + pMOG[k].var.Blue += m_alpha*d; + + pMOG[k].var.Red = (std::max)(pMOG[k].var.Red, m_noise); + pMOG[k].var.Green = (std::max)(pMOG[k].var.Green, m_noise); + pMOG[k].var.Blue = (std::max)(pMOG[k].var.Blue, m_noise); } + else + pMOG[k].w = (1.0 - m_alpha)*pMOG[k].w; + } + } + // no match found... create new one + else + { + if (pK[n] < NUMBERGAUSSIANS) + pK[n]++; - // Normalize weights + kHit = pK[n] - 1; - double wsum = 0.0; + if (pK[n] == 1) + pMOG[kHit].w = 1.0; + else + pMOG[kHit].w = LEARNINGRATEMOG; - for (int k = 0; k < pK[n]; k++) - wsum += pMOG[k].w; + pMOG[kHit].mu.Red = srcR; + pMOG[kHit].mu.Green = srcG; + pMOG[kHit].mu.Blue = srcB; - double wfactor = 1.0 / wsum; + pMOG[kHit].var.Red = m_noise; + pMOG[kHit].var.Green = m_noise; + pMOG[kHit].var.Blue = m_noise; + } - for (int k = 0; k < pK[n]; k++) - { - pMOG[k].w *= wfactor; - pMOG[k].sortKey = pMOG[k].w / sqrt(pMOG[k].var.Red + pMOG[k].var.Green + pMOG[k].var.Blue); - } + // Normalize weights - // Sort distributions + double wsum = 0.0; - for (int k = 0; k < kHit; k++) - { - if (pMOG[kHit].sortKey > pMOG[k].sortKey) - { - std::swap(pMOG[kHit], pMOG[k]); - break; - } - } + for (int k = 0; k < pK[n]; k++) + wsum += pMOG[k].w; - // Determine background distributions + double wfactor = 1.0 / wsum; - wsum = 0.0; + for (int k = 0; k < pK[n]; k++) + { + pMOG[k].w *= wfactor; + pMOG[k].sortKey = pMOG[k].w / sqrt(pMOG[k].var.Red + pMOG[k].var.Green + pMOG[k].var.Blue); + } - for (int k = 0; k < pK[n]; k++) - { - wsum += pMOG[k].w; + // Sort distributions - if (wsum > m_T) - { - kBG = k; - break; - } - } + for (int k = 0; k < kHit; k++) + { + if (pMOG[kHit].sortKey > pMOG[k].sortKey) + { + std::swap(pMOG[kHit], pMOG[k]); + break; + } + } - if (kHit > kBG) - prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; - else - prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + // Determine background distributions - prgbBG[i][j].Red = (unsigned char)pMOG[0].mu.Red; - prgbBG[i][j].Green = (unsigned char)pMOG[0].mu.Green; - prgbBG[i][j].Blue = (unsigned char)pMOG[0].mu.Blue; + wsum = 0.0; - pMOG += NUMBERGAUSSIANS; + for (int k = 0; k < pK[n]; k++) + { + wsum += pMOG[k].w; - n++; + if (wsum > m_T) + { + kBG = k; + break; } } - return; + if (kHit > kBG) + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 255; + else + prgbFG[i][j].Red = prgbFG[i][j].Green = prgbFG[i][j].Blue = 0; + + prgbBG[i][j].Red = (unsigned char)pMOG[0].mu.Red; + prgbBG[i][j].Green = (unsigned char)pMOG[0].mu.Green; + prgbBG[i][j].Blue = (unsigned char)pMOG[0].mu.Blue; + + pMOG += NUMBERGAUSSIANS; + + n++; } } + + return; } diff --git a/src/algorithms/lb/BGModelMog.h b/src/algorithms/lb/BGModelMog.h index 6fd6e90cecdca4ced1c306f20f8a23db4f9291fd..06fd1d066c9e4939f32b858752810c12c05724e7 100644 --- a/src/algorithms/lb/BGModelMog.h +++ b/src/algorithms/lb/BGModelMog.h @@ -2,43 +2,48 @@ #include "BGModel.h" -namespace lb_library +namespace bgslibrary { - namespace MixtureOfGaussians + namespace algorithms { - const unsigned int NUMBERGAUSSIANS = 3; - const float LEARNINGRATEMOG = 0.001f; - const float THRESHOLDMOG = 2.5f; - const float BGTHRESHOLDMOG = 0.5f; - const float INITIALVARMOG = 50.0f; - - typedef struct tagMOGDATA - { - DBLRGB mu; - DBLRGB var; - double w; - double sortKey; - } MOGDATA; - - class BGModelMog : public BGModel + namespace lb { - public: - BGModelMog(int width, int height); - ~BGModelMog(); - - void setBGModelParameter(int id, int value); - - protected: - double m_alpha; - double m_threshold; - double m_noise; - double m_T; - - MOGDATA* m_pMOG; - int* m_pK; // number of distributions per pixel - - void Init(); - void Update(); - }; + namespace BGModelMogParams { + const unsigned int NUMBERGAUSSIANS = 3; + const float LEARNINGRATEMOG = 0.001f; + const float THRESHOLDMOG = 2.5f; + const float BGTHRESHOLDMOG = 0.5f; + const float INITIALVARMOG = 50.0f; + } + + typedef struct tagMOGDATA + { + DBLRGB mu; + DBLRGB var; + double w; + double sortKey; + } MOGDATA; + + class BGModelMog : public BGModel + { + public: + BGModelMog(int width, int height); + ~BGModelMog(); + + void setBGModelParameter(int id, int value); + + protected: + double m_alpha; + double m_threshold; + double m_noise; + double m_T; + + MOGDATA* m_pMOG; + int* m_pK; // number of distributions per pixel + + void Init(); + void Update(); + }; + } } } diff --git a/src/algorithms/lb/BGModelSom.cpp b/src/algorithms/lb/BGModelSom.cpp index e19b8eb28e40648f6b9511c6c8be7740554be4be..040a1daadb31fcbf80266c2ce21824c206162dbd 100644 --- a/src/algorithms/lb/BGModelSom.cpp +++ b/src/algorithms/lb/BGModelSom.cpp @@ -1,255 +1,252 @@ #include "BGModelSom.h" -namespace lb_library +using namespace bgslibrary::algorithms::lb; +using namespace bgslibrary::algorithms::lb::BGModelSomParams; + +BGModelSom::BGModelSom(int width, int height) : BGModel(width, height) { - namespace AdaptiveSOM - { - BGModelSom::BGModelSom(int width, int height) : BGModel(width, height) - { - m_offset = (KERNEL - 1) / 2; + m_offset = (KERNEL - 1) / 2; - if (SPAN_NEIGHBORS) - m_pad = 0; - else - m_pad = m_offset; + if (SPAN_NEIGHBORS) + m_pad = 0; + else + m_pad = m_offset; - // SOM models + // SOM models - m_widthSOM = m_width*M + 2 * m_offset + (m_width - 1)*m_pad; - m_heightSOM = m_height*N + 2 * m_offset + (m_height - 1)*m_pad; + m_widthSOM = m_width*M + 2 * m_offset + (m_width - 1)*m_pad; + m_heightSOM = m_height*N + 2 * m_offset + (m_height - 1)*m_pad; - m_ppSOM = new DBLRGB*[m_heightSOM]; - for (int n = 0; n < m_heightSOM; n++) - m_ppSOM[n] = new DBLRGB[m_widthSOM]; + m_ppSOM = new DBLRGB*[m_heightSOM]; + for (int n = 0; n < m_heightSOM; n++) + m_ppSOM[n] = new DBLRGB[m_widthSOM]; - for (int j = 0; j < m_heightSOM; j++) - { - for (int i = 0; i < m_widthSOM; i++) - { - m_ppSOM[j][i].Red = 0.0; - m_ppSOM[j][i].Green = 0.0; - m_ppSOM[j][i].Blue = 0.0; - } - } + for (int j = 0; j < m_heightSOM; j++) + { + for (int i = 0; i < m_widthSOM; i++) + { + m_ppSOM[j][i].Red = 0.0; + m_ppSOM[j][i].Green = 0.0; + m_ppSOM[j][i].Blue = 0.0; + } + } - // Create weights + // Create weights - m_ppW = new double*[KERNEL]; - for (int n = 0; n < KERNEL; n++) - m_ppW[n] = new double[KERNEL]; + m_ppW = new double*[KERNEL]; + for (int n = 0; n < KERNEL; n++) + m_ppW[n] = new double[KERNEL]; - // Construct Gaussian kernel using Pascal's triangle + // Construct Gaussian kernel using Pascal's triangle - int cM; - int cN; - m_Wmax = DBL_MIN; + int cM; + int cN; + m_Wmax = DBL_MIN; - cN = 1; - for (int j = 0; j < KERNEL; j++) - { - cM = 1; + cN = 1; + for (int j = 0; j < KERNEL; j++) + { + cM = 1; - for (int i = 0; i < KERNEL; i++) - { - m_ppW[j][i] = cN*cM; + for (int i = 0; i < KERNEL; i++) + { + m_ppW[j][i] = cN*cM; - if (m_ppW[j][i] > m_Wmax) - m_Wmax = m_ppW[j][i]; + if (m_ppW[j][i] > m_Wmax) + m_Wmax = m_ppW[j][i]; - cM = cM * (KERNEL - 1 - i) / (i + 1); - } + cM = cM * (KERNEL - 1 - i) / (i + 1); + } - cN = cN * (KERNEL - 1 - j) / (j + 1); - } + cN = cN * (KERNEL - 1 - j) / (j + 1); + } - // Parameters + // Parameters - m_epsilon1 = EPS1*EPS1; - m_epsilon2 = EPS2*EPS2; + m_epsilon1 = EPS1*EPS1; + m_epsilon2 = EPS2*EPS2; - m_alpha1 = C1 / m_Wmax; - m_alpha2 = C2 / m_Wmax; + m_alpha1 = C1 / m_Wmax; + m_alpha2 = C2 / m_Wmax; - m_K = 0; - m_TSteps = TRAINING_STEPS; - } + m_K = 0; + m_TSteps = TRAINING_STEPS; +} - BGModelSom::~BGModelSom() - { - for (int n = 0; n < m_heightSOM; n++) - delete[] m_ppSOM[n]; +BGModelSom::~BGModelSom() +{ + for (int n = 0; n < m_heightSOM; n++) + delete[] m_ppSOM[n]; - delete[] m_ppSOM; + delete[] m_ppSOM; - for (int n = 0; n < KERNEL; n++) - delete[] m_ppW[n]; + for (int n = 0; n < KERNEL; n++) + delete[] m_ppW[n]; - delete[] m_ppW; - } + delete[] m_ppW; +} - void BGModelSom::setBGModelParameter(int id, int value) - { - double dvalue = (double)value / 255.0; +void BGModelSom::setBGModelParameter(int id, int value) +{ + double dvalue = (double)value / 255.0; - switch (id) - { - case 0: - m_epsilon2 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; - break; + switch (id) + { + case 0: + m_epsilon2 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; - case 1: - m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; - break; + case 1: + m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue; + break; - case 2: - m_alpha2 = dvalue*dvalue*dvalue / m_Wmax; - break; + case 2: + m_alpha2 = dvalue*dvalue*dvalue / m_Wmax; + break; - case 3: - m_alpha1 = dvalue*dvalue*dvalue / m_Wmax; - break; + case 3: + m_alpha1 = dvalue*dvalue*dvalue / m_Wmax; + break; - case 5: - m_TSteps = (int)(255.0*dvalue); - break; - } + case 5: + m_TSteps = (int)(255.0*dvalue); + break; + } - return; - } + return; +} + +void BGModelSom::Init() +{ + Image<BYTERGB> prgbSrc(m_SrcImage); + + for (unsigned int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); - void BGModelSom::Init() + for (unsigned int i = 0; i < m_width; i++) { - Image<BYTERGB> prgbSrc(m_SrcImage); + int ii = m_offset + i*(M + m_pad); - for (unsigned int j = 0; j < m_height; j++) + for (int l = 0; l < N; l++) { - int jj = m_offset + j*(N + m_pad); - - for (unsigned int i = 0; i < m_width; i++) + for (int k = 0; k < M; k++) { - int ii = m_offset + i*(M + m_pad); - - for (int l = 0; l < N; l++) - { - for (int k = 0; k < M; k++) - { - m_ppSOM[jj + l][ii + k].Red = (double)prgbSrc[j][i].Red; - m_ppSOM[jj + l][ii + k].Green = (double)prgbSrc[j][i].Green; - m_ppSOM[jj + l][ii + k].Blue = (double)prgbSrc[j][i].Blue; - } - } + m_ppSOM[jj + l][ii + k].Red = (double)prgbSrc[j][i].Red; + m_ppSOM[jj + l][ii + k].Green = (double)prgbSrc[j][i].Green; + m_ppSOM[jj + l][ii + k].Blue = (double)prgbSrc[j][i].Blue; } } + } + } - m_K = 0; + m_K = 0; - return; - } + return; +} - void BGModelSom::Update() - { - double alpha, a; - double epsilon; +void BGModelSom::Update() +{ + double alpha, a; + double epsilon; - // calibration phase - if (m_K <= m_TSteps) - { - epsilon = m_epsilon1; - alpha = (m_alpha1 - m_K*(m_alpha1 - m_alpha2) / m_TSteps); - m_K++; - } - else // online phase - { - epsilon = m_epsilon2; - alpha = m_alpha2; - } + // calibration phase + if (m_K <= m_TSteps) + { + epsilon = m_epsilon1; + alpha = (m_alpha1 - m_K*(m_alpha1 - m_alpha2) / m_TSteps); + m_K++; + } + else // online phase + { + epsilon = m_epsilon2; + alpha = m_alpha2; + } - Image<BYTERGB> prgbSrc(m_SrcImage); - Image<BYTERGB> prgbBG(m_BGImage); - Image<BYTERGB> prgbFG(m_FGImage); + Image<BYTERGB> prgbSrc(m_SrcImage); + Image<BYTERGB> prgbBG(m_BGImage); + Image<BYTERGB> prgbFG(m_FGImage); - for (unsigned int j = 0; j < m_height; j++) - { - int jj = m_offset + j*(N + m_pad); + for (unsigned int j = 0; j < m_height; j++) + { + int jj = m_offset + j*(N + m_pad); - for (unsigned int i = 0; i < m_width; i++) - { - int ii = m_offset + i*(M + m_pad); + for (unsigned int i = 0; i < m_width; i++) + { + int ii = m_offset + i*(M + m_pad); - double srcR = (double)prgbSrc[j][i].Red; - double srcG = (double)prgbSrc[j][i].Green; - double srcB = (double)prgbSrc[j][i].Blue; + double srcR = (double)prgbSrc[j][i].Red; + double srcG = (double)prgbSrc[j][i].Green; + double srcB = (double)prgbSrc[j][i].Blue; - // Find BMU + // Find BMU - double d2min = DBL_MAX; - int iiHit = ii; - int jjHit = jj; + double d2min = DBL_MAX; + int iiHit = ii; + int jjHit = jj; - for (int l = 0; l < N; l++) - { - for (int k = 0; k < M; k++) - { - double dr = srcR - m_ppSOM[jj + l][ii + k].Red; - double dg = srcG - m_ppSOM[jj + l][ii + k].Green; - double db = srcB - m_ppSOM[jj + l][ii + k].Blue; - - double d2 = dr*dr + dg*dg + db*db; - - if (d2 < d2min) - { - d2min = d2; - iiHit = ii + k; - jjHit = jj + l; - } - } - } + for (int l = 0; l < N; l++) + { + for (int k = 0; k < M; k++) + { + double dr = srcR - m_ppSOM[jj + l][ii + k].Red; + double dg = srcG - m_ppSOM[jj + l][ii + k].Green; + double db = srcB - m_ppSOM[jj + l][ii + k].Blue; - // Update SOM + double d2 = dr*dr + dg*dg + db*db; - if (d2min <= epsilon) // matching model found + if (d2 < d2min) { - for (int l = (jjHit - m_offset); l <= (jjHit + m_offset); l++) - { - for (int k = (iiHit - m_offset); k <= (iiHit + m_offset); k++) - { - a = alpha*m_ppW[l - jjHit + m_offset][k - iiHit + m_offset]; + d2min = d2; + iiHit = ii + k; + jjHit = jj + l; + } + } + } - // speed hack.. avoid very small increment values. abs() is sloooow. + // Update SOM - double d; + if (d2min <= epsilon) // matching model found + { + for (int l = (jjHit - m_offset); l <= (jjHit + m_offset); l++) + { + for (int k = (iiHit - m_offset); k <= (iiHit + m_offset); k++) + { + a = alpha*m_ppW[l - jjHit + m_offset][k - iiHit + m_offset]; - d = srcR - m_ppSOM[l][k].Red; - if (d*d > DBL_MIN) - m_ppSOM[l][k].Red += a*d; + // speed hack.. avoid very small increment values. abs() is sloooow. - d = srcG - m_ppSOM[l][k].Green; - if (d*d > DBL_MIN) - m_ppSOM[l][k].Green += a*d; + double d; - d = srcB - m_ppSOM[l][k].Blue; - if (d*d > DBL_MIN) - m_ppSOM[l][k].Blue += a*d; - } - } + d = srcR - m_ppSOM[l][k].Red; + if (d*d > DBL_MIN) + m_ppSOM[l][k].Red += a*d; - // Set background image - prgbBG[j][i].Red = m_ppSOM[jjHit][iiHit].Red; - prgbBG[j][i].Green = m_ppSOM[jjHit][iiHit].Green; - prgbBG[j][i].Blue = m_ppSOM[jjHit][iiHit].Blue; + d = srcG - m_ppSOM[l][k].Green; + if (d*d > DBL_MIN) + m_ppSOM[l][k].Green += a*d; - // Set foreground image - prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0; - } - else - { - // Set foreground image - prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 255; + d = srcB - m_ppSOM[l][k].Blue; + if (d*d > DBL_MIN) + m_ppSOM[l][k].Blue += a*d; } } - } - return; + // Set background image + prgbBG[j][i].Red = m_ppSOM[jjHit][iiHit].Red; + prgbBG[j][i].Green = m_ppSOM[jjHit][iiHit].Green; + prgbBG[j][i].Blue = m_ppSOM[jjHit][iiHit].Blue; + + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0; + } + else + { + // Set foreground image + prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 255; + } } } + + return; } diff --git a/src/algorithms/lb/BGModelSom.h b/src/algorithms/lb/BGModelSom.h index 91a5ec2dba6b64595ff25602c8c05f7e4b7378fb..9774fcab2d2c3a46c0cb2edd8d7a66b50144767c 100644 --- a/src/algorithms/lb/BGModelSom.h +++ b/src/algorithms/lb/BGModelSom.h @@ -2,51 +2,53 @@ #include "BGModel.h" -namespace lb_library +namespace bgslibrary { - namespace AdaptiveSOM + namespace algorithms { - // SOM parameters - const int M = 3; // width SOM (per pixel) - const int N = 3; // height SOM (per pixel) - const int KERNEL = 3; // size Gaussian kernel - - const bool SPAN_NEIGHBORS = false; // true if update neighborhood spans different pixels // - const int TRAINING_STEPS = 100; // number of training steps - - const float EPS1 = 100.0; // model match distance during training - const float EPS2 = 20.0; // model match distance - const float C1 = 1.0; // learning rate during training - const float C2 = 0.05f; // learning rate - - class BGModelSom : public BGModel + namespace lb { - public: - BGModelSom(int width, int height); - ~BGModelSom(); - - void setBGModelParameter(int id, int value); - - protected: - int m_widthSOM; - int m_heightSOM; - int m_offset; - int m_pad; - int m_K; - int m_TSteps; - - double m_Wmax; - - double m_epsilon1; - double m_epsilon2; - double m_alpha1; - double m_alpha2; - - DBLRGB** m_ppSOM; // SOM grid - double** m_ppW; // Weights - - void Init(); - void Update(); - }; + namespace BGModelSomParams { + const int M = 3; // width SOM (per pixel) + const int N = 3; // height SOM (per pixel) + const int KERNEL = 3; // size Gaussian kernel + const bool SPAN_NEIGHBORS = false; // true if update neighborhood spans different pixels // + const int TRAINING_STEPS = 100; // number of training steps + const float EPS1 = 100.0; // model match distance during training + const float EPS2 = 20.0; // model match distance + const float C1 = 1.0; // learning rate during training + const float C2 = 0.05f; // learning rate + } + + class BGModelSom : public BGModel + { + public: + BGModelSom(int width, int height); + ~BGModelSom(); + + void setBGModelParameter(int id, int value); + + protected: + int m_widthSOM; + int m_heightSOM; + int m_offset; + int m_pad; + int m_K; + int m_TSteps; + + double m_Wmax; + + double m_epsilon1; + double m_epsilon2; + double m_alpha1; + double m_alpha2; + + DBLRGB** m_ppSOM; // SOM grid + double** m_ppW; // Weights + + void Init(); + void Update(); + }; + } } } diff --git a/src/algorithms/lb/Types.h b/src/algorithms/lb/Types.h index b6f5ccdb2ddbeb953d4dd69ea7d92fb307fb18c3..1ec3a9855e77be7d1b41ba81fdad3c2a651ca09c 100644 --- a/src/algorithms/lb/Types.h +++ b/src/algorithms/lb/Types.h @@ -1,59 +1,66 @@ #pragma once #include <opencv2/opencv.hpp> +// opencv legacy includes #include <opencv2/core/core_c.h> -namespace lb_library +namespace bgslibrary { -template<class T> class Image -{ -private: - IplImage* imgp; - -public: - Image(IplImage* img=0) {imgp=img;} - ~Image(){imgp=0;} - - void operator=(IplImage* img) {imgp=img;} - - inline T* operator[](const int rowIndx) + namespace algorithms { - return ((T *)(imgp->imageData + rowIndx*imgp->widthStep)); + namespace lb + { + template<class T> class Image + { + private: + IplImage* imgp; + + public: + Image(IplImage* img=0) {imgp=img;} + ~Image(){imgp=0;} + + void operator=(IplImage* img) {imgp=img;} + + inline T* operator[](const int rowIndx) + { + return ((T *)(imgp->imageData + rowIndx*imgp->widthStep)); + } + }; + + typedef struct{ + unsigned char b,g,r; + } RgbPixel; + + typedef struct{ + unsigned char Blue,Green,Red; + } BYTERGB; + + typedef struct{ + unsigned int Blue,Green,Red; + } INTRGB; + + typedef struct{ + float b,g,r; + }RgbPixelFloat; + + typedef struct{ + double Blue,Green,Red; + } DBLRGB; + + typedef Image<RgbPixel> RgbImage; + typedef Image<RgbPixelFloat> RgbImageFloat; + typedef Image<unsigned char> BwImage; + typedef Image<float> BwImageFloat; + + /* + IplImage* img = cvCreateImage(cvSize(640,480), IPL_DEPTH_32F, 3); + RgbImageFloat imgA(img); + for(int i = 0; i < m_height; i++) + for(int j = 0; j < m_width; j++) + imgA[i][j].b = 111; + imgA[i][j].g = 111; + imgA[i][j].r = 111; + */ + } } -}; - -typedef struct{ - unsigned char b,g,r; -} RgbPixel; - -typedef struct{ - unsigned char Blue,Green,Red; -} BYTERGB; - -typedef struct{ - unsigned int Blue,Green,Red; -} INTRGB; - -typedef struct{ - float b,g,r; -}RgbPixelFloat; - -typedef struct{ - double Blue,Green,Red; -} DBLRGB; - -typedef Image<RgbPixel> RgbImage; -typedef Image<RgbPixelFloat> RgbImageFloat; -typedef Image<unsigned char> BwImage; -typedef Image<float> BwImageFloat; - -/* - IplImage* img = cvCreateImage(cvSize(640,480), IPL_DEPTH_32F, 3); - RgbImageFloat imgA(img); - for(int i = 0; i < m_height; i++) - for(int j = 0; j < m_width; j++) - imgA[i][j].b = 111; - imgA[i][j].g = 111; - imgA[i][j].r = 111; - */ } diff --git a/src/tools/ForegroundMaskAnalysis.cpp b/src/tools/ForegroundMaskAnalysis.cpp index beff632e6ce97ae6ea1f8aba8474fa03ae7f0966..75231a773f9553e6f6d613ab5038ad79d1d73601 100644 --- a/src/tools/ForegroundMaskAnalysis.cpp +++ b/src/tools/ForegroundMaskAnalysis.cpp @@ -1,69 +1,68 @@ #include "ForegroundMaskAnalysis.h" -namespace bgslibrary -{ - ForegroundMaskAnalysis::ForegroundMaskAnalysis() : - firstTime(true), showOutput(true), - stopAt(0), img_ref_path("") - { - debug_construction(ForegroundMaskAnalysis); - initLoadSaveConfig(quote(ForegroundMaskAnalysis)); - } - - ForegroundMaskAnalysis::~ForegroundMaskAnalysis() { - debug_destruction(ForegroundMaskAnalysis); - } +using namespace bgslibrary::tools; - void ForegroundMaskAnalysis::process(const long &frameNumber, const std::string &name, const cv::Mat &img_input) - { - if (img_input.empty()) - return; +ForegroundMaskAnalysis::ForegroundMaskAnalysis() : + firstTime(true), showOutput(true), + stopAt(0), img_ref_path("") +{ + debug_construction(ForegroundMaskAnalysis); + initLoadSaveConfig(quote(ForegroundMaskAnalysis)); +} - if (stopAt == frameNumber && img_ref_path.empty() == false) - { - cv::Mat img_ref = cv::imread(img_ref_path, 0); +ForegroundMaskAnalysis::~ForegroundMaskAnalysis() { + debug_destruction(ForegroundMaskAnalysis); +} - if (showOutput) - cv::imshow("ForegroundMaskAnalysis", img_ref); +void ForegroundMaskAnalysis::process(const long &frameNumber, const std::string &name, const cv::Mat &img_input) +{ + if (img_input.empty()) + return; - int rn = cv::countNonZero(img_ref); - cv::Mat i; - cv::Mat u; + if (stopAt == frameNumber && img_ref_path.empty() == false) + { + cv::Mat img_ref = cv::imread(img_ref_path, 0); - if (rn > 0) { - i = img_input & img_ref; - u = img_input | img_ref; - } - else { - i = (~img_input) & (~img_ref); - u = (~img_input) | (~img_ref); - } + if (showOutput) + cv::imshow("ForegroundMaskAnalysis", img_ref); - int in = cv::countNonZero(i); - int un = cv::countNonZero(u); + int rn = cv::countNonZero(img_ref); + cv::Mat i; + cv::Mat u; - double s = (((double)in) / ((double)un)); + if (rn > 0) { + i = img_input & img_ref; + u = img_input | img_ref; + } + else { + i = (~img_input) & (~img_ref); + u = (~img_input) | (~img_ref); + } - if (showOutput) { - cv::imshow("A^B", i); - cv::imshow("AvB", u); - } + int in = cv::countNonZero(i); + int un = cv::countNonZero(u); - std::cout << name << " - Similarity Measure: " << s << " press ENTER to continue" << std::endl; + double s = (((double)in) / ((double)un)); - cv::waitKey(0); + if (showOutput) { + cv::imshow("A^B", i); + cv::imshow("AvB", u); } - firstTime = false; - } + std::cout << name << " - Similarity Measure: " << s << " press ENTER to continue" << std::endl; - void ForegroundMaskAnalysis::save_config(cv::FileStorage &fs) { - fs << "stopAt" << stopAt; - fs << "img_ref_path" << img_ref_path; + cv::waitKey(0); } - void ForegroundMaskAnalysis::load_config(cv::FileStorage &fs) { - fs["stopAt"] >> stopAt; - fs["img_ref_path"] >> img_ref_path; - } + firstTime = false; +} + +void ForegroundMaskAnalysis::save_config(cv::FileStorage &fs) { + fs << "stopAt" << stopAt; + fs << "img_ref_path" << img_ref_path; +} + +void ForegroundMaskAnalysis::load_config(cv::FileStorage &fs) { + fs["stopAt"] >> stopAt; + fs["img_ref_path"] >> img_ref_path; } diff --git a/src/tools/ForegroundMaskAnalysis.h b/src/tools/ForegroundMaskAnalysis.h index 89f46cc3298e8c5b9aa507d5103044c7591c58f5..60d6878c6a4eb351118d85c3ed4e1b1e69331631 100644 --- a/src/tools/ForegroundMaskAnalysis.h +++ b/src/tools/ForegroundMaskAnalysis.h @@ -7,6 +7,7 @@ #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/features2d/features2d.hpp> +// opencv legacy includes #include <opencv2/imgproc/types_c.h> #include <opencv2/imgproc/imgproc_c.h> #include <opencv2/highgui/highgui_c.h> @@ -15,23 +16,26 @@ namespace bgslibrary { - class ForegroundMaskAnalysis: public ILoadSaveConfig + namespace tools { - private: - bool firstTime; - bool showOutput; + class ForegroundMaskAnalysis: public ILoadSaveConfig + { + private: + bool firstTime; + bool showOutput; - public: - ForegroundMaskAnalysis(); - ~ForegroundMaskAnalysis(); + public: + ForegroundMaskAnalysis(); + ~ForegroundMaskAnalysis(); - int stopAt; - std::string img_ref_path; + int stopAt; + std::string img_ref_path; - void process(const long &frameNumber, const std::string &name, const cv::Mat &img_input); + void process(const long &frameNumber, const std::string &name, const cv::Mat &img_input); - private: - void save_config(cv::FileStorage &fs); - void load_config(cv::FileStorage &fs); - }; + private: + void save_config(cv::FileStorage &fs); + void load_config(cv::FileStorage &fs); + }; + } } diff --git a/src/algorithms/T2F/FuzzyUtils.cpp b/src/tools/FuzzyUtils.cpp similarity index 99% rename from src/algorithms/T2F/FuzzyUtils.cpp rename to src/tools/FuzzyUtils.cpp index 8728f861d78a27f187caa9f3d5225ff579b1e747..7fc37e4f587d39bc6c687f87762c1a10d7fa053e 100644 --- a/src/algorithms/T2F/FuzzyUtils.cpp +++ b/src/tools/FuzzyUtils.cpp @@ -1,8 +1,10 @@ #include "FuzzyUtils.h" -FuzzyUtils::FuzzyUtils(void) {} +using namespace bgslibrary::tools; -FuzzyUtils::~FuzzyUtils(void) {} +FuzzyUtils::FuzzyUtils() {} + +FuzzyUtils::~FuzzyUtils() {} void FuzzyUtils::LBP(IplImage* InputImage, IplImage* LBPimage) { diff --git a/src/tools/FuzzyUtils.h b/src/tools/FuzzyUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..4b5b993afe1a04823d67f294a91fcba033b47fd9 --- /dev/null +++ b/src/tools/FuzzyUtils.h @@ -0,0 +1,33 @@ +#pragma once + +#include "PixelUtils.h" + +namespace bgslibrary +{ + namespace tools + { + class FuzzyUtils + { + public: + FuzzyUtils(void); + ~FuzzyUtils(void); + + void LBP(IplImage* InputImage, IplImage* LBP); + void getBinValue(float* neighberGrayPixel, float* BinaryValue, int m, int n); + + void SimilarityDegreesImage(IplImage* CurrentImage, IplImage* BGImage, IplImage* DeltaImage, int n, int color_space); + void RatioPixels(float* CurrentPixel, float* BGPixel, float* DeltaPixel, int n); + + void getFuzzyIntegralSugeno(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage); + void getFuzzyIntegralChoquet(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage); + void FuzzyMeasureG(float g1, float g2, float g3, float *G); + void Trier(float* g, int n, int* index); + float min(float *a, float *b); + float max(float *g, int n); + void gDeDeux(float* a, float* b, float* lambda); + void getLambda(float* g); + + void AdaptativeSelectiveBackgroundModelUpdate(IplImage* CurrentImage, IplImage* BGImage, IplImage* OutputImage, IplImage* Integral, float seuil, float alpha); + }; + } +} diff --git a/src/tools/PerformanceUtils.cpp b/src/tools/PerformanceUtils.cpp index 0e70fd63d670122fe576879126bf8d8064d0513d..c81702a7e7ed343dd3ee526913b451c812838446 100644 --- a/src/tools/PerformanceUtils.cpp +++ b/src/tools/PerformanceUtils.cpp @@ -1,6 +1,6 @@ #include "PerformanceUtils.h" -//#include <opencv2/legacy/compat.hpp> -//#include <opencv2/highgui/highgui_c.h> + +using namespace bgslibrary::tools; PerformanceUtils::PerformanceUtils() { //debug_construction(PerformanceUtils); diff --git a/src/tools/PerformanceUtils.h b/src/tools/PerformanceUtils.h index ce4549d5b59e00a4490a285d4ff2571f78efca89..e0d69c5ed2fcfc099f2c182e4e046bcc752208ce 100644 --- a/src/tools/PerformanceUtils.h +++ b/src/tools/PerformanceUtils.h @@ -6,20 +6,26 @@ #include "PixelUtils.h" -class PerformanceUtils +namespace bgslibrary { -public: - PerformanceUtils(); - ~PerformanceUtils(); + namespace tools + { + class PerformanceUtils + { + public: + PerformanceUtils(); + ~PerformanceUtils(); - float NrPixels(IplImage *image); - float NrAllDetectedPixNotNULL(IplImage *image, IplImage *ground_truth); - float NrTruePositives(IplImage *image, IplImage *ground_truth, bool debug = false); - float NrTrueNegatives(IplImage *image, IplImage *ground_truth, bool debug = false); - float NrFalsePositives(IplImage *image, IplImage *ground_truth, bool debug = false); - float NrFalseNegatives(IplImage *image, IplImage *ground_truth, bool debug = false); - float SimilarityMeasure(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrPixels(IplImage *image); + float NrAllDetectedPixNotNULL(IplImage *image, IplImage *ground_truth); + float NrTruePositives(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrTrueNegatives(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrFalsePositives(IplImage *image, IplImage *ground_truth, bool debug = false); + float NrFalseNegatives(IplImage *image, IplImage *ground_truth, bool debug = false); + float SimilarityMeasure(IplImage *image, IplImage *ground_truth, bool debug = false); - void ImageROC(IplImage *image, IplImage* ground_truth, bool saveResults = false, std::string filename = ""); - void PerformanceEvaluation(IplImage *image, IplImage *ground_truth, bool saveResults = false, std::string filename = "", bool debug = false); -}; + void ImageROC(IplImage *image, IplImage* ground_truth, bool saveResults = false, std::string filename = ""); + void PerformanceEvaluation(IplImage *image, IplImage *ground_truth, bool saveResults = false, std::string filename = "", bool debug = false); + }; + } +} diff --git a/src/tools/PixelUtils.cpp b/src/tools/PixelUtils.cpp index 3770e7eaf925d6c2fcfe86bf73bd21270c224245..2ca5a6c500a661b6e0553732ed21ee09a8c3d877 100644 --- a/src/tools/PixelUtils.cpp +++ b/src/tools/PixelUtils.cpp @@ -1,5 +1,7 @@ #include "PixelUtils.h" +using namespace bgslibrary::tools; + PixelUtils::PixelUtils() { //debug_construction(PixelUtils); } diff --git a/src/tools/PixelUtils.h b/src/tools/PixelUtils.h index 51aade49b62f3918b77d69faa08b1ac2dcf46cb8..fd0842015b4da05292fe243749f49164466e4ee0 100644 --- a/src/tools/PixelUtils.h +++ b/src/tools/PixelUtils.h @@ -2,35 +2,44 @@ #include <stdio.h> #include <opencv2/opencv.hpp> +// opencv legacy includes +//#include <opencv2/legacy/compat.hpp> +//#include <opencv2/highgui/highgui_c.h> #include <opencv2/imgproc/types_c.h> #include <opencv2/imgproc/imgproc_c.h> #include <opencv2/highgui/highgui_c.h> -class PixelUtils +namespace bgslibrary { -public: - PixelUtils(); - ~PixelUtils(); - - void ColorConversion(IplImage* RGBImage, IplImage* ConvertedImage, int color_space); - void cvttoOTHA(IplImage* RGBImage, IplImage* OthaImage); - - void PostProcessing(IplImage *InputImage); - - void GetPixel(IplImage *image, int m, int n, unsigned char *pixelcourant); - void GetGrayPixel(IplImage *image, int m, int n, unsigned char *pixelcourant); - - void PutPixel(IplImage *image, int p, int q, unsigned char *pixelcourant); - void PutGrayPixel(IplImage *image, int p, int q, unsigned char pixelcourant); - - void GetPixel(IplImage *image, int m, int n, float *pixelcourant); - void GetGrayPixel(IplImage *image, int m, int n, float *pixelcourant); - - void PutPixel(IplImage *image, int p, int q, float *pixelcourant); - void PutGrayPixel(IplImage *image, int p, int q, float pixelcourant); - - void getNeighberhoodGrayPixel(IplImage* InputImage, int x, int y, float* neighberPixel); - void ForegroundMaximum(IplImage *Foreground, float *Maximum, int n); - void ForegroundMinimum(IplImage *Foreground, float *Minimum, int n); - void ComplementaryAlphaImageCreation(IplImage *AlphaImage, IplImage *ComplementaryAlphaImage, int n); -}; + namespace tools + { + class PixelUtils + { + public: + PixelUtils(); + ~PixelUtils(); + + void ColorConversion(IplImage* RGBImage, IplImage* ConvertedImage, int color_space); + void cvttoOTHA(IplImage* RGBImage, IplImage* OthaImage); + + void PostProcessing(IplImage *InputImage); + + void GetPixel(IplImage *image, int m, int n, unsigned char *pixelcourant); + void GetGrayPixel(IplImage *image, int m, int n, unsigned char *pixelcourant); + + void PutPixel(IplImage *image, int p, int q, unsigned char *pixelcourant); + void PutGrayPixel(IplImage *image, int p, int q, unsigned char pixelcourant); + + void GetPixel(IplImage *image, int m, int n, float *pixelcourant); + void GetGrayPixel(IplImage *image, int m, int n, float *pixelcourant); + + void PutPixel(IplImage *image, int p, int q, float *pixelcourant); + void PutGrayPixel(IplImage *image, int p, int q, float pixelcourant); + + void getNeighberhoodGrayPixel(IplImage* InputImage, int x, int y, float* neighberPixel); + void ForegroundMaximum(IplImage *Foreground, float *Maximum, int n); + void ForegroundMinimum(IplImage *Foreground, float *Minimum, int n); + void ComplementaryAlphaImageCreation(IplImage *AlphaImage, IplImage *ComplementaryAlphaImage, int n); + }; + } +}