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 namespace qvdta
00031 {
00032 void pruneLowComponentTreeAux(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea, uInt node, uInt validThreshold)
00033         {
00034         Q_ASSERT(componentTree.area(node)[componentTree.firstThreshold(node)] != 0);
00035         Q_ASSERT(componentTree.area(node)[componentTree.lastThreshold(node)] != 0);
00036         Q_ASSERT(validThreshold >= componentTree.lastThreshold(node));
00037 
00038         bool prune = false;
00039         int lastValidThreshold = validThreshold;
00040 
00041         // Here we decide if this node should be directly pruned
00042         // or if there's any sub-node we should prune
00043         for (int threshold = componentTree.lastThreshold(node); threshold >= componentTree.firstThreshold(node) && !prune; threshold--)
00044                 if (componentTree.area(node)[threshold] > 0)
00045                         {
00046                         if (componentTree.area(node)[threshold] < minArea)
00047                                 prune = true;
00048                         else
00049                                 lastValidThreshold = threshold;
00050                         }
00051 
00052         // We prune node, or get on with it's childrens
00053         if (prune)
00054                 myFloodFill(image, componentTree.seedX(node), componentTree.seedY(node), lastValidThreshold, 0, lastValidThreshold-1);
00055         else
00056                 for (uInt child = componentTree.firstChild(node); child != NULL_NODE; child = componentTree.nextSibling(child))
00057                         pruneLowComponentTreeAux(image, componentTree, minArea, child, lastValidThreshold);
00058         }
00059 
00060 void pruneHighComponentTreeAux(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea, uInt node, uInt validThreshold)
00061         {
00062         Q_ASSERT(componentTree.area(node)[componentTree.firstThreshold(node)] != 0);
00063         Q_ASSERT(componentTree.area(node)[componentTree.lastThreshold(node)] != 0);
00064         Q_ASSERT(validThreshold >= componentTree.lastThreshold(node));
00065 
00066         bool prune = false;
00067         int lastValidThreshold = validThreshold;
00068 
00069         // Here we decide if this node should be directly pruned
00070         // or if there's any sub-node we should prune
00071         for (int threshold = componentTree.lastThreshold(node); threshold >= componentTree.firstThreshold(node) && !prune; threshold--)
00072                 if (componentTree.area(node)[threshold] > 0)
00073                         {
00074                         if (componentTree.area(node)[threshold] < minArea)
00075                                 prune = true;
00076                         else
00077                                 lastValidThreshold = threshold;
00078                         }
00079 
00080         // We prune node, or get on with it's childrens
00081         if (prune)
00082                 myFloodFill(image, componentTree.seedX(node), componentTree.seedY(node), 255-lastValidThreshold, 255-lastValidThreshold+1, 255-0);
00083         else
00084                 for (uInt child = componentTree.firstChild(node); child != NULL_NODE; child = componentTree.nextSibling(child))
00085                         pruneHighComponentTreeAux(image, componentTree, minArea, child, lastValidThreshold);
00086         }
00087 
00088 void FilterComponentTreeSmallRegions(QVImage<uChar> &image, QVComponentTree &componentTree, uInt minArea)
00089         {
00090         qDebug() << "pruneRegions()";
00091         if (componentTree.isInverseTree())
00092                 {
00093                 if(componentTree.area(componentTree.rootNode())[componentTree.lastThreshold(componentTree.rootNode())] > minArea)
00094                         pruneHighComponentTreeAux(image, componentTree, minArea, componentTree.rootNode(), componentTree.lastThreshold(componentTree.rootNode()));
00095                 }
00096         else    {
00097                 if(componentTree.area(componentTree.rootNode())[componentTree.lastThreshold(componentTree.rootNode())] > minArea)
00098                         pruneLowComponentTreeAux(image, componentTree, minArea, componentTree.rootNode(), componentTree.lastThreshold(componentTree.rootNode()));
00099                 }
00100 
00101         qDebug() << "pruneRegions() <~ return";
00102         }
00103 
00105 
00106 QVComponentTree::QVComponentTree(const QVImage<uChar,1> &image, bool inverseTree): numNodes(0), freePoints(0), inverseTree(inverseTree)
00107         {
00108         const uInt cols = image.getCols(), rows = image.getRows();
00109         nodes = new QVComponentTreeNode[maxNodes = cols*rows];
00110 
00111         if (inverseTree)
00112                 {
00113                 QVImage<uChar> notImage(cols, rows);
00114 
00115                 QVIMAGE_INIT_READ(uChar,image);
00116                 QVIMAGE_INIT_WRITE(uChar,notImage);
00117                 for (uInt col = 0; col < cols; col++)
00118                         for (uInt row = 0; row < rows; row++)
00119                                 QVIMAGE_PIXEL(notImage, col, row,0) = 255 - QVIMAGE_PIXEL(image, col, row,0);
00120 
00121                 getComponentTree(notImage);
00122                 }
00123         else
00124                 getComponentTree(image);
00125         }
00126 
00127 QVComponentTree::~QVComponentTree()
00128         {
00129         delete nodes;
00130         }
00131 
00132 void QVComponentTree::getComponentTree(const QVImage<uChar> &image)
00133         {
00134         qDebug() << "getComponentTree()";
00135         const uInt cols = image.getCols(), rows = image.getRows();
00136         const QVector< QVector< QPoint > > points = qvdta::CountingSort(image);
00137 
00138         uInt *nodeID = new uInt[maxNodes];
00139         for(uInt i=0; i<maxNodes; i++)
00140                 nodeID[i] = NULL_NODE;
00141 
00142         qvdta::QVDisjointSet disjointSet(cols, rows);
00143 
00144         this->numNodes = 0;
00145         this->leafNodes = 0;
00146         this->freePoints = 0;
00147         this->totalPoints = 0;
00148 
00149         for (uInt i=0; i< cols*rows; i++)
00150                 Q_ASSERT(nodeID[i] == NULL_NODE);
00151 
00152         // This loop creates the structure of the component tree, using the disjoint set, transversiong the pixels of the image
00153         // ordered by their gray-scale value, sorted thanks to 'CountingSort' function.
00154         QVIMAGE_INIT_READ(uChar,image);
00155 
00156         for (int threshold = 0; threshold < points.size(); threshold++)
00157                 {
00158                 // We join in the disjoint set pixels with gray-scale equal to threshold, with adjacent regions, or pixels, with
00159                 // a gray-scale value equal or lesser than threshold. This is done here only for the disjoint set.
00160                 //
00161                 // Also, here are also joined component tree nodes which have in common one of the processed points.
00162                 for (int n=0; n< points[threshold].size(); n++)
00163                         {
00164                         const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00165                         const uChar actualPixel = QVIMAGE_PIXEL(image, col, row,0);
00166 
00167                         // Transverse neighbourhood of a pixel looking for close pixels to join with
00168                         // (those with gray-scale level lower or equal to that of the actual pixel)
00169                         for (uInt i = (uInt) MAX(0,(int)col-1); i< MIN(cols, col+2); i++)
00170                                 for (uInt j = (uInt) MAX(0,(int)row-1); j< MIN(rows, row+2); j++)
00171                                         if ((col != i) || (row != j))
00172                                         {
00173                                         const uInt vecinoSet = disjointSet.find(i,j), actualSet = disjointSet.find(col, row);
00174                                         const uChar vecinoPixel = QVIMAGE_PIXEL(image, i, j,0);
00175 
00176                                         if ( (vecinoPixel <= actualPixel) && (vecinoSet != actualSet) )
00177                                                 {
00178                                                 // We should join this pixel to the set of the neighbour
00179                                                 const uInt      newPointID = disjointSet.unify(col, row, i, j),
00180                                                                 actualNodeID = nodeID[actualSet],
00181                                                                 vecinoNodeID = nodeID[vecinoSet];
00182 
00183                                                 Q_ASSERT(disjointSet.find(disjointSet.index(col, row)) == disjointSet.find(disjointSet.index(i, j)));
00184                                                 Q_ASSERT( (actualSet == newPointID) || (vecinoSet == newPointID) );
00185 
00186                                                 // If new is not in a node already, we associate it with the point that is associated to a node.
00187                                                 if (actualNodeID == NULL_NODE)
00188                                                         nodeID[newPointID] = vecinoNodeID;
00189                                                 else if (vecinoNodeID == NULL_NODE)
00190                                                         nodeID[newPointID] = actualNodeID;
00191 
00192                                                 // Otherwise, both actual and neighbour are associated to a node already.
00193                                                 // We create a new node, and join both nodes of actual and neighbour pixels to
00194                                                 // that one.
00195                                                 else    {
00196                                                         Q_ASSERT(actualNodeID != NULL_NODE);
00197                                                         Q_ASSERT(vecinoNodeID != NULL_NODE);
00198 
00199                                                         // We check that no one of the nodes of actual and neighbour pixels
00200                                                         // is new. In that case it will be parent node.
00201                                                         if (!validNode(actualNodeID) && validNode(vecinoNodeID))
00202                                                                 // We just add the node...
00203                                                                 {
00204                                                                 addChild(actualNodeID, vecinoNodeID);
00205                                                                 nodeID[newPointID] = actualNodeID;
00206                                                                 }
00207                                                         else if (validNode(actualNodeID) && !validNode(vecinoNodeID))
00208                                                                 // We just add the other node...
00209                                                                 {
00210                                                                 addChild(vecinoNodeID, actualNodeID);
00211                                                                 nodeID[newPointID] = vecinoNodeID;
00212                                                                 }
00213                                                         else if (validNode(actualNodeID) && validNode(vecinoNodeID))
00214                                                                 // We have two old nodes, and creaate a parent to unify them.
00215                                                                 {
00216                                                                 const uInt newNodeID = newNode(col, row, threshold);;
00217                                                                 nodeID[disjointSet.index(col, row)] = newNodeID;
00218 
00219                                                                 addChild(newNodeID, actualNodeID);
00220                                                                 addChild(newNodeID, vecinoNodeID);
00221                                                                 nodeID[newPointID] = newNodeID;
00222                                                                 }
00223                                                         else // if ( !NODE(actualNodeID).inited and !NODE(vecinoNodeID).inited )
00224                                                                 // We have two parent nodes, we leave things as they are.
00225                                                                 // No, we should unify both, passing childs of one of them to the
00226                                                                 // other.
00227                                                                 {
00228                                                                 Q_ASSERT(validNode(actualNodeID) == false);
00229                                                                 Q_ASSERT(validNode(vecinoNodeID) == false);
00230                                                                 Q_ASSERT(numChilds(actualNodeID) > 0);
00231                                                                 Q_ASSERT(numChilds(vecinoNodeID) > 0);
00232 
00233                                                                 uInt child;
00234                                                                 for (   child = firstChild(actualNodeID);
00235                                                                         nextSibling(child) != NULL_NODE;
00236                                                                         child = nextSibling(child));
00237 
00238                                                                 uInt lastActualChildNodeID = firstChild(actualNodeID);
00239                                                                 while (nextSibling(lastActualChildNodeID) != NULL_NODE)
00240                                                                         lastActualChildNodeID =
00241                                                                                 nextSibling(lastActualChildNodeID);
00242 
00243                                                                 numChilds(actualNodeID) += numChilds(vecinoNodeID);
00244                                                                 nextSibling(lastActualChildNodeID) = firstChild(vecinoNodeID);
00245                                                                 nodeID[newPointID] = actualNodeID;
00246                                                                 }
00247                                                         }
00248                                                 Q_ASSERT(nodeID[disjointSet.find(disjointSet.index(col, row))] == nodeID[disjointSet.find(disjointSet.index(i, j))]);
00249                                                 }
00250                                         }
00251                         }
00252 
00253                 // In this loop we actualize areas for the gray-level of the threshold of the two old nodes, and create new nodes, 
00254                 // case we find a set of one or several pixels of gray-scale value equal to threshold value, which are not joined
00255                 // to any connected set represented already in a node of the component tree.
00256                 //
00257                 // In this point, we have processed all pixels with gray-scale value equal or lesser to threshold. All of them are
00258                 // grouped to a group of pixels with same gray-scale level, in which case we have the vertex of a node, or well
00259                 // or well joined to a previously created node.
00260                 //
00261                 // Then we creater for the former case new nodes, and in any case we actualize areas for nodes with new points
00262                 // in them.
00263                 for (int n=0; n< points[threshold].size(); n++)
00264                         {
00265                         Q_ASSERT_X(threshold < 256, "getComponentTree", "out of bounds 4");
00266                         const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00267                         const uInt actualID = disjointSet.index(col, row);
00268 
00269                         Q_ASSERT_X(actualID < cols * rows, "getComponentTree", "out of bounds 5");
00270 
00271                         // We have a pixel with gray-scale level equal to threshold, and disjoint set identificator equal to himself.
00272                         // This means either it is an isolated pixel, surrounded by pixels of gray-scale level higher than his, or
00273                         // that he is in a connected set of pixels, all of them with exactly gray-scale level value of threshold
00274                         // (and we hill be the only one of that set, with disjoint set identificator equal to himself).
00275                         // Either case we create a new node, with seed point equal to this node.
00276                         if (actualID == disjointSet.find(actualID))
00277                                 {
00278                                 if (nodeID[actualID] == NULL_NODE)
00279                                         // We have a header point for the new component tree node.
00280                                         // We initialize the values for his node.
00281                                         {
00282                                         nodeID[disjointSet.index(col, row)] = newNode(col, row, threshold);
00283                                         this->leafNodes++;
00284                                         }
00285                                 else    // Actual pixel is associated to a node, but this one was created for other pixel.
00286                                         // We count as a free pixel the one that created the node that contains actual pixel.
00287                                         this->freePoints++;
00288                                 }
00289                         else    // Actual pixel is not group heading
00290                                 {
00291                                 this->freePoints++;
00292                                 // Why this isn't generally true?
00293                                 //Q_ASSERT(this->nodeID[actualID] == NULL_NODE);
00294                                 }
00295 
00296                         const uInt actualNodeID = nodeID[disjointSet.find(actualID)];
00297                         if (actualNodeID != NULL_NODE)
00298                                 {
00299                                 Q_ASSERT(area(actualNodeID)[lastThreshold(actualNodeID)] <= disjointSet.getSetCardinality(actualID));
00300                                 lastThreshold(actualNodeID) = threshold;
00301                                 area(actualNodeID)[threshold] = disjointSet.getSetCardinality(actualID);
00302                                 validNode(actualNodeID) = true;
00303                                 }
00304                         // Actualize total number of points
00305                         this->totalPoints++;
00306                         }
00307                 }
00308 
00309         rootNode() = nodeID[disjointSet.find(0)];
00310 
00311         // COMPONENT TREE FINISHED. STARTING TESTS...
00312         #ifndef QT_NO_DEBUG     // TEST 
00313         Q_ASSERT(validNode(this->rootNodeID));
00314 
00315         // Checking that actually number of nodes, and number of nodes with ID != NULL_NODE equals the
00316         // value holded in this->numNodes.
00317         uInt sumNodes = 0, sumChildNodes = 0, sumLeafNodes = 0;
00318 
00319         // Checking some basic tree structure and values
00320         for (uInt node = 0; node < this->numNodes; node++)
00321                 if (validNode(node))
00322                         {
00323                         const uInt fThreshold = firstThreshold(node), lThreshold = lastThreshold(node);
00324 
00325                         if (numChilds(node) == 0)
00326                                 sumLeafNodes++;
00327 
00328                         sumNodes++;
00329                         // Checking areas correctness
00330                         Q_ASSERT(area(node)[fThreshold] <= area(node)[lThreshold] != 0);
00331                         Q_ASSERT(area(node)[fThreshold] != 0);
00332                         Q_ASSERT(area(node)[lThreshold] != 0);
00333 
00334                         // Check that seed pixel correspond with first threshold
00335                         Q_ASSERT(image(seedX(node), seedY(node)) == fThreshold);
00336 
00337                         // Check areas go from lesser size to bigger
00338                         uInt lastArea = area(node)[fThreshold];
00339                         for (uInt threshold = fThreshold+1; threshold <= lThreshold; threshold++)
00340                                 if (area(node)[threshold] != 0)
00341                                         {
00342                                         const uInt actualArea = area(node)[threshold];
00343                                         Q_ASSERT(actualArea >= lastArea);
00344                                         lastArea = actualArea;
00345                                         }
00346 
00347                         uInt maxAreasChilds = 0;
00348                         uInt childs = 0;
00349                         for (   uint child = firstChild(node);
00350                                 child != NULL_NODE;
00351                                 child = nextSibling(child), childs++)
00352                                 {
00353                                 Q_ASSERT(childs < numChilds(node));
00354                                 Q_ASSERT(child < this->numNodes);
00355                                 Q_ASSERT(child != rootNode());
00356                                 Q_ASSERT(validNode(child));
00357                                 maxAreasChilds += area(child)[lastThreshold(child)];
00358                                 }
00359 
00360                         Q_ASSERT(area(node)[firstThreshold(node)] > maxAreasChilds);
00361                         Q_ASSERT(childs == numChilds(node));
00362 
00363                         // TODO: Check sum of childs for node equals numChilds(node)
00364                         sumChildNodes += numChilds(node);
00365                 }
00366         qDebug() << "getComponentTree(): /////// estadisticos";
00367         qDebug() << "getComponentTree(): sumNodes = " << sumNodes << sumChildNodes;
00368         Q_ASSERT( sumNodes == (sumChildNodes+1) );
00369         Q_ASSERT( this->leafNodes == sumLeafNodes );
00370 
00371         qDebug() << "getComponentTree(): this->numNodes = " << this->numNodes;
00372         qDebug() << "getComponentTree(): this->freePoints = "<< this->freePoints;
00373         qDebug() << "getComponentTree(): this->totalPoints = "<< this->totalPoints;
00374         qDebug() << "getComponentTree(): this->leafNodes + this->freePoints = "
00375                         << this->leafNodes + this->freePoints;
00376 
00377         Q_ASSERT(disjointSet.numberOfSets() == 1);
00378         Q_ASSERT(disjointSet.getSetCardinality(rootNode()) == (image.getCols() * image.getRows()));
00379         Q_ASSERT(this->totalPoints == (image.getCols() * image.getRows()));
00380         Q_ASSERT(this->totalPoints == this->leafNodes + this->freePoints);
00381 
00382         #endif // END TEST 
00383 
00384         delete nodeID;
00385 
00386         qDebug() << "getComponentTree() <~ return";
00387         }
00388 
00389 /*uInt debug_aux_print_componentTree(const QVComponentTree *componentTree, int node, int numTabs)
00390         {
00391         Q_ASSERT_X(numTabs < 8, "debug_aux_print_componentTree", "Can't show trees deeper than 8 level.");
00392         char tabs[16] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
00393         tabs[numTabs] = '\0';
00394 
00395         qDebug() << "debug_print_componentTree():" << tabs << "* Node" << node;
00396         qDebug() << "debug_print_componentTree():" << tabs << "  Seed at (" << componentTree->seedX(node) << componentTree->seedY(node) << ")";
00397         qDebug() << "debug_print_componentTree():" << tabs << "  Areas min/max ["
00398                 << componentTree->area(node)[componentTree->firstThreshold(node)] << ","
00399                 << componentTree->area(node)[componentTree->lastThreshold(node)] << "]";
00400         qDebug() << "debug_print_componentTree():" << tabs << "  Thresholds min/max ["
00401                 << componentTree->firstThreshold(node) << "," << componentTree->lastThreshold(node) << "]"; 
00402         qDebug() << "debug_print_componentTree():" << tabs << "  # childs [" << componentTree->numChilds(node) << "]"; 
00403 
00404         uInt sum = 1;
00405         for (   uInt child = componentTree->firstChild(node), numChilds = 1;
00406                 child != NULL_NODE;
00407                 child = componentTree->nextSibling(child), numChilds++ )
00408                 {
00409                 //Q_ASSERT(child < componentTree->numberOfNodes());
00410                 Q_ASSERT(numChilds <= componentTree->numChilds(node));
00411                 sum += debug_aux_print_componentTree(componentTree, child, numTabs+1);
00412                 }
00413 
00414         return sum;
00415         }
00416 
00417 uInt debug_print_componentTree(const QVComponentTree *componentTree)
00418         {
00419         qDebug() << "debug_print_componentTree()";
00420 
00421         qDebug() << "debug_print_componentTree(): number of nodes =" << componentTree->getNumNodes();
00422         qDebug() << "debug_print_componentTree(): number of leafs =" << componentTree->getLeafNodes();
00423         qDebug() << "debug_print_componentTree(): total points =" << componentTree->getTotalPoints();
00424         qDebug() << "debug_print_componentTree(): root id =" << componentTree->rootNode();
00425 
00426         uInt numPrintedNodes = debug_aux_print_componentTree(componentTree, componentTree->rootNode(), 1);
00427 
00428         qDebug() << "debug_print_componentTree(): numPrintedNodes =" << numPrintedNodes;
00429         qDebug() << "debug_print_componentTree() <~ return";
00430         return numPrintedNodes;
00431         }*/
00432 }

Generated on Thu Dec 13 13:06:25 2007 for QVision by  doxygen 1.5.3