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 "itksys/SystemTools.hxx"
20 #include "itkIPLCommonImageIO.h"
21 #include "itkByteSwapper.h"
22 #include "itkSpatialOrientationAdapter.h"
23 #include "itkDirectory.h"
24 #include "itkMetaDataObject.h"
25 #include <iostream>
26 #include <fstream>
27 #include <cstring>
28 #include <climits>
29 #include <cstdlib>
30 #include <cstdio>
31 #include <ctime>
32 #include <cassert>
33 #include <vector>
34
35 //From uiig library "The University of Iowa Imaging Group-UIIG"
36
37 namespace itk
38 {
39 // Default constructor
IPLCommonImageIO()40 IPLCommonImageIO::IPLCommonImageIO()
41 {
42 m_SystemByteOrder =
43 ByteSwapper< int >::SystemIsBigEndian() ?
44 ImageIOBase::BigEndian :
45 ImageIOBase::LittleEndian;
46 m_ImageHeader = nullptr;
47 m_FilenameList = new IPLFileNameList;
48 this->SetComponentType(ImageIOBase::SHORT);
49 }
50
~IPLCommonImageIO()51 IPLCommonImageIO::~IPLCommonImageIO()
52 {
53 delete m_ImageHeader;
54 delete m_FilenameList;
55 }
56
PrintSelf(std::ostream & os,Indent indent) const57 void IPLCommonImageIO::PrintSelf(std::ostream & os, Indent indent) const
58 {
59 Superclass::PrintSelf(os, indent);
60 }
61
CanWriteFile(const char *)62 bool IPLCommonImageIO::CanWriteFile(const char *)
63 {
64 return false;
65 }
66
GetComponentSize() const67 unsigned int IPLCommonImageIO::GetComponentSize() const
68 {
69 return sizeof( short int );
70 }
71
Read(void * buffer)72 void IPLCommonImageIO::Read(void *buffer)
73 {
74 auto * img_buffer = (short int *)buffer;
75 auto it = m_FilenameList->begin();
76 auto itend = m_FilenameList->end();
77
78 for (; it != itend; it++ )
79 {
80 std::string curfilename = ( *it )->GetImageFileName();
81 std::ifstream f;
82 this->OpenFileForReading( f, curfilename );
83
84 f.seekg ( ( *it )->GetSliceOffset(), std::ios::beg );
85 if ( !this->ReadBufferAsBinary( f, img_buffer, m_FilenameList->GetXDim() * m_FilenameList->GetYDim()
86 * sizeof( short int ) ) )
87 {
88 f.close();
89 RAISE_EXCEPTION();
90 }
91 f.close();
92 // ByteSwapper::SwapRangeFromSystemToBigEndian is set up based on
93 // the FILE endian-ness, not as the name would lead you to believe.
94 // So, on LittleEndian systems, SwapFromSystemToBigEndian will swap.
95 // On BigEndian systems, SwapFromSystemToBigEndian will do nothing.
96 itk::ByteSwapper< short int >::SwapRangeFromSystemToBigEndian( img_buffer,
97 m_FilenameList->GetXDim() * m_FilenameList->GetYDim() );
98 img_buffer += m_FilenameList->GetXDim() * m_FilenameList->GetYDim();
99 }
100 }
101
ReadHeader(const char *)102 GEImageHeader * IPLCommonImageIO::ReadHeader(const char *)
103 {
104 //
105 // must be redefined in a child class
106 //
107 return nullptr;
108 }
109
CanReadFile(const char *)110 bool IPLCommonImageIO::CanReadFile(const char *)
111 {
112 //
113 // must be redefined in child class or you'll never read anything ;-)
114 //
115 return false;
116 }
117
ReadImageInformation()118 void IPLCommonImageIO::ReadImageInformation()
119 {
120 std::string FileNameToRead = this->GetFileName();
121 //
122 // GE images are stored in separate files per slice.
123 //char imagePath[IOCommon::ITK_MAXPATHLEN+1];
124 //TODO -- use std::string instead of C strings
125 char imageMask[IOCommon::ITK_MAXPATHLEN + 1];
126 char imagePath[IOCommon::ITK_MAXPATHLEN + 1];
127 std::string _imagePath =
128 itksys::SystemTools::CollapseFullPath( FileNameToRead.c_str() );
129
130 FileNameToRead = _imagePath;
131
132 this->m_ImageHeader = this->ReadHeader( FileNameToRead.c_str() );
133 //
134 // if anything fails in the header read, just let
135 // exceptions propagate up.
136
137 bool isCT = false;
138 std::string modality = m_ImageHeader->modality;
139 if( modality == "CT" )
140 {
141 isCT = true;
142 }
143
144 AddElementToList(m_ImageHeader->filename,
145 m_ImageHeader->sliceLocation,
146 m_ImageHeader->offset,
147 m_ImageHeader->imageXsize,
148 m_ImageHeader->imageYsize,
149 m_ImageHeader->imageXres,
150 m_ImageHeader->imageYres,
151 m_ImageHeader->seriesNumber,
152 (isCT)?m_ImageHeader->examNumber:
153 m_ImageHeader->echoNumber); // If CT use examNumber, otherwise use echoNumber.
154
155 // Add header info to metadictionary
156
157 itk::MetaDataDictionary & thisDic = this->GetMetaDataDictionary();
158 std::string classname( this->GetNameOfClass() );
159 itk::EncapsulateMetaData< std::string >(thisDic, ITK_InputFilterName, classname);
160 itk::EncapsulateMetaData< std::string >( thisDic, ITK_OnDiskStorageTypeName, std::string("SHORT") );
161 itk::EncapsulateMetaData< short int >(thisDic, ITK_OnDiskBitPerPixel, (short int)16);
162
163 //
164 // has to be set before setting dir cosines,
165 // otherwise the vector doesn't get allocated
166 this->SetNumberOfDimensions(3);
167
168 itk::EncapsulateMetaData< std::string >( thisDic, ITK_PatientID, std::string(m_ImageHeader->patientId) );
169 itk::EncapsulateMetaData< std::string >( thisDic, ITK_ExperimentDate, std::string(m_ImageHeader->date) );
170
171 if ( _imagePath.empty() )
172 {
173 RAISE_EXCEPTION();
174 }
175 strncpy( imagePath, _imagePath.c_str(), sizeof( imagePath ) );
176 imagePath[IOCommon::ITK_MAXPATHLEN] = '\0';
177 strncpy( imageMask, imagePath, sizeof( imageMask ) );
178 imageMask[IOCommon::ITK_MAXPATHLEN] = '\0';
179
180 char *lastslash = strrchr(imagePath, '/');
181 if ( lastslash == nullptr )
182 {
183 strcpy(imagePath, ".");
184 }
185 else
186 {
187 *lastslash = '\0';
188 }
189 itk::Directory::Pointer Dir = itk::Directory::New();
190 if ( Dir->Load(imagePath) == 0 )
191 {
192 RAISE_EXCEPTION();
193 }
194 std::vector< std::string >::size_type i;
195 std::vector< std::string >::size_type numfiles;
196
197 GEImageHeader *curImageHeader;
198
199 for ( i = 0, numfiles = Dir->GetNumberOfFiles(); i < numfiles; i++ )
200 {
201 const char *curFname = Dir->GetFile(static_cast<unsigned int>( i ) );
202
203 if ( curFname == nullptr )
204 {
205 break;
206 }
207 const std::string fullPath=std::string(imagePath) + "/" + curFname;
208 if ( FileNameToRead == fullPath ) //strcmp(curFname,FileNameToRead) ==
209 // 0)
210 {
211 continue;
212 }
213 try
214 {
215 curImageHeader = this->ReadHeader(fullPath.c_str());
216 }
217 catch ( itk::ExceptionObject & )
218 {
219 // ReadGE4XHeader throws an exception on any error.
220 // So if, for example we run into a subdirectory, it would
221 // throw an exception, and we'd just want to skip it.
222 continue;
223 }
224 if( (((isCT)?curImageHeader->examNumber:curImageHeader->echoNumber)
225 == m_FilenameList->GetKey2()) &&
226 (curImageHeader->seriesNumber == m_FilenameList->GetKey1()))
227 {
228 AddElementToList(curImageHeader->filename,
229 curImageHeader->sliceLocation,
230 curImageHeader->offset,
231 curImageHeader->imageXsize,
232 curImageHeader->imageYsize,
233 curImageHeader->imageXres,
234 curImageHeader->imageYres,
235 curImageHeader->seriesNumber,
236 (isCT)?curImageHeader->examNumber:
237 curImageHeader->echoNumber); // If CT use examNumber, otherwise use echoNumber.
238 }
239 delete curImageHeader;
240 }
241
242 //sort image list
243 m_FilenameList->sortImageList();
244
245 //
246 //
247 // set the image properties
248 this->SetDimensions(0, m_ImageHeader->imageXsize);
249 this->SetDimensions(1, m_ImageHeader->imageYsize);
250 this->SetDimensions( 2, static_cast<unsigned int>( m_FilenameList->NumFiles() ) );
251 this->SetSpacing(0, m_ImageHeader->imageXres);
252 this->SetSpacing(1, m_ImageHeader->imageYres);
253 this->SetSpacing(2, m_ImageHeader->sliceThickness + m_ImageHeader->sliceGap);
254
255 //
256 // set direction cosines
257 using OrientAdapterType = SpatialOrientationAdapter;
258 SpatialOrientationAdapter::DirectionType dir =
259 OrientAdapterType().ToDirectionCosines(m_ImageHeader->coordinateOrientation);
260 std::vector< double > dirx(3, 0), diry(3, 0), dirz(3, 0);
261 dirx[0] = dir[0][0];
262 dirx[1] = dir[1][0];
263 dirx[2] = dir[2][0];
264 diry[0] = dir[0][1];
265 diry[1] = dir[1][1];
266 diry[2] = dir[2][1];
267 dirz[0] = dir[0][2];
268 dirz[1] = dir[1][2];
269 dirz[2] = dir[2][2];
270
271 this->SetDirection(0, dirx);
272 this->SetDirection(1, diry);
273 this->SetDirection(2, dirz);
274
275 this->ModifyImageInformation();
276 }
277
SortImageListByNameAscend()278 void IPLCommonImageIO::SortImageListByNameAscend()
279 {
280 m_FilenameList->SetSortOrder(IPLFileNameList::SortByNameAscend);
281 }
282
SortImageListByNameDescend()283 void IPLCommonImageIO::SortImageListByNameDescend()
284 {
285 m_FilenameList->SetSortOrder(IPLFileNameList::SortByNameDescend);
286 }
287
288 /**
289 *
290 */
291 void
292 IPLCommonImageIO
WriteImageInformation()293 ::WriteImageInformation()
294 {
295 RAISE_EXCEPTION();
296 }
297
298 /**
299 *
300 */
301 void
302 IPLCommonImageIO
Write(const void *)303 ::Write(const void *)
304 {
305 RAISE_EXCEPTION();
306 }
307
308 int
309 IPLCommonImageIO
GetStringAt(std::ifstream & f,std::streamoff Offset,char * buf,size_t amount,bool throw_exception)310 ::GetStringAt(std::ifstream & f,
311 std::streamoff Offset,
312 char *buf,
313 size_t amount, bool throw_exception)
314 {
315 f.seekg(Offset, std::ios::beg);
316 if ( f.fail() )
317 {
318 if ( throw_exception )
319 {
320 RAISE_EXCEPTION();
321 }
322 else
323 {
324 return -1;
325 }
326 }
327 if ( !this->ReadBufferAsBinary(f, (void *)buf, amount) )
328 {
329 if ( throw_exception )
330 {
331 RAISE_EXCEPTION();
332 }
333 else
334 {
335 return -1;
336 }
337 }
338 return 0;
339 }
340
341 int IPLCommonImageIO
GetIntAt(std::ifstream & f,std::streamoff Offset,int * ip,bool throw_exception)342 ::GetIntAt(std::ifstream & f, std::streamoff Offset, int *ip,
343 bool throw_exception)
344 {
345 int tmp;
346
347 if ( this->GetStringAt(f, Offset, (char *)&tmp, sizeof( int ),
348 throw_exception) == 0 )
349 {
350 *ip = this->hdr2Int( (char *)&tmp );
351 }
352 else
353 {
354 *ip = 0;
355 }
356 return 0;
357 }
358
359 int IPLCommonImageIO
GetShortAt(std::ifstream & f,std::streamoff Offset,short * ip,bool throw_exception)360 ::GetShortAt(std::ifstream & f, std::streamoff Offset, short *ip,
361 bool throw_exception)
362 {
363 short tmp;
364
365 if ( this->GetStringAt(f, Offset, (char *)&tmp, sizeof( short ),
366 throw_exception) == 0 )
367 {
368 *ip = this->hdr2Short( (char *)&tmp );
369 }
370 else
371 {
372 *ip = 0;
373 }
374 return 0;
375 }
376
377 int IPLCommonImageIO
GetFloatAt(std::ifstream & f,std::streamoff Offset,float * ip,bool throw_exception)378 ::GetFloatAt(std::ifstream & f, std::streamoff Offset, float *ip,
379 bool throw_exception)
380 {
381 float tmp;
382
383 if ( this->GetStringAt(f, Offset, (char *)&tmp, sizeof( float ),
384 throw_exception) == 0 )
385 {
386 *ip = this->hdr2Float( (char *)&tmp );
387 }
388 else
389 {
390 *ip = 0.0f;
391 }
392 return 0;
393 }
394
395 int IPLCommonImageIO
GetDoubleAt(std::ifstream & f,std::streamoff Offset,double * ip,bool throw_exception)396 ::GetDoubleAt(std::ifstream & f, std::streamoff Offset, double *ip,
397 bool throw_exception)
398 {
399 double tmp;
400
401 if ( this->GetStringAt(f, Offset, (char *)&tmp, sizeof( double ),
402 throw_exception) == 0 )
403 {
404 *ip = this->hdr2Double( (char *)&tmp );
405 }
406 else
407 {
408 *ip = 0.0;
409 }
410 return 0;
411 }
412
hdr2Short(char * hdr)413 short IPLCommonImageIO::hdr2Short(char *hdr)
414 {
415 short shortValue;
416
417 memcpy ( &shortValue, hdr, sizeof( short ) );
418 ByteSwapper< short int >::SwapFromSystemToBigEndian (&shortValue);
419 return ( shortValue );
420 }
421
422 int IPLCommonImageIO
hdr2Int(char * hdr)423 ::hdr2Int(char *hdr)
424 {
425 int intValue;
426
427 memcpy ( &intValue, hdr, sizeof( int ) );
428 ByteSwapper< int >::SwapFromSystemToBigEndian (&intValue);
429 return ( intValue );
430 }
431
432 float IPLCommonImageIO
hdr2Float(char * hdr)433 ::hdr2Float(char *hdr)
434 {
435 float floatValue;
436
437 memcpy (&floatValue, hdr, 4);
438 ByteSwapper< float >::SwapFromSystemToBigEndian (&floatValue);
439
440 return ( floatValue );
441 }
442
443 double IPLCommonImageIO
hdr2Double(char * hdr)444 ::hdr2Double(char *hdr)
445 {
446 double doubleValue;
447
448 memcpy ( &doubleValue, hdr, sizeof( double ) );
449 ByteSwapper< double >::SwapFromSystemToBigEndian (&doubleValue);
450
451 return ( doubleValue );
452 }
453
454 int IPLCommonImageIO
AddElementToList(char const * const filename,const float sliceLocation,const int offset,const int XDim,const int YDim,const float XRes,const float YRes,const int Key1,const int Key2)455 ::AddElementToList(char const *const filename,
456 const float sliceLocation,
457 const int offset,
458 const int XDim,
459 const int YDim,
460 const float XRes,
461 const float YRes,
462 const int Key1,
463 const int Key2)
464 {
465 if ( m_FilenameList->NumFiles() == 0 )
466 {
467 m_FilenameList->SetXDim(XDim);
468 m_FilenameList->SetYDim(YDim);
469 m_FilenameList->SetXRes(XRes);
470 m_FilenameList->SetYRes(YRes);
471 m_FilenameList->SetKey1(Key1);
472 m_FilenameList->SetKey2(Key2);
473 }
474 else if ( XDim != m_FilenameList->GetXDim() || YDim != m_FilenameList->GetYDim() )
475 {
476 return 0;
477 }
478 else if( itk::Math::NotAlmostEquals( XRes, m_FilenameList->GetXRes() ) || itk::Math::NotAlmostEquals( YRes, m_FilenameList->GetYRes() ) )
479 {
480 return 0;
481 }
482 else if ( m_FilenameList->GetKey1() != Key1 || m_FilenameList->GetKey2() != Key2 )
483 {
484 return 1; //It is OK for keys to not match, Just don't add.
485 }
486 m_FilenameList->AddElementToList(filename, sliceLocation,
487 offset, XDim, YDim, XRes, YRes, 0, Key1, Key2);
488 return 1;
489 }
490
491 void IPLCommonImageIO
sortImageListAscend()492 ::sortImageListAscend()
493 {
494 m_FilenameList->sortImageListAscend();
495 }
496
497 void IPLCommonImageIO
sortImageListDescend()498 ::sortImageListDescend()
499 {
500 m_FilenameList->sortImageListDescend();
501 }
502
503 int IPLCommonImageIO
statTimeToAscii(void * clock,char * timeString,int len)504 ::statTimeToAscii(void *clock, char *timeString,int len)
505 {
506
507 auto tclock = (time_t)*( (int *)clock );
508 const char * const asciiTime = ctime (&tclock);
509
510 strncpy (timeString, asciiTime, len);
511 timeString[len-1] = '\0';
512 char *newline;
513 if((newline = strrchr(timeString,'\n')) != nullptr ||
514 (newline = strrchr(timeString,'\r')))
515 {
516 *newline = '\0';
517 }
518 return 1;
519 }
520 } // end namespace itk
521