视频教程Part1:https://www.bilibili.com/video/av66260004
视频教程Part2:https://www.bilibili.com/video/av68004639
创建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();
}
-
输出