]> Creatis software - clitk.git/blob - common/clitkDicomRT_StructureSet.cxx
clitkDicomRTStruct2Image: new features
[clitk.git] / common / clitkDicomRT_StructureSet.cxx
1 /*=========================================================================
2   Program:         vv http://www.creatis.insa-lyon.fr/rio/vv
3   Main authors :   XX XX XX
4
5   Authors belongs to:
6   - University of LYON           http://www.universite-lyon.fr/
7   - Léon Bérard cancer center    http://www.centreleonberard.fr
8   - CREATIS CNRS laboratory      http://www.creatis.insa-lyon.fr
9
10   This software is distributed WITHOUT ANY WARRANTY; without even
11   the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12   PURPOSE.  See the copyright notices for more information.
13
14   It is distributed under dual licence
15   - BSD       http://www.opensource.org/licenses/bsd-license.php
16   - CeCILL-B  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17
18   =========================================================================*/
19
20 #include "clitkDicomRT_StructureSet.h"
21 #include <vtksys/SystemTools.hxx>
22 #include "gdcmFile.h"
23
24 //--------------------------------------------------------------------
25 clitk::DicomRT_StructureSet::DicomRT_StructureSet()
26 {
27   mStudyID = "NoStudyID";
28   mStudyTime = "NoStudyTime";
29   mStudyDate = "NoStudyDate";
30   mLabel = "NoLabel";
31   mName = "NoName";
32   mDate = "NoDate";
33   mTime = "NoTime";
34   mFile = NULL;
35 }
36 //--------------------------------------------------------------------
37
38
39 //--------------------------------------------------------------------
40 clitk::DicomRT_StructureSet::~DicomRT_StructureSet()
41 {
42 }
43 //--------------------------------------------------------------------
44
45
46 //--------------------------------------------------------------------
47 const std::string & clitk::DicomRT_StructureSet::GetStudyID() const
48 {
49   return mStudyID;
50 }
51 //--------------------------------------------------------------------
52
53
54 //--------------------------------------------------------------------
55 const std::string & clitk::DicomRT_StructureSet::GetStudyTime() const
56 {
57   return mStudyTime;
58 }
59 //--------------------------------------------------------------------
60
61
62 //--------------------------------------------------------------------
63 const std::string & clitk::DicomRT_StructureSet::GetStudyDate() const
64 {
65   return mStudyDate;
66 }
67 //--------------------------------------------------------------------
68
69
70 //--------------------------------------------------------------------
71 const std::string & clitk::DicomRT_StructureSet::GetLabel() const
72 {
73   return mLabel;
74 }
75 //--------------------------------------------------------------------
76
77
78 //--------------------------------------------------------------------
79 const std::string & clitk::DicomRT_StructureSet::GetName() const
80 {
81   return mName;
82 }
83 //--------------------------------------------------------------------
84
85
86 //--------------------------------------------------------------------
87 const std::string & clitk::DicomRT_StructureSet::GetDate() const
88 {
89   return mDate;
90 }
91 //--------------------------------------------------------------------
92
93
94 //--------------------------------------------------------------------
95 const std::string & clitk::DicomRT_StructureSet::GetTime() const
96 {
97   return mTime;
98 }
99 //--------------------------------------------------------------------
100
101
102 //--------------------------------------------------------------------
103 // const std::vector<clitk::DicomRT_ROI::Pointer> & clitk::DicomRT_StructureSet::GetListOfROI() const
104 // {
105 //   return mListOfROI;
106 // }
107 //--------------------------------------------------------------------
108
109
110 //--------------------------------------------------------------------
111 clitk::DicomRT_ROI* clitk::DicomRT_StructureSet::GetROIFromROINumber(int n)
112 {
113   if (mROIs.find(n) == mROIs.end()) {
114     std::cerr << "No ROI number " << n << std::endl;
115     return NULL;
116   }
117   return mROIs[n];
118 }
119 //--------------------------------------------------------------------
120
121 //--------------------------------------------------------------------
122 clitk::DicomRT_ROI* clitk::DicomRT_StructureSet::GetROIFromROIName(const std::string& name)
123 {
124   std::map<int, std::string>::iterator it = mMapOfROIName.begin();
125   int number = -1;
126   while (it != mMapOfROIName.end() && number == -1) {
127     if (it->second == name)
128       number = it->first;
129     else
130       it++;
131   }
132
133   if (number == -1) {
134     std::cerr << "No ROI name " << name << std::endl;
135     return NULL;
136   }
137   
138   return mROIs[number];
139 }
140 //--------------------------------------------------------------------
141 /*
142 // RP: 08/02/2013
143 // RegEx version shall be available when C++x11 supports it propely
144 //
145 //--------------------------------------------------------------------
146 clitk::DicomRT_ROI* clitk::DicomRT_StructureSet::GetROIFromROINameRegEx(const std::string& regEx)
147 {
148   std::map<int, std::string>::iterator it = mMapOfROIName.begin();
149   int number = -1;
150
151   while (it != mMapOfROIName.end() && number == -1) {
152     if (std::tr1::regex_match (it->second, std::tr1::regex(regEx)))
153       number = it->first;
154     else
155       it++;
156   }
157
158   if (number == -1) {
159     std::cerr << "No ROI name " << number << std::endl;
160     return NULL;
161   }
162   
163   return mROIs[number];
164 }
165 //--------------------------------------------------------------------
166 */
167 //--------------------------------------------------------------------
168 clitk::DicomRT_ROI* clitk::DicomRT_StructureSet::GetROIFromROINameSubstr(const std::string& s)
169 {
170   std::map<int, std::string>::iterator it = mMapOfROIName.begin();
171   int number = -1;
172
173   while (it != mMapOfROIName.end() && number == -1) {
174     if (it->second.find(s) != std::string::npos)
175       number = it->first;
176     else
177       it++;
178   }
179
180   if (number == -1) {
181     std::cerr << "No ROI name " << s << std::endl;
182     return NULL;
183   }
184   
185   return mROIs[number];
186 }
187 //--------------------------------------------------------------------
188
189 //--------------------------------------------------------------------
190 clitk::DicomRT_StructureSet::ROIMapContainer * 
191 clitk::DicomRT_StructureSet::GetROIsFromROINameSubstr(const std::string& s)
192 {
193   static ROIMapContainer rois;
194   rois.clear();
195   
196   ROIMapContainer::iterator it = mROIs.begin();
197   int number = -1;
198
199   while (it != mROIs.end()) {
200     if (it->second->GetName().find(s) != std::string::npos) {
201       number = it->first;
202       rois[number] = it->second;
203     }
204     it++;
205   }
206
207   if (number == -1) {
208     std::cerr << "No ROI name " << s << std::endl;
209     return NULL;
210   }
211   
212   return &rois;
213   
214 }
215 //--------------------------------------------------------------------
216
217 //--------------------------------------------------------------------
218 void clitk::DicomRT_StructureSet::Print(std::ostream & os) const
219 {
220   os << "Study ID      = " << mStudyID << std::endl
221      << "Study Date    = " << mStudyDate << std::endl
222      << "Study Time    = " << mStudyTime << std::endl
223      << "Struct Label  = " << mLabel << std::endl
224      << "Struct Name   = " << mName << std::endl
225      << "Struct Time   = " << mTime << std::endl
226      << "Number of ROI = " << mROIs.size() << std::endl;
227   for(ROIConstIteratorType iter = mROIs.begin(); iter != mROIs.end(); iter++) {
228     iter->second->Print(os);
229   }
230 }
231 //--------------------------------------------------------------------
232
233
234 #if GDCM_MAJOR_VERSION == 2
235 //--------------------------------------------------------------------
236 int clitk::DicomRT_StructureSet::ReadROINumber(const gdcm::Item & item)
237 {
238   // 0x3006,0x0022 = [ROI Number]
239   const gdcm::DataSet & nestedds = item.GetNestedDataSet();
240   gdcm::Attribute<0x3006,0x0022> roinumber;
241   roinumber.SetFromDataSet( nestedds );
242   return roinumber.GetValue();  
243 }
244 //--------------------------------------------------------------------
245 #endif
246
247 //--------------------------------------------------------------------
248 void clitk::DicomRT_StructureSet::Write(const std::string & filename)
249 {
250 #if GDCM_MAJOR_VERSION == 2
251   DD("DCM RT Writer");
252
253   // Assert that the gdcm file is still open (we can write only if it was readed)
254   if (mFile == NULL) {
255     //assert(mFile != NULL);
256     FATAL("Sorry, I can write DICOM only if it was read first from a file with 'Read' function");
257   }
258
259   // Loop and update each ROI 
260   for(ROIIteratorType iter = mROIs.begin(); iter != mROIs.end(); iter++) {
261     iter->second->UpdateDicomItem();
262   }
263
264   // Write [ Structure Set ROI Sequence ] = 0x3006,0x0020
265   gdcm::DataSet & ds = mFile->GetDataSet();
266   gdcm::Tag tssroisq(0x3006,0x0020);
267   const gdcm::DataElement &ssroisq = ds.GetDataElement( tssroisq );
268   gdcm::DataElement de(ssroisq);
269   de.SetValue(*mROIInfoSequenceOfItems);
270   ds.Replace(de);
271   
272   // Write [ ROI Contour Sequence ] = 0x3006,0x0039 
273   DD("ici");
274   gdcm::Tag troicsq(0x3006,0x0039);
275   const gdcm::DataElement &roicsq = ds.GetDataElement( troicsq );
276   gdcm::DataElement de2(roicsq);
277   de2.SetValue(*mROIContoursSequenceOfItems);
278   ds.Replace(de2);
279
280   //DEBUG
281   gdcm::DataSet & a = mROIContoursSequenceOfItems->GetItem(1).GetNestedDataSet();
282   gdcm::Tag tcsq(0x3006,0x0040);
283   const gdcm::DataElement& csq = a.GetDataElement( tcsq );
284   gdcm::SmartPointer<gdcm::SequenceOfItems> sqi2 = csq.GetValueAsSQ();
285   gdcm::Item & j = sqi2->GetItem(1);
286   gdcm::DataSet & b = j.GetNestedDataSet();
287   gdcm::Attribute<0x3006,0x0050> at;
288   gdcm::Tag tcontourdata(0x3006,0x0050);
289   gdcm::DataElement contourdata = b.GetDataElement( tcontourdata );
290   at.SetFromDataElement( contourdata );
291   const double* points = at.GetValues();
292   DD(points[0]);
293
294
295   // Write dicom
296   gdcm::Writer writer;
297   //writer.CheckFileMetaInformationOff();
298   writer.SetFileName(filename.c_str());
299   writer.SetFile(*mFile);
300   DD("before write");
301   writer.Write();
302   DD("End write");
303 #else
304   FATAL("Sorry not compatible with GDCM1, use GDCM2");
305 #endif
306 }
307 //--------------------------------------------------------------------
308
309
310 //--------------------------------------------------------------------
311 void clitk::DicomRT_StructureSet::Read(const std::string & filename)
312 {
313   // Open DICOM
314 #if GDCM_MAJOR_VERSION == 2
315   // Read gdcm file
316   mReader = new gdcm::Reader;
317   mReader->SetFileName(filename.c_str());
318   mReader->Read();
319   mFile = &(mReader->GetFile());
320   const gdcm::DataSet & ds = mFile->GetDataSet();
321   
322   // Check file type
323   //Verify if the file is a RT-Structure-Set dicom file
324   gdcm::MediaStorage ms;
325   ms.SetFromFile(*mFile);
326   if( ms != gdcm::MediaStorage::RTStructureSetStorage )
327     {
328     std::cerr << "Error. the file " << filename
329               << " is not a Dicom Struct ? (must have a SOP Class UID [0008|0016] = 1.2.840.10008.5.1.4.1.1.481.3 ==> [RT Structure Set Storage])"
330               << std::endl;
331     exit(0);
332     }
333
334   gdcm::Attribute<0x8,0x60> modality;
335   modality.SetFromDataSet( ds );
336   if( modality.GetValue() != "RTSTRUCT" )
337     {
338     std::cerr << "Error. the file " << filename
339               << " is not a Dicom Struct ? (must have 0x0008,0x0060 = RTSTRUCT [RT Structure Set Storage])"
340               << std::endl;
341     exit(0);
342     }
343
344   // Read global info
345   gdcm::Attribute<0x20,0x10> studyid;
346   studyid.SetFromDataSet( ds );
347   gdcm::Attribute<0x8,0x20> studytime;
348   studytime.SetFromDataSet( ds );
349   gdcm::Attribute<0x8,0x30> studydate;
350   studydate.SetFromDataSet( ds );
351   gdcm::Attribute<0x3006,0x02> label;
352   label.SetFromDataSet( ds );
353   gdcm::Attribute<0x3006,0x04> atname;
354   atname.SetFromDataSet( ds );
355   gdcm::Attribute<0x3006,0x09> time;
356   time.SetFromDataSet( ds );
357
358   mStudyID   = studyid.GetValue();
359   mStudyTime = studytime.GetValue();
360   mStudyDate = studydate.GetValue();
361   mLabel     = label.GetValue();
362   mName      = atname.GetValue();
363   mTime      = time.GetValue();
364
365   // Temporary store the list of items
366   std::map<int, gdcm::Item*> mMapOfROIInfo;
367   std::map<int, gdcm::Item*> mMapOfROIContours;
368
369   //----------------------------------
370   // Read all ROI Names and number
371   // 0x3006,0x0020 = [ Structure Set ROI Sequence ]
372   gdcm::Tag tssroisq(0x3006,0x0020);
373   const gdcm::DataElement &ssroisq = ds.GetDataElement( tssroisq );
374   mROIInfoSequenceOfItems = ssroisq.GetValueAsSQ();
375   gdcm::SmartPointer<gdcm::SequenceOfItems> & roi_seq = mROIInfoSequenceOfItems;
376   assert(roi_seq); // TODO error message
377   for(unsigned int ridx = 0; ridx < roi_seq->GetNumberOfItems(); ++ridx)
378     {
379     gdcm::Item & item = roi_seq->GetItem( ridx + 1); // Item starts at 1
380     const gdcm::DataSet& nestedds = item.GetNestedDataSet();
381
382     gdcm::Attribute<0x3006,0x26> roiname;
383     roiname.SetFromDataSet( nestedds );
384     std::string name = roiname.GetValue(); // 0x3006,0x0026 = [ROI Name]
385
386     // 0x3006,0x0022 = [ROI Number]
387     int nb = ReadROINumber(item);
388
389     // Store the item
390     mMapOfROIInfo[nb] = &item;
391
392     // Check if such a number already exist
393     if (mMapOfROIName.find(nb) != mMapOfROIName.end()) {
394       std::cerr << "WARNING. A Roi already exist with the number "
395         << nb << ". I replace." << std::endl;
396     }
397     // Add in map
398     mMapOfROIName[nb] = name;
399     }
400
401   //----------------------------------
402   // Read all ROI item
403   // 0x3006,0x0039 = [ ROI Contour Sequence ]
404   gdcm::Tag troicsq(0x3006,0x0039);
405   const gdcm::DataElement &roicsq = ds.GetDataElement( troicsq );
406   gdcm::SmartPointer<gdcm::SequenceOfItems> roi_contour_seq = roicsq.GetValueAsSQ();
407   mROIContoursSequenceOfItems = roi_contour_seq;
408   assert(roi_contour_seq); // TODO error message
409   for(unsigned int ridx = 0; ridx < roi_contour_seq->GetNumberOfItems(); ++ridx) {
410     gdcm::Item & item = roi_contour_seq->GetItem( ridx + 1); // Item starts at 1
411     // ROI number [Referenced ROI Number]
412     const gdcm::DataSet& nestedds = item.GetNestedDataSet();
413     gdcm::Attribute<0x3006,0x0084> referencedroinumber;
414     referencedroinumber.SetFromDataSet( nestedds );
415     int nb = referencedroinumber.GetValue();
416     // Store the item
417     mMapOfROIContours[nb] = &item;
418   }
419
420   //----------------------------------
421   // Create the ROIs
422   for(std::map<int, gdcm::Item*>::iterator i = mMapOfROIInfo.begin(); i != mMapOfROIInfo.end(); i++) {
423     int nb = i->first;//ReadROINumber(i);//mROIIndex[i];
424     // Create the roi
425     DicomRT_ROI::Pointer roi = DicomRT_ROI::New();
426     roi->Read(mMapOfROIInfo[nb], mMapOfROIContours[nb]);
427     //    mListOfROI.push_back(roi);
428     //    mMapOfROIIndex[nb] = i;
429     mROIs[nb] = roi;
430   }
431
432   //----------------------------------------------------------------------------------------
433   //----------------------------------------------------------------------------------------
434   //----------------------------------------------------------------------------------------
435 #else
436   mFile = new gdcm::File;
437   mFile->SetFileName(filename.c_str());
438   mFile->SetMaxSizeLoadEntry(16384); // Needed ...
439   mFile->SetLoadMode(gdcm::LD_NOSHADOW); // don't load shadow tags (in order to save memory)
440   mFile->Load();
441   
442   // Check file type
443   //Verify if the file is a RT-Structure-Set dicom file
444   if (!gdcm::Util::DicomStringEqual(mFile->GetEntryValue(0x0008,0x0016),"1.2.840.10008.5.1.4.1.1.481.3")) {  //SOP clas UID
445     std::cerr << "Error. the file " << filename
446               << " is not a Dicom Struct ? (must have a SOP Class UID [0008|0016] = 1.2.840.10008.5.1.4.1.1.481.3 ==> [RT Structure Set Storage])"
447               << std::endl;
448     exit(0);
449   }
450   if (!gdcm::Util::DicomStringEqual(mFile->GetEntryValue(0x0008,0x0060),"RTSTRUCT")) {  //SOP clas UID
451     std::cerr << "Error. the file " << filename
452               << " is not a Dicom Struct ? (must have 0x0008,0x0060 = RTSTRUCT [RT Structure Set Storage])"
453               << std::endl;
454     exit(0);
455   }
456
457   // Read global info
458   mStudyID   = mFile->GetValEntry(0x0020,0x0010)->GetValue();
459   mStudyTime = mFile->GetValEntry(0x008,0x0020)->GetValue();
460   mStudyDate = mFile->GetValEntry(0x008,0x0030)->GetValue();
461   mLabel     = mFile->GetValEntry(0x3006,0x002)->GetValue();
462   if (!mFile->GetValEntry(0x3006,0x004)) {
463     mName = "Anonymous";
464   }
465   else {
466     mName = mFile->GetValEntry(0x3006,0x004)->GetValue();
467   }
468   mTime      = mFile->GetValEntry(0x3006,0x009)->GetValue();
469
470   //----------------------------------
471   // Read all ROI Names and number
472   // 0x3006,0x0020 = [ Structure Set ROI Sequence ]
473   gdcm::SeqEntry * roi_seq=mFile->GetSeqEntry(0x3006,0x0020);
474   assert(roi_seq); // TODO error message
475   for (gdcm::SQItem* r=roi_seq->GetFirstSQItem(); r!=0; r=roi_seq->GetNextSQItem()) {
476     std::string name = r->GetEntryValue(0x3006,0x0026);      // 0x3006,0x0026 = [ROI Name]
477     int nb = atoi(r->GetEntryValue(0x3006,0x0022).c_str());  // 0x3006,0x0022 = [ROI Number]
478     // Check if such a number already exist
479     if (mMapOfROIName.find(nb) != mMapOfROIName.end()) {
480       std::cerr << "WARNING. A Roi already exist with the number "
481                 << nb << ". I replace." << std::endl;
482     }
483     // Add in map
484     mMapOfROIName[nb] = name;
485   }
486
487   //----------------------------------
488   // Read all ROI
489   // 0x3006,0x0039 = [ ROI Contour Sequence ]
490   gdcm::SeqEntry * roi_contour_seq=mFile->GetSeqEntry(0x3006,0x0039);
491   assert(roi_contour_seq); // TODO error message
492   int n=0;
493   for (gdcm::SQItem* r=roi_contour_seq->GetFirstSQItem(); r!=0; r=roi_contour_seq->GetNextSQItem()) {
494     DicomRT_ROI::Pointer roi = DicomRT_ROI::New();
495     roi->Read(mMapOfROIName, r);
496     mROIs[roi->GetROINumber()] = roi;
497     n++;
498   }
499
500 #endif
501 }
502 //--------------------------------------------------------------------
503
504
505 //--------------------------------------------------------------------
506 bool clitk::DicomRT_StructureSet::IsDicomRTStruct(const std::string & filename)
507 {
508   // Open DICOM
509 #if GDCM_MAJOR_VERSION == 2
510   // Read gdcm file
511   mReader = new gdcm::Reader;
512   mReader->SetFileName(filename.c_str());
513   mReader->Read();
514   mFile = &(mReader->GetFile());
515   const gdcm::DataSet & ds = mFile->GetDataSet();
516   
517   // Check file type
518   //Verify if the file is a RT-Structure-Set dicom file
519   gdcm::MediaStorage ms;
520   ms.SetFromFile(*mFile);
521   if( ms != gdcm::MediaStorage::RTStructureSetStorage ) return false;
522
523   gdcm::Attribute<0x8,0x60> modality;
524   modality.SetFromDataSet( ds );
525   if( modality.GetValue() != "RTSTRUCT" ) return false;
526   
527   return true;
528
529   //----------------------------------------------------------------------------------------
530 #else
531   mFile = new gdcm::File;
532   mFile->SetFileName(filename.c_str());
533   mFile->SetMaxSizeLoadEntry(16384); // Needed ...
534   mFile->SetLoadMode(gdcm::LD_NOSHADOW); // don't load shadow tags (in order to save memory)
535   mFile->Load();
536   
537   // Check file type
538   //Verify if the file is a RT-Structure-Set dicom file
539   if (!gdcm::Util::DicomStringEqual(mFile->GetEntryValue(0x0008,0x0016),"1.2.840.10008.5.1.4.1.1.481.3")) 
540     return false;
541   if (!gdcm::Util::DicomStringEqual(mFile->GetEntryValue(0x0008,0x0060),"RTSTRUCT"))
542     return false;
543
544   return true;
545
546 #endif
547 }
548 //--------------------------------------------------------------------
549
550
551 //--------------------------------------------------------------------
552 int clitk::DicomRT_StructureSet::AddBinaryImageAsNewROI(vvImage * im, std::string n)
553 {
554   // Search max ROI number
555   int max = -1;
556   for(ROIConstIteratorType iter = mROIs.begin(); iter != mROIs.end(); iter++) {
557     //  for(unsigned int i=0; i<mListOfROI.size(); i++) {
558     clitk::DicomRT_ROI::Pointer roi = iter->second;
559     if (roi->GetROINumber() > max)
560       max = roi->GetROINumber();
561   }
562   ++max;
563
564   // Compute name
565   std::ostringstream oss;
566   oss << vtksys::SystemTools::GetFilenameName(vtksys::SystemTools::GetFilenameWithoutLastExtension(n));
567   mMapOfROIName[max] = oss.str();
568
569   // Set color
570   std::vector<double> color;
571   color.push_back(1);
572   color.push_back(0);
573   color.push_back(0);
574
575   // Create ROI
576   DicomRT_ROI::Pointer roi = DicomRT_ROI::New();
577   roi->SetFromBinaryImage(im, max, oss.str(), color, n);
578   mROIs[max] = roi;
579   return max;
580 }
581 //--------------------------------------------------------------------
582
583