Skip to content

Commit

Permalink
feat(compare-images): updated demo output with support for all inputs…
Browse files Browse the repository at this point in the history
… types
  • Loading branch information
thewtex committed Aug 21, 2023
1 parent 86291ef commit 96dcfa6
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// 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 compareDoubleImagesLoadSampleInputs from "./compare-double-images-load-sample-inputs.js"

class CompareDoubleImagesModel {

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 CompareDoubleImagesController {

constructor(loadSampleInputs) {
this.loadSampleInputs = loadSampleInputs

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

this.webWorker = null

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

// ----------------------------------------------
// Inputs
const testImageElement = document.querySelector('#compareDoubleImagesInputs input[name=test-image-file]')
testImageElement.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("testImage", image)
const details = document.getElementById("compareDoubleImages-test-image-details")
details.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(image, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
details.disabled = false
})

// ----------------------------------------------
// Options
const baselineImagesElement = document.querySelector('#compareDoubleImagesInputs input[name=baseline-images-file]')
baselineImagesElement.addEventListener('change', async (event) => {
const dataTransfer = event.dataTransfer
const files = event.target.files || dataTransfer.files

const readImages = await Promise.all(Array.from(files).map(async (file) => readImageFile(null, file)))
readImages.forEach(img => img.webWorker.terminate())
const inputImages = readImages.map(img => img.image)
model.options.set("baselineImages", inputImages)
const details = document.getElementById("compareDoubleImages-baseline-images-details")
details.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(inputImages, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
details.disabled = false
})

const differenceThresholdElement = document.querySelector('#compareDoubleImagesInputs sl-input[name=difference-threshold]')
differenceThresholdElement.addEventListener('sl-change', (event) => {
model.options.set("differenceThreshold", parseFloat(differenceThresholdElement.value))
})

const radiusToleranceElement = document.querySelector('#compareDoubleImagesInputs sl-input[name=radius-tolerance]')
radiusToleranceElement.addEventListener('sl-change', (event) => {
model.options.set("radiusTolerance", parseInt(radiusToleranceElement.value))
})

const numberOfPixelsToleranceElement = document.querySelector('#compareDoubleImagesInputs sl-input[name=number-of-pixels-tolerance]')
numberOfPixelsToleranceElement.addEventListener('sl-change', (event) => {
model.options.set("numberOfPixelsTolerance", parseInt(numberOfPixelsToleranceElement.value))
})

const ignoreBoundaryPixelsElement = document.querySelector('#compareDoubleImagesInputs sl-checkbox[name=ignore-boundary-pixels]')
ignoreBoundaryPixelsElement.addEventListener('sl-change', (event) => {
model.options.set("ignoreBoundaryPixels", ignoreBoundaryPixelsElement.checked)
})

// ----------------------------------------------
// Outputs
const metricsOutputDownload = document.querySelector('#compareDoubleImagesOutputs sl-button[name=metrics-download]')
metricsOutputDownload.addEventListener('click', async (event) => {
event.preventDefault()
event.stopPropagation()
if (model.outputs.has("metrics")) {
const fileName = `metrics.json`
globalThis.downloadFile(new TextEncoder().encode(JSON.stringify(model.outputs.get("metrics"))), fileName)
}
})

const differenceImageOutputDownload = document.querySelector('#compareDoubleImagesOutputs sl-button[name=difference-image-download]')
differenceImageOutputDownload.addEventListener('click', async (event) => {
event.preventDefault()
event.stopPropagation()
if (model.outputs.has("differenceImage")) {
const differenceImageDownloadFormat = document.getElementById('difference-image-output-format')
const downloadFormat = differenceImageDownloadFormat.value || 'nrrd'
const fileName = `differenceImage.${downloadFormat}`
const { webWorker, arrayBuffer } = await writeImageArrayBuffer(null, copyImage(model.outputs.get("differenceImage")), fileName)

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

const differenceUchar2dImageOutputDownload = document.querySelector('#compareDoubleImagesOutputs sl-button[name=difference-uchar-2d-image-download]')
differenceUchar2dImageOutputDownload.addEventListener('click', async (event) => {
event.preventDefault()
event.stopPropagation()
if (model.outputs.has("differenceUchar2dImage")) {
const differenceUchar2dImageDownloadFormat = document.getElementById('difference-uchar-2d-image-output-format')
const downloadFormat = differenceUchar2dImageDownloadFormat.value || 'nrrd'
const fileName = `differenceUchar2dImage.${downloadFormat}`
const { webWorker, arrayBuffer } = await writeImageArrayBuffer(null, copyImage(model.outputs.get("differenceUchar2dImage")), fileName)

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

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

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


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

const { webWorker, metrics, differenceImage, differenceUchar2dImage, } = await compareImages.compareDoubleImages(this.webWorker,
model.inputs.get('testImage'),
Object.fromEntries(model.options.entries())
)

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

model.outputs.set("metrics", metrics)
metricsOutputDownload.variant = "success"
metricsOutputDownload.disabled = false
const metricsDetails = document.getElementById("compareDoubleImages-metrics-details")
metricsDetails.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(metrics, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
metricsDetails.disabled = false
const metricsOutput = document.getElementById("compareDoubleImages-metrics-details")

model.outputs.set("differenceImage", differenceImage)
differenceImageOutputDownload.variant = "success"
differenceImageOutputDownload.disabled = false
const differenceImageDetails = document.getElementById("compareDoubleImages-difference-image-details")
differenceImageDetails.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(differenceImage, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
differenceImageDetails.disabled = false
const differenceImageOutput = document.getElementById('compareDoubleImages-difference-image-details')

model.outputs.set("differenceUchar2dImage", differenceUchar2dImage)
differenceUchar2dImageOutputDownload.variant = "success"
differenceUchar2dImageOutputDownload.disabled = false
const differenceUchar2dImageDetails = document.getElementById("compareDoubleImages-difference-uchar-2d-image-details")
differenceUchar2dImageDetails.innerHTML = `<pre>${globalThis.escapeHtml(JSON.stringify(differenceUchar2dImage, globalThis.interfaceTypeJsonReplacer, 2))}</pre>`
differenceUchar2dImageDetails.disabled = false
const differenceUchar2dImageOutput = document.getElementById('compareDoubleImages-difference-uchar-2d-image-details')
} catch (error) {
globalThis.notify("Error while running pipeline", error.toString(), "danger", "exclamation-octagon")
throw error
} finally {
runButton.loading = false
}
})
}
}

const compareDoubleImagesController = new CompareDoubleImagesController(compareDoubleImagesLoadSampleInputs)
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 compareDoubleImagesLoadSampleInputs (model) {

// Load sample inputs for the compareDoubleImages 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("#compareDoubleImagesInputs [name=example-input]")
// exampleElement.value = 5

// return model
// }
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,97 @@ <h3>👨‍💻 Live API Demo ✨</h3>
<br /><br />

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


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

<small><i>Compare double pixel type images with a tolerance for regression testing.</i></small><br /><br />

<div id="compareDoubleImagesInputs"><form action="">
<sl-tooltip content="Use the Upload button to provide the testImage"><sl-details id="compareDoubleImages-test-image-details" summary="testImage: The input test image" disabled></sl-details></sl-tooltip>
<label for="test-image-file"><sl-button name="test-image-file-button" variant="primary" outline onclick="this.parentElement.nextElementSibling.click()">Upload</sp-button></label><input type="file" name="test-image-file" style="display: none"/>
<br /><br />
<sl-tooltip content="Use the Upload button to provide the baselineImages"><sl-details id="compareDoubleImages-baseline-images-details" summary="baselineImages: Baseline images compare against" disabled></sl-details></sl-tooltip>
<label for="baseline-images-file"><sl-button name="baseline-images-file-button" variant="primary" outline onclick="this.parentElement.nextElementSibling.click()">Upload</sp-button></label><input type="file" name="baseline-images-file" style="display: none"/>
<br /><br />
<sl-input name="difference-threshold" type="number" value="0" step="any" label="differenceThreshold" help-text="Intensity difference for pixels to be considered different."></sl-input>
<br />
<sl-input name="radius-tolerance" type="number" value="0" min="0" step="1" label="radiusTolerance" help-text="Radius of the neighborhood around a pixel to search for similar intensity values."></sl-input>
<br />
<sl-input name="number-of-pixels-tolerance" type="number" value="0" min="0" step="1" label="numberOfPixelsTolerance" help-text="Number of pixels that can be different before the test fails."></sl-input>
<br />
<sl-checkbox name="ignore-boundary-pixels">ignoreBoundaryPixels - <i>Ignore boundary pixels. Useful when resampling may have introduced difference pixel values along the image edge.</i></sl-checkbox>
<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="compareDoubleImagesOutputs">
<sl-details disabled id="compareDoubleImages-metrics-details" summary="metrics: Metrics for the baseline with the fewest number of pixels outside the tolerances."></sl-details>
<sl-button variant="neutral" outline name="metrics-download" disabled>Download</sl-button>
<br /><br />
<sl-details disabled id="compareDoubleImages-difference-image-details" summary="differenceImage: Absolute difference image"></sl-details>
<sl-select id="difference-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="difference-image-download" disabled>Download</sl-button>
<br /><br />
<sl-details disabled id="compareDoubleImages-difference-uchar-2d-image-details" summary="differenceUchar2dImage: Unsigned char, 2D difference image for rendering"></sl-details>
<sl-select id="difference-uchar-2d-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="difference-uchar-2d-image-download" disabled>Download</sl-button>
<br /><br />
</div>

</sl-tab-panel>


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

<small><i>Generate a scalar magnitude image based on the input vector's norm.</i></small><br /><br />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ compareImages.setPipelinesBaseUrl(pipelinesBaseUrl)
const pipelineWorkerUrl: string | URL | null = new URL('/web-workers/pipeline.worker.js', document.location.origin).href
compareImages.setPipelineWorkerUrl(pipelineWorkerUrl)

import './compare-double-images-controller.js'
import './vector-magnitude-controller.js'

0 comments on commit 96dcfa6

Please sign in to comment.