Coder Social home page Coder Social logo

brain-to / girfreco.jl Goto Github PK

View Code? Open in Web Editor NEW
15.0 4.0 1.0 117.81 MB

An Open-Source End-to-End Pipeline for Spiral Magnetic Resonance Image (MRI) Reconstruction in Julia

Home Page: https://brain-to.github.io/GIRFReco.jl/

License: BSD 3-Clause "New" or "Revised" License

Julia 72.51% XSLT 17.23% Shell 0.74% TeX 9.52%
mri mri-brain mri-reconstruction

girfreco.jl's Introduction

Dev codecov DOI

GIRFReco.jl: An open-source pipeline for spiral MRI Reconstruction in Julia

Introduction

This package provides an image reconstruction pipeline for real-world non-Cartesian MRI, designed particularly for spiral diffusion imaging. It is completely implemented in Julia using original code and external packages, e.g., MRIReco.jl for the off-resonance (B0) corrections & core iterative reconstruction tasks, and MRIGradients.jl for the correction of gradient waveforms due to system imperfections via the Gradient Impulse Response Function (GIRF).

This repository includes working examples for spiral reconstruction with GIRF correction of k-space trajectory (kx-ky-kz, or k1 as the first order GIRF) and B0 eddy currents (k0 as zeroth order GIRF), iterative reconstruction (cg-SENSE) and a Cartesian reconstruction example for coil sensitivity and off-resonance map calculation.

The data for the phantom reconstruction joss_demo.jl is publicly available here. The path of the data, params_general[:project_path], needs to be modified accordingly in the config file.

For a full introduction, please refer to our manuscript for a full description of this package, including background, purpose, implementation, and the information of dependent packages.

Quick Installation

To install the package, start Julia and type the following command in the REPL:

julia>]add GIRFReco

It is also possible to install the package with the following code:

using Pkg
Pkg.add("GIRFReco")

Getting Started

There is an example script in the example folder. A quick start guide of this script can be found here.

Development Installation

  1. To get started, make sure you have Julia installed (>=1.9).
  2. Clone the GIRFReco.jl project from Github to a local directory by git clone git@github:BRAIN-TO/GIRFReco.jl or to copy it to the dev directory using the Quick Installation and the following command:
julia> ]dev GIRFReco
  1. Open a Julia REPL under the path of your local GIRFReco.jl folder. Our own development configuration is Visual Studio Code with the Julia extension. Other environments for Julia should work in a similar way with possible extra configurations.
  2. In the Julia REPL, type ] to enter the package manager.
  3. In Julia package manager, Type activate . to activate a the Julia environment defined in Project.toml file in the GIRFReco.jl repo.
  4. Add the MRIGradients.jl package by add MRIGradients.
  5. Install all additional dependent packages:
    • add MRIReco
    • add MRIFiles
    • add MRIBase
    • add ImageUtils
    • add MRICoilSensitivities
    • add FileIO
  6. Use command instantiate in Julia package manager to install all of the dependencies. This may be all you need to do (i.e you might be able to skip steps 7,8).
  7. Download the demonstrating data from Zenodo (https://doi.org/10.5281/zenodo.6510020) and extract to your local folder. Make sure you have both reading and writing privileges on the data folder.
    • Note: The data downloading might take a few minutes.
  8. Open the demo script joss_demo.jl under the folder docs/lit/examples/, make sure to set rootProjPath as the folder that stores demo data, then run it in Julia REPL.

Example Results

  1. Phantom Images. (Presented at ISMRM 2022, p.2435): using a dataset with 4 spiral interleaves for images with 1.1mm in-plane resolution. The code and dataset are both described in the previous section.

    cd docs/lit/examples
    julia joss_demo.jl

The final reconstructed spiral images are similar to the following ones:

Phantom Image

  1. In-vivo Brain Image. Below is a set of T2-weighted in-vivo spiral images reconstructed with both B0 and GIRF corrections using the scripts in this repo. We demonstrated the step-by-step improvement of GIRF and B0 corrections, respectively. Please note that the raw in-vivo data is not publically available due to REB restrictions.

In-vivo Brain Image

girfreco.jl's People

Contributors

alexjaffray avatar jaffraya avatar mrikasper avatar nbwuzhe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

Forkers

mathieuboudreau

girfreco.jl's Issues

Documentation for exported functions (API)

I think that the exported functions must be documented. This also apply to the exported functions in MRIGradients.

This can be done by adding a docstring at the top of the function definitions.

Recommending Development Environments?

I'm being pedantic here, in my opinion recommendations for external tools should be linked to direct benefit for working with this specific project. Using VS code with the Julia extension is probably a good choice for other purposes as well, and so this information is neither specific to this project nor required for running it, and so has little relevance in this context. I appreciate and assume you want to make it easier for beginners; if there is another reason I don't see at the moment please elaborate in the README.md. Happy to get a second opinion on this :)

Automated tests

Hi, I just wanted to point out that despite seeing some files with the name "test", it is the standard practice in Julia packages to put them all in the folder /test with a file runtests.jl to run them all. The tests should have their own Project.toml, so for example, if you read a .mat with MAT.jl to compare the results of the reconstruction, you do not need to add that dependency to the main package. Tests should verify the core functionality of the package each time a commit/pull request is merged using continuous integration (CI) with GitHub actions. Currently I do not see this being done in .github/workflows/. This should also be done in the other developed packages like MRIGradients.jl, or tested in this repo.

By doing it this way the user could verify that the package is working properly by doing ] test GIRFReco.

I also recommend including CodeCov to measure how many parts of the code are currently being tested.

Reduction of strong dependencies in Project.toml

I think the number of strong dependencies in the package needs to be reduced to a minimum to avoid compatibility problems, based on what you want the core package to do. As @felixhorger mentioned, one way of doing this is to separate the package into various simpler packages with minimal dependencies (which I am a fan of!). Another way of doing this, which is easier to implement, is to use package extensions.

Having said that, there are some packages that ultimately should not be in the Project.toml of the main package, like Literate.jl and Documenter.jl as these correspond to the Project.toml of the docs (the same for the tests, and the generation of the figures for the paper). This comment also applies to MRIGradients.jl (whose name is a little confusing because it only appears to do GIRF correction).

The main point that I want to come across is that if, as a user, I want to do only a GIRF-corrected recon, having my k-space, GIRF, and coils estimated my way, I do not expect a package called GIRFReco.jl to use DL tools like Flux.jl for B0 estimation and coil estimation, or to require Nifti.jl, MAT.jl, Unitful.jl, and others. In the specific case of Flux.jl, it may not be needed at all, and the gradient could be computed manually (I only saw it being used in pcg_ml_est_fieldmap and sens_smooth). Arguably, functions that do B0 and coil sensitivity estimation should not be part of this package at all.

Going back to the package extensions, the idea is to define a function only if the package is explicitly imported and added as a weak dependency. A simple example is the function readGIRFFile (from MRIGradients.jl) that depends on MAT.jl (for some reason, MAT.jl is also a dependency of GIRFReco.jl?). Only if the user does this

using MRIGradients, MAT

Then you get the function readGIRFFile defined in /ext/MRIGradientsMATExt.jl as

module MRIGradientsMATExt
    import MRIGradients
    using MAT
    # definition of readGIRFFile with more arguments, inside MRIGradients the function 
    # should be defined as an empty function: function readGIRFFile end
    MRIGradients.readGIRFFile( ...)
        ...
    end
end

that should also be added to the Project.toml

[weakdeps]
MAT = "..."

[extensions]
MRIGradientsMATExt = "MAT"

For the case of Flux.jl, if it is truly necessary, I think the best way of doing this would be to add Flux as a weak dependence of MRIFieldmaps.jl and MRICoilSensitivities.jl. In that way, if I want to do a field map estimation, I do

using MRIFieldmaps
# My code that uses MRIFieldmaps
data = ... # read data somehow
B0 = estimate_field_map(data, ..) #It is probably not called like this

and if I want to use the functions defined with Flux

using MRIFieldmaps, Flux
# My code that uses MRIFieldmaps
data = ... # read data somehow
B0 = estimate_field_map(data, ..) #I can estimate B0 the "traditional way"
B0 = estimate_field_map_flux(data, ..) #But also using Flux for the gradient!

Hope this is useful,
Cheers!

plot_sense_maps missing arg

Missed the num_channels argument

plot_sense_maps(sensitivity, num_channels; slice_index = 1)
Plots coil sensitivity maps from the channels, for a total of num_channels plots
# Arguments
* `sensitivity` - sensitivity maps, a 4D array: [nX, nY, nZ, nCoil]
* `num_channels` - number of coils (usually the last dimension of sensitivity)
* `slice_index` - The index of the slice to be displayed (if multislice)
"""
function plot_sense_maps(sensitivity; slice_index = 1)

Or maybe you meant to remove it but forgot in the demo:

plot_sense_maps(sensitivity,20)

Running the example phantom recon

I think the example for phantom recons needs some improvement. @cncastillo please can you cross-check that this is the right way to do it.

If I understood it correctly, there are two options that work as it is now, firstly I could activate the Project.toml of GIRFReco.jl by using julia --project=/path/to/GIRFReco.jl joss_example.jl (but that's not in the description), or I could have all the required dependencies installed into my main environment (which is active if I run it as described in the readme).
I think both are not optimal because you want the example to have a similar setup to when someone is using your package, but self-contained and with minimal effort to get it running. The first method activates the wrong environment, and the second requires the user to manually install packages they might not want/need into their main environment. I suggest setting up an environment which you ship with the joss_example.jl, i.e. in the examples folder you run ] activate ., and then add all the dependencies for the example and add the Project.toml to the git repo. The instructions then need to be adjusted to say cd .../examples/, then ] and activate, instantiate, then julia --project=. joss_example.jl.

Add more installation details for users new to Julia

Specifically, this instruction,

GIRFReco.jl/README.md

Lines 15 to 19 in 9f5b4b9

## Quick Installation
To install the package, use the following command:
```
julia>]add GIRFReco
```

was not clear to me as someone with only a little Julia experience (and last time I used it was a few years ago).

Because, "use the following command" usually means "copy and paste this" into the terminal/software, and this command as is doesn't work in neither the mac Terminal or the Julia session. Even copy pasting ]add GIRFReco doesn't work once inside the Julia terminal; you need to explicitely type it because typed ] opens the julia package manager but copy-pasting "]" with a command doesn't.

I'd recommend breaking it down into three instructions.

  1. Open a Julia session
  2. Open the package manager by typing ]
  3. Add the package using the command add GIRFReco once in the package manager.

Package automated workflows

The package is missing some key components to be considered a Julia package:

  • Code coverage (related to #13 )
  • Continuous Integration (CI)
  • CompatHelper

I suggest to check PkgTemplates and look into Julia actions.

TagBot trigger issue

This issue is used to trigger TagBot; feel free to unsubscribe.

If you haven't already, you should update your TagBot.yml to include issue comment triggers.
Please see this post on Discourse for instructions and more details.

If you'd like for me to do this for you, comment TagBot fix on this issue.
I'll open a PR within a few hours, please be patient!

Organization of package files

Hi,

These are simple changes, but they will make clearer which code/functions GIRFReco.jl is providing.

  • The files with the definitions of read_gradient_txt_file, plotReconstruction, plotSenseMaps, calculateB0Maps, getSliceOrder, syncTrajAndData!, do_k0_correction!, adjustHeader!, checkAcquisitionNodes!, validateSiemensMRD!, validateAcqData!, preprocessCartesianData, removeOversampling!, mergeRawInterleaves, applyGIRF!, applyK0!, saveMap, loadMap, shiftksp! should be moved. They can be in the same folder structure, but inside src. If these functions depend in other functions you defined in GIRFReco, I would also put them in src.

  • This is an stylistic suggestion, but the export list is generally in the module definition. Feel free to ignore this one if you want to keep it like it is.

Not sure if your are planning to report the Code coverage of the tests, but the coverage is defined using the files in src (at least by default).

Examples in documentation do not show images of the results

Hi I think you are using PlotlyJS so you can use LIterate's special comments to add the figures. I already gave an example in #14, but for convinience I will copy-paste it here:

# This is text that will be present in the docs explaining the code
#jl using Pkg; Pkg.activate(; temp = true); Pkg.add("Package1"); Pkg.add("Package2")
using Package1, Pacakge2
im = recon(data)
p = plot_image(im)
#md savefig(p, "../assets/results.html") # hide
#jl display(p)
#md # ```@raw html
#md # <object type="text/html" data="../../assets/results.html"></object>
#md # ```

Setting up dev description

I have a couple of suggestions for this description in the readme:

  • I'd move point 3 to be the new point 9, because you start describing how to set up the repo in dev mode and then point 3 describes something else, and then point 4 continues with setting up the repo. If you're concerned the current step 3 takes too long (download), I suggest moving it to be the new step 1 or two.
  • For me just doing ]dev GIRFReco or git clone ... and then ]dev ./...mypath.../GIRFReco worked out without installing the dependencies explicitly or using ]instantiate. I'd be happy if the other reviewers can please confirm this.

Config files and root directory

This fails if your root directory isn't the main repo's root (that's one reason I brought up the IDE in #3, the IDE "hides" the fact that you're still running code from the repo's root. If you for example work with cmdline, a reasonable thing to do is to cd to the examples folder, and run the script from there, which doesn't work as described before.

I think what you should do is to make the example self-contained in a sense that it imports all packages it needs (import and using), and the rest of the code is available in the examples folder. This also ties in with my comments on the manuscript here: wouldn't it be better to have a function providing you with a dictionary (or other data structure) containing the options, and then in your example script you can adjust them to your need, instead of having to copy a file?
Open for discussion!

General comments on style of coding

I want to use this issue to give some feedback on the style of coding, independent of the review for JOSS. Additionally to the points below, I'll add more while going through the code. I know that style is quite subjective, but I still want to share my opinion and explain, specifically see the official Julia docs:

  • as a rule of thumb in Julia, camel case should be used for types and module names, snake case for pretty much everything else. In the demo code (and I suppose the rest as well) most variables are camel case.
  • Please reduce abbreviations, unless it is outrageously obvious what is meant. To give an example from the demonstration code, startIndexIntlv, could just be first_interleaf or i_first_interleaf if you want to make clear it's an index. Another one is here, shiftksp!. The three letters ace can be sponsored :) I understand that people like to reduce the amount of characters to type, but ultimately code is read more often than written, and shift_kspace reads a lot easier than shiftksp in my opinion. Another one is in the comments here. When I first read it, I asked myself what difference is meant, until I recalled it's diffusion. Towards making this as accessible as possible, I think it's important to make absolutely clear what you mean, i.e. foolproof.
  • Usage of white-spaces: I never really understood why people find f(a,b,c) more appealing than f(a, b, c) (seems to originate from matlab ...). Same for array indexing.

Portability problems when running phantom example

Hi everyone,

I hope everything is going well. I have tried to run the example and had a few problems. I will describe the problems I encountered in "chronological order", trying to describe the experience of a new user trying to run this example.

To run the example in phantom data, I did not find the link to download the data within the joss_demo. For convenience, I would add a link there, so I don't need to return to the README. Preferably, I would not need to care about this; it should just download the data into data/ in the current directory.

The second problem I encountered was where to put recon_config_joss_demo.jl and data/ as it assumes a specific folder structure not described in the example (or at least not in the setup section). This could also be solved by downloading recon_config_joss_demo.jl into the current directory.

Finally I concluded that it needs to be like this:

data/
joss_demo.jl
recon_config_joss_demo.jl

Then, when I tried running it in a temp environment (clean env. like someone new to Julia), I realized that you are using mulitple packages:

using GIRFReco, MRIGradients
using MRIReco, FileIO, MRIFiles, MRIBase, MRICoilSensitivities
using RegularizedLeastSquares, Flux
using ImageTransformations
using PlotlyJS, Plots

Does someone that just want to run the example actually need to install all of them? When trying to install them, I had some disk space issues, but after deleting some files I managed to proceed. Instructions telling the user what packages to actually install (add PackageX) in the environment must be included, as GIRFReco has a lot of those inside now. This could be a simple Project.toml, as adding all of them manually is a little cumbersome.

Now, where I had some real problems is when it tried to run recon/cartesian_recon.jl that is also searching for "../src/utils/fieldmap_estimator.jl", similar to #9. This is a big portability problem of the example, as it assumes that I am running it within the repo. I am unsure where I need to run the example, maybe this assumes the folder structure before the JOSS changes (?). I could have downloaded the repo and run it there, but I believe this is an incorrect approach, and the user shouldn't need to do this. If the user needs additional functions to run an example, those need to be included in the package.

This is unfortunately as far as I got. I think the example needs to be refined, so the user can download joss_demo.jl and its Project.toml into a new folder, activate the Project.toml with ] activate . to install the necessary packages, run include("joss_demo.jl") in the REPL, and it should just work. These steps need to be described in the example with a warning telling the user that "X GB" will be downloaded for the phantom data. First impressions are very important for new users (especially new Julia users), and the provided example needs to be flawless.

Hope this is helpful,
Cheers!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.