feat: cornerstone3D stack and volume viewports (#2787)
* feat: cs3d working stack viewport and tools (#19) * Squashed everything * fix weird react issue * fix eslint / prettier stuff * thumbnails work now with cpu rendering * feat: use new loadImageToCanvas in cs3d * make jump to measurement work * fix active thumbnail * fix measurement delete * fix window level presets * remove segmentation and sync groups for now * fix the dicom pdf and dicom video * fix cornerstone window assignment for cypress * apply review comments Co-authored-by: Erik <erik.sweed@gmail.com> * feat: cs3d tools and toolGroups (#20) * add more tools to work with cs3d mode * fix hotkeys * add stack manager usage for stach viewports * add image scrollbar * wip viewport overlay * fix toAnnotation schema for tools * fix the unnecessary size change that triggered resize * hanging protocol improvement to allow unmatched errors * study description matching for hanging protocol * fix the displaysetOptions to work * fix handle the active tool when a new viewport is added * fix separate toolGroups for mode * apply review comments * apply review comments * yarn lock * feat: overlay component (#21) * fix default displayset options * add viewport overlay * apply review comments * feat: loading and orientation indicators (#22) * add loading indicator * add orientation marker initial work * apply review comments * fix: orientation markers (#23) * finished the orientation markers * fix various broken cypress tests * apply review comments * update yarn lock * feat: re-working measurement tracking mode with cornerstone3d (#2805) * feat: cs3d working stack viewport and tools (#19) * Squashed everything * fix weird react issue * fix eslint / prettier stuff * thumbnails work now with cpu rendering * feat: use new loadImageToCanvas in cs3d * make jump to measurement work * fix active thumbnail * fix measurement delete * fix window level presets * remove segmentation and sync groups for now * fix the dicom pdf and dicom video * fix cornerstone window assignment for cypress * apply review comments Co-authored-by: Erik <erik.sweed@gmail.com> * feat: Add Measurement tracking mode with cs3D (#2789) * feat: first render for cornerstone3d tracked viewport * make tool active work * wip for SR extension * renamed dicom sr to cornerstone dicom sr * remove cornerstone from dicom pdf and video * move dicom sr logic to sr extension * feat: Add hydration for the length tool * fix SR display tool for length using cs3d * fix default config * fix: various bugs with sr viewport and tracking * fix promptying to continue tracking for when SR is created * feat: add keep trackign of unique identifiers * fix hydration for same imageIds * feat: Add SR toolGroup creation on modeEnter * feat: remove the need for separate mapper for SR hydration * add SR display for ellipse * handle hydration of elliptical ROI tool * remove cornerstone extension * add arrow mapping * feat: Add ArrowAnnotate SR display and hydration * apply review comments * apply review comments * move viewport labels to the viewportData * apply review comments * fix: integration cypress tests with cornerstone3D and add CINE tool (#2795) * fix: integration cypress tests with cornerstone3D * revert to addOrUpdate as it makes more sense * fix local drag and drop * fix tests * move dicomLoaderService to cornerstone extension * fix various import bugs * fix bug for local PT series * fix various unit tests * bump cs3d versions * add angle and magnify tool * bump dependencies to avoid broken peerDeps * feat: add initial work for capture using cs3d * feat: show annotations on the image capture * feat: add svg layer export * feat: Add CINE Tool * feat: remove unnecessary viewport rendering for cine state changes Co-authored-by: Erik Ziegler <erik.sweed@gmail.com> * docs: modify and improve documentation (#2800) * cleanup docs versionings * feat: Add all version explanations * version docs for 3.0 * wip for changing docs * wip for updated docs * add utility module documentation * fix demo with nohoisting of history * add slides and video to resources * apply review comments * fix: drag and drop SR into SR viewport (#2803) * update yarn lock Co-authored-by: Erik <erik.sweed@gmail.com> * update pathnames to match v3-stable * update the e2e pathname * fix: various bugs with regard to tracking workflow (#2811) * fix: various issues with measurement panel * fix: update default tool style for annotations * fix: annotatoin label getting removed * feat: Add backward compatibility for SR hydration with legacy cornerstone * fix: cursors and ellipse ROI max style * fix: ArrowAnnotate SRDisplay * apply review comments * bump package versions * fix: bugin rehydration of SR * fix: e2e tests * fix active viewport thickness and arrowTool ui * add readme for measurement tracking * use uploaded image for readme * add back images * try to fix e2e test * fix: window level presets hotkeys * Update README.md * update yarn lock * feat: volume api and TMTV mode (#2817) * feat: volumeAPI and TMTV mode * feat: use cs3d cache service to obtain viewportData * wip for volume api * wip: fusion viewport * feat: add blend mode option * wip for image scrollbar * fix drag and drop thumbnail * wip for image scrollbar for voluems * fix: element mismatch bug for scroll * feat: Add image scrollbar to volumes * feat: add syncGroups to volume api * feat: add tmtv mode initial setup * feat: add initial image options for the stack viewports * feat: Add custom load strategy for volume viewports via HP * apply review comments fix: Jump presets cs3d (#2812) * feat: Add JumpPreset to OHIF for Cornerstone3D * fix: accessing viewport service from servicesManager feat: volume API and TMTV mode (#2814) * fix: do not display overlays on mip viewports * feat: add optional disableCommands for toggle buttons * fix: toggleCrossharis * feat: config the crosshairs * feat: add PetSUV Panel for changing metadata * feat: initial work for the rectangleROIThreshold panel * wip: measurement service * roi threshold working * feat: Add displayText to segmentations * feat: add remove segmentation * feat: add csv export * feat: add RT export for annotations in tmtv mode * fix: fusion to use pt in tmtv mode and measuremet mappings * fix: various bugs * apply review comments * apply review comments * feat: add fusion color maps * add readme to tmtv mode * Update README.md * fix: try to fix build * fix unit tests * Update README.md * feat: add about to the panel * fix: changing strategy in roi panel * apply review comments * update package versions * wip for stackPrefetch * fix: cornerstone3d hydration and renaming (#2818) * update readme * renamed cornerstone extension * wip for renaming cornerstone3D variables * wip for renaming cornerstone3D variables * wip for fixing bugs for SR viewport * fix: jumpToMeasurement and initial label after hydration * fix: fileName capitalization * feat: use the new prefetch stack in the cs3d (#2820) * feat: use the new prefetch stack in the cs3d * use viewport scroll api for stack viewport * fix cine stop when scrollbar changes * feat: use new prefetch events * fix loading state to not show repeatedly * fix: various bugs for tmtv mode thresholding and new icons (#2823) * feat: make tmtv mode available in worklist * feat: add new icons for tmtv mode * feat: add fusion color icon * fix: parallel scale calculation * fix: bump Cornerstone to get large image support working * fix: Fix issues with CPU viewport flipping, including config files * fix: bump cornerstone to fix magnify tool * fix: Bump Cornerstone version to fix resetCamera issue in StackViewport * ci: Add _headers file to enable CORS headers for Netlify Drag/Drop deploys * fix: WADO-URI was not working. PET Metadata was coming from the wrong place. Fixed some minor React errors * feat(OHIF):Allow modes and extensions to be added after commpile time. (#2838) Also works with the compile time add that the existing cli uses, so that both build types work. Eric and I agreed this doesn't change existing functionality, but is almost entirely build issues/fixes. * bump: dependency versions to fix hydration bugs (#2848) * bump: dcmjs version to fix hydration bugs * try to fix tests * bump dependency versions Co-authored-by: Alireza <ar.sedghi@gmail.com> Co-authored-by: Bill Wallace <wayfarer3130@gmail.com>
This commit is contained in:
668 files changed
+41669
-13410
No files matched your search
@@ -18,16 +18,8 @@ import log from '../log.js';
|
||||
* to extend this class, please check it's source before adding new methods.
|
||||
*/
|
||||
export class CommandsManager {
|
||||
constructor({ getActiveContexts } = {}) {
|
||||
constructor({} = {}) {
|
||||
this.contexts = {};
|
||||
|
||||
if (!getActiveContexts) {
|
||||
throw new Error(
|
||||
'CommandsManager was instantiated without getActiveContexts()'
|
||||
);
|
||||
}
|
||||
|
||||
this._getActiveContexts = getActiveContexts;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -122,12 +114,8 @@ export class CommandsManager {
|
||||
contexts.push(context);
|
||||
}
|
||||
} else {
|
||||
const activeContexts = this._getActiveContexts();
|
||||
activeContexts.forEach(activeContext => {
|
||||
const context = this.getContext(activeContext);
|
||||
if (context) {
|
||||
contexts.push(context);
|
||||
}
|
||||
Object.keys(this.contexts).forEach(contextName => {
|
||||
contexts.push(this.getContext(contextName));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,11 @@ describe('CommandsManager', () => {
|
||||
options: { passMeToCommandFn: ':wave:' },
|
||||
},
|
||||
commandsManagerConfig = {
|
||||
getActiveContexts: () => ['VIEWER', 'ACTIVE_VIEWER::CORNERSTONE'],
|
||||
getAppState: () => {
|
||||
return {
|
||||
viewers: 'Test',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -29,12 +33,6 @@ describe('CommandsManager', () => {
|
||||
expect(localCommandsManager.contexts).toEqual({});
|
||||
});
|
||||
|
||||
it('throws Error if instantiated without getActiveContexts', () => {
|
||||
expect(() => {
|
||||
new CommandsManager();
|
||||
}).toThrow(new Error('CommandsManager was instantiated without getActiveContexts()'));
|
||||
});
|
||||
|
||||
describe('createContext()', () => {
|
||||
it('creates a context', () => {
|
||||
commandsManager.createContext(contextName);
|
||||
|
||||
@@ -21,13 +21,13 @@ describe('HotkeysManager', () => {
|
||||
});
|
||||
it('has expected properties', () => {
|
||||
const allProperties = Object.keys(hotkeysManager);
|
||||
const expectedProprties = [
|
||||
const expectedProperties = [
|
||||
'hotkeyDefinitions',
|
||||
'hotkeyDefaults',
|
||||
'isEnabled',
|
||||
];
|
||||
|
||||
const containsAllExpectedProperties = expectedProprties.every(expected =>
|
||||
const containsAllExpectedProperties = expectedProperties.every(expected =>
|
||||
allProperties.includes(expected)
|
||||
);
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import queryString from 'query-string';
|
||||
import dicomParser from 'dicom-parser';
|
||||
import { imageIdToURI } from '../utils';
|
||||
import getPixelSpacingInformation from '../utils/metadataProvider/getPixelSpacingInformation';
|
||||
import DicomMetadataStore from '../services/DicomMetadataStore';
|
||||
import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData';
|
||||
import toNumber from '../utils/toNumber';
|
||||
|
||||
class MetadataProvider {
|
||||
constructor() {
|
||||
@@ -13,7 +15,17 @@ class MetadataProvider {
|
||||
writable: false,
|
||||
value: new Map(),
|
||||
});
|
||||
Object.defineProperty(this, 'imageIdToUIDs', {
|
||||
Object.defineProperty(this, 'imageURIToUIDs', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: new Map(),
|
||||
});
|
||||
// Can be used to store custom metadata for a specific type.
|
||||
// For instance, the scaling metadata for PET can be stored here
|
||||
// as type "scalingModule"
|
||||
//
|
||||
Object.defineProperty(this, 'customMetadata', {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
@@ -21,13 +33,21 @@ class MetadataProvider {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
addImageIdToUIDs(imageId, uids) {
|
||||
// This method is a fallback for when you don't have WADO-URI or WADO-RS.
|
||||
// You can add instances fetched by any method by calling addInstance, and hook an imageId to point at it here.
|
||||
// An example would be dicom hosted at some random site.
|
||||
const imageURI = imageIdToURI(imageId);
|
||||
this.imageURIToUIDs.set(imageURI, uids);
|
||||
}
|
||||
|
||||
this.imageIdToUIDs.set(imageId, uids);
|
||||
addCustomMetadata(imageId, type, metadata) {
|
||||
const imageURI = imageIdToURI(imageId);
|
||||
if (!this.customMetadata.has(type)) {
|
||||
this.customMetadata.set(type, {});
|
||||
}
|
||||
|
||||
this.customMetadata.get(type)[imageURI] = metadata;
|
||||
}
|
||||
|
||||
_getInstance(imageId) {
|
||||
@@ -53,6 +73,15 @@ class MetadataProvider {
|
||||
return instance;
|
||||
}
|
||||
|
||||
// check inside custom metadata
|
||||
if (this.customMetadata.has(query)) {
|
||||
const customMetadata = this.customMetadata.get(query);
|
||||
const imageURI = imageIdToURI(imageId);
|
||||
if (customMetadata[imageURI]) {
|
||||
return customMetadata[imageURI];
|
||||
}
|
||||
}
|
||||
|
||||
return this.getTagFromInstance(query, instance, options);
|
||||
}
|
||||
|
||||
@@ -106,7 +135,7 @@ class MetadataProvider {
|
||||
metadata = {
|
||||
modality: instance.Modality,
|
||||
seriesInstanceUID: instance.SeriesInstanceUID,
|
||||
seriesNumber: validNumber(instance.SeriesNumber),
|
||||
seriesNumber: toNumber(instance.SeriesNumber),
|
||||
studyInstanceUID: instance.StudyInstanceUID,
|
||||
seriesDate,
|
||||
seriesTime,
|
||||
@@ -114,9 +143,14 @@ class MetadataProvider {
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.PATIENT_STUDY_MODULE:
|
||||
metadata = {
|
||||
patientAge: validNumber(instance.PatientAge),
|
||||
patientSize: validNumber(instance.PatientSize),
|
||||
patientWeight: validNumber(instance.PatientWeight),
|
||||
patientAge: toNumber(instance.PatientAge),
|
||||
patientSize: toNumber(instance.PatientSize),
|
||||
patientWeight: toNumber(instance.PatientWeight),
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.PATIENT_DEMOGRAPHIC_MODULE:
|
||||
metadata = {
|
||||
patientSex: instance.PatientSex,
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.IMAGE_PLANE_MODULE:
|
||||
@@ -145,50 +179,56 @@ class MetadataProvider {
|
||||
|
||||
metadata = {
|
||||
frameOfReferenceUID: instance.FrameOfReferenceUID,
|
||||
rows: validNumber(instance.Rows),
|
||||
columns: validNumber(instance.Columns),
|
||||
imageOrientationPatient: validNumber(ImageOrientationPatient),
|
||||
rowCosines: validNumber(rowCosines),
|
||||
columnCosines: validNumber(columnCosines),
|
||||
imagePositionPatient: validNumber(instance.ImagePositionPatient),
|
||||
sliceThickness: validNumber(instance.SliceThickness),
|
||||
sliceLocation: validNumber(instance.SliceLocation),
|
||||
pixelSpacing: validNumber(PixelSpacing),
|
||||
rowPixelSpacing: validNumber(rowPixelSpacing),
|
||||
columnPixelSpacing: validNumber(columnPixelSpacing),
|
||||
rows: toNumber(instance.Rows),
|
||||
columns: toNumber(instance.Columns),
|
||||
imageOrientationPatient: toNumber(ImageOrientationPatient),
|
||||
rowCosines: toNumber(rowCosines),
|
||||
columnCosines: toNumber(columnCosines),
|
||||
imagePositionPatient: toNumber(instance.ImagePositionPatient),
|
||||
sliceThickness: toNumber(instance.SliceThickness),
|
||||
sliceLocation: toNumber(instance.SliceLocation),
|
||||
pixelSpacing: toNumber(PixelSpacing),
|
||||
rowPixelSpacing: toNumber(rowPixelSpacing),
|
||||
columnPixelSpacing: toNumber(columnPixelSpacing),
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.IMAGE_PIXEL_MODULE:
|
||||
metadata = {
|
||||
samplesPerPixel: validNumber(instance.SamplesPerPixel),
|
||||
samplesPerPixel: toNumber(instance.SamplesPerPixel),
|
||||
photometricInterpretation: instance.PhotometricInterpretation,
|
||||
rows: validNumber(instance.Rows),
|
||||
columns: validNumber(instance.Columns),
|
||||
bitsAllocated: validNumber(instance.BitsAllocated),
|
||||
bitsStored: validNumber(instance.BitsStored),
|
||||
highBit: validNumber(instance.HighBit),
|
||||
pixelRepresentation: validNumber(instance.PixelRepresentation),
|
||||
planarConfiguration: validNumber(instance.PlanarConfiguration),
|
||||
pixelAspectRatio: validNumber(instance.PixelAspectRatio),
|
||||
smallestPixelValue: validNumber(instance.SmallestPixelValue),
|
||||
largestPixelValue: validNumber(instance.LargestPixelValue),
|
||||
redPaletteColorLookupTableDescriptor: validNumber(
|
||||
rows: toNumber(instance.Rows),
|
||||
columns: toNumber(instance.Columns),
|
||||
bitsAllocated: toNumber(instance.BitsAllocated),
|
||||
bitsStored: toNumber(instance.BitsStored),
|
||||
highBit: toNumber(instance.HighBit),
|
||||
pixelRepresentation: toNumber(instance.PixelRepresentation),
|
||||
planarConfiguration: toNumber(instance.PlanarConfiguration),
|
||||
pixelAspectRatio: toNumber(instance.PixelAspectRatio),
|
||||
smallestPixelValue: toNumber(instance.SmallestPixelValue),
|
||||
largestPixelValue: toNumber(instance.LargestPixelValue),
|
||||
redPaletteColorLookupTableDescriptor: toNumber(
|
||||
instance.RedPaletteColorLookupTableDescriptor
|
||||
),
|
||||
greenPaletteColorLookupTableDescriptor: validNumber(
|
||||
greenPaletteColorLookupTableDescriptor: toNumber(
|
||||
instance.GreenPaletteColorLookupTableDescriptor
|
||||
),
|
||||
bluePaletteColorLookupTableDescriptor: validNumber(
|
||||
bluePaletteColorLookupTableDescriptor: toNumber(
|
||||
instance.BluePaletteColorLookupTableDescriptor
|
||||
),
|
||||
redPaletteColorLookupTableData: fetchPaletteColorLookupTableData(
|
||||
instance, "RedPaletteColorLookupTableData", "RedPaletteColorLookupTableDescriptor"
|
||||
instance,
|
||||
'RedPaletteColorLookupTableData',
|
||||
'RedPaletteColorLookupTableDescriptor'
|
||||
),
|
||||
greenPaletteColorLookupTableData: fetchPaletteColorLookupTableData(
|
||||
instance, "GreenPaletteColorLookupTableData", "GreenPaletteColorLookupTableDescriptor"
|
||||
instance,
|
||||
'GreenPaletteColorLookupTableData',
|
||||
'GreenPaletteColorLookupTableDescriptor'
|
||||
),
|
||||
bluePaletteColorLookupTableData: fetchPaletteColorLookupTableData(
|
||||
instance, "BluePaletteColorLookupTableData", "BluePaletteColorLookupTableDescriptor"
|
||||
instance,
|
||||
'BluePaletteColorLookupTableData',
|
||||
'BluePaletteColorLookupTableDescriptor'
|
||||
),
|
||||
};
|
||||
|
||||
@@ -206,8 +246,8 @@ class MetadataProvider {
|
||||
: [WindowWidth];
|
||||
|
||||
metadata = {
|
||||
windowCenter: validNumber(windowCenter),
|
||||
windowWidth: validNumber(windowWidth),
|
||||
windowCenter: toNumber(windowCenter),
|
||||
windowWidth: toNumber(windowWidth),
|
||||
};
|
||||
|
||||
break;
|
||||
@@ -218,8 +258,8 @@ class MetadataProvider {
|
||||
}
|
||||
|
||||
metadata = {
|
||||
rescaleIntercept: validNumber(instance.RescaleIntercept),
|
||||
rescaleSlope: validNumber(instance.RescaleSlope),
|
||||
rescaleIntercept: toNumber(instance.RescaleIntercept),
|
||||
rescaleSlope: toNumber(instance.RescaleSlope),
|
||||
rescaleType: instance.RescaleType,
|
||||
};
|
||||
break;
|
||||
@@ -331,7 +371,7 @@ class MetadataProvider {
|
||||
case WADO_IMAGE_LOADER_TAGS.GENERAL_IMAGE_MODULE:
|
||||
metadata = {
|
||||
sopInstanceUid: instance.SOPInstanceUID,
|
||||
instanceNumber: validNumber(instance.InstanceNumber),
|
||||
instanceNumber: toNumber(instance.InstanceNumber),
|
||||
lossyImageCompression: instance.LossyImageCompression,
|
||||
lossyImageCompressionRatio: instance.LossyImageCompressionRatio,
|
||||
lossyImageCompressionMethod: instance.LossyImageCompressionMethod,
|
||||
@@ -361,7 +401,14 @@ class MetadataProvider {
|
||||
}
|
||||
|
||||
_getUIDsFromImageID(imageId) {
|
||||
if (imageId.includes('wadors:')) {
|
||||
// TODO: adding csiv here is not really correct. Probably need to use
|
||||
// metadataProvider.addImageIdToUIDs(imageId, {
|
||||
// StudyInstanceUID,
|
||||
// SeriesInstanceUID,
|
||||
// SOPInstanceUID,
|
||||
// })
|
||||
// somewhere else
|
||||
if (imageId.startsWith('wadors:')) {
|
||||
const strippedImageId = imageId.split('/studies/')[1];
|
||||
const splitImageId = strippedImageId.split('/');
|
||||
|
||||
@@ -381,26 +428,22 @@ class MetadataProvider {
|
||||
}
|
||||
|
||||
// Maybe its a non-standard imageId
|
||||
return this.imageIdToUIDs.get(imageId);
|
||||
// check if the imageId starts with http:// or https:// using regex
|
||||
// Todo: handle non http imageIds
|
||||
let imageURI;
|
||||
const urlRegex = /^(http|https):\/\//;
|
||||
if (urlRegex.test(imageId)) {
|
||||
imageURI = imageId;
|
||||
} else {
|
||||
imageURI = imageIdToURI(imageId);
|
||||
}
|
||||
|
||||
return this.imageURIToUIDs.get(imageURI);
|
||||
}
|
||||
}
|
||||
|
||||
const metadataProvider = new MetadataProvider();
|
||||
|
||||
/**
|
||||
* Returns the values as an array of javascript numbers
|
||||
*
|
||||
* @param element - The javascript object for the specified element in the metadata
|
||||
* @returns {*}
|
||||
*/
|
||||
const validNumber = val => {
|
||||
if (Array.isArray(val)) {
|
||||
return val.map(v => (v !== undefined ? Number(v) : v));
|
||||
} else {
|
||||
return val !== undefined ? Number(val) : val;
|
||||
}
|
||||
};
|
||||
|
||||
export default metadataProvider;
|
||||
|
||||
const WADO_IMAGE_LOADER_TAGS = {
|
||||
@@ -414,6 +457,7 @@ const WADO_IMAGE_LOADER_TAGS = {
|
||||
SOP_COMMON_MODULE: 'sopCommonModule',
|
||||
PET_ISOTOPE_MODULE: 'petIsotopeModule',
|
||||
OVERLAY_PLANE_MODULE: 'overlayPlaneModule',
|
||||
PATIENT_DEMOGRAPHIC_MODULE: 'patientDemographicModule',
|
||||
|
||||
// react-cornerstone-viewport specifc
|
||||
PATIENT_MODULE: 'patientModule',
|
||||
|
||||
Reference in new issue
Block a user