Skip to content

Commit

Permalink
Merge pull request #578 from PaulHax/multi-component-hax
Browse files Browse the repository at this point in the history
fix(windowing): more robust loading of multi-component DICOMs
  • Loading branch information
floryst authored Apr 29, 2024
2 parents 0934933 + 4bb28a5 commit fccefa9
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 27 deletions.
13 changes: 2 additions & 11 deletions src/components/tools/windowing/WindowLevelControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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 [];
});
Expand Down
38 changes: 22 additions & 16 deletions src/composables/useWindowingConfigInitializer.ts
Original file line number Diff line number Diff line change
@@ -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<Maybe<string>>) {
Expand All @@ -30,7 +31,12 @@ function useAutoRangeValues(imageID: MaybeRef<Maybe<string>>) {

// 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;
Expand Down Expand Up @@ -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) => {
Expand All @@ -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,
},
});
}
Expand Down
21 changes: 21 additions & 0 deletions src/store/datasets-dicom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand Down

0 comments on commit fccefa9

Please sign in to comment.