71 : m_ras2vox(Matrix4f::Identity())
89 if(sParcellationPath.endsWith(QStringLiteral(
".mgz"), Qt::CaseInsensitive)) {
91 qWarning() <<
"[FsAtlasLookup::load] .mgz files not supported in WebAssembly build (QProcess unavailable):" << sParcellationPath;
96 gzipProc.start(QStringLiteral(
"gzip"), QStringList() << QStringLiteral(
"-dc") << sParcellationPath);
97 if(!gzipProc.waitForFinished(30000)) {
98 qWarning() <<
"[FsAtlasLookup::load] gzip decompression timed out for" << sParcellationPath;
101 if(gzipProc.exitCode() != 0) {
102 qWarning() <<
"[FsAtlasLookup::load] gzip decompression failed for" << sParcellationPath;
105 data = gzipProc.readAllStandardOutput();
108 QFile file(sParcellationPath);
109 if(!file.open(QIODevice::ReadOnly)) {
110 qWarning() <<
"[FsAtlasLookup::load] Cannot open" << sParcellationPath;
113 data = file.readAll();
117 if(data.size() < 284 + 64) {
118 qWarning() <<
"[FsAtlasLookup::load] File too small:" << sParcellationPath;
131 const char* raw = data.constData();
133 auto readInt32 = [&](
int offset) -> qint32 {
134 return qFromBigEndian<qint32>(raw + offset);
137 auto readFloat32 = [&](
int offset) ->
float {
138 return qFromBigEndian<qint32>(raw + offset);
141 qint32 version = readInt32(0);
143 qWarning() <<
"[FsAtlasLookup::load] Unsupported MGH version:" << version;
147 m_dimX = readInt32(4);
148 m_dimY = readInt32(8);
149 m_dimZ = readInt32(12);
150 qint32 nframes = readInt32(16);
151 qint32 type = readInt32(20);
153 if(m_dimX <= 0 || m_dimY <= 0 || m_dimZ <= 0) {
154 qWarning() <<
"[FsAtlasLookup::load] Invalid dimensions:" << m_dimX << m_dimY << m_dimZ;
176 qint16 rasGoodFlag = qFromBigEndian<qint16>(raw + 28);
178 Matrix4f vox2ras = Matrix4f::Identity();
180 if(rasGoodFlag > 0) {
183 auto readBEFloat = [&](
int offset) ->
float {
184 quint32 bits = qFromBigEndian<quint32>(raw + offset);
186 memcpy(&val, &bits,
sizeof(
float));
190 float xsize = readBEFloat(30);
191 float ysize = readBEFloat(34);
192 float zsize = readBEFloat(38);
195 Vector3f x_ras(readBEFloat(42), readBEFloat(46), readBEFloat(50));
196 Vector3f y_ras(readBEFloat(54), readBEFloat(58), readBEFloat(62));
197 Vector3f z_ras(readBEFloat(66), readBEFloat(70), readBEFloat(74));
200 Vector3f c_ras(readBEFloat(78), readBEFloat(82), readBEFloat(86));
211 MdcD.col(0) *= xsize;
212 MdcD.col(1) *= ysize;
213 MdcD.col(2) *= zsize;
216 Vector3f Pcrs_center(
static_cast<float>(m_dimX) / 2.0f,
217 static_cast<float>(m_dimY) / 2.0f,
218 static_cast<float>(m_dimZ) / 2.0f);
220 Vector3f Pxyz_0 = c_ras - MdcD * Pcrs_center;
222 vox2ras.block<3,3>(0,0) = MdcD;
223 vox2ras.block<3,1>(0,3) = Pxyz_0;
227 m_ras2vox = vox2ras.inverse();
230 const int headerSize = 284;
231 qint64 nVoxels =
static_cast<qint64
>(m_dimX) * m_dimY * m_dimZ * nframes;
232 m_voxelData.resize(
static_cast<int>(nVoxels));
234 const char* voxPtr = raw + headerSize;
235 qint64 dataSize = data.size() - headerSize;
239 if(dataSize < nVoxels) {
240 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for uchar volume";
243 for(qint64 i = 0; i < nVoxels; ++i)
244 m_voxelData[
static_cast<int>(i)] =
static_cast<int>(
static_cast<unsigned char>(voxPtr[i]));
248 if(dataSize < nVoxels * 4) {
249 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for int volume";
252 for(qint64 i = 0; i < nVoxels; ++i)
253 m_voxelData[
static_cast<int>(i)] = qFromBigEndian<qint32>(voxPtr + i * 4);
257 if(dataSize < nVoxels * 4) {
258 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for float volume";
261 for(qint64 i = 0; i < nVoxels; ++i) {
262 quint32 bits = qFromBigEndian<quint32>(voxPtr + i * 4);
264 memcpy(&val, &bits,
sizeof(
float));
265 m_voxelData[
static_cast<int>(i)] =
static_cast<int>(std::round(val));
270 if(dataSize < nVoxels * 2) {
271 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for short volume";
274 for(qint64 i = 0; i < nVoxels; ++i)
275 m_voxelData[
static_cast<int>(i)] = qFromBigEndian<qint16>(voxPtr + i * 2);
279 qWarning() <<
"[FsAtlasLookup::load] Unsupported MGH data type:" << type;
292 return QStringLiteral(
"Unknown");
294 Vector3i vox = rasToVoxel(ras);
297 if(vox(0) < 0 || vox(0) >= m_dimX ||
298 vox(1) < 0 || vox(1) >= m_dimY ||
299 vox(2) < 0 || vox(2) >= m_dimZ)
300 return QStringLiteral(
"Unknown");
303 int idx = vox(0) + m_dimX * (vox(1) + m_dimY * vox(2));
304 int label = m_voxelData.at(idx);
306 return m_lookupTable.value(label, QStringLiteral(
"Unknown"));
314 result.reserve(positions.size());
315 for(
const auto& pos : positions)
329Vector3i FsAtlasLookup::rasToVoxel(
const Vector3f& ras)
const
331 Vector4f rasH(ras(0), ras(1), ras(2), 1.0f);
332 Vector4f voxH = m_ras2vox * rasH;
333 return Vector3i(
static_cast<int>(std::round(voxH(0))),
334 static_cast<int>(std::round(voxH(1))),
335 static_cast<int>(std::round(voxH(2))));
340void FsAtlasLookup::initLookupTable()
343 m_lookupTable[0] = QStringLiteral(
"Unknown");
344 m_lookupTable[2] = QStringLiteral(
"Left-Cerebral-White-Matter");
345 m_lookupTable[3] = QStringLiteral(
"Left-Cerebral-Cortex");
346 m_lookupTable[4] = QStringLiteral(
"Left-Lateral-Ventricle");
347 m_lookupTable[5] = QStringLiteral(
"Left-Inf-Lat-Vent");
348 m_lookupTable[7] = QStringLiteral(
"Left-Cerebellum-White-Matter");
349 m_lookupTable[8] = QStringLiteral(
"Left-Cerebellum-Cortex");
350 m_lookupTable[10] = QStringLiteral(
"Left-Thalamus");
351 m_lookupTable[11] = QStringLiteral(
"Left-Caudate");
352 m_lookupTable[12] = QStringLiteral(
"Left-Putamen");
353 m_lookupTable[13] = QStringLiteral(
"Left-Pallidum");
354 m_lookupTable[14] = QStringLiteral(
"3rd-Ventricle");
355 m_lookupTable[15] = QStringLiteral(
"4th-Ventricle");
356 m_lookupTable[16] = QStringLiteral(
"Brain-Stem");
357 m_lookupTable[17] = QStringLiteral(
"Left-Hippocampus");
358 m_lookupTable[18] = QStringLiteral(
"Left-Amygdala");
359 m_lookupTable[24] = QStringLiteral(
"CSF");
360 m_lookupTable[26] = QStringLiteral(
"Left-Accumbens-area");
361 m_lookupTable[28] = QStringLiteral(
"Left-VentralDC");
362 m_lookupTable[30] = QStringLiteral(
"Left-vessel");
363 m_lookupTable[31] = QStringLiteral(
"Left-choroid-plexus");
364 m_lookupTable[41] = QStringLiteral(
"Right-Cerebral-White-Matter");
365 m_lookupTable[42] = QStringLiteral(
"Right-Cerebral-Cortex");
366 m_lookupTable[43] = QStringLiteral(
"Right-Lateral-Ventricle");
367 m_lookupTable[44] = QStringLiteral(
"Right-Inf-Lat-Vent");
368 m_lookupTable[46] = QStringLiteral(
"Right-Cerebellum-White-Matter");
369 m_lookupTable[47] = QStringLiteral(
"Right-Cerebellum-Cortex");
370 m_lookupTable[49] = QStringLiteral(
"Right-Thalamus");
371 m_lookupTable[50] = QStringLiteral(
"Right-Caudate");
372 m_lookupTable[51] = QStringLiteral(
"Right-Putamen");
373 m_lookupTable[52] = QStringLiteral(
"Right-Pallidum");
374 m_lookupTable[53] = QStringLiteral(
"Right-Hippocampus");
375 m_lookupTable[54] = QStringLiteral(
"Right-Amygdala");
376 m_lookupTable[58] = QStringLiteral(
"Right-Accumbens-area");
377 m_lookupTable[60] = QStringLiteral(
"Right-VentralDC");
378 m_lookupTable[62] = QStringLiteral(
"Right-vessel");
379 m_lookupTable[63] = QStringLiteral(
"Right-choroid-plexus");
380 m_lookupTable[72] = QStringLiteral(
"5th-Ventricle");
381 m_lookupTable[77] = QStringLiteral(
"WM-hypointensities");
382 m_lookupTable[80] = QStringLiteral(
"non-WM-hypointensities");
383 m_lookupTable[85] = QStringLiteral(
"Optic-Chiasm");
384 m_lookupTable[251] = QStringLiteral(
"CC_Posterior");
385 m_lookupTable[252] = QStringLiteral(
"CC_Mid_Posterior");
386 m_lookupTable[253] = QStringLiteral(
"CC_Central");
387 m_lookupTable[254] = QStringLiteral(
"CC_Mid_Anterior");
388 m_lookupTable[255] = QStringLiteral(
"CC_Anterior");
391 m_lookupTable[1001] = QStringLiteral(
"ctx-lh-bankssts");
392 m_lookupTable[1002] = QStringLiteral(
"ctx-lh-caudalanteriorcingulate");
393 m_lookupTable[1003] = QStringLiteral(
"ctx-lh-caudalmiddlefrontal");
394 m_lookupTable[1005] = QStringLiteral(
"ctx-lh-cuneus");
395 m_lookupTable[1006] = QStringLiteral(
"ctx-lh-entorhinal");
396 m_lookupTable[1007] = QStringLiteral(
"ctx-lh-fusiform");
397 m_lookupTable[1008] = QStringLiteral(
"ctx-lh-inferiorparietal");
398 m_lookupTable[1009] = QStringLiteral(
"ctx-lh-inferiortemporal");
399 m_lookupTable[1010] = QStringLiteral(
"ctx-lh-isthmuscingulate");
400 m_lookupTable[1011] = QStringLiteral(
"ctx-lh-lateraloccipital");
401 m_lookupTable[1012] = QStringLiteral(
"ctx-lh-lateralorbitofrontal");
402 m_lookupTable[1013] = QStringLiteral(
"ctx-lh-lingual");
403 m_lookupTable[1014] = QStringLiteral(
"ctx-lh-medialorbitofrontal");
404 m_lookupTable[1015] = QStringLiteral(
"ctx-lh-middletemporal");
405 m_lookupTable[1016] = QStringLiteral(
"ctx-lh-parahippocampal");
406 m_lookupTable[1017] = QStringLiteral(
"ctx-lh-paracentral");
407 m_lookupTable[1018] = QStringLiteral(
"ctx-lh-parsopercularis");
408 m_lookupTable[1019] = QStringLiteral(
"ctx-lh-parsorbitalis");
409 m_lookupTable[1020] = QStringLiteral(
"ctx-lh-parstriangularis");
410 m_lookupTable[1021] = QStringLiteral(
"ctx-lh-pericalcarine");
411 m_lookupTable[1022] = QStringLiteral(
"ctx-lh-postcentral");
412 m_lookupTable[1023] = QStringLiteral(
"ctx-lh-posteriorcingulate");
413 m_lookupTable[1024] = QStringLiteral(
"ctx-lh-precentral");
414 m_lookupTable[1025] = QStringLiteral(
"ctx-lh-precuneus");
415 m_lookupTable[1026] = QStringLiteral(
"ctx-lh-rostralanteriorcingulate");
416 m_lookupTable[1027] = QStringLiteral(
"ctx-lh-rostralmiddlefrontal");
417 m_lookupTable[1028] = QStringLiteral(
"ctx-lh-superiorfrontal");
418 m_lookupTable[1029] = QStringLiteral(
"ctx-lh-superiorparietal");
419 m_lookupTable[1030] = QStringLiteral(
"ctx-lh-superiortemporal");
420 m_lookupTable[1031] = QStringLiteral(
"ctx-lh-supramarginal");
421 m_lookupTable[1032] = QStringLiteral(
"ctx-lh-frontalpole");
422 m_lookupTable[1033] = QStringLiteral(
"ctx-lh-temporalpole");
423 m_lookupTable[1034] = QStringLiteral(
"ctx-lh-transversetemporal");
424 m_lookupTable[1035] = QStringLiteral(
"ctx-lh-insula");
427 m_lookupTable[2001] = QStringLiteral(
"ctx-rh-bankssts");
428 m_lookupTable[2002] = QStringLiteral(
"ctx-rh-caudalanteriorcingulate");
429 m_lookupTable[2003] = QStringLiteral(
"ctx-rh-caudalmiddlefrontal");
430 m_lookupTable[2005] = QStringLiteral(
"ctx-rh-cuneus");
431 m_lookupTable[2006] = QStringLiteral(
"ctx-rh-entorhinal");
432 m_lookupTable[2007] = QStringLiteral(
"ctx-rh-fusiform");
433 m_lookupTable[2008] = QStringLiteral(
"ctx-rh-inferiorparietal");
434 m_lookupTable[2009] = QStringLiteral(
"ctx-rh-inferiortemporal");
435 m_lookupTable[2010] = QStringLiteral(
"ctx-rh-isthmuscingulate");
436 m_lookupTable[2011] = QStringLiteral(
"ctx-rh-lateraloccipital");
437 m_lookupTable[2012] = QStringLiteral(
"ctx-rh-lateralorbitofrontal");
438 m_lookupTable[2013] = QStringLiteral(
"ctx-rh-lingual");
439 m_lookupTable[2014] = QStringLiteral(
"ctx-rh-medialorbitofrontal");
440 m_lookupTable[2015] = QStringLiteral(
"ctx-rh-middletemporal");
441 m_lookupTable[2016] = QStringLiteral(
"ctx-rh-parahippocampal");
442 m_lookupTable[2017] = QStringLiteral(
"ctx-rh-paracentral");
443 m_lookupTable[2018] = QStringLiteral(
"ctx-rh-parsopercularis");
444 m_lookupTable[2019] = QStringLiteral(
"ctx-rh-parsorbitalis");
445 m_lookupTable[2020] = QStringLiteral(
"ctx-rh-parstriangularis");
446 m_lookupTable[2021] = QStringLiteral(
"ctx-rh-pericalcarine");
447 m_lookupTable[2022] = QStringLiteral(
"ctx-rh-postcentral");
448 m_lookupTable[2023] = QStringLiteral(
"ctx-rh-posteriorcingulate");
449 m_lookupTable[2024] = QStringLiteral(
"ctx-rh-precentral");
450 m_lookupTable[2025] = QStringLiteral(
"ctx-rh-precuneus");
451 m_lookupTable[2026] = QStringLiteral(
"ctx-rh-rostralanteriorcingulate");
452 m_lookupTable[2027] = QStringLiteral(
"ctx-rh-rostralmiddlefrontal");
453 m_lookupTable[2028] = QStringLiteral(
"ctx-rh-superiorfrontal");
454 m_lookupTable[2029] = QStringLiteral(
"ctx-rh-superiorparietal");
455 m_lookupTable[2030] = QStringLiteral(
"ctx-rh-superiortemporal");
456 m_lookupTable[2031] = QStringLiteral(
"ctx-rh-supramarginal");
457 m_lookupTable[2032] = QStringLiteral(
"ctx-rh-frontalpole");
458 m_lookupTable[2033] = QStringLiteral(
"ctx-rh-temporalpole");
459 m_lookupTable[2034] = QStringLiteral(
"ctx-rh-transversetemporal");
460 m_lookupTable[2035] = QStringLiteral(
"ctx-rh-insula");
FsAtlasLookup class — FreeSurfer volume parcellation atlas lookup.
FreeSurfer surface and annotation I/O.
QString labelAtRas(const Eigen::Vector3f &ras) const
Look up the anatomical label at a RAS coordinate.
bool isLoaded() const
Check whether a parcellation volume has been loaded.
bool load(const QString &sParcellationPath)
Load a FreeSurfer MGH/MGZ volume parcellation.
QStringList labelsForPositions(const QVector< Eigen::Vector3f > &positions) const
Look up labels for multiple RAS positions.