Skip to content

Commit

Permalink
implement object oriented JS lib of niimath wasm
Browse files Browse the repository at this point in the history
  • Loading branch information
hanayik committed Aug 19, 2024
1 parent 739d5f8 commit 9209e6e
Show file tree
Hide file tree
Showing 16 changed files with 891 additions and 159 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
/bin/
/src/niimath
niimath
/dist
/node_modules
dist
node_modules
niimath.js
niimath.wasm
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# niimath

[![Build status](https://ci.appveyor.com/api/projects/status/7o0xp2fgbhadkgn1?svg=true)](https://ci.appveyor.com/project/neurolabusc/niimath)

## JavaScript/WebAssembly

To read the WASM specific README, please click [here](./js/README.md). The rest of this README is for the `niimath` CLI program.

## About

It is said that `imitation is the sincerest form of flattery`. This project emulates the popular [fslmaths](https://fsl.fmrib.ox.ac.uk/fslcourse/lectures/practicals/intro3/index.html) tool. fslmaths is advertized as a `general image calculator` and is not only one of the foundational tools for FSL's brain imaging pipelines (such as [FEAT](https://fsl.fmrib.ox.ac.uk/fsl/fslwiki/FEAT)), but has also been widely adopted by many tools. This popularity suggests that it fulfills an important niche. While scientists are often encouraged to discover novel solutions, it sometimes seems that replication is undervalued. Here are some specific reasons for creating this tool:
Expand Down
98 changes: 98 additions & 0 deletions js/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# @niivue/niimath

`@niivue/niimath` is a JavaScript + WASM library for performing mathemetical operations on NIFTI files. This library is intended to be **used in the browser**, not in a Node.js environment.

> All image processing operations are performed using the WASM build of [niimath](https://github.com/rordenlab/niimath), making it much faster than a pure JavaScript implementation. The image processing takes place in a separate worker thread, so it won't block the main thread in your application.
## Usage

The `@niivue/niimath` JavaScript library offers an object oriented API for working with the `niimath` CLI. Since `niimath` is a CLI tool, the API implemented in `@niivue/niimath` is just a wrapper around the CLI options and arguments.

### example

For example, the [difference of gaussian](https://www.biorxiv.org/content/biorxiv/early/2022/09/17/2022.09.14.507937.full.pdf) command `niimath input.nii -dog 2 3.2 output.nii` can be executed using the following `@niivue/niimath` JavaScript code:

```javascript
import { Niimath } from '@niivue/niimath';

const niimath = new Niimath();

// 1. selectedFile is a browser File object
// 2. note the use of the final run() method to execute the command.
// 3. note the use of await. The run method returns a promise that resolves to the output file if the command is successful.
const outFile = await niimath.image(selectedFile).dog(2, 3.2).run();
```

## Installation

To install `@niivue/niimath` in your project, run the following command:

```bash
# TODO: publish to npm
# npm install @niivue/niimath
```

### To install a local build of the library

Fist, `cd` into the `js` directory of the `niimath` repository.

```bash
# from niimath root directory
cd js
```

To install a local build of the library, run the following command:

```bash
npm run build
```

Then, install the library using the following command:

```bash
npm pack # will create a .tgz file in the root directory
```

Then, install the `@niivue/niimath` library in your application locally using the following command:

```bash
npm install /path/to/niivue-niimath.tgz
```

## Development

First `cd` into the `js` directory of the `niimath` repository.

```bash
# from niimath root directory
cd js
```

To install the dependencies, run the following command:

```bash
npm install
```

To build the library, run the following command

```bash
npm run build
```

To run the tests, run the following command:

```bash
npm run test
```

### Test using a simple demo

To test that the `@niivue/niimath` library is working correctly, you can run the following command:

```bash
npm run demo
```



24 changes: 24 additions & 0 deletions js/esbuild.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const esbuild = require('esbuild');
const fs = require('fs');

esbuild.build({
entryPoints: ['./src/index.js'],
outfile: './dist/index.js',
bundle: true,
format: 'esm',
target: ['es2020'],
minify: false,
define: {
'process.env.NODE_ENV': '"production"',
},
}).then(() => {
// copy worker.js, niimath.wasm, niimath.js to dist folder
// (they do not require any processing by esbuild).
// Technically, none of the files in the src folder require processing by esbuild,
// but it does allow minification (optional), and ES version target specification if needed.
// In the future, if we use Typescript, we can use esbuild to transpile the Typescript to JS.
fs.copyFileSync('./src/worker.js', './dist/worker.js');
fs.copyFileSync('./src/niimath.wasm', './dist/niimath.wasm');
fs.copyFileSync('./src/niimath.js', './dist/niimath.js');
console.log('Build completed!');
}).catch(() => process.exit(1));
68 changes: 68 additions & 0 deletions js/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Niimath WASM Demo</title>
</head>

<body>
<h1>Niimath WASM Demo</h1>
<input type="file" id="fileInput">
<button id="processButton" disabled>Process Image</button>
<p id="status">Please select a NIfTI image file to process.</p>
<a id="downloadLink" style="display: none;">Download Processed Image</a>

<script type="module">
import { Niimath } from './dist/index.js';
const niimath = new Niimath();
const fileInput = document.getElementById('fileInput');
const processButton = document.getElementById('processButton');
const status = document.getElementById('status');
const downloadLink = document.getElementById('downloadLink');

let selectedFile = null;

fileInput.addEventListener('change', async (event) => {
selectedFile = event.target.files[0];
if (selectedFile) {
processButton.disabled = false;
status.textContent = `Selected file: ${selectedFile.name}`;
} else {
processButton.disabled = true;
status.textContent = 'Please select a NIfTI image file to process.';
}
});

processButton.addEventListener('click', async () => {
if (!selectedFile) return;

status.textContent = 'Processing...';
processButton.disabled = true;

try {

const outFile = await niimath.image(selectedFile)
.dog(2, 3.2)
.run();

// Create a download link for the processed file
const url = URL.createObjectURL(outFile);
downloadLink.href = url;
downloadLink.download = 'processed_image.nii.gz';
downloadLink.style.display = 'block';
downloadLink.textContent = 'Download Processed Image';

status.textContent = 'Processing complete!';
} catch (error) {
console.error('Processing failed:', error);
status.textContent = 'Processing failed. Please check the console for details.';
} finally {
processButton.disabled = false;
}
});
</script>
</body>

</html>
Loading

0 comments on commit 9209e6e

Please sign in to comment.