Skip to content

DCM标签可视化

1 效果

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

区分 MetaInfoDataSet

四层嵌套显示效果


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

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

  • 直接在控制台或者ostream 输出标签
  • 转成JSON --可以看**app**里 dcm2json
  • 转成XML --可以看**app**里 dcm2xml
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);
  • 获取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);
}

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);
}