OpenCV image recognition - setting up ANN MLP
<p>I am new in OpenCV world and neural networks but I have some coding experience in C++/Java.</p>

<hr>

<p>I created my first ANN MLP and learned it the XOR:</p>

<pre><code>#include &lt;opencv2/core.hpp&gt;
#include &lt;opencv2/imgcodecs.hpp&gt;
#include &lt;opencv2/highgui.hpp&gt;
#include &lt;opencv2/imgproc/imgproc.hpp&gt;
#include &lt;opencv2/ml/ml.hpp&gt;

#include &lt;iostream&gt;
#include &lt;iomanip&gt;

using namespace cv;
using namespace ml;
using namespace std;

void print(Mat&amp; mat, int prec)
{
    for (int i = 0; i&lt;mat.size().height; i++)
    {
        cout &lt;&lt; "[";
        for (int j = 0; j&lt;mat.size().width; j++)
        {
            cout &lt;&lt; fixed &lt;&lt; setw(2) &lt;&lt; setprecision(prec) &lt;&lt; mat.at&lt;float&gt;(i, j);
            if (j != mat.size().width - 1)
                cout &lt;&lt; ", ";
            else
                cout &lt;&lt; "]" &lt;&lt; endl;
        }
    }
}

int main()
{
    const int hiddenLayerSize = 4;
    float inputTrainingDataArray[4][2] = {
        { 0.0, 0.0 },
        { 0.0, 1.0 },
        { 1.0, 0.0 },
        { 1.0, 1.0 }
    };
    Mat inputTrainingData = Mat(4, 2, CV_32F, inputTrainingDataArray);

    float outputTrainingDataArray[4][1] = {
        { 0.0 },
        { 1.0 },
        { 1.0 },
        { 0.0 }
    };
    Mat outputTrainingData = Mat(4, 1, CV_32F, outputTrainingDataArray);

    Ptr&lt;ANN_MLP&gt; mlp = ANN_MLP::create();

    Mat layersSize = Mat(3, 1, CV_16U);
    layersSize.row(0) = Scalar(inputTrainingData.cols);
    layersSize.row(1) = Scalar(hiddenLayerSize);
    layersSize.row(2) = Scalar(outputTrainingData.cols);
    mlp-&gt;setLayerSizes(layersSize);

    mlp-&gt;setActivationFunction(ANN_MLP::ActivationFunctions::SIGMOID_SYM);

    TermCriteria termCrit = TermCriteria(
        TermCriteria::Type::COUNT + TermCriteria::Type::EPS,
        100000000,
        0.000000000000000001
    );
    mlp-&gt;setTermCriteria(termCrit);

    mlp-&gt;setTrainMethod(ANN_MLP::TrainingMethods::BACKPROP);

    Ptr&lt;TrainData&gt; trainingData = TrainData::create(
        inputTrainingData,
        SampleTypes::ROW_SAMPLE,
        outputTrainingData
    );

    mlp-&gt;train(trainingData
        /*, ANN_MLP::TrainFlags::UPDATE_WEIGHTS
        + ANN_MLP::TrainFlags::NO_INPUT_SCALE
        + ANN_MLP::TrainFlags::NO_OUTPUT_SCALE*/
    );

    for (int i = 0; i &lt; inputTrainingData.rows; i++) {
        Mat sample = Mat(1, inputTrainingData.cols, CV_32F, inputTrainingDataArray[i]);
        Mat result;
        mlp-&gt;predict(sample, result);
        cout &lt;&lt; sample &lt;&lt; " -&gt; ";// &lt;&lt; result &lt;&lt; endl;
        print(result, 0);
        cout &lt;&lt; endl;
    }

    return 0;
}
</code></pre>

<p>It works very well for this simple problem, I also learn this network the 1-10 to binary conversion.</p>

<hr>

<p>But i need to use MLP for simple image classification - road signs. I write the code for loading training images and preparing matrix for learning but I'm not able to train the network - it "learn" in one second even with 1 000 000 iterations! And it produce garbage results, the same for all inputs!</p>

<hr>

<p>Here are my <a href="https://dl.dropboxusercontent.com/u/32971403/A.zip" rel="nofollow" title="dropbox zip">test images</a> and the source code:</p>

<pre><code>#include &lt;opencv2/core.hpp&gt;
#include &lt;opencv2/imgcodecs.hpp&gt;
#include &lt;opencv2/highgui.hpp&gt;
#include &lt;opencv2/imgproc/imgproc.hpp&gt;
#include &lt;opencv2/ml/ml.hpp&gt;

#include &lt;iostream&gt;
#include &lt;chrono&gt;
#include &lt;memory&gt;
#include &lt;iomanip&gt;
#include &lt;climits&gt;

#include &lt;Windows.h&gt;

using namespace cv;
using namespace ml;
using namespace std;
using namespace chrono;

const int WIDTH_SIZE = 50;
const int HEIGHT_SIZE = (int)(WIDTH_SIZE * sqrt(3)) / 2;
const int IMAGE_DATA_SIZE = WIDTH_SIZE * HEIGHT_SIZE;

void print(Mat&amp; mat, int prec)
{
    for (int i = 0; i&lt;mat.size().height; i++)
    {
        cout &lt;&lt; "[ ";
        for (int j = 0; j&lt;mat.size().width; j++)
        {
            cout &lt;&lt; fixed &lt;&lt; setw(2) &lt;&lt; setprecision(prec) &lt;&lt; mat.at&lt;float&gt;(i, j);
            if (j != mat.size().width - 1)
                cout &lt;&lt; ", ";
            else
                cout &lt;&lt; " ]" &lt;&lt; endl;
        }
    }
}

bool loadImage(string imagePath, Mat&amp; outputImage)
{
    // load image in grayscale
    Mat image = imread(imagePath, IMREAD_GRAYSCALE);
    Mat temp;

    // check for invalid input
    if (image.empty()) {
        cout &lt;&lt; "Could not open or find the image" &lt;&lt; std::endl;
        return false;
    }

    // resize the image
    Size size(WIDTH_SIZE, HEIGHT_SIZE);
    resize(image, temp, size, 0, 0, CV_INTER_AREA);

    // convert to float 1-channel
    temp.convertTo(outputImage, CV_32FC1, 1.0/255.0);

    return true;
}

vector&lt;string&gt; getFilesNamesInFolder(string folder)
{
    vector&lt;string&gt; names;
    char search_path[200];
    sprintf(search_path, "%s/*.*", folder.c_str());
    WIN32_FIND_DATA fd;
    HANDLE hFind = ::FindFirstFile(search_path, &amp;fd);
    if (hFind != INVALID_HANDLE_VALUE) {
        do {
            // read all (real) files in current folder
            // , delete '!' read other 2 default folder . and ..
            if (!(fd.dwFileAttributes &amp; FILE_ATTRIBUTE_DIRECTORY)) {
                names.push_back(fd.cFileName);
            }
        } while (::FindNextFile(hFind, &amp;fd));
        ::FindClose(hFind);
    }
    return names;
}

class Sign {
public:
    enum class Category { A = 'A', B = 'B', C = 'C', D = 'D' };

    Mat image;
    Category category;
    int number;

    Sign(Mat&amp; image, string name) :image(image) {
        category = static_cast&lt;Category&gt;(name.at(0));
        number = stoi(name.substr(2, name.length()));
    };
};

vector&lt;Sign&gt; loadSignsFromFolder(String folderName) {
    vector&lt;Sign&gt; roadSigns;

    for (string fileName : getFilesNamesInFolder(folderName)) {
        Mat image;
        loadImage(folderName + fileName, image);
        roadSigns.emplace_back(image, fileName.substr(0, (fileName.length() - 4))); //cut .png
    }

    return roadSigns;
}

void showSignsInWindows(vector&lt;Sign&gt; roadSigns) {
    for (Sign sign : roadSigns) {
        String windowName = "Sign " + to_string(sign.number);
        namedWindow(windowName, WINDOW_AUTOSIZE);
        imshow(windowName, sign.image);
    }
    waitKey(0);
}

Mat getInputDataFromSignsVector(vector&lt;Sign&gt; roadSigns) {
    Mat roadSignsImageData;

    for (Sign sign : roadSigns) {
        Mat signImageDataInOneRow = sign.image.reshape(0, 1);
        roadSignsImageData.push_back(signImageDataInOneRow);
    }

    return roadSignsImageData;
}

Mat getOutputDataFromSignsVector(vector&lt;Sign&gt; roadSigns) {
    int signsCount = (int) roadSigns.size();
    int signsVectorSize = signsCount + 1;

    Mat roadSignsData(0, signsVectorSize, CV_32FC1);

    int i = 1;
    for (Sign sign : roadSigns) {
        vector&lt;float&gt; outputTraningVector(signsVectorSize);
        fill(outputTraningVector.begin(), outputTraningVector.end(), -1.0);
        outputTraningVector[i++] = 1.0;

        Mat tempMatrix(outputTraningVector, false);
        roadSignsData.push_back(tempMatrix.reshape(0, 1));
    }

    return roadSignsData;
}

int main(int argc, char* argv[])
{
    if (argc != 2) {
        cout &lt;&lt; " Usage: display_image ImageToLoadAndDisplay" &lt;&lt; endl;
        return -1;
    }

    const int hiddenLayerSize = 500;

    vector&lt;Sign&gt; roadSigns = loadSignsFromFolder("../../../Znaki/A/");
    Mat inputTrainingData = getInputDataFromSignsVector(roadSigns);
    Mat outputTrainingData = getOutputDataFromSignsVector(roadSigns);

    Ptr&lt;ANN_MLP&gt; mlp = ANN_MLP::create();

    Mat layersSize = Mat(3, 1, CV_16U);
    layersSize.row(0) = Scalar(inputTrainingData.cols);
    layersSize.row(1) = Scalar(hiddenLayerSize);
    layersSize.row(2) = Scalar(outputTrainingData.cols);
    mlp-&gt;setLayerSizes(layersSize);

    mlp-&gt;setActivationFunction(ANN_MLP::ActivationFunctions::SIGMOID_SYM, 1.0, 1.0);

    mlp-&gt;setTrainMethod(ANN_MLP::TrainingMethods::BACKPROP, 0.05, 0.05);
    //mlp-&gt;setTrainMethod(ANN_MLP::TrainingMethods::RPROP);

    TermCriteria termCrit = TermCriteria(
        TermCriteria::Type::MAX_ITER //| TermCriteria::Type::EPS,
        ,100 //(int) INT_MAX
        ,0.000001
    );
    mlp-&gt;setTermCriteria(termCrit);

    Ptr&lt;TrainData&gt; trainingData = TrainData::create(
        inputTrainingData,
        SampleTypes::ROW_SAMPLE,
        outputTrainingData
    );

    auto start = system_clock::now();
    mlp-&gt;train(trainingData
        //, //ANN_MLP::TrainFlags::UPDATE_WEIGHTS
        , ANN_MLP::TrainFlags::NO_INPUT_SCALE
        + ANN_MLP::TrainFlags::NO_OUTPUT_SCALE
    );
    auto duration = duration_cast&lt;milliseconds&gt; (system_clock::now() - start);
    cout &lt;&lt; "Training time: " &lt;&lt; duration.count() &lt;&lt; "ms" &lt;&lt; endl;

    for (int i = 0; i &lt; inputTrainingData.rows; i++) {
        Mat result;
        //mlp-&gt;predict(inputTrainingData.row(i), result);
        mlp-&gt;predict(roadSigns[i].image.reshape(0, 1), result);
        //cout &lt;&lt; result &lt;&lt; endl;
        print(result, 2);
    }


    //showSignsInWindows(roadSigns);
    return 0;
}
</code></pre>

<p>What is wrong in this code, that XOR works but images not? I cheked the input and output matrix and they're correct... could somebody also explain me when to/shoud I use the ANN_MLP::TrainFlags::NO_INPUT_SCALE and ANN_MLP::TrainFlags::NO_OUTPUT_SCALE or what values of setActivationFunction and setTrainMethod parameters should I use?</p>

<hr>

<p>Thanks!</p>