1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkMNITagPointReader.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
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 above copyright notice for more information.
13
14 =========================================================================*/
15 /*=========================================================================
16
17 Copyright (c) 2006 Atamai, Inc.
18
19 Use, modification and redistribution of the software, in source or
20 binary forms, are permitted provided that the following terms and
21 conditions are met:
22
23 1) Redistribution of the source code, in verbatim or modified
24 form, must retain the above copyright notice, this license,
25 the following disclaimer, and any notices that refer to this
26 license and/or the following disclaimer.
27
28 2) Redistribution in binary form must include the above copyright
29 notice, a copy of this license and the following disclaimer
30 in the documentation or with other materials provided with the
31 distribution.
32
33 3) Modified copies of the source code must be clearly marked as such,
34 and must not be misrepresented as verbatim copies of the source code.
35
36 THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS"
37 WITHOUT EXPRESSED OR IMPLIED WARRANTY INCLUDING, BUT NOT LIMITED TO,
38 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
39 PURPOSE. IN NO EVENT SHALL ANY COPYRIGHT HOLDER OR OTHER PARTY WHO MAY
40 MODIFY AND/OR REDISTRIBUTE THE SOFTWARE UNDER THE TERMS OF THIS LICENSE
41 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES
42 (INCLUDING, BUT NOT LIMITED TO, LOSS OF DATA OR DATA BECOMING INACCURATE
43 OR LOSS OF PROFIT OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF
44 THE USE OR INABILITY TO USE THE SOFTWARE, EVEN IF ADVISED OF THE
45 POSSIBILITY OF SUCH DAMAGES.
46
47 =========================================================================*/
48
49 #include "vtkMNITagPointReader.h"
50
51 #include "vtkObjectFactory.h"
52
53 #include "vtkInformation.h"
54 #include "vtkInformationVector.h"
55 #include "vtkStreamingDemandDrivenPipeline.h"
56
57 #include "vtkCellArray.h"
58 #include "vtkDoubleArray.h"
59 #include "vtkIntArray.h"
60 #include "vtkPointData.h"
61 #include "vtkPoints.h"
62 #include "vtkPolyData.h"
63 #include "vtkStringArray.h"
64
65 #include <cctype>
66
67 #include <string>
68 #include <vector>
69 #include <vtksys/FStream.hxx>
70 #include <vtksys/SystemTools.hxx>
71
72 //------------------------------------------------------------------------------
73 vtkStandardNewMacro(vtkMNITagPointReader);
74
75 //------------------------------------------------------------------------------
vtkMNITagPointReader()76 vtkMNITagPointReader::vtkMNITagPointReader()
77 {
78 this->FileName = nullptr;
79 this->NumberOfVolumes = 1;
80 this->LineNumber = 0;
81 this->Comments = nullptr;
82
83 this->SetNumberOfInputPorts(0);
84 this->SetNumberOfOutputPorts(2);
85 }
86
87 //------------------------------------------------------------------------------
~vtkMNITagPointReader()88 vtkMNITagPointReader::~vtkMNITagPointReader()
89 {
90 delete[] this->FileName;
91 delete[] this->Comments;
92 }
93
94 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)95 void vtkMNITagPointReader::PrintSelf(ostream& os, vtkIndent indent)
96 {
97 this->Superclass::PrintSelf(os, indent);
98
99 os << indent << "FileName: " << (this->FileName ? this->FileName : "none") << "\n";
100 os << indent << "NumberOfVolumes: " << this->NumberOfVolumes << "\n";
101 os << indent << "Comments: " << (this->Comments ? this->Comments : "none") << "\n";
102 }
103
104 //------------------------------------------------------------------------------
CanReadFile(const char * fname)105 int vtkMNITagPointReader::CanReadFile(const char* fname)
106 {
107 // First make sure the file exists. This prevents an empty file
108 // from being created on older compilers.
109 vtksys::SystemTools::Stat_t fs;
110 if (vtksys::SystemTools::Stat(fname, &fs) != 0)
111 {
112 return 0;
113 }
114
115 // Try to read the first line of the file.
116 int status = 0;
117
118 vtksys::ifstream infile(fname);
119
120 if (infile.good())
121 {
122 status = 1;
123 char linetext[256];
124 infile.getline(linetext, 256);
125 if (strncmp(linetext, "MNI Tag Point File", 18) != 0)
126 {
127 status = 0;
128 }
129
130 infile.close();
131 }
132
133 return status;
134 }
135
136 //------------------------------------------------------------------------------
137 // Internal function to read in a line up to 256 characters and then
138 // skip to the next line in the file.
ReadLine(istream & infile,std::string & linetext,std::string::iterator & pos)139 int vtkMNITagPointReader::ReadLine(
140 istream& infile, std::string& linetext, std::string::iterator& pos)
141 {
142 this->LineNumber++;
143
144 std::getline(infile, linetext);
145 pos = linetext.begin();
146
147 if (infile.fail())
148 {
149 if (infile.eof())
150 {
151 return 0;
152 }
153 }
154
155 return 1;
156 }
157
158 //------------------------------------------------------------------------------
159 // Skip all blank lines or comment lines and return the first useful line
ReadLineAfterComments(istream & infile,std::string & linetext,std::string::iterator & pos)160 int vtkMNITagPointReader::ReadLineAfterComments(
161 istream& infile, std::string& linetext, std::string::iterator& pos)
162 {
163 // Skip over any comment lines or blank lines.
164 // Comment lines start with '%'
165 std::string comments;
166 do
167 {
168 this->ReadLine(infile, linetext, pos);
169 while (pos != linetext.end() && isspace(*pos))
170 {
171 ++pos;
172 }
173 if (linetext.length() != 0 && linetext[0] == '%')
174 {
175 if (comments.length() > 0)
176 {
177 comments.push_back('\n');
178 }
179 if (linetext.length())
180 {
181 comments.append(linetext);
182 }
183 }
184 else if (linetext.length() != 0 && pos != linetext.end())
185 {
186 delete[] this->Comments;
187 this->Comments = new char[comments.length() + 1];
188 strncpy(this->Comments, comments.c_str(), comments.length());
189 this->Comments[comments.length()] = '\0';
190
191 return 1;
192 }
193 } while (infile.good());
194
195 return 0;
196 }
197
198 //------------------------------------------------------------------------------
199 // Skip all whitespace, reading additional lines if necessary if nl != 0
SkipWhitespace(istream & infile,std::string & linetext,std::string::iterator & pos,int nl)200 int vtkMNITagPointReader::SkipWhitespace(
201 istream& infile, std::string& linetext, std::string::iterator& pos, int nl)
202 {
203 while (infile.good())
204 {
205 // Skip leading whitespace
206 while (pos != linetext.end() && isspace(*pos))
207 {
208 ++pos;
209 }
210
211 if (pos != linetext.end())
212 {
213 return 1;
214 }
215
216 if (nl == 0)
217 {
218 break;
219 }
220
221 this->ReadLine(infile, linetext, pos);
222 }
223
224 return 0;
225 }
226
227 //------------------------------------------------------------------------------
228 // Read the left hand side of a statement, including the equals sign
229 // and any whitespace following the equals.
ParseLeftHandSide(istream & infile,std::string & linetext,std::string::iterator & pos,std::string & identifier)230 int vtkMNITagPointReader::ParseLeftHandSide(
231 istream& infile, std::string& linetext, std::string::iterator& pos, std::string& identifier)
232 {
233 identifier.clear();
234
235 // Read alphanumeric plus underscore
236 if (pos != linetext.end() && !isdigit(*pos))
237 {
238 while (pos != linetext.end() && (isalnum(*pos) || *pos == '_'))
239 {
240 identifier.push_back(*pos);
241 ++pos;
242 }
243 }
244
245 // Check for equals
246 this->SkipWhitespace(infile, linetext, pos, 1);
247 if (pos == linetext.end() || *pos != '=')
248 {
249 return 0;
250 }
251
252 // Eat the equals
253 ++pos;
254
255 // Skip ahead to the value part of the statement
256 this->SkipWhitespace(infile, linetext, pos, 1);
257
258 return 1;
259 }
260
261 //------------------------------------------------------------------------------
262 // Read a string value. The terminating semicolon will be read, but
263 // won't be included in the output string. Neither will any
264 // whitespace occurring before the semicolon. The string may not be
265 // split across multiple lines.
ParseStringValue(istream & infile,std::string & linetext,std::string::iterator & pos,std::string & data)266 int vtkMNITagPointReader::ParseStringValue(
267 istream& infile, std::string& linetext, std::string::iterator& pos, std::string& data)
268 {
269 this->SkipWhitespace(infile, linetext, pos, 0);
270
271 if (pos != linetext.end() && *pos == '\"')
272 {
273 // eat the opening quote
274 ++pos;
275
276 // read the string
277 while (pos != linetext.end() && *pos != '\"')
278 {
279 char c = *pos;
280 ++pos;
281 if (c == '\\')
282 {
283 if (pos != linetext.end())
284 {
285 c = '\0';
286 static char ctrltable[] = { '\a', 'a', '\b', 'b', '\f', 'f', '\n', 'n', '\r', 'r', '\t',
287 't', '\v', 'v', '\\', '\\', '\"', '\"', '\0', '\0' };
288
289 if (*pos >= 0 && *pos <= 9)
290 {
291 for (int j = 0; j < 3 && pos != linetext.end() && *pos >= 0 && *pos <= 9; ++j, ++pos)
292 {
293 c = ((c << 3) | (*pos - '0'));
294 }
295 }
296 else if (*pos == 'x')
297 {
298 ++pos;
299 for (int j = 0; j < 2 && pos != linetext.end() && isalnum(*pos); ++j, ++pos)
300 {
301 char x = tolower(*pos);
302 if (x >= '0' && x <= '9')
303 {
304 c = ((c << 4) | (x - '0'));
305 }
306 else if (x >= 'a' && x <= 'f')
307 {
308 c = ((c << 4) | (x - 'a' + 10));
309 }
310 }
311 }
312 else
313 {
314 for (int ci = 0; ctrltable[ci] != '\0'; ci += 2)
315 {
316 if (*pos == ctrltable[ci + 1])
317 {
318 c = ctrltable[ci];
319 break;
320 }
321 }
322 if (c == '\0')
323 {
324 c = *pos;
325 }
326 ++pos;
327 }
328 }
329 }
330
331 data.push_back(c);
332 }
333 }
334
335 if (pos == linetext.end())
336 {
337 vtkErrorMacro("Syntax error " << this->FileName << ":" << this->LineNumber);
338 return 0;
339 }
340
341 // eat the trailing quote
342 ++pos;
343
344 return 1;
345 }
346
347 //------------------------------------------------------------------------------
348 // Read an int value
ParseIntValues(istream & infile,std::string & linetext,std::string::iterator & pos,int * values,int n)349 int vtkMNITagPointReader::ParseIntValues(
350 istream& infile, std::string& linetext, std::string::iterator& pos, int* values, int n)
351 {
352 this->SkipWhitespace(infile, linetext, pos, 0);
353
354 int i = 0;
355 while (pos != linetext.end() && *pos != ';' && i < n)
356 {
357 const char* cp = linetext.c_str() + (pos - linetext.begin());
358 char* ep = nullptr;
359 long val = strtol(cp, &ep, 10);
360 if (ep == cp)
361 {
362 vtkErrorMacro("Syntax error " << this->FileName << ":" << this->LineNumber);
363 return 0;
364 }
365 pos += (ep - cp);
366 values[i++] = static_cast<int>(val);
367 this->SkipWhitespace(infile, linetext, pos, 0);
368 }
369
370 if (i != n)
371 {
372 vtkErrorMacro("Not enough values: " << this->FileName << ":" << this->LineNumber);
373 return 0;
374 }
375
376 return 1;
377 }
378
379 //------------------------------------------------------------------------------
380 // Read floating-point values into a point triplet.
ParseFloatValues(istream & infile,std::string & linetext,std::string::iterator & pos,double * values,int n)381 int vtkMNITagPointReader::ParseFloatValues(
382 istream& infile, std::string& linetext, std::string::iterator& pos, double* values, int n)
383 {
384 this->SkipWhitespace(infile, linetext, pos, 0);
385
386 int i = 0;
387 while (pos != linetext.end() && *pos != ';' && i < n)
388 {
389 const char* cp = linetext.c_str() + (pos - linetext.begin());
390 char* ep = nullptr;
391 double val = strtod(cp, &ep);
392 if (ep == cp)
393 {
394 vtkErrorMacro("Syntax error " << this->FileName << ":" << this->LineNumber);
395 return 0;
396 }
397 pos += (ep - cp);
398 values[i++] = val;
399 this->SkipWhitespace(infile, linetext, pos, 0);
400 }
401
402 if (i != n)
403 {
404 vtkErrorMacro("Not enough values: " << this->FileName << ":" << this->LineNumber);
405 return 0;
406 }
407
408 return 1;
409 }
410
411 //------------------------------------------------------------------------------
ReadFile(vtkPolyData * output1,vtkPolyData * output2)412 int vtkMNITagPointReader::ReadFile(vtkPolyData* output1, vtkPolyData* output2)
413 {
414 // Check that the file name has been set.
415 if (!this->FileName)
416 {
417 vtkErrorMacro("ReadFile: No file name has been set");
418 return 0;
419 }
420
421 // Make sure that the file exists.
422 vtksys::SystemTools::Stat_t fs;
423 if (vtksys::SystemTools::Stat(this->FileName, &fs) != 0)
424 {
425 vtkErrorMacro("ReadFile: Can't open file " << this->FileName);
426 return 0;
427 }
428
429 // Make sure that the file is readable.
430 vtksys::ifstream infile(this->FileName);
431 std::string linetext;
432 std::string::iterator pos = linetext.begin();
433
434 if (infile.fail())
435 {
436 vtkErrorMacro("ReadFile: Can't read the file " << this->FileName);
437 return 0;
438 }
439
440 // Read the first line
441 this->LineNumber = 0;
442 this->ReadLine(infile, linetext, pos);
443 if (strncmp(linetext.c_str(), "MNI Tag Point File", 18) != 0)
444 {
445 vtkErrorMacro("ReadFile: File is not a MNI tag file: " << this->FileName);
446 infile.close();
447 return 0;
448 }
449
450 // Read the number of volumes
451 this->ReadLine(infile, linetext, pos);
452 this->SkipWhitespace(infile, linetext, pos, 1);
453 int numVolumes = 1;
454 std::string identifier;
455 if (!this->ParseLeftHandSide(infile, linetext, pos, identifier) ||
456 strcmp(identifier.c_str(), "Volumes") != 0 ||
457 !this->ParseIntValues(infile, linetext, pos, &numVolumes, 1) ||
458 (numVolumes != 1 && numVolumes != 2) || !this->SkipWhitespace(infile, linetext, pos, 0) ||
459 *pos != ';')
460 {
461 vtkErrorMacro("ReadFile: Line must be Volumes = 1; or Volumes = 2; " << this->FileName << ":"
462 << this->LineNumber);
463 infile.close();
464 return 0;
465 }
466
467 this->NumberOfVolumes = numVolumes;
468
469 // Read the comments
470 this->ReadLineAfterComments(infile, linetext, pos);
471
472 // Rad the tag points
473 if (!this->ParseLeftHandSide(infile, linetext, pos, identifier) ||
474 strcmp(identifier.c_str(), "Points") != 0)
475 {
476 vtkErrorMacro("ReadFile: Cannot find Points in file; " << this->FileName);
477 infile.close();
478 linetext.clear();
479 return 0;
480 }
481
482 vtkPoints* points[2];
483 points[0] = vtkPoints::New();
484 points[1] = vtkPoints::New();
485 vtkCellArray* verts = vtkCellArray::New();
486 vtkStringArray* labels = vtkStringArray::New();
487 vtkDoubleArray* weights = vtkDoubleArray::New();
488 vtkIntArray* structureIds = vtkIntArray::New();
489 vtkIntArray* patientIds = vtkIntArray::New();
490
491 int errorOccurred = 0;
492 this->SkipWhitespace(infile, linetext, pos, 1);
493 for (vtkIdType count = 0; infile.good() && *pos != ';'; count++)
494 {
495 for (int i = 0; i < numVolumes; i++)
496 {
497 double point[3];
498 if (!this->ParseFloatValues(infile, linetext, pos, point, 3))
499 {
500 errorOccurred = 1;
501 break;
502 }
503 points[i]->InsertNextPoint(point);
504 verts->InsertNextCell(1);
505 verts->InsertCellPoint(count);
506 }
507 if (errorOccurred)
508 {
509 break;
510 }
511
512 this->SkipWhitespace(infile, linetext, pos, 0);
513 if (pos != linetext.end() && *pos != '\"' && *pos != ';')
514 {
515 double weight;
516 int structureId;
517 int patientId;
518 if (!this->ParseFloatValues(infile, linetext, pos, &weight, 1) ||
519 !this->ParseIntValues(infile, linetext, pos, &structureId, 1) ||
520 !this->ParseIntValues(infile, linetext, pos, &patientId, 1))
521 {
522 errorOccurred = 1;
523 break;
524 }
525 vtkIdType lastCount = weights->GetNumberOfTuples();
526 weights->InsertValue(count, weight);
527 structureIds->InsertValue(count, structureId);
528 patientIds->InsertValue(count, patientId);
529 for (vtkIdType j = lastCount; j < count; j++)
530 {
531 weights->SetValue(j, 0.0);
532 structureIds->SetValue(j, -1);
533 patientIds->SetValue(j, -1);
534 }
535 }
536
537 this->SkipWhitespace(infile, linetext, pos, 0);
538 if (pos != linetext.end() && *pos == '\"')
539 {
540 vtkStdString stringval;
541 if (!this->ParseStringValue(infile, linetext, pos, stringval))
542 {
543 errorOccurred = 1;
544 break;
545 }
546 labels->InsertValue(count, stringval);
547 }
548
549 this->SkipWhitespace(infile, linetext, pos, 1);
550 }
551
552 // Close the file
553 infile.close();
554
555 if (!errorOccurred)
556 {
557 output1->SetPoints(points[0]);
558 output2->SetPoints(points[1]);
559
560 vtkPolyData* output[2];
561 output[0] = output1;
562 output[1] = output2;
563
564 weights->SetName("Weights");
565 structureIds->SetName("StructureIds");
566 patientIds->SetName("PatientIds");
567 labels->SetName("LabelText");
568
569 for (int k = 0; k < this->NumberOfVolumes; k++)
570 {
571 output[k]->SetVerts(verts);
572
573 if (weights->GetNumberOfTuples())
574 {
575 output[k]->GetPointData()->AddArray(weights);
576 }
577 if (structureIds->GetNumberOfTuples())
578 {
579 output[k]->GetPointData()->AddArray(structureIds);
580 }
581 if (patientIds->GetNumberOfTuples())
582 {
583 output[k]->GetPointData()->AddArray(patientIds);
584 }
585 if (labels->GetNumberOfValues())
586 {
587 output[k]->GetPointData()->AddArray(labels);
588 }
589 }
590 }
591
592 points[0]->Delete();
593 points[1]->Delete();
594
595 verts->Delete();
596 weights->Delete();
597 structureIds->Delete();
598 patientIds->Delete();
599 labels->Delete();
600
601 return 1;
602 }
603
604 //------------------------------------------------------------------------------
GetNumberOfVolumes()605 int vtkMNITagPointReader::GetNumberOfVolumes()
606 {
607 this->Update();
608
609 return this->NumberOfVolumes;
610 }
611
612 //------------------------------------------------------------------------------
GetPoints(int port)613 vtkPoints* vtkMNITagPointReader::GetPoints(int port)
614 {
615 this->Update();
616
617 if (port < 0 || port >= this->NumberOfVolumes)
618 {
619 return nullptr;
620 }
621
622 vtkPolyData* output = static_cast<vtkPolyData*>(this->GetOutputDataObject(port));
623
624 if (output)
625 {
626 return output->GetPoints();
627 }
628
629 return nullptr;
630 }
631
632 //------------------------------------------------------------------------------
GetLabelText()633 vtkStringArray* vtkMNITagPointReader::GetLabelText()
634 {
635 this->Update();
636
637 vtkPolyData* output = static_cast<vtkPolyData*>(this->GetOutputDataObject(0));
638
639 if (output)
640 {
641 return vtkArrayDownCast<vtkStringArray>(output->GetPointData()->GetAbstractArray("LabelText"));
642 }
643
644 return nullptr;
645 }
646
647 //------------------------------------------------------------------------------
GetWeights()648 vtkDoubleArray* vtkMNITagPointReader::GetWeights()
649 {
650 this->Update();
651
652 vtkPolyData* output = static_cast<vtkPolyData*>(this->GetOutputDataObject(0));
653
654 if (output)
655 {
656 return vtkArrayDownCast<vtkDoubleArray>(output->GetPointData()->GetArray("Weights"));
657 }
658
659 return nullptr;
660 }
661
662 //------------------------------------------------------------------------------
GetStructureIds()663 vtkIntArray* vtkMNITagPointReader::GetStructureIds()
664 {
665 this->Update();
666
667 vtkPolyData* output = static_cast<vtkPolyData*>(this->GetOutputDataObject(0));
668
669 if (output)
670 {
671 return vtkArrayDownCast<vtkIntArray>(output->GetPointData()->GetArray("StructureIds"));
672 }
673
674 return nullptr;
675 }
676
677 //------------------------------------------------------------------------------
GetPatientIds()678 vtkIntArray* vtkMNITagPointReader::GetPatientIds()
679 {
680 this->Update();
681
682 vtkPolyData* output = static_cast<vtkPolyData*>(this->GetOutputDataObject(0));
683
684 if (output)
685 {
686 return vtkArrayDownCast<vtkIntArray>(output->GetPointData()->GetArray("PatientIds"));
687 }
688
689 return nullptr;
690 }
691
692 //------------------------------------------------------------------------------
GetComments()693 const char* vtkMNITagPointReader::GetComments()
694 {
695 this->Update();
696
697 return this->Comments;
698 }
699
700 //------------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (inputVector),vtkInformationVector * outputVector)701 int vtkMNITagPointReader::RequestData(vtkInformation* vtkNotUsed(request),
702 vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
703 {
704 // get the info object
705 vtkInformation* outInfo1 = outputVector->GetInformationObject(0);
706 vtkInformation* outInfo2 = outputVector->GetInformationObject(1);
707
708 // get the output
709 vtkPolyData* output1 = vtkPolyData::SafeDownCast(outInfo1->Get(vtkDataObject::DATA_OBJECT()));
710 vtkPolyData* output2 = vtkPolyData::SafeDownCast(outInfo2->Get(vtkDataObject::DATA_OBJECT()));
711
712 // all of the data in the first piece.
713 if (outInfo1->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) > 0 ||
714 outInfo2->Get(vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER()) > 0)
715 {
716 return 0;
717 }
718
719 // read the file
720 return this->ReadFile(output1, output2);
721 }
722