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, bool useAlternative): numNodes(0), freePoints(0), inverseTree(inverseTree)
00107 {
00108 const uInt cols = image.getCols(), rows = image.getRows();
00109 maxNodes = cols * rows;
00110
00111
00112 nodes.resize(maxNodes/10);
00113
00114 if (inverseTree)
00115 {
00116 QVImage<uChar> notImage(cols, rows);
00117
00118 QVIMAGE_INIT_READ(uChar,image);
00119 QVIMAGE_INIT_WRITE(uChar,notImage);
00120 for (uInt col = 0; col < cols; col++)
00121 for (uInt row = 0; row < rows; row++)
00122 QVIMAGE_PIXEL(notImage, col, row,0) = 255 - QVIMAGE_PIXEL(image, col, row,0);
00123
00124 if (useAlternative)
00125 getComponentTree2(notImage);
00126 else
00127 getComponentTree(notImage);
00128 }
00129 else {
00130 if (useAlternative)
00131 getComponentTree2(image);
00132 else
00133 getComponentTree(image);
00134 }
00135 }
00136
00137 QVComponentTree::~QVComponentTree()
00138 {
00139
00140 }
00141
00142 void QVComponentTree::getComponentTree(const QVImage<uChar> &image)
00143 {
00144 qDebug() << "getComponentTree()";
00145 const uInt cols = image.getCols(), rows = image.getRows();
00146 const QVector< QVector< QPoint > > points = qvdta::CountingSort(image);
00147
00148 uInt *nodeID = new uInt[maxNodes];
00149 for(uInt i=0; i<maxNodes; i++)
00150 nodeID[i] = NULL_NODE;
00151
00152 qvdta::QVDisjointSet disjointSet(cols, rows);
00153
00154 this->numNodes = 0;
00155 this->leafNodes = 0;
00156 this->freePoints = 0;
00157 this->totalPoints = 0;
00158
00159 for (uInt i=0; i< cols*rows; i++)
00160 Q_ASSERT(nodeID[i] == NULL_NODE);
00161
00162
00163
00164 QVIMAGE_INIT_READ(uChar,image);
00165
00166 for (int threshold = 0; threshold < points.size(); threshold++)
00167 {
00168
00169
00170
00171
00172 for (int n=0; n< points[threshold].size(); n++)
00173 {
00174 const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00175 const uChar actualPixel = QVIMAGE_PIXEL(image, col, row,0);
00176
00177
00178
00179 for (uInt i = (uInt) MAX(0,(int)col-1); i< MIN(cols, col+2); i++)
00180 for (uInt j = (uInt) MAX(0,(int)row-1); j< MIN(rows, row+2); j++)
00181 if ((col != i) || (row != j))
00182 {
00183 const uInt vecinoSet = disjointSet.find(i,j), actualSet = disjointSet.find(col, row);
00184 const uChar vecinoPixel = QVIMAGE_PIXEL(image, i, j,0);
00185
00186 if ( (vecinoPixel <= actualPixel) && (vecinoSet != actualSet) )
00187 {
00188
00189 const uInt newPointID = disjointSet.unify(col, row, i, j),
00190 actualNodeID = nodeID[actualSet],
00191 vecinoNodeID = nodeID[vecinoSet];
00192
00193 Q_ASSERT(disjointSet.find(disjointSet.index(col, row)) == disjointSet.find(disjointSet.index(i, j)));
00194 Q_ASSERT( (actualSet == newPointID) || (vecinoSet == newPointID) );
00195
00196
00197 if (actualNodeID == NULL_NODE)
00198 nodeID[newPointID] = vecinoNodeID;
00199 else if (vecinoNodeID == NULL_NODE)
00200 nodeID[newPointID] = actualNodeID;
00201
00202
00203
00204
00205 else {
00206 Q_ASSERT(actualNodeID != NULL_NODE);
00207 Q_ASSERT(vecinoNodeID != NULL_NODE);
00208
00209
00210
00211 if (!validNode(actualNodeID) && validNode(vecinoNodeID))
00212
00213 {
00214 addChild(actualNodeID, vecinoNodeID);
00215 nodeID[newPointID] = actualNodeID;
00216 }
00217 else if (validNode(actualNodeID) && !validNode(vecinoNodeID))
00218
00219 {
00220 addChild(vecinoNodeID, actualNodeID);
00221 nodeID[newPointID] = vecinoNodeID;
00222 }
00223 else if (validNode(actualNodeID) && validNode(vecinoNodeID))
00224
00225 {
00226 const uInt newNodeID = newNode(col, row, threshold);;
00227 nodeID[disjointSet.index(col, row)] = newNodeID;
00228
00229 addChild(newNodeID, actualNodeID);
00230 addChild(newNodeID, vecinoNodeID);
00231 nodeID[newPointID] = newNodeID;
00232 }
00233 else
00234
00235
00236
00237 {
00238 Q_ASSERT(validNode(actualNodeID) == false);
00239 Q_ASSERT(validNode(vecinoNodeID) == false);
00240 Q_ASSERT(numChilds(actualNodeID) > 0);
00241 Q_ASSERT(numChilds(vecinoNodeID) > 0);
00242
00243 uInt child;
00244 for ( child = firstChild(actualNodeID);
00245 nextSibling(child) != NULL_NODE;
00246 child = nextSibling(child));
00247
00248 uInt lastActualChildNodeID = firstChild(actualNodeID);
00249 while (nextSibling(lastActualChildNodeID) != NULL_NODE)
00250 lastActualChildNodeID =
00251 nextSibling(lastActualChildNodeID);
00252
00253 numChilds(actualNodeID) += numChilds(vecinoNodeID);
00254 nextSibling(lastActualChildNodeID) = firstChild(vecinoNodeID);
00255 nodeID[newPointID] = actualNodeID;
00256 }
00257 }
00258 Q_ASSERT(nodeID[disjointSet.find(disjointSet.index(col, row))] == nodeID[disjointSet.find(disjointSet.index(i, j))]);
00259 }
00260 }
00261 }
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273 for (int n=0; n< points[threshold].size(); n++)
00274 {
00275 Q_ASSERT_X(threshold < 256, "getComponentTree", "out of bounds 4");
00276 const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00277 const uInt actualID = disjointSet.index(col, row);
00278
00279 Q_ASSERT_X(actualID < cols * rows, "getComponentTree", "out of bounds 5");
00280
00281
00282
00283
00284
00285
00286 if (actualID == disjointSet.find(actualID))
00287 {
00288 if (nodeID[actualID] == NULL_NODE)
00289
00290
00291 {
00292 nodeID[disjointSet.index(col, row)] = newNode(col, row, threshold);
00293 this->leafNodes++;
00294 }
00295 else
00296
00297 this->freePoints++;
00298 }
00299 else
00300 {
00301 this->freePoints++;
00302
00303
00304 }
00305
00306 const uInt actualNodeID = nodeID[disjointSet.find(actualID)];
00307 if (actualNodeID != NULL_NODE)
00308 {
00309 Q_ASSERT(area(actualNodeID)[lastThreshold(actualNodeID)] <= disjointSet.getSetCardinality(actualID));
00310 lastThreshold(actualNodeID) = threshold;
00311 area(actualNodeID)[threshold] = disjointSet.getSetCardinality(actualID);
00312 validNode(actualNodeID) = true;
00313 }
00314
00315 this->totalPoints++;
00316 }
00317 }
00318
00319 rootNode() = nodeID[disjointSet.find(0)];
00320
00321
00322 #ifndef QT_NO_DEBUG // TEST
00323 Q_ASSERT(validNode(this->rootNodeID));
00324
00325
00326
00327 uInt sumNodes = 0, sumChildNodes = 0, sumLeafNodes = 0;
00328
00329
00330 for (uInt node = 0; node < this->numNodes; node++)
00331 if (validNode(node))
00332 {
00333 const uInt fThreshold = firstThreshold(node), lThreshold = lastThreshold(node);
00334
00335 if (numChilds(node) == 0)
00336 sumLeafNodes++;
00337
00338 sumNodes++;
00339
00340 Q_ASSERT(area(node)[fThreshold] <= area(node)[lThreshold] != 0);
00341 Q_ASSERT(area(node)[fThreshold] != 0);
00342 Q_ASSERT(area(node)[lThreshold] != 0);
00343
00344
00345 Q_ASSERT(image(seedX(node), seedY(node)) == fThreshold);
00346
00347
00348 uInt lastArea = area(node)[fThreshold];
00349 for (uInt threshold = fThreshold+1; threshold <= lThreshold; threshold++)
00350 if (area(node)[threshold] != 0)
00351 {
00352 const uInt actualArea = area(node)[threshold];
00353 Q_ASSERT(actualArea >= lastArea);
00354 lastArea = actualArea;
00355 }
00356
00357 uInt maxAreasChilds = 0;
00358 uInt childs = 0;
00359 for ( uint child = firstChild(node);
00360 child != NULL_NODE;
00361 child = nextSibling(child), childs++)
00362 {
00363 Q_ASSERT(childs < numChilds(node));
00364 Q_ASSERT(child < this->numNodes);
00365 Q_ASSERT(child != rootNode());
00366 Q_ASSERT(validNode(child));
00367 maxAreasChilds += area(child)[lastThreshold(child)];
00368 }
00369
00370 Q_ASSERT(area(node)[firstThreshold(node)] > maxAreasChilds);
00371 Q_ASSERT(childs == numChilds(node));
00372
00373 sumChildNodes += numChilds(node);
00374 }
00375 qDebug() << "getComponentTree(): /////// estadisticos";
00376 qDebug() << "getComponentTree(): sumNodes = " << sumNodes << sumChildNodes;
00377 Q_ASSERT( sumNodes == (sumChildNodes+1) );
00378 Q_ASSERT( this->leafNodes == sumLeafNodes );
00379
00380 qDebug() << "getComponentTree(): this->numNodes = " << this->numNodes;
00381 qDebug() << "getComponentTree(): this->freePoints = "<< this->freePoints;
00382 qDebug() << "getComponentTree(): this->totalPoints = "<< this->totalPoints;
00383 qDebug() << "getComponentTree(): this->leafNodes + this->freePoints = "
00384 << this->leafNodes + this->freePoints;
00385
00386 Q_ASSERT(disjointSet.numberOfSets() == 1);
00387 Q_ASSERT(disjointSet.getSetCardinality(rootNode()) == (image.getCols() * image.getRows()));
00388 Q_ASSERT(this->totalPoints == (image.getCols() * image.getRows()));
00389 Q_ASSERT(this->totalPoints == this->leafNodes + this->freePoints);
00390
00391 #endif // END TEST
00392
00393 delete nodeID;
00394
00395 qDebug() << "getComponentTree() <~ return";
00396 }
00397
00398
00404
00405 void QVComponentTree::getComponentTree2(const QVImage<uChar> &image)
00406 {
00407 qDebug() << "getComponentTree()";
00408 const uInt cols = image.getCols(), rows = image.getRows();
00409 const QVector< QVector< QPoint > > points = qvdta::CountingSort(image);
00410
00411 uInt *nodeID = new uInt[maxNodes];
00412 for(uInt i=0; i<maxNodes; i++)
00413 nodeID[i] = NULL_NODE;
00414
00415 qvdta::QVDisjointSet disjointSet(cols, rows);
00416
00417 this->numNodes = 0;
00418 this->leafNodes = 0;
00419 this->freePoints = 0;
00420 this->totalPoints = 0;
00421
00422 for (uInt i=0; i< cols*rows; i++)
00423 Q_ASSERT(nodeID[i] == NULL_NODE);
00424
00425
00426
00427 QVIMAGE_INIT_READ(uChar,image);
00428
00429 for (int threshold = 0; threshold < points.size(); threshold++)
00430 {
00431
00432
00433
00434
00435 for (int n=0; n< points[threshold].size(); n++)
00436 {
00437 const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00438 const uChar actualPixel = QVIMAGE_PIXEL(image, col, row,0);
00439
00440
00441
00442 for (uInt i = (uInt) MAX(0,(int)col-1); i< MIN(cols, col+2); i++)
00443 for (uInt j = (uInt) MAX(0,(int)row-1); j< MIN(rows, row+2); j++)
00444 if ((col != i) || (row != j))
00445 {
00446 const uInt vecinoSet = disjointSet.find(i,j), actualSet = disjointSet.find(col, row);
00447 const uChar vecinoPixel = QVIMAGE_PIXEL(image, i, j,0);
00448
00449 if ( (vecinoPixel <= actualPixel) && (vecinoSet != actualSet) )
00450 {
00451
00452 const uInt newPointID = disjointSet.unify(col, row, i, j),
00453 actualNodeID = nodeID[actualSet],
00454 vecinoNodeID = nodeID[vecinoSet];
00455
00456 Q_ASSERT(disjointSet.find(disjointSet.index(col, row)) == disjointSet.find(disjointSet.index(i, j)));
00457 Q_ASSERT( (actualSet == newPointID) || (vecinoSet == newPointID) );
00458
00459
00460 if (actualNodeID == NULL_NODE)
00461 nodeID[newPointID] = vecinoNodeID;
00462 else if (vecinoNodeID == NULL_NODE)
00463 nodeID[newPointID] = actualNodeID;
00464
00465
00466
00467
00468 else {
00469 Q_ASSERT(actualNodeID != NULL_NODE);
00470 Q_ASSERT(vecinoNodeID != NULL_NODE);
00471
00472
00473
00474 if (!validNode(actualNodeID) && validNode(vecinoNodeID))
00475
00476 {
00477 addChild(actualNodeID, vecinoNodeID);
00478 nodeID[newPointID] = actualNodeID;
00479 }
00480 else if (validNode(actualNodeID) && !validNode(vecinoNodeID))
00481
00482 {
00483 addChild(vecinoNodeID, actualNodeID);
00484 nodeID[newPointID] = vecinoNodeID;
00485 }
00486 else if (validNode(actualNodeID) && validNode(vecinoNodeID))
00487
00488 {
00489 const uInt newNodeID = newNode(col, row, threshold);;
00490 nodeID[disjointSet.index(col, row)] = newNodeID;
00491
00492 addChild(newNodeID, actualNodeID);
00493 addChild(newNodeID, vecinoNodeID);
00494 nodeID[newPointID] = newNodeID;
00495 }
00496 else
00497
00498
00499
00500 {
00501 Q_ASSERT(validNode(actualNodeID) == false);
00502 Q_ASSERT(validNode(vecinoNodeID) == false);
00503 Q_ASSERT(numChilds(actualNodeID) > 0);
00504 Q_ASSERT(numChilds(vecinoNodeID) > 0);
00505
00506 uInt child;
00507 for ( child = firstChild(actualNodeID);
00508 nextSibling(child) != NULL_NODE;
00509 child = nextSibling(child));
00510
00511 uInt lastActualChildNodeID = firstChild(actualNodeID);
00512 while (nextSibling(lastActualChildNodeID) != NULL_NODE)
00513 lastActualChildNodeID =
00514 nextSibling(lastActualChildNodeID);
00515
00516 numChilds(actualNodeID) += numChilds(vecinoNodeID);
00517 nextSibling(lastActualChildNodeID) = firstChild(vecinoNodeID);
00518 nodeID[newPointID] = actualNodeID;
00519 }
00520 }
00521 Q_ASSERT(nodeID[disjointSet.find(disjointSet.index(col, row))] == nodeID[disjointSet.find(disjointSet.index(i, j))]);
00522 }
00523 }
00524 }
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536 for (int n=0; n< points[threshold].size(); n++)
00537 {
00538 Q_ASSERT_X(threshold < 256, "getComponentTree", "out of bounds 4");
00539 const uInt col = points[threshold][n].x(), row = points[threshold][n].y();
00540 const uInt actualID = disjointSet.index(col, row);
00541
00542 Q_ASSERT_X(actualID < cols * rows, "getComponentTree", "out of bounds 5");
00543
00544
00545
00546
00547
00548
00549 if (actualID == disjointSet.find(actualID))
00550 {
00551 if (nodeID[actualID] == NULL_NODE)
00552
00553
00554 {
00555 nodeID[disjointSet.index(col, row)] = newNode(col, row, threshold);
00556 this->leafNodes++;
00557 }
00558 else
00559
00560 this->freePoints++;
00561 }
00562 else
00563 {
00564 this->freePoints++;
00565
00566
00567 }
00568
00569 const uInt actualNodeID = nodeID[disjointSet.find(actualID)];
00570 if (actualNodeID != NULL_NODE)
00571 {
00572 Q_ASSERT(area(actualNodeID)[lastThreshold(actualNodeID)] <= disjointSet.getSetCardinality(actualID));
00573 lastThreshold(actualNodeID) = threshold;
00574 area(actualNodeID)[threshold] = disjointSet.getSetCardinality(actualID);
00575 validNode(actualNodeID) = true;
00576 }
00577
00578 this->totalPoints++;
00579 }
00580 }
00581
00582 rootNode() = nodeID[disjointSet.find(0)];
00583
00584
00585 #ifndef QT_NO_DEBUG // TEST
00586 Q_ASSERT(validNode(this->rootNodeID));
00587
00588
00589
00590 uInt sumNodes = 0, sumChildNodes = 0, sumLeafNodes = 0;
00591
00592
00593 for (uInt node = 0; node < this->numNodes; node++)
00594 if (validNode(node))
00595 {
00596 const uInt fThreshold = firstThreshold(node), lThreshold = lastThreshold(node);
00597
00598 if (numChilds(node) == 0)
00599 sumLeafNodes++;
00600
00601 sumNodes++;
00602
00603 Q_ASSERT(area(node)[fThreshold] <= area(node)[lThreshold] != 0);
00604 Q_ASSERT(area(node)[fThreshold] != 0);
00605 Q_ASSERT(area(node)[lThreshold] != 0);
00606
00607
00608 Q_ASSERT(image(seedX(node), seedY(node)) == fThreshold);
00609
00610
00611 uInt lastArea = area(node)[fThreshold];
00612 for (uInt threshold = fThreshold+1; threshold <= lThreshold; threshold++)
00613 if (area(node)[threshold] != 0)
00614 {
00615 const uInt actualArea = area(node)[threshold];
00616 Q_ASSERT(actualArea >= lastArea);
00617 lastArea = actualArea;
00618 }
00619
00620 uInt maxAreasChilds = 0;
00621 uInt childs = 0;
00622 for ( uint child = firstChild(node);
00623 child != NULL_NODE;
00624 child = nextSibling(child), childs++)
00625 {
00626 Q_ASSERT(childs < numChilds(node));
00627 Q_ASSERT(child < this->numNodes);
00628 Q_ASSERT(child != rootNode());
00629 Q_ASSERT(validNode(child));
00630 maxAreasChilds += area(child)[lastThreshold(child)];
00631 }
00632
00633 Q_ASSERT(area(node)[firstThreshold(node)] > maxAreasChilds);
00634 Q_ASSERT(childs == numChilds(node));
00635
00636 sumChildNodes += numChilds(node);
00637 }
00638 qDebug() << "getComponentTree(): /////// estadisticos";
00639 qDebug() << "getComponentTree(): sumNodes = " << sumNodes << sumChildNodes;
00640 Q_ASSERT( sumNodes == (sumChildNodes+1) );
00641 Q_ASSERT( this->leafNodes == sumLeafNodes );
00642
00643 qDebug() << "getComponentTree(): this->numNodes = " << this->numNodes;
00644 qDebug() << "getComponentTree(): this->freePoints = "<< this->freePoints;
00645 qDebug() << "getComponentTree(): this->totalPoints = "<< this->totalPoints;
00646 qDebug() << "getComponentTree(): this->leafNodes + this->freePoints = "
00647 << this->leafNodes + this->freePoints;
00648
00649 Q_ASSERT(disjointSet.numberOfSets() == 1);
00650 Q_ASSERT(disjointSet.getSetCardinality(rootNode()) == (image.getCols() * image.getRows()));
00651 Q_ASSERT(this->totalPoints == (image.getCols() * image.getRows()));
00652 Q_ASSERT(this->totalPoints == this->leafNodes + this->freePoints);
00653
00654 #endif // END TEST
00655
00656 delete nodeID;
00657
00658 qDebug() << "getComponentTree() <~ return";
00659 }
00660
00661
00662 }