本文由Markdown语法编辑器编辑完成。
1.背景:
最近在做关于DICOM文件脱敏的需求。要求是能够根据任意给定的DICOM TAG, 来进行脱敏。
这里的TAG, 可以是TagName, 如PatientName, InstituteName等; 也可以是按照十六进制形式给出的(Group, Element)的TagID.
当我拿到一个需要脱敏的dicom文件时,我使用dcmdump查看了一下文件都有哪些TAG, 以便测试能够将指定的Tag进行脱敏。我无意中看到了这样一个Tag, 它的TagID是(fffe, e0dd), 是一个非常罕见的Tag.

因为曾经了解过,一般dicom文件,最大的Tag, 就是存储像素信息的tag, (7fe0,0010).
但是这里却出现了一个,大于(7fe0, 0010)的Tag, 似乎跟过去的经验不符了 (https://dicomiseasy.blogspot.com/2012/08/chapter-12-pixel-data.html)。
之前关于为什么要将PixelData的tag标签置为: (7fe0, 0010), 这里有知名博主(Dicom is Easy)的一些解释。

但是也只能硬着头皮,继续处理了。
但是,很奇怪,按照相同的代码,其他的常见的Tag, 无论是采用java dcm4chee的代码,还是采用python pydicom的代码,都可以正常地脱敏。唯独对这个奇怪的Tag, 无论如何都无法将其脱敏掉。
2. 脱敏过程:
2.1 java -> dcm4chee
在dcm4chee的项目(https://github.com/dcm4che/dcm4che)中,dicom所有的Tag, 都会存储在一个叫做Attributes的类对象中。
如果要去除相应的tag, 只需要在另一个类Tag.class中找到这个TagName对应的int类型的数字,便可以调用Attributes类提供的remove方法来脱敏。



比如,如果想要将PatientName这个标签脱敏,那么只需要找到Tag.class中的PatientName对应的int值常量,便可以将其脱敏。
Attributes attr = new Attributes(...);
attr.remove(Tag.PatientID);
或
attr.remove(0x00100020);
而且,我在Tag.class中也找到了我本次想要脱敏的Tag.

但是当我采用类似上面的代码,想要去掉这个Tag时,却失败了。
Attributes attr = new Attributes(...);
attr.remove(Tag.SequenceDelimitationItem);
或
attr.remove(0xFFFEE0DD);
这到底是什么原因呢?为什么同样的代码,在这里就不生效了呢。
而且Tag.class这里的定义常量的方式,其实也感觉是有问题的。
public static final int SequenceDelimitationItem = 0xFFFEE0DD;
我们都知道int值,是有大小范围的。那么int的最大值,对应的十六进制的值,是多少呢?

而上面在定义 SequenceDelimitationItem时,对应的十六进制,已经是超过了0x7FFFFFFF。那么,对应的int值其实已经是一个负数了。这里是我不太理解的地方。
这时陷入了僵局。
转而,我想看看,用python的pydicom提供的脱敏的方法,能不能把这个tag给脱敏掉。
2.2 python -> pydicom
pydicom提供的脱敏的函数,也比较简单。主要是以下几个步骤:
import pydicom
from pydicom.tag import Tag
ds = pydicom.dcmread('test.dcm')
tag = Tag(0x00100020)
if tag in ds:
del ds[tag]
但是同样的代码,在脱敏0xFFFEE0DD时,就又报错了。

在校验这个tag, 是否属于pydicom的dcm的dataset时,直接返回了False. 而从dataset中,希望取出这个值时,也是报了KeyError的错误。
这时,我意识到了,这个Tag,和其他正常的Tag, 是有区别的。
通过咨询AI, 也确实得到了相应的答复。


上面的说法,指出类似: (FFFE, E000), (FFFE, E00D)和(FFFE, E0DD), 这几个Tag, 都是不存在于dataset中的,它们只是一个分割符。用来分割不同层级的Sequence的Tag.
明白了这几个Tag的作用后,我也就不再纠结要专门去脱敏这几个Tag了。而是,我尝试分别把它要分割的那几个Sequence的Tag给脱敏掉。当我把分割符号,前后的Tag都脱敏掉后,再去dump保存后的文件时,这几个分割符,自然也就不存在了。
从下面这幅图中可以看出,(fffe, e0dd)出现了好几次。它其实就是为了分割前后出现的,类型为SQ的Tag的。
(0029, 1140) SQ
(fffe, e0dd) na
(0032, 1060) LO
(0032, 1064) SQ
(fffe, e0dd) na
(0040, 0009) SH
(0040, 0275) SQ
(fffe, e0dd) na
(0400, 0561) SQ
......

后来,我尝试用gdcmdump, 查看之前的那张待脱敏的图像的Tag, 其实显示还是比较明显的。
可以看到,在gdcmdump解析dicom的tag后,对于这几个分割符,可以很明显得看出它们的位置和作用。

明白了分割符的作用后。在脱敏时,只需要把它要分割的,VR为SQ类型的tag脱敏掉,那么这个分割符,也就自动没有了。而且分割符,本身也没有任何的标签值,也不包含敏感信息。
3. 总结:
弄了半天,原来和一个分割符,较了好长时间的劲。这还是源于自己对于dicom tag的认识,缺少认识。通过这次脱敏的实现,也补上了这块短板。

831

被折叠的 条评论
为什么被折叠?



