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