使用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();
}
  • 輸出

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章