Orfeo Toolbox  3.16
itkMRCImageIO.cxx
Go to the documentation of this file.
1 /*=========================================================================
2 
3  Program: Insight Segmentation & Registration Toolkit
4  Module: $RCSfile: itkMRCImageIO.cxx,v $
5  Language: C++
6  Date: $Date: 2010-06-23 17:34:43 $
7  Version: $Revision: 1.6 $
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 "itkMRCImageIO.h"
19 #include "itkByteSwapper.h"
20 #include "itkIntTypes.h"
21 
22 #include "itkMetaDataObject.h"
23 #include "itkIOCommon.h"
24 #include "itkGenericUtilities.h"
25 
26 
27 #include <numeric>
28 #include <algorithm>
29 #include <fstream>
30 
31 #include <itksys/SystemTools.hxx>
32 
33 namespace itk
34 {
35 
36 
37 const char *MRCImageIO::m_MetaDataHeaderName = "MRCHeader";
38 
41 {
42  this->SetNumberOfComponents(1);
43  this->SetNumberOfDimensions(3);
44  this->SetFileTypeToBinary();
45 
46  this->AddSupportedReadExtension(".mrc");
47  this->AddSupportedReadExtension(".rec");
48 
49  this->AddSupportedWriteExtension(".mrc");
50  this->AddSupportedWriteExtension(".rec");
51 }
52 
53 void MRCImageIO::PrintSelf(std::ostream& os, Indent indent) const
54 {
55  Superclass::PrintSelf(os, indent);
56 }
57 
58 bool MRCImageIO::CanReadFile(const char* filename)
59 {
60  std::string fname = filename;
61  if ( fname != "" &&
62  ( fname.find(".mrc") < fname.length() ||
63  fname.find(".rec") < fname.length() ) )
64  {
65  return true;
66  }
67 
68  std::ifstream file;
69 
70  try
71  {
72  // this may throw an expection, but we just return false
73  this->OpenFileForReading(file, filename);
74  }
75  catch (...)
76  {
77  return false;
78  }
79 
80  itkDebugMacro( << "Reading Magic numbers " << filename );
81  char map[4];
82  char stamp[4];
83 
84  // special offset to magic number
85  file.seekg(208);
86 
87  if( !this->ReadBufferAsBinary( file, static_cast<void*>(&map), 4 ) ||
88  !this->ReadBufferAsBinary( file, static_cast<void*>(&stamp), 4 ))
89  {
90  return false;
91  }
92 
93  // check the magic number
94  if ( strncmp(map, magicMAP, 4) != 0 )
95  {
96  return false;
97  }
98 
99  return true;
100 
101 
102 }
103 
105 {
106  if ( m_MRCHeader.IsNull() )
107  {
108  itkExceptionMacro(<< "Must read info first");
109  }
110 
111  return m_MRCHeader->GetExtendedHeaderSize() + m_MRCHeader->GetHeaderSize();
112 }
113 
115  std::ifstream file;
116 
117 
118  this->InternalReadImageInformation( file );
119 
120  if ( m_MRCHeader->IsOriginalHeaderBigEndian() )
121  {
122  this->SetByteOrderToBigEndian();
123  }
124  else
125  {
127  }
128 
129  const MRCHeaderObject::Header & header = m_MRCHeader->GetHeader();
130 
131  // fixed types defined by header
132  switch ( header.mode )
133  {
135  {
136  // todo: the format is unclear weather this is signed or
137  // unsigned, it would be best to check the min and max in the
138  // header to see what makes since
139  this->SetComponentType( UCHAR );
140  this->SetNumberOfComponents( 1 );
141  this->SetPixelType( SCALAR );
142  break;
143  }
145  {
146  this->SetComponentType( SHORT );
147  this->SetNumberOfComponents( 1 );
148  this->SetPixelType( SCALAR );
149  break;
150  }
152  {
153  this->SetComponentType( FLOAT );
154  this->SetNumberOfComponents( 1 );
155  this->SetPixelType( SCALAR );
156  break;
157  }
159  {
160  // ITK does not support short complex well
161  // but if the program has gotten this far we can just write it out
162  this->SetComponentType( SHORT );
163  this->SetNumberOfComponents( 2 );
164  this->SetPixelType( COMPLEX );
165  break;
166  }
168  {
169  this->SetComponentType( FLOAT );
170  this->SetNumberOfComponents( 2 );
171  this->SetPixelType( COMPLEX );
172  break;
173  }
175  {
176  this->SetComponentType( USHORT );
177  this->SetNumberOfComponents( 1 );
178  this->SetPixelType( SCALAR );
179  break;
180  }
182  {
183  this->SetComponentType( UCHAR );
184  this->SetNumberOfComponents( 3 );
185  this->SetPixelType( RGB );
186  break;
187  }
188  default:
189  {
190  itkExceptionMacro(<< "Unrecognized mode");
191  }
192  }
193 
194 
195  if ( header.xlen == 0 &&
196  header.ylen == 0 &&
197  header.zlen == 0 )
198  {
199  // if the spacing was not set in the header then this is the
200  // default
201  m_Spacing[0] = 1.0;
202  m_Spacing[1] = 1.0;
203  m_Spacing[2] = 1.0;
204  }
205  else
206  {
207  m_Spacing[0] = header.xlen / float(header.mx);
208  m_Spacing[1] = header.ylen / float(header.my);
209  m_Spacing[2] = header.zlen / float(header.mz);
210  }
211 
212  // copy the origin
213  m_Origin[0] = header.xorg;
214  m_Origin[1] = header.yorg;
215  m_Origin[2] = header.zorg;
216 
217  // copy the size of the dimensions
218  m_Dimensions[0] = header.nx;
219  m_Dimensions[1] = header.ny;
220  m_Dimensions[2] = header.nz;
221 
222  // add the MRCHeader object into the metadata dictionary to that it
223  // can be accessed
224  MetaDataDictionary &thisDic=this->GetMetaDataDictionary();
225 
226 
227  std::string classname( this->GetNameOfClass() );
228  EncapsulateMetaData<std::string>( thisDic, ITK_InputFilterName, classname );
229  EncapsulateMetaData<MRCHeaderObject::ConstPointer>( thisDic, m_MetaDataHeaderName, MRCHeaderObject::ConstPointer(m_MRCHeader) );
230 
231 
232  return;
233 }
234 
235 // methods to load the data into the MRCHeader member variable
236 void MRCImageIO::InternalReadImageInformation(std::ifstream &file) {
237  char *buffer = 0;
238 
239  try
240  {
242 
243  itkDebugMacro(<< "Reading Information ");
244 
245  this->OpenFileForReading(file, this->m_FileName.c_str());
246 
247  buffer = new char[m_MRCHeader->GetHeaderSize()];
248  if( !this->ReadBufferAsBinary( file, static_cast<void*>(buffer), m_MRCHeader->GetHeaderSize()) )
249  {
250  itkExceptionMacro(<<"Header Read failed: Wanted "
251  << m_MRCHeader->GetHeaderSize()
252  << " bytes, but read "
253  << file.gcount() << " bytes.");
254  }
255 
256  // convert the raw buffer into the header
257  if ( !m_MRCHeader->SetHeader(reinterpret_cast<const MRCHeaderObject::Header*>(buffer) ) )
258  {
259  itkExceptionMacro(<< "Unrecognized header");
260  }
261 
262  delete [] buffer;
263  buffer = 0;
264 
265  buffer = new char[m_MRCHeader->GetExtendedHeaderSize()];
266  if( !this->ReadBufferAsBinary( file, static_cast<void*>(buffer), m_MRCHeader->GetExtendedHeaderSize()) )
267  {
268  itkExceptionMacro(<<"Extended Header Read failed.");
269  }
270 
271 
272  m_MRCHeader->SetExtendedHeader(buffer);
273  }
274  catch (...)
275  {
276  // clean up dynamic allocation
277  if (buffer)
278  delete [] buffer;
279  buffer = 0;
280  throw;
281  }
282 
283  delete [] buffer;
284 }
285 
286 
287 void MRCImageIO
288 ::Read(void *buffer)
289 {
290  std::ifstream file;
291 
292  if( this->RequestedToStream( ) )
293  {
294  // open and stream read
295  this->OpenFileForReading(file, this->m_FileName.c_str());
296 
297  this->StreamReadBufferAsBinary(file, buffer);
298 
299  }
300 
301  else
302  {
303 
304  // open the file
305  this->OpenFileForReading(file, this->m_FileName.c_str());
306 
307  // seek base the header
308  std::streampos dataPos = static_cast<std::streampos>( this->GetHeaderSize() );
309  file.seekg( dataPos, std::ios::beg );
310 
311  if ( file.fail() )
312  {
313  itkExceptionMacro(<<"Failed seeking to data position");
314  }
315 
316 
317  // read the image
318  this->ReadBufferAsBinary( file, buffer, this->GetImageSizeInBytes() );
319  }
320 
321  int size = this->GetComponentSize();
322  switch( size )
323  {
324  case 1:
325  break;
326  case 2:
327  this->GetByteOrder() == BigEndian ?
328  ByteSwapper<uint16_t>::SwapRangeFromSystemToBigEndian((uint16_t *)buffer, this->GetImageSizeInComponents() ) :
329  ByteSwapper<uint16_t>::SwapRangeFromSystemToLittleEndian((uint16_t *)buffer, this->GetImageSizeInComponents() );
330  break;
331  case 4:
332  this->GetByteOrder() == BigEndian ?
333  ByteSwapper<uint32_t>::SwapRangeFromSystemToBigEndian((uint32_t *)buffer, this->GetImageSizeInComponents() ) :
334  ByteSwapper<uint32_t>::SwapRangeFromSystemToLittleEndian((uint32_t *)buffer, this->GetImageSizeInComponents() );
335  break;
336  default:
337  itkExceptionMacro(<< "Unknown component size");
338  }
339 }
340 
341 
342 bool MRCImageIO::CanWriteFile(const char* fname)
343 {
344  std::string filename = fname;
345  if ( filename.length() > 4 &&
346  (filename.find(".mrc") == filename.length() - 4 ||
347  filename.find(".rec") == filename.length() - 4 ) )
348  {
349  return true;
350  }
351  return false;
352 }
353 
355 {
356 
358  memset(&header, 0, sizeof(MRCHeaderObject::Header));
359 
360  itkAssertOrThrowMacro ( this->GetNumberOfDimensions() != 0, "Invalid Dimension for Writting" );
361  if ( this->GetNumberOfDimensions() > 3 )
362  {
363  itkExceptionMacro(<<"MRC Writer can not write more than 3-dimensional images");
364  }
365 
366  // magic number
367  strncpy(header.cmap, magicMAP, 4);
368 
370  header.stamp[0] = 17;
371  else
372  header.stamp[0] = 68;
373 
374 
375  header.alpha = 90;
376  header.beta = 90;
377  header.gamma = 90;
378 
382 
383  header.mx = header.nx = m_Dimensions[0];
384  header.my = header.ny = ( this->GetNumberOfDimensions() >= 2 ) ? m_Dimensions[1] : 1;
385  header.mz = header.nz = ( this->GetNumberOfDimensions() >= 3 ) ? m_Dimensions[2] : 1;
386 
387  header.mode = -1;
388  if (this->GetPixelType() == SCALAR)
389  {
390  if (this->GetComponentType() == UCHAR)
391  {
393  }
394  else if (this->GetComponentType() == SHORT)
395  {
397  }
398  else if (this->GetComponentType() == FLOAT)
399  {
401  }
402  else if (this->GetComponentType() == USHORT)
403  {
405  }
406  }
407  else if (this->GetPixelType() == COMPLEX)
408  {
409  // ITK does not support short complex well
410  // but if we have gotten this far, it's done
411  if (this->GetComponentType() == SHORT)
412  {
414  }
415  else
416  if (this->GetComponentType() == FLOAT)
417  {
419  }
420  }
421  else if (this->GetPixelType() == RGB
422  && this->GetComponentType() == UCHAR)
423  {
425  }
426 
427  if (header.mode == -1)
428  {
429  itkExceptionMacro(<< "Unsupported pixel type: " << this->GetPixelTypeAsString( this->GetPixelType())
430  << " " << this->GetComponentTypeAsString(this->GetComponentType())
431  << std::endl
432  << "Supported pixel types include unsigned byte, unsigned short, signed short, float, rgb unsigned char, float complex"
433  );
434  }
435 
436  header.nxstart = 0;
437  header.nystart = 0;
438  header.nzstart = 0;
439 
440  header.xlen = m_Spacing[0]*float(header.mx);
441  header.ylen = ( this->GetNumberOfDimensions() >= 2 ) ? m_Spacing[1]*float(header.my) : 1;
442  header.zlen = ( this->GetNumberOfDimensions() >= 3 ) ? m_Spacing[2]*float(header.mz) : 1;
443 
444  header.xorg = m_Origin[0];
445  header.yorg = ( this->GetNumberOfDimensions() >= 2 ) ? m_Origin[1] : 0;
446  header.zorg = ( this->GetNumberOfDimensions() >= 3 ) ? m_Origin[2] : 0;
447 
448  // the SetHeader method is used to set the all the internal variable
449  // of the header object correctly, and the data is verified
451  if (!m_MRCHeader->SetHeader(&header))
452  {
453  itkExceptionMacro(<< "Unexpected error setting header");
454  }
455 }
456 
457 void MRCImageIO::WriteImageInformation( const void * buffer )
458 {
459 
460  this->UpdateHeaderFromImageIO();
461 
462  this->UpdateHeaderWithMinMaxMean( buffer );
463 
464  std::ofstream file;
465 
466  this->OpenFileForWriting(file, this->m_FileName.c_str(), true);
467 
468  // write the header
469  const MRCHeaderObject::Header & header = m_MRCHeader->GetHeader();
470  file.write(static_cast<const char*>((void*)&(header)), 1024);
471 }
472 
473 void MRCImageIO
474 ::UpdateHeaderWithMinMaxMean( const void * bufferBegin )
475 {
476 
477  // fixed types defined by header
478  const MRCHeaderObject::Header & header = m_MRCHeader->GetHeader();
479 
480  switch ( header.mode )
481  {
482  case 0:
483  {
484  // scalar unsigned char
485  this->UpdateHeaderWithMinMaxMean(static_cast<const unsigned char*>(bufferBegin));
486  break;
487  }
488  case 1:
489  {
490  // scalar short
491  this->UpdateHeaderWithMinMaxMean(static_cast<const short*>(bufferBegin));
492  break;
493  }
494  case 2:
495  {
496  // scalar float
497  this->UpdateHeaderWithMinMaxMean(static_cast<const float*>(bufferBegin));
498  break;
499  }
500  case 3:
501  {
502  // complex short
503 
504  // What is the best way to map complex to float?
505  // just set resonable values
506  m_MRCHeader->m_Header.amin = -1.0f;
507  m_MRCHeader->m_Header.amax = 1.0f;
508  m_MRCHeader->m_Header.amean = 0.0f;
509  break;
510  }
511  case 4:
512  {
513  // complex float
514 
515  // What is the best way to map complex to float?
516  // just set resonable values
517  m_MRCHeader->m_Header.amin = -1.0f;
518  m_MRCHeader->m_Header.amax = 1.0f;
519  m_MRCHeader->m_Header.amean = 0.0f;
520  break;
521  }
522  case 6:
523  {
524  // scalar unsigned short
525  this->UpdateHeaderWithMinMaxMean(static_cast<const unsigned short*>(bufferBegin));
526  break;
527  }
528  case 16:
529  {
530  // RGB of unsigned char
531 
532  // just set resonable values
533  m_MRCHeader->m_Header.amin = 0.0f;
534  m_MRCHeader->m_Header.amax = 255.0f;
535  m_MRCHeader->m_Header.amean = 127.5f;
536  break;
537  }
538  default:
539  {
540  itkExceptionMacro(<< "Unrecognized mode");
541  }
542  }
543 }
544 
545 void MRCImageIO
546 ::Write(const void* buffer)
547 {
548 
549  if( this->RequestedToStream() )
550  {
551 
552 
553  // we assume that GetActualNumberOfSplitsForWriting is called before
554  // this methods and it will remove the file if a new header needs to
555  // be written
556  if (!itksys::SystemTools::FileExists( m_FileName.c_str() ))
557  {
558  this->WriteImageInformation( buffer );
559 
560 
561  std::ofstream file;
562  // open and stream write
563  this->OpenFileForWriting(file, this->m_FileName.c_str(), false);
564 
565  // write one byte at the end of the file to allocate (this is a
566  // nifty trick which should not write the entire size of the file
567  // just allocate it, if the system supports sparse files)
568  std::streampos seekPos = this->GetImageSizeInBytes() + this->GetHeaderSize() - 1;
569  file.seekp( seekPos, std::ios::cur );
570  file.write("\0", 1);
571  file.seekp( 0 );
572 
573  }
574  else
575  {
576 
577  if ( m_MRCHeader.IsNull() )
578  {
579  // need to determin the size of the header in the file by
580  // reading the header into m_MRCHeader
581 
582  // we assume that GetActualNumberOfSplitsForWriting is called
583  // to verify that the header and file is compatible with the
584  // region to be written, but this call will still overwrite
585  // the internal m_MRCHeader variable
586 
587  std::ifstream file;
588  this->InternalReadImageInformation( file );
589 
590  }
591 
592  // TODO:
593  // how are we suppose to update min, max mean? we could be
594  // overwriting data, and we don't know what's already there if
595  // we were pasting
596  }
597 
598  std::ofstream file;
599  // open and stream write
600  this->OpenFileForWriting(file, this->m_FileName.c_str(), false);
601 
602  this->StreamWriteBufferAsBinary(file, buffer);
603 
604  }
605 
606  else
607  {
608 
609  // this will truncate file and write header
610  this->WriteImageInformation( buffer );
611 
612  std::ofstream file;
613  // open the file
614  this->OpenFileForWriting(file, this->m_FileName.c_str(), false);
615 
616  // seek pass the header
617  std::streampos dataPos = static_cast<std::streampos>( this->GetHeaderSize() );
618  file.seekp( dataPos, std::ios::beg );
619 
620  if ( file.fail() )
621  {
622  itkExceptionMacro(<<"Failed seeking to data position");
623  }
624 
625  // read the image
626  if (!this->WriteBufferAsBinary( file, buffer, this->GetImageSizeInBytes() ))
627  {
628  itkExceptionMacro(<< "Could not write file: " << m_FileName);
629  }
630  }
631 
632 }
633 
634 } // namespace itk

Generated at Sat May 18 2013 23:54:42 for Orfeo Toolbox with doxygen 1.8.3.1