1 /*=========================================================================
2
3 Program: GDCM (Grassroots DICOM). A DICOM library
4
5 Copyright (c) 2006-2011 Mathieu Malaterre
6 All rights reserved.
7 See Copyright.txt or http://gdcm.sourceforge.net/Copyright.html for details.
8
9 This software is distributed WITHOUT ANY WARRANTY; without even
10 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
11 PURPOSE. See the above copyright notice for more information.
12
13 =========================================================================*/
14 #include "gdcmScanner.h"
15 #include "gdcmReader.h"
16 #include "gdcmGlobal.h"
17 #include "gdcmDicts.h"
18 #include "gdcmDict.h"
19 #include "gdcmDictEntry.h"
20 #include "gdcmStringFilter.h"
21 #include "gdcmProgressEvent.h"
22 #include "gdcmFileNameEvent.h"
23
24 #include <algorithm> // std::find
25
26 namespace gdcm
27 {
28
29
30 Scanner::~Scanner()
31 = default;
32
ClearTags()33 void Scanner::ClearTags()
34 {
35 Tags.clear();
36 }
37
ClearSkipTags()38 void Scanner::ClearSkipTags()
39 {
40 SkipTags.clear();
41 }
42
AddSkipTag(Tag const & t)43 void Scanner::AddSkipTag( Tag const & t )
44 {
45 SkipTags.insert( t );
46 assert(0); // This is NOT implemented for now
47 }
48
49 // Warning: API is passing a public tag (no way to specify private tag)
AddPrivateTag(PrivateTag const & t)50 void Scanner::AddPrivateTag( PrivateTag const & t )
51 {
52 static const Global &g = GlobalInstance;
53 static const Dicts &dicts = g.GetDicts();
54 const DictEntry &entry = dicts.GetDictEntry( t );
55 //std::cout << "Debug: " << entry << std::endl;
56 // Is this tag an ASCII on ?
57 if( entry.GetVR() & VR::VRASCII )
58 {
59 PrivateTags.insert( t );
60 }
61 else if( entry.GetVR() == VR::INVALID )
62 {
63 gdcmWarningMacro( "Only tag with known VR are allowed. Tag " << t << " will be discarded" );
64 }
65 else
66 {
67 assert( entry.GetVR() & VR::VRBINARY );
68 //gdcmWarningMacro( "Only ASCII VR are supported for now. Tag " << t << " will be discarded" );
69 PrivateTags.insert( t );
70 }
71 }
72
AddTag(Tag const & t)73 void Scanner::AddTag( Tag const & t )
74 {
75 static const Global &g = GlobalInstance;
76 static const Dicts &dicts = g.GetDicts();
77 const DictEntry &entry = dicts.GetDictEntry( t );
78 // Is this tag an ASCII on ?
79 if( entry.GetVR() & VR::VRASCII )
80 {
81 Tags.insert( t );
82 }
83 else if( entry.GetVR() == VR::INVALID )
84 {
85 gdcmWarningMacro( "Only tag with known VR are allowed. Tag " << t << " will be discarded" );
86 }
87 else
88 {
89 assert( entry.GetVR() & VR::VRBINARY );
90 //gdcmWarningMacro( "Only ASCII VR are supported for now. Tag " << t << " will be discarded" );
91 Tags.insert( t );
92 }
93 }
94
Scan(Directory::FilenamesType const & filenames)95 bool Scanner::Scan( Directory::FilenamesType const & filenames )
96 {
97 this->InvokeEvent( StartEvent() );
98
99 // Is there at least one tag ?
100 if( !Tags.empty() || !PrivateTags.empty() )
101 {
102 //if( filenames.empty() ) return true;
103
104 // Prepare hash table:
105 Mappings.clear();
106 Mappings[""]; // Create a fake table for dummy file
107
108 // Make our own copy:
109 Filenames = filenames;
110
111 // Find the tag with the highest value (get the one from the end of the std::set)
112 Tag last;
113 if( !Tags.empty() )
114 {
115 TagsType::const_reverse_iterator it1 = Tags.rbegin();
116 const Tag & publiclast = *it1;
117 last = publiclast;
118 }
119 if( !PrivateTags.empty() )
120 {
121 PrivateTagsType::const_reverse_iterator pit1 = PrivateTags.rbegin();
122 Tag privatelast = *pit1;
123 if( last < privatelast ) last = privatelast;
124 }
125
126 StringFilter sf;
127 Directory::FilenamesType::const_iterator it = Filenames.begin();
128 const double progresstick = 1. / (double)Filenames.size();
129 Progress = 0;
130 for(; it != Filenames.end(); ++it)
131 {
132 Reader reader;
133 const char *filename = it->c_str();
134 assert( filename );
135 reader.SetFileName( filename );
136 bool read = false;
137 try
138 {
139 // Start reading all tags, including the 'last' one:
140 read = reader.ReadUpToTag(last, SkipTags);
141 }
142 catch(std::exception & ex)
143 {
144 (void)ex;
145 gdcmWarningMacro( "Failed to read:" << filename << " with ex:" << ex.what() );
146 }
147 catch(...)
148 {
149 gdcmWarningMacro( "Failed to read:" << filename << " with unknown error" );
150 }
151 if( read )
152 {
153 // Keep the mapping:
154 sf.SetFile( reader.GetFile() );
155 Scanner::ProcessPublicTag(sf, filename);
156 //Scanner::ProcessPrivateTag(sf, filename);
157 }
158 // Update progress
159 Progress += progresstick;
160 ProgressEvent pe;
161 pe.SetProgress( Progress );
162 this->InvokeEvent( pe );
163 // For outside application tell which file is being processed:
164 FileNameEvent fe( filename );
165 this->InvokeEvent( fe );
166 }
167 }
168
169 this->InvokeEvent( EndEvent() );
170 return true;
171 }
172
Print(std::ostream & os) const173 void Scanner::Print( std::ostream & os ) const
174 {
175 os << "Values:\n";
176 for(ValuesType::const_iterator it = Values.begin() ; it != Values.end();
177 ++it)
178 {
179 os << *it << "\n";
180 }
181 os << "Mapping:\n";
182 Directory::FilenamesType::const_iterator file = Filenames.begin();
183 for(; file != Filenames.end(); ++file)
184 {
185 const char *filename = file->c_str();
186 assert( filename && *filename );
187 bool b = IsKey(filename);
188 const char *comment = !b ? "could not be read" : "could be read";
189 os << "Filename: " << filename << " (" << comment << ")\n";
190 //const FilenameToValue &mapping = Mappings[*tag];
191 if( Mappings.find(filename) != Mappings.end() )
192 {
193 const TagToValue &mapping = GetMapping(filename);
194 TagToValue::const_iterator it = mapping.begin();
195 for( ; it != mapping.end(); ++it)
196 {
197 const Tag & tag = it->first;
198 const char *value = it->second;
199 os << tag << " -> [" << value << "]\n";
200 }
201 }
202 }
203 }
204
IsVRUI(Tag const & tag)205 static bool IsVRUI(Tag const &tag)
206 {
207 static const Global &g = Global::GetInstance();
208 static const Dicts &dicts = g.GetDicts();
209 const DictEntry &dictentry = dicts.GetDictEntry(tag);
210 if( dictentry.GetVR() == VR::UI ) return true;
211 //if( tag == Tag(0x0020,0x000d) // Study Instance UID : UI
212 // || tag == Tag(0x0020,0x0052) //
213 // || tag == Tag(0x0020,0x000e) ) // Series Instance UID : UI
214 // {
215 // return true;
216 // }
217 return false;
218 }
219
PrintTable(std::ostream & os) const220 void Scanner::PrintTable( std::ostream & os ) const
221 {
222 Directory::FilenamesType::const_iterator file = Filenames.begin();
223 for(; file != Filenames.end(); ++file)
224 {
225 const char *filename = file->c_str();
226 assert( filename && *filename );
227 os << '"' << filename << '"' << "\t";
228 TagsType::const_iterator tag = Tags.begin();
229 const TagToValue &mapping = GetMapping(filename);
230 for( ; tag != Tags.end(); ++tag )
231 {
232 const Tag &t = *tag;
233 bool isui = IsVRUI(t);
234 const char *value = "";
235 if( mapping.find(t) != mapping.end() ) {
236 const char * v = mapping.find(t)->second;
237 if(v) value = v;
238 }
239 os << '"' << (isui ? String<>::Trim( value ) : value) << '"';
240 os << "\t";
241 }
242 os << "\n";
243 }
244 }
245
GetMapping(const char * filename) const246 Scanner::TagToValue const & Scanner::GetMapping(const char *filename) const
247 {
248 // assert( Mappings.find(filename) != Mappings.end() );
249 assert( filename && *filename );
250 if( Mappings.find(filename) != Mappings.end() )
251 return Mappings.find(filename)->second;
252 return Mappings.find("")->second; // dummy file could not be found
253 }
254
IsKey(const char * filename) const255 bool Scanner::IsKey( const char * filename ) const
256 {
257 /*
258 // std::find on contiguous array will operate in 0(n) which is way too slow, assume user is not too dumb...
259 Directory::FilenamesType::const_iterator it = std::find(Filenames.begin(), Filenames.end(), filename);
260 if( it == Filenames.end() )
261 {
262 gdcmErrorMacro( "The file: " << filename << " was not scanned" );
263 return false;
264 }
265 */
266 // Look for the file in Mappings table:
267 assert( filename && *filename );
268 MappingType::const_iterator it2 = Mappings.find(filename);
269 return it2 != Mappings.end();
270 }
271
272
GetKeys() const273 Directory::FilenamesType Scanner::GetKeys() const
274 {
275 Directory::FilenamesType keys;
276
277 Directory::FilenamesType::const_iterator file = Filenames.begin();
278 for(; file != Filenames.end(); ++file)
279 {
280 const char *filename = file->c_str();
281 if( IsKey( filename ) )
282 {
283 keys.push_back( filename );
284 }
285 }
286 assert( keys.size() <= Filenames.size() );
287 return keys;
288 }
289
290
GetValue(const char * filename,Tag const & t) const291 const char* Scanner::GetValue(const char *filename, Tag const &t) const
292 {
293 // \precondition
294 assert( Tags.find( t ) != Tags.end() );
295 TagToValue const &ftv = GetMapping(filename);
296 if( ftv.find(t) != ftv.end() )
297 {
298 return ftv.find(t)->second;
299 }
300 return nullptr;
301 }
302
GetFilenameFromTagToValue(Tag const & t,const char * valueref) const303 const char *Scanner::GetFilenameFromTagToValue(Tag const &t, const char *valueref) const
304 {
305 const char *filenameref = nullptr;
306 if( valueref )
307 {
308 Directory::FilenamesType::const_iterator file = Filenames.begin();
309 size_t len = strlen( valueref );
310 if( len && valueref[ len - 1 ] == ' ' )
311 {
312 --len;
313 }
314 for(; file != Filenames.end() && !filenameref; ++file)
315 {
316 const char *filename = file->c_str();
317 const char * value = GetValue(filename, t);
318 if( value && strncmp(value, valueref, len ) == 0 )
319 {
320 filenameref = filename;
321 }
322 }
323 }
324 return filenameref;
325 }
326
327
328 /// Will loop over all files and return a vector of std::strings of filenames
329 /// where value match the reference value 'valueref'
330 Directory::FilenamesType
GetAllFilenamesFromTagToValue(Tag const & t,const char * valueref) const331 Scanner::GetAllFilenamesFromTagToValue(Tag const &t, const char *valueref) const
332 {
333 Directory::FilenamesType theReturn;
334 if( valueref )
335 {
336 const std::string valueref_str = String<>::Trim( valueref );
337 Directory::FilenamesType::const_iterator file = Filenames.begin();
338 for(; file != Filenames.end(); ++file)
339 {
340 const char *filename = file->c_str();
341 const char * value = GetValue(filename, t);
342 const std::string value_str = String<>::Trim( value );
343 if( value_str == valueref_str )
344 {
345 theReturn.push_back( filename );
346 }
347 }
348 }
349 return theReturn;
350
351 }
352
GetMappingFromTagToValue(Tag const & t,const char * valueref) const353 Scanner::TagToValue const & Scanner::GetMappingFromTagToValue(Tag const &t, const char *valueref) const
354 {
355 return GetMapping( GetFilenameFromTagToValue(t, valueref) );
356 }
357
GetValues(Tag const & t) const358 Scanner::ValuesType Scanner::GetValues(Tag const &t) const
359 {
360 ValuesType vt;
361 Directory::FilenamesType::const_iterator file = Filenames.begin();
362 for(; file != Filenames.end(); ++file)
363 {
364 const char *filename = file->c_str();
365 TagToValue const &ttv = GetMapping(filename);
366 if( ttv.find(t) != ttv.end() )
367 {
368 vt.insert( ttv.find(t)->second );
369 }
370 }
371 return vt;
372 }
373
374
GetOrderedValues(Tag const & t) const375 Directory::FilenamesType Scanner::GetOrderedValues(Tag const &t) const
376 {
377 Directory::FilenamesType theReturn;
378 Directory::FilenamesType::const_iterator file = Filenames.begin();
379 for(; file != Filenames.end(); ++file)
380 {
381 const char *filename = file->c_str();
382 TagToValue const &ttv = GetMapping(filename);
383 if( ttv.find(t) != ttv.end() )
384 {
385 std::string theVal = std::string(ttv.find(t)->second);
386 if (std::find(theReturn.begin(), theReturn.end(), theVal) == theReturn.end()){
387 theReturn.push_back( theVal );//only add new tags to the list
388 }
389 }
390 }
391 return theReturn;
392 }
393
ProcessPublicTag(StringFilter & sf,const char * filename)394 void Scanner::ProcessPublicTag(StringFilter &sf, const char *filename)
395 {
396 assert( filename );
397 TagToValue &mapping = Mappings[filename];
398 const File& file = sf.GetFile();
399
400 const FileMetaInformation & header = file.GetHeader();
401 const DataSet & ds = file.GetDataSet();
402 TagsType::const_iterator tag = Tags.begin();
403 for( ; tag != Tags.end(); ++tag )
404 {
405 if( tag->GetGroup() == 0x2 )
406 {
407 if( header.FindDataElement( *tag ) )
408 {
409 //std::string s;
410 DataElement const & de = header.GetDataElement( *tag );
411 //const ByteValue *bv = de.GetByteValue();
412 ////assert( VR::IsASCII( vr ) );
413 //if( bv ) // Hum, should I store an empty string or what ?
414 // {
415 // s = std::string( bv->GetPointer(), bv->GetLength() );
416 // s.resize( std::min( s.size(), strlen( s.c_str() ) ) );
417 // }
418 std::string s = sf.ToString(de.GetTag());
419
420 // Store the potentially new value:
421 Values.insert( s );
422 assert( Values.find( s ) != Values.end() );
423 const char *value = Values.find( s )->c_str();
424 assert( value );
425 mapping.insert(
426 TagToValue::value_type(*tag, value));
427 }
428 }
429 else
430 {
431 if( ds.FindDataElement( *tag ) )
432 {
433 //std::string s;
434 DataElement const & de = ds.GetDataElement( *tag );
435 //const ByteValue *bv = de.GetByteValue();
436 ////assert( VR::IsASCII( vr ) );
437 //if( bv ) // Hum, should I store an empty string or what ?
438 // {
439 // s = std::string( bv->GetPointer(), bv->GetLength() );
440 // s.resize( std::min( s.size(), strlen( s.c_str() ) ) );
441 // }
442 std::string s = sf.ToString(de.GetTag());
443
444 // Store the potentially new value:
445 Values.insert( s );
446 assert( Values.find( s ) != Values.end() );
447 const char *value = Values.find( s )->c_str();
448 assert( value );
449 mapping.insert(
450 TagToValue::value_type(*tag, value));
451 }
452 }
453 } // end for
454 }
455
456 } // end namespace gdcm
457