Orfeo Toolbox  3.16
itkNrrdImageIO.cxx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkNrrdImageIO.cxx,v $
5  Language: C++
6  Date: $Date: 2007-05-16 19:44:55 $
7  Version: $Revision: 1.38 $
8 
9  Copyright (c) Insight Software Consortium. All rights reserved.
10  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.
11 
12  This software is distributed WITHOUT ANY WARRANTY; without even
13  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
14  PURPOSE. See the above copyright notices for more information.
15 
16 =========================================================================*/
17 #ifdef _MSC_VER
18 #pragma warning ( disable : 4786 )
19 #endif
20 
21 #include <string>
22 #include "itkNrrdImageIO.h"
23 #include "itkMacro.h"
24 #include "itkMetaDataObject.h"
25 #include "itkIOCommon.h"
26 
27 #if defined(__BORLANDC__)
28 # include <math.h>
29 # include <float.h> // for _control87()
30 #endif // defined(__BORLANDC__)
31 
32 namespace itk {
33 
34 #define KEY_PREFIX "NRRD_"
35 
36 bool NrrdImageIO::SupportsDimension(unsigned long dim)
37 {
38  if (1 == this->GetNumberOfComponents())
39  {
40  return dim <= NRRD_DIM_MAX;
41  }
42  else
43  {
44  return dim <= NRRD_DIM_MAX - 1;
45  }
46 }
47 
48 void NrrdImageIO::PrintSelf(std::ostream& os, Indent indent) const
49 {
50  Superclass::PrintSelf(os, indent);
51 }
52 
55 NrrdToITKComponentType( const int nrrdComponentType ) const
56 {
57 #if defined(__BORLANDC__)
58 // Disable floating point exceptions in Borland
59  _control87(MCW_EM, MCW_EM);
60 #endif // defined(__BORLANDC__)
61  switch( nrrdComponentType )
62  {
63  case nrrdTypeUnknown:
64  case nrrdTypeBlock:
65  return UNKNOWNCOMPONENTTYPE;
66  break;
67  case nrrdTypeChar:
68  return CHAR;
69  break;
70  case nrrdTypeUChar:
71  return UCHAR;
72  break;
73  case nrrdTypeShort:
74  return SHORT;
75  break;
76  case nrrdTypeUShort:
77  return USHORT;
78  break;
79  // "long" is a silly type because it basically guaranteed not to be
80  // cross-platform across 32-vs-64 bit machines, but we'll use it
81  // where possible.
82  case nrrdTypeLLong:
83  return airMy32Bit ? UNKNOWNCOMPONENTTYPE : LONG;
84  break;
85  case nrrdTypeULLong:
86  return airMy32Bit ? UNKNOWNCOMPONENTTYPE : ULONG;
87  break;
88  case nrrdTypeInt:
89  return INT;
90  break;
91  case nrrdTypeUInt:
92  return UINT;
93  break;
94  case nrrdTypeFloat:
95  return FLOAT;
96  break;
97  case nrrdTypeDouble:
98  return DOUBLE;
99  break;
100  default:
101  return UNKNOWNCOMPONENTTYPE;
102  break;
103  }
104 
105  // Strictly to avoid compiler warning regarding "control may reach end of
106  // non-void function":
107  //
108  return UNKNOWNCOMPONENTTYPE;
109 }
110 
111 int
114 {
115 #if defined(__BORLANDC__)
116 // Disable floating point exceptions in Borland
117  _control87(MCW_EM, MCW_EM);
118 #endif // defined(__BORLANDC__)
119  switch( itkComponentType )
120  {
122  return nrrdTypeUnknown;
123  break;
124  case CHAR:
125  return nrrdTypeChar;
126  break;
127  case UCHAR:
128  return nrrdTypeUChar;
129  break;
130  case SHORT:
131  return nrrdTypeShort;
132  break;
133  case USHORT:
134  return nrrdTypeUShort;
135  break;
136  // "long" is a silly type because it basically guaranteed not to be
137  // cross-platform across 32-vs-64 bit machines, but we can figure out
138  // a cross-platform way of storing the information.
139  case LONG:
140  return airMy32Bit ? nrrdTypeInt : nrrdTypeLLong;
141  break;
142  case ULONG:
143  return airMy32Bit ? nrrdTypeUInt : nrrdTypeULLong;
144  break;
145  case INT:
146  return nrrdTypeInt;
147  break;
148  case UINT:
149  return nrrdTypeUInt;
150  break;
151  case FLOAT:
152  return nrrdTypeFloat;
153  break;
154  case DOUBLE:
155  return nrrdTypeDouble;
156  break;
157  default:
158  return nrrdTypeUnknown;
159  break;
160  }
161 
162  // Strictly to avoid compiler warning regarding "control may reach end of
163  // non-void function":
164  //
165  return nrrdTypeUnknown;
166 }
167 
168 bool NrrdImageIO::CanReadFile( const char* filename )
169 {
170 #if defined(__BORLANDC__)
171 // Disable floating point exceptions in Borland
172  _control87(MCW_EM, MCW_EM);
173 #endif // defined(__BORLANDC__)
174  // Check the extension first to avoid opening files that do not
175  // look like nrrds. The file must have an appropriate extension to be
176  // recognized.
177  std::string fname = filename;
178  if( fname == "" )
179  {
180  itkDebugMacro(<<"No filename specified.");
181  return false;
182  }
183 
184  bool extensionFound = false;
185  std::string::size_type nrrdPos = fname.rfind(".nrrd");
186  if ((nrrdPos != std::string::npos)
187  && (nrrdPos == fname.length() - 5))
188  {
189  extensionFound = true;
190  }
191 
192  std::string::size_type nhdrPos = fname.rfind(".nhdr");
193  if ((nhdrPos != std::string::npos)
194  && (nhdrPos == fname.length() - 5))
195  {
196  extensionFound = true;
197  }
198 
199  if( !extensionFound )
200  {
201  itkDebugMacro(<<"The filename extension is not recognized");
202  return false;
203  }
204 
205  // We have the correct extension, so now check for the Nrrd magic "NRRD",
206  // while ignoring the format version (the next four characters)
207  std::ifstream inputStream;
208 
209  inputStream.open( filename, std::ios::in | std::ios::binary );
210 
211  if( inputStream.fail() )
212  {
213  return false;
214  }
215 
216  char magic[5] = {'\0','\0','\0','\0','\0'};
217  inputStream.read(magic,4*sizeof(char));
218 
219  if( inputStream.eof() )
220  {
221  inputStream.close();
222  return false;
223  }
224 
225  if( strcmp(magic,"NRRD") == 0 )
226  {
227  inputStream.close();
228  return true;
229  }
230 
231  inputStream.close();
232  return false;
233 }
234 
236 {
237 #if defined(__BORLANDC__)
238 // Disable floating point exceptions in Borland
239  _control87(MCW_EM, MCW_EM);
240 #endif // defined(__BORLANDC__)
241  // This method determines the following and sets the appropriate value in
242  // the parent IO class:
243  //
244  // binary/ascii file type
245  // endianness
246  // pixel type
247  // pixel component type
248  // number of pixel components
249  // number of image dimensions
250  // image spacing
251  // image origin
252  // meta data dictionary information
253 
254  Nrrd *nrrd = nrrdNew();
255  NrrdIoState *nio = nrrdIoStateNew();
256 
257  // this is the mechanism by which we tell nrrdLoad to read
258  // just the header, and none of the data
259  nrrdIoStateSet(nio, nrrdIoStateSkipData, 1);
260  if (nrrdLoad(nrrd, this->GetFileName(), nio) != 0)
261  {
262  char *err = biffGetDone(NRRD); // would be nice to free(err)
263  itkExceptionMacro("ReadImageInformation: Error reading "
264  << this->GetFileName() << ":\n" << err);
265  }
266 
267  if (nrrdTypeBlock == nrrd->type)
268  {
269  itkExceptionMacro("ReadImageInformation: Cannot currently "
270  "handle nrrdTypeBlock");
271  }
272  if ( nio->endian == airEndianLittle )
273  {
275  }
276  else if (nio->endian == airEndianBig )
277  {
278  this->SetByteOrderToBigEndian();
279  }
280  else
281  {
283  }
284 
285  if ( nio->encoding == nrrdEncodingAscii )
286  {
287  this->SetFileTypeToASCII();
288  }
289  else
290  {
291  this->SetFileTypeToBinary();
292  }
293  // set type of pixel components; this is orthogonal to pixel type
294 
296  cmpType = this->NrrdToITKComponentType(nrrd->type);
297  if (UNKNOWNCOMPONENTTYPE == cmpType)
298  {
299  itkExceptionMacro("Nrrd type " << airEnumStr(nrrdType, nrrd->type)
300  << " could not be mapped to an ITK component type");
301  }
302  this->SetComponentType( cmpType );
303 
304  // Set the number of image dimensions and bail if needed
305  unsigned int domainAxisNum, domainAxisIdx[NRRD_DIM_MAX],
306  rangeAxisNum, rangeAxisIdx[NRRD_DIM_MAX];
307  domainAxisNum = nrrdDomainAxesGet(nrrd, domainAxisIdx);
308  rangeAxisNum = nrrdRangeAxesGet(nrrd, rangeAxisIdx);
309  if (nrrd->spaceDim && nrrd->spaceDim != domainAxisNum)
310  {
311  itkExceptionMacro("ReadImageInformation: nrrd's # independent axes ("
312  << domainAxisNum << ") doesn't match dimension of space"
313  " in which orientation is defined ("
314  << nrrd->spaceDim << "); not currently handled");
315  }
316  // else nrrd->spaceDim == domainAxisNum when nrrd has orientation
317 
318  if (0 == rangeAxisNum)
319  {
320  // we don't have any non-scalar data
321  this->SetNumberOfDimensions(nrrd->dim);
323  this->SetNumberOfComponents(1);
324  }
325  else if (1 == rangeAxisNum)
326  {
327  this->SetNumberOfDimensions(nrrd->dim - 1);
328  unsigned int kind = nrrd->axis[rangeAxisIdx[0]].kind;
329  unsigned int size = nrrd->axis[rangeAxisIdx[0]].size;
330  // NOTE: it is the NRRD readers responsibility to make sure that
331  // the size (# of components) associated with a specific kind is
332  // matches the actual size of the axis.
333  switch(kind)
334  {
335  case nrrdKindDomain:
336  case nrrdKindSpace:
337  case nrrdKindTime:
338  itkExceptionMacro("ReadImageInformation: range axis kind ("
339  << airEnumStr(nrrdKind, kind) << ") seems more "
340  "like a domain axis than a range axis");
341  break;
342  case nrrdKindStub:
343  case nrrdKindScalar:
345  this->SetNumberOfComponents(size);
346  break;
347  case nrrdKind3Color:
348  case nrrdKindRGBColor:
350  this->SetNumberOfComponents(size);
351  break;
352  case nrrdKind4Color:
353  case nrrdKindRGBAColor:
355  this->SetNumberOfComponents(size);
356  break;
357  case nrrdKindVector:
358  case nrrdKind2Vector:
359  case nrrdKind3Vector:
360  case nrrdKind4Vector:
361  case nrrdKindList:
363  this->SetNumberOfComponents(size);
364  break;
365  case nrrdKindPoint:
367  this->SetNumberOfComponents(size);
368  break;
369  case nrrdKindCovariantVector:
370  case nrrdKind3Gradient:
371  case nrrdKindNormal:
372  case nrrdKind3Normal:
374  this->SetNumberOfComponents(size);
375  break;
376  case nrrdKind3DSymMatrix:
377  // ImageIOBase::DIFFUSIONTENSOR3D is a subclass
379  this->SetNumberOfComponents(size);
380  break;
381  case nrrdKind3DMaskedSymMatrix:
383  // NOTE: we will crop out the mask in Read() below; this is the
384  // one case where NumberOfComponents != size
385  this->SetNumberOfComponents(size-1);
386  break;
387  case nrrdKindComplex:
389  this->SetNumberOfComponents(size);
390  break;
391  case nrrdKindHSVColor:
392  case nrrdKindXYZColor:
393  case nrrdKindQuaternion:
394  case nrrdKind2DSymMatrix:
395  case nrrdKind2DMaskedSymMatrix:
396  case nrrdKind2DMatrix:
397  case nrrdKind2DMaskedMatrix:
398  case nrrdKind3DMatrix:
399  // for all other Nrrd kinds, we punt and call it a vector
401  this->SetNumberOfComponents(size);
402  break;
403  default:
404  itkExceptionMacro("ReadImageInformation: nrrdKind " << kind
405  << " not known!");
406  break;
407  }
408  }
409  else
410  {
411  itkExceptionMacro("ReadImageInformation: nrrd has "
412  << rangeAxisNum
413  << " dependent axis (not 1); not currently handled");
414  }
415 
416  double spacing;
417  double spaceDir[NRRD_SPACE_DIM_MAX];
418  std::vector<double> spaceDirStd(domainAxisNum);
419  int spacingStatus;
420 
421  int iFlipFactors[3]; // used to flip the measurement frame later on
422  for (unsigned int iI=0; iI<3; iI++ )
423  {
424  iFlipFactors[iI] = 1;
425  }
426 
427  for (unsigned int axii=0; axii < domainAxisNum; axii++)
428  {
429  unsigned int naxi = domainAxisIdx[axii];
430  this->SetDimensions(axii, nrrd->axis[naxi].size);
431  spacingStatus = nrrdSpacingCalculate(nrrd, naxi, &spacing, spaceDir);
432 
433  switch(spacingStatus)
434  {
435  case nrrdSpacingStatusNone:
436  // Let ITK's defaults stay
437  // this->SetSpacing(axii, 1.0);
438  break;
439  case nrrdSpacingStatusScalarNoSpace:
440  this->SetSpacing(axii, spacing);
441  break;
442  case nrrdSpacingStatusDirection:
443  if (AIR_EXISTS(spacing))
444  {
445  // only set info if we have something to set
446  switch (nrrd->space)
447  {
448  // on read, convert non-LPS coords into LPS coords, when we can
449  case nrrdSpaceRightAnteriorSuperior:
450  spaceDir[0] *= -1; // R -> L
451  spaceDir[1] *= -1; // A -> P
452  iFlipFactors[0] = -1;
453  iFlipFactors[1] = -1;
454  break;
455  case nrrdSpaceLeftAnteriorSuperior:
456  spaceDir[0] *= -1; // R -> L
457  iFlipFactors[0] = -1;
458  break;
459  case nrrdSpaceLeftPosteriorSuperior:
460  // no change needed
461  break;
462  default:
463  // we're not coming from a space for which the conversion
464  // to LPS is well-defined
465  break;
466  }
467  this->SetSpacing(axii, spacing);
468 
469  for (unsigned int saxi=0; saxi < nrrd->spaceDim; saxi++)
470  {
471  spaceDirStd[saxi] = spaceDir[saxi];
472  }
473  this->SetDirection(axii, spaceDirStd);
474  }
475  break;
476  default:
477  case nrrdSpacingStatusUnknown:
478  itkExceptionMacro("ReadImageInformation: Error interpreting "
479  "nrrd spacing (nrrdSpacingStatusUnknown)");
480  break;
481  case nrrdSpacingStatusScalarWithSpace:
482  itkExceptionMacro("ReadImageInformation: Error interpreting "
483  "nrrd spacing (nrrdSpacingStatusScalarWithSpace)");
484  break;
485  }
486  }
487 
488  // Figure out origin
489  if (nrrd->spaceDim)
490  {
491  if (AIR_EXISTS(nrrd->spaceOrigin[0]))
492  {
493  // only set info if we have something to set
494  double spaceOrigin[NRRD_SPACE_DIM_MAX];
495  for (unsigned int saxi=0; saxi < nrrd->spaceDim; saxi++)
496  {
497  spaceOrigin[saxi] = nrrd->spaceOrigin[saxi];
498  }
499  switch (nrrd->space)
500  {
501  // convert non-LPS coords into LPS coords, when we can
502  case nrrdSpaceRightAnteriorSuperior:
503  spaceOrigin[0] *= -1; // R -> L
504  spaceOrigin[1] *= -1; // A -> P
505  break;
506  case nrrdSpaceLeftAnteriorSuperior:
507  spaceOrigin[0] *= -1; // R -> L
508  break;
509  case nrrdSpaceLeftPosteriorSuperior:
510  // no change needed
511  break;
512  default:
513  // we're not coming from a space for which the conversion
514  // to LPS is well-defined
515  break;
516  }
517  for (unsigned int saxi=0; saxi < nrrd->spaceDim; saxi++)
518  {
519  this->SetOrigin(saxi, spaceOrigin[saxi]);
520  }
521  }
522  }
523  else
524  {
525  double spaceOrigin[NRRD_DIM_MAX];
526  int originStatus = nrrdOriginCalculate(nrrd, domainAxisIdx, domainAxisNum,
527  nrrdCenterCell, spaceOrigin);
528  for (unsigned int saxi=0; saxi < domainAxisNum; saxi++)
529  {
530  switch (originStatus)
531  {
532  case nrrdOriginStatusNoMin:
533  case nrrdOriginStatusNoMaxOrSpacing:
534  // only set info if we have something to set
535  // this->SetOrigin(saxi, 0.0);
536  break;
537  case nrrdOriginStatusOkay:
538  this->SetOrigin(saxi, spaceOrigin[saxi]);
539  break;
540  default:
541  case nrrdOriginStatusUnknown:
542  case nrrdOriginStatusDirection:
543  itkExceptionMacro("ReadImageInformation: Error interpreting "
544  "nrrd origin status");
545  break;
546  }
547  }
548  }
549 
550  // Store key/value pairs in MetaDataDictionary
551  char key[AIR_STRLEN_SMALL];
552  const char *val;
553  char *keyPtr = NULL;
554  char *valPtr = NULL;
555  MetaDataDictionary &thisDic=this->GetMetaDataDictionary();
556  std::string classname(this->GetNameOfClass());
557  EncapsulateMetaData<std::string>(thisDic, ITK_InputFilterName, classname);
558  for (unsigned int kvpi=0; kvpi < nrrdKeyValueSize(nrrd); kvpi++)
559  {
560  nrrdKeyValueIndex(nrrd, &keyPtr, &valPtr, kvpi);
561  EncapsulateMetaData<std::string>(thisDic, std::string(keyPtr),
562  std::string(valPtr));
563  keyPtr = (char *)airFree(keyPtr);
564  valPtr = (char *)airFree(valPtr);
565  }
566 
567  // save in MetaDataDictionary those important nrrd fields that
568  // (currently) have no ITK equivalent. NOTE that for the per-axis
569  // information, we use the same axis index (axii) as in ITK, NOT
570  // the original axis index in nrrd (axi). This is because in the
571  // Read() method, non-scalar data is permuted to the fastest axis,
572  // on the on the Write() side, its always written to the fastest axis,
573  // so we might was well go with consistent and idiomatic indexing.
574  NrrdAxisInfo *naxis;
575  for (unsigned int axii=0; axii < domainAxisNum; axii++)
576  {
577  unsigned int axi = domainAxisIdx[axii];
578  naxis = nrrd->axis + axi;
579  if (AIR_EXISTS(naxis->thickness))
580  {
581  sprintf(key, "%s%s[%d]", KEY_PREFIX,
582  airEnumStr(nrrdField, nrrdField_thicknesses), axii);
583  EncapsulateMetaData<double>(thisDic, std::string(key),
584  naxis->thickness);
585  }
586  if (naxis->center)
587  {
588  sprintf(key, "%s%s[%d]", KEY_PREFIX,
589  airEnumStr(nrrdField, nrrdField_centers), axii);
590  val = airEnumStr(nrrdCenter, naxis->center);
591  EncapsulateMetaData<std::string>(thisDic, std::string(key),
592  std::string(val));
593  }
594  if (naxis->kind)
595  {
596  sprintf(key, "%s%s[%d]", KEY_PREFIX,
597  airEnumStr(nrrdField, nrrdField_kinds), axii);
598  val = airEnumStr(nrrdKind, naxis->kind);
599  EncapsulateMetaData<std::string>(thisDic, std::string(key),
600  std::string(val));
601  }
602  if (airStrlen(naxis->label))
603  {
604  sprintf(key, "%s%s[%d]", KEY_PREFIX,
605  airEnumStr(nrrdField, nrrdField_labels), axii);
606  EncapsulateMetaData<std::string>(thisDic, std::string(key),
607  std::string(naxis->label));
608  }
609  }
610  if (airStrlen(nrrd->content))
611  {
612  sprintf(key, "%s%s", KEY_PREFIX,
613  airEnumStr(nrrdField, nrrdField_content));
614  EncapsulateMetaData<std::string>(thisDic, std::string(key),
615  std::string(nrrd->content));
616  }
617  if (AIR_EXISTS(nrrd->oldMin))
618  {
619  sprintf(key, "%s%s", KEY_PREFIX,
620  airEnumStr(nrrdField, nrrdField_old_min));
621  EncapsulateMetaData<double>(thisDic, std::string(key), nrrd->oldMin);
622  }
623  if (AIR_EXISTS(nrrd->oldMax))
624  {
625  sprintf(key, "%s%s", KEY_PREFIX,
626  airEnumStr(nrrdField, nrrdField_old_max));
627  EncapsulateMetaData<double>(thisDic, std::string(key), nrrd->oldMax);
628  }
629  if (nrrd->space)
630  {
631  sprintf(key, "%s%s", KEY_PREFIX,
632  airEnumStr(nrrdField, nrrdField_space));
633  val = airEnumStr(nrrdSpace, nrrd->space);
634 
635  // keep everything consistent: so enter it as LPS in the meta data
636  // dictionary in case it could get converted, otherwise leave it
637  // as is
638 
639  switch (nrrd->space)
640  {
641  case nrrdSpaceRightAnteriorSuperior:
642  case nrrdSpaceLeftAnteriorSuperior:
643  case nrrdSpaceLeftPosteriorSuperior:
644  // in all these cases we could convert
645  EncapsulateMetaData<std::string>(thisDic, std::string(key),
646  std::string( airEnumStr(nrrdSpace, nrrdSpaceLeftPosteriorSuperior )));
647  break;
648  default:
649  // we're not coming from a space for which the conversion
650  // to LPS is well-defined
651  EncapsulateMetaData<std::string>(thisDic, std::string(key),
652  std::string(val));
653  break;
654  }
655  }
656 
657  if (AIR_EXISTS(nrrd->measurementFrame[0][0]))
658  {
659  sprintf(key, "%s%s", KEY_PREFIX,
660  airEnumStr(nrrdField, nrrdField_measurement_frame));
661  std::vector<std::vector<double> > msrFrame(domainAxisNum);
662 
663  // flip the measurement frame here if we have to
664  // so that everything is consistent with the ITK LPS space directions
665  // but only do this if we have a three dimensional space or smaller
666 
667  for (unsigned int saxi=0; saxi < domainAxisNum; saxi++)
668  {
669  msrFrame[saxi].resize(domainAxisNum);
670  for (unsigned int saxj=0; saxj < domainAxisNum; saxj++)
671  {
672  if ( domainAxisNum<=3 )
673  {
674  msrFrame[saxi][saxj] = iFlipFactors[saxj]*nrrd->measurementFrame[saxi][saxj];
675  }
676  else
677  {
678  msrFrame[saxi][saxj] = nrrd->measurementFrame[saxi][saxj];
679  }
680  }
681  }
682  EncapsulateMetaData<std::vector<std::vector<double> > >(thisDic,
683  std::string(key),
684  msrFrame);
685  }
686 
687  nrrd = nrrdNix(nrrd);
688  nio = nrrdIoStateNix(nio);
689 }
690 
691 
692 void NrrdImageIO::Read(void* buffer)
693 {
694 #if defined(__BORLANDC__)
695 // Disable floating point exceptions in Borland
696  _control87(MCW_EM, MCW_EM);
697 #endif // defined(__BORLANDC__)
698 
699  Nrrd *nrrd = nrrdNew();
700  unsigned int baseDim;
701  bool nrrdAllocated;
702 
703  // NOTE the main reason the logic becomes complicated here is that
704  // ITK has to be the one to allocate the data segment ("buffer")
705 
707  {
708  // It may be that this is coming from a nrrdKind3DMaskedSymMatrix,
709  // in which case ITK's buffer has not been allocated for the
710  // actual size of the data. The data will be allocated by nrrdLoad.
711  nrrdAllocated = true;
712  }
713  else
714  {
715  // The data buffer has already been allocated for the correct size.
716  // Hand the buffer off to the nrrd, setting just enough info so that
717  // the nrrd knows the allocated data size (the axes may actually be out
718  // of order in the case of non-scalar data. Internal to nrrdLoad(), the
719  // given buffer will be re-used, instead of allocating new data.
720  nrrdAllocated = false;
721  nrrd->data = buffer;
722  nrrd->type = this->ITKToNrrdComponentType( this->m_ComponentType );
723  if ( ImageIOBase::SCALAR == this->m_PixelType )
724  {
725  baseDim = 0;
726  }
727  else
728  {
729  baseDim = 1;
730  nrrd->axis[0].size = this->GetNumberOfComponents();
731  }
732  nrrd->dim = baseDim + this->GetNumberOfDimensions();
733  for (unsigned int axi = 0; axi < this->GetNumberOfDimensions(); axi++)
734  {
735  nrrd->axis[axi+baseDim].size = this->GetDimensions(axi);
736  }
737  }
738 
739  // Read in the nrrd. Yes, this means that the header is being read
740  // twice: once by NrrdImageIO::ReadImageInformation, and once here
741  if ( nrrdLoad(nrrd, this->GetFileName(), NULL) != 0 )
742  {
743  char *err = biffGetDone(NRRD); // would be nice to free(err)
744  itkExceptionMacro("Read: Error reading "
745  << this->GetFileName() << ":\n" << err);
746  }
747 
748  unsigned int rangeAxisNum, rangeAxisIdx[NRRD_DIM_MAX];
749  rangeAxisNum = nrrdRangeAxesGet(nrrd, rangeAxisIdx);
750 
751  if ( rangeAxisNum > 1)
752  {
753  itkExceptionMacro("Read: handling more than one non-scalar axis "
754  "not currently handled");
755  }
756  if (1 == rangeAxisNum && 0 != rangeAxisIdx[0])
757  {
758  // the range (dependent variable) is not on the fastest axis,
759  // so we have to permute axes to put it there, since that is
760  // how we set things up in ReadImageInformation() above
761  Nrrd *ntmp = nrrdNew();
762  unsigned int axmap[NRRD_DIM_MAX];
763  axmap[0] = rangeAxisIdx[0];
764  for (unsigned int axi=1; axi<nrrd->dim; axi++)
765  {
766  axmap[axi] = axi - (axi <= rangeAxisIdx[0]);
767  }
768  // The memory size of the input and output of nrrdAxesPermute is
769  // the same; the existing nrrd->data is re-used.
770  if (nrrdCopy(ntmp, nrrd)
771  || nrrdAxesPermute(nrrd, ntmp, axmap))
772  {
773  char *err = biffGetDone(NRRD); // would be nice to free(err)
774  itkExceptionMacro("Read: Error permuting independent axis in "
775  << this->GetFileName() << ":\n" << err);
776  }
777  nrrdNuke(ntmp);
778  }
779 
780  if (nrrdAllocated)
781  {
782  // Now we have to get the data back into the given ITK buffer
783  // In any case, the logic here has the luxury of assuming that the
784  // *single* non-scalar axis is the *first* (fastest) axis.
785  if (nrrdKind3DMaskedSymMatrix == nrrd->axis[0].kind
786  && ImageIOBase::SYMMETRICSECONDRANKTENSOR == this->GetPixelType())
787  {
788  // we crop out the mask and put the output in ITK-allocated "buffer"
789  size_t size[NRRD_DIM_MAX], minIdx[NRRD_DIM_MAX], maxIdx[NRRD_DIM_MAX];
790  for (unsigned int axi=0; axi<nrrd->dim; axi++)
791  {
792  minIdx[axi] = (0 == axi) ? 1 : 0;
793  maxIdx[axi] = nrrd->axis[axi].size-1;
794  size[axi] = maxIdx[axi] - minIdx[axi] + 1;
795  }
796  Nrrd *ntmp = nrrdNew();
797  if (nrrdCopy(ntmp, nrrd)
798  || (nrrdEmpty(nrrd),
799  nrrdWrap_nva(nrrd, buffer, ntmp->type, ntmp->dim, size))
800  || nrrdCrop(nrrd, ntmp, minIdx, maxIdx))
801  {
802  char *err = biffGetDone(NRRD); // would be nice to free(err)
803  itkExceptionMacro("Read: Error copying, crapping or cropping:\n"
804  << err);
805  }
806  nrrdNuke(ntmp);
807  nrrdNix(nrrd);
808  }
809  else
810  {
811 
812  // false alarm; we didn't need to allocate the data ourselves
813  memcpy(buffer, nrrd->data,
814  nrrdElementSize(nrrd)*nrrdElementNumber(nrrd));
815  nrrdNuke(nrrd);
816  }
817  }
818  else //
819  {
820 
821  // "buffer" == nrrd->data was ITK-allocated; lose the nrrd struct
822  nrrdNix(nrrd);
823  }
824 }
825 
826 
827 bool NrrdImageIO::CanWriteFile( const char * name )
828 {
829 #if defined(__BORLANDC__)
830 // Disable floating point exceptions in Borland
831  _control87(MCW_EM, MCW_EM);
832 #endif // defined(__BORLANDC__)
833 
834  std::string filename = name;
835  if( filename == "" )
836  {
837  return false;
838  }
839 
840  std::string::size_type nrrdPos = filename.rfind(".nrrd");
841  if ((nrrdPos != std::string::npos)
842  && (nrrdPos == filename.length() - 5))
843  {
844  return true;
845  }
846 
847  std::string::size_type nhdrPos = filename.rfind(".nhdr");
848  if ((nhdrPos != std::string::npos)
849  && (nhdrPos == filename.length() - 5))
850  {
851  return true;
852  }
853 
854  return false;
855 }
856 
858 {
859 #if defined(__BORLANDC__)
860 // Disable floating point exceptions in Borland
861  _control87(MCW_EM, MCW_EM);
862 #endif // defined(__BORLANDC__)
863 
864  // Nothing needs doing here.
865 }
866 
867 
868 void NrrdImageIO::Write( const void* buffer)
869 {
870 #if defined(__BORLANDC__)
871 // Disable floating point exceptions in Borland
872  _control87(MCW_EM, MCW_EM);
873 #endif // defined(__BORLANDC__)
874 
875  Nrrd *nrrd = nrrdNew();
876  NrrdIoState *nio = nrrdIoStateNew();
877  int kind[NRRD_DIM_MAX];
878  size_t size[NRRD_DIM_MAX];
879  unsigned int nrrdDim, baseDim, spaceDim;
880  double spaceDir[NRRD_DIM_MAX][NRRD_SPACE_DIM_MAX];
881  double origin[NRRD_DIM_MAX];
882 
883  spaceDim = this->GetNumberOfDimensions();
884  if (this->GetNumberOfComponents() > 1)
885  {
886  size[0] = this->GetNumberOfComponents();
887  switch (this->GetPixelType())
888  {
889  case ImageIOBase::RGB:
890  kind[0] = nrrdKindRGBColor;
891  break;
892  case ImageIOBase::RGBA:
893  kind[0] = nrrdKindRGBAColor;
894  break;
895  case ImageIOBase::POINT:
896  kind[0] = nrrdKindPoint;
897  break;
899  kind[0] = nrrdKindCovariantVector;
900  break;
903  kind[0] = nrrdKind3DSymMatrix;
904  break;
906  kind[0] = nrrdKindComplex;
907  break;
908  case ImageIOBase::VECTOR:
909  case ImageIOBase::OFFSET: // HEY is this right?
910  case ImageIOBase::FIXEDARRAY: // HEY is this right?
911  default:
912  kind[0] = nrrdKindVector;
913  break;
914  }
915  // the range axis has no space direction
916  for (unsigned int saxi=0; saxi < spaceDim; saxi++)
917  {
918  spaceDir[0][saxi] = AIR_NAN;
919  }
920  baseDim = 1;
921  }
922  else
923  {
924  baseDim = 0;
925  }
926  nrrdDim = baseDim + spaceDim;
927  std::vector<double> spaceDirStd(spaceDim);
928  unsigned int axi;
929  for (axi=0; axi < spaceDim; axi++)
930  {
931  size[axi+baseDim] = this->GetDimensions(axi);
932  kind[axi+baseDim] = nrrdKindDomain;
933  origin[axi] = this->GetOrigin(axi);
934  double spacing = this->GetSpacing(axi);
935  spaceDirStd = this->GetDirection(axi);
936  for (unsigned int saxi=0; saxi < spaceDim; saxi++)
937  {
938  spaceDir[axi+baseDim][saxi] = spacing*spaceDirStd[saxi];
939  }
940  }
941  if (nrrdWrap_nva(nrrd, const_cast<void *>(buffer),
943  nrrdDim, size) || (3 == spaceDim
944  // special case: ITK is LPS in 3-D
945  ? nrrdSpaceSet(nrrd, nrrdSpaceLeftPosteriorSuperior)
946  : nrrdSpaceDimensionSet(nrrd, spaceDim)) ||
947  nrrdSpaceOriginSet(nrrd, origin))
948  {
949  char *err = biffGetDone(NRRD); // would be nice to free(err)
950  itkExceptionMacro("Write: Error wrapping nrrd for "
951  << this->GetFileName() << ":\n" << err);
952  }
953  nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoKind, kind);
954  nrrdAxisInfoSet_nva(nrrd, nrrdAxisInfoSpaceDirection, spaceDir);
955 
956  // Go through MetaDataDictionary and set either specific nrrd field
957  // or a key/value pair
958  MetaDataDictionary &thisDic = this->GetMetaDataDictionary();
959  std::vector<std::string> keys = thisDic.GetKeys();
960  std::vector<std::string>::const_iterator keyIt;
961  const char *keyField, *field;
962  for( keyIt = keys.begin(); keyIt != keys.end(); keyIt++ )
963  {
964  if (!strncmp(KEY_PREFIX, (*keyIt).c_str(), strlen(KEY_PREFIX)))
965  {
966  keyField = (*keyIt).c_str() + strlen(KEY_PREFIX);
967  // only of one of these can succeed
968  field = airEnumStr(nrrdField, nrrdField_thicknesses);
969  if (!strncmp(keyField, field, strlen(field)))
970  {
971  if (1 == sscanf(keyField + strlen(field), "[%d]", &axi)
972  && axi + baseDim < nrrd->dim)
973  {
974  double thickness = 0.0; // local for Borland
975  ExposeMetaData<double>(thisDic, *keyIt, thickness);
976  nrrd->axis[axi+baseDim].thickness = thickness;
977  }
978  }
979  field = airEnumStr(nrrdField, nrrdField_centers);
980  if (!strncmp(keyField, field, strlen(field)))
981  {
982  if (1 == sscanf(keyField + strlen(field), "[%d]", &axi)
983  && axi + baseDim < nrrd->dim)
984  {
985  std::string value; // local for Borland
986  ExposeMetaData<std::string>(thisDic, *keyIt, value);
987  nrrd->axis[axi+baseDim].center = airEnumVal(nrrdCenter,
988  value.c_str());
989  }
990  }
991  field = airEnumStr(nrrdField, nrrdField_kinds);
992  if (!strncmp(keyField, field, strlen(field)))
993  {
994  if (1 == sscanf(keyField + strlen(field), "[%d]", &axi)
995  && axi + baseDim < nrrd->dim)
996  {
997  std::string value; // local for Borland
998  ExposeMetaData<std::string>(thisDic, *keyIt, value);
999  nrrd->axis[axi+baseDim].kind = airEnumVal(nrrdKind,
1000  value.c_str());
1001  }
1002  }
1003  field = airEnumStr(nrrdField, nrrdField_labels);
1004  if (!strncmp(keyField, field, strlen(field)))
1005  {
1006  if (1 == sscanf(keyField + strlen(field), "[%d]", &axi)
1007  && axi + baseDim < nrrd->dim)
1008  {
1009  std::string value; // local for Borland
1010  ExposeMetaData<std::string>(thisDic, *keyIt, value);
1011  nrrd->axis[axi+baseDim].label = airStrdup(value.c_str());
1012  }
1013  }
1014  field = airEnumStr(nrrdField, nrrdField_old_min);
1015  if (!strncmp(keyField, field, strlen(field)))
1016  {
1017  ExposeMetaData<double>(thisDic, *keyIt, nrrd->oldMin);
1018  }
1019  field = airEnumStr(nrrdField, nrrdField_old_max);
1020  if (!strncmp(keyField, field, strlen(field)))
1021  {
1022  ExposeMetaData<double>(thisDic, *keyIt, nrrd->oldMax);
1023  }
1024 
1025  field = airEnumStr(nrrdField, nrrdField_space);
1026  if (!strncmp(keyField, field, strlen(field)))
1027  {
1028  int space;
1029  std::string value; // local for Borland
1030  ExposeMetaData<std::string>(thisDic, *keyIt, value);
1031  space = airEnumVal(nrrdSpace, value.c_str());
1032  if (nrrdSpaceDimension(space) == nrrd->spaceDim)
1033  {
1034  // sanity check
1035  nrrd->space = space;
1036  }
1037  }
1038 
1039  field = airEnumStr(nrrdField, nrrdField_content);
1040  if (!strncmp(keyField, field, strlen(field)))
1041  {
1042  std::string value; // local for Borland
1043  ExposeMetaData<std::string>(thisDic, *keyIt, value);
1044  nrrd->content = airStrdup(value.c_str());
1045  }
1046  field = airEnumStr(nrrdField, nrrdField_measurement_frame);
1047  if (!strncmp(keyField, field, strlen(field)))
1048  {
1049  std::vector<std::vector<double> > msrFrame;
1050  ExposeMetaData<std::vector<std::vector<double> > >(thisDic,
1051  *keyIt, msrFrame);
1052  for (unsigned int saxi=0; saxi < nrrd->spaceDim; saxi++)
1053  {
1054  for (unsigned int saxj=0; saxj < nrrd->spaceDim; saxj++)
1055  {
1056  if (saxi < msrFrame.size() &&
1057  saxj < msrFrame[saxi].size())
1058  {
1059  nrrd->measurementFrame[saxi][saxj] = msrFrame[saxi][saxj];
1060  }
1061  else
1062  {
1063  // there is a difference between the dimension of the
1064  // recorded measurement frame, and the actual dimension of
1065  // the ITK image, which (for now) determines nrrd->spaceDim.
1066  // We can't set this to AIR_NAN, because the coefficients of
1067  // the measurement frame have to all be equally existent.
1068  // If we used 0, it might not a flag that something is wrong.
1069  // So, we have to get creative.
1070  nrrd->measurementFrame[saxi][saxj] = 666666;
1071  }
1072  }
1073  }
1074  }
1075  }
1076  else
1077  {
1078  // not a NRRD field packed into meta data; just a regular key/value
1079  std::string value; // local for Borland
1080  ExposeMetaData<std::string>(thisDic, *keyIt, value);
1081  nrrdKeyValueAdd(nrrd, (*keyIt).c_str(), value.c_str());
1082  }
1083  }
1084 
1085  // set encoding for data: compressed (raw), (uncompressed) raw, or ascii
1086  if (this->GetUseCompression() == true
1087  && nrrdEncodingGzip->available())
1088  {
1089  // this is necessarily gzip-compressed *raw* data
1090  nio->encoding = nrrdEncodingGzip;
1091  }
1092  else
1093  {
1094  Superclass::FileType fileType = this->GetFileType();
1095  switch ( fileType )
1096  {
1097  default:
1098  case TypeNotApplicable:
1099  case Binary:
1100  nio->encoding = nrrdEncodingRaw;
1101  break;
1102  case ASCII:
1103  nio->encoding = nrrdEncodingAscii;
1104  break;
1105  }
1106  }
1107 
1108  // set desired endianness of output
1109  Superclass::ByteOrder byteOrder = this->GetByteOrder();
1110  switch (byteOrder)
1111  {
1112  default:
1113  case OrderNotApplicable:
1114  nio->endian = airEndianUnknown;
1115  break;
1116  case BigEndian:
1117  nio->endian = airEndianBig;
1118  break;
1119  case LittleEndian:
1120  nio->endian = airEndianLittle;
1121  break;
1122  }
1123 
1124  // Write the nrrd to file.
1125  if (nrrdSave(this->GetFileName(), nrrd, nio))
1126  {
1127  char *err = biffGetDone(NRRD); // would be nice to free(err)
1128  itkExceptionMacro("Write: Error writing "
1129  << this->GetFileName() << ":\n" << err);
1130  }
1131 
1132  // Free the nrrd struct but don't touch nrrd->data
1133  nrrd = nrrdNix(nrrd);
1134  nio = nrrdIoStateNix(nio);
1135 }
1136 
1137 } // end namespace itk

Generated at Sat Feb 2 2013 23:56:47 for Orfeo Toolbox with doxygen 1.8.1.1