diff --git a/DATACITATION.md b/DATACITATION.md new file mode 100644 index 00000000000..48b63514889 --- /dev/null +++ b/DATACITATION.md @@ -0,0 +1,144 @@ +# OHIF public demo data sets + +The OHIF Viewer's public demo page, available at https://viewer.ohif.org/, uses publicly anonymized demo datasets. +These datasets were mostly obtained from the [NIH NCI Imaging Data Commons](https://datacommons.cancer.gov/repository/imaging-data-commons) +and [NIH NCI TCIA](https://www.cancerimagingarchive.net/). Before listing the datasets, +we would like to extend a special thank you to all groups who have made their datasets publicly available. +Without them, we would not have been able to create this demo page. + +Please find below the list of datasets used on the demo page, along with their respective citations. + + +## Platforms +### NIH NCI IDC + +- Fedorov, A., Longabaugh, W.J., Pot, D., Clunie, D.A., Pieper, S., Aerts, H.J., Homeyer, A., Lewis, R., Akbarzadeh, A., Bontempi, D. and Clifford, W., 2021. NCI imaging data commons. Cancer research, 81(16), p.4188. + +### NIH NCI TCIA + +- Clark, K., Vendt, B., Smith, K., Freymann, J., Kirby, J., Koppel, P., Moore, S., Phillips, S., Maffitt, D., Pringle, M., Tarbox, L., & Prior, F. (2013). The Cancer Imaging Archive (TCIA): Maintaining and Operating a Public Information Repository. Journal of Digital Imaging, 26(6), 1045–1057. https://doi.org/10.1007/s10278-013-9622-7 + + + + +## Datasets +Below you can find the StudyInstanceUID of the studies that are used in the demo page along with their citations. + +### 1.3.6.1.4.1.14519.5.2.1.267424821384663813780850856506829388886 + +Segmentation of Vestibular Schwannoma from Magnetic Resonance Imaging: An Open Annotated Dataset and Baseline Algorithm (Vestibular-Schwannoma-SEG) + +- Shapey, J., Kujawa, A., Dorent, R., Wang, G., Bisdas, S., Dimitriadis, A., Grishchuck, D., Paddick, I., Kitchen, N., Bradford, R., Saeed, S., Ourselin, S., & Vercauteren, T. (2021). Segmentation of Vestibular Schwannoma from Magnetic Resonance Imaging: An Open Annotated Dataset and Baseline Algorithm [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.9YTJ-5Q73 + +- Shapey, J., Kujawa, A., Dorent, R., Wang, G., Dimitriadis, A., Grishchuk, D., Paddick, I., Kitchen, N., Bradford, R., Saeed, S. R., Bisdas, S., Ourselin, S., & Vercauteren, T. (2021). Segmentation of vestibular schwannoma from MRI, an open annotated dataset and baseline algorithm. In Scientific Data (Vol. 8, Issue 1). Springer Science and Business Media LLC. https://doi.org/10.1038/s41597-021-01064-w + + +### 1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463 +### 1.3.6.1.4.1.14519.5.2.1.7009.2403.871108593056125491804754960339 + + +ACRIN-NSCLC-FDG-PET (ACRIN 6668) + +- Kinahan, P., Muzi, M., Bialecki, B., Herman, B., & Coombs, L. (2019). Data from the ACRIN 6668 Trial NSCLC-FDG-PET (Version 2) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/tcia.2019.30ilqfcl + +- Machtay, M., Duan, F., Siegel, B. A., Snyder, B. S., Gorelick, J. J., Reddin, J. S., Munden, R., Johnson, D. W., Wilf, L. H., DeNittis, A., Sherwin, N., Cho, K. H., Kim, S., Videtic, G., Neumann, D. R., Komaki, R., Macapinlac, H., Bradley, J. D., & Alavi, A. (2013). Prediction of Survival by [18F]Fluorodeoxyglucose Positron Emission Tomography in Patients With Locally Advanced Non–Small-Cell Lung Cancer Undergoing Definitive Chemoradiation Therapy: Results of the ACRIN 6668/RTOG 0235 Trial. In Journal of Clinical Oncology (Vol. 31, Issue 30, pp. 3823–3830). American Society of Clinical Oncology (ASCO). https://doi.org/10.1200/jco.2012.47.5947 + + +### 2.25.103659964951665749659160840573802789777 + +The Cancer Genome Atlas Glioblastoma Multiforme Collection (TCGA-GBM) + +- Scarpace, L., Mikkelsen, T., Cha, S., Rao, S., Tekchandani, S., Gutman, D., Saltz, J. H., Erickson, B. J., Pedano, N., Flanders, A. E., Barnholtz-Sloan, J., Ostrom, Q., Barboriak, D., & Pierce, L. J. (2016). The Cancer Genome Atlas Glioblastoma Multiforme Collection (TCGA-GBM) (Version 4) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2016.RNYFUYE9 + + +### 1.3.6.1.4.1.14519.5.2.1.256467663913010332776401703474716742458 + +Abdominal or pelvic enhanced CT images within 10 days before surgery of 230 patients with stage II colorectal cancer (StageII-Colorectal-CT) + + +- Tong T., Li M. (2022) Abdominal or pelvic enhanced CT images within 10 days before surgery of 230 patients with stage II colorectal cancer (StageII-Colorectal-CT) [Dataset]. The Cancer Imaging Archive. DOI: https://doi.org/10.7937/p5k5-tg43 + +- Li, M., Gong, J., Bao, Y., Huang, D., Peng, J., & Tong, T. (2022). Special issue “The advance of solid tumor research in China”: Prognosis prediction for stage II colorectal cancer by fusing computed tomography radiomics and deep‐learning features of primary lesions and peripheral lymph nodes. In International Journal of Cancer. Wiley. https://doi.org/10.1002/ijc.34053 + + +### 1.3.6.1.4.1.14519.5.2.1.3023.4024.215308722288168917637555384485 + +The Cancer Genome Atlas Sarcoma Collection (TCGA-SARC) + +- Roche, C., Bonaccio, E., & Filippini, J. (2016). The Cancer Genome Atlas Sarcoma Collection (TCGA-SARC) (Version 3) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2016.CX6YLSUX + + + +### 1.3.6.1.4.1.14519.5.2.1.4792.2001.105216574054253895819671475627 + +BREAST-DIAGNOSIS + + +- Bloch, B. Nicolas, Jain, Ashali, & Jaffe, C. Carl. (2015). BREAST-DIAGNOSIS [Data set]. The Cancer Imaging Archive. http://doi.org/10.7937/K9/TCIA.2015.SDNRQXXR + + + + +### 1.3.6.1.4.1.14519.5.2.1.1706.8374.643249677828306008300337414785 + +Multimodality annotated HCC cases with and without advanced imaging segmentation (HCC-TACE-Seg) + + +- Moawad, A. W., Fuentes, D., Morshid, A., Khalaf, A. M., Elmohr, M. M., Abusaif, A., Hazle, J. D., Kaseb, A. O., Hassan, M., Mahvash, A., Szklaruk, J., Qayyom, A., & Elsayes, K. (2021). Multimodality annotated HCC cases with and without advanced imaging segmentation [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.5FNA-0924 + +- Morshid, A., Elsayes, K. M., Khalaf, A. M., Elmohr, M. M., Yu, J., Kaseb, A. O., Hassan, M., Mahvash, A., Wang, Z., Hazle, J. D., & Fuentes, D. (2019). A Machine Learning Model to Predict Hepatocellular Carcinoma Response to Transcatheter Arterial Chemoembolization. Radiology: Artificial Intelligence, 1(5), e180021. https://doi.org/10.1148/ryai.2019180021 + + + +### 1.3.6.1.4.1.14519.5.2.1.1188.2803.137585363493444318569098508293 + +Ultrasound data of a variety of liver masses (B-mode-and-CEUS-Liver) + +- Eisenbrey, J., Lyshchik, A., & Wessner, C. (2021). Ultrasound data of a variety of liver masses [Data set]. The Cancer Imaging Archive. DOI: https://doi.org/10.7937/TCIA.2021.v4z7-tc39 + + + +### 1.3.6.1.4.1.32722.99.99.62087908186665265759322018723889952421 + +NSCLC-Radiomics + +- Aerts, H. J. W. L., Wee, L., Rios Velazquez, E., Leijenaar, R. T. H., Parmar, C., Grossmann, P., Carvalho, S., Bussink, J., Monshouwer, R., Haibe-Kains, B., Rietveld, D., Hoebers, F., Rietbergen, M. M., Leemans, C. R., Dekker, A., Quackenbush, J., Gillies, R. J., Lambin, P. (2019). Data From NSCLC-Radiomics (version 4) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2015.PF0M9REI + + +- Aerts, H. J. W. L., Velazquez, E. R., Leijenaar, R. T. H., Parmar, C., Grossmann, P., Carvalho, S., Bussink, J., Monshouwer, R., Haibe-Kains, B., Rietveld, D., Hoebers, F., Rietbergen, M. M., Leemans, C. R., Dekker, A., Quackenbush, J., Gillies, R. J., Lambin, P. (2014, June 3). Decoding tumour phenotype by noninvasive imaging using a quantitative radiomics approach. Nature Communications. Nature Publishing Group. https://doi.org/10.1038/ncomms5006 (link) + + +### 1.3.6.1.4.1.14519.5.2.1.3671.4754.298665348758363466150039312520 + +QIN-PROSTATE-Repeatability + +- Fedorov, A; Schwier, M; Clunie, D; Herz, C; Pieper, S; Kikinis, R; Tempany, C; Fennessy, F. (2018). Data From QIN-PROSTATE-Repeatability. The Cancer Imaging Archive. DOI: 10.7937/K9/TCIA.2018.MR1CKGND + + +- Fedorov A, Vangel MG, Tempany CM, Fennessy FM. Multiparametric Magnetic Resonance Imaging of the Prostate: Repeatability of Volume and Apparent Diffusion Coefficient Quantification. Investigative Radiology. 52, 538–546 (2017). DOI: 10.1097/RLI.0000000000000382 + +- Fedorov, A., Schwier, M., Clunie, D., Herz, C., Pieper, S., Kikinis,R., Tempany, C. & Fennessy, F. An annotated test-retest collection of prostate multiparametric MRI. Scientific Data 5, 180281 (2018). DOI: + +### 2.25.141277760791347900862109212450152067508 + +The Clinical Proteomic Tumor Analysis Consortium Clear Cell Renal Cell Carcinoma Collection (CPTAC-CCRCC) + +- National Cancer Institute Clinical Proteomic Tumor Analysis Consortium (CPTAC). (2018). The Clinical Proteomic Tumor Analysis Consortium Clear Cell Renal Cell Carcinoma Collection (CPTAC-CCRCC) (Version 10) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/K9/TCIA.2018.OBLAMN27 + +- The CPTAC program requests that publications using data from this program include the following statement: “Data used in this publication were generated by the National Cancer Institute Clinical Proteomic Tumor Analysis Consortium (CPTAC).” + + +### 2.25.275741864483510678566144889372061815320 + +National Lung Screening Trial + +- National Lung Screening Trial Research Team. (2013). Data from the National Lung Screening Trial (NLST) [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.HMQ8-J677 + +- National Lung Screening Trial Research Team*; Aberle DR, Adams AM, Berg CD, Black WC, Clapp JD, Fagerstrom RM, Gareen IF, Gatsonis C, Marcus PM, Sicks JD (2011). Reduced Lung-Cancer Mortality with Low-Dose Computed Tomographic Screening. New England Journal of Medicine, 365(5), 395–409. https://doi.org/10.1056/nejmoa1102873 + + +### 1.3.6.1.4.1.14519.5.2.1.99.1071.26968527900428638961173806140069 + +Stony Brook University COVID-19 Positive Cases (COVID-19-NY-SBU) + +- Saltz, J., Saltz, M., Prasanna, P., Moffitt, R., Hajagos, J., Bremer, E., Balsamo, J., & Kurc, T. (2021). Stony Brook University COVID-19 Positive Cases [Data set]. The Cancer Imaging Archive. https://doi.org/10.7937/TCIA.BBAG-2923 diff --git a/extensions/cornerstone-dicom-sr/package.json b/extensions/cornerstone-dicom-sr/package.json index ab9161f325b..42c705d4c9c 100644 --- a/extensions/cornerstone-dicom-sr/package.json +++ b/extensions/cornerstone-dicom-sr/package.json @@ -46,7 +46,7 @@ "@babel/runtime": "^7.20.13", "classnames": "^2.3.2", "@cornerstonejs/adapters": "^0.6.0", - "@cornerstonejs/core": "^0.44.2", - "@cornerstonejs/tools": "^0.66.2" + "@cornerstonejs/core": "^0.46.2", + "@cornerstonejs/tools": "^0.67.2" } } diff --git a/extensions/cornerstone/package.json b/extensions/cornerstone/package.json index e73aaf76b43..4ac46a7c56e 100644 --- a/extensions/cornerstone/package.json +++ b/extensions/cornerstone/package.json @@ -34,7 +34,7 @@ "peerDependencies": { "@ohif/core": "^3.0.0", "@ohif/ui": "^2.0.0", - "@cornerstonejs/dicom-image-loader": "^0.4.0", + "@cornerstonejs/dicom-image-loader": "^0.6.4", "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", @@ -53,9 +53,9 @@ "dependencies": { "@babel/runtime": "^7.20.13", "@cornerstonejs/adapters": "^0.6.0", - "@cornerstonejs/core": "^0.44.2", - "@cornerstonejs/streaming-image-volume-loader": "^0.19.2", - "@cornerstonejs/tools": "^0.66.2", + "@cornerstonejs/core": "^0.46.2", + "@cornerstonejs/streaming-image-volume-loader": "^0.20.2", + "@cornerstonejs/tools": "^0.67.2", "@kitware/vtk.js": "27.3.1", "html2canvas": "^1.4.1", "lodash.debounce": "4.0.8", diff --git a/extensions/cornerstone/src/index.tsx b/extensions/cornerstone/src/index.tsx index 056c08d5a55..d213ac77fc4 100644 --- a/extensions/cornerstone/src/index.tsx +++ b/extensions/cornerstone/src/index.tsx @@ -23,6 +23,7 @@ import * as CornerstoneExtensionTypes from './types'; import { toolNames } from './initCornerstoneTools'; import { getEnabledElement, reset as enabledElementReset } from './state'; import dicomLoaderService from './utils/dicomLoaderService'; +import getActiveViewportEnabledElement from './utils/getActiveViewportEnabledElement'; import { registerColormap } from './utils/colormap/transferFunctionHelpers'; import { id } from './id'; @@ -141,5 +142,5 @@ const cornerstoneExtension: Types.Extensions.Extension = { }; export type { PublicViewportOptions }; -export { measurementMappingUtils, CornerstoneExtensionTypes, toolNames }; +export { measurementMappingUtils, CornerstoneExtensionTypes, toolNames , getActiveViewportEnabledElement}; export default cornerstoneExtension; diff --git a/extensions/default/src/DicomLocalDataSource/index.js b/extensions/default/src/DicomLocalDataSource/index.js index d7b3d71066d..c788bb4625f 100644 --- a/extensions/default/src/DicomLocalDataSource/index.js +++ b/extensions/default/src/DicomLocalDataSource/index.js @@ -200,6 +200,7 @@ function createDicomLocalApi(dicomLocalConfig) { displaySet.images.forEach(instance => { const NumberOfFrames = instance.NumberOfFrames; if (NumberOfFrames > 1) { + // in multiframe we start at frame 1 for (let i = 1; i <= NumberOfFrames; i++) { const imageId = this.getImageIdsForInstance({ instance, diff --git a/extensions/default/src/Panels/PanelStudyBrowser.tsx b/extensions/default/src/Panels/PanelStudyBrowser.tsx index 8a8244b3622..63a614f7946 100644 --- a/extensions/default/src/Panels/PanelStudyBrowser.tsx +++ b/extensions/default/src/Panels/PanelStudyBrowser.tsx @@ -12,7 +12,7 @@ const { sortStudyInstances, formatDate } = utils; function PanelStudyBrowser({ servicesManager, getImageSrc, - getStudiesForPatientByStudyInstanceUID, + getStudiesForPatientByMRN, requestDisplaySetCreationForStudy, dataSource, }) { @@ -64,12 +64,24 @@ function PanelStudyBrowser({ useEffect(() => { // Fetch all studies for the patient in each primary study async function fetchStudiesForPatient(StudyInstanceUID) { - const qidoStudiesForPatient = - (await getStudiesForPatientByStudyInstanceUID(StudyInstanceUID)) || []; + // current study qido + const qidoForStudyUID = await dataSource.query.studies.search({ + studyInstanceUid: StudyInstanceUID, + }); - // TODO: This should be "naturalized DICOM JSON" studies - const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient); + let qidoStudiesForPatient = qidoForStudyUID; + + // try to fetch the prior studies based on the patientID if the + // server can respond. + try { + qidoStudiesForPatient = await getStudiesForPatientByMRN( + qidoForStudyUID + ); + } catch (error) { + console.warn(error); + } + const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient); const actuallyMappedStudies = mappedStudies.map(qidoStudy => { return { studyInstanceUid: qidoStudy.StudyInstanceUID, @@ -77,29 +89,27 @@ function PanelStudyBrowser({ description: qidoStudy.StudyDescription, modalities: qidoStudy.ModalitiesInStudy, numInstances: qidoStudy.NumInstances, - // displaySets: [] }; }); - if (isMounted.current) { - setStudyDisplayList(prevArray => { - const ret = [...prevArray]; - for (const study of actuallyMappedStudies) { - if ( - !prevArray.find( - it => it.studyInstanceUid === study.studyInstanceUid - ) - ) { - ret.push(study); - } + + setStudyDisplayList(prevArray => { + const ret = [...prevArray]; + for (const study of actuallyMappedStudies) { + if ( + !prevArray.find( + it => it.studyInstanceUid === study.studyInstanceUid + ) + ) { + ret.push(study); } - return ret; - }); - } + } + return ret; + }); } StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid)); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [StudyInstanceUIDs, getStudiesForPatientByStudyInstanceUID]); + }, [StudyInstanceUIDs, getStudiesForPatientByMRN]); // // ~~ Initial Thumbnails useEffect(() => { @@ -254,7 +264,7 @@ PanelStudyBrowser.propTypes = { getImageIdsForDisplaySet: PropTypes.func.isRequired, }).isRequired, getImageSrc: PropTypes.func.isRequired, - getStudiesForPatientByStudyInstanceUID: PropTypes.func.isRequired, + getStudiesForPatientByMRN: PropTypes.func.isRequired, requestDisplaySetCreationForStudy: PropTypes.func.isRequired, }; diff --git a/extensions/default/src/Panels/WrappedPanelStudyBrowser.tsx b/extensions/default/src/Panels/WrappedPanelStudyBrowser.tsx index 237b5553b7e..b935ebfc767 100644 --- a/extensions/default/src/Panels/WrappedPanelStudyBrowser.tsx +++ b/extensions/default/src/Panels/WrappedPanelStudyBrowser.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; // import PanelStudyBrowser from './PanelStudyBrowser'; import getImageSrcFromImageId from './getImageSrcFromImageId'; -import getStudiesForPatientByStudyInstanceUID from './getStudiesForPatientByStudyInstanceUID'; +import getStudiesForPatientByMRN from './getStudiesForPatientByMRN'; import requestDisplaySetCreationForStudy from './requestDisplaySetCreationForStudy'; /** @@ -21,7 +21,7 @@ function WrappedPanelStudyBrowser({ // TODO: This should be made available a different way; route should have // already determined our datasource const dataSource = extensionManager.getDataSources()[0]; - const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind( + const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind( null, dataSource ); @@ -38,9 +38,7 @@ function WrappedPanelStudyBrowser({ servicesManager={servicesManager} dataSource={dataSource} getImageSrc={_getImageSrcFromImageId} - getStudiesForPatientByStudyInstanceUID={ - _getStudiesForPatientByStudyInstanceUID - } + getStudiesForPatientByMRN={_getStudiesForPatientByMRN} requestDisplaySetCreationForStudy={_requestDisplaySetCreationForStudy} /> ); diff --git a/extensions/default/src/Panels/getImageSrcFromImageId.js b/extensions/default/src/Panels/getImageSrcFromImageId.js index f243e4de42d..5cda3dff05b 100644 --- a/extensions/default/src/Panels/getImageSrcFromImageId.js +++ b/extensions/default/src/Panels/getImageSrcFromImageId.js @@ -6,7 +6,7 @@ function getImageSrcFromImageId(cornerstone, imageId) { return new Promise((resolve, reject) => { const canvas = document.createElement('canvas'); cornerstone.utilities - .loadImageToCanvas(canvas, imageId) + .loadImageToCanvas({ canvas, imageId }) .then(imageId => { resolve(canvas.toDataURL()); }) diff --git a/extensions/default/src/Panels/getStudiesForPatientByMRN.js b/extensions/default/src/Panels/getStudiesForPatientByMRN.js new file mode 100644 index 00000000000..5b74bde81b7 --- /dev/null +++ b/extensions/default/src/Panels/getStudiesForPatientByMRN.js @@ -0,0 +1,11 @@ +async function getStudiesForPatientByMRN(dataSource, qidoForStudyUID) { + if (qidoForStudyUID && qidoForStudyUID.length && qidoForStudyUID[0].mrn) { + return dataSource.query.studies.search({ + patientId: qidoForStudyUID[0].mrn, + }); + } + console.log('No mrn found for', qidoForStudyUID); + return qidoForStudyUID; +} + +export default getStudiesForPatientByMRN; diff --git a/extensions/default/src/Panels/getStudiesForPatientByStudyInstanceUID.js b/extensions/default/src/Panels/getStudiesForPatientByStudyInstanceUID.js deleted file mode 100644 index e12244885c3..00000000000 --- a/extensions/default/src/Panels/getStudiesForPatientByStudyInstanceUID.js +++ /dev/null @@ -1,29 +0,0 @@ -async function getStudiesForPatientByStudyInstanceUID( - dataSource, - StudyInstanceUID -) { - if (StudyInstanceUID === undefined) { - return; - } - // TODO: The `DicomMetadataStore` should short-circuit both of these requests - // Data _could_ be here from route query, or if using JSON data source - // We could also force this to "await" these values being available in the DICOMStore? - // Kind of like promise fulfillment in @cornerstonejs/dicom-image-loader when there are multiple - // outgoing requests for the same data - const getStudyResult = await dataSource.query.studies.search({ - studyInstanceUid: StudyInstanceUID, - }); - - // TODO: To Erik's point, the data source likely shouldn't deviate from - // Naturalized DICOM JSON when returning. It makes things like this awkward (mrn) - if (getStudyResult && getStudyResult.length && getStudyResult[0].mrn) { - return dataSource.query.studies.search({ - patientId: getStudyResult[0].mrn, - }); - } - console.log('No mrn found for', getStudyResult); - // The original study we KNOW belongs to the same set, so just return it - return getStudyResult; -} - -export default getStudiesForPatientByStudyInstanceUID; diff --git a/extensions/default/src/index.ts b/extensions/default/src/index.ts index bb4a63c5219..c56f39b7e5f 100644 --- a/extensions/default/src/index.ts +++ b/extensions/default/src/index.ts @@ -7,7 +7,7 @@ import getSopClassHandlerModule from './getSopClassHandlerModule.js'; import getToolbarModule from './getToolbarModule'; import getCommandsModule from './commandsModule'; import getHangingProtocolModule from './getHangingProtocolModule'; -import getStudiesForPatientByStudyInstanceUID from './Panels/getStudiesForPatientByStudyInstanceUID'; +import getStudiesForPatientByMRN from './Panels/getStudiesForPatientByMRN'; import getCustomizationModule from './getCustomizationModule'; import { id } from './id.js'; import preRegistration from './init'; @@ -34,7 +34,7 @@ const defaultExtension: Types.Extensions.Extension = { { name: 'common', exports: { - getStudiesForPatientByStudyInstanceUID, + getStudiesForPatientByMRN, }, }, ]; @@ -45,4 +45,8 @@ const defaultExtension: Types.Extensions.Extension = { export default defaultExtension; -export { ContextMenuController, CustomizeableContextMenuTypes }; +export { + ContextMenuController, + CustomizeableContextMenuTypes, + getStudiesForPatientByMRN, +}; diff --git a/extensions/measurement-tracking/package.json b/extensions/measurement-tracking/package.json index 07673f30033..94ab0e0db1f 100644 --- a/extensions/measurement-tracking/package.json +++ b/extensions/measurement-tracking/package.json @@ -32,8 +32,8 @@ "peerDependencies": { "@ohif/core": "^3.0.0", "classnames": "^2.3.2", - "@cornerstonejs/core": "^0.44.2", - "@cornerstonejs/tools": "^0.66.2", + "@cornerstonejs/core": "^0.46.2", + "@cornerstonejs/tools": "^0.67.2", "@ohif/extension-cornerstone-dicom-sr": "^3.0.0", "dcmjs": "^0.29.5", "lodash.debounce": "^4.17.21", diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx index ca1f383e2d8..e45dbaa6467 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/PanelStudyBrowserTracking.tsx @@ -18,7 +18,7 @@ const { formatDate } = utils; function PanelStudyBrowserTracking({ servicesManager, getImageSrc, - getStudiesForPatientByStudyInstanceUID, + getStudiesForPatientByMRN, requestDisplaySetCreationForStudy, dataSource, }) { @@ -76,8 +76,6 @@ function PanelStudyBrowserTracking({ const activeViewportDisplaySetInstanceUIDs = viewports[activeViewportIndex]?.displaySetInstanceUIDs; - const isSingleViewport = numCols === 1 && numRows === 1; - useEffect(() => { const added = measurementService.EVENTS.MEASUREMENT_ADDED; const addedRaw = measurementService.EVENTS.RAW_MEASUREMENT_ADDED; @@ -108,15 +106,29 @@ function PanelStudyBrowserTracking({ }; }, [measurementService, activeViewportIndex, sendTrackedMeasurementsEvent]); - const { trackedStudy, trackedSeries } = trackedMeasurements.context; + const { trackedSeries } = trackedMeasurements.context; // ~~ studyDisplayList useEffect(() => { // Fetch all studies for the patient in each primary study async function fetchStudiesForPatient(StudyInstanceUID) { - const qidoStudiesForPatient = - (await getStudiesForPatientByStudyInstanceUID(StudyInstanceUID)) || []; - // TODO: This should be "naturalized DICOM JSON" studies + // current study qido + const qidoForStudyUID = await dataSource.query.studies.search({ + studyInstanceUid: StudyInstanceUID, + }); + + let qidoStudiesForPatient = qidoForStudyUID; + + // try to fetch the prior studies based on the patientID if the + // server can respond. + try { + qidoStudiesForPatient = await getStudiesForPatientByMRN( + qidoForStudyUID + ); + } catch (error) { + console.warn(error); + } + const mappedStudies = _mapDataSourceStudies(qidoStudiesForPatient); const actuallyMappedStudies = mappedStudies.map(qidoStudy => { return { @@ -125,7 +137,6 @@ function PanelStudyBrowserTracking({ description: qidoStudy.StudyDescription, modalities: qidoStudy.ModalitiesInStudy, numInstances: qidoStudy.NumInstances, - // displaySets: [] }; }); @@ -146,11 +157,16 @@ function PanelStudyBrowserTracking({ StudyInstanceUIDs.forEach(sid => fetchStudiesForPatient(sid)); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [StudyInstanceUIDs, getStudiesForPatientByStudyInstanceUID]); + }, [StudyInstanceUIDs, getStudiesForPatientByMRN]); // ~~ Initial Thumbnails useEffect(() => { const currentDisplaySets = displaySetService.activeDisplaySets; + + if (!currentDisplaySets.length) { + return; + } + currentDisplaySets.forEach(async dSet => { const newImageSrcEntry = {}; const displaySet = displaySetService.getDisplaySetByUID( @@ -170,13 +186,16 @@ function PanelStudyBrowserTracking({ }); } }); - // eslint-disable-next-line react-hooks/exhaustive-deps }, [displaySetService, dataSource, getImageSrc]); // ~~ displaySets useEffect(() => { - // TODO: Are we sure `activeDisplaySets` will always be accurate? const currentDisplaySets = displaySetService.activeDisplaySets; + + if (!currentDisplaySets.length) { + return; + } + const mappedDisplaySets = _mapDisplaySets( currentDisplaySets, thumbnailImageSrcMap, @@ -194,9 +213,9 @@ function PanelStudyBrowserTracking({ }, [ displaySetService.activeDisplaySets, trackedSeries, - thumbnailImageSrcMap, viewports, dataSource, + thumbnailImageSrcMap, ]); // ~~ subscriptions --> displaySets @@ -379,7 +398,7 @@ PanelStudyBrowserTracking.propTypes = { getImageIdsForDisplaySet: PropTypes.func.isRequired, }).isRequired, getImageSrc: PropTypes.func.isRequired, - getStudiesForPatientByStudyInstanceUID: PropTypes.func.isRequired, + getStudiesForPatientByMRN: PropTypes.func.isRequired, requestDisplaySetCreationForStudy: PropTypes.func.isRequired, }; diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/getImageSrcFromImageId.js b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/getImageSrcFromImageId.js index af0e8a1c909..60a31499be2 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/getImageSrcFromImageId.js +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/getImageSrcFromImageId.js @@ -6,7 +6,7 @@ function getImageSrcFromImageId(cornerstone, imageId) { return new Promise((resolve, reject) => { const canvas = document.createElement('canvas'); cornerstone.utilities - .loadImageToCanvas(canvas, imageId) + .loadImageToCanvas({ canvas, imageId }) .then(imageId => { resolve(canvas.toDataURL()); }) diff --git a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/index.tsx b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/index.tsx index f84f4aa4c1d..604d5aa1580 100644 --- a/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/index.tsx +++ b/extensions/measurement-tracking/src/panels/PanelStudyBrowserTracking/index.tsx @@ -10,8 +10,8 @@ function _getStudyForPatientUtility(extensionManager) { '@ohif/extension-default.utilityModule.common' ); - const { getStudiesForPatientByStudyInstanceUID } = utilityModule.exports; - return getStudiesForPatientByStudyInstanceUID; + const { getStudiesForPatientByMRN } = utilityModule.exports; + return getStudiesForPatientByMRN; } /** @@ -28,10 +28,10 @@ function WrappedPanelStudyBrowserTracking({ }) { const dataSource = extensionManager.getActiveDataSource()[0]; - const getStudiesForPatientByStudyInstanceUID = _getStudyForPatientUtility( + const getStudiesForPatientByMRN = _getStudyForPatientUtility( extensionManager ); - const _getStudiesForPatientByStudyInstanceUID = getStudiesForPatientByStudyInstanceUID.bind( + const _getStudiesForPatientByMRN = getStudiesForPatientByMRN.bind( null, dataSource ); @@ -48,9 +48,7 @@ function WrappedPanelStudyBrowserTracking({ servicesManager={servicesManager} dataSource={dataSource} getImageSrc={_getImageSrcFromImageId} - getStudiesForPatientByStudyInstanceUID={ - _getStudiesForPatientByStudyInstanceUID - } + getStudiesForPatientByMRN={_getStudiesForPatientByMRN} requestDisplaySetCreationForStudy={_requestDisplaySetCreationForStudy} /> ); diff --git a/extensions/tmtv/src/utils/getStudiesForPatientByStudyInstanceUID.js b/extensions/tmtv/src/utils/getStudiesForPatientByStudyInstanceUID.js deleted file mode 100644 index e12244885c3..00000000000 --- a/extensions/tmtv/src/utils/getStudiesForPatientByStudyInstanceUID.js +++ /dev/null @@ -1,29 +0,0 @@ -async function getStudiesForPatientByStudyInstanceUID( - dataSource, - StudyInstanceUID -) { - if (StudyInstanceUID === undefined) { - return; - } - // TODO: The `DicomMetadataStore` should short-circuit both of these requests - // Data _could_ be here from route query, or if using JSON data source - // We could also force this to "await" these values being available in the DICOMStore? - // Kind of like promise fulfillment in @cornerstonejs/dicom-image-loader when there are multiple - // outgoing requests for the same data - const getStudyResult = await dataSource.query.studies.search({ - studyInstanceUid: StudyInstanceUID, - }); - - // TODO: To Erik's point, the data source likely shouldn't deviate from - // Naturalized DICOM JSON when returning. It makes things like this awkward (mrn) - if (getStudyResult && getStudyResult.length && getStudyResult[0].mrn) { - return dataSource.query.studies.search({ - patientId: getStudyResult[0].mrn, - }); - } - console.log('No mrn found for', getStudyResult); - // The original study we KNOW belongs to the same set, so just return it - return getStudyResult; -} - -export default getStudiesForPatientByStudyInstanceUID; diff --git a/platform/core/package.json b/platform/core/package.json index 43d41044621..293670c0cab 100644 --- a/platform/core/package.json +++ b/platform/core/package.json @@ -32,7 +32,7 @@ }, "peerDependencies": { "cornerstone-math": "0.1.9", - "@cornerstonejs/dicom-image-loader": "^0.4.0", + "@cornerstonejs/dicom-image-loader": "^0.6.4", "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", diff --git a/platform/core/src/classes/MetadataProvider.js b/platform/core/src/classes/MetadataProvider.js index 159fe169d43..75f1ad9fe35 100644 --- a/platform/core/src/classes/MetadataProvider.js +++ b/platform/core/src/classes/MetadataProvider.js @@ -481,6 +481,9 @@ class MetadataProvider { imageURI = imageIdToURI(imageId); } + // remove &frame=number from imageId + imageURI = imageURI.split('&frame=')[0]; + const uids = this.imageURIToUIDs.get(imageURI); let frameNumber = this.getFrameInformationFromURL(imageId) || '1'; diff --git a/platform/core/src/services/CustomizationService/CustomizationService.ts b/platform/core/src/services/CustomizationService/CustomizationService.ts index 91353a59cf5..cf3ee6a5f27 100644 --- a/platform/core/src/services/CustomizationService/CustomizationService.ts +++ b/platform/core/src/services/CustomizationService/CustomizationService.ts @@ -63,7 +63,7 @@ export default class CustomizationService extends PubSubService { modeCustomizations: Record = {}; globalCustomizations: Record = {}; - configuration: CustomizationConfiguration; + configuration: any; constructor({ configuration, commandsManager }) { super(EVENTS); diff --git a/platform/docs/docs/deployment/authorization.md b/platform/docs/docs/deployment/authorization.md new file mode 100644 index 00000000000..daba7454e7b --- /dev/null +++ b/platform/docs/docs/deployment/authorization.md @@ -0,0 +1,67 @@ +--- +sidebar_position: 8 +sidebar_label: Authorization +--- + +# Authorization +The OHIF Viewer can be configured to work with authorization servers that support one or more of the OpenID-Connect authorization flows. The Viewer finds it's OpenID-Connect settings on the oidc configuration key. You can set these values in your configuration files. For instance you can take a look at our +`google.js` configuration file. + + +```js +oidc: [ + { + // ~ REQUIRED + // Authorization Server URL + authority: 'https://accounts.google.com', + client_id: + '723928408739-k9k9r3i44j32rhu69vlnibipmmk9i57p.apps.googleusercontent.com', + redirect_uri: '/callback', + response_type: 'id_token token', + scope: + 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid + // ~ OPTIONAL + post_logout_redirect_uri: '/logout-redirect.html', + revoke_uri: 'https://accounts.google.com/o/oauth2/revoke?token=', + automaticSilentRenew: true, + revokeAccessTokenOnSignout: true, + }, + ], +``` + +You need to provide the following information: +- authority: The URL of the authorization server. +- client_id: The client id of your application (provided by the authorization server). +- redirect_uri: The callback URL of your application. +- response_type: The response type of the authorization flow (e.g. id_token token, [learn more about different flows](https://darutk.medium.com/diagrams-of-all-the-openid-connect-flows-6968e3990660)). +- scope: The scopes that your application needs to access +- post_logout_redirect_uri: The URL that the user will be redirected to after logout. +- revoke_uri: The URL that the user will be redirected to after logout. +- automaticSilentRenew: If true, the user will be automatically logged in after the token expires. +- revokeAccessTokenOnSignout: If true, the access token will be revoked on logout. + + + +## How it works +The Viewer uses the `userAuthenticationService` to set the OpenID-Connect settings. The `userAuthenticationService` is a singleton service that is responsible for authentication and authorization. It is initialized by the app and you can grab it +from the `servicesManager` + +```js +const userAuthenticationService = servicesManager.services.userAuthenticationService; +``` + +Then the userAuthenticationService will inject the token as Authorization header in the requests that are sent to the server (both metadata +and pixelData). + + +## Token based authentication +Sometimes (although not recommended), some servers like to send the token +in the query string. In this case, the viewer will automatically grab the token from the query string +and add it to the userAuthenticationService and remove it from the query string (to prevent it from being logged in the console +in future requests). + +and example would be + +```js +http://localhost:3000/viewer?StudyInstanceUIDs=1.2.3.4.5.6.6.7&token=e123125jsdfahsdf +``` diff --git a/platform/docs/docs/resources.md b/platform/docs/docs/resources.md index 1f629179306..96d55e343af 100644 --- a/platform/docs/docs/resources.md +++ b/platform/docs/docs/resources.md @@ -9,6 +9,23 @@ Throughout the development of the OHIF Viewer, we have participated in various conferences and "hackathons". In this page, we will provide the presentations and other resources that we have provided to the community in the past: +## 2023 +### SIIM 2023 Tech Tools Webinar | April 12th, 2023 + +Free, Open Source Tools for Research: MONAI and OHIF Viewer +[[Slides](https://docs.google.com/presentation/d/1afJ5Y9Tzukgn7eAbaO1oiCtN7XvIimFdmZP-HcOUofA/edit?usp=sharing)][[Video](https://www.youtube.com/watch?v=lo8J5w5jUJI)] + + +### [NA-MIC Project Week 38th 2023 - Remote] + +We participated in the 38th Project Week with three projects around OHIF. [[Website](https://projectweek.na-mic.org/PW38_2023_GranCanaria/)] + +- PolySeg representations for OHIF Viewer ([link](https://projectweek.na-mic.org/PW38_2023_GranCanaria/Projects/OHIF_PolySeg/)) +- Cross study sychronizer for OHIF Crosshair ([link](https://projectweek.na-mic.org/PW38_2023_GranCanaria/Projects/OHIF_SyncCrosshair/)) +- DATSCAN Viewer implementation in OHIF ([link](https://projectweek.na-mic.org/PW38_2023_GranCanaria/Projects/OHIF_DATSCAN/)) + + + ## 2022 ### OHIF Demo to Interns diff --git a/platform/viewer/package.json b/platform/viewer/package.json index 977cb8e1fb2..3ad63cfa433 100644 --- a/platform/viewer/package.json +++ b/platform/viewer/package.json @@ -68,7 +68,7 @@ "config-point": "^0.4.8", "core-js": "^3.16.1", "cornerstone-math": "^0.1.9", - "@cornerstonejs/dicom-image-loader": "^0.4.0", + "@cornerstonejs/dicom-image-loader": "^0.6.4", "@cornerstonejs/codec-charls": "^1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "^1.2.2", "@cornerstonejs/codec-openjpeg": "^1.2.2", diff --git a/platform/viewer/src/routes/Mode/Mode.tsx b/platform/viewer/src/routes/Mode/Mode.tsx index 81badddffdd..e49b13d0a20 100644 --- a/platform/viewer/src/routes/Mode/Mode.tsx +++ b/platform/viewer/src/routes/Mode/Mode.tsx @@ -99,7 +99,6 @@ export default function ModeRoute({ const params = useParams(); const searchParams = useSearchParams(); - const runTimeHangingProtocolId = searchParams.get('hangingprotocolid'); const [studyInstanceUIDs, setStudyInstanceUIDs] = useState(); const [refresh, setRefresh] = useState(false); @@ -123,6 +122,7 @@ export default function ModeRoute({ const { displaySetService, hangingProtocolService, + userAuthenticationService, } = (servicesManager as ServicesManager).services; const { @@ -131,6 +131,34 @@ export default function ModeRoute({ hotkeys: hotkeyObj, hangingProtocol, } = mode; + + const runTimeHangingProtocolId = searchParams.get('hangingprotocolid'); + const token = searchParams.get('token'); + + if (token) { + // if a token is passed in, set the userAuthenticationService to use it + // for the Authorization header for all requests + userAuthenticationService.setServiceImplementation({ + getAuthorizationHeader: () => ({ + Authorization: 'Bearer ' + token, + }), + }); + + // Create a URL object with the current location + const urlObj = new URL( + window.location.origin + location.pathname + location.search + ); + + // Remove the token from the URL object + urlObj.searchParams.delete('token'); + const cleanUrl = urlObj.toString(); + + // Update the browser's history without the token + if (window.history && window.history.replaceState) { + window.history.replaceState(null, '', cleanUrl); + } + } + // Preserve the old array interface for hotkeys const hotkeys = Array.isArray(hotkeyObj) ? hotkeyObj : hotkeyObj?.hotkeys; const hotkeyName = hotkeyObj?.name || 'hotkey-definitions-v2'; diff --git a/yarn.lock b/yarn.lock index f95777479a4..3cde7129ee5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -223,15 +223,6 @@ json5 "^2.2.2" semver "^6.3.0" -"@babel/eslint-parser@^7.19.1": - version "7.21.3" - resolved "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.21.3.tgz#d79e822050f2de65d7f368a076846e7184234af7" - integrity sha512-kfhmPimwo6k4P8zxNs8+T7yR44q1LdpsZdE1NkCsVlfiuTPRfnGgjaF8Qgug9q9Pou17u6wneYF0lDCZJATMFg== - dependencies: - "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" - eslint-visitor-keys "^2.1.0" - semver "^6.3.0" - "@babel/generator@7.0.0-beta.44": version "7.0.0-beta.44" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42" @@ -1478,54 +1469,43 @@ resolved "https://registry.npmjs.org/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.2.tgz#e96721d56f6ec96f7f95c16321d88cc8467d8d81" integrity sha512-lgdvBvvNezleY+4pIe2ceUsJzlZe/0PipdeubQ3vZZOz3xxtHHMR1XFCl4fgd8gosR8COHuD7h6q+MwgrwBsng== -"@cornerstonejs/core@^0.44.0": - version "0.44.0" - resolved "https://registry.npmjs.org/@cornerstonejs/core/-/core-0.44.0.tgz#f1a9af5407b25510e91e3e17b55dd9f3c61c557b" - integrity sha512-EZURQAuILsmiUdp5nusUpL6pJjteVgY7DqrqkFSqeeSlDjt2j+0h+kFvvbpRAhjyWz/1OjK9vtvbRFCpwhnzJw== - dependencies: - detect-gpu "^4.0.45" - lodash.clonedeep "4.5.0" - -"@cornerstonejs/core@^0.44.2": - version "0.44.2" - resolved "https://registry.npmjs.org/@cornerstonejs/core/-/core-0.44.2.tgz#13febc0455c833b029fac4a00a2e98f67ef50609" - integrity sha512-McwG04aFbxhA8LAAgZuh4ttFFW35CMvHwNrfYnQqaExCb62w1lzQBIHDopX05aGe9VAmmIRhaAuhaxxnH8ZsfA== +"@cornerstonejs/core@^0.46.2": + version "0.46.2" + resolved "https://registry.npmjs.org/@cornerstonejs/core/-/core-0.46.2.tgz#89ba0d27f491aab8b0a22d15ce5ed601ee23d2ec" + integrity sha512-HHActbdv3np/QXmQPl+DOqVMXPeOXbkqDov1SagF1om6Me4V7O8N2HbM4CgKCdf2VrEId6h0BL3+MG3Cxn7LPQ== dependencies: "@kitware/vtk.js" "27.3.1" detect-gpu "^5.0.22" gl-matrix "^3.4.3" lodash.clonedeep "4.5.0" -"@cornerstonejs/dicom-image-loader@^0.4.0": - version "0.4.0" - resolved "https://registry.npmjs.org/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-0.4.0.tgz#c7f5c5659a10a6f4f5e96367ab6d27784daeb424" - integrity sha512-ORnF/4qAbTninkesz7ADQu832PW+/xcbUhxSPU1ZPukjr9cglynsDzKjsEYJlY10+rWzbIQ+JEgefrXZgVoT1g== +"@cornerstonejs/dicom-image-loader@^0.6.4": + version "0.6.4" + resolved "https://registry.npmjs.org/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-0.6.4.tgz#8020402b11dd78df52957345ca57ed8c05b2999b" + integrity sha512-+y9NklTFYlq61frxKcJ2u+0h9zfKKiv20N28N5qBr3Ie6CFcNrY51ETkujNnNKKRUBvxXDidjVdY+j+MSPPJKQ== dependencies: - "@babel/eslint-parser" "^7.19.1" "@cornerstonejs/codec-charls" "^1.2.3" "@cornerstonejs/codec-libjpeg-turbo-8bit" "^1.2.2" "@cornerstonejs/codec-openjpeg" "^1.2.2" "@cornerstonejs/codec-openjph" "^2.4.2" - "@cornerstonejs/core" "^0.44.0" - coverage-istanbul-loader "^3.0.5" - date-format "^4.0.14" + "@cornerstonejs/core" "^0.46.2" dicom-parser "^1.8.9" pako "^2.0.4" uuid "^9.0.0" -"@cornerstonejs/streaming-image-volume-loader@^0.19.2": - version "0.19.2" - resolved "https://registry.npmjs.org/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.19.2.tgz#2d83fda24d9fe7a4a1b8fda334a82bddd000fb40" - integrity sha512-N2ws5GBsBnvdQB1gNI03WdiF71LeL5CGcdmkgAZMqFdtiiNbOlf0jW8ZmDwALY3+eL8kv2viHM3aLraXVT9Wwg== +"@cornerstonejs/streaming-image-volume-loader@^0.20.2": + version "0.20.2" + resolved "https://registry.npmjs.org/@cornerstonejs/streaming-image-volume-loader/-/streaming-image-volume-loader-0.20.2.tgz#0df2ed5afd012d2851356af01eed48646d3a6c60" + integrity sha512-2TYoJ0wpE9KDpn8adfmTq0xOb/Xg7jhEyOuBnrr3il/NFvOfMlJieJVCYM0v3hyoANfqmDcFBqqtlm346VOMdw== dependencies: - "@cornerstonejs/core" "^0.44.2" + "@cornerstonejs/core" "^0.46.2" -"@cornerstonejs/tools@^0.66.2": - version "0.66.2" - resolved "https://registry.npmjs.org/@cornerstonejs/tools/-/tools-0.66.2.tgz#58e76ea1c239bcf87a7c00ff661429804e09533a" - integrity sha512-ykSqhjJUIjgFDi9Ua0HwDTbXip8IMeeyM/3oMUlIINKURwvhipzJY6eWrmgwFzIlsXID4oLFPZq3V/V9lSqkaQ== +"@cornerstonejs/tools@^0.67.2": + version "0.67.2" + resolved "https://registry.npmjs.org/@cornerstonejs/tools/-/tools-0.67.2.tgz#7b7c6aac4ec7eaa84a438198837e657328e3c8a2" + integrity sha512-nOg2byAVrdMj3DqLYVT7qbrl2Sz6V82xtfFYJ4oQeGul/u/1AvHj4SSZs7MTV63Li3KP2ax/ZghCjral+exaVg== dependencies: - "@cornerstonejs/core" "^0.44.2" + "@cornerstonejs/core" "^0.46.2" lodash.clonedeep "4.5.0" lodash.get "^4.4.2" @@ -2721,17 +2701,6 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@jsdevtools/coverage-istanbul-loader@3.0.5": - version "3.0.5" - resolved "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" - integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== - dependencies: - convert-source-map "^1.7.0" - istanbul-lib-instrument "^4.0.3" - loader-utils "^2.0.0" - merge-source-map "^1.1.0" - schema-utils "^2.7.0" - "@juggle/resize-observer@^3.3.1": version "3.4.0" resolved "https://registry.npmjs.org/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" @@ -2955,13 +2924,6 @@ pump "^3.0.0" tar-fs "^2.1.1" -"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": - version "5.1.1-v1" - resolved "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz#dbf733a965ca47b1973177dc0bb6c889edcfb129" - integrity sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg== - dependencies: - eslint-scope "5.1.1" - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -7805,13 +7767,6 @@ cosmiconfig@^8.1.3: parse-json "^5.0.0" path-type "^4.0.0" -coverage-istanbul-loader@^3.0.5: - version "3.0.5" - resolved "https://registry.npmjs.org/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#bf942efc0f4e3ac27565203c17dca5008eae6637" - integrity sha512-xsw2phF0VNqUPk47V/vHXkdcTyl0tkMSmaZfLrTOhoPhPMXFelNju7utl5s7I93KXzipqDEK0YwofQSSflPz8A== - dependencies: - "@jsdevtools/coverage-istanbul-loader" "3.0.5" - cross-env@^5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d" @@ -8263,11 +8218,6 @@ date-fns@^1.27.2: resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -date-format@^4.0.14: - version "4.0.14" - resolved "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" - integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== - dateformat@^3.0.0: version "3.0.3" resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -8569,7 +8519,7 @@ detab@2.0.4: dependencies: repeat-string "^1.5.4" -detect-gpu@^4.0.16, detect-gpu@^4.0.45: +detect-gpu@^4.0.16: version "4.0.50" resolved "https://registry.npmjs.org/detect-gpu/-/detect-gpu-4.0.50.tgz#ba9df7e6b8416b1160303811015102bbad283aa0" integrity sha512-T67HE5+ONONN8rPXCBJPupyCg2QT8+l2NUUMuPxAppsMJBDPG/Jg0URLs6GyDzLm2niUE+oncIHSuy3VinoPeQ== @@ -9543,7 +9493,7 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== -eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: +eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== @@ -12369,16 +12319,6 @@ istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== -istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: version "5.2.1" resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d" @@ -14070,13 +14010,6 @@ merge-descriptors@1.0.1: resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"