feat(hp): Add displayArea option for Hanging protocols and example with Mamo(#3808)

This commit is contained in:
Bill Wallace 2024-05-29 11:31:09 -04:00 committed by GitHub
parent cb30b97610
commit 18ac08ed86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
28 changed files with 771 additions and 27 deletions

View File

@ -51,7 +51,6 @@ provided by the <a href="https://ohif.org/">Open Health Imaging Foundation (OHIF
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-video.webp?raw=true" alt="VIDEO" width="350"/> | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) | | <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/demo-video.webp?raw=true" alt="VIDEO" width="350"/> | Video | [Demo](https://viewer.ohif.org/viewer?StudyInstanceUIDs=2.25.96975534054447904995905761963464388233) |
| <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/microscopy.webp?raw=true" alt="microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) | | <img src="https://github.com/OHIF/Viewers/blob/master/platform/docs/docs/assets/img/microscopy.webp?raw=true" alt="microscopy" width="350"/> | Slide Microscopy | [Demo](https://viewer.ohif.org/microscopy?StudyInstanceUIDs=2.25.141277760791347900862109212450152067508) |
## About ## About
The OHIF Viewer can retrieve The OHIF Viewer can retrieve

View File

@ -32,6 +32,7 @@ module.exports = {
'@babel/transform-destructuring', '@babel/transform-destructuring',
'@babel/plugin-transform-runtime', '@babel/plugin-transform-runtime',
'@babel/plugin-transform-typescript', '@babel/plugin-transform-typescript',
'@babel/plugin-transform-class-static-block',
], ],
}, },
production: { production: {

View File

@ -573,6 +573,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
initialImageIndexToUse = this._getInitialImageIndexForViewport(viewportInfo, imageIds) || 0; initialImageIndexToUse = this._getInitialImageIndexForViewport(viewportInfo, imageIds) || 0;
} }
const { rotation, flipHorizontal, displayArea } = viewportInfo.getViewportOptions();
const properties = { ...presentations.lutPresentation?.properties }; const properties = { ...presentations.lutPresentation?.properties };
if (!presentations.lutPresentation?.properties) { if (!presentations.lutPresentation?.properties) {
const { voi, voiInverted, colormap } = displaySetOptions[0]; const { voi, voiInverted, colormap } = displaySetOptions[0];
@ -598,6 +600,15 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
return viewport.setStack(imageIds, initialImageIndexToUse).then(() => { return viewport.setStack(imageIds, initialImageIndexToUse).then(() => {
viewport.setProperties({ ...properties }); viewport.setProperties({ ...properties });
this.setPresentations(viewport.id, presentations); this.setPresentations(viewport.id, presentations);
if (displayArea) {
viewport.setDisplayArea(displayArea);
}
if (rotation) {
viewport.setProperties({ rotation });
}
if (flipHorizontal) {
viewport.setCamera({ flipHorizontal: true });
}
}); });
} }

View File

@ -41,7 +41,7 @@
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-i18next": "^12.2.2", "react-i18next": "^12.2.2",
"react-window": "^1.8.9", "react-window": "^1.8.9",
"webpack": "^5.50.0", "webpack": "^5.89.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
}, },
"dependencies": { "dependencies": {

View File

@ -1,5 +1,7 @@
import hpMNGrid from './hpMNGrid'; import hpMNGrid from './hangingprotocols/hpMNGrid';
import hpMNCompare from './hpCompare'; import hpMNCompare from './hangingprotocols/hpCompare';
import hpMammography from './hangingprotocols/hpMammo';
import hpScale from './hangingprotocols/hpScale';
const defaultProtocol = { const defaultProtocol = {
id: 'default', id: 'default',
@ -114,6 +116,14 @@ function getHangingProtocolModule() {
name: hpMNCompare.id, name: hpMNCompare.id,
protocol: hpMNCompare, protocol: hpMNCompare,
}, },
{
name: hpMammography.id,
protocol: hpMammography,
},
{
name: hpScale.id,
protocol: hpScale,
},
]; ];
} }

View File

@ -120,9 +120,9 @@ const hpMNCompare: Types.HangingProtocol.Protocol = {
{ {
id: 'Two Studies', id: 'Two Studies',
weight: 1000, weight: 1000,
// is there a second study or in another work the attribute
// studyInstanceUIDsIndex that we get from prior should not be null
attribute: 'StudyInstanceUID', attribute: 'StudyInstanceUID',
// The 'from' attribute says where to get the 'attribute' value from. In this case
// prior means the second study in the study list.
from: 'prior', from: 'prior',
required: true, required: true,
constraint: { constraint: {

View File

@ -0,0 +1,201 @@
import {
RCC,
RMLO,
LCC,
LMLO,
RCCPrior,
LCCPrior,
RMLOPrior,
LMLOPrior,
} from './mammoDisplaySetSelector';
const rightDisplayArea = {
storeAsInitialCamera: true,
imageArea: [0.8, 0.8],
imageCanvasPoint: {
imagePoint: [0, 0.5],
canvasPoint: [0, 0.5],
},
};
const leftDisplayArea = {
storeAsInitialCamera: true,
imageArea: [0.8, 0.8],
imageCanvasPoint: {
imagePoint: [1, 0.5],
canvasPoint: [1, 0.5],
},
};
const hpMammography = {
id: '@ohif/hpMammo',
hasUpdatedPriorsInformation: false,
name: 'Mammography Breast Screening',
protocolMatchingRules: [
{
id: 'Mammography',
weight: 150,
attribute: 'ModalitiesInStudy',
constraint: {
contains: 'MG',
},
required: true,
},
{
id: 'numberOfImages',
attribute: 'numberOfDisplaySetsWithImages',
constraint: {
greaterThan: 2,
},
required: true,
},
],
toolGroupIds: ['default'],
displaySetSelectors: {
RCC,
LCC,
RMLO,
LMLO,
RCCPrior,
LCCPrior,
RMLOPrior,
LMLOPrior,
},
stages: [
{
name: 'CC/MLO',
viewportStructure: {
type: 'grid',
layoutType: 'grid',
properties: {
rows: 2,
columns: 2,
},
},
viewports: [
{
viewportOptions: {
toolGroupId: 'default',
displayArea: leftDisplayArea,
// flipHorizontal: true,
// rotation: 180,
allowUnmatchedView: true,
},
displaySets: [
{
id: 'RCC',
},
],
},
{
viewportOptions: {
toolGroupId: 'default',
// flipHorizontal: true,
displayArea: rightDisplayArea,
allowUnmatchedView: true,
},
displaySets: [
{
id: 'LCC',
},
],
},
{
viewportOptions: {
toolGroupId: 'default',
displayArea: leftDisplayArea,
// rotation: 180,
// flipHorizontal: true,
allowUnmatchedView: true,
},
displaySets: [
{
id: 'RMLO',
},
],
},
{
viewportOptions: {
toolGroupId: 'default',
displayArea: rightDisplayArea,
// flipHorizontal: true,
allowUnmatchedView: true,
},
displaySets: [
{
id: 'LMLO',
},
],
},
],
},
// Compare CC current/prior top/bottom
{
name: 'CC compare',
viewportStructure: {
type: 'grid',
layoutType: 'grid',
properties: {
rows: 2,
columns: 2,
},
},
viewports: [
{
viewportOptions: {
toolGroupId: 'default',
displayArea: leftDisplayArea,
flipHorizontal: true,
rotation: 180,
},
displaySets: [
{
id: 'RCC',
},
],
},
{
viewportOptions: {
toolGroupId: 'default',
flipHorizontal: true,
displayArea: rightDisplayArea,
},
displaySets: [
{
id: 'LCC',
},
],
},
{
viewportOptions: {
toolGroupId: 'default',
displayArea: leftDisplayArea,
flipHorizontal: true,
},
displaySets: [
{
id: 'RCCPrior',
},
],
},
{
viewportOptions: {
toolGroupId: 'default',
displayArea: rightDisplayArea,
},
displaySets: [
{
id: 'LCCPrior',
},
],
},
],
},
],
// Indicates it is prior aware, but will work with no priors
numberOfPriorsReferenced: 0,
};
export default hpMammography;

View File

@ -0,0 +1,131 @@
import { Types } from '@ohif/core';
const displayAreaScale1: Types.HangingProtocol.DisplayArea = {
type: 'SCALE',
scale: 1,
storeAsInitialCamera: true,
};
const displayAreaScale15: Types.HangingProtocol.DisplayArea = { ...displayAreaScale1, scale: 15 };
/**
* This hanging protocol can be activated on the primary mode by directly
* referencing it in a URL or by directly including it within a mode, e.g.:
* `&hangingProtocolId=@ohif/mnGrid` added to the viewer URL
* It is not included in the viewer mode by default.
*/
const hpScale: Types.HangingProtocol.Protocol = {
id: '@ohif/hpScale',
description: 'Has various hanging protocol grid layouts',
name: 'Scale Images',
protocolMatchingRules: [
{
id: 'OneOrMoreSeries',
weight: 25,
attribute: 'numberOfDisplaySetsWithImages',
constraint: {
greaterThan: 0,
},
},
],
toolGroupIds: ['default'],
displaySetSelectors: {
defaultDisplaySetId: {
seriesMatchingRules: [
{
attribute: 'numImageFrames',
constraint: {
greaterThan: { value: 0 },
},
required: true,
},
// This display set will select the specified items by preference
// It has no affect if nothing is specified in the URL.
{
attribute: 'isDisplaySetFromUrl',
weight: 10,
constraint: {
equals: true,
},
},
],
},
},
defaultViewport: {
viewportOptions: {
viewportType: 'stack',
toolGroupId: 'default',
displayArea: displayAreaScale1,
allowUnmatchedView: true,
},
displaySets: [
{
id: 'defaultDisplaySetId',
matchedDisplaySetsIndex: -1,
},
],
},
stages: [
// A 1x1 stage - should be automatically activated if there is only 1 viewable instance
{
name: 'Scale 1:1',
stageActivation: {
enabled: {
minViewportsMatched: 1,
},
},
viewportStructure: {
layoutType: 'grid',
properties: {
rows: 1,
columns: 1,
},
},
viewports: [
{
viewportOptions: {
toolGroupId: 'default',
allowUnmatchedView: true,
displayArea: displayAreaScale1,
},
displaySets: [
{
id: 'defaultDisplaySetId',
},
],
},
],
},
{
name: 'Scale 1:15',
stageActivation: {
enabled: {
minViewportsMatched: 1,
},
},
viewportStructure: {
layoutType: 'grid',
properties: {
rows: 1,
columns: 1,
},
},
viewports: [
{
viewportOptions: {
toolGroupId: 'default',
allowUnmatchedView: true,
displayArea: displayAreaScale15,
},
displaySets: [
{
id: 'defaultDisplaySetId',
},
],
},
],
},
],
numberOfPriorsReferenced: -1,
};
export default hpScale;

View File

@ -0,0 +1,15 @@
import viewCodeAttribute from './viewCode';
import lateralityAttribute from './laterality';
import registerHangingProtocolAttributes from './registerHangingProtocolAttributes';
import hpMammography from './hpMammo';
import hpMNGrid from './hpMNGrid';
import hpCompare from './hpCompare';
export {
viewCodeAttribute,
lateralityAttribute,
hpMammography as hpMammo,
hpMNGrid,
hpCompare,
registerHangingProtocolAttributes,
};

View File

@ -0,0 +1,9 @@
export default displaySet => {
const frameAnatomy =
displaySet?.images?.[0]?.SharedFunctionalGroupsSequence?.[0]?.FrameAnatomySequence?.[0];
if (!frameAnatomy) {
return undefined;
}
const laterality = frameAnatomy?.FrameLaterality;
return laterality;
};

View File

@ -0,0 +1,214 @@
const priorStudyMatchingRules = [
{
// The priorInstance is a study counter that indicates what position this study is in
// and the value comes from the options parameter.
attribute: 'studyInstanceUIDsIndex',
from: 'options',
required: true,
constraint: {
equals: { value: 1 },
},
},
];
const currentStudyMatchingRules = [
{
// The priorInstance is a study counter that indicates what position this study is in
// and the value comes from the options parameter.
attribute: 'studyInstanceUIDsIndex',
from: 'options',
required: true,
constraint: {
equals: { value: 0 },
},
},
];
const LCCSeriesMatchingRules = [
{
weight: 10,
attribute: 'ViewCode',
constraint: {
contains: 'SCT:399162004',
},
},
{
weight: 5,
attribute: 'PatientOrientation',
constraint: {
contains: 'L',
},
},
{
weight: 20,
attribute: 'SeriesDescription',
constraint: {
contains: 'L CC',
},
},
];
const RCCSeriesMatchingRules = [
{
weight: 10,
attribute: 'ViewCode',
constraint: {
contains: 'SCT:399162004',
},
},
{
weight: 5,
attribute: 'PatientOrientation',
constraint: {
equals: ['P', 'L'],
},
},
{
attribute: 'PatientOrientation',
constraint: {
doesNotEqual: ['A', 'R'],
},
required: true,
},
{
weight: 20,
attribute: 'SeriesDescription',
constraint: {
contains: 'CC',
},
},
];
const LMLOSeriesMatchingRules = [
{
weight: 10,
attribute: 'ViewCode',
constraint: {
contains: 'SCT:399368009',
},
},
{
weight: 0,
attribute: 'ViewCode',
constraint: {
doesNotEqual: 'SCT:399162004',
},
required: true,
},
{
weight: 5,
attribute: 'PatientOrientation',
constraint: {
equals: ['A', 'R'],
},
},
{
weight: 20,
attribute: 'SeriesDescription',
constraint: {
contains: 'L MLO',
},
},
];
const RMLOSeriesMatchingRules = [
{
weight: 10,
attribute: 'ViewCode',
constraint: {
contains: 'SCT:399368009',
},
},
{
attribute: 'ViewCode',
constraint: {
doesNotEqual: 'SCT:399162004',
},
required: true,
},
{
attribute: 'PatientOrientation',
constraint: {
doesNotContain: ['P', 'FL'],
},
required: true,
},
{
weight: 5,
attribute: 'PatientOrientation',
constraint: {
equals: ['P', 'L'],
},
},
{
weight: 5,
attribute: 'PatientOrientation',
constraint: {
equals: ['A', 'FR'],
},
},
{
weight: 20,
attribute: 'SeriesDescription',
constraint: {
contains: 'R MLO',
},
},
{
attribute: 'SeriesDescription',
required: true,
constraint: {
doesNotContain: 'CC',
},
},
{
attribute: 'SeriesDescription',
required: true,
constraint: {
doesNotEqual: 'L MLO',
},
required: true,
},
];
const RCC = {
seriesMatchingRules: RCCSeriesMatchingRules,
studyMatchingRules: currentStudyMatchingRules,
};
const RCCPrior = {
seriesMatchingRules: RCCSeriesMatchingRules,
studyMatchingRules: priorStudyMatchingRules,
};
const LCC = {
seriesMatchingRules: LCCSeriesMatchingRules,
studyMatchingRules: currentStudyMatchingRules,
};
const LCCPrior = {
seriesMatchingRules: LCCSeriesMatchingRules,
studyMatchingRules: priorStudyMatchingRules,
};
const RMLO = {
seriesMatchingRules: RMLOSeriesMatchingRules,
studyMatchingRules: currentStudyMatchingRules,
};
const RMLOPrior = {
seriesMatchingRules: RMLOSeriesMatchingRules,
studyMatchingRules: priorStudyMatchingRules,
};
const LMLO = {
seriesMatchingRules: LMLOSeriesMatchingRules,
studyMatchingRules: currentStudyMatchingRules,
};
const LMLOPrior = {
seriesMatchingRules: LMLOSeriesMatchingRules,
studyMatchingRules: priorStudyMatchingRules,
};
export { RCC, LCC, RMLO, LMLO, RCCPrior, LCCPrior, RMLOPrior, LMLOPrior };

View File

@ -0,0 +1,8 @@
import viewCode from './viewCode';
import laterality from './laterality';
export default function registerHangingProtocolAttributes({ servicesManager }) {
const { hangingProtocolService } = servicesManager.services;
hangingProtocolService.addCustomAttribute('ViewCode', 'View Code Designator:Value', viewCode);
hangingProtocolService.addCustomAttribute('Laterality', 'Laterality of object', laterality);
}

View File

@ -0,0 +1,11 @@
export default displaySet => {
const ViewCodeSequence = displaySet?.images[0]?.ViewCodeSequence[0];
if (!ViewCodeSequence) {
return undefined;
}
const { CodingSchemeDesignator, CodeValue } = ViewCodeSequence;
if (!CodingSchemeDesignator || !CodeValue) {
return undefined;
}
return `${CodingSchemeDesignator}:${CodeValue}`;
};

View File

@ -2,6 +2,7 @@ import { DicomMetadataStore, classes } from '@ohif/core';
import { calculateSUVScalingFactors } from '@cornerstonejs/calculate-suv'; import { calculateSUVScalingFactors } from '@cornerstonejs/calculate-suv';
import getPTImageIdInstanceMetadata from './getPTImageIdInstanceMetadata'; import getPTImageIdInstanceMetadata from './getPTImageIdInstanceMetadata';
import { registerHangingProtocolAttributes } from './hangingprotocols';
const metadataProvider = classes.MetadataProvider; const metadataProvider = classes.MetadataProvider;
@ -60,6 +61,9 @@ export default function init({
// afterwards. // afterwards.
stateSyncService.register('viewportsByPosition', { clearOnModeExit: true }); stateSyncService.register('viewportsByPosition', { clearOnModeExit: true });
// Adds extra custom attributes for use by hanging protocols
registerHangingProtocolAttributes({ servicesManager });
// Function to process and subscribe to events for a given set of commands and listeners // Function to process and subscribe to events for a given set of commands and listeners
const subscribeToEvents = listeners => { const subscribeToEvents = listeners => {
Object.entries(listeners).forEach(([event, commands]) => { Object.entries(listeners).forEach(([event, commands]) => {

View File

@ -43,7 +43,7 @@
"i18next": "^17.0.3" "i18next": "^17.0.3"
}, },
"devDependencies": { "devDependencies": {
"webpack": "^5.50.0", "webpack": "^5.89.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
} }
} }

View File

@ -48,7 +48,7 @@
"i18next": "^17.0.3" "i18next": "^17.0.3"
}, },
"devDependencies": { "devDependencies": {
"webpack": "^5.50.0", "webpack": "^5.89.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
} }
} }

View File

@ -49,7 +49,7 @@
"i18next": "^17.0.3" "i18next": "^17.0.3"
}, },
"devDependencies": { "devDependencies": {
"webpack": "^5.50.0", "webpack": "^5.89.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
} }
} }

View File

@ -70,7 +70,7 @@
"dotenv": "^14.1.0", "dotenv": "^14.1.0",
"eslint": "^5.0.1", "eslint": "^5.0.1",
"eslint-loader": "^2.0.0", "eslint-loader": "^2.0.0",
"webpack": "^5.50.0", "webpack": "^5.89.0",
"webpack-cli": "^4.7.2", "webpack-cli": "^4.7.2",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
} }

View File

@ -47,7 +47,7 @@
"i18next": "^17.0.3" "i18next": "^17.0.3"
}, },
"devDependencies": { "devDependencies": {
"webpack": "^5.50.0", "webpack": "^5.89.0",
"webpack-merge": "^5.7.3" "webpack-merge": "^5.7.3"
} }
} }

View File

@ -20,6 +20,7 @@ const PUBLIC_URL = process.env.PUBLIC_URL || '/';
const APP_CONFIG = process.env.APP_CONFIG || 'config/default.js'; const APP_CONFIG = process.env.APP_CONFIG || 'config/default.js';
const PROXY_TARGET = process.env.PROXY_TARGET; const PROXY_TARGET = process.env.PROXY_TARGET;
const PROXY_DOMAIN = process.env.PROXY_DOMAIN; const PROXY_DOMAIN = process.env.PROXY_DOMAIN;
const OHIF_PORT = Number(process.env.OHIF_PORT || 3000);
const ENTRY_TARGET = process.env.ENTRY_TARGET || `${SRC_DIR}/index.js`; const ENTRY_TARGET = process.env.ENTRY_TARGET || `${SRC_DIR}/index.js`;
const Dotenv = require('dotenv-webpack'); const Dotenv = require('dotenv-webpack');
const writePluginImportFile = require('./writePluginImportsFile.js'); const writePluginImportFile = require('./writePluginImportsFile.js');
@ -143,7 +144,7 @@ module.exports = (env, argv) => {
// http2: true, // http2: true,
// https: true, // https: true,
open: true, open: true,
port: 3000, port: OHIF_PORT,
client: { client: {
overlay: { errors: true, warnings: false }, overlay: { errors: true, warnings: false },
}, },

View File

@ -0,0 +1,86 @@
import { utilities } from '@cornerstonejs/core';
/**
* Add tests to ensure image consistency and quality
*/
const testPixel = (dx, dy, expectedPixel) => {
cy.get('.cornerstone-canvas').then(v => {
const canvas = v[0];
cy.log(
'testPixel canvas',
dx,
dy,
expectedPixel,
canvas.width,
canvas.height,
canvas.style.width,
canvas.style.height
);
const ctx = canvas.getContext('2d');
cy.window()
.its('cornerstone')
.then(cornerstone => {
const { viewport } = cornerstone.getEnabledElements()[0];
const imageData = viewport.getImageData();
// cy.log("imageData", imageData);
const origin = viewport.worldToCanvas(imageData.origin);
const orX = origin[0] * devicePixelRatio;
const orY = origin[1] * devicePixelRatio;
const x = Math.round(orX + dx);
const y = Math.round(orY + dy);
cy.log('testPixel origin x,y point x,y', orX, orY, x, y);
// cy.log('world origin', imageData.origin);
// cy.log('focal', viewport.getCamera().focalPoint,
// viewport.worldToCanvas(viewport.getCamera().focalPoint));
const pixelData = ctx.getImageData(x, y, 1, 1);
expect(pixelData.data[0]).closeTo(expectedPixel, 1);
});
});
};
describe('CS3D Image Consistency and Quality', () => {
const setupStudySeries = (studyUID, seriesUID) => {
cy.checkStudyRouteInViewer(
studyUID,
`&seriesInstanceUID=${seriesUID}&hangingProtocolId=@ohif/hpScale`
);
cy.initCornerstoneToolsAliases();
cy.initCommonElementsAliases();
};
it('TG18 Resolution Test Displayed 1:1', () => {
setupStudySeries(
'2.16.124.113543.6004.101.103.20021117.061159.1',
'2.16.124.113543.6004.101.103.20021117.061159.1.004'
);
testPixel(1018, 1028, 255);
// Horizontal and vertical delta from this should not be contaminated
// by values from center
testPixel(1019, 1028, 0);
testPixel(1018, 1029, 0);
testPixel(1017, 1028, 0);
testPixel(1018, 1027, 0);
});
// Missing test data - todo
it.skip('8 bit image displayable', () => {
setupStudySeries('1.3.46.670589.17.1.7.1.1.7', '1.3.46.670589.17.1.7.2.1.7');
// Compare with dcm2jpg generated values or by manually computing WL values
testPixel(258, 257, 171);
testPixel(259, 257, 166);
});
it.skip('12 bit image displayable and zoom with pixel spacing', () => {
setupStudySeries(
'1.3.6.1.4.1.25403.345050719074.3824.20170125113417.1',
'1.3.6.1.4.1.25403.345050719074.3824.20170125113608.5'
);
// Compare with dcm2jpg generated values or by manually computing WL values
testPixel(258, 277, 120);
testPixel(259, 277, 122);
});
});

View File

@ -111,6 +111,34 @@ window.config = {
}, },
}, },
}, },
{
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
sourceName: 'dicomweb2',
configuration: {
friendlyName: 'AWS S3 Static wado secondary server',
name: 'aws',
wadoUriRoot: 'https://d28o5kq0jsoob5.cloudfront.net/dicomweb',
qidoRoot: 'https://d28o5kq0jsoob5.cloudfront.net/dicomweb',
wadoRoot: 'https://d28o5kq0jsoob5.cloudfront.net/dicomweb',
qidoSupportsIncludeField: false,
supportsReject: false,
imageRendering: 'wadors',
thumbnailRendering: 'wadors',
enableStudyLazyLoad: true,
supportsFuzzyMatching: false,
supportsWildcard: true,
staticWado: true,
singlepart: 'bulkdata,video',
// whether the data source should use retrieveBulkData to grab metadata,
// and in case of relative path, what would it be relative to, options
// are in the series level or study level (some servers like series some study)
bulkDataURI: {
enabled: true,
relativeResolution: 'studies',
},
omitQuotationForMultipartRequest: true,
},
},
{ {
friendlyName: 'StaticWado default data', friendlyName: 'StaticWado default data',
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb', namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',

View File

@ -24,6 +24,18 @@ export type DisplaySetAndViewportOptions = {
displaySetOptions: DisplaySetOptions; displaySetOptions: DisplaySetOptions;
}; };
export type DisplayArea = {
type?: 'SCALE' | 'FIT';
scale?: number;
interpolationType?: any;
imageArea?: [number, number]; // areaX, areaY
imageCanvasPoint?: {
imagePoint: [number, number]; // imageX, imageY
canvasPoint?: [number, number]; // canvasX, canvasY
};
storeAsInitialCamera?: boolean;
};
export type SetProtocolOptions = { export type SetProtocolOptions = {
/** Used to provide a mapping of what keys are provided for which viewport. /** Used to provide a mapping of what keys are provided for which viewport.
* For example, a Chest XRay might use have the display set selector id of * For example, a Chest XRay might use have the display set selector id of
@ -155,6 +167,7 @@ export type ViewportOptions = {
id?: string; id?: string;
orientation?: CustomOption<string>; orientation?: CustomOption<string>;
viewportId?: string; viewportId?: string;
displayArea?: DisplayArea;
initialImageOptions?: CustomOption<initialImageOptions>; initialImageOptions?: CustomOption<initialImageOptions>;
syncGroups?: CustomOption<SyncGroup>[]; syncGroups?: CustomOption<SyncGroup>[];
customViewportProps?: Record<string, unknown>; customViewportProps?: Record<string, unknown>;

View File

@ -293,6 +293,7 @@ alternative data source (or even specify different default hotkeys).
| `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` | | `APP_CONFIG` | Which [configuration file][config-file] to copy to output as `app-config.js` | `config/default.js` |
| `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` | | `PROXY_TARGET` | When developing, proxy requests that match this pattern to `PROXY_DOMAIN` | `undefined` |
| `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` | | `PROXY_DOMAIN` | When developing, proxy requests from `PROXY_TARGET` to `PROXY_DOMAIN` | `undefined` |
| `OHIF_PORT` | The port to run the webpack server on for PWA builds. | `3000` |
You can also create a new config file and specify its path relative to the build You can also create a new config file and specify its path relative to the build
output's root by setting the `APP_CONFIG` environment variable. You can set the output's root by setting the `APP_CONFIG` environment variable. You can set the

View File

@ -281,21 +281,22 @@ A list of criteria for the protocol along with the provided points for ranking.
}, },
``` ```
### `from` attribute ### `from` attribute (optional)
The from attribute allows getting the attribute to test from some other object The `from` attribute allows you to retrieve the attribute to test from another object, such as the previous study, the overall list of studies, or another provided value from a module.
such as the prior study, the list of studies overall or another module provided
value. Some of the possible attributes are:
* `prior`: To get the value from the prior study. The values provided by OHIF which you can use are:
* `activeStudy`: To match the active study
* `studies`: To match the list of studies to display
* `displaySets`: The display sets for the current study
* `allDisplaySets`: Alll available display sets
* `instance`: An instance from the current display set being tested
* `options`: Gets the options object itself, eg if you want a simple top level
value.
### displaySetSelectors - `activeStudy`: to use the metadata of the active study to match
- `studies`: to use the metadata of the list of studies (all studies) to match
- `allDisplaySets`: all available display sets
- `displaySets`: if the selector has matched a study, these are the display sets for that study
- `prior`: the metadata of the first study in the list of studies that is not the active study
- `options`: during matching, we also provide an options object with the following information that you can use as the `from` value:
- `studyInstanceUIDsIndex`: the index of the study in the list of studies
- `instance`: the metadata of the instance being matched, which is exactly the displaySet.instance metadata.
### displaySetSelectors (mandatory)
Defines the display sets that the protocol will use for arrangement. Defines the display sets that the protocol will use for arrangement.
```js ```js

View File

@ -60,7 +60,7 @@
"react-window": "^1.8.9", "react-window": "^1.8.9",
"react-with-direction": "^1.3.1", "react-with-direction": "^1.3.1",
"swiper": "^8.4.2", "swiper": "^8.4.2",
"webpack": "^5.81.0" "webpack": "^5.89.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.23.2", "@babel/core": "^7.23.2",

View File

@ -22557,7 +22557,7 @@ webpack-virtual-modules@^0.6.1:
resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz#ac6fdb9c5adb8caecd82ec241c9631b7a3681b6f" resolved "https://registry.yarnpkg.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.1.tgz#ac6fdb9c5adb8caecd82ec241c9631b7a3681b6f"
integrity sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg== integrity sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==
webpack@5, webpack@^5.50.0, webpack@^5.73.0, webpack@^5.81.0, webpack@^5.90.3: webpack@5, webpack@^5.50.0, webpack@^5.73.0, webpack@^5.89.0, webpack@^5.90.3:
version "5.91.0" version "5.91.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.91.0.tgz#ffa92c1c618d18c878f06892bbdc3373c71a01d9"
integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw== integrity sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==