ITK读取DICOM文件时返回进度(基于Qt)

基本思路:继承itk::Command类,重写Excute函数,利用itk的观察者模式获取耗时操作的进度数据,然后转化成Qt的信号发送出去。

而要在Excute函数内发送信号,就得利用clientData将报告进度的Reporter类(继承自QObject)对象指针传到Excute函数内部,所以选择itk::CStyleCommand来继承。代码如下:

class FIVISION_DECLSPEC ProgressCommand : public itk::CStyleCommand
{
  public:
    itkNewMacro(ProgressCommand);

    void Execute(itk::Object *caller, const itk::EventObject &event) override
    {
        Execute((const itk::Object *)caller, event);

    }
    void Execute(const itk::Object *caller, const itk::EventObject &event) override
    {
        if (!itk::ProgressEvent().CheckEvent(&event)) {
            return;
        }
        const auto *processObject = dynamic_cast<const itk::ProcessObject *>(caller);
        if (!processObject) {
            return;
        }
        ItkProgressReporter *reporter = (ItkProgressReporter *)m_ClientData;
        reporter->progressChanged(processObject->GetProgress());
    }
};

用于报告进度的ItkProgressReporter类,继承自QObject,它有一个progressChanged信号,发送一个float值。在添加观察者之前,把它的指针作为clientData设置给ProgressCommand类,然后在Excute函数内进行类型转换并发送信号。ItkProgressReporter类实现如下:

class FIVISION_DECLSPEC ItkProgressReporter : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(ItkProgressReporter);

  private:
    ProgressCommand::Pointer m_command;

  signals:
    void progressChanged(float);

  public:
    ItkProgressReporter(QObject *parent = nullptr) : QObject(parent)
    {
        m_command = ProgressCommand::New();
        m_command->SetClientData(this);
    }
    ~ItkProgressReporter() {}
    ProgressCommand::Pointer getCommand() { return m_command; }
};

以读取三维DIOCM图像为例,使用时,用ItkProgressReporter内的command指针给reader添加观察者,观察itk::ProgressEvent事件,然后连接ItkProgressReporter的progressChanged信号即可。

m_imageIO = itk::GDCMImageIO::New();    
m_reader = itk::ImageSeriesReader<Image3DType>::New();
m_reader->SetImageIO(m_imageIO);
m_reproter = new ItkProgressReporter();
m_reader->AddObserver(itk::ProgressEvent(), m_reproter->getCommand());
connect(m_reproter, &ItkProgressReporter::progressChanged, this, &ImageSeriesReader3D::progressChanged);

为了不卡住主线程,还可以将读图操作放到子线程执行,这里基于QThread写了一个QuickItkImageSeriesReader3D类,因为项目前端架构用的Qml,所以多了一些C++注册自定义Qml类型的代码。基本思路是按照Qt官方推荐的多线程写法,将需要在子线程内执行的操作封装在Worker类里面,再写一个Controller类通过信号槽来控制子线程的行为。这里的QuickItkImageSeriesReader3D类对应Controller,ImageSeriesReader3D类对应Worker。

完整代码如下:

fiQuickItkImageSeriesReader3D.h文件:

#ifndef _FI_QUICK_ITK_IMAGESERIESREADER3D_H_
#define _FI_QUICK_ITK_IMAGESERIESREADER3D_H_

#include "fiItkProgressReporter.h"
#include "fiQuickItkImage3D.h"
#include "fiVisionGlobal.h"
#include <QList>
#include <QThread>
#include <itkChangeInformationImageFilter.h>
#include <itkGDCMImageIO.h>
#include <itkGDCMSeriesFileNames.h>
#include <itkImageSeriesReader.h>

class ImageSeriesReader3D;
class QuickItkImageSeriesReader3D : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(QuickItkImageSeriesReader3D);
    Q_PROPERTY(QuickItkImage3D *output READ getOutput NOTIFY outputChanged);
    Q_PROPERTY(QStringList fileNames READ getFileNames WRITE setFileNames NOTIFY fileNamesChanged);
    Q_PROPERTY(QString inputDirectory READ getInputDirectory WRITE setInputDirectory NOTIFY inputDirectoryChanged);
    Q_PROPERTY(float progress READ getProgress NOTIFY progressChanged);

    QML_ELEMENT

  private:
    ImageSeriesReader3D *m_imageReader;
    QThread m_thread;
    QuickItkImage3D *m_outputImageData{nullptr};

  protected:
    float m_progress{0};

  signals:
    void outputChanged();
    void fileNamesChanged(QStringList);
    void inputDirectoryChanged(QString);
    void doRead(QuickItkImage3D *);
    void progressChanged(float);

  public:
    Q_INVOKABLE QuickItkImageSeriesReader3D(QObject *parent = nullptr);
    ~QuickItkImageSeriesReader3D();

    Q_INVOKABLE QuickItkImage3D *getOutput();
    Q_INVOKABLE bool setFileNames(QStringList fileNames);
    Q_INVOKABLE QStringList getFileNames();
    Q_INVOKABLE bool setInputDirectory(QString inputDirectory);
    Q_INVOKABLE QString getInputDirectory();
    Q_INVOKABLE float getProgress();

  private slots:
    bool setProgress(float progress);
};

class ImageSeriesReader3D : public QObject
{
    Q_OBJECT

  private:
    itk::ImageSeriesReader<Image3DType>::Pointer m_reader{nullptr};
    itk::GDCMImageIO::Pointer m_imageIO{nullptr};
    itk::GDCMSeriesFileNames::Pointer m_seriesFileNames{nullptr};
    ItkProgressReporter *m_reproter{nullptr};

  protected:
    QStringList m_fileNames;
    QString m_inputDirectory;
    bool m_isInputDirectory{false};

  signals:
    void outputChanged();
    void progressChanged(float);

  public slots:
    bool update(QuickItkImage3D *outputImage);

  public:
    ImageSeriesReader3D(QObject *parent = nullptr);
    ~ImageSeriesReader3D() {}

  public:
    bool setFileNames(QStringList fileNames);
    QStringList getFileNames();
    bool setInputDirectory(QString inputDirectory);
    QString getInputDirectory();
};

#endif // !_FI_QUICK_ITK_IMAGESERIESREADER3D_H_

fiQuickItkImageSeriesReader3D.cpp文件:

#include "fiQuickItkImageSeriesReader3D.h"

QuickItkImageSeriesReader3D::QuickItkImageSeriesReader3D(QObject *parent) : QObject(parent)
{
    m_outputImageData = new QuickItkImage3D(nullptr, this);
    m_imageReader = new ImageSeriesReader3D();
    m_imageReader->moveToThread(&m_thread);
    connect(&m_thread, &QThread::finished, m_imageReader, &QObject::deleteLater);
    connect(this, &QuickItkImageSeriesReader3D::doRead, m_imageReader, &ImageSeriesReader3D::update);
    connect(m_imageReader, &ImageSeriesReader3D::outputChanged, this, &QuickItkImageSeriesReader3D::outputChanged);
    connect(m_imageReader, &ImageSeriesReader3D::progressChanged, this, &QuickItkImageSeriesReader3D::setProgress);
    m_thread.start();
}

QuickItkImageSeriesReader3D::~QuickItkImageSeriesReader3D()
{
    m_thread.quit();
    m_thread.wait();
}

QuickItkImage3D *QuickItkImageSeriesReader3D::getOutput()
{
    return m_outputImageData;
}

bool QuickItkImageSeriesReader3D::setFileNames(QStringList fileNames)
{
    if (fileNames == m_imageReader->getFileNames())
        return false;

    m_imageReader->setFileNames(fileNames);
    emit fileNamesChanged(fileNames);
    emit doRead(m_outputImageData);
    return true;
}

QStringList QuickItkImageSeriesReader3D::getFileNames()
{
    return m_imageReader->getFileNames();
}

bool QuickItkImageSeriesReader3D::setInputDirectory(QString inputDirectory)
{
    if (inputDirectory == m_imageReader->getInputDirectory())
        return false;
    m_imageReader->setInputDirectory(inputDirectory);
    emit inputDirectoryChanged(inputDirectory);
    emit doRead(m_outputImageData);
    return true;
}

QString QuickItkImageSeriesReader3D::getInputDirectory()
{
    return m_imageReader->getInputDirectory();
}

float QuickItkImageSeriesReader3D::getProgress()
{
    return m_progress;
}

bool QuickItkImageSeriesReader3D::setProgress(float progress)
{
    m_progress = progress;
    emit this->progressChanged(progress);
    return true;
}

//=======================ImageSeriesReader3D=========================//

ImageSeriesReader3D::ImageSeriesReader3D(QObject *parent) : QObject(parent)
{
    m_reader = itk::ImageSeriesReader<Image3DType>::New();
    m_imageIO = itk::GDCMImageIO::New();
    m_seriesFileNames = itk::GDCMSeriesFileNames::New();
    m_reproter = new ItkProgressReporter();
    m_reader->SetImageIO(m_imageIO);
    m_reader->UseStreamingOff();
    m_reader->AddObserver(itk::ProgressEvent(), m_reproter->getCommand());
    connect(m_reproter, &ItkProgressReporter::progressChanged, this, &ImageSeriesReader3D::progressChanged);
}

bool ImageSeriesReader3D::setFileNames(QStringList fileNames)
{
    m_fileNames = fileNames;
    m_isInputDirectory = false;
    return true;
}

QStringList ImageSeriesReader3D::getFileNames()
{
    return m_fileNames;
}

bool ImageSeriesReader3D::setInputDirectory(QString inputDirectory)
{
    m_inputDirectory = inputDirectory;
    m_isInputDirectory = true;
    return true;
}

QString ImageSeriesReader3D::getInputDirectory()
{
    return m_inputDirectory;
}

bool ImageSeriesReader3D::update(QuickItkImage3D *outputImage)
{
    std::vector<std::string> fileNamesVector;
    if (m_isInputDirectory) {
        m_seriesFileNames->SetInputDirectory(m_inputDirectory.toStdString());
        fileNamesVector = m_seriesFileNames->GetInputFileNames();
    } else {
        for (const QString &qstr : m_fileNames) {
            fileNamesVector.push_back(qstr.toStdString());
        }
    }
    if (fileNamesVector.empty()) {
        return false;
    }
    m_reader->SetFileNames(fileNamesVector);
    try {
        m_reader->Update();
    } catch (itk::ExceptionObject &e) {
        std::cout << "ImageSeriesReader3D::update(): itk::Exception: " << std::endl;
        std::cout << e.what() << std::endl;
        return false;
    }
    outputImage->setStorage(m_reader->GetOutput());
    emit this->outputChanged();
    return true;
}

fiQuickItkImage3D.h文件:

#ifndef _QUICK_ITK_fiIMAGE3D_H_
#define _QUICK_ITK_fiIMAGE3D_H_

#include "fiVisionGlobal.h"
#include <QObject>
#include <QtQml/qqml.h>

using SSPixelType = signed short;
const unsigned short Image3DDimention = 3;
using Image3DType = itk::Image<SSPixelType, Image3DDimention>;

class QuickItkImage3D : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(QuickItkImage3D);

    QML_ELEMENT
    QML_UNCREATABLE("QuickItkImage3D is uncreatable.")

  private:
    Image3DType::Pointer m_storage{nullptr};

  protected:
    QuickItkImage3D(QObject *parent = nullptr)
        : QuickItkImage3D(Image3DType::New(), parent) {}

  public:
    QuickItkImage3D(Image3DType *object, QObject *parent = nullptr)
        : QObject(parent) { m_storage = object; }
    ~QuickItkImage3D() {}
    void setStorage(Image3DType *storage) { m_storage = storage; }
    Image3DType *getStorage() { return this->m_storage; }

    QString getNameOfClass() { return QString(m_storage->GetNameOfClass()); }
};

#endif // !_QUICK_ITK_fiIMAGE2D_H_

fiItkProgressReporter.h文件:

#ifndef _FI_ITK_PROGRESS_REPORTER_H_
#define _FI_ITK_PROGRESS_REPORTER_H_

#include <QObject>
#include <itkCommand.h>

class ProgressCommand : public itk::CStyleCommand
{
  public:
    itkNewMacro(ProgressCommand);

    void Execute(itk::Object *caller, const itk::EventObject &event) override
    {
        Execute((const itk::Object *)caller, event);

    }
    void Execute(const itk::Object *caller, const itk::EventObject &event) override;
};

class ItkProgressReporter : public QObject
{
    Q_OBJECT
    Q_DISABLE_COPY(ItkProgressReporter);

  private:
    ProgressCommand::Pointer m_command;

  signals:
    void progressChanged(float);

  public:
    ItkProgressReporter(QObject *parent = nullptr) : QObject(parent)
    {
        m_command = ProgressCommand::New();
        m_command->SetClientData(this);
    }
    ~ItkProgressReporter() {}
    ProgressCommand::Pointer getCommand() { return m_command; }
};

#endif // !_FI_ITK_EVENT_DISPATCHER_H_

fiItkProgressReporter.cpp文件:

#include "fiItkProgressReporter.h"

void ProgressCommand::Execute(const itk::Object *caller, const itk::EventObject &event)
{
    if (!itk::ProgressEvent().CheckEvent(&event)) {
        return;
    }
    const auto *processObject = dynamic_cast<const itk::ProcessObject *>(caller);
    if (!processObject) {
        return;
    }
    ItkProgressReporter *reporter = (ItkProgressReporter *)m_ClientData;
    reporter->progressChanged(processObject->GetProgress());
    // std::cout << "Progress: " << processObject->GetProgress() << std::endl;
}

  • 13
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值