我正在尝试从标准尺寸(512 x 512或256 x 256)的numpy阵列创建一个新的dicom图像。看起来这应该是直截了当的,我已经调整了http://code.google.com/p/pydicom/source/browse/source/dicom/examples/write_new.py的代码,它似乎执行相同的过程,但是当我保存文件时,我无法在RadiAnt或MicroDicom中查看它。
import dicom, dicom.UID
from dicom.dataset import Dataset, FileDataset
def write_dicom(pixel_array,filename):
file_meta = Dataset()
ds = FileDataset(filename, {},file_meta = file_meta,preamble="\0"*128)
ds.PixelData = pixel_array.tostring()
ds.save_as(filename)
return
if __name__ == "__main__":
import numpy as np
pixel_array = np.tile(np.arange(256).reshape(16,16), (16,16)) * 4
write_dicom(pixel_array,'pretty.dcm')
答案 0 :(得分:4)
这是我需要编写的代码的功能版本。它将从给定的2D像素阵列写入16位灰度DICOM图像。根据DICOM标准,UID对于每个图像和系列应该是唯一的,这段代码不担心,因为我不知道UID实际上做了什么。如果其他人这样做,我会很乐意将其添加进去。
import dicom, dicom.UID
from dicom.dataset import Dataset, FileDataset
import numpy as np
import datetime, time
def write_dicom(pixel_array,filename):
"""
INPUTS:
pixel_array: 2D numpy ndarray. If pixel_array is larger than 2D, errors.
filename: string name for the output file.
"""
## This code block was taken from the output of a MATLAB secondary
## capture. I do not know what the long dotted UIDs mean, but
## this code works.
file_meta = Dataset()
file_meta.MediaStorageSOPClassUID = 'Secondary Capture Image Storage'
file_meta.MediaStorageSOPInstanceUID = '1.3.6.1.4.1.9590.100.1.1.111165684411017669021768385720736873780'
file_meta.ImplementationClassUID = '1.3.6.1.4.1.9590.100.1.0.100.4.0'
ds = FileDataset(filename, {},file_meta = file_meta,preamble="\0"*128)
ds.Modality = 'WSD'
ds.ContentDate = str(datetime.date.today()).replace('-','')
ds.ContentTime = str(time.time()) #milliseconds since the epoch
ds.StudyInstanceUID = '1.3.6.1.4.1.9590.100.1.1.124313977412360175234271287472804872093'
ds.SeriesInstanceUID = '1.3.6.1.4.1.9590.100.1.1.369231118011061003403421859172643143649'
ds.SOPInstanceUID = '1.3.6.1.4.1.9590.100.1.1.111165684411017669021768385720736873780'
ds.SOPClassUID = 'Secondary Capture Image Storage'
ds.SecondaryCaptureDeviceManufctur = 'Python 2.7.3'
## These are the necessary imaging components of the FileDataset object.
ds.SamplesPerPixel = 1
ds.PhotometricInterpretation = "MONOCHROME2"
ds.PixelRepresentation = 0
ds.HighBit = 15
ds.BitsStored = 16
ds.BitsAllocated = 16
ds.SmallestImagePixelValue = '\\x00\\x00'
ds.LargestImagePixelValue = '\\xff\\xff'
ds.Columns = pixel_array.shape[0]
ds.Rows = pixel_array.shape[1]
if pixel_array.dtype != np.uint16:
pixel_array = pixel_array.astype(np.uint16)
ds.PixelData = pixel_array.tostring()
ds.save_as(filename)
return
if __name__ == "__main__":
# pixel_array = np.arange(256*256).reshape(256,256)
# pixel_array = np.tile(np.arange(256).reshape(16,16),(16,16))
x = np.arange(16).reshape(16,1)
pixel_array = (x + x.T) * 32
pixel_array = np.tile(pixel_array,(16,16))
write_dicom(pixel_array,'pretty.dcm')
答案 1 :(得分:3)
以上示例有效,但会导致许多工具抱怨DICOM,甚至根本不能使用itk / SimpleITK作为堆栈来读取它们。我发现从numpy制作DICOM的最好方法是使用SimpleITK工具并逐片生成DICOM。一个基本示例(https://github.com/zivy/SimpleITK/blob/8e94451e4c0e90bcc6a1ffdd7bc3d56c81f58d80/Examples/DicomSeriesReadModifyWrite/DicomSeriesReadModifySeriesWrite.py)显示了如何加载堆栈,执行转换然后重新保存文件,但这可以通过使用
轻松修改import SimpleITK as sitk
filtered_image = sitk.GetImageFromArray(my_numpy_array)
最终输出图像中的标签数量非常大,因此手动创建所有这些标签非常繁琐。另外,SimpleITK支持8,16,32位图像以及RGB,因此比在pydicom中制作它们要容易得多。
(0008, 0008) Image Type CS: ['DERIVED', 'SECONDARY']
(0008, 0016) SOP Class UID UI: Secondary Capture Image Storage
(0008, 0018) SOP Instance UID UI: 1.2.826.0.1.3680043.2.1125.1.35596048796922805578234000521866725
(0008, 0020) Study Date DA: '20170803'
(0008, 0021) Series Date DA: '20170803'
(0008, 0023) Content Date DA: 0
(0008, 0030) Study Time TM: '080429.171808'
(0008, 0031) Series Time TM: '080429'
(0008, 0033) Content Time TM: 0
(0008, 0050) Accession Number SH: ''
(0008, 0060) Modality CS: 'OT'
(0008, 0064) Conversion Type CS: 'WSD'
(0008, 0090) Referring Physician's Name PN: ''
(0010, 0010) Patient's Name PN: ''
(0010, 0020) Patient ID LO: ''
(0010, 0030) Patient's Birth Date DA: ''
(0010, 0040) Patient's Sex CS: ''
(0018, 2010) Nominal Scanned Pixel Spacing DS: ['1', '3']
(0020, 000d) Study Instance UID UI: 1.2.826.0.1.3680043.2.1125.1.33389357207068897066210100430826006
(0020, 000e) Series Instance UID UI: 1.2.826.0.1.3680043.2.1125.1.51488923827429438625199681257282809
(0020, 0010) Study ID SH: ''
(0020, 0011) Series Number IS: ''
(0020, 0013) Instance Number IS: ''
(0020, 0020) Patient Orientation CS: ''
(0020, 0052) Frame of Reference UID UI: 1.2.826.0.1.3680043.2.1125.1.35696880630664441938326682384062489
(0028, 0002) Samples per Pixel US: 1
(0028, 0004) Photometric Interpretation CS: 'MONOCHROME2'
(0028, 0010) Rows US: 40
(0028, 0011) Columns US: 50
(0028, 0100) Bits Allocated US: 32
(0028, 0101) Bits Stored US: 32
(0028, 0102) High Bit US: 31
(0028, 0103) Pixel Representation US: 1
(0028, 1052) Rescale Intercept DS: "0"
(0028, 1053) Rescale Slope DS: "1"
(0028, 1054) Rescale Type LO: 'US'
(7fe0, 0010) Pixel Data OW: Array of 8000 bytes
答案 2 :(得分:1)
Corvin的2020年更新几乎对我有用。 该meta仍未写入文件,因此在读取它时引发了以下异常:
pydicom.errors.InvalidDicomError:文件缺少DICOM文件元信息标头或标头中缺少'DICM'前缀。
为了解决此问题并将元数据写入dicom文件,我需要在enforce_standard=True
调用中添加the save_as()
:
ds.save_as(filename=out_filename, enforce_standard=True)
答案 3 :(得分:0)
DICOM是一种非常复杂的格式。有许多方言,兼容性是运气的问题。你也可以试试nibabel
,也许它的方言对RadiAnt或MicroDicom更有吸引力。
一般情况下,我建议尽可能使用Nifti格式。它的标准更加简洁,不兼容性很少。 nibabel也支持这一点。