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 
00086     template <class EdgeFunctor>
00087     class SegmenterFelzenszwalb {
00088     public:
00089 
00090       SegmenterFelzenszwalb(
00091         float k = 200.0f,
00092         float sigma = 0.8f,
00093         size_t minSegmentSize = 20,
00094         EdgeFunctor const& edgeFunctor = EdgeFunctor());
00095 
00096       
00097       virtual
00098       ~SegmenterFelzenszwalb() {}
00099 
00100 
00101       template <ImageFormat FORMAT>
00102       std::vector<Edge>
00103       getEdges(const Image<FORMAT>& inImage);
00104 
00105       
00106       template <ImageFormat FORMAT>
00107       std::vector<Edge>
00108       getEdges4Connected(const Image<FORMAT>& inImage);
00109 
00110 
00111       template <ImageFormat FORMAT>
00112       std::vector<Edge>
00113       getEdges8Connected(const Image<FORMAT>& inImage);
00114 
00115 
00116       virtual Array2D<UnsignedInt32>
00117       getLabelArray();
00118 
00119       
00120       virtual Array2D<UnsignedInt32>
00121       getLabelArray(UnsignedInt32& numberOfSegments,
00122                     std::vector<size_t>& segmentSizes);
00123 
00124 
00125       template <ImageFormat FORMAT>
00126       void
00127       segment(const Image<FORMAT>& inputImage);
00128 
00129 
00130       template <class ITER>
00131       void
00132       segmentFromEdges(size_t imageRows, size_t imageColumns,
00133                        ITER edgeBegin, ITER edgeEnd);
00134 
00135       
00136     protected:
00137 
00138       typedef privateCode::DisjointSet<float> Segment;
00139 
00140 
00141       inline float
00142       getCost(const Segment& C_i, const Segment& C_j);
00143 
00144 
00145       template <ImageFormat FORMAT>
00146       inline void
00147       setEdge(Edge& edge, size_t index0, size_t index1,
00148               Image<FORMAT> inImage);
00149 
00150       
00151       inline void
00152       updateCost(Segment& C_i, float weight);
00153 
00154 
00155       EdgeFunctor m_edgeFunctor;
00156       numeric::Index2D m_imageSize;
00157       float m_k;
00158       size_t m_minimumSegmentSize;
00159       numeric::Array1D<Segment> m_segmentation;
00160       float m_sigma;
00161       size_t m_smoothSize;
00162       
00163     };
00164 
00165 
00166     inline bool
00167     operator<(Edge const& arg0, Edge const& arg1) {
00168       return arg0.weight < arg1.weight;
00169     }
00170     
00171   } // namespace computerVision
00172   
00173 } // namespace dlr
00174 
00175 
00176 /* ============ Definitions of inline & template functions ============ */
00177 
00178 
00179 #include <cmath>
00180 #include <limits>
00181 
00182 namespace dlr {
00183 
00184   namespace computerVision {
00185 
00186     template <class EdgeFunctor>
00187     SegmenterFelzenszwalb<EdgeFunctor>::
00188     SegmenterFelzenszwalb(float k, float sigma, size_t minSegmentSize,
00189                           EdgeFunctor const& edgeFunctor)
00190       : m_edgeFunctor(edgeFunctor),
00191         m_imageSize(0, 0),
00192         m_k(k),
00193         m_minimumSegmentSize(minSegmentSize),
00194         m_segmentation(),
00195         m_sigma(sigma),
00196         m_smoothSize(static_cast<size_t>(std::fabs(6 * sigma + 1)))
00197     {
00198       // Smoothing kernel must not have even size or filter2D() will
00199       // complain later.
00200       if(m_smoothSize % 2 == 0) {
00201         m_smoothSize += 1;
00202       }
00203     }
00204 
00205     
00206     template <class EdgeFunctor>
00207     template <ImageFormat FORMAT>
00208     std::vector<Edge>
00209     SegmenterFelzenszwalb<EdgeFunctor>::
00210     getEdges(const Image<FORMAT>& inImage)
00211     {
00212       return this->getEdges8Connected(inImage);
00213     }
00214 
00215     
00216     template <class EdgeFunctor>
00217     template <ImageFormat FORMAT>
00218     std::vector<Edge>
00219     SegmenterFelzenszwalb<EdgeFunctor>::
00220     getEdges4Connected(const Image<FORMAT>& inImage)
00221     {
00222       // 4-connected means 2 undirected edges per pixel.
00223       size_t numEdges = 2 * inImage.size();
00224 
00225       // Except that some point off the side of the image.
00226       numEdges -= inImage.rows();
00227 
00228       // And some point off the bottom/top of the image.
00229       numEdges -= inImage.columns();
00230 
00231       std::vector<Edge> edges(numEdges);
00232       size_t interiorRows = inImage.rows() - 1;
00233       size_t interiorColumns = inImage.columns() - 1;
00234       size_t edgeNumber = 0;
00235       size_t pixelIndex0 = 0;
00236       size_t pixelIndex1;
00237       for(size_t row = 0; row < interiorRows; ++row) {
00238         // Get the first pixel of the row and the interior pixels of the row.
00239         for(size_t column = 0; column < interiorColumns; ++column) {
00240           pixelIndex1 = pixelIndex0 + 1;
00241           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00242 
00243           pixelIndex1 += (inImage.columns() - 1);
00244           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00245           ++pixelIndex0;
00246         }
00247 
00248         // Get the last pixel of the row.
00249         pixelIndex1 = pixelIndex0 + inImage.columns();
00250         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00251         ++pixelIndex0;
00252       }
00253 
00254       // Sanity check.
00255       if(pixelIndex0 != (inImage.rows() - 1) * inImage.columns()) {
00256         DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges4Connected()",
00257                   "Indexing error.");
00258       }
00259     
00260       // Get the last row of pixels;
00261       for(size_t column = 0; column < interiorColumns; ++column) {
00262         pixelIndex1 = pixelIndex0 + 1;
00263         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00264         ++pixelIndex0;
00265       }
00266 
00267       // Sanity check.
00268       if(edgeNumber != numEdges) {
00269         DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges4Connected()",
00270                   "Edge counting error.");
00271       }
00272       return edges;
00273     }
00274 
00275 
00276     template <class EdgeFunctor>
00277     template <ImageFormat FORMAT>
00278     std::vector<Edge>
00279     SegmenterFelzenszwalb<EdgeFunctor>::
00280     getEdges8Connected(const Image<FORMAT>& inImage)
00281     {
00282       // 8-connected means 4 undirected edges per pixel.
00283       size_t numEdges = 4 * inImage.size();
00284 
00285       // Except that some point off the side of the image.
00286       numEdges -= 3 * inImage.rows();
00287 
00288       // And some point off the bottom/top of the image.
00289       numEdges -= 3 * inImage.columns();
00290 
00291       // Oops! We subtracted the bottom-right and bottom-left corners twice.
00292       numEdges += 2;
00293 
00294       std::vector<Edge> edges(numEdges);
00295       size_t interiorRows = inImage.rows() - 1;
00296       size_t interiorColumns = inImage.columns() - 1;
00297       size_t edgeNumber = 0;
00298       size_t pixelIndex0 = 0;
00299       size_t pixelIndex1;
00300       for(size_t row = 0; row < interiorRows; ++row) {
00301         // Get the first pixel of the row;
00302         pixelIndex1 = pixelIndex0 + 1;
00303         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00304       
00305         pixelIndex1 += inImage.columns();
00306         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00307 
00308         pixelIndex1 -= 1;
00309         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00310         ++pixelIndex0;
00311       
00312         // Get the interior pixels of the row.
00313         for(size_t column = 1; column < interiorColumns; ++column) {
00314           pixelIndex1 = pixelIndex0 + 1;
00315           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00316 
00317           pixelIndex1 += inImage.columns();
00318           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00319 
00320           pixelIndex1 -= 1;
00321           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00322 
00323           pixelIndex1 -= 1;
00324           this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00325           ++pixelIndex0;
00326         }
00327 
00328         // Get the last pixel of the row.
00329         pixelIndex1 = pixelIndex0 + inImage.columns();
00330         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00331 
00332         pixelIndex1 -= 1;
00333         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00334         ++pixelIndex0;
00335       }
00336 
00337       // Sanity check.
00338       if(pixelIndex0 != (inImage.rows() - 1) * inImage.columns()) {
00339         DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges8Connected()",
00340                   "Indexing error.");
00341       }
00342     
00343       // Get the last row of pixels;
00344       for(size_t column = 0; column < interiorColumns; ++column) {
00345         pixelIndex1 = pixelIndex0 + 1;
00346         this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00347         ++pixelIndex0;
00348       }
00349 
00350       // Sanity check.
00351       if(edgeNumber != numEdges) {
00352         DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges8Connected()",
00353                   "Edge counting error.");
00354       }
00355       return edges;
00356     }
00357 
00358 
00359     template <class EdgeFunctor>
00360     Array2D<UnsignedInt32>
00361     SegmenterFelzenszwalb<EdgeFunctor>::
00362     getLabelArray()
00363     {
00364       Array2D<UnsignedInt32> labelArray(
00365         m_imageSize.getRow(), m_imageSize.getColumn());
00366 
00367       numeric::Array1D<Segment>::iterator setIter = m_segmentation.begin();
00368       Array2D<UnsignedInt32>::iterator labelIter = labelArray.begin();
00369       while(setIter != m_segmentation.end()) {
00370         Segment& head = setIter->find();
00371 
00372         // Warning(xxx): Assuming we know something about how both
00373         // vectors and DisjointSets are implemented.
00374         UnsignedInt32 labelIndex = static_cast<UnsignedInt32>(
00375           &head - &(m_segmentation[0]));
00376         *labelIter = labelIndex;
00377 
00378         ++labelIter;
00379         ++setIter;
00380       }
00381 
00382       return labelArray;
00383     }
00384 
00385       
00386     template <class EdgeFunctor>
00387     Array2D<UnsignedInt32>
00388     SegmenterFelzenszwalb<EdgeFunctor>::
00389     getLabelArray(UnsignedInt32& numberOfSegments,
00390                   std::vector<size_t>& segmentSizes)
00391     {
00392       Array2D<UnsignedInt32> labelArray(
00393         m_imageSize.getRow(), m_imageSize.getColumn());
00394       std::vector<UnsignedInt32> labelMap(
00395         m_segmentation.size(), std::numeric_limits<UnsignedInt32>::max());
00396       UnsignedInt32 currentLabel = 0;
00397       segmentSizes.clear();
00398       
00399       // Iterate over each pixel.
00400       numeric::Array1D<Segment>::iterator setIter = m_segmentation.begin();
00401       Array2D<UnsignedInt32>::iterator labelIter = labelArray.begin();
00402       while(setIter != m_segmentation.end()) {
00403 
00404         // Figure out to which segment the current pixel belongs.
00405         // 
00406         // Warning(xxx): Assuming we know something about how both
00407         // vectors and DisjointSets are implemented.
00408         Segment& head = setIter->find();
00409         UnsignedInt32 labelIndex = static_cast<UnsignedInt32>(
00410           &head - &(m_segmentation[0]));
00411 
00412         // Have we labeled this segment yet?
00413         if(labelMap[labelIndex] > currentLabel) {
00414           // No.  Label it now and remember how big the segment is.
00415           labelMap[labelIndex] = currentLabel;
00416           segmentSizes.push_back(head.getSize());
00417           ++currentLabel;
00418         }
00419         // Record the label in our output label image.
00420         *labelIter = labelMap[labelIndex];
00421 
00422         // Move on to next pixel.
00423         ++labelIter;
00424         ++setIter;
00425       }
00426 
00427       numberOfSegments = currentLabel;
00428       return labelArray;
00429     }
00430 
00431 
00432     template <class EdgeFunctor>
00433     template <ImageFormat FORMAT>
00434     void
00435     SegmenterFelzenszwalb<EdgeFunctor>::
00436     segment(const Image<FORMAT>& inputImage)
00437     {
00438       m_imageSize.setValue(inputImage.rows(), inputImage.columns());
00439       
00440       // Smooth the image slightly to reduce artifacts.
00441       Image<GRAY_FLOAT32> smoothedImage;
00442       if(m_sigma == 0.0) {
00443         smoothedImage = convertColorspace<GRAY_FLOAT32>(inputImage);
00444       } else {
00445         Kernel<double> gaussian =
00446           getGaussianKernel<double>(m_smoothSize, m_smoothSize,
00447                                     m_sigma, m_sigma);
00448         smoothedImage = 
00449           filter2D<GRAY_FLOAT32, GRAY_FLOAT32>(
00450             gaussian, inputImage, Float32(0));
00451       }
00452 
00453       // Get a vector of the edges in the image, sorted in ascending
00454       // order.
00455       std::vector<Edge> edges = this->getEdges(smoothedImage);
00456 
00457       this->segmentFromEdges(smoothedImage.rows(), smoothedImage.columns(),
00458                              edges.begin(), edges.end());
00459     }
00460 
00461 
00462     template <class EdgeFunctor>
00463     template <class ITER>
00464     void
00465     SegmenterFelzenszwalb<EdgeFunctor>::
00466     segmentFromEdges(size_t imageRows, size_t imageColumns,
00467                      ITER edgeBegin, ITER edgeEnd)
00468     {
00469       size_t numPixels = imageRows * imageColumns;
00470       m_imageSize.setValue(imageRows, imageColumns);
00471       std::sort(edgeBegin, edgeEnd);
00472 
00473       // Start with segmentation S^0, where every vertex is its own component.
00474       typedef numeric::Array1D<Segment>::iterator SegmentIter;
00475       m_segmentation.reinit(numPixels);
00476       for(SegmentIter segmentIter = m_segmentation.begin();
00477           segmentIter != m_segmentation.end(); ++segmentIter) {
00478         segmentIter->setPayload(m_k);        
00479       }
00480       
00481       // Iteratively merge segments, as described in the paper.
00482       ITER edgeIter = edgeBegin;
00483       while(edgeIter != edgeEnd) {
00484         Segment& C_i = m_segmentation[edgeIter->end0].find();
00485         Segment& C_j = m_segmentation[edgeIter->end1].find();
00486         if(&C_i != &C_j) {
00487           float threshold = this->getCost(C_i, C_j);
00488           if(edgeIter->weight <= threshold) {
00489             C_i.merge(C_j);
00490             this->updateCost(C_i, edgeIter->weight);
00491           }
00492         }
00493         ++edgeIter;
00494       }
00495 
00496       // Merge any undersize segments, merging weak edges first.
00497       edgeIter = edgeBegin;
00498       while(edgeIter != edgeEnd) {
00499         Segment& C_i = m_segmentation[edgeIter->end0].find();
00500         Segment& C_j = m_segmentation[edgeIter->end1].find();
00501         if(C_i.getSize() < m_minimumSegmentSize
00502            || C_i.getSize() < m_minimumSegmentSize) {
00503           C_i.merge(C_j);
00504         }
00505         ++edgeIter;
00506       }
00507     }
00508         
00509     
00510     template <class EdgeFunctor>
00511     inline float
00512     SegmenterFelzenszwalb<EdgeFunctor>::
00513     getCost(const Segment& C_i, const Segment& C_j)
00514     {
00515       return std::min(C_i.getPayload(), C_j.getPayload());
00516     }
00517 
00518 
00519     template <class EdgeFunctor>
00520     template <ImageFormat FORMAT>
00521     inline void
00522     SegmenterFelzenszwalb<EdgeFunctor>::
00523     setEdge(Edge& edge, size_t index0, size_t index1,
00524             Image<FORMAT> inImage)
00525     {
00526       edge.end0 = index0;
00527       edge.end1 = index1;
00528       edge.weight = m_edgeFunctor(inImage, index0, index1);
00529     }
00530 
00531   
00532     template <class EdgeFunctor>
00533     inline void
00534     SegmenterFelzenszwalb<EdgeFunctor>::
00535     updateCost(Segment& C_i, float weight)
00536     {
00537       Segment& head = C_i.find();
00538       head.setPayload(weight + m_k / head.getSize());
00539     }
00540   
00541   } // namespace computerVision
00542 
00543 } // namespace dlr
00544 
00545 #endif /* #ifndef DLR_COMPUTERVISION_SEGMENTERFELZENSZWALB_H */

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