diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..567ea8f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,11 @@ +version : 2.1 +jobs: + build: + docker: + - image: mcr.microsoft.com/playwright:v1.41.2-jammy + steps: + - checkout + - run: npm install + - run: + name: Run tests + command: 'npm test' diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..c2fdd79 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +render3D diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..4c763ba --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,11 @@ +module.exports = { + 'extends': ['naat'], + 'overrides': [ + { + files: ['test.mjs'], + 'env': { + 'mocha': true + } + } + ] +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7b99fd1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +._.DS_Store +node_modules diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..88e080b --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,40 @@ +cff-version: "1.2.0" +authors: +- family-names: Heuer + given-names: Katja + orcid: "https://orcid.org/0000-0002-7237-0196" +- family-names: Traut + given-names: Nicolas + orcid: "https://orcid.org/0000-0003-3277-6316" +- family-names: Toro + given-names: Roberto + orcid: "https://orcid.org/0000-0002-6671-858X" +doi: 10.5281/zenodo.11080336 +message: If you use this software, please cite our article in the + Journal of Open Source Software. +preferred-citation: + authors: + - family-names: Heuer + given-names: Katja + orcid: "https://orcid.org/0000-0002-7237-0196" + - family-names: Traut + given-names: Nicolas + orcid: "https://orcid.org/0000-0003-3277-6316" + - family-names: Toro + given-names: Roberto + orcid: "https://orcid.org/0000-0002-6671-858X" + date-published: 2024-05-03 + doi: 10.21105/joss.06336 + issn: 2475-9066 + issue: 97 + journal: Journal of Open Source Software + publisher: + name: Open Journals + start: 6336 + title: "Thresholdmann: A Web tool for interactively creating adaptive + thresholds to segment MRI data." + type: article + url: "https://joss.theoj.org/papers/10.21105/joss.06336" + volume: 9 +title: "Thresholdmann: A Web tool for interactively creating adaptive + thresholds to segment MRI data." diff --git a/Contributing.md b/Contributing.md new file mode 100644 index 0000000..665b801 --- /dev/null +++ b/Contributing.md @@ -0,0 +1,33 @@ +# Thresholdmann + +### How can you help? + +**Join our efforts on GitHub!** + +We are looking for help to improve the user interface, +improve the user experience, +improve the responsivity by accelerating image rendering, +allow operation in different devices (different Web browsers, iOS, Android), +testing with different nifti files, +add the ability to open files from a URL, +improving the documentation, creating tutorials. We are also open +to hearing your ideas for improvement +and extension of Thresholdmann. + +You can check out if there is an open [issue](https://github.com/neuroanatomy/thresholdmann/issues) you'd like to jump on, and get in touch with us there. +You can also open a new issue to let us know about bugs, or request a new feature. + +We will be happy to work with anyone who would love to join our effort. + +**Test new functionalities** + +If you would like to change the code, please make sure the existing tests run using ```npm install && npm run test```. +If you add a new functionality, please also implement new test(s) to make sure of its correct working. Thank you. + +**Get in touch with us!** + +You can also write us on twitter (X) to [katjaq](https://twitter.com/katjaQheuer) and [R3RT0](https://twitter.com/r3rt0)! + +**We are looking forward to meeting you!** + +All work shall be open and free and respect the Mozilla Community Participation Guidelines. Please have a look at them [here.](https://www.mozilla.org/en-US/about/governance/policies/participation) diff --git a/Issue_Template.md b/Issue_Template.md new file mode 100644 index 0000000..525d9d9 --- /dev/null +++ b/Issue_Template.md @@ -0,0 +1,17 @@ +## Thank you for your feedback. + +### Current behaviour + +* If this is a bug, please explain how to reproduce the problem + * Which tools did you use in which sequence? + * Does the error concern the online display/ functionality or the downloaded result? + * What is the type and resolution of your data? (Can you provide a link to your data?) + * Do not include sensitive information. + * Upload the browser error message if you are familiar with web developer tools. + +### Expected behaviour + +### Version information (for bug reports) + +* **Browser + version**: +* **Your OS + version**: diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c666a70 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +ISC License (ISC) + +Copyright 2020 Thresholdmann Developers + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Pull_Request_Template.md b/Pull_Request_Template.md new file mode 100644 index 0000000..ab0bac2 --- /dev/null +++ b/Pull_Request_Template.md @@ -0,0 +1,17 @@ + + + + +--- + +- [ ] These changes fix #__ (github issue number if applicable). +- [ ] ```npm test``` ran with full success. +- [ ] ```npm test``` resulted in failure in _____ + + +- [ ] I implemented tests for the changes I made OR +- [ ] These changes do not require tests because _____ + + + + diff --git a/ReadMe.md b/ReadMe.md index 03117a1..22037e7 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,13 +1,24 @@ # Thresholdmann -A tool to apply a smoothly varying threshold to a nifti image. +A Web tool for interactively creating adaptive thresholds to segment MRI data. -Simply drag & drop your MRI file for display in an interactive stereotaxic viewer. +Katja Heuer, Nicolas Traut & Roberto Toro, November 2023 - +[![CircleCI](https://circleci.com/gh/neuroanatomy/thresholdmann.svg?style=shield)](https://circleci.com/gh/neuroanatomy/thresholdmann) +[![DOI](https://zenodo.org/badge/135712332.svg)](https://zenodo.org/doi/10.5281/zenodo.11080336) +[![DOI](https://joss.theoj.org/papers/10.21105/joss.06336/status.svg)](https://doi.org/10.21105/joss.06336) -Add a control point and grow or shrink your selection from there. -You can later re-select and adjust control points or delete them. -Once you are happy with your selection, you can download the control points (json) and you can download the mask (.nii.gz). +Simply drag & drop your MRI file for display in an interactive stereotaxic viewer. Move and add a control point and grow or shrink your selection from there. +You can later re-select and adjust control points or delete them. Once you are happy with your selection, you can download the control points (json) and you can download the mask (`.nii.gz`). + + +Brain extraction and segmentation are required for most analyses of neuroimaging data. Obtaining appropriate masks can be particularly difficult in non-human brain imaging, as standard automatic tools struggle with the surrounding muscle tissue, skull, and strong luminosity gradients. A simple interactive threshold is intuitive and fast to apply, and can often provide a rather good initial guess. However, because of luminosity gradients, the threshold that works for one brain region is likely to fail in another. + +[Thresholdmann](https://neuroanatomy.github.io/thresholdmann) is an open source Web tool for the interactive application of space-varying thresholds to Nifti volumes. No download or installation are required and all processing is done on the user’s computer. Nifti volumes are dragged and dropped onto the Web app and become available for visual exploration in a stereotaxic viewer. A space-varying threshold is then created by setting control points, each with their own local threshold. Each point can be repositioned or removed, and each local threshold can be adjusted in real time using sliders or entering their values numerically. The threshold direction can be switched to allow segmentation of the structure of interest in different imaging modalities, such as T1 and T2 weighted contrasts. The opacity of the mask and the brightness and contrast of the MRI image can be adjusted via sliders. A 3D model of the thresholded mask can be computed to inspect the result in an interactive 3D render. Finally, the thresholded mask, the space varying threshold and the list of control points can be saved for later use in scripted workflows, able to reproduce the thresholded volume from the original data. + +Thresholdmann complements the variety of existing brain segmentation tools, providing an easy interface to manually control the segmentation on a local scale across different brain imaging modalities and image contrast gradients. The masks produced by Thresholdmann can serve as a starting point for more detailed manual editing using tools such as [BrainBox](https://brainbox.pasteur.fr) or [ITK Snap](http://www.itksnap.org). This interactive approach is especially valuable for non-human brain imaging data, where automatic approaches often require extensive manual adjustment anyway. We have used Thresholdmann successfully to create initial brain masks for a variety of vertebrate brains – including many non-human primate datasets ([Heuer et al. 2019](https://www.sciencedirect.com/science/article/abs/pii/S0010945219301704?via%3Dihub), [Magielse et al. 2023](https://www.nature.com/articles/s42003-023-05553-z)) – as well as developmental data. Small Web tools, such as Thresholdmann or Reorient (https://neuroanatomy.github.io/reorient), focused on solving a single problem, can become helpful additions to the methodological toolbox of neuroimagers. + +### Documentation +A description of a typical workflow can be found in the [doc](https://neuroanatomy.github.io/thresholdmann/doc.html). diff --git a/doc.html b/doc.html new file mode 100644 index 0000000..343a7fd --- /dev/null +++ b/doc.html @@ -0,0 +1,193 @@ + + + + Thresholdmann documentation + + + + + + + + + + + +
+
+

Documentation

+

Interface overview | Functionalities | Workflow example

+

Thresholdmann interface overview

+ +

+ Interface and 3D viewer. The left panel allows to select different control point actions, set the thresholding direction, switch between the threshold mask or value view, open the 3D viewer, load or save the work, change the opacity of the mask, the brightness and contrast of the MRI, and information about the imaging data. The central panel is a stereotaxic viewer which allows you to move through the slices using the slider at the bottom, to switch between the 3 stereotaxic planes, and to interactively set, move or remove control points. You can view the threshold mask, or the corresponding threshold value space. The right panel shows for each control point the stereotaxic coordinates, an adjustment slider to change the local threshold, and the threshold value. The selected control point is highlighted in the panel and the viewer jumps to the corresponding slice. The interactive 3D viewer opens in a separate browser window.
+

+

Functionalities

+

+ Load and navigate data. To load your data, drag and drop your Nifti volume (.nii.gz or .nii) onto the upload box. Your MRI will appear in a stereotaxic viewer where you can navigate through slices using the slider below the viewer and switch view planes using the buttons. The viewer is initialised with one control point at the centre of the MRI volume, producing a local threshold. You can then adjust the threshold, switch threshold direction, move the control point or add more. Adding more control points will create a space-varying threshold obtained by interpolation of all control points. +

+

+ Adjust threshold value. Each local threshold can be adjusted in real time using its corresponding slider, or by entering a numerical value. The viewer is updated instantaneously. +

+

+ Select the direction of thresholding. Depending on your imaging modality and the mask you would like to create, set the threshold direction according to the grey value gradient that fits your needs using the Select up or Down button in the left panel. + The viewer is updated in real-time. +

+

+ Move control point. To move a control point, select the Move tool in the left panel (shortcut M), and drag the control point into the desired position across the viewer. The threshold mask is updated in real-time. +

+

+ Add control points. Select the Add tool in the left panel (shortcut A), and click in the desired position in the viewer. The control point will be set with the same threshold as the previous control point, and the corresponding slider setting, which can then be further adjusted. So, for example, you can set one control point outside the brain tissue, and decrease its value to exclude the background from your mask. You can then easily add more points outside of the brain, which will have the same settings. Similarly, if you set one control point inside the brain tissue to a good value, you can then add more points in other parts of the brain or across slices that should have a good initial value for inclusion into the mask. +

+

+ Select a control point. Click the Select tool in the left panel (shortcut S) and then in the viewer click on the control point you want to select. Its corresponding threshold slider will be selected automatically. + Vice versa, selecting a slider will automatically select the corresponding control point and jump to the corresponding slice in the viewer. +

+

+ Remove a control point. Select the Remove tool in the left panel (shortcut R) and then in the viewer click on the control point you want to remove. +

+

+ Adjust mask opacity. Select the opacity slider in the left panel to change the opacity of the thresholded mask. +

+

+ Adjust MRI contrast and brightness. Select the contrast or brightness slider in the left panel to interactively adjust contrast and brightness of the imaging data. Please note: The MRI data won't be changed, it is just the viewer. +

+

+ 3D render. Select the 3D render tool in the left panel to compute a 3D model of your mask on the fly and view it in an interactive 3D viewer. It will open in a separate browser window where you can zoom in and out and rotate the 3D object for inspection. +

+

+ View Threshold Value. Select Threshold value in the left panel to view the threshold value corresponding to the current mask. These volumes are updated in real time and can both be inspected and modified interactively.
+

+

+ Saving. Save the control points together with your mask Nifti volume for a fully reproducible workflow.
+ Save Control Points saves a list of the current control points to a json file.
+ Save Mask saves the thresholded mask of the current MRI in Nifti format. Depending on the size of your volume, the save may take a moment. The save progress is displayed below the viewer and you need to click "Download" once the popup appears in your browser window. (If you leave the window unattended, the save will disappear after a moment 🙃). +

+

+ Load Control Points. Load a list of control points from a json file. Please note that this replaces the current control points in case you placed some. +

+

+ Load new MRI file. Simply reload the webpage to start working with a new MRI.
+

+

+ Reproduce the workflow offline. Once you downloaded the control points, you will be able to reproduce the thresholded mask volume using the thresholdmann.py script. It will take the source MRI, and the thresholdmann.json control points file to produce the mask volume, as in
python thresholdmann.py input_nifti controlpoints_file output_nifti.
+

+ +

Workflow example

+ +

+ Usage example: Placing control points. Control points (blue dots) are added by clicking at the desired position in the viewer. This adds a slider to the right, which can be used to locally adapt the threshold. Progressively add control points to create a mask of the brain. The image shows this process for a macaque brain from Prime-DE site "amu" (Brochier et al. 2019, Milham et al. 2020).
+

+ +

+ Usage example: Resulting mask and space-varying threshold. Threshold mask and corresponding threshold value. These volumes are updated in real time and can both be inspected interactively. The set of control points and the mask can be downloaded.
+

+ +

+ Usage example: Final mask. Download the mask created based on your set of control points. Make sure that the brain region is sufficiently disjoint from the rest of the head so that a mathematical morphology closing will be enough to completely separate it. This will give you a mask like shown in this image with its reconstructed surface as an example.
+

+ +

Notes on performance

+

+ Data file size. Thresholdmann can load, display and segment very large MRI files. We tested as an extreme case with the 250µm 1.64GB ultra-high resolution human brain in vivo dataset from Lüsebrink et al. (2018). Loading took about 30 seconds until the data was displayed. The interface was perfectly fluid when browsing the slices, adding as many control points as to have a long scrolling list of points, and switching between view planes. Saving the control points was instantaneous, and saving the mask took a bit over a minute. If the browser asks to exit or wait, simply select wait and you should have no problem to save the mask. In general, we recommend to save control points before attempting to save the mask. 🤓 The 3D render took a bit more than half a minute and then was fluidly interactive and zoomable as usual. However, if the data is very large and at the same time noisy, so that the segmentation will have many flying dots, this will result in lots of triangles, and a 3D render on the fly will not be possible.


+

+ + +
+ +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/fixtures.mjs b/fixtures.mjs new file mode 100644 index 0000000..c549146 --- /dev/null +++ b/fixtures.mjs @@ -0,0 +1,22 @@ +import { createServer } from 'http-server'; + +let server; + +const startServer = () => { + server = createServer({ + root: '.' + }); + server.listen('8080', '127.0.0.1'); + + return server; +}; + +export const mochaGlobalSetup = () => { + server = startServer({port: '8080'}); + console.log('server started at 8080'); +}; + +export const mochaGlobalTeardown = async () => { + await server.close(); + console.log('server stopped'); +}; diff --git a/img/adjust.svg b/img/adjust.svg new file mode 100644 index 0000000..4164f2f --- /dev/null +++ b/img/adjust.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/alpha.svg b/img/alpha.svg new file mode 100644 index 0000000..e16fb5e --- /dev/null +++ b/img/alpha.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/bear_uchar.nii.gz b/img/bear_uchar.nii.gz new file mode 100644 index 0000000..33fbec7 Binary files /dev/null and b/img/bear_uchar.nii.gz differ diff --git a/img/naat-bw.svg b/img/naat-bw.svg new file mode 100755 index 0000000..21083ff --- /dev/null +++ b/img/naat-bw.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/sun-o.svg b/img/sun-o.svg new file mode 100644 index 0000000..2af8373 --- /dev/null +++ b/img/sun-o.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/img/thresholdmann_fig1.png b/img/thresholdmann_fig1.png new file mode 100644 index 0000000..2f8a7cd Binary files /dev/null and b/img/thresholdmann_fig1.png differ diff --git a/img/thresholdmann_fig1_dark.png b/img/thresholdmann_fig1_dark.png new file mode 100644 index 0000000..ad0a6d7 Binary files /dev/null and b/img/thresholdmann_fig1_dark.png differ diff --git a/img/thresholdmann_fig2.png b/img/thresholdmann_fig2.png new file mode 100644 index 0000000..1036c49 Binary files /dev/null and b/img/thresholdmann_fig2.png differ diff --git a/img/thresholdmann_fig2_dark.png b/img/thresholdmann_fig2_dark.png new file mode 100644 index 0000000..856dc88 Binary files /dev/null and b/img/thresholdmann_fig2_dark.png differ diff --git a/img/thresholdmann_fig3.png b/img/thresholdmann_fig3.png new file mode 100644 index 0000000..3f479b4 Binary files /dev/null and b/img/thresholdmann_fig3.png differ diff --git a/img/thresholdmann_fig3_dark.png b/img/thresholdmann_fig3_dark.png new file mode 100644 index 0000000..14e06f3 Binary files /dev/null and b/img/thresholdmann_fig3_dark.png differ diff --git a/img/thresholdmann_fig4.png b/img/thresholdmann_fig4.png new file mode 100644 index 0000000..7ca0a00 Binary files /dev/null and b/img/thresholdmann_fig4.png differ diff --git a/img/thresholdmann_fig4_dark.png b/img/thresholdmann_fig4_dark.png new file mode 100644 index 0000000..5615e12 Binary files /dev/null and b/img/thresholdmann_fig4_dark.png differ diff --git a/index.html b/index.html index 5238d77..088d09e 100644 --- a/index.html +++ b/index.html @@ -2,96 +2,13 @@ Thresholdmann - - - - + + + @@ -99,122 +16,231 @@