Skip to content

Commit

Permalink
feat(compare-images): add vector-magnitude typescript bindings
Browse files Browse the repository at this point in the history
Also remove some unnecessary file and add .gitignore.
  • Loading branch information
thewtex committed Aug 21, 2023
1 parent 71c7118 commit 800778c
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 37 deletions.
4 changes: 4 additions & 0 deletions packages/compare-images/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
typescript/demo-app/
dist/
package-lock.json
typescript/test/browser/demo-app/public

This file was deleted.

This file was deleted.

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Generated file. To retain edits, remove this comment.

import { Image } from 'itk-wasm'

interface VectorMagnitudeNodeResult {
/** Output magnitude image */
magnitudeImage: Image

}

export default VectorMagnitudeNodeResult
65 changes: 65 additions & 0 deletions packages/compare-images/typescript/src/vector-magnitude-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Generated file. To retain edits, remove this comment.

import {
Image,
InterfaceTypes,
PipelineOutput,
PipelineInput,
runPipelineNode
} from 'itk-wasm'

import VectorMagnitudeNodeResult from './vector-magnitude-node-result.js'


import path from 'path'

/**
* Generate a scalar magnitude image based on the input vector's norm.
*
* @param {Image} vectorImage - Input vector image
*
* @returns {Promise<VectorMagnitudeNodeResult>} - result object
*/
async function vectorMagnitudeNode(
vectorImage: Image

) : Promise<VectorMagnitudeNodeResult> {

const desiredOutputs: Array<PipelineOutput> = [
{ type: InterfaceTypes.Image },
]

const inputs: Array<PipelineInput> = [
{ type: InterfaceTypes.Image, data: vectorImage },
]

const args = []
// Inputs
const vectorImageName = '0'
args.push(vectorImageName as string)

// Outputs
const magnitudeImageName = '0'
args.push(magnitudeImageName)

// Options
args.push('--memory-io')

const pipelinePath = path.join(path.dirname(import.meta.url.substring(7)), '..', 'pipelines', 'vector-magnitude')

const {
returnValue,
stderr,
outputs
} = await runPipelineNode(pipelinePath, args, desiredOutputs, inputs)
if (returnValue !== 0) {
throw new Error(stderr)
}

const result = {
magnitudeImage: outputs[0].data as Image,
}
return result
}

export default vectorMagnitudeNode
14 changes: 14 additions & 0 deletions packages/compare-images/typescript/src/vector-magnitude-result.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Generated file. To retain edits, remove this comment.

import { Image } from 'itk-wasm'

interface VectorMagnitudeResult {
/** WebWorker used for computation */
webWorker: Worker | null

/** Output magnitude image */
magnitudeImage: Image

}

export default VectorMagnitudeResult
69 changes: 69 additions & 0 deletions packages/compare-images/typescript/src/vector-magnitude.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Generated file. To retain edits, remove this comment.

import {
Image,
InterfaceTypes,
PipelineOutput,
PipelineInput,
runPipeline
} from 'itk-wasm'

import VectorMagnitudeResult from './vector-magnitude-result.js'


import { getPipelinesBaseUrl } from './pipelines-base-url.js'
import { getPipelineWorkerUrl } from './pipeline-worker-url.js'

/**
* Generate a scalar magnitude image based on the input vector's norm.
*
* @param {Image} vectorImage - Input vector image
*
* @returns {Promise<VectorMagnitudeResult>} - result object
*/
async function vectorMagnitude(
webWorker: null | Worker,
vectorImage: Image

) : Promise<VectorMagnitudeResult> {

const desiredOutputs: Array<PipelineOutput> = [
{ type: InterfaceTypes.Image },
]

const inputs: Array<PipelineInput> = [
{ type: InterfaceTypes.Image, data: vectorImage },
]

const args = []
// Inputs
const vectorImageName = '0'
args.push(vectorImageName as string)

// Outputs
const magnitudeImageName = '0'
args.push(magnitudeImageName)

// Options
args.push('--memory-io')

const pipelinePath = 'vector-magnitude'

const {
webWorker: usedWebWorker,
returnValue,
stderr,
outputs
} = await runPipeline(webWorker, pipelinePath, args, desiredOutputs, inputs, { pipelineBaseUrl: getPipelinesBaseUrl(), pipelineWorkerUrl: getPipelineWorkerUrl() })
if (returnValue !== 0) {
throw new Error(stderr)
}

const result = {
webWorker: usedWebWorker as Worker,
magnitudeImage: outputs[0].data as Image,
}
return result
}

export default vectorMagnitude
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,54 @@ <h2>@itk-wasm/compare-images<img src="./javascript-logo.svg" alt="JavaScript log

<sl-tab-group>
<sl-tab slot="nav" panel="compareDoubleImages-panel" disabled>compareDoubleImages</sl-tab>
<sl-tab slot="nav" panel="vectorMagnitude-panel">vectorMagnitude</sl-tab>


<sl-tab-panel name="vectorMagnitude-panel">

<small><i>Generate a scalar magnitude image based on the input vector's norm.</i></small><br /><br />

<div id="vectorMagnitudeInputs"><form action="">
<sl-tooltip content="Use the Upload button to provide the vectorImage"><sl-details id="vectorMagnitude-vector-image-details" summary="vectorImage: Input vector image" disabled></sl-details></sl-tooltip>
<label for="vector-image-file"><sl-button name="vector-image-file-button" variant="primary" outline onclick="this.parentElement.nextElementSibling.click()">Upload</sp-button></label><input type="file" name="vector-image-file" style="display: none"/>
<br /><br />
<sl-divider></sl-divider>
<br /><sl-button name="loadSampleInputs" variant="default" style="display: none;">Load sample inputs</sl-button>
<sl-button type="button" variant="success" name="run">Run</sl-button><br /><br />

</form></div>
<sl-divider></sl-divider>

<div id="vectorMagnitudeOutputs">
<sl-details disabled id="vectorMagnitude-magnitude-image-details" summary="magnitudeImage: Output magnitude image"></sl-details>
<sl-select id="magnitude-image-output-format" placeholder="Format">
<sl-option value="bmp">bmp</sl-option>
<sl-option value="dcm">dcm</sl-option>
<sl-option value="gipl">gipl</sl-option>
<sl-option value="hdf5">hdf5</sl-option>
<sl-option value="jpg">jpg</sl-option>
<sl-option value="lsm">lsm</sl-option>
<sl-option value="mnc">mnc</sl-option>
<sl-option value="mnc.gz">mnc.gz</sl-option>
<sl-option value="mgh">mgh</sl-option>
<sl-option value="mha">mha</sl-option>
<sl-option value="mrc">mrc</sl-option>
<sl-option value="nii">nii</sl-option>
<sl-option value="nii.gz">nii.gz</sl-option>
<sl-option value="png">png</sl-option>
<sl-option value="nrrd">nrrd</sl-option>
<sl-option value="png">png</sl-option>
<sl-option value="pic">pic</sl-option>
<sl-option value="tif">tif</sl-option>
<sl-option value="isq">isq</sl-option>
<sl-option value="fdf">fdf</sl-option>
<sl-option value="vtk">vtk</sl-option>
</sl-select>
<sl-button variant="neutral" outline name="magnitude-image-download" disabled>Download</sl-button>
<br /><br />
</div>

</sl-tab-panel>


</sl-tab-group>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ compareImages.setPipelinesBaseUrl(pipelinesBaseUrl)
const pipelineWorkerUrl: string | URL | null = new URL('/web-workers/pipeline.worker.js', document.location.origin).href
compareImages.setPipelineWorkerUrl(pipelineWorkerUrl)

import './vector-magnitude-controller.js'
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Generated file. To retain edits, remove this comment.

import { readImageFile } from 'itk-wasm'
import { writeImageArrayBuffer, copyImage } from 'itk-wasm'
import * as compareImages from '../../../dist/bundles/compare-images.js'
import vectorMagnitudeLoadSampleInputs from "./vector-magnitude-load-sample-inputs.js"

class VectorMagnitudeModel {

inputs: Map<string, any>
options: Map<string, any>
outputs: Map<string, any>

constructor() {
this.inputs = new Map()
this.options = new Map()
this.outputs = new Map()
}
}


class VectorMagnitudeController {

constructor(loadSampleInputs) {
this.loadSampleInputs = loadSampleInputs

this.model = new VectorMagnitudeModel()
const model = this.model

this.webWorker = null

if (loadSampleInputs) {
const loadSampleInputsButton = document.querySelector("#vectorMagnitudeInputs [name=loadSampleInputs]")
loadSampleInputsButton.setAttribute('style', 'display: block-inline;')
loadSampleInputsButton.addEventListener('click', async (event) => {
loadSampleInputsButton.loading = true
await loadSampleInputs(model)
loadSampleInputsButton.loading = false
})
}

// ----------------------------------------------
// Inputs
const vectorImageElement = document.querySelector('#vectorMagnitudeInputs input[name=vector-image-file]')
vectorImageElement.addEventListener('change', async (event) => {
const dataTransfer = event.dataTransfer
const files = event.target.files || dataTransfer.files

const { image, webWorker } = await readImageFile(null, files[0])
webWorker.terminate()
model.inputs.set("vectorImage", image)
const details = document.getElementById("vectorMagnitude-vector-image-details")
details.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(image, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
details.disabled = false
})

// ----------------------------------------------
// Options
// ----------------------------------------------
// Outputs
const magnitudeImageOutputDownload = document.querySelector('#vectorMagnitudeOutputs sl-button[name=magnitude-image-download]')
magnitudeImageOutputDownload.addEventListener('click', async (event) => {
event.preventDefault()
event.stopPropagation()
if (model.outputs.has("magnitudeImage")) {
const magnitudeImageDownloadFormat = document.getElementById('magnitude-image-output-format')
const downloadFormat = magnitudeImageDownloadFormat.value || 'nrrd'
const fileName = `magnitudeImage.${downloadFormat}`
const { webWorker, arrayBuffer } = await writeImageArrayBuffer(null, copyImage(model.outputs.get("magnitudeImage")), fileName)

webWorker.terminate()
globalThis.downloadFile(arrayBuffer, fileName)
}
})

const runButton = document.querySelector('#vectorMagnitudeInputs sl-button[name="run"]')
runButton.addEventListener('click', async (event) => {
event.preventDefault()

if(!model.inputs.has('vectorImage')) {
globalThis.notify("Required input not provided", "vectorImage", "danger", "exclamation-octagon")
return
}


try {
runButton.loading = true
const t0 = performance.now()

const { webWorker, magnitudeImage, } = await compareImages.vectorMagnitude(this.webWorker,
model.inputs.get('vectorImage'),
Object.fromEntries(model.options.entries())
)

const t1 = performance.now()
globalThis.notify("vectorMagnitude successfully completed", `in ${t1 - t0} milliseconds.`, "success", "rocket-fill")
this.webWorker = webWorker

model.outputs.set("magnitudeImage", magnitudeImage)
magnitudeImageOutputDownload.variant = "success"
magnitudeImageOutputDownload.disabled = false
const magnitudeImageDetails = document.getElementById("vectorMagnitude-magnitude-image-details")
magnitudeImageDetails.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(magnitudeImage, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
magnitudeImageDetails.disabled = false
const magnitudeImageOutput = document.getElementById('vectorMagnitude-magnitude-image-details')
} catch (error) {
globalThis.notify("Error while running pipeline", error.toString(), "danger", "exclamation-octagon")
throw error
} finally {
runButton.loading = false
}
})
}
}

const vectorMagnitudeController = new VectorMagnitudeController(vectorMagnitudeLoadSampleInputs)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Generated file. To retain edits, remove this comment.

export default null
// export default async function vectorMagnitudeLoadSampleInputs (model) {

// Load sample inputs for the vectorMagnitude function.
//
// This function should load sample inputs:
//
// 1) In the provided model map.
// 2) Into the corresponding HTML input elements.
//
// Example for an input named `exampleInput`:

// const exampleInput = 5
// model.inputs.set("exampleInput", exampleInput)
// const exampleElement = document.querySelector("#vectorMagnitudeInputs [name=example-input]")
// exampleElement.value = 5

// return model
// }

0 comments on commit 800778c

Please sign in to comment.