NumCpp  2.10.1
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 
31 #include <algorithm>
32 #include <cmath>
33 #include <set>
34 #include <string>
35 #include <utility>
36 #include <vector>
37 
40 #include "NumCpp/Core/Types.hpp"
42 #include "NumCpp/NdArray.hpp"
43 
44 namespace nc::imageProcessing
45 {
46  //=============================================================================
47  // Class Description:
49  template<typename dtype>
51  {
52  private:
53  STATIC_ASSERT_ARITHMETIC(dtype);
54 
55  public:
56  //================================Typedefs=====================================
57  using const_iterator = typename std::vector<Cluster<dtype>>::const_iterator;
58 
59  //=============================================================================
60  // Description:
69  ClusterMaker(const NdArray<bool>* const inXcdArrayPtr,
70  const NdArray<dtype>* const inIntensityArrayPtr,
71  uint8 inBorderWidth = 0) :
72  xcds_(inXcdArrayPtr),
73  intensities_(inIntensityArrayPtr)
74  {
75  if (xcds_->shape() != intensities_->shape())
76  {
77  THROW_INVALID_ARGUMENT_ERROR("input xcd and intensity arrays must be the same shape.");
78  }
79 
80  shape_ = xcds_->shape();
81 
82  // convert the NdArray of booleans to a vector of exceedances
83  for (uint32 row = 0; row < shape_.rows; ++row)
84  {
85  for (uint32 col = 0; col < shape_.cols; ++col)
86  {
87  if (xcds_->operator()(row, col))
88  {
89  const Pixel<dtype> thePixel(row, col, intensities_->operator()(row, col));
90  xcdsVec_.push_back(thePixel);
91  }
92  }
93  }
94 
95  runClusterMaker();
96 
97  for (uint8 i = 0; i < inBorderWidth; ++i)
98  {
99  expandClusters();
100  }
101  }
102 
103  //=============================================================================
104  // Description:
109  uint32 size() noexcept
110  {
111  return static_cast<uint32>(clusters_.size());
112  }
113 
114  //=============================================================================
115  // Description:
122  const Cluster<dtype>& operator[](uint32 inIndex) const noexcept
123  {
124  return clusters_[inIndex];
125  }
126 
127  //=============================================================================
128  // Description:
135  [[nodiscard]] const Cluster<dtype>& at(uint32 inIndex) const
136  {
137  if (inIndex >= clusters_.size())
138  {
139  THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size.");
140  }
141  return clusters_[inIndex];
142  }
143 
144  //=============================================================================
145  // Description:
150  [[nodiscard]] const_iterator begin() const noexcept
151  {
152  return clusters_.cbegin();
153  }
154 
155  //=============================================================================
156  // Description:
161  [[nodiscard]] const_iterator end() const noexcept
162  {
163  return clusters_.cend();
164  }
165 
166  private:
167  //==================================Attributes=================================
168  const NdArray<bool>* const xcds_{};
169  const NdArray<dtype>* const intensities_{};
170  std::vector<Pixel<dtype>> xcdsVec_{};
171 
172  Shape shape_{};
173 
174  std::vector<Cluster<dtype>> clusters_{};
175 
176  //=============================================================================
177  // Description:
185  Pixel<dtype> makePixel(int32 inRow, int32 inCol) noexcept
186  {
187  // Make sure that on the edges after i've added or subtracted 1 from the row and col that
188  // i haven't gone over the edge
189  const uint32 row = std::min(static_cast<uint32>(std::max<int32>(inRow, 0)), shape_.rows - 1);
190  const uint32 col = std::min(static_cast<uint32>(std::max<int32>(inCol, 0)), shape_.cols - 1);
191  const dtype intensity = intensities_->operator()(row, col);
192 
193  return Pixel<dtype>(row, col, intensity);
194  }
195 
196  //=============================================================================
197  // Description:
204  void findNeighbors(const Pixel<dtype>& inPixel, std::set<Pixel<dtype>>& outNeighbors)
205  {
206  // using a set will auto take care of adding duplicate pixels on the edges
207 
208  // the 8 surrounding neighbors
209  const auto row = static_cast<int32>(inPixel.row);
210  const auto col = static_cast<int32>(inPixel.col);
211 
212  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col - 1));
213  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col));
214  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col + 1));
215  outNeighbors.insert(outNeighbors.end(), makePixel(row, col - 1));
216  outNeighbors.insert(outNeighbors.end(), makePixel(row, col + 1));
217  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col - 1));
218  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col));
219  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col + 1));
220  }
221 
222  //=============================================================================
223  // Description:
231  void findNeighborNotXcds(const Pixel<dtype>& inPixel, std::vector<Pixel<dtype>>& outNeighbors)
232  {
233  std::set<Pixel<dtype>> neighbors;
234  findNeighbors(inPixel, neighbors);
235 
236  // check if the neighboring pixels are exceedances and insert into the xcd vector
237  for (auto& pixel : neighbors)
238  {
239  if (!xcds_->operator()(pixel.row, pixel.col))
240  {
241  outNeighbors.push_back(pixel);
242  }
243  }
244  }
245 
246  //=============================================================================
247  // Description:
255  void findNeighborXcds(const Pixel<dtype>& inPixel, std::vector<uint32>& outNeighbors)
256  {
257  std::set<Pixel<dtype>> neighbors;
258  findNeighbors(inPixel, neighbors);
259  std::vector<Pixel<dtype>> neighborXcds;
260 
261  // check if the neighboring pixels are exceedances and insert into the xcd vector
262  for (auto& pixel : neighbors)
263  {
264  if (xcds_->operator()(pixel.row, pixel.col))
265  {
266  neighborXcds.push_back(pixel);
267  }
268  }
269 
270  // loop through the neighbors and find the cooresponding index into exceedances_
271  for (auto& pixel : neighborXcds)
272  {
273  auto theExceedanceIter = std::find(xcdsVec_.begin(), xcdsVec_.end(), pixel);
274  outNeighbors.push_back(static_cast<uint32>(theExceedanceIter - xcdsVec_.begin()));
275  }
276  }
277 
278  //=============================================================================
279  // Description:
282  void runClusterMaker()
283  {
284  uint32 clusterId = 0;
285 
286  for (auto& currentPixel : xcdsVec_)
287  {
288  // not already visited
289  if (currentPixel.clusterId == -1)
290  {
291  Cluster<dtype> newCluster(clusterId); // a new cluster
292  currentPixel.clusterId = clusterId;
293  newCluster.addPixel(currentPixel); // assign pixel to cluster
294 
295  // get the neighbors
296  std::vector<uint32> neighborIds;
297  findNeighborXcds(currentPixel, neighborIds);
298  if (neighborIds.empty())
299  {
300  clusters_.push_back(newCluster);
301  ++clusterId;
302  continue;
303  }
304 
305  // loop through the neighbors
306  for (uint32 neighborsIdx = 0; neighborsIdx < neighborIds.size(); ++neighborsIdx)
307  {
308  Pixel<dtype>& currentNeighborPixel = xcdsVec_[neighborIds[neighborsIdx]];
309 
310  // go to neighbors
311  std::vector<uint32> newNeighborIds;
312  findNeighborXcds(currentNeighborPixel, newNeighborIds);
313 
314  // loop through the new neighbors and add them to neighbors
315  for (auto newNeighborId : newNeighborIds)
316  {
317  // not already in neighbors
318  if (std::find(neighborIds.begin(), neighborIds.end(), newNeighborId) == neighborIds.end())
319  {
320  neighborIds.push_back(newNeighborId);
321  }
322  }
323 
324  // not already assigned to a cluster
325  if (currentNeighborPixel.clusterId == -1)
326  {
327  currentNeighborPixel.clusterId = clusterId;
328  newCluster.addPixel(currentNeighborPixel);
329  }
330  }
331 
332  clusters_.push_back(std::move(newCluster));
333  ++clusterId;
334  }
335  }
336  }
337 
338  //=============================================================================
339  // Description:
342  void expandClusters()
343  {
344  // loop through the clusters
345  for (auto& theCluster : clusters_)
346  {
347  // loop through the pixels of the cluster
348  for (auto& thePixel : theCluster)
349  {
350  std::vector<Pixel<dtype>> neighborsNotXcds;
351  findNeighborNotXcds(thePixel, neighborsNotXcds);
352 
353  // loop through the neighbors and if they haven't already been added to the cluster, add them
354  for (auto& newPixel : neighborsNotXcds)
355  {
356  if (std::find(theCluster.begin(), theCluster.end(), newPixel) == theCluster.end())
357  {
358  theCluster.addPixel(newPixel);
359  }
360  }
361  }
362  }
363  }
364  };
365 } // namespace nc::imageProcessing
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition: Error.hpp:37
Shape shape() const noexcept
Definition: NdArrayCore.hpp:4402
uint32 rows
Definition: Core/Shape.hpp:44
uint32 cols
Definition: Core/Shape.hpp:45
Holds the information for a cluster of pixels.
Definition: Cluster.hpp:53
Clusters exceedance data into contiguous groups.
Definition: ClusterMaker.hpp:51
ClusterMaker(const NdArray< bool > *const inXcdArrayPtr, const NdArray< dtype > *const inIntensityArrayPtr, uint8 inBorderWidth=0)
Definition: ClusterMaker.hpp:69
const_iterator begin() const noexcept
Definition: ClusterMaker.hpp:150
const Cluster< dtype > & at(uint32 inIndex) const
Definition: ClusterMaker.hpp:135
const_iterator end() const noexcept
Definition: ClusterMaker.hpp:161
typename std::vector< Cluster< dtype > >::const_iterator const_iterator
Definition: ClusterMaker.hpp:57
uint32 size() noexcept
Definition: ClusterMaker.hpp:109
const Cluster< dtype > & operator[](uint32 inIndex) const noexcept
Definition: ClusterMaker.hpp:122
Holds the information for a single pixel.
Definition: Pixel.hpp:46
Definition: applyThreshold.hpp:34
InputIt find(InputIt first, InputIt last, const T &value) noexcept
Definition: StlAlgorithms.hpp:205
NdArray< dtype > min(const NdArray< dtype > &inArray, Axis inAxis=Axis::NONE)
Definition: min.hpp:44
std::int32_t int32
Definition: Types.hpp:36
std::uint8_t uint8
Definition: Types.hpp:42
std::uint32_t uint32
Definition: Types.hpp:40