108 if (!file.open(QIODevice::ReadOnly)) {
109 qCritical() <<
"MriNiftiIO::decompress - Could not open" << gzFile;
112 const QByteArray compressed = file.readAll();
115 if (compressed.isEmpty()) {
116 qCritical() <<
"MriNiftiIO::decompress - File is empty:" << gzFile;
121 if (inflateInit2(&strm, MAX_WBITS + 16) != Z_OK) {
122 qCritical() <<
"MriNiftiIO::decompress - inflateInit2 failed";
126 strm.next_in =
reinterpret_cast<Bytef*
>(
const_cast<char*
>(compressed.data()));
127 strm.avail_in =
static_cast<uInt
>(compressed.size());
129 const int chunkSize = 256 * 1024;
134 rawData.resize(rawData.size() + chunkSize);
135 strm.next_out =
reinterpret_cast<Bytef*
>(rawData.data() + rawData.size() - chunkSize);
136 strm.avail_out = chunkSize;
138 ret = inflate(&strm, Z_NO_FLUSH);
140 const bool truncated = ret != Z_STREAM_END && strm.avail_in == 0 && strm.avail_out > 0;
141 if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR || truncated) {
142 qCritical() <<
"MriNiftiIO::decompress - inflate failed for" << gzFile
143 <<
"- zlib error:" << (truncated ? Z_BUF_ERROR : ret);
147 }
while (ret != Z_STREAM_END);
149 rawData.resize(rawData.size() -
static_cast<int>(strm.avail_out));
161 const bool compressed = niiFile.endsWith(QStringLiteral(
".gz"), Qt::CaseInsensitive);
168 if (!f.open(QIODevice::ReadOnly)) {
169 qCritical() <<
"MriNiftiIO::read - Could not open" << niiFile;
175 if (bytes.size() < NIFTI_HDR_SIZE) {
176 qCritical() <<
"MriNiftiIO::read - file too small (" << bytes.size() <<
"bytes) :" << niiFile;
180 const char* hdr = bytes.constData();
183 qint32 sizeofHdr = readLE<qint32>(hdr);
184 bool bigEndian =
false;
185 if (sizeofHdr != NIFTI_HDR_SIZE) {
186 sizeofHdr = readBE<qint32>(hdr);
187 if (sizeofHdr != NIFTI_HDR_SIZE) {
188 qCritical() <<
"MriNiftiIO::read - not a NIfTI-1 file (sizeof_hdr ="
189 << sizeofHdr <<
"):" << niiFile;
195 auto i16 = [&](
int off) {
196 return bigEndian ? readBE<qint16>(hdr + off) : readLE<qint16>(hdr + off);
198 auto f32 = [&](
int off) {
200 quint32 raw = readBE<quint32>(hdr + off);
202 std::memcpy(&v, &raw,
sizeof(
float));
205 quint32 raw = readLE<quint32>(hdr + off);
207 std::memcpy(&v, &raw,
sizeof(
float));
212 qint16 ndim = i16(40);
217 if (ndim < 3 || nx <= 0 || ny <= 0 || nz <= 0) {
218 qCritical() <<
"MriNiftiIO::read - degenerate dims" << ndim << nx << ny << nz;
222 qint16 datatype = i16(70);
223 qint16 bitpix = i16(72);
227 float qfac = f32(76);
231 const float dx = std::fabs(f32(80));
232 const float dy = std::fabs(f32(84));
233 const float dz = std::fabs(f32(88));
235 const float voxOffset = f32(108);
236 const float sclSlope = f32(112);
237 const float sclInter = f32(116);
239 const qint16 qformCode = i16(252);
240 const qint16 sformCode = i16(254);
271 qCritical() <<
"MriNiftiIO::read - unsupported datatype" << datatype;
276 Matrix4f vox2ras = Matrix4f::Identity();
278 for (
int c = 0; c < 4; ++c) {
279 vox2ras(0, c) = f32(280 + c * 4);
280 vox2ras(1, c) = f32(296 + c * 4);
281 vox2ras(2, c) = f32(312 + c * 4);
283 }
else if (qformCode > 0) {
284 const float b = f32(256);
285 const float c = f32(260);
286 const float d = f32(264);
287 const float aSq = 1.0f - (b * b + c * c + d * d);
288 const float a = aSq > 0.0f ? std::sqrt(aSq) : 0.0f;
290 R << a * a + b * b - c * c - d * d, 2.0f * (b * c - a * d), 2.0f * (b * d + a * c),
291 2.0f * (b * c + a * d), a * a + c * c - b * b - d * d, 2.0f * (c * d - a * b),
292 2.0f * (b * d - a * c), 2.0f * (c * d + a * b), a * a + d * d - b * b - c * c;
293 Matrix3f
S = Matrix3f::Zero();
298 vox2ras.block<3, 3>(0, 0) = M;
299 vox2ras(0, 3) = f32(268);
300 vox2ras(1, 3) = f32(272);
301 vox2ras(2, 3) = f32(276);
307 vox2ras(0, 3) = -0.5f * dx * nx;
308 vox2ras(1, 3) = -0.5f * dy * ny;
309 vox2ras(2, 3) = -0.5f * dz * nz;
313 Vector3f col0 = vox2ras.block<3, 1>(0, 0);
314 Vector3f col1 = vox2ras.block<3, 1>(0, 1);
315 Vector3f col2 = vox2ras.block<3, 1>(0, 2);
316 float sx = col0.norm();
317 float sy = col1.norm();
318 float sz = col2.norm();
320 sx = dx > 0.0f ? dx : 1.0f;
322 sy = dy > 0.0f ? dy : 1.0f;
324 sz = dz > 0.0f ? dz : 1.0f;
326 Vector3f xRas = col0 / sx;
327 Vector3f yRas = col1 / sy;
328 Vector3f zRas = col2 / sz;
330 Vector4f centreVox(nx * 0.5f, ny * 0.5f, nz * 0.5f, 1.0f);
331 Vector4f cRas4 = vox2ras * centreVox;
337 volData.
nframes = nt > 0 ? nt : 1;
338 volData.
type = mriType;
344 volData.
x_ras = xRas;
345 volData.
y_ras = yRas;
346 volData.
z_ras = zRas;
347 volData.
c_ras = cRas4.head<3>();
352 const qint64 dataOff =
static_cast<qint64
>(voxOffset > 0.0f ? voxOffset : NIFTI_HDR_SIZE + 4);
353 const qint64 frameSize =
static_cast<qint64
>(nx) * ny * nz * bpv;
354 if (bytes.size() < dataOff + frameSize) {
355 qCritical() <<
"MriNiftiIO::read - data section truncated (need"
356 << (dataOff + frameSize) <<
"bytes, got" << bytes.size() <<
")";
360 QDataStream stream(bytes);
361 stream.setByteOrder(bigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian);
362 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
363 stream.device()->seek(dataOff);
365 const int nslice = nz;
366 const int nPixels = nx * ny;
367 volData.
slices.resize(nslice);
370 const bool useScale = (sclSlope != 0.0f) && !(sclSlope == 1.0f && sclInter == 0.0f);
372 for (
int k = 0; k < nslice; ++k) {
376 slice.
dimx = sx / 1000.0f;
377 slice.
dimy = sy / 1000.0f;
383 slice.
pixels.resize(nPixels);
384 if (datatype == DT_INT8) {
385 for (
int p = 0; p < nPixels; ++p) {
388 slice.
pixels[p] =
static_cast<unsigned char>(v < 0 ? 0 : v);
391 for (
int p = 0; p < nPixels; ++p) {
402 if (datatype == DT_UINT16) {
403 for (
int p = 0; p < nPixels; ++p) {
409 for (
int p = 0; p < nPixels; ++p) {
412 slice.
pixelsWord[p] =
static_cast<unsigned short>(v < 0 ? 0 : v);
420 for (
int p = 0; p < nPixels; ++p) {
430 for (
int p = 0; p < nPixels; ++p) {
443 for (
int p = 0; p < slice.
pixelsFloat.size(); ++p) {
448 Vector3f sliceOrigin;
449 sliceOrigin(0) = vox2rasFinal(0, 2) * k + vox2rasFinal(0, 3);
450 sliceOrigin(1) = vox2rasFinal(1, 2) * k + vox2rasFinal(1, 3);
451 sliceOrigin(2) = vox2rasFinal(2, 2) * k + vox2rasFinal(2, 3);
454 sliceRot.col(0) = vox2rasFinal.block<3, 1>(0, 0);
455 sliceRot.col(1) = vox2rasFinal.block<3, 1>(0, 1);
456 sliceRot.col(2) = vox2rasFinal.block<3, 1>(0, 2);
462 qInfo(
"NIfTI file: %dx%dx%d, datatype=%d, voxel %.3fx%.3fx%.3f mm, c_ras=(%.2f, %.2f, %.2f)",
463 nx, ny, nz, datatype, sx, sy, sz,