DICOM:基于fo-dicom的简易DICOM Viewer

背景

正如段子所言,“春节”副本已通关,您的属性有如下变化:力量 0,智力 0,敏捷 0,体重 15,金钱-999。您已被传送回出发点,系统时间从“初八”切换到“周四”,请继续进行主线任务……虽然公司春节多放了几天假,原本以为上班还早的我突然发现原来这个月是2月啊,只有28天,怎奈假期配置测试的DCM4CHEE系列文章还未整理完成,既然到了月底了,就凑一篇水文吧,把春节帮网友写的一个基于fo-dicom的DICOM Viewer贴出来,代码拙劣,仅供参考。

PS:DCM4CHEE是一种基于JAVA的开源DICOM框架,预告一下后续专栏文章会对其进行相关介绍,如果有可能会单独开一个专栏,敬请期待。

DICOM Viewer设计

这里基于fo-dicom开源库设计一个简单的DICOM Viewer,相较于大家所熟知的Sante DICOM Editor、ImageJ、RadiAnt等DICOM阅读器,本文只是简单的提取DICOM文件中的基本信息和图像。目前仅支持常规和JPEG无损压缩(即,JPEG LossLess,Non-Hierachical(Process 14))两种类型图像

本DICOM Viewer采用C#语言开发,程序主界面如下:

从上图可以看出主界面由三部分构成,菜单栏、DICOM信息列表和DICOM图像显示。各部分的功能简单介绍如下:

1)菜单栏

菜单栏包括‘File’‘Help’两个选项。‘File’中包含‘Open’‘Close’两个子项,单击‘Open’会打开“文件打开”对话框,选择要打开的DCM文件( 目前打开对话框中是通过文件扩展名来识别文件的,因此需要明确将DCM文件扩展名设置为DCM或dcm才可以导入 );单击‘Close’选项相当如复原操作,会清除界面中显示的DCM文件的信息列表和图像,返回界面初始状态;按照惯例‘Help’中的‘About’就是一个简单的软件介绍。

2)信息列表

‘信息列表’用于显示DICOM文件中的Patient、Study、Series和Image等相关信息,包含GroupTagElementTagValue三部分

3)图像显示

‘图像显示’区域默认是一副灰色白字背景图,界面初始化以及单击‘File’下的‘Close’按钮都会显示该默认背景图。

DICOM Viewer源码

直接给出软件的核心部分源码,通过fo-dicom开源库来导入和提取DICOM图像的信息,然后利用ListView和Bitmap分别来实现信息的显示和图像绘制。核心函数主要如下:

1)文件导入

直接使用fo-dicom开源库中DicomFile类的静态方法Open完成DCM文件导入,代码如下:

            try
            {
                DicomFile dcmFile = DicomFile.Open(dcmFileName);
                //Load and Show the meta info of the DCM file
                LoadAndShowDCMMetaInfo(dcmFile);
                listView1.Update();
                //Drawing the image in your DCM file to screen.
                DrawingDCMData2Screen(dcmFile);
            }
            catch (System.Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }

2)信息提取及显示

利用DicomDataset的模板方法Get<>()分别提取DCM文件中的相关信息,通过构造ListViewItem添加到ListView中进行显示,具体代码如下:

     /// <summary>
        /// Load all of the Elements in the DCM Meta
        /// </summary>
        /// <param name="dcm">the DICOM file</param>
        private void LoadAndShowDCMMetaInfo(DicomFile dcm)
        {
            //Show the message of DCM file to the listview in the dialog.
            DicomDataset dcmDataset = dcm.Dataset;
            DicomFileMetaInformation dcmMetaInfo = dcm.FileMetaInfo;
            listView1.BeginUpdate();
            //DICOM MetaInfo
            if (!String.IsNullOrWhiteSpace(dcmMetaInfo.MediaStorageSOPClassUID==null?"":dcmMetaInfo.MediaStorageSOPClassUID.ToString()))
            {
                //Insert the ImplementationClassUID
                ListViewItem newItem = new ListViewItem("0002");
                newItem.SubItems.Add("0002");
                newItem.SubItems.Add(dcmMetaInfo.MediaStorageSOPClassUID.ToString());
                listView1.Items.Add(newItem);
            }
            if (!String.IsNullOrWhiteSpace(dcmMetaInfo.MediaStorageSOPInstanceUID==null?"":dcmMetaInfo.MediaStorageSOPInstanceUID.ToString()))
            {
                //Insert the ImplementationClassUID
                ListViewItem newItem = new ListViewItem("0002");
                newItem.SubItems.Add("0003");
                newItem.SubItems.Add(dcmMetaInfo.MediaStorageSOPInstanceUID.ToString());
                listView1.Items.Add(newItem);

            }
            if (!String.IsNullOrWhiteSpace(dcmMetaInfo.TransferSyntax==null?"":dcmMetaInfo.TransferSyntax.ToString()))
            {
                //Insert the ImplementationClassUID
                ListViewItem newItem = new ListViewItem("0002");
                newItem.SubItems.Add("0010");
                newItem.SubItems.Add(dcmMetaInfo.TransferSyntax.ToString());
                listView1.Items.Add(newItem);

            }
            if (!String.IsNullOrWhiteSpace(dcmMetaInfo.ImplementationClassUID==null?"":dcmMetaInfo.ImplementationClassUID.ToString()))
            {
                //Insert the ImplementationClassUID
                ListViewItem newItem = new ListViewItem("0002");
                newItem.SubItems.Add("0012");
                newItem.SubItems.Add(dcmMetaInfo.ImplementationClassUID.ToString());
                listView1.Items.Add(newItem);

            }
            if (!String.IsNullOrWhiteSpace(dcmMetaInfo.ImplementationVersionName==null?"":dcmMetaInfo.ImplementationVersionName.ToString()))
            {
                //Insert the ImplementationClassUID
                ListViewItem newItem = new ListViewItem("0002");
                newItem.SubItems.Add("0013");
                newItem.SubItems.Add(dcmMetaInfo.ImplementationVersionName.ToString());
                listView1.Items.Add(newItem);

            }

            //Patient Info
            string patientName = dcmDataset.Get<string>(DicomTag.PatientName, "");
            if (!String.IsNullOrWhiteSpace(patientName))
            {
                ListViewItem newItem = new ListViewItem("0010");
                newItem.SubItems.Add("0010");
                newItem.SubItems.Add(patientName);
                listView1.Items.Add(newItem);
            }
            string patientID = dcmDataset.Get<string>(DicomTag.PatientID, "");
            if (!String.IsNullOrWhiteSpace(patientID))
            {
                ListViewItem newItem = new ListViewItem("0010");
                newItem.SubItems.Add("0020");
                newItem.SubItems.Add(patientID);
                listView1.Items.Add(newItem);
            }
            string patientBirth = dcmDataset.Get<string>(DicomTag.PatientBirthDate);
            if (!String.IsNullOrWhiteSpace(patientBirth))
            {
                ListViewItem newItem = new ListViewItem("0010");
                newItem.SubItems.Add("0030");
                newItem.SubItems.Add(patientBirth);
                listView1.Items.Add(newItem);
            }
            string patientSex = dcmDataset.Get<string>(DicomTag.PatientSex);
            if (!String.IsNullOrWhiteSpace(patientSex))
            {
                ListViewItem newItem = new ListViewItem("0010");
                newItem.SubItems.Add("0040");
                newItem.SubItems.Add(patientSex);
                listView1.Items.Add(newItem);
            }
            string patientAge = dcmDataset.Get<string>(DicomTag.PatientAge);
            if (!String.IsNullOrWhiteSpace(patientAge))
            {
                ListViewItem newItem = new ListViewItem("0010");
                newItem.SubItems.Add("1010");
                newItem.SubItems.Add(patientAge);
                listView1.Items.Add(newItem);
            }
            //Study & Series Info
            string studyDate = dcmDataset.Get<string>(DicomTag.StudyDate);
            if (!String.IsNullOrWhiteSpace(studyDate))
            {
                ListViewItem newItem = new ListViewItem("0008");
                newItem.SubItems.Add("0020");
                newItem.SubItems.Add(studyDate);
                listView1.Items.Add(newItem);
            }
            string studyTime = dcmDataset.Get<string>(DicomTag.StudyTime);
            if (!String.IsNullOrWhiteSpace(studyTime))
            {
                ListViewItem newItem = new ListViewItem("0008");
                newItem.SubItems.Add("0030");
                newItem.SubItems.Add(studyTime);
                listView1.Items.Add(newItem);
            }
            string studyInstanceID = dcmDataset.Get<string>(DicomTag.StudyInstanceUID);
            if (!String.IsNullOrWhiteSpace(studyInstanceID))
            {
                ListViewItem newItem = new ListViewItem("0020");
                newItem.SubItems.Add("000D");
                newItem.SubItems.Add(studyInstanceID);
                listView1.Items.Add(newItem);
            }
            string seriesInstanceID = dcmDataset.Get<string>(DicomTag.SeriesInstanceUID);
            if (!String.IsNullOrWhiteSpace(seriesInstanceID))
            {
                ListViewItem newItem = new ListViewItem("0020");
                newItem.SubItems.Add("000E");
                newItem.SubItems.Add(seriesInstanceID);
                listView1.Items.Add(newItem);
            }
            string studyID = dcmDataset.Get<string>(DicomTag.StudyID);
            if (!String.IsNullOrWhiteSpace(studyID))
            {
                ListViewItem newItem = new ListViewItem("0020");
                newItem.SubItems.Add("0010");
                newItem.SubItems.Add(studyID);
                listView1.Items.Add(newItem);
            }
            //Image Info
            string imageHeight = dcmDataset.Get<string>(DicomTag.Rows);
            if (!String.IsNullOrWhiteSpace(imageHeight))
            {
                ListViewItem newItem = new ListViewItem("0028");
                newItem.SubItems.Add("0010");
                newItem.SubItems.Add(imageHeight);
                listView1.Items.Add(newItem);
            }
            string imageWidth = dcmDataset.Get<string>(DicomTag.Columns);
            if (!String.IsNullOrWhiteSpace(imageWidth))
            {
                ListViewItem newItem = new ListViewItem("0028");
                newItem.SubItems.Add("0011");
                newItem.SubItems.Add(imageWidth);
                listView1.Items.Add(newItem);
            }
            string bitsAlloctaed = dcmDataset.Get<string>(DicomTag.BitsAllocated);
            if (!String.IsNullOrWhiteSpace(bitsAlloctaed))
            {
                ListViewItem newItem = new ListViewItem("0028");
                newItem.SubItems.Add("0100");
                newItem.SubItems.Add(bitsAlloctaed);
                listView1.Items.Add(newItem);
            }
            string bitsStored = dcmDataset.Get<string>(DicomTag.BitsStored);
            if (!String.IsNullOrWhiteSpace(bitsStored))
            {
                ListViewItem newItem = new ListViewItem("0028");
                newItem.SubItems.Add("0101");
                newItem.SubItems.Add(bitsStored);
                listView1.Items.Add(newItem);
            }
            string highBits = dcmDataset.Get<string>(DicomTag.HighBit);
            if (!String.IsNullOrWhiteSpace(highBits))
            {
                ListViewItem newItem = new ListViewItem("0028");
                newItem.SubItems.Add("0102");
                newItem.SubItems.Add(highBits);
                listView1.Items.Add(newItem);
            }
            listView1.EndUpdate();

        }

3)图像提取及绘制

首先根据DCM文件的TransferSyntax字段是否是压缩语义来进行分类提取,获得DCM文件中的原始图像信息。代码如下:

            DicomFile newDcmFile = null;
            if (dcm.FileMetaInfo.TransferSyntax.IsEncapsulated)//if the data is compressed
            {
                System.Reflection.Assembly.LoadFrom(Path.Combine(Application.StartupPath,"Dicom.Native64.dll"));
                DicomTranscoder.LoadCodecs(Application.StartupPath, "Dicom.Native*.dll");
                newDcmFile=dcm.ChangeTransferSyntax(DicomTransferSyntax.ExplicitVRLittleEndian,new DicomJpegLsParams());
            }
            DicomImage imageDcm=null;
            if (newDcmFile != null)
                imageDcm = new DicomImage(newDcmFile.Dataset);
            else
                imageDcm = new DicomImage(dcm.Dataset);
            DicomDataset dataset = dcm.Dataset;
            byte[] fs = imageDcm.PixelData.NumberOfFrames < 2 ? imageDcm.PixelData.GetFrame(0).Data : imageDcm.PixelData.GetFrame(1).Data;
            uint size = (uint)Marshal.SizeOf(typeof(short));
            uint padding = (uint)fs.Length % size;
            uint count = (uint)fs.Length / size;
            short[] values = new short[count];
            System.Buffer.BlockCopy(fs, 0, values, 0, (int)(fs.Length - padding));

            int height = dataset.Get<int>(DicomTag.Rows);
            int width = dataset.Get<int>(DicomTag.Columns);
            int windowsWidth = (int)dataset.Get<double>(DicomTag.WindowWidth);
            int windowsCenter = (int)dataset.Get<double>(DicomTag.WindowCenter);
            //if the windowsWidth = 0, the DCM file is not standard type.
            if (windowsWidth == 0 || windowsCenter==0)
            {
                windowsWidth = values.Max<short>()-values.Min<short>();
                windowsCenter = windowsWidth / 2;
            }
            int low = windowsCenter - windowsWidth / 2;
            int high = windowsCenter + windowsWidth / 2;

然后构造Bitmap对象,添加到PictureBox控件中完成图像的显示,代码如下:

            Bitmap bitmap = new Bitmap(width, height);
            for (int i = 0; i < height; ++i)
            {
                for (int j = 0; j < width; ++j)
                {
                    int r, g, b;
                    int temp = (int)values[(width - j - 1) * height + i];
                    int val = temp > high ? 255 : (temp < low ? 0 : ((temp - low) * 255 / windowsWidth));
                    r = g = b = val;
                    bitmap.SetPixel(i, width-j-1, Color.FromArgb(r, g, b));
                }
            }
picturebox1.Image=bitmap;

DICOM Viewer源码下载

DICOM Viewer on Github

作者:[email protected]

时间:2015-02-28

时间: 2024-11-04 20:52:07

DICOM:基于fo-dicom的简易DICOM Viewer的相关文章

dicom通讯的工作方式及dicom标准简介

转自:http://www.cnblogs.com/assassinx/p/3223460.html 本文主要讲述dicom标准及dicom通讯的工作方式.dicom全称医学数字图像与通讯 其实嘛就两个方面 那就是“存储”跟“通讯”. 文件数据组织方式  网络数据组织方式.文件数据组织方式就是解析静态的dicom文件 在 <dicom格式文件解析器>一文中已经阐述过了 就不再说了.网络数据组织方式 简而言之就是各种协议 命令控制 数据序列化.那么这一章中我们将会讲他,但是进行实际操作将在后面几

基于C# Winform的简易聊天程序[第一篇-两端通信]

程序简介 本聊天程序支持局域网内部客户端与服务端之间的互相通信. 原理 启动服务端后,服务端通过持续监听客户端发来的请求,一旦监听到客户端传来的信息后,两端便可以互发信息了.服务端需要绑定一个IP,用于客户端在网络中寻找并建立连接.信息发送原理:将手动输入字符串信息转换成机器可以识别的字节数组,然后调用套接字的Send()方法将字节数组发送出去.信息接收原理:调用套接字的Receive()方法,获取对端传来的字节数组,然后将其转换成人可以读懂的字符串信息. 界面设计 - 服务端 IP文本框 na

基于QT的一个简易的安防

工程描述 opencv2.4.8 QT5 背景建模后,当有异物入侵时,把入侵的帧写到视频文件 使用BackgroundSubtractorMOG2背景建模 程序基于QT对话框 .pro #------------------------------------------------- # # Project created by QtCreator 2014-06-19T16:00:40# #------------------------------------------------- Q

WebSocket基于javaweb+tomcat的简易demo程序

由于项目需要,前端向后台发起请求后,后台需要分成多个步骤进行相关操作,而且不能确定各步骤完成所需要的时间 倘若使用ajax重复访问后台以获取实时数据,显然不合适,无论是对客户端,还是服务端的资源很是浪费 这种情况下,WebSocket能够解决此问题 它不像普通的http请求或者ajax访问,返回相应的结果就关闭了连接 WebSocket在个人浅薄的知识看来是属于长连接,能保持连接,随时收发数据 所以对WebSocket进行了初步了解,并按照相关的教程尝试做了一个简易demo 首先需要了解的是,W

基于OpenGL编写一个简易的2D渲染框架-05 渲染文本

阅读文章前需要了解的知识:文本渲染 https://learnopengl-cn.github.io/06%20In%20Practice/02%20Text%20Rendering/ 简要步骤: 获取要绘制的字符的 Unicode 码,使用 FreeType 库获取对应的位图数据,添加到字符表中(后面同样的字符可以再表中直接索引),将字符表上的字符填充到一张纹理上.计算每个字符的纹理坐标,使用渲染器绘制 注意的问题: 对于中英文混合的字符串,使用 char 存储时,英文字符占 1 个字节,而中

基于SSM的JAVA简易网络存储系统

今天将为大家分析一个简易网络存储系统(近年来,随着信息技术的进一步发展,以及网络的大规模应用,带来了数据的爆炸性增长,也给网络存储带来了巨大的发展机会.今天的存储系统已经形成了从简单的直连存储到复杂的网络存储,从单个存储设备到多个存储设备的多层次.复杂的存储体系.存储系统的变化给存储管理带来了质的变革,如何有效的管理整个存储网络系统,为用户提供灵活多样的存储服务,同时保证数据的安全和可用性,这些都是存储管理急需解决的问题. 针对两种典型的网络存储系统:附网存储和存储区域网,在分析其系统结构和特点

基于数据库MySQL的简易学生信息管理系统

通过这几天学习Mysql数据库,对其也有了基本的了解,为了加深印象,于是就写了一个最简易的学生信息管理系统. 一:基本要求 1.通过已知用户名和密码进行登录: 2.可以显示菜单: 3.可以随时插入学生信息: 4.可以删除学生信息: 5.可以通过学生姓名或学号显示学生所有信息: 还可以修改学生信息,添加学生表格属性等等,,,这些实现都基本类似上述的(这些不想写了,最简易的学生信息管理系统): 二:步骤 1.写一个sql脚本,包括创建数据库,使用数据库,创建学生信息表格,插入大部分学生信息. stu

《基于Node.js实现简易聊天室系列之详细设计》

一个完整的项目基本分为三个部分:前端.后台和数据库.依照软件工程的理论知识,应该依次按照以下几个步骤:需求分析.概要设计.详细设计.编码.测试等.由于缺乏相关知识的储备,导致这个Demo系列的文章层次不是很清楚,索性这一章将所有的过程(前后端以及数据库)做一个介绍,下一章写完总结就OK了吧. (1)前端部分 涉及到的技术:html.css.bootstrap.jquery.jquery UI 登录/注册界面使用的是bootstrap响应式布局,即支持不同尺寸的客户端,以此提高用户的体验.在这之前

基于OpenGL编写一个简易的2D渲染框架01——创建窗口

最近正在学习OpenGL,我认为学习的最快方法就是做一个小项目了. 如果对OpenGL感兴趣的话,这里推荐一个很好的学习网站 https://learnopengl-cn.github.io/ 我用的是 vs2013,使用C++语言编写项目.这个小项目叫Simple2D,意味着简易的2D框架.最终的目的是可以渲染几何图形和图片,最后尝试加上一个2D粒子系统和Box2D物理引擎,并编译一个简单的游戏. 第一步,就是创建一个Win32项目. 接下来,生成一个窗口.编写一个RenderWindow类,