diff --git a/src/components/tools/windowing/WindowLevelControls.vue b/src/components/tools/windowing/WindowLevelControls.vue index 4a01d5f2..0f0c4131 100644 --- a/src/components/tools/windowing/WindowLevelControls.vue +++ b/src/components/tools/windowing/WindowLevelControls.vue @@ -6,7 +6,7 @@ import useWindowingStore, { } from '@/src/store/view-configs/windowing'; import { useViewStore } from '@/src/store/views'; import { WLAutoRanges, WLPresetsCT, WL_AUTO_DEFAULT } from '@/src/constants'; -import { useDICOMStore } from '@/src/store/datasets-dicom'; +import { getWindowLevels, useDICOMStore } from '@/src/store/datasets-dicom'; export default defineComponent({ setup() { @@ -97,22 +97,13 @@ export default defineComponent({ }); // --- Tag WL Options --- // - - function parseTags(text: string) { - return text.split('\\'); - } - const tags = computed(() => { if ( currentImageID.value && currentImageID.value in dicomStore.imageIDToVolumeKey ) { const volKey = dicomStore.imageIDToVolumeKey[currentImageID.value]; - const { WindowWidth, WindowLevel } = dicomStore.volumeInfo[volKey]; - const levels = parseTags(WindowLevel); - return parseTags(WindowWidth).map((val, idx) => { - return { width: parseFloat(val), level: parseFloat(levels[idx]) }; - }); + return getWindowLevels(dicomStore.volumeInfo[volKey]); } return []; }); diff --git a/src/composables/useWindowingConfigInitializer.ts b/src/composables/useWindowingConfigInitializer.ts index 7174e980..32971db8 100644 --- a/src/composables/useWindowingConfigInitializer.ts +++ b/src/composables/useWindowingConfigInitializer.ts @@ -1,11 +1,12 @@ +import type { TypedArray } from '@kitware/vtk.js/types'; +import vtkDataArray from '@kitware/vtk.js/Common/Core/DataArray'; +import { watchImmediate } from '@vueuse/core'; import { useImage } from '@/src/composables/useCurrentImage'; import { useWindowingConfig } from '@/src/composables/useWindowingConfig'; import { WLAutoRanges, WL_AUTO_DEFAULT, WL_HIST_BINS } from '@/src/constants'; -import { useDICOMStore } from '@/src/store/datasets-dicom'; +import { getWindowLevels, useDICOMStore } from '@/src/store/datasets-dicom'; import useWindowingStore from '@/src/store/view-configs/windowing'; import { Maybe } from '@/src/types'; -import type { TypedArray } from '@kitware/vtk.js/types'; -import { watchImmediate } from '@vueuse/core'; import { MaybeRef, computed, unref, watch } from 'vue'; function useAutoRangeValues(imageID: MaybeRef>) { @@ -30,7 +31,12 @@ function useAutoRangeValues(imageID: MaybeRef>) { // Pre-compute the auto-range values const scalarData = imageData.value.getPointData().getScalars(); - const [min, max] = scalarData.getRange(); + // Assumes all data is one component + const { min, max } = vtkDataArray.fastComputeRange( + scalarData.getData() as number[], + 0, + 1 + ); const hist = histogram(scalarData.getData(), [min, max], WL_HIST_BINS); const cumm = hist.reduce((acc, val, idx) => { const prev = idx !== 0 ? acc[idx - 1] : 0; @@ -75,13 +81,12 @@ export function useWindowingConfigInitializer( const id = unref(imageID); if (id && id in dicomStore.imageIDToVolumeKey) { const volKey = dicomStore.imageIDToVolumeKey[id]; - const { WindowWidth, WindowLevel } = dicomStore.volumeInfo[volKey]; - return { - width: WindowWidth.split('\\')[0], - level: WindowLevel.split('\\')[0], - }; + const windowLevels = getWindowLevels(dicomStore.volumeInfo[volKey]); + if (windowLevels.length) { + return windowLevels[0]; + } } - return {}; + return undefined; }); watchImmediate(windowConfig, (config) => { @@ -103,16 +108,17 @@ export function useWindowingConfigInitializer( return; } - const range = autoRangeValues.value[autoRange.value]; + const [min, max] = autoRangeValues.value[autoRange.value]; store.updateConfig(viewIdVal, imageIdVal, { - min: range[0], - max: range[1], + min, + max, }); - if (firstTag.value?.width) { + const firstTagVal = unref(firstTag); + if (firstTagVal?.width) { store.updateConfig(viewIdVal, imageIdVal, { preset: { - width: parseFloat(firstTag.value.width), - level: parseFloat(firstTag.value.level), + width: firstTagVal.width, + level: firstTagVal.level, }, }); } diff --git a/src/store/datasets-dicom.ts b/src/store/datasets-dicom.ts index 7f05daae..00f446e9 100644 --- a/src/store/datasets-dicom.ts +++ b/src/store/datasets-dicom.ts @@ -117,6 +117,27 @@ export const getDisplayName = (info: VolumeInfo) => { ); }; +export const getWindowLevels = (info: VolumeInfo) => { + const { WindowWidth, WindowLevel } = info; + if (WindowWidth === '') return []; // missing tag + const widths = WindowWidth.split('\\').map(parseFloat); + const levels = WindowLevel.split('\\').map(parseFloat); + if ( + widths.some((w) => Number.isNaN(w)) || + levels.some((l) => Number.isNaN(l)) + ) { + console.error('Invalid WindowWidth or WindowLevel DICOM tags'); + return []; + } + if (widths.length !== levels.length) { + console.error( + 'Different numbers of WindowWidth and WindowLevel DICOM tags' + ); + return []; + } + return widths.map((width, i) => ({ width, level: levels[i] })); +}; + export const useDICOMStore = defineStore('dicom', { state: (): State => ({ sliceData: {},