Skip to content
Snippets Groups Projects
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;
}