connectedComponents.h

Go to the documentation of this file.
00001 
00015 #ifndef _DLRCOMPUTERVISION_CONNECTEDCOMPONENTS_H_
00016 #define _DLRCOMPUTERVISION_CONNECTEDCOMPONENTS_H_
00017 
00018 #include <list>
00019 #include <dlrComputerVision/imageFormat.h>
00020 #include <dlrComputerVision/image.h>
00021 
00022 namespace dlr {
00023 
00024   namespace computerVision {
00025   
00044     template<ImageFormat FORMAT_OUT, ImageFormat FORMAT_IN>
00045     Image<FORMAT_OUT>
00046     connectedComponents(const Image<FORMAT_IN>& inputImage);
00047   
00048 
00072     template<ImageFormat FORMAT_OUT, ImageFormat FORMAT_IN>
00073     Image<FORMAT_OUT>
00074     connectedComponents(const Image<FORMAT_IN>& inputImage,
00075                         size_t& numberOfComponents);
00076 
00077   } // namespace computerVision
00078     
00079 } // namespace dlr
00080 
00081 
00082 /* ============ Definitions of inline & template functions ============ */
00083 
00084 
00085 #include <cmath>
00086 
00087 namespace dlr {
00088 
00089   namespace computerVision {
00090 
00092     namespace privateCode {
00093 
00094       // This function traverses the blob label graph, starting at a
00095       // particular node, propagating that node's label so that any blobs
00096       // which are connected to the starting node inherit the the starting
00097       // nodes label iff the starting nodes label is less than the current
00098       // label of the connected blob.
00099       bool
00100       propagateLabel(size_t label, size_t node, std::vector<size_t>& labelArray,
00101                      const std::vector< std::list<size_t> >& neighborsVector)
00102       {
00103         // If we already know that the label of this node is not the
00104         // lowest among all of the nodes it's connected to, then stop
00105         // now.
00106         if(labelArray[node] <= label) {
00107           return false;
00108         }
00109         labelArray[node] = label;
00110 
00111         // Make a list of neighbors we need to check.
00112         std::list<size_t> nodeList;
00113         std::copy(neighborsVector[node].begin(),
00114                   neighborsVector[node].end(),
00115                   std::back_inserter(nodeList));
00116 
00117         // For each neighbor...
00118         std::list<size_t>::iterator nextNodeIterator = nodeList.begin();
00119         while(nextNodeIterator != nodeList.end()) {
00120           // Don't update labels that are already lower than the current label.
00121           if(labelArray[*nextNodeIterator] <= label) {
00122             ++nextNodeIterator;
00123             continue;
00124           }
00125           // Propagate the label and add any new neighbors (nodes that
00126           // are connected to the node to which we're propagating) to our
00127           // list.
00128           labelArray[*nextNodeIterator] = label;
00129           std::copy(neighborsVector[*nextNodeIterator].begin(),
00130                     neighborsVector[*nextNodeIterator].end(),
00131                     std::back_inserter(nodeList));
00132           ++nextNodeIterator;
00133         }
00134         return true;
00135       }
00136 
00137     } // namespace privateCode
00139 
00140   
00141     // This function does connected components analysis on a previously
00142     // segmented image.
00143     template<ImageFormat FORMAT_OUT, ImageFormat FORMAT_IN>
00144     Image<FORMAT_OUT>
00145     connectedComponents(const Image<FORMAT_IN>& inputImage)
00146     {
00147       size_t numberOfComponents;
00148       return connectedComponents<FORMAT_OUT>(inputImage, numberOfComponents);
00149     }
00150 
00151 
00152     // This function does connected components analysis on a previously
00153     // segmented image.
00154     template<ImageFormat FORMAT_OUT, ImageFormat FORMAT_IN>
00155     Image<FORMAT_OUT>
00156     connectedComponents(const Image<FORMAT_IN>& inputImage,
00157                         size_t& numberOfComponents)
00158     {
00159       typedef typename Image<FORMAT_IN>::const_iterator InIterator;
00160       typedef typename Image<FORMAT_OUT>::iterator OutIterator;
00161       typedef Array2D<size_t>::iterator LabelIterator;
00162     
00163       Image<FORMAT_OUT> outputImage(inputImage.rows(), inputImage.columns());
00164       Array2D<size_t> labelImage(inputImage.rows(), inputImage.columns());
00165       size_t currentLabel = 0;
00166 
00167       // This variable will be used to keep track of whether or not the
00168       // previous pixel was part of a blob.
00169       bool isActive = false;
00170     
00171       // Get iterators pointing to the first pixel of the input image, and
00172       // the first pixel of the label image.
00173       InIterator inIter = inputImage.begin();
00174       LabelIterator labelIter = labelImage.begin();
00175 
00176       // Label the first row.
00177       for(size_t columnIndex = 0; columnIndex < inputImage.columns();
00178           ++columnIndex) {
00179         if(*inIter) {
00180           // The pixel value is nonzero, we're in a blob.
00181           if(!isActive) {
00182             // If the previous pixel was zero, we're in what might be a
00183             // new blob, so increment the blob label.
00184             ++currentLabel;
00185             isActive = true;
00186           }
00187           // Label the pixel with its tentative label.
00188           *labelIter = currentLabel;
00189         } else {
00190           // The pixel value is zero, not in a blob.
00191           *labelIter = 0;
00192           isActive = false;
00193         }
00194         // Move to the next pixel.
00195         ++inIter;
00196         ++labelIter;
00197       }
00198 
00199       // Label the remaining rows.
00200       std::vector< std::pair<size_t, size_t> > correspondences;
00201       LabelIterator chaseIter = labelImage.begin();
00202       size_t workingLabel = 0;
00203       size_t previousParentLabel = 0;
00204       for(size_t rowIndex = 1; rowIndex < inputImage.rows(); ++rowIndex) {
00205         // At the beginning of each row, pretend there was a zero pixel
00206         // that we just came from, and update isActive accordingly.
00207         isActive = false;
00208         for(size_t columnIndex = 0; columnIndex < inputImage.columns();
00209             ++columnIndex) {
00210           if(*inIter) {
00211             // The pixel value is nonzero, we're in a blob.  Get the
00212             // blob label of the pixel in the row above (0 means "not in
00213             // a blob").
00214             size_t parentLabel = *chaseIter;
00215             if(!isActive) {
00216               // The pixel in the previous column was zero.  This blob
00217               // might be a new one.
00218               if((parentLabel != 0)) {
00219                 // The pixel in the previous row was part of a blob.
00220                 // Adopt its label.
00221                 workingLabel = parentLabel;
00222                 previousParentLabel = parentLabel;
00223               } else {
00224                 // The pixel in the previous row was not part of a blob.
00225                 // Get a new label.
00226                 ++currentLabel;
00227                 workingLabel = currentLabel;
00228                 previousParentLabel = 0;
00229               }
00230               isActive = true;
00231             } else {
00232               // The pixel in the previous column was part of a blob.
00233               if((parentLabel != 0)
00234                  && (parentLabel != previousParentLabel)) {
00235                 // We just connected a blob in the previous row with the
00236                 // blob in the previous column.  Record the
00237                 // correspondence.
00238                 correspondences.push_back(
00239                   std::make_pair(parentLabel, workingLabel));
00240                 previousParentLabel = parentLabel;
00241               }
00242             }
00243             // Assign the appropriate label to this pixel.
00244             *labelIter = workingLabel;
00245           } else {
00246             // We're at a zero pixel.  Update its label accordingly.
00247             *labelIter = 0;
00248             isActive = false;
00249           }
00250           // Move to the next pixel.
00251           ++inIter;
00252           ++labelIter;
00253           ++chaseIter;
00254         }
00255       }
00256 
00257       // === Resolve label equivalences. ===
00258 
00259       // Create an look up table which will take tentative labels
00260       // (assigned above) and map them to finalized labels.  We'll start
00261       // by assigning each element in the lookup table an impossible
00262       // value, and then we'll go back and correct each element in turn.
00263       std::vector<size_t> labelArray(currentLabel + 1);
00264       for(size_t label = 0; label < labelArray.size(); ++label) {
00265         labelArray[label] = currentLabel + 1;
00266       }
00267 
00268       // Imagine a graph in which each node is a label, and each edge is
00269       // an equivalence between labels.  Create a list of edges for each
00270       // node in the graph.
00271       typedef std::vector< std::pair<size_t, size_t> >::const_iterator
00272         CorrespondenceIterator;
00273       std::vector< std::list<size_t> > neighborsVector(labelArray.size());
00274       CorrespondenceIterator cIter = correspondences.begin();
00275       while(cIter != correspondences.end()) {
00276         neighborsVector[cIter->first].push_back(cIter->second);
00277         neighborsVector[cIter->second].push_back(cIter->first);
00278         ++cIter;
00279       }
00280 
00281       // Propagate labels over all edges.
00282       currentLabel = 0;
00283       for(size_t node = 0; node < labelArray.size(); ++node) {
00284         if(privateCode::propagateLabel(
00285              currentLabel, node, labelArray, neighborsVector)) {
00286           ++currentLabel;
00287         }
00288       }
00289 
00290       // Relabel the blobs.
00291       labelIter = labelImage.begin();
00292       OutIterator outIter = outputImage.begin();
00293       while(labelIter != labelImage.end()) {
00294                 *outIter = static_cast<typename ImageFormatTraits<FORMAT_OUT>::PixelType>(labelArray[*labelIter]);
00295         ++outIter;
00296         ++labelIter;
00297       }
00298 
00299       if(currentLabel != 0) {
00300         numberOfComponents = currentLabel - 1;
00301       } else {
00302         numberOfComponents = 0;
00303       }
00304     
00305       return outputImage;
00306     }
00307 
00308   } // namespace computerVision
00309     
00310 } // namespace dlr
00311 
00312 #endif /* #ifndef _DLRCOMPUTERVISION_CONNECTEDCOMPONENTS_H_ */

Generated on Wed Nov 25 12:15:04 2009 for dlrComputerVision Utility Library by  doxygen 1.5.8