/*! * \file main_fgngFib.cpp * * \brief Implementation of a simple test application for the FGNG algorithm * with Fibonacci heap * * \author Carlos Augusto Teixeira Mendes * \date may, 2013 */ #include "fgngFib.h" #include #include #include #include #include #include #include #include #include #include #include // Configuration parameters for GNG algorithm #define LAMBDA 300 //!< Steps between node creations #define EPSILON_B 0.05 //!< Adaptation factor for the winning node #define EPSILON_N 0.0006 //!< Adaptation factor for neighbors of the winning node #define AGEMAX 80 //!< Maximum age for an edge #define ALPHA 0.5 //!< Error decrement factor after node insertion #define BETA 0.0005 //!< Aging factor for nodes #define RTREE_MAXCHILDS 8 //!< Maximum number of childs per RTree node #define RTREE_MINCHILDS 4 //!< Minimum number of childs per RTree node //! Random seed used to init the random number generator. If zero, the seed will be based on current time #define RANDOM_SEED 0 //! Dimension size when the number of dimensions > 2 #define DIM_SIZE 200 //! Auxiliar structure to store context information for the 2D sampler struct SamplerContext2D { //! Probability map. Lists all point in the sample image that can be returned by the sampler QVector probMap; //! Image width int imageWidth; //! Pixmap with the source image QPixmap* pixmap; }; //! Show program options. Returns false. bool showUsage() { #if (FGNG_DIMENSION == 2) printf("Usage: fgngFib_test input_image num_nodes [nnalg] [-noGraphFile]\n\n" " input_image: Image used for 2d sampling\n" " num_nodes: The number of nodes that should be created\n" " nnalg: The Nearest Neighbor algorithm that should be used.\n" " Options: 0 = default = RKV-PP\n" " 1 = HS\n" " 2 = Modified HS\n" " 3 = RKV-PP\n" " -noGraphFile: If defined, no graph text file will be generated\n\n" "To change other FGNG parameters, please check the constants on the\n" "begining of the main_gng.cpp source file.\n"); #else printf("Usage: fgngFib_test num_nodes [nnalg] [-noGraphFile]\n\n" " num_nodes: The number of nodes that should be created\n" " nnalg: The Nearest Neighbor algorithm that should be used.\n" " Options: 0 = default = RKV-PP\n" " 1 = HS\n" " 2 = Modified HS\n" " 3 = RKV-PP\n" " -noGraphFile: If defined, no graph text file will be generated\n\n" "To change other FGNG parameters, please check the constants on the\n" "begining of the main_gng.cpp source file.\n"); #endif return false; } /*! \brief Checks user parameters passed to the executable and defines parameters needed for the algorithm execution \param pars List of input parameters \param numNodes Returns the number of nodes tat will be generated \param nnalg Returns the nearest node algorithm \param inputImage Returns the name of the inputImage used by the 2d sampler. Filled with an empty string for k-dimensional case \param outputImage If inputImage is not empty, returns the name of the output file that should contain the result graph image \param outputGraphFile Returns the name of the output text file to be generated with the graph geometry \return Returns false if the necessary parameters where not given by the user. In that case shows a usage message in the console. */ bool getExecParameters(QStringList pars, int* numNodes, int* nnalg, QString& inputImage, QString& outputImage, QString& outputGraphFile) { #if (FGNG_DIMENSION == 2) if(pars.count() < 3 || pars.count() > 5) return showUsage(); // First parameter is the input image inputImage = pars.at(1); // Second parameter is the number of nodes bool ok; *numNodes = pars.at(2).toInt(&ok); if(!ok) { printf("Invalid number of nodes\n"); return showUsage(); } // Optional parameters are the NN algorithm and the flag to ommit the // output file bool nographfile = false; *nnalg = 0; for(int i=3; i 4) return showUsage(); // First parameter is the number of nodes bool ok; *numNodes = pars.at(1).toInt(&ok); if(!ok) { printf("Invalid number of nodes\n"); return showUsage(); } // Optional parameters are the NN algorithm and the flag to ommit the // output file bool nographfile = false; *nnalg = 0; for(int i=2; iprobMap.size(); int pos = c->probMap[index]; samplePos[0] = pos % c->imageWidth; samplePos[1] = pos / c->imageWidth; } //! Sampler for k dimensional test cases. Fills samplePos with a uniform distribution in each dimension void kDSampler(void* context, double* samplePos) { Q_UNUSED(context); for(int i=0; iimageWidth = image.width(); // Creates the probability map including in it every point // with rgb value different than 1, 1, 1 int nx = image.width(); int ny = image.height(); QRgb white = qRgb(255, 255, 255); for(int y=0, i=0; yprobMap.append(i); } } // Creates a pixmap from the src image so that we can draw the output graph context->pixmap = new QPixmap(QPixmap::fromImage(image)); *sampler = twoDSampler; #else Q_UNUSED(inputImage); Q_UNUSED(context); *sampler = kDSampler; #endif } //! Creates an output image with the generated graph on top of the original sample image void createGraphImage(QString outputImage, QPixmap* pixmap, const FGngFib& gng) { assert(FGNG_DIMENSION == 2); // We are going to draw over the original image pixmap QPainter painter(pixmap); // Draw graph int nodeListSize; FGngNode** nodeList = gng.nodeList(&nodeListSize); QPen pointPen(Qt::red); pointPen.setWidth(5); QPen edgePen(Qt::black); for(int i=0; ipos(); int xn = (int)(pos[0] + 0.5); int yn = (int)(pos[1] + 0.5); // Draw edges to vertices that where not drawn yet // (having an index bigger than our index) painter.setPen(edgePen); foreach(const FGngNode* p, n->neighbours()) { if(p->index() > i) { const double* ppos = p->pos(); painter.drawLine(xn, yn, (int)(ppos[0] + 0.5), (int)(ppos[1] + 0.5)); } } // Draw vertex painter.setPen(pointPen); painter.drawPoint(xn, yn); } // Save image to file pixmap->save(outputImage); } //! Creates a text file containing the topology of the generated graph void createGraphFile(QString outputFile, const FGngFib& gng, unsigned seed) { // Opens the file for writing QFile f(outputFile); if(!f.open(QIODevice::WriteOnly | QIODevice::Text)) { printf("Error saving graph image to file %s\n", qPrintable(outputFile)); return; } // Save parameters data const FGngParameters& par = gng.parameters(); f.write(QString("Parameters:\n").toLatin1()); f.write(QString("----------------------\n").toLatin1()); f.write(QString("Dimensions: %1\n").arg(FGNG_DIMENSION).toLatin1()); f.write(QString("Nodes: %1\n").arg(par.nodes).toLatin1()); f.write(QString("Lambda: %1\n").arg(par.lambda).toLatin1()); f.write(QString("Epsilon B: %1\n").arg(par.epsilonB).toLatin1()); f.write(QString("Epsilon N: %1\n").arg(par.epsilonN).toLatin1()); f.write(QString("Age max: %1\n").arg(par.ageMax).toLatin1()); f.write(QString("Alfa: %1\n").arg(par.alpha).toLatin1()); f.write(QString("Beta: %1\n").arg(par.beta).toLatin1()); f.write(QString("Seed: %1\n\n").arg(seed).toLatin1()); f.write(QString("RTree max nodes: %1\n").arg(par.rtreeMaxChilds).toLatin1()); f.write(QString("RTree min nodes: %1\n").arg(par.rtreeMinChilds).toLatin1()); f.write(QString("RTree NN algorithm: %1\n\n").arg(par.rtreeNNAlg).toLatin1()); // Save graph data f.write(QString("Graph:\n\n").toLatin1()); #if (FGNG_DIMENSION == 2) f.write(QString("Index, (x, y), Neighbor List\n").toLatin1()); #else f.write(QString("Index, (x, y, ...), Neighbor List\n").toLatin1()); #endif f.write(QString("----------------------\n").toLatin1()); int nodeListSize; FGngNode** nodeList = gng.nodeList(&nodeListSize); for(int i=0; ipos(); #if (FGNG_DIMENSION == 2) f.write(QString("%1, (%2, %3)").arg(i) .arg((int)(pos[0] + 0.5)) .arg((int)(pos[1] + 0.5)).toLatin1()); #else f.write(QString("%1, (%2").arg(i).arg(pos[0]).toLatin1()); for(int j=1; jneighbours()) f.write(QString(", %1").arg(p->index()).toLatin1()); f.write(QString("\n").toLatin1()); } // Close the file f.close(); } int main( int argc, char** argv ) { QApplication app( argc, argv ); // Get number of nodes and graph output file (optional). // If working in 2D, get input and output image names int nnodes, nnalg; QString inputImage; QString outputImage; QString outputGraphFile; if(!getExecParameters(app.arguments(), &nnodes, &nnalg, inputImage, outputImage, outputGraphFile)) return -1; // Prepare sampler unsigned seed = RANDOM_SEED == 0 ? time(NULL) : RANDOM_SEED; qsrand(seed); FGngSampleSrc sampler; SamplerContext2D samplerContext; prepareSampler(inputImage, &sampler, &samplerContext); // Create GNG object FGngParameters par(nnodes, LAMBDA, EPSILON_B, EPSILON_N, AGEMAX, ALPHA, BETA, RTREE_MAXCHILDS, RTREE_MINCHILDS, nnalg); FGngFib gng(par, sampler, &samplerContext); // Execute the algorithm! QTime t; t.start(); gng.execute(); printf("FGNG algorithm finished in %.3fs\n", t.elapsed()/1000.0); // Write results if(!outputImage.isEmpty()) createGraphImage(outputImage, samplerContext.pixmap, gng); if(!outputGraphFile.isEmpty()) createGraphFile(outputGraphFile, gng, seed); return 0; }