2 # ---------------------------------------------------------------------
4 # Copyright (c) CREATIS (Centre de Recherche en Acquisition et Traitement de l'Image
6 # Authors : Eduardo Davila, Frederic Cervenansky, Claire Mouton
7 # Previous Authors : Laurent Guigues, Jean-Pierre Roux
8 # CreaTools website : www.creatis.insa-lyon.fr/site/fr/creatools_accueil
10 # This software is governed by the CeCILL-B license under French law and
11 # abiding by the rules of distribution of free software. You can use,
12 # modify and/ or redistribute the software under the terms of the CeCILL-B
13 # license as circulated by CEA, CNRS and INRIA at the following URL
14 # http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
15 # or in the file LICENSE.txt.
17 # As a counterpart to the access to the source code and rights to copy,
18 # modify and redistribute granted by the license, users are provided only
19 # with a limited warranty and the software's author, the holder of the
20 # economic rights, and the successive licensors have only limited
23 # The fact that you are presently reading this means that you have had
24 # knowledge of the CeCILL-B license and that you accept its terms.
25 # ------------------------------------------------------------------------
29 #include "creaImageIOWxDescriptorPanel.h"
30 #include <creaImageIOSystem.h>
32 #include <gdcmGlobal.h>
33 #include <gdcmDictSet.h>
36 #if defined(USE_GDCM2)
37 #include <gdcmGlobal.h>
38 #include <gdcmDicts.h>
41 #include <boost/algorithm/string.hpp>
47 WxDescriptorPanel::WxDescriptorPanel(wxWindow *parent, const std::string path)
48 : wxDialog(parent, -1,_T("Descriptor Creation"), wxDefaultPosition, wxSize(550,550)) , m_path(path)
52 GimmickDebugMessage(1,"WxDescriptorPanel::WxDescriptorPanel"
56 ownatt["FullFileName"] = "Full_File_Name";
57 ownatt["FullFileDirectory"] = "Full_File_Directory";
61 wxButton *NewDescriptor = new wxButton(this, -1,_T("Create a new descriptor"), wxPoint(10,7) );
62 Connect( NewDescriptor->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnNew );
64 wxButton *LoadDescriptor = new wxButton(this, -1,_T("Load a descriptor"), wxPoint(150,7) );
65 Connect( LoadDescriptor->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnLoad );
67 /// \TODO fix warning: unused variable line1
68 wxStaticLine *line1 = new wxStaticLine(this, -1, wxPoint(5,40), wxSize(540,2));
71 /// \TODO fix warning: unused variable LevelText
72 wxStaticText * LevelText=new wxStaticText(this,-1,_T(" Level: "), wxPoint(5,50));
73 LevelCtrl=new wxTextCtrl(this, ID_GR_CTRL,_T("patient"), wxPoint(50,50), wxSize(50,25));
74 wxButton *addLevel = new wxButton(this, ID_LEVEL_ADD,_T("add a level"), wxPoint(150,50) );
75 Connect( addLevel->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnAddLevel );
77 /// \TODO fix warning: unused variable line2
78 wxStaticLine *line2 = new wxStaticLine(this, -1, wxPoint(5,75), wxSize(540,2));
82 /// \TODO fix warning: unused variable GR
83 wxStaticText * GR=new wxStaticText(this,-1,_T(" DICOM Group: "), wxPoint(5,110));
84 GRCtrl=new wxTextCtrl(this, ID_GR_CTRL,_T("0x0010"), wxPoint(82,110), wxSize(50,25));
85 Connect( GRCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED , (wxObjectEventFunction) &WxDescriptorPanel::OnDicomAttribute );
87 /// \TODO fix warning: unused variable EL
88 wxStaticText * EL=new wxStaticText(this,-1,_T(" DICOM Element: "), wxPoint(140,110));
89 ELCtrl=new wxTextCtrl(this, ID_EL_CTRL,_T("0x0010"), wxPoint(230,110), wxSize(50,25));
90 Connect( ELCtrl->GetId(), wxEVT_COMMAND_TEXT_UPDATED , (wxObjectEventFunction) &WxDescriptorPanel::OnDicomAttribute );
93 choices[0] = _T("Unknow Attribute");
94 std::map<std::string, std::string>::iterator it_att =ownatt.begin();
95 for(int i = 1; it_att != ownatt.end(); it_att++, i++)
97 choices[i] = crea::std2wx(it_att->second);
101 AttributeCombo = new wxComboBox(this, ID_ATTRIBUTE_CTRL,_T(""),wxPoint(300,110), wxSize(120,25),3,choices, wxCB_READONLY);
102 AttributeCombo->SetSelection(0);
105 wxButton *addAttribute = new wxButton(this, ID_ATTRIBUTE_ADD,_T("add an attribute"), wxPoint(440,110) );
106 Connect( addAttribute->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnAddAttribute );
107 /// \TODO fix warning: unused variable line3
108 wxStaticLine *line3 = new wxStaticLine(this, -1, wxPoint(5,140), wxSize(540,2));
112 //EED 2017-09-16 Migration wxWidgets 2.8 to 3.0
113 #if wxMAJOR_VERSION <= 2
114 ResultCtrl=new wxTextCtrl(this, ID_EL_CTRL,_T(""), wxPoint(5,150), wxSize(250,310), wxTE_READONLY| wxMac | wxTE_MULTILINE | wxTE_RICH );
116 ResultCtrl=new wxTextCtrl(this, ID_EL_CTRL,_T(""), wxPoint(5,150), wxSize(250,310), wxTE_READONLY| wxTE_MULTILINE | wxTE_RICH );
120 wxButton *RemoveCtrl = new wxButton(this, ID_REMOVE_ADD,_T("Remove an entry"), wxPoint(280,200) );
121 Connect( RemoveCtrl->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnRemove );
123 /// \TODO fix warning: unused variable line4
124 wxStaticLine *line4 = new wxStaticLine(this, -1, wxPoint(5,470), wxSize(540,2));
126 wxButton *Ok = new wxButton(this, -1,_T("OK"), wxPoint(10,480) );
127 Connect( Ok->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnOK );
129 wxButton *Apply = new wxButton(this, -1,_T("APPLY"), wxPoint(150,480) );
130 Connect( Apply->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxObjectEventFunction) &WxDescriptorPanel::OnApply );
132 /// \TODO fix warning: unused variable Cancel
133 wxButton *Cancel = new wxButton(this, wxID_CANCEL,_T("CANCEL"), wxPoint(250,480) );
134 // Connect( Cancel->GetId(), wxEVT_COMMAND_BUTTON_CLICKED , (wxCloseEvent) &wxWindow::Close );
141 WxDescriptorPanel::~WxDescriptorPanel()
143 GimmickDebugMessage(1,"WxCustomizeConfigPanel::~WxCustomizeConfigPanel"
147 //////////////////////////////////////////////////////////
148 // Add an attribute //
149 // @param event : Wxevent //
151 //////////////////////////////////////////////////
152 void WxDescriptorPanel::OnAddAttribute(wxCommandEvent& event)
155 std::string name_att;
156 if (AttributeCombo->GetSelection() == 0)
158 name_att = "D" + crea::wx2std(GRCtrl->GetValue()) + "_" + crea::wx2std(ELCtrl->GetValue());
162 wxString wd = AttributeCombo->GetValue();
163 std::string st = crea::wx2std(wd);
164 name_att = OwnAttribute(st);
166 onAddAttribute(crea::wx2std(AttributeCombo->GetValue()), name_att);
168 //////////////////////////////////////////////////////////
169 // add an attribute //
170 // @param att : attribute //
171 // @param name_att : 's name //
172 // @param level : level to add the attribute //
174 //////////////////////////////////////////////////
175 void WxDescriptorPanel::onAddAttribute( const std::string &att, const std::string &name_att,std::string level )
179 wxMessageBox(_T("Need a level first!"),crea::std2wx("WARNING"),wxOK,this);
185 // Find Name of level
191 if (!addAtribute(level, name_att))
193 wxMessageBox(_T("Attribute already used in this level"),crea::std2wx("WARNING"),wxOK,this);
197 ResultCtrl->SetInsertionPoint(InsertPt);
198 for (int i = 1; i<=lv;i++)
200 ResultCtrl->WriteText(_T(" "));
202 ResultCtrl->WriteText(_T("| - "));
203 ResultCtrl->WriteText(crea::std2wx(att));
204 wxTextAttr ResultAttr(ResultCtrl->GetDefaultStyle());
205 ResultAttr.SetTextColour(*wxWHITE);
206 ResultCtrl->SetDefaultStyle(ResultAttr);
207 std::string text = " ";
208 ResultCtrl->WriteText(crea::std2wx(" " + name_att));
209 ResultAttr.SetTextColour(*wxBLACK);
210 ResultCtrl->SetDefaultStyle(ResultAttr);
211 ResultCtrl->WriteText(_T("\n"));
213 InsertPt = ResultCtrl->GetInsertionPoint();
218 //////////////////////////////////////////////////////////
220 // @param event : Wxevent //
222 //////////////////////////////////////////////////
223 void WxDescriptorPanel::OnAddLevel(wxCommandEvent& event)
225 if( !LevelCtrl->GetValue().IsEmpty() )
227 onAddLevel(crea::wx2std(LevelCtrl->GetValue()));
231 //////////////////////////////////////////////////////////
233 // @param level : level's name //
235 //////////////////////////////////////////////////
236 void WxDescriptorPanel::onAddLevel(const std::string &level)
240 wxMessageBox(_T("Level already used"),crea::std2wx(("WARNING")),wxOK,this);
245 ResultCtrl->SetInsertionPoint(InsertPt);
246 for (int i = 1; i<lv;i++)
248 ResultCtrl->WriteText(_T(" "));
251 { ResultCtrl->WriteText(_T("| \n"));
252 for (int i = 1; i<lv;i++)
254 ResultCtrl->WriteText(_T(" "));
256 ResultCtrl->WriteText(_T("|_"));
259 wxTextAttr ResultAttr(ResultCtrl->GetDefaultStyle());
260 ResultAttr.SetTextColour(*wxRED);
261 ResultCtrl->SetDefaultStyle(ResultAttr);
262 ResultCtrl->WriteText(crea::std2wx(level));
263 ResultAttr.SetTextColour(*wxBLACK);
264 ResultCtrl->SetDefaultStyle(ResultAttr);
265 ResultCtrl->WriteText(_T("\n"));
266 InsertPt = ResultCtrl->GetInsertionPoint();
270 //////////////////////////////////////////////////////////
271 // Find a DICOM attribute from group and element values //
272 // @param event : Wxevent //
274 //////////////////////////////////////////////////
275 void WxDescriptorPanel::OnDicomAttribute(wxCommandEvent& event)
278 if(!GRCtrl->GetValue().IsEmpty() && !ELCtrl->GetValue().IsEmpty()
279 && GRCtrl->GetValue().Len() == 6 && ELCtrl->GetValue().Len() == 6 && AttributeCombo->GetSelection() == 0)
282 std::string gr = crea::wx2std(GRCtrl->GetValue());
283 std::string el = crea::wx2std(ELCtrl->GetValue());
284 std::stringstream val;
286 unsigned short group;
287 unsigned short element;
288 val << std::dec << gr ;
289 val >> std::hex >> group;
291 val << std::dec << el ;
292 val >> std::hex >> element;
293 #if defined(USE_GDCM)
294 // Retrieve the name from gdcm dict
295 GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(group, element);
296 // AttributeCombo->Clear();
299 AttributeCombo->Delete(0);
300 AttributeCombo->Insert(crea::std2wx(entry->GetName()), 0);
304 AttributeCombo->Delete(0);
305 AttributeCombo->Insert(_T("Unknown Attribute"),0);
308 AttributeCombo->SetSelection(0);
315 //////////////////////////////////////////////////////////
316 // determine values for own attributes //
317 // @param name : attribute's name //
318 // @param key : indicates key map or not //
320 //////////////////////////////////////////////////
321 std::string WxDescriptorPanel::OwnAttribute(const std::string name)
325 std::map<std::string, std::string>::iterator it_att = ownatt.begin();
326 for(; it_att != ownatt.end(); it_att++)
328 if(it_att->second == name)
330 result = it_att->first.c_str();
337 //////////////////////////////////////////////////////////
338 // Find a level in function of position in Return Ctrl //
341 //////////////////////////////////////////////////
342 std::string WxDescriptorPanel::findLevel()
347 ResultCtrl->PositionToXY( ResultCtrl->GetInsertionPoint(),&column, &line);
348 std::string tx(crea::wx2std(ResultCtrl->GetRange(0, ResultCtrl->XYToPosition(0,line+1))).c_str());
349 std::string::size_type level_pos_start = tx.rfind("|_");
350 if(level_pos_start == -1)
356 level_pos_start += 2;
359 std::string::size_type level_pos_end = tx.find_first_of("\n",level_pos_start);
360 return tx.substr(level_pos_start,level_pos_end - level_pos_start);
363 //////////////////////////////////////////////////////
365 // @param event : Wxevent //
367 //////////////////////////////////////////////////
368 void WxDescriptorPanel::OnRemove(wxCommandEvent& event)
375 pos_start = ResultCtrl->GetInsertionPoint();
376 ResultCtrl->PositionToXY( pos_start,&column, &line);
379 std::string name("root");
386 wxString text = ResultCtrl->GetLineText(line);
387 if ( text.Find(_T("|_")) == -1)
389 std::string level = findLevel();
390 // find GR and EL values to remove
391 std::string tx = crea::wx2std(text);
392 std::string::size_type EL_start_pos = tx.find_last_of(" ");
393 RemoveAttribute(level, tx.substr(EL_start_pos+1,tx.size() - EL_start_pos));
394 ResultCtrl->Remove( ResultCtrl->XYToPosition(0,line), ResultCtrl->XYToPosition(0,line+1));
398 RemoveLevel(crea::wx2std(text.AfterFirst('_')));
399 lv = text.Find(_T("|"))/3;
400 pos_start= ResultCtrl->XYToPosition(0,line-1);
401 ResultCtrl->SetInsertionPointEnd();
402 pos_end = ResultCtrl->GetInsertionPoint();
403 ResultCtrl->Remove(pos_start, pos_end);
408 //////////////////////////////////////////////
409 // create a descriptor structure //
410 // @param name : level's name to add //
411 // @return : boolean result //
412 //////////////////////////////////////////////////
413 void WxDescriptorPanel::CreateDescriptor(int type)
415 if(type == 0) // First initialization
418 outDscp += "<level>";
422 outDscp += "O Name Name 4";
429 outDscp += "O NumberOfChildren ";
430 outDscp += crea::wx2std(LevelCtrl->GetValue());
434 outDscp += "<level>";
436 outDscp += crea::wx2std(LevelCtrl->GetValue());
444 outDscp += crea::wx2std(GRCtrl->GetValue());
446 outDscp += crea::wx2std(ELCtrl->GetValue());
456 //////////////////////////////////////////////////////
458 // @param name : level's name to add //
459 // @return : boolean result //
460 //////////////////////////////////////////////////
461 bool WxDescriptorPanel::addLevel(const std::string &name)
464 std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
465 for (;it_tree != DscpTree.end(); it_tree++)
467 if(it_tree->first == name)
476 std::vector <std::string> branch;
477 DscpTree[name] = branch;
482 //////////////////////////////////////////////////////
484 // @param name : level's name to remove //
485 // @return : boolean result //
486 //////////////////////////////////////////////////
487 bool WxDescriptorPanel::RemoveLevel(const std::string &name)
489 bool bresult = false;
490 std::map<int, std::string>::iterator it_list= lvlist.begin();
491 for(; it_list != lvlist.end(); it_list++)
493 if(it_list->second == name)
498 std::map<int, std::string>::iterator it_list2 = it_list;
499 for(;it_list != lvlist.end(); it_list++)
501 std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
502 for (;it_tree != DscpTree.end(); it_tree++)
504 if(it_tree->first == name)
506 DscpTree.erase(it_tree);
511 lvlist.erase(it_list2, lvlist.end());
516 //////////////////////////////////////////////////////
517 // add an attribute in a level //
518 // @param level : level's name to add attribute //
519 // @param name : attribute's name //
520 // @return : boolean result //
521 //////////////////////////////////////////////////
522 bool WxDescriptorPanel::addAtribute(const std::string &level, const std::string &name)
525 std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
526 for (;it_tree != DscpTree.end(); it_tree++)
528 if (it_tree->first.c_str() == level)
530 std::vector<std::string>::iterator it_branch = it_tree->second.begin();
531 for(;it_branch != it_tree->second.end(); it_branch++)
533 if(it_branch->c_str() == name)
540 it_tree->second.push_back(name);
548 //////////////////////////////////////////////////////
549 // remove an attribute from a level //
550 // @param level : level's name to remove attribute //
551 // @param name : attribute's name //
552 // @return : boolean result //
553 //////////////////////////////////////////////////
554 bool WxDescriptorPanel::RemoveAttribute(const std::string &level, const std::string &name)
556 bool bresult = false;
557 std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
558 for (;it_tree != DscpTree.end(); it_tree++)
560 if(it_tree->first == level)
562 std::vector<std::string>::iterator it_branch = it_tree->second.begin();
563 cout << it_tree->second.size();
564 for(;it_branch != it_tree->second.end(); it_branch++)
566 if(it_branch->c_str() == name)
569 it_tree->second.erase(it_branch);
578 //////////////////////////////////////////////////
579 // create a new descriptor //
580 // @param event : WxEvent //
582 //////////////////////////////////////////////////
583 void WxDescriptorPanel::OnNew(wxCommandEvent &Event)
585 LevelCtrl->SetValue(_T("patient"));
591 //////////////////////////////////////////////////
592 // Load a descriptor file //
593 // @param event : WxEvent //
595 //////////////////////////////////////////////////
596 void WxDescriptorPanel::OnLoad(wxCommandEvent &Event)
598 //EED 2017-09-16 Migration wxWidgets 2.8 to 3.0
599 #if wxMAJOR_VERSION <= 2
600 long style = wxOPEN | wxFILE_MUST_EXIST;
602 long style = wxFD_OPEN | wxFD_FILE_MUST_EXIST;
604 LevelCtrl->SetValue(_T("patient"));
609 std::string wc("*.dscp");
610 wxFileDialog* FD = new wxFileDialog( 0,
612 crea::std2wx(m_path),
617 if (FD->ShowModal()==wxID_OK)
619 loadDescriptor(crea::wx2std(FD->GetPath()).c_str());
624 //////////////////////////////////////////////////
625 // Save a descriptor //
626 // @param event : WxEvent //
628 //////////////////////////////////////////////////
629 void WxDescriptorPanel::OnOK(wxCommandEvent &Event)
635 /////////////////////////////////////////////////////
636 // Save a descriptor and apply it (create a new DB//
637 // @param event : WxEvent //
639 /////////////////////////////////////////////////////
640 void WxDescriptorPanel::OnApply(wxCommandEvent &Event)
642 m_DscpFile = saveDescriptor();
644 SetReturnCode(ID_DSCP_APPLY);
647 const std::string WxDescriptorPanel::saveDescriptor()
649 std::string file = "";
650 //EED 2017-09-16 Migration wxWidgets 2.8 to 3.0
651 #if wxMAJOR_VERSION <= 2
654 long style = wxFD_SAVE;
656 std::string wc("*.dscp");
657 wxFileDialog* FD = new wxFileDialog( 0,
666 if (FD->ShowModal()==wxID_OK)
668 createDescriptorFile();
669 file = crea::wx2std(FD->GetPath()).c_str();
670 std::ofstream ofs(file.c_str());
678 ///////////////////////////////////////////////////////
680 // @param event : WxEvent //
682 ///////////////////////////////////////////////////////
684 void WxDescriptorPanel::OnCancel(wxCommandEvent& event)
688 ///////////////////////////////////////////////////////
689 // create a descriptor file //
692 ///////////////////////////////////////////////////////
693 void WxDescriptorPanel::createDescriptorFile()
697 outDscp += "<level>";
701 outDscp += "O Name Name 4";
703 std::map<std::string, std::vector <std::string> >::iterator it_tree = DscpTree.begin();
704 std::map<int, std::string >::iterator it_lv_nb = lvlist.begin();
705 std::map<int, std::string >::iterator it_lv = lvlist.begin();
707 for (;it_lv != lvlist.end(); it_lv++)
711 outDscp += it_lv->second.c_str();
713 if(it_lv_nb != lvlist.end())
715 outDscp += "O NumberOfChildren ";
716 outDscp += it_lv_nb->second.c_str();
721 std::vector<std::string>::iterator it_branch = DscpTree[it_lv->second.c_str()].begin();
722 for(;it_branch != DscpTree[it_lv->second.c_str()].end(); it_branch++)
724 std::string att = it_branch->c_str();
725 if(att[0] == 'D' && att[7] == '_' && att.size() == 14)
728 outDscp += att.substr(1,6) + " "; // GR
729 outDscp += att.substr(8,6) + " ";// EL
736 outDscp += it_branch->c_str();
738 outDscp += ownatt[att];
749 ///////////////////////////////////////////////////////
750 // load a descriptor //
751 // @param i_name : file name to load //
753 /////////////////////////////////////////////////////
754 void WxDescriptorPanel::loadDescriptor(const std::string i_name)
756 std::ifstream i_file(i_name.c_str());
757 std::stringstream buffer;
758 buffer << i_file.rdbuf();
762 #if defined(USE_GDCM2)
763 const gdcm::Global& g = gdcm::Global::GetInstance(); // sum of all knowledge !
764 const gdcm::Dicts &dicts = g.GetDicts();
765 const gdcm::Dict &dict = dicts.GetPublicDict(); // Part 6
773 while(std::getline(buffer, line))
776 { //increment levels.
782 // For each level, a name to describe it
792 // split line to find all tags
793 std::vector<std::string> descriptors;
794 std::string separator = " ";
795 std::string::size_type last_pos = line.find_first_not_of(separator);
796 //find first separator
797 std::string::size_type pos = line.find_first_of(separator, last_pos);
798 while(std::string::npos != pos || std::string::npos != last_pos)
800 descriptors.push_back(line.substr(last_pos, pos - last_pos));
801 last_pos = line.find_first_not_of(separator, pos);
802 pos = line.find_first_of(separator, last_pos);
805 // By default, the last tag is at zero and not recorded but if take in count
806 unsigned int flag = 0;
807 if(descriptors.size() == 4)
809 std::stringstream val;
810 val << std::dec << descriptors[3];
814 // if Dicom tag, use "group" and "element" descriptor
815 if(descriptors[0] == "D")
816 { std::stringstream val, val2;
817 unsigned short group;
818 unsigned short element;
819 val << std::dec << descriptors[1] ;
820 val >> std::hex >> group;
821 val2 << std::dec << descriptors[2];
822 val2 >> std::hex >> element;
823 std::string compose = "D";
824 compose += descriptors[1];
826 compose += descriptors[2];
827 #if defined(USE_GDCM)
828 GDCM_NAME_SPACE::DictEntry* entry = GDCM_NAME_SPACE::Global::GetDicts()->GetDefaultPubDict()->GetEntry(group, element);
831 onAddAttribute( entry->GetName(),compose, level);
835 #if defined(USE_GDCM2)
836 gdcm::DictEntry dictentry = dict.GetDictEntry(gdcm::Tag(group, element));
839 onAddAttribute( dictentry.GetName(),compose, level);
845 else if(descriptors[0].find("#") != -1)
847 // commented line continue to next line
849 else // "O" means if user's own tag.
851 boost::algorithm::replace_all(descriptors[2],"_"," ");
852 if(ilevel>0 && descriptors[1] != "NumberOfChildren" )
854 onAddAttribute( descriptors[2].c_str(),descriptors[1].c_str(), level);
861 //======================================================================
863 //======================================================================
865 } // EO namespace creaImageIO