diff --git a/DATACITATION.md b/DATACITATION.md new file mode 100644 index 000000000..48b635148 --- /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 ab9161f32..42c705d4c 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 e73aaf76b..4ac46a7c5 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 056c08d5a..d213ac77f 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 d7b3d7106..c788bb462 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 8a8244b36..63a614f79 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, + }); + + 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); + } - // TODO: This should be "naturalized DICOM JSON" studies 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 237b5553b..b935ebfc7 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 f243e4de4..5cda3dff0 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 000000000..5b74bde81 --- /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 e12244885..000000000 --- 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 bb4a63c52..c56f39b7e 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 07673f300..94ab0e0db 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 ca1f383e2..e45dbaa64 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 af0e8a1c9..60a31499b 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 f84f4aa4c..604d5aa15 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 e12244885..000000000 --- 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 43d410446..293670c0c 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 159fe169d..75f1ad9fe 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 91353a59c..cf3ee6a5f 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 000000000..daba7454e --- /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 1f6291793..96d55e343 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 977cb8e1f..3ad63cfa4 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 81badddff..e49b13d0a 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 f95777479..3cde7129e 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"