src/qvdta/qvcomponenttree.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00024 
00025 #include <qvdta/qvcomponenttree.h>
00026 
00027 #define MIN(X,Y)        (((X)>(Y))?(Y):(X))
00028 #define MAX(X,Y)        (((X)>(Y))?(X):(Y))
00029 
00030 void pruneLowComponentTreeAux(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea, uInt node, uInt validThreshold)
00031         {
00032         Q_ASSERT(componentTree.area(node)[componentTree.firstThreshold(node)] != 0);
00033         Q_ASSERT(componentTree.area(node)[componentTree.lastThreshold(node)] != 0);
00034         Q_ASSERT(validThreshold >= componentTree.lastThreshold(node));
00035 
00036         bool prune = false;
00037         int lastValidThreshold = validThreshold;
00038 
00039         // Here we decide if this node should be directly pruned
00040         // or if there's any sub-node we should prune
00041         for (int threshold = componentTree.lastThreshold(node); threshold >= componentTree.firstThreshold(node) && !prune; threshold--)
00042                 if (componentTree.area(node)[threshold] > 0)
00043                         {
00044                         if (componentTree.area(node)[threshold] < minArea)
00045                                 prune = true;
00046                         else
00047                                 lastValidThreshold = threshold;
00048                         }
00049 
00050         // We prune node, or get on with it's childrens
00051         if (prune)
00052                 myFloodFill(image, componentTree.seedX(node), componentTree.seedY(node), lastValidThreshold, 0, lastValidThreshold-1);
00053         else
00054                 for (uInt child = componentTree.firstChild(node); child != NULL_NODE; child = componentTree.nextSibling(child))
00055                         pruneLowComponentTreeAux(image, componentTree, minArea, child, lastValidThreshold);
00056         }
00057 
00058 void pruneHighComponentTreeAux(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea, uInt node, uInt validThreshold)
00059         {
00060         Q_ASSERT(componentTree.area(node)[componentTree.firstThreshold(node)] != 0);
00061         Q_ASSERT(componentTree.area(node)[componentTree.lastThreshold(node)] != 0);
00062         Q_ASSERT(validThreshold >= componentTree.lastThreshold(node));
00063 
00064         bool prune = false;
00065         int lastValidThreshold = validThreshold;
00066 
00067         // Here we decide if this node should be directly pruned
00068         // or if there's any sub-node we should prune
00069         for (int threshold = componentTree.lastThreshold(node); threshold >= componentTree.firstThreshold(node) && !prune; threshold--)
00070                 if (componentTree.area(node)[threshold] > 0)
00071                         {
00072                         if (componentTree.area(node)[threshold] < minArea)
00073                                 prune = true;
00074                         else
00075                                 lastValidThreshold = threshold;
00076                         }
00077 
00078         // We prune node, or get on with it's childrens
00079         if (prune)
00080                 myFloodFill(image, componentTree.seedX(node), componentTree.seedY(node), 255-lastValidThreshold, 255-lastValidThreshold+1, 255-0);
00081         else
00082                 for (uInt child = componentTree.firstChild(node); child != NULL_NODE; child = componentTree.nextSibling(child))
00083                         pruneHighComponentTreeAux(image, componentTree, minArea, child, lastValidThreshold);
00084         }
00085 
00086 void FilterComponentTreeSmallRegions(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea)
00087         {
00088         qDebug() << "pruneRegions()";
00089         if (componentTree.isInverseTree())
00090                 {
00091                 if(componentTree.area(componentTree.rootNode())[componentTree.lastThreshold(componentTree.rootNode())] > minArea)
00092                         pruneHighComponentTreeAux(image, componentTree, minArea, componentTree.rootNode(), componentTree.lastThreshold(componentTree.rootNode()));
00093                 }
00094         else    {
00095                 if(componentTree.area(componentTree.rootNode())[componentTree.lastThreshold(componentTree.rootNode())] > minArea)
00096                         pruneLowComponentTreeAux(image, componentTree, minArea, componentTree.rootNode(), componentTree.lastThreshold(componentTree.rootNode()));
00097                 }
00098 
00099         qDebug() << "pruneRegions() <~ return";
00100         }
00101 
00103 
00104 QVComponentTree::QVComponentTree(const QVImage<uChar,1> &image, bool inverseTree, bool useAlternative): numNodes(0), freePoints(0), inverseTree(inverseTree)
00105         {
00106         const uInt cols = image.getCols(), rows = image.getRows();
00107 
00108         this->numNodes = 0;
00109         this->leafNodes = 0;
00110         this->freePoints = 0;
00111         this->totalPoints = 0;
00112         this->maxNodes = cols * rows;
00113         this->nodes.resize(maxNodes/10);        // 10 factor is an heuristic value, estimated from several tries.
00114 
00115         if (inverseTree)
00116                 {
00117                 QVImage<uChar> notImage(cols, rows);
00118 
00119                 QVIMAGE_INIT_READ(uChar,image);
00120                 QVIMAGE_INIT_WRITE(uChar,notImage);
00121                 for (uInt col = 0; col < cols; col++)
00122                         for (uInt row = 0; row < rows; row++)
00123                                 QVIMAGE_PIXEL(notImage, col, row,0) = 255 - QVIMAGE_PIXEL(image, col, row,0);
00124 
00125                 if (useAlternative)
00126                         getComponentTree2(notImage);
00127                 else
00128                         getComponentTree(notImage);
00129                 }
00130         else    {
00131                 if (useAlternative)
00132                         getComponentTree2(image);
00133                 else
00134                         getComponentTree(image);
00135                 }
00136         }
00137 
00138 void QVComponentTree::testComponentTree(const QVImage<uChar,1> &image, QVDisjointSet &disjointSet)
00139         {
00140         Q_ASSERT(closedNode(this->rootNodeID));
00141 
00142         // Checking that actually number of nodes, and number of nodes with ID != NULL_NODE equals the
00143         // value holded in this->numNodes.
00144         uInt sumNodes = 0, sumChildNodes = 0, sumLeafNodes = 0;
00145 
00146         // Checking some basic tree structure and values
00147         for (uInt node = 0; node < this->numNodes; node++)
00148                 if (closedNode(node))
00149                         {
00150                         const uInt fThreshold = firstThreshold(node), lThreshold = lastThreshold(node);
00151 
00152                         if (numChilds(node) == 0)
00153                                 sumLeafNodes++;
00154 
00155                         sumNodes++;
00156                         // Checking areas correctness
00157                         Q_ASSERT(area(node)[fThreshold] <= area(node)[lThreshold] != 0);
00158                         Q_ASSERT(area(node)[fThreshold] != 0);
00159                         Q_ASSERT(area(node)[lThreshold] != 0);
00160 
00161                         // Check that seed pixel correspond with first threshold
00162                         Q_ASSERT(image(seedX(node), seedY(node)) == fThreshold);
00163 
00164                         // Check areas go from lesser size to bigger
00165                         uInt lastArea = area(node)[fThreshold];
00166                         for (uInt threshold = fThreshold+1; threshold <= lThreshold; threshold++)
00167                                 if (area(node)[threshold] != 0)
00168                                         {
00169                                         const uInt actualArea = area(node)[threshold];
00170                                         Q_ASSERT(actualArea >= lastArea);
00171                                         lastArea = actualArea;
00172                                         }
00173 
00174                         uInt maxAreasChilds = 0;
00175                         uInt childs = 0;
00176                         for (   uint child = firstChild(node);
00177                                 child != NULL_NODE;
00178                                 child = nextSibling(child), childs++)
00179                                 {
00180                                 Q_ASSERT(childs < numChilds(node));
00181                                 Q_ASSERT(child < this->numNodes);
00182                                 Q_ASSERT(child != rootNode());
00183                                 Q_ASSERT(closedNode(child));
00184                                 maxAreasChilds += area(child)[lastThreshold(child)];
00185                                 }
00186 
00187                         Q_ASSERT(area(node)[firstThreshold(node)] > maxAreasChilds);
00188                         Q_ASSERT(childs == numChilds(node));
00189 
00190                         sumChildNodes += numChilds(node);
00191                 }
00192         qDebug() << "getComponentTree(): /////// estadisticos";
00193         qDebug() << "getComponentTree(): sumNodes = " << sumNodes << sumChildNodes;
00194         Q_ASSERT( sumNodes == (sumChildNodes+1) );
00195         Q_ASSERT( this->leafNodes == sumLeafNodes );
00196 
00197         qDebug() << "getComponentTree(): this->numNodes = " << this->numNodes;
00198         qDebug() << "getComponentTree(): this->freePoints = "<< this->freePoints;
00199         qDebug() << "getComponentTree(): this->totalPoints = "<< this->totalPoints;
00200         qDebug() << "getComponentTree(): this->leafNodes + this->freePoints = "
00201                         << this->leafNodes + this->freePoints;
00202 
00203         Q_ASSERT(disjointSet.numberOfSets() == 1);
00204         Q_ASSERT(disjointSet.getSetCardinality(rootNode()) == (image.getCols() * image.getRows()));
00205         Q_ASSERT(this->totalPoints == (image.getCols() * image.getRows()));
00206         Q_ASSERT(this->totalPoints == this->leafNodes + this->freePoints);
00207         }
00208 
00211 
00212 void QVComponentTree::getComponentTree(const QVImage<uChar> &image)
00213         {
00214         qDebug() << "getComponentTree()";
00215         const uInt cols = image.getCols(), rows = image.getRows();
00216 
00217         QVIMAGE_INIT_READ(uChar,image);
00218 
00219         const QVector< QVector< QPoint > > points = CountingSort(image);
00220         QVDisjointSet disjointSet(cols, rows);
00221 
00222         uInt *nodeID = new uInt[maxNodes];
00223         for(uInt i=0; i<maxNodes; i++)
00224                 nodeID[i] = NULL_NODE;
00225 
00226         // This loop creates the structure of the component tree, using the disjoint set, transversiong the pixels of the image
00227         // ordered by their gray-scale value, sorted thanks to 'CountingSort' function.
00228         for (int threshold = 0; threshold < points.size(); threshold++)
00229                 {
00230                 // We join in the disjoint set pixels with gray-scale equal to threshold, with adjacent regions, or pixels, with
00231                 // a gray-scale value equal or lesser than threshold. This is done here only for the disjoint set.
00232                 //
00233                 // Also, here are also joined component tree nodes which have in common one of the processed points.
00234                 for (int n=0; n< points[threshold].size(); n++)
00235                         {
00236                         const uInt      col = points[threshold][n].x(),
00237                                         row = points[threshold][n].y(),
00238                                         actualIndex = disjointSet.index(col, row);
00239                         const uChar     actualPixel = QVIMAGE_PIXEL(image, col, row,0);
00240                         uInt            actualSet = disjointSet.find(col, row);
00241 
00242                         // Transverse neighbourhood of a pixel looking for close pixels to join with
00243                         // (those with gray-scale level lower or equal to that of the actual pixel)
00244                         for (uInt i = (uInt) MAX(0,(int)col-1); i< MIN(cols, col+2); i++)
00245                                 for (uInt j = (uInt) MAX(0,(int)row-1); j< MIN(rows, row+2); j++)
00246                                         if ((col != i) || (row != j))
00247                                         {
00248                                         const uInt vecinoSet = disjointSet.find(i,j);
00249                                         const uChar vecinoPixel = QVIMAGE_PIXEL(image, i, j, 0);
00250 
00251                                         if ( (vecinoPixel <= actualPixel) && (vecinoSet != actualSet) )
00252                                                 {
00253                                                 // We should join this pixel to the set of the neighbour
00254                                                 const uInt actualNodeID = nodeID[actualSet], vecinoNodeID = nodeID[vecinoSet];
00255 
00256                                                 actualSet = disjointSet.unify(col, row, i, j);
00257 
00258                                                 Q_ASSERT(disjointSet.find(disjointSet.index(col, row)) == disjointSet.find(disjointSet.index(i, j)));
00259 
00260                                                 // If actual point is not in a node already, we associate it with the point that is
00261                                                 // associated to a node.
00262                                                 if (vecinoNodeID == NULL_NODE)
00263                                                         nodeID[actualSet] = actualNodeID;
00264                                                 else if (actualNodeID == NULL_NODE)
00265                                                         nodeID[actualSet] = vecinoNodeID;
00266                                                 else    // Otherwise, both actual and neighbour are associated to a node already.
00267                                                         // We create a new node, and join both nodes of actual and neighbour pixels to
00268                                                         // that one.
00269                                                         {
00270                                                         // We check that no one of the nodes of actual and neighbour pixels
00271                                                         // is new. In that case it will be parent node.
00272                                                         if (!closedNode(actualNodeID) && closedNode(vecinoNodeID))
00273                                                                 // We just add the node...
00274                                                                 {
00275                                                                 addChild(actualNodeID, vecinoNodeID);
00276                                                                 nodeID[actualSet] = actualNodeID;
00277                                                                 }
00278                                                         else if (closedNode(actualNodeID) && !closedNode(vecinoNodeID))
00279                                                                 // We just add the other node...
00280                                                                 {
00281                                                                 addChild(vecinoNodeID, actualNodeID);
00282                                                                 nodeID[actualSet] = vecinoNodeID;
00283                                                                 }
00284                                                         else if (closedNode(actualNodeID) && closedNode(vecinoNodeID))
00285                                                                 // We have two old nodes, and create a parent to unify them.
00286                                                                 {
00287                                                                 const uInt newNodeID = newNode(col, row, threshold);
00288                                                                 addChild(newNodeID, actualNodeID);
00289                                                                 addChild(newNodeID, vecinoNodeID);
00290 
00291                                                                 //nodeID[actualIndex] = newNodeID;
00292                                                                 nodeID[actualSet] = newNodeID;
00293                                                                 }
00294                                                         else // if ( !NODE(actualNodeID).closed and !NODE(vecinoNodeID).closed )
00295                                                                 // We have two parent nodes, we leave things as they are.
00296                                                                 // No, we should unify both, passing childs of one of them to the
00297                                                                 // other.
00298                                                                 {
00299                                                                 Q_ASSERT(closedNode(actualNodeID) == false);
00300                                                                 Q_ASSERT(closedNode(vecinoNodeID) == false);
00301                                                                 Q_ASSERT(numChilds(actualNodeID) > 0);
00302                                                                 Q_ASSERT(numChilds(vecinoNodeID) > 0);
00303 
00304                                                                 mergeNodes(actualNodeID, vecinoNodeID);
00305                                                                 nodeID[actualSet] = actualNodeID;
00306                                                                 }
00307                                                         }
00308 
00309                                                 // Actualize areas for the resulting parent node.
00310                                                 if (nodeID[actualSet] != NULL_NODE)
00311                                                         {
00312                                                         Q_ASSERT(area(actualNodeID)[lastThreshold(actualNodeID)] <= disjointSet.getSetCardinality(actualIndex));
00313                                                         lastThreshold(nodeID[actualSet]) = threshold;
00314                                                         area(nodeID[actualSet])[threshold] = disjointSet.getSetCardinality(actualSet);
00315                                                         }
00316 
00317                                                 Q_ASSERT(nodeID[disjointSet.find(disjointSet.index(col, row))] == nodeID[disjointSet.find(disjointSet.index(i, j))]);
00318                                                 }
00319                                         }
00320                         }
00321 
00322                 // In this loop we actualize areas for the gray-level of the threshold of the two old nodes, and create new nodes, 
00323                 // case we find a set of one or several pixels of gray-scale value equal to threshold value, which are not joined
00324                 // to any connected set represented already in a node of the component tree.
00325                 //
00326                 // In this point, we have processed all pixels with gray-scale value equal or lesser to threshold. All of them are
00327                 // grouped to a group of pixels with same gray-scale level, in which case we have the vertex of a node, or well
00328                 // or well joined to a previously created node.
00329                 //
00330                 // Then we creater for the former case new nodes, and in any case we actualize areas for nodes with new points
00331                 // in them.
00332                 for (int n=0; n< points[threshold].size(); n++)
00333                         {
00334                         const uInt      col = points[threshold][n].x(),
00335                                         row = points[threshold][n].y(),
00336                                         actualIndex = disjointSet.index(col, row),
00337                                         actualSet = disjointSet.find(actualIndex);
00338 
00339                         Q_ASSERT_X(threshold < 256, "getComponentTree", "out of bounds 4");
00340                         Q_ASSERT_X(actualIndex < cols * rows, "getComponentTree", "out of bounds 5");
00341 
00342                         // We have a pixel with gray-scale level equal to threshold, and disjoint set identificator equal to
00343                         // himself.
00344                         // This means either it is an isolated pixel, surrounded by pixels of gray-scale level higher than his, or
00345                         // that he is in a connected set of pixels, all of them with exactly gray-scale level value of threshold
00346                         // (and we hill be the only one of that set, with disjoint set identificator equal to himself).
00347                         // Either case we create a new node, with seed point equal to this node.
00348                         if (actualIndex == actualSet)
00349                                 {
00350                                 if (nodeID[actualIndex] == NULL_NODE)
00351                                         // We have a header point for the new component tree node.
00352                                         // We initialize the values for his node.
00353                                         {
00354                                         nodeID[actualSet] = newNode(col, row, threshold);
00355                                         area(nodeID[actualSet])[threshold] = disjointSet.getSetCardinality(actualSet);
00356 
00357                                         this->leafNodes++;
00358                                         }
00359                                 else    // Actual pixel is associated to a node, but this one was created for other pixel.
00360                                         // We count as a free pixel the one that created the node that contains actual pixel.
00361                                         this->freePoints++;
00362                                 }
00363                         else    // Actual pixel is not its group head
00364                                 this->freePoints++;
00365 
00366                         const uInt actualNodeID = nodeID[actualSet];
00367 
00368                         if (actualNodeID != NULL_NODE)
00369                                 {
00370                                 // Actualize histogram for the node of the actual pixel.
00371                                 //lastThreshold(actualNodeID) = threshold;
00372                                 //area(actualNodeID)[threshold] = disjointSet.getSetCardinality(actualIndex);
00373 
00374                                 // Close node for the actual pixel, if open.
00375                                 closedNode(actualNodeID) = true;
00376                                 }
00377 
00378                         // Actualize total number of points processed.
00379                         this->totalPoints++;
00380                         }
00381                 }
00382 
00383         rootNode() = nodeID[disjointSet.find(0)];
00384 
00385         // Component tree finished, performing some tests....
00386         #ifndef QT_NO_DEBUG
00387         //testComponentTree(image, disjointSet);
00388         #endif
00389 
00390         delete nodeID;
00391 
00392         qDebug() << "getComponentTree() <~ return";
00393         }
00394 
00400 
00401 // This macro gets two points by their coordinates (col1, row1) and (col2,row2), and
00402 // tests wether they should be joined, or their sets marked as non-leaf node.
00403 #define TEST_JOIN_PIXELS(col1, row1, col2,row2)                                                         \
00404         {                                                                                               \
00405         const uChar actualPixel = QVIMAGE_PIXEL(image, col1, row1,0);                                   \
00406         const uChar neigbourPixel = QVIMAGE_PIXEL(image, col2, row2,0);                                 \
00407         uInt    actualSet = disjointSet.find(col1,row1), neighbourSet = disjointSet.find(col2,row2);    \
00408                                                                                                         \
00409         if (neigbourPixel == actualPixel)                                                               \
00410                 /* Both pixels have the same gray-scale value: join their sets. */                      \
00411                 {                                                                                       \
00412                 uInt destinationSet = disjointSet.unify(actualSet, neighbourSet);                       \
00413                 isLeafNode[destinationSet] = isLeafNode[actualSet] && isLeafNode[neighbourSet];         \
00414                 }                                                                                       \
00415         else if (neigbourPixel < actualPixel)                                                           \
00416                 /* Mark as non-leaf node the set for the actual pixel. */                               \
00417                 {                                                                                       \
00418                 borderPixel[disjointSet.index(col1,row1)] = true;                                       \
00419                 isLeafNode[actualSet] = false;                                                          \
00420                 }                                                                                       \
00421         else /* if (neigbourPixel > actualPixel) */                                                     \
00422                 /* Mark as non-leaf node the set for the neighbour pixel. */                            \
00423                 {                                                                                       \
00424                 borderPixel[disjointSet.index(col2,row2)] = true;                                       \
00425                 isLeafNode[neighbourSet] = false;                                                       \
00426                 }                                                                                       \
00427         }
00428 
00429 void QVComponentTree::getComponentTree2(const QVImage<uChar> &image)
00430         {
00431         qDebug() << "getComponentTree()";
00432         const uInt cols = image.getCols(), rows = image.getRows();
00433 
00434         this->maxNodes = cols * rows;
00435         //this->nodes.resize(maxNodes/10);      // 10 factor is an heuristic value, estimated from several tries.
00436         this->numNodes = 0;
00437         this->leafNodes = 0;
00438         this->freePoints = 0;
00439         this->totalPoints = 0;
00440 
00441         QVIMAGE_INIT_READ(uChar,image);
00442 
00443         QVDisjointSet disjointSet(cols, rows);
00444 
00446         // Initial join of sets with pixels of same grayscale value
00447         //
00448         bool    *isLeafNode = new bool[maxNodes],
00449                 *borderPixel = new bool[maxNodes];      // IMPROVEMENT: do not compute border pixels.
00450         uInt    *nodeID = new uInt[maxNodes],
00451                 *setID = new uInt[maxNodes];
00452 
00453         for(uInt i=0; i<maxNodes; i++)
00454                 {
00455                 isLeafNode[i] = true;
00456                 borderPixel[i] = false;
00457                 nodeID[i] = NULL_NODE;
00458                 setID[i] = 0;
00459                 }
00460 
00461         // Then we join sets of neighbour pixels with the same gray-scale value
00462         for (int col =0; col < cols-1; col++)
00463                 for (int row =0; row < rows-1; row++)
00464                         {
00465                         const uChar actualPixel = QVIMAGE_PIXEL(image, col, row,0);
00466                         for (int i = col; i< col+2; i++)
00467                                 for (int j = row; j < row + 2; j++)
00468                                         if ((i != col) || (j != row))
00469                                                 TEST_JOIN_PIXELS(col, row, i,j);
00470 
00471                         // This diagonal join can't be done in the previous loop, for top row pixels
00472                         if (row != 0)
00473                                 TEST_JOIN_PIXELS(col, row, col+1, row-1);
00474                         }
00475 
00476         /*std::cout << "Sets:" << std::endl;
00477         for (uInt col =0 ; col < cols; col++)
00478                 for (uInt row =0 ; row < rows; row++)
00479                         {
00480                         uInt actualIndex = disjointSet.index(col, row), actualSet = disjointSet.find(col, row);
00481                         //if ((actualSet == actualIndex))
00482                         //      std::cout << "\tat pixel " << col << ", " << row << ". " << isLeafNode[actualIndex] << std::endl;
00483                         }*/
00484 
00486         // We create the tree structure, and add the leaf nodes
00487         //
00488         // We count the number of leaf nodes, and create the initial data structure.
00489         for (uInt col =0 ; col < cols; col++)
00490                 for (uInt row =0 ; row < rows; row++)
00491                         {
00492                         uInt actualIndex = disjointSet.index(col, row), actualSet = disjointSet.find(col, row);
00493 
00494                         setID[actualIndex] = actualSet;
00495 
00496                         if ((actualSet == actualIndex) && isLeafNode[actualIndex])
00497                                         this->leafNodes++;
00498 
00499                         //if (borderPixel[actualIndex])
00500                         //      std::cout << "(" << col << ", " << row << ")\t";
00501                         }
00502 
00503         // No component tree can have more than 2*leafNodes.This should be because every parent node has always at least
00504         // two siblings, or more.
00505         this->nodes.resize(2*this->leafNodes);
00506         std::cout << "Sets: " << this->leafNodes << std::endl;
00507 
00508         // IMPROVEMENT: move the code of the following loop to the upper loop, and stablish 'nodes' size to a fixed value.
00509         // ... and create the nodes for the leaf nodes
00510         for (uInt col =0 ; col < cols; col++)
00511                 for (uInt row =0 ; row < rows; row++)
00512                         {
00513                         uInt actualIndex = disjointSet.index(col, row);
00514                         if ((setID[actualIndex] == actualIndex) && isLeafNode[actualIndex])
00515                                         nodeID[actualIndex] = newNode(col, row, QVIMAGE_PIXEL(image, col, row,0));
00516                         }
00517 
00518         // This structure will go joining the sets formed in the variable 'disjointSet'.
00519         // It is better for that reason, because is smaller.
00520         QVDisjointSet superSet(this->leafNodes);
00521 
00522         // This loop creates the structure of the component tree, using the disjoint set, transversiong the pixels of the image
00523         // ordered by their gray-scale value, sorted thanks to 'CountingSort' function.
00524         // IMPROVEMENT: construct 'points' set only with border pixels
00525         const QVector< QVector< QPoint > > points = CountingSort(image);
00526         for (int threshold = 0; threshold < points.size(); threshold++)
00527                 {
00528                 // We join in the disjoint set pixels with gray-scale equal to threshold, with adjacent regions, or pixels, with
00529                 // a gray-scale value equal or lesser than threshold. This is done here only for the disjoint set.
00530                 //
00531                 // Also, here are also joined component tree nodes which have in common one of the processed points.
00532                 for (int n=0; n< points[threshold].size(); n++)
00533                         {
00534                         const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00535 
00536                         // This will eliminate 25% of the pixels in the image.
00537                         if (!borderPixel[disjointSet.index(col, row)])
00538                                 continue;
00539 
00540                         const uInt actualIndex = disjointSet.index(col,row);
00541                         const uChar actualPixel = QVIMAGE_PIXEL(image, col, row,0);
00542                         // Transverse neighbourhood of a pixel looking for close pixels to join with
00543                         // (those with gray-scale level lower or equal to that of the actual pixel)
00544                         for (uInt i = (uInt) MAX(0,(int)col-1); i< MIN(cols, col+2); i++)
00545                                 for (uInt j = (uInt) MAX(0,(int)row-1); j< MIN(rows, row+2); j++)
00546                                         if ((col != i) || (row != j))
00547                                         {
00548                                         const uInt vecinoIndex = disjointSet.index(i,j);
00549                                         const uChar vecinoPixel = QVIMAGE_PIXEL(image, i, j,0);
00550 
00551                                         if (actualPixel > vecinoPixel)
00552                                                 {
00553                                                 const uInt vecinoSet = setID[vecinoIndex], actualSet = setID[actualIndex];
00554 
00555                                                 // If we got here, is because pixels are different, so their main set.
00556                                                 Q_ASSERT(vecinoSet != actualSet);
00557 
00558                                                 const uInt vecinoNode = nodeID[vecinoSet], actualNode = nodeID[actualSet];
00559 
00560                                                 // Any pixel with a greater value should be joined to a set                                             
00561                                                 Q_ASSERT(vecinoNode != NULL_NODE);
00562 
00563                                                 //const uInt vecinoSuperSet = superSet.find(nodeID[vecinoSet]);
00564                                                 //const uInt actualSuperSet = superSet.find(nodeID[actualSet]);
00565 
00566                                                 //std::cout << "***" << vecinoNode << " " << NULL_NODE << std::endl;
00567                                                 if (actualNode == NULL_NODE)
00568                                                         nodeID[actualSet] = vecinoNode;
00569                                                 /*else if (!closedNode(actualNode) && !closedNode(vecinoNode))
00570                                                         {
00571                                                         const uInt newNodeID = newNode(col, row, threshold);
00572 
00573                                                         nodeID[disjointSet.index(col, row)] = newNodeID;
00574                                                         addChild(newNodeID, actualNode);
00575                                                         addChild(newNodeID, vecinoNode);
00576                                                         nodeID[actualSet] = newNodeID;
00577                                                         }       */
00578                                                 }
00579                                         }
00580                         }
00581 
00582                 // We close nodes, so in a future new branches will not be added to them.
00583                 for (int n=0; n< points[threshold].size(); n++)
00584                         {
00585                         const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00586 
00587                         if (!borderPixel[disjointSet.index(col, row)])
00588                                 continue;
00589 
00590                         const uInt actualIndex = disjointSet.index(col,row);
00591                         const uInt actualSet = setID[actualIndex];
00592                         const uInt actualNode = nodeID[actualSet];
00593 
00594                         closedNode(actualNode) = true;
00595                         }
00596                 }
00597 
00598         rootNode() = nodeID[disjointSet.find(0)];
00599 
00600         // COMPONENT TREE FINISHED. STARTING TESTS...
00601         #ifndef QT_NO_DEBUG
00602         //testComponentTree();
00603         #endif
00604 
00605         delete borderPixel;
00606         delete isLeafNode;
00607         delete nodeID;
00608         delete setID;
00609 
00610         qDebug() << "getComponentTree() <~ return";
00611         }
00612 
00613 

Generated on Fri Feb 22 18:26:55 2008 for QVision by  doxygen 1.5.3