基于dcmtk的dicom工具 第十三章 dicom文件导出bmp、jpg、png、tiff、mp4

文章目录


前言

本章介绍从dicom文件生成bmp、jpg、png、tiff格式的图片。单帧图生成一张,多帧图生成与帧数相同数量的图片。超过10帧(含10帧)的多帧图可以导出mp4格式视频

效果如下 :

  1. 单帧图

  2. 多帧图


一、对话框添加按钮

CDcmImageDlg类按钮响应函数:

cpp 复制代码
void CDcmImageDlg::OnBnClickedButtonExpJpg()
{
	std::string dst = SelectFolder();
	if (!m_disp.Export(dst, 0)) {
		MessageBox(_T("导出JPG失败!"));
	}
}


void CDcmImageDlg::OnBnClickedButtonExpPng()
{
	std::string dst = SelectFolder();
	if (!m_disp.Export(dst, 1)) {
		MessageBox(_T("导出PNG失败!"));
	}
}

void CDcmImageDlg::OnBnClickedButtonExpBmp()
{
	std::string dst = SelectFolder();
	if (!m_disp.Export(dst, 2)) {
		MessageBox(_T("导出BMP失败!"));
	}
}

void CDcmImageDlg::OnBnClickedButtonExpTiff()
{
	std::string dst = SelectFolder();
	if (!m_disp.Export(dst, 3)) {
		MessageBox(_T("导出TIFF失败!"));
	}
}

void CDcmImageDlg::OnBnClickedButtonExpVideo()
{
	std::string dst = SelectFolder();
	if (!m_disp.Export(dst, 4)) {
		MessageBox(_T("导出MP4失败!"));
	}
	else {
		MessageBox(_T("导出MP4完成!"));
	}
}

Displayer 接口函数:

cpp 复制代码
// format = 0 jpg, 1 png, 2 bmp, 3 tiff, 4 mp4(多帧图有效)
bool Displayer::Export(std::string dst, int format)
{
	if (m_pImage == nullptr) return false;
	return m_pImage->Export(dst, format);
	
}

二、DcmParser类导出函数

1. Export函数

根据format参数,分别调用SaveToJpg, SaveToPng,SaveToBmp,SaveToTiff,SaveToVideo函数保存为不同格式

format = 0 jpg, 1 png, 2 bmp, 3 tiff, 4 mp4(多帧图有效)

cpp 复制代码
bool DcmParser::Export(std::string dst, int format)
{
	int result = 0;
	int nFrames = GetFrameCount();
	std::string sopInsUid = GetTagStringValue(0x0008, 0x0018);
	std::string serInsUid = GetTagStringValue(0x0020, 0x000e);
	std::string patId = GetTagStringValue(0x0010, 0x0020);
	std::string ext[] = { ".jpg", ".png", ".bmp", ".tif", ".jpg" };
	std::string serDir = dst + "\\" + patId;
	MakePath(serDir);

	std::vector<std::string> videoFiles;
	int w, h;

	for (int i=0; i<nFrames; i++)
	{
		std::string fn = serDir + "\\" + "frame_" + std::to_string(i) + ext[format];

		DicomImage *di = createImage(i);
		int frame = i - m_nFrameStart;

		if (i == 0) {
			w = di->getWidth();
			h = di->getHeight();
		}

		switch (format)
		{
		case 0:  // jpg
		case 4:
		{
			result = SaveToJpg(di, fn, frame);
			if (result && format==4) 
			{
				videoFiles.push_back(fn);
			}
		}
		break;
		case 1:  // png
			result = SaveToPng(di, fn, frame);
			break;
		case 2:  // bmp
			result = di->writeBMP(fn.c_str(), 0, frame);
			result = SaveToBmp(di, fn, frame);
			break;
		case 3:  // tiff
		{
			//std::string tivVer = DiTIFFPlugin::getLibraryVersionString();
			//CString msg = tivVer.c_str();
			//
			//OutputDebugString(msg + "\r\n");
			//DiTIFFPlugin tiffPlugin;
			//tiffPlugin.setCompressionType(E_tiffLZWCompression);
			////tiffPlugin.setCompressionType(E_tiffPackBitsCompression);
			//tiffPlugin.setLZWPredictor(E_tiffLZWPredictorDefault);
			//tiffPlugin.setRowsPerStrip(OFstatic_cast(unsigned long, 0));
			//result = di->writePluginFormat(&tiffPlugin, ofile, frame);

			result = SaveToTiff(di, fn, frame);

		}
		break;
		default:
			break;
		}
	}

	if (format == 4) {
		if (videoFiles.size() > 9) {
			std::string fn = serDir + "\\" + patId + ".mp4";
			SaveToVideo(videoFiles, w, h, fn);
		}
		else {
			result = 0;
		}
		
	}
	
	return result>0;
}

2.导出JPG

导出JPG调用DiJPEGPlugin插件类。

导出JPG,PNG,TIFF格式,都可以调用插件实现,

需要在编译dcmtk时添加预编译库,勾选DCMTK_WITH_OPENJPEG、DCMTK_WITH_PNG、DCMTK_WITH_TIFF,参考文章基于dcmtk的dicom工具 第一章 编译dcmtk3.6.9

cpp 复制代码
int DcmParser::SaveToJpg(DicomImage* di, std::string dst, int frame)
{
	DiJPEGPlugin plugin;
	plugin.setQuality(OFstatic_cast(unsigned int, 100));
	plugin.setSampling(ESS_422);
	return di->writePluginFormat(&plugin, dst.c_str(), frame);
}

3. 导出PNG

导出PNG调用DiPNGPlugin 插件类

cpp 复制代码
int DcmParser::SaveToPng(DicomImage* di, std::string dst, int frame)
{
	DiPNGPlugin pngPlugin;
	pngPlugin.setInterlaceType(E_pngInterlaceAdam7);
	pngPlugin.setMetainfoType(E_pngFileMetainfo);
	return di->writePluginFormat(&pngPlugin, dst.c_str(), frame);
}

4. 导出BMP

导出BMP不需要调用插件类,DicomImage可直接保存为BMP格式

cpp 复制代码
int DcmParser::SaveToBmp(DicomImage* di, std::string dst, int frame)
{
	return di->writeBMP(dst.c_str(), 0, frame);
}

5. 导出TIFF

导出TIFF本来有插件类DiTIFFPlugin可用,但是在我电脑上导出失败,经调试后发现在writePluginFormat内部,TIFFFdOpen函数调用失败。
FILE *stream = fopen(filename, "wb");
int stream_fd = fileno(stream);
TIFF *tif = TIFFFdOpen(stream_fd, "TIFF", "w"); 失败,总是返回0

DiTIFFPlugin类调用方法,有兴趣的朋友可以自行研究:

cpp 复制代码
	DiTIFFPlugin tiffPlugin;
	//tiffPlugin.setCompressionType(E_tiffLZWCompression);
	tiffPlugin.setCompressionType(E_tiffPackBitsCompression);
	tiffPlugin.setLZWPredictor(E_tiffLZWPredictorDefault);
	tiffPlugin.setRowsPerStrip(OFstatic_cast(unsigned long, 0));
	result = di->writePluginFormat(&tiffPlugin, fn.c_str(), frame);

解决方法,不调用TIFFFdOpen,改用TIFFOpen:

cpp 复制代码
int DcmParser::SaveToTiff(DicomImage* di, std::string dst, int frame)
{
	void *data = OFconst_cast(void *, di->getOutputData(8, frame, 0));
	if (data == nullptr) {
		return 0;
	}

	int cols = di->getWidth();
	int rows = di->getHeight();
	OFBool isMono = di->isMonochrome();
	short photometric = isMono ? PHOTOMETRIC_MINISBLACK : PHOTOMETRIC_RGB;
	short samplesperpixel = isMono ? 1 : 3;
	unsigned long bytesperrow = cols * samplesperpixel;

	long opt_rowsperstrip = OFstatic_cast(long, 0);
	if (opt_rowsperstrip <= 0) opt_rowsperstrip = 8192 / bytesperrow;
	if (opt_rowsperstrip == 0) opt_rowsperstrip++;

	TIFF* tif = TIFFOpen(dst.c_str(), "w");
	if (!tif) {
		return 0;
	}

	int ret = 0;
	ret = TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, cols);
	ret = TIFFSetField(tif, TIFFTAG_IMAGELENGTH, rows);
	ret = TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
	ret = TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
	ret = TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric);
	ret = TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
	ret = TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, opt_rowsperstrip);

	int OK = 1;
	unsigned long offset = 0;
	unsigned char *bytedata = OFstatic_cast(unsigned char *, data);
	for (Uint16 i = 0; (i < rows) && OK; i++)
	{
		if (TIFFWriteScanline(tif, bytedata + offset, i, 0) < 0)
			OK = 0;
		offset += bytesperrow;
	}

	TIFFFlushData(tif);
	TIFFClose(tif); 

	return OK;
}

6. 导出MP4

多帧图可导出mp4视频,先导出jpg,再通过opencv用jpg文件生成视频文件。

MFC项目导入opencv参考VS2017配置OpenCV4.5.1

我安装的是opencv4.6.0版本,记得把opencv_world460.dll,opencv_videoio_ffmpeg460.dll都拷贝到程序运行目录,才能生成mp4格式的视频,如果只拷贝了opencv_world460.dll,则只能生成MJPG格式视频

cpp 复制代码
int DcmParser::SaveToVideo(std::vector<std::string>& videoFiles, int w, int h, std::string dst)
{
	cv::VideoWriter writer;
	cv::Mat cvImg;
	//writer = cv::VideoWriter(fn,
	//	cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 5,
	//	cv::Size(w, h));

	writer = cv::VideoWriter(dst,
		cv::VideoWriter::fourcc('M', 'P', '4', 'V'), 5,
		cv::Size(w, h));

	for (int i = 0; i < videoFiles.size(); i++)
	{
		std::string jpgFn = videoFiles.at(i);
		cvImg = cv::imread(jpgFn);
		writer << cvImg;
		DeleteFile(jpgFn.c_str());
	}

	return 1;
}
相关推荐
永远有缘8 小时前
Java、Python、C# 和 C++ 在函数定义语法上的主要区别
java·c++·python·c#
绛洞花主敏明9 小时前
Go切片的赋值
c++·算法·golang
纵有疾風起13 小时前
C++—string(1):string类的学习与使用
开发语言·c++·经验分享·学习·开源·1024程序员节
郭源潮117 小时前
《Muduo网络库:实现TcpServer类终章》
服务器·网络·c++·网络库
MMjeaty20 小时前
查找及其算法
c++·算法
yong158585534321 小时前
1. Linux C++ muduo 库学习——库的编译安装
linux·c++·学习
mit6.8241 天前
回溯剪枝trick
c++
渡我白衣1 天前
C++世界的混沌边界:undefined_behavior
java·开发语言·c++·人工智能·深度学习·语言模型
却道天凉_好个秋1 天前
c++ 协程
c++