Click here to Skip to main content
15,894,017 members
Please Sign up or sign in to vote.
1.00/5 (1 vote)
See more:
Hi again
I am working on a image processing project.I can understand matlab program but i have some problem with C++.
I found a C++ program for foreground detection.But i don't understand some of instruction in it.for example:
C++
bgModel[index].mode[m].r[i] = curTextureHist[index].r[i];

whats mean of .mode[m].
and is it writting for one frame?
i don't see the instruction for increasing framenum.
at this code:
C++
unsigned int frameNum = 0;
axisCam->GetImage(image.Ptr(), frameNum);
LBP(image, texture);
Histogram(texture, curTextureHist);
for(int y = REGION_R+TEXTURE_R; y < height-REGION_R-TEXTURE_R; ++y)
{
    for(int x = REGION_R+TEXTURE_R; x < width-REGION_R-TEXTURE_R; ++x)
    {
        int index = x+y*width;

        for(int m = 0; m < NUM_MODES; ++m)
        {
            for(int i = 0; i < 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];
            }
        }
    }
}


and this is the program.
C++
///////////////////////////////////////////////////////////////////////////////
// Texture-based background subtraction based on the paper:
//
// "A texture-based method for modeling the background and
//  detecting moving objects"
// 		by M. Heikkila and M. Pietikainen (2005)
//
// The method has been extended to work with colour images. Strict conditional
// updating is used. Only a single mode is maintained for each pixel.
//
// 
//
// Hit "ESC" to exit the program. You must set AXIS_IP_ADDR to the IP 
// address of your Axis 206 camera.
//
// By Donovan Parks (donovan.parks@gmail.com)
//    January, 2008
//
///////////////////////////////////////////////////////////////////////////////

#include "AxisCamera.h"

#include <fstream>
#include <assert.h>
#include <iostream>

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
#include <pthread.h>

#include "memJpegDecoder.h"
#include "ConnectedComponents.h"


using namespace std;

// IP address of Axis 206 camera
#define AXIS_IP_ADDR "192.168.2.5"

// Global variable that should be set to true in order to cleanly exit program
bool gTerminate = false;

// Worker threads.
void *readVideo( void *ptr );
void *displayVideo( void *ptr );
void *textureBGS( void *ptr );

int main(int argc, const char* argv[])
{
	AxisSettings axisSettings;
	axisSettings.width = 160;
	axisSettings.height = 120;
	axisSettings.compression = 30;
	axisSettings.requestedFPS = 30;
	axisSettings.colorLevel = 0;		// DP: doesn't work??
	axisSettings.rotation = 0;			
	axisSettings.showClock = 0;
	axisSettings.showDate = 0;
	axisSettings.showText = 0;
	
	// initialize Axis camera
	AxisCamera* axisCam = new AxisCamera(axisSettings, AXIS_IP_ADDR);
	
	// create thread to read video
	pthread_t readVideoThread;
	int ret = pthread_create( &readVideoThread, NULL, readVideo, (void*) axisCam);
	if(ret != 0)
	{
		perror("Error");
		exit(ret);
	}
	
	// create thread to display video
	pthread_t displayVideoThread;
	ret = pthread_create( &displayVideoThread, NULL, displayVideo, (void*) axisCam);
	if(ret != 0)
	{
		perror("Error");
		exit(ret);
	}
	
	// create thread to display results of foreground segmentation
	pthread_t textureThread;
	ret = pthread_create( &textureThread, NULL, textureBGS, (void*) axisCam);
	if(ret != 0)
	{
		perror("Error");
		exit(ret);
	}

	// wait till mediod thread is complete before exiting main
	ret = pthread_join(textureThread, NULL);
	if(ret != 0)
	{
		perror("Error");
		exit(ret);
	}
	
	// wait till display video thread is complete before exiting main
	ret = pthread_join(displayVideoThread, NULL);
	if(ret != 0)
	{
		perror("Error");
		exit(ret);
	}
	
	// wait till read video thread is complete before exiting main
	ret = pthread_join(readVideoThread, NULL);
	if(ret != 0)
	{
		perror("Error");
		exit(ret);
	}
	
	delete axisCam;
	
	exit(0);
}

void *readVideo( void *ptr )
{
	AxisCamera* axisCam = (AxisCamera *) ptr;
	
	// continue to acquire images until the user wishes to terminate
	while(!gTerminate)
	{
		axisCam->AcquireImages();
	}
	
	return 0;
}

void *displayVideo( void *ptr )
{
	AxisCamera* axisCam = (AxisCamera *) ptr;
	
	IplImage* image = cvCreateImage(cvSize(axisCam->Width(), axisCam->Height()), 8, 3);
		
	cvNamedWindow("Video Frame",1);
	
	unsigned int frameNum = 0;
	while(!gTerminate)
	{
		axisCam->GetImage(image, frameNum);
		
		cvShowImage("Video Frame",image);
		
		// sleep for 10ms or until user presses a key
		int key = cvWaitKey(10);
		
		// check if user is finished (i.e., ESC is pressed)
		if((key&0xFF) == 27)	
		{
			gTerminate = true;
		}
	}
	
	cvReleaseImage(&image);	
	cvDestroyWindow("Video Frame");
	
	return 0;
}

// --- Texture-based BGS ----------------------------------------------------------------

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 float ALPHA = 0.01f;
const float THRESHOLD = 0.7*(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];
};

void 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 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 ProximityMeasure(TextureHistogram& bgTexture, TextureHistogram& curTextureHist)
{
	int proximity = 0;	
	for(int i = 0; i < NUM_BINS; ++i)
	{
		proximity += min(bgTexture.r[i], curTextureHist.r[i]);
		proximity += min(bgTexture.g[i], curTextureHist.g[i]);
		proximity += min(bgTexture.b[i], curTextureHist.b[i]);
	}
	
	return proximity;	
}

void 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 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(x,y) == 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);
				}
			}				
		}
	}	
}

void *textureBGS( void *ptr )
{
	AxisCamera* axisCam = (AxisCamera *) ptr;
	int width = axisCam->Width();
	int height = axisCam->Height();
	int size = width*height;
	
	// camera image
	RgbImage image = cvCreateImage(cvSize(width, height), 8, 3);
	
	// foreground masks
	BwImage fgMask = cvCreateImage(cvSize(width, height), 8, 1);
	BwImage tempMask  = cvCreateImage(cvSize(width, height), 8, 1);
	cvZero(fgMask.Ptr());
	
	// create background model
	TextureArray* bgModel = new TextureArray[size];
	RgbImage texture = cvCreateImage(cvSize(width, height), 8, 3);
	unsigned char* modeArray = new unsigned char[size];
	TextureHistogram* curTextureHist = new TextureHistogram[size];
	
	// initialize background model
	unsigned int frameNum = 0;
	axisCam->GetImage(image.Ptr(), frameNum);
	LBP(image, texture);
	Histogram(texture, curTextureHist);
	for(int y = REGION_R+TEXTURE_R; y < height-REGION_R-TEXTURE_R; ++y)
	{
		for(int x = REGION_R+TEXTURE_R; x < width-REGION_R-TEXTURE_R; ++x)
		{
			int index = x+y*width;
			
			for(int m = 0; m < NUM_MODES; ++m)
			{
				for(int i = 0; i < 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];
				}
			}
		}
	}

	// connected-components
	ConnectedComponents cc;
	
	// structuring element used for dilation and erosion
	IplConvKernel* dilateElement = cvCreateStructuringElementEx(7, 7, 3, 3,	CV_SHAPE_RECT);
	IplConvKernel* erodeElement = cvCreateStructuringElementEx(3, 3, 1, 1,	CV_SHAPE_RECT);
		
	cvNamedWindow("Before Post Processing",1);
	cvNamedWindow("Texture BGS",1);
	
	while(!gTerminate)
	{
		// get the new image
		axisCam->GetImage(image.Ptr(), frameNum);

		// preprocessing
		//cvSmooth(image.Ptr(), image.Ptr(), CV_GAUSSIAN, 3, 0, 0); 

		// perform background subtraction
		LBP(image, texture);
		Histogram(texture, curTextureHist);
		BgsCompare(bgModel, curTextureHist, modeArray, THRESHOLD, fgMask);
		
		cvShowImage("Before Post Processing", fgMask.Ptr());
		
		// size filtering
		CBlobResult largeBlobs;
		cc.SetImage(&fgMask);
		cc.Find(127);
		cc.FilterMinArea(size/30, largeBlobs);
		fgMask.Clear();
		CvScalar color = CV_RGB(255,255,255);
		cc.ColorBlobs(fgMask.Ptr(), largeBlobs, color);
		
		// morphological operators
		cvDilate(fgMask.Ptr(), fgMask.Ptr(), dilateElement, 1);
		cvErode(fgMask.Ptr(), fgMask.Ptr(), erodeElement, 1);
			
		// show results of background subtraction
		cvShowImage("Texture BGS", fgMask.Ptr());
		
		// update background subtraction		
		UpdateModel(fgMask, bgModel, curTextureHist, modeArray);
		
		// sleep for 10ms or until user presses a key
		int key = cvWaitKey(10);
		
		// check if user is finished (i.e., ESC is pressed)
		if((key&0xFF) == 27)	
		{
			gTerminate = true;
		}
	}
	
	cvReleaseStructuringElement(&dilateElement);
	cvReleaseStructuringElement(&erodeElement);
	
	cvDestroyWindow("Before Post Processing");
	cvDestroyWindow("Texture BGS");
	
	delete[] bgModel;
	delete[] modeArray;
	delete[] curTextureHist;
	
	return 0;
}
Posted
Updated 15-Oct-12 23:57pm
v2
Comments
Legor 16-Oct-12 5:58am    
The framenum probably gets increased in the function axisCam->GetImage() but one have to look in the definition of the function to know that.
Jochen Arndt 16-Oct-12 6:02am    
Edited the question: Added code tags to the huge block at the end and fixed tags so that all code is shown now (it is a bad idea to add formatting tags like <big> to code blocks; this may result in hidden text).
Jochen Arndt 16-Oct-12 6:37am    
.mode[m]:

The period in C/C++ syntax access an element of a structure or class.
The brackets access an element from an array.

frameNum:
I assume that the camera will return the newest image when calling GetImage() with the frame parameter set to zero. When calling it later again (note the 10 ms delay in the loops), a new image will be available. See the documentation for the used camera and the API (see AxisCamera.h) to know hat is returned.
ssssdaaads 16-Oct-12 7:35am    
could u tell me the mean of entire below instruction?
bgModel[index].mode[m].r[i] = curTextureHist[index].r[i];

1 solution

bgModel is a structure (actually an array of such structures, so bgModel[index] selects the structure at the offset of index. Within that structure is another array of structures (mode) which is indexed by the variable m. Within that structure is an array of some type (r) which is indexed by the variable i. So the target of this expression is the ith item of the r array in the mth structure of the mode array, in the indexth structure of the bgModel array. Similarly the source item is the ith item of the r array in the indexth item of the array of curTextureHist structures. As the values of index, m and i increase in the for loop, the code selects the different items in the arrays.

Without drawing a picture it is difficult to make it much clearer.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900