恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
OHIF SegmentationService 分割服务完全指南:Labelmap 创建、Segment 管理与可视化控制
首页
资讯中心
/
OHIF SegmentationService 分割服务完全指南:Labelmap 创建、Segment 管理与可视化控制
OHIF SegmentationService 分割服务完全指南:Labelmap 创建、Segment 管理与可视化控制
发布时间:2026/9/19 8:53:18
OHIF SegmentationService 分割服务完全指南Labelmap 创建、Segment 管理与可视化控制【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers导读本文深入讲解 OHIF Viewers 平台中SegmentationService的完整用法与技术内幕。该服务是 OHIF 与 CornerstoneJS 分割引擎之间的核心桥梁负责创建 Labelmap/Contour 分割、管理 Segmentation 与 Segment 的生命周期、控制颜色/可见性/样式并提供按 Segment 跳转切片与高亮导航能力。读完本文你将掌握该服务的全部公开 API、事件订阅方式、底层数据结构以及如何在扩展中基于displaySetService与segmentationService组合实现一套可运行的分割工作流。本文对应的官方文档位于 platform/docs/versioned_docs/version-3.11/platform/services/data/SegmentationService.md当前版本文档见 platform/docs/docs/platform/services/data/SegmentationService.md服务实现位于 extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts并配有完整单元测试 extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts。SegmentationService 在 OHIF 中的定位与注册方式SegmentationService不是平台核心ohif/core内置服务而是由cornerstone 扩展提供并注册到ServicesManager中的扩展服务。从源码结构看它继承自ohif/core的PubSubService事件发布/订阅基类通过REGISTRATION静态描述完成注册// extensions/cornerstone/src/services/SegmentationService/SegmentationService.ts#L109-L116 class SegmentationService extends PubSubService implements ISegmentationServiceInternals { static REGISTRATION { name: segmentationService, altName: SegmentationService, create: ({ servicesManager }: OHIFTypes.Extensions.ExtensionParams): SegmentationService { return new SegmentationService({ servicesManager }); }, };因此在任何已加载 cornerstone 扩展的 Mode 中你都可以通过servicesManager.services.segmentationService获取该服务实例altName为SegmentationService。服务在 Mode 进入时通过onModeEnter()初始化——它会向 Cornerstone 的eventTarget注册 8 个事件监听器将底层分割事件转发为 OHIF 服务事件在 Mode 退出时通过onModeExit()调用destroy()注销监听并重置状态。这一进入/退出生命周期是 OHIF 服务体系的通用约定也意味着分割状态是Mode 级作用域切换 Mode 会自动清理。在 3.9 及以后的版本中OHIF 的旧SegmentationService被彻底重写为基于 Cornerstone Tools v3 分割状态cstSegmentation.state的薄封装相关迁移说明可参考 platform/docs/versioned_docs/version-3.11/migration-guide/3p8-to-3p9/1-segmentation/2-segmentationService-basic.md。事件系统EventsSegmentationService通过PubSubService的subscribe(eventName, callback)对外发布事件事件名在EVENTS常量中定义。官方文档列出的事件如下SEGMENTATION_MODIFIED // 当 segmentation 被更新时触发 SEGMENTATION_DATA_MODIFIED // 当 segmentation 数据发生变化时触发 SEGMENTATION_ADDED // 当新的 segmentation 被添加时触发 SEGMENTATION_REMOVED // 当 segmentation 被移除时触发 SEGMENT_LOADING_COMPLETE // 当 segment 组向 volume 添加像素数据时触发如 RTSTRUCT 逐段加载 SEGMENTATION_LOADING_COMPLETE // 当整个分割 volume 被填满时触发 SEGMENTATION_ANNOTATION_CUT_MERGE_PROCESS_COMPLETED // 当分割的 annotation 剪切合并流程完成时触发 SEGMENTATION_STYLE_MODIFIED // 当分割样式被修改时触发这些事件大多是对 Cornerstone 底层事件的透传转发。在源码_initSegmentationService()SegmentationService.ts#L1909-L1949中服务同时监听SEGMENTATION_MODIFIED、SEGMENTATION_REMOVED、SEGMENTATION_DATA_MODIFIED、SEGMENTATION_REPRESENTATION_MODIFIED、SEGMENTATION_REPRESENTATION_ADDED、SEGMENTATION_REPRESENTATION_REMOVED、SEGMENTATION_ADDED以及ANNOTATION_CUT_MERGE_PROCESS_COMPLETED再通过_broadcastEvent转发给订阅者。转发时携带{ segmentationId }表示相关事件还携带viewportId等 detail 字段。除上述文档列出的事件外源码中还定义了以下表示类事件供内部与 Viewport 联动使用SEGMENTATION_REPRESENTATION_MODIFIED // event::segmentation_representation_modified SEGMENTATION_REPRESENTATION_REMOVED // event::segmentation_representation_removed订阅示例const unsub segmentationService.subscribe( segmentationService.EVENTS.SEGMENTATION_ADDED, ({ segmentationId }) { console.log(New segmentation added:, segmentationId); } ); // 不再需要时调用 unsub() 取消订阅单元测试 SegmentationService.test.ts#L221-L260 明确断言了onModeEnter()会注册这 8 个事件监听器可作为事件清单的验证依据。核心 API 总览创建方法Creation MethodscreateLabelmapForDisplaySet( displaySet, { segmentationId?: string, label: string, segments?: { [segmentIndex: number]: PartialSegment } } ) createContourForDisplaySet( displaySet, { segmentationId?: string, label: string, segments?: { [segmentIndex: number]: PartialSegment } } )displaySet目标图像对应的 DisplaySet 对象通常通过displaySetService.getDisplaySetByUID(displaySetUID)获取segmentationId可选自定义分割 ID不传则由源码使用uuidv4()自动生成SegmentationService.ts#L474label分割的名称不传时默认使用Segmentation ${当前分割数量 1}SegmentationService.ts#L489segments可选以segmentIndex为键的初始 Segment 配置对象不传时服务会自动创建一个索引为 1、label 为国际化文本 Segment 1、active: true的默认段。两个方法在源码中分别路由到_createSegmentationForDisplaySet(displaySet, LABELMAP, options)与_createSegmentationForDisplaySet(displaySet, CONTOUR, options)SegmentationService.ts#L424-L450。其中createLabelmapForDisplaySet内部会调用imageLoader.createAndCacheDerivedLabelmapImages()为 DisplaySet 的每个图像派生缓存对应的 Labelmap 图像再构造SegmentationPublicInput交给 Cornerstone 分割状态。值得注意的是对于动态容积displaySet.isDynamicVolume true服务会取中间时间点的图像作为参考图像SegmentationService.ts#L476-L484。除这两个公开创建方法外源码还为 DICOM SEG / RTSTRUCT 显示集提供了内部创建入口createSegmentationForSEGDisplaySet与createSegmentationForRTDisplaySet它们由 cornerstone-dicom-seg / cornerstone-dicom-rt 扩展在加载分割数据时调用见 extensions/cornerstone-dicom-seg/src/commandsModule.ts。分割管理Segmentation ManagementsetActiveSegmentation(viewportId, segmentationId) // 设置指定视口上的激活分割 getSegmentations() // 获取全部分割数组 getSegmentation(segmentationId) // 按 ID 获取单个分割 jumpToSegmentCenter(segmentationId, segmentIndex, viewportId) // 跳转到段质心 jumpToSegmentNext(segmentationId, segmentIndex, forViewportId?, ...) // 跳转到下一个/上一个含段切片 highlightSegment(segmentationId, segmentIndex, viewportId) // 高亮指定段对应的还有成对出现的读取方法getActiveSegmentation(viewportId)返回当前激活分割无则返回nullgetActiveSegment(viewportId)返回激活分割中active: true的那个 SegmentSegmentationService.ts#L900-L933。段操作Segment OperationsaddSegment(segmentationId, { segmentIndex?: number, label?: string, color?: [number, number, number, number], // RGBA visibility?: boolean, isLocked?: boolean, active?: boolean }) setSegmentColor(viewportId, segmentationId, segmentIndex, color) // 设置段颜色RGBA setSegmentVisibility(viewportId, segmentationId, segmentIndex, visibility) // 设置段可见性围绕这些基本操作源码还提供了完整的段管理 API 族锁定setSegmentLocked(segmentationId, segmentIndex, isLocked)、toggleSegmentLocked(...)命名与激活setSegmentLabel(...)、setActiveSegment(segmentationId, segmentIndex)可见性切换toggleSegmentVisibility(viewportId, segmentationId, segmentIndex, type)删除removeSegment(segmentationId, segmentIndex, options?)支持通过DefaultHistoryMemo记录 undo/redo 备忘录SegmentationService.ts#L1147-L1162整段分割操作remove(segmentationId)、removeAllSegmentations()、removeRepresentationsFromViewport(viewportId, specifier?)、clearSegmentationRepresentations(viewportId)索引分配getNextAvailableSegmentIndex(segmentationId)返回当前最大段索引 1空分割返回 1。数据结构深入解析Segmentation Object官方文档给出了分割对象的核心结构interface Segmentation { segmentationId: string; label: string; segments: { [segmentIndex: number]: { segmentIndex: number; label: string; locked: boolean; cachedStats: { [key: string]: unknown }; active: boolean; } }; representationData: RepresentationsData; }结合源码可以补充几点关键事实segmentIndex: 0是保留索引代表无标签背景。addSegment中若传入segmentIndex: 0会直接抛出异常SegmentationService.ts#L1011-L1013。representationData按表示类型分键例如Labelmap下存放volumeId或imageIdsContour下存放geometryIds与annotationUIDsMap。可通过getLabelmapVolume(segmentationId)从 Cornerstone 缓存cache.getVolume取出实际的 Labelmap 体数据SegmentationService.ts#L1281-L1295。cachedStats除了一般统计信息还约定存放center{ image: Point3, world: Point3 }坐标供jumpToSegmentCenter使用SegmentationService.ts#L2096-L2121。SEG 加载时还会写入category、type来自SegmentedPropertyCategoryCodeSequence/SegmentedPropertyTypeCodeSequence的 CodeMeaning、algorithmType、algorithmName等元数据。SegmentationRepresentation表示层服务将 Cornerstone 的表示对象包装为 OHIF 侧的结构_toOHIFSegmentationRepresentationSegmentationService.ts#L1842-L1907export type SegmentationRepresentation cstTypes.SegmentationRepresentation { viewportId: string; id: string; // ${segmentationId}-${type}-${viewportId} label: string; fallbackLabel?: string; styles: cstTypes.RepresentationStyle; segments: { [key: number]: SegmentRepresentation; // { segmentIndex, color, opacity, visible } }; };其中id是分割 表示类型 视口三元组的唯一标识getSegmentationRepresentations(viewportId, specifier?)支持按segmentationId和/或typeLabelmap | Contour | Surface过滤表示对象SegmentationService.ts#L254-L273。底层 SegmentationPublicInput创建分割时源码构造的是 Cornerstone 的SegmentationPublicInputSegmentationService.ts#L493-L521其关键字段包括representation.typeLabelmap或Contourrepresentation.data.imageIds/referencedImageIds派生 Labelmap 图像 ID 与参考图像 IDconfig.label显示名称config.labelIsGenerated标记 label 是否为系统自动生成的由调用方传入labelIsGenerated或根据是否传了label推断config.fallbackLabel形如S:{SeriesNumber} {Modality}的回退名称config.segments初始段配置config.cachedStats携带info: S{SeriesNumber}: {SeriesDescription}等摘要信息。代码示例完整分割工作流以下示例完整继承官方文档并补充了获取服务实例与显示集的前置步骤可直接在自定义扩展/Mode 中使用。创建一个 Labelmap 分割const displaySet displaySetService.getDisplaySetByUID(displaySetUID); const segmentationId await segmentationService.createLabelmapForDisplaySet( displaySet, { label: New Label Map Segmentation, segments: { 1: { label: First Label Map Segment, active: true } } } );创建一个 Contour 分割const displaySet displaySetService.getDisplaySetByUID(displaySetUID); const segmentationId await segmentationService.createContourForDisplaySet( displaySet, { label: New Contour Segmentation, segments: { 1: { label: First Contour Segment, active: true } } } );将分割表示挂载到视口创建分割后还需通过addSegmentationRepresentation将其作为表示挂载到目标视口才会渲染。该方法支持指定表示类型与渲染配置await segmentationService.addSegmentationRepresentation(viewport-1, { segmentationId, type: Labelmap, // 不传时3D/Volume 视口默认 Surface其余默认 Labelmap config: { // blendMode?: BlendModes, // 混合模式 // useSliceRendering?: boolean, // 是否使用切片渲染 }, });在挂载过程中服务会根据视口类型自动处理 Labelmap 的 stack→volume 转换convertStackToVolumeViewport/handleVolumeViewportSegmentationService.ts#L1748-L1829若 Stack 视口的 FrameOfReference 与分割一致会将视口重建为ORTHOGRAPHIC容积视口并在VOLUME_VIEWPORT_NEW_VOLUME事件后恢复先前的视角与ViewReference。管理激活分割segmentationService.setActiveSegmentation(viewport-1, segmentationId); // 读取当前激活分割/激活段 const activeSegmentation segmentationService.getActiveSegmentation(viewport-1); const activeSegment segmentationService.getActiveSegment(viewport-1);添加 SegmentsegmentationService.addSegment(segmentationId, { label: Tumor, color: [255, 0, 0, 255], // RGBA 格式 active: true });不传segmentIndex时服务会自动分配当前最大段索引 1getNextAvailableSegmentIndex未指定label时使用Segment {index}。添加完成后会自动把新段设为激活段并对所有包含该分割的视口同步应用color与visibilitySegmentationService.ts#L999-L1068。可见性管理// 设置段可见性 segmentationService.setSegmentVisibility( viewport-1, segmentationId, 1, // segmentIndex true // visible ); // 获取包含该分割的所有视口 ID const viewportIds segmentationService.getViewportIdsWithSegmentation(segmentationId);段样式与颜色// 设置段颜色RGBA segmentationService.setSegmentColor( viewport-1, segmentationId, 1, // segmentIndex [255, 0, 0, 255] // RGBA ); // 读取段颜色 const color segmentationService.getSegmentColor(viewport-1, segmentationId, 1); // 更细粒度的样式控制fillAlpha、outlineWidth 等 segmentationService.setStyle( { viewportId: viewport-1, segmentationId, segmentIndex: 1, type: Labelmap }, { fillAlpha: 0.8 } );关于颜色的一个重要实现细节服务为每个分割维护一张独立的颜色查找表Color LUT并记录在_segmentationIdToColorLUTIndexMap中。这样同一个分割在不同视口上的所有表示都会复用同一张 LUT保证在某个视口修改段颜色后其他视口不会出现颜色回退到默认值的现象SegmentationService.ts#L523-L534。导航与高亮jumpToSegmentNext是 3.11 文档重点补充的导航 API行为区分表示类型对Labelmap直接跳转到段的质心基于cachedStats.center对Contour遍历所有包含该段轮廓数据的切片按方向找到最近切片并跳转若方向尽头没有含段切片则回绕到另一端的首个含段切片。// 跳转到下一个含 segment 1 的切片默认方向 1 segmentationService.jumpToSegmentNext(segmentationId, 1, viewport-1); // 向后跳转并自定义高亮参数 segmentationService.jumpToSegmentNext( segmentationId, 2, // segmentIndex viewport-1, -1, // direction-1 向后 0.95, // highlightAlpha高亮透明度0-1默认 0.9 true, // highlightSegment跳转后是否高亮默认 true 1000, // animationLength高亮动画时长ms默认 750 true // highlightHideOthers是否隐藏其他段默认 false ); // 不指定视口时作用于所有包含该分割的视口 segmentationService.jumpToSegmentNext(segmentationId, 1);参数签名源码 SegmentationService.ts#L1458-L1468jumpToSegmentNext( segmentationId: string, segmentIndex: number, forViewportId?: string, // 可选不传则作用于所有含该分割的视口 direction?: number, // 1 向前默认-1 向后 highlightAlpha?: number, // 高亮透明度默认 0.9 highlightSegment?: boolean, // 默认 true animationLength?: number, // 默认 750ms highlightHideOthers?: boolean, // 默认 false animationFunctionType?: EasingFunctionEnum // 缓动函数默认 EASE_IN_OUT )jumpToSegmentCenter则是直接以世界坐标cachedStats.center.world为靶点重定位视口Labelmap 专属并通过双后端legacyjumpToWorld/ nextsetViewReference完成跳转跳转成功后触发高亮动画SegmentationService.ts#L1565-L1612。highlightSegment对 Labelmap 通过requestAnimationFrame动画插值fillAlpha对 Contour 则插值outlineWidth放大系数 5 倍动画结束后调用resetToGlobalStyle恢复全局样式SegmentationService.ts#L1955-L2065。源码级原理事件转发、颜色 LUT 与后端分派事件转发链路Cornerstone 底层触发SEGMENTATION_*事件 →eventTarget上的监听器_onSegmentationModifiedFromSource等读取evt.detail→_broadcastEvent以 OHIF 事件名广播 → 扩展中通过segmentationService.subscribe(...)接收。这个模式让上层扩展无需直接依赖cornerstonejs/tools的事件常量也便于在转发时统一事件负载。颜色 LUT 生命周期每个分割创建时都会通过addColorLUT注册一张以[0, 0, 0, 0]透明背景开头的颜色表并记录索引SEG/RTSTRUCT 加载时则按 DICOM 段元数据rgba逐段构建 LUTSegmentationService.ts#L617-L667、SegmentationService.ts#L780-L835。setSegmentColor会同步更新_segmentationIdToColorLUTIndexMap确保该分割所有表示的 LUT 引用一致。双后端分派Legacy / Next从源码注释可以看出SegmentationService.ts#L135-L152服务内部维护LegacySegmentationBackend与NextSegmentationBackend两个后端实现通过_segBackend(viewport)按视口类型分派原生PlanarViewportViewportType.PLANAR_NEXT走 Next 后端传统视口走 Legacy 后端。这保证了在新旧视口体系混用的会话中分割的添加、跳转等操作都能在各自视口上以正确方式执行。测试文件 SegmentationService.test.ts#L918-L1000 专门验证了 Next 视口原地渲染 Labelmap、绝不将视口升级为 ORTHOGRAPHIC的行为。测试验证与使用建议单元测试 extensions/cornerstone/src/services/SegmentationService/SegmentationService.test.ts共 3463 行覆盖了以下核心行为可作为 API 契约的权威参考onModeEnter注册 8 个事件监听器、onModeExit/destroy移除并resetgetSegmentation/getSegmentations直接透传 Cornerstone 分割状态getPresentation与getSegmentationRepresentations的 OHIF 表示映射含id三元组拼接规则addSegmentationRepresentation在 Stack 视口上的两种路径无需转换 / 经convertStackToVolumeViewport转换后等待GRID_STATE_CHANGED再挂载无效segmentationId时的快速失败与Segmentation with ID ... not found异常。实际使用建议在扩展的onModeEnter或工具栏命令中获取服务const { segmentationService } servicesManager.services;遵循创建 → 挂载表示 → 激活 → 绘制/编辑 → 样式/可见性控制 → 导航的调用顺序其中挂载表示addSegmentationRepresentation是渲染的前提文档示例常省略但必不可少监听SEGMENTATION_ADDED/SEGMENTATION_MODIFIED驱动面板 UI 刷新而不是轮询状态需要为分割数据做 DICOM 导出时可参考cornerstone-dicom-seg/cornerstone-dicom-rt扩展的commandsModuleextensions/cornerstone-dicom-seg/src/commandsModule.ts中的加载与序列化流程。结语SegmentationService是 OHIF 分割生态的中枢向上对扩展提供一套声明式、事件驱动、与具体渲染引擎解耦的 API向下封装了 CornerstoneJS 的分割状态、表示、颜色 LUT、样式与导航能力。本文所梳理的事件清单、创建/管理/段操作 API、Segmentation数据结构与完整代码示例均以 SegmentationService.ts 的实现与 SegmentationService.test.ts 的测试契约为准读者可据此在自有扩展中快速落地 Labelmap 与 Contour 分割功能。【免费下载链接】ViewersOHIF zero-footprint DICOM viewer and oncology specific Lesion Tracker, plus shared extension packages项目地址: https://gitcode.com/GitHub_Trending/vi/Viewers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考