分析经过j2k压缩的dicom文件经验分享

最近碰到一个问题,在网上搜到是用JPEG 2000压缩的DICOM文件

JPEG 2000对应的transfer syntax UID为

1.2.840.10008.1.2.4.91

参考:https://dicom.nema.org/medical/dicom/current/output/chtml/part18/sect_8.7.3.html

该文件是用专业德国老牌开发库DCMTK生成的

(0002,0013) SH [OFFIS_DCMTK_364]                        #  16, 1 ImplementationVersionName

怎么看的?很简单,直接用dcmtk的工具包命令dcmdump查下

dcmdump <filename>

就能dump类似如下结果

(0002,0000) UL 230                                      #   4, 1 FileMetaInformationGroupLength
(0002,0001) OB 00\01                                    #   2, 1 FileMetaInformationVersion
(0002,0002) UI =CTImageStorage                          #  26, 1 MediaStorageSOPClassUID
(0002,0003) UI [1.2.826.0.1.3680043.8.1055.1.20111102150758591.03296050.69180943] #  64, 1 MediaStorageSOPInstanceUID
(0002,0010) UI =JPEG2000                                #  22, 1 TransferSyntaxUID
(0002,0012) UI [1.2.276.0.7230010.3.0.3.6.4]            #  28, 1 ImplementationClassUID
(0002,0013) SH [OFFIS_DCMTK_364]                        #  16, 1 ImplementationVersionName
(0002,0016) AE [DICOMLIBRARY]                           #  12, 1 SourceApplicationEntityTitle

# Dicom-Data-Set
# Used TransferSyntax: JPEG 2000 (Lossless or Lossy)
(0008,0005) CS [ISO_IR 100]                             #  10, 1 SpecificCharacterSet
(0008,0008) CS [ORIGINAL\PRIMARY\AXIAL\HELIX]           #  28, 4 ImageType

......

......

(0040,0254) LO [CT1 abdomen]                           #  12, 1 PerformedProcedureStepDescription
(0040,1001) SH [A10026177757]                           #  12, 1 RequestedProcedureID
(7fe0,0010) OB (PixelSequence #=2)                      # u/l, 1 PixelData
  (fffe,e000) pi 00\00\00\00                              #   4, 1 Item
  (fffe,e000) pi ff\4f\ff\51\00\29\00\00\00\00\02\00\00\00\02\00\00\00\00\00\00\00... # 89124, 1 Item
(fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem

出现一个有意思的事情

      我用dcmtk的storescu命令将其发送给用另一个开源库实现的DICOM 服务,接受后存储为新的文件。新的文件用一个免费的DICOM Viewer (Micro DICOM Viewer)查看时提示无法打开,而用Micro DICOM Viewer直接打开原文件是正常的, 但如果用一个开源的cornerstonejs去打开这个新文件又能显示出来。

为了弄清楚原因,我开始着手调研

     首先先将新文件转成未压缩的DICOM文件看看能不能让Micro DICOM Viewer打开,发现dcmtk支持j2k转换的工具是要商业授权的

Two of the DICOM JPEG 2000 transfer syntaxes are supported ("JPEG 2000 Image Compression (Lossless Only)" and "JPEG 2000 Image Compression").

Further details can be found in the online documentation for the tools DCMCJP2K and DCMDJP2K.

DCMJP2K is based on the OFFIS DICOM toolkit DCMTK and is available in different versions: There is a binary version as well as a source code variant. DCMJP2K supports several Unix dialects (e. g. Linux, Solaris and Mac OS X) as well as Windows.

This module is not freely available and must be licensed separately.

来源:https://dcmtk.org/en/dcmtk-expansion-modules/dcmjp2k/

于是我一顿操作猛如虎,找到免费平替

gdcmconv --raw file-in file-out

gdcmconv: Tool to convert dicom to dicom.

这个工具是grassroot dicom社区开发,在sourceforge有代码地址,开源有点历史了。

https://sourceforge.net/projects/gdcm/

于是对Micro DICOM Viewer打不开的压缩文件,用gdcmconv转成未压缩文件后,发现可以用micro dicom viewer打开,说明图像本身没有问题。

于是我反向操作,再压缩回J2K

 gdcmconv --j2k --lossy file-in file-out

这个时候发现压缩回的文件,micro dicom viewer能够打开。这就说明那个开源DICOM服务保存后的压缩图像格式可能与原图及gdcmconv的处理不一致。

于是我对比下两个文件的区别

dcmdump工具再次出手,经过对比发现新文件的pixel data和原文件的pixel data有一点差异:

原文件

(7fe0,0010) OB (PixelSequence #=2)                      # u/l, 1 PixelData
  (fffe,e000) pi 00\00\00\00                              #   4, 1 Item
  (fffe,e000) pi ff\4f\ff\51\00\29\00\00\00\00\02\00\00\00\02\00\00\00\00\00\00\00... # 89124, 1 Item
(fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem

新文件 

(7fe0,0010) OB (PixelSequence #=6)                      # u/l, 1 PixelData
  (fffe,e000) pi 00\00\00\00                              #   4, 1 Item
  (fffe,e000) pi ff\4f\ff\51\00\29\00\00\00\00\02\00\00\00\02\00\00\00\00\00\00\00... # 20480, 1 Item
  (fffe,e000) pi 5b\51\b7\69\ef\8b\ea\28\44\43\39\1c\67\a0\70\eb\2f\73\bf\91\ea\c0... # 20480, 1 Item
  (fffe,e000) pi 82\1c\7c\94\74\3e\c0\63\a0\81\2d\ee\d2\85\d1\30\02\a1\86\6f\2d\f1... # 20480, 1 Item
  (fffe,e000) pi 74\08\37\dd\88\7b\bd\9d\98\9e\2f\6f\19\1f\40\00\ca\26\7f\17\59\68... # 20480, 1 Item
  (fffe,e000) pi f5\0c\47\98\ba\8f\08\63\8d\e4\a3\31\10\2d\bc\7d\4f\4d\e7\0a\7b\11... # 7204, 1 Item
(fffe,e0dd) na (SequenceDelimitationItem)               #   0, 0 SequenceDelimitationItem
 

 原文件只有两段pixel sequence,新文件有6段pixel sequence, 第一个段的内容4个字节都是0可以忽略。新文件把老文件的第二段共89124个字节的内容拆成了5段。

为了验证是否该差异导致,决定二进制修改Pixel Data(自我感觉很牛逼啊)

用vscode装个hex editor插件

用hex editor打开新文件,如下图

点击Ctrl+F打开文件内搜索,选中那个0101的图标表示按二进制内容搜索,

在输入框输入  e07f1000搜到pixel data所在的位置

 解释下为什么是搜素 e07f1000

pixel data的tag是 (7fe0,0010), 因为文件用的是little endian explicit, 需要把高低字节换过来进行搜索, 即7fe0 --> e07f, 0010 --> 1000

上面截图分析可以看到 TAG后面跟上的是VR(OB), 这是OB的官方定义

参考:https://dicom.nema.org/medical/dicom/current/output/chtml/part05/sect_6.2.html#table_6.2-1 

 关于Explicit VR的编码格式定义如下:

 参考https://dicom.nema.org/medical/dicom/current/output/html/part05.html#chapter_7

VR后面跟着4个字节的Value Length, 从截图可以看到是4个FF, 这个表示未知长度,需要靠sequence delimitation item去定义, 就是说用分割符号来判断。

刚才所说的分割符号其实就是指SQ(sequence),  (fffe,00e0)也就是SQ的开头标记,如上截图标记,后面跟着4字节长度,第一段是4个字节,跟着4个00, 第二段是20480个字节,如此类推。

那我要做的就是把第二段的长度:20480改成89124,也就是原文件的第二段的长度,然后再把第三段到第六段的(fffe,00e0)标记及其长度4个字节全部删除掉。这样就做到了和原文件一样的两个SQ段。按照这个思路改完保存后,果不其然,micro dicom viewer能正常打开了。

总结

    原始文件的pixel data是两段SQ,第一段4个字节的00我没搞懂有什么意义,但第二段的内容是完整的JPEG 2000压缩图像。而开源DICOM服务将第二段这个完整的JPEG 2000图像切成了5个SQ来保存,也不能说不符合DICOM标准,你看人家gdcmconv能够正常解压转换,cornerstonejs也能打开显示。

    因此可能还是micro dicom viewer兼容性不够,不能支持到这种将完整jpeg2000图像大卸八块的搞法,作为DICOM服务这端在保存文件时还是应尽量不去改变原文件的格局,都放在一个SQ里保存会具有更好的兼容性。

    

相关推荐

  1. 考车经验分享

    2024-04-21 23:02:03       49 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-21 23:02:03       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-21 23:02:03       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-21 23:02:03       87 阅读
  4. Python语言-面向对象

    2024-04-21 23:02:03       96 阅读

热门阅读

  1. JUC之线程、并发、上下文基本概念

    2024-04-21 23:02:03       35 阅读
  2. Multiprocessing Freeze Support in Python

    2024-04-21 23:02:03       26 阅读
  3. LeetCode题练习与总结:编辑距离--72

    2024-04-21 23:02:03       35 阅读
  4. 卷积层、池化层和全连接层的作用分别是什么

    2024-04-21 23:02:03       38 阅读
  5. CentOS常见的命令用法和示例

    2024-04-21 23:02:03       37 阅读
  6. 使用 hiredis 客户端库封装一个简单的 Redis 类

    2024-04-21 23:02:03       37 阅读