-
Thomas Boy authoredThomas Boy authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Demo.cpp 24.86 KiB
/*
./bgs_demo -i test45/ -a 100 -o test45/results
*/
//cpp c
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <stdio.h> /* printf, scanf, puts, NULL */
#include <stdlib.h> /* srand, rand */
#include <time.h> /* time */
#include <ctime>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <archive.h>
#include <archive_entry.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
//cpp 11X
#include <regex>
#define PROCESS_CENTER_VERSION_MAJOR 2
#define PROCESS_CENTER_VERSION_MINOR 0
#define PROCESS_CENTER_VERSION_MINOR_FIXES 1
//opencv
#include <opencv2/opencv.hpp>
//bgslibrary
#include "package_bgs/bgslibrary.h"
//my class
#include "package_bgs/Tapter.h"
#include "package_bgs/ttoolbox.h"
using namespace cv;
using namespace std;
int g_badSignalFlagAbort = 0;
void my_handler(int signum);
char* getCmdOption(char ** begin, char ** end, const std::string & option);
bool cmdOptionExists(char** begin, char** end, const std::string& option);
std::vector<char> copyDataInBuffer(struct archive *aw);
//! we convert our actual jpg file number to framenumber
int toFrameNumber(std::string filename);
int main(int argc, char * argv[])
{
signal(SIGUSR1, my_handler);
std::cout << "using processcenter " <<PROCESS_CENTER_VERSION_MAJOR <<"."<< PROCESS_CENTER_VERSION_MINOR<<"."<<PROCESS_CENTER_VERSION_MINOR_FIXES<< endl;
std::cout << "Using OpenCV " << CV_MAJOR_VERSION << "." << CV_MINOR_VERSION << "." << CV_SUBMINOR_VERSION << std::endl;
//!** parse programm input****************/
if(cmdOptionExists(argv, argv+argc, "-h"))
{
cout <<" error: please use command as\n./bgs_demo -i inputTarFile -a amountOfJpgFiles -c exactCenterConfFile.xml -o outPutPath -p camparameterFile.xml"<<endl;
return EXIT_FAILURE;
}
if(!cmdOptionExists(argv, argv+argc, "-i")||!cmdOptionExists(argv, argv+argc, "-a")||!cmdOptionExists(argv, argv+argc, "-o") ||!cmdOptionExists(argv, argv+argc, "-p"))
{
cout <<" error: please use command as\n./bgs_demo -i inputTarFile -a amountOfJpgFiles -c exactCenterConfFile.xml -o outPutPath -p camparameterFile.xml"<<endl;
return EXIT_FAILURE;
}
char *testFile = getCmdOption(argv, argv + argc, "-i");
std::string inputFile(".");
if (testFile)
{
//test dir exists
inputFile = string(testFile);
}
int amountFiles = -1;
char *testFileAmount = getCmdOption(argv, argv + argc, "-a");
if (testFileAmount)
{
string s(testFileAmount);
stringstream foo(s);
foo >> amountFiles;
}
char *centerFile = getCmdOption(argv, argv + argc, "-c");
std::string centerFileString(".");
if (centerFile)
{
//test dir exists
centerFileString = string(centerFile);
}
char *testOutputDir = getCmdOption(argv, argv + argc, "-o");
std::string outputDir(".");
if (testOutputDir)
{
//test dir exists
outputDir = string(testOutputDir);
}
char *camerFile = getCmdOption(argv, argv + argc, "-p");
std::string cameraParameterFile(".");
if (camerFile)
{
//test dir exists
cameraParameterFile = string(camerFile);
}
cout <<"args: -i "<<inputFile<<" -a "<<amountFiles << " -c" << centerFileString<<" -o " << outputDir <<" -p " << cameraParameterFile ;
//!**** end parse input***********/
//libarchive things
struct archive *archive;
struct archive *ext;
struct archive_entry *entry;
int r;
int flags = ARCHIVE_EXTRACT_TIME; // see https://linux.die.net/man/3/archive_write_disk for more flags
const char *filename = testFile;
archive = archive_read_new();
ext = archive_write_disk_new();
archive_write_disk_set_options(ext, flags);
archive_read_support_format_tar(archive);
//we get the filename
if (filename != NULL && strcmp(filename, "-") == 0)
filename = NULL;
//and open the handler
if ((r = archive_read_open_filename(archive, filename, 10240)))
{
cerr<<"archive_read_open_filename: error: "<< archive_error_string(archive) <<" will abort"<< endl;
exit(1);
}
/* Background Subtraction Methods */
IBGS *bgs;
//bgs = new FrameDifference;
//bgs = new StaticFrameDifference;
//bgs = new WeightedMovingMean;
//bgs = new WeightedMovingVariance;
//bgs = new MixtureOfGaussianV1; // only on OpenCV 2.x
//bgs = new MixtureOfGaussianV2;
//bgs = new AdaptiveBackgroundLearning;
//bgs = new AdaptiveSelectiveBackgroundLearning;
//bgs = new GMG; // only on OpenCV 2.x
//bgs = new KNN; // only on OpenCV 3.x
//bgs = new DPAdaptiveMedian;
//bgs = new DPGrimsonGMM;
//bgs = new DPZivkovicAGMM;
//bgs = new DPMean;
//bgs = new DPWrenGA;
//bgs = new DPPratiMediod;
//bgs = new DPEigenbackground;
//bgs = new DPTexture;
//bgs = new T2FGMM_UM;
//bgs = new T2FGMM_UV;
//bgs = new T2FMRF_UM;
//bgs = new T2FMRF_UV;
//bgs = new FuzzySugenoIntegral;
//bgs = new FuzzyChoquetIntegral;
//bgs = new MultiLayer;
bgs = new PixelBasedAdaptiveSegmenter;
//bgs = new LBSimpleGaussian;
//bgs = new LBFuzzyGaussian;
//bgs = new LBMixtureOfGaussians;
//bgs = new LBAdaptiveSOM;
//bgs = new LBFuzzyAdaptiveSOM;
//bgs = new LBP_MRF;
//bgs = new VuMeter;
//bgs = new KDE;
//bgs = new IndependentMultimodal;
//bgs = new MultiCue;
//bgs = new SigmaDelta;
//bgs = new SuBSENSE;
//bgs = new LOBSTER;
//bgs = new PAWCS;
//bgs = new TwoPoints;
//bgs = new ViBe;
//bgs = new Tapter;
//Tapter *bgs = new Tapter;
//see paper https://dl.acm.org/citation.cfm?id=2321600
//https://ieeexplore.ieee.org/document/4527178/
//was in benchmark on top https://www.researchgate.net/publication/259340906_A_comprehensive_review_of_background_subtraction_algorithms_evaluated_with_synthetic_and_real_videos
//my own adapter to use the model
//init the random number generator
srand (time(NULL));
clock_t beginAll = clock();
//! we read the center config file for cut out the ROI
//TODO merge this config with the Tapter.xml ??
//circle param
int circleCenterX = 880;
int circleCenterY = 750;
int circleRadius = 700;
cv::String configFileNameCenter(centerFileString);
//read the config
cout << "parameter of centerConfigFile.xml"<<endl;
FileStorage fsCen;
fsCen.open(configFileNameCenter, FileStorage::READ);
if (!fsCen.isOpened())
{
cout << "error during open " <<centerFileString << " will abort\n ";
return EXIT_FAILURE;
}
circleCenterX = (int) fsCen["circleCenterX"];
cout <<"circleCenterX: "<< circleCenterX<<endl;
circleCenterY = (int) fsCen["circleCenterY"];
cout <<"circleCenterY: "<< circleCenterY<<endl;
circleRadius = (int) fsCen["circleRadius"];
cout <<"circleRadius: "<< circleRadius<<endl;
fsCen.release();
//we open the camera config file
cv::String camParam (cameraParameterFile);
Mat intrinsicsCameraMatrix, distortionCoeff;
{
cout << "read cam parameter of: "<< cameraParameterFile ;
FileStorage fs;
fs.open(camParam, FileStorage::READ);
if (!fs.isOpened())
{
cerr << "error during open " << cameraParameterFile << " will abort\n ";
return -1;
}
fs["camera_matrix"] >> intrinsicsCameraMatrix;
std::cout << "camera_matrix = "<< intrinsicsCameraMatrix<<endl;
fs["distortion_coefficients"] >> distortionCoeff;
std::cout << "distortion_coefficients"<< distortionCoeff<<endl;
}
cout <<"a) we process all frames "<<endl;
int everyPic= 60*5;
//std::string fileName;
int frameCounter=0;
int frameCounterOld = -1;
vector<string> myFileErrorList;//list for pure io error
vector<string> myFileListNoContour; //list for no contours found;
vector<string> myFileListAfterContourSelection; //list for no contours found after selection;
//the corruption check is done beforehand
//vector<string> myFileListCorrupted; //list all corrupted jpg files
//here we loop over all picture of the tar archive, independing how many this are
for (;;)
{
//measure time consumption
clock_t begin = clock(); //for every single file
std::vector<char> vec;
//we read the next header
r = archive_read_next_header(archive, &entry);
if (r == ARCHIVE_EOF)
break;
if (r != ARCHIVE_OK)
{
cerr<<"archive_read_next_header: error: "<< archive_error_string(archive) <<" will abort"<< endl;
exit(1);
}
//we get the filename
const char *fileNamePtr = archive_entry_pathname(entry);
std::string filename(fileNamePtr,strlen(fileNamePtr) );
//cout << "fileName: "<< filename <<endl;
//we convert it to a framenumber
frameCounter = toFrameNumber(filename);
if(frameCounterOld>frameCounter)
{
cerr <<" error during read in the file number, we do have non montonic order, will abort";
exit(1);
}
frameCounterOld = frameCounter;
r = archive_write_header(ext, entry);
if (r != ARCHIVE_OK)
{
cerr<<"archive_write_header() error: "<< archive_error_string(ext)<<endl;
}
else
{
//we copy all data to the buffer vector
vec = copyDataInBuffer(archive);
if(vec.empty())
cerr << "error during load data into buffer";
r = archive_write_finish_entry(ext);
if (r != ARCHIVE_OK)
{
cerr<<"archive_write_finish_entry: error: "<< archive_error_string(ext) <<" will abort"<< endl;
exit(1);
}
}
//we read the image buffer as a jpg picture and decode it
Mat img_input = imdecode(Mat(vec), 1);
// //we define the type better to prevent error
// fileName = std::string(inputFile + TToolBox::getFileName(frameCounter));
// cv::String fileNameCV(fileName);
cout <<"\t"<<frameCounter<<"\tof \t"<<amountFiles<<" load file :"<< filename<<endl;
//of data is present
if(img_input.data )
{
//! step 0 we take care about the lense distortion
//! correct lense distortion
Mat imgLenseCorrection = img_input.clone();
//see https://docs.opencv.org/2.4/modules/imgproc/doc/geometric_transformations.html#undistort
cv::undistort(img_input, imgLenseCorrection, intrinsicsCameraMatrix, distortionCoeff);
//! we cut out a smaller ROI,
//! step 1)
img_input = TToolBox::cropImageCircle(imgLenseCorrection,circleCenterX,circleCenterY,circleRadius);
//! normal bgs processing
//! step 2)
cv::Mat img_mask;
cv::Mat img_bkgmodel;
bgs->process(img_input, img_mask, img_bkgmodel); // by default, it shows automatically the foreground mask image
//! step 3) we make we apply a edge detection
//TODO make this in a function, how many times is this listed ??
//TODO read from tapter config
//TODO all parameter from applyCannyEdgeAndCalcCountours also !!
double threshholdMin = 150;
double threshholdMax = 200;
int apertureSize = 3;
std::vector<vector<Point> > contours = TToolBox::applyCannyEdgeAndCalcCountours(img_mask,threshholdMin,threshholdMax,apertureSize);
//define what we will write down
std::vector<vector<Point> > contourSelection;
vector<Point2f> massCenters;
vector<Point> conHull;
Point2f muConvexHullMassCenter(0.0,0.0);
if(!contours.empty())
{
//! step 4) we make a selection out of all counters with area sizes******************************
//we calc all min rotated rectangles for all contour from candy egde detect
//we exlcude very small one and very big ones
vector<RotatedRect> minRect( contours.size() );
//calc boxes around the contours
for( size_t i = 0; i < contours.size(); i++ )
minRect[i] = minAreaRect( Mat(contours[i]) ); //may use boundingRect ?? to use fix non rotated rectangles ?
vector<Point2f> recCenterPoints;
float areaMinThreshold = 150;
float areaMaxThreshold = 300000; //old max threshold was to small //TODO apply moving filter ??, an more adaptive approach
//iterate all rectangles
for( size_t i = 0; i< minRect.size(); i++ )
{
Point2f rect_points[4];
//get all points of the retange
minRect[i].points( rect_points );
//construct contour based on rectangle points because contour != rectangle with points
vector<Point> contourRect;
for(int j=0;j<4;j++)
contourRect.push_back(rect_points[j]);
//calc the area of the contour
double area0 = contourArea(contourRect);
//over stepp all small areas
if(area0<areaMinThreshold||area0>areaMaxThreshold)
{
//cout<<i<<":area0:"<<area0<<" dismissed "<<endl;
//skipe if the area is too small
continue;
}
else
{
//cout<<i<<":area0:"<<area0<<" choose "<<endl;
//get the center of this rectangle
Point2f center = minRect[i].center;
recCenterPoints.push_back(center);
//we also add the this contour to a selection
contourSelection.push_back(contours[i]);
}
}//end iterate all min rectangle
if(!contourSelection.empty())
{
//! step 5) we calc the moments from the contour selection
vector<Moments> mu(contourSelection.size() );
for( size_t i = 0; i < contourSelection.size(); i++ )
{
mu[i] = moments( contourSelection[i], false );
}
// Get the mass centers:
massCenters = vector<Point2f>( contourSelection.size() );
for( size_t i = 0; i < contourSelection.size(); i++ )
{
massCenters[i] = Point2f( mu[i].m10/mu[i].m00 , mu[i].m01/mu[i].m00 );
}
//! step 6) calc convex hull of all points of the contour selection *********
//TODO produce center of convex hull of all polygones
//TODO double check if ne contour sharing a center point ? nearby ??
//we merge all points
vector<Point> allContourPoints;
for (size_t cC = 0; cC < contourSelection.size(); ++cC)
for(size_t cP =0; cP < contourSelection[cC].size(); cP++)
{
Point currentContourPixel = contourSelection[cC][cP];
allContourPoints.push_back(currentContourPixel);
}
// calc the hull ******************
conHull = vector<Point>(allContourPoints.size());
convexHull( Mat(allContourPoints), conHull, false );
// Point roiCenter;
// float roiRadius;
//calc the min circle around
//minEnclosingCircle(conHull,roiCenter,roiRadius);
//we calc the mass center of the convex hull
///we calc the mass center of the convex hull
Moments muConvexHull;
if(!conHull.empty())
{
muConvexHull = moments(conHull, true );
muConvexHullMassCenter= Point2f( muConvexHull.m10/muConvexHull.m00 , muConvexHull.m01/muConvexHull.m00 );
}
}
else //end after selection is empty
{
cerr<<"error, no contour found after selection in file: "<< filename<<endl;
myFileListAfterContourSelection.push_back(filename);
}
}//end if contours are empty, to canny edge found nothing
else
{
cerr<<"error, no contour found at all in file: "<< filename<<endl;
myFileListNoContour.push_back(filename);
}
#ifdef MC_SHOW_STEP_ANALYSE
if(!conHull.empty())//if we any elements, we process further
{
Mat imgConvexHull = Mat::zeros( img_input.size(), CV_8UC3 );
imgConvexHull = Scalar(255,255,255); //fille the picture
Scalar colorB( 0,0,255,255 );//red
polylines(imgConvexHull, hullComplete, true, colorB, 1, 8);
//draw circle around
//circle( imgConvexHull, roiCenter, (int) roiRadius, colorB, 2, 8, 0 );
//we draw it
cv::String outpath2= outputDir;
std::ostringstream convert2;
convert2 << outpath2 <<TToolBox::mNzero(frameCounter) <<"_convex_hull.jpg";
cv::imwrite(convert2.str().c_str(), imgConvexHull);
}
else
cout<<"convex hull has no points will skip file: "<<i<<endl;
#endif
//cout <<" found center at : "<< massCenters.at(0).x<< ";"<<massCenters.at(0).y<<endl;
//! step 8: we write down all our results in yml file
std::string nameOutPutFileData = outputDir + TToolBox::mNzero(frameCounter) + ".yml";
//cout <<"output file:" << nameOutPutFileData;
FileStorage fs(nameOutPutFileData.c_str(), FileStorage::WRITE);
fs << "masscenters" << massCenters;
fs << "polygonselection"<< contourSelection;
fs << "convexhull"<<conHull;
fs << "masscenterconvexhull"<<muConvexHullMassCenter;
fs.release();
//! we write from time to time a dbg picture
if(frameCounter%everyPic==0)
{
Scalar colorRed( 0,0,255,255 );//red
RNG rng(4344234);
Mat imgDebugPaint2 = Mat::zeros( img_input.size(), CV_8UC3 );
imgDebugPaint2 = Scalar(255,255,255); //fill the picture white
//we write all polyies of the selection and the mass centers with a random color
for( size_t i = 0; i< contourSelection.size(); i++ )
{
//random color
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
//contour
drawContours( imgDebugPaint2, contourSelection, i, color, 1, LINE_AA);
//draw the center
circle( imgDebugPaint2, massCenters[i], 4, color, -1, 8, 0 );
}
//we write the convex hull
if(!conHull.empty())
{
//the poly
polylines(imgDebugPaint2, conHull, true, colorRed, 1, 8);
//the center
circle( imgDebugPaint2,muConvexHullMassCenter, 4, colorRed, -1, 8, 0 );
}
//we make a copy
Mat imgOverlay2 = img_input.clone();
//we add a overlay of our paitings
addWeighted( imgDebugPaint2, 0.7, imgOverlay2, 0.3, 0.0, imgOverlay2);
//we write the file down
std::string nameOutPutFileDBGpic = outputDir + TToolBox::mNzero(frameCounter) + std::string(".jpg");
imwrite(nameOutPutFileDBGpic.c_str(),imgOverlay2);
}//end if we write a dbg picture
}//end if the loaded picture has data
else
{
cerr<<"error loading file: "<< filename<<", will skip this file"<<endl;
myFileErrorList.push_back(filename);
}
// }//end if not corrupted picture
// else
// {
// cerr<<"error file: "<< fileName<<" is corrupted, will ignore it"<<endl;
// myFileListCorrupted.push_back(fileName);
// }
//we calc the time which we used for a picture
clock_t end = clock();
double elapsedSecs = double(end - begin) / CLOCKS_PER_SEC;
//we calc the time which was used for all picture
clock_t endAll = clock();
double elapsedSecTotal = double(endAll - beginAll) / CLOCKS_PER_SEC;
cout <<"process single pic:\t"<<elapsedSecs<<" s - \t\t"<<(int)(elapsedSecTotal/60)<<" min -\t"<<(int)(elapsedSecTotal/60/60)<<" h"<<endl;
if(g_badSignalFlagAbort)
frameCounter = amountFiles; //we abort
//if(frameCounter>10)exit(0); //the test abort function
}//end for ever loop
//finishing time
clock_t endAll = clock();
double elapsed = double(endAll - beginAll) / CLOCKS_PER_SEC;
cout <<"process : "<<amountFiles<<" files took:\t"<<(int)(elapsed/60)<<" min -\t"<<(int)(elapsed/60/60)<<" h \n in total"<<endl;
// //we write the random file list to a file
// std::string nameOutRandomFile = outputDir + "randlist.yml";
// FileStorage fs(nameOutRandomFile.c_str(), FileStorage::WRITE);
// fs << "randomlist" << myRandomTrainList;
// fs.release();
//TODO we should merge the file
//we write the random file list to a file
std::string nameOutErrorList = outputDir + "fileErrorList.yml";
cout <<"amount of file errors: "<< myFileErrorList.size()<<endl;
FileStorage fs2(nameOutErrorList.c_str(), FileStorage::WRITE);
fs2 << "fileErrorIOs" << myFileErrorList;
//we write error list no contours found in file
cout <<"amount contour errors with canny edge: "<< myFileListNoContour.size()<<endl;
fs2 << "fileErrorNoContours" << myFileListNoContour;
//we write error list no contours found in file
cout <<"amount contour errors after selection: "<< myFileListAfterContourSelection.size()<<endl;
fs2 << "fileErrorNoContoursAfterSelections" << myFileListAfterContourSelection;
// //we write error list no contours found in file
// cout <<"amount of corrupted jpg files: "<< myFileListCorrupted.size()<<endl;
// fs2 << "fileErrorIOcorruptFiles" << myFileListCorrupted;
//the bgs related things
delete bgs;
//opencv related things
fs2.release();
// capture.release();
cvDestroyAllWindows();
//close lib archive related things
archive_read_close(archive);
archive_read_free(archive);
archive_write_close(ext);
archive_write_free(ext);
return 0;
}
std::vector<char> copyDataInBuffer(struct archive *aw)
{
int r;
const void *buff;
std::vector<char> vec;
size_t size;
#if ARCHIVE_VERSION_NUMBER >= 3000000
int64_t offset;
#else
off_t offset;
#endif
//forever till we get a specific return
for (;;) {
r = archive_read_data_block(aw, &buff, &size, &offset);
if (r == ARCHIVE_EOF)
{
return vec; // everything fine, were are just at the end
}
if (r != ARCHIVE_OK)
{
cerr << "error during read archive "<<endl;
return vec;
}
//a good discussion : https://stackoverflow.com/questions/259297/how-do-you-copy-the-contents-of-an-array-to-a-stdvector-in-c-without-looping
// Method 4: vector::insert
{
//we rename our pointer to avoid a weird compiler warning
char *foo = (char*) buff;
vec.insert(vec.end(), &foo[0], &foo[size]);
}
}
return vec;
}
void my_handler(int signum)
{
if (signum == SIGUSR1)
{
g_badSignalFlagAbort = 1;
cerr << "receive signal to abort"<<endl;
}
}
char* getCmdOption(char ** begin, char ** end, const std::string & option)
{
char ** itr = std::find(begin, end, option);
if (itr != end && ++itr != end)
{
return *itr;
}
return 0;
}
bool cmdOptionExists(char** begin, char** end, const std::string& option)
{
return std::find(begin, end, option) != end;
}
int toFrameNumber(std::string filename)
{
int retVal = -1;
//we cut out all non needed substrings
filename = regex_replace(filename, regex("/"), "");
filename = regex_replace(filename, regex("data_sized"), "");
filename = regex_replace(filename, regex("jpg"), "");
filename = regex_replace(filename, regex("\\."), "");
retVal = std::stoi( filename );
//cout <<"filename out:" << filename << " number: "<< retVal << endl;
return retVal;
}