File: image_set.h

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 (382 lines) | stat: -rw-r--r-- 13,596 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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
// SPDX-License-Identifier: LGPL-3.0-only

#ifndef RADLER_IMAGE_SET_H_
#define RADLER_IMAGE_SET_H_

#include <map>
#include <memory>
#include <vector>

#include <aocommon/image.h>
#include <aocommon/uvector.h>

#include <schaapcommon/fitters/spectralfitter.h>

#include "work_table.h"

namespace radler {

class ImageSet {
 public:
  ImageSet(const WorkTable& table, bool squared_joins,
           const std::set<aocommon::PolarizationEnum>& linked_polarizations,
           size_t width, size_t height);

  /**
   * Constructs an ImageSet with the same settings as an existing ImageSet
   * object, but a different image size.
   *
   * @param image_set An existing ImageSet.
   * @param width The image width for the new ImageSet.
   * @param height The image height for the new ImageSet.
   */
  ImageSet(const ImageSet& image_set, size_t width, size_t height);

  ImageSet(const ImageSet&) = delete;
  ImageSet(ImageSet&&) = delete;
  ImageSet& operator=(const ImageSet&) = delete;
  ImageSet& operator=(ImageSet&&) = delete;

  aocommon::Image Release(size_t image_index) {
    return std::move(images_[image_index]);
  }

  void SetImage(size_t image_index, aocommon::Image&& data) {
    assert(data.Width() == Width() && data.Height() == Height());
    images_[image_index] = std::move(data);
  }

  /**
   * Replace the images of this ImageSet. The images may be of a different size.
   * Both ImageSets are expected to be for the same deconvolution configuration:
   * besides the images and their dimension, no fields are changed.
   */
  void SetImages(ImageSet&& source);

  /**
   * Replace all images. The input array should have the same number of images
   * and each image should have the same dimensions as the images already
   * stored.
   */
  void SetImages(std::vector<aocommon::Image>&& images) {
    assert(images.size() == images_.size());
    // We could move the whole vector at once but this way each image is
    // checked.
    for (size_t image_index = 0; image_index != images_.size(); ++image_index) {
      SetImage(image_index, std::move(images[image_index]));
    }
  }

  /**
   * @param use_residual_images: True: Load residual images. False: Load model
   * images.
   */
  void LoadAndAverage(bool use_residual_images);

  /**
   * Loads the PSF accessors from the entries and creates the proper PSF images.
   *
   * let X is the direction-dependent PSFs index; Y is join axis (frequency)
   * index.
   *
   * In the @ref WorkTable they are stored as @ref aocommon::ImageAccessor in
   * multiple @ref WorkTableEntry objects. There they are stored as
   * @c Worktable.entries_[Y].psf_accessors[X].
   *
   * The returned object of this function stores them as @c result[X][Y].
   * The swapping of the X and Y is done to make it easier to use the result
   * in the deconvolution algorithms. These functions expect a one dimensional
   * array of join axis (frequency) PSF images.
   *
   * The selection algorithm selects the best PSF based on the distance between
   * the PSF offset and the sub image offset. So when PSF index 1 is the best
   * match the LoadAndAveragePsfs()[1] will contain the PSFs for the underlying
   * algorithm.
   *
   * @pre @c work_table_.ValidatePsfs() can be called without throwing.
   */
  std::vector<std::vector<aocommon::Image>> LoadAndAveragePsfs() const;

  void InterpolateAndStoreModel(
      const schaapcommon::fitters::SpectralFitter& fitter);

  void AssignAndStoreResidual();

  /**
   * This function will calculate the integration over all images, squaring
   * images that are in the same squared-image group. For example, with
   * a squared group of [I, Q, ..] and another group [I2, Q2, ...], this
   * will calculate:
   *
   * sqrt(I^2 + Q^2 + ..) + sqrt(I2^2 + Q2^2 ..) + ..
   * ----------------------------------------------
   *           1          +           1          + ..
   *
   * If the 'squared groups' are of size 1, the average of the groups will be
   * returned (i.e., without square-rooting the square).
   *
   * If the squared joining option is set in the provided wsclean settings, the
   * behaviour of this method changes. In that case, it will return the square
   * root of the average squared value:
   *
   *       I^2 + Q^2 + ..  +  I2^2 + Q2^2 ..  + ..
   * sqrt( --------------------------------------- )
   *            1          +        1         + ..
   *
   * These formulae are such that the values will have normal flux values.
   * @param dest Pre-allocated output array that will be filled with the
   * integrated image.
   * @param scratch Pre-allocated scratch space, same size as image.
   */
  void GetSquareIntegrated(aocommon::Image& dest,
                           aocommon::Image& scratch) const {
    if (square_joined_channels_)
      GetSquareIntegratedWithSquaredChannels(dest);
    else
      GetSquareIntegratedWithNormalChannels(dest, scratch);
  }

  /**
   * This function will calculate the 'linear' integration over all images,
   * unless joined channels are requested to be squared. The method will return
   * the weighted average of all images. Normally,
   * @ref GetSquareIntegrated
   * should be used for peak finding, but in case negative values should remain
   * negative, such as with multiscale (otherwise a sidelobe will be fitted with
   * large scales), this function can be used.
   * @param dest Pre-allocated output array that will be filled with the average
   * values.
   */
  void GetLinearIntegrated(aocommon::Image& dest) const {
    if (square_joined_channels_)
      GetSquareIntegratedWithSquaredChannels(dest);
    else
      GetLinearIntegratedWithNormalChannels(dest);
  }

  void GetIntegratedPsf(aocommon::Image& dest,
                        const std::vector<aocommon::Image>& psfs);

  size_t NOriginalChannels() const {
    return work_table_.OriginalGroups().size();
  }

  size_t PsfCount() const { return NDeconvolutionChannels(); }

  size_t NDeconvolutionChannels() const {
    return work_table_.DeconvolutionGroups().size();
  }

  ImageSet& operator=(float val) {
    for (aocommon::Image& image : images_) image = val;
    return *this;
  }

  /**
   * Exposes image data.
   *
   * ImageSet only exposes a non-const pointer to the image data.
   * @param index An image index.
   * @return A non-const pointer to the data area for the image.
   */
  float* Data(size_t index) { return images_[index].Data(); }

  /**
   * Exposes the images in the image set.
   */
  const aocommon::Image& operator[](size_t index) const {
    return images_[index];
  }

  /**
   * Create a non-owning view of an image
   *
   * This allows to modify the data (pixels) of an image.
   * Resizing the view will not affect the image in the ImageSet.
   * @param index An image index.
   * @return A non-owning view of the image.
   */

  aocommon::Image GetView(size_t index) {
    return {images_[index].Data(), images_[index].Width(),
            images_[index].Height()};
  }

  const std::vector<aocommon::Image>& Images() const { return images_; }

  size_t Size() const { return images_.size(); }

  size_t PsfIndex(size_t image_index) const {
    return image_index_to_psf_index_[image_index];
  }

  const WorkTable& Table() const { return work_table_; }

  std::unique_ptr<ImageSet> Trim(size_t x1, size_t y1, size_t x2, size_t y2,
                                 size_t old_width) const {
    auto p = std::make_unique<ImageSet>(*this, x2 - x1, y2 - y1);
    for (size_t i = 0; i != images_.size(); ++i) {
      CopySmallerPart(images_[i], p->images_[i], x1, y1, x2, y2, old_width);
    }
    return p;
  }

  /**
   * Like Trim(), but only copies values that are in the mask. All other values
   * are set to zero.
   * @param mask A mask of size (x2-x1) x (y2-y1)
   */
  std::unique_ptr<ImageSet> TrimMasked(size_t x1, size_t y1, size_t x2,
                                       size_t y2, size_t old_width,
                                       const bool* mask) const {
    std::unique_ptr<ImageSet> p = Trim(x1, y1, x2, y2, old_width);
    for (aocommon::Image& image : p->images_) {
      for (size_t pixel = 0; pixel != image.Size(); ++pixel) {
        if (!mask[pixel]) image[pixel] = 0.0;
      }
    }
    return p;
  }

  void CopyMasked(const ImageSet& from_image_set, size_t to_x, size_t to_y,
                  const bool* from_mask) {
    for (size_t i = 0; i != images_.size(); ++i) {
      aocommon::Image::CopyMasked(
          images_[i].Data(), to_x, to_y, images_[i].Width(),
          from_image_set.images_[i].Data(), from_image_set.images_[i].Width(),
          from_image_set.images_[i].Height(), from_mask);
    }
  }

  /**
   * Place all images in @c from onto the images in this ImageSet at a
   * given position. The dimensions of @c from can be smaller or equal
   * to ones in this.
   */
  void AddSubImage(const ImageSet& from, size_t to_x, size_t to_y) {
    for (size_t i = 0; i != images_.size(); ++i) {
      aocommon::Image::AddSubImage(images_[i].Data(), to_x, to_y,
                                   images_[i].Width(), from.images_[i].Data(),
                                   from.images_[i].Width(),
                                   from.images_[i].Height());
    }
  }

  ImageSet& operator*=(float factor) {
    for (aocommon::Image& image : images_) image *= factor;
    return *this;
  }

  ImageSet& operator+=(const ImageSet& other) {
    for (size_t i = 0; i != Size(); ++i) images_[i] += other.images_[i];
    return *this;
  }

  void FactorAdd(ImageSet& rhs, double factor) {
    for (size_t i = 0; i != Size(); ++i)
      images_[i].AddWithFactor(rhs.images_[i], factor);
  }

  bool SquareJoinedChannels() const { return square_joined_channels_; }

  const std::set<aocommon::PolarizationEnum>& LinkedPolarizations() const {
    return linked_polarizations_;
  }

  size_t Width() const { return images_.front().Width(); }

  size_t Height() const { return images_.front().Height(); }

  static void CalculateDeconvolutionFrequencies(
      const WorkTable& group_table, std::vector<double>& frequencies,
      std::vector<float>& weights);

 private:
  void InitializeIndices();

  void InitializePolFactor() {
    const WorkTable::Group& first_channel_group =
        work_table_.OriginalGroups().front();
    std::set<aocommon::PolarizationEnum> pols;
    bool all_stokes_without_i = true;
    for (const WorkTableEntry* entry : first_channel_group) {
      if (linked_polarizations_.empty() ||
          linked_polarizations_.count(entry->polarization) != 0) {
        if (!aocommon::Polarization::IsStokes(entry->polarization) ||
            entry->polarization == aocommon::Polarization::StokesI)
          all_stokes_without_i = false;
        pols.insert(entry->polarization);
      }
    }
    const bool is_dual =
        pols.size() == 2 && aocommon::Polarization::HasDualPolarization(pols);
    const bool is_full =
        pols.size() == 4 &&
        (aocommon::Polarization::HasFullLinearPolarization(pols) ||
         aocommon::Polarization::HasFullCircularPolarization(pols));
    if (all_stokes_without_i)
      polarization_normalization_factor_ = 1.0 / pols.size();
    else if (is_dual || is_full)
      polarization_normalization_factor_ = 0.5;
    else
      polarization_normalization_factor_ = 1.0;
  }

  static void CopySmallerPart(const aocommon::Image& input,
                              aocommon::Image& output, size_t x1, size_t y1,
                              size_t x2, size_t y2, size_t old_width) {
    size_t new_width = x2 - x1;
    for (size_t y = y1; y != y2; ++y) {
      const float* old_ptr = &input[y * old_width];
      float* new_ptr = &output[(y - y1) * new_width];
      for (size_t x = x1; x != x2; ++x) {
        new_ptr[x - x1] = old_ptr[x];
      }
    }
  }

  static void CopyToLarger(aocommon::Image& to, size_t to_x, size_t to_y,
                           size_t to_width, const aocommon::Image& from,
                           size_t from_width, size_t from_height) {
    for (size_t y = 0; y != from_height; ++y) {
      std::copy(from.Data() + y * from_width,
                from.Data() + (y + 1) * from_width,
                to.Data() + to_x + (to_y + y) * to_width);
    }
  }

  static void CopyToLarger(aocommon::Image& to, size_t to_x, size_t to_y,
                           size_t to_width, const aocommon::Image& from,
                           size_t from_width, size_t from_height,
                           const bool* from_mask) {
    for (size_t y = 0; y != from_height; ++y) {
      for (size_t x = 0; x != from_width; ++x) {
        if (from_mask[y * from_width + x])
          to[to_x + (to_y + y) * to_width + x] = from[y * from_width + x];
      }
    }
  }

  void GetSquareIntegratedWithNormalChannels(aocommon::Image& dest,
                                             aocommon::Image& scratch) const;

  void GetSquareIntegratedWithSquaredChannels(aocommon::Image& dest) const;

  void GetLinearIntegratedWithNormalChannels(aocommon::Image& dest) const;

  const aocommon::Image& EntryToImage(const WorkTableEntry& entry) const {
    return images_[entry_index_to_image_index_[entry.index]];
  }

  std::vector<aocommon::Image> images_;
  // Weight of each deconvolution channels
  std::vector<float> weights_;
  bool square_joined_channels_;
  const WorkTable& work_table_;
  std::vector<size_t> entry_index_to_image_index_;
  aocommon::UVector<size_t> image_index_to_psf_index_;
  float polarization_normalization_factor_;
  std::set<aocommon::PolarizationEnum> linked_polarizations_;
};
}  // namespace radler
#endif  // RADLER_IMAGE_SET_H_