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

news2024/9/8 20:26:31

文章目录

  • 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<>();

    @JsonAnySetter
    public void set(String key, Object value) {
        properties.put(key, value);
    }

    // Getter and Setter for properties
    public 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.coloradmin.cn/o/1958939.html

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

相关文章

Embeddings 赋能 - AI智能匹配,呈现精准内容

&#x1f680;前言 在当今的 AI 时代,传统的相关内容推荐和搜索功能已经显得相对简单和低效。借助 AI 技术,我们可以实现更加智能化和个性化的内容发现体验。 本文将为大家介绍如何利用 OpenAI 的 Embedding 技术,打造出智能、高效的相关内容推荐和搜索功能。 &#x1f680;…

UCOS-III 互斥锁接口详解

在实时操作系统uC/OS-III中&#xff0c;互斥锁&#xff08;Mutex&#xff09;是一种用于管理对共享资源的访问的同步机制。互斥锁通过保证在任何时刻只有一个任务可以持有锁&#xff0c;从而防止资源竞争问题。同时&#xff0c;uC/OS-III还实现了递归锁定和优先级继承机制&…

七款公司常用的加密软件推荐|2024年公司办公加密软件推荐

在现代企业中&#xff0c;加密软件是保护敏感信息、防止数据泄露和确保通信安全的关键工具。加密软件能够对数据进行加密&#xff0c;使其在未经授权的情况下无法被读取或篡改&#xff0c;本文分享七款加密软件&#xff0c;它们各具特色&#xff0c;能够满足不同的安全需求。 1…

狂赚又吸金 身心灵赛道AI玩法全解析

想必很多初入AI的小白们&#xff0c;小白的不能在小白了&#xff0c;因为在他们眼中&#xff0c;确实对AI一无所知。 基于他们平时刷抖音、刷视频号的习惯&#xff0c;有的时候会发一些传统剪辑的作品&#xff0c;问AI怎么做&#xff1f;很多人认为AI所见的视频&#xff0c;AI…

GeoServer发布MongoDB中的shp数据全流程梳理

目录 前言1.shp转geojson2.shp导入MongoDB3.创建空间索引4.GeoServer安装MongoDB插件5.发布6.注意事项6.1 geojson要去掉头尾6.2 MongoDB4.4以上的mongoimport工具需要额外安装6.3 空间索引是必须项 7.总结 前言 网上搜到的GeoServer发布MongoDB中的矢量数据或shp数据的文章比较…

http协议与nginx

动态页面与静态页面的差别&#xff1a; &#xff08;1&#xff09;URL不同 静态⻚⾯链接⾥没有“?” 动态⻚⾯链接⾥包含“&#xff1f;” &#xff08;2&#xff09;后缀不同 (开发语⾔不同) 静态⻚⾯⼀般以 .html .htm .xml 为后缀 动态⻚⾯⼀般以 .php .jsp .py等为后…

我国工业大模型发展中的四个反差现象

以大模型为代表的新一代人工智能技术正加速推进新型工业化的变革进程。2024年1月&#xff0c;国务院常务会议研究部署推动人工智能赋能新型工业化有关工作&#xff0c;强调以人工智能和制造业深度融合为主线&#xff0c;以智能制造为主攻方向&#xff0c;以场景应用为牵引&…

【Git从入门到精通】——知识概述及Git安装

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大二学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

Google Test的使用

Google Test支持的操作系统包含下面这些&#xff1a; 1、Linux 2、Mac OS X 3、Windows 4、Cygwin 5、MinGW 6、Windows Mobile 7、Symbian一、google test的基本使用步骤 1、包含gtest/gtest.h头文件 2、使用TEST()宏定义测试case 3、在测试体中使用gooletest断言进行值检查…

Java从入门到精通(十四) ~ 多线程

晚上好&#xff0c;愿这深深的夜色给你带来安宁&#xff0c;让温馨的夜晚抚平你一天的疲惫&#xff0c;美好的梦想在这个寂静的夜晚悄悄成长。 目录 前言 一、多线程是什么&#xff1f; Java中的多线程 二、使用步骤 1.创建方式 1.1 Thread 线程 1.2 Runnable 任务 1.…

视创云展:重塑线上会议体验,六大核心引领数字空间新纪元

视创云展以其革命性的“数字活动”解决方案为核心&#xff0c;精心构建了一个超越想象的未来数字世界。通过整合六大前沿技术模块&#xff0c;它不仅为参会者打造了一个身临其境的线上会议环境&#xff0c;更让每一位参与者都能跨越物理界限&#xff0c;深刻感受会议的每一个瞬…

2024经济师考试报名『注册流程』图解!

⏰报名时间&#xff1a;8月12日—9月11日 ☑️报名注册流程 1、经济师考试报名注册网站&#xff1a;中国人事考试网. 2、点击考生登录栏目中的【新用户注册】按钮&#xff0c;进行注册。 3、进入用户注册界面&#xff0c;填写注册信息。 4、填写完毕确认无误后点击【提交】&…

CMS61850客户端

近一年都比较忙&#xff0c;很久没有更新文档了。这次抽了点时间&#xff0c;把CMS61850的客户端再补上。方便大家进一步的学习。当然61850相关的文档已经写的快吐了。不出意外&#xff0c;这应该是最后一篇关于61850的文章了。 本次提供的demo&#xff0c;可直接在windows上运…

【React】WeChat微信网站应用登录之wxLogin.js

以下基于wxLogin.js在React应用中使用案例 实验环境 antd&#xff1a;^5.14.1next&#xff1a;14.1.0react&#xff1a;^18 组件调用示例 "use client";import { useEffect, useState } from "react"; import { WechatChannel } from "./channel&qu…

【论文阅读笔记】DeepCAD: A Deep Generative Network for Computer-Aided Design Models

1 引言 现有3D生成模型&#xff1a; 3D点云&#xff1a;大量离散的3D点组成的数据表示形式&#xff1b; 多边形网格&#xff1a;一系列相连的多边形组成的3D模型&#xff1b; 水平集场&#xff1a;使用数值函数来表示物体的边界&#xff0c;并根据函数值的正负来确定物体内部…

示波器显示屏5个名词解释(峰峰值、平均值、频率、占空率、上升时间)

一般在使用示波器时&#xff0c;需要找到示波器测量的一些数据&#xff0c;包括峰峰值、频率等&#xff0c;但所显示出的所有名词可能不是很能理解&#xff0c;小编今天就具体介绍一下。下图是一般我们调节过后的示波器显示屏的数据显示&#xff1a; 1、峰-峰值 &#xff08;Pe…

Clickhouse 生产集群部署(Centos 环境)

文章目录 机器环境配置安装 JDK 8安装 zookeeperClickhouse 集群安装rpm 包离线安装修改全局配置zookeeper配置Shard和Replica设置image.png添加macros配置启动 clickhouse启动 10.82.46.135 clickhouse server启动 10.82.46.163 clickhouse server启动 10.82.46.218 clickhous…

Python中的类型注解和静态类型检查使用详解

概要 Python作为一种动态类型语言,其灵活性和易用性使其广受欢迎。然而,动态类型也带来了一些问题,如代码可读性差和运行时错误等。为了提高代码质量和可维护性,Python从3.5版本开始引入了类型注解(Type Hints),并且借助第三方工具可以实现静态类型检查。本文将详细介绍…

熊猫乳品再创新高:超高温灭菌稀奶油,驱动餐饮品质升级

随着餐饮行业的蓬勃发展&#xff0c;乳制品在餐饮端的应用日益广泛且需求多样化。中华老字号品牌企业熊猫乳品&#xff0c;在近期推出了专为餐饮定制的高品质产品-熊猫乳品超高温灭菌稀奶油&#xff0c;凭借其卓越性能&#xff0c;在多项测试中脱颖而出&#xff0c;成为餐饮制作…

Docker入门指南:基础命令、操作容器与镜像管理,附存储、网络及Compose应用

Docker命令 下载镜像 命令命令示例docker search 镜像名检索镜像docker search nginxdocker pull 镜像名下载镜像docker pull nginxdocker images / docker image ls查看镜像列表/docker rmi 镜像名/IMAGE ID删除指定镜像docker rmi nginx / docker rmi e784f4560448 镜像名&…