feat: orthanc启动日志

This commit is contained in:
mozzie 2024-09-13 15:55:29 +08:00
parent 1c488ef76e
commit 6aa316efbf
5 changed files with 106 additions and 49 deletions

View File

@ -38,4 +38,20 @@ pnpm config set virtual-store-dir-max-length 70
- 心脏结构窗
窗宽500 HU
窗位50 HU
适用于查看整体心脏结构,包括心室腔、心包和大血管。
适用于查看整体心脏结构,包括心室腔、心包和大血管。
## 备忘
- AXIAL轴位
• 方向:横断面,与身体的长轴垂直。
• 描述:将身体分为上(头部)和下(脚部)部分。
• 应用:通常用于查看从头顶到脚底的切片图像。
- SAGITTAL矢状位
• 方向:纵向面,沿身体的前后方向。
• 描述:将身体分为左、右两部分。正中矢状面则正好将身体分为左右对称的两半。
• 应用:用于观察身体的侧面结构,如脊柱的排列。
- CORONAL冠状位
• 方向:冠状面,与矢状面垂直,沿身体的左右方向。
• 描述:将身体分为前(腹侧)和后(背侧)部分。
• 应用:适用于查看从前面到后面的结构,如面部和脑部的解剖。

View File

@ -27,8 +27,8 @@ export const runOrthancServer = (pacsPath: string) => {
if (existsSync(pacsPath)) {
const configPath = path.join(path.dirname(pacsPath), "config.json");
const child_process = spawn(pacsPath, [configPath]);
child_process.stdout.on("data", (data) => log.info(data));
child_process.stderr.on("data", (data) => log.error(data));
child_process.stdout.on("data", (data) => log.info(data.toString()));
child_process.stderr.on("data", (data) => log.error(data.toString()));
} else {
console.error("pacsPath is a not exist");
log.error("pacsPath is a not exist");

View File

@ -1,9 +1,10 @@
export const renderingEngineId = "mprRenderingEngine";
export const viewportId1 = "CT_AXIAL";
export const viewportId2 = "CT_SAGITTAL";
export const viewportId3 = "CT_CORONAL";
export const toolGroupId = "group";
export type ViewportId = "CT_AXIAL" | "CT_SAGITTAL" | "CT_CORONAL";
export const viewportId1: ViewportId = "CT_AXIAL";
export const viewportId2: ViewportId = "CT_SAGITTAL";
export const viewportId3: ViewportId = "CT_CORONAL";
export const viewportColors = {
[viewportId1]: "rgb(200, 0, 0)",
@ -11,7 +12,7 @@ export const viewportColors = {
[viewportId3]: "rgb(0, 200, 0)",
};
export const viewportReferenceLineControllable = [
export const viewportReferenceLineControllable: ViewportId[] = [
viewportId1,
viewportId2,
viewportId3,

View File

@ -17,10 +17,16 @@ import {
viewportId1,
viewportId2,
viewportId3,
ViewportId,
} from "./Crosshair.config";
const { ToolGroupManager, CrosshairsTool, StackScrollMouseWheelTool, WindowLevelTool, Enums: csToolsEnums } =
cornerstoneTools;
const {
ToolGroupManager,
CrosshairsTool,
StackScrollMouseWheelTool,
WindowLevelTool,
Enums: csToolsEnums,
} = cornerstoneTools;
const { MouseBindings } = csToolsEnums;
@ -30,23 +36,22 @@ interface CrosshairMprProps {
children?: JSX.Element;
}
function getReferenceLineColor(viewportId: string | number) {
return viewportColors[viewportId];
function getReferenceLineColor(vpId: ViewportId) {
return viewportColors[vpId];
}
function getReferenceLineControllable(viewportId: string) {
const index = viewportReferenceLineControllable.indexOf(viewportId);
function getReferenceLineControllable(vpId: ViewportId) {
const index = viewportReferenceLineControllable.indexOf(vpId);
return index !== -1;
}
function getReferenceLineDraggableRotatable(viewportId: string) {
const index = viewportReferenceLineDraggableRotatable.indexOf(viewportId);
function getReferenceLineDraggableRotatable(vpId: ViewportId) {
const index = viewportReferenceLineDraggableRotatable.indexOf(vpId);
return index !== -1;
}
function getReferenceLineSlabThicknessControlsOn(viewportId: string) {
const index =
viewportReferenceLineSlabThicknessControlsOn.indexOf(viewportId);
function getReferenceLineSlabThicknessControlsOn(vpId: ViewportId) {
const index = viewportReferenceLineSlabThicknessControlsOn.indexOf(vpId);
return index !== -1;
}
@ -56,19 +61,25 @@ interface CrosshairMprProps {
}
export const CrosshairMpr = (props: CrosshairMprProps) => {
const viewportId1Ref = useRef(null);
const viewportId2Ref = useRef(null);
const viewportId3Ref = useRef(null);
const viewportRef_AXIAL = useRef<HTMLDivElement | null>(null);
const viewportRef_SAGITTAL = useRef<HTMLDivElement | null>(null);
const viewportRef_CORONAL = useRef<HTMLDivElement | null>(null);
const renderingEngine = useRef<RenderingEngine>();
const imageIds = useRef<string[]>();
useEffect(() => {
cornerstoneTools.addTool(StackScrollMouseWheelTool);
cornerstoneTools.addTool(CrosshairsTool);
cornerstoneTools.addTool(WindowLevelTool);
const run = async () => {
if (
!viewportRef_AXIAL.current ||
!viewportRef_SAGITTAL.current ||
!viewportRef_CORONAL.current
)
return;
imageIds.current = await createImageIdsAndCacheMetaData({
StudyInstanceUID: props.StudyInstanceUID,
SeriesInstanceUID: props.SeriesInstanceUID,
@ -86,17 +97,17 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
);
// 默认windowWidtth
const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData
const defaultWindowCenter = 50
const defaultWindowWidth = 850
console.log(2, windowCenter, windowWidth)
const { windowCenter, windowWidth } = volume.cornerstoneImageMetaData;
const defaultWindowCenter = 50;
const defaultWindowWidth = 850;
console.log(2, windowCenter, windowWidth);
// Create the viewports
const viewportInputArray: PublicViewportInput[] = [
{
viewportId: viewportId1,
type: ViewportType.ORTHOGRAPHIC,
element: viewportId1Ref.current!,
element: viewportRef_AXIAL.current,
defaultOptions: {
orientation: CoreEnums.OrientationAxis.AXIAL,
background: [0, 0, 0],
@ -105,7 +116,7 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
{
viewportId: viewportId2,
type: ViewportType.ORTHOGRAPHIC,
element: viewportId2Ref.current!,
element: viewportRef_SAGITTAL.current,
defaultOptions: {
orientation: CoreEnums.OrientationAxis.SAGITTAL,
background: [0, 0, 0],
@ -114,7 +125,7 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
{
viewportId: viewportId3,
type: ViewportType.ORTHOGRAPHIC,
element: viewportId3Ref.current!,
element: viewportRef_CORONAL.current,
defaultOptions: {
orientation: CoreEnums.OrientationAxis.CORONAL,
background: [0, 0, 0],
@ -133,7 +144,12 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
[
{
volumeId: props.SeriesInstanceUID,
callback: ({ volumeActor }) => setCtTransferFunctionForVolumeActor({ volumeActor, defaultWindowCenter, defaultWindowWidth }),
callback: ({ volumeActor }) =>
setCtTransferFunctionForVolumeActor({
volumeActor,
defaultWindowCenter,
defaultWindowWidth,
}),
},
],
[viewportId1, viewportId2, viewportId3]
@ -187,17 +203,18 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
]);
};
if (!props.SeriesInstanceUID && !props.StudyInstanceUID) return;
run();
if (props.SeriesInstanceUID && props.StudyInstanceUID) {
run();
}
return () => {
cornerstoneTools.removeTool(StackScrollMouseWheelTool);
cornerstoneTools.removeTool(CrosshairsTool);
cornerstoneTools.removeTool(WindowLevelTool)
cornerstoneTools.removeTool(WindowLevelTool);
renderingEngine.current?.destroy();
ToolGroupManager.destroyToolGroup(props.SeriesInstanceUID);
};
}, [props.StudyInstanceUID, props.SeriesInstanceUID]);
}, [props]);
useEffect(() => {
const resize = () => renderingEngine.current?.resize();
@ -207,11 +224,19 @@ export const CrosshairMpr = (props: CrosshairMprProps) => {
};
}, []);
const onDBClickViewport = (viewportRef) => {
console.log("dblcik", viewportRef);
};
return (
<div className="h-full flex flex-col gap-y-2">
<div className="h-1/3 border rounded-md" ref={viewportId1Ref}></div>
<div className="h-1/3 border rounded-md" ref={viewportId2Ref}></div>
<div className="h-1/3 border rounded-md" ref={viewportId3Ref}></div>
<div
onDoubleClick={() => onDBClickViewport(viewportRef_AXIAL)}
className="h-1/3 border rounded-md"
ref={viewportRef_AXIAL}
></div>
<div className="h-1/3 border rounded-md" ref={viewportRef_SAGITTAL}></div>
<div className="h-1/3 border rounded-md" ref={viewportRef_CORONAL}></div>
</div>
);
};

View File

@ -3,15 +3,27 @@ import { initCornerstone } from "./MprViewer/CornerstoneDicomLoader/init";
import { CrosshairMpr } from "./MprViewer/Crosshair";
import { useEffect, useState } from "react";
export interface CurrentDicom {
SeriesInstanceUID: string | null;
StudyInstanceUID: string | null;
}
export const Viewer = () => {
const [cornerstoneLoaded, setCornerstoneLoaded] = useState(false);
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const SeriesInstanceUID = queryParams.get("SeriesInstanceUID"); // 获取URL参数
const StudyInstanceUID = queryParams.get("StudyInstanceUID"); // 获取URL参数
const [currentDicom, setCurrentDicom] = useState<CurrentDicom>({
SeriesInstanceUID: queryParams.get("SeriesInstanceUID"),
StudyInstanceUID: queryParams.get("StudyInstanceUID"),
});
console.log(StudyInstanceUID);
console.log(SeriesInstanceUID);
useEffect(() => {
const { SeriesInstanceUID, StudyInstanceUID } = currentDicom;
if (StudyInstanceUID && SeriesInstanceUID) {
const metadata = { SeriesInstanceUID, StudyInstanceUID };
localStorage.setItem("viewCache", JSON.stringify(metadata));
}
}, [currentDicom]);
useEffect(() => {
initCornerstone().then(() => setCornerstoneLoaded(true));
@ -21,12 +33,15 @@ export const Viewer = () => {
<div className="w-full h-full flex">
<div className="w-2/3 h-full">vr</div>
<div className="w-1/3 h-full">
{cornerstoneLoaded && StudyInstanceUID && SeriesInstanceUID && (
<CrosshairMpr
StudyInstanceUID={StudyInstanceUID}
SeriesInstanceUID={SeriesInstanceUID}
/>
)}
{cornerstoneLoaded &&
currentDicom.StudyInstanceUID &&
currentDicom.SeriesInstanceUID && (
<CrosshairMpr
StudyInstanceUID={currentDicom.StudyInstanceUID}
SeriesInstanceUID={currentDicom.SeriesInstanceUID}
/>
)}
{Object.values(currentDicom).some((i) => !i) && "导入dicom"}
</div>
</div>
);