DicomTagSeeker - DICOM 存储库标签搜索器





5.00/5 (12投票s)
一个用于在相当大的 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 以外的其他图像文件的图像搜索技术。大多数图像格式都包含一些标签,这些标签对于特定的基于标签的搜索需求非常重要。