Open graag opened 2 years ago
Przykładowy kod tworzący plik wynikowy dicom stworzony na bazie przykładowych plików PETCT. Do każdego TAGa dodany jest jego kod w postaci pary integerów a także informacja o tym czy jest wymagany, opcjonalny lub warunkowy. Dane binarne wyciąłem, część wartości pozmieniałem.
# 1 - tag wymagany, nie moze byc null
# 2 - tag wymagany, moze byc null
# 3 - tag opcjonalny
# C - tag warunkowy
from pydicom.dataset import Dataset, FileMetaDataset
from pydicom.sequence import Sequence
# File meta info data elements
file_meta = FileMetaDataset()
file_meta.FileMetaInformationGroupLength = 190
file_meta.FileMetaInformationVersion = b'\x00\x01'
file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2' # (0008,1150) 1
file_meta.MediaStorageSOPInstanceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406580734300013807' # (0008,1155) 1
file_meta.TransferSyntaxUID = '1.2.840.10008.1.2.1'
file_meta.ImplementationClassUID = '1.3.12.2.1107.5.1.4'
file_meta.ImplementationVersionName = 'SIEMENS_S5VB42A'
# Main data elements
ds = Dataset()
ds.SpecificCharacterSet = 'ISO_IR 100' # (0008,0005) 1C Required if an expanded or replacement character set is used.
ds.ImageType = ['DERIVED', 'PRIMARY', 'AXIAL', 'CT_SOM5 SPI'] # (0008,0008) 3, PET Image 1
ds.SOPClassUID = '1.2.840.10008.5.1.4.1.1.2' # (0008,0016) 1
ds.SOPInstanceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406580734300013807' # (0008,0018) 1
ds.StudyDate = '20220316' # (0008,0020) 2
ds.SeriesDate = '20220316' # (0008,0021) General Series 3, PET Series 1
ds.AcquisitionDate = '20220316' # (0008,0022) 3, PET Image 2
ds.ContentDate = '20220316' # (0008,0023) 2C
ds.StudyTime = '140030.312000' # (0008,0030) 2
ds.SeriesTime = '140224.515000' # (0008,0031) General Series 3, PET Series 1
ds.AcquisitionTime = '140202.913288' # (0008,0032) 3, PET Image 2
ds.ContentTime = '140202.913288' # (0008,0033) 2C
ds.AccessionNumber = '' # (0008,0050) 2
ds.Modality = 'CT' # (0008,0060) 1
ds.Manufacturer = 'SIEMENS' # (0008,0070) 2, Device 3, SOP Common 1
ds.InstitutionName = '<nazwa zakladu>' # (0008,0080) 3
ds.InstitutionAddress = '<Adres szpitala>' # (0008,0081) 3
ds.ReferringPhysicianName = '' # (0008,0090) 2
ds.StationName = 'CT58039' # (0008,1010) 3
ds.StudyDescription = 'PET^6_PETCT_GAL_68 (Adult)' # (0008,1030) 3
ds.SeriesDescription = 'CTWB TULOW 4.0 eFoV' # (0008,103E) 3
ds.ManufacturerModelName = 'Biograph 64' # (0008,1090) 3
# Referenced Image Sequence
refd_image_sequence = Sequence()
ds.ReferencedImageSequence = refd_image_sequence
# Referenced Image Sequence: Referenced Image 1
refd_image1 = Dataset()
refd_image1.ReferencedSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
refd_image1.ReferencedSOPInstanceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406360382800006407'
refd_image_sequence.append(refd_image1)
ds.DerivationDescription = 'Reconstruction field larger than scan field' # (0008,2111) 3
# Source Image Sequence
source_image_sequence = Sequence()
ds.SourceImageSequence = source_image_sequence
# Source Image Sequence: Source Image 1
source_image1 = Dataset()
source_image1.ReferencedSOPClassUID = '1.3.12.2.1107.5.9.1'
source_image1.ReferencedSOPInstanceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406360382800006426'
source_image_sequence.append(source_image1)
ds.add_new((0x0009, 0x0010), 'LO', 'SIEMENS CT VA1 DUMMY') # UNKNOWN TAG
ds.PatientName = '<IMIE I NAZWISKO>' # (0010,0010) 2
ds.PatientID = '21.12.08-14:36:25-STD-1.3.12.2.1107.5.1.4.1016' # (0010,0020) 1
ds.PatientBirthDate = '19770801' # (0010,0030) 2
ds.PatientSex = 'M' # (0010,0040) 2
ds.PatientAge = '035Y' # (0010,1010) 3
ds.PatientWeight = '80.0' # (0010,1030) 3
ds.BodyPartExamined = 'ABDOMEN' # (0018,0015) 3
ds.SliceThickness = '4.0' # (0018,0050) 2
ds.KVP = '120.0' # (0018,0060) 3
ds.DataCollectionDiameter = '500.0' # (0018,0090) 3 (CT Modules)
ds.DeviceSerialNumber = '1016' # (0018,1000) 3
ds.SoftwareVersions = '6.7.3' # (0018,1020) 3
ds.ProtocolName = '6_PETCT_GAL_68' # (0018,1030) 3
ds.ReconstructionDiameter = '700.0' # (0018,1100) 3 (MR Modules)
ds.DistanceSourceToDetector = '1040.0' # (0018,1110) 3 (CT Modules)
ds.DistanceSourceToPatient = '570.0' # (0018,1111) 3 (X-Ray Modules)
ds.GantryDetectorTilt = '0.0' # (0018,1120) PET Series 3
ds.TableHeight = '145.0' # (0018,1130) 3 (CT Modules)
ds.RotationDirection = 'CW' # (0018,1140) 3 (CT Modules)
ds.ExposureTime = '500' # (0018,1150) 3 (CT Modules)
ds.XRayTubeCurrent = '194' # (0018,1151) 3 (CT Modules)
ds.Exposure = '121' # (0018,1152) 3 (CT Modules)
ds.FilterType = '0' # (0018,1160) 3 (CT Modules)
ds.GeneratorPower = '33' # (0018,1170) 3 (CT Modules)
ds.FocalSpots = '1.2' # (0018,1190) 3 (CT Modules)
ds.DateOfLastCalibration = '20220316' # (0018,1200) 3
ds.TimeOfLastCalibration = '073432.000000' # (0018,1201) 3
ds.ConvolutionKernel = 'B30f' # (0018,1210) 3 (CT Modules)
ds.PatientPosition = 'HFS' # (0018,5100) 2C
ds.SingleCollimationWidth = 1.2 # (0018,9306) 3 (CT Modules)
ds.TotalCollimationWidth = 28.799999999999997 # (0018,9307) 3 (CT Modules)
ds.TableSpeed = 46.0 # (0018,9309) 3 (CT Modules)
ds.TableFeedPerRotation = 23.0 # (0018,9310) 3 (CT Modules)
ds.SpiralPitchFactor = 0.8 # (0018,9311) 3 (CT Modules)
ds.ExposureModulationType = 'XYZ_EC' # (0018,9323) 3 (CT Modules)
ds.EstimatedDoseSaving = 57.8078 # (0018,9324) 3 (CT Modules)
ds.CTDIvol = 8.186462608695651 # (0018,9345) 3 (CT Modules)
ds.add_new((0x0019, 0x0010), 'LO', 'SIEMENS CT VA0 COAD') # UNKNOWN TAG
ds.add_new((0x0019, 0x10b0), 'DS', '23.0') # UNKNOWN TAG
ds.add_new((0x0019, 0x10b1), 'LO', '4.30000022031406570357800000074') # UNKNOWN TAG
ds.StudyInstanceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406564064000000062' # (0020,000D) 1
ds.SeriesInstanceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406580734300013726' # (0020,000E) 1
ds.StudyID = '5' # (0020,0010) 2 (Common Composite Image IOD Modules)
ds.SeriesNumber = '2' # (0020,0011) 2
ds.AcquisitionNumber = '2' # (0020,0012) 3
ds.InstanceNumber = '201' # (0020,0013) 2, SOP Common 3
ds.ImagePositionPatient = [-349.31640625, -494.31640625, -1246.5] # (0020,0032) 1
ds.ImageOrientationPatient = [1, 0, 0, 0, 1, 0] # (0020,0037) 1
ds.FrameOfReferenceUID = '1.3.12.2.1107.5.1.4.1016.30000022031406360382800006406' # (0020,0052) 1
ds.PositionReferenceIndicator = '' # (0020,1040) 2
ds.SliceLocation = '-1246.5' # (0020,1041) 3
ds.ImageComments = '' # (0020,4000) 3
ds.add_new((0x0021, 0x0010), 'LO', 'SIEMENS MED') # UNKNOWN TAG
ds.add_new((0x0021, 0x1011), 'DS', [0, 0]) # UNKNOWN TAG
ds.SamplesPerPixel = 1 # (0028,0002) 1
ds.PhotometricInterpretation = 'MONOCHROME2' # (0028,0004) 1
ds.Rows = 512 # (0028,0010) 1
ds.Columns = 512 # (0028,0011) 1
ds.PixelSpacing = [1.3671875, 1.3671875] # (0028,0030) 1
ds.BitsAllocated = 16 # (0028,0100) 1
ds.BitsStored = 12 # (0028,0101) 1
ds.HighBit = 11 # (0028,0102) 1
ds.PixelRepresentation = 0 # (0028,0103) 1
ds.SmallestImagePixelValue = 0 # (0028,0106) 3
ds.LargestImagePixelValue = 2269 # (0028,0107) 3
ds.WindowCenter = [40, 300] # (0028,1050) 1C Required if VOI LUT Sequence (0028,3010) is not present
ds.WindowWidth = [300, 1500] # (0028,1051) 1C Required if Window Center (0028,1050) is present.
ds.RescaleIntercept = '-1024.0' # (0028,1052) 1
ds.RescaleSlope = '1.0' # (0028,1053) 1
ds.WindowCenterWidthExplanation = ['WINDOW1', 'WINDOW2'] # (0028,1055) 3
ds.LossyImageCompression = '01' # (0028,2110) 3, PET Image 1C
ds.add_new((0x0029, 0x0010), 'LO', 'SIEMENS CSA HEADER') # UNKNOWN TAG
ds.add_new((0x0029, 0x0011), 'LO', 'SIEMENS MEDCOM HEADER') # UNKNOWN TAG
ds.add_new((0x0029, 0x0012), 'LO', 'SIEMENS MEDCOM OOG') # UNKNOWN TAG
ds.add_new((0x0029, 0x1008), 'CS', 'SOM 5') # UNKNOWN TAG
ds.add_new((0x0029, 0x1009), 'LO', 'VA10A 971201') # UNKNOWN TAG
ds.add_new((0x0029, 0x1010), 'OB', <binary data>) # UNKNOWN TAG
# [Application Header Sequence]
tag00291140 = Sequence()
ds.Tag00291140 = tag00291140
# [Application Header Sequence]: [Application Header] 1
tag002911401 = Dataset()
tag002911401.add_new((0x0029, 0x0010), 'LO', 'SIEMENS MEDCOM HEADER') # UNKNOWN TAG
tag002911401.add_new((0x0029, 0x1041), 'CS', 'SOM 5 TPOS') # UNKNOWN TAG
tag002911401.add_new((0x0029, 0x1042), 'LO', 'SOM 5 NULLPOSITION') # UNKNOWN TAG
tag002911401.add_new((0x0029, 0x1043), 'LO', 'VB10A 20030626') # UNKNOWN TAG
tag002911401.add_new((0x0029, 0x1044), 'OB', b'+000000000\x00A') # UNKNOWN TAG
tag00291140.append(tag002911401)
ds.add_new((0x0029, 0x1208), 'CS', 'MEDCOM OOG 2') # UNKNOWN TAG
ds.add_new((0x0029, 0x1209), 'LO', 'VX70G') # UNKNOWN TAG
ds.add_new((0x0029, 0x1210), 'OB', <file description and data>) # UNKNOWN TAG
ds.add_new([0x6000, 0x0010], 'US', 512) # Overlay Rows # (6000,0010) 1
ds.add_new([0x6000, 0x0011], 'US', 512) # Overlay Columns # (6000,0011) 1
ds.add_new([0x6000, 0x0015], 'US', 1) # Overlay number of frames # (6000,0015) 1
ds.add_new([0x6000, 0x0022], 'LO', 'Siemens MedCom Object Graphics') # Overlay description # (6000,0022) 3
ds.add_new([0x6000, 0x0040], 'CS', 'G') # Overlay Type ('R','G'), ROI or Graphics # (6000,0040) 1
ds.add_new([0x6000, 0x0050], 'SS', [1, 1]) # Overlay Origin # (6000,0050) 1
ds.add_new([0x6000, 0x0051], 'US', 1) # Image Frame Origin # (6000,0051) 3
ds.add_new([0x6000, 0x0100], 'US', 1) # Overlay Bits Allocated, mandatory fixed value = 1 # (6000,0100) 1
ds.add_new([0x6000, 0x0102], 'US', 0) # Overlay Bit Position, mandatory fixed value = 0 # (6000,0102) 1
overlay_data = # <binary data>
ds.add_new([0x6000, 0x3000], 'OW', overlay_data) # OverlayData # (6000, 3000) 1
ds.PixelData = # <binary data>
ds.DataSetTrailingPadding = # <binary data> # (FFFC,FFFC)
ds.file_meta = file_meta
ds.is_implicit_VR = False
ds.is_little_endian = True
ds.save_as(r'test.dcm', write_like_original=False)
Kilka komentarzy do atrybutów:
ds.SOPClassUID
| file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
- oznacza SOP class "CT Image Storage" (https://dicom.nema.org/Dicom/2013/output/chtml/part04/sect_I.4.html)ds.SOPInstanceUID
| file_meta.MediaStorageSOPInstanceUID
- najwyraźniej unikalny identyfikator pliku DICOM - do ustalenia jak jest struktura tego identyfikatorafile_meta.TransferSyntaxUID = '1.2.840.10008.1.2.1'
- definiuje kodowanie użye przy zapisie na dysk. W tym wypadku "Explicit VR Little Endian" (https://dicomlibrary.com/dicom/transfer-syntax/, https://www.web3.lu/dicom-transfersyntaxuid/)file_meta.ImplementationClassUID = '1.3.12.2.1107.5.1.4'
- to chyba powinno być "nasze", jedt unikalne dla kązdego software nawet per wersja??ds.StudyDescription
- opcjonalne ale chyba warto użyć żeby sobie identyfikować z jakiej symulacji jest jaki obrazds.PatientID
- można by użyć do definiowania fantomów ...ds.StudyDate
- opcjonalne ale można by ustawić na datę pliku Interfile ...Tagi specyficzne SIEMENS-a możemy olać, tak samo overlay.
@jawka @wkrzemien
Kilka pytań :)
Referenced Image Sequence
a czym Source Image Sequence
? Surowe dane PET i CT użyte w reko? Możemy to olać ??ImagePositionPatient = [-349.31640625, -494.31640625, -1246.5]
? To jakoś z interfile liczymy?ds.SamplesPerPixel = 1 # (0028,0002) 1
ds.PhotometricInterpretation = 'MONOCHROME2' # (0028,0004) 1
ds.Rows = 512 # (0028,0010) 1
ds.Columns = 512 # (0028,0011) 1
ds.PixelSpacing = [1.3671875, 1.3671875] # (0028,0030) 1
ds.BitsAllocated = 16 # (0028,0100) 1
ds.BitsStored = 12 # (0028,0101) 1
ds.HighBit = 11 # (0028,0102) 1
ds.PixelRepresentation = 0 # (0028,0103) 1
ds.SmallestImagePixelValue = 0 # (0028,0106) 3
ds.LargestImagePixelValue = 2269 # (0028,0107) 3
ds.WindowCenter = [40, 300] # (0028,1050) 1C Required if VOI LUT Sequence (0028,3010) is not present
ds.WindowWidth = [300, 1500] # (0028,1051) 1C Required if Window Center (0028,1050) is present.
ds.RescaleIntercept = '-1024.0' # (0028,1052) 1
ds.RescaleSlope = '1.0' # (0028,1053) 1
ds.WindowCenterWidthExplanation = ['WINDOW1', 'WINDOW2'] # (0028,1055) 3
ds.LossyImageCompression = '01'
@wkrzemien Zamykamy bo PR już jest w master. Tak?
Opracować format opisu minimalnego obrazu DICOM
Minimalny zestaw atrybutów definiuje standard DICOM: