Skip to content

Commit

Permalink
Merge pull request #828 from thewtex/dicom-image-package
Browse files Browse the repository at this point in the history
Dicom image package
  • Loading branch information
thewtex authored Aug 6, 2023
2 parents c0941c7 + a77e77c commit 03100d8
Show file tree
Hide file tree
Showing 287 changed files with 19,204 additions and 3,954 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ packages/compress-stringify/emscripten-build/
packages/compress-stringify/wasi-build/
packages/compress-stringify/typescript/demo/
packages/compress-stringify/typescript/dist/
packages/compress-stringify/typescript/test/browser/demo-app/public
packages/compress-stringify/typescript/demo-app
packages/dicom/wasi-build/
packages/dicom/typescript/package-lock.json
packages/dicom/python/itkwasm-dicom-emscripten/test/test_itkwasm_dicom.py

node_modules
.DS_Store
Expand Down
6 changes: 6 additions & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ guide](doc/content/docs/itk_js_to_itk_wasm_migration_guide.md).
opposed to an optional parameter, since it is required.
- dicom functions access { data: <Uint8Array>, path: <string> } arguments instead of just <Uint8Array>.
- `.` removed from structured-report-to-html arguments for wrapping
- readDICOMTags moved to the @itk-wasm/dicom package as readDicomTags, api changed
- readDICOMTagsArrayBuffer removed (use readDicomTags)
- readImageDICOMFileSeries moved to @itk-wasm/dicom package as readImageDicomFileSeries, api changed
- readImageDicomFileSeries does not take casting options -- use castImage
- readImageDICOMFileSeriesArrayBuffer removed (use readImageDicomFileSeries)
- @itk-wasm/dicom node functions that take file arguments use the file path string directly

## From 1.0.0-b.72 to 1.0.0-b.73

Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/pipeline/runPipeline.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ Click. Perfect success.
})


it('writes and reads itk.Image\'s in the Emscripten filesystem', () => {
it('writes and reads itk.Image\'s with Emscripten', () => {
cy.window().then(async (win) => {
const itk = win.itk

Expand Down
14 changes: 13 additions & 1 deletion docs/docs/itk_js_to_itk_wasm_migration_guide.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# itk.js to itk-wasm Migration Guide

*Note: itk-wasm is currently in the beta development stage. The interface is relatively stable while functionality is currently being fleshed out. Updates for the rest of the documentation for changes from itk.js to itk-wasm is in progress. Last updated January 13th, 2023.*
*Note: itk-wasm is currently in the beta development stage. The interface is relatively stable while functionality is currently being fleshed out. Updates for the rest of the documentation for changes from itk.js to itk-wasm is in progress. Last updated August 5th, 2023.*

**itk-wasm** is a major upgrade with a focus on universal, performant computing in WebAssembly. The itk.js to itk-wasm transition also brings improvements, including modern, elegant programming interfaces, accelerated performance, and execution beyond JavaScript thanks to [WASI](https://wasi.dev).

Expand Down Expand Up @@ -53,9 +53,21 @@ The version of these packages follow the `itk-wasm` package version and should b

1. `itk-image-io`
2. `itk-mesh-io`
3. `@itk-wasm/dicom`

An example that vendors these package's webassembly assets into an application for deployment can be found in the [Webpack Example](https://github.com/InsightSoftwareConsortium/itk-wasm/tree/main/examples/Webpack).

### DICOM image IO functions

The following DICOM image IO functions have been migrated to the `@itk-wasm/dicom` package. Their interface changed in some cases.

1. `readImageLocalDICOMFileSeries` -> `readImageDicomFileSeriesNode`
1. `readImageDICOMFileSeries` -> `readImageDicomFileSeries`
1. `readImageDICOMArrayBufferSeries` -> `readImageDicomFileSeries`
1. `readDICOMTags` -> `readDicomTags`
1. `readDICOMTagsArrayBuffer` -> `readDicomTags`
1. `readDICOMTagsLocalFile` -> `readDicomTagsNode`

## itkConfig.js content and usage

The `itkConfig.js` specifies where to look for the io webassembly modules and an optional default base URL for pipeline modules.
Expand Down
2 changes: 1 addition & 1 deletion include/itkPipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
return (pipeline).exit(e); \
}

// Parse options while allowing extra flags, not exiting with help flags, and clearning parse state after finished.
// Parse options while allowing extra flags, not exiting with help flags, and clearing parse state after finished.
// Use this to parse some positionals or options before all options have been added.
// WARNING: It is best to only add the pre-parse options and read them through ITK_WASM_PRE_PARSE before adding other options,
// as you may face issues(EXCEPTIONS) generating bindings (bindgen), if you add "required" options/flags before the PRE_PARSE.
Expand Down
26 changes: 12 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"commit": "git cz",
"bindgen": "node ./src/itk-wasm-cli.js bindgen ./dist/dicom/src ./dist/dicom/public/pipelines/*.wasm",
"build": "npm run build:testData && npm run build:emscripten && npm run build:tsc && npm run build:tscWorkersModuleLoader && npm run build:tscWebWorkers && npm run build:workerBundles && npm run build:workerMinBundles && npm run build:webpack && node ./src/io/internal/packages/package-json-gen.cjs && npm run build:emscripten:packages",
"build:testData": "dam download packages/dicom/test/data packages/dicom/test/data.tar.gz bafybeif4qahwzxa2gb76fiuh7b5tdnqrieoa6zm2hsuywn4a54yrb24ufa https://w3s.link/ipfs/bafybeih2uapx7fk5pqdhi6gshhr6o7frbzdznhvack3h543pu3vietjq4u",
"build:testData": "dam download packages/dicom/test/data packages/dicom/test/data.tar.gz bafybeicskxufnvuem6342pkfwgeo3siiozgzmfo5f34woge6aptuzuwzzu https://github.com/InsightSoftwareConsortium/itk-wasm/releases/download/itk-wasm-v1.0.0-b.119/dicom-test-data.tar.gz https://w3s.link/ipfs/bafybeiby67winzvozowf4moqthwunuxxscssitnb6wahxv4ugvfxhu2vki/data.tar.gz",
"build:debug": "npm run build:emscripten -- --debug && npm run build:tsc && npm run build:tscWorkersModuleLoader && npm run build:tscWebWorkers && npm run build:workerBundles && npm run build:workerMinBundles && npm run build:webpack -- --mode development",
"build:tsc": "tsc --pretty",
"build:tscWorkersModuleLoader": "tsc --types --lib es2017,webworker --rootDir ./src/ --outDir ./dist/ --moduleResolution node --target es2017 --module es2020 --strict --forceConsistentCasingInFileNames --declaration ./src/core/internal/loadEmscriptenModuleWebWorker.ts",
Expand All @@ -36,15 +36,16 @@
"build:webpack": "webpack --mode production --progress --color && webpack --mode development --progress --color",
"build:emscripten": "node ./src/build-emscripten.js",
"build:emscripten:compress-stringify": "node ./src/itk-wasm-cli.js -s packages/compress-stringify -b emscripten-build build ",
"build:bindgen:typescript:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b emscripten-build bindgen --package-version 0.5.0 --package-name @itk-wasm/compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b wasi-build bindgen --language python --package-name itkwasm-compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --package-version 0.5.0 --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:dicom": "node ./src/itk-wasm-cli.js -s packages/dicom -b emscripten-build build ",
"build:bindgen:typescript:dicom": "./src/itk-wasm-cli.js -s packages/dicom -b emscripten-build bindgen --package-version 2.1.0 --package-name @itk-wasm/dicom --package-description \"Read files and images related to DICOM file format.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:dicom": "./src/itk-wasm-cli.js -s packages/dicom -b wasi-build bindgen --package-version 2.1.0 --language python --package-name itkwasm-dicom --package-description \"Read files and images related to DICOM file format.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:typescript:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b emscripten-build bindgen --package-version 0.5.1 --package-name @itk-wasm/compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b wasi-build bindgen --interface python --package-name itkwasm-compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --package-version 0.5.1 --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python-web-demo:compress-stringify": "./src/itk-wasm-cli.js -s packages/compress-stringify -b emscripten-build bindgen --interface python-web-demo --package-name itkwasm-compress-stringify --package-description \"Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.\" --package-version 0.5.1 --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:dicom": "node ./src/itk-wasm-cli.js -s packages/dicom -b emscripten-build build",
"build:bindgen:typescript:dicom": "./src/itk-wasm-cli.js -s packages/dicom -b emscripten-build bindgen --package-version 3.1.0 --package-name @itk-wasm/dicom --package-description \"Read files and images related to DICOM file format.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:bindgen:python:dicom": "./src/itk-wasm-cli.js -s packages/dicom -b wasi-build bindgen --package-version 3.1.0 --interface python --package-name itkwasm-dicom --package-description \"Read files and images related to DICOM file format.\" --repository 'https://github.com/InsightSoftwareConsortium/itk-wasm'",
"build:emscripten:packages": "npm run build:emscripten:compress-stringify && npm run build:bindgen:typescript:compress-stringify && npm run build:emscripten:dicom && npm run build:bindgen:typescript:dicom",
"build:wasi": "node ./src/build-wasi.js && npm run build:wasi:packages",
"build:wasi:compress-stringify": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/compress-stringify -b wasi-build build",
"build:wasi:dicom": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest-debug -s packages/dicom -b wasi-build build -- -DCMAKE_BUILD_TYPE=Debug",
"build:wasi:dicom": "node ./src/itk-wasm-cli.js -i itkwasm/wasi:latest -s packages/dicom -b wasi-build build",
"build:wasi:packages": "npm run build:wasi:compress-stringify && npm run build:bindgen:python:compress-stringify && npm run build:wasi:dicom && npm run build:bindgen:python:dicom",
"cypress:open": "npx cypress open",
"cypress:run": "npx cypress run --config defaultCommandTimeout=8000",
Expand All @@ -56,10 +57,11 @@
"start:production": "webpack-dev-server --mode production",
"test:wasi": "npm run test:cliRun && npm run test:cliTest",
"test": "npm run test:lint && npm run test:node && npm run test:browser && npm run test:chrome && npm run test:firefox",
"test:lint": "ts-standard --fix \"src/**/*.ts\" && standard --fix \"test/**/*.js\"",
"test:lint": "ts-standard --fix \"src/**/*.ts\" && standard --fix \"test/node/**/*.js\"",
"test:node": "ava test/node/core/*.js test/node/io/image/*.js test/node/io/mesh/*.js test/node/pipeline/*.js",
"test:cliRun": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/stdout-stderr-pipeline run -r wasmtime stdout-stderr-test.wasi.wasm",
"test:cliTest": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/stdout-stderr-pipeline test",
"test:cliBindgen:typescript": "node src/itk-wasm-cli.js -b emscripten-build -s ./test/pipelines/bindgen-interface-types-pipeline bindgen --package-version 1.0.0 --package-name @itk-wasm/bindgen-interface-types-test --package-description \"Exercise interface types for bindgen\"",
"test:browser": "karma start ./karma.conf.cjs",
"test:browser:debug": "karma start ./karma.conf.cjs --no-single-run",
"test:browser:debug:cypress": "start-server-and-test start http-get://localhost:8083 cypress:open",
Expand Down Expand Up @@ -171,12 +173,8 @@
"src/web-workers/*.ts",
"src/core/ITKWasmEmscriptenModule.ts",
"src/pipeline/PipelineEmscriptenModule.ts",
"src/bindgen/typescript-resources/*"
]
},
"standard": {
"ignore": [
"test/pipelines/input-output-json-pipeline/emscripten-build/*.js"
"src/bindgen/typescript/resources/*",
"src/bindgen/typescript/resources/demo-app/*"
]
},
"release": {
Expand Down
17 changes: 17 additions & 0 deletions packages/compress-stringify/python-web-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# itkwasm-compress-stringify

[![PyPI version](https://badge.fury.io/py/itkwasm-compress-stringify.svg)](https://badge.fury.io/py/itkwasm-compress-stringify)

> API Demo: Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.
This is a simple web browser app built with Python, HTML, and CSS that demonstrates the [itkwasm-compress-stringify](https://badge.fury.io/py/itkwasm-compress-stringify) Python package.

## Development

The app uses the Python package published on pypi.org and the corresponding JavaScript package published npmjs.com.

The web app can be developed locally with any http server that serves static files. For example:

```sh
python -m http.server
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Generated file. To retain edits, remove this comment.

from dataclasses import dataclass
from typing import Any, Dict

import numpy as np

import js
from pyodide.ffi.wrappers import add_event_listener
import pyodide

from itkwasm_compress_stringify import compress_stringify_async
from compress_stringify_load_sample_inputs import load_sample_inputs

@dataclass
class CompressStringifyModel:
inputs: Dict['str', Any]
options: Dict['str', Any]
outputs: Dict['str', Any]

class CompressStringifyController:

def __init__(self, load_sample_inputs):
self.model = CompressStringifyModel({}, {}, {})

self.load_sample_inputs = load_sample_inputs
if load_sample_inputs is not None:
load_sample_inputs_button = js.document.querySelector("#compress_stringify-inputs [name=load-sample-inputs]")
load_sample_inputs_button.setAttribute('style', 'display: block-inline;')
add_event_listener(load_sample_inputs_button, 'click', self.on_load_sample_inputs_click)

# Inputs
input_element = js.document.querySelector('#compress_stringify-inputs input[name=input-file]')
add_event_listener(input_element, 'change', self.on_input_change)

# Options
stringify_element = js.document.querySelector('#compress_stringify-inputs sl-checkbox[name=stringify]')
self.stringify_element = stringify_element
add_event_listener(stringify_element, 'sl-change', self.on_stringify_change)

compression_level_element = js.document.querySelector('#compress_stringify-inputs sl-input[name=compression-level]')
self.compression_level_element = compression_level_element
add_event_listener(compression_level_element, 'sl-change', self.on_compression_level_change)

data_url_prefix_element = js.document.querySelector('#compress_stringify-inputs sl-input[name=data-url-prefix]')
self.data_url_prefix_element = data_url_prefix_element
add_event_listener(data_url_prefix_element, 'sl-change', self.on_data_url_prefix_change)

# Outputs
output_download_element = js.document.querySelector('#compress_stringify-outputs sl-button[name=output-download]')
self.output_download_element = output_download_element
add_event_listener(output_download_element, 'click', self.on_output_click)

# Run
run_button = js.document.querySelector('#compress_stringify-inputs sl-button[name="run"]')
self.run_button = run_button
add_event_listener(run_button, 'click', self.on_run)

def on_load_sample_inputs_click(self, event):
self.model = self.load_sample_inputs(self.model)

async def on_input_change(self, event):
files = event.target.files
array_buffer = await files.item(0).arrayBuffer()
input_bytes = array_buffer.to_bytes()
self.model.inputs['input'] = input_bytes
input_element = js.document.querySelector("#compress_stringify-inputs sl-input[name=input]")
input_element.value = str(np.frombuffer(input_bytes[:50], dtype=np.uint8)) + ' ...'

def on_stringify_change(self, event):
self.model.options['stringify'] = self.stringify_element.checked

def on_compression_level_change(self, event):
self.model.options['compression_level'] = int(self.compression_level_element.value)

def on_data_url_prefix_change(self, event):
self.model.options['data_url_prefix'] = self.data_url_prefix_element.value

def on_output_click(self, event):
if 'output' not in self.model.outputs:
return
output = pyodide.ffi.to_js(self.model.outputs['output'])
js.globalThis.downloadFile(output, 'output.bin')

async def on_run(self, event):
event.preventDefault()
event.stopPropagation()

if 'input' not in self.model.inputs:

js.globalThis.notify("Error while running pipeline", "Missing input 'input'", "danger", "exclamation-octagon")
return

self.run_button.loading = True
try:
t0 = js.performance.now()
output = await compress_stringify_async(self.model.inputs['input'], **self.model.options)
t1 = js.performance.now()
js.globalThis.notify("compress_stringify successfully completed", f"in {t1 - t0} milliseconds.", "success", "rocket-fill")
self.model.outputs["output"] = output
self.output_download_element.variant = "success"
self.output_download_element.disabled = False
output_element = js.document.querySelector('#compress_stringify-outputs sl-textarea[name=output]')
output_element.value = str(np.frombuffer(output[:200], dtype=np.uint8)) + ' ...'

except Exception as error:
js.globalThis.notify("Error while running pipeline", str(error), "danger", "exclamation-octagon")
raise error
finally:
self.run_button.loading = False

compress_stringify_controller = CompressStringifyController(load_sample_inputs)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import js

# load_sample_inputs = None

def load_sample_inputs(model):
sample_input = bytes([222, 173, 190, 239])
model.inputs['input'] = sample_input
input_element = js.document.querySelector('#compress_stringify-inputs sl-input[name=input]')
input_element.value = str(sample_input)

stringify = True
model.options['stringify'] = stringify
stringify_element = js.document.querySelector('#compress_stringify-inputs sl-checkbox[name=stringify]')
stringify_element.checked = stringify

compression_level = 5
model.options['compression_level'] = compression_level
compression_level_element = js.document.querySelector('#compress_stringify-inputs sl-input[name=compression-level]')
compression_level_element.value = compression_level

data_url_prefix = 'data:application/iwi+cbor+zstd;base64,'
model.options['data_url_prefix'] = data_url_prefix
data_url_prefix_element = js.document.querySelector('#compress_stringify-inputs sl-input[name=data-url-prefix]')
data_url_prefix_element.value = data_url_prefix

return model
Loading

0 comments on commit 03100d8

Please sign in to comment.