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 "gdcmTesting.h"
16 #include "gdcmIPPSorter.h"
17 #include "gdcmDirectionCosines.h"
18 #include <cmath>
19 
20 /*
21  * The following example is a basic sorted which should work in generic cases.
22  * It sort files based on:
23  * Study Instance UID
24  *   Series Instance UID
25  *     Frame of Reference UID
26  *       Image Orientation (Patient)
27  *         Image Position (Patient) (Sorting based on IPP + IOP)
28  */
29 
30 namespace gdcm {
31   const Tag t1(0x0020,0x000d); // Study Instance UID
32   const Tag t2(0x0020,0x000e); // Series Instance UID
33   const Tag t3(0x0020,0x0052); // Frame of Reference UID
34   const Tag t4(0x0020,0x0037); // Image Orientation (Patient)
35 
36 class DiscriminateVolume
37 {
38 private:
39   std::vector< Directory::FilenamesType > SortedFiles;
40   std::vector< Directory::FilenamesType > UnsortedFiles;
41 
GetAllFilenamesFromTagToValue(Scanner const & s,Directory::FilenamesType const & filesubset,Tag const & t,const char * valueref)42   Directory::FilenamesType GetAllFilenamesFromTagToValue(
43     Scanner const & s, Directory::FilenamesType const &filesubset, Tag const &t, const char *valueref)
44 {
45   Directory::FilenamesType theReturn;
46   if( valueref )
47     {
48     size_t len = strlen( valueref );
49     Directory::FilenamesType::const_iterator file = filesubset.begin();
50     for(; file != filesubset.end(); ++file)
51       {
52       const char *filename = file->c_str();
53       const char * value = s.GetValue(filename, t);
54       if( value && strncmp(value, valueref, len ) == 0 )
55         {
56         theReturn.push_back( filename );
57         }
58       }
59     }
60   return theReturn;
61 }
62 
ProcessAIOP(Scanner const &,Directory::FilenamesType const & subset,const char * iopval)63 void ProcessAIOP(Scanner const & , Directory::FilenamesType const & subset, const char *iopval)
64 {
65   std::cout << "IOP: " << iopval << std::endl;
66   IPPSorter ipp;
67   ipp.SetComputeZSpacing( true );
68   ipp.SetZSpacingTolerance( 1e-3 ); // ??
69   bool b = ipp.Sort( subset );
70   if( !b )
71     {
72     // If you reach here this means you need one more parameter to discriminiat this
73     // series. Eg. T1 / T2 intertwinted. Multiple Echo (0018,0081)
74     std::cerr << "Failed to sort: " << subset.begin()->c_str() << std::endl;
75     for(
76       Directory::FilenamesType::const_iterator file = subset.begin();
77       file != subset.end(); ++file)
78       {
79       std::cerr << *file << std::endl;
80       }
81     UnsortedFiles.push_back( subset );
82     return ;
83     }
84   ipp.Print( std::cout );
85   SortedFiles.push_back( ipp.GetFilenames() );
86 }
87 
ProcessAFrameOfRef(Scanner const & s,Directory::FilenamesType const & subset,const char * frameuid)88 void ProcessAFrameOfRef(Scanner const & s, Directory::FilenamesType const & subset, const char * frameuid)
89 {
90   // In this subset of files (belonging to same series), let's find those
91   // belonging to the same Frame ref UID:
92   Directory::FilenamesType files = GetAllFilenamesFromTagToValue(
93     s, subset, t3, frameuid);
94 
95   std::set< std::string > iopset;
96 
97   for(
98     Directory::FilenamesType::const_iterator file = files.begin();
99     file != files.end(); ++file)
100     {
101     //std::cout << *file << std::endl;
102     const char * value = s.GetValue(file->c_str(), gdcm::t4 );
103     assert( value );
104     iopset.insert( value );
105     }
106   size_t n = iopset.size();
107   if ( n == 0 )
108     {
109     assert( files.empty() );
110     return;
111     }
112 
113   std::cout << "Frame of Ref: " << frameuid << std::endl;
114   if ( n == 1 )
115     {
116     ProcessAIOP(s, files, iopset.begin()->c_str() );
117     }
118   else
119     {
120     const char *f = files.begin()->c_str();
121     std::cerr << "More than one IOP: " << f << std::endl;
122     // Make sure that there is actually 'n' different IOP
123     gdcm::DirectionCosines ref;
124     gdcm::DirectionCosines dc;
125     for(
126       std::set< std::string >::const_iterator it = iopset.begin();
127       it != iopset.end(); ++it )
128       {
129       ref.SetFromString( it->c_str() );
130       for(
131         Directory::FilenamesType::const_iterator file = files.begin();
132         file != files.end(); ++file)
133         {
134         std::string value = s.GetValue(file->c_str(), gdcm::t4 );
135         if( value != it->c_str() )
136           {
137           dc.SetFromString( value.c_str() );
138           const double crossdot = ref.CrossDot(dc);
139           const double eps = std::fabs( 1. - crossdot );
140           if( eps < 1e-6 )
141             {
142             std::cerr << "Problem with IOP discrimination: " << file->c_str()
143               << " " << it->c_str() << std::endl;
144             return;
145             }
146           }
147         }
148       }
149       // If we reach here this means there is actually 'n' different IOP
150     for(
151       std::set< std::string >::const_iterator it = iopset.begin();
152       it != iopset.end(); ++it )
153       {
154       const char *iopvalue = it->c_str();
155       Directory::FilenamesType iopfiles = GetAllFilenamesFromTagToValue(
156         s, files, t4, iopvalue );
157       ProcessAIOP(s, iopfiles, iopvalue );
158       }
159     }
160 }
161 
ProcessASeries(Scanner const & s,const char * seriesuid)162 void ProcessASeries(Scanner const & s, const char * seriesuid)
163 {
164   std::cout << "Series: " << seriesuid << std::endl;
165   // let's find all files belonging to this series:
166   Directory::FilenamesType seriesfiles = GetAllFilenamesFromTagToValue(
167     s, s.GetFilenames(), t2, seriesuid);
168 
169   gdcm::Scanner::ValuesType vt3 = s.GetValues(t3);
170   for(
171     gdcm::Scanner::ValuesType::const_iterator it = vt3.begin()
172     ; it != vt3.end(); ++it )
173     {
174     ProcessAFrameOfRef(s, seriesfiles, it->c_str());
175     }
176 }
177 
ProcessAStudy(Scanner const & s,const char * studyuid)178 void ProcessAStudy(Scanner const & s, const char * studyuid)
179 {
180   std::cout << "Study: " << studyuid << std::endl;
181   gdcm::Scanner::ValuesType vt2 = s.GetValues(t2);
182   for(
183     gdcm::Scanner::ValuesType::const_iterator it = vt2.begin()
184     ; it != vt2.end(); ++it )
185     {
186     ProcessASeries(s, it->c_str());
187     }
188 }
189 public:
190 
Print(std::ostream & os)191 void Print( std::ostream & os )
192 {
193   os << "Sorted Files: " << std::endl;
194   for(
195     std::vector< Directory::FilenamesType >::const_iterator it = SortedFiles.begin();
196     it != SortedFiles.end(); ++it )
197     {
198     os << "Group: " << std::endl;
199     for(
200       Directory::FilenamesType::const_iterator file = it->begin();
201       file != it->end(); ++file)
202       {
203       os << *file << std::endl;
204       }
205     }
206   os << "Unsorted Files: " << std::endl;
207   for(
208     std::vector< Directory::FilenamesType >::const_iterator it = UnsortedFiles.begin();
209     it != UnsortedFiles.end(); ++it )
210     {
211     os << "Group: " << std::endl;
212     for(
213       Directory::FilenamesType::const_iterator file = it->begin();
214       file != it->end(); ++file)
215       {
216       os << *file << std::endl;
217       }
218     }
219 
220 }
221 
GetSortedFiles() const222   std::vector< Directory::FilenamesType > const & GetSortedFiles() const { return SortedFiles; }
GetUnsortedFiles() const223   std::vector< Directory::FilenamesType > const & GetUnsortedFiles() const { return UnsortedFiles; }
224 
ProcessIntoVolume(Scanner const & s)225 void ProcessIntoVolume( Scanner const & s )
226 {
227   gdcm::Scanner::ValuesType vt1 = s.GetValues( gdcm::t1 );
228   for(
229     gdcm::Scanner::ValuesType::const_iterator it = vt1.begin()
230     ; it != vt1.end(); ++it )
231     {
232     ProcessAStudy( s, it->c_str() );
233     }
234 
235 }
236 
237 };
238 
239 } // namespace gdcm
240 
main(int argc,char * argv[])241 int main(int argc, char *argv[])
242 {
243   std::string dir1;
244   if( argc < 2 )
245     {
246     const char *extradataroot = nullptr;
247 #ifdef GDCM_BUILD_TESTING
248     extradataroot = gdcm::Testing::GetDataExtraRoot();
249 #endif
250     if( !extradataroot )
251       {
252       return 1;
253       }
254     dir1 = extradataroot;
255     dir1 += "/gdcmSampleData/ForSeriesTesting/VariousIncidences/ST1";
256     }
257   else
258     {
259     dir1 = argv[1];
260     }
261 
262   gdcm::Directory d;
263   d.Load( dir1.c_str(), true ); // recursive !
264 
265   gdcm::Scanner s;
266   s.AddTag( gdcm::t1 );
267   s.AddTag( gdcm::t2 );
268   s.AddTag( gdcm::t3 );
269   s.AddTag( gdcm::t4 );
270   bool b = s.Scan( d.GetFilenames() );
271   if( !b )
272     {
273     std::cerr << "Scanner failed" << std::endl;
274     return 1;
275     }
276 
277   gdcm::DiscriminateVolume dv;
278   dv.ProcessIntoVolume( s );
279   dv.Print( std::cout );
280 
281   return 0;
282 }
283