Orfeo Toolbox  4.0
itkStreamingImageIOBase.cxx
Go to the documentation of this file.
1 /*=========================================================================
2  *
3  * Copyright Insight Software Consortium
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  *=========================================================================*/
19 
20 #include "itksys/SystemTools.hxx"
21 
22 namespace itk
23 {
25  ImageIOBase()
26 {}
27 
28 void StreamingImageIOBase::PrintSelf(std::ostream & os, Indent indent) const
29 {
30  Superclass::PrintSelf(os, indent);
31 }
32 
34 ::StreamReadBufferAsBinary(std::istream & file, void *_buffer)
35 {
36  itkDebugMacro(<< "StreamingReadBufferAsBinary called");
37 
38  char *buffer = static_cast< char * >( _buffer );
39  // Offset into file
40  std::streampos dataPos = this->GetDataPosition();
41 
42  itkDebugStatement(
43  const std::streamsize sizeOfRegion = static_cast< std::streamsize >( m_IORegion.GetNumberOfPixels() )
44  * this->GetPixelSize();
45  );
46 
47  // compute the number of continuous bytes to be read
48  std::streamsize sizeOfChunk = 1;
49  unsigned int movingDirection = 0;
50  do
51  {
52  sizeOfChunk *= m_IORegion.GetSize(movingDirection);
53  ++movingDirection;
54  }
55  while ( movingDirection < m_IORegion.GetImageDimension()
56  && m_IORegion.GetSize(movingDirection - 1) == this->GetDimensions(movingDirection - 1) );
57  sizeOfChunk *= this->GetPixelSize();
58 
59  ImageIORegion::IndexType currentIndex = m_IORegion.GetIndex();
60 
61  while ( m_IORegion.IsInside(currentIndex) )
62  {
63  // calculate the position to seek to in the file
64  std::streampos seekPos = 0;
65  SizeValueType subDimensionQuantity = 1;
66  for ( unsigned int i = 0; i < m_IORegion.GetImageDimension(); ++i )
67  {
68  seekPos = seekPos + static_cast< std::streamoff >( subDimensionQuantity
69  * this->GetPixelSize()
70  * currentIndex[i] );
71  subDimensionQuantity *= this->GetDimensions(i);
72  }
73 
74  itkDebugMacro(
75  << "Reading " << sizeOfChunk << " of " << sizeOfRegion << " bytes for " << m_FileName << " at " << dataPos
76  + seekPos << " position in file");
77 
78  file.seekg(dataPos + seekPos, std::ios::beg);
79 
80  if ( ! this->ReadBufferAsBinary(file, buffer, sizeOfChunk) )
81  {
82  itkExceptionMacro( "Error reading in ReadBufferAsBinary!" );
83  }
84 
85  // increment the buffer pointer
86  buffer += sizeOfChunk;
87 
88  if ( file.fail() )
89  {
90  itkExceptionMacro(<< "Fail reading");
91  }
92 
93  if ( movingDirection == m_IORegion.GetImageDimension() )
94  {
95  break;
96  }
97 
98  // increment index to next chunk
99  ++currentIndex[movingDirection];
100  for ( unsigned int i = movingDirection; i < m_IORegion.GetImageDimension() - 1; ++i )
101  {
102  // when reaching the end of the moving index dimension carry to
103  // higher dimensions
104  if ( static_cast< ImageIORegion::SizeValueType >( currentIndex[i] - m_IORegion.GetIndex(i) ) >=
105  m_IORegion.GetSize(i) )
106  {
107  currentIndex[i] = m_IORegion.GetIndex(i);
108  ++currentIndex[i + 1];
109  }
110  }
111  }
112 
113  return true;
114 }
115 
117 {
118  // some systems have a limit of 2GB to be read at once
119  const SizeType maxChunk = 1024 * 1024 * 1024;
120 
121  std::streamsize bytesRemaining = static_cast< std::streamsize >( num );
122 
123  while ( bytesRemaining )
124  {
125  std::streamsize bytesToRead = bytesRemaining > maxChunk ? maxChunk : bytesRemaining;
126 
127  itkDebugMacro(<< "Reading " << bytesToRead << " of " << bytesRemaining << " bytes for " << m_FileName);
128 
129  is.read(static_cast< char * >( buffer ), bytesToRead);
130 
131  if ( ( is.gcount() != bytesToRead ) || is.fail() )
132  {
133  return false;
134  }
135  buffer = static_cast< char * >( buffer ) + bytesToRead;
136  bytesRemaining -= bytesToRead;
137  }
138 
139  return true;
140 }
141 
143  const void *buffer,
145 {
146  // some systems have a limit of 2GB to be written at once
147  const SizeType maxChunk = 1024 * 1024 * 1024;
148 
149  std::streamsize bytesRemaining = num;
150 
151  while ( bytesRemaining )
152  {
153  SizeType bytesToWrite = bytesRemaining > maxChunk ? maxChunk : bytesRemaining;
154 
155  itkDebugMacro(<< "Writing " << bytesToWrite << " of " << bytesRemaining << " bytes for " << m_FileName);
156 
157  os.write(static_cast< const char * >( buffer ), bytesToWrite);
158  if ( os.fail() )
159  {
160  return false;
161  }
162 
163  buffer = static_cast< const char * >( buffer ) + bytesToWrite;
164  bytesRemaining -= bytesToWrite;
165  }
166 
167  return true;
168 }
169 
170 bool StreamingImageIOBase::StreamWriteBufferAsBinary(std::ostream & file, const void *_buffer)
171 {
172  itkDebugMacro(<< "StreamingWriteBufferAsBinary called");
173 
174  const char *buffer = static_cast< const char * >( _buffer );
175  // Offset into file
176  std::streampos dataPos = this->GetDataPosition();
177 
178  // compute the number of continuous bytes to be written
179  std::streamsize sizeOfChunk = 1;
180  unsigned int movingDirection = 0;
181  do
182  {
183  sizeOfChunk *= m_IORegion.GetSize(movingDirection);
184  ++movingDirection;
185  }
186  while ( movingDirection < m_IORegion.GetImageDimension()
187  && m_IORegion.GetSize(movingDirection - 1) == this->GetDimensions(movingDirection - 1) );
188  sizeOfChunk *= this->GetPixelSize();
189 
191  while ( m_IORegion.IsInside(currentIndex) )
192  {
193  // calculate the position to seek to in the file
194  std::streampos seekPos = 0;
195  SizeValueType subDimensionQuantity = 1;
196  for ( unsigned int i = 0; i < m_IORegion.GetImageDimension(); ++i )
197  {
198  seekPos = seekPos + static_cast< std::streamoff >( subDimensionQuantity
199  * this->GetPixelSize()
200  * currentIndex[i] );
201  subDimensionQuantity *= this->GetDimensions(i);
202  }
203 
204  file.seekp(dataPos + seekPos, std::ios::beg);
205  if ( !this->WriteBufferAsBinary(file, buffer, sizeOfChunk) )
206  {
207  itkExceptionMacro( "Error reading in WriteBufferAsBinary!" );
208  }
209 
210  // increment the buffer pointer
211  buffer += sizeOfChunk;
212 
213  itkDebugMacro(
214  << "Writing " << sizeOfChunk << " of " << " ?? bytes for " << m_FileName << " at " << dataPos + seekPos
215  << " position in file");
216 
217  if ( file.fail() )
218  {
219  itkExceptionMacro(<< "Fail writing");
220  }
221 
222  if ( movingDirection == m_IORegion.GetImageDimension() )
223  {
224  break;
225  }
226 
227  // increment index to next chunk
228  ++currentIndex[movingDirection];
229  for ( unsigned int i = movingDirection; i < m_IORegion.GetImageDimension() - 1; ++i )
230  {
231  // when reaching the end of the movingDirection dimension carry to
232  // higher dimensions
233  if ( static_cast< ImageIORegion::SizeValueType >( currentIndex[i] - m_IORegion.GetIndex(i) )
234  >= m_IORegion.GetSize(i) )
235  {
236  currentIndex[i] = m_IORegion.GetIndex(i);
237  ++currentIndex[i + 1];
238  }
239  }
240  }
241 
242  return true;
243 }
244 
245 void StreamingImageIOBase::OpenFileForReading(std::ifstream & os, const char *filename)
246 {
247  // Make sure that we have a file to
248  if ( *filename == 0 )
249  {
250  itkExceptionMacro(<< "A FileName must be specified.");
251  }
252 
253  // Close file from any previous image
254  if ( os.is_open() )
255  {
256  os.close();
257  }
258 
259  // Open the new file for reading
260  itkDebugMacro(<< "Initialize: opening file " << filename);
261 
262  os.open(filename, std::ios::in | std::ios::binary);
263  if ( os.fail() )
264  {
265  itkExceptionMacro(<< "Could not open file for reading: " << filename);
266  }
267 }
268 
269 void StreamingImageIOBase::OpenFileForWriting(std::ofstream & os, const char *filename, bool truncate)
270 {
271  // Make sure that we have a file to
272  if ( *filename == 0 )
273  {
274  itkExceptionMacro(<< "A FileName must be specified.");
275  }
276 
277  // Close file from any previous image
278  if ( os.is_open() )
279  {
280  os.close();
281  }
282 
283  // Open the new file for writing
284  itkDebugMacro(<< "Initialize: opening file " << filename);
285 
286  if ( truncate )
287  {
288  // truncate
289  os.open(m_FileName.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
290  }
291  else
292  {
293  os.open(m_FileName.c_str(), std::ios::out | std::ios::binary | std::ios::in);
294  }
295 
296  if ( os.fail() )
297  {
298  itkExceptionMacro(<< "Could not open file for writing: " << filename);
299  }
300 }
301 
303 {
304  return true;
305 }
306 
308 {
309  return true;
310 }
311 
312 unsigned int
313 StreamingImageIOBase::GetActualNumberOfSplitsForWriting(unsigned int numberOfRequestedSplits,
314  const ImageIORegion & pasteRegion,
315  const ImageIORegion & largestPossibleRegion)
316 {
317  if ( !const_cast<StreamingImageIOBase*>(this)->CanStreamWrite() )
318  {
319  // ImageIOs may not always be able to stream,
320  // fall back to super classses non-streaming implementation
321  return ImageIOBase::GetActualNumberOfSplitsForWriting( numberOfRequestedSplits,
322  pasteRegion,
323  largestPossibleRegion );
324  }
325  else if ( !itksys::SystemTools::FileExists( m_FileName.c_str() ) )
326  {
327  // file doesn't exits so we don't have potential problems
328  }
329  else if ( pasteRegion != largestPossibleRegion )
330  {
331  // we are going to be pasting (may be streaming too)
332 
333  // need to check to see if the file is compatible
334  std::string errorMessage;
335  Pointer headerImageIOReader = dynamic_cast< StreamingImageIOBase * >( this->CreateAnother().GetPointer() );
336 
337  try
338  {
339  headerImageIOReader->SetFileName( m_FileName.c_str() );
340  headerImageIOReader->ReadImageInformation();
341  }
342  catch ( ... )
343  {
344  errorMessage = "Unable to read information from file: " + m_FileName;
345  }
346 
347  // we now need to check that the following match:
348  // 2)pixel type
349  // 3)dimensions
350  // 4)size/origin/spacing
351  // 5)direction cosines
352  //
353  // todo check for byte order
354 
355  if ( errorMessage.size() )
356  {
357  // 0) Can't read file
358  }
359  // 2)pixel type
360  // this->GetPixelType() is not verified because the metaio file format
361  // stores all multi-component types as arrays, so it does not
362  // distinguish between pixel types. Also as long as the compoent
363  // and number of compoents match we should be able to paste, that
364  // is the numbers should be the same it is just the interpretation
365  // that is not matching
366  else if ( headerImageIOReader->GetNumberOfComponents() != this->GetNumberOfComponents()
367  || headerImageIOReader->GetComponentType() != this->GetComponentType() )
368  {
369  errorMessage = "Component type does not match in file: " + m_FileName;
370  }
371  // 3)dimensions/size
372  else if ( headerImageIOReader->GetNumberOfDimensions() != this->GetNumberOfDimensions() )
373  {
374  errorMessage = "Dimensions does not match in file: " + m_FileName;
375  }
376  else
377  {
378  for ( unsigned int i = 0; i < this->GetNumberOfDimensions(); ++i )
379  {
380  // 4)size/origin/spacing
381  if ( headerImageIOReader->GetDimensions(i) != this->GetDimensions(i)
382  || headerImageIOReader->GetSpacing(i) != this->GetSpacing(i)
383  || headerImageIOReader->GetOrigin(i) != this->GetOrigin(i) )
384  {
385  errorMessage = "Size, spacing or origin does not match in file: " + m_FileName;
386  break;
387  }
388  // 5)direction cosines
389  if ( headerImageIOReader->GetDirection(i) != this->GetDirection(i) )
390  {
391  errorMessage = "Direction cosines does not match in file: " + m_FileName;
392  break;
393  }
394  }
395  }
396 
397  if ( errorMessage.size() )
398  {
399  itkExceptionMacro("Unable to paste because pasting file exists and is different. " << errorMessage);
400  }
401  else if ( headerImageIOReader->GetPixelType() != this->GetPixelType() )
402  {
403  // since there is currently poor support for pixel types in
404  // MetaIO we will just warn when it does not match
405  itkWarningMacro("Pixel types does not match file, but component type and number of components do.");
406  }
407  }
408  else if ( numberOfRequestedSplits != 1 )
409  {
410  // we are going be streaming
411 
412  // need to remove the file incase the file doesn't match our
413  // current header/meta data information
414  if ( !itksys::SystemTools::RemoveFile( m_FileName.c_str() ) )
415  {
416  itkExceptionMacro("Unable to remove file for streaming: " << m_FileName);
417  }
418  }
419 
420  return GetActualNumberOfSplitsForWritingCanStreamWrite(numberOfRequestedSplits, pasteRegion);
421 }
422 
424  const ImageIORegion & requestedRegion) const
425 {
426  // This implementation returns the requestedRegion if
427  // streaming is enabled and we are capable
428 
429  ImageIORegion streamableRegion(this->m_NumberOfDimensions);
430 
431  if ( !m_UseStreamedReading || !const_cast<StreamingImageIOBase*>(this)->CanStreamRead() )
432  {
434  }
435  else
436  {
437  streamableRegion = requestedRegion;
438  }
439 
440  return streamableRegion;
441 }
442 
444 {
445  // we choose the max dimension and then pad the smaller with ones
446  //
447  // This enables a 2D request from a 3D volume to get the first slice,
448  // and a 4D with a 1-sized 4th dimension to equal the 3D volume
449  // as well.
450  unsigned int maxNumberOfDimension = vnl_math_max( this->GetNumberOfDimensions(),
451  this->GetIORegion().GetImageDimension() );
452 
453  ImageIORegion ioregion(maxNumberOfDimension);
454  ImageIORegion largestRegion(maxNumberOfDimension);
455 
456  for ( unsigned int i = 0; i < maxNumberOfDimension; i++ )
457  {
458  largestRegion.SetIndex(i, 0);
459  if ( i < this->GetNumberOfDimensions() )
460  {
461  largestRegion.SetSize( i, this->GetDimensions(i) );
462  }
463  else
464  {
465  largestRegion.SetSize(i, 1);
466  }
467 
468  if ( i < this->GetIORegion().GetImageDimension() )
469  {
470  ioregion.SetIndex( i, this->GetIORegion().GetIndex(i) );
471  ioregion.SetSize( i, this->GetIORegion().GetSize(i) );
472  }
473  else
474  {
475  ioregion.SetIndex(i, 0);
476  ioregion.SetSize(i, 1);
477  }
478  }
479 
480  return ( largestRegion != ioregion );
481 }
482 } // namespace itk

Generated at Sat Mar 8 2014 15:36:27 for Orfeo Toolbox with doxygen 1.8.3.1