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.
- cloud/gmscalc-HCPYA +1 -1
- cloud/mount-and-run +2 -0
- cloud/rapidtide-HCPYA +3 -3
- rapidtide/Colortables.py +538 -38
- rapidtide/OrthoImageItem.py +1094 -51
- rapidtide/RapidtideDataset.py +1709 -114
- rapidtide/__init__.py +0 -8
- rapidtide/_version.py +4 -4
- rapidtide/calccoherence.py +242 -97
- rapidtide/calcnullsimfunc.py +240 -140
- rapidtide/calcsimfunc.py +314 -129
- rapidtide/correlate.py +1211 -389
- rapidtide/data/examples/src/testLD +56 -0
- rapidtide/data/examples/src/test_findmaxlag.py +2 -2
- rapidtide/data/examples/src/test_mlregressallt.py +32 -17
- rapidtide/data/examples/src/testalign +1 -1
- rapidtide/data/examples/src/testatlasaverage +35 -7
- rapidtide/data/examples/src/testboth +21 -0
- rapidtide/data/examples/src/testcifti +11 -0
- rapidtide/data/examples/src/testdelayvar +13 -0
- rapidtide/data/examples/src/testdlfilt +25 -0
- rapidtide/data/examples/src/testfft +35 -0
- rapidtide/data/examples/src/testfileorfloat +37 -0
- rapidtide/data/examples/src/testfmri +92 -42
- rapidtide/data/examples/src/testfuncs +3 -3
- rapidtide/data/examples/src/testglmfilt +8 -6
- rapidtide/data/examples/src/testhappy +84 -51
- rapidtide/data/examples/src/testinitdelay +19 -0
- rapidtide/data/examples/src/testmodels +33 -0
- rapidtide/data/examples/src/testnewrefine +26 -0
- rapidtide/data/examples/src/testnoiseamp +2 -2
- rapidtide/data/examples/src/testppgproc +17 -0
- rapidtide/data/examples/src/testrefineonly +22 -0
- rapidtide/data/examples/src/testretro +26 -13
- rapidtide/data/examples/src/testretrolagtcs +16 -0
- rapidtide/data/examples/src/testrolloff +11 -0
- rapidtide/data/examples/src/testsimdata +45 -28
- rapidtide/data/models/model_cnn_pytorch/loss.png +0 -0
- rapidtide/data/models/model_cnn_pytorch/loss.txt +1 -0
- rapidtide/data/models/model_cnn_pytorch/model.pth +0 -0
- rapidtide/data/models/model_cnn_pytorch/model_meta.json +68 -0
- rapidtide/data/models/model_cnn_pytorch_fulldata/loss.png +0 -0
- rapidtide/data/models/model_cnn_pytorch_fulldata/loss.txt +1 -0
- rapidtide/data/models/model_cnn_pytorch_fulldata/model.pth +0 -0
- rapidtide/data/models/model_cnn_pytorch_fulldata/model_meta.json +80 -0
- rapidtide/data/models/model_cnnbp_pytorch_fullldata/loss.png +0 -0
- rapidtide/data/models/model_cnnbp_pytorch_fullldata/loss.txt +1 -0
- rapidtide/data/models/model_cnnbp_pytorch_fullldata/model.pth +0 -0
- rapidtide/data/models/model_cnnbp_pytorch_fullldata/model_meta.json +138 -0
- rapidtide/data/models/model_cnnfft_pytorch_fulldata/loss.png +0 -0
- rapidtide/data/models/model_cnnfft_pytorch_fulldata/loss.txt +1 -0
- rapidtide/data/models/model_cnnfft_pytorch_fulldata/model.pth +0 -0
- rapidtide/data/models/model_cnnfft_pytorch_fulldata/model_meta.json +128 -0
- rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/loss.png +0 -0
- rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/loss.txt +1 -0
- rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/model.pth +0 -0
- rapidtide/data/models/model_ppgattention_pytorch_w128_fulldata/model_meta.json +49 -0
- rapidtide/data/models/model_revised_tf2/model.keras +0 -0
- rapidtide/data/models/{model_serdar → model_revised_tf2}/model_meta.json +1 -1
- rapidtide/data/models/model_serdar2_tf2/model.keras +0 -0
- rapidtide/data/models/{model_serdar2 → model_serdar2_tf2}/model_meta.json +1 -1
- rapidtide/data/models/model_serdar_tf2/model.keras +0 -0
- rapidtide/data/models/{model_revised → model_serdar_tf2}/model_meta.json +1 -1
- rapidtide/data/reference/HCP1200v2_MTT_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_binmask_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_csf_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_gray_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_graylaghist.json +7 -0
- rapidtide/data/reference/HCP1200v2_graylaghist.tsv.gz +0 -0
- rapidtide/data/reference/HCP1200v2_laghist.json +7 -0
- rapidtide/data/reference/HCP1200v2_laghist.tsv.gz +0 -0
- rapidtide/data/reference/HCP1200v2_mask_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_maxcorr_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_maxtime_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_maxwidth_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_negmask_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_timepercentile_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_white_2mm.nii.gz +0 -0
- rapidtide/data/reference/HCP1200v2_whitelaghist.json +7 -0
- rapidtide/data/reference/HCP1200v2_whitelaghist.tsv.gz +0 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2.xml +131 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2_regions.txt +60 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1-seg2_space-MNI152NLin6Asym_2mm.nii.gz +0 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm.nii.gz +0 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin2009cAsym_2mm_mask.nii.gz +0 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL1_space-MNI152NLin6Asym_2mm_mask.nii.gz +0 -0
- rapidtide/data/reference/JHU-ArterialTerritoriesNoVent-LVL2_space-MNI152NLin6Asym_2mm_mask.nii.gz +0 -0
- rapidtide/data/reference/MNI152_T1_1mm_Brain_FAST_seg.nii.gz +0 -0
- rapidtide/data/reference/MNI152_T1_1mm_Brain_Mask.nii.gz +0 -0
- rapidtide/data/reference/MNI152_T1_2mm_Brain_FAST_seg.nii.gz +0 -0
- rapidtide/data/reference/MNI152_T1_2mm_Brain_Mask.nii.gz +0 -0
- rapidtide/decorators.py +91 -0
- rapidtide/dlfilter.py +2553 -414
- rapidtide/dlfiltertorch.py +5201 -0
- rapidtide/externaltools.py +328 -13
- rapidtide/fMRIData_class.py +108 -92
- rapidtide/ffttools.py +168 -0
- rapidtide/filter.py +2704 -1462
- rapidtide/fit.py +2361 -579
- rapidtide/genericmultiproc.py +197 -0
- rapidtide/happy_supportfuncs.py +3255 -548
- rapidtide/helper_classes.py +587 -1116
- rapidtide/io.py +2569 -468
- rapidtide/linfitfiltpass.py +784 -0
- rapidtide/makelaggedtcs.py +267 -97
- rapidtide/maskutil.py +555 -25
- rapidtide/miscmath.py +835 -144
- rapidtide/multiproc.py +217 -44
- rapidtide/patchmatch.py +752 -0
- rapidtide/peakeval.py +32 -32
- rapidtide/ppgproc.py +2205 -0
- rapidtide/qualitycheck.py +353 -40
- rapidtide/refinedelay.py +854 -0
- rapidtide/refineregressor.py +939 -0
- rapidtide/resample.py +725 -204
- rapidtide/scripts/__init__.py +1 -0
- rapidtide/scripts/{adjustoffset → adjustoffset.py} +7 -2
- rapidtide/scripts/{aligntcs → aligntcs.py} +7 -2
- rapidtide/scripts/{applydlfilter → applydlfilter.py} +7 -2
- rapidtide/scripts/applyppgproc.py +28 -0
- rapidtide/scripts/{atlasaverage → atlasaverage.py} +7 -2
- rapidtide/scripts/{atlastool → atlastool.py} +7 -2
- rapidtide/scripts/{calcicc → calcicc.py} +7 -2
- rapidtide/scripts/{calctexticc → calctexticc.py} +7 -2
- rapidtide/scripts/{calcttest → calcttest.py} +7 -2
- rapidtide/scripts/{ccorrica → ccorrica.py} +7 -2
- rapidtide/scripts/delayvar.py +28 -0
- rapidtide/scripts/{diffrois → diffrois.py} +7 -2
- rapidtide/scripts/{endtidalproc → endtidalproc.py} +7 -2
- rapidtide/scripts/{fdica → fdica.py} +7 -2
- rapidtide/scripts/{filtnifti → filtnifti.py} +7 -2
- rapidtide/scripts/{filttc → filttc.py} +7 -2
- rapidtide/scripts/{fingerprint → fingerprint.py} +20 -16
- rapidtide/scripts/{fixtr → fixtr.py} +7 -2
- rapidtide/scripts/{gmscalc → gmscalc.py} +7 -2
- rapidtide/scripts/{happy → happy.py} +7 -2
- rapidtide/scripts/{happy2std → happy2std.py} +7 -2
- rapidtide/scripts/{happywarp → happywarp.py} +8 -4
- rapidtide/scripts/{histnifti → histnifti.py} +7 -2
- rapidtide/scripts/{histtc → histtc.py} +7 -2
- rapidtide/scripts/{glmfilt → linfitfilt.py} +7 -4
- rapidtide/scripts/{localflow → localflow.py} +7 -2
- rapidtide/scripts/{mergequality → mergequality.py} +7 -2
- rapidtide/scripts/{pairproc → pairproc.py} +7 -2
- rapidtide/scripts/{pairwisemergenifti → pairwisemergenifti.py} +7 -2
- rapidtide/scripts/{physiofreq → physiofreq.py} +7 -2
- rapidtide/scripts/{pixelcomp → pixelcomp.py} +7 -2
- rapidtide/scripts/{plethquality → plethquality.py} +7 -2
- rapidtide/scripts/{polyfitim → polyfitim.py} +7 -2
- rapidtide/scripts/{proj2flow → proj2flow.py} +7 -2
- rapidtide/scripts/{rankimage → rankimage.py} +7 -2
- rapidtide/scripts/{rapidtide → rapidtide.py} +7 -2
- rapidtide/scripts/{rapidtide2std → rapidtide2std.py} +7 -2
- rapidtide/scripts/{resamplenifti → resamplenifti.py} +7 -2
- rapidtide/scripts/{resampletc → resampletc.py} +7 -2
- rapidtide/scripts/retrolagtcs.py +28 -0
- rapidtide/scripts/retroregress.py +28 -0
- rapidtide/scripts/{roisummarize → roisummarize.py} +7 -2
- rapidtide/scripts/{runqualitycheck → runqualitycheck.py} +7 -2
- rapidtide/scripts/{showarbcorr → showarbcorr.py} +7 -2
- rapidtide/scripts/{showhist → showhist.py} +7 -2
- rapidtide/scripts/{showstxcorr → showstxcorr.py} +7 -2
- rapidtide/scripts/{showtc → showtc.py} +7 -2
- rapidtide/scripts/{showxcorr_legacy → showxcorr_legacy.py} +8 -8
- rapidtide/scripts/{showxcorrx → showxcorrx.py} +7 -2
- rapidtide/scripts/{showxy → showxy.py} +7 -2
- rapidtide/scripts/{simdata → simdata.py} +7 -2
- rapidtide/scripts/{spatialdecomp → spatialdecomp.py} +7 -2
- rapidtide/scripts/{spatialfit → spatialfit.py} +7 -2
- rapidtide/scripts/{spatialmi → spatialmi.py} +7 -2
- rapidtide/scripts/{spectrogram → spectrogram.py} +7 -2
- rapidtide/scripts/stupidramtricks.py +238 -0
- rapidtide/scripts/{synthASL → synthASL.py} +7 -2
- rapidtide/scripts/{tcfrom2col → tcfrom2col.py} +7 -2
- rapidtide/scripts/{tcfrom3col → tcfrom3col.py} +7 -2
- rapidtide/scripts/{temporaldecomp → temporaldecomp.py} +7 -2
- rapidtide/scripts/{testhrv → testhrv.py} +1 -1
- rapidtide/scripts/{threeD → threeD.py} +7 -2
- rapidtide/scripts/{tidepool → tidepool.py} +7 -2
- rapidtide/scripts/{variabilityizer → variabilityizer.py} +7 -2
- rapidtide/simFuncClasses.py +2113 -0
- rapidtide/simfuncfit.py +312 -108
- rapidtide/stats.py +579 -247
- rapidtide/tests/.coveragerc +27 -6
- rapidtide-2.9.6.data/scripts/fdica → rapidtide/tests/cleanposttest +4 -6
- rapidtide/tests/happycomp +9 -0
- rapidtide/tests/resethappytargets +1 -1
- rapidtide/tests/resetrapidtidetargets +1 -1
- rapidtide/tests/resettargets +1 -1
- rapidtide/tests/runlocaltest +3 -3
- rapidtide/tests/showkernels +1 -1
- rapidtide/tests/test_aliasedcorrelate.py +4 -4
- rapidtide/tests/test_aligntcs.py +1 -1
- rapidtide/tests/test_calcicc.py +1 -1
- rapidtide/tests/test_cleanregressor.py +184 -0
- rapidtide/tests/test_congrid.py +70 -81
- rapidtide/tests/test_correlate.py +1 -1
- rapidtide/tests/test_corrpass.py +4 -4
- rapidtide/tests/test_delayestimation.py +54 -59
- rapidtide/tests/test_dlfiltertorch.py +437 -0
- rapidtide/tests/test_doresample.py +2 -2
- rapidtide/tests/test_externaltools.py +69 -0
- rapidtide/tests/test_fastresampler.py +9 -5
- rapidtide/tests/test_filter.py +96 -57
- rapidtide/tests/test_findmaxlag.py +50 -19
- rapidtide/tests/test_fullrunhappy_v1.py +15 -10
- rapidtide/tests/test_fullrunhappy_v2.py +19 -13
- rapidtide/tests/test_fullrunhappy_v3.py +28 -13
- rapidtide/tests/test_fullrunhappy_v4.py +30 -11
- rapidtide/tests/test_fullrunhappy_v5.py +62 -0
- rapidtide/tests/test_fullrunrapidtide_v1.py +61 -7
- rapidtide/tests/test_fullrunrapidtide_v2.py +26 -14
- rapidtide/tests/test_fullrunrapidtide_v3.py +28 -8
- rapidtide/tests/test_fullrunrapidtide_v4.py +16 -8
- rapidtide/tests/test_fullrunrapidtide_v5.py +15 -6
- rapidtide/tests/test_fullrunrapidtide_v6.py +142 -0
- rapidtide/tests/test_fullrunrapidtide_v7.py +114 -0
- rapidtide/tests/test_fullrunrapidtide_v8.py +66 -0
- rapidtide/tests/test_getparsers.py +158 -0
- rapidtide/tests/test_io.py +59 -18
- rapidtide/tests/{test_glmpass.py → test_linfitfiltpass.py} +10 -10
- rapidtide/tests/test_mi.py +1 -1
- rapidtide/tests/test_miscmath.py +1 -1
- rapidtide/tests/test_motionregress.py +5 -5
- rapidtide/tests/test_nullcorr.py +6 -9
- rapidtide/tests/test_padvec.py +216 -0
- rapidtide/tests/test_parserfuncs.py +101 -0
- rapidtide/tests/test_phaseanalysis.py +1 -1
- rapidtide/tests/test_rapidtideparser.py +59 -53
- rapidtide/tests/test_refinedelay.py +296 -0
- rapidtide/tests/test_runmisc.py +5 -5
- rapidtide/tests/test_sharedmem.py +60 -0
- rapidtide/tests/test_simroundtrip.py +132 -0
- rapidtide/tests/test_simulate.py +1 -1
- rapidtide/tests/test_stcorrelate.py +4 -2
- rapidtide/tests/test_timeshift.py +2 -2
- rapidtide/tests/test_valtoindex.py +1 -1
- rapidtide/tests/test_zRapidtideDataset.py +5 -3
- rapidtide/tests/utils.py +10 -9
- rapidtide/tidepoolTemplate.py +88 -70
- rapidtide/tidepoolTemplate.ui +60 -46
- rapidtide/tidepoolTemplate_alt.py +88 -53
- rapidtide/tidepoolTemplate_alt.ui +62 -52
- rapidtide/tidepoolTemplate_alt_qt6.py +921 -0
- rapidtide/tidepoolTemplate_big.py +1125 -0
- rapidtide/tidepoolTemplate_big.ui +2386 -0
- rapidtide/tidepoolTemplate_big_qt6.py +1129 -0
- rapidtide/tidepoolTemplate_qt6.py +793 -0
- rapidtide/util.py +1389 -148
- rapidtide/voxelData.py +1048 -0
- rapidtide/wiener.py +138 -25
- rapidtide/wiener2.py +114 -8
- rapidtide/workflows/adjustoffset.py +107 -5
- rapidtide/workflows/aligntcs.py +86 -3
- rapidtide/workflows/applydlfilter.py +231 -89
- rapidtide/workflows/applyppgproc.py +540 -0
- rapidtide/workflows/atlasaverage.py +309 -48
- rapidtide/workflows/atlastool.py +130 -9
- rapidtide/workflows/calcSimFuncMap.py +490 -0
- rapidtide/workflows/calctexticc.py +202 -10
- rapidtide/workflows/ccorrica.py +123 -15
- rapidtide/workflows/cleanregressor.py +415 -0
- rapidtide/workflows/delayvar.py +1268 -0
- rapidtide/workflows/diffrois.py +84 -6
- rapidtide/workflows/endtidalproc.py +149 -9
- rapidtide/workflows/fdica.py +197 -17
- rapidtide/workflows/filtnifti.py +71 -4
- rapidtide/workflows/filttc.py +76 -5
- rapidtide/workflows/fitSimFuncMap.py +578 -0
- rapidtide/workflows/fixtr.py +74 -4
- rapidtide/workflows/gmscalc.py +116 -6
- rapidtide/workflows/happy.py +1242 -480
- rapidtide/workflows/happy2std.py +145 -13
- rapidtide/workflows/happy_parser.py +277 -59
- rapidtide/workflows/histnifti.py +120 -4
- rapidtide/workflows/histtc.py +85 -4
- rapidtide/workflows/{glmfilt.py → linfitfilt.py} +128 -14
- rapidtide/workflows/localflow.py +329 -29
- rapidtide/workflows/mergequality.py +80 -4
- rapidtide/workflows/niftidecomp.py +323 -19
- rapidtide/workflows/niftistats.py +178 -8
- rapidtide/workflows/pairproc.py +99 -5
- rapidtide/workflows/pairwisemergenifti.py +86 -3
- rapidtide/workflows/parser_funcs.py +1488 -56
- rapidtide/workflows/physiofreq.py +139 -12
- rapidtide/workflows/pixelcomp.py +211 -9
- rapidtide/workflows/plethquality.py +105 -23
- rapidtide/workflows/polyfitim.py +159 -19
- rapidtide/workflows/proj2flow.py +76 -3
- rapidtide/workflows/rankimage.py +115 -8
- rapidtide/workflows/rapidtide.py +1785 -1858
- rapidtide/workflows/rapidtide2std.py +101 -3
- rapidtide/workflows/rapidtide_parser.py +590 -389
- rapidtide/workflows/refineDelayMap.py +249 -0
- rapidtide/workflows/refineRegressor.py +1215 -0
- rapidtide/workflows/regressfrommaps.py +308 -0
- rapidtide/workflows/resamplenifti.py +86 -4
- rapidtide/workflows/resampletc.py +92 -4
- rapidtide/workflows/retrolagtcs.py +442 -0
- rapidtide/workflows/retroregress.py +1501 -0
- rapidtide/workflows/roisummarize.py +176 -7
- rapidtide/workflows/runqualitycheck.py +72 -7
- rapidtide/workflows/showarbcorr.py +172 -16
- rapidtide/workflows/showhist.py +87 -3
- rapidtide/workflows/showstxcorr.py +161 -4
- rapidtide/workflows/showtc.py +172 -10
- rapidtide/workflows/showxcorrx.py +250 -62
- rapidtide/workflows/showxy.py +186 -16
- rapidtide/workflows/simdata.py +418 -112
- rapidtide/workflows/spatialfit.py +83 -8
- rapidtide/workflows/spatialmi.py +252 -29
- rapidtide/workflows/spectrogram.py +306 -33
- rapidtide/workflows/synthASL.py +157 -6
- rapidtide/workflows/tcfrom2col.py +77 -3
- rapidtide/workflows/tcfrom3col.py +75 -3
- rapidtide/workflows/tidepool.py +3829 -666
- rapidtide/workflows/utils.py +45 -19
- rapidtide/workflows/utils_doc.py +293 -0
- rapidtide/workflows/variabilityizer.py +118 -5
- {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info}/METADATA +30 -223
- rapidtide-3.1.3.dist-info/RECORD +393 -0
- {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info}/WHEEL +1 -1
- rapidtide-3.1.3.dist-info/entry_points.txt +65 -0
- rapidtide-3.1.3.dist-info/top_level.txt +2 -0
- rapidtide/calcandfitcorrpairs.py +0 -262
- rapidtide/data/examples/src/testoutputsize +0 -45
- rapidtide/data/models/model_revised/model.h5 +0 -0
- rapidtide/data/models/model_serdar/model.h5 +0 -0
- rapidtide/data/models/model_serdar2/model.h5 +0 -0
- rapidtide/data/reference/ASPECTS_nlin_asym_09c_2mm.nii.gz +0 -0
- rapidtide/data/reference/ASPECTS_nlin_asym_09c_2mm_mask.nii.gz +0 -0
- rapidtide/data/reference/ATTbasedFlowTerritories_split_nlin_asym_09c_2mm.nii.gz +0 -0
- rapidtide/data/reference/ATTbasedFlowTerritories_split_nlin_asym_09c_2mm_mask.nii.gz +0 -0
- rapidtide/data/reference/HCP1200_binmask_2mm_2009c_asym.nii.gz +0 -0
- rapidtide/data/reference/HCP1200_lag_2mm_2009c_asym.nii.gz +0 -0
- rapidtide/data/reference/HCP1200_mask_2mm_2009c_asym.nii.gz +0 -0
- rapidtide/data/reference/HCP1200_negmask_2mm_2009c_asym.nii.gz +0 -0
- rapidtide/data/reference/HCP1200_sigma_2mm_2009c_asym.nii.gz +0 -0
- rapidtide/data/reference/HCP1200_strength_2mm_2009c_asym.nii.gz +0 -0
- rapidtide/glmpass.py +0 -434
- rapidtide/refine_factored.py +0 -641
- rapidtide/scripts/retroglm +0 -23
- rapidtide/workflows/glmfrommaps.py +0 -202
- rapidtide/workflows/retroglm.py +0 -643
- rapidtide-2.9.6.data/scripts/adjustoffset +0 -23
- rapidtide-2.9.6.data/scripts/aligntcs +0 -23
- rapidtide-2.9.6.data/scripts/applydlfilter +0 -23
- rapidtide-2.9.6.data/scripts/atlasaverage +0 -23
- rapidtide-2.9.6.data/scripts/atlastool +0 -23
- rapidtide-2.9.6.data/scripts/calcicc +0 -22
- rapidtide-2.9.6.data/scripts/calctexticc +0 -23
- rapidtide-2.9.6.data/scripts/calcttest +0 -22
- rapidtide-2.9.6.data/scripts/ccorrica +0 -23
- rapidtide-2.9.6.data/scripts/diffrois +0 -23
- rapidtide-2.9.6.data/scripts/endtidalproc +0 -23
- rapidtide-2.9.6.data/scripts/filtnifti +0 -23
- rapidtide-2.9.6.data/scripts/filttc +0 -23
- rapidtide-2.9.6.data/scripts/fingerprint +0 -593
- rapidtide-2.9.6.data/scripts/fixtr +0 -23
- rapidtide-2.9.6.data/scripts/glmfilt +0 -24
- rapidtide-2.9.6.data/scripts/gmscalc +0 -22
- rapidtide-2.9.6.data/scripts/happy +0 -25
- rapidtide-2.9.6.data/scripts/happy2std +0 -23
- rapidtide-2.9.6.data/scripts/happywarp +0 -350
- rapidtide-2.9.6.data/scripts/histnifti +0 -23
- rapidtide-2.9.6.data/scripts/histtc +0 -23
- rapidtide-2.9.6.data/scripts/localflow +0 -23
- rapidtide-2.9.6.data/scripts/mergequality +0 -23
- rapidtide-2.9.6.data/scripts/pairproc +0 -23
- rapidtide-2.9.6.data/scripts/pairwisemergenifti +0 -23
- rapidtide-2.9.6.data/scripts/physiofreq +0 -23
- rapidtide-2.9.6.data/scripts/pixelcomp +0 -23
- rapidtide-2.9.6.data/scripts/plethquality +0 -23
- rapidtide-2.9.6.data/scripts/polyfitim +0 -23
- rapidtide-2.9.6.data/scripts/proj2flow +0 -23
- rapidtide-2.9.6.data/scripts/rankimage +0 -23
- rapidtide-2.9.6.data/scripts/rapidtide +0 -23
- rapidtide-2.9.6.data/scripts/rapidtide2std +0 -23
- rapidtide-2.9.6.data/scripts/resamplenifti +0 -23
- rapidtide-2.9.6.data/scripts/resampletc +0 -23
- rapidtide-2.9.6.data/scripts/retroglm +0 -23
- rapidtide-2.9.6.data/scripts/roisummarize +0 -23
- rapidtide-2.9.6.data/scripts/runqualitycheck +0 -23
- rapidtide-2.9.6.data/scripts/showarbcorr +0 -23
- rapidtide-2.9.6.data/scripts/showhist +0 -23
- rapidtide-2.9.6.data/scripts/showstxcorr +0 -23
- rapidtide-2.9.6.data/scripts/showtc +0 -23
- rapidtide-2.9.6.data/scripts/showxcorr_legacy +0 -536
- rapidtide-2.9.6.data/scripts/showxcorrx +0 -23
- rapidtide-2.9.6.data/scripts/showxy +0 -23
- rapidtide-2.9.6.data/scripts/simdata +0 -23
- rapidtide-2.9.6.data/scripts/spatialdecomp +0 -23
- rapidtide-2.9.6.data/scripts/spatialfit +0 -23
- rapidtide-2.9.6.data/scripts/spatialmi +0 -23
- rapidtide-2.9.6.data/scripts/spectrogram +0 -23
- rapidtide-2.9.6.data/scripts/synthASL +0 -23
- rapidtide-2.9.6.data/scripts/tcfrom2col +0 -23
- rapidtide-2.9.6.data/scripts/tcfrom3col +0 -23
- rapidtide-2.9.6.data/scripts/temporaldecomp +0 -23
- rapidtide-2.9.6.data/scripts/threeD +0 -236
- rapidtide-2.9.6.data/scripts/tidepool +0 -23
- rapidtide-2.9.6.data/scripts/variabilityizer +0 -23
- rapidtide-2.9.6.dist-info/RECORD +0 -359
- rapidtide-2.9.6.dist-info/top_level.txt +0 -86
- {rapidtide-2.9.6.dist-info → rapidtide-3.1.3.dist-info/licenses}/LICENSE +0 -0
rapidtide/patchmatch.py
ADDED
|
@@ -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)
|