segmenterFelzenszwalb.h

Go to the documentation of this file.
00001 
00015 #ifndef DLR_COMPUTERVISION_SEGMENTERFELZENSZWALB_H
00016 #define DLR_COMPUTERVISION_SEGMENTERFELZENSZWALB_H
00017 
00018 #include <vector>
00019 #include <dlrComputerVision/disjointSet.h>
00020 #include <dlrComputerVision/filter.h>
00021 #include <dlrComputerVision/image.h>
00022 #include <dlrComputerVision/kernels.h>
00023 #include <dlrComputerVision/utilities.h>
00024 
00025 namespace dlr {
00026 
00027   namespace computerVision {
00028 
00029     class EdgeDefaultFunctor {
00030     public:
00031       template <ImageFormat FORMAT>
00032       double operator()(Image<FORMAT> const& inImage,
00033                         size_t index0, size_t index1) {
00034         double result = inImage[index1] - inImage[index0];
00035         return (result < 0.0) ? -result : result;
00036       }
00037     };
00038       
00039       
00040     struct Edge {
00041       size_t end0;
00042       size_t end1;
00043       float weight;
00044     };
00045 
00046 
00057     template <class EdgeFunctor>
00058     class SegmenterFelzenszwalb {
00059     public:
00060 
00061       SegmenterFelzenszwalb(
00062         float k = 200.0f,
00063         float sigma = 0.8f,
00064         size_t minSegmentSize = 20,
00065         EdgeFunctor const& edgeFunctor = EdgeFunctor());
00066 
00067       
00068       virtual
00069       ~SegmenterFelzenszwalb() {}
00070 
00071 
00072       template <ImageFormat FORMAT>
00073       std::vector<Edge>
00074       getEdges(const Image<FORMAT>& inImage);
00075 
00076       
00077       virtual Array2D<UnsignedInt32>
00078       getLabelArray();
00079 
00080       
00081       virtual Array2D<UnsignedInt32>
00082       getLabelArray(UnsignedInt32& numberOfSegments,
00083                     std::vector<size_t>& segmentSizes);
00084 
00085 
00086       template <ImageFormat FORMAT>
00087       void
00088       segment(const Image<FORMAT>& inputImage);
00089 
00090 
00091       template <class ITER>
00092       void
00093       segmentFromEdges(size_t imageRows, size_t imageColumns,
00094                        ITER edgeBegin, ITER edgeEnd);
00095 
00096       
00097     protected:
00098 
00099       typedef privateCode::DisjointSet<float> Segment;
00100 
00101 
00102       inline float
00103       getCost(const Segment& C_i, const Segment& C_j);
00104 
00105 
00106       template <ImageFormat FORMAT>
00107       inline void
00108       setEdge(Edge& edge, size_t index0, size_t index1,
00109               Image<FORMAT> inImage);
00110 
00111       
00112       inline void
00113       updateCost(Segment& C_i, float weight);
00114 
00115 
00116       EdgeFunctor m_edgeFunctor;
00117       numeric::Index2D m_imageSize;
00118       float m_k;
00119       size_t m_minimumSegmentSize;
00120       numeric::Array1D<Segment> m_segmentation;
00121       float m_sigma;
00122       size_t m_smoothSize;
00123       
00124     };
00125 
00126 
00127     inline bool
00128     operator<(Edge const& arg0, Edge const& arg1) {
00129       return arg0.weight < arg1.weight;
00130     }
00131     
00132   } // namespace computerVision
00133   
00134 } // namespace dlr
00135 
00136 
00137 /* ============ Definitions of inline & template functions ============ */
00138 
00139 
00140 #include <cmath>
00141 #include <limits>
00142 
00143 namespace dlr {
00144 
00145   namespace computerVision {
00146 
00147     template <class EdgeFunctor>
00148     SegmenterFelzenszwalb<EdgeFunctor>::
00149     SegmenterFelzenszwalb(float k, float sigma, size_t minSegmentSize,
00150                           EdgeFunctor const& edgeFunctor)
00151       : m_edgeFunctor(edgeFunctor),
00152         m_imageSize(0, 0),
00153         m_k(k),
00154         m_minimumSegmentSize(minSegmentSize),
00155         m_segmentation(),
00156         m_sigma(sigma),
00157         m_smoothSize(static_cast<size_t>(std::fabs(6 * sigma + 1)))
00158     {
00159       // Smoothing kernel must not have even size or filter2D() will
00160       // complain later.
00161       if(m_smoothSize % 2 == 0) {
00162         m_smoothSize += 1;
00163       }
00164     }
00165 
00166     
00167     template <class EdgeFunctor>
00168     template <ImageFormat FORMAT>
00169     std::vector<Edge>
00170     SegmenterFelzenszwalb<EdgeFunctor>::
00171     getEdges(const Image<FORMAT>& inImage)
00172     {
00173       // 8-connected means 4 undirected edges per pixel.
00174       size_t numEdges = 4 * inImage.size();
00175 
00176       // Except that some point off the side of the image.
00177       numEdges -= 3 * inImage.rows();
00178 
00179       // And some point off the bottom/top of the image.
00180       numEdges -= 3 * inImage.columns();
00181 
00182       // Oops! We counted the bottom-right and bottom-left corners twice.
00183       numEdges += 2;
00184 
00185       std::vector<Edge> edges(numEdges);
00186       size_t interiorRows = inImage.rows() - 1;
00187       size_t interiorColumns = inImage.columns() - 1;
00188       size_t edgeNumber = 0;
00189       size_t pixelIndex0 = 0;
00190       size_t pixelIndex1;
00191       for(size_t row = 0; row < interiorRows; ++row) {
00192         // Get the first pixel of the row;
00193         pixelIndex1 = pixelIndex0 + 1;
00194         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00195       
00196         pixelIndex1 += inImage.columns();
00197         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00198 
00199         pixelIndex1 -= 1;
00200         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00201         ++pixelIndex0;
00202       
00203         // Get the interior pixels of the row.
00204         for(size_t column = 1; column < interiorColumns; ++column) {
00205           pixelIndex1 = pixelIndex0 + 1;
00206           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00207 
00208           pixelIndex1 += inImage.columns();
00209           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00210 
00211           pixelIndex1 -= 1;
00212           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00213 
00214           pixelIndex1 -= 1;
00215           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00216           ++pixelIndex0;
00217         }
00218 
00219         // Get the last pixel of the row.
00220         pixelIndex1 = pixelIndex0 + inImage.columns();
00221         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00222 
00223         pixelIndex1 -= 1;
00224         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00225         ++pixelIndex0;
00226       }
00227 
00228       // Sanity check.
00229       if(pixelIndex0 != (inImage.rows() - 1) * inImage.columns()) {
00230         DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges()",
00231                   "Indexing error.");
00232       }
00233     
00234       // Get the last row of pixels;
00235       for(size_t column = 0; column < interiorColumns; ++column) {
00236         pixelIndex1 = pixelIndex0 + 1;
00237         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00238         ++pixelIndex0;
00239       }
00240 
00241       // Sanity check.
00242       if(edgeNumber != numEdges) {
00243         DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges()",
00244                   "Edge counting error.");
00245       }
00246       return edges;
00247     }
00248 
00249 
00250     template <class EdgeFunctor>
00251     Array2D<UnsignedInt32>
00252     SegmenterFelzenszwalb<EdgeFunctor>::
00253     getLabelArray()
00254     {
00255       Array2D<UnsignedInt32> labelArray(
00256         m_imageSize.getRow(), m_imageSize.getColumn());
00257 
00258       numeric::Array1D<Segment>::iterator setIter = m_segmentation.begin();
00259       Array2D<UnsignedInt32>::iterator labelIter = labelArray.begin();
00260       while(setIter != m_segmentation.end()) {
00261         Segment& head = setIter->find();
00262 
00263         // Warning(xxx): Assuming we know something about how both
00264         // vectors and DisjointSets are implemented.
00265         UnsignedInt32 labelIndex = static_cast<UnsignedInt32>(
00266           &head - &(m_segmentation[0]));
00267         *labelIter = labelIndex;
00268 
00269         ++labelIter;
00270         ++setIter;
00271       }
00272 
00273       return labelArray;
00274     }
00275 
00276       
00277     template <class EdgeFunctor>
00278     Array2D<UnsignedInt32>
00279     SegmenterFelzenszwalb<EdgeFunctor>::
00280     getLabelArray(UnsignedInt32& numberOfSegments,
00281                   std::vector<size_t>& segmentSizes)
00282     {
00283       Array2D<UnsignedInt32> labelArray(
00284         m_imageSize.getRow(), m_imageSize.getColumn());
00285       std::vector<UnsignedInt32> labelMap(
00286         m_segmentation.size(), std::numeric_limits<UnsignedInt32>::max());
00287       UnsignedInt32 currentLabel = 0;
00288       segmentSizes.clear();
00289       
00290       // Iterate over each pixel.
00291       numeric::Array1D<Segment>::iterator setIter = m_segmentation.begin();
00292       Array2D<UnsignedInt32>::iterator labelIter = labelArray.begin();
00293       while(setIter != m_segmentation.end()) {
00294 
00295         // Figure out to which segment the current pixel belongs.
00296         // 
00297         // Warning(xxx): Assuming we know something about how both
00298         // vectors and DisjointSets are implemented.
00299         Segment& head = setIter->find();
00300         UnsignedInt32 labelIndex = static_cast<UnsignedInt32>(
00301           &head - &(m_segmentation[0]));
00302 
00303         // Have we labeled this segment yet?
00304         if(labelMap[labelIndex] > currentLabel) {
00305           // No.  Label it now and remember how big the segment is.
00306           labelMap[labelIndex] = currentLabel;
00307           segmentSizes.push_back(head.getSize());
00308           ++currentLabel;
00309         }
00310         // Record the label in our output label image.
00311         *labelIter = labelMap[labelIndex];
00312 
00313         // Move on to next pixel.
00314         ++labelIter;
00315         ++setIter;
00316       }
00317 
00318       numberOfSegments = currentLabel;
00319       return labelArray;
00320     }
00321 
00322 
00323     template <class EdgeFunctor>
00324     template <ImageFormat FORMAT>
00325     void
00326     SegmenterFelzenszwalb<EdgeFunctor>::
00327     segment(const Image<FORMAT>& inputImage)
00328     {
00329       m_imageSize.setValue(inputImage.rows(), inputImage.columns());
00330       
00331       // Smooth the image slightly to reduce artifacts.
00332       Image<GRAY_FLOAT32> smoothedImage;
00333       if(m_sigma == 0.0) {
00334         smoothedImage = convertColorspace<GRAY_FLOAT32>(inputImage);
00335       } else {
00336         Kernel<double> gaussian =
00337           getGaussianKernel<double>(m_smoothSize, m_smoothSize,
00338                                     m_sigma, m_sigma);
00339         smoothedImage = 
00340           filter2D<GRAY_FLOAT32, GRAY_FLOAT32>(
00341             gaussian, inputImage, Float32(0));
00342       }
00343 
00344       // Get a vector of the edges in the image, sorted in ascending
00345       // order.
00346       std::vector<Edge> edges = this->getEdges(smoothedImage);
00347 
00348       this->segmentFromEdges(smoothedImage.rows(), smoothedImage.columns(),
00349                              edges.begin(), edges.end());
00350     }
00351 
00352 
00353     template <class EdgeFunctor>
00354     template <class ITER>
00355     void
00356     SegmenterFelzenszwalb<EdgeFunctor>::
00357     segmentFromEdges(size_t imageRows, size_t imageColumns,
00358                      ITER edgeBegin, ITER edgeEnd)
00359     {
00360       size_t numPixels = imageRows * imageColumns;
00361       m_imageSize.setValue(imageRows, imageColumns);
00362       std::sort(edgeBegin, edgeEnd);
00363 
00364       // Start with segmentation S^0, where every vertex is its own component.
00365       typedef numeric::Array1D<Segment>::iterator SegmentIter;
00366       m_segmentation.reinit(numPixels);
00367       for(SegmentIter segmentIter = m_segmentation.begin();
00368           segmentIter != m_segmentation.end(); ++segmentIter) {
00369         segmentIter->setPayload(m_k);        
00370       }
00371       
00372       // Iteratively merge segments, as described in the paper.
00373       ITER edgeIter = edgeBegin;
00374       while(edgeIter != edgeEnd) {
00375         Segment& C_i = m_segmentation[edgeIter->end0].find();
00376         Segment& C_j = m_segmentation[edgeIter->end1].find();
00377         if(&C_i != &C_j) {
00378           float threshold = this->getCost(C_i, C_j);
00379           if(edgeIter->weight <= threshold) {
00380             C_i.merge(C_j);
00381             this->updateCost(C_i, edgeIter->weight);
00382           }
00383         }
00384         ++edgeIter;
00385       }
00386 
00387       // Merge any undersize segments, merging weak edges first.
00388       edgeIter = edgeBegin;
00389       while(edgeIter != edgeEnd) {
00390         Segment& C_i = m_segmentation[edgeIter->end0].find();
00391         Segment& C_j = m_segmentation[edgeIter->end1].find();
00392         if(C_i.getSize() < m_minimumSegmentSize
00393            || C_i.getSize() < m_minimumSegmentSize) {
00394           C_i.merge(C_j);
00395         }
00396         ++edgeIter;
00397       }
00398     }
00399         
00400     
00401     template <class EdgeFunctor>
00402     inline float
00403     SegmenterFelzenszwalb<EdgeFunctor>::
00404     getCost(const Segment& C_i, const Segment& C_j)
00405     {
00406       return std::min(C_i.getPayload(), C_j.getPayload());
00407     }
00408 
00409 
00410     template <class EdgeFunctor>
00411     template <ImageFormat FORMAT>
00412     inline void
00413     SegmenterFelzenszwalb<EdgeFunctor>::
00414     setEdge(Edge& edge, size_t index0, size_t index1,
00415             Image<FORMAT> inImage)
00416     {
00417       edge.end0 = index0;
00418       edge.end1 = index1;
00419       edge.weight = m_edgeFunctor(inImage, index0, index1);
00420     }
00421 
00422   
00423     template <class EdgeFunctor>
00424     inline void
00425     SegmenterFelzenszwalb<EdgeFunctor>::
00426     updateCost(Segment& C_i, float weight)
00427     {
00428       Segment& head = C_i.find();
00429       head.setPayload(weight + m_k / head.getSize());
00430     }
00431   
00432   } // namespace computerVision
00433 
00434 } // namespace dlr
00435 
00436 #endif /* #ifndef DLR_COMPUTERVISION_SEGMENTERFELZENSZWALB_H */

Generated on Tue Jan 6 23:24:56 2009 for dlrComputerVision Utility Library by  doxygen 1.5.6