SimpleITK  
DicomSeriesReadModifyWrite/DicomSeriesReadModifySeriesWrite.py
1 # =========================================================================
2 #
3 # Copyright NumFOCUS
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0.txt
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17 # =========================================================================
18 
19 """ A SimpleITK example script demonstrating how to read a DICOM series,
20  modify it, and then write a new DICOM series. """
21 
22 import sys
23 import time
24 import os
25 import SimpleITK as sitk
26 
27 if len(sys.argv) < 3:
28  print(
29  "Usage: python "
30  + __file__
31  + " <input_directory_with_DICOM_series> <output_directory>"
32  )
33  sys.exit(1)
34 
35 # Read the original series. First obtain the series file names using the
36 # image series reader.
37 data_directory = sys.argv[1]
38 series_IDs = sitk.ImageSeriesReader.GetGDCMSeriesIDs(data_directory)
39 if not series_IDs:
40  print(
41  'ERROR: given directory "'
42  + data_directory
43  + '" does not contain a DICOM series.'
44  )
45  sys.exit(1)
46 series_file_names = sitk.ImageSeriesReader.GetGDCMSeriesFileNames(
47  data_directory, series_IDs[0]
48 )
49 
50 series_reader = sitk.ImageSeriesReader()
51 series_reader.SetFileNames(series_file_names)
52 
53 # Configure the reader to load all of the DICOM tags (public+private):
54 # By default tags are not loaded (saves time).
55 # By default if tags are loaded, the private tags are not loaded.
56 # We explicitly configure the reader to load tags, including the
57 # private ones.
58 series_reader.MetaDataDictionaryArrayUpdateOn()
59 series_reader.LoadPrivateTagsOn()
60 image3D = series_reader.Execute()
61 
62 # Modify the image (blurring)
63 filtered_image = sitk.DiscreteGaussian(image3D)
64 
65 # Write the 3D image as a series
66 # IMPORTANT: There are many DICOM tags that need to be updated when you modify
67 # an original image. This is a delicate operation and requires
68 # knowledge of the DICOM standard. This example only modifies some.
69 # For a more complete list of tags that need to be modified see:
70 # http://gdcm.sourceforge.net/wiki/index.php/Writing_DICOM
71 
72 writer = sitk.ImageFileWriter()
73 # Use the study/series/frame of reference information given in the meta-data
74 # dictionary and not the automatically generated information from the file IO
75 writer.KeepOriginalImageUIDOn()
76 
77 # Copy relevant tags from the original meta-data dictionary (private tags are
78 # also accessible).
79 tags_to_copy = [
80  "0010|0010", # Patient Name
81  "0010|0020", # Patient ID
82  "0010|0030", # Patient Birth Date
83  "0020|000d", # Study Instance UID, for machine consumption
84  "0020|0010", # Study ID, for human consumption
85  "0008|0020", # Study Date
86  "0008|0030", # Study Time
87  "0008|0050", # Accession Number
88  "0008|0060", # Modality
89 ]
90 
91 modification_time = time.strftime("%H%M%S")
92 modification_date = time.strftime("%Y%m%d")
93 
94 # Copy some of the tags and add the relevant tags indicating the change.
95 # For the series instance UID (0020|000e), each of the components is a number,
96 # cannot start with zero, and separated by a '.' We create a unique series ID
97 # using the date and time.
98 # NOTE: Always represent DICOM tags using lower case hexadecimals.
99 # DICOM tags represent hexadecimal numbers, so 0020|000D and 0020|000d
100 # are equivalent. The ITK/SimpleITK dictionary is string based, so these
101 # are two different keys, case sensitive. When read from a DICOM file the
102 # hexadecimal string representations are in lower case. To ensure consistency,
103 # always use lower case for the tags.
104 # Tags of interest:
105 direction = filtered_image.GetDirection()
106 series_tag_values = [
107  (k, series_reader.GetMetaData(0, k))
108  for k in tags_to_copy
109  if series_reader.HasMetaDataKey(0, k)
110 ] + [
111  ("0008|0031", modification_time), # Series Time
112  ("0008|0021", modification_date), # Series Date
113  ("0008|0008", "DERIVED\\SECONDARY"), # Image Type
114  (
115  "0020|000e",
116  "1.2.826.0.1.3680043.2.1125." + modification_date + ".1" + modification_time,
117  ),
118  # Series Instance UID
119  (
120  "0020|0037",
121  "\\".join(
122  map(
123  str,
124  (
125  direction[0],
126  direction[3],
127  direction[6],
128  direction[1],
129  direction[4],
130  direction[7],
131  ), # Image Orientation (Patient)
132  )
133  ),
134  ),
135  (
136  "0008|103e",
137  (
138  series_reader.GetMetaData(0, "0008|103e")
139  if series_reader.HasMetaDataKey(0, "0008|103e")
140  else "" + " Processed-SimpleITK"
141  ),
142  ), # Series Description is an optional tag, so may not exist
143 ]
144 
145 for i in range(filtered_image.GetDepth()):
146  image_slice = filtered_image[:, :, i]
147  # Tags shared by the series.
148  for tag, value in series_tag_values:
149  image_slice.SetMetaData(tag, value)
150  # Slice specific tags.
151  # Instance Creation Date
152  image_slice.SetMetaData("0008|0012", time.strftime("%Y%m%d"))
153  # Instance Creation Time
154  image_slice.SetMetaData("0008|0013", time.strftime("%H%M%S"))
155  # Image Position (Patient)
156  image_slice.SetMetaData(
157  "0020|0032",
158  "\\".join(map(str, filtered_image.TransformIndexToPhysicalPoint((0, 0, i)))),
159  )
160  # Instance Number
161  image_slice.SetMetaData("0020|0013", str(i))
162 
163  # Write to the output directory and add the extension dcm, to force writing
164  # in DICOM format.
165  writer.SetFileName(os.path.join(sys.argv[2], str(i) + ".dcm"))
166  writer.Execute(image_slice)
167 sys.exit(0)
itk::simple::DiscreteGaussian
Image DiscreteGaussian(const Image &image1, std::vector< double > variance=std::vector< double >(3, 1.0), unsigned int maximumKernelWidth=32u, std::vector< double > maximumError=std::vector< double >(3, 0.01), bool useImageSpacing=true)
Blurs an image by separable convolution with discrete gaussian kernels. This filter performs Gaussian...
itk::simple::ImageSeriesReader
Read series of image files into a SimpleITK image.
Definition: sitkImageSeriesReader.h:68
itk::simple::ImageFileWriter
Write out a SimpleITK image to the specified file location.
Definition: sitkImageFileWriter.h:51