From 53df4ccf7cb8ce369e79750a430b2bf59b4889c2 Mon Sep 17 00:00:00 2001 From: Korbinian Eckstein Date: Wed, 18 Oct 2023 11:33:15 +1000 Subject: [PATCH 1/4] add @main function --- command_line_script/Project.toml | 4 ++++ command_line_script/tgv_qsm.jl | 27 +++++++++++++++++++++++++++ src/tgv.jl | 13 +++++++++++-- 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 command_line_script/Project.toml create mode 100644 command_line_script/tgv_qsm.jl diff --git a/command_line_script/Project.toml b/command_line_script/Project.toml new file mode 100644 index 0000000..4a77658 --- /dev/null +++ b/command_line_script/Project.toml @@ -0,0 +1,4 @@ +[deps] +Comonicon = "863f3e99-da2a-4334-8734-de3dacbe5542" +MriResearchTools = "557dad86-b9bd-4533-8525-1a39684d020f" +QuantitativeSusceptibilityMappingTGV = "bd393529-335a-4aed-902f-5de61cc7ff49" diff --git a/command_line_script/tgv_qsm.jl b/command_line_script/tgv_qsm.jl new file mode 100644 index 0000000..0ada5a2 --- /dev/null +++ b/command_line_script/tgv_qsm.jl @@ -0,0 +1,27 @@ +#!/usr/bin/env -S julia --color=yes --startup-file=no --threads=auto + +# Call with: `/tgv_qsm.jl phase.nii.gz mask.nii.gz --TE 0.025 -o output.nii.gz` +# On windows use: `julia --threads=auto /tgv_qsm.jl ARGS` + +import Pkg +Pkg.activate(@__DIR__) +try + using QuantitativeSusceptibilityMappingTGV, MriResearchTools, Comonicon +catch + Pkg.add(["QuantitativeSusceptibilityMappingTGV", "MriResearchTools", "Comonicon"]) + using QuantitativeSusceptibilityMappingTGV, MriResearchTools, Comonicon +end + +@main function tgv_qsm(fn_phase, fn_mask; TE::Float64, output::String="output.nii.gz", fieldstrength::Float64=3.0, regularization::Float64=2.0, erosions::Int=3, B0_dir::Array{Int}=[0,0,1], dedimensionalize::Bool=false, no_laplacian_correction::Bool=false, step_size::Float64=3.0, no_gpu::Bool=false, type::DataType=Float32, nblocks::Int=32) + println("Starting calculation...") + phase = readphase(fn_phase) + mask = niread(fn_mask) .!= 0 + res = header(phase).pixdim[2:4] + println("Resolution [mm]: $(round.(Float64.(res); digits=2))") + chi = qsm_tgv(phase, mask, res; TE, B0_dir, fieldstrength, regularization, erosions, dedimensionalize, correct_laplacian=!no_laplacian_correction, gpu=!no_gpu, step_size, type, nblocks) + println("Writing output") + savenii(chi, output; header=header(phase)) +end + + +# julia --threads=auto command_line_script/tgv_qsm.jl "D:\TGV\data\challenge\phs_wrap.nii.gz" "D:\TGV\data\challenge\msk.nii.gz" --TE 0.025 --output "D:\TGV\tmp\cmd\output.nii.gz" --no-gpu \ No newline at end of file diff --git a/src/tgv.jl b/src/tgv.jl index fdf8ab5..cfe401b 100644 --- a/src/tgv.jl +++ b/src/tgv.jl @@ -1,3 +1,12 @@ +""" +qsm_tgv(phase, mask, res; TE, kwargs...) + +Optional arguments: + +Example: + chi = qsm_tgv(randn(20,20,20), trues(20,20,20), [1,1,2]; TE=0.025, fieldstrength=3) + +""" function qsm_tgv(phase, mask, res; TE, B0_dir=[0, 0, 1], fieldstrength=3, regularization=2, alpha=get_default_alpha(regularization), step_size=3, iterations=get_default_iterations(res, step_size), erosions=3, dedimensionalize=false, correct_laplacian=true, laplacian=get_laplace_phase_del, type=Float32, gpu=CUDA.functional(), nblocks=32) device, cu = select_device(gpu) phase, res, alpha, fieldstrength, mask = adjust_types(type, phase, res, alpha, fieldstrength, mask) @@ -176,7 +185,7 @@ function select_device(library::Module) println("Using the GPU via Metal") return library.MetalKernels.MetalBackend(), library.MtlArray else - println("Using $(Threads.nthreads()) CPU cores") + println("Using $(Threads.nthreads()) CPU threads") return CPU(), identity end end @@ -186,7 +195,7 @@ function select_device(gpu) println("Using the GPU") return CUDA.CUDAKernels.CUDABackend(), CUDA.cu else - println("Using $(Threads.nthreads()) CPU cores") + println("Using $(Threads.nthreads()) CPU threads") return CPU(), identity end end From c3ba0a3964ca4a767335d95d18657fbbb8af3998 Mon Sep 17 00:00:00 2001 From: Korbinian Eckstein Date: Thu, 19 Oct 2023 14:32:09 +1000 Subject: [PATCH 2/4] add command line usage to README --- README.md | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7c10b51..a1a2078 100644 --- a/README.md +++ b/README.md @@ -12,16 +12,41 @@ This project is an improvement of the [Python source code](http://www.neuroimagi - Langkammer, C., Bredies, K., Poser, B. A., Barth, M., Reishofer, G., Fan, A. P., ... & Ropele, S. (2015). Fast quantitative susceptibility mapping using 3D EPI and total generalized variation. Neuroimage, 111, 622-630. -## Setup +## Command line usage + +### Command line Setup + +1. Install [Julia](https://julialang.org/downloads/) (v1.9+ recommended) +2. Make sure `julia` can be executed from the command line +3. Download the file command_line_script/tgv_qsm.jl +4. Make the file executable: `chmod +x tgv_qsm.jl` + +### Run script + +```bash +/tgv_qsm.jl --help +``` + +On the first usage, the script will download all dependencies. + +## Run in Julia + +### Setup 1. Install [Julia](https://julialang.org/downloads/) (v1.9+ recommended) 2. Install this package Open the julia REPL and type: + ```julia + julia> ] # enters package manager + pkg> add QuantitativeSusceptibilityMappingTGV MriResearchTools + ``` + + or + ```julia import Pkg - Pkg.add(url="https://github.com/korbinian90/QuantitativeSusceptibilityMappingTGV.jl") - Pkg.add("MriResearchTools") # for nifti handling + Pkg.add(["QuantitativeSusceptibilityMappingTGV", "MriResearchTools"]) ``` ## Example to run TGV @@ -91,7 +116,7 @@ It uses the number of CPU threads julia was started with. You can use `julia --t This implementation doesn't support data with an oblique angle acquisition yet. For rotated data, it is recommended to use the [QSMxT pipeline](https://qsmxt.github.io/QSMxT/) for susceptibility mapping, which applies TGV internally -## Self contained example to test if everything works +## Self contained example to test if package works ```julia using QuantitativeSusceptibilityMappingTGV @@ -120,7 +145,7 @@ The parallel CPU version is about twice as fast as the Cython version, the GPU v ## Run with other GPU types -Other GPU types should be supported, however are only minimally tested. +Other GPU types don't work with the command line script. They have to be accessed via Julia (or the command line script modified). ```julia using AMDGPU From af9edf88c1bc53027eb31e304c1aea72a0b527fa Mon Sep 17 00:00:00 2001 From: Korbinian Eckstein Date: Mon, 23 Oct 2023 09:50:38 +1000 Subject: [PATCH 3/4] fix version --- command_line_script/tgv_qsm.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/command_line_script/tgv_qsm.jl b/command_line_script/tgv_qsm.jl index 0ada5a2..aa3615e 100644 --- a/command_line_script/tgv_qsm.jl +++ b/command_line_script/tgv_qsm.jl @@ -12,6 +12,9 @@ catch using QuantitativeSusceptibilityMappingTGV, MriResearchTools, Comonicon end +version = Comonicon.get_version(QuantitativeSusceptibilityMappingTGV) +Comonicon.get_version(::Module) = version + @main function tgv_qsm(fn_phase, fn_mask; TE::Float64, output::String="output.nii.gz", fieldstrength::Float64=3.0, regularization::Float64=2.0, erosions::Int=3, B0_dir::Array{Int}=[0,0,1], dedimensionalize::Bool=false, no_laplacian_correction::Bool=false, step_size::Float64=3.0, no_gpu::Bool=false, type::DataType=Float32, nblocks::Int=32) println("Starting calculation...") phase = readphase(fn_phase) From c2c6274f71dd353550110d77142b59dc96445d34 Mon Sep 17 00:00:00 2001 From: Korbinian Eckstein Date: Mon, 23 Oct 2023 10:15:46 +1000 Subject: [PATCH 4/4] comonicon usage description --- README.md | 17 ++++++++++------- command_line_script/Project.toml | 4 ---- command_line_script/tgv_qsm.jl => tgv_qsm.jl | 19 +++++++++++++------ 3 files changed, 23 insertions(+), 17 deletions(-) delete mode 100644 command_line_script/Project.toml rename command_line_script/tgv_qsm.jl => tgv_qsm.jl (74%) diff --git a/README.md b/README.md index a1a2078..b212ecd 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,24 @@ This project is an improvement of the [Python source code](http://www.neuroimagi 1. Install [Julia](https://julialang.org/downloads/) (v1.9+ recommended) 2. Make sure `julia` can be executed from the command line -3. Download the file command_line_script/tgv_qsm.jl -4. Make the file executable: `chmod +x tgv_qsm.jl` +3. Download the single file tgv_qsm.jl (Download tgv_qsm.jl) ### Run script ```bash -/tgv_qsm.jl --help +julia /tgv_qsm.jl --help ``` On the first usage, the script will download all dependencies. +### Optional configuration + +Under Linux: Make the file executable with `chmod +x tgv_qsm.jl` and run via + +```bash +/tgv_qsm.jl --help +``` + ## Run in Julia ### Setup @@ -86,10 +93,6 @@ On the first usage, the script will download all dependencies. The first execution might take some time to compile the kernels (~1min). -## Command Line Interface - -Coming soon - ## Settings The default settings were optimized for brain QSM and should lead to good results independently of the acquired resolution. diff --git a/command_line_script/Project.toml b/command_line_script/Project.toml deleted file mode 100644 index 4a77658..0000000 --- a/command_line_script/Project.toml +++ /dev/null @@ -1,4 +0,0 @@ -[deps] -Comonicon = "863f3e99-da2a-4334-8734-de3dacbe5542" -MriResearchTools = "557dad86-b9bd-4533-8525-1a39684d020f" -QuantitativeSusceptibilityMappingTGV = "bd393529-335a-4aed-902f-5de61cc7ff49" diff --git a/command_line_script/tgv_qsm.jl b/tgv_qsm.jl similarity index 74% rename from command_line_script/tgv_qsm.jl rename to tgv_qsm.jl index aa3615e..c02defa 100644 --- a/command_line_script/tgv_qsm.jl +++ b/tgv_qsm.jl @@ -1,10 +1,20 @@ #!/usr/bin/env -S julia --color=yes --startup-file=no --threads=auto -# Call with: `/tgv_qsm.jl phase.nii.gz mask.nii.gz --TE 0.025 -o output.nii.gz` +## Usage + +# Call with: `/tgv_qsm.jl ARGS` # On windows use: `julia --threads=auto /tgv_qsm.jl ARGS` +# Example call: +# `./tgv_qsm.jl phase.nii.gz mask.nii.gz --TE 0.025 --output output.nii.gz --no-gpu + import Pkg -Pkg.activate(@__DIR__) + +## Uncomment to use a local julia package directory instead of the global one +# package_dir = joinpath(@__DIR__, ".tgv_cmd_packages") +# mkpath(package_dir) +# Pkg.activate(package_dir) + try using QuantitativeSusceptibilityMappingTGV, MriResearchTools, Comonicon catch @@ -20,11 +30,8 @@ Comonicon.get_version(::Module) = version phase = readphase(fn_phase) mask = niread(fn_mask) .!= 0 res = header(phase).pixdim[2:4] - println("Resolution [mm]: $(round.(Float64.(res); digits=2))") + println("Resolution from NIfTI header [mm]: $(round.(Float64.(res); digits=2))") chi = qsm_tgv(phase, mask, res; TE, B0_dir, fieldstrength, regularization, erosions, dedimensionalize, correct_laplacian=!no_laplacian_correction, gpu=!no_gpu, step_size, type, nblocks) println("Writing output") savenii(chi, output; header=header(phase)) end - - -# julia --threads=auto command_line_script/tgv_qsm.jl "D:\TGV\data\challenge\phs_wrap.nii.gz" "D:\TGV\data\challenge\msk.nii.gz" --TE 0.025 --output "D:\TGV\tmp\cmd\output.nii.gz" --no-gpu \ No newline at end of file