SourceXtractorPlusPlus 0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
MultiThresholdPartitionStep.cpp
Go to the documentation of this file.
1
17/*
18 * MultiThresholdPartitionStep.cpp
19 *
20 * Created on: Jan 17, 2017
21 * Author: mschefer
22 */
23
24#include <boost/random.hpp>
25
28
31
38
40
42
43namespace SourceXtractor {
44
45namespace {
46 boost::random::mt19937 rng { ((unsigned int) time(NULL)) };
47}
48
49class MultiThresholdNode : public std::enable_shared_from_this<MultiThresholdNode> {
50public:
51
55
60
62 for (auto pixel : m_pixel_list) {
63 if (pixel_group.pixel_list[0] == pixel) {
64 return true;
65 }
66 }
67 return false;
68 }
69
73
75 return m_parent.lock();
76 }
77
80 for (const auto& pixel_coord : m_pixel_list) {
81 total_intensity += (image.getValue(pixel_coord - offset) - m_threshold);
82 }
83
84 return total_intensity;
85 }
86
87 bool isSplit() const {
88 return m_is_split;
89 }
90
91 void flagAsSplit() {
92 m_is_split = true;
93 auto parent = m_parent.lock();
94 if (parent != nullptr) {
95 parent->flagAsSplit();
96 }
97 }
98
100 return m_pixel_list;
101 }
102
103 void debugPrint() const {
104 std::cout << "(" << m_pixel_list.size();
105
106 for (auto& child : m_children) {
107 std::cout << ", ";
108 child->debugPrint();
109 }
110
111 std::cout << ")";
112 }
113
115 m_pixel_list.emplace_back(pixel);
116 }
117
119 return m_threshold;
120 }
121
122private:
124
127
129
131};
132
135
136 auto parent_source_id = original_source->getProperty<SourceId>().getSourceId();
137
138 auto& detection_frame = original_source->getProperty<DetectionFrame>();
139
141 const auto labelling_image = detection_frame_images.getLockedImage(LayerFilteredImage);
142
143 auto& pixel_boundaries = original_source->getProperty<PixelBoundaries>();
144
145 auto& pixel_coords = original_source->getProperty<PixelCoordinateList>().getCoordinateList();
146
147 auto offset = pixel_boundaries.getMin();
149 pixel_boundaries.getWidth(), pixel_boundaries.getHeight());
150 thumbnail_image->fillValue(0);
151
152 auto min_value = original_source->getProperty<PeakValue>().getMinValue() * .8;
153 auto peak_value = original_source->getProperty<PeakValue>().getMaxValue();
154
155 for (auto pixel_coord : pixel_coords) {
156 auto value = labelling_image->getValue(pixel_coord);
157 thumbnail_image->setValue(pixel_coord - offset, value);
158 }
159
161
164
165 // Build the tree
166 for (unsigned int i = 1; i < m_thresholds_nb; i++) {
167
170
172 lutz.labelImage(*subtracted_image, offset);
173
175 for (auto& node : active_nodes_copy) {
176 int nb_of_groups_inside = 0;
177 for (auto& pixel_group : lutz.getGroups()) {
178 if (pixel_group.pixel_list.size() >= m_min_deblend_area && node->contains(pixel_group)) {
180 }
181 }
182
183 if (nb_of_groups_inside == 0) {
184 active_nodes.remove(node);
185 }
186
187 if (nb_of_groups_inside > 1) {
188 active_nodes.remove(node);
189 junction_nodes.push_back(node);
190 for (auto& pixel_group : lutz.getGroups()) {
191 if (pixel_group.pixel_list.size() >= m_min_deblend_area && node->contains(pixel_group)) {
193 node->addChild(new_node);
194 active_nodes.push_back(new_node);
195 }
196 }
197 }
198 }
199 }
200
201 // Identify the sources
202 double intensity_threshold = root->getTotalIntensity(*thumbnail_image, offset) * m_contrast;
203
205 while (!junction_nodes.empty()) {
206 auto node = junction_nodes.back();
207 junction_nodes.pop_back();
208
210
211 for (auto child : node->getChildren()) {
212 if (child->getTotalIntensity(*thumbnail_image, offset) > intensity_threshold) {
214 }
215 }
216
218 node->flagAsSplit();
219 for (auto child : node->getChildren()) {
220 if (child->getTotalIntensity(*thumbnail_image, offset) > intensity_threshold && !child->isSplit()) {
221 source_nodes.push_back(child);
222 }
223 }
224 }
225 }
226
228 if (source_nodes.empty()) {
229 sources.emplace_back(std::move(original_source)); // no split, just forward the source unchanged
230 return sources;
231 }
232
233 for (auto source_node : source_nodes) {
234 // remove pixels in the new sources from the image
235 for (auto& pixel : source_node->getPixels()) {
236 thumbnail_image->setValue(pixel - offset, 0);
237 }
238
239 auto new_source = m_source_factory->createSource();
240
241 new_source->setProperty<PixelCoordinateList>(source_node->getPixels());
242 new_source->setProperty<DetectionFrame>(detection_frame.getEncapsulatedFrame());
243
244 sources.push_back(std::move(new_source));
245 }
246
248
249 for (auto& new_source : new_sources) {
250 new_source->setProperty<DetectionFrame>(detection_frame.getEncapsulatedFrame());
251 new_source->setProperty<SourceId>(parent_source_id);
252 }
253
254 return new_sources;
255}
256
262 const PixelCoordinate& offset
263 ) const {
264
266 for (auto& source : sources) {
267 auto& pixel_list = source->getProperty<PixelCoordinateList>().getCoordinateList();
268 auto& shape_parameters = source->getProperty<ShapeParameters>();
269
270 auto thresh = source->getProperty<PeakValue>().getMinValue();
271 auto peak = source->getProperty<PeakValue>().getMaxValue();
272
273 auto dist = pixel_list.size() / (2.0 * M_PI * shape_parameters.getAbcor() * shape_parameters.getEllipseA() * shape_parameters.getEllipseB());
274 auto amp = dist < 70.0 ? thresh * expf(dist) : 4.0 * peak;
275
276 // limit expansion ??
277 if (amp > 4.0 * peak) {
278 amp = 4.0 * peak;
279 }
280
281 amplitudes.push_back(amp);
282 }
283
284 for (auto pixel : pixel_coords) {
285 if (image->getValue(pixel - offset) > 0) {
287 std::vector<SeFloat> probabilities;
288
291
292 int i = 0;
293 for (auto& source : sources) {
294 auto& shape_parameters = source->getProperty<ShapeParameters>();
295 auto& pixel_centroid = source->getProperty<PixelCentroid>();
296
297 auto dx = pixel.m_x - pixel_centroid.getCentroidX();
298 auto dy = pixel.m_y - pixel_centroid.getCentroidY();
299
300 auto dist = 0.5 * (shape_parameters.getEllipseCxx()*dx*dx +
301 shape_parameters.getEllipseCyy()*dy*dy + shape_parameters.getEllipseCxy()*dx*dy) /
302 shape_parameters.getAbcor();
303
304 if (dist < min_dist) {
305 min_dist = dist;
307 }
308
309 cumulated_probability += dist < 70.0 ? amplitudes[i] * expf(-dist) : 0.0;
310
311 probabilities.push_back(cumulated_probability);
312 i++;
313 }
314
315 if (probabilities.back() > 1.0e-31) {
316 auto drand = double(probabilities.back()) * boost::random::uniform_01<double>()(rng);
317
318 unsigned int i=0;
319 for (; i<probabilities.size() && drand >= probabilities[i]; i++);
320 if (i < source_nodes.size()) {
321 source_nodes[i]->addPixel(pixel);
322 } else {
323 std::cout << i << " oops " << drand << " " << probabilities.back() << std::endl;
324 }
325
326 } else {
327 // select closest source
328 closest_source_node->addPixel(pixel);
329 }
330 }
331 }
332
333 int total_pixels = 0;
334
336 for (auto source_node : source_nodes) {
337 // remove pixels in the new sources from the image
338 for (auto& pixel : source_node->getPixels()) {
339 image->setValue(pixel - offset, 0);
340 }
341
342 auto new_source = m_source_factory->createSource();
343
344 auto pixels = source_node->getPixels();
345 total_pixels += pixels.size();
346
348
349 new_sources.push_back(std::move(new_source));
350 }
351
352 return new_sources;
353}
354
356 unsigned int thresholds_nb, unsigned int min_deblend_area) :
357 m_source_factory(source_factory), m_contrast(contrast), m_thresholds_nb(thresholds_nb),
358 m_min_deblend_area(min_deblend_area) {}
359
360} // namespace
std::shared_ptr< SourceFactory > m_source_factory
std::shared_ptr< EngineParameter > dx
std::shared_ptr< EngineParameter > dy
bool contains(const Lutz::PixelGroup &pixel_group) const
std::weak_ptr< MultiThresholdNode > m_parent
void addChild(std::shared_ptr< MultiThresholdNode > child)
std::vector< std::shared_ptr< MultiThresholdNode > > m_children
MultiThresholdNode(const std::vector< PixelCoordinate > &pixel_list, SeFloat threshold)
const std::vector< std::shared_ptr< MultiThresholdNode > > & getChildren() const
double getTotalIntensity(VectorImage< DetectionImage::PixelType > &image, const PixelCoordinate &offset) const
std::shared_ptr< MultiThresholdNode > getParent() const
const std::vector< PixelCoordinate > & getPixels() const
std::vector< std::unique_ptr< SourceInterface > > reassignPixels(const std::vector< std::unique_ptr< SourceInterface > > &sources, const std::vector< PixelCoordinate > &pixel_coords, std::shared_ptr< VectorImage< DetectionImage::PixelType > > image, const std::vector< std::shared_ptr< MultiThresholdNode > > &source_nodes, const PixelCoordinate &offset) const
virtual std::vector< std::unique_ptr< SourceInterface > > partition(std::unique_ptr< SourceInterface > source) const
MultiThresholdPartitionStep(std::shared_ptr< SourceFactory > source_factory, SeFloat contrast, unsigned int thresholds_nb, unsigned int min_deblend_area)
The bounding box of all the pixels in the source. Both min and max coordinate are inclusive.
The centroid of all the pixels in the source, weighted by their DetectionImage pixel values.
static std::shared_ptr< VectorImage< T > > create(Args &&... args)
T endl(T... args)
T max(T... args)
T move(T... args)
@ LayerFilteredImage
Definition Frame.h:40
SeFloat32 SeFloat
Definition Types.h:32
T pow(T... args)
A pixel coordinate made of two integers m_x and m_y.
T time(T... args)