Xmipp  v3.23.11-Nereus
angular_discrete_assign.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  *
3  * Authors: Carlos Oscar Sanchez Sorzano coss@cnb.csic.es (2002)
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 
27 #include "classification/base_algorithm.h" // std::vector to std::cout
29 #include "data/wavelet.h"
30 #include "data/mask.h"
31 #include "data/filters.h"
32 
33 
34 // Empty constructor =======================================================
36 {
37  produces_a_metadata = true;
38  produces_an_output = true;
39 }
40 
41 // Read arguments ==========================================================
43 {
45  fn_ref = getParam("--ref");
46  fn_sym = getParam("--sym");
47  max_proj_change = getDoubleParam("--max_proj_change");
48  max_psi_change = getDoubleParam("--max_psi_change");
49  max_shift_change = getDoubleParam("--max_shift_change");
50  psi_step = getDoubleParam("--psi_step");
51  shift_step = getDoubleParam("--shift_step");
52  th_discard = getDoubleParam("--keep");
53  smin = getIntParam("--smin");
54  smax = getIntParam("--smax");
55  pick = getIntParam("--pick");
56  tell = 0;
57  if (checkParam("--dont_check_mirrors"))
58  checkMirrors=0;
59  else
60  checkMirrors=1;
61  if (checkParam("--show_rot_tilt"))
63  if (checkParam("--show_psi_shift"))
65  if (checkParam("--show_options"))
66  tell |= TELL_OPTIONS;
67  search5D = checkParam("--search5D");
68 }
69 
70 // Show ====================================================================
72 {
73  if (!verbose)
74  return;
76  std::cout << "Reference images: " << fn_ref << std::endl
77  << "Max proj change: " << max_proj_change << std::endl
78  << "Max psi change: " << max_psi_change << " step: " << psi_step << std::endl
79  << "Max shift change: " << max_shift_change << " step: " << shift_step << std::endl
80  << "Keep %: " << th_discard << std::endl
81  << "smin: " << smin << std::endl
82  << "smax: " << smax << std::endl
83  << "Pick: " << pick << std::endl
84  << "Show level: " << tell << std::endl
85  << "5D search: " << search5D << std::endl
86  << "Check mirrors: " << checkMirrors << std::endl
87  ;
88 }
89 
90 // usage ===================================================================
92 {
93  addUsageLine("Make a projection assignment using wavelets on a discrete library of projections");
94  addUsageLine("+This program assigns Euler angles to experimental projections by matching ");
95  addUsageLine("+with ideal projections. This matching is done via a DWT correlation. For ");
96  addUsageLine("+every experimental projection, different in-plane rotations and shifts are ");
97  addUsageLine("+tried (by exhaustive search). For each possible combination of these two ");
98  addUsageLine("+variables, the best correlating ideal projection is sought using a fast ");
99  addUsageLine("+multirresolution algorithm.");
100  addUsageLine("+The method is fully described at http://www.ncbi.nlm.nih.gov/pubmed/15099579");
101  defaultComments["-i"].clear();
102  defaultComments["-i"].addComment("List of images to align");
103  defaultComments["-i"].addComment("+ Alignment parameters can be provided ");
104  defaultComments["-i"].addComment("+ Only the shifts are taken in consideration ");
105  defaultComments["-i"].addComment("+ in global searches; in local searches, all ");
106  defaultComments["-i"].addComment("+ parameters in the initial docfile are considered.");
107  defaultComments["-o"].clear();
108  defaultComments["-o"].addComment("Metadata with output alignment");
110  addParamsLine(" --ref <selfile> : Metadata with the reference images and their angles");
111  addParamsLine(" :+Must be created with [[angular_project_library_v3][angular_project_library]]");
112  addParamsLine(" [--sym <symmetry_file=\"\">] : Symmetry file if any");
113  addParamsLine(" :+The definition of the symmetry is described at [[transform_symmetrize_v3][transform_symmetrize]]");
114  addParamsLine(" [--max_shift_change <r=0>] : Maximum change allowed in shift");
115  addParamsLine(" [--psi_step <ang=5>] : Step in psi in degrees");
116  addParamsLine(" [--shift_step <r=1>] : Step in shift in pixels");
117  addParamsLine("==+Extra parameters==");
118  addParamsLine(" [--search5D] : Perform a 5D search instead of 3D+2D");
119  addParamsLine(" [--dont_check_mirrors] : Do not check mirrors of the input images");
120  addParamsLine(" :+In this case, the projection library should have the mirrors.");
121  addParamsLine(" [--max_proj_change <ang=-1>] : Maximum change allowed in rot-tilt");
122  addParamsLine(" [--max_psi_change <ang=-1>] : Maximum change allowed in psi");
123  addParamsLine(" [--keep <th=50>] : How many images are kept each round (%)");
124  addParamsLine(" [--smin <s=1>] : Finest scale to consider (lowest value=0)");
125  addParamsLine(" [--smax <s=-1>] : Coarsest scale to consider (highest value=log2(Xdim))");
126  addParamsLine(" [--pick <mth=1>] : 0 --> maximum of the first group");
127  addParamsLine(" : 1 --> maximum of the most populated");
128  addParamsLine(" [--show_rot_tilt] : Show the rot-tilt process");
129  addParamsLine(" [--show_psi_shift] : Show the psi-shift process");
130  addParamsLine(" [--show_options] : Show final options among which");
131  addParamsLine(" : the angles are selected");
132  addExampleLine("Typical use:",false);
133  addExampleLine("xmipp_angular_project_library -i referenceVolume.vol -o reference.stk --sampling_rate 5");
134  addExampleLine("xmipp_angular_discrete_assign -i projections.sel -o discrete_assignment.xmd --ref reference.doc");
135 }
136 
137 // Produce side information ================================================
139 {
140  // Read input reference image names
141  SF_ref.read(fn_ref);
142  size_t refYdim, refXdim, refZdim, refNdim;
143  getImageSize(SF_ref,refYdim, refXdim, refZdim, refNdim);
144  if (refYdim != NEXT_POWER_OF_2(refYdim) || refXdim != NEXT_POWER_OF_2(refXdim))
146  "reference images must be of a size that is power of 2");
147 
148  // Produce side info of the angular distance computer
152 
153  // Read the angle file
154  rot.resize(SF_ref.size());
155  tilt.resize(SF_ref.size());
156  int i = 0;
157  for (size_t objId : SF_ref.ids())
158  {
159  SF_ref.getValue(MDL_ANGLE_ROT, rot[i], objId);
160  SF_ref.getValue(MDL_ANGLE_TILT, tilt[i], objId);
161  i++;
162  }
163 
164  // Build mask for subbands
165  Mask_no.resize(refYdim, refXdim);
166  Mask_no.initConstant(-1);
167 
168  if (smax == -1)
169  smax = Get_Max_Scale(refYdim) - 3;
170  SBNo = (smax - smin + 1) * 3 + 1;
171  SBsize.resize(SBNo);
172 
173  Mask Mask(INT_MASK);
175  Mask.R1 = CEIL((double)refXdim / 2.0);
176  Mask.resize(refYdim, refXdim);
177 
178  int m = 0, s;
179  for (s = smax; s >= smin; s--)
180  {
181  for (int q = 0; q <= 3; q++)
182  {
183  if (q == 0 && s != smax)
184  continue;
185  Mask.smin = s;
186  Mask.smax = s;
187  Mask.quadrant = Quadrant2D(q);
188  Mask.generate_mask();
189 
190  const MultidimArray<int> imask2D=Mask.get_binary_mask();
192  if (DIRECT_A2D_ELEM(imask2D, i, j))
193  {
194  Mask_no(i, j) = m;
195  SBsize(m)++;
196  }
197 
198  m++;
199  }
200  }
201 
202  // Produce library
203  produce_library();
204 
205  // Save a little space
206  SF_ref.clear();
207 }
208 
209 // PostProcess ---------------------------------------------------------------
211 {
212  if (single_image)
214 }
215 
216 // Produce library -----------------------------------------------------------
218 {
219  Image<double> I;
220  int number_of_imgs = SF_ref.size();
222 
223  // Create space for all the DWT coefficients of the library
224  Matrix1D<int> SBidx(SBNo);
225  for (int m = 0; m < SBNo; m++)
226  {
227  library.emplace_back(number_of_imgs, SBsize(m));
228  }
229  library_power.initZeros(number_of_imgs, SBNo);
230 
231  if (verbose)
232  {
233  std::cerr << "Generating reference library ...\n";
234  init_progress_bar(number_of_imgs);
235  }
236  int n = 0, nstep = XMIPP_MAX(1, number_of_imgs / 60); // For progress bar
237  for (size_t objId : SF_ref.ids())
238  {
239  I.readApplyGeo(SF_ref, objId);
240  library_name.push_back(I.name());
241 
242  // Make and distribute its DWT coefficients in the different PCA bins
243  I().statisticsAdjust(0., 1.);
244  DWT(I(), I());
245  SBidx.initZeros();
247  {
248  int m = Mask_no(i, j);
249  if (m != -1)
250  {
251  double coef = I(i, j), coef2 = coef * coef;
252  library[m](n, SBidx(m)++) = coef;
253  for (int mp = m; mp < SBNo; mp++)
254  library_power(n, mp) += coef2;
255  }
256  }
257 
258  // Prepare for next iteration
259  if (++n % nstep == 0 && verbose)
260  progress_bar(n);
261  }
262  if (verbose)
264 }
265 
266 // Build candidate list ------------------------------------------------------
268  bool *candidate_list, std::vector<double> &cumulative_corr,
269  std::vector<double> &sumxy)
270 {
271  int refNo = rot.size();
272  cumulative_corr.resize(refNo);
273  sumxy.resize(refNo);
274  for (int i = 0; i < refNo; i++)
275  {
276  candidate_list[i] = true;
277  sumxy[i] = cumulative_corr[i] = 0;
278  if (max_proj_change != -1)
279  {
280  double dummy_rot = rot[i], dummy_tilt = tilt[i], dummy_psi;
281  double ang_distance = distance_prm.SL.computeDistance(
282  I.rot(), I.tilt(), 0,
283  dummy_rot, dummy_tilt, dummy_psi,
284  true, false, false);
285  candidate_list[i] = (ang_distance <= max_proj_change);
286 #ifdef DEBUG
287 
288  std::cout << "(" << I.rot() << "," << I.tilt() << ") and ("
289  << rot[i] << "," << tilt[i] << ") --> " << ang_distance << std::endl;
290 #endif
291 
292  }
293  }
294 }
295 
296 // Refine candidate list ---------------------------------------------------
298  int m,
299  Matrix1D<double> &dwt,
300  bool *candidate_list, std::vector<double> &cumulative_corr,
301  Matrix1D<double> &x_power, std::vector<double> &sumxy,
302  double th)
303 {
304  int dimp = SBsize(m);
305  int imax = rot.size();
306  const MultidimArray<double> &library_m = library[m];
307  std::vector<double> sortedCorr;
308  sortedCorr.reserve(imax);
309  for (int i = 0; i < imax; i++)
310  {
311  if (candidate_list[i])
312  {
313  double sumxyp = 0.0;
314  // Loop unrolling
315  unsigned long jmax=4*(dimp/4);
316  for (int j = 0; j < dimp; j+=4)
317  {
318  int j_1=j+1;
319  int j_2=j+2;
320  int j_3=j+3;
321  sumxyp += VEC_ELEM(dwt,j) * DIRECT_A2D_ELEM(library_m,i, j) +
322  VEC_ELEM(dwt,j_1) * DIRECT_A2D_ELEM(library_m,i, j_1) +
323  VEC_ELEM(dwt,j_2) * DIRECT_A2D_ELEM(library_m,i, j_2) +
324  VEC_ELEM(dwt,j_3) * DIRECT_A2D_ELEM(library_m,i, j_3);
325  }
326  for (int j = jmax+1;j<dimp;++j)
327  sumxyp += VEC_ELEM(dwt,j) * DIRECT_A2D_ELEM(library_m,i, j);
328 
329  sumxy[i] += sumxyp;
330 
331  double corr = sumxy[i] / sqrt(DIRECT_A2D_ELEM(library_power,i, m) *
332  VEC_ELEM(x_power,m));
333  cumulative_corr[i] = corr;
334  sortedCorr.push_back(corr);
335 
336  if (tell & TELL_ROT_TILT)
337  {
338  std::cout << "Candidate " << i << " corr= " << cumulative_corr[i]
339  << " rot= " << rot[i] << " tilt= " << tilt[i] << std::endl;
340  }
341  }
342  }
343  std::sort(sortedCorr.begin(),sortedCorr.end());
344  auto idx=(int)floor(sortedCorr.size()*(1-th/100.0));
345 
346  double corr_th = sortedCorr[idx];
347 
348  // Remove all those projections below the threshold
349  for (int i = 0; i < imax; i++)
350  if (candidate_list[i])
351  candidate_list[i] = (cumulative_corr[i] >= corr_th);
352 
353  // Show the percentil used
354  if (tell & TELL_ROT_TILT)
355  std::cout << "# Percentil " << corr_th << std::endl << std::endl;
356 }
357 
358 // Predict rot and tilt ----------------------------------------------------
360  double &assigned_rot, double &assigned_tilt, int &best_ref_idx)
361 {
362  if (XSIZE(I()) != NEXT_POWER_OF_2(XSIZE(I())) ||
363  YSIZE(I()) != NEXT_POWER_OF_2(YSIZE(I())))
365  "experimental images must be of a size that is power of 2");
366 
367  // Build initial candidate list
368  auto* candidate_list=new bool[rot.size()];
369  std::vector<double> cumulative_corr;
370  std::vector<double> sumxy;
371  build_ref_candidate_list(I, candidate_list, cumulative_corr, sumxy);
372 
373  // Make DWT of the input image and build vectors for comparison
374  std::vector<Matrix1D<double> * > Idwt;
375  Matrix1D<double> x_power(SBNo);
376  x_power.initZeros();
377  Matrix1D<int> SBidx(SBNo);
378  SBidx.initZeros();
379  for (int m = 0; m < SBNo; m++)
380  {
381  auto *subband = new Matrix1D<double>;
382  subband->resize(SBsize(m));
383  Idwt.push_back(subband);
384  }
385 
386  I().statisticsAdjust(0., 1.);
387  DWT(I(), I());
389  {
390  int m = DIRECT_A2D_ELEM(Mask_no,i, j);
391  if (m != -1)
392  {
393  double coef = DIRECT_A2D_ELEM(IMGMATRIX(I), i, j), coef2 = coef * coef;
394  (*(Idwt[m]))(SBidx(m)++) = coef;
395  for (int mp = m; mp < SBNo; mp++)
396  VEC_ELEM(x_power,mp) += coef2;
397  }
398  }
399 
400  // Measure correlation for all possible PCAs
401  // These variables are used to compute the correlation at a certain
402  // scale
403  for (int m = 0; m < SBNo; m++)
404  {
405  // Show image name
406  if (tell & TELL_ROT_TILT)
407  std::cout << "# " << I.name() << " m=" << m
408  << " current rot=" << I.rot()
409  << " current tilt=" << I.tilt() << std::endl;
411  candidate_list, cumulative_corr,
412  x_power, sumxy, th_discard);
413  }
414 
415  // Select the maximum
416  int best_i = -1;
417  bool first = true;
418  int N_max = 0;
419  int imax = rot.size();
420  for (int i = 0; i < imax; i++)
421  if (candidate_list[i])
422  {
423  if (first)
424  {
425  best_i = i;
426  first = false;
427  N_max = 1;
428  }
429  else if (cumulative_corr[i] > cumulative_corr[best_i])
430  best_i = i;
431  else if (cumulative_corr[i] == cumulative_corr[best_i])
432  N_max++;
433  }
434 
435  if (N_max == 0)
436  {
437  if (verbose)
438  std::cerr << "Predict_angles: Empty candidate list for image "
439  << I.name() << std::endl;
440  assigned_rot = I.rot();
441  assigned_tilt = I.tilt();
442  return 0;
443  }
444 
445  // There are several maxima, choose one randomly
446  if (N_max != 1)
447  {
448  int selected = FLOOR(rnd_unif(0, 3));
449  for (int i = 0; i < imax && selected >= 0; i++)
450  if (candidate_list[i])
451  if (cumulative_corr[i] == cumulative_corr[best_i])
452  {
453  best_i = i;
454  selected--;
455  }
456  }
457 
458  // Free asked memory
459  for (int m = 0; m < SBNo; m++)
460  delete Idwt[m];
461 
462  assigned_rot = rot[best_i];
463  assigned_tilt = tilt[best_i];
464  best_ref_idx = best_i;
465  delete [] candidate_list;
466  return cumulative_corr[best_i];
467 }
468 
469 // Evaluate candidates by correlation ----------------------------------------
471  const std::vector<double> &vscore,
472  const std::vector<int> &candidate_idx,
473  std::vector<double> &candidate_rate, double weight)
474 {
475  // Compute maximum and minimum of correlations
476  int imax = vscore.size();
477  double min_score, max_score;
478  min_score = max_score = vscore[0];
479  for (int i = 1; i < imax; i++)
480  {
481  if (vscore[i] < min_score)
482  min_score = vscore [i];
483  if (vscore[i] > max_score)
484  max_score = vscore [i];
485  }
486 
487  // Divide the correlation segment in as many pieces as candidates
488  double score_step = (max_score - min_score) / 10;
489 
490  int jmax = candidate_idx.size();
491  for (int j = 0; j < jmax; j++)
492  {
493  int i = candidate_idx[j];
494  int points;
495  if (score_step != 0)
496  points = FLOOR((vscore[i] - min_score) / score_step);
497  else
498  points = 10;
499  if (tell & TELL_PSI_SHIFT)
500  std::cout << "Candidate (" << i << ") score=" << vscore[i]
501  << " points=" << points << std::endl;
502  candidate_rate[j] += weight * points;
503  }
504 
505  if (tell & TELL_PSI_SHIFT)
506  std::cout << "Evaluation:" << candidate_rate << std::endl
507  << "Threshold for obtaining a 7 in score: "
508  << min_score + 7*score_step << std::endl;
509  return min_score + 7*score_step;
510 }
511 
512 // Group images --------------------------------------------------------------
513 //#define DEBUG
514 void ProgAngularDiscreteAssign::group_views(const std::vector<double> &vrot,
515  const std::vector<double> &vtilt, const std::vector<double> &vpsi,
516  const std::vector<int> &best_idx, const std::vector<int> &candidate_idx,
517  std::vector< std::vector<int> > &groups)
518 {
519  for (size_t j = 0; j < best_idx.size(); j++)
520  {
521  int i = candidate_idx[best_idx[j]];
522 #ifdef DEBUG
523 
524  std::cout << "Looking for a group for image " << best_idx[j] << std::endl;
525 #endif
526 
527  double roti = vrot[i];
528  double tilti = vtilt[i];
529  double psii = vpsi[i];
530  // See if there is any suitable group
531  bool assigned = false;
532  size_t g;
533  for (g = 0; g < groups.size(); g++)
534  {
535  bool fits = true;
536  for (size_t jp = 0; jp < groups[g].size(); jp++)
537  {
538  int ip = candidate_idx[groups[g][jp]];
539  double ang_distance = distance_prm.SL.computeDistance(
540  vrot[ip], vtilt[ip], vpsi[ip],
541  roti, tilti, psii, false, false, false);
542 #ifdef DEBUG
543 
544  std::cout << " comparing with " << groups[g][jp] << " d="
545  << ang_distance << std::endl;
546 #endif
547 
548  if (ang_distance > 15)
549  {
550  fits = false;
551  break;
552  }
553  }
554  if (fits)
555  {
556  assigned = true;
557  break;
558  }
559  }
560 
561  if (!assigned)
562  {
563 #ifdef DEBUG
564  std::cout << "Creating a new group\n";
565 #endif
566  // Create a new group with the first item in the list
567  std::vector<int> group;
568  group.push_back(best_idx[j]);
569  groups.push_back(group);
570  }
571  else
572  {
573 #ifdef DEBUG
574  std::cout << "Assigning to group " << g << std::endl;
575 #endif
576  // Insert the image in the fitting group
577  groups[g].push_back(best_idx[j]);
578  }
579  }
580 
581  // Check if there are so many groups as images.
582  // If that is the case, then everything is a single group
583  // if denoising is not used
584  if (groups.size() == best_idx.size())
585  {
586  groups.clear();
587  std::vector<int> group;
588  for (size_t j = 0; j < best_idx.size(); j++)
589  group.push_back(best_idx[j]);
590  groups.push_back(group);
591  }
592 }
593 #undef DEBUG
594 
595 // Pick view -----------------------------------------------------------------
597  std::vector< std::vector<int> > &groups,
598  std::vector<double> &vscore,
599  const std::vector<int> &candidate_idx, const std::vector<double> &candidate_rate)
600 {
601  if (method == 0)
602  {
603  // This one returns the most scored image of the first group
604  double best_rate = -1e38;
605  int best_j=0, jmax = groups[0].size();
606  for (int j = 0; j < jmax; j++)
607  // Select the best with the scoreelation
608  if (vscore[candidate_idx[groups[0][j]]] > best_rate)
609  {
610  best_j = j;
611  best_rate = vscore[candidate_idx[groups[0][j]]];
612  }
613  return groups[0][best_j];
614  }
615  else if (method == 1)
616  {
617  // Sum the rates in all groups
618  std::vector<double> group_rate;
619  group_rate.reserve(groups.size());
620  int best_g=0;
621  double best_group_rate = -1e38;
622  for (size_t g = 0; g < groups.size(); g++)
623  {
624  double temp_group_rate = 0;
625  for (size_t j = 0; j < groups[g].size(); j++)
626  temp_group_rate += candidate_rate[groups[g][j]];
627  group_rate.push_back(temp_group_rate);
628  if (temp_group_rate > best_group_rate)
629  {
630  best_g = g;
631  best_group_rate = group_rate[g];
632  }
633  }
634 
635  // Check that there are not two groups with the same score
636  int groups_with_max_rate = 0;
637  for (size_t g = 0; g < groups.size(); g++)
638  if (group_rate[g] == best_group_rate)
639  groups_with_max_rate++;
640 #ifdef DEBUG
641 
642  if (groups_with_max_rate > 1)
643  {
644  std::cout << "There are two groups with maximum rate\n";
645  }
646 #endif
647 
648  // Take the best image within that group
649  int best_j=0;
650  double best_rate = -1e38;
651  for (size_t j = 0; j < groups[best_g].size(); j++)
652  {
653 #ifdef NEVER_DEFINED
654  // Select the best with the rate
655  if (candidate_rate[groups[best_g][j]] > best_rate)
656  {
657  best_j = j;
658  best_rate = candidate_rate[groups[best_g][j]];
659  }
660 #endif
661  // Select the best with the scoreelation
662  if (vscore[candidate_idx[groups[best_g][j]]] > best_rate)
663  {
664  best_j = j;
665  best_rate = vscore[candidate_idx[groups[best_g][j]]];
666  }
667  }
668 
669  // Check that there are not two images with the same rate
670  int images_with_max_rate = 0;
671  for (size_t j = 0; j < groups[best_g].size(); j++)
672 #ifdef NEVER_DEFINED
673  // Select the best with the rate
674  if (candidate_rate[groups[best_g][j]] == best_rate)
675  images_with_max_rate++;
676 #endif
677  // Select the best with scoreelation
678  if (vscore[candidate_idx[groups[best_g][j]]] == best_rate)
679  images_with_max_rate++;
680  if (images_with_max_rate > 1)
681  {
682  // If there are several with the same punctuation take the one
683  // with the best scoreelation
684  double best_score = -1e38;
685  for (size_t j = 0; j < groups[best_g].size(); j++)
686  {
687  if (vscore[candidate_idx[groups[best_g][j]]] > best_score &&
688  candidate_rate[groups[best_g][j]] == best_rate)
689  {
690  best_j = j;
691  best_score = vscore[candidate_idx[groups[best_g][j]]];
692  }
693  }
694  }
695  return groups[best_g][best_j];
696  }
697  REPORT_ERROR(ERR_UNCLASSIFIED,"The code should not have reached this point");
698 }
699 
700 // Run ---------------------------------------------------------------------
701 // Predict shift and psi ---------------------------------------------------
702 // #define DEBUG
703 void ProgAngularDiscreteAssign::processImage(const FileName &fnImg, const FileName &fnImgOut, const MDRow &rowIn, MDRow &rowOut)
704 {
705  // Read the image and take its angles from the Metadata
706  // if they are available. If not, take them from the header.
707  // If not, set them to 0.
708  Image<double> img;
709  img.read(fnImg);
710  img.setGeo(rowIn);
711  if (rowIn.containsLabel(MDL_ANGLE_PSI))
712  img.setPsi(-img.psi());
713 
714  double best_rot, best_tilt, best_psi, best_shiftX, best_shiftY,
715  best_score = 0, best_rate;
716 
717  Image<double> Ip;
718  Ip = img;
719  Matrix1D<double> shift(2);
720 
721  // Get the 2D alignment shift
722  double Xoff = img.Xoff();
723  double Yoff = img.Yoff();
724 
725  // Establish psi limits
726  double psi0, psiF;
727  if (max_psi_change == -1)
728  {
729  psi0 = -180;
730  psiF = 180 - psi_step;
731  }
732  else
733  {
734  psi0 = img.psi() - max_psi_change;
735  psiF = img.psi() + max_psi_change;
736  }
737  double R2 = max_shift_change * max_shift_change;
738 
739  // Search in the psi-shift space
740  int N_trials = 0;
741  std::vector<double> vshiftX, vshiftY, vpsi, vrot, vtilt, vcorr,
742  vproj_error, vproj_compact, vang_jump, vscore;
743  std::vector<int> vref_idx;
744 
745  double backup_max_shift_change = max_shift_change;
746  if (!search5D)
747  max_shift_change = 0;
748 
749 #ifdef DEBUG
750 
751  img.write("PPPoriginal.xmp");
752  double bestCorr=-1e38;
753 #endif
754 
755  for (int flip = 0; flip <= checkMirrors; ++flip)
756  {
757  for (double shiftX = Xoff - max_shift_change; shiftX <= Xoff + max_shift_change; shiftX += shift_step)
758  {
759  for (double shiftY = Yoff - max_shift_change; shiftY <= Yoff + max_shift_change; shiftY += shift_step)
760  {
761  {
762  if ((shiftX - Xoff) * (shiftX - Xoff) + (shiftY - Yoff) * (shiftY - Yoff) > R2)
763  continue;
764  for (double psi = psi0; psi <= psiF; psi += psi_step)
765  {
766  N_trials++;
767 
768  // Flip if necessary
769  Ip() = img();
770  if (flip)
771  Ip().selfReverseX();
772 
773  // Shift image if necessary
774  if (shiftX != 0 || shiftY != 0)
775  {
776  VECTOR_R2(shift, shiftX, shiftY);
777  selfTranslate(xmipp_transformation::LINEAR, Ip(), shift, xmipp_transformation::WRAP);
778  }
779 
780  // Rotate image if necessary
781  // Adding 2 is a trick to avoid that the 0, 90, 180 and 270
782  // are treated in a different way
783  selfRotate(xmipp_transformation::LINEAR, Ip(), psi + 2, xmipp_transformation::WRAP);
784  selfRotate(xmipp_transformation::LINEAR, Ip(), -2, xmipp_transformation::WRAP);
785 #ifdef DEBUG
786  Image<double> Ipsave;
787  Ipsave() = Ip();
788 #endif
789 
790  // Project the resulting image onto the visible space
791  double proj_error = 0.0, proj_compact = 0.0;
792 
793  // Search for the best tilt, rot angles
794  double rotp, tiltp;
795  int best_ref_idx;
796  double corrp =
797  predict_rot_tilt_angles(Ip, rotp, tiltp, best_ref_idx);
798 
799  double aux_rot = rotp, aux_tilt = tiltp, aux_psi = psi;
800  double ang_jump = distance_prm.SL.computeDistance(
801  img.rot(), img.tilt(), img.psi(),
802  aux_rot, aux_tilt, aux_psi,
803  false, false, false);
804 
805  double shiftXp = shiftX;
806  double shiftYp = shiftY;
807  double psip = psi;
808  if (flip)
809  {
810  // std::cout << " before flipping " << rotp << " " << tiltp << " " << psip << " " << shiftXp << " " << shiftYp << " " << corrp << std::endl;
811  shiftXp = -shiftXp;
812  double newrot, newtilt, newpsi;
813  Euler_mirrorY(rotp, tiltp, psi, newrot, newtilt, newpsi);
814  rotp = newrot;
815  tiltp = newtilt;
816  psip = newpsi;
817  }
818 
819  vshiftX.push_back(shiftXp);
820  vshiftY.push_back(shiftYp);
821  vrot.push_back(rotp);
822  vtilt.push_back(tiltp);
823  vpsi.push_back(psip);
824  vcorr.push_back(corrp);
825  vproj_error.push_back(proj_error);
826  vproj_compact.push_back(proj_compact);
827  vang_jump.push_back(ang_jump);
828  vref_idx.push_back(best_ref_idx);
829  // std::cout << flip << " " << rotp << " " << tiltp << " " << psip << " " << shiftXp << " " << shiftYp << " " << corrp << std::endl;
830 
831 #ifdef DEBUG
832  if (corrp > bestCorr)
833  {
834  Ipsave.write("PPPafter_denoising.xmp");
835  Image<double> Iref;
836  Iref.read(library_name[best_ref_idx]);
837  Iref.write("PPPref.xmp");
838  std::cerr << "This is index " << vcorr.size() - 1 << std::endl;
839  std::cerr << "corrp=" << corrp << "\nPress any key\n";
840  bestCorr = corrp;
841  char c;
842  std::cin >> c;
843  }
844 #endif
845  }
846  }
847  }
848  }
849  }
850 
851  // Compute extrema of all scoring factors
852  double max_corr = vcorr[0], min_corr = vcorr[0];
853  double max_proj_error = vproj_error[0], min_proj_error = vproj_error[0];
854  double max_proj_compact = vproj_compact[0], min_proj_compact = vproj_compact[0];
855  for (int i = 1; i < N_trials; i++)
856  {
857  // Compute extrema of projection error
858  if (vproj_error[i] < min_proj_error)
859  min_proj_error = vproj_error[i];
860  if (vproj_error[i] > max_proj_error)
861  max_proj_error = vproj_error[i];
862 
863  // Compute extrema of correlation
864  if (vcorr[i] < min_corr)
865  min_corr = vcorr[i];
866  if (vcorr[i] > max_corr)
867  max_corr = vcorr[i];
868 
869  // Compute extrema of projection error
870  if (vproj_compact[i] < min_proj_compact)
871  min_proj_compact = vproj_compact[i];
872  if (vproj_compact[i] > max_proj_compact)
873  max_proj_compact = vproj_compact[i];
874  }
875 
876  // Score each projection
877  vscore.reserve(vcorr.size());
878  for (int i = 0; i < N_trials; i++)
879  {
880  vscore.push_back(vcorr[i]);
881  if (tell & TELL_PSI_SHIFT)
882  std::cout << "i=" << i
883  << " shiftX= " << vshiftX[i] << " shiftY= " << vshiftY[i]
884  << " psi= " << vpsi[i]
885  << " rot= " << vrot[i]
886  << " tilt= " << vtilt[i]
887  << " score= " << vscore[i]
888  << " corr= " << vcorr[i]
889  << " proj_error= " << vproj_error[i]
890  << " proj_compact= " << vproj_compact[i]
891  << " refidx= " << vref_idx[i]
892  << " ang_jump= " << vang_jump[i]
893  << std::endl;
894  }
895 
896  // Is the psi range circular?
897  bool circular = realWRAP(vpsi[0] - psi_step, -180, 180) ==
898  realWRAP(vpsi[N_trials-1], -180, 180);
899 
900  // Compute minimum and maximum of the correlation and projection error
901  double avg_score_maxima = 0;
902  std::vector<int> local_maxima;
903  if (tell & TELL_PSI_SHIFT)
904  std::cout << "Local maxima\n";
905  for (int i = 0; i < N_trials; i++)
906  {
907  // Look for the left and right sample
908  int il = i - 1, ir = i + 1;
909  if (i == 0 && circular)
910  il = N_trials - 1;
911  else if (i == N_trials - 1)
912  {
913  if (circular)
914  ir = 0;
915  else
916  ir = -1;
917  }
918 
919  // Check if the error is a local minimum of the projection error
920  // or a local maxima of the correlation
921  bool is_local_maxima = true;
922  if (il != -1)
923  if (vscore[il] >= vscore[i])
924  is_local_maxima = false;
925  if (ir != -1)
926  if (vscore[ir] >= vscore[i])
927  is_local_maxima = false;
928 
929  // It is a local minimum
930  if (is_local_maxima /*|| visible_space*/)
931  avg_score_maxima += vscore[i];
932  if (is_local_maxima /*|| visible_space*/)
933  {
934  local_maxima.push_back(i);
935  if (tell & TELL_PSI_SHIFT)
936  std::cout << "i= " << i
937  << " psi= " << vpsi[i] << " rot= " << vrot[i] << " tilt= "
938  << vtilt[i] << " score= " << vscore[i] << std::endl;
939  }
940  }
941  avg_score_maxima /= local_maxima.size();
942  if (tell & TELL_PSI_SHIFT)
943  std::cout << "Avg_maxima=" << avg_score_maxima << std::endl;
944 
945  // Remove all local maxima below the average
946  int jmax = local_maxima.size();
947  std::vector<int> candidate_local_maxima;
948  std::vector<double> candidate_rate;
949  if (tell & TELL_PSI_SHIFT)
950  std::cout << "Keeping ...\n";
951  for (int j = 0; j < jmax; j++)
952  {
953  int i = local_maxima[j];
954  if (vscore[i] >= avg_score_maxima)
955  {
956  candidate_local_maxima.push_back(i);
957  candidate_rate.push_back(0);
958  if (tell & TELL_PSI_SHIFT)
959  std::cout << "i= " << i
960  << " psi= " << vpsi[i] << " rot= " << vrot[i] << " tilt= "
961  << vtilt[i] << " score= " << vscore[i] << std::endl;
962  }
963  }
964  jmax = candidate_local_maxima.size();
965 
966  // Evaluate the local maxima according to their correlations
967  evaluate_candidates(vscore, candidate_local_maxima, candidate_rate, 1);
968 
969  // Sort the candidates
970  if (tell & TELL_PSI_SHIFT)
971  std::cout << "\nSelecting image\n";
972  MultidimArray<double> score(jmax);
973  for (int j = 0; j < jmax; j++)
974  score(j) = vscore[candidate_local_maxima[j]];
975  MultidimArray<int> idx_score;
976  score.indexSort(idx_score);
977 
978  if (tell & (TELL_PSI_SHIFT | TELL_OPTIONS))
979  {
980  std::cout << img.name() << std::endl;
981  std::cout.flush();
982  for (int j = 0; j < jmax; j++)
983  {
984  int jp = idx_score(j) - 1;
985  int i = candidate_local_maxima[jp];
986  std::cout << "i= " << i
987  << " psi= " << vpsi[i] << " rot= " << vrot[i] << " tilt= "
988  << vtilt[i]
989  << " score= " << vscore[i]
990  << " corr= " << vcorr[i]
991  << " error= " << vproj_error[i]
992  << " compact= " << vproj_compact[i]
993  << " angjump= " << vang_jump[i]
994  << " rate=" << candidate_rate[jp]
995  << " reference image #=" << vref_idx[i] + 1 << std::endl;
996  }
997  std::cout << std::endl;
998  std::cout.flush();
999  }
1000 
1001  // Consider the top
1002  int jtop = jmax - 1;
1003  std::vector<int> best_idx;
1004  int max_score_diff = 1;
1005  while (jtop > 0 &&
1006  candidate_rate[idx_score(jmax-1)-1] -
1007  candidate_rate[idx_score(jtop-1)-1] <= max_score_diff)
1008  {
1009  best_idx.push_back(idx_score(jtop) - 1);
1010  jtop--;
1011  }
1012  best_idx.push_back(idx_score(jtop) - 1);
1013  if (tell & TELL_PSI_SHIFT)
1014  std::cout << "Best indices: " << best_idx << std::endl;
1015 
1016  // Pick the best one from the top
1017  int ibest, jbest;
1018  if (jtop == jmax - 1)
1019  {
1020  // There is only one on the top
1021  jbest = best_idx[0];
1022  ibest = candidate_local_maxima[jbest];
1023  }
1024  else
1025  {
1026  // There are more than one in the top
1027  // Group the different views
1028  std::vector< std::vector<int> > groups;
1029  group_views(vrot, vtilt, vpsi, best_idx, candidate_local_maxima, groups);
1030  if (tell & TELL_PSI_SHIFT)
1031  std::cout << "Partition: " << groups << std::endl;
1032 
1033  // Pick the best image from the groups
1034  jbest = pick_view(pick, groups, vscore,
1035  candidate_local_maxima, candidate_rate);
1036  ibest = candidate_local_maxima[jbest];
1037  }
1038 
1039  // Is it a 3D+2D search?
1040  if (!search5D)
1041  {
1042  Image<double> Iref;
1043  //Iref.readApplyGeo(library_name[vref_idx[ibest]]);
1044  //TODO: Check if this is correct
1045  Iref.read(library_name[vref_idx[ibest]]);
1046  Iref().setXmippOrigin();
1047  selfRotate(xmipp_transformation::LINEAR,Iref(),-vpsi[ibest]);
1048  if (Xoff == 0 && Yoff == 0)
1049  Ip() = img();
1050  else
1051  {
1052  VECTOR_R2(shift, Xoff, Yoff);
1053  translate(xmipp_transformation::LINEAR,Ip(),img(),shift,xmipp_transformation::WRAP);
1054  }
1055  Ip().setXmippOrigin();
1056 
1057  double shiftX, shiftY;
1058  CorrelationAux aux;
1059  bestShift(Iref(), Ip(), shiftX, shiftY, aux);
1060  if (shiftX*shiftX + shiftY*shiftY > R2)
1061  {
1062  shiftX = shiftY = 0;
1063  }
1064  vshiftX[ibest] = Xoff + shiftX;
1065  vshiftY[ibest] = Yoff + shiftY;
1066  max_shift_change = backup_max_shift_change;
1067  }
1068 
1069  // Take the information of the best image
1070  best_rot = vrot[ibest];
1071  best_tilt = vtilt[ibest];
1072  best_psi = vpsi[ibest];
1073  best_shiftX = vshiftX[ibest];
1074  best_shiftY = vshiftY[ibest];
1075  best_score = vscore[ibest];
1076  best_rate = candidate_rate[jbest];
1077 
1078  if (tell & (TELL_PSI_SHIFT | TELL_OPTIONS))
1079  {
1080  std::cout << "Originally it had, psi=" << img.psi() << " rot=" << img.rot()
1081  << " tilt=" << img.tilt() << std::endl;
1082  std::cout << "Finally I choose: ";
1083  if (tell & TELL_PSI_SHIFT)
1084  std::cout << jbest << "\n";
1085  std::cout << "psi= " << best_psi << " rot= " << best_rot << " tilt= "
1086  << best_tilt << " shiftX=" << best_shiftX
1087  << " shiftY=" << best_shiftY << " score= " << best_score
1088  << " rate= " << best_rate << std::endl << std::endl;
1089  }
1090 
1091  // Save results
1092  rowOut.setValue(MDL_ANGLE_ROT, best_rot);
1093  rowOut.setValue(MDL_ANGLE_TILT, best_tilt);
1094  rowOut.setValue(MDL_ANGLE_PSI, -best_psi);
1095  rowOut.setValue(MDL_SHIFT_X, best_shiftX);
1096  rowOut.setValue(MDL_SHIFT_Y, best_shiftY);
1097  rowOut.setValue(MDL_MAXCC, best_score);
1098 }
1099 #undef DEBUG
1100 
1101 // Finish processing ---------------------------------------------------------
1102 //void ProgAngularDiscreteAssign::postProcess()
1103 //{
1104 // mdOut.write(fn_out);
1105 //}
Just to locate unclassified errors.
Definition: xmipp_error.h:192
#define VECTOR_R2(v, x, y)
Definition: matrix1d.h:112
void init_progress_bar(long total)
void processImage(const FileName &fnImg, const FileName &fnImgOut, const MDRow &rowIn, MDRow &rowOut)
Rotation angle of an image (double,degrees)
#define YSIZE(v)
#define VEC_ELEM(v, i)
Definition: matrix1d.h:245
void set_DWT_type(int DWT_type)
Definition: wavelet.cpp:154
void resize(size_t Ndim, size_t Zdim, size_t Ydim, size_t Xdim, bool copy=true)
#define XMIPP_MAX(x, y)
Definition: xmipp_macros.h:193
int Get_Max_Scale(int size)
Definition: wavelet.h:71
double getDoubleParam(const char *param, int arg=0)
int smin
Definition: mask.h:474
__host__ __device__ float2 floor(const float2 v)
void read(const FileName &inFile, const std::vector< MDLabel > *desiredLabels=nullptr, bool decomposeStack=true) override
void group_views(const std::vector< double > &vrot, const std::vector< double > &vtilt, const std::vector< double > &vpsi, const std::vector< int > &best_idx, const std::vector< int > &candidate_idx, std::vector< std::vector< int > > &groups)
#define REPORT_ERROR(nerr, ErrormMsg)
Definition: xmipp_error.h:211
double psi(const size_t n=0) const
#define FOR_ALL_DIRECT_ELEMENTS_IN_ARRAY2D(m)
doublereal * c
doublereal * g
int pick_view(int method, std::vector< std::vector< int > > &groups, std::vector< double > &vscore, const std::vector< int > &candidate_idx, const std::vector< double > &candidate_rates)
Definition: mask.h:360
int smax
Definition: mask.h:478
void sqrt(Image< double > &op)
Tilting angle of an image (double,degrees)
Shift for the image in the X axis (double)
#define DIRECT_A2D_ELEM(v, i, j)
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 Euler_mirrorY(double rot, double tilt, double psi, double &newrot, double &newtilt, double &newpsi)
Definition: geometry.cpp:1011
void getImageSize(const MetaData &md, size_t &Xdim, size_t &Ydim, size_t &Zdim, size_t &Ndim, MDLabel image_label)
void resize(size_t Xdim)
Definition: mask.cpp:654
Special label to be used when gathering MDs in MpiMetadataPrograms.
void initConstant(T val)
const FileName & name() const
void write(const FileName &outFile, WriteModeMetaData mode=MD_OVERWRITE) const
bool single_image
Input is a single image.
double rot(const size_t n=0) const
Incorrect MultidimArray size.
Definition: xmipp_error.h:174
#define TELL_ROT_TILT
virtual IdIteratorProxy< false > ids()
void selfRotate(int SplineDegree, MultidimArray< T > &V1, double ang, char axis='Z', bool wrap=xmipp_transformation::DONT_WRAP, T outside=0)
void setPsi(double psi, const size_t n=0)
#define IMGMATRIX(I)
int readApplyGeo(const FileName &name, const MDRow &row, const ApplyGeoParams &params=DefaultApplyGeoParams)
size_t size() const override
#define i
#define FOR_ALL_ELEMENTS_IN_ARRAY2D(m)
void refine_candidate_list_with_correlation(int m, Matrix1D< double > &dwt, bool *candidate_list, std::vector< double > &cumulative_corr, Matrix1D< double > &x_power, std::vector< double > &sumxy, double th=50)
double tilt(const size_t n=0) const
void build_ref_candidate_list(const Image< double > &I, bool *candidate_list, std::vector< double > &cumulative_corr, std::vector< double > &sumxy)
double rnd_unif()
void clear() override
#define TELL_OPTIONS
glob_log first
void readParams()
Read argument from command line.
double computeDistance(double rot1, double tilt1, double psi1, double &rot2, double &tilt2, double &psi2, bool projdir_mode, bool check_mirrors, bool object_rotation=false, bool write_mirrors=true)
#define FLOOR(x)
Definition: xmipp_macros.h:240
const char * getParam(const char *param, int arg=0)
#define DAUB12
Definition: wavelet.h:39
void selfTranslate(int SplineDegree, MultidimArray< T > &V1, const Matrix1D< double > &v, bool wrap=xmipp_transformation::WRAP, T outside=0)
#define CEIL(x)
Definition: xmipp_macros.h:225
void DWT(const MultidimArray< T > &v, MultidimArray< double > &result, int isign=1)
Definition: wavelet.h:83
std::vector< MultidimArray< double > > library
double R1
Definition: mask.h:413
void resize(size_t Xdim, bool copy=true)
Definition: matrix1d.h:410
#define XSIZE(v)
double Yoff(const size_t n=0) const
double evaluate_candidates(const std::vector< double > &vscore, const std::vector< int > &candidate_idx, std::vector< double > &candidate_rate, double weight)
void progress_bar(long rlen)
bool produces_an_output
Indicate that a unique final output is produced.
int type
Definition: mask.h:402
void addExampleLine(const char *example, bool verbatim=true)
std::string quadrant
Definition: mask.h:483
Maximum cross-correlation for the image (double)
int verbose
Verbosity level.
void initZeros()
Definition: matrix1d.h:592
void sort(struct DCEL_T *dcel)
Definition: sorting.cpp:18
#define BINARY_DWT_CIRCULAR_MASK
Definition: mask.h:377
ProgAngularDiscreteAssign()
Empty constructor.
#define NEXT_POWER_OF_2(x)
Definition: xmipp_macros.h:374
#define j
int m
bool getValue(MDObject &mdValueOut, size_t id) const override
std::vector< FileName > library_name
void setValue(MDLabel label, const T &d, bool addLabel=true)
void show() const override
void generate_mask(bool apply_geo=false)
Definition: mask.cpp:1577
void translate(PDB *pdb_original, PDB *pdb_move, unsigned num_atoms, double x0, double y0, double z0)
Definition: lib_pwk.cpp:125
virtual bool containsLabel(MDLabel label) const =0
double predict_rot_tilt_angles(Image< double > &I, double &assigned_rot, double &assigned_tilt, int &best_ref_idx)
double psi(const double x)
double Xoff(const size_t n=0) const
#define INT_MASK
Definition: mask.h:385
std::string Quadrant2D(int q)
Definition: wavelet.cpp:187
#define realWRAP(x, x0, xF)
Definition: xmipp_macros.h:304
template float bestShift(MultidimArray< float > &, float &, float &, const MultidimArray< int > *, int)
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)
Shift for the image in the Y axis (double)
void addUsageLine(const char *line, bool verbatim=false)
void initZeros(const MultidimArray< T1 > &op)
const MultidimArray< int > & get_binary_mask() const
Definition: mask.h:707
int getIntParam(const char *param, int arg=0)
int * n
bool produces_a_metadata
Indicate that the unique final output file is a Metadata.
void indexSort(MultidimArray< int > &indx) const
void addParamsLine(const String &line)
int ir
#define TELL_PSI_SHIFT
MultidimArray< double > library_power
std::map< String, CommentList > defaultComments
Definition: xmipp_program.h:83
void setGeo(const MDRow &row, size_t n=0)