2 #include "purify/config.h"
12 t_int
sub2ind(
const t_int &row,
const t_int &col,
const t_int &rows,
const t_int &cols) {
23 return row * cols + col;
26 std::tuple<t_int, t_int>
ind2sub(
const t_int &sub,
const t_int &cols,
const t_int &rows) {
38 return std::make_tuple<t_int, t_int>(std::floor((sub - (sub % cols)) / cols), sub % cols);
41 t_real
mod(
const t_real &x,
const t_real &y) {
45 t_real r = std::fmod(x, y);
64 Image<t_complex> output = Image<t_complex>::Zero(a_y + b_y, a_x + b_x);
66 for (t_int l = 0; l < b.cols(); ++l) {
67 for (t_int k = 0; k < b.rows(); ++k) {
68 output(k, l) = (a * b.block(k, l, a_y, a_x)).sum();
76 const std::string distirbution) {
81 if (distirbution ==
"chi^2") {
83 return std::sqrt(2 * y_size + n_sigma * std::sqrt(4 * y_size));
85 return std::sqrt(2 * y_size + n_sigma * std::sqrt(4 * y_size)) * sigma;
87 if (distirbution ==
"chi") {
89 1. / (8 * y_size) - 1. / (128 * y_size * y_size);
90 auto mean = std::sqrt(2) * std::sqrt(y_size) *
110 return y0.stableNorm() / std::sqrt(2 * y0.size()) * std::pow(10.0, -(SNR / 20.0));
113 Vector<t_complex>
add_noise(
const Vector<t_complex> &y0,
const t_complex &
mean,
122 std::random_device rd_real;
123 std::random_device rd_imag;
124 std::mt19937_64 rng_real(rd_real());
125 std::mt19937_64 rng_imag(rd_imag());
129 return normal_dist_real(rng_real) +
I * normal_dist_imag(rng_imag);
132 auto noise = Vector<t_complex>::Zero(y0.size()).unaryExpr(sample);
145 return (stat(name.c_str(), &buffer) == 0);
148 std::tuple<t_real, t_real, t_real>
fit_fwhm(
const Image<t_real> &psf,
const t_int &size) {
158 t_int x0 = std::floor(psf.cols() * 0.5);
159 t_int y0 = std::floor(psf.rows() * 0.5);
162 Image<t_real> patch =
163 psf.block(std::floor(y0 - size * 0.5) + 1, std::floor(x0 - size * 0.5) + 1, size, size);
168 std::vector<t_tripletList> entries;
169 t_int total_entries = 0;
171 for (t_int i = 0; i < patch.cols(); ++i) {
172 for (t_int j = 0; j < patch.rows(); ++j) {
173 entries.emplace_back(j, i, std::log(patch(j, i)));
177 Matrix<t_real> A = Matrix<t_real>::Zero(total_entries, 4);
178 Vector<t_real> q = Vector<t_real>::Zero(total_entries);
180 for (t_int i = 0; i < total_entries; ++i) {
181 t_real x = entries.at(i).col() - size * 0.5 + 0.5;
182 t_real y = entries.at(i).row() - size * 0.5 + 0.5;
183 A(i, 0) =
static_cast<t_real
>(x * x);
184 A(i, 1) =
static_cast<t_real
>(x * y);
185 A(i, 2) =
static_cast<t_real
>(y * y);
186 q(i) = std::real(entries.at(i).value());
189 const auto solution =
190 static_cast<Vector<t_real>
>(A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(q));
191 t_real
const a = -solution(0);
192 t_real
const b = +solution(1) * 0.5;
193 t_real
const c = -solution(2);
195 t_real theta = std::atan2(b, std::sqrt(std::pow(2 * b, 2) + std::pow(
c - a, 2)) +
200 if (std::abs(b) < 1e-13) {
205 t = (a +
c - 2 * b / std::sin(2 * theta)) * 0.5;
206 s = (a +
c + 2 * b / std::sin(2 * theta)) * 0.5;
209 t_real
const sigma_x = std::sqrt(1 / (2 * t));
210 t_real
const sigma_y = std::sqrt(1 / (2 * s));
212 std::tuple<t_real, t_real, t_real> fit_parameters;
215 std::get<0>(fit_parameters) = sigma_x;
216 std::get<1>(fit_parameters) = sigma_y;
217 std::get<2>(fit_parameters) = theta;
218 return fit_parameters;
221 t_real
median(
const Vector<t_real> &input) {
223 auto size = input.size();
224 Vector<t_real> x(size);
225 std::copy(input.data(), input.data() + size, x.data());
226 std::sort(x.data(), x.data() + size);
227 if (std::floor(size / 2) - std::ceil(size / 2) == 0)
228 return 0.5 * (x(
int(std::floor(size / 2) - 1)) + x(
int(std::floor(size / 2))));
229 return x(
int(std::ceil(size / 2)));
232 t_real
dynamic_range(
const Image<t_complex> &model,
const Image<t_complex> &residuals,
233 const t_real &operator_norm) {
242 return std::sqrt(model.size()) * (operator_norm * operator_norm) / residuals.matrix().norm() *
243 model.cwiseAbs().maxCoeff();
247 const Vector<t_complex> &weights,
const t_real &oversample_factor,
248 const std::string &weighting_type,
const t_real &R,
249 const t_int &ftsizeu,
const t_int &ftsizev) {
254 if (weighting_type ==
"none")
return weights.array() * 0 + 1.;
255 if (weighting_type ==
"natural" or weighting_type ==
"whiten")
return weights;
256 Vector<t_complex> out_weights(weights.size());
257 if ((weighting_type ==
"uniform") or (weighting_type ==
"robust")) {
259 1. / oversample_factor;
260 Matrix<t_complex> gridded_weights = Matrix<t_complex>::Zero(ftsizev, ftsizeu);
261 for (t_int i = 0; i < weights.size(); ++i) {
264 gridded_weights(p, q) += 1 * 1;
267 t_complex
const sum_weights = (weights.array() * weights.array()).sum();
268 t_complex
const sum_grid_weights2 = (gridded_weights.array() * gridded_weights.array()).sum();
269 t_complex
const robust_scale =
270 sum_weights / sum_grid_weights2 * 25. *
271 std::pow(10, -2 * R);
272 gridded_weights = gridded_weights.array().sqrt();
273 for (t_int i = 0; i < weights.size(); ++i) {
276 if (weighting_type ==
"uniform") out_weights(i) = weights(i) / gridded_weights(p, q);
277 if (weighting_type ==
"robust") {
278 out_weights(i) = weights(i) / std::sqrt(1. + robust_scale * gridded_weights(p, q) *
279 gridded_weights(p, q));
283 throw std::runtime_error(
"Wrong weighting type, " + weighting_type +
" not recognised.");
293 std::ifstream log_file(diagnostic);
297 std::getline(log_file, s);
300 if (!std::getline(log_file, s))
break;
301 std::istringstream ss(s);
302 std::getline(ss, entry,
' ');
303 iters = std::floor(std::stod(entry));
304 std::getline(ss, entry,
' ');
305 gamma = std::stod(entry);
308 return std::make_tuple(iters, gamma);
311 Matrix<t_complex>
re_sample_ft_grid(
const Matrix<t_complex> &input,
const t_real &re_sample_ratio) {
318 if (re_sample_ratio == 1)
return input;
321 t_int old_x_centre_floor = std::floor(input.cols() * 0.5);
322 t_int old_y_centre_floor = std::floor(input.rows() * 0.5);
324 t_int old_x_centre_ceil = std::ceil(input.cols() * 0.5);
325 t_int old_y_centre_ceil = std::ceil(input.rows() * 0.5);
328 t_int new_x = std::floor(input.cols() * re_sample_ratio);
329 t_int new_y = std::floor(input.rows() * re_sample_ratio);
331 t_int new_x_centre_floor = std::floor(new_x * 0.5);
332 t_int new_y_centre_floor = std::floor(new_y * 0.5);
334 t_int new_x_centre_ceil = std::ceil(new_x * 0.5);
335 t_int new_y_centre_ceil = std::ceil(new_y * 0.5);
337 Matrix<t_complex> output = Matrix<t_complex>::Zero(new_y, new_x);
343 box_dim_x = std::min(old_x_centre_ceil, new_x_centre_ceil);
344 box_dim_y = std::min(old_y_centre_ceil, new_y_centre_ceil);
346 output.bottomLeftCorner(box_dim_y, box_dim_x) = input.bottomLeftCorner(box_dim_y, box_dim_x);
348 box_dim_x = std::min(old_x_centre_ceil, new_x_centre_ceil);
349 box_dim_y = std::min(old_y_centre_floor, new_y_centre_floor);
351 output.topLeftCorner(box_dim_y, box_dim_x) = input.topLeftCorner(box_dim_y, box_dim_x);
353 box_dim_x = std::min(old_x_centre_floor, new_x_centre_floor);
354 box_dim_y = std::min(old_y_centre_ceil, new_y_centre_ceil);
356 output.bottomRightCorner(box_dim_y, box_dim_x) = input.bottomRightCorner(box_dim_y, box_dim_x);
358 box_dim_x = std::min(old_x_centre_floor, new_x_centre_floor);
359 box_dim_y = std::min(old_y_centre_floor, new_y_centre_floor);
361 output.topRightCorner(box_dim_y, box_dim_x) = input.topRightCorner(box_dim_y, box_dim_x);
365 Matrix<t_complex>
re_sample_image(
const Matrix<t_complex> &input,
const t_real &re_sample_ratio) {
368 purify::operators::init_FFT_2d<Vector<t_complex>>(input.rows(), input.cols(), 1., ft_plan);
369 Vector<t_complex> ft_grid = Vector<t_complex>::Zero(input.size());
370 std::get<0>(fft)(ft_grid, Vector<t_complex>::Map(input.data(), input.size()));
372 Matrix<t_complex>::Map(ft_grid.data(), input.rows(), input.cols()), re_sample_ratio);
373 Vector<t_complex> output = Vector<t_complex>::Zero(input.size());
374 fft = purify::operators::init_FFT_2d<Vector<t_complex>>(new_ft_grid.rows(), new_ft_grid.cols(),
376 std::get<1>(fft)(output, Vector<t_complex>::Map(new_ft_grid.data(), new_ft_grid.size()));
377 return Matrix<t_complex>::Map(output.data(), new_ft_grid.rows(), new_ft_grid.cols()) *
#define PURIFY_LOW_LOG(...)
Low priority message.
const std::vector< t_real > u
data for u coordinate
const std::vector< t_real > v
data for v coordinate
const t_real c
speed of light in vacuum
Array< t_complex > init_weights(const Vector< t_real > &u, const Vector< t_real > &v, const Vector< t_complex > &weights, const t_real &oversample_factor, const std::string &weighting_type, const t_real &R, const t_int &ftsizeu, const t_int &ftsizev)
Calculate weightings.
t_real median(const Vector< t_real > &input)
Return median of real vector.
std::tuple< t_real, t_real, t_real > fit_fwhm(const Image< t_real > &psf, const t_int &size)
Method to fit Gaussian to PSF.
t_real SNR_to_standard_deviation(const Vector< t_complex > &y0, const t_real &SNR)
Converts SNR to RMS noise.
K::Scalar mean(const K x)
Calculate mean of vector.
Vector< t_complex > add_noise(const Vector< t_complex > &y0, const t_complex &mean, const t_real &standard_deviation)
Add guassian noise to vector.
t_int sub2ind(const t_int &row, const t_int &col, const t_int &rows, const t_int &cols)
Converts from subscript to index for matrix.
std::tuple< t_int, t_real > checkpoint_log(const std::string &diagnostic)
Reads a diagnostic file and updates parameters.
Matrix< t_complex > re_sample_ft_grid(const Matrix< t_complex > &input, const t_real &re_sample_ratio)
zero pads ft grid for image up sampling and downsampling
Matrix< t_complex > re_sample_image(const Matrix< t_complex > &input, const t_real &re_sample_ratio)
resamples image size
bool file_exists(const std::string &name)
Test to see if file exists.
t_real dynamic_range(const Image< t_complex > &model, const Image< t_complex > &residuals, const t_real &operator_norm)
Calculate the dynamic range between the model and residuals.
t_real mod(const t_real &x, const t_real &y)
Mod function modified to wrap circularly for negative numbers.
t_real standard_deviation(const K x)
Calculates the standard deviation of a vector.
Image< t_complex > convolution_operator(const Image< t_complex > &a, const Image< t_complex > &b)
Calculates the convolution between two images.
t_real calculate_l2_radius(const t_uint y_size, const t_real &sigma, const t_real &n_sigma, const std::string distirbution)
A function that calculates the l2 ball radius for sopt.
std::tuple< t_int, t_int > ind2sub(const t_int &sub, const t_int &cols, const t_int &rows)
Converts from index to subscript for matrix.