Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

LabeledSkeletonImage.C

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------+
00002  | Library QgarLib, graphics analysis and recognition                  |
00003  | Copyright (C) 2002  Qgar Project, LORIA                             |
00004  |                                                                     |
00005  | This library is free software; you can redistribute it and/or       |
00006  | modify it under the terms of the GNU Lesser General Public          |
00007  | License version 2.1, as published by the Free Software Foundation.  |
00008  |                                                                     |
00009  | This library is distributed in the hope that it will be useful,     |
00010  | but WITHOUT ANY WARRANTY; without even the implied warranty of      |
00011  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                |
00012  | See the GNU Lesser General Public License for more details.         |
00013  |                                                                     |
00014  | The GNU Lesser General Public License is included in the file       |
00015  | LICENSE.LGPL, in the root directory of the Qgar packaging. See      |
00016  | http://www.gnu.org/licenses/lgpl.html for the terms of the licence. |
00017  | To receive a paper copy, write to the Free Software Foundation,     |
00018  | Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.       |
00019  |                                                                     |
00020  | Contact Project Qgar for any information:                           |
00021  |   LORIA - équipe Qgar                                               |
00022  |   B.P. 239, 54506 Vandoeuvre-lès-Nancy Cedex, France                |
00023  |   email: qgar-contact@loria.fr                                      |
00024  |   http://www.qgar.org/                                              |
00025  *---------------------------------------------------------------------*/
00026 
00027 
00028 /**
00029  * @file   LabeledSkeletonImage.C
00030  * @brief  Implementation of class qgar::LabeledSkeletonImage.
00031  *
00032  *         See file LabeledSkeletonImage.H for the interface.
00033  *
00034  * @author <a href="mailto:qgar-develop@loria.fr?subject=Qgar fwd Karl Tombre">Adlane Habed and Karl Tombre</a>
00035  * @date   July 3, 2001  16:57
00036  * @since  Qgar 1.0
00037  */
00038 
00039 
00040 
00041 // STD
00042 #include <vector>
00043 // QGAR
00044 #include <qgarlib/Dist34BlackCCImage.H>
00045 #include <qgarlib/GenImage.H>
00046 #include <qgarlib/LabeledSkeletonImage.H>
00047 #include <qgarlib/primitives.H>
00048 
00049 
00050 
00051 using namespace std;
00052 
00053 
00054 
00055 namespace qgar
00056 {
00057 
00058 // -------------------------------------------------------------------
00059 // C O N S T R U C T O R
00060 // -------------------------------------------------------------------
00061 
00062 
00063 LabeledSkeletonImage::LabeledSkeletonImage(const Dist34BlackCCImage& img,
00064                                            int maxPruning)
00065 
00066   : GreyLevelImage(img)
00067 
00068 {
00069   _pixMap2 = new GreyLevelImage::value_type[img.width() * img.height()];
00070   int pixsize = _width * _height; 
00071   GreyLevelImage::value_type* newp = _pixMap2; 
00072   int i = 0;
00073   int j = 0;    
00074   
00075   // Initialize _pixMap2
00076   for (i = 0 ; i < pixsize ; ++i, ++newp)
00077     {
00078       *newp = 255;
00079     }
00080 
00081   // Change labels from 3 to 1 and from 6 to 5
00082   // (see referenced article)
00083   GreyLevelImage::value_type* oldp = _pPixMap;
00084   for(i = 0 ; i < pixsize ; ++i, ++oldp)
00085     {
00086       if (*oldp == 3)
00087         {
00088           *oldp = 1;
00089         }
00090       if (*oldp == 6)
00091         {
00092           *oldp = 5;
00093         }
00094     }
00095 
00096   // Parallelwise detectable pixels
00097   parallelSkelet();
00098   
00099   // Change labels back from (1,5) to (3,6)
00100   oldp=_pPixMap;
00101   for(i = 0 ; i < pixsize ; ++i, ++oldp)
00102     {
00103       if (*oldp == 1)
00104         {
00105           *oldp = 3;
00106         }
00107       if (*oldp == 5)
00108         {
00109           *oldp = 6;
00110         }
00111     }
00112 
00113   // Sequentially detectable skeletal pixels
00114   sequentSkelet();
00115 
00116   // Now that they are tracked, mark also these pixels
00117   // as belonging to skeleton 
00118   newp = _pixMap2;
00119   for(i = 0 ; i < pixsize ; ++i, ++newp)
00120     {
00121       if (*newp == 1)
00122         {
00123           *newp = 0;
00124         }
00125     }
00126 
00127   // Hole filling and final thinning
00128   finalThinning();
00129 
00130   // Pruning if requested
00131   if (maxPruning > 0)
00132     {
00133       // End points detection
00134       // Create an array containing all the end points
00135       vector<Point> myEndPoints;
00136       GreyLevelImage::value_type n[10];
00137 
00138       for (i = 1 ; i < (_height - 1) ; ++i)
00139         {
00140           oldp = _pPixMap + (i * _width) + 1;
00141           newp = _pixMap2 + (i * _width) + 1;
00142 
00143           for (j = 1 ; j < (_width - 1) ; ++j, ++newp)
00144             {
00145               if(*newp == 0) // marked point
00146                 {
00147                   loadBinaryNeighbors(newp, n);
00148                   int x4=crossingCnt(n);
00149                   if(x4 == 1)
00150                     {
00151                       Point endp(j, i);
00152                       myEndPoints.push_back(endp);
00153                     }
00154                 }
00155             } // END for j
00156         } // END for i
00157 
00158       pruning(myEndPoints, maxPruning);
00159     }
00160 
00161   // Remove jaggedness
00162   removeJaggedness(); 
00163   
00164   // And now that we have marked everything, create the labeled skeleton
00165   // by setting to zero all non-skeleton pixels in original distance image
00166   oldp = _pPixMap;
00167   newp = _pixMap2;
00168 
00169   for (i = 0 ; i < pixsize ; ++i, ++oldp, ++newp)
00170     {
00171       if ((*oldp != 0) && (*newp != 0))
00172         {
00173           *oldp = 0;
00174         }
00175     }
00176 
00177   // Delete local images
00178   delete newp;
00179 }
00180 
00181 
00182 // -------------------------------------------------------------------
00183 // D E S T R U C T O R
00184 // -------------------------------------------------------------------
00185 
00186 
00187 // VIRTUAL DESTRUCTOR
00188 
00189 LabeledSkeletonImage::~LabeledSkeletonImage()
00190 {
00191   if (_pixMap2 != 0)
00192     {
00193       delete [] _pixMap2;
00194     }
00195 }
00196 
00197 
00198 // -------------------------------------------------------------------
00199 // A U X I L I A R I E S
00200 // -------------------------------------------------------------------
00201 
00202 
00203 // FIND PARALLELWISE DETECTABLE SKELETON PIXELS
00204 // ============================================
00205 
00206 void
00207 LabeledSkeletonImage::parallelSkelet()
00208 {
00209   GreyLevelImage::value_type lab[10];
00210   GreyLevelImage::value_type neighbors[10];
00211   GreyLevelImage::value_type suitbin[10];
00212 
00213   GreyLevelImage::value_type* newp;
00214   GreyLevelImage::value_type* oldp;
00215   
00216   for (int i = 1 ; i < (_height - 1) ; ++i)
00217     {
00218       oldp = _pPixMap + (i * _width) + 1;
00219       newp = _pixMap2 + (i * _width) + 1;
00220       
00221       for(int j = 1 ; j < (_width - 1) ; ++j, ++newp, ++oldp)
00222         {
00223           if (*oldp != 0) // Not background
00224             {  
00225               // Check if it is a maximal center
00226 
00227               loadLabeledNeighbors(oldp,lab);
00228             
00229               if ((lab[2] < (*oldp + 4)) && (lab[3] < (*oldp + 3)) &&
00230                   (lab[4] < (*oldp + 4)) && (lab[1] < (*oldp + 3)) &&
00231                   (lab[5] < (*oldp + 3)) && (lab[6] < (*oldp + 4)) &&
00232                   (lab[7] < (*oldp + 3)) && (lab[8] < (*oldp + 4)))
00233                 {
00234                   // any maximal center having a triple of consecutive neighbors
00235                   // odd/even/odd which are all labeled more than it is not
00236                   // marked
00237                   if (((lab[1] > *oldp) && (lab[2] > *oldp) && (lab[3] > *oldp)) ||
00238                       ((lab[3] > *oldp) && (lab[4] > *oldp) && (lab[5] > *oldp)) ||
00239                       ((lab[5] > *oldp) && (lab[6] > *oldp) && (lab[7] > *oldp)) || 
00240                       ((lab[7] > *oldp) && (lab[8] > *oldp) && (lab[1] > *oldp)))
00241                     {
00242                       *newp = 255;
00243                     }
00244                   else
00245                     {
00246                       *newp = 0; 
00247                     }
00248                 }
00249               else 
00250                 { // Check if we have a saddle pixel
00251                   loadLabeledNeighbors(oldp,neighbors);
00252                   loadSuitCBinary(suitbin,neighbors);
00253                   int c8 = connexityCnt(suitbin);
00254 
00255                   // loadLabeledNeighbors(oldp,neighbors);
00256                   loadSuitXBinary(suitbin,neighbors);
00257                   int x4 = crossingCnt(suitbin);
00258 
00259                   if (*oldp != 1)
00260                     {
00261                       if ((c8 > 1) || (x4 > 1))
00262                         {
00263                           *newp = 0;
00264                         }
00265                       else 
00266                         {
00267                           *newp = 255;
00268                         }
00269                     }
00270                   else
00271                     {
00272                       if ((c8 > 1) || (x4 > 1) || 
00273                           (oddEvenOddLab3(lab,1)==3) ||
00274                           (oddEvenOddLab3(lab,3)==3) ||
00275                           (oddEvenOddLab3(lab,5)==3) ||
00276                           (oddEvenOddLab3(lab,7)==3))
00277                         {
00278                           *newp = 0;
00279                         }
00280                       else
00281                         {
00282                           *newp = 255;
00283                         }
00284                     }
00285                 }
00286             }
00287           else
00288             {
00289               *newp = 255;
00290             }
00291 
00292         } // END for j
00293 
00294     } // END for i
00295 }
00296 
00297 
00298 // FIND SEQUENTIALLY DETECTABLE SKELETON PIXELS
00299 // ============================================
00300 
00301 
00302 void
00303 LabeledSkeletonImage::sequentSkelet()
00304 {
00305   GreyLevelImage::value_type lab[10];
00306   float grad[9];
00307   
00308   for (int i = 1 ; i < (_height - 1) ; ++i)
00309     {
00310       GreyLevelImage::value_type* oldp = _pPixMap + (i * _width) + 1;
00311       GreyLevelImage::value_type* newp = _pixMap2 + (i * _width) + 1;
00312 
00313       for (int j = 1 ; j < (_width - 1) ; ++j, ++newp, ++oldp)
00314         {
00315           if(*newp == 0)  // if marked as a maximal center by previous step
00316             {
00317               // Compute gradient in distance image
00318               loadLabeledNeighbors(oldp, lab);
00319               gradient(lab, grad);
00320             
00321               int indmax = maxGradientPos1(oldp, grad);
00322               int indmax2 = maxGradientPos2(grad, oldp, indmax);
00323             
00324               int encore = 1;
00325               while (encore)
00326                 { 
00327                   GreyLevelImage::value_type* savoldp = oldp; 
00328                   GreyLevelImage::value_type* savnewp = newp;
00329                   while (indmax != 0)
00330                     { 
00331                       // Go in the direction of maximum gradient
00332                       switch(indmax)
00333                         {
00334                         case 1:
00335                           --oldp;
00336                           --newp;
00337                           *newp = 1; // mark
00338                           break;
00339                         case 2:
00340                           oldp -= _width + 1;
00341                           newp -= _width + 1;
00342                           *newp = 1;
00343                           break;
00344                         case 3:
00345                           oldp -= _width;
00346                           newp -= _width;
00347                           *newp = 1;
00348                           break;
00349                         case 4:
00350                           oldp -= _width - 1;
00351                           newp -= _width - 1;
00352                           *newp = 1;
00353                           break;
00354                         case 5:
00355                           ++oldp;
00356                           ++newp;
00357                           *newp = 1;
00358                           break;
00359                         case 6:
00360                           oldp += _width + 1;
00361                           newp += _width + 1;
00362                           *newp = 1;
00363                           break;
00364                         case 7:
00365                           oldp += _width;
00366                           newp += _width;
00367                           *newp = 1;
00368                           break;
00369                         case 8:
00370                           oldp += _width - 1;
00371                           newp += _width - 1;
00372                           *newp = 1;
00373                           break;
00374                         default:
00375                           break; 
00376                         } // END switch
00377                           
00378                       // Compute gradient at new position
00379                       loadLabeledNeighbors(oldp, lab);
00380                       gradient(lab, grad); 
00381                    
00382                       indmax = maxGradientPos1(oldp, grad);
00383                     } // END while indmax
00384                   
00385                   // Restore
00386                   oldp = savoldp;  
00387                   newp = savnewp;
00388 
00389                   if (indmax2 != 0) // there was another starting point
00390                     {
00391                       encore = 1;
00392                       indmax = indmax2;
00393                       indmax2 = 0;
00394                     }
00395                   else
00396                     {
00397                       encore = 0;
00398                     }
00399                 } // END while encore
00400             }
00401         } // END for j
00402       
00403     } // END for i
00404 }
00405 
00406 
00407 // HOLE FILLING AND FINAL THINNING
00408 // ===============================
00409 
00410 
00411 void
00412 LabeledSkeletonImage::finalThinning()
00413 {
00414   GreyLevelImage::value_type lab[10];
00415     
00416   for (int i = 1 ; i < (_height - 1) ; ++i)
00417     {
00418       GreyLevelImage::value_type* newp = _pixMap2 + (i * _width) + 1;
00419 
00420       for (int j = 1 ; j < (_width - 1) ; ++j, ++newp)
00421         {
00422           if (*newp == 0) // marked: reduce to unit width
00423             {
00424               loadLabeledNeighbors(newp, lab);
00425               unitWidth(newp, lab);
00426             }
00427           else // not marked: detect holes
00428             {
00429               if ((*(newp - 1) == 0) && (*(newp - _width) == 0) &&
00430                   (*(newp + 1) == 0) && (*(newp + _width) == 0))
00431                 {  // if all odd-neighbors are marked
00432                   // Fill the hole
00433                   *newp = 0;
00434                   
00435                   // check again already inspected odd-neighbors to see
00436                   // if we can remove marker
00437                   GreyLevelImage::value_type* savp = newp;
00438                   
00439                   --newp;
00440                   loadLabeledNeighbors(newp, lab);
00441                   unitWidth(newp, lab);
00442                   
00443                   newp = savp - _width;
00444                   loadLabeledNeighbors(newp, lab);
00445                   unitWidth(newp, lab);
00446                   
00447                   // Restore pointer to continue inspection
00448                   newp = savp;
00449                 }
00450             }
00451         } // END for j
00452     } // END for i
00453 }
00454 
00455 
00456 // PRUNING
00457 // =======
00458 
00459 void
00460 LabeledSkeletonImage::pruning(vector< GenPoint<int> >& myEndPoints,
00461                               int maxPruning)
00462 {
00463   GreyLevelImage::value_type n[10];
00464   vector<Point>::iterator myIter = myEndPoints.begin();
00465 
00466   // Create a vector of pointers
00467   vector<GreyLevelImage::value_type*> pointsToBePruned;
00468 
00469   while (myIter != myEndPoints.end())
00470     {
00471       // Get the point
00472       Point endP = *myIter;
00473       // Put pointer on right position
00474       GreyLevelImage::value_type* startOldP =  _pPixMap + (endP.y() * _width) + endP.x();
00475       GreyLevelImage::value_type* startNewP =  _pixMap2 + (endP.y() * _width) + endP.x();
00476 
00477       GreyLevelImage::value_type* currOldP = startOldP;
00478       GreyLevelImage::value_type* currNewP = startNewP;
00479 
00480       // Where do we go from here?
00481       int nextPosition = 0;
00482       int d34 = 0; // (3,4) distance between start point and current point
00483       loadBinaryNeighbors(currNewP, n);
00484 
00485       for (int k = 1 ; (k < 9) && (nextPosition == 0) ; ++k)
00486         {
00487           if (n[k] != 0)
00488             {
00489               nextPosition = k;
00490             }
00491         }
00492 
00493       int iCameFrom = 0;
00494 
00495       // Go to next point
00496       switch (nextPosition)
00497         {
00498         case 1:
00499           --currOldP;
00500           --currNewP;
00501           d34 += 3;
00502           iCameFrom = 5;
00503           break;
00504         case 2:
00505           currOldP -= _width + 1;
00506           currNewP -= _width + 1;
00507           d34 += 4;
00508           iCameFrom = 6;
00509           break;
00510         case 3:
00511           currOldP -= _width;
00512           currNewP -= _width;
00513           d34 += 3;
00514           iCameFrom = 7;
00515           break;
00516         case 4:
00517           currOldP -= _width - 1;
00518           currNewP -= _width - 1;
00519           d34 += 4;
00520           iCameFrom = 8;
00521           break;
00522         case 5:
00523           ++currOldP;
00524           ++currNewP;
00525           d34 += 3;
00526           iCameFrom = 1;
00527           break;
00528         case 6:
00529           currOldP += _width + 1;
00530           currNewP += _width + 1;
00531           iCameFrom = 2;
00532           d34 += 4;
00533           break;
00534         case 7:
00535           currOldP += _width;
00536           currNewP += _width;
00537           iCameFrom = 3;
00538           d34 += 3;
00539           break;
00540         case 8:
00541           currOldP += _width - 1;
00542           currNewP += _width - 1;
00543           iCameFrom = 4;
00544           d34 += 4;
00545           break;
00546         default:
00547           break; 
00548         } // END switch
00549           
00550       // if pruning condition does hold, start a pruning process
00551       if (((*currOldP > *startOldP) || // labeled more than startig point
00552            (((*currOldP + 2)/3) == ((*startOldP +2)/3))) && // or same layer
00553           ((*startOldP - *currOldP + d34) <= 3 * maxPruning))
00554         {
00555           // Potential pruning branch
00556           vector<GreyLevelImage::value_type*> possiblePointsToBePruned;
00557 
00558           // The starting point can be pruned
00559           possiblePointsToBePruned.push_back(startNewP);
00560 
00561           // Can we go on?
00562           int canContinue = 1;
00563           // Can we prune?
00564           int canPrune = 1;
00565 
00566           while (canContinue)
00567             {
00568               // compute degree of current point
00569               int mydeg = degree(currNewP);
00570               if (mydeg > 2)
00571                 canContinue = 0;
00572               else if (mydeg < 2) // Do not prune, we have come to an end
00573                 {
00574                   canContinue = 0;
00575                   canPrune = 0;
00576                 }
00577               else // actual tracking, with mydeg == 2
00578                 {
00579                   // Save position from which we are
00580                   GreyLevelImage::value_type* savNewP = currNewP;
00581                   // Apparently not used!
00582                   //GreyLevelImage::value_type* savOldP = currOldP;
00583 
00584                   // Find where we are going, without going back
00585                   // to where we came from
00586                   nextPosition = 0;
00587                   loadBinaryNeighbors(currNewP, n);
00588                   for (int k = 1 ; (k < 9) && (nextPosition == 0) ; ++k)
00589                     {
00590                     if ((n[k] != 0) && (k != iCameFrom))
00591                       {
00592                         nextPosition = k;
00593                       }
00594                     }
00595                   
00596                   // Go to next point
00597                   switch (nextPosition)
00598                     {
00599                     case 1:
00600                       --currOldP;
00601                       --currNewP;
00602                       d34 += 3;
00603                       iCameFrom = 5;
00604                       break;
00605                     case 2:
00606                       currOldP -= _width + 1;
00607                       currNewP -= _width + 1;
00608                       d34 += 4;
00609                       iCameFrom = 6;
00610                       break;
00611                     case 3:
00612                       currOldP -= _width;
00613                       currNewP -= _width;
00614                       d34 += 3;
00615                       iCameFrom = 7;
00616                       break;
00617                     case 4:
00618                       currOldP -= _width - 1;
00619                       currNewP -= _width - 1;
00620                       d34 += 4;
00621                       iCameFrom = 8;
00622                       break;
00623                     case 5:
00624                       ++currOldP;
00625                       ++currNewP;
00626                       d34 += 3;
00627                       iCameFrom = 1;
00628                       break;
00629                     case 6:
00630                       currOldP += _width + 1;
00631                       currNewP += _width + 1;
00632                       iCameFrom = 2;
00633                       d34 += 4;
00634                       break;
00635                     case 7:
00636                       currOldP += _width;
00637                       currNewP += _width;
00638                       iCameFrom = 3;
00639                       d34 += 3;
00640                       break;
00641                     case 8:
00642                       currOldP += _width - 1;
00643                       currNewP += _width - 1;
00644                       iCameFrom = 4;
00645                       d34 += 4;
00646                       break;
00647                     default:
00648                       break; 
00649                     }
00650                   // Does the pruning condition hold?
00651                   // ** Should we rather check from savOldP ???
00652                   if (((*currOldP > *startOldP) || 
00653                        (((*currOldP + 2)/3) == ((*startOldP +2)/3))) && 
00654                       ((*startOldP - *currOldP + d34) <= 3 * maxPruning))
00655                     // The previous point can definitively be pruned
00656                     {
00657                       possiblePointsToBePruned.push_back(savNewP);
00658                       mydeg = degree(currNewP);
00659                     }
00660                   else
00661                     {
00662                       canContinue = 0;
00663                       canPrune = 0;  // ** or should we prune what we can ?
00664                     }
00665       
00666                 }
00667             }
00668 
00669           if (canPrune != 0)
00670             {
00671               // we actually can prune these points
00672               pointsToBePruned.insert(pointsToBePruned.end(), 
00673                                       possiblePointsToBePruned.begin(),
00674                                       possiblePointsToBePruned.end());
00675             }
00676           
00677           possiblePointsToBePruned.erase(possiblePointsToBePruned.begin(),
00678                                          possiblePointsToBePruned.end());
00679           
00680         }
00681       else
00682         {
00683           // if pruning condition does not hold, forget it
00684         }
00685 
00686       // Advance
00687       ++myIter;
00688     }
00689 
00690   // Now we have found all that can be pruned
00691   // Remove the corresponding points from
00692 
00693   vector<GreyLevelImage::value_type*>::iterator iterPoints =
00694     pointsToBePruned.begin();
00695 
00696   while (iterPoints != pointsToBePruned.end())
00697     {
00698       *(*iterPoints) = 255;
00699       ++iterPoints;
00700     }
00701 }
00702 
00703 
00704 // REMOVE JAGGEDNESS
00705 // =================
00706 
00707 
00708 // As noted by Sanniti di Baja in her article, we may want to make
00709 // this condition more strict, by allowing the shift only if the
00710 // resulting marked pixel belongs to the set of skeletal pixels
00711 // identified during the process. This would need a special marking
00712 // for those pixels.
00713 // I think this is not necessary, but let us keep it in mind. (KT)
00714 
00715 void
00716 LabeledSkeletonImage::removeJaggedness()
00717 {
00718   GreyLevelImage::value_type n[10];
00719 
00720   for (int i = 1 ; i < (_height-1) ; ++i)
00721     {
00722       GreyLevelImage::value_type* newp = _pixMap2 + (i * _width) + 1;
00723       GreyLevelImage::value_type* oldp = _pPixMap + (i * _width) + 1;
00724 
00725       for(int j = 1 ; j < (_width - 1) ; ++j, ++newp, ++oldp)
00726         {
00727           if ((*newp == 0) && (degree(newp) == 2))
00728             {
00729               loadBinaryNeighbors(newp, n);
00730 
00731               if ((n[2]==1) && (n[4]==1) && (*(oldp-_width)!=0))
00732                 {
00733                   *newp          = 255;
00734                   *(newp-_width) = 0;
00735                 }
00736               else if ((n[4]==1) && (n[6]==1) && (*(oldp+1)!=0))
00737                 {
00738                   *newp     = 255;
00739                   *(newp+1) = 0;
00740                 }
00741               else if ((n[6]==1) && (n[8]==1) && (*(oldp+_width)!=0))
00742                 {
00743                   *newp          = 255;
00744                   *(newp+_width) = 0;
00745                 }
00746               
00747               else if ((n[8]==1) && (n[2]==1) && (*(oldp-1)!=0))
00748                 {
00749                   *newp     = 255;
00750                   *(newp-1) = 0;
00751                 }
00752             } // END if
00753         } // END for j
00754     } // END for i
00755 }
00756 
00757 
00758 
00759 // LOAD THE RIGHT LABELS FOR A NEIGHBORHOOD
00760 // ========================================
00761 
00762 
00763 void
00764 LabeledSkeletonImage::loadLabeledNeighbors(unsigned char* p, unsigned char* n)
00765 {
00766   *n = *p;
00767   ++n;
00768   *n = *(p - 1);
00769   ++n;
00770   *n = *(p - _width - 1);
00771   ++n;
00772   *n = *(p -_width);
00773   ++n;
00774   *n = *(p -_width + 1);
00775   ++n;
00776   *n = *(p + 1);
00777   ++n;
00778   *n = *(p + _width + 1);
00779   ++n;
00780   *n = *(p + _width);
00781   ++n;
00782   *n = *(p + _width - 1);
00783   ++n;
00784   *n = *(p - 1);  // n[9] == n[1]
00785 }
00786 
00787 
00788 // LOAD THE BINARY IMAGE OF A NEIGHBOURHOOD
00789 // ========================================
00790 
00791 
00792 void
00793 LabeledSkeletonImage::loadBinaryNeighbors(unsigned char* p, unsigned char* n)
00794 {
00795   *n = *p ? 0 : 1;
00796   ++n;
00797   *n = *(p-1) ? 0 : 1;
00798   ++n;
00799   *n = *(p - _width - 1) ? 0 : 1;
00800   ++n;
00801   *n = *(p - _width) ? 0 : 1;
00802   ++n;
00803   *n = *(p - _width + 1) ? 0 : 1;
00804   ++n;
00805   *n = *(p + 1) ? 0 : 1;
00806   ++n;
00807   *n = *(p + _width + 1) ? 0 : 1;
00808   ++n;
00809   *n = *(p + _width) ? 0 : 1;
00810   ++n;
00811   *n = *(p + _width - 1) ? 0 : 1;
00812   ++n;
00813   *n = *(p - 1) ? 0 : 1;  // n[9] == n[1]
00814 }
00815 
00816 
00817 // LOAD SUITABLE X BINARY NEIGHBORS
00818 // ================================
00819 
00820 
00821 void
00822 LabeledSkeletonImage::loadSuitXBinary(unsigned char suitbin[], unsigned char n[])
00823 {
00824   for (int i = 1 ; i < 10 ; ++i)
00825     {
00826       if ((n[i] < n[0]) && (n[i] != 255))
00827         {
00828           suitbin[i] = 1;
00829         }
00830       else
00831         {
00832           suitbin[i] = 0;
00833         }
00834     }
00835 }
00836 
00837 
00838 // LOAD SUITABLE C BINARY NEIGHBORS
00839 // ================================
00840 
00841 
00842 void
00843 LabeledSkeletonImage::loadSuitCBinary(unsigned char suitbin[], unsigned char n[])
00844 {
00845   for (int i = 1 ; i < 10 ; ++i)
00846     {
00847       if((n[i] > n[0]) && (n[i] != 255))
00848         {
00849           suitbin[i] = 1;
00850         }
00851       else
00852         {
00853           suitbin[i] = 0;
00854         }
00855     }
00856 }
00857 
00858 
00859 // NUMBER OF 8-CONNECTED COMPONENTS IN A NEIGHBORHOOD
00860 // ==================================================
00861 
00862 
00863 int
00864 LabeledSkeletonImage::connexityCnt(unsigned char n[])
00865 {
00866   int c8 = 0;
00867 
00868   for (int k = 1 ; k <= 4 ; ++k)
00869     {
00870       int k2 = 2 * k;
00871       int tmp = 1 - n[k2 - 1];
00872       c8 += tmp - (tmp * (1 - n[k2]) * (1 - n[k2 + 1]));
00873     }
00874 
00875   return c8;
00876 }
00877 
00878 
00879 // NUMBER OF 4-CONNECTED COMPONENTS IN A NEIGHBORHOOD
00880 // ==================================================
00881 
00882 
00883 int
00884 LabeledSkeletonImage::crossingCnt(unsigned char n[])
00885 {
00886   int x4 = 0;
00887   
00888   for(int k = 1 ; k <= 8 ; ++k)
00889     {
00890       x4 += std::abs(n[k + 1] - n[k]);
00891     }
00892   
00893   x4 /= 2;
00894   return x4;
00895 }
00896 
00897 
00898 // DEGREE OF A POINT: ITS NUMBER OF MARKED NEIGHBORS
00899 // =================================================
00900 
00901 
00902 int
00903 LabeledSkeletonImage::degree(unsigned char* p)
00904 {
00905   int d = 0;
00906 
00907   if(*(p-1) != 255)
00908     {
00909       ++d;
00910     }
00911   if(*(p - _width - 1) != 255)
00912     {
00913       ++d;
00914     }
00915   if(*(p - _width) != 255)
00916     {
00917       ++d;
00918     }
00919   if(*(p - _width + 1) != 255)
00920     { 
00921       ++d;
00922     }
00923   if(*(p + 1) != 255)
00924     {
00925       ++d;
00926     }
00927   if(*(p + _width + 1) != 255)
00928     {
00929       ++d;
00930     }
00931   if(*(p + _width) != 255)
00932     {
00933       ++d;
00934     }
00935   if(*(p + _width - 1) != 255)  
00936     {
00937       ++d;
00938     }
00939 
00940   return d;
00941 }
00942 
00943 
00944 // TRIPLE CONSECUTIVE NEIGHBORS ALL LABELED 3
00945 // ==========================================
00946 
00947 
00948 int
00949 LabeledSkeletonImage::oddEvenOddLab3(unsigned char n[], int debut)
00950 {
00951   return n[debut] + n[debut + 1] + n[debut + 2];
00952 }
00953 
00954 
00955 // GRADIENT IN A 3*3 NEIGHBORHOOD
00956 // ==============================
00957 
00958 
00959 void
00960 LabeledSkeletonImage::gradient(unsigned char n[], float grad[])
00961 {
00962   float* g = grad;
00963   *g++ = 0.0;
00964 
00965   unsigned char* pn = n + 1;
00966 
00967   for (int k = 1 ; k < 9 ; ++k)
00968     {
00969       *g++ = (float) (*pn++ - *n) / (((k % 2) == 0) ? 4.0 : 3.0);
00970     }
00971 }
00972 
00973 
00974 // POSITION OF MAXIMUM GRADIENT
00975 // ============================
00976 
00977 
00978 int
00979 LabeledSkeletonImage::maxGradientPos1(unsigned char* p, float grad[])
00980 {
00981   int ind = 0;
00982   float* g = grad + 1;
00983 
00984   for(int k = 1 ; k < 9 ; ++k, ++g)
00985     {
00986       if (*g > grad[0])
00987         {
00988           ind = k;
00989           grad[0] = *g;
00990         }
00991     }
00992 
00993   return ind;
00994 }
00995 
00996 
00997 // FIND, IF ANY, SECOND POSITION WITH SAME MAXIMUM GRADIENT
00998 // ========================================================
00999 
01000 
01001 int
01002 LabeledSkeletonImage::maxGradientPos2(float grad[], unsigned char* p, int indmax)
01003 {
01004   int ind=0;
01005 
01006   if ((*p == 3) || (*p == 4))
01007     {
01008       float* g = grad + indmax + 1;
01009 
01010       for(int k = indmax + 1 ; k < 9 ; ++k, ++g)
01011         {
01012           if(*g == grad[0])
01013             {
01014               ind = k;
01015             }
01016         }
01017     }
01018 
01019   return ind;
01020 }
01021 
01022 
01023 // REDUCE TO UNIT WIDTH
01024 // ====================
01025 
01026 
01027 // Erase marker from any marked pixel satisfying both:
01028 // - at least one odd-neighbor is not marked,
01029 // - at least a triplet of neighbors (nk, nk+2, nk+5)
01030 //   (k odd, addition modulo 8) exists such that
01031 //   nk and nk+2 are marked, with nk+5 not marked
01032 
01033 void
01034 LabeledSkeletonImage::unitWidth(unsigned char* p, unsigned char lab[])
01035 {
01036   if (((lab[1] == 255) || (lab[3] == 255) || 
01037        (lab[5] == 255) || (lab[7] == 255))
01038       &&
01039       (((lab[1] == 0) && (lab[3] == 0) && (lab[6] == 255)) ||
01040        ((lab[3] == 0) && (lab[5] == 0) && (lab[8] == 255)) ||
01041        ((lab[5] == 0) && (lab[7] == 0) && (lab[2] == 255)) ||
01042        ((lab[7] == 0) && (lab[1] == 0) && (lab[4] == 255))))
01043     {
01044       *p = 255;
01045     }
01046 }
01047 
01048 
01049 // -------------------------------------------------------------------
01050 
01051 } // namespace qgar