TPCCLIB
Loading...
Searching...
No Matches
imgmatch.c
Go to the documentation of this file.
1
7/*****************************************************************************/
8#include "tpcclibConfig.h"
9/*****************************************************************************/
10#include <stdio.h>
11#include <stdlib.h>
12#include <math.h>
13#include <string.h>
14#include <time.h>
15#include <float.h>
16/*****************************************************************************/
17#include "libtpcmisc.h"
18#include "libtpcimgio.h"
19//#include "libtpcimgp.h"
20/*****************************************************************************/
21
22/*****************************************************************************/
23static char *info[] = {
24 "Verify that data in two PET images are similar. ECAT 6.3 and 7, NIfTI-1,",
25 "Analyze 7.5 and microPET formats are (partially) supported.",
26 "Returns 0 if files match and a nonzero value otherwise.",
27 " ",
28 "Usage: @P [Options] image1 image2",
29 " ",
30 "Options:",
31//" -a[ll]",
32//" All values in the header are required to match.",
33//" (As default only image sizes and data types are checked.)",
34 " -header=<y|N>",
35 " All values in the headers (excluding transformation parameters)",
36 " are required to match (y) or not tested (n, default).",
37 " -transform=<y|N>",
38 " Transformation parameters in the headers are required to match (y),",
39 " or not tested (n, default).",
40 " -planes=<Y|n>",
41 " Planes numbers are required to match (y, default) or not tested (n).",
42 " -frames=<Y|n>",
43 " Frame times are required to match (y, default) or not tested (n).",
44 " -abs=<value>",
45 " Absolute result differences must not exceed the specified limit value;",
46 " by default no difference is allowed.",
47 " -rel=<value>",
48 " Relative differences must not exceed the specified percent limit.",
49//" -roughly",
50//" Pixel values are required to match roughly (99.9%), not exactly.",
51//" -around",
52//" Pixel values are required to be around the same (90%), not exactly.",
53 " -ss",
54 " Calculates sum-of-squares between image pixel values.",
55 " -stdoptions", // List standard options like --help, -v, etc
56 " ",
57 "Image1 and image2 must be in the same format, and they must have same",
58 "matrix size and plane numbers.",
59 " ",
60 "See also: tacmatch, resmatch, imgcalc, imgunit, ecatmax, img2tif",
61 " ",
62 "Keywords: Image, tool, software testing",
63 0};
64/*****************************************************************************/
65
66/*****************************************************************************/
67/* Turn on the globbing of the command line, since it is disabled by default in
68 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
69 In Unix&Linux wildcard command line processing is enabled by default. */
70/*
71#undef _CRT_glob
72#define _CRT_glob -1
73*/
74int _dowildcard = -1;
75/*****************************************************************************/
76
77/*****************************************************************************/
81int main(int argc, char **argv)
82{
83 int ai, help=0, version=0, verbose=1;
84 double ROUGH_TEST_LIMIT=0.001;
85 double AROUND_TEST_LIMIT=0.1;
86 int ret;
87 int isHeader=0;
88 int isTransform=0;
89 int isPlanes=1;
90 int isFrames=1;
91 int calcSS=0;
92 char file1[FILENAME_MAX], file2[FILENAME_MAX], *cptr;
93 IMG img1, img2;
94 double accur=0.0;
95 double test_abs=0.0;
96 float maxrel, maxabs;
97 VOXEL_4D maxrelvoxel, maxabsvoxel;
98
99
100 /*
101 * Get arguments
102 */
103 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
104 file1[0]=file2[0]=(char)0;
105 imgInit(&img1); imgInit(&img2);
106 /* Options */
107 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') {
108 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
109 cptr=argv[ai]+1;
110 if(strncasecmp(cptr, "ROUGHLY", 4)==0) { // deprecated
111 accur=ROUGH_TEST_LIMIT; continue;
112 } else if(strncasecmp(cptr, "AROUND", 3)==0) { // deprecated
113 accur=AROUND_TEST_LIMIT; continue;
114 } else if(strncasecmp(cptr, "ABS=", 4)==0) {
115 cptr+=4; test_abs=atof_dpi(cptr); if(test_abs>=0.0) continue;
116 } else if(strncasecmp(cptr, "REL=", 4)==0) {
117 cptr+=4; accur=0.01*atof_dpi(cptr); if(accur>=0.0) continue;
118 } else if(strncasecmp(cptr, "HEADER=", 7)==0) {
119 cptr+=7;
120 if(strncasecmp(cptr, "YES", 1)==0) {isHeader=1; continue;}
121 if(strncasecmp(cptr, "NO", 1)==0) {isHeader=0; continue;}
122 } else if(strncasecmp(cptr, "TRANSFORM=", 10)==0) {
123 cptr+=10;
124 if(strncasecmp(cptr, "YES", 1)==0) {isTransform=1; continue;}
125 if(strncasecmp(cptr, "NO", 1)==0) {isTransform=0; continue;}
126 } else if(strncasecmp(cptr, "ALL", 1)==0) { // deprecated
127 isHeader=1; continue;
128 } else if(strncasecmp(cptr, "PLANES=", 7)==0) {
129 cptr+=7;
130 if(strncasecmp(cptr, "YES", 1)==0) {isPlanes=1; continue;}
131 if(strncasecmp(cptr, "NO", 1)==0) {isPlanes=0; continue;}
132 } else if(strncasecmp(cptr, "FRAMES=", 7)==0) {
133 cptr+=7;
134 if(strncasecmp(cptr, "YES", 1)==0) {isFrames=1; continue;}
135 if(strncasecmp(cptr, "NO", 1)==0) {isFrames=0; continue;}
136 } else if(strcasecmp(cptr, "SS")==0) {
137 calcSS=1; continue;
138 }
139 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
140 return(1);
141 } else break;
142
143 /* Print help or version? */
144 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
145 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
146 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
147
148 /* Arguments */
149 if(ai<argc) strlcpy(file1, argv[ai++], FILENAME_MAX);
150 if(ai<argc) strlcpy(file2, argv[ai++], FILENAME_MAX);
151 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
152
153 /* Is something missing? */
154 if(!file2[0]) {tpcPrintUsage(argv[0], info, stdout); return(1);}
155
156
157 /* In verbose mode print arguments and options */
158 if(verbose>1) {
159 for(ai=0; ai<argc; ai++) printf("%s ", argv[ai]);
160 printf("\n");
161 printf("file1 := %s\n", file1);
162 printf("file2 := %s\n", file2);
163 printf("isHeader := %d\n", isHeader);
164 printf("isTransform := %d\n", isTransform);
165 printf("isPlanes := %d\n", isPlanes);
166 printf("isFrames := %d\n", isFrames);
167 printf("accur := %g\n", accur);
168 printf("test_abs := %g\n", test_abs);
169 printf("calcSS := %d\n", calcSS);
170 }
171
172#if(0) // Analyze filenames can be funny, do not check
173 /* Check that input files do exist */
174 if(access(file1, 0) == -1) {
175 fprintf(stderr, "Error: file '%s' not found.\n", file1); return(2);}
176 if(access(file2, 0) == -1) {
177 fprintf(stderr, "Error: file '%s' not found.\n", file2); return(2);}
178#endif
179
180 /*
181 * Read image files
182 */
183 if(verbose>1) fprintf(stdout, "reading image %s\n", file1);
184 ret=imgRead(file1, &img1); if(verbose>10) imgInfo(&img1);
185 if(ret) {fprintf(stderr, "Error: %s\n", img1.statmsg); return(2);}
186 if(verbose>1) fprintf(stdout, "reading image %s\n", file2);
187 ret=imgRead(file2, &img2); if(verbose>10) imgInfo(&img2);
188 if(ret) {
189 fprintf(stderr, "Error: %s\n", img2.statmsg);
190 imgEmpty(&img1); return(2);
191 }
192
193 /*
194 * Check that file types, sizes etc are matching
195 */
196 if(verbose>1) fprintf(stdout, "testing image size etc\n");
197 if(img1.type!=img2.type) {
198 if(verbose>0) fprintf(stderr, "Mismatching image type.\n");
199 imgEmpty(&img1); imgEmpty(&img2); return(3);
200 }
201 if(img1.dimt!=img2.dimt){
202 if(verbose>0) fprintf(stderr, "Mismatching number of frames.\n");
203 imgEmpty(&img1); imgEmpty(&img2); return(4);
204 }
205 if(img1.dimx!=img2.dimx){
206 if(verbose>0) fprintf(stderr, "Mismatching number of pixels (dimension x).\n");
207 imgEmpty(&img1); imgEmpty(&img2); return(5);
208 }
209 if(img1.dimy!=img2.dimy){
210 if(verbose>0) fprintf(stderr, "Mismatching number of pixels (dimension y).\n");
211 imgEmpty(&img1); imgEmpty(&img2); return(6);
212 }
213 if(img1.dimz!=img2.dimz){
214 if(verbose>0) fprintf(stderr, "Mismatching number of planes.\n");
215 imgEmpty(&img1); imgEmpty(&img2); return(7);
216 }
217
218 /*
219 * Match headers if required
220 */
221 if(isHeader){
222 if(verbose>1) fprintf(stdout, "testing header contents\n");
223 ret=imgMatchHeader(&img1, &img2);
224 if(ret) {
225 if(verbose>0) fprintf(stdout, "Image headers do not match.\n");
226 imgEmpty(&img1); imgEmpty(&img2); return(11);
227 }
228 }
229
230 /*
231 * Match transformation parameters in headers if required
232 */
233 if(isTransform){
234 if(verbose>1) fprintf(stdout, "testing header transformation parameters\n");
235 ret=imgMatchTransform(&img1, &img2);
236 if(ret) {
237 if(verbose>0) fprintf(stdout, "Image transformation parameters do not match.\n");
238 imgEmpty(&img1); imgEmpty(&img2); return(11);
239 }
240 }
241
242 /*
243 * Match plane numbers if required
244 */
245 if(isPlanes) {
246 if(verbose>1) fprintf(stdout, "testing plane numbers\n");
247 ret=imgMatchPlanes(&img1, &img2);
248 if(ret) {
249 if(verbose>0) fprintf(stdout, "Plane numbers do not match.\n");
250 imgEmpty(&img1); imgEmpty(&img2); return(21);
251 }
252 }
253
254 /*
255 * Match frame times if required
256 */
257 if(isFrames) {
258 if(verbose>1) fprintf(stdout, "testing frame times\n");
259 ret=imgMatchFrames(&img1, &img2);
260 if(ret) {
261 if(verbose>0) fprintf(stdout, "Frame times do not match.\n");
262 imgEmpty(&img1); imgEmpty(&img2); return(31);
263 }
264 }
265
266 /*
267 * Calculate sum-of-squares between image pixel values, if requested
268 */
269 if(calcSS) {
270 double ss=0.0;
271 if(imgSS(&img1, &img2, &ss)!=0 || !isfinite(ss)) {
272 fprintf(stderr, "Error: cannot calculate sum-of-squares.\n");
273 fflush(stderr);
274 } else {
275 printf("SS := %e\n", ss);
276 fflush(stdout);
277 }
278 }
279
280 /*
281 * Check pixel values
282 */
283 if(verbose>1) fprintf(stdout, "testing pixel values\n");
284/*
285 ret=imgMatchMatrix(&img1, &img2, accur);
286 if(ret) {
287 fprintf(stdout, "Images do not match.\n");
288 imgEmpty(&img1); imgEmpty(&img2); return(101);
289 }
290*/
291 /* Calculate maximal absolute and relational differences */
292 ret=imgMaxDifference(&img1, &img2,
293 &maxabsvoxel, &maxabs, &maxrelvoxel, &maxrel);
294 if(ret>0) {
295 fprintf(stdout, "Error in testing pixel values (%d).\n", ret);
296 imgEmpty(&img1); imgEmpty(&img2); return(101);
297 } else if(ret<0) { // no differences
298 imgEmpty(&img1); imgEmpty(&img2);
299 if(verbose>0) fprintf(stdout, "Images match.\n");
300 return(0);
301 }
302 /* Differences were found; check if below allowed limits */
303 if(verbose>1) {
304 printf("max_absolute_difference := %g\n", maxabs);
305 if(maxabs>0.0)
306 printf("max_absolute_difference_at := %d,%d,%d,%d\n",
307 maxabsvoxel.z, maxabsvoxel.y, maxabsvoxel.x, maxabsvoxel.t);
308 printf("max_relational_difference := %g\n", maxrel);
309 if(maxrel>0.0)
310 printf("max_relational_difference_at := %d,%d,%d,%d\n",
311 maxrelvoxel.z, maxrelvoxel.y, maxrelvoxel.x, maxrelvoxel.t);
312 }
313 if(maxabs>test_abs && maxrel>accur) {
314 if(verbose>0) fprintf(stdout, "Images do not match.\n");
315 if(verbose>1) {
316 printf(" %g > %g\n", maxabs, test_abs);
317 printf(" %g > %g\n", maxrel, accur);
318 }
319 imgEmpty(&img1); imgEmpty(&img2); return(102);
320 }
321 /* but difference is below limit */
322 if(maxabs>0.0 && maxabs<=test_abs) {
323 if(verbose>0) printf("Maximal absolute difference is below the limit.\n");
324 if(verbose>1) printf(" %g <= %g\n", maxabs, test_abs);
325 }
326 if(maxrel>0.0 && maxrel<=accur) {
327 if(verbose>0) printf("Maximal relational difference is below the limit.\n");
328 if(verbose>1) printf(" %g <= %g\n", maxrel, accur);
329 }
330
331 imgEmpty(&img1); imgEmpty(&img2);
332 if(verbose>0) fprintf(stdout, "Images are similar within specified limits.\n");
333
334 return(0);
335}
336/*****************************************************************************/
337
338/*****************************************************************************/
double atof_dpi(char *str)
Definition decpoint.c:59
void imgInfo(IMG *image)
Definition img.c:359
void imgEmpty(IMG *image)
Definition img.c:121
void imgInit(IMG *image)
Definition img.c:60
int imgMatchPlanes(IMG *img1, IMG *img2)
Definition imgcomp.c:307
int imgSS(IMG *img1, IMG *img2, double *ss)
Definition imgcomp.c:412
int imgMatchHeader(IMG *img1, IMG *img2)
Definition imgcomp.c:128
int imgMatchTransform(IMG *img1, IMG *img2)
Definition imgcomp.c:85
int imgMaxDifference(IMG *img1, IMG *img2, VOXEL_4D *absdiff, float *abs_max, VOXEL_4D *reldiff, float *rel_max)
Definition imgcomp.c:328
int imgMatchFrames(IMG *img1, IMG *img2)
Definition imgcomp.c:286
int imgRead(const char *fname, IMG *img)
Definition imgfile.c:26
Header file for libtpcimgio.
Header file for libtpcmisc.
int tpcProcessStdOptions(const char *s, int *print_usage, int *print_version, int *verbose_level)
Definition proginfo.c:40
size_t strlcpy(char *dst, const char *src, size_t dstsize)
Definition strext.c:245
int tpcHtmlUsage(const char *program, char *text[], const char *path)
Definition proginfo.c:213
void tpcPrintBuild(const char *program, FILE *fp)
Definition proginfo.c:383
void tpcPrintUsage(const char *program, char *text[], FILE *fp)
Definition proginfo.c:158
unsigned short int dimx
char type
unsigned short int dimt
unsigned short int dimz
unsigned short int dimy
const char * statmsg