NumCpp  2.9.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 
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
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:
71  ClusterMaker(const NdArray<bool>* const inXcdArrayPtr,
72  const NdArray<dtype>* const inIntensityArrayPtr,
73  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:
111  uint32 size() noexcept
112  {
113  return static_cast<uint32>(clusters_.size());
114  }
115 
116  //=============================================================================
117  // Description:
124  const Cluster<dtype>& operator[](uint32 inIndex) const noexcept
125  {
126  return clusters_[inIndex];
127  }
128 
129  //=============================================================================
130  // Description:
137  const Cluster<dtype>& at(uint32 inIndex) const
138  {
139  if (inIndex >= clusters_.size())
140  {
141  THROW_INVALID_ARGUMENT_ERROR("index exceeds cluster size.");
142  }
143  return clusters_[inIndex];
144  }
145 
146  //=============================================================================
147  // Description:
152  const_iterator begin() const noexcept
153  {
154  return clusters_.cbegin();
155  }
156 
157  //=============================================================================
158  // Description:
163  const_iterator end() const noexcept
164  {
165  return clusters_.cend();
166  }
167 
168  private:
169  //==================================Attributes=================================
170  const NdArray<bool>* const xcds_;
171  const NdArray<dtype>* const intensities_;
172  std::vector<Pixel<dtype>> xcdsVec_{};
173 
174  Shape shape_{};
175 
176  std::vector<Cluster<dtype>> clusters_{};
177 
178  //=============================================================================
179  // Description:
187  Pixel<dtype> makePixel(int32 inRow, int32 inCol) noexcept
188  {
189  // Make sure that on the edges after i've added or subtracted 1 from the row and col that
190  // i haven't gone over the edge
191  const uint32 row = std::min(static_cast<uint32>(std::max<int32>(inRow, 0)), shape_.rows - 1);
192  const uint32 col = std::min(static_cast<uint32>(std::max<int32>(inCol, 0)), shape_.cols - 1);
193  const dtype intensity = intensities_->operator()(row, col);
194 
195  return Pixel<dtype>(row, col, intensity);
196  }
197 
198  //=============================================================================
199  // Description:
206  void findNeighbors(const Pixel<dtype>& inPixel, std::set<Pixel<dtype>>& outNeighbors)
207  {
208  // using a set will auto take care of adding duplicate pixels on the edges
209 
210  // the 8 surrounding neighbors
211  const auto row = static_cast<int32>(inPixel.row);
212  const auto col = static_cast<int32>(inPixel.col);
213 
214  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col - 1));
215  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col));
216  outNeighbors.insert(outNeighbors.end(), makePixel(row - 1, col + 1));
217  outNeighbors.insert(outNeighbors.end(), makePixel(row, col - 1));
218  outNeighbors.insert(outNeighbors.end(), makePixel(row, col + 1));
219  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col - 1));
220  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col));
221  outNeighbors.insert(outNeighbors.end(), makePixel(row + 1, col + 1));
222  }
223 
224  //=============================================================================
225  // Description:
233  void findNeighborNotXcds(const Pixel<dtype>& inPixel, std::vector<Pixel<dtype>>& outNeighbors)
234  {
235  std::set<Pixel<dtype>> neighbors;
236  findNeighbors(inPixel, neighbors);
237 
238  // check if the neighboring pixels are exceedances and insert into the xcd vector
239  for (auto& pixel : neighbors)
240  {
241  if (!xcds_->operator()(pixel.row, pixel.col))
242  {
243  outNeighbors.push_back(pixel);
244  }
245  }
246  }
247 
248  //=============================================================================
249  // Description:
257  void findNeighborXcds(const Pixel<dtype>& inPixel, std::vector<uint32>& outNeighbors)
258  {
259  std::set<Pixel<dtype>> neighbors;
260  findNeighbors(inPixel, neighbors);
261  std::vector<Pixel<dtype>> neighborXcds;
262 
263  // check if the neighboring pixels are exceedances and insert into the xcd vector
264  for (auto& pixel : neighbors)
265  {
266  if (xcds_->operator()(pixel.row, pixel.col))
267  {
268  neighborXcds.push_back(pixel);
269  }
270  }
271 
272  // loop through the neighbors and find the cooresponding index into exceedances_
273  for (auto& pixel : neighborXcds)
274  {
275  auto theExceedanceIter = std::find(xcdsVec_.begin(), xcdsVec_.end(), pixel);
276  outNeighbors.push_back(static_cast<uint32>(theExceedanceIter - xcdsVec_.begin()));
277  }
278  }
279 
280  //=============================================================================
281  // Description:
284  void runClusterMaker()
285  {
286  uint32 clusterId = 0;
287 
288  for (auto& currentPixel : xcdsVec_)
289  {
290  // not already visited
291  if (currentPixel.clusterId == -1)
292  {
293  Cluster<dtype> newCluster(clusterId); // a new cluster
294  currentPixel.clusterId = clusterId;
295  newCluster.addPixel(currentPixel); // assign pixel to cluster
296 
297  // get the neighbors
298  std::vector<uint32> neighborIds;
299  findNeighborXcds(currentPixel, neighborIds);
300  if (neighborIds.empty())
301  {
302  clusters_.push_back(newCluster);
303  ++clusterId;
304  continue;
305  }
306 
307  // loop through the neighbors
308  for (uint32 neighborsIdx = 0; neighborsIdx < neighborIds.size(); ++neighborsIdx)
309  {
310  Pixel<dtype>& currentNeighborPixel = xcdsVec_[neighborIds[neighborsIdx]];
311 
312  // go to neighbors
313  std::vector<uint32> newNeighborIds;
314  findNeighborXcds(currentNeighborPixel, newNeighborIds);
315 
316  // loop through the new neighbors and add them to neighbors
317  for (auto newNeighborId : newNeighborIds)
318  {
319  // not already in neighbors
320  if (std::find(neighborIds.begin(), neighborIds.end(), newNeighborId) ==
321  neighborIds.end())
322  {
323  neighborIds.push_back(newNeighborId);
324  }
325  }
326 
327  // not already assigned to a cluster
328  if (currentNeighborPixel.clusterId == -1)
329  {
330  currentNeighborPixel.clusterId = clusterId;
331  newCluster.addPixel(currentNeighborPixel);
332  }
333  }
334 
335  clusters_.push_back(std::move(newCluster));
336  ++clusterId;
337  }
338  }
339  }
340 
341  //=============================================================================
342  // Description:
345  void expandClusters()
346  {
347  // loop through the clusters
348  for (auto& theCluster : clusters_)
349  {
350  // loop through the pixels of the cluster
351  for (auto& thePixel : theCluster)
352  {
353  std::vector<Pixel<dtype>> neighborsNotXcds;
354  findNeighborNotXcds(thePixel, neighborsNotXcds);
355 
356  // loop through the neighbors and if they haven't already been added to the cluster, add them
357  for (auto& newPixel : neighborsNotXcds)
358  {
359  if (std::find(theCluster.begin(), theCluster.end(), newPixel) == theCluster.end())
360  {
361  theCluster.addPixel(newPixel);
362  }
363  }
364  }
365  }
366  }
367  };
368  } // namespace imageProcessing
369 } // namespace nc
#define THROW_INVALID_ARGUMENT_ERROR(msg)
Definition: Error.hpp:36
Shape shape() const noexcept
Definition: NdArrayCore.hpp:4092
A Shape Class for NdArrays.
Definition: Core/Shape.hpp:41
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:55
Clusters exceedance data into contiguous groups.
Definition: ClusterMaker.hpp:53
ClusterMaker(const NdArray< bool > *const inXcdArrayPtr, const NdArray< dtype > *const inIntensityArrayPtr, uint8 inBorderWidth=0)
Definition: ClusterMaker.hpp:71
const_iterator begin() const noexcept
Definition: ClusterMaker.hpp:152
const Cluster< dtype > & at(uint32 inIndex) const
Definition: ClusterMaker.hpp:137
const_iterator end() const noexcept
Definition: ClusterMaker.hpp:163
typename std::vector< Cluster< dtype > >::const_iterator const_iterator
Definition: ClusterMaker.hpp:59
uint32 size() noexcept
Definition: ClusterMaker.hpp:111
const Cluster< dtype > & operator[](uint32 inIndex) const noexcept
Definition: ClusterMaker.hpp:124
Holds the information for a single pixel.
Definition: Pixel.hpp:47
InputIt find(InputIt first, InputIt last, const T &value) noexcept
Definition: StlAlgorithms.hpp:207
Definition: Coordinate.hpp:45
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