SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
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 
43 namespace SourceXtractor {
44 
45 namespace {
46  boost::random::mt19937 rng { ((unsigned int) time(NULL)) };
47 }
48 
49 class MultiThresholdNode : public std::enable_shared_from_this<MultiThresholdNode> {
50 public:
51 
53  : m_pixel_list(pixel_list), m_is_split(false), m_threshold(threshold) {
54  }
55 
57  m_children.push_back(child);
58  child->m_parent = shared_from_this();
59  }
60 
61  bool contains(const Lutz::PixelGroup& pixel_group) const {
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 
71  return m_children;
72  }
73 
75  return m_parent.lock();
76  }
77 
79  DetectionImage::PixelType total_intensity = 0;
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 
114  void addPixel(PixelCoordinate pixel) {
115  m_pixel_list.emplace_back(pixel);
116  }
117 
119  return m_threshold;
120  }
121 
122 private:
124 
127 
129 
131 };
132 
134  std::unique_ptr<SourceInterface> original_source) const {
135 
136  auto parent_source_id = original_source->getProperty<SourceId>().getSourceId();
137 
138  auto& detection_frame = original_source->getProperty<DetectionFrame>();
139 
140  auto& detection_frame_images = original_source->getProperty<DetectionFrameImages>();
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();
148  auto thumbnail_image = VectorImage<DetectionImage::PixelType>::create(
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 
160  auto root = std::make_shared<MultiThresholdNode>(pixel_coords, 0);
161 
162  std::list<std::shared_ptr<MultiThresholdNode>> active_nodes { root };
164 
165  // Build the tree
166  for (unsigned int i = 1; i < m_thresholds_nb; i++) {
167 
168  auto threshold = min_value * pow(peak_value / min_value, (double) i / m_thresholds_nb);
169  auto subtracted_image = SubtractImage<DetectionImage::PixelType>::create(thumbnail_image, threshold);
170 
171  LutzList lutz;
172  lutz.labelImage(*subtracted_image, offset);
173 
174  std::list<std::shared_ptr<MultiThresholdNode>> active_nodes_copy(active_nodes);
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)) {
179  nb_of_groups_inside++;
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)) {
192  auto new_node = std::make_shared<MultiThresholdNode>(pixel_group.pixel_list, threshold);
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 
209  int nb_of_children_above_threshold = 0;
210 
211  for (auto child : node->getChildren()) {
212  if (child->getTotalIntensity(*thumbnail_image, offset) > intensity_threshold) {
213  nb_of_children_above_threshold++;
214  }
215  }
216 
217  if (nb_of_children_above_threshold >= 2) {
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 
247  auto new_sources = reassignPixels(sources, pixel_coords, thumbnail_image, source_nodes, offset);
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 
259  const std::vector<PixelCoordinate>& pixel_coords,
262  const PixelCoordinate& offset
263  ) const {
264 
265  std::vector<SeFloat> amplitudes;
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) {
286  SeFloat cumulated_probability = 0;
287  std::vector<SeFloat> probabilities;
288 
290  std::shared_ptr<MultiThresholdNode> closest_source_node;
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;
306  closest_source_node = source_nodes[i];
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 
347  new_source->setProperty<PixelCoordinateList>(pixels);
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
T back(T... args)
const std::vector< PixelGroup > & getGroups() const
Definition: Lutz.h:71
void labelImage(const DetectionImage &image, PixelCoordinate offset=PixelCoordinate(0, 0))
Definition: Lutz.h:75
std::vector< PixelCoordinate > pixel_list
Definition: Lutz.h:44
bool contains(const Lutz::PixelGroup &pixel_group) const
std::shared_ptr< MultiThresholdNode > getParent() 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< PixelCoordinate > & getPixels() const
double getTotalIntensity(VectorImage< DetectionImage::PixelType > &image, const PixelCoordinate &offset) const
const std::vector< std::shared_ptr< MultiThresholdNode > > & getChildren() 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.
Definition: PixelCentroid.h:37
static std::shared_ptr< ProcessedImage< T, P > > 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)
Definition: VectorImage.h:100
T getValue(int x, int y) const
Definition: VectorImage.h:116
T emplace_back(T... args)
T empty(T... args)
T endl(T... args)
T max(T... args)
T move(T... args)
SeFloat32 SeFloat
Definition: Types.h:32
@ LayerFilteredImage
Definition: Frame.h:40
T pop_back(T... args)
T pow(T... args)
T push_back(T... args)
T size(T... args)
A pixel coordinate made of two integers m_x and m_y.
T time(T... args)