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 auto readFloat32 = [&](
int offset) ->
float {
116 return qFromBigEndian<qint32>(raw + offset);
119 qint32 version = readInt32(0);
121 qWarning() <<
"[FsAtlasLookup::load] Unsupported MGH version:" << version;
125 m_dimX = readInt32(4);
126 m_dimY = readInt32(8);
127 m_dimZ = readInt32(12);
128 qint32 nframes = readInt32(16);
129 qint32 type = readInt32(20);
131 if(m_dimX <= 0 || m_dimY <= 0 || m_dimZ <= 0) {
132 qWarning() <<
"[FsAtlasLookup::load] Invalid dimensions:" << m_dimX << m_dimY << m_dimZ;
154 qint16 rasGoodFlag = qFromBigEndian<qint16>(raw + 28);
156 Matrix4f vox2ras = Matrix4f::Identity();
158 if(rasGoodFlag > 0) {
161 auto readBEFloat = [&](
int offset) ->
float {
162 quint32 bits = qFromBigEndian<quint32>(raw + offset);
164 memcpy(&val, &bits,
sizeof(
float));
168 float xsize = readBEFloat(30);
169 float ysize = readBEFloat(34);
170 float zsize = readBEFloat(38);
173 Vector3f x_ras(readBEFloat(42), readBEFloat(46), readBEFloat(50));
174 Vector3f y_ras(readBEFloat(54), readBEFloat(58), readBEFloat(62));
175 Vector3f z_ras(readBEFloat(66), readBEFloat(70), readBEFloat(74));
178 Vector3f c_ras(readBEFloat(78), readBEFloat(82), readBEFloat(86));
189 MdcD.col(0) *= xsize;
190 MdcD.col(1) *= ysize;
191 MdcD.col(2) *= zsize;
194 Vector3f Pcrs_center(
static_cast<float>(m_dimX) / 2.0f,
195 static_cast<float>(m_dimY) / 2.0f,
196 static_cast<float>(m_dimZ) / 2.0f);
198 Vector3f Pxyz_0 = c_ras - MdcD * Pcrs_center;
200 vox2ras.block<3,3>(0,0) = MdcD;
201 vox2ras.block<3,1>(0,3) = Pxyz_0;
205 m_ras2vox = vox2ras.inverse();
208 const int headerSize = 284;
209 qint64 nVoxels =
static_cast<qint64
>(m_dimX) * m_dimY * m_dimZ * nframes;
210 m_voxelData.resize(
static_cast<int>(nVoxels));
212 const char* voxPtr = raw + headerSize;
213 qint64 dataSize = data.size() - headerSize;
217 if(dataSize < nVoxels) {
218 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for uchar volume";
221 for(qint64 i = 0; i < nVoxels; ++i)
222 m_voxelData[
static_cast<int>(i)] =
static_cast<int>(
static_cast<unsigned char>(voxPtr[i]));
226 if(dataSize < nVoxels * 4) {
227 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for int volume";
230 for(qint64 i = 0; i < nVoxels; ++i)
231 m_voxelData[
static_cast<int>(i)] = qFromBigEndian<qint32>(voxPtr + i * 4);
235 if(dataSize < nVoxels * 4) {
236 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for float volume";
239 for(qint64 i = 0; i < nVoxels; ++i) {
240 quint32 bits = qFromBigEndian<quint32>(voxPtr + i * 4);
242 memcpy(&val, &bits,
sizeof(
float));
243 m_voxelData[
static_cast<int>(i)] =
static_cast<int>(std::round(val));
248 if(dataSize < nVoxels * 2) {
249 qWarning() <<
"[FsAtlasLookup::load] Insufficient data for short volume";
252 for(qint64 i = 0; i < nVoxels; ++i)
253 m_voxelData[
static_cast<int>(i)] = qFromBigEndian<qint16>(voxPtr + i * 2);
257 qWarning() <<
"[FsAtlasLookup::load] Unsupported MGH data type:" << type;
270 return QStringLiteral(
"Unknown");
272 Vector3i vox = rasToVoxel(ras);
275 if(vox(0) < 0 || vox(0) >= m_dimX ||
276 vox(1) < 0 || vox(1) >= m_dimY ||
277 vox(2) < 0 || vox(2) >= m_dimZ)
278 return QStringLiteral(
"Unknown");
281 int idx = vox(0) + m_dimX * (vox(1) + m_dimY * vox(2));
282 int label = m_voxelData.at(idx);
284 return m_lookupTable.value(label, QStringLiteral(
"Unknown"));
292 result.reserve(positions.size());
293 for(
const auto& pos : positions)
307Vector3i FsAtlasLookup::rasToVoxel(
const Vector3f& ras)
const
309 Vector4f rasH(ras(0), ras(1), ras(2), 1.0f);
310 Vector4f voxH = m_ras2vox * rasH;
311 return Vector3i(
static_cast<int>(std::round(voxH(0))),
312 static_cast<int>(std::round(voxH(1))),
313 static_cast<int>(std::round(voxH(2))));
318void FsAtlasLookup::initLookupTable()
321 m_lookupTable[0] = QStringLiteral(
"Unknown");
322 m_lookupTable[2] = QStringLiteral(
"Left-Cerebral-White-Matter");
323 m_lookupTable[3] = QStringLiteral(
"Left-Cerebral-Cortex");
324 m_lookupTable[4] = QStringLiteral(
"Left-Lateral-Ventricle");
325 m_lookupTable[5] = QStringLiteral(
"Left-Inf-Lat-Vent");
326 m_lookupTable[7] = QStringLiteral(
"Left-Cerebellum-White-Matter");
327 m_lookupTable[8] = QStringLiteral(
"Left-Cerebellum-Cortex");
328 m_lookupTable[10] = QStringLiteral(
"Left-Thalamus");
329 m_lookupTable[11] = QStringLiteral(
"Left-Caudate");
330 m_lookupTable[12] = QStringLiteral(
"Left-Putamen");
331 m_lookupTable[13] = QStringLiteral(
"Left-Pallidum");
332 m_lookupTable[14] = QStringLiteral(
"3rd-Ventricle");
333 m_lookupTable[15] = QStringLiteral(
"4th-Ventricle");
334 m_lookupTable[16] = QStringLiteral(
"Brain-Stem");
335 m_lookupTable[17] = QStringLiteral(
"Left-Hippocampus");
336 m_lookupTable[18] = QStringLiteral(
"Left-Amygdala");
337 m_lookupTable[24] = QStringLiteral(
"CSF");
338 m_lookupTable[26] = QStringLiteral(
"Left-Accumbens-area");
339 m_lookupTable[28] = QStringLiteral(
"Left-VentralDC");
340 m_lookupTable[30] = QStringLiteral(
"Left-vessel");
341 m_lookupTable[31] = QStringLiteral(
"Left-choroid-plexus");
342 m_lookupTable[41] = QStringLiteral(
"Right-Cerebral-White-Matter");
343 m_lookupTable[42] = QStringLiteral(
"Right-Cerebral-Cortex");
344 m_lookupTable[43] = QStringLiteral(
"Right-Lateral-Ventricle");
345 m_lookupTable[44] = QStringLiteral(
"Right-Inf-Lat-Vent");
346 m_lookupTable[46] = QStringLiteral(
"Right-Cerebellum-White-Matter");
347 m_lookupTable[47] = QStringLiteral(
"Right-Cerebellum-Cortex");
348 m_lookupTable[49] = QStringLiteral(
"Right-Thalamus");
349 m_lookupTable[50] = QStringLiteral(
"Right-Caudate");
350 m_lookupTable[51] = QStringLiteral(
"Right-Putamen");
351 m_lookupTable[52] = QStringLiteral(
"Right-Pallidum");
352 m_lookupTable[53] = QStringLiteral(
"Right-Hippocampus");
353 m_lookupTable[54] = QStringLiteral(
"Right-Amygdala");
354 m_lookupTable[58] = QStringLiteral(
"Right-Accumbens-area");
355 m_lookupTable[60] = QStringLiteral(
"Right-VentralDC");
356 m_lookupTable[62] = QStringLiteral(
"Right-vessel");
357 m_lookupTable[63] = QStringLiteral(
"Right-choroid-plexus");
358 m_lookupTable[72] = QStringLiteral(
"5th-Ventricle");
359 m_lookupTable[77] = QStringLiteral(
"WM-hypointensities");
360 m_lookupTable[80] = QStringLiteral(
"non-WM-hypointensities");
361 m_lookupTable[85] = QStringLiteral(
"Optic-Chiasm");
362 m_lookupTable[251] = QStringLiteral(
"CC_Posterior");
363 m_lookupTable[252] = QStringLiteral(
"CC_Mid_Posterior");
364 m_lookupTable[253] = QStringLiteral(
"CC_Central");
365 m_lookupTable[254] = QStringLiteral(
"CC_Mid_Anterior");
366 m_lookupTable[255] = QStringLiteral(
"CC_Anterior");
369 m_lookupTable[1001] = QStringLiteral(
"ctx-lh-bankssts");
370 m_lookupTable[1002] = QStringLiteral(
"ctx-lh-caudalanteriorcingulate");
371 m_lookupTable[1003] = QStringLiteral(
"ctx-lh-caudalmiddlefrontal");
372 m_lookupTable[1005] = QStringLiteral(
"ctx-lh-cuneus");
373 m_lookupTable[1006] = QStringLiteral(
"ctx-lh-entorhinal");
374 m_lookupTable[1007] = QStringLiteral(
"ctx-lh-fusiform");
375 m_lookupTable[1008] = QStringLiteral(
"ctx-lh-inferiorparietal");
376 m_lookupTable[1009] = QStringLiteral(
"ctx-lh-inferiortemporal");
377 m_lookupTable[1010] = QStringLiteral(
"ctx-lh-isthmuscingulate");
378 m_lookupTable[1011] = QStringLiteral(
"ctx-lh-lateraloccipital");
379 m_lookupTable[1012] = QStringLiteral(
"ctx-lh-lateralorbitofrontal");
380 m_lookupTable[1013] = QStringLiteral(
"ctx-lh-lingual");
381 m_lookupTable[1014] = QStringLiteral(
"ctx-lh-medialorbitofrontal");
382 m_lookupTable[1015] = QStringLiteral(
"ctx-lh-middletemporal");
383 m_lookupTable[1016] = QStringLiteral(
"ctx-lh-parahippocampal");
384 m_lookupTable[1017] = QStringLiteral(
"ctx-lh-paracentral");
385 m_lookupTable[1018] = QStringLiteral(
"ctx-lh-parsopercularis");
386 m_lookupTable[1019] = QStringLiteral(
"ctx-lh-parsorbitalis");
387 m_lookupTable[1020] = QStringLiteral(
"ctx-lh-parstriangularis");
388 m_lookupTable[1021] = QStringLiteral(
"ctx-lh-pericalcarine");
389 m_lookupTable[1022] = QStringLiteral(
"ctx-lh-postcentral");
390 m_lookupTable[1023] = QStringLiteral(
"ctx-lh-posteriorcingulate");
391 m_lookupTable[1024] = QStringLiteral(
"ctx-lh-precentral");
392 m_lookupTable[1025] = QStringLiteral(
"ctx-lh-precuneus");
393 m_lookupTable[1026] = QStringLiteral(
"ctx-lh-rostralanteriorcingulate");
394 m_lookupTable[1027] = QStringLiteral(
"ctx-lh-rostralmiddlefrontal");
395 m_lookupTable[1028] = QStringLiteral(
"ctx-lh-superiorfrontal");
396 m_lookupTable[1029] = QStringLiteral(
"ctx-lh-superiorparietal");
397 m_lookupTable[1030] = QStringLiteral(
"ctx-lh-superiortemporal");
398 m_lookupTable[1031] = QStringLiteral(
"ctx-lh-supramarginal");
399 m_lookupTable[1032] = QStringLiteral(
"ctx-lh-frontalpole");
400 m_lookupTable[1033] = QStringLiteral(
"ctx-lh-temporalpole");
401 m_lookupTable[1034] = QStringLiteral(
"ctx-lh-transversetemporal");
402 m_lookupTable[1035] = QStringLiteral(
"ctx-lh-insula");
405 m_lookupTable[2001] = QStringLiteral(
"ctx-rh-bankssts");
406 m_lookupTable[2002] = QStringLiteral(
"ctx-rh-caudalanteriorcingulate");
407 m_lookupTable[2003] = QStringLiteral(
"ctx-rh-caudalmiddlefrontal");
408 m_lookupTable[2005] = QStringLiteral(
"ctx-rh-cuneus");
409 m_lookupTable[2006] = QStringLiteral(
"ctx-rh-entorhinal");
410 m_lookupTable[2007] = QStringLiteral(
"ctx-rh-fusiform");
411 m_lookupTable[2008] = QStringLiteral(
"ctx-rh-inferiorparietal");
412 m_lookupTable[2009] = QStringLiteral(
"ctx-rh-inferiortemporal");
413 m_lookupTable[2010] = QStringLiteral(
"ctx-rh-isthmuscingulate");
414 m_lookupTable[2011] = QStringLiteral(
"ctx-rh-lateraloccipital");
415 m_lookupTable[2012] = QStringLiteral(
"ctx-rh-lateralorbitofrontal");
416 m_lookupTable[2013] = QStringLiteral(
"ctx-rh-lingual");
417 m_lookupTable[2014] = QStringLiteral(
"ctx-rh-medialorbitofrontal");
418 m_lookupTable[2015] = QStringLiteral(
"ctx-rh-middletemporal");
419 m_lookupTable[2016] = QStringLiteral(
"ctx-rh-parahippocampal");
420 m_lookupTable[2017] = QStringLiteral(
"ctx-rh-paracentral");
421 m_lookupTable[2018] = QStringLiteral(
"ctx-rh-parsopercularis");
422 m_lookupTable[2019] = QStringLiteral(
"ctx-rh-parsorbitalis");
423 m_lookupTable[2020] = QStringLiteral(
"ctx-rh-parstriangularis");
424 m_lookupTable[2021] = QStringLiteral(
"ctx-rh-pericalcarine");
425 m_lookupTable[2022] = QStringLiteral(
"ctx-rh-postcentral");
426 m_lookupTable[2023] = QStringLiteral(
"ctx-rh-posteriorcingulate");
427 m_lookupTable[2024] = QStringLiteral(
"ctx-rh-precentral");
428 m_lookupTable[2025] = QStringLiteral(
"ctx-rh-precuneus");
429 m_lookupTable[2026] = QStringLiteral(
"ctx-rh-rostralanteriorcingulate");
430 m_lookupTable[2027] = QStringLiteral(
"ctx-rh-rostralmiddlefrontal");
431 m_lookupTable[2028] = QStringLiteral(
"ctx-rh-superiorfrontal");
432 m_lookupTable[2029] = QStringLiteral(
"ctx-rh-superiorparietal");
433 m_lookupTable[2030] = QStringLiteral(
"ctx-rh-superiortemporal");
434 m_lookupTable[2031] = QStringLiteral(
"ctx-rh-supramarginal");
435 m_lookupTable[2032] = QStringLiteral(
"ctx-rh-frontalpole");
436 m_lookupTable[2033] = QStringLiteral(
"ctx-rh-temporalpole");
437 m_lookupTable[2034] = QStringLiteral(
"ctx-rh-transversetemporal");
438 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.