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