TPCCLIB
Loading...
Searching...
No Matches
tac2nii.c
Go to the documentation of this file.
1
8/*****************************************************************************/
9#include "tpcclibConfig.h"
10/*****************************************************************************/
11#include <stdio.h>
12#include <stdlib.h>
13#include <unistd.h>
14#include <math.h>
15#include <string.h>
16#include <time.h>
17/*****************************************************************************/
18#include "libtpcmisc.h"
19#include "libtpccurveio.h"
20#include "libtpcimgio.h"
21#include "libtpcroi.h"
22#include "libtpcimgp.h"
23/*****************************************************************************/
24
25/*****************************************************************************/
26int simRCDims(
28 const long long N,
30 const int mxdim,
32 const int mydim,
34 const int mzdim,
36 int *rnx,
38 int *rny,
40 int *rnz,
42 int *rcxdim,
44 int *rcydim,
46 int *rczdim
47);
48/*****************************************************************************/
49
50/*****************************************************************************/
51static char *info[] = {
52 "Create a 4D PET image file in NIfTI 1S format, with contents from",
53 "the user-specified TAC file, for software testing.",
54 "Image volume is divided into rectangular cuboids, containing as pixel",
55 "values the regional TAC values.",
56 "Frame times are written in SIF as specified in the TAC file.",
57 " ",
58 "Usage: @P [Options] tacfile xdim ydim zdim imagefile [template]",
59 " ",
60 "If file name for template NIfTI is given, the rectangular cuboids are",
61 "are saved in it, with integer pixel values starting from 1.",
62 " ",
63 "Options:",
64 " -stdoptions", // List standard options like --help, -v, etc
65 " ",
66 "See also: dft2img, flat2img, img2tif, img2dft, simboxes, pxl2mask",
67 " ",
68 "Keywords: image, NIfTI, simulation, software testing",
69 0};
70/*****************************************************************************/
71
72/*****************************************************************************/
73/* Turn on the globbing of the command line, since it is disabled by default in
74 mingw-w64 (_dowildcard=0); in MinGW32 define _CRT_glob instead, if necessary;
75 In Unix&Linux wildcard command line processing is enabled by default. */
76/*
77#undef _CRT_glob
78#define _CRT_glob -1
79*/
80int _dowildcard = -1;
81/*****************************************************************************/
82
83/*****************************************************************************/
87/*****************************************************************************/
88int main(int argc, char **argv)
89{
90 int ai, help=0, version=0, verbose=1;
91 char tacfile[FILENAME_MAX], dbname[FILENAME_MAX], tname[FILENAME_MAX];
92 int dimx=0, dimy=0, dimz=0;
93
94 /*
95 * Get arguments
96 */
97 if(argc==1) {tpcPrintUsage(argv[0], info, stderr); return(1);}
98 tacfile[0]=dbname[0]=tname[0]=(char)0;
99 /* Options */
100 for(ai=1; ai<argc; ai++) if(*argv[ai]=='-') { /* options */
101 char *cptr=argv[ai]+1; if(*cptr=='-') cptr++; if(cptr==NULL) continue;
102 if(tpcProcessStdOptions(argv[ai], &help, &version, &verbose)==0) continue;
103 fprintf(stderr, "Error: invalid option '%s'.\n", argv[ai]);
104 return(1);
105 } else break;
106
107 /* Print help or version? */
108 if(help==2) {tpcHtmlUsage(argv[0], info, ""); return(0);}
109 if(help) {tpcPrintUsage(argv[0], info, stdout); return(0);}
110 if(version) {tpcPrintBuild(argv[0], stdout); return(0);}
111
112 /* Process other arguments, starting from the first non-option */
113 if(ai<argc) {strlcpy(tacfile, argv[ai], FILENAME_MAX); ai++;}
114 if(ai<argc) {dimx=atoi(argv[ai++]);}
115 if(ai<argc) {dimy=atoi(argv[ai++]);}
116 if(ai<argc) {dimz=atoi(argv[ai++]);}
117 if(ai<argc) {strlcpy(dbname, argv[ai], FILENAME_MAX); ai++;}
118 if(ai<argc) {strlcpy(tname, argv[ai], FILENAME_MAX); ai++;}
119 if(ai<argc) {fprintf(stderr, "Error: invalid argument '%s'.\n", argv[ai]); return(1);}
120
121
122 /* Is something missing? */
123 if(!dbname[0] || dimz<1) {
124 fprintf(stderr, "Error: missing command-line argument; try %s --help\n", argv[0]);
125 return(1);
126 }
127
128 /* In verbose mode print arguments and options */
129 if(verbose>1) {
130 printf("tacfile := %s\n", tacfile);
131 printf("dbname := %s\n", dbname);
132 if(tname[0]) printf("tname := %s\n", tname);
133 printf("dimx := %d\n", dimx);
134 printf("dimy := %d\n", dimy);
135 printf("dimz := %d\n", dimz);
136 fflush(stdout);
137 }
138
139
140 /*
141 * Read regional data
142 */
143 if(verbose>1) printf("Reading TAC file %s\n", tacfile);
144 DFT dft; dftInit(&dft);
145 if(dftRead(tacfile, &dft)) {
146 fprintf(stderr, "Error in reading '%s': %s\n", tacfile, dfterrmsg);
147 return(2);
148 }
149 if(verbose>2) printf("tacNr := %d\nframeNr := %d\n", dft.voiNr, dft.frameNr);
150 /* Get the min and max pixel values for the header and scaling */
151 double min, max;
152 if(dftMinMax(&dft, NULL, NULL, &min, &max)) {
153 fprintf(stderr, "Error: invalid TAC contents.\n");
154 dftEmpty(&dft);
155 return(2);
156 }
157 if(verbose>2) printf("min := %g\nmax := %g\n", min, max);
158
159
160 /*
161 * Determine dimensions of rectangular cuboids
162 */
163 if(verbose>1) printf("Determine dimensions of rectangular cuboids\n");
164 int rdimx=0, rdimy=0, rdimz=0;
165 int rnx=0, rny=0, rnz=0;
166 if(simRCDims(dft.voiNr, dimx, dimy, dimz, &rnx, &rny, &rnz, &rdimx, &rdimy, &rdimz)) {
167 fprintf(stderr, "Error: incompatible input data.\n");
168 dftEmpty(&dft);
169 return(3);
170 }
171 if(verbose>3)
172 printf("%d into %d x %d x %d -> %d x %d x %d (%d x %d x %d)\n",
173 dft.voiNr, dimx, dimy, dimz, rdimx, rdimy, rdimz, rnx, rny, rnz);
174
175
176 /*
177 * Make template data
178 */
179 if(verbose>1) printf("Allocate memory for template xyz matrix\n");
180 size_t pxlNr=(size_t)dimz*dimy*dimx;
181 int *idata;
182 idata=(int*)calloc(pxlNr, sizeof(int));
183 if(idata==NULL) {
184 fprintf(stderr, "Error: out of memory.\n");
185 dftEmpty(&dft);
186 return(4);
187 }
188 /* Fill the template with TAC numbers */
189 if(verbose>1) printf("Fill the template\n");
190 {
191 int xi, yi, zi;
192 int x[2], y[2], z[2];
193 xi=yi=zi=0;
194 for(int ri=0; ri<dft.voiNr; ri++) {
195 x[0]=xi*rdimx; x[1]=x[0]+rdimx-1;
196 y[0]=yi*rdimy; y[1]=y[0]+rdimy-1;
197 z[0]=zi*rdimz; z[1]=z[0]+rdimz-1;
198 if(verbose>4)
199 printf(" %s : %d,%d,%d - %d,%d,%d\n", dft.voi[ri].name,
200 x[0],y[0],z[0], x[1],y[1],z[1]);
201 for(int cz=z[0]; cz<=z[1]; cz++)
202 for(int cy=y[0]; cy<=y[1]; cy++)
203 for(int cx=x[0]; cx<=x[1]; cx++) {
204 size_t pos=((size_t)dimy*dimx)*cz + dimx*cy + cx;
205 idata[pos]=1+ri;
206 }
207 xi++; if(xi==rnx) {xi=0; yi++; if(yi==rny) {yi=0; zi++; if(zi==rnz) break;}}
208 }
209 }
210
211
212 /*
213 * Set NIfTI header contents, first for the template
214 */
215 if(verbose>1) printf("Fill NIfTI header\n");
216 NIFTI_DSR dsr;
217 /* Set NIfTI byte order to current machines byte order */
219 /* Initiate header struct with zeroes */
220 memset(&dsr.h, 0, sizeof(NIFTI_1_HEADER));
221 memset(&dsr.e, 0, sizeof(NIFTI_EXTENDER));
222 /* Set header */
224 strcpy(dsr.h.data_type, "");
225 strlcpy(dsr.h.db_name, dbname, 17);
226 dsr.h.extents=16384; // not used in NIfTI, but required for Analyze compatibility
227 dsr.h.regular='r'; // not used in NIfTI, but required for Analyze compatibility
228 dsr.h.dim_info='\0'; // MRI slice ordering
229 /* Image dimension */
230 for(int i=0; i<8; i++) dsr.h.dim[i]=1;
231 dsr.h.dim[0]=3;
232 dsr.h.dim[1]=dimx;
233 dsr.h.dim[2]=dimy;
234 dsr.h.dim[3]=dimz;
235 dsr.h.dim[4]=1;
236 dsr.h.intent_p1=0.0;
237 dsr.h.intent_p2=0.0;
238 dsr.h.intent_p3=0.0;
240 dsr.h.datatype=NIFTI_DT_SIGNED_INT; // For the template
241 dsr.h.bitpix=32;
242 dsr.h.slice_start=0;
243 for(int i=0; i<8; i++) dsr.h.pixdim[i]=0.0;
244 // https://nifti.nimh.nih.gov/nifti-1/documentation/nifti1fields/nifti1fields_pages/qsform.html
245 dsr.h.pixdim[0]=1.0; // Set to either 1.0 or -1.0
246 dsr.h.pixdim[1]=1.0;
247 dsr.h.pixdim[2]=1.0;
248 dsr.h.pixdim[3]=1.0;
249 dsr.h.vox_offset=352; // Would be 0 for 1D format
250 dsr.h.scl_slope=1.0; // data as floats, so no need to scale
251 dsr.h.scl_inter=0.0; // data as floats, so no need to scale
252 dsr.h.slice_end=0;
253 dsr.h.slice_code=0;
255 dsr.h.cal_max=dft.voiNr;
256 dsr.h.cal_min=0; // there may be voxels that have no TAC defined
257 dsr.h.slice_duration=0.0;
258 dsr.h.toffset=0.0;
259 dsr.h.glmax=dsr.h.cal_max; // unused in NIfTI
260 dsr.h.glmin=0; // unused in NIfTI
261 strlcpy(dsr.h.descrip, "tac2nii", 80);
262 strcpy(dsr.h.aux_file, "");
263 dsr.h.qform_code=0;
264 dsr.h.sform_code=0;
265 dsr.h.quatern_b=0;
266 dsr.h.quatern_c=0;
267 dsr.h.quatern_d=0;
268 dsr.h.qoffset_x=0;
269 dsr.h.qoffset_y=0;
270 dsr.h.qoffset_z=0;
271 for(int i=0; i<4; i++) dsr.h.srow_x[i]=0;
272 for(int i=0; i<4; i++) dsr.h.srow_y[i]=0;
273 for(int i=0; i<4; i++) dsr.h.srow_z[i]=0;
274 strcpy(dsr.h.intent_name, "");
275 strcpy(dsr.h.magic, "n+1"); // Would be "ni1" for 1D format
276 /* Extension is left as 0 0 0 0 */
277
278 /*
279 * Write template, if required.
280 */
281 if(tname[0]) {
282
283 if(verbose>1) printf("Make NIfTI file names for the template\n");
284 char hdrfile[FILENAME_MAX], imgfile[FILENAME_MAX];
285 if(niftiCreateFNames(tname, hdrfile, imgfile, NULL, IMG_NIFTI_1S)) {
286 fprintf(stderr, " Error: invalid NIfTI name %s\n", tname);
287 dftEmpty(&dft); free(idata);
288 return(11);
289 }
290
291 if(verbose>1) printf("Writing template NIfTI header\n");
292 /* Delete previous NIfTI */
293 niftiRemove(tname, IMG_NIFTI_1S, verbose-3);
294 /* Write NIfTI header */
295 char tmp[256];
296 if(niftiWriteHeader(hdrfile, &dsr, verbose-1, tmp)) {
297 fprintf(stderr, "Error: cannot write template header.\n");
298 if(verbose>1) fprintf(stderr, "Status: %s\n", tmp);
299 dftEmpty(&dft); free(idata);
300 return(12);
301 }
302
303 if(verbose>1) printf("Writing NIfTI image data\n");
304 FILE *fp=fopen(imgfile, "r+b");
305 if(fp==NULL) {
306 fprintf(stderr, "Error: cannot open %s for write.\n", imgfile);
307 dftEmpty(&dft); free(idata);
308 niftiRemove(tname, IMG_NIFTI_1S, verbose-3);
309 return(13);
310 }
311 /* Move file pointer to the place of matrix data start */
312 if(fseeko(fp, (size_t)dsr.h.vox_offset, SEEK_SET)!=0) {
313 fprintf(stderr, "Error: invalid file write position.\n");
314 fclose(fp); dftEmpty(&dft); free(idata);
315 niftiRemove(tname, IMG_NIFTI_1S, verbose-3);
316 return(14);
317 }
318 /* Write template data */
319 if(fwrite(idata, sizeof(int), pxlNr, fp) != pxlNr) {
320 fprintf(stderr, "Error: cannot write template matrix.\n");
321 fclose(fp); dftEmpty(&dft); free(idata);
322 niftiRemove(tname, IMG_NIFTI_1S, verbose-3);
323 return(15);
324 }
325 fclose(fp);
326 if(verbose>0) printf("written %s\n", imgfile);
327 }
328
329
330 /*
331 * Edit NIfTI header for the 4D image
332 */
333 if(verbose>1) printf("Set NIfTI header\n");
334 dsr.h.dim[0]=4;
335 dsr.h.dim[4]=dft.frameNr;
336 dsr.h.datatype=NIFTI_DT_FLOAT; // data as floats, so no need to scale
337 dsr.h.bitpix=32;
338 dsr.h.cal_max=max;
339 if(min>0.0) dsr.h.cal_min=0.0; // For voxels that do not represent any TAC
340 else dsr.h.cal_min=min;
341 dsr.h.glmax=max; // unused in NIfTI
342 dsr.h.glmin=dsr.h.cal_min; // unused in NIfTI
343 strlcpy(dsr.h.descrip, "tac2nii", 80);
344
345 /* Make NIfTI filenames */
346 if(verbose>1) printf("Make NIfTI file names\n");
347 char hdrfile[FILENAME_MAX], imgfile[FILENAME_MAX], siffile[FILENAME_MAX];
348 if(niftiCreateFNames(dbname, hdrfile, imgfile, siffile, IMG_NIFTI_1S)) {
349 fprintf(stderr, " Error: invalid NIfTI name %s\n", dbname);
350 dftEmpty(&dft); free(idata);
351 return(21);
352 }
353
354 /* Allocate memory for float data for one frame */
355 if(verbose>1) printf("Allocate memory for one xyz matrix\n");
356 float *fdata;
357 fdata=(float*)calloc(pxlNr, sizeof(float));
358 if(fdata==NULL) {
359 fprintf(stderr, "Error: out of memory.\n");
360 dftEmpty(&dft); free(idata);
361 return(6);
362 }
363
364 /*
365 * Write NIfTI header
366 */
367 if(verbose>1) printf("Writing NIfTI header\n");
368 /* Delete previous NIfTI */
369 /* It does not need to be valid NIfTI format, just that the filenames match */
370 niftiRemove(dbname, IMG_NIFTI_1S, verbose-3);
371 /* Write NIfTI header */
372 {
373 char tmp[256];
374 if(niftiWriteHeader(hdrfile, &dsr, verbose-1, tmp)) {
375 fprintf(stderr, "Error: cannot write header.\n");
376 if(verbose>1) fprintf(stderr, "Status: %s\n", tmp);
377 dftEmpty(&dft); free(fdata); free(idata);
378 return(22);
379 }
380 }
381
382 /*
383 * Write matrix data
384 */
385 if(verbose>1) printf("Writing NIfTI image data\n");
386 FILE *fp=fopen(imgfile, "r+b");
387 if(fp==NULL) {
388 fprintf(stderr, "Error: cannot open %s for write.\n", imgfile);
389 dftEmpty(&dft); free(fdata); free(idata);
390 niftiRemove(dbname, IMG_NIFTI_1S, verbose-3);
391 return(23);
392 }
393 /* Move file pointer to the place of matrix data start */
394 if(fseeko(fp, (size_t)dsr.h.vox_offset, SEEK_SET)!=0) {
395 fprintf(stderr, "Error: invalid file write position.\n");
396 fclose(fp); dftEmpty(&dft); free(fdata); free(idata);
397 niftiRemove(dbname, IMG_NIFTI_1S, verbose-3);
398 return(24);
399 }
400 /* Write frame data */
401 for(int fi=0; fi<dft.frameNr; fi++) {
402 if(verbose>8) printf("Writing frame %d\n", 1+fi);
403 /* Fill matrix data */
404 memset(fdata, 0, pxlNr*sizeof(float));
405 for(size_t i=0; i<pxlNr; i++) {
406 if(idata[i]==0) fdata[i]=0.0;
407 else fdata[i]=dft.voi[idata[i]-1].y[fi];
408 }
409 if(fwrite(fdata, sizeof(float), pxlNr, fp) != pxlNr) {
410 fprintf(stderr, "Error: cannot write image matrix.\n");
411 fclose(fp); dftEmpty(&dft); free(fdata); free(idata);
412 niftiRemove(dbname, IMG_NIFTI_1S, verbose-3);
413 return(25);
414 }
415 }
416 fclose(fp); free(fdata); free(idata);
417 if(verbose>0) printf("written %s\n", imgfile);
418
419
420 /*
421 * Write SIF
422 */
423 if(verbose>1) printf("Making SIF\n");
424 SIF sif; sifInit(&sif);
425 if(sifSetmem(&sif, dft.frameNr)) {
426 fprintf(stderr, "Error: cannot allocate memory for SIF data.\n");
427 dftEmpty(&dft);
428 return(31);
429 }
430 /* copy SIF header */
431 sif.scantime=time(NULL);
432 sif.colNr=4;
433 sif.version=1;
434 strcpy(sif.isotope_name, "Unknown");
435 if(strlen(dft.studynr)>0 && strcmp(dft.studynr, ".")!=0)
437 else
438 strcpy(sif.studynr, "");
439 /* copy frame times */
440 dftFrametimes(&dft);
441 dftTimeunitConversion(&dft, TUNIT_SEC);
442 for(int fi=0; fi<dft.frameNr; fi++) {
443 sif.x1[fi]=dft.x1[fi];
444 sif.x2[fi]=dft.x2[fi];
445 }
446 /* Write SIF */
447 if(sifWrite(&sif, siffile)) {
448 fprintf(stderr, "Error: cannot write %s\n", siffile);
449 dftEmpty(&dft); sifEmpty(&sif);
450 return(32);
451 }
452 sifEmpty(&sif);
453 if(verbose>0) printf("written %s\n", siffile);
454
455
456 dftEmpty(&dft);
457 return(0);
458}
459/*****************************************************************************/
460
461/*****************************************************************************/
463/*****************************************************************************/
464
465/*****************************************************************************/
471 const long long N,
473 const int mxdim,
475 const int mydim,
477 const int mzdim,
479 int *rnx,
481 int *rny,
483 int *rnz,
485 int *rcxdim,
487 int *rcydim,
489 int *rczdim
490) {
491 if(rnx!=NULL) *rnx=0;
492 if(rny!=NULL) *rny=0;
493 if(rnz!=NULL) *rnz=0;
494 if(rcxdim!=NULL) *rcxdim=0;
495 if(rcydim!=NULL) *rcydim=0;
496 if(rczdim!=NULL) *rczdim=0;
497 if(N<1 || mxdim<1 || mydim<1 || mzdim<1) return(1);
498
499 int xd, yd, zd;
500 xd=mxdim; yd=mydim; zd=mzdim;
501
502 long long nx=1, ny=1, nz=1;
503 long long maxn=nx*ny*nz;
504 long long n=N-maxn;
505 while(n>0 && (nz<mzdim || ny<mydim || nx<mxdim)) {
506//printf(" n=%lld nx=%lld ny=%lld nz=%lld\n", n, nx, ny, nz);
507 if(n>0 && nx<mxdim) {
508 nx++; maxn=nx*ny*nz; n=N-maxn;
509 }
510 if(n>0 && ny<mydim) {
511 ny++; maxn=nx*ny*nz; n=N-maxn;
512 }
513 if(n>0 && nz<mzdim) {
514 nz++; maxn=nx*ny*nz; n=N-maxn;
515 }
516 }
517 if(n>0) return(2);
518
519 xd/=nx; yd/=ny; zd/=nz;
520
521 if(rnx!=NULL) *rnx=nx;
522 if(rny!=NULL) *rny=ny;
523 if(rnz!=NULL) *rnz=nz;
524 if(rcxdim!=NULL) *rcxdim=xd;
525 if(rcydim!=NULL) *rcydim=yd;
526 if(rczdim!=NULL) *rczdim=zd;
527 return(0);
528}
529/*****************************************************************************/
530
531/*****************************************************************************/
void dftInit(DFT *data)
Definition dft.c:38
char dfterrmsg[64]
Definition dft.c:6
int dftMinMax(DFT *dft, double *minx, double *maxx, double *miny, double *maxy)
Definition dft.c:974
void dftEmpty(DFT *data)
Definition dft.c:20
void dftFrametimes(DFT *data)
Definition dft.c:340
int dftRead(char *filename, DFT *data)
Definition dftio.c:22
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
Header file for libtpccurveio.
Header file for libtpcimgio.
int sifWrite(SIF *data, char *filename)
Definition sifio.c:145
#define IMG_NIFTI_1S
int niftiWriteHeader(char *filename, NIFTI_DSR *dsr, int verbose, char *status)
Definition nifti.c:844
#define NIFTI_DT_FLOAT
int niftiCreateFNames(const char *filename, char *hdrfile, char *imgfile, char *siffile, int fileformat)
Definition nifti.c:44
void sifInit(SIF *data)
Definition sif.c:17
#define NIFTI_DT_SIGNED_INT
#define NIFTI_HEADER_SIZE
int sifSetmem(SIF *data, int frameNr)
Definition sif.c:56
#define NIFTI_INTENT_NONE
void sifEmpty(SIF *data)
Definition sif.c:33
int niftiRemove(const char *dbname, int fileformat, int verbose)
Definition nifti.c:100
#define NIFTI_UNITS_SEC
#define NIFTI_UNITS_MM
Header file for libtpcimgp.
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 little_endian()
Definition swap.c:14
#define MAX_STUDYNR_LEN
Definition libtpcmisc.h:163
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
Header file for libtpcroi.
Voi * voi
char studynr[MAX_STUDYNR_LEN+1]
double * x1
int voiNr
double * x2
int frameNr
short int qform_code
short int slice_start
short int slice_end
short int datatype
char aux_file[24]
char intent_name[16]
short int bitpix
char data_type[10]
short int sform_code
short int dim[8]
short int intent_code
NIFTI_1_HEADER h
NIFTI_EXTENDER e
double * x1
double * x2
int version
char studynr[MAX_STUDYNR_LEN+1]
time_t scantime
char isotope_name[8]
int colNr
double * y
char name[MAX_REGIONNAME_LEN+1]
int simRCDims(const long long N, const int mxdim, const int mydim, const int mzdim, int *rnx, int *rny, int *rnz, int *rcxdim, int *rcydim, int *rczdim)
Definition tac2nii.c:469