diff --git a/src/io/dicom.ts b/src/io/dicom.ts index fed1a7b5..2d8256d3 100644 --- a/src/io/dicom.ts +++ b/src/io/dicom.ts @@ -1,6 +1,10 @@ import { runPipeline, TextStream, InterfaceTypes, Image } from 'itk-wasm'; -import { readDicomTags, readImageDicomFileSeries } from '@itk-wasm/dicom'; +import { + readDicomTags, + readImageDicomFileSeries, + readOverlappingSegmentation, +} from '@itk-wasm/dicom'; import itkConfig from '@/src/io/itk/itkConfig'; import { getDicomSeriesWorkerPool, getWorker } from '@/src/io/itk/worker'; @@ -176,13 +180,20 @@ export async function readVolumeSlice( * @param {File[]} seriesFiles the set of files to build volume from * @returns ItkImage */ -export async function buildImage(seriesFiles: File[]) { +export async function buildImage(seriesFiles: File[], modality: string) { const inputImages = seriesFiles.map((file) => sanitizeFile(file)); + if (modality === 'SEG') { + const result = await readOverlappingSegmentation(inputImages[0], { + webWorker: getWorker(), + mergeSegments: true, + }); + console.log(result.metaInfo); + return result.segImage; + } const result = await readImageDicomFileSeries({ webWorkerPool: getDicomSeriesWorkerPool(), inputImages, singleSortedSeries: false, }); - return result.outputImage; } diff --git a/src/main.js b/src/main.js index 98d76d5b..016aef90 100644 --- a/src/main.js +++ b/src/main.js @@ -10,9 +10,11 @@ import { createApp } from 'vue'; import VueToast from 'vue-toastification'; import { createPinia } from 'pinia'; import vtkMapper from '@kitware/vtk.js/Rendering/Core/Mapper'; -import { setPipelinesBaseUrl, setPipelineWorkerUrl } from '@itk-wasm/image-io'; +import { setPipelinesBaseUrl, setPipelineWorkerUrl } from 'itk-wasm'; +import { setPipelinesBaseUrl as imageIoSetPipelinesBaseUrl } from '@itk-wasm/image-io'; import itkConfig from '@/src/io/itk/itkConfig'; + import App from './components/App.vue'; import vuetify from './plugins/vuetify'; import { FILE_READERS } from './io'; @@ -36,9 +38,11 @@ vtkMapper.setResolveCoincidentTopologyLineOffsetParameters(-3, -3); registerAllReaders(FILE_READERS); -// for @itk-wasm/image-io +// Must be set at runtime as new version of @itk-wasm/dicom and @itk-wasm/image-io +// do not pickup build time `../itkConfig` alias remap. +setPipelinesBaseUrl(itkConfig.pipelinesUrl); setPipelineWorkerUrl(itkConfig.pipelineWorkerUrl); -setPipelinesBaseUrl(itkConfig.imageIOUrl); +imageIoSetPipelinesBaseUrl(itkConfig.imageIOUrl); const pinia = createPinia(); pinia.use(CorePiniaProviderPlugin({})); diff --git a/src/store/datasets-dicom.ts b/src/store/datasets-dicom.ts index 41da3478..341708d4 100644 --- a/src/store/datasets-dicom.ts +++ b/src/store/datasets-dicom.ts @@ -143,12 +143,12 @@ export const getWindowLevels = (info: VolumeInfo) => { return widths.map((width, i) => ({ width, level: levels[i] })); }; -const constructImage = async (volumeKey: string) => { +const constructImage = async (volumeKey: string, volumeInfo: VolumeInfo) => { const fileStore = useFileStore(); const files = fileStore.getFiles(volumeKey); if (!files) throw new Error('No files for volume key'); const image = vtkITKHelper.convertItkToVtkImage( - await DICOM.buildImage(files) + await DICOM.buildImage(files, volumeInfo.Modality) ); return image; }; @@ -196,7 +196,11 @@ export const useDICOMStore = defineStore('dicom', { Object.entries(volumeToFiles).map(async ([volumeKey, files]) => { // Read tags of first file if (!(volumeKey in this.volumeInfo)) { - const tags = await readDicomTags(files[0]); + const rawTags = await readDicomTags(files[0]); + // trim whitespace from all values + const tags = Object.fromEntries( + Object.entries(rawTags).map(([key, value]) => [key, value.trim()]) + ); // TODO parse the raw string values const patient = { PatientID: tags.PatientID || ANONYMOUS_PATIENT_ID, @@ -398,7 +402,7 @@ export const useDICOMStore = defineStore('dicom', { : []; // actually build volume or wait for existing build? const newImagePromise = buildNeeded - ? constructImage(volumeKey) + ? constructImage(volumeKey, this.volumeInfo[volumeKey]) : this.volumeImageData[volumeKey]; // let other calls to buildVolume reuse this constructImage work this.volumeImageData[volumeKey] = newImagePromise; diff --git a/vite.config.ts b/vite.config.ts index 6271bb9a..1f3bc7f4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -57,7 +57,6 @@ function resolvePath(...args: string[]) { const rootDir = resolvePath(__dirname); const distDir = resolvePath(rootDir, 'dist'); -const itkConfig = resolvePath(rootDir, 'src', 'io', 'itk', 'itkConfig.js'); const { ANALYZE_BUNDLE, SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT } = process.env; @@ -113,17 +112,6 @@ export default defineConfig({ find: '@src', replacement: resolvePath(rootDir, 'src'), }, - // Patch itk-wasm library code with image-io .wasm file paths - // itkConfig alias only applies to itk-wasm library code after "npm run build" - // During "npm run serve", itk-wasm fetches image-io .wasm files from CDN - { - find: '../itkConfig.js', - replacement: itkConfig, - }, - { - find: '../../itkConfig.js', - replacement: itkConfig, - }, ], }, plugins: [