49: m_ras2vox(Matrix4f::Identity())
67 if (sParcellationPath.endsWith(QStringLiteral(
".mgz"), Qt::CaseInsensitive)) {
69 qWarning() <<
"[FsAtlasLookup::load] .mgz files not supported in WebAssembly build (QProcess unavailable):" << sParcellationPath;
74 gzipProc.start(QStringLiteral(
"gzip"), QStringList() << QStringLiteral(
"-dc") << sParcellationPath);
75 if (!gzipProc.waitForFinished(30000)) {
76 qWarning() <<
"[FsAtlasLookup::load] gzip decompression timed out for" << sParcellationPath;
79 if (gzipProc.exitCode() != 0) {
80 qWarning() <<
"[FsAtlasLookup::load] gzip decompression failed for" << sParcellationPath;
83 data = gzipProc.readAllStandardOutput();
86 QFile file(sParcellationPath);
87 if (!file.open(QIODevice::ReadOnly)) {
88 qWarning() <<
"[FsAtlasLookup::load] Cannot open" << sParcellationPath;
91 data = file.readAll();
95 if (data.size() < 284 + 64) {
96 qWarning() <<
"[FsAtlasLookup::load] File too small:" << sParcellationPath;
109 const char* raw = data.constData();
111 auto readInt32 = [&](
int offset) -> qint32 {
112 return qFromBigEndian<qint32>(raw + offset);
115 qint32 version = readInt32(0);
117 qWarning() <<
"[FsAtlasLookup::load] Unsupported MGH version:" << version;
121 m_dimX = readInt32(4);
122 m_dimY = readInt32(8);
123 m_dimZ = readInt32(12);
124 qint32 nframes = readInt32(16);
125 qint32 type = readInt32(20);
127 if (m_dimX <= 0 || m_dimY <= 0 || m_dimZ <= 0) {
128 qWarning() <<
"[FsAtlasLookup::load] Invalid dimensions:" << m_dimX << m_dimY << m_dimZ;
150 qint16 rasGoodFlag = qFromBigEndian<qint16>(raw + 28);
152 Matrix4f vox2ras = Matrix4f::Identity();
154 if (rasGoodFlag > 0) {
157 auto readBEFloat = [&](
int offset) ->
float {
158 quint32 bits = qFromBigEndian<quint32>(raw + offset);
160 memcpy(&val, &bits,
sizeof(
float));
164 float xsize = readBEFloat(30);
165 float ysize = readBEFloat(34);
166 float zsize = readBEFloat(38);
169 Vector3f x_ras(readBEFloat(42), readBEFloat(46), readBEFloat(50));
170 Vector3f y_ras(readBEFloat(54), readBEFloat(58), readBEFloat(62));
171 Vector3f z_ras(readBEFloat(66), readBEFloat(70), readBEFloat(74));
174 Vector3f c_ras(readBEFloat(78), readBEFloat(82), readBEFloat(86));
185 MdcD.col(0) *= xsize;
186 MdcD.col(1) *= ysize;
187 MdcD.col(2) *= zsize;
190 Vector3f Pcrs_center(
static_cast<float>(m_dimX) / 2.0f,
191 static_cast<float>(m_dimY) / 2.0f,
192 static_cast<float>(m_dimZ) / 2.0f);
194 Vector3f Pxyz_0 = c_ras - MdcD * Pcrs_center;
196 vox2ras.block<3, 3>(0, 0) = MdcD;
197 vox2ras.block<3, 1>(0, 3) = Pxyz_0;
201 m_ras2vox = vox2ras.inverse();
204 const int headerSize = 284;
205 qint64 nVoxels =
static_cast<qint64
>(m_dimX) * m_dimY * m_dimZ * nframes;
206 m_voxelData.resize(
static_cast<int>(nVoxels));
208 const char* voxPtr = raw + headerSize;
209 qint64 dataSize = data.size() - headerSize;
213 if (dataSize < nVoxels) {
214 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for uchar volume";
217 for (qint64 i = 0; i < nVoxels; ++i)
218 m_voxelData[
static_cast<int>(i)] =
static_cast<int>(
static_cast<unsigned char>(voxPtr[i]));
222 if (dataSize < nVoxels * 4) {
223 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for int volume";
226 for (qint64 i = 0; i < nVoxels; ++i)
227 m_voxelData[
static_cast<int>(i)] = qFromBigEndian<qint32>(voxPtr + i * 4);
231 if (dataSize < nVoxels * 4) {
232 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for float volume";
235 for (qint64 i = 0; i < nVoxels; ++i) {
236 quint32 bits = qFromBigEndian<quint32>(voxPtr + i * 4);
238 memcpy(&val, &bits,
sizeof(
float));
239 m_voxelData[
static_cast<int>(i)] =
static_cast<int>(std::round(val));
244 if (dataSize < nVoxels * 2) {
245 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for short volume";
248 for (qint64 i = 0; i < nVoxels; ++i)
249 m_voxelData[
static_cast<int>(i)] = qFromBigEndian<qint16>(voxPtr + i * 2);
253 qWarning() <<
"[FsAtlasLookup::load] Unsupported MGH data type:" << type;
266 return QStringLiteral(
"Unknown");
268 Vector3i vox = rasToVoxel(ras);
271 if (vox(0) < 0 || vox(0) >= m_dimX ||
272 vox(1) < 0 || vox(1) >= m_dimY ||
273 vox(2) < 0 || vox(2) >= m_dimZ)
274 return QStringLiteral(
"Unknown");
277 int idx = vox(0) + m_dimX * (vox(1) + m_dimY * vox(2));
278 int label = m_voxelData.at(idx);
280 return m_lookupTable.value(label, QStringLiteral(
"Unknown"));
288 result.reserve(positions.size());
289 for (
const auto& pos : positions)
303Vector3i FsAtlasLookup::rasToVoxel(
const Vector3f& ras)
const
305 Vector4f rasH(ras(0), ras(1), ras(2), 1.0f);
306 Vector4f voxH = m_ras2vox * rasH;
307 return Vector3i(
static_cast<int>(std::round(voxH(0))),
308 static_cast<int>(std::round(voxH(1))),
309 static_cast<int>(std::round(voxH(2))));
314void FsAtlasLookup::initLookupTable()
317 m_lookupTable[0] = QStringLiteral(
"Unknown");
318 m_lookupTable[2] = QStringLiteral(
"Left-Cerebral-White-Matter");
319 m_lookupTable[3] = QStringLiteral(
"Left-Cerebral-Cortex");
320 m_lookupTable[4] = QStringLiteral(
"Left-Lateral-Ventricle");
321 m_lookupTable[5] = QStringLiteral(
"Left-Inf-Lat-Vent");
322 m_lookupTable[7] = QStringLiteral(
"Left-Cerebellum-White-Matter");
323 m_lookupTable[8] = QStringLiteral(
"Left-Cerebellum-Cortex");
324 m_lookupTable[10] = QStringLiteral(
"Left-Thalamus");
325 m_lookupTable[11] = QStringLiteral(
"Left-Caudate");
326 m_lookupTable[12] = QStringLiteral(
"Left-Putamen");
327 m_lookupTable[13] = QStringLiteral(
"Left-Pallidum");
328 m_lookupTable[14] = QStringLiteral(
"3rd-Ventricle");
329 m_lookupTable[15] = QStringLiteral(
"4th-Ventricle");
330 m_lookupTable[16] = QStringLiteral(
"Brain-Stem");
331 m_lookupTable[17] = QStringLiteral(
"Left-Hippocampus");
332 m_lookupTable[18] = QStringLiteral(
"Left-Amygdala");
333 m_lookupTable[24] = QStringLiteral(
"CSF");
334 m_lookupTable[26] = QStringLiteral(
"Left-Accumbens-area");
335 m_lookupTable[28] = QStringLiteral(
"Left-VentralDC");
336 m_lookupTable[30] = QStringLiteral(
"Left-vessel");
337 m_lookupTable[31] = QStringLiteral(
"Left-choroid-plexus");
338 m_lookupTable[41] = QStringLiteral(
"Right-Cerebral-White-Matter");
339 m_lookupTable[42] = QStringLiteral(
"Right-Cerebral-Cortex");
340 m_lookupTable[43] = QStringLiteral(
"Right-Lateral-Ventricle");
341 m_lookupTable[44] = QStringLiteral(
"Right-Inf-Lat-Vent");
342 m_lookupTable[46] = QStringLiteral(
"Right-Cerebellum-White-Matter");
343 m_lookupTable[47] = QStringLiteral(
"Right-Cerebellum-Cortex");
344 m_lookupTable[49] = QStringLiteral(
"Right-Thalamus");
345 m_lookupTable[50] = QStringLiteral(
"Right-Caudate");
346 m_lookupTable[51] = QStringLiteral(
"Right-Putamen");
347 m_lookupTable[52] = QStringLiteral(
"Right-Pallidum");
348 m_lookupTable[53] = QStringLiteral(
"Right-Hippocampus");
349 m_lookupTable[54] = QStringLiteral(
"Right-Amygdala");
350 m_lookupTable[58] = QStringLiteral(
"Right-Accumbens-area");
351 m_lookupTable[60] = QStringLiteral(
"Right-VentralDC");
352 m_lookupTable[62] = QStringLiteral(
"Right-vessel");
353 m_lookupTable[63] = QStringLiteral(
"Right-choroid-plexus");
354 m_lookupTable[72] = QStringLiteral(
"5th-Ventricle");
355 m_lookupTable[77] = QStringLiteral(
"WM-hypointensities");
356 m_lookupTable[80] = QStringLiteral(
"non-WM-hypointensities");
357 m_lookupTable[85] = QStringLiteral(
"Optic-Chiasm");
358 m_lookupTable[251] = QStringLiteral(
"CC_Posterior");
359 m_lookupTable[252] = QStringLiteral(
"CC_Mid_Posterior");
360 m_lookupTable[253] = QStringLiteral(
"CC_Central");
361 m_lookupTable[254] = QStringLiteral(
"CC_Mid_Anterior");
362 m_lookupTable[255] = QStringLiteral(
"CC_Anterior");
365 m_lookupTable[1001] = QStringLiteral(
"ctx-lh-bankssts");
366 m_lookupTable[1002] = QStringLiteral(
"ctx-lh-caudalanteriorcingulate");
367 m_lookupTable[1003] = QStringLiteral(
"ctx-lh-caudalmiddlefrontal");
368 m_lookupTable[1005] = QStringLiteral(
"ctx-lh-cuneus");
369 m_lookupTable[1006] = QStringLiteral(
"ctx-lh-entorhinal");
370 m_lookupTable[1007] = QStringLiteral(
"ctx-lh-fusiform");
371 m_lookupTable[1008] = QStringLiteral(
"ctx-lh-inferiorparietal");
372 m_lookupTable[1009] = QStringLiteral(
"ctx-lh-inferiortemporal");
373 m_lookupTable[1010] = QStringLiteral(
"ctx-lh-isthmuscingulate");
374 m_lookupTable[1011] = QStringLiteral(
"ctx-lh-lateraloccipital");
375 m_lookupTable[1012] = QStringLiteral(
"ctx-lh-lateralorbitofrontal");
376 m_lookupTable[1013] = QStringLiteral(
"ctx-lh-lingual");
377 m_lookupTable[1014] = QStringLiteral(
"ctx-lh-medialorbitofrontal");
378 m_lookupTable[1015] = QStringLiteral(
"ctx-lh-middletemporal");
379 m_lookupTable[1016] = QStringLiteral(
"ctx-lh-parahippocampal");
380 m_lookupTable[1017] = QStringLiteral(
"ctx-lh-paracentral");
381 m_lookupTable[1018] = QStringLiteral(
"ctx-lh-parsopercularis");
382 m_lookupTable[1019] = QStringLiteral(
"ctx-lh-parsorbitalis");
383 m_lookupTable[1020] = QStringLiteral(
"ctx-lh-parstriangularis");
384 m_lookupTable[1021] = QStringLiteral(
"ctx-lh-pericalcarine");
385 m_lookupTable[1022] = QStringLiteral(
"ctx-lh-postcentral");
386 m_lookupTable[1023] = QStringLiteral(
"ctx-lh-posteriorcingulate");
387 m_lookupTable[1024] = QStringLiteral(
"ctx-lh-precentral");
388 m_lookupTable[1025] = QStringLiteral(
"ctx-lh-precuneus");
389 m_lookupTable[1026] = QStringLiteral(
"ctx-lh-rostralanteriorcingulate");
390 m_lookupTable[1027] = QStringLiteral(
"ctx-lh-rostralmiddlefrontal");
391 m_lookupTable[1028] = QStringLiteral(
"ctx-lh-superiorfrontal");
392 m_lookupTable[1029] = QStringLiteral(
"ctx-lh-superiorparietal");
393 m_lookupTable[1030] = QStringLiteral(
"ctx-lh-superiortemporal");
394 m_lookupTable[1031] = QStringLiteral(
"ctx-lh-supramarginal");
395 m_lookupTable[1032] = QStringLiteral(
"ctx-lh-frontalpole");
396 m_lookupTable[1033] = QStringLiteral(
"ctx-lh-temporalpole");
397 m_lookupTable[1034] = QStringLiteral(
"ctx-lh-transversetemporal");
398 m_lookupTable[1035] = QStringLiteral(
"ctx-lh-insula");
401 m_lookupTable[2001] = QStringLiteral(
"ctx-rh-bankssts");
402 m_lookupTable[2002] = QStringLiteral(
"ctx-rh-caudalanteriorcingulate");
403 m_lookupTable[2003] = QStringLiteral(
"ctx-rh-caudalmiddlefrontal");
404 m_lookupTable[2005] = QStringLiteral(
"ctx-rh-cuneus");
405 m_lookupTable[2006] = QStringLiteral(
"ctx-rh-entorhinal");
406 m_lookupTable[2007] = QStringLiteral(
"ctx-rh-fusiform");
407 m_lookupTable[2008] = QStringLiteral(
"ctx-rh-inferiorparietal");
408 m_lookupTable[2009] = QStringLiteral(
"ctx-rh-inferiortemporal");
409 m_lookupTable[2010] = QStringLiteral(
"ctx-rh-isthmuscingulate");
410 m_lookupTable[2011] = QStringLiteral(
"ctx-rh-lateraloccipital");
411 m_lookupTable[2012] = QStringLiteral(
"ctx-rh-lateralorbitofrontal");
412 m_lookupTable[2013] = QStringLiteral(
"ctx-rh-lingual");
413 m_lookupTable[2014] = QStringLiteral(
"ctx-rh-medialorbitofrontal");
414 m_lookupTable[2015] = QStringLiteral(
"ctx-rh-middletemporal");
415 m_lookupTable[2016] = QStringLiteral(
"ctx-rh-parahippocampal");
416 m_lookupTable[2017] = QStringLiteral(
"ctx-rh-paracentral");
417 m_lookupTable[2018] = QStringLiteral(
"ctx-rh-parsopercularis");
418 m_lookupTable[2019] = QStringLiteral(
"ctx-rh-parsorbitalis");
419 m_lookupTable[2020] = QStringLiteral(
"ctx-rh-parstriangularis");
420 m_lookupTable[2021] = QStringLiteral(
"ctx-rh-pericalcarine");
421 m_lookupTable[2022] = QStringLiteral(
"ctx-rh-postcentral");
422 m_lookupTable[2023] = QStringLiteral(
"ctx-rh-posteriorcingulate");
423 m_lookupTable[2024] = QStringLiteral(
"ctx-rh-precentral");
424 m_lookupTable[2025] = QStringLiteral(
"ctx-rh-precuneus");
425 m_lookupTable[2026] = QStringLiteral(
"ctx-rh-rostralanteriorcingulate");
426 m_lookupTable[2027] = QStringLiteral(
"ctx-rh-rostralmiddlefrontal");
427 m_lookupTable[2028] = QStringLiteral(
"ctx-rh-superiorfrontal");
428 m_lookupTable[2029] = QStringLiteral(
"ctx-rh-superiorparietal");
429 m_lookupTable[2030] = QStringLiteral(
"ctx-rh-superiortemporal");
430 m_lookupTable[2031] = QStringLiteral(
"ctx-rh-supramarginal");
431 m_lookupTable[2032] = QStringLiteral(
"ctx-rh-frontalpole");
432 m_lookupTable[2033] = QStringLiteral(
"ctx-rh-temporalpole");
433 m_lookupTable[2034] = QStringLiteral(
"ctx-rh-transversetemporal");
434 m_lookupTable[2035] = QStringLiteral(
"ctx-rh-insula");
RAS-coordinate lookup against a FreeSurfer volumetric parcellation (e.g. aparc+aseg....
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
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.