TPCCLIB
Loading...
Searching...
No Matches
img_mtga.c
Go to the documentation of this file.
1
6/*****************************************************************************/
7
8/*****************************************************************************/
9#include "libtpcmodext.h"
10/*****************************************************************************/
11
12/*****************************************************************************/
20 DFT *input,
23 IMG *dyn_img,
26 int start,
29 int end,
32 linefit_range fit_range,
34 float thrs,
36 IMG *ki_img,
39 IMG *ic_img,
42 IMG *nr_img,
45 char *status,
47 int verbose
48) {
49 int zi, yi, xi, fi, nr, ret=0;
50 DFT tac;
51 double *plotData, *xaxis, *yaxis, slope, ic, f;
52
53
54 if(verbose>0) {
55 printf("%s(input, dyn_img, %d, %d, range, %g, ki_img, ", __func__, start, end, thrs);
56 if(ic_img==NULL) printf("NULL, "); else printf("ic_img, ");
57 if(nr_img==NULL) printf("NULL, "); else printf("nr_img, ");
58 if(status==NULL) printf("NULL)\n"); else printf("status)\n");
59 }
60 /* Initial check for the arguments */
61 if(status!=NULL) sprintf(status, "invalid data");
62 if(dyn_img->status!=IMG_STATUS_OCCUPIED || dyn_img->dimt<1) return(1);
63 if(input==NULL || input->frameNr<1) return(2);
64 nr=1+end-start; if(nr<2) return(3);
65 if(end>dyn_img->dimt-1 || start<0) return(4);
66 if(ki_img==NULL) return(5);
67 /* Convert input time units to min */
68 if(input->timeunit==TUNIT_SEC) dftTimeunitConversion(input, TUNIT_MIN);
69
70 /* Check that input contains samples until at least 80% of line fit range */
71 if(verbose>1) {
72 printf("input_last_sample_time := %g\n", input->x[input->frameNr-1]);
73 printf("patlak_start_time := %g\n", dyn_img->start[start]/60.0);
74 printf("patlak_end_time := %g\n", dyn_img->end[end]/60.0);
75 }
76 if(input->x[input->frameNr-1] < (0.2*dyn_img->mid[start]+0.8*dyn_img->mid[end])/60.0) {
77 sprintf(status, "too few input samples"); return(6);
78 }
79
80 /* Allocate memory for interpolated and integrated input and
81 tissue pixel TACs */
82 dftInit(&tac); if(dftSetmem(&tac, nr, 2)!=0) {sprintf(status, "out of memory"); return(11);}
83 strcpy(tac.voi[0].voiname, "input"); strcpy(tac.voi[1].voiname, "tissue");
84 tac.voiNr=2; tac.frameNr=nr;
85 for(fi=0; fi<tac.frameNr; fi++) {
86 tac.x1[fi]=dyn_img->start[start+fi]/60.;
87 tac.x2[fi]=dyn_img->end[start+fi]/60.;
88 tac.x[fi]=dyn_img->mid[start+fi]/60.;
89 }
90
91 /* If input sample times are much different from PET frame times,
92 as they usually are if arterial plasma is used as input, then use
93 interpolate4pet(), otherwise with image-derived input use petintegral()
94 */
95 int equal_times;
96 equal_times=check_times_dft_vs_img(dyn_img, input, verbose-1);
97 if(equal_times==1) {
98 if(verbose>1) printf("copying input curve and using petintegral()\n");
99 /* Copy frame start and end times from image to input data */
100 ret=copy_times_from_img_to_dft(dyn_img, input, verbose-1);
101 if(ret) {
102 sprintf(status, "cannot set input sample times");
103 dftEmpty(&tac); return(12);
104 }
105 /* integral must be calculated from the zero time */
106 ret=petintegral(input->x1, input->x2, input->voi[0].y, input->frameNr,
107 input->voi[0].y2, input->voi[0].y3);
108 /* because times are the same, the values can be copied directly */
109 if(ret==0) for(fi=0; fi<tac.frameNr; fi++) {
110 tac.voi[0].y[fi]=input->voi[0].y[start+fi];
111 tac.voi[0].y2[fi]=input->voi[0].y2[start+fi];
112 tac.voi[0].y3[fi]=input->voi[0].y3[start+fi];
113 }
114 } else {
115 if(verbose>1) printf("using interpolate4pet() for input curve\n");
116 ret=interpolate4pet(
117 input->x, input->voi[0].y, input->frameNr,
118 tac.x1, tac.x2, tac.voi[0].y, tac.voi[0].y2, tac.voi[0].y3, tac.frameNr
119 );
120 }
121 if(ret) {
122 dftEmpty(&tac);
123 sprintf(status, "cannot interpolate input data"); return(12);
124 }
125 if(verbose>3) dftPrint(&tac);
126
127 /*
128 * Allocate result images and fill the header info
129 */
130 /* Ki image */
131 imgEmpty(ki_img);
132 ret=imgAllocateWithHeader(ki_img, dyn_img->dimz, dyn_img->dimy, dyn_img->dimx, 1, dyn_img);
133 if(ret) {
134 sprintf(status, "cannot setup memory for Ki image");
135 imgEmpty(ki_img); dftEmpty(&tac); return(21);
136 }
137 ki_img->unit=CUNIT_ML_PER_ML_PER_MIN; /* mL/(mL*min) */
139 ki_img->start[0]=dyn_img->start[start]; ki_img->end[0]=dyn_img->end[end];
140 if(verbose>9) imgInfo(ki_img);
141 /* Ic image */
142 if(ic_img!=NULL) {
143 imgEmpty(ic_img);
144 ret=imgAllocateWithHeader(ic_img, dyn_img->dimz, dyn_img->dimy, dyn_img->dimx, 1, dyn_img);
145 if(ret) {
146 sprintf(status, "cannot setup memory for Ic image");
147 imgEmpty(ic_img); imgEmpty(ki_img); dftEmpty(&tac); return(23);
148 }
149 ic_img->unit=CUNIT_ML_PER_ML; /* mL/mL */
151 ic_img->start[0]=dyn_img->start[start]; ic_img->end[0]=dyn_img->end[end];
152 if(verbose>9) imgInfo(ic_img);
153 }
154 /* Nr image */
155 if(nr_img!=NULL) {
156 imgEmpty(nr_img);
157 ret=imgAllocateWithHeader(nr_img, dyn_img->dimz, dyn_img->dimy, dyn_img->dimx, 1, dyn_img);
158 if(ret) {
159 sprintf(status, "cannot setup memory for nr image");
160 imgEmpty(nr_img); imgEmpty(ic_img); imgEmpty(ki_img); dftEmpty(&tac);
161 return(25);
162 }
163 nr_img->unit=CUNIT_UNITLESS;
165 nr_img->start[0]=dyn_img->start[start]; nr_img->end[0]=dyn_img->end[end];
166 if(verbose>9) imgInfo(ic_img);
167 }
168
169 /* Allocate memory for graphical analysis plot data */
170 plotData=malloc(2*nr*sizeof(double));
171 if(plotData==NULL) {
172 sprintf(status, "cannot allocate memory for plots");
173 imgEmpty(ic_img); imgEmpty(ki_img); dftEmpty(&tac); return(25);
174 }
175 xaxis=plotData; yaxis=plotData+nr;
176
177 /* Calculate threshold */
178 thrs*=tac.voi[0].y2[tac.frameNr-1];
179 if(verbose>1) printf(" threshold-AUC := %g\n", thrs);
180
181 /*
182 * Compute pixel-by-pixel
183 */
184 if(verbose>1) printf("computing MTGA pixel-by-pixel\n");
185 float pxlauc[dyn_img->dimt];
186 int best_nr;
187 for(zi=0; zi<dyn_img->dimz; zi++) {
188 for(yi=0; yi<dyn_img->dimy; yi++) {
189 for(xi=0; xi<dyn_img->dimx; xi++) {
190 /* Initiate pixel output values */
191 ki_img->m[zi][yi][xi][0]=0.0;
192 if(ic_img!=NULL) ic_img->m[zi][yi][xi][0]=0.0;
193 if(nr_img!=NULL) nr_img->m[zi][yi][xi][0]=0;
194 /* Check for threshold */
195 ret=fpetintegral(dyn_img->start, dyn_img->end, dyn_img->m[zi][yi][xi],
196 dyn_img->dimt, pxlauc, NULL);
197 if(ret) continue;
198 if((pxlauc[dyn_img->dimt-1]/60.0) < thrs) continue;
199 /* Calculate Patlak plot data */
200 for(fi=0; fi<tac.frameNr; fi++) tac.voi[1].y[fi]=dyn_img->m[zi][yi][xi][start+fi];
201 int pn=patlak_data(nr, tac.voi[0].y, tac.voi[0].y2, tac.voi[1].y, xaxis, yaxis);
202 /* Line fit */
203 if(fit_range==PRESET || pn<MTGA_BEST_MIN_NR) {
204 ret=llsqperp(xaxis, yaxis, pn, &slope, &ic, &f);
205 best_nr=pn;
206 } else {
207 ret=mtga_best_perp(xaxis, yaxis, pn, &slope, &ic, &f, &best_nr);
208 }
209 if(ret==0) {
210 ki_img->m[zi][yi][xi][0]=slope;
211 if(ic_img!=NULL) ic_img->m[zi][yi][xi][0]=ic;
212 if(nr_img!=NULL) nr_img->m[zi][yi][xi][0]=best_nr;
213 }
214 } /* next column */
215 } /* next row */
216 } /* next plane */
217
218 free(plotData); dftEmpty(&tac);
219 return(0);
220}
221/*****************************************************************************/
222
223/*****************************************************************************/
231 DFT *input,
234 IMG *dyn_img,
237 int start,
240 int end,
243 linefit_range fit_range,
245 float thrs,
247 double k2,
249 IMG *vt_img,
252 IMG *ic_img,
255 IMG *nr_img,
258 char *status,
260 int verbose
261) {
262 int zi, yi, xi, fi, nr, ret=0;
263 DFT tac;
264 double *plotData, *xaxis, *yaxis, slope, ic, f;
265 double aucrat;
266
267
268 if(verbose>0) {
269 printf("img_logan(%s, dyn_img, %d, %d, %g, %g, ki_img, ", __func__, start, end, thrs, k2);
270 if(ic_img==NULL) printf("NULL, "); else printf("ic_img, ");
271 if(nr_img==NULL) printf("NULL, "); else printf("nr_img, ");
272 if(status==NULL) printf("NULL)\n"); else printf("status)\n");
273 }
274 /* Initial check for the arguments */
275 if(status!=NULL) sprintf(status, "invalid data");
276 if(dyn_img->status!=IMG_STATUS_OCCUPIED || dyn_img->dimt<1) return(1);
277 if(input==NULL || input->frameNr<1) return(2);
278 nr=1+end-start; if(nr<2) return(3);
279 if(end>dyn_img->dimt-1 || start<0) return(4);
280 if(vt_img==NULL) return(5);
281 /* Convert input time units to min */
282 if(input->timeunit==TUNIT_SEC) dftTimeunitConversion(input, TUNIT_MIN);
283
284 /* Check that input contains samples until at least 80% of line fit range */
285 if(verbose>1) {
286 printf("input_last_sample_time := %g\n", input->x[input->frameNr-1]);
287 printf("logan_start_time := %g\n", dyn_img->start[start]/60.0);
288 printf("logan_end_time := %g\n", dyn_img->end[end]/60.0);
289 }
290 if(input->x[input->frameNr-1] < (0.2*dyn_img->mid[start]+0.8*dyn_img->mid[end])/60.0) {
291 sprintf(status, "too few input samples"); return(6);
292 }
293
294 /* Allocate memory for interpolated and integrated input and
295 tissue pixel TACs */
296 dftInit(&tac); if(dftSetmem(&tac, nr, 2)!=0) {sprintf(status, "out of memory"); return(11);}
297 strcpy(tac.voi[0].voiname, "input"); strcpy(tac.voi[1].voiname, "tissue");
298 tac.voiNr=2; tac.frameNr=nr;
299 for(int fi=0; fi<tac.frameNr; fi++) {
300 tac.x1[fi]=dyn_img->start[start+fi]/60.;
301 tac.x2[fi]=dyn_img->end[start+fi]/60.;
302 tac.x[fi]=dyn_img->mid[start+fi]/60.;
303 }
304
305 /* If input sample times are much different from PET frame times,
306 as they usually are if arterial plasma is used as input, then use
307 interpolate4pet(), otherwise with image-derived input use petintegral()
308 */
309 int equal_times;
310 equal_times=check_times_dft_vs_img(dyn_img, input, verbose-1);
311 if(equal_times==1) {
312 if(verbose>1) printf("copying input curve and using petintegral()\n");
313 /* Copy frame start and end times from image to input data */
314 ret=copy_times_from_img_to_dft(dyn_img, input, verbose-1);
315 if(ret) {
316 sprintf(status, "cannot set input sample times");
317 dftEmpty(&tac); return(12);
318 }
319 /* integral must be calculated from the zero time */
320 ret=petintegral(input->x1, input->x2, input->voi[0].y, input->frameNr,
321 input->voi[0].y2, input->voi[0].y3);
322 /* because times are the same, the values can be copied directly */
323 if(ret==0) for(fi=0; fi<tac.frameNr; fi++) {
324 tac.voi[0].y[fi]=input->voi[0].y[start+fi];
325 tac.voi[0].y2[fi]=input->voi[0].y2[start+fi];
326 tac.voi[0].y3[fi]=input->voi[0].y3[start+fi];
327 }
328 } else {
329 if(verbose>1) printf("using interpolate4pet() for input curve\n");
330 ret=interpolate4pet(
331 input->x, input->voi[0].y, input->frameNr,
332 tac.x1, tac.x2, tac.voi[0].y, tac.voi[0].y2, tac.voi[0].y3, tac.frameNr
333 );
334 }
335 if(ret) {
336 dftEmpty(&tac);
337 sprintf(status, "cannot interpolate input data"); return(12);
338 }
339 if(verbose>3) dftPrint(&tac);
340
341 /*
342 * Allocate result images and fill the header info
343 */
344 /* Vt image */
345 imgEmpty(vt_img);
346 ret=imgAllocateWithHeader(vt_img, dyn_img->dimz, dyn_img->dimy, dyn_img->dimx, 1, dyn_img);
347 if(ret) {
348 sprintf(status, "cannot setup memory for Vt image");
349 imgEmpty(vt_img); dftEmpty(&tac); return(21);
350 }
351 vt_img->unit=CUNIT_ML_PER_ML;
353 vt_img->start[0]=dyn_img->start[start]; vt_img->end[0]=dyn_img->end[end];
354 if(verbose>9) imgInfo(vt_img);
355 /* Ic image */
356 if(ic_img!=NULL) {
357 imgEmpty(ic_img);
358 ret=imgAllocateWithHeader(ic_img, dyn_img->dimz, dyn_img->dimy, dyn_img->dimx, 1, dyn_img);
359 if(ret) {
360 sprintf(status, "cannot setup memory for Ic image");
361 imgEmpty(ic_img); imgEmpty(vt_img); dftEmpty(&tac); return(23);
362 }
363 ic_img->unit=CUNIT_UNITLESS; /* mL/mL */
365 ic_img->start[0]=dyn_img->start[start]; ic_img->end[0]=dyn_img->end[end];
366 if(verbose>9) imgInfo(ic_img);
367 }
368 /* Nr image */
369 if(nr_img!=NULL) {
370 imgEmpty(nr_img);
371 ret=imgAllocateWithHeader(nr_img, dyn_img->dimz, dyn_img->dimy, dyn_img->dimx, 1, dyn_img);
372 if(ret) {
373 sprintf(status, "cannot setup memory for nr image");
374 imgEmpty(nr_img); imgEmpty(ic_img); imgEmpty(vt_img); dftEmpty(&tac);
375 return(25);
376 }
377 nr_img->unit=CUNIT_UNITLESS;
379 nr_img->start[0]=dyn_img->start[start]; nr_img->end[0]=dyn_img->end[end];
380 if(verbose>9) imgInfo(ic_img);
381 }
382
383 /* Allocate memory for graphical analysis plot data */
384 plotData=malloc(2*nr*sizeof(double));
385 if(plotData==NULL) {
386 sprintf(status, "cannot allocate memory for plots");
387 imgEmpty(ic_img); imgEmpty(vt_img); dftEmpty(&tac);
388 return(25);
389 }
390 xaxis=plotData; yaxis=plotData+nr;
391 float pxlauc[dyn_img->dimt];
392
393 /* Calculate threshold */
394 thrs*=tac.voi[0].y2[tac.frameNr-1];
395 if(verbose>1) printf(" threshold-AUC := %g\n", thrs);
396
397 /*
398 * Compute pixel-by-pixel
399 */
400 if(verbose>1) printf("computing MTGA pixel-by-pixel\n");
401 int best_nr;
402 for(zi=0; zi<dyn_img->dimz; zi++) {
403 for(yi=0; yi<dyn_img->dimy; yi++) {
404 for(xi=0; xi<dyn_img->dimx; xi++) {
405 /* Initiate pixel output values */
406 vt_img->m[zi][yi][xi][0]=0.0;
407 if(ic_img!=NULL) ic_img->m[zi][yi][xi][0]=0.0;
408 if(nr_img!=NULL) nr_img->m[zi][yi][xi][0]=0.0;
409 /* Copy TTAC values */
410 for(fi=0; fi<tac.frameNr; fi++) tac.voi[1].y[fi]=dyn_img->m[zi][yi][xi][start+fi];
411 /* Compute TTAC AUC(0-t) and check for threshold */
412 ret=fpetintegral(dyn_img->start, dyn_img->end, dyn_img->m[zi][yi][xi],
413 dyn_img->dimt, pxlauc, NULL);
414 if(ret) continue;
415 if((pxlauc[dyn_img->dimt-1]/60.0) < thrs) continue;
416 for(fi=0; fi<tac.frameNr; fi++)
417 tac.voi[1].y2[fi]=pxlauc[start+fi]/60.0; // conc*sec -> conc*min
418 /* Calculate Logan plot data */
419 int pn=logan_data(nr, tac.voi[0].y, tac.voi[0].y2, tac.voi[1].y, tac.voi[1].y2, k2, xaxis, yaxis);
420 /* Line fit */
421 if(fit_range==PRESET || pn<MTGA_BEST_MIN_NR) {
422 ret=llsqperp(xaxis, yaxis, pn, &slope, &ic, &f);
423 best_nr=pn;
424 } else {
425 ret=mtga_best_perp(xaxis, yaxis, pn, &slope, &ic, &f, &best_nr);
426 }
427 if(ret!=0) continue; // line fit failed
428 /* Use 10xAUCratio as upper limit to prevent image where only
429 noise-induced hot spots can be seen */
430 aucrat=tac.voi[1].y2[tac.frameNr-1]/tac.voi[0].y2[tac.frameNr-1];
431 if(slope>10.0*aucrat) {
432 if(verbose>50) printf("%g > 10 x %g\n", slope, aucrat);
433 slope=10.0*aucrat;
434 }
435 /* copy result to pixels */
436 vt_img->m[zi][yi][xi][0]=slope;
437 if(ic_img!=NULL) ic_img->m[zi][yi][xi][0]=-ic;
438 if(nr_img!=NULL) nr_img->m[zi][yi][xi][0]=best_nr;
439 } /* next column */
440 } /* next row */
441 } /* next plane */
442
443 free(plotData); dftEmpty(&tac);
444 return(0);
445}
446/*****************************************************************************/
447
448/*****************************************************************************/
void dftInit(DFT *data)
Definition dft.c:38
int dftSetmem(DFT *data, int frameNr, int voiNr)
Definition dft.c:57
void dftEmpty(DFT *data)
Definition dft.c:20
void dftPrint(DFT *data)
Definition dftio.c:538
int dftTimeunitConversion(DFT *dft, int tunit)
Definition dftunit.c:119
int copy_times_from_img_to_dft(IMG *img, DFT *dft, int verbose)
Definition fittime.c:246
int check_times_dft_vs_img(IMG *img, DFT *dft, int verbose)
Definition fittime.c:109
void imgInfo(IMG *image)
Definition img.c:359
int imgAllocateWithHeader(IMG *image, int planes, int rows, int columns, int frames, IMG *image_from)
Definition img.c:279
void imgEmpty(IMG *image)
Definition img.c:121
int img_patlak(DFT *input, IMG *dyn_img, int start, int end, linefit_range fit_range, float thrs, IMG *ki_img, IMG *ic_img, IMG *nr_img, char *status, int verbose)
Definition img_mtga.c:17
int img_logan(DFT *input, IMG *dyn_img, int start, int end, linefit_range fit_range, float thrs, double k2, IMG *vt_img, IMG *ic_img, IMG *nr_img, char *status, int verbose)
Definition img_mtga.c:228
int petintegral(double *x1, double *x2, double *y, int nr, double *ie, double *iie)
Integrate PET TAC data to frame mid times.
Definition integr.c:771
int fpetintegral(float *x1, float *x2, float *y, int nr, float *ie, float *iie)
Integrate PET TAC data to frame mid times. Float version of petintegral().
Definition integr.c:838
int interpolate4pet(double *x, double *y, int nr, double *newx1, double *newx2, double *newy, double *newyi, double *newyii, int newnr)
Interpolate and integrate TAC to PET frames.
Definition integr.c:510
#define IMG_STATUS_OCCUPIED
#define IMG_DC_NONCORRECTED
int llsqperp(double *x, double *y, int nr, double *slope, double *ic, double *ssd)
Definition llsqwt.c:382
#define MTGA_BEST_MIN_NR
int patlak_data(int data_nr, double *i, double *ii, double *c, double *x, double *y)
Definition mtga.c:22
linefit_range
int mtga_best_perp(double *x, double *y, int nr, double *slope, double *ic, double *ssd, int *fnr)
Definition mtga.c:141
int logan_data(int data_nr, double *i, double *ii, double *c, double *ci, double k2, double *x, double *y)
Definition mtga.c:81
Header file for libtpcmodext.
Voi * voi
int timeunit
double * x1
int voiNr
double * x2
int frameNr
double * x
unsigned short int dimx
float **** m
char decayCorrection
char unit
char status
unsigned short int dimt
float * start
unsigned short int dimz
unsigned short int dimy
float * end
float * mid
char isWeight
double * y2
char voiname[MAX_REGIONSUBNAME_LEN+1]
double * y
double * y3