news 2026/9/19 11:44:55

OHIF 视口叠加层(Viewport Overlay)定制指南:四个角落的信息自定义实现与源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OHIF 视口叠加层(Viewport Overlay)定制指南:四个角落的信息自定义实现与源码解析

OHIF 视口叠加层(Viewport Overlay)定制指南:四个角落的信息自定义实现与源码解析

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

本指南讲解 OHIF Viewer 中 Viewport Overlay(视口叠加层)的定制机制——即显示在医学影像视口四个角落(左上、右上、左下、右下)的文字信息,如何通过customizationServicewindow.config中按需增删改查。读完本文,你将掌握四个定制端点的作用、默认项结构、$set/$push/$splice等配置操作符的用法,以及底层渲染组件和 overlay item 的完整实现原理。

Viewport Overlay 是什么

Viewport Overlay 是渲染在视口(Viewport)之上的信息层,用于在不遮挡影像主体的情况下,向用户展示当前影像的关键元数据,例如检查日期、序列描述、窗宽窗位(W/L)、缩放级别、实例编号等。这些信息以绝对定位的<div>覆盖在影像画布四角,且默认不响应鼠标事件(pointer-events-none),不会干扰影像操作。

根据 官方文档,OHIF 提供4 个视图叠加层定制端点(customization end points)

  • viewportOverlay.topRight
  • viewportOverlay.topLeft
  • viewportOverlay.bottomLeft
  • viewportOverlay.bottomRight

每个端点对应视口的一个角落,其值是一个overlay item 数组。你可以通过定制服务(CustomizationService)单独替换任意一个角落的内容,也可以只对默认项做局部增删。

四个角落的默认内容

官方 示例定制文档 与 cornerstone 扩展源码 中给出了一致的默认值,汇总如下:

定制端点默认内容说明
viewportOverlay.topLeftStudyDate(检查日期)、SeriesDescription(序列描述)通过contentFreferenceInstance提取并格式化
viewportOverlay.topRight[](空)默认不显示任何内容
viewportOverlay.bottomLeftWindowLevel(窗宽窗位)、ZoomLevel(缩放级别)ZoomLevel仅在当前活动工具为 Zoom 时显示
viewportOverlay.bottomRightInstanceNumber(实例编号)显示当前实例在序列中的序号

viewportOverlay.topLeft为例,默认两个 item 的定义如下(来自 viewportOverlayCustomization.tsx):

'viewportOverlay.topLeft': [ { id: 'StudyDate', inheritsFrom: 'ohif.overlayItem', label: '', title: 'Study date', condition: ({ referenceInstance }) => referenceInstance?.StudyDate, contentF: ({ referenceInstance, formatters: { formatDate } }) => formatDate(referenceInstance.StudyDate), }, { id: 'SeriesDescription', inheritsFrom: 'ohif.overlayItem', label: '', title: 'Series description', condition: ({ referenceInstance }) => { return referenceInstance && referenceInstance.SeriesDescription; }, contentF: ({ referenceInstance }) => referenceInstance.SeriesDescription, }, ],

可以看到,默认内容由一组 item 组成,每个 item 都基于ohif.overlayItem这一基础定制项派生而来。

Overlay Item 的字段结构与渲染规则

每个 overlay item 本质上是一个“声明式组件描述”,核心渲染逻辑集中在 overlayItemCustomization.tsx 的ohif.overlayItem中。它支持的字段包括:

字段类型作用
idstringitem 的唯一标识,用于定位和后续操作
inheritsFromstring继承的基础定制项 ID,如ohif.overlayItemohif.overlayItem.windowLevel
attributestring直接从instance上读取的 DICOM 属性名(如PatientName),有值时优先于contentF
conditionfunction接收props,返回false时该 item 不渲染(不满足条件则返回null
contentFfunction计算显示内容的函数,接收props并返回字符串
labelstring显示在内容前的标签文本,如'PN:''Patient'
titlestringHTMLtitle属性,鼠标悬停时的提示文字
colorstring文字颜色(CSS 颜色值),如'yellow'

底层渲染逻辑(见 overlayItemCustomization.tsx)依次执行以下步骤:

  1. 若定义了condition且返回假值,直接不渲染该 item;
  2. 若定义了attributeinstance存在,则取instance[attribute]作为原始值;否则调用contentF(props)计算值;
  3. utils.formatValue格式化最终值,若格式化后为空则返回null
  4. 渲染为一个<span>,应用labeltitlecolor
'ohif.overlayItem': function (props) { if (this.condition && !this.condition(props)) { return null; } const { instance } = props; const value = instance && this.attribute ? instance[this.attribute] : this.contentF && typeof this.contentF === 'function' ? this.contentF(props) : null; const displayValue = utils.formatValue(value); if (!displayValue) { return null; } return ( <span className="overlay-item flex flex-row" style={{ color: this.color || undefined }} title={this.title || ''} > {this.label && <span className="mr-1 shrink-0">{this.label}</span>} <span className="font-light">{displayValue}</span> </span> ); },

除基础ohif.overlayItem外,cornerstone 扩展还注册了三种派生 item,用于渲染特殊内容(见 CustomizableViewportOverlay.tsx):

  • ohif.overlayItem.windowLevel:渲染窗宽窗位,如W: 1892 L: 1048
  • ohif.overlayItem.zoomLevel:渲染当前缩放级别,通常配合活动工具判断(toolGroupService.getActiveToolForViewport);
  • ohif.overlayItem.instanceNumber:渲染实例编号。

通过 window.config 定制叠加层

叠加层定制与其他 CustomizationService 定制一样,通过应用配置文件中的customizationService数组完成。下面给出四种常见操作。

1. 用$set整体替换(topRight)

viewportOverlay.topRight默认是空数组,用$set可以整体注入自定义 item(参考 sampleCustomizations.tsx):

window.config = { // rest of window config customizationService: [ { 'viewportOverlay.topRight': { $set: [ // Add your overlay items here, e.g.: // { id: 'CustomOverlay', inheritsFrom: 'ohif.overlayItem.custom' }, ], }, }, ], };

$set会以传入的新数组整体替换该端点的默认数组。

2. 用$splice删除默认项(topLeft)

如果不希望左上角显示StudyDate,可以在viewportOverlay.topLeft上使用$splice,按索引删除第一个元素(参考 sampleCustomizations.tsx):

window.config = { // rest of window config customizationService: [ { 'viewportOverlay.topLeft': { $splice: [ [0, 1], // Remove 1 item starting at index 0 (removes StudyDate) ], }, }, ], };

3. 用$push追加新 item(bottomLeft)

在左下角追加一个自定义的黄色患者姓名叠加项PatientNameOverlay(参考 sampleCustomizations.tsx):

window.config = { // rest of window config customizationService: [ { 'viewportOverlay.bottomLeft': { $push: [ { id: 'PatientNameOverlay', inheritsFrom: 'ohif.overlayItem', attribute: 'PatientName', label: 'PN:', title: 'Patient Name', color: 'yellow', condition: ({ instance }) => instance && instance.PatientName && instance.PatientName.Alphabetic, contentF: ({ instance, formatters: { formatPN } }) => formatPN(instance.PatientName.Alphabetic) + ' ' + (instance.PatientSex ? '(' + instance.PatientSex + ')' : ''), }, ], }, }, ], };

这个例子综合演示了多个字段:attribute用于直接取 DICOM 标签PatientNamelabel提供PN:前缀,color设为黄色,condition在影像没有姓名信息时隐藏该 item,contentF则利用formatPN格式化姓名并附加性别信息。

4. 条件显示的 ZoomLevel 默认项

默认的ZoomLevelitem 演示了“根据上下文决定是否显示”的典型写法(见 viewportOverlayCustomization.tsx):

{ id: 'ZoomLevel', inheritsFrom: 'ohif.overlayItem.zoomLevel', condition: props => { const activeToolName = props.toolGroupService.getActiveToolForViewport(props.viewportId); return activeToolName === 'Zoom'; }, },

condition接收的props中包含toolGroupServiceviewportIdinstancereferenceInstanceformatters等运行上下文,开发者可以据此实现任意的显示逻辑。

源码级原理:叠加层如何被读取与渲染

叠加层的实际渲染由 cornerstone 扩展的 CustomizableViewportOverlay.tsx 组件完成。其核心流程如下:

  1. servicesManager.services中取出customizationServicetoolGroupServicedisplaySetServicecornerstoneViewportService
  2. 分别调用customizationService.getCustomization('viewportOverlay.topLeft')等四个方法读取四个角落的定制数组(对应源码);
  3. 通过displaySetService与当前imageIndex组装出displaySetProps,其中包含instance(当前实例)与referenceInstance(参考实例),这些正是 overlay item 的condition/contentF所依赖的数据(对应源码);
  4. 监听 Cornerstone 的相机/注释事件(如缩放变化、超声 Pleura B-line 标注修改)以刷新 scale、VOI 与叠加层内容(对应源码);
  5. 将每个 item 与OverlayItemComponents中的对应组件(ohif.overlayItemohif.overlayItem.windowLevel等)匹配,渲染到对应角落的容器中。

源码注释还特别指出:早期版本将四个角落定义成独立 item,由于当时缺少追加(append)能力才这样做;现在推荐直接向cornerstoneOverlay的默认数组中追加/修改,而不是为单个角落定义独立 item(见 CustomizableViewportOverlay.tsx)。不过四个viewportOverlay.*端点依然保持向后兼容,可继续使用。

此外,微缩显微镜(dicom-microscopy)扩展提供了一套较早的、基于config对象的叠加层生成方式:generateFromConfig接收{ topLeft, topRight, bottomLeft, bottomRight }四组 item 列表,直接渲染到对应角落(见 index.tsx),其 item 字段使用value/contents函数而非contentF,供以config驱动叠加层的扩展参考。

实战案例:兽医影像的运行时叠加层定制

仓库提供了一个完整、可直接对照的实战案例——兽医影像(veterinary)叠加层,文件位于 veterinaryOverlay.jsonc。该文件演示了:

  • global顶层结构承载定制;
  • $set整体替换viewportOverlay.topLeftviewportOverlay.topRight
  • 每个 item 均使用inheritsFrom: 'ohif.overlayItem',与 cornerstone 扩展默认项写法保持一致;
  • 通过attribute直接绑定兽医专用 DICOM 标签(如PatientSpeciesDescriptionPatientBreedDescription)。
{ "global": { "viewportOverlay.topLeft": { "$set": [ { "id": "PatientName", "inheritsFrom": "ohif.overlayItem", "attribute": "PatientName", "label": "Patient", "title": "Patient name" }, { "id": "PatientID", "inheritsFrom": "ohif.overlayItem", "attribute": "PatientID", "label": "ID", "title": "Patient ID" }, { "id": "StudyDate", "inheritsFrom": "ohif.overlayItem", "attribute": "StudyDate", "label": "Date", "title": "Study date" } ] }, "viewportOverlay.topRight": { "$set": [ { "id": "PatientSpecies", "inheritsFrom": "ohif.overlayItem", "attribute": "PatientSpeciesDescription", "label": "Species", "title": "Patient species" }, { "id": "PatientBreed", "inheritsFrom": "ohif.overlayItem", "attribute": "PatientBreedDescription", "label": "Breed", "title": "Patient breed" } ] } } }

根据文件头注释,该定制支持通过 URL 参数?customization=veterinary/veterinaryOverlay在运行时动态加载(CustomizationService 的 URL 处理机制),这为多院区、多科室按需切换叠加层布局提供了轻量方案。

小结与排查建议

定制 Viewport Overlay 只需记住三条主线:

  1. 四个端点各管一个角落viewportOverlay.topLeft/topRight/bottomLeft/bottomRight,值均为 item 数组;
  2. item 是声明式组件:基于inheritsFrom: 'ohif.overlayItem'派生,可用attributecontentFconditionlabeltitlecolor精确控制内容与显隐;
  3. 定制操作符决定增删改方式$set整体替换、$push追加、$splice按索引删除、$merge合并属性。

排查叠加层不生效的问题时,可以从 CustomizableViewportOverlay.tsx 的getCustomization读取逻辑入手,确认四个端点定制是否被正确加载;若自定义 item 始终不显示,请检查condition是否返回了假值、contentF/attribute是否能取到非空值,以及formatValue格式化后的结果是否为空——这三处是 ohif.overlayItem 渲染链路上最常见的“静默跳过”原因。

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 11:44:52

Win11右键新建文本文档消失?记事本找回与注册表修复指南

升级到 Win11 以后&#xff0c;右键新建菜单里找不到“文本文档”&#xff0c;开始菜单搜“记事本”也提示找不到应用——这个场景我在好几台电脑上遇到过&#xff0c;有同事从 Windows 10 升级后突然冒出来的&#xff0c;有朋友用了系统清理工具后消失的&#xff0c;还有的电脑…

作者头像 李华
网站建设 2026/9/19 11:41:33

DeepSeek-R1架构解析:MLA与MoE协同的高效推理实践

简介&#xff1a;本资源是一份面向AI研发工程师、大模型算法研究员及进阶技术学习者的DeepSeek-R1模型架构深度解析PDF&#xff0c;聚焦其在长上下文建模、高效注意力机制与稀疏化结构设计上的核心突破。文档系统梳理了128K超长上下文实现原理&#xff08;基于YaRN的RoPE扩展&a…

作者头像 李华
网站建设 2026/9/19 11:37:25

RAD Studio 10.4.2 安装避坑指南:从镜像校验到编译验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 11:36:57

区块链+IPFS+SSI构建可验证疫苗凭证系统

简介&#xff1a;本资源是一份面向计算机与信息安全领域研究者及区块链开发者的学术型技术方案&#xff0c;聚焦于利用区块链技术解决疫苗接种证书在跨境流动中的隐私保护、可信验证与去中心化共享难题。文档提出融合智能合约、IPFS与自主主权身份&#xff08;SSI&#xff09;的…

作者头像 李华