Skip to content

Commit

Permalink
feat(segmentGroups): resample when converting to image
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulHax committed Apr 11, 2024
1 parent 4348c75 commit c19dcf2
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 10 deletions.
1 change: 1 addition & 0 deletions src/io/resample/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ find_package(ITK REQUIRED
WebAssemblyInterface
ITKImageGrid
ITKImageFunction
GenericLabelInterpolator
)
include(${ITK_USE_FILE})

Expand Down
Binary file modified src/io/resample/emscripten-build/resample.wasm
Binary file not shown.
Binary file modified src/io/resample/emscripten-build/resample.wasm.zst
Binary file not shown.
1 change: 1 addition & 0 deletions src/io/resample/itkWasmUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {

import itkConfig from '@/src/io/itk/itkConfig';

// Splits the input image into multiple slices and processes them in parallel
export async function runWasm(
pipeline,
args,
Expand Down
10 changes: 10 additions & 0 deletions src/io/resample/resample.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "itkVectorImage.h"
#include "itkResampleImageFilter.h"
#include "itkLinearInterpolateImageFunction.h"
#include "itkLabelImageGenericInterpolateImageFunction.h"
#include "itkImageRegionSplitterSlowDimension.h"
#include "itkExtractImageFilter.h"
#include "itkRGBPixel.h"
Expand Down Expand Up @@ -53,6 +54,9 @@ int Resample(itk::wasm::Pipeline &pipeline, itk::wasm::InputImage<TImage> &input
OutputImageType outputImage;
pipeline.add_option("OutputImage", outputImage, "Output image")->required();

bool label = false;
pipeline.add_flag("-l,--label", label, "Interpolate for label image")->default_val(false);

std::vector<unsigned int> outSize;
pipeline.add_option("-z,--size", outSize, "New image size for each direction")->expected(2, 3)->delimiter(',');

Expand Down Expand Up @@ -81,6 +85,12 @@ int Resample(itk::wasm::Pipeline &pipeline, itk::wasm::InputImage<TImage> &input

using ResampleFilterType = itk::ResampleImageFilter<ImageType, ImageType>;
auto resampleFilter = ResampleFilterType::New();
if (label)
{
using InterpolatorType = itk::LabelImageGenericInterpolateImageFunction<ImageType, itk::LinearInterpolateImageFunction>;
resampleFilter->SetInterpolator(InterpolatorType::New());
}

resampleFilter->SetInput(inImage);

typename ImageType::SizeType outputSize;
Expand Down
19 changes: 18 additions & 1 deletion src/io/resample/resample.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { Image } from 'itk-wasm';
import vtkImageData from '@kitware/vtk.js/Common/DataModel/ImageData';
import vtkITKHelper from '@kitware/vtk.js/Common/DataModel/ITKHelper';
import { compareImageSpaces } from '@/src/utils/imageSpace';
import { runWasm } from './itkWasmUtils';


export async function resample(fixed: Image, moving: Image) {
export async function resample(fixed: Image, moving: Image, label = false) {
const labelFlag = label ? ['--label'] : [];
const { size, spacing, origin, direction } = fixed;
const args = [
...labelFlag,
'--size',
size.join(','),
'--spacing',
Expand All @@ -17,3 +22,15 @@ export async function resample(fixed: Image, moving: Image) {

return runWasm('resample', args, [moving]);
}

export async function ensureSameSpace(target: vtkImageData, resampleCandidate: vtkImageData, label = false) {
if (compareImageSpaces(target, resampleCandidate)) {
return resampleCandidate; // could still be different pixel dimensions
}
const itkImage = await resample(
vtkITKHelper.convertVtkToItkImage(target),
vtkITKHelper.convertVtkToItkImage(resampleCandidate),
label
);
return vtkITKHelper.convertItkToVtkImage(itkImage);
}
27 changes: 18 additions & 9 deletions src/store/segmentGroups.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
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 vtkBoundingBox from '@kitware/vtk.js/Common/DataModel/BoundingBox';
import { defineStore } from 'pinia';
import { useImageStore } from '@/src/store/datasets-images';
import { join, normalize } from '@/src/utils/path';
import { useIdStore } from '@/src/store/id';
import { onImageDeleted } from '@/src/composables/onImageDeleted';
import { normalizeForStore, removeFromArray } from '@/src/utils';
import { compareImageSpaces } from '@/src/utils/imageSpace';
import { SegmentMask } from '@/src/types/segment';
import { DEFAULT_SEGMENT_MASKS } from '@/src/config';
import { readImage, writeImage } from '@/src/io/readWriteImage';
Expand All @@ -27,6 +27,7 @@ import {
getImageID,
selectionEquals,
} from './datasets';
import { ensureSameSpace } from '../io/resample/resample';

const LabelmapArrayType = Uint8Array;
export type LabelmapArrayType = Uint8Array;
Expand Down Expand Up @@ -215,8 +216,9 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => {
throw new Error('Cannot convert an image to be a labelmap of itself');

// Build vtkImageData for DICOMs
await getImage(image);
await getImage(parent);
const [childImage, parentImage] = await Promise.all(
[image, parent].map(getImage)
);

const imageID = getImageID(image);
const parentID = getImageID(parent);
Expand All @@ -225,18 +227,25 @@ export const useSegmentGroupStore = defineStore('segmentGroup', () => {
throw new Error('Image and/or parent datasets do not exist');

const imageStore = useImageStore();
const parentImage = imageStore.dataIndex[parentID];
const childImage = imageStore.dataIndex[imageID];

if (!compareImageSpaces(childImage, parentImage))
throw new Error('Image does not match parent image space');
const intersects = vtkBoundingBox.intersects(
parentImage.getBounds(),
childImage.getBounds()
);
if (!intersects) {
throw new Error(
'Segment group and parent image bounds do not intersect. So no overlap in physical space.'
);
}

const resampled = await ensureSameSpace(parentImage, childImage, true);
const labelmapImage = toLabelMap(resampled);

const segmentGroupStore = useSegmentGroupStore();
const labelmapImage = toLabelMap(childImage);
const { order, byKey } = normalizeForStore(
structuredClone(DEFAULT_SEGMENT_MASKS),
'value'
);
const segmentGroupStore = useSegmentGroupStore();
segmentGroupStore.addLabelmap(labelmapImage, {
name: imageStore.metadata[imageID].name,
parentImage: parentID,
Expand Down

0 comments on commit c19dcf2

Please sign in to comment.