rapidtide 2.9.6__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 +92 -42
  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 +2 -2
  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 +108 -92
  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 +587 -1116
  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 +835 -144
  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.6.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 +26 -14
  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 +1785 -1858
  292. rapidtide/workflows/rapidtide2std.py +101 -3
  293. rapidtide/workflows/rapidtide_parser.py +590 -389
  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.6.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.6.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.6.data/scripts/adjustoffset +0 -23
  346. rapidtide-2.9.6.data/scripts/aligntcs +0 -23
  347. rapidtide-2.9.6.data/scripts/applydlfilter +0 -23
  348. rapidtide-2.9.6.data/scripts/atlasaverage +0 -23
  349. rapidtide-2.9.6.data/scripts/atlastool +0 -23
  350. rapidtide-2.9.6.data/scripts/calcicc +0 -22
  351. rapidtide-2.9.6.data/scripts/calctexticc +0 -23
  352. rapidtide-2.9.6.data/scripts/calcttest +0 -22
  353. rapidtide-2.9.6.data/scripts/ccorrica +0 -23
  354. rapidtide-2.9.6.data/scripts/diffrois +0 -23
  355. rapidtide-2.9.6.data/scripts/endtidalproc +0 -23
  356. rapidtide-2.9.6.data/scripts/filtnifti +0 -23
  357. rapidtide-2.9.6.data/scripts/filttc +0 -23
  358. rapidtide-2.9.6.data/scripts/fingerprint +0 -593
  359. rapidtide-2.9.6.data/scripts/fixtr +0 -23
  360. rapidtide-2.9.6.data/scripts/glmfilt +0 -24
  361. rapidtide-2.9.6.data/scripts/gmscalc +0 -22
  362. rapidtide-2.9.6.data/scripts/happy +0 -25
  363. rapidtide-2.9.6.data/scripts/happy2std +0 -23
  364. rapidtide-2.9.6.data/scripts/happywarp +0 -350
  365. rapidtide-2.9.6.data/scripts/histnifti +0 -23
  366. rapidtide-2.9.6.data/scripts/histtc +0 -23
  367. rapidtide-2.9.6.data/scripts/localflow +0 -23
  368. rapidtide-2.9.6.data/scripts/mergequality +0 -23
  369. rapidtide-2.9.6.data/scripts/pairproc +0 -23
  370. rapidtide-2.9.6.data/scripts/pairwisemergenifti +0 -23
  371. rapidtide-2.9.6.data/scripts/physiofreq +0 -23
  372. rapidtide-2.9.6.data/scripts/pixelcomp +0 -23
  373. rapidtide-2.9.6.data/scripts/plethquality +0 -23
  374. rapidtide-2.9.6.data/scripts/polyfitim +0 -23
  375. rapidtide-2.9.6.data/scripts/proj2flow +0 -23
  376. rapidtide-2.9.6.data/scripts/rankimage +0 -23
  377. rapidtide-2.9.6.data/scripts/rapidtide +0 -23
  378. rapidtide-2.9.6.data/scripts/rapidtide2std +0 -23
  379. rapidtide-2.9.6.data/scripts/resamplenifti +0 -23
  380. rapidtide-2.9.6.data/scripts/resampletc +0 -23
  381. rapidtide-2.9.6.data/scripts/retroglm +0 -23
  382. rapidtide-2.9.6.data/scripts/roisummarize +0 -23
  383. rapidtide-2.9.6.data/scripts/runqualitycheck +0 -23
  384. rapidtide-2.9.6.data/scripts/showarbcorr +0 -23
  385. rapidtide-2.9.6.data/scripts/showhist +0 -23
  386. rapidtide-2.9.6.data/scripts/showstxcorr +0 -23
  387. rapidtide-2.9.6.data/scripts/showtc +0 -23
  388. rapidtide-2.9.6.data/scripts/showxcorr_legacy +0 -536
  389. rapidtide-2.9.6.data/scripts/showxcorrx +0 -23
  390. rapidtide-2.9.6.data/scripts/showxy +0 -23
  391. rapidtide-2.9.6.data/scripts/simdata +0 -23
  392. rapidtide-2.9.6.data/scripts/spatialdecomp +0 -23
  393. rapidtide-2.9.6.data/scripts/spatialfit +0 -23
  394. rapidtide-2.9.6.data/scripts/spatialmi +0 -23
  395. rapidtide-2.9.6.data/scripts/spectrogram +0 -23
  396. rapidtide-2.9.6.data/scripts/synthASL +0 -23
  397. rapidtide-2.9.6.data/scripts/tcfrom2col +0 -23
  398. rapidtide-2.9.6.data/scripts/tcfrom3col +0 -23
  399. rapidtide-2.9.6.data/scripts/temporaldecomp +0 -23
  400. rapidtide-2.9.6.data/scripts/threeD +0 -236
  401. rapidtide-2.9.6.data/scripts/tidepool +0 -23
  402. rapidtide-2.9.6.data/scripts/variabilityizer +0 -23
  403. rapidtide-2.9.6.dist-info/RECORD +0 -359
  404. rapidtide-2.9.6.dist-info/top_level.txt +0 -86
  405. {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,752 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # Copyright 2016-2025 Blaise Frederick (except for some routines listed below)
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ #
19
+ import copy
20
+ import math
21
+ import os
22
+ import sys
23
+ import warnings
24
+
25
+ import numpy as np
26
+ from numpy.typing import NDArray
27
+ from scipy.interpolate import griddata
28
+ from scipy.ndimage import distance_transform_edt, gaussian_filter1d
29
+ from skimage.filters import threshold_multiotsu
30
+ from skimage.segmentation import flood_fill
31
+
32
+ import rapidtide.io as tide_io
33
+
34
+
35
+ def interpolate_masked_voxels(
36
+ data: NDArray, mask: NDArray, method: str = "linear", extrapolate: bool = True
37
+ ) -> NDArray:
38
+ """
39
+ Replaces masked voxels in a 3D numpy array with interpolated values
40
+ from the unmasked region. Supports boundary extrapolation and multiple interpolation methods.
41
+
42
+ Parameters:
43
+ data (NDArray): A 3D numpy array containing the data.
44
+ mask (NDArray): A 3D binary numpy array of the same shape as `data`,
45
+ where 1 indicates masked voxels and 0 indicates unmasked voxels.
46
+ method (str): Interpolation method ('linear', 'nearest', or 'cubic').
47
+ extrapolate (bool): Whether to extrapolate values for masked voxels outside the convex hull
48
+ of the unmasked points.
49
+
50
+ Returns:
51
+ NDArray: A new 3D array with interpolated (and optionally extrapolated)
52
+ values replacing masked regions.
53
+ """
54
+ if data.shape != mask.shape:
55
+ raise ValueError("Data and mask must have the same shape.")
56
+
57
+ # Ensure mask is binary
58
+ mask = mask.astype(bool)
59
+
60
+ # Get the coordinates of all voxels
61
+ coords = np.array(np.nonzero(~mask)).T # Unmasked voxel coordinates
62
+ masked_coords = np.array(np.nonzero(mask)).T # Masked voxel coordinates
63
+
64
+ # Extract values at unmasked voxel locations
65
+ values = data[~mask]
66
+
67
+ # Perform interpolation
68
+ interpolated_values = griddata(
69
+ points=coords,
70
+ values=values,
71
+ xi=masked_coords,
72
+ method=method,
73
+ fill_value=np.nan, # Use NaN to mark regions outside convex hull
74
+ )
75
+
76
+ # Handle extrapolation if requested
77
+ if extrapolate:
78
+ nan_mask = np.isnan(interpolated_values)
79
+ if np.any(nan_mask):
80
+ # Use nearest neighbor interpolation for NaNs
81
+ extrapolated_values = griddata(
82
+ points=coords, values=values, xi=masked_coords[nan_mask], method="nearest"
83
+ )
84
+ interpolated_values[nan_mask] = extrapolated_values
85
+
86
+ # Create a copy of the data to avoid modifying the original
87
+ interpolated_data = data.copy()
88
+ interpolated_data[mask] = interpolated_values
89
+
90
+ return interpolated_data
91
+
92
+
93
+ def get_bounding_box(mask: NDArray, value: int, buffer: int = 0) -> tuple[tuple, tuple]:
94
+ """
95
+ Computes the 3D bounding box that contains all the voxels in the mask with value value.
96
+
97
+ Parameters
98
+ ----------
99
+ mask : NDArray
100
+ A 3D binary mask where non-zero values indicate the masked region.
101
+ value : int
102
+ The masked region value to compute the bounding box for.
103
+ buffer : int, optional
104
+ Buffer to add around the bounding box in all directions. Default is 0.
105
+
106
+ Returns
107
+ -------
108
+ tuple of tuple of int
109
+ Two tuples defining the bounding box:
110
+ ((min_x, min_y, min_z), (max_x, max_y, max_z)),
111
+ where min and max are inclusive coordinates of the bounding box.
112
+
113
+ Notes
114
+ -----
115
+ The function handles edge cases where the buffer extends beyond the mask boundaries
116
+ by clamping the coordinates to the valid range [0, shape[axis]-1].
117
+
118
+ Examples
119
+ --------
120
+ >>> import numpy as np
121
+ >>> mask = np.zeros((10, 10, 10), dtype=int)
122
+ >>> mask[3:7, 3:7, 3:7] = 1
123
+ >>> get_bounding_box(mask, 1)
124
+ ((3, 3, 3), (6, 6, 6))
125
+
126
+ >>> get_bounding_box(mask, 1, buffer=1)
127
+ ((2, 2, 2), (7, 7, 7))
128
+ """
129
+ if mask.ndim != 3:
130
+ raise ValueError("Input mask must be a 3D array.")
131
+
132
+ # Get the indices of all non-zero voxels
133
+ non_zero_indices = np.argwhere(mask == value)
134
+
135
+ # Find the min and max coordinates along each axis
136
+ min_coords = np.min(non_zero_indices, axis=0)
137
+ max_coords = np.max(non_zero_indices, axis=0)
138
+
139
+ if buffer > 0:
140
+ for axis in range(mask.ndim):
141
+ min_coords[axis] = np.max([min_coords[axis] - buffer, 0])
142
+ max_coords[axis] = np.min([max_coords[axis] + buffer, mask.shape[axis]])
143
+
144
+ # Return the bounding box as ((min_x, min_y, min_z), (max_x, max_y, max_z))
145
+ return tuple(min_coords), tuple(max_coords)
146
+
147
+
148
+ def flood3d(image: NDArray, newvalue: int) -> NDArray:
149
+ """
150
+ Apply flood fill to each slice of a 3D image.
151
+
152
+ This function performs a connected-component flood fill operation on each
153
+ 2D slice of a 3D image, starting from the top-left corner (0, 0).
154
+
155
+ Parameters
156
+ ----------
157
+ image : NDArray
158
+ Input 3D image array of shape (height, width, depth)
159
+ newvalue : int
160
+ The value to fill the connected component with
161
+
162
+ Returns
163
+ -------
164
+ NDArray
165
+ 3D image array of the same shape as input, with flood fill applied
166
+ to each slice
167
+
168
+ Notes
169
+ -----
170
+ - Uses 4-connectivity (rook-style connectivity) for flood fill
171
+ - Each slice is processed independently
172
+ - The fill operation starts from position (0, 0) in each slice
173
+ - Original image values are preserved in the output where fill did not occur
174
+
175
+ Examples
176
+ --------
177
+ >>> import numpy as np
178
+ >>> image = np.array([[[1, 1, 0],
179
+ ... [1, 0, 0],
180
+ ... [0, 0, 0]],
181
+ ... [[1, 1, 0],
182
+ ... [1, 0, 0],
183
+ ... [0, 0, 0]]])
184
+ >>> result = flood3d(image, 5)
185
+ >>> print(result)
186
+ """
187
+ filledim = np.zeros_like(image)
188
+ for slice in range(image.shape[2]):
189
+ filledim[:, :, slice] = flood_fill(image[:, :, slice], (0, 0), newvalue, connectivity=1)
190
+ return filledim
191
+
192
+
193
+ def invertedflood3D(image: NDArray, newvalue: int) -> NDArray:
194
+ """
195
+ Apply inverted flood fill operation to a 3D image.
196
+
197
+ This function performs an inverted flood fill by adding the new value to the
198
+ original image and subtracting the result of a standard flood3d operation.
199
+
200
+ Parameters
201
+ ----------
202
+ image : NDArray
203
+ Input 3D image array to process
204
+ newvalue : int
205
+ Value to be added during the inverted flood fill operation
206
+
207
+ Returns
208
+ -------
209
+ NDArray
210
+ Resulting image after inverted flood fill operation
211
+
212
+ Notes
213
+ -----
214
+ The function relies on a `flood3d` function which is assumed to be defined
215
+ elsewhere in the codebase. The inverted flood fill is computed as:
216
+ result = image + newvalue - flood3d(image, newvalue)
217
+
218
+ Examples
219
+ --------
220
+ >>> import numpy as np
221
+ >>> image = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
222
+ >>> result = invertedflood3D(image, 10)
223
+ >>> print(result)
224
+ """
225
+ return image + newvalue - flood3d(image, newvalue)
226
+
227
+
228
+ def growregion(
229
+ image: NDArray,
230
+ location: tuple[int, int, int],
231
+ value: int,
232
+ separatedimage: NDArray,
233
+ regionsize: int,
234
+ debug: bool = False,
235
+ ) -> int:
236
+ separatedimage[location[0], location[1], location[2]] = value
237
+ regionsize += 1
238
+ if debug:
239
+ print(f"{location=}, {value=}")
240
+ xstart = np.max([location[0] - 1, 0])
241
+ xend = np.min([location[0] + 2, image.shape[0]])
242
+ ystart = np.max([location[1] - 1, 0])
243
+ yend = np.min([location[1] + 2, image.shape[1]])
244
+ zstart = np.max([location[2] - 1, 0])
245
+ zend = np.min([location[2] + 2, image.shape[2]])
246
+ if debug:
247
+ print(f"{xstart=}, {xend=}, {ystart=}, {yend=}, {zstart=}, {zend=}")
248
+ for x in range(xstart, xend):
249
+ for y in range(ystart, yend):
250
+ for z in range(zstart, zend):
251
+ if (x != location[0]) or (y != location[1]) or (z != location[2]):
252
+ if separatedimage[x, y, z] == 0 and image[x, y, z] == 1:
253
+ regionsize = growregion(
254
+ image, (x, y, z), value, separatedimage, regionsize
255
+ )
256
+ return regionsize
257
+
258
+
259
+ def separateclusters(image: NDArray, sizethresh: int = 0, debug: bool = False) -> NDArray:
260
+ separatedclusters = np.zeros_like(image)
261
+ stop = False
262
+ value = 1
263
+ while not stop:
264
+ regionsize = 0
265
+ searchvoxels = image * np.where(separatedclusters == 0, 1, 0)
266
+ seedvoxels = np.where(searchvoxels > 0)
267
+ if debug:
268
+ print(f"{seedvoxels=}")
269
+ if len(seedvoxels[0]) > 0:
270
+ location = (seedvoxels[0][0], seedvoxels[1][0], seedvoxels[2][0])
271
+ if debug:
272
+ if debug:
273
+ print(f"growing from {location}")
274
+ try:
275
+ regionsize = growregion(
276
+ image, location, value, separatedclusters, regionsize, debug=debug
277
+ )
278
+ except RecursionError:
279
+ raise RecursionError("Clusters are not separable.")
280
+ if regionsize >= sizethresh:
281
+ if debug:
282
+ print(f"region:{value}: {regionsize=} - retained")
283
+ value += 1
284
+ else:
285
+ image[np.where(separatedclusters == value)] = 0
286
+ separatedclusters[np.where(separatedclusters == value)] = 0
287
+ else:
288
+ stop = True
289
+ return separatedclusters
290
+
291
+
292
+ #
293
+ # The following functions (to the end of the file) were adapted from the PyDog library on GitHub
294
+ # This is the license information for that library
295
+ #
296
+ # BSD 2-Clause License
297
+ #
298
+ # Copyright (c) 2021, Chris Rorden
299
+ # All rights reserved.
300
+ #
301
+ # Redistribution and use in source and binary forms, with or without
302
+ # modification, are permitted provided that the following conditions are met:
303
+ #
304
+ # 1. Redistributions of source code must retain the above copyright notice, this
305
+ # list of conditions and the following disclaimer.
306
+ #
307
+ # 2. Redistributions in binary form must reproduce the above copyright notice,
308
+ # this list of conditions and the following disclaimer in the documentation
309
+ # and/or other materials provided with the distribution.
310
+ #
311
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
312
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
313
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
314
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
315
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
316
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
317
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
318
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
319
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
320
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
321
+ def clamp(low: int, high: int, value: int) -> int:
322
+ """
323
+ Bound an integer to a range.
324
+
325
+ This function clamps a value to ensure it falls within the inclusive range [low, high].
326
+ If the value is less than low, it returns low. If the value is greater than high,
327
+ it returns high. Otherwise, it returns the value unchanged.
328
+
329
+ Parameters
330
+ ----------
331
+ low : int
332
+ The lower bound of the range (inclusive).
333
+ high : int
334
+ The upper bound of the range (inclusive).
335
+ value : int
336
+ The value to be clamped.
337
+
338
+ Returns
339
+ -------
340
+ int
341
+ The clamped value within the range [low, high].
342
+
343
+ Notes
344
+ -----
345
+ The function assumes that `low <= high`. If this condition is not met,
346
+ the behavior is undefined and may return unexpected results.
347
+
348
+ Examples
349
+ --------
350
+ >>> clamp(0, 10, 5)
351
+ 5
352
+ >>> clamp(0, 10, -1)
353
+ 0
354
+ >>> clamp(0, 10, 15)
355
+ 10
356
+ """
357
+ return max(low, min(high, value))
358
+
359
+
360
+ def dehaze(fdata: NDArray, level: int, debug: bool = False) -> NDArray:
361
+ """
362
+ use Otsu to threshold https://scikit-image.org/docs/stable/auto_examples/segmentation/plot_multiotsu.html
363
+ n.b. threshold used to mask image: dark values are zeroed, but result is NOT binary
364
+
365
+ Parameters
366
+ ----------
367
+ fdata : numpy.memmap from Niimg-like object
368
+ Image(s) to run DoG on (see :ref:`extracting_data`
369
+ for a detailed description of the valid input types).
370
+ level : int
371
+ value 1..5 with larger values preserving more bright voxels
372
+ dark_classes/total_classes
373
+ 1: 3/4
374
+ 2: 2/3
375
+ 3: 1/2
376
+ 4: 1/3
377
+ 5: 1/4
378
+ debug : :obj:`bool`, optional
379
+ Controls the amount of verbosity: True give more messages
380
+ (False means no messages). Default=False.
381
+
382
+ Returns
383
+ -------
384
+ :class:`nibabel.nifti1.Nifti1Image`
385
+ """
386
+ level = clamp(1, 5, level)
387
+ n_classes = abs(3 - level) + 2
388
+ dark_classes = 4 - level
389
+ dark_classes = clamp(1, 3, dark_classes)
390
+ thresholds = threshold_multiotsu(fdata, n_classes)
391
+ thresh = thresholds[dark_classes - 1]
392
+ if debug:
393
+ print("Zeroing voxels darker than {}".format(thresh))
394
+ fdata[fdata < thresh] = 0
395
+ return fdata
396
+
397
+
398
+ # https://github.com/nilearn/nilearn/blob/1607b52458c28953a87bbe6f42448b7b4e30a72f/nilearn/image/image.py#L164
399
+ def _smooth_array(
400
+ arr: NDArray,
401
+ affine: NDArray | None,
402
+ fwhm: float | NDArray | tuple | list | str | None = None,
403
+ ensure_finite: bool = True,
404
+ copy: bool = True,
405
+ ) -> NDArray:
406
+ """
407
+ Smooth images by applying a Gaussian filter.
408
+
409
+ Apply a Gaussian filter along the three first dimensions of `arr`.
410
+
411
+ Parameters
412
+ ----------
413
+ arr : :class:`NDArray`
414
+ 4D array, with image number as last dimension. 3D arrays are also
415
+ accepted.
416
+
417
+ affine : :class:`NDArray`
418
+ (4, 4) matrix, giving affine transformation for image. (3, 3) matrices
419
+ are also accepted (only these coefficients are used).
420
+ If `fwhm='fast'`, the affine is not used and can be None.
421
+
422
+ fwhm : scalar, :class:`NDArray`/:obj:`tuple`/:obj:`list`, 'fast' or None, optional
423
+ Smoothing strength, as a full-width at half maximum, in millimeters.
424
+ If a nonzero scalar is given, width is identical in all 3 directions.
425
+ A :class:`NDArray`, :obj:`tuple`, or :obj:`list` must have 3 elements,
426
+ giving the FWHM along each axis.
427
+ If any of the elements is zero or None, smoothing is not performed
428
+ along that axis.
429
+ If `fwhm='fast'`, a fast smoothing will be performed with a filter
430
+ [0.2, 1, 0.2] in each direction and a normalisation
431
+ to preserve the local average value.
432
+ If fwhm is None, no filtering is performed (useful when just removal
433
+ of non-finite values is needed).
434
+
435
+ ensure_finite : :obj:`bool`, optional
436
+ If True, replace every non-finite values (like NaNs) by zero before
437
+ filtering. Default=True.
438
+
439
+ copy : :obj:`bool`, optional
440
+ If True, input array is not modified. True by default: the filtering
441
+ is not performed in-place. Default=True.
442
+
443
+ Returns
444
+ -------
445
+ :class:`NDArray`
446
+ Filtered `arr`.
447
+
448
+ Notes
449
+ -----
450
+ This function is most efficient with arr in C order.
451
+
452
+ """
453
+ # Here, we have to investigate use cases of fwhm. Particularly, if fwhm=0.
454
+ # See issue #1537
455
+ if isinstance(fwhm, (int, float)) and (fwhm == 0.0):
456
+ warnings.warn(
457
+ "The parameter 'fwhm' for smoothing is specified "
458
+ "as {0}. Setting it to None "
459
+ "(no smoothing will be performed)".format(fwhm)
460
+ )
461
+ fwhm = None
462
+ if arr.dtype.kind == "i":
463
+ if arr.dtype == np.int64:
464
+ arr = arr.astype(np.float64)
465
+ else:
466
+ arr = arr.astype(np.float32) # We don't need crazy precision.
467
+ if copy:
468
+ arr = arr.copy()
469
+ if ensure_finite:
470
+ # SPM tends to put NaNs in the data outside the brain
471
+ arr[np.logical_not(np.isfinite(arr))] = 0
472
+ if fwhm is not None:
473
+ fwhm = np.asarray([fwhm]).ravel()
474
+ fwhm = np.asarray([0.0 if elem is None else elem for elem in fwhm])
475
+ affine = affine[:3, :3] # Keep only the scale part.
476
+ fwhm_over_sigma_ratio = np.sqrt(8 * np.log(2)) # FWHM to sigma.
477
+ vox_size = np.sqrt(np.sum(affine**2, axis=0))
478
+ # n.b. FSL specifies blur in sigma, SPM in FWHM
479
+ # FWHM = sigma*sqrt(8*ln(2)) = sigma*2.3548.
480
+ # convert fwhm to sd in voxels see https://github.com/0todd0000/spm1d
481
+ fwhmvox = fwhm / vox_size
482
+ sd = fwhmvox / math.sqrt(8 * math.log(2))
483
+ for n, s in enumerate(sd):
484
+ if s > 0.0:
485
+ gaussian_filter1d(arr, s, output=arr, axis=n)
486
+ return arr
487
+
488
+
489
+ def binary_zero_crossing(fdata: NDArray) -> NDArray:
490
+ """
491
+ binarize (negative voxels are zero)
492
+
493
+ Parameters
494
+ ----------
495
+ fdata : numpy.memmap from Niimg-like object
496
+ Returns
497
+ -------
498
+ :class:`nibabel.nifti1.Nifti1Image`
499
+ """
500
+ edge = np.where(fdata > 0.0, 1, 0)
501
+ edge = distance_transform_edt(edge)
502
+ edge[edge > 1] = 0
503
+ edge[edge > 0] = 1
504
+ edge = edge.astype("uint8")
505
+ return edge
506
+
507
+
508
+ def difference_of_gaussian(
509
+ fdata: NDArray, affine: NDArray, fwhmNarrow: float, ratioopt: bool = True, debug: bool = False
510
+ ) -> NDArray:
511
+ """
512
+ Apply Difference of Gaussian (DoG) filter.
513
+ https://en.wikipedia.org/wiki/Difference_of_Gaussians
514
+ https://en.wikipedia.org/wiki/Marr–Hildreth_algorithm
515
+ D. Marr and E. C. Hildreth. Theory of edge detection. Proceedings of the Royal Society, London B, 207:187-217, 1980
516
+ Parameters
517
+ ----------
518
+ fdata : numpy.memmap from Niimg-like object
519
+ affine : :class:`NDArray`
520
+ (4, 4) matrix, giving affine transformation for image. (3, 3) matrices
521
+ are also accepted (only these coefficients are used).
522
+ fwhmNarrow : int
523
+ Narrow kernel width, in millimeters. Is an arbitrary ratio of wide to narrow kernel.
524
+ human cortex about 2.5mm thick
525
+ Large values yield smoother results
526
+ debug : :obj:`bool`, optional
527
+ Controls the amount of verbosity: True give more messages
528
+ (False means no messages). Default=False.
529
+ Returns
530
+ -------
531
+ :class:`nibabel.nifti1.Nifti1Image`
532
+ """
533
+
534
+ # Hardcode 1.6 as ratio of wide versus narrow FWHM
535
+ # Marr and Hildreth (1980) suggest narrow to wide ratio of 1.6
536
+ # Wilson and Giese (1977) suggest narrow to wide ratio of 1.5
537
+ fwhmWide = fwhmNarrow * 1.6
538
+ # optimization: we will use the narrow Gaussian as the input to the wide filter
539
+ if ratioopt:
540
+ fwhmWide = math.sqrt((fwhmWide * fwhmWide) - (fwhmNarrow * fwhmNarrow))
541
+ if debug:
542
+ print("Narrow/Wide FWHM {} / {}".format(fwhmNarrow, fwhmWide))
543
+ imgNarrow = _smooth_array(fdata, affine, fwhmNarrow)
544
+ imgWide = _smooth_array(imgNarrow, affine, fwhmWide)
545
+ img = imgNarrow - imgWide
546
+ img = binary_zero_crossing(img)
547
+ return img
548
+
549
+
550
+ # for rapidtide purposes, this is the main entry point to the DOG calculation.
551
+ # We are operating on data in memory that are closely associated with the source
552
+ # NIFTI files, so the affine and sizes fields are easy to come by, but unlike the
553
+ # original library, we are not working directly with NIFTI images.
554
+ def calc_DoG(
555
+ thedata: NDArray,
556
+ theaffine: NDArray,
557
+ thesizes: tuple,
558
+ fwhm: float = 3,
559
+ ratioopt: bool = True,
560
+ debug: bool = False,
561
+ ) -> NDArray:
562
+ """
563
+ Find edges of a NIfTI image using the Difference of Gaussian (DoG).
564
+ Parameters
565
+ ----------
566
+ thedata : 3D data array
567
+ Image(s) to run DoG on (see :ref:`extracting_data`
568
+ for a detailed description of the valid input types).
569
+ fwhm : int
570
+ Edge detection strength, as a full-width at half maximum, in millimeters.
571
+ debug : :obj:`bool`, optional
572
+ Controls the amount of verbosity: True give more messages
573
+ (False means no messages). Default=False.
574
+ Returns
575
+ -------
576
+ :class:`nibabel.nifti1.Nifti1Image`
577
+ """
578
+
579
+ if debug:
580
+ print("Input intensity range {}..{}".format(np.nanmin(thedata), np.nanmax(thedata)))
581
+ print("Image shape {}x{}x{}".format(thesizes[1], thesizes[2], thesizes[3]))
582
+
583
+ dehazed_data = dehaze(thedata, 3, debug=debug)
584
+ return difference_of_gaussian(dehazed_data, theaffine, fwhm, ratioopt=ratioopt, debug=debug)
585
+
586
+
587
+ def getclusters(
588
+ theimage: NDArray,
589
+ theaffine: NDArray,
590
+ thesizes: tuple,
591
+ fwhm: float = 5,
592
+ ratioopt: bool = True,
593
+ sizethresh: int = 10,
594
+ debug: bool = False,
595
+ ) -> NDArray:
596
+ if debug:
597
+ print("Detecting clusters..")
598
+ print(f"\t{theimage.shape=}")
599
+ print(f"\t{theaffine=}")
600
+ print(f"\t{thesizes=}")
601
+ print(f"\t{sizethresh=}")
602
+ return separateclusters(
603
+ invertedflood3D(
604
+ calc_DoG(theimage.copy(), theaffine, thesizes, fwhm=fwhm, ratioopt=ratioopt), 1
605
+ ),
606
+ sizethresh=sizethresh,
607
+ )
608
+
609
+
610
+ def interppatch(
611
+ img_data: NDArray, separatedimage: NDArray, method: str = "linear", debug: bool = False
612
+ ) -> tuple[NDArray, NDArray]:
613
+ """
614
+ Interpolate voxel values within labeled regions of a 3D image.
615
+
616
+ This function applies interpolation to each labeled region in a separated image,
617
+ using the specified interpolation method. It returns both the interpolated image
618
+ and a copy of the original image with the same spatial extent.
619
+
620
+ Parameters
621
+ ----------
622
+ img_data : NDArray
623
+ A 3D array representing the input image data to be interpolated.
624
+ separatedimage : NDArray
625
+ A 3D array of integers where each unique positive integer represents a
626
+ distinct region. Zero values are treated as background.
627
+ method : str, optional
628
+ The interpolation method to use. Default is "linear". Other options may
629
+ include "nearest", "cubic", etc., depending on the implementation of
630
+ `interpolate_masked_voxels`.
631
+ debug : bool, optional
632
+ If True, print debug information for each region being processed.
633
+ Default is False.
634
+
635
+ Returns
636
+ -------
637
+ tuple[NDArray, NDArray]
638
+ A tuple containing:
639
+ - `interpolated`: The image with interpolated values in each region.
640
+ - `justboxes`: A copy of the original image data, with the same shape
641
+ as `img_data`, used for reference or visualization purposes.
642
+
643
+ Notes
644
+ -----
645
+ - Each region is processed independently using its bounding box.
646
+ - The function modifies `img_data` only within the bounds of each region.
647
+ - The `interpolate_masked_voxels` function is assumed to handle the actual
648
+ interpolation logic for masked voxels.
649
+
650
+ Examples
651
+ --------
652
+ >>> import numpy as np
653
+ >>> img = np.random.rand(10, 10, 10)
654
+ >>> labels = np.zeros((10, 10, 10))
655
+ >>> labels[3:7, 3:7, 3:7] = 1
656
+ >>> interpolated, boxes = interppatch(img, labels, method="linear")
657
+ """
658
+ interpolated = img_data + 0.0
659
+ justboxes = np.zeros_like(img_data)
660
+ numregions = np.max(separatedimage)
661
+ for region in range(1, numregions + 1):
662
+ if debug:
663
+ print(f"Region {region}:")
664
+ bbmins, bbmaxs = get_bounding_box(separatedimage, region, buffer=3)
665
+ if debug:
666
+ print(f"\t{bbmins}, {bbmaxs} (buffer 3)")
667
+ interpolated[
668
+ bbmins[0] : bbmaxs[0] + 1, bbmins[1] : bbmaxs[1] + 1, bbmins[2] : bbmaxs[2] + 1
669
+ ] = interpolate_masked_voxels(
670
+ img_data[
671
+ bbmins[0] : bbmaxs[0] + 1, bbmins[1] : bbmaxs[1] + 1, bbmins[2] : bbmaxs[2] + 1
672
+ ],
673
+ np.where(
674
+ separatedimage[
675
+ bbmins[0] : bbmaxs[0] + 1, bbmins[1] : bbmaxs[1] + 1, bbmins[2] : bbmaxs[2] + 1
676
+ ]
677
+ > 0,
678
+ True,
679
+ False,
680
+ ),
681
+ method=method,
682
+ )
683
+ justboxes[
684
+ bbmins[0] : bbmaxs[0] + 1, bbmins[1] : bbmaxs[1] + 1, bbmins[2] : bbmaxs[2] + 1
685
+ ] = (
686
+ img_data[
687
+ bbmins[0] : bbmaxs[0] + 1, bbmins[1] : bbmaxs[1] + 1, bbmins[2] : bbmaxs[2] + 1
688
+ ]
689
+ + 0.0
690
+ )
691
+ return interpolated, justboxes
692
+
693
+
694
+ if __name__ == "__main__":
695
+ """
696
+ Apply Gaussian smooth to image
697
+ Parameters
698
+ ----------
699
+ fnm : str
700
+ NIfTI image to convert
701
+ """
702
+ if len(sys.argv) < 2:
703
+ print("No filename provided: I do not know which image to convert!")
704
+ sys.exit()
705
+ fnm = sys.argv[1]
706
+ img, img_data, img_hdr, thedims, thesizes = tide_io.readfromnifti(fnm)
707
+ img_data = img_data.astype(np.float32)
708
+ theaffine = img.affine
709
+
710
+ # update header
711
+ out_hdr = copy.deepcopy(img_hdr)
712
+ out_hdr.set_data_dtype(np.uint8)
713
+ out_hdr["intent_code"] = 0
714
+ out_hdr["scl_slope"] = 1.0
715
+ out_hdr["scl_inter"] = 0.0
716
+ out_hdr["cal_max"] = 0.0
717
+ out_hdr["cal_min"] = 0.0
718
+ pth, nm = os.path.split(fnm)
719
+ if nm.endswith(".nii") or nm.endswith(".nii.gz"):
720
+ if nm.endswith(".nii"):
721
+ nm = nm[:-4]
722
+ elif nm.endswith(".nii.gz"):
723
+ nm = nm[:-7]
724
+ if not pth:
725
+ pth = "."
726
+
727
+ dog = calc_DoG(img_data.copy(), theaffine, thesizes, fwhm=5, ratioopt=True, debug=True)
728
+ outnm = pth + os.path.sep + "z2dog" + nm
729
+ tide_io.savetonifti(dog, out_hdr, outnm)
730
+
731
+ outnm = pth + os.path.sep + "z1img" + nm
732
+ tide_io.savetonifti(img_data, out_hdr, outnm)
733
+
734
+ filledim = invertedflood3D(dog, 1)
735
+ outnm = pth + os.path.sep + "z3fill" + nm
736
+ tide_io.savetonifti(filledim, out_hdr, outnm)
737
+
738
+ separatedimage = separateclusters(filledim, sizethresh=10)
739
+ outnm = pth + os.path.sep + "z4sep" + nm
740
+ tide_io.savetonifti(separatedimage, out_hdr, outnm)
741
+
742
+ otherseparatedimage = getclusters(
743
+ img_data, theaffine, thesizes, fwhm=5, ratioopt=True, sizethresh=10, debug=False
744
+ )
745
+ outnm = pth + os.path.sep + "z5sep" + nm
746
+ tide_io.savetonifti(otherseparatedimage, out_hdr, outnm)
747
+
748
+ interpolated, justboxes = interppatch(img_data, separatedimage)
749
+ outnm = pth + os.path.sep + "z6boxes" + nm
750
+ tide_io.savetonifti(justboxes, out_hdr, outnm)
751
+ outnm = pth + os.path.sep + "z7interp" + nm
752
+ tide_io.savetonifti(interpolated, out_hdr, outnm)