基于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;界面美观 二、项目代码 本质是通…

mac中如何使用obs推流以及使用vlc播放

使用obs推流 1.打开obs&#xff0c;在“来源”框中->点加号->选择媒体源->选择本地ts文件 2.obs中->点击右下角设置->点直播->服务选自定义->服务器填写你的srt服务url&#xff0c;比如&#xff1a;srt://192.168.13.211:14000?modecaller 注意&#xff…

中小企业常见的网络安全问题及防范措施

在数字化浪潮的推动下&#xff0c;我国中小企业的信息化建设取得了显著成就。然而&#xff0c;随着网络安全形势的日益严峻&#xff0c;中小企业在网络安全方面的短板逐渐暴露出来。本文将从中小企业网络安全现状出发&#xff0c;深入剖析其存在的问题&#xff0c;并提出针对性…

概率论--矩估计

目录 简介 矩估计法的基本步骤 延伸 矩估计法在大样本情况下的准确性和有效性如何评估&#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;为我们提供了比前…

【微信小程序】微信小程序设置本地背景图片在真机无法显示的解决方案

微信小程序设置本地背景图片在真机无法显示的解决方案 在开发微信小程序时&#xff0c;很多开发者会遇到一个常见的问题&#xff1a;在调试环境中设置本地背景图片可以正常显示&#xff0c;但在真机上却无法显示。本文将详细探讨这一问题的原因&#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…

接口自动化测试如何灵活地验证动态变化的response返回数据

嗨&#xff0c;我是兰若&#xff0c;很多小伙伴在针对动态返回的response&#xff0c;不知道怎么断言&#xff0c;今天教给大家几种方式&#xff0c;可以在接口自动化测试时确保测试的有效性和灵活性&#xff1a; 1. 断言静态字段 首先&#xff0c;您可以断言一些静态字段的值…

JS中map()使用记录

优点和缺点 总的来说&#xff0c;map() 方法是一个强大的工具&#xff0c;适合于需要将数组中的每个元素转换为新形式的情况。然而&#xff0c;对于性能敏感的应用或需要更复杂控制逻辑的场景&#xff0c;可能需要考虑其他方法。 优点&#xff1a; 函数式编程风格&#xff1a…

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…

【C语言】用队列实现栈

用两个队列&#xff08;先进先出&#xff09;实现一个栈&#xff08;后进先出&#xff09; 题目链接&#xff1a;https://leetcode.cn/problems/implement-stack-using-queues/description/ 1.C语言首先要造一个队列出来 2.两个队列实现栈&#xff0c;始终保持一个队列为空&…

无人机之环保监控篇

随着科技的不断进步&#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 之后没有代码了所以运行为队…

vue中无法调试

vue.config.js中增加 devtool configureWebpack: {name: name,resolve: {alias: {: resolve(src)}},devtool: "cheap-module-source-map" // add},然后重启即可。 顺便招聘&#xff1a;1.需要会日语。2.Java&#xff0c;JS&#xff0c;Vue&#xff0c;DB任一会者皆…