文章目录
- 前言
- 一、对话框添加按钮
- 二、DcmParser类导出函数
-
- [1. Export函数](#1. Export函数)
- 2.导出JPG
- [3. 导出PNG](#3. 导出PNG)
- [4. 导出BMP](#4. 导出BMP)
- [5. 导出TIFF](#5. 导出TIFF)
- [6. 导出MP4](#6. 导出MP4)
前言
本章介绍从dicom文件生成bmp、jpg、png、tiff格式的图片。单帧图生成一张,多帧图生成与帧数相同数量的图片。超过10帧(含10帧)的多帧图可以导出mp4格式视频
效果如下 :
-
单帧图

-
多帧图

一、对话框添加按钮
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;
}