SourceXtractorPlusPlus 0.21
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
PsfPluginConfig.cpp
Go to the documentation of this file.
1
17/*
18 * PsfPluginConfig.cpp
19 *
20 * Created on: Jun 25, 2018
21 * Author: Alejandro Álvarez Ayllón (greatly adapted from mschefer's code)
22 */
23#include <boost/algorithm/string.hpp>
24
29#include <CCfits/CCfits>
30#include <Configuration/ProgramOptionsHelper.h>
31#include <ElementsKernel/Logging.h>
32
33namespace po = boost::program_options;
35
36static auto logger = Elements::Logging::getLogger("PsfPlugin");
37
38namespace fs = boost::filesystem;
39
40namespace SourceXtractor {
41
42static const std::string PSF_FILE{"psf-filename"};
43static const std::string PSF_FWHM {"psf-fwhm" };
44static const std::string PSF_PIXEL_SAMPLING {"psf-pixel-sampling" };
45
46/*
47 * Reading in a stacked PSF as it is being developed for co-added images in Euclid
48 */
54
56 try {
57 CCfits::ExtHDU& psf_data = pFits->extension("PSF_DATA");
58
59 int n_components;
60 psf_data.readKey("POLNAXIS", n_components);
61
62 double pixel_sampling;
63 psf_data.readKey("PSF_SAMP", pixel_sampling);
64
66
67 for (int i = 0; i < n_components; ++i) {
68 auto index_str = std::to_string(i + 1);
69
70 psf_data.readKey("POLGRP" + index_str, components[i].group_id);
71 psf_data.readKey("POLNAME" + index_str, components[i].name);
72 psf_data.readKey("POLZERO" + index_str, components[i].offset);
73 psf_data.readKey("POLSCAL" + index_str, components[i].scale);
74
75 // Groups in the FITS file are indexed starting at 1, but we use a zero-based index
76 --components[i].group_id;
77 }
78
79 int n_groups;
80 psf_data.readKey("POLNGRP", n_groups);
81
83
84 for (int i = 0; i < n_groups; ++i) {
85 auto index_str = std::to_string(i + 1);
86
87 psf_data.readKey("POLDEG" + index_str, group_degrees[i]);
88 }
89
90 int width, height, n_coeffs, n_pixels;
91 psf_data.readKey("PSFAXIS1", width);
92 psf_data.readKey("PSFAXIS2", height);
93 psf_data.readKey("PSFAXIS3", n_coeffs);
94
95 if (width != height) {
96 throw Elements::Exception() << "Non square PSFEX format PSF (" << width << 'X' << height << ')';
97 }
98 if (width % 2 == 0) {
99 throw Elements::Exception() << "PSF kernel must have odd size";
100 }
101
102 n_pixels = width * height;
103
105 psf_data.column("PSF_MASK").readArrays(all_data, 0, 0);
106 auto& raw_coeff_data = all_data[0];
107
109
110 for (int i = 0; i < n_coeffs; ++i) {
111 auto offset = std::begin(raw_coeff_data) + i * n_pixels;
112 coefficients[i] = VectorImage<SeFloat>::create(width, height, offset, offset + n_pixels);
113 }
114
115 logger.debug() << "Loaded variable PSF " << pFits->name() << " (" << width << ", " << height << ") with " << n_coeffs
116 << " coefficients";
117 auto ll = logger.debug();
118 ll << "Components: ";
119 for (auto c : components) {
120 ll << c.name << " ";
122 throw Elements::Exception() << "Can not find a getter for the component " << c.name;
123 }
124 }
125
127 } catch (CCfits::FITS::NoSuchHDU&) {
128 throw;
129 } catch (CCfits::FitsException &e) {
130 throw Elements::Exception() << "Error loading PSFEx file: " << e.message();
131 }
132}
133
134// templated to work around CCfits limitation that primary and extension HDUs are different classes
135template<typename T>
137 double pixel_sampling;
138 try {
139 image_hdu.readKey("SAMPLING", pixel_sampling);
140 }
141 catch (CCfits::HDU::NoSuchKeyword&) {
142 pixel_sampling = 1.;
143 }
144
145 if (image_hdu.axis(0) != image_hdu.axis(1)) {
146 throw Elements::Exception() << "Non square image PSF (" << image_hdu.axis(0) << 'X'
147 << image_hdu.axis(1) << ')';
148 }
149
150 auto size = image_hdu.axis(0);
151
153 image_hdu.read(data);
154 auto kernel = VectorImage<SeFloat>::create(size, size);
155 std::copy(begin(data), end(data), kernel->getData().begin());
156
157 logger.debug() << "Loaded image PSF(" << size << ", " << size << ") with sampling step " << pixel_sampling;
158
160}
161
165
166 // check whether the filename points to a delta function as PSF or NOPSF
167 if (boost::to_upper_copy(fs::path(filename).filename().string())=="NOPSF") {
168 return nullptr;
169 }
170
171 try {
172 // Read the HDU from the file
173 std::unique_ptr<CCfits::FITS> pFits{new CCfits::FITS(filename, CCfits::Read)};
174 auto& image_hdu = pFits->pHDU();
175
176 auto axes = image_hdu.axes();
177 // PSF as image
178 if (axes == 2) {
179 if (hdu_number == 1) {
180 return readImage(image_hdu);
181 } else {
182 auto& extension = pFits->extension(hdu_number - 1);
183 return readImage(extension);
184 }
185 } else {
186 try {
187 // PSFEx format
188 return readPsfEx(pFits);
189 } catch (CCfits::FITS::NoSuchHDU& e) {
190 logger.debug() << "Failed Reading a PsfEx file!";
191 return readStackedPsf(pFits);
192 }
193 }
194 } catch (CCfits::FitsException& e) {
195 throw Elements::Exception() << "Error loading PSF file: " << e.message();
196 }
197}
198
200 int size = int(fwhm / pixel_sampling + 1) * 6 + 1;
201 auto kernel = VectorImage<SeFloat>::create(size, size);
202
203 // convert full width half maximum to standard deviation
204 double sigma_squared = (fwhm / (pixel_sampling * 2.355)) * (fwhm / (pixel_sampling * 2.355));
205
206 double total = 0;
207 for (int y = 0; y < size; y++) {
208 for (int x = 0; x < size; x++) {
209 double sx = (x - size / 2);
210 double sy = (y - size / 2);
211 double pixel_value = exp(-(sx * sx + sy * sy) / (2 * sigma_squared));
212 total += pixel_value;
213 kernel->setValue(x, y, pixel_value);
214 }
215 }
216 for (int y = 0; y < size; y++) {
217 for (int x = 0; x < size; x++) {
218 kernel->setValue(x, y, kernel->getValue(x, y) / total);
219 }
220 }
221
222 logger.info() << "Using gaussian PSF(" << fwhm << ") with pixel sampling step size " << pixel_sampling << " (size " << size << ")";
223
225}
226
228 return {{"Variable PSF", {
229 {PSF_FILE.c_str(), po::value<std::string>(),
230 "Psf file (PSFEx/psfStack/image/NOPSF)."},
231 {PSF_FWHM.c_str(), po::value<double>(),
232 "Generate a gaussian PSF with the given full-width half-maximum (in pixels)"},
233 {PSF_PIXEL_SAMPLING.c_str(), po::value<double>(),
234 "Generate a gaussian PSF with the given pixel sampling step size"}
235 }}};
236}
237
239 // Fail if there is no PSF file, but PSF_FWHM is set and PSF_PIXEL_SAMPLING is not
240 if (args.find(PSF_FILE) == args.end() && args.find(PSF_FWHM) != args.end() &&
241 args.find(PSF_PIXEL_SAMPLING) == args.end()) {
242 throw Elements::Exception(PSF_PIXEL_SAMPLING + " is required when using " + PSF_FWHM);
243 }
244}
245
247 if (args.find(PSF_FILE) != args.end()) {
248 auto psf_file = args.find(PSF_FILE)->second.as<std::string>();
249 logger.debug() << "Provided by user: " << psf_file;
250 if (boost::to_upper_copy(psf_file) == "NOPSF"){
251 m_vpsf = nullptr;
252 } else {
253 m_vpsf = readPsf(args.find(PSF_FILE)->second.as<std::string>());
254 }
255 } else if (args.find(PSF_FWHM) != args.end()) {
256 m_vpsf = generateGaussianPsf(args.find(PSF_FWHM)->second.as<double>(),
257 args.find(PSF_PIXEL_SAMPLING)->second.as<double>());
258 }
259}
260
262 return m_vpsf;
263}
264
265} // end SourceXtractor
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
static auto logger
T begin(T... args)
void debug(const std::string &logMessage)
static Logging getLogger(const std::string &name="")
void info(const std::string &logMessage)
std::map< std::string, OptionDescriptionList > getProgramOptions() override
std::shared_ptr< Psf > m_vpsf
void initialize(const UserValues &args) override
static std::shared_ptr< Psf > readPsf(const std::string &filename, int hdu_number=1)
const std::shared_ptr< Psf > & getPsf() const
static std::shared_ptr< Psf > generateGaussianPsf(SeFloat fwhm, SeFloat pixel_sampling)
void preInitialize(const UserValues &args) override
static std::map< std::string, ValueGetter > component_value_getters
Definition PsfTask.h:41
static std::shared_ptr< VectorImage< T > > create(Args &&... args)
T copy(T... args)
T end(T... args)
T exp(T... args)
T find(T... args)
T move(T... args)
static Elements::Logging logger
static const std::string PSF_PIXEL_SAMPLING
static std::shared_ptr< VariablePsfStack > readStackedPsf(std::unique_ptr< CCfits::FITS > &pFits)
static std::shared_ptr< VariablePsf > readImage(T &image_hdu)
static const std::string PSF_FILE
SeFloat32 SeFloat
Definition Types.h:32
static const std::string PSF_FWHM
static std::shared_ptr< VariablePsf > readPsfEx(std::unique_ptr< CCfits::FITS > &pFits)
T to_string(T... args)