Fix contour annotations RTSS saving (#5545)
* [WIP] Fix contour annotations RTSS saving * fix: Export image sop image reference provider * Fixes for download with filename * Updates to fix metadata issues with CS3D * Updates to save menu * Fixing some additional re-save metadata * Add support for saving with predecessor image sequence * PR comment fixes * fix: Load RTSS after save * fix segmentation save * fix load of mixed seg/rt studies * Use frame module to get information on the frame shown * Fix study browser to use instances.length when numImageFrames not present * fix: Load of display set split from multiple non-FOR referenes * Fix RT and SEG applying to wrong series because of using referenced series sequence without checkout sop instances * Fix save seg * Update to released CS3D versions * test: Add revokeObjectURL to test download blob * Add hard dependency on mode-basic to fix build order issues * fix: build * Try to fix build segmentation * PR comments * PR comment update
This commit is contained in:
parent
9c5043ca2c
commit
57205703bb
@ -38,7 +38,7 @@
|
||||
"resolutions": {
|
||||
"**/@babel/runtime": "7.28.2",
|
||||
"commander": "8.3.0",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"nth-check": "2.1.1",
|
||||
"trim-newlines": "5.0.0",
|
||||
|
||||
402
bun.lock
402
bun.lock
@ -52,7 +52,7 @@
|
||||
},
|
||||
"addOns/externals/devDependencies": {
|
||||
"name": "@externals/devDependencies",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
@ -137,22 +137,22 @@
|
||||
},
|
||||
"addOns/externals/dicom-microscopy-viewer": {
|
||||
"name": "@externals/dicom-microscopy-viewer",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"dicom-microscopy-viewer": "0.48.6",
|
||||
},
|
||||
},
|
||||
"extensions/cornerstone": {
|
||||
"name": "@ohif/extension-cornerstone",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/ai": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.11.2",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/ai": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.11.3",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"html2canvas": "1.4.1",
|
||||
@ -169,10 +169,10 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.2",
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -183,19 +183,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-pmap": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-pmap",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/i18n": "3.12.0-beta.87",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@ -206,16 +206,16 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-rt": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-rt",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"react-color": "2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.89",
|
||||
"@ohif/extension-default": "3.12.0-beta.89",
|
||||
"@ohif/i18n": "3.12.0-beta.89",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@ -226,19 +226,19 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-seg": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-seg",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "2.19.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.89",
|
||||
"@ohif/extension-default": "3.12.0-beta.89",
|
||||
"@ohif/i18n": "3.12.0-beta.89",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@ -249,20 +249,20 @@
|
||||
},
|
||||
"extensions/cornerstone-dicom-sr": {
|
||||
"name": "@ohif/extension-cornerstone-dicom-sr",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.89",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.89",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -271,20 +271,20 @@
|
||||
},
|
||||
"extensions/cornerstone-dynamic-volume": {
|
||||
"name": "@ohif/extension-cornerstone-dynamic-volume",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/i18n": "3.12.0-beta.87",
|
||||
"@ohif/ui": "3.12.0-beta.87",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -293,7 +293,7 @@
|
||||
},
|
||||
"extensions/default": {
|
||||
"name": "@ohif/extension-default",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/calculate-suv": "1.1.0",
|
||||
@ -301,9 +301,9 @@
|
||||
"lodash.uniqby": "4.7.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/i18n": "3.12.0-beta.89",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
@ -316,7 +316,7 @@
|
||||
},
|
||||
"extensions/dicom-microscopy": {
|
||||
"name": "@ohif/extension-dicom-microscopy",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
@ -327,10 +327,10 @@
|
||||
"mathjs": "12.4.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/i18n": "3.12.0-beta.87",
|
||||
"@ohif/ui": "3.12.0-beta.87",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
@ -341,15 +341,15 @@
|
||||
},
|
||||
"extensions/dicom-pdf": {
|
||||
"name": "@ohif/extension-dicom-pdf",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -358,15 +358,15 @@
|
||||
},
|
||||
"extensions/dicom-video": {
|
||||
"name": "@ohif/extension-dicom-video",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -375,22 +375,22 @@
|
||||
},
|
||||
"extensions/measurement-tracking": {
|
||||
"name": "@ohif/extension-measurement-tracking",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"@xstate/react": "3.2.2",
|
||||
"xstate": "4.38.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.89",
|
||||
"@ohif/extension-default": "3.12.0-beta.89",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"classnames": "2.5.1",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
@ -401,15 +401,15 @@
|
||||
},
|
||||
"extensions/test-extension": {
|
||||
"name": "@ohif/extension-test",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/ui": "3.12.0-beta.87",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -418,15 +418,15 @@
|
||||
},
|
||||
"extensions/tmtv": {
|
||||
"name": "@ohif/extension-tmtv",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"classnames": "2.5.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.78",
|
||||
"@ohif/ui": "3.12.0-beta.78",
|
||||
"dcmjs": "0.43.1",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/ui": "3.12.0-beta.87",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -435,16 +435,16 @@
|
||||
},
|
||||
"extensions/usAnnotation": {
|
||||
"name": "@ohif/extension-ultrasound-pleura-bline",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/ui-next": "3.12.0-beta.86",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/i18n": "3.12.0-beta.87",
|
||||
"@ohif/ui-next": "3.12.0-beta.87",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.28.0",
|
||||
@ -487,7 +487,7 @@
|
||||
},
|
||||
"modes/basic": {
|
||||
"name": "@ohif/mode-basic",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
},
|
||||
@ -496,19 +496,19 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.78",
|
||||
"@ohif/extension-default": "3.12.0-beta.78",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.78",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.78",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.89",
|
||||
"@ohif/extension-default": "3.12.0-beta.89",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.89",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.89",
|
||||
},
|
||||
},
|
||||
"modes/basic-dev-mode": {
|
||||
"name": "@ohif/mode-basic-dev-mode",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@ -518,17 +518,17 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.87",
|
||||
},
|
||||
},
|
||||
"modes/basic-test-mode": {
|
||||
"name": "@ohif/mode-test",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@ -538,19 +538,19 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.86",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.86",
|
||||
"@ohif/extension-test": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.87",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.87",
|
||||
"@ohif/extension-test": "3.12.0-beta.87",
|
||||
},
|
||||
},
|
||||
"modes/longitudinal": {
|
||||
"name": "@ohif/mode-longitudinal",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@ -560,32 +560,33 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.86",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.89",
|
||||
"@ohif/extension-default": "3.12.0-beta.89",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.89",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.89",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.89",
|
||||
"@ohif/mode-basic": "3.12.0-beta.89",
|
||||
},
|
||||
},
|
||||
"modes/microscopy": {
|
||||
"name": "@ohif/mode-microscopy",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.87",
|
||||
},
|
||||
},
|
||||
"modes/preclinical-4d": {
|
||||
"name": "@ohif/mode-preclinical-4d",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
},
|
||||
@ -594,17 +595,17 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dynamic-volume": "3.12.0-beta.78",
|
||||
"@ohif/extension-default": "3.12.0-beta.78",
|
||||
"@ohif/extension-tmtv": "3.12.0-beta.78",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dynamic-volume": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/extension-tmtv": "3.12.0-beta.87",
|
||||
},
|
||||
},
|
||||
"modes/segmentation": {
|
||||
"name": "@ohif/mode-segmentation",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@ -636,19 +637,20 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.78",
|
||||
"@ohif/extension-default": "3.12.0-beta.78",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.78",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.78",
|
||||
"@ohif/core": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.89",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.89",
|
||||
"@ohif/extension-default": "3.12.0-beta.89",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.89",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.89",
|
||||
"@ohif/mode-basic": "3.12.0-beta.89",
|
||||
},
|
||||
},
|
||||
"modes/tmtv": {
|
||||
"name": "@ohif/mode-tmtv",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next": "17.3.1",
|
||||
@ -658,25 +660,25 @@
|
||||
"webpack-merge": "5.10.0",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.78",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.78",
|
||||
"@ohif/extension-default": "3.12.0-beta.78",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.78",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.78",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.78",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.87",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.87",
|
||||
},
|
||||
},
|
||||
"modes/usAnnotation": {
|
||||
"name": "@ohif/mode-ultrasound-pleura-bline",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.86",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.86",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.87",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.87",
|
||||
"i18next": "17.3.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -708,40 +710,40 @@
|
||||
},
|
||||
"platform/app": {
|
||||
"name": "@ohif/app",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/codec-charls": "1.2.3",
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.2",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.3",
|
||||
"@emotion/serialize": "1.3.3",
|
||||
"@ohif/core": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.86",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.86",
|
||||
"@ohif/extension-default": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.86",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.86",
|
||||
"@ohif/extension-test": "3.12.0-beta.86",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.86",
|
||||
"@ohif/i18n": "3.12.0-beta.86",
|
||||
"@ohif/mode-basic-dev-mode": "3.12.0-beta.86",
|
||||
"@ohif/mode-longitudinal": "3.12.0-beta.86",
|
||||
"@ohif/mode-microscopy": "3.12.0-beta.86",
|
||||
"@ohif/mode-test": "3.12.0-beta.86",
|
||||
"@ohif/mode-ultrasound-pleura-bline": "3.12.0-beta.86",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"@ohif/ui-next": "3.12.0-beta.86",
|
||||
"@ohif/core": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-seg": "3.12.0-beta.87",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.87",
|
||||
"@ohif/extension-default": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-microscopy": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.87",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.87",
|
||||
"@ohif/extension-test": "3.12.0-beta.87",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.87",
|
||||
"@ohif/i18n": "3.12.0-beta.87",
|
||||
"@ohif/mode-basic-dev-mode": "3.12.0-beta.87",
|
||||
"@ohif/mode-longitudinal": "3.12.0-beta.87",
|
||||
"@ohif/mode-microscopy": "3.12.0-beta.87",
|
||||
"@ohif/mode-test": "3.12.0-beta.87",
|
||||
"@ohif/mode-ultrasound-pleura-bline": "3.12.0-beta.87",
|
||||
"@ohif/ui": "3.12.0-beta.87",
|
||||
"@ohif/ui-next": "3.12.0-beta.87",
|
||||
"@svgr/webpack": "8.1.0",
|
||||
"@types/react": "18.3.23",
|
||||
"classnames": "2.5.1",
|
||||
"core-js": "3.45.1",
|
||||
"cornerstone-math": "0.1.10",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"detect-gpu": "4.0.50",
|
||||
"dicom-parser": "1.8.21",
|
||||
"dotenv-webpack": "1.8.0",
|
||||
@ -787,7 +789,7 @@
|
||||
},
|
||||
"platform/cli": {
|
||||
"name": "@ohif/cli",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"bin": {
|
||||
"ohif-cli": "src/index.js",
|
||||
},
|
||||
@ -811,10 +813,10 @@
|
||||
},
|
||||
"platform/core": {
|
||||
"name": "@ohif/core",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"gl-matrix": "3.4.3",
|
||||
"immutability-helper": "3.1.1",
|
||||
@ -836,16 +838,16 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.2",
|
||||
"@ohif/ui": "3.12.0-beta.86",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.3",
|
||||
"@ohif/ui": "3.12.0-beta.89",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "1.8.21",
|
||||
},
|
||||
},
|
||||
"platform/i18n": {
|
||||
"name": "@ohif/i18n",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"i18next-locize-backend": "2.2.2",
|
||||
@ -870,7 +872,7 @@
|
||||
},
|
||||
"platform/ui": {
|
||||
"name": "@ohif/ui",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@testing-library/react": "13.4.0",
|
||||
"browser-detect": "0.2.28",
|
||||
@ -921,7 +923,7 @@
|
||||
},
|
||||
"platform/ui-next": {
|
||||
"name": "@ohif/ui-next",
|
||||
"version": "3.12.0-beta.86",
|
||||
"version": "3.12.0-beta.89",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "1.2.11",
|
||||
"@radix-ui/react-checkbox": "1.3.2",
|
||||
@ -973,7 +975,7 @@
|
||||
"core-js": "3.45.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cross-spawn": "7.0.6",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"glob-parent": "6.0.2",
|
||||
"nth-check": "2.1.1",
|
||||
"package-json": "8.1.1",
|
||||
@ -1228,9 +1230,9 @@
|
||||
|
||||
"@bcoe/v8-coverage": ["@bcoe/v8-coverage@0.2.3", "", {}, "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw=="],
|
||||
|
||||
"@cornerstonejs/adapters": ["@cornerstonejs/adapters@4.11.2", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.43.1", "gl-matrix": "3.4.3", "ndarray": "1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "4.11.2", "@cornerstonejs/tools": "4.11.2" } }, "sha512-yYZd4W+BnM9So0SsPYYjYN+cho5FBlXIOg0o3JX+gQGoguSk9UedRfd5xa7f7TYoN4ect7RdU4ytwajdabRY5Q=="],
|
||||
"@cornerstonejs/adapters": ["@cornerstonejs/adapters@4.11.3", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.45.0", "gl-matrix": "3.4.3", "ndarray": "1.0.19" }, "peerDependencies": { "@cornerstonejs/core": "4.11.3", "@cornerstonejs/tools": "4.11.3" } }, "sha512-N0nKn7ZKBr4ryvveSy19h8EnyhAUyYkdmn23KibPrF84VjzFfK99xMIFWYHxxhYWfCJwzcGf7RR4LIop3FD60Q=="],
|
||||
|
||||
"@cornerstonejs/ai": ["@cornerstonejs/ai@4.11.2", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.43.1", "gl-matrix": "3.4.3", "lodash.clonedeep": "4.5.0", "ndarray": "1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "4.11.2", "@cornerstonejs/tools": "4.11.2" } }, "sha512-aUfcRidr8/48Szw9MPK4MFXZPhoPlABxOXltx6pZHFe7da3KE1WYoSLD/Dw0T72AtqV2w8blXQuNpplTL4ErAg=="],
|
||||
"@cornerstonejs/ai": ["@cornerstonejs/ai@4.11.3", "", { "dependencies": { "@babel/runtime-corejs2": "7.26.10", "buffer": "6.0.3", "dcmjs": "0.45.0", "gl-matrix": "3.4.3", "lodash.clonedeep": "4.5.0", "ndarray": "1.0.19", "onnxruntime-common": "1.17.1", "onnxruntime-web": "1.17.1" }, "peerDependencies": { "@cornerstonejs/core": "4.11.3", "@cornerstonejs/tools": "4.11.3" } }, "sha512-qHrFOuM8iYtCX683+Nii9fBkBvgPQA2x+/9jbZpV17YPHAxUSy5gi6a+3ISMwVzKtFhIVhVZK+JMKAAOcCtVVQ=="],
|
||||
|
||||
"@cornerstonejs/calculate-suv": ["@cornerstonejs/calculate-suv@1.1.0", "", {}, "sha512-Q9XraiDJif9aMFArD2iEuDO/HXbcRVCqB7KfaHgDrdTTjgDFovS91Psbdim7crypRSvE6dh/+HKeFNHdvNkA6w=="],
|
||||
|
||||
@ -1242,15 +1244,15 @@
|
||||
|
||||
"@cornerstonejs/codec-openjph": ["@cornerstonejs/codec-openjph@2.4.7", "", {}, "sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w=="],
|
||||
|
||||
"@cornerstonejs/core": ["@cornerstonejs/core@4.11.2", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "4.4.2", "gl-matrix": "3.4.3", "loglevel": "1.9.2" } }, "sha512-8Lq3hq+k31C4KbU0sGfUEP/ORn/6nqVYioXvQG4iU+SpLbbQVyhUgEqtQPIKoZVsGaV2mX4HOU3fWXAmMfP4fQ=="],
|
||||
"@cornerstonejs/core": ["@cornerstonejs/core@4.11.3", "", { "dependencies": { "@kitware/vtk.js": "32.12.1", "comlink": "4.4.2", "gl-matrix": "3.4.3", "loglevel": "1.9.2" } }, "sha512-72yGsvuf5XezQ2nV6JPIOPMlIRtXzuABn1JgpDpoCYbjqbrmApL6G0nLswj0Tuo2dmh4E3iS80KSVVggjllcPg=="],
|
||||
|
||||
"@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@4.11.2", "", { "dependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", "comlink": "4.4.2", "dicom-parser": "1.8.21", "jpeg-lossless-decoder-js": "2.1.2", "pako": "2.1.0", "uuid": "9.0.1" }, "peerDependencies": { "@cornerstonejs/core": "4.11.2" } }, "sha512-xIWbAOrkVBgpBpcqlyYIL2fiAZ8KRF453Ao6svDRixoL7Fl2gvhnpJGbG3wWCYhHaPXbYpbhmfzIovT9qZ7ACg=="],
|
||||
"@cornerstonejs/dicom-image-loader": ["@cornerstonejs/dicom-image-loader@4.11.3", "", { "dependencies": { "@cornerstonejs/codec-charls": "1.2.3", "@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2", "@cornerstonejs/codec-openjpeg": "1.3.0", "@cornerstonejs/codec-openjph": "2.4.7", "comlink": "4.4.2", "dicom-parser": "1.8.21", "jpeg-lossless-decoder-js": "2.1.2", "pako": "2.1.0", "uuid": "9.0.1" }, "peerDependencies": { "@cornerstonejs/core": "4.11.3" } }, "sha512-SULpwXEpHF0cINusoTehwUpzQOyxakSTrZ+h4WTLu9GKosxxoIPorw3/eQpsJR9tUBxNnrvgSPFkXXGBdEq4Cw=="],
|
||||
|
||||
"@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.11.2", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.11.2", "@cornerstonejs/tools": "4.11.2", "@kitware/vtk.js": "32.12.1" } }, "sha512-gS0Gt07U9hDzjoD75I3fKcXRM47sEWsAI706L23/3Q0nN3DGz5NDDySIDo8E+IfZDdSJeX2yBO38RqszVlTkcA=="],
|
||||
"@cornerstonejs/labelmap-interpolation": ["@cornerstonejs/labelmap-interpolation@4.11.3", "", { "dependencies": { "@itk-wasm/morphological-contour-interpolation": "1.1.0", "itk-wasm": "1.0.0-b.165" }, "peerDependencies": { "@cornerstonejs/core": "4.11.3", "@cornerstonejs/tools": "4.11.3", "@kitware/vtk.js": "32.12.1" } }, "sha512-yqIvR+7x8HC1l6cMgTBx75LMQf3ZMXdsfiBbOl4mYOlRiiQ5tEcx+8anU+kllwEhsWThu5MjY7s5ZSRybyks0g=="],
|
||||
|
||||
"@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.11.2", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.11.2", "@cornerstonejs/tools": "4.11.2", "@kitware/vtk.js": "32.12.1" } }, "sha512-cW1kVQOiwzWnsHdQOi6wi/k7ji3+qC1sqWb7WgFqf7uTuBbab4Oh0KO6ODcg3G0SOWhzQCPT+GcZf7tsgQh2Tw=="],
|
||||
"@cornerstonejs/polymorphic-segmentation": ["@cornerstonejs/polymorphic-segmentation@4.11.3", "", { "dependencies": { "@icr/polyseg-wasm": "0.4.0" }, "peerDependencies": { "@cornerstonejs/core": "4.11.3", "@cornerstonejs/tools": "4.11.3", "@kitware/vtk.js": "32.12.1" } }, "sha512-RXKP26mEcQLzpC1xArr/QpMq+minkjO4W02jhhJxmGdTTVypyFyq3Kr9tPJUhosM91yssUHa5mIIHwPwURWxKg=="],
|
||||
|
||||
"@cornerstonejs/tools": ["@cornerstonejs/tools@4.11.2", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.11.2", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-7opk5t0l3HDSEEa2/PfoiuWBtK6wMdHUjCzy1LY9dVqTpkoqE7YRhNsCuByvd4xgzfp/gijaqlcxS1gyZz0hlw=="],
|
||||
"@cornerstonejs/tools": ["@cornerstonejs/tools@4.11.3", "", { "dependencies": { "@types/offscreencanvas": "2019.7.3", "comlink": "4.4.2", "lodash.get": "4.4.2" }, "peerDependencies": { "@cornerstonejs/core": "4.11.3", "@kitware/vtk.js": "32.12.1", "@types/d3-array": "3.2.1", "@types/d3-interpolate": "3.0.4", "d3-array": "3.2.4", "d3-interpolate": "3.0.1", "gl-matrix": "3.4.3" } }, "sha512-L1H57kP0/mfMsmEKc9VhI8XDWYfuPJ0QfNKax2hfF1lma2w/zBsvkYJYNUqpUTZkDFSFVUjPZTBaPxP/BeXcSA=="],
|
||||
|
||||
"@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="],
|
||||
|
||||
@ -2678,7 +2680,7 @@
|
||||
|
||||
"dayjs": ["dayjs@1.11.13", "", {}, "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg=="],
|
||||
|
||||
"dcmjs": ["dcmjs@0.43.1", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.5", "adm-zip": "^0.5.10", "gl-matrix": "^3.1.0", "lodash.clonedeep": "^4.5.0", "loglevel": "^1.8.1", "ndarray": "^1.0.19", "pako": "^2.0.4" } }, "sha512-7IAB2cs2F8QYINbfhA7PV+IDraqBuHn8N31xJc6HsyNxZXyGUPQRujcq732ACqjLhl7oJi1z8PJJgLdblQtD3Q=="],
|
||||
"dcmjs": ["dcmjs@0.45.0", "", { "dependencies": { "@babel/runtime-corejs3": "^7.22.5", "adm-zip": "^0.5.10", "gl-matrix": "^3.1.0", "lodash.clonedeep": "^4.5.0", "loglevel": "^1.8.1", "ndarray": "^1.0.19", "pako": "^2.0.4" } }, "sha512-MFtACBLQB6vj/3lzHEf6iGBLFC8f/QysoDO+DVUJeLrKYQi4mXYzzBbHmZCpMXqIpj8qXXLvnP/u2ugd9DVu7Q=="],
|
||||
|
||||
"debounce": ["debounce@1.2.1", "", {}, "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="],
|
||||
|
||||
|
||||
@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "2.19.3"
|
||||
}
|
||||
|
||||
@ -17,19 +17,25 @@ function _getDisplaySetsFromSeries(
|
||||
servicesManager: AppTypes.ServicesManager,
|
||||
extensionManager
|
||||
) {
|
||||
const instance = instances[0];
|
||||
utils.sortStudyInstances(instances);
|
||||
// Choose the LAST instance in the list as the most recently created one.
|
||||
const instance = instances[instances.length - 1];
|
||||
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesDescription,
|
||||
SeriesDescription = '',
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
StructureSetDate,
|
||||
StructureSetTime,
|
||||
SOPClassUID,
|
||||
wadoRoot,
|
||||
wadoUri,
|
||||
wadoUriRoot,
|
||||
imageId: predecessorImageId,
|
||||
} = instance;
|
||||
|
||||
const displaySet = {
|
||||
@ -39,7 +45,13 @@ function _getDisplaySetsFromSeries(
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
/**
|
||||
* The "SeriesDate" for a display set is really the display set date, which
|
||||
* should be the date of the instance being used, which will be the structure
|
||||
* set date in this case.
|
||||
*/
|
||||
SeriesDate: StructureSetDate || SeriesDate,
|
||||
SeriesTime: StructureSetTime || SeriesTime,
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
@ -54,6 +66,8 @@ function _getDisplaySetsFromSeries(
|
||||
structureSet: null,
|
||||
sopClassUids,
|
||||
instance,
|
||||
instances,
|
||||
predecessorImageId,
|
||||
wadoRoot,
|
||||
wadoUriRoot,
|
||||
wadoUri,
|
||||
@ -62,7 +76,10 @@ function _getDisplaySetsFromSeries(
|
||||
};
|
||||
|
||||
let referencedSeriesSequence = instance.ReferencedSeriesSequence;
|
||||
if (instance.ReferencedFrameOfReferenceSequence && !instance.ReferencedSeriesSequence) {
|
||||
if (
|
||||
instance.ReferencedFrameOfReferenceSequence?.RTReferencedStudySequence &&
|
||||
!instance.ReferencedSeriesSequence
|
||||
) {
|
||||
instance.ReferencedSeriesSequence = _deriveReferencedSeriesSequenceFromFrameOfReferenceSequence(
|
||||
instance.ReferencedFrameOfReferenceSequence
|
||||
);
|
||||
@ -70,7 +87,8 @@ function _getDisplaySetsFromSeries(
|
||||
}
|
||||
|
||||
if (!referencedSeriesSequence) {
|
||||
throw new Error('ReferencedSeriesSequence is missing for the RTSTRUCT');
|
||||
console.error('ReferencedSeriesSequence is missing for the RTSTRUCT');
|
||||
return;
|
||||
}
|
||||
|
||||
const referencedSeries = referencedSeriesSequence[0];
|
||||
@ -79,9 +97,13 @@ function _getDisplaySetsFromSeries(
|
||||
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
||||
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
|
||||
displaySet.referencedSeriesInstanceUID
|
||||
);
|
||||
const referencedDisplaySets =
|
||||
displaySetService.getDisplaySetsForReferences(referencedSeriesSequence);
|
||||
if (referencedDisplaySets?.length > 1) {
|
||||
console.warn(
|
||||
'Reference applies to more than 1 display set for Contours, applying only to first display set'
|
||||
);
|
||||
}
|
||||
|
||||
if (!referencedDisplaySets || referencedDisplaySets.length === 0) {
|
||||
// Instead of throwing error, subscribe to display sets added
|
||||
@ -97,7 +119,7 @@ function _getDisplaySetsFromSeries(
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const referencedDisplaySet = referencedDisplaySets[0];
|
||||
const [referencedDisplaySet] = referencedDisplaySets;
|
||||
displaySet.referencedDisplaySetInstanceUID = referencedDisplaySet.displaySetInstanceUID;
|
||||
displaySet.isReconstructable = referencedDisplaySet.isReconstructable;
|
||||
}
|
||||
|
||||
@ -89,9 +89,12 @@ async function checkAndLoadContourData({
|
||||
ROIContour.ContourSequence.forEach((Contour, index) => {
|
||||
const promise = resolvedPromises[index];
|
||||
if (promise.status === 'fulfilled') {
|
||||
if (Array.isArray(promise.value) && promise.value.every(Number.isFinite)) {
|
||||
if (
|
||||
Array.isArray(promise.value) &&
|
||||
promise.value.every(it => Number.isFinite(Number(it)))
|
||||
) {
|
||||
// If promise.value is already an array of numbers, use it directly
|
||||
Contour.ContourData = promise.value;
|
||||
Contour.ContourData = promise.value.map(Number);
|
||||
} else {
|
||||
// If the resolved promise value is a byte array (Blob), it needs to be decoded
|
||||
const uint8Array = new Uint8Array(promise.value);
|
||||
|
||||
@ -103,7 +103,7 @@ function OHIFCornerstoneRTViewport(props: withAppTypes) {
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
|
||||
evt => {
|
||||
if (evt.rtDisplaySet.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
|
||||
if (evt.rtDisplaySet?.displaySetInstanceUID === rtDisplaySet.displaySetInstanceUID) {
|
||||
setRtIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
@ -46,8 +46,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"react-color": "2.19.3"
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import dcmjs from 'dcmjs';
|
||||
import { classes, Types } from '@ohif/core';
|
||||
import { classes, Types, utils } from '@ohif/core';
|
||||
import { cache, metaData } from '@cornerstonejs/core';
|
||||
import { segmentation as cornerstoneToolsSegmentation } from '@cornerstonejs/tools';
|
||||
import { adaptersRT, helpers, adaptersSEG } from '@cornerstonejs/adapters';
|
||||
@ -8,8 +8,6 @@ import { DicomMetadataStore } from '@ohif/core';
|
||||
|
||||
import PROMPT_RESPONSES from '../../default/src/utils/_shared/PROMPT_RESPONSES';
|
||||
|
||||
const { datasetToBlob } = dcmjs.data;
|
||||
|
||||
const getTargetViewport = ({ viewportId, viewportGridService }) => {
|
||||
const { viewports, activeViewportId } = viewportGridService.getState();
|
||||
const targetViewportId = viewportId || activeViewportId;
|
||||
@ -27,7 +25,7 @@ const {
|
||||
|
||||
const {
|
||||
Cornerstone3D: {
|
||||
RTSS: { generateRTSSFromSegmentations },
|
||||
RTSS: { generateRTSSFromRepresentation },
|
||||
},
|
||||
} = adaptersRT;
|
||||
|
||||
@ -92,6 +90,7 @@ const commandsModule = ({
|
||||
*/
|
||||
generateSegmentation: ({ segmentationId, options = {} }) => {
|
||||
const segmentation = cornerstoneToolsSegmentation.state.getSegmentation(segmentationId);
|
||||
const predecessorImageId = options.predecessorImageId ?? segmentation.predecessorImageId;
|
||||
|
||||
const { imageIds } = segmentation.representationData.Labelmap;
|
||||
|
||||
@ -173,12 +172,10 @@ const commandsModule = ({
|
||||
labelmap3D.metadata[segmentIndex] = segmentMetadata;
|
||||
});
|
||||
|
||||
const generatedSegmentation = generateSegmentation(
|
||||
referencedImages,
|
||||
labelmap3D,
|
||||
metaData,
|
||||
options
|
||||
);
|
||||
const generatedSegmentation = generateSegmentation(referencedImages, labelmap3D, metaData, {
|
||||
predecessorImageId,
|
||||
...options,
|
||||
});
|
||||
|
||||
return generatedSegmentation;
|
||||
},
|
||||
@ -212,24 +209,28 @@ const commandsModule = ({
|
||||
* @returns {Object|void} Returns the naturalized report if successfully stored,
|
||||
* otherwise throws an error.
|
||||
*/
|
||||
storeSegmentation: async ({ segmentationId, dataSource }) => {
|
||||
storeSegmentation: async ({ segmentationId, dataSource, modality = 'SEG' }) => {
|
||||
const segmentation = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
if (!segmentation) {
|
||||
throw new Error('No segmentation found');
|
||||
}
|
||||
|
||||
const { label } = segmentation;
|
||||
const { label, predecessorImageId } = segmentation;
|
||||
const defaultDataSource = dataSource ?? extensionManager.getActiveDataSource()[0];
|
||||
|
||||
const {
|
||||
value: reportName,
|
||||
dataSourceName: selectedDataSource,
|
||||
series,
|
||||
priorSeriesNumber,
|
||||
action,
|
||||
} = await createReportDialogPrompt({
|
||||
servicesManager,
|
||||
extensionManager,
|
||||
predecessorImageId,
|
||||
title: 'Store Segmentation',
|
||||
modality,
|
||||
});
|
||||
|
||||
if (action === PROMPT_RESPONSES.CREATE_REPORT) {
|
||||
@ -238,12 +239,18 @@ const commandsModule = ({
|
||||
? extensionManager.getDataSources(selectedDataSource)[0]
|
||||
: defaultDataSource;
|
||||
|
||||
const generatedData = actions.generateSegmentation({
|
||||
const args = {
|
||||
segmentationId,
|
||||
options: {
|
||||
SeriesDescription: reportName || label || 'Research Derived Series',
|
||||
SeriesDescription: series ? undefined : reportName || label || 'Contour Series',
|
||||
SeriesNumber: series ? undefined : 1 + priorSeriesNumber,
|
||||
predecessorImageId: series,
|
||||
},
|
||||
});
|
||||
};
|
||||
const generatedDataAsync =
|
||||
(modality === 'SEG' && actions.generateSegmentation(args)) ||
|
||||
(modality === 'RTSTRUCT' && actions.generateContour(args));
|
||||
const generatedData = await generatedDataAsync;
|
||||
|
||||
if (!generatedData || !generatedData.dataset) {
|
||||
throw new Error('Error during segmentation generation');
|
||||
@ -270,14 +277,9 @@ const commandsModule = ({
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Converts segmentations into RTSS for download.
|
||||
* This sample function retrieves all segentations and passes to
|
||||
* cornerstone tool adapter to convert to DICOM RTSS format. It then
|
||||
* converts dataset to downloadable blob.
|
||||
*
|
||||
*/
|
||||
downloadRTSS: async ({ segmentationId }) => {
|
||||
|
||||
generateContour: async args => {
|
||||
const { segmentationId, options } = args;
|
||||
const segmentations = segmentationService.getSegmentation(segmentationId);
|
||||
|
||||
// inject colors to the segmentIndex
|
||||
@ -287,26 +289,34 @@ const commandsModule = ({
|
||||
segment.color = segmentationService.getSegmentColor(
|
||||
firstRepresentation.viewportId,
|
||||
segmentationId,
|
||||
segmentIndex
|
||||
Number(segmentIndex)
|
||||
);
|
||||
});
|
||||
const predecessorImageId = options?.predecessorImageId ?? segmentations.predecessorImageId;
|
||||
const dataset = await generateRTSSFromRepresentation(segmentations, {
|
||||
predecessorImageId,
|
||||
...options,
|
||||
});
|
||||
return { dataset };
|
||||
},
|
||||
|
||||
const RTSS = await generateRTSSFromSegmentations(
|
||||
segmentations,
|
||||
classes.MetadataProvider,
|
||||
DicomMetadataStore
|
||||
);
|
||||
/**
|
||||
* Downloads an RTSS instance from a segmentation or contour
|
||||
* representation.
|
||||
*/
|
||||
downloadRTSS: async args => {
|
||||
const { dataset } = await actions.generateContour(args);
|
||||
const { InstanceNumber: instanceNumber = 1, SeriesInstanceUID: seriesUID } = dataset;
|
||||
|
||||
try {
|
||||
const reportBlob = datasetToBlob(RTSS);
|
||||
|
||||
//Create a URL for the binary.
|
||||
const objectUrl = URL.createObjectURL(reportBlob);
|
||||
window.location.assign(objectUrl);
|
||||
const filename = `rtss-${seriesUID}-${instanceNumber}.dcm`;
|
||||
downloadDICOMData(dataset, filename);
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
},
|
||||
|
||||
toggleActiveSegmentationUtility: ({ itemId: buttonId }) => {
|
||||
const { uiState, setUIState } = useUIStateStore.getState();
|
||||
const isButtonActive = uiState['activeSegmentationUtility'] === buttonId;
|
||||
@ -321,25 +331,12 @@ const commandsModule = ({
|
||||
};
|
||||
|
||||
const definitions = {
|
||||
loadSegmentationsForViewport: {
|
||||
commandFn: actions.loadSegmentationsForViewport,
|
||||
},
|
||||
|
||||
generateSegmentation: {
|
||||
commandFn: actions.generateSegmentation,
|
||||
},
|
||||
downloadSegmentation: {
|
||||
commandFn: actions.downloadSegmentation,
|
||||
},
|
||||
storeSegmentation: {
|
||||
commandFn: actions.storeSegmentation,
|
||||
},
|
||||
downloadRTSS: {
|
||||
commandFn: actions.downloadRTSS,
|
||||
},
|
||||
toggleActiveSegmentationUtility: {
|
||||
commandFn: actions.toggleActiveSegmentationUtility,
|
||||
},
|
||||
loadSegmentationsForViewport: actions.loadSegmentationsForViewport,
|
||||
generateSegmentation: actions.generateSegmentation,
|
||||
downloadSegmentation: actions.downloadSegmentation,
|
||||
storeSegmentation: actions.storeSegmentation,
|
||||
downloadRTSS: actions.downloadRTSS,
|
||||
toggleActiveSegmentationUtility: actions.toggleActiveSegmentationUtility,
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -16,19 +16,24 @@ function _getDisplaySetsFromSeries(
|
||||
servicesManager: AppTypes.ServicesManager,
|
||||
extensionManager
|
||||
) {
|
||||
const instance = instances[0];
|
||||
utils.sortStudyInstances(instances);
|
||||
|
||||
// Choose the LAST instance in the list as the most recently created one.
|
||||
const instance = instances[instances.length - 1];
|
||||
|
||||
const {
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID,
|
||||
SeriesDescription,
|
||||
SeriesDescription = '',
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
StructureSetDate,
|
||||
SOPClassUID,
|
||||
wadoRoot,
|
||||
wadoUri,
|
||||
wadoUriRoot,
|
||||
imageId: predecessorImageId,
|
||||
} = instance;
|
||||
|
||||
const displaySet = {
|
||||
@ -38,7 +43,7 @@ function _getDisplaySetsFromSeries(
|
||||
displaySetInstanceUID: utils.guid(),
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
SeriesDate,
|
||||
SeriesDate: SeriesDate || StructureSetDate || '',
|
||||
SOPInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
@ -53,6 +58,7 @@ function _getDisplaySetsFromSeries(
|
||||
segments: {},
|
||||
sopClassUids,
|
||||
instance,
|
||||
predecessorImageId,
|
||||
instances: [instance],
|
||||
wadoRoot,
|
||||
wadoUriRoot,
|
||||
@ -73,10 +79,16 @@ function _getDisplaySetsFromSeries(
|
||||
displaySet.referencedImages = instance.ReferencedSeriesSequence.ReferencedInstanceSequence;
|
||||
displaySet.referencedSeriesInstanceUID = referencedSeries.SeriesInstanceUID;
|
||||
const { displaySetService } = servicesManager.services;
|
||||
const referencedDisplaySets = displaySetService.getDisplaySetsForSeries(
|
||||
displaySet.referencedSeriesInstanceUID
|
||||
const referencedDisplaySets = displaySetService.getDisplaySetsForReferences(
|
||||
instance.ReferencedSeriesSequence
|
||||
);
|
||||
|
||||
if (referencedDisplaySets?.length > 1) {
|
||||
console.warn(
|
||||
'Segmentation does not currently handle references to multiple series, defaulting to first series'
|
||||
);
|
||||
}
|
||||
|
||||
const referencedDisplaySet = referencedDisplaySets[0];
|
||||
|
||||
if (!referencedDisplaySet) {
|
||||
|
||||
@ -134,7 +134,7 @@ function OHIFCornerstoneSEGViewport(props: withAppTypes) {
|
||||
const { unsubscribe } = segmentationService.subscribe(
|
||||
segmentationService.EVENTS.SEGMENTATION_LOADING_COMPLETE,
|
||||
evt => {
|
||||
if (evt.segDisplaySet.displaySetInstanceUID === segDisplaySet.displaySetInstanceUID) {
|
||||
if (evt.segDisplaySet?.displaySetInstanceUID === segDisplaySet?.displaySetInstanceUID) {
|
||||
setSegIsLoading(false);
|
||||
}
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.90",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -46,9 +46,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"classnames": "2.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { metaData, utilities } from '@cornerstonejs/core';
|
||||
import { metaData } from '@cornerstonejs/core';
|
||||
|
||||
import OHIF, { DicomMetadataStore } from '@ohif/core';
|
||||
import OHIF, { DicomMetadataStore, utils } from '@ohif/core';
|
||||
import dcmjs from 'dcmjs';
|
||||
import { adaptersSR } from '@cornerstonejs/adapters';
|
||||
|
||||
import getFilteredCornerstoneToolState from './utils/getFilteredCornerstoneToolState';
|
||||
import hydrateStructuredReport from './utils/hydrateStructuredReport';
|
||||
|
||||
const { downloadBlob } = utils;
|
||||
|
||||
const { MeasurementReport } = adaptersSR.Cornerstone3D;
|
||||
const { log } = OHIF;
|
||||
|
||||
@ -42,8 +44,6 @@ const _generateReport = (measurementData, additionalFindingTypes, options: Optio
|
||||
dataset.SpecificCharacterSet = 'ISO_IR 192';
|
||||
}
|
||||
|
||||
dataset.InstanceNumber = options.InstanceNumber ?? 1;
|
||||
|
||||
return dataset;
|
||||
};
|
||||
|
||||
@ -85,8 +85,7 @@ const commandsModule = (props: withAppTypes) => {
|
||||
const reportBlob = dcmjs.data.datasetToBlob(srDataset);
|
||||
|
||||
//Create a URL for the binary.
|
||||
const objectUrl = URL.createObjectURL(reportBlob);
|
||||
window.location.assign(objectUrl);
|
||||
downloadBlob(reportBlob, { filename: 'dicom-sr.dcm' });
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@ -100,6 +100,7 @@ function _getDisplaySetsFromSeries(
|
||||
SeriesTime,
|
||||
ConceptNameCodeSequence,
|
||||
SOPClassUID,
|
||||
imageId: predecessorImageId,
|
||||
} = instance;
|
||||
validateSameStudyUID(instance.StudyInstanceUID, instances);
|
||||
|
||||
@ -128,6 +129,7 @@ function _getDisplaySetsFromSeries(
|
||||
isImagingMeasurementReport,
|
||||
sopClassUids,
|
||||
instance,
|
||||
predecessorImageId,
|
||||
addInstances,
|
||||
label: SeriesDescription || `${i18n.t('Series')} ${SeriesNumber} - ${i18n.t('SR')}`,
|
||||
};
|
||||
@ -184,6 +186,10 @@ async function _load(
|
||||
srDisplaySet.referencedImages = [];
|
||||
srDisplaySet.measurements = [];
|
||||
}
|
||||
const { predecessorImageId } = srDisplaySet;
|
||||
for (const measurement of srDisplaySet.measurements) {
|
||||
measurement.predecessorImageId = predecessorImageId;
|
||||
}
|
||||
|
||||
const mappings = measurementService.getSourceMappings(
|
||||
CORNERSTONE_3D_TOOLS_SOURCE_NAME,
|
||||
|
||||
@ -54,6 +54,11 @@ export default function hydrateStructuredReport(
|
||||
const disableEditing = customizationService.getCustomization('panelMeasurement.disableEditing');
|
||||
|
||||
const displaySet = displaySetService.getDisplaySetByUID(displaySetInstanceUID);
|
||||
const {
|
||||
StudyInstanceUID: studyUID,
|
||||
SeriesInstanceUID: seriesUID,
|
||||
instance: { SOPInstanceUID: sopUID },
|
||||
} = displaySet;
|
||||
|
||||
// TODO -> We should define a strict versioning somewhere.
|
||||
const mappings = measurementService.getSourceMappings(
|
||||
@ -67,16 +72,12 @@ export default function hydrateStructuredReport(
|
||||
);
|
||||
}
|
||||
|
||||
const instance = DicomMetadataStore.getInstance(
|
||||
displaySet.StudyInstanceUID,
|
||||
displaySet.SeriesInstanceUID,
|
||||
displaySet.SOPInstanceUID
|
||||
);
|
||||
const instance = DicomMetadataStore.getInstance(studyUID, seriesUID, sopUID);
|
||||
|
||||
const sopInstanceUIDToImageId = {};
|
||||
|
||||
displaySet.measurements.forEach(measurement => {
|
||||
const { ReferencedSOPInstanceUID, imageId, frameNumber=1 } = measurement;
|
||||
const { ReferencedSOPInstanceUID, imageId, frameNumber = 1 } = measurement;
|
||||
const key = `${ReferencedSOPInstanceUID}:${frameNumber}`;
|
||||
|
||||
if (!sopInstanceUIDToImageId[key]) {
|
||||
@ -200,6 +201,7 @@ export default function hydrateStructuredReport(
|
||||
const annotation = {
|
||||
annotationUID: toolData.annotation.annotationUID,
|
||||
data: toolData.annotation.data,
|
||||
predecessorImageId: toolData.predecessorImageId,
|
||||
metadata: {
|
||||
...referenceData,
|
||||
toolName: annotationType,
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
"@ohif/extension-default": "3.12.0-beta.90",
|
||||
"@ohif/i18n": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"classnames": "2.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import * as importedActions from './actions';
|
||||
import { utilities, Enums } from '@cornerstonejs/tools';
|
||||
import { cache } from '@cornerstonejs/core';
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const LABELMAP = Enums.SegmentationRepresentations.Labelmap;
|
||||
import * as importedActions from './actions';
|
||||
|
||||
const { downloadCsv } = utils;
|
||||
|
||||
const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
const services = servicesManager.services;
|
||||
@ -37,7 +39,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
});
|
||||
return computedDisplaySets;
|
||||
},
|
||||
exportTimeReportCSV: ({ segmentations, config, options, summaryStats }) => {
|
||||
exportTimeReportCSV: ({ segmentations, summaryStats }) => {
|
||||
const dynamic4DDisplaySet = actions.getDynamic4DDisplaySet();
|
||||
|
||||
const volumeId = dynamic4DDisplaySet?.displaySetInstanceUID;
|
||||
@ -203,15 +205,7 @@ const commandsModule = ({ commandsManager, servicesManager }: withAppTypes) => {
|
||||
|
||||
// Generate filename and trigger download
|
||||
const filename = `${instance.PatientID}.csv`;
|
||||
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
downloadCsv(csvContent, { filename });
|
||||
},
|
||||
swapDynamicWithComputedDisplaySet: ({ displaySet }) => {
|
||||
const computedDisplaySet = displaySet;
|
||||
|
||||
@ -40,10 +40,10 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.2",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
@ -53,12 +53,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/adapters": "4.11.2",
|
||||
"@cornerstonejs/ai": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.11.2",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/adapters": "4.11.3",
|
||||
"@cornerstonejs/ai": "4.11.3",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/labelmap-interpolation": "4.11.3",
|
||||
"@cornerstonejs/polymorphic-segmentation": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@itk-wasm/morphological-contour-interpolation": "1.1.0",
|
||||
"@kitware/vtk.js": "32.12.0",
|
||||
"html2canvas": "1.4.1",
|
||||
|
||||
@ -1647,15 +1647,13 @@ function commandsModule({
|
||||
* Stores a segmentation and shows it in the viewport
|
||||
* @param props.segmentationId - The ID of the segmentation to store
|
||||
*/
|
||||
storeSegmentationCommand: async ({ segmentationId }) => {
|
||||
storeSegmentationCommand: async args => {
|
||||
const { segmentationId } = args;
|
||||
const { segmentationService, viewportGridService } = servicesManager.services;
|
||||
|
||||
const displaySetInstanceUIDs = await createReportAsync({
|
||||
servicesManager,
|
||||
getReport: () =>
|
||||
commandsManager.runCommand('storeSegmentation', {
|
||||
segmentationId,
|
||||
}),
|
||||
getReport: () => commandsManager.runCommand('storeSegmentation', args),
|
||||
reportType: 'Segmentation',
|
||||
});
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ interface ExportSegmentationSubMenuItemProps {
|
||||
segmentationRepresentationType: string;
|
||||
allowExport: boolean;
|
||||
actions: {
|
||||
storeSegmentation: (segmentationId: string) => Promise<void>;
|
||||
storeSegmentation: (segmentationId: string, modality?: string) => Promise<unknown>;
|
||||
onSegmentationDownloadRTSS: (segmentationId: string) => void;
|
||||
onSegmentationDownload: (segmentationId: string) => void;
|
||||
downloadCSVSegmentationReport: (segmentationId: string) => void;
|
||||
@ -34,66 +34,39 @@ export const ExportSegmentationSubMenuItem: React.FC<ExportSegmentationSubMenuIt
|
||||
const { t } = useTranslation('SegmentationTable');
|
||||
|
||||
return (
|
||||
<>
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger className="pl-1">
|
||||
<Icons.Export className="text-foreground" />
|
||||
<span className="pl-2">{t('Download & Export')}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Download className="h-5 w-5" />
|
||||
<span className="pl-1">{t('Download')}</span>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.downloadCSVSegmentationReport(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('CSV Report')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownload(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownloadRTSS(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM RTSS')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Export className="h-5 w-5" />
|
||||
<span className="pl-1 pt-1">{t('Export')}</span>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.storeSegmentation(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
)}
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Contour && (
|
||||
<>
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger className="pl-1">
|
||||
<Icons.Export className="text-foreground" />
|
||||
<span className="pl-2">{t('Download & Export')}</span>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Download className="h-5 w-5" />
|
||||
<span className="pl-1">{t('Download')}</span>
|
||||
</DropdownMenuLabel>
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.downloadCSVSegmentationReport(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('CSV Report')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.onSegmentationDownload(segmentationId);
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
@ -101,11 +74,35 @@ export const ExportSegmentationSubMenuItem: React.FC<ExportSegmentationSubMenuIt
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
<Icons.Export className="text-foreground" />
|
||||
<span className="pl-2">{t('Download DICOM RTSS')}</span>
|
||||
{t('DICOM RTSS')}
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel className="flex items-center pl-0">
|
||||
<Icons.Export className="h-5 w-5" />
|
||||
<span className="pl-1 pt-1">{t('Export')}</span>
|
||||
</DropdownMenuLabel>
|
||||
{segmentationRepresentationType === SegmentationRepresentations.Labelmap && (
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.storeSegmentation(segmentationId, 'SEG');
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM SEG')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
actions.storeSegmentation(segmentationId, 'RTSTRUCT');
|
||||
}}
|
||||
disabled={!allowExport}
|
||||
>
|
||||
{t('DICOM RTSS')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
);
|
||||
};
|
||||
|
||||
@ -46,21 +46,21 @@ export const CustomDropdownMenuContent = () => {
|
||||
segmentationId = activeSegmentationId;
|
||||
}
|
||||
|
||||
// Determine if export is allowed for this segmentation
|
||||
if (exportOptions && segmentationId) {
|
||||
const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId);
|
||||
allowExport = exportOption?.isExportable || false;
|
||||
}
|
||||
|
||||
if (!segmentation || !segmentationId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine if export is allowed for this segmentation
|
||||
if (exportOptions) {
|
||||
const exportOption = exportOptions.find(opt => opt.segmentationId === segmentationId);
|
||||
allowExport = exportOption?.isExportable || false;
|
||||
}
|
||||
|
||||
const actions = {
|
||||
storeSegmentation: async segmentationId => {
|
||||
storeSegmentation: async (segmentationId, modality = 'SEG') => {
|
||||
commandsManager.run({
|
||||
commandName: 'storeSegmentation',
|
||||
commandOptions: { segmentationId },
|
||||
commandOptions: { segmentationId, modality },
|
||||
context: 'CORNERSTONE',
|
||||
});
|
||||
},
|
||||
|
||||
@ -461,6 +461,9 @@ const connectMeasurementServiceToTools = ({
|
||||
const annotationManager = annotation.state.getAnnotationManager();
|
||||
const newAnnotation = {
|
||||
annotationUID: measurement.uid,
|
||||
// Not used in CS3D but stored in the CS3D state so that saving
|
||||
// can apply the predecessor consistently.
|
||||
predecessorImageId: measurement?.predecessorImageId,
|
||||
highlighted: false,
|
||||
isLocked: false,
|
||||
// This is used to force a re-render of the annotation to
|
||||
|
||||
@ -278,11 +278,13 @@ class SegmentationService extends PubSubService {
|
||||
viewportId: string,
|
||||
{
|
||||
segmentationId,
|
||||
predecessorImageId,
|
||||
type,
|
||||
config,
|
||||
suppressEvents = false,
|
||||
}: {
|
||||
segmentationId: string;
|
||||
predecessorImageId?: string;
|
||||
type?: csToolsEnums.SegmentationRepresentations;
|
||||
config?: {
|
||||
blendMode?: csEnums.BlendModes;
|
||||
@ -291,6 +293,9 @@ class SegmentationService extends PubSubService {
|
||||
}
|
||||
): Promise<void> {
|
||||
const segmentation = this.getSegmentation(segmentationId);
|
||||
if (segmentation && !segmentation.predecessorImageId && predecessorImageId) {
|
||||
segmentation.predecessorImageId = predecessorImageId;
|
||||
}
|
||||
const csViewport = this.getAndValidateViewport(viewportId);
|
||||
|
||||
if (!csViewport) {
|
||||
|
||||
@ -1090,8 +1090,10 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
|
||||
? csToolsEnums.SegmentationRepresentations.Labelmap
|
||||
: csToolsEnums.SegmentationRepresentations.Contour;
|
||||
|
||||
const { predecessorImageId } = displaySet;
|
||||
segmentationService.addSegmentationRepresentation(viewport.id, {
|
||||
segmentationId,
|
||||
predecessorImageId,
|
||||
type: representationType,
|
||||
});
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { utils } from '@ohif/core';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { getEnabledElement, StackViewport, BaseVolumeViewport } from '@cornerstonejs/core';
|
||||
@ -5,6 +6,8 @@ import { ToolGroupManager, segmentation, Enums } from '@cornerstonejs/tools';
|
||||
import { getEnabledElement as OHIFgetEnabledElement } from '../state';
|
||||
import { useSystem } from '@ohif/core/src';
|
||||
|
||||
const { downloadUrl } = utils;
|
||||
|
||||
const DEFAULT_SIZE = 512;
|
||||
const MAX_TEXTURE_SIZE = 10000;
|
||||
const VIEWPORT_ID = 'cornerstone-viewport-download-form';
|
||||
@ -218,10 +221,7 @@ const CornerstoneViewportDownloadForm = ({
|
||||
}
|
||||
|
||||
const canvas = await html2canvas(divForDownloadViewport as HTMLElement);
|
||||
const link = document.createElement('a');
|
||||
link.download = `${filename}.${fileType}`;
|
||||
link.href = canvas.toDataURL(`image/${fileType}`, 1.0);
|
||||
link.click();
|
||||
downloadUrl(canvas.toDataURL(`image/${fileType}`, 1.0));
|
||||
};
|
||||
|
||||
const ViewportDownloadFormNew = customizationService.getCustomization(
|
||||
|
||||
@ -13,6 +13,7 @@ Object.defineProperty(global, 'URL', {
|
||||
writable: true,
|
||||
value: {
|
||||
createObjectURL: jest.fn().mockReturnValue('mock-url'),
|
||||
revokeObjectURL: () => undefined,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const { downloadCsv } = utils;
|
||||
|
||||
export function generateSegmentationCSVReport(
|
||||
segmentationData,
|
||||
info: {
|
||||
@ -121,19 +125,7 @@ export function generateSegmentationCSVReport(
|
||||
csvString += formattedRow.join(',') + '\n';
|
||||
}
|
||||
|
||||
// Create a download link and trigger the download
|
||||
const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute(
|
||||
'download',
|
||||
`${segmentationData.label || 'Segmentation'}_Report_${new Date().toISOString().split('T')[0]}.csv`
|
||||
);
|
||||
link.style.visibility = 'hidden';
|
||||
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
downloadCsv(csvString, {
|
||||
filename: `${segmentationData.label || 'Segmentation'}_Report_${new Date().toISOString().split('T')[0]}.csv`,
|
||||
});
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ export const hasExportableLabelMapData = (
|
||||
SeriesInstanceUID
|
||||
);
|
||||
|
||||
return displaySet?.isReconstructable;
|
||||
return !!displaySet;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/i18n": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
|
||||
@ -28,7 +28,8 @@ function PanelStudyBrowser({
|
||||
const { servicesManager, commandsManager, extensionManager } = useSystem();
|
||||
const { displaySetService, customizationService } = servicesManager.services;
|
||||
const navigate = useNavigate();
|
||||
const studyMode = (customizationService.getCustomization('studyBrowser.studyMode') as string) || 'all';
|
||||
const studyMode =
|
||||
(customizationService.getCustomization('studyBrowser.studyMode') as string) || 'all';
|
||||
|
||||
const internalImageViewer = useImageViewer();
|
||||
const StudyInstanceUIDs = internalImageViewer.StudyInstanceUIDs;
|
||||
@ -384,7 +385,11 @@ function PanelStudyBrowser({
|
||||
|
||||
const displaySetInstanceUID = jumpToDisplaySet;
|
||||
// It is possible to navigate to a study not currently in view
|
||||
const thumbnailLocation = _findTabAndStudyOfDisplaySet(displaySetInstanceUID, tabs, activeTabName);
|
||||
const thumbnailLocation = _findTabAndStudyOfDisplaySet(
|
||||
displaySetInstanceUID,
|
||||
tabs,
|
||||
activeTabName
|
||||
);
|
||||
if (!thumbnailLocation) {
|
||||
return;
|
||||
}
|
||||
@ -489,7 +494,7 @@ function _mapDisplaySets(displaySets, displaySetLoadingState, thumbnailImageSrcM
|
||||
seriesNumber: ds.SeriesNumber,
|
||||
modality: ds.Modality,
|
||||
seriesDate: formatDate(ds.SeriesDate),
|
||||
numInstances: ds.numImageFrames,
|
||||
numInstances: ds.numImageFrames ?? ds.instances?.length,
|
||||
loadingProgress,
|
||||
countIcon: ds.countIcon,
|
||||
messages: ds.messages,
|
||||
@ -538,11 +543,13 @@ function _findTabAndStudyOfDisplaySet(
|
||||
tabs: TabsProps,
|
||||
currentTabName: string
|
||||
) {
|
||||
const current = tabs.find(tab => tab.name===currentTabName) || tabs[0];
|
||||
const current = tabs.find(tab => tab.name === currentTabName) || tabs[0];
|
||||
const biasedTabs = [current, ...tabs];
|
||||
|
||||
for (let t = 0; t < biasedTabs.length; t++) {
|
||||
const study = biasedTabs[t].studies.find(study => study.displaySets.find(ds => ds.displaySetInstanceUID ===displaySetInstanceUID));
|
||||
const study = biasedTabs[t].studies.find(study =>
|
||||
study.displaySets.find(ds => ds.displaySetInstanceUID === displaySetInstanceUID)
|
||||
);
|
||||
if (study) {
|
||||
return {
|
||||
tabName: biasedTabs[t].name,
|
||||
|
||||
@ -1,12 +1,39 @@
|
||||
import PROMPT_RESPONSES from '../utils/_shared/PROMPT_RESPONSES';
|
||||
|
||||
/**
|
||||
* Creates and shows a report dialog prompt.
|
||||
* The input for this is:
|
||||
* - `title` shown in the dialog
|
||||
* - `modality` being stored, used to query existing series
|
||||
* - `minSeriesNumber` is the start of new series of this modality type.
|
||||
* Will get set to 4000 if not determined by the modality
|
||||
* - predecessorImageId is the image id that this series was currently loaded
|
||||
* from. That allows defaulting the dialog to show the specified series instead
|
||||
* of always creating a new series.
|
||||
*
|
||||
* The response is:
|
||||
* - `value`, the default name of the object/series being created
|
||||
* - `dataSourceName`, where to store the object to
|
||||
* - `series`, is the series to store do, as referenced by a predecessorImageId value.
|
||||
* - `priorSeriesNumber` is the previously lowest series number at least minSeriesNumber
|
||||
* of all the seris of the given modality type.
|
||||
*
|
||||
* This should be provided to the DICOM encoder, which will get the predecessor
|
||||
* sequence from the metaData provider so that the saved series will replace
|
||||
* the existing instance in the same series.
|
||||
* This will be falsy for a new series.
|
||||
*/
|
||||
export default function CreateReportDialogPrompt({
|
||||
title = 'Create Report',
|
||||
modality = 'SR',
|
||||
minSeriesNumber = 0,
|
||||
predecessorImageId,
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
}): Promise<{
|
||||
value: string;
|
||||
dataSourceName: string;
|
||||
priorSeriesNumber?: number;
|
||||
series: string;
|
||||
action: (typeof PROMPT_RESPONSES)[keyof typeof PROMPT_RESPONSES];
|
||||
}> {
|
||||
@ -16,18 +43,33 @@ export default function CreateReportDialogPrompt({
|
||||
|
||||
const allowMultipleDataSources = window.config.allowMultiSelectExport;
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
minSeriesNumber ||=
|
||||
(modality === 'SR' && 3000) ||
|
||||
(modality === 'SEG' && 3100) ||
|
||||
(modality === 'RTSTRUCT' && 3200) ||
|
||||
4000;
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
uiDialogService.show({
|
||||
id: 'report-dialog',
|
||||
title,
|
||||
content: ReportDialog,
|
||||
contentProps: {
|
||||
dataSources: allowMultipleDataSources ? dataSources : undefined,
|
||||
onSave: async ({ reportName, dataSource: selectedDataSource, series }) => {
|
||||
predecessorImageId,
|
||||
minSeriesNumber,
|
||||
modality,
|
||||
onSave: async ({
|
||||
reportName,
|
||||
dataSource: selectedDataSource,
|
||||
series,
|
||||
priorSeriesNumber,
|
||||
}) => {
|
||||
resolve({
|
||||
value: reportName,
|
||||
dataSourceName: selectedDataSource,
|
||||
series,
|
||||
priorSeriesNumber,
|
||||
action: PROMPT_RESPONSES.CREATE_REPORT,
|
||||
});
|
||||
},
|
||||
|
||||
@ -11,28 +11,44 @@ type DataSource = {
|
||||
|
||||
type ReportDialogProps = {
|
||||
dataSources: DataSource[];
|
||||
modality?: string;
|
||||
predecessorImageId?: string;
|
||||
hide: () => void;
|
||||
onSave: (data: { reportName: string; dataSource: string | null; series: string | null }) => void;
|
||||
onSave: (data: {
|
||||
reportName: string;
|
||||
dataSource: string | null;
|
||||
series: string | null;
|
||||
priorSeriesNumber: number;
|
||||
}) => void;
|
||||
onCancel: () => void;
|
||||
};
|
||||
|
||||
function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps) {
|
||||
function ReportDialog({
|
||||
dataSources,
|
||||
modality = 'SR',
|
||||
predecessorImageId,
|
||||
minSeriesNumber = 3000,
|
||||
hide,
|
||||
onSave,
|
||||
onCancel,
|
||||
}: ReportDialogProps) {
|
||||
const { servicesManager } = useSystem();
|
||||
const [selectedDataSource, setSelectedDataSource] = useState<string | null>(
|
||||
dataSources?.[0]?.value ?? null
|
||||
);
|
||||
const [selectedSeries, setSelectedSeries] = useState<string | null>(null);
|
||||
const [reportName, setReportName] = useState('');
|
||||
|
||||
const { displaySetService } = servicesManager.services;
|
||||
|
||||
const [selectedSeries, setSelectedSeries] = useState<string | null>(predecessorImageId || null);
|
||||
const [reportName, setReportName] = useState('');
|
||||
|
||||
const seriesOptions = useMemo(() => {
|
||||
const displaySetsMap = displaySetService.getDisplaySetCache();
|
||||
const displaySets = Array.from(displaySetsMap.values());
|
||||
const options = displaySets
|
||||
.filter(ds => ds.Modality === 'SR')
|
||||
.filter(ds => ds.Modality === modality)
|
||||
.map(ds => ({
|
||||
value: ds.SeriesInstanceUID,
|
||||
value: ds.predecessorImageId || ds.SeriesInstanceUID,
|
||||
seriesNumber: isFinite(ds.SeriesNumber) ? ds.SeriesNumber : minSeriesNumber,
|
||||
description: ds.SeriesDescription,
|
||||
label: `${ds.SeriesDescription} ${ds.SeriesDate}/${ds.SeriesTime} ${ds.SeriesNumber}`,
|
||||
}));
|
||||
@ -41,11 +57,12 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps
|
||||
{
|
||||
value: null,
|
||||
description: null,
|
||||
seriesNumber: minSeriesNumber,
|
||||
label: 'Create new series',
|
||||
},
|
||||
...options,
|
||||
];
|
||||
}, [displaySetService]);
|
||||
}, [displaySetService, modality]);
|
||||
|
||||
useEffect(() => {
|
||||
const seriesOption = seriesOptions.find(s => s.value === selectedSeries);
|
||||
@ -58,6 +75,7 @@ function ReportDialog({ dataSources, hide, onSave, onCancel }: ReportDialogProps
|
||||
onSave({
|
||||
reportName,
|
||||
dataSource: selectedDataSource,
|
||||
priorSeriesNumber: Math.max(...seriesOptions.map(it => it.seriesNumber)),
|
||||
series: selectedSeries,
|
||||
});
|
||||
hide();
|
||||
|
||||
@ -32,7 +32,8 @@ export default function getDisplaySetsFromUnsupportedSeries(instances) {
|
||||
SOPClassUID: instance.SOPClassUID,
|
||||
SeriesDescription: instance.SeriesDescription || '',
|
||||
Modality: instance.Modality,
|
||||
numImageFrames: instances.length,
|
||||
instances,
|
||||
instance: instances[instance.length - 1],
|
||||
unsupported: true,
|
||||
SOPClassHandlerId: 'unsupported',
|
||||
isReconstructable: false,
|
||||
|
||||
@ -3,8 +3,6 @@ import { utils } from '@ohif/core';
|
||||
import createReportAsync from '../Actions/createReportAsync';
|
||||
import { createReportDialogPrompt } from '../Panels';
|
||||
import PROMPT_RESPONSES from './_shared/PROMPT_RESPONSES';
|
||||
import { getSRSeriesAndInstanceNumber } from './getSRSeriesAndInstanceNumber';
|
||||
import { getSeriesDateTime } from './getCurrentDicomDateTime';
|
||||
|
||||
const { filterAnd, filterMeasurementsByStudyUID, filterMeasurementsBySeriesUID } =
|
||||
utils.MeasurementFilters;
|
||||
@ -27,9 +25,14 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan
|
||||
} = ctx;
|
||||
let displaySetInstanceUIDs;
|
||||
|
||||
const measurementData = measurementService.getMeasurements(measurementFilter);
|
||||
const predecessorImageId = findPredecessorImageId(measurementData);
|
||||
|
||||
try {
|
||||
const promptResult = await createReportDialogPrompt({
|
||||
title: defaultSaveTitle,
|
||||
predecessorImageId,
|
||||
minSeriesNumber: 3000,
|
||||
extensionManager,
|
||||
servicesManager,
|
||||
});
|
||||
@ -37,16 +40,9 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan
|
||||
if (promptResult.action === PROMPT_RESPONSES.CREATE_REPORT) {
|
||||
const dataSources = extensionManager.getDataSources(promptResult.dataSourceName);
|
||||
const dataSource = dataSources[0];
|
||||
const measurementData = measurementService.getMeasurements(measurementFilter);
|
||||
|
||||
const SeriesDescription = promptResult.value || defaultSaveTitle;
|
||||
|
||||
const { SeriesNumber, InstanceNumber, referenceDisplaySet } = getSRSeriesAndInstanceNumber({
|
||||
displaySetService,
|
||||
SeriesInstanceUid: promptResult.series,
|
||||
});
|
||||
|
||||
const { SeriesDate, SeriesTime } = referenceDisplaySet ?? getSeriesDateTime();
|
||||
const { series, priorSeriesNumber, value: reportName } = promptResult;
|
||||
const SeriesDescription = reportName || defaultSaveTitle;
|
||||
|
||||
const getReport = async () => {
|
||||
return commandsManager.runCommand(
|
||||
@ -57,11 +53,8 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan
|
||||
additionalFindingTypes: ['ArrowAnnotate'],
|
||||
options: {
|
||||
SeriesDescription,
|
||||
SeriesNumber,
|
||||
InstanceNumber,
|
||||
SeriesInstanceUID: promptResult.series,
|
||||
SeriesDate,
|
||||
SeriesTime,
|
||||
SeriesNumber: 1 + priorSeriesNumber,
|
||||
predecessorImageId: series,
|
||||
},
|
||||
},
|
||||
'CORNERSTONE_STRUCTURED_REPORT'
|
||||
@ -90,4 +83,20 @@ async function promptSaveReport({ servicesManager, commandsManager, extensionMan
|
||||
}
|
||||
}
|
||||
|
||||
export function findPredecessorImageId(annotations) {
|
||||
let predecessorImageId;
|
||||
for (const annotation of annotations) {
|
||||
if (
|
||||
predecessorImageId &&
|
||||
annotation.predecessorImageId &&
|
||||
annotation.predecessorImageId !== predecessorImageId
|
||||
) {
|
||||
console.warn('Found multiple source predecessors, not defaulting to same series');
|
||||
return;
|
||||
}
|
||||
predecessorImageId ||= annotation.predecessorImageId;
|
||||
}
|
||||
return predecessorImageId;
|
||||
}
|
||||
|
||||
export default promptSaveReport;
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { callInputDialog } from '@ohif/extension-default';
|
||||
import { ExtensionManager, CommandsManager, DicomMetadataStore } from '@ohif/core';
|
||||
import { ExtensionManager, CommandsManager, DicomMetadataStore, utils } from '@ohif/core';
|
||||
import { DataRow } from '@ohif/ui-next';
|
||||
import { withTranslation, WithTranslation } from 'react-i18next';
|
||||
import { EVENTS as MicroscopyEvents } from '../../services/MicroscopyService';
|
||||
import dcmjs from 'dcmjs';
|
||||
import constructSR from '../../utils/constructSR';
|
||||
import { saveByteArray } from '../../utils/saveByteArray';
|
||||
|
||||
const { downloadDicom } = utils;
|
||||
|
||||
let saving = false;
|
||||
const { datasetToBuffer } = dcmjs.data;
|
||||
@ -197,7 +198,7 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
|
||||
if (dataSource.wadoRoot == 'saveDicom') {
|
||||
// download as DICOM file
|
||||
const part10Buffer = datasetToBuffer(dataset);
|
||||
saveByteArray(part10Buffer, `sr-microscopy.dcm`);
|
||||
downloadDicom(part10Buffer, { filename: `sr-microscopy.dcm` });
|
||||
} else {
|
||||
// Save into Web Data source
|
||||
const { StudyInstanceUID } = dataset;
|
||||
@ -277,11 +278,11 @@ function MicroscopyPanel(props: IMicroscopyPanelProps) {
|
||||
* Handler for "Edit" action of an annotation item
|
||||
* @param param0
|
||||
*/
|
||||
const onMeasurementItemEditHandler = ({ uid, isActive }: { uid: string; isActive: boolean }) => {
|
||||
const onMeasurementItemEditHandler = ({ uid }: { uid: string; isActive: boolean }) => {
|
||||
props.commandsManager.runCommand('setLabel', { uid }, 'MICROSCOPY');
|
||||
};
|
||||
|
||||
const onMeasurementDeleteHandler = ({ uid, isActive }: { uid: string; isActive: boolean }) => {
|
||||
const onMeasurementDeleteHandler = ({ uid }: { uid: string; isActive: boolean }) => {
|
||||
const roiAnnotation = microscopyService.getAnnotation(uid);
|
||||
microscopyService.removeAnnotation(roiAnnotation);
|
||||
};
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Trigger file download from an array buffer
|
||||
* @param buffer
|
||||
* @param filename
|
||||
*/
|
||||
export function saveByteArray(buffer: ArrayBuffer, filename: string) {
|
||||
const blob = new Blob([buffer], { type: 'application/dicom' });
|
||||
const link = document.createElement('a');
|
||||
link.href = window.URL.createObjectURL(blob);
|
||||
link.download = filename;
|
||||
link.click();
|
||||
}
|
||||
@ -32,7 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
|
||||
@ -32,14 +32,14 @@
|
||||
"start": "yarn run dev"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.90",
|
||||
"@ohif/extension-default": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"classnames": "2.5.1",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"lodash.debounce": "4.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "18.3.1",
|
||||
|
||||
@ -95,7 +95,7 @@ export default function PanelStudyBrowserTracking({
|
||||
seriesNumber: ds.SeriesNumber,
|
||||
modality: ds.Modality,
|
||||
seriesDate: ds.SeriesDate ? new Date(ds.SeriesDate).toLocaleDateString() : '',
|
||||
numInstances: ds.numImageFrames,
|
||||
numInstances: ds.numImageFrames ?? ds.instances?.length,
|
||||
loadingProgress,
|
||||
countIcon: ds.countIcon,
|
||||
messages: ds.messages,
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
"peerDependencies": {
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicom-parser": "1.8.21",
|
||||
"hammerjs": "2.0.8",
|
||||
"prop-types": "15.8.1",
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import { utils } from '@ohif/core';
|
||||
|
||||
const { downloadCsv } = utils;
|
||||
|
||||
export default function createAndDownloadTMTVReport(segReport, additionalReportRows, options = {}) {
|
||||
const firstReport = segReport[Object.keys(segReport)[0]];
|
||||
const columns = Object.keys(firstReport);
|
||||
@ -44,14 +48,7 @@ export default function createAndDownloadTMTVReport(segReport, additionalReportR
|
||||
csv.push(temp.join(','));
|
||||
});
|
||||
|
||||
const blob = new Blob([csv.join('\n')], {
|
||||
type: 'text/csv;charset=utf-8',
|
||||
downloadCsv(csv.join('\n'), {
|
||||
filename: options.filename ?? `${firstReport.PatientID}_tmtv.csv`,
|
||||
});
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = options.filename ?? `${firstReport.PatientID}_tmtv.csv`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
@ -42,8 +42,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.90",
|
||||
"@ohif/extension-default": "3.12.0-beta.90",
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
import { UltrasoundPleuraBLineTool, Enums as csToolsEnums } from '@cornerstonejs/tools';
|
||||
import { Types as OhifTypes } from '@ohif/core';
|
||||
import { Types as OhifTypes, utils } from '@ohif/core';
|
||||
import { eventTarget, triggerEvent, utilities } from '@cornerstonejs/core';
|
||||
import getInstanceByImageId from './getInstanceByImageId';
|
||||
import { setShowPercentage } from './PleuraBlinePercentage';
|
||||
|
||||
const { downloadBlob } = utils;
|
||||
|
||||
const { transformWorldToIndex } = utilities;
|
||||
|
||||
/**
|
||||
@ -13,7 +15,6 @@ const { transformWorldToIndex } = utilities;
|
||||
*/
|
||||
function commandsModule({
|
||||
servicesManager,
|
||||
commandsManager,
|
||||
}: OhifTypes.Extensions.ExtensionParams): OhifTypes.Extensions.CommandsModule {
|
||||
const { viewportGridService, toolGroupService, cornerstoneViewportService } =
|
||||
servicesManager.services as AppTypes.Services;
|
||||
@ -275,14 +276,9 @@ function commandsModule({
|
||||
|
||||
// Create a blob with the JSON data
|
||||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||
|
||||
// Create a URL for the blob
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
// Create an anchor element
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `ultrasound_annotations_${new Date().toISOString().slice(0, 10)}.json`;
|
||||
downloadBlob(blob, {
|
||||
filename: `ultrasound_annotations_${new Date().toISOString().slice(0, 10)}.json`,
|
||||
});
|
||||
|
||||
// Append to the document, click to download, and remove
|
||||
document.body.appendChild(a);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
"author": "OHIF Contributors",
|
||||
"license": "MIT",
|
||||
"main": "dist/ohif-mode-basic.js",
|
||||
"module": "src/index.tsx",
|
||||
"files": [
|
||||
"dist/**",
|
||||
"public/**",
|
||||
@ -14,7 +15,6 @@
|
||||
"keywords": [
|
||||
"ohif-mode"
|
||||
],
|
||||
"module": "src/index.tsx",
|
||||
"engines": {
|
||||
"node": ">=14",
|
||||
"npm": ">=6",
|
||||
|
||||
@ -21,12 +21,17 @@ export const ohif = {
|
||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||
sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',
|
||||
thumbnailList: '@ohif/extension-default.panelModule.seriesList',
|
||||
hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default',
|
||||
wsiSopClassHandler:
|
||||
'@ohif/extension-cornerstone.sopClassHandlerModule.DicomMicroscopySopClassHandler',
|
||||
};
|
||||
|
||||
export const cornerstone = {
|
||||
measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement',
|
||||
labelMapSegmentationPanel:
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap',
|
||||
contourSegmentationPanel:
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour',
|
||||
segmentation: '@ohif/extension-cornerstone.panelModule.panelSegmentation',
|
||||
viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone',
|
||||
};
|
||||
@ -62,6 +67,11 @@ export const dicomRT = {
|
||||
sopClassHandler: '@ohif/extension-cornerstone-dicom-rt.sopClassHandlerModule.dicom-rt',
|
||||
};
|
||||
|
||||
export const segmentation = {
|
||||
sopClassHandler: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg',
|
||||
};
|
||||
|
||||
export const extensionDependencies = {
|
||||
// Can derive the versions at least process.env.from npm_package_version
|
||||
'@ohif/extension-default': '^3.0.0',
|
||||
|
||||
@ -42,7 +42,8 @@
|
||||
"@ohif/extension-default": "3.12.0-beta.90",
|
||||
"@ohif/extension-dicom-pdf": "3.12.0-beta.90",
|
||||
"@ohif/extension-dicom-video": "3.12.0-beta.90",
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.90"
|
||||
"@ohif/extension-measurement-tracking": "3.12.0-beta.90",
|
||||
"@ohif/mode-basic": "3.12.0-beta.90"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
|
||||
@ -33,6 +33,12 @@ const config = {
|
||||
amd: '@ohif/core',
|
||||
root: '@ohif/core',
|
||||
},
|
||||
'@ohif/mode-basic': {
|
||||
commonjs2: '@ohif/mode-basic',
|
||||
commonjs: '@ohif/mode-basic',
|
||||
amd: '@ohif/mode-basic',
|
||||
root: '@ohif/mode-basic',
|
||||
},
|
||||
'@ohif/ui': {
|
||||
commonjs2: '@ohif/ui',
|
||||
commonjs: '@ohif/ui',
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
"test:unit:ci": "jest --ci --runInBand --collectCoverage --passWithNoTests"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ohif/mode-basic": "3.12.0-beta.90",
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.90",
|
||||
"@ohif/extension-cornerstone-dicom-rt": "3.12.0-beta.90",
|
||||
|
||||
@ -2,42 +2,8 @@ import { id } from './id';
|
||||
import toolbarButtons from './toolbarButtons';
|
||||
import initToolGroups from './initToolGroups';
|
||||
import setUpAutoTabSwitchHandler from './utils/setUpAutoTabSwitchHandler';
|
||||
|
||||
const ohif = {
|
||||
layout: '@ohif/extension-default.layoutTemplateModule.viewerLayout',
|
||||
sopClassHandler: '@ohif/extension-default.sopClassHandlerModule.stack',
|
||||
hangingProtocol: '@ohif/extension-default.hangingProtocolModule.default',
|
||||
leftPanel: '@ohif/extension-default.panelModule.seriesList',
|
||||
};
|
||||
|
||||
const cornerstone = {
|
||||
viewport: '@ohif/extension-cornerstone.viewportModule.cornerstone',
|
||||
labelMapSegmentationPanel:
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsLabelMap',
|
||||
contourSegmentationPanel:
|
||||
'@ohif/extension-cornerstone.panelModule.panelSegmentationWithToolsContour',
|
||||
measurements: '@ohif/extension-cornerstone.panelModule.panelMeasurement',
|
||||
};
|
||||
|
||||
const segmentation = {
|
||||
sopClassHandler: '@ohif/extension-cornerstone-dicom-seg.sopClassHandlerModule.dicom-seg',
|
||||
viewport: '@ohif/extension-cornerstone-dicom-seg.viewportModule.dicom-seg',
|
||||
};
|
||||
|
||||
const dicomRT = {
|
||||
viewport: '@ohif/extension-cornerstone-dicom-rt.viewportModule.dicom-rt',
|
||||
sopClassHandler: '@ohif/extension-cornerstone-dicom-rt.sopClassHandlerModule.dicom-rt',
|
||||
};
|
||||
/**
|
||||
* Just two dependencies to be able to render a viewport with panels in order
|
||||
* to make sure that the mode is working.
|
||||
*/
|
||||
const extensionDependencies = {
|
||||
'@ohif/extension-default': '^3.0.0',
|
||||
'@ohif/extension-cornerstone': '^3.0.0',
|
||||
'@ohif/extension-cornerstone-dicom-seg': '^3.0.0',
|
||||
'@ohif/extension-cornerstone-dicom-rt': '^3.0.0',
|
||||
};
|
||||
import { ohif, cornerstone, extensionDependencies, dicomRT, segmentation } from '@ohif/mode-basic';
|
||||
export * from './toolbarButtons';
|
||||
|
||||
function modeFactory({ modeConfiguration }) {
|
||||
const _unsubscriptions = [];
|
||||
@ -236,7 +202,7 @@ function modeFactory({ modeConfiguration }) {
|
||||
return {
|
||||
id: ohif.layout,
|
||||
props: {
|
||||
leftPanels: [ohif.leftPanel],
|
||||
leftPanels: [ohif.thumbnailList],
|
||||
leftPanelResizable: true,
|
||||
rightPanels: [
|
||||
cornerstone.labelMapSegmentationPanel,
|
||||
|
||||
@ -20,7 +20,7 @@ const callbacks = (toolName: string) => [
|
||||
},
|
||||
];
|
||||
|
||||
const toolbarButtons: Button[] = [
|
||||
export const toolbarButtons: Button[] = [
|
||||
{
|
||||
id: 'AdvancedRenderingControls',
|
||||
uiType: 'ohif.advancedRenderingControls',
|
||||
|
||||
@ -34,8 +34,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/tools": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/tools": "4.11.3",
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/extension-cornerstone-dicom-sr": "3.12.0-beta.90",
|
||||
"@ohif/extension-ultrasound-pleura-bline": "3.12.0-beta.90",
|
||||
|
||||
@ -33,8 +33,8 @@
|
||||
"cli": "node ./platform/cli/src/index.js",
|
||||
"build:ui:deploy-preview": "lerna run build:ui:deploy-preview --stream",
|
||||
"build:demo": "lerna run build:viewer:demo --stream",
|
||||
"build:package-all": "lerna run build:package --parallel --stream",
|
||||
"build:package-all-1": "lerna run build:package-1 --parallel --stream",
|
||||
"build:package-all": "lerna run build:package --stream --concurrency 4",
|
||||
"build:package-all-1": "lerna run build:package-1 --stream --concurrency 4",
|
||||
"dev:fast": "cd platform/app && yarn run dev:fast",
|
||||
"show:config": "echo Config is $APP_CONFIG on $PUBLIC_URL",
|
||||
"dev": "lerna run dev:viewer --stream",
|
||||
@ -47,7 +47,8 @@
|
||||
"orthanc:up": "docker compose -f platform/app/.recipes/Nginx-Orthanc/docker-compose.yml up",
|
||||
"install:dev": "cp -f yarn.lock addOns/yarn.lock && cd addOns && yarn install --frozen-lockfile --modules-folder ../node_modules",
|
||||
"install:update-lockfile": "yarn install && bun install --config=./bunfig.update-lockfile.toml",
|
||||
"install:yarn:frozen-lockfile": "yarn install --frozen-lockfile",
|
||||
"install:yarn:frozen-lockfile": "echo Deprecated - use install:frozen && yarn install --frozen-lockfile",
|
||||
"install:frozen": "yarn install --frozen-lockfile",
|
||||
"audit": "bun audit --ignore=GHSA-5j98-mcp5-4vw2",
|
||||
"preinstall": "node preinstall.js",
|
||||
"start": "yarn run dev",
|
||||
@ -138,7 +139,7 @@
|
||||
"commander": "8.3.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cross-spawn": "7.0.6",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"path-to-regexp": "0.1.12",
|
||||
"nth-check": "2.1.1",
|
||||
"trim-newlines": "5.0.0",
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.2",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.3",
|
||||
"@emotion/serialize": "1.3.3",
|
||||
"@ohif/core": "3.12.0-beta.90",
|
||||
"@ohif/extension-cornerstone": "3.12.0-beta.90",
|
||||
@ -79,7 +79,7 @@
|
||||
"classnames": "2.5.1",
|
||||
"core-js": "3.45.1",
|
||||
"cornerstone-math": "0.1.10",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"detect-gpu": "4.0.50",
|
||||
"dicom-parser": "1.8.21",
|
||||
"dotenv-webpack": "1.8.0",
|
||||
|
||||
@ -126,6 +126,29 @@ window.config = {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
friendlyName: 'StaticWado default data',
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomweb',
|
||||
sourceName: 'dicomweb',
|
||||
configuration: {
|
||||
name: 'DCM4CHEE',
|
||||
wadoUriRoot: '/dicomweb',
|
||||
qidoRoot: '/dicomweb',
|
||||
wadoRoot: '/dicomweb',
|
||||
qidoSupportsIncludeField: false,
|
||||
supportsReject: false,
|
||||
imageRendering: 'wadors',
|
||||
thumbnailRendering: 'wadors',
|
||||
enableStudyLazyLoad: true,
|
||||
supportsFuzzyMatching: false,
|
||||
supportsWildcard: true,
|
||||
staticWado: true,
|
||||
bulkDataURI: {
|
||||
enabled: true,
|
||||
relativeResolution: 'studies',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
namespace: '@ohif/extension-default.dataSourcesModule.dicomwebproxy',
|
||||
|
||||
@ -37,15 +37,15 @@
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit": "1.2.2",
|
||||
"@cornerstonejs/codec-openjpeg": "1.3.0",
|
||||
"@cornerstonejs/codec-openjph": "2.4.7",
|
||||
"@cornerstonejs/core": "4.11.2",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.2",
|
||||
"@cornerstonejs/core": "4.11.3",
|
||||
"@cornerstonejs/dicom-image-loader": "4.11.3",
|
||||
"@ohif/ui": "3.12.0-beta.90",
|
||||
"cornerstone-math": "0.1.9",
|
||||
"dicom-parser": "1.8.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.2",
|
||||
"dcmjs": "0.43.1",
|
||||
"dcmjs": "0.45.0",
|
||||
"dicomweb-client": "0.10.4",
|
||||
"gl-matrix": "3.4.3",
|
||||
"immutability-helper": "3.1.1",
|
||||
|
||||
@ -6,7 +6,7 @@ import DicomMetadataStore from '../services/DicomMetadataStore';
|
||||
import fetchPaletteColorLookupTableData from '../utils/metadataProvider/fetchPaletteColorLookupTableData';
|
||||
import toNumber from '../utils/toNumber';
|
||||
import combineFrameInstance from '../utils/combineFrameInstance';
|
||||
import formatPN from '../utils/formatPN';
|
||||
|
||||
const { calibratedPixelSpacingMetadataProvider, getPixelSpacingInformation } = utilities;
|
||||
|
||||
class MetadataProvider {
|
||||
@ -123,33 +123,21 @@ class MetadataProvider {
|
||||
|
||||
switch (wadoImageLoaderTag) {
|
||||
case WADO_IMAGE_LOADER_TAGS.GENERAL_SERIES_MODULE:
|
||||
const { SeriesDate, SeriesTime } = instance;
|
||||
|
||||
let seriesDate;
|
||||
let seriesTime;
|
||||
|
||||
if (SeriesDate) {
|
||||
seriesDate = dicomParser.parseDA(SeriesDate);
|
||||
}
|
||||
|
||||
if (SeriesTime) {
|
||||
seriesTime = dicomParser.parseTM(SeriesTime);
|
||||
}
|
||||
|
||||
metadata = {
|
||||
modality: instance.Modality,
|
||||
seriesInstanceUID: instance.SeriesInstanceUID,
|
||||
seriesNumber: toNumber(instance.SeriesNumber),
|
||||
studyInstanceUID: instance.StudyInstanceUID,
|
||||
seriesDate,
|
||||
seriesTime,
|
||||
seriesDescription: instance.SeriesDescription,
|
||||
seriesDate: instance.SeriesDate,
|
||||
seriesTime: instance.SeriesTime,
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.PATIENT_STUDY_MODULE:
|
||||
metadata = {
|
||||
patientAge: toNumber(instance.PatientAge),
|
||||
patientSize: toNumber(instance.PatientSize),
|
||||
patientWeight: toNumber(instance.PatientWeight),
|
||||
patientAge: instance.PatientAge,
|
||||
patientSize: instance.PatientSize,
|
||||
patientWeight: instance.PatientWeight,
|
||||
};
|
||||
break;
|
||||
case WADO_IMAGE_LOADER_TAGS.PATIENT_DEMOGRAPHIC_MODULE:
|
||||
@ -346,16 +334,13 @@ class MetadataProvider {
|
||||
break;
|
||||
|
||||
case WADO_IMAGE_LOADER_TAGS.PATIENT_MODULE:
|
||||
const { PatientName } = instance;
|
||||
|
||||
let patientName;
|
||||
if (PatientName) {
|
||||
patientName = formatPN(PatientName);
|
||||
}
|
||||
|
||||
metadata = {
|
||||
patientName,
|
||||
patientName: instance.PatientName,
|
||||
patientId: instance.PatientID,
|
||||
patientSex: instance.PatientSex,
|
||||
patientBirthDate: instance.PatientBirthDate,
|
||||
issuerOfPatientId: instance.IssuerOfPatientID,
|
||||
otherPatientIDsSequence: instance.OtherPatientIDsSequence,
|
||||
};
|
||||
|
||||
break;
|
||||
@ -373,9 +358,11 @@ class MetadataProvider {
|
||||
case WADO_IMAGE_LOADER_TAGS.GENERAL_STUDY_MODULE:
|
||||
metadata = {
|
||||
studyDescription: instance.StudyDescription,
|
||||
studyInstanceUID: instance.StudyInstanceUID,
|
||||
studyDate: instance.StudyDate,
|
||||
studyTime: instance.StudyTime,
|
||||
accessionNumber: instance.AccessionNumber,
|
||||
studyId: instance.StudyID,
|
||||
};
|
||||
|
||||
break;
|
||||
@ -512,9 +499,57 @@ class MetadataProvider {
|
||||
|
||||
const metadataProvider = new MetadataProvider();
|
||||
|
||||
DicomMetadataStore.setMetaDataProvider(metadataProvider);
|
||||
|
||||
export default metadataProvider;
|
||||
|
||||
const WADO_IMAGE_LOADER_TAGS = {
|
||||
// dicomImageLoader specific
|
||||
GENERAL_SERIES_MODULE: 'generalSeriesModule',
|
||||
PATIENT_STUDY_MODULE: 'patientStudyModule',
|
||||
IMAGE_PIXEL_MODULE: 'imagePixelModule',
|
||||
VOI_LUT_MODULE: 'voiLutModule',
|
||||
MODALITY_LUT_MODULE: 'modalityLutModule',
|
||||
SOP_COMMON_MODULE: 'sopCommonModule',
|
||||
PET_IMAGE_MODULE: 'petImageModule',
|
||||
PET_ISOTOPE_MODULE: 'petIsotopeModule',
|
||||
PET_SERIES_MODULE: 'petSeriesModule',
|
||||
OVERLAY_PLANE_MODULE: 'overlayPlaneModule',
|
||||
PATIENT_DEMOGRAPHIC_MODULE: 'patientDemographicModule',
|
||||
|
||||
// react-cornerstone-viewport specific
|
||||
PATIENT_MODULE: 'patientModule',
|
||||
GENERAL_IMAGE_MODULE: 'generalImageModule',
|
||||
GENERAL_STUDY_MODULE: 'generalStudyModule',
|
||||
CINE_MODULE: 'cineModule',
|
||||
CALIBRATION_MODULE: 'calibrationModule',
|
||||
|
||||
// Computed tags for new data
|
||||
// Note these get returned in naturalized format
|
||||
IMAGE_SOP_INSTANCE_REFERENCE: 'ImageSopInstanceReference',
|
||||
};
|
||||
|
||||
const WADO_IMAGE_LOADER = {
|
||||
/** Returns information on the current frame reference */
|
||||
frameModule: instance => {
|
||||
const {
|
||||
frameNumber = 1,
|
||||
numberOfFrames = 1,
|
||||
SOPClassUID: sopClassUID,
|
||||
SOPInstanceUID: sopInstanceUID,
|
||||
SeriesInstanceUID: seriesInstanceUID,
|
||||
StudyInstanceUID: studyInstanceUID,
|
||||
} = instance;
|
||||
return {
|
||||
frameNumber,
|
||||
numberOfFrames,
|
||||
sopClassUID,
|
||||
sopInstanceUID,
|
||||
seriesInstanceUID,
|
||||
studyInstanceUID,
|
||||
};
|
||||
},
|
||||
|
||||
imagePlaneModule: instance => {
|
||||
const { ImageOrientationPatient, ImagePositionPatient } = instance;
|
||||
|
||||
@ -590,26 +625,4 @@ const WADO_IMAGE_LOADER = {
|
||||
},
|
||||
};
|
||||
|
||||
const WADO_IMAGE_LOADER_TAGS = {
|
||||
// dicomImageLoader specific
|
||||
GENERAL_SERIES_MODULE: 'generalSeriesModule',
|
||||
PATIENT_STUDY_MODULE: 'patientStudyModule',
|
||||
IMAGE_PIXEL_MODULE: 'imagePixelModule',
|
||||
VOI_LUT_MODULE: 'voiLutModule',
|
||||
MODALITY_LUT_MODULE: 'modalityLutModule',
|
||||
SOP_COMMON_MODULE: 'sopCommonModule',
|
||||
PET_IMAGE_MODULE: 'petImageModule',
|
||||
PET_ISOTOPE_MODULE: 'petIsotopeModule',
|
||||
PET_SERIES_MODULE: 'petSeriesModule',
|
||||
OVERLAY_PLANE_MODULE: 'overlayPlaneModule',
|
||||
PATIENT_DEMOGRAPHIC_MODULE: 'patientDemographicModule',
|
||||
|
||||
// react-cornerstone-viewport specific
|
||||
PATIENT_MODULE: 'patientModule',
|
||||
GENERAL_IMAGE_MODULE: 'generalImageModule',
|
||||
GENERAL_STUDY_MODULE: 'generalStudyModule',
|
||||
CINE_MODULE: 'cineModule',
|
||||
CALIBRATION_MODULE: 'calibrationModule',
|
||||
};
|
||||
|
||||
const INSTANCE = 'instance';
|
||||
|
||||
@ -50,6 +50,12 @@ const _model = {
|
||||
studies: [],
|
||||
};
|
||||
|
||||
let metaDataProvider;
|
||||
|
||||
function _setMetaDataProvider(metaData) {
|
||||
metaDataProvider = metaData;
|
||||
}
|
||||
|
||||
function _getStudyInstanceUIDs() {
|
||||
return _model.studies.map(aStudy => aStudy.StudyInstanceUID);
|
||||
}
|
||||
@ -102,7 +108,17 @@ function _getInstance(StudyInstanceUID, SeriesInstanceUID, SOPInstanceUID) {
|
||||
return series.getInstance(SOPInstanceUID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the frame module from the OHIF metadata provider, and then
|
||||
* uses the study/series/instance uids to get the instance data.
|
||||
*/
|
||||
function _getInstanceByImageId(imageId) {
|
||||
const metadataResult = metaDataProvider?.get('frameModule', imageId);
|
||||
if (metadataResult) {
|
||||
const { studyInstanceUID, seriesInstanceUID, sopInstanceUID } = metadataResult;
|
||||
return _getInstance(studyInstanceUID, seriesInstanceUID, sopInstanceUID);
|
||||
}
|
||||
console.warn('No metadata result found for', imageId, 'looking through instances');
|
||||
for (const study of _model.studies) {
|
||||
for (const series of study.series) {
|
||||
for (const instance of series.instances) {
|
||||
@ -186,7 +202,8 @@ const BaseImplementation = {
|
||||
study = _model.studies[_model.studies.length - 1];
|
||||
}
|
||||
|
||||
naturalizedDataset = numberOfFrames > 1 ? addProxyFields(naturalizedDataset) : naturalizedDataset;
|
||||
naturalizedDataset =
|
||||
numberOfFrames > 1 ? addProxyFields(naturalizedDataset) : naturalizedDataset;
|
||||
|
||||
study.addInstanceToSeries(naturalizedDataset);
|
||||
},
|
||||
@ -289,6 +306,7 @@ const BaseImplementation = {
|
||||
getInstance: _getInstance,
|
||||
getInstanceByImageId: _getInstanceByImageId,
|
||||
updateMetadataForSeries: _updateMetadataForSeries,
|
||||
setMetaDataProvider: _setMetaDataProvider,
|
||||
};
|
||||
const DicomMetadataStore = Object.assign(
|
||||
// get study
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ExtensionManager } from '../../extensions';
|
||||
import { DisplaySet, InstanceMetadata } from '../../types';
|
||||
import { DisplaySet, InstanceMetadata, ReferencedSeriesSequence } from '../../types';
|
||||
import { PubSubService } from '../_shared/pubSubServiceInterface';
|
||||
import EVENTS from './EVENTS';
|
||||
|
||||
@ -115,16 +115,52 @@ export default class DisplaySetService extends PubSubService {
|
||||
return this.activeDisplaySets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the set of display sets with this series instance UID
|
||||
*
|
||||
* <b>WARNING: Do not use this method when you have a referenced series sequence
|
||||
* as this method does NOT check sop instances. Instead, use getDisplaySetsForReference
|
||||
* to get those with the correct sop instances in them.</b>
|
||||
*/
|
||||
public getDisplaySetsForSeries = (seriesInstanceUID: string): DisplaySet[] => {
|
||||
return [...displaySetCache.values()].filter(
|
||||
displaySet => displaySet.SeriesInstanceUID === seriesInstanceUID
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a reference to a series/sop, returns the set of display sets
|
||||
* containing an instance from the references.
|
||||
*/
|
||||
public getDisplaySetsForReferences = (
|
||||
references: ReferencedSeriesSequence | ReferencedSeriesSequence[]
|
||||
): DisplaySet[] => {
|
||||
const mapSeriesReferences = new Map<string, Set<string>>();
|
||||
const referenceArr = Array.isArray(references) ? references : [references];
|
||||
for (const seriesRef of referenceArr) {
|
||||
const { SeriesInstanceUID, ReferencedInstanceSequence } = seriesRef;
|
||||
if (!mapSeriesReferences.has(SeriesInstanceUID)) {
|
||||
mapSeriesReferences.set(SeriesInstanceUID, new Set<string>());
|
||||
}
|
||||
const sops = mapSeriesReferences.get(SeriesInstanceUID);
|
||||
for (const sopReference of ReferencedInstanceSequence) {
|
||||
sops.add(sopReference.ReferencedSOPInstanceUID);
|
||||
}
|
||||
}
|
||||
|
||||
return [...displaySetCache.values()].filter(displaySet => {
|
||||
const sopReferences = mapSeriesReferences.get(displaySet.SeriesInstanceUID);
|
||||
if (!sopReferences || !displaySet.instances) {
|
||||
return;
|
||||
}
|
||||
return displaySet.instances.some(instance => sopReferences.has(instance.SOPInstanceUID));
|
||||
});
|
||||
};
|
||||
|
||||
public getDisplaySetForSOPInstanceUID(
|
||||
sopInstanceUID: string,
|
||||
seriesInstanceUID: string,
|
||||
frameNumber?: number
|
||||
_frameNumber?: number
|
||||
): DisplaySet {
|
||||
const displaySets = seriesInstanceUID
|
||||
? this.getDisplaySetsForSeries(seriesInstanceUID)
|
||||
|
||||
@ -434,10 +434,13 @@ class MeasurementService extends PubSubService {
|
||||
|
||||
const internalUID = data.uid || guid();
|
||||
|
||||
const annotationData = data.annotation.data;
|
||||
const {
|
||||
annotation: { predecessorImageId, data: annotationData },
|
||||
} = data;
|
||||
|
||||
const newMeasurement = {
|
||||
finding: annotationData.finding,
|
||||
predecessorImageId,
|
||||
findingSites: annotationData.findingSites,
|
||||
site: annotationData.findingSites?.[0],
|
||||
...measurement,
|
||||
@ -643,8 +646,12 @@ class MeasurementService extends PubSubService {
|
||||
if (referencedImageId) {
|
||||
measurement.referencedImageId = referencedImageId;
|
||||
const instance = DicomMetadataStore.getInstanceByImageId(referencedImageId);
|
||||
measurement.referenceStudyUID = instance.StudyInstanceUID;
|
||||
measurement.referenceSeriesUID = instance.SeriesInstanceUID;
|
||||
if (!instance) {
|
||||
console.warn("Didn't find instance for", referencedImageId);
|
||||
} else {
|
||||
measurement.referenceStudyUID = instance.StudyInstanceUID;
|
||||
measurement.referenceSeriesUID = instance.SeriesInstanceUID;
|
||||
}
|
||||
}
|
||||
|
||||
if (displaySetInstanceUID) {
|
||||
@ -683,8 +690,7 @@ class MeasurementService extends PubSubService {
|
||||
removeMany(measurementUIDs: string[]): void {
|
||||
const measurements = [];
|
||||
for (const measurementUID of measurementUIDs) {
|
||||
const measurement =
|
||||
this.measurements.get(measurementUID)
|
||||
const measurement = this.measurements.get(measurementUID);
|
||||
|
||||
if (!measurementUID || !measurement) {
|
||||
console.debug(`No uid provided, or unable to find measurement by uid.`);
|
||||
@ -727,8 +733,7 @@ class MeasurementService extends PubSubService {
|
||||
*/
|
||||
|
||||
public jumpToMeasurement(viewportId: string, measurementUID: string): void {
|
||||
const measurement =
|
||||
this.measurements.get(measurementUID)
|
||||
const measurement = this.measurements.get(measurementUID);
|
||||
|
||||
if (!measurement) {
|
||||
log.warn(`No measurement uid, or unable to find by uid.`);
|
||||
@ -850,8 +855,7 @@ class MeasurementService extends PubSubService {
|
||||
};
|
||||
|
||||
public toggleLockMeasurement(measurementUID: string): void {
|
||||
const measurement =
|
||||
this.measurements.get(measurementUID)
|
||||
const measurement = this.measurements.get(measurementUID);
|
||||
|
||||
if (!measurement) {
|
||||
console.debug(`No measurement found for uid: ${measurementUID}`);
|
||||
@ -868,8 +872,7 @@ class MeasurementService extends PubSubService {
|
||||
}
|
||||
|
||||
public toggleVisibilityMeasurement(measurementUID: string, visibility?: boolean): void {
|
||||
const measurement =
|
||||
this.measurements.get(measurementUID)
|
||||
const measurement = this.measurements.get(measurementUID);
|
||||
|
||||
if (!measurement) {
|
||||
console.debug(`No measurement found for uid: ${measurementUID}`);
|
||||
@ -893,8 +896,7 @@ class MeasurementService extends PubSubService {
|
||||
}
|
||||
|
||||
public updateColorMeasurement(measurementUID: string, color: number[]): void {
|
||||
const measurement =
|
||||
this.measurements.get(measurementUID)
|
||||
const measurement = this.measurements.get(measurementUID);
|
||||
|
||||
if (!measurement) {
|
||||
console.debug(`No measurement found for uid: ${measurementUID}`);
|
||||
|
||||
@ -1,5 +1,15 @@
|
||||
import { InstanceMetadata } from './StudyMetadata';
|
||||
|
||||
export type ReferencedInstance = {
|
||||
ReferencedSOPClassUID: string;
|
||||
ReferencedSOPInstanceUID: string;
|
||||
};
|
||||
|
||||
export type ReferencedSeriesSequence = {
|
||||
SeriesInstanceUID: string;
|
||||
ReferencedInstanceSequence: ReferencedInstance[];
|
||||
};
|
||||
|
||||
export type DisplaySet = {
|
||||
displaySetInstanceUID: string;
|
||||
instances: InstanceMetadata[];
|
||||
@ -16,6 +26,8 @@ export type DisplaySet = {
|
||||
label?: string;
|
||||
/** Flag indicating if this is an overlay display set (e.g., SEG, RTSTRUCT) */
|
||||
isOverlayDisplaySet?: boolean;
|
||||
/** Flag indicating this is a derived dataset */
|
||||
isDerived?: boolean;
|
||||
/** flag indicating if it supports window level */
|
||||
supportsWindowLevel?: boolean;
|
||||
|
||||
@ -49,6 +61,21 @@ export type DisplaySet = {
|
||||
SeriesTime?: string;
|
||||
instance?: InstanceMetadata;
|
||||
|
||||
/**
|
||||
* The predecessor image id refers to the SOP instance that is currently loaded
|
||||
* into this display set for SEG/SR/RTSTRUCT type values. The name is chosen
|
||||
* for consistency when this value is used as the origin instance
|
||||
* for saving a new instance intended to replace this instance where the
|
||||
* new instance has a "predecessor sequence".
|
||||
*/
|
||||
predecessorImageId?: string;
|
||||
|
||||
/**
|
||||
* isLoaded is used for display sets containing a load operation that
|
||||
* is required before the display set can be shown. This is separate from
|
||||
* isHydrated, which means it is loaded into view.
|
||||
*/
|
||||
isLoaded?: boolean;
|
||||
isHydrated?: boolean;
|
||||
isRehydratable?: boolean;
|
||||
};
|
||||
|
||||
@ -44,6 +44,13 @@ export function addProxyFields(instance) {
|
||||
enumerable: true,
|
||||
get: () => {
|
||||
return instance._parentInstance?.[fieldProxy] ?? instance._parentInstance?._shared?.[fieldProxy];
|
||||
},
|
||||
set: (value) => {
|
||||
Object.defineProperty(instance,fieldProxy, {
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
value,
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
36
platform/core/src/utils/downloadBlob.ts
Normal file
36
platform/core/src/utils/downloadBlob.ts
Normal file
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Converts a blob to a URL and downloads immediate
|
||||
*/
|
||||
export function downloadBlob(content, options?) {
|
||||
const url = URL.createObjectURL(content);
|
||||
downloadUrl(url, options);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger file download from an array buffer
|
||||
* @param buffer
|
||||
* @param filename
|
||||
*/
|
||||
export function downloadDicom(buffer: ArrayBuffer, options) {
|
||||
const blob = new Blob([buffer], { type: 'application/dicom' });
|
||||
downloadBlob(blob, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a URL
|
||||
*/
|
||||
export function downloadUrl(url, options?) {
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', url);
|
||||
const filename = options?.filename || 'file.dcm';
|
||||
link.setAttribute('download', filename);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
|
||||
export function downloadCsv(csvString: string, options?) {
|
||||
const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' });
|
||||
downloadBlob(blob, options);
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import { downloadUrl } from './downloadBlob';
|
||||
import { DicomMetadataStore } from '../services/DicomMetadataStore/DicomMetadataStore';
|
||||
import formatPN from './formatPN';
|
||||
|
||||
@ -97,10 +98,5 @@ function _getCommonRowItems(measurement, seriesMetadata) {
|
||||
|
||||
function _createAndDownloadFile(csvContent) {
|
||||
const encodedUri = encodeURI(csvContent);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', encodedUri);
|
||||
link.setAttribute('download', 'MeasurementReport.csv');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
downloadUrl(encodedUri, { filename: 'MeasurementReport.csv' });
|
||||
}
|
||||
|
||||
@ -48,6 +48,8 @@ import areAllImageOrientationsEqual from './areAllImageOrientationsEqual';
|
||||
import { structuredCloneWithFunctions } from './structuredCloneWithFunctions';
|
||||
import { buildButtonCommands } from './buildButtonCommands';
|
||||
|
||||
import { downloadBlob, downloadUrl, downloadCsv, downloadDicom } from './downloadBlob';
|
||||
|
||||
// Commented out unused functionality.
|
||||
// Need to implement new mechanism for derived displaySets using the displaySetManager.
|
||||
|
||||
@ -99,6 +101,10 @@ const utils = {
|
||||
getClosestOrientationFromIOP,
|
||||
calculateScanAxisNormal,
|
||||
areAllImageOrientationsEqual,
|
||||
downloadBlob,
|
||||
downloadUrl,
|
||||
downloadCsv,
|
||||
downloadDicom,
|
||||
};
|
||||
|
||||
export {
|
||||
@ -137,6 +143,10 @@ export {
|
||||
MeasurementFilters,
|
||||
getClosestOrientationFromIOP,
|
||||
buildButtonCommands,
|
||||
downloadBlob,
|
||||
downloadUrl,
|
||||
downloadCsv,
|
||||
downloadDicom,
|
||||
};
|
||||
|
||||
export default utils;
|
||||
|
||||
@ -24,3 +24,45 @@ Using the class text size with the ui-button is inconsistent as to whether
|
||||
it will apply or not. Instead, create a new size value to assign the desired size.
|
||||
To support this, a new size enum is created, smallTall, which is used in the worklist
|
||||
for an over-ride.
|
||||
|
||||
## `createReportDialogPrompt`
|
||||
|
||||
The create report dialog prompt (which is MOSTLY an internal component) has
|
||||
change the API a bit. The input is now:
|
||||
|
||||
- `title` shown in the dialog
|
||||
- `modality` being stored, used to query existing series
|
||||
- `minSeriesNumber` is the start of new series of this modality type.
|
||||
Will get set to 4000 if not determined by the modality
|
||||
- predecessorImageId is the image id that this series was currently loaded
|
||||
from. That allows defaulting the dialog to show the specified series instead
|
||||
of always creating a new series.
|
||||
|
||||
The minSeries and predecessor are both optional, so the input doesn't have to be
|
||||
updated.
|
||||
|
||||
The output has been enhanced with:
|
||||
|
||||
- `series`, is the series to store do, as referenced by a predecessorImageId value.
|
||||
This allows exactly specifying which item to replace, which allows selecting it in the
|
||||
menu by default instead of just guessing what value is being replaced.
|
||||
- `priorSeriesNumber` is the previously lowest series number at least minSeriesNumber
|
||||
of all the seris of the given modality type. This allows adding an instance to the
|
||||
`next` series number by adding 1 to this value.
|
||||
|
||||
The priorSeriesNumber will default to 4000 for an unknown modality type, or
|
||||
3000 for sr, 3100 for seg and 3200 for rtstruct.
|
||||
|
||||
## metadataProvider and formatted metadata
|
||||
|
||||
The metadata provider has been formatting some fields, which causes inconsistency
|
||||
between different metadata providers and use of the instance object. The
|
||||
specific fields that have changed are:
|
||||
|
||||
- `patientName`
|
||||
- `studyDate`, `studyTime`
|
||||
- `seriesDate`, `seriesTime`
|
||||
|
||||
If these fields need formatted versions, it is recommended to add a secondary/computed
|
||||
metadata provider which simply gets the base metadata module and adds the
|
||||
formatting. That way different metadata providers are all handled identically.
|
||||
|
||||
@ -39,6 +39,31 @@ return {
|
||||
}
|
||||
```
|
||||
|
||||
## Replacing Instances in Series
|
||||
|
||||
Series such as segmentations and annotations often have the concept of replacing
|
||||
an earlier instance with a newer one. The official way to do this is using the
|
||||
`predecessor document sequence` to reference the old object in the new one.
|
||||
It isn't that the old one is deleted (never delete data), but rather that the new
|
||||
one is used in preference to the old one. This results in code that looks like:
|
||||
|
||||
```
|
||||
utils.sortStudyInstances(instances);
|
||||
// Choose the LAST instance in the list as the most recently created one.
|
||||
const instance = instances[instances.length - 1];
|
||||
|
||||
...
|
||||
const displaySet = {
|
||||
...
|
||||
predecessorImageId: instance.imageId,
|
||||
numImageFrames: instances.length,
|
||||
}
|
||||
```
|
||||
|
||||
so that the last/latest SOP instance is used by default, and that it is
|
||||
recorded what imageId was used to load that instance. This then allows saving
|
||||
new instances in the same series by using a new predecessor sequence.
|
||||
|
||||
## Example SOP Class Handler Module
|
||||
|
||||
```js
|
||||
|
||||
68
yarn.lock
68
yarn.lock
@ -1280,25 +1280,25 @@
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@cornerstonejs/adapters@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-4.11.2.tgz#f33a4321f53722c30fcf8e08bf76e894ae5c5e5e"
|
||||
integrity sha512-yYZd4W+BnM9So0SsPYYjYN+cho5FBlXIOg0o3JX+gQGoguSk9UedRfd5xa7f7TYoN4ect7RdU4ytwajdabRY5Q==
|
||||
"@cornerstonejs/adapters@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/adapters/-/adapters-4.11.3.tgz#72b594804b14926d4d5e6fddcf4b2947ce42a94d"
|
||||
integrity sha512-N0nKn7ZKBr4ryvveSy19h8EnyhAUyYkdmn23KibPrF84VjzFfK99xMIFWYHxxhYWfCJwzcGf7RR4LIop3FD60Q==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "7.26.10"
|
||||
buffer "6.0.3"
|
||||
dcmjs "0.43.1"
|
||||
dcmjs "0.45.0"
|
||||
gl-matrix "3.4.3"
|
||||
ndarray "1.0.19"
|
||||
|
||||
"@cornerstonejs/ai@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-4.11.2.tgz#e55085c6daa5a804ae64123b385c2aa565425761"
|
||||
integrity sha512-aUfcRidr8/48Szw9MPK4MFXZPhoPlABxOXltx6pZHFe7da3KE1WYoSLD/Dw0T72AtqV2w8blXQuNpplTL4ErAg==
|
||||
"@cornerstonejs/ai@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/ai/-/ai-4.11.3.tgz#2d670f7a2cb2762deb903575f3df4240cad4be82"
|
||||
integrity sha512-qHrFOuM8iYtCX683+Nii9fBkBvgPQA2x+/9jbZpV17YPHAxUSy5gi6a+3ISMwVzKtFhIVhVZK+JMKAAOcCtVVQ==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "7.26.10"
|
||||
buffer "6.0.3"
|
||||
dcmjs "0.43.1"
|
||||
dcmjs "0.45.0"
|
||||
gl-matrix "3.4.3"
|
||||
lodash.clonedeep "4.5.0"
|
||||
ndarray "1.0.19"
|
||||
@ -1330,20 +1330,20 @@
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/codec-openjph/-/codec-openjph-2.4.7.tgz#784394d05d8cf735640e9c3baa206535ee8e0376"
|
||||
integrity sha512-qvP4q4JDib7mi9r7LqKOwqz7YZ8gjtDX4ZCezeYf8+eb7MBXCz5uXAMeVF3yz9Axw4XiIMdB/pqXkm8tqCl13w==
|
||||
|
||||
"@cornerstonejs/core@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-4.11.2.tgz#9ba3559d1cddc3fe9f50ec6f6ebc6f803622e39a"
|
||||
integrity sha512-8Lq3hq+k31C4KbU0sGfUEP/ORn/6nqVYioXvQG4iU+SpLbbQVyhUgEqtQPIKoZVsGaV2mX4HOU3fWXAmMfP4fQ==
|
||||
"@cornerstonejs/core@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/core/-/core-4.11.3.tgz#bf5fa0b3d187ae00789ebe4ff994b43be6a9bfcd"
|
||||
integrity sha512-72yGsvuf5XezQ2nV6JPIOPMlIRtXzuABn1JgpDpoCYbjqbrmApL6G0nLswj0Tuo2dmh4E3iS80KSVVggjllcPg==
|
||||
dependencies:
|
||||
"@kitware/vtk.js" "32.12.1"
|
||||
comlink "4.4.2"
|
||||
gl-matrix "3.4.3"
|
||||
loglevel "1.9.2"
|
||||
|
||||
"@cornerstonejs/dicom-image-loader@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-4.11.2.tgz#2cb74fa20dc349317a037a96011d7f5ed44facee"
|
||||
integrity sha512-xIWbAOrkVBgpBpcqlyYIL2fiAZ8KRF453Ao6svDRixoL7Fl2gvhnpJGbG3wWCYhHaPXbYpbhmfzIovT9qZ7ACg==
|
||||
"@cornerstonejs/dicom-image-loader@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/dicom-image-loader/-/dicom-image-loader-4.11.3.tgz#552615e35db0dc64ce8ed19eb5aa784574ac9f76"
|
||||
integrity sha512-SULpwXEpHF0cINusoTehwUpzQOyxakSTrZ+h4WTLu9GKosxxoIPorw3/eQpsJR9tUBxNnrvgSPFkXXGBdEq4Cw==
|
||||
dependencies:
|
||||
"@cornerstonejs/codec-charls" "1.2.3"
|
||||
"@cornerstonejs/codec-libjpeg-turbo-8bit" "1.2.2"
|
||||
@ -1355,25 +1355,25 @@
|
||||
pako "2.1.0"
|
||||
uuid "9.0.1"
|
||||
|
||||
"@cornerstonejs/labelmap-interpolation@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-4.11.2.tgz#4868455c0c5084e0f54d1d0292fa9cde681ff243"
|
||||
integrity sha512-gS0Gt07U9hDzjoD75I3fKcXRM47sEWsAI706L23/3Q0nN3DGz5NDDySIDo8E+IfZDdSJeX2yBO38RqszVlTkcA==
|
||||
"@cornerstonejs/labelmap-interpolation@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/labelmap-interpolation/-/labelmap-interpolation-4.11.3.tgz#963cb0a912f40536f38caa839ba68b394a4f801b"
|
||||
integrity sha512-yqIvR+7x8HC1l6cMgTBx75LMQf3ZMXdsfiBbOl4mYOlRiiQ5tEcx+8anU+kllwEhsWThu5MjY7s5ZSRybyks0g==
|
||||
dependencies:
|
||||
"@itk-wasm/morphological-contour-interpolation" "1.1.0"
|
||||
itk-wasm "1.0.0-b.165"
|
||||
|
||||
"@cornerstonejs/polymorphic-segmentation@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-4.11.2.tgz#228bd205d0aef2d5cf8d13d4b8811839ccf54fc2"
|
||||
integrity sha512-cW1kVQOiwzWnsHdQOi6wi/k7ji3+qC1sqWb7WgFqf7uTuBbab4Oh0KO6ODcg3G0SOWhzQCPT+GcZf7tsgQh2Tw==
|
||||
"@cornerstonejs/polymorphic-segmentation@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/polymorphic-segmentation/-/polymorphic-segmentation-4.11.3.tgz#d0ea417a1de482bd377480a7b574e9ea02c3f251"
|
||||
integrity sha512-RXKP26mEcQLzpC1xArr/QpMq+minkjO4W02jhhJxmGdTTVypyFyq3Kr9tPJUhosM91yssUHa5mIIHwPwURWxKg==
|
||||
dependencies:
|
||||
"@icr/polyseg-wasm" "0.4.0"
|
||||
|
||||
"@cornerstonejs/tools@4.11.2":
|
||||
version "4.11.2"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-4.11.2.tgz#185cb573895c0a604868cb42777f22f3b8158841"
|
||||
integrity sha512-7opk5t0l3HDSEEa2/PfoiuWBtK6wMdHUjCzy1LY9dVqTpkoqE7YRhNsCuByvd4xgzfp/gijaqlcxS1gyZz0hlw==
|
||||
"@cornerstonejs/tools@4.11.3":
|
||||
version "4.11.3"
|
||||
resolved "https://registry.yarnpkg.com/@cornerstonejs/tools/-/tools-4.11.3.tgz#8677a31b91e7834b82fce53f7efc0075cce5dad0"
|
||||
integrity sha512-L1H57kP0/mfMsmEKc9VhI8XDWYfuPJ0QfNKax2hfF1lma2w/zBsvkYJYNUqpUTZkDFSFVUjPZTBaPxP/BeXcSA==
|
||||
dependencies:
|
||||
"@types/offscreencanvas" "2019.7.3"
|
||||
comlink "4.4.2"
|
||||
@ -7179,10 +7179,10 @@ dayjs@^1.10.4:
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c"
|
||||
integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==
|
||||
|
||||
dcmjs@0.43.1, dcmjs@^0.41.0:
|
||||
version "0.43.1"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.43.1.tgz#03223e56d52e9ec2c3b8c10afe9200d754befe62"
|
||||
integrity sha512-7IAB2cs2F8QYINbfhA7PV+IDraqBuHn8N31xJc6HsyNxZXyGUPQRujcq732ACqjLhl7oJi1z8PJJgLdblQtD3Q==
|
||||
dcmjs@0.45.0, dcmjs@^0.41.0:
|
||||
version "0.45.0"
|
||||
resolved "https://registry.yarnpkg.com/dcmjs/-/dcmjs-0.45.0.tgz#ce53dd06fc345e8231e28c340a36a8094a62422a"
|
||||
integrity sha512-MFtACBLQB6vj/3lzHEf6iGBLFC8f/QysoDO+DVUJeLrKYQi4mXYzzBbHmZCpMXqIpj8qXXLvnP/u2ugd9DVu7Q==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs3" "^7.22.5"
|
||||
adm-zip "^0.5.10"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user