Orfeo Toolbox  3.16
itkBrains2MaskImageIO.cxx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkBrains2MaskImageIO.cxx,v $
5  Language: C++
6  Date: $Date: 2009-02-07 05:23:54 $
7  Version: $Revision: 1.32 $
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 
18 #include "itkBrains2MaskImageIO.h"
19 #include "itkExceptionObject.h"
20 #include "itkByteSwapper.h"
21 #include "itkIOCommon.h"
22 #include "itkMetaDataObject.h"
23 #if !defined(_MSC_VER) || (_MSC_VER > 1300)
25 #endif
26 #include <stdio.h>
27 #include "itk_zlib.h"
28 #include <time.h>
29 #include <itksys/SystemTools.hxx>
30 #include <sstream>
31 static const unsigned char DEF_WHITE_MASK=255;
32 namespace itk
33 {
34 #define Brains2_MASKFILE_WHITE 0
35 #define Brains2_MASKFILE_BLACK 1
36 #define Brains2_MASKFILE_GRAY 2
37 
38 
39 template <class TPixel>
41 public:
42  unsigned int Evaluate(const TPixel *pixel);
43 };
44 template <class TPixel>
45 unsigned int
47 Evaluate(const TPixel *pixel)
48 {
49  return *pixel == 0 ? 0 : 1;
50 }
51 
52 
53 // Default constructor
55 {
56  //by default, only have 3 dimensions
57  this->SetNumberOfDimensions(3);
60  //The file byte order
63 }
64 
66 {
67  //Purposefully left blank
68 }
69 
70 void Brains2MaskImageIO::PrintSelf(std::ostream& os, Indent indent) const
71 {
72  Superclass::PrintSelf(os, indent);
73 }
74 
75 bool Brains2MaskImageIO::CanWriteFile(const char * FileNameToWrite)
76 {
77  m_FileName=FileNameToWrite;
78  // Check filename to ensure that it meets the requirement of ending with .mask
79  const std::string FileExtension=itksys::SystemTools::GetFilenameLastExtension(m_FileName);
80  if( FileExtension == std::string(".mask"))// .mask is at the end of the filename
81  {
82  return true;
83  }
84  return false;
85 }
86 
87 //The function that is used to read the octree stream to an octree.
89 readOctree (std::ifstream & octreestream,
90  const ImageIOBase::ByteOrder machineByteOrder,
91  const ImageIOBase::ByteOrder fileByteOrder)
92 {
93  //Read in the color to set
94  unsigned short int colorCode;
95  octreestream.read((char *)&colorCode, sizeof (unsigned short int));
96  if (machineByteOrder != fileByteOrder)
97  {
98  if ( machineByteOrder == LittleEndian )
99  {
101  SwapFromSystemToBigEndian( &colorCode );
102  }
103  else if ( machineByteOrder == BigEndian )
104  {
106  SwapFromSystemToLittleEndian( &colorCode );
107  }
108  }
109  //Create child array of nodes.
110  OctreeNodeBranch *CurrentNodeBranch = new OctreeNodeBranch(m_Octree);
111  //7766554433221100 ChildID
112  //1111110000000000 Bit
113  //5432109876543210 Numbers
114  for (unsigned int i = ZERO; i <= SEVEN; i++)
115  {
116  OctreeNode *curnode =
117  CurrentNodeBranch->GetLeaf(static_cast<enum LeafIdentifier>(i));
118 
119  switch ((colorCode >> (i << 1)) & 3) //(colorCode/vcl_pow(2,i*2) ) & 00000011b
120  {
121  case Brains2_MASKFILE_WHITE: // 0
123  break;
124  case Brains2_MASKFILE_BLACK: // 1
126  break;
127  case Brains2_MASKFILE_GRAY: // 2
128  //NOTE recursive call on all children to set them.
129  curnode->SetBranch(
130  readOctree(octreestream,machineByteOrder,
131  fileByteOrder));
132  break;
133  }
134  }
135  return CurrentNodeBranch;
136 }
137 
139 ::Read(void* buffer)
140 {
141  std::ifstream local_InputStream;
142  local_InputStream.open( this->m_FileName.c_str(), std::ios::in | std::ios::binary );
143  if( local_InputStream.fail() )
144  {
145  itkExceptionMacro(<< "File "<< this->m_FileName << "cannot be opened for reading");
146  }
147  //Just fast forward throuth the file header NOTE: This re-reads the header information.
148  this->m_B2MaskHeader.ReadBrains2Header(local_InputStream);
149  //Actually start reading the octree
150  unsigned int octreeHdr[6];
151  //Need to gobble up the end of line character here and move one more byte.
152  //Except for Borland where the operator>> has already gobbled the endline char
153 #if !defined(__BORLANDC__)
154  local_InputStream.ignore();
155 #endif
156  local_InputStream.read((char *)octreeHdr,6*sizeof(unsigned int));
157  if(this->m_ByteOrder != this->m_MachineByteOrder)
158  {
159  if ( m_MachineByteOrder == LittleEndian )
160  {
162  }
163  else
164  {
166  }
167  }
170  octree->SetDepth(octreeHdr[0]);
171  octree->SetWidth(octreeHdr[1]);
172  octree->SetTrueDims(octreeHdr[2],octreeHdr[3],octreeHdr[4]);
173  this->m_Octree = octree;
174  switch (octreeHdr[5])
175  {
177  //NOTE: THIS ALMOST NEVER HAPPENS!! All white image
178  octree->SetColor(DEF_WHITE_MASK);
179  break;
181  //NOTE: THIS ALMOST NEVER HAPPENS!! All black image
182  octree->SetColor(0);
183  break;
185  octree->SetTree(readOctree(local_InputStream,this->m_MachineByteOrder,this->m_ByteOrder ));
186  }
187  local_InputStream.close();
188  //DEBUG: Now just convert the octree into an image for returning!!!
189  //DEBUG: This is written for 3D octreees only right now
190  unsigned char * const p = static_cast<unsigned char *>(buffer);
191  for(unsigned int k=0; k< this->m_Dimensions[2]; k++)
192  {
193  const unsigned int slice_offset=k*this->m_Dimensions[1]*this->m_Dimensions[0];
194  for(unsigned int j=0; j< this->m_Dimensions[1]; j++)
195  {
196  const unsigned int sliceandrowoffset=slice_offset+j*this->m_Dimensions[0];
197  for(unsigned int i=0; i< this->m_Dimensions[0]; i++)
198  {
199  //unsigned int val = octree->GetValue(i,this->m_Dimensions[1]-1-j,k);
200  unsigned int val = octree->GetValue(i,j,k);
201  if(val != 0)
202  {
203  p[sliceandrowoffset+i] = DEF_WHITE_MASK;
204  }
205  else
206  {
207  p[sliceandrowoffset+i] = 0;
208  }
209  }
210  }
211  }
212  return;
213 }
214 // This method will only test if the header looks like an
215 // Brains2Mask Header. Some code is redundant with ReadImageInformation
216 // a StateMachine could provide a better implementation
217 bool Brains2MaskImageIO::CanReadFile( const char* FileNameToRead )
218 {
219  { // Check filename to ensure that it meets the requirement of ending with .mask
220  const std::string FileName(FileNameToRead);
221  const std::string FileExtension=itksys::SystemTools::GetFilenameLastExtension(FileName);
222  if( FileExtension != std::string(".mask"))// .mask is at the end of the filename
223  {
224  return false;
225  }
226  }
227  std::ifstream local_InputStream;
228  local_InputStream.open( FileNameToRead, std::ios::in | std::ios::binary );
229  if( local_InputStream.fail() )
230  {
231  return false;
232  }
233  try
234  {
236  this->m_IPLHeaderInfo.ReadBrains2Header(local_InputStream);
237  }
238  catch (ExceptionObject & itkNotUsed(e))
239  {
240  return false;
241  }
242  //
243  // to try and maintain some backwards compatibility
244  unsigned dims = this->GetNumberOfDimensions();
245  std::vector<double> dirx(dims,0), diry(dims,0), dirz(dims,0);
246  if(this->m_IPLHeaderInfo.DoesKeyExist("DIRX0:"))
247  {
248  dirx[0] = this->m_IPLHeaderInfo.getFloat("DIRX0:");
249  dirx[1] = this->m_IPLHeaderInfo.getFloat("DIRX1:");
250  dirx[2] = this->m_IPLHeaderInfo.getFloat("DIRX2:");
251  diry[0] = this->m_IPLHeaderInfo.getFloat("DIRY0:");
252  diry[1] = this->m_IPLHeaderInfo.getFloat("DIRY1:");
253  diry[2] = this->m_IPLHeaderInfo.getFloat("DIRY2:");
254  dirz[0] = this->m_IPLHeaderInfo.getFloat("DIRZ0:");
255  dirz[1] = this->m_IPLHeaderInfo.getFloat("DIRZ1:");
256  dirz[2] = this->m_IPLHeaderInfo.getFloat("DIRZ2:");
257  }
258  else if(this->m_IPLHeaderInfo.DoesKeyExist("MASK_ACQ_PLANE:"))
259  {
260  // backwards compatibility -- no newly created mask file will
261  // include this tag.
262  MetaDataDictionary &thisDic=this->GetMetaDataDictionary();
265  std::string acqVal = this->m_IPLHeaderInfo.getString("MASK_ACQ_PLANE:");
266  if(acqVal == "SAGITTAL")
267  {
269  }
270  else if(acqVal == "AXIAL")
271  {
273  }
274  else if(acqVal == "CORONAL")
275  {
277  }
278  else
279  {
280  itkExceptionMacro(<< "If MASK_ACQ_PLANE is specified, then it must be one of CORONAL, AXIAL, or SAGITAL flags.");
281  }
282  EncapsulateMetaData<SpatialOrientation::ValidCoordinateOrientationFlags>
283  (thisDic,ITK_CoordinateOrientation, coord_orient);
284 #if !defined(_MSC_VER) || (_MSC_VER > 1300)
285  //An error was encountered in code that depends upon the valid coord_orientation.
286  typedef SpatialOrientationAdapter OrientAdapterType;
288  dir = OrientAdapterType().ToDirectionCosines(coord_orient);
289  dirx[0] = dir[0][0];
290  dirx[1] = dir[1][0];
291  dirx[2] = dir[2][0];
292  diry[0] = dir[0][1];
293  diry[1] = dir[1][1];
294  diry[2] = dir[2][1];
295  dirz[0] = dir[0][2];
296  dirz[1] = dir[1][2];
297  dirz[2] = dir[2][2];
298 #endif
299  for(unsigned i = 3; i < dims; i++)
300  {
301  dirx[i] = diry[i] = dirz[i] = 0;
302  }
303  }
304  else
305  {
306  itkExceptionMacro(<< "No orientation specified.");
307  }
308  this->SetDirection(0,dirx);
309  this->SetDirection(1,diry);
310  this->SetDirection(2,dirz);
311  if(this->m_IPLHeaderInfo.DoesKeyExist("ORIGIN0:"))
312  {
313  double origin[3];
314  origin[0] = this->m_IPLHeaderInfo.getFloat("ORIGIN0:");
315  origin[1] = this->m_IPLHeaderInfo.getFloat("ORIGIN1:");
316  origin[2] = this->m_IPLHeaderInfo.getFloat("ORIGIN2:");
317  this->SetOrigin(0,origin[0]);
318  this->SetOrigin(1,origin[1]);
319  this->SetOrigin(2,origin[2]);
320  }
321 
322  local_InputStream.close();
323  if(this->m_IPLHeaderInfo.DoesKeyExist("MASK_HEADER_BEGIN") == false)
324  {
325  return false;
326  }
327  this->m_ByteOrder=(this->m_IPLHeaderInfo.getString("BYTE_ORDER:")
328  =="LITTLE_ENDIAN") ? LittleEndian : BigEndian;
331 
332  //this->m_IPLHeaderInfo.PrintSelf(std::cout);
333  const int TempNumDims=this->m_IPLHeaderInfo.getInt("MASK_NUM_DIMS:");
334  this->SetNumberOfDimensions(TempNumDims);
335  //NOTE: Brains2MaskImage dim[0] are the number of dims, and dim[1..7] are the
336  // actual dims.
337  m_Dimensions[ 0 ] = this->m_IPLHeaderInfo.getInt("MASK_X_SIZE:");
338  m_Dimensions[ 1 ] = this->m_IPLHeaderInfo.getInt("MASK_Y_SIZE:");
339  m_Dimensions[ 2 ] = this->m_IPLHeaderInfo.getInt("MASK_Z_SIZE:");
340  m_Spacing[ 0 ] = this->m_IPLHeaderInfo.getFloat("MASK_X_RESOLUTION:");
341  m_Spacing[ 1 ] = this->m_IPLHeaderInfo.getFloat("MASK_Y_RESOLUTION:");
342  m_Spacing[ 2 ] = this->m_IPLHeaderInfo.getFloat("MASK_Z_RESOLUTION:");
343 
345  return true;
346 }
347 
349 {
350  this->CanReadFile( this->m_FileName.c_str() );
351 }
352 
353 // cut the gordian knot, just do the header in one
354 // long printf
355 static const char mask_header_format[] =
356  "IPL_HEADER_BEGIN\n"
357  "PATIENT_ID: %s\n" // 0
358  "SCAN_ID: %s\n" // 1
359  "FILENAME: %s\n" // 2
360  "DATE: %s\n" // 3
361  "CREATOR: %s\n" // 4
362  "PROGRAM: %s\n" // 5
363  "MODULE: %s\n" // 6
364  "VERSION: %s\n" // 7
365  "NAME: %s\n" // 8
366  "BYTE_ORDER: BIG_ENDIAN\n"
367  "MASK_HEADER_BEGIN\n"
368  "MASK_NUM_DIMS: %d\n" // 9
369  "MASK_X_SIZE: %d\n" // 10
370  "MASK_X_RESOLUTION: %f\n" // 11
371  "MASK_Y_SIZE: %d\n" // 12
372  "MASK_Y_RESOLUTION: %f\n" // 13
373  "MASK_Z_SIZE: %d\n" // 14
374  "MASK_Z_RESOLUTION: %f\n" // 15
375  "MASK_THRESHOLD: %f\n" // 16
376  "MASK_NAME: %d\n" // 17
377 #if defined(OBSOLETE)
378  "MASK_ACQ_PLANE: %s\n" // 18
379 #endif
380  "DIRX0: %f\n" // 18
381  "DIRX1: %f\n" // 19
382  "DIRX2: %f\n" // 20
383  "DIRY0: %f\n" // 21
384  "DIRY1: %f\n" // 22
385  "DIRY2: %f\n" // 23
386  "DIRZ0: %f\n" // 24
387  "DIRZ1: %f\n" // 25
388  "DIRZ2: %f\n" // 26
389  "ORIGIN0: %f\n" // 27
390  "ORIGIN1: %f\n" // 28
391  "ORIGIN2: %f\n" // 29
392  "MASK_HEADER_END\n"
393  "IPL_HEADER_END\n";
394 
395 void
398 {
399  return;
400 }
401 static bool
402 writeOctree (OctreeNode *branch,std::ofstream &output)
403 {
404  unsigned i;
405  unsigned short colorCode = 0;
406 
407  for (i = 0; i < 8; i++)
408  {
409  OctreeNode &subnode =
410  branch->GetChild(static_cast<enum LeafIdentifier>(i));
411  if (subnode.IsNodeColored())
412  {
413  if(subnode.GetColor() == Brains2_MASKFILE_BLACK)
414  {
415  colorCode |= Brains2_MASKFILE_BLACK << (i << 1);
416  }
417  else
418  {
419  colorCode |= Brains2_MASKFILE_WHITE << (i << 1);
420  }
421  }
422  else
423  {
424  colorCode |= Brains2_MASKFILE_GRAY << (i << 1);
425  }
426  }
428  output.write((const char *)&colorCode,sizeof(colorCode));
429  for (i = 0; i < 8; i++)
430  {
431  OctreeNode &subnode =
432  branch->GetChild(static_cast<enum LeafIdentifier>(i));
433  if (!subnode.IsNodeColored())
434  {
435  writeOctree (&subnode, output);
436  }
437  }
438  return true;
439 }
440 
441 
442 static void replace_blanks(std::string &s)
443 {
444  for(unsigned i = 0; i < s.size(); i++)
445  {
446  if(s[i] == ' ')
447  {
448  s[i] = '_';
449  }
450  }
451 }
452 
453 void
455 ::Write( const void* buffer)
456 {
457  if(this->m_FileName == "")
458  {
459  itkExceptionMacro(<< "Error in missing Filename");
460  }
461  std::ofstream output(this->m_FileName.c_str(), std::ios::out | std::ios::binary );
462  if(output.fail())
463  {
464  itkExceptionMacro(<< "Error in opening file "<< this->m_FileName << " for writing");
465  }
466  const unsigned xsize = this->GetDimensions(0);
467  const unsigned ysize = this->GetDimensions(1);
468  const unsigned zsize = this->GetDimensions(2);
469 
470  const double xres = this->GetSpacing(0);
471  const double yres = this->GetSpacing(1);
472  const double zres = this->GetSpacing(2);
473 
474  MetaDataDictionary &thisDic=this->GetMetaDataDictionary();
475  std::string temp;
476  std::string patient_id("00000");
477  if(ExposeMetaData<std::string>(thisDic,ITK_PatientID,temp))
478  {
479  patient_id = temp;
480  }
481  // to do -- add more header crap
482  //Write the image Information before writing data
483  char buf[16384];
484  time_t rawtime;
485  struct tm *timeinfo;
486  time(&rawtime);
487  timeinfo = localtime(&rawtime);
488  std::string timestr = asctime(timeinfo);
489  replace_blanks(timestr);
490  std::string::size_type newline = timestr.rfind('\n');
491  if(newline != std::string::npos)
492  {
493  timestr.erase(newline);
494  }
495  if(patient_id == "" || patient_id == " " )
496  {
497  patient_id = "00000";
498  }
499  std::string fname = this->m_FileName;
500  replace_blanks(fname);
501  std::string orientation = "UNKNOWN";
502  std::vector<double> dirx = this->GetDirection(0);
503  std::vector<double> diry = this->GetDirection(1);
504  std::vector<double> dirz = this->GetDirection(2);
505  double origin[3];
506  origin[0] = this->GetOrigin(0);
507  origin[1] = this->GetOrigin(1);
508  origin[2] = this->GetOrigin(2);
509  //
510  // the old way of doing things...
511 #if defined(OBSOLETE)
514  for(unsigned int i = 0; i < 3; i++)
515  {
516  dir[i][1] = dirx[i];
517  dir[i][1] = diry[i];
518  dir[i][2] = dirz[i];
519  }
520  coord_orient = SpatialOrientationAdapter().FromDirectionCosines(dir);
521 
522  switch (coord_orient)
523  {
525  // AnalyzeImageIO::ITK_ANALYZE_ORIENTATION_RPI_TRANSVERSE;
526  orientation = "AXIAL";
527  break;
529  // AnalyzeImageIO::ITK_ANALYZE_ORIENTATION_PIR_SAGITTAL;
530  orientation = "SAGITTAL";
531  break;
533  // AnalyzeImageIO::ITK_ANALYZE_ORIENTATION_RIP_CORONAL;
534  orientation = "CORONAL";
535  break;
536  default:
540  itkExceptionMacro(<< "Error: Invalid orientation specified for writing mask. \n"
541  << "\nGIVEN " << coord_orient << "\n" << dir
542  << "\n Only Axial, Sagital, and Coronal orietations are supported in this file format."
543  << "\nAXIAL " << SpatialOrientation::ITK_COORDINATE_ORIENTATION_RPI << "\n" << AXIdir
544  << "\nSAGITTAL " << SpatialOrientation::ITK_COORDINATE_ORIENTATION_PIR << "\n" << SAGdir
545  << "\nCORONAL " << SpatialOrientation::ITK_COORDINATE_ORIENTATION_RIP << "\n" << CORdir
546  );
547  break;
548  }
549 #endif
550  sprintf(buf,mask_header_format,
551  patient_id.c_str(), // 0
552  "00000", // scan_id 1
553  fname.c_str(), // file_name 2
554  timestr.c_str(), // date 3
555  "Anonymous", // creator 4
556  "itkBrains2MaskImageIO", // program 5
557  "None", // module 6
558  "1", // version 7
559  itksys::SystemTools::GetFilenameName(m_FileName).c_str(), // name 8
560  3, // num_dims 9
561  xsize, // xsize 10
562  xres, // x_res 11
563  ysize, // ysize 12
564  yres, // y_res 13
565  zsize, // zsize 14
566  zres, // z_res 15
567  0.0, // threshold 16
568  -1, // mask_name 17
569 #if defined(OBSOLETE)
570  orientation.c_str() // acq plane
571 #endif
572  dirx[0],dirx[1],dirx[2], // 18, 19, 20
573  diry[0],diry[1],diry[2], // 21, 22, 23
574  dirz[0],dirz[1],dirz[2], // 24, 25, 26
575  origin[0],origin[1],origin[2] // 27, 28, 29
576  );
577  output.write(buf,strlen(buf));
578  unsigned octreeHdr[6];
579  OctreeNode *tree;
580  OctreeBase::Pointer octBasePtr;
581  if(m_ComponentType == CHAR)
582  {
584  }
585  else if(m_ComponentType == UCHAR)
586  {
588  }
589  else if(m_ComponentType == SHORT)
590  {
592  }
593  else if(m_ComponentType == USHORT)
594  {
596  }
597  else if(m_ComponentType == INT)
598  {
599  octBasePtr = Octree<int,2,Brains2MaskMappingFunction<int> >::New();
600  }
601  else if(m_ComponentType == UINT)
602  {
604  }
605  else if(m_ComponentType == LONG)
606  {
608  }
609  else if(m_ComponentType == ULONG)
610  {
612  }
613  else if(m_ComponentType == FLOAT)
614  {
616  }
617  else if(m_ComponentType == DOUBLE)
618  {
620  }
621  else
622  {
623  itkExceptionMacro(<< "Pixel type unsupported in this file type.");
624  }
625  octBasePtr->BuildFromBuffer(buffer,xsize,ysize,zsize);
626  tree = octBasePtr->GetTree();
627 
628  octreeHdr[0] = octBasePtr->GetDepth();
629  octreeHdr[1] = octBasePtr->GetWidth();
630  octreeHdr[2] = xsize;
631  octreeHdr[3] = ysize;
632  octreeHdr[4] = zsize;
633  if(tree->IsNodeColored())
634  {
635  octreeHdr[5] = tree->GetColor();
636  }
637  else
638  {
639  octreeHdr[5] = Brains2_MASKFILE_GRAY;
640  }
642  6);
643  output.write((const char *)octreeHdr,sizeof(unsigned)*6);
644 
645  if(!tree->IsNodeColored())
646  {
647  writeOctree(tree,output);
648  }
649  output.close();
650 }
651 } // end namespace itk

Generated at Sat Feb 2 2013 23:30:29 for Orfeo Toolbox with doxygen 1.8.1.1