PARP Research Group University of Murcia, Spain


src/qvip/qvimagefeatures/qvcomponenttree.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008. 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 <qvdefines.h>
00026 
00027 #include <QVDisjointSet>
00028 #include <QVComponentTree>
00029 
00030 QVComponentTree::QVComponentTree(const QVImage<uChar,1> &image, bool inverseTree, bool useAlternative): numNodes(0), freePoints(0), inverseTree(inverseTree)
00031         {
00032         const uInt cols = image.getCols(), rows = image.getRows();
00033 
00034         this->numNodes = 0;
00035         this->leafNodes = 0;
00036         this->freePoints = 0;
00037         this->totalPoints = 0;
00038         this->maxNodes = cols * rows;
00039         this->nodes.resize(maxNodes/10);        // 10 factor is an heuristic value, estimated from several tries.
00040 
00041         if (inverseTree)
00042                 {
00043                 QVImage<uChar> notImage(cols, rows);
00044 
00045                 QVIMAGE_INIT_READ(uChar,image);
00046                 QVIMAGE_INIT_WRITE(uChar,notImage);
00047                 for (uInt col = 0; col < cols; col++)
00048                         for (uInt row = 0; row < rows; row++)
00049                                 QVIMAGE_PIXEL(notImage, col, row,0) = 255 - QVIMAGE_PIXEL(image, col, row,0);
00050 
00051                 getComponentTree(notImage);
00052                 }
00053         else
00054                 getComponentTree(image);
00055         }
00056 
00057 void QVComponentTree::getComponentTree(const QVImage<uChar> &image)
00058         {
00059         qDebug() << "getComponentTree()";
00060         const uInt cols = image.getCols(), rows = image.getRows();
00061 
00062         QVIMAGE_INIT_READ(uChar,image);
00063 
00064         const QVector< QVector< QPoint > > points = CountingSort(image);
00065         QVDisjointSet disjointSet(cols, rows);
00066 
00067         uInt *nodeID = new uInt[maxNodes];
00068         for(uInt i=0; i<maxNodes; i++)
00069                 nodeID[i] = NULL_NODE;
00070 
00071         // This loop creates the structure of the component tree, using the disjoint set, transversiong the pixels of the image
00072         // ordered by their gray-scale value, sorted thanks to 'CountingSort' function.
00073         for (int threshold = 0; threshold < points.size(); threshold++)
00074                 {
00075                 // We join in the disjoint set pixels with gray-scale equal to threshold, with adjacent regions, or pixels, with
00076                 // a gray-scale value equal or lesser than threshold. This is done here only for the disjoint set.
00077                 //
00078                 // Also, here are also joined component tree nodes which have in common one of the processed points.
00079                 for (int n=0; n< points[threshold].size(); n++)
00080                         {
00081                         const uInt      col = points[threshold][n].x(),  
00082                                         row = points[threshold][n].y();                 // coordinates of the current pixel
00083                         const uChar     actualPixel = QVIMAGE_PIXEL(image, col, row,0); // Value of the current pixel
00084                         uInt            actualSet = disjointSet.find(col, row);         // canonical element of the subset
00085 
00086                         // Transverse neighbourhood of a pixel looking for close pixels to join with
00087                         // (those with gray-scale level lower or equal to that of the actual pixel)
00088                         for (uInt i = (uInt) MAX(0,(int)col-1); i< MIN(cols, col+2); i++)  // we look at the 8 rounding neighbours
00089                                 for (uInt j = (uInt) MAX(0,(int)row-1); j< MIN(rows, row+2); j++)
00090                                         if ((col != i) || (row != j))
00091                                         {
00092                                         const uChar vecinoPixel = QVIMAGE_PIXEL(image, i, j, 0);  // neighbour's gray-level 
00093                                         if (vecinoPixel <= actualPixel)
00094                                                 {
00095                                                 const uInt vecinoSet = disjointSet.find(i,j);  // neighbour's canonical element
00096                                                 if (vecinoSet != actualSet)
00097                                                         {
00098                                                         // We should join this pixel to the set of the neighbour
00099                                                         // Each canonical element of a subset has a nodeID element which contains information about the region.
00100                                                         const uInt actualNodeID = nodeID[actualSet], vecinoNodeID = nodeID[vecinoSet];
00101         
00102                                                         actualSet = disjointSet.unify(col, row, i, j); // both subsets are unified.
00103         
00104                                                         Q_ASSERT(disjointSet.find(disjointSet.index(col, row)) == disjointSet.find(disjointSet.index(i, j)));
00105         
00106                                                         // If actual point is not in a node already, we associate it with the point that is
00107                                                         // associated to a node.
00108                                                         if (vecinoNodeID == NULL_NODE)
00109                                                                 nodeID[actualSet] = actualNodeID;
00110                                                         else if (actualNodeID == NULL_NODE)
00111                                                                 nodeID[actualSet] = vecinoNodeID;
00112                                                         else    // Otherwise, both actual and neighbour are associated to a node already.
00113                                                                 // We create a new node, and join both nodes of actual and neighbour pixels to
00114                                                                 // that one.
00115                                                                 {
00116                                                                 // We check that no one of the nodes of actual and neighbour pixels
00117                                                                 // is new. In that case it will be parent node.
00118                                                                 if (!closedNode(actualNodeID) && closedNode(vecinoNodeID))
00119                                                                         // We just add the node...
00120                                                                         {
00121                                                                         addChild(actualNodeID, vecinoNodeID);
00122                                                                         nodeID[actualSet] = actualNodeID;
00123                                                                         }
00124                                                                 else if (closedNode(actualNodeID) && !closedNode(vecinoNodeID))
00125                                                                         // We just add the other node...
00126                                                                         {
00127                                                                         addChild(vecinoNodeID, actualNodeID);
00128                                                                         nodeID[actualSet] = vecinoNodeID;
00129                                                                         }
00130                                                                 else if (closedNode(actualNodeID) && closedNode(vecinoNodeID))
00131                                                                         // We have two old nodes, and create a parent to unify them.
00132                                                                         {
00133                                                                         const uInt newNodeID = newNode(col, row, threshold);
00134                                                                         addChild(newNodeID, actualNodeID);
00135                                                                         addChild(newNodeID, vecinoNodeID);
00136         
00137                                                                         //nodeID[actualIndex] = newNodeID;
00138                                                                         nodeID[actualSet] = newNodeID;
00139                                                                         }
00140                                                                 else // if ( !NODE(actualNodeID).closed and !NODE(vecinoNodeID).closed )
00141                                                                         // We have two parent nodes, we leave things as they are.
00142                                                                         // No, we should unify both, passing childs of one of them to the
00143                                                                         // other.
00144                                                                         {
00145                                                                         Q_ASSERT(closedNode(actualNodeID) == false);
00146                                                                         Q_ASSERT(closedNode(vecinoNodeID) == false);
00147                                                                         Q_ASSERT(numChilds(actualNodeID) > 0);
00148                                                                         Q_ASSERT(numChilds(vecinoNodeID) > 0);
00149         
00150                                                                         mergeNodes(actualNodeID, vecinoNodeID);
00151                                                                         nodeID[actualSet] = actualNodeID;
00152                                                                         }
00153                                                                 }
00154         
00155                                                         // Actualize areas for the resulting parent node.
00156                                                         if (nodeID[actualSet] != NULL_NODE)
00157                                                                 {
00159                                                                 //Q_ASSERT(area(actualNodeID)[lastThreshold(actualNodeID)]
00160                                                                 //      <= disjointSet.getSetCardinality(actualIndex));
00161                                                                 lastThreshold(nodeID[actualSet]) = threshold;
00162                                                                 area(nodeID[actualSet])[threshold] = disjointSet.getSetCardinality(actualSet);
00163                                                                 }
00164         
00165                                                         Q_ASSERT(nodeID[disjointSet.find(disjointSet.index(col, row))] ==
00166                                                                 nodeID[disjointSet.find(disjointSet.index(i, j))]);
00167                                                         }
00168                                                 }
00169                                         }
00170                         }
00171 
00172                 // In this loop we actualize areas for the gray-level of the threshold of the two old nodes, and create new nodes, 
00173                 // case we find a set of one or several pixels of gray-scale value equal to threshold value, which are not joined
00174                 // to any connected set represented already in a node of the component tree.
00175                 //
00176                 // In this point, we have processed all pixels with gray-scale value equal or lesser to threshold. All of them are
00177                 // grouped to a group of pixels with same gray-scale level, in which case we have the vertex of a node, or well
00178                 // or well joined to a previously created node.
00179                 //
00180                 // Then we creater for the former case new nodes, and in any case we actualize areas for nodes with new points
00181                 // in them.
00182                 for (int n=0; n< points[threshold].size(); n++)
00183                         {
00184                         const uInt      col = points[threshold][n].x(),
00185                                         row = points[threshold][n].y(),
00186                                         actualIndex = disjointSet.index(col, row),
00187                                         actualSet = disjointSet.find(actualIndex);
00188 
00189                         Q_ASSERT_X(threshold < 256, "getComponentTree", "out of bounds 4");
00190                         Q_ASSERT_X(actualIndex < cols * rows, "getComponentTree", "out of bounds 5");
00191 
00192                         // We have a pixel with gray-scale level equal to threshold, and disjoint set identificator equal to
00193                         // himself.
00194                         // This means either it is an isolated pixel, surrounded by pixels of gray-scale level higher than his, or
00195                         // that he is in a connected set of pixels, all of them with exactly gray-scale level value of threshold
00196                         // (and we hill be the only one of that set, with disjoint set identificator equal to himself).
00197                         // Either case we create a new node, with seed point equal to this node.
00198                         if (actualIndex == actualSet)
00199                                 {
00200                                 if (nodeID[actualIndex] == NULL_NODE)
00201                                         // We have a header point for the new component tree node.
00202                                         // We initialize the values for his node.
00203                                         {
00204                                         nodeID[actualSet] = newNode(col, row, threshold);
00205                                         area(nodeID[actualSet])[threshold] = disjointSet.getSetCardinality(actualSet);
00206 
00207                                         this->leafNodes++;
00208                                         }
00209                                 else    // Actual pixel is associated to a node, but this one was created for other pixel.
00210                                         // We count as a free pixel the one that created the node that contains actual pixel.
00211                                         this->freePoints++;
00212                                 }
00213                         else    // Actual pixel is not its group head
00214                                 this->freePoints++;
00215 
00216                         const uInt actualNodeID = nodeID[actualSet];
00217 
00218                         if (actualNodeID != NULL_NODE)
00219                                 {
00220                                 // Actualize histogram for the node of the actual pixel.
00221                                 //lastThreshold(actualNodeID) = threshold;
00222                                 //area(actualNodeID)[threshold] = disjointSet.getSetCardinality(actualIndex);
00223 
00224                                 // Close node for the actual pixel, if open.
00225                                 closedNode(actualNodeID) = true;
00226                                 }
00227 
00228                         // Actualize total number of points processed.
00229                         this->totalPoints++;
00230                         }
00231                 }
00232 
00233         rootNode() = nodeID[disjointSet.find(0)];
00234 
00235         // Component tree finished, performing some tests....
00236         #ifndef QT_NO_DEBUG
00237         //testComponentTree(image, disjointSet);
00238         #endif
00239 
00240         delete nodeID;
00241 
00242         qDebug() << "getComponentTree() <~ return";
00243         }



QVision framework. PARP research group, copyright 2007, 2008.