SourceXtractorPlusPlus  0.19
SourceXtractor++, the next generation SExtractor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
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>
31 #include <ElementsKernel/Logging.h>
32 
33 namespace po = boost::program_options;
35 
36 static auto logger = Elements::Logging::getLogger("PsfPlugin");
37 
38 namespace fs = boost::filesystem;
39 
40 namespace SourceXtractor {
41 
42 static const std::string PSF_FILE{"psf-filename"};
43 static const std::string PSF_FWHM {"psf-fwhm" };
44 static 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  */
50  logger.debug() << "Loading a PSF stack file.";
51  std::shared_ptr<VariablePsfStack> act_stack = std::make_shared<VariablePsfStack>(std::move(pFits));
52  return act_stack;
53 }
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 
65  std::vector<VariablePsf::Component> components(n_components);
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 
82  std::vector<int> group_degrees(n_groups);
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 
108  std::vector<std::shared_ptr<VectorImage<SeFloat>>> coefficients(n_coeffs);
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 
126  return std::make_shared<VariablePsf>(pixel_sampling, components, group_degrees, coefficients);
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
135 template<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 
152  std::valarray<double> data{};
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 
159  return std::make_shared<VariablePsf>(pixel_sampling, kernel);
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 
224  return std::make_shared<VariablePsf>(pixel_sampling, kernel);
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 
238 void PsfPluginConfig::preInitialize(const Euclid::Configuration::Configuration::UserValues &args) {
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 
246 void PsfPluginConfig::initialize(const UserValues &args) {
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
static const std::string PSF_FWHM
std::map< std::string, OptionDescriptionList > getProgramOptions() override
T copy(T...args)
static std::shared_ptr< VariablePsf > readImage(T &image_hdu)
static auto logger
Definition: WCS.cpp:41
static std::shared_ptr< Psf > readPsf(const std::string &filename, int hdu_number=1)
T exp(T...args)
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > x
T to_string(T...args)
void initialize(const UserValues &args) override
SeFloat32 SeFloat
Definition: Types.h:32
T end(T...args)
static std::shared_ptr< VectorImage< T > > create(Args &&...args)
Definition: VectorImage.h:100
STL class.
std::shared_ptr< DependentParameter< std::shared_ptr< EngineParameter > > > y
STL class.
constexpr double e
void preInitialize(const UserValues &args) override
static std::shared_ptr< VariablePsf > readPsfEx(std::unique_ptr< CCfits::FITS > &pFits)
static const std::string PSF_FILE
static std::map< std::string, ValueGetter > component_value_getters
Definition: PsfTask.h:41
string filename
Definition: conf.py:65
STL class.
std::shared_ptr< Psf > m_vpsf
static const std::string PSF_PIXEL_SAMPLING
T move(T...args)
T find(T...args)
STL class.
T begin(T...args)
T c_str(T...args)
static std::shared_ptr< VariablePsfStack > readStackedPsf(std::unique_ptr< CCfits::FITS > &pFits)
static std::shared_ptr< Psf > generateGaussianPsf(SeFloat fwhm, SeFloat pixel_sampling)
static auto logger
static Logging getLogger(const std::string &name="")
const std::shared_ptr< Psf > & getPsf() const