赞
踩
记录之前写过一个读取DICOM文件,修改对应Tag标签内容后,保存为新的DICOM文件的例子;
其中的对DICOM信息处理的过程为,将DICOM中的一系列连续DICOM图像,处理后生成一张多帧的DICOM文件;
主要步骤为:
1.读取DICOM文件;
2.对DICOM文件中的信息处理,修改;
3.保存为新的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文件就很简单了。对应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; } };
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。