基于cornerstone3D的dicom影像浏览器 第三十一章 从PACS服务加载图像

文章目录

  • 前言
  • 一、两个服务接口
    • 1. 查询检查接口
    • 2. 查询图像接口
  • 二、查询界面组件
  • 三、修改归档
  • 总结


前言

"基于cornerstone3D的dicom影像浏览器"系列文章中都是加载本地文件夹的的dicom图像。
作为一个合格的dicom影像浏览器需要对接PACS服务端,从PACS服务查询检查,下载图像。
本章实现一个查询界面,对接PACS服务。
效果如下 :
在这里插入图片描述


一、两个服务接口

PACS服务需要提供两个接口

  1. 查询检查接口
  2. 查询图像接口

1. 查询检查接口

接口名称可自定义,本文中的接口名称:“http://localhost:9000/queryStudy”
查询参数可自定义,本文中的接口参数有:

startDate    // 检查开始日期
endDate      // 检查结束日期
studyId      // 检查号
modality     // 设备类型
name         // 姓名

示例:
http://localhost:9000/queryStudy?startDate=2000-06-09&endDate=2025-06-09&studyId=2&modality=CT

返回数据示例:

[{"No": "1","StudyId": "2","PatName": "程**","Sex": "女","Age": "43Y","ImageNum": "1","MzId": "","Modality": "CT","RadiId": "CT0000000001234567","StudyDate": "2020-08-25 10:03:33","ExamItem": "胸部X线计算机体层(CT)平扫","ExamPart": ""},{"No": "2","StudyId": "4","PatName": "赢**","Sex": "女","Age": "63Y","ImageNum": "1","MzId": "","Modality": "CT","RadiId": "CT0000000002","StudyDate": "2020-08-25 10:17:01","ExamItem": "颅脑X线计算机体层(CT)平扫","ExamPart": ""},{"No": "3","StudyId": "13","PatName": "余*","Sex": "女","Age": "37Y","ImageNum": "1","MzId": "","Modality": "CT","RadiId": "CT0000000003","StudyDate": "2022-11-14 16:41:29","ExamItem": "DR胸椎正位 *(1)","ExamPart": ""}...
]

2. 查询图像接口

接口名称可自定义,本文中的接口名称:“http://localhost:9000/queryImage”
参数:

studyId   // 检查号

示例:http://localhost:9000/queryImage?studyId=2

返回数据示例:

["http://localhost:9000/pacs/CT/2020-08-25/2/c8353e44b039eca9b334e78b741f3b35/1.dcm","http://localhost:9000/pacs/CT/2020-08-25/2/c8353e44b039eca9b334e78b741f3b35/2.dcm","http://localhost:9000/pacs/CT/2020-08-25/2/c8353e44b039eca9b334e78b741f3b35/3.dcm","http://localhost:9000/pacs/CT/2020-08-25/2/c8353e44b039eca9b334e78b741f3b35/4.dcm",...
]

二、查询界面组件

onSearch 查询检查,显示到列表
onRowClick 查询一次检查所有图像,并归档

<template><div class="pacs"><el-dialog:title="查询检查"draggablev-model="visible":modal="false":close-on-click-modal="false"width="840px"@keydown.native.stop><div class="querycond"><el-form :model="form" label-width="100px" @submit.prevent><el-row :gutter="0"><el-col :span="12"><el-form-item :label="检查号:"><el-input v-model="form.idNumber"></el-input></el-form-item></el-col><el-col :span="7"><el-form-item :label="姓名:"><el-input v-model="form.name"></el-input></el-form-item></el-col><el-col :span="5"><el-form-item class="right-align" label-width="30px"><el-button type="primary" @click="onSearch"><el-icon><Search /></el-icon><span style="vertical-align: middle">查询</span></el-button></el-form-item></el-col></el-row><el-row :gutter="0"><el-col :span="12"><el-form-item :label="检查日期:"><el-date-pickerv-model="form.queryDate"type="daterange"align="right"unlink-panels:range-separator="至":shortcuts="shortcuts"></el-date-picker></el-form-item></el-col><el-col :span="7"><el-form-item :label="检查类型:"><el-select v-model="form.modality" placeholder=""><el-optionv-for="mod in modalities":key="mod":label="mod":value="mod"></el-option></el-select></el-form-item></el-col><el-col :span="5"><el-form-item class="right-align" label-width="30px"><el-button @click="onClear"><el-icon><Delete /></el-icon><span style="vertical-align: middle">清空</span></el-button></el-form-item></el-col></el-row></el-form></div><el-table :data="studyData" border height="420px" @row-dblclick="onRowClick"><el-table-columnproperty="StudyId":label="检查号"width="100":show-overflow-tooltip="true"></el-table-column><el-table-columnproperty="PatName":label="姓名"width="100":show-overflow-tooltip="true"></el-table-column><el-table-columnproperty="Sex":label="性别"width="80"></el-table-column><el-table-columnproperty="Age":label="年龄"width="80"></el-table-column><el-table-columnproperty="StudyDate":label="检查日期"width="168":show-overflow-tooltip="true"></el-table-column><el-table-columnproperty="Modality":label="设备"width="90"></el-table-column><el-table-columnproperty="ExamItem":label="检查项目"width="220":show-overflow-tooltip="true"></el-table-column></el-table></el-dialog></div>
</template><script lang="js" setup name="SearchPACS">
import { ref, reactive, onMounted } from "vue";
import { desensitizeSubstring } from "@/utils";
import { useArchiveStore } from "../stores/archive";
import { ElMessage } from "element-plus";const archiveStore = useArchiveStore();
const studyData = ref([]);
const visible = ref(false);
const modalities = ["ALL", "CR", "DX", "MG", "CT", "MR", "RF", "OT", "XA"];
const form = reactive({idNumber: "",name: "",modality: "ALL",queryDate: [new Date(), new Date()],
});const shortcuts: [{text: "今天",value: () => {const end = new Date();const start = new Date();return [start, end];},},{text: "昨天",value: () => {const end = new Date();const start = new Date();start.setTime(start.getTime() - 3600 * 1000 * 24);return [start, end];},},{text: "最近三天",value: () => {const end = new Date();const start = new Date();start.setTime(start.getTime() - 3600 * 1000 * 24 * 3);return [start, end];},},{text: "最近一周",value: () => {const end = new Date();const start = new Date();start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);return [start, end];},},{text: "最近一个月",value: () => {const end = new Date();const start = new Date();start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);return [start, end];},},{text: "最近一年",value: () => {const end = new Date();const start = new Date();start.setTime(start.getTime() - 3600 * 1000 * 24 * 365);return [start, end];},},
];const show = () => {visible.value = true;
};const onSearch = () => {// 查询检查服务接口,实际可存储到配置中const studyapi = "http://localhost:9000/queryStudy";let url = studyapi;const sdate = form.queryDate[0];const edate = form.queryDate[1];url += "?startDate=" + sdate + "&endDate=" + edate;if (form.modality !== "ALL") {url += "&modality=" + form.modality;}if (form.idNumber) {url += "&id=" + form.idNumber;}if (form.name) {url += "&name=" + form.name;}//console.log(url);fetch(url).then((response) => response.json()).then((data) => {// console.log(data);studyData.value = data.map((item) => {return {...item, PatName: desensitizeSubstring(item.PatName, 1, -1) }});// console.log(studyData.value);}).catch((error) => {console.log(error);ElMessage.error("查询失败");});};const onClear = () => {form.idNumber = "";form.name = "";form.modality = "ALL";form.queryDate = [new Date(), new Date()];studyData.value = [];
};const onRowClick = (row) => {// 查询图像服务接口,实际可存储到配置中const imageapi = "http://localhost:9000/queryImage";let url;let params = "studyId=" + row.StudyId;url = imageapi + "?" + params;// console.log(url);ElMessage.success("正在获取图像, 请稍候...");visible.value = false;fetch(url).then((response) => response.json()).then((data) => {// console.log(data);const imageIds = data.map((item) => "dicomweb:" + item);//console.log("imageIds: ", imageIds);imageIds.forEach((imageId) => {archiveStore.archiveFile(imageId);});}).catch((error) => {console.log(error);ElMessage.error("查询失败");});
};defineExpose({show,
});</script><style lang="scss" scoped>
:deep(.el-dialog) {padding: 1px;border: 1px solid gray;border-radius: 4px;
}
:deep(.el-dialog__header) {background-color: #eee;height: 40px;padding: 8px 8px;border: none;
}:deep(.el-dialog__title) {color: #444;
}:deep(.el-table__body-wrapper) {background-color: white;
}:deep(.el-table__header .is-leaf) {background-color: white;color: #444;
}:deep(.el-table__cell) {background-color: white;color: #444;padding: 4px 0px;
}:deep(.el-table--enable-row-hover .el-table__body tr:hover > td) {background-color: #d3e3fd;
}:deep(.el-table--border .el-table__cell:first-child .cell) {padding-left: 4px;
}.querycond {width: 100%;height: 100px;padding: 8px 0;background-color: white;
}:deep(.el-form-item) {margin-bottom: 4px;
}:deep(.el-form-item__label) {color: #444;
}:deep(.el-form-item__content .el-input .el-input__inner) {height: 30px;line-height: 30px;padding: 0px 8px;
}:deep(.el-button) {height: 32px;width: 100px;line-height: 32px;padding: 0;
}:deep(.el-range-editor.el-input__inner) {height: 32px;padding: 0 10px;
}
:deep(.el-date-editor--daterange) {width: 320px;
}:deep(.el-date-editor .el-range-separator) {padding: 0;width: 8%;
}
</style>

三、修改归档

之前只对本地文件归档,现增加对网络文件归档的兼容
归档逻辑请查看第四章 加载本地文件夹中的dicom文件并归档

修改archiveStore.js文件中的archiveFile函数。
修改前:

async function archiveFile(file) {const imageId = cornerstoneDICOMImageLoader.wadouri.fileManager.add(file);const dcmImage = new DCMImage({ imageId });await dcmImage.parse();...
}

修改后:

async function archiveFile(file) {let imageId = "";if (typeof file === "string") {imageId = file;} else {imageId = cornerstoneDICOMImageLoader.wadouri.fileManager.add(file);}const dcmImage = new DCMImage({ imageId });await dcmImage.parse();...
}

总结

本章实现从PACS服务器加载dicom图像。
说明了对接PACS服务所需的两个接口的定义和返回数据格式。

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

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

相关文章

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…

基于React Native开发HarmonyOS 5.0医疗类应用

随着HarmonyOS 5.0的发布和React Native技术的成熟&#xff0c;开发者现在可以利用React Native框架为HarmonyOS平台构建高性能的跨平台医疗应用。 一、技术选型与优势 1.React Native HarmonyOS的组合优势 &#xff08;1&#xff09;跨平台能力​​&#xff1a;React Nati…

姜伟生《统计至简》

姜伟生《统计至简》 系列丛书之一 这套书图真漂亮&#xff0c;字间距也大&#xff0c;特别合适直接作为课件。但是理论上弱&#xff0c;有的地方算法也get不点上。适合初学者&#xff0c;因为能看图说话&#xff1b;又不适合初学者&#xff0c;因为没有解析、没有分析。 这学…

滚动—横向滚动时,如何直接滚动到对应的内容板块

使用scrollIntoView方法方法解读 scrollIntoView 是 HTML 元素&#xff08;HTMLElement&#xff09;的一个方法。当调用该方法时&#xff0c;它会尝试将调用它的元素滚动到浏览器的可视区域内。这个方法特别适用于处理页面上的滚动行为&#xff0c;比如让用户能够快速定位到页面…

HTML5 定位网页元素

1. 定位&#xff08;position&#xff09; position&#xff1a;static&#xff08;标准&#xff09; position&#xff1a;relative&#xff08;相对定位&#xff09; 偏移量的方向 相对定位的规律 浮动元素设置相对定位 position&#xff1a;absolute&#xff08;绝对…

分类数据集 - 植物分类数据集下载

数据集介绍&#xff1a;植物分类数据集&#xff0c;真实场景高质量图片数据&#xff1b;适用实际项目应用&#xff1a;自然场景植物分类项目&#xff0c;以及作为通用分类数据集场景数据的补充&#xff1b;数据集类别&#xff1a;标注说明&#xff1a;采用文件夹来区分不同的目…

​React Hooks 的闭包陷阱问题

这是主包在面试中遇到的一道题目&#xff0c;面试官的问题是&#xff1a;"这个页面初次展示出来时Count和step的值是什么&#xff0c;我点击按钮count和step的值有什么变化&#xff1f;“ 这个题目主包回答的不好&#xff0c;所以想做一个总结。 题目 import React, { …

新基建浪潮下:中国新能源汽车充电桩智慧化建设与管理实践

在新基建战略的强力推动下&#xff0c;中国新能源汽车充电桩建设正迎来智慧化升级的重要机遇期。作为连接能源革命与交通革命的关键节点&#xff0c;充电基础设施的智能化转型不仅关乎新能源汽车产业的可持续发展&#xff0c;更是构建新型电力系统的重要支撑。当前&#xff0c;…

如何在多任务环境中设定清晰的项目优先级?

在多任务环境中设定清晰的项目优先级需要明确项目战略价值、紧急性、资源利用效率、风险管理。其中&#xff0c;明确项目战略价值尤为重要&#xff0c;它决定了项目对组织整体战略目标实现的贡献程度。例如&#xff0c;战略价值高的项目&#xff0c;即使不紧急&#xff0c;也应…

【Django】性能优化-普通版

性能优化&#xff1a; 思路 通常无论是什么编程语言或者是什么框架&#xff0c;瓶颈通常都是数据库相关的操作&#xff1b; 大部分的查询慢的问题接口都是频繁查库、全盘扫描、多层for循环嵌套、高频查redis、序列化时多级外键&#xff1b; 多用O(1)查找复杂度的数据 合理使…

数据治理域——离线数据开发

摘要 文本主要介绍了离线数据开发相关内容,包括业务与流程、阿里MaxCompute系统设计以及阿里调度系统设计。离线数据开发是大数据开发核心组成部分,用于处理批量数据,支持企业多种需求,其流程涵盖需求调研、数据源接入等环节。阿里MaxCompute系统架构与特点被阐述,调度系…

python-docx 库教程

Python-docx 库介绍 官网文档 python-docx 是一个用于创建和修改 Microsoft Word (.docx) 文件的 Python 库。它允许你通过编程方式生成格式化的文档&#xff0c;添加文本、段落、表格、图片等元素&#xff0c;而无需依赖 Microsoft Word 应用程序。 主要功能 创建新的 Word…

Ansible小试牛刀

注意事项 除了安装的zabbix相关组件 使用此脚本安装的所有软件版本均为系统默认版本 安装软件 zabbix相关组件&#xff0c;包括server&#xff0c;agent等 MySQL Redis NGINX openjdk maven nodejs keepalived iptables ipvsadm 使用剧本 --- - hosts: allname…

MCP使用

什么是MCP Model Context Protocol (MCP) 是由 Anthropic 公司于 2024 年 11 月推出的一种开放协议标准&#xff0c;目的在于标准化LLM 与外部数据源、工具及服务之间的交互方式。MCP 被广泛类比为“AI 领域的 USB-C 接口” MCP与Function Calling的区别 MCP 的核心概念 1.…

边缘计算一:现代前端架构演进图谱 —— 从 SPA 到边缘渲染

过去十年&#xff0c;前端项目架构经历了从简单 HTML 文件到复杂框架的飞跃&#xff0c;但很多开发者忽略了**“渲染位置”与“资源交付方式”**对体验与性能的根本性影响。 从最初的浏览器渲染&#xff0c;到现在“在离用户最近的地方动态返回 HTML”&#xff0c;架构正在悄悄…

linux学习记录(六)三个常用命令介绍

1.vim命令 Vim是由Vi发展过来的文本编译器&#xff0c;其代码补全、编译及错误跳转等方便编程的功能特别丰富&#xff0c;在程序员中被广泛使用。 1.1 语法 vim filename 1.2 vi/vim的使用 vi/vim 共分为三种模式&#xff0c;命令模式&#xff08;Command Mode&#xff09;、…

用Python获取京东关键字接口的用户指南

在电商数据分析和市场研究中&#xff0c;获取京东平台的关键字搜索结果数据具有重要意义。本文将详细介绍如何使用Python调用京东开放平台的API接口&#xff0c;获取关键字相关的商品数据&#xff0c;并进行解析和处理。 一、准备工作 &#xff08;一&#xff09;注册京东开发…

观测云,全球领先的监控观测平台亮相亚马逊云科技中国峰会!

观测云每年都不会缺席亚马逊云科技峰会 忙完一整季的产品发布&#xff0c;我们终于将目光投向这场全球顶尖的云技术盛会——2025亚马逊云科技中国峰会。如果你也在这个领域&#xff0c;应该已经感觉到了&#xff1a;这不只是一场大会&#xff0c;而是一个信号。AI、可观测性、…

消息队列处理模式:流式与批处理的艺术

&#x1f30a; 消息队列处理模式&#xff1a;流式与批处理的艺术 &#x1f4cc; 深入解析现代分布式系统中的数据处理范式 一、流式处理&#xff1a;实时数据的"活水" 在大数据时代&#xff0c;流式处理已成为实时分析的核心技术。它将数据视为无限的流&#xff0c;…

一起学习swin-transformer(一)

Transform学习链接 从零开始设计Transformer模型&#xff08;1/2&#xff09;——剥离RNN&#xff0c;保留Attention-CSDN博客 Transformer-PyTorch实战项目——文本分类_transformer文本分类 pytorch-CSDN博客 从零开始设计Transformer模型&#xff08;2/2&#xff09;——…