Browse Source

实现展示图像,目前只是展示了一个公共地址的公开图像

dengdx 1 month ago
parent
commit
cfce255f68

+ 10 - 0
config/index.ts

@@ -73,6 +73,16 @@ export default defineConfig<'webpack5'>(async (merge) => {
       },
       webpackChain(chain) {
         chain.resolve.plugin('tsconfig-paths').use(TsconfigPathsPlugin);
+        chain.merge({
+          resolve: {
+            fallback: {
+              util: require.resolve('util/'),
+              stream: require.resolve('stream-browserify'),
+              fs: require.resolve('browserify-fs'),
+              path: require.resolve('path-browserify'),
+            },
+          },
+        });
       },
     },
     rn: {

File diff suppressed because it is too large
+ 268 - 88
package-lock.json


+ 8 - 0
package.json

@@ -40,6 +40,9 @@
   "author": "",
   "dependencies": {
     "@babel/runtime": "^7.24.4",
+    "@cornerstonejs/core": "^3.28.0",
+    "@cornerstonejs/dicom-image-loader": "^3.28.0",
+    "@cornerstonejs/tools": "^3.28.0",
     "@reduxjs/toolkit": "^2.8.2",
     "@tarojs/components": "4.1.1",
     "@tarojs/helper": "4.1.1",
@@ -61,6 +64,7 @@
     "antd-mobile": "^5.39.0",
     "axios": "^1.9.0",
     "dayjs": "^1.11.13",
+    "dicomweb-client": "0.10.4",
     "react": "^18.0.0",
     "react-dom": "^18.0.0",
     "react-intl": "^7.1.11",
@@ -84,6 +88,7 @@
     "@typescript-eslint/parser": "^8.33.1",
     "autoprefixer": "^10.4.21",
     "babel-preset-taro": "4.1.1",
+    "browserify-fs": "^1.0.0",
     "eslint": "^9.28.0",
     "eslint-config-prettier": "^10.1.5",
     "eslint-plugin-prettier": "^5.4.1",
@@ -91,15 +96,18 @@
     "husky": "^9.1.7",
     "lint-staged": "^16.1.0",
     "msw": "^2.10.2",
+    "path-browserify": "^1.0.1",
     "postcss": "^8.5.4",
     "postcss-loader": "^8.1.1",
     "prettier": "^3.5.3",
     "react-refresh": "^0.14.0",
+    "stream-browserify": "^3.0.0",
     "stylelint": "^16.4.0",
     "tailwindcss": "^3.4.17",
     "tsconfig-paths-webpack-plugin": "^4.1.0",
     "typescript": "^5.4.5",
     "typescript-eslint": "^8.33.1",
+    "util": "^0.12.5",
     "webpack": "5.91.0"
   },
   "msw": {

+ 5 - 0
src/pages/view/components/ImageControl.tsx

@@ -1,5 +1,6 @@
 import React from 'react';
 import { Image } from 'antd';
+import StackViewer from './viewers/stack.image.viewer';
 
 const ImageControl = () => {
   return (
@@ -9,6 +10,10 @@ const ImageControl = () => {
         src="https://via.placeholder.com/200"
         alt="Sample Image"
       />
+      <div style={{ width: '800px', height: '600px' }}>
+        <StackViewer imageIndex={30} />
+        {/* 第10张图和第30张图明显不同,可以用于测试,看效果*/}
+      </div>
     </div>
   );
 };

+ 62 - 0
src/pages/view/components/viewers/stack.image.viewer.tsx

@@ -0,0 +1,62 @@
+import React, { useEffect, useRef } from 'react';
+import * as cornerstone from '@cornerstonejs/core';
+import * as cornerstoneTools from '@cornerstonejs/tools';
+import * as cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
+import createImageIdsAndCacheMetaData from '../../createImageIdsAndCacheMetaData';
+
+const StackViewer = ({ imageIndex }) => {
+  const elementRef = useRef<HTMLDivElement>(null);
+
+  useEffect(() => {
+    const setup = async () => {
+      // 初始化 Cornerstone
+      cornerstone.init();
+      cornerstoneTools.init();
+      // 注册加载器
+      cornerstoneDICOMImageLoader.init();
+      // Get Cornerstone imageIds and fetch metadata into RAM
+      const imageIds = await createImageIdsAndCacheMetaData({
+        StudyInstanceUID:
+          '1.3.6.1.4.1.14519.5.2.1.7009.2403.334240657131972136850343327463',
+        SeriesInstanceUID:
+          '1.3.6.1.4.1.14519.5.2.1.7009.2403.226151125820845824875394858561',
+        wadoRsRoot: 'https://d14fa38qiwhyfd.cloudfront.net/dicomweb',
+      });
+      // Instantiate a rendering engine
+      const renderingEngineId = 'myRenderingEngine';
+      const renderingEngine = new cornerstone.RenderingEngine(
+        renderingEngineId
+      );
+
+      const viewportId = 'CT_AXIAL_STACK';
+
+      const viewportInput: cornerstone.Types.PublicViewportInput = {
+        viewportId,
+        // @ts-expect-error why error ?
+        element: elementRef.current,
+        type: cornerstone.Enums.ViewportType.STACK,
+      };
+
+      renderingEngine.enableElement(viewportInput);
+      // Get the stack viewport that was created
+      const viewport = renderingEngine.getViewport(
+        viewportId
+      ) as cornerstone.Types.IStackViewport;
+
+      viewport.setStack(imageIds, imageIndex);
+
+      viewport.render();
+    };
+
+    setup();
+  }, [elementRef]);
+
+  return (
+    <div
+      ref={elementRef}
+      style={{ width: '100%', height: '100%', backgroundColor: '#000' }}
+    />
+  );
+};
+
+export default StackViewer;

+ 63 - 0
src/pages/view/createImageIdsAndCacheMetaData.ts

@@ -0,0 +1,63 @@
+import { api } from 'dicomweb-client';
+import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
+
+/**
+/**
+ * Uses dicomweb-client to fetch metadata of a study, cache it in cornerstone,
+ * and return a list of imageIds for the frames.
+ *
+ * Uses the app config to choose which study to fetch, and which
+ * dicom-web server to fetch it from.
+ *
+ * @returns {string[]} An array of imageIds for instances in the study.
+ */
+
+export default async function createImageIdsAndCacheMetaData({
+  StudyInstanceUID,
+  SeriesInstanceUID,
+  SOPInstanceUID = null,
+  wadoRsRoot,
+  client = null,
+}) {
+  const SOP_INSTANCE_UID = '00080018';
+  const SERIES_INSTANCE_UID = '0020000E';
+
+  const studySearchOptions = {
+    studyInstanceUID: StudyInstanceUID,
+    seriesInstanceUID: SeriesInstanceUID,
+  };
+
+  client =
+    client ||
+    new api.DICOMwebClient({ url: wadoRsRoot as string, singlepart: true });
+  const instances = await client.retrieveSeriesMetadata(studySearchOptions);
+  const imageIds = instances.map((instanceMetaData) => {
+    const SeriesInstanceUID = instanceMetaData[SERIES_INSTANCE_UID].Value[0];
+    const SOPInstanceUIDToUse =
+      SOPInstanceUID || instanceMetaData[SOP_INSTANCE_UID].Value[0];
+
+    const prefix = 'wadors:';
+
+    const imageId =
+      prefix +
+      wadoRsRoot +
+      '/studies/' +
+      StudyInstanceUID +
+      '/series/' +
+      SeriesInstanceUID +
+      '/instances/' +
+      SOPInstanceUIDToUse +
+      '/frames/1';
+
+    cornerstoneDICOMImageLoader.wadors.metaDataManager.add(
+      imageId,
+      instanceMetaData
+    );
+    return imageId;
+  });
+
+  // we don't want to add non-pet
+  // Note: for 99% of scanners SUV calculation is consistent bw slices
+
+  return imageIds;
+}

Some files were not shown because too many files changed in this diff