From 357aad7fc89c2da2004258b7f373844fb629001b Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Thu, 30 Nov 2023 21:44:50 -0500 Subject: [PATCH 1/5] feat(segmentGroups): add image format config on serialization --- package-lock.json | 17 ++++++++ package.json | 3 +- src/io/import/common.ts | 2 +- .../import/{configSchema.ts => configJson.ts} | 16 ++++++++ src/io/import/importDataSources.ts | 2 +- src/io/import/processors/handleConfig.ts | 2 +- src/shims-vtk.d.ts | 6 --- src/store/segmentGroups.ts | 40 ++++++++++++++++--- 8 files changed, 72 insertions(+), 16 deletions(-) rename src/io/import/{configSchema.ts => configJson.ts} (89%) diff --git a/package-lock.json b/package-lock.json index 4db0f8b6f..3ebe00e6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.435.0", "@itk-wasm/dicom": "^5.0.0", + "@itk-wasm/image-io": "^0.5.0", "@kitware/vtk.js": "^28.13.0", "@netlify/edge-functions": "^2.0.0", "@sentry/vue": "^7.54.0", @@ -2713,6 +2714,14 @@ "itk-wasm": "^1.0.0-b.154" } }, + "node_modules/@itk-wasm/image-io": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@itk-wasm/image-io/-/image-io-0.5.0.tgz", + "integrity": "sha512-h7SF9bb+DDLZsH+tpBFZyxCM7Dqf0bbvayhS8/bn3voyTZQy80bEMnPgmRCzUoGWGyDOygbSgqohqJ6IelvDvA==", + "dependencies": { + "itk-wasm": "^1.0.0-b.154" + } + }, "node_modules/@jest/expect-utils": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.3.tgz", @@ -24511,6 +24520,14 @@ "itk-wasm": "^1.0.0-b.154" } }, + "@itk-wasm/image-io": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@itk-wasm/image-io/-/image-io-0.5.0.tgz", + "integrity": "sha512-h7SF9bb+DDLZsH+tpBFZyxCM7Dqf0bbvayhS8/bn3voyTZQy80bEMnPgmRCzUoGWGyDOygbSgqohqJ6IelvDvA==", + "requires": { + "itk-wasm": "^1.0.0-b.154" + } + }, "@jest/expect-utils": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.3.tgz", diff --git a/package.json b/package.json index 88368e319..6e31d66fc 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "dependencies": { "@aws-sdk/client-s3": "^3.435.0", "@itk-wasm/dicom": "^5.0.0", + "@itk-wasm/image-io": "^0.5.0", "@kitware/vtk.js": "^28.13.0", "@netlify/edge-functions": "^2.0.0", "@sentry/vue": "^7.54.0", @@ -126,4 +127,4 @@ "eslint" ] } -} \ No newline at end of file +} diff --git a/src/io/import/common.ts b/src/io/import/common.ts index bc34371ea..ab9627d04 100644 --- a/src/io/import/common.ts +++ b/src/io/import/common.ts @@ -3,7 +3,7 @@ import { DataSource, FileSource } from '@/src/io/import/dataSource'; import { Handler } from '@/src/core/pipeline'; import { ARCHIVE_FILE_TYPES } from '@/src/io/mimeTypes'; import { Awaitable } from '@vueuse/core'; -import { Config } from '@/src/io/import/configSchema'; +import { Config } from '@/src/io/import/configJson'; interface DataResult { dataSource: DataSource; diff --git a/src/io/import/configSchema.ts b/src/io/import/configJson.ts similarity index 89% rename from src/io/import/configSchema.ts rename to src/io/import/configJson.ts index 900544463..231da4373 100644 --- a/src/io/import/configSchema.ts +++ b/src/io/import/configJson.ts @@ -10,6 +10,7 @@ import { useDataBrowserStore } from '@/src/store/data-browser'; import { usePolygonStore } from '@/src/store/tools/polygons'; import { useViewStore } from '@/src/store/views'; import { actionToKey } from '@/src/composables/useKeyboardShortcuts'; +import { useSegmentGroupStore } from '@/src/store/segmentGroups'; const layout = z .object({ @@ -54,11 +55,18 @@ const labels = z }) .optional(); +const io = z + .object({ + segmentGroupSaveFormat: z.string().optional(), + }) + .optional(); + export const config = z.object({ layout, dataBrowser, labels, shortcuts, + io, }); export type Config = z.infer; @@ -107,9 +115,17 @@ const applyShortcuts = (manifest: Config) => { }; }; +const applyIo = (manifest: Config) => { + if (!manifest.io) return; + + if (manifest.io.segmentGroupSaveFormat) + useSegmentGroupStore().saveFormat = manifest.io.segmentGroupSaveFormat; +}; + export const applyConfig = (manifest: Config) => { applyLayout(manifest); applyLabels(manifest); applySampleData(manifest); applyShortcuts(manifest); + applyIo(manifest); }; diff --git a/src/io/import/importDataSources.ts b/src/io/import/importDataSources.ts index e3ddc7127..656192b6e 100644 --- a/src/io/import/importDataSources.ts +++ b/src/io/import/importDataSources.ts @@ -23,7 +23,7 @@ import updateFileMimeType from '@/src/io/import/processors/updateFileMimeType'; import handleConfig from '@/src/io/import/processors/handleConfig'; import { useDICOMStore } from '@/src/store/datasets-dicom'; import { makeDICOMSelection, makeImageSelection } from '@/src/store/datasets'; -import { applyConfig } from '@/src/io/import/configSchema'; +import { applyConfig } from '@/src/io/import/configJson'; /** * Tries to turn a thrown object into a meaningful error string. diff --git a/src/io/import/processors/handleConfig.ts b/src/io/import/processors/handleConfig.ts index a04f290c0..b093ba236 100644 --- a/src/io/import/processors/handleConfig.ts +++ b/src/io/import/processors/handleConfig.ts @@ -1,6 +1,6 @@ import { ImportHandler } from '@/src/io/import/common'; import { ensureError } from '@/src/utils'; -import { readConfigFile } from '@/src/io/import/configSchema'; +import { readConfigFile } from '@/src/io/import/configJson'; /** * Reads a JSON file with label config and updates stores. diff --git a/src/shims-vtk.d.ts b/src/shims-vtk.d.ts index 4bb4d5721..f1ee4b343 100644 --- a/src/shims-vtk.d.ts +++ b/src/shims-vtk.d.ts @@ -11,12 +11,6 @@ declare module '@kitware/vtk.js/Interaction/Style/InteractorStyleManipulator/Pre export default any; } -declare module '@kitware/vtk.js/Common/DataModel/ITKHelper' { - import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData'; - export function convertVtkToItkImage(image: vtkImageData): Image; - export function convertItkToVtkImage(image: Image): vtkImageData; -} - declare module '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps' { export declare const vtkColorMaps: { addPreset(preset: any): void; diff --git a/src/store/segmentGroups.ts b/src/store/segmentGroups.ts index 4cd1b225f..20914dbab 100644 --- a/src/store/segmentGroups.ts +++ b/src/store/segmentGroups.ts @@ -1,6 +1,12 @@ import { computed, reactive, ref, toRaw, watch } from 'vue'; import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray'; import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData'; +import vtkITKHelper from '@kitware/vtk.js/Common/DataModel/ITKHelper'; +import { copyImage } from 'itk-wasm'; +import { + readImage as readImageItk, + writeImage as writeImageItk, +} from '@itk-wasm/image-io'; import { defineStore } from 'pinia'; import { useImageStore } from '@/src/store/datasets-images'; import { join, normalize } from '@/src/utils/path'; @@ -27,6 +33,24 @@ import { selectionEquals, } from './datasets'; +const readImage = async (file: File) => { + if (file.name.endsWith('.vti')) + return (await vtiReader(file)) as vtkImageData; + + const { image } = await readImageItk(null, file); + return vtkITKHelper.convertItkToVtkImage(image); +}; + +const writeImage = async (format: string, image: vtkImageData) => { + if (format === 'vti') { + return vtiWriter(image); + } + // copyImage so writeImage does not detach live data when passing to worker + const itkImage = copyImage(vtkITKHelper.convertVtkToItkImage(image)); + const result = await writeImageItk(null, itkImage, `image.${format}`); + return result.serializedImage.data; +}; + const LabelmapArrayType = Uint8Array; export type LabelmapArrayType = Uint8Array; @@ -332,6 +356,8 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => { ); } + const saveFormat = ref('vti'); + /** * Serializes the store's state. */ @@ -348,7 +374,7 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => { const metadata = metadataByID[id]; return { id, - path: `labels/${id}.vti`, + path: `labels/${id}.${saveFormat.value}`, metadata: { ...metadata, parentImage: getDataID(metadata.parentImage), @@ -362,8 +388,9 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => { // save labelmap images await Promise.all( serialized.map(async ({ id, path }) => { - const vtiImage = await vtiWriter(dataIndex[id]); - zip.file(path, vtiImage); + const vtkImage = dataIndex[id]; + const serializedImage = await writeImage(saveFormat.value, vtkImage); + zip.file(path, serializedImage); }) ); } @@ -394,9 +421,9 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => { ) .map((entry) => entry.file); - const labelmapImage = toLabelMap( - (await vtiReader(file)) as vtkImageData - ); + const vtkImage = await readImage(file); + const labelmapImage = toLabelMap(vtkImage); + const id = useIdStore().nextId(); dataIndex[id] = labelmapImage; this.$proxies.addData(id, labelmapImage); @@ -464,6 +491,7 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => { metadataByID, orderByParent, segmentByGroupID, + saveFormat, addLabelmap, newLabelmapFromImage, removeGroup, From 5e2bf3012f2deb90222de14769da3b34b776af10 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Tue, 5 Dec 2023 18:40:40 -0500 Subject: [PATCH 2/5] fix(io): remove itk-image-io dependency --- package-lock.json | 17 ----------------- package.json | 1 - src/io/readers.ts | 18 +++++------------- src/main.js | 6 ++++++ src/store/dicom-web/dicom-web-store.ts | 1 + vite.config.ts | 4 ++-- 6 files changed, 14 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ebe00e6d..83c713b68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "dicomweb-client-typed": "^0.8.6", "file-saver": "^2.0.5", "gl-matrix": "3.4.3", - "itk-image-io": "1.0.0-b.156", "itk-wasm": "1.0.0-b.156", "jszip": "3.10.0", "mitt": "^3.0.0", @@ -13166,14 +13165,6 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, - "node_modules/itk-image-io": { - "version": "1.0.0-b.156", - "resolved": "https://registry.npmjs.org/itk-image-io/-/itk-image-io-1.0.0-b.156.tgz", - "integrity": "sha512-PxhCRK0QqBKbQy20moj7ecftlXZjvjlEZDY9WjxsKif4jN1z8VYOxQS9Ie5eOdOVQDylbXu3Ejgg+xa4DBf1Rw==", - "dependencies": { - "itk-wasm": "1.0.0-b.156" - } - }, "node_modules/itk-wasm": { "version": "1.0.0-b.156", "resolved": "https://registry.npmjs.org/itk-wasm/-/itk-wasm-1.0.0-b.156.tgz", @@ -32536,14 +32527,6 @@ "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true }, - "itk-image-io": { - "version": "1.0.0-b.156", - "resolved": "https://registry.npmjs.org/itk-image-io/-/itk-image-io-1.0.0-b.156.tgz", - "integrity": "sha512-PxhCRK0QqBKbQy20moj7ecftlXZjvjlEZDY9WjxsKif4jN1z8VYOxQS9Ie5eOdOVQDylbXu3Ejgg+xa4DBf1Rw==", - "requires": { - "itk-wasm": "1.0.0-b.156" - } - }, "itk-wasm": { "version": "1.0.0-b.156", "resolved": "https://registry.npmjs.org/itk-wasm/-/itk-wasm-1.0.0-b.156.tgz", diff --git a/package.json b/package.json index 6e31d66fc..4d6ec07b5 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "dicomweb-client-typed": "^0.8.6", "file-saver": "^2.0.5", "gl-matrix": "3.4.3", - "itk-image-io": "1.0.0-b.156", "itk-wasm": "1.0.0-b.156", "jszip": "3.10.0", "mitt": "^3.0.0", diff --git a/src/io/readers.ts b/src/io/readers.ts index 62ff3425f..14ce9336b 100644 --- a/src/io/readers.ts +++ b/src/io/readers.ts @@ -1,8 +1,9 @@ import vtkITKImageReader from '@kitware/vtk.js/IO/Misc/ITKImageReader'; +import { convertItkToVtkImage } from '@kitware/vtk.js/Common/DataModel/ITKHelper'; import { readImageArrayBuffer, extensionToImageIO } from 'itk-wasm'; +import { readImage } from '@itk-wasm/image-io'; import { FileReaderMap } from '.'; -import { readFileAsArrayBuffer } from './io'; import { stlReader, vtiReader, vtpReader } from './vtk/async'; import { FILE_EXT_TO_MIME } from './mimeTypes'; @@ -17,18 +18,9 @@ export const ITK_IMAGE_MIME_TYPES = Array.from( vtkITKImageReader.setReadImageArrayBufferFromITK(readImageArrayBuffer); async function itkReader(file: File) { - const fileBuffer = await readFileAsArrayBuffer(file); - - const reader = vtkITKImageReader.newInstance(); - reader.setFileName(file.name); - try { - await reader.parseAsArrayBuffer(fileBuffer); - } catch (e) { - // itkreader doesn't give us a meaningful error - throw new Error('Failed to parse file'); - } - - return reader.getOutputData(); + const { image, webWorker } = await readImage(null, file); + webWorker.terminate(); + return convertItkToVtkImage(image); } /** diff --git a/src/main.js b/src/main.js index 4501beec5..b53260df4 100644 --- a/src/main.js +++ b/src/main.js @@ -12,7 +12,9 @@ import { createPinia } from 'pinia'; import vtkProxyManager from '@kitware/vtk.js/Proxy/Core/ProxyManager'; import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper'; import vtkImageMapper from '@kitware/vtk.js/Rendering/Core/ImageMapper'; +import { setPipelinesBaseUrl, setPipelineWorkerUrl } from '@itk-wasm/image-io'; +import itkConfig from '@/src/io/itk/itkConfig'; import App from './components/App.vue'; import vuetify from './plugins/vuetify'; import { DICOMIO } from './io/dicom'; @@ -44,6 +46,10 @@ const proxyManager = vtkProxyManager.newInstance({ proxyConfiguration }); const dicomIO = new DICOMIO(); dicomIO.initialize(); +// for @itk-wasm/image-io +setPipelineWorkerUrl(itkConfig.pipelineWorkerUrl); +setPipelinesBaseUrl(itkConfig.imageIOUrl); + const pinia = createPinia(); pinia.use( CorePiniaProviderPlugin({ diff --git a/src/store/dicom-web/dicom-web-store.ts b/src/store/dicom-web/dicom-web-store.ts index fe33fc1a5..a51c8bb2b 100644 --- a/src/store/dicom-web/dicom-web-store.ts +++ b/src/store/dicom-web/dicom-web-store.ts @@ -203,6 +203,7 @@ export const useDicomWebStore = defineStore('dicom-web', () => { state: 'Done', }; } catch (error) { + console.error(error); const messageStore = useMessageStore(); messageStore.addError('Failed to load DICOM', error as Error); volumes.value[volumeKey] = { diff --git a/vite.config.ts b/vite.config.ts index b3d5f6a86..3da1114f7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -151,8 +151,8 @@ export default defineConfig({ }, { src: resolvePath( - resolveNodeModulePath('itk-image-io'), - '*{.wasm,.js,.zst}' + resolveNodeModulePath('@itk-wasm/image-io'), + 'dist/pipelines/*{.wasm,.js,.zst}' ), dest: 'itk/image-io', }, From 6ae16e8ff42c0d80259589486da235d92ff28ab7 Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Fri, 8 Dec 2023 16:18:37 -0500 Subject: [PATCH 3/5] fix(segmentGroups): workaround direction matrix bug in writeImage --- src/store/segmentGroups.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/store/segmentGroups.ts b/src/store/segmentGroups.ts index 20914dbab..fb9929d98 100644 --- a/src/store/segmentGroups.ts +++ b/src/store/segmentGroups.ts @@ -47,6 +47,18 @@ const writeImage = async (format: string, image: vtkImageData) => { } // copyImage so writeImage does not detach live data when passing to worker const itkImage = copyImage(vtkITKHelper.convertVtkToItkImage(image)); + + // Transpose the direction matrix to fix bug in @itk-wasm/image-io.writeImage + // Remove when @itk-wasm/image-io version is above 0.5.0 https://github.com/InsightSoftwareConsortium/itk-wasm/commit/ad9ca85eedc47c9d3444cf36859569c529886bde + const oldDirection = [...itkImage.direction]; + const { dimension } = itkImage.imageType; + for (let idx = 0; idx < dimension; ++idx) { + for (let idy = 0; idy < dimension; ++idy) { + itkImage.direction[idx + idy * dimension] = + oldDirection[idy + idx * dimension]; + } + } + const result = await writeImageItk(null, itkImage, `image.${format}`); return result.serializedImage.data; }; From 12f770aee12e166a08cdbf977f7da30f7af0ffad Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Mon, 11 Dec 2023 12:16:51 -0500 Subject: [PATCH 4/5] doc(configuration_file): add separate page for JSON config Add segmentGroupSaveFormat section. --- .../content/doc/configuration_file.md | 141 ++++++++++++++++++ documentation/content/doc/loading_data.md | 86 ----------- documentation/content/doc/state_files.md | 2 +- documentation/tpl/__en__ | 3 +- documentation/tpl/__sidebar__ | 5 +- 5 files changed, 147 insertions(+), 90 deletions(-) create mode 100644 documentation/content/doc/configuration_file.md diff --git a/documentation/content/doc/configuration_file.md b/documentation/content/doc/configuration_file.md new file mode 100644 index 000000000..af0e78a52 --- /dev/null +++ b/documentation/content/doc/configuration_file.md @@ -0,0 +1,141 @@ +# Configuration JSON File + +By loading a JSON file, you can set VolView's: + +- Starting view layout (Axial Only, 3D Primary, etc). +- Labels for tools +- Visibility of Sample Data section +- Keyboard shortcuts + +## Starting view layout + +The `activeLayout` key has options (Axial Only, 3D Primary, etc.) defined in `config.ts` + +```json +{ + "layout": { + "activeLayout": "Axial Only" + } +} +``` + +## Labels for tools + +Each tool type (Rectangle, Polygon, etc.) can have tool specific labels. To share labels +across tools, define the `defaultLabels` key and don't provide labels for a tool that +should use the default labels. + +```json +{ + "labels": { + "defaultLabels": { + "lesion": { "color": "#ff0000" }, + "tumor": { "color": "green", "strokeWidth": 3 } + } + } +} +``` + +## Segment Group File Format + +The `segmentGroupSaveFormat` key specifies the file extension of the segment group images +VolView will include in the volview.zip file. + +```json +{ + "io": { + "segmentGroupSaveFormat": "nii" + } +} +``` + +These are the supported file formats: + +hdf5, iwi.cbor, mha, nii, nii.gz, nrrd, vtk + +## Keyboard Shortcuts + +Configure the keys to activate tools, change selected labels, and more. +All [shortcut actions](https://github.com/Kitware/VolView/blob/main/src/constants.ts#L53) are under the `ACTIONS` variable. + +To configure a key for an action, add its action name and the key(s) under the `shortcuts` section. For key combinations, use `+` like `Ctrl+f`. + +```json +{ + "shortcuts": { + "polygon": "Ctrl+p", + "showKeyboardShortcuts": "t" + } +} +``` + +## Visibility of Sample Data section + +Simplify the data browser by hiding the Sample Data expandable section. + +```json +{ + "dataBrowser": { + "hideSampleData": false + } +} +``` + +## Example JSON: + +```json +{ + "labels": { + "defaultLabels": { + "lesion": { "color": "#ff0000" }, + "tumor": { "color": "green", "strokeWidth": 3 } + } + }, + "layout": { + "activeLayout": "Axial Only" + } +} +``` + +## All options: + +```json +{ + "labels": { + "defaultLabels": { + "lesion": { "color": "#ff0000" }, + "tumor": { "color": "green", "strokeWidth": 3 }, + "innocuous": { "color": "white" } + }, + "rulerLabels": { + "big": { "color": "#ff0000" }, + "small": { "color": "white" } + }, + "rectangleLabels": { + "red": { "color": "#ff0000", "fillColor": "transparent" }, + "green": { "color": "green", "fillColor": "transparent" }, + "white-yellow-fill": { + "color": "white", + "fillColor": "#00ff0030" + } + }, + "polygonLabels": { + "poly1": { "color": "#ff0000" }, + "poly2Label": { "color": "green" } + } + }, + "layout": { + "activeLayout": "Axial Only" + }, + "dataBrowser": { + "hideSampleData": false + }, + "shortcuts": { + "polygon": "Ctrl+p", + "showKeyboardShortcuts": "t" + }, + "io": { + "segmentGroupSaveFormat": "nrrd" + } +} +``` diff --git a/documentation/content/doc/loading_data.md b/documentation/content/doc/loading_data.md index d0ef92498..04b368a13 100644 --- a/documentation/content/doc/loading_data.md +++ b/documentation/content/doc/loading_data.md @@ -56,92 +56,6 @@ page](https://github.com/Kitware/VolView/issues)! In order for VolView to download and display your remote datasets, your server must be configured with the correct [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) configuration. CORS is a browser security mechanism that restricts how web applications can access remote resources. Without proper CORS configuration, VolView will be unable to download and display your datasets. Configuring your web server with CORS is beyond the scope of this documentation; please refer to your server's documentation. -### Configuration JSON File - -By loading a JSON file, you can set VolView's: - -- Starting view layout (Axial Only, 3D Primary, etc). -- Labels for tools -- Visibility of Sample Data section -- Keyboard shortcuts - -#### Starting view layout - -The `activeLayout` key has options (Axial Only, 3D Primary, etc.) defined in `config.ts` - -#### Labels for tools - -Each tool type (Rectangle, Polygon, etc.) can have tool specific labels. To share labels -across tools, define the `defaultLabels` key and don't provide labels for a tool that -should use the default labels. - -#### Visibility of Sample Data section - -Simplify the data browser by hiding the Sample Data expandable section. - -#### Keyboard Shortcuts - -Configure the keys to activate tools, change selected labels, and more. -All [shortcut actions](https://github.com/Kitware/VolView/blob/main/src/constants.ts#L53) are under the `ACTIONS` variable. - -To configure a key for an action, add its action name and the key(s) under the `shortcuts` section. For key combinations, use `+` like `Ctrl+f`. - -#### Example JSON: - -```json -{ - "labels": { - "defaultLabels": { - "lesion": { "color": "#ff0000" }, - "tumor": { "color": "green", "strokeWidth": 3 } - } - }, - "layout": { - "activeLayout": "Axial Only" - } -} -``` - -#### All options: - -```json -{ - "labels": { - "defaultLabels": { - "lesion": { "color": "#ff0000" }, - "tumor": { "color": "green", "strokeWidth": 3 }, - "innocuous": { "color": "white" } - }, - "rulerLabels": { - "big": { "color": "#ff0000" }, - "small": { "color": "white" } - }, - "rectangleLabels": { - "red": { "color": "#ff0000", "fillColor": "transparent" }, - "green": { "color": "green", "fillColor": "transparent" }, - "white-yellow-fill": { - "color": "white", - "fillColor": "#00ff0030" - } - }, - "polygonLabels": { - "poly1": { "color": "#ff0000" }, - "poly2Label": { "color": "green" } - } - }, - "layout": { - "activeLayout": "Axial Only" - }, - "dataBrowser": { - "hideSampleData": false - } - "shortcuts": { - "polygon": "Ctrl+p", - "showKeyboardShortcuts": "t" - } -} -``` - ## Layer Images To overlay images in the 2D views, there is a layer button on image thumbnails. A PET image could be layered on top of a CT image. The layered image is resampled to the base image using the image's spatial metadata. If the spatial metadata does not place the images in the same coordinate system, the layer alignment will be incorrect. diff --git a/documentation/content/doc/state_files.md b/documentation/content/doc/state_files.md index 7a9d8e8c6..ac78eaf44 100644 --- a/documentation/content/doc/state_files.md +++ b/documentation/content/doc/state_files.md @@ -2,7 +2,7 @@ VolView state files are a great way to save your scene and data to either be used later, or for distributing to collaborators and other users. These files store all of the information you need to restore the state of VolView: your data, annotations, camera positions, background colors, colormaps, and more. -State files can be saved by clicking on "Disk" icon in the top of the toolbar. This button will generate a `*.volview` file that can then be re-opened in VolView at any time. +State files can be saved by clicking on "Disk" icon in the top of the toolbar. This button will generate a `*.volview.zip` file that can then be re-opened in VolView at any time. When saving VolView state, your data is saved along with the application state. This way, when you send a state file to a collaborator, they too can open the state file and load the previously saved data. However, this means that your state file will be as large as your dataset(s) and may contain patient identifying information. Please follow your institutes HIPAA, IRB and other regulatory and confidentiality requirements. diff --git a/documentation/tpl/__en__ b/documentation/tpl/__en__ index 9bf65bbeb..b41bdc4c0 100644 --- a/documentation/tpl/__en__ +++ b/documentation/tpl/__en__ @@ -29,4 +29,5 @@ sidebar: mouse_controls: Mouse/Keyboard Controls rendering: Cinematic Rendering state_files: State Files - server: Remote Server Capabilities \ No newline at end of file + server: Remote Server Capabilities + configuration_file: Configuration File diff --git a/documentation/tpl/__sidebar__ b/documentation/tpl/__sidebar__ index 56f52dfad..6ee3e3420 100644 --- a/documentation/tpl/__sidebar__ +++ b/documentation/tpl/__sidebar__ @@ -1,8 +1,8 @@ doc: - introduction: + introduction: what_is_volview: index.html quick_start_guide: quick_start_guide.html - details: + details: welcome_screen: welcome_screen.html loading_data: loading_data.html toolbar: toolbar.html @@ -10,3 +10,4 @@ doc: rendering: rendering.html state_files: state_files.html server: server.html + configuration_file: configuration_file.html From 67d2631cd6000625844015a6e44a62630191fc6d Mon Sep 17 00:00:00 2001 From: Paul Elliott Date: Thu, 14 Dec 2023 16:07:33 -0500 Subject: [PATCH 5/5] fix(segmentGroups): terminate webworker after exporting --- src/store/segmentGroups.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/store/segmentGroups.ts b/src/store/segmentGroups.ts index fb9929d98..b04238103 100644 --- a/src/store/segmentGroups.ts +++ b/src/store/segmentGroups.ts @@ -60,6 +60,7 @@ const writeImage = async (format: string, image: vtkImageData) => { } const result = await writeImageItk(null, itkImage, `image.${format}`); + result.webWorker?.terminate(); return result.serializedImage.data; };