☰
OHIF 工具栏实战:从第一笔测量到源码级定制的完整操作路径
2026/10/19 1:36:47 网站建设 项目流程

OHIF 工具栏实战:从第一笔测量到源码级定制的完整操作路径

【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers

OHIF Viewer 顶部工具栏把测量、窗宽窗位、截图导出与布局切换收进了一条数据驱动的横栏。本篇用 4 个真实场景串起操作流程,并拆出命令与 evaluate 背后的机制。

📏 场景一:打开检查后做第一笔测量

激活 Length 工具,在图像上点两点,线性距离立刻以 mm 显示。工具栏左侧的 Length 按钮是测量组入口,点击激活后在断层图像上点取两个端点,数值以黄色标尺形式呈现,单位为 mm。

用工具旁的下拉箭头切换其他测量工具。列表默认顺序为:Length、Bidirectional、ArrowAnnotate(注释)、EllipticalROI、RectangleROI、CircleROI、PlanarFreehandROI、SplineROI、LivewireContour。常用几项速查:

工具用途结果单位
Length两点直线距离mm
Bidirectional最长径与最长垂直径mm(双值)
EllipticalROI椭圆 ROI 面积与平均 CT 值mm² + HU
RectangleROI / CircleROI区域面积测量mm² + HU
CalibrationLine校正 Pixel Spacingmm/pixel

测量值对不上标尺怎么办?多半是 DICOM 的 Pixel Spacing 元数据缺失或错误——通过下拉菜单切换到 Calibration 工具,沿已知长度的结构画一条线并填入真实尺寸即可完成校准,上面所有长度类工具此后都会按校准后的像素间距计算。

源码层面,这些测量按钮的commands都指向 setToolActiveToolbar:这一条命令把激活事件同时广播给default、mpr、SRToolGroup、volume3d四个工具组,无论当前聚焦哪个视口,工具都能生效。下拉列表的排布则由toolbarSections中的MeasurementTools分区决定(toolbarButtonsCustomization.ts)。

🎚️ 场景二:图像对比度不对,窗宽窗位怎么调

手动微调:直接在图像上点击并拖拽。激活 Window/Level 工具后,在图像上水平拖拽调整窗位(window level),垂直拖拽调整窗宽(window width),对比度随拖拽实时变化,拖到感兴趣结构清晰后松手即可。

一键预设:用下拉箭头展开预设菜单。默认预设分 CT 与 PT 两组:CT 提供 Soft tissue(400/40)、Lung(1500/-600)、Liver(150/90)、Bone(2500/480)、Brain(80/40),PT 提供 SUV 3 到 SUV 15 的一列档位,数值即窗宽/窗位,点击即生效。预设数据定义在 defaultWindowLevelPresets.ts,并且可以通过 customization service 覆盖或追加——给本院加一个自定义"骨窗",只是改一份数据的事。

Window/Level 家族其实由多个按钮组成:工具栏主按钮WindowLevel、视口左下角的windowLevelMenu、视口底部中间内嵌的windowLevelMenuEmbedded,以及高级手动菜单voiManualControlMenu,都挂在AdvancedRenderingControls分组下(见 toolbarButtonsCustomization.ts)。注意WindowLevel按钮通过evaluate.viewport.supported排除了wholeSlide与 ECG 类视口,这些视口上会自动禁用。

📸 场景三:导出截图与切换多视口布局

截图导出:点相机图标弹出配置弹窗。Capture 按钮位于工具栏中部,就是相机造型的那个图标。

点击后由showDownloadViewportModal命令弹出下载弹窗(见 commandsModule.ts),弹窗中可配置:

  • 文件名(默认image)
  • 文件格式:JPG / PNG 等
  • 图像宽高:单位 px,上限 10000(受MAX_TEXTURE_SIZE限制)
  • Include annotations:是否把已有测量标注画进图里
  • Include warning message:是否附带 "Not For Diagnostic Use" 水印

最后点击Save Image下载到本地,或点击Copy to Clipboard直接复制进剪贴板。弹窗组件见 captureViewportModalCustomization.tsx。再看按钮定义(toolbarButtonsCustomization.ts):evaluate.viewport.supported排除了video与wholeSlide,视频视口和 WSI 全切片视口因此拿不到这个导出能力。

布局切换:工具栏上的 Layout Selector。点击网格图标打开布局菜单:common 区提供 1×1、1×2、2×2、2×3 四种常用格(定义在 layoutSelectorCustomization.ts),advanced 区则根据当前检查的悬挂协议与 display set 动态生成可选组合。网格上限由按钮上的rows: 3, columns: 4参数控制(toolbarButtonsCustomization.ts),也就是最多可自由切出 3 行 × 4 列的视口网格。

🧭 场景四:用"更多工具"菜单做视图微调

工具栏最右侧的 More Tools 菜单聚合了一批视图操作工具,选中某项后它会保持激活,直到你点选其他工具替换:

工具绑定命令作用
Reset ViewresetViewport位置、缩放、窗宽窗位一次还原
Rotate RightrotateViewportCW图像顺时针转 90°
Flip HorizontalflipViewportHorizontal水平镜像翻转
Image Slice SynctoggleSynchronizer(type:imageSlice)多视口位置联动
Stack ScrollsetToolActiveToolbar序列滚动工具
InvertinvertViewport灰度反色
CinetoggleCine逐帧播放,再点一次关闭
Probe / Angle / MagnifysetToolActiveToolbar探针取像素值 / 角度 / 放大镜

几个使用细节:

  • Magnify:激活后点击图像某区域打开放大镜窗口,放大区域细节实时显示;
  • Cine:播放开始后点击播放器上的x,或再次点击工具按钮即可退出;
  • Flip Horizontal同时挂了evaluate.viewportProperties.toggle与evaluate.viewport.supported(排除video、volume3d),在 3D 视口上自动置灰;
  • Rotate Right只排除video,2D 堆栈与 MPR 都能用。

这份菜单的完整排布见MoreTools分区(toolbarButtonsCustomization.ts)。

给开发者:按钮即数据

整条工具栏没有写死任何一个按钮,机制分三层。

数据层(按钮定义):cornerstone 扩展注册了两个能力包——cornerstone.toolbarButtons(全部按钮定义)与cornerstone.toolbarSections(分区到按钮 ID 的映射),见 toolbarButtonsCustomization.ts。以flipHorizontal为例,核心只有两个字段:

props: { commands: 'flipViewportHorizontal', evaluate: ['evaluate.viewportProperties.toggle', { name: 'evaluate.viewport.supported', unsupportedViewportTypes: ['video', 'volume3d'] }], }

commands声明点击时执行什么命令,evaluate声明按钮该不该用、该不该显示。数组形式表示多个求值器并联,任一判 disabled 则按钮整体禁用,合并逻辑在 ToolbarService.ts 的handleEvaluate里。对比Capture按钮(toolbarButtonsCustomization.ts):commands换成showDownloadViewportModal,evaluate.viewport.supported排除video/wholeSlide——同一套按钮结构,因此可以无差别地投放在 2D 堆栈、MPR、3D 容积、视频、WSI 各类视口上,不需要为每种视口单独写分支。

组合层(模式装配):Basic 模式在 index.tsx 里没有写任何按钮实体,只有toolbarSections: [{ $reference: 'cornerstone.toolbarSections' }]与toolbarButtons: [{ $reference: 'cornerstone.toolbarButtons' }]两条引用。进入模式时,modeCustomization.ts 的registerModeToolbar负责调toolbarService.register()注册按钮、逐个updateSection()装配分区。这也解释了为什么?customization=模块能仅靠$push追加按钮,而不必重建整个模式。

渲染层(UI 组件):Toolbar.tsx 通过useToolbarhook 拿到指定分区(默认primary)的按钮列表,为每个按钮注入isOpen、isLocked、onOpen、onToggleLock等交互状态,再按uiType(ohif.toolButton、ohif.layoutSelector、ohif.toolButtonList等)渲染成组件。ToolbarService的refreshToolbarState(ToolbarService.ts)会在视口切换、状态变化时重算所有 evaluate——modalityLoadBadge、trackingStatus这类按钮随数据出现或消失,靠的就是这条刷新链路。

收尾:一个设计思想贯穿整条工具栏

OHIF 的工具栏本质是"按钮即数据 + 注册进服务"的两段式设计:命令绑定在数据上,可用状态交给 evaluate 计算。你要为特定临床工作流做定制查看器时,改一份toolbarSections、用$reference/$push追加按钮即可,渲染组件一行不用碰。

【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询