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 }
00133
00134 }
00135
00136
00137
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
00160
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
00174 size_t numEdges = 4 * inImage.size();
00175
00176
00177 numEdges -= 3 * inImage.rows();
00178
00179
00180 numEdges -= 3 * inImage.columns();
00181
00182
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
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
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
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
00229 if(pixelIndex0 != (inImage.rows() - 1) * inImage.columns()) {
00230 DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges()",
00231 "Indexing error.");
00232 }
00233
00234
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
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
00264
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
00291 numeric::Array1D<Segment>::iterator setIter = m_segmentation.begin();
00292 Array2D<UnsignedInt32>::iterator labelIter = labelArray.begin();
00293 while(setIter != m_segmentation.end()) {
00294
00295
00296
00297
00298
00299 Segment& head = setIter->find();
00300 UnsignedInt32 labelIndex = static_cast<UnsignedInt32>(
00301 &head - &(m_segmentation[0]));
00302
00303
00304 if(labelMap[labelIndex] > currentLabel) {
00305
00306 labelMap[labelIndex] = currentLabel;
00307 segmentSizes.push_back(head.getSize());
00308 ++currentLabel;
00309 }
00310
00311 *labelIter = labelMap[labelIndex];
00312
00313
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
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
00345
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
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
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
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 }
00433
00434 }
00435
00436 #endif