Xmipp  v3.23.11-Nereus
shift_corr_estimator.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 <iostream>
27 #include "shift_corr_estimator.h"
28 
29 namespace Alignment {
30 
31 
32 template<typename T>
33 void ShiftCorrEstimator<T>::init2D(const std::vector<HW*> &hw, AlignType type,
34  const FFTSettings<T> &settings, size_t maxShift,
35  bool includingBatchFT, bool includingSingleFT,
36  bool allowDataOverwrite) {
37  if (1 != hw.size()) {
38  REPORT_ERROR(ERR_ARG_INCORRECT, "A single CPU thread expected");
39  }
40  release();
41  try {
42  m_cpu = dynamic_cast<CPU*>(hw.at(0));
43  } catch (std::bad_cast&) {
44  REPORT_ERROR(ERR_ARG_INCORRECT, "Instance of CPU expected");
45  }
46 
47  AShiftCorrEstimator<T>::init2D(type, settings, maxShift,
48  includingBatchFT, includingSingleFT, allowDataOverwrite);
49 
50  this->m_isInit = true;
51 }
52 
53 template<typename T>
55  auto isReady = (this->m_isInit && (AlignType::OneToN == this->m_type) && this->m_includingSingleFT);
56  if ( ! isReady) {
57  REPORT_ERROR(ERR_LOGIC_ERROR, "Not ready to load a reference signal");
58  }
59 
60  // perform FT
61  FFTwT<T>::fft(m_singleToFD, ref, m_single_FD);
62 
63  // update state
64  this->m_is_ref_FD_loaded = true;
65 }
66 
67 template<typename T>
70 
71  // allocate plans and space for data in Fourier domain
72  m_batchToSD = FFTwT<T>::createPlan(*m_cpu, *this->m_settingsInv);
73  auto settingsForw = this->m_settingsInv->createInverse();
74  if (this->m_includingBatchFT) {
75  m_batch_FD = new std::complex<T>[this->m_settingsInv->fElemsBatch()];
76  m_batch_SD = new T[this->m_settingsInv->sElemsBatch()];
77  m_batchToFD = FFTwT<T>::createPlan(*m_cpu, settingsForw);
78  }
79  if (this->m_includingSingleFT) {
80  m_single_FD = new std::complex<T>[this->m_settingsInv->fDim().xyzPadded()];
81  m_singleToFD = FFTwT<T>::createPlan(*m_cpu, settingsForw.createSingle());
82  }
83 }
84 
85 template<typename T>
87  // host memory
88  if (this->m_includingSingleFT) {
89  delete[] m_single_FD;
90  }
91  if (this->m_includingBatchFT) {
92  delete[] m_batch_FD;
93  delete[] m_batch_SD;
94  }
95 
96  // FT plans
97  FFTwT<T>::release(m_singleToFD);
98  FFTwT<T>::release(m_batchToFD);
99  FFTwT<T>::release(m_batchToSD);
100 
102 
104 }
105 
106 template<typename T>
109 
110  m_cpu = nullptr;
111 
112  // host memory
113  m_single_FD = nullptr;
114  m_batch_FD = nullptr;
115  m_batch_SD = nullptr;
116 
117  // plans
118  m_singleToFD = nullptr;
119  m_batchToFD = nullptr;
120  m_batchToSD = nullptr;
121 }
122 
123 
124 template<typename T>
125 void ShiftCorrEstimator<T>::load2DReferenceOneToN(const std::complex<T> *ref) {
126  auto isReady = (this->m_isInit && (AlignType::OneToN == this->m_type));
127  if ( ! isReady) {
128  REPORT_ERROR(ERR_LOGIC_ERROR, "Not ready to load a reference signal");
129  }
130 
131  // simply remember the pointer. Expect that nobody will change it meanwhile
132  // We won't change it, but since generally speaking, we do change this pointer
133  // remove the const
134  m_single_FD = const_cast<std::complex<T>*>(ref);
135 
136  // update state
137  this->m_is_ref_FD_loaded = true;
138 }
139 
140 template<typename T>
142  std::complex<T> *inOut, bool center) {
143  bool isReady = (this->m_isInit && (AlignType::OneToN == this->m_type) && this->m_is_ref_FD_loaded);
144 
145  if ( ! isReady) {
146  REPORT_ERROR(ERR_LOGIC_ERROR, "Not ready to execute. Call init() before");
147  }
148 
149  sComputeCorrelations2DOneToN(
150  *m_cpu,
151  inOut, m_single_FD,
152  this->m_settingsInv->fDim(),
153  center);
154 }
155 
156 template<typename T>
158  const HW &hw,
159  std::complex<T> *inOut,
160  const std::complex<T> *ref,
161  const Dimensions &dims,
162  bool center) {
163  const CPU *cpu;
164  try {
165  cpu = &dynamic_cast<const CPU&>(hw);
166  } catch (std::bad_cast&) {
167  REPORT_ERROR(ERR_ARG_INCORRECT, "Instance of CPU expected");
168  }
169  return sComputeCorrelations2DOneToN(*cpu, inOut, ref, dims, center);
170 }
171 
172 template<typename T>
173 template<bool CENTER>
175  const HW &hw,
176  std::complex<T> *__restrict inOut,
177  const std::complex<T> *__restrict ref,
178  const Dimensions &__restrict dims) {
179  if (CENTER) {
180  // we cannot assert xDim, as we don't know if the spatial size was even
181  assert(0 == (dims.y() % 2));
182  }
183  assert(0 < dims.x());
184  assert(0 < dims.y());
185  assert(1 == dims.z());
186  assert(0 < dims.n());
187 
188  const size_t maxN = dims.n();
189  const size_t maxY = dims.y();
190  const size_t maxX = dims.x();
191 
192  for (size_t n = 0; n < maxN; ++n) {
193  size_t offsetN = n * dims.xyzPadded();
194  for (size_t y = 0; y < maxY; ++y) {
195  int centerCoeff = (0 == y % 2) ? 1 : -1;
196  size_t offsetY = y * dims.xPadded();
197  for (size_t x = 0; x < maxX; ++x) {
198  size_t destIndex = offsetN + offsetY + x;
199  auto r = ref[offsetY + x];
200  auto o = r * std::conj(inOut[destIndex]);
201  inOut[destIndex] = o;
202  if (CENTER) {
203  inOut[destIndex] *= centerCoeff;
204  centerCoeff *= -1;
205  }
206  }
207  }
208  }
209 }
210 
211 template<typename T>
213  T *others) {
214  bool isReady = (this->m_isInit && (AlignType::OneToN == this->m_type)
215  && this->m_is_ref_FD_loaded && this->m_includingBatchFT);
216 
217  if ( ! isReady) {
218  REPORT_ERROR(ERR_LOGIC_ERROR, "Not ready to execute. Call init() before");
219  }
220 
221  // reserve enough space for shifts
222  this->m_shifts2D.reserve(this->m_settingsInv->fDim().n());
223  // process signals
224  for (size_t offset = 0; offset < this->m_settingsInv->fDim().n(); offset += this->m_settingsInv->batch()) {
225  // how many signals to process
226  size_t toProcess = std::min(this->m_settingsInv->batch(), this->m_settingsInv->fDim().n() - offset);
227 
228  T *batchStart;
229  if (toProcess == this->m_settingsInv->batch()) {
230  // we process whole batch, so we don't need to copy data
231  batchStart = others + offset * this->m_settingsInv->sDim().xyPadded();
232  } else {
233  assert(this->m_settingsInv->batch() <= this->m_settingsInv->sDim().n());
234  // less than 'batch' signals are left, so we need to process last 'batch'
235  // signals to avoid invalid memory access
236  batchStart = others
237  + (this->m_settingsInv->sDim().n() - this->m_settingsInv->batch())
238  * this->m_settingsInv->sDim().xy();
239  }
240 
241  // perform FT
242  FFTwT<T>::fft(m_batchToFD, batchStart, m_batch_FD);
243 
244  // compute shifts
245  auto shifts = computeShifts2DOneToN(
246  *m_cpu,
247  m_batch_FD,
248  m_batch_SD,
249  m_single_FD,
250  this->m_settingsInv->createBatch(), // always process whole batch, as we do it to avoid copying memory
251  m_batchToSD,
252  this->m_maxShift);
253 
254  // append shifts to existing results
255  this->m_shifts2D.insert(this->m_shifts2D.end(),
256  // in case of the last iteration, take only the shifts we actually need
257  shifts.begin() + this->m_settingsInv->batch() - toProcess,
258  shifts.end());
259  }
260 
261  // update state
262  this->m_is_shift_computed = true;
263 }
264 
265 template<typename T>
267  const CPU &cpu,
268  std::complex<T> *othersF,
269  T *othersS,
270  std::complex<T> *ref,
271  const FFTSettings<T> &settings,
272  void *plan,
273  size_t maxShift) {
274  // we need even input in order to perform the shift (in FD, while correlating) properly
275  assert(0 == (settings.sDim().x() % 2));
276  assert(0 == (settings.sDim().y() % 2));
277  assert(1 == settings.sDim().zPadded());
278 
279  // correlate signals and shift FT so that it will be centered after IFT
280  sComputeCorrelations2DOneToN(cpu,
281  othersF, ref,
282  settings.fDim(), true);
283 
284  // perform IFT
285  FFTwT<T>::ifft(plan, othersF, othersS);
286 
287  // compute shifts
288  auto maxIndices = std::vector<float>(settings.sDim().n(), -1.f);
290  othersS, maxIndices.data(), nullptr, maxShift);
291  // convert absolute indices to 2D position
292  // FIXME DS extract this to some indexing utils or sth
293  int cX = settings.sDim().x() / 2;
294  int cY = settings.sDim().y() / 2;
295  auto result = std::vector<Point2D<float>>();
296  const int x = settings.sDim().x();
297  for (auto i : maxIndices) {
298  result.emplace_back(((int)i % x) - cX, ((int)i / x) - cY);
299  }
300  return result;
301 }
302 
303 template<typename T>
306  if (this->m_settingsInv->isInPlace()) {
307  REPORT_ERROR(ERR_VALUE_INCORRECT, "Only out-of-place transform is supported");
308  }
309  if (this->m_allowDataOverwrite) {
310  std::cerr << "'AllowDataOverwrite' flat ignored. This is not supported yet\n";
311  }
312 }
313 
314 // explicit instantiation
315 template class ShiftCorrEstimator<float>;
316 template class ShiftCorrEstimator<double>;
317 
318 } /* namespace Alignment */
void min(Image< double > &op1, const Image< double > &op2)
alglib::complex conj(const alglib::complex &z)
Definition: ap.cpp:4988
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
virtual void init2D(const std::vector< HW *> &hw, AlignType type, const FFTSettings< T > &dims, size_t maxShift, bool includingBatchFT, bool includingSingleFT, bool allowDataOverwrite)=0
static double * y
void load2DReferenceOneToN(const std::complex< T > *ref) override
static const fftw_plan createPlan(const CPU &cpu, const FFTSettings< double > &settings, bool isDataAligned=false)
void release()
Definition: fftwT.cpp:180
constexpr Dimensions fDim() const
Definition: fft_settings.h:82
Definition: hw.h:35
std::complex< T > * fft(T *inOut)
Definition: fftwT.cpp:204
doublereal * x
#define i
CUDA_HD constexpr size_t x() const
Definition: dimensions.h:51
void init2D(const std::vector< HW *> &hw, AlignType type, const FFTSettings< T > &dims, size_t maxShift, bool includingBatchFT, bool includingSingleFT, bool allowDataOverwrite) override
constexpr size_t zPadded() const
Definition: dimensions.h:73
viol type
void computeCorrelations2DOneToN(std::complex< T > *inOut, bool center) override
Incorrect argument received.
Definition: xmipp_error.h:113
CUDA_HD constexpr size_t y() const
Definition: dimensions.h:60
CUDA_HD constexpr size_t n() const
Definition: dimensions.h:78
constexpr Dimensions sDim() const
Definition: fft_settings.h:78
T * ifft(std::complex< T > *inOut)
Definition: fftwT.cpp:252
static std::vector< Point2D< float > > computeShifts2DOneToN(const CPU &cpu, std::complex< T > *othersF, T *othersS, std::complex< T > *ref, const FFTSettings< T > &settings, void *plan, size_t maxShift)
Definition: cpu.h:37
Incorrect value received.
Definition: xmipp_error.h:195
void computeShift2DOneToN(T *others) override
static void sFindMax2DAroundCenter(const CPU &cpu, const Dimensions &dims, const T *data, float *positions, T *values, size_t maxDist)
int * n
static void sComputeCorrelations2DOneToN(const HW &hw, std::complex< T > *inOut, const std::complex< T > *ref, const Dimensions &dims, bool center)
Some logical error in the pipeline.
Definition: xmipp_error.h:147