diff --git a/apps/aorta/package.json b/apps/aorta/package.json index 16e83a2..3e13d8d 100644 --- a/apps/aorta/package.json +++ b/apps/aorta/package.json @@ -33,7 +33,9 @@ "dicomweb-client": "0.10.3", "@cornerstonejs/tools": "1.41.0", "@cornerstonejs/streaming-image-volume-loader": "1.41.0", - "@cornerstonejs/calculate-suv": "1.1.0" + "@cornerstonejs/calculate-suv": "1.1.0", + "vtk.js": "29.2.0", + "itk": "14.1.1" }, "devDependencies": { "@babel/core": "^7.21.8", diff --git a/apps/aorta/src/modules/Root/Viewer/LocalMpr/index.tsx b/apps/aorta/src/modules/Root/Viewer/LocalMpr/index.tsx new file mode 100644 index 0000000..7470789 --- /dev/null +++ b/apps/aorta/src/modules/Root/Viewer/LocalMpr/index.tsx @@ -0,0 +1,57 @@ +import { useRef, useState } from "react"; +import dicomParser from "dicom-parser"; +import vtkVolume from '@kitware/vtk.js/Rendering/Core/Volume'; +import vtkVolumeMapper from '@kitware/vtk.js/Rendering/Core/VolumeMapper'; +import vtkFullScreenRenderWindow from '@kitware/vtk.js/Rendering/Misc/FullScreenRenderWindow'; +import readImageDICOMFileSeries from "itk/readImageDICOMFileSeries"; +import vtkITKHelper from '@kitware/vtk.js/Common/DataModel/ITKHelper'; + + +interface LocalMprProps { + children?: JSX.Element; +} + +export const LocalMpr = (props: LocalMprProps) => { + const containerRef = useRef(null); + const handleFolderUpload = async ( + event: React.ChangeEvent + ) => { + if (event.target.files) { + const files = event.target.files; + const volumeImageData = await readImageDICOMFileSeries(files); + const vtkImage = vtkITKHelper.convertItkToVtkImage(volumeImageData); + const volume = vtkVolume.newInstance(); + const mapper = vtkVolumeMapper.newInstance(); + mapper.setInputData(vtkImage); + volume.setMapper(mapper); + + // 创建 vtk 的全屏渲染窗口 + const fullScreenRenderer = vtkFullScreenRenderWindow.newInstance({ + background: [0, 0, 0], + container: containerRef.current!, + }); + const renderer = fullScreenRenderer.getRenderer(); + const renderWindow = fullScreenRenderer.getRenderWindow(); + + // 将 volume 添加到渲染器 + renderer.addVolume(volume); + // 设置相机位置等 + renderer.resetCamera(); + renderWindow.render(); + } + }; + + return ( +
+
+ +
+
+
+ ); +}; diff --git a/apps/aorta/src/modules/Root/Viewer/index.tsx b/apps/aorta/src/modules/Root/Viewer/index.tsx index 1ccc4eb..1ac82bb 100644 --- a/apps/aorta/src/modules/Root/Viewer/index.tsx +++ b/apps/aorta/src/modules/Root/Viewer/index.tsx @@ -1,5 +1,6 @@ import { CrosshairMpr } from "./Crosshair"; import { DiffViewer } from "./DiffViewer"; +import { LocalMpr } from "./LocalMpr"; import { MprViewer } from "./MprViewer"; import { StackViewer } from "./StackViewer"; @@ -10,7 +11,8 @@ interface RootViewerProps { export const RootViewer = (props: RootViewerProps) => { return (
- + + {/* */} {/* */}
); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc49621..6497677 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: dicomweb-client: specifier: 0.10.3 version: 0.10.3 + itk: + specifier: 14.1.1 + version: 14.1.1 js-cookie: specifier: 3.0.5 version: 3.0.5 @@ -92,6 +95,9 @@ importers: three: specifier: 0.156.1 version: 0.156.1 + vtk.js: + specifier: 29.2.0 + version: 29.2.0(@babel/preset-env@7.21.5)(autoprefixer@10.4.15)(webpack@5.75.0)(wslink@1.11.4) devDependencies: '@babel/core': specifier: ^7.21.8 @@ -2977,6 +2983,13 @@ packages: regenerator-runtime: 0.13.11 dev: false + /@babel/runtime@7.22.11: + resolution: {integrity: sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==, tarball: http://192.168.4.201:4873/@babel%2fruntime/-/runtime-7.22.11.tgz} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: false + /@babel/runtime@7.22.15: resolution: {integrity: sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==} engines: {node: '>=6.9.0'} @@ -4838,6 +4851,10 @@ packages: resolution: {integrity: sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==} dev: false + /@types/webxr@0.5.10: + resolution: {integrity: sha512-n3u5sqXQJhf1CS68mw3Wf16FQ4cRPNBBwdYLFzq3UddiADOim1Pn3Y6PBdDilz1vOJF3ybLxJ8ZEDlLIzrOQZg==, tarball: http://192.168.4.201:4873/@types%2fwebxr/-/webxr-0.5.10.tgz} + dev: false + /@types/webxr@0.5.4: resolution: {integrity: sha512-41gfGLTtqXZhcmoDlLDHqMJDuwAMwhHwXf9Q2job3TUBsvkNfPNI/3IWVEtLH4tyY1ElWtfwIaoNeqeEX238/Q==} dev: true @@ -5765,6 +5782,11 @@ packages: /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + /at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==, tarball: http://192.168.4.201:4873/at-least-node/-/at-least-node-1.0.0.tgz} + engines: {node: '>= 4.0.0'} + dev: false + /atob@2.1.2: resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} engines: {node: '>= 4.5.0'} @@ -5802,6 +5824,14 @@ packages: ee-first: 1.1.1 dev: false + /axios@0.21.4: + resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==, tarball: http://192.168.4.201:4873/axios/-/axios-0.21.4.tgz} + dependencies: + follow-redirects: 1.15.2 + transitivePeerDependencies: + - debug + dev: false + /axios@1.3.6: resolution: {integrity: sha512-PEcdkk7JcdPiMDkvM4K6ZBRYq9keuVJsToxm2zQIM70Qqo2WHTdJZMXcG9X+RmRp2VPNUQC8W1RAGbgt6b1yMg==} dependencies: @@ -6454,7 +6484,7 @@ packages: engines: {node: '>=14'} /commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, tarball: http://192.168.4.201:4873/commander/-/commander-2.20.3.tgz} /commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} @@ -7785,7 +7815,7 @@ packages: dev: true /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, tarball: http://192.168.4.201:4873/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz} /fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} @@ -8069,14 +8099,24 @@ packages: dev: true /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==, tarball: http://192.168.4.201:4873/fs-extra/-/fs-extra-8.1.0.tgz} engines: {node: '>=6 <7 || >=8'} dependencies: - graceful-fs: 192.168.4.201+4873/graceful-fs@4.2.11 + graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 dev: true + /fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==, tarball: http://192.168.4.201:4873/fs-extra/-/fs-extra-9.1.0.tgz} + engines: {node: '>=10'} + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.0 + dev: false + /fs-monkey@1.0.4: resolution: {integrity: sha512-INM/fWAxMICjttnD0DX1rBvinKskj5G1w+oy/pnm9u/tSlnBrzFonJMcalKJ30P8RRsPzKcCG7Q8l0jx5Fh9YQ==} @@ -8973,6 +9013,21 @@ packages: resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} engines: {node: '>=6'} + /itk@14.1.1: + resolution: {integrity: sha512-gLHkndNe7s00lYzasdM74IgrxryF5PY5uqlTRXlCXK09l095M8Qs6WKhZm/DEavQ+rhE5tQL1qowqmQTIfUM2A==, tarball: http://192.168.4.201:4873/itk/-/itk-14.1.1.tgz} + hasBin: true + dependencies: + '@babel/runtime': 7.22.15 + axios: 0.21.4 + commander: 2.20.3 + fs-extra: 9.1.0 + mime-types: 2.1.35 + promise-file-reader: 1.0.3 + webworker-promise: 0.4.4 + transitivePeerDependencies: + - debug + dev: false + /jest-util@29.6.3: resolution: {integrity: sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -9106,18 +9161,17 @@ packages: dev: true /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==, tarball: http://192.168.4.201:4873/jsonfile/-/jsonfile-4.0.0.tgz} optionalDependencies: graceful-fs: 4.2.11 dev: true /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==, tarball: http://192.168.4.201:4873/jsonfile/-/jsonfile-6.1.0.tgz} dependencies: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.11 - dev: true /jsonwebtoken@9.0.0: resolution: {integrity: sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==} @@ -9549,7 +9603,7 @@ packages: engines: {node: '>= 0.6'} /mime-types@2.1.18: - resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==} + resolution: {integrity: sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==, tarball: http://192.168.4.201:4873/mime-types/-/mime-types-2.1.18.tgz} engines: {node: '>= 0.6'} dependencies: mime-db: 1.33.0 @@ -10749,6 +10803,10 @@ packages: /promise-breaker@6.0.0: resolution: {integrity: sha512-BthzO9yTPswGf7etOBiHCVuugs2N01/Q/94dIPls48z2zCmrnDptUUZzfIb+41xq0MnYZ/BzmOd6ikDR4ibNZA==} + /promise-file-reader@1.0.3: + resolution: {integrity: sha512-/sumzg8xjurm6PchjhlXb592lv3tFFaO13DtsFqbglhdZjCzxhDgvmPv/vMcarpfGiCRPC4ZvZ+YsV0am0d4+g==, tarball: http://192.168.4.201:4873/promise-file-reader/-/promise-file-reader-1.0.3.tgz} + dev: false + /proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -11802,7 +11860,7 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} dependencies: - graceful-fs: 192.168.4.201+4873/graceful-fs@4.2.11 + graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 @@ -13615,14 +13673,13 @@ packages: engines: {node: '>=4'} /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==, tarball: http://192.168.4.201:4873/universalify/-/universalify-0.1.2.tgz} engines: {node: '>= 4.0.0'} dev: true /universalify@2.0.0: - resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==, tarball: http://192.168.4.201:4873/universalify/-/universalify-2.0.0.tgz} engines: {node: '>= 10.0.0'} - dev: true /unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} @@ -13800,6 +13857,37 @@ packages: engines: {node: '>=0.10.0'} dev: false + /vtk.js@29.2.0(@babel/preset-env@7.21.5)(autoprefixer@10.4.15)(webpack@5.75.0)(wslink@1.11.4): + resolution: {integrity: sha512-z2clkJEYgs78+EAjl+/JTXe4yv98yuZE0g9iTT+xIcT+mKGGOy4Lqq6UNMWv1IWVDqXzprpCK/ucjDox3sJB1w==, tarball: http://192.168.4.201:4873/vtk.js/-/vtk.js-29.2.0.tgz} + hasBin: true + peerDependencies: + '@babel/preset-env': ^7.17.10 + autoprefixer: ^10.4.7 + wslink: ^1.1.0 + dependencies: + '@babel/preset-env': 7.21.5(@babel/core@7.21.8) + '@babel/runtime': 7.22.11 + '@types/webxr': 0.5.10 + autoprefixer: 10.4.15(postcss@8.4.29) + commander: 9.2.0 + d3-scale: 4.0.2 + fast-deep-equal: 3.1.3 + fflate: 0.7.3 + gl-matrix: 3.4.3 + globalthis: 1.0.3 + seedrandom: 3.0.5 + shader-loader: 1.3.1 + shelljs: 0.8.5 + spark-md5: 3.0.2 + stream-browserify: 3.0.0 + webworker-promise: 0.5.0 + worker-loader: 3.0.8(webpack@5.75.0) + wslink: 1.11.4 + xmlbuilder2: 3.0.2 + transitivePeerDependencies: + - webpack + dev: false + /watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -14116,6 +14204,10 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} + /webworker-promise@0.4.4: + resolution: {integrity: sha512-NfdSlaWqd+0iSrQudB0N0MELfJ9TVTlynhXMpi06piuZhyc9Yy7Hz6BFu2HUkvIb9lCS0pFW42ptd/JnXVnptg==, tarball: http://192.168.4.201:4873/webworker-promise/-/webworker-promise-0.4.4.tgz} + dev: false + /webworker-promise@0.5.0: resolution: {integrity: sha512-14iR79jHAV7ozwvbfif+3wCaApT3I1g8Lo0rJZrwAu6wxZGx/08Y8KXz6as6ZLNUEEufeiEBBYrqyDBClXOsEw==, tarball: http://192.168.4.201:4873/webworker-promise/-/webworker-promise-0.5.0.tgz} dev: false @@ -14785,12 +14877,6 @@ packages: define-properties: 192.168.4.201+4873/define-properties@1.2.0 dev: false - 192.168.4.201+4873/graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, registry: http://npm.tavi.fit/, tarball: http://192.168.4.201:4873/graceful-fs/-/graceful-fs-4.2.11.tgz} - name: graceful-fs - version: 4.2.11 - dev: true - 192.168.4.201+4873/has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==, registry: http://npm.tavi.fit/, tarball: http://192.168.4.201:4873/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz} name: has-property-descriptors