]> Creatis software - clitk.git/blob - vv/vvLandmarks.cxx
(more) robust handling of landmarks in time-sequences
[clitk.git] / vv / vvLandmarks.cxx
1 /*=========================================================================
2   Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
3
4   Authors belong to:
5   - University of LYON              http://www.universite-lyon.fr/
6   - Léon Bérard cancer center       http://www.centreleonberard.fr
7   - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
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 copyright notices for more information.
12
13   It is distributed under dual licence
14
15   - BSD        See included LICENSE.txt file
16   - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
17   ===========================================================================**/
18 #include "vvLandmarks.h"
19
20 #include <ios>
21 #include <fstream>
22 #include <sstream>
23 #include <string>
24 #include <locale.h>
25
26 #include "vtkPolyData.h"
27 #include "vtkPoints.h"
28 #include "vtkFloatArray.h"
29 #include "vtkPointData.h"
30 #include "clitkCommon.h"
31 #include <itksys/SystemTools.hxx>
32
33 //--------------------------------------------------------------------
34 vvLandmarks::vvLandmarks(int size)
35 {
36   mLandmarks.resize(size);
37   mFilenames.resize(0);
38   mTime = 0;
39
40   for (int i = 0; i < size; i++) {
41     mLandmarks[i].resize(0);
42     vtkPoints *points = vtkPoints::New();
43     mPoints.push_back(points);
44     mIds.push_back(vtkFloatArray::New());
45     mLabels.push_back(vtkStringArray::New());
46     mLabels.back()->SetName("labels");
47   }
48   mPolyData = vtkPolyData::New();
49 }
50 //--------------------------------------------------------------------
51
52
53 //--------------------------------------------------------------------
54 vvLandmarks::~vvLandmarks()
55 {
56   for (unsigned int i = 0; i < mPoints.size(); i++) {
57     mPoints[i]->Delete();
58     mIds[i]->Delete();
59     mLabels[i]->Delete();
60   }
61   /*for(unsigned int i = 0; i < mText.size(); i++) {
62     mText[i]->Delete();
63     }*/
64   if (mPolyData)
65     mPolyData->Delete();
66 }
67 //--------------------------------------------------------------------
68
69
70 //--------------------------------------------------------------------
71 void vvLandmarks::AddLandmark(float x,float y,float z,float t,double value)
72 {
73   vvLandmark point;
74   vtkIdType idPoint;
75   point.coordinates[0] = x;
76   point.coordinates[1] = y;
77   point.coordinates[2] = z;
78   point.coordinates[3] = t;
79   point.pixel_value=value;
80   mLandmarks[mTime].push_back(point);
81
82   idPoint = mPoints[int(t)]->InsertNextPoint(x,y,z);
83   std::string str_vtkIdType;        // string which will contain the result
84   std::ostringstream convert;       // stream used for the conversion
85   convert << idPoint;                   // insert the textual representation of 'idPoint' in the characters in the stream
86   str_vtkIdType = convert.str();    // set 'str_vtkIdType' to the contents of the stream
87   mLabels[mTime]->InsertNextValue(str_vtkIdType.c_str());
88
89   std::stringstream numberVal;
90   numberVal << (mLandmarks.size()-1);
91   /*
92   vvLandmarksGlyph *number = vvLandmarksGlyph::New();
93   number->SetText(numberVal.str().c_str());
94   number->BackingOff();
95   DD(numberVal.str().c_str());
96   mText.push_back(number);
97   */
98
99   mIds[mTime]->InsertNextTuple1(0.55);
100   //mIds->InsertTuple1(mLandmarks.size(),mLandmarks.size());
101   SetTime(int(t));
102 }
103 //--------------------------------------------------------------------
104
105
106 //--------------------------------------------------------------------
107 void vvLandmarks::RemoveLastLandmark()
108 {
109   mPoints[mTime]->SetNumberOfPoints(mPoints[mTime]->GetNumberOfPoints()-1);
110   //  mText.pop_back();
111   mLandmarks[mTime].pop_back();
112   mIds[mTime]->RemoveLastTuple();
113   mLabels[mTime]->SetNumberOfValues(mLabels[mTime]->GetNumberOfValues()-1);
114   mLabels[mTime]->Modified();
115   mPolyData->Modified();
116 }
117 //--------------------------------------------------------------------
118
119
120 //--------------------------------------------------------------------
121 void vvLandmarks::RemoveLandmark(int index)
122 {
123   // erase a vtkPoint by shifiting the array .
124   // not a problem here because there are no 
125   // pologyons linking the points
126   int t = mTime;//mLandmarks[index].coordinates[3];
127   int npoints = mPoints[t]->GetNumberOfPoints();
128   for (int i = index; i < npoints - 1; i++) {
129     mPoints[t]->InsertPoint(i, mPoints[t]->GetPoint(i+1));
130         std::string str_i;                   // string which will contain the result
131         std::ostringstream convert;      // stream used for the conversion
132         convert << i;                        // insert the textual representation of 'i' in the characters in the stream
133         str_i = convert.str();           // set 'str_i' to the contents of the stream
134         mLabels[t]->SetValue(i,str_i.c_str());
135     }
136   mPoints[t]->SetNumberOfPoints(npoints-1);
137   mLabels[t]->SetNumberOfValues(npoints-1);
138   mLabels[t]->Modified();
139   mPolyData->Modified();
140
141   mLandmarks[t].erase(mLandmarks[t].begin() + index);
142   mIds[t]->RemoveLastTuple();
143 }
144 //--------------------------------------------------------------------
145
146
147 //--------------------------------------------------------------------
148 void vvLandmarks::ChangeComments(int index, std::string comments)
149 {
150   mLandmarks[mTime][index].comments = comments;
151 }
152 //--------------------------------------------------------------------
153
154
155 //--------------------------------------------------------------------
156 double vvLandmarks::GetPixelValue(int index)
157 {
158   return mLandmarks[mTime][index].pixel_value;
159 }
160 //--------------------------------------------------------------------
161
162
163 //--------------------------------------------------------------------
164 float* vvLandmarks::GetCoordinates(int index)
165 {
166   return mLandmarks[mTime][index].coordinates;
167 }
168 //--------------------------------------------------------------------
169
170
171 //--------------------------------------------------------------------
172 std::string vvLandmarks::GetComments(int index)
173 {
174   return mLandmarks[mTime][index].comments;
175 }
176 //--------------------------------------------------------------------
177
178
179 //--------------------------------------------------------------------
180 bool vvLandmarks::LoadFile(std::vector<std::string> filenames)
181 {
182   // all files in the sequence must be of the same type
183   std::string extension = itksys::SystemTools::GetFilenameExtension(filenames[0]);
184   if (extension == ".txt")
185     return LoadTxtFile(filenames);
186   else if (extension == ".pts")
187     return LoadPtsFile(filenames);
188
189   return false;
190 }
191
192 //--------------------------------------------------------------------
193 bool vvLandmarks::LoadTxtFile(std::vector<std::string> filenames)
194 {
195   mFilenames = filenames;
196   for (unsigned int i = 0; i < mPoints.size(); i++) {
197     mLandmarks[i].clear();
198     mPoints[i]->SetNumberOfPoints(0);
199   }
200
201   int err = 0;
202   for (unsigned int f = 0; f < filenames.size(); f++) {
203     std::ifstream fp(filenames[f].c_str(), std::ios::in|std::ios::binary);
204     if (!fp.is_open()) {
205       std::cerr <<"Unable to open file " << filenames[f] << std::endl;
206       err++;
207     }
208     vtkIdType idPoint;
209     char line[255];
210     bool first_line=true;
211     while (fp.getline(line,255)) {
212       //    DD(line);
213       std::string stringline = line;
214       stringline += "\n";
215       
216       if (first_line) {
217         first_line=false;
218         ///New landmark format: first line is "LANDMARKSXX", where XX is the version number
219         if (stringline.size() >= 10 && stringline.compare(0,9,"LANDMARKS")==0) {
220           std::istringstream ss(stringline.c_str()+9);
221           ss >> mFormatVersion;
222           continue; //skip first line
223         } else
224           mFormatVersion=0;
225       }
226       if (stringline.size() > 1) {
227         vvLandmark point;
228         int previousSpace = 0;
229         int space=0;
230         if (mFormatVersion>0) {
231           space = stringline.find(" ", previousSpace+1);
232           if (space < -1 || space > (int)stringline.size()) {
233             ErrorMsg(mLandmarks.size(),"index");
234             continue;
235           }
236           //int index = atoi(stringline.substr(previousSpace,space - previousSpace).c_str());
237           previousSpace = space;
238         }
239         space = stringline.find(" ", previousSpace+1);
240         if (space < -1 || space > (int)stringline.size()) {
241           ErrorMsg(mLandmarks.size(),"x position");
242           continue;
243         }
244         point.coordinates[0] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
245         //      DD(point.coordinates[0]);
246         previousSpace = space;
247         space = stringline.find(" ", previousSpace+1);
248         if (space < -1 || space > (int)stringline.size()) {
249           ErrorMsg(mLandmarks.size(),"y position");
250           continue;
251         }
252         point.coordinates[1] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
253         //      DD(point.coordinates[1]);
254         previousSpace = space;
255         space = stringline.find(" ", previousSpace+1);
256         if (space < -1 || space > (int)stringline.size()) {
257           ErrorMsg(mLandmarks.size(),"z position");
258           continue;
259         }
260         point.coordinates[2] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
261         previousSpace = space;
262         if (mFormatVersion>0) {
263           space = stringline.find(" ", previousSpace+1);
264           if (space < -1 || space > (int)stringline.size()) {
265             ErrorMsg(mLandmarks.size(),"t position");
266             continue;
267           }
268           point.coordinates[3] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
269           previousSpace = space;
270           space = stringline.find(" ", previousSpace+1);
271           if (space < -1 || space > (int)stringline.size()) {
272             ErrorMsg(mLandmarks.size(),"pixel value");
273             continue;
274           }
275           point.pixel_value = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
276           //        DD(point.pixel_value);
277         } else {
278           point.pixel_value=0.; //Not in file
279           point.coordinates[3]=(float)f;
280         }
281         previousSpace = space;
282         //this is the maximum size of comments
283         space = (stringline.find("\n", previousSpace+1) < 254 ? stringline.find("\n", previousSpace+1) : 254);
284         if (previousSpace != -1) {
285           point.comments = stringline.substr(previousSpace,space - (previousSpace)).c_str();
286         }
287         //      DD(point.comments);
288         mLandmarks[int(point.coordinates[3])].push_back(point);
289         mIds[int(point.coordinates[3])]->InsertNextTuple1(0.55);
290         idPoint = mPoints[int(point.coordinates[3])]->InsertNextPoint(
291                                                             point.coordinates[0],point.coordinates[1],point.coordinates[2]);
292         std::string str_vtkIdType;          // string which will contain the result
293         std::ostringstream convert;     // stream used for the conversion
294         convert << idPoint;                 // insert the textual representation of 'idPoint' in the characters in the stream
295         str_vtkIdType = convert.str(); // set 'str_vtkIdType' to the contents of the stream
296         mLabels[int(point.coordinates[3])]->InsertNextValue(str_vtkIdType.c_str());
297       }
298     }
299   }
300
301   if (err > 0 && err == filenames.size())
302     return false;
303   
304   SetTime(0);
305   
306   return true;
307 }
308 //--------------------------------------------------------------------
309
310 //--------------------------------------------------------------------
311 bool vvLandmarks::LoadPtsFile(std::vector<std::string> filenames)
312 {
313   mFilenames = filenames;
314   for (unsigned int i = 0; i < mPoints.size(); i++) {
315     mPoints[i]->SetNumberOfPoints(0);
316     mLandmarks[i].clear();
317   }
318
319   int err = 0;
320   for (unsigned int f = 0; f < filenames.size(); f++) {
321     std::ifstream fp(filenames[f].c_str(), std::ios::in|std::ios::binary);
322     if (!fp.is_open()) {
323       std::cerr <<"Unable to open file " << filenames[f] << std::endl;
324       err++;
325     }
326     vtkIdType idPoint;
327     char line[255];
328     bool first_line=true;
329     while (fp.getline(line,255)) {
330       std::string stringline = line;
331       stringline += "\n";
332       
333       std::string separators = "\t\n\r ";
334       if (stringline.size() > 1) {
335         vvLandmark point;
336         int previousSpace = 0;
337         int space=0;
338
339         if (stringline[0] == '#') // comments
340           continue;
341         
342         space = stringline.find_first_of(separators, previousSpace+1);
343         if (space == std::string::npos) {
344           ErrorMsg(mLandmarks.size(),"x position");
345           continue;
346         }
347         point.coordinates[0] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
348         //      DD(point.coordinates[0]);
349         previousSpace = space;
350         space = stringline.find_first_of(separators, previousSpace+1);
351         if (space == std::string::npos) {
352           ErrorMsg(mLandmarks.size(),"y position");
353           continue;
354         }
355         point.coordinates[1] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
356         //      DD(point.coordinates[1]);
357         previousSpace = space;
358         space = stringline.find_first_of(separators, previousSpace+1);
359         if (space == std::string::npos) {
360           ErrorMsg(mLandmarks.size(),"z position");
361           continue;
362         }
363         point.coordinates[2] = atof(replace_dots(stringline.substr(previousSpace,space - previousSpace)).c_str());
364         previousSpace = space;
365         point.pixel_value=0.; //Not in file
366         point.coordinates[3]=(float)f;  //Not in file
367         point.comments = "";  //Not in file
368
369         //      DD(point.comments);
370         mLandmarks[int(point.coordinates[3])].push_back(point);
371         mIds[int(point.coordinates[3])]->InsertNextTuple1(0.55);
372         idPoint = mPoints[int(point.coordinates[3])]->InsertNextPoint(
373                                                             point.coordinates[0],point.coordinates[1],point.coordinates[2]);
374         std::string str_vtkIdType;     // string which will contain the result
375         std::ostringstream convert;  // stream used for the conversion
376         convert << idPoint;        // insert the textual representation of 'idPoint' in the characters in the stream
377         str_vtkIdType = convert.str(); // set 'str_vtkIdType' to the contents of the stream
378         mLabels[int(point.coordinates[3])]->InsertNextValue(str_vtkIdType.c_str());
379       }
380     }
381   }
382   
383   SetTime(0);
384   DD("vvLandmarks::LoadPtsFile")
385   if (err > 0 && err == filenames.size())
386     return false;
387
388   
389   return true;
390 }
391 //--------------------------------------------------------------------
392
393 //--------------------------------------------------------------------
394 bool vvLandmarks::ErrorMsg(int num,const char * text)
395 {
396   std::cerr << "error when loading point " << num << " at " << text << std::endl;
397   return false;
398 }
399 //--------------------------------------------------------------------
400
401
402 //--------------------------------------------------------------------
403 void vvLandmarks::SaveFile(std::string filename)
404 {
405   std::string fileContent = "LANDMARKS1\n"; //File format version identification
406   for (unsigned int t = 0; t < mLandmarks.size(); t++) {
407     for (unsigned int i = 0; i < mLandmarks[t].size(); i++) {
408       std::stringstream out;
409       out.imbue(std::locale("C")); //This is to specify that the dot is to be used as the decimal separator
410       out << i << " "
411           << mLandmarks[t][i].coordinates[0] << " "
412           << mLandmarks[t][i].coordinates[1] << " "
413           << mLandmarks[t][i].coordinates[2] << " "
414           << mLandmarks[t][i].coordinates[3] << " "
415           << mLandmarks[t][i].pixel_value << " ";
416       fileContent += out.str();
417       if (mLandmarks[t][i].comments.size() == 0)
418         fileContent += " ";
419       else
420         fileContent += mLandmarks[t][i].comments;
421       fileContent += "\n";
422     }
423   }
424   std::ofstream fp(filename.c_str(), std::ios::trunc);
425   if ( !fp ) {
426     std::cerr << "Unable to open file" << std::endl;
427     return;
428   }
429   fp << fileContent.c_str()<< std::endl;
430   fp.close();
431 }
432 //--------------------------------------------------------------------
433
434
435 //--------------------------------------------------------------------
436 void vvLandmarks::SetTime(int time)
437 {
438   if (time >= 0 && time <= ((int)mPoints.size() -1)) {
439     mPolyData->SetPoints(mPoints[time]);
440     mPolyData->GetPointData()->SetScalars(mIds[time]);
441     mPolyData->GetPointData()->AddArray(mLabels[time]);
442     mPolyData->Modified();
443     mPolyData->Update();
444     mTime = time;
445   }
446 }
447 //--------------------------------------------------------------------
448
449
450 //--------------------------------------------------------------------
451 std::string vvLandmarks::replace_dots(std::string input)
452 {
453   ///Replaces the dots used in the file with the decimal separator in use on the platform
454   lconv * conv=localeconv();
455   unsigned int position = input.find( "." );
456   while ( position < input.size() ) {
457     input.replace(position, 1, conv->decimal_point);
458     position = input.find( ".", position + 1 );
459   }
460   return input;
461 }
462 //--------------------------------------------------------------------