使用 tensorflow_io 将 DICOM 图像转换为 pixel_array 时出错
Error when converting DICOM image to pixel_array using tensorflow_io
我正在尝试使用 tf.data API 和 tensorflow_io 从 DICOM 图像创建 TensorFlow 数据集,我想使用图像中的 Hounsfield 单位执行一些预处理. DICOM 图像的形状为 (512,512)。我已经从图像中提取了 PixelData 并希望使用以下代码将其转换为适当形状的 numpy 数组:
image_bytes = tf.io.read_file(image_path)
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.frombuffer(PixelData, dtype=tf.uint16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
这段代码应该等同于
Image = pydicom.dcmread(image_path)
pixel_array = Image.pixel_array
print(pixel_array)
和
Image = pydicom.dcmread(image_path)
PixelData = Image.PixelData
pixel_array = np.frombuffer(PixelData, dtype=np.uint16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
DICOM 标签与 pydicom 使用的标签相同,并给出 here。 PixelData 应该 return DICOM 图像的原始字节值。我已经通过 pydicom 确认原始像素数据存储为 np.uint16 值。但是,当我尝试使用 np.frombuffer 函数将 tensorflow 给出的字节数据转换为 numpy 数组时,出现缓冲区大小不能被元素长度整除的错误。
当我运行上述脚本时,这些是以下输出形状
- Tensorflow:不 运行 使用 tf.uint16,在使用 tf.uint8
时给出输出形状 (1310719,)
- Pydicom 直接 pixel_array:输出形状 (512,512)
- Pydicom PixelData 到 pixel_array:输出形状 (512,512)
pydicom 示例在两种情况下提供相同的输出,但是 tensorflow DICOM 标签似乎提供完全不同的结果。请查找随附的示例 DICOM 文件 here。库或我的实现有问题吗?
编辑:
DICOM 图像实际上是有符号的 16 位整数,而不是无符号的。因此,以下三个代码片段产生相同的输出:
直接从 pydicompixel_array
import pydicom
import numpy as np
dcm = pydicom.dcmread("ID_000012eaf.dcm")
print(dcm.pixel_array)
手动将 PixelData 转换为 pixel_array
import pydicom
import numpy as np
dcm = pydicom.dcmread("ID_000012eaf.dcm")
PixelData = dcm.PixelData
pixel_array = np.frombuffer(PixelData, dtype=np.int16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
使用tensorflow_io
直接获取pixel_array
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
pixel_array = tfio.image.decode_dicom_image(image_bytes, on_error='lossy', scale='preserve', dtype=tf.float32).numpy()
pixel_array = pixel_array.astype('int16')
pixel_array /= 2.
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
但是,由于某些原因,这最后一段代码仍然不起作用:
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.frombuffer(PixelData, dtype=np.int16)
print(pixel_array)
编辑 2:这两个代码片段理论上应该可以工作,但是它们显示错误,即字节字符串的长度不能被 int16 的大小整除:
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.frombuffer(PixelData, dtype=np.int16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
和
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData)
pixel_array = tf.io.decode_raw(PixelData, tf.int16)
pixel_array = tf.reshape(pixel_array, (512,512))
print(pixel_array)
编辑3:得到decode_dicom_data提供的字节串包含十六进制值的提示后,我找到了一种方法将我的数据转换成所需的pixel_array,但我很好奇为什么PixelData 以这种方式存储:
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.zeros(262144, dtype=np.int16)
start,stop = 0,4
for i in range(262144):
pixel_array[i] = int(PixelData[start:stop], base=16)
start+=5
stop+=5
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
来自 pydicom 的像素数据:
PixelData = b'0\xf80\xf80\xf80...'
来自 Tensorflow_io
的像素数据
PixelData = b'f830\f830\f830\...'
任何有关代码重构和 linting 的建议都将不胜感激。非常感谢@ai2ys 帮助我诊断这些问题。
函数tfio.image.decode_dicom_data
解码标签信息而不是像素信息。
要读取像素数据,请改用 tfio.image.decode_dicom_image
。
import tensorflow_io as tfio
image_bytes = tf.io.read_file(image_path)
pixel_data = tfio.image.decode_dicom_image(
image_bytes,
dtype=tf.uint16)
# type conversion and reshaping is not required
# as can be checked with the print statement
print(pixel_data.dtype, pixel_data.shape)
# if required the pixel_data can be converted to a numpy array
# but calculations like scaling and offset correction can
# be done on tensors as well
pixel_data_nparray = pixel_data.numpy()
# reading tag information, e.g. rescale intercept and slope
intersept = tfio.image.decode_dicom_data(
image_bytes,
tfio.image.dicom_tags.RescaleIntercept)
slope = tfio.image.decode_dicom_data(
image_bytes,
tfio.image.dicom_tags.RescaleSlope)
print(intersept)
print(slope)
请查看文档以获取更多信息:
- https://www.tensorflow.org/io/api_docs/python/tfio/image/decode_dicom_data
- https://www.tensorflow.org/io/api_docs/python/tfio/image/decode_dicom_image
使用共享文件编辑 2021-02-01:
也可以使用 tfio.image.decode_dicom_data
并传递 tfio.image.dicom_tags.PixelData
来读取像素数据,但是必须对返回的字节字符串进行解码。
data = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData)
print(data)
输出(缩短):
tf.Tensor(b'f830\f830\f830\f830\ ...')
被解释为 int16
的十六进制值 f830
是 -2000
。
我发现了问题:
我拥有的图像是带符号的 16 位整数数据类型,但 tensorflow_io 库中不存在此类选项。将数组值转换为 16 位有符号数后,问题就解决了。我必须在 decode_dicom_image 函数中将数据转换为更高的数据类型,如 float32,在 numpy 中重新转换为 signed int16,最后除以 2(不确定为什么最后一步),但我最终得到了 pixel_array 与 pydicom 的输出相同。
现在除了从 dicom_tag PixelData 转换数据外,一切都有意义了,它仍然显示无法解释的行为。我已经更新了这里的 python 脚本,为任何感兴趣的人展示了不同库 here 的不同 DICOM 图像转换方法。
我正在尝试使用 tf.data API 和 tensorflow_io 从 DICOM 图像创建 TensorFlow 数据集,我想使用图像中的 Hounsfield 单位执行一些预处理. DICOM 图像的形状为 (512,512)。我已经从图像中提取了 PixelData 并希望使用以下代码将其转换为适当形状的 numpy 数组:
image_bytes = tf.io.read_file(image_path)
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.frombuffer(PixelData, dtype=tf.uint16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
这段代码应该等同于
Image = pydicom.dcmread(image_path)
pixel_array = Image.pixel_array
print(pixel_array)
和
Image = pydicom.dcmread(image_path)
PixelData = Image.PixelData
pixel_array = np.frombuffer(PixelData, dtype=np.uint16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
DICOM 标签与 pydicom 使用的标签相同,并给出 here。 PixelData 应该 return DICOM 图像的原始字节值。我已经通过 pydicom 确认原始像素数据存储为 np.uint16 值。但是,当我尝试使用 np.frombuffer 函数将 tensorflow 给出的字节数据转换为 numpy 数组时,出现缓冲区大小不能被元素长度整除的错误。
当我运行上述脚本时,这些是以下输出形状
- Tensorflow:不 运行 使用 tf.uint16,在使用 tf.uint8 时给出输出形状 (1310719,)
- Pydicom 直接 pixel_array:输出形状 (512,512)
- Pydicom PixelData 到 pixel_array:输出形状 (512,512)
pydicom 示例在两种情况下提供相同的输出,但是 tensorflow DICOM 标签似乎提供完全不同的结果。请查找随附的示例 DICOM 文件 here。库或我的实现有问题吗?
编辑: DICOM 图像实际上是有符号的 16 位整数,而不是无符号的。因此,以下三个代码片段产生相同的输出:
直接从 pydicompixel_array
import pydicom
import numpy as np
dcm = pydicom.dcmread("ID_000012eaf.dcm")
print(dcm.pixel_array)
手动将 PixelData 转换为 pixel_array
import pydicom
import numpy as np
dcm = pydicom.dcmread("ID_000012eaf.dcm")
PixelData = dcm.PixelData
pixel_array = np.frombuffer(PixelData, dtype=np.int16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
使用tensorflow_io
直接获取pixel_arrayimport tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
pixel_array = tfio.image.decode_dicom_image(image_bytes, on_error='lossy', scale='preserve', dtype=tf.float32).numpy()
pixel_array = pixel_array.astype('int16')
pixel_array /= 2.
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
但是,由于某些原因,这最后一段代码仍然不起作用:
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.frombuffer(PixelData, dtype=np.int16)
print(pixel_array)
编辑 2:这两个代码片段理论上应该可以工作,但是它们显示错误,即字节字符串的长度不能被 int16 的大小整除:
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.frombuffer(PixelData, dtype=np.int16)
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
和
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData)
pixel_array = tf.io.decode_raw(PixelData, tf.int16)
pixel_array = tf.reshape(pixel_array, (512,512))
print(pixel_array)
编辑3:得到decode_dicom_data提供的字节串包含十六进制值的提示后,我找到了一种方法将我的数据转换成所需的pixel_array,但我很好奇为什么PixelData 以这种方式存储:
import tensorflow as tf
import tensorflow_io as tfio
import numpy as np
image_bytes = tf.io.read_file("ID_000012eaf.dcm")
PixelData = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData).numpy()
pixel_array = np.zeros(262144, dtype=np.int16)
start,stop = 0,4
for i in range(262144):
pixel_array[i] = int(PixelData[start:stop], base=16)
start+=5
stop+=5
pixel_array = np.reshape(pixel_array, (512,512))
print(pixel_array)
来自 pydicom 的像素数据:
PixelData = b'0\xf80\xf80\xf80...'
来自 Tensorflow_io
的像素数据PixelData = b'f830\f830\f830\...'
任何有关代码重构和 linting 的建议都将不胜感激。非常感谢@ai2ys 帮助我诊断这些问题。
函数tfio.image.decode_dicom_data
解码标签信息而不是像素信息。
要读取像素数据,请改用 tfio.image.decode_dicom_image
。
import tensorflow_io as tfio
image_bytes = tf.io.read_file(image_path)
pixel_data = tfio.image.decode_dicom_image(
image_bytes,
dtype=tf.uint16)
# type conversion and reshaping is not required
# as can be checked with the print statement
print(pixel_data.dtype, pixel_data.shape)
# if required the pixel_data can be converted to a numpy array
# but calculations like scaling and offset correction can
# be done on tensors as well
pixel_data_nparray = pixel_data.numpy()
# reading tag information, e.g. rescale intercept and slope
intersept = tfio.image.decode_dicom_data(
image_bytes,
tfio.image.dicom_tags.RescaleIntercept)
slope = tfio.image.decode_dicom_data(
image_bytes,
tfio.image.dicom_tags.RescaleSlope)
print(intersept)
print(slope)
请查看文档以获取更多信息:
- https://www.tensorflow.org/io/api_docs/python/tfio/image/decode_dicom_data
- https://www.tensorflow.org/io/api_docs/python/tfio/image/decode_dicom_image
使用共享文件编辑 2021-02-01:
也可以使用 tfio.image.decode_dicom_data
并传递 tfio.image.dicom_tags.PixelData
来读取像素数据,但是必须对返回的字节字符串进行解码。
data = tfio.image.decode_dicom_data(image_bytes, tfio.image.dicom_tags.PixelData)
print(data)
输出(缩短):
tf.Tensor(b'f830\f830\f830\f830\ ...')
被解释为 int16
的十六进制值 f830
是 -2000
。
我发现了问题: 我拥有的图像是带符号的 16 位整数数据类型,但 tensorflow_io 库中不存在此类选项。将数组值转换为 16 位有符号数后,问题就解决了。我必须在 decode_dicom_image 函数中将数据转换为更高的数据类型,如 float32,在 numpy 中重新转换为 signed int16,最后除以 2(不确定为什么最后一步),但我最终得到了 pixel_array 与 pydicom 的输出相同。
现在除了从 dicom_tag PixelData 转换数据外,一切都有意义了,它仍然显示无法解释的行为。我已经更新了这里的 python 脚本,为任何感兴趣的人展示了不同库 here 的不同 DICOM 图像转换方法。