由于mitk.net被人恶意抢注, 中科院分子影像重点实验室的www.mitk.net 改到 www.mitk.net.cn 。
目录:
第一章 《DCMTK(MD版)、QT、VS2015编写Dicom序列浏览应用程序-新建项目,配置环境》
第二章 《第二章 基于QT和DCMTK的Dicom 图像浏览器---界面设计》
第三章 《 基于QT和DCMTK的Dicom 图像浏览器---单个Dicom图像读取类》
第四章 《基于QT和DCMTK的Dicom 图像浏览器---检查文件夹下Dicom序列个数》
第五章 《基于QT和DCMTK的Dicom 图像浏览器---Dicom图像序列类》
第六章 《基于QT和DCMTK的Dicom 图像浏览器---Dicom视图类》
第七章 《基于QT和DCMTK的Dicom 图像浏览器---收尾》
一、描述
一个文件夹下(有多个DCOM单文件),可能有多个DCOM序列,每个序列有多个DCOM单文件。
series UID 是区别每个序列的标识:同Dicom序列的series UID 一样,不同序列的series UID不一样。
instanceNumber是序列内Dicom单文件的标识: instanceNumber决定了Dicom单文件在序列内的位置。
本章实现功能 “点击“选择Dicom文件夹”按钮,选择文件夹,检索文件夹下的DICOM序列个数,并初始化选择序列下拉菜单”,具体见第五节
二、添加在子线程工作的类ReadWorker
添加一个QT类继承QOBJect:
1) 在项目上右键->添加->Add Qt Class , 然后按照向导添加类 ReadWorker,如下图
三、编辑ReadWorker如下
#pragma once
#include <QObject>
#include <QMap>
class ReadWorker : public QObject
{
Q_OBJECT
public:
ReadWorker(QString DicomsFileDir, QObject *parent = 0);
~ReadWorker();
// 每个seriesUid 对应多个文件,
static QMap< QString, QStringList> UID_Files;
public slots:
void checkDicomsFileDir(); // 检查文件夹下Dicom序列个数,应在单独的线程运行
signals:
// 发送进度(0-100)
void progress(int);
// 线程结束信号
void finish();
private:
// 包含多个Dicom 单文件的文件夹,可能有多个序列。
QString DicomsFileDir;
};
#include "ReadWorker.h"
#include "Image.h"
#include <QDir>
ReadWorker::ReadWorker(QString DicomsFileDir,QObject *parent)
: QObject(parent)
{
this->DicomsFileDir = DicomsFileDir;
}
ReadWorker::~ReadWorker()
{
}
QMap< QString, QStringList> ReadWorker::UID_Files;
void ReadWorker::checkDicomsFileDir()
{
// 清空序列
UID_Files.clear();
QDir dir(DicomsFileDir);
// 获取文件夹下所有文件名
QStringList files = dir.entryList(QDir::Files);
int filesCount = files.count(); // 所有文件的个数
Image *dicom = 0; // 单Dicom文件
int progressValue = 0;
// 遍历每个文件
foreach(QString fileName, files)
{
++progressValue;
emit progress(100 * progressValue / filesCount/* 转到0-100之间*/);
// 带路径的文件名
fileName = DicomsFileDir + "/" + fileName;
//使用我们写的Image类读取dicom,此处使用了new,记住释放
dicom = new Image(fileName);
if (!dicom->isNormal())
{
delete dicom; dicom = 0;
continue; // 读取不成功(不是Dicom文件),跳出直接读取下一个文件
}
/*---读取成功,整理序列---*/
QString temp = dicom->getSeriesUID(); // 获取该序列的 UID
/*-----UID_Files键存在 序列号 temp 就把fileName添加到值------*/
/*-----UID_Files键不存在 序列号 temp 就创建值,并插入键值---*/
UID_Files.keys().contains(temp)? UID_Files[temp].append(fileName): UID_Files.insert(temp, QStringList(fileName));
delete dicom; dicom = 0; // 操作完成后,释放,循环读取下一个。
}
emit finish();
}
四、编辑主界面DicomBrowse.h 和 DicomBrowse.cpp
DicomBrowse.h 中添加属性 bool isOpeing; // 是否正在读取文件
...
class DicomBrowse : public QMainWindow
{
...
public slots:
...
void ReadCheckCompleted(); // worker完成后执行该函数
private:
...
bool isOpeing; // 是否正在读取文件
};
为了使QSettings 生效,编辑main.cpp 如下行
...
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
a.setOrganizationName("CASMI");
a.setOrganizationDomain("www.radiomics.net.cn");
a.setApplicationName("DicomBrowse");
...
}
DicomBrowse.cpp
...
#include "ReadWorker.h"
#include<QThread>
#include<QSettings>
#include <QFileDialog>
#include <QMessageBox>
DicomBrowse::DicomBrowse(QWidget *parent)
: QMainWindow(parent)
{
...
isOpeing = false; // 添加
}
...
void DicomBrowse::on_pushButton_clicked()
{
// 点击 "选择Dicom文件夹"按钮后,执行该函数
QSettings s; // 为了记住上一次的路径
QString dirStr = s.value("OPEN_FILEPATH", ".").toString();// 不存在的话为当前应用程序路径
dirStr = QFileDialog::getExistingDirectory(this, QStringLiteral("选择Dicom序列所在的文件夹"), dirStr);
if (dirStr.isEmpty())
return;
s.setValue("OPEN_FILEPATH", dirStr); // 记住该路径,以备下次使用
if (isOpeing)
{
QMessageBox::warning(this, "TIP", QStringLiteral("正在检查数据,请等待完成打开其他数据!"));
return;
}
isOpeing = true;
ReadWorker *worker = new ReadWorker(dirStr);
QThread *thread = new QThread();
connect(thread, SIGNAL(started()), worker, SLOT(checkDicomsFileDir())); // 线程开始后执行worker->checkDicomsFileDir()
connect(worker, SIGNAL(progress(int)), this, SLOT(setProgressBarValue(int)));// worker 发送信号,执行this->setProgressBarValue
connect(worker, SIGNAL(finish()), this, SLOT(ReadCheckCompleted()));// 检查完成,执行this->ReadCheckCompleted()
connect(worker, SIGNAL(finish()), worker, SLOT(deleteLater()));// 执行完成,析构worker
connect(worker, SIGNAL(destroyed(QObject*)), thread, SLOT(quit()));// 析构worker 完成, 推出线程
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater())); // 推出线程, 析构线程
worker->moveToThread(thread); // 把worker 移动到线程
thread->start(); // 开始线程
}
...
void DicomBrowse::ReadCheckCompleted()
{
ui.comboBox_which->clear();
// worker->checkDicomsFileDir()线程执行完成后,执行该函数
// 序列的个数
int seriesCount = ReadWorker::UID_Files.count();
ui.textBrowser->append(QStringLiteral("共有%1个序列: ").arg(seriesCount));
foreach(QString uid, ReadWorker::UID_Files.keys())
{
ui.comboBox_which->addItem(uid);
ui.textBrowser->append(QStringLiteral("序列%1: 共%2张图像").arg(uid).arg(ReadWorker::UID_Files[uid].count()));
}
isOpeing = false;
}
...
五 、 实验