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 #include "itkGE4ImageIO.h"
19 #include "itkByteSwapper.h"
20 #include "Ge4xHdr.h"
21 #include "itksys/SystemTools.hxx"
22 #include <iostream>
23 #include <fstream>
24 //From uiig library "The University of Iowa Imaging Group-UIIG"
25 
26 namespace itk
27 {
28 // Default constructor
29 GE4ImageIO::GE4ImageIO() = default;
30 
~GE4ImageIO()31 GE4ImageIO::~GE4ImageIO()
32 {
33   //Purposefully left blank
34 }
35 
CanReadFile(const char * FileNameToRead)36 bool GE4ImageIO::CanReadFile(const char *FileNameToRead)
37 {
38   char tmpStr[64];
39 
40   std::ifstream f;
41   try
42     {
43     this->OpenFileForReading( f, FileNameToRead );
44     }
45   catch( ExceptionObject & )
46     {
47     return false;
48     }
49 
50   // This is a weak heuristic but should only be true for GE4 files
51   //
52   // Get the Plane from the IMAGE Header.
53   if ( this->GetStringAt(f, SIGNA_SEHDR_START * 2 + SIGNA_SEHDR_PLANENAME * 2, tmpStr, 16, false) == -1 )
54     {
55     f.close();
56     return false;
57     }
58   tmpStr[16] = '\0';
59   // if none of these strings show up, most likely not GE4
60   if ( strstr (tmpStr, "CORONAL") == nullptr
61        && strstr (tmpStr, "SAGITTAL") == nullptr
62        && strstr (tmpStr, "AXIAL") == nullptr
63        && strstr (tmpStr, "OBLIQUE") == nullptr )
64     {
65     f.close();
66     return false;
67     }
68   //
69   // doesn't appear to be any signature in the header so I guess
70   // I have to assume it's readable
71   f.close();
72   return true;
73 }
74 
ReadHeader(const char * FileNameToRead)75 GEImageHeader * GE4ImageIO::ReadHeader(const char *FileNameToRead)
76 {
77   // #define VERBOSE_DEBUGGING
78 #if defined( VERBOSE_DEBUGGING )
79 #define RGEDEBUG(x) x
80 #else
81 #define RGEDEBUG(x)
82 #endif
83   if ( FileNameToRead == nullptr || strlen(FileNameToRead) == 0 )
84     {
85     return nullptr;
86     }
87   //
88   // need to check if this is a valid file before going further
89   if ( !this->CanReadFile(FileNameToRead) )
90     {
91     RAISE_EXCEPTION();
92     }
93   auto * hdr = new GEImageHeader;
94   if ( hdr == nullptr )
95     {
96     RAISE_EXCEPTION();
97     }
98   // Set modality to UNKNOWN
99   strcpy(hdr->modality, "UNK");
100 
101   //  RGEDEBUG(char debugbuf[16384];)
102   char      tmpStr[IOCommon::ITK_MAXPATHLEN + 1];
103   int       intTmp;
104   short int tmpShort;
105   float     tmpFloat;
106 
107   //
108   // save off the name of the current file...
109   strncpy(hdr->filename, FileNameToRead, sizeof(hdr->filename)-1);
110   hdr->filename[sizeof(hdr->filename)-1] = '\0';
111 
112   //
113   // Next, can you open it?
114 
115   std::ifstream f;
116   this->OpenFileForReading( f, FileNameToRead );
117 
118   this->GetStringAt(f, SIGNA_STHDR_START * 2 + SIGNA_STHDR_DATE_ASCII * 2, tmpStr, 10);
119   tmpStr[10] = '\0';
120   RGEDEBUG(std::sprintf (debugbuf, "Date = %s\n", tmpStr); cerr << debugbuf; )
121   strncpy(hdr->date, tmpStr, sizeof(hdr->date)-1);
122   hdr->date[sizeof(hdr->date)-1] = '\0';
123 
124   // Get Patient-Name from the STUDY Header
125   this->GetStringAt(f, SIGNA_STHDR_START * 2 + SIGNA_STHDR_PATIENT_NAME * 2, tmpStr, 32);
126   tmpStr[32] = '\0';
127   strncpy(hdr->hospital, tmpStr, sizeof(hdr->hospital)-1);
128   hdr->hospital[sizeof(hdr->hospital)-1] = '\0';
129 
130   /* Get Patient-Number from the STUDY Header */
131   this->GetStringAt(f, SIGNA_STHDR_START * 2 + SIGNA_STHDR_PATIENT_ID * 2, tmpStr, 12);
132   tmpStr[12] = '\0';
133   RGEDEBUG(std::sprintf (debugbuf, "Patient-Number = %s\n", tmpStr); cerr << debugbuf; )
134   strncpy(hdr->patientId, tmpStr, sizeof(hdr->patientId)-1);
135   hdr->patientId[sizeof(hdr->patientId)-1] = '\0';
136 
137   /* Get the Exam-Number from the STUDY Header */
138   this->GetStringAt(f, SIGNA_STHDR_START * 2 + SIGNA_STHDR_STUDY_NUM * 2, tmpStr, 6);
139   tmpStr[6] = '\0';
140   RGEDEBUG(std::sprintf (debugbuf, "Exam-Number = %s\n", tmpStr); cerr << debugbuf; )
141   strncpy(hdr->scanId, tmpStr, sizeof(hdr->scanId)-1);
142   hdr->scanId[sizeof(hdr->scanId)-1] = '\0';
143 
144   /* Get the FOV from the SERIES Header */
145   f.seekg (SIGNA_SEHDR_START * 2 + SIGNA_SEHDR_FOV * 2, std::ios::beg);
146   IOCHECK();
147   f.read ( (char *)&intTmp, sizeof( intTmp ) );
148   IOCHECK();
149   tmpFloat = MvtSunf (intTmp);
150 
151   hdr->xFOV = tmpFloat;
152   hdr->yFOV = hdr->xFOV;
153   RGEDEBUG(std::sprintf (debugbuf, "FOV = %fx%f\n", hdr->xFOV, hdr->yFOV); cerr << debugbuf; )
154 
155   /* Get the Plane from the IMAGE Header */
156   this->GetStringAt(f, SIGNA_SEHDR_START * 2 + SIGNA_SEHDR_PLANENAME * 2, tmpStr, 16);
157   tmpStr[16] = '\0';
158 
159   if ( strstr (tmpStr, "CORONAL") != nullptr )
160     {
161     //hdr->imagePlane =
162     // itk::SpatialOrientation::ITK_ANALYZE_ORIENTATION_IRP_CORONAL;
163     //hdr->origin = itk::SpatialOrientation::ITK_ORIGIN_SRP; // was SLA in the
164     // brains2 filter.
165     hdr->coordinateOrientation = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP;
166     }
167   else if ( strstr (tmpStr, "SAGITTAL") != nullptr )
168     {
169     //hdr->imagePlane =
170     // itk::SpatialOrientation::ITK_ANALYZE_ORIENTATION_IRP_SAGITTAL;
171     //hdr->origin = itk::SpatialOrientation::ITK_ORIGIN_SRA;  //was SLP in the
172     // brains2 filter.
173     hdr->coordinateOrientation = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_AIR;
174     }
175   else if ( strstr (tmpStr, "AXIAL") != nullptr )
176     {
177     //hdr->imagePlane =
178     // itk::SpatialOrientation::ITK_ANALYZE_ORIENTATION_IRP_TRANSVERSE;
179     //hdr->origin = itk::SpatialOrientation::ITK_ORIGIN_SRA;  //was SLP in the
180     // brains2 filter.
181     hdr->coordinateOrientation = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RAI;
182     }
183   else
184     {
185     //hdr->imagePlane =
186     // itk::SpatialOrientation::ITK_ANALYZE_ORIENTATION_IRP_CORONAL;
187     //hdr->origin = itk::SpatialOrientation::ITK_ORIGIN_SRP; // was SLA
188     hdr->coordinateOrientation = itk::SpatialOrientation::ITK_COORDINATE_ORIENTATION_RSP;
189     }
190   //RGEDEBUG(std::sprintf (debugbuf, "Plane = %d\n", hdr->imagePlane); cerr <<
191   // debugbuf;)
192 
193   /* Get the Scan Matrix from the IMAGE Header */
194   this->GetShortAt( f, SIGNA_SEHDR_START * 2 + SIGNA_SEHDR_SCANMATRIXX * 2, &( hdr->acqXsize ) );
195   this->GetShortAt( f, ( SIGNA_SEHDR_START * 2 + SIGNA_SEHDR_SCANMATRIXY * 2 ) + sizeof( short ),
196                     &( hdr->acqYsize ) );
197 
198   RGEDEBUG(std::sprintf (debugbuf, "Scan Matrix = %dx%d\n", hdr->acqXsize, hdr->acqYsize); cerr << debugbuf; )
199 
200   /* Get Series-Number from SERIES Header */
201   this->GetStringAt(f, SIGNA_SEHDR_START * 2 + SIGNA_SEHDR_SERIES_NUM * 2, tmpStr, 3);
202   tmpStr[3] = '\0';
203   hdr->seriesNumber = std::stoi (tmpStr);
204   RGEDEBUG(std::sprintf (debugbuf, "Series Number = %d\n", hdr->seriesNumber); cerr << debugbuf; )
205 
206   /* Get Image-Number from IMAGE Header */
207   this->GetStringAt(f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_IMAGE_NUM * 2, tmpStr, 3);
208   tmpStr[3] = '\0';
209   hdr->imageNumber = std::stoi (tmpStr);
210   RGEDEBUG(std::sprintf (debugbuf, "Image Number = %d\n", hdr->imageNumber); cerr << debugbuf; )
211 
212   /* Get Images-Per-Slice from IMAGE Header */
213   const int per_slice_status = this->GetStringAt(f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_PHASENUM * 2, tmpStr, 3);
214   tmpStr[3] = '\0';
215   if (strlen(tmpStr) > 0  && per_slice_status >=0 )
216   {
217     hdr->imagesPerSlice = static_cast<short>(std::stoi(tmpStr));
218   }
219   else
220   {
221     hdr->imagesPerSlice = 0; // Use default of 0 to mimic previous atoi failure result.
222   }
223   RGEDEBUG(std::sprintf (debugbuf, "Images Per Slice = %d\n", hdr->imagesPerSlice); cerr << debugbuf; )
224 
225   /* Get the Slice Location from the IMAGE Header */
226   // hack alert -- and this goes back to a hack in the original code
227   // you read in an integer, but you DON'T byte swap it, and then pass
228   // it into the MvtSunf function to get the floating point value.
229   // to circumvent byte swapping in GetIntAt, use GetStringAt
230   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_SLICELOC * 2,
231                      (char *)&intTmp, sizeof( int ) );
232 
233   hdr->sliceLocation = MvtSunf (intTmp);
234 
235   RGEDEBUG(std::sprintf (debugbuf, "Location = %f\n", hdr->sliceLocation); cerr << debugbuf; )
236 
237   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_SLICE_THICK * 2,
238                      (char *)&intTmp, sizeof( intTmp ) );
239 
240   hdr->sliceThickness = MvtSunf (intTmp);
241 
242   RGEDEBUG(std::sprintf (debugbuf, "Thickness = %f\n", hdr->sliceThickness); cerr << debugbuf; )
243 
244   /* Get the Slice Spacing from the IMAGE Header */
245   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_SLICE_SPACING * 2,
246                      (char *)&intTmp, sizeof( int ) );
247 
248   hdr->sliceGap = MvtSunf (intTmp);
249 
250   RGEDEBUG(std::sprintf (debugbuf, "Slice Gap = %f\n", hdr->sliceGap); cerr << debugbuf; )
251 
252   /* Get TR from the IMAGE Header */
253   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_TR * 2,
254                      (char *)&intTmp, sizeof( int ) );
255 
256   hdr->TR = MvtSunf (intTmp);
257 
258   RGEDEBUG(std::sprintf (debugbuf, "TR = %f\n", hdr->TR); cerr << debugbuf; )
259 
260   /* Get TE from the IMAGE Header */
261   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_TE * 2,
262                      (char *)&intTmp, sizeof( int ) );
263 
264   hdr->TE = MvtSunf (intTmp);
265   //  RGEDEBUG(std::sprintf (debugbuf, "TE = %f\n", hdr->TE); cerr << debugbuf;)
266 
267   /* Get TI from the IMAGE Header */
268   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_TI * 2,
269                      (char *)&intTmp, sizeof( int ) );
270 
271   hdr->TI = MvtSunf (intTmp);
272   RGEDEBUG(std::sprintf (debugbuf, "TI = %f\n", hdr->TI); cerr << debugbuf; )
273 
274   /* Get Number of Echos from the IMAGE Header */
275   this->GetShortAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_NUMECHOS * 2, &( hdr->numberOfEchoes ) );
276   RGEDEBUG(std::sprintf (debugbuf, "Number of Echos = %d\n", hdr->numberOfEchoes); cerr << debugbuf; )
277 
278   /* Get Echo Number from the IMAGE Header */
279   this->GetShortAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_ECHONUM * 2, &( hdr->echoNumber ) );
280   RGEDEBUG(std::sprintf (debugbuf, "Echo Number = %d\n", hdr->echoNumber); cerr << debugbuf; )
281 
282   /* Get PSD-Name from the IMAGE Header */
283   this->GetStringAt(f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_PSD_NAME * 2, tmpStr, 12);
284   tmpStr[12] = '\0';
285   RGEDEBUG(std::sprintf (debugbuf, "PSD Name = %s\n", tmpStr); cerr << debugbuf; )
286 
287   /* Get X Pixel Dimension from the IMAGE Header */
288   this->GetShortAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_X_DIM * 2, &( hdr->imageXsize ) );
289   RGEDEBUG(std::sprintf (debugbuf, "X Pixel Dimension = %d\n", hdr->imageXsize); cerr << debugbuf; )
290 
291   /* Get Y Pixel Dimension from the IMAGE Header */
292   this->GetShortAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_Y_DIM * 2, &( hdr->imageYsize ) );
293   RGEDEBUG(std::sprintf (debugbuf, "Y Pixel Dimension = %d\n", hdr->imageYsize); cerr << debugbuf; )
294 
295   /* Get Pixel Size from the IMAGE Header */
296   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_PIXELSIZE * 2,
297                      (char *)&intTmp, sizeof( int ) );
298 
299   hdr->imageXres = MvtSunf (intTmp);
300   hdr->imageYres = hdr->imageXres;
301   RGEDEBUG(std::sprintf (debugbuf, "Pixel Size = %fx%f\n", hdr->imageXres, hdr->imageYres); cerr << debugbuf; )
302 
303   /* Get NEX from the IMAGE Header */
304   this->GetStringAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_NEX * 2,
305                      (char *)&intTmp, sizeof( int ) );
306 
307   hdr->NEX = (short)MvtSunf (intTmp);
308   RGEDEBUG(std::sprintf (debugbuf, "NEX = %d\n", hdr->NEX); cerr << debugbuf; )
309 
310   /* Get Flip Angle from the IMAGE Header */
311   this->GetShortAt(f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_FLIP * 2, &tmpShort);
312 
313   if ( tmpShort > 0 )
314     {
315     hdr->flipAngle = (int)tmpShort;
316     }
317   else
318     {
319     hdr->flipAngle = 90;
320     }
321   RGEDEBUG(std::sprintf (debugbuf, "Flip Angle = %d\n", hdr->flipAngle); cerr << debugbuf; )
322 
323   //DEBUG: HACK -- what should pulse sequence be?  Is it valid for 4x filters
324   // Just setting it to dummy value -- Hans
325   //copy from ge5x strncpy (hdr->pulseSequence, &hdr[IM_HDR_START + IM_PSDNAME],
326   // 31);
327   strncpy (hdr->pulseSequence, "UNKNOWN_GE4x_PULSE_SEQUENCE", 31);
328   hdr->pulseSequence[31] = '\0';
329 
330   /* Get the Number of Images from the IMAGE Header */
331   this->GetShortAt( f, SIGNA_IHDR_START * 2 + SIGNA_IMHDR_NUMSLICES * 2, &( hdr->numberOfSlices ) );
332   //  RGEDEBUG(std::sprintf (debugbuf, "Number of SLices = %d\n",
333   // hdr->numberOfSlices); cerr << debugbuf;)
334 
335   //    status = stat (imageFile, &statBuf);
336   //    if (status == -1)
337   //      {
338   //  return (nullptr);
339   //      }
340   //
341   //    hdr->offset = statBuf.st_size - (hdr->imageXsize * hdr->imageYsize * 2);
342   //
343   // find file length in line ...
344   SizeValueType file_length = itksys::SystemTools::FileLength(FileNameToRead);
345 
346   hdr->offset = file_length
347                 - ( hdr->imageXsize * hdr->imageYsize * 2 );
348   return hdr;
349 }
350 
351 float GE4ImageIO
MvtSunf(int numb)352 ::MvtSunf(int numb)
353 {
354 #define signbit 020000000000U
355 #define dmantissa 077777777U
356 #define dexponent 0177U
357 #define smantissa 037777777U
358 #define smantlen 23U
359   ByteSwapper< int >::SwapFromSystemToBigEndian(&numb);
360   unsigned int dg_exp = ( numb >> 24 ) & dexponent;
361   unsigned int dg_sign = numb & signbit;
362   unsigned int dg_mantissa = ( numb & dmantissa ) << 8;
363   int sun_exp = 4 * ( dg_exp - 64 );
364   while ( ( dg_mantissa & signbit ) == 0 && dg_mantissa != 0 )
365     {
366     sun_exp--;
367     dg_mantissa = dg_mantissa << 1;
368     }
369   sun_exp += 126;
370   if ( sun_exp < 0 )
371     {
372     sun_exp = 0;
373     }
374   else if ( sun_exp > 255 )
375     {
376     sun_exp = 255;
377     }
378   dg_mantissa = dg_mantissa << 1;
379   int sun_num = dg_sign | ( sun_exp << smantlen ) | ( ( dg_mantissa >> 9 ) & smantissa );
380   float x;
381   memcpy ( &x, &sun_num, sizeof( x ) );
382   return x;
383 }
384 } // end namespace itk
385