00001 /* 00002 Segmentation of simply connected spaces in 3D 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.4.0 (2012-07-05) 00013 */ 00014 00020 #ifndef REGION_GROWING_3D_1234689743 00021 #define REGION_GROWING_3D_1234689743 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 00203 template <class BinaryDataType, class LabelType = unsigned short, class BinaryFieldType = const BinaryDataType *> 00204 class RegionGrowing3D 00205 { 00206 public: 00207 typedef BinaryDataType binary_value_type; 00208 typedef LabelType label_type; 00209 00210 typedef Coordinate<unsigned> Point; 00211 typedef std::deque<Point> Region; 00212 typedef std::deque<Region> Regions; 00213 00214 RegionGrowing3D(); 00215 RegionGrowing3D(BinaryFieldType data, const unsigned width, const unsigned height, const unsigned depth); 00216 virtual ~RegionGrowing3D(); 00217 00218 void setData(BinaryFieldType data, const unsigned width, const unsigned height, const unsigned depth); 00219 void setNeighborhoodSize(const unsigned size); 00220 00221 template <int StrideX, int StrideY, int StrideZ> void compute(); 00222 void compute(); 00223 void compute(const Point & seed_point); 00224 void compute(const std::deque<Point> & seed_points); 00225 00226 const Regions & getRegions() const; 00227 const LabelType * getLabelMask() const; 00228 00229 private: 00230 00231 unsigned getIndexFromPoint(const Point & point) const; 00232 std::list<Point> getNeighbors(const Point & point, const unsigned size = 2) const; 00233 Point getPointFromIndex(const unsigned index) const; 00234 void initLabelMask(); 00235 00236 unsigned height; 00237 unsigned width; 00238 unsigned depth; 00239 unsigned neighborhood_size; 00240 00241 bool data_available; 00242 BinaryFieldType data; 00243 LabelType * label_mask; 00244 Regions regions; 00245 }; 00246 00247 00250 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00251 RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::RegionGrowing3D() : 00252 height(0), 00253 width(0), 00254 depth(0), 00255 neighborhood_size(2), 00256 data_available(false), 00257 label_mask(NULL) 00258 { 00259 } 00260 00261 00270 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00271 RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::RegionGrowing3D(BinaryFieldType data, const unsigned width, const unsigned height, const unsigned depth) : 00272 height(height), 00273 width(width), 00274 depth(depth), 00275 neighborhood_size(2), 00276 data_available(true), 00277 data(data) 00278 { 00279 label_mask = new LabelType[width * height * depth]; 00280 initLabelMask(); 00281 } 00282 00283 00286 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00287 RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::~RegionGrowing3D() 00288 { 00289 if (label_mask) delete[] label_mask; 00290 } 00291 00292 00303 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00304 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::compute() 00305 { 00306 // Note: We use Point(x, y, z) and index = (z * width * height + y * width + x) 00307 // interchangeably. Conversions are done with getPointFromIndex() and 00308 // getIndexFromPoint(). 00309 00310 if (!data_available) return; 00311 00312 regions.clear(); 00313 initLabelMask(); 00314 00315 LabelType unique_label = 1; 00316 00317 // For each point in the binary image, do... 00318 00319 for (unsigned index = 0; index < width * height * depth; ++index) 00320 { 00321 // Skip the point if it is not set, i.e., if its value is zero. 00322 00323 if (!data[index]) 00324 { 00325 continue; 00326 } 00327 else 00328 { 00329 // If the point has a label it was already assigned to a region and 00330 // thus skip it. Otherwise, this point is part of a new region. Hence, 00331 // get a new unique region label and add the current point as well as 00332 // its neighbors to this new region. To be able to process all points 00333 // and to add new points at the same time, a queue is used (FIFO). 00334 00335 if (label_mask[index]) 00336 { 00337 continue; 00338 } 00339 00340 LabelType label = unique_label++; 00341 regions.push_back(Region()); 00342 00343 std::queue<Point> queue; 00344 queue.push(getPointFromIndex(index)); 00345 00346 while (!queue.empty()) 00347 { 00348 Point point = queue.front(); 00349 queue.pop(); 00350 00351 // Assign the label of the current region to the current point. 00352 00353 label_mask[getIndexFromPoint(point)] = label; 00354 00355 // Save the point in a global list which only contains points of a certain region. 00356 00357 regions[label - 1].push_back(point); 00358 00359 // Now, add those points of the neighborhood to the queue which are 00360 // set within the binary mask but still don't carry any label. Then 00361 // set the label of these points to the current region. As a result, 00362 // these points won't be selected a second time. 00363 00364 std::list<Point> neighbors = getNeighbors(point, neighborhood_size); 00365 00366 for(std::list<Point>::iterator neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) 00367 { 00368 const unsigned index = getIndexFromPoint(*neighbor); 00369 if (data[index] && !label_mask[index]) 00370 { 00371 label_mask[index] = label; 00372 queue.push(*neighbor); 00373 } 00374 } 00375 } 00376 } 00377 } 00378 } 00379 00380 00400 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00401 template <int StrideX, int StrideY, int StrideZ> 00402 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::compute() 00403 { 00404 // Note: We use Point(x, y, z) and index = (z * width * height + y * width + x) 00405 // interchangeably. Conversions are done with getPointFromIndex() and 00406 // getIndexFromPoint(). 00407 00408 if (!data_available) return; 00409 00410 regions.clear(); 00411 initLabelMask(); 00412 00413 LabelType unique_label = 1; 00414 00415 // For each point in the binary image, do... 00416 00417 for (unsigned z = 0; z < depth; z += StrideZ) 00418 { 00419 for (unsigned y = 0; y < height; y += StrideY) 00420 { 00421 for (unsigned x = 0; x < width; x += StrideX) 00422 { 00423 unsigned index = z * width * height + y * width + x; 00424 00425 // Skip the point if it is not set, i.e., if its value is zero. 00426 00427 if (!data[index]) 00428 { 00429 continue; 00430 } 00431 else 00432 { 00433 // If the point has a label it was already assigned to a region and 00434 // thus skip it. Otherwise, this point is part of a new region. Hence, 00435 // get a new unique region label and add the current point as well as 00436 // its neighbors to this new region. To be able to process all points 00437 // and to add new points at the same time, a queue is used (FIFO). 00438 00439 if (label_mask[index]) 00440 { 00441 continue; 00442 } 00443 00444 LabelType label = unique_label++; 00445 regions.push_back(Region()); 00446 00447 std::queue<Point> queue; 00448 queue.push(getPointFromIndex(index)); 00449 00450 while (!queue.empty()) 00451 { 00452 Point point = queue.front(); 00453 queue.pop(); 00454 00455 // Assign the label of the current region to the current point. 00456 00457 label_mask[getIndexFromPoint(point)] = label; 00458 00459 // Save the point in a global list which only contains points of a certain region. 00460 00461 regions[label - 1].push_back(point); 00462 00463 // Now, add those points of the neighborhood to the queue which are 00464 // set within the binary mask but still don't carry any label. Then 00465 // set the label of these points to the current region. As a result, 00466 // these points won't be selected a second time. 00467 00468 std::list<Point> neighbors = getNeighbors(point, neighborhood_size); 00469 00470 for(std::list<Point>::iterator neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) 00471 { 00472 const unsigned index = getIndexFromPoint(*neighbor); 00473 if (data[index] && !label_mask[index]) 00474 { 00475 label_mask[index] = label; 00476 queue.push(*neighbor); 00477 } 00478 } 00479 } 00480 } 00481 } 00482 } 00483 } 00484 } 00485 00486 00498 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00499 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::compute(const Point & seed_point) 00500 { 00501 std::deque<Point> seed_points; 00502 seed_points.push_back(seed_point); 00503 compute(seed_points); 00504 } 00505 00506 00520 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00521 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::compute(const std::deque<Point> & seed_points) 00522 { 00523 // Note: We use Point(x, y, z) and index = (z * width * height + y * width + x) 00524 // interchangeably. Conversions are done with getPointFromIndex() and 00525 // getIndexFromPoint(). 00526 00527 if (!data_available) return; 00528 00529 regions.clear(); 00530 initLabelMask(); 00531 00532 LabelType unique_label = 1; 00533 00534 // For each point in the binary image, do... 00535 00536 for (unsigned i = 0; i < seed_points.size(); ++i) 00537 { 00538 unsigned index = getIndexFromPoint(seed_points[i]); 00539 00540 // Skip the point if it is not set, i.e., if its value is zero. 00541 00542 if (!data[index]) 00543 { 00544 continue; 00545 } 00546 else 00547 { 00548 // If the point has a label it was already assigned to a region and 00549 // thus skip it. Otherwise, this point is part of a new region. Hence, 00550 // get a new unique region label and add the current point as well as 00551 // its neighbors to this new region. To be able to process all points 00552 // and to add new points at the same time, a queue is used (FIFO). 00553 00554 if (label_mask[index]) 00555 { 00556 continue; 00557 } 00558 00559 LabelType label = unique_label++; 00560 regions.push_back(Region()); 00561 00562 std::queue<Point> queue; 00563 queue.push(getPointFromIndex(index)); 00564 00565 00566 while (!queue.empty()) 00567 { 00568 Point point = queue.front(); 00569 queue.pop(); 00570 00571 // Assign the label of the current region to the current point. 00572 00573 label_mask[getIndexFromPoint(point)] = label; 00574 00575 // Save the point in a global list which only contains points of a certain region. 00576 00577 regions[label - 1].push_back(point); 00578 00579 // Now, add those points of the neighborhood to the queue which are 00580 // set within the binary mask but still don't carry any label. Then 00581 // set the label of these points to the current region. As a result, 00582 // these points won't be selected a second time. 00583 00584 std::list<Point> neighbors = getNeighbors(point, neighborhood_size); 00585 00586 for(std::list<Point>::iterator neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor) 00587 { 00588 const unsigned index = getIndexFromPoint(*neighbor); 00589 if (data[index] && !label_mask[index]) 00590 { 00591 label_mask[index] = label; 00592 queue.push(*neighbor); 00593 } 00594 } 00595 } 00596 } 00597 } 00598 } 00599 00600 00609 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00610 inline unsigned RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::getIndexFromPoint(const Point & point) const 00611 { 00612 return point.z() * width * height + point.y() * width + point.x(); 00613 } 00614 00615 00623 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00624 const LabelType * RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::getLabelMask() const 00625 { 00626 return label_mask; 00627 } 00628 00629 00639 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00640 std::list<typename RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::Point> RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::getNeighbors(const Point & point, const unsigned size) const 00641 { 00642 // typical values for size are 0, 1, 2, 4, 8 00643 00644 // Return a list with all adjacent neighbors. Also check, 00645 // whether a neighbor is still within the data. 00646 00647 std::list<Point> neighbors; 00648 00649 const int delta = size == 2 ? 1 : int(std::ceil(std::sqrt(float(size)))); 00650 00651 for (int delta_x = -delta; delta_x <= delta; ++delta_x) 00652 { 00653 for (int delta_y = -delta; delta_y <= delta; ++delta_y) 00654 { 00655 for (int delta_z = -delta; delta_z <= delta; ++delta_z) 00656 { 00657 if (unsigned(delta_x * delta_x + delta_y * delta_y + delta_z * delta_z) <= size) 00658 { 00659 int x = point.x() + delta_x; 00660 int y = point.y() + delta_y; 00661 int z = point.z() + delta_z; 00662 00663 if (0 <= x && x < int(width) && 0 <= y && y < int(height) && 0 <= z && z < int(depth)) 00664 { 00665 neighbors.push_back(Point(x, y, z)); 00666 } 00667 } 00668 } 00669 } 00670 } 00671 00672 return neighbors; 00673 } 00674 00675 00685 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00686 inline typename RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::Point RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::getPointFromIndex(const unsigned index) const 00687 { 00688 const unsigned remainder_xy = index % (width * height); 00689 const Point::value_type x = remainder_xy % width; 00690 const Point::value_type y = remainder_xy / width; 00691 const Point::value_type z = index / (width * height); 00692 00693 return Point(x, y, z); 00694 } 00695 00696 00701 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00702 inline const typename RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::Regions & RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::getRegions() const 00703 { 00704 return regions; 00705 } 00706 00707 00713 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00714 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::initLabelMask() 00715 { 00716 for (unsigned i = 0; i < width * height * depth; ++i) 00717 { 00718 label_mask[i] = LabelType(0); 00719 } 00720 } 00721 00722 00731 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00732 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::setData(BinaryFieldType data, const unsigned width, const unsigned height, const unsigned depth) 00733 { 00734 this->data = data; 00735 this->data_available = true; 00736 00737 this->width = width; 00738 this->height = height; 00739 this->depth = depth; 00740 00741 if (label_mask) delete[] label_mask; 00742 00743 label_mask = new LabelType[width * height * depth]; 00744 initLabelMask(); 00745 } 00746 00747 00758 template <class BinaryDataType, class LabelType, class BinaryFieldType> 00759 void RegionGrowing3D<BinaryDataType, LabelType, BinaryFieldType>::setNeighborhoodSize(const unsigned size) 00760 { 00761 neighborhood_size = size; 00762 } 00763 00764 00765 } // namespace TRTK 00766 00767 #endif // REGION_GROWING_3D_1234689743
Documentation generated by Doxygen