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 }
00172
00173 }
00174
00175
00176
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
00199
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
00223 size_t numEdges = 2 * inImage.size();
00224
00225
00226 numEdges -= inImage.rows();
00227
00228
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
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
00249 pixelIndex1 = pixelIndex0 + inImage.columns();
00250 this->setEdge(edges[edgeNumber++], pixelIndex0, pixelIndex1, inImage);
00251 ++pixelIndex0;
00252 }
00253
00254
00255 if(pixelIndex0 != (inImage.rows() - 1) * inImage.columns()) {
00256 DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges4Connected()",
00257 "Indexing error.");
00258 }
00259
00260
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
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
00283 size_t numEdges = 4 * inImage.size();
00284
00285
00286 numEdges -= 3 * inImage.rows();
00287
00288
00289 numEdges -= 3 * inImage.columns();
00290
00291
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
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
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
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
00338 if(pixelIndex0 != (inImage.rows() - 1) * inImage.columns()) {
00339 DLR_THROW(LogicException, "SegmenterFelzenszwalb::getEdges8Connected()",
00340 "Indexing error.");
00341 }
00342
00343
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
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
00373
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
00400 numeric::Array1D<Segment>::iterator setIter = m_segmentation.begin();
00401 Array2D<UnsignedInt32>::iterator labelIter = labelArray.begin();
00402 while(setIter != m_segmentation.end()) {
00403
00404
00405
00406
00407
00408 Segment& head = setIter->find();
00409 UnsignedInt32 labelIndex = static_cast<UnsignedInt32>(
00410 &head - &(m_segmentation[0]));
00411
00412
00413 if(labelMap[labelIndex] > currentLabel) {
00414
00415 labelMap[labelIndex] = currentLabel;
00416 segmentSizes.push_back(head.getSize());
00417 ++currentLabel;
00418 }
00419
00420 *labelIter = labelMap[labelIndex];
00421
00422
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
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
00454
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
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
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
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 }
00542
00543 }
00544
00545 #endif