Xmipp  v3.23.11-Nereus
phantom_movie.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: David Strelak (davidstrelak@gmail.com)
4  *
5  * Unidad de Bioinformatica of Centro Nacional de Biotecnologia , CSIC
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  * 02111-1307 USA
21  *
22  * All comments concerning this program package may be sent to the
23  * e-mail address 'xmipp@cnb.csic.es'
24  ***************************************************************************/
25 
26 #include "phantom_movie.h"
27 #include <random>
28 #include "data/fourier_filter.h"
29 
30 template <typename T>
31 auto PhantomMovie<T>::shiftX(size_t t) const
32 {
33  const auto tf = static_cast<float>(t);
34  if (dispParams.simple)
35  return dispParams.a1 * tf;
36  return dispParams.a1 * tf + dispParams.a2 * tf * tf + std::cos(tf / 10.f) / 10.f;
37 };
38 
39 template <typename T>
40 auto PhantomMovie<T>::shiftY(size_t t) const
41 {
42  const auto tf = static_cast<float>(t);
43  if (dispParams.simple)
44  return dispParams.b1 * tf;
45  return dispParams.b1 * tf + dispParams.b2 * tf * tf + (std::sin(tf * tf)) / 5.f;
46 };
47 
48 template <typename T>
49 T PhantomMovie<T>::bilinearInterpolation(const MultidimArray<T> &src, float x, float y) const
50 {
51  auto x_center = static_cast<float>(src.xdim) / 2.f;
52  auto y_center = static_cast<float>(src.ydim) / 2.f;
53  const auto xdim = static_cast<float>(src.xdim);
54  x += x_center;
55  y += y_center;
56  auto xf = std::floor(x);
57  auto xc = std::ceil(x);
58  auto yf = std::floor(y);
59  auto yc = std::ceil(y);
60  auto xw = x - xf;
61  auto yw = y - yf;
62  auto vff = src.data[static_cast<size_t>(xdim * yf + xf)];
63  auto vfc = src.data[static_cast<size_t>(xdim * yc + xf)];
64  auto vcf = src.data[static_cast<size_t>(xdim * yf + xc)];
65  auto vcc = src.data[static_cast<size_t>(xdim * yc + xc)];
66  return vff * (1.f - xw) * (1.f - yw) + vcf * xw * (1.f - yw) + vfc * (1.f - xw) * yw + vcc * xw * yw;
67 }
68 
69 template <typename T>
70 void PhantomMovie<T>::displace(float &x, float &y, size_t n) const
71 {
72  auto x_shift = options.skipShift ? 0 : shiftX(params.req_size.n() - n - 1); // 'reverse' the order (see doc)
73  auto y_shift = options.skipShift ? 0 : shiftY(params.req_size.n() - n - 1); // 'reverse' the order (see doc)
74  if (options.skipBarrel)
75  {
76  x += x_shift;
77  y += y_shift;
78  }
79  else
80  {
81  auto x_center = static_cast<float>(params.req_size.x()) / 2.f;
82  auto y_center = static_cast<float>(params.req_size.y()) / 2.f;
83  auto k1 = dispParams.k1_start + static_cast<float>(n) * (dispParams.k1_end - dispParams.k1_start) / (static_cast<float>(params.req_size.n()) - 1);
84  auto k2 = dispParams.k2_start + static_cast<float>(n) * (dispParams.k2_end - dispParams.k2_start) / (static_cast<float>(params.req_size.n()) - 1);
85  auto y_norm = (y - y_center + (options.shiftAfterBarrel ? 0 : y_shift)) / y_center;
86  auto x_norm = (x - x_center + (options.shiftAfterBarrel ? 0 : x_shift)) / x_center;
87  auto r_out = sqrt(x_norm * x_norm + y_norm * y_norm);
88  auto r_out_2 = r_out * r_out;
89  auto r_out_4 = r_out_2 * r_out_2;
90  auto scale = (1 + k1 * r_out_2 + k2 * r_out_4);
91  x = (x_norm * scale * x_center) + x_center + (options.shiftAfterBarrel ? x_shift : 0);
92  y = (y_norm * scale * y_center) + y_center + (options.shiftAfterBarrel ? y_shift : 0);
93  }
94 }
95 
96 template <typename T>
98 {
99  switch (content.type)
100  {
101  case PhantomType::grid:
102  return addGrid(frame);
103  case PhantomType::particleCircle:
104  return addCircles(frame);
105  case PhantomType::particleCross:
106  return addCrosses(frame);
107  default:
108  throw std::logic_error("Unsupported PhantomType");
109  }
110 }
111 
112 template <typename T>
114 {
115  std::cout << "Generating circles" << std::endl;
116  auto drawCircle = [&frame](const auto r, const auto x, const auto y, const int thickness, const auto val)
117  {
118  const auto xdim = frame.xdim;
119  for (int j = -r - thickness; j <= r + thickness; ++j)
120  {
121  for (int i = -r- thickness; i <= r + thickness; ++i)
122  {
123  int d = sqrt(j * j + i * i);
124  if (d >= r - thickness / 2 && d <= r + thickness / 2)
125  {
126  frame.data[(y + j) * xdim + i + x] += val;
127  }
128  }
129  }
130  };
131 
132  std::mt19937 gen(content.seed);
133  const auto border = content.maxSize / 2 + content.thickness / 2 + 3; // + 3 for various rounding errors
134  std::uniform_int_distribution<size_t> distX(border, frame.xdim - border);
135  std::uniform_int_distribution<size_t> distY(border, frame.ydim - border);
136  std::uniform_int_distribution<> size(content.minSize, content.maxSize);
137  for (auto i = 0; i < content.count; ++i)
138  {
139  auto r = size(gen) / 2;
140  auto x = distX(gen);
141  auto y = distY(gen);
142  drawCircle(r, x, y, content.thickness, content.signal_val);
143  }
144 }
145 
146 template <typename T>
148 {
149  std::cout << "Generating crosses" << std::endl;
150  auto drawCross = [&frame](const auto s, const auto x, const auto y, const auto val)
151  {
152  const auto xdim = frame.xdim;
153  for (int d = 0; d < s; ++d)
154  {
155  frame.data[(y - d) * xdim - d + x] += val;
156  frame.data[(y - d) * xdim + d + x] += val;
157  frame.data[(y + d) * xdim - d + x] += val;
158  frame.data[(y + d) * xdim + d + x] += val;
159  }
160  };
161 
162  std::mt19937 gen(content.seed);
163  std::uniform_int_distribution<size_t> distX(content.maxSize / 2 + content.thickness / 2, frame.xdim - 1 - content.maxSize / 2 - content.thickness / 2);
164  std::uniform_int_distribution<size_t> distY(content.maxSize / 2 + content.thickness / 2, frame.ydim - 1 - content.maxSize / 2 - content.thickness / 2);
165  std::uniform_int_distribution<> size(content.minSize, content.maxSize);
166  const auto xdim = frame.xdim;
167  const int thickness = content.thickness;
168  for (auto i = 0; i < content.count; ++i)
169  {
170  auto s = size(gen) / 2;
171  auto x = distX(gen);
172  auto y = distY(gen);
173  // draw cross
174  for (int t = 0; t < thickness / 2; ++t)
175  {
176  // move the center to change the thickness
177  drawCross(s, x, y - t, content.signal_val);
178  drawCross(s, x - t, y, content.signal_val);
179  drawCross(s, x + t, y, content.signal_val);
180  drawCross(s, x, y + t, content.signal_val);
181  }
182  }
183 }
184 
185 template <typename T>
187 {
188  std::cout << "Generating grid" << std::endl;
189  const auto xdim = frame.xdim;
190  const auto ydim = frame.ydim;
191  // add rows
192  for (auto y = content.ystep - (content.thickness / 2); y < ydim - (content.thickness / 2) + 1; y += content.ystep)
193  {
194  for (auto t = 0; t < content.thickness; ++t)
195  {
196  size_t y_offset = (y + t) * xdim;
197  for (size_t x = 0; x < xdim; ++x)
198  {
199  size_t index = y_offset + x;
200  frame.data[index] += content.signal_val;
201  }
202  }
203  }
204  // add columns
205  for (auto x = content.xstep; x < xdim - (content.thickness / 2) + 1; x += content.xstep)
206  {
207  for (int t = 0; t < content.thickness; ++t)
208  {
209  size_t x_offset = (x + t);
210  for (size_t y = 0; y < ydim; ++y)
211  {
212  size_t index = x_offset + y * xdim;
213  frame.data[index] += content.signal_val;
214  }
215  }
216  }
217 }
218 
219 template <typename T>
221 {
222  auto x_max_shift = 0.f;
223  auto y_max_shift = 0.f;
224  const auto x = static_cast<float>(params.req_size.x());
225  const auto y = static_cast<float>(params.req_size.y());
226  for (size_t n = 0; n < params.req_size.n(); ++n)
227  {
228  auto x_0 = 0.f;
229  auto y_0 = 0.f;
230  auto x_n = x - 1.f;
231  auto y_n = y - 1.f;
232  displace(x_0, y_0, n); // top left corner
233  displace(x_n, y_n, n); // bottom right corner
234  // barrel deformation moves to center, so we need to read outside of the edge - [0,0] will be negative and [n-1,n-1] will be bigger than that
235  auto x_shift = std::max(std::abs(x_0), x_n - x - 1.f);
236  auto y_shift = std::max(std::abs(y_0), y_n - y - 1.f);
237  x_max_shift = std::max(x_max_shift, x_shift);
238  y_max_shift = std::max(y_max_shift, y_shift);
239  }
240  // new size must incorporate 'gaps' on both sides, min values should be negative, max values positive
241  // the shift might not be uniform, so the safer solution is to have the bigger gap on both sides
242  // + 10 is in case we made a rounding mistake on multiple places :/
243  auto x_new = params.req_size.x() + 2 * static_cast<size_t>(std::ceil(x_max_shift)) + 10;
244  auto y_new = params.req_size.y() + 2 * static_cast<size_t>(std::ceil(y_max_shift)) + 10;
245  std::cout << "Due to displacement, working with frames of size [" << x_new << ", " << y_new << "]\n";
246  return MultidimArray<T>(1, 1, static_cast<int>(y_new), static_cast<int>(x_new));
247 }
248 
249 template <typename T>
251 {
252  std::cout << "Applying low-pass filter\n";
253  auto filter = FourierFilter();
254  filter.w1 = ice.low_w1;
255  filter.raised_w = ice.low_raised_w;
256  filter.FilterBand = LOWPASS;
257  filter.FilterShape = RAISED_COSINE;
258  filter.apply(frame);
259 }
260 
261 template <typename T>
263 {
264  std::cout << "Generating ice\n";
265  std::mt19937 gen(ice.seed);
266  std::normal_distribution<> d(ice.avg, ice.stddev);
267  const auto nzyxdim = frame.nzyxdim;
268  for (size_t i = 0; i < nzyxdim; ++i)
269  {
270  frame[i] = d(gen);
271  }
272 }
273 
274 template <typename T>
275 template <bool SKIP_DOSE>
276 void PhantomMovie<T>::generateMovie(const MultidimArray<T> &refFrame) const
277 {
278  MultidimArray<T> frame(1, 1, static_cast<int>(params.req_size.y()), static_cast<int>(params.req_size.x()));
279  params.fn_out.deleteFile();
280  std::mt19937 gen(content.seed);
281 
282  auto genFrame = [&frame, &refFrame, &gen, this](auto n)
283  {
284  float x_center = static_cast<float>(frame.xdim) / 2.f;
285  float y_center = static_cast<float>(frame.ydim) / 2.f;
286  std::cout << "Processing frame " << n << std::endl;
287  for (size_t y = 0; y < params.req_size.y(); ++y)
288  {
289  for (size_t x = 0; x < params.req_size.x(); ++x)
290  {
291  auto x_tmp = static_cast<float>(x);
292  auto y_tmp = static_cast<float>(y);
293  displace(x_tmp, y_tmp, n);
294  // move coordinate system to center - [0, 0] will be in the center of the frame
295  auto val = bilinearInterpolation(refFrame, x_tmp - x_center, y_tmp - y_center);
296  if (!SKIP_DOSE)
297  {
298  auto dist = std::poisson_distribution<int>(val * content.dose);
299  val = dist(gen);
300  }
301  frame.data[y * frame.xdim + x] = val;
302  }
303  }
304  };
305 
306  for (size_t n = 0; n < params.req_size.n(); ++n)
307  {
308  genFrame(n);
309  Image<T> tmp(frame);
310  tmp.write(params.fn_out, n + 1, true, WRITE_REPLACE);
311  }
312 }
313 
314 template <typename T>
316 {
317  auto refFrame = findWorkSize();
318  if (!options.skipIce)
319  {
320  generateIce(refFrame);
321  applyLowPass(refFrame);
322  refFrame.rangeAdjust(ice.min, ice.max);
323  }
324  addContent(refFrame);
325  if (options.skipDose)
326  {
327  generateMovie<true>(refFrame);
328  }
329  else
330  {
331  generateMovie<false>(refFrame);
332  }
333  if (!params.fn_gain.empty())
334  {
335  Image<T> gain(static_cast<int>(params.req_size.x()), static_cast<int>(params.req_size.y()));
336  gain().initConstant(1);
337  gain.write(params.fn_gain);
338  }
339  if (!params.fn_dark.empty())
340  {
341  Image<T> dark(static_cast<int>(params.req_size.x()), static_cast<int>(params.req_size.y()));
342  dark().initConstant(0);
343  dark.write(params.fn_dark);
344  }
345 }
346 
347 // template class PhantomMovie<float>; // can't be used because the fourier filter doesn't support it
348 template class PhantomMovie<double>;
#define yc
__host__ __device__ float2 floor(const float2 v)
void sqrt(Image< double > &op)
static double * y
void write(const FileName &name="", size_t select_img=ALL_IMAGES, bool isStack=false, int mode=WRITE_OVERWRITE, CastWriteMode castMode=CW_CAST, int _swapWrite=0)
void border(const MultidimArray< double > &img, MultidimArray< double > &border)
Definition: morphology.cpp:167
void abs(Image< double > &op)
doublereal * x
#define i
doublereal * d
viol index
double * f
#define RAISED_COSINE
void max(Image< double > &op1, const Image< double > &op2)
void run() const
#define j
int * n
#define LOWPASS
#define xc