--#-- BGSLIBRARY CODEBASE --#--

This file contains the full codebase of the BGSLibrary (Background Subtraction Library).
It includes all source files, excluding those in the following directories:
.git, build, dataset, modules, pybind11, and cmake-modules.

File structure:
Each file in the codebase is represented in the following format:

--#-- START /path/to/file --#--
[File contents]
--#-- END /path/to/file --#--

This structure allows for easy identification of individual files within the codebase.

--#-- END OF PROMPT --#--

--#-- START ./run_camera.bat --#--
@echo off
cls
build\bgslibrary.exe --use_cam --camera=0
--#-- END ./run_camera.bat --#--

--#-- START ./run_video.bat --#--
@echo off
cls
build\bgslibrary.exe -uf -fn=dataset/video.avi
--#-- END ./run_video.bat --#--

--#-- START ./bgslibrary/tools/PixelUtils.cpp --#--
#include "PixelUtils.h"

using namespace bgslibrary::tools;

PixelUtils::PixelUtils() {
  //debug_construction(PixelUtils);
}
PixelUtils::~PixelUtils() {
  //debug_destruction(PixelUtils);
}

void PixelUtils::ColorConversion(IplImage* RGBImage, IplImage* ConvertedImage, int color_space)
{
  // Space Color RGB - Nothing to do!
  if (color_space == 1)
    cvCopy(RGBImage, ConvertedImage);

  // Space Color Ohta
  if (color_space == 2)
    cvttoOTHA(RGBImage, ConvertedImage);

  // Space Color HSV - V Intensity - (H,S) Chromaticity
  if (color_space == 3)
    cvCvtColor(RGBImage, ConvertedImage, CV_BGR2HSV);

  // Space Color YCrCb - Y Intensity - (Cr,Cb) Chromaticity
  if (color_space == 4)
    cvCvtColor(RGBImage, ConvertedImage, CV_BGR2YCrCb);
}

void PixelUtils::cvttoOTHA(IplImage* RGBImage, IplImage* OthaImage)
{
  float* OhtaPixel = (float*)malloc(3 * (sizeof(float)));
  float* RGBPixel = (float*)malloc(3 * (sizeof(float)));

  for (int i = 0; i < RGBImage->width; i++)
  {
    for (int j = 0; j < RGBImage->height; j++)
    {
      GetPixel(RGBImage, i, j, RGBPixel);

      // I1 = (R + G + B) / 3
      *OhtaPixel = (*(RGBPixel)+(*(RGBPixel + 1)) + (*(RGBPixel + 2))) / 3.0;

      // I2 = (R - B) / 2
      *(OhtaPixel + 1) = (*RGBPixel - (*(RGBPixel + 2))) / 2.0;

      // I3 = (2G - R - B) / 4
      *(OhtaPixel + 2) = (2 * (*(RGBPixel + 1)) - (*RGBPixel) - (*(RGBPixel + 2))) / 4.0;

      PutPixel(OthaImage, i, j, OhtaPixel);
    }
  }

  free(OhtaPixel);
  free(RGBPixel);
}

void PixelUtils::PostProcessing(IplImage *InputImage)
{
  IplImage *ResultImage = cvCreateImage(cvSize(InputImage->width, InputImage->height), IPL_DEPTH_32F, 3);

  cvErode(InputImage, ResultImage, NULL, 1);
  cvDilate(ResultImage, InputImage, NULL, 0);

  cvReleaseImage(&ResultImage);
}

void PixelUtils::GetPixel(IplImage *image, int m, int n, unsigned char *pixelcourant)
{
  for (int k = 0; k < 3; k++)
    pixelcourant[k] = ((unsigned char*)(image->imageData + image->widthStep*n))[m * 3 + k];
}

void PixelUtils::GetGrayPixel(IplImage *image, int m, int n, unsigned char *pixelcourant)
{
  *pixelcourant = ((unsigned char*)(image->imageData + image->widthStep*n))[m];
}

void PixelUtils::PutPixel(IplImage *image, int p, int q, unsigned char *pixelcourant)
{
  for (int r = 0; r < 3; r++)
    ((unsigned char*)(image->imageData + image->widthStep*q))[p * 3 + r] = pixelcourant[r];
}

void PixelUtils::PutGrayPixel(IplImage *image, int p, int q, unsigned char pixelcourant)
{
  ((unsigned char*)(image->imageData + image->widthStep*q))[p] = pixelcourant;
}

void PixelUtils::GetPixel(IplImage *image, int m, int n, float *pixelcourant)
{
  for (int k = 0; k < 3; k++)
    pixelcourant[k] = ((float*)(image->imageData + image->widthStep*n))[m * 3 + k];
}

void PixelUtils::GetGrayPixel(IplImage *image, int m, int n, float *pixelcourant)
{
  *pixelcourant = ((float*)(image->imageData + image->widthStep*n))[m];
}

void PixelUtils::PutPixel(IplImage *image, int p, int q, float *pixelcourant)
{
  for (int r = 0; r < 3; r++)
    ((float*)(image->imageData + image->widthStep*q))[p * 3 + r] = pixelcourant[r];
}

void PixelUtils::PutGrayPixel(IplImage *image, int p, int q, float pixelcourant)
{
  ((float*)(image->imageData + image->widthStep*q))[p] = pixelcourant;
}

void PixelUtils::getNeighberhoodGrayPixel(IplImage* InputImage, int x, int y, float* neighberPixel)
{
  int i, j, k;

  float* pixelCourant = (float*)malloc(1 * (sizeof(float)));

  //le calcul de voisinage pour les 4 coins;
  /* 1.*/
  if (x == 0 && y == 0)
  {
    k = 0;
    for (i = x; i < x + 2; i++)
      for (j = y; j < y + 2; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  /* 2.*/
  if (x == 0 && y == InputImage->width)
  {
    k = 0;
    for (i = x; i < x + 2; i++)
      for (j = y - 1; j < y + 1; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  /* 3.*/
  if (x == InputImage->height && y == 0)
  {
    k = 0;
    for (i = x - 1; i < x + 1; i++)
      for (j = y; j < y + 2; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  /* 4.*/
  if (x == InputImage->height && y == InputImage->width)
  {
    k = 0;
    for (i = x - 1; i < x + 1; i++)
      for (j = y - 1; j < y + 1; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  // Voisinage de la premiere ligne : L(0)
  if (x == 0 && (y != 0 && y != InputImage->width))
  {
    k = 0;
    for (i = x + 1; i >= x; i--)
      for (j = y - 1; j < y + 2; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  // Voisinage de la dernière colonne : C(w)
  if ((x != 0 && x != InputImage->height) && y == InputImage->width)
  {
    k = 0;
    for (i = x + 1; i > x - 2; i--)
      for (j = y - 1; j < y + 1; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  // Voisinage de la dernière ligne : L(h)
  if (x == InputImage->height && (y != 0 && y != InputImage->width))
  {
    k = 0;
    for (i = x; i > x - 2; i--)
      for (j = y - 1; j < y + 2; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  // Voisinage de la premiere colonne : C(0)
  if ((x != 0 && x != InputImage->height) && y == 0)
  {
    k = 0;
    for (i = x - 1; i < x + 2; i++)
      for (j = y; j < y + 2; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  //le calcul du voisinage pour le reste des elementes d'image
  if ((x != 0 && x != InputImage->height) && (y != 0 && y != InputImage->width))
  {
    k = 0;
    for (i = x + 1; i > x - 2; i--)
      for (j = y - 1; j < y + 2; j++)
      {
        GetGrayPixel(InputImage, i, j, pixelCourant);
        *(neighberPixel + k) = *pixelCourant;
        k++;
      }
  }

  free(pixelCourant);
}

void PixelUtils::ForegroundMinimum(IplImage *Foreground, float *Minimum, int n)
{
  int i, j, k;
  float *pixelcourant;

  pixelcourant = (float *)malloc(n * sizeof(float));

  for (k = 0; k < n; k++)
    *(Minimum + k) = 255;

  for (i = 0; i < Foreground->width; i++)
    for (j = 0; j < Foreground->height; j++)
    {
      if (n == 3)
      {
        GetPixel(Foreground, i, j, pixelcourant);

        for (k = 0; k < n; k++)
          if (*(pixelcourant + k) < *(Minimum + k))
            *(Minimum + k) = *(pixelcourant + k);
      }

      if (n == 1)
      {
        GetGrayPixel(Foreground, i, j, pixelcourant);

        if (*pixelcourant < *Minimum)
          *Minimum = *pixelcourant;
      }
    }

  free(pixelcourant);
}

void PixelUtils::ForegroundMaximum(IplImage *Foreground, float *Maximum, int n)
{
  int i, j, k;
  float *pixelcourant;

  pixelcourant = (float *)malloc(n * sizeof(float));

  for (k = 0; k < n; k++)
    *(Maximum + k) = 0;

  for (i = 0; i < Foreground->width; i++)
    for (j = 0; j < Foreground->height; j++)
    {
      if (n == 3)
      {
        GetPixel(Foreground, i, j, pixelcourant);

        for (k = 0; k < n; k++)
          if (*(pixelcourant + k) > *(Maximum + k))
            *(Maximum + k) = *(pixelcourant + k);
      }

      if (n == 1)
      {
        GetGrayPixel(Foreground, i, j, pixelcourant);

        if (*pixelcourant > *Maximum)
          *Maximum = *pixelcourant;
      }
    }

  free(pixelcourant);
}

void PixelUtils::ComplementaryAlphaImageCreation(IplImage *AlphaImage, IplImage *ComplementaryAlphaImage, int n)
{
  int i, j, k;
  float *pixelcourant, *pixelcourant1;

  pixelcourant = (float *)malloc(n * sizeof(float));
  pixelcourant1 = (float *)malloc(n * sizeof(float));

  for (i = 0; i < AlphaImage->width; i++)
    for (j = 0; j < AlphaImage->height; j++)
    {
      if (n == 1)
      {
        GetGrayPixel(AlphaImage, i, j, pixelcourant);
        *pixelcourant1 = 1 - *(pixelcourant);
        PutGrayPixel(ComplementaryAlphaImage, i, j, *pixelcourant1);
      }

      if (n == 3)
      {
        GetPixel(AlphaImage, i, j, pixelcourant);
        for (k = 0; k < 3; k++)
        {
          *pixelcourant1 = 1.0 - *(pixelcourant);
          *(pixelcourant1 + 1) = 1.0 - *(pixelcourant + 1);
          *(pixelcourant1 + 2) = 1.0 - *(pixelcourant + 2);
        }
        PutPixel(ComplementaryAlphaImage, i, j, pixelcourant1);
      }
    }

  free(pixelcourant);
  free(pixelcourant1);
}

--#-- END ./bgslibrary/tools/PixelUtils.cpp --#--

--#-- START ./bgslibrary/tools/ForegroundMaskAnalysis.cpp --#--
#include "ForegroundMaskAnalysis.h"

using namespace bgslibrary::tools;

ForegroundMaskAnalysis::ForegroundMaskAnalysis() :
  firstTime(true), showOutput(true),
  stopAt(0), img_ref_path("")
{
  debug_construction(ForegroundMaskAnalysis);
  initLoadSaveConfig(quote(ForegroundMaskAnalysis));
}

ForegroundMaskAnalysis::~ForegroundMaskAnalysis() {
  debug_destruction(ForegroundMaskAnalysis);
}

void ForegroundMaskAnalysis::process(const long &frameNumber, const std::string &name, const cv::Mat &img_input)
{
  if (img_input.empty())
    return;

  if (stopAt == frameNumber && img_ref_path.empty() == false)
  {
    cv::Mat img_ref = cv::imread(img_ref_path, 0);

    if (showOutput)
      cv::imshow("ForegroundMaskAnalysis", img_ref);

    int rn = cv::countNonZero(img_ref);
    cv::Mat i;
    cv::Mat u;

    if (rn > 0) {
      i = img_input & img_ref;
      u = img_input | img_ref;
    }
    else {
      i = (~img_input) & (~img_ref);
      u = (~img_input) | (~img_ref);
    }

    int in = cv::countNonZero(i);
    int un = cv::countNonZero(u);

    double s = (((double)in) / ((double)un));

    if (showOutput) {
      cv::imshow("A^B", i);
      cv::imshow("AvB", u);
    }

    std::cout << name << " - Similarity Measure: " << s << " press ENTER to continue" << std::endl;

    cv::waitKey(0);
  }

  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;
}

--#-- END ./bgslibrary/tools/ForegroundMaskAnalysis.cpp --#--

--#-- START ./bgslibrary/tools/PerformanceUtils.cpp --#--
#include "PerformanceUtils.h"

using namespace bgslibrary::tools;

PerformanceUtils::PerformanceUtils() {
  //debug_construction(PerformanceUtils);
}

PerformanceUtils::~PerformanceUtils() {
  //debug_destruction(PerformanceUtils);
}

float PerformanceUtils::NrPixels(IplImage *image) {
  return (float)(image->width * image->height);
}

float PerformanceUtils::NrAllDetectedPixNotNULL(IplImage *image, IplImage *ground_truth)
{
  //Nombre de tous les pixels non nuls dans Groundthruth et dans image
  float Union12 = 0.0;

  unsigned char *pixelGT = (unsigned char*)malloc(1 * sizeof(unsigned char));
  unsigned char *pixelI = (unsigned char*)malloc(1 * sizeof(unsigned char));

  PixelUtils p;

  for (int y = 0; y < image->height; y++) {
    for (int x = 0; x < image->width; x++) {
      p.GetGrayPixel(ground_truth, x, y, pixelGT);
      p.GetGrayPixel(image, x, y, pixelI);
      if ((pixelGT[0] != 0) || (pixelI[0] != 0))
        Union12++;
    }
  }

  free(pixelGT);
  free(pixelI);

  return Union12;
}

float PerformanceUtils::NrTruePositives(IplImage *image, IplImage *ground_truth, bool debug)
{
  float nTP = 0.0;

  unsigned char *pixelGT = (unsigned char*)malloc(1 * sizeof(unsigned char));
  unsigned char *pixelI = (unsigned char*)malloc(1 * sizeof(unsigned char));

  IplImage *TPimage = 0;

  if (debug) {
    TPimage = cvCreateImage(cvSize(image->width, image->height), image->depth, image->nChannels);
    cvSetZero(TPimage);
  }

  PixelUtils p;

  for (int y = 0; y < image->height; y++) {
    for (int x = 0; x < image->width; x++) {
      p.GetGrayPixel(ground_truth, x, y, pixelGT);
      p.GetGrayPixel(image, x, y, pixelI);

      if ((pixelGT[0] != 0) && (pixelI[0] != 0)) {
        if (debug)
          p.PutGrayPixel(TPimage, x, y, *pixelI);
        nTP++;
      }
    }
  }

  if (debug) {
    cvNamedWindow("TPImage", 0);
    cvShowImage("TPImage", TPimage);
    //std::cout << "True Positives: " << nTP << std::endl;
    //<< " press ENTER to continue" << std::endl;
    //cvWaitKey(0);
    cvReleaseImage(&TPimage);
  }

  free(pixelGT);
  free(pixelI);

  return nTP;
}

float PerformanceUtils::NrTrueNegatives(IplImage* image, IplImage* ground_truth, bool debug)
{
  float nTN = 0.0;

  unsigned char *pixelGT = (unsigned char *)malloc(1 * sizeof(unsigned char));
  unsigned char *pixelI = (unsigned char *)malloc(1 * sizeof(unsigned char));

  IplImage *TNimage = 0;

  if (debug) {
    TNimage = cvCreateImage(cvSize(image->width, image->height), image->depth, image->nChannels);
    cvSetZero(TNimage);
  }

  PixelUtils p;

  for (int y = 0; y < image->height; y++) {
    for (int x = 0; x < image->width; x++) {
      p.GetGrayPixel(ground_truth, x, y, pixelGT);
      p.GetGrayPixel(image, x, y, pixelI);

      if ((pixelGT[0] == 0) && (pixelI[0] == 0.0)) {
        *pixelI = 255;
        if (debug)
          p.PutGrayPixel(TNimage, x, y, *pixelI);
        nTN++;
      }
    }
  }

  if (debug) {
    cvNamedWindow("TNImage", 0);
    cvShowImage("TNImage", TNimage);
    //std::cout << "True Negatives: " << nTN << std::endl;
    //<< " press ENTER to continue" << std::endl;
    //cvWaitKey(0);
    cvReleaseImage(&TNimage);
  }

  free(pixelGT);
  free(pixelI);

  return nTN;
}

float PerformanceUtils::NrFalsePositives(IplImage *image, IplImage *ground_truth, bool debug)
{
  float nFP = 0.0;

  unsigned char *pixelGT = (unsigned char*)malloc(1 * sizeof(unsigned char));
  unsigned char *pixelI = (unsigned char*)malloc(1 * sizeof(unsigned char));

  IplImage *FPimage = 0;

  if (debug) {
    FPimage = cvCreateImage(cvSize(image->width, image->height), image->depth, image->nChannels);
    cvSetZero(FPimage);
  }

  PixelUtils p;

  for (int y = 0; y < image->height; y++) {
    for (int x = 0; x < image->width; x++) {
      p.GetGrayPixel(ground_truth, x, y, pixelGT);
      p.GetGrayPixel(image, x, y, pixelI);

      if ((pixelGT[0] == 0) && (pixelI[0] != 0)) {
        if (debug)
          p.PutGrayPixel(FPimage, x, y, *pixelI);
        nFP++;
      }
    }
  }

  if (debug) {
    cvNamedWindow("FPImage", 0);
    cvShowImage("FPImage", FPimage);
    //std::cout << "False Positives: " << nFP << std::endl;
    //<< " press ENTER to continue" << std::endl;
    //cvWaitKey(0);
    cvReleaseImage(&FPimage);
  }

  free(pixelGT);
  free(pixelI);

  return nFP;
}

float PerformanceUtils::NrFalseNegatives(IplImage * image, IplImage *ground_truth, bool debug)
{
  float nFN = 0.0;

  unsigned char *pixelGT = (unsigned char*)malloc(1 * sizeof(unsigned char));
  unsigned char *pixelI = (unsigned char*)malloc(1 * sizeof(unsigned char));

  IplImage *FNimage = 0;

  if (debug) {
    FNimage = cvCreateImage(cvSize(image->width, image->height), image->depth, image->nChannels);
    cvSetZero(FNimage);
  }

  PixelUtils p;

  for (int y = 0; y < image->height; y++) {
    for (int x = 0; x < image->width; x++) {
      p.GetGrayPixel(ground_truth, x, y, pixelGT);
      p.GetGrayPixel(image, x, y, pixelI);

      if ((pixelGT[0] != 0) && (pixelI[0] == 0)) {
        if (debug)
          p.PutGrayPixel(FNimage, x, y, *pixelGT);
        nFN++;
      }
    }
  }

  if (debug) {
    cvNamedWindow("FNImage", 0);
    cvShowImage("FNImage", FNimage);
    //std::cout << "False Negatives: " << nFN << std::endl;
    //<< " press ENTER to continue" << std::endl;
    //cvWaitKey(0);
    cvReleaseImage(&FNimage);
  }

  free(pixelGT);
  free(pixelI);

  return nFN;
}

float PerformanceUtils::SimilarityMeasure(IplImage *image, IplImage *ground_truth, bool debug)
{
  cv::Mat img_input = cv::cvarrToMat(image, true);
  cv::Mat img_ref = cv::cvarrToMat(ground_truth, true);

  int rn = cv::countNonZero(img_ref);
  cv::Mat i;
  cv::Mat u;

  if (rn > 0) {
    i = img_input & img_ref;
    u = img_input | img_ref;
  }
  else {
    i = (~img_input) & (~img_ref);
    u = (~img_input) | (~img_ref);
  }

  int in = cv::countNonZero(i);
  int un = cv::countNonZero(u);

  double s = (((double)in) / ((double)un));

  if (debug) {
    cv::imshow("A^B", i);
    cv::imshow("AvB", u);

    //std::cout << "Similarity Measure: " << s  << std::endl;

    //<< " press ENTER to continue" << std::endl;
    //cv::waitKey(0);
  }

  return s;
}

void PerformanceUtils::ImageROC(IplImage *image, IplImage* ground_truth, bool saveResults, std::string filename)
{
  unsigned char *pixelGT = (unsigned char*)malloc(1 * sizeof(unsigned char));
  unsigned char *pixelI = (unsigned char*)malloc(1 * sizeof(unsigned char));

  IplImage *ROCimage = cvCreateImage(cvSize(image->width, image->height), image->depth, image->nChannels);
  cvSetZero(ROCimage);

  PixelUtils p;

  for (int y = 0; y < image->height; y++) {
    for (int x = 0; x < image->width; x++) {
      p.GetGrayPixel(ground_truth, x, y, pixelGT);
      p.GetGrayPixel(image, x, y, pixelI);

      if ((pixelGT[0] != 0) && (pixelI[0] != 0)) { // TP
        *pixelI = 30;
        p.PutGrayPixel(ROCimage, x, y, *pixelI);
      }

      if ((pixelGT[0] == 0) && (pixelI[0] == 0.0)) { // TN
        *pixelI = 0;
        p.PutGrayPixel(ROCimage, x, y, *pixelI);
      }

      if ((pixelGT[0] == 0) && (pixelI[0] != 0)) { // FP
        *pixelI = 255;
        p.PutGrayPixel(ROCimage, x, y, *pixelI);
      }

      if ((pixelGT[0] != 0) && (pixelI[0] == 0)) { // FN
        *pixelI = 100;
        p.PutGrayPixel(ROCimage, x, y, *pixelI);
      }
    }
  }

  cvNamedWindow("ROC image", 0);
  cvShowImage("ROC image", ROCimage);

  if (saveResults) {
    unsigned char *pixelOI = (unsigned char*)malloc(1 * sizeof(unsigned char));
    unsigned char *pixelROC = (unsigned char*)malloc(1 * sizeof(unsigned char));

    float** freq;
    float nTP = 0.0;
    float nTN = 0.0;
    float nFP = 0.0;
    float nFN = 0.0;

    freq = (float**)malloc(256 * (sizeof(float*)));
    for (int i = 0; i < 256; i++)
      freq[i] = (float*)malloc(7 * (sizeof(float)));

    for (int i = 0; i < 256; i++)
      for (int j = 0; j < 6; j++)
        freq[i][j] = 0.0;

    for (int y = 0; y < image->height; y++) {
      for (int x = 0; x < image->width; x++) {
        for (int i = 0; i < 256; i++) {
          p.GetGrayPixel(image, x, y, pixelOI);
          p.GetGrayPixel(ROCimage, x, y, pixelROC);

          if ((pixelOI[0] == i) && (pixelROC[0] == 30.0)) { // TP
            nTP++;
            freq[i][0] = nTP;
            break;
          }

          if ((pixelOI[0] == i) && (pixelROC[0] == 0.0)) { // TN
            nTN++;
            freq[i][1] = nTN;
            break;
          }

          if ((pixelOI[0] == i) && (pixelROC[0] == 255.0)) { // FP
            nFP++;
            freq[i][2] = nFP;
            break;
          }

          if ((pixelOI[0] == i) && (pixelROC[0] == 100)) { // FN
            nFN++;
            freq[i][3] = nFN;
            break;
          }
        }
      }
    }

    //freq[i][0] = TP
    //freq[i][1] = TN
    //freq[i][2] = FP
    //freq[i][3] = FN
    //freq[i][4] = FNR
    //freq[i][5] = FPR

    std::ofstream f(filename);

    if (!f.is_open())
      std::cout << "Failed to open file " << filename << " for writing!" << std::endl;
    else {
      f << "  I     TP     TN     FP     FN    FPR      FNR      DR   \n" << std::endl;

      for (int i = 0; i < 256; i++) {
        //printf("%4d - TP:%5.0f, TN:%5.0f, FP:%5.0f, FN:%5.0f,", i, freq[i][0], freq[i][1], freq[i][2], freq[i][3]);

        if ((freq[i][3] + freq[i][0] != 0.0) && (freq[i][2] + freq[i][1] != 0.0)) {
          freq[i][4] = freq[i][3] / (freq[i][3] + freq[i][0]);  // FNR = FN / (TP + FN);
          freq[i][5] = freq[i][2] / (freq[i][2] + freq[i][1]);	// FPR = FP / (FP + TN);
          freq[i][6] = freq[i][0] / (freq[i][0] + freq[i][3]);	// DR = TP / (TP+FN);

          //printf(" FPR:%1.5f, FNR:%1.5f, D:%1.5f\n", freq[i][5], freq[i][4], freq[i][6]);
          ////fprintf(f," %4d     %1.6f     %1.6f\n",i,freq[i][5],freq[i][4]);
          ////fprintf(f,"  %1.6f     %1.6f\n",freq[i][5],freq[i][4]);
          char line[255];
          sprintf(line, "%3d %6.0f %6.0f %6.0f %6.0f %1.6f %1.6f %1.6f\n",
            i, freq[i][0], freq[i][1], freq[i][2], freq[i][3], freq[i][5], freq[i][4], freq[i][6]);
          f << line;
        }
        //else
        //printf("\n");
      }

      std::cout << "Results saved in " << filename << std::endl;
      f.close();
    }

    free(freq);
    free(pixelOI);
    free(pixelROC);
  }

  //std::cout << "press ENTER to continue" << std::endl;
  //cvWaitKey(0);
  cvReleaseImage(&ROCimage);

  free(pixelGT);
  free(pixelI);
}

void PerformanceUtils::PerformanceEvaluation(IplImage *image, IplImage *ground_truth, bool saveResults, std::string filename, bool debug)
{
  float N = 0;
  N = NrPixels(image);

  float U = 0;
  U = NrAllDetectedPixNotNULL(image, ground_truth);

  float TP = 0;
  TP = NrTruePositives(image, ground_truth, debug);

  float TN = 0;
  TN = NrTrueNegatives(image, ground_truth, debug);

  float FP = 0;
  FP = NrFalsePositives(image, ground_truth, debug);

  float FN = 0;
  FN = NrFalseNegatives(image, ground_truth, debug);

  float DetectionRate = TP / (TP + FN);
  float Precision = TP / (TP + FP);
  float Fmeasure = (2 * DetectionRate * Precision) / (DetectionRate + Precision);

  float Accuracy = (TN + TP) / N;
  float FalseNegativeRate = FN / (TP + FN);

  float FalsePositiveRate = FP / (FP + TN);
  float TruePositiveRate = TP / (TP + FN);

  float SM = 0;
  SM = SimilarityMeasure(image, ground_truth, debug);

  std::stringstream sstm;
  sstm << "N = " << N << std::endl;
  sstm << "U = " << U << std::endl;
  sstm << "TP = " << TP << std::endl;
  sstm << "TN = " << TN << std::endl;
  sstm << "FP = " << FP << std::endl;
  sstm << "FN = " << FN << std::endl;
  sstm << "DetectionRate     = " << DetectionRate << std::endl;
  sstm << "Precision         = " << Precision << std::endl;
  sstm << "Fmeasure          = " << Fmeasure << std::endl;
  sstm << "Accuracy          = " << Accuracy << std::endl;
  sstm << "FalseNegativeRate = " << FalseNegativeRate << std::endl;
  sstm << "FalsePositiveRate = " << FalsePositiveRate << std::endl;
  sstm << "TruePositiveRate  = " << TruePositiveRate << std::endl;
  sstm << "SimilarityMeasure = " << SM << std::endl;

  std::string results = sstm.str();
  std::cout << results;

  if (saveResults) {
    std::ofstream f(filename);

    if (!f.is_open())
      std::cout << "Failed to open file " << filename << " for writing!" << std::endl;
    else {
      f << results;
      std::cout << "Results saved in " << filename << std::endl;
      f.close();
    }
  }
}

--#-- END ./bgslibrary/tools/PerformanceUtils.cpp --#--

--#-- START ./bgslibrary/tools/FuzzyUtils.cpp --#--
#include "FuzzyUtils.h"

using namespace bgslibrary::tools;

FuzzyUtils::FuzzyUtils() {}

FuzzyUtils::~FuzzyUtils() {}

void FuzzyUtils::LBP(IplImage* InputImage, IplImage* LBPimage)
{
  PixelUtils p;

  float* neighberPixel = (float*)malloc(9 * sizeof(float));
  float* BinaryValue = (float*)malloc(9 * sizeof(float));
  float* CarreExp = (float*)malloc(9 * sizeof(float));
  float* valLBP = (float*)malloc(1 * sizeof(float));

  *valLBP = 0;

  int x = 0, y = 0;

  // on implemente les 8 valeurs puissance de 2 qui correspondent aux 8 elem. d'image voisins au elem. d'image central
  *(CarreExp + 0) = 1.0;
  *(CarreExp + 1) = 2.0;
  *(CarreExp + 2) = 4.0;
  *(CarreExp + 3) = 8.0;
  *(CarreExp + 4) = 0.0;
  *(CarreExp + 5) = 16.0;
  *(CarreExp + 6) = 32.0;
  *(CarreExp + 7) = 64.0;
  *(CarreExp + 8) = 128.0;

  //le calcule de LBP
  //pour les 4 coins
  /* 1.*/
  if (x == 0 && y == 0)
  {
    p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
    getBinValue(neighberPixel, BinaryValue, 4, 0);
    *valLBP = *valLBP + ((*(BinaryValue + 1))*(*(CarreExp + 1)) + (*(BinaryValue + 2))*(*(CarreExp + 2)) + (*(BinaryValue + 3))*(*(CarreExp + 3))) / 255.0;
    p.PutGrayPixel(LBPimage, x, y, *valLBP);
  }

  /* 2.*/
  if (x == 0 && y == InputImage->width)
  {
    *valLBP = 0;
    p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
    getBinValue(neighberPixel, BinaryValue, 4, 1);
    *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp)) + (*(BinaryValue + 2))*(*(CarreExp + 2)) + (*(BinaryValue + 3))*(*(CarreExp + 3))) / 255.0;
    p.PutGrayPixel(LBPimage, x, y, *valLBP);
  }

  /* 3.*/
  if (x == InputImage->height && y == 0)
  {
    *valLBP = 0;
    p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
    getBinValue(neighberPixel, BinaryValue, 4, 2);
    *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp)) + (*(BinaryValue + 1))*(*(CarreExp + 1)) + (*(BinaryValue + 3))*(*(CarreExp + 3))) / 255.0;
    p.PutGrayPixel(LBPimage, x, y, *valLBP);
  }

  /* 4.*/
  if (x == InputImage->height && y == InputImage->width)
  {
    *valLBP = 0;
    p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
    getBinValue(neighberPixel, BinaryValue, 4, 3);
    *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp)) + (*(BinaryValue + 1))*(*(CarreExp + 1)) + (*(BinaryValue + 2))*(*(CarreExp + 2))) / 255.0;
    p.PutGrayPixel(LBPimage, x, y, *valLBP);
  }

  //le calcul de LBP pour la première ligne : L(0)
  if (x == 0 && (y != 0 && y != InputImage->width))
  {
    for (int y = 1; y < InputImage->width - 1; y++)
    {
      p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
      getBinValue(neighberPixel, BinaryValue, 6, 4);
      *valLBP = 0;
      *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp)) + (*(BinaryValue + 1))*(*(CarreExp + 1)) + (*(BinaryValue + 2))*(*(CarreExp + 2)) + (*(BinaryValue + 3))*(*(CarreExp + 3)) + (*(BinaryValue + 5))*(*(CarreExp + 5))) / 255.0;
      p.PutGrayPixel(LBPimage, x, y, *valLBP);
    }
  }

  //le calcul de LBP pour la dernière colonne : C(w)
  if ((x != 0 && x != InputImage->height) && y == InputImage->width)
  {
    for (int x = 1; x < InputImage->height - 1; x++)
    {
      p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
      getBinValue(neighberPixel, BinaryValue, 6, 4);
      *valLBP = 0;
      *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp)) + (*(BinaryValue + 1))*(*(CarreExp + 1)) + (*(BinaryValue + 2))*(*(CarreExp + 2)) + (*(BinaryValue + 3))*(*(CarreExp + 3)) + (*(BinaryValue + 5))*(*(CarreExp + 5))) / 255.0;
      p.PutGrayPixel(LBPimage, x, y, *valLBP);
    }
  }

  //le calcul de LBP pour la dernière ligne : L(h)
  if (x == InputImage->height && (y != 0 && y != InputImage->width))
  {
    for (int y = 1; y < InputImage->width - 1; y++)
    {
      p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
      getBinValue(neighberPixel, BinaryValue, 6, 1);
      *valLBP = 0;
      *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp)) + (*(BinaryValue + 2))*(*(CarreExp + 2)) + (*(BinaryValue + 3))*(*(CarreExp + 3)) + (*(BinaryValue + 4))*(*(CarreExp + 4)) + (*(BinaryValue + 5))*(*(CarreExp + 5))) / 255.0;
      p.PutGrayPixel(LBPimage, x, y, *valLBP);
    }
  }

  //le calcul de LBP pour la première colonne : C(0)
  if ((x != 0 && x != InputImage->height) && y == 0)
  {
    for (int x = 1; x < InputImage->height - 1; x++)
    {
      p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
      getBinValue(neighberPixel, BinaryValue, 6, 2);
      *valLBP = 0;
      *valLBP = *valLBP + ((*(BinaryValue))*(*(CarreExp + 5)) + (*(BinaryValue + 1))*(*(CarreExp + 6)) + (*(BinaryValue + 3))*(*(CarreExp + 3)) + (*(BinaryValue + 4))*(*(CarreExp)) + (*(BinaryValue + 5))*(*(CarreExp + 1))) / 255.0;
      p.PutGrayPixel(LBPimage, x, y, *valLBP);
    }
  }

  //pour le reste des elements d'image
  for (int y = 1; y < InputImage->height - 1; y++)
  {
    for (int x = 1; x < InputImage->width - 1; x++)
    {
      p.getNeighberhoodGrayPixel(InputImage, x, y, neighberPixel);
      getBinValue(neighberPixel, BinaryValue, 9, 4);
      //le calcul de la valeur du LBP pour chaque elem. d'im.
      *valLBP = 0;
      for (int l = 0; l < 9; l++)
        *valLBP = *valLBP + ((*(BinaryValue + l)) * (*(CarreExp + l))) / 255.0;
      //printf("\nvalLBP(%d,%d)=%f",x,y,*valLBP);
      p.PutGrayPixel(LBPimage, x, y, *valLBP);
    }
  }

  free(neighberPixel);
  free(BinaryValue);
  free(CarreExp);
  free(valLBP);
}

void FuzzyUtils::getBinValue(float* neighberGrayPixel, float* BinaryValue, int m, int n)
{
  // la comparaison entre la valeur d'elem d'image central et les valeurs des elem. d'im. voisins
  // m = le numero des elements (4, 6 ou 9);
  // n = la position de l'element central;

  int h = 0;
  for (int k = 0; k < m; k++)
  {
    if (*(neighberGrayPixel + k) >= *(neighberGrayPixel + n))
    {
      *(BinaryValue + h) = 1;
      h++;
    }
    else
    {
      *(BinaryValue + h) = 0;
      h++;
    }
  }
}

void FuzzyUtils::SimilarityDegreesImage(IplImage* CurrentImage, IplImage* BGImage, IplImage* DeltaImage, int n, int color_space)
{
  PixelUtils p;
  int i, j;

  if (n == 1)
  {
    float* CurrentGrayPixel = (float*)malloc(1 * (sizeof(float)));
    float* BGGrayPixel = (float*)malloc(1 * (sizeof(float)));
    float* DeltaGrayPixel = (float*)malloc(1 * (sizeof(float)));

    for (i = 0; i < CurrentImage->width; i++)
    {
      for (j = 0; j < CurrentImage->height; j++)
      {
        p.GetGrayPixel(CurrentImage, i, j, CurrentGrayPixel);
        p.GetGrayPixel(BGImage, i, j, BGGrayPixel);
        RatioPixels(CurrentGrayPixel, BGGrayPixel, DeltaGrayPixel, 1);
        p.PutGrayPixel(DeltaImage, i, j, *DeltaGrayPixel);
      }
    }

    free(CurrentGrayPixel);
    free(BGGrayPixel);
    free(DeltaGrayPixel);
  }

  if (n != 1)
  {
    IplImage* ConvertedCurrentImage = cvCreateImage(cvSize(CurrentImage->width, CurrentImage->height), IPL_DEPTH_32F, 3);
    IplImage* ConvertedBGImage = cvCreateImage(cvSize(CurrentImage->width, CurrentImage->height), IPL_DEPTH_32F, 3);

    float* ConvertedCurrentPixel = (float*)malloc(3 * (sizeof(float)));
    float* ConvertedBGPixel = (float*)malloc(3 * (sizeof(float)));
    float* DeltaConvertedPixel = (float*)malloc(3 * (sizeof(float)));

    p.ColorConversion(CurrentImage, ConvertedCurrentImage, color_space);
    p.ColorConversion(BGImage, ConvertedBGImage, color_space);

    for (i = 0; i < CurrentImage->width; i++)
    {
      for (j = 0; j < CurrentImage->height; j++)
      {
        p.GetPixel(ConvertedCurrentImage, i, j, ConvertedCurrentPixel);
        p.GetPixel(ConvertedBGImage, i, j, ConvertedBGPixel);
        RatioPixels(ConvertedCurrentPixel, ConvertedBGPixel, DeltaConvertedPixel, 3);
        p.PutPixel(DeltaImage, i, j, DeltaConvertedPixel);
      }
    }

    free(ConvertedCurrentPixel);
    free(ConvertedBGPixel);
    free(DeltaConvertedPixel);

    cvReleaseImage(&ConvertedCurrentImage);
    cvReleaseImage(&ConvertedBGImage);
  }
}

void FuzzyUtils::RatioPixels(float* CurrentPixel, float* BGPixel, float* DeltaPixel, int n)
{
  if (n == 1)
  {
    if (*CurrentPixel < *BGPixel)
      *DeltaPixel = *CurrentPixel / *BGPixel;

    if (*CurrentPixel > *BGPixel)
      *DeltaPixel = *BGPixel / *CurrentPixel;

    if (*CurrentPixel == *BGPixel)
      *DeltaPixel = 1.0;
  }

  if (n == 3)
    for (int i = 0; i < 3; i++)
    {
      if (*(CurrentPixel + i) < *(BGPixel + i))
        *(DeltaPixel + i) = *(CurrentPixel + i) / *(BGPixel + i);

      if (*(CurrentPixel + i) > *(BGPixel + i))
        *(DeltaPixel + i) = *(BGPixel + i) / *(CurrentPixel + i);

      if (*(CurrentPixel + i) == *(BGPixel + i))
        *(DeltaPixel + i) = 1.0;
    }
}

void FuzzyUtils::getFuzzyIntegralSugeno(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage)
{
  // MeasureG : est un vecteur contenant 3 mesure g (g1,g2,g3) tel que : g1+g2+g3=1
  // n : =2 cad aggreger les 2 images "H" et "Delta"
  //	   =1 cad aggreger uniquement les valeurs des composantes couleurs de l'image "Delta"

  PixelUtils p;

  float* HTexturePixel = (float*)malloc(1 * sizeof(float));
  float* DeltaOhtaPixel = (float*)malloc(3 * (sizeof(float)));
  int *Indice = (int*)malloc(3 * (sizeof(int)));
  float *HI = (float*)malloc(3 * (sizeof(float)));
  float *Integral = (float*)malloc(3 * (sizeof(float)));
  float* X = (float*)malloc(1 * sizeof(float));
  float* XiXj = (float*)malloc(1 * sizeof(float));
  float IntegralFlou;

  *Indice = 0;
  *(Indice + 1) = 1;
  *(Indice + 2) = 2;
  *X = 1.0;

  for (int i = 0; i < H->width; i++)
  {
    for (int j = 0; j < H->height; j++)
    {
      p.GetGrayPixel(H, i, j, HTexturePixel);
      p.GetPixel(Delta, i, j, DeltaOhtaPixel);

      *(HI + 0) = *(HTexturePixel + 0);
      *(HI + 1) = *(DeltaOhtaPixel + 0);
      *(HI + 2) = *(DeltaOhtaPixel + 1);

      Trier(HI, 3, Indice);

      *XiXj = *(MeasureG + (*(Indice + 1))) + (*(MeasureG + (*(Indice + 2))));

      *(Integral + 0) = min((HI + (*(Indice + 0))), X);
      *(Integral + 1) = min((HI + (*(Indice + 1))), XiXj);
      *(Integral + 2) = min((HI + (*(Indice + 2))), ((MeasureG + (*(Indice + 2)))));

      IntegralFlou = max(Integral, 3);
      p.PutGrayPixel(OutputImage, i, j, IntegralFlou);
    }
  }

  free(HTexturePixel);
  free(DeltaOhtaPixel);
  free(Indice);
  free(HI);
  free(X);
  free(XiXj);
  free(Integral);
}

void FuzzyUtils::getFuzzyIntegralChoquet(IplImage* H, IplImage* Delta, int n, float *MeasureG, IplImage* OutputImage)
{
  // MeasureG : est un vecteur contenant 3 mesure g (g1,g2,g3) tel que : g1+g2+g3=1
  // n : =2 cad aggreger les 2 images "H" et "Delta"
  //	   =1 cad aggreger uniquement les valeurs des composantes couleurs de l'image "Delta"

  PixelUtils p;

  float* HTexturePixel = (float*)malloc(1 * sizeof(float));
  float* DeltaOhtaPixel = (float*)malloc(3 * (sizeof(float)));
  int *Indice = (int*)malloc(3 * (sizeof(int)));
  float *HI = (float*)malloc(3 * (sizeof(float)));
  float *Integral = (float*)malloc(3 * (sizeof(float)));
  float* X = (float*)malloc(1 * sizeof(float));
  float* XiXj = (float*)malloc(1 * sizeof(float));
  float IntegralFlou;

  *Indice = 0;
  *(Indice + 1) = 1;
  *(Indice + 2) = 2;
  *X = 1.0;

  for (int i = 0; i < Delta->width; i++)
  {
    for (int j = 0; j < Delta->height; j++)
    {
      if (n == 2)
      {
        p.GetGrayPixel(H, i, j, HTexturePixel);
        p.GetPixel(Delta, i, j, DeltaOhtaPixel);

        *(HI + 0) = *(HTexturePixel + 0);
        *(HI + 1) = *(DeltaOhtaPixel + 0);
        *(HI + 2) = *(DeltaOhtaPixel + 1);
      }

      if (n == 1)
      {
        //remplir HI par les valeurs des 3 composantes couleurs uniquement
        p.GetPixel(Delta, i, j, DeltaOhtaPixel);

        *(HI + 0) = *(DeltaOhtaPixel + 0);
        //*(HI+0) = *(DeltaOhtaPixel+2);
        *(HI + 1) = *(DeltaOhtaPixel + 1);
        *(HI + 2) = *(DeltaOhtaPixel + 2);
      }

      Trier(HI, 3, Indice);
      *XiXj = *(MeasureG + (*(Indice + 1))) + (*(MeasureG + (*(Indice + 2))));

      *(Integral + 0) = *(HI + (*(Indice + 0)))* (*X - *XiXj);
      *(Integral + 1) = *(HI + (*(Indice + 1)))* (*XiXj - *(MeasureG + (*(Indice + 2))));
      *(Integral + 2) = *(HI + (*(Indice + 2)))* (*(MeasureG + (*(Indice + 2))));

      IntegralFlou = *(Integral + 0) + *(Integral + 1) + *(Integral + 2);
      p.PutGrayPixel(OutputImage, i, j, IntegralFlou);
    }
  }

  free(HTexturePixel);
  free(DeltaOhtaPixel);
  free(Indice);
  free(HI);
  free(X);
  free(XiXj);
  free(Integral);
}

void FuzzyUtils::FuzzyMeasureG(float g1, float g2, float g3, float *G)
{
  *(G + 0) = g1;
  *(G + 1) = g2;
  *(G + 2) = g3;
}

void FuzzyUtils::Trier(float* g, int n, int* index)
{
  // Cette fonction trie un vecteur g par ordre croissant et
  // sort egalement l'indice des elements selon le trie dans le vecteur "index" supposé initialisé par des valeurs de 1 a n

  float t;
  int r, a, b;

  for (a = 1; a <= n; a++)
  {
    for (b = n - 1; b >= a; b--)
      if (*(g + b - 1) < (*(g + b)))
      {
        // ordre croissant des élements
        t = *(g + b - 1);
        *(g + b - 1) = *(g + b);
        *(g + b) = t;

        // ordre des indices des élements du vecteur g
        r = *(index + b - 1);
        *(index + b - 1) = *(index + b);
        *(index + b) = r;
      }
  }
}

float FuzzyUtils::min(float *a, float *b)
{
  float min = 0;

  if (*a >= (*b))
    min = *b;
  else
    min = *a;

  return min;
}

float FuzzyUtils::max(float* g, int n)
{
  float max = 0;

  for (int i = 0; i < n; i++)
  {
    if (*(g + i) >= max)
      max = *(g + i);
  }

  return max;
}

void FuzzyUtils::gDeDeux(float* a, float* b, float* lambda)
{
  float* c = (float*)malloc(1 * sizeof(float));
  *c = *a + (*b) + (*lambda) * (*a) * (*b);
}

//void FuzzyUtils::getLambda(float* g)
//{
//  float a, b;
//  float* lambda = (float*)malloc(1 * sizeof(float));
//
//  a = (*(g + 0) * (*(g + 1)) + (*(g + 1)) * (*(g + 2)) + (*(g + 0)) * (*(g + 2)));
//  *lambda = -(*(g + 0) * (*(g + 1)) + (*(g + 1)) * (*(g + 2)) + (*(g + 0)) * (*(g + 2))) / (*(g + 0) * (*(g + 1)) * (*(g + 2)));
//  b = (*(g + 0) * (*(g + 1)) * (*(g + 2)));
//
//  //printf("\na:%f",a);
//  //printf("\nb:%f",b);
//  //printf("\nlambda:%f", *lambda);
//
//  free(lambda);
//}

void FuzzyUtils::AdaptativeSelectiveBackgroundModelUpdate(IplImage* CurrentImage, IplImage* BGImage, IplImage* OutputImage, IplImage* Integral, float seuil, float alpha)
{
  PixelUtils p;

  float beta = 0.0;
  float* CurentImagePixel = (float*)malloc(3 * sizeof(float));
  float* BGImagePixel = (float*)malloc(3 * sizeof(float));
  float* OutputImagePixel = (float*)malloc(3 * sizeof(float));
  float* IntegralImagePixel = (float*)malloc(1 * sizeof(float));
  float *Maximum = (float*)malloc(1 * sizeof(float));
  float *Minimum = (float*)malloc(1 * sizeof(float));

  p.ForegroundMaximum(Integral, Maximum, 1);
  p.ForegroundMinimum(Integral, Minimum, 1);

  for (int i = 0; i < CurrentImage->width; i++)
  {
    for (int j = 0; j < CurrentImage->height; j++)
    {
      p.GetPixel(CurrentImage, i, j, CurentImagePixel);
      p.GetPixel(BGImage, i, j, BGImagePixel);
      p.GetGrayPixel(Integral, i, j, IntegralImagePixel);

      beta = 1 - ((*IntegralImagePixel) - ((*Minimum / (*Minimum - *Maximum)) * (*IntegralImagePixel) - (*Minimum * (*Maximum) / (*Minimum - *Maximum))));

      for (int k = 0; k < 3; k++)
        *(OutputImagePixel + k) = beta * (*(BGImagePixel + k)) + (1 - beta) * (alpha * (*(CurentImagePixel + k)) + (1 - alpha) * (*(BGImagePixel + k)));

      p.PutPixel(OutputImage, i, j, OutputImagePixel);
    }
  }

  free(CurentImagePixel);
  free(BGImagePixel);
  free(OutputImagePixel);
  free(IntegralImagePixel);
  free(Maximum);
  free(Minimum);
}

--#-- END ./bgslibrary/tools/FuzzyUtils.cpp --#--

--#-- START ./bgslibrary/PreProcessor.cpp --#--
#include "PreProcessor.h"

namespace bgslibrary
{
  PreProcessor::PreProcessor() : 
    firstTime(true), equalizeHist(false), gaussianBlur(false)
  {
    debug_construction(PreProcessor);
    initLoadSaveConfig(quote(PreProcessor));
  }

  PreProcessor::~PreProcessor() {
    debug_destruction(PreProcessor);
  }

  void PreProcessor::setEqualizeHist(bool value) {
    equalizeHist = value;
  }

  void PreProcessor::setGaussianBlur(bool value) {
    gaussianBlur = value;
  }

  cv::Mat PreProcessor::getGrayScale() {
    return img_gray.clone();
  }

  void PreProcessor::process(const cv::Mat &img_input, cv::Mat &img_output)
  {
    if (img_input.empty())
      return;

    img_input.copyTo(img_output);

    // Converts image from one color space to another
    // http://opencv.willowgarage.com/documentation/cpp/miscellaneous_image_transformations.html#cv-cvtcolor
    cv::cvtColor(img_input, img_gray, CV_BGR2GRAY);
    //img_gray.copyTo(img_output);

    // Equalizes the histogram of a grayscale image
    // http://opencv.willowgarage.com/documentation/cpp/histograms.html#cv-equalizehist
    if (equalizeHist)
      cv::equalizeHist(img_output, img_output);

    // Smoothes image using a Gaussian filter
    // http://opencv.willowgarage.com/documentation/cpp/imgproc_image_filtering.html#GaussianBlur
    if (gaussianBlur)
      cv::GaussianBlur(img_output, img_output, cv::Size(7, 7), 1.5);

    if (enableShow)
      cv::imshow("PreProcessor", img_output);

    firstTime = false;
  }
  /*
  void PreProcessor::rotate(const cv::Mat &img_input, cv::Mat &img_output, float angle)
  {
    IplImage* image = new IplImage(img_input);

    //IplImage *rotatedImage = cvCreateImage(cvSize(480,320), IPL_DEPTH_8U, image->nChannels);
    //IplImage *rotatedImage = cvCreateImage(cvSize(image->width,image->height), IPL_DEPTH_8U, image->nChannels);
    IplImage* rotatedImage = cvCreateImage(cvSize(image->height, image->width), IPL_DEPTH_8U, image->nChannels);

    CvPoint2D32f center;
    //center.x = 160;
    //center.y = 160;
    center.x = (image->height / 2);
    center.y = (image->width / 2);

    CvMat* mapMatrix = cvCreateMat(2, 3, CV_32FC1);

    cv2DRotationMatrix(center, angle, 1.0, mapMatrix);
    cvWarpAffine(image, rotatedImage, mapMatrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0));

    cv::Mat img_rot = cv::cvarrToMat(rotatedImage);
    img_rot.copyTo(img_output);

    cvReleaseImage(&image);
    cvReleaseImage(&rotatedImage);
    cvReleaseMat(&mapMatrix);
  }
  */
  void PreProcessor::applyCanny(const cv::Mat &img_input, cv::Mat &img_output)
  {
    if (img_input.empty())
      return;

    //------------------------------------------------------------------
    // Canny
    // Finds edges in an image using Canny algorithm.
    // http://opencv.willowgarage.com/documentation/cpp/imgproc_feature_detection.html#cv-canny
    //------------------------------------------------------------------

    cv::Mat img_canny;
    cv::Canny(
      img_input, // image � Single-channel 8-bit input image
      img_canny,  // edges � The output edge map. It will have the same size and the same type as image
      100,       // threshold1 � The first threshold for the hysteresis procedure
      200);      // threshold2 � The second threshold for the hysteresis procedure
    cv::threshold(img_canny, img_canny, 128, 255, cv::THRESH_BINARY_INV);

    img_canny.copyTo(img_output);
  }

  void PreProcessor::save_config(cv::FileStorage &fs) {
    fs << "equalizeHist" << equalizeHist;
    fs << "gaussianBlur" << gaussianBlur;
    fs << "enableShow" << enableShow;
  }

  void PreProcessor::load_config(cv::FileStorage &fs) {
    fs["equalizeHist"] >> equalizeHist;
    fs["gaussianBlur"] >> gaussianBlur;
    fs["enableShow"] >> enableShow;
  }
}

--#-- END ./bgslibrary/PreProcessor.cpp --#--

--#-- START ./bgslibrary/VideoCapture.cpp --#--
#include "VideoCapture.h"

#if  CV_MAJOR_VERSION >= 4
//#define CV_CAP_PROP_POS_FRAMES cv::CAP_PROP_POS_FRAMES
//#define CV_CAP_PROP_FRAME_COUNT cv::CAP_PROP_FRAME_COUNT
#define CV_CAP_PROP_FPS cv::CAP_PROP_FPS
#define CV_EVENT_LBUTTONDOWN cv::EVENT_LBUTTONDOWN
#define CV_EVENT_MOUSEMOVE cv::EVENT_MOUSEMOVE
#define cvSetMouseCallback cv::setMouseCallback
#endif

namespace bgslibrary
{
  namespace VC_ROI
  {
    cv::Mat img_input1;
    cv::Mat img_input2;
    int roi_x0 = 0;
    int roi_y0 = 0;
    int roi_x1 = 0;
    int roi_y1 = 0;
    int numOfRec = 0;
    bool startDraw = false;
    bool roi_defined = false;
    bool use_roi = true;
    bool disable_event = false;

    void reset(void)
    {
      disable_event = false;
      startDraw = false;
    }

    void VideoCapture_on_mouse(int evt, int x, int y, int flag, void* param)
    {
      if (use_roi == false || disable_event == true)
        return;
      
      if (evt == CV_EVENT_LBUTTONDOWN)
      {
        if (!startDraw)
        {
          roi_x0 = x;
          roi_y0 = y;
          startDraw = 1;
        }
        else
        {
          roi_x1 = x;
          roi_y1 = y;
          startDraw = 0;
          roi_defined = true;
          disable_event = true;
        }
      }

      if (evt == CV_EVENT_MOUSEMOVE && startDraw)
      {
        //redraw ROI selection
        img_input1.copyTo(img_input2);
        cv::Point pt1(roi_x0, roi_y0);
        cv::Point pt2(roi_x1, roi_y1);
        cv::rectangle(img_input2, pt1, pt2, cv::Scalar(255, 0, 0));
        cv::imshow("Input", img_input2);
        //startDraw = false;
        //disable_event = true;
      }
    }
  }

  VideoCapture::VideoCapture() :
    key(0), start_time(0), delta_time(0), freq(0),
    fps(0), frameNumber(0), stopAt(0), useCamera(false),
    cameraIndex(0), useVideo(false), input_resize_percent(100),
    showOutput(true), showFPS(true), enableFlip(false)
  {
    debug_construction(VideoCapture);
    initLoadSaveConfig(quote(VideoCapture));
  }

  VideoCapture::~VideoCapture() {
    debug_destruction(VideoCapture);
  }

  void VideoCapture::setFrameProcessor(const std::shared_ptr<IFrameProcessor> &_frameProcessor)
  {
    frameProcessor = _frameProcessor;
  }

  void VideoCapture::setCamera(int _index)
  {
    useCamera = true;
    cameraIndex = _index;
    useVideo = false;
  }

  void VideoCapture::setUpCamera()
  {
    std::cout << "Camera index:" << cameraIndex << std::endl;
    capture.open(cameraIndex);

    if (!capture.isOpened())
      std::cerr << "Cannot initialize webcam!\n" << std::endl;
  }

  void VideoCapture::setVideo(std::string _filename)
  {
    useVideo = true;
    videoFileName = _filename;
    useCamera = false;
  }

  void VideoCapture::setUpVideo()
  {
    std::cout << "Openning: " << videoFileName << std::endl;
    capture.open(videoFileName.c_str());

    if (!capture.isOpened())
      std::cerr << "Cannot open video file " << videoFileName << std::endl;
    else
      std::cout << "OK" << std::endl;
  }

  void VideoCapture::start()
  {
    if (useCamera) setUpCamera();
    if (useVideo)  setUpVideo();
    //if (!capture)  std::cerr << "Capture error..." << std::endl;

    //using namespace std::chrono_literals;
    do
    {
      capture >> frame;
      if (frame.empty())
      {
        std::cout << "Frame is not ready" << std::endl;
        std::string dummy;
        std::cout << "Enter to continue..." << std::endl;
        std::getline(std::cin, dummy);
        //cv::waitKey(1000);
        //std::this_thread::sleep_for(1s);
      }
      else
        break;
    } while (1);

    int input_fps = capture.get(CV_CAP_PROP_FPS);
    std::cout << "input->fps:" << input_fps << std::endl;
    std::cout << "input->width:" << frame.size().width << std::endl;
    std::cout << "input->height:" << frame.size().height << std::endl;

    if (input_fps > 0)
      loopDelay = (1. / input_fps)*1000.;
    std::cout << "loopDelay:" << loopDelay << std::endl;

    std::cout << "Press 'ESC' to stop..." << std::endl;
    do
    {
      frameNumber++;

      capture >> frame;
      if (frame.empty()) break;

      cv::resize(frame, frame, cv::Size(), input_resize_percent/100., input_resize_percent / 100.);

      if (firstTime && input_resize_percent != 100)
      {
        std::cout << "Resized to:" << std::endl;
        std::cout << "input->width:" << frame.size().width << std::endl;
        std::cout << "input->height:" << frame.size().height << std::endl;
      }
      
      //if (enableFlip)
      //  cvFlip(frame, frame, 0);

      if (VC_ROI::use_roi == true && VC_ROI::roi_defined == false && firstTime == true)
      {
        VC_ROI::reset();

        do
        {
          cv::Mat img_input;
          frame.copyTo(img_input);

          if (showOutput)
          {
            cv::imshow("Input", img_input);

            std::cout << "Set ROI (press ESC to skip)" << std::endl;
            //VC_ROI::img_input1 = new IplImage(img_input);
            cvSetMouseCallback("Input", VC_ROI::VideoCapture_on_mouse, NULL);
            key = cv::waitKey(0);
            //delete VC_ROI::img_input1;
          }
          else
            key = KEY_ESC;

          if (key == KEY_ESC)
          {
            std::cout << "ROI disabled" << std::endl;
            VC_ROI::reset();
            VC_ROI::use_roi = false;
            break;
          }

          if (VC_ROI::roi_defined)
          {
            std::cout << "ROI defined (" << VC_ROI::roi_x0 << "," << VC_ROI::roi_y0 << "," << VC_ROI::roi_x1 << "," << VC_ROI::roi_y1 << ")" << std::endl;
            break;
          }
          else
            std::cout << "ROI undefined" << std::endl;

        } while (1);
      }

      if (VC_ROI::use_roi == true && VC_ROI::roi_defined == true)
      {
        cv::Rect roi(VC_ROI::roi_x0, VC_ROI::roi_y0, VC_ROI::roi_x1 - VC_ROI::roi_x0, VC_ROI::roi_y1 - VC_ROI::roi_y0);
        frame = frame(roi);
      }

      cv::Mat img_input;
      frame.copyTo(img_input);

      start_time = cv::getTickCount();
      frameProcessor->process(img_input);
      delta_time = cv::getTickCount() - start_time;
      freq = cv::getTickFrequency();
      fps = freq / delta_time;
      //std::cout << "FPS: " << fps << std::endl;
      
      if (showFPS)
        cv::putText(img_input,
              "FPS: " + std::to_string(fps),
              cv::Point(10,15), // Coordinates
              cv::FONT_HERSHEY_COMPLEX_SMALL, // Font
              1.0, // Scale. 2.0 = 2x bigger
              cv::Scalar(0,0,255), // BGR Color
              1); // Line Thickness (Optional)

      if (showOutput)
        cv::imshow("Input", img_input);

      //cvResetImageROI(frame);

      key = cv::waitKey(loopDelay);
      //std::cout << "key: " << key << std::endl;

      if (key == KEY_SPACE)
        key = cv::waitKey(0);

      if (key == KEY_ESC)
        break;

      if (stopAt > 0 && stopAt == frameNumber)
        key = cv::waitKey(0);

      firstTime = false;
    } while (1);

    capture.release();
  }

  void VideoCapture::save_config(cv::FileStorage &fs) {
    fs << "stopAt" << stopAt;
    fs << "input_resize_percent" << input_resize_percent;
    fs << "enableFlip" << enableFlip;
    fs << "use_roi" << VC_ROI::use_roi;
    fs << "roi_defined" << VC_ROI::roi_defined;
    fs << "roi_x0" << VC_ROI::roi_x0;
    fs << "roi_y0" << VC_ROI::roi_y0;
    fs << "roi_x1" << VC_ROI::roi_x1;
    fs << "roi_y1" << VC_ROI::roi_y1;
    fs << "showFPS" << showFPS;
    fs << "showOutput" << showOutput;
  }

  void VideoCapture::load_config(cv::FileStorage &fs) {
    fs["stopAt"] >> stopAt;
    fs["input_resize_percent"] >> input_resize_percent;
    fs["enableFlip"] >> enableFlip;
    fs["use_roi"] >> VC_ROI::use_roi;
    fs["roi_defined"] >> VC_ROI::roi_defined;
    fs["roi_x0"] >> VC_ROI::roi_x0;
    fs["roi_y0"] >> VC_ROI::roi_y0;
    fs["roi_x1"] >> VC_ROI::roi_x1;
    fs["roi_y1"] >> VC_ROI::roi_y1;
    fs["showFPS"] >> showFPS;
    fs["showOutput"] >> showOutput;
  }
}

--#-- END ./bgslibrary/VideoCapture.cpp --#--

--#-- START ./bgslibrary/algorithms/LBFuzzyGaussian.cpp --#--
#include "LBFuzzyGaussian.h"

using namespace bgslibrary::algorithms;

LBFuzzyGaussian::LBFuzzyGaussian() :
  IBGS(quote(LBFuzzyGaussian)),
  sensitivity(72), bgThreshold(162), 
  learningRate(49), noiseVariance(195)
{
  debug_construction(LBFuzzyGaussian);
  initLoadSaveConfig(algorithmName);
}

LBFuzzyGaussian::~LBFuzzyGaussian() {
  debug_destruction(LBFuzzyGaussian);
  delete m_pBGModel;
}

void LBFuzzyGaussian::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  IplImage* frame = cvCloneImage(&_frame);

  if (firstTime) {
    int w = img_input.size().width;
    int h = img_input.size().height;

    m_pBGModel = new lb::BGModelFuzzyGauss(w, h);
    m_pBGModel->InitModel(frame);
  }

  m_pBGModel->setBGModelParameter(0, sensitivity);
  m_pBGModel->setBGModelParameter(1, bgThreshold);
  m_pBGModel->setBGModelParameter(2, learningRate);
  m_pBGModel->setBGModelParameter(3, noiseVariance);

  m_pBGModel->UpdateModel(frame);

  img_foreground = cv::cvarrToMat(m_pBGModel->GetFG());
  img_background = cv::cvarrToMat(m_pBGModel->GetBG());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&frame);

  firstTime = false;
}

void LBFuzzyGaussian::save_config(cv::FileStorage &fs) {
  fs << "sensitivity" << sensitivity;
  fs << "bgThreshold" << bgThreshold;
  fs << "learningRate" << learningRate;
  fs << "noiseVariance" << noiseVariance;
  fs << "showOutput" << showOutput;
}

void LBFuzzyGaussian::load_config(cv::FileStorage &fs) {
  fs["sensitivity"] >> sensitivity;
  fs["bgThreshold"] >> bgThreshold;
  fs["learningRate"] >> learningRate;
  fs["noiseVariance"] >> noiseVariance;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/LBFuzzyGaussian.cpp --#--

--#-- START ./bgslibrary/algorithms/KDE.cpp --#--
#include "KDE.h"

using namespace bgslibrary::algorithms;

KDE::KDE() :
  IBGS(quote(KDE)),
  SequenceLength(50), TimeWindowSize(100), 
  SDEstimationFlag(1), lUseColorRatiosFlag(1),
  th(10e-8), alpha(0.3), framesToLearn(10), frameNumber(0)
{
  debug_construction(KDE);
  initLoadSaveConfig(algorithmName);
  p = new kde::NPBGSubtractor;
}

KDE::~KDE() {
  debug_destruction(KDE);
  delete FGImage;
  delete p;
}

void KDE::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    rows = img_input.size().height;
    cols = img_input.size().width;
    color_channels = img_input.channels();

    // SequenceLength: number of samples for each pixel.
    // TimeWindowSize: Time window for sampling. for example in the call above, the bg will sample 50 points out of 100 frames.
    // this rate will affect how fast the model adapt.
    // SDEstimationFlag: True means to estimate suitable kernel bandwidth to each pixel, False uses a default value.
    // lUseColorRatiosFlag: True means use normalized RGB for color (recommended.)
    p->Intialize(rows, cols, color_channels, SequenceLength, TimeWindowSize, SDEstimationFlag, lUseColorRatiosFlag);
    // th: 0-1 is the probability threshold for a pixel to be a foregroud. typically make it small as 10e-8. the smaller the value the less false positive and more false negative.
    // alpha: 0-1, for color. typically set to 0.3. this affect shadow suppression.
    p->SetThresholds(th, alpha);

    FGImage = new unsigned char[rows*cols];
    //FilteredFGImage = new unsigned char[rows*cols];
    FilteredFGImage = 0;
    DisplayBuffers = 0;

    img_foreground = cv::Mat::zeros(img_input.size(), CV_8UC1);
    img_background = cv::Mat::zeros(img_input.size(), img_input.type());

    frameNumber = 0;
    firstTime = false;
  }

  // Stores the first N frames to build the background model
  if (frameNumber < framesToLearn) {
    p->AddFrame(img_input.data);
    frameNumber++;
  }
  else {
    // Build the background model with first 10 frames
    if (frameNumber == framesToLearn) {
      p->Estimation();
      frameNumber++;
    }

    // Now, we can subtract the background
    ((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.
    ((kde::NPBGSubtractor*)p)->Update(FGImage);

    img_foreground.data = FGImage;
  }

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
}

void KDE::save_config(cv::FileStorage &fs) {
  fs << "framesToLearn" << framesToLearn;
  fs << "SequenceLength" << SequenceLength;
  fs << "TimeWindowSize" << TimeWindowSize;
  fs << "SDEstimationFlag" << SDEstimationFlag;
  fs << "lUseColorRatiosFlag" << lUseColorRatiosFlag;
  fs << "th" << th;
  fs << "alpha" << alpha;
  fs << "showOutput" << showOutput;
}

void KDE::load_config(cv::FileStorage &fs) {
  fs["framesToLearn"] >> framesToLearn;
  fs["SequenceLength"] >> SequenceLength;
  fs["TimeWindowSize"] >> TimeWindowSize;
  fs["SDEstimationFlag"] >> SDEstimationFlag;
  fs["lUseColorRatiosFlag"] >> lUseColorRatiosFlag;
  fs["th"] >> th;
  fs["alpha"] >> alpha;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/KDE.cpp --#--

--#-- START ./bgslibrary/algorithms/FuzzyChoquetIntegral.cpp --#--
#include "FuzzyChoquetIntegral.h"

using namespace bgslibrary::algorithms;

FuzzyChoquetIntegral::FuzzyChoquetIntegral() :
  IBGS(quote(FuzzyChoquetIntegral)),
  frameNumber(0), framesToLearn(10), alphaLearn(0.1),
  alphaUpdate(0.01), colorSpace(1), option(2),
  smooth(true), threshold(0.67)
{
  debug_construction(FuzzyChoquetIntegral);
  initLoadSaveConfig(algorithmName);
}

FuzzyChoquetIntegral::~FuzzyChoquetIntegral() {
  debug_destruction(FuzzyChoquetIntegral);
}

void FuzzyChoquetIntegral::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  cv::Mat img_input_f3(img_input.size(), CV_32F);
  img_input.convertTo(img_input_f3, CV_32F, 1. / 255.);

  if (firstTime) {
    std::cout << algorithmName + " parameters:" << std::endl;

    std::string colorSpaceName = "";
    switch (colorSpace)
    {
    case 1: colorSpaceName = "RGB";  break;
    case 2: colorSpaceName = "OHTA"; break;
    case 3: colorSpaceName = "HSV";  break;
    case 4: colorSpaceName = "YCrCb"; break;
    }
    std::cout << "Color space: " << colorSpaceName << std::endl;

    if (option == 1)
      std::cout << "Fuzzing by 3 color components" << std::endl;
    if (option == 2)
      std::cout << "Fuzzing by 2 color components + 1 texture component" << std::endl;
  }

  if (frameNumber <= framesToLearn) {
    if (frameNumber == 0)
      std::cout << algorithmName + " initializing background model by adaptive learning" << std::endl;

    if (img_background_f3.empty())
      img_input_f3.copyTo(img_background_f3);
    else
      img_background_f3 = alphaLearn*img_input_f3 + (1 - alphaLearn)*img_background_f3;

    double minVal = 0., maxVal = 1.;
    img_background_f3.convertTo(img_background, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    img_background.copyTo(img_bgmodel);

    img_foreground = cv::Mat::zeros(img_input.size(), img_input.type());
    img_foreground.copyTo(img_output);

#ifndef MEX_COMPILE_FLAG
    if (showOutput)
      cv::imshow(algorithmName + "_BG", img_background);
#endif
  }
  else
  {
    cv::Mat img_input_f1;
    cv::cvtColor(img_input_f3, img_input_f1, CV_BGR2GRAY);

    cv::Mat img_background_f1;
    cv::cvtColor(img_background_f3, img_background_f1, CV_BGR2GRAY);

    IplImage _input_f3 = cvIplImage(img_input_f3);
    IplImage* input_f3 = cvCloneImage(&_input_f3);

    IplImage _input_f1 = cvIplImage(img_input_f1);
    IplImage* input_f1 = cvCloneImage(&_input_f1);

    IplImage _background_f3 = cvIplImage(img_background_f3);
    IplImage* background_f3 = cvCloneImage(&_background_f3);

    IplImage _background_f1 = cvIplImage(img_background_f1);
    IplImage* background_f1 = cvCloneImage(&_background_f1);

    IplImage* lbp_input_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1);
    cvSetZero(lbp_input_f1);
    fu.LBP(input_f1, lbp_input_f1);

    IplImage* lbp_background_f1 = cvCreateImage(cvSize(background_f1->width, background_f1->height), IPL_DEPTH_32F, 1);
    cvSetZero(lbp_background_f1);
    fu.LBP(background_f1, lbp_background_f1);

    IplImage* sim_texture_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1);
    fu.SimilarityDegreesImage(lbp_input_f1, lbp_background_f1, sim_texture_f1, 1, colorSpace);

    IplImage* sim_color_f3 = cvCreateImage(cvSize(input_f3->width, input_f3->height), IPL_DEPTH_32F, 3);
    fu.SimilarityDegreesImage(input_f3, background_f3, sim_color_f3, 3, colorSpace);

    float* measureG = (float*)malloc(3 * (sizeof(float)));
    IplImage* integral_choquet_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1);

    // 3 color components
    if (option == 1) {
      fu.FuzzyMeasureG(0.4f, 0.3f, 0.3f, measureG);
      fu.getFuzzyIntegralChoquet(sim_texture_f1, sim_color_f3, option, measureG, integral_choquet_f1);
    }

    // 2 color components + 1 texture component
    if (option == 2) {
      fu.FuzzyMeasureG(0.6f, 0.3f, 0.1f, measureG);
      fu.getFuzzyIntegralChoquet(sim_texture_f1, sim_color_f3, option, measureG, integral_choquet_f1);
    }

    free(measureG);
    cv::Mat img_integral_choquet_f1 = cv::cvarrToMat(integral_choquet_f1);

    if (smooth)
      cv::medianBlur(img_integral_choquet_f1, img_integral_choquet_f1, 3);

    cv::Mat img_foreground_f1(img_input.size(), CV_32F);
    cv::threshold(img_integral_choquet_f1, img_foreground_f1, threshold, 255, cv::THRESH_BINARY_INV);

    //cv::Mat img_foreground_u1(img_input.size(), CV_8U);
    double minVal = 0., maxVal = 1.;
    img_foreground_f1.convertTo(img_foreground, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    img_foreground.copyTo(img_output);

    //cv::Mat img_background_u3(img_input.size(), CV_8U);
    //double minVal = 0., maxVal = 1.;
    img_background_f3.convertTo(img_background, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    img_background.copyTo(img_bgmodel);

#ifndef MEX_COMPILE_FLAG
    if (showOutput) {
      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);
    }
#endif

    if (frameNumber == (framesToLearn + 1))
      std::cout << algorithmName + " updating background model by adaptive-selective learning" << std::endl;

    IplImage* updated_background_f3 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 3);
    cvSetZero(updated_background_f3);
    fu.AdaptativeSelectiveBackgroundModelUpdate(input_f3, background_f3, updated_background_f3, integral_choquet_f1, threshold, alphaUpdate);
    cv::Mat img_updated_background_f3 = cv::cvarrToMat(updated_background_f3);
    img_updated_background_f3.copyTo(img_background_f3);

    cvReleaseImage(&input_f3);
    cvReleaseImage(&input_f1);
    cvReleaseImage(&background_f3);
    cvReleaseImage(&background_f1);
    cvReleaseImage(&lbp_input_f1);
    cvReleaseImage(&lbp_background_f1);
    cvReleaseImage(&sim_texture_f1);
    cvReleaseImage(&sim_color_f3);
    cvReleaseImage(&integral_choquet_f1);
    cvReleaseImage(&updated_background_f3);
  }

  firstTime = false;
  frameNumber++;
}

void FuzzyChoquetIntegral::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "framesToLearn" << framesToLearn;
  fs << "alphaLearn" << alphaLearn;
  fs << "alphaUpdate" << alphaUpdate;
  fs << "colorSpace" << colorSpace;
  fs << "option" << option;
  fs << "smooth" << smooth;
  fs << "showOutput" << showOutput;
}

void FuzzyChoquetIntegral::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["framesToLearn"] >> framesToLearn;
  fs["alphaLearn"] >> alphaLearn;
  fs["alphaUpdate"] >> alphaUpdate;
  fs["colorSpace"] >> colorSpace;
  fs["option"] >> option;
  fs["smooth"] >> smooth;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/FuzzyChoquetIntegral.cpp --#--

--#-- START ./bgslibrary/algorithms/algorithms.cpp --#--
#include "algorithms.h"

--#-- END ./bgslibrary/algorithms/algorithms.cpp --#--

--#-- START ./bgslibrary/algorithms/T2FGMM_UM.cpp --#--
#include "T2FGMM_UM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

T2FGMM_UM::T2FGMM_UM() :
  IBGS(quote(T2FGMM_UM)),
  frameNumber(0), threshold(9.0), alpha(0.01), 
  km(1.5f), kv(0.6f), gaussians(3)
{
  debug_construction(T2FGMM_UM);
  initLoadSaveConfig(algorithmName);
}

T2FGMM_UM::~T2FGMM_UM() {
  debug_destruction(T2FGMM_UM);
}

void T2FGMM_UM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold;
    params.HighThreshold() = 2 * params.LowThreshold();
    params.Alpha() = alpha;
    params.MaxModes() = gaussians;
    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

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void T2FGMM_UM::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "km" << km;
  fs << "kv" << kv;
  fs << "gaussians" << gaussians;
  fs << "showOutput" << showOutput;
}

void T2FGMM_UM::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["km"] >> km;
  fs["kv"] >> kv;
  fs["gaussians"] >> gaussians;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/T2FGMM_UM.cpp --#--

--#-- START ./bgslibrary/algorithms/SuBSENSE.cpp --#--
#include "SuBSENSE.h"

using namespace bgslibrary::algorithms;

SuBSENSE::SuBSENSE() :
  IBGS(quote(SuBSENSE)),
  pSubsense(0),
  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);
}

SuBSENSE::~SuBSENSE() {
  debug_destruction(SuBSENSE);
  if (pSubsense)
    delete pSubsense;
}

void SuBSENSE::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    pSubsense = new lbsp::BackgroundSubtractorSuBSENSE(
      fRelLBSPThreshold, nDescDistThresholdOffset, nMinColorDistThreshold,
      nBGSamples, nRequiredBGSamples, nSamplesForMovingAvgs);

    pSubsense->initialize(img_input, cv::Mat(img_input.size(), CV_8UC1, cv::Scalar_<uchar>(255)));
    firstTime = false;
  }

  pSubsense->apply(img_input, img_foreground);
  pSubsense->getBackgroundImage(img_background);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
}

void SuBSENSE::save_config(cv::FileStorage &fs) {
  fs << "fRelLBSPThreshold" << fRelLBSPThreshold;
  fs << "nDescDistThresholdOffset" << nDescDistThresholdOffset;
  fs << "nMinColorDistThreshold" << nMinColorDistThreshold;
  fs << "nBGSamples" << nBGSamples;
  fs << "nRequiredBGSamples" << nRequiredBGSamples;
  fs << "nSamplesForMovingAvgs" << nSamplesForMovingAvgs;
  fs << "showOutput" << showOutput;
}

void SuBSENSE::load_config(cv::FileStorage &fs) {
  fs["fRelLBSPThreshold"] >> fRelLBSPThreshold;
  fs["nDescDistThresholdOffset"] >> nDescDistThresholdOffset;
  fs["nMinColorDistThreshold"] >> nMinColorDistThreshold;
  fs["nBGSamples"] >> nBGSamples;
  fs["nRequiredBGSamples"] >> nRequiredBGSamples;
  fs["nSamplesForMovingAvgs"] >> nSamplesForMovingAvgs;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/SuBSENSE.cpp --#--

--#-- START ./bgslibrary/algorithms/DPTexture.cpp --#--
#include "DPTexture.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPTexture::DPTexture() :
  IBGS(quote(DPTexture))
  //, enableFiltering(true)
{
  debug_construction(DPTexture);
  initLoadSaveConfig(algorithmName);
}

DPTexture::~DPTexture() {
  debug_destruction(DPTexture);
  delete[] bgModel; // ~10Kb (25.708-15.968)
  delete[] modeArray;
  delete[] curTextureHist; // ~10Kb (16-6.396)
  //cvReleaseStructuringElement(&dilateElement);
  //cvReleaseStructuringElement(&erodeElement);
  image.ReleaseImage();
  fgMask.ReleaseImage();
  tempMask.ReleaseImage();
  texture.ReleaseImage();
}

void DPTexture::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame = cvCloneImage(&_frame);

  width = img_input.size().width;
  height = img_input.size().height;
  size = width * height;

  // input image
  image = cvCreateImage(cvSize(width, height), 8, 3);
  cvCopy(frame, image.Ptr());

  if (firstTime) {
    // foreground masks
    fgMask = cvCreateImage(cvSize(width, height), 8, 1);
    tempMask = cvCreateImage(cvSize(width, height), 8, 1);
    cvZero(fgMask.Ptr());
    cvZero(tempMask.Ptr());

    // create background model
    bgModel = new dp::TextureArray[size];
    texture = cvCreateImage(cvSize(width, height), 8, 3);
    cvZero(texture.Ptr());
    modeArray = new unsigned char[size];
    curTextureHist = new dp::TextureHistogram[size];

    // initialize background model
    bgs.LBP(image, texture);
    bgs.Histogram(texture, curTextureHist);
    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 < 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];
          }
        }
      }
    }

    //dilateElement = cvCreateStructuringElementEx(7, 7, 3, 3,	CV_SHAPE_RECT);
    //erodeElement = cvCreateStructuringElementEx(3, 3, 1, 1,	CV_SHAPE_RECT);
    firstTime = false;
  }
  else {
    // cvCopy(frame, image.Ptr());
    // perform background subtraction
    bgs.LBP(image, texture);
    bgs.Histogram(texture, curTextureHist);
    bgs.BgsCompare(bgModel, curTextureHist, modeArray, dp::THRESHOLD, fgMask);
  }

  //if(enableFiltering)
  //{
  //  // size filtering
  //  ConnectedComponents cc;
  //  CBlobResult largeBlobs;
  //  cc.SetImage(&fgMask);
  //  cc.Find(127);
  //  cc.FilterMinArea(size/30, largeBlobs);
  //  fgMask.Clear();
  //  cc.ColorBlobs(fgMask.Ptr(), largeBlobs, CV_RGB(255,255,255));
  //  largeBlobs.ClearBlobs();

  //  // morphological operators
  //  cvDilate(fgMask.Ptr(), fgMask.Ptr(), dilateElement, 1);
  //  cvErode(fgMask.Ptr(), fgMask.Ptr(), erodeElement, 1);
  //}

  img_foreground = cv::cvarrToMat(fgMask.Ptr());
  //img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  // update background subtraction
  bgs.UpdateModel(fgMask, bgModel, curTextureHist, modeArray);

  // free memory
  image.ReleaseImage();
  cvReleaseImage(&frame);
}

void DPTexture::save_config(cv::FileStorage &fs) {
  //fs << "alpha" << alpha;
  //fs << "enableFiltering" << enableFiltering;
  fs << "showOutput" << showOutput;
}

void DPTexture::load_config(cv::FileStorage &fs) {
  //fs["alpha"] >> alpha;
  //fs["enableFiltering"] >> enableFiltering;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPTexture.cpp --#--

--#-- START ./bgslibrary/algorithms/SigmaDelta/sdLaMa091.cpp --#--
#include "sdLaMa091.h"

//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 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;
      }

      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

        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->N = DEFAULT_N;
        sdLaMa091->Vmin = DEFAULT_VMIN;
        sdLaMa091->Vmax = DEFAULT_VMAX;

        sdLaMa091->Mt = NULL;
        sdLaMa091->Ot = NULL;
        sdLaMa091->Vt = NULL;

        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;
        }

        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;
        }

        if (stride < width) {
          outputError("Cannot allocate ressources for a stride lower than the width");
          return EXIT_FAILURE;
        }
      #endif

        sdLaMa091->imageType = C1R;

        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;

        for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) {

          for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workOt)
            *workOt = 0;


          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;


        for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) {

          for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++workVt)
            *workVt = sdLaMa091->Vmin;


          if (sdLaMa091->unusedBytes > 0)
            workVt += sdLaMa091->unusedBytes;
        }

        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;
        }

        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 

      #ifdef DEFENSIVE_PARAM
        if (amplificationFactor == 0) {
          outputError("Cannot set a parameter with a zero value");
          return EXIT_FAILURE;
        }
      #endif 

        sdLaMa091->N = amplificationFactor;

        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;

          return EXIT_FAILURE;
        }
      #endif 

        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 

      #ifdef DEFENSIVE_PARAM
        if (maximalVariance == 0) {
          outputError("Cannot set a parameter with a zero value");
          return EXIT_FAILURE;
        }
      #endif 

        sdLaMa091->Vmax = maximalVariance;

        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;

          return EXIT_FAILURE;
        }
      #endif

        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 

        sdLaMa091->Vmin = minimalVariance;

        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;

          return EXIT_FAILURE;
        }
      #endif 

        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;
        }

        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 (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->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;
        }

        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->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;


        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);
          }


          if (sdLaMa091->unusedBytes > 0) {
            workImage += sdLaMa091->unusedBytes;
            workMt += sdLaMa091->unusedBytes;
          }
        }

        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 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;
          }
        }


        workOt = sdLaMa091->Ot;
        uint8_t* workVt = sdLaMa091->Vt;


        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;

            if (*workVt < ampOt)
              ++(*workVt);
            else if (*workVt > ampOt)
              --(*workVt);

            *workVt = max(min(*workVt, sdLaMa091->Vmax), sdLaMa091->Vmin);
          }


          if (sdLaMa091->unusedBytes > 0) {
            workOt += sdLaMa091->unusedBytes;
            workVt += sdLaMa091->unusedBytes;
          }
        }


        workOt = sdLaMa091->Ot;
        workVt = sdLaMa091->Vt;


        for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) {

          for (uint32_t j = 0; j < sdLaMa091->width; ++j, ++segmentation_map,
            ++workOt, ++workVt) {

            if (*workOt < *workVt)
              *segmentation_map = BACKGROUND;
            else
              *segmentation_map = FOREGROUND;
          }


          if (sdLaMa091->unusedBytes > 0) {
            segmentation_map += sdLaMa091->unusedBytes;
            workOt += sdLaMa091->unusedBytes;
            workVt += sdLaMa091->unusedBytes;
          }
        }

        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;
        }

        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 (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->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;
        }

        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->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;


        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);
          }


          if (sdLaMa091->rgbUnusedBytes > 0) {
            workImage += sdLaMa091->rgbUnusedBytes;
            workMt += sdLaMa091->rgbUnusedBytes;
          }
        }


        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 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;
          }
        }

        workOt = sdLaMa091->Ot;
        uint8_t* workVt = sdLaMa091->Vt;


        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;

            if (*workVt < ampOt)
              ++(*workVt);
            else if (*workVt > ampOt)
              --(*workVt);

            *workVt = max(min(*workVt, sdLaMa091->Vmax), sdLaMa091->Vmin);
          }


          if (sdLaMa091->rgbUnusedBytes > 0) {
            workOt += sdLaMa091->rgbUnusedBytes;
            workVt += sdLaMa091->rgbUnusedBytes;
          }
        }

        workOt = sdLaMa091->Ot;
        workVt = sdLaMa091->Vt;


        for (uint32_t i = 0; i < sdLaMa091->numBytes; i += sdLaMa091->stride) {

          uint32_t numColor = 0;

          bool isForeground = false;


          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;
              }

              isForeground = false;
            }

            numColor = (numColor + 1) % CHANNELS;
          }


          if (sdLaMa091->rgbUnusedBytes > 0) {
            segmentation_map += sdLaMa091->rgbUnusedBytes;
            workOt += sdLaMa091->rgbUnusedBytes;
            workVt += sdLaMa091->rgbUnusedBytes;
          }
        }

        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

        if (sdLaMa091->Mt != NULL)
          free(sdLaMa091->Mt);
        if (sdLaMa091->Ot != NULL)
          free(sdLaMa091->Ot);
        if (sdLaMa091->Vt != NULL)
          free(sdLaMa091->Vt);

        free(sdLaMa091);

        return EXIT_SUCCESS;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/SigmaDelta/sdLaMa091.cpp --#--

--#-- START ./bgslibrary/algorithms/ViBe.cpp --#--
#include "ViBe.h"

using namespace bgslibrary::algorithms;
//using namespace bgslibrary::algorithms::vibe;

ViBe::ViBe() :
  IBGS(quote(ViBe)),
  //numberOfSamples(DEFAULT_NUM_SAMPLES),
  matchingThreshold(DEFAULT_MATCH_THRESH),
  matchingNumber(DEFAULT_MATCH_NUM),
  updateFactor(DEFAULT_UPDATE_FACTOR),
  model(nullptr)
{
  debug_construction(ViBe);
  initLoadSaveConfig(algorithmName);
  model = vibe::libvibeModel_Sequential_New();
}

ViBe::~ViBe() {
  debug_destruction(ViBe);
  vibe::libvibeModel_Sequential_Free(model);
}

void ViBe::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_input.empty())
    return;

  if (firstTime) {
    /* Create a buffer for the output image. */
    //img_output = cv::Mat(img_input.rows, img_input.cols, CV_8UC1);

    /* Initialization of the ViBe model. */
    vibe::libvibeModel_Sequential_AllocInit_8u_C3R(model, img_input.data, img_input.cols, img_input.rows);

    /* Sets default model values. */
    //vibe::libvibeModel_Sequential_SetNumberOfSamples(model, numberOfSamples);
    vibe::libvibeModel_Sequential_SetMatchingThreshold(model, matchingThreshold);
    vibe::libvibeModel_Sequential_SetMatchingNumber(model, matchingNumber);
    vibe::libvibeModel_Sequential_SetUpdateFactor(model, updateFactor);
  }

  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)
    cv::imshow(algorithmName + "_FG", img_output);
#endif

  firstTime = false;
}

void ViBe::save_config(cv::FileStorage &fs) {
  //fs << "numberOfSamples" << numberOfSamples;
  fs << "matchingThreshold" << matchingThreshold;
  fs << "matchingNumber" << matchingNumber;
  fs << "updateFactor" << updateFactor;
  fs << "showOutput" << showOutput;
}

void ViBe::load_config(cv::FileStorage &fs) {
  //fs["numberOfSamples"] >> numberOfSamples;
  fs["matchingThreshold"] >> matchingThreshold;
  fs["matchingNumber"] >> matchingNumber;
  fs["updateFactor"] >> updateFactor;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/ViBe.cpp --#--

--#-- START ./bgslibrary/algorithms/DPMean.cpp --#--
#include "DPMean.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPMean::DPMean() :
  IBGS(quote(DPMean)),
  frameNumber(0), threshold(2700),
  alpha(1e-6f), learningFrames(30)
{
  debug_construction(DPMean);
  initLoadSaveConfig(algorithmName);
}

DPMean::~DPMean() {
  debug_destruction(DPMean);
}

void DPMean::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold; //3*30*30; // 2700
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    //params.Alpha() = 1e-6f;
    params.Alpha() = alpha;
    params.LearningFrames() = learningFrames;//30;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPMean::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "learningFrames" << learningFrames;
  fs << "showOutput" << showOutput;
}

void DPMean::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["learningFrames"] >> learningFrames;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPMean.cpp --#--

--#-- START ./bgslibrary/algorithms/T2F/T2FGMM.cpp --#--
#include "T2FGMM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

//using namespace bgslibrary::algorithms::dp;

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      int compareT2FGMM(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;
      }

      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);

        // Factor control for the T2FGMM-UM [0,3]
        //km = (float) 1.5;
        km = (float)m_params.KM();

        // Factor control for the T2FGMM-UV [0.3,1]
        //kv = (float) 0.6;
        kv = (float)m_params.KV();
      }

      RgbImage* T2FGMM::Background()
      {
        return &m_background;
      }

      void T2FGMM::InitModel(const RgbImage& data)
      {
        m_modes_per_pixel.Clear();

        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;
        }
      }

      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
      }

      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;
        }

        // 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 = 0;
            float HG = 0;
            float HB = 0;

            // 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);
        }

        // 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);
          }
        }

        // 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 (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;

        // 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);

            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;
          }
        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2F/T2FGMM.cpp --#--

--#-- START ./bgslibrary/algorithms/T2F/MRF.cpp --#--
#include "MRF.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

//init the basic MRF
MRF::MRF()
{
  in_image = out_image = 0;
  width = height = 0;

  //////////////////////////////////////////////////////////////////////////
  no_regions = 2;
  beta = 2.8;// 0.9;
  t = 10;//0.05

  //////////////////////////////////////////////////////////////////////////
  K = 0;
  E = E_old = 0;

  //////////////////////////////////////////////////////////////////////////
  T0 = 4;
  c = 0.98;
  T = 0;

  //////////////////////////////////////////////////////////////////////////
  alpha = 0.1;

  //////////////////////////////////////////////////////////////////////////
  classes = 0;
  in_image_data = 0;
  local_evidence = 0;
}

/************************************************************************/
/* the Markov Random Field with time constraints for T2FGMM    */
/************************************************************************/

MRF_TC::MRF_TC()
{
  beta_time = 0.9;
}

MRF_TC::~MRF_TC()
{
  delete[]classes;
  delete[]old_labeling;
  delete[]in_image_data;
  delete[]local_evidence;
}

double MRF_TC::TimeEnergy2(int i, int j, int label)
{
  double energy = 0.0;

  if (old_labeling[i][j] == (label * 255))
    energy -= beta_time;
  else
    energy += beta_time;

  if (i != height - 1) // south
  {
    if (label * 255 == old_labeling[i + 1][j])
      energy -= beta_time;
    else
      energy += beta_time;

    if ((j != width - 1) && (label * 255 == old_labeling[i + 1][j + 1]))
      energy -= beta_time;
    else
      energy += beta_time;

    if ((j != 0) && (label * 255 == old_labeling[i + 1][j - 1]))
      energy -= beta_time;
    else
      energy += beta_time;
  }

  if (j != width - 1) // east
  {
    if (label * 255 == old_labeling[i][j + 1])
      energy -= beta_time;
    else
      energy += beta_time;
  }

  if (i != 0) // nord
  {
    if (label * 255 == old_labeling[i - 1][j])
      energy -= beta_time;
    else
      energy += beta_time;

    if ((j != width - 1) && (label * 255 == old_labeling[i - 1][j + 1]))
      energy -= beta_time;
    else
      energy += beta_time;

    if ((j != 0) && (label * 255 == old_labeling[i - 1][j - 1]))
      energy -= beta_time;
    else
      energy += beta_time;
  }

  if (j != 0) // west
  {
    if (label * 255 == old_labeling[i][j - 1])
      energy -= beta_time;
    else
      energy += beta_time;
  }

  return energy;
}

double MRF_TC::Doubleton2(int i, int j, int label)
{
  double energy = 0.0;

  if (i != height - 1) // south
  {
    if (label == classes[i + 1][j])
      energy -= beta;
    else
      energy += beta;

    if ((j != width - 1) && (label == classes[i + 1][j + 1]))
      energy -= beta;
    else
      energy += beta;

    if ((j != 0) && (label == classes[i + 1][j - 1]))
      energy -= beta;
    else
      energy += beta;
  }

  if (j != width - 1) // east
  {
    if (label == classes[i][j + 1])
      energy -= beta;
    else
      energy += beta;
  }

  if (i != 0) // nord
  {
    if (label == classes[i - 1][j])
      energy -= beta;
    else
      energy += beta;

    if ((j != width - 1) && (label == classes[i - 1][j + 1]))
      energy -= beta;
    else
      energy += beta;

    if ((j != 0) && (label == classes[i - 1][j - 1]))
      energy -= beta;
    else
      energy += beta;
  }

  if (j != 0) // west
  {
    if (label == classes[i][j - 1])
      energy -= beta;
    else
      energy += beta;
  }

  return energy;
}

void MRF_TC::OnIterationOver2(void)
{
  CreateOutput2();
  //cout<<"\rI="<<K<<", ";
}

void MRF_TC::Build_Classes_OldLabeling_InImage_LocalEnergy()
{
  int i;
  classes = new int*[height];
  old_labeling = new int *[height];
  in_image_data = new int*[height];
  local_evidence = new float*[height];

  for (i = 0; i < height; ++i)
  {
    classes[i] = new int[width];
    old_labeling[i] = new int[width];
    in_image_data[i] = new int[width];
    local_evidence[i] = new float[width * 2];
  }
}

void MRF_TC::InitEvidence2(GMM *gmm, HMM *hmm, IplImage *labeling)
{
  int i, j;

  background = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
  cvCopy(background2, background.Ptr());

  unsigned char *in_data = (unsigned char *)(in_image->imageData);
  unsigned char *labeling_data = (unsigned char *)(labeling->imageData);

  for (i = 0; i < height; ++i)
  {
    for (j = 0; j < width; ++j)
    {
      in_image_data[i][j] = in_data[(i*in_image->widthStep) + j];
      old_labeling[i][j] = labeling_data[i*width + j];

      if (in_image_data[i][j] == 255)
        classes[i][j] = 1;
      else
        classes[i][j] = 0;
    }

    float variance;
    float muR;
    float muG;
    float muB;

    float pixel;

    int modes = 3;
    float mu;

    for (i = 0; i < height; ++i)
    {
      for (j = 0; j < width; ++j)
      {
        variance = gmm[(i*width + j) * modes + 0].variance;
        muR = gmm[(i*width + j) * modes + 0].muR;
        muG = gmm[(i*width + j) * modes + 0].muG;
        muB = gmm[(i*width + j) * modes + 0].muB;

        mu = (muR + muG + muB) / 3;

        pixel = (background(i, j, 0) + background(i, j, 1) + background(i, j, 2)) / 3;

        if (variance == 0) variance = 1;

        local_evidence[i][j * 2 + 0] = pow((pixel - mu), 2) / 2 / variance;

        if (pixel >= mu)
          local_evidence[i][j * 2 + 1] = pow((pixel - mu - 2.5*sqrt(variance)), 2) / 2 / variance;
        else
          local_evidence[i][j * 2 + 1] = pow((pixel - mu + 2.5*sqrt(variance)), 2) / 2 / variance;
      }
    }
  }
  background.ReleaseImage();
}

void MRF_TC::CreateOutput2()
{
  int i, j;
  unsigned char *out_data;

  out_data = (unsigned char *)out_image->imageData;

  // create output image
  for (i = 0; i < height; ++i)
    for (j = 0; j < width; ++j)
      out_data[(i*width) + j] = (unsigned char)((classes[i][j]) * 255);
}

//calculate the whole energy
double MRF_TC::CalculateEnergy2()
{
  double sum = 0.0;
  int i, j, k;
  // !FAIL!
  for (i = 0; i < height; ++i)
  {
    for (j = 0; j < width; ++j)
    {
      k = classes[i][j];
      sum = sum + local_evidence[i][j * 2 + k] + Doubleton2(i, j, k) + TimeEnergy2(i, j, k);//min the value
    }
  }
  //sum = 0.1;
  return sum;
}

// local energy
double MRF_TC::LocalEnergy2(int i, int j, int label)
{
  return local_evidence[i][j * 2 + label] + Doubleton2(i, j, label) + TimeEnergy2(i, j, label);
}

void MRF_TC::ICM2()
{
  int i, j;
  //double summa_deltaE = 0;
  double localenergy0 = 0, localenergy1 = 0;

  K = 0;
  //E_old = CalculateEnergy2();

  do
  {
    for (i = 0; i < height; ++i)
      for (j = 0; j < width; ++j)
      {
        localenergy0 = LocalEnergy2(i, j, 0);
        localenergy1 = LocalEnergy2(i, j, 1);

        if (localenergy0 < localenergy1)
          classes[i][j] = 0;
        else
          classes[i][j] = 1;
      }

    //E = CalculateEnergy2();
    //summa_deltaE = fabs(E_old-E);
    //E_old = E;
    ++K;
    OnIterationOver2();
  } while (K < 2);
}

#endif

--#-- END ./bgslibrary/algorithms/T2F/MRF.cpp --#--

--#-- START ./bgslibrary/algorithms/T2F/T2FMRF.cpp --#--
#include "T2FMRF.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

//using namespace bgslibrary::algorithms::dp;

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      int compareT2FMRF(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;
      }

      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)
        {
          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)
          {
            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 = 0;
            float HG = 0;
            float HB = 0;

            // 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;
        }

        // 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;

          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;
      }

      ///////////////////////////////////////////////////////////////////////////////
      //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();

            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;

            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;
          }
        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2F/T2FMRF.cpp --#--

--#-- START ./bgslibrary/algorithms/TwoPoints.cpp --#--
#include "TwoPoints.h"

using namespace bgslibrary::algorithms;

TwoPoints::TwoPoints() :
  IBGS(quote(TwoPoints)),
  matchingThreshold(DEFAULT_MATCH_THRESH),
  updateFactor(DEFAULT_UPDATE_FACTOR), model(nullptr)
{
  debug_construction(TwoPoints);
  initLoadSaveConfig(algorithmName);
  //model = static_cast<twopointsModel_t*>(libtwopointsModel_New());
  model = twopoints::libtwopointsModel_New();
}

TwoPoints::~TwoPoints() {
  debug_destruction(TwoPoints);
  twopoints::libtwopointsModel_Free(model);
}

void TwoPoints::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_input.empty())
    return;

  cv::Mat updatingMask;
  cv::Mat img_input_grayscale;

  // Convert input image to a grayscale image
  cvtColor(img_input, img_input_grayscale, CV_BGR2GRAY);

  if (firstTime) {
    // Create a buffer for the output image.
    //img_output = Mat(img_input.rows, img_input.cols, CV_8UC1);

    // Initialization of the ViBe model.
    twopoints::libtwopointsModel_AllocInit_8u_C1R(model, img_input_grayscale.data, img_input.cols, img_input.rows);

    // Sets default model values.
    // twopoints::libvibeModel_Sequential_SetMatchingThreshold(model, matchingThreshold);
    // twopoints::libvibeModel_Sequential_SetUpdateFactor(model, updateFactor);
  }

  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
  for (int i = 0; i < img_input.cols * img_input.rows; i++) {
    if (img_output.data[i] == 0) { // Foreground pixel
      updatingMask.data[i] = 0;
      img_output.data[i] = 255;
    }
    else { // Background
      updatingMask.data[i] = 255;
      img_output.data[i] = 0;
    }
  }

  twopoints::libtwopointsModel_Update_8u_C1R(model, img_input_grayscale.data, updatingMask.data);

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_output);
#endif

  firstTime = false;
}

void TwoPoints::save_config(cv::FileStorage &fs) {
  fs << "matchingThreshold" << matchingThreshold;
  fs << "updateFactor" << updateFactor;
  fs << "showOutput" << showOutput;
}

void TwoPoints::load_config(cv::FileStorage &fs) {
  fs["matchingThreshold"] >> matchingThreshold;
  fs["updateFactor"] >> updateFactor;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/TwoPoints.cpp --#--

--#-- START ./bgslibrary/algorithms/IndependentMultimodal.cpp --#--
#include "IndependentMultimodal.h"

using namespace bgslibrary::algorithms;

IndependentMultimodal::IndependentMultimodal() : 
  IBGS(quote(IndependentMultimodal)), fps(10)
{
  debug_construction(IndependentMultimodal);
  initLoadSaveConfig(algorithmName);
  pIMBS = new imbs::BackgroundSubtractorIMBS(fps);
}

IndependentMultimodal::~IndependentMultimodal() {
  debug_destruction(IndependentMultimodal);
  delete pIMBS;
}

void IndependentMultimodal::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  //get the fgmask and update the background model
  pIMBS->apply(img_input, img_foreground);

  //get background image
  pIMBS->getBackgroundImage(img_background);

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  firstTime = false;
}

void IndependentMultimodal::save_config(cv::FileStorage &fs) {
  fs << "fps" << fps;
  fs << "showOutput" << showOutput;
}

void IndependentMultimodal::load_config(cv::FileStorage &fs) {
  fs["fps"] >> fps;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/IndependentMultimodal.cpp --#--

--#-- START ./bgslibrary/algorithms/DPAdaptiveMedian.cpp --#--
#include "DPAdaptiveMedian.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPAdaptiveMedian::DPAdaptiveMedian() :
  IBGS(quote(DPAdaptiveMedian)),
  frameNumber(0), threshold(40), 
  samplingRate(7), learningFrames(30)
{
  debug_construction(DPAdaptiveMedian);
  initLoadSaveConfig(algorithmName);
}

DPAdaptiveMedian::~DPAdaptiveMedian() {
  debug_destruction(DPAdaptiveMedian);
}

void DPAdaptiveMedian::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold;
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    params.SamplingRate() = samplingRate;
    params.LearningFrames() = learningFrames;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  // bitwise_not(img_foreground, img_foreground);
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPAdaptiveMedian::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "samplingRate" << samplingRate;
  fs << "learningFrames" << learningFrames;
  fs << "showOutput" << showOutput;
}

void DPAdaptiveMedian::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["samplingRate"] >> samplingRate;
  fs["learningFrames"] >> learningFrames;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPAdaptiveMedian.cpp --#--

--#-- START ./bgslibrary/algorithms/_template_/MyBGS.cpp --#--
#include "MyBGS.h"

using namespace bgslibrary::algorithms;

MyBGS::MyBGS() :
  IBGS(quote(MyBGS))
{
  debug_construction(MyBGS);
}
MyBGS::~MyBGS() {
  debug_destruction(MyBGS);
}

void MyBGS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  if (img_input.empty())
    return;

  if (img_previous.empty())
    img_input.copyTo(img_previous);

  cv::Mat img_foreground;
  cv::absdiff(img_previous, img_input, img_foreground);

  if (img_foreground.channels() == 3)
    cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY);

  cv::threshold(img_foreground, img_foreground, 15, 255, cv::THRESH_BINARY);
  
#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_previous.copyTo(img_bgmodel);

  img_input.copyTo(img_previous);
}

void MyBGS::save_config(cv::FileStorage &fs) {
  fs << "showOutput" << showOutput;
}

void MyBGS::load_config(cv::FileStorage &fs) {
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/_template_/MyBGS.cpp --#--

--#-- START ./bgslibrary/algorithms/PBAS/PBAS.cpp --#--
#include "PBAS.h"

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;
  formerMeanNorm = 0;
  width = 0;

  //result image
  foregroundValue = 255;
  backgroundValue = 0;

  //length of random array
  countOfRandomNumb = 1000;

  //the T(x_i) value needs initiation
  T_init = R_lower;

  //check if something is moving in the picture
  isMove = false;

  //for init, count number of runs
  runs = 0;
  newInitialization();
}

void PBAS::newInitialization()
{
  if (!randomN.empty())
    randomN.clear();

  if (!randomX.empty())
    randomX.clear();

  if (!randomY.empty())
    randomY.clear();

  if (!randomMinDist.empty())
    randomMinDist.clear();

  if (!randomT.empty())
    randomT.clear();

  if (!randomTN.empty())
    randomTN.clear();

  for (int l = 0; l < countOfRandomNumb; l++)
  {
    randomN.push_back((int)randomGenerator.uniform((int)0, (int)N));
    randomX.push_back((int)randomGenerator.uniform(-1, +2));
    randomY.push_back((int)randomGenerator.uniform(-1, +2));
    randomMinDist.push_back((int)randomGenerator.uniform((int)0, (int)N));
    randomT.push_back((int)randomGenerator.uniform((int)0, (int)T_upper));
    randomTN.push_back((int)randomGenerator.uniform((int)0, (int)T_upper));
  }
}

PBAS::~PBAS(void)
{
  randomN.clear();
  randomX.clear();
  randomY.clear();
  randomMinDist.clear();
  randomT.clear();
  randomTN.clear();

  for (int k = 0; k < backgroundModel.size(); ++k)
  {
    if (chans == 1)
    {
      backgroundModel.at(k).at(0).release();
      backgroundModel.at(k).at(1).release();
    }
    else
    {
      backgroundModel.at(k).at(0).release();
      backgroundModel.at(k).at(1).release();
      backgroundModel.at(k).at(2).release();

      backgroundModel.at(k).at(3).release();
      backgroundModel.at(k).at(4).release();
      backgroundModel.at(k).at(5).release();
    }
  }

  backgroundModel.clear();
  meanMinDist.release();

  actualR.release();
  actualT.release();

  sobelX.release();
  sobelY.release();
}

bool PBAS::process(cv::Mat* input, cv::Mat* output)
{
  if (width != input->cols)
  {
    width = input->cols;
    chans = input->channels();
    height = input->rows;

    if (input->rows < 1 || input->cols < 1)
    {
      std::cout << "Error: Occurrence of to small (or empty?) image size in PBAS. STOPPING " << std::endl;
      return false;
    }
  }

  //iniate the background model
  init(input);

  resultMap = new cv::Mat(input->rows, input->cols, CV_8UC1);

  //calculate features
  calculateFeatures(&currentFeatures, input);

  //set sumMagnitude to zero at beginning and then sum up in the loop
  sumMagnitude = 0;
  long glCounterFore = 0;
  isMove = false;

  //Here starts the whole processing of each pixel of the image
  // for each pixel
  for (int j = 0; j < resultMap->rows; ++j)
  {
    resultMap_Pt = resultMap->ptr<uchar>(j);
    currentFeaturesM_Pt.clear();
    currentFeaturesC_Pt.clear();
    std::vector<float*> fT;
    std::vector<uchar*> uT;
    B_Mag_Pts.clear();
    B_Col_Pts.clear();

    for (int z = 0; z < chans; ++z)
    {
      currentFeaturesM_Pt.push_back(currentFeatures.at(z).ptr<float>(j));
      currentFeaturesC_Pt.push_back(currentFeatures.at(z + chans).ptr<uchar>(j));

      B_Mag_Pts.push_back(fT);

      B_Col_Pts.push_back(uT);
    }

    meanMinDist_Pt = meanMinDist.ptr<float>(j);
    actualR_Pt = actualR.ptr<float>(j);
    actualT_Pt = actualT.ptr<float>(j);

    for (int k = 0; k < runs; ++k)
    {
      for (int z = 0; z < chans; ++z)
      {
        B_Mag_Pts.at(z).push_back(backgroundModel.at(k).at(z).ptr<float>(j));
        B_Col_Pts.at(z).push_back(backgroundModel.at(k).at(z + chans).ptr<uchar>(j));
      }
    }

    for (int i = 0; i < resultMap->cols; ++i)
    {
      //Compare each pixel to in the worst runtime-case each background model
      int count = 0;
      int index = 0;

      double norm = 0.0;
      double dist = 0.0;
      double minDist = 1000.0;
      int entry = randomGenerator.uniform(3, countOfRandomNumb - 4);

      do
      {
        if (chans == 3)
        {
          norm = sqrt(
            (((double)B_Mag_Pts.at(0).at(index)[i] - ((double)*currentFeaturesM_Pt.at(0)))*((double)B_Mag_Pts.at(0).at(index)[i] - ((double)*currentFeaturesM_Pt.at(0)))) +
            (((double)B_Mag_Pts.at(1).at(index)[i] - ((double)*currentFeaturesM_Pt.at(1)))*((double)B_Mag_Pts.at(1).at(index)[i] - ((double)*currentFeaturesM_Pt.at(1)))) +
            (((double)B_Mag_Pts.at(2).at(index)[i] - ((double)*currentFeaturesM_Pt.at(2)))*((double)B_Mag_Pts.at(2).at(index)[i] - ((double)*currentFeaturesM_Pt.at(2))))
          );

          dist = sqrt(
            (((double)B_Col_Pts.at(0).at(index)[i] - ((double)*currentFeaturesC_Pt.at(0)))*((double)B_Col_Pts.at(0).at(index)[i] - ((double)*currentFeaturesC_Pt.at(0)))) +
            (((double)B_Col_Pts.at(1).at(index)[i] - ((double)*currentFeaturesC_Pt.at(1)))*((double)B_Col_Pts.at(1).at(index)[i] - ((double)*currentFeaturesC_Pt.at(1)))) +
            (((double)B_Col_Pts.at(2).at(index)[i] - ((double)*currentFeaturesC_Pt.at(2)))*((double)B_Col_Pts.at(2).at(index)[i] - ((double)*currentFeaturesC_Pt.at(2))))
          );
        }
        else
        {
          norm = abs((((double)B_Mag_Pts.at(0).at(index)[i] -
            ((double)*currentFeaturesM_Pt.at(0)))*((double)B_Mag_Pts.at(0).at(index)[i] - ((double)*currentFeaturesM_Pt.at(0)))));

          dist = abs(((double)B_Col_Pts.at(0).at(index)[i] - ((double)*currentFeaturesC_Pt.at(0))));
        }
        dist = ((double)alpha*(norm / formerMeanMag) + beta*dist);

        if ((dist < *actualR_Pt))
        {
          ++count;
          if (minDist > dist)
            minDist = dist;
        }
        else
        {
          sumMagnitude += (double)(norm);
          ++glCounterFore;
        }
        ++index;
      } while ((count < Raute_min) && (index < runs));


      //#############################################
      //update backgroundmodel
      // is BACKGROUND
      if (count >= Raute_min)
      {
        *resultMap_Pt = 0;
        double ratio = std::ceil((double)T_upper / (double)(*actualT_Pt));
        //in the first run every distance is zero, because there is no background model
        //in the secont run, we have already one image as background model, hence a
        // reasonable minDist could be found -> because of the partly 1/run changing in the running average, we set in the first try meanMinDist to the actual minDist value
        if (runs < N && runs > 2)
        {
          *meanMinDist_Pt = ((((float)(runs - 1)) * (*meanMinDist_Pt)) + (float)minDist) / ((float)runs);
        }
        else if (runs < N && runs == 2)
        {
          *meanMinDist_Pt = (float)minDist;
        }

        //1. update model
        if (runs == N)
        {
          //Update current pixel
          //check if random numer is smaller than ratio
          if (randomT.at(entry) < ratio)
          {
            // replace randomly chosen sample
            int rand = randomN.at(entry + 1); //randomGenerator.uniform((int)0,(int)N-1);
            for (int z = 0; z < chans; ++z)
            {
              B_Mag_Pts.at(z).at(rand)[i] = (float)*currentFeaturesM_Pt.at(z);
              B_Col_Pts.at(z).at(rand)[i] = (uchar)*currentFeaturesC_Pt.at(z);

            }

            *meanMinDist_Pt = ((((float)(N - 1)) * (*meanMinDist_Pt)) + (float)minDist) / ((float)N);
          }

          //Update neighboring pixel model
          if (randomTN.at(entry) < ratio)
          {
            //choose neighboring pixel randomly
            int xNeigh = randomX.at(entry) + i;
            int yNeigh = randomY.at(entry) + j;
            checkValid(&xNeigh, &yNeigh);

            // replace randomly chosen sample
            int rand = randomN.at(entry - 1);
            for (int z = 0; z < chans; ++z)
            {
              (backgroundModel.at(rand)).at(z).at<float>(yNeigh, xNeigh) = currentFeatures.at(z).at<float>(yNeigh, xNeigh);
              (backgroundModel.at(rand)).at(z + chans).at<uchar>(yNeigh, xNeigh) = currentFeatures.at(z + chans).at<uchar>(yNeigh, xNeigh);
            }
          }
        }
      }
      else
      {
        // store pixel as foreground
        *resultMap_Pt = 255;

        //there is some movement
        isMove = true;
      }

      //#######################//#######################//#######################//#######################
      //control loops
      //#######################//#######################//#######################//#######################
      //update R
      decisionThresholdRegulator(actualR_Pt, meanMinDist_Pt);

      //update T
      learningRateRegulator(actualT_Pt, meanMinDist_Pt, resultMap_Pt);

      //#######################//#######################//#######################//#######################
      //#######################//#######################//#######################//#######################

      //jump to next pixel
      ++resultMap_Pt;
      for (int z = 0; z < chans; ++z)
      {
        ++currentFeaturesM_Pt.at(z);
        ++currentFeaturesC_Pt.at(z);
      }

      ++meanMinDist_Pt;
      ++actualR_Pt;
      ++actualT_Pt;
    }
  }

  resultMap->copyTo(*output);

  //if there is no foreground -> no magnitudes fount
  //-> initiate some low value to prevent diving through zero
  double meanMag = sumMagnitude / (double)(glCounterFore + 1); //height*width);

  if (meanMag > 20)
    formerMeanMag = meanMag;
  else
    formerMeanMag = 20;

  delete resultMap;

  for (int z = 0; z < chans; ++z)
  {
    currentFeatures.at(z + chans).release();
    currentFeatures.at(z).release();
  }

  return true;
}

void PBAS::decisionThresholdRegulator(float* pt, float* meanDist)
{
  //update R
  double tempR = *pt;
  double newThresh = (*meanDist)*R_scale;

  if (tempR < newThresh)
  {
    tempR += tempR * R_incdec;
  }
  else
  {
    tempR -= tempR * R_incdec;
  }

  if (tempR >= R_lower)
    *pt = (float)tempR;
  else
    *pt = (float)R_lower;
}

void PBAS::learningRateRegulator(float* pt, float* meanDist, uchar* isFore)
{
  //time update
  double tempT = *pt;

  if ((int)*isFore < 128)
  {
    tempT -= T_inc / (*meanDist + 1.0);
  }
  else
  {
    tempT += T_dec / (*meanDist + 1.0);
  }

  if (tempT > T_lower && tempT < T_upper)
    *pt = (float)tempT;
}

void PBAS::checkValid(int *x, int *y)
{
  if (*x < 0)
  {
    *x = 0;
  }
  else if (*x >= width)
  {
    *x = width - 1;
  }

  if (*y < 0)
  {
    *y = 0;
  }
  else if (*y >= height)
  {
    *y = height - 1;
  }
}

void PBAS::init(cv::Mat* input)
{
  if (runs < N)
  {
    std::vector<cv::Mat> init;
    calculateFeatures(&init, input);
    backgroundModel.push_back(init);

    if (chans == 1)
    {
      init.at(0).release();
      init.at(1).release();
    }
    else
    {
      init.at(0).release();
      init.at(1).release();
      init.at(2).release();
      init.at(3).release();
      init.at(4).release();
      init.at(5).release();
    }

    init.clear();

    if (runs == 0)
    {
      meanMinDist.create(input->size(), CV_32FC1);
      meanMinDist = meanMinDist.zeros(input->rows, input->cols, CV_32FC1);

      actualR.create(input->rows, input->cols, CV_32FC1);
      actualT.create(input->rows, input->cols, CV_32FC1);

      float* ptRs, *ptTs; //, *ptM;
      for (int rows = 0; rows < actualR.rows; ++rows)
      {
        ptRs = actualR.ptr<float>(rows);
        ptTs = actualT.ptr<float>(rows);

        for (int cols = 0; cols < actualR.cols; ++cols)
        {
          ptRs[cols] = (float)R_lower;
          ptTs[cols] = (float)T_init;
        }
      }
    }

    ++runs;
  }
}

void PBAS::calculateFeatures(std::vector<cv::Mat>* feature, cv::Mat* inputImage)
{
  if (!feature->empty())
    feature->clear();

  cv::Mat mag[3], dir;

  if (inputImage->channels() == 3)
  {
    std::vector<cv::Mat> rgbChannels(3);
    cv::split(*inputImage, rgbChannels);

    for (int l = 0; l < 3; ++l)
    {
      cv::Sobel(rgbChannels.at(l), sobelX, CV_32F, 1, 0, 3, 1, 0.0);
      cv::Sobel(rgbChannels.at(l), sobelY, CV_32F, 0, 1, 3, 1, 0.0);

      // Compute the L2 norm and direction of the gradient
      cv::cartToPolar(sobelX, sobelY, mag[l], dir, true);
      feature->push_back(mag[l]);
      sobelX.release();
      sobelY.release();
    }

    feature->push_back(rgbChannels.at(0));
    feature->push_back(rgbChannels.at(1));
    feature->push_back(rgbChannels.at(2));
    rgbChannels.at(0).release();
    rgbChannels.at(1).release();
    rgbChannels.at(2).release();
  }
  else
  {
    cv::Sobel(*inputImage, sobelX, CV_32F, 1, 0, 3, 1, 0.0);
    cv::Sobel(*inputImage, sobelY, CV_32F, 0, 1, 3, 1, 0.0);

    // Compute the L2 norm and direction of the gradient
    cv::cartToPolar(sobelX, sobelY, mag[0], dir, true);
    feature->push_back(mag[0]);

    cv::Mat temp;
    inputImage->copyTo(temp);
    feature->push_back(temp);
    temp.release();
  }

  mag[0].release();
  mag[1].release();
  mag[2].release();
  dir.release();
}

void PBAS::setN(int temp)
{
  N = temp;
  newInitialization();
}

void PBAS::setRaute_min(int temp)
{
  Raute_min = temp;
}

void PBAS::setR_lower(double temp)
{
  R_lower = temp;
}

void PBAS::setR_incdec(double temp)
{
  R_incdec = temp;
}

void PBAS::setR_scale(double temp)
{
  R_scale = temp;
}

void PBAS::setT_init(double temp)
{
  T_init = temp;
}

void PBAS::setT_lower(double temp)
{
  T_lower = temp;
}

void PBAS::setT_upper(double temp)
{
  T_upper = temp;
  newInitialization();
}

void PBAS::setT_dec(double temp)
{
  T_dec = temp;
}

void PBAS::setT_inc(double temp)
{
  T_inc = temp;
}

void PBAS::setAlpha(double temp)
{
  alpha = temp;
}

void PBAS::setBeta(double temp)
{
  beta = temp;
}

bool PBAS::isMovement()
{
  return isMove;
}

//cv::Mat* PBAS::getR1_xi()
//{
//	return &actualR;
//}
//
//cv::Mat* PBAS::getT_xi()
//{
//	return &actualT;
//}

--#-- END ./bgslibrary/algorithms/PBAS/PBAS.cpp --#--

--#-- START ./bgslibrary/algorithms/DPEigenbackground.cpp --#--
#include "DPEigenbackground.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPEigenbackground::DPEigenbackground() :
  IBGS(quote(DPEigenbackground)),
  frameNumber(0), threshold(225), 
  historySize(20), embeddedDim(10)
{
  debug_construction(DPEigenbackground);
  initLoadSaveConfig(algorithmName);
}

DPEigenbackground::~DPEigenbackground() {
  debug_destruction(DPEigenbackground);
}

void DPEigenbackground::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold; //15*15;
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    //params.HistorySize() = 100;
    params.HistorySize() = historySize;
    //params.EmbeddedDim() = 20;
    params.EmbeddedDim() = embeddedDim;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPEigenbackground::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "historySize" << historySize;
  fs << "embeddedDim" << embeddedDim;
  fs << "showOutput" << showOutput;
}

void DPEigenbackground::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["historySize"] >> historySize;
  fs["embeddedDim"] >> embeddedDim;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPEigenbackground.cpp --#--

--#-- START ./bgslibrary/algorithms/FuzzySugenoIntegral.cpp --#--
#include "FuzzySugenoIntegral.h"

using namespace bgslibrary::algorithms;

FuzzySugenoIntegral::FuzzySugenoIntegral() :
  IBGS(quote(FuzzySugenoIntegral)),
  frameNumber(0), framesToLearn(10), alphaLearn(0.1), alphaUpdate(0.01),
  colorSpace(1), option(2), smooth(true), threshold(0.67)
{
  debug_construction(FuzzySugenoIntegral);
  initLoadSaveConfig(algorithmName);
}

FuzzySugenoIntegral::~FuzzySugenoIntegral() {
  debug_destruction(FuzzySugenoIntegral);
}

void FuzzySugenoIntegral::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  cv::Mat img_input_f3(img_input.size(), CV_32F);
  img_input.convertTo(img_input_f3, CV_32F, 1. / 255.);

  if (firstTime) {
    std::cout << algorithmName + " parameters:" << std::endl;

    std::string colorSpaceName = "";
    switch (colorSpace)
    {
    case 1: colorSpaceName = "RGB";  break;
    case 2: colorSpaceName = "OHTA"; break;
    case 3: colorSpaceName = "HSV";  break;
    case 4: colorSpaceName = "YCrCb"; break;
    }
    std::cout << "Color space: " << colorSpaceName << std::endl;

    if (option == 1)
      std::cout << "Fuzzing by 3 color components" << std::endl;
    if (option == 2)
      std::cout << "Fuzzing by 2 color components + 1 texture component" << std::endl;
  }

  if (frameNumber <= framesToLearn) {
    if (frameNumber == 0)
      std::cout << algorithmName + " initializing background model by adaptive learning" << std::endl;

    if (img_background_f3.empty())
      img_input_f3.copyTo(img_background_f3);
    else
      img_background_f3 = alphaLearn*img_input_f3 + (1 - alphaLearn)*img_background_f3;

    double minVal = 0., maxVal = 1.;
    img_background_f3.convertTo(img_background, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    img_background.copyTo(img_bgmodel);

    img_foreground = cv::Mat::zeros(img_input.size(), img_input.type());
    img_foreground.copyTo(img_output);

#ifndef MEX_COMPILE_FLAG
    if (showOutput)
      cv::imshow(algorithmName + "_BG", img_background);
#endif
  }
  else
  {
    cv::Mat img_input_f1;
    cv::cvtColor(img_input_f3, img_input_f1, CV_BGR2GRAY);

    cv::Mat img_background_f1;
    cv::cvtColor(img_background_f3, img_background_f1, CV_BGR2GRAY);

    IplImage _input_f3 = cvIplImage(img_input_f3);
    IplImage* input_f3 = cvCloneImage(&_input_f3);

    IplImage _input_f1 = cvIplImage(img_input_f1);
    IplImage* input_f1 = cvCloneImage(&_input_f1);

    IplImage _background_f3 = cvIplImage(img_background_f3);
    IplImage* background_f3 = cvCloneImage(&_background_f3);

    IplImage _background_f1 = cvIplImage(img_background_f1);
    IplImage* background_f1 = cvCloneImage(&_background_f1);

    IplImage* lbp_input_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1);
    cvSetZero(lbp_input_f1);
    fu.LBP(input_f1, lbp_input_f1);

    IplImage* lbp_background_f1 = cvCreateImage(cvSize(background_f1->width, background_f1->height), IPL_DEPTH_32F, 1);
    cvSetZero(lbp_background_f1);
    fu.LBP(background_f1, lbp_background_f1);

    IplImage* sim_texture_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1);
    fu.SimilarityDegreesImage(lbp_input_f1, lbp_background_f1, sim_texture_f1, 1, colorSpace);

    IplImage* sim_color_f3 = cvCreateImage(cvSize(input_f3->width, input_f3->height), IPL_DEPTH_32F, 3);
    fu.SimilarityDegreesImage(input_f3, background_f3, sim_color_f3, 3, colorSpace);

    float* measureG = (float*)malloc(3 * (sizeof(float)));
    IplImage* integral_sugeno_f1 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 1);

    // 3 color components
    if (option == 1) {
      fu.FuzzyMeasureG(0.4f, 0.3f, 0.3f, measureG);
      fu.getFuzzyIntegralSugeno(sim_texture_f1, sim_color_f3, option, measureG, integral_sugeno_f1);
    }

    // 2 color components + 1 texture component
    if (option == 2) {
      fu.FuzzyMeasureG(0.6f, 0.3f, 0.1f, measureG);
      fu.getFuzzyIntegralSugeno(sim_texture_f1, sim_color_f3, option, measureG, integral_sugeno_f1);
    }

    free(measureG);
    cv::Mat img_integral_sugeno_f1 = cv::cvarrToMat(integral_sugeno_f1);

    if (smooth)
      cv::medianBlur(img_integral_sugeno_f1, img_integral_sugeno_f1, 3);

    cv::Mat img_foreground_f1(img_input.size(), CV_32F);
    cv::threshold(img_integral_sugeno_f1, img_foreground_f1, threshold, 255, cv::THRESH_BINARY_INV);

    //cv::Mat img_foreground_u1(img_input.size(), CV_8U);
    double minVal = 0., maxVal = 1.;
    img_foreground_f1.convertTo(img_foreground, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    img_foreground.copyTo(img_output);

    //cv::Mat img_background_u3(img_input.size(), CV_8U);
    //double minVal = 0., maxVal = 1.;
    img_background_f3.convertTo(img_background, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    img_background.copyTo(img_bgmodel);

#ifndef MEX_COMPILE_FLAG
    if (showOutput) {
      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);
    }
#endif

    if (frameNumber == (framesToLearn + 1))
      std::cout << algorithmName + " updating background model by adaptive-selective learning" << std::endl;

    IplImage* updated_background_f3 = cvCreateImage(cvSize(input_f1->width, input_f1->height), IPL_DEPTH_32F, 3);
    cvSetZero(updated_background_f3);
    fu.AdaptativeSelectiveBackgroundModelUpdate(input_f3, background_f3, updated_background_f3, integral_sugeno_f1, threshold, alphaUpdate);
    cv::Mat img_updated_background_f3 = cv::cvarrToMat(updated_background_f3);
    img_updated_background_f3.copyTo(img_background_f3);

    cvReleaseImage(&input_f3);
    cvReleaseImage(&input_f1);
    cvReleaseImage(&background_f3);
    cvReleaseImage(&background_f1);
    cvReleaseImage(&lbp_input_f1);
    cvReleaseImage(&lbp_background_f1);
    cvReleaseImage(&sim_texture_f1);
    cvReleaseImage(&sim_color_f3);
    cvReleaseImage(&integral_sugeno_f1);
    cvReleaseImage(&updated_background_f3);
  }

  firstTime = false;
  frameNumber++;
}

void FuzzySugenoIntegral::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "framesToLearn" << framesToLearn;
  fs << "alphaLearn" << alphaLearn;
  fs << "alphaUpdate" << alphaUpdate;
  fs << "colorSpace" << colorSpace;
  fs << "option" << option;
  fs << "smooth" << smooth;
  fs << "showOutput" << showOutput;
}

void FuzzySugenoIntegral::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["framesToLearn"] >> framesToLearn;
  fs["alphaLearn"] >> alphaLearn;
  fs["alphaUpdate"] >> alphaUpdate;
  fs["colorSpace"] >> colorSpace;
  fs["option"] >> option;
  fs["smooth"] >> smooth;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/FuzzySugenoIntegral.cpp --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelSom.cpp --#--
#include "BGModelSom.h"

using namespace bgslibrary::algorithms::lb;
using namespace bgslibrary::algorithms::lb::BGModelSomParams;

BGModelSom::BGModelSom(int width, int height) : BGModel(width, height)
{
  m_offset = (KERNEL - 1) / 2;

  if (SPAN_NEIGHBORS)
    m_pad = 0;
  else
    m_pad = m_offset;

  // 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_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;
    }
  }

  // Create weights

  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

  int cM;
  int cN;
  m_Wmax = DBL_MIN;

  cN = 1;
  for (int j = 0; j < KERNEL; j++)
  {
    cM = 1;

    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];

      cM = cM * (KERNEL - 1 - i) / (i + 1);
    }

    cN = cN * (KERNEL - 1 - j) / (j + 1);
  }

  // Parameters

  m_epsilon1 = EPS1*EPS1;
  m_epsilon2 = EPS2*EPS2;

  m_alpha1 = C1 / m_Wmax;
  m_alpha2 = C2 / m_Wmax;

  m_K = 0;
  m_TSteps = TRAINING_STEPS;
}

BGModelSom::~BGModelSom()
{
  for (int n = 0; n < m_heightSOM; n++)
    delete[] m_ppSOM[n];

  delete[] m_ppSOM;

  for (int n = 0; n < KERNEL; n++)
    delete[] m_ppW[n];

  delete[] m_ppW;
}

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;

  case 1:
    m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue;
    break;

  case 2:
    m_alpha2 = 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;
  }

  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);

    for (unsigned int i = 0; i < m_width; i++)
    {
      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_K = 0;

  return;
}

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;
  }

  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 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;

      // Find BMU

      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;
          }
        }
      }

      // Update SOM

      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];

            // speed hack.. avoid very small increment values. abs() is sloooow.

            double d;

            d = srcR - m_ppSOM[l][k].Red;
            if (d*d > DBL_MIN)
              m_ppSOM[l][k].Red += a*d;

            d = srcG - m_ppSOM[l][k].Green;
            if (d*d > DBL_MIN)
              m_ppSOM[l][k].Green += a*d;

            d = srcB - m_ppSOM[l][k].Blue;
            if (d*d > DBL_MIN)
              m_ppSOM[l][k].Blue += 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;

        // 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;
}

--#-- END ./bgslibrary/algorithms/lb/BGModelSom.cpp --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelFuzzyGauss.cpp --#--
#include "BGModelFuzzyGauss.h"

using namespace bgslibrary::algorithms::lb;
using namespace bgslibrary::algorithms::lb::BGModelFuzzyGaussParams;

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++;
  }
}

BGModelFuzzyGauss::~BGModelFuzzyGauss()
{
  delete[] m_pMu;
  delete[] m_pVar;
}

void BGModelFuzzyGauss::setBGModelParameter(int id, int value)
{
  double dvalue = (double)value / 255.0;

  switch (id)
  {
  case 0:
    m_threshold = 100.0*dvalue*dvalue;
    break;

  case 1:
    m_threshBG = dvalue;
    break;

  case 2:
    m_alphamax = dvalue*dvalue*dvalue;
    break;

  case 3:
    m_noise = 100.0*dvalue;
    break;
  }

  return;
}

void BGModelFuzzyGauss::Init()
{
  DBLRGB *pMu = m_pMu;
  DBLRGB *pVar = m_pVar;

  Image<BYTERGB> prgbSrc(m_SrcImage);

  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;

      pVar->Red = m_noise;
      pVar->Green = m_noise;
      pVar->Blue = m_noise;

      pMu++;
      pVar++;
    }
  }

  return;
}

void BGModelFuzzyGauss::Update()
{
  DBLRGB *pMu = m_pMu;
  DBLRGB *pVar = m_pVar;

  Image<BYTERGB> prgbSrc(m_SrcImage);
  Image<BYTERGB> prgbBG(m_BGImage);
  Image<BYTERGB> prgbFG(m_FGImage);

  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;

      // Fuzzy background subtraction (Mahalanobis distance)

      double dr = srcR - pMu->Red;
      double dg = srcG - pMu->Green;
      double db = srcB - pMu->Blue;

      double d2 = dr*dr / pVar->Red + dg*dg / pVar->Green + db*db / pVar->Blue;

      double fuzzyBG = 1.0;

      if (d2 < m_threshold)
        fuzzyBG = d2 / m_threshold;

      // Fuzzy running average

      double alpha = m_alphamax*exp(FUZZYEXP*fuzzyBG);

      if (dr*dr > DBL_MIN)
        pMu->Red += alpha*dr;

      if (dg*dg > DBL_MIN)
        pMu->Green += alpha*dg;

      if (db*db > DBL_MIN)
        pMu->Blue += alpha*db;

      double d;

      d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red;
      if (d*d > DBL_MIN)
        pVar->Red += alpha*d;

      d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green;
      if (d*d > DBL_MIN)
        pVar->Green += alpha*d;

      d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue;
      if (d*d > DBL_MIN)
        pVar->Blue += 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);

      // Set foreground and background

      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;

      prgbBG[i][j].Red = (unsigned char)pMu->Red;
      prgbBG[i][j].Green = (unsigned char)pMu->Green;
      prgbBG[i][j].Blue = (unsigned char)pMu->Blue;

      pMu++;
      pVar++;
    }
  }

  return;
}

--#-- END ./bgslibrary/algorithms/lb/BGModelFuzzyGauss.cpp --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelGauss.cpp --#--
#include "BGModelGauss.h"

using namespace bgslibrary::algorithms::lb;
using namespace bgslibrary::algorithms::lb::BGModelGaussParams;

BGModelGauss::BGModelGauss(int width, int height) : BGModel(width, height)
{
  m_alpha = ALPHAGAUSS;
  m_threshold = THRESHGAUSS*THRESHGAUSS;
  m_noise = NOISEGAUSS;

  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++;
  }
}

BGModelGauss::~BGModelGauss()
{
  delete[] m_pMu;
  delete[] m_pVar;
}

void BGModelGauss::setBGModelParameter(int id, int value)
{
  double dvalue = (double)value / 255.0;

  switch (id)
  {
  case 0:
    m_threshold = 100.0*dvalue*dvalue;
    break;

  case 1:
    m_noise = 100.0*dvalue;
    break;

  case 2:
    m_alpha = dvalue*dvalue*dvalue;
    break;
  }

  return;
}

void BGModelGauss::Init()
{
  DBLRGB *pMu = m_pMu;
  DBLRGB *pVar = m_pVar;

  Image<BYTERGB> prgbSrc(m_SrcImage);

  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;

      pVar->Red = m_noise;
      pVar->Green = m_noise;
      pVar->Blue = m_noise;

      pMu++;
      pVar++;
    }
  }

  return;
}

void BGModelGauss::Update()
{
  DBLRGB *pMu = m_pMu;
  DBLRGB *pVar = m_pVar;

  Image<BYTERGB> prgbSrc(m_SrcImage);
  Image<BYTERGB> prgbBG(m_BGImage);
  Image<BYTERGB> prgbFG(m_FGImage);

  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;

      // Mahalanobis distance

      double dr = srcR - pMu->Red;
      double dg = srcG - pMu->Green;
      double db = srcB - pMu->Blue;

      double d2 = dr*dr / pVar->Red + dg*dg / pVar->Green + db*db / pVar->Blue;

      // Classify

      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;

      // Update parameters

      if (dr*dr > DBL_MIN)
        pMu->Red += m_alpha*dr;

      if (dg*dg > DBL_MIN)
        pMu->Green += m_alpha*dg;

      if (db*db > DBL_MIN)
        pMu->Blue += m_alpha*db;

      double d;

      d = (srcR - pMu->Red)*(srcR - pMu->Red) - pVar->Red;
      if (d*d > DBL_MIN)
        pVar->Red += m_alpha*d;

      d = (srcG - pMu->Green)*(srcG - pMu->Green) - pVar->Green;
      if (d*d > DBL_MIN)
        pVar->Green += m_alpha*d;

      d = (srcB - pMu->Blue)*(srcB - pMu->Blue) - pVar->Blue;
      if (d*d > DBL_MIN)
        pVar->Blue += 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);

      // Set 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;

      pMu++;
      pVar++;
    }
  }

  return;
}

--#-- END ./bgslibrary/algorithms/lb/BGModelGauss.cpp --#--

--#-- START ./bgslibrary/algorithms/lb/BGModel.cpp --#--
#include "BGModel.h"

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)
{
  cvCopy(image, m_SrcImage);
  Update();
  return;
}

--#-- END ./bgslibrary/algorithms/lb/BGModel.cpp --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelFuzzySom.cpp --#--
#include "BGModelFuzzySom.h"

using namespace bgslibrary::algorithms::lb;
using namespace bgslibrary::algorithms::lb::BGModelFuzzySomParams;

BGModelFuzzySom::BGModelFuzzySom(int width, int height) : BGModel(width, height)
{
  m_offset = (KERNEL - 1) / 2;

  if (SPAN_NEIGHBORS)
    m_pad = 0;
  else
    m_pad = m_offset;

  // 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_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;
    }
  }

  // Create weights

  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

  int cM;
  int cN;
  m_Wmax = DBL_MIN;

  cN = 1;
  for (int j = 0; j < KERNEL; j++)
  {
    cM = 1;

    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];

      cM = cM * (KERNEL - 1 - i) / (i + 1);
    }

    cN = cN * (KERNEL - 1 - j) / (j + 1);
  }

  // Parameters

  m_epsilon1 = EPS1*EPS1;
  m_epsilon2 = EPS2*EPS2;

  m_alpha1 = C1 / m_Wmax;
  m_alpha2 = C2 / m_Wmax;

  m_K = 0;
  m_TSteps = TRAINING_STEPS;
}

BGModelFuzzySom::~BGModelFuzzySom()
{
  for (int n = 0; n < m_heightSOM; n++)
    delete[] m_ppSOM[n];

  delete[] m_ppSOM;

  for (int n = 0; n < KERNEL; n++)
    delete[] m_ppW[n];

  delete[] m_ppW;
}

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;

  case 1:
    m_epsilon1 = 255.0*255.0*dvalue*dvalue*dvalue*dvalue;
    break;

  case 2:
    m_alpha2 = 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;
  }

  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);

    for (unsigned int i = 0; i < m_width; i++)
    {
      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_K = 0;

  return;
}

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;
  }

  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 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;

      // Find BMU

      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;
          }
        }
      }

      double fuzzyBG = 1.0;

      if (d2min < epsilon)
        fuzzyBG = d2min / epsilon;

      // Update SOM

      double alphamax = alpha*exp(FUZZYEXP*fuzzyBG);

      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];

          // speed hack.. avoid very small increment values. abs() is sloooow.

          double d;

          d = srcR - m_ppSOM[l][k].Red;
          if (d*d > DBL_MIN)
            m_ppSOM[l][k].Red += a*d;

          d = srcG - m_ppSOM[l][k].Green;
          if (d*d > DBL_MIN)
            m_ppSOM[l][k].Green += a*d;

          d = srcB - m_ppSOM[l][k].Blue;
          if (d*d > DBL_MIN)
            m_ppSOM[l][k].Blue += 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;

        // Set foreground image
        prgbFG[j][i].Red = prgbFG[j][i].Green = prgbFG[j][i].Blue = 0;
      }
    }
  }

  return;
}

--#-- END ./bgslibrary/algorithms/lb/BGModelFuzzySom.cpp --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelMog.cpp --#--
#include "BGModelMog.h"

using namespace bgslibrary::algorithms::lb;
using namespace bgslibrary::algorithms::lb::BGModelMogParams;

BGModelMog::BGModelMog(int width, int height) : BGModel(width, height)
{
  m_alpha = LEARNINGRATEMOG;
  m_threshold = THRESHOLDMOG*THRESHOLDMOG;
  m_noise = INITIALVARMOG;

  m_T = BGTHRESHOLDMOG;

  m_pMOG = new MOGDATA[NUMBERGAUSSIANS*m_width*m_height];
  m_pK = new int[m_width*m_height];

  MOGDATA *pMOG = m_pMOG;
  int *pK = m_pK;

  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->var.Red = 0.0;
      pMOG->var.Green = 0.0;
      pMOG->var.Blue = 0.0;

      pMOG->w = 0.0;
      pMOG->sortKey = 0.0;

      pMOG++;
    }

    pK[i] = 0;
  }
}

BGModelMog::~BGModelMog()
{
  delete[] m_pMOG;
  delete[] m_pK;
}

void BGModelMog::setBGModelParameter(int id, int value)
{
  double dvalue = (double)value / 255.0;

  switch (id)
  {
  case 0:
    m_threshold = 100.0*dvalue*dvalue;
    break;

  case 1:
    m_T = dvalue;
    break;

  case 2:
    m_alpha = dvalue*dvalue*dvalue;
    break;

  case 3:
    m_noise = 100.0*dvalue;;
    break;
  }

  return;
}

void BGModelMog::Init()
{
  MOGDATA *pMOG = m_pMOG;
  int *pK = m_pK;

  Image<BYTERGB> prgbSrc(m_SrcImage);

  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].var.Red = m_noise;
      pMOG[0].var.Green = m_noise;
      pMOG[0].var.Blue = m_noise;

      pMOG[0].w = 1.0;
      pMOG[0].sortKey = pMOG[0].w / sqrt(pMOG[0].var.Red + pMOG[0].var.Green + pMOG[0].var.Blue);

      pK[n] = 1;
      n++;

      pMOG += NUMBERGAUSSIANS;
    }
  }

  return;
}

void BGModelMog::Update()
{
  int kBG = 0;

  MOGDATA *pMOG = m_pMOG;
  int *pK = m_pK;

  Image<BYTERGB> prgbSrc(m_SrcImage);
  Image<BYTERGB> prgbBG(m_BGImage);
  Image<BYTERGB> prgbFG(m_FGImage);

  int n = 0;
  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;

      // Find matching distribution

      int kHit = -1;

      for (int k = 0; k < pK[n]; k++)
      {
        // 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;
        }
      }

      // Adjust parameters

      // 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]++;

        kHit = pK[n] - 1;

        if (pK[n] == 1)
          pMOG[kHit].w = 1.0;
        else
          pMOG[kHit].w = LEARNINGRATEMOG;

        pMOG[kHit].mu.Red = srcR;
        pMOG[kHit].mu.Green = srcG;
        pMOG[kHit].mu.Blue = srcB;

        pMOG[kHit].var.Red = m_noise;
        pMOG[kHit].var.Green = m_noise;
        pMOG[kHit].var.Blue = m_noise;
      }

      // Normalize weights

      double wsum = 0.0;

      for (int k = 0; k < pK[n]; k++)
        wsum += pMOG[k].w;

      double wfactor = 1.0 / wsum;

      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);
      }

      // Sort distributions

      for (int k = 0; k < kHit; k++)
      {
        if (pMOG[kHit].sortKey > pMOG[k].sortKey)
        {
          std::swap(pMOG[kHit], pMOG[k]);
          break;
        }
      }

      // Determine background distributions

      wsum = 0.0;

      for (int k = 0; k < pK[n]; k++)
      {
        wsum += pMOG[k].w;

        if (wsum > m_T)
        {
          kBG = 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;

      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;
}

--#-- END ./bgslibrary/algorithms/lb/BGModelMog.cpp --#--

--#-- START ./bgslibrary/algorithms/VuMeter/TBackgroundVuMeter.cpp --#--
#include "TBackgroundVuMeter.h"

//using namespace bgslibrary::algorithms::vumeter;

namespace bgslibrary
{
  namespace algorithms
  {
    namespace vumeter
    {
      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)
      {
        std::cout << "TBackgroundVuMeter()" << std::endl;
      }

      TBackgroundVuMeter::~TBackgroundVuMeter(void)
      {
        Clear();
        std::cout << "~TBackgroundVuMeter()" << std::endl;
      }

      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;
      }

      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;
      }

      int TBackgroundVuMeter::GetParameterCount(void)
      {
        return TBackground::GetParameterCount() + PROCESS_PAR_COUNT;
      }

      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;
      }

      std::string TBackgroundVuMeter::GetParameterValue(int nInd)
      {
        std::string csResult;
        int nNb;

        nNb = TBackground::GetParameterCount();

        if (nInd >= nNb)
        {
          nInd -= nNb;

          char buff[100];

          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;
          }

          csResult = buff;
        }
        else
          csResult = TBackground::GetParameterValue(nInd);

        return csResult;
      }

      int TBackgroundVuMeter::SetParameterValue(int nInd, std::string csNew)
      {
        int nErr = 0;

        int nNb;

        nNb = TBackground::GetParameterCount();

        if (nInd >= nNb)
        {
          nInd -= nNb;

          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);

        return nErr;
      }

      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;
      }

      bool TBackgroundVuMeter::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask)
      {
        bool bResult = true;
        int i;

        bResult = TBackground::isInitOk(pSource, pBackground, pMotionMask);

        if (pSource == NULL)
          bResult = false;

        if (m_nBinSize == 0)
          bResult = false;

        if (bResult)
        {
          i = (m_nBinSize != 0) ? 256 / m_nBinSize : 0;

          if (i != m_nBinCount || m_pHist == NULL)
            bResult = false;
        }

        if (bResult)
        {
          int nbl = pSource->height;
          int nbc = pSource->width;

          for (i = 0; i < m_nBinCount; ++i)
          {
            if (m_pHist[i] == NULL || m_pHist[i]->width != nbc || m_pHist[i]->height != nbl)
              bResult = false;
          }
        }

        return bResult;
      }

      int TBackgroundVuMeter::UpdateBackground(IplImage *pSource, IplImage *pBackground, IplImage *pMotionMask)
      {
        int nErr = 0;
        unsigned char *ptrs, *ptrb, *ptrm;
        float *ptr1, *ptr2;

        if (!isInitOk(pSource, pBackground, pMotionMask))
          nErr = Init(pSource);

        if (!nErr)
        {
          m_nCount++;
          int nbc = pSource->width;
          int nbl = pSource->height;
          unsigned char v = m_nBinSize;

          // multiplie tout par alpha
          for (int i = 0; i < m_nBinCount; ++i)
            cvConvertScale(m_pHist[i], m_pHist[i], m_fAlpha, 0.0);

          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);

            for (int c = 0; c < nbc; ++c, ptrs++, ptrb++, ptrm++)
            {
              // 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;

              *ptr1 += (float)(1.0 - m_fAlpha);
              *ptrm = (*ptr1 < m_fThreshold) ? 255 : 0;

              // recherche le bin du fond actuel
              i = *ptrb / v;

              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;
            }
          }

          if (m_nCount < 5)
            cvSetZero(pMotionMask);
        }

        return nErr;
      }

      IplImage *TBackgroundVuMeter::CreateTestImg()
      {
        IplImage *pImage = NULL;

        if (m_nBinCount > 0)
          pImage = cvCreateImage(cvSize(m_nBinCount, 100), IPL_DEPTH_8U, 3);

        if (pImage != NULL)
          cvSetZero(pImage);

        return pImage;
      }

      int TBackgroundVuMeter::UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd)
      {
        int nErr = 0;
        float *ptrf;

        if (pTest == NULL || !isInitOk(pSource, pBackground, pSource))
          nErr = 1;

        if (!nErr)
        {
          int nbl = pTest->height;
          int nbc = pTest->width;

          if (nbl != 100 || nbc != m_nBinCount)
            nErr = 1;

          if (nX < 0 || nX >= pSource->width || nY < 0 || nY >= pSource->height)
            nErr = 1;
        }

        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));
            }
          }

          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;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/VuMeter/TBackgroundVuMeter.cpp --#--

--#-- START ./bgslibrary/algorithms/VuMeter/TBackground.cpp --#--
#include "TBackground.h"

using namespace bgslibrary::algorithms::vumeter;

TBackground::TBackground(){
  std::cout << "TBackground()" << std::endl;
}

TBackground::~TBackground(){
  Clear();
  std::cout << "~TBackground()" << std::endl;
}

void TBackground::Clear(){}

void TBackground::Reset(){}

int TBackground::GetParameterCount(void){
  return 0;
}

std::string TBackground::GetParameterName(int nInd){
  return "";
}

std::string TBackground::GetParameterValue(int nInd){
  return "";
}

int TBackground::SetParameterValue(int nInd, std::string csNew){
  return 0;
}

int TBackground::Init(IplImage * pSource){
  return 0;
}

bool TBackground::isInitOk(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask)
{
  bool bResult = true;

  if (pSource == NULL || pSource->nChannels != 1 || pSource->depth != IPL_DEPTH_8U)
    bResult = false;

  if (bResult)
  {
    int nbl, nbc;
    nbl = pSource->height;
    nbc = pSource->width;

    if (pBackground == NULL || pBackground->width != nbc || pBackground->height != nbl || pBackground->imageSize != pSource->imageSize)
      bResult = false;

    if (pMotionMask == NULL || pMotionMask->width != nbc || pMotionMask->height != nbl || pMotionMask->imageSize != pSource->imageSize)
      bResult = false;
  }

  return bResult;
}

int TBackground::UpdateBackground(IplImage * pSource, IplImage *pBackground, IplImage *pMotionMask){
  return 0;
}

IplImage *TBackground::CreateTestImg()
{
  IplImage *pImage = cvCreateImage(cvSize(256, 256), IPL_DEPTH_8U, 3);

  if (pImage != NULL)
    cvSetZero(pImage);

  return pImage;
}

int TBackground::UpdateTest(IplImage *pSource, IplImage *pBackground, IplImage *pTest, int nX, int nY, int nInd)
{
  int nErr = 0;
  CvScalar Color;
  unsigned char *ptr;

  if (pTest == NULL || !isInitOk(pSource, pBackground, pSource))
    nErr = 1;

  if (!nErr)
  {
    if (pTest->width != 256 || pTest->height != 256 || pTest->nChannels != 3)
      nErr = 1;

    if (nX < 0 || nX > pSource->width || nY < 0 || nY > pSource->height)
      nErr = 1;

    switch (nInd)
    {
    case 0: Color = cvScalar(128, 0, 0); break;
    case 1: Color = cvScalar(0, 128, 0); break;
    case 2: Color = cvScalar(0, 0, 128); break;
    default: nErr = 1;
    }
  }

  if (!nErr)
  {
    int l, c;
    // recupere l'indice de la colonne
    ptr = (unsigned char *)(pTest->imageData);
    c = *ptr;

    // efface la colonne
    cvLine(pTest, cvPoint(c, 0), cvPoint(c, 255), cvScalar(0));
    *ptr += 1;

    //recupere la couleur du fond
    ptr = (unsigned char *)(pBackground->imageData + pBackground->widthStep * nY);
    ptr += nX;
    l = *ptr;

    // dessine la couleur
    cvLine(pTest, cvPoint(c, l - 5), cvPoint(c, l + 5), Color);

    //recupere la couleur du point
    ptr = (unsigned char *)(pSource->imageData + pSource->widthStep * nY);
    ptr += nX;
    l = *ptr;

    // dessine la couleur
    ptr = (unsigned char *)(pTest->imageData + pTest->widthStep * l);
    ptr += (c * 3) + nInd;
    *ptr = 255;
  }

  return nErr;
}

--#-- END ./bgslibrary/algorithms/VuMeter/TBackground.cpp --#--

--#-- START ./bgslibrary/algorithms/DPWrenGA.cpp --#--
#include "DPWrenGA.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPWrenGA::DPWrenGA() :
  IBGS(quote(DPWrenGA)),
  frameNumber(0), threshold(12.25f), 
  alpha(0.005f), learningFrames(30)
{
  debug_construction(DPWrenGA);
  initLoadSaveConfig(algorithmName);
}

DPWrenGA::~DPWrenGA() {
  debug_destruction(DPWrenGA);
}

void DPWrenGA::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold; //3.5f*3.5f;
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    params.Alpha() = alpha; //0.005f;
    params.LearningFrames() = learningFrames; //30;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPWrenGA::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "learningFrames" << learningFrames;
  fs << "showOutput" << showOutput;
}

void DPWrenGA::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["learningFrames"] >> learningFrames;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPWrenGA.cpp --#--

--#-- START ./bgslibrary/algorithms/GMG.cpp --#--
#include "GMG.h"

#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3

using namespace bgslibrary::algorithms;

GMG::GMG() : 
  IBGS(quote(GMG)),
  initializationFrames(20), decisionThreshold(0.7)
{
  debug_construction(GMG);
  initLoadSaveConfig(algorithmName);

  cv::initModule_video();
  cv::setUseOptimized(true);
  cv::setNumThreads(4);

  fgbg = cv::Algorithm::create<cv::BackgroundSubtractorGMG>("BackgroundSubtractor.GMG");
}

GMG::~GMG() {
  debug_destruction(GMG);
}

void GMG::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    fgbg->set("initializationFrames", initializationFrames);
    fgbg->set("decisionThreshold", decisionThreshold);
  }

  if (fgbg.empty()) {
    std::cerr << "Failed to create " + algorithmName << std::endl;
    return;
  }

  (*fgbg)(img_input, img_foreground);
  (*fgbg).getBackgroundImage(img_background);

  img_input.copyTo(img_segmentation);
  cv::add(img_input, cv::Scalar(100, 100, 0), img_segmentation, img_foreground);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void GMG::save_config(cv::FileStorage &fs) {
  fs << "initializationFrames" << initializationFrames;
  fs << "decisionThreshold" << decisionThreshold;
  fs << "showOutput" << showOutput;
}

void GMG::load_config(cv::FileStorage &fs) {
  fs["initializationFrames"] >> initializationFrames;
  fs["decisionThreshold"] >> decisionThreshold;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/GMG.cpp --#--

--#-- START ./bgslibrary/algorithms/DPZivkovicAGMM.cpp --#--
#include "DPZivkovicAGMM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPZivkovicAGMM::DPZivkovicAGMM() :
  IBGS(quote(DPZivkovicAGMM)),
  frameNumber(0), threshold(25.0f), 
  alpha(0.001f), gaussians(3)
{
  debug_construction(DPZivkovicAGMM);
  initLoadSaveConfig(algorithmName);
}

DPZivkovicAGMM::~DPZivkovicAGMM() {
  debug_destruction(DPZivkovicAGMM);
}

void DPZivkovicAGMM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold; //5.0f*5.0f;
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    params.Alpha() = alpha; //0.001f;
    params.MaxModes() = gaussians; //3;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPZivkovicAGMM::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "gaussians" << gaussians;
  fs << "showOutput" << showOutput;
}

void DPZivkovicAGMM::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["gaussians"] >> gaussians;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPZivkovicAGMM.cpp --#--

--#-- START ./bgslibrary/algorithms/FrameDifference.cpp --#--
#include "FrameDifference.h"

using namespace bgslibrary::algorithms;

FrameDifference::FrameDifference() :
  IBGS(quote(FrameDifference)),
  enableThreshold(true), threshold(15)
{
  debug_construction(FrameDifference);
  initLoadSaveConfig(algorithmName);
}

FrameDifference::~FrameDifference() {
  debug_destruction(FrameDifference);
}

void FrameDifference::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_background.empty()) {
    img_input.copyTo(img_background);
    return;
  }

  cv::absdiff(img_background, img_input, img_foreground);

  if (img_foreground.channels() == 3)
    cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY);

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);

  img_input.copyTo(img_background);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void FrameDifference::save_config(cv::FileStorage &fs) {
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void FrameDifference::load_config(cv::FileStorage &fs) {
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/FrameDifference.cpp --#--

--#-- START ./bgslibrary/algorithms/MixtureOfGaussianV2.cpp --#--
#include "MixtureOfGaussianV2.h"

using namespace bgslibrary::algorithms;

MixtureOfGaussianV2::MixtureOfGaussianV2() :
  IBGS(quote(MixtureOfGaussianV2)),
  alpha(0.05), enableThreshold(true), threshold(15)
{
  debug_construction(MixtureOfGaussianV2);
  initLoadSaveConfig(algorithmName);
}

MixtureOfGaussianV2::~MixtureOfGaussianV2() {
  debug_destruction(MixtureOfGaussianV2);
}

void MixtureOfGaussianV2::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
#if CV_MAJOR_VERSION >= 3
    mog = cv::createBackgroundSubtractorMOG2();
#endif
  }

  //------------------------------------------------------------------
  // BackgroundSubtractorMOG2
  // http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog2
  //
  // Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm.
  //
  // The class implements the Gaussian mixture model background subtraction described in:
  //  (1) Z.Zivkovic, Improved adaptive Gausian mixture model for background subtraction, International Conference Pattern Recognition, UK, August, 2004,
  //  The code is very fast and performs also shadow detection. Number of Gausssian components is adapted per pixel.
  //
  //  (2) Z.Zivkovic, F. van der Heijden, Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction,
  //  Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006.
  //  The algorithm similar to the standard Stauffer&Grimson algorithm with additional selection of the number of the Gaussian components based on:
  //    Z.Zivkovic, F.van der Heijden, Recursive unsupervised learning of finite mixture models, IEEE Trans. on Pattern Analysis and Machine Intelligence,
  //    vol.26, no.5, pages 651-656, 2004.
  //------------------------------------------------------------------

#if CV_MAJOR_VERSION == 2
  mog(img_input, img_foreground, alpha);
  mog.getBackgroundImage(img_background);
#elif CV_MAJOR_VERSION >= 3
  mog->apply(img_input, img_foreground, alpha);
  mog->getBackgroundImage(img_background);
#endif

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void MixtureOfGaussianV2::save_config(cv::FileStorage &fs) {
  fs << "alpha" << alpha;
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void MixtureOfGaussianV2::load_config(cv::FileStorage &fs) {
  fs["alpha"] >> alpha;
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/MixtureOfGaussianV2.cpp --#--

--#-- START ./bgslibrary/algorithms/DPGrimsonGMM.cpp --#--
#include "DPGrimsonGMM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPGrimsonGMM::DPGrimsonGMM() :
  IBGS(quote(DPGrimsonGMM)),
  frameNumber(0), threshold(9.0), 
  alpha(0.01), gaussians(3)
{
  debug_construction(DPGrimsonGMM);
  initLoadSaveConfig(algorithmName);
}

DPGrimsonGMM::~DPGrimsonGMM() {
  debug_destruction(DPGrimsonGMM);
}

void DPGrimsonGMM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold; //3.0f*3.0f;
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    //params.Alpha() = 0.001f;
    params.Alpha() = alpha; //0.01f;
    params.MaxModes() = gaussians; //3;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPGrimsonGMM::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "gaussians" << gaussians;
  fs << "showOutput" << showOutput;
}

void DPGrimsonGMM::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["gaussians"] >> gaussians;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPGrimsonGMM.cpp --#--

--#-- START ./bgslibrary/algorithms/MixtureOfGaussianV1.cpp --#--
#include "MixtureOfGaussianV1.h"

#if CV_MAJOR_VERSION == 2

using namespace bgslibrary::algorithms;

MixtureOfGaussianV1::MixtureOfGaussianV1() :
  IBGS(quote(MixtureOfGaussianV1)),
  alpha(0.05), enableThreshold(true), threshold(15)
{
  debug_construction(MixtureOfGaussianV1);
  initLoadSaveConfig(algorithmName);
}

MixtureOfGaussianV1::~MixtureOfGaussianV1() {
  debug_destruction(MixtureOfGaussianV1);
}

void MixtureOfGaussianV1::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  //------------------------------------------------------------------
  // BackgroundSubtractorMOG
  // http://opencv.itseez.com/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractormog
  //
  // Gaussian Mixture-based Backbround/Foreground Segmentation Algorithm.
  //
  // The class implements the algorithm described in:
  //   P. KadewTraKuPong and R. Bowden,
  //   An improved adaptive background mixture model for real-time tracking with shadow detection,
  //   Proc. 2nd European Workshp on Advanced Video-Based Surveillance Systems, 2001
  //------------------------------------------------------------------

  mog(img_input, img_foreground, alpha);
  mog.getBackgroundImage(img_background);

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

  if (img_foreground.empty())
    img_foreground = cv::Mat::zeros(img_input.size(), img_input.type());

  if (img_background.empty())
    img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void MixtureOfGaussianV1::save_config(cv::FileStorage &fs) {
  fs << "alpha" << alpha;
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void MixtureOfGaussianV1::load_config(cv::FileStorage &fs) {
  fs["alpha"] >> alpha;
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/MixtureOfGaussianV1.cpp --#--

--#-- START ./bgslibrary/algorithms/VuMeter.cpp --#--
#include "VuMeter.h"

using namespace bgslibrary::algorithms;

VuMeter::VuMeter() :
  IBGS(quote(VuMeter)),
  enableFilter(true), binSize(8), 
  alpha(0.995), threshold(0.03)
{
  debug_construction(VuMeter);
  initLoadSaveConfig(algorithmName);
}

VuMeter::~VuMeter() {
  debug_destruction(VuMeter);
  cvReleaseImage(&mask);
  cvReleaseImage(&background);
  cvReleaseImage(&gray);
}

void VuMeter::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame = cvCloneImage(&_frame);

  if (firstTime) {
    int w = img_input.size().width;
    int h = img_input.size().height;

    bgs.SetAlpha(alpha);
    bgs.SetBinSize(binSize);
    bgs.SetThreshold(threshold);

    gray = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
    cvCvtColor(frame, gray, CV_RGB2GRAY);

    background = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
    cvCopy(gray, background);

    mask = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
    cvZero(mask);
  }
  else
    cvCvtColor(frame, gray, CV_RGB2GRAY);

  bgs.UpdateBackground(gray, background, mask);
  img_foreground = cv::cvarrToMat(mask);
  img_background = cv::cvarrToMat(background);

  if (enableFilter) {
    cv::erode(img_foreground, img_foreground, cv::Mat());
    cv::medianBlur(img_foreground, img_foreground, 5);
  }

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&frame);

  firstTime = false;
}

void VuMeter::save_config(cv::FileStorage &fs) {
  fs << "enableFilter" << enableFilter;
  fs << "binSize" << binSize;
  fs << "alpha" << alpha;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void VuMeter::load_config(cv::FileStorage &fs) {
  fs["enableFilter"] >> enableFilter;
  fs["binSize"] >> binSize;
  fs["alpha"] >> alpha;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/VuMeter.cpp --#--

--#-- START ./bgslibrary/algorithms/T2FMRF_UV.cpp --#--
#include "T2FMRF_UV.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

T2FMRF_UV::T2FMRF_UV() :
  IBGS(quote(T2FMRF_UV)),
  frameNumber(0), threshold(9.0), alpha(0.01), 
  km(2.f), kv(0.9f), gaussians(3)
{
  debug_construction(T2FMRF_UV);
  initLoadSaveConfig(algorithmName);
}

T2FMRF_UV::~T2FMRF_UV() {
  debug_destruction(T2FMRF_UV);
}

void T2FMRF_UV::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold;
    params.HighThreshold() = 2 * params.LowThreshold();
    params.Alpha() = alpha;
    params.MaxModes() = gaussians;
    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

    bgs.Initalize(params);
    bgs.InitModel(frame_data);

    old_labeling = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    old = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);

    mrf.height = height;
    mrf.width = width;
    mrf.Build_Classes_OldLabeling_InImage_LocalEnergy();

    gmm = bgs.gmm();
    hmm = bgs.hmm();

    firstTime = false;
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  cvCopy(lowThresholdMask.Ptr(), old);

  /************************************************************************/
  /* the code for MRF, it can be noted when using other methods   */
  /************************************************************************/
  //the optimization process is done when the foreground detection is stable,
  if (frameNumber >= 10)
  {
    mrf.background2 = frame_data.Ptr();
    mrf.in_image = lowThresholdMask.Ptr();
    mrf.out_image = lowThresholdMask.Ptr();
    mrf.InitEvidence2(gmm, hmm, old_labeling);
    mrf.ICM2();
    cvCopy(mrf.out_image, lowThresholdMask.Ptr());
  }

  cvCopy(old, old_labeling);

  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  frameNumber++;
}

void T2FMRF_UV::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "km" << km;
  fs << "kv" << kv;
  fs << "gaussians" << gaussians;
  fs << "showOutput" << showOutput;
}

void T2FMRF_UV::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["km"] >> km;
  fs["kv"] >> kv;
  fs["gaussians"] >> gaussians;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/T2FMRF_UV.cpp --#--

--#-- START ./bgslibrary/algorithms/PixelBasedAdaptiveSegmenter.cpp --#--
#include "PixelBasedAdaptiveSegmenter.h"

using namespace bgslibrary::algorithms;

PixelBasedAdaptiveSegmenter::PixelBasedAdaptiveSegmenter() :
  IBGS(quote(PixelBasedAdaptiveSegmenter)),
  enableInputBlur(true), enableOutputBlur(true),
  alpha(7.0), beta(1.0), N(20), Raute_min(2), R_incdec(0.05), R_lower(18),
  R_scale(5), T_dec(0.05), T_inc(1), T_init(18), T_lower(2), T_upper(200)
{
  debug_construction(PixelBasedAdaptiveSegmenter);
  initLoadSaveConfig(algorithmName);
}

PixelBasedAdaptiveSegmenter::~PixelBasedAdaptiveSegmenter() {
  debug_destruction(PixelBasedAdaptiveSegmenter);
}

void PixelBasedAdaptiveSegmenter::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    pbas.setAlpha(alpha);
    pbas.setBeta(beta);
    pbas.setN(N);
    pbas.setRaute_min(Raute_min);
    pbas.setR_incdec(R_incdec);
    pbas.setR_lower(R_lower);
    pbas.setR_scale(R_scale);
    pbas.setT_dec(T_dec);
    pbas.setT_inc(T_inc);
    pbas.setT_init(T_init);
    pbas.setT_lower(T_lower);
    pbas.setT_upper(T_upper);
  }

  cv::Mat img_input_new;
  if (enableInputBlur)
    cv::GaussianBlur(img_input, img_input_new, cv::Size(5, 5), 1.5);
  else
    img_input.copyTo(img_input_new);

  pbas.process(&img_input_new, &img_foreground);
  img_background = cv::Mat::zeros(img_input.size(), img_input.type());

  if (enableOutputBlur)
    cv::medianBlur(img_foreground, img_foreground, 5);

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void PixelBasedAdaptiveSegmenter::save_config(cv::FileStorage &fs) {
  fs << "enableInputBlur" << enableInputBlur;
  fs << "enableOutputBlur" << enableOutputBlur;
  fs << "alpha" << alpha;
  fs << "beta" << beta;
  fs << "N" << N;
  fs << "Raute_min" << Raute_min;
  fs << "R_incdec" << R_incdec;
  fs << "R_lower" << R_lower;
  fs << "R_scale" << R_scale;
  fs << "T_dec" << T_dec;
  fs << "T_inc" << T_inc;
  fs << "T_init" << T_init;
  fs << "T_lower" << T_lower;
  fs << "T_upper" << T_upper;
  fs << "showOutput" << showOutput;
}

void PixelBasedAdaptiveSegmenter::load_config(cv::FileStorage &fs) {
  fs["enableInputBlur"] >> enableInputBlur;
  fs["enableOutputBlur"] >> enableOutputBlur;
  fs["alpha"] >> alpha;
  fs["beta"] >> beta;
  fs["N"] >> N;
  fs["Raute_min"] >> Raute_min;
  fs["R_incdec"] >> R_incdec;
  fs["R_lower"] >> R_lower;
  fs["R_scale"] >> R_scale;
  fs["T_dec"] >> T_dec;
  fs["T_inc"] >> T_inc;
  fs["T_init"] >> T_init;
  fs["T_lower"] >> T_lower;
  fs["T_upper"] >> T_upper;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/PixelBasedAdaptiveSegmenter.cpp --#--

--#-- START ./bgslibrary/algorithms/LOBSTER.cpp --#--
#include "LOBSTER.h"

using namespace bgslibrary::algorithms;

LOBSTER::LOBSTER() :
  IBGS(quote(LOBSTER)),
  pLOBSTER(nullptr),
  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);
}

LOBSTER::~LOBSTER() {
  debug_destruction(LOBSTER);
  if (pLOBSTER)
    delete pLOBSTER;
}

void LOBSTER::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    pLOBSTER = new lbsp::BackgroundSubtractorLOBSTER(
      fRelLBSPThreshold, nLBSPThresholdOffset, nDescDistThreshold,
      nColorDistThreshold, nBGSamples, nRequiredBGSamples);

    pLOBSTER->initialize(img_input, cv::Mat(img_input.size(), CV_8UC1, cv::Scalar_<uchar>(255)));
    firstTime = false;
  }

  pLOBSTER->apply(img_input, img_foreground);
  pLOBSTER->getBackgroundImage(img_background);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
}

void LOBSTER::save_config(cv::FileStorage &fs) {
  fs << "fRelLBSPThreshold" << fRelLBSPThreshold;
  fs << "nLBSPThresholdOffset" << nLBSPThresholdOffset;
  fs << "nDescDistThreshold" << nDescDistThreshold;
  fs << "nColorDistThreshold" << nColorDistThreshold;
  fs << "nBGSamples" << nBGSamples;
  fs << "nRequiredBGSamples" << nRequiredBGSamples;
  fs << "showOutput" << showOutput;
}

void LOBSTER::load_config(cv::FileStorage &fs) {
  fs["fRelLBSPThreshold"] >> fRelLBSPThreshold;
  fs["nLBSPThresholdOffset"] >> nLBSPThresholdOffset;
  fs["nDescDistThreshold"] >> nDescDistThreshold;
  fs["nColorDistThreshold"] >> nColorDistThreshold;
  fs["nBGSamples"] >> nBGSamples;
  fs["nRequiredBGSamples"] >> nRequiredBGSamples;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/LOBSTER.cpp --#--

--#-- START ./bgslibrary/algorithms/LBMixtureOfGaussians.cpp --#--
#include "LBMixtureOfGaussians.h"

using namespace bgslibrary::algorithms;

LBMixtureOfGaussians::LBMixtureOfGaussians() :
  IBGS(quote(LBMixtureOfGaussians)),
  sensitivity(81), bgThreshold(83), 
  learningRate(59), noiseVariance(206)
{
  debug_construction(LBMixtureOfGaussians);
  initLoadSaveConfig(algorithmName);
}

LBMixtureOfGaussians::~LBMixtureOfGaussians() {
  debug_destruction(LBMixtureOfGaussians);
  delete m_pBGModel;
}

void LBMixtureOfGaussians::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  IplImage* frame = cvCloneImage(&_frame);

  if (firstTime) {
    int w = img_input.size().width;
    int h = img_input.size().height;

    m_pBGModel = new lb::BGModelMog(w, h);
    m_pBGModel->InitModel(frame);
  }

  m_pBGModel->setBGModelParameter(0, sensitivity);
  m_pBGModel->setBGModelParameter(1, bgThreshold);
  m_pBGModel->setBGModelParameter(2, learningRate);
  m_pBGModel->setBGModelParameter(3, noiseVariance);

  m_pBGModel->UpdateModel(frame);

  img_foreground = cv::cvarrToMat(m_pBGModel->GetFG());
  img_background = cv::cvarrToMat(m_pBGModel->GetBG());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&frame);

  firstTime = false;
}

void LBMixtureOfGaussians::save_config(cv::FileStorage &fs) {
  fs << "sensitivity" << sensitivity;
  fs << "bgThreshold" << bgThreshold;
  fs << "learningRate" << learningRate;
  fs << "noiseVariance" << noiseVariance;
  fs << "showOutput" << showOutput;
}

void LBMixtureOfGaussians::load_config(cv::FileStorage &fs) {
  fs["sensitivity"] >> sensitivity;
  fs["bgThreshold"] >> bgThreshold;
  fs["learningRate"] >> learningRate;
  fs["noiseVariance"] >> noiseVariance;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/LBMixtureOfGaussians.cpp --#--

--#-- START ./bgslibrary/algorithms/WeightedMovingVariance.cpp --#--
#include "WeightedMovingVariance.h"

using namespace bgslibrary::algorithms;

WeightedMovingVariance::WeightedMovingVariance() :
  IBGS(quote(WeightedMovingVariance)),
  enableWeight(true), enableThreshold(true), threshold(15)
{
  debug_construction(WeightedMovingVariance);
  initLoadSaveConfig(algorithmName);
}

WeightedMovingVariance::~WeightedMovingVariance() {
  debug_destruction(WeightedMovingVariance);
}

void WeightedMovingVariance::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_input_prev_1.empty()) {
    img_input.copyTo(img_input_prev_1);
    return;
  }

  if (img_input_prev_2.empty()) {
    img_input_prev_1.copyTo(img_input_prev_2);
    img_input.copyTo(img_input_prev_1);
    return;
  }

  cv::Mat img_input_f(img_input.size(), CV_32F);
  img_input.convertTo(img_input_f, CV_32F, 1. / 255.);

  cv::Mat img_input_prev_1_f(img_input.size(), CV_32F);
  img_input_prev_1.convertTo(img_input_prev_1_f, CV_32F, 1. / 255.);

  cv::Mat img_input_prev_2_f(img_input.size(), CV_32F);
  img_input_prev_2.convertTo(img_input_prev_2_f, CV_32F, 1. / 255.);

  // Weighted mean
  cv::Mat img_mean_f(img_input.size(), CV_32F);

  if (enableWeight)
    img_mean_f = ((img_input_f * 0.5) + (img_input_prev_1_f * 0.3) + (img_input_prev_2_f * 0.2));
  else
    img_mean_f = ((img_input_f * 0.3) + (img_input_prev_1_f * 0.3) + (img_input_prev_2_f * 0.3));

  // Weighted variance
  cv::Mat img_1_f(img_input.size(), CV_32F);
  cv::Mat img_2_f(img_input.size(), CV_32F);
  cv::Mat img_3_f(img_input.size(), CV_32F);
  cv::Mat img_4_f(img_input.size(), CV_32F);

  if (enableWeight)
  {
    img_1_f = computeWeightedVariance(img_input_f, img_mean_f, 0.5);
    img_2_f = computeWeightedVariance(img_input_prev_1_f, img_mean_f, 0.3);
    img_3_f = computeWeightedVariance(img_input_prev_2_f, img_mean_f, 0.2);
    img_4_f = (img_1_f + img_2_f + img_3_f);
  }
  else
  {
    img_1_f = computeWeightedVariance(img_input_f, img_mean_f, 0.3);
    img_2_f = computeWeightedVariance(img_input_prev_1_f, img_mean_f, 0.3);
    img_3_f = computeWeightedVariance(img_input_prev_2_f, img_mean_f, 0.3);
    img_4_f = (img_1_f + img_2_f + img_3_f);
  }

  // Standard deviation
  cv::Mat img_sqrt_f(img_input.size(), CV_32F);
  cv::sqrt(img_4_f, img_sqrt_f);
  cv::Mat img_sqrt(img_input.size(), CV_8U);
  double minVal, maxVal;
  minVal = 0.; maxVal = 1.;
  img_sqrt_f.convertTo(img_sqrt, CV_8U, 255.0 / (maxVal - minVal), -minVal);
  img_sqrt.copyTo(img_foreground);

  if (img_foreground.channels() == 3)
    cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY);

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);

  img_background = cv::Mat::zeros(img_input.size(), img_input.type());
  img_background.copyTo(img_bgmodel);

  img_input_prev_1.copyTo(img_input_prev_2);
  img_input.copyTo(img_input_prev_1);

  firstTime = false;
}

cv::Mat WeightedMovingVariance::computeWeightedVariance(const cv::Mat &img_input_f, const cv::Mat &img_mean_f, const double weight)
{
  //ERROR in return (weight * ((cv::abs(img_input_f - img_mean_f))^2.));

  cv::Mat img_f_absdiff(img_input_f.size(), CV_32F);
  cv::absdiff(img_input_f, img_mean_f, img_f_absdiff);
  cv::Mat img_f_pow(img_input_f.size(), CV_32F);
  cv::pow(img_f_absdiff, 2.0, img_f_pow);
  cv::Mat img_f = weight * img_f_pow;

  return img_f;
}

void WeightedMovingVariance::save_config(cv::FileStorage &fs) {
  fs << "enableWeight" << enableWeight;
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void WeightedMovingVariance::load_config(cv::FileStorage &fs) {
  fs["enableWeight"] >> enableWeight;
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/WeightedMovingVariance.cpp --#--

--#-- START ./bgslibrary/algorithms/AdaptiveBackgroundLearning.cpp --#--
#include "AdaptiveBackgroundLearning.h"

using namespace bgslibrary::algorithms;

AdaptiveBackgroundLearning::AdaptiveBackgroundLearning() :
  IBGS(quote(AdaptiveBackgroundLearning)),
  alpha(0.05), maxLearningFrames(-1), currentLearningFrame(0), minVal(0.0),
  maxVal(1.0), enableThreshold(true), threshold(15)
{
  debug_construction(AdaptiveBackgroundLearning);
  initLoadSaveConfig(algorithmName);
}

AdaptiveBackgroundLearning::~AdaptiveBackgroundLearning() {
  debug_destruction(AdaptiveBackgroundLearning);
}

void AdaptiveBackgroundLearning::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_background.empty())
    img_input.copyTo(img_background);

  cv::Mat img_input_f(img_input.size(), CV_32F);
  img_input.convertTo(img_input_f, CV_32F, 1. / 255.);

  cv::Mat img_background_f(img_background.size(), CV_32F);
  img_background.convertTo(img_background_f, CV_32F, 1. / 255.);

  cv::Mat img_diff_f(img_input.size(), CV_32F);
  cv::absdiff(img_input_f, img_background_f, img_diff_f);

  // Adaptive learning phase (controlled by maxLearningFrames)
  if ((maxLearningFrames > 0 && currentLearningFrame < maxLearningFrames) || maxLearningFrames == -1) {
    img_background_f = alpha*img_input_f + (1 - alpha)*img_background_f;
    img_background_f.convertTo(img_background, CV_8U, 255.0 / (maxVal - minVal), -minVal);
    
    if (maxLearningFrames > 0 && currentLearningFrame < maxLearningFrames)
      currentLearningFrame++;
  }

  img_diff_f.convertTo(img_foreground, CV_8UC1, 255.0 / (maxVal - minVal), -minVal);

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void AdaptiveBackgroundLearning::save_config(cv::FileStorage &fs) {
  fs << "alpha" << alpha;
  fs << "maxLearningFrames" << maxLearningFrames;
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void AdaptiveBackgroundLearning::load_config(cv::FileStorage &fs) {
  fs["alpha"] >> alpha;
  fs["maxLearningFrames"] >> maxLearningFrames;
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/AdaptiveBackgroundLearning.cpp --#--

--#-- START ./bgslibrary/algorithms/KDE/NPBGmodel.cpp --#--
#include "memory.h"

#include "NPBGmodel.h"

//#ifdef _DEBUG
//#undef THIS_FILE
//static char THIS_FILE[] = __FILE__;
////#define new DEBUG_NEW
//#endif

using namespace bgslibrary::algorithms::kde;

NPBGmodel::NPBGmodel() {}

NPBGmodel::~NPBGmodel() {
  delete Sequence;
  delete PixelQTop;
  delete TemporalBuffer;
  delete TemporalMask;
  delete AccMask;
  //delete SDbinsImage;
}

NPBGmodel::NPBGmodel(unsigned int Rows,
  unsigned int Cols,
  unsigned int ColorChannels,
  unsigned int Length,
  unsigned int pTimeWindowSize,
  unsigned int bg_suppression_time)
{
  imagesize = Rows*Cols*ColorChannels;

  rows = Rows;
  cols = Cols;
  color_channels = ColorChannels;

  SampleSize = Length;

  TimeWindowSize = pTimeWindowSize;

  Sequence = new unsigned char[imagesize*Length];
  Top = 0;
  memset(Sequence, 0, imagesize*Length);

  PixelQTop = new unsigned char[rows*cols];

  // temporalBuffer
  TemporalBufferLength = (TimeWindowSize / Length > 2 ? TimeWindowSize / Length : 2);
  TemporalBuffer = new unsigned char[imagesize*TemporalBufferLength];
  TemporalMask = new unsigned char[rows*cols*TemporalBufferLength];

  TemporalBufferTop = 0;

  AccMask = new unsigned int[rows*cols];

  ResetMaskTh = bg_suppression_time;
}

void NPBGmodel::AddFrame(unsigned char *ImageBuffer)
{
  memcpy(Sequence + Top*imagesize, ImageBuffer, imagesize);
  Top = (Top + 1) % SampleSize;

  memset(PixelQTop, (unsigned char)Top, rows*cols);

  memcpy(TemporalBuffer, ImageBuffer, imagesize);
}

--#-- END ./bgslibrary/algorithms/KDE/NPBGmodel.cpp --#--

--#-- START ./bgslibrary/algorithms/KDE/NPBGSubtractor.cpp --#--
#include <assert.h>
#include <math.h>
#include <string.h>

#include "NPBGSubtractor.h"

//#ifdef _DEBUG
//#undef THIS_FILE
//static char THIS_FILE[]=__FILE__;
//#define new DEBUG_NEW
//#endif
//using namespace bgslibrary::algorithms::kde;

namespace bgslibrary
{
  namespace algorithms
  {
    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;

        for (i = 0; i < rows*cols * 3; i += 3)
        {
          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);

          r2 = (unsigned int)((g + 10) * s);
          r3 = (unsigned int)((r + 10) * s);

          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);
        }
      }

      void UpdateDiffHist(unsigned char * image1, unsigned char * image2, DynamicMedianHistogram * pHist)
      {
        unsigned int j;
        int bin, diff;

        unsigned int  imagesize = pHist->imagesize;
        unsigned char histbins = pHist->histbins;
        unsigned char *pAbsDiffHist = pHist->Hist;

        int histbins_1 = histbins - 1;

        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 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;

          histindex = j*histbins;

          while (sum < medianCount)
          {
            sum += Hist[histindex + bin];
            bin++;
          }

          bin--;

          MedianBins[j] = bin;
          AccSum[j] = sum;
        }
      }

      DynamicMedianHistogram BuildAbsDiffHist(unsigned char * pSequence,
        unsigned int rows,
        unsigned int cols,
        unsigned int color_channels,
        unsigned int SequenceLength,
        unsigned int histbins)
      {

        unsigned int imagesize = rows*cols*color_channels;
        unsigned int i;

        DynamicMedianHistogram Hist;

        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];

        memset(pAbsDiffHist, 0, rows*cols*color_channels*histbins);

        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;

        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;

          UpdateDiffHist(image1, image2, &Hist);
        }

        FindHistMedians(&Hist);

        return Hist;
      }

      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;

          bin = MedianBins[j];
          sum = AccSum[j];

          x1 = sum - Hist[histindex + bin];
          x2 = sum;

          // interpolate to get the median
          // x1 < 50 % < x2

          v = 1.04 * ((double)bin - (double)(x2 - medianCount) / (double)(x2 - x1));
          v = (v <= MinSD ? MinSD : v);

          // convert sd to kernel table bin

          bin = (int)(v >= MaxSD ? kernelbins - 1 : floor((v - MinSD)*kernelbinfactor + .5));

          assert(bin >= 0 && bin < kernelbins);

          pSDs[j] = bin;
        }
      }

      //////////////////////////////////////////////////////////////////////
      // Construction/Destruction
      //////////////////////////////////////////////////////////////////////

      NPBGSubtractor::NPBGSubtractor() {}

      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;
      }

      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)
      {

        rows = prows;
        cols = pcols;
        color_channels = pcolor_channels;
        imagesize = rows*cols*color_channels;
        SdEstimateFlag = pSDEstimationFlag;
        UseColorRatiosFlag = pUseColorRatiosFlag;
        //SampleSize = SequenceLength;

        AdaptBGFlag = FALSE;
        //
        SubsetFlag = TRUE;

        UpdateSDRate = 0;

        BGModel = new NPBGmodel(rows, cols, color_channels, SequenceLength, pTimeWindowSize, 500);

        Pimage1 = new double[rows*cols];
        Pimage2 = new double[rows*cols];

        tempFrame = new unsigned char[rows*cols * 3];

        imageindex = new ImageIndex;
        imageindex->List = new unsigned int[rows*cols];

        // error checking
        if (BGModel == NULL)
          return 0;

        return 1;
      }

      void NPBGSubtractor::AddFrame(unsigned char *ImageBuffer)
      {
        if (UseColorRatiosFlag && color_channels == 3)
          BGR2SnGnRn(ImageBuffer, ImageBuffer, rows, cols);

        BGModel->AddFrame(ImageBuffer);
      }

      void NPBGSubtractor::Estimation()
      {
        int SampleSize = BGModel->SampleSize;

        memset(BGModel->TemporalMask, 0, rows*cols*BGModel->TemporalBufferLength);

        //BGModel->AccMask= new unsigned int [rows*cols];
        memset(BGModel->AccMask, 0, rows*cols * sizeof(unsigned int));

        unsigned char *pSDs = new unsigned char[rows*cols*color_channels];

        //DynamicMedianHistogram AbsDiffHist;

        int Abshistbins = 20;

        TimeIndex = 0;

        // 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));
        }

        BGModel->SDbinsImage = pSDs;

        // Generate the Kernel
        KernelTable = new KernelLUTable(KERNELHALFWIDTH, SEGMAMIN, SEGMAMAX, SEGMABINS);
      }

      /*********************************************************************/

      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;

        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;

            j++;
          }
          j += 2;
        }

        imageIndex->cnt = i;
      }

      /*********************************************************************/

      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;

        int rows, cols;

        int Nbr[9];
        unsigned int i, j;
        // unsigned int k;
        unsigned int idx;

        rows = (int)urows;
        cols = (int)ucols;

        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;

        memset(outImage, 0, 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];

            if (Pimage[idx] < hyst_th)
              outImage[idx] = 255;
          }
        }

        // build index for out image
        BuildImageIndex(outImage, outIndex, urows, 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;

        int rows, cols;

        int Nbr[9];
        unsigned int i, j;
        unsigned int idx; // k

        rows = (int)urows;
        cols = (int)ucols;

        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;

        memset(outImage, 0, 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++;

          if (j >= 9 || Pimage[idx] <= hyst_th)
            outImage[idx] = 255;
        }

        BuildImageIndex(outImage, outIndex, rows, cols);
      }

      /*********************************************************************/

      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;

        int rows, cols;

        int Nbr[9];
        unsigned int i, j;
        // unsigned int k;
        unsigned int idx;

        rows = (int)urows;
        cols = (int)ucols;

        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;


        memset(outImage, 0, 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

        BuildImageIndex(outImage, outIndex, rows, cols);

      }

      /*********************************************************************/

      void ShrinkOperatorIndexed(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;

        int rows, cols;

        int Nbr[9];
        unsigned int i, j;
        unsigned int idx; // k

        rows = (int)urows;
        cols = (int)ucols;

        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;


        memset(outImage, 0, 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++;
          }

          if (j >= 9) {
            outImage[idx] = 255;
          }
        }

        BuildImageIndex(outImage, outIndex, rows, cols);
      }

      /*********************************************************************/

      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 */


        int r, c;
        unsigned char *p, *n, *nw, *ne, *e, *w, *s, *sw, *se;
        unsigned int v;
        unsigned int TH;

        unsigned char * ResultPtr;

        TH = 255 * th;

        memset(ResultIm, 0, rows*cols);

        p = Image + cols + 1;
        ResultPtr = ResultIm + cols + 1;

        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;
        }
      }

      /*********************************************************************/

      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 TemporalBufferTop = (int)BGModel->TemporalBufferTop;
        unsigned char * pTemporalBuffer = BGModel->TemporalBuffer;
        unsigned char * pTemporalMask = BGModel->TemporalMask;
        int TemporalBufferLength = BGModel->TemporalBufferLength;

        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;

        int TimeWindowSize = BGModel->TimeWindowSize;
        int SampleSize = BGModel->SampleSize;

        int TemporalBufferNext;

        unsigned int imagebuffersize = rows*cols*color_channels;
        unsigned int imagespatialsize = rows*cols;

        unsigned char mask;

        unsigned int histindex;
        unsigned char diff;
        unsigned char bin;

        static int TBCount = 0;

        unsigned char * pTBbase1, *pTBbase2;
        unsigned char * pModelbase1, *pModelbase2;

        rate = TimeWindowSize / SampleSize;
        rate = (rate > 2) ? rate : 2;


        TemporalBufferNext = (TemporalBufferTop + 1)
          % TemporalBufferLength;

        // pointers to Masks : Top and Next
        unsigned char * pTMaskTop = pTemporalMask + TemporalBufferTop*imagespatialsize;
        unsigned char * pTMaskNext = pTemporalMask + TemporalBufferNext*imagespatialsize;

        // 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); // 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;
            }
          }
        } // 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++;

        // estimate SDs

        if (SdEstimateFlag && UpdateSDRate && ((TimeIndex) % UpdateSDRate == 0))
        {
          double MaxSD = KernelTable->maxsegma;
          double MinSD = KernelTable->minsegma;
          int KernelBins = KernelTable->segmabins;

          unsigned char * pSDs = BGModel->SDbinsImage;

          FindHistMedians(&(AbsDiffHist));
          EstimateSDsFromAbsDiffHist(&(AbsDiffHist), pSDs, imagebuffersize, MinSD, MaxSD, KernelBins);
        }

        TimeIndex++;
      }

      /*********************************************************************/

      void DisplayPropabilityImageWithThresholding(double * Pimage,
        unsigned char * DisplayImage,
        double Threshold,
        unsigned int rows,
        unsigned int cols)
      {
        double p;

        for (unsigned int i = 0; i < rows*cols; i++)
        {
          p = Pimage[i];

          DisplayImage[i] = (p > Threshold) ? 0 : 255;
        }
      }

      /*********************************************************************/

      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;

        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;

        //unsigned char * SaturationImage = FilteredFGImage;

        // default sigmas .. to be removed.
        // double sigma1;
        // double sigma2;
        // double sigma3;

        // sigma1 = 2.25;
        // sigma2 = 2.25;
        // sigma3 = 2.25;

        double p;
        double th;

        double alpha;

        alpha = AlphaValue;

        /* intialize FG image */

        memset(FGImage, 0, rows*cols);

        //Threshold=1;
        th = Threshold * SampleSize;

        double sum = 0, kernel1, kernel2, kernel3;
        int k, g;


        if (color_channels == 1)
        {
          // gray scale

          int kernelbase;

          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

          unsigned int ig;
          int base;

          // int kernelbase1;
          int kernelbase2;
          int kernelbase3;

          unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1;

          double beta = 3.0;    // minimum bound on the range.
          double betau = 100.0;

          double beta_over_alpha = beta / alpha;
          double betau_over_alpha = betau / alpha;


          double brightness_lowerbound = 1 - alpha;
          double brightness_upperbound = 1 + alpha;
          int x1, x2;
          unsigned int SubsampleCount;

          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 if (UseColorRatiosFlag && !SubsetFlag)
        {
          // color ratios

          unsigned int ig;
          int base;
          int bin;

          int kernelbase1;
          int kernelbase2;
          int kernelbase3;

          unsigned int kerneltablewidth = 2 * KernelHalfWidth + 1;

          int gmin, gmax;
          double gfactor;

          gmax = 200;
          gmin = 10;

          gfactor = (KernelMaxSigma - KernelMinSigma) / (double)(gmax - gmin);

          for (i = 0, ig = 0; i < imagesize; i += 3, ig++)
          {

            bin = (int)floor(((alpha * 16 - KernelMinSigma)*KernelBins) / (KernelMaxSigma - KernelMinSigma));

            kernelbase1 = bin*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];

              if (g < gmin)
                bin = 0;
              else if (g > gmax)
                bin = KernelBins - 1;
              else
                bin = (int)((g - gmin) * gfactor + 0.5);

              kernelbase1 = bin*kerneltablewidth;

              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;
          }
        }
        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)
            {
              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;
          }
        }

        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);
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/KDE/NPBGSubtractor.cpp --#--

--#-- START ./bgslibrary/algorithms/KDE/KernelTable.cpp --#--
#include <math.h>

#include "KernelTable.h"

#ifndef PI
#define PI 3.141592653589793f
#endif

using namespace bgslibrary::algorithms::kde;

KernelLUTable::KernelLUTable()
{
  std::cout << "KernelLUTable()" << std::endl;
}

KernelLUTable::~KernelLUTable()
{
  delete kerneltable;
  delete kernelsums;
  std::cout << "~KernelLUTable()" << std::endl;
}

KernelLUTable::KernelLUTable(int KernelHalfWidth, double Segmamin, double Segmamax, int Segmabins)
{
  std::cout << "KernelLUTable()" << std::endl;

  double C1, C2, v, segma, sum;
  int bin, b;

  minsegma = Segmamin;
  maxsegma = Segmamax;
  segmabins = Segmabins;
  tablehalfwidth = KernelHalfWidth;

  // Generate the Kernel

  // allocate memory for the Kernal Table
  kerneltable = new double[segmabins*(2 * KernelHalfWidth + 1)];
  kernelsums = new double[segmabins];

  double segmastep = (maxsegma - minsegma) / segmabins;
  double y;

  for (segma = minsegma, bin = 0; bin < segmabins; segma += segmastep, bin++)
  {
    C1 = 1 / (sqrt(2 * PI)*segma);
    C2 = -1 / (2 * segma*segma);

    b = (2 * KernelHalfWidth + 1)*bin;
    sum = 0;

    for (int x = 0; x <= KernelHalfWidth; x++)
    {
      y = x / 1.0;
      v = C1*exp(C2*y*y);
      kerneltable[b + KernelHalfWidth + x] = v;
      kerneltable[b + KernelHalfWidth - x] = v;
      sum += 2 * v;
    }

    sum -= C1;

    kernelsums[bin] = sum;

    // Normailization
    for (int x = 0; x <= KernelHalfWidth; x++)
    {
      v = kerneltable[b + KernelHalfWidth + x] / sum;
      kerneltable[b + KernelHalfWidth + x] = v;
      kerneltable[b + KernelHalfWidth - x] = v;
    }
  }
}

--#-- END ./bgslibrary/algorithms/KDE/KernelTable.cpp --#--

--#-- START ./bgslibrary/algorithms/T2FMRF_UM.cpp --#--
#include "T2FMRF_UM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

T2FMRF_UM::T2FMRF_UM() :
  IBGS(quote(T2FMRF_UM)),
  frameNumber(0), threshold(9.0), alpha(0.01), 
  km(2.f), kv(0.9f), gaussians(3)
{
  debug_construction(T2FMRF_UM);
  initLoadSaveConfig(algorithmName);
}

T2FMRF_UM::~T2FMRF_UM() {
  debug_destruction(T2FMRF_UM);
}

void T2FMRF_UM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    width = img_input.size().width;
    height = img_input.size().height;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold;
    params.HighThreshold() = 2 * params.LowThreshold();
    params.Alpha() = alpha;
    params.MaxModes() = gaussians;
    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

    bgs.Initalize(params);
    bgs.InitModel(frame_data);

    mrf.height = height;
    mrf.width = width;
    mrf.Build_Classes_OldLabeling_InImage_LocalEnergy();

    old_labeling = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    old = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);

    gmm = bgs.gmm();
    hmm = bgs.hmm();

    firstTime = false;
  }

  lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

  highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
  highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  cvCopy(lowThresholdMask.Ptr(), old);

  /************************************************************************/
  /* the code for MRF, it can be noted when using other methods   */
  /************************************************************************/
  //the optimization process is done when the foreground detection is stable,
  if (frameNumber >= 10)
  {
    mrf.background2 = frame_data.Ptr();
    mrf.in_image = lowThresholdMask.Ptr();
    mrf.out_image = lowThresholdMask.Ptr();
    mrf.InitEvidence2(gmm, hmm, old_labeling);
    // mrf.InitEvidence2(gmm, hmm, lowThresholdMask.Ptr());
    mrf.ICM2();
    cvCopy(mrf.out_image, lowThresholdMask.Ptr());
  }

  cvCopy(old, old_labeling);

  // lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();
  lowThresholdMask.ReleaseImage();
  highThresholdMask.ReleaseImage();

  frameNumber++;
}

void T2FMRF_UM::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "km" << km;
  fs << "kv" << kv;
  fs << "gaussians" << gaussians;
  fs << "showOutput" << showOutput;
}

void T2FMRF_UM::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["km"] >> km;
  fs["kv"] >> kv;
  fs["gaussians"] >> gaussians;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/T2FMRF_UM.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiCue.cpp --#--
#include "MultiCue.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

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))
{
  debug_construction(MultiCue);
  //----------------------------------
  //	User adjustable parameters
  //----------------------------------
  g_iTrainingPeriod = 20;											//the training period								(The parameter t in the paper)
  g_iT_ModelThreshold = 1;										//the threshold for texture-model based BGS.		(The parameter tau_T in the paper)
  g_iC_ModelThreshold = 10;										//the threshold for appearance based verification.  (The parameter tau_A in the paper)

  g_fLearningRate = 0.05f;											//the learning rate for background models.			(The parameter alpha in the paper)

  g_nTextureTrainVolRange = 15;									//the codebook size factor for texture models.		(The parameter k in the paper)
  g_nColorTrainVolRange = 20;										//the codebook size factor for color models.		(The parameter eta_1 in the paper)

  g_bAbsorptionEnable = TRUE;										//If TRUE, cache-book is also modeled for ghost region removal.
  g_iAbsortionPeriod = 200;										//the period to absorb static ghost regions

  g_iRWidth = 160, g_iRHeight = 120;								//Frames are precessed after reduced in this size .

  //------------------------------------
  //	For codebook maintenance
  //------------------------------------
  g_iBackClearPeriod = 300;										//the period to clear background models
  g_iCacheClearPeriod = 30;										//the period to clear cache-book models

  //------------------------------------
  //	Initialization of other parameters
  //------------------------------------
  g_nNeighborNum = 6, g_nRadius = 2;
  g_fConfidenceThre = g_iT_ModelThreshold / (float)g_nNeighborNum;	//the final decision threshold

  g_iFrameCount = 0;
  g_bForegroundMapEnable = FALSE;									//TRUE only when BGS is successful
  g_bModelMemAllocated = FALSE;									//To handle memory..
  g_bNonModelMemAllocated = FALSE;								//To handle memory..

  initLoadSaveConfig(algorithmName);
}

MultiCue::~MultiCue() {
  debug_destruction(MultiCue);
  Destroy();
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//											the main function to background modeling and subtraction									   //																   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  // Step 1: Background Modeling
  IplImage _frame = cvIplImage(img_input);
  IplImage* frame = cvCloneImage(&_frame);

  int width = img_input.size().width;
  int height = img_input.size().height;
  IplImage* result_image = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
  cvSetZero(result_image);
  if (g_iFrameCount <= g_iTrainingPeriod) {
    BackgroundModeling_Par(frame);
    g_iFrameCount++;
  }
  // Step 2: Background Subtraction
  else {
    g_bForegroundMapEnable = FALSE;

    ForegroundExtraction(frame);
    UpdateModel_Par();

    // Get BGS Results
    GetForegroundMap(result_image, NULL);
  }

  img_background = cv::Mat::zeros(img_input.size(), img_input.type());
  img_foreground = cv::cvarrToMat(result_image, TRUE);
  cvReleaseImage(&result_image);
  cvReleaseImage(&frame);

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void MultiCue::save_config(cv::FileStorage &fs) {
  fs << "g_fLearningRate" << g_fLearningRate;
  fs << "g_iAbsortionPeriod" << g_iAbsortionPeriod;
  fs << "g_iC_ModelThreshold" << g_iC_ModelThreshold;
  fs << "g_iT_ModelThreshold" << g_iT_ModelThreshold;
  fs << "g_iBackClearPeriod" << g_iBackClearPeriod;
  fs << "g_iCacheClearPeriod" << g_iCacheClearPeriod;
  fs << "g_nNeighborNum" << g_nNeighborNum;
  fs << "g_nRadius" << g_nRadius;
  fs << "g_nTextureTrainVolRange" << g_nTextureTrainVolRange;
  fs << "g_bAbsorptionEnable" << g_bAbsorptionEnable;
  fs << "g_iTrainingPeriod" << g_iTrainingPeriod;
  fs << "g_iRWidth" << g_iRWidth;
  fs << "g_iRHeight" << g_iRHeight;
  fs << "showOutput" << showOutput;
}

void MultiCue::load_config(cv::FileStorage &fs) {
  fs["g_fLearningRate"] >> g_fLearningRate;
  fs["g_iAbsortionPeriod"] >> g_iAbsortionPeriod;
  fs["g_iC_ModelThreshold"] >> g_iC_ModelThreshold;
  fs["g_iT_ModelThreshold"] >> g_iT_ModelThreshold;
  fs["g_iBackClearPeriod"] >> g_iBackClearPeriod;
  fs["g_iCacheClearPeriod"] >> g_iCacheClearPeriod;
  fs["g_nNeighborNum"] >> g_nNeighborNum;
  fs["g_nRadius"] >> g_nRadius;
  fs["g_nTextureTrainVolRange"] >> g_nTextureTrainVolRange;
  fs["g_bAbsorptionEnable"] >> g_bAbsorptionEnable;
  fs["g_iTrainingPeriod"] >> g_iTrainingPeriod;
  fs["g_iRWidth"] >> g_iRWidth;
  fs["g_iRHeight"] >> g_iRHeight;
  fs["showOutput"] >> showOutput;
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//													the system initialization function													   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::Initialize(IplImage* frame)
{
  int i, j;

  g_iHeight = frame->height;
  g_iWidth = frame->width;

  Destroy();

  //--------------------------------------------------------
  // memory initialization
  //--------------------------------------------------------
  g_ResizedFrame = cvCreateImage(cvSize(g_iRWidth, g_iRHeight), IPL_DEPTH_8U, 3);

  g_aGaussFilteredFrame = (uchar***)malloc(sizeof(uchar**)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) {
    g_aGaussFilteredFrame[i] = (uchar**)malloc(sizeof(uchar*)*g_iRWidth);
    for (j = 0; j < g_iRWidth; j++) g_aGaussFilteredFrame[i][j] = (uchar*)malloc(sizeof(uchar) * 3);
  }

  g_aXYZFrame = (uchar***)malloc(sizeof(uchar**)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) {
    g_aXYZFrame[i] = (uchar**)malloc(sizeof(uchar*)*g_iRWidth);
    for (j = 0; j < g_iRWidth; j++) g_aXYZFrame[i][j] = (uchar*)malloc(sizeof(uchar) * 3);
  }

  g_aLandmarkArray = (uchar**)malloc(sizeof(uchar*)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) g_aLandmarkArray[i] = (uchar*)malloc(sizeof(uchar)*g_iRWidth);

  g_aResizedForeMap = (uchar**)malloc(sizeof(uchar*)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) g_aResizedForeMap[i] = (uchar*)malloc(sizeof(uchar)*g_iRWidth);

  g_aForegroundMap = (uchar**)malloc(sizeof(uchar*)*g_iHeight);
  for (i = 0; i < g_iHeight; i++) g_aForegroundMap[i] = (uchar*)malloc(sizeof(uchar)*g_iWidth);

  g_aUpdateMap = (BOOL**)malloc(sizeof(BOOL*)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) g_aUpdateMap[i] = (BOOL*)malloc(sizeof(BOOL)*g_iRWidth);

  //Bound Box Related..
  int iElementNum = 300;
  g_BoundBoxInfo = (BoundingBoxInfo*)malloc(sizeof(BoundingBoxInfo));
  g_BoundBoxInfo->m_iArraySize = iElementNum;
  g_BoundBoxInfo->m_iBoundBoxNum = iElementNum;

  g_BoundBoxInfo->m_aLeft = (short*)malloc(sizeof(short)* iElementNum); g_BoundBoxInfo->m_aRight = (short*)malloc(sizeof(short)* iElementNum);
  g_BoundBoxInfo->m_aBottom = (short*)malloc(sizeof(short)* iElementNum); g_BoundBoxInfo->m_aUpper = (short*)malloc(sizeof(short)* iElementNum);

  g_BoundBoxInfo->m_aRLeft = (short*)malloc(sizeof(short)* iElementNum); g_BoundBoxInfo->m_aRRight = (short*)malloc(sizeof(short)* iElementNum);
  g_BoundBoxInfo->m_aRBottom = (short*)malloc(sizeof(short)* iElementNum); g_BoundBoxInfo->m_aRUpper = (short*)malloc(sizeof(short)* iElementNum);

  g_BoundBoxInfo->m_ValidBox = (BOOL*)malloc(sizeof(BOOL)* iElementNum);

  //--------------------------------------------------------
  // texture model related
  //--------------------------------------------------------
  T_AllocateTextureModelRelatedMemory();

  //--------------------------------------------------------
  // color moddel related
  //--------------------------------------------------------
  C_AllocateColorModelRelatedMemory();

  g_bModelMemAllocated = TRUE;
  g_bNonModelMemAllocated = TRUE;

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//												the function to release allocated memories											       //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::Destroy()
{
  if (g_bModelMemAllocated == FALSE && g_bNonModelMemAllocated == FALSE) return;

  //short nNeighborNum = g_nNeighborNum;

  if (g_bModelMemAllocated == TRUE) {
    T_ReleaseTextureModelRelatedMemory();
    C_ReleaseColorModelRelatedMemory();

    g_bModelMemAllocated = FALSE;
  }

  if (g_bNonModelMemAllocated == TRUE) {

    cvReleaseImage(&g_ResizedFrame);

    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) free(g_aGaussFilteredFrame[i][j]);
      free(g_aGaussFilteredFrame[i]);
    }
    free(g_aGaussFilteredFrame);

    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) free(g_aXYZFrame[i][j]);
      free(g_aXYZFrame[i]);
    }
    free(g_aXYZFrame);

    for (int i = 0; i < g_iRHeight; i++) free(g_aLandmarkArray[i]);
    free(g_aLandmarkArray);

    for (int i = 0; i < g_iRHeight; i++) free(g_aResizedForeMap[i]);
    free(g_aResizedForeMap);

    for (int i = 0; i < g_iHeight; i++) free(g_aForegroundMap[i]);
    free(g_aForegroundMap);

    for (int i = 0; i < g_iRHeight; i++) free(g_aUpdateMap[i]);
    free(g_aUpdateMap);

    free(g_BoundBoxInfo->m_aLeft); free(g_BoundBoxInfo->m_aRight); free(g_BoundBoxInfo->m_aBottom); free(g_BoundBoxInfo->m_aUpper);
    free(g_BoundBoxInfo->m_aRLeft); free(g_BoundBoxInfo->m_aRRight); free(g_BoundBoxInfo->m_aRBottom); free(g_BoundBoxInfo->m_aRUpper);
    free(g_BoundBoxInfo->m_ValidBox);
    free(g_BoundBoxInfo);

    g_bNonModelMemAllocated = FALSE;
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the preprocessing function		    											   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::PreProcessing(IplImage* frame) {

  //image resize
  ReduceImageSize(frame, g_ResizedFrame);

  //Gaussian filtering
  GaussianFiltering(g_ResizedFrame, g_aGaussFilteredFrame);

  //color space conversion
  BGR2HSVxyz_Par(g_aGaussFilteredFrame, g_aXYZFrame);
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the background modeling function												   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::BackgroundModeling_Par(IplImage* frame) {

  //initialization
  if (g_iFrameCount == 0) Initialize(frame);

  //Step1: pre-processing
  PreProcessing(frame);

  int iH_Start = g_nRadius, iH_end = g_iRHeight - g_nRadius;
  int iW_Start = g_nRadius, iW_end = g_iRWidth - g_nRadius;

  float fLearningRate = g_fLearningRate * 4;

  //Step2: background modeling
  for (int i = iH_Start; i < iH_end; i++) {
    for (int j = iW_Start; j < iW_end; j++) {
      point center;
      center.m_nX = j;
      center.m_nY = i;

      T_ModelConstruction(g_nTextureTrainVolRange, fLearningRate, g_aXYZFrame, center, g_aNeighborDirection[i][j], g_TextureModel[i][j]);
      C_CodebookConstruction(g_aXYZFrame[i][j], j, i, g_nColorTrainVolRange, fLearningRate, g_ColorModel[i][j]);
    }
  }

  //Step3: Clear non-essential codewords
  if (g_iFrameCount == g_iTrainingPeriod) {
    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) {
        T_ClearNonEssentialEntries(g_iTrainingPeriod, g_TextureModel[i][j]);

        C_ClearNonEssentialEntries(g_iTrainingPeriod, g_ColorModel[i][j]);
      }
    }
    g_iFrameCount++;
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the background subtraction function							                       //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::ForegroundExtraction(IplImage* frame) {

  //Step1:pre-processing
  PreProcessing(frame);

  //Step3: texture-model based process
  T_GetConfidenceMap_Par(g_aXYZFrame, g_aTextureConfMap, g_aNeighborDirection, g_TextureModel);

  //Step2: color-model based verification
  CreateLandmarkArray_Par(g_fConfidenceThre, g_nColorTrainVolRange, g_aTextureConfMap, g_nNeighborNum, g_aXYZFrame, g_aNeighborDirection,
    g_TextureModel, g_ColorModel, g_aLandmarkArray);

  //Step3: verification procedures
  PostProcessing(frame);

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the post-processing function													   //												
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::PostProcessing(IplImage* frame) {

  //Step1: morphological operation
  MorphologicalOpearions(g_aLandmarkArray, g_aResizedForeMap, 0.5, 5, g_iRWidth, g_iRHeight);
  g_bForegroundMapEnable = TRUE;

  //Step2: labeling
  int** aLabelTable = (int**)malloc(sizeof(int*)*g_iRHeight);
  for (int i = 0; i < g_iRHeight; i++)  aLabelTable[i] = (int*)malloc(sizeof(int)*g_iRWidth);

  int iLabelCount;
  Labeling(g_aResizedForeMap, &iLabelCount, aLabelTable);

  //Step3: getting bounding boxes for each candidate fore-blob
  SetBoundingBox(iLabelCount, aLabelTable);

  //Step4: size  and appearance based verification
  BoundBoxVerification(frame, g_aResizedForeMap, g_BoundBoxInfo);

  //Step5: Foreground Region
  RemovingInvalidForeRegions(g_aResizedForeMap, g_BoundBoxInfo);

  for (int i = 0; i < g_iRHeight; i++) free(aLabelTable[i]);
  free(aLabelTable);
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the background-model update function			                                   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::UpdateModel_Par() {
  //short nNeighborNum = g_nNeighborNum;

  //Step1: update map construction
  for (int i = 0; i < g_iRHeight; i++) {
    for (int j = 0; j < g_iRWidth; j++) {
      g_aUpdateMap[i][j] = TRUE;
    }
  }

  for (int k = 0; k < g_BoundBoxInfo->m_iBoundBoxNum; k++) {
    if (g_BoundBoxInfo->m_ValidBox[k] == TRUE) {
      for (int i = g_BoundBoxInfo->m_aRUpper[k]; i <= g_BoundBoxInfo->m_aRBottom[k]; i++) {
        for (int j = g_BoundBoxInfo->m_aRLeft[k]; j <= g_BoundBoxInfo->m_aRRight[k]; j++) {
          g_aUpdateMap[i][j] = FALSE;
        }
      }
    }
  }

  //Step2: update
  int iH_Start = g_nRadius, iH_End = g_iRHeight - g_nRadius;
  int iW_Start = g_nRadius, iW_End = g_iRWidth - g_nRadius;

  float fLearningRate = (float)g_fLearningRate;

  for (int i = iH_Start; i < iH_End; i++) {
    for (int j = iW_Start; j < iW_End; j++) {

      point center;

      center.m_nX = j;
      center.m_nY = i;

      if (g_aUpdateMap[i][j] == TRUE) {
        //model update
        T_ModelConstruction(g_nTextureTrainVolRange, fLearningRate, g_aXYZFrame, center, g_aNeighborDirection[i][j], g_TextureModel[i][j]);
        C_CodebookConstruction(g_aXYZFrame[i][j], j, i, g_nColorTrainVolRange, fLearningRate, g_ColorModel[i][j]);

        //clearing non-essential codewords
        T_ClearNonEssentialEntries(g_iBackClearPeriod, g_TextureModel[i][j]);
        C_ClearNonEssentialEntries(g_iBackClearPeriod, g_ColorModel[i][j]);

      }
      else {
        if (g_bAbsorptionEnable == TRUE) {
          //model update
          T_ModelConstruction(g_nTextureTrainVolRange, fLearningRate, g_aXYZFrame, center, g_aNeighborDirection[i][j], g_TCacheBook[i][j]);
          C_CodebookConstruction(g_aXYZFrame[i][j], j, i, g_nColorTrainVolRange, fLearningRate, g_CCacheBook[i][j]);

          //clearing non-essential codewords
          T_Absorption(g_iAbsortionPeriod, center, g_aTContinuousCnt, g_aTReferredIndex, g_TextureModel[i][j], g_TCacheBook[i][j]);
          C_Absorption(g_iAbsortionPeriod, center, g_aCContinuousCnt, g_aCReferredIndex, g_ColorModel[i][j], g_CCacheBook[i][j]);

        }
      }

      //clearing non-essential codewords for cache-books
      if (g_bAbsorptionEnable == TRUE) {
        T_ClearNonEssentialEntriesForCachebook(g_aLandmarkArray[i][j], g_aTReferredIndex[i][j], 10, g_TCacheBook[i][j]);
        C_ClearNonEssentialEntriesForCachebook(g_aLandmarkArray[i][j], g_aCReferredIndex[i][j], 10, g_CCacheBook[i][j]);
      }
    }
  }

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the color based verification function											   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::CreateLandmarkArray_Par(float fConfThre, short nTrainVolRange, float**aConfMap, int iNehborNum, uchar*** aXYZ,
  point*** aNeiDir, TextureModel**** TModel, ColorModel*** CModel, uchar**aLandmarkArr) {

  int iBound_w = g_iRWidth - g_nRadius;
  int iBound_h = g_iRHeight - g_nRadius;

  for (int i = 0; i < g_iRHeight; i++) {
    for (int j = 0; j < g_iRWidth; j++) {

      if (i < g_nRadius || i >= iBound_h || j < g_nRadius || j >= iBound_w) {
        aLandmarkArr[i][j] = 0;
        continue;
      }

      double tmp = aConfMap[i][j];

      if (tmp > fConfThre) aLandmarkArr[i][j] = 255;
      else {
        aLandmarkArr[i][j] = 0;
        //Calculating texture amount in the background
        double dBackAmt, dCnt;
        dBackAmt = dCnt = 0;

        for (int m = 0; m < iNehborNum; m++) {
          for (int n = 0; n < TModel[i][j][m]->m_iNumEntries; n++) {
            dBackAmt += TModel[i][j][m]->m_Codewords[n]->m_fMean;
            dCnt++;
          }
        }
        dBackAmt /= dCnt;

        //Calculating texture amount in the input image
        double dTemp, dInputAmt = 0;
        for (int m = 0; m < iNehborNum; m++) {
          dTemp = aXYZ[i][j][2] - aXYZ[aNeiDir[i][j][m].m_nY][aNeiDir[i][j][m].m_nX][2];

          if (dTemp >= 0) dInputAmt += dTemp;
          else dInputAmt -= dTemp;

        }

        //If there are only few textures in both background and input image
        if (dBackAmt < 50 && dInputAmt < 50) {
          //Conduct color codebook matching
          BOOL bMatched = FALSE;
          for (int m = 0; m < CModel[i][j]->m_iNumEntries; m++) {

            int iMatchedCount = 0;
            for (int n = 0; n < 3; n++) {
              double dLowThre = CModel[i][j]->m_Codewords[m]->m_dMean[n] - nTrainVolRange - 10;
              double dHighThre = CModel[i][j]->m_Codewords[m]->m_dMean[n] + nTrainVolRange + 10;

              if (dLowThre <= aXYZ[i][j][n] && aXYZ[i][j][n] <= dHighThre) iMatchedCount++;
            }

            if (iMatchedCount == 3) {
              bMatched = TRUE;
              break;
            }

          }
          if (bMatched == TRUE) aLandmarkArr[i][j] = 125;
          else aLandmarkArr[i][j] = 255;

        }

      }
    }
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//													the Gaussian filtering function								                           //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::GaussianFiltering(IplImage* frame, uchar*** aFilteredFrame) {

  double dSigma = 0.7;

  if (dSigma == 0) {
    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) {
        aFilteredFrame[i][j][0] = frame->imageData[i*frame->widthStep + j * 3];
        aFilteredFrame[i][j][1] = frame->imageData[i*frame->widthStep + j * 3 + 1];
        aFilteredFrame[i][j][2] = frame->imageData[i*frame->widthStep + j * 3 + 2];
      }
    }
  }
  else
  {
    cv::Mat temp_img = cv::cvarrToMat(frame, TRUE);
    cv::GaussianBlur(temp_img, temp_img, cv::Size(7, 7), dSigma);

    //Store results into aFilteredFrame[][][]
    IplImage _img = cvIplImage(temp_img);
    IplImage* img = cvCloneImage(&_img);

    //int iWidthStep = img->widthStep;

    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) {
        aFilteredFrame[i][j][0] = img->imageData[i*img->widthStep + j * 3];
        aFilteredFrame[i][j][1] = img->imageData[i*img->widthStep + j * 3 + 1];
        aFilteredFrame[i][j][2] = img->imageData[i*img->widthStep + j * 3 + 2];
      }
    }

    cvReleaseImage(&img);
  }
}

//------------------------------------------------------------------------------------------------------------------------------------//
//														the image resize function									                 //
//------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::ReduceImageSize(IplImage* SrcImage, IplImage* DstImage) {

  int iChannel = 3;

  double dResizeFactor_w = (double)g_iWidth / (double)g_iRWidth;
  double dResizeFactor_h = (double)g_iHeight / (double)g_iRHeight;


  for (int i = 0; i < g_iRHeight; i++) {
    for (int j = 0; j < g_iRWidth; j++) {
      int iSrcY = (int)(i*dResizeFactor_h);
      int iSrcX = (int)(j*dResizeFactor_w);

      for (int k = 0; k < iChannel; k++) DstImage->imageData[i*DstImage->widthStep + j * 3 + k]
        = SrcImage->imageData[iSrcY*SrcImage->widthStep + iSrcX * 3 + k];
    }
  }

}

//------------------------------------------------------------------------------------------------------------------------------------//
//											    the color space conversion function                                                   //
//------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::BGR2HSVxyz_Par(uchar*** aBGR, uchar*** aXYZ) {

  double dH_ratio = (2 * PI) / 360;

  for (int i = 0; i < g_iRHeight; i++) {

    double dR, dG, dB;
    double dMax, dMin;

    double dH, dS, dV;

    for (int j = 0; j < g_iRWidth; j++) {

      dB = (double)(aBGR[i][j][0]) / 255;
      dG = (double)(aBGR[i][j][1]) / 255;
      dR = (double)(aBGR[i][j][2]) / 255;


      //Find max, min
      dMin = MIN3(dR, dG, dB);
      dMax = MAX3(dR, dG, dB);


      //Get V
      dV = dMax;

      //Get S, H
      if (dV == 0) dS = dH = 0;
      else {

        //S value
        dS = (dMax - dMin) / dMax;

        if (dS == 0) dH = 0;
        else {
          //H value
          if (dMax == dR) {
            dH = 60 * (dG - dB) / dS;
            if (dH < 0) dH = 360 + dH;
          }
          else if (dMax == dG) dH = 120 + 60 * (dB - dR) / dS;
          else dH = 240 + 60 * (dR - dG) / dS;
        }
      }
      dH = dH * dH_ratio;

      aXYZ[i][j][0] = (uchar)((dV * dS * cos(dH) * 127.5) + 127.5);		//X  --> 0~255
      aXYZ[i][j][1] = (uchar)((dV * dS * sin(dH) * 127.5) + 127.5);		//Y  --> 0~255
      aXYZ[i][j][2] = (uchar)(dV * 255);							    //Z  --> 0~255

    }
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//												the function to get enlarged confidence map												   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::GetEnlargedMap(float** aOriginMap, float** aEnlargedMap) {
  int i, j;

  short nSrcX;
  short nSrcY;

  double dEWweight, dNSweight;
  double dEWtop, dEWbottom;

  double dNW; //north-west
  double dNE; //north-east
  double dSW; //south-west
  double dSE; //south-east

  double dScaleFactor_w = ((double)g_iWidth) / ((double)g_iRWidth);
  double dScaleFactor_h = ((double)g_iHeight) / ((double)g_iRHeight);

  for (i = 0; i < g_iHeight; i++) {
    for (j = 0; j < g_iWidth; j++) {
      //backward mapping
      nSrcY = (int)(i / dScaleFactor_h);
      nSrcX = (int)(j / dScaleFactor_w);

      if (nSrcY == (g_iRHeight - 1)) nSrcY -= 1;
      if (nSrcX == (g_iRWidth - 1)) nSrcX -= 1;

      dEWweight = i / dScaleFactor_h - nSrcY;
      dNSweight = j / dScaleFactor_w - nSrcX;

      dNW = (double)aOriginMap[nSrcY][nSrcX];
      dNE = (double)aOriginMap[nSrcY][nSrcX + 1];
      dSW = (double)aOriginMap[nSrcY + 1][nSrcX];
      dSE = (double)aOriginMap[nSrcY + 1][nSrcX + 1];

      // interpolation
      dEWtop = dNW + dEWweight * (dNE - dNW);
      dEWbottom = dSW + dEWweight * (dSE - dSW);

      aEnlargedMap[i][j] = (float)(dEWtop + dNSweight * (dEWbottom - dEWtop));
    }
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//										   				 the morphological operation function				    				   		   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::MorphologicalOpearions(uchar** aInput, uchar** aOutput, double dThresholdRatio, int iMaskSize, int iWidth, int iHeight) {

  int iOffset = (int)(iMaskSize / 2);

  int iBound_w = iWidth - iOffset;
  int iBound_h = iHeight - iOffset;

  uchar** aTemp = (uchar**)malloc(sizeof(uchar*)*iHeight);
  for (int i = 0; i < iHeight; i++) {
    aTemp[i] = (uchar*)malloc(sizeof(uchar)*iWidth);
  }

  for (int i = 0; i < iHeight; i++) {
    for (int j = 0; j < iWidth; j++) {
      aTemp[i][j] = aInput[i][j];
    }
  }

  int iThreshold = (int)(iMaskSize*iMaskSize*dThresholdRatio);
  for (int i = 0; i < iHeight; i++) {
    for (int j = 0; j < iWidth; j++) {

      if (i < iOffset || i >= iBound_h || j < iOffset || j >= iBound_w) {
        aOutput[i][j] = 0;
        continue;
      }

      int iCnt = 0;
      for (int m = -iOffset; m <= iOffset; m++) {
        for (int n = -iOffset; n <= iOffset; n++) {
          if (aTemp[i + m][j + n] == 255) iCnt++;
        }
      }

      if (iCnt >= iThreshold) aOutput[i][j] = 255;
      else aOutput[i][j] = 0;
    }
  }


  for (int i = 0; i < iHeight; i++) {
    free(aTemp[i]);
  }
  free(aTemp);

}
//-----------------------------------------------------------------------------------------------------------------------------------------//
//													2-raster scan pass based labeling function											   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::Labeling(uchar** aBinaryArray, int* pLabelCount, int** aLabelTable) {
  int x, y, i;		// pass 1,2
  int cnt = 0;		// pass 1
  int label = 0;	// pass 2

  int iSize = g_iRWidth * g_iRHeight;
  int iTableSize = iSize / 2;

  // initialize , table1 table1
  int* aPass1 = (int*)malloc(iSize * sizeof(int));
  int* aTable1 = (int*)malloc(iSize / 2 * sizeof(int));
  int* aTable2 = (int*)malloc(iSize / 2 * sizeof(int));

  memset(aPass1, 0, (iSize) * sizeof(int));
  for (y = 1; y < (g_iRHeight); y++) {
    for (x = 1; x < (g_iRWidth); x++) {
      aLabelTable[y][x] = 0;
    }
  }

  for (i = 0; i < iTableSize; i++) {
    aTable1[i] = i;
  }
  memset(aTable2, 0, iTableSize * sizeof(int));

  // pass 1
  for (y = 1; y < (g_iRHeight); y++) {
    for (x = 1; x < (g_iRWidth); x++) {

      if (aBinaryArray[y][x] == 255) { // fore ground??
        int up, le;

        up = aPass1[(y - 1)*(g_iRWidth)+(x)]; // up  index
        le = aPass1[(y)*(g_iRWidth)+(x - 1)]; // left index

        // case
        if (up == 0 && le == 0) {
          ++cnt;
          aPass1[y * g_iRWidth + x] = cnt;

        }
        else if (up != 0 && le != 0) {
          if (up > le) {
            aPass1[y *g_iRWidth + x] = le;
            aTable1[up] = aTable1[le]; // update table1 table1
          }
          else {
            aPass1[y * g_iRWidth + x] = up;
            aTable1[le] = aTable1[up]; // update table1 table1
          }
        }
        else {
          aPass1[y * g_iRWidth + x] = up + le;
        }

      }

    }
  }

  // pass 2
  for (y = 1; y < (g_iRHeight); y++) {
    for (x = 1; x < (g_iRWidth); x++) {

      if (aPass1[y * g_iRWidth + x]) {
        int v = aTable1[aPass1[y * g_iRWidth + x]];

        if (aTable2[v] == 0) {
          ++label;
          aTable2[v] = label;
        }

        aLabelTable[y][x] = aTable2[v];
      }
    }
  }

  *pLabelCount = label;

  free(aPass1);
  free(aTable1);
  free(aTable2);
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//									the function to set bounding boxes for each candidate foreground regions					    	   //																									   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::SetBoundingBox(int iLabelCount, int** aLabelTable) {
  int iBoundBoxIndex;

  g_BoundBoxInfo->m_iBoundBoxNum = iLabelCount;

  for (int i = 0; i < g_BoundBoxInfo->m_iBoundBoxNum; i++) {
    g_BoundBoxInfo->m_aRLeft[i] = 9999;		//left
    g_BoundBoxInfo->m_aRUpper[i] = 9999;	//top
    g_BoundBoxInfo->m_aRRight[i] = 0;		//right
    g_BoundBoxInfo->m_aRBottom[i] = 0;		//bottom
  }

  //Step1: Set tight bounding boxes
  for (int i = 1; i < g_iRHeight; i++) {
    for (int j = 1; j < g_iRWidth; j++) {

      if (aLabelTable[i][j] == 0) continue;

      iBoundBoxIndex = aLabelTable[i][j] - 1;

      if (g_BoundBoxInfo->m_aRLeft[iBoundBoxIndex] > j) g_BoundBoxInfo->m_aRLeft[iBoundBoxIndex] = j;		//left
      if (g_BoundBoxInfo->m_aRUpper[iBoundBoxIndex] > i) g_BoundBoxInfo->m_aRUpper[iBoundBoxIndex] = i;		//top
      if (g_BoundBoxInfo->m_aRRight[iBoundBoxIndex] < j) g_BoundBoxInfo->m_aRRight[iBoundBoxIndex] = j;		//right
      if (g_BoundBoxInfo->m_aRBottom[iBoundBoxIndex] < i) g_BoundBoxInfo->m_aRBottom[iBoundBoxIndex] = i;	//bottom

    }
  }

  //Step2: Add margins.
  int iBoundary_w = (int)(g_iRWidth / 80), iBoundary_h = (int)(g_iRHeight / 60);

  for (int i = 0; i < g_BoundBoxInfo->m_iBoundBoxNum; i++) {

    g_BoundBoxInfo->m_aRLeft[i] -= iBoundary_w;
    if (g_BoundBoxInfo->m_aRLeft[i] < g_nRadius) g_BoundBoxInfo->m_aRLeft[i] = g_nRadius;									//left

    g_BoundBoxInfo->m_aRRight[i] += iBoundary_w;
    if (g_BoundBoxInfo->m_aRRight[i] >= g_iRWidth - g_nRadius) g_BoundBoxInfo->m_aRRight[i] = g_iRWidth - g_nRadius - 1;		    //Right

    g_BoundBoxInfo->m_aRUpper[i] -= iBoundary_h;
    if (g_BoundBoxInfo->m_aRUpper[i] < g_nRadius) g_BoundBoxInfo->m_aRUpper[i] = g_nRadius;									//Top

    g_BoundBoxInfo->m_aRBottom[i] += iBoundary_h;
    if (g_BoundBoxInfo->m_aRBottom[i] >= g_iRHeight - g_nRadius) g_BoundBoxInfo->m_aRBottom[i] = g_iRHeight - g_nRadius - 1;
  }

  double dH_ratio = (double)g_iHeight / (double)g_iRHeight;
  double dW_ratio = (double)g_iWidth / (double)g_iRWidth;

  for (int i = 0; i < g_BoundBoxInfo->m_iBoundBoxNum; i++) {
    g_BoundBoxInfo->m_aLeft[i] = (int)(g_BoundBoxInfo->m_aRLeft[i] * dW_ratio);
    g_BoundBoxInfo->m_aUpper[i] = (int)(g_BoundBoxInfo->m_aRUpper[i] * dH_ratio);
    g_BoundBoxInfo->m_aRight[i] = (int)(g_BoundBoxInfo->m_aRRight[i] * dW_ratio);
    g_BoundBoxInfo->m_aBottom[i] = (int)(g_BoundBoxInfo->m_aRBottom[i] * dH_ratio);
  }

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the box verification function													   //																							   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::BoundBoxVerification(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo) {

  //Step1: Verification by the bounding box size
  EvaluateBoxSize(BoundBoxInfo);

  //Step2: Verification by checking whether the region is ghost
  EvaluateGhostRegion(frame, aResForeMap, BoundBoxInfo);

  //Step3: Counting the # of valid box
  g_iForegroundNum = 0;
  for (int i = 0; i < BoundBoxInfo->m_iBoundBoxNum; i++) {
    if (BoundBoxInfo->m_ValidBox[i] == TRUE) g_iForegroundNum++;
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//																the size based verification												   //																							   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::EvaluateBoxSize(BoundingBoxInfo* BoundBoxInfo) {

  //Set thresholds
  int iLowThreshold_w, iHighThreshold_w;
  iLowThreshold_w = g_iRWidth / 32; if (iLowThreshold_w < 5) iLowThreshold_w = 5;
  iHighThreshold_w = g_iRWidth;

  int iLowThreshold_h, iHighThreshold_h;
  iLowThreshold_h = g_iRHeight / 24; if (iLowThreshold_h < 5) iLowThreshold_h = 5;
  iHighThreshold_h = g_iRHeight;

  int iBoxWidth, iBoxHeight;

  //Perform verification.
  for (int i = 0; i < BoundBoxInfo->m_iBoundBoxNum; i++) {

    iBoxWidth = BoundBoxInfo->m_aRRight[i] - BoundBoxInfo->m_aRLeft[i];
    iBoxHeight = BoundBoxInfo->m_aRBottom[i] - BoundBoxInfo->m_aRUpper[i];

    if (iLowThreshold_w <= iBoxWidth && iBoxWidth <= iHighThreshold_w && iLowThreshold_h <= iBoxHeight && iBoxHeight <= iHighThreshold_h) {
      BoundBoxInfo->m_ValidBox[i] = TRUE;
    }
    else BoundBoxInfo->m_ValidBox[i] = FALSE;
  }
}

//------------------------------------------------------------------------------------------------------------------------------------//
//														overlapped region removal													  //
//------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::EvaluateOverlapRegionSize(BoundingBoxInfo* SrcBoxInfo) {

  BOOL *aValidBoxFlag = new BOOL[SrcBoxInfo->m_iBoundBoxNum];
  for (int i = 0; i < SrcBoxInfo->m_iBoundBoxNum; i++) aValidBoxFlag[i] = TRUE;

  int size1, size2;
  short *aLeft = SrcBoxInfo->m_aRLeft, *aRight = SrcBoxInfo->m_aRRight;
  short *aTop = SrcBoxInfo->m_aRUpper, *aBottom = SrcBoxInfo->m_aRBottom;

  int iThreshold, iCount, iSmall_Idx, iLarge_Idx;
  double dThreRatio = 0.7;

  for (int i = 0; i < SrcBoxInfo->m_iBoundBoxNum; i++) {

    if (SrcBoxInfo->m_ValidBox[i] == FALSE) {
      aValidBoxFlag[i] = FALSE;
      continue;
    }

    size1 = (aRight[i] - aLeft[i]) * (aBottom[i] - aTop[i]);

    for (int j = i; j < SrcBoxInfo->m_iBoundBoxNum; j++) {
      if ((i == j) || (SrcBoxInfo->m_ValidBox[j] == FALSE)) continue;

      //Setting threshold for checking overlapped region size
      size2 = (aRight[j] - aLeft[j]) * (aBottom[j] - aTop[j]);

      if (size1 >= size2) {
        iThreshold = (int)(size2 * dThreRatio);
        iSmall_Idx = j; iLarge_Idx = i;
      }
      else {
        iThreshold = (int)(size1 * dThreRatio);
        iSmall_Idx = i; iLarge_Idx = j;
      }

      //Calculating overlapped region size
      iCount = 0;
      for (int m = aLeft[iSmall_Idx]; m < aRight[iSmall_Idx]; m++) {
        for (int n = aTop[iSmall_Idx]; n < aBottom[iSmall_Idx]; n++) {
          if (aLeft[iLarge_Idx] <= m && m <= aRight[iLarge_Idx] && aTop[iLarge_Idx] <= n && n <= aBottom[iLarge_Idx]) iCount++;
        }
      }

      //Evaluating overlapped region size
      if (iCount > iThreshold) aValidBoxFlag[iSmall_Idx] = FALSE;
    }
  }

  for (int i = 0; i < SrcBoxInfo->m_iBoundBoxNum; i++) SrcBoxInfo->m_ValidBox[i] = aValidBoxFlag[i];
  delete[] aValidBoxFlag;
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														appearance based verification													   //																							   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::EvaluateGhostRegion(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo) {

  double dThreshold = 10;

  BOOL** aUpdateMap = (BOOL**)malloc(sizeof(BOOL*)*g_iRHeight);
  for (int i = 0; i < g_iRHeight; i++) {
    aUpdateMap[i] = (BOOL*)malloc(sizeof(BOOL)*g_iRWidth);
    for (int j = 0; j < g_iRWidth; j++) aUpdateMap[i][j] = FALSE;
  }

  //Step1: Conduct fore-region evaluation to identify ghost regions

  for (int i = 0; i < BoundBoxInfo->m_iBoundBoxNum; i++) {
    if (BoundBoxInfo->m_ValidBox[i] == TRUE) {
      int iWin_w = BoundBoxInfo->m_aRRight[i] - BoundBoxInfo->m_aRLeft[i];
      int iWin_h = BoundBoxInfo->m_aRBottom[i] - BoundBoxInfo->m_aRUpper[i];

      //Generating edge image from bound-boxed frame region
      IplImage* resized_frame = cvCreateImage(cvSize(g_iRWidth, g_iRHeight), IPL_DEPTH_8U, 3);
      cvResize(frame, resized_frame, CV_INTER_NN);

      cvSetImageROI(resized_frame, cvRect(BoundBoxInfo->m_aRLeft[i], BoundBoxInfo->m_aRUpper[i], iWin_w, iWin_h));
      IplImage* edge_frame = cvCreateImage(cvSize(iWin_w, iWin_h), IPL_DEPTH_8U, 1);

      cvCvtColor(resized_frame, edge_frame, CV_BGR2GRAY);
      cvResetImageROI(resized_frame);

      cvCanny(edge_frame, edge_frame, 100, 150);

      //Generating edge image from aResForeMap
      IplImage* edge_fore = cvCreateImage(cvSize(iWin_w, iWin_h), IPL_DEPTH_8U, 1);
      for (int m = BoundBoxInfo->m_aRUpper[i]; m < BoundBoxInfo->m_aRBottom[i]; m++) {
        for (int n = BoundBoxInfo->m_aRLeft[i]; n < BoundBoxInfo->m_aRRight[i]; n++) {
          edge_fore->imageData[(m - BoundBoxInfo->m_aRUpper[i])*edge_fore->widthStep + (n - BoundBoxInfo->m_aRLeft[i])] = (char)aResForeMap[m][n];
        }
      }
      cvCanny(edge_fore, edge_fore, 100, 150);
      //Calculating partial undirected Hausdorff distance
      double distance = CalculateHausdorffDist(edge_frame, edge_fore);

      //Recording evaluation result
      if (distance > dThreshold) {

        for (int m = BoundBoxInfo->m_aRUpper[i]; m < BoundBoxInfo->m_aRBottom[i]; m++) {
          for (int n = BoundBoxInfo->m_aRLeft[i]; n < BoundBoxInfo->m_aRRight[i]; n++) {
            aUpdateMap[m][n] = TRUE;
          }
        }

        BoundBoxInfo->m_ValidBox[i] = FALSE;
      }

      cvReleaseImage(&resized_frame);
      cvReleaseImage(&edge_frame);
      cvReleaseImage(&edge_fore);
    }
  }

  //Step2: Adding information fo ghost region pixels to background model
  float fLearningRate = g_fLearningRate;

  for (int i = 0; i < g_iRHeight; i++) {
    for (int j = 0; j < g_iRWidth; j++) {
      if (aUpdateMap[i][j] == TRUE) {
        point center;
        center.m_nX = j; center.m_nY = i;

        T_ModelConstruction(g_nTextureTrainVolRange, fLearningRate, g_aXYZFrame, center, g_aNeighborDirection[i][j], g_TextureModel[i][j]);
        C_CodebookConstruction(g_aXYZFrame[i][j], j, i, g_nColorTrainVolRange, fLearningRate, g_ColorModel[i][j]);

        T_ClearNonEssentialEntries(g_iBackClearPeriod, g_TextureModel[i][j]);
        C_ClearNonEssentialEntries(g_iBackClearPeriod, g_ColorModel[i][j]);

      }
    }
  }

  for (int i = 0; i < g_iRHeight; i++) free(aUpdateMap[i]);
  free(aUpdateMap);
}


//-----------------------------------------------------------------------------------------------------------------------------------------//
//								the function to calculate partial undirected Hausdorff distance(forward distance)						   //																							   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
double MultiCue::CalculateHausdorffDist(IplImage* input_image, IplImage* model_image) {

  //Step1: Generating imag vectors
  //For reduce errors, points at the image boundary are excluded
  std::vector<point> vInput, vModel;
  point temp;

  //input image --> input vector
  for (int i = 0; i < input_image->height; i++) {
    for (int j = 0; j < input_image->width; j++) {

      if ((uchar)input_image->imageData[i*input_image->widthStep + j] == 0) continue;

      temp.m_nX = j; temp.m_nY = i;
      vInput.push_back(temp);
    }
  }
  //model image --> model vector
  for (int i = 0; i < model_image->height; i++) {
    for (int j = 0; j < model_image->width; j++) {
      if ((uchar)model_image->imageData[i*model_image->widthStep + j] == 0) continue;

      temp.m_nX = j; temp.m_nY = i;
      vModel.push_back(temp);
    }
  }

  if (vInput.empty() && !vModel.empty()) return (double)vModel.size();
  else if (!vInput.empty() && vModel.empty()) return (double)vInput.size();
  else if (vInput.empty() && vModel.empty()) return 0.0;

  //Step2: Calculating forward distance h(Model,Image)
  double dDist, temp1, temp2, dMinDist;
  std::vector<double> vTempDist;

  for (auto iter_m = vModel.begin(); iter_m < vModel.end(); iter_m++) {

    dMinDist = 9999999;
    for (auto iter_i = vInput.begin(); iter_i < vInput.end(); iter_i++) {
      temp1 = (*iter_m).m_nX - (*iter_i).m_nX;
      temp2 = (*iter_m).m_nY - (*iter_i).m_nY;
      dDist = temp1*temp1 + temp2*temp2;

      if (dDist < dMinDist) dMinDist = dDist;
    }
    vTempDist.push_back(dMinDist);
  }
  sort(vTempDist.begin(), vTempDist.end()); //in ascending order

  double dQuantileVal = 0.9, dForwardDistance;
  int iDistIndex = (int)(dQuantileVal*vTempDist.size());
  if (iDistIndex == vTempDist.size()) iDistIndex -= 1;

  dForwardDistance = sqrt(vTempDist[iDistIndex]);
  return dForwardDistance;
}


//-----------------------------------------------------------------------------------------------------------------------------------------//
//											function to remove non-valid bounding boxes fore fore-candidates							   //												
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::RemovingInvalidForeRegions(uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo) {

  int iBoxNum = BoundBoxInfo->m_iBoundBoxNum;

  for (int k = 0; k < iBoxNum; k++) {

    if (BoundBoxInfo->m_ValidBox[k] == FALSE) {
      for (int i = BoundBoxInfo->m_aRUpper[k]; i < BoundBoxInfo->m_aRBottom[k]; i++) {
        for (int j = BoundBoxInfo->m_aRLeft[k]; j < BoundBoxInfo->m_aRRight[k]; j++) {
          if (aResForeMap[i][j] == 255) aResForeMap[i][j] = 0;
        }
      }
    }

  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//													the function returning a foreground binary-map										   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::GetForegroundMap(IplImage* return_image, IplImage* input_frame) {

  if (g_bForegroundMapEnable == FALSE) return;

  IplImage* temp_image = cvCreateImage(cvSize(g_iRWidth, g_iRHeight), IPL_DEPTH_8U, 3);

  if (input_frame == NULL) {
    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) {
        temp_image->imageData[i*temp_image->widthStep + j * 3] = (char)g_aResizedForeMap[i][j];
        temp_image->imageData[i*temp_image->widthStep + j * 3 + 1] = (char)g_aResizedForeMap[i][j];
        temp_image->imageData[i*temp_image->widthStep + j * 3 + 2] = (char)g_aResizedForeMap[i][j];
      }
    }
  }

  else {

    cvResize(input_frame, temp_image);
    CvScalar MixColor;
    MixColor.val[0] = 255;	//B
    MixColor.val[1] = 0;	//G
    MixColor.val[2] = 255;   //R

    for (int i = 0; i < g_iRHeight; i++) {
      for (int j = 0; j < g_iRWidth; j++) {

        if (g_aResizedForeMap[i][j] == 255) {

          uchar B = (uchar)temp_image->imageData[i*temp_image->widthStep + j * 3];
          uchar G = (uchar)temp_image->imageData[i*temp_image->widthStep + j * 3];
          uchar R = (uchar)temp_image->imageData[i*temp_image->widthStep + j * 3];

          B = (uchar)(B*0.45 + MixColor.val[0] * 0.55);
          G = (uchar)(G*0.45 + MixColor.val[1] * 0.55);
          R = (uchar)(R*0.45 + MixColor.val[2] * 0.55);

          temp_image->imageData[i*temp_image->widthStep + j * 3] = (char)B;
          temp_image->imageData[i*temp_image->widthStep + j * 3 + 1] = (char)G;
          temp_image->imageData[i*temp_image->widthStep + j * 3 + 2] = (char)R;
        }
      }
    }
  }

  cvResize(temp_image, return_image);

  cvReleaseImage(&temp_image);
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//												the	initialization function for the texture-models									       //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_AllocateTextureModelRelatedMemory() {
  int i, j, k;

  //neighborhood system related
  int iMaxNeighborArraySize = 8;
  g_aNeighborDirection = (point***)malloc(sizeof(point**)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) {
    g_aNeighborDirection[i] = (point**)malloc(sizeof(point*)*g_iRWidth);
    for (j = 0; j < g_iRWidth; j++) {
      g_aNeighborDirection[i][j] = (point*)malloc(sizeof(point)*iMaxNeighborArraySize);
    }
  }
  T_SetNeighborDirection(g_aNeighborDirection);

  //texture-model related
  int iElementArraySize = 6;
  g_TextureModel = (TextureModel****)malloc(sizeof(TextureModel***)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) {
    g_TextureModel[i] = (TextureModel***)malloc(sizeof(TextureModel**)*g_iRWidth);
    for (j = 0; j < g_iRWidth; j++) {
      g_TextureModel[i][j] = (TextureModel**)malloc(sizeof(TextureModel*)*g_nNeighborNum);
      for (k = 0; k < g_nNeighborNum; k++) {
        g_TextureModel[i][j][k] = (TextureModel*)malloc(sizeof(TextureModel));
        g_TextureModel[i][j][k]->m_Codewords = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*iElementArraySize);
        g_TextureModel[i][j][k]->m_iElementArraySize = iElementArraySize;
        g_TextureModel[i][j][k]->m_iNumEntries = 0;
        g_TextureModel[i][j][k]->m_iTotal = 0;
        g_TextureModel[i][j][k]->m_bID = 1;
      }
    }
  }

  g_aTextureConfMap = (float**)malloc(sizeof(float*)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) g_aTextureConfMap[i] = (float*)malloc(sizeof(float)*g_iRWidth);

  //cache-book related
  if (g_bAbsorptionEnable == TRUE) {
    iElementArraySize = iElementArraySize / 2;
    if (iElementArraySize < 3)iElementArraySize = 3;

    g_TCacheBook = (TextureModel****)malloc(sizeof(TextureModel***)*g_iRHeight);
    for (i = 0; i < g_iRHeight; i++) {
      g_TCacheBook[i] = (TextureModel***)malloc(sizeof(TextureModel**)*g_iRWidth);
      for (j = 0; j < g_iRWidth; j++) {
        g_TCacheBook[i][j] = (TextureModel**)malloc(sizeof(TextureModel*)*g_nNeighborNum);
        for (k = 0; k < g_nNeighborNum; k++) {
          g_TCacheBook[i][j][k] = (TextureModel*)malloc(sizeof(TextureModel));
          g_TCacheBook[i][j][k]->m_Codewords = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*iElementArraySize);
          g_TCacheBook[i][j][k]->m_iElementArraySize = iElementArraySize;
          g_TCacheBook[i][j][k]->m_iNumEntries = 0;
          g_TCacheBook[i][j][k]->m_iTotal = 0;
          g_TCacheBook[i][j][k]->m_bID = 0;
        }
      }
    }

    g_aTReferredIndex = (short***)malloc(sizeof(short**)*g_iRHeight);
    g_aTContinuousCnt = (short***)malloc(sizeof(short**)*g_iRHeight);
    for (i = 0; i < g_iRHeight; i++) {
      g_aTReferredIndex[i] = (short**)malloc(sizeof(short*)*g_iRWidth);
      g_aTContinuousCnt[i] = (short**)malloc(sizeof(short*)*g_iRWidth);
      for (j = 0; j < g_iRWidth; j++) {
        g_aTReferredIndex[i][j] = (short*)malloc(sizeof(short)*g_nNeighborNum);
        g_aTContinuousCnt[i][j] = (short*)malloc(sizeof(short)*g_nNeighborNum);
        for (k = 0; k < g_nNeighborNum; k++) {
          g_aTReferredIndex[i][j][k] = -1;
          g_aTContinuousCnt[i][j][k] = 0;
        }
      }
    }
  }
}
//-----------------------------------------------------------------------------------------------------------------------------------------//
//															the memory release function											           //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_ReleaseTextureModelRelatedMemory() {
  int i, j, k, m;
  short nNeighborNum = g_nNeighborNum;

  for (i = 0; i < g_iRHeight; i++) {
    for (j = 0; j < g_iRWidth; j++) {
      for (k = 0; k < nNeighborNum; k++) {
        for (m = 0; m < g_TextureModel[i][j][k]->m_iNumEntries; m++) free(g_TextureModel[i][j][k]->m_Codewords[m]);
        free(g_TextureModel[i][j][k]->m_Codewords);
        free(g_TextureModel[i][j][k]);
      }
      free(g_TextureModel[i][j]);
    }free(g_TextureModel[i]);
  }
  free(g_TextureModel);

  for (i = 0; i < g_iRHeight; i++) {
    for (j = 0; j < g_iRWidth; j++) free(g_aNeighborDirection[i][j]);
    free(g_aNeighborDirection[i]);
  }
  free(g_aNeighborDirection);

  for (i = 0; i < g_iRHeight; i++) free(g_aTextureConfMap[i]);
  free(g_aTextureConfMap);

  if (g_bAbsorptionEnable == TRUE) {
    for (i = 0; i < g_iRHeight; i++) {
      for (j = 0; j < g_iRWidth; j++) {
        for (k = 0; k < nNeighborNum; k++) {
          for (m = 0; m < g_TCacheBook[i][j][k]->m_iNumEntries; m++) free(g_TCacheBook[i][j][k]->m_Codewords[m]);
          free(g_TCacheBook[i][j][k]->m_Codewords);
          free(g_TCacheBook[i][j][k]);
        }
        free(g_TCacheBook[i][j]);
      }free(g_TCacheBook[i]);
    }
    free(g_TCacheBook);

    for (i = 0; i < g_iRHeight; i++) {
      for (j = 0; j < g_iRWidth; j++) {
        free(g_aTReferredIndex[i][j]);
        free(g_aTContinuousCnt[i][j]);
      }
      free(g_aTReferredIndex[i]);
      free(g_aTContinuousCnt[i]);
    }
    free(g_aTReferredIndex);
    free(g_aTContinuousCnt);

  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the codebook construction function				                                   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_ModelConstruction(short nTrainVolRange, float fLearningRate, uchar*** aXYZ, point center, point* aNei, TextureModel** aModel) {

  int i, j;
  int iMatchedIndex;

  short nNeighborNum = g_nNeighborNum;

  float fDifference;
  float fDiffMean;

  float fNegLearningRate = 1 - fLearningRate;

  //for all neighboring pairs
  for (i = 0; i < nNeighborNum; i++) {

    fDifference = (float)(aXYZ[center.m_nY][center.m_nX][2] - aXYZ[aNei[i].m_nY][aNei[i].m_nX][2]);

    //Step1: matching
    iMatchedIndex = -1;
    for (j = 0; j < aModel[i]->m_iNumEntries; j++) {
      if (aModel[i]->m_Codewords[j]->m_fLowThre <= fDifference && fDifference <= aModel[i]->m_Codewords[j]->m_fHighThre) {
        iMatchedIndex = j;
        break;
      }
    }

    aModel[i]->m_iTotal++;
    //Step2: adding a new element
    if (iMatchedIndex == -1) {
      //element array
      if (aModel[i]->m_iElementArraySize == aModel[i]->m_iNumEntries) {
        aModel[i]->m_iElementArraySize += 5;
        TextureCodeword **temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*aModel[i]->m_iElementArraySize);
        for (j = 0; j < aModel[i]->m_iNumEntries; j++) {
          temp[j] = aModel[i]->m_Codewords[j];
          aModel[i]->m_Codewords[j] = NULL;
        }
        free(aModel[i]->m_Codewords);
        aModel[i]->m_Codewords = temp;
      }
      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries] = (TextureCodeword*)malloc(sizeof(TextureCodeword));

      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fMean = fDifference;
      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fLowThre = aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fMean - nTrainVolRange;
      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fHighThre = aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_fMean + nTrainVolRange;

      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_iT_first_time = aModel[i]->m_iTotal;
      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_iT_last_time = aModel[i]->m_iTotal;
      aModel[i]->m_Codewords[aModel[i]->m_iNumEntries]->m_iMNRL = aModel[i]->m_iTotal - 1;
      aModel[i]->m_iNumEntries++;
    }

    //Step3: update
    else {
      fDiffMean = aModel[i]->m_Codewords[iMatchedIndex]->m_fMean;
      aModel[i]->m_Codewords[iMatchedIndex]->m_fMean = fLearningRate*fDifference + fNegLearningRate*fDiffMean;

      aModel[i]->m_Codewords[iMatchedIndex]->m_fLowThre = aModel[i]->m_Codewords[iMatchedIndex]->m_fMean - nTrainVolRange;
      aModel[i]->m_Codewords[iMatchedIndex]->m_fHighThre = aModel[i]->m_Codewords[iMatchedIndex]->m_fMean + nTrainVolRange;

      aModel[i]->m_Codewords[iMatchedIndex]->m_iT_last_time = aModel[i]->m_iTotal;
    }

    //cache-book handling
    if (aModel[i]->m_bID == 1) {
      //1. m_iMNRL update
      int negTime;
      for (j = 0; j < aModel[i]->m_iNumEntries; j++) {
        //m_iMNRL update
        negTime = aModel[i]->m_iTotal - aModel[i]->m_Codewords[j]->m_iT_last_time + aModel[i]->m_Codewords[j]->m_iT_first_time - 1;
        if (aModel[i]->m_Codewords[j]->m_iMNRL < negTime) aModel[i]->m_Codewords[j]->m_iMNRL = negTime;
      }


      //2. g_aTReferredIndex[center.m_nY][center.m_nX][i] update
      if (g_bAbsorptionEnable == TRUE) g_aTReferredIndex[center.m_nY][center.m_nX][i] = -1;
    }

    else {
      //1. m_iMNRL update
      if (iMatchedIndex == -1) aModel[i]->m_Codewords[aModel[i]->m_iNumEntries - 1]->m_iMNRL = 0;

      //2. g_aTReferredIndex[center.m_nY][center.m_nX][i] update
      if (iMatchedIndex == -1) {
        g_aTReferredIndex[center.m_nY][center.m_nX][i] = aModel[i]->m_iNumEntries - 1;
        g_aTContinuousCnt[center.m_nY][center.m_nX][i] = 1;
      }
      else {
        if (iMatchedIndex == g_aTReferredIndex[center.m_nY][center.m_nX][i]) g_aTContinuousCnt[center.m_nY][center.m_nX][i]++;
        else {
          g_aTReferredIndex[center.m_nY][center.m_nX][i] = iMatchedIndex;
          g_aTContinuousCnt[center.m_nY][center.m_nX][i] = 1;
        }
      }
    }

  }

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//												Clear non-essential codewords of the given codebook						                   //																									   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_ClearNonEssentialEntries(short nClearNum, TextureModel** aModel) {
  int i, n;
  int iStaleThresh = (int)(nClearNum*0.5);
  int iKeepCnt;
  int* aKeep;

  short nNeighborNum = g_nNeighborNum;

  TextureModel* c;

  for (n = 0; n < nNeighborNum; n++) {
    c = aModel[n];

    if (c->m_iTotal < nClearNum) continue; //(being operated only when c[w][h]->m_iTotal == nClearNum)

    //Step1: initialization
    aKeep = (int*)malloc(sizeof(int)*c->m_iNumEntries);

    iKeepCnt = 0;

    //Step2: Find non-essential code-words
    for (i = 0; i < c->m_iNumEntries; i++) {
      if (c->m_Codewords[i]->m_iMNRL > iStaleThresh) {
        aKeep[i] = 0; //removal candidate
      }
      else {
        aKeep[i] = 1;
        iKeepCnt++;
      }
    }

    //Step3: Perform removal
    if (iKeepCnt == 0 || iKeepCnt == c->m_iNumEntries) {
      for (i = 0; i < c->m_iNumEntries; i++) {
        c->m_Codewords[i]->m_iT_first_time = 1;
        c->m_Codewords[i]->m_iT_last_time = 1;
        c->m_Codewords[i]->m_iMNRL = 0;
      }
    }

    else {
      iKeepCnt = 0;
      TextureCodeword** temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*c->m_iNumEntries);

      for (i = 0; i < c->m_iNumEntries; i++) {
        if (aKeep[i] == 1) {
          temp[iKeepCnt] = c->m_Codewords[i];
          temp[iKeepCnt]->m_iT_first_time = 1;
          temp[iKeepCnt]->m_iT_last_time = 1;
          temp[iKeepCnt]->m_iMNRL = 0;
          iKeepCnt++;
        }
        else free(c->m_Codewords[i]);
      }

      //ending..
      free(c->m_Codewords);
      c->m_Codewords = temp;
      c->m_iElementArraySize = c->m_iNumEntries;
      c->m_iNumEntries = iKeepCnt;
    }
    c->m_iTotal = 0;
    free(aKeep);

  }

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//								Clear non-essential codewords of the given codebook (only for the cache-book)			                   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short* nReferredIdxArr, short nClearNum, TextureModel** pCachebook) {
  int i, n;
  short nNeighborNum = g_nNeighborNum;

  TextureModel* c;
  short nReferredIdx;

  for (n = 0; n < nNeighborNum; n++) {

    c = pCachebook[n];
    nReferredIdx = nReferredIdxArr[n];

    //pCachebook->m_iTotal < nClearNum? --> MNRL update
    if (c->m_iTotal < nClearNum) {
      for (i = 0; i < c->m_iNumEntries; i++) {
        if (bLandmark == 255 && i == nReferredIdx) c->m_Codewords[i]->m_iMNRL = 0;
        else c->m_Codewords[i]->m_iMNRL++;
      }

      c->m_iTotal++;
    }

    //Perform clearing
    else {
      int iStaleThreshold = 5;

      int* aKeep;
      short nKeepCnt;

      aKeep = (int*)malloc(sizeof(int)*c->m_iNumEntries);
      nKeepCnt = 0;

      for (i = 0; i < c->m_iNumEntries; i++) {
        if (c->m_Codewords[i]->m_iMNRL < iStaleThreshold) {
          aKeep[i] = 1;
          nKeepCnt++;
        }
        else aKeep[i] = 0;
      }

      c->m_iElementArraySize = nKeepCnt + 2;
      if (c->m_iElementArraySize < 3) c->m_iElementArraySize = 3;

      TextureCodeword** temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*c->m_iElementArraySize);
      nKeepCnt = 0;

      for (i = 0; i < c->m_iNumEntries; i++) {
        if (aKeep[i] == 1) {
          temp[nKeepCnt] = c->m_Codewords[i];
          temp[nKeepCnt]->m_iMNRL = 0;
          nKeepCnt++;
        }
        else {
          free(c->m_Codewords[i]);
        }

      }

      //ending..
      free(c->m_Codewords);
      c->m_Codewords = temp;
      c->m_iNumEntries = nKeepCnt;
      c->m_iTotal = 0;

      free(aKeep);
    }
  }

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//											the function to generate texture confidence maps				                               //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_GetConfidenceMap_Par(uchar*** aXYZ, float** aTextureMap, point*** aNeiDirArr, TextureModel**** aModel) {

  int iBound_w = g_iRWidth - g_nRadius;
  int iBound_h = g_iRHeight - g_nRadius;

  short nNeighborNum = g_nNeighborNum;
  float fPadding = 5;

  for (int h = 0; h < g_iRHeight; h++) {
    for (int w = 0; w < g_iRWidth; w++) {

      if (h < g_nRadius || h >= iBound_h || w < g_nRadius || w >= iBound_w) {
        aTextureMap[h][w] = 0;
        continue;
      }

      int nMatchedCount = 0;
      float fDiffSum = 0;
      float fDifference;
      point nei;

      for (int i = 0; i < nNeighborNum; i++) {

        nei.m_nX = aNeiDirArr[h][w][i].m_nX;
        nei.m_nY = aNeiDirArr[h][w][i].m_nY;

        fDifference = (float)(aXYZ[h][w][2] - aXYZ[nei.m_nY][nei.m_nX][2]);
        if (fDifference < 0) fDiffSum -= fDifference;
        else fDiffSum += fDifference;

        for (int j = 0; j < aModel[h][w][i]->m_iNumEntries; j++) {
          if (aModel[h][w][i]->m_Codewords[j]->m_fLowThre - fPadding <= fDifference && fDifference <= aModel[h][w][i]->m_Codewords[j]->m_fHighThre + fPadding) {
            nMatchedCount++;
            break;
          }
        }

      }
      aTextureMap[h][w] = 1 - (float)nMatchedCount / nNeighborNum;
    }
  }
}
//-----------------------------------------------------------------------------------------------------------------------------------------//
//											Absorbing Ghost Non-background Region Update					                               //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_Absorption(int iAbsorbCnt, point pos, short*** aContinuCnt, short*** aRefferedIndex, TextureModel** pModel, TextureModel** pCache) {
  int i, j, k;
  int iLeavingIndex;

  //short g_nRadius = 2;
  short nNeighborNum = g_nNeighborNum;

  for (i = 0; i < nNeighborNum; i++) {
    //set iLeavingIndex
    if (aContinuCnt[pos.m_nY][pos.m_nX][i] < iAbsorbCnt) continue;

    iLeavingIndex = aRefferedIndex[pos.m_nY][pos.m_nX][i];

    //array expansion
    if (pModel[i]->m_iElementArraySize == pModel[i]->m_iNumEntries) {
      pModel[i]->m_iElementArraySize = pModel[i]->m_iElementArraySize + 5;
      TextureCodeword** temp = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*pModel[i]->m_iElementArraySize);
      for (j = 0; j < pModel[i]->m_iNumEntries; j++) temp[j] = pModel[i]->m_Codewords[j];
      free(pModel[i]->m_Codewords);
      pModel[i]->m_Codewords = temp;
    }

    //movement from the cache-book to the codebook
    pModel[i]->m_Codewords[pModel[i]->m_iNumEntries] = pCache[i]->m_Codewords[iLeavingIndex];

    pModel[i]->m_iTotal = pModel[i]->m_iTotal + 1;

    pModel[i]->m_Codewords[pModel[i]->m_iNumEntries]->m_iT_first_time = pModel[i]->m_iTotal;
    pModel[i]->m_Codewords[pModel[i]->m_iNumEntries]->m_iT_last_time = pModel[i]->m_iTotal;
    pModel[i]->m_Codewords[pModel[i]->m_iNumEntries]->m_iMNRL = pModel[i]->m_iTotal - 1;
    pModel[i]->m_iNumEntries = pModel[i]->m_iNumEntries + 1;

    k = 0;
    TextureCodeword **temp_Cache = (TextureCodeword**)malloc(sizeof(TextureCodeword*)*pCache[i]->m_iElementArraySize);
    for (j = 0; j < pCache[i]->m_iNumEntries; j++) {
      if (j == iLeavingIndex) continue;
      else {
        temp_Cache[k] = pCache[i]->m_Codewords[j];
        k++;
      }
    }
    free(pCache[i]->m_Codewords);
    pCache[i]->m_Codewords = temp_Cache;
    pCache[i]->m_iNumEntries = k;
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//													the function to set neighborhood system												   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::T_SetNeighborDirection(point*** aNeighborPos) {
  int i, j, k;
  point* aSearchDirection = (point*)malloc(sizeof(point)*g_nNeighborNum);

  aSearchDirection[0].m_nX = -2;//180 degree
  aSearchDirection[0].m_nY = 0;

  aSearchDirection[1].m_nX = -1;//123 degree
  aSearchDirection[1].m_nY = -2;

  aSearchDirection[2].m_nX = 1;//45 degree
  aSearchDirection[2].m_nY = -2;

  aSearchDirection[3].m_nX = 2;//0 degree
  aSearchDirection[3].m_nY = 0;

  aSearchDirection[4].m_nX = 1;//-45 degree
  aSearchDirection[4].m_nY = 2;

  aSearchDirection[5].m_nX = -1;//-135 degree
  aSearchDirection[5].m_nY = 2;

  point temp_pos;

  for (i = 0; i < g_iRHeight; i++) {
    for (j = 0; j < g_iRWidth; j++) {
      for (k = 0; k < g_nNeighborNum; k++) {
        temp_pos.m_nX = j + aSearchDirection[k].m_nX;
        temp_pos.m_nY = i + aSearchDirection[k].m_nY;

        if (temp_pos.m_nX < 0 || temp_pos.m_nX >= g_iRWidth || temp_pos.m_nY < 0 || temp_pos.m_nY >= g_iRHeight) {
          aNeighborPos[i][j][k].m_nX = -1;
          aNeighborPos[i][j][k].m_nY = -1;
        }

        else {
          aNeighborPos[i][j][k].m_nX = temp_pos.m_nX;
          aNeighborPos[i][j][k].m_nY = temp_pos.m_nY;
        }
      }
    }
  }
  free(aSearchDirection);
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//													the color-model initialization function												   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::C_AllocateColorModelRelatedMemory() {
  int i, j;

  int iElementArraySize = 10;

  //codebook initialization
  g_ColorModel = (ColorModel***)malloc(sizeof(ColorModel**)*g_iRHeight);
  for (i = 0; i < g_iRHeight; i++) {
    g_ColorModel[i] = (ColorModel**)malloc(sizeof(ColorModel*)*g_iRWidth);
    for (j = 0; j < g_iRWidth; j++) {
      //initialization of each CodeBookArray.
      g_ColorModel[i][j] = (ColorModel*)malloc(sizeof(ColorModel));
      g_ColorModel[i][j]->m_Codewords = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*iElementArraySize);
      g_ColorModel[i][j]->m_iNumEntries = 0;
      g_ColorModel[i][j]->m_iElementArraySize = iElementArraySize;
      g_ColorModel[i][j]->m_iTotal = 0;
      g_ColorModel[i][j]->m_bID = 1;
    }
  }

  //cache-book initialization
  if (g_bAbsorptionEnable == TRUE) {
    iElementArraySize = 3;

    g_CCacheBook = (ColorModel***)malloc(sizeof(ColorModel**)*g_iRHeight);
    for (i = 0; i < g_iRHeight; i++) {
      g_CCacheBook[i] = (ColorModel**)malloc(sizeof(ColorModel*)*g_iRWidth);
      for (j = 0; j < g_iRWidth; j++) {
        //initialization of each CodeBookArray.
        g_CCacheBook[i][j] = (ColorModel*)malloc(sizeof(ColorModel));
        g_CCacheBook[i][j]->m_Codewords = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*iElementArraySize);
        g_CCacheBook[i][j]->m_iNumEntries = 0;
        g_CCacheBook[i][j]->m_iElementArraySize = iElementArraySize;
        g_CCacheBook[i][j]->m_iTotal = 0;
        g_CCacheBook[i][j]->m_bID = 0;
      }
    }

    g_aCReferredIndex = (short**)malloc(sizeof(short*)*g_iRHeight);
    g_aCContinuousCnt = (short**)malloc(sizeof(short*)*g_iRHeight);
    for (i = 0; i < g_iRHeight; i++) {
      g_aCReferredIndex[i] = (short*)malloc(sizeof(short)*g_iRWidth);
      g_aCContinuousCnt[i] = (short*)malloc(sizeof(short)*g_iRWidth);
      for (j = 0; j < g_iRWidth; j++) {
        g_aCReferredIndex[i][j] = -1;
        g_aCContinuousCnt[i][j] = 0;
      }
    }
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//															the memory release function											           //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::C_ReleaseColorModelRelatedMemory() {
  int i, j, k;

  for (i = 0; i < g_iRHeight; i++) {
    for (j = 0; j < g_iRWidth; j++) {
      for (k = 0; k < g_ColorModel[i][j]->m_iNumEntries; k++) {
        free(g_ColorModel[i][j]->m_Codewords[k]);
      }
      free(g_ColorModel[i][j]->m_Codewords);
      free(g_ColorModel[i][j]);
    }
    free(g_ColorModel[i]);
  }
  free(g_ColorModel);

  if (g_bAbsorptionEnable == TRUE) {
    for (i = 0; i < g_iRHeight; i++) {
      for (j = 0; j < g_iRWidth; j++) {
        for (k = 0; k < g_CCacheBook[i][j]->m_iNumEntries; k++) {
          free(g_CCacheBook[i][j]->m_Codewords[k]);
        }
        free(g_CCacheBook[i][j]->m_Codewords);
        free(g_CCacheBook[i][j]);
      }
      free(g_CCacheBook[i]);
    }
    free(g_CCacheBook);

    for (i = 0; i < g_iRHeight; i++) {
      free(g_aCReferredIndex[i]);
      free(g_aCContinuousCnt[i]);
    }
    free(g_aCReferredIndex);
    free(g_aCContinuousCnt);
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the codebook construction function								                   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::C_CodebookConstruction(uchar* aP, int iPosX, int iPosY, short nTrainVolRange, float fLearningRate, ColorModel* pC) {

  //Step1: matching
  short nMatchedIndex;

  float fNegLearningRate = 1 - fLearningRate;

  nMatchedIndex = -1;

  for (int i = 0; i < pC->m_iNumEntries; i++) {

    //Checking X
    if (pC->m_Codewords[i]->m_dMean[0] - nTrainVolRange <= aP[0] && aP[0] <= pC->m_Codewords[i]->m_dMean[0] + nTrainVolRange) {
      //Checking Y
      if (pC->m_Codewords[i]->m_dMean[1] - nTrainVolRange <= aP[1] && aP[1] <= pC->m_Codewords[i]->m_dMean[1] + nTrainVolRange) {
        //Checking Z
        if (pC->m_Codewords[i]->m_dMean[2] - nTrainVolRange <= aP[2] && aP[2] <= pC->m_Codewords[i]->m_dMean[2] + nTrainVolRange) {
          nMatchedIndex = i;
          break;
        }
      }
    }
  }

  pC->m_iTotal = pC->m_iTotal + 1;

  //Step2 : adding a new element
  if (nMatchedIndex == -1) {
    if (pC->m_iElementArraySize == pC->m_iNumEntries) {
      pC->m_iElementArraySize = pC->m_iElementArraySize + 5;
      ColorCodeword **temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pC->m_iElementArraySize);
      for (int j = 0; j < pC->m_iNumEntries; j++) {
        temp[j] = pC->m_Codewords[j];
        pC->m_Codewords[j] = NULL;
      }
      free(pC->m_Codewords);
      pC->m_Codewords = temp;

    }
    pC->m_Codewords[pC->m_iNumEntries] = (ColorCodeword*)malloc(sizeof(ColorCodeword));

    pC->m_Codewords[pC->m_iNumEntries]->m_dMean[0] = aP[0];//X
    pC->m_Codewords[pC->m_iNumEntries]->m_dMean[1] = aP[1];//Y
    pC->m_Codewords[pC->m_iNumEntries]->m_dMean[2] = aP[2];//Z

    pC->m_Codewords[pC->m_iNumEntries]->m_iT_first_time = pC->m_iTotal;
    pC->m_Codewords[pC->m_iNumEntries]->m_iT_last_time = pC->m_iTotal;
    pC->m_Codewords[pC->m_iNumEntries]->m_iMNRL = pC->m_iTotal - 1;
    pC->m_iNumEntries = pC->m_iNumEntries + 1;
  }

  //Step3 : update
  else {
    //m_dMean update
    pC->m_Codewords[nMatchedIndex]->m_dMean[0] = (fLearningRate*aP[0]) + fNegLearningRate*pC->m_Codewords[nMatchedIndex]->m_dMean[0];//X
    pC->m_Codewords[nMatchedIndex]->m_dMean[1] = (fLearningRate*aP[1]) + fNegLearningRate*pC->m_Codewords[nMatchedIndex]->m_dMean[1];//Y
    pC->m_Codewords[nMatchedIndex]->m_dMean[2] = (fLearningRate*aP[2]) + fNegLearningRate*pC->m_Codewords[nMatchedIndex]->m_dMean[2];//Z

    pC->m_Codewords[nMatchedIndex]->m_iT_last_time = pC->m_iTotal;
  }

  //cache-book handling
  if (pC->m_bID == 1) {
    //1. m_iMNRL update
    int iNegTime;
    for (int i = 0; i < pC->m_iNumEntries; i++) {
      //m_iMNRL update
      iNegTime = pC->m_iTotal - pC->m_Codewords[i]->m_iT_last_time + pC->m_Codewords[i]->m_iT_first_time - 1;
      if (pC->m_Codewords[i]->m_iMNRL < iNegTime) pC->m_Codewords[i]->m_iMNRL = iNegTime;
    }

    //2. g_aCReferredIndex[iPosY][iPosX] update
    if (g_bAbsorptionEnable == TRUE) g_aCReferredIndex[iPosY][iPosX] = -1;
  }

  else {
    //1. m_iMNRL update:
    if (nMatchedIndex == -1) pC->m_Codewords[pC->m_iNumEntries - 1]->m_iMNRL = 0;

    //2. g_aCReferredIndex[iPosY][iPosX] update
    if (nMatchedIndex == -1) {
      g_aCReferredIndex[iPosY][iPosX] = pC->m_iNumEntries - 1;
      g_aCContinuousCnt[iPosY][iPosX] = 1;
    }
    else {
      if (nMatchedIndex == g_aCReferredIndex[iPosY][iPosX]) g_aCContinuousCnt[iPosY][iPosX]++;
      else {
        g_aCReferredIndex[iPosY][iPosX] = nMatchedIndex;
        g_aCContinuousCnt[iPosY][iPosX] = 1;
      }
    }
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//												Clear non-essential codewords of the given codebook							               //																													   //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::C_ClearNonEssentialEntries(short nClearNum, ColorModel* pModel) {
  int i;
  short nStaleThresh = (int)(nClearNum*0.5);
  short nKeepCnt;
  int* aKeep;

  ColorModel* pC = pModel;

  if (pC->m_iTotal < nClearNum) return; //(Being operated only when pC->t >= nClearNum)

  //Step1:initialization
  aKeep = (int*)malloc(sizeof(int)*pC->m_iNumEntries);

  nKeepCnt = 0;

  //Step2: Find non-essential codewords
  for (i = 0; i < pC->m_iNumEntries; i++) {
    if (pC->m_Codewords[i]->m_iMNRL > nStaleThresh) {
      aKeep[i] = 0; //removal
    }
    else {
      aKeep[i] = 1; //keep
      nKeepCnt++;
    }
  }

  //Step3: Perform removal
  if (nKeepCnt == 0 || nKeepCnt == pC->m_iNumEntries) {
    for (i = 0; i < pC->m_iNumEntries; i++) {
      pC->m_Codewords[i]->m_iT_first_time = 1;
      pC->m_Codewords[i]->m_iT_last_time = 1;
      pC->m_Codewords[i]->m_iMNRL = 0;
    }
  }
  else {
    nKeepCnt = 0;
    ColorCodeword** temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pC->m_iNumEntries);

    for (i = 0; i < pC->m_iNumEntries; i++) {
      if (aKeep[i] == 1) {
        temp[nKeepCnt] = pC->m_Codewords[i];
        temp[nKeepCnt]->m_iT_first_time = 1;
        temp[nKeepCnt]->m_iT_last_time = 1;
        temp[nKeepCnt]->m_iMNRL = 0;
        nKeepCnt++;
      }
      else free(pC->m_Codewords[i]);
    }

    //ending..
    free(pC->m_Codewords);
    pC->m_Codewords = temp;
    pC->m_iElementArraySize = pC->m_iNumEntries;
    pC->m_iNumEntries = nKeepCnt;
  }

  pC->m_iTotal = 0;
  free(aKeep);

}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//										Clear non-essential codewords of the given codebook (for cache-book)				               //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::C_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short nReferredIdx, short nClearNum, ColorModel* pCachebook) {
  int i;

  if (pCachebook->m_iTotal < nClearNum) {
    for (i = 0; i < pCachebook->m_iNumEntries; i++) {
      if (bLandmark == 255 && i == nReferredIdx) pCachebook->m_Codewords[i]->m_iMNRL = 0;
      else pCachebook->m_Codewords[i]->m_iMNRL++;
    }

    pCachebook->m_iTotal++;
  }

  else {
    int iStaleThreshold = 5;

    int* aKeep;
    short nKeepCnt;

    aKeep = (int*)malloc(sizeof(int)*pCachebook->m_iNumEntries);
    nKeepCnt = 0;

    for (i = 0; i < pCachebook->m_iNumEntries; i++) {
      if (pCachebook->m_Codewords[i]->m_iMNRL < iStaleThreshold) {
        aKeep[i] = 1;
        nKeepCnt++;
      }
      else aKeep[i] = 0;
    }

    pCachebook->m_iElementArraySize = nKeepCnt + 2;
    if (pCachebook->m_iElementArraySize < 3) pCachebook->m_iElementArraySize = 3;

    ColorCodeword** temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pCachebook->m_iElementArraySize);
    nKeepCnt = 0;

    for (i = 0; i < pCachebook->m_iNumEntries; i++) {
      if (aKeep[i] == 1) {
        temp[nKeepCnt] = pCachebook->m_Codewords[i];
        temp[nKeepCnt]->m_iMNRL = 0;
        nKeepCnt++;
      }
      else {
        free(pCachebook->m_Codewords[i]);
      }

    }

    //ending..
    free(pCachebook->m_Codewords);
    pCachebook->m_Codewords = temp;
    pCachebook->m_iNumEntries = nKeepCnt;
    pCachebook->m_iTotal = 0;

    free(aKeep);
  }
}

//-----------------------------------------------------------------------------------------------------------------------------------------//
//														the ghost-region absorption function										       //
//-----------------------------------------------------------------------------------------------------------------------------------------//
void MultiCue::C_Absorption(int iAbsorbCnt, point pos, short** aContinuCnt, short** aRefferedIndex, ColorModel* pModel, ColorModel* pCache) {

  //set iLeavingIndex
  if (aContinuCnt[pos.m_nY][pos.m_nX] < iAbsorbCnt) return;

  int iLeavingIndex = aRefferedIndex[pos.m_nY][pos.m_nX];

  //array expansion
  if (pModel->m_iElementArraySize == pModel->m_iNumEntries) {
    pModel->m_iElementArraySize = pModel->m_iElementArraySize + 5;
    ColorCodeword** temp = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pModel->m_iElementArraySize);
    for (int i = 0; i < pModel->m_iNumEntries; i++) temp[i] = pModel->m_Codewords[i];
    free(pModel->m_Codewords);
    pModel->m_Codewords = temp;
  }

  //movement from the cache-book to the codebook
  pModel->m_Codewords[pModel->m_iNumEntries] = pCache->m_Codewords[iLeavingIndex];

  pModel->m_iTotal = pModel->m_iTotal + 1;

  pModel->m_Codewords[pModel->m_iNumEntries]->m_iT_first_time = pModel->m_iTotal;
  pModel->m_Codewords[pModel->m_iNumEntries]->m_iT_last_time = pModel->m_iTotal;
  pModel->m_Codewords[pModel->m_iNumEntries]->m_iMNRL = pModel->m_iTotal - 1;

  pModel->m_iNumEntries = pModel->m_iNumEntries + 1;

  int k = 0;
  ColorCodeword **pTempCache = (ColorCodeword**)malloc(sizeof(ColorCodeword*)*pCache->m_iElementArraySize);
  for (int i = 0; i < pCache->m_iNumEntries; i++) {
    if (i == iLeavingIndex) continue;
    else {
      pTempCache[k] = pCache->m_Codewords[i];
      k++;
    }
  }
  free(pCache->m_Codewords);
  pCache->m_Codewords = pTempCache;
  pCache->m_iNumEntries = k;
}

#endif

--#-- END ./bgslibrary/algorithms/MultiCue.cpp --#--

--#-- START ./bgslibrary/algorithms/KNN.cpp --#--
#include "KNN.h"

#if CV_MAJOR_VERSION >= 3

using namespace bgslibrary::algorithms;

KNN::KNN() :
  IBGS(quote(KNN)),
  history(10), dist2Threshold(20.0f * 20.0f), shadowThreshold(0.5f)
{
  debug_construction(KNN);
  initLoadSaveConfig(algorithmName);
}

KNN::~KNN() {
  debug_destruction(KNN);
}

void KNN::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  //------------------------------------------------------------------
  // BackgroundSubtractorKNN
  // http://docs.opencv.org/trunk/modules/video/doc/motion_analysis_and_object_tracking.html#backgroundsubtractorknn
  //
  //  The class implements the K nearest neigbours algorithm from:
  //  "Efficient Adaptive Density Estimation per Image Pixel for the Task of Background Subtraction"
  //  Z.Zivkovic, F. van der Heijden
  //  Pattern Recognition Letters, vol. 27, no. 7, pages 773-780, 2006
  //  http:  //www.zoranz.net/Publications/zivkovicPRL2006.pdf
  //
  //  Fast for small foreground object. Results on the benchmark data is at http://www.changedetection.net.
  //------------------------------------------------------------------

  if (firstTime)
    knn = cv::createBackgroundSubtractorKNN(); // history, dist2Threshold, doShadowDetection

  knn->setHistory(history);
  knn->setDist2Threshold(dist2Threshold);
  knn->setShadowThreshold(shadowThreshold);

  //knn->apply(img_input, img_foreground, nSamples >= knnSamples ? 0.f : 1.f);
  knn->apply(img_input, img_foreground);
  knn->getBackgroundImage(img_background);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void KNN::save_config(cv::FileStorage &fs) {
  fs << "history" << history;
  fs << "dist2Threshold" << dist2Threshold;
  fs << "shadowThreshold" << shadowThreshold;
  fs << "showOutput" << showOutput;
}

void KNN::load_config(cv::FileStorage &fs) {
  fs["history"] >> history;
  fs["dist2Threshold"] >> dist2Threshold;
  fs["shadowThreshold"] >> shadowThreshold;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/KNN.cpp --#--

--#-- START ./bgslibrary/algorithms/DPPratiMediod.cpp --#--
#include "DPPratiMediod.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

DPPratiMediod::DPPratiMediod() :
  IBGS(quote(DPPratiMediod)),
  frameNumber(0), threshold(30), samplingRate(5), 
  historySize(16), weight(5)
{
  debug_construction(DPPratiMediod);
  initLoadSaveConfig(algorithmName);
}

DPPratiMediod::~DPPratiMediod() {
  debug_destruction(DPPratiMediod);
}

void DPPratiMediod::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold;
    params.HighThreshold() = 2 * params.LowThreshold();	// Note: high threshold is used by post-processing
    params.SamplingRate() = samplingRate;
    params.HistorySize() = historySize;
    params.Weight() = weight;

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void DPPratiMediod::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "samplingRate" << samplingRate;
  fs << "historySize" << historySize;
  fs << "weight" << weight;
  fs << "showOutput" << showOutput;
}

void DPPratiMediod::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["samplingRate"] >> samplingRate;
  fs["historySize"] >> historySize;
  fs["weight"] >> weight;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/DPPratiMediod.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSimpleGaussian.cpp --#--
#include "LBSimpleGaussian.h"

using namespace bgslibrary::algorithms;

LBSimpleGaussian::LBSimpleGaussian() :
  IBGS(quote(LBSimpleGaussian)),
  sensitivity(66), noiseVariance(162), learningRate(18)
{
  debug_construction(LBSimpleGaussian);
  initLoadSaveConfig(algorithmName);
}

LBSimpleGaussian::~LBSimpleGaussian() {
  debug_destruction(LBSimpleGaussian);
  delete m_pBGModel;
}

void LBSimpleGaussian::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  IplImage* frame = cvCloneImage(&_frame);

  if (firstTime) {
    int w = img_input.size().width;
    int h = img_input.size().height;

    m_pBGModel = new lb::BGModelGauss(w, h);
    m_pBGModel->InitModel(frame);
  }

  m_pBGModel->setBGModelParameter(0, sensitivity);
  m_pBGModel->setBGModelParameter(1, noiseVariance);
  m_pBGModel->setBGModelParameter(2, learningRate);

  m_pBGModel->UpdateModel(frame);

  img_foreground = cv::cvarrToMat(m_pBGModel->GetFG());
  img_background = cv::cvarrToMat(m_pBGModel->GetBG());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&frame);

  firstTime = false;
}

void LBSimpleGaussian::save_config(cv::FileStorage &fs) {
  fs << "sensitivity" << sensitivity;
  fs << "noiseVariance" << noiseVariance;
  fs << "learningRate" << learningRate;
  fs << "showOutput" << showOutput;
}

void LBSimpleGaussian::load_config(cv::FileStorage &fs) {
  fs["sensitivity"] >> sensitivity;
  fs["noiseVariance"] >> noiseVariance;
  fs["learningRate"] >> learningRate;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/LBSimpleGaussian.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/maxflow.cpp --#--
#include <stdio.h>

#include "graph.h"

/*
special constants for node->parent
*/
#define TERMINAL ( (arc *) 1 )		/* to terminal */
#define ORPHAN   ( (arc *) 2 )		/* orphan */

#define INFINITE_D 1000000000		/* infinite distance to the terminal */

/***********************************************************************/

/*
Functions for processing active list.
i->next points to the next node in the list
(or to i, if i is the last node in the list).
If i->next is NULL iff i is not in the list.

There are two queues. Active nodes are added
to the end of the second queue and read from
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 bgslibrary
{
  namespace algorithms
  {
    namespace lbp_mrf
    {
      inline void Graph::set_active(node *i)
      {
        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;
        }
      }

      /*
          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]))
          {
            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;

          /* 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)
          {
            /* 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;
      }

      /***********************************************************************/

      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;


        /* 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;
          }
        }
        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::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))
            {
              /* 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--;
                }
              }
            }
          }

        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))
            {
              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;
              }
            }
          }
        }
      }

      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))
            {
              /* 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--;
                }
              }
            }
          }

        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))
            {
              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;
              }
            }
          }
        }
      }

      /***********************************************************************/

      Graph::flowtype Graph::maxflow()
      {
        node *i, *j, *current_node = NULL;
        arc *a;
        nodeptr *np, *np_next;

        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;
          }

          /* growth */
          if (!i->is_sink)
          {
            /* 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;

            /* 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;
        }

        delete nodeptr_block;

        return flow;
      }

      /***********************************************************************/

      Graph::termtype Graph::what_segment(node_id i)
      {
        if (((node*)i)->parent && !((node*)i)->is_sink) return SOURCE;
        return SINK;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBP_MRF/maxflow.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/graph.cpp --#--
#include <stdio.h>

#include "graph.h"

namespace bgslibrary
{
  namespace algorithms
  {
    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::node_id Graph::add_node()
      {
        node *i = node_block->New();

        i->first = NULL;
        i->tr_cap = 0;

        return (node_id)i;
      }

      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->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::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;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBP_MRF/graph.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MEHistogram.cpp --#--
#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>

#include "MEHistogram.hpp"
#include "MEDefs.hpp"
#include "MEImage.hpp"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

using namespace bgslibrary::algorithms::lbp_mrf;

MEHistogram::MEHistogram() {
  Clear();
}

MEHistogram::~MEHistogram(){}

void MEHistogram::Clear() {
  memset(&HistogramData, 0, 256 * sizeof(int));
}

bool MEHistogram::operator==(MEHistogram& histogram) const
{
  bool ret = true;

  for (int i = 255; i >= 0; --i)
  {
    if (HistogramData[i] != histogram.HistogramData[i])
    {
      ret = false;
      break;
    }
  }
  return ret;
}

void MEHistogram::Calculate(MEImage& image, int channel, HistogramType mode)
{
  int Channel = (channel < 1) ? 1 : ((channel > image.GetLayers()) ? image.GetLayers() : channel);

  if (mode == h_Overwrite)
  {
    Clear();
  }

  unsigned char *ImageData = image.GetImageData();
  int rowStart = 0;

  for (int i = image.GetHeight() - 1; i >= 0; i--)
  {
    for (int i1 = (image.GetWidth() - 1)*image.GetLayers() + Channel - 1; i1 >= Channel - 1; i1 -= image.GetLayers())
    {
      HistogramData[ImageData[rowStart + i1]]++;
    }
    rowStart += image.GetRowWidth();
  }
}

void MEHistogram::Calculate(MEImage& image, HistogramType mode)
{
  if (mode == h_Overwrite)
  {
    Clear();
  }

  unsigned char *ImageData = image.GetImageData();
  int RowStart = 0;
  int RowWidth = image.GetRowWidth();

  for (int i = image.GetHeight() - 1; i >= 0; i--)
  {
    for (int i1 = image.GetWidth()*image.GetLayers() - 1; i1 >= 0; i1--)
    {
      HistogramData[ImageData[RowStart + i1]]++;
    }
    RowStart += RowWidth;
  }
}

void MEHistogram::Calculate(MEImage& image, int channel, int x0, int y0, int x1, int y1)
{
  int Channel = (channel < 1) ? 1 : ((channel > image.GetLayers()) ? image.GetLayers() : channel);
  unsigned char *ImageData = image.GetImageData();
  int RowStart = 0;
  int RowWidth = image.GetRowWidth();
  int X0 = x0 > x1 ? x1 : x0;
  int Y0 = y0 > y1 ? y1 : y0;
  int X1 = x0 < x1 ? x1 : x0;
  int Y1 = y0 < y1 ? y1 : y0;

  Clear();

  // Compute the correct region coordinates and check them
  X0 = X0 < 0 ? 0 : X0;
  Y0 = Y0 < 0 ? 0 : Y0;
  X1 = X1 > image.GetWidth() - 1 ? image.GetWidth() - 1 : X1;
  Y1 = Y1 > image.GetHeight() - 1 ? image.GetHeight() - 1 : Y1;
  RowStart = Y0*image.GetRowWidth();

  for (int i = Y1; i >= Y0; --i)
  {
    for (int i1 = X1*image.GetLayers() + Channel - 1; i1 >= X0*image.GetLayers() + Channel - 1;
      i1 -= image.GetLayers())
    {
      HistogramData[ImageData[RowStart + i1]]++;
    }
    RowStart += RowWidth;
  }
}

int MEHistogram::GetPeakIndex() const
{
  int PeakIndex = 0;
  int PeakValue = 0;

  for (int i = 0; i < 256; i++)
  {
    if (PeakValue < HistogramData[i])
    {
      PeakValue = HistogramData[i];
      PeakIndex = i;
    }
  }
  return PeakIndex;
}

int MEHistogram::GetLowestLimitIndex(int threshold) const
{
  int MinIndex = 0;

  for (int i = 0; i < 256; i++)
  {
    if (threshold <= HistogramData[i])
    {
      MinIndex = i;
      break;
    }
  }
  return MinIndex;
}

int MEHistogram::GetHighestLimitIndex(int threshold) const
{
  int MaxIndex = 255;

  for (int i = 255; i >= 0; i--)
  {
    if (threshold <= HistogramData[i])
    {
      MaxIndex = i;
      break;
    }
  }
  return MaxIndex;
}

int MEHistogram::GetPowerAmount(int minindex, int maxindex) const
{
  int ValueAmount = 0;
  int MinIndex = (minindex > 255) ? 255 : ((minindex < 0) ? 0 : minindex);
  int MaxIndex = (maxindex > 255) ? 255 : ((maxindex < 0) ? 0 : maxindex);

  if (MinIndex > MaxIndex)
  {
    int TempInt = MinIndex;
    MinIndex = MaxIndex;
    MaxIndex = TempInt;
  }

  for (int i = MinIndex; i <= MaxIndex; i++)
  {
    ValueAmount += HistogramData[i];
  }
  return ValueAmount;
}

int MEHistogram::GetCentroidIndex() const
{
  int ValueAmount = GetPowerAmount(0, 255);
  int WeightedValueAmount = 1;
  int CentroidIndex = 0;

  // Calculate the normal and weighted amount of histogram values
  for (int i = 0; i < 256; i++)
  {
    WeightedValueAmount += i*HistogramData[i];
  }
  // Calculate the centroid point of the histogram
  CentroidIndex = WeightedValueAmount / ValueAmount;
  return CentroidIndex;
}

bool MEHistogram::Stretch(StretchType mode)
{
  int MinIndex = -1;
  int MaxIndex = -1;
  int Percent = -1;
  double Percentage = 0.0;
  double NextPercentage = 0.0;
  double Count = 0.0;
  double NewCount = 0.0;
  bool Ret = true;

  switch (mode)
  {
  case s_OwnMode:
    Percent = 20;
    MinIndex = GetLowestLimitIndex(Percent);
    MaxIndex = GetHighestLimitIndex(Percent);

    while ((abs(MaxIndex - MinIndex) < 52) && (Percent > 1))
    {
      Percent = Percent / 2;
      MinIndex = GetLowestLimitIndex(Percent);
      MaxIndex = GetHighestLimitIndex(Percent);

      // The calculation gives wrong answer back
      if (MinIndex == 0 && MaxIndex == 255)
      {
        MinIndex = 128;
        MaxIndex = 128;
        Ret = false;
      }
    }
    break;

  case s_GimpMode:
    Count = GetPowerAmount(0, 255);
    NewCount = 0;

    for (int i = 0; i < 255; i++)
    {
      double Value = 0.0;
      double NextValue = 0.0;

      Value = HistogramData[i];
      NextValue = HistogramData[i + 1];
      NewCount += Value;
      Percentage = NewCount / Count;
      NextPercentage = (NewCount + NextValue) / Count;

      if (fabs(Percentage - 0.006) < fabs(NextPercentage - 0.006))
      {
        MinIndex = i + 1;
        break;
      }
    }
    NewCount = 0.0;
    for (int i = 255; i > 0; i--)
    {
      double Value = 0.0;
      double NextValue = 0.0;

      Value = HistogramData[i];
      NextValue = HistogramData[i - 1];
      NewCount += Value;
      Percentage = NewCount / Count;
      NextPercentage = (NewCount + NextValue) / Count;

      if (fabs(Percentage - 0.006) < fabs(NextPercentage - 0.006))
      {
        MaxIndex = i - 1;
        break;
      }
    }
    break;

  default:
    break;
  }

  if (MaxIndex <= MinIndex)
  {
    MinIndex = 0;
    MaxIndex = 255;
    Ret = false;
  }
  if (MaxIndex - MinIndex <= 10 ||
    (MaxIndex - MinIndex <= 20 && (float)GetPowerAmount(MinIndex, MaxIndex) / GetPowerAmount(0, 255) < 0.20))
  {
    MinIndex = 0;
    MaxIndex = 255;
    Ret = false;
  }
  if (Ret)
  {
    unsigned char TransformedHistogram[256];

    for (int i = 0; i < 256; ++i)
    {
      TransformedHistogram[i] = (unsigned char)MEBound(0, 255 * (i - MinIndex) / (MaxIndex - MinIndex), 255);
    }
    for (int i = 0; i < 256; ++i)
    {
      HistogramData[i] = TransformedHistogram[i];
    }
  }
  return Ret;
}

MEHistogramTransform::MEHistogramTransform() : ChannelMode(p_SeparateChannels),
StretchMode(MEHistogram::s_GimpMode), DiscreteStretchingDone(false)
{
}

MEHistogramTransform::~MEHistogramTransform()
{
}

void MEHistogramTransform::HistogramStretch(MEImage& image)
{
  SetStretchProcessingMode(p_SeparateChannels, MEHistogram::s_GimpMode);
  HistogramStretch(image, t_Continuous);
}

void MEHistogramTransform::HistogramStretch(MEImage& image, TransformType time_mode)
{
  if (time_mode == t_Continuous)
  {
    DiscreteStretchingDone = false;
  }

  if (ChannelMode == p_Average || image.GetLayers() == 1)
  {
    if (time_mode == t_Continuous || (time_mode == t_Discrete && !DiscreteStretchingDone))
    {
      RedChannel.Calculate(image, 1, MEHistogram::h_Overwrite);

      for (int l = 1; l < image.GetLayers(); l++)
      {
        RedChannel.Calculate(image, l + 1, MEHistogram::h_Add);
      }
      RedChannel.Stretch(StretchMode);
      if (time_mode == t_Discrete && !DiscreteStretchingDone)
      {
        DiscreteStretchingDone = true;
      }
    }
    unsigned char *ImageData = image.GetImageData();
    int RowStart = 0;
    int RowWidth = image.GetRowWidth();

    for (int i = image.GetHeight() - 1; i >= 0; i--)
    {
      for (int i1 = image.GetWidth()*image.GetLayers() - 1; i1 >= 0; i1--)
      {
        ImageData[RowStart + i1] = RedChannel.HistogramData[ImageData[RowStart + i1]];
      }
      RowStart += RowWidth;
    }
  }
  else
    if (ChannelMode == p_SeparateChannels)
    {
      if (time_mode == t_Continuous || (time_mode == t_Discrete && !DiscreteStretchingDone))
      {
        RedChannel.Calculate(image, 1, MEHistogram::h_Overwrite);
        GreenChannel.Calculate(image, 2, MEHistogram::h_Overwrite);
        BlueChannel.Calculate(image, 3, MEHistogram::h_Overwrite);
        RedChannel.Stretch(StretchMode);
        GreenChannel.Stretch(StretchMode);
        BlueChannel.Stretch(StretchMode);
        if (time_mode == t_Discrete && !DiscreteStretchingDone)
        {
          DiscreteStretchingDone = true;
        }
      }
      unsigned char *ImageData = image.GetImageData();
      int RowStart = 0;
      int RowWidth = image.GetRowWidth();

      for (int i = image.GetHeight() - 1; i >= 0; i--)
      {
        for (int i1 = image.GetWidth()*image.GetLayers() - 3; i1 >= 0; i1 -= 3)
        {
          ImageData[RowStart + i1] = RedChannel.HistogramData[ImageData[RowStart + i1]];
          ImageData[RowStart + i1 + 1] = GreenChannel.HistogramData[ImageData[RowStart + i1 + 1]];
          ImageData[RowStart + i1 + 2] = BlueChannel.HistogramData[ImageData[RowStart + i1 + 2]];
        }
        RowStart += RowWidth;
      }
    }
}

void MEHistogramTransform::HistogramEqualize(MEImage& image)
{
  DiscreteStretchingDone = false;
  IplImage* cvDest8bitImg = NULL;
  IplImage* cvDestImg = NULL;

  switch (image.GetLayers())
  {
  case 1:
    // Grayscale image
    cvDest8bitImg = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1);
    cvEqualizeHist((IplImage*)image.GetIplImage(), cvDest8bitImg);
    image.SetIplImage((void*)cvDest8bitImg);
    cvReleaseImage(&cvDest8bitImg);
    break;

  case 3:
    // RGB image
    cvDestImg = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 3);
    IplImage *cvR, *cvG, *cvB;

    cvR = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1);
    cvG = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1);
    cvB = cvCreateImage(cvSize(image.GetWidth(), image.GetHeight()), 8, 1);

    cvSplit((IplImage*)image.GetIplImage(), cvR, cvG, cvB, NULL);
    cvEqualizeHist(cvR, cvR);
    cvEqualizeHist(cvG, cvG);
    cvEqualizeHist(cvB, cvB);
    cvMerge(cvR, cvG, cvB, NULL, cvDestImg);

    image.SetIplImage((void*)cvDestImg);
    cvReleaseImage(&cvR);
    cvReleaseImage(&cvG);
    cvReleaseImage(&cvB);
    cvReleaseImage(&cvDestImg);
    break;

  default:
    break;
  }
}

void MEHistogramTransform::SetStretchProcessingMode(ProcessingType new_channel_mode,
  MEHistogram::StretchType new_stretch_mode)
{
  DiscreteStretchingDone = false;

  switch (new_channel_mode)
  {
  case p_SeparateChannels:
    ChannelMode = new_channel_mode;
    break;

  case p_Average:
    ChannelMode = new_channel_mode;
    break;

  default:
    break;
  }

  switch (new_stretch_mode)
  {
  case MEHistogram::s_OwnMode:
    StretchMode = new_stretch_mode;
    break;

  case MEHistogram::s_GimpMode:
    StretchMode = new_stretch_mode;
    break;

  default:
    break;
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MEHistogram.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MotionDetection.cpp --#--
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc.hpp>

#include "MotionDetection.hpp"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "graph.h"
#include "MEHistogram.hpp"
#include "MEImage.hpp"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lbp_mrf
    {
      // 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()
      {
        if (MDMode != md_NotDefined)
        {
          ReleaseData();
        }
      }

      void MotionDetection::SetMode(DetectorType newmode)
      {
        if (MDMode != md_NotDefined && MDMode != newmode)
        {
          ReleaseData();
          Frames = 0;
          HUOFFrames = -1;
          HUOFCamMovement = false;
          HUOFCamMovementX = 0;
          ReadyMask = false;
        }

        switch (newmode)
        {
        case md_LBPHistograms:
          MDMode = md_LBPHistograms;
          break;

        case md_DLBPHistograms:
          MDMode = md_DLBPHistograms;
          break;

        default:
          MDMode = md_LBPHistograms;
          break;
        }
      }

      float MotionDetection::GetParameter(ParametersType param) const
      {
        float ret = 0.0;

        switch (param)
        {
        case mdp_HUProximityThreshold:
          ret = (float)HUPrThres;
          break;

        case mdp_HUBackgroundThreshold:
          ret = (float)HUBackgrThres;
          break;

        case mdp_HUHistogramLearningRate:
          ret = (float)HUHistLRate;
          break;

        case mdp_HUWeightsLearningRate:
          ret = (float)HUWeightsLRate;
          break;

        case mdp_HUMinCutWeight:
          ret = (float)HUMinCutWeight;
          break;

        case mdp_HUDesiredSamplePixels:
          ret = (float)HUDesiredSamplePixels;
          break;

        case mdp_HUHistogramsPerPixel:
          ret = (float)HUHistogramsPerPixel;
          break;

        case mdp_HUHistogramArea:
          ret = (float)HUHistogramArea;
          break;

        case mdp_HUHistogramBins:
          ret = (float)HUHistogramBins;
          break;

        case mdp_HUColorSpace:
          ret = (float)HUColorSpace;
          break;

        case mdp_HULBPMode:
          ret = (float)HULBPMode;
          break;

        default:
          break;
        }
        return ret;
      }

      void MotionDetection::SetParameter(ParametersType param, float value)
      {
        switch (param)
        {
        case mdp_HUProximityThreshold:
          HUPrThres = (float)value;
          break;

        case mdp_HUBackgroundThreshold:
          HUBackgrThres = (float)value;
          break;

        case mdp_HUHistogramLearningRate:
          HUHistLRate = (float)value;
          break;

        case mdp_HUWeightsLearningRate:
          HUWeightsLRate = (float)value;
          break;

        case mdp_HUMinCutWeight:
          HUMinCutWeight = (float)value;
          break;

        case mdp_HUDesiredSamplePixels:
          HUDesiredSamplePixels = (int)value;
          break;

        case mdp_HUHistogramsPerPixel:
          HUHistogramsPerPixel = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramsPerPixel;
          break;

        case mdp_HUHistogramArea:
          HUHistogramArea = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramArea;
          break;

        case mdp_HUHistogramBins:
          HUHistogramBins = (MDDataState == ps_Uninitialized) ? (int)value : HUHistogramBins;
          break;

        case mdp_HUColorSpace:
          HUColorSpace = (MDDataState == ps_Uninitialized) ? (int)value : HUColorSpace;
          break;

        case mdp_HULBPMode:
          HULBPMode = (MDDataState == ps_Uninitialized) ? (int)value : HULBPMode;
          break;

        default:
          break;
        }
      }

      void MotionDetection::DetectMotions(MEImage& image)
      {
        switch (MDMode)
        {
        case md_LBPHistograms:
        case md_DLBPHistograms:
          DetectMotionsHU(image);
          break;

        default:
          break;
        }
      }

      void MotionDetection::GetMotionsMask(MEImage& mask_image)
      {
        if (ReadyMask)
        {
          mask_image = MaskImage;
        }

        switch (MDMode)
        {
        case md_LBPHistograms:
        case md_DLBPHistograms:
          GetMotionsMaskHU(MaskImage);
          break;

        default:
          break;
        }

        ReadyMask = true;
        mask_image = MaskImage;
      }

      void MotionDetection::CalculateResults(MEImage& referenceimage, int& tnegatives, int& tpositives,
        int& ttnegatives, int& ttpositives)
      {
        if (MDDataState != ps_Successful)
        {
          printf("No data for calculation.\n");
          return;
        }

        if (referenceimage.GetLayers() != 1)
          referenceimage.ConvertToGrayscale(MEImage::g_OpenCV);

        referenceimage.Binarize(1);

        MEImage mask_image;

        GetMotionsMask(mask_image);

        if ((mask_image.GetWidth() != referenceimage.GetWidth()) ||
          (mask_image.GetHeight() != referenceimage.GetHeight()))
        {
          printf("Different resolutions of mask<->reference image.\n");
          return;
        }

        unsigned char* RefMaskImgData = referenceimage.GetImageData();
        unsigned char* MaskImgData = mask_image.GetImageData();
        int RowStart = 0;
        int RowWidth = referenceimage.GetRowWidth();

        int TrueNegatives = 0;
        int TruePositives = 0;
        int TotalTrueNegatives = 0;
        int TotalTruePositives = 0;

        int ImageFrame = 0;

        if (MDMode == md_LBPHistograms || md_DLBPHistograms)
        {
          ImageFrame = HUHistogramArea / 2;
        }

        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;
        }

        tnegatives = TrueNegatives;
        ttnegatives = TotalTrueNegatives;
        tpositives = TruePositives;
        ttpositives = TotalTruePositives;
      }

      void MotionDetection::ReleaseData()
      {
        if (MDMode == md_LBPHistograms || MDMode == md_DLBPHistograms)
        {
          ReleaseHUData();
        }
      }

      void MotionDetection::InitHUData(int imagewidth, int imageheight)
      {
        if ((HUImageWidth != imagewidth - HUHistogramArea + 1) ||
          (HUImageHeight != imageheight - HUHistogramArea + 1) ||
          (MDDataState == ps_Uninitialized))
        {
          if (MDDataState != ps_Uninitialized)
          {
            ReleaseHUData();
          }

          MDDataState = ps_Initialized;

          HUImageWidth = imagewidth - HUHistogramArea + 1;
          HUImageHeight = imageheight - HUHistogramArea + 1;

          HULBPPixelData = new MEPixelDataType**[HUImageWidth / 2];

          for (int i = 0; i < HUImageWidth / 2; ++i)
          {
            HULBPPixelData[i] = new MEPixelDataType*[HUImageHeight];
          }

          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];
            }

          // Allocate auxiliary variables
          HUMaskColumnAddDel = new int*[HUHistogramArea];
          for (int i = 0; i < HUHistogramArea; ++i)
            HUMaskColumnAddDel[i] = new int[2];

          HUMaskRowAddDel = new int*[HUHistogramArea];
          for (int i = 0; i < HUHistogramArea; ++i)
            HUMaskRowAddDel[i] = new int[2];

          // Generate sample mask
          SetSampleMaskHU(sm_Circle, HUDesiredSamplePixels);

          // Init HU optical flow data
          if (MDMode == md_DLBPHistograms)
            InitHUOFData(imagewidth, imageheight);

          ClearHUData();
        }
      }

      void MotionDetection::InitHUOFData(int imagewidth, int imageheight)
      {
        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]));
        }
      }

      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];
            }

          for (int i = 0; i < HUImageWidth / 2; i++)
          {
            delete[] HULBPPixelData[i];
          }
          delete[] HULBPPixelData;

          if (MDMode == md_DLBPHistograms)
            ReleaseHUOFData();

          HUImageWidth = -1;
          HUImageHeight = -1;
          HULBPPixelData = NULL;
          MDDataState = ps_Uninitialized;

          // Release auxiliary variables
          for (int i = 0; i < HUHistogramArea; ++i)
            delete[] HUMaskColumnAddDel[i];
          delete[] HUMaskColumnAddDel;

          for (int i = 0; i < HUHistogramArea; ++i)
            delete[] HUMaskRowAddDel[i];
          delete[] HUMaskRowAddDel;

          HUMaskColumnAddDel = NULL;
          HUMaskRowAddDel = NULL;
        }
      }

      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;
        }
      }

      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;
        }
      }

      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());
        }

        if (newimage.GetLayers() == 1)
        {
          newimage.ConvertGrayscaleToRGB();
        }

        MEImage blueimage = newimage;
        blueimage.ColorSpace(MEImage::csc_RGBtoCIELuv);

        if (HUColorSpace != -1)
        {
          newimage.ColorSpace((MEImage::ColorSpaceConvertType)HUColorSpace);
        }

        if (Frames == 0)
        {
          MEImage BlueLayer;
          blueimage.GetLayer(BlueLayer, 1);
          BlueLayer.Resize(32, 24);
          PreviousBlueLayer = BlueLayer;
        }

        Frames++;

        // Detect the fast, big changes in the scene
        MEImage BlueLayer;
        blueimage.GetLayer(BlueLayer, 1);
        BlueLayer.Resize(32, 24);
        DiffAreas = BlueLayer.DifferenceAreas(PreviousBlueLayer, 12);

        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;

        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();
            }
          }

        newimage.ConvertToGrayscale(MEImage::g_OpenCV);

        if (HULBPMode != -1)
        {
          newimage.LBP((MEImage::LBPType)HULBPMode);
        }

        // Set some auxiliary variables
        ImgData = newimage.GetImageData();
        int DivisionOperator = (int)(log((double)256 / HUHistogramBins) / log((double) 2.)) + 1;

        // Downscale the image
        for (int i = newimage.GetRowWidth()*newimage.GetHeight() - 1; i >= 0; --i)
        {
          ImgData[i] >>= DivisionOperator;
        }

        UpdateModelHU(newimage, HULBPPixelData);

        // Change the state of the HU data structures
        if (MDDataState == ps_Initialized)
        {
          MDDataState = ps_Successful;
        }
        HUOFCamMovement = false;
        ReadyMask = false;
      }

      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)
              {
                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));

              model[(HUImageWidth - 1) / 2][y] = PixelData;
            }
            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)(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));
                }
              }
            }
          }

          // Copy the histogram
          memcpy(CurrentHistogram2, CurrentHistogram, HUHistogramBins * sizeof(float));

          // This cycle generates a column of histograms
          for (int x = HUImageWidth - 2; x >= 0; --x)
          {
            RowStart = RowWidth*y;

            // 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]]]--;

              RowStart += RowWidth;
            }
            if (x % 2 == 0)
            {
              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;
      }

      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;

        // 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);

          if (PixelData->BackgroundHistogram[i] &&
            IntersectionResults[i] > PixelData->BackgroundRate)
          {
            PixelData->BackgroundRate = IntersectionResults[i];
          }

          if (MaxValue < IntersectionResults[i])
          {
            MaxValue = IntersectionResults[i];
            MaxIndex = i;
          }

          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;
            }
          }

          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];

          for (int i1 = HUHistogramsPerPixel - 1; i1 >= 0; --i1)
            PixelData->Weights[i1] = PixelData->Weights[i1] / sum;

          return;
        }

        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);

        // 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);

        // 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];

        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];

              Weights[i][0] = Weights[i - 1][0];
              Weights[i][1] = Weights[i - 1][1];

              Weights[i - 1][0] = tmp;
              Weights[i - 1][1] = tmp2;
            }
          }

        float Sum = 0;
        int i = 0;

        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;
      }

      void MotionDetection::OpticalFlowCorrection()
      {
        IplImage *PreviousGray = NULL, *CurrentGray = NULL;
        char* PointsStatus = (char*)cvAlloc(HUOFPointsNumber);
        int i = 0, i1 = 0;

        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();

        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);
        }

        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];

        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))
              {
                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++;
            }
          }
        }

        // 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;

        // Calculate the final camera movement
        for (i = 0; i < DistanceMax; ++i)
        {
          if ((Distances[i][2] <= MaxTrackedPoints / 10))
            break;

          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]);
          }

          MoveX += Distances[i][0] * Distances[i][2];
          MoveY += Distances[i][1] * Distances[i][2];
          SampleNums += Distances[i][2];
        }

        if (SampleNums > 0)
        {
          MoveX = MERound(MoveX / SampleNums);
          MoveY = MERound(MoveY / SampleNums);
        }

        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];

            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)
            {
              for (int x = MaxX; x >= 0; --x)
              {
                PreviousData[x][y] = NULL;
              }
            }

            // Move the LBP data to new locations
            for (int y = MaxY; y >= 0; --y)
            {
              for (int x = MaxX; x >= 0; --x)
              {
                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;
                  }
                }
              }
            }

            // Release unused data
            for (int y = MaxY; y >= 0; --y)
            {
              for (int x = MaxX; x >= 0; --x)
              {
                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;
          }
        }

        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 (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;
      }

      void MotionDetection::GetMotionsMaskHU(MEImage& mask_image)
      {
        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))
        {
          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();
          }
        }

        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)));

            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);
            }
          }
        }

        LBPGraph->maxflow();

        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;
          }
        }

        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;
        }

        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);

        for (int i = 0; i < HUImageWidth / 2; ++i)
          delete[] Nodes[i];
        delete[] Nodes;
      }

      void MotionDetection::SetSampleMaskHU(SampleMaskType mask_type, int desiredarea)
      {
        if (HUMaskColumnAddDel == NULL || HUMaskRowAddDel == NULL)
        {
          printf("Auxiliary variables are NULL\n");
          return;
        }

        // Generate a mask for computing the histograms
        IplImage *MaskImage = cvCreateImage(cvSize(HUHistogramArea, HUHistogramArea), 8, 1);
        int DesiredArea = desiredarea <= 0 ? HUHistogramBins * 2 : desiredarea;

        int **CalculationMask = new int*[HUHistogramArea];
        for (int i = 0; i < HUHistogramArea; ++i)
          CalculationMask[i] = new int[HUHistogramArea];

        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));

        cvSetZero(MaskImage);

        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;

            if (MaskImage->imageData[i*MaskImage->widthStep + j] == 0)
            {
              MaskImage->imageData[i*MaskImage->widthStep + j] = 1;
              HUSamplePixels++;
            }
          }
          break;

        default:
          cvCircle(MaskImage, cvPoint(HUHistogramArea / 2, HUHistogramArea / 2),
            (int)MERound(sqrt((float)DesiredArea / ME_PI_VALUE)), CV_RGB(1, 1, 1), -1);
          break;
        }

        HUSamplePixels = 0;
        //memset(CalculationMask, 0, sizeof(CalculationMask));

        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)
        {
          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;
            }
          }
        }

        // 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;
            }
          }
        }

        // Freeing memory
        cvReleaseImage(&MaskImage);

        for (int i = 0; i < HUHistogramArea; ++i)
          delete[] CalculationMask[i];
        delete[] CalculationMask;
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MotionDetection.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MEDefs.cpp --#--
#include <math.h>

#include "MEDefs.hpp"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

//using namespace bgslibrary::algorithms::lbp_mrf;

namespace bgslibrary
{
  namespace algorithms
  {
    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;
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MEDefs.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MEImage.cpp --#--
#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"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

//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;

namespace bgslibrary
{
  namespace algorithms
  {
    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);
      }

      MEImage::MEImage(const MEImage& other) : cvImg(NULL)
      {
        _Copy(other);
      }

      MEImage::~MEImage()
      {
        if (ME_CAST_TO_IPLIMAGE(cvImg))
        {
          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;

        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);

          LayerNumber = ME_CAST_TO_IPLIMAGE(cvImg)->nChannels;
        }
        if (LayerNumber <= 0)
        {
          printf("The given layer number is too small (%d <= 0)\n", LayerNumber);
          LayerNumber = 1;
        }

        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::SetLayer(MEImage& layer, int layer_number)
      {
        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);
      }

      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);
      }

      void* MEImage::GetIplImage() const
      {
        return (void*)ME_CAST_TO_IPLIMAGE(cvImg);
      }

      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;
        }
      }

      bool MEImage::operator==(const MEImage& image)
      {
        return Equal(image);
      }

      bool MEImage::operator!=(const MEImage& image)
      {
        return !operator==(image);
      }

      MEImage& MEImage::operator=(const MEImage& other_image)
      {
        if (&other_image == this)
          return *this;

        _Copy(other_image);
        return *this;
      }

      int MEImage::GetWidth() const
      {
        return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->width : 0;
      }

      int MEImage::GetRowWidth() const
      {
        return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->widthStep : 0;
      }

      int MEImage::GetHeight() const
      {
        return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->height : 0;
      }

      int MEImage::GetLayers() const
      {
        return ME_CAST_TO_IPLIMAGE(cvImg) ? ME_CAST_TO_IPLIMAGE(cvImg)->nChannels : 0;
      }

      int MEImage::GetPixelDataNumber() const
      {
        return ME_CAST_TO_IPLIMAGE(cvImg) ? GetWidth()*GetHeight()*GetLayers() : 0;
      }

      unsigned char* MEImage::GetImageData() const
      {
        return ME_CAST_TO_IPLIMAGE(cvImg) ? (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData : NULL;
      }

      void MEImage::SetData(unsigned char* image_data, int width, int height, int channels)
      {
        _Init(width, height, channels);

        for (int y = height - 1; y >= 0; --y)
        {
          int Start = GetRowWidth()*y;
          int Start2 = width*channels*y;

          memcpy(&ME_CAST_TO_IPLIMAGE(cvImg)->imageData[Start], &image_data[Start2], width*channels);
        }
      }

      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::Realloc(int width, int height, int layers)
      {
        _Init(width, height, layers);
      }

      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);

        cvResize(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_INTER_NN);
        ME_RELEASE_IPLIMAGE(cvImg);
        cvImg = TempImg;
      }


      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::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::MirrorHorizontal()
      {
        cvFlip(ME_CAST_TO_IPLIMAGE(cvImg), NULL, 1);
      }

      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);

        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::CopyImageInside(int x, int y, MEImage& source_image)
      {
        int NewX = x;
        int NewY = y;
        int PasteLengthX = source_image.GetWidth();
        int PasteLengthY = source_image.GetHeight();

        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::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);

        cvErode(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations);
        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);

        cvDilate(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, NULL, iterations);
        ME_RELEASE_IPLIMAGE(cvImg);
        cvImg = TempImg;
      }

      void MEImage::Smooth()
      {
        SmoothAdvanced(s_Median, 3);
      }

      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);

        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::Canny()
      {
        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);
        cvCanny(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, 800, 1100, 5);
        ME_RELEASE_IPLIMAGE(cvImg);
        cvImg = TempImg;
      }

      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::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;

        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::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;

        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::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::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)
        {
          if (MaskImageData[i] == 0)
          {
            ImageData[i] = 0;
          }
        }
      }

      void MEImage::ColorSpace(ColorSpaceConvertType mode)
      {
        IplImage* TempImg = NULL;
        unsigned char* ImageData = NULL;
        int WidthStep = 0;
        int RowStart = 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)
            {
              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;

        default:
          break;
        }
      }

      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;

        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;

          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;
        }
      }

      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);

        cvCvtColor(ME_CAST_TO_IPLIMAGE(cvImg), TempImg, CV_GRAY2RGB);
        ME_RELEASE_IPLIMAGE(cvImg);
        cvImg = TempImg;
      }

      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);
      }

      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;

        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;

        default:
          break;
        }
        ME_RELEASE_IPLIMAGE(cvImg);
        cvImg = TempImg;
      }

      void MEImage::Binarize(int threshold)
      {
        unsigned char* ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData;

        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::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;

        switch (mode)
        {
        case sub_Normal:
          ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData;
          DstData = source.GetImageData();
          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] - DstData[RowStart + x] < 0 ? 0 :
                ImageData[RowStart + x] - DstData[RowStart + x];
            }
            RowStart += WidthStep;
          }
          break;

        case sub_Absolut:
          ImageData = (unsigned char*)ME_CAST_TO_IPLIMAGE(cvImg)->imageData;
          DstData = source.GetImageData();
          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] -
                DstData[RowStart + x] < 0 ? -ImageData[RowStart + x] +
                DstData[RowStart + x] : ImageData[RowStart + x] - DstData[RowStart + x];
            }
            RowStart += WidthStep;
          }
          break;

        default:
          break;
        }
      }

      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)
          {
            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;
        }
      }

      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;

        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;

        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)
            {
              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;
        }
        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;

        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;

        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;

        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, 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;

        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;
      }

      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;

        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);
      }

      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;
        }

        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;

        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;

            if (Value == 255)
            {
              Counter++;
            }
          }
        return Counter;
      }

      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);
        }

        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);
        }

        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)));

            GradientVector(false, x, y, (int)(0.707*masksize), Resultx, Resulty);

            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);
          }
      }

      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;
      }

      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);
      }

      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 = 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;
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MEImage.cpp --#--

--#-- START ./bgslibrary/algorithms/CodeBook.cpp --#--
#include "CodeBook.h"

using namespace bgslibrary::algorithms;

CodeBook::CodeBook() :
  IBGS(quote(CodeBook)),
  t(0), learningFrames(DEFAULT_LEARNFRAMES), 
  alpha(DEFAULT_ALPHA), beta(DEFAULT_BETA)
{
  debug_construction(CodeBook);
  initLoadSaveConfig(algorithmName);
}

CodeBook::~CodeBook() {
  debug_destruction(CodeBook);
}

void CodeBook::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);
  
  if(firstTime) {
    img_foreground = cv::Mat::zeros(img_input.size(), CV_8UC1);
    //img_background = cv::Mat::zeros(img_input.size(), CV_8UC3);
    initializeCodebook(img_input.rows, img_input.cols);
  } 

  cv::Mat img_input_gray;

  if (img_input.channels() == 1)
    img_input_gray = img_input; 
  else
    cv::cvtColor(img_input, img_input_gray, CV_BGR2GRAY);

  fg_cb(img_input_gray, img_foreground);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    //cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  //img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void CodeBook::initializeCodebook(int w, int h)
{
  cbMain = new std::vector<codeword>*[w];
  for (int i = 0; i < w; ++i)
    cbMain[i] = new std::vector<codeword>[h];

  cbCache = new std::vector<codeword>*[w];
  for (int i = 0; i < w; ++i)
    cbCache[i] = new std::vector<codeword>[h];
}

void CodeBook::update_cb(const cv::Mat& frame)
{
  if (t > learningFrames)
    return;

  for (int i = 0; i < frame.rows; i++)
  {
    for (int j = 0; j < frame.cols; j++)
    {
      int pix = frame.at<uchar>(i, j);
      std::vector<codeword>& cm = cbMain[i][j];
      bool found = false;
      for (int k = 0; k < (int)cm.size(); k++)
      {
        if (cm[k].min <= pix && pix <= cm[k].max && !found)
        {
          found = true;
          cm[k].min = ((pix - alpha) + (cm[k].f*cm[k].min)) / (cm[k].f + 1);
          cm[k].max = ((pix + alpha) + (cm[k].f*cm[k].max)) / (cm[k].f + 1);
          cm[k].l = 0;
          cm[k].last = t;
          cm[k].f++;
        }
        else
        {
          cm[k].l++;
        }
      }
      if (!found)
      {
        codeword n = {};
        n.min = std::max(0, pix - alpha);
        n.max = std::min(255, pix + alpha);
        n.f = 1;
        n.l = 0;
        n.first = t;
        n.last = t;
        cm.push_back(n);
      }
    }
  }
  t++;
}

void CodeBook::fg_cb(const cv::Mat& frame, cv::Mat& fg)
{
  //fg = cv::Mat::zeros(frame.size(), CV_8UC1);
  //if (cbMain == 0) initializeCodebook(frame.rows, frame.cols);
  
  if (t <= learningFrames) {
    update_cb(frame);
    return;
  }

  for (int i = 0; i<frame.rows; i++)
  {
    for (int j = 0; j<frame.cols; j++)
    {
      int pix = frame.at<uchar>(i, j);
      std::vector<codeword>& cm = cbMain[i][j];
      bool found = false;
      for (int k = 0; k < (int)cm.size(); k++)
      {
        if (cm[k].min <= pix && pix <= cm[k].max && !found)
        {
          cm[k].min = ((1 - beta)*(pix - alpha)) + (beta*cm[k].min);
          cm[k].max = ((1 - beta)*(pix + alpha)) + (beta*cm[k].max);
          cm[k].l = 0;
          cm[k].first = t;
          cm[k].f++;
          found = true;
        }
        else
          cm[k].l++;
      }
      cm.erase(remove_if(cm.begin(), cm.end(), [](codeword& c) { return c.l >= Tdel; }), cm.end());
      fg.at<uchar>(i, j) = found ? 0 : 255;
      if (found) continue;
      found = false;
      std::vector<codeword>& cc = cbCache[i][j];
      for (int k = 0; k < (int)cc.size(); k++)
      {
        if (cc[k].min <= pix && pix <= cc[k].max && !found)
        {
          cc[k].min = ((1 - beta)*(pix - alpha)) + (beta*cc[k].min);
          cc[k].max = ((1 - beta)*(pix + alpha)) + (beta*cc[k].max);
          cc[k].l = 0;
          cc[k].first = t;
          cc[k].f++;
          found = true;
        }
        else
          cc[k].l++;
      }

      if (!found)
      {
        codeword n = {};
        n.min = std::max(0, pix - alpha);
        n.max = std::min(255, pix + alpha);
        n.f = 1;
        n.l = 0;
        n.first = t;
        n.last = t;
        cc.push_back(n);
      }

      cc.erase(remove_if(cc.begin(), cc.end(), [](codeword& c) { return c.l >= Th; }), cc.end());

      for (std::vector<codeword>::iterator it = cc.begin(); it != cc.end(); it++)
        if (it->f > Tadd)
          cm.push_back(*it);

      cc.erase(remove_if(cc.begin(), cc.end(), [](codeword& c) { return c.f > Tadd; }), cc.end());
    }
  }
}

void CodeBook::save_config(cv::FileStorage &fs) {
  fs << "alpha" << alpha;
  fs << "beta" << beta;
  fs << "learningFrames" << learningFrames;
  fs << "showOutput" << showOutput;
}

void CodeBook::load_config(cv::FileStorage &fs) {
  fs["alpha"] >> alpha;
  fs["beta"] >> beta;
  fs["learningFrames"] >> learningFrames;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/CodeBook.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/MeanBGS.cpp --#--
#include "MeanBGS.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

void MeanBGS::Initalize(const BgsParams& param)
{
  m_params = (MeanParams&)param;

  m_mean = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_32F, 3);
  m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3);
}

void MeanBGS::InitModel(const RgbImage& data)
{
  for (unsigned int r = 0; r < m_params.Height(); ++r)
  {
    for (unsigned int c = 0; c < m_params.Width(); ++c)
    {
      for (int ch = 0; ch < NUM_CHANNELS; ++ch)
      {
        m_mean(r, c, ch) = (float)data(r, c, ch);
      }
    }
  }
}

void MeanBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask)
{
  // update background model
  for (unsigned int r = 0; r < m_params.Height(); ++r)
  {
    for (unsigned int c = 0; c < m_params.Width(); ++c)
    {
      // perform conditional updating only if we are passed the learning phase
      if (update_mask(r, c) == BACKGROUND || frame_num < m_params.LearningFrames())
      {
        // update B/G model
        float mean;
        for (int ch = 0; ch < NUM_CHANNELS; ++ch)
        {
          mean = m_params.Alpha() * m_mean(r, c, ch) + (1.0f - m_params.Alpha()) * data(r, c, ch);
          m_mean(r, c, ch) = mean;
          m_background(r, c, ch) = (unsigned char)(mean + 0.5);
        }
      }
    }
  }
}

void MeanBGS::SubtractPixel(int r, int c, const RgbPixel& pixel,
  unsigned char& low_threshold,
  unsigned char& high_threshold)
{
  // calculate distance to sample point
  float dist = 0;
  for (int ch = 0; ch < NUM_CHANNELS; ++ch)
  {
    dist += (pixel(ch) - m_mean(r, c, ch))*(pixel(ch) - m_mean(r, c, ch));
  }

  // determine if sample point is F/G or B/G pixel
  low_threshold = BACKGROUND;
  if (dist > m_params.LowThreshold())
  {
    low_threshold = FOREGROUND;
  }

  high_threshold = BACKGROUND;
  if (dist > m_params.HighThreshold())
  {
    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 
//					values: 255-foreground, 0-background
///////////////////////////////////////////////////////////////////////////////
void MeanBGS::Subtract(int frame_num, const RgbImage& data,
  BwImage& low_threshold_mask, BwImage& high_threshold_mask)
{
  unsigned char low_threshold, high_threshold;

  // 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)
    {
      // perform background subtraction + update background model
      SubtractPixel(r, c, data(r, c), low_threshold, high_threshold);

      // setup silhouette mask
      low_threshold_mask(r, c) = low_threshold;
      high_threshold_mask(r, c) = high_threshold;
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/MeanBGS.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/ZivkovicAGMM.cpp --#--
#include "ZivkovicAGMM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

ZivkovicAGMM::ZivkovicAGMM()
{
  m_modes = NULL;
  m_modes_per_pixel = NULL;
}

ZivkovicAGMM::~ZivkovicAGMM()
{
  delete[] m_modes;
  delete[] m_modes_per_pixel;
}

void ZivkovicAGMM::Initalize(const BgsParams& param)
{
  m_params = (ZivkovicParams&)param;

  m_num_bands = 3;							//always 3 - not implemented for other values!
  m_bg_threshold = 0.75f;				//1-cf from the paper
  m_variance = 36.0f;						// variance for the new mode
  m_complexity_prior = 0.05f;		// complexity reduction prior constant

  // GMM for each pixel
  m_modes = new GMM[m_params.Size()*m_params.MaxModes()];

  // used modes per pixel
  m_modes_per_pixel = new unsigned char[m_params.Size()];

  m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3);
}

void ZivkovicAGMM::InitModel(const RgbImage& data)
{
  for (unsigned int i = 0; i < m_params.Size(); ++i)
  {
    m_modes_per_pixel[i] = 0;
  }

  for (unsigned int i = 0; i < m_params.Size()*m_params.MaxModes(); ++i)
  {
    m_modes[i].weight = 0;
    m_modes[i].sigma = 0;
    m_modes[i].muR = 0;
    m_modes[i].muG = 0;
    m_modes[i].muB = 0;
  }
}

void ZivkovicAGMM::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 ZivkovicAGMM::SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char* pModesUsed,
  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 = 0;
  bool bBackgroundLow = false;
  bool bBackgroundHigh = false;

  float fOneMinAlpha = 1 - m_params.Alpha();

  float prune = -m_params.Alpha()*m_complexity_prior;

  int nModes = *pModesUsed;
  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 < nModes; ++i)
  {
    if (sum < m_bg_threshold)
    {
      backgroundGaussians++;
      sum += m_modes[posPixel + i].weight;
    }
    else
    {
      break;
    }
  }

  // update all distributions and check for match with current pixel
  for (int iModes = 0; iModes < nModes; 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].sigma;
      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;

      //check fit
      if (dist < m_params.LowThreshold()*var)
      {
        /////
        //belongs to the mode
        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 + prune;
        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 update speed for cov matrice
        //not needed
        //k=k>20*m_m_params.Alpha()?20*m_m_params.Alpha():k;
        //float sigmanew = var + k*((0.33*(dR*dR+dG*dG+dB*dB))-var);
        //float sigmanew = var + k*((dR*dR+dG*dG+dB*dB)-var);
        //float sigmanew = var + k*((0.33*dist)-var);
        float sigmanew = var + k*(dist - var);

        //limit the variance
        m_modes[pos].sigma = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew;

        // Sort weights so they are in desending order. Note that only the weight for this
        // mode will increase and that the weight for all modes that were previously larger than
        // this one have already been modified and will not be modified again. Thus, we just need to
        // the correct position of this mode in the already sorted list.

        // Zivkovic implementation has been modified for clarity, but the results are equivalent
        /*
        for (int iLocal = iModes;iLocal>0;iLocal--)
        {
          long posLocal=posPixel + iLocal;
          if (weight < (m_modes[posLocal-1].weight))
          {
            break;
          }
          else
          {
            //swap
            GMM temp = m_modes[posLocal];
            m_modes[posLocal] = m_modes[posLocal-1];
            m_modes[posLocal-1] = temp;
          }
        }
        */

        for (int iLocal = iModes; iLocal > 0; iLocal--)
        {
          long posLocal = posPixel + iLocal;
          if (m_modes[posLocal].weight > m_modes[posLocal - 1].weight)
          {
            //swap
            GMM temp = m_modes[posLocal];
            m_modes[posLocal] = m_modes[posLocal - 1];
            m_modes[posLocal - 1] = temp;
          }
          else
          {
            break;
          }
        }
      }
      else
      {
        weight = fOneMinAlpha*weight + prune;
        //check prune
        if (weight < -prune)
        {
          weight = 0.0;
          nModes--;
        }
        m_modes[pos].weight = weight;
      }
      //check if it fits the current mode (2.5 sigma)
      ///////
    }
    //fit not found yet
    /////
    else
    {
      weight = fOneMinAlpha*weight + prune;
      //check prune
      if (weight < -prune)
      {
        weight = 0.0;
        nModes--;
      }
      m_modes[pos].weight = weight;
    }
    totalWeight += weight;
  }

  //renormalize weights so they sum to 1
  for (int iLocal = 0; iLocal < nModes; iLocal++)
  {
    m_modes[posPixel + iLocal].weight = m_modes[posPixel + iLocal].weight / totalWeight;
  }

  //make new mode if needed and exit
  if (!bFitsPDF)
  {
    if (nModes == m_params.MaxModes())
    {
      //replace the weakest
    }
    else
    {
      nModes++;
    }
    pos = posPixel + nModes - 1;

    if (nModes == 1)
      m_modes[pos].weight = 1;
    else
      m_modes[pos].weight = m_params.Alpha();

    // Zivkovic implementation changes as this will not result in the
    // weights adding to 1
    /*
    int iLocal;
    for (iLocal = 0; iLocal < m_params.MaxModes()odes-1; iLocal++)
    {
      m_modes[posPixel+ iLocal].weight *= fOneMinAlpha;
    }
    */

    // Revised implementation:
    //renormalize weights
    int iLocal;
    float sum = 0.0;
    for (iLocal = 0; iLocal < nModes; iLocal++)
    {
      sum += m_modes[posPixel + iLocal].weight;
    }

    float invSum = 1.0f / sum;
    for (iLocal = 0; iLocal < nModes; iLocal++)
    {
      m_modes[posPixel + iLocal].weight *= invSum;
    }

    m_modes[pos].muR = pixel(0);
    m_modes[pos].muG = pixel(1);
    m_modes[pos].muB = pixel(2);
    m_modes[pos].sigma = m_variance;

    // Zivkovic implementation to sort GMM so they are sorted in descending order according to their weight.
    // It has been revised for clarity, but the results are equivalent
    /*
    for (iLocal = m_params.MaxModes()odes-1; iLocal > 0; iLocal--)
    {
      long posLocal = posPixel + iLocal;
      if (m_params.Alpha() < (m_modes[posLocal-1].weight))
      {
        break;
      }
      else
      {
        //swap
        GMM temp = m_modes[posLocal];
        m_modes[posLocal] = m_modes[posLocal-1];
        m_modes[posLocal-1] = temp;
      }
    }
    */

    // sort GMM so they are sorted in descending order according to their weight
    for (iLocal = nModes - 1; iLocal > 0; iLocal--)
    {
      long posLocal = posPixel + iLocal;
      if (m_modes[posLocal].weight > m_modes[posLocal - 1].weight)
      {
        //swap
        GMM temp = m_modes[posLocal];
        m_modes[posLocal] = m_modes[posLocal - 1];
        m_modes[posLocal - 1] = temp;
      }
      else
      {
        break;
      }
    }
  }

  //set the number of modes
  *pModesUsed = nModes;

  if (bBackgroundLow)
  {
    low_threshold = BACKGROUND;
  }
  else
  {
    low_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 ZivkovicAGMM::Subtract(int frame_num, const RgbImage& data,
  BwImage& low_threshold_mask, BwImage& high_threshold_mask)
{
  unsigned char low_threshold, high_threshold;

  // update each pixel of the image
  long posPixel;
  unsigned char* pUsedModes = m_modes_per_pixel;
  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), pUsedModes, low_threshold, 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;

      pUsedModes++;
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/ZivkovicAGMM.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/GrimsonGMM.cpp --#--
#include "GrimsonGMM.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

//using namespace bgslibrary::algorithms::dp;

namespace bgslibrary
{
  namespace algorithms
  {
    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;
      }

      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)
        {
          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;

        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;
          }
        }

        // 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 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), 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);

        if (bBackgroundLow)
        {
          low_threshold = BACKGROUND;
        }
        else
        {
          low_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;

        // 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);

            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;
          }
        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/GrimsonGMM.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/Image.cpp --#--
#include "Image.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

//using namespace bgslibrary::algorithms::dp;

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      ImageBase::~ImageBase()
      {
        if (imgp != NULL && m_bReleaseMemory)
          cvReleaseImage(&imgp);
        imgp = NULL;
      }

      void DensityFilter(BwImage& image, BwImage& filtered, int minDensity, unsigned char fgValue)
      {
        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;
            }
          }
        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/Image.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/Error.cpp --#--
#include <iostream> 
#include <fstream>

#include "Error.h"

//using namespace bgslibrary::algorithms::dp;

namespace bgslibrary
{
  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();
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/dp/Error.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/WrenGA.cpp --#--
#include "WrenGA.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

WrenGA::WrenGA()
{
  m_gaussian = NULL;
}

WrenGA::~WrenGA()
{
  delete[] m_gaussian;
}

void WrenGA::Initalize(const BgsParams& param)
{
  m_params = (WrenParams&)param;

  m_variance = 36.0f;

  // GMM for each pixel
  m_gaussian = new GAUSSIAN[m_params.Size()];
  for (unsigned int i = 0; i < m_params.Size(); ++i)
  {
    for (int ch = 0; ch < NUM_CHANNELS; ++ch)
    {
      m_gaussian[i].mu[ch] = 0;
      m_gaussian[i].var[ch] = 0;
    }
  }

  m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3);
}

void WrenGA::InitModel(const RgbImage& data)
{
  int pos = 0;

  for (unsigned int r = 0; r < m_params.Height(); ++r)
  {
    for (unsigned int c = 0; c < m_params.Width(); ++c)
    {
      for (int ch = 0; ch < NUM_CHANNELS; ++ch)
      {
        m_gaussian[pos].mu[ch] = data(r, c, ch);
        m_gaussian[pos].var[ch] = m_variance;
      }

      pos++;
    }
  }
}

void WrenGA::Update(int frame_num, const RgbImage& data, const BwImage& update_mask)
{
  int pos = 0;

  for (unsigned int r = 0; r < m_params.Height(); ++r)
  {
    for (unsigned int c = 0; c < m_params.Width(); ++c)
    {
      // perform conditional updating only if we are passed the learning phase
      if (update_mask(r, c) == BACKGROUND || frame_num < m_params.LearningFrames())
      {
        float dR = m_gaussian[pos].mu[0] - data(r, c, 0);
        float dG = m_gaussian[pos].mu[1] - data(r, c, 1);
        float dB = m_gaussian[pos].mu[2] - data(r, c, 2);

        float dist = (dR*dR + dG*dG + dB*dB);

        m_gaussian[pos].mu[0] -= m_params.Alpha()*(dR);
        m_gaussian[pos].mu[1] -= m_params.Alpha()*(dG);
        m_gaussian[pos].mu[2] -= m_params.Alpha()*(dB);

        float sigmanew = m_gaussian[pos].var[0] + m_params.Alpha()*(dist - m_gaussian[pos].var[0]);
        m_gaussian[pos].var[0] = sigmanew < 4 ? 4 : sigmanew > 5 * m_variance ? 5 * m_variance : sigmanew;

        m_background(r, c, 0) = (unsigned char)(m_gaussian[pos].mu[0] + 0.5);
        m_background(r, c, 1) = (unsigned char)(m_gaussian[pos].mu[1] + 0.5);
        m_background(r, c, 2) = (unsigned char)(m_gaussian[pos].mu[2] + 0.5);
      }

      pos++;
    }
  }
}

void WrenGA::SubtractPixel(int r, int c, const RgbPixel& pixel,
  unsigned char& low_threshold,
  unsigned char& high_threshold)
{
  unsigned int pos = r*m_params.Width() + c;

  // calculate distance between model and pixel
  float mu[NUM_CHANNELS];
  float var[1];
  float delta[NUM_CHANNELS];
  float dist = 0;
  for (int ch = 0; ch < NUM_CHANNELS; ++ch)
  {
    mu[ch] = m_gaussian[pos].mu[ch];
    var[0] = m_gaussian[pos].var[0];
    delta[ch] = mu[ch] - pixel(ch);
    dist += delta[ch] * delta[ch];
  }

  // calculate the squared distance and see if pixel fits the B/G model
  low_threshold = BACKGROUND;
  high_threshold = BACKGROUND;

  if (dist > m_params.LowThreshold()*var[0])
    low_threshold = FOREGROUND;
  if (dist > m_params.HighThreshold()*var[0])
    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 WrenGA::Subtract(int frame_num, const RgbImage& data,
  BwImage& low_threshold_mask, BwImage& high_threshold_mask)
{
  unsigned char low_threshold, high_threshold;

  // 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)
    {
      SubtractPixel(r, c, data(r, c), low_threshold, high_threshold);
      low_threshold_mask(r, c) = low_threshold;
      high_threshold_mask(r, c) = high_threshold;
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/WrenGA.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/AdaptiveMedianBGS.cpp --#--
#include "AdaptiveMedianBGS.h"
#include "opencv2/core/types_c.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

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(), cvScalar(CV_RGB(BACKGROUND, BACKGROUND, BACKGROUND)));
}

RgbImage* AdaptiveMedianBGS::Background()
{
  return &m_median;
}

void AdaptiveMedianBGS::InitModel(const RgbImage& data)
{
  // initialize the background model
  for (unsigned int r = 0; r < m_params.Height(); ++r)
  {
    for (unsigned int c = 0; c < m_params.Width(); ++c)
    {
      m_median(r, c) = data(r, c);
    }
  }
}

void AdaptiveMedianBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask)
{
  if (frame_num % m_params.SamplingRate() == 1)
  {
    // update background model
    for (unsigned int r = 0; r < m_params.Height(); ++r)
    {
      for (unsigned int c = 0; c < m_params.Width(); ++c)
      {
        // perform conditional updating only if we are passed the learning phase
        if (update_mask(r, c) == BACKGROUND || frame_num < m_params.LearningFrames())
        {
          for (int ch = 0; ch < NUM_CHANNELS; ++ch)
          {
            if (data(r, c, ch) > m_median(r, c, ch))
            {
              m_median(r, c, ch)++;
            }
            else if (data(r, c, ch) < m_median(r, c, ch))
            {
              m_median(r, c, ch)--;
            }
          }
        }
      }
    }
  }
}

void AdaptiveMedianBGS::SubtractPixel(int r, int c, const RgbPixel& pixel,
  unsigned char& low_threshold, unsigned char& high_threshold)
{
  // perform background subtraction
  low_threshold = high_threshold = FOREGROUND;

  int diffR = abs(pixel(0) - m_median(r, c, 0));
  int diffG = abs(pixel(1) - m_median(r, c, 1));
  int diffB = abs(pixel(2) - m_median(r, c, 2));

  if (diffR <= m_params.LowThreshold() && diffG <= m_params.LowThreshold() && diffB <= m_params.LowThreshold())
  {
    low_threshold = BACKGROUND;
  }

  if (diffR <= m_params.HighThreshold() && diffG <= m_params.HighThreshold() && diffB <= m_params.HighThreshold())
  {
    high_threshold = BACKGROUND;
  }
}

///////////////////////////////////////////////////////////////////////////////
//Input:
//  data - a pointer to the image data
//Output:
//  output - a pointer to the data of a gray value image
//					(the memory should already be reserved) 
//					values: 255-foreground, 0-background
///////////////////////////////////////////////////////////////////////////////
void AdaptiveMedianBGS::Subtract(int frame_num, const RgbImage& data,
  BwImage& low_threshold_mask, BwImage& high_threshold_mask)
{
  unsigned char low_threshold, high_threshold;

  // 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)
    {
      // perform background subtraction
      SubtractPixel(r, c, data(r, c), low_threshold, high_threshold);

      // setup silhouette mask
      low_threshold_mask(r, c) = low_threshold;
      high_threshold_mask(r, c) = high_threshold;
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/AdaptiveMedianBGS.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/PratiMediodBGS.cpp --#--
#include "PratiMediodBGS.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

PratiMediodBGS::PratiMediodBGS()
{
  m_median_buffer = NULL;
}

PratiMediodBGS::~PratiMediodBGS()
{
  delete[] m_median_buffer;
}

void PratiMediodBGS::Initalize(const BgsParams& param)
{
  m_params = (PratiParams&)param;

  m_mask_low_threshold = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 1);
  m_mask_high_threshold = 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);

  m_median_buffer = new MEDIAN_BUFFER[m_params.Size()];
}

void PratiMediodBGS::InitModel(const RgbImage& data)
{
  // there is no need to initialize the mode since it needs a buffer of frames
  // before it can performing background subtraction
}

void PratiMediodBGS::Update(int frame_num, const RgbImage& data, const BwImage& update_mask)
{
  // update the image buffer with the new frame and calculate new median values
  if (frame_num % m_params.SamplingRate() == 0)
  {
    if (m_median_buffer[0].dist.size() == m_params.HistorySize())
    {
      // subtract distance to sample being removed from all distances
      for (unsigned int r = 0; r < m_params.Height(); ++r)
      {
        for (unsigned int c = 0; c < m_params.Width(); ++c)
        {
          int i = r*m_params.Width() + c;

          if (update_mask(r, c) == BACKGROUND)
          {
            int oldPos = m_median_buffer[i].pos;
            for (unsigned int s = 0; s < m_median_buffer[i].pixels.size(); ++s)
            {
              int maxDist = 0;
              for (int ch = 0; ch < NUM_CHANNELS; ++ch)
              {
                int tempDist = abs(m_median_buffer[i].pixels.at(oldPos)(ch)
                  - m_median_buffer[i].pixels.at(s)(ch));
                if (tempDist > maxDist)
                  maxDist = tempDist;
              }

              m_median_buffer[i].dist.at(s) -= maxDist;
            }

            int dist;
            UpdateMediod(r, c, data, dist);
            m_median_buffer[i].dist.at(oldPos) = dist;
            m_median_buffer[i].pixels.at(oldPos) = data(r, c);
            m_median_buffer[i].pos++;
            if (m_median_buffer[i].pos >= m_params.HistorySize())
              m_median_buffer[i].pos = 0;
          }
        }
      }
    }
    else
    {
      // calculate sum of L-inf distances for new point and
      // add distance from each sample point to this point to their L-inf sum
      int dist;
      for (unsigned int r = 0; r < m_params.Height(); ++r)
      {
        for (unsigned int c = 0; c < m_params.Width(); ++c)
        {
          int index = r*m_params.Width() + c;
          UpdateMediod(r, c, data, dist);
          m_median_buffer[index].dist.push_back(dist);
          m_median_buffer[index].pos = 0;
          m_median_buffer[index].pixels.push_back(data(r, c));
        }
      }
    }
  }
}

void PratiMediodBGS::UpdateMediod(int r, int c, const RgbImage& new_frame, int& dist)
{
  // calculate sum of L-inf distances for new point and
  // add distance from each sample point to this point to their L-inf sum
  unsigned int i = (r*m_params.Width() + c);

  m_median_buffer[i].medianDist = INT_MAX;

  int L_inf_dist = 0;
  for (unsigned int s = 0; s < m_median_buffer[i].dist.size(); ++s)
  {
    int maxDist = 0;
    for (int ch = 0; ch < NUM_CHANNELS; ++ch)
    {
      int tempDist = abs(m_median_buffer[i].pixels.at(s)(ch) - new_frame(r, c, ch));
      if (tempDist > maxDist)
        maxDist = tempDist;
    }

    // check if point from this frame in the image buffer is the median
    m_median_buffer[i].dist.at(s) += maxDist;
    if (m_median_buffer[i].dist.at(s) < m_median_buffer[i].medianDist)
    {
      m_median_buffer[i].medianDist = m_median_buffer[i].dist.at(s);
      m_median_buffer[i].median = m_median_buffer[i].pixels.at(s);
    }

    L_inf_dist += maxDist;
  }

  dist = L_inf_dist;

  // check if the new point is the median
  if (L_inf_dist < m_median_buffer[i].medianDist)
  {
    m_median_buffer[i].medianDist = L_inf_dist;
    m_median_buffer[i].median = new_frame(r, c);
  }
}

void PratiMediodBGS::Combine(const BwImage& low_mask, const BwImage& high_mask, BwImage& output)
{
  for (unsigned int r = 0; r < m_params.Height(); ++r)
  {
    for (unsigned int c = 0; c < m_params.Width(); ++c)
    {
      output(r, c) = BACKGROUND;

      if (r == 0 || c == 0 || r == m_params.Height() - 1 || c == m_params.Width() - 1)
        continue;

      if (high_mask(r, c) == FOREGROUND)
      {
        output(r, c) = FOREGROUND;
      }
      else if (low_mask(r, c) == FOREGROUND)
      {
        // consider the pixel to be a F/G pixel if it is 8-connected to
        // a F/G pixel in the high mask
        // check if there is an 8-connected foreground pixel
        if (high_mask(r - 1, c - 1))
          output(r, c) = FOREGROUND;
        else if (high_mask(r - 1, c))
          output(r, c) = FOREGROUND;
        else if (high_mask(r - 1, c + 1))
          output(r, c) = FOREGROUND;
        else if (high_mask(r, c - 1))
          output(r, c) = FOREGROUND;
        else if (high_mask(r, c + 1))
          output(r, c) = FOREGROUND;
        else if (high_mask(r + 1, c - 1))
          output(r, c) = FOREGROUND;
        else if (high_mask(r + 1, c))
          output(r, c) = FOREGROUND;
        else if (high_mask(r + 1, c + 1))
          output(r, c) = FOREGROUND;
      }
    }
  }
}

void PratiMediodBGS::CalculateMasks(int r, int c, const RgbPixel& pixel)
{
  int pos = r*m_params.Width() + c;

  // calculate l-inf distance between current value and median value
  unsigned char dist = 0;
  for (int ch = 0; ch < NUM_CHANNELS; ++ch)
  {
    int tempDist = abs(pixel(ch) - m_median_buffer[pos].median(ch));
    if (tempDist > dist)
      dist = tempDist;
  }
  m_background(r, c) = m_median_buffer[pos].median;

  // check if pixel is a B/G or F/G pixel according to the low threshold B/G model
  m_mask_low_threshold(r, c) = BACKGROUND;
  if (dist > m_params.LowThreshold())
  {
    m_mask_low_threshold(r, c) = FOREGROUND;
  }

  // check if pixel is a B/G or F/G pixel according to the high threshold B/G model
  m_mask_high_threshold(r, c) = BACKGROUND;
  if (dist > m_params.HighThreshold())
  {
    m_mask_high_threshold(r, c) = 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 
//					values: 255-foreground, 0-background
///////////////////////////////////////////////////////////////////////////////
void PratiMediodBGS::Subtract(int frame_num, const RgbImage& data,
  BwImage& low_threshold_mark, BwImage& high_threshold_mark)
{
  if (frame_num < m_params.HistorySize())
  {
    low_threshold_mark.Clear();
    high_threshold_mark.Clear();
    return;
  }

  // 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)
    {
      // need at least one frame of data before we can start calculating the masks
      CalculateMasks(r, c, data(r, c));
    }
  }

  // combine low and high threshold masks
  Combine(m_mask_low_threshold, m_mask_high_threshold, low_threshold_mark);
  Combine(m_mask_low_threshold, m_mask_high_threshold, high_threshold_mark);
}

#endif

--#-- END ./bgslibrary/algorithms/dp/PratiMediodBGS.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/Eigenbackground.cpp --#--
#include "Eigenbackground.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

Eigenbackground::Eigenbackground()
{
  m_pcaData = NULL;
  m_pcaAvg = NULL;
  m_eigenValues = NULL;
  m_eigenVectors = NULL;
}

Eigenbackground::~Eigenbackground()
{
  if (m_pcaData != NULL) cvReleaseMat(&m_pcaData);
  if (m_pcaAvg != NULL) cvReleaseMat(&m_pcaAvg);
  if (m_eigenValues != NULL) cvReleaseMat(&m_eigenValues);
  if (m_eigenVectors != NULL) cvReleaseMat(&m_eigenVectors);
}

void Eigenbackground::Initalize(const BgsParams& param)
{
  m_params = (EigenbackgroundParams&)param;

  m_background = cvCreateImage(cvSize(m_params.Width(), m_params.Height()), IPL_DEPTH_8U, 3);
  m_background.Clear();
}

void Eigenbackground::InitModel(const RgbImage& data)
{
  if (m_pcaData != NULL) cvReleaseMat(&m_pcaData);
  if (m_pcaAvg != NULL) cvReleaseMat(&m_pcaAvg);
  if (m_eigenValues != NULL) cvReleaseMat(&m_eigenValues);
  if (m_eigenVectors != NULL) cvReleaseMat(&m_eigenVectors);

  m_pcaData = cvCreateMat(m_params.HistorySize(), m_params.Size() * 3, CV_8UC1);

  m_background.Clear();
}

void Eigenbackground::Update(int frame_num, const RgbImage& data, const BwImage& update_mask)
{
  // the eigenbackground model is not updated (serious limitation!)
}

void Eigenbackground::Subtract(int frame_num, const RgbImage& data,
  BwImage& low_threshold_mask, BwImage& high_threshold_mask)
{
  // create eigenbackground
  if (frame_num == m_params.HistorySize())
  {
    // create the eigenspace
    m_pcaAvg = cvCreateMat(1, m_pcaData->cols, CV_32F);
    m_eigenValues = cvCreateMat(m_pcaData->rows, 1, CV_32F);
    m_eigenVectors = cvCreateMat(m_pcaData->rows, m_pcaData->cols, CV_32F);
    cvCalcPCA(m_pcaData, m_pcaAvg, m_eigenValues, m_eigenVectors, CV_PCA_DATA_AS_ROW);

    int index = 0;
    for (unsigned int r = 0; r < m_params.Height(); ++r)
    {
      for (unsigned int c = 0; c < m_params.Width(); ++c)
      {
        for (int ch = 0; ch < m_background.Ptr()->nChannels; ++ch)
        {
          m_background(r, c, 0) = static_cast<unsigned char>(cvmGet(m_pcaAvg, 0, index) + 0.5);
          index++;
        }
      }
    }
  }

  if (frame_num >= m_params.HistorySize())
  {
    // project new image into the eigenspace
    int w = data.Ptr()->width;
    int h = data.Ptr()->height;
    int ch = data.Ptr()->nChannels;
    CvMat* dataPt = cvCreateMat(1, w*h*ch, CV_8UC1);
    CvMat data_row;
    cvGetRow(dataPt, &data_row, 0);
    cvReshape(&data_row, &data_row, 3, data.Ptr()->height);
    cvCopy(data.Ptr(), &data_row);

    CvMat* proj = cvCreateMat(1, m_params.EmbeddedDim(), CV_32F);
    cvProjectPCA(dataPt, m_pcaAvg, m_eigenVectors, proj);

    // reconstruct point
    CvMat* result = cvCreateMat(1, m_pcaData->cols, CV_32F);
    cvBackProjectPCA(proj, m_pcaAvg, m_eigenVectors, result);

    // calculate Euclidean distance between new image and its eigenspace projection
    int index = 0;
    for (unsigned int r = 0; r < m_params.Height(); ++r)
    {
      for (unsigned int c = 0; c < m_params.Width(); ++c)
      {
        double dist = 0;
        bool bgLow = true;
        bool bgHigh = true;
        for (int ch = 0; ch < 3; ++ch)
        {
          dist = (data(r, c, ch) - cvmGet(result, 0, index))*(data(r, c, ch) - cvmGet(result, 0, index));
          if (dist > m_params.LowThreshold())
            bgLow = false;
          if (dist > m_params.HighThreshold())
            bgHigh = false;
          index++;
        }

        if (!bgLow)
        {
          low_threshold_mask(r, c) = FOREGROUND;
        }
        else
        {
          low_threshold_mask(r, c) = BACKGROUND;
        }

        if (!bgHigh)
        {
          high_threshold_mask(r, c) = FOREGROUND;
        }
        else
        {
          high_threshold_mask(r, c) = BACKGROUND;
        }
      }
    }

    cvReleaseMat(&result);
    cvReleaseMat(&proj);
    cvReleaseMat(&dataPt);
  }
  else
  {
    // set entire image to background since there is not enough information yet
    // to start performing background subtraction
    for (unsigned int r = 0; r < m_params.Height(); ++r)
    {
      for (unsigned int c = 0; c < m_params.Width(); ++c)
      {
        low_threshold_mask(r, c) = BACKGROUND;
        high_threshold_mask(r, c) = BACKGROUND;
      }
    }
  }

  UpdateHistory(frame_num, data);
}

void Eigenbackground::UpdateHistory(int frame_num, const RgbImage& new_frame)
{
  if (frame_num < m_params.HistorySize())
  {
    CvMat src_row;
    cvGetRow(m_pcaData, &src_row, frame_num);
    cvReshape(&src_row, &src_row, 3, new_frame.Ptr()->height);
    cvCopy(new_frame.Ptr(), &src_row);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/Eigenbackground.cpp --#--

--#-- START ./bgslibrary/algorithms/dp/TextureBGS.cpp --#--
#include "TextureBGS.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms::dp;

TextureBGS::TextureBGS() {}
TextureBGS::~TextureBGS() {}

void TextureBGS::LBP(RgbImage& image, RgbImage& texture)
{
  for (int y = TEXTURE_R; y < image.Ptr()->height - TEXTURE_R; ++y)
  {
    for (int x = TEXTURE_R; x < image.Ptr()->width - TEXTURE_R; ++x)
    {
      for (int ch = 0; ch < NUM_CHANNELS; ++ch)
      {
        unsigned char textureCode = 0;
        int centerValue = (int)image(y, x, ch);

        // this only works for a texture radius of 2
        if (centerValue - (int)image(y - 2, x, ch) + HYSTERSIS >= 0)
          textureCode += 1;

        if (centerValue - (int)image(y - 1, x - 2, ch) + HYSTERSIS >= 0)
          textureCode += 2;

        if (centerValue - (int)image(y - 1, x + 2, ch) + HYSTERSIS >= 0)
          textureCode += 4;

        if (centerValue - (int)image(y + 1, x - 2, ch) + HYSTERSIS >= 0)
          textureCode += 8;

        if (centerValue - (int)image(y + 1, x + 2, ch) + HYSTERSIS >= 0)
          textureCode += 16;

        if (centerValue - (int)image(y + 2, x, ch) + HYSTERSIS >= 0)
          textureCode += 32;

        texture(y, x, ch) = textureCode;
      }
    }
  }
}

void TextureBGS::Histogram(RgbImage& texture, TextureHistogram* curTextureHist)
{
  // calculate histogram within a 2*REGION_R square
  for (int y = REGION_R + TEXTURE_R; y < texture.Ptr()->height - REGION_R - TEXTURE_R; ++y)
  {
    for (int x = REGION_R + TEXTURE_R; x < texture.Ptr()->width - REGION_R - TEXTURE_R; ++x)
    {
      int index = x + y*(texture.Ptr()->width);

      // clear histogram
      for (int i = 0; i < NUM_BINS; ++i)
      {
        curTextureHist[index].r[i] = 0;
        curTextureHist[index].g[i] = 0;
        curTextureHist[index].b[i] = 0;
      }

      // calculate histogram
      for (int j = -REGION_R; j <= REGION_R; ++j)
      {
        for (int i = -REGION_R; i <= REGION_R; ++i)
        {
          curTextureHist[index].r[texture(y + j, x + i, 2)]++;
          curTextureHist[index].g[texture(y + j, x + i, 1)]++;
          curTextureHist[index].b[texture(y + j, x + i, 0)]++;
        }
      }
    }
  }
}

int TextureBGS::ProximityMeasure(TextureHistogram& bgTexture, TextureHistogram& curTextureHist)
{
  int proximity = 0;
  for (int i = 0; i < NUM_BINS; ++i)
  {
    proximity += std::min(bgTexture.r[i], curTextureHist.r[i]);
    proximity += std::min(bgTexture.g[i], curTextureHist.g[i]);
    proximity += std::min(bgTexture.b[i], curTextureHist.b[i]);
  }

  return proximity;
}

void TextureBGS::BgsCompare(TextureArray* bgModel, TextureHistogram* curTextureHist,
  unsigned char* modeArray, float threshold, BwImage& fgMask)
{
  cvZero(fgMask.Ptr());

  for (int y = REGION_R + TEXTURE_R; y < fgMask.Ptr()->height - REGION_R - TEXTURE_R; ++y)
  {
    for (int x = REGION_R + TEXTURE_R; x < fgMask.Ptr()->width - REGION_R - TEXTURE_R; ++x)
    {
      int index = x + y*(fgMask.Ptr()->width);

      // find closest matching texture in background model
      int maxProximity = -1;

      for (int m = 0; m < NUM_MODES; ++m)
      {
        int proximity = ProximityMeasure(bgModel[index].mode[m], curTextureHist[index]);

        if (proximity > maxProximity)
        {
          maxProximity = proximity;
          modeArray[index] = m;
        }
      }

      if (maxProximity < threshold)
        fgMask(y, x) = 255;
    }
  }
}

void TextureBGS::UpdateModel(BwImage& fgMask, TextureArray* bgModel,
  TextureHistogram* curTextureHist, unsigned char* modeArray)
{
  for (int y = REGION_R + TEXTURE_R; y < fgMask.Ptr()->height - REGION_R - TEXTURE_R; ++y)
  {
    for (int x = REGION_R + TEXTURE_R; x < fgMask.Ptr()->width - REGION_R - TEXTURE_R; ++x)
    {
      int index = x + y*(fgMask.Ptr()->width);

      if (fgMask(y, x) == 0)
      {
        for (int i = 0; i < NUM_BINS; ++i)
        {
          bgModel[index].mode[modeArray[index]].r[i]
            = (unsigned char)(ALPHA*curTextureHist[index].r[i]
              + (1 - ALPHA)*bgModel[index].mode[modeArray[index]].r[i] + 0.5);
        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/TextureBGS.cpp --#--

--#-- START ./bgslibrary/algorithms/IMBS/IMBS.cpp --#--
#include "IMBS.hpp"

using namespace bgslibrary::algorithms::imbs;
using namespace std;
using namespace cv;

BackgroundSubtractorIMBS::BackgroundSubtractorIMBS()
{
  fps = 0.;
  fgThreshold = 15;
  associationThreshold = 5;
  samplingPeriod = 50;//500.ms
  minBinHeight = 2;
  numSamples = 10; //30
  alpha = 0.65f;
  beta = 1.15f;
  tau_s = 60;
  tau_h = 40;
  minArea = 30.;
  nframes = 0;
  bgBins = NULL;
  bgModel = NULL;
  persistenceMap = NULL;
  persistencePeriod = samplingPeriod*numSamples / 3.;//ms

  initial_tick_count = (double)getTickCount();

  //morphological Opening and closing
  morphologicalFiltering = false;
}

BackgroundSubtractorIMBS::BackgroundSubtractorIMBS(
  double fps,
  unsigned int fgThreshold,
  unsigned int associationThreshold,
  double samplingPeriod,
  unsigned int minBinHeight,
  unsigned int numSamples,
  double alpha,
  double beta,
  double tau_s,
  double tau_h,
  double minArea,
  double persistencePeriod,
  bool morphologicalFiltering)
{
  this->fps = fps;
  this->fgThreshold = fgThreshold;
  this->persistencePeriod = persistencePeriod;
  if (minBinHeight <= 1) {
    this->minBinHeight = 1;
  }
  else {
    this->minBinHeight = minBinHeight;
  }
  this->associationThreshold = associationThreshold;
  this->samplingPeriod = samplingPeriod;//ms
  this->minBinHeight = minBinHeight;
  this->numSamples = numSamples;
  this->alpha = alpha;
  this->beta = beta;
  this->tau_s = tau_s;
  this->tau_h = tau_h;
  this->minArea = minArea;
  nframes = 0;
  bgBins = NULL;
  bgModel = NULL;
  persistenceMap = NULL;

  if (fps == 0.)
    initial_tick_count = (double)getTickCount();
  else
    initial_tick_count = 0;

  //morphological Opening and closing
  this->morphologicalFiltering = morphologicalFiltering;
}

BackgroundSubtractorIMBS::~BackgroundSubtractorIMBS()
{
  delete[] bgBins;
  delete[] bgModel;
  delete[] persistenceMap;
}

void BackgroundSubtractorIMBS::initialize(Size frameSize, int frameType)
{
  /*cout << "INPUT: WIDTH " << frameSize.width << "  HEIGHT " << frameSize.height <<
    "  FPS " << fps << endl;
  cout << endl;*/

  this->frameSize = frameSize;
  this->frameType = frameType;
  this->numPixels = frameSize.width*frameSize.height;

  persistenceMap = new unsigned int[numPixels];
  for (unsigned int i = 0; i < numPixels; i++) {
    persistenceMap[i] = 0;
  }

  bgBins = new Bins[numPixels];
  bgModel = new BgModel[numPixels];
  maxBgBins = numSamples / minBinHeight;

  timestamp = 0.;//ms
  prev_timestamp = 0.;//ms
  prev_bg_frame_time = 0;
  bg_frame_counter = 0;
  bg_reset = false;
  prev_area = 0;
  sudden_change = false;

  SHADOW_LABEL = 80;
  PERSISTENCE_LABEL = 180;
  FOREGROUND_LABEL = 255;

  fgmask.create(frameSize, CV_8UC1);
  fgfiltered.create(frameSize, CV_8UC1);
  persistenceImage = Mat::zeros(frameSize, CV_8UC1);
  bgSample.create(frameSize, CV_8UC3);
  bgImage = Mat::zeros(frameSize, CV_8UC3);

  //initial message to be shown until the first fg mask is computed
  initialMsgGray = Mat::zeros(frameSize, CV_8UC1);
  putText(initialMsgGray, "Creating", Point(10, 20), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255));
  putText(initialMsgGray, "initial", Point(10, 40), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255));
  putText(initialMsgGray, "background...", Point(10, 60), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255));

  initialMsgRGB = Mat::zeros(frameSize, CV_8UC3);
  putText(initialMsgRGB, "Creating", Point(10, 20), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255));
  putText(initialMsgRGB, "initial", Point(10, 40), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255));
  putText(initialMsgRGB, "background...", Point(10, 60), FONT_HERSHEY_SIMPLEX, 0.4, CV_RGB(255, 255, 255));

  if (minBinHeight <= 1) {
    minBinHeight = 1;
  }

  for (unsigned int p = 0; p < numPixels; ++p)
  {
    bgBins[p].initialize(numSamples);
    bgModel[p].initialize(maxBgBins);
  }
}

void BackgroundSubtractorIMBS::apply(InputArray _frame, OutputArray _fgmask, double learningRate)
{
  frame = _frame.getMat();

  CV_Assert(frame.depth() == CV_8U);
  CV_Assert(frame.channels() == 3);
  CV_Assert(frame.size().width > 0);
  CV_Assert(frame.size().height > 0);

  bool needToInitialize = nframes == 0 || frame.type() != frameType;
  if (needToInitialize) {
    initialize(frame.size(), frame.type());
  }

  _fgmask.create(frameSize, CV_8UC1);
  fgmask = _fgmask.getMat();
  fgmask = Scalar(0);

  //get current time
  prev_timestamp = timestamp;
  if (fps == 0.) {
    timestamp = getTimestamp();//ms
  }
  else {
    timestamp += 1000. / fps;//ms
  }

  //check for global changes
  if (sudden_change) {
    changeBg();
  }

  //wait for the first model to be generated
  if (bgModel[0].isValid[0]) {
    getFg();
    hsvSuppression();
    filterFg();
  }
  //update the bg model
  updateBg();

  //show an initial message if the first bg is not yet ready
  if (!bgModel[0].isValid[0]) {
    initialMsgGray.copyTo(fgmask);
    initialMsgRGB.copyTo(bgImage);
  }
  ++nframes;
}

void BackgroundSubtractorIMBS::updateBg() {
  if (bg_reset) {
    if (bg_frame_counter > numSamples - 1) {
      bg_frame_counter = numSamples - 1;
    }
  }

  if (prev_bg_frame_time > timestamp) {
    prev_bg_frame_time = timestamp;
  }

  if (bg_frame_counter == numSamples - 1) {
    createBg(bg_frame_counter);
    bg_frame_counter = 0;
  }
  else { //bg_frame_counter < (numSamples - 1)

    if ((timestamp - prev_bg_frame_time) >= samplingPeriod)
    {
      //get a new sample for creating the bg model
      prev_bg_frame_time = timestamp;
      frame.copyTo(bgSample);
      createBg(bg_frame_counter);
      bg_frame_counter++;
    }
  }
}

double BackgroundSubtractorIMBS::getTimestamp() {
  return ((double)getTickCount() - initial_tick_count)*1000. / getTickFrequency();
}

void BackgroundSubtractorIMBS::hsvSuppression() {

  uchar h_i, s_i, v_i;
  uchar h_b, s_b, v_b;
  float h_diff, s_diff, v_ratio;

  Mat bgrPixel(cv::Size(1, 1), CV_8UC3);

  vector<Mat> imHSV;
  cv::split(convertImageRGBtoHSV(frame), imHSV);

  for (unsigned int p = 0; p < numPixels; ++p) {
    if (fgmask.data[p]) {

      h_i = imHSV[0].data[p];
      s_i = imHSV[1].data[p];
      v_i = imHSV[2].data[p];

      for (unsigned int n = 0; n < maxBgBins; ++n) {
        if (!bgModel[p].isValid[n]) {
          break;
        }

        if (bgModel[p].isFg[n]) {
          continue;
        }

        bgrPixel.at<cv::Vec3b>(0, 0) = bgModel[p].values[n];

        cv::Mat hsvPixel = convertImageRGBtoHSV(bgrPixel);

        h_b = hsvPixel.at<cv::Vec3b>(0, 0)[0];
        s_b = hsvPixel.at<cv::Vec3b>(0, 0)[1];
        v_b = hsvPixel.at<cv::Vec3b>(0, 0)[2];

        v_ratio = (float)v_i / (float)v_b;
        s_diff = std::abs(s_i - s_b);
        h_diff = std::min(std::abs(h_i - h_b), 255 - std::abs(h_i - h_b));

        if (h_diff <= tau_h &&
          s_diff <= tau_s &&
          v_ratio >= alpha &&
          v_ratio < beta)
        {
          fgmask.data[p] = SHADOW_LABEL;
          break;
        }
      }//for
    }//if
  }//numPixels
}

void BackgroundSubtractorIMBS::createBg(unsigned int bg_sample_number) {
  if (!bgSample.data) {
    //cerr << "createBg -- an error occurred: " <<
    //		" unable to retrieve frame no. " << bg_sample_number << endl;

    //TODO vedere gestione errori
    abort();
  }
  //aux variable
  Vec3b currentPixel;
  //split bgSample in channels
  cv::split(bgSample, bgSampleBGR);
  //create a statistical model for each pixel (a set of bins of variable size)
  for (unsigned int p = 0; p < numPixels; ++p) {
    //create an initial bin for each pixel from the first sample (bg_sample_number = 0)
    if (bg_sample_number == 0) {
      for (int k = 0; k < 3; ++k) {
        bgBins[p].binValues[0][k] = bgSampleBGR[k].data[p];
      }
      bgBins[p].binHeights[0] = 1;
      for (unsigned int s = 1; s < numSamples; ++s) {
        bgBins[p].binHeights[s] = 0;
      }
      //if the sample pixel is from foreground keep track of that situation
      if (fgmask.data[p] == FOREGROUND_LABEL) {
        bgBins[p].isFg[0] = true;
      }
      else {
        bgBins[p].isFg[0] = false;
      }
    }//if(bg_sample_number == 0)
    else { //bg_sample_number > 0
      for (int k = 0; k < 3; ++k) {
        currentPixel[k] = bgSampleBGR[k].data[p];
      }
      int den = 0;
      for (unsigned int s = 0; s < bg_sample_number; ++s) {
        //try to associate the current pixel values to an existing bin
        if (std::abs(currentPixel[2] - bgBins[p].binValues[s][2]) <= associationThreshold &&
          std::abs(currentPixel[1] - bgBins[p].binValues[s][1]) <= associationThreshold &&
          std::abs(currentPixel[0] - bgBins[p].binValues[s][0]) <= associationThreshold)
        {
          den = (bgBins[p].binHeights[s] + 1);
          for (int k = 0; k < 3; ++k) {
            bgBins[p].binValues[s][k] =
              (bgBins[p].binValues[s][k] * bgBins[p].binHeights[s] + currentPixel[k]) / den;
          }
          bgBins[p].binHeights[s]++; //increment the height of the bin
          if (fgmask.data[p] == FOREGROUND_LABEL) {
            bgBins[p].isFg[s] = true;
          }
          break;
        }
        //if the association is not possible, create a new bin
        else if (bgBins[p].binHeights[s] == 0) {
          bgBins[p].binValues[s] = currentPixel;
          bgBins[p].binHeights[s]++;
          if (fgmask.data[p] == FOREGROUND_LABEL) {
            bgBins[p].isFg[s] = true;
          }
          else {
            bgBins[p].isFg[s] = false;
          }
          break;
        }
        else continue;
      }//for(unsigned int s = 0; s <= bg_sample_number; ++s)

      //if all samples have been processed
      //it is time to compute the fg mask
      if (bg_sample_number == (numSamples - 1)) {
        unsigned int index = 0;
        int max_height = -1;
        for (unsigned int s = 0; s < numSamples; ++s) {
          if (bgBins[p].binHeights[s] == 0) {
            bgModel[p].isValid[index] = false;
            break;
          }
          if (index == maxBgBins) {
            break;
          }
          else if (bgBins[p].binHeights[s] >= minBinHeight) {
            if (fgmask.data[p] == PERSISTENCE_LABEL) {
              for (unsigned int n = 0; n < maxBgBins; n++) {
                if (!bgModel[p].isValid[n]) {
                  break;
                }
                unsigned int d = std::max((int)std::abs(bgModel[p].values[n][0] - bgBins[p].binValues[s][0]),
                  std::abs(bgModel[p].values[n][1] - bgBins[p].binValues[s][1]));
                d = std::max((int)d, std::abs(bgModel[p].values[n][2] - bgBins[p].binValues[s][2]));
                if (d < fgThreshold) {
                  bgModel[p].isFg[n] = false;
                  bgBins[p].isFg[s] = false;
                }
              }
            }

            if (bgBins[p].binHeights[s] > max_height) {
              max_height = bgBins[p].binHeights[s];

              for (int k = 0; k < 3; ++k) {
                bgModel[p].values[index][k] = bgModel[p].values[0][k];
              }
              bgModel[p].isValid[index] = true;
              bgModel[p].isFg[index] = bgModel[p].isFg[0];
              bgModel[p].counter[index] = bgModel[p].counter[0];

              for (int k = 0; k < 3; ++k) {
                bgModel[p].values[0][k] = bgBins[p].binValues[s][k];
              }
              bgModel[p].isValid[0] = true;
              bgModel[p].isFg[0] = bgBins[p].isFg[s];
              bgModel[p].counter[0] = bgBins[p].binHeights[s];
            }
            else {
              for (int k = 0; k < 3; ++k) {
                bgModel[p].values[index][k] = bgBins[p].binValues[s][k];
              }
              bgModel[p].isValid[index] = true;
              bgModel[p].isFg[index] = bgBins[p].isFg[s];
              bgModel[p].counter[index] = bgBins[p].binHeights[s];
            }
            ++index;
          }
        } //for all numSamples
      }//bg_sample_number == (numSamples - 1)
    }//else --> if(frame_number == 0)
  }//numPixels

  if (bg_sample_number == (numSamples - 1)) {
    //std::cout << "new bg created" << std::endl;
    persistenceImage = Scalar(0);

    bg_reset = false;
    if (sudden_change) {
      numSamples *= 2.;
      samplingPeriod *= 2.;
      sudden_change = false;
    }

    for (unsigned int i = 0; i < numPixels; i++) {
      persistenceMap[i] = 0;
    }

    unsigned int p = 0;
    for (int i = 0; i < bgImage.rows; ++i) {
      for (int j = 0; j < bgImage.cols; ++j, ++p) {
        bgImage.at<cv::Vec3b>(i, j) = bgModel[p].values[0];
      }
    }
  }
}

void BackgroundSubtractorIMBS::getFg() {
  fgmask = Scalar(0);
  cv::split(frame, frameBGR);

  bool isFg = true;
  bool conditionalUpdated = false;
  unsigned int d = 0;
  for (unsigned int p = 0; p < numPixels; ++p) {
    isFg = true;
    conditionalUpdated = false;
    d = 0;
    for (unsigned int n = 0; n < maxBgBins; ++n) {
      if (!bgModel[p].isValid[n]) {
        if (n == 0) {
          isFg = false;
        }
        break;
      }
      else { //the model is valid
        d = std::max(
          (int)std::abs(bgModel[p].values[n][0] - frameBGR[0].data[p]),
          std::abs(bgModel[p].values[n][1] - frameBGR[1].data[p]));
        d = std::max(
          (int)d, std::abs(bgModel[p].values[n][2] - frameBGR[2].data[p]));
        if (d < fgThreshold) {
          //check if it is a potential background pixel
          //from stationary object
          if (bgModel[p].isFg[n]) {
            conditionalUpdated = true;
            break;
          }
          else {
            isFg = false;
            persistenceMap[p] = 0;
          }
        }
      }
    }
    if (isFg) {
      if (conditionalUpdated) {
        fgmask.data[p] = PERSISTENCE_LABEL;
        persistenceMap[p] += (timestamp - prev_timestamp);
        if (persistenceMap[p] > persistencePeriod) {
          for (unsigned int n = 0; n < maxBgBins; ++n) {
            if (!bgModel[p].isValid[n]) {
              break;
            }
            bgModel[p].isFg[n] = false;
          }
        }
      }
      else {
        fgmask.data[p] = FOREGROUND_LABEL;
        persistenceMap[p] = 0;
      }
    }
  }
}

void BackgroundSubtractorIMBS::areaThresholding()
{
  double maxArea = 0.6 * numPixels;

  std::vector < std::vector<Point> > contours;
  Mat tmpBinaryImage = fgfiltered.clone();
  findContours(tmpBinaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE);

  tmpBinaryImage = Scalar(0);

  for (size_t contourIdx = 0; contourIdx < contours.size(); ++contourIdx)
  {
    Moments moms = moments(Mat(contours[contourIdx]));
    double area = moms.m00;
    if (area < minArea || area >= maxArea)
      continue;
    else {
      drawContours(tmpBinaryImage, contours, contourIdx, Scalar(255), CV_FILLED);
    }
  }
  for (int i = 0; i < fgfiltered.rows; ++i) {
    for (int j = 0; j < fgfiltered.cols; ++j) {
      if (!tmpBinaryImage.at<uchar>(i, j)) {
        fgfiltered.at<uchar>(i, j) = 0;
      }
    }
  }
}

// Create a HSV image from the RGB image using the full 8-bits, since OpenCV only allows Hues up to 180 instead of 255.
// ref: "http://cs.haifa.ac.il/hagit/courses/ist/Lectures/Demos/ColorApplet2/t_convert.html"
// Remember to free the generated HSV image.
Mat BackgroundSubtractorIMBS::convertImageRGBtoHSV(const Mat& imageRGB)
{
  float fR, fG, fB;
  float fH, fS, fV;
  const float FLOAT_TO_BYTE = 255.0f;
  const float BYTE_TO_FLOAT = 1.0f / FLOAT_TO_BYTE;

  // Create a blank HSV image
  Mat imageHSV(imageRGB.size(), CV_8UC3);
  //if (!imageHSV || imageRGB->depth != 8 || imageRGB->nChannels != 3) {
  //printf("ERROR in convertImageRGBtoHSV()! Bad input image.\n");
  //exit(1);
  //}

  int h = imageRGB.rows;		// Pixel height.
  int w = imageRGB.cols;		// Pixel width.
  //int rowSizeRGB = imageRGB->widthStep;	// Size of row in bytes, including extra padding.
  //char *imRGB = imageRGB->imageData;	// Pointer to the start of the image pixels.
  //int rowSizeHSV = imageHSV->widthStep;	// Size of row in bytes, including extra padding.
  //char *imHSV = imageHSV->imageData;	// Pointer to the start of the image pixels.
  for (int y = 0; y < h; ++y) {
    for (int x = 0; x < w; ++x) {
      // Get the RGB pixel components. NOTE that OpenCV stores RGB pixels in B,G,R order.
      //uchar *pRGB = (uchar*)(imRGB + y*rowSizeRGB + x*3);
      int bB = imageRGB.at<Vec3b>(y, x)[0]; //*(uchar*)(pRGB+0);	// Blue component
      int bG = imageRGB.at<Vec3b>(y, x)[1]; //*(uchar*)(pRGB+1);	// Green component
      int bR = imageRGB.at<Vec3b>(y, x)[2]; //*(uchar*)(pRGB+2);	// Red component

      // Convert from 8-bit integers to floats.
      fR = bR * BYTE_TO_FLOAT;
      fG = bG * BYTE_TO_FLOAT;
      fB = bB * BYTE_TO_FLOAT;

      // Convert from RGB to HSV, using float ranges 0.0 to 1.0.
      float fDelta;
      float fMin, fMax;
      int iMax;
      // Get the min and max, but use integer comparisons for slight speedup.
      if (bB < bG) {
        if (bB < bR) {
          fMin = fB;
          if (bR > bG) {
            iMax = bR;
            fMax = fR;
          }
          else {
            iMax = bG;
            fMax = fG;
          }
        }
        else {
          fMin = fR;
          fMax = fG;
          iMax = bG;
        }
      }
      else {
        if (bG < bR) {
          fMin = fG;
          if (bB > bR) {
            fMax = fB;
            iMax = bB;
          }
          else {
            fMax = fR;
            iMax = bR;
          }
        }
        else {
          fMin = fR;
          fMax = fB;
          iMax = bB;
        }
      }
      fDelta = fMax - fMin;
      fV = fMax;				// Value (Brightness).
      if (iMax != 0) {			// Make sure its not pure black.
        fS = fDelta / fMax;		// Saturation.
        float ANGLE_TO_UNIT = 1.0f / (6.0f * fDelta);	// Make the Hues between 0.0 to 1.0 instead of 6.0
        if (iMax == bR) {		// between yellow and magenta.
          fH = (fG - fB) * ANGLE_TO_UNIT;
        }
        else if (iMax == bG) {		// between cyan and yellow.
          fH = (2.0f / 6.0f) + (fB - fR) * ANGLE_TO_UNIT;
        }
        else {				// between magenta and cyan.
          fH = (4.0f / 6.0f) + (fR - fG) * ANGLE_TO_UNIT;
        }
        // Wrap outlier Hues around the circle.
        if (fH < 0.0f)
          fH += 1.0f;
        if (fH >= 1.0f)
          fH -= 1.0f;
      }
      else {
        // color is pure Black.
        fS = 0;
        fH = 0;	// undefined hue
      }

      // Convert from floats to 8-bit integers.
      int bH = (int)(0.5f + fH * 255.0f);
      int bS = (int)(0.5f + fS * 255.0f);
      int bV = (int)(0.5f + fV * 255.0f);

      // Clip the values to make sure it fits within the 8bits.
      if (bH > 255)
        bH = 255;
      if (bH < 0)
        bH = 0;
      if (bS > 255)
        bS = 255;
      if (bS < 0)
        bS = 0;
      if (bV > 255)
        bV = 255;
      if (bV < 0)
        bV = 0;

      // Set the HSV pixel components.
      imageHSV.at<Vec3b>(y, x)[0] = bH;		// H component
      imageHSV.at<Vec3b>(y, x)[1] = bS;		// S component
      imageHSV.at<Vec3b>(y, x)[2] = bV;		// V component
    }
  }
  return imageHSV;
}

void BackgroundSubtractorIMBS::getBackgroundImage(OutputArray backgroundImage) const
{
  bgImage.copyTo(backgroundImage);
}

void BackgroundSubtractorIMBS::filterFg() {

  unsigned int cnt = 0;
  for (unsigned int p = 0; p < numPixels; ++p) {
    if (fgmask.data[p] == (uchar)255) {
      fgfiltered.data[p] = 255;
      cnt++;
    }
    else {
      fgfiltered.data[p] = 0;
    }
  }

  if (cnt > numPixels*0.5) {
    sudden_change = true;
  }

  if (morphologicalFiltering) {
    cv::Mat element3(3, 3, CV_8U, cv::Scalar(1));
    cv::morphologyEx(fgfiltered, fgfiltered, cv::MORPH_OPEN, element3);
    cv::morphologyEx(fgfiltered, fgfiltered, cv::MORPH_CLOSE, element3);
  }

  areaThresholding();

  for (unsigned int p = 0; p < numPixels; ++p) {
    if (fgmask.data[p] == PERSISTENCE_LABEL) {
      fgfiltered.data[p] = PERSISTENCE_LABEL;
    }
    else if (fgmask.data[p] == SHADOW_LABEL) {
      fgfiltered.data[p] = SHADOW_LABEL;
    }
  }

  fgfiltered.copyTo(fgmask);
}

void BackgroundSubtractorIMBS::changeBg() {

  std::cout << "\n\n\n\nWARNING: changeBg\n\n\n\n\n" << std::endl;

  //samplingPeriod /= 2.;
  //numSamples /= 2.;
  //bg_reset = true;
  //cout << "qua" << endl;

  if (!bg_reset) {
    numSamples /= 2.;
    samplingPeriod /= 2.;
    bg_frame_counter = 0;
    bg_reset = true;
  }
}

void BackgroundSubtractorIMBS::getBgModel(BgModel bgModel_copy[], unsigned int size) {
  if (size != numPixels) {
    return;
  }
  for (unsigned int i = 0; i < numPixels; ++i) {
    bgModel_copy[i].values = new Vec3b[maxBgBins];
    bgModel_copy[i].isValid = new bool[maxBgBins];
    bgModel_copy[i].isValid[0] = false;
    bgModel_copy[i].isFg = new bool[maxBgBins];
    bgModel_copy[i].counter = new uchar[maxBgBins];
  }
  for (unsigned int p = 0; p < numPixels; ++p) {
    for (unsigned int n = 0; n < maxBgBins; ++n) {
      if (!bgModel[p].isValid[n]) {
        break;
      }
      bgModel_copy[p].values[n] = bgModel[p].values[n];
      bgModel_copy[p].isValid[n] = bgModel[p].isValid[n];
      bgModel_copy[p].isFg[n] = bgModel[p].isFg[n];
      bgModel_copy[p].counter[n] = bgModel[p].counter[n];
    }
  }
}

--#-- END ./bgslibrary/algorithms/IMBS/IMBS.cpp --#--

--#-- START ./bgslibrary/algorithms/SigmaDelta.cpp --#--
#include "SigmaDelta.h"

using namespace bgslibrary::algorithms;

SigmaDelta::SigmaDelta() :
  IBGS(quote(SigmaDelta)),
  ampFactor(1), minVar(15), maxVar(255), 
  algorithm(sigmadelta::sdLaMa091New())
{
  debug_construction(SigmaDelta);
  initLoadSaveConfig(algorithmName);
  applyParams();
}

SigmaDelta::~SigmaDelta() {
  debug_destruction(SigmaDelta);
  sigmadelta::sdLaMa091Free(algorithm);
}

void SigmaDelta::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    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);
    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;

    for (size_t i = 0; i < img_foreground.total(); ++i) {
      *outBuffer = *tmpBuffer;
      ++outBuffer;
      tmpBuffer += img_output_tmp.channels();
    }
  }

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
}

void SigmaDelta::save_config(cv::FileStorage &fs) {
  fs << "ampFactor" << ampFactor;
  fs << "minVar" << minVar;
  fs << "maxVar" << maxVar;
  fs << "showOutput" << showOutput;
}

void SigmaDelta::load_config(cv::FileStorage &fs) {
  fs["ampFactor"] >> ampFactor;
  fs["minVar"] >> minVar;
  fs["maxVar"] >> maxVar;
  fs["showOutput"] >> showOutput;
}

void SigmaDelta::applyParams() {
  sigmadelta::sdLaMa091SetAmplificationFactor(algorithm, ampFactor);
  sigmadelta::sdLaMa091SetMinimalVariance(algorithm, minVar);
  sigmadelta::sdLaMa091SetMaximalVariance(algorithm, maxVar);
}

--#-- END ./bgslibrary/algorithms/SigmaDelta.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiLayer.cpp --#--
#include "MultiLayer.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

using namespace bgslibrary::algorithms;

MultiLayer::MultiLayer() :
  IBGS(quote(MultiLayer)),
  frameNumber(0), saveModel(false),
  disableDetectMode(true), disableLearning(false),
  detectAfter(0), bg_model_preload(""), loadDefaultParams(true)
{
  debug_construction(MultiLayer);
  initLoadSaveConfig(algorithmName);
}

MultiLayer::~MultiLayer() {
  debug_destruction(MultiLayer);
  finish();
}

void MultiLayer::setStatus(Status _status) {
  status = _status;
}

void MultiLayer::finish() {
  if (bg_model_preload.empty()) {
    bg_model_preload = "./" + algorithmName + ".yml";
  }

  if (status == MLBGS_LEARN && saveModel == true) {
    std::cout << algorithmName + " saving background model: " << bg_model_preload << std::endl;
    BGS->Save(bg_model_preload.c_str());
  }

  cvReleaseImage(&fg_img);
  cvReleaseImage(&bg_img);
  cvReleaseImage(&fg_prob_img);
  cvReleaseImage(&fg_mask_img);
  cvReleaseImage(&fg_prob_img3);
  cvReleaseImage(&merged_img);

  delete BGS;
}

void MultiLayer::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);
  CvSize img_size = cvSize(cvCeil((double)img_input.size().width), cvCeil((double)img_input.size().height));

  if (firstTime) {
    if (disableDetectMode)
      status = MLBGS_LEARN;

    if (status == MLBGS_LEARN)
      std::cout << algorithmName + " in LEARN mode" << std::endl;

    if (status == MLBGS_DETECT)
      std::cout << algorithmName + " in DETECT mode" << std::endl;
    
    IplImage _frame = cvIplImage(img_input);
    org_img = cvCloneImage(&_frame);

    fg_img = cvCreateImage(img_size, org_img->depth, org_img->nChannels);
    bg_img = cvCreateImage(img_size, org_img->depth, org_img->nChannels);
    fg_prob_img = cvCreateImage(img_size, org_img->depth, 1);
    fg_mask_img = cvCreateImage(img_size, org_img->depth, 1);
    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 multilayer::CMultiLayerBGS();
    BGS->Init(img_size.width, img_size.height);
    BGS->SetForegroundMaskImage(fg_mask_img);
    BGS->SetForegroundProbImage(fg_prob_img);

    if (bg_model_preload.empty() == false) {
      std::cout << algorithmName + " loading background model: " << bg_model_preload << std::endl;
      BGS->Load(bg_model_preload.c_str());
    }

    if (status == MLBGS_DETECT) {
      BGS->m_disableLearning = disableLearning;

      if (disableLearning)
        std::cout << algorithmName + " disabled learning in DETECT mode" << std::endl;
      else
        std::cout << algorithmName + " enabled learning in DETECT mode" << std::endl;
    }

    if (loadDefaultParams) {
      std::cout << algorithmName + " loading default params" << std::endl;
      max_mode_num = 5;
      weight_updating_constant = 5.0;
      texture_weight = 0.5;
      bg_mode_percent = 0.6f;
      pattern_neig_half_size = 4;
      pattern_neig_gaus_sigma = 3.0f;
      bg_prob_threshold = 0.2f;
      bg_prob_updating_threshold = 0.2f;
      robust_LBP_constant = 3;
      min_noised_angle = 10.0 / 180.0 * PI; //0,01768
      shadow_rate = 0.6f;
      highlight_rate = 1.2f;
      bilater_filter_sigma_s = 3.0f;
      bilater_filter_sigma_r = 0.1f;
    }
    else
      std::cout << algorithmName + " loading config params" << std::endl;

    BGS->m_nMaxLBPModeNum = max_mode_num;
    BGS->m_fWeightUpdatingConstant = weight_updating_constant;
    BGS->m_fTextureWeight = texture_weight;
    BGS->m_fBackgroundModelPercent = bg_mode_percent;
    BGS->m_nPatternDistSmoothNeigHalfSize = pattern_neig_half_size;
    BGS->m_fPatternDistConvGaussianSigma = pattern_neig_gaus_sigma;
    BGS->m_fPatternColorDistBgThreshold = bg_prob_threshold;
    BGS->m_fPatternColorDistBgUpdatedThreshold = bg_prob_updating_threshold;
    BGS->m_fRobustColorOffset = robust_LBP_constant;
    BGS->m_fMinNoisedAngle = min_noised_angle;
    BGS->m_fRobustShadowRate = shadow_rate;
    BGS->m_fRobustHighlightRate = highlight_rate;
    BGS->m_fSigmaS = bilater_filter_sigma_s;
    BGS->m_fSigmaR = bilater_filter_sigma_r;

    if (loadDefaultParams) {
      //frame_duration = 1.0 / 30.0;
      //frame_duration = 1.0 / 25.0;
      frame_duration = 1.0f / 10.0f;
    }

    BGS->SetFrameRate(frame_duration);

    if (status == MLBGS_LEARN) {
      if (loadDefaultParams) {
        mode_learn_rate_per_second = 0.5;
        weight_learn_rate_per_second = 0.5;
        init_mode_weight = 0.05f;
      }
      else {
        mode_learn_rate_per_second = learn_mode_learn_rate_per_second;
        weight_learn_rate_per_second = learn_weight_learn_rate_per_second;
        init_mode_weight = learn_init_mode_weight;
      }
    }

    if (status == MLBGS_DETECT) {
      if (loadDefaultParams) {
        mode_learn_rate_per_second = 0.01f;
        weight_learn_rate_per_second = 0.01f;
        init_mode_weight = 0.001f;
      }
      else {
        mode_learn_rate_per_second = detect_mode_learn_rate_per_second;
        weight_learn_rate_per_second = detect_weight_learn_rate_per_second;
        init_mode_weight = detect_init_mode_weight;
      }
    }

    BGS->SetParameters(max_mode_num, mode_learn_rate_per_second, weight_learn_rate_per_second, init_mode_weight);
  }

  //IplImage* inputImage = new IplImage(img_input);
  //IplImage* img = cvCreateImage(img_size, IPL_DEPTH_8U, 3);
  //cvCopy(inputImage, img);
  //delete inputImage;

  if (detectAfter > 0 && detectAfter == frameNumber) {
    std::cout << algorithmName + " in DETECT mode" << std::endl;
    status = MLBGS_DETECT;

    mode_learn_rate_per_second = 0.01f;
    weight_learn_rate_per_second = 0.01f;
    init_mode_weight = 0.001f;

    BGS->SetParameters(max_mode_num, mode_learn_rate_per_second, weight_learn_rate_per_second, init_mode_weight);
    BGS->m_disableLearning = disableLearning;

    if (disableLearning)
      std::cout << algorithmName + " disabled learning in DETECT mode" << std::endl;
    else
      std::cout << algorithmName + " enabled learning in DETECT mode" << std::endl;
  }

  IplImage _frame = cvIplImage(img_input);
  IplImage* img = cvCloneImage(&_frame);

  BGS->SetRGBInputImage(img);
  BGS->Process();

  BGS->GetBackgroundImage(bg_img);
  BGS->GetForegroundImage(fg_img);
  BGS->GetForegroundProbabilityImage(fg_prob_img3);
  BGS->GetForegroundMaskImage(fg_mask_img);
  BGS->MergeImages(4, img, bg_img, fg_prob_img3, fg_img, merged_img);

  img_merged = cv::cvarrToMat(merged_img);
  img_foreground = cv::cvarrToMat(fg_mask_img);
  img_background = cv::cvarrToMat(bg_img);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_LAYERS", img_merged);
    cv::imshow(algorithmName + "_FG", img_foreground);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&img);

  firstTime = false;
  frameNumber++;
}

void MultiLayer::save_config(cv::FileStorage &fs) {
  fs << "preloadModel" << bg_model_preload;
  fs << "saveModel" << saveModel;
  fs << "detectAfter" << detectAfter;
  fs << "disableDetectMode" << showOutput;
  fs << "disableLearningInDetecMode" << disableLearning;
  fs << "loadDefaultParams" << loadDefaultParams;
  fs << "frame_duration" << frame_duration;
  fs << "max_mode_num" << max_mode_num;
  fs << "weight_updating_constant" << weight_updating_constant;
  fs << "texture_weight" << texture_weight;
  fs << "bg_mode_percent" << bg_mode_percent;
  fs << "pattern_neig_half_size" << pattern_neig_half_size;
  fs << "pattern_neig_gaus_sigma" << pattern_neig_gaus_sigma;
  fs << "bg_prob_threshold" << bg_prob_threshold;
  fs << "bg_prob_updating_threshold" << bg_prob_updating_threshold;
  fs << "robust_LBP_constant" << robust_LBP_constant;
  fs << "min_noised_angle" << min_noised_angle;
  fs << "shadow_rate" << shadow_rate;
  fs << "highlight_rate" << highlight_rate;
  fs << "bilater_filter_sigma_s" << bilater_filter_sigma_s;
  fs << "bilater_filter_sigma_r" << bilater_filter_sigma_r;
  fs << "learn_mode_learn_rate_per_second" << learn_mode_learn_rate_per_second;
  fs << "learn_weight_learn_rate_per_second" << learn_weight_learn_rate_per_second;
  fs << "learn_init_mode_weight" << learn_init_mode_weight;
  fs << "detect_mode_learn_rate_per_second" << detect_mode_learn_rate_per_second;
  fs << "detect_weight_learn_rate_per_second" << detect_weight_learn_rate_per_second;
  fs << "detect_init_mode_weight" << detect_init_mode_weight;
  fs << "showOutput" << showOutput;
}

void MultiLayer::load_config(cv::FileStorage &fs) {
  fs["preloadModel"] >> bg_model_preload;
  fs["saveModel"] >> saveModel;
  fs["detectAfter"] >> detectAfter;
  fs["disableDetectMode"] >> disableDetectMode;
  fs["disableLearningInDetecMode"] >> disableLearning;
  fs["loadDefaultParams"] >> loadDefaultParams;
  fs["frame_duration"] >> frame_duration;
  fs["max_mode_num"] >> max_mode_num;
  fs["weight_updating_constant"] >> weight_updating_constant;
  fs["texture_weight"] >> texture_weight;
  fs["bg_mode_percent"] >> bg_mode_percent;
  fs["pattern_neig_half_size"] >> pattern_neig_half_size;
  fs["pattern_neig_gaus_sigma"] >> pattern_neig_gaus_sigma;
  fs["bg_prob_threshold"] >> bg_prob_threshold;
  fs["bg_prob_updating_threshold"] >> bg_prob_updating_threshold;
  fs["robust_LBP_constant"] >> robust_LBP_constant;
  fs["min_noised_angle"] >> min_noised_angle;
  fs["shadow_rate"] >> shadow_rate;
  fs["highlight_rate"] >> highlight_rate;
  fs["bilater_filter_sigma_s"] >> bilater_filter_sigma_s;
  fs["bilater_filter_sigma_r"] >> bilater_filter_sigma_r;
  fs["learn_mode_learn_rate_per_second"] >> learn_mode_learn_rate_per_second;
  fs["learn_weight_learn_rate_per_second"] >> learn_weight_learn_rate_per_second;
  fs["learn_init_mode_weight"] >> learn_init_mode_weight;
  fs["detect_mode_learn_rate_per_second"] >> detect_mode_learn_rate_per_second;
  fs["detect_weight_learn_rate_per_second"] >> detect_weight_learn_rate_per_second;
  fs["detect_init_mode_weight"] >> detect_init_mode_weight;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BlobExtraction.cpp --#--
#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "BlobResult.h"
#include "BlobExtraction.h"
#include "OpenCvLegacyIncludes.h"

//! Indica si la connectivitat es a 8 (si es desactiva es a 4)
#define B_CONNECTIVITAT_8

namespace bgslibrary
{
  namespace algorithms
  {
    namespace multilayer
    {
      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;

          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;
          }

          int Trans = Cols;				// MAX trans in any row
          char* pMask = NULL;
          char* pImage;

          // 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

          // row 0 and row Rows+1 represent the border
          int i;
          int *Transition;				// Transition Matrix

          int nombre_pixels_mascara = 0;
          //! Imatge amb el perimetre extern de cada pixel
          IplImage *imatgePerimetreExtern;

          // 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 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;

          /*
            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;

            //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;

              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

            char perimeter;
            char *pPerimetre;

            // creem la imatge que contindrà el perimetre extern de cada pixel
            imatgePerimetreExtern = cvCreateImage(cvSize(maskImage->width, maskImage->height), IPL_DEPTH_8U, 1);
            cvSetZero(imatgePerimetreExtern);

            pMask = maskImage->imageData - 1;

            //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;

              pPerimetre = imatgePerimetreExtern->imageData + (iRow - 1) * imatgePerimetreExtern->widthStep;
              //pMask = maskImage->imageData + (iRow-1) * maskImage->widthStep;

              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;

                if (ThisCell != LastCell)
                {
                  Transition[TransitionOffset + iTran] = Tran;	// Save completed Tran
                  iTran++;						// Prepare new index
                  LastCell = ThisCell;			// With this color
                }

                /*////////////////////////////////////////////////////////////////////////
                  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 al nord de l'actual
                    if (iRow > 1)
                    {
                      if (*(pMask - maskImage->widthStep) == PIXEL_EXTERIOR) perimeter++;
                    }

                    // pixels a l'est i oest de l'actual
                    if (iRow < imatgePerimetreExtern->height)
                    {
                      if ((iCol > 0) && (*(pMask - 1) == PIXEL_EXTERIOR)) perimeter++;

                      if ((iCol < imatgePerimetreExtern->width - 1) && (*(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;
                  }
                }

                Tran++;	// Tran continues
                pImage++;
                pMask++;
                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;
              //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;
            }
          }

          // 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;
          }

          //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

          // 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;

          char *pImageAux, *pMaskAux = NULL;

          // 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

              if (ThisRegion[j] == -1) { EndLast = 1; }
              if (TranVal < 0) { EndThis = 1; }

              if (EndLast > 0 && EndThis > 0) { break; }

              LastRegion[j] = ThisRegion[j];
              ThisRegion[j] = -1;		// Flag indicates region is not initialized
            }

            int MaxIndexCount = LastIndexCount;
            if (ThisIndexCount > MaxIndexCount) MaxIndexCount = ThisIndexCount;

            // 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;
              }

              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);

              int TestMatch = (ThisColor == LastColor);		// initially true
              int TestKnown = (ThisRegion[ThisIndex] >= 0);	// initially false

              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;
              }

              // Initialize common variables
              ThisArea = (float) 0.0;

              if (findmoments)
              {
                ThisSumX = ThisSumY = (float) 0.0;
                ThisSumXX = ThisSumYY = ThisSumXY = (float) 0.0;
              }
              ThisMinX = ThisMinY = (float) 1000000.0;
              ThisMaxX = ThisMaxY = (float)-1.0;

              LastPerimeter = ThisPerimeter = (float) 0.0;
              ThisParent = (float)-1;
              ThisExternPerimeter = 0.0;

              // Determine necessary action and take it
              switch (Case)
              {
              case 1: //|xxx    |
                //|    yyy|

                ThisRegion[ThisIndex] = ThisRegionNum;
                LastRegion[LastIndex] = LastRegionNum;
                LastIndex++;

                //afegim la cantonada a LastRegion
                actualedge.x = ThisEnd;
                actualedge.y = ThisRow - 1;
                cvSeqPush(regionDataLastRegion->edges, &actualedge);

                //afegim la cantonada a ThisRegion
                actualedge.x = ThisStart - 1;
                actualedge.y = ThisRow - 1;
                cvSeqPush(regionDataThisRegion->edges, &actualedge);

                break;


              case 2: //|xxxxoo |
                //|    yyy|

                if (TestMatch)	// Same color
                {
                  ThisRegionNum = LastRegionNum;
                  regionDataThisRegion = regionDataLastRegion;

                  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;
                }

                //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);
                  }
                }

                ThisRegion[ThisIndex] = ThisRegionNum;
                LastRegion[LastIndex] = LastRegionNum;
                LastIndex++;
                break;


              case 3: //|xxxxxxx|
                //|  yyyy |

                if (TestMatch)	// Same color
                {
                  ThisRegionNum = LastRegionNum;
                  regionDataThisRegion = regionDataLastRegion;

                  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);

                }

                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);
                }

                ThisRegion[ThisIndex] = ThisRegionNum;
                LastRegion[LastIndex] = LastRegionNum;
                ComputeData = 1;
                ThisIndex++;
                break;


              case 4:	//|xxxxxxx|
                //|  yyyyy|

                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);

                    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);

                }

                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);
                }

                ThisRegion[ThisIndex] = ThisRegionNum;
                LastRegion[LastIndex] = LastRegionNum;
                ComputeData = 1;

      #ifdef B_CONNECTIVITAT_8
                if (TestMatch)
                {
                  LastIndex++;
                  ThisIndex++;
                }
                else
                {
                  LastIndex++;
                }
      #else
                LastIndex++;
                ThisIndex++;
      #endif					
                break;


              case 5:	//|ooxxxxx|
                //|yyyyyyy|

                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);

                }
                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);


                    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 (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 != -1)
                {
                  actualedge.x = ThisEnd;
                  actualedge.y = ThisRow - 1;
                  cvSeqPush(regionDataThisRegion->edges, &actualedge);

                  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);
                }

                ThisRegion[ThisIndex] = ThisRegionNum;
                LastRegion[LastIndex] = LastRegionNum;

      #ifdef B_CONNECTIVITAT_8
                if (TestMatch)
                {
                  LastIndex++;
                  ThisIndex++;
                }
                else
                {
                  LastIndex++;
                }
      #else
                LastIndex++;
                ThisIndex++;
      #endif	
                break;


              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);


                    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;
                  }
                }


                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;


              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);

                }
                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);

                    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;
                  }
                }

                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);
                  }
                }

                ThisRegion[ThisIndex] = ThisRegionNum;
                LastRegion[LastIndex] = LastRegionNum;
                ThisIndex++;
                break;

              case 8:	//|    xxx|
                //|yyyy   |

      #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;
                  }

                  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)
                {
                  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;
              }

              // 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)
                {
                  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;
                }

              }
            }	// 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++)
              {
                //if(RegionData[i]->parent == iOld) { RegionData[i]->parent = iNew; }
                if ((*iti)->parent == iOld) { (*iti)->parent = iNew; }

                ++iti;
              }
              iNew++;
              ++itNew;
            }
          }


          HighRegionNum = iNew - 1;				// Update where the data ends
          RegionData[HighRegionNum]->parent = -1;	// and set end of array flag


          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;
        }

        /**
          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; }
          }

          // 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();
        }

        /**
          - 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 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;

            pPerimetre = imatgePerimetreExtern->imageData + (row - 1) * imatgePerimetreExtern->widthStep + start;
            for (int x = start - 1; x <= end; x++)
            {
              perimeter += *pPerimetre;
              pPerimetre++;
            }
          }

          return perimeter;
        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/BlobExtraction.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/blob.cpp --#--
#include <limits.h>
#include "blob.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace multilayer
    {
      namespace blob
      {
        /**
          - 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()
        {
          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))
          {
            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Ó: 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;

          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);
        }

        /**
          - 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);
        }

        /**
          - 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Ó: 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;
        }



        /***************************************************************************
          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();

          //Moment 10
          if ((m_p == 1) && (m_q == 0))
            return blob.SumX();

          //Moment 01
          if ((m_p == 0) && (m_q == 1))
            return blob.SumY();

          //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;
        }

        /**
          - 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();
        }

        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Ó: 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;

          cvStartReadSeq(blob.Edges(), &reader);

          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 MinX_at_MinY;
        }

        /**
          - 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;

          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;
            }
          }

          return MinY_at_MaxX;
        }

        /**
          - 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;

          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;
            }
          }

          return MaxX_at_MaxY;
        }

        /**
          - 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;

          CvSeqReader reader;
          CvPoint edgeactual;

          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;
            }
          }

          return MaxY_at_MinX;
        }

        /**
          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;

          ampladaC = (double)(blob.Perimeter() + sqrt(pow(blob.Perimeter(), 2) - 16 * blob.Area())) / 4;
          if (ampladaC <= 0.0) return 0;
          longitudC = (double)blob.Area() / ampladaC;

          longitud = MAX(longitudC, ampladaC);
          amplada = MIN(longitudC, ampladaC);

          return (double)longitud / amplada;
        }

        /**
          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;
        }

        /**
          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();

          double hullPerimeter = getHullPerimeter(blob);

          if (hullPerimeter != 0.0)
            return blob.Perimeter() / hullPerimeter;//HullPerimeter();

          return 0.0;
        }

        /**
          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();

          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;

          return MAX(longitudC, ampladaC);
        }

        /**
          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;

          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 (ampladaC <= 0.0) return 0;
          longitudC = (double)blob.Area() / ampladaC;

          return MIN(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;

          xmitjana = m_x - getXCenter(blob);
          ymitjana = m_y - getYCenter(blob);

          return sqrt((xmitjana*xmitjana) + (ymitjana*ymitjana));
        }

        /**
          - 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;
        }

      #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
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/blob.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/CMultiLayerBGS.cpp --#--
#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <cmath>
#include <iostream>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "CMultiLayerBGS.h"
#include "OpenCvLegacyIncludes.h"

using namespace bgslibrary::algorithms::multilayer;
using namespace bgslibrary::algorithms::multilayer::blob;

CMultiLayerBGS::CMultiLayerBGS() {
  m_nMaxLBPModeNum = MAX_LBP_MODE_NUM;

  m_fModeUpdatingLearnRate = MODE_UPDATING_LEARN_RATE;
  m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate;

  m_fWeightUpdatingLearnRate = WEIGHT_UPDATING_LEARN_RATE;
  m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate;

  m_fRobustColorOffset = ROBUST_COLOR_OFFSET;

  m_fLowInitialModeWeight = LOW_INITIAL_MODE_WEIGHT;

  m_fPatternColorDistBgThreshold = PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD;
  m_fPatternColorDistBgUpdatedThreshold = PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD;

  m_fBackgroundModelPercent = BACKGROUND_MODEL_PERCENT;

  m_nPatternDistSmoothNeigHalfSize = PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE;
  m_fPatternDistConvGaussianSigma = PATTERN_DIST_CONV_GAUSSIAN_SIGMA;

  m_fRobustShadowRate = ROBUST_SHADOW_RATE;
  m_fRobustHighlightRate = ROBUST_HIGHLIGHT_RATE;

  m_nCurImgFrameIdx = 0;

  m_pBkMaskImg = NULL;

  m_bUsedColorLBP = false;
  m_bUsedGradImage = false;

  m_fMinLBPBinaryProb = 0.1f;
  m_f1_MinLBPBinaryProb = 1.0f - m_fMinLBPBinaryProb;

  m_pOrgImg = m_pFgImg = m_pBgImg = m_pFgMaskImg = m_pBgDistImg = m_pEdgeImg = NULL;
  m_ppOrgLBPImgs = NULL;

  m_disableLearning = false;
  m_fSigmaS = 3.0f;
  m_fSigmaR = 0.1f;

  m_fTextureWeight = 0.5f;
  m_fColorWeight = 1.0f - m_fTextureWeight;

  m_fWeightUpdatingConstant = 5.0f;

  m_fReliableBackgroundModeWeight = 0.9f;

  //m_fMinBgLayerWeight = m_fLowInitialModeWeight/50.0f;
  m_fMinBgLayerWeight = 0.0001f;
  //m_fMinBgLayerWeight = 0.88f;

  m_fMinNoisedAngle = 3.0f / 180.0f * PI;
  m_fMinNoisedAngleSine = sinf(m_fMinNoisedAngle);

  m_fFrameDuration = 1.0f / 25.0f; /* 25 frames per second */

  m_fModeUpdatingLearnRatePerSecond = 0.2f;
  m_fWeightUpdatingLearnRatePerSecond = 0.2f;

  m_pROI = NULL;
}

CMultiLayerBGS::~CMultiLayerBGS() {
  int img_length = m_cvImgSize.height * m_cvImgSize.width;
  PixelLBPStruct* PLBP = m_pPixelLBPs;
  for (int yx = 0; yx < img_length; yx++) {
    delete (*PLBP).cur_intensity;
    delete (*PLBP).cur_pattern;
    delete (*PLBP).lbp_idxes;
    for (int a = 0; a < m_nMaxLBPModeNum; a++) {
      delete (*PLBP).LBPs[a].bg_intensity;
      delete (*PLBP).LBPs[a].max_intensity;
      delete (*PLBP).LBPs[a].min_intensity;
      delete (*PLBP).LBPs[a].bg_pattern;
    }
    delete (*PLBP).LBPs;
    PLBP++;
  }
  delete m_pPixelLBPs;

  /* release memories */
  if (m_pFgImg != NULL)
    cvReleaseImage(&m_pFgImg);
  if (m_pBgImg != NULL)
    cvReleaseImage(&m_pBgImg);
  if (m_pBgDistImg != NULL)
    cvReleaseImage(&m_pBgDistImg);
  if (m_ppOrgLBPImgs != NULL) {
    int a;
    for (a = 0; a < m_nLBPImgNum; a++)
      cvReleaseImage(&m_ppOrgLBPImgs[a]);
    delete[] m_ppOrgLBPImgs;
  }
  if (m_pEdgeImg)
    cvReleaseImage(&m_pEdgeImg);
}

void CMultiLayerBGS::ResetAllParameters() {
  m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate;
  m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate;
  m_f1_MinLBPBinaryProb = 1.0f - m_fMinLBPBinaryProb;

  m_fColorWeight = 1.0f - m_fTextureWeight;

  m_fMinNoisedAngleSine = sinf(m_fMinNoisedAngle);

  //m_fMinBgLayerWeight = m_fLowInitialModeWeight/50.0f;
  m_fMinBgLayerWeight = 0.0001f;

  m_cLBP.m_fRobustWhiteNoise = m_fRobustColorOffset;
}

void CMultiLayerBGS::MergeImages(int num, ...) {
  if (num < 1 || num > 9) {
    printf("Error: the number %d of merging images.\n", num);
    exit(0);
  }

  int nCols = 0, nRows = 0;
  switch (num) {
  case 1: nCols = nRows = 1;
    break;
  case 2: nCols = 1;
    nRows = 2;
    break;
  case 3:
  case 4: nCols = 2;
    nRows = 2;
    break;
  case 5:
  case 6: nCols = 3;
    nRows = 2;
    break;
  case 7:
  case 8:
  case 9: nCols = 3;
    nRows = 3;
    break;
  }

  int a, b;

  IplImage** ppIplImg = new IplImage*[num + 1];

  va_list arg_ptr;
  va_start(arg_ptr, num);
  for (a = 0; a < num + 1; a++)
    ppIplImg[a] = va_arg(arg_ptr, IplImage*);
  va_end(arg_ptr);

  CvRect imgROIRect;
  CvSize imgSize = cvSize(ppIplImg[0]->width, ppIplImg[0]->height);
  if (ppIplImg[num] == NULL) { // for the output video
    ppIplImg[num] = cvCreateImage(cvSize(imgSize.width*nCols, imgSize.height * nRows), IPL_DEPTH_8U, ppIplImg[0]->nChannels);
  }

  int img_idx = 0;
  for (a = 0; a < nRows; a++)
    for (b = 0; b < nCols; b++) {
      if (img_idx >= num)
        break;

      imgROIRect = cvRect(b * imgSize.width, a * imgSize.height, imgSize.width, imgSize.height);

      cvSetImageROI(ppIplImg[num], imgROIRect);
      cvCopy(ppIplImg[img_idx++], ppIplImg[num]);
      cvResetImageROI(ppIplImg[num]);
    }

  delete[] ppIplImg;
}

void CMultiLayerBGS::Update_MAX_MIN_Intensity(unsigned char *cur_intensity, float *max_intensity, float *min_intensity) {
  int a;
  float curI;
  for (a = 0; a < m_nChannel; a++) {
    curI = (float)cur_intensity[a];

    min_intensity[a] = MIN(curI, min_intensity[a]);
    max_intensity[a] = MAX(curI, max_intensity[a]);
  }
}

void CMultiLayerBGS::UpdateBgPixelColor(unsigned char *cur_intensity, float* bg_intensity) {
  int a;
  for (a = 0; a < m_nChannel; a++)
    bg_intensity[a] = m_f1_ModeUpdatingLearnRate * bg_intensity[a] + m_fModeUpdatingLearnRate * (float)cur_intensity[a];
}

void CMultiLayerBGS::UpdateBgPixelPattern(float *cur_pattern, float *bg_pattern) {
  int a;
  for (a = 0; a < m_nLBPLength; a++)
    bg_pattern[a] = m_f1_ModeUpdatingLearnRate * bg_pattern[a] + m_fModeUpdatingLearnRate * cur_pattern[a];
}

/* sort everything inbetween `low' <-> `high' */
void CMultiLayerBGS::QuickSort(float *pData, unsigned short *pIdxes, long low, long high, bool bAscent) {
  long i = low;
  long j = high;
  float y = 0;
  int idx = 0;

  /* compare value */
  float z = pData[(low + high) / 2];

  /* partition */
  do {
    if (bAscent) {
      /* find member above ... */
      while (pData[i] < z) i++;

      /* find element below ... */
      while (pData[j] > z) j--;
    }
    else {
      /* find member below ... */
      while (pData[i] > z) i++;

      /* find element above ... */
      while (pData[j] < z) j--;
    }

    if (i <= j) {
      /* swap two elements */
      y = pData[i];
      pData[i] = pData[j];
      pData[j] = y;

      idx = pIdxes[i];
      pIdxes[i] = pIdxes[j];
      pIdxes[j] = idx;

      i++;
      j--;
    }
  } while (i <= j);

  /* recurse */
  if (low < j)
    QuickSort(pData, pIdxes, low, j, bAscent);

  if (i < high)
    QuickSort(pData, pIdxes, i, high, bAscent);
}

float CMultiLayerBGS::DistLBP(LBPStruct *LBP1, LBPStruct *LBP2) {
  int a;

  float pattern_dist = 0;
  for (a = 0; a < m_nLBPLength; a++) {
    pattern_dist = fabsf(LBP1->bg_pattern[a] - LBP1->bg_pattern[a]);
  }
  pattern_dist /= (float)m_nLBPLength;

  float color_dist = 0;
  for (a = 0; a < m_nChannel; a++) {
    color_dist += fabsf((float)LBP1->bg_intensity[a] - (float)LBP2->bg_intensity[a]);
  }
  color_dist /= 3.0f * 125.0f;

  //return MAX(pattern_dist, color_dist);
  return color_dist;
}

void CMultiLayerBGS::SetNewImage(IplImage *new_img, CvRect *roi) {
  m_pOrgImg = new_img;
  m_pROI = roi;
  if (roi && (roi->width <= 0 || roi->height <= 0))
    return;

  if (roi) {
    cvSetImageROI(m_pOrgImg, *roi);
    for (int a = 0; a < m_nLBPImgNum; a++)
      cvSetImageROI(m_ppOrgLBPImgs[a], *roi);
  }

  switch (m_nLBPImgNum) {
  case 1:
    cvCvtColor(m_pOrgImg, m_ppOrgLBPImgs[0], CV_BGR2GRAY);
    break;
  case 2:
    cvCvtColor(m_pOrgImg, m_ppOrgLBPImgs[0], CV_BGR2GRAY);
    ComputeGradientImage(m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[1], false);
    break;
  case 3:
    cvSplit(m_pOrgImg, m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[1], m_ppOrgLBPImgs[2], NULL);
    break;
  case 4:
    cvSplit(m_pOrgImg, m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[1], m_ppOrgLBPImgs[2], NULL);
    ComputeGradientImage(m_ppOrgLBPImgs[0], m_ppOrgLBPImgs[3], false);
    break;
  }

  if (roi) {
    cvResetImageROI(m_pOrgImg);
    for (int a = 0; a < m_nLBPImgNum; a++)
      cvResetImageROI(m_ppOrgLBPImgs[a]);
  }
  m_cLBP.SetNewImages(m_ppOrgLBPImgs);

  m_nCurImgFrameIdx++;
}

void CMultiLayerBGS::SetBkMaskImage(IplImage *mask_img) {
  if (m_pBkMaskImg == NULL) {
    m_pBkMaskImg = cvCreateImage(cvSize(mask_img->width, mask_img->height), mask_img->depth, mask_img->nChannels);
  }
  cvCopy(mask_img, m_pBkMaskImg);
}

void CMultiLayerBGS::BackgroundSubtractionProcess() {
  CvRect *roi = m_pROI;

  if (roi && (roi->width <= 0 || roi->height <= 0))
    return;
  LBPStruct* LBPs;
  unsigned int bg_num;
  float* cur_pattern;
  unsigned char* cur_intensity;
  int a, b;
  unsigned int lbp_num;
  unsigned short* lbp_idxes;
  unsigned short cur_lbp_idx;
  bool bBackgroundUpdating;

  PixelLBPStruct *PLBP = m_pPixelLBPs;

  bool bFirstFrame = (PLBP[0].num == 0);
  float best_match_bg_dist, bg_pattern_dist, bg_color_dist, bg_pattern_color_dist;

  // compute the local binary pattern
  if (m_fTextureWeight > 0)
    m_cLBP.ComputeLBP(PLBP, roi);

  LBPStruct* curLBP;

  int data_length;

  if (roi)
    data_length = roi->width * roi->height;
  else
    data_length = m_cvImgSize.width * m_cvImgSize.height;

  int best_match_idx;

  COpencvDataConversion<uchar, uchar> ODC1;
  if (roi) {
    cvSetImageROI(m_pBkMaskImg, *roi);
    cvSetImageROI(m_pOrgImg, *roi);
  }
  uchar *_mask = ODC1.GetImageData(m_pBkMaskImg);
  uchar *_org_intensity = ODC1.GetImageData(m_pOrgImg);

  if (roi) {
    cvResetImageROI(m_pBkMaskImg);
    cvResetImageROI(m_pOrgImg);
  }

  COpencvDataConversion<float, float> ODC2;
  float *_bg_dist = new float[data_length];

  uchar *mask = _mask;
  uchar *org_intensity = _org_intensity;
  float *bg_dist = _bg_dist;

  bool removed_modes[10];

  // scanning the point via first x-axis and then y-axis
  int x, y;

  for (y = 0; y < (roi ? roi->height : m_cvImgSize.height); y++) {
    if (roi)
      PLBP = m_pPixelLBPs + (roi->y + y) * m_cvImgSize.width + roi->x;
    else
      PLBP = m_pPixelLBPs + y * m_cvImgSize.width;

    for (x = 0; x < (roi ? roi->width : m_cvImgSize.width); x++) {
      // check whether the current pixel is the pixel to be modeled
      if (*mask++ == 0) {
        PLBP++;
        *bg_dist++ = 0.0f; //m_fPatternColorDistBgThreshold*1.01f;
        org_intensity += m_nChannel;
        continue;
      }

      // removing the background layers
      if (!m_disableLearning) {
        RemoveBackgroundLayers(PLBP);
      }

      // check whether the current image is the first image
      bFirstFrame = ((*PLBP).num == 0);

      // get lbp information
      lbp_num = (*PLBP).num;
      LBPs = (*PLBP).LBPs;
      lbp_idxes = (*PLBP).lbp_idxes;

      (*PLBP).cur_bg_layer_no = 0;

      // set the current pixel's intensity
      cur_intensity = (*PLBP).cur_intensity;
      for (a = 0; a < m_nChannel; a++)
        cur_intensity[a] = *org_intensity++;

      // get the current lbp pattern
      cur_pattern = (*PLBP).cur_pattern;

      // first check whether the pixel is background or foreground and then update the background pattern model
      if (lbp_num == 0) { // empty pattern list
        curLBP = (&(LBPs[0]));
        for (a = 0; a < m_nLBPLength; a++) {
          curLBP->bg_pattern[a] = (float)cur_pattern[a];
        }

        curLBP->bg_layer_num = 0;
        curLBP->weight = m_fLowInitialModeWeight;
        curLBP->max_weight = m_fLowInitialModeWeight;

        curLBP->first_time = m_nCurImgFrameIdx;
        curLBP->last_time = m_nCurImgFrameIdx;
        curLBP->freq = 1;

        (*PLBP).matched_mode_first_time = (float)m_nCurImgFrameIdx;

        for (a = 0; a < m_nChannel; a++) {
          curLBP->bg_intensity[a] = (float)cur_intensity[a];
          curLBP->min_intensity[a] = (float)cur_intensity[a];
          curLBP->max_intensity[a] = (float)cur_intensity[a];
        }

        lbp_idxes[0] = 0;

        lbp_num++;
        (*PLBP).num = 1;
        (*PLBP).bg_num = 1;

        PLBP++;
        *bg_dist++ = 0.0f;
        continue;
      }
      else { // not empty pattern list
        /*
        // remove the background layers
        // end of removing the background layer
        */

        best_match_idx = -1;
        best_match_bg_dist = 999.0f;

        // find the best match
        for (a = 0; a < (int)lbp_num; a++) {
          // get the current index for lbp pattern
          cur_lbp_idx = lbp_idxes[a];

          // get the current LBP pointer
          curLBP = &(LBPs[cur_lbp_idx]);

          // compute the background probability based on lbp pattern
          bg_pattern_dist = 0.0f;
          if (m_fTextureWeight > 0)
            bg_pattern_dist = CalPatternBgDist(cur_pattern, curLBP->bg_pattern);

          // compute the color invariant probability based on RGB color
          bg_color_dist = 0.0f;
          if (m_fColorWeight > 0)
            bg_color_dist = CalColorBgDist(cur_intensity, curLBP->bg_intensity, curLBP->max_intensity, curLBP->min_intensity);

          // compute the joint background probability
          //bg_pattern_color_dist = sqrtf(bg_color_dist*bg_pattern_dist);

          //UpdatePatternColorDistWeights(cur_pattern, curLBP->bg_pattern);

          bg_pattern_color_dist = m_fColorWeight * bg_color_dist + m_fTextureWeight*bg_pattern_dist;
          //bg_pattern_color_dist = MAX(bg_color_dist, bg_pattern_dist);

          //bg_pattern_color_dist = 1.0f - (1.0f-bg_color_dist)*(1.0-bg_pattern_dist);
          //bg_pattern_color_dist = bg_pattern_dist;

          //bg_pattern_color_dist = bg_color_dist;

          if (bg_pattern_color_dist < best_match_bg_dist) {
            best_match_bg_dist = bg_pattern_color_dist;
            best_match_idx = a;
          }
        }

        bg_num = (*PLBP).bg_num;

        // check
        bBackgroundUpdating = ((best_match_bg_dist < m_fPatternColorDistBgUpdatedThreshold));

        // reset the weight of the mode
        if (best_match_idx >= (int)bg_num && LBPs[lbp_idxes[best_match_idx]].max_weight < m_fReliableBackgroundModeWeight) // found not in the background models
          best_match_bg_dist = MAX(best_match_bg_dist, m_fPatternColorDistBgThreshold * 2.5f);

        *bg_dist = best_match_bg_dist;
      }
      if (m_disableLearning) {
        // no creation or update when learning is disabled
      }
      else if (!bBackgroundUpdating) { // no match

        for (a = 0; a < (int)lbp_num; a++) { // decrease the weights
          curLBP = &(LBPs[lbp_idxes[a]]);
          curLBP->weight *= (1.0f - m_fWeightUpdatingLearnRate / (1.0f + m_fWeightUpdatingConstant * curLBP->max_weight));
        }

        if ((int)lbp_num < m_nMaxLBPModeNum) { // add a new pattern
          // find the pattern index for addition
          int add_lbp_idx = 0;
          bool bFound;
          for (a = 0; a < m_nMaxLBPModeNum; a++) {
            bFound = true;
            for (b = 0; b < (int)lbp_num; b++)
              bFound &= (a != lbp_idxes[b]);
            if (bFound) {
              add_lbp_idx = a;
              break;
            }
          }
          curLBP = &(LBPs[add_lbp_idx]);

          curLBP->first_time = m_nCurImgFrameIdx;
          curLBP->last_time = m_nCurImgFrameIdx;
          curLBP->freq = 1;
          curLBP->layer_time = -1;

          (*PLBP).matched_mode_first_time = (float)m_nCurImgFrameIdx;

          for (a = 0; a < m_nLBPLength; a++) {
            curLBP->bg_pattern[a] = (float)cur_pattern[a];
          }

          curLBP->bg_layer_num = 0;
          curLBP->weight = m_fLowInitialModeWeight;
          curLBP->max_weight = m_fLowInitialModeWeight;

          for (a = 0; a < m_nChannel; a++) {
            curLBP->bg_intensity[a] = (float)cur_intensity[a];
            curLBP->min_intensity[a] = (float)cur_intensity[a];
            curLBP->max_intensity[a] = (float)cur_intensity[a];
          }

          lbp_idxes[lbp_num] = add_lbp_idx;

          lbp_num++;
          (*PLBP).num = lbp_num;
        }
        else { // replacing the pattern with the minimal weight
          // find the replaced pattern index
          /*
          int rep_pattern_idx = -1;
          for ( a = m_nLBPLength-1 ; a >= 0 ; a-- ) {
          if ( LBPs[lbp_idxes[a]].bg_layer_num == 0 )
          rep_pattern_idx = lbp_idxes[a];
          }
          if ( rep_pattern_idx < 0 ) {
          rep_pattern_idx = lbp_idxes[m_nMaxLBPModeNum-1];
          for ( a = 0 ; a < m_nLBPLength ; a++ ) {
          if ( LBPs[lbp_idxes[a]].bg_layer_num > LBPs[rep_pattern_idx].bg_layer_num )
          LBPs[lbp_idxes[a]].bg_layer_num--;
          }
          }
          */
          int rep_pattern_idx = lbp_idxes[m_nMaxLBPModeNum - 1];

          curLBP = &(LBPs[rep_pattern_idx]);

          curLBP->first_time = m_nCurImgFrameIdx;
          curLBP->last_time = m_nCurImgFrameIdx;
          curLBP->freq = 1;
          curLBP->layer_time = -1;

          (*PLBP).matched_mode_first_time = (float)m_nCurImgFrameIdx;

          for (a = 0; a < m_nLBPLength; a++) {
            curLBP->bg_pattern[a] = (float)cur_pattern[a];
          }

          curLBP->bg_layer_num = 0;
          curLBP->weight = m_fLowInitialModeWeight;
          curLBP->max_weight = m_fLowInitialModeWeight;

          for (a = 0; a < m_nChannel; a++) {
            curLBP->bg_intensity[a] = (float)cur_intensity[a];
            curLBP->min_intensity[a] = (float)cur_intensity[a];
            curLBP->max_intensity[a] = (float)cur_intensity[a];
          }
        }
      }
      else { // find match
        // updating the background pattern model
        cur_lbp_idx = lbp_idxes[best_match_idx];
        curLBP = &(LBPs[cur_lbp_idx]);

        curLBP->first_time = MAX(MIN(curLBP->first_time, m_nCurImgFrameIdx), 0);
        (*PLBP).matched_mode_first_time = curLBP->first_time;

        curLBP->last_time = m_nCurImgFrameIdx;
        curLBP->freq++;

        if (m_fColorWeight > 0) {
          // update the color information
          UpdateBgPixelColor(cur_intensity, curLBP->bg_intensity);
          // update the MAX and MIN color intensity
          Update_MAX_MIN_Intensity(cur_intensity, curLBP->max_intensity, curLBP->min_intensity);
        }

        // update the texture information
        if (m_fTextureWeight > 0)
          UpdateBgPixelPattern(cur_pattern, curLBP->bg_pattern);


        // increase the weight of the best matched mode
        float increasing_weight_factor = m_fWeightUpdatingLearnRate * (1.0f + m_fWeightUpdatingConstant * curLBP->max_weight);
        curLBP->weight = (1.0f - increasing_weight_factor) * curLBP->weight + increasing_weight_factor; //*expf(-best_match_dist/m_fPatternColorDistBgThreshold);

        // update the maximal weight for the best matched mode
        curLBP->max_weight = MAX(curLBP->weight, curLBP->max_weight);

        // calculate the number of background layer
        if (curLBP->bg_layer_num > 0) {
          bool removed_bg_layers = false;
          if (curLBP->weight > curLBP->max_weight * 0.2f) {
            for (a = 0; a < (int)lbp_num; a++) {
              removed_modes[a] = false;
              if (LBPs[lbp_idxes[a]].bg_layer_num > curLBP->bg_layer_num &&
                LBPs[lbp_idxes[a]].weight < LBPs[lbp_idxes[a]].max_weight * 0.9f) { /* remove layers */
              //LBPs[lbp_idxes[a]].bg_layer_num = 0;
                removed_modes[a] = true;
                removed_bg_layers = true;
              }
            }
          }

          if (removed_bg_layers) {
            RemoveBackgroundLayers(PLBP, removed_modes);
            lbp_num = (*PLBP).num;
          }
        }
        else if (curLBP->max_weight > m_fReliableBackgroundModeWeight && curLBP->bg_layer_num == 0) {
          int max_bg_layer_num = LBPs[lbp_idxes[0]].bg_layer_num;
          for (a = 1; a < (int)lbp_num; a++)
            max_bg_layer_num = MAX(max_bg_layer_num, LBPs[lbp_idxes[a]].bg_layer_num);
          curLBP->bg_layer_num = max_bg_layer_num + 1;
          curLBP->layer_time = m_nCurImgFrameIdx;
        }

        (*PLBP).cur_bg_layer_no = curLBP->bg_layer_num;

        // decrease the weights of non-best matched modes
        for (a = 0; a < (int)lbp_num; a++) {
          if (a != best_match_idx) {
            curLBP = &(LBPs[lbp_idxes[a]]);
            curLBP->weight *= (1.0f - m_fWeightUpdatingLearnRate / (1.0f + m_fWeightUpdatingConstant * curLBP->max_weight));
          }
        }
      }

      // sort the list of modes based on the weights of modes
      if ((int)lbp_num > 1 && !m_disableLearning) {
        float weights[100], tot_weights = 0;
        for (a = 0; a < (int)lbp_num; a++) {
          weights[a] = LBPs[lbp_idxes[a]].weight;
          tot_weights += weights[a];
        }

        // sort weights in the descent order
        QuickSort(weights, lbp_idxes, 0, (int)lbp_num - 1, false);

        // calculate the first potential background modes number, bg_num
        float threshold_weight = m_fBackgroundModelPercent*tot_weights;
        tot_weights = 0;
        for (a = 0; a < (int)lbp_num; a++) {
          tot_weights += LBPs[lbp_idxes[a]].weight;
          if (tot_weights > threshold_weight) {
            bg_num = a + 1;
            break;
          }
        }
        (*PLBP).bg_num = bg_num;
      }

      PLBP++;
      bg_dist++;
    }
  }

  if (bFirstFrame) { // check whether it is the first frame for background modeling
    if (m_pFgMaskImg)
      cvSetZero(m_pFgMaskImg);
    cvSetZero(m_pBgDistImg);
  }
  else {
    // set the image data
    if (roi) {
      cvSetZero(m_pBgDistImg);
      cvSetImageROI(m_pBgDistImg, *roi);
    }
    ODC2.SetImageData(m_pBgDistImg, _bg_dist);

    // do gaussian smooth
    if (m_nPatternDistSmoothNeigHalfSize >= 0)
      cvSmooth(m_pBgDistImg, m_pBgDistImg, CV_GAUSSIAN, (2 * m_nPatternDistSmoothNeigHalfSize + 1), (2 * m_nPatternDistSmoothNeigHalfSize + 1), m_fPatternDistConvGaussianSigma);

    if (roi)
      cvResetImageROI(m_pBgDistImg);
#ifdef LINUX_BILATERAL_FILTER
    // do cross bilateral filter
    fprintf(stderr, "%f %f\n", m_fSigmaS, m_fSigmaR);
    if (m_fSigmaS > 0 && m_fSigmaR > 0) {
      GetFloatEdgeImage(m_ppOrgLBPImgs[0], m_pEdgeImg);
      //ComputeGradientImage(m_ppOrgLBPImgs[0], m_pEdgeImg, true);
      m_cCrossBF.SetNewImages(m_pBgDistImg, m_pEdgeImg);
      m_cCrossBF.FastCrossBF();
      m_cCrossBF.GetFilteredImage(m_pBgDistImg);
    }
#endif

    // get the foreground mask by thresholding
    if (m_pFgMaskImg)
      cvThreshold(m_pBgDistImg, m_pFgMaskImg, m_fPatternColorDistBgThreshold, 255, CV_THRESH_BINARY);

    // get the foreground probability image (uchar)
    if (m_pFgProbImg)
      GetForegroundProbabilityImage(m_pFgProbImg);

    // do post-processing
    //Postprocessing();
  }

  // release memories
  delete[] _mask;
  delete[] _bg_dist;
  delete[] _org_intensity;
}

void CMultiLayerBGS::GetBackgroundImage(IplImage *bk_img) {
  IplImage *bg_img = m_pBgImg;
  uchar *c1;
  float *c2;
  int bg_img_idx;
  int channel;
  int img_length = m_cvImgSize.height * m_cvImgSize.width;
  int yx;

  COpencvDataConversion<uchar, uchar> ODC;
  uchar *org_data = ODC.GetImageData(bg_img);
  c1 = org_data;

  //c1 = (uchar*)(bg_img->imageData);

  PixelLBPStruct* PLBP = m_pPixelLBPs;

  for (yx = 0; yx < img_length; yx++) {
    // the newest background image
    bg_img_idx = (*PLBP).lbp_idxes[0];
    if ((*PLBP).num == 0) {
      for (channel = 0; channel < m_nChannel; channel++)
        *c1++ = 0;
    }
    else {
      c2 = (*PLBP).LBPs[bg_img_idx].bg_intensity;
      for (channel = 0; channel < m_nChannel; channel++)
        *c1++ = cvRound(*c2++);
    }
    PLBP++;
  }

  ODC.SetImageData(bg_img, org_data);
  delete[] org_data;

  cvCopy(m_pBgImg, bk_img);
}

void CMultiLayerBGS::GetForegroundImage(IplImage *fg_img, CvScalar bg_color) {
  if (m_pROI && (m_pROI->width <= 0 || m_pROI->height <= 0))
    return;
  IplImage* org_img;
  IplImage* fg_mask_img; // the Mat pointer of the foreground mask matrices at different levels

  org_img = m_pOrgImg;
  fg_mask_img = m_pFgMaskImg;

  cvSet(fg_img, bg_color);
  if (m_pROI) {
    cvSetImageROI(org_img, *m_pROI);
    cvSetImageROI(fg_img, *m_pROI);
    cvSetImageROI(fg_mask_img, *m_pROI);
    cvCopy(org_img, fg_img, fg_mask_img);
    cvResetImageROI(org_img);
    cvResetImageROI(fg_img);
    cvResetImageROI(fg_mask_img);
  }
  else
    cvCopy(org_img, fg_img, fg_mask_img);
}

void CMultiLayerBGS::GetForegroundMaskImage(IplImage *fg_mask_img) {
  if (m_pROI && (m_pROI->width <= 0 || m_pROI->height <= 0))
    return;

  //cvCopy(m_pFgMaskImg, fg_mask_img);
  if (m_pROI) {
    cvSetImageROI(m_pFgMaskImg, *m_pROI);
    cvSetImageROI(fg_mask_img, *m_pROI);
    cvThreshold(m_pFgMaskImg, fg_mask_img, 0, 255, CV_THRESH_BINARY);
    cvResetImageROI(m_pFgMaskImg);
    cvResetImageROI(fg_mask_img);
  }
  else
    cvThreshold(m_pFgMaskImg, fg_mask_img, 0, 255, CV_THRESH_BINARY);
}

void CMultiLayerBGS::GetForegroundMaskMap(CvMat *fg_mask_mat) {
  COpencvDataConversion<uchar, uchar> ODC;
  ODC.ConvertData(m_pFgMaskImg, fg_mask_mat);
}

void CMultiLayerBGS::GetCurrentBackgroundDistMap(CvMat *bk_dist_map) {
  cvCopy(m_pBgDistImg, bk_dist_map);
}

void CMultiLayerBGS::Initialization(IplImage *first_img, int lbp_level_num, float *radiuses, int *neig_pt_nums) {
  int a;

  m_nLBPLength = 0;
  m_nLBPLevelNum = lbp_level_num;
  for (a = 0; a < lbp_level_num; a++) {
    m_nLBPLength += neig_pt_nums[a];
    m_pLBPRadiuses[a] = radiuses[a];
    m_pLBPMeigPointNums[a] = neig_pt_nums[a];
  }

  m_pFgImg = NULL;
  m_pFgMaskImg = NULL;
  m_pBgDistImg = NULL;
  m_pOrgImg = NULL;
  m_pBgImg = NULL;
  m_ppOrgLBPImgs = NULL;
  m_pFgProbImg = NULL;

  m_cvImgSize = cvSize(first_img->width, first_img->height);

  m_nChannel = first_img->nChannels;

  m_pOrgImg = first_img;

  if (m_bUsedColorLBP && m_bUsedGradImage)
    m_nLBPImgNum = 4;
  else if (m_bUsedColorLBP && !m_bUsedGradImage)
    m_nLBPImgNum = 3;
  else if (!m_bUsedColorLBP && m_bUsedGradImage)
    m_nLBPImgNum = 2;
  else
    m_nLBPImgNum = 1;

  m_nLBPLength *= m_nLBPImgNum;

  m_ppOrgLBPImgs = new IplImage*[m_nLBPImgNum];
  for (a = 0; a < m_nLBPImgNum; a++)
    m_ppOrgLBPImgs[a] = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, 1);

  m_pBgImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, m_nChannel);
  m_pFgImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, m_nChannel);
  m_pEdgeImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_32F, 1);
  m_pBgDistImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_32F, 1);

  ResetAllParameters();

  int img_length = m_cvImgSize.height * m_cvImgSize.width;
  m_pPixelLBPs = new PixelLBPStruct[img_length];
  PixelLBPStruct* PLBP = m_pPixelLBPs;
  int yx;
  for (yx = 0; yx < img_length; yx++) {
    (*PLBP).cur_intensity = new unsigned char[m_nChannel];
    (*PLBP).cur_pattern = new float[m_nLBPLength];
    (*PLBP).LBPs = new LBPStruct[m_nMaxLBPModeNum];
    (*PLBP).lbp_idxes = new unsigned short[m_nMaxLBPModeNum];
    (*PLBP).lbp_idxes[0] = 0;
    (*PLBP).num = 0;
    (*PLBP).cur_bg_layer_no = 0;
    (*PLBP).matched_mode_first_time = 0;
    for (a = 0; a < m_nMaxLBPModeNum; a++) {
      (*PLBP).LBPs[a].bg_intensity = new float[m_nChannel];
      (*PLBP).LBPs[a].max_intensity = new float[m_nChannel];
      (*PLBP).LBPs[a].min_intensity = new float[m_nChannel];
      (*PLBP).LBPs[a].bg_pattern = new float[m_nLBPLength];
      (*PLBP).LBPs[a].first_time = -1;
      (*PLBP).LBPs[a].last_time = -1;
      (*PLBP).LBPs[a].freq = -1;
      (*PLBP).LBPs[a].layer_time = -1;
    }
    PLBP++;
  }

  m_pBkMaskImg = cvCreateImage(m_cvImgSize, IPL_DEPTH_8U, 1);
  cvSet(m_pBkMaskImg, cvScalar(1));

  m_cLBP.Initialization(m_ppOrgLBPImgs, m_nLBPImgNum, lbp_level_num, radiuses, neig_pt_nums, m_fRobustColorOffset);

#ifdef LINUX_BILATERAL_FILTER
  if (m_fSigmaS > 0 && m_fSigmaR > 0)
    m_cCrossBF.Initialization(m_pBgDistImg, m_pBgDistImg, m_fSigmaS, m_fSigmaR);
#endif
}

float CMultiLayerBGS::CalPatternBgDist(float *cur_pattern, float *bg_pattern) {
  float bg_hamming_dist = 0;
  int a;
  for (a = 0; a < m_nLBPLength; a++)
    bg_hamming_dist += fabsf(cur_pattern[a] - bg_pattern[a]) > m_f1_MinLBPBinaryProb;

  bg_hamming_dist /= (float)m_nLBPLength;

  return bg_hamming_dist;
}

float CMultiLayerBGS::CalColorBgDist(uchar *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity) {
  float noised_angle, range_dist, bg_color_dist;

  range_dist = CalColorRangeDist(cur_intensity, bg_intensity, max_intensity, min_intensity, m_fRobustShadowRate, m_fRobustHighlightRate);

  if (range_dist == 1.0f)
    bg_color_dist = range_dist;
  else {
    noised_angle = CalVectorsNoisedAngle(bg_intensity, cur_intensity, MAX(m_fRobustColorOffset, 5.0f), m_nChannel);
    bg_color_dist = (1.0f - expf(-100.0f * noised_angle * noised_angle));
  }

  //float bg_color_dist = (expf(-100.0f*noised_angle*noised_angle)*(1.0f-range_dist);

  //float bg_color_dist =  0.5f*(1.0f-expf(-noised_angle*noised_angle/0.005f)) + 0.5f*range_dist;
  //float bg_color_dist =  MAX((float)(noised_angle>0.08f), range_dist);

  return bg_color_dist;
}

void CMultiLayerBGS::ComputeGradientImage(IplImage *src, IplImage *dst, bool bIsFloat) {
  if (src->nChannels != 1 || dst->nChannels != 1) {
    printf("Input images error for computing gradient images!");
    exit(1);
  }

  int a;
  IplImage* _dX = cvCreateImage(cvSize(dst->width, dst->height), IPL_DEPTH_16S, 1);
  IplImage* _dY = cvCreateImage(cvSize(dst->width, dst->height), IPL_DEPTH_16S, 1);

  int aperture_size = 3;

  cvSobel(src, _dX, 1, 0, aperture_size);
  cvSobel(src, _dY, 0, 1, aperture_size);

  COpencvDataConversion<short, short> ODC1;
  COpencvDataConversion<uchar, uchar> ODC2;
  COpencvDataConversion<float, float> ODC3;

  short* dX_data = ODC1.GetImageData(_dX);
  short* dY_data = ODC1.GetImageData(_dY);

  uchar* dst_u_data = NULL;
  float* dst_f_data = NULL;

  if (bIsFloat)
    dst_f_data = ODC3.GetImageData(dst);
  else
    dst_u_data = ODC2.GetImageData(dst);

  short* dX = dX_data;
  short* dY = dY_data;
  uchar *uSrc = dst_u_data;
  float *fSrc = dst_f_data;

  /*
  short* dX = (short*)(_dX->imageData);
  short* dY = (short*)(_dY->imageData);
  uchar *dSrc = (uchar*)(dst->imageData);
  */

  int length;
  if (src->roi)
    length = dst->width * dst->height;
  else
    length = dst->roi->width * dst->roi->height;
  /*
  int x, y;
  uchar *x_u_data;
  float *x_f_data;
  */

  if (bIsFloat) {
    for (a = 0; a < length; a++) {
      *fSrc = cvSqrt((float)((*dX)*(*dX) + (*dY)*(*dY)) / (32.0f * 255.0f));
      fSrc++;
      dX++;
      dY++;
    }
    ODC3.SetImageData(dst, dst_f_data);
    delete[] dst_f_data;
  }
  else {
    for (a = 0; a < length; a++) {
      *uSrc = cvRound(cvSqrt((float)((*dX)*(*dX) + (*dY)*(*dY)) / 32.0f));
      uSrc++;
      dX++;
      dY++;
    }
    ODC2.SetImageData(dst, dst_u_data);
    delete[] dst_u_data;
  }

  delete[] dX_data;
  delete[] dY_data;

  cvReleaseImage(&_dX);
  cvReleaseImage(&_dY);
}

float CMultiLayerBGS::CalVectorsNoisedAngle(float *bg_color, unsigned char *noised_color, float offset, int length) {
  float org_angle = CalVectorsAngle(bg_color, noised_color, length);
  float norm_color = 0, elem, noised_angle;
  int a;
  for (a = 0; a < length; a++) {
    elem = bg_color[a];
    norm_color += elem*elem;
  }
  norm_color = sqrtf(norm_color);
  if (norm_color == 0)
    noised_angle = PI;
  else {
    float sin_angle = offset / norm_color;
    if (sin_angle < m_fMinNoisedAngleSine)
      noised_angle = m_fMinNoisedAngle;
    else
      //noised_angle = ( sin_angle >= 1 ? PI : asinf(sin_angle));
      noised_angle = (sin_angle >= 1 ? PI : sin_angle);
  }

  float angle = org_angle - noised_angle;
  if (angle < 0)
    angle = 0;
  return angle;

  /*
  float org_angle = CalVectorsAngle(bg_color, noised_color, length);
  float max_norm_color, bg_norm_color = 0, noised_norm_color = 0, elem, noised_angle;
  int a;
  for ( a = 0 ; a <  length ; a++ ) {
  elem = bg_color[a];
  bg_norm_color += elem*elem;
  elem = (float)noised_color[a];
  noised_norm_color += elem*elem;
  }
  max_norm_color = MIN(bg_norm_color, noised_norm_color);
  max_norm_color = sqrtf(max_norm_color);
  if ( max_norm_color == 0 )
  noised_angle = PI;
  else {
  float sin_angle = offset/max_norm_color;
  noised_angle = ( sin_angle >= 1 ? PI : asinf(sin_angle));
  }

  float angle = org_angle-noised_angle;
  if ( angle < 0 )
  angle = 0;
  return angle;
  */
}

float CMultiLayerBGS::CalVectorsAngle(float *c1, unsigned char *c2, int length) {
  float angle;
  float dot2, norm1, norm2, elem1, elem2;

  dot2 = norm1 = norm2 = 0;

  int a;
  for (a = 0; a < length; a++) {
    elem1 = (float)(c1[a]);
    elem2 = (float)(c2[a]);
    dot2 += elem1*elem2;
    norm1 += elem1*elem1;
    norm2 += elem2*elem2;
  }

  //angle = (norm1*norm2==0 ? 0 : acosf(dot2/sqrtf(norm1*norm2)));
  //angle = (norm1 * norm2 == 0 ? 0 : sqrtf(fmax(1.0f - dot2 * dot2 / (norm1 * norm2), 0.f)));
  angle = (norm1 * norm2 == 0 ? 0 : sqrtf(std::max(1.0f - dot2 * dot2 / (norm1 * norm2), 0.f)));

  return angle;
}

float CMultiLayerBGS::CalColorRangeDist(unsigned char *cur_intensity, float *bg_intensity, float *max_intensity, float *min_intensity, float shadow_rate, float highlight_rate) {
  float dist = 0.0f, minI, maxI, bgI, curI;
  int channel;
  //float cdist;


  for (channel = 0; channel < m_nChannel; channel++) {
    bgI = bg_intensity[channel];

    /*
    minI = MIN(MIN(min_intensity[channel], bgI*shadow_rate), min_intensity[channel]-15.0f);
    maxI = MAX(MAX(max_intensity[channel], bgI*highlight_rate), max_intensity[channel]+15.0f);
    */

    minI = MIN(min_intensity[channel], bgI * shadow_rate - 5.0f);
    maxI = MAX(max_intensity[channel], bgI * highlight_rate + 5.0f);

    /*
    if ( rand()/((double)RAND_MAX+1) > 0.999 ) {
    char msg[200];
    sprintf(msg, "%d\t%d\n", min_intensity[channel]<bgI*shadow_rate-5.0f ? 1 : 0, max_intensity[channel]>bgI*highlight_rate+5.0f ? 1 : 0);
    ExportLogMessage(msg);
    }
    */

    /*
    minI = max_intensity[channel]*shadow_rate;
    maxI = MAX(bg_intensity[channel]+m_fRobustColorOffset, MIN(max_intensity[channel]*highlight_rate,min_intensity[channel]/shadow_rate));
    */

    curI = (float)(cur_intensity[channel]);

    /*
    //cdist = fabsf(bgI-curI)/512.0f;
    if ( curI > bgI )
    cdist = fabsf(bgI-curI)/256.0f;
    else
    cdist = fabsf(bgI-curI)/512.0f;

    if ( curI > maxI )
    //cdist += (curI-maxI)/(2.0f*(255.0f-maxI))*0.5f;
    cdist += (1.0f-expf(10.0f*(maxI-curI)/MAX(255.0f-maxI,10.0f)))*0.5f;
    else if ( curI < minI )
    //cdist += (minI-curI)/(2.0f*minI)*0.5f;
    cdist += (1.0f-expf(10.0f*(curI-minI)/MAX(minI,10.0f)))*0.5f;
    //dist += cdist;
    if ( cdist > dist )
    dist = cdist;
    */

    if (curI > maxI || curI < minI) {
      dist = 1.0f;
      break;
    }
  }
  //dist = powf(dist, 1.0f/(float)m_nChannel);
  //dist /= (float)m_nChannel;

  return dist;
}

void CMultiLayerBGS::GetLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, CvScalar empty_color) {
  PixelLBPStruct *PLBP = m_pPixelLBPs;
  LBPStruct* LBPs;
  unsigned short* lbp_idxes;

  int a, b, c;
  int img_length = m_pOrgImg->width * m_pOrgImg->height;

  cvSet(layered_bg_img, empty_color);

  COpencvDataConversion<uchar, uchar> ODC;

  uchar *bg_img_data = ODC.GetImageData(layered_bg_img);
  uchar *_bg_img_data = bg_img_data;
  float *cur_bg_intensity;
  int lbp_num;

  for (a = 0; a < img_length; a++) {
    // get lbp information
    LBPs = (*PLBP).LBPs;
    lbp_idxes = (*PLBP).lbp_idxes;
    lbp_num = (int)((*PLBP).num);
    bool found = false;
    for (b = 0; b < lbp_num; b++) {
      if (LBPs[lbp_idxes[b]].bg_layer_num == layered_no) {
        cur_bg_intensity = LBPs[lbp_idxes[b]].bg_intensity;
        for (c = 0; c < m_pOrgImg->nChannels; c++)
          *_bg_img_data++ = (uchar)* cur_bg_intensity++;
        found = true;
        break;
      }
    }
    if (!found)
      _bg_img_data += m_pOrgImg->nChannels;

    PLBP++;
  }

  ODC.SetImageData(layered_bg_img, bg_img_data);

  delete[] bg_img_data;
}

void CMultiLayerBGS::GetBgLayerNoImage(IplImage *bg_layer_no_img, CvScalar *layer_colors, int layer_num) {
  if (layer_num != 0 && layer_num != m_nMaxLBPModeNum) {
    printf("Must be set in %d layers in function GetBgLayerNoImage!\n", m_nMaxLBPModeNum);
    exit(1);
  }

  CvScalar *bg_layer_colors;
  int bg_layer_color_num = layer_num;
  if (bg_layer_color_num == 0)
    bg_layer_color_num = m_nMaxLBPModeNum;
  bg_layer_colors = new CvScalar[bg_layer_color_num];
  if (layer_colors) {
    for (int l = 0; l < layer_num; l++)
      bg_layer_colors[l] = layer_colors[l];
  }
  else {
    int rgb[3];
    rgb[0] = rgb[1] = rgb[2] = 0;
    int rgb_idx = 0;
    for (int l = 0; l < bg_layer_color_num; l++) {
      bg_layer_colors[l] = CV_RGB(rgb[0], rgb[1], rgb[2]);
      rgb[rgb_idx] += 200;
      rgb[rgb_idx] %= 255;
      rgb_idx++;
      rgb_idx %= 3;
    }
  }

  int img_length = m_pOrgImg->width * m_pOrgImg->height;
  uchar *bg_layer_data = new uchar[img_length * bg_layer_no_img->nChannels];
  uchar *_bg_layer_data = bg_layer_data;

  PixelLBPStruct *PLBP = m_pPixelLBPs;
  unsigned int cur_bg_layer_no;

  for (int a = 0; a < img_length; a++) {
    cur_bg_layer_no = (*PLBP).cur_bg_layer_no;
    for (int b = 0; b < bg_layer_no_img->nChannels; b++) {
      *_bg_layer_data++ = (uchar)(bg_layer_colors[cur_bg_layer_no].val[b]);
    }
    PLBP++;
  }

  COpencvDataConversion<uchar, uchar> ODC;
  ODC.SetImageData(bg_layer_no_img, bg_layer_data);

  delete[] bg_layer_data;
  delete[] bg_layer_colors;
}

void CMultiLayerBGS::GetCurrentLayeredBackgroundImage(int layered_no, IplImage *layered_bg_img, IplImage *layered_fg_img, CvScalar layered_bg_bk_color, CvScalar layered_fg_color,
  int smooth_win, float smooth_sigma, float below_layer_noise, float above_layer_noise, int min_blob_size) {
  PixelLBPStruct *PLBP = m_pPixelLBPs;
  LBPStruct* LBPs;
  unsigned short* lbp_idxes;

  int a;
  int img_length = m_pOrgImg->width * m_pOrgImg->height;

  float *bg_layer_mask = new float[img_length];
  float *_bg_layer_mask = bg_layer_mask;

  for (a = 0; a < img_length; a++) {
    // get lbp information
    LBPs = (*PLBP).LBPs;
    lbp_idxes = (*PLBP).lbp_idxes;
    *_bg_layer_mask++ = (float)(*PLBP).cur_bg_layer_no;
    PLBP++;
  }

  COpencvDataConversion<float, float> ODC;
  IplImage* bg_layer_float_mask_img = cvCreateImage(cvSize(m_pOrgImg->width, m_pOrgImg->height), IPL_DEPTH_32F, 1);
  IplImage* bg_layer_low_mask_img = cvCreateImage(cvSize(m_pOrgImg->width, m_pOrgImg->height), IPL_DEPTH_8U, 1);
  IplImage* bg_layer_high_mask_img = cvCreateImage(cvSize(m_pOrgImg->width, m_pOrgImg->height), IPL_DEPTH_8U, 1);
  IplImage* bg_layer_mask_img = cvCreateImage(cvSize(m_pOrgImg->width, m_pOrgImg->height), IPL_DEPTH_8U, 1);

  ODC.SetImageData(bg_layer_float_mask_img, bg_layer_mask);

  /* method 1 using smooth */
  /*
  cvSmooth(bg_layer_float_mask_img, bg_layer_float_mask_img, CV_GAUSSIAN, smooth_win, smooth_win, smooth_sigma);

  cvThreshold(bg_layer_float_mask_img, bg_layer_low_mask_img, (float)layered_no-below_layer_noise, 1, CV_THRESH_BINARY);
  cvThreshold(bg_layer_float_mask_img, bg_layer_high_mask_img, (float)layered_no+above_layer_noise, 1, CV_THRESH_BINARY_INV);

  cvAnd(bg_layer_low_mask_img, bg_layer_high_mask_img, bg_layer_mask_img);
  */

  /* method 2 using dilate, erode, blob removing */
  cvSmooth(bg_layer_float_mask_img, bg_layer_float_mask_img, CV_GAUSSIAN, smooth_win, smooth_win, smooth_sigma);

  cvThreshold(bg_layer_float_mask_img, bg_layer_low_mask_img, (float)layered_no - below_layer_noise, 1, CV_THRESH_BINARY);
  cvThreshold(bg_layer_float_mask_img, bg_layer_high_mask_img, (float)layered_no + above_layer_noise, 1, CV_THRESH_BINARY_INV);
  cvAnd(bg_layer_low_mask_img, bg_layer_high_mask_img, bg_layer_mask_img);

  cvDilate(bg_layer_mask_img, bg_layer_mask_img, 0, 2);
  cvErode(bg_layer_mask_img, bg_layer_mask_img, 0, 2);

  //cvMorphologyEx(bg_layer_mask_img, bg_layer_mask_img, NULL, 0, CV_MOP_OPEN|CV_MOP_CLOSE, 1);

  // Extract the blobs using a threshold of 100 in the image
  CBlobResult blobs = CBlobResult(bg_layer_mask_img, NULL, 0, true);
  // discard the blobs with less area than 100 pixels
  // ( the criteria to filter can be any class derived from COperadorBlob )
  blobs.Filter(blobs, B_INCLUDE, CBlobGetArea(), B_GREATER, min_blob_size);

  CBlob filtered_blob;
  cvSetZero(bg_layer_mask_img);
  for (a = 0; a < blobs.GetNumBlobs(); a++) {
    filtered_blob = blobs.GetBlob(a);
    filtered_blob.FillBlob(bg_layer_mask_img, cvScalar(1));
  }
  blobs.GetNthBlob(CBlobGetArea(), 0, filtered_blob);
  filtered_blob.FillBlob(bg_layer_mask_img, cvScalar(0));


  cvSet(layered_bg_img, layered_bg_bk_color);
  cvCopy(m_pBgImg, layered_bg_img, bg_layer_mask_img);

  if (layered_fg_img) {
    cvCopy(m_pOrgImg, layered_fg_img);
    cvSet(layered_fg_img, layered_fg_color, bg_layer_mask_img);
  }

  cvReleaseImage(&bg_layer_float_mask_img);
  cvReleaseImage(&bg_layer_low_mask_img);
  cvReleaseImage(&bg_layer_high_mask_img);
  cvReleaseImage(&bg_layer_mask_img);
  delete[] bg_layer_mask;
}

void CMultiLayerBGS::GetColoredBgMultiLayeredImage(IplImage *bg_multi_layer_img, CvScalar *layer_colors) {
  cvCopy(m_pOrgImg, bg_multi_layer_img);

  COpencvDataConversion<uchar, uchar> ODC;

  uchar *bg_ml_imgD = ODC.GetImageData(bg_multi_layer_img);
  uchar *fg_maskD = ODC.GetImageData(m_pFgMaskImg);

  uchar *_bg_ml_imgD = bg_ml_imgD;
  uchar *_fg_maskD = fg_maskD;

  PixelLBPStruct *PLBP = m_pPixelLBPs;
  LBPStruct* LBPs;
  unsigned short* lbp_idxes;
  unsigned int lbp_num;
  int bg_layer_num;

  int a, c;
  int img_length = m_pOrgImg->width * m_pOrgImg->height;
  int channels = m_pOrgImg->nChannels;
  bool bLayeredBg;

  for (a = 0; a < img_length; a++) {
    // get lbp information
    lbp_num = (*PLBP).num;
    LBPs = (*PLBP).LBPs;
    lbp_idxes = (*PLBP).lbp_idxes;
    bLayeredBg = false;

    if (*_fg_maskD == 0) {
      bg_layer_num = LBPs[lbp_idxes[0]].bg_layer_num;
      int first_layer_idx = 0;
      for (c = 0; c < (int)lbp_num; c++) {
        if (LBPs[lbp_idxes[c]].bg_layer_num == 1) {
          first_layer_idx = c;
          break;
        }
      }
      if (bg_layer_num > 1 && DistLBP(&(LBPs[lbp_idxes[0]]), &(LBPs[first_layer_idx])) > 0.1f) {
        for (c = 0; c < channels; c++)
          *_bg_ml_imgD++ = (uchar)(layer_colors[bg_layer_num].val[c]);
        bLayeredBg = true;
      }

      if (!bLayeredBg)
        _bg_ml_imgD += channels;
    }
    else {
      _bg_ml_imgD += channels;
    }

    PLBP++;
    _fg_maskD++;
  }

  ODC.SetImageData(bg_multi_layer_img, bg_ml_imgD);

  delete[] fg_maskD;
  delete[] bg_ml_imgD;
}

void CMultiLayerBGS::GetForegroundProbabilityImage(IplImage *fg_dist_img) {
  COpencvDataConversion<float, float> ODC1;
  COpencvDataConversion<uchar, uchar> ODC2;

  float *_fg_distD = ODC1.GetImageData(m_pBgDistImg);
  uchar *_fg_progI = ODC2.GetImageData(fg_dist_img);
  float *fg_distD = _fg_distD;
  uchar *fg_progI = _fg_progI;

  /*
  float *fg_distD = (float*)(m_pBgDistImg->imageData);
  uchar *fg_progI = (uchar*)(fg_dist_img->imageData);
  */

  int channels = fg_dist_img->nChannels;

  int a, b;
  int img_length = fg_dist_img->width * fg_dist_img->height;
  uchar temp;
  for (a = 0; a < img_length; a++) {
    temp = cvRound(255.0f * ((*fg_distD++)));
    for (b = 0; b < channels; b++)
      *fg_progI++ = temp;
  }

  ODC2.SetImageData(fg_dist_img, _fg_progI);

  delete[] _fg_distD;
  delete[] _fg_progI;
}

void CMultiLayerBGS::RemoveBackgroundLayers(PixelLBPStruct *PLBP, bool *removed_modes) {
  int a, b;
  int lbp_num = PLBP->num;

  /*
  if ( lbp_num < m_nMaxLBPModeNum )
  return;
  */

  /* testing */

  unsigned short* lbp_idxes = PLBP->lbp_idxes;
  if (!removed_modes) {
    int removed_bg_layer_num = 0;
    for (a = 0; a < lbp_num; a++) {
      if (PLBP->LBPs[lbp_idxes[a]].bg_layer_num && PLBP->LBPs[lbp_idxes[a]].weight < m_fMinBgLayerWeight) { // should be removed
        removed_bg_layer_num = PLBP->LBPs[lbp_idxes[a]].bg_layer_num;
        lbp_num--;
        for (b = a; b < lbp_num; b++)
          lbp_idxes[b] = lbp_idxes[b + 1];
        break;
      }
    }
    if (removed_bg_layer_num) {
      for (a = 0; a < lbp_num; a++) {
        if (PLBP->LBPs[lbp_idxes[a]].bg_layer_num > removed_bg_layer_num)
          PLBP->LBPs[lbp_idxes[a]].bg_layer_num--;
      }
    }
  }
  else {
    int removed_bg_layer_nums[10];
    int removed_layer_num = 0;
    for (a = 0; a < lbp_num; a++) {
      if (removed_modes[a] && PLBP->LBPs[lbp_idxes[a]].bg_layer_num) { // should be removed
        removed_bg_layer_nums[removed_layer_num++] = PLBP->LBPs[lbp_idxes[a]].bg_layer_num;
      }
    }

    for (a = 0; a < lbp_num; a++) {
      if (removed_modes[a]) { // should be removed
        lbp_num--;
        for (b = a; b < lbp_num; b++)
          lbp_idxes[b] = lbp_idxes[b + 1];
      }
    }

    for (a = 0; a < lbp_num; a++) {
      for (b = 0; b < removed_layer_num; b++) {
        if (PLBP->LBPs[lbp_idxes[a]].bg_layer_num > removed_bg_layer_nums[b])
          PLBP->LBPs[lbp_idxes[a]].bg_layer_num--;
      }
    }
  }

  // sort the list of modes based on the weights of modes
  if (lbp_num != (int)PLBP->num) {
    float weights[100], tot_weights = 0;
    for (a = 0; a < (int)lbp_num; a++) {
      weights[a] = PLBP->LBPs[lbp_idxes[a]].weight;
      tot_weights += weights[a];
    }

    // sort weights in the descent order
    QuickSort(weights, lbp_idxes, 0, (int)lbp_num - 1, false);

    // calculate the first potential background modes number, bg_num
    float threshold_weight = m_fBackgroundModelPercent*tot_weights;
    int bg_num = 0;
    tot_weights = 0;
    for (a = 0; a < (int)lbp_num; a++) {
      tot_weights += PLBP->LBPs[lbp_idxes[a]].weight;
      if (tot_weights > threshold_weight) {
        bg_num = a + 1;
        break;
      }
    }
    (*PLBP).bg_num = bg_num;

  }

  PLBP->num = lbp_num;

  float bg_layer_data[10];
  unsigned short bg_layer_idxes[10];
  int bg_layer_num;
  int tot_bg_layer_num = 0;
  for (a = 0; a < lbp_num; a++) {
    bg_layer_num = PLBP->LBPs[lbp_idxes[a]].bg_layer_num;
    if (bg_layer_num) {
      bg_layer_data[tot_bg_layer_num] = (float)bg_layer_num;
      bg_layer_idxes[tot_bg_layer_num++] = lbp_idxes[a];
    }
  }
  if (tot_bg_layer_num == 1) {
    PLBP->LBPs[bg_layer_idxes[0]].bg_layer_num = 1;
  }
  else if (tot_bg_layer_num) {
    // sort weights in the descent order
    QuickSort(bg_layer_data, bg_layer_idxes, 0, tot_bg_layer_num - 1, true);
    for (a = 0; a < tot_bg_layer_num; a++)
      PLBP->LBPs[bg_layer_idxes[a]].bg_layer_num = a + 1;
  }

  /*
  int max_bg_layer_num = 0;
  for ( a = 0 ; a < lbp_num ; a++ )
  max_bg_layer_num = MAX(max_bg_layer_num, PLBP->LBPs[lbp_idxes[a]].bg_layer_num);
  if ( max_bg_layer_num >= 2 ) {
  bool find_first_layer = false;
  for ( a = 0 ; a < lbp_num ; a++ ) {
  max_bg_layer_num = MAX(max_bg_layer_num, PLBP->LBPs[lbp_idxes[a]].bg_layer_num);
  if ( PLBP->LBPs[lbp_idxes[a]].bg_layer_num == 1 ) {
  find_first_layer = true;
  break;
  }
  }
  if ( !find_first_layer ) {
  printf("\n===============================================\n");
  printf(" have second layer, no first layer \n");
  printf("\n===============================================\n");
  exit(1);
  }
  }
  */
}

void CMultiLayerBGS::Postprocessing() {
  // post-processing for background subtraction results
  cvDilate(m_pFgMaskImg, m_pFgMaskImg, 0, 2);
  cvErode(m_pFgMaskImg, m_pFgMaskImg, 0, 2);

  /** Example of extracting and filtering the blobs of an image */

  // object that will contain blobs of inputImage
  CBlobResult blobs;

  IplImage *inputImage = m_pFgMaskImg;

  // Extract the blobs using a threshold of 100 in the image
  blobs = CBlobResult(inputImage, NULL, 0, true);

  // create a file with some of the extracted features
  //blobs.PrintBlobs( ".\\blobs.txt" );

  // discard the blobs with less area than 100 pixels
  // ( the criteria to filter can be any class derived from COperadorBlob )
  blobs.Filter(blobs, B_INCLUDE, CBlobGetArea(), B_GREATER, 100);

  // create a file with filtered results
  //blobs.PrintBlobs( ".\\filteredBlobs.txt" );

  // build an output image equal to the input but with 3 channels (to draw the coloured blobs)
  IplImage *outputImage;
  outputImage = cvCreateImage(cvSize(inputImage->width, inputImage->height), IPL_DEPTH_8U, 1);
  cvSet(outputImage, cvScalar(0));

  // plot the selected blobs in a output image
  CBlob filtered_blob;
  //cvSet(outputImage, CV_RGB(0,0,255));
  int a;
  for (a = 0; a < blobs.GetNumBlobs(); a++) {
    filtered_blob = blobs.GetBlob(a);
    filtered_blob.FillBlob(outputImage, cvScalar(255));
  }
  blobs.GetNthBlob(CBlobGetArea(), 0, filtered_blob);
  filtered_blob.FillBlob(outputImage, cvScalar(0));

  /*
  char *win_name = "blob filtered image";
  cvNamedWindow(win_name);
  cvShowImage(win_name, outputImage);
  cvWaitKey(3);
  */

  cvReleaseImage(&outputImage);
}

void CMultiLayerBGS::GetFloatEdgeImage(IplImage *src, IplImage *dst) {
  if (src->nChannels > 1) {
    printf("Error: the input source image must be single-channel image!\n");
    exit(1);
  }
  if (dst->depth != IPL_DEPTH_32F) {
    printf("Error: the output edge image must be float image ranging in [0,1]!\n");
    exit(1);
  }

  uchar *src_x_data;
  float *dst_x_data;

  int x, y;
  for (y = 0; y < dst->height; y++) {
    src_x_data = (uchar*)(src->imageData + src->widthStep * y);
    dst_x_data = (float*)(dst->imageData + dst->widthStep * y);
    for (x = 0; x < dst->width; x++) {
      *dst_x_data++ = (float)(*src_x_data++) / 255.0f;
    }
  }
}

void CMultiLayerBGS::ExportLogMessage(char *msg) {
  const char *log_fn = "log_message.txt";
  std::ofstream fout(log_fn, std::ios::app);
  if (fout.fail()) {
    printf("Error opening log output file %s.\n", log_fn);
    fout.close();
    exit(0);
  }

  fout << msg;
  fout.close();
}

void CMultiLayerBGS::UpdatePatternColorDistWeights(float *cur_pattern, float *bg_pattern) {
  return;

  int cur_true_num = 0, cur_false_num = 0, bg_true_num = 0, bg_false_num = 0;
  int a;

  for (a = 0; a < m_nLBPLength; a++) {
    cur_true_num += (cur_pattern[a] > 0.5f ? 1 : 0);
    cur_false_num += (cur_pattern[a] < 0.5f ? 0 : 1);
    bg_true_num += (bg_pattern[a] > 0.5f);
    bg_false_num += (bg_pattern[a] < 0.5f);
  }
  m_fTextureWeight = expf(-(std::abs(cur_true_num - cur_false_num) + std::abs(bg_true_num - bg_false_num) + 0.8f) / (float)m_nLBPLength);
  m_fTextureWeight = MAX(MIN(m_fTextureWeight, 0.5f), 0.1f);
  m_fColorWeight = 1.0f - m_fTextureWeight;
}

void CMultiLayerBGS::Save(const char *bg_model_fn) {
  Save(bg_model_fn, 2);
}

void CMultiLayerBGS::Save(const char *bg_model_fn, int save_type) {
  FILE * fout = fopen(bg_model_fn, "w");
  if (!fout) {
    printf("Error opening background model output file %s.\n", bg_model_fn);
    fclose(fout);
    //exit(0);
    return;
  }

  int i, j;
  if (save_type == 0) { /* save the background model information */
    fprintf(fout, "FILE_TYPE:  MODEL_INFO\n\n");

    fprintf(fout, "MAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum);
    fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength);
    fprintf(fout, "CHANNELS_NUM: %5d\n", m_nChannel);
    fprintf(fout, "IMAGE_SIZE: %5d %5d\n\n", m_cvImgSize.width, m_cvImgSize.height);

    fprintf(fout, "MODEL_INFO_PIXEL_BY_PIXEL:\n");

    int img_length = m_cvImgSize.height * m_cvImgSize.width;
    PixelLBPStruct* PLBP = m_pPixelLBPs;

    for (int yx = 0; yx < img_length; yx++) {
      fprintf(fout, "%3d %3d %3d", (*PLBP).num, (*PLBP).bg_num, (*PLBP).cur_bg_layer_no);
      for (i = 0; i < (int)(*PLBP).num; i++)
        fprintf(fout, " %3d", (*PLBP).lbp_idxes[i]);
      for (i = 0; i < (int)(*PLBP).num; i++) {
        int li = (*PLBP).lbp_idxes[i];
        for (j = 0; j < m_nChannel; j++) {
          fprintf(fout, " %7.1f %7.1f %7.1f", (*PLBP).LBPs[li].bg_intensity[j],
            (*PLBP).LBPs[li].max_intensity[j], (*PLBP).LBPs[li].min_intensity[j]);
        }
        for (j = 0; j < m_nLBPLength; j++)
          fprintf(fout, " %7.3f", (*PLBP).LBPs[li].bg_pattern[j]);
        fprintf(fout, " %10.5f", (*PLBP).LBPs[li].weight);
        fprintf(fout, " %10.5f", (*PLBP).LBPs[li].max_weight);
        fprintf(fout, " %3d", (*PLBP).LBPs[li].bg_layer_num);
        fprintf(fout, " %20lu", (*PLBP).LBPs[li].first_time);
        fprintf(fout, " %20lu", (*PLBP).LBPs[li].last_time);
        fprintf(fout, " %8d", (*PLBP).LBPs[li].freq);
      }
      fprintf(fout, "\n");
      PLBP++;
    }
  }
  else if (save_type == 1) { /* save current parameters for background subtraction */
    fprintf(fout, "FILE_TYPE:  MODEL_PARAS\n\n");

    fprintf(fout, "MAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum);
    fprintf(fout, "FRAME_DURATION: %f\n", m_fFrameDuration);
    fprintf(fout, "MODEL_UPDATING_LEARN_RATE: %f\n", m_fModeUpdatingLearnRate);
    fprintf(fout, "WEIGHT_UPDATING_LEARN_RATE: %f\n", m_fWeightUpdatingLearnRate);
    fprintf(fout, "WEIGHT_UPDATING_CONSTANT: %f\n", m_fWeightUpdatingConstant);
    fprintf(fout, "LOW_INITIAL_MODE_WEIGHT: %f\n", m_fLowInitialModeWeight);
    fprintf(fout, "RELIABLE_BACKGROUND_MODE_WEIGHT: %f\n", m_fReliableBackgroundModeWeight);
    fprintf(fout, "ROBUST_COLOR_OFFSET: %f\n", m_fRobustColorOffset);
    fprintf(fout, "BACKGROUND_MODEL_PERCENT: %f\n", m_fBackgroundModelPercent);
    fprintf(fout, "ROBUST_SHADOW_RATE: %f\n", m_fRobustShadowRate);
    fprintf(fout, "ROBUST_HIGHLIGHT_RATE: %f\n", m_fRobustHighlightRate);
    fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD: %f\n", m_fPatternColorDistBgThreshold);
    fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_UPDATED_THRESHOLD: %f\n", m_fPatternColorDistBgUpdatedThreshold);
    fprintf(fout, "MIN_BACKGROUND_LAYER_WEIGHT: %f\n", m_fMinBgLayerWeight);
    fprintf(fout, "PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE: %d\n", m_nPatternDistSmoothNeigHalfSize);
    fprintf(fout, "PATTERN_DIST_CONV_GAUSSIAN_SIGMA: %f\n", m_fPatternDistConvGaussianSigma);
    fprintf(fout, "TEXTURE_WEIGHT: %f\n", m_fTextureWeight);
    fprintf(fout, "MIN_NOISED_ANGLE: %f\n", m_fMinNoisedAngle);
    fprintf(fout, "MIN_NOISED_ANGLE_SINE: %f\n", m_fMinNoisedAngleSine);
    fprintf(fout, "BILATERAL_SIGMA_S: %f\n", m_fSigmaS);
    fprintf(fout, "BILATERAL_SIGMA_R: %f\n", m_fSigmaR);
    fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength);
    fprintf(fout, "LBP_LEVEL_NUM: %5d\n", m_nLBPLevelNum);
    fprintf(fout, "LBP_RADIUSES: ");
    for (i = 0; i < m_nLBPLevelNum; i++)
      fprintf(fout, "%10.5f", m_pLBPRadiuses[i]);
    fprintf(fout, "\nLBP_NEIG_POINT_NUMS: ");
    for (i = 0; i < m_nLBPLevelNum; i++)
      fprintf(fout, "%6d", m_pLBPMeigPointNums[i]);
  }
  else if (save_type == 2) { /* save the background model information and parameters */
    fprintf(fout, "FILE_TYPE:  MODEL_PARAS_INFO\n\n");

    fprintf(fout, "MAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum);
    fprintf(fout, "FRAME_DURATION: %f\n", m_fFrameDuration);
    fprintf(fout, "MODEL_UPDATING_LEARN_RATE: %f\n", m_fModeUpdatingLearnRate);
    fprintf(fout, "WEIGHT_UPDATING_LEARN_RATE: %f\n", m_fWeightUpdatingLearnRate);
    fprintf(fout, "WEIGHT_UPDATING_CONSTANT: %f\n", m_fWeightUpdatingConstant);
    fprintf(fout, "LOW_INITIAL_MODE_WEIGHT: %f\n", m_fLowInitialModeWeight);
    fprintf(fout, "RELIABLE_BACKGROUND_MODE_WEIGHT: %f\n", m_fReliableBackgroundModeWeight);
    fprintf(fout, "ROBUST_COLOR_OFFSET: %f\n", m_fRobustColorOffset);
    fprintf(fout, "BACKGROUND_MODEL_PERCENT: %f\n", m_fBackgroundModelPercent);
    fprintf(fout, "ROBUST_SHADOW_RATE: %f\n", m_fRobustShadowRate);
    fprintf(fout, "ROBUST_HIGHLIGHT_RATE: %f\n", m_fRobustHighlightRate);
    fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_THRESHOLD: %f\n", m_fPatternColorDistBgThreshold);
    fprintf(fout, "PATTERN_COLOR_DIST_BACKGROUND_UPDATED_THRESHOLD: %f\n", m_fPatternColorDistBgUpdatedThreshold);
    fprintf(fout, "MIN_BACKGROUND_LAYER_WEIGHT: %f\n", m_fMinBgLayerWeight);
    fprintf(fout, "PATTERN_DIST_SMOOTH_NEIG_HALF_SIZE: %d\n", m_nPatternDistSmoothNeigHalfSize);
    fprintf(fout, "PATTERN_DIST_CONV_GAUSSIAN_SIGMA: %f\n", m_fPatternDistConvGaussianSigma);
    fprintf(fout, "TEXTURE_WEIGHT: %f\n", m_fTextureWeight);
    fprintf(fout, "MIN_NOISED_ANGLE: %f\n", m_fMinNoisedAngle);
    fprintf(fout, "MIN_NOISED_ANGLE_SINE: %f\n", m_fMinNoisedAngleSine);
    fprintf(fout, "BILATERAL_SIGMA_S: %f\n", m_fSigmaS);
    fprintf(fout, "BILATERAL_SIGMA_R: %f\n", m_fSigmaR);
    fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength);
    fprintf(fout, "LBP_LEVEL_NUM: %5d\n", m_nLBPLevelNum);
    fprintf(fout, "LBP_RADIUSES: ");
    for (i = 0; i < m_nLBPLevelNum; i++)
      fprintf(fout, "%10.5f", m_pLBPRadiuses[i]);
    fprintf(fout, "\nLBP_NEIG_POINT_NUMS: ");
    for (i = 0; i < m_nLBPLevelNum; i++)
      fprintf(fout, "%6d", m_pLBPMeigPointNums[i]);

    fprintf(fout, "\nMAX_MODEL_NUM: %5d\n", m_nMaxLBPModeNum);
    fprintf(fout, "LBP_LENGTH: %5d\n", m_nLBPLength);
    fprintf(fout, "CHANNELS_NUM: %5d\n", m_nChannel);
    fprintf(fout, "IMAGE_SIZE: %5d %5d\n\n", m_cvImgSize.width, m_cvImgSize.height);

    fprintf(fout, "MODEL_INFO_PIXEL_BY_PIXEL:\n");

    int img_length = m_cvImgSize.height * m_cvImgSize.width;
    PixelLBPStruct* PLBP = m_pPixelLBPs;

    for (int yx = 0; yx < img_length; yx++) {
      fprintf(fout, "%3d %3d %3d", (*PLBP).num, (*PLBP).bg_num, (*PLBP).cur_bg_layer_no);
      for (i = 0; i < (int)(*PLBP).num; i++)
        fprintf(fout, " %3d", (*PLBP).lbp_idxes[i]);
      for (i = 0; i < (int)(*PLBP).num; i++) {
        int li = (*PLBP).lbp_idxes[i];
        for (j = 0; j < m_nChannel; j++) {
          fprintf(fout, " %7.1f %7.1f %7.1f", (*PLBP).LBPs[li].bg_intensity[j],
            (*PLBP).LBPs[li].max_intensity[j], (*PLBP).LBPs[li].min_intensity[j]);
        }
        for (j = 0; j < m_nLBPLength; j++)
          fprintf(fout, " %7.3f", (*PLBP).LBPs[li].bg_pattern[j]);
        fprintf(fout, " %10.5f", (*PLBP).LBPs[li].weight);
        fprintf(fout, " %10.5f", (*PLBP).LBPs[li].max_weight);
        fprintf(fout, " %3d", (*PLBP).LBPs[li].bg_layer_num);
        fprintf(fout, " %20lu", (*PLBP).LBPs[li].first_time);
        fprintf(fout, " %20lu", (*PLBP).LBPs[li].last_time);
        fprintf(fout, " %8d", (*PLBP).LBPs[li].freq);
      }
      fprintf(fout, "\n");
      PLBP++;
    }
  }
  else { /* wrong save type */
    printf("Please input correct save type: 0 - model_info  1 - model_paras  2 - model_paras_info\n");
    fclose(fout);
    exit(0);
  }

  fclose(fout);
}

bool CMultiLayerBGS::Load(const char *bg_model_fn) {
  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();
    return false;
  }

  char para_name[1024], model_type[1024];

  fin >> para_name >> model_type;

  int i, j;
  CvSize img_size;
  int img_length = m_cvImgSize.width * m_cvImgSize.height;
  int max_lbp_mode_num = m_nMaxLBPModeNum;

  if (!strcmp(model_type, "MODEL_INFO")) {
    fin >> para_name >> m_nMaxLBPModeNum;
    fin >> para_name >> m_nLBPLength;
    fin >> para_name >> m_nChannel;
    fin >> para_name >> img_size.width >> img_size.height;

    if (m_cvImgSize.width != img_size.width || m_cvImgSize.height != img_size.height) {
      printf("Image size is not matched!\n");
      return false;
    }

    if (max_lbp_mode_num != m_nMaxLBPModeNum) {
      PixelLBPStruct* PLBP = m_pPixelLBPs;
      for (int yx = 0; yx < img_length; yx++) {
        delete[](*PLBP).LBPs;
        delete[](*PLBP).lbp_idxes;
        (*PLBP).LBPs = new LBPStruct[m_nMaxLBPModeNum];
        (*PLBP).lbp_idxes = new unsigned short[m_nMaxLBPModeNum];
      }
    }

    fin >> para_name;

    int img_length = m_cvImgSize.height * m_cvImgSize.width;
    PixelLBPStruct* PLBP = m_pPixelLBPs;

    for (int yx = 0; yx < img_length; yx++) {
      fin >> (*PLBP).num >> (*PLBP).bg_num >> (*PLBP).cur_bg_layer_no;
      for (i = 0; i < (int)(*PLBP).num; i++)
        fin >> (*PLBP).lbp_idxes[i];
      for (i = 0; i < (int)(*PLBP).num; i++) {
        int li = (*PLBP).lbp_idxes[i];
        for (j = 0; j < m_nChannel; j++) {
          fin >> (*PLBP).LBPs[li].bg_intensity[j] >>
            (*PLBP).LBPs[li].max_intensity[j] >> (*PLBP).LBPs[li].min_intensity[j];
        }
        for (j = 0; j < m_nLBPLength; j++)
          fin >> (*PLBP).LBPs[li].bg_pattern[j];
        fin >> (*PLBP).LBPs[li].weight >> (*PLBP).LBPs[li].max_weight >> (*PLBP).LBPs[li].bg_layer_num
          >> (*PLBP).LBPs[li].first_time >> (*PLBP).LBPs[li].last_time >> (*PLBP).LBPs[li].freq;
      }
      PLBP++;
    }
  }
  else if (!strcmp(model_type, "MODEL_PARAS")) {
    fin >> para_name >> m_nMaxLBPModeNum;
    fin >> para_name >> m_fFrameDuration;
    fin >> para_name >> m_fModeUpdatingLearnRate;
    fin >> para_name >> m_fWeightUpdatingLearnRate;
    fin >> para_name >> m_fWeightUpdatingConstant;
    fin >> para_name >> m_fLowInitialModeWeight;
    fin >> para_name >> m_fReliableBackgroundModeWeight;
    fin >> para_name >> m_fRobustColorOffset;
    fin >> para_name >> m_fBackgroundModelPercent;
    fin >> para_name >> m_fRobustShadowRate;
    fin >> para_name >> m_fRobustHighlightRate;
    fin >> para_name >> m_fPatternColorDistBgThreshold;
    fin >> para_name >> m_fPatternColorDistBgUpdatedThreshold;
    fin >> para_name >> m_fMinBgLayerWeight;
    fin >> para_name >> m_nPatternDistSmoothNeigHalfSize;
    fin >> para_name >> m_fPatternDistConvGaussianSigma;
    fin >> para_name >> m_fTextureWeight;
    fin >> para_name >> m_fMinNoisedAngle;
    fin >> para_name >> m_fMinNoisedAngleSine;
    fin >> para_name >> m_fSigmaS;
    fin >> para_name >> m_fSigmaR;
    fin >> para_name >> m_nLBPLength;
    fin >> para_name >> m_nLBPLevelNum;
    fin >> para_name;
    for (i = 0; i < m_nLBPLevelNum; i++)
      fin >> m_pLBPRadiuses[i];
    fin >> para_name;
    for (i = 0; i < m_nLBPLevelNum; i++)
      fin >> m_pLBPMeigPointNums[i];
  }
  else if (!strcmp(model_type, "MODEL_PARAS_INFO")) {
    fin >> para_name >> m_nMaxLBPModeNum;
    fin >> para_name >> m_fFrameDuration;
    fin >> para_name >> m_fModeUpdatingLearnRate;
    fin >> para_name >> m_fWeightUpdatingLearnRate;
    fin >> para_name >> m_fWeightUpdatingConstant;
    fin >> para_name >> m_fLowInitialModeWeight;
    fin >> para_name >> m_fReliableBackgroundModeWeight;
    fin >> para_name >> m_fRobustColorOffset;
    fin >> para_name >> m_fBackgroundModelPercent;
    fin >> para_name >> m_fRobustShadowRate;
    fin >> para_name >> m_fRobustHighlightRate;
    fin >> para_name >> m_fPatternColorDistBgThreshold;
    fin >> para_name >> m_fPatternColorDistBgUpdatedThreshold;
    fin >> para_name >> m_fMinBgLayerWeight;
    fin >> para_name >> m_nPatternDistSmoothNeigHalfSize;
    fin >> para_name >> m_fPatternDistConvGaussianSigma;
    fin >> para_name >> m_fTextureWeight;
    fin >> para_name >> m_fMinNoisedAngle;
    fin >> para_name >> m_fMinNoisedAngleSine;
    fin >> para_name >> m_fSigmaS;
    fin >> para_name >> m_fSigmaR;
    fin >> para_name >> m_nLBPLength;
    fin >> para_name >> m_nLBPLevelNum;
    fin >> para_name;
    for (i = 0; i < m_nLBPLevelNum; i++)
      fin >> m_pLBPRadiuses[i];
    fin >> para_name;
    for (i = 0; i < m_nLBPLevelNum; i++)
      fin >> m_pLBPMeigPointNums[i];

    fin >> para_name >> m_nMaxLBPModeNum;
    fin >> para_name >> m_nLBPLength;
    fin >> para_name >> m_nChannel;
    fin >> para_name >> img_size.width >> img_size.height;

    if (m_cvImgSize.width != img_size.width || m_cvImgSize.height != img_size.height) {
      printf("Image size is not matched!\n");
      return false;
    }

    if (max_lbp_mode_num != m_nMaxLBPModeNum) {
      PixelLBPStruct* PLBP = m_pPixelLBPs;
      for (int yx = 0; yx < img_length; yx++) {
        delete[](*PLBP).LBPs;
        delete[](*PLBP).lbp_idxes;
        (*PLBP).LBPs = new LBPStruct[m_nMaxLBPModeNum];
        (*PLBP).lbp_idxes = new unsigned short[m_nMaxLBPModeNum];
      }
    }

    fin >> para_name;

    int img_length = m_cvImgSize.height * m_cvImgSize.width;
    PixelLBPStruct* PLBP = m_pPixelLBPs;

    for (int yx = 0; yx < img_length; yx++) {
      fin >> (*PLBP).num >> (*PLBP).bg_num >> (*PLBP).cur_bg_layer_no;
      for (i = 0; i < (int)(*PLBP).num; i++)
        fin >> (*PLBP).lbp_idxes[i];
      for (i = 0; i < (int)(*PLBP).num; i++) {
        int li = (*PLBP).lbp_idxes[i];
        for (j = 0; j < m_nChannel; j++) {
          fin >> (*PLBP).LBPs[li].bg_intensity[j] >>
            (*PLBP).LBPs[li].max_intensity[j] >> (*PLBP).LBPs[li].min_intensity[j];
        }
        for (j = 0; j < m_nLBPLength; j++)
          fin >> (*PLBP).LBPs[li].bg_pattern[j];
        fin >> (*PLBP).LBPs[li].weight >> (*PLBP).LBPs[li].max_weight >> (*PLBP).LBPs[li].bg_layer_num
          >> (*PLBP).LBPs[li].first_time >> (*PLBP).LBPs[li].last_time >> (*PLBP).LBPs[li].freq;
      }
      PLBP++;
    }
  }
  else {
    printf("Not correct model save type!\n");
    fin.close();
    exit(0);
  }

  fin.close();

  ResetAllParameters();

  return true;
}

void CMultiLayerBGS::SetValidPointMask(IplImage *maskImage, int mode) {
  if (mode == 1)
    SetBkMaskImage(maskImage);
  else
    cvAnd(m_pBkMaskImg, maskImage, m_pBkMaskImg);
}

void CMultiLayerBGS::SetFrameRate(float frameDuration) {
  m_fModeUpdatingLearnRate = m_fModeUpdatingLearnRatePerSecond*frameDuration;
  m_fWeightUpdatingLearnRate = m_fWeightUpdatingLearnRatePerSecond*frameDuration;

  m_fFrameDuration = frameDuration;

  m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate;
  m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate;
}

void CMultiLayerBGS::Init(int width, int height) {
  IplImage* first_img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3);
  int lbp_level_num = 1;
  float radiuses[] = { 2.0f };
  int neig_pt_nums[] = { 6 };
  Initialization(first_img, lbp_level_num, radiuses, neig_pt_nums);
  cvReleaseImage(&first_img);
}

int CMultiLayerBGS::SetRGBInputImage(IplImage *inputImage, CvRect *roi) {
  if (!inputImage) {
    printf("Please allocate the IplImage memory!\n");
    return 0;
  }
  if (inputImage->width != m_cvImgSize.width ||
    inputImage->height != m_cvImgSize.height ||
    inputImage->depth != IPL_DEPTH_8U ||
    inputImage->nChannels != 3) {
    printf("Please provide the correct IplImage pointer, \ne.g. inputImage = cvCreateImage(imgSize, IPL_DEPTH_8U, 3);\n");
    return 0;
  }
  SetNewImage(inputImage, roi);
  return 1;
}

void CMultiLayerBGS::SetParameters(int max_lbp_mode_num, float mode_updating_learn_rate_per_second, float weight_updating_learn_rate_per_second, float low_init_mode_weight) {
  m_nMaxLBPModeNum = max_lbp_mode_num;
  m_fModeUpdatingLearnRate = mode_updating_learn_rate_per_second*m_fFrameDuration;
  m_fWeightUpdatingLearnRate = weight_updating_learn_rate_per_second*m_fFrameDuration;
  m_fLowInitialModeWeight = low_init_mode_weight;

  m_fModeUpdatingLearnRatePerSecond = mode_updating_learn_rate_per_second;
  m_fWeightUpdatingLearnRatePerSecond = weight_updating_learn_rate_per_second;

  m_f1_ModeUpdatingLearnRate = 1.0f - m_fModeUpdatingLearnRate;
  m_f1_WeightUpdatingLearnRate = 1.0f - m_fWeightUpdatingLearnRate;
}

int CMultiLayerBGS::Process() {
  BackgroundSubtractionProcess();
  return 1;
}

int CMultiLayerBGS::SetForegroundMaskImage(IplImage* fg_mask_img) {
  if (!fg_mask_img) {
    printf("Please allocate the IplImage memory!\n");
    return 0;
  }
  if (fg_mask_img->width != m_cvImgSize.width ||
    fg_mask_img->height != m_cvImgSize.height ||
    fg_mask_img->depth != IPL_DEPTH_8U ||
    fg_mask_img->nChannels != 1) {
    printf("Please provide the correct IplImage pointer, \ne.g. fg_mask_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1);\n");
    return 0;
  }

  m_pFgMaskImg = fg_mask_img;

  return 1;
}

int CMultiLayerBGS::SetForegroundProbImage(IplImage* fg_prob_img) {
  if (!fg_prob_img) {
    printf("Please allocate the IplImage memory!\n");
    return 0;
  }
  if (fg_prob_img->width != m_cvImgSize.width ||
    fg_prob_img->height != m_cvImgSize.height ||
    fg_prob_img->depth != IPL_DEPTH_8U) {
    printf("Please provide the correct IplImage pointer, \ne.g. fg_prob_img = cvCreateImage(imgSize, IPL_DEPTH_8U, 1);\n");
    return 0;
  }

  m_pFgProbImg = fg_prob_img;

  return 1;
}

void CMultiLayerBGS::SetCurrentFrameNumber(unsigned long cur_frame_no) {
  m_nCurImgFrameIdx = cur_frame_no;
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/CMultiLayerBGS.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/LocalBinaryPattern.cpp --#--
#include "LocalBinaryPattern.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

using namespace bgslibrary::algorithms::multilayer;

CLocalBinaryPattern::CLocalBinaryPattern() {
  m_ppOrgImgs = NULL;
  m_pRadiuses = NULL;
  m_fRobustWhiteNoise = 3.0f;
  m_pNeigPointsNums = NULL;
  m_pXYShifts = NULL;
  m_pShiftedImg = NULL;
}

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 = cvSize(first_imgs[a]->width, first_imgs[a]->height);
    if (first_imgs[a]->nChannels > 1) {
      printf("Input image channel must be 1!");
      exit(1);
    }
  }

  int tot_neig_pts_num = 0;
  for (a = 0; a < m_nLBPLevelNum; a++) {
    m_pRadiuses[a] = radius[a];
    m_pNeigPointsNums[a] = neig_pt_num[a];
    tot_neig_pts_num += neig_pt_num[a];
    if (m_pNeigPointsNums[a] % 2 != 0) {
      printf("Even number must be given for points number for LBP!\n");
      exit(1);
    }
  }

  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;
  for (a = 0; a < m_nLBPLevelNum; a++)
    for (b = 0; b < m_pNeigPointsNums[a]; b++) {
      // compute the offset of neig point
      CalNeigPixelOffset(m_pRadiuses[a], m_pNeigPointsNums[a], b, m_pXYShifts[shift_idx].x, m_pXYShifts[shift_idx].y);
      m_nMaxShift.x = MAX(m_nMaxShift.x, m_pXYShifts[shift_idx].x);
      m_nMaxShift.y = MAX(m_nMaxShift.y, m_pXYShifts[shift_idx].y);
      shift_idx++;
    }

  m_fRobustWhiteNoise = robust_white_noise;
}

void CLocalBinaryPattern::SetNewImages(IplImage **new_imgs)
{
  m_ppOrgImgs = new_imgs;
}

void CLocalBinaryPattern::ComputeLBP(PixelLBPStruct *PLBP, CvRect *roi)
{
  float *dif_pattern;
  float *_dif_pattern;
  PixelLBPStruct *_PLBP;
  int data_length;
  float *cur_pattern;

  // allocate memories
  if (roi)
    data_length = roi->height*roi->width;
  else
    data_length = m_cvImgSize.width*m_cvImgSize.height;

  dif_pattern = new float[data_length];

  int img_idx, pt_idx, yx, level;
  int pattern_idx = 0;
  for (img_idx = 0; img_idx < m_nImgsNum; img_idx++) {
    for (level = 0; level < m_nLBPLevelNum; level++) {
      for (pt_idx = 0; pt_idx < m_pNeigPointsNums[level]; pt_idx++) {

        // computing the shifted image
        CalShiftedImage(m_ppOrgImgs[img_idx], m_pXYShifts[pattern_idx].x, m_pXYShifts[pattern_idx].y, m_pShiftedImg, roi);

        // computing the different binary images
        CalImageDifferenceMap(m_ppOrgImgs[img_idx], m_pShiftedImg, dif_pattern, roi);

        // set the binary values
        _PLBP = PLBP;
        _dif_pattern = dif_pattern;

        if (roi) {
          int x, y;
          for (y = 0; y < roi->height; y++) {
            _PLBP = PLBP + (y + roi->y)*m_cvImgSize.width + roi->x;
            for (x = 0; x < roi->width; x++) {
              cur_pattern = (*_PLBP++).cur_pattern;
              cur_pattern[pattern_idx] = *_dif_pattern++;
            }
          }
        }
        else {
          for (yx = 0; yx < data_length; yx++) {
            cur_pattern = (*_PLBP++).cur_pattern;
            cur_pattern[pattern_idx] = *_dif_pattern++;
          }
        }

        pattern_idx++;

      }
    }
  }

  // release memories
  delete[] dif_pattern;

  //delete [] shifted_dif_pattern;
  //cvReleaseImage(&shifted_img);
  //cvReleaseImage(&pattern_img);
}

void CLocalBinaryPattern::FreeMemories()
{
  delete[] m_pRadiuses;
  delete[] m_pNeigPointsNums;
  delete[] m_pXYShifts;
  cvReleaseImage(&m_pShiftedImg);

  m_pXYShifts = NULL;
  m_pRadiuses = NULL;
  m_pNeigPointsNums = NULL;
  m_pShiftedImg = NULL;
}

void CLocalBinaryPattern::SetShiftedMeshGrid(CvSize img_size, float offset_x, float offset_y, CvMat *grid_map_x, CvMat *grid_map_y)
{
  float *gX = (float*)(grid_map_x->data.ptr);
  float *gY = (float*)(grid_map_y->data.ptr);

  int x, y;
  for (y = 0; y < img_size.height; y++) {
    for (x = 0; x < img_size.width; x++) {
      *gX++ = (float)x + offset_x;
      *gY++ = (float)y + offset_y;
    }
  }
}

void CLocalBinaryPattern::CalShiftedImage(IplImage *src, int offset_x, int offset_y, IplImage *dst, CvRect *roi)
{
  CvRect src_roi, dst_roi;
  int roi_width, roi_height;

  if (roi) {
    src_roi.x = MAX(offset_x + roi->x, 0);
    src_roi.y = MAX(offset_y + roi->y, 0);

    dst_roi.x = MAX(-(offset_x + roi->x), roi->x);
    dst_roi.y = MAX(-(offset_y + roi->y), roi->y);

    roi_width = MIN(MIN(roi->width + (int)fabsf((float)offset_x), src->width - src_roi.x), dst->width - dst_roi.x);
    roi_height = MIN(MIN(roi->height + (int)fabsf((float)offset_y), src->height - src_roi.y), dst->height - dst_roi.y);

    src_roi.width = roi_width;
    src_roi.height = roi_height;

    dst_roi.width = roi_width;
    dst_roi.height = roi_height;
  }
  else {
    roi_width = src->width - (int)fabsf((float)offset_x);
    roi_height = src->height - (int)fabsf((float)offset_y);

    src_roi.x = MAX(offset_x, 0);
    src_roi.y = MAX(offset_y, 0);
    src_roi.width = roi_width;
    src_roi.height = roi_height;

    dst_roi.x = MAX(-offset_x, 0);
    dst_roi.y = MAX(-offset_y, 0);
    dst_roi.width = roi_width;
    dst_roi.height = roi_height;
  }

  cvSet(dst, cvScalar(0));

  if (roi_width <= 0 || roi_height <= 0)
    return;

  cvSetImageROI(src, src_roi);
  cvSetImageROI(dst, dst_roi);
  cvCopy(src, dst);
  cvResetImageROI(src);
  cvResetImageROI(dst);
}

void CLocalBinaryPattern::CalNeigPixelOffset(float radius, int tot_neig_pts_num, int neig_pt_idx, int &offset_x, int &offset_y)
{
  float angle = (float)neig_pt_idx / (float)tot_neig_pts_num*2.0f*PI;
  offset_x = cvRound(radius*cosf(angle));
  offset_y = cvRound(-radius*sinf(angle));
}

void CLocalBinaryPattern::CalImageDifferenceMap(IplImage *cent_img, IplImage *neig_img, float *pattern, CvRect *roi)
{
  COpencvDataConversion<uchar, uchar> ODC;

  if (roi) {
    cvSetImageROI(cent_img, *roi);
    cvSetImageROI(neig_img, *roi);
  }

  uchar *_centI = ODC.GetImageData(cent_img);
  uchar *_neigI = ODC.GetImageData(neig_img);
  uchar *centI = _centI;
  uchar *neigI = _neigI;

  float *tmp_pattern = pattern;

  int xy;
  int length;

  if (roi)
    length = roi->height*roi->width;
  else
    length = cent_img->height*cent_img->width;

  for (xy = 0; xy < length; xy++) {
    *tmp_pattern = (float)BINARY_PATTERM_ELEM(*neigI, *centI, m_fRobustWhiteNoise);
    tmp_pattern++;
    centI++;
    neigI++;
  }

  if (roi) {
    cvResetImageROI(cent_img);
    cvResetImageROI(neig_img);
  }

  // release memories
  delete[] _centI;
  delete[] _neigI;

}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/LocalBinaryPattern.cpp --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BlobResult.cpp --#--
#include <limits.h>
#include <stdio.h>
#include <functional>
#include <algorithm>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "BlobResult.h"
#include "BlobExtraction.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace multilayer
    {
      namespace blob
      {
        /**
          - 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()
        {
          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)
          {
            // 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Ó: 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);

          // reservem memòria per als nous blobs
          resultat.m_blobs.resize(resultat.GetNumBlobs() + source.GetNumBlobs());

          // 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;
        }

        /**************************************************************************
          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));
        }


        /**
          - 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();
          }

          // 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;
        }

        /**
          - 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]);
        }

        /////////////////////////// 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*/)

        {
          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);
          }
        }


        /**
          - 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());

          // 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++;
          }
        }

        /**
          - 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);

        }
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/BlobResult.cpp --#--

--#-- START ./bgslibrary/algorithms/ViBe/vibe-background-sequential.cpp --#--
#include <assert.h>
#include <time.h>

#include "vibe-background-sequential.h"

//using namespace bgslibrary::algorithms::vibe;
namespace bgslibrary
{
  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);
      }

      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)
      {
        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

        /* 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);
      }

      // ----------------------------------------------------------------------------
      // 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];
          }
        }

        /* 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];
        }

        /* 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];
        }

        /* 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];
        }

        /* 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];
        }

        /* 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);
      }

      // ----------------------------------------------------------------------------
      // -------------------------- 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];
        }

        assert(model->historyImage != NULL);

        /* 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);

        auto plus_noise = [](uint8_t value) -> int {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; }
          return value_plus_noise;};
        for(int index = (3 * width) * height - 1; index >= 0; index -= 3) {
          uint8_t value_1 = image_data[index];
          uint8_t value_2 = image_data[index - 1];
          uint8_t value_3 = image_data[index - 2];
          for(int x = 0; x < model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES; ++x) {
            int value_plus_noise1 = plus_noise(value_1);
            int value_plus_noise2 = plus_noise(value_2);
            int value_plus_noise3 = plus_noise(value_3);
            model->historyBuffer[(index - 2) * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x * 3 + 2] = value_plus_noise1;
            model->historyBuffer[(index - 2) * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x * 3 + 1] = value_plus_noise2;
            model->historyBuffer[(index - 2) * (model->numberOfSamples - NUMBER_OF_HISTORY_IMAGES) + x * 3] = value_plus_noise3;
          }
        }

        /* 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 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;
        }

        /* 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];
          }
        }

        // 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);
      }

      // ----------------------------------------------------------------------------
      // 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];
          }
        }

        /* 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];
        }

        /* 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];
        }

        /* 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];
        }

        /* 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;
            }
          }
        }

        return(0);
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/ViBe/vibe-background-sequential.cpp --#--

--#-- START ./bgslibrary/algorithms/LBFuzzyAdaptiveSOM.cpp --#--
#include "LBFuzzyAdaptiveSOM.h"

using namespace bgslibrary::algorithms;

LBFuzzyAdaptiveSOM::LBFuzzyAdaptiveSOM() :
  IBGS(quote(LBFuzzyAdaptiveSOM)),
  sensitivity(90), trainingSensitivity(240), learningRate(38), 
  trainingLearningRate(255), trainingSteps(81)
{
  debug_construction(LBFuzzyAdaptiveSOM);
  initLoadSaveConfig(algorithmName);
}

LBFuzzyAdaptiveSOM::~LBFuzzyAdaptiveSOM() {
  debug_destruction(LBFuzzyAdaptiveSOM);
  delete m_pBGModel;
}

void LBFuzzyAdaptiveSOM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  IplImage* frame = cvCloneImage(&_frame);

  if (firstTime) {
    int w = img_input.size().width;
    int h = img_input.size().height;

    m_pBGModel = new lb::BGModelFuzzySom(w, h);
    m_pBGModel->InitModel(frame);
  }

  m_pBGModel->setBGModelParameter(0, sensitivity);
  m_pBGModel->setBGModelParameter(1, trainingSensitivity);
  m_pBGModel->setBGModelParameter(2, learningRate);
  m_pBGModel->setBGModelParameter(3, trainingLearningRate);
  m_pBGModel->setBGModelParameter(5, trainingSteps);

  m_pBGModel->UpdateModel(frame);

  img_foreground = cv::cvarrToMat(m_pBGModel->GetFG());
  img_background = cv::cvarrToMat(m_pBGModel->GetBG());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&frame);

  firstTime = false;
}

void LBFuzzyAdaptiveSOM::save_config(cv::FileStorage &fs) {
  fs << "sensitivity" << sensitivity;
  fs << "trainingSensitivity" << trainingSensitivity;
  fs << "learningRate" << learningRate;
  fs << "trainingLearningRate" << trainingLearningRate;
  fs << "trainingSteps" << trainingSteps;
  fs << "showOutput" << showOutput;
}

void LBFuzzyAdaptiveSOM::load_config(cv::FileStorage &fs) {
  fs["sensitivity"] >> sensitivity;
  fs["trainingSensitivity"] >> trainingSensitivity;
  fs["learningRate"] >> learningRate;
  fs["trainingLearningRate"] >> trainingLearningRate;
  fs["trainingSteps"] >> trainingSteps;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/LBFuzzyAdaptiveSOM.cpp --#--

--#-- START ./bgslibrary/algorithms/StaticFrameDifference.cpp --#--
#include "StaticFrameDifference.h"

using namespace bgslibrary::algorithms;

StaticFrameDifference::StaticFrameDifference() :
  IBGS(quote(StaticFrameDifference)),
  enableThreshold(true), threshold(15)
{
  debug_construction(StaticFrameDifference);
  initLoadSaveConfig(algorithmName);
}

StaticFrameDifference::~StaticFrameDifference() {
  debug_destruction(StaticFrameDifference);
}

void StaticFrameDifference::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_background.empty())
    img_input.copyTo(img_background);

  cv::absdiff(img_input, img_background, img_foreground);

  if (img_foreground.channels() == 3)
    cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY);

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void StaticFrameDifference::save_config(cv::FileStorage &fs) {
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void StaticFrameDifference::load_config(cv::FileStorage &fs) {
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/StaticFrameDifference.cpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF.cpp --#--
#include "LBP_MRF.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

using namespace bgslibrary::algorithms;

LBP_MRF::LBP_MRF() :
  IBGS(quote(LBP_MRF)),
  Detector(nullptr)
{
  debug_construction(LBP_MRF);
  initLoadSaveConfig(algorithmName);
  Detector = new lbp_mrf::MotionDetection();
  Detector->SetMode(lbp_mrf::MotionDetection::md_LBPHistograms);
}

LBP_MRF::~LBP_MRF() {
  debug_destruction(LBP_MRF);
  delete Detector;
  Detector = nullptr;
}

void LBP_MRF::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage TempImage(img_input);
  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);

  Detector->DetectMotions(InputImage);
  Detector->GetMotionsMask(OutputImage);
  img_foreground = cv::cvarrToMat((IplImage*)OutputImage.GetIplImage());
  //bitwise_not(img_foreground, img_background);
  img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void LBP_MRF::save_config(cv::FileStorage &fs) {
  fs << "showOutput" << showOutput;
}

void LBP_MRF::load_config(cv::FileStorage &fs) {
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/LBSP.cpp --#--
#include "LBSP.h"

//using namespace bgslibrary::algorithms::lbsp;

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(float fRelThreshold, size_t nThresholdOffset)
        : m_bOnlyUsingAbsThreshold(false)
        , m_fRelThreshold(fRelThreshold)
        , m_nThreshold(nThresholdOffset)
        , m_oRefImage() {
        CV_Assert(m_fRelThreshold >= 0);
      }

      LBSP::~LBSP() {}

      void LBSP::read(const cv::FileNode& /*fn*/) {
        // ... = fn["..."];
      }

      void LBSP::write(cv::FileStorage& /*fs*/) const {
        //fs << "..." << ...;
      }

      void LBSP::setReference(const cv::Mat& img) {
        CV_DbgAssert(img.empty() || img.type() == CV_8UC1 || img.type() == CV_8UC3);
        m_oRefImage = img;
      }

      int LBSP::descriptorSize() const {
        return DESC_SIZE;
      }

      int LBSP::descriptorType() const {
        return CV_16U;
      }

      bool LBSP::isUsingRelThreshold() const {
        return !m_bOnlyUsingAbsThreshold;
      }

      float LBSP::getRelThreshold() const {
        return m_fRelThreshold;
      }

      size_t LBSP::getAbsThreshold() const {
        return m_nThreshold;
      }

      static inline void lbsp_computeImpl(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        size_t _t) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create((int)nKeyPoints, 1, CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>((int)k);
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create((int)nKeyPoints, 1, CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * k));
#include "LBSP_16bits_dbcross_3ch1t.i"
          }
        }
      }

      static inline void lbsp_computeImpl(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        float fThreshold,
        size_t nThresholdOffset) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(fThreshold >= 0);
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create((int)nKeyPoints, 1, CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>((int)k);
            const size_t _t = (size_t)(_ref*fThreshold) + nThresholdOffset;
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create((int)nKeyPoints, 1, CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * k));
            const size_t _t[3] = { (size_t)(_ref[0] * fThreshold) + nThresholdOffset,(size_t)(_ref[1] * fThreshold) + nThresholdOffset,(size_t)(_ref[2] * fThreshold) + nThresholdOffset };
#include "LBSP_16bits_dbcross_3ch3t.i"
          }
        }
      }

      static inline void lbsp_computeImpl2(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        size_t _t) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create(oInputImg.size(), CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>(_y, _x);
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create(oInputImg.size(), CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * _y + oDesc.step.p[1] * _x));
#include "LBSP_16bits_dbcross_3ch1t.i"
          }
        }
      }

      static inline void lbsp_computeImpl2(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        float fThreshold,
        size_t nThresholdOffset) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(fThreshold >= 0);
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create(oInputImg.size(), CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>(_y, _x);
            const size_t _t = (size_t)(_ref*fThreshold) + nThresholdOffset;
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create(oInputImg.size(), CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * _y + oDesc.step.p[1] * _x));
            const size_t _t[3] = { (size_t)(_ref[0] * fThreshold) + nThresholdOffset,(size_t)(_ref[1] * fThreshold) + nThresholdOffset,(size_t)(_ref[2] * fThreshold) + nThresholdOffset };
#include "LBSP_16bits_dbcross_3ch3t.i"
          }
        }
      }

      void LBSP::compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
        CV_Assert(!oImage.empty());
        cv::KeyPointsFilter::runByImageBorder(voKeypoints, oImage.size(), PATCH_SIZE / 2);
        cv::KeyPointsFilter::runByKeypointSize(voKeypoints, std::numeric_limits<float>::epsilon());
        if (voKeypoints.empty()) {
          oDescriptors.release();
          return;
        }
        if (m_bOnlyUsingAbsThreshold)
          lbsp_computeImpl2(oImage, m_oRefImage, voKeypoints, oDescriptors, m_nThreshold);
        else
          lbsp_computeImpl2(oImage, m_oRefImage, voKeypoints, oDescriptors, m_fRelThreshold, m_nThreshold);
      }

      void LBSP::compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const {
        CV_Assert(voImageCollection.size() == vvoPointCollection.size());
        voDescCollection.resize(voImageCollection.size());
        for (size_t i = 0; i < voImageCollection.size(); i++)
          compute2(voImageCollection[i], vvoPointCollection[i], voDescCollection[i]);
      }

      void LBSP::computeImpl(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
        CV_Assert(!oImage.empty());
        cv::KeyPointsFilter::runByImageBorder(voKeypoints, oImage.size(), PATCH_SIZE / 2);
        cv::KeyPointsFilter::runByKeypointSize(voKeypoints, std::numeric_limits<float>::epsilon());
        if (voKeypoints.empty()) {
          oDescriptors.release();
          return;
        }
        if (m_bOnlyUsingAbsThreshold)
          lbsp_computeImpl(oImage, m_oRefImage, voKeypoints, oDescriptors, m_nThreshold);
        else
          lbsp_computeImpl(oImage, m_oRefImage, voKeypoints, oDescriptors, m_fRelThreshold, m_nThreshold);
      }

      void LBSP::reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput) {
        CV_DbgAssert(!voKeypoints.empty());
        CV_DbgAssert(!oDescriptors.empty() && oDescriptors.cols == 1);
        CV_DbgAssert(oSize.width > 0 && oSize.height > 0);
        CV_DbgAssert(DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(oDescriptors.type() == CV_16UC1 || oDescriptors.type() == CV_16UC3);
        const size_t nChannels = (size_t)oDescriptors.channels();
        const size_t nKeyPoints = voKeypoints.size();
        if (nChannels == 1) {
          oOutput.create(oSize, CV_16UC1);
          oOutput = cv::Scalar_<ushort>(0);
          for (size_t k = 0; k < nKeyPoints; ++k)
            oOutput.at<ushort>(voKeypoints[k].pt) = oDescriptors.at<ushort>((int)k);
        }
        else { //nChannels==3
          oOutput.create(oSize, CV_16UC3);
          oOutput = cv::Scalar_<ushort>(0, 0, 0);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            ushort* output_ptr = (ushort*)(oOutput.data + oOutput.step.p[0] * (int)voKeypoints[k].pt.y);
            const ushort* const desc_ptr = (ushort*)(oDescriptors.data + oDescriptors.step.p[0] * k);
            const size_t idx = 3 * (int)voKeypoints[k].pt.x;
            for (size_t n = 0; n < 3; ++n)
              output_ptr[idx + n] = desc_ptr[n];
          }
        }
      }

      void LBSP::calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels) {
        CV_DbgAssert(oDesc1.size() == oDesc2.size() && oDesc1.type() == oDesc2.type());
        CV_DbgAssert(DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(oDesc1.type() == CV_16UC1 || oDesc1.type() == CV_16UC3);
        CV_DbgAssert(CV_MAT_DEPTH(oDesc1.type()) == CV_16U);
        CV_DbgAssert(DESC_SIZE * 8 <= UCHAR_MAX);
        CV_DbgAssert(oDesc1.step.p[0] == oDesc2.step.p[0] && oDesc1.step.p[1] == oDesc2.step.p[1]);
        const float fScaleFactor = (float)UCHAR_MAX / (DESC_SIZE * 8);
        const size_t nChannels = CV_MAT_CN(oDesc1.type());
        const size_t _step_row = oDesc1.step.p[0];
        if (nChannels == 1) {
          oOutput.create(oDesc1.size(), CV_8UC1);
          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;
                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::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;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/LBSP.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorSuBSENSE.cpp --#--
#include <iostream>
#include <iomanip>

#include <opencv2/imgproc/imgproc.hpp>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui.hpp>
#endif

#include "BackgroundSubtractorSuBSENSE.h"
#include "RandUtils.h"

//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)
      const int FRAMELEVEL_ANALYSIS_DOWNSAMPLE_RATIO = 8;

      // 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);
      }

      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;
            }
          }
        }
        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);
      }

      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));
                }
              }
            }
          }
        }
        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));
                  }
                }
              }
            }
          }
        }
      }

      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;
#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);
            }
            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;
            }
          }
        }
        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();
            }
          }
        }
        oAvgBGDesc.convertTo(backgroundDescImage, CV_16U);
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorSuBSENSE.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP.cpp --#--
#include <iostream>
#include <iomanip>
#include <exception>

#include <opencv2/imgproc/imgproc.hpp>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui.hpp>
#endif

#include "BackgroundSubtractorLBSP.h"
#include "DistanceUtils.h"
#include "RandUtils.h"

//using namespace bgslibrary::algorithms::lbsp;

#ifndef SIZE_MAX
# if __WORDSIZE == 64
#  define SIZE_MAX		(18446744073709551615UL)
# else
#  define SIZE_MAX		(4294967295U)
# endif
#endif

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() {}

      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::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;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/LBSP_.cpp --#--
#include "LBSP_.h"

//using namespace bgslibrary::algorithms::lbsp;

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_(float fRelThreshold, size_t nThresholdOffset)
        : m_bOnlyUsingAbsThreshold(false)
        , m_fRelThreshold(fRelThreshold)
        , m_nThreshold(nThresholdOffset)
        , m_oRefImage() {
        CV_Assert(m_fRelThreshold >= 0);
      }

      LBSP_::~LBSP_() {}

      void LBSP_::read(const cv::FileNode& /*fn*/) {
        // ... = fn["..."];
      }

      void LBSP_::write(cv::FileStorage& /*fs*/) const {
        //fs << "..." << ...;
      }

      void LBSP_::setReference(const cv::Mat& img) {
        CV_DbgAssert(img.empty() || img.type() == CV_8UC1 || img.type() == CV_8UC3);
        m_oRefImage = img;
      }

      int LBSP_::descriptorSize() const {
        return DESC_SIZE;
      }

      int LBSP_::descriptorType() const {
        return CV_16U;
      }

      bool LBSP_::isUsingRelThreshold() const {
        return !m_bOnlyUsingAbsThreshold;
      }

      float LBSP_::getRelThreshold() const {
        return m_fRelThreshold;
      }

      size_t LBSP_::getAbsThreshold() const {
        return m_nThreshold;
      }

      static inline void LBSP__computeImpl(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        size_t _t) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create((int)nKeyPoints, 1, CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>((int)k);
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create((int)nKeyPoints, 1, CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * k));
#include "LBSP_16bits_dbcross_3ch1t.i"
          }
        }
      }

      static inline void LBSP__computeImpl(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        float fThreshold,
        size_t nThresholdOffset) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(fThreshold >= 0);
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create((int)nKeyPoints, 1, CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>((int)k);
            const size_t _t = (size_t)(_ref*fThreshold) + nThresholdOffset;
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create((int)nKeyPoints, 1, CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * k));
            const size_t _t[3] = { (size_t)(_ref[0] * fThreshold) + nThresholdOffset,(size_t)(_ref[1] * fThreshold) + nThresholdOffset,(size_t)(_ref[2] * fThreshold) + nThresholdOffset };
#include "LBSP_16bits_dbcross_3ch3t.i"
          }
        }
      }

      static inline void LBSP__computeImpl2(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        size_t _t) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create(oInputImg.size(), CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>(_y, _x);
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create(oInputImg.size(), CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * _y + oDesc.step.p[1] * _x));
#include "LBSP_16bits_dbcross_3ch1t.i"
          }
        }
      }

      static inline void LBSP__computeImpl2(const cv::Mat& oInputImg,
        const cv::Mat& oRefImg,
        const std::vector<cv::KeyPoint>& voKeyPoints,
        cv::Mat& oDesc,
        float fThreshold,
        size_t nThresholdOffset) {
        CV_DbgAssert(oRefImg.empty() || (oRefImg.size == oInputImg.size && oRefImg.type() == oInputImg.type()));
        CV_DbgAssert(oInputImg.type() == CV_8UC1 || oInputImg.type() == CV_8UC3);
        CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(fThreshold >= 0);
        const size_t nChannels = (size_t)oInputImg.channels();
        const size_t _step_row = oInputImg.step.p[0];
        const uchar* _data = oInputImg.data;
        const uchar* _refdata = oRefImg.empty() ? oInputImg.data : oRefImg.data;
        const size_t nKeyPoints = voKeyPoints.size();
        if (nChannels == 1) {
          oDesc.create(oInputImg.size(), CV_16UC1);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar _ref = _refdata[_step_row*(_y)+_x];
            ushort& _res = oDesc.at<ushort>(_y, _x);
            const size_t _t = (size_t)(_ref*fThreshold) + nThresholdOffset;
#include "LBSP_16bits_dbcross_1ch.i"
          }
        }
        else { //nChannels==3
          oDesc.create(oInputImg.size(), CV_16UC3);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            const int _x = (int)voKeyPoints[k].pt.x;
            const int _y = (int)voKeyPoints[k].pt.y;
            const uchar* _ref = _refdata + _step_row*(_y)+3 * (_x);
            ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0] * _y + oDesc.step.p[1] * _x));
            const size_t _t[3] = { (size_t)(_ref[0] * fThreshold) + nThresholdOffset,(size_t)(_ref[1] * fThreshold) + nThresholdOffset,(size_t)(_ref[2] * fThreshold) + nThresholdOffset };
#include "LBSP_16bits_dbcross_3ch3t.i"
          }
        }
      }

      void LBSP_::compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
        CV_Assert(!oImage.empty());
        cv::KeyPointsFilter::runByImageBorder(voKeypoints, oImage.size(), PATCH_SIZE / 2);
        cv::KeyPointsFilter::runByKeypointSize(voKeypoints, std::numeric_limits<float>::epsilon());
        if (voKeypoints.empty()) {
          oDescriptors.release();
          return;
        }
        if (m_bOnlyUsingAbsThreshold)
          LBSP__computeImpl2(oImage, m_oRefImage, voKeypoints, oDescriptors, m_nThreshold);
        else
          LBSP__computeImpl2(oImage, m_oRefImage, voKeypoints, oDescriptors, m_fRelThreshold, m_nThreshold);
      }

      void LBSP_::compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const {
        CV_Assert(voImageCollection.size() == vvoPointCollection.size());
        voDescCollection.resize(voImageCollection.size());
        for (size_t i = 0; i < voImageCollection.size(); i++)
          compute2(voImageCollection[i], vvoPointCollection[i], voDescCollection[i]);
      }

      void LBSP_::computeImpl(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
        CV_Assert(!oImage.empty());
        cv::KeyPointsFilter::runByImageBorder(voKeypoints, oImage.size(), PATCH_SIZE / 2);
        cv::KeyPointsFilter::runByKeypointSize(voKeypoints, std::numeric_limits<float>::epsilon());
        if (voKeypoints.empty()) {
          oDescriptors.release();
          return;
        }
        if (m_bOnlyUsingAbsThreshold)
          LBSP__computeImpl(oImage, m_oRefImage, voKeypoints, oDescriptors, m_nThreshold);
        else
          LBSP__computeImpl(oImage, m_oRefImage, voKeypoints, oDescriptors, m_fRelThreshold, m_nThreshold);
      }

      void LBSP_::reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput) {
        CV_DbgAssert(!voKeypoints.empty());
        CV_DbgAssert(!oDescriptors.empty() && oDescriptors.cols == 1);
        CV_DbgAssert(oSize.width > 0 && oSize.height > 0);
        CV_DbgAssert(DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(oDescriptors.type() == CV_16UC1 || oDescriptors.type() == CV_16UC3);
        const size_t nChannels = (size_t)oDescriptors.channels();
        const size_t nKeyPoints = voKeypoints.size();
        if (nChannels == 1) {
          oOutput.create(oSize, CV_16UC1);
          oOutput = cv::Scalar_<ushort>(0);
          for (size_t k = 0; k < nKeyPoints; ++k)
            oOutput.at<ushort>(voKeypoints[k].pt) = oDescriptors.at<ushort>((int)k);
        }
        else { //nChannels==3
          oOutput.create(oSize, CV_16UC3);
          oOutput = cv::Scalar_<ushort>(0, 0, 0);
          for (size_t k = 0; k < nKeyPoints; ++k) {
            ushort* output_ptr = (ushort*)(oOutput.data + oOutput.step.p[0] * (int)voKeypoints[k].pt.y);
            const ushort* const desc_ptr = (ushort*)(oDescriptors.data + oDescriptors.step.p[0] * k);
            const size_t idx = 3 * (int)voKeypoints[k].pt.x;
            for (size_t n = 0; n < 3; ++n)
              output_ptr[idx + n] = desc_ptr[n];
          }
        }
      }

      void LBSP_::calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels) {
        CV_DbgAssert(oDesc1.size() == oDesc2.size() && oDesc1.type() == oDesc2.type());
        CV_DbgAssert(DESC_SIZE == 2); // @@@ also relies on a constant desc size
        CV_DbgAssert(oDesc1.type() == CV_16UC1 || oDesc1.type() == CV_16UC3);
        CV_DbgAssert(CV_MAT_DEPTH(oDesc1.type()) == CV_16U);
        CV_DbgAssert(DESC_SIZE * 8 <= UCHAR_MAX);
        CV_DbgAssert(oDesc1.step.p[0] == oDesc2.step.p[0] && oDesc1.step.p[1] == oDesc2.step.p[1]);
        const float fScaleFactor = (float)UCHAR_MAX / (DESC_SIZE * 8);
        const size_t nChannels = CV_MAT_CN(oDesc1.type());
        const size_t _step_row = oDesc1.step.p[0];
        if (nChannels == 1) {
          oOutput.create(oDesc1.size(), CV_8UC1);
          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;
                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_::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;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/LBSP_.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorPAWCS.cpp --#--
#include <iostream>
#include <iomanip>

#include <opencv2/imgproc/imgproc.hpp>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui.hpp>
#endif

#include "BackgroundSubtractorPAWCS.h"
#include "RandUtils.h"

//using namespace bgslibrary::algorithms::lbsp;

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;

      // 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)

      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;

      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();
      }

      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;
            }
          }
        }
        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);
      }

      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;
                }
              }
            }
          }
          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;
            }
          }
          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);
                }
              }
              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;
                }
              }
            }
          }
          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;
                  }
                }
              }
            }
            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;
                }
                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];
            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];
                  }
                  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;
            }
            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;
                  }
                }
              }
            }
            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;
              }
            }
            // == 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];
            }
          }
        }
        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;
            }
            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;
            }
            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];
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorPAWCS.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLOBSTER.cpp --#--
#include <iostream>
#include <iomanip>

#include <opencv2/imgproc/imgproc.hpp>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui.hpp>
#endif

#include "BackgroundSubtractorLOBSTER.h"
#include "RandUtils.h"

using namespace bgslibrary::algorithms::lbsp;

BackgroundSubtractorLOBSTER::BackgroundSubtractorLOBSTER(float fRelLBSPThreshold
  , size_t nLBSPThresholdOffset
  , size_t nDescDistThreshold
  , size_t nColorDistThreshold
  , size_t nBGSamples
  , size_t nRequiredBGSamples)
  : BackgroundSubtractorLBSP(fRelLBSPThreshold, nLBSPThresholdOffset)
  , m_nColorDistThreshold(nColorDistThreshold)
  , m_nDescDistThreshold(nDescDistThreshold)
  , m_nBGSamples(nBGSamples)
  , m_nRequiredBGSamples(nRequiredBGSamples) {
  CV_Assert(m_nRequiredBGSamples <= m_nBGSamples);
  m_bAutoModelResetEnabled = false; // @@@@@@ not supported here for now
}

BackgroundSubtractorLOBSTER::~BackgroundSubtractorLOBSTER() {
  if (m_aPxIdxLUT)
    delete[] m_aPxIdxLUT;
  if (m_aPxInfoLUT)
    delete[] m_aPxInfoLUT;
}

void BackgroundSubtractorLOBSTER::initialize(const cv::Mat& oInitImg, const cv::Mat& oROI) {
  CV_Assert(!oInitImg.empty() && oInitImg.cols > 0 && oInitImg.rows > 0);
  CV_Assert(oInitImg.isContinuous());
  CV_Assert(oInitImg.type() == CV_8UC1 || oInitImg.type() == CV_8UC3);
  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 << "\tBackgroundSubtractorLOBSTER : 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();
  }
  LBSP::validateROI(oNewBGROI);
  const size_t nROIPxCount = (size_t)cv::countNonZero(oNewBGROI);
  CV_Assert(nROIPxCount > 0);
  m_oROI = oNewBGROI;
  m_oImgSize = oInitImg.size();
  m_nImgType = oInitImg.type();
  m_nImgChannels = oInitImg.channels();
  m_nTotPxCount = m_oImgSize.area();
  m_nTotRelevantPxCount = nROIPxCount;
  m_nFrameIndex = 0;
  m_nFramesSinceLastReset = 0;
  m_nModelResetCooldown = 0;
  m_oLastFGMask.create(m_oImgSize, CV_8UC1);
  m_oLastFGMask = cv::Scalar_<uchar>(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_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>((t*m_fRelLBSPThreshold + m_nLBSPThresholdOffset) / 2);
    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;
      }
    }
  }
  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>(t*m_fRelLBSPThreshold + m_nLBSPThresholdOffset);
    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);
}

void BackgroundSubtractorLOBSTER::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));
          }
        }
      }
    }
  }
  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));
            }
          }
        }
      }
    }
  }
}

void BackgroundSubtractorLOBSTER::apply(cv::InputArray _image, cv::OutputArray _fgmask, double learningRate) {
  CV_Assert(m_bInitialized);
  CV_Assert(learningRate > 0);
  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();
  oCurrFGMask = cv::Scalar_<uchar>(0);
  const size_t nLearningRate = (size_t)ceil(learningRate);
  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 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 nGoodSamplesCount = 0, nModelIdx = 0;
      ushort nCurrInputDesc;
      while (nGoodSamplesCount < m_nRequiredBGSamples && nModelIdx < m_nBGSamples) {
        const uchar nBGColor = m_voBGColorSamples[nModelIdx].data[nPxIter];
        {
          const size_t nColorDist = L1dist(nCurrColor, nBGColor);
          if (nColorDist > m_nColorDistThreshold / 2)
            goto failedcheck1ch;
          LBSP::computeGrayscaleDescriptor(oInputImg, nBGColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nBGColor], nCurrInputDesc);
          const size_t nDescDist = hdist(nCurrInputDesc, *((ushort*)(m_voBGDescSamples[nModelIdx].data + nDescIter)));
          if (nDescDist > m_nDescDistThreshold)
            goto failedcheck1ch;
          nGoodSamplesCount++;
        }
      failedcheck1ch:
        nModelIdx++;
      }
      if (nGoodSamplesCount < m_nRequiredBGSamples)
        oCurrFGMask.data[nPxIter] = UCHAR_MAX;
      else {
        if ((rand() % nLearningRate) == 0) {
          const size_t nSampleModelIdx = rand() % m_nBGSamples;
          ushort& nRandInputDesc = *((ushort*)(m_voBGDescSamples[nSampleModelIdx].data + nDescIter));
          LBSP::computeGrayscaleDescriptor(oInputImg, nCurrColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nCurrColor], nRandInputDesc);
          m_voBGColorSamples[nSampleModelIdx].data[nPxIter] = nCurrColor;
        }
        if ((rand() % nLearningRate) == 0) {
          int nSampleImgCoord_Y, nSampleImgCoord_X;
          getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize);
          const size_t nSampleModelIdx = rand() % m_nBGSamples;
          ushort& nRandInputDesc = m_voBGDescSamples[nSampleModelIdx].at<ushort>(nSampleImgCoord_Y, nSampleImgCoord_X);
          LBSP::computeGrayscaleDescriptor(oInputImg, nCurrColor, nCurrImgCoord_X, nCurrImgCoord_Y, m_anLBSPThreshold_8bitLUT[nCurrColor], nRandInputDesc);
          m_voBGColorSamples[nSampleModelIdx].at<uchar>(nSampleImgCoord_Y, nSampleImgCoord_X) = nCurrColor;
        }
      }
    }
  }
  else { //m_nImgChannels==3
    const size_t nCurrDescDistThreshold = m_nDescDistThreshold * 3;
    const size_t nCurrColorDistThreshold = m_nColorDistThreshold * 3;
    const size_t nCurrSCDescDistThreshold = nCurrDescDistThreshold / 2;
    const size_t nCurrSCColorDistThreshold = nCurrColorDistThreshold / 2;
    const size_t desc_row_step = m_voBGDescSamples[0].step.p[0];
    const size_t img_row_step = m_voBGColorSamples[0].step.p[0];
    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 uchar* const anCurrColor = oInputImg.data + nPxIterRGB;
      size_t nGoodSamplesCount = 0, nModelIdx = 0;
      ushort anCurrInputDesc[3];
      while (nGoodSamplesCount < m_nRequiredBGSamples && nModelIdx < m_nBGSamples) {
        const ushort* const anBGDesc = (ushort*)(m_voBGDescSamples[nModelIdx].data + nDescIterRGB);
        const uchar* const anBGColor = m_voBGColorSamples[nModelIdx].data + nPxIterRGB;
        size_t nTotColorDist = 0;
        size_t nTotDescDist = 0;
        for (size_t c = 0; c < 3; ++c) {
          const size_t nColorDist = L1dist(anCurrColor[c], anBGColor[c]);
          if (nColorDist > nCurrSCColorDistThreshold)
            goto failedcheck3ch;
          LBSP::computeSingleRGBDescriptor(oInputImg, anBGColor[c], nCurrImgCoord_X, nCurrImgCoord_Y, c, m_anLBSPThreshold_8bitLUT[anBGColor[c]], anCurrInputDesc[c]);
          const size_t nDescDist = hdist(anCurrInputDesc[c], anBGDesc[c]);
          if (nDescDist > nCurrSCDescDistThreshold)
            goto failedcheck3ch;
          nTotColorDist += nColorDist;
          nTotDescDist += nDescDist;
        }
        if (nTotDescDist <= nCurrDescDistThreshold && nTotColorDist <= nCurrColorDistThreshold)
          nGoodSamplesCount++;
      failedcheck3ch:
        nModelIdx++;
      }
      if (nGoodSamplesCount < m_nRequiredBGSamples)
        oCurrFGMask.data[nPxIter] = UCHAR_MAX;
      else {
        if ((rand() % nLearningRate) == 0) {
          const size_t nSampleModelIdx = rand() % m_nBGSamples;
          ushort* anRandInputDesc = ((ushort*)(m_voBGDescSamples[nSampleModelIdx].data + nDescIterRGB));
          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, anRandInputDesc);
          for (size_t c = 0; c < 3; ++c)
            *(m_voBGColorSamples[nSampleModelIdx].data + nPxIterRGB + c) = anCurrColor[c];
        }
        if ((rand() % nLearningRate) == 0) {
          int nSampleImgCoord_Y, nSampleImgCoord_X;
          getRandNeighborPosition_3x3(nSampleImgCoord_X, nSampleImgCoord_Y, nCurrImgCoord_X, nCurrImgCoord_Y, LBSP::PATCH_SIZE / 2, m_oImgSize);
          const size_t nSampleModelIdx = rand() % m_nBGSamples;
          ushort* anRandInputDesc = ((ushort*)(m_voBGDescSamples[nSampleModelIdx].data + desc_row_step*nSampleImgCoord_Y + 6 * nSampleImgCoord_X));
          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, anRandInputDesc);
          for (size_t c = 0; c < 3; ++c)
            *(m_voBGColorSamples[nSampleModelIdx].data + img_row_step*nSampleImgCoord_Y + 3 * nSampleImgCoord_X + c) = anCurrColor[c];
        }
      }
    }
  }
  cv::medianBlur(oCurrFGMask, m_oLastFGMask, m_nDefaultMedianBlurKernelSize);
  m_oLastFGMask.copyTo(oCurrFGMask);
}

void BackgroundSubtractorLOBSTER::getBackgroundImage(cv::OutputArray backgroundImage) const {
  CV_DbgAssert(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 idx_flt32 = idx_nimg * 4;
        float* oAvgBgImgPtr = (float*)(oAvgBGImg.data + idx_flt32);
        const uchar* const oBGImgPtr = m_voBGColorSamples[s].data + idx_nimg;
        for (size_t c = 0; c < m_nImgChannels; ++c)
          oAvgBgImgPtr[c] += ((float)oBGImgPtr[c]) / m_nBGSamples;
      }
    }
  }
  oAvgBGImg.convertTo(backgroundImage, CV_8U);
}

void BackgroundSubtractorLOBSTER::getBackgroundDescriptorsImage(cv::OutputArray backgroundDescImage) const {
  CV_Assert(LBSP::DESC_SIZE == 2);
  CV_Assert(m_bInitialized);
  cv::Mat oAvgBGDesc = cv::Mat::zeros(m_oImgSize, CV_32FC((int)m_nImgChannels));
  for (size_t n = 0; n < m_voBGDescSamples.size(); ++n) {
    for (int y = 0; y < m_oImgSize.height; ++y) {
      for (int x = 0; x < m_oImgSize.width; ++x) {
        const size_t idx_ndesc = m_voBGDescSamples[n].step.p[0] * y + m_voBGDescSamples[n].step.p[1] * x;
        const size_t idx_flt32 = idx_ndesc * 2;
        float* oAvgBgDescPtr = (float*)(oAvgBGDesc.data + idx_flt32);
        const ushort* const oBGDescPtr = (ushort*)(m_voBGDescSamples[n].data + idx_ndesc);
        for (size_t c = 0; c < m_nImgChannels; ++c)
          oAvgBgDescPtr[c] += ((float)oBGDescPtr[c]) / m_voBGDescSamples.size();
      }
    }
  }
  oAvgBGDesc.convertTo(backgroundDescImage, CV_16U);
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLOBSTER.cpp --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP_.cpp --#--
#include <iostream>
#include <iomanip>
#include <exception>

#include <opencv2/imgproc/imgproc.hpp>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui.hpp>
#endif

#include "BackgroundSubtractorLBSP_.h"
#include "DistanceUtils.h"
#include "RandUtils.h"

//using namespace bgslibrary::algorithms::lbsp;

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)
        , m_nDebugCoordX(0)
        , m_nDebugCoordY(0)
        , m_pDebugFS(nullptr) {
        CV_Assert(m_fRelLBSPThreshold >= 0);
      }

      BackgroundSubtractorLBSP_::~BackgroundSubtractorLBSP_() {}

      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_::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;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP_.cpp --#--

--#-- START ./bgslibrary/algorithms/T2FGMM_UV.cpp --#--
#include "T2FGMM_UV.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

using namespace bgslibrary::algorithms;

T2FGMM_UV::T2FGMM_UV() :
  IBGS(quote(T2FGMM_UV)),
  frameNumber(0), threshold(9.0), alpha(0.01), 
  km(1.5f), kv(0.6f), gaussians(3)
{
  debug_construction(T2FGMM_UV);
  initLoadSaveConfig(algorithmName);
}

T2FGMM_UV::~T2FGMM_UV() {
  debug_destruction(T2FGMM_UV);
}

void T2FGMM_UV::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  frame_data = cvCloneImage(&_frame);

  if (firstTime) {
    int width = img_input.size().width;
    int height = img_input.size().height;

    lowThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    lowThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    highThresholdMask = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    highThresholdMask.Ptr()->origin = IPL_ORIGIN_BL;

    params.SetFrameSize(width, height);
    params.LowThreshold() = threshold;
    params.HighThreshold() = 2 * params.LowThreshold();
    params.Alpha() = alpha;
    params.MaxModes() = gaussians;
    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

    bgs.Initalize(params);
    bgs.InitModel(frame_data);
  }

  bgs.Subtract(frameNumber, frame_data, lowThresholdMask, highThresholdMask);
  lowThresholdMask.Clear();
  bgs.Update(frameNumber, frame_data, lowThresholdMask);

  img_foreground = cv::cvarrToMat(highThresholdMask.Ptr());
  img_background = cv::cvarrToMat(bgs.Background()->Ptr());
  //img_background = cv::Mat::zeros(img_input.size(), img_input.type());

#ifndef MEX_COMPILE_FLAG
  if (showOutput)
    cv::imshow(algorithmName + "_FG", img_foreground);
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  frame_data.ReleaseImage();

  firstTime = false;
  frameNumber++;
}

void T2FGMM_UV::save_config(cv::FileStorage &fs) {
  fs << "threshold" << threshold;
  fs << "alpha" << alpha;
  fs << "km" << km;
  fs << "kv" << kv;
  fs << "gaussians" << gaussians;
  fs << "showOutput" << showOutput;
}

void T2FGMM_UV::load_config(cv::FileStorage &fs) {
  fs["threshold"] >> threshold;
  fs["alpha"] >> alpha;
  fs["km"] >> km;
  fs["kv"] >> kv;
  fs["gaussians"] >> gaussians;
  fs["showOutput"] >> showOutput;
}

#endif

--#-- END ./bgslibrary/algorithms/T2FGMM_UV.cpp --#--

--#-- START ./bgslibrary/algorithms/AdaptiveSelectiveBackgroundLearning.cpp --#--
#include "AdaptiveSelectiveBackgroundLearning.h"

using namespace bgslibrary::algorithms;

AdaptiveSelectiveBackgroundLearning::AdaptiveSelectiveBackgroundLearning() :
  IBGS(quote(AdaptiveSelectiveBackgroundLearning)),
  alphaLearn(0.05), alphaDetection(0.05), learningFrames(-1), 
  counter(0), minVal(0.0), maxVal(1.0), threshold(15)
{
  debug_construction(AdaptiveSelectiveBackgroundLearning);
  initLoadSaveConfig(algorithmName);
}

AdaptiveSelectiveBackgroundLearning::~AdaptiveSelectiveBackgroundLearning() {
  debug_destruction(AdaptiveSelectiveBackgroundLearning);
}

void AdaptiveSelectiveBackgroundLearning::process(const cv::Mat &img_input_, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input_, img_output, img_bgmodel);

  cv::Mat img_input;
  if (img_input_.channels() == 3)
    cv::cvtColor(img_input_, img_input, CV_BGR2GRAY);
  else
    img_input_.copyTo(img_input);

  if (img_background.empty())
    img_input.copyTo(img_background);

  cv::Mat img_input_f(img_input.size(), CV_32F);
  img_input.convertTo(img_input_f, CV_32F, 1. / 255.);

  cv::Mat img_background_f(img_background.size(), CV_32F);
  img_background.convertTo(img_background_f, CV_32F, 1. / 255.);

  cv::Mat img_diff_f(img_input.size(), CV_32F);
  cv::absdiff(img_input_f, img_background_f, img_diff_f);

  //cv::Mat img_foreground(img_input.size(), CV_8U);
  img_diff_f.convertTo(img_foreground, CV_8U, 255.0 / (maxVal - minVal), -minVal);

  cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);
  cv::medianBlur(img_foreground, img_foreground, 3);

  if (learningFrames > 0 && counter <= learningFrames) {
    //std::cout << "Adaptive update..." << std::endl;
    // Only Adaptive update of the background model
    img_background_f = alphaLearn * img_input_f + (1 - alphaLearn) * img_background_f;
    counter++;
  }
  else {
    //std::cout << "Adaptive and Selective update..." << std::endl;
    int rows = img_input.rows;
    int cols = img_input.cols;
    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        // Adaptive and Selective update of the background model
        if (img_foreground.at<uchar>(i, j) == 0) {
          img_background_f.at<float>(i, j) = alphaDetection * img_input_f.at<float>(i, j) + (1 - alphaDetection) * img_background_f.at<float>(i, j);
        }
      }
    }
  }

  //cv::Mat img_new_background(img_input.size(), CV_8U);
  img_background_f.convertTo(img_background, CV_8UC1, 255.0 / (maxVal - minVal), -minVal);
  //img_new_background.copyTo(img_background);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  firstTime = false;
}

void AdaptiveSelectiveBackgroundLearning::save_config(cv::FileStorage &fs) {
  fs << "learningFrames" << learningFrames;
  fs << "alphaLearn" << alphaLearn;
  fs << "alphaDetection" << alphaDetection;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void AdaptiveSelectiveBackgroundLearning::load_config(cv::FileStorage &fs) {
  fs["learningFrames"] >> learningFrames;
  fs["alphaLearn"] >> alphaLearn;
  fs["alphaDetection"] >> alphaDetection;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/AdaptiveSelectiveBackgroundLearning.cpp --#--

--#-- START ./bgslibrary/algorithms/WeightedMovingMean.cpp --#--
#include "WeightedMovingMean.h"

using namespace bgslibrary::algorithms;

WeightedMovingMean::WeightedMovingMean() :
  IBGS(quote(WeightedMovingMean)),
  enableWeight(true), enableThreshold(true), threshold(15)
{
  debug_construction(WeightedMovingMean);
  initLoadSaveConfig(algorithmName);
}

WeightedMovingMean::~WeightedMovingMean() {
  debug_destruction(WeightedMovingMean);
}

void WeightedMovingMean::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (img_input_prev_1.empty()) {
    img_input.copyTo(img_input_prev_1);
    return;
  }

  if (img_input_prev_2.empty()) {
    img_input_prev_1.copyTo(img_input_prev_2);
    img_input.copyTo(img_input_prev_1);
    return;
  }

  cv::Mat img_input_f(img_input.size(), CV_32F);
  img_input.convertTo(img_input_f, CV_32F, 1. / 255.);

  cv::Mat img_input_prev_1_f(img_input.size(), CV_32F);
  img_input_prev_1.convertTo(img_input_prev_1_f, CV_32F, 1. / 255.);

  cv::Mat img_input_prev_2_f(img_input.size(), CV_32F);
  img_input_prev_2.convertTo(img_input_prev_2_f, CV_32F, 1. / 255.);

  cv::Mat img_background_f(img_input.size(), CV_32F);

  if (enableWeight)
    img_background_f = ((img_input_f * 0.5) + (img_input_prev_1_f * 0.3) + (img_input_prev_2_f * 0.2));
  else
    img_background_f = ((img_input_f)+(img_input_prev_1_f)+(img_input_prev_2_f)) / 3.0;

  double minVal, maxVal;
  minVal = 0.; maxVal = 1.;
  img_background_f.convertTo(img_background, CV_8U, 255.0 / (maxVal - minVal), -minVal);

  cv::absdiff(img_input, img_background, img_foreground);

  if (img_foreground.channels() == 3)
    cv::cvtColor(img_foreground, img_foreground, CV_BGR2GRAY);

  if (enableThreshold)
    cv::threshold(img_foreground, img_foreground, threshold, 255, cv::THRESH_BINARY);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);

  img_input_prev_1.copyTo(img_input_prev_2);
  img_input.copyTo(img_input_prev_1);

  firstTime = false;
}

void WeightedMovingMean::save_config(cv::FileStorage &fs) {
  fs << "enableWeight" << enableWeight;
  fs << "enableThreshold" << enableThreshold;
  fs << "threshold" << threshold;
  fs << "showOutput" << showOutput;
}

void WeightedMovingMean::load_config(cv::FileStorage &fs) {
  fs["enableWeight"] >> enableWeight;
  fs["enableThreshold"] >> enableThreshold;
  fs["threshold"] >> threshold;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/WeightedMovingMean.cpp --#--

--#-- START ./bgslibrary/algorithms/LBAdaptiveSOM.cpp --#--
#include "LBAdaptiveSOM.h"

using namespace bgslibrary::algorithms;

LBAdaptiveSOM::LBAdaptiveSOM() :
  IBGS(quote(LBAdaptiveSOM)),
  sensitivity(75), trainingSensitivity(245),
  learningRate(62), trainingLearningRate(255),
  trainingSteps(55)
{
  debug_construction(LBAdaptiveSOM);
  initLoadSaveConfig(algorithmName);
}

LBAdaptiveSOM::~LBAdaptiveSOM() {
  debug_destruction(LBAdaptiveSOM);
  delete m_pBGModel;
}

void LBAdaptiveSOM::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  IplImage _frame = cvIplImage(img_input);
  IplImage* frame = cvCloneImage(&_frame);

  if (firstTime) {
    int w = img_input.size().width;
    int h = img_input.size().height;

    m_pBGModel = new lb::BGModelSom(w, h);
    m_pBGModel->InitModel(frame);
  }

  m_pBGModel->setBGModelParameter(0, sensitivity);
  m_pBGModel->setBGModelParameter(1, trainingSensitivity);
  m_pBGModel->setBGModelParameter(2, learningRate);
  m_pBGModel->setBGModelParameter(3, trainingLearningRate);
  m_pBGModel->setBGModelParameter(5, trainingSteps);

  m_pBGModel->UpdateModel(frame);

  img_foreground = cv::cvarrToMat(m_pBGModel->GetFG());
  img_background = cv::cvarrToMat(m_pBGModel->GetBG());

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
  cvReleaseImage(&frame);

  firstTime = false;
}

void LBAdaptiveSOM::save_config(cv::FileStorage &fs) {
  fs << "sensitivity" << sensitivity;
  fs << "trainingSensitivity" << trainingSensitivity;
  fs << "learningRate" << learningRate;
  fs << "trainingLearningRate" << trainingLearningRate;
  fs << "trainingSteps" << trainingSteps;
  fs << "showOutput" << showOutput;
}

void LBAdaptiveSOM::load_config(cv::FileStorage &fs) {
  fs["sensitivity"] >> sensitivity;
  fs["trainingSensitivity"] >> trainingSensitivity;
  fs["learningRate"] >> learningRate;
  fs["trainingLearningRate"] >> trainingLearningRate;
  fs["trainingSteps"] >> trainingSteps;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/LBAdaptiveSOM.cpp --#--

--#-- START ./bgslibrary/algorithms/TwoPoints/two_points.cpp --#--
#include <assert.h>

#include "two_points.h"

//using namespace bgslibrary::algorithms::twopoints;
namespace bgslibrary
{
  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);
      }

      // ----------------------------------------------------------------------------
      // 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;
      }


      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];
          }
        }

        // 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];
        }

        // 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];
        }

        // 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];
        }

        // 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);
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/TwoPoints/two_points.cpp --#--

--#-- START ./bgslibrary/algorithms/PAWCS.cpp --#--
#include "PAWCS.h"

using namespace bgslibrary::algorithms;

PAWCS::PAWCS() : 
  IBGS(quote(PAWCS)),
  pPAWCS(nullptr),
  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);
}

PAWCS::~PAWCS() {
  debug_destruction(PAWCS);
  if (pPAWCS)
    delete pPAWCS;
}

void PAWCS::process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel)
{
  init(img_input, img_output, img_bgmodel);

  if (firstTime) {
    pPAWCS = new lbsp::BackgroundSubtractorPAWCS(
      fRelLBSPThreshold, nDescDistThresholdOffset, nMinColorDistThreshold,
      nMaxNbWords, nSamplesForMovingAvgs);

    pPAWCS->initialize(img_input, cv::Mat(img_input.size(), CV_8UC1, cv::Scalar_<uchar>(255)));
    firstTime = false;
  }

  pPAWCS->apply(img_input, img_foreground);
  pPAWCS->getBackgroundImage(img_background);

#ifndef MEX_COMPILE_FLAG
  if (showOutput) {
    cv::imshow(algorithmName + "_FG", img_foreground);
    cv::imshow(algorithmName + "_BG", img_background);
  }
#endif

  img_foreground.copyTo(img_output);
  img_background.copyTo(img_bgmodel);
}

void PAWCS::save_config(cv::FileStorage &fs) {
  fs << "fRelLBSPThreshold" << fRelLBSPThreshold;
  fs << "nDescDistThresholdOffset" << nDescDistThresholdOffset;
  fs << "nMinColorDistThreshold" << nMinColorDistThreshold;
  fs << "nMaxNbWords" << nMaxNbWords;
  fs << "nSamplesForMovingAvgs" << nSamplesForMovingAvgs;
  fs << "showOutput" << showOutput;
}

void PAWCS::load_config(cv::FileStorage &fs) {
  fs["fRelLBSPThreshold"] >> fRelLBSPThreshold;
  fs["nDescDistThresholdOffset"] >> nDescDistThresholdOffset;
  fs["nMinColorDistThreshold"] >> nMinColorDistThreshold;
  fs["nMaxNbWords"] >> nMaxNbWords;
  fs["nSamplesForMovingAvgs"] >> nSamplesForMovingAvgs;
  fs["showOutput"] >> showOutput;
}

--#-- END ./bgslibrary/algorithms/PAWCS.cpp --#--

--#-- START ./bgslibrary/VideoAnalysis.cpp --#--
#include "VideoAnalysis.h"

namespace bgslibrary
{
  VideoAnalysis::VideoAnalysis() :
    use_file(false), use_camera(false), cameraIndex(0),
    use_comp(false), frameToStop(0)
  {
    debug_construction(VideoAnalysis);
  }

  VideoAnalysis::~VideoAnalysis() {
    debug_destruction(VideoAnalysis);
  }

  bool VideoAnalysis::setup(int argc, const char **argv)
  {
    bool flag = false;

#if CV_MAJOR_VERSION == 2
    const char* keys =
      "{hp|help|false|Print this message}"
      "{uf|use_file|false|Use video file}"
      "{fn|filename||Specify video file}"
      "{uc|use_cam|false|Use camera}"
      "{ca|camera|0|Specify camera index}"
      "{co|use_comp|false|Use mask comparator}"
      "{st|stopAt|0|Frame number to stop}"
      "{im|imgref||Specify image file}"
      ;
#elif CV_MAJOR_VERSION >= 3
    const std::string keys =
      "{h help ?     |     | Print this message   }"
      "{uf use_file  |false| Use a video file     }"
      "{fn filename  |     | Specify a video file }"
      "{uc use_cam   |false| Use a webcamera      }"
      "{ca camera    | 0   | Specify camera index }"
      "{co use_comp  |false| Use mask comparator  }"
      "{st stopAt    | 0   | Frame number to stop }"
      "{im imgref    |     | Specify a image file }"
      ;
#endif

    cv::CommandLineParser cmd(argc, argv, keys);

#if CV_MAJOR_VERSION == 2
    if (argc <= 1 || cmd.get<bool>("help") == true)
    {
      std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
      std::cout << "Available options:" << std::endl;
      cmd.printParams();
      return false;
    }
#elif CV_MAJOR_VERSION >= 3
    if (argc <= 1 || cmd.has("help"))
    {
      std::cout << "Usage: " << argv[0] << " [options]" << std::endl;
      std::cout << "Available options:" << std::endl;
      cmd.printMessage();
      return false;
    }
    if (!cmd.check())
    {
      cmd.printErrors();
      return false;
    }
#endif

    use_file = cmd.get<bool>("uf"); //use_file
    filename = cmd.get<std::string>("fn"); //filename
    use_camera = cmd.get<bool>("uc"); //use_cam
    cameraIndex = cmd.get<int>("ca"); //camera
    use_comp = cmd.get<bool>("co"); //use_comp
    frameToStop = cmd.get<int>("st"); //stopAt
    imgref = cmd.get<std::string>("im"); //imgref

    std::cout << "use_file:    " << use_file << std::endl;
    std::cout << "filename:    " << filename << std::endl;
    std::cout << "use_camera:  " << use_camera << std::endl;
    std::cout << "cameraIndex: " << cameraIndex << std::endl;
    std::cout << "use_comp:    " << use_comp << std::endl;
    std::cout << "frameToStop: " << frameToStop << std::endl;
    std::cout << "imgref:      " << imgref << std::endl;
    //return false;

    if (use_file) {
      if (filename.empty()) {
        std::cout << "Specify filename" << std::endl;
        return false;
      }

      flag = true;
    }

    if (use_camera)
      flag = true;

    if (flag && use_comp) {
      if (imgref.empty()) {
        std::cout << "Specify image reference" << std::endl;
        return false;
      }
    }

    return flag;
  }

  void VideoAnalysis::start()
  {
    //std::cout << "Press 'ESC' to stop..." << std::endl;

    do {
      videoCapture = std::make_unique<VideoCapture>();
      frameProcessor = std::make_shared<FrameProcessor>();

      frameProcessor->init();
      frameProcessor->frameToStop = frameToStop;
      frameProcessor->imgref = imgref;

      videoCapture->setFrameProcessor(frameProcessor);

      if (use_file)
        videoCapture->setVideo(filename);

      if (use_camera)
        videoCapture->setCamera(cameraIndex);

      videoCapture->start();

      if (use_file || use_camera)
        break;

      frameProcessor->finish();

      auto key = cv::waitKey(500);
      if (key == KEY_ESC)
        break;

    } while (1);
  }
}

--#-- END ./bgslibrary/VideoAnalysis.cpp --#--

--#-- START ./bgslibrary/FrameProcessor.cpp --#--
#include <iomanip>

#include "FrameProcessor.h"

namespace bgslibrary
{
  FrameProcessor::FrameProcessor() : 
    firstTime(true), frameNumber(0), duration(0), 
    tictoc(""), frameToStop(0)
  {
    debug_construction(FrameProcessor);
    initLoadSaveConfig(quote(FrameProcessor));
  }

  FrameProcessor::~FrameProcessor() {
    debug_destruction(FrameProcessor);
  }

  void FrameProcessor::init()
  {
    if (enablePreProcessor)
      preProcessor = std::make_unique<PreProcessor>();

    if (enableFrameDifference)
      frameDifference = std::make_shared<FrameDifference>();

    if (enableStaticFrameDifference)
      staticFrameDifference = std::make_shared<StaticFrameDifference>();

    if (enableWeightedMovingMean)
      weightedMovingMean = std::make_shared<WeightedMovingMean>();

    if (enableWeightedMovingVariance)
      weightedMovingVariance = std::make_shared<WeightedMovingVariance>();

    if (enableAdaptiveBackgroundLearning)
      adaptiveBackgroundLearning = std::make_shared<AdaptiveBackgroundLearning>();

    if (enableAdaptiveSelectiveBackgroundLearning)
      adaptiveSelectiveBackgroundLearning = std::make_shared<AdaptiveSelectiveBackgroundLearning>();

    if (enableMixtureOfGaussianV2)
      mixtureOfGaussianV2 = std::make_shared<MixtureOfGaussianV2>();

#if CV_MAJOR_VERSION == 2
    if (enableMixtureOfGaussianV1)
      mixtureOfGaussianV1 = std::make_shared<MixtureOfGaussianV1>();
#endif

#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3
    if (enableGMG)
      gmg = std::make_shared<GMG>();
#endif

#if CV_MAJOR_VERSION >= 3
    if (enableKNN)
      knn = std::make_shared<KNN>();
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3
    if (enableDPAdaptiveMedian)
      dpAdaptiveMedian = std::make_shared<DPAdaptiveMedian>();

    if (enableDPGrimsonGMM)
      dpGrimsonGMM = std::make_shared<DPGrimsonGMM>();

    if (enableDPZivkovicAGMM)
      dpZivkovicAGMM = std::make_shared<DPZivkovicAGMM>();

    if (enableDPMean)
      dpTemporalMean = std::make_shared<DPMean>();

    if (enableDPWrenGA)
      dpWrenGA = std::make_shared<DPWrenGA>();

    if (enableDPPratiMediod)
      dpPratiMediod = std::make_shared<DPPratiMediod>();

    if (enableDPEigenbackground)
      dpEigenBackground = std::make_shared<DPEigenbackground>();

    if (enableDPTexture)
      dpTexture = std::make_shared<DPTexture>();

    if (enableT2FGMM_UM)
      type2FuzzyGMM_UM = std::make_shared<T2FGMM_UM>();

    if (enableT2FGMM_UV)
      type2FuzzyGMM_UV = std::make_shared<T2FGMM_UV>();

    if (enableT2FMRF_UM)
      type2FuzzyMRF_UM = std::make_shared<T2FMRF_UM>();

    if (enableT2FMRF_UV)
      type2FuzzyMRF_UV = std::make_shared<T2FMRF_UV>();

    if (enableFuzzySugenoIntegral)
      fuzzySugenoIntegral = std::make_shared<FuzzySugenoIntegral>();

    if (enableFuzzyChoquetIntegral)
      fuzzyChoquetIntegral = std::make_shared<FuzzyChoquetIntegral>();

    if (enableLBSimpleGaussian)
      lbSimpleGaussian = std::make_shared<LBSimpleGaussian>();

    if (enableLBFuzzyGaussian)
      lbFuzzyGaussian = std::make_shared<LBFuzzyGaussian>();

    if (enableLBMixtureOfGaussians)
      lbMixtureOfGaussians = std::make_shared<LBMixtureOfGaussians>();

    if (enableLBAdaptiveSOM)
      lbAdaptiveSOM = std::make_shared<LBAdaptiveSOM>();

    if (enableLBFuzzyAdaptiveSOM)
      lbFuzzyAdaptiveSOM = std::make_shared<LBFuzzyAdaptiveSOM>();

    if (enablePBAS)
      pixelBasedAdaptiveSegmenter = std::make_shared<PixelBasedAdaptiveSegmenter>();

    if (enableVuMeter)
      vuMeter = std::make_shared<VuMeter>();

    if (enableKDE)
      kde = std::make_shared<KDE>();

    if (enableIMBS)
      imbs = std::make_shared<IndependentMultimodal>();

    if (enableMultiCue)
      multiCue = std::make_shared<MultiCue>();
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7
    if (enableLbpMrf)
      lbpMrf = std::make_shared<LBP_MRF>();

    if (enableMultiLayer)
      multiLayer = std::make_shared<MultiLayer>();
#endif

    if (enableSigmaDelta)
      sigmaDelta = std::make_shared<SigmaDelta>();

    if (enableSuBSENSE)
      subSENSE = std::make_shared<SuBSENSE>();

    if (enableLOBSTER)
      lobster = std::make_shared<LOBSTER>();

    if (enablePAWCS)
      pawcs = std::make_shared<PAWCS>();

    if (enableTwoPoints)
      twoPoints = std::make_shared<TwoPoints>();

    if (enableViBe)
      vibe = std::make_shared<ViBe>();

    if (enableCodeBook)
      codeBook = std::make_shared<CodeBook>();

    if (enableForegroundMaskAnalysis)
      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)
  {
    if (tictoc == name)
      tic(name);
    
    cv::Mat img_bkgmodel;
    bgs->process(img_input, img_bgs, img_bkgmodel);
    
    if (tictoc == name)
      toc();
  }
  
  void FrameProcessor::process(const cv::Mat &img_input)
  {
    frameNumber++;

    if (enablePreProcessor)
      preProcessor->process(img_input, img_preProcessor);
    else
      img_input.copyTo(img_preProcessor);

    if (enableFrameDifference)
      process("FrameDifference", frameDifference, img_preProcessor, img_frameDifference);

    if (enableStaticFrameDifference)
      process("StaticFrameDifference", staticFrameDifference, img_preProcessor, img_staticFrameDifference);

    if (enableWeightedMovingMean)
      process("WeightedMovingMean", weightedMovingMean, img_preProcessor, img_weightedMovingMean);

    if (enableWeightedMovingVariance)
      process("WeightedMovingVariance", weightedMovingVariance, img_preProcessor, img_weightedMovingVariance);

    if (enableAdaptiveBackgroundLearning)
      process("AdaptiveBackgroundLearning", adaptiveBackgroundLearning, img_preProcessor, img_adaptiveBackgroundLearning);

    if (enableAdaptiveSelectiveBackgroundLearning)
      process("AdaptiveSelectiveBackgroundLearning", adaptiveSelectiveBackgroundLearning, img_preProcessor, img_adaptiveSelectiveBackgroundLearning);

    if (enableMixtureOfGaussianV2)
      process("MixtureOfGaussianV2", mixtureOfGaussianV2, img_preProcessor, img_mixtureOfGaussianV2);

#if CV_MAJOR_VERSION == 2
    if (enableMixtureOfGaussianV1)
      process("MixtureOfGaussianV1", mixtureOfGaussianV1, img_preProcessor, img_mixtureOfGaussianV1);
#endif

#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3
    if (enableGMG)
      process("GMG", gmg, img_preProcessor, img_gmg);
#endif

#if CV_MAJOR_VERSION >= 3
    if (enableKNN)
      process("KNN", knn, img_preProcessor, img_knn);
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3
    if (enableDPAdaptiveMedian)
      process("DPAdaptiveMedian", dpAdaptiveMedian, img_preProcessor, img_dpAdaptiveMedian);

    if (enableDPGrimsonGMM)
      process("DPGrimsonGMM", dpGrimsonGMM, img_preProcessor, img_dpGrimsonGMM);

    if (enableDPZivkovicAGMM)
      process("DPZivkovicAGMM", dpZivkovicAGMM, img_preProcessor, img_dpZivkovicAGMM);

    if (enableDPMean)
      process("DPMean", dpTemporalMean, img_preProcessor, img_dpTemporalMean);

    if (enableDPWrenGA)
      process("DPWrenGA", dpWrenGA, img_preProcessor, img_dpWrenGA);

    if (enableDPPratiMediod)
      process("DPPratiMediod", dpPratiMediod, img_preProcessor, img_dpPratiMediod);

    if (enableDPEigenbackground)
      process("DPEigenbackground", dpEigenBackground, img_preProcessor, img_dpEigenBackground);

    if (enableDPTexture)
      process("DPTexture", dpTexture, img_preProcessor, img_dpTexture);

    if (enableT2FGMM_UM)
      process("T2FGMM_UM", type2FuzzyGMM_UM, img_preProcessor, img_type2FuzzyGMM_UM);

    if (enableT2FGMM_UV)
      process("T2FGMM_UV", type2FuzzyGMM_UV, img_preProcessor, img_type2FuzzyGMM_UV);

    if (enableT2FMRF_UM)
      process("T2FMRF_UM", type2FuzzyMRF_UM, img_preProcessor, img_type2FuzzyMRF_UM);

    if (enableT2FMRF_UV)
      process("T2FMRF_UV", type2FuzzyMRF_UV, img_preProcessor, img_type2FuzzyMRF_UV);

    if (enableFuzzySugenoIntegral)
      process("FuzzySugenoIntegral", fuzzySugenoIntegral, img_preProcessor, img_fuzzySugenoIntegral);

    if (enableFuzzyChoquetIntegral)
      process("FuzzyChoquetIntegral", fuzzyChoquetIntegral, img_preProcessor, img_fuzzyChoquetIntegral);

    if (enableLBSimpleGaussian)
      process("LBSimpleGaussian", lbSimpleGaussian, img_preProcessor, img_lbSimpleGaussian);

    if (enableLBFuzzyGaussian)
      process("LBFuzzyGaussian", lbFuzzyGaussian, img_preProcessor, img_lbFuzzyGaussian);

    if (enableLBMixtureOfGaussians)
      process("LBMixtureOfGaussians", lbMixtureOfGaussians, img_preProcessor, img_lbMixtureOfGaussians);

    if (enableLBAdaptiveSOM)
      process("LBAdaptiveSOM", lbAdaptiveSOM, img_preProcessor, img_lbAdaptiveSOM);

    if (enableLBFuzzyAdaptiveSOM)
      process("LBFuzzyAdaptiveSOM", lbFuzzyAdaptiveSOM, img_preProcessor, img_lbFuzzyAdaptiveSOM);

    if (enablePBAS)
      process("PBAS", pixelBasedAdaptiveSegmenter, img_preProcessor, img_pixelBasedAdaptiveSegmenter);

    if (enableVuMeter)
      process("VuMeter", vuMeter, img_preProcessor, img_vumeter);

    if (enableKDE)
      process("KDE", kde, img_preProcessor, img_kde);

    if (enableIMBS)
      process("IMBS", imbs, img_preProcessor, img_imbs);

    if (enableMultiCue)
      process("MultiCue", multiCue, img_preProcessor, img_multiCue);
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7
    if (enableLbpMrf)
      process("LbpMrf", lbpMrf, img_preProcessor, img_lbpMrf);

    if (enableMultiLayer)
    {
      multiLayer->setStatus(MultiLayer::MLBGS_LEARN);
      //multiLayer->setStatus(MultiLayer::MLBGS_DETECT);
      process("MultiLayer", multiLayer, img_preProcessor, img_multiLayer);
    }
#endif

    if (enableSigmaDelta)
      process("SigmaDelta", sigmaDelta, img_preProcessor, img_sigmaDelta);

    if (enableSuBSENSE)
      process("SuBSENSE", subSENSE, img_preProcessor, img_subSENSE);

    if (enableLOBSTER)
      process("LOBSTER", lobster, img_preProcessor, img_lobster);

    if (enablePAWCS)
      process("PAWCS", pawcs, img_preProcessor, img_pawcs);

    if (enableTwoPoints)
      process("TwoPoints", twoPoints, img_preProcessor, img_twoPoints);

    if (enableViBe)
      process("ViBe", vibe, img_preProcessor, img_vibe);

    if (enableCodeBook)
      process("CodeBook", codeBook, img_preProcessor, img_codeBook);

    if (enableForegroundMaskAnalysis)
    {
      foregroundMaskAnalysis->stopAt = frameToStop;
      foregroundMaskAnalysis->img_ref_path = imgref;

      foregroundMaskAnalysis->process(frameNumber, "FrameDifference", img_frameDifference);
      foregroundMaskAnalysis->process(frameNumber, "StaticFrameDifference", img_staticFrameDifference);
      foregroundMaskAnalysis->process(frameNumber, "WeightedMovingMean", img_weightedMovingMean);
      foregroundMaskAnalysis->process(frameNumber, "WeightedMovingVariance", img_weightedMovingVariance);
      foregroundMaskAnalysis->process(frameNumber, "AdaptiveBackgroundLearning", img_adaptiveBackgroundLearning);
      foregroundMaskAnalysis->process(frameNumber, "AdaptiveSelectiveBackgroundLearning", img_adaptiveSelectiveBackgroundLearning);
      foregroundMaskAnalysis->process(frameNumber, "MixtureOfGaussianV2", img_mixtureOfGaussianV2);

#if CV_MAJOR_VERSION == 2
      foregroundMaskAnalysis->process(frameNumber, "MixtureOfGaussianV1", img_mixtureOfGaussianV1);
#endif

#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3
      foregroundMaskAnalysis->process(frameNumber, "GMG", img_gmg);
#endif
      
#if CV_MAJOR_VERSION >= 3
      foregroundMaskAnalysis->process(frameNumber, "KNN", img_knn);
#endif
      
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3
      foregroundMaskAnalysis->process(frameNumber, "DPAdaptiveMedian", img_dpAdaptiveMedian);
      foregroundMaskAnalysis->process(frameNumber, "DPGrimsonGMM", img_dpGrimsonGMM);
      foregroundMaskAnalysis->process(frameNumber, "DPZivkovicAGMM", img_dpZivkovicAGMM);
      foregroundMaskAnalysis->process(frameNumber, "DPMean", img_dpTemporalMean);
      foregroundMaskAnalysis->process(frameNumber, "DPWrenGA", img_dpWrenGA);
      foregroundMaskAnalysis->process(frameNumber, "DPPratiMediod", img_dpPratiMediod);
      foregroundMaskAnalysis->process(frameNumber, "DPEigenbackground", img_dpEigenBackground);
      foregroundMaskAnalysis->process(frameNumber, "DPTexture", img_dpTexture);
      foregroundMaskAnalysis->process(frameNumber, "T2FGMM_UM", img_type2FuzzyGMM_UM);
      foregroundMaskAnalysis->process(frameNumber, "T2FGMM_UV", img_type2FuzzyGMM_UV);
      foregroundMaskAnalysis->process(frameNumber, "T2FMRF_UM", img_type2FuzzyMRF_UM);
      foregroundMaskAnalysis->process(frameNumber, "T2FMRF_UV", img_type2FuzzyMRF_UV);
      foregroundMaskAnalysis->process(frameNumber, "FuzzySugenoIntegral", img_fuzzySugenoIntegral);
      foregroundMaskAnalysis->process(frameNumber, "FuzzyChoquetIntegral", img_fuzzyChoquetIntegral);
      foregroundMaskAnalysis->process(frameNumber, "LBSimpleGaussian", img_lbSimpleGaussian);
      foregroundMaskAnalysis->process(frameNumber, "LBFuzzyGaussian", img_lbFuzzyGaussian);
      foregroundMaskAnalysis->process(frameNumber, "LBMixtureOfGaussians", img_lbMixtureOfGaussians);
      foregroundMaskAnalysis->process(frameNumber, "LBAdaptiveSOM", img_lbAdaptiveSOM);
      foregroundMaskAnalysis->process(frameNumber, "LBFuzzyAdaptiveSOM", img_lbFuzzyAdaptiveSOM);
      foregroundMaskAnalysis->process(frameNumber, "PBAS", img_pixelBasedAdaptiveSegmenter);
      foregroundMaskAnalysis->process(frameNumber, "VuMeter", img_vumeter);
      foregroundMaskAnalysis->process(frameNumber, "KDE", img_kde);
      foregroundMaskAnalysis->process(frameNumber, "IMBS", img_imbs);
      foregroundMaskAnalysis->process(frameNumber, "MultiCue", img_multiCue);
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7
      foregroundMaskAnalysis->process(frameNumber, "LbpMrf", img_lbpMrf);
      foregroundMaskAnalysis->process(frameNumber, "MultiLayer", img_multiLayer);
#endif

      foregroundMaskAnalysis->process(frameNumber, "SigmaDelta", img_sigmaDelta);
      foregroundMaskAnalysis->process(frameNumber, "SuBSENSE", img_subSENSE);
      foregroundMaskAnalysis->process(frameNumber, "LOBSTER", img_lobster);
      foregroundMaskAnalysis->process(frameNumber, "PAWCS", img_pawcs);
      foregroundMaskAnalysis->process(frameNumber, "TwoPoints", img_twoPoints);
      foregroundMaskAnalysis->process(frameNumber, "ViBe", img_vibe);
      foregroundMaskAnalysis->process(frameNumber, "CodeBook", img_codeBook);
    }

    firstTime = false;
  }

  void FrameProcessor::finish(void){}

  void FrameProcessor::tic(std::string value)
  {
    processname = value;
    duration = static_cast<double>(cv::getTickCount());
  }

  void FrameProcessor::toc()
  {
    duration = (static_cast<double>(cv::getTickCount()) - duration) / cv::getTickFrequency();
    std::cout << processname << "\ttime(sec):" << std::fixed << std::setprecision(6) << duration << std::endl;
  }

  void FrameProcessor::save_config(cv::FileStorage &fs) {
    fs << "tictoc" << tictoc;
    fs << "enablePreProcessor" << enablePreProcessor;
    fs << "enableForegroundMaskAnalysis" << enableForegroundMaskAnalysis;
    fs << "enableFrameDifference" << enableFrameDifference;
    fs << "enableStaticFrameDifference" << enableStaticFrameDifference;
    fs << "enableWeightedMovingMean" << enableWeightedMovingMean;
    fs << "enableWeightedMovingVariance" << enableWeightedMovingVariance;
    fs << "enableAdaptiveBackgroundLearning" << enableAdaptiveBackgroundLearning;
    fs << "enableAdaptiveSelectiveBackgroundLearning" << enableAdaptiveSelectiveBackgroundLearning;
    fs << "enableMixtureOfGaussianV2" << enableMixtureOfGaussianV2;

#if CV_MAJOR_VERSION == 2
    fs << "enableMixtureOfGaussianV1" << enableMixtureOfGaussianV1;
#endif
    
#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3
    fs << "enableGMG" << enableGMG;
#endif
    
#if CV_MAJOR_VERSION >= 3
    fs << "enableKNN" << enableKNN;
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3
    fs << "enableDPAdaptiveMedian" << enableDPAdaptiveMedian;
    fs << "enableDPGrimsonGMM" << enableDPGrimsonGMM;
    fs << "enableDPZivkovicAGMM" << enableDPZivkovicAGMM;
    fs << "enableDPMean" << enableDPMean;
    fs << "enableDPWrenGA" << enableDPWrenGA;
    fs << "enableDPPratiMediod" << enableDPPratiMediod;
    fs << "enableDPEigenbackground" << enableDPEigenbackground;
    fs << "enableDPTexture" << enableDPTexture;
    fs << "enableT2FGMM_UM" << enableT2FGMM_UM;
    fs << "enableT2FGMM_UV" << enableT2FGMM_UV;
    fs << "enableT2FMRF_UM" << enableT2FMRF_UM;
    fs << "enableT2FMRF_UV" << enableT2FMRF_UV;
    fs << "enableFuzzySugenoIntegral" << enableFuzzySugenoIntegral;
    fs << "enableFuzzyChoquetIntegral" << enableFuzzyChoquetIntegral;
    fs << "enableLBSimpleGaussian" << enableLBSimpleGaussian;
    fs << "enableLBFuzzyGaussian" << enableLBFuzzyGaussian;
    fs << "enableLBMixtureOfGaussians" << enableLBMixtureOfGaussians;
    fs << "enableLBAdaptiveSOM" << enableLBAdaptiveSOM;
    fs << "enableLBFuzzyAdaptiveSOM" << enableLBFuzzyAdaptiveSOM;
    fs << "enablePBAS" << enablePBAS;
    fs << "enableVuMeter" << enableVuMeter;
    fs << "enableKDE" << enableKDE;
    fs << "enableIMBS" << enableIMBS;
    fs << "enableMultiCue" << enableMultiCue;
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7
    fs << "enableLbpMrf" << enableLbpMrf;
    fs << "enableMultiLayer" << enableMultiLayer;
#endif

    fs << "enableSigmaDelta" << enableSigmaDelta;
    fs << "enableSuBSENSE" << enableSuBSENSE;
    fs << "enableLOBSTER" << enableLOBSTER;
    fs << "enablePAWCS" << enablePAWCS;
    fs << "enableTwoPoints" << enableTwoPoints;
    fs << "enableViBe" << enableViBe;
    fs << "enableCodeBook" << enableCodeBook;
  }

  void FrameProcessor::load_config(cv::FileStorage &fs) {
    fs["tictoc"] >> tictoc;
    fs["enablePreProcessor"] >> enablePreProcessor;
    fs["enableForegroundMaskAnalysis"] >> enableForegroundMaskAnalysis;
    fs["enableFrameDifference"] >> enableFrameDifference;
    fs["enableStaticFrameDifference"] >> enableStaticFrameDifference;
    fs["enableWeightedMovingMean"] >> enableWeightedMovingMean;
    fs["enableWeightedMovingVariance"] >> enableWeightedMovingVariance;
    fs["enableAdaptiveBackgroundLearning"] >> enableAdaptiveBackgroundLearning;
    fs["enableAdaptiveSelectiveBackgroundLearning"] >> enableAdaptiveSelectiveBackgroundLearning;
    fs["enableMixtureOfGaussianV2"] >> enableMixtureOfGaussianV2;

#if CV_MAJOR_VERSION == 2
    fs["enableMixtureOfGaussianV1"] >> enableMixtureOfGaussianV1;
#endif
    
#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3
    fs["enableGMG"] >> enableGMG;
#endif
    
#if CV_MAJOR_VERSION >= 3
    fs["enableKNN"] >> enableKNN;
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3
    fs["enableDPAdaptiveMedian"] >> enableDPAdaptiveMedian;
    fs["enableDPGrimsonGMM"] >> enableDPGrimsonGMM;
    fs["enableDPZivkovicAGMM"] >> enableDPZivkovicAGMM;
    fs["enableDPMean"] >> enableDPMean;
    fs["enableDPWrenGA"] >> enableDPWrenGA;
    fs["enableDPPratiMediod"] >> enableDPPratiMediod;
    fs["enableDPEigenbackground"] >> enableDPEigenbackground;
    fs["enableDPTexture"] >> enableDPTexture;
    fs["enableT2FGMM_UM"] >> enableT2FGMM_UM;
    fs["enableT2FGMM_UV"] >> enableT2FGMM_UV;
    fs["enableT2FMRF_UM"] >> enableT2FMRF_UM;
    fs["enableT2FMRF_UV"] >> enableT2FMRF_UV;
    fs["enableFuzzySugenoIntegral"] >> enableFuzzySugenoIntegral;
    fs["enableFuzzyChoquetIntegral"] >> enableFuzzyChoquetIntegral;
    fs["enableLBSimpleGaussian"] >> enableLBSimpleGaussian;
    fs["enableLBFuzzyGaussian"] >> enableLBFuzzyGaussian;
    fs["enableLBMixtureOfGaussians"] >> enableLBMixtureOfGaussians;
    fs["enableLBAdaptiveSOM"] >> enableLBAdaptiveSOM;
    fs["enableLBFuzzyAdaptiveSOM"] >> enableLBFuzzyAdaptiveSOM;
    fs["enablePBAS"] >> enablePBAS;
    fs["enableVuMeter"] >> enableVuMeter;
    fs["enableKDE"] >> enableKDE;
    fs["enableIMBS"] >> enableIMBS;
    fs["enableMultiCue"] >> enableMultiCue;
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7
    fs["enableLbpMrf"] >> enableLbpMrf;
    fs["enableMultiLayer"] >> enableMultiLayer;
#endif

    fs["enableSigmaDelta"] >> enableSigmaDelta;
    fs["enableSuBSENSE"] >> enableSuBSENSE;
    fs["enableLOBSTER"] >> enableLOBSTER;
    fs["enablePAWCS"] >> enablePAWCS;
    fs["enableTwoPoints"] >> enableTwoPoints;
    fs["enableViBe"] >> enableViBe;
    fs["enableCodeBook"] >> enableCodeBook;
  }
}

--#-- END ./bgslibrary/FrameProcessor.cpp --#--

--#-- START ./bgslibrary/Main.cpp --#--
#include <iostream>

#include "utils/GenericKeys.h"
#include "VideoAnalysis.h"

namespace bgslibrary
{
  class Main
  {
  private:
    Main();

  public:
    static void start(int argc, const char **argv)
    {
      std::cout << "---------------------------------------------" << std::endl;
      std::cout << "Background Subtraction Library               " << std::endl;
      std::cout << "https://github.com/andrewssobral/bgslibrary  " << std::endl;
      std::cout << "This software is under the MIT License       " << std::endl;
      std::cout << "---------------------------------------------" << std::endl;
      std::cout << "Using OpenCV version " << CV_VERSION << std::endl;

      try
      {
        auto key = KEY_ESC;

        do
        {
          auto videoAnalysis = std::make_unique<VideoAnalysis>();

          if (videoAnalysis->setup(argc, argv))
          {
            videoAnalysis->start();

            std::cout << "Processing finished, enter:" << std::endl;
            std::cout << "R - Repeat" << std::endl;
            std::cout << "Q - Quit" << std::endl;

            key = cv::waitKey();
          }

          cv::destroyAllWindows();

        } while (key == KEY_REPEAT);
      }
      catch (const std::exception& ex)
      {
        std::cout << "std::exception:" << ex.what() << std::endl;
        return;
      }
      catch (...)
      {
        std::cout << "Unknow error" << std::endl;
        return;
      }

#ifdef WIN32
      //system("pause");
#endif
    }
  };
}

int main(int argc, const char **argv)
{
  bgslibrary::Main::start(argc, argv);
  return 0;
}

--#-- END ./bgslibrary/Main.cpp --#--

--#-- START ./.gitattributes --#--
*.vcproj    eol=crlf
*.bat       eol=crlf
*.sln       eol=crlf

--#-- END ./.gitattributes --#--

--#-- START ./config/.gitignore --#--
# Ignore everything in this directory
*
# Except these files
!.gitignore
!FrameProcessor.xml
!PreProcessor.xml
!VideoCapture.xml

--#-- END ./config/.gitignore --#--

--#-- START ./output/in/.gitignore --#--
# Ignore everything in this directory
*
# Except these files
!.gitignore

--#-- END ./output/in/.gitignore --#--

--#-- START ./output/bg/.gitignore --#--
# Ignore everything in this directory
*
# Except these files
!.gitignore

--#-- END ./output/bg/.gitignore --#--

--#-- START ./output/fg/.gitignore --#--
# Ignore everything in this directory
*
# Except these files
!.gitignore

--#-- END ./output/fg/.gitignore --#--

--#-- START ./.gitignore --#--
etc/
tmp/
bkp/
build-*/
build_*/
dist/
dataset_*/
binaries*/
examples/build-*/
examples/build_*/
gui/java/dist/
gui/java/build/
gui/java/bgslibrary.exe
_*
*.exe
*.pdb
*.suo
*.dll
*.pyd
*.so
*.config
*.creator
*.creator.user
*.files
*.includes
*.egg-info/
.vscode/
bgslibrary_gui
.pypirc
upload.sh
*.code-workspace
env
bgslibrary_env
bgslibrary_test_env

--#-- END ./.gitignore --#--

--#-- START ./bgslibrary/tools/ForegroundMaskAnalysis.h --#--
#pragma once

#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#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>

#include "../utils/ILoadSaveConfig.h"

namespace bgslibrary
{
  namespace tools
  {
    class ForegroundMaskAnalysis: public ILoadSaveConfig
    {
    private:
      bool firstTime;
      bool showOutput;

    public:
      ForegroundMaskAnalysis();
      ~ForegroundMaskAnalysis();

      int stopAt;
      std::string img_ref_path;

      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);
    };
  }
}

--#-- END ./bgslibrary/tools/ForegroundMaskAnalysis.h --#--

--#-- START ./bgslibrary/tools/FuzzyUtils.h --#--
#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);
    };
  }
}

--#-- END ./bgslibrary/tools/FuzzyUtils.h --#--

--#-- START ./bgslibrary/tools/PixelUtils.h --#--
#pragma once

#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>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui_c.h>
#endif

namespace bgslibrary
{
  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);
    };
  }
}

--#-- END ./bgslibrary/tools/PixelUtils.h --#--

--#-- START ./bgslibrary/tools/PerformanceUtils.h --#--
#pragma once

#include <stdio.h>
#include <fstream>
#include <opencv2/opencv.hpp>

#include "PixelUtils.h"

namespace bgslibrary
{
  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);

      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);
    };
  }
}

--#-- END ./bgslibrary/tools/PerformanceUtils.h --#--

--#-- START ./bgslibrary/algorithms/T2FMRF_UV.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "IBGS.h"
#include "T2F/MRF.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class T2FMRF_UV : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      float km;
      float kv;
      int gaussians;
      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();
      ~T2FMRF_UV();

      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);
    };

    bgs_register(T2FMRF_UV);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2FMRF_UV.h --#--

--#-- START ./bgslibrary/algorithms/PixelBasedAdaptiveSegmenter.h --#--
#pragma once

#include "IBGS.h"
#include "PBAS/PBAS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class PixelBasedAdaptiveSegmenter : public IBGS
    {
    private:
      pbas::PBAS pbas;

      bool enableInputBlur;
      bool enableOutputBlur;

      float alpha;
      float beta;
      int N;
      int Raute_min;
      float R_incdec;
      int R_lower;
      int R_scale;
      float T_dec;
      int T_inc;
      int T_init;
      int T_lower;
      int T_upper;

    public:
      PixelBasedAdaptiveSegmenter();
      ~PixelBasedAdaptiveSegmenter();

      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);
    };

    bgs_register(PixelBasedAdaptiveSegmenter);
  }
}

--#-- END ./bgslibrary/algorithms/PixelBasedAdaptiveSegmenter.h --#--

--#-- START ./bgslibrary/algorithms/LBFuzzyAdaptiveSOM.h --#--
#pragma once

#include "IBGS.h"
#include "lb/BGModelFuzzySom.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class LBFuzzyAdaptiveSOM : public IBGS
    {
    private:
      lb::BGModel* m_pBGModel;
      int sensitivity;
      int trainingSensitivity;
      int learningRate;
      int trainingLearningRate;
      int trainingSteps;

    public:
      LBFuzzyAdaptiveSOM();
      ~LBFuzzyAdaptiveSOM();

      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);
    };

    bgs_register(LBFuzzyAdaptiveSOM);
  }
}

--#-- END ./bgslibrary/algorithms/LBFuzzyAdaptiveSOM.h --#--

--#-- START ./bgslibrary/algorithms/T2FGMM_UV.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "IBGS.h"
#include "T2F/T2FGMM.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class T2FGMM_UV : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      float km;
      float kv;
      int gaussians;
      dp::RgbImage frame_data;
      dp::T2FGMMParams params;
      dp::T2FGMM bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      T2FGMM_UV();
      ~T2FGMM_UV();

      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);
    };

    bgs_register(T2FGMM_UV);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2FGMM_UV.h --#--

--#-- START ./bgslibrary/algorithms/MixtureOfGaussianV1.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/video/background_segm.hpp>

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class MixtureOfGaussianV1 : public IBGS
    {
    private:
      cv::BackgroundSubtractorMOG mog;
      double alpha;
      bool enableThreshold;
      int threshold;

    public:
      MixtureOfGaussianV1();
      ~MixtureOfGaussianV1();

      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);
    };

    bgs_register(MixtureOfGaussianV1);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MixtureOfGaussianV1.h --#--

--#-- START ./bgslibrary/algorithms/DPWrenGA.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "dp/WrenGA.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPWrenGA : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      int learningFrames;
      dp::RgbImage frame_data;
      dp::WrenParams params;
      dp::WrenGA bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPWrenGA();
      ~DPWrenGA();

      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);
    };

    bgs_register(DPWrenGA);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPWrenGA.h --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if (CV_MAJOR_VERSION == 2) || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7)

#include "LBP_MRF/MotionDetection.hpp"

namespace bgslibrary
{
  namespace algorithms
  {
    class LBP_MRF : public IBGS
    {
    private:
      lbp_mrf::MotionDetection* Detector;
      cv::Mat img_segmentation;

    public:
      LBP_MRF();
      ~LBP_MRF();

      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);
    };

    bgs_register(LBP_MRF);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF.h --#--

--#-- START ./bgslibrary/algorithms/VuMeter.h --#--
#pragma once

#include "IBGS.h"
#include "VuMeter/TBackgroundVuMeter.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class VuMeter : public IBGS
    {
    private:
      vumeter::TBackgroundVuMeter bgs;
      IplImage *frame;
      IplImage *gray;
      IplImage *background;
      IplImage *mask;
      bool enableFilter;
      int binSize;
      double alpha;
      double threshold;

    public:
      VuMeter();
      ~VuMeter();

      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);
    };

    bgs_register(VuMeter);
  }
}

--#-- END ./bgslibrary/algorithms/VuMeter.h --#--

--#-- START ./bgslibrary/algorithms/SuBSENSE.h --#--
#pragma once

#include "IBGS.h"
#include "LBSP/BackgroundSubtractorSuBSENSE.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class SuBSENSE : public IBGS
    {
    private:
      lbsp::BackgroundSubtractorSuBSENSE* pSubsense;

      float fRelLBSPThreshold;
      int nDescDistThresholdOffset;
      int nMinColorDistThreshold;
      int nBGSamples;
      int nRequiredBGSamples;
      int nSamplesForMovingAvgs;

    public:
      SuBSENSE();
      ~SuBSENSE();

      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);
    };

    bgs_register(SuBSENSE);
  }
}

--#-- END ./bgslibrary/algorithms/SuBSENSE.h --#--

--#-- START ./bgslibrary/algorithms/LBSimpleGaussian.h --#--
#pragma once

#include "IBGS.h"
#include "lb/BGModelGauss.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class LBSimpleGaussian : public IBGS
    {
    private:
      lb::BGModel* m_pBGModel;
      int sensitivity;
      int noiseVariance;
      int learningRate;

    public:
      LBSimpleGaussian();
      ~LBSimpleGaussian();

      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);
    };

    bgs_register(LBSimpleGaussian);
  }
}

--#-- END ./bgslibrary/algorithms/LBSimpleGaussian.h --#--

--#-- START ./bgslibrary/algorithms/SigmaDelta/sdLaMa091.h --#--
#pragma once

#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

namespace bgslibrary
{
  namespace algorithms
  {
    namespace sigmadelta
    {
      const int BACKGROUND = 0;
      const int FOREGROUND = 255;
      const int ERROR_OCCURED = 1;

      typedef struct sdLaMa091 sdLaMa091_t;

      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_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);

      uint32_t sdLaMa091GetAmplificationFactor(const sdLaMa091_t* sdLaMa091);

      int32_t sdLaMa091SetMaximalVariance(sdLaMa091_t* sdLaMa091,
        const uint32_t maximalVariance);

      uint32_t sdLaMa091GetMaximalVariance(const sdLaMa091_t* sdLaMa091);

      int32_t sdLaMa091SetMinimalVariance(sdLaMa091_t* sdLaMa091,
        const uint32_t minimalVariance);

      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_C3R(sdLaMa091_t* sdLaMa091,
        const uint8_t* image_data,
        uint8_t* segmentation_map);

      int32_t sdLaMa091Free(sdLaMa091_t* sdLaMa091);
    }
  }
}

--#-- END ./bgslibrary/algorithms/SigmaDelta/sdLaMa091.h --#--

--#-- START ./bgslibrary/algorithms/IBGS.h --#--
#pragma once

#include <iostream>
#include <fstream>
#include <list>
#include <memory>
#include <string>
#include <functional>
#include <map>

#include <opencv2/opencv.hpp>

// opencv legacy includes
#include <opencv2/imgproc/types_c.h>
#include <opencv2/imgproc/imgproc_c.h>
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui_c.h>
#endif

#include "../utils/ILoadSaveConfig.h"

#if !defined(bgs_register)
#define bgs_register(x) static BGS_Register<x> register_##x(quote(x))
#endif

namespace bgslibrary
{
  namespace algorithms
  {
    class IBGS : public ILoadSaveConfig
    {
    private:
      friend std::ostream& operator<<(std::ostream& o, const std::shared_ptr<IBGS>& ibgs) {
        return ibgs.get()->dump(o);
      }
      friend std::ostream& operator<<(std::ostream& o, const IBGS *ibgs) {
        return ibgs->dump(o);
      }
      friend std::string to_string(const std::shared_ptr<IBGS>& ibgs) {
        std::ostringstream ss;
        ss << ibgs;
        return ss.str();
      }
    public:
      virtual std::ostream& dump(std::ostream& o) const {
        return o << getAlgorithmName();
      }
      std::string getAlgorithmName() const {
        return algorithmName;
      }
      void setShowOutput(const bool _showOutput) {
        showOutput = _showOutput;
      }
      cv::Mat apply(const cv::Mat &img_input) {
        setShowOutput(false);
        cv::Mat _img_foreground;
        cv::Mat _img_background;
        process(img_input, _img_foreground, _img_background);
                _img_background.copyTo(img_background);
        return _img_foreground;
      }
      cv::Mat getBackgroundModel() {
        return img_background;
      }
      IBGS(const std::string _algorithmName){
        //debug_construction(IBGS);
        algorithmName = _algorithmName;
      }
      IBGS(){
        //debug_construction(IBGS);
        algorithmName = "";
      }
      virtual ~IBGS() {
        //debug_destruction(IBGS);
      }
      virtual void process(const cv::Mat &img_input, cv::Mat &img_foreground, cv::Mat &img_background) = 0;
    protected:
      std::string algorithmName;
      bool firstTime = true;
      bool showOutput = true;
      cv::Mat img_background;
      cv::Mat img_foreground;
      void init(const cv::Mat &img_input, cv::Mat &img_outfg, cv::Mat &img_outbg) {
        assert(img_input.empty() == false);
        //img_outfg = cv::Mat::zeros(img_input.size(), img_input.type());
        //img_outbg = cv::Mat::zeros(img_input.size(), img_input.type());
        img_outfg = cv::Mat::zeros(img_input.size(), CV_8UC1);
        img_outbg = cv::Mat::zeros(img_input.size(), CV_8UC3);
      }
    };
    
    class BGS_Factory
    {
    public:
      BGS_Factory() {
        //debug_construction(BGS_Factory);
      }
      virtual ~BGS_Factory() {
        //debug_destruction(BGS_Factory);
      }
      static BGS_Factory* Instance() {
        static BGS_Factory factory;
        return &factory;
      }

      std::shared_ptr<IBGS> Create(std::string name) {
        IBGS* instance = nullptr;

        // find name in the registry and call factory method.
        auto it = factoryFunctionRegistry.find(name);
        if (it != factoryFunctionRegistry.end())
          instance = it->second();

        // wrap instance in a shared ptr and return
        if (instance != nullptr)
          return std::shared_ptr<IBGS>(instance);
        else
          return nullptr;
      }

      std::vector<std::string> GetRegisteredAlgorithmsName() {
        std::vector<std::string> algorithmsName;
        for (auto it = factoryFunctionRegistry.begin(); it != factoryFunctionRegistry.end(); ++it) {
          algorithmsName.push_back(it->first);
        }
        return algorithmsName;
      }

      void RegisterFactoryFunction(std::string name,
        std::function<IBGS*(void)> classFactoryFunction) {
        // register the class factory function
        factoryFunctionRegistry[name] = classFactoryFunction;
      }
      
    private:
      std::map<std::string, std::function<IBGS*(void)>> factoryFunctionRegistry;
    };

    template<class T>
    class BGS_Register
    {
    public:
      BGS_Register(const std::string className) {
        //debug_construction(BGS_Register);
        // register the class factory function
        BGS_Factory::Instance()->RegisterFactoryFunction(className,
          [](void) -> IBGS* { return new T(); });
      }
      virtual ~BGS_Register() {
        //debug_destruction(BGS_Register);
      }
    };
  }
}

--#-- END ./bgslibrary/algorithms/IBGS.h --#--

--#-- START ./bgslibrary/algorithms/StaticFrameDifference.h --#--
#pragma once

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class StaticFrameDifference : public IBGS
    {
    private:
      bool enableThreshold;
      int threshold;

    public:
      StaticFrameDifference();
      ~StaticFrameDifference();

      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);
    };

    bgs_register(StaticFrameDifference);
  }
}

--#-- END ./bgslibrary/algorithms/StaticFrameDifference.h --#--

--#-- START ./bgslibrary/algorithms/T2F/T2FGMM.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include "../dp/Bgs.h"
#include "../dp/GrimsonGMM.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      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;
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2F/T2FGMM.h --#--

--#-- START ./bgslibrary/algorithms/T2F/MRF.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include "T2FMRF.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      // base class
      class MRF
      {
      public:
        IplImage *in_image, *out_image;
        //image's width and height
        int width, height;

      public:
        MRF();

      protected:

        //////////////////////////////////////////////////////////////////////////
        //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;

        //////////////////////////////////////////////////////////////////////////
        // alpha value for MMD
        double alpha;

        //////////////////////////////////////////////////////////////////////////
        //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;
      };

      /************************************************************************/
      /* 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:
        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);
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2F/MRF.h --#--

--#-- START ./bgslibrary/algorithms/T2F/T2FMRF.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include "../dp/Bgs.h"
#include "../dp/GrimsonGMM.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      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;
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2F/T2FMRF.h --#--

--#-- START ./bgslibrary/algorithms/TwoPoints.h --#--
#pragma once

#include "IBGS.h"
#include "TwoPoints/two_points.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class TwoPoints : public IBGS
    {
    private:
      static const int DEFAULT_MATCH_THRESH = 20;
      static const int DEFAULT_UPDATE_FACTOR = 16;
      int matchingThreshold;
      int updateFactor;
      twopoints::twopointsModel_t* model;

    public:
      TwoPoints();
      ~TwoPoints();

      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);
    };

    bgs_register(TwoPoints);
  }
}

--#-- END ./bgslibrary/algorithms/TwoPoints.h --#--

--#-- START ./bgslibrary/algorithms/algorithms.h --#--
#pragma once

#include "FrameDifference.h"
#include "StaticFrameDifference.h"
#include "WeightedMovingMean.h"
#include "WeightedMovingVariance.h"
#include "MixtureOfGaussianV1.h" // Only for OpenCV == 2
#include "MixtureOfGaussianV2.h"
#include "AdaptiveBackgroundLearning.h"
#include "AdaptiveSelectiveBackgroundLearning.h"
#include "KNN.h" // Only for OpenCV >= 3
#include "GMG.h" // Only for OpenCV >= 2.4.3 and OpenCV < 3
#include "DPAdaptiveMedian.h" // Only for OpenCV 2 or 3
#include "DPGrimsonGMM.h" // Only for OpenCV 2 or 3
#include "DPZivkovicAGMM.h" // Only for OpenCV 2 or 3
#include "DPMean.h" // Only for OpenCV 2 or 3
#include "DPWrenGA.h" // Only for OpenCV 2 or 3
#include "DPPratiMediod.h" // Only for OpenCV 2 or 3
#include "DPEigenbackground.h" // Only for OpenCV 2 or 3
#include "DPTexture.h" // Only for OpenCV 2 or 3
#include "T2FGMM_UM.h" // Only for OpenCV 2 or 3
#include "T2FGMM_UV.h" // Only for OpenCV 2 or 3
#include "T2FMRF_UM.h" // Only for OpenCV 2 or 3
#include "T2FMRF_UV.h" // Only for OpenCV 2 or 3
#include "FuzzySugenoIntegral.h"
#include "FuzzyChoquetIntegral.h"
#include "LBSimpleGaussian.h"
#include "LBFuzzyGaussian.h"
#include "LBMixtureOfGaussians.h"
#include "LBAdaptiveSOM.h"
#include "LBFuzzyAdaptiveSOM.h"
#include "LBP_MRF.h" // Only for OpenCV 2 or OpenCV <= 3.4.7
#include "MultiLayer.h" // Only for OpenCV 2 or OpenCV <= 3.4.7
#include "PixelBasedAdaptiveSegmenter.h"
#include "VuMeter.h"
#include "KDE.h"
#include "IndependentMultimodal.h"
#include "MultiCue.h" // Only for OpenCV 2 or 3
#include "SigmaDelta.h"
#include "SuBSENSE.h"
#include "LOBSTER.h"
#include "PAWCS.h"
#include "TwoPoints.h"
#include "ViBe.h"
#include "CodeBook.h"

// #include "_template_/MyBGS.h"

using namespace bgslibrary::algorithms;

--#-- END ./bgslibrary/algorithms/algorithms.h --#--

--#-- START ./bgslibrary/algorithms/_template_/MyBGS.h --#--
#pragma once

#include <opencv2/opencv.hpp>

#include "../IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class MyBGS : public IBGS
    {
    private:
      cv::Mat img_previous;

    public:
      MyBGS();
      ~MyBGS();

      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);
    };

    bgs_register(MyBGS);
  }
}

--#-- END ./bgslibrary/algorithms/_template_/MyBGS.h --#--

--#-- START ./bgslibrary/algorithms/KDE.h --#--
#pragma once

#include "IBGS.h"
#include "KDE/NPBGSubtractor.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class KDE : public IBGS
    {
    private:
      kde::NPBGSubtractor *p;
      int rows;
      int cols;
      int color_channels;
      int SequenceLength;
      int TimeWindowSize;
      int SDEstimationFlag;
      int lUseColorRatiosFlag;
      double th;
      double alpha;
      int framesToLearn;
      int frameNumber;

      unsigned char *FGImage;
      unsigned char *FilteredFGImage;
      unsigned char **DisplayBuffers;

    public:
      KDE();
      ~KDE();

      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);
    };

    bgs_register(KDE);
  }
}

--#-- END ./bgslibrary/algorithms/KDE.h --#--

--#-- START ./bgslibrary/algorithms/FrameDifference.h --#--
#pragma once

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class FrameDifference : public IBGS
    {
    private:
      bool enableThreshold;
      int threshold;

    public:
      FrameDifference();
      ~FrameDifference();

      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);
    };

    bgs_register(FrameDifference);
  }
}

--#-- END ./bgslibrary/algorithms/FrameDifference.h --#--

--#-- START ./bgslibrary/algorithms/PBAS/PBAS.h --#--
#pragma once

#include <iostream>
#include <opencv2/opencv.hpp>
//#include <highgui.h>

namespace bgslibrary
{
  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;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/PBAS/PBAS.h --#--

--#-- START ./bgslibrary/algorithms/MixtureOfGaussianV2.h --#--
#pragma once

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/video/background_segm.hpp>

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class MixtureOfGaussianV2 : public IBGS
    {
    private:
#if CV_MAJOR_VERSION == 2
      cv::BackgroundSubtractorMOG2 mog;
#elif CV_MAJOR_VERSION >= 3
      cv::Ptr<cv::BackgroundSubtractorMOG2> mog;
#endif
      double alpha;
      bool enableThreshold;
      int threshold;

    public:
      MixtureOfGaussianV2();
      ~MixtureOfGaussianV2();

      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);
    };

    bgs_register(MixtureOfGaussianV2);
  }
}

--#-- END ./bgslibrary/algorithms/MixtureOfGaussianV2.h --#--

--#-- START ./bgslibrary/algorithms/IndependentMultimodal.h --#--
#pragma once

#include "IBGS.h"
#include "IMBS/IMBS.hpp"

namespace bgslibrary
{
  namespace algorithms
  {
    class IndependentMultimodal : public IBGS
    {
    private:
      imbs::BackgroundSubtractorIMBS* pIMBS;
      int fps;

    public:
      IndependentMultimodal();
      ~IndependentMultimodal();

      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);
    };

    bgs_register(IndependentMultimodal);
  }
}

--#-- END ./bgslibrary/algorithms/IndependentMultimodal.h --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelMog.h --#--
#pragma once

#include "BGModel.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lb
    {
      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();
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/BGModelMog.h --#--

--#-- START ./bgslibrary/algorithms/lb/Types.h --#--
#pragma once

#include <opencv2/opencv.hpp>
// opencv legacy includes
#include <opencv2/core/core_c.h>

namespace bgslibrary
{
  namespace algorithms
  {
    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;
        */
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/Types.h --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelGauss.h --#--
#pragma once

#include "BGModel.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lb
    {
      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);

      protected:
        double m_alpha;
        double m_threshold;
        double m_noise;

        DBLRGB* m_pMu;
        DBLRGB* m_pVar;

        void Init();
        void Update();
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/BGModelGauss.h --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelFuzzySom.h --#--
#pragma once

#include "BGModel.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lb
    {
      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();
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/BGModelFuzzySom.h --#--

--#-- START ./bgslibrary/algorithms/lb/BGModel.h --#--
#pragma once

#include <math.h>
#include <float.h>

#include <opencv2/opencv.hpp>

#include "Types.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lb
    {
      class BGModel
      {
      public:

        BGModel(int width, int height);
        virtual ~BGModel();

        void InitModel(IplImage* image);
        void UpdateModel(IplImage* image);

        virtual void setBGModelParameter(int id, int value) {};

        virtual IplImage* GetSrc();
        virtual IplImage* GetFG();
        virtual IplImage* GetBG();

      protected:

        IplImage* m_SrcImage;
        IplImage* m_BGImage;
        IplImage* m_FGImage;

        const unsigned int m_width;
        const unsigned int m_height;

        virtual void Init() = 0;
        virtual void Update() = 0;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/BGModel.h --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelFuzzyGauss.h --#--
#pragma once

#include "BGModel.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lb
    {
      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);

      protected:
        double m_alphamax;
        double m_threshold;
        double m_threshBG;
        double m_noise;

        DBLRGB* m_pMu;
        DBLRGB* m_pVar;

        void Init();
        void Update();
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/BGModelFuzzyGauss.h --#--

--#-- START ./bgslibrary/algorithms/lb/BGModelSom.h --#--
#pragma once

#include "BGModel.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lb
    {
      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();
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/lb/BGModelSom.h --#--

--#-- START ./bgslibrary/algorithms/VuMeter/TBackground.h --#--
#pragma once

#include <iostream>
#include <opencv2/opencv.hpp>
// opencv legacy includes
#include <opencv2/core/core_c.h>
#include <opencv2/imgproc/imgproc_c.h>

namespace bgslibrary
{
  namespace algorithms
  {
    namespace vumeter
    {
      class TBackground
      {
      public:
        TBackground();
        virtual ~TBackground();

        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 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);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/VuMeter/TBackground.h --#--

--#-- START ./bgslibrary/algorithms/VuMeter/TBackgroundVuMeter.h --#--
#pragma once

#include "TBackground.h"

namespace bgslibrary
{
  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);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/VuMeter/TBackgroundVuMeter.h --#--

--#-- START ./bgslibrary/algorithms/AdaptiveBackgroundLearning.h --#--
#pragma once

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class AdaptiveBackgroundLearning : public IBGS
    {
    private:
      double alpha;
      int maxLearningFrames;
      long currentLearningFrame;
      double minVal;
      double maxVal;
      bool enableThreshold;
      int threshold;

    public:
      AdaptiveBackgroundLearning();
      ~AdaptiveBackgroundLearning();

      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);
    };

    bgs_register(AdaptiveBackgroundLearning);
  }
}

--#-- END ./bgslibrary/algorithms/AdaptiveBackgroundLearning.h --#--

--#-- START ./bgslibrary/algorithms/DPEigenbackground.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "dp/Eigenbackground.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPEigenbackground : public IBGS
    {
    private:
      long frameNumber;
      int threshold;
      int historySize;
      int embeddedDim;
      dp::RgbImage frame_data;
      dp::EigenbackgroundParams params;
      dp::Eigenbackground bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPEigenbackground();
      ~DPEigenbackground();

      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);
    };

    bgs_register(DPEigenbackground);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPEigenbackground.h --#--

--#-- START ./bgslibrary/algorithms/DPPratiMediod.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "dp/PratiMediodBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPPratiMediod : public IBGS
    {
    private:
      long frameNumber;
      int threshold;
      int samplingRate;
      int historySize;
      int weight;
      dp::RgbImage frame_data;
      dp::PratiParams params;
      dp::PratiMediodBGS bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPPratiMediod();
      ~DPPratiMediod();

      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);
    };

    bgs_register(DPPratiMediod);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPPratiMediod.h --#--

--#-- START ./bgslibrary/algorithms/T2FMRF_UM.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "IBGS.h"
#include "T2F/MRF.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class T2FMRF_UM : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      float km;
      float kv;
      int gaussians;
      int width;
      int height;
      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();
      ~T2FMRF_UM();

      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);
    };

    bgs_register(T2FMRF_UM);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2FMRF_UM.h --#--

--#-- START ./bgslibrary/algorithms/WeightedMovingMean.h --#--
#pragma once

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class WeightedMovingMean : public IBGS
    {
    private:
      cv::Mat img_input_prev_1;
      cv::Mat img_input_prev_2;
      bool enableWeight;
      bool enableThreshold;
      int threshold;

    public:
      WeightedMovingMean();
      ~WeightedMovingMean();

      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);
    };

    bgs_register(WeightedMovingMean);
  }
}

--#-- END ./bgslibrary/algorithms/WeightedMovingMean.h --#--

--#-- START ./bgslibrary/algorithms/T2FGMM_UM.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "IBGS.h"
#include "T2F/T2FGMM.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class T2FGMM_UM : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      float km;
      float kv;
      int gaussians;
      dp::RgbImage frame_data;
      dp::T2FGMMParams params;
      dp::T2FGMM bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      T2FGMM_UM();
      ~T2FGMM_UM();

      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);
    };

    bgs_register(T2FGMM_UM);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/T2FGMM_UM.h --#--

--#-- START ./bgslibrary/algorithms/DPZivkovicAGMM.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "dp/ZivkovicAGMM.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPZivkovicAGMM : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      int gaussians;
      dp::RgbImage frame_data;
      dp::ZivkovicParams params;
      dp::ZivkovicAGMM bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPZivkovicAGMM();
      ~DPZivkovicAGMM();

      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);
    };

    bgs_register(DPZivkovicAGMM);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPZivkovicAGMM.h --#--

--#-- START ./bgslibrary/algorithms/MultiCue.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#if !defined(__APPLE__)
#include <malloc.h>
#endif
#include "math.h"

#include <vector>
#include <algorithm>
#include <opencv2/opencv.hpp>

#include "IBGS.h"

//------------------------------------Structure Lists-------------------------------------//
namespace bgslibrary
{
  namespace algorithms
  {
    namespace multiCue
    {
      typedef int BOOL;

      struct point {
        short m_nX;
        short m_nY;
      };

      struct neighbor_pos {
        short m_nX;
        short m_nY;
      };
      //1) Bounding Box Structure
      struct BoundingBoxInfo {
        int m_iBoundBoxNum;										//# of bounding boxes for all foreground and false-positive blobs
        int m_iArraySize;										//the size of the below arrays to store bounding box information

        short *m_aLeft, *m_aRight, *m_aUpper, *m_aBottom;		//arrays to store bounding box information for (the original frame size)
        short *m_aRLeft, *m_aRRight, *m_aRUpper, *m_aRBottom;	//arrays to store bounding box information for (the reduced frame size)
        BOOL* m_ValidBox;										//If this value is true, the corresponding bounding box is for a foreground blob.
                                            //Else, it is for a false-positive blob
      };

      //2) Texture Model Structure
      struct TextureCodeword {
        int m_iMNRL;											//the maximum negative run-length
        int m_iT_first_time;									//the first access time
        int m_iT_last_time;										//the last access time

        float m_fLowThre;										//a low threshold for the matching
        float m_fHighThre;										//a high threshold for the matching
        float m_fMean;											//mean of the codeword
      };

      struct TextureModel {
        TextureCodeword** m_Codewords;							//the texture-codeword Array

        int m_iTotal;											//# of learned samples after the last clear process
        int m_iElementArraySize;								//the array size of m_Codewords
        int m_iNumEntries;										//# of codewords

        BOOL m_bID;												//id=1 --> background model, id=0 --> cachebook
      };

      //3) Color Model Structure
      struct ColorCodeword {
        int m_iMNRL;											//the maximum negative run-length
        int m_iT_first_time;									//the first access time
        int m_iT_last_time;										//the last access time

        double m_dMean[3];										//mean vector of the codeword

      };

      struct ColorModel {
        ColorCodeword** m_Codewords;							//the color-codeword Array

        int m_iTotal;											//# of learned samples after the last clear process
        int m_iElementArraySize;								//the array size of m_Codewords
        int m_iNumEntries;										//# of codewords

        BOOL m_bID;												//id=1 --> background model, id=0 --> cachebookk
      };
    }
  }
}

namespace bgslibrary
{
  namespace algorithms
  {
    //using namespace bgslibrary::algorithms::multiCue;

    class MultiCue : public IBGS
    {
    private:
      void save_config(cv::FileStorage &fs);
      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();
      //----------------------------------------------------
      //		APIs and User-Adjustable Parameters
      //----------------------------------------------------
      void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel);			//the main function to background modeling and subtraction

      void GetForegroundMap(IplImage* return_image, IplImage* input_frame = NULL);					//the function returning a foreground binary-map
      void Destroy();																				//the function to release allocated memories

      int g_iTrainingPeriod;										//the training period								(The parameter t in the paper)
      int g_iT_ModelThreshold;									//the threshold for texture-model based BGS.		(The parameter tau_T in the paper)
      int g_iC_ModelThreshold;									//the threshold for appearance based verification.  (The parameter tau_A in the paper)

      float g_fLearningRate;										//the learning rate for background models.			(The parameter alpha in the paper)

      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)

      //----------------------------------------------------
      //	Implemented Function Lists
      //----------------------------------------------------

      //--1) General Functions
      void Initialize(IplImage* frame);

      void PreProcessing(IplImage* frame);
      void ReduceImageSize(IplImage* SrcImage, IplImage* DstImage);
      void GaussianFiltering(IplImage* frame, uchar*** aFilteredFrame);
      void BGR2HSVxyz_Par(uchar*** aBGR, uchar*** aXYZ);

      void BackgroundModeling_Par(IplImage* frame);
      void ForegroundExtraction(IplImage* frame);
      void CreateLandmarkArray_Par(float fConfThre, short nTrainVolRange, float**aConfMap, int iNehborNum, uchar*** aXYZ,
        point*** aNeiDir, TextureModel**** TModel, ColorModel*** CModel, uchar**aLandmarkArr);

      void PostProcessing(IplImage* frame);
      void MorphologicalOpearions(uchar** aInput, uchar** aOutput, double dThresholdRatio, int iMaskSize, int iWidth, int iHeight);
      void Labeling(uchar** aBinaryArray, int* pLabelCount, int** aLabelTable);
      void SetBoundingBox(int iLabelCount, int** aLabelTable);
      void BoundBoxVerification(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo);
      void EvaluateBoxSize(BoundingBoxInfo* BoundBoxInfo);
      void EvaluateOverlapRegionSize(BoundingBoxInfo* SrcBoxInfo);
      void EvaluateGhostRegion(IplImage* frame, uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo);
      double CalculateHausdorffDist(IplImage* input_image, IplImage* model_image);
      void RemovingInvalidForeRegions(uchar** aResForeMap, BoundingBoxInfo* BoundBoxInfo);

      void UpdateModel_Par();
      void GetEnlargedMap(float** aOriginMap, float** aEnlargedMap);

      //--2) Texture Model Related Functions
      void T_AllocateTextureModelRelatedMemory();
      void T_ReleaseTextureModelRelatedMemory();
      void T_SetNeighborDirection(point*** aNeighborPos);
      void T_ModelConstruction(short nTrainVolRange, float fLearningRate, uchar*** aXYZ, point center, point* aNei, TextureModel** aModel);
      void T_ClearNonEssentialEntries(short nClearNum, TextureModel** aModel);
      void T_ClearNonEssentialEntriesForCachebook(uchar bLandmark, short* nReferredIdxArr, short nClearNum, TextureModel** pCachebook);
      void T_GetConfidenceMap_Par(uchar*** aXYZ, float** aTextureMap, point*** aNeiDirArr, TextureModel**** aModel);
      void T_Absorption(int iAbsorbCnt, point pos, short*** aContinuCnt, short*** aRefferedIndex, TextureModel** pModel, TextureModel** pCache);

      //--3) Color Model Related Functions
      void C_AllocateColorModelRelatedMemory();
      void C_ReleaseColorModelRelatedMemory();
      void C_CodebookConstruction(uchar* aP, int iPosX, int iPosY, short nTrainVolRange, float fLearningRate, ColorModel* pC);
      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);

      //----------------------------------------------------
      //	Implemented Variable Lists
      //----------------------------------------------------

      //--1) General Variables
      int g_iFrameCount;							//the counter of processed frames

      int g_iBackClearPeriod;						//the period to clear background models
      int g_iCacheClearPeriod;					//the period to clear cache-book models

      int g_iAbsortionPeriod;						//the period to absorb static ghost regions
      BOOL g_bAbsorptionEnable;					//If True, procedures for ghost region absorption are activated.

      BOOL g_bModelMemAllocated;					//To handle memory..
      BOOL g_bNonModelMemAllocated;				//To handle memory..

      float g_fConfidenceThre;					//the final decision threshold

      int g_iWidth, g_iHeight;					//width and height of input frames
      int g_iRWidth, g_iRHeight;					//width and height of reduced frames (For efficiency, the reduced size of frames are processed)
      int g_iForegroundNum;						//# of detected foreground regions
      BOOL g_bForegroundMapEnable;				//TRUE only when BGS is successful

      IplImage* g_ResizedFrame;					//reduced size of frame (For efficiency, the reduced size of frames are processed)
      uchar*** g_aGaussFilteredFrame;
      uchar*** g_aXYZFrame;
      uchar** g_aLandmarkArray;					//the landmark map
      uchar** g_aResizedForeMap;					//the resized foreground map
      uchar** g_aForegroundMap;					//the final foreground map
      BOOL** g_aUpdateMap;						//the location map of update candidate pixels

      BoundingBoxInfo* g_BoundBoxInfo;			//the array of bounding boxes of each foreground blob

                                            //--2) Texture Model Related
      TextureModel**** g_TextureModel;			//the texture background model
      TextureModel**** g_TCacheBook;				//the texture cache-book
      short*** g_aTReferredIndex;					//To handle cache-book
      short*** g_aTContinuousCnt;					//To handle cache-book
      point*** g_aNeighborDirection;
      float**g_aTextureConfMap;					//the texture confidence map

      short g_nNeighborNum;						//# of neighborhoods
      short g_nRadius;
      short g_nBoundarySize;

      //--3) Texture Model Related
      ColorModel*** g_ColorModel;					//the color background model
      ColorModel*** g_CCacheBook;					//the color cache-book
      short** g_aCReferredIndex;					//To handle cache-book
      short** g_aCContinuousCnt;					//To handle cache-book
    };

    bgs_register(MultiCue);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiCue.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if (CV_MAJOR_VERSION == 2) || (CV_MAJOR_VERSION == 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7)

#include "IBGS.h"
#include "MultiLayer/CMultiLayerBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class MultiLayer : public IBGS
    {
    public:
      enum Status
      {
        MLBGS_NONE = -1,
        MLBGS_LEARN = 0,
        MLBGS_DETECT = 1
      };

    private:
      long frameNumber;
      cv::Mat img_merged;
      bool saveModel;
      bool disableDetectMode;
      bool disableLearning;
      int detectAfter;
      multilayer::CMultiLayerBGS* BGS;
      Status status;
      //IplImage* img;
      IplImage* org_img;
      IplImage* fg_img;
      IplImage* bg_img;
      IplImage* fg_prob_img;
      IplImage* fg_mask_img;
      IplImage* fg_prob_img3;
      IplImage* merged_img;
      std::string bg_model_preload;

      bool loadDefaultParams;

      int max_mode_num;
      float weight_updating_constant;
      float texture_weight;
      float bg_mode_percent;
      int pattern_neig_half_size;
      float pattern_neig_gaus_sigma;
      float bg_prob_threshold;
      float bg_prob_updating_threshold;
      int robust_LBP_constant;
      float min_noised_angle;
      float shadow_rate;
      float highlight_rate;
      float bilater_filter_sigma_s;
      float bilater_filter_sigma_r;

      float frame_duration;

      float mode_learn_rate_per_second;
      float weight_learn_rate_per_second;
      float init_mode_weight;

      float learn_mode_learn_rate_per_second;
      float learn_weight_learn_rate_per_second;
      float learn_init_mode_weight;

      float detect_mode_learn_rate_per_second;
      float detect_weight_learn_rate_per_second;
      float detect_init_mode_weight;

    public:
      MultiLayer();
      ~MultiLayer();

      void setStatus(Status status);
      void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel);

    private:
      void finish();
      void save_config(cv::FileStorage &fs);
      void load_config(cv::FileStorage &fs);
    };

    bgs_register(MultiLayer);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer.h --#--

--#-- START ./bgslibrary/algorithms/KDE/NPBGSubtractor.h --#--
#pragma once

#include "NPBGmodel.h"
#include "KernelTable.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace kde
    {
      const int FALSE = 0;
      const int TRUE = 1;

      // 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 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);

      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);

        void AddFrame(unsigned char * ImageBuffer);

        void Estimation();

        void NBBGSubtraction(unsigned char *Frame,
          unsigned char *FGImage,
          unsigned char *FilteredFGImage,
          unsigned char **DisplayBuffers);

        void Update(unsigned char *);

        void SetThresholds(double th, double alpha)
        {
          Threshold = th;
          AlphaValue = alpha;
        };

        void SetUpdateFlag(unsigned int bgflag) {
          UpdateBGFlag = bgflag;
        };
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/KDE/NPBGSubtractor.h --#--

--#-- START ./bgslibrary/algorithms/KDE/NPBGmodel.h --#--
#pragma once

#include <iostream>

namespace bgslibrary
{
  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;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/KDE/NPBGmodel.h --#--

--#-- START ./bgslibrary/algorithms/KDE/KernelTable.h --#--
#pragma once

#include <iostream>

namespace bgslibrary
{
  namespace algorithms
  {
    namespace kde
    {
      class KernelLUTable
      {
      public:
        double minsegma;
        double maxsegma;
        int segmabins;
        int tablehalfwidth;
        double *kerneltable;
        double *kernelsums;

      public:
        KernelLUTable();
        ~KernelLUTable();

        KernelLUTable(int KernelHalfWidth, double Segmamin, double Segmamax, int Segmabins);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/KDE/KernelTable.h --#--

--#-- START ./bgslibrary/algorithms/GMG.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3

namespace bgslibrary
{
  namespace algorithms
  {
    class GMG : public IBGS
    {
    private:
      cv::Ptr<cv::BackgroundSubtractorGMG> fgbg;
      int initializationFrames;
      double decisionThreshold;
      cv::Mat img_segmentation;

    public:
      GMG();
      ~GMG();

      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);
    };

    bgs_register(GMG);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/GMG.h --#--

--#-- START ./bgslibrary/algorithms/WeightedMovingVariance.h --#--
#pragma once

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class WeightedMovingVariance : public IBGS
    {
    private:
      cv::Mat img_input_prev_1;
      cv::Mat img_input_prev_2;
      bool enableWeight;
      bool enableThreshold;
      int threshold;

    public:
      WeightedMovingVariance();
      ~WeightedMovingVariance();

      void process(const cv::Mat &img_input, cv::Mat &img_output, cv::Mat &img_bgmodel);
      cv::Mat computeWeightedVariance(const cv::Mat &img_input_f, const cv::Mat &img_mean_f, const double weight);

    private:
      void save_config(cv::FileStorage &fs);
      void load_config(cv::FileStorage &fs);
    };

    bgs_register(WeightedMovingVariance);
  }
}

--#-- END ./bgslibrary/algorithms/WeightedMovingVariance.h --#--

--#-- START ./bgslibrary/algorithms/SigmaDelta.h --#--
#pragma once

#include "IBGS.h"

//extern "C" {
#include "SigmaDelta/sdLaMa091.h"
//}

namespace bgslibrary
{
  namespace algorithms
  {
    class SigmaDelta : public IBGS
    {
    private:
      int ampFactor;
      int minVar;
      int maxVar;
      sigmadelta::sdLaMa091_t* algorithm;

    public:
      SigmaDelta();
      ~SigmaDelta();

      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);
      void applyParams();
    };

    bgs_register(SigmaDelta);
  }
}

--#-- END ./bgslibrary/algorithms/SigmaDelta.h --#--

--#-- START ./bgslibrary/algorithms/LBFuzzyGaussian.h --#--
#pragma once

#include "IBGS.h"
#include "lb/BGModelFuzzyGauss.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class LBFuzzyGaussian : public IBGS
    {
    private:
      lb::BGModel* m_pBGModel;
      int sensitivity;
      int bgThreshold;
      int learningRate;
      int noiseVariance;

    public:
      LBFuzzyGaussian();
      ~LBFuzzyGaussian();

      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);
    };

    bgs_register(LBFuzzyGaussian);
  }
}

--#-- END ./bgslibrary/algorithms/LBFuzzyGaussian.h --#--

--#-- START ./bgslibrary/algorithms/DPTexture.h --#--
#pragma once

#include "IBGS.h"

#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"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPTexture : public IBGS
    {
    private:
      int width;
      int height;
      int size;
      unsigned char* modeArray;
      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;
      //IplConvKernel* erodeElement;
      //bool enableFiltering;

    public:
      DPTexture();
      ~DPTexture();

      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);
    };

    bgs_register(DPTexture);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPTexture.h --#--

--#-- START ./bgslibrary/algorithms/ViBe.h --#--
#pragma once

#include "IBGS.h"
#include "ViBe/vibe-background-sequential.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class ViBe : public IBGS
    {
    private:
      static const int DEFAULT_NUM_SAMPLES = 20;
      static const int DEFAULT_MATCH_THRESH = 20;
      static const int DEFAULT_MATCH_NUM = 2;
      static const int DEFAULT_UPDATE_FACTOR = 16;

    private:
      //int numberOfSamples;
      int matchingThreshold;
      int matchingNumber;
      int updateFactor;
      vibe::vibeModel_Sequential_t* model;

    public:
      ViBe();
      ~ViBe();

      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);
    };

    bgs_register(ViBe);
  }
}

--#-- END ./bgslibrary/algorithms/ViBe.h --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/graph.h --#--
#pragma once

#include "block.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lbp_mrf
    {
      /*
        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);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBP_MRF/graph.h --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/block.h --#--
#pragma once

#include <stdlib.h>
#include <stdio.h>

namespace bgslibrary
{
  namespace algorithms
  {
    namespace lbp_mrf
    {
      template <class Type> class Block
      {
      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)
        {
          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;
        }

        /* 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++;
        }

        /* 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;

        int		block_size;
        block	*first;
        block	*last;

        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)
          {
            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;
        }

        /***********************************************************************/

      private:

        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;

        int			block_size;
        block		*first;
        block_item	*first_free;

        void(*error_function)(char *);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBP_MRF/block.h --#--

--#-- START ./bgslibrary/algorithms/DPGrimsonGMM.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "dp/GrimsonGMM.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPGrimsonGMM : public IBGS
    {
    private:
      long frameNumber;
      double threshold;
      double alpha;
      int gaussians;
      dp::RgbImage frame_data;
      dp::GrimsonParams params;
      dp::GrimsonGMM bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPGrimsonGMM();
      ~DPGrimsonGMM();

      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);
    };

    bgs_register(DPGrimsonGMM);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPGrimsonGMM.h --#--

--#-- START ./bgslibrary/algorithms/LBAdaptiveSOM.h --#--
#pragma once

#include "IBGS.h"
#include "lb/BGModelSom.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class LBAdaptiveSOM : public IBGS
    {
    private:
      lb::BGModel* m_pBGModel;
      int sensitivity;
      int trainingSensitivity;
      int learningRate;
      int trainingLearningRate;
      int trainingSteps;

    public:
      LBAdaptiveSOM();
      ~LBAdaptiveSOM();

      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);
    };

    bgs_register(LBAdaptiveSOM);
  }
}

--#-- END ./bgslibrary/algorithms/LBAdaptiveSOM.h --#--

--#-- START ./bgslibrary/algorithms/dp/GrimsonGMM.h --#--
#pragma once

#include "Bgs.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    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 Grimson GMM BGS algorithm ---
    class GrimsonParams : 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; }

    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;
    };

    // --- Grimson GMM BGS algorithm ---
    class GrimsonGMM : public Bgs
    {
    public:
      GrimsonGMM();
      ~GrimsonGMM();

      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
      GrimsonParams 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;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/GrimsonGMM.h --#--

--#-- START ./bgslibrary/algorithms/dp/Error.h --#--
#pragma once

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();
    }
  }
}

--#-- END ./bgslibrary/algorithms/dp/Error.h --#--

--#-- START ./bgslibrary/algorithms/dp/ZivkovicAGMM.h --#--
#pragma once

#include "Bgs.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    // --- User adjustable parameters used by the Grimson GMM BGS algorithm ---
    class ZivkovicParams : 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; }

    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 (not standard deviations)
      // 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;
    };

    // --- Zivkovic AGMM BGS algorithm ---
    class ZivkovicAGMM : public Bgs
    {
    private:
      struct GMM
      {
        float sigma;
        float muR;
        float muG;
        float muB;
        float weight;
      };

    public:
      ZivkovicAGMM();
      ~ZivkovicAGMM();

      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() { return &m_background; }

    private:
      void SubtractPixel(long posPixel, const RgbPixel& pixel, unsigned char* pModesUsed,
        unsigned char& lowThreshold, unsigned char& highThreshold);

      // User adjustable parameters
      ZivkovicParams 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;

      // This is related to the number of samples needed to accept that a component
      // actually exists.
      float m_complexity_prior;

      //data
      int m_num_bands;	//only RGB now ==3

      // dynamic array for the mixture of Gaussians
      GMM* m_modes;

      RgbImage m_background;

      //number of Gaussian components per pixel
      unsigned char* m_modes_per_pixel;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/ZivkovicAGMM.h --#--

--#-- START ./bgslibrary/algorithms/dp/TextureBGS.h --#--
#pragma once

#include <math.h>
#include "Image.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  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

--#-- END ./bgslibrary/algorithms/dp/TextureBGS.h --#--

--#-- START ./bgslibrary/algorithms/dp/Eigenbackground.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include "Bgs.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    // --- Parameters used by the Mean BGS algorithm ---
    class EigenbackgroundParams : public BgsParams
    {
    public:
      float &LowThreshold() { return m_low_threshold; }
      float &HighThreshold() { return m_high_threshold; }

      int &HistorySize() { return m_history_size; }
      int &EmbeddedDim() { return m_dim; }

    private:
      // A pixel will be classified as foreground if the squared distance of any
      // color channel is greater than the specified threshold
      float m_low_threshold;
      float m_high_threshold;

      int m_history_size;			// number frames used to create eigenspace
      int m_dim;							// eigenspace dimensionality
    };

    // --- Eigenbackground BGS algorithm ---
    class Eigenbackground : public Bgs
    {
    public:
      Eigenbackground();
      ~Eigenbackground();

      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() { return &m_background; }

    private:
      void UpdateHistory(int frameNum, const RgbImage& newFrame);

      EigenbackgroundParams m_params;

      CvMat* m_pcaData;
      CvMat* m_pcaAvg;
      CvMat* m_eigenValues;
      CvMat* m_eigenVectors;

      RgbImage m_background;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/Eigenbackground.h --#--

--#-- START ./bgslibrary/algorithms/dp/MeanBGS.h --#--
#pragma once

#include "Bgs.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    // --- Parameters used by the Mean BGS algorithm ---
    class MeanParams : public BgsParams
    {
    public:
      unsigned int &LowThreshold() { return m_low_threshold; }
      unsigned int &HighThreshold() { return m_high_threshold; }

      float &Alpha() { return m_alpha; }
      int &LearningFrames() { return m_learning_frames; }

    private:
      // A pixel is considered to be from the background if the squared distance between
      // it and the background model is less than the threshold.
      unsigned int m_low_threshold;
      unsigned int m_high_threshold;

      float m_alpha;
      int m_learning_frames;
    };


    // --- Mean BGS algorithm ---
    class MeanBGS : public Bgs
    {
    public:
      virtual ~MeanBGS() {}

      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() { return &m_background; }

    private:
      void SubtractPixel(int r, int c, const RgbPixel& pixel,
        unsigned char& lowThreshold, unsigned char& highThreshold);

      MeanParams m_params;

      RgbImageFloat m_mean;
      RgbImage m_background;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/MeanBGS.h --#--

--#-- START ./bgslibrary/algorithms/dp/Bgs.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include "Image.h"
#include "BgsParams.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    class Bgs
    {
    public:
      static const int BACKGROUND = 0;
      static const int FOREGROUND = 255;

      virtual ~Bgs() {}

      // Initialize any data required by the BGS algorithm. Should be called once before calling
      // any of the following functions.
      virtual void Initalize(const BgsParams& param) = 0;

      // Initialize the background model. Typically, the background model is initialized using the first
      // frame of the incoming video stream, but alternatives are possible.
      virtual void InitModel(const RgbImage& data) = 0;

      // Subtract the current frame from the background model and produce a binary foreground mask using
      // both a low and high threshold value.
      virtual void Subtract(int frame_num, const RgbImage& data,
                            BwImage& low_threshold_mask, BwImage& high_threshold_mask) = 0;

      // Update the background model. Only pixels set to background in update_mask are updated.
      virtual void Update(int frame_num, const RgbImage& data,  const BwImage& update_mask) = 0;

      // Return the current background model.
      virtual RgbImage *Background() = 0;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/Bgs.h --#--

--#-- START ./bgslibrary/algorithms/dp/WrenGA.h --#--
#pragma once

#include "Bgs.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    // --- Parameters used by the Mean BGS algorithm ---
    class WrenParams : public BgsParams
    {
    public:
      float &LowThreshold() { return m_low_threshold; }
      float &HighThreshold() { return m_high_threshold; }

      float &Alpha() { return m_alpha; }
      int &LearningFrames() { return m_learning_frames; }

    private:
      // The threshold indicates the number of variances (not standard deviations) away
      // from the mean before a pixel is considered to be from the foreground.
      float m_low_threshold;
      float m_high_threshold;

      float m_alpha;
      int m_learning_frames;
    };

    // --- Mean BGS algorithm ---
    class WrenGA : public Bgs
    {
    private:
      struct GAUSSIAN
      {
        float mu[NUM_CHANNELS];
        float var[NUM_CHANNELS];
      };

    public:
      WrenGA();
      ~WrenGA();

      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() { return &m_background; }

    private:
      void SubtractPixel(int r, int c, const RgbPixel& pixel,
        unsigned char& lowThreshold, unsigned char& highThreshold);

      WrenParams m_params;

      // Initial variance for the newly generated components.
      float m_variance;

      // dynamic array for the mixture of Gaussians
      GAUSSIAN* m_gaussian;

      RgbImage m_background;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/WrenGA.h --#--

--#-- START ./bgslibrary/algorithms/dp/AdaptiveMedianBGS.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include <iostream>
#include <stdlib.h>
#include <cmath>

#include "Bgs.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    // --- Parameters used by the Adaptive Median BGS algorithm ---
    class AdaptiveMedianParams : public BgsParams
    {
    public:
      unsigned char &LowThreshold() { return m_low_threshold; }
      unsigned char &HighThreshold() { return m_high_threshold; }

      int &SamplingRate() { return m_samplingRate; }
      int &LearningFrames() { return m_learning_frames; }

    private:
      unsigned char m_low_threshold;
      unsigned char m_high_threshold;

      int m_samplingRate;
      int m_learning_frames;
    };


    // --- Adaptive Median BGS algorithm ---
    class AdaptiveMedianBGS : public Bgs
    {
    public:
      virtual ~AdaptiveMedianBGS() {}

      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(int r, int c, const RgbPixel& pixel,
        unsigned char& low_threshold, unsigned char& high_threshold);

      AdaptiveMedianParams m_params;

      RgbImage m_median;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/AdaptiveMedianBGS.h --#--

--#-- START ./bgslibrary/algorithms/dp/BgsParams.h --#--
#pragma once

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    class BgsParams
    {
    public:
      virtual ~BgsParams() {}

      virtual void SetFrameSize(unsigned int width, unsigned int height)
      {
        m_width = width;
        m_height = height;
        m_size = width*height;
      }

      unsigned int &Width() { return m_width; }
      unsigned int &Height() { return m_height; }
      unsigned int &Size() { return m_size; }

    protected:
      unsigned int m_width;
      unsigned int m_height;
      unsigned int m_size;
    };
    }
  }
}

--#-- END ./bgslibrary/algorithms/dp/BgsParams.h --#--

--#-- START ./bgslibrary/algorithms/dp/PratiMediodBGS.h --#--
#pragma once

#include <vector>
#include "Bgs.h"

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
    // --- Parameters used by the Prati Mediod BGS algorithm ---
    class PratiParams : public BgsParams
    {
    public:
      unsigned int &LowThreshold() { return m_low_threshold; }
      unsigned int &HighThreshold() { return m_high_threshold; }

      int &Weight() { return m_weight; }
      int &SamplingRate() { return m_sampling_rate; }
      int &HistorySize() { return m_history_size; }

    private:
      // The low threshold is used to supress noise. The high thresohld is used
      // to find pixels highly likely to be foreground. This implementation uses an L-inf
      // distance measure and a pixel p is considered F/G if D(I(p), B(p)) > threshold.
      // The two threshold maps are combined as in [2].
      unsigned int m_low_threshold;
      unsigned int m_high_threshold;

      // The weight parameter controls the amount of influence given to previous background samples
      // see w_b in equation (2) of [1]
      // in [2] this value is set to 1
      int m_weight;

      // Number of samples to consider when calculating temporal mediod value
      int m_history_size;

      // Rate at which to obtain new samples
      int m_sampling_rate;
    };

    // --- Prati Mediod BGS algorithm ---
    class PratiMediodBGS : public Bgs
    {
    private:
      // sum of L-inf distances from a sample point to all other sample points
      struct MEDIAN_BUFFER
      {
        std::vector<RgbPixel> pixels;		// vector of pixels at give location in image
        std::vector<int> dist;					// distance from pixel to all other pixels
        int pos;												// current position in circular buffer

        RgbPixel median;								// median at this pixel location
        int medianDist;									// distance from median pixel to all other pixels
      };

    public:
      PratiMediodBGS();
      ~PratiMediodBGS();

      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() { return &m_background; }

    private:
      MEDIAN_BUFFER* m_median_buffer;

      void CalculateMasks(int r, int c, const RgbPixel& pixel);
      void Combine(const BwImage& low_mask, const BwImage& high_mask, BwImage& output);
      void UpdateMediod(int r, int c, const RgbImage& new_frame, int& dist);

      PratiParams m_params;

      RgbImage m_background;

      BwImage m_mask_low_threshold;
      BwImage m_mask_high_threshold;
    };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/PratiMediodBGS.h --#--

--#-- START ./bgslibrary/algorithms/dp/Image.h --#--
#pragma once

#include <opencv2/opencv.hpp>

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

namespace bgslibrary
{
  namespace algorithms
  {
    namespace dp
    {
      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);
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/dp/Image.h --#--

--#-- START ./bgslibrary/algorithms/FuzzyChoquetIntegral.h --#--
#pragma once

#include "IBGS.h"
#include "../tools/FuzzyUtils.h"

namespace bgslibrary
{
  namespace algorithms
  {
    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 frameNumber;
      int framesToLearn;
      double alphaLearn;
      double alphaUpdate;
      int colorSpace;
      int option;
      bool smooth;
      double threshold;
      FuzzyUtils fu;
      cv::Mat img_background_f3;
      
      void save_config(cv::FileStorage &fs);
      void load_config(cv::FileStorage &fs);
    };

    bgs_register(FuzzyChoquetIntegral);
  }
}

--#-- END ./bgslibrary/algorithms/FuzzyChoquetIntegral.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/OpenCvDataConversion.h --#--
#pragma once

#include <stdio.h>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

// opencv legacy includes
#include "OpenCvLegacyIncludes.h"

namespace bgslibrary
{
  namespace algorithms
  {
    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() {};
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/OpenCvDataConversion.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/OpenCvLegacyIncludes.h --#--
#pragma once

#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"

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/OpenCvLegacyIncludes.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/LocalBinaryPattern.h --#--
#pragma once

#include <cstdio>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "BGS.h"
#include "OpenCvDataConversion.h"

namespace bgslibrary
{
  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;
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/LocalBinaryPattern.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BlobLibraryConfiguration.h --#--
#pragma once

//! Indica si es volen fer servir les MatrixCV o no
//! Use/Not use the MatrixCV class
//#define MATRIXCV_ACTIU

// Uses/not use the blob object factory
//#define BLOB_OBJECT_FACTORY


--#-- END ./bgslibrary/algorithms/MultiLayer/BlobLibraryConfiguration.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/blob.h --#--
#pragma once

#include <functional>
#include <vector>
#include <algorithm>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

#include "BlobLibraryConfiguration.h"
// opencv legacy includes
#include "OpenCvLegacyIncludes.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace multilayer
    {
      namespace blob
      {
        //! 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;
        };
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/blob.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BGS.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3

// opencv legacy includes
#include "OpenCvLegacyIncludes.h"

#define BINARY_PATTERM_ELEM(c1, c2, offset)	\
  ((float)(c2)-(float)(c1)+offset > 0)

/*
#define BINARY_PATTERM_ELEM(c1, c2, offset)	\
( fabsf((float)(c2)-(float)(c1)) <= offset ? 1 : (float)(c2)-(float)(c1) >=0 )
*/

#ifndef PI
#define PI 3.141592653589793f
#endif

namespace bgslibrary
{
  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;
        }
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/BGS.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BackgroundSubtractionAPI.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

// opencv legacy includes
#include "OpenCvLegacyIncludes.h"

namespace bgslibrary
{
  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);

        //-------------------------------------------------------------
        // 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);

        //-------------------------------------------------------------
        //   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);

        //   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 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);
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/BackgroundSubtractionAPI.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BlobExtraction.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

namespace bgslibrary
{
  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

        //! 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);
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/BlobExtraction.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/CMultiLayerBGS.h --#--
#pragma once

/*
Since the used fast cross bilateral filter codes can not be compiled under Windows,
we don't use the bilateral filter to remove the noise in the foreground detection
step. If you compile it under Linux, please uncomment it.
*/

//#define LINUX_BILATERAL_FILTER

#include <stdio.h>
#include <stdarg.h>
#include <ctime>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <cmath>
#include <iostream>

#include <opencv2/imgproc.hpp>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "LocalBinaryPattern.h"
#include "BlobResult.h"
#include "OpenCvDataConversion.h"
#include "BackgroundSubtractionAPI.h"

#ifdef LINUX_BILATERAL_FILTER
#include "CrossBilateralFilter.h"
#endif

namespace bgslibrary
{
  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_fModeUpdatingLearnRatePerSecond;
        float	m_fWeightUpdatingLearnRatePerSecond;

        int m_nLBPLevelNum;
        float m_pLBPRadiuses[10];
        int m_pLBPMeigPointNums[10];

        CvRect* m_pROI;
        CMultiLayerBGS();
        virtual ~CMultiLayerBGS();
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/CMultiLayerBGS.h --#--

--#-- START ./bgslibrary/algorithms/MultiLayer/BlobResult.h --#--
#pragma once

#include <math.h>
#include <vector>
#include <functional>

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include "BlobLibraryConfiguration.h"
// opencv legacy includes
#include "OpenCvLegacyIncludes.h"
#include "blob.h"

namespace bgslibrary
{
  namespace algorithms
  {
    namespace multilayer
    {
      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;
        };
      }
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/MultiLayer/BlobResult.h --#--

--#-- START ./bgslibrary/algorithms/DPAdaptiveMedian.h --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "IBGS.h"
#include "dp/AdaptiveMedianBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPAdaptiveMedian : public IBGS
    {
    private:
      long frameNumber;
      int threshold;
      int samplingRate;
      int learningFrames;
      dp::RgbImage frame_data;
      dp::AdaptiveMedianParams params;
      dp::AdaptiveMedianBGS bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPAdaptiveMedian();
      ~DPAdaptiveMedian();

      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);
    };

    bgs_register(DPAdaptiveMedian);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPAdaptiveMedian.h --#--

--#-- START ./bgslibrary/algorithms/PAWCS.h --#--
#pragma once

#include "IBGS.h"
#include "LBSP/BackgroundSubtractorPAWCS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class PAWCS : public IBGS
    {
    private:
      lbsp::BackgroundSubtractorPAWCS* pPAWCS;

      float fRelLBSPThreshold;
      int nDescDistThresholdOffset;
      int nMinColorDistThreshold;
      int nMaxNbWords;
      int nSamplesForMovingAvgs;

    public:
      PAWCS();
      ~PAWCS();

      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);
    };

    bgs_register(PAWCS);
  }
}

--#-- END ./bgslibrary/algorithms/PAWCS.h --#--

--#-- START ./bgslibrary/algorithms/KNN.h --#--
#pragma once

#include <iostream>

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 3

#include <opencv2/opencv.hpp>
#include <opencv2/video/background_segm.hpp>

namespace bgslibrary
{
  namespace algorithms
  {
    class KNN : public IBGS
    {
    private:
      cv::Ptr<cv::BackgroundSubtractorKNN> knn;
      int history;
      float dist2Threshold;
      float shadowThreshold;

    public:
      KNN();
      ~KNN();

      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);
    };

    bgs_register(KNN);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/KNN.h --#--

--#-- START ./bgslibrary/algorithms/LBMixtureOfGaussians.h --#--
#pragma once

#include "IBGS.h"
#include "lb/BGModelMog.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class LBMixtureOfGaussians : public IBGS
    {
    private:
      lb::BGModel* m_pBGModel;
      int sensitivity;
      int bgThreshold;
      int learningRate;
      int noiseVariance;

    public:
      LBMixtureOfGaussians();
      ~LBMixtureOfGaussians();

      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);
    };

    bgs_register(LBMixtureOfGaussians);
  }
}

--#-- END ./bgslibrary/algorithms/LBMixtureOfGaussians.h --#--

--#-- START ./bgslibrary/algorithms/ViBe/vibe-background-sequential.h --#--
#pragma once

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

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
      );
    }
  }
}

--#-- END ./bgslibrary/algorithms/ViBe/vibe-background-sequential.h --#--

--#-- START ./bgslibrary/algorithms/FuzzySugenoIntegral.h --#--
#pragma once

#include "IBGS.h"
#include "../tools/FuzzyUtils.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class FuzzySugenoIntegral : public IBGS
    {
    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;
      int colorSpace;
      int option;
      bool smooth;
      double threshold;
      cv::Mat img_background_f3;
      FuzzyUtils fu;

      void save_config(cv::FileStorage &fs);
      void load_config(cv::FileStorage &fs);
    };

    bgs_register(FuzzySugenoIntegral);
  }
}

--#-- END ./bgslibrary/algorithms/FuzzySugenoIntegral.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/DistanceUtils.h --#--
#pragma once

// opencv legacy includes
#include <opencv2/core/types_c.h>

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));
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/DistanceUtils.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP_.h --#--
#pragma once

#include <opencv2/features2d/features2d.hpp>
#include <opencv2/video/background_segm.hpp>

#include "LBSP_.h"

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.

          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;

      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;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP_.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/RandUtils.h --#--
#pragma once

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,},
      };

      //! returns a random init/sampling position for the specified pixel position; also guards against out-of-bounds values via image/border size check.
      static inline void getRandSamplePosition(int& x_sample, int& y_sample, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) {
        int r = 1+rand()%s_nSamplesInitPatternTot;
        for(x_sample=0; x_sample<s_nSamplesInitPatternWidth; ++x_sample) {
          for(y_sample=0; y_sample<s_nSamplesInitPatternHeight; ++y_sample) {
            r -= s_anSamplesInitPattern[y_sample][x_sample];
            if(r<=0)
              goto stop;
          }
        }
      stop:
        x_sample += x_orig-s_nSamplesInitPatternWidth/2;
        y_sample += y_orig-s_nSamplesInitPatternHeight/2;
        if(x_sample<border)
          x_sample = border;
        else if(x_sample>=imgsize.width-border)
          x_sample = imgsize.width-border-1;
        if(y_sample<border)
          y_sample = border;
        else if(y_sample>=imgsize.height-border)
          y_sample = imgsize.height-border-1;
      }

      // simple 8-connected (3x3) neighbors pattern
      static const int s_anNeighborPatternSize_3x3 = 8;
      static const int s_anNeighborPattern_3x3[8][2] = {
        {-1, 1},  { 0, 1},  { 1, 1},
        {-1, 0},            { 1, 0},
        {-1,-1},  { 0,-1},  { 1,-1},
      };

      //! returns a random neighbor position for the specified pixel position; also guards against out-of-bounds values via image/border size check.
      static inline void getRandNeighborPosition_3x3(int& x_neighbor, int& y_neighbor, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) {
        int r = rand()%s_anNeighborPatternSize_3x3;
        x_neighbor = x_orig+s_anNeighborPattern_3x3[r][0];
        y_neighbor = y_orig+s_anNeighborPattern_3x3[r][1];
        if(x_neighbor<border)
          x_neighbor = border;
        else if(x_neighbor>=imgsize.width-border)
          x_neighbor = imgsize.width-border-1;
        if(y_neighbor<border)
          y_neighbor = border;
        else if(y_neighbor>=imgsize.height-border)
          y_neighbor = imgsize.height-border-1;
      }

      // 5x5 neighbors pattern
      static const int s_anNeighborPatternSize_5x5 = 24;
      static const int s_anNeighborPattern_5x5[24][2] = {
        {-2, 2},  {-1, 2},  { 0, 2},  { 1, 2},  { 2, 2},
        {-2, 1},  {-1, 1},  { 0, 1},  { 1, 1},  { 2, 1},
        {-2, 0},  {-1, 0},            { 1, 0},  { 2, 0},
        {-2,-1},  {-1,-1},  { 0,-1},  { 1,-1},  { 2,-1},
        {-2,-2},  {-1,-2},  { 0,-2},  { 1,-2},  { 2,-2},
      };

      //! returns a random neighbor position for the specified pixel position; also guards against out-of-bounds values via image/border size check.
      static inline void getRandNeighborPosition_5x5(int& x_neighbor, int& y_neighbor, const int x_orig, const int y_orig, const int border, const cv::Size& imgsize) {
        int r = rand()%s_anNeighborPatternSize_5x5;
        x_neighbor = x_orig+s_anNeighborPattern_5x5[r][0];
        y_neighbor = y_orig+s_anNeighborPattern_5x5[r][1];
        if(x_neighbor<border)
          x_neighbor = border;
        else if(x_neighbor>=imgsize.width-border)
          x_neighbor = imgsize.width-border-1;
        if(y_neighbor<border)
          y_neighbor = border;
        else if(y_neighbor>=imgsize.height-border)
          y_neighbor = imgsize.height-border-1;
      }
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/RandUtils.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorPAWCS.h --#--
#pragma once

#include "BackgroundSubtractorLBSP_.h"

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.

          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.

          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;

        //! 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;

        //! 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);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorPAWCS.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/LBSP.h --#--
#pragma once

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>

#include "DistanceUtils.h"

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(...).

        For more details on the different parameters, see G.-A. Bilodeau et al, "Change Detection in Feature Space Using Local
        Binary Similarity Patterns", in CRV 2013.

        This algorithm is currently NOT thread-safe.
      */
      class LBSP : public cv::DescriptorExtractor {
      public:
        //! constructor 1, threshold = absolute intensity 'similarity' threshold used when computing comparisons
        LBSP(size_t nThreshold);
        //! constructor 2, threshold = relative intensity 'similarity' threshold used when computing comparisons
        LBSP(float fRelThreshold, size_t nThresholdOffset = 0);
        //! default destructor
        virtual ~LBSP();
        //! loads extractor params from the specified file node @@@@ not impl
        virtual void read(const cv::FileNode&);
        //! writes extractor params to the specified file storage @@@@ not impl
        virtual void write(cv::FileStorage&) const;
        //! sets the 'reference' image to be used for inter-frame comparisons (note: if no image is set or if the image is empty, the algorithm will default back to intra-frame comparisons)
        virtual void setReference(const cv::Mat&);
        //! returns the current descriptor size, in bytes
        virtual int descriptorSize() const;
        //! returns the current descriptor data type
        virtual int descriptorType() const;
        //! returns whether this extractor is using a relative threshold or not
        virtual bool isUsingRelThreshold() const;
        //! returns the current relative threshold used for comparisons (-1 = invalid/not used)
        virtual float getRelThreshold() const;
        //! returns the current absolute threshold used for comparisons (-1 = invalid/not used)
        virtual size_t getAbsThreshold() const;

        //! similar to DescriptorExtractor::compute(const cv::Mat& image, ...), but in this case, the descriptors matrix has the same shape as the input matrix (possibly slower, but the result can be displayed)
        void compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const;
        //! batch version of LBSP::compute2(const cv::Mat& image, ...), also similar to DescriptorExtractor::compute(const std::vector<cv::Mat>& imageCollection, ...)
        void compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const;

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel version)
        inline static void computeGrayscaleDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _t, ushort& _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC1);
          CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP::PATCH_SIZE / 2 && _y >= (int)LBSP::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_1ch.i"
        }

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
        inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref, const int _x, const int _y, const size_t* const _t, ushort* _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC3);
          CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP::PATCH_SIZE / 2 && _y >= (int)LBSP::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_3ch3t.i"
        }

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
        inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref, const int _x, const int _y, const size_t _t, ushort* _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC3);
          CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP::PATCH_SIZE / 2 && _y >= (int)LBSP::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_3ch1t.i"
        }

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel-RGB version)
        inline static void computeSingleRGBDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _c, const size_t _t, ushort& _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC3 && _c < 3);
          CV_DbgAssert(LBSP::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP::PATCH_SIZE / 2 && _y >= (int)LBSP::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_s3ch.i"
        }

        //! utility function, used to reshape a descriptors matrix to its input image size via their keypoint locations
        static void reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput);
        //! utility function, used to illustrate the difference between two descriptor images
        static void calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels = false);
        //! utility function, used to filter out bad keypoints that would trigger out of bounds error because they're too close to the image border
        static void validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize);
        //! utility 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;

        const bool m_bOnlyUsingAbsThreshold;
        const float m_fRelThreshold;
        const size_t m_nThreshold;
        cv::Mat m_oRefImage;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/LBSP.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorSuBSENSE.h --#--
#pragma once

#include "BackgroundSubtractorLBSP.h"

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.

        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.

        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;

        //! 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;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorSuBSENSE.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLOBSTER.h --#--
#pragma once

#include "BackgroundSubtractorLBSP.h"

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.

        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.

        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;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLOBSTER.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP.h --#--
#pragma once

#include <opencv2/features2d/features2d.hpp>
#include <opencv2/video/background_segm.hpp>

#include "LBSP.h"

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.

        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;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/BackgroundSubtractorLBSP.h --#--

--#-- START ./bgslibrary/algorithms/LBSP/LBSP_.h --#--
#pragma once

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>

#include "DistanceUtils.h"

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(...).

          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;

        //! similar to DescriptorExtractor::compute(const cv::Mat& image, ...), but in this case, the descriptors matrix has the same shape as the input matrix (possibly slower, but the result can be displayed)
        void compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const;
        //! batch version of LBSP_::compute2(const cv::Mat& image, ...), also similar to DescriptorExtractor::compute(const std::vector<cv::Mat>& imageCollection, ...)
        void compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const;

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel version)
        inline static void computeGrayscaleDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _t, ushort& _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC1);
          CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP_::PATCH_SIZE / 2 && _y >= (int)LBSP_::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP_::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP_::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_1ch.i"
        }

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
        inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref, const int _x, const int _y, const size_t* const _t, ushort* _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC3);
          CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP_::PATCH_SIZE / 2 && _y >= (int)LBSP_::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP_::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP_::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_3ch3t.i"
        }

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
        inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref, const int _x, const int _y, const size_t _t, ushort* _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC3);
          CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP_::PATCH_SIZE / 2 && _y >= (int)LBSP_::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP_::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP_::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_3ch1t.i"
        }

        //! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel-RGB version)
        inline static void computeSingleRGBDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _c, const size_t _t, ushort& _res) {
          CV_DbgAssert(!oInputImg.empty());
          CV_DbgAssert(oInputImg.type() == CV_8UC3 && _c < 3);
          CV_DbgAssert(LBSP_::DESC_SIZE == 2); // @@@ also relies on a constant desc size
          CV_DbgAssert(_x >= (int)LBSP_::PATCH_SIZE / 2 && _y >= (int)LBSP_::PATCH_SIZE / 2);
          CV_DbgAssert(_x < oInputImg.cols - (int)LBSP_::PATCH_SIZE / 2 && _y < oInputImg.rows - (int)LBSP_::PATCH_SIZE / 2);
          const size_t _step_row = oInputImg.step.p[0];
          const uchar* const _data = oInputImg.data;
      #include "LBSP_16bits_dbcross_s3ch.i"
        }

        //! utility function, used to reshape a descriptors matrix to its input image size via their keypoint locations
        static void reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput);
        //! utility function, used to illustrate the difference between two descriptor images
        static void calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels = false);
        //! utility function, used to filter out bad keypoints that would trigger out of bounds error because they're too close to the image border
        static void validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize);
        //! utility 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;

        const bool m_bOnlyUsingAbsThreshold;
        const float m_fRelThreshold;
        const size_t m_nThreshold;
        cv::Mat m_oRefImage;
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/LBSP/LBSP_.h --#--

--#-- START ./bgslibrary/algorithms/LOBSTER.h --#--
#pragma once

#include "IBGS.h"

#include "LBSP/BackgroundSubtractorLOBSTER.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class LOBSTER : public IBGS
    {
    private:
      lbsp::BackgroundSubtractorLOBSTER* pLOBSTER;

      float fRelLBSPThreshold;
      int nLBSPThresholdOffset;
      int nDescDistThreshold;
      int nColorDistThreshold;
      int nBGSamples;
      int nRequiredBGSamples;

    public:
      LOBSTER();
      ~LOBSTER();

      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);
    };

    bgs_register(LOBSTER);
  }
}

--#-- END ./bgslibrary/algorithms/LOBSTER.h --#--

--#-- START ./bgslibrary/algorithms/AdaptiveSelectiveBackgroundLearning.h --#--
#pragma once

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class AdaptiveSelectiveBackgroundLearning : public IBGS
    {
    private:
      double alphaLearn;
      double alphaDetection;
      int learningFrames;
      long counter;
      double minVal;
      double maxVal;
      int threshold;

    public:
      AdaptiveSelectiveBackgroundLearning();
      ~AdaptiveSelectiveBackgroundLearning();

      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);
    };

    bgs_register(AdaptiveSelectiveBackgroundLearning);
  }
}

--#-- END ./bgslibrary/algorithms/AdaptiveSelectiveBackgroundLearning.h --#--

--#-- START ./bgslibrary/algorithms/DPMean.h --#--
#pragma once

#include "IBGS.h"

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION == 2 || CV_MAJOR_VERSION == 3

#include "dp/MeanBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    class DPMean : public IBGS
    {
    private:
      long frameNumber;
      int threshold;
      double alpha;
      int learningFrames;
      dp::RgbImage frame_data;
      dp::MeanParams params;
      dp::MeanBGS bgs;
      dp::BwImage lowThresholdMask;
      dp::BwImage highThresholdMask;

    public:
      DPMean();
      ~DPMean();

      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);
    };

    bgs_register(DPMean);
  }
}

#endif

--#-- END ./bgslibrary/algorithms/DPMean.h --#--

--#-- START ./bgslibrary/algorithms/TwoPoints/two_points.h --#--
#pragma once

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

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
      );
    }
  }
}

--#-- END ./bgslibrary/algorithms/TwoPoints/two_points.h --#--

--#-- START ./bgslibrary/algorithms/CodeBook.h --#--
#pragma once

#include <opencv2/opencv.hpp>

#include "IBGS.h"

namespace bgslibrary
{
  namespace algorithms
  {
    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;
      static const int Th = 200;
      const int DEFAULT_ALPHA = 10;
      const float DEFAULT_BETA = 1.;
      const int DEFAULT_LEARNFRAMES = 10;
      int t = 0;
      int learningFrames = 10;
      int alpha = 10;
      float beta = 1;
      std::vector<codeword> **cbMain;
      std::vector<codeword> **cbCache;

      void initializeCodebook(int w, int h);
      void update_cb(const cv::Mat& frame);
      void fg_cb(const cv::Mat& frame, cv::Mat& fg);
      
      void save_config(cv::FileStorage &fs);
      void load_config(cv::FileStorage &fs);
    };

    bgs_register(CodeBook);
  }
}

--#-- END ./bgslibrary/algorithms/CodeBook.h --#--

--#-- START ./bgslibrary/utils/GenericKeys.h --#--
#pragma once

namespace bgslibrary
{
  const int KEY_REPEAT = 'r';
  const int KEY_SPACE = 32;
  const int KEY_ESC = 27;
  const int KEY_ESC2 = 'q';
}

--#-- END ./bgslibrary/utils/GenericKeys.h --#--

--#-- START ./bgslibrary/utils/ILoadSaveConfig.h --#--
#pragma once

#include <iostream>
#include <fstream>
#include <string>

#include "GenericMacros.h"

namespace bgslibrary
{
  const std::string DEFAULT_CONFIG_BASE_PATH = "./config";
  const std::string DEFAULT_CONFIG_EXTENSION = ".xml";
  
  class ILoadSaveConfig
  {
  public:
    ILoadSaveConfig() :
    config_base_path(DEFAULT_CONFIG_BASE_PATH),
    config_extension(DEFAULT_CONFIG_EXTENSION),
    config_file_path("")
    {
      //debug_construction(ILoadSaveConfig);
    }
    virtual ~ILoadSaveConfig() {
      //debug_destruction(ILoadSaveConfig);
    }

  protected:
    std::string config_base_path;
    std::string config_extension;
    std::string config_file_path;
    //static const std::string config_base_path;
    //static const std::string config_extension;
    virtual void save_config(cv::FileStorage &fs) = 0;
    virtual void load_config(cv::FileStorage &fs) = 0;
    void initLoadSaveConfig(const std::string _config_file_name) {
      if(!_config_file_name.empty()) {
        config_file_path = config_base_path + "/" + _config_file_name + config_extension;
        if (!std::ifstream(config_file_path))
          _save_config();
        _load_config();
      }
    }
    
  private:
    void _save_config() {
      //std::cout << "_save_config: " << config_file_path << std::endl;
      cv::FileStorage fs(config_file_path, cv::FileStorage::WRITE);
      if (_is_valid(fs))
        save_config(fs);
      fs.release();
    }
    void _load_config() {
      //std::cout << "_load_config: " << config_file_path << std::endl;
      cv::FileStorage fs;
      fs.open(config_file_path, cv::FileStorage::READ);
      if (_is_valid(fs))
        load_config(fs);
      fs.release();
    }
    bool _is_valid(cv::FileStorage &fs) {
      if (!fs.isOpened()) {
        std::cerr << "Failed to open " << config_file_path << std::endl;
        //std::cerr << "Please check if the path above is valid" << std::endl;
        return false;
      }
      return true;
    }
  };
  //const std::string ILoadSaveConfig::config_base_path = "./config";
  //const std::string ILoadSaveConfig::config_extension = ".xml";
}

--#-- END ./bgslibrary/utils/ILoadSaveConfig.h --#--

--#-- START ./bgslibrary/utils/GenericMacros.h --#--
#pragma once

#include <iostream>

#define DEBUG_OBJ_LIFE

#if !defined(quote)
#define quote(x) #x
#endif

#if !defined(debug_construction)
#if defined(DEBUG_OBJ_LIFE)
#define debug_construction(x) std::cout << "+" << quote(x) << "()" << std::endl
#else
#define debug_construction(x)
#endif
#endif

#if !defined(debug_destruction)
#if defined(DEBUG_OBJ_LIFE)
#define debug_destruction(x) std::cout << "-" << quote(x) << "()" << std::endl
#else
#define debug_destruction(x)
#endif
#endif

--#-- END ./bgslibrary/utils/GenericMacros.h --#--

--#-- START ./bgslibrary/FrameProcessor.h --#--
#pragma once
#pragma warning(disable : 4482)

#include "IFrameProcessor.h"
#include "PreProcessor.h"

#include "algorithms/algorithms.h"
#include "tools/ForegroundMaskAnalysis.h"

namespace bgslibrary
{
  class FrameProcessor : public IFrameProcessor, public ILoadSaveConfig
  {
  private:
    bool firstTime;
    long frameNumber;
    std::string processname;
    double duration;
    std::string tictoc;

    cv::Mat img_preProcessor;
    std::unique_ptr<PreProcessor> preProcessor;
    bool enablePreProcessor = false;

    cv::Mat img_frameDifference;
    std::shared_ptr<FrameDifference> frameDifference;
    bool enableFrameDifference = false;

    cv::Mat img_staticFrameDifference;
    std::shared_ptr<StaticFrameDifference> staticFrameDifference;
    bool enableStaticFrameDifference = false;

    cv::Mat img_weightedMovingMean;
    std::shared_ptr<WeightedMovingMean> weightedMovingMean;
    bool enableWeightedMovingMean = false;

    cv::Mat img_weightedMovingVariance;
    std::shared_ptr<WeightedMovingVariance> weightedMovingVariance;
    bool enableWeightedMovingVariance = false;

    cv::Mat img_adaptiveBackgroundLearning;
    std::shared_ptr<AdaptiveBackgroundLearning> adaptiveBackgroundLearning;
    bool enableAdaptiveBackgroundLearning = false;

    cv::Mat img_adaptiveSelectiveBackgroundLearning;
    std::shared_ptr<AdaptiveSelectiveBackgroundLearning> adaptiveSelectiveBackgroundLearning;
    bool enableAdaptiveSelectiveBackgroundLearning = false;

    cv::Mat img_mixtureOfGaussianV2;
    std::shared_ptr<MixtureOfGaussianV2> mixtureOfGaussianV2;
    bool enableMixtureOfGaussianV2 = false;

#if CV_MAJOR_VERSION == 2
    cv::Mat img_mixtureOfGaussianV1;
    std::shared_ptr<MixtureOfGaussianV1> mixtureOfGaussianV1;
    bool enableMixtureOfGaussianV1 = false;
#endif

#if CV_MAJOR_VERSION == 2 && CV_MINOR_VERSION >= 4 && CV_SUBMINOR_VERSION >= 3
    cv::Mat img_gmg;
    std::shared_ptr<GMG> gmg;
    bool enableGMG = false;
#endif

#if CV_MAJOR_VERSION >= 3
    cv::Mat img_knn;
    std::shared_ptr<KNN> knn;
    bool enableKNN = false;
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3
    cv::Mat img_dpAdaptiveMedian;
    std::shared_ptr<DPAdaptiveMedian> dpAdaptiveMedian;
    bool enableDPAdaptiveMedian = false;

    cv::Mat img_dpGrimsonGMM;
    std::shared_ptr<DPGrimsonGMM> dpGrimsonGMM;
    bool enableDPGrimsonGMM = false;

    cv::Mat img_dpZivkovicAGMM;
    std::shared_ptr<DPZivkovicAGMM> dpZivkovicAGMM;
    bool enableDPZivkovicAGMM = false;

    cv::Mat img_dpTemporalMean;
    std::shared_ptr<DPMean> dpTemporalMean;
    bool enableDPMean = false;

    cv::Mat img_dpWrenGA;
    std::shared_ptr<DPWrenGA> dpWrenGA;
    bool enableDPWrenGA = false;

    cv::Mat img_dpPratiMediod;
    std::shared_ptr<DPPratiMediod> dpPratiMediod;
    bool enableDPPratiMediod = false;

    cv::Mat img_dpEigenBackground;
    std::shared_ptr<DPEigenbackground> dpEigenBackground;
    bool enableDPEigenbackground = false;

    cv::Mat img_dpTexture;
    std::shared_ptr<DPTexture> dpTexture;
    bool enableDPTexture = false;

    cv::Mat img_type2FuzzyGMM_UM;
    std::shared_ptr<T2FGMM_UM> type2FuzzyGMM_UM;
    bool enableT2FGMM_UM = false;

    cv::Mat img_type2FuzzyGMM_UV;
    std::shared_ptr<T2FGMM_UV> type2FuzzyGMM_UV;
    bool enableT2FGMM_UV = false;

    cv::Mat img_type2FuzzyMRF_UM;
    std::shared_ptr<T2FMRF_UM> type2FuzzyMRF_UM;
    bool enableT2FMRF_UM = false;

    cv::Mat img_type2FuzzyMRF_UV;
    std::shared_ptr<T2FMRF_UV> type2FuzzyMRF_UV;
    bool enableT2FMRF_UV = false;

    cv::Mat img_fuzzySugenoIntegral;
    std::shared_ptr<FuzzySugenoIntegral> fuzzySugenoIntegral;
    bool enableFuzzySugenoIntegral = false;

    cv::Mat img_fuzzyChoquetIntegral;
    std::shared_ptr<FuzzyChoquetIntegral> fuzzyChoquetIntegral;
    bool enableFuzzyChoquetIntegral = false;

    cv::Mat img_lbSimpleGaussian;
    std::shared_ptr<LBSimpleGaussian> lbSimpleGaussian;
    bool enableLBSimpleGaussian = false;

    cv::Mat img_lbFuzzyGaussian;
    std::shared_ptr<LBFuzzyGaussian> lbFuzzyGaussian;
    bool enableLBFuzzyGaussian = false;

    cv::Mat img_lbMixtureOfGaussians;
    std::shared_ptr<LBMixtureOfGaussians> lbMixtureOfGaussians;
    bool enableLBMixtureOfGaussians = false;

    cv::Mat img_lbAdaptiveSOM;
    std::shared_ptr<LBAdaptiveSOM> lbAdaptiveSOM;
    bool enableLBAdaptiveSOM = false;

    cv::Mat img_lbFuzzyAdaptiveSOM;
    std::shared_ptr<LBFuzzyAdaptiveSOM> lbFuzzyAdaptiveSOM;
    bool enableLBFuzzyAdaptiveSOM = false;

    cv::Mat img_pixelBasedAdaptiveSegmenter;
    std::shared_ptr<PixelBasedAdaptiveSegmenter> pixelBasedAdaptiveSegmenter;
    bool enablePBAS = false;

    cv::Mat img_vumeter;
    std::shared_ptr<VuMeter> vuMeter;
    bool enableVuMeter = false;

    cv::Mat img_kde;
    std::shared_ptr<KDE> kde;
    bool enableKDE = false;

    cv::Mat img_imbs;
    std::shared_ptr<IndependentMultimodal> imbs;
    bool enableIMBS = false;

    cv::Mat img_multiCue;
    std::shared_ptr<MultiCue> multiCue;
    bool enableMultiCue = false;
#endif

#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7
    cv::Mat img_lbpMrf;
    std::shared_ptr<LBP_MRF> lbpMrf;
    bool enableLbpMrf = false;

    cv::Mat img_multiLayer;
    std::shared_ptr<MultiLayer> multiLayer;
    bool enableMultiLayer = false;
#endif

    cv::Mat img_sigmaDelta;
    std::shared_ptr<SigmaDelta> sigmaDelta;
    bool enableSigmaDelta = false;

    cv::Mat img_subSENSE;
    std::shared_ptr<SuBSENSE> subSENSE;
    bool enableSuBSENSE = false;

    cv::Mat img_lobster;
    std::shared_ptr<LOBSTER> lobster;
    bool enableLOBSTER = false;

    cv::Mat img_pawcs;
    std::shared_ptr<PAWCS> pawcs;
    bool enablePAWCS = false;

    cv::Mat img_twoPoints;
    std::shared_ptr<TwoPoints> twoPoints;
    bool enableTwoPoints = false;

    cv::Mat img_vibe;
    std::shared_ptr<ViBe> vibe;
    bool enableViBe = false;

    cv::Mat img_codeBook;
    std::shared_ptr<CodeBook> codeBook;
    bool enableCodeBook = false;
    
    std::shared_ptr<tools::ForegroundMaskAnalysis> foregroundMaskAnalysis;
    bool enableForegroundMaskAnalysis = false;

  public:
    FrameProcessor();
    ~FrameProcessor();

    long frameToStop;
    std::string imgref;

    void init();
    void process(const cv::Mat &img_input);
    void finish(void);

  private:
    void process(const std::string name, const std::shared_ptr<IBGS> &bgs, const cv::Mat &img_input, cv::Mat &img_bgs);
    void tic(std::string value);
    void toc();
    
    void save_config(cv::FileStorage &fs);
    void load_config(cv::FileStorage &fs);
  };
}

--#-- END ./bgslibrary/FrameProcessor.h --#--

--#-- START ./bgslibrary/VideoAnalysis.h --#--
#pragma once

#include <iostream>
#include <sstream>

#include "VideoCapture.h"
#include "FrameProcessor.h"

namespace bgslibrary
{
  class VideoAnalysis
  {
  private:
    std::unique_ptr<VideoCapture> videoCapture;
    std::shared_ptr<FrameProcessor> frameProcessor;
    bool use_file;
    std::string filename;
    bool use_camera;
    int cameraIndex;
    bool use_comp;
    long frameToStop;
    std::string imgref;

  public:
    VideoAnalysis();
    ~VideoAnalysis();

    bool setup(int argc, const char **argv);
    void start();
  };
}

--#-- END ./bgslibrary/VideoAnalysis.h --#--

--#-- START ./bgslibrary/IFrameProcessor.h --#--
#pragma once

#include <opencv2/opencv.hpp>

#include "utils/GenericMacros.h"

namespace bgslibrary
{
  class IFrameProcessor
  {
  public:
    IFrameProcessor(){
      //debug_construction(IFrameProcessor);
    }
    virtual ~IFrameProcessor() {
      //debug_destruction(IFrameProcessor);
    }
    virtual void process(const cv::Mat &input) = 0;
  };
}

--#-- END ./bgslibrary/IFrameProcessor.h --#--

--#-- START ./bgslibrary/VideoCapture.h --#--
#pragma once

#include <iostream>
#include <fstream>
#include <memory>
//#include <chrono>
//#include <thread>
#include <opencv2/opencv.hpp>
// opencv legacy includes
//#include <opencv2/highgui/highgui_c.h>
//#include <opencv2/imgproc/imgproc_c.h>
//#include <opencv2/imgproc/types_c.h>

#include "utils/GenericKeys.h"
#include "utils/ILoadSaveConfig.h"
#include "IFrameProcessor.h"

namespace bgslibrary
{
  class VideoCapture : public ILoadSaveConfig
  {
  private:
    std::shared_ptr<IFrameProcessor> frameProcessor;
    cv::VideoCapture capture;
    cv::Mat frame;
    int key;
    int64 start_time;
    int64 delta_time;
    double freq;
    double fps;
    int frameNumber;
    int stopAt;
    bool useCamera;
    int cameraIndex;
    bool useVideo;
    std::string videoFileName;
    int input_resize_percent;
    bool showOutput;
    bool showFPS;
    bool enableFlip;
    double loopDelay = 33.333;
    bool firstTime = true;

  public:
    VideoCapture();
    ~VideoCapture();

    void setFrameProcessor(const std::shared_ptr<IFrameProcessor> &_frameProcessor);
    void setCamera(int _index);
    void setVideo(std::string _filename);
    void start();

  private:
    void setUpCamera();
    void setUpVideo();
    
    void save_config(cv::FileStorage &fs);
    void load_config(cv::FileStorage &fs);
  };
}

--#-- END ./bgslibrary/VideoCapture.h --#--

--#-- START ./bgslibrary/PreProcessor.h --#--
#pragma once

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#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>

#include "utils/ILoadSaveConfig.h"

namespace bgslibrary
{
  class PreProcessor : public ILoadSaveConfig
  {
  private:
    bool firstTime;
    bool equalizeHist;
    bool gaussianBlur;
    cv::Mat img_gray;
    bool enableShow;

  public:
    PreProcessor();
    ~PreProcessor();

    void setEqualizeHist(bool value);
    void setGaussianBlur(bool value);
    cv::Mat getGrayScale();

    void process(const cv::Mat &img_input, cv::Mat &img_output);

    //void rotate(const cv::Mat &img_input, cv::Mat &img_output, float angle);
    void applyCanny(const cv::Mat &img_input, cv::Mat &img_output);

  private:
    void save_config(cv::FileStorage &fs);
    void load_config(cv::FileStorage &fs);
  };
}

--#-- END ./bgslibrary/PreProcessor.h --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MEDefs.hpp --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

namespace bgslibrary
{
  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);

      /** @} */
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MEDefs.hpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MEImage.hpp --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

namespace bgslibrary
{
  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;
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MEImage.hpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MotionDetection.hpp --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

#include <opencv2/imgproc/types_c.h>

#include "MEDefs.hpp"
#include "MEImage.hpp"

namespace bgslibrary
{
  namespace algorithms
  {
    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

--#-- END ./bgslibrary/algorithms/LBP_MRF/MotionDetection.hpp --#--

--#-- START ./bgslibrary/algorithms/LBP_MRF/MEHistogram.hpp --#--
#pragma once

#include "opencv2/core/version.hpp"
#if CV_MAJOR_VERSION >= 2 && CV_MAJOR_VERSION <= 3 && CV_MINOR_VERSION <= 4 && CV_VERSION_REVISION <= 7

namespace bgslibrary
{
  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;
      };
    }
  }
}

#endif

--#-- END ./bgslibrary/algorithms/LBP_MRF/MEHistogram.hpp --#--

--#-- START ./bgslibrary/algorithms/IMBS/IMBS.hpp --#--
#pragma once

#include <iostream>
#include <vector>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
// opencv legacy includes
#ifndef MEX_COMPILE_FLAG
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
#endif
#include <opencv2/imgproc/types_c.h>
#include <opencv2/imgproc/imgproc_c.h>

namespace bgslibrary
{
  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 = 50.,
          unsigned int minBinHeight = 2,
          unsigned int numSamples = 10,
          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);
      };
    }
  }
}

--#-- END ./bgslibrary/algorithms/IMBS/IMBS.hpp --#--

--#-- START ./README.md --#--
<p align="center">
<img src="https://github.com/andrewssobral/bgslibrary/blob/master/docs/images/bgslibrary-logo.jpg?raw=true" alt="BGSLibrary" width="200">
</p>

# BGSLibrary: A Background Subtraction Library

[![Release](https://img.shields.io/badge/Release-3.3.0-blue.svg)](https://github.com/andrewssobral/bgslibrary/wiki/Build-status) [![License: GPL v3](https://img.shields.io/badge/License-MIT-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) [![Platform: Windows, Linux, OS X](https://img.shields.io/badge/Platform-Windows%2C%20Linux%2C%20OS%20X-blue.svg)](https://github.com/andrewssobral/bgslibrary/wiki/Build-status) [![OpenCV](https://img.shields.io/badge/OpenCV-2.4.x%2C%203.x%2C%204.x-blue.svg)](https://github.com/andrewssobral/bgslibrary/wiki/Build-status) [![Wrapper: Python, MATLAB](https://img.shields.io/badge/Wrapper-Java%2C%20Python%2C%20MATLAB-orange.svg)](https://github.com/andrewssobral/bgslibrary/wiki/Build-status) [![Algorithms](https://img.shields.io/badge/Algorithms-43-red.svg)](https://github.com/andrewssobral/bgslibrary/wiki/List-of-available-algorithms) <a href="https://app.commanddash.io/agent?github=https://github.com/andrewssobral/bgslibrary"><img src="https://img.shields.io/badge/AI-Code%20Gen-EB9FDA"></a>

<p align="center">
<a href="https://youtu.be/_UbERwuQ0OU" target="_blank">
<img src="https://raw.githubusercontent.com/andrewssobral/bgslibrary/master/docs/images/bgs_giphy2.gif" border="0" />
</a>
</p>

## Introduction

The **BGSLibrary** (Background Subtraction Library) is a comprehensive C++ framework designed for background subtraction in computer vision applications, particularly for detecting moving objects in video streams. It provides an easy-to-use and extensible platform for researchers and developers to experiment with and implement various background subtraction techniques.

## Library Version

**3.3.0** (see **[Build Status](https://github.com/andrewssobral/bgslibrary/wiki/Build-status)** and **[Release Notes](https://github.com/andrewssobral/bgslibrary/wiki/Release-notes)** for more info)

## Background and Development

The BGSLibrary was developed in early 2012 by [Andrews Cordolino Sobral](http://andrewssobral.wixsite.com/home) as a C++ framework with wrappers available for Python, Java, and MATLAB. It aims to facilitate foreground-background separation in videos using the OpenCV library.

## Compatibility

The library is compatible with OpenCV versions 2.4.x, 3.x, and 4.x. It can be compiled and used on Windows, Linux, and Mac OS X systems.

## Licensing

The library's source code is available under the [MIT license](https://opensource.org/licenses/MIT), making it free for both academic and commercial use.

## Getting started

* [List of available algorithms](https://github.com/andrewssobral/bgslibrary/wiki/List-of-available-algorithms)
* [Algorithms benchmark](https://github.com/andrewssobral/bgslibrary/wiki/Algorithms-benchmark)
* [Which algorithms really matter?](https://github.com/andrewssobral/bgslibrary/wiki/Which-algorithms-really-matter%3F)
* [Library architecture](https://github.com/andrewssobral/bgslibrary/wiki/Library-architecture)

<a href="https://app.commanddash.io/agent?github=https://github.com/andrewssobral/bgslibrary"><img src="https://img.shields.io/badge/AI-Code%20Gen-EB9FDA"></a>
```cpp
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>

// Include the OpenCV and BGSLibrary libraries
#include <opencv2/opencv.hpp>
#include <bgslibrary/algorithms/algorithms.h>

int main( int argc, char** argv )
{
    // Gets the names of the background subtraction algorithms registered in the BGSLibrary factory
    auto algorithmsName = BGS_Factory::Instance()->GetRegisteredAlgorithmsName();

    // Displays the number of available background subtraction algorithms in the BGSLibrary
    std::cout << "Number of available algorithms: " << algorithmsName.size() << std::endl;

    // Displays the list of available background subtraction algorithms in the BGSLibrary
    std::cout << "List of available algorithms:" << std::endl;
    std::copy(algorithmsName.begin(), algorithmsName.end(), std::ostream_iterator<std::string>(std::cout, "\n"));

    // Returns 0 to indicate that the execution was successful
    return 0;
}
```

### Installation instructions

You can either install BGSLibrary via [pre-built binary package](https://github.com/andrewssobral/bgslibrary/releases) or build it from source

* [Windows installation](https://github.com/andrewssobral/bgslibrary/wiki/Installation-instructions---Windows)
* [Ubuntu / OS X installation](https://github.com/andrewssobral/bgslibrary/wiki/Installation-instructions-Ubuntu-or-OSX)

Supported Compilers:

* GCC 4.8 and above
* Clang 3.4 and above
* MSVC 2015, 2017, 2019 or newer

Other compilers might work, but are not officially supported.
The bgslibrary requires some features from the ISO C++ 2014 standard.

### Graphical User Interface

* [C++ QT](https://github.com/andrewssobral/bgslibrary/wiki/Graphical-User-Interface:-QT) ***(Official)***
* [C++ MFC](https://github.com/andrewssobral/bgslibrary/wiki/Graphical-User-Interface:-MFC) ***(Deprecated)***
* [Java](https://github.com/andrewssobral/bgslibrary/wiki/Graphical-User-Interface:-Java) ***(Obsolete)***

### Wrappers

* [Python](https://github.com/andrewssobral/bgslibrary/wiki/Wrapper:-Python) [![Downloads](https://static.pepy.tech/badge/pybgs)](https://pepy.tech/project/pybgs) [![Downloads](https://static.pepy.tech/badge/pybgs/month)](https://pepy.tech/project/pybgs) [![Downloads](https://static.pepy.tech/badge/pybgs/week)](https://pepy.tech/project/pybgs)
* [MATLAB](https://github.com/andrewssobral/bgslibrary/wiki/Wrapper:-MATLAB)
* [Java](https://github.com/andrewssobral/bgslibrary/wiki/Wrapper:-Java)

### Usage examples

* BGSlibrary examples folder
* * <https://github.com/andrewssobral/bgslibrary/tree/master/examples>
* BGSlibrary examples in C++
* * <https://github.com/andrewssobral/bgslibrary-examples-cpp>
* BGSlibrary examples in Python
* * <https://github.com/andrewssobral/bgslibrary-examples-python>

### More

* [Docker images](https://github.com/andrewssobral/bgslibrary/wiki/Docker-images)
* [How to integrate BGSLibrary in your own CPP code](https://github.com/andrewssobral/bgslibrary/wiki/How-to-integrate-BGSLibrary-in-your-own-CPP-code)
* [How to contribute](https://github.com/andrewssobral/bgslibrary/wiki/How-to-contribute)
* [List of collaborators](https://github.com/andrewssobral/bgslibrary/wiki/List-of-collaborators)
* [Release notes](https://github.com/andrewssobral/bgslibrary/wiki/Release-notes)

## Algorithm compatibility across OpenCV versions

| Algorithm | OpenCV < 3.0 (42) | 3.0 <= OpenCV <= 3.4.7 (41) | 3.4.7 < OpenCV < 4.0 (39) | OpenCV >= 4.0 (26) |
|--------------------------------|:-----------:|:----------------------:|:---------------------:|:------------:|
| AdaptiveBackgroundLearning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| AdaptiveSelectiveBackgroundLearning | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| CodeBook | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| DPAdaptiveMedian | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPEigenbackground | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPGrimsonGMM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPMean | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPPratiMediod | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPTexture | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPWrenGA | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| DPZivkovicAGMM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| FrameDifference | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| FuzzyChoquetIntegral | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| FuzzySugenoIntegral | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| GMG | :heavy_check_mark: | :x: | :x: | :x: |
| IndependentMultimodal | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| KDE | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| KNN | :x: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| LBAdaptiveSOM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| LBFuzzyAdaptiveSOM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| LBFuzzyGaussian | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| LBMixtureOfGaussians | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| LBP_MRF | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| LBSimpleGaussian | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| LOBSTER | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| MixtureOfGaussianV1 | :heavy_check_mark: | :x: | :x: | :x: |
| MixtureOfGaussianV2 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| MultiCue | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| MultiLayer | :heavy_check_mark: | :heavy_check_mark: | :x: | :x: |
| PAWCS | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| PixelBasedAdaptiveSegmenter | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| SigmaDelta | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| StaticFrameDifference | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| SuBSENSE | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| T2FGMM_UM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| T2FGMM_UV | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| T2FMRF_UM | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| T2FMRF_UV | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :x: |
| TwoPoints | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| ViBe | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| VuMeter | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| WeightedMovingMean | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| WeightedMovingVariance | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |

## Stargazers over time

[![Stargazers over time](https://starchart.cc/andrewssobral/bgslibrary.svg)](https://starchart.cc/andrewssobral/bgslibrary)

## Citation

If you use this library for your publications, please cite it as:

```
@inproceedings{bgslibrary,
author    = {Sobral, Andrews},
title     = {{BGSLibrary}: An OpenCV C++ Background Subtraction Library},
booktitle = {IX Workshop de Visão Computacional (WVC'2013)},
address   = {Rio de Janeiro, Brazil},
year      = {2013},
month     = {Jun},
url       = {https://github.com/andrewssobral/bgslibrary}
}
```

A chapter about the BGSLibrary has been published in the handbook on [Background Modeling and Foreground Detection for Video Surveillance](https://sites.google.com/site/backgroundsubtraction/).

```
@incollection{bgslibrarychapter,
author    = {Sobral, Andrews and Bouwmans, Thierry},
title     = {BGS Library: A Library Framework for Algorithm’s Evaluation in Foreground/Background Segmentation},
booktitle = {Background Modeling and Foreground Detection for Video Surveillance},
publisher = {CRC Press, Taylor and Francis Group.}
year      = {2014},
}
```

## References

* Sobral, Andrews. BGSLibrary: An OpenCV C++ Background Subtraction Library. IX Workshop de Visão Computacional (WVC'2013), Rio de Janeiro, Brazil, Jun. 2013. ([PDF](http://www.researchgate.net/publication/257424214_BGSLibrary_An_OpenCV_C_Background_Subtraction_Library) in brazilian-portuguese containing an english abstract).

* Sobral, Andrews; Bouwmans, Thierry. "BGS Library: A Library Framework for Algorithm’s Evaluation in Foreground/Background Segmentation". Chapter on the handbook "Background Modeling and Foreground Detection for Video Surveillance", CRC Press, Taylor and Francis Group, 2014. ([PDF](http://www.researchgate.net/publication/257424214_BGSLibrary_An_OpenCV_C_Background_Subtraction_Library) in english).

Some algorithms of the BGSLibrary were used successfully in the following papers:

* (2014) Sobral, Andrews; Vacavant, Antoine. A comprehensive review of background subtraction algorithms evaluated with synthetic and real videos. Computer Vision and Image Understanding (CVIU), 2014. ([Online](http://dx.doi.org/10.1016/j.cviu.2013.12.005)) ([PDF](http://www.researchgate.net/publication/259340906_A_comprehensive_review_of_background_subtraction_algorithms_evaluated_with_synthetic_and_real_videos))

* (2013) Sobral, Andrews; Oliveira, Luciano; Schnitman, Leizer; Souza, Felippe. (**Best Paper Award**) Highway Traffic Congestion Classification Using Holistic Properties. In International Conference on Signal Processing, Pattern Recognition and Applications (SPPRA'2013), Innsbruck, Austria, Feb 2013. ([Online](http://dx.doi.org/10.2316/P.2013.798-105)) ([PDF](http://www.researchgate.net/publication/233427564_HIGHWAY_TRAFFIC_CONGESTION_CLASSIFICATION_USING_HOLISTIC_PROPERTIES))

## Videos

<p align="center">
<a href="https://www.youtube.com/watch?v=_UbERwuQ0OU" target="_blank">
<img src="https://raw.githubusercontent.com/andrewssobral/bgslibrary/master/docs/images/bgslibrary_qt_gui_video.png" width="600" border="0" />
</a>
</p>

<p align="center">
<a href="https://www.youtube.com/watch?v=Ccqa9KBO9_U" target="_blank">
<img src="https://raw.githubusercontent.com/andrewssobral/bgslibrary/master/docs/images/bgslibrary_youtube.png" width="600" border="0" />
</a>
</p>

--#-- END ./README.md --#--

--#-- START ./.github/ISSUE_TEMPLATE/feature_request.md --#--
---
name: Feature request
about: Suggest an idea for this project

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.

--#-- END ./.github/ISSUE_TEMPLATE/feature_request.md --#--

--#-- START ./.github/ISSUE_TEMPLATE/bug_report.md --#--
---
name: Bug report
about: Create a report to help us improve

---

If you open a GitHub issue, here is our policy:

1. It must be a bug, a feature request, or a significant problem with documentation (for small docs fixes please send a PR instead).
2. The form below must be filled out.

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior.

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

### System information
**Desktop (please complete the following information):**
- **OS Platform and Distribution (e.g., Linux Ubuntu 16.04)**:
- **Python version (for issues related to the python wrapper)**: 
- **Java version (for issues related to the java wrapper)**: 
- **MATLAB version (for issues related to the matlab wrapper)**: 

**Additional context**
Add any other context about the problem here.

--#-- END ./.github/ISSUE_TEMPLATE/bug_report.md --#--

--#-- START ./.properties --#--
version=3.3.0-SNAPSHOT

--#-- END ./.properties --#--

--#-- START ./tools/fet/fet.py --#--
# -*- coding: utf-8 -*-
"""
FET - FOREGROUND EVALUATION TOOL

@author: Andrews Sobral
"""

import numpy as np
import cv2
import os
from os import listdir
from os.path import isfile, join

import sys
if sys.version_info >= (3, 0):
  from six.moves import xrange

path_gt = 'GT/'
path_fg = 'FG/'
path_sc = 'SC/'

if not os.path.exists(path_sc):
	os.makedirs(path_sc)

files_gt = [ f for f in listdir(path_gt) if isfile(join(path_gt,f)) ]
files_fg = [ f for f in listdir(path_fg) if isfile(join(path_fg,f)) ]

TP = .0 # True positive pixels
FP = .0 # False positive pixels
TN = .0 # True negative pixels
FN = .0 # False negative pixels
Recall = .0 # TP / (TP + FN)
Precision = .0 # TP / (TP + FP)
Fscore = .0 # 2*(Precision*Recall)/(Precision+Recall)

# TP - # of foreground pixels classified as foreground pixels.
# FP - # of background pixels classified as foreground pixels.
# TN - # of background pixels classified as background pixels.
# FN - # of foreground pixels classified as background pixels.

green = [0,255,0] # for FN
blue  = [255,0,0] 
red   = [0,0,255] # for FP
white = [255,255,255] # for TP
black = [0,0,0] # for TN

print('Processing')
k = 1
for file_gt, file_fg in zip(files_gt, files_fg):
  print(k, file_gt, file_fg)
  img_gt = cv2.imread(path_gt + file_gt,cv2.IMREAD_GRAYSCALE)
  img_fg = cv2.imread(path_fg + file_fg,cv2.IMREAD_GRAYSCALE)
  # img_gt = cv2.resize(img_gt, (0,0), fx=0.5, fy=0.5) 
  # print(img_gt.shape,img_fg.shape)
  rows,cols = img_gt.shape
  img_fg = cv2.resize(img_fg,(cols,rows)) 
  img_res = np.zeros((rows,cols,3),np.uint8)
  for i in xrange(rows):
    for j in xrange(cols):
      pixel_gt = img_gt[i,j]
      pixel_fg = img_fg[i,j]        
      if(pixel_gt == 255 and pixel_fg == 255):
        TP = TP + 1
        img_res[i,j] = white
      if(pixel_gt == 0 and pixel_fg == 255):
        FP = FP + 1
        img_res[i,j] = red
      if(pixel_gt == 0 and pixel_fg == 0):
        TN = TN + 1
        img_res[i,j] = black
      if(pixel_gt == 255 and pixel_fg == 0):
        FN = FN + 1
        img_res[i,j] = green
  cv2.imshow('GT',img_gt)
  cv2.imshow('FG',img_fg)
  cv2.imshow('SC',img_res)
  cv2.imwrite(path_sc + file_gt, img_res)
  cv2.waitKey(1) # 33
  k = k + 1
  #break
cv2.destroyAllWindows()

Recall = TP / (TP + FN)
Precision = TP / (TP + FP)
Fscore = 2*Precision*Recall/(Precision+Recall)

print('Score:')
print('TP: ', TP)
print('FP: ', FP)
print('TN: ', TN)
print('FN: ', FN)
print('Recall: ', Recall)
print('Precision: ', Precision)
print('Fscore: ', Fscore)
print('')

#####################################################################

--#-- END ./tools/fet/fet.py --#--

--#-- START ./demo2.py --#--
##############################################
# Demo file
# python demo2.py --- will use video
# python demo2.py image --- will use images
#############################################

import numpy as np
import cv2
import pybgs as bgs
import sys
import glob

print("OpenCV Version: {}".format(cv2.__version__))

def is_cv2():
  return check_opencv_version("2.")

def is_cv3():
  return check_opencv_version("3.")

def is_lower_or_equals_cv347():
  [major, minor, revision] = str(cv2.__version__).split('.')
  return int(major) == 3 and int(minor) <= 4 and int(revision) <= 7

def is_cv4():
  return check_opencv_version("4.")

def check_opencv_version(major):
  return cv2.__version__.startswith(major)

## bgslibrary algorithms
algorithms=[]
algorithms.append(bgs.FrameDifference())
algorithms.append(bgs.StaticFrameDifference())
algorithms.append(bgs.WeightedMovingMean())
algorithms.append(bgs.WeightedMovingVariance())
algorithms.append(bgs.AdaptiveBackgroundLearning())
algorithms.append(bgs.AdaptiveSelectiveBackgroundLearning())
algorithms.append(bgs.MixtureOfGaussianV2())
algorithms.append(bgs.PixelBasedAdaptiveSegmenter())
algorithms.append(bgs.SigmaDelta())
algorithms.append(bgs.SuBSENSE())
algorithms.append(bgs.LOBSTER())
algorithms.append(bgs.PAWCS())
algorithms.append(bgs.TwoPoints())
algorithms.append(bgs.ViBe())
algorithms.append(bgs.CodeBook())
algorithms.append(bgs.FuzzySugenoIntegral())
algorithms.append(bgs.FuzzyChoquetIntegral())
algorithms.append(bgs.LBSimpleGaussian())
algorithms.append(bgs.LBFuzzyGaussian())
algorithms.append(bgs.LBMixtureOfGaussians())
algorithms.append(bgs.LBAdaptiveSOM())
algorithms.append(bgs.LBFuzzyAdaptiveSOM())
algorithms.append(bgs.VuMeter())
algorithms.append(bgs.KDE())
algorithms.append(bgs.IndependentMultimodal())

if is_cv2():
  algorithms.append(bgs.MixtureOfGaussianV1()) # if opencv 2.x
  algorithms.append(bgs.GMG()) # if opencv 2.x

if not is_cv2():
  algorithms.append(bgs.KNN()) # if opencv > 2

if is_cv2() or is_cv3():
  algorithms.append(bgs.DPAdaptiveMedian())
  algorithms.append(bgs.DPGrimsonGMM())
  algorithms.append(bgs.DPZivkovicAGMM())
  algorithms.append(bgs.DPMean())
  algorithms.append(bgs.DPWrenGA())
  algorithms.append(bgs.DPPratiMediod())
  algorithms.append(bgs.DPEigenbackground())
  algorithms.append(bgs.DPTexture())
  algorithms.append(bgs.T2FGMM_UM())
  algorithms.append(bgs.T2FGMM_UV())
  algorithms.append(bgs.T2FMRF_UM())
  algorithms.append(bgs.T2FMRF_UV())
  algorithms.append(bgs.MultiCue())

if is_cv2() or is_lower_or_equals_cv347():
  algorithms.append(bgs.LBP_MRF())
  algorithms.append(bgs.MultiLayer())


# check if we want to use the images
image = False
if (len(sys.argv) == 2):
    if(sys.argv[1] == "image"):
        image = True
        img_folder = "dataset/frames"
        img_array = sorted(glob.iglob(img_folder + '/*.png'))

video_file = "dataset/video.avi"

print("Number of available algorithms: ", len(algorithms))
for algorithm in algorithms:
  print("Running ", algorithm.__class__)

  if(image):
    # loop x times as files in our folder
    for x in range(0, len(img_array)):

        # we can loop now through our array of images
        img_path = img_array[x]

        # read file into open cv and apply to algorithm to generate background model
        img = cv2.imread(img_path)
        img_output = algorithm.apply(img)
        img_bgmodel = algorithm.getBackgroundModel()

        # show images in python imshow window
        cv2.imshow('image', img)
        cv2.imshow('img_output', img_output)
        cv2.imshow('img_bgmodel', img_bgmodel)

        # we need waitKey otherwise it wont display the image
        if 0xFF & cv2.waitKey(10) == 27:
          break

        # Comment out to save images to bg and fg folder
        #img_bg = img_path.replace(img_folder, "output/bg")
        #img_fg = img_path.replace(img_folder, "output/fg")
        #cv2.imwrite(img_bg, img_bgmodel)
        #cv2.imwrite(img_fg, img_output)

        print("Frames left: " + str(len(img_array)-x))

  else:

      capture = cv2.VideoCapture(video_file)
      while not capture.isOpened():
        capture = cv2.VideoCapture(video_file)
        cv2.waitKey(1000)
        print("Wait for the header")

      #pos_frame = capture.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
      #pos_frame = capture.get(cv2.CV_CAP_PROP_POS_FRAMES)
      pos_frame = capture.get(1)
      while True:
        flag, frame = capture.read()

        if flag:
          cv2.imshow('video', frame)
          #pos_frame = capture.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
          #pos_frame = capture.get(cv2.CV_CAP_PROP_POS_FRAMES)
          pos_frame = capture.get(1)
          #print str(pos_frame)+" frames"

          img_output = algorithm.apply(frame)
          img_bgmodel = algorithm.getBackgroundModel()

          cv2.imshow('img_output', img_output)
          cv2.imshow('img_bgmodel', img_bgmodel)

        else:
          #capture.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, pos_frame-1)
          #capture.set(cv2.CV_CAP_PROP_POS_FRAMES, pos_frame-1)
          #capture.set(1, pos_frame-1)
          #print "Frame is not ready"
          cv2.waitKey(1000)
          break

        if 0xFF & cv2.waitKey(10) == 27:
          break

        #if capture.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) == capture.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT):
        #if capture.get(cv2.CV_CAP_PROP_POS_FRAMES) == capture.get(cv2.CV_CAP_PROP_FRAME_COUNT):
        #if capture.get(1) == capture.get(cv2.CV_CAP_PROP_FRAME_COUNT):
          #break

print("Finished")
cv2.destroyAllWindows()

--#-- END ./demo2.py --#--

--#-- START ./setup.py --#--
"""
To build the bgslibrary:
    python setup.py build
To build and install:
    python setup.py install
To install using pip:
    pip install .
To install using PyPI:
    pip install pybgs
To package the wheel (after pip installing twine and wheel):
    python setup.py bdist_wheel
To upload the binary wheel to PyPi
    twine upload dist/*.whl
To upload the source distribution to PyPi
    python setup.py sdist
    twine upload dist/pybgs-*.tar.gz
"""
import os, re, sys, shutil, platform, subprocess

from setuptools import setup, find_packages, Extension
from setuptools.command.build_ext import build_ext
from setuptools.command.install_lib import install_lib
from setuptools.command.install_scripts import install_scripts
from distutils.command.install_data import install_data
from distutils.version import LooseVersion

PACKAGE_NAME = "pybgs"

class CMakeExtension(Extension):
    def __init__(self, name, sourcedir=''):
        Extension.__init__(self, name, sources=[])
        self.sourcedir = os.path.abspath(sourcedir)

class InstallCMakeLibsData(install_data):
    """
    Just a wrapper to get the install data into the egg-info

    Listing the installed files in the egg-info guarantees that
    all of the package files will be uninstalled when the user
    uninstalls your package through pip
    """
    def run(self):
        """
        Outfiles are the libraries that were built using cmake
        """
        # There seems to be no other way to do this; I tried listing the
        # libraries during the execution of the InstallCMakeLibs.run() but
        # setuptools never tracked them, seems like setuptools wants to
        # track the libraries through package data more than anything...
        # help would be appriciated
        self.outfiles = self.distribution.data_files

__metaclass__ = type
class InstallCMakeLibs(install_lib, object):
    """
    Get the libraries from the parent distribution, use those as the outfiles

    Skip building anything; everything is already built, forward libraries to
    the installation step
    """
    def run(self):
        """
        Copy libraries from the bin directory and place them as appropriate
        """
        self.announce("Moving library files", level=3)
        # We have already built the libraries in the previous build_ext step
        self.skip_build = True
        if hasattr(self.distribution, 'bin_dir'):
            bin_dir = self.distribution.bin_dir
        else:
            bin_dir = os.path.join(self.build_dir)
        # Depending on the files that are generated from your cmake
        # build chain, you may need to change the below code, such that
        # your files are moved to the appropriate location when the installation
        # is run
        libs = [os.path.join(bin_dir, _lib) for _lib in 
                os.listdir(bin_dir) if 
                os.path.isfile(os.path.join(bin_dir, _lib)) and 
                os.path.splitext(_lib)[1] in [".dll", ".so"]
                and not (_lib.startswith("python") or _lib.startswith(PACKAGE_NAME))]
        for lib in libs:
            shutil.move(lib, os.path.join(self.build_dir, os.path.basename(lib)))
        # Mark the libs for installation, adding them to 
        # distribution.data_files seems to ensure that setuptools' record 
        # writer appends them to installed-files.txt in the package's egg-info
        #
        # Also tried adding the libraries to the distribution.libraries list, 
        # but that never seemed to add them to the installed-files.txt in the 
        # egg-info, and the online recommendation seems to be adding libraries 
        # into eager_resources in the call to setup(), which I think puts them 
        # in data_files anyways. 
        # 
        # What is the best way?
        # These are the additional installation files that should be
        # included in the package, but are resultant of the cmake build
        # step; depending on the files that are generated from your cmake
        # build chain, you may need to modify the below code
        self.distribution.data_files = [os.path.join(self.install_dir, os.path.basename(lib)) for lib in libs]
        # Must be forced to run after adding the libs to data_files
        self.distribution.run_command("install_data")
        super(InstallCMakeLibs, self).run()

__metaclass__ = type
class InstallCMakeScripts(install_scripts, object):
    """
    Install the scripts in the build dir
    """
    def run(self):
        """
        Copy the required directory to the build directory and super().run()
        """
        self.announce("Moving scripts files", level=3)
        # Scripts were already built in a previous step
        self.skip_build = True
        bin_dir = self.distribution.bin_dir
        scripts_dirs = [os.path.join(bin_dir, _dir) for _dir in
                        os.listdir(bin_dir) if
                        os.path.isdir(os.path.join(bin_dir, _dir))]
        for scripts_dir in scripts_dirs:
            shutil.move(scripts_dir, os.path.join(self.build_dir, os.path.basename(scripts_dir)))
        # Mark the scripts for installation, adding them to 
        # distribution.scripts seems to ensure that the setuptools' record 
        # writer appends them to installed-files.txt in the package's egg-info
        self.distribution.scripts = scripts_dirs
        super(InstallCMakeScripts, self).run()

__metaclass__ = type
class BuildCMakeExt(build_ext, object):
    """
    Builds using cmake instead of the python setuptools implicit build
    """
    def run(self):
        """
        Perform build_cmake before doing the 'normal' stuff
        """
        for extension in self.extensions:
            self.build_cmake(extension)
        super(BuildCMakeExt, self).run()

    def build_cmake(self, extension):
        """
        The steps required to build the extension
        """
        self.announce("Preparing the build environment", level=3)
        build_dir = os.path.join(self.build_temp)
        extension_path = os.path.abspath(os.path.dirname(self.get_ext_fullpath(extension.name)))
        os.makedirs(build_dir)
        os.makedirs(extension_path)
        python_version = str(sys.version_info[0]) + "." + str(sys.version_info[1])

        # Now that the necessary directories are created, build
        self.announce("Configuring cmake project", level=3)
        cmake_args = ['-DPYTHON_EXECUTABLE=' + sys.executable,
                      '-DBGS_CORE_STATIC=ON',
                      '-DBGS_PYTHON_SUPPORT=ON',
                      '-DBGS_PYTHON_ONLY=ON',
                      '-DBGS_PYTHON_VERSION=' + python_version]
        if not os.path.exists(self.build_temp):
            os.makedirs(self.build_temp)
        self.spawn(['cmake', '-H'+extension.sourcedir, '-B'+self.build_temp]+ cmake_args)

        # Check which generator was used and use the correct command line switches.
        generator = ''
        cmake_cache_file = os.path.join(self.build_temp, 'CMakeCache.txt')
        with open(cmake_cache_file, 'r') as cmake_cache:
            for line in cmake_cache.readlines():
                if line.find('CMAKE_GENERATOR:') != -1:
                    generator = line[line.find('=') + 1:].strip()
        
        self.announce("Building binaries", level=3)
        
        if generator.find('Visual Studio') != -1:
            self.spawn(["cmake", "--build", self.build_temp, 
                        "--config", "Release", "--", "-m"])
        else:
            self.spawn(["cmake", "--build", self.build_temp, 
                        "--config", "Release", "--", "-j8"])

        # Build finished, now copy the files into the copy directory
        # The copy directory is the parent directory of the extension (.pyd)
        self.announce("Moving built python module", level=3)
        bin_dir = build_dir
        self.distribution.bin_dir = bin_dir
        pyd_path = [os.path.join(bin_dir, _pyd) for _pyd in
                    os.listdir(bin_dir) if
                    os.path.isfile(os.path.join(bin_dir, _pyd)) and
                    os.path.splitext(_pyd)[0].startswith(PACKAGE_NAME) and
                    os.path.splitext(_pyd)[1] in [".pyd", ".so"]][0]
        shutil.move(pyd_path, extension_path)

        # After build_ext is run, the following commands will run:
        # 
        # install_lib
        # install_scripts
        # 
        # These commands are subclassed above to avoid pitfalls that
        # setuptools tries to impose when installing these, as it usually
        # wants to build those libs and scripts as well or move them to a
        # different place. See comments above for additional information

with open("README.md", "r") as fh:
    long_description = fh.read()

setup(
    name='pybgs',
    version='3.3.0.post2',
    author='Andrews Sobral',
    author_email='andrewssobral@gmail.com',
    url='https://github.com/andrewssobral/bgslibrary',
    license='MIT',
    description='Official Python wrapper for BGSLibrary',
    long_description=long_description,
    long_description_content_type="text/markdown",
    ext_modules=[CMakeExtension(name='pybgs', sourcedir='.')],
    cmdclass={
        'build_ext': BuildCMakeExt,
        'install_data': InstallCMakeLibsData,
        'install_lib': InstallCMakeLibs,
        #'install_scripts': InstallCMakeScripts
        },
    zip_safe=False,
    packages=find_packages(),
    keywords=['BGSLibrary', 'Background Subtraction', 'Computer Vision', 'Machine Learning'],
)

--#-- END ./setup.py --#--

--#-- START ./demo.py --#--
import numpy as np
import cv2
import pybgs as bgs

algorithm = bgs.FrameDifference()
video_file = "dataset/video.avi"

capture = cv2.VideoCapture(video_file)
while not capture.isOpened():
  capture = cv2.VideoCapture(video_file)
  cv2.waitKey(1000)
  print("Wait for the header")

#pos_frame = capture.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
#pos_frame = capture.get(cv2.CV_CAP_PROP_POS_FRAMES)
pos_frame = capture.get(1)
while True:
  flag, frame = capture.read()
  
  if flag:
    cv2.imshow('video', frame)
    #pos_frame = capture.get(cv2.cv.CV_CAP_PROP_POS_FRAMES)
    #pos_frame = capture.get(cv2.CV_CAP_PROP_POS_FRAMES)
    pos_frame = capture.get(1)
    #print str(pos_frame)+" frames"
    
    img_output = algorithm.apply(frame)
    img_bgmodel = algorithm.getBackgroundModel()
    
    cv2.imshow('img_output', img_output)
    cv2.imshow('img_bgmodel', img_bgmodel)

  else:
    #capture.set(cv2.cv.CV_CAP_PROP_POS_FRAMES, pos_frame-1)
    #capture.set(cv2.CV_CAP_PROP_POS_FRAMES, pos_frame-1)
    #capture.set(1, pos_frame-1)
    #print "Frame is not ready"
    cv2.waitKey(1000)
    break
  
  if 0xFF & cv2.waitKey(10) == 27:
    break
  
  #if capture.get(cv2.cv.CV_CAP_PROP_POS_FRAMES) == capture.get(cv2.cv.CV_CAP_PROP_FRAME_COUNT):
  #if capture.get(cv2.CV_CAP_PROP_POS_FRAMES) == capture.get(cv2.CV_CAP_PROP_FRAME_COUNT):
  #if capture.get(1) == capture.get(cv2.CV_CAP_PROP_FRAME_COUNT):
    #break

cv2.destroyAllWindows()

--#-- END ./demo.py --#--

--#-- START ./virtualenv-install-test.sh --#--
# Remove the existing virtual environment if it exists
rm -rf bgslibrary_test_env

# Create a new virtual environment called bgslibrary_test_env using Python3
python3 -m venv bgslibrary_test_env

# Activate the newly created virtual environment
source bgslibrary_test_env/bin/activate

# Upgrade pip and install required packages numpy and OpenCV
python -m pip install --upgrade pip
python -m pip install numpy
python -m pip install opencv-python

# Build and install the package using the setup.py script
# python setup.py build
# python setup.py install

# Set the PYTHONPATH environment variable to the build directory to access the installed library
# The following line is for Linux
# export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.linux-x86_64-cpython-38
# The following line is for Mac
# export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.macosx-11-x86_64-cpython-39

# Install the pybgs directly from PyPI
python -m pip install pybgs

# Run demo.py and demo2.py to verify the package installation
python demo.py
python demo2.py

--#-- END ./virtualenv-install-test.sh --#--

--#-- START ./virtualenv-on-docker.sh --#--
# For Linux users
#
# OpenCV 4.6.0
# https://github.com/andrewssobral/docker/blob/master/bgslibrary/opencv_4.6.0py38/Dockerfile
DOCKER_IMAGE=andrewssobral/bgslibrary:opencv_4.6.0py38
#
# OpenCV 3.4.16
# https://github.com/andrewssobral/docker/blob/master/bgslibrary/opencv_3.4.16py38/Dockerfile
DOCKER_IMAGE=andrewssobral/bgslibrary:opencv_3.4.16py38
#
# OpenCV 3.4.7
# https://github.com/andrewssobral/docker/blob/master/bgslibrary/opencv_3.4.7py38/Dockerfile
DOCKER_IMAGE=andrewssobral/bgslibrary:opencv_3.4.7py38

docker run -it --rm -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=$DISPLAY $DOCKER_IMAGE bash

# For Mac users
# 
# X11 forwarding on macOS and docker
# https://gist.github.com/andrewssobral/7a5924c475ff936247f237f35d2e3cd7
#
# Or
#
# To run a Docker container on Mac with display support, you need to use XQuartz, a free and open-source software that provides an X11 window system for macOS.
# Here are the steps you need to follow:
# Install XQuartz: Download and install XQuartz from the official website (https://www.xquartz.org/).
# Enable XQuartz: Open XQuartz and go to XQuartz > Preferences > Security. Make sure that the “Allow connections from network clients” option is checked.
# Install socat: Install socat using Homebrew. Run the following command in the Terminal:
brew install socat
# Run socat: Run socat in the Terminal to forward X11 connections from the Docker container to XQuartz. Run the following command:
socat TCP-LISTEN:6000,reuseaddr,fork UNIX-CLIENT:\"$DISPLAY\"
# To check the tcp ports that are listenning
sudo lsof -i -P | grep LISTEN | grep :$PORT
# Start the Docker container: Start the Docker container with the following command:
docker run -e DISPLAY=host.docker.internal:0 <image-name>
# Replace <image-name> with the name of the Docker image you want to run.
# Test the display support: Test the display support by running a graphical application inside the Docker container. For example, you can run xclock or xeyes.
# e.g:
docker run -it --rm -e DISPLAY=host.docker.internal:0 $DOCKER_IMAGE bash
python -m pip install --upgrade pip
apt update && apt install x11-apps
xclock
xeyes

--#-- END ./virtualenv-on-docker.sh --#--

--#-- START ./build.sh --#--
#!/bin/bash

cmake -B build -S .
cmake --build build --config Release

--#-- END ./build.sh --#--

--#-- START ./run_video.sh --#--
#!/bin/bash
./build/bgslibrary -uf -fn=dataset/video.avi

--#-- END ./run_video.sh --#--

--#-- START ./qt-apple-silicon.sh --#--
# https://www.qt.io/blog/qt-on-apple-silicon
To build for arm64, add  -DCMAKE_OSX_ARCHITECTURES=arm64 explicitly 
to the "Initial CMake parameters" of the project build settings, 
or QMAKE_APPLE_DEVICE_ARCHS=arm64 to the qmake "Additional arguments" field.

cmake .. \
    -D CMAKE_PREFIX_PATH=$HOME/Qt/5.15.2/clang_64 \
    -D CMAKE_SYSTEM_PROCESSOR=arm64 \
    -D CMAKE_OSX_ARCHITECTURES=arm64

cmake .. \
    -D CMAKE_PREFIX_PATH=$HOME/Qt/6.2.4/macos \
    -D CMAKE_SYSTEM_PROCESSOR=arm64 \
    -D CMAKE_OSX_ARCHITECTURES=arm64

--#-- END ./qt-apple-silicon.sh --#--

--#-- START ./virtualenv-build-test-publish.sh --#--
# Remove the existing virtual environment if it exists
rm -rf bgslibrary_env

# Create a new virtual environment called bgslibrary_env using Python3
python3 -m venv bgslibrary_env

# Activate the newly created virtual environment
source bgslibrary_env/bin/activate

# Upgrade pip and install required packages numpy and OpenCV
python -m pip install --upgrade pip
python -m pip install wheel setuptools
python -m pip install numpy
python -m pip install opencv-python

# Remove any existing build directory
rm -rf build/*

# Build and install the package using the setup.py script
python setup.py build
python setup.py install

# Set the PYTHONPATH environment variable to the build directory to access the installed library
# The following line is for Linux
# ubuntu 20
export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.linux-x86_64-cpython-38
# ubuntu 22
export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.linux-x86_64-cpython-310
# ubuntu 24
export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.linux-x86_64-cpython-312
# The following line is for Mac (intel)
export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.macosx-11-x86_64-cpython-39
# The following line is for Mac (arm64)
export PYTHONPATH=$PYTHONPATH:$PWD/build/lib.macosx-13-arm64-cpython-39

# Run demo.py and demo2.py to verify the package installation
python demo.py
python demo2.py

# Install the Twine package for uploading the distribution packages
python -m pip install twine

# Remove any existing build directory
rm -rf build/*

# Build a Wheel distribution package for the project
python setup.py bdist_wheel

# Upload any generated Wheel distribution packages using Twine
twine upload dist/*.whl

# Remove any existing dist directory
rm -rf dist/*

# Create a source distribution package for the project
python setup.py sdist

# Upload the generated source distribution package using Twine
twine upload --repository testpypi dist/pybgs-*.tar.gz
twine upload dist/pybgs-*.tar.gz

--#-- END ./virtualenv-build-test-publish.sh --#--

--#-- START ./run_camera.sh --#--
#!/bin/bash
./build/bgslibrary --use_cam --camera=0

--#-- END ./run_camera.sh --#--

--#-- START ./tools/fet/README.txt --#--
FET - FOREGROUND EVALUATION TOOL

Implemented in Python 2.7

Available measurements
----------------------

TP - True positive:  Total number of true positive pixels.
FP - False positive: Total number of false positive pixels.
TN - True negative:  Total number of true negative pixels.
FN - False negative: Total number of false negative pixels.

Precision: Number of true positive pixels / (number of true positive pixels + number of false positive pixels).
Recall:    Number of true positive pixels / (number of true positive pixels + number of false negative pixels).
F-factor:  2 × (precision × recall)/(precision + recall). *** Also known as F1 score, F-score or F-measure.

Description
-----------
# TP - # of foreground pixels classified as foreground pixels.
# FP - # of background pixels classified as foreground pixels.
# TN - # of background pixels classified as background pixels.
# FN - # of foreground pixels classified as background pixels.

--#-- END ./tools/fet/README.txt --#--

--#-- START ./CMakeLists.txt --#--
cmake_minimum_required(VERSION 3.1)

project(bgslibrary VERSION 3.3.0)

include(CheckCXXCompilerFlag)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

function(append_if condition value)
  if (${condition})
    foreach(variable ${ARGN})
      set(${variable} "${${variable}} ${value}" PARENT_SCOPE)
    endforeach(variable)
  endif()
endfunction()

#if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR
#    CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR
#    CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
#  check_cxx_compiler_flag("-fvisibility-inlines-hidden" SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG)
#  append_if(SUPPORTS_FVISIBILITY_INLINES_HIDDEN_FLAG "-fvisibility=hidden -fvisibility-inlines-hidden" CMAKE_CXX_FLAGS)
#endif()

if(UNIX)
  #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++0x")
  set(CMAKE_MACOSX_RPATH 1)
endif(UNIX)

# Avoid cmake warnings about changes in behavior of some Mac OS X path
# variable we don't care about.
if (POLICY CMP0042)
  cmake_policy(SET CMP0042 NEW)
endif()

#set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
#set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules)

# compilation mode setup
set(CMAKE_BUILD_TYPE Release)
#set(CMAKE_BUILD_TYPE Debug)

if(WIN32)
  set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
  set(BUILD_SHARED_LIBS TRUE)
  #if(BGS_PYTHON_SUPPORT)
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MD")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MDd")
  #else()
  #  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
  #  set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
  #endif()
endif(WIN32)

set(bgs_out_dir ".")
# First for the generic no-config case (e.g. with mingw)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${bgs_out_dir})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${bgs_out_dir})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${bgs_out_dir})
# Second, for multi-config builds (e.g. msvc)
foreach(OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES})
  string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${bgs_out_dir})
  set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${bgs_out_dir})
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG} ${bgs_out_dir})
endforeach(OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES)

if(UNIX)
  # add some standard warnings
  ADD_DEFINITIONS(-Wno-variadic-macros -Wno-long-long -Wall -Wextra -Winit-self -Woverloaded-virtual -Wsign-promo -Wno-unused-parameter -pedantic -Woverloaded-virtual -Wno-unknown-pragmas)

  # -ansi does not compile with sjn module
  #ADD_DEFINITIONS(-ansi)

  # if you like to have warinings about conversions, e.g. double->int or double->float etc., or float compare
  #ADD_DEFINITIONS(-Wconversion -Wfloat-equal)
endif(UNIX)

# cmake -D BGS_PYTHON_SUPPORT=ON ..
if(NOT DEFINED BGS_PYTHON_SUPPORT)
  set(BGS_PYTHON_SUPPORT OFF)
else()
  # add_definitions(-DBGS_PYTHON_SUPPORT)
endif()
# cmake -D BGS_PYTHON_ONLY=ON ..
if(NOT DEFINED BGS_PYTHON_ONLY)
  set(BGS_PYTHON_ONLY OFF)
else()
  # add_definitions(-DBGS_PYTHON_ONLY)
endif()
# cmake -D BGS_CORE_STATIC=ON ..
if(NOT DEFINED BGS_CORE_STATIC)
  set(BGS_CORE_STATIC OFF)
else()
  # add_definitions(-DBGS_CORE_STATIC)
endif()
message(STATUS "")
message(STATUS "BGS_PYTHON_SUPPORT: ${BGS_PYTHON_SUPPORT}")
message(STATUS "BGS_PYTHON_ONLY:    ${BGS_PYTHON_ONLY}")
message(STATUS "BGS_CORE_STATIC:    ${BGS_CORE_STATIC}")

# cmake -D BGS_PYTHON_SUPPORT=ON -D BGS_PYTHON_VERSION=3 ..
if(NOT DEFINED BGS_PYTHON_VERSION)
  set(BGS_PYTHON_VERSION 3)
endif()
if(BGS_PYTHON_SUPPORT)
  message(STATUS "PYTHON VERSION: ${BGS_PYTHON_VERSION}")
endif()

if(BGS_CORE_STATIC)
  set(OpenCV_STATIC ON)
else()
  set(OpenCV_STATIC OFF)
endif()

find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
  message(STATUS "")
  message(STATUS "OpenCV library status:")
  message(STATUS "    version: ${OpenCV_VERSION}")
  message(STATUS "    libraries: ${OpenCV_LIBS}")
  message(STATUS "    include path: ${OpenCV_INCLUDE_DIRS}\n")
endif()

# if(${OpenCV_VERSION} VERSION_EQUAL 3 OR ${OpenCV_VERSION} VERSION_GREATER 3)
#   message(FATAL_ERROR "OpenCV version is not compatible: ${OpenCV_VERSION}")
# endif()

if(${OpenCV_VERSION} VERSION_LESS 2.3.1)
  message(FATAL_ERROR "OpenCV version is not compatible: ${OpenCV_VERSION}")
endif()

if(BGS_PYTHON_SUPPORT)
  #if(WIN32)
  #  set(Boost_USE_STATIC_LIBS ON)
  #else()
  #  set(Boost_USE_STATIC_LIBS OFF)
  #endif()

  #set(Boost_USE_MULTITHREADED ON)
  #set(Boost_USE_STATIC_RUNTIME OFF)

  #message(STATUS "SEARCHING FOR BOOST COMPONENT FOR PYTHON ${BGS_PYTHON_VERSION}")
  #if(BGS_PYTHON_VERSION EQUAL 2)
  #  find_package(Boost REQUIRED COMPONENTS python)
  #else()
  #  find_package(Boost REQUIRED COMPONENTS python3)
  #endif()

  # Pybind11's cmake scripts enable link time optimization by default.  However,
  # it makes linking take a really long time and doesn't seem to substantively
  # improve runtime performance.  So we disable LTO here to make building bgslibrary
  # faster.
  set(PYBIND11_LTO_CXX_FLAGS "")

  #set(PYBIND11_PYTHON_VERSION 2.7 3.5 3.6)
  set(PYBIND11_PYTHON_VERSION ${BGS_PYTHON_VERSION})
  #find_package(pybind11 REQUIRED)
  add_subdirectory(modules/pybind11)

  #find_package(PythonInterp ${BGS_PYTHON_VERSION} REQUIRED)
  #find_package(PythonLibs ${BGS_PYTHON_VERSION} REQUIRED)

  #message(STATUS "Boost library status:")
  #message(STATUS "    version: ${Boost_VERSION}")
  #message(STATUS "    libraries: ${Boost_LIBRARIES}")
  #message(STATUS "    include path: ${Boost_INCLUDE_DIRS}")

  message(STATUS "")
  message(STATUS "Python library status:")
  message(STATUS "    executable: ${PYTHON_EXECUTABLE}")
  #message(STATUS "    version: ${PYTHON_VERSION_STRING}")
  #message(STATUS "    libraries: ${PYTHON_LIBRARIES}")
  message(STATUS "    library: ${PYTHON_LIBRARY}")
  message(STATUS "    include path: ${PYTHON_INCLUDE_DIRS}")
  if(NOT NUMPY_INCLUDE_DIR)
    # message(FATAL_ERROR "You must define NUMPY_INCLUDE_DIR by 'cmake -D NUMPY_INCLUDE_DIR=/python/lib/site-packages/numpy/core/include ..'")
    exec_program ("${PYTHON_EXECUTABLE}"
      ARGS "-c \"import numpy; print(numpy.get_include())\""
      OUTPUT_VARIABLE NUMPY_INCLUDE_DIR
      RETURN_VALUE NUMPY_NOT_FOUND)
  endif()
  message(STATUS "NUMPY_INCLUDE_DIR: ${NUMPY_INCLUDE_DIR}\n")
endif()

if(NOT BGS_PYTHON_ONLY)
  file(GLOB main_src bgslibrary/*.cpp bgslibrary/*.c)
  file(GLOB main_inc bgslibrary/*.h bgslibrary/*.hpp)
endif()

file(GLOB_RECURSE utils_src bgslibrary/utils/*.cpp bgslibrary/utils/*.c)
file(GLOB_RECURSE utils_inc bgslibrary/utils/*.h bgslibrary/utils/*.hpp)

file(GLOB_RECURSE tools_src bgslibrary/tools/*.cpp bgslibrary/tools/*.c)
file(GLOB_RECURSE tools_inc bgslibrary/tools/*.h bgslibrary/tools/*.hpp)

file(GLOB_RECURSE bgs_src bgslibrary/algorithms/*.cpp bgslibrary/algorithms/*.c)
file(GLOB_RECURSE bgs_inc bgslibrary/algorithms/*.h bgslibrary/algorithms/*.hpp)

include_directories(${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${OpenCV_INCLUDE_DIRS})

if(BGS_PYTHON_SUPPORT)
  file(GLOB_RECURSE bgs_python_src wrapper/python/*.cpp)
  file(GLOB_RECURSE bgs_python_inc wrapper/python/*.h)

  include_directories(${CMAKE_CURRENT_SOURCE_DIR}/modules/pybind11/include)
  #include_directories(${Boost_INCLUDE_DIRS})
  include_directories(${PYTHON_INCLUDE_DIRS})
  include_directories(${NUMPY_INCLUDE_DIR})
endif()

# GMG is not available in older OpenCV versions
if(${OpenCV_VERSION} VERSION_LESS 2.4.3)
  file(GLOB gmg bgslibrary/algorithms/GMG.cpp)
  list(REMOVE_ITEM bgs_src ${gmg})
endif()

if(BGS_CORE_STATIC)
  message(STATUS "Bulding bgslibrary_core STATIC")
  add_library(bgslibrary_core STATIC ${bgs_src} ${tools_src} ${utils_src} ${bgs_inc} ${tools_inc} ${utils_inc})
  #set_property(TARGET bgslibrary_core PROPERTY POSITION_INDEPENDENT_CODE ON)
else()
  message(STATUS "Bulding bgslibrary_core SHARED")
  add_library(bgslibrary_core SHARED ${bgs_src} ${tools_src} ${utils_src} ${bgs_inc} ${tools_inc} ${utils_inc})
  target_link_libraries(bgslibrary_core ${OpenCV_LIBS})
  # generates the export header bgslibrary_core_EXPORTS.h automatically
  include(GenerateExportHeader)
  GENERATE_EXPORT_HEADER(bgslibrary_core
      BASE_NAME bgslibrary_core
      EXPORT_MACRO_NAME bgslibrary_core_EXPORTS
      EXPORT_FILE_NAME bgslibrary_core_EXPORTS.h
      STATIC_DEFINE BGSLIBRARY_CORE_EXPORTS_BUILT_AS_STATIC)
  #set_property(TARGET bgslibrary_core PROPERTY PUBLIC_HEADER ${bgs_inc} ${tools_inc} ${utils_inc})
endif()

if(BGS_PYTHON_SUPPORT)
  #add_library(bgs_python SHARED ${bgs_src} ${tools_src})
  #pybind11_add_module(bgs_python ${bgs_src} ${tools_src})
  pybind11_add_module(bgs_python ${bgs_python_src} ${bgs_python_inc})

  target_link_libraries(bgs_python PRIVATE bgslibrary_core ${OpenCV_LIBS} ${PYTHON_LIBRARY} pybind11::module)
  #target_link_libraries(bgs_python ${OpenCV_LIBS} ${Boost_LIBRARIES} ${PYTHON_LIBRARY})
  #target_link_libraries(bgs_python ${OpenCV_LIBS} ${PYTHON_LIBRARY} pybind11::module)
  #target_link_libraries(bgs_python PRIVATE ${OpenCV_LIBS} ${PYTHON_LIBRARY} pybind11::embed)

  #set_target_properties(bgs_python PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}")
  set_target_properties(bgs_python PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}")

  target_compile_definitions(bgs_python PRIVATE BGS_PYTHON_SUPPORT=1)

  # Set the output library name to bgslibrary because that's what setup.py and distutils expects.
  set_property(TARGET bgs_python PROPERTY OUTPUT_NAME "pybgs")
  #set_property(TARGET bgs_python PROPERTY POSITION_INDEPENDENT_CODE ON)
endif()

#if(WIN32)
#  # set_property(TARGET bgslibrary_core PROPERTY SUFFIX ".lib")
#  #if(BGS_PYTHON_SUPPORT)
#  #  set_property(TARGET bgslibrary_core PROPERTY SUFFIX ".pyd")
#  #endif()
#else()
#  set_property(TARGET bgslibrary_core PROPERTY OUTPUT_NAME "bgs")
#endif()

#if(APPLE)
#  if(BGS_PYTHON_SUPPORT)
#    set_property(TARGET bgslibrary_core PROPERTY SUFFIX ".so")
#    set_target_properties(bgslibrary_core PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
#  endif()
#endif()

if(NOT BGS_PYTHON_ONLY)
  add_executable(bgslibrary ${main_src} ${main_inc})
  target_link_libraries(bgslibrary ${OpenCV_LIBS} bgslibrary_core)
  # set_target_properties(bgslibrary PROPERTIES OUTPUT_NAME bgs)
endif()

if(UNIX AND BGS_PYTHON_SUPPORT)
  execute_process(
    COMMAND "${PYTHON_EXECUTABLE}" -c "if True:
      from distutils import sysconfig as sc
      print(sc.get_python_lib(prefix='', plat_specific=True))"
    OUTPUT_VARIABLE PYTHON_SITE
    OUTPUT_STRIP_TRAILING_WHITESPACE
  )

  message(STATUS "")
  message(STATUS "The bgslibrary python package will be installed at: ${PYTHON_SITE}\n")

  install(TARGETS bgs_python DESTINATION ${PYTHON_SITE})
  #install(FILES ${mypackage_python_files} DESTINATION ${PYTHON_SITE}/mypackage)
  #install(TARGETS bgs_python DESTINATION ${CMAKE_CURRENT_SOURCE_DIR})
endif()

if(NOT BGS_PYTHON_ONLY)
  include(GNUInstallDirs)
  set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/BGSLibrary)
  target_include_directories(bgslibrary_core
    PUBLIC
        $<INSTALL_INTERFACE:include>)


  install(TARGETS bgslibrary_core
    bgslibrary
    EXPORT bgslibrary_core-targets
    RUNTIME DESTINATION bin COMPONENT app
    LIBRARY DESTINATION lib COMPONENT runtime
    ARCHIVE DESTINATION lib COMPONENT runtime
    #PUBLIC_HEADER DESTINATION include/bgslibrary COMPONENT dev
    FRAMEWORK DESTINATION "/Library/Frameworks"
  )
  set_target_properties(bgslibrary_core PROPERTIES EXPORT_NAME BGSLibrary)

  install(DIRECTORY bgslibrary/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/bgslibrary)
  install(EXPORT bgslibrary_core-targets
    FILE
      BGSLibraryTargets.cmake
    NAMESPACE
      BGSLibrary::
    DESTINATION
      ${INSTALL_CONFIGDIR}
  )

  include(CMakePackageConfigHelpers)
  write_basic_package_version_file(
    ${CMAKE_CURRENT_BINARY_DIR}/BGSLibraryConfigVersion.cmake
    VERSION ${PROJECT_VERSION}
    COMPATIBILITY AnyNewerVersion
  )

  configure_package_config_file(${CMAKE_CURRENT_LIST_DIR}/cmake-modules/BGSLibraryConfig.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/BGSLibraryConfig.cmake
    INSTALL_DESTINATION ${INSTALL_CONFIGDIR}
  )

  install(FILES
    ${CMAKE_CURRENT_LIST_DIR}/cmake-modules/FindOpenCV.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/BGSLibraryConfig.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/BGSLibraryConfigVersion.cmake
    DESTINATION ${INSTALL_CONFIGDIR}
  )

  if(UNIX)
    # to avoid: error while loading shared libraries: libbgslibrary_core.so
    message(STATUS "You might need to run:")
    message(STATUS "$ LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib")
    message(STATUS "$ export LD_LIBRARY_PATH")
    message(STATUS "after 'make install' to avoid error while loading libbgslibrary_core\n")
  endif()

  if(WIN32)
    message(STATUS "You might need to add ${CMAKE_CURRENT_BINARY_DIR} to your PATH to be able to run your applications.")
    message(STATUS "> set PATH=%PATH%;${CMAKE_CURRENT_BINARY_DIR}\n")
  endif()
endif()

--#-- END ./CMakeLists.txt --#--

--#-- START ./docs/README_CMAKE_USERS_OPENCV2.txt --#--
-------------------------------------------------
-------------- WINDOWS CMAKE USERS --------------

How to build BGSLibrary with OpenCV 2.4.10 and Visual Studio 2013 from CMAKE.

For Linux users, please see the instruction in README_LINUX_USERS.txt file.

Dependencies:
* GIT (tested with git version 2.7.2.windows.1).
* CMAKE for Windows (tested with cmake version 3.1.1).
* Microsoft Visual Studio (tested with VS2013).

Please follow the instructions below:

1) Go to Windows console.

2) Clone BGSLibrary git repository:
git clone https://github.com/andrewssobral/bgslibrary.git

3) Go to bgslibrary/build folder.

4) Set your OpenCV PATH:
setlocal
set OpenCV_DIR=C:\OpenCV2.4.10\build

5) Launch CMAKE:
(For Windows x86 32bits) cmake -DOpenCV_DIR=%OpenCV_DIR% -G "Visual Studio 12" ..
(For Windows x64 64bits) cmake -DOpenCV_DIR=%OpenCV_DIR% -G "Visual Studio 12 Win64" ..

Now, you will see something like (for win64):
-------------------------------------------------
-- The C compiler identification is MSVC 18.0.40629.0
-- The CXX compiler identification is MSVC 18.0.40629.0
-- Check for working C compiler using: Visual Studio 12 2013 Win64
-- Check for working C compiler using: Visual Studio 12 2013 Win64 -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler using: Visual Studio 12 2013 Win64
-- Check for working CXX compiler using: Visual Studio 12 2013 Win64 -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- OpenCV ARCH: x64
-- OpenCV RUNTIME: vc12
-- OpenCV STATIC: OFF
-- Found OpenCV 2.4.10 in C:/OpenCV2.4.10/build/x64/vc12/lib
-- You might need to add C:\OpenCV2.4.10\build\x64\vc12\bin to your PATH to be able to run your applications.
-- Configuring done
-- Generating done
-- Build files have been written to: C:/bgslibrary/build
-------------------------------------------------

6) Include OpenCV binaries in the system path:
(For Windows x86 32bits) set PATH=%PATH%;%OpenCV_DIR%\x86\vc12\bin
(For Windows x64 64bits) set PATH=%PATH%;%OpenCV_DIR%\x64\vc12\bin

7) Open 'bgs.sln' in Visual Studio and switch to 'RELEASE' mode 
7.1) Note if you are using a Visual Studio version superior than 2013, you will need to CANCEL the project wizard update. However, you can go to step (5) and change the Visual Studio version, e.g.: -G "Visual Studio XX", where XX is your Visual Studio version.

8) Click on 'ALL_BUILD' project and build!

9) If everything goes well, you can run bgslibrary in the Windows console as follows:

9.1) Running BGSLibrary with a webcamera:
C:\bgslibrary> build\bgslibrary.exe --use_cam --camera=0

9.2) Running demo code:
C:\bgslibrary> build\bgs_demo.exe dataset/video.avi

9.3) Running demo2 code:
C:\bgslibrary> build\bgs_demo2.exe

Additional information:
* Note that bgslibrary requires a 'config' folder in the working directory.

--#-- END ./docs/README_CMAKE_USERS_OPENCV2.txt --#--

--#-- START ./docs/README_CMAKE_USERS_OPENCV3.txt --#--
-------------------------------------------------
-------------- WINDOWS CMAKE USERS --------------

How to build BGSLibrary with OpenCV 3.2.0 and Visual Studio 2015 from CMAKE.

For Linux users, please see the instruction in README_LINUX_USERS.txt file.

Dependencies:
* GIT (tested with git version 2.7.2.windows.1).
* CMAKE for Windows (tested with cmake version 3.1.1).
* Microsoft Visual Studio (tested with VS2015).

Please follow the instructions below:

1) Go to Windows console.

2) Clone BGSLibrary git repository:
git clone https://github.com/andrewssobral/bgslibrary.git

3) Go to bgslibrary/build folder.

4) Set your OpenCV PATH:
setlocal
set OpenCV_DIR=C:\OpenCV3.2.0\build

5) Launch CMAKE:
cmake -DOpenCV_DIR=%OpenCV_DIR% -G "Visual Studio 14 Win64" ..

Now, you will see something like:
-------------------------------------------------
-- The C compiler identification is MSVC 19.0.24215.1
-- The CXX compiler identification is MSVC 19.0.24215.1
-- Check for working C compiler using: Visual Studio 14 2015 Win64
-- Check for working C compiler using: Visual Studio 14 2015 Win64 -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler using: Visual Studio 14 2015 Win64
-- Check for working CXX compiler using: Visual Studio 14 2015 Win64 -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- OpenCV ARCH: x64
-- OpenCV RUNTIME: vc14
-- OpenCV STATIC: ON
-- Found OpenCV 3.2.0 in C:/OpenCV3.2.0/build/x64/vc14/lib
-- You might need to add C:\OpenCV3.2.0\build\x64\vc14\bin to your PATH to be able to run your applications.
-- OpenCV library status:
--     version: 3.2.0
--     libraries: opencv_world;opencv_videostab;opencv_videoio;opencv_video;opencv_superres;opencv_stitching;opencv_shape;opencv_photo;opencv_objdetect;opencv_ml;opencv_imgproc;opencv_imgcodecs;opencv_highgui;opencv_flann;opencv_features2d;opencv_core;opencv_calib3d
--     include path: C:/OpenCV3.2.0/build/include;C:/OpenCV3.2.0/build/include/opencv
-- Configuring done
-- Generating done
-- Build files have been written to: C:/bgslibrary/build
-------------------------------------------------

6) Include OpenCV binaries in the system path:
set PATH=%PATH%;%OpenCV_DIR%\x64\vc14\bin

7) Open 'bgs.sln' in Visual Studio and switch to 'RELEASE' mode 
7.1) Note if you are using a Visual Studio version superior than 2015, you will need to CANCEL the project wizard update. However, you can go to step (2) and change the Visual Studio version, e.g.: -G "Visual Studio XX", where XX is your Visual Studio version.

8) Click on 'ALL_BUILD' project and build!

9) If everything goes well, you can run bgslibrary in the Windows console as follows:

9.1) Running BGSLibrary with a webcamera:
C:\bgslibrary> build\bgslibrary.exe --use_cam --camera=0

9.2) Running demo code:
C:\bgslibrary> build\bgs_demo.exe dataset/video.avi

9.3) Running demo2 code:
C:\bgslibrary> build\bgs_demo2.exe

Additional information:
* Note that bgslibrary requires a 'config' folder in the working directory.

--#-- END ./docs/README_CMAKE_USERS_OPENCV3.txt --#--

--#-- START ./docs/README_LINUX_USERS.txt --#--
#
# HOW TO COMPILE ON LINUX
#
# Requirements:
# cmake >= 2.8
# opencv >= 2.3.1
#
# Tested with: Ubuntu 14.04 and Ubuntu 16.04

cd build
cmake ..
make
####### OPTIONAL #######
make install
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export LD_LIBRARY_PATH
# for debug: echo $LD_LIBRARY_PATH
# Next, copy the <<config>> folder from bgslibrary repository to your working space.
# Now you can run bgslibrary by: bgs -i video.avi
########################
cd ..
chmod +x *.sh
./run_video.sh
./run_camera.sh
./run_demo.sh
./run_demo2.sh

--#-- END ./docs/README_LINUX_USERS.txt --#--

--#-- START ./docs/README_VS2010_OPENCV2.txt --#--
---------------------------------------------------
BGSLibrary with Visual Studio 2010 and Opencv 2.4.x
---------------------------------------------------
--- Tutorial for Windows x86 32 bits ---
----------------------------------------

1) Install OpenCV
1.a) Download OpenCV 2.4.x from http://opencv.org/
1.b) Install in: C:\OpenCV2.4.x
1.c) Add OpenCV binaries in your Path
C:\OpenCV2.4.x\build\x86\vc10\bin

2) Download BGSLibrary
2.a) Clone bgslibrary on GitHub at C:\bgslibrary

3) Start Visual Studio 2010
3.a) Create New Project
3.b) Select Visual C++ -> Win32 -> Win32 Console Application
3.c) Set project location: C:\bgslibrary
3.d) Set project name: bgslibrary
3.e) Set Empty project 
3.f) Add Main.cpp in [Source Files]
3.g) Add content of c:\bgslibrary\package_bgs\*.* in [Header Files]
3.h) Add content of c:\bgslibrary\package_analysis\*.* in [Header Files]
3.i) Change to [Release] [Win32] mode
3.j) Click on Project->Properties
3.k) Change [Output Directory] to ..\
3.l) Add OpenCV include in [C/C++] -> [Additional Include Directories]
C:\OpenCV2.4.x\build\include;C:\OpenCV2.4.x\build\include\opencv;
3.m) Add OpenCV libraries in [Linker]->[Input]
C:\OpenCV2.4.x\build\x86\vc10\lib\*.lib
3.n) Click in Build and wait
3.o) Run C:\bgslibrary\bgslibrary.exe
Enjoy!

--#-- END ./docs/README_VS2010_OPENCV2.txt --#--

--#-- START ./config/LBFuzzyAdaptiveSOM.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<sensitivity>90</sensitivity>
<trainingSensitivity>240</trainingSensitivity>
<learningRate>38</learningRate>
<trainingLearningRate>255</trainingLearningRate>
<trainingSteps>81</trainingSteps>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/LBFuzzyAdaptiveSOM.xml --#--

--#-- START ./config/StaticFrameDifference.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<enableThreshold>1</enableThreshold>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/StaticFrameDifference.xml --#--

--#-- START ./config/FrameProcessor.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<tictoc>""</tictoc>
<enablePreProcessor>0</enablePreProcessor>
<enableForegroundMaskAnalysis>0</enableForegroundMaskAnalysis>
<enableFrameDifference>1</enableFrameDifference>
<enableStaticFrameDifference>0</enableStaticFrameDifference>
<enableWeightedMovingMean>0</enableWeightedMovingMean>
<enableWeightedMovingVariance>0</enableWeightedMovingVariance>
<enableAdaptiveBackgroundLearning>0</enableAdaptiveBackgroundLearning>
<enableAdaptiveSelectiveBackgroundLearning>0</enableAdaptiveSelectiveBackgroundLearning>
<enableMixtureOfGaussianV2>0</enableMixtureOfGaussianV2>
<enableMixtureOfGaussianV1>0</enableMixtureOfGaussianV1>
<enableKNN>0</enableKNN>
<enableDPAdaptiveMedian>0</enableDPAdaptiveMedian>
<enableDPGrimsonGMM>0</enableDPGrimsonGMM>
<enableDPZivkovicAGMM>0</enableDPZivkovicAGMM>
<enableDPMean>0</enableDPMean>
<enableDPWrenGA>0</enableDPWrenGA>
<enableDPPratiMediod>0</enableDPPratiMediod>
<enableDPEigenbackground>0</enableDPEigenbackground>
<enableDPTexture>0</enableDPTexture>
<enableT2FGMM_UM>0</enableT2FGMM_UM>
<enableT2FGMM_UV>0</enableT2FGMM_UV>
<enableT2FMRF_UM>0</enableT2FMRF_UM>
<enableT2FMRF_UV>0</enableT2FMRF_UV>
<enableFuzzySugenoIntegral>0</enableFuzzySugenoIntegral>
<enableFuzzyChoquetIntegral>0</enableFuzzyChoquetIntegral>
<enableLBSimpleGaussian>0</enableLBSimpleGaussian>
<enableLBFuzzyGaussian>0</enableLBFuzzyGaussian>
<enableLBMixtureOfGaussians>0</enableLBMixtureOfGaussians>
<enableLBAdaptiveSOM>0</enableLBAdaptiveSOM>
<enableLBFuzzyAdaptiveSOM>0</enableLBFuzzyAdaptiveSOM>
<enableLbpMrf>0</enableLbpMrf>
<enableMultiLayer>0</enableMultiLayer>
<enablePBAS>0</enablePBAS>
<enableVuMeter>0</enableVuMeter>
<enableKDE>0</enableKDE>
<enableIMBS>0</enableIMBS>
<enableMultiCue>0</enableMultiCue>
<enableSigmaDelta>0</enableSigmaDelta>
<enableSuBSENSE>0</enableSuBSENSE>
<enableLOBSTER>0</enableLOBSTER>
<enablePAWCS>0</enablePAWCS>
<enableTwoPoints>0</enableTwoPoints>
<enableViBe>0</enableViBe>
<enableCodeBook>0</enableCodeBook>
<enableGMG>0</enableGMG>
</opencv_storage>

--#-- END ./config/FrameProcessor.xml --#--

--#-- START ./config/PAWCS.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<fRelLBSPThreshold>3.3300000429153442e-01</fRelLBSPThreshold>
<nDescDistThresholdOffset>2</nDescDistThresholdOffset>
<nMinColorDistThreshold>20</nMinColorDistThreshold>
<nMaxNbWords>50</nMaxNbWords>
<nSamplesForMovingAvgs>100</nSamplesForMovingAvgs>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/PAWCS.xml --#--

--#-- START ./config/LBAdaptiveSOM.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<sensitivity>75</sensitivity>
<trainingSensitivity>245</trainingSensitivity>
<learningRate>62</learningRate>
<trainingLearningRate>255</trainingLearningRate>
<trainingSteps>55</trainingSteps>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/LBAdaptiveSOM.xml --#--

--#-- START ./config/WeightedMovingMean.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<enableWeight>1</enableWeight>
<enableThreshold>1</enableThreshold>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/WeightedMovingMean.xml --#--

--#-- START ./config/AdaptiveSelectiveBackgroundLearning.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<learningFrames>-1</learningFrames>
<alphaLearn>5.0000000000000003e-02</alphaLearn>
<alphaDetection>5.0000000000000003e-02</alphaDetection>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/AdaptiveSelectiveBackgroundLearning.xml --#--

--#-- START ./config/KNN.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<history>10</history>
<dist2Threshold>400.</dist2Threshold>
<shadowThreshold>5.0000000000000000e-01</shadowThreshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/KNN.xml --#--

--#-- START ./config/WeightedMovingVariance.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<enableWeight>1</enableWeight>
<enableThreshold>1</enableThreshold>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/WeightedMovingVariance.xml --#--

--#-- START ./config/LBMixtureOfGaussians.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<sensitivity>81</sensitivity>
<bgThreshold>83</bgThreshold>
<learningRate>59</learningRate>
<noiseVariance>206</noiseVariance>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/LBMixtureOfGaussians.xml --#--

--#-- START ./config/AdaptiveBackgroundLearning.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<alpha>5.0000000000000003e-02</alpha>
<maxLearningFrames>-1</maxLearningFrames>
<enableThreshold>1</enableThreshold>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/AdaptiveBackgroundLearning.xml --#--

--#-- START ./config/LOBSTER.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<fRelLBSPThreshold>3.6500000953674316e-01</fRelLBSPThreshold>
<nLBSPThresholdOffset>0</nLBSPThresholdOffset>
<nDescDistThreshold>4</nDescDistThreshold>
<nColorDistThreshold>30</nColorDistThreshold>
<nBGSamples>35</nBGSamples>
<nRequiredBGSamples>2</nRequiredBGSamples>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/LOBSTER.xml --#--

--#-- START ./config/PixelBasedAdaptiveSegmenter.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<enableInputBlur>1</enableInputBlur>
<enableOutputBlur>1</enableOutputBlur>
<alpha>7.</alpha>
<beta>1.</beta>
<N>20</N>
<Raute_min>2</Raute_min>
<R_incdec>5.0000000745058060e-02</R_incdec>
<R_lower>18</R_lower>
<R_scale>5</R_scale>
<T_dec>5.0000000745058060e-02</T_dec>
<T_inc>1</T_inc>
<T_init>18</T_init>
<T_lower>2</T_lower>
<T_upper>200</T_upper>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/PixelBasedAdaptiveSegmenter.xml --#--

--#-- START ./config/SigmaDelta.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<ampFactor>1</ampFactor>
<minVar>15</minVar>
<maxVar>255</maxVar>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/SigmaDelta.xml --#--

--#-- START ./config/CodeBook.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<alpha>10</alpha>
<beta>1.</beta>
<learningFrames>10</learningFrames>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/CodeBook.xml --#--

--#-- START ./config/LBSimpleGaussian.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<sensitivity>66</sensitivity>
<noiseVariance>162</noiseVariance>
<learningRate>18</learningRate>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/LBSimpleGaussian.xml --#--

--#-- START ./config/FuzzySugenoIntegral.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<threshold>6.7000000000000004e-01</threshold>
<framesToLearn>10</framesToLearn>
<alphaLearn>1.0000000000000001e-01</alphaLearn>
<alphaUpdate>1.0000000000000000e-02</alphaUpdate>
<colorSpace>1</colorSpace>
<option>2</option>
<smooth>1</smooth>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/FuzzySugenoIntegral.xml --#--

--#-- START ./config/VideoCapture.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<stopAt>0</stopAt>
<input_resize_percent>100</input_resize_percent>
<enableFlip>0</enableFlip>
<use_roi>0</use_roi>
<roi_defined>0</roi_defined>
<roi_x0>0</roi_x0>
<roi_y0>0</roi_y0>
<roi_x1>0</roi_x1>
<roi_y1>0</roi_y1>
<showFPS>1</showFPS>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/VideoCapture.xml --#--

--#-- START ./config/IndependentMultimodal.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<fps>10</fps>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/IndependentMultimodal.xml --#--

--#-- START ./config/VuMeter.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<enableFilter>1</enableFilter>
<binSize>8</binSize>
<alpha>9.9500000000000000e-01</alpha>
<threshold>2.9999999999999999e-02</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/VuMeter.xml --#--

--#-- START ./config/MixtureOfGaussianV2.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<alpha>5.0000000000000003e-02</alpha>
<enableThreshold>1</enableThreshold>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/MixtureOfGaussianV2.xml --#--

--#-- START ./config/FrameDifference.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<enableThreshold>1</enableThreshold>
<threshold>15</threshold>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/FrameDifference.xml --#--

--#-- START ./config/FuzzyChoquetIntegral.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<threshold>6.7000000000000004e-01</threshold>
<framesToLearn>10</framesToLearn>
<alphaLearn>1.0000000000000001e-01</alphaLearn>
<alphaUpdate>1.0000000000000000e-02</alphaUpdate>
<colorSpace>1</colorSpace>
<option>2</option>
<smooth>1</smooth>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/FuzzyChoquetIntegral.xml --#--

--#-- START ./config/KDE.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<framesToLearn>10</framesToLearn>
<SequenceLength>50</SequenceLength>
<TimeWindowSize>100</TimeWindowSize>
<SDEstimationFlag>1</SDEstimationFlag>
<lUseColorRatiosFlag>1</lUseColorRatiosFlag>
<th>9.9999999999999995e-08</th>
<alpha>2.9999999999999999e-01</alpha>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/KDE.xml --#--

--#-- START ./config/LBFuzzyGaussian.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<sensitivity>72</sensitivity>
<bgThreshold>162</bgThreshold>
<learningRate>49</learningRate>
<noiseVariance>195</noiseVariance>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/LBFuzzyGaussian.xml --#--

--#-- START ./config/TwoPoints.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<matchingThreshold>20</matchingThreshold>
<updateFactor>16</updateFactor>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/TwoPoints.xml --#--

--#-- START ./config/ViBe.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<matchingThreshold>20</matchingThreshold>
<matchingNumber>2</matchingNumber>
<updateFactor>16</updateFactor>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/ViBe.xml --#--

--#-- START ./config/SuBSENSE.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<fRelLBSPThreshold>3.3300000429153442e-01</fRelLBSPThreshold>
<nDescDistThresholdOffset>3</nDescDistThresholdOffset>
<nMinColorDistThreshold>30</nMinColorDistThreshold>
<nBGSamples>50</nBGSamples>
<nRequiredBGSamples>2</nRequiredBGSamples>
<nSamplesForMovingAvgs>100</nSamplesForMovingAvgs>
<showOutput>1</showOutput>
</opencv_storage>

--#-- END ./config/SuBSENSE.xml --#--

--#-- START ./config/PreProcessor.xml --#--
<?xml version="1.0"?>
<opencv_storage>
<equalizeHist>0</equalizeHist>
<gaussianBlur>0</gaussianBlur>
<enableShow>1</enableShow>
</opencv_storage>

--#-- END ./config/PreProcessor.xml --#--

--#-- START ./.github/FUNDING.yml --#--
custom: https://www.paypal.me/andrewssobral

--#-- END ./.github/FUNDING.yml --#--

--#-- START ./.github/workflows/cmake_autobuild_ubuntu_ocv3416.yml --#--
name: CMake autobuild on Ubuntu + OpenCV 3.4.16

on:
  pull_request:
    branches: [ "master" ]
  # push:
  #   branches: [ "master" ]

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: Release

jobs:
  container:
    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
    # You can convert this to a matrix build if you need cross-platform coverage.
    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
    runs-on: ubuntu-latest
    container: andrewssobral/opencv3:3.4.16

    steps:
    - uses: actions/checkout@v3

    - name: Compile C++ library
      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
      run: cd build && cmake .. && make -j $(nproc)

    - name: Build and install the python wrapper
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: git config --global --add safe.directory /__w/bgslibrary/bgslibrary && git submodule update --init --recursive && python setup.py build && python setup.py install

    - name: Compile the C++ QT User Interface
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd gui/qt/build && cmake .. && make -j $(nproc)

    - name: Compile examples
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd examples/build && cmake .. && make -j $(nproc)

--#-- END ./.github/workflows/cmake_autobuild_ubuntu_ocv3416.yml --#--

--#-- START ./.github/workflows/docker-image_manualbuild_ocv347.yml --#--
name: Docker image manual build for OpenCV 3.4.7

on:
  workflow_dispatch: {}
  # push:
  #   branches: [ "master" ]
  # pull_request:
  #   branches: [ "master" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: docker login
      env:
        DOCKER_USER: ${{secrets.DOCKER_USER}}
        DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      run: |
        docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
    - name: Build the Docker image
      run: docker build --build-arg INCUBATOR_VER=$(date +%Y%m%d-%H%M%S) --file dockerfiles/opencv_3.4.7py38/Dockerfile -t andrewssobral/bgslibrary:opencv_3.4.7py38 .
    - name: Docker Push
      run: docker push andrewssobral/bgslibrary:opencv_3.4.7py38

--#-- END ./.github/workflows/docker-image_manualbuild_ocv347.yml --#--

--#-- START ./.github/workflows/cmake_manualbuild_ubuntu_ocv347.yml --#--
name: CMake manual build on Ubuntu + OpenCV 3.4.7

on:
  workflow_dispatch: {}

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: Release

jobs:
  container:
    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
    # You can convert this to a matrix build if you need cross-platform coverage.
    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
    runs-on: ubuntu-latest
    container: andrewssobral/opencv3:3.4.7

    steps:
    - uses: actions/checkout@v3

    - name: Compile C++ library
      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
      run: cd build && cmake .. && make -j $(nproc)

    - name: Build and install the python wrapper
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: git config --global --add safe.directory /__w/bgslibrary/bgslibrary && git submodule update --init --recursive && python setup.py build && python setup.py install

    - name: Compile the C++ QT User Interface
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd gui/qt/build && cmake .. && make -j $(nproc)

    - name: Compile examples
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd examples/build && cmake .. && make -j $(nproc)

--#-- END ./.github/workflows/cmake_manualbuild_ubuntu_ocv347.yml --#--

--#-- START ./.github/workflows/cmake_autobuild_ubuntu_ocv460.yml --#--
name: CMake autobuild on Ubuntu + OpenCV 4.6.0

on:
  pull_request:
    branches: [ "master" ]
  # push:
  #   branches: [ "master" ]

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: Release

jobs:
  container:
    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
    # You can convert this to a matrix build if you need cross-platform coverage.
    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
    runs-on: ubuntu-latest
    container: andrewssobral/opencv4:4.6.0

    steps:
    - uses: actions/checkout@v3

    - name: Compile C++ library
      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
      run: cd build && cmake .. && make -j $(nproc)

    - name: Build and install the python wrapper
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: git config --global --add safe.directory /__w/bgslibrary/bgslibrary && git submodule update --init --recursive && python setup.py build && python setup.py install

    - name: Compile the C++ QT User Interface
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd gui/qt/build && cmake .. && make -j $(nproc)

    - name: Compile examples
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd examples/build && cmake .. && make -j $(nproc)

--#-- END ./.github/workflows/cmake_autobuild_ubuntu_ocv460.yml --#--

--#-- START ./.github/workflows/cmake_autobuild_ubuntu_ocv347.yml --#--
name: CMake autobuild on Ubuntu + OpenCV 3.4.7

on:
  pull_request:
    branches: [ "master" ]
  # push:
  #   branches: [ "master" ]

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: Release

jobs:
  container:
    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
    # You can convert this to a matrix build if you need cross-platform coverage.
    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
    runs-on: ubuntu-latest
    container: andrewssobral/opencv3:3.4.7

    steps:
    - uses: actions/checkout@v3

    - name: Compile C++ library
      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
      run: cd build && cmake .. && make -j $(nproc)

    - name: Build and install the python wrapper
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: git config --global --add safe.directory /__w/bgslibrary/bgslibrary && git submodule update --init --recursive && python setup.py build && python setup.py install

    - name: Compile the C++ QT User Interface
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd gui/qt/build && cmake .. && make -j $(nproc)

    - name: Compile examples
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd examples/build && cmake .. && make -j $(nproc)

--#-- END ./.github/workflows/cmake_autobuild_ubuntu_ocv347.yml --#--

--#-- START ./.github/workflows/docker-image_manualbuild_ocv460.yml --#--
name: Docker image manual build for OpenCV 4.6.0

on:
  workflow_dispatch: {}
  # push:
  #   branches: [ "master" ]
  # pull_request:
  #   branches: [ "master" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: docker login
      env:
        DOCKER_USER: ${{secrets.DOCKER_USER}}
        DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      run: |
        docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
    - name: Build the Docker image
      run: docker build --build-arg INCUBATOR_VER=$(date +%Y%m%d-%H%M%S) --file dockerfiles/opencv_4.6.0py38/Dockerfile -t andrewssobral/bgslibrary:opencv_4.6.0py38 .
    - name: Docker Push
      run: docker push andrewssobral/bgslibrary:opencv_4.6.0py38

--#-- END ./.github/workflows/docker-image_manualbuild_ocv460.yml --#--

--#-- START ./.github/workflows/cmake_manualbuild_ubuntu_ocv460.yml --#--
name: CMake manual build on Ubuntu + OpenCV 4.6.0

on:
  workflow_dispatch: {}

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: Release

jobs:
  container:
    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
    # You can convert this to a matrix build if you need cross-platform coverage.
    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
    runs-on: ubuntu-latest
    container: andrewssobral/opencv4:4.6.0

    steps:
    - uses: actions/checkout@v3

    - name: Compile C++ library
      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
      run: cd build && cmake .. && make -j $(nproc)

    - name: Build and install the python wrapper
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: git config --global --add safe.directory /__w/bgslibrary/bgslibrary && git submodule update --init --recursive && python setup.py build && python setup.py install

    - name: Compile the C++ QT User Interface
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd gui/qt/build && cmake .. && make -j $(nproc)

    - name: Compile examples
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd examples/build && cmake .. && make -j $(nproc)

--#-- END ./.github/workflows/cmake_manualbuild_ubuntu_ocv460.yml --#--

--#-- START ./.github/workflows/docker-image_manualbuild_ocv3416.yml --#--
name: Docker image manual build for OpenCV 3.4.16

on:
  workflow_dispatch: {}
  # push:
  #   branches: [ "master" ]
  # pull_request:
  #   branches: [ "master" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: docker login
      env:
        DOCKER_USER: ${{secrets.DOCKER_USER}}
        DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      run: |
        docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
    - name: Build the Docker image
      run: docker build --build-arg INCUBATOR_VER=$(date +%Y%m%d-%H%M%S) --file dockerfiles/opencv_3.4.16py38/Dockerfile -t andrewssobral/bgslibrary:opencv_3.4.16py38 .
    - name: Docker Push
      run: docker push andrewssobral/bgslibrary:opencv_3.4.16py38

--#-- END ./.github/workflows/docker-image_manualbuild_ocv3416.yml --#--

--#-- START ./.github/workflows/cmake_manualbuild_ubuntu_ocv3416.yml --#--
name: CMake manual build on Ubuntu + OpenCV 3.4.16

on:
  workflow_dispatch: {}

env:
  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
  BUILD_TYPE: Release

jobs:
  container:
    # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
    # You can convert this to a matrix build if you need cross-platform coverage.
    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
    runs-on: ubuntu-latest
    container: andrewssobral/opencv3:3.4.16

    steps:
    - uses: actions/checkout@v3

    - name: Compile C++ library
      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
      # run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
      run: cd build && cmake .. && make -j $(nproc)

    - name: Build and install the python wrapper
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: git config --global --add safe.directory /__w/bgslibrary/bgslibrary && git submodule update --init --recursive && python setup.py build && python setup.py install

    - name: Compile the C++ QT User Interface
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd gui/qt/build && cmake .. && make -j $(nproc)

    - name: Compile examples
      # Build your program with the given configuration
      # run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
      run: cd examples/build && cmake .. && make -j $(nproc)

--#-- END ./.github/workflows/cmake_manualbuild_ubuntu_ocv3416.yml --#--

