SourceXtractorPlusPlus 0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
FlexibleModelFittingTask.cpp
Go to the documentation of this file.
1
17/*
18 * FlexibleModelFittingTask.cpp
19 *
20 * Created on: Sep 17, 2018
21 * Author: mschefer
22 */
23
24#include <mutex>
25
35
37
40
43
48
49
55
59
61
62namespace SourceXtractor {
63
64using namespace ModelFitting;
65
66static auto logger = Elements::Logging::getLogger("FlexibleModelFitting");
67
77
80 return stamp_rect.getWidth() > 0 && stamp_rect.getHeight() > 0;
81}
82
85 const auto& frame_images = group.begin()->getProperty<MeasurementFrameImages>(frame_index);
88 LayerSubtractedImage, rect.getTopLeft().m_x, rect.getTopLeft().m_y, rect.getWidth(), rect.getHeight()));
89
90 return image;
91}
92
95 const auto& frame_images = group.begin()->getProperty<MeasurementFrameImages>(frame_index);
96
97 auto frame_image = frame_images.getLockedImage(LayerSubtractedImage);
99 auto variance_map = frame_images.getLockedImage(LayerVarianceMap);
100
101 const auto& frame_info = group.begin()->getProperty<MeasurementFrameInfo>(frame_index);
102 SeFloat gain = frame_info.getGain();
103 SeFloat saturation = frame_info.getSaturation();
104
106 auto weight = VectorImage<SeFloat>::create(rect.getWidth(), rect.getHeight());
107
108 for (int y = 0; y < rect.getHeight(); y++) {
109 for (int x = 0; x < rect.getWidth(); x++) {
110 auto back_var = variance_map->getValue(rect.getTopLeft().m_x + x, rect.getTopLeft().m_y + y);
111 auto pixel_val = frame_image->getValue(rect.getTopLeft().m_x + x, rect.getTopLeft().m_y + y);
112 if (saturation > 0 && pixel_val > saturation) {
113 weight->at(x, y) = 0;
114 }
115 else if (gain > 0.0 && pixel_val > 0.0) {
116 weight->at(x, y) = sqrt(1.0 / (back_var + pixel_val / gain));
117 }
118 else {
119 weight->at(x, y) = sqrt(1.0 / back_var); // infinite gain
120 }
121 }
122 }
123
124 return weight;
125}
126
131
132 int frame_index = frame->getFrameNb();
133
134 auto frame_coordinates =
135 group.begin()->getProperty<MeasurementFrameCoordinates>(frame_index).getCoordinateSystem();
136 auto ref_coordinates =
137 group.begin()->getProperty<ReferenceCoordinates>().getCoordinateSystem();
138
140 auto psf_property = group.getProperty<PsfProperty>(frame_index);
141 auto jacobian = group.getProperty<JacobianGroup>(frame_index).asTuple();
142
143 if (psf_property.getPsf() == nullptr) {
144 throw Elements::Exception() << "Missing PSF. No PSF mode is not supported in legacy model fitting";
145 }
146
147 // The model fitting module expects to get a PSF with a pixel scale, but we have the pixel sampling step size
148 // It will be used to compute the rastering grid size, and after convolving with the PSF the result will be
149 // downscaled before copied into the frame image.
150 // We can multiply here then, as the unit is pixel/pixel, rather than "/pixel or similar
151 auto group_psf = ImagePsf(pixel_scale * psf_property.getPixelSampling(), psf_property.getPsf());
152
156
157 double model_size = std::max(stamp_rect.getWidth(), stamp_rect.getHeight());
158 for (auto& source : group) {
159 for (auto model : frame->getModels()) {
162 }
163 }
164
165 // Full frame model with all sources
167 pixel_scale, (size_t) stamp_rect.getWidth(), (size_t) stamp_rect.getHeight(),
169
170 return frame_model;
171}
172
173
175 double pixel_scale = 1 / m_scale_factor;
178 int n_free_parameters = 0;
179
180 // Prepare parameters
181 for (auto& source : group) {
182 for (auto parameter : m_parameters) {
185 }
186 parameter_manager.addParameter(source, parameter,
188 }
189 }
190
191 try {
192 // Reset access checks, as a dependent parameter could have triggered it
193 parameter_manager.clearAccessCheck();
194
195 // Add models for all frames
197
198 int valid_frames = 0;
199 int n_good_pixels = 0;
200 for (auto frame : m_frames) {
201 int frame_index = frame->getFrameNb();
202 // Validate that each frame covers the model fitting region
204 valid_frames++;
205
207
209 auto weight = createWeightImage(group, frame_index);
210
211 for (int y = 0; y < weight->getHeight(); ++y) {
212 for (int x = 0; x < weight->getWidth(); ++x) {
213 n_good_pixels += (weight->at(x, y) != 0.);
214 }
215 }
216
217 // Setup residuals
218 auto data_vs_model =
220 //LogChiSquareComparator(m_modified_chi_squared_scale));
222 res_estimator.registerBlockProvider(std::move(data_vs_model));
223 }
224 }
225
226 // Check that we had enough data for the fit
228 if (valid_frames == 0) {
230 }
231 else if (n_good_pixels < n_free_parameters) {
233 }
234
235 if (group_flags != Flags::NONE) {
237 return;
238 }
239
240 // Add priors
241 for (auto& source : group) {
242 for (auto prior : m_priors) {
244 }
245 }
246
247 // Model fitting
248
249 // FIXME we can no longer specify different settings with LeastSquareEngineManager!!
250 // LevmarEngine engine{m_max_iterations, 1E-3, 1E-6, 1E-6, 1E-6, 1E-4};
253 auto iterations = solution.iteration_no;
254 auto stop_reason = solution.engine_stop_reason;
255 switch (solution.status_flag) {
258 break;
261 break;
262 default:
263 break;
264 }
265
266 int total_data_points = 0;
268
269 int nb_of_free_parameters = 0;
270 for (auto& source : group) {
271 for (auto parameter : m_parameters) {
276 }
277 }
278 }
280
281 // Collect parameters for output
282 for (auto& source : group) {
285
286 for (auto parameter : m_parameters) {
291
293 parameter_values[parameter->getId()] = modelfitting_parameter->getValue();
294 parameter_sigmas[parameter->getId()] = parameter->getSigma(parameter_manager, source, solution.parameter_sigmas);
295 }
296 else {
297 // Need to cascade the NaN to any potential dependent parameter
299 if (engine_parameter) {
301 }
303 parameter_sigmas[parameter->getId()] = std::numeric_limits<double>::quiet_NaN();
305 }
306 }
307 source.setProperty<FlexibleModelFitting>(iterations, stop_reason,
309 parameter_values, parameter_sigmas,
311 std::vector<int>({(int) iterations}), (int) 1);
312 }
314
315 }
316 catch (const Elements::Exception& e) {
317 logger.error() << "An exception occured during model fitting: " << e.what();
319 }
320}
321
322// Used to set a dummy property in case of error that contains no result but just an error flag
341
344
345 int frame_id = 0;
346 for (auto frame : m_frames) {
347 int frame_index = frame->getFrameNb();
348 // Validate that each frame covers the model fitting region
351 auto final_stamp = frame_model.getImage();
352
354
355 auto debug_image = CheckImages::getInstance().getModelFittingImage(frame_index);
356 if (debug_image) {
358 for (int x = 0; x < final_stamp->getWidth(); x++) {
359 for (int y = 0; y < final_stamp->getHeight(); y++) {
360 auto x_coord = stamp_rect.getTopLeft().m_x + x;
361 auto y_coord = stamp_rect.getTopLeft().m_y + y;
362 debug_image->setValue(x_coord, y_coord,
363 debugAccessor.getValue(x_coord, y_coord) + final_stamp->getValue(x, y));
364 }
365 }
366 }
367 }
368 frame_id++;
369 }
370}
371
374 double reduced_chi_squared = 0.0;
375 data_points = 0;
376
379
380 for (int y=0; y < image->getHeight(); y++) {
381 for (int x=0; x < image->getWidth(); x++) {
382 double tmp = imageAccessor.getValue(x, y) - modelAccessor.getValue(x, y);
383 reduced_chi_squared += tmp * tmp * weightAccessor.getValue(x, y) * weightAccessor.getValue(x, y);
384 if (weightAccessor.getValue(x, y) > 0) {
385 data_points++;
386 }
387 }
388 }
389 return reduced_chi_squared;
390}
391
394
397 int valid_frames = 0;
398 for (auto frame : m_frames) {
399 int frame_index = frame->getFrameNb();
400 // Validate that each frame covers the model fitting region
402 valid_frames++;
404 auto final_stamp = frame_model.getImage();
405
408 auto weight = createWeightImage(group, frame_index);
409
410 int data_points = 0;
412 image, final_stamp, weight, data_points);
413
416 }
417 }
418
419 return total_chi_squared;
420}
421
424
425}
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
const double pixel_scale
Definition TestImage.cpp:74
void error(const std::string &logMessage)
static Logging getLogger(const std::string &name="")
Data vs model comparator which computes a modified residual, using asinh.
Class responsible for managing the parameters the least square engine minimizes.
static std::shared_ptr< LeastSquareEngine > create(const std::string &name, unsigned max_iterations=1000)
Provides to the LeastSquareEngine the residual values.
static CheckImages & getInstance()
std::vector< std::shared_ptr< FlexibleModelFittingFrame > > m_frames
void setDummyProperty(SourceGroupInterface &group, FlexibleModelFittingParameterManager &parameter_manager, Flags flags) const
SeFloat computeChiSquared(SourceGroupInterface &group, double pixel_scale, FlexibleModelFittingParameterManager &manager, int &total_data_points) const
ModelFitting::FrameModel< ImagePsf, std::shared_ptr< VectorImage< SourceXtractor::SeFloat > > > createFrameModel(SourceGroupInterface &group, double pixel_scale, FlexibleModelFittingParameterManager &manager, std::shared_ptr< FlexibleModelFittingFrame > frame) const
std::vector< std::shared_ptr< FlexibleModelFittingPrior > > m_priors
void computeProperties(SourceGroupInterface &group) const override
Computes one or more properties for the SourceGroup and/or the Sources it contains.
std::shared_ptr< VectorImage< SeFloat > > createWeightImage(SourceGroupInterface &group, int frame_index) const
FlexibleModelFittingTask(const std::string &least_squares_engine, unsigned int max_iterations, double modified_chi_squared_scale, std::vector< std::shared_ptr< FlexibleModelFittingParameter > > parameters, std::vector< std::shared_ptr< FlexibleModelFittingFrame > > frames, std::vector< std::shared_ptr< FlexibleModelFittingPrior > > priors, double scale_factor=1.0)
std::vector< std::shared_ptr< FlexibleModelFittingParameter > > m_parameters
bool isFrameValid(SourceGroupInterface &group, int frame_index) const
std::shared_ptr< VectorImage< SeFloat > > createImageCopy(SourceGroupInterface &group, int frame_index) const
void updateCheckImages(SourceGroupInterface &group, double pixel_scale, FlexibleModelFittingParameterManager &manager) const
SeFloat computeChiSquaredForFrame(std::shared_ptr< const Image< SeFloat > > image, std::shared_ptr< const Image< SeFloat > > model, std::shared_ptr< const Image< SeFloat > > weights, int &data_points) const
Defines the interface used to group sources.
static std::shared_ptr< VectorImage< T > > create(Args &&... args)
T max(T... args)
T move(T... args)
static Elements::Logging logger
std::unique_ptr< DataVsModelResiduals< typename std::remove_reference< DataType >::type, typename std::remove_reference< ModelType >::type, typename std::remove_reference< WeightType >::type, typename std::remove_reference< Comparator >::type > > createDataVsModelResiduals(DataType &&data, ModelType &&model, WeightType &&weight, Comparator &&comparator)
Flags
Flagging of bad sources.
Definition SourceFlags.h:37
@ MEMORY
Failed to allocate an object, buffer, etc.
@ OUTSIDE
The object is completely outside of the measurement frame.
@ NONE
No flag is set.
@ ERROR
Error flag: something bad happened during the measurement, model fitting, etc.
@ INSUFFICIENT_DATA
There are not enough good pixels to fit the parameters.
@ PARTIAL_FIT
Some/all of the model parameters could not be fitted.
static StaticPlugin< SourceFlagsPlugin > source_flags
@ LayerVarianceMap
Definition Frame.h:45
@ LayerThresholdedImage
Definition Frame.h:41
@ LayerSubtractedImage
Definition Frame.h:39
SeFloat32 SeFloat
Definition Types.h:32
T quiet_NaN(T... args)
T sqrt(T... args)