【DCMTK】遍历标签tags

本文介绍了如何使用DCMTK库遍历DICOM文件的标签,并通过Qt实现标签的可视化。作者探讨了直接使用DCMTK接口、转换为JSON或XML以及自定义遍历方法的优缺点,最终选择直接操作DcmList进行遍历。文章还展示了利用Qt创建树形结构展示 DICOM 数据集和元信息的代码示例,并分享了Qt界面的样式表。
摘要由CSDN通过智能技术生成

  做kissDicomViewer需要用到遍历标签功能。记录下每天学习内容

项目地址:
CodeChina kissDicomViewer
详细介绍:
CSDN 一个简单的 DICOM 浏览器


DCMTK 遍历 DICOM 标签/Tags 并 可视化

1. 效果

  请无视目前样式,这个只是自己做遍历标签可视化测试用的。后续会抄小蚂蚁加上搜索、右键拷贝值、拷贝全部值、拷贝键等等。

  区分MetaInfoDataSet

在这里插入图片描述

  四层嵌套显示效果

在这里插入图片描述


2. 现成接口,转成其他格式后再解析

  做DICOM浏览器时有一个需求是显示所有DCIOM标签。找了下dcmtk的相关资料,竟然没有输出所有DCIOM标签的案例。DCIOM提供了三个默认的接口用来可视化DCIOM标签。

  • 直接在控制台或者ostream 输出标签
  • 转成JSON --可以看appdcm2json
  • 转成XML --可以看appdcm2xml
virtual void print(STD_NAMESPACE ostream &out,
                    const size_t flags = 0,
                    const int level = 0,
                    const char *pixelFileName = NULL,
                    size_t *pixelCounter = NULL) = 0;

virtual OFCondition writeJson(STD_NAMESPACE ostream &out,
                              DcmJsonFormat &format);

virtual OFCondition writeXML(STD_NAMESPACE ostream &out,
                              const size_t flags = 0);

  一开始打算转成XML/JSON,然后用QT做一个XML/JSON解析器再可视化到窗口。也能实现就是感觉太麻烦了。


3. 利用DcmItem::nextInContainer 实现遍历

  这块中文资料还是太少了,没有一个现成的直接遍历标签的办法。找到两篇中文博客说:用DcmItem::nextInContainer来实现,看doxygen介绍应该是默认用的办法,就是没找到现成案例
https://blog.csdn.net/qq_39071305/article/details/102474753
https://blog.csdn.net/a36254094/article/details/7614428
  我没去试,这两篇博客作者指出这个办法SQ数据不能很好的读取。

看到好几个博客说SQ只能嵌套三层,我手上测试用的影响就是嵌套四层的。DICOM协议我找了一下part05 第七章也没看到有说最多嵌套几层。不知道是当时作者翻译错了还是我没找到。有知道望给个说明。


4. 直接拿DcmList遍历

  现成的实在没找到,看下源码吧。DCMTK就是把数据集搞了一个双链表类:DcmList。看了下源码后自己的理解:DCMTK 关于DICOM数据集的处理。那我直接获取这个DcmList,然后遍历吧。

想了个 2B 的办法,够简单就是不优雅、DCMTK开发人员看到估计很鄙视这种办法。

  下边段代码都是实现遍历输出,如果你只是输出的话,DCMTK有现成的(开头三种办法)

  • 使用默认接口的办法和输出结果
dfile.getMetaInfo()->print(std::cout,DCMTypes::PF_useANSIEscapeCodes);
dfile.getDataset()->print(std::cout,DCMTypes::PF_useANSIEscapeCodes);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2qmdIkXG-1611324177459)(vx_images/5436003775916.png)]

  • 获取DcmList自己遍历的办法和输出结果
#include "dcmtk/dcmdata/dctk.h"

#define Print std::cout<<
#define Printend <<std::endl;
#define PrintemspValue(emsp,value1,value2,value3,value4,value5,value6) \
    for(int i = 0; i < emsp; i++) {Print "\40\40";}\
    Print value1 <<" "  <<value2<<" ["<< value3 <<"] "\
                 << value4 <<" " <<value5 <<" (" <<value6<<")" Printend

template<typename T>
class NewDcmItem: public T {
  public:
    NewDcmItem(const T &old): T(old) {
    }
    DcmList *GetDcmList()const {
        return this->elementList;
    }
  protected:
    virtual ~NewDcmItem() {}
};
using MyDcmDataset = NewDcmItem<DcmDataset>;
using MyDcmMetaInfo = NewDcmItem<DcmMetaInfo>;
using MywDcmItem = NewDcmItem<DcmItem>;

template<typename T>
void PaintTags(T &t, const int &emsp = 0) {
    DcmList *elementList = t.GetDcmList();
    if (!elementList->empty()) {
        DcmObject *dO;
        DcmTag tag;
        OFString value;
        elementList->seek(ELP_first);
        do {
            dO = elementList->get();
            tag = dO->getTag();
            DcmElement *elem;
            t.findAndGetElement(tag, elem);
            elem->getOFString(value, 0);
            PrintemspValue(emsp, tag, tag.getVRName(),
                           value, dO->getLength(), dO->getVM(), tag.getTagName())
            if(EVR_SQ == dO->getVR()) {
                DcmItem *sq;
                t.findAndGetSequenceItem(dO->getTag(), sq);
                MywDcmItem *dcmitem_info = new MywDcmItem(*sq);
                PaintTags(*dcmitem_info, emsp + 1);
            }
        } while (elementList->seek(ELP_next));
        delete dO;
    }
}

int main() {
    DcmFileFormat dfile;
    dfile.loadFile("/home/arteryflow/图片/DicomData/DSA/Liyunlong/IMG-0002-00001.dcm");
    Print "DcmMetaInfo------" Printend
    MyDcmMetaInfo *meta_info = new MyDcmMetaInfo(*dfile.getMetaInfo());
    PaintTags(*meta_info);
    Print "DcmDataset------" Printend
    MyDcmDataset *dataset_info = new MyDcmDataset(*dfile.getDataset());
    PaintTags(*dataset_info);
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IIiSBGOI-1611324177461)(vx_images/626357890759.png)]

5. 结合Qt可视化

  最开始结果图的程序,仅仅可视化。小蚂蚁这块的功能等过年在搞。

#include "dcmtk/dcmdata/dctk.h"

template<typename T>
class NewDcmItem: public T {
  public:
    NewDcmItem(const T &old): T(old) {
    }
    DcmList *GetDcmList()const {
        return this->elementList;
    }
  protected:
    virtual ~NewDcmItem() {}
};
using MyDcmDataset = NewDcmItem<DcmDataset>;
using MyDcmMetaInfo = NewDcmItem<DcmMetaInfo>;
using MywDcmItem = NewDcmItem<DcmItem>;

//
template<typename T>
void GenerateItems(QList<QTreeWidgetItem *> &items, T &t) {
    DcmList *elementList = t.GetDcmList();
    if (!elementList->empty()) {
        DcmObject *dO;
        DcmTag tag;
        OFString value;
        elementList->seek(ELP_first);
        do {
            dO = elementList->get();
            tag = dO->getTag();
            DcmElement *elem;
            t.findAndGetElement(tag, elem);
            elem->getOFString(value, 0);
            QTreeWidgetItem *tmp_item = new QTreeWidgetItem;
            tmp_item->setText(0, QString::fromLocal8Bit(tag.toString().c_str()));
            tmp_item->setText(1, tag.getVRName());
            tmp_item->setText(2, QString::number(dO->getLength()));
            tmp_item->setText(3, QString::number(dO->getVM()));
            tmp_item->setText(4, tag.getTagName());
            tmp_item->setText(5, QString::fromLocal8Bit(value.c_str()));
            if(EVR_SQ == dO->getVR()) {
                QList<QTreeWidgetItem *> tmp_items;
                DcmItem *sq;
                t.findAndGetSequenceItem(dO->getTag(), sq);
                MywDcmItem *dcmitem_info = new MywDcmItem(*sq);
                GenerateItems(tmp_items, *dcmitem_info);
                tmp_item->addChildren(tmp_items);
            }
            items << tmp_item;
        } while (elementList->seek(ELP_next));
        delete dO;
    }
}

QTreeWidget *GetWid() {
    QTreeWidget *wid = new QTreeWidget;
    wid->setHeaderLabels(QStringList()
                         << "Tag ID" << "VR" << "VM" << "Length" << "Description" << "value");
    wid->setColumnWidth(0, 200);
    wid->setColumnWidth(1, 70);
    wid->setColumnWidth(2, 100);
    wid->setColumnWidth(3, 50);
    wid->setColumnWidth(4, 300);
    wid->setColumnWidth(5, 300);
    wid->setGeometry(0, 0, 1200, 800);
    return wid;
}

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    using namespace Kiss;
    QUIHelper::SetStyle(":/Style/style.qss");
    // 初始化 TreeWidget
    QTreeWidget *wid = GetWid();
    QList<QTreeWidgetItem *> items;
    // 读取文件
    DcmFileFormat dfile;
    dfile.loadFile("C:/Users/77935/Pictures/dcm/source.dcm");
    //
    QTreeWidgetItem *metainfo_item = new QTreeWidgetItem;
    metainfo_item->setText(0, "Dicom-MetaInfo");
    items << metainfo_item;
    MyDcmMetaInfo *meta_info = new MyDcmMetaInfo(*dfile.getMetaInfo());
    GenerateItems(items, *meta_info);
    QTreeWidgetItem *dataset_item = new QTreeWidgetItem;
    dataset_item->setText(0, "Dicom-Data-Set");
    items << dataset_item;
    MyDcmDataset *dataset_info = new MyDcmDataset(*dfile.getDataset());
    GenerateItems(items, *dataset_info);
    // 显示
    wid->addTopLevelItems(items);
    wid->show();
    return a.exec();
}

  样式表

/*---------------------------------------------------------------------------*/
/*  QTreeWidget  */
/*---------------------------------------------------------------------------*/
/*  QListView  */
/*---------------------------------------------------------------------------*/
/*  QTreeView  */

QTreeView,
QTreeWidget,
QListView {
    border-radius:5px;
    font-size: 14px;
    background-color:rgba(37, 41, 43, 1);
    border:1px solid rgba(47, 51, 54, 1);
    outline:0px;
    selection-background-color:#262829;
    selection-color:#BEC0C2;
    alternate-background-color:#262829;
    gridline-color:#67696B;
}

QTreeView::branch,
QTreeWidget::branch,
QListView::branch{
    background:#2E2F30;
}

QTreeView::branch:closed:has-children,
QTreeWidget::branch:closed:has-children,
QListView::branch:closed:has-children{
    border-image:url(:/Style/StyleResources/branch_open.png);
}

QTreeView::branch:open:has-children,
QTreeWidget::branch:open:has-children,
QListView::branch:open:has-children{
    border-image:url(:/Style/StyleResources/branch_close.png);
}

QTreeView::item,
QTreeWidget::item,
QListView::item{
    color:rgb(184,200,212,255);
    background: rgb(38, 40, 44);
    padding:10px 14px;
    height: 2px;
}

QTreeView::item:alternate,
QTreeWidget::item:alternate,
QListView::item:alternate {
    background: rgb(42, 46, 49);
}


QTreeWidget::item:hover,
QTreeView::item:hover,
QListView::item:hover{
    background: rgb(55, 61, 64);
    color:rgb(255,255,255,255);
}

QTreeView::item:selected,
QListView::item:selected,
QTreeWidget::item:selected{
    background: rgb(85, 91, 94);
    color:rgb(255,255,255,255);
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Beyond欣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值