rapidtide 3.0.11__py3-none-any.whl → 3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. rapidtide/Colortables.py +492 -27
  2. rapidtide/OrthoImageItem.py +1049 -46
  3. rapidtide/RapidtideDataset.py +1533 -86
  4. rapidtide/_version.py +3 -3
  5. rapidtide/calccoherence.py +196 -29
  6. rapidtide/calcnullsimfunc.py +191 -40
  7. rapidtide/calcsimfunc.py +245 -42
  8. rapidtide/correlate.py +1210 -393
  9. rapidtide/data/examples/src/testLD +56 -0
  10. rapidtide/data/examples/src/testalign +1 -1
  11. rapidtide/data/examples/src/testdelayvar +0 -1
  12. rapidtide/data/examples/src/testfmri +19 -1
  13. rapidtide/data/examples/src/testglmfilt +5 -5
  14. rapidtide/data/examples/src/testhappy +25 -3
  15. rapidtide/data/examples/src/testppgproc +17 -0
  16. rapidtide/data/examples/src/testrolloff +11 -0
  17. rapidtide/data/models/model_cnn_pytorch/best_model.pth +0 -0
  18. rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
  19. rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
  20. rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
  21. rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
  22. rapidtide/decorators.py +91 -0
  23. rapidtide/dlfilter.py +2225 -108
  24. rapidtide/dlfiltertorch.py +4843 -0
  25. rapidtide/externaltools.py +327 -12
  26. rapidtide/fMRIData_class.py +79 -40
  27. rapidtide/filter.py +1899 -810
  28. rapidtide/fit.py +2004 -574
  29. rapidtide/genericmultiproc.py +93 -18
  30. rapidtide/happy_supportfuncs.py +2044 -171
  31. rapidtide/helper_classes.py +584 -43
  32. rapidtide/io.py +2363 -370
  33. rapidtide/linfitfiltpass.py +341 -75
  34. rapidtide/makelaggedtcs.py +211 -20
  35. rapidtide/maskutil.py +423 -53
  36. rapidtide/miscmath.py +827 -121
  37. rapidtide/multiproc.py +210 -22
  38. rapidtide/patchmatch.py +234 -33
  39. rapidtide/peakeval.py +32 -30
  40. rapidtide/ppgproc.py +2203 -0
  41. rapidtide/qualitycheck.py +352 -39
  42. rapidtide/refinedelay.py +422 -57
  43. rapidtide/refineregressor.py +498 -184
  44. rapidtide/resample.py +671 -185
  45. rapidtide/scripts/applyppgproc.py +28 -0
  46. rapidtide/simFuncClasses.py +1052 -77
  47. rapidtide/simfuncfit.py +260 -46
  48. rapidtide/stats.py +540 -238
  49. rapidtide/tests/happycomp +9 -0
  50. rapidtide/tests/test_dlfiltertorch.py +627 -0
  51. rapidtide/tests/test_findmaxlag.py +24 -8
  52. rapidtide/tests/test_fullrunhappy_v1.py +0 -2
  53. rapidtide/tests/test_fullrunhappy_v2.py +0 -2
  54. rapidtide/tests/test_fullrunhappy_v3.py +1 -0
  55. rapidtide/tests/test_fullrunhappy_v4.py +2 -2
  56. rapidtide/tests/test_fullrunrapidtide_v7.py +1 -1
  57. rapidtide/tests/test_simroundtrip.py +8 -8
  58. rapidtide/tests/utils.py +9 -8
  59. rapidtide/tidepoolTemplate.py +142 -38
  60. rapidtide/tidepoolTemplate_alt.py +165 -44
  61. rapidtide/tidepoolTemplate_big.py +189 -52
  62. rapidtide/util.py +1217 -118
  63. rapidtide/voxelData.py +684 -37
  64. rapidtide/wiener.py +19 -12
  65. rapidtide/wiener2.py +113 -7
  66. rapidtide/wiener_doc.py +255 -0
  67. rapidtide/workflows/adjustoffset.py +105 -3
  68. rapidtide/workflows/aligntcs.py +85 -2
  69. rapidtide/workflows/applydlfilter.py +87 -10
  70. rapidtide/workflows/applyppgproc.py +522 -0
  71. rapidtide/workflows/atlasaverage.py +210 -47
  72. rapidtide/workflows/atlastool.py +100 -3
  73. rapidtide/workflows/calcSimFuncMap.py +294 -64
  74. rapidtide/workflows/calctexticc.py +201 -9
  75. rapidtide/workflows/ccorrica.py +97 -4
  76. rapidtide/workflows/cleanregressor.py +168 -29
  77. rapidtide/workflows/delayvar.py +163 -10
  78. rapidtide/workflows/diffrois.py +81 -3
  79. rapidtide/workflows/endtidalproc.py +144 -4
  80. rapidtide/workflows/fdica.py +195 -15
  81. rapidtide/workflows/filtnifti.py +70 -3
  82. rapidtide/workflows/filttc.py +74 -3
  83. rapidtide/workflows/fitSimFuncMap.py +206 -48
  84. rapidtide/workflows/fixtr.py +73 -3
  85. rapidtide/workflows/gmscalc.py +113 -3
  86. rapidtide/workflows/happy.py +801 -199
  87. rapidtide/workflows/happy2std.py +144 -12
  88. rapidtide/workflows/happy_parser.py +138 -9
  89. rapidtide/workflows/histnifti.py +118 -2
  90. rapidtide/workflows/histtc.py +84 -3
  91. rapidtide/workflows/linfitfilt.py +117 -4
  92. rapidtide/workflows/localflow.py +328 -28
  93. rapidtide/workflows/mergequality.py +79 -3
  94. rapidtide/workflows/niftidecomp.py +322 -18
  95. rapidtide/workflows/niftistats.py +174 -4
  96. rapidtide/workflows/pairproc.py +88 -2
  97. rapidtide/workflows/pairwisemergenifti.py +85 -2
  98. rapidtide/workflows/parser_funcs.py +1421 -40
  99. rapidtide/workflows/physiofreq.py +137 -11
  100. rapidtide/workflows/pixelcomp.py +208 -5
  101. rapidtide/workflows/plethquality.py +103 -21
  102. rapidtide/workflows/polyfitim.py +151 -11
  103. rapidtide/workflows/proj2flow.py +75 -2
  104. rapidtide/workflows/rankimage.py +111 -4
  105. rapidtide/workflows/rapidtide.py +272 -15
  106. rapidtide/workflows/rapidtide2std.py +98 -2
  107. rapidtide/workflows/rapidtide_parser.py +109 -9
  108. rapidtide/workflows/refineDelayMap.py +143 -33
  109. rapidtide/workflows/refineRegressor.py +682 -93
  110. rapidtide/workflows/regressfrommaps.py +152 -31
  111. rapidtide/workflows/resamplenifti.py +85 -3
  112. rapidtide/workflows/resampletc.py +91 -3
  113. rapidtide/workflows/retrolagtcs.py +98 -6
  114. rapidtide/workflows/retroregress.py +165 -9
  115. rapidtide/workflows/roisummarize.py +173 -5
  116. rapidtide/workflows/runqualitycheck.py +71 -3
  117. rapidtide/workflows/showarbcorr.py +147 -4
  118. rapidtide/workflows/showhist.py +86 -2
  119. rapidtide/workflows/showstxcorr.py +160 -3
  120. rapidtide/workflows/showtc.py +159 -3
  121. rapidtide/workflows/showxcorrx.py +184 -4
  122. rapidtide/workflows/showxy.py +185 -15
  123. rapidtide/workflows/simdata.py +262 -36
  124. rapidtide/workflows/spatialfit.py +77 -2
  125. rapidtide/workflows/spatialmi.py +251 -27
  126. rapidtide/workflows/spectrogram.py +305 -32
  127. rapidtide/workflows/synthASL.py +154 -3
  128. rapidtide/workflows/tcfrom2col.py +76 -2
  129. rapidtide/workflows/tcfrom3col.py +74 -2
  130. rapidtide/workflows/tidepool.py +2969 -130
  131. rapidtide/workflows/utils.py +19 -14
  132. rapidtide/workflows/utils_doc.py +293 -0
  133. rapidtide/workflows/variabilityizer.py +116 -3
  134. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/METADATA +3 -2
  135. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/RECORD +139 -122
  136. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/entry_points.txt +1 -0
  137. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/WHEEL +0 -0
  138. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/licenses/LICENSE +0 -0
  139. {rapidtide-3.0.11.dist-info → rapidtide-3.1.dist-info}/top_level.txt +0 -0
@@ -22,8 +22,10 @@ import sys
22
22
  import time
23
23
  import warnings
24
24
  from pathlib import Path
25
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
25
26
 
26
27
  import numpy as np
28
+ from numpy.typing import NDArray
27
29
 
28
30
  import rapidtide.correlate as tide_corr
29
31
  import rapidtide.filter as tide_filt
@@ -49,23 +51,104 @@ try:
49
51
  except ImportError:
50
52
  mklexists = False
51
53
 
52
- try:
53
- import rapidtide.dlfilter as tide_dlfilt
54
-
55
- dlfilterexists = True
56
- print("dlfilter exists")
57
- except ImportError:
58
- dlfilterexists = False
59
- print("dlfilter does not exist")
60
54
 
61
-
62
- def happy_main(argparsingfunc):
55
+ def happy_main(argparsingfunc: Any) -> None:
56
+ """
57
+ Process fMRI data to remove cardiac noise using temporal and/or spatial regression.
58
+
59
+ This function performs cardiac noise regression on fMRI data using either
60
+ temporal or spatial regression methods. It calculates cardiac noise signals
61
+ from the cardiac phase information and removes them from the fMRI data.
62
+
63
+ Parameters
64
+ ----------
65
+ args : argparse.Namespace
66
+ Command line arguments containing processing options
67
+ infodict : dict
68
+ Dictionary containing processing information including shared memory settings
69
+ fmri_data : numpy.ndarray
70
+ 4D fMRI data array (x, y, z, time)
71
+ mask : numpy.ndarray
72
+ 3D mask array indicating valid voxels
73
+ projmask_byslice : numpy.ndarray
74
+ 2D mask array for each slice indicating valid projection locations
75
+ cardphasevals : numpy.ndarray
76
+ 2D array of cardiac phase values for each slice and timepoint
77
+ rawapp_byslice : numpy.ndarray
78
+ 3D array of raw cardiac signals for each slice and timepoint
79
+ outphases : numpy.ndarray
80
+ Array of cardiac phases
81
+ timepoints : int
82
+ Number of timepoints in the fMRI data
83
+ xsize : int
84
+ X dimension of the fMRI data
85
+ ysize : int
86
+ Y dimension of the fMRI data
87
+ numslices : int
88
+ Number of slices in the fMRI data
89
+ outputroot : str
90
+ Root name for output files
91
+ tide_util : module
92
+ Utility module for memory management and other operations
93
+ tide_io : module
94
+ I/O module for reading/writing data
95
+ tide_linfitfiltpass : module
96
+ Linear fitting and filtering module
97
+ tide_filt : module
98
+ Filtering module
99
+ platform : module
100
+ Platform information module
101
+ Path : class
102
+ Path class from pathlib module
103
+
104
+ Returns
105
+ -------
106
+ None
107
+ Function performs in-place processing and saves results to files
108
+
109
+ Notes
110
+ -----
111
+ This function performs multiple steps:
112
+ 1. Calculates cardiac noise signals from cardiac phase information
113
+ 2. Applies temporal or spatial regression to remove cardiac noise
114
+ 3. Saves filtered data and regression coefficients
115
+ 4. Handles shared memory cleanup when needed
116
+
117
+ Examples
118
+ --------
119
+ >>> process_fmri_with_cardiac_regression(
120
+ ... args, infodict, fmri_data, mask, projmask_byslice,
121
+ ... cardphasevals, rawapp_byslice, outphases,
122
+ ... timepoints, xsize, ysize, numslices,
123
+ ... outputroot, tide_util, tide_io, tide_linfitfiltpass,
124
+ ... tide_filt, platform, Path
125
+ ... )
126
+ """
63
127
  timings = [["Start", time.time(), None, None]]
64
128
 
65
129
  # get the command line parameters
66
130
  args = argparsingfunc
67
131
  infodict = vars(args)
68
132
 
133
+ if args.usepytorch:
134
+ try:
135
+ import rapidtide.dlfiltertorch as tide_dlfilt
136
+
137
+ dlfilterexists = True
138
+ print("pytorch dlfilter initialized")
139
+ except ImportError:
140
+ dlfilterexists = False
141
+ print("pytorch dlfilter could not be initialized")
142
+ else:
143
+ try:
144
+ import rapidtide.dlfilter as tide_dlfilt
145
+
146
+ dlfilterexists = True
147
+ print("tensorflow dlfilter initialized")
148
+ except ImportError:
149
+ dlfilterexists = False
150
+ print("tensorflow dlfilter could not be initialized")
151
+
69
152
  fmrifilename = args.fmrifilename
70
153
  slicetimename = args.slicetimename
71
154
  outputroot = args.outputroot
@@ -92,7 +175,7 @@ def happy_main(argparsingfunc):
92
175
  logger_filename=f"{outputroot}_log.txt",
93
176
  timing_filename=f"{outputroot}_runtimings.tsv",
94
177
  memory_filename=f"{outputroot}_memusage.tsv",
95
- verbose=args.verbose,
178
+ isverbose=args.verbose,
96
179
  debug=args.debug,
97
180
  )
98
181
 
@@ -113,6 +196,8 @@ def happy_main(argparsingfunc):
113
196
  "***********************************************************************************************************************************")
114
197
  print("")"""
115
198
 
199
+ infodict["pid"] = os.getpid()
200
+
116
201
  infodict["fmrifilename"] = fmrifilename
117
202
  infodict["slicetimename"] = slicetimename
118
203
  infodict["outputroot"] = outputroot
@@ -139,6 +224,12 @@ def happy_main(argparsingfunc):
139
224
  "CommandLineArgs": args.commandline,
140
225
  }
141
226
 
227
+ # decide if we need to use shared memory
228
+ if args.nprocs > 1:
229
+ infodict["sharedmem"] = True
230
+ else:
231
+ infodict["sharedmem"] = False
232
+
142
233
  # save the information file
143
234
  if args.saveinfoasjson:
144
235
  tide_io.writedicttojson(infodict, outputroot + "_info.json")
@@ -199,6 +290,7 @@ def happy_main(argparsingfunc):
199
290
  args.processmask,
200
291
  input_data.copyheader(numtimepoints=1),
201
292
  xsize,
293
+ thresh=0.001,
202
294
  maskname="process",
203
295
  debug=args.debug,
204
296
  )
@@ -209,10 +301,25 @@ def happy_main(argparsingfunc):
209
301
  theheader = input_data.copyheader(numtimepoints=1)
210
302
  timings.append(["Mask created", time.time(), None, None])
211
303
  if args.outputlevel > 0:
212
- maskfilename = outputroot + "_desc-processvoxels_mask"
213
304
  bidsdict = bidsbasedict.copy()
214
- tide_io.writedicttojson(bidsdict, maskfilename + ".json")
215
- tide_io.savetonifti(mask.reshape((xsize, ysize, numslices)), theheader, maskfilename)
305
+ maplist = [
306
+ (
307
+ mask.reshape((xsize, ysize, numslices)),
308
+ "processvoxels",
309
+ "mask",
310
+ None,
311
+ "fMRI voxels processed by happy",
312
+ ),
313
+ ]
314
+ tide_io.savemaplist(
315
+ outputroot,
316
+ maplist,
317
+ None,
318
+ (xsize, ysize, numslices),
319
+ theheader,
320
+ bidsdict,
321
+ debug=args.debug,
322
+ )
216
323
  timings.append(["Mask saved", time.time(), None, None])
217
324
  mask_byslice = mask.reshape((xsize * ysize, numslices))
218
325
 
@@ -277,25 +384,49 @@ def happy_main(argparsingfunc):
277
384
  )
278
385
  # save motionr2 map
279
386
  theheader = input_data.copyheader(numtimepoints=1)
280
- motionr2filename = outputroot + "_desc-motionr2_map"
281
387
  bidsdict = bidsbasedict.copy()
282
- tide_io.writedicttojson(bidsdict, motionr2filename + ".json")
283
388
  outarray = np.zeros((xsize, ysize, numslices), dtype=float)
284
389
  outarray.reshape(numspatiallocs)[validprojvoxels] = confoundr2
285
- tide_io.savetonifti(
286
- outarray.reshape((xsize, ysize, numslices)), theheader, motionr2filename
390
+ maplist = [
391
+ (
392
+ outarray.reshape((xsize, ysize, numslices)),
393
+ "motionr2",
394
+ "map",
395
+ None,
396
+ "R2 of motion regression",
397
+ ),
398
+ ]
399
+ tide_io.savemaplist(
400
+ outputroot,
401
+ maplist,
402
+ None,
403
+ (xsize, ysize, numslices),
404
+ theheader,
405
+ bidsdict,
406
+ debug=args.debug,
287
407
  )
288
408
  if args.savemotionglmfilt:
289
409
  print("prior to save - fmri_data has shape", fmri_data.shape)
290
- motionfilteredfilename = outputroot + "_desc-motionfiltered_bold"
291
410
  bidsdict = bidsbasedict.copy()
292
- bidsdict["Units"] = "second"
293
- tide_io.writedicttojson(bidsdict, motionfilteredfilename + ".json")
294
- tide_io.savetonifti(
295
- fmri_data.reshape((xsize, ysize, numslices, timepoints)),
411
+ maplist = [
412
+ (
413
+ fmri_data.reshape((xsize, ysize, numslices, timepoints)),
414
+ "motionfiltered",
415
+ "bold",
416
+ None,
417
+ "fMRI data after motion regression",
418
+ ),
419
+ ]
420
+ tide_io.savemaplist(
421
+ outputroot,
422
+ maplist,
423
+ None,
424
+ (xsize, ysize, numslices, timepoints),
296
425
  theheader,
297
- motionfilteredfilename,
426
+ bidsdict,
427
+ debug=args.debug,
298
428
  )
429
+
299
430
  timings.append(["Motion filtered data saved", time.time(), numspatiallocs, "voxels"])
300
431
 
301
432
  # get slice times
@@ -334,16 +465,39 @@ def happy_main(argparsingfunc):
334
465
 
335
466
  # save means, medians, and mads
336
467
  theheader = input_data.copyheader(numtimepoints=1)
337
- meansfilename = outputroot + "_desc-means_map"
338
- mediansfilename = outputroot + "_desc-medians_map"
339
- madsfilename = outputroot + "_desc-mads_map"
340
468
  bidsdict = bidsbasedict.copy()
341
- tide_io.writedicttojson(bidsdict, meansfilename + ".json")
342
- tide_io.writedicttojson(bidsdict, mediansfilename + ".json")
343
- tide_io.writedicttojson(bidsdict, madsfilename + ".json")
344
- tide_io.savetonifti(means.reshape((xsize, ysize, numslices)), theheader, meansfilename)
345
- tide_io.savetonifti(medians.reshape((xsize, ysize, numslices)), theheader, mediansfilename)
346
- tide_io.savetonifti(mads.reshape((xsize, ysize, numslices)), theheader, madsfilename)
469
+ maplist = [
470
+ (
471
+ means.reshape((xsize, ysize, numslices)),
472
+ "means",
473
+ "map",
474
+ None,
475
+ "fMRI timecourse mean over time",
476
+ ),
477
+ (
478
+ medians.reshape((xsize, ysize, numslices)),
479
+ "medians",
480
+ "map",
481
+ None,
482
+ "fMRI timecourse median over time",
483
+ ),
484
+ (
485
+ mads.reshape((xsize, ysize, numslices)),
486
+ "mads",
487
+ "map",
488
+ None,
489
+ "fMRI timecourse MAD over time",
490
+ ),
491
+ ]
492
+ tide_io.savemaplist(
493
+ outputroot,
494
+ maplist,
495
+ None,
496
+ (xsize, ysize, numslices),
497
+ theheader,
498
+ bidsdict,
499
+ debug=args.debug,
500
+ )
347
501
 
348
502
  # read in estimation mask if present. Otherwise, otherwise use intensity mask.
349
503
  infodict["estweightsname"] = args.estweightsname
@@ -381,10 +535,12 @@ def happy_main(argparsingfunc):
381
535
  if numpasses > 1:
382
536
  print()
383
537
  print()
384
- print("starting pass", thispass + 1, "of", numpasses)
385
- passstring = " - pass " + str(thispass + 1)
538
+ print(f"starting pass {thispass + 1} of {numpasses}")
539
+ passstring = f" - pass {thispass + 1}"
540
+ passnamefrag = f"pass{thispass + 1}"
386
541
  else:
387
542
  passstring = ""
543
+ passnamefrag = ""
388
544
  # now get an estimate of the cardiac signal
389
545
  print("estimating cardiac signal from fmri data")
390
546
  tide_util.logmem("before cardiacfromimage")
@@ -412,6 +568,7 @@ def happy_main(argparsingfunc):
412
568
  madnorm=args.domadnorm,
413
569
  nprocs=args.nprocs,
414
570
  notchpct=args.notchpct,
571
+ notchrolloff=args.notchrolloff,
415
572
  fliparteries=args.fliparteries,
416
573
  arteriesonly=args.arteriesonly,
417
574
  usemask=args.usemaskcardfromfmri,
@@ -1415,9 +1572,24 @@ def happy_main(argparsingfunc):
1415
1572
  )
1416
1573
 
1417
1574
  theheader = input_data.copyheader(numtimepoints=1)
1418
- wrightfilename = f"{outputroot}_desc-wrightcorrspass{thispass + 1}_map"
1419
- tide_io.writedicttojson(bidsbasedict, wrightfilename + ".json")
1420
- tide_io.savetonifti(wrightcorrs, theheader, wrightfilename)
1575
+ maplist = [
1576
+ (
1577
+ wrightcorrs,
1578
+ f"wrightcorrspass{passnamefrag}",
1579
+ "map",
1580
+ None,
1581
+ "fMRI timecourse MAD over time",
1582
+ ),
1583
+ ]
1584
+ tide_io.savemaplist(
1585
+ outputroot,
1586
+ maplist,
1587
+ None,
1588
+ (xsize, ysize, numslices),
1589
+ theheader,
1590
+ bidsdict,
1591
+ debug=args.debug,
1592
+ )
1421
1593
  timings.append(
1422
1594
  [
1423
1595
  "Wright mask generation completed" + passstring,
@@ -1545,23 +1717,49 @@ def happy_main(argparsingfunc):
1545
1717
  numtimepoints=args.destpoints, tr=-np.pi, toffset=2.0 * np.pi / args.destpoints
1546
1718
  )
1547
1719
  if thispass == numpasses - 1:
1548
- appfilename = outputroot + "_desc-app_info"
1549
- normappfilename = outputroot + "_desc-normapp_info"
1550
- cinefilename = outputroot + "_desc-cine_info"
1551
- rawappfilename = outputroot + "_desc-rawapp_info"
1552
- bidsdict = bidsbasedict.copy()
1553
- bidsdict["Units"] = "second"
1554
- tide_io.writedicttojson(bidsdict, appfilename + ".json")
1555
- tide_io.writedicttojson(bidsdict, normappfilename + ".json")
1556
- tide_io.writedicttojson(bidsdict, cinefilename + ".json")
1557
- if args.outputlevel > 0:
1558
- tide_io.writedicttojson(bidsdict, rawappfilename + ".json")
1559
- tide_io.savetonifti(app, theheader, appfilename)
1560
- tide_io.savetonifti(normapp, theheader, normappfilename)
1561
- tide_io.savetonifti(cine, theheader, cinefilename)
1720
+ maplist = [
1721
+ (
1722
+ app,
1723
+ "app",
1724
+ "info",
1725
+ None,
1726
+ "Cardiac pulsatility waveform with minimum over time removed",
1727
+ ),
1728
+ (
1729
+ normapp,
1730
+ "normapp",
1731
+ "info",
1732
+ "percent",
1733
+ "Cardiac pulsatility waveform in percentage change relative to mean",
1734
+ ),
1735
+ (
1736
+ cine,
1737
+ "cine",
1738
+ "info",
1739
+ None,
1740
+ "fMRI signal averaged over a single cardiac cycle",
1741
+ ),
1742
+ ]
1562
1743
  if args.outputlevel > 0:
1563
- tide_io.savetonifti(rawapp, theheader, rawappfilename)
1564
-
1744
+ maplist += [
1745
+ (
1746
+ rawapp,
1747
+ "rawapp",
1748
+ "info",
1749
+ None,
1750
+ "Cardiac pulsatility waveform",
1751
+ ),
1752
+ ]
1753
+ # write the 4D phase projection maps
1754
+ tide_io.savemaplist(
1755
+ outputroot,
1756
+ maplist,
1757
+ None,
1758
+ (xsize, ysize, numslices, args.destpoints),
1759
+ theheader,
1760
+ bidsdict,
1761
+ debug=args.debug,
1762
+ )
1565
1763
  timings.append(["Phase projected data saved" + passstring, time.time(), None, None])
1566
1764
 
1567
1765
  if args.doaliasedcorrelation and thispass == numpasses - 1:
@@ -1570,21 +1768,57 @@ def happy_main(argparsingfunc):
1570
1768
  tr=(thealiasedcorrx[1] - thealiasedcorrx[0]),
1571
1769
  toffset=0.0,
1572
1770
  )
1573
- corrfuncfilename = outputroot + "_desc-corrfunc_info"
1574
- wavedelayfilename = outputroot + "_desc-wavedelay_map"
1575
- wavedelayCOMfilename = outputroot + "_desc-wavedelayCOM_map"
1576
- waveampfilename = outputroot + "_desc-waveamp_map"
1577
- bidsdict = bidsbasedict.copy()
1578
- tide_io.writedicttojson(bidsdict, waveampfilename + ".json")
1579
- bidsdict["Units"] = "second"
1580
- tide_io.writedicttojson(bidsdict, corrfuncfilename + ".json")
1581
- tide_io.writedicttojson(bidsdict, wavedelayfilename + ".json")
1582
- tide_io.writedicttojson(bidsdict, wavedelayCOMfilename + ".json")
1583
- tide_io.savetonifti(thecorrfunc, theheader, corrfuncfilename)
1771
+ maplist = [
1772
+ (
1773
+ thecorrfunc,
1774
+ "corrfunc",
1775
+ "info",
1776
+ None,
1777
+ "Aliased correlation function",
1778
+ ),
1779
+ ]
1780
+ tide_io.savemaplist(
1781
+ outputroot,
1782
+ maplist,
1783
+ None,
1784
+ (xsize, ysize, numslices, aliasedcorrelationpts),
1785
+ theheader,
1786
+ bidsdict,
1787
+ debug=args.debug,
1788
+ )
1584
1789
  theheader["dim"][4] = 1
1585
- tide_io.savetonifti(wavedelay, theheader, wavedelayfilename)
1586
- tide_io.savetonifti(wavedelayCOM, theheader, wavedelayCOMfilename)
1587
- tide_io.savetonifti(waveamp, theheader, waveampfilename)
1790
+ maplist = [
1791
+ (
1792
+ wavedelay,
1793
+ "wavedelay",
1794
+ "map",
1795
+ "seconds",
1796
+ "Peak delay of aliased correlation function",
1797
+ ),
1798
+ (
1799
+ wavedelayCOM,
1800
+ "wavedelayCOM",
1801
+ "map",
1802
+ "seconds",
1803
+ "Center of mass of aliased correlation function",
1804
+ ),
1805
+ (
1806
+ waveamp,
1807
+ "waveamp",
1808
+ "map",
1809
+ None,
1810
+ "Peak amplitude of aliased correlation function",
1811
+ ),
1812
+ ]
1813
+ tide_io.savemaplist(
1814
+ outputroot,
1815
+ maplist,
1816
+ None,
1817
+ (xsize, ysize, numslices),
1818
+ theheader,
1819
+ bidsdict,
1820
+ debug=args.debug,
1821
+ )
1588
1822
 
1589
1823
  # make and save a voxel intensity histogram
1590
1824
  if args.unnormvesselmap:
@@ -1622,24 +1856,34 @@ def happy_main(argparsingfunc):
1622
1856
  maskedapp2d[np.where(vesselmask.reshape(numspatiallocs) == 0)[0], :] = 0.0
1623
1857
  if args.outputlevel > 1:
1624
1858
  if thispass == numpasses - 1:
1625
- maskedappfilename = outputroot + "_desc-maskedapp_info"
1626
1859
  bidsdict = bidsbasedict.copy()
1627
- bidsdict["Units"] = "second"
1628
- tide_io.writedicttojson(bidsdict, maskedappfilename + ".json")
1629
- tide_io.savetonifti(
1630
- maskedapp2d.reshape((xsize, ysize, numslices, args.destpoints)),
1860
+ maplist = [
1861
+ (
1862
+ maskedapp2d.reshape((xsize, ysize, numslices, args.destpoints)),
1863
+ "maskedapp",
1864
+ "info",
1865
+ None,
1866
+ "Masked analytic phase projection",
1867
+ ),
1868
+ ]
1869
+ tide_io.savemaplist(
1870
+ outputroot,
1871
+ maplist,
1872
+ None,
1873
+ (xsize, ysize, numslices, args.destpoints),
1631
1874
  theheader,
1632
- maskedappfilename,
1875
+ bidsdict,
1876
+ debug=args.debug,
1877
+ )
1878
+ timings.append(
1879
+ [
1880
+ "Vessel masked phase projected data saved" + passstring,
1881
+ time.time(),
1882
+ None,
1883
+ None,
1884
+ ]
1633
1885
  )
1634
1886
  del maskedapp2d
1635
- timings.append(
1636
- [
1637
- "Vessel masked phase projected data saved" + passstring,
1638
- time.time(),
1639
- None,
1640
- None,
1641
- ]
1642
- )
1643
1887
 
1644
1888
  # save multiple versions of the hard vessel mask
1645
1889
  if args.unnormvesselmap:
@@ -1655,57 +1899,316 @@ def happy_main(argparsingfunc):
1655
1899
  veins = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) > 0, vesselmask, 0)
1656
1900
  theheader = input_data.copyheader(numtimepoints=1)
1657
1901
  if thispass == numpasses - 1:
1658
- vesselmaskfilename = outputroot + "_desc-vessels_mask"
1659
- minphasefilename = outputroot + "_desc-minphase_map"
1660
- maxphasefilename = outputroot + "_desc-maxphase_map"
1661
- arterymapfilename = outputroot + "_desc-arteries_map"
1662
- veinmapfilename = outputroot + "_desc-veins_map"
1663
- bidsdict = bidsbasedict.copy()
1664
- tide_io.writedicttojson(bidsdict, vesselmaskfilename + ".json")
1665
- tide_io.savetonifti(vesselmask, theheader, vesselmaskfilename)
1902
+ maplist = [
1903
+ (
1904
+ vesselmask,
1905
+ "vessels",
1906
+ "mask",
1907
+ None,
1908
+ "Vessel mask",
1909
+ ),
1910
+ ]
1666
1911
  if args.outputlevel > 0:
1667
- tide_io.writedicttojson(bidsdict, arterymapfilename + ".json")
1668
- tide_io.writedicttojson(bidsdict, veinmapfilename + ".json")
1669
- bidsdict["Units"] = "radians"
1670
- tide_io.writedicttojson(bidsdict, minphasefilename + ".json")
1671
- tide_io.writedicttojson(bidsdict, maxphasefilename + ".json")
1672
- tide_io.savetonifti(minphase, theheader, minphasefilename)
1673
- tide_io.savetonifti(maxphase, theheader, maxphasefilename)
1674
- tide_io.savetonifti(arteries, theheader, arterymapfilename)
1675
- tide_io.savetonifti(veins, theheader, veinmapfilename)
1912
+ maplist += [
1913
+ (
1914
+ minphase,
1915
+ "minphase",
1916
+ "map",
1917
+ "radians",
1918
+ "Cardiac phase where minimum pulsatility signal occurs",
1919
+ ),
1920
+ (
1921
+ maxphase,
1922
+ "maxphase",
1923
+ "map",
1924
+ "radians",
1925
+ "Cardiac phase where maximum pulsatility signal occurs",
1926
+ ),
1927
+ (
1928
+ arteries,
1929
+ "arteries",
1930
+ "map",
1931
+ None,
1932
+ "Arterial voxels (maybe)",
1933
+ ),
1934
+ (
1935
+ veins,
1936
+ "veins",
1937
+ "map",
1938
+ None,
1939
+ "Venous voxels (maybe)",
1940
+ ),
1941
+ ]
1942
+ tide_io.savemaplist(
1943
+ outputroot,
1944
+ maplist,
1945
+ None,
1946
+ (xsize, ysize, numslices),
1947
+ theheader,
1948
+ bidsdict,
1949
+ debug=args.debug,
1950
+ )
1676
1951
  timings.append(["Masks saved" + passstring, time.time(), None, None])
1677
1952
 
1678
1953
  # save the mask we used for this pass
1679
- tide_io.savetonifti(
1680
- estweights_byslice.reshape((xsize, ysize, numslices)),
1954
+ tide_io.savemaplist(
1955
+ outputroot,
1956
+ [
1957
+ (
1958
+ estweights_byslice.reshape((xsize, ysize, numslices)),
1959
+ f"estweights{passnamefrag}",
1960
+ "map",
1961
+ None,
1962
+ f"Estweights{passstring}",
1963
+ ),
1964
+ ],
1965
+ None,
1966
+ (xsize, ysize, numslices),
1681
1967
  theheader,
1682
- f"{outputroot}_desc-estweightspass{thispass}_map",
1968
+ bidsdict,
1969
+ debug=args.debug,
1683
1970
  )
1684
1971
 
1972
+ # estimate pulsatility
1973
+ (
1974
+ card_min,
1975
+ card_max,
1976
+ card_mean,
1977
+ card_std,
1978
+ card_median,
1979
+ card_mad,
1980
+ card_skew,
1981
+ card_kurtosis,
1982
+ ) = tide_stats.fmristats(normapp.reshape(numspatiallocs, -1))
1983
+ maplist = [
1984
+ (
1985
+ card_min,
1986
+ "appmin",
1987
+ "map",
1988
+ None,
1989
+ "Minimum value of analytic phase projection across all phases",
1990
+ ),
1991
+ (
1992
+ card_max,
1993
+ "appmax",
1994
+ "map",
1995
+ None,
1996
+ "Maximum value of analytic phase projection across all phases",
1997
+ ),
1998
+ (
1999
+ card_mean,
2000
+ "appmean",
2001
+ "map",
2002
+ None,
2003
+ "Mean value of analytic phase projection across all phases",
2004
+ ),
2005
+ (
2006
+ card_std,
2007
+ "appstd",
2008
+ "map",
2009
+ None,
2010
+ "Standard deviation of analytic phase projection across all phases",
2011
+ ),
2012
+ (
2013
+ card_median,
2014
+ "appmedian",
2015
+ "map",
2016
+ None,
2017
+ "Median of analytic phase projection across all phases",
2018
+ ),
2019
+ (
2020
+ card_mad,
2021
+ "appMAD",
2022
+ "map",
2023
+ None,
2024
+ "Median average deviate of analytic phase projection across all phases",
2025
+ ),
2026
+ (
2027
+ card_skew,
2028
+ "appskew",
2029
+ "map",
2030
+ None,
2031
+ "Skewness of analytic phase projection across all phases",
2032
+ ),
2033
+ (
2034
+ card_kurtosis,
2035
+ "appkurtosis",
2036
+ "map",
2037
+ None,
2038
+ "Kurtosis of analytic phase projection across all phases",
2039
+ ),
2040
+ ]
2041
+ # write the 3D maps
2042
+ tide_io.savemaplist(
2043
+ outputroot,
2044
+ maplist,
2045
+ None,
2046
+ (xsize, ysize, numslices),
2047
+ theheader,
2048
+ bidsdict,
2049
+ debug=args.debug,
2050
+ )
2051
+
2052
+ pulsatilitymap = 100.0 * (np.max(normapp, axis=3) - np.min(normapp, axis=3))
2053
+ rawrobustmax = tide_stats.getfracval(pulsatilitymap, 0.98, nozero=True)
2054
+ rawmedian = tide_stats.getfracval(pulsatilitymap, 0.50, nozero=True)
2055
+ infodict["pulsatilitythresh"] = rawmedian * 3.0
2056
+ pulsatilitymap = np.where(pulsatilitymap < rawrobustmax, pulsatilitymap, rawrobustmax)
2057
+ pulsatilitymask = np.where(pulsatilitymap > infodict["pulsatilitythresh"], 1.0, 0.0)
2058
+
1685
2059
  # now get ready to start again with a new mask
1686
2060
  if args.doaliasedcorrelation and thispass > 0:
1687
2061
  estweights_byslice = waveamp_byslice * vesselmask.reshape((xsize * ysize, numslices))
1688
2062
  else:
1689
- estweights_byslice = vesselmask.reshape((xsize * ysize, numslices)) + 0
1690
-
1691
- # save a vessel image
1692
- if args.unnormvesselmap:
1693
- vesselmap = np.max(app, axis=3)
2063
+ if args.useoriginalvesselmethod:
2064
+ estweights_byslice = vesselmask.reshape((xsize * ysize, numslices)) + 0
2065
+ else:
2066
+ estweights_byslice = pulsatilitymask.reshape((xsize * ysize, numslices)) + 0
2067
+
2068
+ # calculate the lf and hf pulsatility maps
2069
+ lfnormapp = normapp * 0.0
2070
+ hfnormapp = normapp * 0.0
2071
+ for thephase in range(args.destpoints):
2072
+ lfnormapp[:, :, :, thephase] = (
2073
+ tide_filt.ssmooth(
2074
+ xdim, ydim, slicethickness, args.pulsatilitysigma, normapp[:, :, :, thephase]
2075
+ )
2076
+ * pulsatilitymask
2077
+ )
2078
+ hfnormapp[:, :, :, thephase] = normapp[:, :, :, thephase] - lfnormapp[:, :, :, thephase]
2079
+ lfnormapp -= np.min(lfnormapp, axis=3)[:, :, :, None]
2080
+ hfnormapp -= np.min(hfnormapp, axis=3)[:, :, :, None]
2081
+ lfpulsatilitymap = (
2082
+ tide_filt.ssmooth(xdim, ydim, slicethickness, args.pulsatilitysigma, pulsatilitymap)
2083
+ * pulsatilitymask
2084
+ )
2085
+ hfpulsatilitymap = pulsatilitymap - lfpulsatilitymap
2086
+ lfpulsatilitymap2 = 100.0 * np.max(lfnormapp, axis=3)
2087
+ lfrobustmax = tide_stats.getfracval(lfpulsatilitymap2, 0.98, nozero=True)
2088
+ lfpulsatilitymap2 = np.where(lfpulsatilitymap2 < lfrobustmax, lfpulsatilitymap2, lfrobustmax)
2089
+ hfpulsatilitymap2 = 100.0 * np.max(hfnormapp, axis=3)
2090
+ hfrobustmax = tide_stats.getfracval(hfpulsatilitymap2, 0.98, nozero=True)
2091
+ hfpulsatilitymap2 = np.where(hfpulsatilitymap2 < hfrobustmax, hfpulsatilitymap2, hfrobustmax)
2092
+
2093
+ # make a vessel map
2094
+ if args.useoriginalvesselmethod:
2095
+ if args.unnormvesselmap:
2096
+ vesselmap = np.max(app, axis=3)
2097
+ else:
2098
+ # vesselmap = np.max(normapp, axis=3)
2099
+ vesselmap = np.where(hfpulsatilitymap > args.pulsatilitythreshold, 1.0, 0.0)
1694
2100
  else:
1695
- vesselmap = np.max(normapp, axis=3)
1696
- vesselmapfilename = outputroot + "_desc-vessels_map"
1697
- arterymapfilename = outputroot + "_desc-arteries_map"
1698
- veinmapfilename = outputroot + "_desc-veins_map"
1699
- tide_io.savetonifti(vesselmap, theheader, vesselmapfilename)
1700
- tide_io.savetonifti(
1701
- np.where(appflips_byslice.reshape((xsize, ysize, numslices)) < 0, vesselmap, 0.0),
2101
+ vesselmap = pulsatilitymask
2102
+ veinmap = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) > 0, vesselmap, 0.0)
2103
+ arterymap = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) < 0, vesselmap, 0.0)
2104
+
2105
+ # specify the 3D maps
2106
+ maplist = [
2107
+ (
2108
+ pulsatilitymap,
2109
+ "pulsatility",
2110
+ "map",
2111
+ "percent",
2112
+ "Cardiac pulsatility in percentage change relative to mean",
2113
+ ),
2114
+ (
2115
+ pulsatilitymask,
2116
+ "pulsatility",
2117
+ "mask",
2118
+ None,
2119
+ "Valid cardiac pulsatility voxels",
2120
+ ),
2121
+ (
2122
+ lfpulsatilitymap,
2123
+ "lfpulsatility",
2124
+ "map",
2125
+ "percent",
2126
+ "Low spatial frequency cardiac pulsatility in percentage change relative to mean",
2127
+ ),
2128
+ (
2129
+ hfpulsatilitymap,
2130
+ "hfpulsatility",
2131
+ "map",
2132
+ "percent",
2133
+ "High spatial frequency cardiac pulsatility in percentage change relative to mean",
2134
+ ),
2135
+ (
2136
+ lfpulsatilitymap2,
2137
+ "lfpulsatility2",
2138
+ "map",
2139
+ "percent",
2140
+ "Low spatial frequency cardiac pulsatility in percentage change relative to mean",
2141
+ ),
2142
+ (
2143
+ hfpulsatilitymap2,
2144
+ "hfpulsatility2",
2145
+ "map",
2146
+ "percent",
2147
+ "High spatial frequency cardiac pulsatility in percentage change relative to mean",
2148
+ ),
2149
+ (
2150
+ vesselmap,
2151
+ "vessels",
2152
+ "map",
2153
+ None,
2154
+ "Vessel voxels",
2155
+ ),
2156
+ (
2157
+ arterymap,
2158
+ "arteries",
2159
+ "map",
2160
+ None,
2161
+ "Arterial voxels (maybe)",
2162
+ ),
2163
+ (
2164
+ veinmap,
2165
+ "veins",
2166
+ "map",
2167
+ None,
2168
+ "Venous voxels (maybe)",
2169
+ ),
2170
+ ]
2171
+ # write the 3D maps
2172
+ tide_io.savemaplist(
2173
+ outputroot,
2174
+ maplist,
2175
+ None,
2176
+ (xsize, ysize, numslices),
1702
2177
  theheader,
1703
- arterymapfilename,
2178
+ bidsdict,
2179
+ debug=args.debug,
1704
2180
  )
1705
- tide_io.savetonifti(
1706
- np.where(appflips_byslice.reshape((xsize, ysize, numslices)) > 0, vesselmap, 0.0),
2181
+
2182
+ # specify the 4D maps
2183
+ theheader = input_data.copyheader(
2184
+ numtimepoints=args.destpoints, tr=-np.pi, toffset=2.0 * np.pi / args.destpoints
2185
+ )
2186
+ maplist = [
2187
+ (
2188
+ lfnormapp,
2189
+ "lfnormapp",
2190
+ "info",
2191
+ "percent",
2192
+ "Low spatial frequency cardiac pulsatility waveform in percentage change relative to mean",
2193
+ ),
2194
+ (
2195
+ hfnormapp,
2196
+ "hfnormapp",
2197
+ "info",
2198
+ "percent",
2199
+ "High spatial frequency cardiac pulsatility waveform in percentage change relative to mean",
2200
+ ),
2201
+ ]
2202
+
2203
+ # write the 4D maps
2204
+ tide_io.savemaplist(
2205
+ outputroot,
2206
+ maplist,
2207
+ None,
2208
+ (xsize, ysize, numslices, args.destpoints),
1707
2209
  theheader,
1708
- veinmapfilename,
2210
+ bidsdict,
2211
+ debug=args.debug,
1709
2212
  )
1710
2213
 
1711
2214
  # now generate aliased cardiac signals and regress them out of the data
@@ -1731,34 +2234,85 @@ def happy_main(argparsingfunc):
1731
2234
  theheader = input_data.copyheader()
1732
2235
  timings.append(["Cardiac signal generated", time.time(), None, None])
1733
2236
  if args.savecardiacnoise:
1734
- cardiacnoisefilename = outputroot + "_desc-cardiacnoise_info"
1735
- phaseindexfilename = outputroot + "_desc-phaseindices_info"
1736
- tide_io.savetonifti(
1737
- cardiacnoise.reshape((xsize, ysize, numslices, timepoints)),
1738
- theheader,
1739
- cardiacnoisefilename,
1740
- )
1741
- tide_io.savetonifti(
1742
- phaseindices.reshape((xsize, ysize, numslices, timepoints)),
2237
+ maplist = [
2238
+ (
2239
+ cardiacnoise.reshape((xsize, ysize, numslices, timepoints)),
2240
+ "cardiacnoise",
2241
+ "info",
2242
+ None,
2243
+ "Calculated cardiac noise EVs",
2244
+ ),
2245
+ (
2246
+ phaseindices.reshape((xsize, ysize, numslices, timepoints)),
2247
+ "phaseindices",
2248
+ "info",
2249
+ None,
2250
+ "Phase indices",
2251
+ ),
2252
+ ]
2253
+ tide_io.savemaplist(
2254
+ outputroot,
2255
+ maplist,
2256
+ None,
2257
+ (xsize, ysize, numslices, timepoints),
1743
2258
  theheader,
1744
- phaseindexfilename,
2259
+ bidsdict,
2260
+ debug=args.debug,
1745
2261
  )
1746
2262
  timings.append(["Cardiac signal saved", time.time(), None, None])
1747
2263
 
1748
2264
  # now remove them
1749
2265
  tide_util.logmem("before cardiac removal")
1750
2266
  print("Removing cardiac signal with GLM")
1751
- filtereddata = 0.0 * fmri_data
1752
2267
  validlocs = np.where(mask > 0)[0]
1753
2268
  numvalidspatiallocs = len(validlocs)
2269
+ if args.focaldebug:
2270
+ print(f"{numvalidspatiallocs=}, {fmri_data.shape=}")
2271
+ filtereddata, filtereddata_shm = tide_util.allocarray(
2272
+ fmri_data.shape,
2273
+ "float64",
2274
+ shared=infodict["sharedmem"],
2275
+ name=f"filtereddata_{infodict['pid']}",
2276
+ )
2277
+ datatoremove, datatoremove_shm = tide_util.allocarray(
2278
+ fmri_data.shape,
2279
+ "float64",
2280
+ shared=infodict["sharedmem"],
2281
+ name=f"datatoremove_{infodict['pid']}",
2282
+ )
1754
2283
  threshval = 0.0
1755
2284
  if args.dospatialregression:
1756
- meanvals = np.zeros(timepoints, dtype=np.float64)
1757
- rvals = np.zeros(timepoints, dtype=np.float64)
1758
- r2vals = np.zeros(timepoints, dtype=np.float64)
1759
- fitcoffs = np.zeros(timepoints, dtype=np.float64)
1760
- fitNorm = np.zeros(timepoints, dtype=np.float64)
1761
- datatoremove = 0.0 * fmri_data
2285
+ regressiontype = "spatial"
2286
+ meanvals, meanvals_shm = tide_util.allocarray(
2287
+ (timepoints),
2288
+ "float64",
2289
+ shared=infodict["sharedmem"],
2290
+ name=f"meanvals_{infodict['pid']}",
2291
+ )
2292
+ rvals, rvals_shm = tide_util.allocarray(
2293
+ (timepoints),
2294
+ "float64",
2295
+ shared=infodict["sharedmem"],
2296
+ name=f"rvals_{infodict['pid']}",
2297
+ )
2298
+ r2vals, r2vals_shm = tide_util.allocarray(
2299
+ (timepoints),
2300
+ "float64",
2301
+ shared=infodict["sharedmem"],
2302
+ name=f"r2vals_{infodict['pid']}",
2303
+ )
2304
+ fitcoffs, fitcoffs_shm = tide_util.allocarray(
2305
+ (timepoints),
2306
+ "float64",
2307
+ shared=infodict["sharedmem"],
2308
+ name=f"fitcoffs_{infodict['pid']}",
2309
+ )
2310
+ fitNorm, fitNorm_shm = tide_util.allocarray(
2311
+ (timepoints),
2312
+ "float64",
2313
+ shared=infodict["sharedmem"],
2314
+ name=f"fitNorm_{infodict['pid']}",
2315
+ )
1762
2316
  print("Running spatial regression on", timepoints, "timepoints")
1763
2317
  tide_util.disablemkl(args.nprocs)
1764
2318
  tide_linfitfiltpass.linfitfiltpass(
@@ -1778,8 +2332,10 @@ def happy_main(argparsingfunc):
1778
2332
  nprocs=args.nprocs,
1779
2333
  )
1780
2334
  tide_util.enablemkl(args.mklthreads)
1781
- print(datatoremove.shape, cardiacnoise.shape, fitcoffs.shape)
1782
- # datatoremove[validlocs, :] = np.multiply(cardiacnoise[validlocs, :], fitcoffs[:, None])
2335
+ datatoremove[validlocs, :] = np.multiply(cardiacnoise[validlocs, :], fitcoffs[None, :])
2336
+ print(f"{datatoremove.shape=}, {np.min(datatoremove)=}, {np.max(datatoremove)=}")
2337
+ print(f"{cardiacnoise.shape=}, {np.min(cardiacnoise)=}, {np.max(cardiacnoise)=}")
2338
+ print(f"{fitcoffs.shape=}, {np.min(fitcoffs)=}, {np.max(fitcoffs)=}")
1783
2339
  filtereddata = fmri_data - datatoremove
1784
2340
  timings.append(
1785
2341
  [
@@ -1789,38 +2345,39 @@ def happy_main(argparsingfunc):
1789
2345
  "timepoints",
1790
2346
  ]
1791
2347
  )
1792
- tide_io.writevec(fitcoffs, outputroot + "_fitcoff.txt")
1793
- tide_io.writevec(meanvals, outputroot + "_fitmean.txt")
1794
- tide_io.writevec(rvals, outputroot + "_fitR.txt")
1795
- theheader = input_data.copyheader()
1796
- cardfiltresultfilename = outputroot + "_desc-cardfiltResult_bold"
1797
- cardfiltremovedfilename = outputroot + "_desc-cardfiltRemoved_bold"
1798
- tide_io.savetonifti(
1799
- filtereddata.reshape((xsize, ysize, numslices, timepoints)),
1800
- theheader,
1801
- cardfiltresultfilename,
2348
+
2349
+ if args.dotemporalregression:
2350
+ regressiontype = "temporal"
2351
+ meanvals, meanvals_shm = tide_util.allocarray(
2352
+ (numspatiallocs),
2353
+ "float64",
2354
+ shared=infodict["sharedmem"],
2355
+ name=f"meanvals_{infodict['pid']}",
1802
2356
  )
1803
- tide_io.savetonifti(
1804
- datatoremove.reshape((xsize, ysize, numslices, timepoints)),
1805
- theheader,
1806
- cardfiltremovedfilename,
2357
+ rvals, rvals_shm = tide_util.allocarray(
2358
+ (numspatiallocs),
2359
+ "float64",
2360
+ shared=infodict["sharedmem"],
2361
+ name=f"rvals_{infodict['pid']}",
1807
2362
  )
1808
- timings.append(
1809
- [
1810
- "Cardiac signal spatial regression files written",
1811
- time.time(),
1812
- None,
1813
- None,
1814
- ]
2363
+ r2vals, r2vals_shm = tide_util.allocarray(
2364
+ (numspatiallocs),
2365
+ "float64",
2366
+ shared=infodict["sharedmem"],
2367
+ name=f"r2vals_{infodict['pid']}",
2368
+ )
2369
+ fitcoffs, fitcoffs_shm = tide_util.allocarray(
2370
+ (numspatiallocs),
2371
+ "float64",
2372
+ shared=infodict["sharedmem"],
2373
+ name=f"fitcoffs_{infodict['pid']}",
2374
+ )
2375
+ fitNorm, fitNorm_shm = tide_util.allocarray(
2376
+ (numspatiallocs),
2377
+ "float64",
2378
+ shared=infodict["sharedmem"],
2379
+ name=f"fitNorm_{infodict['pid']}",
1815
2380
  )
1816
-
1817
- if args.dotemporalregression:
1818
- meanvals = np.zeros(numspatiallocs, dtype=np.float64)
1819
- rvals = np.zeros(numspatiallocs, dtype=np.float64)
1820
- r2vals = np.zeros(numspatiallocs, dtype=np.float64)
1821
- fitcoffs = np.zeros(numspatiallocs, dtype=np.float64)
1822
- fitNorm = np.zeros(numspatiallocs, dtype=np.float64)
1823
- datatoremove = 0.0 * fmri_data
1824
2381
  print("Running temporal regression on", numvalidspatiallocs, "voxels")
1825
2382
  tide_util.disablemkl(args.nprocs)
1826
2383
  tide_linfitfiltpass.linfitfiltpass(
@@ -1837,11 +2394,15 @@ def happy_main(argparsingfunc):
1837
2394
  filtereddata[validlocs, :],
1838
2395
  procbyvoxel=True,
1839
2396
  nprocs=args.nprocs,
2397
+ debug=args.focaldebug,
1840
2398
  )
1841
2399
  tide_util.enablemkl(args.mklthreads)
1842
2400
  datatoremove[validlocs, :] = np.multiply(
1843
2401
  cardiacnoise[validlocs, :], fitcoffs[validlocs, None]
1844
2402
  )
2403
+ print(f"{datatoremove.shape=}, {np.min(datatoremove)=}, {np.max(datatoremove)=}")
2404
+ print(f"{cardiacnoise.shape=}, {np.min(cardiacnoise)=}, {np.max(cardiacnoise)=}")
2405
+ print(f"{fitcoffs.shape=}, {np.min(fitcoffs)=}, {np.max(fitcoffs)=}")
1845
2406
  filtereddata[validlocs, :] = fmri_data[validlocs, :] - datatoremove[validlocs, :]
1846
2407
  timings.append(
1847
2408
  [
@@ -1852,44 +2413,85 @@ def happy_main(argparsingfunc):
1852
2413
  ]
1853
2414
  )
1854
2415
  theheader = input_data.copyheader(numtimepoints=1)
1855
- cardfiltcoeffsfilename = outputroot + "_desc-cardfiltCoeffs_map"
1856
- cardfiltmeanfilename = outputroot + "_desc-cardfiltMean_map"
1857
- cardfiltRfilename = outputroot + "_desc-cardfiltR_map"
1858
- tide_io.savetonifti(
1859
- fitcoffs.reshape((xsize, ysize, numslices)),
1860
- theheader,
1861
- cardfiltcoeffsfilename,
1862
- )
1863
- tide_io.savetonifti(
1864
- meanvals.reshape((xsize, ysize, numslices)),
2416
+ maplist = [
2417
+ (
2418
+ fitcoffs.reshape((xsize, ysize, numslices)),
2419
+ "cardfiltCoeffs",
2420
+ "map",
2421
+ None,
2422
+ "Coefficients for temporal cardiac noise regression",
2423
+ ),
2424
+ (
2425
+ meanvals.reshape((xsize, ysize, numslices)),
2426
+ "cardfiltMean",
2427
+ "map",
2428
+ None,
2429
+ "Mean values after temporal cardiac noise regression",
2430
+ ),
2431
+ (
2432
+ rvals.reshape((xsize, ysize, numslices)),
2433
+ "cardfiltR",
2434
+ "map",
2435
+ None,
2436
+ "R values for temporal cardiac noise regression",
2437
+ ),
2438
+ ]
2439
+ tide_io.savemaplist(
2440
+ outputroot,
2441
+ maplist,
2442
+ None,
2443
+ (xsize, ysize, numslices),
1865
2444
  theheader,
1866
- cardfiltmeanfilename,
1867
- )
1868
- tide_io.savetonifti(
1869
- rvals.reshape((xsize, ysize, numslices)), theheader, cardfiltRfilename
2445
+ bidsdict,
2446
+ debug=args.debug,
1870
2447
  )
1871
2448
 
1872
- theheader = input_data.copyheader()
1873
- cardfiltresultfilename = outputroot + "_desc-cardfiltResult_bold"
1874
- cardfiltremovedfilename = outputroot + "_desc-cardfiltRemoved_bold"
1875
- tide_io.savetonifti(
2449
+ # now write out the filtered data
2450
+ theheader = input_data.copyheader()
2451
+ maplist = [
2452
+ (
1876
2453
  filtereddata.reshape((xsize, ysize, numslices, timepoints)),
1877
- theheader,
1878
- cardfiltresultfilename,
1879
- )
1880
- tide_io.savetonifti(
2454
+ "cardfiltResult",
2455
+ "bold",
2456
+ None,
2457
+ f"Cardiac filtered BOLD data after {regressiontype} regression",
2458
+ ),
2459
+ (
1881
2460
  datatoremove.reshape((xsize, ysize, numslices, timepoints)),
1882
- theheader,
1883
- cardfiltremovedfilename,
1884
- )
1885
- timings.append(
1886
- [
1887
- "Cardiac signal temporal regression files written",
1888
- time.time(),
1889
- None,
1890
- None,
1891
- ]
1892
- )
2461
+ "cardfiltRemoved",
2462
+ "bold",
2463
+ None,
2464
+ f"Cardiac noise removed with {regressiontype} regression",
2465
+ ),
2466
+ ]
2467
+ tide_io.savemaplist(
2468
+ outputroot,
2469
+ maplist,
2470
+ None,
2471
+ (xsize, ysize, numslices, timepoints),
2472
+ theheader,
2473
+ bidsdict,
2474
+ debug=args.debug,
2475
+ )
2476
+ timings.append(
2477
+ [
2478
+ f"Cardiac signal {regressiontype} regression files written",
2479
+ time.time(),
2480
+ None,
2481
+ None,
2482
+ ]
2483
+ )
2484
+
2485
+ # clean up shared memory, if using
2486
+ if args.dotemporalregression or args.dospatialregression:
2487
+ if infodict["sharedmem"]:
2488
+ tide_util.cleanup_shm(filtereddata_shm)
2489
+ tide_util.cleanup_shm(datatoremove_shm)
2490
+ tide_util.cleanup_shm(meanvals_shm)
2491
+ tide_util.cleanup_shm(rvals_shm)
2492
+ tide_util.cleanup_shm(r2vals_shm)
2493
+ tide_util.cleanup_shm(fitcoffs_shm)
2494
+ tide_util.cleanup_shm(fitNorm_shm)
1893
2495
 
1894
2496
  timings.append(["Done", time.time(), None, None])
1895
2497