feat: 完善了demo ts约束
This commit is contained in:
parent
42d18e116c
commit
c04884ffd3
5
apps/aorta/src/global.d.ts
vendored
Normal file
5
apps/aorta/src/global.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
declare global {
|
||||
interface Window {
|
||||
cornerstone: any;
|
||||
}
|
||||
}
|
|
@ -9,42 +9,50 @@ import removeInvalidTags from "./removeInvalidTags";
|
|||
const { DicomMetaDictionary } = dcmjs.data;
|
||||
const { calibratedPixelSpacingMetadataProvider } = utilities;
|
||||
|
||||
export default async function createImageIdsAndCacheMetaData({
|
||||
StudyInstanceUID,
|
||||
SeriesInstanceUID,
|
||||
SOPInstanceUID = null,
|
||||
wadoRsRoot,
|
||||
client = null,
|
||||
}) {
|
||||
interface CreateImageIdsAndCacheMetaDataOptions {
|
||||
StudyInstanceUID: string;
|
||||
SeriesInstanceUID: string;
|
||||
SOPInstanceUID?: string | null;
|
||||
wadoRsRoot: string;
|
||||
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(
|
||||
options: CreateImageIdsAndCacheMetaDataOptions
|
||||
): Promise<string[]> {
|
||||
const studySearchOptions = {
|
||||
studyInstanceUID: StudyInstanceUID,
|
||||
seriesInstanceUID: SeriesInstanceUID,
|
||||
studyInstanceUID: options.StudyInstanceUID,
|
||||
seriesInstanceUID: options.SeriesInstanceUID,
|
||||
};
|
||||
|
||||
client = client || new api.DICOMwebClient({ url: wadoRsRoot });
|
||||
const client =
|
||||
options.client ||
|
||||
new api.DICOMwebClient({ url: options.wadoRsRoot, singlepart: true });
|
||||
|
||||
try {
|
||||
// TODO: 取第一张的metadata看下扫描设备,加上限制,如果不满足条件,就别缓存了
|
||||
const instances = await client.retrieveSeriesMetadata(studySearchOptions);
|
||||
const modality = instances[0][MODALITY].Value[0];
|
||||
let imageIds = instances.map((instanceMetaData) => {
|
||||
|
||||
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 =
|
||||
SOPInstanceUID || instanceMetaData[SOP_INSTANCE_UID].Value[0];
|
||||
options.SOPInstanceUID || instanceMetaData[SOP_INSTANCE_UID].Value[0];
|
||||
|
||||
const prefix = "wadors:";
|
||||
|
||||
const imageId =
|
||||
prefix +
|
||||
wadoRsRoot +
|
||||
"/studies/" +
|
||||
StudyInstanceUID +
|
||||
"/series/" +
|
||||
SeriesInstanceUID +
|
||||
"/instances/" +
|
||||
SOPInstanceUIDToUse +
|
||||
"/frames/1";
|
||||
const imageId = `${prefix}${options.wadoRsRoot}/studies/${options.StudyInstanceUID}/series/${SeriesInstanceUID}/instances/${SOPInstanceUIDToUse}/frames/1`;
|
||||
|
||||
cornerstoneDICOMImageLoader.wadors.metaDataManager.add(
|
||||
imageId,
|
||||
|
@ -53,30 +61,32 @@ export default async function createImageIdsAndCacheMetaData({
|
|||
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
|
||||
// 如果图像 ID 代表多帧信息,则为每个帧创建一个新的图像 ID 列表
|
||||
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);
|
||||
|
||||
// 添加校准的像素间距
|
||||
const metadata =
|
||||
DicomMetaDictionary.naturalizeDataset(instanceMetaData);
|
||||
const pixelSpacing = getPixelSpacingInformation(metadata) as Number[];
|
||||
if (pixelSpacing) {
|
||||
calibratedPixelSpacingMetadataProvider.add(imageId, {
|
||||
rowPixelSpacing: parseFloat(pixelSpacing[0]),
|
||||
columnPixelSpacing: parseFloat(pixelSpacing[1]),
|
||||
});
|
||||
const payload = {
|
||||
rowPixelSpacing: pixelSpacing[0],
|
||||
columnPixelSpacing: pixelSpacing[1],
|
||||
};
|
||||
// FIXME: 这个地方cornerstone ts类型约束洗的有问题
|
||||
// @ts-ignore
|
||||
calibratedPixelSpacingMetadataProvider.add(imageId, payload);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return imageIds;
|
||||
} catch (error) {
|
||||
throw new Error("pacs中数据不存在");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,35 @@
|
|||
// See https://github.com/OHIF/Viewers/blob/94a9067fe3d291d30e25a1bda5913511388edea2/platform/core/src/utils/metadataProvider/getPixelSpacingInformation.js
|
||||
interface DICOMInstance {
|
||||
PixelSpacing?: number[];
|
||||
ImagerPixelSpacing?: number[];
|
||||
SOPClassUID?: string;
|
||||
PixelSpacingCalibrationType?: string;
|
||||
PixelSpacingCalibrationDescription?: string;
|
||||
EstimatedRadiographicMagnificationFactor?: number;
|
||||
SequenceOfUltrasoundRegions?: {
|
||||
PhysicalDeltaX: number;
|
||||
PhysicalDeltaY: number;
|
||||
}[]; // 根据实际需要调整类型
|
||||
}
|
||||
|
||||
export default function getPixelSpacingInformation(instance) {
|
||||
// See http://gdcm.sourceforge.net/wiki/index.php/Imager_Pixel_Spacing
|
||||
interface PixelSpacingResult {
|
||||
PixelSpacing?: number[];
|
||||
type?: string;
|
||||
isProjection?: boolean;
|
||||
PixelSpacingCalibrationType?: string;
|
||||
PixelSpacingCalibrationDescription?: string;
|
||||
}
|
||||
|
||||
// TODO: Add Ultrasound region spacing
|
||||
// TODO: Add manual calibration
|
||||
|
||||
// TODO: Use ENUMS from dcmjs
|
||||
const projectionRadiographSOPClassUIDs = [
|
||||
/**
|
||||
* 获取像素间距信息。
|
||||
* 此函数根据 DICOM 实例的不同属性来确定适当的像素间距。
|
||||
*
|
||||
* @param instance - DICOM 实例对象
|
||||
* @returns 像素间距信息
|
||||
*/
|
||||
export default function getPixelSpacingInformation(
|
||||
instance: DICOMInstance
|
||||
): 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
|
||||
"1.2.840.10008.5.1.4.1.1.1.1.1", // Digital X-Ray Image Storage – for Processing
|
||||
|
@ -32,7 +54,9 @@ export default function getPixelSpacingInformation(instance) {
|
|||
SequenceOfUltrasoundRegions,
|
||||
} = instance;
|
||||
|
||||
const isProjection = projectionRadiographSOPClassUIDs.includes(SOPClassUID);
|
||||
const isProjection = SOPClassUID
|
||||
? projectionRadiographSOPClassUIDs.includes(SOPClassUID)
|
||||
: false;
|
||||
|
||||
const TYPES = {
|
||||
NOT_APPLICABLE: "NOT_APPLICABLE",
|
||||
|
@ -46,9 +70,6 @@ export default function getPixelSpacingInformation(instance) {
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -57,10 +78,8 @@ export default function getPixelSpacingInformation(instance) {
|
|||
} else if (
|
||||
PixelSpacing &&
|
||||
ImagerPixelSpacing &&
|
||||
PixelSpacing === ImagerPixelSpacing
|
||||
PixelSpacing.join() === ImagerPixelSpacing.join()
|
||||
) {
|
||||
// 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,
|
||||
|
@ -69,12 +88,8 @@ export default function getPixelSpacingInformation(instance) {
|
|||
} else if (
|
||||
PixelSpacing &&
|
||||
ImagerPixelSpacing &&
|
||||
PixelSpacing !== ImagerPixelSpacing
|
||||
PixelSpacing.join() !== ImagerPixelSpacing.join()
|
||||
) {
|
||||
// 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,
|
||||
|
@ -85,9 +100,6 @@ export default function getPixelSpacingInformation(instance) {
|
|||
} 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
|
||||
|
@ -104,25 +116,16 @@ export default function getPixelSpacingInformation(instance) {
|
|||
};
|
||||
} else if (
|
||||
SequenceOfUltrasoundRegions &&
|
||||
typeof SequenceOfUltrasoundRegions === "object"
|
||||
Array.isArray(SequenceOfUltrasoundRegions) &&
|
||||
SequenceOfUltrasoundRegions.length > 0
|
||||
) {
|
||||
const { PhysicalDeltaX, PhysicalDeltaY } = SequenceOfUltrasoundRegions;
|
||||
const { PhysicalDeltaX, PhysicalDeltaY } = SequenceOfUltrasoundRegions[0];
|
||||
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
|
||||
} else if (isProjection && !ImagerPixelSpacing) {
|
||||
return {
|
||||
PixelSpacing,
|
||||
type: TYPES.NOT_APPLICABLE,
|
||||
|
@ -134,3 +137,6 @@ export default function getPixelSpacingInformation(instance) {
|
|||
"Unknown combination of PixelSpacing and ImagerPixelSpacing identified. Unable to determine spacing."
|
||||
);
|
||||
}
|
||||
|
||||
// 可选的,如果需要单独导出函数
|
||||
export { getPixelSpacingInformation };
|
||||
|
|
|
@ -33,8 +33,6 @@ export const VolumeViewer = (props: VolumeViewerProps) => {
|
|||
wadoRsRoot: "/dicom-web",
|
||||
});
|
||||
|
||||
console.log(imageIds)
|
||||
|
||||
const renderingEngine = new RenderingEngine(renderingEngineId);
|
||||
|
||||
const viewportInput = {
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
/**
|
||||
* Remove invalid tags from a metadata and return a new object.
|
||||
* Removes invalid tags from a metadata object and returns 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.
|
||||
* This function currently removes tags that have `null` or `undefined` values.
|
||||
* Removing these values is important to prevent errors when processing
|
||||
* the metadata, such as when calling `naturalizeDataset(...)`.
|
||||
*
|
||||
* 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.
|
||||
* The performance of this function has been considered. Validating each
|
||||
* tag ID with a regex significantly reduces the function's speed, so this
|
||||
* approach is avoided. The function is designed to be efficient, with an
|
||||
* average runtime of around +12ms per 1000 images, though this can vary
|
||||
* depending on the machine.
|
||||
*
|
||||
* @param srcMetadata - source metadata
|
||||
* @returns new metadata object without invalid tags
|
||||
* @param srcMetadata - The source metadata object containing the tags.
|
||||
* @returns A new metadata object with invalid tags removed.
|
||||
*/
|
||||
function removeInvalidTags(srcMetadata) {
|
||||
// Object.create(null) make it ~9% faster
|
||||
const dstMetadata = Object.create(null);
|
||||
function removeInvalidTags(
|
||||
srcMetadata: Record<string, any>
|
||||
): Record<string, any> {
|
||||
const dstMetadata: Record<string, any> = Object.create(null);
|
||||
const tagIds = Object.keys(srcMetadata);
|
||||
let tagValue;
|
||||
let tagValue: any;
|
||||
|
||||
tagIds.forEach((tagId) => {
|
||||
tagValue = srcMetadata[tagId];
|
||||
|
|
|
@ -1 +1,2 @@
|
|||
declare module "@cornerstonejs/dicom-image-loader";
|
||||
declare module "dcmjs";
|
Loading…
Reference in New Issue
Block a user