基于dcm4chee搭建的PACS系统讲解(三)服务端使用Rest API获取study等数据

文章目录

  • DICOMWeb Support模块
  • 主要数据结构ER
  • 查询信息
    • 基本信息
    • metadata信息
    • 统计信息
  • 实践
    • 查询API及参数
    • 解析API返回的json数组
      • 定义VRObjectNode
      • ObjectMapper解析
      • 显示指定tag并解析
  • 后记

前期预研的PACS系统,近期要在项目中上线了。因为PACS系统采用无权限认证,业务系统若直接访问PACS系统获取数据,有认证风险,所以决定将PACS系统中部分数据同步至服务端。

DICOMWeb Support模块

dcm4chee搭建的PACS系统中,包含DICOMWeb Support模块,即web形式访问DICOM对象,包含查询Study、StudySeries及instance等数据API,具体可以查看官方提供的swagger地址。

主要数据结构ER

PACS主要数据结构包括:Patient(患者) / Study(病例) / Series(序列) / SOP Instances(图像信息),ER图可以参考下图

  • Patient(1) - Study(n)
  • Study(1) - Series(n)
  • Series(1) - SOP Instances(n)
    在这里插入图片描述

查询信息

基本信息

在这里插入图片描述
以上API是查询各个数据结构对应的基本信息(主要为DICOMWeb页面展示数据),返回数据为json数组,数据包括:

  • 查询病例:病例信息 + 按病例维度的相关统计
  • 查询序列:病例信息 + 序列信息
  • 查询图像:病例信息 + 序列信息 + 图像信息
  • 查询患者:患者信息 + 按患者维度的相关统计

metadata信息

图片中红框中的API是获取study、series及instance对象在dicom文件中的所有属性,非常的全面。返回数据为json数组。
在这里插入图片描述

统计信息

PACS系统提供了获取patient、study、instance、modality及institution维度的统计信息,返回json形如{"count":10}
在这里插入图片描述

实践

查询API及参数

本文主要涉及查询如下API:

API名称url
study列表http://<host>/dcm4chee-arc/aets/DCM4CHEE/rs/studies?includefield=all
study单条记录http://<host>/dcm4chee-arc/aets/DCM4CHEE/rs/studies?StudyInstanceUID={studyIUID}&includefield=all
series列表http://<host>/dcm4chee-arc/aets/DCM4CHEE/rs/studies/{studyIUID}/series?includefield=all&orderby=SeriesNumber
单条instance记录http://<host>/dcm4chee-arc/aets/DCM4CHEE/rs/studies/{studyIUID}/series/{seriesIUID}/instances?offset=0&limit=1&includefield=all
单条instance metadata记录http://<host>/dcm4chee-arc/aets/DCM4CHEE/rs/studies/{studyIUID}/series/{seriesIUID}/instances/{sopIUID}/metadata

NOTE:includefield属性表示查询PACS支持暴露的所有字段名。

解析API返回的json数组

查询病例数据,返回json数组如下:

[{"00080005":{"vr":"CS","Value":["ISO_IR 100"]},"00080020":{"vr":"DA","Value":["20151124"]},"00080030":{"vr":"TM","Value":["165546.548881"]},"00080050":{"vr":"SH"},"00080054":{"vr":"AE","Value":["DCM4CHEE"]},"00080056":{"vr":"CS","Value":["ONLINE"]},"00080061":{"vr":"CS","Value":["CT"]},"00080090":{"vr":"PN"},"00080201":{"vr":"SH"},"00081190":{"vr":"UR","Value":["http://172.16.100.216:8080/dcm4chee-arc/aets/DCM4CHEE/rs/studies/1.2.826.0.1.3680043.2.1125.1.45651447217485882639453512019955538"]},"00100010":{"vr":"PN","Value":[{"Alphabetic":"PANCREAS_0034"}]},"00100020":{"vr":"LO","Value":["PANCREAS_0034"]},"00100030":{"vr":"DA"},"00100040":{"vr":"CS"},"0020000D":{"vr":"UI","Value":["1.2.826.0.1.3680043.2.1125.1.45651447217485882639453512019955538"]},"00200010":{"vr":"SH","Value":["PANCREAS_0034"]},"00201206":{"vr":"IS","Value":["1"]},"00201208":{"vr":"IS","Value":["205"]}}]

刚开始定义VO与json层次对应,每个属性对应dicom的tag(json中的key值),结果解析失败,不得不将json数据解析为Map形式。

  1. 定义VRObjectNode接收json数组的key和value
  2. 使用fasterxml的ObjectMapper解析
  3. 显示指定tag值,解析到对应属性中

定义VRObjectNode

package com.lizzy.vo.admin.dicom;import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnySetter;public class VRObjectNode {private Map<String, Object> properties = new HashMap<>();@JsonAnySetterpublic void set(String key, Object value) {properties.put(key, value);}// Getter and Setter for propertiespublic Map<String, Object> getProperties() {return properties;}public void setProperties(Map<String, Object> properties) {this.properties = properties;}
}

ObjectMapper解析

import com.fasterxml.jackson.databind.ObjectMapper;public List<VRObjectNode> parse(String url) {ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);if (HttpStatus.OK != responseEntity.getStatusCode()) {log.info("[parse] 访问PACS系统失败(url:{}),失败cdoe:{}", url, responseEntity.getStatusCodeValue());return new ArrayList<VRObjectNode>();}String dataStr = responseEntity.getBody();if (!StringUtils.hasLength(dataStr)) {log.info("[parse] PACS系统中无病例数据(url:{})!", url);return new ArrayList<VRObjectNode>();}	try {ObjectMapper objectMapper = new ObjectMapper();List<VRObjectNode> studies = objectMapper.readValue(dataStr, objectMapper.getTypeFactory().constructCollectionType(List.class, VRObjectNode.class));return studies;} catch (JsonMappingException e) {e.printStackTrace();} catch (JsonProcessingException e) {e.printStackTrace();}
}

显示指定tag并解析

如下代码中,定义一个StudyConvertVo接收study属性。

public void setStudyAttr() {// get nodeList // ...for (VRObjectNode node : nodeList) {	StudyConvertVo vo = new StudyConvertVo();vo.setAccessionNo(parseVRValue("00080050", node));vo.setDcmStudyId(parseVRValue("00200010", node));vo.setStudyIUID(parseVRValue("0020000D", node));vo.setStudyDate(parseVRValue("00080020", node));vo.setStudyTime(parseVRValue("00080030", node));vo.setStudyDescr(parseVRValue("00081030", node));vo.setPatientId(parseVRValue("00100020", node));vo.setPatientBirthdate(parseVRValue("00100030", node));vo.setPatientName(parseVRValue("00100010", node));vo.setPatientSex(parseVRValue("00100040", node));vo.setSeriesNum(parseVRValue("00201206", node));vo.setImageNum(parseVRValue("00201208", node));// save }
}private String parseVRValue(String tag, VRObjectNode node) {HashMap<String, Object> objectMap = (HashMap<String, Object>) node.getProperties().get(tag);if (null == objectMap || !objectMap.containsKey("Value")) {return null;}String parseValue = null;String vr = (String) objectMap.get("vr");// 患者姓名需要特殊处理if ("PN".equals(vr)) {// 此处强转HashMap<String, Object>会报错,提示类型为List<String> List<String> values =  (List<String>) objectMap.get("Value");for (Object value : values) {HashMap<String, Object> nameMap = (HashMap<String, Object>) value;if (nameMap != null && nameMap.containsKey("Alphabetic")) {parseValue = String.valueOf(nameMap.get("Alphabetic"));break;}}} else {List<String> values = (List<String>) objectMap.get("Value");if (CollectionUtils.isNotEmpty(values)) {parseValue = String.valueOf(values.get(0));}}log.debug("vr:{}, value:{}", vr, parseValue);return parseValue;
}

后记

解析dicom数据,可以依赖dcm4chee提供的各个工具包,但若将这些工具包加入到项目中十分的厚重,所以本文采用显示解析dicom tag方式,算是取巧了。。。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

【初阶数据结构】8.二叉树(3)

文章目录 4.实现链式结构二叉树4.1 前中后序遍历4.1.1 遍历规则4.1.2 代码实现 4.2 结点个数以及高度等4.3 层序遍历4.4 判断是否为完全二叉树4.5层序遍历和判断是否为完全二叉树完整代码 4.实现链式结构二叉树 用链表来表示一棵二叉树&#xff0c;即用链来指示元素的逻辑关系…

减轻幻觉新SOTA,7B模型自迭代训练效果超越GPT-4,上海AI lab发布

LLMs在回答各种复杂问题时&#xff0c;有时会“胡言乱语”&#xff0c;产生所谓的幻觉。解决这一问题的初始步骤就是创建高质量幻觉数据集训练模型以帮助检测、缓解幻觉。 但现有的幻觉标注数据集&#xff0c;因为领域窄、数量少&#xff0c;加上制作成本高、标注人员水平不一…

php反序列化--前置知识

&#x1f3bc;个人主页&#xff1a;金灰 &#x1f60e;作者简介:一名简单的大一学生;易编橙终身成长社群的嘉宾.✨ 专注网络空间安全服务,期待与您的交流分享~ 感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持&#xff01;❤️ &#x1f34a;易编橙终身成长社群&#…

文件共享功能无法使用提示错误代码0x80004005【笔记】

环境情况&#xff1a; 其他电脑可以正常访问共享端&#xff0c;但有一台电脑访问提示错误代码0x80004005。 处理检查&#xff1a; 搜索里输入“启用或关闭Windows功能”按回车键&#xff0c;在“启用或关闭Windows功能”里将“SMB 1.0/CIFS文件共享支持”勾选后&#xff08;故…

不同行情下算法的具体使用!

上一篇我们说到了不同公司算法交易的区分&#xff0c;有朋友提出了不同的行情下的算法交易应该怎么使用&#xff0c;小编今天就带大家了解下&#xff01;当然具体实际状况百出&#xff0c;这种可以实际为准&#xff08;韭菜修养全拼实际探讨交流&#xff09;&#xff01; 我们在…

qt做的分页控件

介绍 qt做的分页控件 如何使用 创建 Pagination必须基于一个QWidget创建&#xff0c;否则会引发错误。 Pagination* pa new Pagination(QWidget*);设置总页数 Pagination需要设置一个总的页数&#xff0c;来初始化页码。 pa->SetTotalItem(count);设置可选的每页数量…

Java 每日一题: for 与 foreach 的区别 ?

for 循环&#xff1a;是最基本的循环结构&#xff0c;可以通过初始化语句、循环条件和迭代语句来控制循环的执行。 foreach 循环&#xff08;也称为增强型 for 循环&#xff09;&#xff1a;用于遍历集合或数组中的元素&#xff0c;简化了遍历过程&#xff0c;没有显式地控制索…

[STM32]HAL库实现自己的BootLoader-BootLoader与OTA-STM32CUBEMX

目录 一、前言 二、BootLoader 三、BootLoader的实现 四、APP程序 五、效果展示 六、拓展 一、前言 听到BootLoader大家一定很熟悉&#xff0c;在很多常见的系统中都会存在BootLoader。本文将介绍BootLoader的含义和简易实现&#xff0c;建议大家学习前掌握些原理基础。 …

全链路追踪 性能监控,GO 应用可观测全面升级

作者&#xff1a;古琦 01 介绍 随着 Kubernetes 和容器化技术的普及&#xff0c;Go 语言不仅在云原生基础组件领域广泛应用&#xff0c;也在各类业务场景中占据了重要地位。如今&#xff0c;越来越多的新兴业务选择 Golang 作为首选编程语言。得益于丰富的 RPC 框架&#xff…

编程类精品GPTs

文章目录 编程类精品GPTs前言种类ChatGPT - GrimoireProfessional-coder-auto-programming 总结 编程类精品GPTs 前言 代码类的AI, 主要看以下要点: 面对含糊不清的需求是否能引导出完整的需求面对完整的需求是否能分步编写代码完成需求编写的代码是否具有可读性和可扩展性 …

力扣算法题:矩阵(玄幻不变量法),链表(虚拟头节点,递归法)

20240725 一、矩阵54.螺旋矩阵(循环不变量) 二、链表1 移除链表元素1.1 原链表删除元素&#xff1a;1.2 虚拟头节点&#xff08;&#xff01;&#xff01;&#xff01;&#xff09; 2. 设计链表206. 反转链表(双向指针和递归)双指针递归 交换链表中的元素虚拟头节点法递归法 删…

如何解决 Nginx 与边缘计算节点的集成问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 如何解决 Nginx 与边缘计算节点的集成问题&#xff1f;一、理解集成的需求和目标二、解决网络配置问题三、优化 Nginx 配置四、处理安全与认证问题五、监控与调试…

STM32是使用的内部时钟还是外部时钟

STM32是使用的内部时钟还是外部时钟&#xff0c;经常会有人问这个问题。 1、先了解时钟树&#xff0c;见下图&#xff1a; 2、在MDK中&#xff0c;使用的是HSEPLL作为SYSCLK&#xff0c;因此需要对时钟配置寄存器&#xff08;RCC_CFGR&#xff09;进行配置&#xff0c;寄存器内…

Jacoco 单元测试配置

前言 编写单元测试是开发健壮程序的有效途径&#xff0c;单元测试写的好不好可以从多个指标考量&#xff0c;其中一个就是单元测试的覆盖率。单元测试覆盖率可以看到我们的单元测试覆盖了多少代码行、类、分支等。查看单元测试覆盖率可以使用一些工具帮助我们计算&#xff0c;…

在IDEA中切换分支没有反应

说明&#xff1a;记录一次在IDEA中切换分支没有反应的情况&#xff0c;新建一个分支后&#xff0c;准备暂存代码&#xff0c;切换到其他分支去&#xff0c;发现怎么切都没有反应&#xff0c;也没有切过去&#xff1b; 解决&#xff1a;首先&#xff0c;我想到是不是当前新分支…

如何解决 Nginx 与无服务器架构的集成问题?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01; 文章目录 如何解决 Nginx 与无服务器架构的集成问题&#xff1f; 如何解决 Nginx 与无服务器架构的集成问题&#xff1f; 在当今的云计算时代&#xff0c;无服务器架构因…

机器学习驱动的智能化电池管理技术与应用

目录 主要内容 电池管理技术概述 电池的工作原理与关键性能指标 电池管理系统的核心功能 SOC估计 SOH估计 寿命预测 故障诊断 人工智能机器学习 基础 人工智能的发展 机器学习的关键概念 机器学习在电池管理中的应用案例介绍 人工智能在电池荷电状态估计中的…

AttributeError: ‘list‘ object has no attribute ‘text‘

AttributeError: ‘list‘ object has no attribute ‘text‘ 目录 AttributeError: ‘list‘ object has no attribute ‘text‘ 【常见模块错误】 【解决方案】 示例代码 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英…

谷粒商城实战笔记-63-商品服务-API-品牌管理-OSS获取服务端签名

文章目录 一&#xff0c;创建第三方服务模块thrid-party1&#xff0c;创建一个名为gulimall-third-party的模块2&#xff0c;nacos上创建third-party命名空间&#xff0c;用来管理这个服务的所有配置3&#xff0c;配置pom文件4&#xff0c;配置文件5&#xff0c;单元测试6&…

模型剪枝中有哪些经验|mobile-yolov5-pruning-distillation项目中剪枝知识分析

项目地址&#xff1a;https://github.com/Syencil/mobile-yolov5-pruning-distillation 项目时间&#xff1a;2022年 mobile-yolov5-pruning-distillation是一个以yolov5改进为主的开源项目&#xff0c;主要包含3中改进方向&#xff1a;更改backbone、模型剪枝、知识蒸馏。这里…