From 20c7cbba1f5c4e32c36cbf33bba6dbb79a8c7112 Mon Sep 17 00:00:00 2001 From: Rodrigo Tobar Date: Tue, 9 Feb 2021 09:32:26 +0800 Subject: [PATCH] Add new evaluate() overload to Model Until now when users evaluate a Model, the Model internally creates the Image object that will hold the result, and then returns it to the user. This way, each time a new evaluation happens, a new Image object is created, then eventually destroyed. Although for small images this doesn't cause too much of an overhead, for bigger images where allocation times are noticeable this can become one of the driving costs (e.g., a 10k x 10k Image object takes ~350 ms to be created). To help take that cost down, and allow users reuse allocated memory/Images, this commit adds a new evaluate() overload that takes in an Image reference rather than returning one. The given image is checked, and if its size is exactly that needed by the Model internal evaluation routines then it is simply zero'd out and then used for drawing pixels on. If on the other hand the image's size is not exactly that needed by the Model, a new one is created with the required dimensions and used instead. With these changes, the existing evaluate() overload becomes a thin wrapper around the new one, creating an empty Image object, passing it to the new overload, then returning it to the user. Signed-off-by: Rodrigo Tobar --- docs/changelog.rst | 6 ++++++ src/profit/model.cpp | 29 +++++++++++++++++++++-------- src/profit/model.h | 7 ++++++- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 243e2cd..3be72f7 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -7,6 +7,12 @@ Changelog .. rubric:: Development version +* :class:`Model` offers a new flavour of the ``evaluate`` function + where users can give a pre-existing :class:`Image` object + on which the result will be written. + If this user-provided :class:`Image` object + is not of the size internally needed by :class:`Model` + then it will be internally resized automatically. * A new ``get_drawing_dimensions`` method has been added to the :class:`Model` class. * A new ``null`` convolver has been added diff --git a/src/profit/model.cpp b/src/profit/model.cpp index 5a53c37..6cf3426 100644 --- a/src/profit/model.cpp +++ b/src/profit/model.cpp @@ -262,23 +262,36 @@ Dimensions Model::get_drawing_dimensions() const } Image Model::evaluate(Point &offset_out) +{ + Image image; + evaluate(image, offset_out); + return image; +} + +void Model::evaluate(Image &image, Point &offset_out) { auto analysis = analyze_inputs(); + if (image.getDimensions() == analysis.drawing_dims) { + image.zero(); + } + else { + image = Image{analysis.drawing_dims}; + } + /* so long folks! */ if (dry_run) { inform_offset({0, 0}, offset_out); - return Image{analysis.drawing_dims}; + return; } // Adjust mask before passing it down to profiles Point offset; - Image image; Mask adjusted_mask; if (adjust_mask && analysis.mask_needs_adjustment) { adjusted_mask = mask; adjust(adjusted_mask, psf, finesampling, analysis); - image = produce_image(adjusted_mask, analysis, offset); + produce_image(image, adjusted_mask, analysis, offset); } else { if (!adjust_mask && mask.getDimensions() != analysis.drawing_dims) { @@ -287,7 +300,7 @@ Image Model::evaluate(Point &offset_out) << mask.getDimensions() << " != " << analysis.drawing_dims; throw invalid_parameter(os.str()); } - image = produce_image(mask, analysis, offset); + produce_image(image, mask, analysis, offset); } // Remove PSF padding if one was added, and downsample if necessary @@ -321,14 +334,15 @@ Image Model::evaluate(Point &offset_out) } inform_offset(offset, offset_out); - return image; + return; } -Image Model::produce_image(const Mask &mask, const input_analysis &analysis, +void Model::produce_image(Image &model_image, const Mask &mask, const input_analysis &analysis, Point &offset) { + assert(model_image.getDimensions() == analysis.drawing_dims); + // Avoiding memory allocation if no convolution is needed - Image model_image{analysis.drawing_dims}; Image to_convolve; if (analysis.convolution_required) { to_convolve = Image{analysis.drawing_dims}; @@ -361,7 +375,6 @@ Image Model::produce_image(const Mask &mask, const input_analysis &analysis, } /* Done! Good job :-) */ - return model_image; } std::map> Model::get_stats() const { diff --git a/src/profit/model.h b/src/profit/model.h index 8a88603..d222260 100644 --- a/src/profit/model.h +++ b/src/profit/model.h @@ -112,6 +112,11 @@ class PROFIT_API Model { */ Image evaluate(Point &offset_out = NO_OFFSET); + /** + * Like evaluate(Point &), but the user provides an Image to write data on. + */ + void evaluate(Image &image, Point &offset_out = NO_OFFSET); + /** * Returns the dimensions that this model will need to use internally when * drawing profile images, considering any effects like PSF padding, @@ -395,7 +400,7 @@ class PROFIT_API Model { ProfilePtr make_profile(const std::string &name); // Actually produce the image from the profiles and convolve it against the psf - Image produce_image(const Mask &mask, const input_analysis &analysis, Point &offset); + void produce_image(Image &model_image, const Mask &mask, const input_analysis &analysis, Point &offset); // Analyze the model's inputs and produce information needed by other steps input_analysis analyze_inputs() const;