NumCpp  1.0
A C++ implementation of the Python Numpy library
ClusterMaker.hpp
Go to the documentation of this file.
1 
30 #pragma once
31 
34 #include "NumCpp/Core/Types.hpp"
36 #include "NumCpp/NdArray.hpp"
37 
38 #include <algorithm>
39 #include <cmath>
40 #include <set>
41 #include <string>
42 #include <utility>
43 #include <vector>
44 
45 namespace nc
46 {
47  namespace imageProcessing
48  {
49  //=============================================================================
50  // Class Description:
52  template<typename dtype>
54  {
55  private:
56  STATIC_ASSERT_ARITHMETIC(dtype);
57 
58  public:
59  //================================Typedefs=====================================
60  using const_iterator = typename std::vector<Cluster<dtype> >::const_iterator;
61 
62  //=============================================================================
63  // Description:
73  ClusterMaker(const NdArray<bool>* const inXcdArrayPtr, const NdArray<dtype>* const inIntensityArrayPtr, uint8 inBorderWidth = 0) :
74  xcds_(inXcdArrayPtr),
75  intensities_(inIntensityArrayPtr)
76  {
77  if (xcds_->shape() != intensities_->shape())
78  {
79  THROW_INVALID_ARGUMENT_ERROR("input xcd and intensity arrays must be the same shape.");
80  }
81 
82  shape_ = xcds_->shape();
83 
84  // convert the NdArray of booleans to a vector of exceedances
85  for (uint32 row = 0; row < shape_.rows; ++row)
86  {
87  for (uint32 col = 0; col < shape_.cols; ++col)
88  {
89  if (xcds_->operator()(row, col))
90  {
91  const Pixel<dtype> thePixel(row, col, intensities_->operator()(row, col));
92  xcdsVec_.push_back(thePixel);
93  }
94  }
95  }
96 
97  runClusterMaker();
98 
99  for (uint8 i = 0; i < inBorderWidth; ++i)
100  {
101  expandClusters();
102  }
103  }
104 
105  //=============================================================================
106  // Description:
112  uint32 size() noexcept
113  {
114  return static_cast<uint32>(clusters_.size());
115  }
116 
117  //=============================================================================
118  // Description:
127  const Cluster<dtype>& operator[](uint32 inIndex) const noexcept
128  {
129  return clusters_[inIndex];
130  }
131 
132  //=============================================================================
133  // Description:
142  const Cluster<dtype>& at(uint32 inIndex) const
143  {
144  if (inIndex >= clusters_.size())
145  {
146  THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size.");
147  }
148  return clusters_[inIndex];
149  }
150 
151  //=============================================================================
152  // Description:
158  const_iterator begin() const noexcept
159  {
160  return clusters_.cbegin();
161  }
162 
163  //=============================================================================
164  // Description:
170  const_iterator end() const noexcept
171  {
172  return clusters_.cend();
173  }
174 
175  private:
176  //==================================Attributes=================================
177  const NdArray<bool>* const xcds_;
178  const NdArray<dtype>* const intensities_;
179  std::vector<Pixel<dtype> > xcdsVec_{};
180 
181  Shape shape_{};
182 
183  std::vector<Cluster<dtype> > clusters_{};
184 
185  //=============================================================================
186  // Description:
195  Pixel<dtype> makePixel(int32 inRow, int32 inCol) noexcept
196  {
197  // Make sure that on the edges after i've added or subtracted 1 from the row and col that
198  // i haven't gone over the edge
199  const uint32 row = std::min(static_cast<uint32>(std::max<int32>(inRow, 0)), shape_.rows - 1);
200  const uint32 col = std::min(static_cast<uint32>(std::max<int32>(inCol, 0)), shape_.cols - 1);
201  const dtype intensity = intensities_->operator()(row, col);
202 
203  return Pixel<dtype>(row, col, intensity);
204  }
205 
206  //=============================================================================
207  // Description:
215  void findNeighbors(const Pixel<dtype>& inPixel, std::set<Pixel<dtype> >& outNeighbors)
216  {
217  // using a set will auto take care of adding duplicate pixels on the edges
218 
219  // the 8 surrounding neighbors
220  const int32 row = static_cast<int32>(inPixel.row);
221  const int32 col = static_cast<int32>(inPixel.col);
222 
223  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col - 1));
224  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col));
225  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col + 1));
226  outNeighbors.insert(outNeighbors.end(), makePixel(row, col - 1));
227  outNeighbors.insert(outNeighbors.end(), makePixel(row, col + 1));
228  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col - 1));
229  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col));
230  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col + 1));
231  }
232 
233  //=============================================================================
234  // Description:
243  void findNeighborNotXcds(const Pixel<dtype>& inPixel, std::vector<Pixel<dtype> >& outNeighbors)
244  {
245  std::set<Pixel<dtype> > neighbors;
246  findNeighbors(inPixel, neighbors);
247 
248  // check if the neighboring pixels are exceedances and insert into the xcd vector
249  for (auto& pixel : neighbors)
250  {
251  if (!xcds_->operator()(pixel.row, pixel.col))
252  {
253  outNeighbors.push_back(pixel);
254  }
255  }
256  }
257 
258  //=============================================================================
259  // Description:
268  void findNeighborXcds(const Pixel<dtype>& inPixel, std::vector<uint32>& outNeighbors)
269  {
270  std::set<Pixel<dtype> > neighbors;
271  findNeighbors(inPixel, neighbors);
272  std::vector<Pixel<dtype> > neighborXcds;
273 
274  // check if the neighboring pixels are exceedances and insert into the xcd vector
275  for (auto& pixel : neighbors)
276  {
277  if (xcds_->operator()(pixel.row, pixel.col))
278  {
279  neighborXcds.push_back(pixel);
280  }
281  }
282 
283  // loop through the neighbors and find the cooresponding index into exceedances_
284  for (auto& pixel : neighborXcds)
285  {
286  auto theExceedanceIter = std::find(xcdsVec_.begin(), xcdsVec_.end(), pixel);
287  outNeighbors.push_back(static_cast<uint32>(theExceedanceIter - xcdsVec_.begin()));
288  }
289  }
290 
291  //=============================================================================
292  // Description:
295  void runClusterMaker()
296  {
297  uint32 clusterId = 0;
298 
299  for (auto& currentPixel : xcdsVec_)
300  {
301  // not already visited
302  if (currentPixel.clusterId == -1)
303  {
304  Cluster<dtype> newCluster(clusterId); // a new cluster
305  currentPixel.clusterId = clusterId;
306  newCluster.addPixel(currentPixel); // assign pixel to cluster
307 
308  // get the neighbors
309  std::vector<uint32> neighborIds;
310  findNeighborXcds(currentPixel, neighborIds);
311  if (neighborIds.empty())
312  {
313  clusters_.push_back(newCluster);
314  ++clusterId;
315  continue;
316  }
317 
318  // loop through the neighbors
319  for (uint32 neighborsIdx = 0; neighborsIdx < neighborIds.size(); ++neighborsIdx)
320  {
321  Pixel<dtype>& currentNeighborPixel = xcdsVec_[neighborIds[neighborsIdx]];
322 
323  // go to neighbors
324  std::vector<uint32> newNeighborIds;
325  findNeighborXcds(currentNeighborPixel, newNeighborIds);
326 
327  // loop through the new neighbors and add them to neighbors
328  for (auto newNeighborId : newNeighborIds)
329  {
330  // not already in neighbors
331  if (std::find(neighborIds.begin(), neighborIds.end(), newNeighborId) == neighborIds.end())
332  {
333  neighborIds.push_back(newNeighborId);
334  }
335  }
336 
337  // not already assigned to a cluster
338  if (currentNeighborPixel.clusterId == -1)
339  {
340  currentNeighborPixel.clusterId = clusterId;
341  newCluster.addPixel(currentNeighborPixel);
342  }
343  }
344 
345  clusters_.push_back(std::move(newCluster));
346  ++clusterId;
347  }
348  }
349  }
350 
351  //=============================================================================
352  // Description:
355  void expandClusters()
356  {
357  // loop through the clusters
358  for (auto& theCluster : clusters_)
359  {
360  // loop through the pixels of the cluster
361  for (auto& thePixel : theCluster)
362  {
363  std::vector<Pixel<dtype> > neighborsNotXcds;
364  findNeighborNotXcds(thePixel, neighborsNotXcds);
365 
366  // loop through the neighbors and if they haven't already been added to the cluster, add them
367  for (auto& newPixel : neighborsNotXcds)
368  {
369  if (std::find(theCluster.begin(), theCluster.end(), newPixel) == theCluster.end())
370  {
371  theCluster.addPixel(newPixel);
372  }
373  }
374  }
375  }
376  }
377  };
378  }
379 }
StaticAsserts.hpp
nc::NdArray::shape
Shape shape() const noexcept
Definition: NdArrayCore.hpp:4296
nc::int32
std::int32_t int32
Definition: Types.hpp:37
Error.hpp
nc::imageProcessing::ClusterMaker::size
uint32 size() noexcept
Definition: ClusterMaker.hpp:112
nc::uint8
std::uint8_t uint8
Definition: Types.hpp:43
nc::NdArray< bool >
nc::uint32
std::uint32_t uint32
Definition: Types.hpp:41
nc::imageProcessing::ClusterMaker::operator[]
const Cluster< dtype > & operator[](uint32 inIndex) const noexcept
Definition: ClusterMaker.hpp:127
nc::imageProcessing::ClusterMaker::at
const Cluster< dtype > & at(uint32 inIndex) const
Definition: ClusterMaker.hpp:142
NdArray.hpp
nc::imageProcessing::ClusterMaker::const_iterator
typename std::vector< Cluster< dtype > >::const_iterator const_iterator
Definition: ClusterMaker.hpp:60
nc::Shape
A Shape Class for NdArrays.
Definition: Core/Shape.hpp:41
nc::Shape::cols
uint32 cols
Definition: Core/Shape.hpp:46
nc::imageProcessing::ClusterMaker::begin
const_iterator begin() const noexcept
Definition: ClusterMaker.hpp:158
nc::stl_algorithms::find
InputIt find(InputIt first, InputIt last, const T &value) noexcept
Definition: StlAlgorithms.hpp:196
nc::imageProcessing::ClusterMaker::ClusterMaker
ClusterMaker(const NdArray< bool > *const inXcdArrayPtr, const NdArray< dtype > *const inIntensityArrayPtr, uint8 inBorderWidth=0)
Definition: ClusterMaker.hpp:73
nc::imageProcessing::ClusterMaker
Clusters exceedance data into contiguous groups.
Definition: ClusterMaker.hpp:53
nc
Definition: Coordinate.hpp:45
nc::Shape::rows
uint32 rows
Definition: Core/Shape.hpp:45
Cluster.hpp
THROW_INVALID_ARGUMENT_ERROR
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition: Error.hpp:37
Types.hpp
nc::imageProcessing::ClusterMaker::end
const_iterator end() const noexcept
Definition: ClusterMaker.hpp:170
nc::imageProcessing::Cluster
Holds the information for a cluster of pixels.
Definition: Cluster.hpp:54
nc::min
NdArray< dtype > min(const NdArray< dtype > &inArray, Axis inAxis=Axis::NONE)
Definition: min.hpp:46
nc::imageProcessing::Pixel
Holds the information for a single pixel.
Definition: Pixel.hpp:47