3d Slicer-DICOM模块之Query/Retrieve功能介绍

Posted inter_peng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3d Slicer-DICOM模块之Query/Retrieve功能介绍相关的知识,希望对你有一定的参考价值。

本文由Markdown语法编辑器编辑完成。

1. DICOM模块

3d Slicer作为医学影像的开源处理软件,当然离不开医学影像数据。
DICOM模块,就是专门负责DICOM影像的查找,获取,发送和接收的模块。

官方文档中关于DICOM的描述,见链接: https://slicer.readthedocs.io/en/latest/user_guide/modules/dicom.html
在这里插入图片描述
上述图像介绍了,3d Slicer加载医学影像的流程。
3d Slicer既支持加载DICOM影像数据,也支持加载非dcm数据,如NRRD, STL, JSON等格式的数据。
除了本地加载影像文件外,DICOM协议支持通过网络传输影像。也就是如上图中,红色框内的: DICOM query/retrieve。

2. Query/Retrieve功能介绍

Query/Retrieve功能,对应的是dicom协议中的findscu和movescu/getscu.

功能dcmtk指令
Queryfindscu
Retrievemovescu/getscu

在3d Slicer的DICOM页面中,有一个项目是: DICOM networking, 就是提供通过网络来进行DICOM影像传输的功能入口。
在这里插入图片描述
点击: "Query and retrieve"按钮,即可以进入如下的弹出页面。
页面中显示了,3d Slicer默认的AE Title为: “CTKSTORE”。
下面则是配置需要连接的PACS服务器的信息。
由于我已经在本地用dcm4chee搭建了一个PACS服务器。PACS服务器的AE Title是: DCM4CHEE,
Address是:192.168.109.83,Port是11112,且支持通过CGET的方式获取数据。
在这里插入图片描述
右侧的红框 Search Options, 就是通过一些已知的Patient/Study/Series的信息,来从PACS的服务器检索相关的数据。检索选择框下方的两列,看起来比较明显。一列是日期,另一列是Modality。
但是上方的 Name/Study/Series/ID, 就不是很明显。不知道应该输入什么信息,也不知道应该以什么样的格式来输入。
在Google上检索了很多关于3d Slicer的query/retrieve的文章,但都是关于如何利用下面的日期和Modality来query数据的,对于上面的Name/Study/Series/ID, 却没有介绍。

最后实在没有办法了。只能通过查询3d Slicer的源码来寻找有价值的信息了。
由于3d Slicer是一个持续了20多年的开源项目,因此代码量是相当庞大的。如何在如此庞大的代码量前面迅速地定位相关代码,也没有更好的办法。我只能是通过将代码加载在Visual Studio Code里面,然后利用编辑器自带的搜索功能,来搜索了。

我用的关键字是搜索框上方的"Search Options"。首先是在源码里面搜索,结果没有搜到任何信息。后来想到,3d Slicer在编译的过程中,同时还下载和编译了十几个项目的代码。因此,我又在3d Slicer bulid的目录下查找,结果真的找到了。

最后定位到的文件是位于3d Slicer的编译目录: Slicer-SuperBuild-Debug下的:
CTK/Libs/DICOM/Core/ctkDICOMQuery.cpp的函数: ctkDICOMQuery:query()中的Line 324 ~ 380.

  /* Now, for all keys that the user provided for filtering on STUDY level,
   * overwrite empty keys with value. For now, only Patient's Name, Patient ID,
   * Study Description, Modalities in Study, and Study Date are used.
   */
  QString seriesDescription;
  foreach( QString key, d->Filters.keys() )
    {
    if ( key == QString("Name") && !d->Filters[key].toString().isEmpty())
      {
      // make the filter a wildcard in dicom style
      d->Query->putAndInsertString( DCM_PatientName,
        (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data());
      }
    else if ( key == QString("Study") && !d->Filters[key].toString().isEmpty())
      {
      // make the filter a wildcard in dicom style
      d->Query->putAndInsertString( DCM_StudyDescription,
        (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data());
      }
    else if ( key == QString("ID") && !d->Filters[key].toString().isEmpty())
      {
      // make the filter a wildcard in dicom style
      d->Query->putAndInsertString( DCM_PatientID,
        (QString("*") + d->Filters[key].toString() + QString("*")).toLatin1().data());
      }
    else if ( key == QString("Modalities") && !d->Filters[key].toString().isEmpty())
      {
      // make the filter be an "OR" of modalities using backslash (dicom-style)
      QString modalitySearch("");
      foreach (const QString& modality, d->Filters[key].toStringList())
      {
        modalitySearch += modality + QString("\\\\");
      }
      modalitySearch.chop(1); // remove final backslash
      logger.debug("modalityInStudySearch " + modalitySearch);
      d->Query->putAndInsertString( DCM_ModalitiesInStudy, modalitySearch.toLatin1().data() );
      }
    // Rememer Series Description for later series query if we go through the keys now
    else if ( key == QString("Series") && !d->Filters[key].toString().isEmpty())
      {
      // make the filter a wildcard in dicom style
      seriesDescription = "*" + d->Filters[key].toString() + "*";
      }
    else
      {
      logger.debug("Ignoring unknown search key: " + key);
      }
    }

  if ( d->Filters.keys().contains("StartDate") && d->Filters.keys().contains("EndDate") )
    {
    QString dateRange = d->Filters["StartDate"].toString() +
                        QString("-") +
                        d->Filters["EndDate"].toString();
    d->Query->putAndInsertString ( DCM_StudyDate, dateRange.toLatin1().data() );
    logger.debug("Query on study date " + dateRange);
    }

正如这段代码的注释部分所描述的,3d Slicer的Search Options,目前的对应关系是:
在这里插入图片描述

Search OptionsDICOM TAG
NamePatient’s Name
StudyStudy Description
SeriesSeries Description
IDPatient ID
DateStudy Date
ModalityModalities in Study

根据以上的对应关系,便可以在Search Options的对应的Tab页输入相应的查询条件,来进行数据的检索。

完。

以上是关于3d Slicer-DICOM模块之Query/Retrieve功能介绍的主要内容,如果未能解决你的问题,请参考以下文章

妙味课堂实战功能开发视频教程 3D翻转焦点图/瀑布流/拖拽购物车/模块化开发等实战教程

Irrlicht 3D Engine 笔记系列 之 教程5- User Interface

Unity3D 游戏开发之内存优化

r query_tblastn.R

技术小新 | Qt开发指南之Data Visualization模块解读

Qt OpenGL模块和Qt/3D有啥区别