[疑难杂症2024-003]如何判断一张没有头信息的dcm图像,是否是压缩图像?

本文由Markdown语法编辑器编辑完成.

写作时间跨度: 4月17 ~ 4.24日
字数:3598.

1. 前言:

DCM格式,是医学图像领域里面的通用格式.DCM图像一般分为两大部分,一部分是TAG信息,一部分是像素.

而TAG信息,一般又会分为两部分,如下图所示, 是用dcmtk下面的dcmdump指令,来读图任意一张正常的dcm图像后,展示的tag信息.
可以看到,主要分为两部分:
Dicom-Meta-Information-Header和DICOM-Data-Set.
在这里插入图片描述
DICOM-Meta-Information-Header, 是dicom头,一般是0002开头的Element.
这里面, TransferSyntaxUID, 是用来标识这个图像的传输方式.

以下是对Transfer Syntax UID的一个说明:

来源于: https://www.dicomlibrary.com/dicom/transfer-syntax/Transfer Syntax
A Transfer Syntax is a set of encoding rules able to unambiguously represent one or more Abstract Syntaxes. 
In particular, it allows communicating Application Entities to negotiate common encoding techniques they both support (e.g., byte ordering, compression, etc.).
A Transfer Syntax is an attribute of a Presentation Context, one or more of which are negotiated at the establishment of an Association between DICOM Application Entities.

这里面提到了,它用来标识图像的字节排序,压缩算法,等等。

今天要提到的这个解决方案,就是跟dicom的Trasnfer Syntax UID取值相关的。

从以上的定义,我们可以知道,如果想知道,一张dcm图像,到底是不是压缩图像,我们可以很方便地根据这个tag来识别。
以下是我分别读取一张标准的(非压缩图),和压缩图,读取图像后的区别。

# 读取正常图像
import pydicom
file_1 = "/path/to/normal"
ds1 = pydicom.read_file(file_1)
print(ds1.file_meta.TransferSyntaxUID.is_compressed)

file_2 = "/path/to/compress"
ds2 = pydicom.read_file(file2)
print(ds2.file_meta.TransferSyntaxUID.is_compressed)

但是,今天遇到的问题,却是,这个图像没有头信息,所以使用pydicom读取后,没有file_meta, 当然也就没有TransferSyntaxUID. 由于后续的服务,只能在原图上面进行。

因此,我需要做的事情就是,在没有file_meta的情况下,依然能够根据图像是否是压缩图像来解压。

2. 解决方案

2.1 TAG说明及判断图像压缩方法

经过一系列的调研,dicom的TAG中,还是会有一些,可以帮助我们来推算出图像的压缩类型。
以下列举这些有用的dicom, 以及计算方法:

DICOM TAG含义取值
BitsAllocated
Rows
Columns
SamplesPerPixelNumber of samples (planes) in this image
NumberOfFramesNumber of frames in a Multi-frame Image, 一张图像内部包含多少帧.

根据一张dicom图像里面,读取到的这些tag值,我们就可以估算出,如果图像未经过任何压缩算法,它的像素所占的字节数是多少。
算法是什么呢?

import pydicom
import math

file_path = '/path/to/dicom_file'
ds = pydicom.read_file(file_path)

# 备注:大部分图像,可能读取图像后,无法读取到NumberOfFrames这个tag值,它主要适用于超声,DSA等类型的图像;
# 因此,实际对于CT等图像,计算时,可以把 ds.NumberOfFrames替换成1.
expect_len = ds.Columns * ds.Rows * ds.SamplesPerPixel * int(math.ceil(ds.BitsAllocated / 8.0)) * ds.NumberOfFrames

另一方面,使用pydicom读取图像后,还有一个tag, PixelData, 就是该图像像素所实际占用的字节长度。当然,也是未经压缩的。

因此,当通过比较这两个值,是否相等,我们便可以判断出,当前这张图像,是否是压缩格式的了。

is_compressed = False
if ds.Columns * ds.Rows * ds.SamplesPerPixel * int(math.ceil(ds.BitsAllocated / 8.0)) * ds.NumberOfFrames == len(ds.PixelData)
	is_compressed = False
else:
	is_compressed = True

如果是压缩图像,那么期望的像素长度,会大于当前实际占用的像素长度。

比如我目前手头有两张dcm图像,一张是像素正常的图像,一张是压缩图像,那么通过读取它们的tag, 并且进行计算可以验证一下上面的判断,是否准确.

正常的dcm图像:

Tag name正常dcm压缩dcm
Rows512512
Columns512512
SamplesPerPixel11
BitsAllocated1616
NumberOfFrames--
PixelData524288168260

在这里插入图片描述

可以看出,如果是正常图像:

预期像素长度:
ds.Columns * ds.Rows * ds.SamplesPerPixel * int(math.ceil(ds.BitsAllocated / 8.0)) * ds.NumberOfFrames = 512*512*1*16/8*1 =  524288

实际像素长度:
len(ds.PixelData) = 524288

2.2 压缩图像解压:

在python中,常用的解压工具,是gdcm.
它既提供了pypi包,可以在import后,直接调用它的解压函数;也可以直接在shell中,通过使用gdcm工具自带的解压工具,进行解压。

2.2.1 基于python包的解压:

一般如果依赖gdcm的python package时,还会同时依赖两个package包。只有这三个包都安装到容器里面,才可以正常的解压。
在这里插入图片描述
其中第22行,就是引入gdcm这个库函数以后,就可以在根据TransferSyntaxUID, 是压缩图像后,调用ds.decompress()函数进行解压。解压后,再把它保存到原始路径下,即可以覆盖原来的压缩图像,得到解压后的图像。

2.2.2 基于shell应用程序的解压:
gdcmconv -i input_compressed.dcm -o output_decompressed.dcm -W --raw

在安装了gdcm工具后,可以运行以上命令,得到解压后的图像。
如果想直接解压,可以将input_compressed.dcm和output_decompressed.dcm的路径和文件名保持一致,即可以就地解压。
更多的gdcm的命令,可以参考链接:https://manpages.ubuntu.com/manpages/jammy/man1/gdcmconv.1.html

(完)

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

inter_peng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值