2026-05-07-17-28-34 实现DICOM与模型三维融合视角
This commit is contained in:
@@ -618,6 +618,53 @@ function createReformattedPreview(files: string[], plane: Exclude<DicomPlane, 'a
|
||||
};
|
||||
}
|
||||
|
||||
function createDicomFusionVolume(files: string[], start: number, end: number, mode: DicomDisplayMode) {
|
||||
const volume = getDicomVolume(files, mode);
|
||||
const total = volume.frames.length;
|
||||
const safeStart = Math.max(0, Math.min(total - 1, Number.isFinite(start) ? start : 0));
|
||||
const safeEnd = Math.max(safeStart, Math.min(total - 1, Number.isFinite(end) ? end : safeStart + 49));
|
||||
const maxFrames = 64;
|
||||
const rangeLength = safeEnd - safeStart + 1;
|
||||
const step = Math.max(1, Math.ceil(rangeLength / maxFrames));
|
||||
const indices: number[] = [];
|
||||
for (let index = safeStart; index <= safeEnd; index += step) {
|
||||
indices.push(index);
|
||||
}
|
||||
if (indices[indices.length - 1] !== safeEnd) {
|
||||
indices.push(safeEnd);
|
||||
}
|
||||
|
||||
const maxTextureDimension = 256;
|
||||
const textureScale = Math.min(1, maxTextureDimension / Math.max(volume.width, volume.height));
|
||||
const targetWidth = Math.max(1, Math.round(volume.width * textureScale));
|
||||
const targetHeight = Math.max(1, Math.round(volume.height * textureScale));
|
||||
const frames = indices.map((index) => (
|
||||
resampleNearest(volume.frames[index], volume.width, volume.height, targetWidth, targetHeight).toString('base64')
|
||||
));
|
||||
|
||||
return {
|
||||
width: targetWidth,
|
||||
height: targetHeight,
|
||||
start: safeStart,
|
||||
end: safeEnd,
|
||||
total,
|
||||
indices,
|
||||
frames,
|
||||
mode,
|
||||
spacing: {
|
||||
row: volume.rowSpacing,
|
||||
column: volume.columnSpacing,
|
||||
slice: volume.sliceSpacing,
|
||||
},
|
||||
physicalSize: {
|
||||
width: volume.width * volume.columnSpacing,
|
||||
height: volume.height * volume.rowSpacing,
|
||||
depth: Math.max(1, rangeLength) * volume.sliceSpacing,
|
||||
unit: 'mm',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function enhanceDicomEdges(pixels: Buffer, width: number, height: number) {
|
||||
if (width < 3 || height < 3) {
|
||||
return pixels;
|
||||
@@ -1055,6 +1102,31 @@ async function startServer() {
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/projects/:projectId/dicom-fusion-volume', (req, res) => {
|
||||
const project = findProject(readState(), req.params.projectId);
|
||||
if (!project) {
|
||||
res.status(404).json({ message: '项目不存在' });
|
||||
return;
|
||||
}
|
||||
|
||||
const files = getProjectDicomFiles(project);
|
||||
if (!files.length) {
|
||||
res.status(404).json({ message: '当前项目没有可融合的 DICOM 文件' });
|
||||
return;
|
||||
}
|
||||
|
||||
const requestedMode = String(req.query.mode ?? 'soft');
|
||||
const mode: DicomDisplayMode = requestedMode === 'bone' || requestedMode === 'soft' || requestedMode === 'contrast' ? requestedMode : 'soft';
|
||||
const start = Number.parseInt(String(req.query.start ?? '0'), 10);
|
||||
const end = Number.parseInt(String(req.query.end ?? '49'), 10);
|
||||
|
||||
try {
|
||||
res.json(createDicomFusionVolume(files, start, end, mode));
|
||||
} catch (error) {
|
||||
res.status(422).json({ message: error instanceof Error ? error.message : 'DICOM 三维融合体生成失败' });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/projects/:projectId/dicom-archive', (req, res) => {
|
||||
const project = findProject(readState(), req.params.projectId);
|
||||
if (!project) {
|
||||
|
||||
Reference in New Issue
Block a user