Search code examples
c++opencvcomputer-visionknn

Opencv Error using cv::ml::StatModel::train in order to use KNN


Question #1:

I am trying to implement KNN in opencv 3.4 using Visual Studio 2017 and C++. I have been able to solve my problems so far but yesterday I ran into this error: "A nonstatic member reference must be relative to a specific object"

int main(int argc, const char *argv[]) {

ReadData("smokeDataBase.yml", 0);//14 smoke
ReadData("forestDataBase.yml" , 1); //14 forest
ReadData("skyDataBase.yml", 2); //12 sky

ReadData("testDataBase.yml", 3);


Mat trainClasses = (Mat_<float>(40, 1) << 1, 1, 1, 1, 1, 1, 1, 1,1,1, 1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0); //Couldn't figure out how to do this in some other way


//We prepare the data to use in KNN using self-made function that gets every
//vector in my .yml and appends them together
Mat trainData = prepTrain(forestDataBase, skyDataBase, smokeDataBase);

// learn classifier int K = 10; Ptr knn = ml::KNearest::create(); ml::StatModel::train(trainData, ml::ROW_SAMPLE , trainClasses); //This line gives the error

}

As I can't train the KNN (although KNN is not strictly a learning method) I can't use KNearest::findNearest to actually get what I want from the code. I couldn't find any answer that fit my problem thus I am writing this.

I deleted every part of the code that wasn't used in an attempt to make it easier for you guys to help me.

Question #2:

When I create the variable TrainData I put together all the data that I have on my smoke/forest/skyDatabase. In other words, I append all my data into one single massive vector. Is this the correct way to go? Should I divide into a "vector TrainData" variable in which every TrainData[i] (i from 0 to TrainData.size) is the vector representing a sample of my data?


Solution

  • That's for your first question: Here's the signature of the function train as decribed in official documentation

    virtual bool cv::ml::StatModel::train   (   InputArray      samples,
            int     layout,
            InputArray      responses 
        )   
    

    As you can see, the method is virtual which means YOU CAN'T CALL IT IN THAT WAY!. This function should be implemented by child classes of StatModel. So when you want to use that method, you call the method from the child class implementing this method. The class KNearest implements that. Your code should be changed to:

    Ptr<ml::KNearest> knn = ml::KNearest::create();  
    knn->train(trainData, ml::ROW_SAMPLE , trainClasses);
    

    Now for your second question:

    You can prepare your data as ROW_SAMPLE or COL_SAMPLE. In ROW_SAMPLE each sample is in one line. So if your sample is consisting of three floats and you have 10 samples, you should end up with a Mat object that has 10 rows and 3 columns. In COL_SAMPLE, everything is inverted. So you end up with 3 rows and 10 columns.