Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(brush): update data only once when Brush interaction is done #762

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion common/reviews/api/core.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export abstract class BaseVolumeViewport extends Viewport implements IVolumeView
// (undocumented)
setOrientation(orientation: OrientationAxis, immediate?: boolean): void;
// (undocumented)
setProperties({ voiRange, VOILUTFunction, invert, colormap, preset, }?: VolumeViewportProperties, volumeId?: string, suppressEvents?: boolean): void;
setProperties({ voiRange, VOILUTFunction, invert, colormap, preset, interpolationType, }?: VolumeViewportProperties, volumeId?: string, suppressEvents?: boolean): void;
// (undocumented)
abstract setSlabThickness(slabThickness: number, filterActorUIDs?: Array<string>): void;
// (undocumented)
Expand Down Expand Up @@ -2738,6 +2738,7 @@ type ViewportProperties = {
voiRange?: VOIRange;
VOILUTFunction?: VOILUTFunctionType;
invert?: boolean;
interpolationType?: InterpolationType;
};

// @public (undocumented)
Expand Down
2 changes: 2 additions & 0 deletions common/reviews/api/streaming-image-volume-loader.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,7 @@ interface IVolumeViewport extends IViewport {
// (undocumented)
useCPURendering: boolean;
worldToCanvas: (worldPos: Point3) => Point2;

}

// @public
Expand Down Expand Up @@ -1611,6 +1612,7 @@ type ViewportProperties = {
voiRange?: VOIRange;
VOILUTFunction?: VOILUTFunctionType;
invert?: boolean;
interpolationType?: InterpolationType;
};

// @public (undocumented)
Expand Down
37 changes: 31 additions & 6 deletions common/reviews/api/tools.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -562,24 +562,46 @@ declare namespace boundingBox {
type BoundsIJK = [Types_2.Point2, Types_2.Point2, Types_2.Point2];

// @public (undocumented)
export class BrushTool extends BaseTool {
interface BrushCursor extends Annotation {
// (undocumented)
data: {
handles: {
points: [Types_2.Point3];
};
};
}

// @public (undocumented)
export class BrushTool extends AnnotationTool {
constructor(toolProps?: PublicToolProps, defaultToolProps?: ToolProps);
// (undocumented)
addNewAnnotation: (evt: EventTypes_2.MouseDownActivateEventType) => BrushCursor;
// (undocumented)
cancel: () => void;
// (undocumented)
handleSelectedCallback: () => void;
// (undocumented)
invalidateBrushCursor(): void;
// (undocumented)
mouseMoveCallback: (evt: EventTypes_2.InteractionEventType) => void;
isPointNearTool(): boolean;
// (undocumented)
mouseMoveCallback: (evt: EventTypes_2.MouseMoveEventType) => boolean;
// (undocumented)
onSetConfiguration: () => void;
// (undocumented)
onSetToolActive: () => void;
// (undocumented)
onSetToolDisabled: () => void;
// (undocumented)
onSetToolEnabled: () => void;
// (undocumented)
onSetToolPassive: () => void;
// (undocumented)
preMouseDownCallback: (evt: EventTypes_2.MouseDownActivateEventType) => boolean;
// (undocumented)
renderAnnotation(enabledElement: Types_2.IEnabledElement, svgDrawingHelper: SVGDrawingHelper): void;
renderAnnotation(enabledElement: Types_2.IEnabledElement, svgDrawingHelper: SVGDrawingHelper): boolean;
// (undocumented)
static toolName: any;
// (undocumented)
toolSelectedCallback: () => void;
}

// @public (undocumented)
Expand Down Expand Up @@ -3163,6 +3185,7 @@ interface IVolumeViewport extends IViewport {
// (undocumented)
useCPURendering: boolean;
worldToCanvas: (worldPos: Point3) => Point2;

}

// @public (undocumented)
Expand Down Expand Up @@ -5143,7 +5166,8 @@ declare namespace ToolSpecificAnnotationTypes {
AngleAnnotation,
ReferenceCursor,
ReferenceLineAnnotation,
ScaleOverlayAnnotation
ScaleOverlayAnnotation,
BrushCursor
}
}

Expand Down Expand Up @@ -5449,6 +5473,7 @@ type ViewportProperties = {
voiRange?: VOIRange;
VOILUTFunction?: VOILUTFunctionType;
invert?: boolean;
interpolationType?: InterpolationType;
};

declare namespace visibility {
Expand Down
19 changes: 10 additions & 9 deletions packages/adapters/examples/segmentationExport/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import {
createImageIdsAndCacheMetaData
} from "../../../../utils/demo/helpers";
import * as cornerstoneTools from "@cornerstonejs/tools";
import { adaptersSEG } from "@cornerstonejs/adapters";
import { adaptersSEG, helpers } from "@cornerstonejs/adapters";
import dcmjs from "dcmjs";

const { downloadDICOMData } = helpers;

// This is for debugging purposes
console.warn(
"Click on index.ts to open source code for this example --------->"
Expand Down Expand Up @@ -130,15 +132,14 @@ addButtonToToolbar({
labelmapObj.metadata[segmentIndex] = segmentMetadata;
});

const segBlob = Cornerstone3D.Segmentation.generateSegmentation(
images,
labelmapObj,
metaData
);
const generatedSegmentation =
Cornerstone3D.Segmentation.generateSegmentation(
images,
labelmapObj,
metaData
);

//Create a URL for the binary.
const objectUrl = URL.createObjectURL(segBlob);
window.open(objectUrl);
downloadDICOMData(generatedSegmentation.dataset, "mySEG.dcm");
}
});

Expand Down
20 changes: 8 additions & 12 deletions packages/adapters/src/adapters/Cornerstone/Segmentation_4X.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
} from "dcmjs";
import ndarray from "ndarray";
import cloneDeep from "lodash.clonedeep";
import { Buffer } from "buffer";

import { Events } from "../enums";

Expand All @@ -19,8 +18,7 @@ const {
nearlyEqual
} = utilities.orientation;

const { datasetToDict, BitArray, DicomMessage, DicomMetaDictionary } =
dcmjsData;
const { BitArray, DicomMessage, DicomMetaDictionary } = dcmjsData;

const { Normalizer } = normalizers;
const { Segmentation: SegmentationDerivation } = derivations;
Expand Down Expand Up @@ -61,12 +59,13 @@ function generateSegmentation(images, inputLabelmaps3D, userOptions = {}) {
}

/**
* fillSegmentation - Fills a derived segmentation dataset with cornerstoneTools `LabelMap3D` data.
* Fills a given segmentation object with data from the input labelmaps3D
*
* @param {object[]} segmentation An empty segmentation derived dataset.
* @param {Object|Object[]} inputLabelmaps3D The cornerstone `Labelmap3D` object, or an array of objects.
* @param {Object} userOptions Options object to override default options.
* @returns {Blob} description
* @param segmentation - The segmentation object to be filled.
* @param inputLabelmaps3D - An array of 3D labelmaps, or a single 3D labelmap.
* @param userOptions - Optional configuration settings. Will override the default options.
*
* @returns {object} The filled segmentation object.
*/
function fillSegmentation(segmentation, inputLabelmaps3D, userOptions = {}) {
const options = Object.assign(
Expand Down Expand Up @@ -191,10 +190,7 @@ function fillSegmentation(segmentation, inputLabelmaps3D, userOptions = {}) {
segmentation.bitPackPixelData();
}

const buffer = Buffer.from(datasetToDict(segmentation.dataset).write());
const segBlob = new Blob([buffer], { type: "application/dicom" });

return segBlob;
return segmentation;
sedghi marked this conversation as resolved.
Show resolved Hide resolved
}

function _getLabelmapsFromReferencedFrameIndicies(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const { Segmentation: SegmentationDerivation } = derivations;
* @param images - An array of the cornerstone image objects, which includes imageId and metadata
* @param labelmaps - An array of the 3D Volumes that contain the segmentation data.
*/
function generateSegmentation(images, labelmaps, metadata, options) {
function generateSegmentation(images, labelmaps, metadata, options = {}) {
const segmentation = _createMultiframeSegmentationFromReferencedImages(
images,
metadata,
Expand Down Expand Up @@ -41,8 +41,8 @@ function _createMultiframeSegmentationFromReferencedImages(
...image,
...instance,
// Todo: move to dcmjs tag style
SOPClassUID: instance.SopClassUID,
SOPInstanceUID: instance.SopInstanceUID,
SOPClassUID: instance.SopClassUID || instance.SOPClassUID,
SOPInstanceUID: instance.SopInstanceUID || instance.SOPInstanceUID,
PixelData: image.getPixelData(),
_vrMap: {
PixelData: "OW"
Expand Down
35 changes: 35 additions & 0 deletions packages/adapters/src/adapters/helpers/downloadDICOMData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { data } from "dcmjs";
import { Buffer } from "buffer";
const { datasetToDict } = data;

interface DicomDataset {
_meta?: any;
// other properties
}

/**
* Trigger file download from an array buffer
* @param bufferOrDataset - ArrayBuffer or DicomDataset
* @param filename - name of the file to download
*/
export function downloadDICOMData(
bufferOrDataset: ArrayBuffer | DicomDataset,
filename: string
) {
let blob;
if (bufferOrDataset instanceof ArrayBuffer) {
blob = new Blob([bufferOrDataset], { type: "application/dicom" });
} else {
if (!bufferOrDataset._meta) {
throw new Error("Dataset must have a _meta property");
}

const buffer = Buffer.from(datasetToDict(bufferOrDataset).write());
blob = new Blob([buffer], { type: "application/dicom" });
}

const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = filename;
link.click();
}
3 changes: 2 additions & 1 deletion packages/adapters/src/adapters/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { toArray } from "./toArray";
import { codeMeaningEquals } from "./codeMeaningEquals";
import { graphicTypeEquals } from "./graphicTypeEquals";
import { downloadDICOMData } from "./downloadDICOMData";

export { toArray, codeMeaningEquals, graphicTypeEquals };
export { toArray, codeMeaningEquals, graphicTypeEquals, downloadDICOMData };
23 changes: 23 additions & 0 deletions packages/core/src/RenderingEngine/BaseVolumeViewport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import {
BlendModes,
Events,
InterpolationType,
OrientationAxis,
ViewportStatus,
VOILUTFunctionType,
Expand Down Expand Up @@ -367,6 +368,23 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
return newRGBTransferFunction;
}

private setInterpolationType(
interpolationType: InterpolationType,
volumeId?: string
) {
const applicableVolumeActorInfo = this._getApplicableVolumeActor(volumeId);

if (!applicableVolumeActorInfo) {
return;
}

const { volumeActor } = applicableVolumeActorInfo;
const volumeProperty = volumeActor.getProperty();

// @ts-ignore
volumeProperty.setInterpolationType(interpolationType);
}

/**
* Sets the properties for the volume viewport on the volume
* (if fusion, it sets it for the first volume in the fusion)
Expand Down Expand Up @@ -449,6 +467,7 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
invert,
colormap,
preset,
interpolationType,
}: VolumeViewportProperties = {},
volumeId?: string,
suppressEvents = false
Expand All @@ -467,6 +486,10 @@ abstract class BaseVolumeViewport extends Viewport implements IVolumeViewport {
this.setVOI(voiRange, volumeId, suppressEvents);
}

if (typeof interpolationType !== 'undefined') {
this.setInterpolationType(interpolationType);
}

if (VOILUTFunction !== undefined) {
this.setVOILUTFunction(VOILUTFunction, volumeId, suppressEvents);
}
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/types/ViewportProperties.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { VOILUTFunctionType } from '../enums';
import { InterpolationType, VOILUTFunctionType } from '../enums';
import { VOIRange } from './voi';

/**
Expand All @@ -11,6 +11,8 @@ type ViewportProperties = {
VOILUTFunction?: VOILUTFunctionType;
/** invert flag - whether the image is inverted */
invert?: boolean;
/** interpolation type */
interpolationType?: InterpolationType;
};

export type { ViewportProperties };
Original file line number Diff line number Diff line change
Expand Up @@ -243,10 +243,10 @@ function metaDataProvider(type, imageId) {

if (type === 'petImageModule') {
return {
frameReferenceTime: dicomParser.floatString(
frameReferenceTime: dataSet.floatString(
dataSet.string('x00541300') || ''
),
actualFrameDuration: dicomParser.intString(dataSet.string('x00181242')),
actualFrameDuration: dataSet.intString(dataSet.string('x00181242')),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ async function _convertStackToVolumeViewport(

const { id, element } = viewport;

const prevCamera = viewport.getCamera();

let imageIds = viewport.getImageIds();
imageIds = imageIds.map((imageId) => {
const imageURI = utilities.imageIdToURI(imageId);
Expand All @@ -78,7 +80,6 @@ async function _convertStackToVolumeViewport(
type: ViewportType.ORTHOGRAPHIC,
element,
defaultOptions: {
orientation: Enums.OrientationAxis.SAGITTAL,
background: <Types.Point3>[0.2, 0.4, 0.2],
},
},
Expand All @@ -98,11 +99,27 @@ async function _convertStackToVolumeViewport(

// Set the volume to load
volume.load();
const volumeViewport = <Types.IVolumeViewport>renderingEngine.getViewport(id);

setVolumesForViewports(renderingEngine, [{ volumeId }], [id]);
setVolumesForViewports(
renderingEngine,
[
{
volumeId,
},
],
[id]
);

// preserve the slice location when switching from stack to volume
element.addEventListener(Enums.Events.VOLUME_VIEWPORT_NEW_VOLUME, () => {
volumeViewport.setCamera({
focalPoint: prevCamera.focalPoint,
});
volumeViewport.render();
});

// Render the image
renderingEngine.renderViewports([id]);
volumeViewport.render();
}

export { _convertStackToVolumeViewport, _convertVolumeToStackViewport };
Loading