feat: 打通了wadors
This commit is contained in:
parent
3d1afd7a18
commit
42d18e116c
|
@ -29,7 +29,10 @@
|
||||||
"pako": "2.1.0",
|
"pako": "2.1.0",
|
||||||
"@kitware/vtk.js": "29.2.0",
|
"@kitware/vtk.js": "29.2.0",
|
||||||
"@cornerstonejs/dicom-image-loader": "1.41.0",
|
"@cornerstonejs/dicom-image-loader": "1.41.0",
|
||||||
"cornerstone-wado-image-loader": "4.13.2"
|
"cornerstone-wado-image-loader": "4.13.2",
|
||||||
|
"@cornerstonejs/core": "1.41.0",
|
||||||
|
"dcmjs": "0.30.0",
|
||||||
|
"dicomweb-client": "0.10.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.21.8",
|
"@babel/core": "^7.21.8",
|
||||||
|
|
|
@ -12,6 +12,11 @@ export const proxyMap: TProxyMap = {
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { "^/dicom-web": "" },
|
pathRewrite: { "^/dicom-web": "" },
|
||||||
},
|
},
|
||||||
|
'/wado':{
|
||||||
|
target: "http://localhost:8042/wado",
|
||||||
|
changeOrigin: true,
|
||||||
|
pathRewrite: { "^/wado": "" },
|
||||||
|
},
|
||||||
"/cert": {
|
"/cert": {
|
||||||
target: "http://localhost:12144/",
|
target: "http://localhost:12144/",
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
import { metaData } from "@cornerstonejs/core";
|
||||||
|
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preloads imageIds metadata in memory
|
||||||
|
**/
|
||||||
|
async function prefetchMetadataInformation(
|
||||||
|
imageIdsToPrefetch: string[]
|
||||||
|
): Promise<void> {
|
||||||
|
for (let i = 0; i < imageIdsToPrefetch.length; i++) {
|
||||||
|
await cornerstoneDICOMImageLoader.wadouri.loadImage(imageIdsToPrefetch[i])
|
||||||
|
.promise;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FrameInformation {
|
||||||
|
frameIndex: number;
|
||||||
|
imageIdFrameless: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFrameInformation(imageId: string): FrameInformation {
|
||||||
|
if (imageId.includes("wadors:")) {
|
||||||
|
const frameIndex = imageId.indexOf("/frames/");
|
||||||
|
const imageIdFrameless =
|
||||||
|
frameIndex > 0 ? imageId.slice(0, frameIndex + 8) : imageId;
|
||||||
|
return {
|
||||||
|
frameIndex,
|
||||||
|
imageIdFrameless,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const frameIndex = imageId.indexOf("&frame=");
|
||||||
|
let imageIdFrameless =
|
||||||
|
frameIndex > 0 ? imageId.slice(0, frameIndex + 7) : imageId;
|
||||||
|
if (!imageIdFrameless.includes("&frame=")) {
|
||||||
|
imageIdFrameless += "&frame=";
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
frameIndex,
|
||||||
|
imageIdFrameless,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of imageids possibly referring to multiframe dicom images
|
||||||
|
* into a list of imageids where each imageid refers to one frame.
|
||||||
|
* For each imageId representing a multiframe image with n frames,
|
||||||
|
* it creates n new imageids, one for each frame, and returns the new list of imageids.
|
||||||
|
* If a particular imageid does not refer to a multiframe image data, it will be just copied into the new list.
|
||||||
|
* @returns new list of imageids where each imageid represents a frame
|
||||||
|
*/
|
||||||
|
function convertMultiframeImageIds(imageIds: string[]): string[] {
|
||||||
|
const newImageIds: string[] = [];
|
||||||
|
imageIds.forEach((imageId) => {
|
||||||
|
const { imageIdFrameless } = getFrameInformation(imageId);
|
||||||
|
const instanceMetaData = metaData.get("multiframeModule", imageId);
|
||||||
|
if (
|
||||||
|
instanceMetaData &&
|
||||||
|
instanceMetaData.NumberOfFrames &&
|
||||||
|
instanceMetaData.NumberOfFrames > 1
|
||||||
|
) {
|
||||||
|
const numberOfFrames = instanceMetaData.NumberOfFrames;
|
||||||
|
for (let i = 0; i < numberOfFrames; i++) {
|
||||||
|
const newImageId = imageIdFrameless + (i + 1);
|
||||||
|
newImageIds.push(newImageId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newImageIds.push(imageId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return newImageIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { convertMultiframeImageIds, prefetchMetadataInformation };
|
|
@ -0,0 +1,82 @@
|
||||||
|
import { api } from "dicomweb-client";
|
||||||
|
import dcmjs from "dcmjs";
|
||||||
|
import { utilities } from "@cornerstonejs/core";
|
||||||
|
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
||||||
|
import getPixelSpacingInformation from "./getPixelSpacingInformation";
|
||||||
|
import { convertMultiframeImageIds } from "./convertMultiframeImageIds";
|
||||||
|
import removeInvalidTags from "./removeInvalidTags";
|
||||||
|
|
||||||
|
const { DicomMetaDictionary } = dcmjs.data;
|
||||||
|
const { calibratedPixelSpacingMetadataProvider } = utilities;
|
||||||
|
|
||||||
|
export default async function createImageIdsAndCacheMetaData({
|
||||||
|
StudyInstanceUID,
|
||||||
|
SeriesInstanceUID,
|
||||||
|
SOPInstanceUID = null,
|
||||||
|
wadoRsRoot,
|
||||||
|
client = null,
|
||||||
|
}) {
|
||||||
|
const SOP_INSTANCE_UID = "00080018";
|
||||||
|
const SERIES_INSTANCE_UID = "0020000E";
|
||||||
|
const MODALITY = "00080060";
|
||||||
|
|
||||||
|
const studySearchOptions = {
|
||||||
|
studyInstanceUID: StudyInstanceUID,
|
||||||
|
seriesInstanceUID: SeriesInstanceUID,
|
||||||
|
};
|
||||||
|
|
||||||
|
client = client || new api.DICOMwebClient({ url: wadoRsRoot });
|
||||||
|
const instances = await client.retrieveSeriesMetadata(studySearchOptions);
|
||||||
|
const modality = instances[0][MODALITY].Value[0];
|
||||||
|
let 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;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the image ids represent multiframe information, creates a new list with one image id per frame
|
||||||
|
// if not multiframe data available, just returns the same list given
|
||||||
|
imageIds = convertMultiframeImageIds(imageIds);
|
||||||
|
|
||||||
|
imageIds.forEach((imageId) => {
|
||||||
|
let instanceMetaData =
|
||||||
|
cornerstoneDICOMImageLoader.wadors.metaDataManager.get(imageId);
|
||||||
|
|
||||||
|
// It was using JSON.parse(JSON.stringify(...)) before but it is 8x slower
|
||||||
|
instanceMetaData = removeInvalidTags(instanceMetaData);
|
||||||
|
|
||||||
|
if (instanceMetaData) {
|
||||||
|
// Add calibrated pixel spacing
|
||||||
|
const metadata = DicomMetaDictionary.naturalizeDataset(instanceMetaData);
|
||||||
|
const pixelSpacing = getPixelSpacingInformation(metadata);
|
||||||
|
|
||||||
|
if (pixelSpacing) {
|
||||||
|
calibratedPixelSpacingMetadataProvider.add(imageId, {
|
||||||
|
rowPixelSpacing: parseFloat(pixelSpacing[0]),
|
||||||
|
columnPixelSpacing: parseFloat(pixelSpacing[1]),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return imageIds;
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
// See https://github.com/OHIF/Viewers/blob/94a9067fe3d291d30e25a1bda5913511388edea2/platform/core/src/utils/metadataProvider/getPixelSpacingInformation.js
|
||||||
|
|
||||||
|
export default function getPixelSpacingInformation(instance) {
|
||||||
|
// See http://gdcm.sourceforge.net/wiki/index.php/Imager_Pixel_Spacing
|
||||||
|
|
||||||
|
// TODO: Add Ultrasound region spacing
|
||||||
|
// TODO: Add manual calibration
|
||||||
|
|
||||||
|
// TODO: Use ENUMS from dcmjs
|
||||||
|
const projectionRadiographSOPClassUIDs = [
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1", // CR Image Storage
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1.1", // Digital X-Ray Image Storage – for Presentation
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1.1.1", // Digital X-Ray Image Storage – for Processing
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1.2", // Digital Mammography X-Ray Image Storage – for Presentation
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1.2.1", // Digital Mammography X-Ray Image Storage – for Processing
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1.3", // Digital Intra – oral X-Ray Image Storage – for Presentation
|
||||||
|
"1.2.840.10008.5.1.4.1.1.1.3.1", // Digital Intra – oral X-Ray Image Storage – for Processing
|
||||||
|
"1.2.840.10008.5.1.4.1.1.12.1", // X-Ray Angiographic Image Storage
|
||||||
|
"1.2.840.10008.5.1.4.1.1.12.1.1", // Enhanced XA Image Storage
|
||||||
|
"1.2.840.10008.5.1.4.1.1.12.2", // X-Ray Radiofluoroscopic Image Storage
|
||||||
|
"1.2.840.10008.5.1.4.1.1.12.2.1", // Enhanced XRF Image Storage
|
||||||
|
"1.2.840.10008.5.1.4.1.1.12.3", // X-Ray Angiographic Bi-plane Image Storage Retired
|
||||||
|
];
|
||||||
|
|
||||||
|
const {
|
||||||
|
PixelSpacing,
|
||||||
|
ImagerPixelSpacing,
|
||||||
|
SOPClassUID,
|
||||||
|
PixelSpacingCalibrationType,
|
||||||
|
PixelSpacingCalibrationDescription,
|
||||||
|
EstimatedRadiographicMagnificationFactor,
|
||||||
|
SequenceOfUltrasoundRegions,
|
||||||
|
} = instance;
|
||||||
|
|
||||||
|
const isProjection = projectionRadiographSOPClassUIDs.includes(SOPClassUID);
|
||||||
|
|
||||||
|
const TYPES = {
|
||||||
|
NOT_APPLICABLE: "NOT_APPLICABLE",
|
||||||
|
UNKNOWN: "UNKNOWN",
|
||||||
|
CALIBRATED: "CALIBRATED",
|
||||||
|
DETECTOR: "DETECTOR",
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isProjection) {
|
||||||
|
return PixelSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProjection && !ImagerPixelSpacing) {
|
||||||
|
// If only Pixel Spacing is present, and this is a projection radiograph,
|
||||||
|
// PixelSpacing should be used, but the user should be informed that
|
||||||
|
// what it means is unknown
|
||||||
|
return {
|
||||||
|
PixelSpacing,
|
||||||
|
type: TYPES.UNKNOWN,
|
||||||
|
isProjection,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
PixelSpacing &&
|
||||||
|
ImagerPixelSpacing &&
|
||||||
|
PixelSpacing === ImagerPixelSpacing
|
||||||
|
) {
|
||||||
|
// If Imager Pixel Spacing and Pixel Spacing are present and they have the same values,
|
||||||
|
// then the user should be informed that the measurements are at the detector plane
|
||||||
|
return {
|
||||||
|
PixelSpacing,
|
||||||
|
type: TYPES.DETECTOR,
|
||||||
|
isProjection,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
PixelSpacing &&
|
||||||
|
ImagerPixelSpacing &&
|
||||||
|
PixelSpacing !== ImagerPixelSpacing
|
||||||
|
) {
|
||||||
|
// If Imager Pixel Spacing and Pixel Spacing are present and they have different values,
|
||||||
|
// then the user should be informed that these are "calibrated"
|
||||||
|
// (in some unknown manner if Pixel Spacing Calibration Type and/or
|
||||||
|
// Pixel Spacing Calibration Description are absent)
|
||||||
|
return {
|
||||||
|
PixelSpacing,
|
||||||
|
type: TYPES.CALIBRATED,
|
||||||
|
isProjection,
|
||||||
|
PixelSpacingCalibrationType,
|
||||||
|
PixelSpacingCalibrationDescription,
|
||||||
|
};
|
||||||
|
} else if (!PixelSpacing && ImagerPixelSpacing) {
|
||||||
|
let CorrectedImagerPixelSpacing = ImagerPixelSpacing;
|
||||||
|
if (EstimatedRadiographicMagnificationFactor) {
|
||||||
|
// Note that in IHE Mammo profile compliant displays, the value of Imager Pixel Spacing is required to be corrected by
|
||||||
|
// Estimated Radiographic Magnification Factor and the user informed of that.
|
||||||
|
// TODO: should this correction be done before all of this logic?
|
||||||
|
CorrectedImagerPixelSpacing = ImagerPixelSpacing.map(
|
||||||
|
(pixelSpacing) =>
|
||||||
|
pixelSpacing / EstimatedRadiographicMagnificationFactor
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
"EstimatedRadiographicMagnificationFactor was not present. Unable to correct ImagerPixelSpacing."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
PixelSpacing: CorrectedImagerPixelSpacing,
|
||||||
|
isProjection,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
SequenceOfUltrasoundRegions &&
|
||||||
|
typeof SequenceOfUltrasoundRegions === "object"
|
||||||
|
) {
|
||||||
|
const { PhysicalDeltaX, PhysicalDeltaY } = SequenceOfUltrasoundRegions;
|
||||||
|
const USPixelSpacing = [PhysicalDeltaX * 10, PhysicalDeltaY * 10];
|
||||||
|
|
||||||
|
return {
|
||||||
|
PixelSpacing: USPixelSpacing,
|
||||||
|
};
|
||||||
|
} else if (
|
||||||
|
SequenceOfUltrasoundRegions &&
|
||||||
|
Array.isArray(SequenceOfUltrasoundRegions) &&
|
||||||
|
SequenceOfUltrasoundRegions.length > 1
|
||||||
|
) {
|
||||||
|
console.warn(
|
||||||
|
"Sequence of Ultrasound Regions > one entry. This is not yet implemented, all measurements will be shown in pixels."
|
||||||
|
);
|
||||||
|
} else if (isProjection === false && !ImagerPixelSpacing) {
|
||||||
|
// If only Pixel Spacing is present, and this is not a projection radiograph,
|
||||||
|
// we can stop here
|
||||||
|
return {
|
||||||
|
PixelSpacing,
|
||||||
|
type: TYPES.NOT_APPLICABLE,
|
||||||
|
isProjection,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn(
|
||||||
|
"Unknown combination of PixelSpacing and ImagerPixelSpacing identified. Unable to determine spacing."
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,58 +1,61 @@
|
||||||
import axios from "axios";
|
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
import * as cornerstone from "cornerstone-core";
|
import { Enums, RenderingEngine } from "@cornerstonejs/core";
|
||||||
import dicomParser from "dicom-parser";
|
import initDemo from "./init.demo";
|
||||||
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
import createImageIdsAndCacheMetaData from "./createImageIdsAndCacheMetaData";
|
||||||
|
import {
|
||||||
|
IStackViewport,
|
||||||
|
PublicViewportInput,
|
||||||
|
} from "@cornerstonejs/core/dist/types/types";
|
||||||
|
|
||||||
interface VolumeViewerProps {
|
interface VolumeViewerProps {
|
||||||
children?: JSX.Element;
|
children?: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const renderingEngineId = "myRenderingEngine";
|
||||||
|
const viewportId = "CT_STACK";
|
||||||
|
|
||||||
|
const { ViewportType } = Enums;
|
||||||
|
|
||||||
const StudyInstanceUID =
|
const StudyInstanceUID =
|
||||||
"1.2.840.113619.2.416.200043823236217877797891016883696407563"; // 你的 studyInstanceUID
|
"1.2.840.113564.345049290535.9692.637552042156233117.433089";
|
||||||
const SeriesInstanceUID =
|
const SeriesInstanceUID =
|
||||||
"1.2.840.113619.6.80.114374075765625.22940.1553237925965.1"; // 你的 seriesInstanceUID
|
"1.3.12.2.1107.5.1.4.76315.30000021042706150001900118311";
|
||||||
|
|
||||||
export const VolumeViewer = (props: VolumeViewerProps) => {
|
export const VolumeViewer = (props: VolumeViewerProps) => {
|
||||||
const dicomRef = useRef(null);
|
const dicomRef = useRef(null);
|
||||||
const url = `/dicom-web/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/instances`;
|
|
||||||
|
const run = async () => {
|
||||||
|
await initDemo();
|
||||||
|
// Get Cornerstone imageIds and fetch metadata into RAM
|
||||||
|
const imageIds = await createImageIdsAndCacheMetaData({
|
||||||
|
StudyInstanceUID,
|
||||||
|
SeriesInstanceUID,
|
||||||
|
wadoRsRoot: "/dicom-web",
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(imageIds)
|
||||||
|
|
||||||
|
const renderingEngine = new RenderingEngine(renderingEngineId);
|
||||||
|
|
||||||
|
const viewportInput = {
|
||||||
|
viewportId,
|
||||||
|
type: ViewportType.STACK,
|
||||||
|
element: dicomRef.current,
|
||||||
|
} as unknown as PublicViewportInput;
|
||||||
|
|
||||||
|
renderingEngine.enableElement(viewportInput);
|
||||||
|
|
||||||
|
const viewport = renderingEngine.getViewport(viewportId) as IStackViewport;
|
||||||
|
|
||||||
|
const stack = imageIds;
|
||||||
|
|
||||||
|
await viewport.setStack(stack);
|
||||||
|
|
||||||
|
viewport.render();
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
cornerstoneDICOMImageLoader.external.cornerstone = cornerstone;
|
run();
|
||||||
cornerstoneDICOMImageLoader.external.dicomParser = dicomParser;
|
|
||||||
|
|
||||||
// cornerstoneWADOImageLoader.configure({ useWebWorkers: true });
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
maxWebWorkers: navigator.hardwareConcurrency || 1,
|
|
||||||
startWebWorkersOnDemand: true,
|
|
||||||
taskConfiguration: {
|
|
||||||
decodeTask: {
|
|
||||||
initializeCodecsOnStartup: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
cornerstoneDICOMImageLoader.webWorkerManager.initialize(config);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
cornerstone.enable(dicomRef.current!);
|
|
||||||
axios
|
|
||||||
.get(url)
|
|
||||||
.then((response) => {
|
|
||||||
const objectUIDs = response.data.map(
|
|
||||||
(i: any) => i["00080018"].Value[0]
|
|
||||||
);
|
|
||||||
objectUIDs.forEach((i: any) => {
|
|
||||||
const imageId = `wadors:/dicom-web/studies/${StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${i}/frames/1`;
|
|
||||||
cornerstone.loadImage(imageId).then((image: any) => {
|
|
||||||
cornerstone.displayImage(dicomRef.current!, image);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error fetching instances:", error);
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import initCornerstoneDICOMImageLoader from "./initCornerstoneDICOMImageLoader";
|
||||||
|
import { init as csRenderInit } from "@cornerstonejs/core";
|
||||||
|
|
||||||
|
export default async function initDemo() {
|
||||||
|
initCornerstoneDICOMImageLoader();
|
||||||
|
await csRenderInit();
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
import dicomParser from "dicom-parser";
|
||||||
|
import * as cornerstone from "@cornerstonejs/core";
|
||||||
|
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
||||||
|
|
||||||
|
window.cornerstone = cornerstone;
|
||||||
|
const { preferSizeOverAccuracy, useNorm16Texture } =
|
||||||
|
cornerstone.getConfiguration().rendering;
|
||||||
|
|
||||||
|
export default function initCornerstoneDICOMImageLoader() {
|
||||||
|
cornerstoneDICOMImageLoader.external.cornerstone = cornerstone;
|
||||||
|
cornerstoneDICOMImageLoader.external.dicomParser = dicomParser;
|
||||||
|
cornerstoneDICOMImageLoader.configure({
|
||||||
|
useWebWorkers: true,
|
||||||
|
decodeConfig: {
|
||||||
|
convertFloatPixelDataToInt: false,
|
||||||
|
use16BitDataType: preferSizeOverAccuracy || useNorm16Texture,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let maxWebWorkers = 1;
|
||||||
|
|
||||||
|
if (navigator.hardwareConcurrency) {
|
||||||
|
maxWebWorkers = Math.min(navigator.hardwareConcurrency, 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = {
|
||||||
|
maxWebWorkers,
|
||||||
|
startWebWorkersOnDemand: false,
|
||||||
|
taskConfiguration: {
|
||||||
|
decodeTask: {
|
||||||
|
initializeCodecsOnStartup: false,
|
||||||
|
strict: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
cornerstoneDICOMImageLoader.webWorkerManager.initialize(config);
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Remove invalid tags from a metadata and return a new object.
|
||||||
|
*
|
||||||
|
* At this time it is only removing tags that has `null` or `undefined` values
|
||||||
|
* which is our main goal because that breaks when `naturalizeDataset(...)` is
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* Validating the tag id using regex like /^[a-fA-F0-9]{8}$/ make it run
|
||||||
|
* +50% slower and looping through all characteres (split+every+Set or simple
|
||||||
|
* FOR+Set) double the time it takes to run. It is currently taking +12ms/1k
|
||||||
|
* images on average which can change depending on the machine.
|
||||||
|
*
|
||||||
|
* @param srcMetadata - source metadata
|
||||||
|
* @returns new metadata object without invalid tags
|
||||||
|
*/
|
||||||
|
function removeInvalidTags(srcMetadata) {
|
||||||
|
// Object.create(null) make it ~9% faster
|
||||||
|
const dstMetadata = Object.create(null);
|
||||||
|
const tagIds = Object.keys(srcMetadata);
|
||||||
|
let tagValue;
|
||||||
|
|
||||||
|
tagIds.forEach((tagId) => {
|
||||||
|
tagValue = srcMetadata[tagId];
|
||||||
|
|
||||||
|
if (tagValue !== undefined && tagValue !== null) {
|
||||||
|
dstMetadata[tagId] = tagValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return dstMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { removeInvalidTags as default, removeInvalidTags };
|
1
apps/aorta/src/modules/Root/Viewer/VolumeViewer/type.d.ts
vendored
Normal file
1
apps/aorta/src/modules/Root/Viewer/VolumeViewer/type.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
declare module "@cornerstonejs/dicom-image-loader";
|
|
@ -100,7 +100,7 @@ services:
|
||||||
- POSTGRES_DB=postgres
|
- POSTGRES_DB=postgres
|
||||||
|
|
||||||
pacs:
|
pacs:
|
||||||
image: osimis/orthanc:20.11.2
|
image: osimis/orthanc:master-full
|
||||||
container_name: tavi-orthanc
|
container_name: tavi-orthanc
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
|
|
2398
pnpm-lock.yaml
2398
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user