00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
00042
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
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
00070
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
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
00153
00154 QVIMAGE_INIT_READ(uChar,image);
00155
00156 for (int threshold = 0; threshold < points.size(); threshold++)
00157 {
00158
00159
00160
00161
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
00168
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
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
00187 if (actualNodeID == NULL_NODE)
00188 nodeID[newPointID] = vecinoNodeID;
00189 else if (vecinoNodeID == NULL_NODE)
00190 nodeID[newPointID] = actualNodeID;
00191
00192
00193
00194
00195 else {
00196 Q_ASSERT(actualNodeID != NULL_NODE);
00197 Q_ASSERT(vecinoNodeID != NULL_NODE);
00198
00199
00200
00201 if (!validNode(actualNodeID) && validNode(vecinoNodeID))
00202
00203 {
00204 addChild(actualNodeID, vecinoNodeID);
00205 nodeID[newPointID] = actualNodeID;
00206 }
00207 else if (validNode(actualNodeID) && !validNode(vecinoNodeID))
00208
00209 {
00210 addChild(vecinoNodeID, actualNodeID);
00211 nodeID[newPointID] = vecinoNodeID;
00212 }
00213 else if (validNode(actualNodeID) && validNode(vecinoNodeID))
00214
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
00224
00225
00226
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
00254
00255
00256
00257
00258
00259
00260
00261
00262
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
00272
00273
00274
00275
00276 if (actualID == disjointSet.find(actualID))
00277 {
00278 if (nodeID[actualID] == NULL_NODE)
00279
00280
00281 {
00282 nodeID[disjointSet.index(col, row)] = newNode(col, row, threshold);
00283 this->leafNodes++;
00284 }
00285 else
00286
00287 this->freePoints++;
00288 }
00289 else
00290 {
00291 this->freePoints++;
00292
00293
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
00305 this->totalPoints++;
00306 }
00307 }
00308
00309 rootNode() = nodeID[disjointSet.find(0)];
00310
00311
00312 #ifndef QT_NO_DEBUG // TEST
00313 Q_ASSERT(validNode(this->rootNodeID));
00314
00315
00316
00317 uInt sumNodes = 0, sumChildNodes = 0, sumLeafNodes = 0;
00318
00319
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
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
00335 Q_ASSERT(image(seedX(node), seedY(node)) == fThreshold);
00336
00337
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
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
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432 }