多帧Dcm图片的读取与拆分存储

1. 概述

在项目中为了保留对之前代码的兼容和加上新功能,需要将多帧的Dcm图像转换为单帧的Dcm图像,近期对其进行了实践。本篇文章中将实现的代码予以分享,不妥之处请见谅。多帧图像

2. 实现代码

之前有两篇博客分别讲了DCMTK在vs上配置过程和读取Dcm图的实现,感兴趣的读者可以看看之前的文章。
//************************************************************************
// 函数名称:    	LoadDcmDataSet
// 访问权限:    	public 
// 创建日期:		2016/11/28
// 创 建 人:		
// 函数说明:		加载Dcm图像返回它的DicomImage
// 函数参数: 	std::string file_path	文件的路径
// 返 回 值:   	DicomImage* $
//************************************************************************
DicomImage* CLoadMutiFrameDcm::LoadDcmDataSet(std::string file_path)
{
	this->filepath = file_path;

	DcmFileFormat fileformat;
	OFCondition oc = fileformat.loadFile(file_path.c_str());                    //读取Dicom图像  
	if (!oc.good())     //判断Dicom文件是否读取成功  
	{
		std::cout << "file Load error" << std::endl;
		return nullptr;
	}
	DcmDataset *dataset = fileformat.getDataset();                              //得到Dicom的数据集  
	E_TransferSyntax xfer = dataset->getOriginalXfer();                          //得到传输语法  

	OFString patientname;
	dataset->findAndGetOFString(DCM_PatientName, patientname);                   //获取病人姓名  

	unsigned short bit_count(0);
	dataset->findAndGetUint16(DCM_BitsStored, bit_count);                        //获取像素的位数 bit  

	OFString isRGB;
	dataset->findAndGetOFString(DCM_PhotometricInterpretation, isRGB);           //DCM图片的图像模式  

	unsigned short img_bits(0);
	dataset->findAndGetUint16(DCM_SamplesPerPixel, img_bits);                    //单个像素占用多少byte  
	this->Img_bitCount = (int)img_bits;

	OFString framecount;
	dataset->findAndGetOFString(DCM_NumberOfFrames, framecount);				//DCM图片的帧数  
	

	//DicomImage* img_xfer = new DicomImage(xfer, 0, 0, 1);                     //由传输语法得到图像的帧  

	unsigned short m_width;                                                     //获取图像的窗宽高  
	unsigned short m_height;
	dataset->findAndGetUint16(DCM_Rows, m_height);
	dataset->findAndGetUint16(DCM_Columns, m_width);

	/////////////////////////////////////////////////////////////////////////  
	const char* transferSyntax = NULL;
	fileformat.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, transferSyntax);       //获得传输语法字符串  
	string losslessTransUID = "1.2.840.10008.1.2.4.70";
	string lossTransUID = "1.2.840.10008.1.2.4.51";
	string losslessP14 = "1.2.840.10008.1.2.4.57";
	string lossyP1 = "1.2.840.10008.1.2.4.50";
	string lossyRLE = "1.2.840.10008.1.2.5";
	if (transferSyntax == losslessTransUID || transferSyntax == lossTransUID ||
		transferSyntax == losslessP14 || transferSyntax == lossyP1)
	{
		DJDecoderRegistration::registerCodecs();
		dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);                       //对压缩的图像像素进行解压  
		DJDecoderRegistration::cleanup();
	}
	else if (transferSyntax == lossyRLE)
	{
		DcmRLEDecoderRegistration::registerCodecs();
		dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
		DcmRLEDecoderRegistration::cleanup();
	}
	else
	{
		dataset->chooseRepresentation(xfer, NULL);
	}

	DicomImage* m_dcmImage = new DicomImage((DcmObject*)dataset, xfer); //利用dataset生成DicomImage,需要上面的解压方法;  
	
	return m_dcmImage;
}

//************************************************************************
// 函数名称:    	GetImageFromDcmDataSet
// 访问权限:    	public 
// 创建日期:		2016/11/28
// 创 建 人:		
// 函数说明:		从DicomImage中获取所有的图像帧
// 函数参数: 	DicomImage * m_dcmImage		DCM文件的DicomImage对象
// 返 回 值:   	std::vector
//************************************************************************
std::vector CLoadMutiFrameDcm::GetImageFromDcmDataSet(DicomImage* m_dcmImage)
{
	std::vector output_img;				//输出图像向量
	int framecount(m_dcmImage->getFrameCount());	//获取这个文件包含的图像的帧数
	for (int k=0; kgetOutputData(8, k, 0)); //获得8位的图像数据指针  
		if (pixelData != NULL)
		{
			int m_height = m_dcmImage->getHeight();
			int m_width = m_dcmImage->getWidth();
			if (3 == this->Img_bitCount)
			{
				cv::Mat dst2(m_height, m_width, CV_8UC3, cv::Scalar::all(0));
				for (int i = 0; i < m_height; i++)
				{
					for (int j = 0; j < m_width; j++)
					{
						dst2.at(i, j)[0] = *(pixelData + i*m_width * 3 + j * 3 + 2);   //B channel  
						dst2.at(i, j)[1] = *(pixelData + i*m_width * 3 + j * 3 + 1);   //G channel  
						dst2.at(i, j)[2] = *(pixelData + i*m_width * 3 + j * 3);       //R channel  
					}
				}
				output_img.push_back(dst2);
			}
			else if (1 == this->Img_bitCount)
			{
				cv::Mat dst2(m_height, m_width, CV_8UC1, cv::Scalar::all(0));
				uchar* data = nullptr;
				for (int i = 0; i < m_height; i++)
				{
					data = dst2.ptr(i);
					for (int j = 0; j < m_width; j++)
					{
						data[j] = *(pixelData + i*m_width + j);
					}
				}
				output_img.push_back(dst2);
			}
			
			/*cv::imshow("image", dst2);
			cv::waitKey(0);*/
		}
	}

	return output_img;
}

//************************************************************************
// 函数名称:    	WriteMat2Dcm
// 访问权限:    	public 
// 创建日期:		2016/11/28
// 创 建 人:		
// 函数说明:		将Mat类型的数据转换为单个的dcm文件进行存储,使用原来的存储格式
// 函数参数: 	std::vector & source_img	输入的图像资源
// 函数参数: 	std::string output_path				输出dcm文件的路径
// 返 回 值:   	bool
//************************************************************************
bool CLoadMutiFrameDcm::WriteMat2Dcm(std::vector& source_img, std::string output_path)
{
	if (source_img.size()<=0)
	{
		cout << "No source image! error" << endl;
		return false;
	}
	if (!this->filepath.length())
	{
		cout << "No No parent dcm filepath! error" << endl;
		return false;
	}
	DcmFileFormat fileformat;
	OFCondition oc = fileformat.loadFile(this->filepath.c_str());              //读取Dicom图像  
	if (!oc.good())     //判断Dicom文件是否读取成功  
	{
		std::cout << "file Load error" << std::endl;
		return nullptr;
	}
	DcmDataset *dataset = fileformat.getDataset();                             //得到Dicom的数据集  
	E_TransferSyntax xfer = dataset->getOriginalXfer();                        //得到传输语法 
	unsigned short img_bits(0);
	dataset->findAndGetUint16(DCM_SamplesPerPixel, img_bits);                    //单个像素占用多少byte 
	unsigned short bit_count(0);
	dataset->findAndGetUint16(DCM_BitsStored, bit_count);                        //获取像素的位数 bit 
	OFString isRGB;
	dataset->findAndGetOFString(DCM_PhotometricInterpretation, isRGB);           //DCM图片的图像模式  

	for (int i = 0; i < (int)source_img.size(); i++)
	{
		DcmFileFormat *fileFormat = new DcmFileFormat();
		DcmDataset *dataSet = fileFormat->getDataset();
		int rows(source_img[i].rows);
		int cols(source_img[i].cols);
		int img_channels(source_img[i].channels());
		int length(rows*cols*img_channels);
		uchar* img_pixel = new uchar[length];
		memset(img_pixel, 0, length);
		uchar* Mat_data = nullptr;
		cv::Mat temp = source_img[i];
		if (3 == img_channels)
		{
			for (int j = 0; j < rows; j++)
			{
				for (int k = 0; k < cols; k++)
				{
					img_pixel[j * 3 * cols + 3*k] = source_img[i].at(j, k)[2];
					img_pixel[j * 3 * cols + 3*k + 1] = source_img[i].at(j, k)[1];
					img_pixel[j * 3 * cols + 3*k + 2] = source_img[i].at(j, k)[0];
				}
			}
		}
		else if (1 == img_channels)
		{
			for (int j = 0; j < rows; j++)
			{
				Mat_data = source_img[i].ptr(j);
				for (int k = 0; k < cols; k++)
				{
					img_pixel[j * cols + k] = Mat_data[k];
				}
			}
		}

		dataSet->putAndInsertUint8Array(DCM_PixelData, OFreinterpret_cast(Uint8*, img_pixel), length);
		dataSet->putAndInsertString(DCM_NumberOfFrames, "1");
		dataSet->putAndInsertUint16(DCM_Rows, rows);
		dataSet->putAndInsertUint16(DCM_Columns, cols);
		dataSet->putAndInsertUint16(DCM_SamplesPerPixel, img_bits);
		dataSet->putAndInsertUint16(DCM_BitsStored, bit_count);
		dataSet->putAndInsertOFStringArray(DCM_PhotometricInterpretation, isRGB);
		//dataSet->putAndInsertString();

		//保存文件
		char t[10];
		string filename;
		sprintf(t, "%d", i);
		filename = t;
		filename = output_path + filename + ".dcm";
		OFCondition status = fileFormat->saveFile(filename.c_str(), xfer);
		if (status.bad())
			cout << "Error: cannot write DICOM file (" << status.text() << ")" << endl;

		delete fileFormat;
		delete[] img_pixel;

		this->ShowImage(filename);
	}
	return true;
}

//************************************************************************
// 函数名称:    	ShowImage
// 访问权限:    	public 
// 创建日期:		2016/11/28
// 创 建 人:		
// 函数说明:		读取自己保存的Dcm图片查看是否能读取成功
// 函数参数: 	std::string filepath	保存的文件的路径
// 返 回 值:   	bool
//************************************************************************
bool CLoadMutiFrameDcm::ShowImage(std::string filepath)
{
	if (!filepath.length())
	{
		cout << "No No parent dcm filepath! error" << endl;
		return false;
	}

	DcmFileFormat fileformat;
	OFCondition oc = fileformat.loadFile(filepath.c_str());                    //读取Dicom图像  
	if (!oc.good())     //判断Dicom文件是否读取成功  
	{
		std::cout << "file Load error" << std::endl;
		return false;
	}
	DcmDataset *dataset = fileformat.getDataset();                              //得到Dicom的数据集  
	E_TransferSyntax xfer = dataset->getOriginalXfer();                          //得到传输语法  

	OFString patientname;
	dataset->findAndGetOFString(DCM_PatientName, patientname);                   //获取病人姓名  

	unsigned short bit_count(0);
	dataset->findAndGetUint16(DCM_BitsStored, bit_count);                        //获取像素的位数 bit  

	OFString isRGB;
	dataset->findAndGetOFString(DCM_PhotometricInterpretation, isRGB);           //DCM图片的图像模式  

	unsigned short img_bits(0);
	dataset->findAndGetUint16(DCM_SamplesPerPixel, img_bits);                    //单个像素占用多少byte  

	//DicomImage* img_xfer = new DicomImage(xfer, 0, 0, 1);                     //由传输语法得到图像的帧  

	unsigned short m_width;                                                     //获取图像的窗宽高  
	unsigned short m_height;
	dataset->findAndGetUint16(DCM_Rows, m_height);
	dataset->findAndGetUint16(DCM_Columns, m_width);
	


	/////////////////////////////////////////////////////////////////////////  
	const char* transferSyntax = NULL;
	fileformat.getMetaInfo()->findAndGetString(DCM_TransferSyntaxUID, transferSyntax);       //获得传输语法字符串  
	string losslessTransUID = "1.2.840.10008.1.2.4.70";
	string lossTransUID = "1.2.840.10008.1.2.4.51";
	string losslessP14 = "1.2.840.10008.1.2.4.57";
	string lossyP1 = "1.2.840.10008.1.2.4.50";
	string lossyRLE = "1.2.840.10008.1.2.5";
	if (transferSyntax == losslessTransUID || transferSyntax == lossTransUID ||
		transferSyntax == losslessP14 || transferSyntax == lossyP1)
	{
		DJDecoderRegistration::registerCodecs();
		dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);                       //对压缩的图像像素进行解压  
		DJDecoderRegistration::cleanup();
	}
	else if (transferSyntax == lossyRLE)
	{
		DcmRLEDecoderRegistration::registerCodecs();
		dataset->chooseRepresentation(EXS_LittleEndianExplicit, NULL);
		DcmRLEDecoderRegistration::cleanup();
	}
	else
	{
		dataset->chooseRepresentation(xfer, NULL);
	}

	DcmElement* element = NULL;
	OFCondition result = dataset->findAndGetElement(DCM_PixelData, element); //获取图像数据  
	if (result.bad() || element == NULL)
		return false;
	const Uint8* image_data = nullptr;
	Uint16* image16_data = nullptr;
	//if (8 == bit_count)
	//{
	//	result = element->getUint8Array(image_data);     //获取8位的图像数据  
	//}
	//else if (8 < bit_count)
	//{
	//	result = element->getUint16Array(image16_data);      //获取>8位的图像数据  
	//}
	dataset->findAndGetUint8Array(DCM_PixelData, image_data);

	if (image_data != NULL || image16_data != NULL)
	{
		if (1 == img_bits)      //1 byte per pixel  
		{
			cv::Mat dst(m_height, m_width, CV_8UC1, cv::Scalar::all(0));
			unsigned char* data = nullptr;
			for (int i = 0; i < m_height; i++)
			{
				data = dst.ptr(i); //取得每一行的头指针 也可使用dst2.at(i, j) = ?  
				for (int j = 0; j < m_width; j++)
				{
					*data++ = image_data[i*m_width + j];
				}
			}
			cv::imshow("gray", dst);
			cv::waitKey(0);
		}
		else if (3 == img_bits)     //3 bytes per pixel  
		{
			cv::Mat dst(m_height, m_width, CV_8UC3, cv::Scalar::all(0));
			unsigned short* data = nullptr;
			for (int i = 0; i < m_height; i++)
			{
				for (int j = 0; j < m_width; j++)
				{
					dst.at(i, j)[0] = image_data[i*m_width * 3 + j * 3 + 2];   //B channel  
					dst.at(i, j)[1] = image_data[i*m_width * 3 + j * 3 + 1];   //G channel  
					dst.at(i, j)[2] = image_data[i*m_width * 3 + j * 3];       //R channel  
				}
			}
			//cv::imshow("colored", dst);
			//cv::waitKey(0);
		}
	}
	return true;
}

3. 结果

读取分别保存的的单帧Dcm图像:
多帧Dcm图片的读取与拆分存储_第1张图片
拆分得到的图像:
多帧Dcm图片的读取与拆分存储_第2张图片

4. 参考资料
1.DICOM医学图像处理:DICOM存储操作之 “多幅JPG图像数据存入DCM文件”
2.DCMTK:单张jpg、bmp图像转dcm文件

你可能感兴趣的:([4],图像处理相关)