lsst.gauss2d gbf99507273+b0138be388
 
Loading...
Searching...
No Matches
image.h
1/*
2 * This file is part of gauss2d.
3 *
4 * Developed for the LSST Data Management System.
5 * This product includes software developed by the LSST Project
6 * (https://www.lsst.org).
7 * See the COPYRIGHT file at the top-level directory of this distribution
8 * for details of code ownership.
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24#ifndef LSST_GAUSS2D_PYTHON_IMAGE_H
25#define LSST_GAUSS2D_PYTHON_IMAGE_H
26
27#include <memory>
28#include <stdexcept>
29
30#include <pybind11/pybind11.h>
31#include <pybind11/numpy.h>
32#include <pybind11/operators.h>
33#include <pybind11/stl.h>
34
35#include "lsst/gauss2d/evaluate.h"
36#include "lsst/gauss2d/image.h"
37#include "lsst/gauss2d/string_utils.h"
38#include "lsst/gauss2d/type_name.h"
39
40namespace py = pybind11;
41using namespace pybind11::literals;
42
43namespace lsst::gauss2d::python {
44
45/*
46 Convenience functions for binding concrete image types.
47*/
48
49template <typename T>
50std::string replace_type(std::string target, std::string replacement) {
51 std::string token = "<" + gauss2d::type_name_str<T>() + ">";
52 return replace_all(target, token, replacement);
53}
54
55/*
56 * This suppresses warnings of the form:
57 *
58 * warning: 'lsst::gauss2d::python::Image<double>' declared
59 * with greater visibility than the type of its field
60 * 'lsst::gauss2d::python::Image<double>::_data'
61 *
62 * This may be a pybind11 issue, see:
63 * https://github.com/pybind/pybind11/discussions/4862
64 */
65#pragma GCC visibility push(hidden)
71template <typename T>
72class Image : public lsst::gauss2d::Image<T, Image<T>> {
73public:
74 explicit Image(size_t n_rows, size_t n_cols,
75 const T *value_init = lsst::gauss2d::Image<T, Image<T>>::_value_default_ptr(),
76 const std::shared_ptr<const lsst::gauss2d::CoordinateSystem> coordsys = nullptr)
77 : gauss2d::Image<T, Image<T>>(coordsys),
78 _ptr_own(std::make_unique<py::array_t<T>>(py::array::ShapeContainer({n_rows, n_cols}))),
79 _data(*_ptr_own),
80 _data_ref(_validate().template mutable_unchecked<2>()) {
81 if (value_init != nullptr) {
82 this->fill(*value_init);
83 }
84 }
94 explicit Image(py::array_t<T> data,
95 const std::shared_ptr<const lsst::gauss2d::CoordinateSystem> coordsys = nullptr)
96 : gauss2d::Image<T, Image<T>>(coordsys),
97 _data_in(data),
98 _data(_data_in),
99 _data_ref(_validate().template mutable_unchecked<2>()) {}
100
101 ~Image() {};
102
103 inline T &_get_value_unchecked_impl(size_t row, size_t col) { return this->_data_ref(row, col); };
104
105 py::array_t<T> &get_data() { return this->_data; }
106
107 size_t get_n_cols_impl() const { return _data.shape(1); };
108 size_t get_n_rows_impl() const { return _data.shape(0); };
109
110 inline T get_value_unchecked_impl(size_t row, size_t col) const { return this->_data_ref(row, col); };
111 void set_value_unchecked_impl(size_t row, size_t col, T value) { this->_data_ref(row, col) = value; }
112
113private:
114 // Data initialized in C++
115 std::unique_ptr<py::array_t<T>> _ptr_own = nullptr;
116 // Data passed from Python (which cannot be stored as a C++ reference
117 // for some reason)
118 py::array_t<T> _data_in;
119 // A ref to whichever of the two is initialized by the constructor
120 py::array_t<T> &_data;
121 // A more convenient ref for matrix operations
122 py::detail::unchecked_mutable_reference<T, 2> _data_ref;
123
124 py::array_t<T> &_validate() const {
125 if (_data.ndim() != 2) {
126 throw std::runtime_error("Input data must have 2 dimensions");
127 }
128 return _data;
129 }
130};
131#pragma GCC visibility pop
132
143template <typename Value, typename Index>
144std::string replace_images_types(std::string target, std::string str_type, std::string_view separator) {
145 std::string token1 = std::string("<") + type_name_str<Value>() + ", ";
146 target = replace_all_none(target, token1);
147 std::string token2 = type_name_str<Image<Value>>(false, separator) + std::string(", ");
148 target = replace_all_none(target, token2);
149 std::string token3 = type_name_str<Image<Index>>(false, separator) + std::string(" >");
150 target = replace_all(target, token3, str_type);
151 // str appears to return e.g. Image<double> for some reason
152 target = replace_type<Value>(target, str_type);
153 return target;
154}
155
156template <typename T>
157void declare_image(py::module &m, std::string str_type) {
158 using Class = Image<T>;
159 std::string pyclass_name = std::string("Image") + str_type;
160 py::classh<Class>(m, pyclass_name.c_str())
161 .def(py::init<size_t, size_t, const T *,
162 const std::shared_ptr<const lsst::gauss2d::CoordinateSystem>>(),
163 "n_rows"_a, "n_cols"_a, "value_init"_a = Class::_value_default_ptr(),
164 "coordsys"_a = gauss2d::COORDS_DEFAULT)
165 .def(py::init<py::array_t<T>, const std::shared_ptr<const lsst::gauss2d::CoordinateSystem>>(),
166 "data"_a, "coordsys"_a = gauss2d::COORDS_DEFAULT)
167 .def_property_readonly("coordsys", &Class::get_coordsys_ptr_const)
168 .def_property_readonly("data", &Class::get_data)
169 .def_property_readonly("n_rows", &Class::get_n_rows)
170 .def_property_readonly("n_cols", &Class::get_n_cols)
171 .def("fill", &Class::fill)
172 .def("get_value", &Class::get_value, "row"_a, "col"_a)
173 .def("set_value", &Class::set_value, "row"_a, "col"_a, "value"_a)
174 .def("get_value_unchecked", &Class::get_value_unchecked, "row"_a, "col"_a)
175 .def("set_value_unchecked", &Class::set_value_unchecked, "row"_a, "col"_a, "value"_a)
176 .def_property_readonly("size", &Class::size)
177 .def_property_readonly("shape", &Class::shape)
178 .def(py::self == py::self)
179 .def(py::self != py::self)
180 .def(py::self += T())
181 .def(py::self *= T())
182 .def("__repr__",
183 [str_type](const Class &self) {
184 std::string repr = self.repr(true, self.PY_NAMESPACE_SEPARATOR);
185 return replace_type<T>(repr, str_type);
186 })
187 .def("__str__", [str_type](const Class &self) {
188 std::string str = self.str();
189 return replace_type<T>(str, str_type);
190 });
191}
192
202template <typename T>
203std::string replace_image_types(std::string target, std::string str_type, std::string_view separator) {
204 std::string token1 = std::string("<") + type_name_str<T>() + ", ";
205 target = replace_all_none(target, token1);
206 std::string token2 = type_name_str<Image<T>>(false, separator) + std::string(" >");
207 target = replace_all(target, token2, str_type);
208 // str appears to return e.g. Image<double> for some reason
209 target = replace_type<T>(target, str_type);
210 return target;
211}
212
213template <typename T>
214void declare_image_array(py::module &m, std::string str_type) {
215 using Class = lsst::gauss2d::ImageArray<T, Image<T>>;
216 std::string pyclass_name = std::string("ImageArray") + str_type;
217 py::classh<Class>(m, pyclass_name.c_str())
218 .def(py::init<const typename Class::Data *>(), "data"_a)
219 .def("at", &Class::at, py::return_value_policy::reference)
220 .def_property_readonly("size", &Class::size)
221 .def("__getitem__", &Class::at, py::return_value_policy::reference)
222 .def("__len__", &Class::size)
223 .def("__repr__",
224 [str_type](const Class &self) {
225 std::string repr = self.repr(true, self.PY_NAMESPACE_SEPARATOR);
226 repr = replace_image_types<T>(repr, str_type, self.PY_NAMESPACE_SEPARATOR);
227 return repr;
228 })
229 .def("__str__", [str_type](const Class &self) {
230 std::string str = self.str();
231 str = replace_image_types<T>(str, str_type, self.CC_NAMESPACE_SEPARATOR);
232 return str;
233 });
234}
235
236template <typename T>
237void declare_evaluator(py::module &m, std::string str_type) {
238 using Class = lsst::gauss2d::GaussianEvaluator<T, Image<T>, Image<lsst::gauss2d::idx_type>>;
239 std::string pyclass_name = std::string("GaussianEvaluator") + str_type;
240 py::classh<Class>(m, pyclass_name.c_str())
241 .def(py::init<const std::shared_ptr<const gauss2d::ConvolvedGaussians>,
242 const std::shared_ptr<const Image<T>>, const std::shared_ptr<const Image<T>>,
243 const std::shared_ptr<Image<T>>, const std::shared_ptr<Image<T>>,
244 const std::shared_ptr<gauss2d::ImageArray<T, Image<T>>>,
245 const std::shared_ptr<const Image<gauss2d::idx_type>>,
246 const std::shared_ptr<const Image<T>>,
247 const std::shared_ptr<const Image<gauss2d::idx_type>>,
248 const std::shared_ptr<const Image<T>>, const std::shared_ptr<const Image<T>>>(),
249 "gaussians"_a, "data"_a = nullptr, "sigma_inv"_a = nullptr, "output"_a = nullptr,
250 "residual"_a = nullptr, "grads"_a = nullptr, "grad_param_map"_a = nullptr,
251 "grad_param_factor"_a = nullptr, "extra_param_map"_a = nullptr,
252 "extra_param_factor"_a = nullptr, "background"_a = nullptr)
253 .def("loglike_pixel", &Class::loglike_pixel, "to_add"_a = false)
254 .def_property_readonly("n_cols", &Class::get_n_cols)
255 .def_property_readonly("n_rows", &Class::get_n_rows)
256 .def_property_readonly("size", &Class::get_size)
257 .def("__len__", &Class::get_size)
258 .def("__repr__",
259 [str_type](const Class &self) {
260 std::string repr = self.repr(true, self.PY_NAMESPACE_SEPARATOR);
261 repr = replace_images_types<T, lsst::gauss2d::idx_type>(repr, str_type,
262 self.PY_NAMESPACE_SEPARATOR);
263 return repr;
264 })
265 .def("__str__", [str_type](const Class &self) {
266 std::string str = self.str();
267 str = replace_images_types<T, lsst::gauss2d::idx_type>(str, str_type,
268 self.CC_NAMESPACE_SEPARATOR);
269 return str;
270 });
271}
272
273template <typename T, class Data, class Indices>
274void declare_maker(py::module &m, std::string str_type) {
275 m.def(("make_gaussians_pixel_" + str_type).c_str(), lsst::gauss2d::make_gaussians_pixel<T, Data, Indices>,
276 "Evaluate a 2D Gaussian at the centers of pixels on a rectangular grid using the standard bivariate"
277 "Gaussian PDF.",
278 "gaussians"_a, "output"_a = nullptr, "n_rows"_a = 0, "n_cols"_a = 0, "coordsys"_a = nullptr,
279 "to_add"_a = false);
280}
281
282} // namespace lsst::gauss2d::python
283
284#endif // LSST_GAUSS2D_PYTHON_IMAGE_H
A Python image using numpy arrrays for storage.
Definition image.h:72
Image(py::array_t< T > data, const std::shared_ptr< const lsst::gauss2d::CoordinateSystem > coordsys=nullptr)
Definition image.h:94