Xmipp  v3.23.11-Nereus
volume_subtraction.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Estrella Fernandez Gimenez (me.fernandez@cnb.csic.es)
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 "volume_subtraction.h"
27 #include "core/transformations.h"
28 #include <core/histogram.h>
29 #include <core/xmipp_fftw.h>
30 #include <core/xmipp_program.h>
31 #include <data/fourier_filter.h>
32 
33 
34 // Usage ===================================================================
35 void ProgVolumeSubtraction::defineParams() {
36  // Usage
37  addUsageLine("This program modifies a volume as much as possible in order "
38  "to assimilate it to another one, "
39  "without loosing the important information in it ('adjustment "
40  "process'). Then, the subtraction of "
41  "the two volumes can be optionally calculated. Sharpening: "
42  "reference volume must be an atomic "
43  "structure previously converted into a density map of the "
44  "same specimen than in input volume 2.");
45  // Parameters
46  addParamsLine("--i1 <volume> : Reference volume");
47  addParamsLine("--i2 <volume> : Volume to modify");
48  addParamsLine("[-o <structure=\"\">]\t: Volume 2 modified or "
49  "volume difference");
50  addParamsLine("\t: If no name is given, "
51  "then output_volume.mrc");
52  addParamsLine("[--sub]\t: Perform the "
53  "subtraction of the volumes. Output will be the difference");
54  addParamsLine("[--sigma <s=3>]\t: Decay of the filter "
55  "(sigma) to smooth the mask transition");
57  "[--iter <n=5>]\t: Number of iterations for the adjustment process");
58  addParamsLine("[--mask1 <mask=\"\">] : Mask for volume 1");
59  addParamsLine("[--mask2 <mask=\"\">] : Mask for volume 2");
61  "[--maskSub <mask=\"\">]\t: Mask for subtraction region");
63  "[--cutFreq <f=0>]\t: Filter both volumes with a filter which "
64  "specified cutoff frequency (i.e. resolution inverse, <0.5)");
66  "[--lambda <l=1>]\t: Relaxation factor for Fourier Amplitude POCS, "
67  "i.e. 'how much modification of volume Fourier amplitudes', between 1 "
68  "(full modification, recommended) and 0 (no modification)");
69  addParamsLine("[--radavg]\t: Match the radially averaged Fourier "
70  "amplitudes when adjusting the amplitudes instead of taking "
71  "directly them from the reference volume");
73  "[--computeEnergy]\t: Compute the energy difference between each step "
74  "(energy difference gives information about the convergence of the "
75  "adjustment process, while it can slightly slow the performance)");
77  "[--saveV1 <structure=\"\"> ]\t: Save subtraction intermediate file "
78  "(vol1 filtered) just when option --sub is passed, if not passed the "
79  "input reference volume is not modified");
81  "[--saveV2 <structure=\"\"> ]\t: Save subtraction intermediate file "
82  "(vol2 adjusted) just when option --sub is passed, if not passed the "
83  "output of the program is this file");
84 }
85 
86 // Read arguments ==========================================================
87 void ProgVolumeSubtraction::readParams() {
88  fnVol1 = getParam("--i1");
89  fnVol2 = getParam("--i2");
90  fnOutVol = getParam("-o");
91  if (fnOutVol.isEmpty())
92  fnOutVol = "output_volume.mrc";
93  performSubtraction = checkParam("--sub");
94  iter = getIntParam("--iter");
95  sigma = getIntParam("--sigma");
96  fnMask1 = getParam("--mask1");
97  fnMask2 = getParam("--mask2");
98  fnMaskSub = getParam("--maskSub");
99  cutFreq = getDoubleParam("--cutFreq");
100  lambda = getDoubleParam("--lambda");
101  fnVol1F = getParam("--saveV1");
102  if (fnVol1F.isEmpty())
103  fnVol1F = "volume1_filtered.mrc";
104  fnVol2A = getParam("--saveV2");
105  if (fnVol2A.isEmpty())
106  fnVol2A = "volume2_adjusted.mrc";
107  radavg = checkParam("--radavg");
108  computeE = checkParam("--computeEnergy");
109 }
110 
111 // Show ====================================================================
112 void ProgVolumeSubtraction::show() const {
113  std::cout << "Input volume 1:\t" << fnVol1 << std::endl
114  << "Input volume 2: " << fnVol2 << std::endl
115  << "Input mask 1: " << fnMask1 << std::endl
116  << "Input mask 2: " << fnMask2 << std::endl
117  << "Input mask sub: " << fnMaskSub << std::endl
118  << "Sigma: " << sigma << std::endl
119  << "Iterations: " << iter << std::endl
120  << "Cutoff frequency: " << cutFreq << std::endl
121  << "Relaxation factor: " << lambda << std::endl
122  << "Match radial averages:\t" << radavg << std::endl
123  << "Output:\t" << fnOutVol << std::endl;
124 }
125 
126 /* Methods used to adjust an input volume (V) to a another reference volume (V1) through
127 the use of Projectors Onto Convex Sets (POCS) */
130  DIRECT_MULTIDIM_ELEM(I, n) *= DIRECT_MULTIDIM_ELEM(mask, n);
131 }
132 
136 }
137 
139  MultidimArray<std::complex<double>> &V2Fourier, double l) {
141  double mod = std::abs(DIRECT_MULTIDIM_ELEM(V2Fourier, n));
142  if (mod > 1e-10) // Condition to avoid divide by zero, values smaller than
143  // this threshold are considered zero
144  DIRECT_MULTIDIM_ELEM(V2Fourier, n) *=
145  ((1 - l) + l * DIRECT_MULTIDIM_ELEM(V1FourierMag, n)) / mod;
146  }
147 }
148 
149 void POCSFourierAmplitudeRadAvg(MultidimArray<std::complex<double>> &V, double l, const MultidimArray<double> &rQ,
150 int V1size_x, int V1size_y, int V1size_z) {
151  int V1size2_x = V1size_x/2;
152  double V1sizei_x = 1.0/V1size_x;
153  int V1size2_y = V1size_y/2;
154  double V1sizei_y = 1.0/V1size_y;
155  int V1size2_z = V1size_z/2;
156  double V1sizei_z = 1.0/V1size_z;
157  double wx;
158  double wy;
159  double wz;
160  for (int k=0; k<ZSIZE(V); ++k)
161  {
162  FFT_IDX2DIGFREQ_FAST(k,V1size_z,V1size2_z,V1sizei_z,wz)
163  double wz2 = wz*wz;
164  for (int i=0; i<YSIZE(V); ++i)
165  {
166  FFT_IDX2DIGFREQ_FAST(i,V1size_y,V1size2_y,V1sizei_y,wy)
167  double wy2_wz2 = wy*wy + wz2;
168  for (int j=0; j<XSIZE(V); ++j)
169  {
170  FFT_IDX2DIGFREQ_FAST(j,V1size_x,V1size2_x,V1sizei_x,wx)
171  double w = sqrt(wx*wx + wy2_wz2);
172  auto iw = std::min((int)floor(w*V1size_x), (int)XSIZE(rQ) -1);
173  DIRECT_A3D_ELEM(V,k,i,j)*=(1-l)+l*DIRECT_MULTIDIM_ELEM(rQ,iw);
174  }
175  }
176  }
177 }
178 
179 void POCSMinMax(MultidimArray<double> &V, double v1m, double v1M) {
181  double val = DIRECT_MULTIDIM_ELEM(V, n);
182  if (val < v1m)
183  DIRECT_MULTIDIM_ELEM(V, n) = v1m;
184  else if (val > v1M)
185  DIRECT_MULTIDIM_ELEM(V, n) = v1M;
186  }
187 }
188 
189 void POCSFourierPhase(const MultidimArray<std::complex<double>> &phase,
190  MultidimArray<std::complex<double>> &FI) {
192  DIRECT_MULTIDIM_ELEM(FI, n) =
194 }
195 
196 /* Other methods needed to pre-process and operate with the volumes */
197 void ProgVolumeSubtraction::extractPhase(MultidimArray<std::complex<double>> &FI) const{
199  auto *ptr = (double *)&DIRECT_MULTIDIM_ELEM(FI, n);
200  double phi = atan2(*(ptr + 1), *ptr);
201  DIRECT_MULTIDIM_ELEM(FI, n) = std::complex<double>(cos(phi), sin(phi));
202  }
203 }
204 
205 void ProgVolumeSubtraction::computeEnergy(MultidimArray<double> &Vdif, const MultidimArray<double> &Vact) const{
206  Vdif = Vdif - Vact;
207  double energy = 0;
209  energy += DIRECT_MULTIDIM_ELEM(Vdif, n) * DIRECT_MULTIDIM_ELEM(Vdif, n);
210  energy = sqrt(energy / MULTIDIM_SIZE(Vdif));
211 }
212 
213 void ProgVolumeSubtraction::centerFFTMagnitude(MultidimArray<double> &VolRad,
214  MultidimArray<std::complex<double>> &VolFourierRad,
215  MultidimArray<double> &VolFourierMagRad) const{
216  FourierTransformer transformerRad;
217  transformerRad.completeFourierTransform(VolRad, VolFourierRad);
218  CenterFFT(VolFourierRad, true);
219  FFT_magnitude(VolFourierRad, VolFourierMagRad);
220  VolFourierMagRad.setXmippOrigin();
221 }
222 
223 void radialAverage(const MultidimArray<double> &VolFourierMag,
224  const MultidimArray<double> &V, MultidimArray<double> &radial_mean) {
225  MultidimArray<double> radial_count;
226  int Vsize2_x = int(XSIZE(V))/2;
227  double Vsizei_x = 1.0/int(XSIZE(V));
228  int Vsize2_y = int(YSIZE(V))/2;
229  double Vsizei_y = 1.0/int(YSIZE(V));
230  int Vsize2_z = int(ZSIZE(V))/2;
231  double Vsizei_z = 1.0/int(ZSIZE(V));
232  double wx;
233  double wy;
234  double wz;
235  auto maxrad = int(floor(sqrt(Vsize2_x*Vsize2_x + Vsize2_y*Vsize2_y + Vsize2_z*Vsize2_z)));
236  radial_count.initZeros(maxrad);
237  radial_mean.initZeros(maxrad);
238  for (int k=0; k<Vsize2_z; ++k)
239  {
240  FFT_IDX2DIGFREQ_FAST(k,ZSIZE(V),Vsize2_z,Vsizei_z,wz)
241  double wz2 = wz*wz;
242  for (int i=0; i<Vsize2_y; ++i)
243  {
244  FFT_IDX2DIGFREQ_FAST(i,YSIZE(V),Vsize2_y,Vsizei_y,wy)
245  double wy2_wz2 = wy*wy + wz2;
246  for (int j=0; j<Vsize2_x; ++j)
247  {
248  FFT_IDX2DIGFREQ_FAST(j,XSIZE(V),Vsize2_x,Vsizei_x,wx)
249  double w = sqrt(wx*wx + wy2_wz2);
250  auto iw = (int)round(w*int(XSIZE(V)));
251  DIRECT_A1D_ELEM(radial_mean,iw)+=DIRECT_A3D_ELEM(VolFourierMag,k,i,j);
252  DIRECT_A1D_ELEM(radial_count,iw)+=1.0;
253  }
254  }
255  }
256  radial_mean/= radial_count;
257 }
258 
260  const MultidimArray<double> &vMag, const MultidimArray<double> &V1,
261  const MultidimArray<double> &V) {
262  // Compute the quotient of the radial mean of the volumes to use it in POCS amplitude
263  MultidimArray<double> radial_meanV1;
264  radialAverage(v1Mag, V1, radial_meanV1);
265  MultidimArray<double> radial_meanV;
266  radialAverage(vMag, V, radial_meanV);
267  MultidimArray<double> radQuotient = radial_meanV1 / radial_meanV;
268  FOR_ALL_ELEMENTS_IN_ARRAY1D(radQuotient)
269  {
270  radQuotient(i) = std::min(radQuotient(i), 1.0);
271  if (radQuotient(i) != radQuotient(i)) // Check if it is NaN and change it by 0
272  radQuotient(i) = 0;
273  }
274  return radQuotient;
275 }
276 
277 void createFilter(FourierFilter &filter2, double cutFreq) {
278  filter2.FilterBand = LOWPASS;
279  filter2.FilterShape = RAISED_COSINE;
280  filter2.raised_w = 0.02;
281  filter2.w1 = cutFreq;
282 }
283 
285  const MultidimArray<double> &mask, const FileName &fnVol1F,
286  const FileName &fnVol2A, FourierFilter &filter2, double cutFreq) {
287  Image<double> V1Filtered;
288  V1Filtered() = V1();
289  if (cutFreq != 0){
290  filter2.applyMaskSpace(V1Filtered());
291  }
292  if (!fnVol1F.isEmpty()) {
293  V1Filtered.write(fnVol1F);
294  }
295  if (!fnVol2A.isEmpty()) {
296  V.write(fnVol2A);
297  }
299  DIRECT_MULTIDIM_ELEM(V1(), n) =
300  DIRECT_MULTIDIM_ELEM(V1(), n) * (1 - DIRECT_MULTIDIM_ELEM(mask, n)) +
301  (DIRECT_MULTIDIM_ELEM(V1Filtered(), n) -
303  DIRECT_MULTIDIM_ELEM(V1Filtered(), n))) *
304  DIRECT_MULTIDIM_ELEM(mask, n);
305  return V1;
306 }
307 
309  FourierTransformer transformer;
311  MultidimArray<double> magnitude;
312  transformer.FourierTransform(volume, fourier, false);
313  FFT_magnitude(fourier, magnitude);
314  return magnitude;
315 }
316 
317 MultidimArray<double> ProgVolumeSubtraction::createMask(const Image<double> &volume, const FileName &fnM1, const FileName &fnM2) {
319  if (fnM1 != "" && fnM2 != "") {
320  Image<double> mask1;
321  Image<double> mask2;
322  mask1.read(fnM1);
323  mask2.read(fnM2);
324  mask = mask1() * mask2();
325  } else {
326  mask.resizeNoCopy(volume());
327  mask.initConstant(1.0);
328  }
329  return mask;
330 }
331 
332 void ProgVolumeSubtraction::filterMask(MultidimArray<double> &mask) const{
333  FourierFilter Filter;
334  Filter.FilterShape = REALGAUSSIAN;
335  Filter.FilterBand = LOWPASS;
336  Filter.w1 = sigma;
337  Filter.applyMaskSpace(mask);
338 }
339 
340 MultidimArray<std::complex<double>> ProgVolumeSubtraction::computePhase(MultidimArray<double> &volume) {
342  transformer2.FourierTransform(volume, phase, true);
343  extractPhase(phase);
344  return phase;
345 }
346 
347 MultidimArray<double> ProgVolumeSubtraction::getSubtractionMask(const FileName &fnMSub, MultidimArray<double> mask){
348  if (fnMSub.isEmpty()){
349  filterMask(mask);
350  return mask;
351  }
352  else {
353  Image<double> masksub;
354  masksub.read(fnMSub);
355  return masksub();
356  }
357 }
358 
359 /* Core of the program: processing needed to adjust input
360  * volume V2 to reference volume V1. Several iteration of
361  * this processing should be run. */
362 void ProgVolumeSubtraction::runIteration(Image<double> &V,Image<double> &V1,const MultidimArray<double> &radQuotient,
363  const MultidimArray<double> &V1FourierMag,const MultidimArray<std::complex<double>> &V2FourierPhase,
364  const MultidimArray<double> &mask, FourierFilter &filter2) {
365  transformer2.FourierTransform(V(), V2Fourier, false);
366  if (radavg) {
367  auto V1size_x = (int)XSIZE(V1());
368  auto V1size_y = (int)YSIZE(V1());
369  auto V1size_z = (int)ZSIZE(V1());
370  POCSFourierAmplitudeRadAvg(V2Fourier, lambda, radQuotient, V1size_x, V1size_y, V1size_z);
371  }
372  else {
373  POCSFourierAmplitude(V1FourierMag, V2Fourier, lambda);
374  }
375  transformer2.inverseFourierTransform();
376  if (computeE) {
377  computeEnergy(Vdiff(), V());
378  Vdiff = V;
379  }
380  POCSMinMax(V(), v1min, v1max);
381  if (computeE) {
382  computeEnergy(Vdiff(), V());
383  Vdiff = V;
384  }
385  POCSmask(mask, V());
386  if (computeE) {
387  computeEnergy(Vdiff(), V());
388  Vdiff = V;
389  }
390  transformer2.FourierTransform();
391  POCSFourierPhase(V2FourierPhase, V2Fourier);
392  transformer2.inverseFourierTransform();
393  if (computeE) {
394  computeEnergy(Vdiff(), V());
395  Vdiff = V;
396  }
397  POCSnonnegative(V());
398  if (computeE) {
399  computeEnergy(Vdiff(), V());
400  Vdiff = V;
401  }
402  double std2 = V().computeStddev();
403  V() *= std1 / std2;
404  if (computeE) {
405  computeEnergy(Vdiff(), V());
406  Vdiff = V;
407  }
408 
409  if (cutFreq != 0) {
410  filter2.generateMask(V());
411  filter2.do_generate_3dmask = true;
412  filter2.applyMaskSpace(V());
413  if (computeE) {
414  computeEnergy(Vdiff(), V());
415  Vdiff = V;
416  }
417  }
418 }
419 
420 void ProgVolumeSubtraction::run() {
421  show();
422  Image<double> V1;
423  V1.read(fnVol1);
424  auto mask = createMask(V1, fnMask1, fnMask2);
425  POCSmask(mask, V1());
426  POCSnonnegative(V1());
427  V1().computeDoubleMinMax(v1min, v1max);
428  Image<double> V;
429  V.read(fnVol2);
430  POCSmask(mask, V());
431  POCSnonnegative(V());
432  std1 = V1().computeStddev();
433  Vdiff = V;
434  auto V2FourierPhase = computePhase(V());
435  auto V1FourierMag = computeMagnitude(V1());
436  auto V2FourierMag = computeMagnitude(V());
437  auto radQuotient = computeRadQuotient(V1FourierMag, V2FourierMag, V1(), V());
438  FourierFilter filter2;
439  createFilter(filter2, cutFreq);
440  for (n = 0; n < iter; ++n) {
441  runIteration(V, V1, radQuotient, V1FourierMag, V2FourierPhase, mask, filter2);
442  }
443  if (performSubtraction) {
444  auto masksub = getSubtractionMask(fnMaskSub, mask);
445  V1.read(fnVol1);
446  V = subtraction(V1, V, masksub, fnVol1F, fnVol2A, filter2, cutFreq);
447  }
448  /* The output of this program is either a modified
449  * version of V (V') or the subtraction between
450  * V1 and V' if performSubtraction flag is activated' */
451  V.write(fnOutVol);
452 }
#define YSIZE(v)
void min(Image< double > &op1, const Image< double > &op2)
double getDoubleParam(const char *param, int arg=0)
Image< double > subtraction(Image< double > V1, Image< double > &V, const MultidimArray< double > &mask, const FileName &fnVol1F, const FileName &fnVol2A, FourierFilter &filter2, double cutFreq)
__host__ __device__ float2 floor(const float2 v)
void inverseFourierTransform()
Definition: xmipp_fftw.cpp:329
#define MULTIDIM_SIZE(v)
void resizeNoCopy(const MultidimArray< T1 > &v)
void createFilter(FourierFilter &filter2, double cutFreq)
void sqrt(Image< double > &op)
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 POCSnonnegative(MultidimArray< double > &I)
doublereal * w
void initConstant(T val)
void abs(Image< double > &op)
#define i
ql0001_ & k(htemp+1),(cvec+1),(atemp+1),(bj+1),(bl+1),(bu+1),(x+1),(clamda+1), &iout, infoqp, &zero,(w+1), &lenw,(iw+1), &leniw, &glob_grd.epsmac
void POCSmask(const MultidimArray< double > &mask, MultidimArray< double > &I)
void CenterFFT(MultidimArray< T > &v, bool forward)
Definition: xmipp_fft.h:291
#define FFT_IDX2DIGFREQ_FAST(idx, size, size_2, isize, freq)
Definition: xmipp_fft.h:54
#define DIRECT_A1D_ELEM(v, i)
MultidimArray< double > computeMagnitude(MultidimArray< double > &volume)
const char * getParam(const char *param, int arg=0)
#define XSIZE(v)
#define RAISED_COSINE
void max(Image< double > &op1, const Image< double > &op2)
#define FOR_ALL_DIRECT_ELEMENTS_IN_MULTIDIMARRAY(v)
void radialAverage(const MultidimArray< double > &VolFourierMag, const MultidimArray< double > &V, MultidimArray< double > &radial_mean)
#define ZSIZE(v)
#define DIRECT_MULTIDIM_ELEM(v, n)
void POCSFourierAmplitude(const MultidimArray< double > &V1FourierMag, MultidimArray< std::complex< double >> &V2Fourier, double l)
void mod(const MultidimArray< T > &x, MultidimArray< T > &m, double y)
#define DIRECT_A3D_ELEM(v, k, i, j)
void FourierTransform(T &v, T1 &V, bool getCopy=true)
Definition: xmipp_fftw.h:166
#define j
#define FOR_ALL_ELEMENTS_IN_ARRAY1D(v)
int round(double x)
Definition: ap.cpp:7245
bool isEmpty() const
void POCSMinMax(MultidimArray< double > &V, double v1m, double v1M)
void completeFourierTransform(T &v, T1 &V)
Definition: xmipp_fftw.h:315
bool checkParam(const char *param)
int read(const FileName &name, DataMode datamode=DATA, size_t select_img=ALL_IMAGES, bool mapData=false, int mode=WRITE_READONLY)
#define REALGAUSSIAN
void addUsageLine(const char *line, bool verbatim=false)
void initZeros(const MultidimArray< T1 > &op)
void POCSFourierPhase(const MultidimArray< std::complex< double >> &phase, MultidimArray< std::complex< double >> &FI)
void POCSFourierAmplitudeRadAvg(MultidimArray< std::complex< double >> &V, double l, const MultidimArray< double > &rQ, int V1size_x, int V1size_y, int V1size_z)
int getIntParam(const char *param, int arg=0)
void FFT_magnitude(const MultidimArray< std::complex< double > > &v, MultidimArray< double > &mag)
Definition: xmipp_fftw.cpp:393
void generateMask(MultidimArray< double > &v)
MultidimArray< double > computeRadQuotient(const MultidimArray< double > &v1Mag, const MultidimArray< double > &vMag, const MultidimArray< double > &V1, const MultidimArray< double > &V)
#define LOWPASS
void addParamsLine(const String &line)
void applyMaskSpace(MultidimArray< double > &v)