1 /*=========================================================================
2 *
3 * Copyright Insight Software Consortium
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 // Software Guide : BeginLatex
20 //
21 // This example illustrates how to read a DICOM series into a volume and then
22 // print most of the DICOM header information. The binary fields are skipped.
23 //
24 // \index{DICOM!Header}
25 // \index{DICOM!Tags}
26 // \index{DICOM!Printing Tags}
27 //
28 // Software Guide : EndLatex
29
30 // Software Guide : BeginLatex
31 //
32 // The header files for the series reader and the GDCM classes for image IO and
33 // name generation should be included first.
34 //
35 // Software Guide : EndLatex
36
37 // Software Guide : BeginCodeSnippet
38 #include "itkImageSeriesReader.h"
39 #include "itkGDCMImageIO.h"
40 #include "itkGDCMSeriesFileNames.h"
41 // Software Guide : EndCodeSnippet
42
main(int argc,char * argv[])43 int main( int argc, char* argv[] )
44 {
45 if( argc < 2 )
46 {
47 std::cerr << "Usage: " << argv[0] << " DicomDirectory " << std::endl;
48 return EXIT_FAILURE;
49 }
50
51 // Software Guide : BeginLatex
52 //
53 // Next, we instantiate the type to be used for storing the image once it is
54 // read into memory.
55 //
56 // Software Guide : EndLatex
57
58 // Software Guide : BeginCodeSnippet
59 using PixelType = signed short;
60 constexpr unsigned int Dimension = 3;
61
62 using ImageType = itk::Image< PixelType, Dimension >;
63 // Software Guide : EndCodeSnippet
64
65 // Software Guide : BeginLatex
66 //
67 // We use the image type for instantiating the series reader type and then we
68 // construct one object of this class.
69 //
70 // Software Guide : EndLatex
71
72 // Software Guide : BeginCodeSnippet
73 using ReaderType = itk::ImageSeriesReader< ImageType >;
74
75 ReaderType::Pointer reader = ReaderType::New();
76 // Software Guide : EndCodeSnippet
77
78 // Software Guide : BeginLatex
79 //
80 // A GDCMImageIO object is created and assigned to the reader.
81 //
82 // Software Guide : EndLatex
83
84 // Software Guide : BeginCodeSnippet
85 using ImageIOType = itk::GDCMImageIO;
86
87 ImageIOType::Pointer dicomIO = ImageIOType::New();
88
89 reader->SetImageIO( dicomIO );
90 // Software Guide : EndCodeSnippet
91
92 // Software Guide : BeginLatex
93 //
94 // A GDCMSeriesFileNames is declared in order to generate the names of DICOM
95 // slices. We specify the directory with the \code{SetInputDirectory()} method
96 // and, in this case, take the directory name from the command line arguments.
97 // You could have obtained the directory name from a file dialog in a GUI.
98 //
99 // Software Guide : EndLatex
100
101 // Software Guide : BeginCodeSnippet
102 using NamesGeneratorType = itk::GDCMSeriesFileNames;
103
104 NamesGeneratorType::Pointer nameGenerator = NamesGeneratorType::New();
105
106 nameGenerator->SetInputDirectory( argv[1] );
107 // Software Guide : EndCodeSnippet
108
109 // Software Guide : BeginLatex
110 //
111 // The list of files to read is obtained from the name generator by invoking
112 // the \code{GetInputFileNames()} method and receiving the results in a
113 // container of strings. The list of filenames is passed to the reader using
114 // the \code{SetFileNames()} method.
115 //
116 // Software Guide : EndLatex
117
118 // Software Guide : BeginCodeSnippet
119 using FileNamesContainer = std::vector<std::string>;
120 FileNamesContainer fileNames = nameGenerator->GetInputFileNames();
121
122 reader->SetFileNames( fileNames );
123 // Software Guide : EndCodeSnippet
124
125 // Software Guide : BeginLatex
126 //
127 // We trigger the reader by invoking the \code{Update()} method. This
128 // invocation should normally be done inside a \code{try/catch} block given
129 // that it may eventually throw exceptions.
130 //
131 // Software Guide : EndLatex
132
133 try
134 {
135 // Software Guide : BeginCodeSnippet
136 reader->Update();
137 // Software Guide : EndCodeSnippet
138 }
139 catch (itk::ExceptionObject &ex)
140 {
141 std::cout << ex << std::endl;
142 return EXIT_FAILURE;
143 }
144
145 // Software Guide : BeginLatex
146 //
147 // ITK internally queries GDCM and obtains all the DICOM tags from the file
148 // headers. The tag values are stored in the \doxygen{MetaDataDictionary}
149 // which is a general-purpose container for \{key,value\} pairs. The Metadata
150 // dictionary can be recovered from any ImageIO class by invoking the
151 // \code{GetMetaDataDictionary()} method.
152 //
153 // \index{MetaDataDictionary}
154 // \index{ImageIO!GetMetaDataDictionary()}
155 //
156 // Software Guide : EndLatex
157
158 // Software Guide : BeginCodeSnippet
159 using DictionaryType = itk::MetaDataDictionary;
160
161 const DictionaryType & dictionary = dicomIO->GetMetaDataDictionary();
162 // Software Guide : EndCodeSnippet
163
164 // Software Guide : BeginLatex
165 //
166 // In this example, we are only interested in the DICOM tags that can be
167 // represented as strings. Therefore, we declare a \doxygen{MetaDataObject} of
168 // string type in order to receive those particular values.
169 //
170 // \index{MetaDataDictionary!MetaDataObject}
171 // \index{MetaDataDictionary!String entries}
172 // \index{MetaDataObject!Strings}
173 //
174 // Software Guide : EndLatex
175
176 // Software Guide : BeginCodeSnippet
177 using MetaDataStringType = itk::MetaDataObject< std::string >;
178 // Software Guide : EndCodeSnippet
179
180 // Software Guide : BeginLatex
181 //
182 // The metadata dictionary is organized as a container with its corresponding
183 // iterators. We can therefore visit all its entries by first getting access to
184 // its \code{Begin()} and \code{End()} methods.
185 //
186 // \index{MetaDataDictionary!Begin()}
187 // \index{MetaDataDictionary!End()}
188 // \index{MetaDataDictionary!Iterator}
189 // \index{MetaDataDictionary!ConstIterator}
190 //
191 // Software Guide : EndLatex
192
193 // Software Guide : BeginCodeSnippet
194 auto itr = dictionary.Begin();
195 auto end = dictionary.End();
196 // Software Guide : EndCodeSnippet
197
198 // Software Guide : BeginLatex
199 //
200 // We are now ready for walking through the list of DICOM tags. For this
201 // purpose we use the iterators that we just declared. At every entry we
202 // attempt to convert it into a string entry by using the \code{dynamic\_cast}
203 // based on RTTI information\footnote{Run Time Type Information}. The
204 // dictionary is organized like a \code{std::map} structure, so we should use
205 // the \code{first} and \code{second} members of every entry in order
206 // to get access to the \{key,value\} pairs.
207 //
208 // Software Guide : EndLatex
209
210 // Software Guide : BeginCodeSnippet
211 while( itr != end )
212 {
213 itk::MetaDataObjectBase::Pointer entry = itr->second;
214
215 MetaDataStringType::Pointer entryvalue =
216 dynamic_cast<MetaDataStringType *>( entry.GetPointer() );
217
218 if( entryvalue )
219 {
220 std::string tagkey = itr->first;
221 std::string tagvalue = entryvalue->GetMetaDataObjectValue();
222 std::cout << tagkey << " = " << tagvalue << std::endl;
223 }
224
225 ++itr;
226 }
227 // Software Guide : EndCodeSnippet
228
229 // Software Guide : BeginLatex
230 //
231 // It is also possible to query for specific entries instead of reading all of
232 // them as we did above. In this case, the user must provide the tag
233 // identifier using the standard DICOM encoding. The identifier is stored in a
234 // string and used as key in the dictionary.
235 //
236 // Software Guide : EndLatex
237
238 // Software Guide : BeginCodeSnippet
239 std::string entryId = "0010|0010";
240
241 auto tagItr = dictionary.Find( entryId );
242
243 if( tagItr == end )
244 {
245 std::cerr << "Tag " << entryId;
246 std::cerr << " not found in the DICOM header" << std::endl;
247 return EXIT_FAILURE;
248 }
249 // Software Guide : EndCodeSnippet
250
251 // Software Guide : BeginLatex
252 //
253 // Since the entry may or may not be of string type we must again use a
254 // \code{dynamic\_cast} in order to attempt to convert it to a string dictionary
255 // entry. If the conversion is successful, we can then print out its content.
256 //
257 // Software Guide : EndLatex
258
259 // Software Guide : BeginCodeSnippet
260 MetaDataStringType::ConstPointer entryvalue =
261 dynamic_cast<const MetaDataStringType *>( tagItr->second.GetPointer() );
262
263 if( entryvalue )
264 {
265 std::string tagvalue = entryvalue->GetMetaDataObjectValue();
266 std::cout << "Patient's Name (" << entryId << ") ";
267 std::cout << " is: " << tagvalue << std::endl;
268 }
269 else
270 {
271 std::cerr << "Entry was not of string type" << std::endl;
272 return EXIT_FAILURE;
273 }
274 // Software Guide : EndCodeSnippet
275
276 // Software Guide : BeginLatex
277 //
278 // This type of functionality will probably be more useful when provided
279 // through a graphical user interface. For a full description of the DICOM
280 // dictionary please look at the following file.
281 //
282 // \code{Insight/Utilities/gdcm/Dicts/dicomV3.dic}
283 //
284 // Software Guide : EndLatex
285
286 return EXIT_SUCCESS;
287
288 }
289