dcm4chee-tool-findscu的使用

记录Java对接pacs系统并进行查询

准备

我这里使用的是springboot+jdk1.8,并且已经在本地安装了一个dcm4chee-arc来模拟pacs系统。

开始

1、修改pom.xml添加dcm4che依赖

<!-- dcm4che start -->
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId>
</dependency>
<dependency><groupId>commons-cli</groupId><artifactId>commons-cli</artifactId><version>1.4</version>
</dependency>
<dependency><groupId>org.dcm4che.tool</groupId><artifactId>dcm4che-tool-common</artifactId><version>5.25.2</version>
</dependency>
<dependency><groupId>org.dcm4che</groupId><artifactId>dcm4che-imageio</artifactId><version>5.25.2</version>
</dependency>
<!--dcm end-->

2、新建FindSCU,参考链接: dcm4che官网与 长辞笙-初次使用dcm4che-tool-findscu做查询

import com.evo.common.BusinessException;
import com.evo.pojo.PacsData;
import org.apache.commons.cli.ParseException;
import org.apache.commons.lang3.StringUtils;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.data.VR;
import org.dcm4che3.net.*;
import org.dcm4che3.net.pdu.AAssociateRQ;
import org.dcm4che3.net.pdu.ExtendedNegotiation;
import org.dcm4che3.net.pdu.PresentationContext;
import org.dcm4che3.util.SafeClose;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;@Component
public class FindSCU {public static enum InformationModel {PatientRoot(UID.PatientRootQueryRetrieveInformationModelFind, "STUDY"),StudyRoot(UID.StudyRootQueryRetrieveInformationModelFind, "STUDY"),PatientStudyOnly(UID.PatientStudyOnlyQueryRetrieveInformationModelFind, "STUDY"),MWL(UID.ModalityWorklistInformationModelFind, null),UPSPull(UID.UnifiedProcedureStepPull, null),UPSWatch(UID.UnifiedProcedureStepWatch, null),UPSQuery(UID.UnifiedProcedureStepQuery, null),HangingProtocol(UID.HangingProtocolInformationModelFind, null),ColorPalette(UID.ColorPaletteQueryRetrieveInformationModelFind, null);final String cuid;final String level;InformationModel(String cuid, String level) {this.cuid = cuid;this.level = level;}public void adjustQueryOptions(EnumSet<QueryOption> queryOptions) {if (level == null) {queryOptions.add(QueryOption.RELATIONAL);queryOptions.add(QueryOption.DATETIME);}}}private static ResourceBundle rb = ResourceBundle.getBundle("static/message", Locale.getDefault());private static String[] IVR_LE_FIRST = new String[]{"1.2.840.10008.1.2", "1.2.840.10008.1.2.1","1.2.840.10008.1.2.2"};private final Device device = new Device("findscu");private final ApplicationEntity ae = new ApplicationEntity("FINDSCU");private final Connection conn = new Connection();private final Connection remote = new Connection();private final AAssociateRQ rq = new AAssociateRQ();private int priority;private int cancelAfter;private InformationModel model;private Attributes keys = new Attributes();private OutputStream out;private Association as;private FindSCU() {device.addConnection(conn);device.addApplicationEntity(ae);ae.addConnection(conn);}private void setPriority(int priority) {this.priority = priority;}private void setInformationModel(InformationModel model, String[] tss, EnumSet<QueryOption> queryOptions) {this.model = model;rq.addPresentationContext(new PresentationContext(1, model.cuid, tss));if (!queryOptions.isEmpty()) {model.adjustQueryOptions(queryOptions);rq.addExtendedNegotiation(new ExtendedNegotiation(model.cuid, QueryOption.toExtendedNegotiationInformation(queryOptions)));}if (model.level != null)addLevel(model.level);}private void addLevel(String s) {keys.setString(Tag.QueryRetrieveLevel, VR.CS, s);}private void setCancelAfter(int cancelAfter) {this.cancelAfter = cancelAfter;}private static EnumSet<QueryOption> queryOptionsOf() {EnumSet<QueryOption> queryOptions = EnumSet.noneOf(QueryOption.class);queryOptions.add(QueryOption.FUZZY);return queryOptions;}private static void configureCancel(FindSCU main) {if (StringUtils.isNotBlank(rb.getString("cancel"))) {main.setCancelAfter(Integer.parseInt(rb.getString("cancel")));}}private static void configureRetrieve(FindSCU main) {if (StringUtils.isNotBlank(rb.getString("level"))) {// Retrieve是指SCU通过Query 拿到信息后,要求对方根据请求级别 (Patient/Study/Series/Image) 发送影像给己方。// 默认Patientmain.addLevel(rb.getString("level"));}}/*** 设置Information Model** @param main* @throws ParseException*/private static void configureServiceClass(FindSCU main) throws ParseException {main.setInformationModel(informationModelOf(), IVR_LE_FIRST, queryOptionsOf());}private static InformationModel informationModelOf() throws ParseException {try {String model = rb.getString("model");// 如果model为空,默认StudyRootreturn StringUtils.isNotBlank(model) ? InformationModel.valueOf(model) : InformationModel.StudyRoot;} catch (IllegalArgumentException e) {throw new ParseException(MessageFormat.format(rb.getString("invalid-model-name"), rb.getString("model")));}}private static int priorityOf() {String high = rb.getString("prior-high");String low = rb.getString("prior-low");return StringUtils.isNotBlank(high) ? 1 : (StringUtils.isNotBlank(low) ? 2 : 0);}private void open()throws IOException, InterruptedException, IncompatibleConnectionException, GeneralSecurityException {as = ae.connect(conn, remote, rq);}private void close() throws IOException, InterruptedException {if (as != null && as.isReadyForDataTransfer()) {as.waitForOutstandingRSP();as.release();}SafeClose.close(out);out = null;}private void configureKeys(Attributes keys) {this.keys.addAll(keys);}private List<PacsData>  query() throws IOException, InterruptedException {return query(keys);}private List<PacsData> query(Attributes keys) throws IOException, InterruptedException {List<PacsData> pacsDataList = new ArrayList<>();DimseRSPHandler rspHandler = new DimseRSPHandler(as.nextMessageID()) {int cancelAfter = FindSCU.this.cancelAfter;int numMatches;@Overridepublic void onDimseRSP(Association as, Attributes cmd, Attributes data) {super.onDimseRSP(as, cmd, data);int status = cmd.getInt(Tag.Status, -1);if (Status.isPending(status)) {++numMatches;PacsData pacsData = FindSCU.this.printResult(data);pacsDataList.add(pacsData);if (cancelAfter != 0 && numMatches >= cancelAfter)try {cancel(as);cancelAfter = 0;} catch (IOException e) {e.printStackTrace();}}}};query(keys, rspHandler);return pacsDataList;}private void query(Attributes keys, DimseRSPHandler rspHandler) throws IOException, InterruptedException {as.cfind(model.cuid, priority, keys, null, rspHandler);//as.cget(model.cuid, priority, keys, null, rspHandler);}private PacsData printResult(Attributes data) {String SpecificCharacterSet = data.getString(Tag.SpecificCharacterSet);// 设置编码,防止乱码if (StringUtils.isBlank(SpecificCharacterSet)) {data.setString(Tag.SpecificCharacterSet, VR.CS, "GB18030");data.setString(Tag.SpecificCharacterSet, VR.PN, "GB18030");}PacsData pacsData = new PacsData();pacsData.setPatientId(data.getString(Tag.PatientID));pacsData.setPatientName(data.getString(Tag.PatientName));pacsData.setPatientBirthDate(data.getDate(Tag.PatientBirthDate));pacsData.setNumberOfStudyRelatedSeries(data.getString(Tag.NumberOfStudyRelatedSeries));pacsData.setPatientWeight(data.getString(Tag.PatientWeight));pacsData.setPatientAge(data.getString(Tag.PatientAge));pacsData.setPatientSex(data.getString(Tag.PatientSex));pacsData.setPregnancyStatus(data.getString(Tag.PregnancyStatus));pacsData.setInstitutionName(data.getString(Tag.InstitutionName));pacsData.setAccessionNumber(data.getString(Tag.AccessionNumber));pacsData.setStudyId(data.getString(Tag.StudyID));pacsData.setStudyInstanceUid(data.getString(Tag.StudyInstanceUID));pacsData.setStudyDate(data.getDate(Tag.StudyDate));pacsData.setModality(data.getString(Tag.Modality));pacsData.setModalitiesInStudy(data.getString(Tag.ModalitiesInStudy));pacsData.setStudyDescription(data.getString(Tag.StudyDescription));//pacsData.setBodyPartExamined(data.getString(Tag.BodyPartExamined));pacsData.setProtocolName(data.getString(Tag.ProtocolName));pacsData.setSeriesDescription(data.getString(Tag.SeriesDescription));pacsData.setSeriesInstanceUID(data.getString(Tag.SeriesInstanceUID));pacsData.setNumberOfSeriesRelatedInstances(data.getString(Tag.NumberOfSeriesRelatedInstances));pacsData.setNumberOfStudyRelatedInstances(data.getString(Tag.NumberOfStudyRelatedInstances));return pacsData;}/*** 配置远程连接** @param conn Connection* @param rq   AAssociateRQ*/private void configureConnect(Connection conn, AAssociateRQ rq) throws ParseException {// 获取title属性值//String title = "MYAET";//修改成你的String title = rb.getString("title");if (StringUtils.isBlank(title)) {throw new ParseException("title cannot be missing");}// 设置AE titlerq.setCalledAET(title);// 读取host和port属性值//String host = "192.168.3.86";//修改成你的String host = rb.getString("host");//String port = "11112";//修改成你的String port = rb.getString("port");if (StringUtils.isBlank(host) || StringUtils.isBlank(port)) {throw new ParseException("host or port cannot be missing");}// 设置host和porconn.setHostname(host);conn.setPort(Integer.parseInt(port));}public List<PacsData> matchingKeys(Attributes attrs) {try {FindSCU main = new FindSCU();configureConnect(main.remote, main.rq); // 设置连接ip和端口 (远程)main.remote.setTlsProtocols(main.conn.getTlsProtocols()); // 设置Tls协议main.remote.setTlsCipherSuites(main.conn.getTlsCipherSuites());configureServiceClass(main); // 设置Information ModelconfigureRetrieve(main); // 设置检索级别configureCancel(main); // 配置 --cancelmain.setPriority(priorityOf()); // 设置优先级ExecutorService executorService = Executors.newSingleThreadExecutor(); // 单线程化线程池ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); // 定时任务main.device.setExecutor(executorService);main.device.setScheduledExecutor(scheduledExecutorService);try {main.open(); // 打开链接main.configureKeys(attrs);List<PacsData> query = main.query();// 查询return query;} finally {main.close();executorService.shutdown();scheduledExecutorService.shutdown();}} catch (ParseException | InterruptedException | IncompatibleConnectionException | GeneralSecurityException| IOException e) {e.printStackTrace();throw new BusinessException(e.getMessage());}}}

message.properties

title=要连接的title
host=要连接的的ip
port=要连接的端口号
#specifies Information Model. Supported names: PatientRoot, StudyRoot, PatientStudyOnly,
#MWL, UPSPull, UPSWatch, UPSQuery, HangingProtocol or ColorPalette. If no Information Model is specified,
#StudyRoot will be used.
model=
invalid-model-name={0} is not a supported Information Model name
#specifies retrieve level. Use STUDY for PatientRoot, StudyRoot, PatientStudyOnly by default.
level=SERIES
#cancel the query request after the receive of the specified number of matches.
cancel=1
xml=write received matches as XML Infoset specified in DICOM Part 19
xsl=apply specified XSLT stylesheet to XML representation of received matches; implies -X
prior-high=
prior-low=

修改了官网的命令行式的使用方式而改为java常用方式,我这里的level声明为SERIES,可以查找到STUDY下一级的数据。
官网的查询没有返回值,我这边的因为要拿到所有数据,所以改变了返回值,如果你们只需要保存数据库或者可以直接操作可以不用这样做。

使用

 //设置dcm参数
Attributes attrs = new Attributes();// 查询条件 相当于命令 findscu -c DCM4CHEE@192.168.100.55:11112 -m ModalitiesInStudy=MRattrs.setString(Tag.ModalitiesInStudy, VR.CS, "PT", "CT");//attrs.setString(Tag.Modality, VR.CS);attrs.setString(Tag.NumberOfStudyRelatedSeries, VR.IS);attrs.setString(Tag.PatientID, VR.LO);...//想获取的参数要先声明attrs.set//调用findscuList<PacsData> pacsDataList = findSCU.matchingKeys(attrs);

这种拿到的数据可以根据自己的需求进行再封装。
3、结尾
这就是全部关于findscu的使用啦,但是我没有找到分页的位置在哪,我觉得在

if (cancelAfter != 0 && numMatches >= cancelAfter)try {cancel(as);cancelAfter = 0;} catch (IOException e) {e.printStackTrace();}```当查询到的数目大于你设置的cancelAfter就不在查询,但是设置完之后好像没生效,cancel(as)没能结束连接,不知道有没有知道的小伙伴可以告知。

Java 使用dcm4che的工具类findscu查询pacs数据相关推荐

  1. java redis remove_最全的Java操作Redis的工具类

    RedisUtil 当前版本:1.1 增加更全的方法,对以前的部分方法进行了规范命名,请放心替换成新版本. 介绍 最全的Java操作Redis的工具类,使用StringRedisTemplate实现, ...

  2. java Excel导入导出工具类 及使用demo

    java Excel导入导出工具类 及使用demo 前言:相信进来的都是想尽快解决问题的,话不多说,按照以下步骤来,可以操作导出excel到本地,导入同理,自行学习.步骤一:直接复制以下excel工具 ...

  3. java项目常用的工具类

    前言 在开发过程中,我们会遇到很多繁琐或者棘手的问题,但是,这些问题往往会存在一些便捷的工具类,来简化我们的开发,下面是我工作中经常使用到的工具类 常用工具类 日期工具类 import java.te ...

  4. Java 线程 - 基础及工具类 (二)

    Java 并发系列文章 Java 线程 - 并发理论基础(一) Java 线程 - 基础及工具类 (二) Java 线程 - 并发设计模式 (三) Java 线程(二) 通用的线程生命周期 Java ...

  5. java图片缩放工具类,一个JAVA图形缩放处置工具类

    一个JAVA图形缩放处理工具类 调用的例子 import java.io.FileOutputStream; import java.io.IOException; import javax.imag ...

  6. JAVA I/O流工具类TextFile

    JAVA I/O流工具类TextFile由广州疯狂软件java培训分享: 本文是一个TextFile类,通过这个类我们可以调用其中的方法来简化对文件的读写,这段代码的可用性比较强.这个TextFile ...

  7. java图形验证码生成工具类

    转载自   java图形验证码生成工具类 生成验证码效果       ValidateCode.java 验证码生成类 package cn.dsna.util.images; import java ...

  8. java 代理ip工具类_Java基础之java处理ip的工具类

    java处理ip的工具类,包括把long类型的Ip转为一般Ip类型.把xx.xx.xx.xx类型的转为long类型.根据掩码位获取掩码.根据 ip/掩码位 计算IP段的起始IP.根据 ip/掩码位 计 ...

  9. java练习(数组工具类的封装)[目前写过最长的代码,虽然不难]

    java练习(数组工具类的封装)[目前写过最长的代码,虽然不难] package myUtil.arrayUtil;public class ArraysUtil {/*** 升序排列一个byte数组 ...

最新文章

  1. linux下的软硬资源限制,关于ulimit命令修改软硬资源大小说明及正确修改软硬资源限制数配置...
  2. ASP.NET MVC实现一个用户只能登录一次 单用户登录
  3. c语言中的0UL或1UL是什么意思
  4. AI理论知识整理(15)-行列式
  5. 高人对libsvm的经典总结(全面至极)
  6. 用java智能锁远程,从生产者-消费者模型了解线程、同步、锁(java)
  7. docker下交叉编译环境配置
  8. CloudCC CRM:物联网必将成为CRM的推动力
  9. 服务器上传至云系统,上传至云服务器命令
  10. hdu 4974 贪心
  11. bfv同态加密_五分钟了解同态加密及三种常见方案
  12. Good Bye 2016 //智商再次下线,边界爆炸.....
  13. GDI和GUI的区别
  14. 负margin使用注意的一个问题
  15. 多屏互动电脑版_手机、电脑屏幕太小怎么办?这4招秒变大屏
  16. 【面试算法题】一维消除游戏
  17. golang单线程对比map与bigCache小对象存取性能差别
  18. ae合成设置快捷键_AE脚本使用快捷键控制关键帧操作 Keyboard v1.2.1 + 使用教程【资源分享1081】...
  19. 论文阅读:Seg4Reg+: Consistency Learning Between Spine Segmentation and Cobb Angle Regression
  20. 为什么电源层可以作为信号参考平面

热门文章

  1. 新冠疫情可视化(7月9日,7月10日)
  2. hdu6217 - BBP Formula
  3. 十三、Jmeter生成html报告
  4. 热释电探测器多用在_热敏电阻和热释电探测器分别适合探测哪种形式的辐射信号?...
  5. How to test Neutron VRRP HA rapidly (by quqi99)
  6. android商城首页布局,仿JD商城UI布局达到90%
  7. far manager引出的windows编码问题
  8. MODIS数据批量投影工具
  9. FPGA ISE PROMs下载程序问题
  10. 使用w3c生成xml文件