EMAN2
cmp.cpp
Go to the documentation of this file.
1/*
2 * Author: Steven Ludtke, 04/10/2003 (sludtke@bcm.edu)
3 * Copyright (c) 2000-2006 Baylor College of Medicine
4 *
5 * This software is issued under a joint BSD/GNU license. You may use the
6 * source code in this file under either license. However, note that the
7 * complete EMAN2 and SPARX software packages have some GPL dependencies,
8 * so you are responsible for compliance with the licenses of these packages
9 * if you opt to use BSD licensing. The warranty disclaimer below holds
10 * in either instance.
11 *
12 * This complete copyright notice must be included in any revised version of the
13 * source code. Additional authorship citations may be added, but existing
14 * author citations must be preserved.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 *
30 * */
31
32#include "cmp.h"
33#include "emdata.h"
34#include "ctf.h"
36#undef max
37#include <climits>
38
39#ifdef EMAN2_USING_CUDA
40// Only CCC, DOT and CCC.TOMO are cuda enabled
41#include "cuda/cuda_processor.h"
42#include "cuda/cuda_cmp.h"
43#endif // EMAN2_USING_CUDA
44
45using namespace EMAN;
46
47const string CccCmp::NAME = "ccc";
48const string LodCmp::NAME = "lod";
49const string SqEuclideanCmp::NAME = "sqeuclidean";
50const string DotCmp::NAME = "dot";
51const string TomoCccCmp::NAME = "ccc.tomo";
52const string TomoWedgeCccCmp::NAME = "ccc.tomo.thresh";
53const string TomoWedgeFscCmp::NAME = "fsc.tomo.auto";
54const string TomoFscCmp::NAME = "fsc.tomo";
55const string QuadMinDotCmp::NAME = "quadmindot";
56const string OptVarianceCmp::NAME = "optvariance";
57const string OptSubCmp::NAME = "optsub";
58const string PhaseCmp::NAME = "phase";
59const string FRCCmp::NAME = "frc";
60const string VerticalCmp::NAME = "vertical";
61
62template <> Factory < Cmp >::Factory()
63{
64 force_add<CccCmp>();
65 force_add<LodCmp>();
66 force_add<SqEuclideanCmp>();
67 force_add<DotCmp>();
68 force_add<TomoCccCmp>();
69 force_add<TomoWedgeCccCmp>();
70 force_add<TomoWedgeFscCmp>();
71 force_add<TomoFscCmp>();
72 force_add<QuadMinDotCmp>();
73 force_add<OptVarianceCmp>();
74 force_add<OptSubCmp>();
75 force_add<PhaseCmp>();
76 force_add<FRCCmp>();
77 force_add<VerticalCmp>();
78// force_add<XYZCmp>();
79}
80
81void Cmp::validate_input_args(const EMData * image, const EMData *with) const
82{
83
84#ifdef EMAN2_USING_CUDA
85 if (image->getcudarwdata() && with->getcudarwdata()) {
86 //no need for futher checking, which will induce an expensive copy from device to host
87 return;
88 }
89#endif
90 if (!image) {
91 throw NullPointerException("compared image");
92 }
93 if (!with) {
94 throw NullPointerException("compare-with image");
95 }
96
97 if (!EMUtil::is_same_size(image, with)) {
98 throw ImageFormatException( "images not same size");
99 }
100
101 float *d1 = image->get_data();
102 if (!d1) {
103 throw NullPointerException("image contains no data");
104 }
105
106 float *d2 = with->get_data();
107 if (!d2) {
108 throw NullPointerException("compare-with image data");
109 }
110}
111
112// It would be good to add code for complex images! PAP
113float CccCmp::cmp(EMData * image, EMData *with) const
114{
115 ENTERFUNC;
116 if (image==0 || with==0) throw ImageFormatException("NULL image provided to CCC cmp");
117 if (image->is_complex() || with->is_complex())
118 throw ImageFormatException( "Complex images not supported by CMP::CccCmp");
119 validate_input_args(image, with);
120
121 const float *const d1 = image->get_const_data();
122 const float *const d2 = with->get_const_data();
123
124 float negative = (float)params.set_default("negative", 1);
125 if (negative) negative=-1.0; else negative=1.0;
126
127 double avg1 = 0.0, var1 = 0.0, avg2 = 0.0, var2 = 0.0, ccc = 0.0;
128 long n = 0;
129 size_t totsize = image->get_xsize()*image->get_ysize()*image->get_zsize();
130
131 bool has_mask = false;
132 EMData* mask = 0;
133 if (params.has_key("mask")) {
134 mask = params["mask"];
135 if(mask!=0) {has_mask=true;}
136 }
137#ifdef EMAN2_USING_CUDA
138 if (image->getcudarwdata() && with->getcudarwdata()) {
139 //cout << "CUDA ccc cmp" << endl;
140 float* maskdata = 0;
141 if(has_mask && !mask->getcudarwdata()){
142 mask->copy_to_cuda();
143 maskdata = mask->getcudarwdata();
144 }
145 float ccc = ccc_cmp_cuda(image->getcudarwdata(), with->getcudarwdata(), maskdata, image->get_xsize(), image->get_ysize(), image->get_zsize());
146 ccc *= negative;
147 //cout << "CUDA CCC is: " << ccc << endl;
148 return ccc;
149 }
150#endif
151 if (has_mask) {
152 const float *const dm = mask->get_const_data();
153 for (size_t i = 0; i < totsize; ++i) {
154 if (dm[i] > 0.5) {
155 avg1 += double(d1[i]);
156 var1 += d1[i]*double(d1[i]);
157 avg2 += double(d2[i]);
158 var2 += d2[i]*double(d2[i]);
159 ccc += d1[i]*double(d2[i]);
160 n++;
161 }
162 }
163 } else {
164 for (size_t i = 0; i < totsize; ++i) {
165 avg1 += double(d1[i]);
166 var1 += d1[i]*double(d1[i]);
167 avg2 += double(d2[i]);
168 var2 += d2[i]*double(d2[i]);
169 ccc += d1[i]*double(d2[i]);
170 }
171 n = totsize;
172 }
173
174 avg1 /= double(n);
175 var1 = var1/double(n) - avg1*avg1;
176 avg2 /= double(n);
177 var2 = var2/double(n) - avg2*avg2;
178 ccc = ccc/double(n) - avg1*avg2;
179 ccc /= sqrt(var1*var2);
180 if (!Util::goodf(&ccc)) ccc=-2.0; // Steve - if one image was 0, this returned nan, which messes certain things up. -2.0 is out of range, and should serve as a proxy
181 ccc *= negative;
182 return static_cast<float>(ccc);
183 EXITFUNC;
184}
185
186
187// Added by JAK 11/12/10
188// L^1-norm difference of two maps, after normalization.
189float LodCmp::cmp(EMData * image, EMData *with) const
190{
191 ENTERFUNC;
192 if (image->is_complex() || with->is_complex())
193 throw ImageFormatException( "Complex images not yet supported by CMP::LodCmp");
194 validate_input_args(image, with);
195
196 const float *const d1 = image->get_const_data();
197 const float *const d2 = with->get_const_data();
198
199 float negative = (float)params.set_default("negative", 1);
200 if (negative) negative=-1.0; else negative=1.0;
201
202 double avg1 = 0.0, sig1 = 0.0, avg2 = 0.0, sig2 = 0.0, lod = 0.0;
203 long n = 0;
204 size_t totsize = image->get_xsize()*image->get_ysize()*image->get_zsize();
205 size_t i;
206
207 bool has_mask = false;
208 EMData* mask = 0;
209 if (params.has_key("mask")) {
210 mask = params["mask"];
211 if(mask!=0) {has_mask=true;}
212 }
213
214 int normalize = 0;
215 if (params.has_key("normalize")) {
216 normalize = params["normalize"];
217 }
218
219 if (normalize) {
220 if (has_mask) {
221 const float *const dm = mask->get_const_data();
222 for (i = 0; i < totsize; ++i) {
223 if (dm[i] > 0.5) {
224 avg1 += double(d1[i]);
225 avg2 += double(d2[i]);
226 n++;
227 }
228 }
229 } else {
230 for (i = 0; i < totsize; ++i) {
231 avg1 += double(d1[i]);
232 avg2 += double(d2[i]);
233 }
234 n = totsize;
235 }
236
237 avg1 /= double(n);
238 avg2 /= double(n);
239
240 if (has_mask) {
241 const float *const dm = mask->get_const_data();
242 for (i = 0; i < totsize; ++i) {
243 if (dm[i] > 0.5) {
244 sig1 += fabs(double(d1[i])-avg1);
245 sig2 += fabs(double(d2[i])-avg2);
246 }
247 }
248 } else {
249 for (i = 0; i < totsize; ++i) {
250 sig1 += fabs(double(d1[i])-avg1);
251 sig2 += fabs(double(d2[i])-avg2);
252 }
253 }
254 } else {
255 avg1 = 0.0; avg2 = 0.0;
256 sig1 = 1.0; sig2 = 1.0;
257 }
258
259 if (has_mask) {
260 const float *const dm = mask->get_const_data();
261 for (i = 0; i < totsize; ++i) {
262 if (dm[i] > 0.5) {
263 lod += fabs((double(d1[i])-avg1)/sig1 - (double(d2[i])-avg2)/sig2);
264 }
265 }
266 } else {
267 for (i = 0; i < totsize; ++i) {
268 lod += fabs((double(d1[i])-avg1)/sig1 - (double(d2[i])-avg2)/sig2);
269 }
270 }
271
272 lod *= (-0.5);
273 lod *= negative;
274 return static_cast<float>(lod);
275 EXITFUNC;
276}
277
278
279//float SqEuclideanCmp::cmp(EMData * image, EMData *withorig) const
280float SqEuclideanCmp::cmp(EMData *image,EMData * withorig ) const
281{
282 ENTERFUNC;
283 EMData *with = withorig;
284 validate_input_args(image, with);
285
286 int zeromask = params.set_default("zeromask",0);
287 int normto = params.set_default("normto",0);
288
289 if (normto) {
290 if (zeromask) with = withorig->process("normalize.toimage",Dict("to",image));
291 else with = withorig->process("normalize.toimage",Dict("to",image,"ignore_zero",0));
292 with->set_attr("deleteme",1);
293 if ((float)(with->get_attr("norm_mult"))<=0) { // This means the normalization inverted the image, a clear probablity of noise bias, so we undo the normalization
294 delete with;
295 with=withorig;
296 }
297 }
298
299 const float *const y_data = with->get_const_data();
300 const float *const x_data = image->get_const_data();
301 double result = 0.;
302 float n = 0.0f;
303 if(image->is_complex() && with->is_complex()) {
304 // Implemented by PAP 01/09/06 - please do not change. If in doubts, write/call me.
305 int nx = with->get_xsize();
306 int ny = with->get_ysize();
307 int nz = with->get_zsize();
308 nx = (nx - 2 + with->is_fftodd()); // nx is the real-space size of the input image
309 int lsd2 = (nx + 2 - nx%2) ; // Extended x-dimension of the complex image
310
311 int ixb = 2*((nx+1)%2);
312 int iyb = ny%2;
313 //
314 if(nz == 1) {
315 // it looks like it could work in 3D, but it does not, really.
316 for ( int iz = 0; iz <= nz-1; iz++) {
317 double part = 0.;
318 for ( int iy = 0; iy <= ny-1; iy++) {
319 for ( int ix = 2; ix <= lsd2 - 1 - ixb; ix++) {
320 size_t ii = ix + (iy + iz * ny)* lsd2;
321 part += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
322 }
323 }
324 for ( int iy = 1; iy <= ny/2-1 + iyb; iy++) {
325 size_t ii = (iy + iz * ny)* lsd2;
326 part += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
327 part += (x_data[ii+1] - y_data[ii+1])*double(x_data[ii+1] - y_data[ii+1]);
328 }
329 if(nx%2 == 0) {
330 for ( int iy = 1; iy <= ny/2-1 + iyb; iy++) {
331 size_t ii = lsd2 - 2 + (iy + iz * ny)* lsd2;
332 part += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
333 part += (x_data[ii+1] - y_data[ii+1])*double(x_data[ii+1] - y_data[ii+1]);
334 }
335
336 }
337 part *= 2;
338 part += (x_data[0] - y_data[0])*double(x_data[0] - y_data[0]);
339 if(ny%2 == 0) {
340 int ii = (ny/2 + iz * ny)* lsd2;
341 part += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
342 }
343 if(nx%2 == 0) {
344 int ii = lsd2 - 2 + (0 + iz * ny)* lsd2;
345 part += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
346 if(ny%2 == 0) {
347 int ii = lsd2 - 2 +(ny/2 + iz * ny)* lsd2;
348 part += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
349 }
350 }
351 result += part;
352 }
353 n = (float)nx*(float)ny*(float)nz*(float)nx*(float)ny*(float)nz;
354
355 } else { //This 3D code is incorrect, but it is the best I can do now 01/09/06 PAP
356 int ky, kz;
357 int ny2 = ny/2; int nz2 = nz/2;
358 for ( int iz = 0; iz <= nz-1; iz++) {
359 if(iz>nz2) kz=iz-nz; else kz=iz;
360 for ( int iy = 0; iy <= ny-1; iy++) {
361 if(iy>ny2) ky=iy-ny; else ky=iy;
362 for ( int ix = 0; ix <= lsd2-1; ix++) {
363 // Skip Friedel related values
364 if(ix>0 || (kz>=0 && (ky>=0 || kz!=0))) {
365 size_t ii = ix + (iy + iz * ny)* (size_t)lsd2;
366 result += (x_data[ii] - y_data[ii])*double(x_data[ii] - y_data[ii]);
367 }
368 }
369 }
370 }
371 n = ((float)nx*(float)ny*(float)nz*(float)nx*(float)ny*(float)nz)/2.0f;
372 }
373 } else { // real space
374 size_t totsize = (size_t)image->get_xsize()*image->get_ysize()*image->get_zsize();
375 if (params.has_key("mask")) {
376 EMData* mask;
377 mask = params["mask"];
378 const float *const dm = mask->get_const_data();
379 for (size_t i = 0; i < totsize; i++) {
380 if (dm[i] > 0.5) {
381 double temp = x_data[i]- y_data[i];
382 result += temp*temp;
383 n++;
384 }
385 }
386 }
387 else if (zeromask) {
388 n=0;
389 for (size_t i = 0; i < totsize; i++) {
390 if (x_data[i]==0 || y_data[i]==0) continue;
391 double temp = x_data[i]- y_data[i];
392 result += temp*temp;
393 n++;
394 }
395
396 }
397 else {
398 for (size_t i = 0; i < totsize; i++) {
399 double temp = x_data[i]- y_data[i];
400 result += temp*temp;
401 }
402 n = (float)totsize;
403 }
404 }
405 result/=n;
406
407 EXITFUNC;
408 if (with->has_attr("deleteme")) delete with;
409 float ret = (float)result;
410 if (!Util::goodf(&ret)) return FLT_MAX;
411 return ret;
412}
413
414
415// Even though this uses doubles, it might be wise to recode it row-wise
416// to avoid numerical errors on large images
417float DotCmp::cmp(EMData* image, EMData* with) const
418{
419 ENTERFUNC;
420
421 validate_input_args(image, with);
422
423 int normalize = params.set_default("normalize", 0);
424 float negative = (float)params.set_default("negative", 1);
425 if (negative) negative=-1.0; else negative=1.0;
426#ifdef EMAN2_USING_CUDA // SO far only works for real images I put CUDA first to avoid running non CUDA overhead (calls to getdata are expensive!!!!)
427 if(image->is_complex() && with->is_complex()) {
428 } else {
429 if (image->getcudarwdata() && with->getcudarwdata()) {
430 //cout << "CUDA dot cmp" << endl;
431 float* maskdata = 0;
432 bool has_mask = false;
433 EMData* mask = 0;
434 if (params.has_key("mask")) {
435 mask = params["mask"];
436 if(mask!=0) {has_mask=true;}
437 }
438 if(has_mask && !mask->getcudarwdata()){
439 mask->copy_to_cuda();
440 maskdata = mask->getcudarwdata();
441 }
442
443 float result = dot_cmp_cuda(image->getcudarwdata(), with->getcudarwdata(), maskdata, image->get_xsize(), image->get_ysize(), image->get_zsize());
444 result *= negative;
445
446 return result;
447
448 }
449 }
450#endif
451 const float *const x_data = image->get_const_data();
452 const float *const y_data = with->get_const_data();
453
454 double result = 0.;
455 long n = 0;
456 if(image->is_complex() && with->is_complex()) {
457 // Implemented by PAP 01/09/06 - please do not change. If in doubts, write/call me.
458 int nx = with->get_xsize();
459 int ny = with->get_ysize();
460 int nz = with->get_zsize();
461 nx = (nx - 2 + with->is_fftodd()); // nx is the real-space size of the input image
462 int lsd2 = (nx + 2 - nx%2) ; // Extended x-dimension of the complex image
463
464 int ixb = 2*((nx+1)%2);
465 int iyb = ny%2;
466 //
467 if(nz == 1) {
468 // it looks like it could work in 3D, but does not
469 for ( int iz = 0; iz <= nz-1; ++iz) {
470 double part = 0.;
471 for ( int iy = 0; iy <= ny-1; ++iy) {
472 for ( int ix = 2; ix <= lsd2 - 1 - ixb; ++ix) {
473 size_t ii = ix + (iy + iz * ny)* lsd2;
474 part += x_data[ii] * double(y_data[ii]);
475 }
476 }
477 for ( int iy = 1; iy <= ny/2-1 + iyb; ++iy) {
478 size_t ii = (iy + iz * ny)* lsd2;
479 part += x_data[ii] * double(y_data[ii]);
480 part += x_data[ii+1] * double(y_data[ii+1]);
481 }
482 if(nx%2 == 0) {
483 for ( int iy = 1; iy <= ny/2-1 + iyb; ++iy) {
484 size_t ii = lsd2 - 2 + (iy + iz * ny)* lsd2;
485 part += x_data[ii] * double(y_data[ii]);
486 part += x_data[ii+1] * double(y_data[ii+1]);
487 }
488
489 }
490 part *= 2;
491 part += x_data[0] * double(y_data[0]);
492 if(ny%2 == 0) {
493 size_t ii = (ny/2 + iz * ny)* lsd2;
494 part += x_data[ii] * double(y_data[ii]);
495 }
496 if(nx%2 == 0) {
497 size_t ii = lsd2 - 2 + (0 + iz * ny)* lsd2;
498 part += x_data[ii] * double(y_data[ii]);
499 if(ny%2 == 0) {
500 int ii = lsd2 - 2 +(ny/2 + iz * ny)* lsd2;
501 part += x_data[ii] * double(y_data[ii]);
502 }
503 }
504 result += part;
505 }
506 } else {
507 // The 3D code
508 int ky, kz;
509 int ny2 = ny/2; int nz2 = nz/2;
510 for ( int iz = 0; iz <= nz-1; ++iz) {
511 if(iz>nz2) kz=iz-nz; else kz=iz;
512 for ( int iy = 0; iy <= ny-1; ++iy) {
513 if(iy>ny2) ky=iy-ny; else ky=iy;
514 for (int ix = 0; ix <= lsd2-1; ++ix) {
515 int p = 2;
516 if (ix <= 1 || (ix >= lsd2-2 && nx%2 == 0) ) p = 1;
517 size_t ii = ix + (iy + iz * ny)* (size_t)lsd2;
518 result += p * x_data[ii] * double(y_data[ii]);
519 }
520 }
521 }
522 if( normalize ) {
523 double square_sum1 = 0., square_sum2 = 0.;
524 int ky, kz;
525 int ny2 = ny/2; int nz2 = nz/2;
526 for ( int iz = 0; iz <= nz-1; ++iz) {
527 if(iz>nz2) kz=iz-nz; else kz=iz;
528 for ( int iy = 0; iy <= ny-1; ++iy) {
529 if(iy>ny2) ky=iy-ny; else ky=iy;
530 for (int ix = 0; ix <= lsd2-1; ++ix) {
531 int p = 2;
532 if (ix <= 1 || (ix >= lsd2-2 && nx%2 == 0) ) p = 1;
533 size_t ii = ix + (iy + iz * ny)* (size_t)lsd2;
534 square_sum1 += p * x_data[ii] * double(x_data[ii]);
535 square_sum2 += p * y_data[ii] * double(y_data[ii]);
536 }
537 }
538 }
539 result /= sqrt(square_sum1*square_sum2);
540 } else result /= ((float)nx*(float)ny*(float)nz*(float)nx*(float)ny*(float)nz);
541 if( normalize ) {
542 // it looks like it could work in 3D, but does not
543 double square_sum1 = 0., square_sum2 = 0.;
544 for ( int iz = 0; iz <= nz-1; ++iz) {
545 for ( int iy = 0; iy <= ny-1; ++iy) {
546 for ( int ix = 2; ix <= lsd2 - 1 - ixb; ++ix) {
547 size_t ii = ix + (iy + iz * ny)* lsd2;
548 square_sum1 += x_data[ii] * double(x_data[ii]);
549 square_sum2 += y_data[ii] * double(y_data[ii]);
550 }
551 }
552 for ( int iy = 1; iy <= ny/2-1 + iyb; ++iy) {
553 size_t ii = (iy + iz * ny)* lsd2;
554 square_sum1 += x_data[ii] * double(x_data[ii]);
555 square_sum1 += x_data[ii+1] * double(x_data[ii+1]);
556 square_sum2 += y_data[ii] * double(y_data[ii]);
557 square_sum2 += y_data[ii+1] * double(y_data[ii+1]);
558 }
559 if(nx%2 == 0) {
560 for ( int iy = 1; iy <= ny/2-1 + iyb; ++iy) {
561 size_t ii = lsd2 - 2 + (iy + iz * ny)* lsd2;
562 square_sum1 += x_data[ii] * double(x_data[ii]);
563 square_sum1 += x_data[ii+1] * double(x_data[ii+1]);
564 square_sum2 += y_data[ii] * double(y_data[ii]);
565 square_sum2 += y_data[ii+1] * double(y_data[ii+1]);
566 }
567
568 }
569 square_sum1 *= 2;
570 square_sum1 += x_data[0] * double(x_data[0]);
571 square_sum2 *= 2;
572 square_sum2 += y_data[0] * double(y_data[0]);
573 if(ny%2 == 0) {
574 int ii = (ny/2 + iz * ny)* lsd2;
575 square_sum1 += x_data[ii] * double(x_data[ii]);
576 square_sum2 += y_data[ii] * double(y_data[ii]);
577 }
578 if(nx%2 == 0) {
579 int ii = lsd2 - 2 + (0 + iz * ny)* lsd2;
580 square_sum1 += x_data[ii] * double(x_data[ii]);
581 square_sum2 += y_data[ii] * double(y_data[ii]);
582 if(ny%2 == 0) {
583 int ii = lsd2 - 2 +(ny/2 + iz * ny)* lsd2;
584 square_sum1 += x_data[ii] * double(x_data[ii]);
585 square_sum2 += y_data[ii] * double(y_data[ii]);
586 }
587 }
588 }
589 result /= sqrt(square_sum1*square_sum2);
590 } else result /= ((float)nx*(float)ny*(float)nz*(float)nx*(float)ny*(float)nz);
591 // This concludes the 2D part.
592 }
593 } else {
594 // This part is for when two images are real, which is much easier because 2-D or 3-D
595 // is the same code.
596 size_t totsize = (size_t)image->get_xsize() * image->get_ysize() * image->get_zsize();
597
598 double square_sum1 = 0., square_sum2 = 0.;
599
600 if (params.has_key("mask")) {
601 EMData* mask;
602 mask = params["mask"];
603 const float *const dm = mask->get_const_data();
604 if (normalize) {
605 for (size_t i = 0; i < totsize; i++) {
606 if (dm[i] > 0.5) {
607 square_sum1 += x_data[i]*double(x_data[i]);
608 square_sum2 += y_data[i]*double(y_data[i]);
609 result += x_data[i]*double(y_data[i]);
610 }
611 }
612 } else {
613 for (size_t i = 0; i < totsize; i++) {
614 if (dm[i] > 0.5) {
615 result += x_data[i]*double(y_data[i]);
616 n++;
617 }
618 }
619 }
620 } else {
621 // this little bit of manual loop unrolling makes the dot product as fast as sqeuclidean with -O2
622 for (size_t i=0; i<totsize; i++) result += x_data[i]*y_data[i];
623
624 if (normalize) {
625 square_sum1 = image->get_attr("square_sum");
626 square_sum2 = with->get_attr("square_sum");
627 } else n = totsize;
628 }
629 if (normalize) result /= (sqrt(square_sum1*square_sum2)); else result /= n;
630 }
631
632
633 EXITFUNC;
634 return (float) (negative*result);
635}
636
637// This implements the technique of Mike Schmid where by the cross correlation is normalized
638// in an effort to remove the effects of the missing wedge. Somewhat of a heuristic solution, but it seems
639// to work. Basically it relies on the observation that 'good' matchs will conentrate the correlation
640// signal in the peak, wheras 'bad' correlations will distribute the signal.
641// John Flanagan 18-10-2010
642
643float TomoCccCmp::cmp(EMData * image, EMData *with) const
644{
645 ENTERFUNC;
646 EMData* ccf = params.set_default("ccf",(EMData*) NULL);
647 bool ccf_ownership = false;
648 bool norm = params.set_default("norm",true);
649 float negative = (float)params.set_default("negative", 1);
650 if (negative) negative=-1.0; else negative=1.0;
651
652#ifdef EMAN2_USING_CUDA
653 if(image->getcudarwdata() && with->getcudarwdata()){
654 if (!ccf) {
655 ccf = image->calc_ccf(with);
656 ccf_ownership = true;
657 }
658 //cout << "using CUDA" << endl;
659 float2 stats = get_stats_cuda(ccf->getcudarwdata(), ccf->get_xsize(), ccf->get_ysize(), ccf->get_zsize());
660 float best_score = get_value_at_wrap_cuda(ccf->getcudarwdata(), 0, 0, 0, ccf->get_xsize(), ccf->get_ysize(), ccf->get_zsize());
661 if(norm) {
662 best_score = negative*(best_score - stats.x)/sqrt(stats.y);
663 } else {
664 best_score = negative*best_score;
665 }
666
667 if (ccf_ownership) delete ccf; ccf = 0;
668
669 EXITFUNC;
670 return best_score;
671
672 }
673#endif
674
675 if (!ccf) {
676 ccf = image->calc_ccf(with);
677 ccf_ownership = true;
678 }
679 if (norm) ccf->process_inplace("normalize");
680
681 float best_score = ccf->get_value_at_wrap(0,0,0);
682 if (ccf_ownership) delete ccf; ccf = 0;
683
684 return negative*best_score;
685
686}
687
688// CCC in Fourier space with small valued pixels presumed to be in missing wedge, and excluded from similarity
689// Fractional overlap returned in "image"
690
691float TomoWedgeCccCmp::cmp(EMData * image, EMData *with) const
692{
693 ENTERFUNC;
694 EMData *imageo=image;
695 EMData *witho=with;
696 if (!image->is_complex()) image=image->do_fft();
697 if (!with ->is_complex()) with =with ->do_fft();
698 if (image->get_xsize()!=with->get_xsize() || image->get_ysize()!=with->get_ysize() || image->get_zsize()!=with->get_zsize()) throw InvalidCallException("Error: TomoWedgeCccCmp requires same sized images");
699
700 float sigmaimg = params.set_default("sigmaimg",0.5f);
701 float sigmawith = params.set_default("sigmawith",0.5f);
702 int negative = params.set_default("negative",1);
703
704 // Note 'sigma' is the STD of real and imag values treated independently
705 // s1 and s2 are threshold values squared (for speed)
706 float s1=pow((float)image->get_attr("sigma")*sigmaimg,(float)2.0);
707 float s2=pow((float)with->get_attr("sigma")*sigmawith,(float)2.0);
708
709
710 double sum=0;
711 double sumsq1=0;
712 double sumsq2=0;
713 double norm=0;
714 for (int z=0; z<image->get_zsize(); z++) {
715 for (int y=0; y<image->get_ysize(); y++) {
716 for (int x=0; x<image->get_xsize(); x+=2) {
717 float v1r=image->get_value_at(x,y,z);
718 float v1i=image->get_value_at(x+1,y,z);
719 float v1=Util::square_sum(v1r,v1i);
720 if (v1<s1) continue;
721
722 float v2r=with->get_value_at(x,y,z);
723 float v2i=with->get_value_at(x+1,y,z);
724 float v2=Util::square_sum(v2r,v2i);
725 if (v2<s2) continue;
726
727 sum+=v1r*v2r+v1i*v2i;
728 sumsq1+=v1;
729 if (Util::is_nan(sumsq1)) { printf("TomoWedgeCccCmp: NaN encountered: %d %d %d %f %f %f\n",x,y,z,v1r,v1i,v1); }
730 sumsq2+=v2;
731 norm+=1.0;
732 }
733 }
734 }
735 imageo->set_attr("fft_overlap",(float)(2.0*norm/(image->get_xsize()*image->get_ysize()*image->get_zsize())));
736// printf("%f\t%f\t%f\t%f\t%f\n",s1,s2,sumsq1,sumsq2,norm);
737
738 if (imageo!=image) delete image;
739 if (witho!=with) delete with;
740
741 if (negative) sum*=-1.0;
742 return float(sum/(sqrt(sumsq1)*sqrt(sumsq2)));
743}
744
745// CCC in Fourier space with small valued pixels presumed to be in missing wedge, and excluded from similarity
746// Fractional overlap returned in "image"
747
748float TomoWedgeFscCmp::cmp(EMData * image, EMData *with) const
749{
750 ENTERFUNC;
751 EMData *imageo=image;
752 EMData *witho=with;
753 if (!image->is_complex()) image=image->do_fft();
754 if (!with ->is_complex()) with =with ->do_fft();
755 if (image->get_xsize()!=with->get_xsize() || image->get_ysize()!=with->get_ysize() || image->get_zsize()!=with->get_zsize()) throw InvalidCallException("Error: TomoWedgeFscCmp requires 2 images the same size");
756
757 int nx=image->get_xsize();
758 int ny=image->get_ysize();
759 int nz=image->get_zsize();
760
761 float sigmaimgval = params.set_default("sigmaimgval",0.5f);
762 float sigmawithval = params.set_default("sigmawithval",0.5f);
763
764 bool retcurve = params.set_default("retcurve",false);
765 // User can pass in a sigma vector if they like, otherwise we call it 1/10 of the standard deviation in each shell
766 // This has proven to be a good cutoff for identifying the missing wedge voxels, without throwing away too many
767 // voxels if the image is complete in Fourier space (no wedge)
768 vector<float> sigmaimg;
769 if (params.has_key("sigmaimg")) sigmaimg=params["sigmaimg"];
770 else {
771 sigmaimg=image->calc_radial_dist(nx/2,0,1,4);
772 for (int i=0; i<nx/2; i++) sigmaimg[i]*=sigmaimg[i]*sigmaimgval; // The value here is amplitude, we square to make comparison less expensive
773 }
774
775 vector<float> sigmawith;
776 if (params.has_key("sigmawith")) sigmawith = params["sigmawith"];
777 else {
778 sigmawith=with->calc_radial_dist(nx/2,0,1,4);
779 for (int i=0; i<nx/2; i++) sigmawith[i]*=sigmawith[i]*sigmawithval; // The value here is amplitude, we square to make comparison less expensive
780 }
781
782 float apix = params.set_default("apix",image->get_attr_default("apix_x", 1.0f));
783 //get min and max res
784 float minres = params.set_default("minres",0.0f);
785 float maxres = params.set_default("maxres", 0.0f);
786
787 int pmin = params.set_default("pmin",3);
788 int pmax = params.set_default("pmax", ny/2);
789
790 if (minres>0)
791 pmin=(int)floor((apix*ny)/minres); //cutoff in pixels, assume square
792 if (maxres>0)
793 pmax=(int)ceil((apix*ny)/maxres);
794
795 int negative = params.set_default("negative",1);
796
797 double sum=0;
798 double sumsq1=0;
799 double sumsq2=0;
800 double norm=0;
801 vector<float> curve(nx/2), sqcv1(nx/2), sqcv2(nx/2);
802 for (int z=0; z<nz; z++) {
803 int za=z<nz/2?z:nz-z;
804 if (za>pmax) continue;
805 for (int y=0; y<ny; y++) {
806 int ya=y<ny/2?y:ny-y;
807 if (ya>pmax) continue;
808 for (int x=0; x<nx; x+=2) {
809 float r2=Util::hypot3sq(x/2,ya,za); // origin at 0,0; periodic
810 int r=int(sqrtf(r2));
811// float rf=Util::hypot3(x/2,y<ny/2?y:ny-y,z<nz/2?z:nz-z); // origin at 0,0; periodic
812// int r=int(rf);
813 if (r<pmin || r>pmax) continue;
814
815 float v1r=image->get_value_at(x,y,z);
816 float v1i=image->get_value_at(x+1,y,z);
817 float v1=Util::square_sum(v1r,v1i);
818 if (v1<sigmaimg[r]) continue;
819
820 float v2r=with->get_value_at(x,y,z);
821 float v2i=with->get_value_at(x+1,y,z);
822 float v2=Util::square_sum(v2r,v2i);
823 if (v2<sigmawith[r]) continue;
824
825 sum+=(v1r*v2r+v1i*v2i)/r2; // The r downweighting of points allows us to integrate over the entire image rather than computing a FSC and integrating that
826 sumsq1+=v1/r2;
827// if (Util::is_nan(sumsq1)) { printf("TomoWedgeCccCmp: NaN encountered: %d %d %d %f %f %f\n",x,y,z,v1r,v1i,v1); }
828 sumsq2+=v2/r2;
829 norm+=1.0;
830 if (retcurve){
831 curve[r]+=v1r*v2r+v1i*v2i;
832 sqcv1[r]+=v1;
833 sqcv2[r]+=v2;
834 }
835 }
836 }
837 }
838 image->set_attr("fft_overlap",(float)(2.0*norm/(image->get_xsize()*image->get_ysize()*image->get_zsize())));
839// printf("%f\t%f\t%f\t%f\t%f\n",s1,s2,sumsq1,sumsq2,norm);
840 if (retcurve){
841 for(int i=0; i<nx/2; i++){
842 if (sqcv1[i]==0) sqcv1[i]=1;
843 if (sqcv2[i]==0) sqcv2[i]=1;
844 curve[i]=curve[i]/(sqrt(sqcv1[i])*sqrt(sqcv2[i]));
845 }
846 image->set_attr("fsc_curve", curve);
847 }
848
849 if (imageo!=image) delete image;
850 if (witho!=with) delete with;
851
852 if (negative) sum*=-1.0;
853 return float(sum/(sqrt(sumsq1)*sqrt(sumsq2)));
854}
855
856float TomoFscCmp::cmp(EMData * image, EMData *with) const
857{
858 ENTERFUNC;
859 bool usecpu = 1;
860 bool del_imagefft = 0;
861 bool del_withfft = 0;
862 float score = 1.0f;
863
864 //get parameters
865 if (!image->has_attr("spt_wedge_mean") || !image->has_attr("spt_wedge_sigma")) throw InvalidCallException("Rubbish!!! Image Subtomogram does not have mena and/or sigma amps metadata");
866 // BAD DESIGN!!!! The fact that I have to load attrs into a variable before I can do operations on them is silly
867
868 //get threshold information
869 float image_meanwedgeamp = image->get_attr("spt_wedge_mean");
870 float image_sigmawedgeamp = image->get_attr("spt_wedge_sigma");
871
872 // Use with amp stats if avaliable otherwise effectivly ignore
873 float with_meanwedgeamp = image_meanwedgeamp;
874 float with_sigmawedgeamp = image_sigmawedgeamp;
875 if (with->has_attr("spt_wedge_mean") && with->has_attr("spt_wedge_sigma"))
876 {
877 with_meanwedgeamp = with->get_attr("spt_wedge_mean");
878 with_sigmawedgeamp = with->get_attr("spt_wedge_sigma");
879 }
880
881 float apix = params.set_default("apix",image->get_attr_default("apix_x", 1.0f));
882 //get min and max res
883 float minres = params.set_default("minres",std::numeric_limits<float>::max());
884 float maxres = params.set_default("maxres", 0.0f);
885
886 // Find threshold
887 float sigmas = params.set_default("sigmas",5.0f);
888 float img_amp_thres = pow(image_meanwedgeamp + sigmas*image_sigmawedgeamp, 2.0f);
889 float with_amp_thres = pow(with_meanwedgeamp + sigmas*with_sigmawedgeamp, 2.0f);
890
891 // take negative of score
892 float negative = (float)params.set_default("negative", 1.0f);
893 if (negative) negative=-1.0; else negative=1.0;
894 //get apix, use param apix, if not specified use apix_x, if this is not specified then apix=1.0
895
896 //Check to ensure that images are complex
897 EMData* image_fft = image;
898 EMData* with_fft = with;
899
900#ifdef EMAN2_USING_CUDA
901 //do CUDA FFT, does not allow minres, maxres yet
902 if(EMData::usecuda == 1 && image->getcudarwdata() && with->getcudarwdata()) {
903 if(!image->is_complex()){
904 del_imagefft = 1;
905 image_fft = image->do_fft_cuda();
906 }
907 if(!with->is_complex()){
908 del_withfft = 1;
909 with_fft = with->do_fft_cuda();
910 }
911 score = fsc_tomo_cmp_cuda(image_fft->getcudarwdata(), with_fft->getcudarwdata(), img_amp_thres, with_amp_thres, 0.0, 0.0, image_fft->get_xsize(), image_fft->get_ysize(), image_fft->get_zsize());
912 usecpu = 0;
913 }
914#endif
915 if(usecpu){
916 if(!image->is_complex()){
917 del_imagefft = 1;
918 image_fft = image->do_fft();
919 }
920 if(!with->is_complex()){
921 del_withfft = 1;
922 with_fft = with->do_fft();
923 }
924
925 //loop over all voxels
926 int count = 0;
927 double sum_imgamp_sq = 0.0;
928 double sum_withamp_sq = 0.0;
929 double cong = 0.0;
930 float* img_data = image_fft->get_data();
931 float* with_data = with_fft->get_data();
932
933 int nx = image_fft->get_xsize();
934 int ny = image_fft->get_ysize();
935 int nz = image_fft->get_zsize();
936 int ny2 = ny/2;
937 int nz2 = nz/2;
938
939 //compute FSC
940 int ii, kz, ky;
941 for (int iz = 0; iz < nz; iz++) {
942 if(iz > nz2) kz = nz-iz; else kz=iz;
943 for (int iy = 0; iy < ny; iy++) {
944 if(iy > ny2) ky = ny-iy; else ky=iy;
945 for (int ix = 0; ix < nx; ix+=2) {
946 //compute spatial frequency and convert to resolution stat
947 float freq = std::sqrt(kz*kz + ky*ky + ix*ix/4.0f)/float(nz);
948 float reso = apix/freq;
949
950 //only look within a resolution domain
951 if(reso < minres && reso > maxres){
952 ii = ix + (iy + iz * ny)* nx;
953 float img_r = img_data[ii];
954 float img_i = img_data[ii+1];
955 float with_r = with_data[ii];
956 float with_i = with_data[ii+1];
957 double img_amp_sq = img_r*img_r + img_i*img_i;
958 double with_amp_sq = with_r*with_r + with_i*with_i;
959
960 if((img_amp_sq > img_amp_thres) && (with_amp_sq > with_amp_thres)){
961 count ++;
962 sum_imgamp_sq += img_amp_sq;
963 sum_withamp_sq += with_amp_sq;
964 cong += img_r*with_r + img_i*with_i;
965 }
966 }
967 }
968 }
969 }
970
971 if(count > 0){
972 score = (float)(cong/sqrt(sum_imgamp_sq*sum_withamp_sq));
973 }else{
974 score = 1.0f;
975 }
976 }
977
978 //avoid mem leaks
979 if(del_imagefft) delete image_fft;
980 if(del_withfft) delete with_fft;
981
982 return negative*score;
983 EXITFUNC;
984}
985
986// Even though this uses doubles, it might be wise to recode it row-wise
987// to avoid numerical errors on large images
988float QuadMinDotCmp::cmp(EMData * image, EMData *with) const
989{
990 ENTERFUNC;
991 validate_input_args(image, with);
992
993 if (image->get_zsize()!=1) throw InvalidValueException(0, "QuadMinDotCmp supports 2D only");
994
995 int nx=image->get_xsize();
996 int ny=image->get_ysize();
997
998 int normalize = params.set_default("normalize", 0);
999 float negative = (float)params.set_default("negative", 1);
1000
1001 if (negative) negative=-1.0; else negative=1.0;
1002
1003 double result[4] = { 0,0,0,0 }, sq1[4] = { 0,0,0,0 }, sq2[4] = { 0,0,0,0 } ;
1004
1005 vector<int> image_saved_offsets = image->get_array_offsets();
1006 vector<int> with_saved_offsets = with->get_array_offsets();
1007 image->set_array_offsets(-nx/2,-ny/2);
1008 with->set_array_offsets(-nx/2,-ny/2);
1009 int i,x,y;
1010 for (y=-ny/2; y<ny/2; y++) {
1011 for (x=-nx/2; x<nx/2; x++) {
1012 int quad=(x<0?0:1) + (y<0?0:2);
1013 result[quad]+=(*image)(x,y)*(*with)(x,y);
1014 if (normalize) {
1015 sq1[quad]+=(*image)(x,y)*(*image)(x,y);
1016 sq2[quad]+=(*with)(x,y)*(*with)(x,y);
1017 }
1018 }
1019 }
1020 image->set_array_offsets(image_saved_offsets);
1021 with->set_array_offsets(with_saved_offsets);
1022
1023 if (normalize) {
1024 for (i=0; i<4; i++) result[i]/=sqrt(sq1[i]*sq2[i]);
1025 } else {
1026 for (i=0; i<4; i++) result[i]/=nx*ny/4;
1027 }
1028
1029 float worst=static_cast<float>(result[0]);
1030 for (i=1; i<4; i++) if (static_cast<float>(result[i])<worst) worst=static_cast<float>(result[i]);
1031
1032 EXITFUNC;
1033 return (float) (negative*worst);
1034}
1035
1036float OptVarianceCmp::cmp(EMData * image, EMData *with) const
1037{
1038 ENTERFUNC;
1039 validate_input_args(image, with);
1040
1041 int keepzero = params.set_default("keepzero", 1);
1042 int invert = params.set_default("invert",0);
1043 int matchfilt = params.set_default("matchfilt",1);
1044 int matchamp = params.set_default("matchamp",0);
1045 int radweight = params.set_default("radweight",0);
1046 int dbug = params.set_default("debug",0);
1047
1048 size_t size = (size_t)image->get_xsize() * image->get_ysize() * image->get_zsize();
1049
1050
1051 EMData *with2=NULL;
1052 if (matchfilt) {
1053 EMData *a = image->do_fft();
1054 EMData *b = with->do_fft();
1055
1056 vector <float> rfa=a->calc_radial_dist(a->get_ysize()/2,0.0f,1.0f,1);
1057 vector <float> rfb=b->calc_radial_dist(b->get_ysize()/2,0.0f,1.0f,1);
1058
1059 float avg=0;
1060 for (size_t i=0; i<a->get_ysize()/2.0f; i++) {
1061 rfa[i]=(rfb[i]==0?0.0f:(rfa[i]/rfb[i]));
1062 avg+=rfa[i];
1063 }
1064
1065 avg/=a->get_ysize()/2.0f;
1066 for (size_t i=0; i<a->get_ysize()/2.0f; i++) {
1067 if (rfa[i]>avg*10.0) rfa[i]=10.0; // If some particular location has a small but non-zero value, we don't want to overcorrect it
1068 }
1069 rfa[0]=0.0;
1070
1071 if (dbug) b->write_image("a.hdf",-1);
1072
1073 b->apply_radial_func(0.0f,1.0f/a->get_ysize(),rfa);
1074 with2=b->do_ift();
1075
1076 if (dbug) b->write_image("a.hdf",-1);
1077 if (dbug) a->write_image("a.hdf",-1);
1078
1079/* if (dbug) {
1080 FILE *out=fopen("a.txt","w");
1081 for (int i=0; i<a->get_ysize()/2.0; i++) fprintf(out,"%d\t%f\n",i,rfa[i]);
1082 fclose(out);
1083
1084 out=fopen("b.txt","w");
1085 for (int i=0; i<a->get_ysize()/2.0; i++) fprintf(out,"%d\t%f\n",i,rfb[i]);
1086 fclose(out);
1087 }*/
1088
1089
1090 delete a;
1091 delete b;
1092
1093 if (dbug) {
1094 with2->write_image("a.hdf",-1);
1095 image->write_image("a.hdf",-1);
1096 }
1097
1098// with2->process_inplace("matchfilt",Dict("to",this));
1099// x_data = with2->get_data();
1100 }
1101
1102 // This applies the individual Fourier amplitudes from 'image' and
1103 // applies them to 'with'
1104 if (matchamp) {
1105 EMData *a = image->do_fft();
1106 EMData *b = with->do_fft();
1107 size_t size2 = (size_t)a->get_xsize() * a->get_ysize() * a->get_zsize();
1108
1109 a->ri2ap();
1110 b->ri2ap();
1111
1112 const float *const ad=a->get_const_data();
1113 float * bd=b->get_data();
1114
1115 for (size_t i=0; i<size2; i+=2) bd[i]=ad[i];
1116 b->update();
1117
1118 b->ap2ri();
1119 with2=b->do_ift();
1120//with2->write_image("a.hdf",-1);
1121 delete a;
1122 delete b;
1123 }
1124
1125 const float * x_data;
1126 if (with2) x_data=with2->get_const_data();
1127 else x_data = with->get_const_data();
1128 const float *const y_data = image->get_const_data();
1129
1130 size_t nx = image->get_xsize();
1131 float m = 0;
1132 float b = 0;
1133
1134 // This will write the x vs y file used to calculate the density
1135 // optimization. This behavior may change in the future
1136 if (dbug) {
1137 FILE *out=fopen("dbug.optvar.txt","w");
1138 if (out) {
1139 for (size_t i=0; i<size; i++) {
1140 if ( !keepzero || (x_data[i] && y_data[i])) fprintf(out,"%g\t%g\n",x_data[i],y_data[i]);
1141 }
1142 fclose(out);
1143 }
1144 }
1145
1146
1147 Util::calc_least_square_fit(size, x_data, y_data, &m, &b, keepzero);
1148 if (m == 0) {
1149 m = FLT_MIN;
1150 }
1151 b = -b / m;
1152 m = 1.0f / m;
1153
1154 // While negative slopes are really not a valid comparison in most cases, we
1155 // still want to detect these instances, so this if is removed
1156/* if (m < 0) {
1157 b = 0;
1158 m = 1000.0;
1159 }*/
1160
1161 double result = 0;
1162 int count = 0;
1163
1164 if (radweight) {
1165 if (image->get_zsize()!=1) throw ImageDimensionException("radweight option is 2D only");
1166 if (keepzero) {
1167 for (size_t i = 0,y=0; i < size; y++) {
1168 for (size_t x=0; x<nx; i++,x++) {
1169 if (y_data[i] && x_data[i]) {
1170 if (invert) result += Util::square(x_data[i] - (y_data[i]-b)/m)*(hypot((float)x,(float)y)+nx/4.0);
1171 else result += Util::square((x_data[i] * m) + b - y_data[i])*(hypot((float)x,(float)y)+nx/4.0);
1172 count++;
1173 }
1174 }
1175 }
1176 result/=count;
1177 }
1178 else {
1179 for (size_t i = 0,y=0; i < size; y++) {
1180 for (size_t x=0; x<nx; i++,x++) {
1181 if (invert) result += Util::square(x_data[i] - (y_data[i]-b)/m)*(hypot((float)x,(float)y)+nx/4.0);
1182 else result += Util::square((x_data[i] * m) + b - y_data[i])*(hypot((float)x,(float)y)+nx/4.0);
1183 }
1184 }
1185 result = result / size;
1186 }
1187 }
1188 else {
1189 if (keepzero) {
1190 for (size_t i = 0; i < size; i++) {
1191 if (y_data[i] && x_data[i]) {
1192 if (invert) result += Util::square(x_data[i] - (y_data[i]-b)/m);
1193 else result += Util::square((x_data[i] * m) + b - y_data[i]);
1194 count++;
1195 }
1196 }
1197 result/=count;
1198 }
1199 else {
1200 for (size_t i = 0; i < size; i++) {
1201 if (invert) result += Util::square(x_data[i] - (y_data[i]-b)/m);
1202 else result += Util::square((x_data[i] * m) + b - y_data[i]);
1203 }
1204 result = result / size;
1205 }
1206 }
1207 scale = m;
1208 shift = b;
1209
1210 image->set_attr("ovcmp_m",m);
1211 image->set_attr("ovcmp_b",b);
1212 if (with2) delete with2;
1213 EXITFUNC;
1214
1215#if 0
1216 return (1 - result);
1217#endif
1218
1219 return static_cast<float>(result);
1220}
1221
1222float PhaseCmp::cmp(EMData * image, EMData *with) const
1223{
1224 ENTERFUNC;
1225
1226 int snrweight = params.set_default("snrweight", 0);
1227 int snrfn = params.set_default("snrfn",0);
1228 int ampweight = params.set_default("ampweight",0);
1229 int zeromask = params.set_default("zeromask",0);
1230 float minres = params.set_default("minres",500.0f);
1231 float maxres = params.set_default("maxres",10.0f);
1232
1233 if (snrweight && snrfn) throw InvalidCallException("SNR weight and SNRfn cannot both be set in the phase comparator");
1234
1235 EMData *image_fft = NULL;
1236 EMData *with_fft = NULL;
1237
1238 int ny = image->get_ysize();
1239// int np = (int) ceil(Ctf::CTFOS * sqrt(2.0f) * ny / 2) + 2;
1240 int np = 0;
1241 vector<float> snr;
1242
1243 // weighting based on SNR estimate from CTF
1244 if (snrweight) {
1245 Ctf *ctf = NULL;
1246 if (!image->has_attr("ctf")) {
1247 if (!with->has_attr("ctf")) throw InvalidCallException("SNR weight with no CTF parameters");
1248 ctf=with->get_attr("ctf");
1249 }
1250 else ctf=image->get_attr("ctf");
1251
1252 float ds=1.0f/(ctf->apix*ny);
1253 snr=ctf->compute_1d(ny,ds,Ctf::CTF_SNR); // note that this returns ny/2 values
1254 for (int i=0; i<snr.size(); i++) {
1255 if (snr[i]<=0) snr[i]=0.001; // make sure that points don't get completely excluded due to SNR estimation issues, or worse, contribute with a negative weight
1256 }
1257 if(ctf) {delete ctf; ctf=0;}
1258 np=snr.size();
1259 }
1260 // weighting based on empirical SNR function (not really good)
1261 else if (snrfn==1) {
1262 np=ny/2;
1263 snr.resize(np);
1264
1265 for (int i = 0; i < np; i++) {
1266 float x2 = 10.0f*i/np;
1267 snr[i] = x2 * exp(-x2);
1268 }
1269 }
1270 // no weighting
1271 else {
1272 np=ny/2;
1273 snr.resize(np);
1274
1275 for (int i = 0; i < np; i++) snr[i]=1.0;
1276 }
1277
1278 // Min/max modifications to weighting
1279// float pmin,pmax;
1280
1281 float pmin = params.set_default("pmin",0.0f);
1282 float pmax = params.set_default("pmax",0.0f);
1283 if (minres>0 && pmin==0) pmin=((float)image->get_attr("apix_x")*image->get_ysize())/minres; //cutoff in pixels, assume square
1284// else pmin=0;
1285 if (maxres>0 && pmax==0) pmax=((float)image->get_attr("apix_x")*image->get_ysize())/maxres;
1286// else pmax=0;
1287
1288// printf("%f\t%f\n",pmin,pmax);
1289
1290 // We use 'soft' edges for the Fourier cutoffs to minimize issues with pixel interpolation
1291 for (int i = 0; i < np; i++) {
1292 if (pmin>0) snr[i]*=(tanh(5.0f*(i-pmin)/pmin)+1.0f)/2.0f;
1293 if (pmax>0) snr[i]*=(1.0f-tanh(i-pmax))/2.0f;
1294// printf("%d\t%f\n",i,snr[i]);
1295 }
1296
1297 if (zeromask) {
1298 image_fft=image->copy();
1299 with_fft=with->copy();
1300
1301 if (image_fft->is_complex()) image_fft->do_ift_inplace();
1302 if (with_fft->is_complex()) with_fft->do_ift_inplace();
1303
1304 int sz=image_fft->get_xsize()*image_fft->get_ysize()*image_fft->get_zsize();
1305 float *d1=image_fft->get_data();
1306 float *d2=with_fft->get_data();
1307
1308 for (int i=0; i<sz; i++) {
1309 if (d1[i]==0.0 || d2[i]==0.0) { d1[i]=0.0; d2[i]=0.0; }
1310 }
1311
1312 image_fft->update();
1313 with_fft->update();
1314 image_fft->do_fft_inplace();
1315 with_fft->do_fft_inplace();
1316 image_fft->set_attr("free_me",1);
1317 with_fft->set_attr("free_me",1);
1318 }
1319 else {
1320 if (image->is_complex()) image_fft=image;
1321 else {
1322 image_fft=image->do_fft();
1323 image_fft->set_attr("free_me",1);
1324 }
1325
1326 if (with->is_complex()) with_fft=with;
1327 else {
1328 with_fft=with->do_fft();
1329 with_fft->set_attr("free_me",1);
1330 }
1331 }
1332// image_fft->ri2ap();
1333// with_fft->ri2ap();
1334
1335 const float *const image_fft_data = image_fft->get_const_data();
1336 const float *const with_fft_data = with_fft->get_const_data();
1337 double sum = 0;
1338 double norm = FLT_MIN;
1339 size_t i = 0;
1340// int nx=image_fft->get_xsize();
1341 ny=image_fft->get_ysize();
1342 int nz=image_fft->get_zsize();
1343 int nx2=image_fft->get_xsize()/2;
1344 int ny2=image_fft->get_ysize()/2;
1345// int nz2=image_fft->get_zsize()/2;
1346
1347 // This can never happen any more...
1348 if (np==0) {
1349 for (int z = 0; z < nz; z++){
1350 for (int y = 0; y < ny; y++) {
1351 for (int x = 0; x < nx2; x ++) {
1352 float a;
1353 if (ampweight) a= (float)hypot(with_fft_data[i],with_fft_data[i+1]);
1354 else a=1.0f;
1355 sum += Util::angle_err_ri(image_fft_data[i],image_fft_data[i+1],with_fft_data[i],with_fft_data[i+1]) * a;
1356 norm += a;
1357 i += 2;
1358 }
1359 }
1360 }
1361
1362 }
1363 else if (nz==1) {
1364 for (int y = 0; y < ny; y++) {
1365 for (int x = 0; x < nx2; x ++) {
1366 int r=Util::hypot_fast_int(x,y>ny/2?ny-y:y);
1367 if (r>=ny2) { i+=2; continue; } // we only have snr values to the box radius
1368
1369 float a;
1370 if (ampweight) a= (float)hypot(with_fft_data[i],with_fft_data[i+1]);
1371 else a=1.0f;
1372 a*=snr[r];
1373 sum += Util::angle_err_ri(image_fft_data[i],image_fft_data[i+1],with_fft_data[i],with_fft_data[i+1]) * a;
1374 norm += a;
1375 i += 2;
1376 }
1377 }
1378 }
1379 else {
1380 for (int z = 0; z < nz; z++){
1381 for (int y = 0; y < ny; y++) {
1382 for (int x = 0; x < nx2; x ++) {
1383 int r=(int)Util::hypot3(x,y>ny/2?ny-y:y,z>nz/2?nz-z:z);
1384 if (r>=ny2) { i+=2; continue; } // we only have snr values to the box radius
1385
1386 float a;
1387 if (ampweight) a= (float)hypot(with_fft_data[i],with_fft_data[i+1]);
1388 else a=1.0f;
1389 a*=snr[r];
1390 sum += Util::angle_err_ri(image_fft_data[i],image_fft_data[i+1],with_fft_data[i],with_fft_data[i+1]) * a;
1391 norm += a;
1392 i += 2;
1393 }
1394 }
1395 }
1396
1397 }
1398
1399 EXITFUNC;
1400
1401 if( image_fft->has_attr("free_me") )
1402 {
1403 delete image_fft;
1404 image_fft = 0;
1405 }
1406 if( with_fft->has_attr("free_me") )
1407 {
1408 delete with_fft;
1409 with_fft = 0;
1410 }
1411#if 0
1412 return (1.0f - sum / norm);
1413#endif
1414 return (float)(sum / norm);
1415}
1416
1417float FRCCmp::cmp(EMData * image, EMData * with) const
1418{
1419 ENTERFUNC;
1420 validate_input_args(image, with);
1421
1422 int snrweight = params.set_default("snrweight", 0);
1423 int ampweight = params.set_default("ampweight", 0);
1424 int sweight = params.set_default("sweight", 1);
1425 int nweight = params.set_default("nweight", 0);
1426 int zeromask = params.set_default("zeromask",0);
1427 float minres = params.set_default("minres",200.0f);
1428 float maxres = params.set_default("maxres",8.0f);
1429
1430 vector < float >fsc;
1431 bool use_cpu = true;
1432
1433 if (use_cpu) {
1434 if (zeromask) {
1435 image=image->copy();
1436 with=with->copy();
1437
1438 int sz=image->get_xsize()*image->get_ysize()*image->get_zsize();
1439 float *d1=image->get_data();
1440 float *d2=with->get_data();
1441
1442 for (int i=0; i<sz; i++) {
1443 if (d1[i]==0.0 || d2[i]==0.0) { d1[i]=0.0; d2[i]=0.0; }
1444 }
1445
1446 image->update();
1447 with->update();
1448 image->do_fft_inplace();
1449 with->do_fft_inplace();
1450 image->set_attr("free_me",1);
1451 with->set_attr("free_me",1);
1452 }
1453
1454
1455 if (!image->is_complex()) {
1456 image=image->do_fft();
1457 image->set_attr("free_me",1);
1458 }
1459 if (!with->is_complex()) {
1460 with=with->do_fft();
1461 with->set_attr("free_me",1);
1462 }
1463
1464 fsc = image->calc_fourier_shell_correlation(with,1);
1465 }
1466
1467 int ny = image->get_ysize();
1468 int ny2=ny/2+1;
1469
1470 // The fast hypot here was supposed to speed things up. Little effect
1471// if (image->get_zsize()>1) fsc = image->calc_fourier_shell_correlation(with,1);
1472// else {
1473// double *sxy = (double *)malloc(ny2*sizeof(double)*4);
1474// double *sxx = sxy+ny2;
1475// double *syy = sxy+2*ny2;
1476// double *norm= sxy+3*ny2;
1477//
1478// float *df1=image->get_data();
1479// float *df2=with->get_data();
1480// int nx2=image->get_xsize();
1481//
1482// for (int y=-ny/2; y<ny/2; y++) {
1483// for (int x=0; x<nx2/2; x++) {
1484// if (x==0 && y<0) continue; // skip Friedel pair
1485// short r=Util::hypot_fast_int(x,y);
1486// if (r>ny2-1) continue;
1487// int l=x*2+(y<0?ny+y:y)*nx2;
1488// sxy[r]+=df1[l]*df2[l]+df1[l+1]*df2[l+1];
1489// sxx[r]+=df1[l]*df1[l];
1490// syy[r]+=df2[l]*df2[l];
1491// norm[r]+=1.0;
1492// }
1493// }
1494// fsc.resize(ny2*3);
1495// for (int r=0; r<ny2; r++) {
1496// fsc[r]=r*0.5/ny2;
1497// fsc[ny2+r]=sxy[r]/(sqrt(sxx[r])*sqrt(syy[r]));
1498// fsc[ny2*2+r]=norm[r];
1499// }
1500// free(sxy);
1501// }
1502
1503 vector<float> snr;
1504 if (snrweight) {
1505 Ctf *ctf = NULL;
1506 if (!image->has_attr("ctf")) {
1507 if (!with->has_attr("ctf")) throw InvalidCallException("SNR weight with no CTF parameters");
1508 ctf=with->get_attr("ctf");
1509 }
1510 else ctf=image->get_attr("ctf");
1511
1512 float ds=1.0f/(ctf->apix*ny);
1513 snr=ctf->compute_1d(ny,ds,Ctf::CTF_SNR);
1514 for (int i=0; i<snr.size(); i++) {
1515 if (snr[i]<=0) snr[i]=0.001; // make sure that points don't get completely excluded due to SNR estimation issues, or worse, contribute with a negative weight
1516 }
1517 if(ctf) {delete ctf; ctf=0;}
1518 }
1519
1520 vector<float> amp;
1521 if (ampweight) amp=image->calc_radial_dist(ny/2,0,1,0);
1522
1523 // Min/max modifications to weighting
1524 float pmin = params.set_default("pmin",0);
1525 float pmax = params.set_default("pmax", 0);
1526
1527 if (pmin==0 && minres>0)
1528 pmin=((float)image->get_attr("apix_x")*image->get_ysize())/minres; //cutoff in pixels, assume square
1529
1530 if (pmax==0 && maxres>0)
1531 pmax=((float)image->get_attr("apix_x")*image->get_ysize())/maxres;
1532
1533
1534 double sum=0.0, norm=0.0;
1535
1536 for (int i=0; i<ny/2; i++) {
1537 double weight=1.0;
1538 if (sweight) weight*=fsc[(ny2)*2+i];
1539 if (ampweight) weight*=amp[i];
1540 if (snrweight) weight*=snr[i];
1541// if (snrweight) {
1542// if (snr[i]>0) weight*=sqrt(snr[i]);
1543// else weight=0;
1544// }
1545//if(snr[i]<0) printf("snr[%d] = %1.5g\n",i,snr[i]);
1546 if (pmin>0) weight*=(tanh(5.0*(i-pmin)/pmin)+1.0)/2.0;
1547 if (pmax>0) weight*=(1.0-tanh(i-pmax))/2.0;
1548
1549 sum+=weight*fsc[ny2+i];
1550 norm+=weight;
1551// printf("%d\t%f\t%f\n",i,weight,fsc[ny/2+1+i]);
1552 }
1553
1554 // This performs a weighting that tries to normalize FRC by correcting from the number of particles represented by the average
1555 sum/=norm;
1556 if (nweight && with->get_attr_default("ptcl_repr",0) && sum>=0 && sum<1.0) {
1557 sum=sum/(1.0-sum); // convert to SNR
1558 sum/=(float)with->get_attr_default("ptcl_repr",0); // divide by ptcl represented
1559 sum=sum/(1.0+sum); // convert back to correlation
1560 }
1561
1562 if (image->has_attr("free_me")) delete image;
1563 if (with->has_attr("free_me")) delete with;
1564
1565 EXITFUNC;
1566
1567 if (!Util::goodf(&sum)) sum=-2.0; // normally should be >-1.0
1568
1569 //.Note the negative! This is because EMAN2 follows the convention that
1570 // smaller return values from comparitors indicate higher similarity -
1571 // this enables comparitors to be used in a generic fashion.
1572 return (float)-sum;
1573}
1574
1575float OptSubCmp::cmp(EMData * image, EMData * with) const
1576{
1577 ENTERFUNC;
1578 validate_input_args(image, with);
1579
1580// int snrweight = params.set_default("snrweight", 0);
1581// int ampweight = params.set_default("ampweight", 0);
1582// int sweight = params.set_default("sweight", 1);
1583// int nweight = params.set_default("nweight", 0);
1584 int ctfweight = params.set_default("ctfweight",0);
1585 int zeromask = params.set_default("zeromask",0);
1586// int swap = params.set_default("swap",0);
1587 float minres = params.set_default("minres",200.0f);
1588 float maxres = params.set_default("maxres",10.0f);
1589 EMData *mask = params.set_default("mask",(EMData *)NULL);
1590
1591// float ds=1.0f/((float)image->get_attr("apix_x")*(int)image->get_ysize());
1592 float apix=(float)image->get_attr("apix_x");
1593
1594 // Sometimes we will get the "raw" image with CTF as image and sometimes as with, we always want to subtract the less noisy
1595 // reference, so if one has CTF parameters (even if ctfweight isn't used) we always pass it in as the primary
1596 EMData *diff;
1597 if (image->has_attr("ctf")) diff=image->process("math.sub.optimal",Dict("ref",with,"return_presigma",1,"low_cutoff_frequency",apix/minres ,"high_cutoff_frequency",apix/maxres,"ctfweight",ctfweight));
1598 else diff=with->process("math.sub.optimal",Dict("ref",image,"return_presigma",1,"low_cutoff_frequency",apix/minres ,"high_cutoff_frequency",apix/maxres,"ctfweight",ctfweight));
1599
1600 if (mask!=NULL) diff->mult(*mask);
1601 if (zeromask) {
1602 EMData *tmp=with->process("threshold.notzero");
1603 diff->mult(*tmp);
1604 delete tmp;
1605 }
1606
1607// diff->process_inplace("filter.highpass.tophat",Dict("cutoff_freq",(float)1.0/minres));
1608// diff->process_inplace("filter.lowpass.tophat",Dict("cutoff_freq",(float)1.0/maxres));
1609
1610 float ret=(float)diff->get_attr("sigma")/(float)diff->get_attr("sigma_presub");
1611 delete diff;
1612 return ret;
1613
1614
1615// EMData *diff=image->process("math.sub.optimal",Dict("ref",with,"return_fft",1));
1616//
1617// // Very expensive to use a mask, since it requires another ift/fft pair
1618// if (mask!=NULL) {
1619// EMData *tmp = diff->do_ift();
1620// tmp->mult(*mask);
1621// delete diff;
1622// diff=tmp->do_fft();
1623// delete tmp;
1624// }
1625//
1626// // This gives us basically the 1-D power spectrum of what's left after subtraction (and masking)
1627// vector<float> dist=diff->calc_radial_dist(diff->get_ysize()/2,0.0f,1.0f,1);
1628// int s0=int(floor(1.0/(minres*ds)));
1629// int s1=int(ceil(1.0/(maxres*ds)));
1630// if (s0<2) s0=2;
1631//
1632// if (s1<=s0) throw InvalidCallException("OptSubCmp error. minres must be greater than maxres.");
1633// // printf("%d %d\n",s0,s1);
1634//
1635// double sum=0.0f,sum2=0.0f;
1636// for (int i=s0; i<s1; i++) sum+=dist[i]*i;
1637// sum/=image->get_size()*s1;
1638//
1639// delete diff;
1640// return sum;
1641}
1642float VerticalCmp::cmp(EMData * image, EMData * with) const
1643{
1644 ENTERFUNC;
1645 int nx=image->get_xsize();
1646 int ny=image->get_ysize();
1647// vector<float> proj(nx,0);
1648 float max=0;
1649 for (int x=0; x<nx; x++){
1650 float s=0;
1651 for (int y=0; y<ny; y++){
1652 s+=image->get_value_at(x,y);
1653 }
1654// proj[x]=s;
1655 if (s>max)
1656 max=s;
1657 }
1658
1659// Dict params=Util::get_stats(proj);
1660// float ret=params["std_dev"];
1661 return -max;
1662}
1663
1665{
1666 dump_factory < Cmp > ();
1667}
1668
1669map<string, vector<string> > EMAN::dump_cmps_list()
1670{
1671 return dump_factory_list < Cmp > ();
1672}
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:113
void validate_input_args(const EMData *image, const EMData *with) const
Definition: cmp.cpp:81
Dict params
Definition: cmp.h:132
Ctf is the base class for all CTF model.
Definition: ctf.h:60
float apix
Definition: ctf.h:88
virtual vector< float > compute_1d(int size, float ds, CtfType t, XYData *struct_factor=0)=0
@ CTF_SNR
Definition: ctf.h:68
Dict is a dictionary to store <string, EMObject> pair.
Definition: emobject.h:385
type set_default(const string &key, type val)
Default setting behavior This can be achieved using a template - d.woolford Jan 2008 (before there wa...
Definition: emobject.h:569
bool has_key(const string &key) const
Ask the Dictionary if it as a particular key.
Definition: emobject.h:511
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:417
EMData stores an image's data and defines core image processing routines.
Definition: emdata.h:82
void apply_radial_func(float x0, float dx, vector< float >array, bool interp=true)
multiplies by a radial function in fourier space.
Definition: emdata.cpp:2677
vector< float > calc_radial_dist(int n, float x0, float dx, int inten)
calculates radial distribution.
Definition: emdata.cpp:2781
static bool is_same_size(const EMData *image1, const EMData *image2)
Check whether two EMData images are of the same size.
Definition: emutil.cpp:1224
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:1417
Factory is used to store objects to create new instances.
Definition: emobject.h:725
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:189
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:1575
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:1036
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:1222
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:988
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:280
virtual float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:643
virtual float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:856
virtual float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:691
virtual float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:748
static void calc_least_square_fit(size_t nitems, const float *data_x, const float *data_y, float *p_slope, float *p_intercept, bool ignore_zero, float absmax=0)
calculate the least square fit value.
Definition: util.cpp:538
static int hypot3sq(int x, int y, int z)
Euclidean distance function squared in 3D: f(x,y,z) = (x*x + y*y + z*z);.
Definition: util.h:805
static short hypot_fast_int(int x, int y)
Euclidean distance in 2D for integers computed fast using a cached lookup table.
Definition: util.cpp:764
static int square(int n)
Calculate a number's square.
Definition: util.h:736
static int goodf(const float *p_f)
Check whether a number is a good float.
Definition: util.h:1112
static float angle_err_ri(float r1, float i1, float r2, float i2)
Calculate the angular phase difference between two r/i vectors.
Definition: util.h:1099
static bool is_nan(const float number)
tell whether a float value is a NaN
Definition: util.h:105
static float hypot3(int x, int y, int z)
Euclidean distance function in 3D: f(x,y,z) = sqrt(x*x + y*y + z*z);.
Definition: util.h:827
static float square_sum(float x, float y)
Calcuate (x*x + y*y).
Definition: util.h:764
float cmp(EMData *image, EMData *with) const
To compare 'image' with another image passed in through its parameters.
Definition: cmp.cpp:1642
float get_value_at_wrap_cuda(float *data, int tx, int ty, int tz, int nx, int ny, int nz)
float2 get_stats_cuda(const float *data, const int nx, const int ny, const int nz)
float ccc_cmp_cuda(const float *data1, const float *data2, const float *dm, const int &nx, const int &ny, const int &nz)
float dot_cmp_cuda(float *data1, float *data2, const float *dm, const int &nx, const int &ny, const int &nz)
float fsc_tomo_cmp_cuda(const float *data1, const float *data2, const float data1threshold, const float data2threshold, const float minres, const float maxres, const int nx, const int ny, const int nz)
EMData * sqrt() const
return square root of current image
bool is_complex() const
Is this a complex image?
#define InvalidValueException(val, desc)
Definition: exception.h:285
#define ImageFormatException(desc)
Definition: exception.h:147
#define ImageDimensionException(desc)
Definition: exception.h:166
#define InvalidCallException(desc)
Definition: exception.h:348
#define NullPointerException(desc)
Definition: exception.h:241
EMData * calc_ccf(EMData *with=0, fp_flag fpflag=CIRCULANT, bool center=false)
Calculate Cross-Correlation Function (CCF).
Definition: emdata.cpp:1499
#define ENTERFUNC
Definition: log.h:48
#define EXITFUNC
Definition: log.h:49
E2Exception class.
Definition: aligner.h:40
void dump_cmps()
Definition: cmp.cpp:1664
map< string, vector< string > > dump_cmps_list()
Definition: cmp.cpp:1669
#define y(i, j)
Definition: projector.cpp:1516
#define x(i)
Definition: projector.cpp:1517
#define dm(i)
Definition: projector.cpp:1606