a b/CONTRIBUTING.md
1
# Contributing to DOSMA
2
3
Thank you for considering to contribute to `DOSMA` and encouraging open-source tools for medical image analysis.
4
5
If you use DOSMA in your work (research, company, etc.) and find it useful, spread the word!
6
7
This guide is inspired by [Huggingface transformers](https://github.com/huggingface/transformers).
8
9
## How to contribute
10
There are many ways to contribute to DOSMA:
11
12
* Issues: Submitting bugs or suggesting new features
13
* Documentation: Adding to the documentation or to the examples 
14
* Features: Implementing new features or bug fixes
15
* Community: Answering questions and helping others get started 
16
17
## Submitting a new issue or feature request
18
Please do your best to follow these guidelines when opening an issue. It will make it signficantly easier to give useful feedback and resolve the issue faster.
19
20
### Found a bug?
21
We would very much appreciate if you could **make sure the bug was not already reported** (use the search bar on Github under Issues). If you cannot find you bug, follow the instructions in the [Bug Report](https://github.com/ad12/DOSMA/issues/new/choose) template.
22
23
### Have a new algorithm/model?
24
Great! Please open an issue and provide the following information:
25
26
* Short description of the algorithm and a link to the corresponding publication
27
* Link to implementation if it is open-source
28
* Link to model weights if they are available (AI models only)
29
30
If you are willing to contribute the algorithm yourself, let us know so we can best guide you.
31
32
### Want a new feature (that is not an algorithm)?
33
34
A world-class feature request addresses the following points:
35
36
1. Motivation first:
37
  * Is it related to a problem/frustration with the library? If so, please explain why. Providing a code snippet that demonstrates the problem is best.
38
  * Is it related to something you would need for a project? We'd love to hear about it!
39
  * Is it something you worked on and think could benefit the community? Awesome! Tell us what problem it solved for you.
40
2. Write a full paragraph describing the feature;
41
3. Provide a code snippet that demonstrates its future use;
42
4. In case this is related to a paper, please attach a link;
43
5. Attach any additional information (drawings, screenshots, etc.) you think may help.
44
45
If your issue is well written we're already 80% of the way there by the time you post it. Follow the instructions in the [Feature Request](https://github.com/ad12/DOSMA/issues/new/choose)
46
47
## Contributing
48
Before writing code, we strongly advise you to search through the existing PRs or issues to make sure that nobody is already working on the same thing. If you are unsure, it is always a good idea to open an issue to get some feedback.
49
50
You will need basic git proficiency to be able to contribute to dosma. git is not the easiest tool to use but it has the greatest manual. Type git --help in a shell and enjoy. If you prefer books, [Pro Git](https://git-scm.com/book/en/v2) is a very good reference.
51
52
Follow these steps to start contributing:
53
54
1. Fork the [`repository`](https://github.com/ad12/DOSMA) by clicking on the 'Fork' button the repository's page. This creates a copy of the code under your GitHub user account.
55
56
2. Clone your fork to your local disk, and add the base repository as a remote:
57
58
   ```bash
59
   $ git clone git@github.com:<your Github handle>/DOSMA.git
60
   $ cd DOSMA
61
   $ git remote add upstream https://github.com/ad12/DOSMA.git
62
   ```
63
64
3. Create a new branch to hold your development changes:
65
66
   ```bash
67
   $ git checkout -b a-descriptive-name-for-my-changes
68
   ```
69
70
   **Do not** work on the `master` branch.
71
72
4. Set up a development environment by running the following command in a virtual environment:
73
74
  ```bash
75
  pip install -e ".[dev]"
76
  ```
77
78
5. Develop features on your branch.
79
80
    As you work on the features, you should make sure that the test suite passes:
81
82
    ```bash
83
    $ make test
84
    ```
85
86
    After you make changes, autoformat them with:
87
88
    ```bash
89
    $ make autoformat
90
    ```
91
92
93
    If you modify documentation (`docs/source`), verify the documents build:
94
95
    ```bash
96
    $ make build-docs
97
    ```
98
99
   Once you're happy with your changes, add changed files using `git add` and
100
   make a commit with `git commit` to record your changes locally:
101
102
   ```bash
103
   $ git add modified_file.py
104
   $ git commit
105
   ```
106
107
   Please write [good commit messages](https://chris.beams.io/posts/git-commit/).
108
109
   It is a good idea to sync your copy of the code with the original
110
   repository regularly. This way you can quickly account for changes:
111
112
   ```bash
113
   $ git fetch upstream
114
   $ git rebase upstream/master
115
   ```
116
117
   Push the changes to your account using:
118
119
   ```bash
120
   $ git push -u origin a-descriptive-name-for-my-changes
121
   ```
122
123
6. Once you are satisfied (**and the checklist below is happy too**), go to the
124
   webpage of your fork on GitHub. Click on 'Pull request' to send your changes
125
   to the project maintainers for review.
126
127
7. It's ok if maintainers ask you for changes. It happens to core contributors
128
   too! So everyone can see the changes in the Pull request, work in your local
129
   branch and push the changes to your fork. They will automatically appear in
130
   the pull request.
131
132
HINT: Run all major formatting and checks using the following:
133
134
```bash
135
make autoformat test build-docs
136
```
137
138
### Checklist
139
140
1. Make the title of your pull request should be a summary of its contribution;
141
2. If your pull request addresses an issue, mention the issue number in
142
  the pull request description
143
3. If your PR is a work in progress, start the title with `[WIP]`
144
4. Make sure existing tests pass;
145
5. Add high-coverage tests. Additions without tests will not be merged
146
6. All public methods must have informative docstrings in the google style.
147
148
### Tests
149
150
Library tests can be found in the 
151
[tests folder](https://github.com/ad12/DOSMA/tree/master/tests).
152
153
From the root of the repository, here's how to run tests with `pytest` for the library:
154
155
```bash
156
$ make test
157
```
158
159
### Style guide
160
`dosma` follows the [google style](https://google.github.io/styleguide/pyguide.html) for documentation.
161
162
163
## Popular Interfaces
164
DOSMA offers a range of interfaces to help developers get started with open-sourcing their algorithms.
165
Two interfaces that have been increasingly popular are
166
167
1. `ScanSequences`: An interface for implementing scan-specific algorithms (e.g. quantitative MRI)
168
2. `AI toolbox`: An interface for distributing and using pretrained models.
169
170
**NOTE**: The guides below are slightly outdated. Please reach out to us for up-to-date instructions on how to proceed.
171
172
### Scan Sequences
173
Thank you for adding analysis support for new scan sequences! Please follow the instructions below to streamline adding and using these sequences.
174
175
1. Use/create a unique, but relevant, name for your sequence.
176
    - `qDESS`: Quantitative Double Echo in Steady-State
177
    - `MAPSS`: Magnetization-Prepared Angle-Modulated Partitioned k-Space Spoiled Gradient Echo Snapshots
178
2. Create a new file in the `scan_sequences` folder with your sequence name (use [snake_casing](https://en.wikipedia.org/wiki/Snake_case))
179
3. Create a class in the file that inherits from `dosma.scan_sequences.ScanSequence`.
180
181
#### Separating Sequences Into Volumes
182
In many quantitative sequences, multiple echos are acquired for each slice to perform some form of voxel-wise quantitative fitting/extrapolation. We define a **volume** as a 3D matrix with values from a single echo. Therefore, a *qDESS* sequence, which has two echos, has volumes.
183
184
Each scan sequence implementation has a instance variable called `volumes`, in which the total pool of DICOM files are intelligently split into their respective volumes. For *qDESS*, the volumes instance variable would be a list with `len(volumes) = 2`. Sequences encoding for one echo will have the `volumes` field be a list with `len(volumes) = 1`.
185
186
By default, all scan sequences are split by the `EchoNumbers` DICOM tag, which specifies which echo the current DICOM slice corresponds to. However, depending on the scan sequence, where the volumes may need to be split by a different DICOM tag, override the field `__DEFAULT_SPLIT_BY__` in the scan sequence class.
187
188
#### Fitting quantitative values
189
Any scans that support quantitative parameter fitting should have a method named `generate_<QuantitativeValue>_map` (e.g. `generate_t1_rho_map`).
190
191
```python
192
def generate_<QuantitativeValue>_map(self, tissue: Tissue, ...) --> QuantitativeValue:
193
  ...
194
```
195
196
#### Scan Template
197
This template defines the basic implementation for any new scan sequence
198
```python
199
class NewScanSequence(ScanSequence/TargetSequence/NonTargetSequence):
200
  NAME=''  # add name of sequence in snake casing here
201
  __DEFAULT_SPLIT_BY__ = 'EchoNumbers'  # specify dicom tag to split volume by. default: 'EchoNumbers`
202
203
  def __validate_scan__(self) -> bool:
204
    """Validate this scan (usually done by checking dicom header tags, if available)
205
      :return a boolean
206
    """
207
```
208
209
### Automatic Segmentation
210
Robust automatic segmentation methods are critical to eliminating the bottleneck for morphological and quantitative analysis. The DOSMA framework enables easy integration of automatic segmentation techniques.
211
212
#### Deep-Learning Segmentation
213
Typically, deep learning segmentation algorithms consist of four blocks during inference:
214
1. **Data preprocessing**: Data is typically preprocessed to fit in the distribution expected by the network
215
    - e.g: zero-mean & unit-standard deviation whitening, scaling, etc.
216
2. **Architecture**: Each network can have a unique architecture (U-Net, SegNet, etc.). These architectures can be hard-coded into the file itself, or can be loaded from a `JSON` format (as outputted by Keras)
217
3. **Weights**: The model parameters, which determine the weights and biases for different layers of the network, can be exported to an `h5` file and loaded in dynamically.
218
4. **Mask Post-processing**: Some post-processing you wish to complete on the probability/binarized output.
219
  - e.g: Conditional Random Fields (CRFs), etc.
220
221
#### Naming Weight Files
222
All weight files should contain the aliases of the tissues that they can segment and must end with the extension `.h5`. For example, a weights file saved for femoral cartilage segmentation should have the alias `fc` in its name (eg. `fc.h5`, `oai-unet-fc.h5`, etc.).
223
224
#### Sharing Files
225
Weight files must be shared. Currently, there is no centralized location where these files can be hosted. As a result, please host the data on the cloud (google drive, box, dropbox, etc) and allow public download.
226
227
#### Segmentation Model Template
228
##### Keras (Tensorflow)
229
This template defines the basic implementation for any new Keras segmentation model.
230
See `models/oaiunet2d.py` for an example.
231
```python
232
class KerasSegModel(SegModel):
233
    """
234
    Abstract wrapper for Keras model used for semantic segmentation
235
    """
236
237
    def __load_keras_model__(self, input_shape):
238
        """Build Keras architecture
239
240
        :param input_shape: tuple or list of tuples for initializing input(s) into Keras model
241
242
        :return: a Keras model
243
        """
244
245
    def generate_mask(self, volume: MedicalVolume):
246
        """Segment the MRI volumes
247
248
        :param volume: A Medical Volume (height, width, slices)
249
250
        :return: A Medical volume or list of Medical Volumes with volume as binarized (0,1) uint8 3D numpy array of shape volumes.shape
251
252
        :raise ValueError if volumes is not 3D numpy array
253
        :raise ValueError if tissue is not a string or not in list permitted tissues
254
255
        """
256
```