基于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.xdnf.cn/news/1488378.html

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

相关文章

EtherNet/IP转Profinet协议网关(经典配置案例)

怎么样才能把EtherNet/IP和Profinet网络连接起来呢?这几天有几个朋友问到了这个问题&#xff0c;作者在这里统一为大家详细说明一下。其实有一个设备可以很轻松地解决这个问题&#xff0c;名为JM-PN-EIP&#xff0c;下面是详细介绍。 一&#xff0c;设备主要功能 1、捷米特J…

霍尔传感器介绍

霍尔传感器概述 霍尔传感器是一种基于霍尔效应原理的传感器&#xff0c;‌广泛应用于各种电子和工业领域。‌ 霍尔传感器的工作原理基于霍尔效应&#xff0c;‌即当电流通过一个位于磁场中的导体时&#xff0c;‌在导体两侧会产生电势差。‌这种效应由美国物理学家爱德华霍尔…

“真互动”线上艺术空间,为艺术展览注入新活力!

在数字技术蓬勃发展的浪潮中&#xff0c;线上艺术展览作为艺术界的一股新兴力量&#xff0c;正以其独特的魅力重塑展览形态。相较于传统线下艺术展的复杂筹备与地域限制&#xff0c;线上艺术展凭借其高效的时间空间利用、无界限的沉浸式探索&#xff0c;赋予观众前所未有的艺术…

shell-awk文本处理工具

1、awk概述 AWK 是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具。 它是专门为文本处理设计的编程语言&#xff0c;也是行处理软件&#xff0c;通常用于扫描、过滤、统计汇总工作 数据可以来自标准输入也可以是管道或文件 在 linux 上常用的是 gawk,awk …

UE4调试UE4Editor-Cmd.exe

在工作中&#xff0c;我们看到这样的构建命令&#xff1a; %EnginePath%\Binaries\Win64\UE4Editor-Cmd.exe %ClientPath%\%ProjectName%.uproject -runHotPatcher {其它参数} 我们应该如何调试UE4Editor-Cmd.exe呢&#xff1f;其实调试 UE4Editor.exe 就可以了&#xff08;参考…

全球模块化机器人市场展望与未来增长机遇预测:未来六年CAGR为14.9%

在全球自动化和智能化水平提升的背景下&#xff0c;模块化机器人正成为市场的焦点。本文详细分析了全球模块化机器人市场的现状、增长趋势及未来前景&#xff0c;旨在为投资者和业内人士提供深入的市场洞察和指导。 市场概览 据恒州诚思团队研究分析显示&#xff0c;2023年&am…

Ubuntu上编译多个版本的frida

准备工作 Ubuntu20(WSL) 略 安装依赖 sudo apt update sudo apt-get install build-essential git lib32stdc-9-dev libc6-dev-i386 -y nodejs 去官网[1]下载nodejs&#xff0c;版本的话我就选的20.15.1&#xff1a; tar -xf node-v20.15.1-linux-x64.tar.xz 下载源码 …

Hyperledger Fabric 网络体验 - 网络启动过程概览

进入fabric-samples/test-network目录&#xff0c;执行指令&#xff1a; ./network.sh up -i 2.5执行完指令能看到fabric已经启动。 作为第一次Fabric网络体验&#xff0c;网络启动主要包含三个操作&#xff0c;分别是生成配置文件、启动网络和操作网络。 配置文件 使用cr…

AutoMySQLBackup execution.. Backup failed Docker部署mysql 自动备份失败!!

摘要&#xff1a; Docker容器部署的mysql5.7版本遇到使用AutoMYSQLBackup备份失败了&#xff0c;反复修改automysqlbackup.conf也不起效。这里推荐一种新的办法绕开老路子直接备份。 目录 一、环境介绍 二、AutoMYSQLBackup 三、问题描述 四、解决思路 4.1第一种解决思…

论文阅读:面向自动驾驶场景的多目标点云检测算法

论文地址:面向自动驾驶场景的多目标点云检测算法 概要 点云在自动驾驶系统中的三维目标检测是关键技术之一。目前主流的基于体素的无锚框检测算法通常采用复杂的二阶段修正模块,虽然在算法性能上有所提升,但往往伴随着较大的延迟。单阶段无锚框点云检测算法简化了检测流程,…

C# Nmodbus,EasyModbusTCP读写操作

Nmodbus读写 两个Button控件分别为 读取和写入 分别使用控件的点击方法 ①引用第三方《NModbus4》2.1.0版本 全局 public SerialPort port new SerialPort("COM2", 9600, Parity.None, 8, (StopBits)1); ModbusSerialMaster master; public Form1() port.Open();…

分布式搜索引擎ES--Elasticsearch集群

1.Elasticsearch集群的概念 分片机制&#xff1a;每个索引都可以被分片 索引my_doc只有一个主分片&#xff1b;索引shop有三个主分片&#xff1b;索引shop2有5个主分片;(参考前面案例) 每个主分片都包含索引的数据&#xff0c;由于目前是单机&#xff0c;所以副分片是没有的&a…

面试前端实习常问的关于【ES6新特性】的问题

ES6新特性 日常前端代码开发中&#xff0c;有哪些值得用 ES6 去改进的编程优化或者规范? 常用箭头函数来取代有this指向的函数常用 let 取代 var 命令常用数组/对象的结构赋值来命名变量&#xff08;结构更清晰&#xff0c;语义更明确&#xff0c;可读性更好&#xff09;在长字…

【ARM】MDK-ARM软件开发工具的最终用户许可协议获取

【更多软件使用问题请点击亿道电子官方网站】 1、 文档目标 了解MDK-ARM系列产品内软件开发工具的最终用户许可协议的获取。 2、 问题场景 对于部分外企客户需要软件开发工具的最终用户许可协议作为产品资料&#xff0c;以便附录并说明。 3、软硬件环境 1&#xff09;、软件…

mysql语法介绍

MySQL 语法主要基于 SQL&#xff08;Structured Query Language&#xff09;标准&#xff0c;用于管理和操作关系型数据库。以下是一些基本的 MySQL 语句&#xff1a; 1.创建数据库&#xff1a; CREATE DATABASE database_name; 1.选择数据库&#xff1a; USE database_name;…

极速下载!青苹果系统Win7旗舰版:稳定安全!

今天系统之家小编给大家带来了青苹果系统Win7旗舰版镜像&#xff0c;该版本系统离线制作而成&#xff0c;安全无病毒&#xff0c;还升级了防火墙功能&#xff0c;大家安装后时刻都能放心操作。同时&#xff0c;它是在保持原有的功能并适度优化&#xff0c;保留下来的功能轻松满…

算法 —— 暴力枚举

目录 循环枚举 P2241 统计方形&#xff08;数据加强版&#xff09; P2089 烤鸡 P1618 三连击&#xff08;升级版&#xff09; 子集枚举 P1036 [NOIP2002 普及组] 选数 P1157 组合的输出 排列枚举 P1706 全排列问题 P1088 [NOIP2004 普及组] 火星人 循环枚举 顾名思…

如何学习Presto:糙快猛的大数据之路(建立整体框架)

这个系列文章用"粗快猛大模型问答讲故事"的创新学习方法&#xff0c;让你轻松理解复杂知识&#xff01;涵盖Hadoop、Spark、MySQL、Flink等大数据所有热门技术栈&#xff0c;每篇万字长文。时间紧&#xff1f;只看开头20%就能有收获&#xff01;精彩内容太多&#xf…

重拾CSS,前端样式精读-函数(颜色,计算,图像和图形)

前言 本文收录于CSS系列文章中&#xff0c;欢迎阅读指正 在计算机编程中&#xff0c;函数有着重要的作用和意义&#xff0c;它可以实现封装&#xff0c;复用&#xff0c;模块化&#xff0c;参数等功能效果&#xff0c;在如何在CSS中写变量&#xff1f;一文带你了解前端样式利…

【网络安全学习】 SQL注入01:基础知识

&#x1f4bb; 1. 什么是SQL注入 SQL注入是一种针对Web程序中数据库层的安全漏洞的攻击方式。它利用了程序对用户输入数据合法性的判断或过滤不严&#xff0c;允许攻击者在设计不良的程序中添加额外的SQL语句&#xff0c;从而执行计划外的命令或访问未授权的数据。攻击者可以通…