Skip to content

Commit e815be6

Browse files
authored
Merge pull request #96 from ORMIR-XCT/enhancement/incorporate_aimio_1
Restructure and Refactor Header Handling
2 parents 434b550 + 1308359 commit e815be6

14 files changed

+2042
-926
lines changed

include/itkAIMHeaderIO.h

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
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+
* https://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+
#ifndef itkAIMHeaderIO_h
20+
#define itkAIMHeaderIO_h
21+
#include "itkScancoHeaderIO.h"
22+
#include <sstream>
23+
24+
struct AIMV020StructHeader;
25+
struct AIMV030StructHeader;
26+
27+
namespace itk
28+
{
29+
class AIMHeaderIO : public ScancoHeaderIO
30+
{
31+
public:
32+
using ScancoHeaderIO::ScancoHeaderIO;
33+
34+
~AIMHeaderIO();
35+
36+
/** Read the header from an infile.
37+
* Fills in the m_HeaderData structure with the header information.
38+
* \param infile The input file stream to read the header from.
39+
* \throws std::runtime_error if the file cannot be opened or read.
40+
* \note Overrides base class virtual method
41+
*/
42+
unsigned long
43+
ReadHeader(std::ifstream & infile) override;
44+
45+
/** Write the header to an open file stream.
46+
* \param outfile Pointer to an open std::ofstream where the header will be written.
47+
* \param imageSize size of the image that will be written after the header, in bytes
48+
* \throws std::runtime_error if headerData is null.
49+
* \note overrides base class virtual method
50+
* \returns number of bytes written to the file.
51+
*/
52+
unsigned long
53+
WriteHeader(std::ofstream & outfile, unsigned long imageSize) override;
54+
55+
protected:
56+
/** Read the AIM pre-header from the file stream.
57+
* \param file The input file stream to read the header from.
58+
* \param length The length of the pre-header to read.
59+
* \param offset The offset in the file to start reading from.
60+
* \note m_IntSize is used to determine the size of integers in the pre-header,
61+
* and should be set appropriately before calling
62+
* \returns -1 on failure, 0 on success.
63+
*/
64+
int
65+
ReadPreHeader(std::ifstream & file, unsigned long length, unsigned long offset = 0);
66+
67+
/** Read the AIM v020 image structure header from a data structure.
68+
* \param headerData pointer to structure containing image structure header
69+
*/
70+
void
71+
ReadImgStructHeader(AIMV020StructHeader * headerData);
72+
73+
/** Read the AIM v030 image structure header from a data structure.
74+
* \param headerData pointer to structure containing image structure header
75+
* \overload ReadImgStructHeader for AIM v020
76+
*/
77+
void
78+
ReadImgStructHeader(AIMV030StructHeader * headerData);
79+
80+
/** Read the AIM processing log from the file stream.
81+
* \param file The input file stream to read the header from.
82+
* \param length The length of the processing log to read.
83+
* \param offset The offset in the file to start reading from.
84+
* \returns -1 on failure, 0 on success.
85+
*/
86+
int
87+
ReadProcessingLog(std::ifstream & infile, unsigned long offset, unsigned long length);
88+
89+
/** Write the image structure header to an AIM v020 data structure
90+
* \returns AIMV020StructHeader structure filled with encoded header data
91+
*/
92+
AIMV020StructHeader
93+
WriteStructHeaderV020();
94+
95+
/** Write the image structure header to an AIM v030 data structure
96+
* \returns AIMV030StructHeader structure filled with encoded header data
97+
*/
98+
AIMV030StructHeader
99+
WriteStructHeaderV030();
100+
101+
/** Write the processing log to a string.
102+
* \returns std::string containing the processing log populated with the header data
103+
*/
104+
std::string
105+
WriteProcessingLog();
106+
107+
/** Write the image pre-header to a file
108+
* \param outfile The output file stream to write the pre-header to
109+
* \param imageSize size of the image that will be written after the header, in bytes
110+
* \param version The AIM file version to write (default is AIM_020)
111+
* \returns number of bytes written to the file
112+
* \note pre-header is written into the current file pointer of the outfile stream
113+
* \note pre-header blocks are written based on imageSize, m_ImgStructSize, and m_ProcessingLogSize,
114+
* which should be set accordingly
115+
*/
116+
size_t
117+
WritePreHeader(std::ofstream & outfile, size_t imageSize, ScancoFileVersions version = ScancoFileVersions::AIM_020);
118+
119+
private:
120+
unsigned int m_IntSize{ 4 }; // Size of integers in the header (4 for AIM v020, 8 for AIM v030)
121+
122+
/** Header Size = m_PreHeaderSize + m_ImgStructSize + m_ProcessingLogSize */
123+
unsigned long m_PreHeaderSize{ 0 };
124+
unsigned long m_ImgStructSize{ 0 };
125+
unsigned long m_ProcessingLogSize{ 0 };
126+
};
127+
} // namespace itk
128+
#endif // itkAIMHeaderIO_h

include/itkISQHeaderIO.h

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
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+
* https://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+
#ifndef itkISQHeaderIO_h
20+
#define itkISQHeaderIO_h
21+
#include "itkScancoHeaderIO.h"
22+
23+
struct ISQEncodedPreHeader;
24+
struct ISQEncodedHeaderBlock;
25+
struct RADEncodedHeaderBlock;
26+
27+
namespace itk
28+
{
29+
class ISQHeaderIO : public ScancoHeaderIO
30+
{
31+
public:
32+
using ScancoHeaderIO::ScancoHeaderIO;
33+
34+
/** Read the header from an infile.
35+
* Fills in the m_HeaderData structure with the header information.
36+
* \param infile The input file stream to read the header from.
37+
* \throws std::runtime_error if the file cannot be opened or read.
38+
* \note Overrides base class virtual method
39+
*/
40+
unsigned long
41+
ReadHeader(std::ifstream & infile) override;
42+
43+
/** Write the header to an open file stream.
44+
* \param outfile Pointer to an open std::ofstream where the header will be written.
45+
* \throws std::runtime_error if headerData is null.
46+
* \returns number of bytes written to the file.
47+
*/
48+
unsigned long
49+
WriteHeader(std::ofstream & outfile, unsigned long imageSize) override;
50+
51+
private:
52+
/** Read date into creation and modification date strings
53+
* \param year The Gregorian year.
54+
* \param month The Gregorian month (1-12).
55+
* \param day The Gregorian day (1-31).
56+
* \param hour The hour (0-23).
57+
* \param minute The minute (0-59).
58+
* \param second The second (0-59).
59+
* \param millis The milliseconds (0-999).
60+
*/
61+
void
62+
ReadDateValues(const int year,
63+
const int month,
64+
const int day,
65+
const int hour,
66+
const int minute,
67+
const int second,
68+
const int milli);
69+
70+
/** Parse and Save pixel and physical dimension values
71+
* Values are converted into appropriate units
72+
* \see unit conversions in itkScancoImageIO.h
73+
* \param imageData encoded data of at least 24 bytes to read from
74+
* \returns true if the file is a RAD file based on the dimension data,
75+
* false otherise
76+
*/
77+
bool
78+
ReadDimensionData(ISQEncodedPreHeader * imageData);
79+
80+
/** Read RAD header values and convert units appropriately
81+
* \see unit conversions in itkScancoImageIO.h
82+
* \param headerData struct holding encoded RAD header data
83+
*/
84+
void
85+
ReadRADHeader(RADEncodedHeaderBlock * headerData);
86+
87+
/** Read ISQ header values and convert units appropriately
88+
* \see unit conversions in itkScancoImageIO.h
89+
* \param headerData struct holding encoded ISQ header data
90+
*/
91+
void
92+
ReadISQHeader(ISQEncodedHeaderBlock * headerData);
93+
94+
/** Read in extended header data
95+
* This may include a multi-header section with calibration data
96+
* \param buffer pointer to start of extended header data
97+
* \param length length of extended header,
98+
* this can be calculated from the data offset found in the last 4 bytes of the main header block
99+
* \note length must be at least 3 blocks
100+
* \note buffer must be at least length bytes
101+
*/
102+
void
103+
ReadExtendedHeader(const char * buffer, unsigned long length);
104+
105+
/** Write Calibration data to extended header blocks
106+
* \param outfile file to write data to
107+
* \note requires first block (512 bytes) to be written with header data
108+
* \returns number of bytes written to the file
109+
*/
110+
unsigned long
111+
WriteExtendedHeader(std::ofstream & outfile);
112+
113+
unsigned long m_HeaderSize;
114+
};
115+
116+
} // namespace itk
117+
118+
#endif // itkISQHeaderIO_h

include/itkScancoDataManipulation.h

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,77 @@
1717
*=========================================================================*/
1818
#ifndef itkScancoDataManipulation_h
1919
#define itkScancoDataManipulation_h
20+
#include "itkMacro.h"
2021
#include <cstddef>
22+
#include <array>
23+
24+
struct itkScancoPixelData
25+
{
26+
int m_Dimensions[3]; // Dimensions of the pixel data
27+
float m_Origin[3]; // Origin of the pixel data in physical space
28+
double m_Spacing[3]; // Spacing between pixels in physical space
29+
int m_ComponentType; // Data Type (e.g., unsigned char, short, float)
30+
int m_PixelType;
31+
};
32+
33+
struct itkScancoHeaderData
34+
{
35+
char m_Version[18]; // Version string, e.g., "AIMDATA_V020 "
36+
char m_PatientName[42];
37+
int m_PatientIndex;
38+
int m_ScannerID;
39+
char m_CreationDate[32];
40+
char m_ModificationDate[32];
41+
int m_ScanDimensionsPixels[3];
42+
double m_ScanDimensionsPhysical[3];
43+
double m_SliceThickness; // Slice thickness in mm
44+
double m_SliceIncrement; // Slice increment in mm
45+
double m_StartPosition;
46+
double m_EndPosition;
47+
double m_ZPosition;
48+
std::array<double, 2> m_DataRange;
49+
double m_MuScaling;
50+
int m_NumberOfSamples;
51+
int m_NumberOfProjections;
52+
double m_ScanDistance;
53+
double m_SampleTime;
54+
int m_ScannerType;
55+
int m_MeasurementIndex;
56+
int m_Site;
57+
int m_ReconstructionAlg;
58+
double m_ReferenceLine;
59+
double m_Energy;
60+
double m_Intensity;
61+
int m_RescaleType;
62+
char m_RescaleUnits[18];
63+
char m_CalibrationData[66];
64+
double m_RescaleSlope;
65+
double m_RescaleIntercept;
66+
double m_MuWater;
67+
char * m_RawHeader;
68+
itkScancoPixelData m_PixelData; // Pixel data information
69+
};
70+
71+
#define ScancoGetConstMacro(name, type) \
72+
virtual type Get##name() const { return this->m_HeaderData.m_##name; } \
73+
ITK_MACROEND_NOOP_STATEMENT
74+
75+
#define ScancoSetMacro(name, type) \
76+
virtual void Set##name(type _arg) \
77+
{ \
78+
itkDebugMacro("setting " #name " to " << _arg); \
79+
ITK_GCC_PRAGMA_PUSH \
80+
ITK_GCC_SUPPRESS_Wfloat_equal \
81+
if (this->m_HeaderData.m_##name != _arg) \
82+
{ \
83+
this->m_HeaderData.m_##name = std::move(_arg); \
84+
this->Modified(); \
85+
} \
86+
ITK_GCC_PRAGMA_POP \
87+
} \
88+
ITK_MACROEND_NOOP_STATEMENT
89+
90+
constexpr int ScancoHeaderBlockSize = 512;
2191

2292
/** Check the file header to see what type of file it is.
2393
*
@@ -44,6 +114,14 @@ DecodeInt(const void * data);
44114
void
45115
EncodeInt(int data, void * target);
46116

117+
/** Convert 64-bit int (little-endian) to char data.
118+
*
119+
* \param data The integer to convert.
120+
* \param target Pointer to a buffer of at least 8 bytes to store the result.
121+
*/
122+
void
123+
EncodeInt64(int64_t data, void * target);
124+
47125
/** Convert char data to float (single precision).
48126
*
49127
* \param data Pointer to a buffer of at least 4 bytes.
@@ -52,6 +130,14 @@ EncodeInt(int data, void * target);
52130
float
53131
DecodeFloat(const void * data);
54132

133+
/** Convert float data to char data.
134+
*
135+
* \param data The float to convert.
136+
* \param target Pointer to a buffer of at least 4 bytes to store the result.
137+
*/
138+
void
139+
EncodeFloat(float data, void * target);
140+
55141
/** Convert char data to float (double precision).
56142
*
57143
* \param data Pointer to a buffer of at least 8 bytes.
@@ -60,6 +146,9 @@ DecodeFloat(const void * data);
60146
double
61147
DecodeDouble(const void * data);
62148

149+
void
150+
EncodeDouble(double data, void * target);
151+
63152
/** Convert a VMS timestamp to a calendar date.
64153
*
65154
* \param data Pointer to a buffer of at least 8 bytes containing the VMS timestamp.
@@ -74,6 +163,25 @@ DecodeDouble(const void * data);
74163
void
75164
DecodeDate(const void * data, int & year, int & month, int & day, int & hour, int & minute, int & second, int & millis);
76165

166+
/** Formats a date into a string of the form DD-MMM-YYYY HH:MM:SS:mmm
167+
* \param target Pointer to a buffer of at least 32 bytes to store the formatted date string.
168+
* \param year The year.
169+
* \param month The month (1-12).
170+
* \param day The day (1-31).
171+
* \param hour The hour (0-23).
172+
* \param minute The minute (0-59).
173+
* \param second The second (0-59).
174+
* \param millis The milliseconds (0-999).
175+
*/
176+
void
177+
DateToString(const void * target, int year, int month, int day, int hour, int minute, int second, int millis);
178+
179+
/** Get the current date and time as a string in the format "YYYY-MM-DD HH:MM:SS.mmm".
180+
* \param target Pointer to a buffer of at least 32 bytes to store the current date string.
181+
*/
182+
void
183+
GetCurrentDateString(void * target);
184+
77185
/** Convert the current calendar date to a VMS timestamp and store in target
78186
*
79187
* \param target Pointer to a buffer of at least 8 bytes to store the VMS timestamp.

0 commit comments

Comments
 (0)