项目结构
项目架构
│
├── extensions
│ ├── default # 默认功能
│ ├── cornerstone # 使用 Cornerstonejs 处理 2D/3D 图像
│ ├── cornerstone-dicom-sr # 结构化报告 (DICOM SR)
│ ├── measurement-tracking # 测量追踪功能
│ └── dicom-pdf # 在视口中查看 DICOM 包装的 PDF 文件
│ # 还有更多扩展功能...
│
├── modes
│ └── longitudinal # 纵向测量追踪模式
│ └── basic-dev-mode # 使用 Cornerstone 的基础查看器 (面向开发者的模式)
│ # 还有更多模式...
│
├── platform
│ ├── core # 业务逻辑核心
│ ├── i18n # 国际化支持
│ ├── ui # React 组件库
│ └── app # 连接平台和扩展项目
│
├── ... # 其他共享配置文件
├── lerna.json # MonoRepo (Lerna) 设置
├── package.json # 共享的开发依赖项和命令
└── README.md # 项目说明文档
OHIF v3 由以下组件组成,将在进一步中详细描述 部分:
- @ohif/app:控制扩展注册、模式的核心框架 组合和路由。
- @ohif/core:一个有用且可重复使用的医学成像功能库 对于网络。
- @ohif/ui:一个可重用的组件库,用于构建 OHIF 风格的应用程序 跟。
- Extensions:一组用于构建应用程序的构建块。OHIF组织 维护一些核心库。
- Modes:告诉 @ohif/app 如何撰写的配置对象 扩展,用于在平台的不同路线上构建应用程序。
OHIF 为一些常见的使用场景维护了少量功能强大的扩展。这些扩展与 OHIF/Viewers 仓库一起存放在项目的根目录下的 extensions/ 文件夹中。
下面那个表格展示了 OHIF 中各种类型模块的功能描述。
OHIF V3 组件库
@ohif/app
此库是使用模式、扩展和构建的核心库 一个应用程序。扩展可以作为应用配置传入,并且将 应用程序在适当的时间使用和初始化。后 初始化 Viewer 将使用扩展和模式并构建 所需的路线,然后可以通过研究列表访问这些内容,或直接通过 URL 参数。
发布时,模式也将通过配置插入到应用程序中,但这 仍然是一个正在开发/讨论的领域,他们目前正在 在测试版中从窗户拉出。
这个框架的未来想法只涉及添加模式和获取 在运行时或构建时都需要扩展版本,但此决定 还有待商榷。
视口重新渲染优化
我们利用 React 记忆模式来防止不必要的重新渲染 除非视口属性的某些方面发生变化。你可以采取 查看组件中的函数以 看看这是如何完成的。areEqualOHIFCornerstoneViewport
function areEqual(prevProps, nextProps) {
if (prevProps.displaySets.length !== nextProps.displaySets.length) {
return false;
}
if ( prevProps.viewportOptions.orientation !== nextProps.viewportOptions.orientation )
{ return false; } // rest of the code
如您所见,我们检查道具是否为真,如果是,我们将 如果属性或方向发生变化,请重新渲染视口 变化。needsRerenderingdisplaySets
我们使用 viewportId 来标识视口,并在 React 中将其用作键 渲染。这很重要,因为它允许我们跟踪视口 以及它的状态,并且还让 React 在 网格而不重新渲染它。但是,在某些情况下,我们需要这样做 强制重新渲染视口,例如,当视口被冻结时 具有新的细分。对于这些情况,我们使用 prop 强制重新渲染视口。您可以将其添加到needsRerenderingviewportOptions
视口组件由组件管理。哪个视口 组件的使用取决于:ViewportGrid
- 挂起协议
- 布局配置
- 已注册的 SopClassHandlers
@ohif/core
OHIF核心是一套经过精心维护和测试的基于网络的医学成像设备 函数和类。此库包括用于以下位置的管理器和服务 在查看器应用程序中。
OHIF 核心在很大程度上类似于 v2 中的 @ohif/core 库,但是很多 逻辑已移至扩展:但是,有关 DICOMWeb 和其他的所有逻辑 数据获取机制已被拉出,因为这些机制现在存在于扩展中, 稍后再述。
@ohif/ui
首先,我们发现一个巨大的时间消耗/进入壁垒正在建造新的 UI及时符合OHIF的主题。出于这个原因,我们建立了一个新的 UI组件库,其中包含构建其所需的所有组件 自己的观众。
这些组件仅是演示性的,因此您可以将它们与任何内容重复使用 你想要的逻辑。由于组件是演示性的,因此您可以换掉 如果您希望为白标,请使用具有符合 API 的自定义 UI 库的 @ohif/ui 观众。UI 库是为了让开发更轻松、更快捷,但 扩展组件不是强制性的。
OHIF V3体系结构
Video Tutorials | OHIF 扩展模式
OHIF CLI 促进了创建、链接和发布 OHIF 模式和扩展。以下视频演示了如何使用 CLI 进行
- 创建模式和扩展
- 链接本地模式和扩展
- NPM 的发布模式和扩展
- 向 OHIF 添加已发布的模式和扩展
- 向 OHIF 提交模式
更新数据源的配置
可以使用该方法更新现有数据源的配置。以下代码片段 代码演示了如何使用来更新 配置现有 DICOMWeb 数据源(名为 ),并使用 Google Cloud Healthcare API 数据源的配置。ExtensionManager.updateDataSourceConfigurationupdateDataSourceConfigurationdicomweb
extensionManager.updateDataSourceConfiguration( "dicomweb", { name: 'GCP', wadoUriRoot: 'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb', qidoRoot: 'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb', wadoRoot: 'https://healthcare.googleapis.com/v1/projects/ohif-cloud-healthcare/locations/us-east4/datasets/ohif-qa-dataset/dicomStores/ohif-qa-2/dicomWeb', qidoSupportsIncludeField: true, imageRendering: 'wadors', thumbnailRendering: 'wadors', enableStudyLazyLoad: true, supportsFuzzyMatching: true, supportsWildcard: false, dicomUploadEnabled: true, omitQuotationForMultipartRequest: true, },);
模板:布局模式
概述
LayoutTemplates是 v3 中的一个新概念,模式使用它来控制布局 的路线。布局模板是一个 React 组件,它被赋予了一组 定义 API 以访问工具栏状态、命令和热键的管理器,如 以及布局模板定义的属性。
例如,默认的 LayoutTemplate 接受 leftPanels、rightPanels 和 视口作为属性,它使用它来构建其视图。
此外,对结构具有完全的控制权。 应用。你可以在左侧放置工具,或者有严格的指导 工作流 通过以编程方式设置工具,您可以选择适合您的用例。layout template
const getLayoutTemplateModule = (/* ... */) => [
{
id: 'exampleLayout',
name: 'exampleLayout',
component: ExampleLayoutComponent,
},
];
传递给的是经理和服务 使用定义的模式左/右面板、模式定义的视口和 OHIF 。LayoutTemplate 利用 extensionManager 来抓取类型 扩展模块条目:propslayoutTemplateViewportGridComp*.getModuleEntry(id)
布局模板的简化代码为:Default extension
extensions/default/src/ViewerLayout/index.jsx
import React from 'react';
import { SidePanel } from '@ohif/ui';
function Toolbar({ servicesManager }) {
const { ToolBarService } = servicesManager.services;
return (
<>
// ToolBarService.getButtonSection('primary') to get toolbarButtons
{toolbarButtons.map((toolDef, index) => {
const { id, Component, componentProps } = toolDef;
return (
<Component
key={id}
id={id}
{...componentProps}
bState={buttonState}
isActive={isActive}
onInteraction={args => ToolBarService.recordInteraction(args)}
/>
);
})}
</>
);
}
function ViewerLayout({
// From Extension Module Params
extensionManager,
servicesManager,
hotkeysManager,
commandsManager,
// From Modes
leftPanels,
rightPanels,
viewports,
ViewportGridComp,
}) {
const getPanelData = id => {
const entry = extensionManager.getModuleEntry(id);
const content = entry.component;
return {
iconName: entry.iconName,
iconLabel: entry.iconLabel,
label: entry.label,
name: entry.name,
content,
};
};
const getViewportComponentData = viewportComponent => {
const entry = extensionManager.getModuleEntry(viewportComponent.namespace);
return {
component: entry.component,
displaySetsToDisplay: viewportComponent.displaySetsToDisplay,
};
};
const leftPanelComponents = leftPanels.map(getPanelData);
const rightPanelComponents = rightPanels.map(getPanelData);
const viewportComponents = viewports.map(getViewportComponentData);
return (
<div>
<Toolbar servicesManager={servicesManager} />
<div>
{/* LEFT SIDEPANELS */}
<SidePanel
side="left"
defaultComponentOpen={leftPanelComponents[0].name}
childComponents={leftPanelComponents}
/>
{/* TOOLBAR + GRID */}
<ViewportGridComp
servicesManager={servicesManager}
viewportComponents={viewportComponents}
commandsManager={commandsManager}
/>
{/* Right SIDEPANELS */}
<SidePanel
side="right"
defaultComponentOpen={rightPanelComponents[0].name}
childComponents={rightPanelComponents}
/>
</div>
</div>
);
}
悬挂协议
悬挂协议是任何放射学观察器的重要组成部分。 OHIF 使用悬挂协议来处理视口中图像的排列。在 简而言之,已注册的协议将与 DisplaySet 匹配 可用。每个协议都会得到一个分数,并且它们会被排名。这 应用获胜协议(最高分)并运行其设置以用于视口 待安排。
在挂起协议中,您可以:OHIF-v3
- 定义视口应从什么布局开始(例如,2x2 布局)
- 指定视口的类型及其方向(例如,堆栈、带有 Sagittal 视图的体积)
- 定义在布局的哪个视口中显示哪个 displaySets(例如,具有“CT”模态的 displaySet 和 “Coronary Arteries” 的 “SeriesDescription” 将显示在布局的第一个视口中)
- 应用某些初始视口设置(例如,反转对比度、跳转到特定切片等)
- 为视口添加特定的同步规则(例如,同步索引 1、2 的视口缩放,或同步索引 2、3 的视口的 VOI)
使用您可以提供/注册 OHIF 的协议以 利用。hangingProtocolModule
这是一个示例协议,如果使用该协议,它将悬挂一个 1x3 布局,第一个视口显示 CT 图像,第二个视口显示 PT 图像,第三个视口显示它们的融合,所有这些都以矢状方向实现视图
const oneByThreeProtocol = {
id: 'oneByThreeProtocol',
locked: true,
name: 'Default',
createdDate: '2021-02-23T19:22:08.894Z',
modifiedDate: '2022-10-04T19:22:08.894Z',
availableTo: {},
editableBy: {},
imageLoadStrategy: 'interleaveTopToBottom',
protocolMatchingRules: [
{
attribute: 'ModalitiesInStudy',
constraint: {
contains: ['CT', 'PT'],
},
},
],
displaySetSelectors: {
ctDisplaySet: {
seriesMatchingRules: [
{
weight: 1,
attribute: 'Modality',
constraint: {
equals: {
value: 'CT',
},
},
required: true,
},
{
weight: 1,
attribute: 'isReconstructable',
constraint: {
equals: {
value: true,
},
},
required: true,
},
],
},
ptDisplaySet: {
seriesMatchingRules: [
{
attribute: 'Modality',
constraint: {
equals: 'PT',
},
required: true,
},
{
weight: 1,
attribute: 'isReconstructable',
constraint: {
equals: {
value: true,
},
},
required: true,
},
{
attribute: 'SeriesDescription',
constraint: {
contains: 'Corrected',
},
},
],
},
},
stages: [
{
id: 'hYbmMy3b7pz7GLiaT',
name: 'default',
viewportStructure: {
layoutType: 'grid',
properties: {
rows: 1,
columns: 3,
},
},
viewports: [
{
viewportOptions: {
viewportId: 'ctAXIAL',
viewportType: 'volume',
orientation: 'sagittal',
initialImageOptions: {
preset: 'middle',
},
syncGroups: [
{
type: 'voi',
id: 'ctWLSync',
source: true,
target: true,
},
],
},
displaySets: [
{
id: 'ctDisplaySet',
},
],
},
{
viewportOptions: {
viewportId: 'ptAXIAL',
viewportType: 'volume',
orientation: 'sagittal',
initialImageOptions: {
preset: 'middle',
},
},
displaySets: [
{
id: 'ptDisplaySet',
},
],
},
{
viewportOptions: {
viewportId: 'fusionSAGITTAL',
viewportType: 'volume',
orientation: 'sagittal',
initialImageOptions: {
preset: 'middle',
},
syncGroups: [
{
type: 'voi',
id: 'ctWLSync',
source: false,
target: true,
},
],
},
displaySets: [
{
id: 'ctDisplaySet',
},
{
options: {
colormap: 'hsv',
voi: {
windowWidth: 5,
windowCenter: 2.5,
},
},
id: 'ptDisplaySet',
},
],
},
],
createdDate: '2021-02-23T18:32:42.850Z',
},
],
numberOfPriorsReferenced: -1,
};
function getHangingProtocolModule() {
return [
{
id: 'oneByThreeProtocol',
protocol: oneByThreeProtocol,
},
];
}
协议
id
协议的唯一标识符,此 ID 可以在模式配置中使用 指定应为特定模式使用哪个协议。一种模式可以 通过其 ID 请求协议(这使得 OHIF 在没有的情况下应用该协议 matching),或者提供一个 ID 数组,该数组将 使 ProtocolEngine 选择最佳匹配协议(基于 protocolMatching rules,这是下一节)。
imageLoadStrategy
图像加载策略指定了一个函数(按名称),其中包含要重新排序的逻辑 图像加载请求。这允许加载之前查看的图像 比装得更晚的早。可用的策略包括:
- interleaveTopToBottom 从顶部开始,然后向底部工作,用于正在加载的所有序列
- interleaveCenter 就像从上到下一样,但从中心开始
- 第 n 个实例是加载每 n 个实例的策略,从中心开始 和终点,然后沿着图像逐渐填充。这导致部分 图像查看非常快。
protocolMatchingRules
协议的标准列表以及提供的排名分数。
- weight:匹配规则的权重。最终,所有注册的 协议根据权重进行排序,获胜的协议获得 应用于查看器。
- attribute:需要匹配的标签。这可以是 研究级元数据或自定义属性,例如“StudyInstanceUID”, “StudyDescription”, “ModalitiesInStudy”, “NumberOfStudyRelatedSeries”, “NumberOfSeriesRelatedInstances” 除了这些标签之外,您还可以使用之前注册的自定义属性。 我们稍后将对此进行详细了解。
- from:表示属性的来源。这允许获取值 从其他对象(如实例对象)而不是从 当前的一个。prior
- constraint:属性需要满足的约束。它接受的 a 可以是 [、、、、、validatorequalsdoesNotEqualcontainsdoesNotContainstartsWithendsWidth]
from 属性
该属性允许您从另一个对象中检索要测试的属性,例如上一个研究、研究的整个列表或模块中提供的其他值。from
OHIF 提供的值可供您使用:
- activeStudy:使用活动研究的元数据进行匹配
- studies:使用研究列表(所有研究)的元数据进行匹配
- allDisplaySets:所有可用的显示集
- displaySets:如果选择器与某个研究匹配,则这些是该研究的显示集
- prior:研究列表中第一个研究的元数据,该研究不是活动研究
- options:在匹配过程中,我们还提供了一个 Options 对象,其中包含您可以用作值的以下信息:from
- studyInstanceUIDsIndex:研究列表中的研究索引
- instance:被匹配实例的元数据,恰好是 displaySet.instance 元数据。
displaySetSelectors(必需)
定义协议将用于排列的显示集。
displaySetSelectors: {
ctDisplaySet: {
seriesMatchingRules: [
{
weight: 1,
attribute: 'Modality',
constraint: {
equals: {
value: 'CT',
},
},
required: true,
},
{
weight: 1,
attribute: 'isReconstructable',
constraint: {
equals: {
value: true,
},
},
required: true,
},
],
},
ptDisplaySet: {
seriesMatchingRules: [
{
attribute: 'Modality',
constraint: {
equals: 'PT',
},
required: true,
},
{
weight: 1,
attribute: 'isReconstructable',
constraint: {
equals: {
value: true,
},
},
required: true,
},
{
attribute: 'SeriesDescription',
constraint: {
contains: 'Corrected',
},
},
],
},
}
如上所述,我们指定了两个 displayset:1) ctDisplaySet 、 2) ptDisplaySet ctDisplaySet 将与所有 CT 和可重构的系列匹配 ptDisplaySet 将与所有 PT 和可重构的系列匹配。
如您所见,每个选择器都由一个作为键和一组 (displaySetMatchingRules) 组成,后者为 displaySet 提供分数 根据匹配规则。得分最高的 displaySet 将用于 。idseriesMatchingRulesid
阶段
每个协议都可以定义一个或多个阶段。每个阶段都定义了特定的布局和视口规则。因此,该属性是一个对象数组,每个对象都是一个阶段。stages
viewportStructure(视口结构)
定义查看器的布局。您可以定义 和 的数量。应该有数量 属性中的视口配置。请注意,视口的顺序是首先是行,然后是列。rowscolumnsrows * columnsviewports
viewportStructure: {
type: 'grid',
properties: {
rows: 1,
columns: 2,
viewportOptions: [],
},
},
除了相等的视口大小外,您还可以定义视口以跨越多行或多列。
viewportStructure: {
type: 'grid',
properties: {
rows: 1,
columns: 2,
viewportOptions: [
{
x: 0,
y: 0,
width: 1 / 4,
height: 1,
},
{
x: 1 / 4,
y: 0,
width: 3 / 4,
height: 1,
},
],
},
},
视口
此字段包括将挂在查看器上的视口。
viewports: [
{
viewportOptions: {
viewportId: 'ctAXIAL',
viewportType: 'volume',
orientation: 'sagittal',
initialImageOptions: {
preset: 'middle',
},
syncGroups: [
{
type: 'voi',
id: 'ctWLSync',
source: true,
target: true,
},
],
},
displaySets: [
{
id: 'ctDisplaySet',
},
],
},
// the rest
],
服务
具体的可以查看ohif官方文档哦~~~