hamster-desktop/electron/core/dicom.ts

124 lines
3.6 KiB
TypeScript
Raw Normal View History

2024-08-06 09:44:11 +08:00
import path from "path";
import * as dicomParser from "dicom-parser";
import fs from "fs";
2024-08-07 10:28:05 +08:00
export interface StructuredData {
[StudyInstanceUID: string]: {
[SeriesInstanceUID: string]: ExtractMetadata[];
};
}
2024-08-06 16:59:49 +08:00
export interface ExtractMetadata {
filePath: string;
StudyInstanceUID?: string;
SeriesInstanceUID?: string;
pixelData?: Uint16Array;
}
2024-08-06 09:44:11 +08:00
/**
* .dcm文件
* @param dir
* @param fileList
* @returns
*/
export const findDcmFiles = async (
dir: string,
fileList: string[] = []
): Promise<string[]> => {
const files = await fs.promises.readdir(dir, { withFileTypes: true });
await Promise.all(
files.map(async (file) => {
const filePath = path.join(dir, file.name);
if (file.isDirectory()) {
await findDcmFiles(filePath, fileList); // 递归调用以遍历子目录
} else if (file.name.endsWith(".dcm")) {
fileList.push(filePath); // 如果文件是.dcm文件添加到列表中
}
})
);
return fileList;
};
/**
* dcm文件的metadata信息
*/
2024-08-06 16:59:49 +08:00
export const parseDICOMFile = async (
filePath: string
): Promise<ExtractMetadata | undefined> => {
2024-08-06 09:44:11 +08:00
try {
const arrayBuffer = await fs.promises.readFile(filePath);
const byteArray = new Uint8Array(arrayBuffer);
const options = { TransferSyntaxUID: "1.2.840.10008.1.2" };
const dataSet = dicomParser.parseDicom(byteArray, options);
const StudyInstanceUID = dataSet.string("x0020000d");
const SeriesInstanceUID = dataSet.string("x0020000e");
const pixelDataElement = dataSet.elements.x7fe00010;
const pixelData = new Uint16Array(
dataSet.byteArray.buffer,
pixelDataElement.dataOffset,
pixelDataElement.length / 2
);
return {
filePath,
StudyInstanceUID,
SeriesInstanceUID,
2024-08-07 10:28:05 +08:00
// pixelData,
2024-08-06 09:44:11 +08:00
};
} catch (error) {
console.error(`Error parsing file ${filePath}:`, error);
2024-08-06 16:59:49 +08:00
return undefined;
2024-08-06 09:44:11 +08:00
}
};
/**
*
* @param filePaths
* @param {number} batchSize
* @returns
*/
export const processFilesInBatches = async (
filePaths: string[],
batchSize: number
) => {
const results = [];
for (let i = 0; i < filePaths.length; i += batchSize) {
const batch = filePaths.slice(i, i + batchSize);
2024-08-06 16:59:49 +08:00
const batchResults = await Promise.allSettled(
2024-08-06 09:44:11 +08:00
batch.map((filePath) => parseDICOMFile(filePath))
);
2024-08-06 16:59:49 +08:00
// 只提取状态为 'fulfilled' 的结果的 value
const fulfilledResults = batchResults
.filter((result) => result.status === "fulfilled")
.map(
(result) => (result as PromiseFulfilledResult<ExtractMetadata>).value
);
results.push(...fulfilledResults);
2024-08-06 09:44:11 +08:00
}
return results;
};
2024-08-07 10:28:05 +08:00
export const structureMetadata = (data: ExtractMetadata[]): StructuredData => {
const structured: StructuredData = {};
data.forEach((item) => {
// 确保每个元素都有有效的 StudyInstanceUID 和 SeriesInstanceUID
if (item.StudyInstanceUID && item.SeriesInstanceUID) {
// 如果还没有为这个 StudyInstanceUID 创建记录,则初始化一个空对象
if (!structured[item.StudyInstanceUID]) {
structured[item.StudyInstanceUID] = {};
}
// 如果这个 StudyInstanceUID 下还没有这个 SeriesInstanceUID 的记录,则初始化一个空数组
if (!structured[item.StudyInstanceUID][item.SeriesInstanceUID]) {
structured[item.StudyInstanceUID][item.SeriesInstanceUID] = [];
}
// 将当前元素添加到对应的数组中
structured[item.StudyInstanceUID][item.SeriesInstanceUID].push(item);
}
});
return structured;
};