56#include <QRegularExpression>
80 QVector<FiffCoordTrans>& additionalTrans,
81 const QString& subjectMriDir,
87 bool isCompressed = mgzFile.endsWith(
".mgz", Qt::CaseInsensitive);
91 if (!decompress(mgzFile, fileData)) {
96 if (!file.open(QIODevice::ReadOnly)) {
97 qCritical() <<
"MriMghIO::read - Could not open" << mgzFile;
100 fileData = file.readAll();
105 qCritical() <<
"MriMghIO::read - File" << mgzFile
106 <<
"is too small to be a valid MGH file ("
107 << fileData.size() <<
"bytes)";
112 if (!parseHeader(fileData, volData, verbose)) {
122 printf(
"Voxel -> Surface RAS transform:\n");
123 for (
int r = 0; r < 4; ++r) {
124 printf(
" %10.6f %10.6f %10.6f %10.6f\n",
125 vox2ras(r, 0), vox2ras(r, 1), vox2ras(r, 2), vox2ras(r, 3));
130 if (!readVoxelData(fileData, volData)) {
135 parseFooter(fileData, volData, additionalTrans, subjectMriDir, verbose);
138 printf(
"Read %d slices from %s (%dx%d pixels)\n",
139 static_cast<int>(volData.
slices.size()), qPrintable(mgzFile),
148bool MriMghIO::decompress(
const QString& mgzFile, QByteArray& rawData)
151 if (!file.open(QIODevice::ReadOnly)) {
152 qCritical() <<
"MriMghIO::decompress - Could not open" << mgzFile;
155 QByteArray compressedData = file.readAll();
158 if (compressedData.isEmpty()) {
159 qCritical() <<
"MriMghIO::decompress - File is empty:" << mgzFile;
165 memset(&strm, 0,
sizeof(strm));
168 int ret = inflateInit2(&strm, MAX_WBITS + 16);
170 qCritical() <<
"MriMghIO::decompress - inflateInit2 failed";
174 strm.next_in =
reinterpret_cast<Bytef*
>(compressedData.data());
175 strm.avail_in =
static_cast<uInt
>(compressedData.size());
177 const int chunkSize = 256 * 1024;
181 rawData.resize(rawData.size() + chunkSize);
182 strm.next_out =
reinterpret_cast<Bytef*
>(rawData.data() + rawData.size() - chunkSize);
183 strm.avail_out = chunkSize;
185 ret = inflate(&strm, Z_NO_FLUSH);
186 if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
187 qCritical() <<
"MriMghIO::decompress - inflate failed for" << mgzFile
188 <<
"- zlib error:" << ret;
192 }
while (ret != Z_STREAM_END);
195 rawData.resize(rawData.size() -
static_cast<int>(strm.avail_out));
203bool MriMghIO::parseHeader(
const QByteArray& data,
MriVolData& volData,
bool verbose)
221 QDataStream stream(data);
222 stream.setByteOrder(QDataStream::BigEndian);
223 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
225 qint32 version, width, height, depth, nframes, type, dof;
226 stream >> version >> width >> height >> depth >> nframes >> type >> dof;
229 qCritical() <<
"MriMghIO::parseHeader - Unknown MGH version:" << version;
234 volData.
width = width;
236 volData.
depth = depth;
242 printf(
"MGH file: %dx%dx%d, %d frame(s), type=%d\n",
243 width, height, depth, nframes, type);
248 stream >> goodRASflag;
249 volData.
rasGood = (goodRASflag > 0);
251 if (goodRASflag > 0) {
269 printf(
"Voxel sizes: %.4f x %.4f x %.4f mm\n",
271 printf(
"goodRAS: %d\n", goodRASflag);
272 printf(
"c_ras: %.4f %.4f %.4f\n",
281bool MriMghIO::readVoxelData(
const QByteArray& data,
MriVolData& volData)
290 switch (volData.
type) {
291 case MRI_UCHAR: bytesPerVoxel = 1;
break;
292 case MRI_SHORT: bytesPerVoxel = 2;
break;
293 case MRI_INT: bytesPerVoxel = 4;
break;
294 case MRI_FLOAT: bytesPerVoxel = 4;
break;
296 qCritical() <<
"MriMghIO::readVoxelData - Unsupported MGH data type:" << volData.
type;
300 qint64 frameSize =
static_cast<qint64
>(volData.
width) * volData.
height * volData.
depth * bytesPerVoxel;
302 qCritical() <<
"MriMghIO::readVoxelData - File too small for expected data size";
306 QDataStream stream(data);
307 stream.setByteOrder(QDataStream::BigEndian);
308 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
311 int nslice = volData.
depth;
313 volData.
slices.resize(nslice);
318 for (
int k = 0; k < nslice; ++k) {
319 MriSlice& slice = volData.
slices[k];
326 switch (volData.
type) {
329 slice.
pixels.resize(nPixels);
330 for (
int p = 0; p < nPixels; ++p) {
341 for (
int p = 0; p < nPixels; ++p) {
344 slice.
pixelsWord[p] =
static_cast<unsigned short>(val < 0 ? 0 : val);
353 for (
int p = 0; p < nPixels; ++p) {
364 for (
int p = 0; p < nPixels; ++p) {
380 Vector3f sliceOrigin;
381 sliceOrigin(0) = vox2ras(0, 2) * k + vox2ras(0, 3);
382 sliceOrigin(1) = vox2ras(1, 2) * k + vox2ras(1, 3);
383 sliceOrigin(2) = vox2ras(2, 2) * k + vox2ras(2, 3);
386 sliceRot.col(0) = vox2ras.block<3, 1>(0, 0);
387 sliceRot.col(1) = vox2ras.block<3, 1>(0, 1);
388 sliceRot.col(2) = vox2ras.block<3, 1>(0, 2);
391 sliceMove << sliceOrigin(0), sliceOrigin(1), sliceOrigin(2);
401bool MriMghIO::parseFooter(
const QByteArray& data,
403 QVector<FiffCoordTrans>& additionalTrans,
404 const QString& subjectMriDir,
415 switch (volData.
type) {
416 case MRI_UCHAR: bytesPerVoxel = 1;
break;
417 case MRI_SHORT: bytesPerVoxel = 2;
break;
418 case MRI_INT: bytesPerVoxel = 4;
break;
419 case MRI_FLOAT: bytesPerVoxel = 4;
break;
420 default: bytesPerVoxel = 1;
break;
423 qint64 frameSize =
static_cast<qint64
>(volData.
width) * volData.
height * volData.
depth * bytesPerVoxel;
426 if (data.size() <= footerPos) {
431 QDataStream stream(data);
432 stream.setByteOrder(QDataStream::BigEndian);
433 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
434 stream.device()->seek(footerPos);
437 while (!stream.atEnd()) {
440 if (stream.atEnd())
break;
455 if (tagLen <= 0 || tagLen > data.size())
break;
457 QByteArray tagData(tagLen,
'\0');
458 if (stream.readRawData(tagData.data(), tagLen) != tagLen)
break;
462 QString xfmPath = QString::fromLatin1(tagData).trimmed();
466 printf(
"Found Talairach transform reference: %s\n", qPrintable(xfmPath));
470 if (!QFileInfo(xfmPath).isAbsolute() && !subjectMriDir.isEmpty()) {
471 xfmPath = subjectMriDir +
"/transforms/" + xfmPath;
474 if (QFileInfo::exists(xfmPath)) {
475 QFile xfmFile(xfmPath);
476 if (xfmFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
485 QString xfmContent = xfmFile.readAll();
488 int ltIdx = xfmContent.indexOf(
"Linear_Transform");
490 int eqIdx = xfmContent.indexOf(
'=', ltIdx);
492 QString matStr = xfmContent.mid(eqIdx + 1).trimmed();
495 QStringList vals = matStr.split(QRegularExpression(
"\\s+"),
498 if (vals.size() >= 12) {
500 Matrix4f rasMniTal = Matrix4f::Identity();
501 for (
int r = 0; r < 3; ++r) {
502 for (
int c = 0; c < 4; ++c) {
503 rasMniTal(r, c) = vals[r * 4 + c].toFloat();
508 rasMniTal(0, 3) /= 1000.0f;
509 rasMniTal(1, 3) /= 1000.0f;
510 rasMniTal(2, 3) /= 1000.0f;
513 FiffCoordTrans talTrans(
517 additionalTrans.append(talTrans);
520 printf(
"Read Talairach transform from %s\n", qPrintable(xfmPath));
528 printf(
"Talairach transform file not found: %s\n", qPrintable(xfmPath));
#define FIFFV_COORD_MRI_SLICE
#define FIFFV_COORD_MRI_DISPLAY
Header file describing the numerical values used in fif files.
#define FIFFV_MRI_PIXEL_BYTE
#define FIFFV_MRI_PIXEL_FLOAT
#define FIFFV_MRI_PIXEL_WORD
FiffCoordTrans class declaration.
MriMghIO class declaration.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
MRI volume and coordinate-system I/O (volumes, voxel geometry, transforms).
constexpr int MRI_MGH_VERSION
constexpr int MGH_TAG_OLD_MGH_XFORM
constexpr int MRI_MGH_DATA_OFFSET
constexpr int MGH_TAG_OLD_SURF_GEOM
constexpr int MGH_TAG_MGH_XFORM
Coordinate transformation description.
static bool read(const QString &mgzFile, MriVolData &volData, QVector< FIFFLIB::FiffCoordTrans > &additionalTrans, const QString &subjectMriDir=QString(), bool verbose=false)
QVector< unsigned char > pixels
FIFFLIB::FiffCoordTrans trans
QVector< unsigned short > pixelsWord
QVector< float > pixelsFloat
MRI volume data from FreeSurfer MGH/MGZ file.
FIFFLIB::FiffCoordTrans voxelSurfRasT
QVector< MriSlice > slices
Eigen::Matrix4f computeVox2Ras() const