numpy Pydicom错误while pydicom file - ValueError:数据集中像素数据的长度与预期长度不匹配

nlejzf6q  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(126)

我是一个新的工作与DICOM文件。
我想调整非正方形DICOM图像的大小,例如,512 x 768像素,并将其重新缩放为512 x 512,而不压缩或拉伸任何尺寸。因此,这需要将最大维度设置为512,同时保留纵横比,然后沿最短维度沿着填充零。
在使用pydicom python包阅读dicom文件后,我采用了numpy和PIL resize方法来完成此操作。此代码成功运行并存储输出文件。
但是,当我尝试单独访问输出dicom文件pixel_array以查看CT图像时,它向我抛出以下错误-

Traceback (most recent call last):
  File "/Users/abhimanyu/Maastricht/Internship/medical_imaging/read_dicom_file.py", line 32, in <module>
    arr = ds.pixel_array
          ^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pydicom/dataset.py", line 1955, in pixel_array
    self.convert_pixel_data()
  File "/opt/homebrew/lib/python3.11/site-packages/pydicom/dataset.py", line 1512, in convert_pixel_data
    self._convert_pixel_data_without_handler()
  File "/opt/homebrew/lib/python3.11/site-packages/pydicom/dataset.py", line 1624, in _convert_pixel_data_without_handler
    raise last_exception  # type: ignore[misc]
    ^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pydicom/dataset.py", line 1604, in _convert_pixel_data_without_handler
    self._do_pixel_data_conversion(handler)
  File "/opt/homebrew/lib/python3.11/site-packages/pydicom/dataset.py", line 1631, in _do_pixel_data_conversion
    arr = handler.get_pixeldata(self)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pydicom/pixel_data_handlers/numpy_handler.py", line 221, in get_pixeldata
    raise ValueError(
ValueError: The length of the pixel data in the dataset (262144 bytes) doesn't match the expected length (524288 bytes). The dataset may be corrupted or there may be an issue with the pixel data handler.

我调整大小的函数看起来像这样-

import pydicom
from pydicom.dataset import FileMetaDataset
from PIL import Image
import numpy as np

def resize_dicom(input_path, output_path, target_size):
    """
    Resize a DICOM image while preserving aspect ratio and padding with zeros.
    
    Args:
        input_path (str): Path to the input DICOM file.
        output_path (str): Path to the output DICOM file.
        target_size (tuple): Desired size as (width, height).
    """
    # Load the DICOM file
    dicom_data = pydicom.dcmread(input_path)
    
    # Extract pixel data and convert it to a NumPy array
    pixel_data = dicom_data.pixel_array
    
    # Calculate the desired aspect ratio
    aspect_ratio = dicom_data.Columns / dicom_data.Rows
    
    # Determine the scaling factor to maintain aspect ratio
    target_width, target_height = target_size
    if target_width / aspect_ratio <= target_height:
        new_width = int(target_width)
        new_height = int(target_width / aspect_ratio)
    else:
        new_width = int(target_height * aspect_ratio)
        new_height = int(target_height)

    # Resize the image using PIL
    img = Image.fromarray(pixel_data)
    img = img.resize((new_width, new_height))
    
    # Create a new NumPy array for the resized image
    resized_pixel_data = np.zeros([target_height, target_width], dtype=np.uint8)
    
    # Copy the resized image into the center of the new NumPy array
    x_offset = (target_width - new_width) // 2
    y_offset = (target_height - new_height) // 2
    resized_pixel_data[y_offset:y_offset + new_height, x_offset:x_offset + new_width] = np.array(img)

    # Update DICOM metadata for the resized image
    file_meta = FileMetaDataset()
    p_new = pydicom.Dataset()
    p_new.file_meta = file_meta
    exclude_tags = [(0x0028, 0x0010), (0x0028, 0x0011), (0x7FE0, 0x0010)]
    for elem in dicom_data:
        if elem.tag not in exclude_tags:
            p_new.add(elem)

    p_new.is_little_endian = True
    p_new.SOPInstanceUID = '{}.9999'.format(p_new.SOPInstanceUID)
    p_new.file_meta.TransferSyntaxUID = pydicom.uid.ImplicitVRLittleEndian
    p_new.is_implicit_VR = True
    p_new.Rows = target_width
    p_new.Columns = target_height
    p_new.PixelData = resized_pixel_data.tobytes()

    p_new.save_as(output_path)

我尝试读取输出dicom文件,就像-

ds = dcmread(file, force=True)
print(ds.Rows, ds.Columns) # This works fine. It gives the correct result
arr = ds.pixel_array # <-- This is where I get the error
plt.imshow(arr, cmap="gray")
plt.show()

我肯定我做错了,而保存文件,因为它已损坏。尽管打印正确,但仍会显示“列”和“列”。
在错误中,它显示length of the pixel data in the dataset (262144 bytes) = 512*512,但expected length (524288 bytes) = 512*512*2。我不明白这是怎么回事。
你能帮我解决这个错误吗?

lstz6jyr

lstz6jyr1#

由于您处理的是CT图像,因此每个像素很可能由超过8位(可能是16位)表示。在调整大小功能中,尝试替换

resized_pixel_data = np.zeros([target_height, target_width], dtype=np.uint8)

resized_pixel_data = np.zeros([target_height, target_width], dtype=np.uint16)

通过阅读Pydicom的源代码,可以看到预期长度是这样计算的:

rows = cast(int, ds.Rows)
    columns = cast(int, ds.Columns)
    samples_per_pixel = cast(int, ds.SamplesPerPixel)
    bits_allocated = cast(int, ds.BitsAllocated)

    length = rows * columns * samples_per_pixel
    length *= get_nr_frames(ds)

    if unit == "pixels":
        return length

    # Correct for the number of bytes per pixel
    if bits_allocated == 1:
        # Determine the nearest whole number of bytes needed to contain
        #   1-bit pixel data. e.g. 10 x 10 1-bit pixels is 100 bits, which
        #   are packed into 12.5 -> 13 bytes
        length = length // 8 + (length % 8 > 0)
    else:
        length *= bits_allocated // 8

    # DICOM Standard, Part 4, Annex C.7.6.3.1.2
    if ds.PhotometricInterpretation == "YBR_FULL_422":
        length = length // 3 * 2

    return length

如果我的建议不能解决您的问题,请尝试打印

rows = cast(int, ds.Rows)
columns = cast(int, ds.Columns)
samples_per_pixel = cast(int, ds.SamplesPerPixel)
bits_allocated = cast(int, ds.BitsAllocated)

并确保它们如您所愿。

相关问题