简单的Dicom图像显示和保存的方法(利用dcmtk库和opencv库)

视频教程:一种简单的Dicom图像显示和保存的方法(利用dcmtk库和opencv库)

Git: https://github.com/JasonLiThirty/Dicom

opencv的下载

  • 官网:https://opencv.org/releases/,下载最新版本4.3.0
  • 安装即解压:

Dicom_Module工程配置opencv库

  • 配置系统的环境变量Path:把之前解压(安装)好的opencv路径下的x64的bin路径添加到Path系统环境变量中,重启。

  • 在Dicom_Module工程配置中加入opencv的include路径和lib路径。

  • 在工程配置的linker->input里加入opencv的依赖lib,Debug配置opencv_world430d.lib,Relase配置opencv_world430.lib,这两个lib文件在opencv\build\x64\vc14\lib下。

  • 将opencv\build\x64\vc14\bin目录下的opencv_world430.dll和opencv_world430d.dll拷贝到工程的输出目录下。

Dicom_Module工程配置dcmimgle和dcmimage库

  • 在工程配置的linker->input里加入dcmimgle和dcmimage的依赖lib

  • 将最先编译出的DCMTK类包中bin目录下的dcmimgle.dll和dcmimage.dll拷贝到工程的输出目录下。


Dicom_Module工程里编写所需类和函数

接口类DicomIF

  • 新增显示图像和保存图像为BMP的接口
1
2
3
4
5
6
7
8
class __declspec(dllexport) IDicom
{
public:
...
virtual bool ShowImage(std::string filePath, Image_Pattern pattern = ImagePattern_Normal, signed int degree = 0) = 0;
virtual bool SavetoBMP(std::string filePath, Image_Pattern pattern = ImagePattern_Normal, signed int degree = 0) = 0;
...
}

实现类DicomProcessor

  • 新增显示图像和保存图像为BMP的实现(头文件包含dcmimage.h)
1
2
3
#include "dcmtk/dcmimgle/dcmimage.h"
bool ShowImage(std::string filePath, Image_Pattern pattern = ImagePattern_Normal, signed int degree = 0);
bool SavetoBMP(std::string filePath, Image_Pattern pattern = ImagePattern_Normal, signed int degree = 0);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
bool DicomProcessor::ShowImage(std::string filePath, Image_Pattern pattern, signed int degree)
{
    if (!Read(filePath))
    {
        std::cout << "Read Dicom File Failed: " << filePath.c_str() << std::endl;
        return false;
    }

    E_TransferSyntax tfs = m_fileformat.getDataset()->getOriginalXfer();
    DicomImage *pImage = new DicomImage(&m_fileformat, tfs);
    pImage->setWindow(m_image->WindowCenter, m_image->WindowWidth);

    PatternConversion(pImage, pattern, degree);

    Uint16 *pixelData = (Uint16*)(pImage->getOutputData(m_image->BitsAllocated));
    if (pixelData == NULL)
    {
        std::cout << "Get Image Data Failed: " << filePath.c_str() << std::endl;
        return false;
    }

    cv::Mat dst(pImage->getWidth(), pImage->getHeight(), CV_16U, pixelData);
    cv::imshow(m_patient->PatientsName, dst);
    cv::waitKey(0);

    return true;
}

bool DicomProcessor::SavetoBMP(std::string filePath, Image_Pattern pattern, signed int degree)
{
    if (!Read(filePath))
    {
        std::cout << "Read Dicom File Failed: " << filePath.c_str() << std::endl;
        return false;
    }

    E_TransferSyntax tfs = m_fileformat.getDataset()->getOriginalXfer();
    DicomImage *pImage = new DicomImage(&m_fileformat, tfs);
    pImage->setWindow(m_image->WindowCenter, m_image->WindowWidth);

    PatternConversion(pImage, pattern, degree);

    if (!pImage->writeBMP(GenerateImageName(filePath, pattern).c_str(), 8))
    {
        std::cout << "Read Dicom File Failed: " << filePath.c_str() << std::endl;
        return false;
    }
    return true;
}
  • 新增图像变换函数
1
void PatternConversion(DicomImage* &image, Image_Pattern pattern, signed int degree = 0);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void DicomProcessor::PatternConversion(DicomImage* &image, Image_Pattern pattern, signed int degree)
{
    switch (pattern)
    {
    case ImagePattern_Normal:
        break;
    case ImagePattern_Flip:
        image->flipImage();
        break;
    case ImagePattern_Rotate:
        image->rotateImage(degree);
        break;
    case ImagePattern_Polarity:
        image->setPolarity(EPP_Reverse);
        break;
    default:
        break;
    }
}

DicomInfo加入图像变换类型定义

1
2
3
4
5
6
7
enum Image_Pattern
{
    ImagePattern_Normal = 1,
    ImagePattern_Flip,
    ImagePattern_Rotate,
    ImagePattern_Polarity
};

DicomUtils加入生成BMP文件名的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
__declspec(dllexport) std::string GenerateImageName(std::string name, Image_Pattern pattern);
std::string GenerateImageName(std::string name, Image_Pattern pattern)
{
    std::string prefix = "";
    switch (pattern)
    {
    case ImagePattern_Normal:
        break;
    case ImagePattern_Flip:
        prefix = "_flip";
        break;
    case ImagePattern_Rotate:
        prefix = "_rotate";
        break;
    case ImagePattern_Polarity:
        prefix = "_polarity";
        break;
    default:
        break;
    }

    auto last_pos = name.find_last_of(".");
    std::string srcName = name.substr(0, last_pos);
    std::cout << srcName << std::endl;
    std::string fileName = srcName + prefix + ".bmp";
    return fileName;
}

Dicom_Sample加入调用代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int main()
{
    CreateDicomProcessor();
    std::string readFile = "E:\\Learning\\DICOM\\Module_Sample\\MRBRAIN.dcm";

    //if (!GetDicomProcessor()->ShowImage(readFile, ImagePattern_Flip))
    //if (!GetDicomProcessor()->ShowImage(readFile, ImagePattern_Rotate, 90));
    //if (!GetDicomProcessor()->ShowImage(readFile, ImagePattern_Polarity))
    if (!GetDicomProcessor()->ShowImage(readFile))
    {
        std::cout << "Read Dicom Image Failed: " << readFile.c_str() << std::endl;
        return -1;
    }

    if (!GetDicomProcessor()->SavetoBMP(readFile))
    //if (!GetDicomProcessor()->SavetoBMP(readFile, ImagePattern_Flip))
    //if (!GetDicomProcessor()->SavetoBMP(readFile, ImagePattern_Rotate, 90))
    //if (!GetDicomProcessor()->SavetoBMP(readFile, ImagePattern_Polarity))
    {
        std::cout << "Read Dicom Image Failed: " << readFile.c_str() << std::endl;
        return -1;
    }
    DeleteDicomProcessor();
    return 0;
}