1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkMINCImageReader.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 "vtkMINCImageReader.h"
50
51 #include "vtkObjectFactory.h"
52
53 #include "vtkImageData.h"
54 #include "vtkStringArray.h"
55 #include "vtkCharArray.h"
56 #include "vtkUnsignedCharArray.h"
57 #include "vtkShortArray.h"
58 #include "vtkIntArray.h"
59 #include "vtkFloatArray.h"
60 #include "vtkDoubleArray.h"
61 #include "vtkIdTypeArray.h"
62 #include "vtkMatrix4x4.h"
63 #include "vtkSmartPointer.h"
64 #include "vtkMath.h"
65 #include "vtkStreamingDemandDrivenPipeline.h"
66 #include "vtkInformation.h"
67
68 #include "vtkType.h"
69
70 #include "vtkMINCImageAttributes.h"
71 #include "vtkMINC.h"
72 #include "vtk_netcdf.h"
73
74 #include <cstdlib>
75 #include <cctype>
76 #include <cfloat>
77 #include <string>
78 #include <map>
79
80 #define VTK_MINC_MAX_DIMS 8
81
82 //--------------------------------------------------------------------------
83 vtkStandardNewMacro(vtkMINCImageReader);
84
85 //-------------------------------------------------------------------------
vtkMINCImageReader()86 vtkMINCImageReader::vtkMINCImageReader()
87 {
88 this->NumberOfTimeSteps = 1;
89 this->TimeStep = 0;
90 this->DirectionCosines = vtkMatrix4x4::New();
91 this->RescaleIntercept = 0.0;
92 this->RescaleSlope = 1.0;
93 this->RescaleRealValues = 0;
94
95 this->MINCImageType = 0;
96 this->MINCImageTypeSigned = 1;
97
98 this->ValidRange[0] = 0.0;
99 this->ValidRange[1] = 1.0;
100
101 this->ImageRange[0] = 0.0;
102 this->ImageRange[1] = 1.0;
103
104 this->DataRange[0] = 0.0;
105 this->DataRange[1] = 1.0;
106
107 this->ImageAttributes = vtkMINCImageAttributes::New();
108 this->ImageAttributes->ValidateAttributesOff();
109
110 this->FileNameHasChanged = 0;
111 }
112
113 //-------------------------------------------------------------------------
~vtkMINCImageReader()114 vtkMINCImageReader::~vtkMINCImageReader()
115 {
116 if (this->DirectionCosines)
117 {
118 this->DirectionCosines->Delete();
119 this->DirectionCosines = nullptr;
120 }
121 if (this->ImageAttributes)
122 {
123 this->ImageAttributes->Delete();
124 this->ImageAttributes = nullptr;
125 }
126 }
127
128 //-------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)129 void vtkMINCImageReader::PrintSelf(ostream& os, vtkIndent indent)
130 {
131 this->Superclass::PrintSelf(os,indent);
132
133 os << indent << "ImageAttributes: " << this->ImageAttributes << "\n";
134 if (this->ImageAttributes)
135 {
136 this->ImageAttributes->PrintSelf(os, indent.GetNextIndent());
137 }
138 os << indent << "DirectionCosines: " << this->DirectionCosines << "\n";
139 if (this->DirectionCosines)
140 {
141 this->DirectionCosines->PrintSelf(os, indent.GetNextIndent());
142 }
143 os << indent << "RescaleSlope: " << this->RescaleSlope << "\n";
144 os << indent << "RescaleIntercept: " << this->RescaleIntercept << "\n";
145 os << indent << "RescaleRealValues: "
146 << (this->RescaleRealValues ? "On" : "Off") << "\n";
147 os << indent << "DataRange: (" << this->DataRange[0]
148 << ", " << this->DataRange[1] << ")\n";
149
150 os << indent << "NumberOfTimeSteps: " << this->NumberOfTimeSteps << "\n";
151 os << indent << "TimeStep: " << this->TimeStep << "\n";
152 }
153
154 //-------------------------------------------------------------------------
SetFileName(const char * name)155 void vtkMINCImageReader::SetFileName(const char *name)
156 {
157 // Set FileNameHasChanged even if the file name hasn't changed,
158 // because it is possible that the user is re-reading a file after
159 // changing it.
160 if (!(name == nullptr && this->GetFileName() == nullptr))
161 {
162 this->FileNameHasChanged = 1;
163 }
164
165 this->Superclass::SetFileName(name);
166 }
167
168 //-------------------------------------------------------------------------
CanReadFile(const char * fname)169 int vtkMINCImageReader::CanReadFile(const char* fname)
170 {
171 // First do a very rapid check of the magic number
172 FILE *fp = fopen(fname, "rb");
173 if (!fp)
174 {
175 return 0;
176 }
177
178 char magic[4];
179 size_t count = fread(magic, 4, 1, fp);
180 fclose(fp);
181
182 if (count != 1 ||
183 magic[0] != 'C' ||
184 magic[1] != 'D' ||
185 magic[2] != 'F' ||
186 magic[3] != '\001')
187 {
188 return 0;
189 }
190
191 // Do a more thorough check of the image:version attribute, since
192 // there are lots of NetCDF files out there that aren't minc files.
193 int ncid = 0;
194 int status = nc_open(fname, 0, &ncid);
195 if (status != NC_NOERR)
196 {
197 return 0;
198 }
199
200 int ndims = 0;
201 int nvars = 0;
202 int ngatts = 0;
203 int unlimdimid = 0;
204 status = nc_inq(ncid, &ndims, &nvars, &ngatts, &unlimdimid);
205 if (status != NC_NOERR)
206 {
207 return 0;
208 }
209
210 int varid = 0;
211 char varname[NC_MAX_NAME+1];
212 nc_type vartype = NC_INT;
213 int nvardims;
214 int dimids[VTK_MINC_MAX_DIMS];
215 int nvaratts = 0;
216 for (varid = 0; varid < nvars && status == NC_NOERR; varid++)
217 {
218 status = nc_inq_var(ncid, varid, varname, &vartype, &nvardims,
219 dimids, &nvaratts);
220 if (status == NC_NOERR && strcmp(varname, MIimage) == 0)
221 {
222 nc_type atttype = NC_INT;
223 size_t attlength = 0;
224 status = nc_inq_att(ncid, varid, MIversion, &atttype, &attlength);
225 if (status == NC_NOERR && atttype == NC_CHAR && attlength < 32)
226 {
227 char verstring[32];
228 status = nc_get_att_text(ncid, varid, MIversion, verstring);
229 if (status == NC_NOERR && strncmp(verstring, "MINC ", 5) == 0)
230 {
231 nc_close(ncid);
232 return 1;
233 }
234 }
235 break;
236 }
237 }
238
239 nc_close(ncid);
240
241 return 0;
242 }
243
244 //-------------------------------------------------------------------------
GetDirectionCosines()245 vtkMatrix4x4 *vtkMINCImageReader::GetDirectionCosines()
246 {
247 this->ReadMINCFileAttributes();
248 return this->DirectionCosines;
249 }
250
251 //-------------------------------------------------------------------------
GetRescaleSlope()252 double vtkMINCImageReader::GetRescaleSlope()
253 {
254 this->ReadMINCFileAttributes();
255 this->FindRangeAndRescaleValues();
256 return this->RescaleSlope;
257 }
258
259 //-------------------------------------------------------------------------
GetRescaleIntercept()260 double vtkMINCImageReader::GetRescaleIntercept()
261 {
262 this->ReadMINCFileAttributes();
263 this->FindRangeAndRescaleValues();
264 return this->RescaleIntercept;
265 }
266
267 //-------------------------------------------------------------------------
GetDataRange()268 double *vtkMINCImageReader::GetDataRange()
269 {
270 this->ReadMINCFileAttributes();
271 this->FindRangeAndRescaleValues();
272 return this->DataRange;
273 }
274
275 //-------------------------------------------------------------------------
GetNumberOfTimeSteps()276 int vtkMINCImageReader::GetNumberOfTimeSteps()
277 {
278 this->ReadMINCFileAttributes();
279 return this->NumberOfTimeSteps;
280 }
281
282 //-------------------------------------------------------------------------
GetImageAttributes()283 vtkMINCImageAttributes *vtkMINCImageReader::GetImageAttributes()
284 {
285 this->ReadMINCFileAttributes();
286 return this->ImageAttributes;
287 }
288
289 //-------------------------------------------------------------------------
OpenNetCDFFile(const char * filename,int & ncid)290 int vtkMINCImageReader::OpenNetCDFFile(const char *filename, int& ncid)
291 {
292 int status = 0;
293
294 if (filename == nullptr)
295 {
296 vtkErrorMacro("No filename was set");
297 return 0;
298 }
299
300 status = nc_open(filename, 0, &ncid);
301 if (status != NC_NOERR)
302 {
303 vtkErrorMacro("Could not open the MINC file:\n"
304 << nc_strerror(status));
305 return 0;
306 }
307
308 return 1;
309 }
310
311 //-------------------------------------------------------------------------
CloseNetCDFFile(int ncid)312 int vtkMINCImageReader::CloseNetCDFFile(int ncid)
313 {
314 int status = 0;
315 status = nc_close(ncid);
316 if (status != NC_NOERR)
317 {
318 vtkErrorMacro("Could not close the MINC file:\n"
319 << nc_strerror(status));
320 return 0;
321 }
322
323 return 1;
324 }
325
326 //-------------------------------------------------------------------------
327 // this is a macro so the vtkErrorMacro will report a useful line number
328 #define vtkMINCImageReaderFailAndClose(ncid, status) \
329 { \
330 if ((status) != NC_NOERR) \
331 { \
332 vtkErrorMacro("There was an error with the MINC file:\n" \
333 << this->GetFileName() << "\n" \
334 << nc_strerror(status)); \
335 } \
336 nc_close(ncid); \
337 }
338
339
340 //-------------------------------------------------------------------------
341 // Function for getting VTK dimension index from the dimension name.
IndexFromDimensionName(const char * dimName)342 int vtkMINCImageReader::IndexFromDimensionName(const char *dimName)
343 {
344 switch(dimName[0])
345 {
346 case 'x':
347 return 0;
348 case 'y':
349 return 1;
350 case 'z':
351 return 2;
352 default:
353 if (strcmp(dimName, MIvector_dimension) == 0)
354 {
355 return -1;
356 }
357 break;
358 }
359
360 // Any unrecognized dimensions are returned as index 3
361 return 3;
362 }
363
364 //-------------------------------------------------------------------------
ReadMINCFileAttributes()365 int vtkMINCImageReader::ReadMINCFileAttributes()
366 {
367 // If the filename hasn't changed since the last time the attributes
368 // were read, don't read them again.
369 if (!this->FileNameHasChanged)
370 {
371 return 1;
372 }
373
374 // Reset the MINC information for the file.
375 this->MINCImageType = 0;
376 this->MINCImageTypeSigned = 1;
377
378 this->NumberOfTimeSteps = 1;
379 this->DirectionCosines->Identity();
380
381 // Orientation set tells us which direction cosines were found
382 int orientationSet[3] = {0, 0, 0};
383
384 this->ImageAttributes->Reset();
385
386 // Miscellaneous NetCDF variables
387 int status = 0;
388 int ncid = 0;
389 int dimid = 0;
390 int varid = 0;
391 int ndims = 0;
392 int nvars = 0;
393 int ngatts = 0;
394 int unlimdimid = 0;
395
396 if (this->OpenNetCDFFile(this->GetFileName(), ncid) == 0)
397 {
398 return 0;
399 }
400
401 // Get the basic information for the file. The ndims are
402 // ignored here, because we only want the dimensions that
403 // belong to the image variable.
404 status = nc_inq(ncid, &ndims, &nvars, &ngatts, &unlimdimid);
405 if (status != NC_NOERR)
406 {
407 vtkMINCImageReaderFailAndClose(ncid, status);
408 return 0;
409 }
410 if (ndims > VTK_MINC_MAX_DIMS)
411 {
412 vtkErrorMacro("MINC file has " << ndims << ", but this reader"
413 " only supports " << VTK_MINC_MAX_DIMS << ".");
414 return 0;
415 }
416
417 // Go through all the variables in the MINC file. A varid of -1
418 // is used to signal global attributes.
419 for (varid = -1; varid < nvars; varid++)
420 {
421 char varname[NC_MAX_NAME+1];
422 int dimids[VTK_MINC_MAX_DIMS];
423 nc_type vartype = NC_SHORT;
424 int nvardims = 0;
425 int nvaratts = 0;
426
427 if (varid == -1) // for global attributes
428 {
429 nvaratts = ngatts;
430 varname[0] = '\0';
431 }
432 else
433 {
434 status = nc_inq_var(ncid, varid, varname, &vartype, &nvardims,
435 dimids, &nvaratts);
436 if (status != NC_NOERR)
437 {
438 vtkMINCImageReaderFailAndClose(ncid, status);
439 return 0;
440 }
441 }
442
443 // Get all the variable attributes
444 for (int j = 0; j < nvaratts; j++)
445 {
446 char attname[NC_MAX_NAME+1];
447 nc_type atttype;
448 size_t attlength = 0;
449
450 status = nc_inq_attname(ncid, varid, j, attname);
451 if (status != NC_NOERR)
452 {
453 vtkMINCImageReaderFailAndClose(ncid, status);
454 return 0;
455 }
456 status = nc_inq_att(ncid, varid, attname, &atttype, &attlength);
457 if (status != NC_NOERR)
458 {
459 vtkMINCImageReaderFailAndClose(ncid, status);
460 return 0;
461 }
462
463 // Get the attribute values as a vtkDataArray.
464 vtkDataArray *dataArray = nullptr;
465 switch (atttype)
466 {
467 case NC_BYTE:
468 {
469 // NetCDF leaves it up to us to decide whether NC_BYTE
470 // should be signed.
471 vtkUnsignedCharArray *ucharArray = vtkUnsignedCharArray::New();
472 ucharArray->SetNumberOfValues(static_cast<vtkIdType>(attlength));
473 nc_get_att_uchar(ncid, varid, attname,
474 ucharArray->GetPointer(0));
475 dataArray = ucharArray;
476 }
477 break;
478 case NC_CHAR:
479 {
480 // The NC_CHAR type is for text.
481 vtkCharArray *charArray = vtkCharArray::New();
482 // The netcdf standard doesn't enforce null-termination
483 // of string attributes, so we add a null here.
484 charArray->Resize(static_cast<vtkIdType>(attlength + 1));
485 char *dest = charArray->WritePointer(0, static_cast<vtkIdType>(attlength));
486 nc_get_att_text(ncid, varid, attname, dest);
487 dest[attlength] = '\0';
488 dataArray = charArray;
489 }
490 break;
491 case NC_SHORT:
492 {
493 vtkShortArray *shortArray = vtkShortArray::New();
494 shortArray->SetNumberOfValues(static_cast<vtkIdType>(attlength));
495 nc_get_att_short(ncid, varid, attname,
496 shortArray->GetPointer(0));
497 dataArray = shortArray;
498 }
499 break;
500 case NC_INT:
501 {
502 vtkIntArray *intArray = vtkIntArray::New();
503 intArray->SetNumberOfValues(static_cast<vtkIdType>(attlength));
504 nc_get_att_int(ncid, varid, attname,
505 intArray->GetPointer(0));
506 dataArray = intArray;
507 }
508 break;
509 case NC_FLOAT:
510 {
511 vtkFloatArray *floatArray = vtkFloatArray::New();
512 floatArray->SetNumberOfValues(static_cast<vtkIdType>(attlength));
513 nc_get_att_float(ncid, varid, attname,
514 floatArray->GetPointer(0));
515 dataArray = floatArray;
516 }
517 break;
518 case NC_DOUBLE:
519 {
520 vtkDoubleArray *doubleArray = vtkDoubleArray::New();
521 doubleArray->SetNumberOfValues(static_cast<vtkIdType>(attlength));
522 nc_get_att_double(ncid, varid, attname,
523 doubleArray->GetPointer(0));
524 dataArray = doubleArray;
525 }
526 break;
527 default:
528 break;
529 }
530 if (dataArray)
531 {
532 this->ImageAttributes->SetAttributeValueAsArray(
533 varname, attname, dataArray);
534 dataArray->Delete();
535 }
536 }
537
538 // Special treatment of image variable.
539 if (strcmp(varname, MIimage) == 0)
540 {
541 // Set the type of the data.
542 this->MINCImageType = vartype;
543
544 // Find the sign of the data, default to "signed"
545 int signedType = 1;
546 // Except for bytes, where default is "unsigned"
547 if (vartype == NC_BYTE)
548 {
549 signedType = 0;
550 }
551 const char *signtype =
552 this->ImageAttributes->GetAttributeValueAsString(
553 MIimage, MIsigntype);
554 if (signtype)
555 {
556 if (strcmp(signtype, MI_UNSIGNED) == 0)
557 {
558 signedType = 0;
559 }
560 }
561 this->MINCImageTypeSigned = signedType;
562
563 for (int i = 0; i < nvardims; i++)
564 {
565 char dimname[NC_MAX_NAME+1];
566 size_t dimlength = 0;
567
568 dimid = dimids[i];
569
570 status = nc_inq_dim(ncid, dimid, dimname, &dimlength);
571 if (status != NC_NOERR)
572 {
573 vtkMINCImageReaderFailAndClose(ncid, status);
574 return 0;
575 }
576
577 this->ImageAttributes->AddDimension(dimname, static_cast<vtkIdType>(dimlength));
578
579 int dimIndex = this->IndexFromDimensionName(dimname);
580
581 if (dimIndex >= 0 && dimIndex < 3)
582 {
583 // Set the orientation matrix from the direction_cosines
584 vtkDoubleArray *doubleArray =
585 vtkArrayDownCast<vtkDoubleArray>(
586 this->ImageAttributes->GetAttributeValueAsArray(
587 dimname, MIdirection_cosines));
588 if (doubleArray && doubleArray->GetNumberOfTuples() == 3)
589 {
590 double *dimDirCos = doubleArray->GetPointer(0);
591 this->DirectionCosines->SetElement(0, dimIndex, dimDirCos[0]);
592 this->DirectionCosines->SetElement(1, dimIndex, dimDirCos[1]);
593 this->DirectionCosines->SetElement(2, dimIndex, dimDirCos[2]);
594 orientationSet[dimIndex] = 1;
595 }
596 }
597 else if (strcmp(dimname, MIvector_dimension) != 0)
598 {
599 // Set the NumberOfTimeSteps to the product of all dimensions
600 // that are neither spatial dimensions nor vector dimensions.
601 this->NumberOfTimeSteps *= static_cast<int>(dimlength);
602 }
603 }
604 }
605 else if (strcmp(varname, MIimagemin) == 0 ||
606 strcmp(varname, MIimagemax) == 0)
607 {
608 // Read the image-min and image-max.
609 this->ImageAttributes->SetNumberOfImageMinMaxDimensions(nvardims);
610
611 vtkDoubleArray *doubleArray = vtkDoubleArray::New();
612 if (strcmp(varname, MIimagemin) == 0)
613 {
614 this->ImageAttributes->SetImageMin(doubleArray);
615 }
616 else
617 {
618 this->ImageAttributes->SetImageMax(doubleArray);
619 }
620 doubleArray->Delete();
621
622 vtkIdType size = 1;
623 size_t start[VTK_MINC_MAX_DIMS];
624 size_t count[VTK_MINC_MAX_DIMS];
625
626 for (int i = 0; i < nvardims; i++)
627 {
628 char dimname[NC_MAX_NAME+1];
629 size_t dimlength = 0;
630
631 dimid = dimids[i];
632
633 status = nc_inq_dim(ncid, dimid, dimname, &dimlength);
634 if (status != NC_NOERR)
635 {
636 vtkMINCImageReaderFailAndClose(ncid, status);
637 return 0;
638 }
639
640 start[i] = 0;
641 count[i] = dimlength;
642
643 size *= static_cast<vtkIdType>(dimlength);
644 }
645
646 doubleArray->SetNumberOfValues(size);
647 status = nc_get_vara_double(ncid, varid, start, count,
648 doubleArray->GetPointer(0));
649 if (status != NC_NOERR)
650 {
651 vtkMINCImageReaderFailAndClose(ncid, status);
652 return 0;
653 }
654 }
655 }
656
657 // Check to see if only 2 spatial dimensions were included,
658 // since we'll have to make up the third dircos if that is the case
659 int numDirCos = 0;
660 int notSetIndex = 0;
661 for (int dcount = 0; dcount < 3; dcount++)
662 {
663 if (orientationSet[dcount])
664 {
665 numDirCos++;
666 }
667 else
668 {
669 notSetIndex = dcount;
670 }
671 }
672 // If only two were set, use cross product to get the third
673 if (numDirCos == 2)
674 {
675 int idx1 = (notSetIndex + 1) % 3;
676 int idx2 = (notSetIndex + 2) % 3;
677 double v1[4];
678 double v2[4];
679 double v3[3];
680 for (int tmpi = 0; tmpi < 4; tmpi++)
681 {
682 v1[tmpi] = v2[tmpi] = 0.0;
683 }
684 v1[idx1] = 1.0;
685 v2[idx2] = 1.0;
686 this->DirectionCosines->MultiplyPoint(v1, v1);
687 this->DirectionCosines->MultiplyPoint(v2, v2);
688 vtkMath::Cross(v1, v2, v3);
689 this->DirectionCosines->SetElement(0, notSetIndex, v3[0]);
690 this->DirectionCosines->SetElement(1, notSetIndex, v3[1]);
691 this->DirectionCosines->SetElement(2, notSetIndex, v3[2]);
692 }
693
694 // Get the data type
695 int dataType = this->ConvertMINCTypeToVTKType(this->MINCImageType,
696 this->MINCImageTypeSigned);
697 this->ImageAttributes->SetDataType(dataType);
698
699 // Get the name from the file name by removing the path and
700 // the extension.
701 const char *fileName = this->FileName;
702 char name[4096];
703 name[0] = '\0';
704 int startChar = 0;
705 int endChar = static_cast<int>(strlen(fileName));
706
707 for (startChar = endChar-1; startChar > 0; startChar--)
708 {
709 if (fileName[startChar] == '.')
710 {
711 endChar = startChar;
712 }
713 if (fileName[startChar-1] == '/'
714 #ifdef _WIN32
715 || fileName[startChar-1] == '\\'
716 #endif
717 )
718 {
719 break;
720 }
721 }
722 if (endChar - startChar > 127)
723 {
724 endChar = startChar + 128;
725 }
726 if (endChar > startChar)
727 {
728 strncpy(name, &fileName[startChar], endChar-startChar);
729 name[endChar - startChar] = '\0';
730 }
731
732 this->ImageAttributes->SetName(name);
733
734 // We're done reading the attributes, so close the file.
735 if (this->CloseNetCDFFile(ncid) == 0)
736 {
737 return 0;
738 }
739
740 // Get the ValidRange and ImageRange.
741 this->ImageAttributes->FindValidRange(this->ValidRange);
742 this->ImageAttributes->FindImageRange(this->ImageRange);
743
744 // Don't have to do this again until the file name changes.
745 this->FileNameHasChanged = 0;
746
747 return 1;
748 }
749
750 //-------------------------------------------------------------------------
ConvertMINCTypeToVTKType(int minctype,int mincsigned)751 int vtkMINCImageReader::ConvertMINCTypeToVTKType(
752 int minctype,
753 int mincsigned)
754 {
755 int dataType = 0;
756
757 // Get the vtk type of the data.
758 switch (minctype)
759 {
760 case NC_BYTE:
761 dataType = VTK_UNSIGNED_CHAR;
762 if (mincsigned)
763 {
764 dataType = VTK_SIGNED_CHAR;
765 }
766 break;
767 case NC_SHORT:
768 dataType = VTK_UNSIGNED_SHORT;
769 if (mincsigned)
770 {
771 dataType = VTK_SHORT;
772 }
773 break;
774 case NC_INT:
775 dataType = VTK_UNSIGNED_INT;
776 if (mincsigned)
777 {
778 dataType = VTK_INT;
779 }
780 break;
781 case NC_FLOAT:
782 dataType = VTK_FLOAT;
783 break;
784 case NC_DOUBLE:
785 dataType = VTK_DOUBLE;
786 break;
787 default:
788 break;
789 }
790
791 return dataType;
792 }
793
794 //-------------------------------------------------------------------------
FindRangeAndRescaleValues()795 void vtkMINCImageReader::FindRangeAndRescaleValues()
796 {
797 // Set DataRange and Rescale values according to whether
798 // RescaleRealValues is set
799 if (this->RescaleRealValues)
800 {
801 // Set DataRange to ImageRange
802 this->DataRange[0] = this->ImageRange[0];
803 this->DataRange[1] = this->ImageRange[1];
804
805 // The output data values will be the real data values.
806 this->RescaleSlope = 1.0;
807 this->RescaleIntercept = 0.0;
808 }
809 else
810 {
811 // Set DataRange to ValidRange
812 this->DataRange[0] = this->ValidRange[0];
813 this->DataRange[1] = this->ValidRange[1];
814
815 // Set rescale parameters
816 this->RescaleSlope = ((this->ImageRange[1] - this->ImageRange[0])/
817 (this->ValidRange[1] - this->ValidRange[0]));
818
819 this->RescaleIntercept = (this->ImageRange[0] -
820 this->RescaleSlope*this->ValidRange[0]);
821 }
822 }
823
824 //-------------------------------------------------------------------------
ExecuteInformation()825 void vtkMINCImageReader::ExecuteInformation()
826 {
827 // Read the MINC attributes from the file.
828 if (this->ReadMINCFileAttributes() == 0)
829 {
830 return;
831 }
832
833 // Set the VTK information from the MINC information.
834 int dataExtent[6] = {0, 0, 0, 0, 0, 0};
835
836 double dataSpacing[3] = {1.0, 1.0, 1.0};
837
838 double dataOrigin[3] = {0.0, 0.0, 0.0};
839
840 int numberOfComponents = 1;
841
842 int fileType = this->ConvertMINCTypeToVTKType(this->MINCImageType,
843 this->MINCImageTypeSigned);
844
845 if (fileType == 0)
846 {
847 vtkErrorMacro("Couldn't convert NetCDF data type " << this->MINCImageType
848 << (this->MINCImageTypeSigned ? " signed" : " unsigned")
849 << " to a VTK data type.");
850 return;
851 }
852
853 // Compute the DataRange, RescaleSlope, and RescaleIntercept
854 this->FindRangeAndRescaleValues();
855
856 // If we are rescaling the data, find the appropriate
857 // output data type. The data is only rescaled if the
858 // data has an ImageMin and ImageMax.
859 int dataType = fileType;
860 if (this->RescaleRealValues &&
861 this->ImageAttributes->GetImageMin() &&
862 this->ImageAttributes->GetImageMax())
863 {
864 switch (fileType)
865 {
866 case VTK_SIGNED_CHAR:
867 case VTK_UNSIGNED_CHAR:
868 case VTK_CHAR:
869 case VTK_SHORT:
870 case VTK_UNSIGNED_SHORT:
871 dataType = VTK_FLOAT;
872 break;
873 case VTK_INT:
874 case VTK_UNSIGNED_INT:
875 dataType = VTK_DOUBLE;
876 break;
877 default:
878 dataType = fileType;
879 break;
880 }
881 }
882
883 // Go through the image dimensions to discover data information.
884 vtkStringArray *dimensionNames =
885 this->ImageAttributes->GetDimensionNames();
886 vtkIdTypeArray *dimensionLengths =
887 this->ImageAttributes->GetDimensionLengths();
888
889 unsigned int numberOfDimensions = dimensionNames->GetNumberOfValues();
890 for (unsigned int i = 0; i < numberOfDimensions; i++)
891 {
892 const char *dimName = dimensionNames->GetValue(i);
893 vtkIdType dimLength = dimensionLengths->GetValue(i);
894
895 // Set the VTK dimension index.
896 int dimIndex = this->IndexFromDimensionName(dimName);
897
898 // Do special things with the spatial dimensions.
899 if (dimIndex >= 0 && dimIndex < 3)
900 {
901 // Set the spacing from the 'step' attribute.
902 double step = this->ImageAttributes->GetAttributeValueAsDouble(
903 dimName, MIstep);
904 if (step)
905 {
906 dataSpacing[dimIndex] = step;
907 }
908
909 // Set the origin from the 'start' attribute.
910 double start = this->ImageAttributes->GetAttributeValueAsDouble(
911 dimName, MIstart);
912 if (start)
913 {
914 dataOrigin[dimIndex] = start;
915 }
916
917 // Set the extent from the dimension length.
918 dataExtent[2*dimIndex + 1] = static_cast<int>(dimLength - 1);
919 }
920
921 // Check for vector_dimension.
922 else if (strcmp(dimName, MIvector_dimension) == 0)
923 {
924 numberOfComponents = dimLength;
925 }
926 }
927
928 this->SetDataExtent(dataExtent);
929 this->SetDataSpacing(dataSpacing[0], dataSpacing[1], dataSpacing[2]);
930 this->SetDataOrigin(dataOrigin[0], dataOrigin[1], dataOrigin[2]);
931 this->SetDataScalarType(dataType);
932 this->SetNumberOfScalarComponents(numberOfComponents);
933 }
934
935 //-------------------------------------------------------------------------
936 // Data conversion functions. The rounding is done using the same
937 // method as in the MINC libraries.
938 #define vtkMINCImageReaderConvertMacro(F, T, MIN, MAX) \
939 inline void vtkMINCImageReaderConvert(const F& inVal, T& outVal) \
940 { \
941 double val = inVal; \
942 if (val >= static_cast<double>(MIN)) \
943 { \
944 if (val <= static_cast<double>(MAX)) \
945 { \
946 outVal = static_cast<T>((val < 0) ? (val - 0.5) : (val + 0.5)); \
947 return; \
948 } \
949 outVal = static_cast<T>(MAX); \
950 return; \
951 } \
952 outVal = static_cast<T>(MIN); \
953 }
954
955 #define vtkMINCImageReaderConvertMacroFloat(F, T) \
956 inline void vtkMINCImageReaderConvert(const F &inVal, T &outVal) \
957 { \
958 outVal = static_cast<T>(inVal); \
959 }
960
961 vtkMINCImageReaderConvertMacro(double, signed char,
962 VTK_SIGNED_CHAR_MIN, VTK_SIGNED_CHAR_MAX);
963 vtkMINCImageReaderConvertMacro(double, unsigned char,
964 0, VTK_UNSIGNED_CHAR_MAX);
965 vtkMINCImageReaderConvertMacro(double, short,
966 VTK_SHORT_MIN, VTK_SHORT_MAX);
967 vtkMINCImageReaderConvertMacro(double, unsigned short,
968 0, VTK_UNSIGNED_SHORT_MAX);
969 vtkMINCImageReaderConvertMacro(double, int,
970 VTK_INT_MIN, VTK_INT_MAX);
971 vtkMINCImageReaderConvertMacro(double, unsigned int,
972 0, VTK_UNSIGNED_INT_MAX);
973 vtkMINCImageReaderConvertMacroFloat(double, float);
974 vtkMINCImageReaderConvertMacroFloat(double, double);
975
976 //-------------------------------------------------------------------------
977 // Overloaded functions for reading various data types.
978
979 // Handle most with a macro.
980 #define vtkMINCImageReaderReadChunkMacro(ncFunction, T) \
981 inline int vtkMINCImageReaderReadChunk( \
982 int ncid, int varid, size_t *start, size_t *count, T *buffer) \
983 { \
984 return ncFunction(ncid, varid, start, count, buffer); \
985 }
986
987 #define vtkMINCImageReaderReadChunkMacro2(ncFunction, T1, T2) \
988 inline int vtkMINCImageReaderReadChunk( \
989 int ncid, int varid, size_t *start, size_t *count, T1 *buffer) \
990 { \
991 return ncFunction(ncid, varid, start, count, (T2 *)buffer); \
992 }
993
994 vtkMINCImageReaderReadChunkMacro(nc_get_vara_schar, signed char);
995 vtkMINCImageReaderReadChunkMacro(nc_get_vara_uchar, unsigned char);
996 vtkMINCImageReaderReadChunkMacro(nc_get_vara_short, short);
997 vtkMINCImageReaderReadChunkMacro2(nc_get_vara_short, unsigned short, short);
998 vtkMINCImageReaderReadChunkMacro(nc_get_vara_int, int);
999 vtkMINCImageReaderReadChunkMacro2(nc_get_vara_int, unsigned int, int);
1000 vtkMINCImageReaderReadChunkMacro(nc_get_vara_float, float);
1001 vtkMINCImageReaderReadChunkMacro(nc_get_vara_double, double);
1002
1003 //-------------------------------------------------------------------------
1004 template<class T1, class T2>
vtkMINCImageReaderExecuteChunk(T1 * outPtr,T2 * buffer,double slope,double intercept,int ncid,int varid,int ndims,size_t * start,size_t * count,vtkIdType * permutedInc)1005 void vtkMINCImageReaderExecuteChunk(
1006 T1 *outPtr, T2 *buffer, double slope, double intercept,
1007 int ncid, int varid, int ndims, size_t *start, size_t *count,
1008 vtkIdType *permutedInc)
1009 {
1010 // Read the chunk of data from the MINC file.
1011 vtkMINCImageReaderReadChunk(ncid, varid, start, count, buffer);
1012
1013 // Create space to save values during the copy loop.
1014 T1 *saveOutPtr[VTK_MINC_MAX_DIMS];
1015 size_t index[VTK_MINC_MAX_DIMS];
1016 int idim = 0;
1017 for (idim = 0; idim < ndims; idim++)
1018 {
1019 index[idim] = 0;
1020 saveOutPtr[idim] = outPtr;
1021 }
1022
1023 // See if there is a range of dimensions over which the
1024 // the MINC data and VTK data will be contiguous. The
1025 // lastdim is the dimension after which all dimensions
1026 // are contiguous between the MINC file and the output.
1027 int lastdim = ndims-1;
1028 int ncontiguous = 1;
1029 vtkIdType dimprod = 1;
1030 for (idim = ndims; idim > 0; )
1031 {
1032 idim--;
1033
1034 lastdim = idim;
1035 ncontiguous = dimprod;
1036
1037 if (dimprod != permutedInc[idim])
1038 {
1039 break;
1040 }
1041
1042 dimprod *= static_cast<vtkIdType>(count[idim]);
1043 }
1044
1045 // Save the count and permuted increment of this dimension.
1046 size_t lastdimcount = count[lastdim];
1047 size_t lastdimindex = 0;
1048 vtkIdType lastdimInc = permutedInc[lastdim];
1049 T1 *lastdimOutPtr = saveOutPtr[lastdim];
1050
1051 // Loop over all contiguous sections of the image.
1052 for (;;)
1053 {
1054 // Loop through one contiguous section
1055 vtkIdType k = ncontiguous;
1056 do
1057 {
1058 // Use special function for type conversion.
1059 vtkMINCImageReaderConvert((*buffer++)*slope + intercept, *outPtr++);
1060 }
1061 while (--k);
1062
1063 lastdimindex++;
1064 lastdimOutPtr += lastdimInc;
1065 outPtr = lastdimOutPtr;
1066
1067 // Continue until done lastdim.
1068 if (lastdimindex < lastdimcount)
1069 {
1070 continue;
1071 }
1072
1073 // Handle all dimensions that are lower than lastdim. Go down
1074 // the dimensions one at a time until we find one for which
1075 // the index is still less than the count.
1076 idim = lastdim;
1077 do
1078 {
1079 // We're done if the lowest dim's index has reached its count.
1080 if (idim == 0)
1081 {
1082 return;
1083 }
1084 // Reset the index to zero if it previously reached its count.
1085 index[idim--] = 0;
1086
1087 // Now increase the index for the next lower dimension;
1088 index[idim]++;
1089 saveOutPtr[idim] += permutedInc[idim];
1090
1091 // Continue the loop if this dim's index has reached its count.
1092 }
1093 while (index[idim] >= count[idim]);
1094
1095 // Increment back up to the lastdim, resetting the pointers.
1096 outPtr = saveOutPtr[idim];
1097 do
1098 {
1099 saveOutPtr[++idim] = outPtr;
1100 }
1101 while (idim < lastdim);
1102
1103 lastdimOutPtr = outPtr;
1104 lastdimindex = 0;
1105 }
1106 }
1107
1108 //-------------------------------------------------------------------------
1109 // Our own template that only includes MINC data types.
1110
1111 #define vtkMINCImageReaderTemplateMacro(call) \
1112 case VTK_DOUBLE: { typedef double VTK_TT; call; }; break; \
1113 case VTK_FLOAT: { typedef float VTK_TT; call; }; break; \
1114 case VTK_INT: { typedef int VTK_TT; call; }; break; \
1115 case VTK_UNSIGNED_INT: { typedef unsigned int VTK_TT; call; }; break; \
1116 case VTK_SHORT: { typedef short VTK_TT; call; }; break; \
1117 case VTK_UNSIGNED_SHORT: { typedef unsigned short VTK_TT; call; }; break; \
1118 case VTK_SIGNED_CHAR: { typedef signed char VTK_TT; call; }; break; \
1119 case VTK_UNSIGNED_CHAR: { typedef unsigned char VTK_TT; call; }; break
1120
1121 //-------------------------------------------------------------------------
ExecuteDataWithInformation(vtkDataObject * output,vtkInformation * outInfo)1122 void vtkMINCImageReader::ExecuteDataWithInformation(vtkDataObject *output,
1123 vtkInformation *outInfo)
1124 {
1125 vtkImageData *data = this->AllocateOutputData(output, outInfo);
1126 int scalarType = data->GetScalarType();
1127 int scalarSize = data->GetScalarSize();
1128 int numComponents = data->GetNumberOfScalarComponents();
1129 int outExt[6];
1130 this->GetOutputInformation(0)->Get(
1131 vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), outExt);
1132 vtkIdType outInc[3];
1133 data->GetIncrements(outInc);
1134 int outSize[3];
1135 data->GetDimensions(outSize);
1136
1137 void *outPtr = data->GetScalarPointerForExtent(outExt);
1138
1139 int timeStep = this->TimeStep;
1140 if (timeStep < 0 || timeStep >= this->NumberOfTimeSteps)
1141 {
1142 vtkWarningMacro("TimeStep is set to " << this->TimeStep <<
1143 " but there are only " << this->NumberOfTimeSteps <<
1144 " time steps.");
1145 timeStep = timeStep % this->NumberOfTimeSteps;
1146 }
1147
1148 int status = 0;
1149 int ncid = 0;
1150 int varid = 0;
1151
1152 if (this->OpenNetCDFFile(this->GetFileName(), ncid) == 0)
1153 {
1154 return;
1155 }
1156
1157 // Get the image variable.
1158 status = nc_inq_varid(ncid, MIimage, &varid);
1159 if (status != NC_NOERR)
1160 {
1161 vtkMINCImageReaderFailAndClose(ncid, status);
1162 return;
1163 }
1164
1165 // Get the dimensions.
1166 vtkStringArray *dimensionNames =
1167 this->ImageAttributes->GetDimensionNames();
1168 vtkIdTypeArray *dimensionLengths =
1169 this->ImageAttributes->GetDimensionLengths();
1170 int ndims = dimensionNames->GetNumberOfValues();
1171 int idim = 0;
1172 int nminmaxdims = this->ImageAttributes->GetNumberOfImageMinMaxDimensions();
1173 vtkIdType minmaxSize = 0;
1174 if (this->ImageAttributes->GetImageMin())
1175 {
1176 minmaxSize = this->ImageAttributes->GetImageMin()->GetNumberOfTuples();
1177 }
1178
1179 // The default dimensionality of the chunks that are used.
1180 int nchunkdims = ndims - nminmaxdims;
1181
1182 // All of these values will be changed in the following loop
1183 vtkIdType nchunks = 1;
1184 vtkIdType numTimeSteps = 1;
1185 vtkIdType chunkSize = 1;
1186 int hitChunkSizeLimit = 0;
1187 int nchunkdimsIsSet = 0;
1188
1189 // These arrays will be filled in by the following loop
1190 vtkIdType permutedInc[VTK_MINC_MAX_DIMS];
1191 size_t start[VTK_MINC_MAX_DIMS];
1192 size_t count[VTK_MINC_MAX_DIMS];
1193 size_t length[VTK_MINC_MAX_DIMS];
1194
1195 // Loop over the dimensions starting with the fastest-varying.
1196 for (idim = ndims; idim > 0; )
1197 {
1198 idim--;
1199
1200 const char *dimName = dimensionNames->GetValue(idim);
1201 vtkIdType dimLength = dimensionLengths->GetValue(idim);
1202 length[idim] = dimLength;
1203
1204 // Find the VTK dimension index.
1205 int dimIndex = this->IndexFromDimensionName(dimName);
1206
1207 if (dimIndex >= 0 && dimIndex < 3)
1208 {
1209 // Set start and count according to the update extent.
1210 start[idim] = outExt[2*dimIndex];
1211 count[idim] = outExt[2*dimIndex+1] - outExt[2*dimIndex] + 1;
1212 permutedInc[idim] = outInc[dimIndex];
1213 }
1214 else if (strcmp(dimName, MIvector_dimension) == 0)
1215 {
1216 // Vector dimension size is also stored in numComponents.
1217 start[idim] = 0;
1218 count[idim] = numComponents;
1219 permutedInc[idim] = 1;
1220 }
1221 else
1222 {
1223 // Use TimeStep to compute the index into the remaining dimensions.
1224 start[idim] = (timeStep / numTimeSteps) % dimLength;
1225 count[idim] = 1;
1226 numTimeSteps *= dimLength;
1227 permutedInc[idim] = 0;
1228 }
1229
1230 // For scalar minmax, use chunk sizes of 65536 or less,
1231 // unless this would force the chunk size to be 1
1232 if (nminmaxdims == 0 && chunkSize != 1 &&
1233 chunkSize*count[idim] > 65536)
1234 {
1235 hitChunkSizeLimit = 1;
1236 }
1237
1238 // If idim is one of the image-min/image-max dimensions, or if
1239 // we have reached the maximum chunk size, then increase the
1240 // number of chunks instead of increasing the chunk size
1241 if (idim < nminmaxdims || hitChunkSizeLimit)
1242 {
1243 // Number of chunks is product of dimensions in minmax.
1244 nchunks *= static_cast<vtkIdType>(count[idim]);
1245
1246 // Only set nchunkdims once
1247 if (nchunkdimsIsSet == 0)
1248 {
1249 nchunkdims = ndims - idim - 1;
1250 nchunkdimsIsSet = 1;
1251 }
1252 }
1253 else
1254 {
1255 chunkSize *= static_cast<vtkIdType>(count[idim]);
1256 }
1257 }
1258
1259 // Create a buffer for intermediate results.
1260 int fileType = this->ImageAttributes->GetDataType();
1261 void *buffer = nullptr;
1262 switch (fileType)
1263 {
1264 vtkMINCImageReaderTemplateMacro(buffer=(void *)(new VTK_TT[chunkSize]));
1265 }
1266
1267 // Initialize the min and max to the global min max.
1268 double *minPtr = &this->ImageRange[0];
1269 double *maxPtr = &this->ImageRange[1];
1270
1271 // If min and max arrays are not empty, use them instead.
1272 if (minmaxSize > 0)
1273 {
1274 minPtr = this->ImageAttributes->GetImageMin()->GetPointer(0);
1275 maxPtr = this->ImageAttributes->GetImageMax()->GetPointer(0);
1276 }
1277
1278 // Initialize the start and count to use for each chunk.
1279 size_t start2[VTK_MINC_MAX_DIMS];
1280 size_t count2[VTK_MINC_MAX_DIMS];
1281 for (idim = 0; idim < ndims; idim++)
1282 {
1283 start2[idim] = start[idim];
1284 count2[idim] = count[idim];
1285 }
1286
1287 // Go through all the chunks
1288 for (vtkIdType ichunk = 0; ichunk < nchunks; ichunk++)
1289 {
1290 // Find the start and count to use for each chunk.
1291 vtkIdType minmaxIdx = 0;
1292 vtkIdType minmaxInc = 1;
1293 vtkIdType chunkProd = 1;
1294 vtkIdType chunkOffset = 0;
1295 for (idim = ndims - nchunkdims; idim > 0; )
1296 {
1297 idim--;
1298 start2[idim] = start[idim] + (ichunk / chunkProd) % count[idim];
1299 count2[idim] = 1;
1300 if (idim < nminmaxdims)
1301 {
1302 minmaxIdx += static_cast<vtkIdType>(start2[idim]*minmaxInc);
1303 minmaxInc *= static_cast<vtkIdType>(length[idim]);
1304 }
1305 chunkOffset += static_cast<vtkIdType>(
1306 (start2[idim] - start[idim])*permutedInc[idim]);
1307 chunkProd *= static_cast<vtkIdType>(count[idim]);
1308 }
1309
1310 // Get the min and max values to apply to this chunk
1311 double chunkRange[2];
1312 if (fileType == VTK_FLOAT || fileType == VTK_DOUBLE)
1313 {
1314 // minc files that are float or double use global scaling
1315 chunkRange[0] = this->ImageRange[0];
1316 chunkRange[1] = this->ImageRange[1];
1317 }
1318 else
1319 {
1320 // minc files of other types use slice-by-slice scaling
1321 chunkRange[0] = minPtr[minmaxIdx];
1322 chunkRange[1] = maxPtr[minmaxIdx];
1323 }
1324
1325 // Use the range to calculate a linear transformation
1326 // to apply to the data values of this chunk.
1327 double slope = ((chunkRange[1] - chunkRange[0])/
1328 ((this->ValidRange[1] - this->ValidRange[0])
1329 *this->RescaleSlope));
1330 double intercept = ((chunkRange[0] - this->RescaleIntercept)/
1331 this->RescaleSlope) - slope*this->ValidRange[0];
1332
1333 // set the output pointer to use for this chunk
1334 void *outPtr1 = (void *)(((char *)outPtr) + chunkOffset*scalarSize);
1335
1336 // Read in the chunks and permute them.
1337 if (scalarType == fileType)
1338 {
1339 switch (scalarType)
1340 {
1341 vtkMINCImageReaderTemplateMacro(
1342 vtkMINCImageReaderExecuteChunk(
1343 (VTK_TT *)outPtr1, (VTK_TT *)buffer, slope, intercept,
1344 ncid, varid, ndims, start2, count2, permutedInc));
1345 }
1346 }
1347 else if (scalarType == VTK_FLOAT)
1348 {
1349 switch (fileType)
1350 {
1351 vtkMINCImageReaderTemplateMacro(
1352 vtkMINCImageReaderExecuteChunk(
1353 (float *)outPtr1, (VTK_TT *)buffer, slope, intercept,
1354 ncid, varid, ndims, start2, count2, permutedInc));
1355 }
1356 }
1357 else if (scalarType == VTK_DOUBLE)
1358 {
1359 switch (fileType)
1360 {
1361 vtkMINCImageReaderTemplateMacro(
1362 vtkMINCImageReaderExecuteChunk(
1363 (double *)outPtr1, (VTK_TT *)buffer, slope, intercept,
1364 ncid, varid, ndims, start2, count2, permutedInc));
1365 }
1366 }
1367 }
1368
1369 switch (fileType)
1370 {
1371 vtkMINCImageReaderTemplateMacro(delete [] ((VTK_TT *)buffer));
1372 }
1373
1374 this->CloseNetCDFFile(ncid);
1375 }
1376