SourceXtractorPlusPlus 0.19.2
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
47
48
54
58
60
61namespace SourceXtractor {
62
63using namespace ModelFitting;
64
65static auto logger = Elements::Logging::getLogger("FlexibleModelFitting");
66
76
79 return stamp_rect.getWidth() > 0 && stamp_rect.getHeight() > 0;
80}
81
84 const auto& frame_images = group.begin()->getProperty<MeasurementFrameImages>(frame_index);
87 LayerSubtractedImage, rect.getTopLeft().m_x, rect.getTopLeft().m_y, rect.getWidth(), rect.getHeight()));
88
89 return image;
90}
91
94 const auto& frame_images = group.begin()->getProperty<MeasurementFrameImages>(frame_index);
95
96 auto frame_image = frame_images.getLockedImage(LayerSubtractedImage);
98 auto variance_map = frame_images.getLockedImage(LayerVarianceMap);
99
100 const auto& frame_info = group.begin()->getProperty<MeasurementFrameInfo>(frame_index);
101 SeFloat gain = frame_info.getGain();
102 SeFloat saturation = frame_info.getSaturation();
103
105 auto weight = VectorImage<SeFloat>::create(rect.getWidth(), rect.getHeight());
106
107 for (int y = 0; y < rect.getHeight(); y++) {
108 for (int x = 0; x < rect.getWidth(); x++) {
109 auto back_var = variance_map->getValue(rect.getTopLeft().m_x + x, rect.getTopLeft().m_y + y);
110 auto pixel_val = frame_image->getValue(rect.getTopLeft().m_x + x, rect.getTopLeft().m_y + y);
111 if (saturation > 0 && pixel_val > saturation) {
112 weight->at(x, y) = 0;
113 }
114 else if (gain > 0.0 && pixel_val > 0.0) {
115 weight->at(x, y) = sqrt(1.0 / (back_var + pixel_val / gain));
116 }
117 else {
118 weight->at(x, y) = sqrt(1.0 / back_var); // infinite gain
119 }
120 }
121 }
122
123 return weight;
124}
125
130
131 int frame_index = frame->getFrameNb();
132
133 auto frame_coordinates =
134 group.begin()->getProperty<MeasurementFrameCoordinates>(frame_index).getCoordinateSystem();
135 auto ref_coordinates =
136 group.begin()->getProperty<DetectionFrameCoordinates>().getCoordinateSystem();
137
139 auto psf_property = group.getProperty<PsfProperty>(frame_index);
140 auto jacobian = group.getProperty<JacobianGroup>(frame_index).asTuple();
141
142 if (psf_property.getPsf() == nullptr) {
143 throw Elements::Exception() << "Missing PSF. No PSF mode is not supported in legacy model fitting";
144 }
145
146 // The model fitting module expects to get a PSF with a pixel scale, but we have the pixel sampling step size
147 // It will be used to compute the rastering grid size, and after convolving with the PSF the result will be
148 // downscaled before copied into the frame image.
149 // We can multiply here then, as the unit is pixel/pixel, rather than "/pixel or similar
150 auto group_psf = ImagePsf(pixel_scale * psf_property.getPixelSampling(), psf_property.getPsf());
151
155
156 for (auto& source : group) {
157 for (auto model : frame->getModels()) {
159 stamp_rect.getTopLeft());
160 }
161 }
162
163 // Full frame model with all sources
165 pixel_scale, (size_t) stamp_rect.getWidth(), (size_t) stamp_rect.getHeight(),
167
168 return frame_model;
169}
170
171
173 double pixel_scale = 1 / m_scale_factor;
176 int n_free_parameters = 0;
177
178 // Prepare parameters
179 for (auto& source : group) {
180 for (auto parameter : m_parameters) {
183 }
184 parameter_manager.addParameter(source, parameter,
186 }
187 }
188
189 try {
190 // Reset access checks, as a dependent parameter could have triggered it
191 parameter_manager.clearAccessCheck();
192
193 // Add models for all frames
195
196 int valid_frames = 0;
197 int n_good_pixels = 0;
198 for (auto frame : m_frames) {
199 int frame_index = frame->getFrameNb();
200 // Validate that each frame covers the model fitting region
202 valid_frames++;
203
205
207 auto weight = createWeightImage(group, frame_index);
208
209 for (int y = 0; y < weight->getHeight(); ++y) {
210 for (int x = 0; x < weight->getWidth(); ++x) {
211 n_good_pixels += (weight->at(x, y) != 0.);
212 }
213 }
214
215 // Setup residuals
216 auto data_vs_model =
218 //LogChiSquareComparator(m_modified_chi_squared_scale));
220 res_estimator.registerBlockProvider(std::move(data_vs_model));
221 }
222 }
223
224 // Check that we had enough data for the fit
226 if (valid_frames == 0) {
228 }
229 else if (n_good_pixels < n_free_parameters) {
231 }
232
233 if (group_flags != Flags::NONE) {
235 return;
236 }
237
238 // Add priors
239 for (auto& source : group) {
240 for (auto prior : m_priors) {
242 }
243 }
244
245 // Model fitting
246
247 // FIXME we can no longer specify different settings with LeastSquareEngineManager!!
248 // LevmarEngine engine{m_max_iterations, 1E-3, 1E-6, 1E-6, 1E-6, 1E-4};
251 auto iterations = solution.iteration_no;
252 auto stop_reason = solution.engine_stop_reason;
253 switch (solution.status_flag) {
256 break;
259 break;
260 default:
261 break;
262 }
263
264 int total_data_points = 0;
266
267 int nb_of_free_parameters = 0;
268 for (auto& source : group) {
269 for (auto parameter : m_parameters) {
274 }
275 }
276 }
278
279 // Collect parameters for output
280 for (auto& source : group) {
283
284 for (auto parameter : m_parameters) {
289
291 parameter_values[parameter->getId()] = modelfitting_parameter->getValue();
292 parameter_sigmas[parameter->getId()] = parameter->getSigma(parameter_manager, source, solution.parameter_sigmas);
293 }
294 else {
295 // Need to cascade the NaN to any potential dependent parameter
297 if (engine_parameter) {
299 }
301 parameter_sigmas[parameter->getId()] = std::numeric_limits<double>::quiet_NaN();
303 }
304 }
305 source.setProperty<FlexibleModelFitting>(iterations, stop_reason,
307 parameter_values, parameter_sigmas,
309 std::vector<int>({(int) iterations}), (int) 1);
310 }
312
313 }
314 catch (const Elements::Exception& e) {
315 logger.error() << "An exception occured during model fitting: " << e.what();
317 }
318}
319
320// Used to set a dummy property in case of error that contains no result but just an error flag
339
342
343 int frame_id = 0;
344 for (auto frame : m_frames) {
345 int frame_index = frame->getFrameNb();
346 // Validate that each frame covers the model fitting region
349 auto final_stamp = frame_model.getImage();
350
352
353 auto debug_image = CheckImages::getInstance().getModelFittingImage(frame_index);
354 if (debug_image) {
356 for (int x = 0; x < final_stamp->getWidth(); x++) {
357 for (int y = 0; y < final_stamp->getHeight(); y++) {
358 auto x_coord = stamp_rect.getTopLeft().m_x + x;
359 auto y_coord = stamp_rect.getTopLeft().m_y + y;
360 debug_image->setValue(x_coord, y_coord,
361 debugAccessor.getValue(x_coord, y_coord) + final_stamp->getValue(x, y));
362 }
363 }
364 }
365 }
366 frame_id++;
367 }
368}
369
372 double reduced_chi_squared = 0.0;
373 data_points = 0;
374
377
378 for (int y=0; y < image->getHeight(); y++) {
379 for (int x=0; x < image->getWidth(); x++) {
380 double tmp = imageAccessor.getValue(x, y) - modelAccessor.getValue(x, y);
381 reduced_chi_squared += tmp * tmp * weightAccessor.getValue(x, y) * weightAccessor.getValue(x, y);
382 if (weightAccessor.getValue(x, y) > 0) {
383 data_points++;
384 }
385 }
386 }
387 return reduced_chi_squared;
388}
389
392
395 int valid_frames = 0;
396 for (auto frame : m_frames) {
397 int frame_index = frame->getFrameNb();
398 // Validate that each frame covers the model fitting region
400 valid_frames++;
402 auto final_stamp = frame_model.getImage();
403
406 auto weight = createWeightImage(group, frame_index);
407
408 int data_points = 0;
410 image, final_stamp, weight, data_points);
411
414 }
415 }
416
417 return total_chi_squared;
418}
419
422
423}
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 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.
SeFloat32 SeFloat
Definition Types.h:32
static StaticPlugin< SourceFlagsPlugin > source_flags
@ LayerVarianceMap
Definition Frame.h:45
@ LayerThresholdedImage
Definition Frame.h:41
@ LayerSubtractedImage
Definition Frame.h:39
T quiet_NaN(T... args)
T sqrt(T... args)