AARE
Data analysis library for PSI hybrid detectors
Loading...
Searching...
No Matches
VariableSizeClusterFinder.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <map>
5#include <unordered_map>
6#include <vector>
7
9
10const int MAX_CLUSTER_SIZE = 200;
11namespace aare {
12
13template <typename T> class ClusterFinder {
14 public:
15 struct Hit {
16 int16_t size{};
17 int16_t row{};
18 int16_t col{};
19 uint16_t reserved{}; // for alignment
20 T energy{};
21 T max{};
22
23 // std::vector<int16_t> rows{};
24 // std::vector<int16_t> cols{};
25 int16_t rows[MAX_CLUSTER_SIZE] = {0};
26 int16_t cols[MAX_CLUSTER_SIZE] = {0};
27 double enes[MAX_CLUSTER_SIZE] = {0};
28 };
29
30 private:
31 const std::array<ssize_t, 2> shape_;
35 NDArray<bool, 2> binary_; // over threshold flag
38 bool use_noise_map = false;
41 const std::array<int, 4> di{{0, -1, -1, -1}}; // row ### 8-neighbour by scaning from left to right
42 const std::array<int, 4> dj{{-1, -1, 0, 1}}; // col ### 8-neighbour by scaning from top to bottom
43 const std::array<int, 8> di_{{0, 0, -1, 1, -1, 1, -1, 1}}; // row
44 const std::array<int, 8> dj_{{-1, 1, 0, 0, 1, -1, -1, 1}}; // col
45 std::map<int, int> child; // heirachy: key: child; val: parent
46 std::unordered_map<int, Hit> h_size;
47 std::vector<Hit> hits;
48 // std::vector<std::vector<int16_t>> row
49 int check_neighbours(int i, int j);
50
51 public:
52 ClusterFinder(image_shape shape, T threshold)
53 : shape_(shape), labeled_(shape, 0), peripheral_labeled_(shape, 0), binary_(shape), threshold_(threshold) {
54 hits.reserve(2000);
55 }
56
58
59 void set_noiseMap(NDView<T, 2> noise_map) {
60 noiseMap = noise_map;
61 use_noise_map = true;
62 }
66 void rec_FillHit(int clusterIndex, int i, int j);
67 void single_pass(NDView<T, 2> img);
68 void first_pass();
69 void second_pass();
70 void store_clusters();
71
72 std::vector<Hit> steal_hits() {
73 std::vector<Hit> tmp;
74 std::swap(tmp, hits);
75 return tmp;
76 };
77 void clear_hits() { hits.clear(); };
78
80 fmt::print("Connections:\n");
81 for (auto it = child.begin(); it != child.end(); ++it) {
82 fmt::print("{} -> {}\n", it->first, it->second);
83 }
84 }
85 size_t total_clusters() const {
86 // TODO! fix for stealing
87 return hits.size();
88 }
89
90 private:
91 void add_link(int from, int to) {
92 // we want to add key from -> value to
93 // fmt::print("add_link({},{})\n", from, to);
94 auto it = child.find(from);
95 if (it == child.end()) {
96 child[from] = to;
97 } else {
98 // found need to disambiguate
99 if (it->second == to)
100 return;
101 else {
102 if (it->second > to) {
103 // child[from] = to;
104 auto old = it->second;
105 it->second = to;
106 add_link(old, to);
107 } else {
108 // found value is smaller than what we want to link
109 add_link(to, it->second);
110 }
111 }
112 }
113 }
114};
115template <typename T> int ClusterFinder<T>::check_neighbours(int i, int j) {
116 std::vector<int> neighbour_labels;
117
118 for (int k = 0; k < 4; ++k) {
119 const auto row = i + di[k];
120 const auto col = j + dj[k];
121 if (row >= 0 && col >= 0 && row < shape_[0] && col < shape_[1]) {
122 auto tmp = labeled_.value(i + di[k], j + dj[k]);
123 if (tmp != 0)
124 neighbour_labels.push_back(tmp);
125 }
126 }
127
128 if (neighbour_labels.size() == 0) {
129 return 0;
130 } else {
131
132 // need to sort and add to union field
133 std::sort(neighbour_labels.rbegin(), neighbour_labels.rend());
134 auto first = neighbour_labels.begin();
135 auto last = std::unique(first, neighbour_labels.end());
136 if (last - first == 1)
137 return *neighbour_labels.begin();
138
139 for (auto current = first; current != last - 1; ++current) {
140 auto next = current + 1;
141 add_link(*current, *next);
142 }
143 return neighbour_labels.back(); // already sorted
144 }
145}
146
147template <typename T> void ClusterFinder<T>::find_clusters(NDView<T, 2> img) {
148 original_ = img;
149 labeled_ = 0;
150 peripheral_labeled_ = 0;
151 current_label = 0;
152 child.clear();
153 first_pass();
154 // print_connections();
155 second_pass();
156 store_clusters();
157}
158
159template <typename T> void ClusterFinder<T>::find_clusters_X(NDView<T, 2> img) {
160 original_ = img;
161 int clusterIndex = 0;
162 for (int i = 0; i < shape_[0]; ++i) {
163 for (int j = 0; j < shape_[1]; ++j) {
164 if (use_noise_map)
165 threshold_ = 5 * noiseMap(i, j);
166 if (original_(i, j) > threshold_) {
167 // printf("========== Cluster index: %d\n", clusterIndex);
168 rec_FillHit(clusterIndex, i, j);
169 clusterIndex++;
170 }
171 }
172 }
173 for (const auto &h : h_size)
174 hits.push_back(h.second);
175 h_size.clear();
176}
177
178template <typename T> void ClusterFinder<T>::rec_FillHit(int clusterIndex, int i, int j) {
179 // printf("original_(%d, %d)=%f\n", i, j, original_(i,j));
180 // printf("h_size[%d].size=%d\n", clusterIndex, h_size[clusterIndex].size);
181 if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE) {
182 h_size[clusterIndex].rows[h_size[clusterIndex].size] = i;
183 h_size[clusterIndex].cols[h_size[clusterIndex].size] = j;
184 h_size[clusterIndex].enes[h_size[clusterIndex].size] = original_(i, j);
185 }
186 h_size[clusterIndex].size += 1;
187 h_size[clusterIndex].energy += original_(i, j);
188 if (h_size[clusterIndex].max < original_(i, j)) {
189 h_size[clusterIndex].row = i;
190 h_size[clusterIndex].col = j;
191 h_size[clusterIndex].max = original_(i, j);
192 }
193 original_(i, j) = 0;
194
195 for (int k = 0; k < 8; ++k) { // 8 for 8-neighbour
196 const auto row = i + di_[k];
197 const auto col = j + dj_[k];
198 if (row >= 0 && col >= 0 && row < shape_[0] && col < shape_[1]) {
199 if (use_noise_map)
200 threshold_ = peripheralThresholdFactor_ * noiseMap(row, col);
201 if (original_(row, col) > threshold_) {
202 rec_FillHit(clusterIndex, row, col);
203 } else {
204 // if (h_size[clusterIndex].size < MAX_CLUSTER_SIZE){
205 // h_size[clusterIndex].size += 1;
206 // h_size[clusterIndex].rows[h_size[clusterIndex].size] = row;
207 // h_size[clusterIndex].cols[h_size[clusterIndex].size] = col;
208 // h_size[clusterIndex].enes[h_size[clusterIndex].size] = original_(row, col);
209 // }// ? weather to include peripheral pixels
210 original_(row, col) = 0; // remove peripheral pixels, to avoid potential influence for pedestal updating
211 }
212 }
213 }
214}
215
216template <typename T> void ClusterFinder<T>::single_pass(NDView<T, 2> img) {
217 original_ = img;
218 labeled_ = 0;
219 current_label = 0;
220 child.clear();
221 first_pass();
222 // print_connections();
223 // second_pass();
224 // store_clusters();
225}
226
227template <typename T> void ClusterFinder<T>::first_pass() {
228
229 for (int i = 0; i < original_.size(); ++i) {
230 if (use_noise_map)
231 threshold_ = 5 * noiseMap(i);
232 binary_(i) = (original_(i) > threshold_);
233 }
234
235 for (int i = 0; i < shape_[0]; ++i) {
236 for (int j = 0; j < shape_[1]; ++j) {
237
238 // do we have someting to process?
239 if (binary_(i, j)) {
240 auto tmp = check_neighbours(i, j);
241 if (tmp != 0) {
242 labeled_(i, j) = tmp;
243 } else {
244 labeled_(i, j) = ++current_label;
245 }
246 }
247 }
248 }
249}
250
251template <typename T> void ClusterFinder<T>::second_pass() {
252
253 for (ssize_t i = 0; i != labeled_.size(); ++i) {
254 auto current_label = labeled_(i);
255 if (current_label != 0) {
256 auto it = child.find(current_label);
257 while (it != child.end()) {
258 current_label = it->second;
259 it = child.find(current_label);
260 // do this once before doing the second pass?
261 // all values point to the final one...
262 }
263 labeled_(i) = current_label;
264 }
265 }
266}
267
268template <typename T> void ClusterFinder<T>::store_clusters() {
269
270 // Accumulate hit information in a map
271 // Do we always have monotonic increasing
272 // labels? Then vector?
273 // here the translation is label -> Hit
274 std::unordered_map<int, Hit> h_size;
275 for (int i = 0; i < shape_[0]; ++i) {
276 for (int j = 0; j < shape_[1]; ++j) {
277 if (labeled_(i, j) != 0 or false
278 // (i-1 >= 0 and labeled_(i-1, j) != 0) or // another circle of peripheral pixels
279 // (j-1 >= 0 and labeled_(i, j-1) != 0) or
280 // (i+1 < shape_[0] and labeled_(i+1, j) != 0) or
281 // (j+1 < shape_[1] and labeled_(i, j+1) != 0)
282 ) {
283 Hit &record = h_size[labeled_(i, j)];
284 if (record.size < MAX_CLUSTER_SIZE) {
285 record.rows[record.size] = i;
286 record.cols[record.size] = j;
287 record.enes[record.size] = original_(i, j);
288 } else {
289 continue;
290 }
291 record.size += 1;
292 record.energy += original_(i, j);
293
294 if (record.max < original_(i, j)) {
295 record.row = i;
296 record.col = j;
297 record.max = original_(i, j);
298 }
299 }
300 }
301 }
302
303 for (const auto &h : h_size)
304 hits.push_back(h.second);
305}
306
307} // namespace aare
const int MAX_CLUSTER_SIZE
Definition VariableSizeClusterFinder.hpp:10
Definition VariableSizeClusterFinder.hpp:13
void store_clusters()
Definition VariableSizeClusterFinder.hpp:268
NDArray< int, 2 > labeled_
Definition VariableSizeClusterFinder.hpp:33
void add_link(int from, int to)
Definition VariableSizeClusterFinder.hpp:91
T threshold_
Definition VariableSizeClusterFinder.hpp:36
size_t total_clusters() const
Definition VariableSizeClusterFinder.hpp:85
void set_peripheralThresholdFactor(int factor)
Definition VariableSizeClusterFinder.hpp:63
void clear_hits()
Definition VariableSizeClusterFinder.hpp:77
std::vector< Hit > steal_hits()
Definition VariableSizeClusterFinder.hpp:72
void rec_FillHit(int clusterIndex, int i, int j)
Definition VariableSizeClusterFinder.hpp:178
void second_pass()
Definition VariableSizeClusterFinder.hpp:251
const std::array< int, 4 > di
Definition VariableSizeClusterFinder.hpp:41
const std::array< int, 4 > dj
Definition VariableSizeClusterFinder.hpp:42
NDView< T, 2 > original_
Definition VariableSizeClusterFinder.hpp:32
void first_pass()
Definition VariableSizeClusterFinder.hpp:227
NDView< T, 2 > noiseMap
Definition VariableSizeClusterFinder.hpp:37
NDArray< int, 2 > peripheral_labeled_
Definition VariableSizeClusterFinder.hpp:34
const std::array< ssize_t, 2 > shape_
Definition VariableSizeClusterFinder.hpp:31
NDArray< int, 2 > labeled()
Definition VariableSizeClusterFinder.hpp:57
int current_label
Definition VariableSizeClusterFinder.hpp:40
ClusterFinder(image_shape shape, T threshold)
Definition VariableSizeClusterFinder.hpp:52
const std::array< int, 8 > dj_
Definition VariableSizeClusterFinder.hpp:44
void single_pass(NDView< T, 2 > img)
Definition VariableSizeClusterFinder.hpp:216
void find_clusters(NDView< T, 2 > img)
Definition VariableSizeClusterFinder.hpp:147
std::vector< Hit > hits
Definition VariableSizeClusterFinder.hpp:47
int check_neighbours(int i, int j)
Definition VariableSizeClusterFinder.hpp:115
NDArray< bool, 2 > binary_
Definition VariableSizeClusterFinder.hpp:35
void set_noiseMap(NDView< T, 2 > noise_map)
Definition VariableSizeClusterFinder.hpp:59
std::unordered_map< int, Hit > h_size
Definition VariableSizeClusterFinder.hpp:46
bool use_noise_map
Definition VariableSizeClusterFinder.hpp:38
void print_connections()
Definition VariableSizeClusterFinder.hpp:79
std::map< int, int > child
Definition VariableSizeClusterFinder.hpp:45
void find_clusters_X(NDView< T, 2 > img)
Definition VariableSizeClusterFinder.hpp:159
int peripheralThresholdFactor_
Definition VariableSizeClusterFinder.hpp:39
const std::array< int, 8 > di_
Definition VariableSizeClusterFinder.hpp:43
Definition NDArray.hpp:23
Definition NDView.hpp:46
Frame class to represent a single frame of data model class should be able to work with streams comin...
Definition CircularFifo.hpp:11
Definition VariableSizeClusterFinder.hpp:15
int16_t rows[MAX_CLUSTER_SIZE]
Definition VariableSizeClusterFinder.hpp:25
uint16_t reserved
Definition VariableSizeClusterFinder.hpp:19
int16_t size
Definition VariableSizeClusterFinder.hpp:16
int16_t cols[MAX_CLUSTER_SIZE]
Definition VariableSizeClusterFinder.hpp:26
int16_t row
Definition VariableSizeClusterFinder.hpp:17
double enes[MAX_CLUSTER_SIZE]
Definition VariableSizeClusterFinder.hpp:27
T max
Definition VariableSizeClusterFinder.hpp:21
T energy
Definition VariableSizeClusterFinder.hpp:20
int16_t col
Definition VariableSizeClusterFinder.hpp:18