65.9K
CodeProject 正在变化。 阅读更多。
Home

DicomTagSeeker - DICOM 存储库标签搜索器

starIconstarIconstarIconstarIconstarIcon

5.00/5 (12投票s)

2013年4月19日

CPOL

4分钟阅读

viewsIcon

31618

downloadIcon

3171

一个用于在相当大的 DICOM 存储库中搜索、排序和报告的工具

介绍 

DICOM(医学影像数字化和通信)是用于处理、存储、打印和传输医学影像信息的标准。它包括文件格式定义和网络通信协议。通信协议是使用 TCP/IP 在系统之间进行通信的应用协议。DICOM 文件可以在能够接收 DICOM 格式图像和患者数据的两个实体之间交换。美国国家电气制造商协会 (NEMA) 拥有此标准的版权。它由 DICOM 标准委员会制定,其成员 [2] 也部分是 NEMA 的成员。

DICOM 数据格式 

DICOM 与某些(但并非所有)数据格式不同,因为它将信息分组到数据集中。这意味着,例如,一张胸部 X 光图像的文件实际上包含文件内的患者 ID,因此图像永远不会与此信息错误地分开。这类似于 JPEG 等图像格式也可以嵌入标签来识别和描述图像的方式。

DICOM 数据对象由多个属性组成,包括姓名、ID 等项目,以及一个包含图像像素数据的特殊属性(即,逻辑上,主对象本身没有“头”:只是一系列属性,包括像素数据)。单个 DICOM 对象只能有一个包含像素数据的属性。对于许多模态,这对应于单个图像。但请注意,该属性可能包含多个“帧”,允许存储电影循环或其他多帧数据。另一个例子是 NM 数据,其中 NM 图像按定义是多维多帧图像。在这些情况下,三维或四维数据可以封装在单个 DICOM 对象中。像素数据可以使用多种标准进行压缩,包括 JPEG、JPEG 无损、JPEG 2000 和行程长度编码 (RLE)。LZW(zip)压缩可用于整个数据集(不仅仅是像素数据),但这很少实现。

DICOM 使用三种不同的数据元素编码方案。对于显式值表示 (VR) 数据元素,对于非 OB、OW、OF、SQ、UT 或 UN 的 VR,每个数据元素的格式为:组(2 字节)元素(2 字节)VR(2 字节)字节长度(2 字节)数据(可变长度)。对于其他显式数据元素或隐式数据元素,请参阅 DICOM 标准第 5 部分第 7.1 节。

所有应用程序使用相同的基本格式,包括网络和文件使用,但在写入文件时,通常会添加一个真正的“头”(包含一些关键属性的副本以及写入它的应用程序的详细信息)。

来源: http://en.wikipedia.org/wiki/DICOM 

背景 

在医学影像平台测试中,通常需要搜索本地图像池(无论是网络文件夹、版本控制系统还是其他数据源)中具有特定 DICOM 属性(组-元素 TAG)的图像。这些路径的最大大小可达 TB 级(由于 DICOM 图像的巨大文件大小),目录数量达到数万个,甚至更多。

看起来,检查所有文件和目录只是为了找到我们需要的那些,非常困难、极其耗时且容易出错。此类任务有时需要数小时甚至数天。有时搜索会失败,因为没有图像,或者由于其他关键任务或单调乏味而放弃。对于人类来说,这不仅繁琐乏味,而且对团队或项目来说也效率低下。

该工具消除了维护大型 DICOM 数据库的障碍。它是一种多线程解决方案,可以查看所有/选定的 DCM 文件,并提取信息用于报告、制表或简单地查找具有特定属性(组-元素标签)的文件。

此工具使用 OpenDicom.NET 作为 dicom 文件解析库。

使用代码 

该工具优于其他 dicom 搜索器的方面:

a) 大多数可用工具是索引搜索,它在上述路径的预先准备好的图像索引上工作。难怪它不能泛化到查找任何地方的图像。

b) 没有搜索模式(如 FirstFile/All Files)。例如,对于特定搜索,我们会获得所有文件作为结果。如果一个路径包含 2000 个文件,我们都会得到它们作为结果,而我们只需要一个来获取路径。这会将其他所需的结果隐藏在 100 页之后。

c) 它是

  • 体积小
  • 可移植
  • 高效
  • 用户友好
  • 可配置

用法 

步骤 1:输入要搜索图像的路径

步骤 2:输入一个文本文件名,结果将转储到该文件

步骤 3:选择要搜索的属性列表

步骤 4:点击 BeginFind(异步搜索路径中的图像)

步骤 5(可选):在工具搜索时,有一个 EndFind 选项可以在中途终止进程;如果用户对迄今为止列出的结果满意

结果显示如下(相当原始,但足以找到您的文件)

 

该范围内的主要类是 Finder 类。这是代码片段:

namespace ImageFinder
{
    public delegate void FindEventHandler();

    enum SearchMethod
    {
        FirstFile,
        AllFiles
    }

    enum OtherInfo
    {
        None = 0,
        FileSize = 1,
        FileCount = 2
    }

    class Finder
    {
        // Live tested data sample where the tool works
        //\\network1\path1 383 GB
        //\\network2\path2 67.4 GB 
        //\\network3\path3 14.2 GB


        private Tag GetGroupElement(string tag)
        {
            string[] str = tag.Split(new char[] { ',' });
            return new Tag(int.Parse(str[0], NumberStyles.HexNumber),
                int.Parse(str[1], NumberStyles.HexNumber));
        }

        private string GetValueString(Value value)
        {
            string substr = "";
            switch (value.VR.Name)
            {
                default:
                    for (int k = 0; k < value.Count; k++)
                    {
                        substr += value[k].ToString();
                        if (k < value.Count - 1)
                        {
                            substr += "/";
                        }
                    }
                    break;
            }
            return substr;
        }

        private string ReadFile(FileInfo fileInfo)
        {
            if (!myString.Contains(fileInfo.DirectoryName) || mySearchMethod == SearchMethod.AllFiles)
            {
                string aValueString = "";

                AcrNemaFile file = null;
                try
                {
                    if (DicomFile.IsDicomFile(fileInfo.FullName))
                    {
                        file = new DicomFile(fileInfo.FullName, false);
                    }
                    else if (AcrNemaFile.IsAcrNemaFile(fileInfo.FullName))
                    {
                        file = new AcrNemaFile(fileInfo.FullName, false);
                    }
                    else
                    {
                        //Console.WriteLine("Selected file is wether a DICOM nor an ACR-NEMA file.");
                        return "Unknown File Format Exception";
                    }
                }
                catch (Exception dicomFileException)
                {
                    //Console.WriteLine("Problems processing DICOM file" + dicomFileException);
                    return "Unknown File Format Exception";
                }

                for (int j = 0; j < myItems.Count; j++)
                {
                    if (myItems[j] == "7FE0,0010")
                    {
                        Tag tag = GetGroupElement(myItems[j]);
                        try
                        {
                            DataElement elem = file.DataSet[tag];
                            aValueString += "Length = " + elem.ValueLength.ToString() + mySeparator;
                        }
                        catch
                        {
                            aValueString += "--" + mySeparator;
                        }
                    }
                    else if (myItems[j] == "0002,0010")
                    {
                        aValueString += file.GetJointDataSets().TransferSyntax + mySeparator;
                    }
                    else
                    {
                        try
                        {
                            Tag tag = GetGroupElement(myItems[j]);
                            if (tag != null)
                            {
                                DataElement elem = file.DataSet[tag];
                                aValueString += GetValueString(elem.Value) + mySeparator;
                            }
                            else
                            {
                                aValueString += "??" + mySeparator;
                            }

                        }
                        catch
                        {
                            aValueString += "--" + mySeparator;
                        }
                    }
                }

                myString.Add(fileInfo.DirectoryName);
                if (mySearchMethod == SearchMethod.FirstFile)
                {
                    // WARNING : Cross Thread Operation
                    if (myProgressIndicator.Value < myProgressIndicator.Maximum)
                    myProgressIndicator.Value++;
                    // WARNING : Cross Thread Operation
                    return aValueString;
                }
                else
                {
                    if (myShowAllCondition == true || IsFound(aValueString))
                    {
                        string writeString = fileInfo.FullName;

                        if ((myOtherInfoRequired &= OtherInfo.FileSize) != 0)
                        {
                            writeString = writeString + mySeparator + fileInfo.Length;
                        }
                        if ((myOtherInfoRequired & OtherInfo.FileCount) != 0)
                        {
                            writeString = writeString + mySeparator + "1";
                        }
                        // WARNING : Cross Thread Operation
                        if (myProgressIndicator.Value < myProgressIndicator.Maximum)
                        myProgressIndicator.Value++;
                        // WARNING : Cross Thread Operation
                        myOutStream.WriteLine(writeString + mySeparator + aValueString);
                        myOutStream.Flush();
                    }
                }
            }

            return string.Empty;
        }      
    }
}

关注点

基于标签的搜索理念可以很好地扩展到 DICOM 以外的其他图像文件的图像搜索技术。大多数图像格式都包含一些标签,这些标签对于特定的基于标签的搜索需求非常重要。

© . All rights reserved.