SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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
T empty(T...args)
double getTotalIntensity(VectorImage< DetectionImage::PixelType > &image, const PixelCoordinate &offset) const
std::vector< PixelCoordinate > pixel_list
Definition: Lutz.h:44
The bounding box of all the pixels in the source. Both min and max coordinate are inclusive...
const std::vector< std::shared_ptr< MultiThresholdNode > > & getChildren() const
MultiThresholdNode(const std::vector< PixelCoordinate > &pixel_list, SeFloat threshold)
std::weak_ptr< MultiThresholdNode > m_parent
T endl(T...args)
SeFloat32 SeFloat
Definition: Types.h:32
static std::shared_ptr< VectorImage< T > > create(Args &&...args)
Definition: VectorImage.h:100
The centroid of all the pixels in the source, weighted by their DetectionImage pixel values...
Definition: PixelCentroid.h:37
void labelImage(const DetectionImage &image, PixelCoordinate offset=PixelCoordinate(0, 0))
Definition: Lutz.h:75
T time(T...args)
T push_back(T...args)
std::shared_ptr< SourceFactory > m_source_factory
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
bool contains(const Lutz::PixelGroup &pixel_group) const
std::shared_ptr< MultiThresholdNode > getParent() const
Image implementation which keeps the pixel values in memory.
Definition: VectorImage.h:52
void addChild(std::shared_ptr< MultiThresholdNode > child)
T pop_back(T...args)
T getValue(int x, int y) const
Definition: VectorImage.h:116
MultiThresholdPartitionStep(std::shared_ptr< SourceFactory > source_factory, SeFloat contrast, unsigned int thresholds_nb, unsigned int min_deblend_area)
const std::vector< PixelGroup > & getGroups() const
Definition: Lutz.h:71
STL class.
A pixel coordinate made of two integers m_x and m_y.
T move(T...args)
T size(T...args)
STL class.
STL class.
STL class.
const std::vector< PixelCoordinate > & getPixels() const
T pow(T...args)
T back(T...args)
std::vector< std::shared_ptr< MultiThresholdNode > > m_children
virtual std::vector< std::unique_ptr< SourceInterface > > partition(std::unique_ptr< SourceInterface > source) const
std::shared_ptr< EngineParameter > dx
std::shared_ptr< EngineParameter > dy
T emplace_back(T...args)
static std::shared_ptr< ProcessedImage< T, P > > create(std::shared_ptr< const Image< T >> image_a, std::shared_ptr< const Image< T >> image_b)