rapidtide 2.9.5__py3-none-any.whl → 3.1.3__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 (405) hide show
  1. cloud/gmscalc-HCPYA +1 -1
  2. cloud/mount-and-run +2 -0
  3. cloud/rapidtide-HCPYA +3 -3
  4. rapidtide/Colortables.py +538 -38
  5. rapidtide/OrthoImageItem.py +1094 -51
  6. rapidtide/RapidtideDataset.py +1709 -114
  7. rapidtide/__init__.py +0 -8
  8. rapidtide/_version.py +4 -4
  9. rapidtide/calccoherence.py +242 -97
  10. rapidtide/calcnullsimfunc.py +240 -140
  11. rapidtide/calcsimfunc.py +314 -129
  12. rapidtide/correlate.py +1211 -389
  13. rapidtide/data/examples/src/testLD +56 -0
  14. rapidtide/data/examples/src/test_findmaxlag.py +2 -2
  15. rapidtide/data/examples/src/test_mlregressallt.py +32 -17
  16. rapidtide/data/examples/src/testalign +1 -1
  17. rapidtide/data/examples/src/testatlasaverage +35 -7
  18. rapidtide/data/examples/src/testboth +21 -0
  19. rapidtide/data/examples/src/testcifti +11 -0
  20. rapidtide/data/examples/src/testdelayvar +13 -0
  21. rapidtide/data/examples/src/testdlfilt +25 -0
  22. rapidtide/data/examples/src/testfft +35 -0
  23. rapidtide/data/examples/src/testfileorfloat +37 -0
  24. rapidtide/data/examples/src/testfmri +94 -27
  25. rapidtide/data/examples/src/testfuncs +3 -3
  26. rapidtide/data/examples/src/testglmfilt +8 -6
  27. rapidtide/data/examples/src/testhappy +84 -51
  28. rapidtide/data/examples/src/testinitdelay +19 -0
  29. rapidtide/data/examples/src/testmodels +33 -0
  30. rapidtide/data/examples/src/testnewrefine +26 -0
  31. rapidtide/data/examples/src/testnoiseamp +21 -0
  32. rapidtide/data/examples/src/testppgproc +17 -0
  33. rapidtide/data/examples/src/testrefineonly +22 -0
  34. rapidtide/data/examples/src/testretro +26 -13
  35. rapidtide/data/examples/src/testretrolagtcs +16 -0
  36. rapidtide/data/examples/src/testrolloff +11 -0
  37. rapidtide/data/examples/src/testsimdata +45 -28
  38. rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
  39. rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
  40. rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
  41. rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
  42. rapidtide/data/models/model_cnn_pytorch_fulldata/loss.png +0 -0
  43. rapidtide/data/models/model_cnn_pytorch_fulldata/loss.txt +1 -0
  44. rapidtide/data/models/model_cnn_pytorch_fulldata/model.pth +0 -0
  45. rapidtide/data/models/model_cnn_pytorch_fulldata/model_meta.json +80 -0
  46. rapidtide/data/models/model_cnnbp_pytorch_fullldata/loss.png +0 -0
  47. rapidtide/data/models/model_cnnbp_pytorch_fullldata/loss.txt +1 -0
  48. rapidtide/data/models/model_cnnbp_pytorch_fullldata/model.pth +0 -0
  49. rapidtide/data/models/model_cnnbp_pytorch_fullldata/model_meta.json +138 -0
  50. rapidtide/data/models/model_cnnfft_pytorch_fulldata/loss.png +0 -0
  51. rapidtide/data/models/model_cnnfft_pytorch_fulldata/loss.txt +1 -0
  52. rapidtide/data/models/model_cnnfft_pytorch_fulldata/model.pth +0 -0
  53. rapidtide/data/models/model_cnnfft_pytorch_fulldata/model_meta.json +128 -0
  54. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/loss.png +0 -0
  55. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/loss.txt +1 -0
  56. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/model.pth +0 -0
  57. rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/model_meta.json +49 -0
  58. rapidtide/data/models/model_revised_tf2/model.keras +0 -0
  59. rapidtide/data/models/{model_serdar → model_revised_tf2}/model_meta.json +1 -1
  60. rapidtide/data/models/model_serdar2_tf2/model.keras +0 -0
  61. rapidtide/data/models/{model_serdar2 → model_serdar2_tf2}/model_meta.json +1 -1
  62. rapidtide/data/models/model_serdar_tf2/model.keras +0 -0
  63. rapidtide/data/models/{model_revised → model_serdar_tf2}/model_meta.json +1 -1
  64. rapidtide/data/reference/HCP1200v2_MTT_2mm.nii.gz +0 -0
  65. rapidtide/data/reference/HCP1200v2_binmask_2mm.nii.gz +0 -0
  66. rapidtide/data/reference/HCP1200v2_csf_2mm.nii.gz +0 -0
  67. rapidtide/data/reference/HCP1200v2_gray_2mm.nii.gz +0 -0
  68. rapidtide/data/reference/HCP1200v2_graylaghist.json +7 -0
  69. rapidtide/data/reference/HCP1200v2_graylaghist.tsv.gz +0 -0
  70. rapidtide/data/reference/HCP1200v2_laghist.json +7 -0
  71. rapidtide/data/reference/HCP1200v2_laghist.tsv.gz +0 -0
  72. rapidtide/data/reference/HCP1200v2_mask_2mm.nii.gz +0 -0
  73. rapidtide/data/reference/HCP1200v2_maxcorr_2mm.nii.gz +0 -0
  74. rapidtide/data/reference/HCP1200v2_maxtime_2mm.nii.gz +0 -0
  75. rapidtide/data/reference/HCP1200v2_maxwidth_2mm.nii.gz +0 -0
  76. rapidtide/data/reference/HCP1200v2_negmask_2mm.nii.gz +0 -0
  77. rapidtide/data/reference/HCP1200v2_timepercentile_2mm.nii.gz +0 -0
  78. rapidtide/data/reference/HCP1200v2_white_2mm.nii.gz +0 -0
  79. rapidtide/data/reference/HCP1200v2_whitelaghist.json +7 -0
  80. rapidtide/data/reference/HCP1200v2_whitelaghist.tsv.gz +0 -0
  81. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2.xml +131 -0
  82. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2_regions.txt +60 -0
  83. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2_space-MNI152NLin6Asym_2mm.nii.gz +0 -0
  84. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm.nii.gz +0 -0
  85. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm_mask.nii.gz +0 -0
  86. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin6Asym_2mm_mask.nii.gz +0 -0
  87. rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL2_space-MNI152NLin6Asym_2mm_mask.nii.gz +0 -0
  88. rapidtide/data/reference/MNI152_T1_1mm_Brain_FAST_seg.nii.gz +0 -0
  89. rapidtide/data/reference/MNI152_T1_1mm_Brain_Mask.nii.gz +0 -0
  90. rapidtide/data/reference/MNI152_T1_2mm_Brain_FAST_seg.nii.gz +0 -0
  91. rapidtide/data/reference/MNI152_T1_2mm_Brain_Mask.nii.gz +0 -0
  92. rapidtide/decorators.py +91 -0
  93. rapidtide/dlfilter.py +2553 -414
  94. rapidtide/dlfiltertorch.py +5201 -0
  95. rapidtide/externaltools.py +328 -13
  96. rapidtide/fMRIData_class.py +178 -0
  97. rapidtide/ffttools.py +168 -0
  98. rapidtide/filter.py +2704 -1462
  99. rapidtide/fit.py +2361 -579
  100. rapidtide/genericmultiproc.py +197 -0
  101. rapidtide/happy_supportfuncs.py +3255 -548
  102. rapidtide/helper_classes.py +590 -1181
  103. rapidtide/io.py +2569 -468
  104. rapidtide/linfitfiltpass.py +784 -0
  105. rapidtide/makelaggedtcs.py +267 -97
  106. rapidtide/maskutil.py +555 -25
  107. rapidtide/miscmath.py +867 -137
  108. rapidtide/multiproc.py +217 -44
  109. rapidtide/patchmatch.py +752 -0
  110. rapidtide/peakeval.py +32 -32
  111. rapidtide/ppgproc.py +2205 -0
  112. rapidtide/qualitycheck.py +353 -40
  113. rapidtide/refinedelay.py +854 -0
  114. rapidtide/refineregressor.py +939 -0
  115. rapidtide/resample.py +725 -204
  116. rapidtide/scripts/__init__.py +1 -0
  117. rapidtide/scripts/{adjustoffset → adjustoffset.py} +7 -2
  118. rapidtide/scripts/{aligntcs → aligntcs.py} +7 -2
  119. rapidtide/scripts/{applydlfilter → applydlfilter.py} +7 -2
  120. rapidtide/scripts/applyppgproc.py +28 -0
  121. rapidtide/scripts/{atlasaverage → atlasaverage.py} +7 -2
  122. rapidtide/scripts/{atlastool → atlastool.py} +7 -2
  123. rapidtide/scripts/{calcicc → calcicc.py} +7 -2
  124. rapidtide/scripts/{calctexticc → calctexticc.py} +7 -2
  125. rapidtide/scripts/{calcttest → calcttest.py} +7 -2
  126. rapidtide/scripts/{ccorrica → ccorrica.py} +7 -2
  127. rapidtide/scripts/delayvar.py +28 -0
  128. rapidtide/scripts/{diffrois → diffrois.py} +7 -2
  129. rapidtide/scripts/{endtidalproc → endtidalproc.py} +7 -2
  130. rapidtide/scripts/{fdica → fdica.py} +7 -2
  131. rapidtide/scripts/{filtnifti → filtnifti.py} +7 -2
  132. rapidtide/scripts/{filttc → filttc.py} +7 -2
  133. rapidtide/scripts/{fingerprint → fingerprint.py} +20 -16
  134. rapidtide/scripts/{fixtr → fixtr.py} +7 -2
  135. rapidtide/scripts/{gmscalc → gmscalc.py} +7 -2
  136. rapidtide/scripts/{happy → happy.py} +7 -2
  137. rapidtide/scripts/{happy2std → happy2std.py} +7 -2
  138. rapidtide/scripts/{happywarp → happywarp.py} +8 -4
  139. rapidtide/scripts/{histnifti → histnifti.py} +7 -2
  140. rapidtide/scripts/{histtc → histtc.py} +7 -2
  141. rapidtide/scripts/{glmfilt → linfitfilt.py} +7 -4
  142. rapidtide/scripts/{localflow → localflow.py} +7 -2
  143. rapidtide/scripts/{mergequality → mergequality.py} +7 -2
  144. rapidtide/scripts/{pairproc → pairproc.py} +7 -2
  145. rapidtide/scripts/{pairwisemergenifti → pairwisemergenifti.py} +7 -2
  146. rapidtide/scripts/{physiofreq → physiofreq.py} +7 -2
  147. rapidtide/scripts/{pixelcomp → pixelcomp.py} +7 -2
  148. rapidtide/scripts/{plethquality → plethquality.py} +7 -2
  149. rapidtide/scripts/{polyfitim → polyfitim.py} +7 -2
  150. rapidtide/scripts/{proj2flow → proj2flow.py} +7 -2
  151. rapidtide/scripts/{rankimage → rankimage.py} +7 -2
  152. rapidtide/scripts/{rapidtide → rapidtide.py} +7 -2
  153. rapidtide/scripts/{rapidtide2std → rapidtide2std.py} +7 -2
  154. rapidtide/scripts/{resamplenifti → resamplenifti.py} +7 -2
  155. rapidtide/scripts/{resampletc → resampletc.py} +7 -2
  156. rapidtide/scripts/retrolagtcs.py +28 -0
  157. rapidtide/scripts/retroregress.py +28 -0
  158. rapidtide/scripts/{roisummarize → roisummarize.py} +7 -2
  159. rapidtide/scripts/{runqualitycheck → runqualitycheck.py} +7 -2
  160. rapidtide/scripts/{showarbcorr → showarbcorr.py} +7 -2
  161. rapidtide/scripts/{showhist → showhist.py} +7 -2
  162. rapidtide/scripts/{showstxcorr → showstxcorr.py} +7 -2
  163. rapidtide/scripts/{showtc → showtc.py} +7 -2
  164. rapidtide/scripts/{showxcorr_legacy → showxcorr_legacy.py} +8 -8
  165. rapidtide/scripts/{showxcorrx → showxcorrx.py} +7 -2
  166. rapidtide/scripts/{showxy → showxy.py} +7 -2
  167. rapidtide/scripts/{simdata → simdata.py} +7 -2
  168. rapidtide/scripts/{spatialdecomp → spatialdecomp.py} +7 -2
  169. rapidtide/scripts/{spatialfit → spatialfit.py} +7 -2
  170. rapidtide/scripts/{spatialmi → spatialmi.py} +7 -2
  171. rapidtide/scripts/{spectrogram → spectrogram.py} +7 -2
  172. rapidtide/scripts/stupidramtricks.py +238 -0
  173. rapidtide/scripts/{synthASL → synthASL.py} +7 -2
  174. rapidtide/scripts/{tcfrom2col → tcfrom2col.py} +7 -2
  175. rapidtide/scripts/{tcfrom3col → tcfrom3col.py} +7 -2
  176. rapidtide/scripts/{temporaldecomp → temporaldecomp.py} +7 -2
  177. rapidtide/scripts/{testhrv → testhrv.py} +1 -1
  178. rapidtide/scripts/{threeD → threeD.py} +7 -2
  179. rapidtide/scripts/{tidepool → tidepool.py} +7 -2
  180. rapidtide/scripts/{variabilityizer → variabilityizer.py} +7 -2
  181. rapidtide/simFuncClasses.py +2113 -0
  182. rapidtide/simfuncfit.py +312 -108
  183. rapidtide/stats.py +579 -247
  184. rapidtide/tests/.coveragerc +27 -6
  185. rapidtide-2.9.5.data/scripts/fdica → rapidtide/tests/cleanposttest +4 -6
  186. rapidtide/tests/happycomp +9 -0
  187. rapidtide/tests/resethappytargets +1 -1
  188. rapidtide/tests/resetrapidtidetargets +1 -1
  189. rapidtide/tests/resettargets +1 -1
  190. rapidtide/tests/runlocaltest +3 -3
  191. rapidtide/tests/showkernels +1 -1
  192. rapidtide/tests/test_aliasedcorrelate.py +4 -4
  193. rapidtide/tests/test_aligntcs.py +1 -1
  194. rapidtide/tests/test_calcicc.py +1 -1
  195. rapidtide/tests/test_cleanregressor.py +184 -0
  196. rapidtide/tests/test_congrid.py +70 -81
  197. rapidtide/tests/test_correlate.py +1 -1
  198. rapidtide/tests/test_corrpass.py +4 -4
  199. rapidtide/tests/test_delayestimation.py +54 -59
  200. rapidtide/tests/test_dlfiltertorch.py +437 -0
  201. rapidtide/tests/test_doresample.py +2 -2
  202. rapidtide/tests/test_externaltools.py +69 -0
  203. rapidtide/tests/test_fastresampler.py +9 -5
  204. rapidtide/tests/test_filter.py +96 -57
  205. rapidtide/tests/test_findmaxlag.py +50 -19
  206. rapidtide/tests/test_fullrunhappy_v1.py +15 -10
  207. rapidtide/tests/test_fullrunhappy_v2.py +19 -13
  208. rapidtide/tests/test_fullrunhappy_v3.py +28 -13
  209. rapidtide/tests/test_fullrunhappy_v4.py +30 -11
  210. rapidtide/tests/test_fullrunhappy_v5.py +62 -0
  211. rapidtide/tests/test_fullrunrapidtide_v1.py +61 -7
  212. rapidtide/tests/test_fullrunrapidtide_v2.py +27 -15
  213. rapidtide/tests/test_fullrunrapidtide_v3.py +28 -8
  214. rapidtide/tests/test_fullrunrapidtide_v4.py +16 -8
  215. rapidtide/tests/test_fullrunrapidtide_v5.py +15 -6
  216. rapidtide/tests/test_fullrunrapidtide_v6.py +142 -0
  217. rapidtide/tests/test_fullrunrapidtide_v7.py +114 -0
  218. rapidtide/tests/test_fullrunrapidtide_v8.py +66 -0
  219. rapidtide/tests/test_getparsers.py +158 -0
  220. rapidtide/tests/test_io.py +59 -18
  221. rapidtide/tests/{test_glmpass.py → test_linfitfiltpass.py} +10 -10
  222. rapidtide/tests/test_mi.py +1 -1
  223. rapidtide/tests/test_miscmath.py +1 -1
  224. rapidtide/tests/test_motionregress.py +5 -5
  225. rapidtide/tests/test_nullcorr.py +6 -9
  226. rapidtide/tests/test_padvec.py +216 -0
  227. rapidtide/tests/test_parserfuncs.py +101 -0
  228. rapidtide/tests/test_phaseanalysis.py +1 -1
  229. rapidtide/tests/test_rapidtideparser.py +59 -53
  230. rapidtide/tests/test_refinedelay.py +296 -0
  231. rapidtide/tests/test_runmisc.py +5 -5
  232. rapidtide/tests/test_sharedmem.py +60 -0
  233. rapidtide/tests/test_simroundtrip.py +132 -0
  234. rapidtide/tests/test_simulate.py +1 -1
  235. rapidtide/tests/test_stcorrelate.py +4 -2
  236. rapidtide/tests/test_timeshift.py +2 -2
  237. rapidtide/tests/test_valtoindex.py +1 -1
  238. rapidtide/tests/test_zRapidtideDataset.py +5 -3
  239. rapidtide/tests/utils.py +10 -9
  240. rapidtide/tidepoolTemplate.py +88 -70
  241. rapidtide/tidepoolTemplate.ui +60 -46
  242. rapidtide/tidepoolTemplate_alt.py +88 -53
  243. rapidtide/tidepoolTemplate_alt.ui +62 -52
  244. rapidtide/tidepoolTemplate_alt_qt6.py +921 -0
  245. rapidtide/tidepoolTemplate_big.py +1125 -0
  246. rapidtide/tidepoolTemplate_big.ui +2386 -0
  247. rapidtide/tidepoolTemplate_big_qt6.py +1129 -0
  248. rapidtide/tidepoolTemplate_qt6.py +793 -0
  249. rapidtide/util.py +1389 -148
  250. rapidtide/voxelData.py +1048 -0
  251. rapidtide/wiener.py +138 -25
  252. rapidtide/wiener2.py +114 -8
  253. rapidtide/workflows/adjustoffset.py +107 -5
  254. rapidtide/workflows/aligntcs.py +86 -3
  255. rapidtide/workflows/applydlfilter.py +231 -89
  256. rapidtide/workflows/applyppgproc.py +540 -0
  257. rapidtide/workflows/atlasaverage.py +309 -48
  258. rapidtide/workflows/atlastool.py +130 -9
  259. rapidtide/workflows/calcSimFuncMap.py +490 -0
  260. rapidtide/workflows/calctexticc.py +202 -10
  261. rapidtide/workflows/ccorrica.py +123 -15
  262. rapidtide/workflows/cleanregressor.py +415 -0
  263. rapidtide/workflows/delayvar.py +1268 -0
  264. rapidtide/workflows/diffrois.py +84 -6
  265. rapidtide/workflows/endtidalproc.py +149 -9
  266. rapidtide/workflows/fdica.py +197 -17
  267. rapidtide/workflows/filtnifti.py +71 -4
  268. rapidtide/workflows/filttc.py +76 -5
  269. rapidtide/workflows/fitSimFuncMap.py +578 -0
  270. rapidtide/workflows/fixtr.py +74 -4
  271. rapidtide/workflows/gmscalc.py +116 -6
  272. rapidtide/workflows/happy.py +1242 -480
  273. rapidtide/workflows/happy2std.py +145 -13
  274. rapidtide/workflows/happy_parser.py +277 -59
  275. rapidtide/workflows/histnifti.py +120 -4
  276. rapidtide/workflows/histtc.py +85 -4
  277. rapidtide/workflows/{glmfilt.py → linfitfilt.py} +128 -14
  278. rapidtide/workflows/localflow.py +329 -29
  279. rapidtide/workflows/mergequality.py +80 -4
  280. rapidtide/workflows/niftidecomp.py +323 -19
  281. rapidtide/workflows/niftistats.py +178 -8
  282. rapidtide/workflows/pairproc.py +99 -5
  283. rapidtide/workflows/pairwisemergenifti.py +86 -3
  284. rapidtide/workflows/parser_funcs.py +1488 -56
  285. rapidtide/workflows/physiofreq.py +139 -12
  286. rapidtide/workflows/pixelcomp.py +211 -9
  287. rapidtide/workflows/plethquality.py +105 -23
  288. rapidtide/workflows/polyfitim.py +159 -19
  289. rapidtide/workflows/proj2flow.py +76 -3
  290. rapidtide/workflows/rankimage.py +115 -8
  291. rapidtide/workflows/rapidtide.py +1833 -1919
  292. rapidtide/workflows/rapidtide2std.py +101 -3
  293. rapidtide/workflows/rapidtide_parser.py +607 -372
  294. rapidtide/workflows/refineDelayMap.py +249 -0
  295. rapidtide/workflows/refineRegressor.py +1215 -0
  296. rapidtide/workflows/regressfrommaps.py +308 -0
  297. rapidtide/workflows/resamplenifti.py +86 -4
  298. rapidtide/workflows/resampletc.py +92 -4
  299. rapidtide/workflows/retrolagtcs.py +442 -0
  300. rapidtide/workflows/retroregress.py +1501 -0
  301. rapidtide/workflows/roisummarize.py +176 -7
  302. rapidtide/workflows/runqualitycheck.py +72 -7
  303. rapidtide/workflows/showarbcorr.py +172 -16
  304. rapidtide/workflows/showhist.py +87 -3
  305. rapidtide/workflows/showstxcorr.py +161 -4
  306. rapidtide/workflows/showtc.py +172 -10
  307. rapidtide/workflows/showxcorrx.py +250 -62
  308. rapidtide/workflows/showxy.py +186 -16
  309. rapidtide/workflows/simdata.py +418 -112
  310. rapidtide/workflows/spatialfit.py +83 -8
  311. rapidtide/workflows/spatialmi.py +252 -29
  312. rapidtide/workflows/spectrogram.py +306 -33
  313. rapidtide/workflows/synthASL.py +157 -6
  314. rapidtide/workflows/tcfrom2col.py +77 -3
  315. rapidtide/workflows/tcfrom3col.py +75 -3
  316. rapidtide/workflows/tidepool.py +3829 -666
  317. rapidtide/workflows/utils.py +45 -19
  318. rapidtide/workflows/utils_doc.py +293 -0
  319. rapidtide/workflows/variabilityizer.py +118 -5
  320. {rapidtide-2.9.5.dist-info → rapidtide-3.1.3.dist-info}/METADATA +30 -223
  321. rapidtide-3.1.3.dist-info/RECORD +393 -0
  322. {rapidtide-2.9.5.dist-info → rapidtide-3.1.3.dist-info}/WHEEL +1 -1
  323. rapidtide-3.1.3.dist-info/entry_points.txt +65 -0
  324. rapidtide-3.1.3.dist-info/top_level.txt +2 -0
  325. rapidtide/calcandfitcorrpairs.py +0 -262
  326. rapidtide/data/examples/src/testoutputsize +0 -45
  327. rapidtide/data/models/model_revised/model.h5 +0 -0
  328. rapidtide/data/models/model_serdar/model.h5 +0 -0
  329. rapidtide/data/models/model_serdar2/model.h5 +0 -0
  330. rapidtide/data/reference/ASPECTS_nlin_asym_09c_2mm.nii.gz +0 -0
  331. rapidtide/data/reference/ASPECTS_nlin_asym_09c_2mm_mask.nii.gz +0 -0
  332. rapidtide/data/reference/ATTbasedFlowTerritories_split_nlin_asym_09c_2mm.nii.gz +0 -0
  333. rapidtide/data/reference/ATTbasedFlowTerritories_split_nlin_asym_09c_2mm_mask.nii.gz +0 -0
  334. rapidtide/data/reference/HCP1200_binmask_2mm_2009c_asym.nii.gz +0 -0
  335. rapidtide/data/reference/HCP1200_lag_2mm_2009c_asym.nii.gz +0 -0
  336. rapidtide/data/reference/HCP1200_mask_2mm_2009c_asym.nii.gz +0 -0
  337. rapidtide/data/reference/HCP1200_negmask_2mm_2009c_asym.nii.gz +0 -0
  338. rapidtide/data/reference/HCP1200_sigma_2mm_2009c_asym.nii.gz +0 -0
  339. rapidtide/data/reference/HCP1200_strength_2mm_2009c_asym.nii.gz +0 -0
  340. rapidtide/glmpass.py +0 -434
  341. rapidtide/refine_factored.py +0 -641
  342. rapidtide/scripts/retroglm +0 -23
  343. rapidtide/workflows/glmfrommaps.py +0 -202
  344. rapidtide/workflows/retroglm.py +0 -643
  345. rapidtide-2.9.5.data/scripts/adjustoffset +0 -23
  346. rapidtide-2.9.5.data/scripts/aligntcs +0 -23
  347. rapidtide-2.9.5.data/scripts/applydlfilter +0 -23
  348. rapidtide-2.9.5.data/scripts/atlasaverage +0 -23
  349. rapidtide-2.9.5.data/scripts/atlastool +0 -23
  350. rapidtide-2.9.5.data/scripts/calcicc +0 -22
  351. rapidtide-2.9.5.data/scripts/calctexticc +0 -23
  352. rapidtide-2.9.5.data/scripts/calcttest +0 -22
  353. rapidtide-2.9.5.data/scripts/ccorrica +0 -23
  354. rapidtide-2.9.5.data/scripts/diffrois +0 -23
  355. rapidtide-2.9.5.data/scripts/endtidalproc +0 -23
  356. rapidtide-2.9.5.data/scripts/filtnifti +0 -23
  357. rapidtide-2.9.5.data/scripts/filttc +0 -23
  358. rapidtide-2.9.5.data/scripts/fingerprint +0 -593
  359. rapidtide-2.9.5.data/scripts/fixtr +0 -23
  360. rapidtide-2.9.5.data/scripts/glmfilt +0 -24
  361. rapidtide-2.9.5.data/scripts/gmscalc +0 -22
  362. rapidtide-2.9.5.data/scripts/happy +0 -25
  363. rapidtide-2.9.5.data/scripts/happy2std +0 -23
  364. rapidtide-2.9.5.data/scripts/happywarp +0 -350
  365. rapidtide-2.9.5.data/scripts/histnifti +0 -23
  366. rapidtide-2.9.5.data/scripts/histtc +0 -23
  367. rapidtide-2.9.5.data/scripts/localflow +0 -23
  368. rapidtide-2.9.5.data/scripts/mergequality +0 -23
  369. rapidtide-2.9.5.data/scripts/pairproc +0 -23
  370. rapidtide-2.9.5.data/scripts/pairwisemergenifti +0 -23
  371. rapidtide-2.9.5.data/scripts/physiofreq +0 -23
  372. rapidtide-2.9.5.data/scripts/pixelcomp +0 -23
  373. rapidtide-2.9.5.data/scripts/plethquality +0 -23
  374. rapidtide-2.9.5.data/scripts/polyfitim +0 -23
  375. rapidtide-2.9.5.data/scripts/proj2flow +0 -23
  376. rapidtide-2.9.5.data/scripts/rankimage +0 -23
  377. rapidtide-2.9.5.data/scripts/rapidtide +0 -23
  378. rapidtide-2.9.5.data/scripts/rapidtide2std +0 -23
  379. rapidtide-2.9.5.data/scripts/resamplenifti +0 -23
  380. rapidtide-2.9.5.data/scripts/resampletc +0 -23
  381. rapidtide-2.9.5.data/scripts/retroglm +0 -23
  382. rapidtide-2.9.5.data/scripts/roisummarize +0 -23
  383. rapidtide-2.9.5.data/scripts/runqualitycheck +0 -23
  384. rapidtide-2.9.5.data/scripts/showarbcorr +0 -23
  385. rapidtide-2.9.5.data/scripts/showhist +0 -23
  386. rapidtide-2.9.5.data/scripts/showstxcorr +0 -23
  387. rapidtide-2.9.5.data/scripts/showtc +0 -23
  388. rapidtide-2.9.5.data/scripts/showxcorr_legacy +0 -536
  389. rapidtide-2.9.5.data/scripts/showxcorrx +0 -23
  390. rapidtide-2.9.5.data/scripts/showxy +0 -23
  391. rapidtide-2.9.5.data/scripts/simdata +0 -23
  392. rapidtide-2.9.5.data/scripts/spatialdecomp +0 -23
  393. rapidtide-2.9.5.data/scripts/spatialfit +0 -23
  394. rapidtide-2.9.5.data/scripts/spatialmi +0 -23
  395. rapidtide-2.9.5.data/scripts/spectrogram +0 -23
  396. rapidtide-2.9.5.data/scripts/synthASL +0 -23
  397. rapidtide-2.9.5.data/scripts/tcfrom2col +0 -23
  398. rapidtide-2.9.5.data/scripts/tcfrom3col +0 -23
  399. rapidtide-2.9.5.data/scripts/temporaldecomp +0 -23
  400. rapidtide-2.9.5.data/scripts/threeD +0 -236
  401. rapidtide-2.9.5.data/scripts/tidepool +0 -23
  402. rapidtide-2.9.5.data/scripts/variabilityizer +0 -23
  403. rapidtide-2.9.5.dist-info/RECORD +0 -357
  404. rapidtide-2.9.5.dist-info/top_level.txt +0 -86
  405. {rapidtide-2.9.5.dist-info → rapidtide-3.1.3.dist-info/licenses}/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env python
2
2
  # -*- coding: utf-8 -*-
3
3
  #
4
- # Copyright 2018-2024 Blaise Frederick
4
+ # Copyright 2018-2025 Blaise Frederick
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -16,31 +16,34 @@
16
16
  # limitations under the License.
17
17
  #
18
18
  #
19
- import copy
20
19
  import os
21
20
  import platform
22
21
  import sys
23
22
  import time
24
23
  import warnings
24
+ from pathlib import Path
25
+ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
25
26
 
26
27
  import numpy as np
27
- from tqdm import tqdm
28
+ from numpy.typing import NDArray
28
29
 
29
30
  import rapidtide.correlate as tide_corr
30
31
  import rapidtide.filter as tide_filt
31
32
  import rapidtide.fit as tide_fit
32
- import rapidtide.glmpass as tide_glmpass
33
33
  import rapidtide.happy_supportfuncs as happy_support
34
- import rapidtide.helper_classes as tide_classes
35
34
  import rapidtide.io as tide_io
35
+ import rapidtide.linfitfiltpass as tide_linfitfiltpass
36
36
  import rapidtide.maskutil as tide_mask
37
37
  import rapidtide.miscmath as tide_math
38
38
  import rapidtide.resample as tide_resample
39
39
  import rapidtide.stats as tide_stats
40
40
  import rapidtide.util as tide_util
41
+ import rapidtide.voxelData as tide_voxelData
41
42
 
42
43
  from .utils import setup_logger
43
44
 
45
+ MIN_PULSATILITY_VOX_PCT = 7.0
46
+
44
47
  warnings.simplefilter(action="ignore", category=FutureWarning)
45
48
 
46
49
  try:
@@ -50,43 +53,134 @@ try:
50
53
  except ImportError:
51
54
  mklexists = False
52
55
 
53
- try:
54
- import rapidtide.dlfilter as tide_dlfilt
55
-
56
- dlfilterexists = True
57
- print("dlfilter exists")
58
- except ImportError:
59
- dlfilterexists = False
60
- print("dlfilter does not exist")
61
-
62
56
 
63
- def happy_main(argparsingfunc):
57
+ def happy_main(argparsingfunc: Any) -> None:
58
+ """
59
+ Process fMRI data to remove cardiac noise using temporal and/or spatial regression.
60
+
61
+ This function performs cardiac noise regression on fMRI data using either
62
+ temporal or spatial regression methods. It calculates cardiac noise signals
63
+ from the cardiac phase information and removes them from the fMRI data.
64
+
65
+ Parameters
66
+ ----------
67
+ args : argparse.Namespace
68
+ Command line arguments containing processing options
69
+ infodict : dict
70
+ Dictionary containing processing information including shared memory settings
71
+ fmri_data : NDArray
72
+ 4D fMRI data array (x, y, z, time)
73
+ mask : NDArray
74
+ 3D mask array indicating valid voxels
75
+ projmask_byslice : NDArray
76
+ 2D mask array for each slice indicating valid projection locations
77
+ cardphasevals : NDArray
78
+ 2D array of cardiac phase values for each slice and timepoint
79
+ rawapp_byslice : NDArray
80
+ 3D array of raw cardiac signals for each slice and timepoint
81
+ outphases : NDArray
82
+ Array of cardiac phases
83
+ timepoints : int
84
+ Number of timepoints in the fMRI data
85
+ xsize : int
86
+ X dimension of the fMRI data
87
+ ysize : int
88
+ Y dimension of the fMRI data
89
+ numslices : int
90
+ Number of slices in the fMRI data
91
+ outputroot : str
92
+ Root name for output files
93
+ tide_util : module
94
+ Utility module for memory management and other operations
95
+ tide_io : module
96
+ I/O module for reading/writing data
97
+ tide_linfitfiltpass : module
98
+ Linear fitting and filtering module
99
+ tide_filt : module
100
+ Filtering module
101
+ platform : module
102
+ Platform information module
103
+ Path : class
104
+ Path class from pathlib module
105
+
106
+ Returns
107
+ -------
108
+ None
109
+ Function performs in-place processing and saves results to files
110
+
111
+ Notes
112
+ -----
113
+ This function performs multiple steps:
114
+ 1. Calculates cardiac noise signals from cardiac phase information
115
+ 2. Applies temporal or spatial regression to remove cardiac noise
116
+ 3. Saves filtered data and regression coefficients
117
+ 4. Handles shared memory cleanup when needed
118
+
119
+ Examples
120
+ --------
121
+ >>> process_fmri_with_cardiac_regression(
122
+ ... args, infodict, fmri_data, mask, projmask_byslice,
123
+ ... cardphasevals, rawapp_byslice, outphases,
124
+ ... timepoints, xsize, ysize, numslices,
125
+ ... outputroot, tide_util, tide_io, tide_linfitfiltpass,
126
+ ... tide_filt, platform, Path
127
+ ... )
128
+ """
64
129
  timings = [["Start", time.time(), None, None]]
65
130
 
66
131
  # get the command line parameters
67
132
  args = argparsingfunc
68
133
  infodict = vars(args)
69
134
 
135
+ if args.usepytorch:
136
+ try:
137
+ import rapidtide.dlfiltertorch as tide_dlfilt
138
+
139
+ dlfilterexists = True
140
+ print("pytorch dlfilter initialized")
141
+ except ImportError:
142
+ dlfilterexists = False
143
+ print("pytorch dlfilter could not be initialized")
144
+ else:
145
+ try:
146
+ import rapidtide.dlfilter as tide_dlfilt
147
+
148
+ dlfilterexists = True
149
+ print("tensorflow dlfilter initialized")
150
+ except ImportError:
151
+ dlfilterexists = False
152
+ print("tensorflow dlfilter could not be initialized")
153
+
70
154
  fmrifilename = args.fmrifilename
71
155
  slicetimename = args.slicetimename
72
156
  outputroot = args.outputroot
73
157
 
74
- # if we are running in a Docker container, make sure we enforce memory limits properly
75
- try:
76
- testval = os.environ["IS_DOCKER_8395080871"]
77
- except KeyError:
78
- args.runningindocker = False
79
- else:
80
- args.runningindocker = True
81
- args.dockermemfree, args.dockermemswap = tide_util.findavailablemem()
82
- tide_util.setmemlimit(args.dockermemfree)
158
+ # delete the old completion file, if present
159
+ Path(f"{outputroot}_DONE.txt").unlink(missing_ok=True)
160
+
161
+ # create the canary file
162
+ Path(f"{outputroot}_ISRUNNING.txt").touch()
163
+
164
+ # if running in Docker or Apptainer/Singularity, this is necessary to enforce memory limits properly
165
+ # otherwise likely to error out in gzip.py or at voxelnormalize step. But do nothing if running in CircleCI
166
+ # because it does NOT like you messing with the container.
167
+ args.containertype = tide_util.checkifincontainer()
168
+ if args.containertype is not None:
169
+ if args.containertype != "CircleCI":
170
+ args.containermemfree, args.containermemswap = tide_util.findavailablemem()
171
+ tide_util.setmemlimit(args.containermemfree)
172
+ else:
173
+ print("running in CircleCI environment - not messing with memory")
174
+
175
+ # load pyfftw wisdom
176
+ args.pyfftw_wisdom = tide_util.configurepyfftw()
83
177
 
84
178
  # Set up loggers for workflow
85
179
  setup_logger(
86
180
  logger_filename=f"{outputroot}_log.txt",
87
- timing_filename=f"{outputroot}_runtimings.tsv",
88
- memory_filename=f"{outputroot}_memusage.tsv",
89
- verbose=args.verbose,
181
+ timing_filename=f"{outputroot}_desc-runtimings.tsv",
182
+ memory_filename=f"{outputroot}_desc-memusage.tsv",
183
+ isverbose=args.verbose,
90
184
  debug=args.debug,
91
185
  )
92
186
 
@@ -107,6 +201,8 @@ def happy_main(argparsingfunc):
107
201
  "***********************************************************************************************************************************")
108
202
  print("")"""
109
203
 
204
+ infodict["pid"] = os.getpid()
205
+
110
206
  infodict["fmrifilename"] = fmrifilename
111
207
  infodict["slicetimename"] = slicetimename
112
208
  infodict["outputroot"] = outputroot
@@ -133,9 +229,15 @@ def happy_main(argparsingfunc):
133
229
  "CommandLineArgs": args.commandline,
134
230
  }
135
231
 
232
+ # decide if we need to use shared memory
233
+ if args.nprocs > 1:
234
+ infodict["sharedmem"] = True
235
+ else:
236
+ infodict["sharedmem"] = False
237
+
136
238
  # save the information file
137
239
  if args.saveinfoasjson:
138
- tide_io.writedicttojson(infodict, outputroot + "_info.json")
240
+ tide_io.writedicttojson(infodict, outputroot + "_desc-runinfo.json")
139
241
  else:
140
242
  tide_io.writedict(infodict, outputroot + "_info.txt")
141
243
 
@@ -149,8 +251,8 @@ def happy_main(argparsingfunc):
149
251
  args.mklthreads = mklmaxthreads
150
252
  mkl.set_num_threads(args.mklthreads)
151
253
 
152
- # if we are going to do a glm, make sure we are generating app matrix
153
- if (args.dotemporalglm or args.dospatialglm) and args.cardcalconly:
254
+ # if we are going to do noise regression, make sure we are generating app matrix
255
+ if (args.dotemporalregression or args.dospatialregression) and args.cardcalconly:
154
256
  print("doing glm fit requires phase projection - setting cardcalconly to False")
155
257
  args.cardcalconly = False
156
258
 
@@ -171,41 +273,58 @@ def happy_main(argparsingfunc):
171
273
 
172
274
  # read in the image data
173
275
  tide_util.logmem("before reading in fmri data")
174
- nim, nim_data, nim_hdr, thedims, thesizes = tide_io.readfromnifti(fmrifilename)
175
- input_data = tide_classes.fMRIDataset(nim_data, numskip=args.numskip)
176
- timepoints = input_data.timepoints
177
- xsize = input_data.xsize
178
- ysize = input_data.ysize
179
- numslices = input_data.numslices
180
-
181
- xdim, ydim, slicethickness, tr = tide_io.parseniftisizes(thesizes)
182
- spaceunit, timeunit = nim_hdr.get_xyzt_units()
183
- if timeunit == "msec":
184
- tr /= 1000.0
276
+ input_data = tide_voxelData.VoxelData(fmrifilename, validstart=args.numskip)
277
+ xsize, ysize, numslices, dummy = input_data.getdims()
278
+ timepoints = input_data.realtimepoints
279
+ xdim, ydim, slicethickness, tr = input_data.getsizes()
280
+ numspatiallocs = input_data.numspatiallocs
185
281
  mrsamplerate = 1.0 / tr
186
282
  print("tr is", tr, "seconds, mrsamplerate is", "{:.3f}".format(mrsamplerate))
187
- numspatiallocs = int(xsize) * int(ysize) * int(numslices)
188
283
  infodict["tr"] = tr
189
284
  infodict["mrsamplerate"] = mrsamplerate
190
285
  timings.append(["Image data read in", time.time(), None, None])
191
286
 
192
287
  # remap to space by time
193
- fmri_data = input_data.byvol()
194
- del nim_data
288
+ fmri_data = input_data.byvoxel()
289
+ print("fmri_data has shape", fmri_data.shape)
195
290
 
196
291
  # make and save a mask of the voxels to process based on image intensity
197
292
  tide_util.logmem("before mask creation")
198
- # mask = np.uint16(masking.compute_epi_mask(nim).dataobj.reshape(numspatiallocs))
199
- mask = np.uint16(tide_mask.makeepimask(nim).dataobj.reshape(numspatiallocs))
200
- validvoxels = np.where(mask > 0)[0]
201
- theheader = copy.deepcopy(nim_hdr)
202
- theheader["dim"][4] = 1
293
+ if args.processmask is not None:
294
+ mask = tide_mask.readamask(
295
+ args.processmask,
296
+ input_data.copyheader(numtimepoints=1),
297
+ xsize,
298
+ thresh=0.001,
299
+ maskname="process",
300
+ debug=args.debug,
301
+ )
302
+ mask = np.uint16(np.where(mask > 0, 1, 0).reshape(numspatiallocs))
303
+ else:
304
+ mask = np.uint16(tide_mask.makeepimask(input_data.nim).dataobj.reshape(numspatiallocs))
305
+
306
+ theheader = input_data.copyheader(numtimepoints=1)
203
307
  timings.append(["Mask created", time.time(), None, None])
204
308
  if args.outputlevel > 0:
205
- maskfilename = outputroot + "_desc-processvoxels_mask"
206
309
  bidsdict = bidsbasedict.copy()
207
- tide_io.writedicttojson(bidsdict, maskfilename + ".json")
208
- tide_io.savetonifti(mask.reshape((xsize, ysize, numslices)), theheader, maskfilename)
310
+ maplist = [
311
+ (
312
+ mask.reshape((xsize, ysize, numslices)),
313
+ "processvoxels",
314
+ "mask",
315
+ None,
316
+ "fMRI voxels processed by happy",
317
+ ),
318
+ ]
319
+ tide_io.savemaplist(
320
+ outputroot,
321
+ maplist,
322
+ None,
323
+ (xsize, ysize, numslices),
324
+ theheader,
325
+ bidsdict,
326
+ debug=args.debug,
327
+ )
209
328
  timings.append(["Mask saved", time.time(), None, None])
210
329
  mask_byslice = mask.reshape((xsize * ysize, numslices))
211
330
 
@@ -213,7 +332,7 @@ def happy_main(argparsingfunc):
213
332
  if args.projmaskname is not None:
214
333
  tide_util.logmem("before reading in projmask")
215
334
  projmask = happy_support.readextmask(
216
- args.projmaskname, nim_hdr, xsize, ysize, numslices, args.debug
335
+ args.projmaskname, input_data.nim_hdr, xsize, ysize, numslices, args.debug
217
336
  )
218
337
  # * np.float64(mask_byslice)
219
338
  projmask_byslice = projmask.reshape(xsize * ysize, numslices)
@@ -224,22 +343,33 @@ def happy_main(argparsingfunc):
224
343
  # output mask size
225
344
  validprojvoxels = np.where(projmask.reshape(numspatiallocs) > 0)[0]
226
345
  print(f"projmask has {len(validprojvoxels)} voxels above threshold.")
346
+ infodict["projmaskvoxels"] = len(validprojvoxels)
227
347
 
228
348
  # filter out motion regressors here
229
349
  if args.motionfilename is not None:
230
350
  timings.append(["Motion filtering start", time.time(), None, None])
351
+ motiondict = tide_io.readmotion(args.motionfilename, tr=tr, colspec=args.motionfilecolspec)
352
+ confoundregressors, confoundregressorlabels = tide_fit.calcexpandedregressors(
353
+ motiondict,
354
+ labels=["xtrans", "ytrans", "ztrans", "xrot", "yrot", "zrot"],
355
+ deriv=args.motfilt_deriv,
356
+ order=args.motfilt_order,
357
+ )
358
+ tide_util.disablemkl(args.nprocs)
231
359
  (motionregressors, motionregressorlabels, filtereddata, confoundr2) = (
232
- tide_glmpass.confoundregress(
233
- args.motionfilename,
360
+ tide_linfitfiltpass.confoundregress(
361
+ confoundregressors,
362
+ confoundregressorlabels,
234
363
  fmri_data[validprojvoxels, :],
235
364
  tr,
236
365
  orthogonalize=args.orthogonalize,
237
- motstart=args.motskip,
238
- motionhp=args.motionhp,
239
- motionlp=args.motionlp,
240
- deriv=args.motfilt_deriv,
366
+ tcstart=args.motskip,
367
+ tchp=args.motionhp,
368
+ tclp=args.motionlp,
369
+ nprocs=args.nprocs,
241
370
  )
242
371
  )
372
+ tide_util.enablemkl(args.mklthreads)
243
373
  if confoundr2 is None:
244
374
  print("There are no nonzero confound regressors - exiting")
245
375
  sys.exit()
@@ -258,23 +388,69 @@ def happy_main(argparsingfunc):
258
388
  append=False,
259
389
  debug=args.debug,
260
390
  )
391
+ # save motionr2 map
392
+ theheader = input_data.copyheader(numtimepoints=1)
393
+ bidsdict = bidsbasedict.copy()
394
+ outarray = np.zeros((xsize, ysize, numslices), dtype=float)
395
+ outarray.reshape(numspatiallocs)[validprojvoxels] = confoundr2
396
+ maplist = [
397
+ (
398
+ outarray.reshape((xsize, ysize, numslices)),
399
+ "motionr2",
400
+ "map",
401
+ None,
402
+ "R2 of motion regression",
403
+ ),
404
+ ]
405
+ tide_io.savemaplist(
406
+ outputroot,
407
+ maplist,
408
+ None,
409
+ (xsize, ysize, numslices),
410
+ theheader,
411
+ bidsdict,
412
+ debug=args.debug,
413
+ )
261
414
  if args.savemotionglmfilt:
262
- motionfilteredfilename = outputroot + "_desc-motionfiltered_bold"
415
+ print("prior to save - fmri_data has shape", fmri_data.shape)
263
416
  bidsdict = bidsbasedict.copy()
264
- bidsdict["Units"] = "second"
265
- tide_io.writedicttojson(bidsdict, motionfilteredfilename + ".json")
266
- tide_io.savetonifti(
267
- fmri_data.reshape((xsize, ysize, numslices, timepoints)),
417
+ maplist = [
418
+ (
419
+ fmri_data.reshape((xsize, ysize, numslices, timepoints)),
420
+ "motionfiltered",
421
+ "bold",
422
+ None,
423
+ "fMRI data after motion regression",
424
+ ),
425
+ ]
426
+ tide_io.savemaplist(
427
+ outputroot,
428
+ maplist,
429
+ None,
430
+ (xsize, ysize, numslices, timepoints),
268
431
  theheader,
269
- motionfilteredfilename,
432
+ bidsdict,
433
+ debug=args.debug,
270
434
  )
435
+
271
436
  timings.append(["Motion filtered data saved", time.time(), numspatiallocs, "voxels"])
272
437
 
273
438
  # get slice times
274
- slicetimes, normalizedtotr = tide_io.getslicetimesfromfile(slicetimename)
439
+ slicetimes, normalizedtotr, fileisbidsjson = tide_io.getslicetimesfromfile(slicetimename)
275
440
  if normalizedtotr and not args.slicetimesareinseconds:
276
441
  slicetimes *= tr
277
-
442
+ if args.teoffset is not None:
443
+ teoffset = float(args.teoffset)
444
+ else:
445
+ if fileisbidsjson:
446
+ jsoninfodict = tide_io.readdictfromjson(slicetimename)
447
+ try:
448
+ teoffset = jsoninfodict["EchoTime"]
449
+ except KeyError:
450
+ teoffset = 0.0
451
+ else:
452
+ teoffset = 0.0
453
+ infodict["teoffset"] = teoffset
278
454
  timings.append(["Slice times determined", time.time(), None, None])
279
455
 
280
456
  # normalize the input data
@@ -285,49 +461,77 @@ def happy_main(argparsingfunc):
285
461
  validprojvoxels,
286
462
  time,
287
463
  timings,
464
+ LGR=None,
465
+ mpcode=args.mpdetrend,
466
+ nprocs=args.nprocs,
288
467
  showprogressbar=args.showprogressbar,
468
+ debug=args.debug,
289
469
  )
290
470
  normdata_byslice = normdata.reshape((xsize * ysize, numslices, timepoints))
291
471
 
292
472
  # save means, medians, and mads
293
- theheader = copy.deepcopy(nim_hdr)
294
- theheader["dim"][4] = 1
295
- meansfilename = outputroot + "_desc-means_map"
296
- mediansfilename = outputroot + "_desc-medians_map"
297
- madsfilename = outputroot + "_desc-mads_map"
473
+ theheader = input_data.copyheader(numtimepoints=1)
298
474
  bidsdict = bidsbasedict.copy()
299
- tide_io.writedicttojson(bidsdict, meansfilename + ".json")
300
- tide_io.writedicttojson(bidsdict, mediansfilename + ".json")
301
- tide_io.writedicttojson(bidsdict, madsfilename + ".json")
302
- tide_io.savetonifti(means.reshape((xsize, ysize, numslices)), theheader, meansfilename)
303
- tide_io.savetonifti(medians.reshape((xsize, ysize, numslices)), theheader, mediansfilename)
304
- tide_io.savetonifti(mads.reshape((xsize, ysize, numslices)), theheader, madsfilename)
475
+ maplist = [
476
+ (
477
+ means.reshape((xsize, ysize, numslices)),
478
+ "means",
479
+ "map",
480
+ None,
481
+ "fMRI timecourse mean over time",
482
+ ),
483
+ (
484
+ medians.reshape((xsize, ysize, numslices)),
485
+ "medians",
486
+ "map",
487
+ None,
488
+ "fMRI timecourse median over time",
489
+ ),
490
+ (
491
+ mads.reshape((xsize, ysize, numslices)),
492
+ "mads",
493
+ "map",
494
+ None,
495
+ "fMRI timecourse MAD over time",
496
+ ),
497
+ ]
498
+ tide_io.savemaplist(
499
+ outputroot,
500
+ maplist,
501
+ None,
502
+ (xsize, ysize, numslices),
503
+ theheader,
504
+ bidsdict,
505
+ debug=args.debug,
506
+ )
305
507
 
306
508
  # read in estimation mask if present. Otherwise, otherwise use intensity mask.
307
- infodict["estmaskname"] = args.estmaskname
509
+ infodict["estweightsname"] = args.estweightsname
308
510
  if args.debug:
309
- print(args.estmaskname)
310
- if args.estmaskname is not None:
311
- tide_util.logmem("before reading in estmask")
312
- estmask = happy_support.readextmask(
313
- args.estmaskname, nim_hdr, xsize, ysize, numslices, args.debug
511
+ print(args.estweightsname)
512
+ if args.estweightsname is not None:
513
+ tide_util.logmem("before reading in estweights")
514
+ estweights = happy_support.readextmask(
515
+ args.estweightsname, input_data.nim_hdr, xsize, ysize, numslices, args.debug
314
516
  )
315
- # * np.float64(mask_byslice)
316
- estmask_byslice = estmask.reshape(xsize * ysize, numslices)
317
- print("using estmask from file", args.estmaskname)
517
+ estweights_byslice = estweights.reshape(xsize * ysize, numslices)
518
+ print("using estweights from file", args.estweightsname)
318
519
  numpasses = 1
319
520
  else:
320
521
  # just fall back to the intensity mask
321
- estmask_byslice = mask_byslice.astype("float64")
522
+ estweights_byslice = mask_byslice.astype("float64")
322
523
  numpasses = 2
323
524
  print("Not using separate estimation mask - doing initial estimate using intensity mask")
324
- if args.fliparteries:
325
- # add another pass to refine the waveform after getting the new appflips
525
+
526
+ # add another pass to refine the waveform after getting the new appflips
527
+ if args.fliparteries or args.doaliasedcorrelation:
326
528
  numpasses += 1
327
- print("Adding a pass to regenerate cardiac waveform using bettter appflips")
529
+ print("Adding a pass to regenerate cardiac waveform using better vessel specification")
328
530
 
329
531
  # output mask size
330
- print(f"estmask has {len(np.where(estmask_byslice[:, :] > 0)[0])} voxels above threshold.")
532
+ print(
533
+ f"estweights has {len(np.where(estweights_byslice[:, :] > 0)[0])} voxels above threshold."
534
+ )
331
535
 
332
536
  infodict["numpasses"] = numpasses
333
537
 
@@ -337,43 +541,57 @@ def happy_main(argparsingfunc):
337
541
  if numpasses > 1:
338
542
  print()
339
543
  print()
340
- print("starting pass", thispass + 1, "of", numpasses)
341
- passstring = " - pass " + str(thispass + 1)
544
+ print(f"starting pass {thispass + 1} of {numpasses}")
545
+ passstring = f" - pass {thispass + 1}"
546
+ passnamefrag = f"pass{thispass + 1}"
342
547
  else:
343
548
  passstring = ""
549
+ passnamefrag = ""
344
550
  # now get an estimate of the cardiac signal
345
551
  print("estimating cardiac signal from fmri data")
346
552
  tide_util.logmem("before cardiacfromimage")
347
- (
348
- cardfromfmri_sliceres,
349
- cardfromfmri_normfac,
350
- respfromfmri_sliceres,
351
- respfromfmri_normfac,
352
- slicesamplerate,
353
- numsteps,
354
- sliceoffsets,
355
- cycleaverage,
356
- slicenorms,
357
- ) = happy_support.cardiacfromimage(
553
+ tide_util.disablemkl(args.nprocs)
554
+
555
+ # Create configuration for cardiac extraction
556
+ cardiac_config = happy_support.CardiacExtractionConfig(
557
+ notchpct=args.notchpct,
558
+ notchrolloff=args.notchrolloff,
559
+ invertphysiosign=args.invertphysiosign,
560
+ madnorm=args.domadnorm,
561
+ nprocs=args.nprocs,
562
+ arteriesonly=args.arteriesonly,
563
+ fliparteries=args.fliparteries,
564
+ debug=args.debug,
565
+ verbose=args.verbose,
566
+ usemask=args.usemaskcardfromfmri,
567
+ multiplicative=True,
568
+ )
569
+
570
+ result = happy_support.cardiacfromimage(
358
571
  normdata_byslice,
359
- estmask_byslice,
572
+ estweights_byslice,
360
573
  numslices,
361
574
  timepoints,
362
575
  tr,
363
576
  slicetimes,
364
577
  thecardbandfilter,
365
578
  therespbandfilter,
366
- invertphysiosign=args.invertphysiosign,
367
- madnorm=args.domadnorm,
368
- nprocs=args.nprocs,
369
- notchpct=args.notchpct,
370
- fliparteries=args.fliparteries,
371
- arteriesonly=args.arteriesonly,
372
- usemask=args.usemaskcardfromfmri,
579
+ cardiac_config,
373
580
  appflips_byslice=appflips_byslice,
374
- debug=args.debug,
375
- verbose=args.verbose,
376
581
  )
582
+
583
+ # Extract results
584
+ cardfromfmri_sliceres = result.hirescardtc
585
+ cardfromfmri_normfac = result.cardnormfac
586
+ respfromfmri_sliceres = result.hiresresptc
587
+ respfromfmri_normfac = result.respnormfac
588
+ slicesamplerate = result.slicesamplerate
589
+ numsteps = result.numsteps
590
+ sliceoffsets = result.sliceoffsets
591
+ cycleaverage = result.cycleaverage
592
+ slicenorms = result.slicenorms
593
+
594
+ tide_util.enablemkl(args.mklthreads)
377
595
  timings.append(
378
596
  [
379
597
  "Cardiac signal generated from image data" + passstring,
@@ -383,43 +601,15 @@ def happy_main(argparsingfunc):
383
601
  ]
384
602
  )
385
603
  infodict["cardfromfmri_normfac"] = cardfromfmri_normfac
386
- slicetimeaxis = np.linspace(
387
- 0.0, tr * timepoints, num=(timepoints * numsteps), endpoint=False
604
+ slicetimeaxis = (
605
+ np.linspace(0.0, tr * timepoints, num=(timepoints * numsteps), endpoint=False)
606
+ + teoffset
388
607
  )
389
608
  if (thispass == 0) and args.doupsampling:
390
- # allocate the upsampled image
391
- upsampleimage = np.zeros((xsize, ysize, numslices, numsteps * timepoints), dtype=float)
392
- upsampleimage_byslice = upsampleimage.reshape(
393
- xsize * ysize, numslices, numsteps * timepoints
394
- )
395
-
396
- # drop in the raw data
397
- for theslice in range(numslices):
398
- upsampleimage[
399
- :, :, theslice, sliceoffsets[theslice] : timepoints * numsteps : numsteps
400
- ] = fmri_data.reshape((xsize, ysize, numslices, timepoints))[:, :, theslice, :]
401
-
402
- # interpolate along the slice direction
403
- for thestep in range(numsteps):
404
- print(f"interpolating step {thestep}")
405
- thesrclocs = np.where(sliceoffsets == thestep)[0]
406
- print(f"sourcelocs: {thesrclocs}")
407
- thedstlocs = np.linspace(0, numslices, num=len(sliceoffsets), endpoint=False)
408
- print(f"len(destlocst), destlocs: {len(thedstlocs)}, {thedstlocs}")
409
- for thetimepoint in range(0, timepoints * numsteps):
410
- print(f"timepoint: {thetimepoint}")
411
- for thexyvoxel in range(xsize * ysize):
412
- theinterps = np.interp(
413
- thedstlocs,
414
- 1.0 * thesrclocs,
415
- upsampleimage_byslice[thexyvoxel, thesrclocs, thetimepoint],
416
- )
417
- upsampleimage_byslice[thexyvoxel, :, thetimepoint] = 1.0 * theinterps
418
-
419
- theheader = copy.deepcopy(nim_hdr)
420
- theheader["dim"][4] = timepoints * numsteps
421
- theheader["pixdim"][4] = 1.0 / slicesamplerate
422
- tide_io.savetonifti(upsampleimage, theheader, outputroot + "_upsampled")
609
+ happy_support.upsampleimage(
610
+ input_data, numsteps, sliceoffsets, slicesamplerate, outputroot
611
+ )
612
+ sys.exit(0)
423
613
 
424
614
  if thispass == numpasses - 1:
425
615
  tide_io.writebidstsv(
@@ -438,6 +628,14 @@ def happy_main(argparsingfunc):
438
628
  append=False,
439
629
  debug=args.debug,
440
630
  )
631
+ tide_io.writebidstsv(
632
+ outputroot + "_desc-sliceresrespfromfmri_timeseries",
633
+ respfromfmri_sliceres,
634
+ slicesamplerate,
635
+ columns=["respfromfmri"],
636
+ append=False,
637
+ debug=args.debug,
638
+ )
441
639
 
442
640
  # stash away a copy of the waveform if we need it later
443
641
  raw_cardfromfmri_sliceres = np.array(cardfromfmri_sliceres)
@@ -454,6 +652,7 @@ def happy_main(argparsingfunc):
454
652
  cardiacwaveform = np.array(cardfromfmri_sliceres)
455
653
  badpointlist = np.array(thebadcardpts)
456
654
 
655
+ infodict["badptspct"] = 100.0 * np.mean(thebadcardpts)
457
656
  infodict["slicesamplerate"] = slicesamplerate
458
657
  infodict["numcardpts_sliceres"] = timepoints * numsteps
459
658
  infodict["numsteps"] = numsteps
@@ -469,6 +668,15 @@ def happy_main(argparsingfunc):
469
668
  slicesamplerate,
470
669
  columns=["cardiacfromfmri_censored"],
471
670
  append=True,
671
+ extraheaderinfo={"badptspct": infodict["badptspct"]},
672
+ debug=args.debug,
673
+ )
674
+ tide_io.writebidstsv(
675
+ outputroot + "_desc-sliceresrespfromfmri_timeseries",
676
+ respfromfmri_sliceres * (1.0 - thebadcardpts),
677
+ slicesamplerate,
678
+ columns=["respfromfmri_censored"],
679
+ append=True,
472
680
  debug=args.debug,
473
681
  )
474
682
  peakfreq_bold = happy_support.getcardcoeffs(
@@ -499,7 +707,7 @@ def happy_main(argparsingfunc):
499
707
  decimate=True,
500
708
  debug=False,
501
709
  )
502
- )
710
+ )[0]
503
711
 
504
712
  if thispass == numpasses - 1:
505
713
  tide_io.writebidstsv(
@@ -508,6 +716,7 @@ def happy_main(argparsingfunc):
508
716
  args.stdfreq,
509
717
  columns=["cardiacfromfmri_" + str(args.stdfreq) + "Hz"],
510
718
  append=False,
719
+ extraheaderinfo={"cardiacbpm_bold": float(infodict["cardiacbpm_bold"])},
511
720
  debug=args.debug,
512
721
  )
513
722
  infodict["numcardpts_stdres"] = len(cardfromfmri_stdres)
@@ -531,7 +740,7 @@ def happy_main(argparsingfunc):
531
740
  outputroot + "_desc-stdrescardfromfmri_timeseries",
532
741
  normcardfromfmri_stdres,
533
742
  args.stdfreq,
534
- columns=["normcardiac_" + str(args.stdfreq) + "Hz"],
743
+ columns=["normcardiacfromfmri_" + str(args.stdfreq) + "Hz"],
535
744
  append=True,
536
745
  debug=args.debug,
537
746
  )
@@ -573,8 +782,10 @@ def happy_main(argparsingfunc):
573
782
  )
574
783
 
575
784
  # apply the deep learning filter if we're going to do that
785
+ infodict["used_dlreconstruction_filter"] = False
576
786
  if args.dodlfilter:
577
787
  if dlfilterexists:
788
+ infodict["used_dlreconstruction_filter"] = True
578
789
  if args.mpfix:
579
790
  print("performing super dangerous openmp workaround")
580
791
  os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
@@ -621,7 +832,7 @@ def happy_main(argparsingfunc):
621
832
 
622
833
  # downsample to sliceres from stdres
623
834
  # cardfromfmri_sliceres = tide_math.madnormalize(
624
- # tide_resample.arbresample(dlfilteredcard_stdres, args.stdfreq, slicesamplerate, decimate=True, debug=False))
835
+ # tide_resample.arbresample(dlfilteredcard_stdres, args.stdfreq, slicesamplerate, decimate=True, debug=False))[0]
625
836
  stdtimeaxis = (1.0 / args.stdfreq) * np.linspace(
626
837
  0.0,
627
838
  len(dlfilteredcard_stdres),
@@ -643,7 +854,7 @@ def happy_main(argparsingfunc):
643
854
  method="univariate",
644
855
  padlen=0,
645
856
  )
646
- )
857
+ )[0]
647
858
  if thispass == numpasses - 1:
648
859
  tide_io.writebidstsv(
649
860
  outputroot + "_desc-slicerescardfromfmri_timeseries",
@@ -653,7 +864,6 @@ def happy_main(argparsingfunc):
653
864
  append=True,
654
865
  debug=args.debug,
655
866
  )
656
- infodict["used_dlreconstruction_filter"] = True
657
867
  peakfreq_dlfiltered = happy_support.getcardcoeffs(
658
868
  cardfromfmri_sliceres,
659
869
  slicesamplerate,
@@ -785,6 +995,7 @@ def happy_main(argparsingfunc):
785
995
  append=True,
786
996
  debug=args.debug,
787
997
  )
998
+
788
999
  tide_io.writebidstsv(
789
1000
  outputroot + "_desc-stdrescardfromfmri_timeseries",
790
1001
  pleth_stdres,
@@ -807,6 +1018,19 @@ def happy_main(argparsingfunc):
807
1018
  cutoff=args.envcutoff,
808
1019
  thresh=args.envthresh,
809
1020
  )
1021
+
1022
+ # calculate quality metrics
1023
+ happy_support.calcplethquality(
1024
+ filtpleth_stdres,
1025
+ args.stdfreq,
1026
+ infodict,
1027
+ "_pleth",
1028
+ outputroot,
1029
+ outputlevel=args.outputlevel,
1030
+ initfile=False,
1031
+ debug=args.debug,
1032
+ )
1033
+
810
1034
  if thispass == numpasses - 1:
811
1035
  tide_io.writebidstsv(
812
1036
  outputroot + "_desc-stdrescardfromfmri_timeseries",
@@ -816,6 +1040,14 @@ def happy_main(argparsingfunc):
816
1040
  append=True,
817
1041
  debug=args.debug,
818
1042
  )
1043
+ tide_io.writebidstsv(
1044
+ outputroot + "_desc-stdrescardfromfmri_timeseries",
1045
+ filtpleth_stdres,
1046
+ args.stdfreq,
1047
+ columns=["pleth_cleaned"],
1048
+ append=True,
1049
+ debug=args.debug,
1050
+ )
819
1051
  tide_io.writebidstsv(
820
1052
  outputroot + "_desc-stdrescardfromfmri_timeseries",
821
1053
  plethenv_stdres,
@@ -825,17 +1057,24 @@ def happy_main(argparsingfunc):
825
1057
  debug=args.debug,
826
1058
  )
827
1059
 
828
- # calculate quality metrics
829
- happy_support.calcplethquality(
1060
+ # check the match between the cleaned bold and physio cardiac signals
1061
+ maxval, maxdelay, failreason = happy_support.checkcardmatch(
830
1062
  filtpleth_stdres,
831
- args.stdfreq,
832
- infodict,
833
- "_pleth",
834
- outputroot,
835
- outputlevel=args.outputlevel,
836
- initfile=False,
1063
+ filtcardfromfmri_stdres,
1064
+ slicesamplerate,
837
1065
  debug=args.debug,
838
1066
  )
1067
+ print(
1068
+ "Input cardiac waveform delay is",
1069
+ "{:.3f}".format(maxdelay),
1070
+ )
1071
+ print(
1072
+ "Correlation coefficient between cardiac regressors is",
1073
+ "{:.3f}".format(maxval),
1074
+ )
1075
+ infodict["corrcoeff_filtraw2filtpleth"] = maxval + 0
1076
+ infodict["delay_filtraw2filtpleth"] = maxdelay + 0
1077
+ infodict["failreason_filtraw2filtpleth"] = failreason + 0
839
1078
 
840
1079
  if args.dodlfilter and dlfilterexists:
841
1080
  dlfilteredpleth = thedlfilter.apply(pleth_stdres)
@@ -895,7 +1134,7 @@ def happy_main(argparsingfunc):
895
1134
  # find key components of cardiac waveform
896
1135
  filtpleth = tide_math.madnormalize(
897
1136
  thecardbandfilter.apply(slicesamplerate, pleth_sliceres)
898
- )
1137
+ )[0]
899
1138
  peakfreq_file = happy_support.getcardcoeffs(
900
1139
  (1.0 - thebadplethpts_sliceres) * filtpleth,
901
1140
  slicesamplerate,
@@ -941,13 +1180,38 @@ def happy_main(argparsingfunc):
941
1180
  infodict["pleth"] = False
942
1181
  peakfreq = peakfreq_bold
943
1182
  if args.outputlevel > 0:
1183
+ extraheaderdict = {"badptspct": infodict["badptspct"]}
1184
+ if infodict["pleth"]:
1185
+ extraheaderdict["cardiacbpm_pleth"] = float(infodict["cardiacbpm_pleth"])
1186
+ if infodict["used_dlreconstruction_filter"]:
1187
+ extraheaderdict["cardiacbpm_dlfiltered"] = float(infodict["cardiacbpm_dlfiltered"])
944
1188
  if thispass == numpasses - 1:
945
1189
  tide_io.writebidstsv(
946
1190
  outputroot + "_desc-slicerescardfromfmri_timeseries",
947
1191
  badpointlist,
948
1192
  slicesamplerate,
949
1193
  columns=["badpts"],
1194
+ extraheaderinfo=extraheaderdict,
1195
+ append=True,
1196
+ debug=args.debug,
1197
+ )
1198
+ badpointlist_stdres = np.round(
1199
+ tide_resample.arbresample(
1200
+ badpointlist,
1201
+ slicesamplerate,
1202
+ args.stdfreq,
1203
+ decimate=True,
1204
+ debug=False,
1205
+ ),
1206
+ 0,
1207
+ )
1208
+ tide_io.writebidstsv(
1209
+ outputroot + "_desc-stdrescardfromfmri_timeseries",
1210
+ badpointlist_stdres,
1211
+ args.stdfreq,
1212
+ columns=["badpts"],
950
1213
  append=True,
1214
+ extraheaderinfo=extraheaderdict,
951
1215
  debug=args.debug,
952
1216
  )
953
1217
 
@@ -963,7 +1227,7 @@ def happy_main(argparsingfunc):
963
1227
  peakfreq,
964
1228
  ncomps=args.hilbertcomponents,
965
1229
  )
966
- )
1230
+ )[0]
967
1231
  else:
968
1232
  filthiresfund = tide_math.madnormalize(
969
1233
  happy_support.getperiodic(
@@ -972,7 +1236,7 @@ def happy_main(argparsingfunc):
972
1236
  peakfreq,
973
1237
  ncomps=args.hilbertcomponents,
974
1238
  )
975
- )
1239
+ )[0]
976
1240
  if args.outputlevel > 1:
977
1241
  if thispass == numpasses - 1:
978
1242
  tide_io.writebidstsv(
@@ -1066,7 +1330,7 @@ def happy_main(argparsingfunc):
1066
1330
  infodict["respsamplerate"] = returnedinputfreq
1067
1331
  infodict["numresppts_fullres"] = fullrespts
1068
1332
 
1069
- # account for slice time offests
1333
+ # account for slice time offsets
1070
1334
  offsets_byslice = np.zeros((xsize * ysize, numslices), dtype=np.float64)
1071
1335
  for i in range(numslices):
1072
1336
  offsets_byslice[:, i] = slicetimes[i]
@@ -1076,11 +1340,15 @@ def happy_main(argparsingfunc):
1076
1340
 
1077
1341
  # save the information file
1078
1342
  if args.saveinfoasjson:
1079
- tide_io.writedicttojson(infodict, outputroot + "_info.json")
1343
+ tide_io.writedicttojson(infodict, outputroot + "_desc-runinfo.json")
1080
1344
  else:
1081
1345
  tide_io.writedict(infodict, outputroot + "_info.txt")
1082
1346
 
1083
1347
  # interpolate the instantaneous phase
1348
+ print(f"{tr=}")
1349
+ print(f"{timepoints=}")
1350
+ print(f"{numsteps=}")
1351
+ print(f"{args.upsamplefac=}")
1084
1352
  upsampledslicetimeaxis = np.linspace(
1085
1353
  0.0,
1086
1354
  tr * timepoints,
@@ -1158,20 +1426,12 @@ def happy_main(argparsingfunc):
1158
1426
  # construct the destination arrays
1159
1427
  tide_util.logmem("before making destination arrays")
1160
1428
  app = np.zeros((xsize, ysize, numslices, args.destpoints), dtype=np.float64)
1161
- app_byslice = app.reshape((xsize * ysize, numslices, args.destpoints))
1162
1429
  cine = np.zeros((xsize, ysize, numslices, args.destpoints), dtype=np.float64)
1163
- cine_byslice = cine.reshape((xsize * ysize, numslices, args.destpoints))
1164
1430
  rawapp = np.zeros((xsize, ysize, numslices, args.destpoints), dtype=np.float64)
1165
- rawapp_byslice = rawapp.reshape((xsize * ysize, numslices, args.destpoints))
1166
1431
  corrected_rawapp = np.zeros((xsize, ysize, numslices, args.destpoints), dtype=np.float64)
1167
- corrected_rawapp_byslice = rawapp.reshape((xsize * ysize, numslices, args.destpoints))
1168
1432
  normapp = np.zeros((xsize, ysize, numslices, args.destpoints), dtype=np.float64)
1169
- normapp_byslice = normapp.reshape((xsize * ysize, numslices, args.destpoints))
1170
1433
  weights = np.zeros((xsize, ysize, numslices, args.destpoints), dtype=np.float64)
1171
- weight_byslice = weights.reshape((xsize * ysize, numslices, args.destpoints))
1172
1434
  derivatives = np.zeros((xsize, ysize, numslices, 4), dtype=np.float64)
1173
- derivatives_byslice = derivatives.reshape((xsize * ysize, numslices, 4))
1174
-
1175
1435
  timings.append(["Output arrays allocated" + passstring, time.time(), None, None])
1176
1436
 
1177
1437
  if args.centric:
@@ -1185,10 +1445,22 @@ def happy_main(argparsingfunc):
1185
1445
  # now do the phase projection
1186
1446
  #
1187
1447
  #
1448
+ app_byslice = app.reshape((xsize * ysize, numslices, args.destpoints))
1449
+ rawapp_byslice = rawapp.reshape((xsize * ysize, numslices, args.destpoints))
1450
+ corrected_rawapp_byslice = corrected_rawapp.reshape(
1451
+ (xsize * ysize, numslices, args.destpoints)
1452
+ )
1453
+ normapp_byslice = normapp.reshape((xsize * ysize, numslices, args.destpoints))
1454
+ weights_byslice = weights.reshape((xsize * ysize, numslices, args.destpoints))
1455
+ cine_byslice = cine.reshape((xsize * ysize, numslices, args.destpoints))
1456
+ derivatives_byslice = derivatives.reshape((xsize * ysize, numslices, 4))
1457
+
1188
1458
  demeandata_byslice = demeandata.reshape((xsize * ysize, numslices, timepoints))
1189
1459
  means_byslice = means.reshape((xsize * ysize, numslices))
1190
1460
 
1191
- timings.append(["Phase projection to image started" + passstring, time.time(), None, None])
1461
+ timings.append(
1462
+ ["Phase projection to image prep started" + passstring, time.time(), None, None]
1463
+ )
1192
1464
  print("Starting phase projection")
1193
1465
  proctrs = range(timepoints) # proctrs is the list of all fmri trs to be projected
1194
1466
  procpoints = range(
@@ -1202,8 +1474,16 @@ def happy_main(argparsingfunc):
1202
1474
  proctrs = np.where(censortrs < 1)[0]
1203
1475
  procpoints = np.where(censorpoints < 1)[0]
1204
1476
 
1477
+ # preload congrid
1478
+ if args.preloadcongrid:
1479
+ print("Preloading congrid values...")
1480
+ happy_support.preloadcongrid(
1481
+ outphases, args.congridbins, gridkernel=args.gridkernel, cyclic=True, debug=False
1482
+ )
1483
+ print("done")
1484
+
1205
1485
  # do phase averaging
1206
- app_bypoint, weight_bypoint = happy_support.cardiaccycleaverage(
1486
+ app_bypoint, weights_bypoint = happy_support.cardiaccycleaverage(
1207
1487
  instantaneous_cardiacphase,
1208
1488
  outphases,
1209
1489
  cardfromfmri_sliceres,
@@ -1211,6 +1491,7 @@ def happy_main(argparsingfunc):
1211
1491
  args.congridbins,
1212
1492
  args.gridkernel,
1213
1493
  args.centric,
1494
+ cache=args.congridcache,
1214
1495
  cyclic=True,
1215
1496
  )
1216
1497
  if thispass == numpasses - 1:
@@ -1225,7 +1506,7 @@ def happy_main(argparsingfunc):
1225
1506
  )
1226
1507
  tide_io.writebidstsv(
1227
1508
  outputroot + "_desc-cardiaccycleweightfromfmri_timeseries",
1228
- weight_bypoint,
1509
+ weights_bypoint,
1229
1510
  1.0 / (outphases[1] - outphases[0]),
1230
1511
  starttime=outphases[0],
1231
1512
  columns=["cardiaccycleweightfromfmri"],
@@ -1259,7 +1540,7 @@ def happy_main(argparsingfunc):
1259
1540
  timediff = minloc - maxloc
1260
1541
  zerophaselocs.append(1.0 * minloc - (minval - outphases[0]) * timediff / phasediff)
1261
1542
  # print(idx, [maxloc, maxval], [minloc, minval], phasediff, timediff, zerophaselocs[-1])
1262
- instantaneous_cardiactime = instantaneous_cardiacphase * 0.0
1543
+ instantaneous_cardiactime = np.zeros_like(instantaneous_cardiacphase)
1263
1544
 
1264
1545
  whichpeak = 0
1265
1546
  for t in procpoints:
@@ -1282,7 +1563,7 @@ def happy_main(argparsingfunc):
1282
1563
  outtimes = np.linspace(
1283
1564
  0.0, maxtime, num=int(maxtime / args.pulsereconstepsize), endpoint=False
1284
1565
  )
1285
- atp_bypoint, atpweight_bypoint = happy_support.cardiaccycleaverage(
1566
+ atp_bypoint, atpweights_bypoint = happy_support.cardiaccycleaverage(
1286
1567
  instantaneous_cardiactime,
1287
1568
  outtimes,
1288
1569
  cardfromfmri_sliceres,
@@ -1303,23 +1584,83 @@ def happy_main(argparsingfunc):
1303
1584
  debug=args.debug,
1304
1585
  )
1305
1586
 
1587
+ timings.append(
1588
+ ["Phase projection to image prep ended" + passstring, time.time(), None, None]
1589
+ )
1306
1590
  if not args.verbose:
1307
- print("Phase projecting...")
1591
+ print("Setting up for phase projection...")
1592
+
1593
+ # make a vessel map using Wright's method
1594
+ if args.wrightiterations > 0:
1595
+ timings.append(
1596
+ [
1597
+ "Wright mask generation started" + passstring,
1598
+ time.time(),
1599
+ None,
1600
+ None,
1601
+ ]
1602
+ )
1603
+ wrightcorrs = happy_support.wrightmap(
1604
+ input_data,
1605
+ demeandata_byslice,
1606
+ rawapp_byslice,
1607
+ projmask_byslice,
1608
+ outphases,
1609
+ cardphasevals,
1610
+ proctrs,
1611
+ args.congridbins,
1612
+ args.gridkernel,
1613
+ args.destpoints,
1614
+ iterations=args.wrightiterations,
1615
+ nprocs=args.nprocs,
1616
+ verbose=False,
1617
+ debug=args.debug,
1618
+ )
1619
+
1620
+ theheader = input_data.copyheader(numtimepoints=1)
1621
+ maplist = [
1622
+ (
1623
+ wrightcorrs,
1624
+ f"wrightcorrspass{passnamefrag}",
1625
+ "map",
1626
+ None,
1627
+ "fMRI timecourse MAD over time",
1628
+ ),
1629
+ ]
1630
+ tide_io.savemaplist(
1631
+ outputroot,
1632
+ maplist,
1633
+ None,
1634
+ (xsize, ysize, numslices),
1635
+ theheader,
1636
+ bidsdict,
1637
+ debug=args.debug,
1638
+ )
1639
+ timings.append(
1640
+ [
1641
+ "Wright mask generation completed" + passstring,
1642
+ time.time(),
1643
+ None,
1644
+ None,
1645
+ ]
1646
+ )
1308
1647
 
1648
+ timings.append(["Phase projection to image started" + passstring, time.time(), None, None])
1309
1649
  # make a lowpass filter for the projected data. Limit frequency to 3 cycles per 2pi (1/6th Fs)
1310
1650
  phaseFs = 1.0 / phasestep
1311
1651
  phaseFc = phaseFs / 6.0
1312
- appsmoothingfilter = tide_filt.NoncausalFilter("arb", cyclic=True, padtime=0.0)
1652
+ appsmoothingfilter = tide_filt.NoncausalFilter("arb", padtime=0.0)
1313
1653
  appsmoothingfilter.setfreqs(0.0, 0.0, phaseFc, phaseFc)
1314
1654
 
1315
1655
  # setup for aliased correlation if we're going to do it
1316
- if args.doaliasedcorrelation and (thispass == numpasses - 1):
1656
+ if args.doaliasedcorrelation:
1317
1657
  if args.cardiacfilename and False:
1318
1658
  signal_sliceres = pleth_sliceres
1319
- # signal_stdres = pleth_stdres
1320
1659
  else:
1321
1660
  signal_sliceres = cardfromfmri_sliceres
1322
- # signal_stdres = dlfilteredcard_stdres
1661
+
1662
+ # zero out bad points
1663
+ signal_sliceres *= 1.0 - badpointlist
1323
1664
 
1324
1665
  theAliasedCorrelator = tide_corr.AliasedCorrelator(
1325
1666
  signal_sliceres,
@@ -1332,120 +1673,64 @@ def happy_main(argparsingfunc):
1332
1673
  )
1333
1674
  correndloc = tide_util.valtoindex(thealiasedcorrx, args.aliasedcorrelationwidth / 2.0)
1334
1675
  aliasedcorrelationpts = correndloc - corrstartloc + 1
1335
- thecorrfunc = np.zeros(
1336
- (xsize, ysize, numslices, aliasedcorrelationpts), dtype=np.float64
1337
- )
1338
- thecorrfunc_byslice = thecorrfunc.reshape(
1339
- (xsize * ysize, numslices, aliasedcorrelationpts)
1340
- )
1341
- wavedelay = np.zeros((xsize, ysize, numslices), dtype=np.float64)
1342
- wavedelay_byslice = wavedelay.reshape((xsize * ysize, numslices))
1343
- waveamp = np.zeros((xsize, ysize, numslices), dtype=np.float64)
1344
- waveamp_byslice = waveamp.reshape((xsize * ysize, numslices))
1345
-
1346
- # now project the data
1347
- fmri_data_byslice = input_data.byslice()
1348
- for theslice in tqdm(
1349
- range(numslices),
1350
- desc="Slice",
1351
- unit="slices",
1352
- disable=(not args.showprogressbar),
1353
- ):
1354
- if args.verbose:
1355
- print("Phase projecting for slice", theslice)
1356
- validlocs = np.where(projmask_byslice[:, theslice] > 0)[0]
1357
- # indexlist = range(0, len(cardphasevals[theslice, :]))
1358
- if len(validlocs) > 0:
1359
- for t in proctrs:
1360
- filteredmr = -demeandata_byslice[validlocs, theslice, t]
1361
- cinemr = fmri_data_byslice[validlocs, theslice, t]
1362
- thevals, theweights, theindices = tide_resample.congrid(
1363
- outphases,
1364
- cardphasevals[theslice, t],
1365
- 1.0,
1366
- args.congridbins,
1367
- kernel=args.gridkernel,
1368
- cyclic=True,
1369
- )
1370
- for i in range(len(theindices)):
1371
- weight_byslice[validlocs, theslice, theindices[i]] += theweights[i]
1372
- # rawapp_byslice[validlocs, theslice, theindices[i]] += (
1373
- # theweights[i] * filteredmr
1374
- # )
1375
- rawapp_byslice[validlocs, theslice, theindices[i]] += filteredmr
1376
- cine_byslice[validlocs, theslice, theindices[i]] += theweights[i] * cinemr
1377
- for d in range(args.destpoints):
1378
- if weight_byslice[validlocs[0], theslice, d] == 0.0:
1379
- weight_byslice[validlocs, theslice, d] = 1.0
1380
- rawapp_byslice[validlocs, theslice, :] = np.nan_to_num(
1381
- rawapp_byslice[validlocs, theslice, :] / weight_byslice[validlocs, theslice, :]
1676
+ if thispass == 0:
1677
+ thecorrfunc = np.zeros(
1678
+ (xsize, ysize, numslices, aliasedcorrelationpts), dtype=np.float64
1382
1679
  )
1383
- cine_byslice[validlocs, theslice, :] = np.nan_to_num(
1384
- cine_byslice[validlocs, theslice, :] / weight_byslice[validlocs, theslice, :]
1680
+ thecorrfunc_byslice = thecorrfunc.reshape(
1681
+ (xsize * ysize, numslices, aliasedcorrelationpts)
1385
1682
  )
1683
+ wavedelay = np.zeros((xsize, ysize, numslices), dtype=np.float64)
1684
+ wavedelay_byslice = wavedelay.reshape((xsize * ysize, numslices))
1685
+ wavedelayCOM = np.zeros((xsize, ysize, numslices), dtype=np.float64)
1686
+ wavedelayCOM_byslice = wavedelayCOM.reshape((xsize * ysize, numslices))
1687
+ waveamp = np.zeros((xsize, ysize, numslices), dtype=np.float64)
1688
+ waveamp_byslice = waveamp.reshape((xsize * ysize, numslices))
1386
1689
  else:
1387
- rawapp_byslice[:, theslice, :] = 0.0
1388
- cine_byslice[:, theslice, :] = 0.0
1389
-
1390
- # smooth the projected data along the time dimension
1391
- if args.smoothapp:
1392
- for loc in validlocs:
1393
- rawapp_byslice[loc, theslice, :] = appsmoothingfilter.apply(
1394
- phaseFs, rawapp_byslice[loc, theslice, :]
1395
- )
1396
- derivatives_byslice[loc, theslice, :] = happy_support.circularderivs(
1397
- rawapp_byslice[loc, theslice, :]
1398
- )
1399
- appflips_byslice = np.where(
1400
- -derivatives_byslice[:, :, 2] > derivatives_byslice[:, :, 0], -1.0, 1.0
1401
- )
1402
- timecoursemean = np.mean(rawapp_byslice[validlocs, theslice, :], axis=1).reshape(
1403
- (-1, 1)
1404
- )
1405
- if args.fliparteries:
1406
- corrected_rawapp_byslice[validlocs, theslice, :] = (
1407
- rawapp_byslice[validlocs, theslice, :] - timecoursemean
1408
- ) * appflips_byslice[validlocs, theslice, None] + timecoursemean
1409
- if args.doaliasedcorrelation and (thispass == numpasses - 1):
1410
- for theloc in validlocs:
1411
- thecorrfunc_byslice[theloc, theslice, :] = theAliasedCorrelator.apply(
1412
- -appflips_byslice[theloc, theslice]
1413
- * demeandata_byslice[theloc, theslice, :],
1414
- int(sliceoffsets[theslice]),
1415
- )[corrstartloc : correndloc + 1]
1416
- maxloc = np.argmax(thecorrfunc_byslice[theloc, theslice, :])
1417
- wavedelay_byslice[theloc, theslice] = (
1418
- thealiasedcorrx[corrstartloc : correndloc + 1]
1419
- )[maxloc]
1420
- waveamp_byslice[theloc, theslice] = thecorrfunc_byslice[
1421
- theloc, theslice, maxloc
1422
- ]
1423
- else:
1424
- corrected_rawapp_byslice[validlocs, theslice, :] = rawapp_byslice[
1425
- validlocs, theslice, :
1426
- ]
1427
- if args.doaliasedcorrelation and (thispass == numpasses - 1):
1428
- for theloc in validlocs:
1429
- thecorrfunc_byslice[theloc, theslice, :] = theAliasedCorrelator.apply(
1430
- -demeandata_byslice[theloc, theslice, :],
1431
- int(sliceoffsets[theslice]),
1432
- )[corrstartloc : correndloc + 1]
1433
- maxloc = np.argmax(np.abs(thecorrfunc_byslice[theloc, theslice, :]))
1434
- wavedelay_byslice[theloc, theslice] = (
1435
- thealiasedcorrx[corrstartloc : correndloc + 1]
1436
- )[maxloc]
1437
- waveamp_byslice[theloc, theslice] = thecorrfunc_byslice[
1438
- theloc, theslice, maxloc
1439
- ]
1440
- timecoursemin = np.min(
1441
- corrected_rawapp_byslice[validlocs, theslice, :], axis=1
1442
- ).reshape((-1, 1))
1443
- app_byslice[validlocs, theslice, :] = (
1444
- corrected_rawapp_byslice[validlocs, theslice, :] - timecoursemin
1445
- )
1446
- normapp_byslice[validlocs, theslice, :] = np.nan_to_num(
1447
- app_byslice[validlocs, theslice, :] / means_byslice[validlocs, theslice, None]
1448
- )
1690
+ thecorrfunc *= 0.0
1691
+ wavedelay *= 0.0
1692
+ wavedelayCOM *= 0.0
1693
+ waveamp *= 0.0
1694
+ else:
1695
+ thecorrfunc_byslice = None
1696
+ waveamp_byslice = None
1697
+ wavedelay_byslice = None
1698
+ wavedelayCOM_byslice = None
1699
+ corrstartloc = None
1700
+ correndloc = None
1701
+ thealiasedcorrx = None
1702
+ theAliasedCorrelator = None
1703
+
1704
+ # now project the data
1705
+ appflips_byslice = happy_support.phaseproject(
1706
+ input_data,
1707
+ demeandata_byslice,
1708
+ means_byslice,
1709
+ rawapp_byslice,
1710
+ app_byslice,
1711
+ normapp_byslice,
1712
+ weights_byslice,
1713
+ cine_byslice,
1714
+ projmask_byslice,
1715
+ derivatives_byslice,
1716
+ proctrs,
1717
+ thispass,
1718
+ args,
1719
+ sliceoffsets,
1720
+ cardphasevals,
1721
+ outphases,
1722
+ appsmoothingfilter,
1723
+ phaseFs,
1724
+ thecorrfunc_byslice,
1725
+ waveamp_byslice,
1726
+ wavedelay_byslice,
1727
+ wavedelayCOM_byslice,
1728
+ corrected_rawapp_byslice,
1729
+ corrstartloc,
1730
+ correndloc,
1731
+ thealiasedcorrx,
1732
+ theAliasedCorrelator,
1733
+ )
1449
1734
  if not args.verbose:
1450
1735
  print(" done")
1451
1736
  timings.append(
@@ -1461,10 +1746,7 @@ def happy_main(argparsingfunc):
1461
1746
  # calculate the flow field from the normapp
1462
1747
  if args.doflowfields:
1463
1748
  print("calculating flow fields")
1464
- flowhdr = copy.deepcopy(nim_hdr)
1465
- flowhdr["dim"][4] = 3
1466
- flowhdr["toffset"] = 0
1467
- flowhdr["pixdim"][4] = 1
1749
+ flowhdr = input_data.copyheader(numtimepoints=3, tr=1.0, toffset=0.0)
1468
1750
 
1469
1751
  flowfield = happy_support.calc_3d_optical_flow(
1470
1752
  app,
@@ -1477,47 +1759,112 @@ def happy_main(argparsingfunc):
1477
1759
  print(f"flow field shape: {flowfield.shape}")
1478
1760
 
1479
1761
  # save the analytic phase projection image
1480
- theheader = copy.deepcopy(nim_hdr)
1481
- theheader["dim"][4] = args.destpoints
1482
- theheader["toffset"] = -np.pi
1483
- theheader["pixdim"][4] = 2.0 * np.pi / args.destpoints
1762
+ theheader = input_data.copyheader(
1763
+ numtimepoints=args.destpoints, tr=-np.pi, toffset=2.0 * np.pi / args.destpoints
1764
+ )
1484
1765
  if thispass == numpasses - 1:
1485
- appfilename = outputroot + "_desc-app_info"
1486
- normappfilename = outputroot + "_desc-normapp_info"
1487
- cinefilename = outputroot + "_desc-cine_info"
1488
- rawappfilename = outputroot + "_desc-rawapp_info"
1489
- bidsdict = bidsbasedict.copy()
1490
- bidsdict["Units"] = "second"
1491
- tide_io.writedicttojson(bidsdict, appfilename + ".json")
1492
- tide_io.writedicttojson(bidsdict, normappfilename + ".json")
1493
- tide_io.writedicttojson(bidsdict, cinefilename + ".json")
1494
- if args.outputlevel > 0:
1495
- tide_io.writedicttojson(bidsdict, rawappfilename + ".json")
1496
- tide_io.savetonifti(app, theheader, appfilename)
1497
- tide_io.savetonifti(normapp, theheader, normappfilename)
1498
- tide_io.savetonifti(cine, theheader, cinefilename)
1766
+ maplist = [
1767
+ (
1768
+ app,
1769
+ "app",
1770
+ "info",
1771
+ None,
1772
+ "Cardiac pulsatility waveform with minimum over time removed",
1773
+ ),
1774
+ (
1775
+ normapp,
1776
+ "normapp",
1777
+ "info",
1778
+ "percent",
1779
+ "Cardiac pulsatility waveform in percentage change relative to mean",
1780
+ ),
1781
+ (
1782
+ cine,
1783
+ "cine",
1784
+ "info",
1785
+ None,
1786
+ "fMRI signal averaged over a single cardiac cycle",
1787
+ ),
1788
+ ]
1499
1789
  if args.outputlevel > 0:
1500
- tide_io.savetonifti(rawapp, theheader, rawappfilename)
1501
-
1790
+ maplist += [
1791
+ (
1792
+ rawapp,
1793
+ "rawapp",
1794
+ "info",
1795
+ None,
1796
+ "Cardiac pulsatility waveform",
1797
+ ),
1798
+ ]
1799
+ # write the 4D phase projection maps
1800
+ tide_io.savemaplist(
1801
+ outputroot,
1802
+ maplist,
1803
+ None,
1804
+ (xsize, ysize, numslices, args.destpoints),
1805
+ theheader,
1806
+ bidsdict,
1807
+ debug=args.debug,
1808
+ )
1502
1809
  timings.append(["Phase projected data saved" + passstring, time.time(), None, None])
1503
1810
 
1504
1811
  if args.doaliasedcorrelation and thispass == numpasses - 1:
1505
- theheader = copy.deepcopy(nim_hdr)
1506
- theheader["dim"][4] = aliasedcorrelationpts
1507
- theheader["toffset"] = 0.0
1508
- theheader["pixdim"][4] = thealiasedcorrx[1] - thealiasedcorrx[0]
1509
- corrfuncfilename = outputroot + "_desc-corrfunc_info"
1510
- wavedelayfilename = outputroot + "_desc-wavedelay_map"
1511
- waveampfilename = outputroot + "_desc-waveamp_map"
1512
- bidsdict = bidsbasedict.copy()
1513
- tide_io.writedicttojson(bidsdict, waveampfilename + ".json")
1514
- bidsdict["Units"] = "second"
1515
- tide_io.writedicttojson(bidsdict, corrfuncfilename + ".json")
1516
- tide_io.writedicttojson(bidsdict, wavedelayfilename + ".json")
1517
- tide_io.savetonifti(thecorrfunc, theheader, corrfuncfilename)
1812
+ theheader = input_data.copyheader(
1813
+ numtimepoints=aliasedcorrelationpts,
1814
+ tr=(thealiasedcorrx[1] - thealiasedcorrx[0]),
1815
+ toffset=0.0,
1816
+ )
1817
+ maplist = [
1818
+ (
1819
+ thecorrfunc,
1820
+ "corrfunc",
1821
+ "info",
1822
+ None,
1823
+ "Aliased correlation function",
1824
+ ),
1825
+ ]
1826
+ tide_io.savemaplist(
1827
+ outputroot,
1828
+ maplist,
1829
+ None,
1830
+ (xsize, ysize, numslices, aliasedcorrelationpts),
1831
+ theheader,
1832
+ bidsdict,
1833
+ debug=args.debug,
1834
+ )
1518
1835
  theheader["dim"][4] = 1
1519
- tide_io.savetonifti(wavedelay, theheader, wavedelayfilename)
1520
- tide_io.savetonifti(waveamp, theheader, waveampfilename)
1836
+ maplist = [
1837
+ (
1838
+ wavedelay,
1839
+ "wavedelay",
1840
+ "map",
1841
+ "seconds",
1842
+ "Peak delay of aliased correlation function",
1843
+ ),
1844
+ (
1845
+ wavedelayCOM,
1846
+ "wavedelayCOM",
1847
+ "map",
1848
+ "seconds",
1849
+ "Center of mass of aliased correlation function",
1850
+ ),
1851
+ (
1852
+ waveamp,
1853
+ "waveamp",
1854
+ "map",
1855
+ None,
1856
+ "Peak amplitude of aliased correlation function",
1857
+ ),
1858
+ ]
1859
+ tide_io.savemaplist(
1860
+ outputroot,
1861
+ maplist,
1862
+ None,
1863
+ (xsize, ysize, numslices),
1864
+ theheader,
1865
+ bidsdict,
1866
+ debug=args.debug,
1867
+ )
1521
1868
 
1522
1869
  # make and save a voxel intensity histogram
1523
1870
  if args.unnormvesselmap:
@@ -1536,12 +1883,12 @@ def happy_main(argparsingfunc):
1536
1883
  debug=args.debug,
1537
1884
  )
1538
1885
 
1539
- # find vessel threshholds
1886
+ # find vessel thresholds
1540
1887
  tide_util.logmem("before making vessel masks")
1541
1888
  hardvesselthresh = tide_stats.getfracvals(np.max(histinput, axis=1), [0.98])[0] / 2.0
1542
1889
  softvesselthresh = args.softvesselfrac * hardvesselthresh
1543
1890
  print(
1544
- "hard, soft vessel threshholds set to",
1891
+ "hard, soft vessel thresholds set to",
1545
1892
  "{:.3f}".format(hardvesselthresh),
1546
1893
  "{:.3f}".format(softvesselthresh),
1547
1894
  )
@@ -1551,28 +1898,42 @@ def happy_main(argparsingfunc):
1551
1898
  vesselmask = np.where(np.max(app, axis=3) > softvesselthresh, 1, 0)
1552
1899
  else:
1553
1900
  vesselmask = np.where(np.max(normapp, axis=3) > softvesselthresh, 1, 0)
1901
+ infodict["vesselmaskvoxels"] = np.int64(np.sum(vesselmask))
1902
+ infodict["vesselmaskpct"] = (
1903
+ 100.0 * infodict["vesselmaskvoxels"] / infodict["projmaskvoxels"]
1904
+ )
1554
1905
  maskedapp2d = np.array(app2d)
1555
1906
  maskedapp2d[np.where(vesselmask.reshape(numspatiallocs) == 0)[0], :] = 0.0
1556
1907
  if args.outputlevel > 1:
1557
1908
  if thispass == numpasses - 1:
1558
- maskedappfilename = outputroot + "_desc-maskedapp_info"
1559
1909
  bidsdict = bidsbasedict.copy()
1560
- bidsdict["Units"] = "second"
1561
- tide_io.writedicttojson(bidsdict, maskedappfilename + ".json")
1562
- tide_io.savetonifti(
1563
- maskedapp2d.reshape((xsize, ysize, numslices, args.destpoints)),
1910
+ maplist = [
1911
+ (
1912
+ maskedapp2d.reshape((xsize, ysize, numslices, args.destpoints)),
1913
+ "maskedapp",
1914
+ "info",
1915
+ None,
1916
+ "Masked analytic phase projection",
1917
+ ),
1918
+ ]
1919
+ tide_io.savemaplist(
1920
+ outputroot,
1921
+ maplist,
1922
+ None,
1923
+ (xsize, ysize, numslices, args.destpoints),
1564
1924
  theheader,
1565
- maskedappfilename,
1925
+ bidsdict,
1926
+ debug=args.debug,
1927
+ )
1928
+ timings.append(
1929
+ [
1930
+ "Vessel masked phase projected data saved" + passstring,
1931
+ time.time(),
1932
+ None,
1933
+ None,
1934
+ ]
1566
1935
  )
1567
1936
  del maskedapp2d
1568
- timings.append(
1569
- [
1570
- "Vessel masked phase projected data saved" + passstring,
1571
- time.time(),
1572
- None,
1573
- None,
1574
- ]
1575
- )
1576
1937
 
1577
1938
  # save multiple versions of the hard vessel mask
1578
1939
  if args.unnormvesselmap:
@@ -1586,61 +1947,345 @@ def happy_main(argparsingfunc):
1586
1947
  risediff = (maxphase - minphase) * vesselmask
1587
1948
  arteries = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) < 0, vesselmask, 0)
1588
1949
  veins = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) > 0, vesselmask, 0)
1589
- theheader = copy.deepcopy(nim_hdr)
1590
- theheader["dim"][4] = 1
1950
+ theheader = input_data.copyheader(numtimepoints=1)
1591
1951
  if thispass == numpasses - 1:
1592
- vesselmaskfilename = outputroot + "_desc-vessels_mask"
1593
- minphasefilename = outputroot + "_desc-minphase_map"
1594
- maxphasefilename = outputroot + "_desc-maxphase_map"
1595
- arterymapfilename = outputroot + "_desc-arteries_map"
1596
- veinmapfilename = outputroot + "_desc-veins_map"
1597
- bidsdict = bidsbasedict.copy()
1598
- tide_io.writedicttojson(bidsdict, vesselmaskfilename + ".json")
1599
- tide_io.savetonifti(vesselmask, theheader, vesselmaskfilename)
1952
+ maplist = [
1953
+ (
1954
+ vesselmask,
1955
+ "vessels",
1956
+ "mask",
1957
+ None,
1958
+ "Vessel mask",
1959
+ ),
1960
+ ]
1600
1961
  if args.outputlevel > 0:
1601
- tide_io.writedicttojson(bidsdict, arterymapfilename + ".json")
1602
- tide_io.writedicttojson(bidsdict, veinmapfilename + ".json")
1603
- bidsdict["Units"] = "radians"
1604
- tide_io.writedicttojson(bidsdict, minphasefilename + ".json")
1605
- tide_io.writedicttojson(bidsdict, maxphasefilename + ".json")
1606
- tide_io.savetonifti(minphase, theheader, minphasefilename)
1607
- tide_io.savetonifti(maxphase, theheader, maxphasefilename)
1608
- tide_io.savetonifti(arteries, theheader, arterymapfilename)
1609
- tide_io.savetonifti(veins, theheader, veinmapfilename)
1962
+ maplist += [
1963
+ (
1964
+ minphase,
1965
+ "minphase",
1966
+ "map",
1967
+ "radians",
1968
+ "Cardiac phase where minimum pulsatility signal occurs",
1969
+ ),
1970
+ (
1971
+ maxphase,
1972
+ "maxphase",
1973
+ "map",
1974
+ "radians",
1975
+ "Cardiac phase where maximum pulsatility signal occurs",
1976
+ ),
1977
+ (
1978
+ arteries,
1979
+ "arteries",
1980
+ "map",
1981
+ None,
1982
+ "Arterial voxels (maybe)",
1983
+ ),
1984
+ (
1985
+ veins,
1986
+ "veins",
1987
+ "map",
1988
+ None,
1989
+ "Venous voxels (maybe)",
1990
+ ),
1991
+ ]
1992
+ tide_io.savemaplist(
1993
+ outputroot,
1994
+ maplist,
1995
+ None,
1996
+ (xsize, ysize, numslices),
1997
+ theheader,
1998
+ bidsdict,
1999
+ debug=args.debug,
2000
+ )
1610
2001
  timings.append(["Masks saved" + passstring, time.time(), None, None])
1611
2002
 
1612
- # now get ready to start again with a new mask
1613
- estmask_byslice = vesselmask.reshape((xsize * ysize, numslices)) + 0
2003
+ # save the mask we used for this pass
2004
+ tide_io.savemaplist(
2005
+ outputroot,
2006
+ [
2007
+ (
2008
+ estweights_byslice.reshape((xsize, ysize, numslices)),
2009
+ f"estweights{passnamefrag}",
2010
+ "map",
2011
+ None,
2012
+ f"Estweights{passstring}",
2013
+ ),
2014
+ ],
2015
+ None,
2016
+ (xsize, ysize, numslices),
2017
+ theheader,
2018
+ bidsdict,
2019
+ debug=args.debug,
2020
+ )
2021
+
2022
+ # estimate pulsatility
2023
+ (
2024
+ card_min,
2025
+ card_max,
2026
+ card_mean,
2027
+ card_std,
2028
+ card_median,
2029
+ card_mad,
2030
+ card_skew,
2031
+ card_kurtosis,
2032
+ ) = tide_stats.fmristats(normapp.reshape(numspatiallocs, -1))
2033
+ maplist = [
2034
+ (
2035
+ card_min,
2036
+ "appmin",
2037
+ "map",
2038
+ None,
2039
+ "Minimum value of analytic phase projection across all phases",
2040
+ ),
2041
+ (
2042
+ card_max,
2043
+ "appmax",
2044
+ "map",
2045
+ None,
2046
+ "Maximum value of analytic phase projection across all phases",
2047
+ ),
2048
+ (
2049
+ card_mean,
2050
+ "appmean",
2051
+ "map",
2052
+ None,
2053
+ "Mean value of analytic phase projection across all phases",
2054
+ ),
2055
+ (
2056
+ card_std,
2057
+ "appstd",
2058
+ "map",
2059
+ None,
2060
+ "Standard deviation of analytic phase projection across all phases",
2061
+ ),
2062
+ (
2063
+ card_median,
2064
+ "appmedian",
2065
+ "map",
2066
+ None,
2067
+ "Median of analytic phase projection across all phases",
2068
+ ),
2069
+ (
2070
+ card_mad,
2071
+ "appMAD",
2072
+ "map",
2073
+ None,
2074
+ "Median average deviate of analytic phase projection across all phases",
2075
+ ),
2076
+ (
2077
+ card_skew,
2078
+ "appskew",
2079
+ "map",
2080
+ None,
2081
+ "Skewness of analytic phase projection across all phases",
2082
+ ),
2083
+ (
2084
+ card_kurtosis,
2085
+ "appkurtosis",
2086
+ "map",
2087
+ None,
2088
+ "Kurtosis of analytic phase projection across all phases",
2089
+ ),
2090
+ ]
2091
+ # write the 3D maps
2092
+ tide_io.savemaplist(
2093
+ outputroot,
2094
+ maplist,
2095
+ None,
2096
+ (xsize, ysize, numslices),
2097
+ theheader,
2098
+ bidsdict,
2099
+ debug=args.debug,
2100
+ )
2101
+
2102
+ pulsatilitymap = 100.0 * (np.max(normapp, axis=3) - np.min(normapp, axis=3))
2103
+ rawrobustmax = tide_stats.getfracval(pulsatilitymap, 0.98, nozero=True)
2104
+ pulsatilityfloor = tide_stats.getfracval(
2105
+ pulsatilitymap, 1.0 - (MIN_PULSATILITY_VOX_PCT / 100.0), nozero=True
2106
+ )
2107
+ rawmedian = tide_stats.getfracval(pulsatilitymap, 0.50, nozero=True)
2108
+ infodict["pulsatilityrobustmax"] = rawrobustmax
2109
+ infodict["pulsatilitymedian"] = rawmedian
2110
+ # make sure at least a minimum percentage of voxels remain in pulsatility mask
2111
+ infodict["pulsatilitythresh"] = np.min([rawmedian * 3.0, pulsatilityfloor])
2112
+ pulsatilitymap = np.where(pulsatilitymap < rawrobustmax, pulsatilitymap, rawrobustmax)
2113
+ pulsatilitymask = np.where(pulsatilitymap > infodict["pulsatilitythresh"], 1.0, 0.0)
2114
+ infodict["pulsatilitymaskvoxels"] = np.int64(np.sum(vesselmask))
2115
+ infodict["pulsatilitymaskpct"] = (
2116
+ 100.0 * infodict["pulsatilitymaskvoxels"] / infodict["projmaskvoxels"]
2117
+ )
2118
+
2119
+ # save the information file
2120
+ if args.saveinfoasjson:
2121
+ tide_io.writedicttojson(infodict, outputroot + "_desc-runinfo.json")
2122
+ else:
2123
+ tide_io.writedict(infodict, outputroot + "_info.txt")
1614
2124
 
1615
- # save a vessel image
1616
- if args.unnormvesselmap:
1617
- vesselmap = np.max(app, axis=3)
2125
+ # now get ready to start again with a new mask
2126
+ if args.doaliasedcorrelation and thispass > 0:
2127
+ estweights_byslice = waveamp_byslice * vesselmask.reshape((xsize * ysize, numslices))
2128
+ else:
2129
+ if args.useoriginalvesselmethod:
2130
+ estweights_byslice = vesselmask.reshape((xsize * ysize, numslices)) + 0
2131
+ else:
2132
+ estweights_byslice = pulsatilitymask.reshape((xsize * ysize, numslices)) + 0
2133
+
2134
+ # calculate the lf and hf pulsatility maps
2135
+ lfnormapp = np.zeros_like(normapp)
2136
+ hfnormapp = np.zeros_like(normapp)
2137
+ for thephase in range(args.destpoints):
2138
+ lfnormapp[:, :, :, thephase] = (
2139
+ tide_filt.ssmooth(
2140
+ xdim, ydim, slicethickness, args.pulsatilitysigma, normapp[:, :, :, thephase]
2141
+ )
2142
+ * pulsatilitymask
2143
+ )
2144
+ hfnormapp[:, :, :, thephase] = normapp[:, :, :, thephase] - lfnormapp[:, :, :, thephase]
2145
+ lfnormapp -= np.min(lfnormapp, axis=3)[:, :, :, None]
2146
+ hfnormapp -= np.min(hfnormapp, axis=3)[:, :, :, None]
2147
+ lfpulsatilitymap = (
2148
+ tide_filt.ssmooth(xdim, ydim, slicethickness, args.pulsatilitysigma, pulsatilitymap)
2149
+ * pulsatilitymask
2150
+ )
2151
+ hfpulsatilitymap = pulsatilitymap - lfpulsatilitymap
2152
+ lfpulsatilitymap2 = 100.0 * np.max(lfnormapp, axis=3)
2153
+ lfrobustmax = tide_stats.getfracval(lfpulsatilitymap2, 0.98, nozero=True)
2154
+ lfpulsatilitymap2 = np.where(lfpulsatilitymap2 < lfrobustmax, lfpulsatilitymap2, lfrobustmax)
2155
+ hfpulsatilitymap2 = 100.0 * np.max(hfnormapp, axis=3)
2156
+ hfrobustmax = tide_stats.getfracval(hfpulsatilitymap2, 0.98, nozero=True)
2157
+ hfpulsatilitymap2 = np.where(hfpulsatilitymap2 < hfrobustmax, hfpulsatilitymap2, hfrobustmax)
2158
+
2159
+ # make a vessel map
2160
+ if args.useoriginalvesselmethod:
2161
+ if args.unnormvesselmap:
2162
+ vesselmap = np.max(app, axis=3)
2163
+ else:
2164
+ # vesselmap = np.max(normapp, axis=3)
2165
+ vesselmap = np.where(hfpulsatilitymap > args.pulsatilitythreshold, 1.0, 0.0)
1618
2166
  else:
1619
- vesselmap = np.max(normapp, axis=3)
1620
- vesselmapfilename = outputroot + "_desc-vessels_map"
1621
- arterymapfilename = outputroot + "_desc-arteries_map"
1622
- veinmapfilename = outputroot + "_desc-veins_map"
1623
- tide_io.savetonifti(vesselmap, theheader, vesselmapfilename)
1624
- tide_io.savetonifti(
1625
- np.where(appflips_byslice.reshape((xsize, ysize, numslices)) < 0, vesselmap, 0.0),
2167
+ vesselmap = pulsatilitymask
2168
+ veinmap = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) > 0, vesselmap, 0.0)
2169
+ arterymap = np.where(appflips_byslice.reshape((xsize, ysize, numslices)) < 0, vesselmap, 0.0)
2170
+
2171
+ # specify the 3D maps
2172
+ maplist = [
2173
+ (
2174
+ pulsatilitymap,
2175
+ "pulsatility",
2176
+ "map",
2177
+ "percent",
2178
+ "Cardiac pulsatility in percentage change relative to mean",
2179
+ ),
2180
+ (
2181
+ pulsatilitymask,
2182
+ "pulsatility",
2183
+ "mask",
2184
+ None,
2185
+ "Valid cardiac pulsatility voxels",
2186
+ ),
2187
+ (
2188
+ lfpulsatilitymap,
2189
+ "lfpulsatility",
2190
+ "map",
2191
+ "percent",
2192
+ "Low spatial frequency cardiac pulsatility in percentage change relative to mean",
2193
+ ),
2194
+ (
2195
+ hfpulsatilitymap,
2196
+ "hfpulsatility",
2197
+ "map",
2198
+ "percent",
2199
+ "High spatial frequency cardiac pulsatility in percentage change relative to mean",
2200
+ ),
2201
+ (
2202
+ lfpulsatilitymap2,
2203
+ "lfpulsatility2",
2204
+ "map",
2205
+ "percent",
2206
+ "Low spatial frequency cardiac pulsatility in percentage change relative to mean",
2207
+ ),
2208
+ (
2209
+ hfpulsatilitymap2,
2210
+ "hfpulsatility2",
2211
+ "map",
2212
+ "percent",
2213
+ "High spatial frequency cardiac pulsatility in percentage change relative to mean",
2214
+ ),
2215
+ (
2216
+ vesselmap,
2217
+ "vessels",
2218
+ "map",
2219
+ None,
2220
+ "Vessel voxels",
2221
+ ),
2222
+ (
2223
+ arterymap,
2224
+ "arteries",
2225
+ "map",
2226
+ None,
2227
+ "Arterial voxels (maybe)",
2228
+ ),
2229
+ (
2230
+ veinmap,
2231
+ "veins",
2232
+ "map",
2233
+ None,
2234
+ "Venous voxels (maybe)",
2235
+ ),
2236
+ ]
2237
+ # write the 3D maps
2238
+ tide_io.savemaplist(
2239
+ outputroot,
2240
+ maplist,
2241
+ None,
2242
+ (xsize, ysize, numslices),
1626
2243
  theheader,
1627
- arterymapfilename,
2244
+ bidsdict,
2245
+ debug=args.debug,
1628
2246
  )
1629
- tide_io.savetonifti(
1630
- np.where(appflips_byslice.reshape((xsize, ysize, numslices)) > 0, vesselmap, 0.0),
2247
+
2248
+ # specify the 4D maps
2249
+ theheader = input_data.copyheader(
2250
+ numtimepoints=args.destpoints, tr=-np.pi, toffset=2.0 * np.pi / args.destpoints
2251
+ )
2252
+ maplist = [
2253
+ (
2254
+ lfnormapp,
2255
+ "lfnormapp",
2256
+ "info",
2257
+ "percent",
2258
+ "Low spatial frequency cardiac pulsatility waveform in percentage change relative to mean",
2259
+ ),
2260
+ (
2261
+ hfnormapp,
2262
+ "hfnormapp",
2263
+ "info",
2264
+ "percent",
2265
+ "High spatial frequency cardiac pulsatility waveform in percentage change relative to mean",
2266
+ ),
2267
+ ]
2268
+
2269
+ # write the 4D maps
2270
+ tide_io.savemaplist(
2271
+ outputroot,
2272
+ maplist,
2273
+ None,
2274
+ (xsize, ysize, numslices, args.destpoints),
1631
2275
  theheader,
1632
- veinmapfilename,
2276
+ bidsdict,
2277
+ debug=args.debug,
1633
2278
  )
1634
2279
 
1635
2280
  # now generate aliased cardiac signals and regress them out of the data
1636
- if args.dotemporalglm or args.dospatialglm:
2281
+ if args.dotemporalregression or args.dospatialregression:
1637
2282
  # generate the signals
1638
2283
  timings.append(["Cardiac signal regression started", time.time(), None, None])
1639
2284
  tide_util.logmem("before cardiac regression")
1640
2285
  print("Generating cardiac regressors")
1641
- cardiacnoise = fmri_data * 0.0
2286
+ cardiacnoise = np.zeros_like(fmri_data)
1642
2287
  cardiacnoise_byslice = cardiacnoise.reshape((xsize * ysize, numslices, timepoints))
1643
- phaseindices = (cardiacnoise * 0.0).astype(np.int16)
2288
+ phaseindices = np.zeros_like(cardiacnoise, np.int16)
1644
2289
  phaseindices_byslice = phaseindices.reshape((xsize * ysize, numslices, timepoints))
1645
2290
  for theslice in range(numslices):
1646
2291
  print("Calculating cardiac noise for slice", theslice)
@@ -1652,39 +2297,91 @@ def happy_main(argparsingfunc):
1652
2297
  cardiacnoise_byslice[validlocs, theslice, t] = rawapp_byslice[
1653
2298
  validlocs, theslice, phaseindices_byslice[validlocs, theslice, t]
1654
2299
  ]
1655
- theheader = copy.deepcopy(nim_hdr)
2300
+ theheader = input_data.copyheader()
1656
2301
  timings.append(["Cardiac signal generated", time.time(), None, None])
1657
2302
  if args.savecardiacnoise:
1658
- cardiacnoisefilename = outputroot + "_desc-cardiacnoise_info"
1659
- phaseindexfilename = outputroot + "_desc-phaseindices_info"
1660
- tide_io.savetonifti(
1661
- cardiacnoise.reshape((xsize, ysize, numslices, timepoints)),
1662
- theheader,
1663
- cardiacnoisefilename,
1664
- )
1665
- tide_io.savetonifti(
1666
- phaseindices.reshape((xsize, ysize, numslices, timepoints)),
2303
+ maplist = [
2304
+ (
2305
+ cardiacnoise.reshape((xsize, ysize, numslices, timepoints)),
2306
+ "cardiacnoise",
2307
+ "info",
2308
+ None,
2309
+ "Calculated cardiac noise EVs",
2310
+ ),
2311
+ (
2312
+ phaseindices.reshape((xsize, ysize, numslices, timepoints)),
2313
+ "phaseindices",
2314
+ "info",
2315
+ None,
2316
+ "Phase indices",
2317
+ ),
2318
+ ]
2319
+ tide_io.savemaplist(
2320
+ outputroot,
2321
+ maplist,
2322
+ None,
2323
+ (xsize, ysize, numslices, timepoints),
1667
2324
  theheader,
1668
- phaseindexfilename,
2325
+ bidsdict,
2326
+ debug=args.debug,
1669
2327
  )
1670
2328
  timings.append(["Cardiac signal saved", time.time(), None, None])
1671
2329
 
1672
2330
  # now remove them
1673
2331
  tide_util.logmem("before cardiac removal")
1674
2332
  print("Removing cardiac signal with GLM")
1675
- filtereddata = 0.0 * fmri_data
1676
2333
  validlocs = np.where(mask > 0)[0]
1677
2334
  numvalidspatiallocs = len(validlocs)
2335
+ if args.focaldebug:
2336
+ print(f"{numvalidspatiallocs=}, {fmri_data.shape=}")
2337
+ filtereddata, filtereddata_shm = tide_util.allocarray(
2338
+ fmri_data.shape,
2339
+ "float64",
2340
+ shared=infodict["sharedmem"],
2341
+ name=f"filtereddata_{infodict['pid']}",
2342
+ )
2343
+ datatoremove, datatoremove_shm = tide_util.allocarray(
2344
+ fmri_data.shape,
2345
+ "float64",
2346
+ shared=infodict["sharedmem"],
2347
+ name=f"datatoremove_{infodict['pid']}",
2348
+ )
1678
2349
  threshval = 0.0
1679
- if args.dospatialglm:
1680
- meanvals = np.zeros(timepoints, dtype=np.float64)
1681
- rvals = np.zeros(timepoints, dtype=np.float64)
1682
- r2vals = np.zeros(timepoints, dtype=np.float64)
1683
- fitcoffs = np.zeros(timepoints, dtype=np.float64)
1684
- fitNorm = np.zeros(timepoints, dtype=np.float64)
1685
- datatoremove = 0.0 * fmri_data
1686
- print("Running spatial glm on", timepoints, "timepoints")
1687
- tide_glmpass.glmpass(
2350
+ if args.dospatialregression:
2351
+ regressiontype = "spatial"
2352
+ meanvals, meanvals_shm = tide_util.allocarray(
2353
+ (timepoints),
2354
+ "float64",
2355
+ shared=infodict["sharedmem"],
2356
+ name=f"meanvals_{infodict['pid']}",
2357
+ )
2358
+ rvals, rvals_shm = tide_util.allocarray(
2359
+ (timepoints),
2360
+ "float64",
2361
+ shared=infodict["sharedmem"],
2362
+ name=f"rvals_{infodict['pid']}",
2363
+ )
2364
+ r2vals, r2vals_shm = tide_util.allocarray(
2365
+ (timepoints),
2366
+ "float64",
2367
+ shared=infodict["sharedmem"],
2368
+ name=f"r2vals_{infodict['pid']}",
2369
+ )
2370
+ fitcoffs, fitcoffs_shm = tide_util.allocarray(
2371
+ (timepoints),
2372
+ "float64",
2373
+ shared=infodict["sharedmem"],
2374
+ name=f"fitcoffs_{infodict['pid']}",
2375
+ )
2376
+ fitNorm, fitNorm_shm = tide_util.allocarray(
2377
+ (timepoints),
2378
+ "float64",
2379
+ shared=infodict["sharedmem"],
2380
+ name=f"fitNorm_{infodict['pid']}",
2381
+ )
2382
+ print("Running spatial regression on", timepoints, "timepoints")
2383
+ tide_util.disablemkl(args.nprocs)
2384
+ tide_linfitfiltpass.linfitfiltpass(
1688
2385
  timepoints,
1689
2386
  fmri_data[validlocs, :],
1690
2387
  threshval,
@@ -1696,12 +2393,15 @@ def happy_main(argparsingfunc):
1696
2393
  fitNorm,
1697
2394
  datatoremove[validlocs, :],
1698
2395
  filtereddata[validlocs, :],
1699
- mp_chunksize=10,
2396
+ chunksize=10,
1700
2397
  procbyvoxel=False,
1701
2398
  nprocs=args.nprocs,
1702
2399
  )
1703
- print(datatoremove.shape, cardiacnoise.shape, fitcoffs.shape)
1704
- # datatoremove[validlocs, :] = np.multiply(cardiacnoise[validlocs, :], fitcoffs[:, None])
2400
+ tide_util.enablemkl(args.mklthreads)
2401
+ datatoremove[validlocs, :] = np.multiply(cardiacnoise[validlocs, :], fitcoffs[None, :])
2402
+ print(f"{datatoremove.shape=}, {np.min(datatoremove)=}, {np.max(datatoremove)=}")
2403
+ print(f"{cardiacnoise.shape=}, {np.min(cardiacnoise)=}, {np.max(cardiacnoise)=}")
2404
+ print(f"{fitcoffs.shape=}, {np.min(fitcoffs)=}, {np.max(fitcoffs)=}")
1705
2405
  filtereddata = fmri_data - datatoremove
1706
2406
  timings.append(
1707
2407
  [
@@ -1711,40 +2411,42 @@ def happy_main(argparsingfunc):
1711
2411
  "timepoints",
1712
2412
  ]
1713
2413
  )
1714
- tide_io.writevec(fitcoffs, outputroot + "_fitcoff.txt")
1715
- tide_io.writevec(meanvals, outputroot + "_fitmean.txt")
1716
- tide_io.writevec(rvals, outputroot + "_fitR.txt")
1717
- theheader = copy.deepcopy(nim_hdr)
1718
- cardfiltresultfilename = outputroot + "_desc-cardfiltResult_bold"
1719
- cardfiltremovedfilename = outputroot + "_desc-cardfiltRemoved_bold"
1720
- tide_io.savetonifti(
1721
- filtereddata.reshape((xsize, ysize, numslices, timepoints)),
1722
- theheader,
1723
- cardfiltresultfilename,
2414
+
2415
+ if args.dotemporalregression:
2416
+ regressiontype = "temporal"
2417
+ meanvals, meanvals_shm = tide_util.allocarray(
2418
+ (numspatiallocs),
2419
+ "float64",
2420
+ shared=infodict["sharedmem"],
2421
+ name=f"meanvals_{infodict['pid']}",
1724
2422
  )
1725
- tide_io.savetonifti(
1726
- datatoremove.reshape((xsize, ysize, numslices, timepoints)),
1727
- theheader,
1728
- cardfiltremovedfilename,
2423
+ rvals, rvals_shm = tide_util.allocarray(
2424
+ (numspatiallocs),
2425
+ "float64",
2426
+ shared=infodict["sharedmem"],
2427
+ name=f"rvals_{infodict['pid']}",
1729
2428
  )
1730
- timings.append(
1731
- [
1732
- "Cardiac signal spatial regression files written",
1733
- time.time(),
1734
- None,
1735
- None,
1736
- ]
2429
+ r2vals, r2vals_shm = tide_util.allocarray(
2430
+ (numspatiallocs),
2431
+ "float64",
2432
+ shared=infodict["sharedmem"],
2433
+ name=f"r2vals_{infodict['pid']}",
1737
2434
  )
1738
-
1739
- if args.dotemporalglm:
1740
- meanvals = np.zeros(numspatiallocs, dtype=np.float64)
1741
- rvals = np.zeros(numspatiallocs, dtype=np.float64)
1742
- r2vals = np.zeros(numspatiallocs, dtype=np.float64)
1743
- fitcoffs = np.zeros(numspatiallocs, dtype=np.float64)
1744
- fitNorm = np.zeros(numspatiallocs, dtype=np.float64)
1745
- datatoremove = 0.0 * fmri_data
1746
- print("Running temporal glm on", numvalidspatiallocs, "voxels")
1747
- tide_glmpass.glmpass(
2435
+ fitcoffs, fitcoffs_shm = tide_util.allocarray(
2436
+ (numspatiallocs),
2437
+ "float64",
2438
+ shared=infodict["sharedmem"],
2439
+ name=f"fitcoffs_{infodict['pid']}",
2440
+ )
2441
+ fitNorm, fitNorm_shm = tide_util.allocarray(
2442
+ (numspatiallocs),
2443
+ "float64",
2444
+ shared=infodict["sharedmem"],
2445
+ name=f"fitNorm_{infodict['pid']}",
2446
+ )
2447
+ print("Running temporal regression on", numvalidspatiallocs, "voxels")
2448
+ tide_util.disablemkl(args.nprocs)
2449
+ tide_linfitfiltpass.linfitfiltpass(
1748
2450
  numvalidspatiallocs,
1749
2451
  fmri_data[validlocs, :],
1750
2452
  threshval,
@@ -1758,10 +2460,15 @@ def happy_main(argparsingfunc):
1758
2460
  filtereddata[validlocs, :],
1759
2461
  procbyvoxel=True,
1760
2462
  nprocs=args.nprocs,
2463
+ debug=args.focaldebug,
1761
2464
  )
2465
+ tide_util.enablemkl(args.mklthreads)
1762
2466
  datatoremove[validlocs, :] = np.multiply(
1763
2467
  cardiacnoise[validlocs, :], fitcoffs[validlocs, None]
1764
2468
  )
2469
+ print(f"{datatoremove.shape=}, {np.min(datatoremove)=}, {np.max(datatoremove)=}")
2470
+ print(f"{cardiacnoise.shape=}, {np.min(cardiacnoise)=}, {np.max(cardiacnoise)=}")
2471
+ print(f"{fitcoffs.shape=}, {np.min(fitcoffs)=}, {np.max(fitcoffs)=}")
1765
2472
  filtereddata[validlocs, :] = fmri_data[validlocs, :] - datatoremove[validlocs, :]
1766
2473
  timings.append(
1767
2474
  [
@@ -1771,49 +2478,92 @@ def happy_main(argparsingfunc):
1771
2478
  "voxels",
1772
2479
  ]
1773
2480
  )
1774
- theheader = copy.deepcopy(nim_hdr)
1775
- theheader["dim"][4] = 1
1776
- cardfiltcoeffsfilename = outputroot + "_desc-cardfiltCoeffs_map"
1777
- cardfiltmeanfilename = outputroot + "_desc-cardfiltMean_map"
1778
- cardfiltRfilename = outputroot + "_desc-cardfiltR_map"
1779
- tide_io.savetonifti(
1780
- fitcoffs.reshape((xsize, ysize, numslices)),
1781
- theheader,
1782
- cardfiltcoeffsfilename,
1783
- )
1784
- tide_io.savetonifti(
1785
- meanvals.reshape((xsize, ysize, numslices)),
2481
+ theheader = input_data.copyheader(numtimepoints=1)
2482
+ maplist = [
2483
+ (
2484
+ fitcoffs.reshape((xsize, ysize, numslices)),
2485
+ "cardfiltCoeffs",
2486
+ "map",
2487
+ None,
2488
+ "Coefficients for temporal cardiac noise regression",
2489
+ ),
2490
+ (
2491
+ meanvals.reshape((xsize, ysize, numslices)),
2492
+ "cardfiltMean",
2493
+ "map",
2494
+ None,
2495
+ "Mean values after temporal cardiac noise regression",
2496
+ ),
2497
+ (
2498
+ rvals.reshape((xsize, ysize, numslices)),
2499
+ "cardfiltR",
2500
+ "map",
2501
+ None,
2502
+ "R values for temporal cardiac noise regression",
2503
+ ),
2504
+ ]
2505
+ tide_io.savemaplist(
2506
+ outputroot,
2507
+ maplist,
2508
+ None,
2509
+ (xsize, ysize, numslices),
1786
2510
  theheader,
1787
- cardfiltmeanfilename,
1788
- )
1789
- tide_io.savetonifti(
1790
- rvals.reshape((xsize, ysize, numslices)), theheader, cardfiltRfilename
2511
+ bidsdict,
2512
+ debug=args.debug,
1791
2513
  )
1792
2514
 
1793
- theheader = copy.deepcopy(nim_hdr)
1794
- cardfiltresultfilename = outputroot + "_desc-cardfiltResult_bold"
1795
- cardfiltremovedfilename = outputroot + "_desc-cardfiltRemoved_bold"
1796
- tide_io.savetonifti(
2515
+ # now write out the filtered data
2516
+ theheader = input_data.copyheader()
2517
+ maplist = [
2518
+ (
1797
2519
  filtereddata.reshape((xsize, ysize, numslices, timepoints)),
1798
- theheader,
1799
- cardfiltresultfilename,
1800
- )
1801
- tide_io.savetonifti(
2520
+ "cardfiltResult",
2521
+ "bold",
2522
+ None,
2523
+ f"Cardiac filtered BOLD data after {regressiontype} regression",
2524
+ ),
2525
+ (
1802
2526
  datatoremove.reshape((xsize, ysize, numslices, timepoints)),
1803
- theheader,
1804
- cardfiltremovedfilename,
1805
- )
1806
- timings.append(
1807
- [
1808
- "Cardiac signal temporal regression files written",
1809
- time.time(),
1810
- None,
1811
- None,
1812
- ]
1813
- )
2527
+ "cardfiltRemoved",
2528
+ "bold",
2529
+ None,
2530
+ f"Cardiac noise removed with {regressiontype} regression",
2531
+ ),
2532
+ ]
2533
+ tide_io.savemaplist(
2534
+ outputroot,
2535
+ maplist,
2536
+ None,
2537
+ (xsize, ysize, numslices, timepoints),
2538
+ theheader,
2539
+ bidsdict,
2540
+ debug=args.debug,
2541
+ )
2542
+ timings.append(
2543
+ [
2544
+ f"Cardiac signal {regressiontype} regression files written",
2545
+ time.time(),
2546
+ None,
2547
+ None,
2548
+ ]
2549
+ )
2550
+
2551
+ # clean up shared memory, if using
2552
+ if args.dotemporalregression or args.dospatialregression:
2553
+ if infodict["sharedmem"]:
2554
+ tide_util.cleanup_shm(filtereddata_shm)
2555
+ tide_util.cleanup_shm(datatoremove_shm)
2556
+ tide_util.cleanup_shm(meanvals_shm)
2557
+ tide_util.cleanup_shm(rvals_shm)
2558
+ tide_util.cleanup_shm(r2vals_shm)
2559
+ tide_util.cleanup_shm(fitcoffs_shm)
2560
+ tide_util.cleanup_shm(fitNorm_shm)
1814
2561
 
1815
2562
  timings.append(["Done", time.time(), None, None])
1816
2563
 
2564
+ # save pyfftw wisdom
2565
+ tide_util.savewisdom(args.pyfftw_wisdom)
2566
+
1817
2567
  # Process and save timing information
1818
2568
  nodeline = "Processed on " + platform.node()
1819
2569
  tide_util.proctiminginfo(
@@ -1821,3 +2571,15 @@ def happy_main(argparsingfunc):
1821
2571
  )
1822
2572
 
1823
2573
  tide_util.logmem("final")
2574
+
2575
+ # save the information file
2576
+ if args.saveinfoasjson:
2577
+ tide_io.writedicttojson(infodict, outputroot + "_desc-runinfo.json")
2578
+ else:
2579
+ tide_io.writedict(infodict, outputroot + "_info.txt")
2580
+
2581
+ # delete the canary file
2582
+ Path(f"{outputroot}_ISRUNNING.txt").unlink()
2583
+
2584
+ # create the finished file
2585
+ Path(f"{outputroot}_DONE.txt").touch()