0

I want to train a HOG based classifier to detect pedestrians in videos, for this I have done computing hog features and storing them in text file with proper labeling. I am turning to use CvSVM utility and there I need a Mat object carrying information of all featureVectors of overall samples (Positive and negative). I found this code that could convert those featureVector file (text file) into Mat but the code

Hogfeat.create(ders.size(),1,CV_32FC1);

for(int i=0;i<ders.size();i++)
{
  Hogfeat.at<float>(i,0)=ders.at(i);

}

It provides error of assertion. I can see the error is because my featureVector ( per image) is of the size 1*3780 but the code is trying to load featureVector at each new row. my basic source of confusion is "WHATthe proper format of Mat file that needs to be fed to CvSVM load function?". here is given the idea of Mat format but that is for 2 feature vectors i.e. height and width as feature vecotor, should that be same for pedestrian detection as well?

Community
  • 1
  • 1
Lahori
  • 1,071
  • 11
  • 20
  • Please give more info. a: what is ders? (a vector, arrar, vector of arrays, array, tomato) what is Hogfeat? if Hogfeat is a cv::Mat then please amend your code, as if we don't know what those things are then how can we possibly tell you how to copy/move them around, think about it! This shows you the format, follow the example. http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html – QED Aug 02 '14 at 19:35
  • I thought you would get the idea from link i provided. well, its a vector of Points that stores the features of HOG per image. "Hogfeat" is the Mat object that 'll store these features so that it could be fed to the function at line 28 in the link you provided, [here](http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html) – Lahori Aug 03 '14 at 05:30
  • But for that function to work, this Mat object should be in proper format and that's where i am not clear how to place those feature in Mat object! – Lahori Aug 03 '14 at 06:29
  • okay in that case you need to decompose it back to a vector per-box. the image may contain many boxes, hence the confusion. To decompose the long vector you will need to know the descriptorSize. Then simply create a mat from each feature vector using the cv::Mat boxDescriptor(1,descriptorSize ,CV_32FC1,OneBoxDescVector.data(),true); constructor. You can push_back into a mat, like a vector also. So you can do for all boxes allDataMat.push_back(cv::Mat(1,descriptorSize ,CV_32FC1,allBoxDescVector[i].data(),true)) where allBoxDescVector is vector> with each entry as one of the ders – QED Aug 03 '14 at 14:04
  • ^^please note that is descriptorSize, not ders.size(), ders.size = no.of boxes * descriptorSize. (you could try reshape after copy all like you have, but i have not tried that myself. reshape to descriptorSize X no.of boxes, it expects a descriptor per row of the matrix) – QED Aug 03 '14 at 14:08
  • hi, sorry too many comments but "well, its a vector of Points that stores the features of HOG per image" doesn't make sense. it should either be a vector with all descriptors in slot[0] or a vector> with each slot having a vector of size discriptorSize. – QED Aug 03 '14 at 14:29
  • ok! i got your point! I 'll try this way now! Thank you for your response. – Lahori Aug 04 '14 at 13:51
  • I did follow your approach & its working fine. when i find size() of allDataMat, it shows [3780 1] which means 3780 collumns and 1 row, right? For testing purpose i used only 68 images of 64*128 size whcih produced complete feature Vector of size (3780*68 =) 257040 fetures. I followed your code i.e. alldataMat.push_back(cv::Mat(1,3780,CV_32FC1,results.data(),true)); Now How may I confirm that whether all features i.e. 257040 are mapped onto Mat object or not? and BTW Thank you so much for your to-the-point response. – Lahori Aug 04 '14 at 15:19
  • I checked it. If i place 3780 in above code i.e. alldataMat.push_back(cv::Mat(1,3780,CV_32FC1,results.data(),true)); it stores only one feature vector but if i replace 3780 with completefeatureVector.siz(), then all the features (in this case 257040) are mapped onto alldataMat object. – Lahori Aug 04 '14 at 16:00
  • you need to to reshape it. reshape(0,68). see answer – QED Aug 04 '14 at 17:14
  • Oh, yes. last night i was getting an error of something like "Response array must contain as many elements as the total number of elements ". But after following your suggestion of reshape(0,68) its working fine! – Lahori Aug 05 '14 at 08:37

1 Answers1

0

this sample should contain the cases that we have discussed above in the comments.

//dummy readers
std:: vector<float> 
dummyDerReaderForOneDer(const vector<float> &pattern)
{
    int i = std::rand() % pattern.size();
    int j = std::rand() % pattern.size();
    vector<float> patternPulNoise(pattern);
    std::random_shuffle(patternPulNoise.begin()+std::min(i,j),patternPulNoise.begin()+std::max(i,j));
    return patternPulNoise;
};

std:: vector<float> 
dummyDerReaderForManyDers(const vector<float> &posPattern,
                                const vector<float> &negPattern, 
                                    vector<float> labels,
                                        float posLabel, float negLabel)
{

    int i,j;
    vector<float> allPatternsInOneVector;

    for(i=0;i< labels.size(); i++)
    {
        vector<float> patternPulNoise;
        if(labels[i]==posLabel)
        {
            patternPulNoise  = dummyDerReaderForOneDer(posPattern);
        }
        if(labels[i]==negLabel)
        {
            patternPulNoise  = dummyDerReaderForOneDer(negPattern);
        }

        for(j=0;j< patternPulNoise.size(); j++)
        {
            allPatternsInOneVector.push_back(patternPulNoise[j]);
        }
    }
    return allPatternsInOneVector;
}



// main harness entry point for detector test
int main (int argc, const char * argv[])
{

    //dummy variables for example
    int posFiles = 128;
    int negFiles = 128;
    int dims = 3780;

    //setup some dummy data
    vector<float> dummyPosPattern;
    dummyPosPattern.assign(1000,1.f);
    dummyPosPattern.resize(dims );
    random_shuffle(dummyPosPattern.begin(),dummyPosPattern.end());

    vector<float> dummyNegPattern;
    dummyNegPattern.assign(1000,1.f);
    dummyNegPattern.resize(dims );
    random_shuffle(dummyNegPattern.begin(),dummyNegPattern.end());

    // the labels and lables mat
    float posLabel = 1.f;
    float negLabel = 2.f;
    cv::Mat cSvmLabels;

    //the data mat
    cv::Mat cSvmTrainingData;

    //dummy linear svm parmas
    SVMParams cSvmParams;
    cSvmParams.svm_type = cv::SVM::C_SVC;
    cSvmParams.C = 0.0100;
    cSvmParams.kernel_type = cv::SVM::LINEAR;
    cSvmParams.term_crit =  cv::TermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS, 1000000, FLT_EPSILON);




    cout << "creating training data. please wait" << endl;
    int i;
    for(i=0;i<posFiles;i++)
    {
        //your feature for one box from file
        vector<float> d = dummyDerReaderForOneDer(dummyPosPattern);

        //push back a new mat made from the vectors data, with copy  data flag on
        //this shows the format of the mat for a single example, (1 (row) X dims(col) ), as  training mat has each **row** as an example;
        //the push_back works like vector add adds each example to the bottom of the matrix
        cSvmTrainingData.push_back(cv::Mat(1,dims,CV_32FC1,d.data(),true));

        //push back a pos label to the labels mat
        cSvmLabels.push_back(posLabel);
    }

    //do same with neg files;
    for(i=0;i<negFiles;i++)
    {
        float a =  rand(); 
        vector<float> d = dummyDerReaderForOneDer(dummyNegPattern);
        cSvmTrainingData.push_back(cv::Mat(1,dims,CV_32FC1,d.data(),true));
        cSvmLabels.push_back(negLabel);
    }

    //have a look
    cv::Mat viz;
    cSvmTrainingData.convertTo(viz,CV_8UC3);
    viz = viz*255;
    cv::imshow("svmData", viz);
    cv::waitKey(10);
    cout << "press any key to continue" << endl;
    getchar();

    viz.release();

    //create the svm;
    cout << "training, please wait" << endl;

    CvSVM svm;
    svm.train(cSvmTrainingData,cSvmLabels,cv::Mat(),cv::Mat(),cSvmParams);

    cout << "testing, please wait" << endl;
    //test the svm with a large amount of new unseen fake one at a time
    int totExamples = 10000;
    float TP(0.f), FP(0.f), FN(0.f), TN(0.f);
    for(i=0;i<totExamples; i++)
    {
        vector<float> dPos = dummyDerReaderForOneDer(dummyPosPattern);
        cv::Mat dMatPos(1,dims,CV_32FC1,dPos.data(),true);
        float predLabelPos = svm.predict(dMatPos);

        if(predLabelPos == posLabel) TP++;
        else(FN++);

        vector<float> dNeg = dummyDerReaderForOneDer(dummyNegPattern);
        cv::Mat dMatNeg(1,dims,CV_32FC1,dNeg.data(),true);
        float predLabelNeg = svm.predict(dMatNeg);

        if(predLabelNeg == negLabel) TN++;
        else(FP++);

        if(i%1000==0)
            cout << "testing " << i << "of " <<  totExamples << endl; 
    }

    //http://en.wikipedia.org/wiki/Precision_and_recall
    float sensitivity  = TP / (TP + FN);
    float specificity = TN / (TN + FP);
    float precision = TP / (TP + FP);

    cout << "sensitivity: " <<  sensitivity << endl;
    cout << "specificity: " <<  specificity << endl;
    cout << "precision: " <<  precision << endl;

    cout << "press any key to continue" << endl;
    getchar();

    cout << "creating test data, please wait" << endl;
    //test the svm with a large amount of new unseen fake data all at one time
    TP=FP=FN=TN = 0.f;
    vector<float> testLabels;
    for(i=0;i<int(totExamples/2);i++)
    {
        testLabels.push_back(posLabel);
    }
    for(i;i<totExamples;i++)
    {
        testLabels.push_back(negLabel);
    }


    vector<float> allExamples = dummyDerReaderForManyDers(dummyPosPattern,dummyNegPattern,testLabels,posLabel,negLabel);

    //on big mat
    cv::Mat allExamplesMat(1,allExamples.size(),CV_32FC1,allExamples.data(),true);

    //need reshape to one example per row

    allExamplesMat = allExamplesMat.reshape(0,totExamples);

    //have a look
    allExamplesMat.convertTo(viz,CV_8UC3);
    viz = viz*255;
    cv::imshow("testData", viz);
    cv::waitKey(10);
    cout << "press any key to continue" << endl;
    getchar();
    viz.release();

    //test them all at once ( uses intel tbb :)
    cout << "predict all at once, please wait" << endl;  
    cv::Mat predLabels_mat;
    svm.predict(allExamplesMat,predLabels_mat);

    //evaluate
    for(i=0;i<testLabels.size();i++)
    {
        float testLabel = testLabels.at(i);
        float predLabel = predLabels_mat.at<float>(i);
        if(testLabel==predLabel)
        {
            if(testLabel==posLabel) TP++;
            else TN++;

        }
        else
        {
            if(testLabel==posLabel) FP++;
            else FN++;

        }

    }

    //http://en.wikipedia.org/wiki/Precision_and_recall
    sensitivity  = TP / (TP + FN);
    specificity = TN / (TN + FP);
    precision = TP / (TP + FP);

    cout << "sensitivity: " <<  sensitivity << endl;
    cout << "specificity: " <<  specificity << endl;
    cout << "precision: " <<  precision << endl;

    cout << "press any key to continue" << endl;
    getchar();
    return(0);
}
QED
  • 808
  • 8
  • 11
  • That's so nice of you @QED. I understand your flow of code & your style is great. One thing that i do not understand is the line in very first function "std::random_shuffle(patternPulNoise.begin()+std::min(i,j),patternPulNoise.begin‌​()+std::max(i,j)); " what it has to do with reading descriptor values? as i see, it is shufftling the features only! I must mention that the "viz" thing is great but unfortunately i don't know what it reveals :( But Thank you so much for your time! – Lahori Aug 05 '14 at 09:04
  • Hi, that is only a dummy function to simulate a feature vector, the random shuffle just makes sure each simulated feature is slightly different from each other for a more realistic test. You don't need those functions, your will be generating/reading real features. – QED Aug 05 '14 at 12:58
  • What is the difference b/w predicted & testing set since you used two different loops, one for each i.e. predict and test. – Lahori Aug 05 '14 at 13:51
  • one, uses 1 feature to predict that one instance, the other puts many instances into one matrix and predicts for all of them, giving a mat of predictions back, one for each example. This was mainly to show the reshape of matrix is needed after creating the huge mat. – QED Aug 05 '14 at 16:54
  • ok. now i want to use this trained model in detect() function and for this it should be in single row vector form, right? to accomplish that, i am following [this](http://pastebin.com/F0YNFWnA). but i don't know how to get those alpha values. Can you identify me where to look for them and do you think that code is well written? – Lahori Aug 05 '14 at 17:10
  • please see http://stackoverflow.com/questions/15033363/obtaining-weights-in-cvsvm-the-svm-implementation-of-opencv. extend cvSvm and add that fuction to get the weight vector, than just add it to hog detector using HOGDescriptor::setSVMDetector method. The Hog detector will take care of the rest. make sure all hog parmas are the same or else you will get assert error. – QED Aug 05 '14 at 17:18