From 93934208f8e85163bf47954a4dca33bac2a8721b Mon Sep 17 00:00:00 2001 From: Taylor Hanayik Date: Fri, 23 Aug 2024 16:01:35 +0100 Subject: [PATCH 1/2] enable mesh suboptions in javascript wrapper --- js/README.md | 33 +++- js/index.html | 22 ++- js/scripts/parseNiimathHelp.js | 29 ++- js/src/index.js | 38 +++- js/src/niimathOperators.json | 315 ++++++++++++++++++++------------- src/niimath.c | 15 ++ 6 files changed, 321 insertions(+), 131 deletions(-) diff --git a/js/README.md b/js/README.md index 9d5d752..d611fb3 100644 --- a/js/README.md +++ b/js/README.md @@ -8,7 +8,7 @@ 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 +### Example: volumes 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: @@ -25,6 +25,37 @@ await niimath.init(); const outFile = await niimath.image(selectedFile).dog(2, 3.2).run(); ``` +### Example: meshes + +The `@niivue/niimath` library also supports the `-mesh` options available in the `niimath` CLI. However, the JavaScript API is slightly different from the volume processing due to the use of the `-mesh` suboptions. + +```javascript +import { Niimath } from '@niivue/niimath'; +const niimath = new Niimath(); +await niimath.init(); +const outName = 'out.mz3'; // outname must be a mesh format! +const outMesh = await niimath.image(selectedFile) + .mesh({ + i: 'm', // 'd'ark, 'm'edium, 'b'right or numeric (e.g. 128) isosurface + b: 1, // fill bubbles + }) + .run(outName); +/* +Here's the help from the niimath CLI program +The mesh option has multiple sub-options: + -mesh : meshify requires 'd'ark, 'm'edium, 'b'right or numeric isosurface ('niimath bet -mesh -i d mesh.gii') + -i : 'd'ark, 'm'edium, 'b'right or numeric isosurface + -a : roi based atlas to mesh + -b : fill bubbles + -l : only largest + -o : original marching cubes + -q : quality + -s : post smooth + -r : reduce fraction + -v : verbose +*/ +``` + ## Installation To install `@niivue/niimath` in your project, run the following command: diff --git a/js/index.html b/js/index.html index 5e2616f..b56d1cd 100644 --- a/js/index.html +++ b/js/index.html @@ -45,15 +45,33 @@

Niimath WASM Demo

console.log('Initializing niimath wasm...'); await niimath.init(); console.log('niimath wasm initialized.'); + const t0 = performance.now(); - const outFile = await niimath.image(selectedFile).sobel().thr(20).fmean().otsu(5).run() + + // test the -mesh command and mesh output + const outName = 'out.mz3'; + const outFile = await niimath.image(selectedFile) + .mesh({ + i: 'm', // 'm' for medium + b: 1, // fill bubbles + v: 0 // not verbose + }) + .run(outName) const t1 = performance.now(); console.log("niimath wasm took " + (t1 - t0) + " milliseconds.") + // test with a volume output + // const outName = 'out.nii'; + // const outFile = await niimath.image(selectedFile) + // .sobel() + // .run(outName) + // const t1 = performance.now(); + // console.log("niimath wasm took " + (t1 - t0) + " milliseconds.") + // Create a download link for the processed file const url = URL.createObjectURL(outFile); downloadLink.href = url; - downloadLink.download = 'processed_image.nii.gz'; + downloadLink.download = outName; downloadLink.style.display = 'block'; downloadLink.textContent = 'Download Processed Image'; diff --git a/js/scripts/parseNiimathHelp.js b/js/scripts/parseNiimathHelp.js index ba6240f..cd9bc6d 100644 --- a/js/scripts/parseNiimathHelp.js +++ b/js/scripts/parseNiimathHelp.js @@ -7,6 +7,7 @@ function parseHelpText(helpText) { const methodDefinitions = {}; let currentKernel = false; + let currentMesh = false; lines.forEach(line => { // Handle kernel operations @@ -20,12 +21,19 @@ function parseHelpText(helpText) { const match = line.match(/^\s*(-[\w]+)\s*(.*?)\s*:\s*(.*)/); if (match) { + // each -mesh suboption has at least 4 spaces before the dash (DO NOT USE TABS) + // so we can use this to determine if we are in a mesh suboption after seeing the -mesh string. + // Store the first 4 characters of the line to determine if we are in a mesh suboption + const leadingChars = line.substring(0, 4); + const nSpaces = ' '; const command = match[1].trim(); + // console log below is for debugging + // console.log(leadingChars, command, leadingChars === nSpaces); const argsString = match[2].trim(); const args = argsString.split(/\s+/).filter(arg => arg.startsWith('<') && arg.endsWith('>')); const key = command.replace(/^-+/, ''); // Remove leading dashes - const helpText = line.trim(); + const helpText = match[3].trim(); if (currentKernel) { // Special handling for kernel operations @@ -41,12 +49,29 @@ function parseHelpText(helpText) { }; } } + } else if (command === '-mesh') { + // Special handling for the mesh option + currentMesh = true; + methodDefinitions.mesh = { + args: args.map(arg => arg.replace(/[<>]/g, '')), + help: helpText, + subOperations: {} + }; + // check if this is a valid mesh suboption (which is indented in the help text) + } else if (currentMesh && leadingChars === nSpaces && command.startsWith('-')) { + // Handling sub-options of the mesh command + const subKey = command.replace(/^-+/, ''); // Remove leading dashes + methodDefinitions.mesh.subOperations[subKey] = { + args: args.map(arg => arg.replace(/[<>]/g, '')), + help: helpText + }; } else { - // General case for non-kernel operations + // General case for non-kernel and non-mesh operations methodDefinitions[key] = { args: args.map(arg => arg.replace(/[<>]/g, '')), help: helpText }; + currentMesh = false; // Stop handling mesh sub-options if another main option is encountered } } diff --git a/js/src/index.js b/js/src/index.js index f3642a4..82e0845 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -12,12 +12,12 @@ export class Niimath { // This gets reassigned in the run() method, // but we need to handle the ready message before that. // Maybe there is a less hacky way to do this? - this.worker.onmessage = (event) => { + this.worker.onmessage = (event) => { if (event.data && event.data.type === 'ready') { resolve(true); // Resolve the promise when the worker is ready } } - + // Handle worker init errors. this.worker.onerror = (error) => { reject(new Error(`Worker failed to load: ${error.message}`)); @@ -26,13 +26,13 @@ export class Niimath { } image(file) { - return new ImageProcessor({worker: this.worker, file, operators: this.operators}); + return new ImageProcessor({ worker: this.worker, file, operators: this.operators }); } } class ImageProcessor { - constructor({worker, file, operators}) { + constructor({ worker, file, operators }) { this.worker = worker; this.file = file; this.operators = operators; @@ -50,7 +50,7 @@ class ImageProcessor { const definition = this.operators[methodName]; if (methodName === 'kernel') { - // special case for kernels because they have different types with varying arguments + // Special case for kernels because they have different types with varying arguments Object.keys(definition.subOperations).forEach((subOpName) => { const subOpDefinition = definition.subOperations[subOpName]; this[`kernel${subOpName.charAt(0).toUpperCase() + subOpName.slice(1)}`] = (...args) => { @@ -60,8 +60,34 @@ class ImageProcessor { return this._addCommand('-kernel', subOpName, ...args); }; }); + } else if (methodName === 'mesh') { + // Special case for mesh because it has sub-options that can be passed as an object + this.mesh = (options = {}) => { + const subCommands = []; + + Object.keys(options).forEach((subOptionKey) => { + if (definition.subOperations[subOptionKey]) { + const subOpDefinition = definition.subOperations[subOptionKey]; + const subOptionValue = options[subOptionKey]; + + if (subOpDefinition.args.length > 0 && subOptionValue === undefined) { + throw new Error(`Sub-option -${subOptionKey} requires a value.`); + } + + subCommands.push(`-${subOptionKey}`); + + if (subOpDefinition.args.length > 0) { + subCommands.push(subOptionValue); + } + } else { + throw new Error(`Invalid sub-option -${subOptionKey} for mesh.`); + } + }); + + return this._addCommand('-mesh', ...subCommands); + }; } else { - // all other non-kernel operations + // General case for non-kernel and non-mesh operations this[methodName] = (...args) => { if (args.length < definition.args.length || (!definition.optional && args.length > definition.args.length)) { throw new Error(`Expected ${definition.args.length} arguments for ${methodName}, but got ${args.length}`); diff --git a/js/src/niimathOperators.json b/js/src/niimathOperators.json index 2b967ac..d3906a1 100644 --- a/js/src/niimathOperators.json +++ b/js/src/niimathOperators.json @@ -4,92 +4,167 @@ "hp", "lp" ], - "help": "-bptfm : Same as bptf but does not remove mean (emulates fslmaths < 5.0.7)" + "help": "Same as bptf but does not remove mean (emulates fslmaths < 5.0.7)" }, "bwlabel": { "args": [ "conn" ], - "help": "-bwlabel : Connected component labelling for non-zero voxels (conn sets neighbors: 6, 18, 26)" + "help": "Connected component labelling for non-zero voxels (conn sets neighbors: 6, 18, 26)" }, "c2h": { "args": [], - "help": "-c2h : reverse h2c transform" + "help": "reverse h2c transform" }, "ceil": { "args": [], - "help": "-ceil : round voxels upwards to the nearest integer" + "help": "round voxels upwards to the nearest integer" + }, + "conform": { + "args": [], + "help": "reslice to 1mm size in coronal slice direction with 256^3 voxels" }, "crop": { "args": [ "tmin", "tsize" ], - "help": "-crop : remove volumes, starts with 0 not 1! Inputting -1 for a size will set it to the full range" + "help": "remove volumes, starts with 0 not 1! Inputting -1 for a size will set it to the full range" }, "dehaze": { "args": [ "mode" ], - "help": "-dehaze : set dark voxels to zero (mode 1..5; higher yields more surviving voxels)" + "help": "set dark voxels to zero (mode 1..5; higher yields more surviving voxels)" }, "detrend": { "args": [], - "help": "-detrend : remove linear trend (and mean) from input" + "help": "remove linear trend (and mean) from input" }, "demean": { "args": [], - "help": "-demean : remove average signal across volumes (requires 4D input)" + "help": "remove average signal across volumes (requires 4D input)" }, "edt": { "args": [], - "help": "-edt : estimate Euler Distance Transform (distance field). Assumes isotropic input" + "help": "estimate Euler Distance Transform (distance field). Assumes isotropic input" + }, + "close": { + "args": [ + "thr", + "dx1", + "dx2" + ], + "help": "morphological close that binarizes with `thr`, dilates with `dx1` and erodes with `dx2` (fills bubbles with `thr`)" }, "floor": { "args": [], - "help": "-floor : round voxels downwards to the nearest integer" + "help": "round voxels downwards to the nearest integer" }, "h2c": { "args": [], - "help": "-h2c : convert CT scans from 'Hounsfield' to 'Cormack' units to emphasize soft tissue contrast" + "help": "convert CT scans from 'Hounsfield' to 'Cormack' units to emphasize soft tissue contrast" }, "mesh": { "args": [], - "help": "-mesh : meshify requires 'd'ark, 'm'edium, 'b'right or numeric isosurface ('niimath bet -mesh -i d mesh.gii')" + "help": "meshify requires 'd'ark, 'm'edium, 'b'right or numeric isosurface ('niimath bet -mesh -i d mesh.gii')", + "subOperations": { + "i": { + "args": [ + "isovalue" + ], + "help": "'d'ark, 'm'edium, 'b'right or numeric isosurface" + }, + "a": { + "args": [ + "atlasFile" + ], + "help": "roi based atlas to mesh" + }, + "b": { + "args": [ + "fillBubbles" + ], + "help": "fill bubbles" + }, + "l": { + "args": [ + "onlyLargest" + ], + "help": "only largest" + }, + "o": { + "args": [ + "originalMC" + ], + "help": "original marching cubes" + }, + "q": { + "args": [ + "quality" + ], + "help": "quality" + }, + "s": { + "args": [ + "postSmooth" + ], + "help": "post smooth" + }, + "r": { + "args": [ + "reduceFraction" + ], + "help": "reduce fraction" + }, + "v": { + "args": [ + "verbose" + ], + "help": "verbose" + } + } + }, + "hollow": { + "args": [ + "threshold", + "thickness" + ], + "help": "hollow out a mesh" }, "mod": { "args": [], - "help": "-mod : modulus fractional remainder - same as '-rem' but includes fractions" + "help": "modulus fractional remainder - same as '-rem' but includes fractions" }, "otsu": { "args": [ "mode" ], - "help": "-otsu : binarize image using Otsu's method (mode 1..5; higher yields more bright voxels)" + "help": "binarize image using Otsu's method (mode 1..5; higher yields more bright voxels)" }, "power": { "args": [ "exponent" ], - "help": "-power : raise the current image by following exponent" + "help": "raise the current image by following exponent" }, "qform": { "args": [ "code" ], - "help": "-qform : set qform_code" + "help": "set qform_code" }, "sform": { "args": [ "code" ], - "help": "-sform : set sform_code" + "help": "set sform_code" }, "p": { "args": [ "threads" ], - "help": "-p : set maximum number of parallel threads. DISABLED: recompile for OpenMP support" + "help": "set maximum number of parallel threads. DISABLED: recompile for OpenMP support" }, "resize": { "args": [ @@ -98,286 +173,286 @@ "Z", "m" ], - "help": "-resize : grow (>1) or shrink (<1) image. Method (0=nearest,1=linear,2=spline,3=Lanczos,4=Mitchell)" + "help": "grow (>1) or shrink (<1) image. Method (0=nearest,1=linear,2=spline,3=Lanczos,4=Mitchell)" }, "round": { "args": [], - "help": "-round : round voxels to the nearest integer" + "help": "round voxels to the nearest integer" }, "sobel": { "args": [], - "help": "-sobel : fast edge detection" + "help": "fast edge detection" }, "sobel_binary": { "args": [], - "help": "-sobel_binary : sobel creating binary edge" + "help": "sobel creating binary edge" }, "tensor_2lower": { "args": [], - "help": "-tensor_2lower : convert FSL style upper triangle image to NIfTI standard lower triangle order" + "help": "convert FSL style upper triangle image to NIfTI standard lower triangle order" }, "tensor_2upper": { "args": [], - "help": "-tensor_2upper : convert NIfTI standard lower triangle image to FSL style upper triangle order" + "help": "convert NIfTI standard lower triangle image to FSL style upper triangle order" }, "tensor_decomp_lower": { "args": [], - "help": "-tensor_decomp_lower : as tensor_decomp except input stores lower diagonal (AFNI, ANTS, Camino convention)" + "help": "as tensor_decomp except input stores lower diagonal (AFNI, ANTS, Camino convention)" }, "trunc": { "args": [], - "help": "-trunc : truncates the decimal value from floating point value and returns integer value" + "help": "truncates the decimal value from floating point value and returns integer value" }, "unsharp": { "args": [ "sigma", "scl" ], - "help": "-unsharp : edge enhancing unsharp mask (sigma in mm, not voxels; 1.0 is typical)" + "help": "edge enhancing unsharp mask (sigma in mm, not voxels; 1.0 is typical)" }, "dog": { "args": [ "sPos", "sNeg" ], - "help": "-dog : difference of gaussian with zero-crossing edges (positive and negative sigma mm)" + "help": "difference of gaussian with zero-crossing edges (positive and negative sigma mm)" }, "dogr": { "args": [ "sPos", "sNeg" ], - "help": "-dogr : as dog, without zero-crossing (raw rather than binarized data)" + "help": "as dog, without zero-crossing (raw rather than binarized data)" }, "dogx": { "args": [ "sPos", "sNeg" ], - "help": "-dogx : as dog, zero-crossing for 2D sagittal slices" + "help": "as dog, zero-crossing for 2D sagittal slices" }, "dogy": { "args": [ "sPos", "sNeg" ], - "help": "-dogy : as dog, zero-crossing for 2D coronal slices" + "help": "as dog, zero-crossing for 2D coronal slices" }, "dogz": { "args": [ "sPos", "sNeg" ], - "help": "-dogz : as dog, zero-crossing for 2D axial slices" + "help": "as dog, zero-crossing for 2D axial slices" }, "add": { "args": [ "input" ], - "help": "-add : add following input to current image" + "help": "add following input to current image" }, "sub": { "args": [ "input" ], - "help": "-sub : subtract following input from current image" + "help": "subtract following input from current image" }, "mul": { "args": [ "input" ], - "help": "-mul : multiply current image by following input" + "help": "multiply current image by following input" }, "div": { "args": [ "input" ], - "help": "-div : divide current image by following input" + "help": "divide current image by following input" }, "rem": { "args": [ "number" ], - "help": "-rem : modulus remainder - divide current image by following input and take remainder" + "help": "modulus remainder - divide current image by following input and take remainder" }, "mas": { "args": [ "file" ], - "help": "-mas : use (following image>0) to mask current image" + "help": "use (following image>0) to mask current image" }, "thr": { "args": [ "number" ], - "help": "-thr : use following number to threshold current image (zero anything below the number)" + "help": "use following number to threshold current image (zero anything below the number)" }, "thrp": { "args": [ "input" ], - "help": "-thrp > : use following percentage (0-100) of ROBUST RANGE to threshold current image (zero anything below the number)" + "help": "use following percentage (0-100) of ROBUST RANGE to threshold current image (zero anything below the number)" }, "thrP": { "args": [ "input" ], - "help": "-thrP : use following percentage (0-100) of ROBUST RANGE of non-zero voxels and threshold below" + "help": "use following percentage (0-100) of ROBUST RANGE of non-zero voxels and threshold below" }, "uthr": { "args": [ "number" ], - "help": "-uthr : use following number to upper-threshold current image (zero anything above the number)" + "help": "use following number to upper-threshold current image (zero anything above the number)" }, "uthrp": { "args": [ "input" ], - "help": "-uthrp : use following percentage (0-100) of ROBUST RANGE to upper-threshold current image (zero anything above the number)" + "help": "use following percentage (0-100) of ROBUST RANGE to upper-threshold current image (zero anything above the number)" }, "uthrP": { "args": [ "input" ], - "help": "-uthrP : use following percentage (0-100) of ROBUST RANGE of non-zero voxels and threshold above" + "help": "use following percentage (0-100) of ROBUST RANGE of non-zero voxels and threshold above" }, "clamp": { "args": [ "input" ], - "help": "-clamp : use following percentage (0-100) of ROBUST RANGE to threshold current image (anything below set to this threshold)" + "help": "use following percentage (0-100) of ROBUST RANGE to threshold current image (anything below set to this threshold)" }, "uclamp": { "args": [ "input" ], - "help": "-uclamp : use following percentage (0-100) of ROBUST RANGE to threshold current image (anything above set to this threshold)" + "help": "use following percentage (0-100) of ROBUST RANGE to threshold current image (anything above set to this threshold)" }, "max": { "args": [ "input" ], - "help": "-max : take maximum of following input and current image" + "help": "take maximum of following input and current image" }, "min": { "args": [ "input" ], - "help": "-min : take minimum of following input and current image" + "help": "take minimum of following input and current image" }, "seed": { "args": [ "number" ], - "help": "-seed : seed random number generator with following number" + "help": "seed random number generator with following number" }, "restart": { "args": [ "file" ], - "help": "-restart : replace the current image with input for future processing operations" + "help": "replace the current image with input for future processing operations" }, "save": { "args": [], - "help": "-save : save the current working image to the input filename" + "help": "save the current working image to the input filename" }, "inm": { "args": [ "mean" ], - "help": "-inm : (-i i ip.c) intensity normalisation (per 3D volume mean)" + "help": "(-i i ip.c) intensity normalisation (per 3D volume mean)" }, "ing": { "args": [ "mean" ], - "help": "-ing : (-I i ip.c) intensity normalisation, global 4D mean)" + "help": "(-I i ip.c) intensity normalisation, global 4D mean)" }, "s": { "args": [ "sigma" ], - "help": "-s : create a gauss kernel of sigma mm and perform mean filtering" + "help": "create a gauss kernel of sigma mm and perform mean filtering" }, "exp": { "args": [], - "help": "-exp : exponential" + "help": "exponential" }, "log": { "args": [], - "help": "-log : natural logarithm" + "help": "natural logarithm" }, "sin": { "args": [], - "help": "-sin : sine function" + "help": "sine function" }, "cos": { "args": [], - "help": "-cos : cosine function" + "help": "cosine function" }, "tan": { "args": [], - "help": "-tan : tangent function" + "help": "tangent function" }, "asin": { "args": [], - "help": "-asin : arc sine function" + "help": "arc sine function" }, "acos": { "args": [], - "help": "-acos : arc cosine function" + "help": "arc cosine function" }, "atan": { "args": [], - "help": "-atan : arc tangent function" + "help": "arc tangent function" }, "sqr": { "args": [], - "help": "-sqr : square" + "help": "square" }, "sqrt": { "args": [], - "help": "-sqrt : square root" + "help": "square root" }, "recip": { "args": [], - "help": "-recip : reciprocal (1/current image)" + "help": "reciprocal (1/current image)" }, "abs": { "args": [], - "help": "-abs : absolute value" + "help": "absolute value" }, "bin": { "args": [], - "help": "-bin : use (current image>0) to binarise" + "help": "use (current image>0) to binarise" }, "binv": { "args": [], - "help": "-binv : binarise and invert (binarisation and logical inversion)" + "help": "binarise and invert (binarisation and logical inversion)" }, "fillh": { "args": [], - "help": "-fillh : fill holes in a binary mask (holes are internal - i.e. do not touch the edge of the FOV)" + "help": "fill holes in a binary mask (holes are internal - i.e. do not touch the edge of the FOV)" }, "fillh26": { "args": [], - "help": "-fillh26 : fill holes using 26 connectivity" + "help": "fill holes using 26 connectivity" }, "index": { "args": [], - "help": "-index : replace each nonzero voxel with a unique (subject to wrapping) index number" + "help": "replace each nonzero voxel with a unique (subject to wrapping) index number" }, "grid": { "args": [ "value", "spacing" ], - "help": "-grid : add a 3D grid of intensity with grid spacing " + "help": "add a 3D grid of intensity with grid spacing " }, "edge": { "args": [], - "help": "-edge : edge strength" + "help": "edge strength" }, "tfce": { "args": [ @@ -385,7 +460,7 @@ "E", "connectivity" ], - "help": "-tfce : enhance with TFCE, e.g. -tfce 2 0.5 6 (maybe change 6 to 26 for skeletons)" + "help": "enhance with TFCE, e.g. -tfce 2 0.5 6 (maybe change 6 to 26 for skeletons)" }, "tfceS": { "args": [ @@ -397,53 +472,53 @@ "Z", "tfce_thresh" ], - "help": "-tfceS : show support area for voxel (X,Y,Z)" + "help": "show support area for voxel (X,Y,Z)" }, "nan": { "args": [], - "help": "-nan : replace NaNs (improper numbers) with 0" + "help": "replace NaNs (improper numbers) with 0" }, "nanm": { "args": [], - "help": "-nanm : make NaN (improper number) mask with 1 for NaN voxels, 0 otherwise" + "help": "make NaN (improper number) mask with 1 for NaN voxels, 0 otherwise" }, "rand": { "args": [], - "help": "-rand : add uniform noise (range 0:1)" + "help": "add uniform noise (range 0:1)" }, "randn": { "args": [], - "help": "-randn : add Gaussian noise (mean=0 sigma=1)" + "help": "add Gaussian noise (mean=0 sigma=1)" }, "range": { "args": [], - "help": "-range : set the output calmin/max to full data range" + "help": "set the output calmin/max to full data range" }, "tensor_decomp": { "args": [], - "help": "-tensor_decomp : convert a 4D (6-timepoint )tensor image into L1,2,3,FA,MD,MO,V1,2,3 (remaining image in pipeline is FA)" + "help": "convert a 4D (6-timepoint )tensor image into L1,2,3,FA,MD,MO,V1,2,3 (remaining image in pipeline is FA)" }, "kernel": { "subOperations": { "3D": { "args": [], - "help": "-kernel 3D : 3x3x3 box centered on target voxel (set as default kernel)" + "help": "3x3x3 box centered on target voxel (set as default kernel)" }, "2D": { "args": [], - "help": "-kernel 2D : 3x3x1 box centered on target voxel" + "help": "3x3x1 box centered on target voxel" }, "box": { "args": [ "size" ], - "help": "-kernel box : all voxels in a cube of width mm centered on target voxel" + "help": "all voxels in a cube of width mm centered on target voxel" }, "boxv": { "args": [ "size" ], - "help": "-kernel boxv : all voxels in a cube of width voxels centered on target voxel, CAUTION: size should be an odd number" + "help": "all voxels in a cube of width voxels centered on target voxel, CAUTION: size should be an odd number" }, "boxv3": { "args": [ @@ -451,141 +526,141 @@ "Y", "Z" ], - "help": "-kernel boxv3 : all voxels in a cuboid of dimensions X x Y x Z centered on target voxel, CAUTION: size should be an odd number" + "help": "all voxels in a cuboid of dimensions X x Y x Z centered on target voxel, CAUTION: size should be an odd number" }, "gauss": { "args": [ "sigma" ], - "help": "-kernel gauss : gaussian kernel (sigma in mm, not voxels)" + "help": "gaussian kernel (sigma in mm, not voxels)" }, "sphere": { "args": [ "size" ], - "help": "-kernel sphere : all voxels in a sphere of radius mm centered on target voxel" + "help": "all voxels in a sphere of radius mm centered on target voxel" }, "file": { "args": [ "filename" ], - "help": "-kernel file : use external file as kernel" + "help": "use external file as kernel" } } }, "dilM": { "args": [], - "help": "-dilM : Mean Dilation of non-zero voxels" + "help": "Mean Dilation of non-zero voxels" }, "dilD": { "args": [], - "help": "-dilD : Maximum Dilation of non-zero voxels (emulating output of fslmaths 6.0.1, max not modal)" + "help": "Maximum Dilation of non-zero voxels (emulating output of fslmaths 6.0.1, max not modal)" }, "dilF": { "args": [], - "help": "-dilF : Maximum filtering of all voxels" + "help": "Maximum filtering of all voxels" }, "dilall": { "args": [], - "help": "-dilall : Apply -dilM repeatedly until the entire FOV is covered" + "help": "Apply -dilM repeatedly until the entire FOV is covered" }, "ero": { "args": [], - "help": "-ero : Erode by zeroing non-zero voxels when zero voxels found in kernel" + "help": "Erode by zeroing non-zero voxels when zero voxels found in kernel" }, "eroF": { "args": [], - "help": "-eroF : Minimum filtering of all voxels" + "help": "Minimum filtering of all voxels" }, "fmedian": { "args": [], - "help": "-fmedian : Median Filtering" + "help": "Median Filtering" }, "fmean": { "args": [], - "help": "-fmean : Mean filtering, kernel weighted (conventionally used with gauss kernel)" + "help": "Mean filtering, kernel weighted (conventionally used with gauss kernel)" }, "fmeanu": { "args": [], - "help": "-fmeanu : Mean filtering, kernel weighted, un-normalized (gives edge effects)" + "help": "Mean filtering, kernel weighted, un-normalized (gives edge effects)" }, "subsamp2": { "args": [], - "help": "-subsamp2 : downsamples image by a factor of 2 (keeping new voxels centered on old)" + "help": "downsamples image by a factor of 2 (keeping new voxels centered on old)" }, "subsamp2offc": { "args": [], - "help": "-subsamp2offc : downsamples image by a factor of 2 (non-centered)" + "help": "downsamples image by a factor of 2 (non-centered)" }, "Tmean": { "args": [], - "help": "-Tmean : mean across time" + "help": "mean across time" }, "Tstd": { "args": [], - "help": "-Tstd : standard deviation across time" + "help": "standard deviation across time" }, "Tmax": { "args": [], - "help": "-Tmax : max across time" + "help": "max across time" }, "Tmaxn": { "args": [], - "help": "-Tmaxn : time index of max across time" + "help": "time index of max across time" }, "Tmin": { "args": [], - "help": "-Tmin : min across time" + "help": "min across time" }, "Tmedian": { "args": [], - "help": "-Tmedian : median across time" + "help": "median across time" }, "Tperc": { "args": [ "percentage" ], - "help": "-Tperc : nth percentile (0-100) of FULL RANGE across time" + "help": "nth percentile (0-100) of FULL RANGE across time" }, "Tar1": { "args": [], - "help": "-Tar1 : temporal AR(1) coefficient (use -odt float and probably demean first)" + "help": "temporal AR(1) coefficient (use -odt float and probably demean first)" }, "pval": { "args": [], - "help": "-pval : Nonparametric uncorrected P-value, assuming timepoints are the permutations; first timepoint is actual (unpermuted) stats image" + "help": "Nonparametric uncorrected P-value, assuming timepoints are the permutations; first timepoint is actual (unpermuted) stats image" }, "pval0": { "args": [], - "help": "-pval0 : Same as -pval, but treat zeros as missing data" + "help": "Same as -pval, but treat zeros as missing data" }, "cpval": { "args": [], - "help": "-cpval : Same as -pval, but gives FWE corrected P-values" + "help": "Same as -pval, but gives FWE corrected P-values" }, "ztop": { "args": [], - "help": "-ztop : Convert Z-stat to (uncorrected) P" + "help": "Convert Z-stat to (uncorrected) P" }, "ptoz": { "args": [], - "help": "-ptoz : Convert (uncorrected) P to Z" + "help": "Convert (uncorrected) P to Z" }, "ztopc": { "args": [], - "help": "-ztopc : Convert Z-stat to (uncorrected but clamped) P" + "help": "Convert Z-stat to (uncorrected but clamped) P" }, "ptozc": { "args": [], - "help": "-ptozc : Convert (uncorrected but clamped) P to Z" + "help": "Convert (uncorrected but clamped) P to Z" }, "rank": { "args": [], - "help": "-rank : Convert data to ranks (over T dim)" + "help": "Convert data to ranks (over T dim)" }, "ranknorm": { "args": [], - "help": "-ranknorm: Transform to Normal dist via ranks" + "help": "Transform to Normal dist via ranks" }, "roi": { "args": [ @@ -598,14 +673,14 @@ "tmin", "tsize" ], - "help": "-roi : zero outside roi (using voxel coordinates). Inputting -1 for a size will set it to the full image extent for that dimension" + "help": "zero outside roi (using voxel coordinates). Inputting -1 for a size will set it to the full image extent for that dimension" }, "bptf": { "args": [ "hp_sigma", "lp_sigma" ], - "help": "-bptf : (-t in ip.c) Bandpass temporal filtering; nonlinear highpass and Gaussian linear lowpass (with sigmas in volumes, not seconds); set either sigma<0 to skip that filter" + "help": "(-t in ip.c) Bandpass temporal filtering; nonlinear highpass and Gaussian linear lowpass (with sigmas in volumes, not seconds); set either sigma<0 to skip that filter" }, "roc": { "args": [ @@ -613,6 +688,6 @@ "outfile", "truth" ], - "help": "-roc [4Dnoiseonly] : take (normally binary) truth and test current image in ROC analysis against truth. is usually 0.05 and is limit of Area-under-ROC measure FP axis. is a text file of the ROC curve (triplets of values: FP TP threshold). If the truth image contains negative voxels these get excluded from all calculations. If is positive then the [4Dnoiseonly] option needs to be set, and the FP rate is determined from this noise-only data, and is set to be the fraction of timepoints where any FP (anywhere) is seen, as found in the noise-only 4d-dataset. This is then controlling the FWE rate. If is negative the FP rate is calculated from the zero-value parts of the image, this time averaging voxelwise FP rate over all timepoints. In both cases the TP rate is the average fraction of truth=positive voxels correctly found" + "help": "take (normally binary) truth and test current image in ROC analysis against truth. is usually 0.05 and is limit of Area-under-ROC measure FP axis. is a text file of the ROC curve (triplets of values: FP TP threshold). If the truth image contains negative voxels these get excluded from all calculations. If is positive then the [4Dnoiseonly] option needs to be set, and the FP rate is determined from this noise-only data, and is set to be the fraction of timepoints where any FP (anywhere) is seen, as found in the noise-only 4d-dataset. This is then controlling the FWE rate. If is negative the FP rate is calculated from the zero-value parts of the image, this time averaging voxelwise FP rate over all timepoints. In both cases the TP rate is the average fraction of truth=positive voxels correctly found" } } \ No newline at end of file diff --git a/src/niimath.c b/src/niimath.c index 93bc0d7..2ac95ac 100644 --- a/src/niimath.c +++ b/src/niimath.c @@ -291,7 +291,22 @@ int show_help( void ) { printf(" -floor : round voxels downwards to the nearest integer\n"); printf(" -h2c : convert CT scans from 'Hounsfield' to 'Cormack' units to emphasize soft tissue contrast\n"); #ifdef NII2MESH + printf("\n"); + printf(" The mesh option has multiple sub-options:\n"); printf(" -mesh : meshify requires 'd'ark, 'm'edium, 'b'right or numeric isosurface ('niimath bet -mesh -i d mesh.gii')\n"); + // We should indent the next few help lines to indicate that they are sub-options of -mesh + // DO NOT USE TABS!!! Use spaces to indent + printf(" -i : 'd'ark, 'm'edium, 'b'right or numeric isosurface\n"); + printf(" -a : roi based atlas to mesh\n"); + printf(" -b : fill bubbles\n"); + printf(" -l : only largest\n"); + printf(" -o : original marching cubes\n"); + printf(" -q : quality\n"); + printf(" -s : post smooth\n"); + printf(" -r : reduce fraction\n"); + printf(" -v : verbose\n"); + // add -hollow if mesh support enabled + printf(" -hollow : hollow out a mesh\n"); #endif printf(" -mod : modulus fractional remainder - same as '-rem' but includes fractions\n"); printf(" -otsu : binarize image using Otsu's method (mode 1..5; higher yields more bright voxels)\n"); From 2035ce7e8b79bbba306ea39cba16e0967279db30 Mon Sep 17 00:00:00 2001 From: Taylor Hanayik Date: Fri, 23 Aug 2024 16:02:38 +0100 Subject: [PATCH 2/2] update version --- js/package-lock.json | 4 ++-- js/package.json | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/js/package-lock.json b/js/package-lock.json index 7e0eeff..6989411 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "@niivue/niimath", - "version": "0.1.0", + "version": "0.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@niivue/niimath", - "version": "0.1.0", + "version": "0.1.1", "license": "BSD-2-Clause", "devDependencies": { "esbuild": "^0.23.1" diff --git a/js/package.json b/js/package.json index 88ba30d..64e1a8e 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "@niivue/niimath", - "version": "0.1.0", + "version": "0.1.1", "main": "dist/index.js", "module": "dist/index.js", "exports": { @@ -18,7 +18,14 @@ "demo": "npm run build && npx http-server .", "pub": "npm run build && npm publish --access public" }, - "keywords": ["niivue", "niimath", "nifti", "medical", "imaging", "brain"], + "keywords": [ + "niivue", + "niimath", + "nifti", + "medical", + "imaging", + "brain" + ], "author": "NiiVue developers", "license": "BSD-2-Clause", "description": "A javascript library to easily use the WASM build of Chris Rorden's niimath command line program written in C", @@ -28,4 +35,4 @@ "devDependencies": { "esbuild": "^0.23.1" } -} \ No newline at end of file +}