-
Holger Friedrich authoredHolger Friedrich authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
MotionDetection.cpp 40.10 KiB
/*
* This file is part of the AiBO+ project
*
* Copyright (C) 2005-2013 Csaba Kertész (csaba.kertesz@gmail.com)
*
* AiBO+ is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* AiBO+ is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*
* Paper: Csaba, Kertész: Texture-Based Foreground Detection, International Journal of Signal Processing,
* Image Processing and Pattern Recognition (IJSIP), Vol. 4, No. 4, 2011.
*/
#include "MotionDetection.hpp"
#include "graph.h"
#include <opencv2/opencv.hpp>
#include "MEHistogram.hpp"
#include "MEImage.hpp"
// 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(256 / HUHistogramBins) / log(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[HUHistogramBins], CurrentHistogram2[HUHistogramBins];
unsigned char *ImgData = image.GetImageData();
int RowWidth = image.GetRowWidth();
int RowStart = (HUImageHeight-1)*RowWidth;
memset(CurrentHistogram, 0, sizeof(CurrentHistogram));
// 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, sizeof(CurrentHistogram));
PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel;
PixelData->BackgroundHistogram[i] = true;
}
PixelData->BackgroundRate = 1.0;
PixelData->LifeCycle = 0;
memcpy(PixelData->PreviousHistogram, CurrentHistogram, sizeof(CurrentHistogram));
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, sizeof(CurrentHistogram));
PixelData->Weights[i] = 1.0 / HUHistogramsPerPixel;
PixelData->BackgroundHistogram[i] = true;
}
memcpy(PixelData->PreviousHistogram, CurrentHistogram, sizeof(CurrentHistogram));
PixelData->BackgroundRate = 1.0;
PixelData->LifeCycle = 0;
} else {
// Update the HU data structures
UpdateHUPixelData(PixelData, CurrentHistogram);
if (MDMode == md_DLBPHistograms)
{
memcpy(PixelData->PreviousHistogram, CurrentHistogram, sizeof(CurrentHistogram));
}
}
}
}
// Copy the histogram
memcpy(CurrentHistogram2, CurrentHistogram, sizeof(CurrentHistogram));
// 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));
}
}
}
}
}
}
}
void MotionDetection::UpdateHUPixelData(MEPixelDataType* PixelData, const float *histogram)
{
int MaxIndex = 0;
float MaxValue = -1;
bool Replace = true;
float IntersectionResults[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[HUHistogramsPerPixel][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;
}
}
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,
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[HUOFPointsNumber][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[MaxX+1][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;
}
}
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);
}
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[HUImageWidth / 2][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);
}
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[HUHistogramArea][HUHistogramArea];
int SquareSide = (int)MERound(sqrtf(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);
}