使用DCMTK实现DICOM文件的读写

使用DCMTK实现DICOM文件的读写

 

 

创建Dicom_Module的Win32 DLL工程并设置

  • 创建名为Dicom_Module的Win32 DLL工程,并配置include目录和lib目录(dcmtk和boost)

  • 配置依赖库

  • 将工程的字符集改为多字节字符集

  • 为了方便,将Dicom_Module的输出目录设置到自定义的\Learning\DICOM\Module_Sample目录下


创建Dicom_Sample的Win32 Console工程并设置

  • 创建名为Dicom_Sample的Win32 Console工程,并配置include目录和lib目录,用来使用Dicom_Module

  • 为了方便,将Dicom_Sample的输出目录也设置到自定义的E:\Learning\DICOM\Module_Sample目录下

  • 将DCMTK库bin目录下的dcmdata.dll, oflog.dll, ofstd.dll拷贝到E:\Learning\DICOM\Module_Sample目录下,同时拷贝示例Dicom文(brain_005)E:\Learning\DICOM\Module_Sample目录下

 

Dicom_Module工程里编写所需类和函数

DicomInfo定义DicomModule所需数据结构,常量,枚举等

  • 公有数据结构Patient,Study,Series,Image
  • 私有数据结构PrivateData
  • 常量和枚举

Patient信息

struct Patient
{
    std::string                             PatientsName;
    std::string                             PatientID;
    std::chrono::system_clock::time_point   PatientsBirthDate;
    std::string                             PatientsSex;
    std::string                             OtherPatientNames;
    std::string                             PatientsWeight;
    std::string                             PatientComments;
};

Study信息

struct Study
{
    std::string                             StudyInstanceUID;
    std::string                             StudyID;
    std::chrono::system_clock::time_point   StudyDateTime;
    std::string                             AccessionNumber;
    std::string                             StudyDescription;
};

Series信息

struct Series
{
    std::string                             SeriesInstanceUID;
    std::string                             SeriesNumber;
    std::chrono::system_clock::time_point   SeriesDateTime;
    std::string                             PerformingPhysicianName;
    std::string                             ProtocolName;
    std::string                             BodyPartExamined;
};

Image信息

struct Image
{

    unsigned int                            SamplesPerPixel;
    std::string                             PhotometricInterpretation;
    unsigned int                            Rows; 
    unsigned int                            Columns; 
    unsigned int                            BitsAllocated;
    unsigned int                            BitsStored;
    unsigned int                            HighBit;
    unsigned int                            PixelRepresentation;
    unsigned short*                         PixelData;
};

Private信息

struct PrivateData
{
    uint16_t                                GroupTag;
    uint16_t                                ElementTag;
    std::string                             Name;
    std::string                             VR;
    std::string                             Value;
};

一些常量和枚举

#define PRIVATE_GROUP_NAME "Private Group"

enum UID_Type 
{
    UID_Study = 1,
    UID_Series = 2,
    UID_Image = 3,
    UID_Other = 4,
};

enum DataVR_Type
{
    DataVRType_CS = 1,
    DataVRType_SH,
    DataVRType_LO,
    DataVRType_ST,
    DataVRType_LT,
    DataVRType_UT,
    DataVRType_AE,
    DataVRType_PN,
    DataVRType_UI,
    DataVRType_DA,
    DataVRType_TM,
    DataVRType_DT,
    DataVRType_AS,
    DataVRType_IS,
    DataVRType_DS,
    DataVRType_SS,
    DataVRType_US,
    DataVRType_SL,
    DataVRType_UL,
    DataVRType_AT,
    DataVRType_FL,
    DataVRType_FD,
    DataVRType_OB,
    DataVRType_OW,
    DataVRType_OF,
    DataVRType_SQ,
    DataVRType_UN
};

接口类DicomIF,导出外部可调用的接口类和成员函数

class __declspec(dllexport) IDicom
{
public:
    virtual ~IDicom() {}
    virtual bool Read(std::string filePath) = 0;
    virtual bool Save(std::string filePath) = 0;
    virtual void Reset() = 0;

    virtual void MetaInfoTags() = 0;
    virtual void DataSetTags() = 0;

    virtual std::shared_ptr<Patient>    PatientInfo() = 0;
    virtual std::shared_ptr<Study>      StudyInfo() = 0;
    virtual std::shared_ptr<Series>     SeriesInfo() = 0;
    virtual std::shared_ptr<Image>      ImageInfo() = 0;
    virtual std::list<PrivateData>&     PrivateDataInfo() = 0;
};

实现类DicomProcessor,实现Dicom文件的读写功能

class __declspec(dllexport) DicomProcessor : public IDicom
{
public:
    DicomProcessor();
    ~DicomProcessor();

    bool Read(std::string filePath);
    bool Save(std::string filePath);
    void Reset();

    inline std::shared_ptr<Patient> PatientInfo()
    {
        return m_patient;
    }

    inline std::shared_ptr<Study> StudyInfo()
    {
        return m_study;
    }

    inline std::shared_ptr<Series> SeriesInfo()
    {
        return m_series;
    }

    inline std::shared_ptr<Image> ImageInfo()
    {
        return m_image;
    }

    inline std::list<PrivateData> &PrivateDataInfo()
    {
        return m_privateData;
    }

    void MetaInfoTags();
    void DataSetTags();
private:
    void GetPatient();
    void GetStudy();
    void GetSeries();
    void GetImage();
    void GetPrivateData();

    void FillPatient();
    void FillStudy();
    void FillSeries();
    void FillImage();
    void FillPrivateData();

    void GetInfo(DcmTagKey tagKey, std::string &info);
    void GetInfo(DcmTagKey tagKey, unsigned int &info);
    void GetDateTimeInfo(DcmTagKey tagKey, DcmTagKey tagTimeKey, std::chrono::system_clock::time_point &info);
    void FillInfo(DcmTagKey tagKey, std::string &info);
    void RegisterPrivateTags(PrivateData data);
    void FreePixelData();

    std::string                 m_fileName;
    DcmFileFormat               m_fileformat;
    std::shared_ptr<Patient>    m_patient;
    std::shared_ptr<Study>      m_study;
    std::shared_ptr<Series>     m_series;
    std::shared_ptr<Image>      m_image;
    std::list<PrivateData>      m_privateData;
};
#include "DicomProcessor.h"
#include "DicomUtils.h"

DicomProcessor::DicomProcessor()
{
    Reset();
}


DicomProcessor::~DicomProcessor()
{
    FreePixelData();
}

void DicomProcessor::Reset()
{
    m_patient.reset(new Patient());
    m_study.reset(new Study());
    m_series.reset(new Series());
    m_image.reset(new Image());
    m_privateData.clear();
    FreePixelData();
}

bool DicomProcessor::Read(std::string filePath)
{
    OFCondition status = m_fileformat.loadFile(filePath.c_str());
    if (!status.good())
    {
        std::cout << "Read dimcom file error:" << status.text() << ",file: " << filePath << std::endl;
        return false;
    }
    try
    {
        GetPatient();
        GetStudy();
        GetSeries();
        GetImage();
        GetPrivateData();
    }
    catch (...)
    {
        std::cout << "Get dimcom info error!" << std::endl;
        FreePixelData();
        return false;
    }
    std::cout << "Read dimcom file succeed!" << std::endl;
    return true;
}

bool DicomProcessor::Save(std::string filePath)
{
    try
    {
        FillPatient();
        FillStudy();
        FillSeries();
        FillImage();
        FillPrivateData();
    }
    catch (const std::exception&)
    {
        std::cout << "Fill dimcom info error!" << std::endl;
        return false;
    }

    OFCondition status = m_fileformat.saveFile(filePath.c_str());
    if (!status.good())
    {
        std::cout << "Save dimcom file error, file: " << filePath << std::endl;
        return false;
    }

    std::cout << "Save dimcom file succeed!" << std::endl;
    return true;
}

void DicomProcessor::MetaInfoTags()
{
    std::cout << "Meta Tag-----------------------------Meta Tag" << std::endl;
    DcmObject* item = m_fileformat.getMetaInfo()->nextInContainer(NULL);
    while (item)
    {
        DcmVR valueVR(item->getVR());
        DcmTag tag(item->getTag());

        std::cout << item->getTag().toString().c_str() << "    " << valueVR.getVRName() << "    " << tag.getTagName() << std::endl;
        item = m_fileformat.getMetaInfo()->nextInContainer(item);
    }
}

void DicomProcessor::DataSetTags()
{
    std::cout << "DataSet Tag------------------------------DataSet Tag" << std::endl;
    DcmObject* item = m_fileformat.getDataset()->nextInContainer(NULL);
    while (item)
    {
        DcmVR valueVR(item->getVR());
        DcmTag tag(item->getTag());
        std::cout << item->getTag().toString().c_str() << "    " << valueVR.getVRName() << "    " << tag.getTagName() << std::endl;
        item = m_fileformat.getDataset()->nextInContainer(item);
    }
}


void DicomProcessor::GetPatient()
{
    GetInfo(DCM_PatientID, m_patient->PatientID);
    GetInfo(DCM_PatientName, m_patient->PatientsName);
}

void DicomProcessor::GetStudy()
{
    GetInfo(DCM_StudyInstanceUID, m_study->StudyInstanceUID);
    if (m_study->StudyInstanceUID.empty())
    {
        std::cout << "Get Tag DCM_StudyInstanceUID Error!" << std::endl;
    }
}

void DicomProcessor::GetSeries()
{
    GetDateTimeInfo(DCM_SeriesDate, DCM_SeriesTime, m_series->SeriesDateTime);
    GetInfo(DCM_BodyPartExamined, m_series->BodyPartExamined);
}

void DicomProcessor::GetImage()
{
    GetInfo(DCM_SamplesPerPixel, m_image->SamplesPerPixel);
    GetInfo(DCM_PhotometricInterpretation, m_image->PhotometricInterpretation);
    GetInfo(DCM_Rows, m_image->Rows);
    GetInfo(DCM_Columns, m_image->Columns);
    GetInfo(DCM_BitsAllocated, m_image->BitsAllocated);
    GetInfo(DCM_BitsStored, m_image->BitsStored);
    GetInfo(DCM_HighBit, m_image->HighBit);
    GetInfo(DCM_PixelRepresentation, m_image->PixelRepresentation);

    uint pixelByteCount = (m_image->BitsAllocated <= 8) ? 1 : 2;
    ulong dataLength = m_image->Rows * m_image->Columns * pixelByteCount * m_image->SamplesPerPixel;

    DcmElement* element = NULL;
    OFCondition status = m_fileformat.getDataset()->findAndGetElement(DCM_PixelData, element);
    if (!status.good())
    {
        std::cout<< "Get pixel data element error:" << status.text() << std::endl;
    }
    else
    {
        //std::cout << "Pixel data element's length:" << element->getLength() << std::endl;
        
        unsigned short* pData = NULL;
        status = element->getUint16Array(pData);
        if (!status.good())
        {
            std::cout << "Get pixel data array error:" << status.text() << std::endl;
            return;
        }
        FreePixelData();
        m_image->PixelData = (uint16_t *)malloc(dataLength);
        memcpy(m_image->PixelData, pData, dataLength);
    }
}

void DicomProcessor::GetPrivateData()
{
    DcmTagKey tagKey;
    OFString value;
    DcmElement* element = NULL;

    for (auto &data : m_privateData)
    {
        tagKey.set((Uint16)data.GroupTag, (Uint16)data.ElementTag);
        value.clear();
        element = NULL;
        OFCondition status = m_fileformat.getDataset()->findAndGetElement(tagKey, element);

        if (!status.good())
        {
            std::ios::fmtflags fmt(std::cout.flags());
            std::cout << "Get private Tag(" << std::hex << data.GroupTag << "," << data.ElementTag << ") error:" << status.text() << std::endl;
            std::cout.flags(fmt);
        }
        else
        {
            status = element->getOFString(value, 0);
            if (!status.good())
            {
                std::ios::fmtflags fmt(std::cout.flags());
                std::cout << "Get private Tag(" << std::hex << data.GroupTag << "," << data.ElementTag << ") error:" << status.text() << std::endl;
                std::cout.flags(fmt);
            }
            else
            {
                data.Value = std::string(value.c_str());
                DcmVR valueVR(element->getVR());
                data.VR = std::string(valueVR.getVRName());
            }
        }
    }
}


void DicomProcessor::FillPatient()
{
    FillInfo(DCM_PatientID, m_patient->PatientID);
    FillInfo(DCM_PatientName, m_patient->PatientsName);
}

void DicomProcessor::FillStudy()
{
    if (m_study->StudyInstanceUID.empty())
    {
        std::cout << "Study instanceUID is empty!" << std::endl;
    }

    FillInfo(DCM_StudyInstanceUID, m_study->StudyInstanceUID);
}

void DicomProcessor::FillSeries()
{
    FillInfo(DCM_SeriesDate, DatetoString(m_series->SeriesDateTime));
    FillInfo(DCM_SeriesTime, TimetoString(m_series->SeriesDateTime));
}

void DicomProcessor::FillImage()
{
    FillInfo(DCM_SamplesPerPixel, std::to_string(m_image->SamplesPerPixel));
    if (m_image->PhotometricInterpretation.empty())
    {
        std::cout << "No PhotometricInterpretation Info in Fill image Pixel" << std::endl;
    }

    FillInfo(DCM_PhotometricInterpretation, m_image->PhotometricInterpretation);
    FillInfo(DCM_Rows, std::to_string(m_image->Rows));
    FillInfo(DCM_Columns, std::to_string(m_image->Columns));
    FillInfo(DCM_BitsAllocated, std::to_string(m_image->BitsAllocated));
    FillInfo(DCM_BitsStored, std::to_string(m_image->BitsStored));
    FillInfo(DCM_HighBit, std::to_string(m_image->HighBit));
    FillInfo(DCM_PixelRepresentation, std::to_string(m_image->PixelRepresentation));

    uint pixelByteCount = (m_image->BitsAllocated <= 8) ? 1 : 2;
    ulong dataLength = m_image->Rows * m_image->Columns * pixelByteCount * m_image->SamplesPerPixel;

    if (m_image->PixelData != NULL)
    {
        OFCondition status = m_fileformat.getDataset()->putAndInsertUint16Array(DCM_PixelData, m_image->PixelData, dataLength / pixelByteCount);
        if (!status.good())
        {
            std::cout << "Fill pixel data error:" << status.text() << std::endl;
        }
    }
}

void DicomProcessor::FillPrivateData()
{
    for (auto &data : m_privateData)
    {
        if (!data.VR.empty())
        {
            RegisterPrivateTags(data);
            OFCondition status = m_fileformat.getDataset()->putAndInsertString(DcmTag((Uint16)data.GroupTag, (Uint16)data.ElementTag, PRIVATE_GROUP_NAME), data.Value.c_str());
            if (!status.good())
            {
                std::cout << "Fill private Tag(" << data.GroupTag << "," << data.ElementTag << ") error:" << status.text();
            }
        }
    }
    m_privateData.clear();
}

void DicomProcessor::RegisterPrivateTags(PrivateData data)
{
    DcmDataDictionary &dict = dcmDataDict.wrlock();

    if ((data.VR.compare("OB") == 0) || (data.VR.compare("FL") == 0) || (data.VR.compare("FD") == 0))
    {
        dict.addEntry(new DcmDictEntry((Uint16)data.GroupTag, (Uint16)data.ElementTag, EVR_UT, data.Name.c_str(), 1, 1, "private", OFTrue, PRIVATE_GROUP_NAME));
    }
    else
    {
        dict.addEntry(new DcmDictEntry((Uint16)data.GroupTag, (Uint16)data.ElementTag, DcmVR(data.VR.c_str()), data.Name.c_str(), 1, 1, "private", OFTrue, PRIVATE_GROUP_NAME));
    }
    dcmDataDict.wrunlock();
}


void DicomProcessor::GetInfo(DcmTagKey tagKey, std::string &info)
{
    OFString ofData;
    OFCondition status = m_fileformat.getDataset()->findAndGetOFString(tagKey, ofData);
    if (!status.good())
    {
        std::cout << "Get Tag(" << tagKey.toString().c_str() << ") error: " << status.text() << std::endl;
    }
    info = ofData.c_str();
}

void DicomProcessor::GetInfo(DcmTagKey tagKey, unsigned int &info)
{
    std::string strInfo;
    GetInfo(tagKey, strInfo);
    try
    {
        info = std::atoi(strInfo.c_str());
    }
    catch (...)
    {
        std::cout << "Get Tag(" << tagKey.toString().c_str() << ") error(String to UInt)!" << std::endl;
    }
}

void DicomProcessor::GetDateTimeInfo(DcmTagKey tagDateKey, DcmTagKey tagTimeKey, std::chrono::system_clock::time_point &info)
{
    std::string date;
    std::string time;
    GetInfo(tagDateKey, date);
    GetInfo(tagTimeKey, time);
    info = StringtoDateTime(date, time);
}

void DicomProcessor::FillInfo(DcmTagKey tagKey, std::string &info)
{
    OFCondition status = m_fileformat.getDataset()->putAndInsertString(tagKey, info.c_str());
    if (!status.good())
    {
        std::cout << "Fill Tag(" << tagKey.toString().c_str() << ") error: " << status.text() << std::endl;
    }
}

void DicomProcessor::FreePixelData()
{
    if (m_image->PixelData != NULL)
    {
        //std::cout << "Free pixel data of Dicom Handler" << std::endl;
        free(m_image->PixelData);
        m_image->PixelData = NULL;
    }
}

DicomFactory:实现DicomIF对象指针的管理

static IDicom* s_dicom = nullptr;

__declspec(dllexport) void CreateDicomProcessor();

__declspec(dllexport) IDicom* GetDicomProcessor();

__declspec(dllexport) void DeleteDicomProcessor();

#include "DicomFactory.h"
#include "DicomProcessor.h"
#include <iostream>

void CreateDicomProcessor()
{
    if (nullptr == s_dicom)
    {
        s_dicom = new DicomProcessor();
    }
}

IDicom* GetDicomProcessor()
{
    return s_dicom;
}

void DeleteDicomProcessor()
{
    if (nullptr != s_dicom)
    {
        delete s_dicom;
        s_dicom = nullptr;
    }
}

DicomUtils:实现一些基本函数的功能

#include "DicomInfo.h"

__declspec(dllexport) std::string GenerateUniqueId(UID_Type type);
__declspec(dllexport) std::string GetDataVR(DataVR_Type type);
__declspec(dllexport) std::chrono::system_clock::time_point StringtoDate(std::string date);
__declspec(dllexport) std::chrono::system_clock::time_point StringtoDateTime(std::string date, std::string time);
__declspec(dllexport) std::string DatetoString(std::chrono::system_clock::time_point timePoint);
__declspec(dllexport) std::string TimetoString(std::chrono::system_clock::time_point timePoint);
#include "DicomUtils.h"
#include <boost/format.hpp>
#include "dcmtk/dcmdata/dcuid.h"

std::string GenerateUniqueId(UID_Type type)
{
    char uid[100];
    switch (type)
    {
    case UID_Study:
        dcmGenerateUniqueIdentifier(uid, SITE_STUDY_UID_ROOT);
        break;
    case UID_Series:
        dcmGenerateUniqueIdentifier(uid, SITE_SERIES_UID_ROOT);
        break;
    case UID_Image:
        dcmGenerateUniqueIdentifier(uid, SITE_INSTANCE_UID_ROOT);
        break;
    default:
        std::cout << "The type of generate unique id that is not supported! type :" << type << std::endl;
        break;
    }
    return std::string(uid);
}

std::string GetDataVR(DataVR_Type type)
{
    std::string vr = "";
    switch (type)
    {
    case DataVRType_CS:
        vr = "CS";
        break;
    case DataVRType_SH:
        vr = "SH";
        break;
    case DataVRType_LO:
        vr = "LO";
        break;
    case DataVRType_ST:
        vr = "ST";
        break;
    case DataVRType_LT:
        vr = "LT";
        break;
    case DataVRType_UT:
        vr = "UT";
        break;
    case DataVRType_AE:
        vr = "AE";
        break;
    case DataVRType_PN:
        vr = "PN";
        break;
    case DataVRType_UI:
        vr = "UI";
        break;
    case DataVRType_DA:
        vr = "DA";
        break;
    case DataVRType_TM:
        vr = "TM";
        break;
    case DataVRType_DT:
        vr = "DT";
        break;
    case DataVRType_AS:
        vr = "AS";
        break;
    case DataVRType_IS:
        vr = "IS";
        break;
    case DataVRType_DS:
        vr = "DS";
        break;
    case DataVRType_SS:
        vr = "SS";
        break;
    case DataVRType_US:
        vr = "US";
        break;
    case DataVRType_SL:
        vr = "SL";
        break;
    case DataVRType_UL:
        vr = "UL";
        break;
    case DataVRType_AT:
        vr = "AT";
        break;
    case DataVRType_FL:
        vr = "FL";
        break;
    case DataVRType_FD:
        vr = "FD";
        break;
    case DataVRType_OB:
        vr = "OB";
        break;
    case DataVRType_OW:
        vr = "OW";
        break;
    case DataVRType_OF:
        vr = "OF";
        break;
    case DataVRType_SQ:
        vr = "SQ";
        break;
    case DataVRType_UN:
        vr = "UN";
        break;
    default:
        break;
    }
    return vr;
}


std::chrono::system_clock::time_point StringtoDate(std::string date)
{
    std::string year = date.substr(0, 4);
    std::string month = date.substr(4, 2);
    std::string day = date.substr(6, 2);
    tm tmObj;
    tmObj.tm_year = std::atoi(year.c_str()) - 1900;
    tmObj.tm_mon = std::atoi(month.c_str()) - 1;
    tmObj.tm_mday = std::atoi(day.c_str());
    tmObj.tm_hour = std::atoi("0");
    tmObj.tm_min = std::atoi("0");
    tmObj.tm_sec = std::atoi("0");

    //std::time_t tt = mktime(&tmObj);
    return std::chrono::system_clock::from_time_t(mktime(&tmObj));
}

std::chrono::system_clock::time_point StringtoDateTime(std::string date, std::string time)
{
    std::string year = date.substr(0, 4);
    std::string month = date.substr(4, 2);
    std::string day = date.substr(6, 2);
    std::string hour = time.substr(0, 2);
    std::string min = time.substr(2, 2);
    std::string sec = time.substr(4, 2);

    tm tmObj;
    tmObj.tm_year = std::atoi(year.c_str()) - 1900;
    tmObj.tm_mon = std::atoi(month.c_str()) - 1;
    tmObj.tm_mday = std::atoi(day.c_str());
    tmObj.tm_hour = std::atoi(hour.c_str());
    tmObj.tm_min = std::atoi(min.c_str());
    tmObj.tm_sec = std::atoi(sec.c_str());

    size_t pos = time.find(".", 0);
    if (pos == std::string::npos)
    {
        return std::chrono::system_clock::from_time_t(std::mktime(&tmObj));
    }
    else
    {
        std::string strMS = time.substr(pos + 1, time.length() - pos - 1);
        long long ms = std::atoll(strMS.c_str());
        return (std::chrono::system_clock::from_time_t(std::mktime(&tmObj)) + std::chrono::microseconds(ms));
    }   
}


std::string DatetoString(std::chrono::system_clock::time_point timePoint)
{
    std::time_t tt = std::chrono::system_clock::to_time_t(timePoint);
    std::stringstream ss;
    ss << std::put_time(std::localtime(&tt), "%Y%m%d");
    return ss.str();
}

std::string TimetoString(std::chrono::system_clock::time_point timePoint)
{
    std::time_t tt = std::chrono::system_clock::to_time_t(timePoint);

    auto ms = timePoint.time_since_epoch();
    auto diff = std::chrono::duration_cast<std::chrono::microseconds>(ms).count();
    auto const mcsecs = diff % 1000000;

    std::stringstream ss;
    if (mcsecs == 0)
    {
        ss << std::put_time(std::localtime(&tt), "%H%M%S");
    }
    else
    {
        ss << std::put_time(std::localtime(&tt), "%H%M%S") << "." << mcsecs;
    }
    return ss.str();
}

Dicom_Sample工程里编写Dicom文件读写的功能

编写RecomDicom函数,读取Dicom文件

  • 首先在main函数开始时,调用CreateDicomProcessor函数生成Dicom文件处理对象
  • 在main函数结束时,调用DeleteDicomProcessor函数释放Dicom文件处理对象
  • 使用GetDicomProcessor函数就可以得到Dicom文件处理对象
// C++_Dicom_Sample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include "DicomIF.h"
#include "DicomFactory.h"
#include "DicomUtils.h"
#include <iostream>

void ReadDicom(std::string file)
{
    if (!GetDicomProcessor()->Read(file))
    {
        std::cout << "Read Dicom File Failed: " << file.c_str() << std::endl;
        return;
    }

    std::cout << "Patient Name:" << GetDicomProcessor()->PatientInfo()->PatientsName << std::endl;
    std::cout << "Patient ID:" << GetDicomProcessor()->PatientInfo()->PatientID << std::endl;

}

int main()
{
    CreateDicomProcessor();

    std::string readFile = "E:\\Learning\\DICOM\\Module_Sample\\brain_005.dcm";

    ReadDicom(readFile);

    DeleteDicomProcessor();
    return 0;
}
  • 如果此Dicom文件没有某Tag信息,则提示出该Tag

  • 也有Tag存在,但值没空的情况

     

  • 在Pydicom里打开该Dicom文件,查看信息发现确实如上述情况

     

  • 读取更多信息
// C++_Dicom_Sample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include "DicomIF.h"
#include "DicomFactory.h"
#include "DicomUtils.h"
#include <iostream>

void ReadDicom(std::string file)
{
    if (!GetDicomProcessor()->Read(file))
    {
        std::cout << "Read Dicom File Failed: " << file.c_str() << std::endl;
        return;
    }

    std::cout << "Patient Name:" << GetDicomProcessor()->PatientInfo()->PatientsName << std::endl;
    std::cout << "Patient ID:" << GetDicomProcessor()->PatientInfo()->PatientID << std::endl;

    std::cout << "Study Instance UID:" << GetDicomProcessor()->StudyInfo()->StudyInstanceUID << std::endl;

    std::cout << "Serial Date:" << DatetoString(GetDicomProcessor()->SeriesInfo()->SeriesDateTime) << std::endl;
    std::cout << "Serial Time:" << TimetoString(GetDicomProcessor()->SeriesInfo()->SeriesDateTime) << std::endl;
    std::cout << "Body Part Examined:" << GetDicomProcessor()->SeriesInfo()->BodyPartExamined << std::endl;

    std::cout << "Rows:" << GetDicomProcessor()->ImageInfo()->Rows << std::endl;
    std::cout << "Columns:" << GetDicomProcessor()->ImageInfo()->Columns << std::endl;
}

int main()
{
    CreateDicomProcessor();

    std::string readFile = "E:\\Learning\\DICOM\\Module_Sample\\brain_005.dcm";

    ReadDicom(readFile);

    DeleteDicomProcessor();
    return 0;
}

编写WriteDicom函数,存储Dicom文件

  • 先读取名为brain_005的Dicom文件,改写其中的某些数据,再另存为名为brain_005_New的Dicom文件
void SaveDicom(std::string file)
{
    GetDicomProcessor()->PatientInfo()->PatientID = "88888";
    GetDicomProcessor()->StudyInfo()->StudyInstanceUID = GenerateUniqueId(UID_Study);
    GetDicomProcessor()->SeriesInfo()->SeriesDateTime = std::chrono::system_clock::now();


    if (!GetDicomProcessor()->Save(file))
    {
        std::cout << "Save Dicom File Failed: " << file.c_str() << std::endl;
        return;
    }
}

int main()
{
    CreateDicomProcessor();

    std::string readFile = "E:\\Learning\\DICOM\\Module_Sample\\brain_005.dcm";
    std::string saveFile = "E:\\Learning\\DICOM\\Module_Sample\\brain_005_New.dcm";

    ReadDicom(readFile);
    SaveDicom(saveFile);

    DeleteDicomProcessor();
    return 0;
}
  • 输出保存成功

  • 在Pydicom里打开Dicom文件(brain_005_New),发现修改的值保存成功

 

 

读取和存储Private数据

  • 存取私有字段(0x4001,0x1000),私有字段的Group必须是奇数
// C++_Dicom_Sample.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include "DicomIF.h"
#include "DicomFactory.h"
#include "DicomUtils.h"
#include <iostream>

void ReadDicom(std::string file)
{
    PrivateData data;
    data.GroupTag = 0x4001;
    data.ElementTag = 0x1000;
    GetDicomProcessor()->PrivateDataInfo().push_back(data);

    if (!GetDicomProcessor()->Read(file))
    {
        std::cout << "Read Dicom File Failed: " << file.c_str() << std::endl;
        return;
    }

    for (auto &item : GetDicomProcessor()->PrivateDataInfo())
    {
        std::ios::fmtflags fmt(std::cout.flags());
        std::cout << "Private Data:(0x" << std::hex << item.GroupTag << ",0x" << item.ElementTag << ")" << item.Value << std::endl;
        std::cout.flags(fmt);
    }
}

void SaveDicom(std::string file)
{
    PrivateData data;
    data.GroupTag = 0x4001;
    data.ElementTag = 0x1000;
    data.Name = "private1";
    data.VR = GetDataVR(DataVRType_UT);
    data.Value = "Private Test...";
    GetDicomProcessor()->PrivateDataInfo().push_back(data);

    if (!GetDicomProcessor()->Save(file))
    {
        std::cout << "Save Dicom File Failed: " << file.c_str() << std::endl;
        return;
    }
}

int main()
{
    CreateDicomProcessor();

    //std::string readFile = "E:\\Learning\\DICOM\\Module_Sample\\brain_005.dcm";
    std::string saveFile = "E:\\Learning\\DICOM\\Module_Sample\\brain_005_New.dcm";

    //ReadDicom(readFile);
    ReadDicom(saveFile);
    SaveDicom(saveFile);

    DeleteDicomProcessor();
    return 0;
}
  • 第二次运行时可显示Private数据已写入
  • 在Pydicom里打开Dicom文件(brain_005_New),发现Private数据存在

  • 编写ReadDicomTags函数,显示所有数据元素的Tag

void ReadDicomTags(std::string file)
{
    if (!GetDicomProcessor()->Read(file))
    {
        std::cout << "Read Dicom File Failed: " << file.c_str() << std::endl;
        return;
    }

    GetDicomProcessor()->MetaInfoTags();
    GetDicomProcessor()->DataSetTags();
}
  • 输出

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
使用 DCMTK 库读取 DICOM 文件需要进行以下步骤: 1. 引入 DCMTK 库 在 QT 项目中引入 DCMTK 库,可以通过在项目文件中添加以下代码引入静态库: ``` LIBS += -L/path/to/dcmtk/lib -ldcmdata -loflog -lofstd -li2d -ldcmimage -lzlib -lpng -ltiff ``` 注意:需要将 /path/to/dcmtk/lib 替换为 DCMTK 库的安装路径。 2. 初始化 DCMTK 库 在 QT 代码中,需要先初始化 DCMTK 库,可以在 main 函数中添加以下代码: ``` #include <dcmtk/config/osconfig.h> #include <dcmtk/dcmdata/dctk.h> int main(int argc, char *argv[]) { // 初始化 DCMTK 库 DcmInitialize(argc, argv); ... } ``` 3. 读取 DICOM 文件 使用 DCMTK 库读取 DICOM 文件可以使用 DcmFileFormat 类,以下是一个示例代码: ``` #include <dcmtk/config/osconfig.h> #include <dcmtk/dcmdata/dctk.h> void readDICOM(const char* filename) { // 创建 DcmFileFormat 对象 DcmFileFormat fileformat; // 读取 DICOM 文件到 DcmFileFormat 对象中 OFCondition status = fileformat.loadFile(filename); if (!status.good()) { qDebug() << "Failed to read DICOM file"; return; } // 获取 DICOM 数据集 DcmDataset* dataset = fileformat.getDataset(); // 获取 DICOM 图像数据 Uint16 *pixelData; dataset->findAndGetUint16Array(DCM_PixelData, pixelData); // 获取 DICOM 图像大小 Uint16 rows, cols; dataset->findAndGetUint16(DCM_Rows, rows); dataset->findAndGetUint16(DCM_Columns, cols); } ``` 上述代码中,首先创建 DcmFileFormat 对象,然后使用 loadFile() 方法读取 DICOM 文件,如果读取成功,则可以使用 getDataset() 方法获取 DICOM 数据集,使用 findAndGetUint16Array() 方法获取图像数据,使用 findAndGetUint16() 方法获取图像大小。 注意:DCMTK使用 C++98 标准,因此需要在 QT 项目中添加 -std=c++98 编译选项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值