]> Creatis software - gdcm.git/blob - Example/exDicomRTStructSetFile.cxx
no message
[gdcm.git] / Example / exDicomRTStructSetFile.cxx
1  
2  /*=========================================================================
3                                                                                 
4   Program:   gdcm
5   Module:    $RCSfile: exDicomRTStructSetFile.cxx,v $
6   Language:  C++
7   Date:      $Date: 2007/06/21 15:06:13 $
8   Version:   $Revision: 1.2 $
9                                                                                 
10   Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
11   l'Image). All rights reserved. See Doc/License.txt or
12   http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13                                                                                 
14      This software is distributed WITHOUT ANY WARRANTY; without even
15      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
16      PURPOSE.  See the above copyright notices for more information.
17                                                                                 
18 =========================================================================*/
19
20   /* =================================================
21    * freely inspired from : clitkDicomRTIOCommon.cxx
22    * written by : Laurent ZAGNI
23    * on : 18 August 2006
24    * 
25    =================================================*/
26    
27     
28 #include "gdcmFile.h"
29 // ----------
30 //#include "gdcmValEntry.h"
31 #include "gdcmDataEntry.h"
32 // ----------
33 #include "gdcmSeqEntry.h"
34 #include "gdcmSQItem.h"
35 #include "gdcmDocEntrySet.h"
36 #include "gdcmSerieHelper.h"
37 #include "gdcmDirList.h"
38 #include "gdcmUtil.h"
39
40 // ---------- 
41 #define GetEntryValue(g,e) GetEntryString(g,e)
42 #define GetValEntry(g,e)   GetDataEntry(g,e)
43 #define GetValue()         GetString()
44 #define ValEntry           DataEntry
45 #define InsertValEntry(a,b,c) InsertEntryString(a,b,c) // warning mind the VR!
46 // ----------
47
48 #define itkGenericExceptionMacro(a) std::cout<<"===========================Exception "a<<std::endl;return false;
49
50 #include "gdcmArgMgr.h" 
51  
52 bool TestDicomRTStructSetFile(GDCM_NAME_SPACE::File* file); 
53 bool TestDicomCTSerie(const GDCM_NAME_SPACE::FileList serie);
54
55   //====================================================================
56   
57   //====================================================================
58   //In a dicom seq try to find an item with a gived tag/(int)value pair, else creates it
59   GDCM_NAME_SPACE::SQItem* GetAnItemWithTagValue(GDCM_NAME_SPACE::SeqEntry* seqEntry, const uint16_t group, 
60       const uint16_t elem, const int value, const bool writeItem) {
61     int foundValue;
62     std::stringstream valueStream;
63     valueStream<<value;
64     std::string strValue = valueStream.str();
65     GDCM_NAME_SPACE::SQItem* curItem = seqEntry->GetFirstSQItem();
66     GDCM_NAME_SPACE::ValEntry* valEntry;
67     while (curItem != NULL) {
68       valEntry = curItem->GetValEntry(group,elem);
69       std::istringstream(valEntry->GetValue())>>foundValue;
70       if (foundValue == value) {
71          return curItem;
72       }
73       curItem = seqEntry->GetNextSQItem();
74     }
75     if (writeItem == true) {
76       unsigned int newItemNumber = seqEntry->GetNumberOfSQItems ();
77 // ----------       
78       //GDCM_NAME_SPACE::SQItem* newItem = new GDCM_NAME_SPACE::SQItem(seqEntry->GetDepthLevel()+1);
79        GDCM_NAME_SPACE::SQItem* newItem = GDCM_NAME_SPACE::SQItem::New(seqEntry->GetDepthLevel()+1);
80 // ---------- 
81       seqEntry->AddSQItem(newItem,(int)newItemNumber);
82       newItem->InsertValEntry(strValue, group, elem);  /// \TODO : si VR absent, le chercher dans le dict!
83       return newItem;
84     } else {
85       return NULL;
86     }
87   }  
88   
89
90   /* =================================================
91    * freely inspired from : clitkDicomRTIOCommon.cxx
92    * written by : Laurent ZAGNI
93    * on : 18 August 2006
94    * 
95    =================================================*/
96   // Test if a file list is  a valid CT serie
97        
98   bool TestDicomCTSerie(const GDCM_NAME_SPACE::FileList serie) {
99   
100     if (serie.size() < 2) {
101       itkGenericExceptionMacro(<<"Serie must contain at least 2 files !");
102     }
103     
104     float firstSliceImagePosition[3];
105     float currentSliceImagePosition[3];    
106     
107     GDCM_NAME_SPACE::FileList::const_iterator first = serie.begin();
108     GDCM_NAME_SPACE::FileList::const_iterator it = first ++;
109     
110     bool res = (*first)->GetImageOrientationPatient(firstSliceImagePosition);
111     if (!res) {
112        itkGenericExceptionMacro(<<"No ImagePositionPatient found;");
113     }
114     
115     while (it != serie.end()) {
116       if (!GDCM_NAME_SPACE::Util::DicomStringEqual((*it)->GetEntryValue(0x0008,0x0016),"1.2.840.10008.5.1.4.1.1.2")) {
117            itkGenericExceptionMacro();
118            //CT Dicom slices must have a SOP Class UID [0008|0016] = [1.2.840.10008.5.1.4.1.1.2] ==> [CT Image Storage]
119       }
120       
121       if (!GDCM_NAME_SPACE::Util::DicomStringEqual((*it)->GetEntryValue(0x0008,0x0060),"CT")) {
122         itkGenericExceptionMacro();
123        //CT Dicom slices must have a Modality [0008|0060] = [CT]
124       }
125       
126       if (!GDCM_NAME_SPACE::Util::DicomStringEqual((*it)->GetEntryValue(0x0020,0x0037),"1.0000\\0.0000\\0.0000\\0.0000\\1.0000\\0.0000")) {
127          itkGenericExceptionMacro("CT Dicom slices must an Image Orientation [0020|0037]"
128          <<" = [1.0000\0.0000\0.0000\0.0000\1.0000\0.0000]");  
129       }
130       
131       res =(*it)->GetImageOrientationPatient(currentSliceImagePosition);
132       if (!res) {
133          itkGenericExceptionMacro(<<"No ImagePositionPatient found;");
134       }
135       if ((firstSliceImagePosition[0] != currentSliceImagePosition[0])||(firstSliceImagePosition[1] != currentSliceImagePosition[1])){
136          itkGenericExceptionMacro("All CT Dicom slices must have same image position on x and y [0020|0032]");      
137       }
138       
139       if ((*it)->GetEntryValue(0x0028,0x0030) != (*first)->GetEntryValue(0x0028,0x0030).c_str()) {
140         itkGenericExceptionMacro("All CT Dicom slices must have same Pixel Spacing [0028|0030]");
141       }
142       
143       if ((*it)->GetEntryValue(0x0028,0x0010) != (*first)->GetEntryValue(0x0028,0x0010).c_str()) {
144          itkGenericExceptionMacro("All CT Dicom slices must have same Rows number [0028|0010]");      
145       }
146       
147       if ((*it)->GetEntryValue(0x0028,0x0011) != (*first)->GetEntryValue(0x0028,0x0011).c_str()) {
148          itkGenericExceptionMacro("All CT Dicom slices must have same Columns number [0028|0011]");     
149       }
150       
151       if ((*it)->GetEntryValue(0x0018,0x0050) != (*first)->GetEntryValue(0x0018,0x0050).c_str()) {
152          itkGenericExceptionMacro("All CT Dicom slices must have same slice thickness [0018|0050]");    
153       }
154       it++;
155     }
156     //it = serie.begin();
157     //     while (it != serie.end()) {
158     //       fileName = (*it)->GetFileName();
159     //       std::cout << fileName << std::endl;
160     //       it++;
161     //     }
162   }
163
164
165
166
167 // -----------------------------------------------------------------------------------------------------------------
168   /* =================================================
169    * freely inspired from : clitkDicomRTIOCommon.cxx
170    * written by : Laurent ZAGNI
171    * on : 18 August 2006
172    * 
173    =================================================*/
174     
175  // Test if a file is a valid Dicom-RT Structure-Set file (readable by us) 
176   bool TestDicomRTStructSetFile(GDCM_NAME_SPACE::File* file) {
177 // ----------  
178     //GDCM_NAME_SPACE::ValEntry* valEntry;
179     GDCM_NAME_SPACE::DataEntry* valEntry;
180 // ---------- 
181
182     std::string exception0 = "Not a [RT Structure Set Storage]";
183     std::string exception1 = "2 or more different 'Roi Referenced Frame UID'!";
184     std::string exception2 = "No 'Series Instance UID'!";
185     std::string exception3 = "Modality not= RTSTRUCT"; 
186             
187     //Verify if the file is a RT-Structure-Set dicom file
188     if (!GDCM_NAME_SPACE::Util::DicomStringEqual(file->GetEntryValue(0x0008,0x0016),"1.2.840.10008.5.1.4.1.1.481.3")) {  //SOP clas UID
189       itkGenericExceptionMacro(<<exception0);
190       // (the file must have a SOP Class UID [0008|0016] = 1.2.840.10008.5.1.4.1.1.481.3 ==> [RT Structure Set Storage] !
191     }
192         
193     if (!GDCM_NAME_SPACE::Util::DicomStringEqual(file->GetEntryValue(0x0008,0x0060),"RTSTRUCT")) {  //SOP clas UID
194       itkGenericExceptionMacro(<<exception3);
195       // (the file must have a Modality tag = RTSTRUCT !
196     }    
197
198     //Verify only one Referenced Frame UID and one or more Series UID
199
200     GDCM_NAME_SPACE::SeqEntry* seqEntry;
201     GDCM_NAME_SPACE::SQItem* currentItem;
202     std::string currentFrameRefUID;
203     
204     seqEntry  = file->GetSeqEntry(0x3006,0x0020); //Structure Set ROI sequence
205     if (seqEntry->GetNumberOfSQItems() > 1) {
206       currentItem = seqEntry->GetFirstSQItem();
207       valEntry = currentItem->GetValEntry(0x3006,0x0024); //Roi Frame of Reference UID
208       currentFrameRefUID = valEntry->GetValue();
209       currentItem = seqEntry->GetNextSQItem();
210       while (currentItem != NULL) {
211         valEntry = currentItem->GetValEntry(0x3006,0x0024); //Roi Frame of Reference UID
212         if (valEntry->GetValue() != currentFrameRefUID) {
213         // std::cout<<"..ref UID"<<currentFrameRefUID<<std::endl;
214         // std::cout<<"..new UID"<<valEntry->GetValue().c_str()<<std::endl;
215         // std::cout<<"..exception"<<std::endl;
216         itkGenericExceptionMacro(<<exception1);
217         }
218         currentItem = seqEntry->GetNextSQItem();
219       }
220     }
221     
222     seqEntry = file->GetSeqEntry(0x3006,0x0010); //Referenced Frame of Reference Sequence
223     currentItem = seqEntry->GetFirstSQItem();
224     while (currentItem != NULL) {
225       valEntry = currentItem->GetValEntry(0x0020,0x0052);  //Frame of Reference UID
226       if (valEntry->GetValue() == currentFrameRefUID) { //search for the ROI Frame of Reference UID
227         break;
228       }else {
229          currentItem = seqEntry->GetNextSQItem();
230       }
231     }
232     if (currentItem == NULL) {
233       itkGenericExceptionMacro(<<exception1);
234     }
235     
236     GDCM_NAME_SPACE::SeqEntry* seqEntry2 = currentItem->GetSeqEntry(0x3006,0x0012); //Referenced Study sequence
237     if (seqEntry2->GetNumberOfSQItems() < 1) {
238       itkGenericExceptionMacro(<<exception2);    
239     }
240     
241     valEntry = file->GetValEntry(0x0020,0x0010); //study ID
242     if ((valEntry == 0) || (valEntry->GetValue() == "")) { //(type 1 : mandatory attribute)
243       itkGenericExceptionMacro("RT-Structure-Set file must contain Study ID [0020|0010] !");    
244     }
245     
246     valEntry = file->GetValEntry(0x3006,0x002); //structure set label 
247     if ((valEntry == 0) || (valEntry->GetValue() == "")) { //(type 1 : mandatory attribute)
248       itkGenericExceptionMacro("RT-Structure-Set file must contain Structure Set Label [0006|0002] !");   
249     }
250     
251     seqEntry = file->GetSeqEntry(0x3006,0x0020); //Structure Set ROI sequence
252     if (seqEntry != 0) {
253       currentItem = seqEntry->GetFirstSQItem();
254       while (currentItem != NULL) {
255          valEntry = currentItem->GetValEntry(0x3006,0x0022); //Roi number
256          if ((valEntry == 0) || (valEntry->GetValue() == "")) { //(type 1 : mandatory attribute)
257            itkGenericExceptionMacro("All ROI in Structure Set Roi Sequence must contain Roi Number [3006|0022] !");   
258          }
259          currentItem = seqEntry->GetNextSQItem();
260       }
261     }
262     
263     seqEntry = file->GetSeqEntry(0x3006,0x0080); //ROI Observation sequence
264     if (seqEntry == 0) { //(type 1 : mandatory attribute)
265       itkGenericExceptionMacro("RT-Structure-Set file must contain ROI Observations Sequence [3006|0080] !");        
266     }
267     
268     seqEntry = file->GetSeqEntry(0x3006,0x0039); //ROI Contour sequence
269     if (seqEntry == 0) { //(type 1 : mandatory attribute)
270       itkGenericExceptionMacro("RT-Structure-Set file must contain ROI Contour Sequence [3006|0039] !");    
271     }
272     std::cout<<"..."<<file->GetFileName()<<" is a valid DICOM RT-Structure-Set file"<<std::endl;
273   }
274
275 // -------------------------------------------------------------------------------------------------------
276
277 int main(int argc, char *argv[])
278 {
279    START_USAGE(usage)
280    "\n exExtractCSA :\n                                                       ",
281    "Extracts and displays the CSA tag(s) of gdcm-parsable Dicom file          ",
282    "                                                                          ",
283    "usage: exExtractCSA {filein=inputFileName|dirin=inputDirectoryName}       ",
284    "                   tmp=temporaryWorkFileName                              ",
285    "                       [extract=listOfElementsToExtract]                  ",
286    "                       [ { [noshadowseq] | [noshadow][noseq] } ] [debug]  ",
287    "       inputFileName : Name of the (single) file user wants to anonymize  ",
288    "       listOfElementsExtract : group-elem,g2-e2,... (in hexa, no space)   ",
289    "                                of Elements to extract                    ",
290    "                              default : 0029-1210,0029-1220               ",
291    "       noshadowseq: user doesn't want to load Private Sequences           ",
292    "       noshadow   : user doesn't want to load Private groups (odd number) ",
293    "       noseq      : user doesn't want to load Sequences                   ",
294    "       verbose    : developper wants to run the program in 'verbose mode' ",
295    "       debug      : developper wants to run the program in 'debug mode'   ",
296    FINISH_USAGE
297
298    // ----- Initialize Arguments Manager ------
299   
300    GDCM_NAME_SPACE::ArgMgr *am = new GDCM_NAME_SPACE::ArgMgr(argc, argv);
301   
302    if (am->ArgMgrDefined("usage") || argc == 1) 
303    {
304       am->ArgMgrUsage(usage); // Display 'usage'
305       delete am;
306       return 0;
307    }
308    
309       if (am->ArgMgrDefined("debug"))
310       GDCM_NAME_SPACE::Debug::DebugOn();
311
312    bool verbose = am->ArgMgrDefined("verbose");
313    
314    const char *fileName = am->ArgMgrGetString("filein");
315    
316       /* if unused Param we give up */
317    if ( am->ArgMgrPrintUnusedLabels() )
318    {
319       am->ArgMgrUsage(usage);
320       delete am;
321       return 0;
322    }  
323    delete am;  // ------ we don't need Arguments Manager any longer ------
324
325 // ============================================================
326 //   Read the input image.
327 // ============================================================ 
328
329    GDCM_NAME_SPACE::File *f = GDCM_NAME_SPACE::File::New( );
330
331    //f->SetLoadMode(GDCM_NAME_SPACE::LD_NOSEQ | GDCM_NAME_SPACE::LD_NOSHADOW);
332    f->SetFileName( fileName );
333    f->SetMaxSizeLoadEntry(0xffff);
334    bool res = f->Load();  
335
336    if( GDCM_NAME_SPACE::Debug::GetDebugFlag())
337    {
338       std::cout << "---------------------------------------------" << std::endl;
339       f->Print();
340       std::cout << "---------------------------------------------" << std::endl;
341    }
342    if (!res) {
343        std::cerr << "Sorry, " << fileName << " not a gdcm-readable "
344            << "DICOM / ACR File"
345            << std::endl;
346       f->Delete();
347       return 1;
348    }
349    std::cout << " ... is readable " << std::endl;
350       
351    
352    TestDicomRTStructSetFile(f);
353    std::cout << " ... is readable " << std::endl;   
354
355    
356    
357    f->Delete();   
358 return 0;
359 }      
360