00001 /* 00002 Segmentation of simply connected spaces in 2D data. 00003 00004 Copyright (C) 2010 - 2014 Christoph Haenisch 00005 00006 Chair of Medical Engineering (mediTEC) 00007 RWTH Aachen University 00008 Pauwelsstr. 20 00009 52074 Aachen 00010 Germany 00011 00012 Version 0.3.0 (2012-07-05) 00013 */ 00014 00020 #ifndef REGION_GROWING_2D_1376134334 00021 #define REGION_GROWING_2D_1376134334 00022 00023 #include <cmath> 00024 #include <cstddef> 00025 #include <deque> 00026 #include <list> 00027 #include <queue> 00028 00029 #include "Coordinate.hpp" 00030 00031 00032 namespace TRTK 00033 { 00034 00035 00184 template <class BinaryDataType, class LabelType = unsigned short> 00185 class RegionGrowing2D 00186 { 00187 public: 00188 typedef BinaryDataType binary_value_type; 00189 typedef LabelType label_type; 00190 00191 typedef Coordinate<unsigned> Point; 00192 typedef std::deque<Point> Region; 00193 typedef std::deque<Region> Regions; 00194 00195 RegionGrowing2D(); 00196 RegionGrowing2D(const BinaryDataType * data, const unsigned width, const unsigned height); 00197 virtual ~RegionGrowing2D(); 00198 00199 void setData(const BinaryDataType * data, const unsigned width, const unsigned height); 00200 void setNeighborhoodSize(const unsigned size); 00201 00202 void compute(); 00203 template <int StrideX, int StrideY> void compute(); 00204 void compute(const Point & seed_point); 00205 void compute(const std::deque<Point> & seed_points); 00206 00207 const Regions & getRegions() const; 00208 const LabelType * getLabelMask() const; 00209 00210 private: 00211 00212 unsigned getIndexFromPoint(const Point & point) const; 00213 std::list<Point> getNeighbors(const Point & point, const unsigned size = 2) const; 00214 Point getPointFromIndex(const unsigned index) const; 00215 void initLabelMask(); 00216 00217 unsigned height; 00218 unsigned width; 00219 unsigned neighborhood_size; 00220 const BinaryDataType * data; 00221 LabelType * label_mask; 00222 Regions regions; 00223 }; 00224 00225 00228 template <class BinaryDataType, class LabelType> 00229 RegionGrowing2D<BinaryDataType, LabelType>::RegionGrowing2D() : 00230 height(0), 00231 width(0), 00232 neighborhood_size(2), 00233 data(NULL), 00234 label_mask(NULL) 00235 { 00236 } 00237 00238 00246 template <class BinaryDataType, class LabelType> 00247 RegionGrowing2D<BinaryDataType, LabelType>::RegionGrowing2D(const BinaryDataType * data, const unsigned width, const unsigned height) : 00248 height(height), 00249 width(width), 00250 neighborhood_size(2), 00251 data(data) 00252 { 00253 label_mask = new LabelType[width * height]; 00254 initLabelMask(); 00255 } 00256 00257 00260 template <class BinaryDataType, class LabelType> 00261 RegionGrowing2D<BinaryDataType, LabelType>::~RegionGrowing2D() 00262 { 00263 if (label_mask) delete[] label_mask; 00264 } 00265 00266 00277 template <class BinaryDataType, class LabelType> 00278 void RegionGrowing2D<BinaryDataType, LabelType>::compute() 00279 { 00280 // Note: We use Point(x, y) and index = (y * width + x) interchangeably. 00281 // Conversions are done with getPointFromIndex() and getIndexFromPoint(). 00282 00283 if (data == NULL) return; 00284 00285 regions.clear(); 00286 initLabelMask(); 00287 00288 LabelType unique_label = 1; 00289 00290 // For each point in the binary image, do... 00291 00292 for (unsigned index = 0; index < width * height; ++index) 00293 { 00294 // Skip the point if it is not set, i.e., if its value is zero. 00295 00296 if (!data[index]) 00297 { 00298 continue; 00299 } 00300 else 00301 { 00302 // If the point has a label it was already assigned to a region and 00303 // thus skip it. Otherwise, this point is part of a new region. Hence, 00304 // get a new unique region label and add the current point as well as 00305 // its neighbors to this new region. To be able to process all points 00306 // and to add new points at the same time, a queue is used (FIFO). 00307 00308 if (label_mask[index]) 00309 { 00310 continue; 00311 } 00312 00313 LabelType label = unique_label++; 00314 regions.push_back(Region()); 00315 00316 std::queue<Point> queue; 00317 queue.push(getPointFromIndex(index)); 00318 00319 while (!queue.empty()) 00320 { 00321 Point point = queue.front(); 00322 queue.pop(); 00323 00324 // Assign the label of the current region to the current point. 00325 00326 label_mask[getIndexFromPoint(point)] = label; 00327 00328 // Save the point in a global list which only contains points of a certain region. 00329 00330 regions[label - 1].push_back(point); 00331 00332 // Now, add those points of the neighborhood to the queue which are 00333 // set within the binary mask but still don't carry any label. Then 00334 // set the label of these points to the current region. As a result, 00335 // these points won't be selected a second time. 00336 00337 std::list<Point> neighbors = getNeighbors(point, neighborhood_size); 00338 00339 for(std::list<Point>::iterator neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) 00340 { 00341 const unsigned index = getIndexFromPoint(*neighbor); 00342 if (data[index] && !label_mask[index]) 00343 { 00344 label_mask[index] = label; 00345 queue.push(*neighbor); 00346 } 00347 } 00348 } 00349 } 00350 } 00351 } 00352 00353 00372 template <class BinaryDataType, class LabelType> 00373 template <int StrideX, int StrideY> 00374 void RegionGrowing2D<BinaryDataType, LabelType>::compute() 00375 { 00376 // Note: We use Point(x, y) and index = (y * width + x) interchangeably. 00377 // Conversions are done with getPointFromIndex() and getIndexFromPoint(). 00378 00379 if (data == NULL) return; 00380 00381 regions.clear(); 00382 initLabelMask(); 00383 00384 LabelType unique_label = 1; 00385 00386 // For each point in the binary image, do... 00387 00388 for (unsigned y = 0; y < height; y += StrideY) 00389 { 00390 for (unsigned x = 0; x < width; x += StrideX) 00391 { 00392 unsigned index = y * width + x; 00393 00394 // Skip the point if it is not set, i.e., if its value is zero. 00395 00396 if (!data[index]) 00397 { 00398 continue; 00399 } 00400 else 00401 { 00402 // If the point has a label it was already assigned to a region and 00403 // thus skip it. Otherwise, this point is part of a new region. Hence, 00404 // get a new unique region label and add the current point as well as 00405 // its neighbors to this new region. To be able to process all points 00406 // and to add new points at the same time, a queue is used (FIFO). 00407 00408 if (label_mask[index]) 00409 { 00410 continue; 00411 } 00412 00413 LabelType label = unique_label++; 00414 regions.push_back(Region()); 00415 00416 std::queue<Point> queue; 00417 queue.push(getPointFromIndex(index)); 00418 00419 while (!queue.empty()) 00420 { 00421 Point point = queue.front(); 00422 queue.pop(); 00423 00424 // Assign the label of the current region to the current point. 00425 00426 label_mask[getIndexFromPoint(point)] = label; 00427 00428 // Save the point in a global list which only contains points of a certain region. 00429 00430 regions[label - 1].push_back(point); 00431 00432 // Now, add those points of the neighborhood to the queue which are 00433 // set within the binary mask but still don't carry any label. Then 00434 // set the label of these points to the current region. As a result, 00435 // these points won't be selected a second time. 00436 00437 std::list<Point> neighbors = getNeighbors(point, neighborhood_size); 00438 00439 for(std::list<Point>::iterator neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) 00440 { 00441 const unsigned index = getIndexFromPoint(*neighbor); 00442 if (data[index] && !label_mask[index]) 00443 { 00444 label_mask[index] = label; 00445 queue.push(*neighbor); 00446 } 00447 } 00448 } 00449 } 00450 } 00451 } 00452 } 00453 00454 00466 template <class BinaryDataType, class LabelType> 00467 void RegionGrowing2D<BinaryDataType, LabelType>::compute(const Point & seed_point) 00468 { 00469 std::deque<Point> seed_points; 00470 seed_points.push_back(seed_point); 00471 compute(seed_points); 00472 } 00473 00474 00488 template <class BinaryDataType, class LabelType> 00489 void RegionGrowing2D<BinaryDataType, LabelType>::compute(const std::deque<Point> & seed_points) 00490 { 00491 // Note: We use Point(x, y) and index = (y * width + x) interchangeably. 00492 // Conversions are done with getPointFromIndex() and getIndexFromPoint(). 00493 00494 if (data == NULL) return; 00495 00496 regions.clear(); 00497 initLabelMask(); 00498 00499 LabelType unique_label = 1; 00500 00501 // For each seed point do... 00502 00503 for (unsigned i = 0; i < seed_points.size(); ++i) 00504 { 00505 unsigned index = getIndexFromPoint(seed_points[i]); 00506 00507 // Skip the point if it is not set, i.e., if its value is zero. 00508 00509 if (!data[index]) 00510 { 00511 continue; 00512 } 00513 else 00514 { 00515 // If the point has a label it was already assigned to a region and 00516 // thus skip it. Otherwise, this point is part of a new region. Hence, 00517 // get a new unique region label and add the current point as well as 00518 // its neighbors to this new region. To be able to process all points 00519 // and to add new points at the same time, a queue is used (FIFO). 00520 00521 if (label_mask[index]) 00522 { 00523 continue; 00524 } 00525 00526 LabelType label = unique_label++; 00527 regions.push_back(Region()); 00528 00529 std::queue<Point> queue; 00530 queue.push(getPointFromIndex(index)); 00531 00532 00533 while (!queue.empty()) 00534 { 00535 Point point = queue.front(); 00536 queue.pop(); 00537 00538 // Assign the label of the current region to the current point. 00539 00540 label_mask[getIndexFromPoint(point)] = label; 00541 00542 // Save the point in a global list which only contains points of a certain region. 00543 00544 regions[label - 1].push_back(point); 00545 00546 // Now, add those points of the neighborhood to the queue which are 00547 // set within the binary mask but still don't carry any label. Then 00548 // set the label of these points to the current region. As a result, 00549 // these points won't be selected a second time. 00550 00551 std::list<Point> neighbors = getNeighbors(point, neighborhood_size); 00552 00553 for(std::list<Point>::iterator neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) 00554 { 00555 const unsigned index = getIndexFromPoint(*neighbor); 00556 if (data[index] && !label_mask[index]) 00557 { 00558 label_mask[index] = label; 00559 queue.push(*neighbor); 00560 } 00561 } 00562 } 00563 } 00564 } 00565 } 00566 00567 00576 template <class BinaryDataType, class LabelType> 00577 inline unsigned RegionGrowing2D<BinaryDataType, LabelType>::getIndexFromPoint(const Point & point) const 00578 { 00579 return point.y() * width + point.x(); 00580 } 00581 00582 00587 template <class BinaryDataType, class LabelType> 00588 const LabelType * RegionGrowing2D<BinaryDataType, LabelType>::getLabelMask() const 00589 { 00590 return label_mask; 00591 } 00592 00593 00603 template <class BinaryDataType, class LabelType> 00604 std::list<typename RegionGrowing2D<BinaryDataType, LabelType>::Point> RegionGrowing2D<BinaryDataType, LabelType>::getNeighbors(const Point & point, const unsigned size) const 00605 { 00606 // Typical values for size are 0, 1, 2, 4, 8. 00607 00608 // Return a list with all adjacent neighbors. Also check, 00609 // whether a neighbor is still within the data. 00610 00611 std::list<Point> neighbors; 00612 00613 const int delta = size == 2 ? 1 : int(std::ceil(std::sqrt(float(size)))); 00614 00615 for (int delta_x = -delta; delta_x <= delta; ++delta_x) 00616 { 00617 for (int delta_y = -delta; delta_y <= delta; ++delta_y) 00618 { 00619 if (unsigned(delta_x * delta_x + delta_y * delta_y) <= size) 00620 { 00621 int x = point.x() + delta_x; 00622 int y = point.y() + delta_y; 00623 00624 if (0 <= x && x < int(width) && 0 <= y && y < int(height)) 00625 { 00626 neighbors.push_back(Point(x, y)); 00627 } 00628 } 00629 } 00630 } 00631 00632 return neighbors; 00633 } 00634 00635 00644 template <class BinaryDataType, class LabelType> 00645 inline typename RegionGrowing2D<BinaryDataType, LabelType>::Point RegionGrowing2D<BinaryDataType, LabelType>::getPointFromIndex(const unsigned index) const 00646 { 00647 const Point::value_type x = index % width; 00648 const Point::value_type y = index / width; 00649 00650 return Point(x, y); 00651 } 00652 00653 00658 template <class BinaryDataType, class LabelType> 00659 inline const typename RegionGrowing2D<BinaryDataType, LabelType>::Regions & RegionGrowing2D<BinaryDataType, LabelType>::getRegions() const 00660 { 00661 return regions; 00662 } 00663 00664 00670 template <class BinaryDataType, class LabelType> 00671 void RegionGrowing2D<BinaryDataType, LabelType>::initLabelMask() 00672 { 00673 for (unsigned i = 0; i < width * height; ++i) 00674 { 00675 label_mask[i] = LabelType(0); 00676 } 00677 } 00678 00679 00687 template <class BinaryDataType, class LabelType> 00688 void RegionGrowing2D<BinaryDataType, LabelType>::setData(const BinaryDataType * data, const unsigned width, const unsigned height) 00689 { 00690 this->data = data; 00691 this->width = width; 00692 this->height = height; 00693 00694 if (label_mask) delete[] label_mask; 00695 00696 label_mask = new LabelType[width * height]; 00697 initLabelMask(); 00698 } 00699 00700 00711 template <class BinaryDataType, class LabelType> 00712 void RegionGrowing2D<BinaryDataType, LabelType>::setNeighborhoodSize(const unsigned size) 00713 { 00714 neighborhood_size = size; 00715 } 00716 00717 00718 } // namespace TRTK 00719 00720 #endif // REGION_GROWING_2D_1376134334
Documentation generated by Doxygen