SourceXtractorPlusPlus  0.17
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::shared_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  return { original_source }; // no split, just forward the source unchanged
230  }
231 
232  for (auto source_node : source_nodes) {
233  // remove pixels in the new sources from the image
234  for (auto& pixel : source_node->getPixels()) {
235  thumbnail_image->setValue(pixel - offset, 0);
236  }
237 
238  auto new_source = m_source_factory->createSource();
239 
240  new_source->setProperty<PixelCoordinateList>(source_node->getPixels());
241  new_source->setProperty<DetectionFrame>(detection_frame.getEncapsulatedFrame());
242 
243  sources.push_back(new_source);
244  }
245 
246  auto new_sources = reassignPixels(sources, pixel_coords, thumbnail_image, source_nodes, offset);
247 
248  for (auto& new_source : new_sources) {
249  new_source->setProperty<DetectionFrame>(detection_frame.getEncapsulatedFrame());
250  new_source->setProperty<SourceId>(parent_source_id);
251  }
252 
253  return new_sources;
254 }
255 
258  const std::vector<PixelCoordinate>& pixel_coords,
261  const PixelCoordinate& offset
262  ) const {
263 
264  std::vector<SeFloat> amplitudes;
265  for (auto& source : sources) {
266  auto& pixel_list = source->getProperty<PixelCoordinateList>().getCoordinateList();
267  auto& shape_parameters = source->getProperty<ShapeParameters>();
268 
269  auto thresh = source->getProperty<PeakValue>().getMinValue();
270  auto peak = source->getProperty<PeakValue>().getMaxValue();
271 
272  auto dist = pixel_list.size() / (2.0 * M_PI * shape_parameters.getAbcor() * shape_parameters.getEllipseA() * shape_parameters.getEllipseB());
273  auto amp = dist < 70.0 ? thresh * expf(dist) : 4.0 * peak;
274 
275  // limit expansion ??
276  if (amp > 4.0 * peak) {
277  amp = 4.0 * peak;
278  }
279 
280  amplitudes.push_back(amp);
281  }
282 
283  for (auto pixel : pixel_coords) {
284  if (image->getValue(pixel - offset) > 0) {
285  SeFloat cumulated_probability = 0;
286  std::vector<SeFloat> probabilities;
287 
289  std::shared_ptr<MultiThresholdNode> closest_source_node;
290 
291  int i = 0;
292  for (auto& source : sources) {
293  auto& shape_parameters = source->getProperty<ShapeParameters>();
294  auto& pixel_centroid = source->getProperty<PixelCentroid>();
295 
296  auto dx = pixel.m_x - pixel_centroid.getCentroidX();
297  auto dy = pixel.m_y - pixel_centroid.getCentroidY();
298 
299  auto dist = 0.5 * (shape_parameters.getEllipseCxx()*dx*dx +
300  shape_parameters.getEllipseCyy()*dy*dy + shape_parameters.getEllipseCxy()*dx*dy) /
301  shape_parameters.getAbcor();
302 
303  if (dist < min_dist) {
304  min_dist = dist;
305  closest_source_node = source_nodes[i];
306  }
307 
308  cumulated_probability += dist < 70.0 ? amplitudes[i] * expf(-dist) : 0.0;
309 
310  probabilities.push_back(cumulated_probability);
311  i++;
312  }
313 
314  if (probabilities.back() > 1.0e-31) {
315  auto drand = double(probabilities.back()) * boost::random::uniform_01<double>()(rng);
316 
317  unsigned int i=0;
318  for (; i<probabilities.size() && drand >= probabilities[i]; i++);
319  if (i < source_nodes.size()) {
320  source_nodes[i]->addPixel(pixel);
321  } else {
322  std::cout << i << " oops " << drand << " " << probabilities.back() << std::endl;
323  }
324 
325  } else {
326  // select closest source
327  closest_source_node->addPixel(pixel);
328  }
329  }
330  }
331 
332  int total_pixels = 0;
333 
335  for (auto source_node : source_nodes) {
336  // remove pixels in the new sources from the image
337  for (auto& pixel : source_node->getPixels()) {
338  image->setValue(pixel - offset, 0);
339  }
340 
341  auto new_source = m_source_factory->createSource();
342 
343  auto pixels = source_node->getPixels();
344  total_pixels += pixels.size();
345 
346  new_source->setProperty<PixelCoordinateList>(pixels);
347 
348  new_sources.push_back(new_source);
349  }
350 
351  return new_sources;
352 }
353 
355  unsigned int thresholds_nb, unsigned int min_deblend_area) :
356  m_source_factory(source_factory), m_contrast(contrast), m_thresholds_nb(thresholds_nb),
357  m_min_deblend_area(min_deblend_area) {}
358 
359 } // 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
virtual std::vector< std::shared_ptr< SourceInterface > > partition(std::shared_ptr< SourceInterface > source) const
std::vector< std::shared_ptr< SourceInterface > > reassignPixels(const std::vector< std::shared_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
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 empty(T... args)
T endl(T... args)
T max(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)