StudyBrowser almost works
This commit is contained in:
parent
ec2df02e83
commit
70b7158c9b
@ -34,8 +34,8 @@
|
|||||||
"cornerstone-core": "^2.3.0",
|
"cornerstone-core": "^2.3.0",
|
||||||
"cornerstone-math": "^0.1.8",
|
"cornerstone-math": "^0.1.8",
|
||||||
"cornerstone-tools": "4.15.1",
|
"cornerstone-tools": "4.15.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
|
||||||
"dcmjs": "^0.12.3",
|
"dcmjs": "^0.12.3",
|
||||||
|
"cornerstone-wado-image-loader": "^3.1.2",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"hammerjs": "^2.0.8",
|
"hammerjs": "^2.0.8",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
|
|||||||
@ -12,6 +12,9 @@ const { setViewportSpecificData } = OHIF.redux.actions;
|
|||||||
|
|
||||||
const commandsModule = ({ servicesManager }) => {
|
const commandsModule = ({ servicesManager }) => {
|
||||||
const actions = {
|
const actions = {
|
||||||
|
getCornerstoneLibraries: () => {
|
||||||
|
return { cornerstone, cornerstoneTools };
|
||||||
|
},
|
||||||
rotateViewport: ({ viewports, rotation }) => {
|
rotateViewport: ({ viewports, rotation }) => {
|
||||||
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
const enabledElement = getEnabledElement(viewports.activeViewportIndex);
|
||||||
|
|
||||||
@ -262,23 +265,28 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
StudyInstanceUID,
|
StudyInstanceUID,
|
||||||
SOPInstanceUID,
|
SOPInstanceUID,
|
||||||
frameIndex,
|
frameIndex,
|
||||||
activeViewportIndex
|
activeViewportIndex,
|
||||||
}) => {
|
}) => {
|
||||||
const study = studyMetadataManager.get(StudyInstanceUID);
|
const study = studyMetadataManager.get(StudyInstanceUID);
|
||||||
|
|
||||||
const displaySet = study.findDisplaySet(ds => {
|
const displaySet = study.findDisplaySet(ds => {
|
||||||
return ds.images && ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
|
return (
|
||||||
|
ds.images &&
|
||||||
|
ds.images.find(i => i.getSOPInstanceUID() === SOPInstanceUID)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
displaySet.SOPInstanceUID = SOPInstanceUID;
|
displaySet.SOPInstanceUID = SOPInstanceUID;
|
||||||
displaySet.frameIndex = frameIndex;
|
displaySet.frameIndex = frameIndex;
|
||||||
|
|
||||||
window.store.dispatch(setViewportSpecificData(activeViewportIndex, displaySet));
|
window.store.dispatch(
|
||||||
|
setViewportSpecificData(activeViewportIndex, displaySet)
|
||||||
|
);
|
||||||
|
|
||||||
cornerstone.getEnabledElements().forEach(enabledElement => {
|
cornerstone.getEnabledElements().forEach(enabledElement => {
|
||||||
cornerstone.updateImage(enabledElement.element);
|
cornerstone.updateImage(enabledElement.element);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const definitions = {
|
const definitions = {
|
||||||
@ -287,6 +295,12 @@ const commandsModule = ({ servicesManager }) => {
|
|||||||
storeContexts: [],
|
storeContexts: [],
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
|
getCornerstoneLibraries: {
|
||||||
|
commandFn: actions.getCornerstoneLibraries,
|
||||||
|
storeContexts: [],
|
||||||
|
options: {},
|
||||||
|
context: 'VIEWER',
|
||||||
|
},
|
||||||
getNearbyToolData: {
|
getNearbyToolData: {
|
||||||
commandFn: actions.getNearbyToolData,
|
commandFn: actions.getNearbyToolData,
|
||||||
storeContexts: [],
|
storeContexts: [],
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import cornerstone from 'cornerstone-core';
|
|||||||
import csTools from 'cornerstone-tools';
|
import csTools from 'cornerstone-tools';
|
||||||
import merge from 'lodash.merge';
|
import merge from 'lodash.merge';
|
||||||
import initCornerstoneTools from './initCornerstoneTools.js';
|
import initCornerstoneTools from './initCornerstoneTools.js';
|
||||||
|
import initWADOImageLoader from './initWADOImageLoader.js';
|
||||||
import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory';
|
import measurementServiceMappingsFactory from './utils/measurementServiceMappings/measurementServiceMappingsFactory';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
18
extensions/cornerstone/src/initWADOImageLoader.js
Normal file
18
extensions/cornerstone/src/initWADOImageLoader.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import cornerstone from 'cornerstone-core';
|
||||||
|
import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader';
|
||||||
|
import dicomParser from 'dicom-parser';
|
||||||
|
|
||||||
|
//import { initWebWorkers } from './utils/index.js';
|
||||||
|
|
||||||
|
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
||||||
|
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||||
|
|
||||||
|
cornerstoneWADOImageLoader.configure({
|
||||||
|
beforeSend: function(xhr) {
|
||||||
|
/*const headers = OHIF.DICOMWeb.getAuthorizationHeader();
|
||||||
|
|
||||||
|
if (headers.Authorization) {
|
||||||
|
xhr.setRequestHeader('Authorization', headers.Authorization);
|
||||||
|
}*/
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -31,7 +31,7 @@ function ViewerLayout({
|
|||||||
const getPanelData = id => {
|
const getPanelData = id => {
|
||||||
const entry = extensionManager.getModuleEntry(id);
|
const entry = extensionManager.getModuleEntry(id);
|
||||||
// TODO, not sure why sidepanel content has to be JSX, and not a children prop?
|
// TODO, not sure why sidepanel content has to be JSX, and not a children prop?
|
||||||
const content = entry.component({ extensionManager });
|
const content = entry.component;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
iconName: entry.iconName,
|
iconName: entry.iconName,
|
||||||
|
|||||||
@ -7,37 +7,86 @@ import {
|
|||||||
displaySetManager,
|
displaySetManager,
|
||||||
} from '@ohif/core';
|
} from '@ohif/core';
|
||||||
|
|
||||||
function StudyBrowserPanel({ extensionManager }) {
|
// Create map in local state from displaySetInstanceUids to thumbnails
|
||||||
// TODO: need to check how this is intended to be passed in
|
// Get thumbnail imageId from displaySet
|
||||||
const dataSource = extensionManager.dataSourceMap.dicomweb[0];
|
// When displaySetInstanceUids change, initiate async render into canvas
|
||||||
|
// Get image data-uri from canvas offscreen
|
||||||
|
// Set image data-uri into state (NOTE: This will probably end up in the fucking browser cache, NEED to find a way to prevent that from happening)
|
||||||
|
// state triggers rerender
|
||||||
|
//
|
||||||
|
// TODO:
|
||||||
|
// - No loading UI exists yet
|
||||||
|
// - cancel promises when component is destroyed
|
||||||
|
// - show errors in UI for thumbnails if promise fails
|
||||||
|
|
||||||
|
function getImageSrc(imageId, { cornerstone }) {
|
||||||
|
// TODO: Switch to async/await when it stops failing
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
cornerstone.loadAndCacheImage(imageId).then(image => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
cornerstone.renderToCanvas(canvas, image);
|
||||||
|
|
||||||
|
resolve(canvas.toDataURL());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function StudyBrowserPanel({ getDataSources, commandsManager }) {
|
||||||
const viewModel = useViewModel();
|
const viewModel = useViewModel();
|
||||||
|
|
||||||
|
const dataSource = getDataSources('dicomweb')[0];
|
||||||
|
const [studyData, setStudyData] = useState([]);
|
||||||
|
const [thumbnailImageSrcMap, setThumbnailImageSrcMap] = useState(new Map());
|
||||||
|
const updateThumbnailMap = (k, v) => {
|
||||||
|
setThumbnailImageSrcMap(thumbnailImageSrcMap.set(k, v));
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const command = commandsManager.getCommand(
|
||||||
|
'getCornerstoneLibraries',
|
||||||
|
'VIEWER'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
throw new Error('Required command not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { cornerstone, cornerstoneTools } = command.commandFn();
|
||||||
|
|
||||||
|
if (!viewModel.displaySetInstanceUids.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.displaySetInstanceUids.forEach(uid => {
|
||||||
|
const imageIds = dataSource.getImageIdsForDisplaySet(uid);
|
||||||
|
const imageId = imageIds[0];
|
||||||
|
|
||||||
|
getImageSrc(imageId, { cornerstone }).then(imageSrc => {
|
||||||
|
updateThumbnailMap(uid, imageSrc);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [viewModel.displaySetInstanceUids]);
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
const viewportData = []; //useViewportGrid();
|
const viewportData = []; //useViewportGrid();
|
||||||
const seriesTracking = {}; //useSeriesTracking();
|
const seriesTracking = {}; //useSeriesTracking();
|
||||||
|
|
||||||
const [studyData, setStudyData] = useState([]);
|
|
||||||
|
|
||||||
console.log(viewModel);
|
|
||||||
|
|
||||||
const displaySets = viewModel.displaySetInstanceUids.map(
|
const displaySets = viewModel.displaySetInstanceUids.map(
|
||||||
displaySetManager.getDisplaySetByUID
|
displaySetManager.getDisplaySetByUID
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(displaySets);
|
|
||||||
|
|
||||||
if (!displaySets.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - Put this in something so it only runs once
|
// - Put this in something so it only runs once
|
||||||
// - Have update the query update the dicom data store at the study level and then have this component use the data in the view model
|
// - Have update the query update the dicom data store at the study level and then have this component use the data in the view model
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!viewModel.displaySetInstanceUids.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const dSets = viewModel.displaySetInstanceUids.map(
|
const dSets = viewModel.displaySetInstanceUids.map(
|
||||||
displaySetManager.getDisplaySetByUID
|
displaySetManager.getDisplaySetByUID
|
||||||
);
|
);
|
||||||
|
|
||||||
const aDisplaySet = dSets[0];
|
const aDisplaySet = dSets[0];
|
||||||
const firstStudy = dicomMetadataStore.getStudy(
|
const firstStudy = dicomMetadataStore.getStudy(
|
||||||
aDisplaySet.StudyInstanceUID
|
aDisplaySet.StudyInstanceUID
|
||||||
@ -74,6 +123,7 @@ function StudyBrowserPanel({ extensionManager }) {
|
|||||||
modality: ds.Modality,
|
modality: ds.Modality,
|
||||||
date: ds.SeriesDate,
|
date: ds.SeriesDate,
|
||||||
numInstances: ds.numImageFrames,
|
numInstances: ds.numImageFrames,
|
||||||
|
//imageSrc,
|
||||||
};
|
};
|
||||||
|
|
||||||
const displaySetViewportData = viewportData.find(
|
const displaySetViewportData = viewportData.find(
|
||||||
@ -203,21 +253,30 @@ function StudyBrowserPanel({ extensionManager }) {
|
|||||||
return <StudyBrowser tabs={tabs} onClickStudy={memoOnClickStudy} />;
|
return <StudyBrowser tabs={tabs} onClickStudy={memoOnClickStudy} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPanelModule() {
|
function getPanelModule({ getDataSources, commandsManager }) {
|
||||||
|
const wrappedStudyBrowserPanel = () => {
|
||||||
|
return (
|
||||||
|
<StudyBrowserPanel
|
||||||
|
getDataSources={getDataSources}
|
||||||
|
commandsManager={commandsManager}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'seriesList',
|
name: 'seriesList',
|
||||||
iconName: 'group-layers',
|
iconName: 'group-layers',
|
||||||
iconLabel: 'Studies',
|
iconLabel: 'Studies',
|
||||||
label: 'Studies',
|
label: 'Studies',
|
||||||
component: StudyBrowserPanel,
|
component: wrappedStudyBrowserPanel,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'measure',
|
name: 'measure',
|
||||||
iconName: 'list-bullets',
|
iconName: 'list-bullets',
|
||||||
iconLabel: 'Measure',
|
iconLabel: 'Measure',
|
||||||
label: 'Measurements',
|
label: 'Measurements',
|
||||||
component: StudyBrowserPanel,
|
component: wrappedStudyBrowserPanel,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,13 +28,12 @@
|
|||||||
"start": "yarn run dev"
|
"start": "yarn run dev"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@ohif/core": "^0.50.0",
|
"@ohif/core": "^2.5.1",
|
||||||
"@ohif/i18n": "^0.50.0",
|
"@ohif/i18n": "^0.52.6",
|
||||||
"@ohif/ui": "^0.50.0",
|
"@ohif/ui": "^2.0.0",
|
||||||
"cornerstone-core": "^2.2.8",
|
"cornerstone-core": "^2.3.0",
|
||||||
"cornerstone-tools": "4.15.1",
|
"cornerstone-wado-image-loader": "^3.1.2",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"dcmjs": "0.12.2",
|
||||||
"dcmjs": "^0.12.2",
|
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
"i18next-browser-languagedetector": "^3.0.1",
|
"i18next-browser-languagedetector": "^3.0.1",
|
||||||
@ -56,9 +55,10 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ohif/core": "^2.9.6",
|
"@ohif/core": "^2.9.6",
|
||||||
"@ohif/ui": "^1.4.4",
|
|
||||||
"cornerstone-tools": "4.15.1",
|
"cornerstone-tools": "4.15.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
"@ohif/ui": "^2.0.0",
|
||||||
|
"cornerstone-core": "^2.3.0",
|
||||||
|
"cornerstone-wado-image-loader": "^3.1.2",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"gh-pages": "^2.0.1",
|
"gh-pages": "^2.0.1",
|
||||||
"i18next": "^17.0.3",
|
"i18next": "^17.0.3",
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"cornerstone-core": "^2.3.0",
|
"cornerstone-core": "^2.3.0",
|
||||||
"cornerstone-tools": "^4.12.0",
|
"cornerstone-tools": "^4.12.0",
|
||||||
"cornerstone-wado-image-loader": "^3.0.0",
|
"cornerstone-wado-image-loader": "^3.1.2",
|
||||||
"dicom-parser": "^1.8.3"
|
"dicom-parser": "^1.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@ -177,6 +177,7 @@ export default class ExtensionManager {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const extensionModule = getModuleFn({
|
const extensionModule = getModuleFn({
|
||||||
|
getDataSources: this.getDataSources,
|
||||||
servicesManager: this._servicesManager,
|
servicesManager: this._servicesManager,
|
||||||
commandsManager: this._commandsManager,
|
commandsManager: this._commandsManager,
|
||||||
appConfig: this._appConfig,
|
appConfig: this._appConfig,
|
||||||
|
|||||||
@ -141,7 +141,7 @@ const SidePanel = ({
|
|||||||
</span>
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{childComponent.content}
|
<childComponent.content/>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment>{getPanelButtons()}</React.Fragment>
|
<React.Fragment>{getPanelButtons()}</React.Fragment>
|
||||||
|
|||||||
@ -18,6 +18,8 @@ const ThumbnailList = ({ thumbnails }) => {
|
|||||||
viewportIdentificator,
|
viewportIdentificator,
|
||||||
isTracked,
|
isTracked,
|
||||||
isActive,
|
isActive,
|
||||||
|
imageSrc,
|
||||||
|
imageAltText,
|
||||||
}) => {
|
}) => {
|
||||||
switch (componentType) {
|
switch (componentType) {
|
||||||
case 'thumbnail':
|
case 'thumbnail':
|
||||||
@ -27,6 +29,9 @@ const ThumbnailList = ({ thumbnails }) => {
|
|||||||
description={description}
|
description={description}
|
||||||
seriesNumber={seriesNumber}
|
seriesNumber={seriesNumber}
|
||||||
numInstances={numInstances}
|
numInstances={numInstances}
|
||||||
|
imageSrc={imageSrc}
|
||||||
|
imageAltText={imageAltText}
|
||||||
|
viewportIdentificator={viewportIdentificator}
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
/>
|
/>
|
||||||
@ -48,6 +53,8 @@ const ThumbnailList = ({ thumbnails }) => {
|
|||||||
description={description}
|
description={description}
|
||||||
seriesNumber={seriesNumber}
|
seriesNumber={seriesNumber}
|
||||||
numInstances={numInstances}
|
numInstances={numInstances}
|
||||||
|
imageSrc={imageSrc}
|
||||||
|
imageAltText={imageAltText}
|
||||||
viewportIdentificator={viewportIdentificator}
|
viewportIdentificator={viewportIdentificator}
|
||||||
isTracked={isTracked}
|
isTracked={isTracked}
|
||||||
isActive={isActive}
|
isActive={isActive}
|
||||||
|
|||||||
@ -67,8 +67,8 @@
|
|||||||
"core-js": "^3.2.1",
|
"core-js": "^3.2.1",
|
||||||
"cornerstone-math": "^0.1.8",
|
"cornerstone-math": "^0.1.8",
|
||||||
"cornerstone-tools": "4.15.1",
|
"cornerstone-tools": "4.15.1",
|
||||||
"cornerstone-wado-image-loader": "^3.1.0",
|
|
||||||
"dcmjs": "^0.12.2",
|
"dcmjs": "^0.12.2",
|
||||||
|
"cornerstone-wado-image-loader": "^3.1.2",
|
||||||
"dicom-parser": "^1.8.3",
|
"dicom-parser": "^1.8.3",
|
||||||
"dicomweb-client": "^0.4.4",
|
"dicomweb-client": "^0.4.4",
|
||||||
"dotenv-webpack": "^1.7.0",
|
"dotenv-webpack": "^1.7.0",
|
||||||
|
|||||||
@ -5791,10 +5791,10 @@ cornerstone-tools@^4.12.0:
|
|||||||
"@babel/runtime" "7.1.2"
|
"@babel/runtime" "7.1.2"
|
||||||
cornerstone-math "0.1.7"
|
cornerstone-math "0.1.7"
|
||||||
|
|
||||||
cornerstone-wado-image-loader@^3.0.0:
|
cornerstone-wado-image-loader@^3.1.2:
|
||||||
version "3.0.6"
|
version "3.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.0.6.tgz#7b2be91f8f658de3a7474e029aa8314a35725132"
|
resolved "https://registry.yarnpkg.com/cornerstone-wado-image-loader/-/cornerstone-wado-image-loader-3.1.2.tgz#5113c317938510670061d11d05deb70266e4a35e"
|
||||||
integrity sha512-1luWOh1GzkboZ3uxfu4T8vh7K9nlDiiEbQ4dYIY9oobRZVZQbQiLT8wEZPbb4JTUe7UWJISHPCNgtAU8UE9DNw==
|
integrity sha512-cnDHrsa2GREqdMb9pjsoHgue+suHkDyuS/W0R8ZVD1lr+LQdv56dVM8oK9n7yOeorA4/IXG8630IL7uHdCHAQA==
|
||||||
dependencies:
|
dependencies:
|
||||||
dicom-parser "^1.8.3"
|
dicom-parser "^1.8.3"
|
||||||
pako "^1.0.10"
|
pako "^1.0.10"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user