当前位置: 首页 > article >正文

第01章 11 分别使用DCMTK和gdcm库,解析DICOM文件系列的dicom标准数据信息

下面是一个使用 Qt 和 C++ 的示例,分别使用 DCMTK 和 GDCM 库解析 DICOM 文件,将解析到的 DICOM 标准数据信息显示在 QT 属性控件中。我们将创建一个简单的 GUI 应用程序,用户可以选择 DICOM 文件,解析其信息,并显示在属性控件中。

环境准备

  1. 安装 Qt 6.x。
  2. 安装 DCMTK 和 GDCM 库。
  3. 创建一个 Qt 项目。

项目结构

DicomInfoViewer/
├── CMakeLists.txt
├── main.cpp
├── dicom_loader.h
├── dicom_loader.cpp
├── viewer.h
├── viewer.cpp
├── dicom_properties_model.h
├── dicom_properties_model.cpp
├── mainwindow.h
├── mainwindow.cpp
├── mainwindow.ui
└── CMakeLists.txt

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(DicomInfoViewer)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)

add_executable(DicomInfoViewer
    main.cpp
    dicom_loader.cpp
    dicom_loader.h
    viewer.cpp
    viewer.h
    dicom_properties_model.cpp
    dicom_properties_model.h
    mainwindow.cpp
    mainwindow.h
    mainwindow.ui
)

target_link_libraries(DicomInfoViewer PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets)

# 链接 DCMTK 和 GDCM 库
find_package(DCMTK REQUIRED)
find_package(GDCM REQUIRED)

target_link_libraries(DicomInfoViewer PRIVATE ${DCMTK_LIBRARIES} ${GDCM_LIBRARIES})

# 包含路径
target_include_directories(DicomInfoViewer PRIVATE ${DCMTK_INCLUDE_DIRS} ${GDCM_INCLUDE_DIRS})

main.cpp

#include <QtWidgets>
#include "mainwindow.h"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    MainWindow mainWindow;
    mainWindow.show();
    return app.exec();
}

dicom_loader.h

#ifndef DICOM_LOADER_H
#define DICOM_LOADER_H

#include <string>
#include <unordered_map>
#include <QVector>
#include <QMap>
#include <QPair>

class DicomLoader {
public:
    struct DicomTag {
        std::string tag;
        std::string value;
    };

    using DicomTags = QVector<DicomTag>;

    DicomTags loadTagsWithDCMTK(const std::string& filePath);
    DicomTags loadTagsWithGDCM(const std::string& filePath);
};

#endif // DICOM_LOADER_H

dicom_loader.cpp

#include "dicom_loader.h"
#include <dcmtk/dcmdata/dctk.h>
#include <gdcmReader.h>
#include <gdcmTag.h>
#include <gdcmStringFilter.h>

DicomLoader::DicomTags DicomLoader::loadTagsWithDCMTK(const std::string& filePath) {
    DicomTags tags;
    DcmFileFormat fileformat;
    if (fileformat.loadFile(filePath.c_str()).good()) {
        DcmDataset *dataset = fileformat.getDataset();
        OFString tagValue;
        for (int i = 0x0002; i < 0x1000; ++i) {
            for (int j = 0x0000; j < 0xFFFF; ++j) {
                if (dataset->search(tagValue, makeTag(i, j),<hsearch_mode>(EEM Std) | EGL create)) {
                    tags.append({fmt::format("({:04X},{:04X})", i, j).c_str(), tagValue.c_str()});
                }
            }
        }
    }
    return tags;
}

DicomLoader::DicomTags DicomLoader::loadTagsWithGDCM(const std::string& filePath) {
    DicomTags tags;
    gdcm::Reader reader;
    reader.SetFileName(filePath.c_str());
    if (reader.Read()) {
        gdcm::File &file = reader.GetFile();
        gdcm::DataSet &dataset = file.GetDataSet();
        std::vector<gdcm::Tag> tags = file.GetHeader().GetTags();
        for (const auto& tag : tags) {
            gdcm::SmartPointer<gdcm::DataElement> element = dataset.GetDataElement(tag);
            if (element) {
                gdcm::StringFilter sf;
                sf.SetElement(element);
                tags.append({fmt::format("({:04X},{:04X})", tag.GetGroup(), tag.GetElement()).c_str(), sf.ToString().c_str()});
            }
        }
    }
    return tags;
}

dicom_properties_model.h

#ifndef DICOM_PROPERTIES_MODEL_H
#define DICOM_PROPERTIES_MODEL_H

#include <QAbstractTableModel>
#include <QVector>
#include <QPair>

class DicomPropertiesModel : public QAbstractTableModel {
    Q_OBJECT

public:
    explicit DicomPropertiesModel(QObject *parent = nullptr);
    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
    void setTags(const QVector<QPair<QString, QString>>& tags);

private:
    QVector<QPair<QString, QString>> m_tags;
};

#endif // DICOM_PROPERTIES_MODEL_H

dicom_properties_model.cpp

#include "dicom_properties_model.h"

DicomPropertiesModel::DicomPropertiesModel(QObject *parent)
    : QAbstractTableModel(parent) {}

int DicomPropertiesModel::rowCount(const QModelIndex &parent) const {
    return parent.isValid() ? 0 : m_tags.size();
}

int DicomPropertiesModel::columnCount(const QModelIndex &parent) const {
    return parent.isValid() ? 0 : 2;
}

QVariant DicomPropertiesModel::data(const QModelIndex &index, int role) const {
    if (!index.isValid() || index.row() >= m_tags.size() || index.column() >= 2) {
        return QVariant();
    }

    if (role == Qt::DisplayRole) {
        const auto& tag = m_tags[index.row()];
        return index.column() == 0 ? tag.first : tag.second;
    }

    return QVariant();
}

QVariant DicomPropertiesModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (role != Qt::DisplayRole || orientation != Qt::Horizontal) {
        return QVariant();
    }

    return section == 0 ? "Tag" : "Value";
}

void DicomPropertiesModel::setTags(const QVector<QPair<QString, QString>>& tags) {
    beginResetModel();
    m_tags = tags;
    endResetModel();
}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "dicom_loader.h"
#include "dicom_properties_model.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow {
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void onOpenFile();
    void onLoadTagsWithDCMTK();
    void onLoadTagsWithGDCM();

private:
    Ui::MainWindow *ui;
    DicomLoader loader;
    DicomPropertiesModel model;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QFileDialog>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow) {
    ui->setupUi(this);
    ui->tableView->setModel(&model);
    connect(ui->actionOpenFile, &QAction::triggered, this, &MainWindow::onOpenFile);
    connect(ui->actionLoadTagsWithDCMTK, &QAction::triggered, this, &MainWindow::onLoadTagsWithDCMTK);
    connect(ui->actionLoadTagsWithGDCM, &QAction::triggered, this, &MainWindow::onLoadTagsWithGDCM);
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::onOpenFile() {
    QString filePath = QFileDialog::getOpenFileName(this, "Open DICOM File", "", "DICOM Files (*.dcm)");
    if (filePath.isEmpty()) {
        return;
    }

    onOpenFile(filePath.toStdString());
}

void MainWindow::onLoadTagsWithDCMTK() {
    QString filePath = QFileDialog::getOpenFileName(this, "Open DICOM File", "", "DICOM Files (*.dcm)");
    if (filePath.isEmpty()) {
        return;
    }

    auto tags = loader.loadTagsWithDCMTK(filePath.toStdString());
    QVector<QPair<QString, QString>> tagPairs;
    for (const auto& tag : tags) {
        tagPairs.append({tag.tag.c_str(), tag.value.c_str()});
    }
    model.setTags(tagPairs);
}

void MainWindow::onLoadTagsWithGDCM() {
    QString filePath = QFileDialog::getOpenFileName(this, "Open DICOM File", "", "DICOM Files (*.dcm)");
    if (filePath.isEmpty()) {
        return;
    }

    auto tags = loader.loadTagsWithGDCM(filePath.toStdString());
    QVector<QPair<QString, QString>> tagPairs;
    for (const auto& tag : tags) {
        tagPairs.append({tag.tag.c_str(), tag.value.c_str()});
    }
    model.setTags(tagPairs);
}

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>DICOM Info Viewer</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <layout class="QVBoxLayout" name="verticalLayout">
    <item>
     <widget class="QTableView" name="tableView"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>21</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuFile">
    <property name="title">
     <string>File</string>
    </property>
    <addaction name="actionOpenFile"/>
   </widget>
   <widget class="QMenu" name="menuLoadTags">
    <property name="title">
     <string>Load Tags</string>
    </property>
    <addaction name="actionLoadTagsWithDCMTK"/>
    <addaction name="actionLoadTagsWithGDCM"/>
   </widget>
   <addaction name="menuFile"/>
   <addaction name="menuLoadTags"/>
  </widget>
  <action name="actionOpenFile">
   <property name="text">
    <string>Open File</string>
   </property>
  </action>
  <action name="actionLoadTagsWithDCMTK">
   <property name="text">
    <string>Load Tags with DCMTK</string>
   </property>
  </action>
  <action name="actionLoadTagsWithGDCM">
   <property name="text">
    <string>Load Tags with GDCM</string>
   </property>
  </action>
 </widget>
</ui>

构建和运行

  1. 使用 CMake 构建项目:

    mkdir build
    cd build
    cmake ..
    make
    
  2. 运行应用程序:

    ./DicomInfoViewer
    
  3. 打开应用程序后,选择一个 DICOM 文件,选择使用 DCMTK 或 GDCM 解析其标签信息,信息将显示在表格中。


http://www.kler.cn/a/512886.html

相关文章:

  • Kotlin协程中withContext、async 和 launch 的区别
  • Pix2Pix:图像到图像转换的条件生成对抗网络深度解析
  • 代码随想录_字符串
  • 1.2.神经网络基础
  • 深度学习-89-大语言模型LLM之AI应用开发的基本概念
  • iOS UIScrollView的一个特性
  • SpringBoot 搭建 SSE
  • Numpy基础01(Jupyter基本用法/Ndarray创建与基本操作)
  • vue.draggable 拖拽
  • 2025年国产化推进.NET跨平台应用框架推荐
  • MyBatis操作数据库(入门)
  • 【Java实现导出Excel使用EasyExcel快速实现数据下载到Excel功能】
  • Qt之QDjango-db的简单使用
  • 三格电子——MODBUS TCP 转 CANOpen 协议网关
  • 网络通信---MCU移植LWIP
  • 从零开始:使用 Brain.js 创建你的第一个神经网络(一)
  • Redis - 通用命令
  • Spring Boot 整合 PageHelper 实现分页功能
  • 线程池遇到未处理的异常会崩溃吗?
  • Redis的Windows版本安装以及可视化工具
  • PHP代码审计学习01
  • Github 2025-01-20 开源项目周报 Top15
  • Docker:基于自制openjdk8镜像 or 官方openjdk8镜像,制作tomcat镜像
  • Linux 时间操作详解
  • 什么是馈线自动化(FA)?其优点是什么?本文给出答案
  • 14,c++——继承