Direct Graphical Models  v.1.5.2
Demo Random Model

This is an advanced tutorial. Be sure to get through Demo Train, Demo Feature Extraction and read the Training of a random model article before proceeding with this tutorial.

In this tutorial we use only 2 features, in order to be able to visualize the distribution of the training samples at 2-diensional canvas. For sake of simplicity we also limit the number of states (classes) till 3 and show them with pure red, green and blue colors. The real sample distribution, known from the training image, is than approximated with generative and discriminative methods. In order to reconstruct these approximations, we classify a square of 256 x 256 pixels, using one of the unary (node) trainers. Thus the resulting potential map shows us how concrete classifier sees the feature distribution.

Please note, that we use the values of the partition functions, stored in variable Z. Thus, the generative classifiers try to reconstruct the training samples distribution. They do that with the number of inner parameters, which is much less than the number of training samples. The discriminative methods do not aim to reconstruct the training distribution itself, but provide high and normalized potentials for every pixel of the classification area.

#include "DGM.h"
#include "VIS.h"
#include "FEX.h"
#include "DGM\timer.h"
using namespace DirectGraphicalModels;
int main(int argc, char *argv[])
{
const CvSize imgSize = cvSize(400, 400);
const unsigned int nStates = 3;
const unsigned int nFeatures = 2; // {ndvi, saturation}
if (argc != 5) {
print_help(argv[0]);
return 0;
}
// Reading parameters and images
int nodeModel = atoi(argv[1]);
Mat img = imread(argv[2], 1); resize(img, img, imgSize, 0, 0, INTER_LANCZOS4); // training image
Mat gt = imread(argv[3], 0); resize(gt, gt, imgSize, 0, 0, INTER_NEAREST); // groundtruth for training
gt = shrinkStateImage(gt, nStates); // reduce the number of classes in gt to nStates
float Z; // the value of partition function
CTrainNode * nodeTrainer = NULL;
switch(nodeModel) {
case 0: nodeTrainer = new CTrainNodeNaiveBayes(nStates, nFeatures); Z = 2e34f; break;
case 1: nodeTrainer = new CTrainNodeGMM(nStates, nFeatures); Z = 1.0f; break;
case 2: nodeTrainer = new CTrainNodeCvGMM(nStates, nFeatures); Z = 1.0f; break;
case 3: nodeTrainer = new CTrainNodeKNN(nStates, nFeatures); Z = 1.0f; break;
case 4: nodeTrainer = new CTrainNodeCvRF(nStates, nFeatures); Z = 1.0f; break;
#ifdef USE_SHERWOOD
case 5: nodeTrainer = new CTrainNodeMsRF(nStates, nFeatures); Z = 1.0f; break;
#endif
default: printf("Unknown node_training_model is given\n"); print_help(argv[0]); return 0;
}
CMarkerHistogram marker(nodeTrainer, DEF_PALETTE_3);
// ---------- Features Extraction ----------
vec_mat_t featureVector;
featureVector.push_back(fExtractor.getNDVI(0).autoContrast().get());
featureVector.push_back(fExtractor.getSaturation().invert().get());
// ---------- Training ----------
Timer::start("Training... ");
nodeTrainer->addFeatureVec(featureVector, gt);
nodeTrainer->train();
// ---------- Visualization ----------
if (nodeModel == 0) {
imshow("histogram 1d", marker.drawHistogram());
imshow("histogram 2d", marker.drawHistogram2D());
}
Timer::start("Classifying...");
Mat classMap = marker.drawClassificationMap2D(Z);
imwrite(argv[4], classMap);
imshow("class map 2d", classMap);
cvWaitKey(1000);
return 0;
}

The function, which reduces the amount of classes in the training data by merging some classes into one.

Mat shrinkStateImage(const Mat &gt, byte nStates)
{
// assertions
if (gt.type() != CV_8UC1) return Mat();
Mat res;
gt.copyTo(res);
for (auto it = res.begin<byte>(); it != res.end<byte>(); it++)
*it = *it % nStates;
for (int y = 0; y < res.rows; y++) {
byte *pImg = img.ptr<byte>(y);
byte *pRes = res.ptr<byte>(y);
for (int x = 0; x < img.cols; x++) {
switch (pImg[x]) {
case 0: pRes[x] = 0; break;
case 1: pRes[x] = 0; break;
case 2: pRes[x] = 0; break;
case 3: pRes[x] = 1; break;
case 4: pRes[x] = 2; break;
case 5: pRes[x] = 2; break;
}
} // x
} // y
return res;
}