feat: 切换单个viewport的图片
This commit is contained in:
parent
c04884ffd3
commit
5008849d82
5
apps/aorta/src/global.d.ts
vendored
5
apps/aorta/src/global.d.ts
vendored
|
@ -1,5 +0,0 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
cornerstone: any;
|
||||
}
|
||||
}
|
|
@ -4,21 +4,21 @@ import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
|||
/**
|
||||
* Preloads imageIds metadata in memory
|
||||
**/
|
||||
async function prefetchMetadataInformation(
|
||||
export const prefetchMetadataInformation = async (
|
||||
imageIdsToPrefetch: string[]
|
||||
): Promise<void> {
|
||||
): 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 {
|
||||
export const getFrameInformation = (imageId: string): FrameInformation => {
|
||||
if (imageId.includes("wadors:")) {
|
||||
const frameIndex = imageId.indexOf("/frames/");
|
||||
const imageIdFrameless =
|
||||
|
@ -39,7 +39,7 @@ function getFrameInformation(imageId: string): FrameInformation {
|
|||
imageIdFrameless,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts a list of imageids possibly referring to multiframe dicom images
|
||||
|
@ -49,7 +49,7 @@ function getFrameInformation(imageId: string): FrameInformation {
|
|||
* 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[] {
|
||||
export const convertMultiframeImageIds = (imageIds: string[]): string[] => {
|
||||
const newImageIds: string[] = [];
|
||||
imageIds.forEach((imageId) => {
|
||||
const { imageIdFrameless } = getFrameInformation(imageId);
|
||||
|
@ -69,6 +69,4 @@ function convertMultiframeImageIds(imageIds: string[]): string[] {
|
|||
}
|
||||
});
|
||||
return newImageIds;
|
||||
}
|
||||
|
||||
export { convertMultiframeImageIds, prefetchMetadataInformation };
|
||||
};
|
||||
|
|
|
@ -2,13 +2,17 @@ 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 { getPixelSpacingInformation } from "./getPixelSpacingInformation";
|
||||
import { convertMultiframeImageIds } from "./convertMultiframeImageIds";
|
||||
import removeInvalidTags from "./removeInvalidTags";
|
||||
import { removeInvalidTags } from "./removeInvalidTags";
|
||||
|
||||
const { DicomMetaDictionary } = dcmjs.data;
|
||||
const { calibratedPixelSpacingMetadataProvider } = utilities;
|
||||
|
||||
const SOP_INSTANCE_UID = "00080018";
|
||||
const SERIES_INSTANCE_UID = "0020000E";
|
||||
const MODALITY = "00080060";
|
||||
|
||||
interface CreateImageIdsAndCacheMetaDataOptions {
|
||||
StudyInstanceUID: string;
|
||||
SeriesInstanceUID: string;
|
||||
|
@ -17,42 +21,34 @@ interface CreateImageIdsAndCacheMetaDataOptions {
|
|||
client?: api.DICOMwebClient | null;
|
||||
}
|
||||
|
||||
const SOP_INSTANCE_UID = "00080018";
|
||||
const SERIES_INSTANCE_UID = "0020000E";
|
||||
// 数据扫描设备 CT、CTA
|
||||
const MODALITY = "00080060";
|
||||
|
||||
/**
|
||||
* 从 DICOMweb 服务创建图像 ID 并缓存相关元数据。
|
||||
*
|
||||
* @param options - 包含图像和 DICOMweb 服务信息的对象
|
||||
* @returns 图像 ID 数组
|
||||
*/
|
||||
export default async function createImageIdsAndCacheMetaData(
|
||||
export const createImageIdsAndCacheMetaData = async (
|
||||
options: CreateImageIdsAndCacheMetaDataOptions
|
||||
): Promise<string[]> {
|
||||
const studySearchOptions = {
|
||||
studyInstanceUID: options.StudyInstanceUID,
|
||||
seriesInstanceUID: options.SeriesInstanceUID,
|
||||
};
|
||||
|
||||
): Promise<string[]> => {
|
||||
const client =
|
||||
options.client ||
|
||||
new api.DICOMwebClient({ url: options.wadoRsRoot, singlepart: true });
|
||||
|
||||
try {
|
||||
// TODO: 取第一张的metadata看下扫描设备,加上限制,如果不满足条件,就别缓存了
|
||||
const instances = await client.retrieveSeriesMetadata(studySearchOptions);
|
||||
const instances = await client.retrieveSeriesMetadata({
|
||||
studyInstanceUID: options.StudyInstanceUID,
|
||||
seriesInstanceUID: options.SeriesInstanceUID,
|
||||
});
|
||||
|
||||
const modality = (instances[0] as unknown as any)[MODALITY].Value[0];
|
||||
console.log("modality", modality);
|
||||
|
||||
let imageIds = instances.map((instanceMetaData: any) => {
|
||||
const SeriesInstanceUID = instanceMetaData[SERIES_INSTANCE_UID].Value[0];
|
||||
const SOPInstanceUIDToUse =
|
||||
const seriesInstanceUID = instanceMetaData[SERIES_INSTANCE_UID].Value[0];
|
||||
const sopInstanceUIDToUse =
|
||||
options.SOPInstanceUID || instanceMetaData[SOP_INSTANCE_UID].Value[0];
|
||||
|
||||
const prefix = "wadors:";
|
||||
const imageId = `${prefix}${options.wadoRsRoot}/studies/${options.StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${SOPInstanceUIDToUse}/frames/1`;
|
||||
const imageId = `wadors:${options.wadoRsRoot}/studies/${options.StudyInstanceUID}/series/${seriesInstanceUID}/instances/${sopInstanceUIDToUse}/frames/1`;
|
||||
|
||||
cornerstoneDICOMImageLoader.wadors.metaDataManager.add(
|
||||
imageId,
|
||||
|
@ -61,7 +57,6 @@ export default async function createImageIdsAndCacheMetaData(
|
|||
return imageId;
|
||||
});
|
||||
|
||||
// 如果图像 ID 代表多帧信息,则为每个帧创建一个新的图像 ID 列表
|
||||
imageIds = convertMultiframeImageIds(imageIds);
|
||||
|
||||
imageIds.forEach((imageId) => {
|
||||
|
@ -70,23 +65,24 @@ export default async function createImageIdsAndCacheMetaData(
|
|||
instanceMetaData = removeInvalidTags(instanceMetaData);
|
||||
|
||||
if (instanceMetaData) {
|
||||
// 添加校准的像素间距
|
||||
const metadata =
|
||||
DicomMetaDictionary.naturalizeDataset(instanceMetaData);
|
||||
const pixelSpacing = getPixelSpacingInformation(metadata) as Number[];
|
||||
|
||||
if (pixelSpacing) {
|
||||
const payload = {
|
||||
// FIXME: cornerstone类型定义有问题,这里.add方法缺少type属性
|
||||
calibratedPixelSpacingMetadataProvider.add(imageId, {
|
||||
// @ts-ignore
|
||||
rowPixelSpacing: pixelSpacing[0],
|
||||
// @ts-ignore
|
||||
columnPixelSpacing: pixelSpacing[1],
|
||||
};
|
||||
// FIXME: 这个地方cornerstone ts类型约束洗的有问题
|
||||
// @ts-ignore
|
||||
calibratedPixelSpacingMetadataProvider.add(imageId, payload);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return imageIds;
|
||||
} catch (error) {
|
||||
throw new Error("pacs中数据不存在");
|
||||
throw new Error("PACS 中数据不存在");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -26,9 +26,9 @@ interface PixelSpacingResult {
|
|||
* @param instance - DICOM 实例对象
|
||||
* @returns 像素间距信息
|
||||
*/
|
||||
export default function getPixelSpacingInformation(
|
||||
export const getPixelSpacingInformation = (
|
||||
instance: DICOMInstance
|
||||
): PixelSpacingResult | number[] | undefined {
|
||||
): PixelSpacingResult | number[] | undefined => {
|
||||
const projectionRadiographSOPClassUIDs: string[] = [
|
||||
"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
|
||||
|
@ -136,7 +136,4 @@ export default function getPixelSpacingInformation(
|
|||
console.warn(
|
||||
"Unknown combination of PixelSpacing and ImagerPixelSpacing identified. Unable to determine spacing."
|
||||
);
|
||||
}
|
||||
|
||||
// 可选的,如果需要单独导出函数
|
||||
export { getPixelSpacingInformation };
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useEffect, useRef } from "react";
|
||||
import { Enums, RenderingEngine } from "@cornerstonejs/core";
|
||||
import initDemo from "./init.demo";
|
||||
import createImageIdsAndCacheMetaData from "./createImageIdsAndCacheMetaData";
|
||||
import { Enums, RenderingEngine, cache } from "@cornerstonejs/core";
|
||||
import { initCornerstone } from "./initCornerstoneDicomImageLoader";
|
||||
import { createImageIdsAndCacheMetaData } from "./createImageIdsAndCacheMetaData";
|
||||
import {
|
||||
IStackViewport,
|
||||
PublicViewportInput,
|
||||
|
@ -25,7 +25,7 @@ export const VolumeViewer = (props: VolumeViewerProps) => {
|
|||
const dicomRef = useRef(null);
|
||||
|
||||
const run = async () => {
|
||||
await initDemo();
|
||||
await initCornerstone();
|
||||
// Get Cornerstone imageIds and fetch metadata into RAM
|
||||
const imageIds = await createImageIdsAndCacheMetaData({
|
||||
StudyInstanceUID,
|
||||
|
@ -48,8 +48,11 @@ export const VolumeViewer = (props: VolumeViewerProps) => {
|
|||
const stack = imageIds;
|
||||
|
||||
await viewport.setStack(stack);
|
||||
|
||||
viewport.render();
|
||||
const currentImageIdIndex = viewport.getCurrentImageIdIndex();
|
||||
const numImages = viewport.getImageIds().length;
|
||||
let newImageIdIndex = currentImageIdIndex + 1;
|
||||
newImageIdIndex = Math.min(newImageIdIndex, numImages - 1);
|
||||
viewport.setImageIdIndex(newImageIdIndex);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -58,7 +61,7 @@ export const VolumeViewer = (props: VolumeViewerProps) => {
|
|||
|
||||
return (
|
||||
<div>
|
||||
<div ref={dicomRef} style={{ width: 200, height: 200 }}></div>
|
||||
<div ref={dicomRef} style={{ width: 400, height: 400 }}></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import initCornerstoneDICOMImageLoader from "./initCornerstoneDICOMImageLoader";
|
||||
import { init as csRenderInit } from "@cornerstonejs/core";
|
||||
|
||||
export default async function initDemo() {
|
||||
initCornerstoneDICOMImageLoader();
|
||||
await csRenderInit();
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import { init as csRenderInit } from "@cornerstonejs/core";
|
||||
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;
|
||||
const initCornerstoneDICOMImageLoader = () => {
|
||||
const { preferSizeOverAccuracy, useNorm16Texture } =
|
||||
cornerstone.getConfiguration().rendering;
|
||||
|
||||
export default function initCornerstoneDICOMImageLoader() {
|
||||
cornerstoneDICOMImageLoader.external.cornerstone = cornerstone;
|
||||
cornerstoneDICOMImageLoader.external.dicomParser = dicomParser;
|
||||
cornerstoneDICOMImageLoader.configure({
|
||||
|
@ -17,14 +17,8 @@ export default function initCornerstoneDICOMImageLoader() {
|
|||
},
|
||||
});
|
||||
|
||||
let maxWebWorkers = 1;
|
||||
|
||||
if (navigator.hardwareConcurrency) {
|
||||
maxWebWorkers = Math.min(navigator.hardwareConcurrency, 7);
|
||||
}
|
||||
|
||||
var config = {
|
||||
maxWebWorkers,
|
||||
const config = {
|
||||
maxWebWorkers: navigator.hardwareConcurrency ?? 7,
|
||||
startWebWorkersOnDemand: false,
|
||||
taskConfiguration: {
|
||||
decodeTask: {
|
||||
|
@ -35,4 +29,9 @@ export default function initCornerstoneDICOMImageLoader() {
|
|||
};
|
||||
|
||||
cornerstoneDICOMImageLoader.webWorkerManager.initialize(config);
|
||||
}
|
||||
};
|
||||
|
||||
export const initCornerstone = async () => {
|
||||
initCornerstoneDICOMImageLoader();
|
||||
await csRenderInit();
|
||||
};
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
* @param srcMetadata - The source metadata object containing the tags.
|
||||
* @returns A new metadata object with invalid tags removed.
|
||||
*/
|
||||
function removeInvalidTags(
|
||||
export const removeInvalidTags = (
|
||||
srcMetadata: Record<string, any>
|
||||
): Record<string, any> {
|
||||
): Record<string, any> => {
|
||||
const dstMetadata: Record<string, any> = Object.create(null);
|
||||
const tagIds = Object.keys(srcMetadata);
|
||||
let tagValue: any;
|
||||
|
@ -30,6 +30,4 @@ function removeInvalidTags(
|
|||
});
|
||||
|
||||
return dstMetadata;
|
||||
}
|
||||
|
||||
export { removeInvalidTags as default, removeInvalidTags };
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user