Cornerstone3D Tools对影像进行交互(中篇)-注释类工具使用

🏄‍♀️ 前言

这里是关于Cornerstone3D Tools对影像进行交互(中篇 )- 注释类工具的使用介绍。在 Cornerstone3D Tools对影像进行交互(上篇)-基础交互工具及同步器 一文中主要介绍了一下基础交互类工具的使用,感兴趣的小伙伴可以去回顾一下呦。接下来主要介绍的就是注释类的工具,当我们拿到一个影像时,总是会有各种各种需要对影像进行注释标注类操作的需求,比如测量下长短、计算下Mean和面积、做个文字标注、或者圈定一下边缘等等。不过不用着急,上述说的这些操作Cornerstone3D Tools中基本都已涵盖,本文从支持的注释类工具种类及使用场景、编程式操作注释两大方面展开介绍。

🚀 效果演示

依旧是先来看一下案例演示效果,点击查看完整代码

  • 注释工具交互效果演示
    在这里插入图片描述

  • 注释类相关API交互效果演示
    在这里插入图片描述

🏝 工具介绍

对于目前Cornerstone3D Tools中提供的注释类工具,将它们按照功能分为了3大类:基础测量类工具、轮廓&自由绘制类工具及特定场景类绘制工具,分别对应了影像的基础注释功能、自由绘制和轮廓绘制功能及特定场景下的绘制功能。单就代码层面来说,激活使用不同的工具的实现方式基本一致。

本小节主要是对目前版本(~1.70)已支持的工具进行一个罗列介绍,目的在于当遇到某些场景时能够了解到当前版本是否具有相关的交互工具,不感兴趣的小伙伴可以直接跳过或者只针对某一寻找的工具具体查看。

基础测量工具

简单介绍

  • LengthTool(长度测量工具): 最基础的测量工具,在一个切片上计算两点之间的距离,既可以用于volume视图也可以用于stack视图

  • ProbeTool(探针工具):获取影像上某一点的位置和CT值

  • RectangleROITool(长方形ROI测量):方形测量工具,可以计算当前区域的面积、平均值、最大最小值,也可以根据业务需求由圈定的范围自行计算某些数据。

  • EllipticalROITool(椭圆形ROI测量):椭圆形测量工具,同样可以计算区域面积、平均值、最大最小值等

  • CircleROITool(圆形ROI测量):圆形测量工具

  • ArrowAnnotateTool(箭头标注):箭头指示工具,📣 📣 📣 特别说明:很多小伙伴问目前有没有文字标注工具,目前没有单独一个用于文本标注的工具,但是可以使用箭头指示工具替代,在绘制箭头指向需要标注的位置后可以自定义输入标注的文本内容。

  • AngleTool(通用角度测量):测量两条线之间的角度

使用工具

如何在项目中激活使用工具的完整代码可以参考 windowLevel 中的完整代码

// 注册工具
cornerstoneTools.addTool(ProbeTool)const toolGroup = ToolGroupManager.createToolGroup('toolGroupId')
toolGroup.addTool(ProbeTool.toolName)toolGroup.addViewport('viewportId', 'renderingEngineId')// 激活工具
toolGroup.setToolActive(ProbeTool.toolName, {bindings: [{mouseButton: MouseBindings.Primary, // Left Click},],
})

轮廓&自由绘制工具

轮廓绘制工具与基础测量工具不同,针对不同的场景需要灵活选择对应的工具

动态轮廓测量工具(LivewireContourTool

  • 用于进行半自动化轮廓绘制。工具可以通过用户提供的种子点来进行动态计算影像中的最优路径,从而生成相对比较精确的轮廓线。

  • 适用场景:高精度的区域识别识别标注任务,例如病灶的边界识别任务

自由曲线测量工具(PlanarFreehandROITool

  • 自由手绘ROI标注工具,允许用户在图像上自由勾勒出不规则形状的区域轮廓,可以计算选中长度及面积,支持二次编辑调整。

  • 适用场景:相对比较复杂且不规则的区域标注及分割、自由工具无法精准捕获边界需要手动标记时

样条曲线绘制工具(SplineROITool

  • 创建平滑曲线形式的ROI绘制工具,支持用户通过一系列控制点生成光滑的曲线轮廓,可以更好的捕获到复杂的结构轮廓。

  • 适用场景:适用于血管、神经等弯曲边界结构的标注;高平滑度的标注任务等

  • 样条种类:

    • 线性样条(Linear Spline):在控制点之间直接连接直线段,不进行平滑处理。适合简单直线标注,快速但不平滑

    • 张量样条(Cardinal Spline):一种平滑曲线,适度紧贴控制点,并在控制点之间生成柔和过渡的曲线。平滑度适中,适合适度弯曲的区域标注

    • 卡特姆-罗姆样条(Catmull-Rom Spline):一种特殊的 Cardinal 样条,具有自动设置的张力值。它生成的曲线会严格穿过所有控制点,使曲线平滑贴合于每个控制点。严格通过控制点,适合精确标注,不适合过度弯曲。

    • B样条(Cubic B-Spline):一种高阶样条曲线,不必严格穿过控制点,而是生成的平滑曲线会跟随控制点的位置。与 Catmull-Rom 样条不同,B-Spline 曲线控制点对曲线有影响,但不会强制曲线通过控制点。平滑度最高,不严格贴合控制点,适合自然曲线标注。

  • 使用样条工具

    📣 跟直接全局添加及工具组添加工具不同,不同种类的样条工具是以工具实例的方式添加到工具管理器中

    toolGroup.addToolInstance(instanceToolName, SplineROITool.toolName, {spline: {type: CARDINAL,configuration: {[CARDINAL]: {scale: DEFAULT_CARDINAL_SCALE,},},},
    })
    

特定场景绘制工具

脊柱侧弯角度测量工具(CobbAngleTool)

  • 专门用于评估脊柱的弯曲角度(Cobb角)

  • 适用场景:主要用于脊柱侧弯的诊断和评估,可以帮助医生确认脊柱侧弯的严重程度

超声波束的方向测量工具(UltrasoundDirectionalTool)

  • 专门为超声影像设计的一种测量工具,用于在超声图像中标注方向信息,帮助医护人员更好地理解和分析超声成像中的位置和方向

🚢 操作API演示

终于啰里啰嗦的完成了这一大堆工具的简单介绍(这个过程简直比直接写演示Demo还要痛苦🥱),接下来终于就是代码环节了,我们可以通过激活工具的方式绘制注释,但是往往又需要对绘制的工具进行一些编程式操作,接下来介绍一些编程式交互API,主要为数据管理、选中状态管理、锁定状态管理、显示隐藏状态管理、代码添加删除注释等等方面。

// tools 中 的注释管理对象
import { annotation } from "@cornerstonejs/tools";

数据管理

数据管理相关的操作主要基于 annotation 对象的 state 属性,在state中可以对当前注释进行查看、新增、删除等等操作

  • 获取当前工具在某个视图中的所有注释
annotation.state.getAnnotations(toolName, annotationGroupSelector)
  • 获取当前工具的注释数量
annotation.state.getNumberOfAnnotations(toolName, annotationGroupSelector),
  • 获取所有工具的全部注释
annotation.state.getAllAnnotations()
  • 删除所有工具的全部注释
annotation.state.removeAllAnnotations()
  • 获取指定注释
annotation.state.getAnnotation(uid)
  • 删除指定注释
annotation.state.removeAnnotation(uid)

选中状态管理

是否选中状态管理的操作主要基于 annotation 对象的 selection 属性,想要选中某个注释一个方式是通过鼠标直接点击选中,另外一个便是使用编程式的方式进行选中。同一时间有且仅有一个注释处于选中状态。

  • 获取所有选中的注释
annotation.selection.getAnnotationsSelected()
  • 获取某个工具下的注释
annotation.selection.getAnnotationsSelectedByToolName(toolName)
  • 获取选中的个数
annotation.selection.getAnnotationsSelectedCount()
  • 设置某个注释为选中
annotation.selection.setAnnotationSelected(uid);
  • 判断某个注释是否是选中状态
annotation.selection.isAnnotationSelected(uid)
  • 取消第一个注释的选中状态
annotation.selection.deselectAnnotation(uid);

锁定状态管理

是否选中状态管理的操作主要基于 annotation 对象的 locking 属性。值得注意的是:注释中的选中状态与锁定状态是在内部管理的,不会在annotation的对象属性中发生改变。即使是在执行操作成功后,也不能通过state中的isLocked属性对注释进行过滤获取,而是应该通过selectionlocking 属性的对应api获取。

  • 获取当前锁定的所有注释
annotation.locking.getAnnotationsLocked()
  • 获取当前锁定的个数
 annotation.locking.getAnnotationsLockedCount()
  • 设置某个注释的锁定状态
annotation.locking.setAnnotationLocked(Annotation, true);
  • 取消第一个注释的锁定状态
annotation.locking.setAnnotationLocked(Annotation, false);
  • 取消所有注释的锁定状态
nnotation.locking.unlockAllAnnotations();
  • 判断第一个注释是否被锁定
annotation.locking.isAnnotationLocked(Annotation)

显示隐藏状态管理

是否选中状态管理的操作主要基于 annotation 对象的 visibility 属性。与选中和锁定状态不同,当我们试图使用API的方式控制一个注释的显示与否时,确实会更改annotation对象的isVisible属性值。在必要的场景下我们可以通过过滤annotationisVisible属性值来获取到全部隐藏或显示的注释

  • 显示所有的注释
annotation.visibility.showAllAnnotations();
  • 隐藏第一个注释
annotation.visibility.setAnnotationVisibility(uid, false);
  • 显示第一个注释
annotation.visibility.setAnnotationVisibility(uid, true);
  • 判断第一个注释的显示状态
annotation.visibility.isAnnotationVisible(uid)特别说明

‼️ ‼️ ‼️ 特别说明

与其他状态的实时性改变不同,我们可以会发现其他API执行后对应的状态会立刻改变,但是现实隐藏相关的API在执行后没有相对应的执行效果,在执行完后我们需要视图重新渲染才可以获取对应的效果

getRenderingEngine(renderingEngine_id).getViewport(volumeId).render();

新增注释

当我们通过算法或者其他方式获取到了一个ROI区域时,可能需要在影像初始化加载完后直接就标注在了影像上,而不是让用户交互标注,这种情况下我们就需要编程式新增注释

  1. 处理坐标点

编程式新增注释时,最重要的就是注释的坐标点,根据要添加的注释形式来传参对应的世界坐标

//  假设我们需要向以下二维坐标点中添加一个注释,首先我们需要知道当前二维坐标点是基于什么定位的
// canvas定位:位置不会跟着影像的不同而不同,绘制后与像素位置保持一致
// 影像定位:位置与影像强绑定,同一坐标点不同的影像转换成的世界坐标不同,如果是进行ROI标注,一般都是影像定位
const canvasPos = [[0,150], [300,150]];// 二维坐标转世界坐标 => 当前坐标点的位置是基于canvas定位的
const worldPos = canvasPos.map(point => viewport.canvasToWorld(point))// 二维坐标转时间坐标 => 当前坐标点的位置是基于影像定位的
const worldPos1 = canvasPos.map(point => csUtils.imageToWorldCoords(viewport.getCurrentImageId(), point))
  1. 创建一个新注释

注释是按照一定的规则声明的,基本属性如下,针对不同的工具的注释对象,最大的区别点在于point属性值。

const viewport = getRenderingEngine(renderingEngine_id)?.getViewport(volumeId);
const camera = viewport?.getCamera();const newAnnotation1 = {annotationUID: csUtils.uuidv4(), // 随机生成的注释idinvalidated: true,isLocked: false,isVisible: true,metadata: {FrameOfReferenceUID: viewport.getFrameOfReferenceUID,cameraFocalPoint: camera.focalPoint,cameraPosition: camera.position,volumeId: ctVolumeId,viewPlaneNormal: camera.viewPlaneNormal,viewUp: camera.viewUp,referencedImageId: '',toolName: 'Length',},data: {cachedStats: {},handles: {activeHandleIndex: null,points: points, // 重点:注释需要的世界坐标textBox: {hasMoved: false,worldPosition: [0, 0, 0],worldBoundingBox: {topLeft: [0, 0, 0],topRight: [0, 0, 0],bottomLeft: [0, 0, 0],bottomRight: [0, 0, 0]}}},},
};
  1. 添加到管理器中
annotationManager.addAnnotation(newAnnotation1,annotationManager.getGroupKey(document.querySelector(`#${volumeDom[0]}`)));
viewport.render(); // 注意:在新增完之后必须再次进行刷新渲染

样式配置

除了对已有的注释状态进行管理外,可能会有要同时显示很多注释,但是注释的含义不一样,需要通过颜色、粗细等外观性的可视化属性去区分,这就涉及到了对注释的样式进行配置。

我们可以通过4个不同的等级去配置注释的样式,配置后作用的范围也有所不同,按以下顺序优先级逐渐提升:

  • 全局级别配置:全局性配置,作用于全部工具组及视图
const newStyle = {global: {color: 'rgb(0, 100, 0)',colorSelected: 'rgb(255, 0, 0)',colorLocked: 'rgb(0, 0, 255)',}}
// 用于进行全局配置的API
annotation.config.style.setDefaultToolStyles(newStyle);
  • 工具组级别配置:作用于指定工具组及工具组作用的所有视图
const styles = {Length: {colorHighlighted: 'rgb(255, 0, 0)',},global: {lineWidth: '2',},}// 对工具组进行配置APIannotation.config.style.setToolGroupToolStyles(toolGroupId, styles);
  • 视图级别配置:作用于指定的视图
const styles = {Length: {colorHighlighted: 'rgb(255, 0, 255)',},global: {lineWidth: '2',},
}
// 对视图进行配置APIannotation.config.style.setViewportToolStyles(volumeId, styles);
  • 注释级别配置:仅对当前配置的注释有效
const styles = {colorHighlighted: 'rgb(255, 0, 0)',
};
// 对某个注释配置API
annotation.config.style.setAnnotationStyles(uid, styles);

除了了解如何进行配置外,简单介绍下我们可以对注释的哪些样式进行配置,在上述示例中的colorHighlighted属性是如何得来的。
一个注释的样式主要由3部分组成 样式的基础属性 + 注释状态 + 注释模式

  • 基本属性支持的配置项:
type Properties =| 'color'| 'colorAutoGenerated'| 'lineWidth'| 'lineWidthAutoGenerated'| 'lineDash'| 'textBoxFontFamily'| 'textBoxFontSize'| 'textBoxColor'| 'textBoxBackground'| 'textBoxLinkLineWidth'| 'textBoxLinkLineDash';
  • 注释状态
type States = '' | 'Highlighted' | 'Selected' | 'Locked' | 'AutoGenerated';
  • 注释模式
type Modes = '' | 'Active' | 'Passive' | 'Enabled';

所以上述示例中的 colorHighlighted 表示注释高亮状态下的颜色。

不同状态的表现

最后的最后,简单接上文再演示一下不同状态下注释的行为区别

  • Active:激活状态,很容易理解,就是我们激活一个工具时可以执行的操作,可以新增、移动、选中等等所有操作

  • Passive:被动状态,在注释工具中来说,已存在注释行为与Active状态一致,但是不能通过工具新增注释

  • Enabled:可用状态,仅仅是渲染注释内容,不可以进行移动、选中、更改等操作

  • Disable:禁用状态,禁用当前工具,不显示注释内容

所以我们可以根据日常的业务需求选择合适的注释状态、如果仅仅是对已存在的注释进行操作,则可以设置状态为Passive,如果只是对注释进行展示禁止用户二次编辑,可以设置状态为Enabled。

🌶 结束语

至此,我们的交互工具(中篇)- 注释类工具的介绍就结束啦,文中主要就目前已支持的注释种类、注释管理及注释不同状态的管理等做了详细介绍。大家可以通过运行Demo示例自行感受一下不同工具的展示效果及API执行结果,本文是对Demo代码的一个说明及总结。关于大家比较关注的注释导出、自定义注释工具会在后续扩展篇中单独出一篇,Cornerstone3D学习中, 文中涉及到的代码已全部更新至 github,欢迎评论交流 👏👏👏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/56521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【优先算法】--双指针1

“一念既出,万山无阻。”加油陌生人! 目录 1.双指针--移动零 2.双指针-复写零 ok,首先在学习之前,为了方便大家后面的学习,我们这里需要补充一个知识点,我这里所谓的指针,不是之前学习的带有…

Linux系统移植

目录 一、简介 嵌入式Linux系统移植组成部分: 二、搭建交叉开发环境 选择交叉开发环境的原因: 1.Ubuntu和Windows下的文件互传 2.Ubuntu 下 NFS和 SSH服务开启 3.Ubuntu 交叉编译工具链安装 3.1修改环境变量 3.2安装相关库 三、bootloader的选择和移植 编译U-boot 总…

中大型企业网络架构和建设方案

1. 需求分析 (1)用户需求: 员工访问:支持内部员工通过有线和无线网络访问企业资源。 远程访问:支持远程办公员工通过VPN安全访问企业内部资源。 合作伙伴和客户访问:允许外部合作伙伴和客户通过受控渠道访问…

Linux 外设驱动 应用 2 KEY 按键实验

2 按键 2.1 按键介绍 按键是指轻触式按键开关,也称之为轻触开关。按键开关是一种电子开关,属于电子元器件类,最早出现在日本,称之为:敏感型开关,使用时以满足操作力的条件向开关操作方向施压开关功能闭合…

东方通 TongWebV7 Docker 部署与 Spring Boot 集成指南

东方通 TongWebV7 Docker 部署与 Spring Boot 集成指南 文章目录 东方通 TongWebV7 Docker 部署与 Spring Boot 集成指南一 TongWeb V7二 Spring Boot JAR 配置文件三 修改 maven 依赖四 docker compose 启动项目五 查看 docker 信息 本文详细讲解了如何在 Docker 环境中将东方…

【笔记】Day2.5.1查询运费模板列表(未完

(一)代码编写 1.阅读需求,确保理解其中的每一个要素: 获取全部运费模板:这意味着我需要从数据库中查询所有运费模板数据。按创建时间倒序排序:这意味着查询结果需要根据模板的创建时间进行排序&#xff0…

嵌入式学习-IO进程-Day02

嵌入式学习-IO进程-Day02 标准IO函数接口 fread,fwrite 文件指针偏移函数 文件IO 概念 文件IO的特点 文件描述符 文件IO的函数接口 open 打开文件 close 关闭文件 read 读函数 write 写函数 lseek 移动文件指针 标准IO和文件IO对比 目录操作函数 opendir 打开目录 c…

套接字Socket

套接字 在网络中通过 IP 地址来表示和区别不同的主机,通过端口号来标识和区分一台主机中的不同应用进程,端口号拼接到 IP 地址即构成套接字 Socket。在网络中采用发送方和接收方的套接字来识别端点。套接字,实际上是一个通信端点&#xff0c…

C语言初阶小练习2(三子棋小游戏的实现代码)

这是C语言小游戏三子棋的代码实现 test.c文件是用来测试的部分 game.h文件是用来声明我们说写出的函数 game.c文件是用来编写我们的功能实现函数部分 1.test.c #define _CRT_SECURE_NO_WARNINGS 1 #include"game.h" void menu() {printf("***************…

使用 cmake 在 x86 系统中为 arm 系统交叉编译程序

原理: 在 x86 系统里使用交叉编译工具链(arm 版 gcc/g)编译程序,然后放在 arm 系统里运行。 arm 版本 使用 lscpu 查看 cpu 架构 版本说明armv732 bitarmv8/arrch6464 bit 安装交叉编译工具链 # 针对 armv7 sudo apt install…

库卡ForceTorqueControl(一)

1. 功能说明 ForceTorqueControl 是一个可后载入的备选软件包,具有下列功能: 执行取决于测得的过程力和力矩的运动 遵守过程力和力矩,不取决于工件的位置和尺寸 遵守加工工件期间复杂的过程力变化 沿着根据测得的过程力编程的轨迹调整速度 通…

MySQL上新:MySQL 9.1.0发布

MySQL 9.1.0 已经于 2024 年 10 月 15 日正式发布。这是一个创新版本,增加了一些新功能、修复了一些问题并且弃用了一些旧功能。 同时发布的还有 MySQL 8.4.3 以及 MySQL 8.0.40。 以下是该版本包含的部分更新。 原子DDL 在该版本之前,虽然 CREATE DAT…

【高分论文密码】AI赋能大尺度空间模拟与不确定性分析及数字制图

随着AI大语言模型的广泛应用,大尺度空间模拟预测与数字制图技术在不确定性分析中的重要性日益凸显。这些技术已经成为撰写高分SCI论文的关键工具,被誉为“高分论文密码”。大尺度模拟技术能够从不同的时空尺度揭示农业生态环境领域的内在机理和时空变化规…

JAVA开源项目 课程智能组卷系统 计算机毕业设计

本文项目编号 T 009 ,文末自助获取源码 \color{red}{T009,文末自助获取源码} T009,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 老…

【JAVA毕业设计】基于Vue和SpringBoot的医院电子病历管理系统

本文项目编号 T 008 ,文末自助获取源码 \color{red}{T008,文末自助获取源码} T008,文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 医…

Focal Loss

Focal Loss Kullback-Leibler Divergence(相对熵)NLL LossCross Entropy LossBalanced Cross Entropy LossFocal Loss应用场景:1、车道检测与分类 Kullback-Leibler Divergence(相对熵) KL散度(Kullback-L…

python自动化办公实例(使用openpyxl、os处理统计Excel表中的数据并将其合并)

源数据格式 以其中一个表格为例 可以看到表中数据比较杂乱且并没有我们想要的数据、指标(如一等奖、二等奖的数量)不利于下一步数据的分析。所以我们需要手动对数据进行一些处理,大致格式如下: 数据处理格式 这里的手动处理可以…

图像的空域处理实验作业

# 在绘图中显示中文字体,而非乱码 from pylab import mpl mpl.rcParams["font.sans-serif"] ["SimHei"]import cv2 import matplotlib.pyplot as plt import numpy as np1 绘制伽马变换的函数图像,并导入一张图像,对其进…

基础算法(6)——模拟

1. 替换所有的问号 题目描述: 算法思路: 从前往后遍历整个字符串,找到问号之后,尝试用 a ~ z 的每一个字符替换即可 注意点:需考虑数组开头和结尾是问号的边界情况 代码实现: class Solution {public …

《深度学习》OpenCV FisherFaces算法人脸识别 原理及案例解析

目录 一、FisherFaces算法 1、什么是FisherFaces算法 2、原理 3、特点 4、算法步骤 1)数据预处理 2)特征提取 3)LDA降维 4)特征投影 5)人脸识别 二、案例解析 1、完整代码 运行结果: 一、Fish…