159 const bool compressed = niiFile.endsWith(QStringLiteral(
".gz"), Qt::CaseInsensitive);
166 if (!f.open(QIODevice::ReadOnly)) {
167 qCritical() <<
"MriNiftiIO::read - Could not open" << niiFile;
173 if (bytes.size() < NIFTI_HDR_SIZE) {
174 qCritical() <<
"MriNiftiIO::read - file too small (" << bytes.size() <<
"bytes) :" << niiFile;
178 const char* hdr = bytes.constData();
181 qint32 sizeofHdr = readLE<qint32>(hdr);
182 bool bigEndian =
false;
183 if (sizeofHdr != NIFTI_HDR_SIZE) {
184 sizeofHdr = readBE<qint32>(hdr);
185 if (sizeofHdr != NIFTI_HDR_SIZE) {
186 qCritical() <<
"MriNiftiIO::read - not a NIfTI-1 file (sizeof_hdr ="
187 << sizeofHdr <<
"):" << niiFile;
193 auto i16 = [&](
int off) {
194 return bigEndian ? readBE<qint16>(hdr + off) : readLE<qint16>(hdr + off);
196 auto f32 = [&](
int off) {
198 quint32 raw = readBE<quint32>(hdr + off);
200 std::memcpy(&v, &raw,
sizeof(
float));
203 quint32 raw = readLE<quint32>(hdr + off);
205 std::memcpy(&v, &raw,
sizeof(
float));
210 qint16 ndim = i16(40);
215 if (ndim < 3 || nx <= 0 || ny <= 0 || nz <= 0) {
216 qCritical() <<
"MriNiftiIO::read - degenerate dims" << ndim << nx << ny << nz;
220 qint16 datatype = i16(70);
221 qint16 bitpix = i16(72);
225 float qfac = f32(76);
229 const float dx = std::fabs(f32(80));
230 const float dy = std::fabs(f32(84));
231 const float dz = std::fabs(f32(88));
233 const float voxOffset = f32(108);
234 const float sclSlope = f32(112);
235 const float sclInter = f32(116);
237 const qint16 qformCode = i16(252);
238 const qint16 sformCode = i16(254);
269 qCritical() <<
"MriNiftiIO::read - unsupported datatype" << datatype;
274 Matrix4f vox2ras = Matrix4f::Identity();
276 for (
int c = 0; c < 4; ++c) {
277 vox2ras(0, c) = f32(280 + c * 4);
278 vox2ras(1, c) = f32(296 + c * 4);
279 vox2ras(2, c) = f32(312 + c * 4);
281 }
else if (qformCode > 0) {
282 const float b = f32(256);
283 const float c = f32(260);
284 const float d = f32(264);
285 const float aSq = 1.0f - (b * b + c * c + d * d);
286 const float a = aSq > 0.0f ? std::sqrt(aSq) : 0.0f;
288 R << a * a + b * b - c * c - d * d, 2.0f * (b * c - a * d), 2.0f * (b * d + a * c),
289 2.0f * (b * c + a * d), a * a + c * c - b * b - d * d, 2.0f * (c * d - a * b),
290 2.0f * (b * d - a * c), 2.0f * (c * d + a * b), a * a + d * d - b * b - c * c;
291 Matrix3f
S = Matrix3f::Zero();
296 vox2ras.block<3, 3>(0, 0) = M;
297 vox2ras(0, 3) = f32(268);
298 vox2ras(1, 3) = f32(272);
299 vox2ras(2, 3) = f32(276);
305 vox2ras(0, 3) = -0.5f * dx * nx;
306 vox2ras(1, 3) = -0.5f * dy * ny;
307 vox2ras(2, 3) = -0.5f * dz * nz;
311 Vector3f col0 = vox2ras.block<3, 1>(0, 0);
312 Vector3f col1 = vox2ras.block<3, 1>(0, 1);
313 Vector3f col2 = vox2ras.block<3, 1>(0, 2);
314 float sx = col0.norm();
315 float sy = col1.norm();
316 float sz = col2.norm();
318 sx = dx > 0.0f ? dx : 1.0f;
320 sy = dy > 0.0f ? dy : 1.0f;
322 sz = dz > 0.0f ? dz : 1.0f;
324 Vector3f xRas = col0 / sx;
325 Vector3f yRas = col1 / sy;
326 Vector3f zRas = col2 / sz;
328 Vector4f centreVox(nx * 0.5f, ny * 0.5f, nz * 0.5f, 1.0f);
329 Vector4f cRas4 = vox2ras * centreVox;
335 volData.
nframes = nt > 0 ? nt : 1;
336 volData.
type = mriType;
342 volData.
x_ras = xRas;
343 volData.
y_ras = yRas;
344 volData.
z_ras = zRas;
345 volData.
c_ras = cRas4.head<3>();
350 const qint64 dataOff =
static_cast<qint64
>(voxOffset > 0.0f ? voxOffset : NIFTI_HDR_SIZE + 4);
351 const qint64 frameSize =
static_cast<qint64
>(nx) * ny * nz * bpv;
352 if (bytes.size() < dataOff + frameSize) {
353 qCritical() <<
"MriNiftiIO::read - data section truncated (need"
354 << (dataOff + frameSize) <<
"bytes, got" << bytes.size() <<
")";
358 QDataStream stream(bytes);
359 stream.setByteOrder(bigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian);
360 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
361 stream.device()->seek(dataOff);
363 const int nslice = nz;
364 const int nPixels = nx * ny;
365 volData.
slices.resize(nslice);
368 const bool useScale = (sclSlope != 0.0f) && !(sclSlope == 1.0f && sclInter == 0.0f);
370 for (
int k = 0; k < nslice; ++k) {
374 slice.
dimx = sx / 1000.0f;
375 slice.
dimy = sy / 1000.0f;
381 slice.
pixels.resize(nPixels);
382 if (datatype == DT_INT8) {
383 for (
int p = 0; p < nPixels; ++p) {
386 slice.
pixels[p] =
static_cast<unsigned char>(v < 0 ? 0 : v);
389 for (
int p = 0; p < nPixels; ++p) {
400 if (datatype == DT_UINT16) {
401 for (
int p = 0; p < nPixels; ++p) {
407 for (
int p = 0; p < nPixels; ++p) {
410 slice.
pixelsWord[p] =
static_cast<unsigned short>(v < 0 ? 0 : v);
418 for (
int p = 0; p < nPixels; ++p) {
428 for (
int p = 0; p < nPixels; ++p) {
441 for (
int p = 0; p < slice.
pixelsFloat.size(); ++p) {
446 Vector3f sliceOrigin;
447 sliceOrigin(0) = vox2rasFinal(0, 2) * k + vox2rasFinal(0, 3);
448 sliceOrigin(1) = vox2rasFinal(1, 2) * k + vox2rasFinal(1, 3);
449 sliceOrigin(2) = vox2rasFinal(2, 2) * k + vox2rasFinal(2, 3);
452 sliceRot.col(0) = vox2rasFinal.block<3, 1>(0, 0);
453 sliceRot.col(1) = vox2rasFinal.block<3, 1>(0, 1);
454 sliceRot.col(2) = vox2rasFinal.block<3, 1>(0, 2);
460 qInfo(
"NIfTI file: %dx%dx%d, datatype=%d, voxel %.3fx%.3fx%.3f mm, c_ras=(%.2f, %.2f, %.2f)",
461 nx, ny, nz, datatype, sx, sy, sz,