File: renderer.cpp

package info (click to toggle)
wsclean 3.6-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 16,296 kB
  • sloc: cpp: 129,246; python: 22,066; sh: 360; ansic: 230; makefile: 185
file content (186 lines) | stat: -rw-r--r-- 7,840 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186

#include "renderer.h"

#include <aocommon/imagecoordinates.h>

#include <schaapcommon/math/convolution.h>
#include <schaapcommon/math/restoreimage.h>

#include <cmath>
#include <array>

#include <boost/algorithm/clamp.hpp>

using aocommon::ImageCoordinates;
using boost::algorithm::clamp;

namespace wsclean::renderer {
namespace {
long double Gaussian(long double x, long double sigma) {
  // Evaluate unnormalized Gaussian (unit valued peak, but integral over <-inf,
  // inf> doesn't evaluate to unity)
  const long double xi = x / sigma;
  return std::exp(static_cast<long double>(-0.5) * xi * xi);
}

void RenderPointComponent(float* image_data, size_t image_width,
                          size_t image_height, long double ra, long double dec,
                          long double pixel_scale_l, long double pixel_scale_m,
                          long double l_shift, long double m_shift,
                          long double position_ra, long double position_dec,
                          long double flux) {
  long double source_l;
  long double source_m;
  ImageCoordinates::RaDecToLM(position_ra, position_dec, ra, dec, source_l,
                              source_m);
  source_l -= l_shift;
  source_m -= m_shift;

  int source_x;
  int source_y;
  ImageCoordinates::LMToXY<long double>(source_l, source_m, pixel_scale_l,
                                        pixel_scale_m, image_width,
                                        image_height, source_x, source_y);

  if (source_x >= 0 && source_x < (int)image_width && source_y >= 0 &&
      source_y < (int)image_height) {
    float* image_data_ptr = image_data + source_y * image_width + source_x;
    (*image_data_ptr) += static_cast<double>(flux);
  }
}

void RenderGaussianComponent(
    float* image_data, size_t image_width, size_t image_height, long double ra,
    long double dec, long double pixel_scale_l, long double pixel_scale_m,
    long double l_shift, long double m_shift, long double position_ra,
    long double position_dec, long double gaus_major_axis,
    long double gaus_minor_axis, long double gaus_position_angle,
    long double flux) {
  // Using the FWHM formula for a Gaussian:
  const long double fwhm_constant = (2.0L * std::sqrt(2.0L * std::log(2.0L)));
  const long double sigma_major_axis = gaus_major_axis / fwhm_constant;
  const long double sigma_minor_axis = gaus_minor_axis / fwhm_constant;
  // TODO this won't work for non-equally spaced dimensions
  const long double min_pixel_scale = std::min(pixel_scale_l, pixel_scale_m);

  // Position angle is angle from North:
  const long double angle = gaus_position_angle + M_PI_2;
  const long double cos_angle = std::cos(angle);
  const long double sin_angle = std::sin(angle);

  // Make rotation matrix
  std::array<long double, 4> transf;
  transf[0] = cos_angle;
  transf[1] = -sin_angle;
  transf[2] = sin_angle;
  transf[3] = cos_angle;

  const double sigma_max = std::max(std::fabs(sigma_major_axis * transf[0]),
                                    std::fabs(sigma_major_axis * transf[1]));
  // Multiply with scaling matrix to make variance 1.
  transf[0] /= sigma_major_axis;
  transf[1] /= sigma_major_axis;
  transf[2] /= sigma_minor_axis;
  transf[3] /= sigma_minor_axis;
  const int bounding_box_size = std::ceil(sigma_max * 20.0 / min_pixel_scale);
  long double source_l;
  long double source_m;
  ImageCoordinates::RaDecToLM(position_ra, position_dec, ra, dec, source_l,
                              source_m);

  // Calculate the bounding box
  int source_x;
  int source_y;
  ImageCoordinates::LMToXY<long double>(
      source_l - l_shift, source_m - m_shift, pixel_scale_l, pixel_scale_m,
      image_width, image_height, source_x, source_y);
  const int x_left = clamp(source_x - bounding_box_size, 0, int(image_width));
  const int x_right =
      clamp(source_x + bounding_box_size, x_left, int(image_width));
  const int y_top = clamp(source_y - bounding_box_size, 0, int(image_height));
  const int y_bottom =
      clamp(source_y + bounding_box_size, y_top, int(image_height));

  std::vector<double> values;
  double flux_sum = 0.0;
  for (int y = y_top; y != y_bottom; ++y) {
    for (int x = x_left; x != x_right; ++x) {
      long double l, m;
      ImageCoordinates::XYToLM<long double>(x, y, pixel_scale_l, pixel_scale_m,
                                            image_width, image_height, l, m);
      l += l_shift;
      m += m_shift;
      const long double l_transf =
          (l - source_l) * transf[0] + (m - source_m) * transf[1];
      const long double m_transf =
          (l - source_l) * transf[2] + (m - source_m) * transf[3];
      const long double dist =
          std::sqrt(l_transf * l_transf + m_transf * m_transf);
      long double v = Gaussian(dist, 1.0L) * flux;
      flux_sum += static_cast<double>(v);
      values.emplace_back(v);
    }
  }
  const double* iter = values.data();
  const double factor = flux / flux_sum;
  for (int y = y_top; y != y_bottom; ++y) {
    float* image_data_ptr = image_data + y * image_width + x_left;
    for (int x = x_left; x != x_right; ++x) {
      (*image_data_ptr) += *iter * factor;
      ++image_data_ptr;
      ++iter;
    }
  }
}

void RenderModel(aocommon::Image& image,
                 const ImageCoordinateSettings& image_settings,
                 const Model& model, long double start_frequency,
                 long double end_frequency,
                 aocommon::PolarizationEnum polarization) {
  for (const ModelSource& source : model) {
    for (const ModelComponent& component : source) {
      const long double position_ra = component.PosRA(),
                        position_dec = component.PosDec();
      const long double integrated_flux = component.SED().IntegratedFlux(
          start_frequency, end_frequency, polarization);

      if (component.Type() == ModelComponent::GaussianSource) {
        const long double gaus_major_axis = component.MajorAxis(),
                          gaus_minor_axis = component.MinorAxis();
        const long double gaus_position_angle = component.PositionAngle();
        RenderGaussianComponent(
            image.Data(), image.Width(), image.Height(), image_settings.ra,
            image_settings.dec, image_settings.pixel_scale_l,
            image_settings.pixel_scale_m, image_settings.l_shift,
            image_settings.m_shift, position_ra, position_dec, gaus_major_axis,
            gaus_minor_axis, gaus_position_angle, integrated_flux);
      } else
        RenderPointComponent(
            image.Data(), image.Width(), image.Height(), image_settings.ra,
            image_settings.dec, image_settings.pixel_scale_l,
            image_settings.pixel_scale_m, image_settings.l_shift,
            image_settings.m_shift, position_ra, position_dec, integrated_flux);
    }
  }
}

}  // namespace

void RestoreWithEllipticalBeam(aocommon::Image& image,
                               const ImageCoordinateSettings& image_settings,
                               const Model& model, long double beam_major_axis,
                               long double beam_minor_axis,
                               long double beam_position_angle,
                               long double start_frequency,
                               long double end_frequency,
                               aocommon::PolarizationEnum polarization) {
  aocommon::Image rendered_without_beam(image.Width(), image.Height(), 0.0);
  RenderModel(rendered_without_beam, image_settings, model, start_frequency,
              end_frequency, polarization);
  schaapcommon::math::RestoreImage(
      image.Data(), rendered_without_beam.Data(), image.Width(), image.Height(),
      beam_major_axis, beam_minor_axis, beam_position_angle,
      image_settings.pixel_scale_l, image_settings.pixel_scale_m);
}
}  // namespace wsclean::renderer