基于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://www.mzph.cn/pingmian/50149.shtml

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

相关文章

qt--电子相册

一、项目要求 设计一个电子相册&#xff0c;点击上一张&#xff0c;切换到上一张图片&#xff0c;点击下一张&#xff0c;切换到下一张图片。 要求&#xff1a;图片的展示可以循环&#xff08;QList<QString>&#xff09; 要求&#xff1a;界面美观 二、项目代码 本质是通…

概率论--矩估计

目录 简介 矩估计法的基本步骤 延伸 矩估计法在大样本情况下的准确性和有效性如何评估&#xff1f; 在实际应用中&#xff0c;矩估计法的局限性有哪些具体例子&#xff1f; 如何处理矩估计法在某些情况下可能出现的不合理解或无法唯一确定参数的问题&#xff1f; …

vue3前端开发-小兔鲜项目-form表单的统一校验

vue3前端开发-小兔鲜项目-form表单的统一校验&#xff01;实际上&#xff0c;为了安全起见&#xff0c;用户输入的表单信息&#xff0c;要满足我们的业务需求&#xff0c;参数类型等种种标准之后&#xff0c;才会允许用户向服务器发送登录请求。为此&#xff0c;有必要进行一次…

gstreamer使用cairo实现视频OSD叠加

前言 gstreamer中视频叠加OSD有很多种方式&#xff0c;比如textoverlay添加文字&#xff0c;gdkpixbufoverlay添加图片&#xff0c;clockoverlay或timeoverlay插件显示时间&#xff0c;pango插件进行复杂文本渲染&#xff0c;使用cairo插件绘制图形或者文字。 今天使用最后一…

【React】详解样式控制:从基础到进阶应用的全面指南

文章目录 一、内联样式1. 什么是内联样式&#xff1f;2. 内联样式的定义3. 基本示例4. 动态内联样式 二、CSS模块1. 什么是CSS模块&#xff1f;2. CSS模块的定义3. 基本示例4. 动态应用样式 三、CSS-in-JS1. 什么是CSS-in-JS&#xff1f;2. styled-components的定义3. 基本示例…

ADS 使用教程(二十八)Working with FEM Mesh Field Data in ADS

ADS 使用教程&#xff08;二十七&#xff09;Getting Started with Full 3D FEM Simulation in ADS 在这一节中&#xff0c;我们来谈论一下在ADS中处理有限元法&#xff08;FEM&#xff09;网格和场数据的步骤。 在上一节中&#xff0c;我们进行了FEM仿真&#xff0c;并保存了…

在 MinIO 使用 SVE 将 ARM 带入人工智能数据基础设施领域

MinIO 性能如此之高的原因之一是&#xff0c;我们做了其他人不会或不能做的细粒度工作。从 SIMD 加速到 AVX-512 优化&#xff0c;我们已经完成了艰巨的任务。ARM CPU 架构的最新发展&#xff0c;特别是可扩展矢量扩展 &#xff08;SVE&#xff09;&#xff0c;为我们提供了比前…

《Cross-Modal Dynamic Transfer Learning for Multimodal Emotion Recognition》

Multi-modal系列论文研读目录 文章目录 Multi-modal系列论文研读目录1.ABSTRACT2.INDEX TERMS3.INTRODUCTION4.RELATED WORKSA. MULTIMODAL EMOTION RECOGNITION 多模态情感识别1) CONVENTIONAL FUSION METHODS 常规融合方法2) TRANSFORMER-BASED FUSION METHODS 基于变压器的融…

2023河南萌新联赛第(二)场 南阳理工学院

A. 国际旅行Ⅰ 题目&#xff1a; 思路&#xff1a; 因为题意上每个国家可以相互到达&#xff0c;所以只需要排序&#xff0c;输出第k小的值就可以了。 AC代码&#xff1a; #include<bits/stdc.h> #define int long long #define IOS ios::sync_with_stdio(0);cin.tie…

2024 微信小程序 学习笔记 第二天

1. WXML 模板语法 数据绑定 事件绑定 条件渲染 列表渲染 2. WXSS 模板样式 rpx 样式导入 全局和局部样式 3. 全局配置 window tabBar 配置tabBar案例 4. 网络数据请求 Get请求 Post 请求 加载时请求 5. 案例 -本地生活&#xff08;首页&#xff09; 导航栏 轮播图 九宫格效果…

webpack插件给所有的:src文件目录增加前缀

1.webpack4的版本写法 class AddPrefixPlugin {apply(compiler) {compiler.hooks.compilation.tap(AddPrefixPlugin, (compilation) > {HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(AddPrefixPlugin,(data, cb) > {// 使用正则表达式替换所有包含 /st…

无人机之环保监控篇

随着科技的不断进步&#xff0c;无人机作为一种创新的技术手段&#xff0c;在环保监控领域发挥着越来越重要的作用。 一、覆盖范围广 无人机能够轻松覆盖广阔的地理区域&#xff0c;无论是偏远的山区、广袤的森林还是大型的工业园区。相比传统的地面检测方式&#xff0c;其不…

关于promise的一些例题(运行步骤详细说明)

关于promise的一些例题(详细说明) 基本例题 // 直接运行 输出 1 2 const promise new Promise((resolve, reject) > {console.log(1);resolve();console.log(2); });// then后面放入微队列 promise.then(() > {console.log(3); });// 输出4 之后没有代码了所以运行为队…

17 敏捷开发—Scrum(2)

从上一篇 「16 敏捷开发实践&#xff08;1&#xff09;」中了解了Scrum是一个用于开发和维护复杂产品的框架&#xff0c;是一个增量的、迭代的开发过程。一般由多个Sprint&#xff08;迭代冲刺&#xff09;组成&#xff0c;每个Sprint长度一般为2-4周。下面全面介绍Scrumde 角色…

PostgreSQL 数据库 安装

1、官网下载 起源与发展&#xff1a;PostgreSQL最初起源于加州大学伯克利分校的Postgres项目&#xff0c;该项目始于1986年&#xff0c;并一直演进到1994年。在1995年&#xff0c;Postgres项目增加了SQL翻译程序&#xff0c;并更名为Postgres95。随后&#xff0c;在1996年&…

Linux:core文件无法生成排查步骤

1、进程的RLIMIT_CORE或RLIMIT_SIZE被设置为0。使用getrlimit和ulimit检查修改。 使用ulimit -a 命令检查是否开启core文件生成限制 如果发现-c后面的结果是0&#xff0c;就临时添加环境变量ulimit -c unlimited&#xff0c;之后在启动程序观察是否有core生成&#xff0c;如果…

Linux网络:传输层协议TCP(一)

目录 一、TCP协议的定义 二、确认应答机制ACK 三、序号、确认序号 四、超时重传机制 一、TCP协议的定义 TCP 全称为 "传输控制协议(Transmission Control Protocol"). 人如其名, 要对数据的传 输进行一个详细的控制; TCP 协议段格式 • 源/目的端口号: 表示数据…

解决Windows 11更新错误0x800f081f的详细指南

在尝试更新Windows 11时&#xff0c;用户可能会遇到各种错误代码&#xff0c;其中之一是0x800f081f。这个错误通常与Windows更新组件或系统文件的损坏有关。本文将提供解决这一特定错误的详细步骤&#xff0c;并解释可能的原因。 错误代码0x800f081f概述 错误代码0x800f081f指…

pythonGame-实现简单的贪食蛇游戏

通过python简单复现贪食蛇游戏。 使用到的库函数&#xff1a; import pygame import time import random 游戏源码&#xff1a; import pygame import time import randompygame.init()white (255, 255, 255) yellow (255, 255, 102) black (0, 0, 0) red (213, 50, 80…

新版海螺影视主题模板M3.1全解密版本多功能苹果CMSv10后台自适应主题

苹果CMS2022新版海螺影视主题M3.1版本&#xff0c;这个主题我挺喜欢的&#xff0c;之前也有朋友给我提供过原版主题&#xff0c;一直想要破解但是后来找了几个SG11解密的大哥都表示解密需要大几百大洋&#xff0c;所以一直被搁置了。这个版本是完全解密的&#xff0c;无需SG11加…