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 *=========================================================================*/
18
19 #include "itkImageIOBase.h"
20 #include "itkImageRegionSplitterSlowDimension.h"
21 #include <mutex>
22 #include "itksys/SystemTools.hxx"
23 #include "itkPrintHelper.h"
24
25
26 namespace itk
27 {
ImageIOBase()28 ImageIOBase::ImageIOBase()
29
30 {
31 Reset(false);
32 }
33
Reset(const bool)34 void ImageIOBase::Reset(const bool)
35 {
36 m_Initialized = false;
37 m_FileName = "";
38 m_NumberOfComponents = 1;
39 for ( unsigned int i = 0; i < m_NumberOfDimensions; i++ )
40 {
41 m_Dimensions[i] = 0;
42 m_Strides[i] = 0;
43 }
44 m_NumberOfDimensions = 0;
45
46 m_UseStreamedReading = false;
47 m_UseStreamedWriting = false;
48 m_ExpandRGBPalette = true;
49 m_IsReadAsScalarPlusPalette = false;
50
51 }
52
53 ImageIOBase::~ImageIOBase() = default;
54
55 const ImageIOBase::ArrayOfExtensionsType &
GetSupportedWriteExtensions() const56 ImageIOBase::GetSupportedWriteExtensions() const
57 {
58 return this->m_SupportedWriteExtensions;
59 }
60
61 const ImageIOBase::ArrayOfExtensionsType &
GetSupportedReadExtensions() const62 ImageIOBase::GetSupportedReadExtensions() const
63 {
64 return this->m_SupportedReadExtensions;
65 }
66
AddSupportedReadExtension(const char * extension)67 void ImageIOBase::AddSupportedReadExtension(const char *extension)
68 {
69 this->m_SupportedReadExtensions.push_back(extension);
70 }
71
AddSupportedWriteExtension(const char * extension)72 void ImageIOBase::AddSupportedWriteExtension(const char *extension)
73 {
74 this->m_SupportedWriteExtensions.push_back(extension);
75 }
76
Resize(const unsigned int numDimensions,const unsigned int * dimensions)77 void ImageIOBase::Resize(const unsigned int numDimensions,
78 const unsigned int *dimensions)
79 {
80 m_NumberOfDimensions = numDimensions;
81 if ( dimensions != nullptr )
82 {
83 for ( unsigned int i = 0; i < m_NumberOfDimensions; i++ )
84 {
85 m_Dimensions[i] = dimensions[i];
86 }
87 ComputeStrides();
88 }
89 }
90
SetDimensions(unsigned int i,SizeValueType dim)91 void ImageIOBase::SetDimensions(unsigned int i, SizeValueType dim)
92 {
93 if ( i >= m_Dimensions.size() )
94 {
95 itkWarningMacro( "Index: " << i
96 << " is out of bounds, expected maximum is "
97 << m_Dimensions.size() );
98 itkExceptionMacro( "Index: " << i
99 << " is out of bounds, expected maximum is "
100 << m_Dimensions.size() );
101 }
102 this->Modified();
103 m_Dimensions[i] = dim;
104 }
105
SetOrigin(unsigned int i,double origin)106 void ImageIOBase::SetOrigin(unsigned int i, double origin)
107 {
108 if ( i >= m_Origin.size() )
109 {
110 itkWarningMacro( "Index: " << i
111 << " is out of bounds, expected maximum is "
112 << m_Origin.size() );
113 itkExceptionMacro( "Index: " << i
114 << " is out of bounds, expected maximum is "
115 << m_Origin.size() );
116 }
117 this->Modified();
118 m_Origin[i] = origin;
119 }
120
SetSpacing(unsigned int i,double spacing)121 void ImageIOBase::SetSpacing(unsigned int i, double spacing)
122 {
123 if ( i >= m_Spacing.size() )
124 {
125 itkWarningMacro( "Index: " << i
126 << " is out of bounds, expected maximum is "
127 << m_Spacing.size() );
128 itkExceptionMacro( "Index: " << i
129 << " is out of bounds, expected maximum is "
130 << m_Spacing.size() );
131 }
132 this->Modified();
133 m_Spacing[i] = spacing;
134 }
135
SetDirection(unsigned int i,const std::vector<double> & direction)136 void ImageIOBase::SetDirection(unsigned int i, const std::vector< double > & direction)
137 {
138 if ( i >= m_Direction.size() )
139 {
140 itkWarningMacro( "Index: " << i
141 << " is out of bounds, expected maximum is "
142 << m_Direction.size() );
143 itkExceptionMacro( "Index: " << i
144 << " is out of bounds, expected maximum is "
145 << m_Direction.size() );
146 }
147 this->Modified();
148 m_Direction[i] = direction;
149 }
150
SetDirection(unsigned int i,const vnl_vector<double> & direction)151 void ImageIOBase::SetDirection(unsigned int i, const vnl_vector< double > & direction)
152 {
153 if ( i >= m_Direction.size() )
154 {
155 itkWarningMacro( "Index: " << i
156 << " is out of bounds, expected maximum is "
157 << m_Direction.size() );
158 itkExceptionMacro( "Index: " << i
159 << " is out of bounds, expected maximum is "
160 << m_Direction.size() );
161 }
162 this->Modified();
163 std::vector< double > v;
164 v.resize( m_Direction.size() );
165 // Note: direction.size() <= v.size().
166 for ( unsigned int j = 0; j < direction.size(); j++ )
167 {
168 v[j] = direction[j];
169 }
170 m_Direction[i] = v;
171 }
172
GetComponentTypeInfo() const173 const std::type_info & ImageIOBase::GetComponentTypeInfo() const
174 {
175 switch ( m_ComponentType )
176 {
177 case UCHAR:
178 return typeid( unsigned char );
179 case CHAR:
180 return typeid( char );
181 case USHORT:
182 return typeid( unsigned short );
183 case SHORT:
184 return typeid( short );
185 case UINT:
186 return typeid( unsigned int );
187 case INT:
188 return typeid( int );
189 case ULONG:
190 return typeid( unsigned long );
191 case LONG:
192 return typeid( long );
193 case ULONGLONG:
194 return typeid( unsigned long long );
195 case LONGLONG:
196 return typeid( long long);
197 case FLOAT:
198 return typeid( float );
199 case DOUBLE:
200 return typeid( double );
201 case UNKNOWNCOMPONENTTYPE:
202 default:
203 itkExceptionMacro ("Unknown component type: " << m_ComponentType);
204 }
205 }
206
ComputeStrides()207 void ImageIOBase::ComputeStrides()
208 {
209 m_Strides[0] = this->GetComponentSize();
210 m_Strides[1] = m_NumberOfComponents * m_Strides[0];
211 for ( unsigned int i = 2; i <= ( m_NumberOfDimensions + 1 ); i++ )
212 {
213 m_Strides[i] = static_cast<SizeType>(m_Dimensions[i - 2]) * m_Strides[i - 1];
214 }
215 }
216
217 // Calculates the image size in PIXELS
218 ImageIOBase::SizeType
219 ImageIOBase
GetImageSizeInPixels() const220 ::GetImageSizeInPixels() const
221 {
222 unsigned int i;
223 SizeType numPixels = 1;
224
225 for ( i = 0; i < m_NumberOfDimensions; i++ )
226 {
227 numPixels *= m_Dimensions[i];
228 }
229
230 return numPixels;
231 }
232
233 ImageIOBase::SizeType
234 ImageIOBase
GetImageSizeInComponents() const235 ::GetImageSizeInComponents() const
236 {
237 return ( this->GetImageSizeInPixels() * m_NumberOfComponents );
238 }
239
240 ImageIOBase::SizeType
241 ImageIOBase
GetImageSizeInBytes() const242 ::GetImageSizeInBytes() const
243 {
244 return ( this->GetImageSizeInComponents() * this->GetComponentSize() );
245 }
246
247 ImageIOBase::SizeType
248 ImageIOBase
GetComponentStride() const249 ::GetComponentStride() const
250 {
251 return m_Strides[0];
252 }
253
254 ImageIOBase::SizeType
255 ImageIOBase
GetPixelStride() const256 ::GetPixelStride() const
257 {
258 return m_Strides[1];
259 }
260
261 ImageIOBase::SizeType
262 ImageIOBase
GetRowStride() const263 ::GetRowStride() const
264 {
265 return m_Strides[2];
266 }
267
268 ImageIOBase::SizeType
269 ImageIOBase
GetSliceStride() const270 ::GetSliceStride() const
271 {
272 return m_Strides[3];
273 }
274
SetNumberOfDimensions(unsigned int dim)275 void ImageIOBase::SetNumberOfDimensions(unsigned int dim)
276 {
277 if ( dim != m_NumberOfDimensions )
278 {
279 m_Origin.resize(dim);
280 m_Spacing.resize(dim);
281 m_Direction.resize(dim);
282 m_Strides.resize(dim + 2);
283 m_NumberOfDimensions = dim;
284 m_Dimensions.resize(dim);
285 m_Direction.resize(dim);
286 std::vector< double > axis(dim);
287 for ( unsigned int i = 0; i < dim; i++ )
288 {
289 for ( unsigned int j = 0; j < dim; j++ )
290 {
291 if ( i == j )
292 {
293 axis[j] = 1.0;
294 }
295 else
296 {
297 axis[j] = 0.0;
298 }
299 }
300 this->SetDirection(i, axis);
301 this->SetOrigin(i, 0.0);
302 this->SetSpacing(i, 1.0);
303 }
304 this->Modified();
305 }
306 }
307
308 bool
309 ImageIOBase
ReadBufferAsBinary(std::istream & is,void * buffer,ImageIOBase::SizeType num)310 ::ReadBufferAsBinary(std::istream & is, void *buffer, ImageIOBase::SizeType num)
311 {
312 const auto numberOfBytesToBeRead = Math::CastWithRangeCheck< std::streamsize >(num);
313
314 is.read(static_cast< char * >( buffer ), numberOfBytesToBeRead);
315
316 const std::streamsize numberOfBytesRead = is.gcount();
317
318 #ifdef __APPLE_CC__
319 // fail() is broken in the Mac. It returns true when reaches eof().
320 if ( numberOfBytesRead != numberOfBytesToBeRead )
321 #else
322 if ( ( numberOfBytesRead != numberOfBytesToBeRead ) || is.fail() )
323 #endif
324 {
325 return false; // read failed
326 }
327
328 return true;
329 }
330
GetPixelSize() const331 unsigned int ImageIOBase::GetPixelSize() const
332 {
333 if ( m_ComponentType == UNKNOWNCOMPONENTTYPE
334 || m_PixelType == UNKNOWNPIXELTYPE )
335 {
336 itkExceptionMacro ("Unknown pixel or component type: ("
337 << m_PixelType << ", " << m_ComponentType << ")");
338 }
339
340 return this->GetComponentSize() * this->GetNumberOfComponents();
341 }
342
343
SetCompressor(std::string _c)344 void ImageIOBase::SetCompressor( std::string _c )
345 {
346 if ( this->m_Compressor != _c )
347 {
348 this->m_Compressor = _c;
349 this->Modified();
350
351 std::transform(_c.begin(), _c.end(), _c.begin(), ::toupper);
352 this->InternalSetCompressor(_c);
353 }
354 }
355
SetMaximumCompressionLevel(int _MaximumCompressionLevel)356 void ImageIOBase::SetMaximumCompressionLevel( int _MaximumCompressionLevel )
357 {
358 this->m_MaximumCompressionLevel = _MaximumCompressionLevel;
359 this->SetCompressionLevel( this->GetCompressionLevel() );
360 }
361
InternalSetCompressor(const std::string & _compressor)362 void ImageIOBase::InternalSetCompressor(const std::string &_compressor)
363 {
364 if (_compressor != "")
365 {
366 itkWarningMacro("Unknown compressor: \"" << _compressor << "\", setting to default.");
367 this->SetCompressor("");
368 }
369 }
370
GetComponentSize() const371 unsigned int ImageIOBase::GetComponentSize() const
372 {
373 switch ( m_ComponentType )
374 {
375 case UCHAR:
376 return sizeof( unsigned char );
377 case CHAR:
378 return sizeof( char );
379 case USHORT:
380 return sizeof( unsigned short );
381 case SHORT:
382 return sizeof( short );
383 case UINT:
384 return sizeof( unsigned int );
385 case INT:
386 return sizeof( int );
387 case ULONG:
388 return sizeof( unsigned long );
389 case LONG:
390 return sizeof( long );
391 case ULONGLONG:
392 return sizeof( unsigned long long );
393 case LONGLONG:
394 return sizeof( long long );
395 case FLOAT:
396 return sizeof( float );
397 case DOUBLE:
398 return sizeof( double );
399 case UNKNOWNCOMPONENTTYPE:
400 default:
401 itkExceptionMacro ("Unknown component type: " << m_ComponentType);
402 }
403 }
404
GetFileTypeAsString(FileType t) const405 std::string ImageIOBase::GetFileTypeAsString(FileType t) const
406 {
407 switch ( t )
408 {
409 case ASCII:
410 return std::string( "ASCII");
411 case Binary:
412 return std::string( "Binary");
413 case TypeNotApplicable:
414 default:
415 return std::string( "TypeNotApplicable");
416 }
417 //Not reachable return s = "TypeNotApplicable";
418 }
419
GetByteOrderAsString(ByteOrder t) const420 std::string ImageIOBase::GetByteOrderAsString(ByteOrder t) const
421 {
422 switch ( t )
423 {
424 case BigEndian:
425 return std::string( "BigEndian");
426 case LittleEndian:
427 return std::string( "LittleEndian");
428 case OrderNotApplicable:
429 default:
430 return std::string( "OrderNotApplicable");
431 }
432 }
433
GetComponentTypeAsString(IOComponentType t)434 std::string ImageIOBase::GetComponentTypeAsString(IOComponentType t)
435 {
436 switch ( t )
437 {
438 case UCHAR:
439 return std::string( "unsigned_char" );
440 case CHAR:
441 return std::string( "char" );
442 case USHORT:
443 return std::string( "unsigned_short" );
444 case SHORT:
445 return std::string( "short" );
446 case UINT:
447 return std::string( "unsigned_int" );
448 case INT:
449 return std::string( "int" );
450 case ULONG:
451 return std::string( "unsigned_long" );
452 case LONG:
453 return std::string( "long" );
454 case ULONGLONG:
455 return std::string( "unsigned_long_long" );
456 case LONGLONG:
457 return std::string( "long_long" );
458 case FLOAT:
459 return std::string( "float" );
460 case DOUBLE:
461 return std::string( "double" );
462 case UNKNOWNCOMPONENTTYPE:
463 return std::string( "unknown" );
464 default:
465 return std::string( "unknown" );
466 }
467 }
468
GetComponentTypeFromString(const std::string & typeString)469 ImageIOBase::IOComponentType ImageIOBase::GetComponentTypeFromString(const std::string &typeString)
470 {
471 if(typeString.compare("unsigned_char") == 0)
472 {
473 return UCHAR;
474 }
475 else if(typeString.compare("char") == 0)
476 {
477 return CHAR;
478 }
479 else if(typeString.compare("unsigned_short") == 0)
480 {
481 return USHORT;
482 }
483 else if(typeString.compare("short") == 0)
484 {
485 return SHORT;
486 }
487 else if(typeString.compare("unsigned_int") == 0)
488 {
489 return UINT;
490 }
491 else if(typeString.compare("int") == 0)
492 {
493 return INT;
494 }
495 else if(typeString.compare("unsigned_long") == 0)
496 {
497 return ULONG;
498 }
499 else if(typeString.compare("long") == 0)
500 {
501 return LONG;
502 }
503 else if(typeString.compare("unsigned_long_long") == 0)
504 {
505 return ULONGLONG;
506 }
507 else if(typeString.compare("long_long") == 0)
508 {
509 return LONGLONG;
510 }
511 else if(typeString.compare("float") == 0)
512 {
513 return FLOAT;
514 }
515 else if(typeString.compare("double") == 0)
516 {
517 return DOUBLE;
518 }
519 else
520 {
521 return UNKNOWNCOMPONENTTYPE;
522 }
523 }
524
GetPixelTypeAsString(IOPixelType t)525 std::string ImageIOBase::GetPixelTypeAsString(IOPixelType t)
526 {
527 switch ( t )
528 {
529 case SCALAR:
530 return std::string( "scalar" );
531 case VECTOR:
532 return std::string( "vector" );
533 case COVARIANTVECTOR:
534 return std::string( "covariant_vector" );
535 case POINT:
536 return std::string( "point" );
537 case OFFSET:
538 return std::string( "offset" );
539 case RGB:
540 return std::string( "rgb" );
541 case RGBA:
542 return std::string( "rgba" );
543 case SYMMETRICSECONDRANKTENSOR:
544 return std::string( "symmetric_second_rank_tensor" );
545 case DIFFUSIONTENSOR3D:
546 return std::string( "diffusion_tensor_3D" );
547 case COMPLEX:
548 return std::string( "complex" );
549 case FIXEDARRAY:
550 return std::string( "fixed_array" );
551 case MATRIX:
552 return std::string( "matrix" );
553 case UNKNOWNPIXELTYPE:
554 return std::string( "unknown" );
555 default:
556 return std::string( "unknown" );
557 }
558 }
559
GetPixelTypeFromString(const std::string & pixelString)560 ImageIOBase::IOPixelType ImageIOBase::GetPixelTypeFromString(const std::string &pixelString)
561 {
562 if(pixelString.compare("scalar") == 0)
563 {
564 return SCALAR;
565 }
566 else if(pixelString.compare("vector") == 0)
567 {
568 return VECTOR;
569 }
570 else if(pixelString.compare("covariant_vector") == 0)
571 {
572 return COVARIANTVECTOR;
573 }
574 else if(pixelString.compare("point") == 0)
575 {
576 return POINT;
577 }
578 else if(pixelString.compare("offset") == 0)
579 {
580 return OFFSET;
581 }
582 else if(pixelString.compare("rgb") == 0)
583 {
584 return RGB;
585 }
586 else if(pixelString.compare("rgba") == 0)
587 {
588 return RGBA;
589 }
590 else if(pixelString.compare("symmetric_second_rank_tensor") == 0)
591 {
592 return SYMMETRICSECONDRANKTENSOR;
593 }
594 else if(pixelString.compare("diffusion_tensor_3D") == 0)
595 {
596 return DIFFUSIONTENSOR3D;
597 }
598 else if(pixelString.compare("complex") == 0)
599 {
600 return COMPLEX;
601 }
602 else if(pixelString.compare("fixed_array") == 0)
603 {
604 return FIXEDARRAY;
605 }
606 else if(pixelString.compare("matrix") == 0)
607 {
608 return MATRIX;
609 }
610 else
611 {
612 return UNKNOWNPIXELTYPE;
613 }
614 }
615
OpenFileForReading(std::ifstream & inputStream,const std::string & filename,bool ascii)616 void ImageIOBase::OpenFileForReading(std::ifstream & inputStream, const std::string & filename,
617 bool ascii)
618 {
619 // Make sure that we have a file to
620 if ( filename.empty() )
621 {
622 itkExceptionMacro( << "A FileName must be specified." );
623 }
624
625 // Close file from any previous image
626 if ( inputStream.is_open() )
627 {
628 inputStream.close();
629 }
630
631 // Open the new file for reading
632 itkDebugMacro( << "Opening file for reading: " << filename );
633
634 std::ios::openmode mode = std::ios::in;
635 if ( !ascii )
636 {
637 mode |= std::ios::binary;
638 }
639
640 inputStream.open( filename.c_str(), mode );
641
642 if ( !inputStream.is_open() || inputStream.fail() )
643 {
644 itkExceptionMacro( << "Could not open file: "
645 << filename << " for reading."
646 << std::endl
647 << "Reason: "
648 << itksys::SystemTools::GetLastSystemError() );
649 }
650 }
651
OpenFileForWriting(std::ofstream & outputStream,const std::string & filename,bool truncate,bool ascii)652 void ImageIOBase::OpenFileForWriting(std::ofstream & outputStream, const std::string & filename,
653 bool truncate, bool ascii)
654 {
655 // Make sure that we have a file to
656 if ( filename.empty() )
657 {
658 itkExceptionMacro( << "A FileName must be specified." );
659 }
660
661 // Close file from any previous image
662 if ( outputStream.is_open() )
663 {
664 outputStream.close();
665 }
666
667 // Open the new file for writing
668 itkDebugMacro( << "Opening file for writing: " << filename );
669
670 std::ios::openmode mode = std::ios::out;
671 if ( truncate )
672 {
673 // typically, ios::out also implies ios::trunc, but being explicit is safer
674 mode |= std::ios::trunc;
675 }
676 else
677 {
678 mode |= std::ios::in;
679 // opening a nonexistent file for reading + writing is not allowed on some platforms
680 if ( !itksys::SystemTools::FileExists( filename.c_str() ) )
681 {
682 itksys::SystemTools::Touch( filename.c_str(), true );
683 // don't worry about failure here, errors should be detected later when the file
684 // is "actually" opened, unless there is a race condition
685 }
686 }
687 if ( !ascii )
688 {
689 mode |= std::ios::binary;
690 }
691
692 outputStream.open( filename.c_str(), mode );
693
694 if ( !outputStream.is_open() || outputStream.fail() )
695 {
696 itkExceptionMacro( << "Could not open file: "
697 << filename << " for writing."
698 << std::endl
699 << "Reason: "
700 << itksys::SystemTools::GetLastSystemError() );
701 }
702 }
703
704 namespace
705 {
706 template< typename TComponent >
WriteBuffer(std::ostream & os,const TComponent * buffer,ImageIOBase::SizeType num)707 void WriteBuffer(std::ostream & os, const TComponent *buffer, ImageIOBase::SizeType num)
708 {
709 const TComponent *ptr = buffer;
710
711 using PrintType = typename itk::NumericTraits< TComponent >::PrintType;
712 for ( ImageIOBase::SizeType i = 0; i < num; i++ )
713 {
714 if ( !( i % 6 ) && i )
715 {
716 os << "\n";
717 }
718 os << PrintType(*ptr++) << " ";
719 }
720 }
721 }
WriteBufferAsASCII(std::ostream & os,const void * buffer,IOComponentType ctype,ImageIOBase::SizeType numComp)722 void ImageIOBase::WriteBufferAsASCII(std::ostream & os, const void *buffer,
723 IOComponentType ctype,
724 ImageIOBase::SizeType numComp)
725 {
726 switch ( ctype )
727 {
728 case UCHAR:
729 {
730 using Type = const unsigned char *;
731 auto buf = reinterpret_cast< Type >( buffer );
732 WriteBuffer(os, buf, numComp);
733 }
734 break;
735 case CHAR:
736 {
737 using Type = const char *;
738 auto buf = reinterpret_cast< Type >( buffer );
739 WriteBuffer(os, buf, numComp);
740 }
741 break;
742
743 case USHORT:
744 {
745 using Type = const unsigned short *;
746 auto buf = reinterpret_cast< Type >( buffer );
747 WriteBuffer(os, buf, numComp);
748 }
749 break;
750
751 case SHORT:
752 {
753 using Type = const short *;
754 auto buf = reinterpret_cast< Type >( buffer );
755 WriteBuffer(os, buf, numComp);
756 }
757 break;
758
759 case UINT:
760 {
761 using Type = const unsigned int *;
762 auto buf = reinterpret_cast< Type >( buffer );
763 WriteBuffer(os, buf, numComp);
764 }
765 break;
766
767 case INT:
768 {
769 using Type = const int *;
770 auto buf = reinterpret_cast< Type >( buffer );
771 WriteBuffer(os, buf, numComp);
772 }
773 break;
774
775 case ULONG:
776 {
777 using Type = const unsigned long *;
778 auto buf = reinterpret_cast< Type >( buffer );
779 WriteBuffer(os, buf, numComp);
780 }
781 break;
782
783 case LONG:
784 {
785 using Type = const long *;
786 auto buf = reinterpret_cast< Type >( buffer );
787 WriteBuffer(os, buf, numComp);
788 }
789 break;
790
791 case ULONGLONG:
792 {
793 using Type = const unsigned long long *;
794 auto buf = reinterpret_cast< Type >( buffer );
795 WriteBuffer(os, buf, numComp);
796 }
797 break;
798
799 case LONGLONG:
800 {
801 using Type = const long long *;
802 auto buf = reinterpret_cast< Type >( buffer );
803 WriteBuffer(os, buf, numComp);
804 }
805 break;
806
807 case FLOAT:
808 {
809 using Type = const float *;
810 auto buf = reinterpret_cast< Type >( buffer );
811 WriteBuffer(os, buf, numComp);
812 }
813 break;
814
815 case DOUBLE:
816 {
817 using Type = const double *;
818 auto buf = reinterpret_cast< Type >( buffer );
819 WriteBuffer(os, buf, numComp);
820 }
821 break;
822
823 default:
824 break;
825 }
826 }
827
828 namespace
829 {
830 template< typename TComponent >
ReadBuffer(std::istream & is,TComponent * buffer,ImageIOBase::SizeType num)831 void ReadBuffer(std::istream & is, TComponent *buffer, ImageIOBase::SizeType num)
832 {
833 using PrintType = typename itk::NumericTraits< TComponent >::PrintType;
834 PrintType temp;
835 TComponent *ptr = buffer;
836 for ( ImageIOBase::SizeType i = 0; i < num; i++, ptr++ )
837 {
838 is >> temp;
839 *ptr = static_cast< TComponent >( temp );
840 }
841 }
842 }
ReadBufferAsASCII(std::istream & is,void * buffer,IOComponentType ctype,ImageIOBase::SizeType numComp)843 void ImageIOBase::ReadBufferAsASCII(std::istream & is, void *buffer,
844 IOComponentType ctype,
845 ImageIOBase::SizeType numComp)
846 {
847 switch ( ctype )
848 {
849 case UCHAR:
850 {
851 auto * buf = reinterpret_cast< unsigned char * >( buffer );
852 ReadBuffer(is, buf, numComp);
853 }
854 break;
855 case CHAR:
856 {
857 auto * buf = reinterpret_cast< char * >( buffer );
858 ReadBuffer(is, buf, numComp);
859 }
860 break;
861
862 case USHORT:
863 {
864 auto * buf = reinterpret_cast< unsigned short * >( buffer );
865 ReadBuffer(is, buf, numComp);
866 }
867 break;
868
869 case SHORT:
870 {
871 auto * buf = reinterpret_cast< short * >( buffer );
872 ReadBuffer(is, buf, numComp);
873 }
874 break;
875
876 case UINT:
877 {
878 auto * buf = reinterpret_cast< unsigned int * >( buffer );
879 ReadBuffer(is, buf, numComp);
880 }
881 break;
882
883 case INT:
884 {
885 auto * buf = reinterpret_cast< int * >( buffer );
886 ReadBuffer(is, buf, numComp);
887 }
888 break;
889
890 case ULONG:
891 {
892 auto * buf = reinterpret_cast< unsigned long * >( buffer );
893 ReadBuffer(is, buf, numComp);
894 }
895 break;
896
897 case LONG:
898 {
899 auto * buf = reinterpret_cast< long * >( buffer );
900 ReadBuffer(is, buf, numComp);
901 }
902 break;
903
904 case ULONGLONG:
905 {
906 auto * buf = reinterpret_cast< unsigned long long * >( buffer );
907 ReadBuffer(is, buf, numComp);
908 }
909 break;
910
911 case LONGLONG:
912 {
913 auto * buf = reinterpret_cast< long long * >( buffer );
914 ReadBuffer(is, buf, numComp);
915 }
916 break;
917
918 case FLOAT:
919 {
920 auto * buf = reinterpret_cast< float * >( buffer );
921 ReadBuffer(is, buf, numComp);
922 }
923 break;
924
925 case DOUBLE:
926 {
927 auto * buf = reinterpret_cast< double * >( buffer );
928 ReadBuffer(is, buf, numComp);
929 }
930 break;
931
932 default:
933 break;
934 }
935 }
936
937 namespace
938 {
939 std::mutex ioDefaultSplitterLock;
940 ImageRegionSplitterBase::Pointer ioDefaultSplitter;
941
942 }
943
944 const ImageRegionSplitterBase*
GetImageRegionSplitter() const945 ImageIOBase::GetImageRegionSplitter() const
946 {
947 if ( ioDefaultSplitter.IsNull() )
948 {
949 // thread safe lazy initialization, prevent race condition on
950 // setting, with an atomic set if null.
951 std::lock_guard< std::mutex > lock( ioDefaultSplitterLock );
952 if ( ioDefaultSplitter.IsNull() )
953 {
954 ioDefaultSplitter = ImageRegionSplitterSlowDimension::New().GetPointer();
955 }
956 }
957 return ioDefaultSplitter;
958 }
959
960
961 bool
HasSupportedReadExtension(const char * fileName,bool ignoreCase)962 ImageIOBase::HasSupportedReadExtension( const char *fileName, bool ignoreCase )
963 {
964 return this->HasSupportedExtension( fileName,
965 this->GetSupportedReadExtensions(),
966 ignoreCase );
967 }
968
969
970 bool
HasSupportedWriteExtension(const char * fileName,bool ignoreCase)971 ImageIOBase::HasSupportedWriteExtension( const char *fileName, bool ignoreCase )
972 {
973
974 return this->HasSupportedExtension( fileName,
975 this->GetSupportedWriteExtensions(),
976 ignoreCase );
977 }
978
979
980 bool
HasSupportedExtension(const char * filename,const ImageIOBase::ArrayOfExtensionsType & supportedExtensions,bool ignoreCase)981 ImageIOBase::HasSupportedExtension( const char * filename,
982 const ImageIOBase::ArrayOfExtensionsType &supportedExtensions,
983 bool ignoreCase )
984 {
985
986 std::string ext = itksys::SystemTools::GetFilenameLastExtension(filename);
987 if ( ignoreCase )
988 {
989 std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
990 }
991
992 for ( auto && candidate : supportedExtensions )
993 {
994 if ( ignoreCase )
995 {
996 size_t n = candidate.size();
997 if ( n == ext.size() && n > 0)
998 {
999
1000 while ( true )
1001 {
1002 --n;
1003 if (ext[n] != ::tolower(candidate[n]))
1004 {
1005 break;
1006 }
1007 if ( n == 0 )
1008 {
1009 return true;
1010 }
1011 }
1012
1013 }
1014 }
1015 else
1016 {
1017 if ( candidate == ext )
1018 {
1019 return true;
1020 }
1021 }
1022 }
1023 return false;
1024 }
1025
1026 unsigned int
GetActualNumberOfSplitsForWritingCanStreamWrite(unsigned int numberOfRequestedSplits,const ImageIORegion & pasteRegion) const1027 ImageIOBase::GetActualNumberOfSplitsForWritingCanStreamWrite(unsigned int numberOfRequestedSplits,
1028 const ImageIORegion & pasteRegion) const
1029 {
1030 const ImageRegionSplitterBase* splitter = this->GetImageRegionSplitter();
1031 return splitter->GetNumberOfSplits(pasteRegion, numberOfRequestedSplits);
1032 }
1033
1034 unsigned int
GetActualNumberOfSplitsForWriting(unsigned int numberOfRequestedSplits,const ImageIORegion & pasteRegion,const ImageIORegion & largestPossibleRegion)1035 ImageIOBase::GetActualNumberOfSplitsForWriting(unsigned int numberOfRequestedSplits,
1036 const ImageIORegion & pasteRegion,
1037 const ImageIORegion & largestPossibleRegion)
1038 {
1039 if ( this->CanStreamWrite() )
1040 {
1041 return GetActualNumberOfSplitsForWritingCanStreamWrite(numberOfRequestedSplits, pasteRegion);
1042 }
1043 if ( pasteRegion != largestPossibleRegion )
1044 {
1045 itkExceptionMacro( "Pasting is not supported! Can't write:" << this->GetFileName() );
1046 }
1047 if ( numberOfRequestedSplits != 1 )
1048 {
1049 itkDebugMacro("Requested more then 1 splits for streaming");
1050 itkDebugMacro("This IO class does not support streaming!");
1051 }
1052 return 1;
1053 }
1054
1055 ImageIORegion
GetSplitRegionForWritingCanStreamWrite(unsigned int ithPiece,unsigned int numberOfActualSplits,const ImageIORegion & pasteRegion) const1056 ImageIOBase::GetSplitRegionForWritingCanStreamWrite(unsigned int ithPiece,
1057 unsigned int numberOfActualSplits,
1058 const ImageIORegion & pasteRegion) const
1059 {
1060 ImageIORegion splitRegion = pasteRegion;
1061
1062 const ImageRegionSplitterBase* splitter = this->GetImageRegionSplitter();
1063 splitter->GetSplit( ithPiece, numberOfActualSplits, splitRegion );
1064
1065 return splitRegion;
1066 }
1067
1068 ImageIORegion
GetSplitRegionForWriting(unsigned int ithPiece,unsigned int numberOfActualSplits,const ImageIORegion & pasteRegion,const ImageIORegion & largestPossibleRegion)1069 ImageIOBase::GetSplitRegionForWriting(unsigned int ithPiece,
1070 unsigned int numberOfActualSplits,
1071 const ImageIORegion & pasteRegion,
1072 const ImageIORegion & largestPossibleRegion)
1073 {
1074 if ( this->CanStreamWrite() )
1075 {
1076 return GetSplitRegionForWritingCanStreamWrite(ithPiece, numberOfActualSplits, pasteRegion);
1077 }
1078 return largestPossibleRegion;
1079 }
1080
1081 /** Given a requested region, determine what could be the region that we can
1082 * read from the file. This is called the streamable region, which will be
1083 * smaller than the LargestPossibleRegion and greater or equal to the
1084 * RequestedRegion */
1085 ImageIORegion
1086 ImageIOBase
GenerateStreamableReadRegionFromRequestedRegion(const ImageIORegion & requested) const1087 ::GenerateStreamableReadRegionFromRequestedRegion(
1088 const ImageIORegion & requested) const
1089 {
1090 //
1091 // The default implementations determines that the streamable region is
1092 // equal to the minimal size of the image in the file. That is two
1093 // say the return ImageIORegion::GetImageSizeInPixels() is equal to
1094 // the number in the file.
1095 //
1096
1097 // Since the image in the file may have a lower or higher dimension
1098 // than the image type over which the ImageFileReader is
1099 // being instantiated we must choose an image dimension which will
1100 // represent all the pixels. That is we can trim trailing 1s.
1101
1102 unsigned int minIODimension = this->m_NumberOfDimensions;
1103
1104 while ( minIODimension )
1105 {
1106 if ( this->m_Dimensions[minIODimension - 1] == 1 )
1107 {
1108 --minIODimension;
1109 }
1110 else
1111 {
1112 break;
1113 }
1114 }
1115
1116 // dimension size we use to represent the region
1117 unsigned int maxDimension =
1118 minIODimension > requested.GetImageDimension() ? minIODimension : requested.GetImageDimension();
1119
1120 // First: allocate with the correct dimensions
1121 ImageIORegion streamableRegion(maxDimension);
1122
1123 // Second: copy only the number of dimension that the file has.
1124 for ( unsigned int i = 0; i < minIODimension; i++ )
1125 {
1126 streamableRegion.SetSize(i, this->m_Dimensions[i]);
1127 streamableRegion.SetIndex(i, 0);
1128 }
1129
1130 // Third: set the rest to the default : start = 0, size = 1
1131 for ( unsigned int j = minIODimension; j < streamableRegion.GetImageDimension(); j++ )
1132 {
1133 streamableRegion.SetSize(j, 1);
1134 streamableRegion.SetIndex(j, 0);
1135 }
1136
1137 // Finally: return the streamable region
1138 return streamableRegion;
1139 }
1140
1141 /** Return the directions that this particular ImageIO would use by default
1142 * in the case the recipient image dimension is smaller than the dimension
1143 * of the image in file. */
1144 std::vector< double >
1145 ImageIOBase
GetDefaultDirection(unsigned int k) const1146 ::GetDefaultDirection(unsigned int k) const
1147 {
1148 std::vector< double > axis;
1149 axis.resize( this->GetNumberOfDimensions() );
1150
1151 // Fill up with the equivalent of a line from an Identity matrix
1152 for (double & axi : axis)
1153 {
1154 axi = 0.0;
1155 }
1156
1157 axis[k] = 1.0;
1158
1159 return axis;
1160 }
1161
PrintSelf(std::ostream & os,Indent indent) const1162 void ImageIOBase::PrintSelf(std::ostream & os, Indent indent) const
1163 {
1164 using namespace print_helper;
1165
1166 Superclass::PrintSelf(os, indent);
1167
1168 os << indent << "FileName: " << m_FileName << std::endl;
1169 os << indent << "FileType: " << this->GetFileTypeAsString(m_FileType) << std::endl;
1170 os << indent << "ByteOrder: " << this->GetByteOrderAsString(m_ByteOrder) << std::endl;
1171 os << indent << "IORegion: " << std::endl;
1172 m_IORegion.Print( os, indent.GetNextIndent() );
1173 os << indent << "Number of Components/Pixel: " << m_NumberOfComponents << "\n";
1174 os << indent << "Pixel Type: " << this->GetPixelTypeAsString(m_PixelType) << std::endl;
1175 os << indent << "Component Type: " << this->GetComponentTypeAsString(m_ComponentType)
1176 << std::endl;
1177 os << indent << "Dimensions: " << m_Dimensions << std::endl;
1178 os << indent << "Origin: " << m_Origin << std::endl;
1179 os << indent << "Spacing: " << m_Spacing << std::endl;
1180 os << indent << "Direction: " << std::endl;
1181 for( const auto & direction : m_Direction )
1182 {
1183 os << indent << direction << std::endl;
1184 }
1185 if( m_UseCompression )
1186 {
1187 os << indent << "UseCompression: On" << std::endl;
1188 }
1189 else
1190 {
1191 os << indent << "UseCompression: Off" << std::endl;
1192 }
1193 os << indent << "CompressionLevel: " << m_CompressionLevel << std::endl;
1194 os << indent << "MaximumCompressionLevel: " << m_MaximumCompressionLevel << std::endl;
1195 os << indent << "Compressor: " << m_Compressor << std::endl;
1196 if( m_UseStreamedReading )
1197 {
1198 os << indent << "UseStreamedReading: On" << std::endl;
1199 }
1200 else
1201 {
1202 os << indent << "UseStreamedReading: Off" << std::endl;
1203 }
1204 if( m_UseStreamedWriting )
1205 {
1206 os << indent << "UseStreamedWriting: On" << std::endl;
1207 }
1208 else
1209 {
1210 os << indent << "UseStreamedWriting: Off" << std::endl;
1211 }
1212 if( m_ExpandRGBPalette )
1213 {
1214 os << indent << "ExpandRGBPalette: On" << std::endl;
1215 }
1216 else
1217 {
1218 os << indent << "ExpandRGBPalette: Off" << std::endl;
1219 }
1220 if( m_IsReadAsScalarPlusPalette )
1221 {
1222 os << indent << "IsReadAsScalarPlusPalette: True" << std::endl;
1223 }
1224 else
1225 {
1226 os << indent << "IsReadAsScalarPlusPalette: False" << std::endl;
1227 }
1228 }
1229
1230 } //namespace itk
1231