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