SourceXtractorPlusPlus 1.0.3
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
26
29
36
38
40
41namespace SourceXtractor {
42
43namespace {
44}
45
46class MultiThresholdNode : public std::enable_shared_from_this<MultiThresholdNode> {
47public:
48
50 : m_pixel_list(pixel_list), m_is_split(false), m_threshold(threshold) {
51 }
52
54 m_children.push_back(child);
55 child->m_parent = shared_from_this();
56 }
57
58 bool contains(const Lutz::PixelGroup& pixel_group) const {
59 for (auto pixel : m_pixel_list) {
60 if (pixel_group.pixel_list[0] == pixel) {
61 return true;
62 }
63 }
64 return false;
65 }
66
70
72 return m_parent.lock();
73 }
74
76 DetectionImage::PixelType total_intensity = 0;
77 for (const auto& pixel_coord : m_pixel_list) {
78 total_intensity += (image.getValue(pixel_coord - offset) - m_threshold);
79 }
80
81 return total_intensity;
82 }
83
84 bool isSplit() const {
85 return m_is_split;
86 }
87
88 void flagAsSplit() {
89 m_is_split = true;
90 auto parent = m_parent.lock();
91 if (parent != nullptr) {
92 parent->flagAsSplit();
93 }
94 }
95
97 return m_pixel_list;
98 }
99
100 void debugPrint() const {
101 std::cout << "(" << m_pixel_list.size();
102
103 for (auto& child : m_children) {
104 std::cout << ", ";
105 child->debugPrint();
106 }
107
108 std::cout << ")";
109 }
110
112 m_pixel_list.emplace_back(pixel);
113 }
114
116 return m_threshold;
117 }
118
119private:
121
124
126
128};
129
131 std::unique_ptr<SourceInterface> original_source) const {
132
133 auto parent_source_id = original_source->getProperty<SourceId>().getSourceId();
134
135 auto& detection_frame = original_source->getProperty<DetectionFrame>();
136
137 auto& detection_frame_images = original_source->getProperty<DetectionFrameImages>();
138 const auto labelling_image = detection_frame_images.getLockedImage(LayerFilteredImage);
139
140 auto& pixel_boundaries = original_source->getProperty<PixelBoundaries>();
141
142 auto& pixel_coords = original_source->getProperty<PixelCoordinateList>().getCoordinateList();
143
144 auto offset = pixel_boundaries.getMin();
146 pixel_boundaries.getWidth(), pixel_boundaries.getHeight());
147 thumbnail_image->fillValue(0);
148
149 auto min_value = original_source->getProperty<PeakValue>().getMinValue() * .8;
150 auto peak_value = original_source->getProperty<PeakValue>().getMaxValue();
151
152 for (auto pixel_coord : pixel_coords) {
153 auto value = labelling_image->getValue(pixel_coord);
154 thumbnail_image->setValue(pixel_coord - offset, value);
155 }
156
157 auto root = std::make_shared<MultiThresholdNode>(pixel_coords, 0);
158
161
162 // Build the tree
163 for (unsigned int i = 1; i < m_thresholds_nb; i++) {
164
165 auto threshold = min_value * pow(peak_value / min_value, (double) i / m_thresholds_nb);
166 auto subtracted_image = SubtractImage<DetectionImage::PixelType>::create(thumbnail_image, threshold);
167
168 LutzList lutz;
169 lutz.labelImage(*subtracted_image, offset);
170
171 std::list<std::shared_ptr<MultiThresholdNode>> active_nodes_copy(active_nodes);
172 for (auto& node : active_nodes_copy) {
173 int nb_of_groups_inside = 0;
174 for (auto& pixel_group : lutz.getGroups()) {
175 if (pixel_group.pixel_list.size() >= m_min_deblend_area && node->contains(pixel_group)) {
176 nb_of_groups_inside++;
177 }
178 }
179
180 if (nb_of_groups_inside == 0) {
181 active_nodes.remove(node);
182 }
183
184 if (nb_of_groups_inside > 1) {
185 active_nodes.remove(node);
186 junction_nodes.push_back(node);
187 for (auto& pixel_group : lutz.getGroups()) {
188 if (pixel_group.pixel_list.size() >= m_min_deblend_area && node->contains(pixel_group)) {
189 auto new_node = std::make_shared<MultiThresholdNode>(pixel_group.pixel_list, threshold);
190 node->addChild(new_node);
191 active_nodes.push_back(new_node);
192 }
193 }
194 }
195 }
196 }
197
198 // Identify the sources
199 double intensity_threshold = root->getTotalIntensity(*thumbnail_image, offset) * m_contrast;
200
202 while (!junction_nodes.empty()) {
203 auto node = junction_nodes.back();
204 junction_nodes.pop_back();
205
206 int nb_of_children_above_threshold = 0;
207
208 for (auto child : node->getChildren()) {
209 if (child->getTotalIntensity(*thumbnail_image, offset) > intensity_threshold) {
210 nb_of_children_above_threshold++;
211 }
212 }
213
214 if (nb_of_children_above_threshold >= 2) {
215 node->flagAsSplit();
216 for (auto child : node->getChildren()) {
217 if (child->getTotalIntensity(*thumbnail_image, offset) > intensity_threshold && !child->isSplit()) {
218 source_nodes.push_back(child);
219 }
220 }
221 }
222 }
223
225 if (source_nodes.empty()) {
226 sources.emplace_back(std::move(original_source)); // no split, just forward the source unchanged
227 return sources;
228 }
229
230 for (auto source_node : source_nodes) {
231 // remove pixels in the new sources from the image
232 for (auto& pixel : source_node->getPixels()) {
233 thumbnail_image->setValue(pixel - offset, 0);
234 }
235
236 auto new_source = m_source_factory->createSource();
237
238 new_source->setProperty<PixelCoordinateList>(source_node->getPixels());
239 new_source->setProperty<DetectionFrame>(detection_frame.getEncapsulatedFrame());
240
241 sources.push_back(std::move(new_source));
242 }
243
244 auto new_sources = reassignPixels(sources, pixel_coords, thumbnail_image, source_nodes, offset);
245
246 for (auto& new_source : new_sources) {
247 new_source->setProperty<DetectionFrame>(detection_frame.getEncapsulatedFrame());
248 new_source->setProperty<SourceId>(parent_source_id);
249 }
250
251 return new_sources;
252}
253
256 const std::vector<PixelCoordinate>& pixel_coords,
259 const PixelCoordinate& offset
260 ) const {
261
262 std::vector<SeFloat> amplitudes;
263 for (auto& source : sources) {
264 auto& pixel_list = source->getProperty<PixelCoordinateList>().getCoordinateList();
265 auto& shape_parameters = source->getProperty<ShapeParameters>();
266
267 auto thresh = source->getProperty<PeakValue>().getMinValue();
268 auto peak = source->getProperty<PeakValue>().getMaxValue();
269
270 auto dist = pixel_list.size() / (2.0 * M_PI * shape_parameters.getAbcor() * shape_parameters.getEllipseA() * shape_parameters.getEllipseB());
271 auto amp = dist < 70.0 ? thresh * expf(dist) : 4.0 * peak;
272
273 // limit expansion ??
274 if (amp > 4.0 * peak) {
275 amp = 4.0 * peak;
276 }
277
278 amplitudes.push_back(amp);
279 }
280
281 for (auto pixel : pixel_coords) {
282 if (image->getValue(pixel - offset) > 0) {
283 SeFloat cumulated_probability = 0;
284 std::vector<SeFloat> probabilities;
285
287 std::shared_ptr<MultiThresholdNode> closest_source_node;
288
289 int i = 0;
290 for (auto& source : sources) {
291 auto& shape_parameters = source->getProperty<ShapeParameters>();
292 auto& pixel_centroid = source->getProperty<PixelCentroid>();
293
294 auto dx = pixel.m_x - pixel_centroid.getCentroidX();
295 auto dy = pixel.m_y - pixel_centroid.getCentroidY();
296
297 auto dist = 0.5 * (shape_parameters.getEllipseCxx()*dx*dx +
298 shape_parameters.getEllipseCyy()*dy*dy + shape_parameters.getEllipseCxy()*dx*dy) /
299 shape_parameters.getAbcor();
300
301 if (dist < min_dist) {
302 min_dist = dist;
303 closest_source_node = source_nodes[i];
304 }
305
306 cumulated_probability += dist < 70.0 ? amplitudes[i] * expf(-dist) : 0.0;
307
308 probabilities.push_back(cumulated_probability);
309 i++;
310 }
311
312 if (probabilities.back() > 1.0e-31) {
313 auto drand = double(probabilities.back()) * boost::random::uniform_01<double>()
314 (const_cast<MultiThresholdPartitionStep*>(this)->m_rng);
315
316 unsigned int i=0;
317 for (; i<probabilities.size() && drand >= probabilities[i]; i++);
318 if (i < source_nodes.size()) {
319 source_nodes[i]->addPixel(pixel);
320 } else {
321 std::cout << i << " oops " << drand << " " << probabilities.back() << std::endl;
322 }
323
324 } else {
325 // select closest source
326 closest_source_node->addPixel(pixel);
327 }
328 }
329 }
330
331 int total_pixels = 0;
332
334 for (auto source_node : source_nodes) {
335 // remove pixels in the new sources from the image
336 for (auto& pixel : source_node->getPixels()) {
337 image->setValue(pixel - offset, 0);
338 }
339
340 auto new_source = m_source_factory->createSource();
341
342 auto pixels = source_node->getPixels();
343 total_pixels += pixels.size();
344
345 new_source->setProperty<PixelCoordinateList>(pixels);
346
347 new_sources.push_back(std::move(new_source));
348 }
349
350 return new_sources;
351}
352
354 unsigned int thresholds_nb, unsigned int min_deblend_area, unsigned int seed) :
355 m_source_factory(source_factory), m_contrast(contrast), m_thresholds_nb(thresholds_nb),
356 m_min_deblend_area(min_deblend_area), m_seed(seed), m_rng(seed) {}
357
358} // namespace
T back(T... args)
void labelImage(const DetectionImage &image, PixelCoordinate offset=PixelCoordinate(0, 0))
Definition Lutz.h:75
const std::vector< PixelGroup > & getGroups() const
Definition Lutz.h:71
std::vector< PixelCoordinate > pixel_list
Definition Lutz.h:44
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
MultiThresholdPartitionStep(std::shared_ptr< SourceFactory > source_factory, SeFloat contrast, unsigned int thresholds_nb, unsigned int min_deblend_area, unsigned int seed)
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
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< ProcessedImage< T, SubtractOperation< T > > > create(std::shared_ptr< const Image< T > > image_a, std::shared_ptr< const Image< T > > image_b)
Image implementation which keeps the pixel values in memory.
Definition VectorImage.h:52
static std::shared_ptr< VectorImage< T > > create(Args &&... args)
T getValue(int x, int y) const
T emplace_back(T... args)
T empty(T... args)
T endl(T... args)
T make_shared(T... args)
T max(T... args)
T move(T... args)
@ LayerFilteredImage
Definition Frame.h:40
SeFloat32 SeFloat
Definition Types.h:32
T pop_back(T... args)
T pow(T... args)
T push_back(T... args)
T remove(T... args)
T size(T... args)
A pixel coordinate made of two integers m_x and m_y.