feat: 完善了demo ts约束

This commit is contained in:
mozzie 2023-12-20 10:24:54 +08:00
parent 42d18e116c
commit c04884ffd3
6 changed files with 132 additions and 110 deletions

5
apps/aorta/src/global.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare global {
interface Window {
cornerstone: any;
}
}

View File

@ -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,
}) {
const SOP_INSTANCE_UID = "00080018";
const SERIES_INSTANCE_UID = "0020000E";
const MODALITY = "00080060";
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中数据不存在");
}
}

View File

@ -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 };

View File

@ -33,8 +33,6 @@ export const VolumeViewer = (props: VolumeViewerProps) => {
wadoRsRoot: "/dicom-web",
});
console.log(imageIds)
const renderingEngine = new RenderingEngine(renderingEngineId);
const viewportInput = {

View File

@ -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];

View File

@ -1 +1,2 @@
declare module "@cornerstonejs/dicom-image-loader";
declare module "dcmjs";