记录之前写过一个读取DICOM文件,修改对应Tag标签内容后,保存为新的DICOM文件的例子;
其中的对DICOM信息处理的过程为,将DICOM中的一系列连续DICOM图像,处理后生成一张多帧的DICOM文件;
主要步骤为:
1.读取DICOM文件;
2.对DICOM文件中的信息处理,修改;
3.保存为新的DICOM文件;
读取DICOM文件
读取DICOM文件的信息,使用一系列的findAndGetOFXXX方法;
可以根据DICOM标签中的VR标识来选择,DCMTK库中每个findAndGetOFXXX方法都有详细的注释;
读取DICOM文件的步骤为:
DcmFileFormat* nm_dcm_format = new DcmFileFormat;
OFCondition cond = nm_dcm_format->loadFile(nm_file.c_str());
// DICOM信息头
DcmMetaInfo* meta_info = nm_dcm_format->getMetaInfo();
// DICOM信息
DcmDataset* dataset = nm_dcm_format->getDataset();
OFString new_instance_id;
if (meta_info->findAndGetOFString(DCM_MediaStorageSOPInstanceUID, new_instance_id).good())
{
instance_id = new_instance_id.c_str();
}
保存DICOM文件
保存DICOM文件就很简单了。对应findAndGetOFXXX方法有putAndInsertXXX方法来设置DICOM标签对应的信息;
// 1.构建DICOM文件的信息;
DcmFileFormat* nm_dcm_format = new DcmFileFormat;
DcmDataset* dataset = nm_dcm_format->getDataset();
dataset->putAndInsertString(DCM_FrameOfReferenceUID, series_instance_id.c_str());
// 2.使用saveFile方法,保存DICOM文件;
nm_dcm_format->saveFile(mult_pet_file.c_str());
注意:DICOM标签内的数据长度必须是偶数,如果实际内容为奇数,就需要补足为偶数字节个数;
主要代码
std::string getTime()
{
time_t timep;
time(&timep);
struct tm* p = localtime(&timep);
char tmp[64] = { 0 };
sprintf(tmp, "%02d%02d%02d.0000 ", p->tm_hour, p->tm_min, p->tm_sec);
return tmp;
}
class Dicom_info
{
public:
Dicom_info(DcmFileFormat* nm_dcm_format)
:number_of_frames(0)
{
DcmMetaInfo* meta_info = nm_dcm_format->getMetaInfo();
DcmDataset* dataset = nm_dcm_format->getDataset();
// 0002,0003序列ID;
OFString new_instance_id;
if (meta_info->findAndGetOFString(DCM_MediaStorageSOPInstanceUID, new_instance_id).good())
{
instance_id = new_instance_id.c_str();
}
OFString of_series_instance_id;
if (dataset->findAndGetOFString(DCM_SeriesInstanceUID, of_series_instance_id).good())
{
series_instance_id = of_series_instance_id.c_str();
}
OFString of_frame_referenceUID;
if (dataset->findAndGetOFString(DCM_FrameOfReferenceUID, of_frame_referenceUID).good())
{
frame_referenceUID = of_frame_referenceUID.c_str();
}
const char* pbuf = NULL;
if (dataset->findAndGetString(DCM_PixelSpacing, pbuf).good())
{
pixel_spacings = Tool::DealString::SplitString(pbuf, "\\");
}
if (!dataset->findAndGetUint16(DCM_Rows, rows).good())
{
rows = 0;
}
if (!dataset->findAndGetUint16(DCM_Columns, columns).good())
{
columns = 0;
}
const Uint16* pix_inbuf = nullptr;
unsigned long size = 0;
if (dataset->findAndGetUint16Array(DCM_PixelData, pix_inbuf, &size).good())
{
pix_buf = new float[size];
for (size_t i = 0; i < size; i++)
{
pix_buf[i] = pix_inbuf[i];
}
}
OFString numberOfFrames;
if (dataset->findAndGetOFString(DCM_NumberOfFrames, numberOfFrames).good())
{
number_of_frames = atoi(numberOfFrames.c_str());
}
}
~Dicom_info()
{
if (pix_buf != NULL)
{
delete[]pix_buf;
}
}
int ChangeDcmFileFormat(DcmFileFormat* nm_dcm_format, float* pinbuf, int rows, int columns, int num)
{
try
{
DcmMetaInfo* meta_info = nm_dcm_format->getMetaInfo();
DcmDataset* dataset = nm_dcm_format->getDataset();
instance_id += Superaddition;
CheckStringLengthIsEvenSize(instance_id);
meta_info->putAndInsertString(DCM_MediaStorageSOPInstanceUID, instance_id.c_str());
dataset->putAndInsertString(DCM_SOPInstanceUID, instance_id.c_str());
series_instance_id += Superaddition;
CheckStringLengthIsEvenSize(series_instance_id);
dataset->putAndInsertString(DCM_SeriesInstanceUID, series_instance_id.c_str());
frame_referenceUID += Superaddition;
CheckStringLengthIsEvenSize(frame_referenceUID);
dataset->putAndInsertString(DCM_FrameOfReferenceUID, series_instance_id.c_str());
std::string cur_time = getTime();
dataset->putAndInsertString(DCM_InstanceCreationTime, cur_time.c_str());
dataset->putAndInsertString(DCM_ImageType, "RECON TOMO");
//dataset->putAndInsertString(DCM_SeriesDescription, "MEMRS RECON RESULTS ");
std::string org_value = pixel_spacings.at(0);
std::string neg_value = "-" + org_value;
CheckStringLengthIsEvenSize(org_value);
CheckStringLengthIsEvenSize(neg_value);
dataset->putAndInsertString(DCM_SliceThickness, org_value.c_str());
dataset->putAndInsertString(DCM_SpacingBetweenSlices, neg_value.c_str());
dataset->putAndInsertString(DCM_AcquisitionTerminationCondition, "MANU");
std::string num_str = std::to_string(num);
CheckStringLengthIsEvenSize(num_str);
dataset->putAndInsertString(DCM_NumberOfFrames, num_str.c_str());
dataset->putAndInsertUint16(DCM_NumberOfSlices, num);
dataset->putAndInsertUint16(DCM_Rows, rows);
dataset->putAndInsertUint16(DCM_Columns, columns);
dataset->putAndInsertString(DCM_CorrectedImage, "ATTN");
std::string image_orientation = "1.000000\\0.000000\\0.000000\\0.000000\\1.000000\\0.000000";
CheckStringLengthIsEvenSize(image_orientation);
DcmItem* sq_item;
if (dataset->findAndGetSequenceItem(DCM_DetectorInformationSequence, sq_item).good())
{
sq_item->putAndInsertString(DCM_ImageOrientationPatient, image_orientation.c_str());
}
dataset->putAndInsertString(DCM_ImageID, "TOMO_IRAC ");
DcmElement* element;
int out_size = rows * columns * num;
Uint16* pix_buf = new Uint16[out_size];
int max_pixel = INT_MIN;
int min_pixel = INT_MAX;
for (size_t i = 0; i < out_size; i++)
{
pix_buf[i] = pinbuf[i] * 1000;
if (max_pixel < pix_buf[i])
{
max_pixel = pix_buf[i];
}
if (min_pixel > pix_buf[i])
{
min_pixel = pix_buf[i];
}
}
dataset->putAndInsertUint16(DCM_SmallestImagePixelValue, min_pixel);
dataset->putAndInsertUint16(DCM_LargestImagePixelValue, max_pixel);
std::pair<double, double> center_width = CalculateWindowCenterAndWidth(max_pixel, min_pixel);
std::string window_center_str = std::to_string(center_width.first);
std::string window_width_str = std::to_string(center_width.second);
CheckStringLengthIsEvenSize(window_center_str);
CheckStringLengthIsEvenSize(window_width_str);
//DCM_WindowCenter DCM_WindowWidth
dataset->putAndInsertString(DCM_WindowCenter, window_center_str.c_str());
dataset->putAndInsertString(DCM_WindowWidth, window_width_str.c_str());
Uint16* deal_buf = new Uint16[out_size];
int size_one_image = num * columns;
int size_org = rows * columns;
// 一共多少张横断图
for (size_t i = 0; i < columns; i++)
{
Uint16* one_image = deal_buf + i * size_one_image;
// 对每张图的行数据进行拼接;
for (size_t j = 0; j < rows; j++)
{
memcpy(one_image + (num - 1 - j) * num, pix_buf + j * size_org + i * rows, columns * sizeof(Uint16));
}
}
if (dataset->findAndGetElement(DCM_PixelData, element).good())
{
element->putUint16Array(deal_buf, out_size);
}
delete[] deal_buf;
delete[] pix_buf;
return 0;
}
catch (...)
{
return 1;
}
}
private:
void CheckStringLengthIsEvenSize(std::string content)
{
if (content.size() / 2 != 0)
{
content += " ";
}
}
std::pair<double, double> CalculateWindowCenterAndWidth(int max_pixel, int min_pixel)
{
double windowWidth = max_pixel - min_pixel;
double windowCenter = min_pixel + windowWidth / 2.0;
return std::make_pair(windowCenter, windowWidth);
}
public:
std::string instance_id;
std::string series_instance_id;
std::string frame_referenceUID;
std::vector<std::string> pixel_spacings;
float* pix_buf;
Uint16 rows;
Uint16 columns;
double window_center;
double window_width;
Uint16 number_of_frames;
};
class ReconDicom
{
public:
static int GenerateMultPET(std::string nm_file, std::string mult_pet_file)
{
ODI("GenerateMultPET begin");
if (nm_file.empty() || mult_pet_file.empty())
{
ODI("文件名为空");
return 1;
}
DcmFileFormat* nm_dcm_format = new DcmFileFormat;
OFCondition cond = nm_dcm_format->loadFile(nm_file.c_str());
if (cond.good())
{
Dicom_info decode_dicom(nm_dcm_format);
DcmRawData data_out = { 0 };
DcmRawData raw_data;
ret = decode_dicom.ChangeDcmFileFormat(nm_dcm_format, data_out._img_data, data_out._size_x, data_out._size_y, data_out._series_num);
if (ret == 1)
{
return -1;
}
cond = nm_dcm_format->saveFile(mult_pet_file.c_str());
if (cond.bad())
{
return -1;
}
}
return 0;
}
};