feat: orthanc启动日志
This commit is contained in:
parent
1c488ef76e
commit
6aa316efbf
18
README.md
18
README.md
|
@ -38,4 +38,20 @@ pnpm config set virtual-store-dir-max-length 70
|
|||
- 心脏结构窗
|
||||
窗宽:500 HU
|
||||
窗位:50 HU
|
||||
适用于查看整体心脏结构,包括心室腔、心包和大血管。
|
||||
适用于查看整体心脏结构,包括心室腔、心包和大血管。
|
||||
|
||||
|
||||
## 备忘
|
||||
|
||||
- AXIAL(轴位):
|
||||
• 方向:横断面,与身体的长轴垂直。
|
||||
• 描述:将身体分为上(头部)和下(脚部)部分。
|
||||
• 应用:通常用于查看从头顶到脚底的切片图像。
|
||||
- SAGITTAL(矢状位):
|
||||
• 方向:纵向面,沿身体的前后方向。
|
||||
• 描述:将身体分为左、右两部分。正中矢状面则正好将身体分为左右对称的两半。
|
||||
• 应用:用于观察身体的侧面结构,如脊柱的排列。
|
||||
- CORONAL(冠状位):
|
||||
• 方向:冠状面,与矢状面垂直,沿身体的左右方向。
|
||||
• 描述:将身体分为前(腹侧)和后(背侧)部分。
|
||||
• 应用:适用于查看从前面到后面的结构,如面部和脑部的解剖。
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
Loading…
Reference in New Issue
Block a user