27#include <QCoreApplication>
46Eigen::Vector3f applyTransform(
const Eigen::Vector3f& point,
51 float r[3] = {point.x(), point.y(), point.z()};
53 return Eigen::Vector3f(r[0], r[1], r[2]);
62 for (
int r = 0; r < 4; ++r)
63 for (
int c = 0; c < 4; ++c)
83 QMutexLocker locker(&m_mutex);
85 m_hasEvoked = (evoked.
data.rows() > 0 && evoked.
data.cols() > 0);
91 bool applySensorTrans)
93 QMutexLocker locker(&m_mutex);
94 m_headToMriTrans = trans;
95 m_applySensorTrans = applySensorTrans;
102 QMutexLocker locker(&m_mutex);
103 m_megOnHead = onHead;
109 const Eigen::MatrixX3f& vertices,
110 const Eigen::MatrixX3f& normals,
111 const Eigen::MatrixX3i& triangles)
113 QMutexLocker locker(&m_mutex);
114 m_megSurfaceKey = surfaceKey;
115 m_megVertices = vertices;
116 m_megNormals = normals;
117 m_megTriangles = triangles;
118 m_hasMegSurface = (vertices.rows() > 0);
124 const Eigen::MatrixX3f& vertices)
126 QMutexLocker locker(&m_mutex);
127 m_eegSurfaceKey = surfaceKey;
128 m_eegVertices = vertices;
129 m_hasEegSurface = (vertices.rows() > 0);
136 QMutexLocker locker(&m_mutex);
147 bool applySensorTrans;
149 QString megSurfaceKey, eegSurfaceKey;
150 Eigen::MatrixX3f megVerts, megNorms, eegVerts;
151 Eigen::MatrixX3i megTris;
152 bool hasMegSurface, hasEegSurface, hasEvoked;
156 QMutexLocker locker(&m_mutex);
158 hasEvoked = m_hasEvoked;
159 headToMriTrans = m_headToMriTrans;
160 applySensorTrans = m_applySensorTrans;
161 megOnHead = m_megOnHead;
162 megSurfaceKey = m_megSurfaceKey;
163 eegSurfaceKey = m_eegSurfaceKey;
164 megVerts = m_megVertices;
165 megNorms = m_megNormals;
166 megTris = m_megTriangles;
167 hasMegSurface = m_hasMegSurface;
168 eegVerts = m_eegVertices;
169 hasEegSurface = m_hasEegSurface;
174 qDebug() <<
"RtSensorInterpolationMatWorker: No evoked data set, skipping.";
178 qDebug() <<
"RtSensorInterpolationMatWorker: Computing mapping matrices...";
181 bool hasDevHead =
false;
182 QMatrix4x4 devHeadQt;
191 QMatrix4x4 headToMri;
192 if (applySensorTrans && !headToMriTrans.
isEmpty()) {
193 headToMri = toQMatrix4x4(headToMriTrans.
trans);
197 QList<FiffChInfo> megChs, eegChs;
198 QVector<int> megPick, eegPick;
200 for (
int k = 0; k < evoked.
info.
chs.size(); ++k) {
201 const auto& ch = evoked.
info.
chs[k];
202 if (bads.contains(ch.ch_name) || evoked.
info.
bads.contains(ch.ch_name))
205 QVector3D pos(ch.chpos.r0(0), ch.chpos.r0(1), ch.chpos.r0(2));
209 pos = devHeadQt.map(pos);
210 if (applySensorTrans && !headToMriTrans.
isEmpty())
211 pos = headToMri.map(pos);
215 if (applySensorTrans && !headToMriTrans.
isEmpty())
216 pos = headToMri.map(pos);
223 constexpr float kIntrad = 0.06f;
224 constexpr float kMegMiss = 1e-4f;
225 constexpr float kEegMiss = 1e-3f;
239 if (hasMegSurface && !megChs.isEmpty()) {
240 Eigen::MatrixX3f norms = megNorms;
243 if (norms.rows() != megVerts.rows()) {
244 if (megTris.rows() > 0) {
249 if (megVerts.rows() > 0 && norms.rows() == megVerts.rows()) {
250 const QString coilPath = QCoreApplication::applicationDirPath() +
"/../resources/general/coilDefinitions/coil_def.dat";
256 if (megOnHead && !headMri.
isEmpty()) {
262 }
else if (!devHead.
isEmpty()) {
263 devToTarget = devHead;
266 Eigen::Vector3f origin = fittedOrigin;
267 if (megOnHead && !headMri.
isEmpty()) {
268 origin = applyTransform(origin, headMri);
271 auto coils = templates->create_meg_coils(
274 if (coils && coils->ncoil() > 0) {
276 *coils, megVerts, norms, origin, kIntrad, kMegMiss);
278 if (mat && mat->rows() > 0) {
279 qDebug() <<
"RtSensorInterpolationMatWorker: MEG mapping computed:"
280 << mat->rows() <<
"x" << mat->cols();
282 std::shared_ptr<Eigen::MatrixXf>(std::move(mat)), megPick);
290 if (hasEegSurface && !eegChs.isEmpty()) {
291 if (eegVerts.rows() > 0) {
292 Eigen::Vector3f origin = fittedOrigin;
294 origin = applyTransform(origin, headMri);
298 eegChs, eegChs.size(), headMri);
300 if (eegCoils && eegCoils->ncoil() > 0) {
302 *eegCoils, eegVerts, origin, kIntrad, kEegMiss);
304 if (mat && mat->rows() > 0) {
305 qDebug() <<
"RtSensorInterpolationMatWorker: EEG mapping computed:"
306 << mat->rows() <<
"x" << mat->cols();
308 std::shared_ptr<Eigen::MatrixXf>(std::move(mat)), eegPick);
314 qDebug() <<
"RtSensorInterpolationMatWorker: Mapping computation complete.";
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
constexpr auto FIFFV_COORD_HEAD
constexpr auto FIFFV_MOVE
constexpr auto FIFFV_COORD_DEVICE
constexpr auto FIFFV_COORD_MRI
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
return FiffCoordTrans(from_frame, to_frame, R, moveVec)
Background worker that recomputes the dense MEG / EEG sensor-to-surface mapping matrix off the GUI th...
Builds the dense sensor-to-surface mapping matrix and the iso-contour overlay for MEG / EEG evoked da...
Container of FwdCoil instances representing either a sensor-type template database or a concrete per-...
Sphere-model field interpolator that maps measured MEG/EEG values onto a dense scalp or cortical surf...
Reader and in-memory representation of a single FreeSurfer triangular surface (e.g....
FIFF file I/O, in-memory data structures and high-level readers/writers.
3-D brain visualisation using the Qt RHI rendering backend.
QMatrix4x4 toQMatrix4x4(const Eigen::Matrix4f &m)
constexpr int FWD_COIL_ACCURACY_NORMAL
static Eigen::Vector3f fitSphereOrigin(const FIFFLIB::FiffInfo &info, float *radius=nullptr)
void setEvoked(const FIFFLIB::FiffEvoked &evoked)
RtSensorInterpolationMatWorker(QObject *parent=nullptr)
void newEegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
void setMegSurface(const QString &surfaceKey, const Eigen::MatrixX3f &vertices, const Eigen::MatrixX3f &normals, const Eigen::MatrixX3i &triangles)
void setTransform(const FIFFLIB::FiffCoordTrans &trans, bool applySensorTrans)
void newMegMappingAvailable(const QString &surfaceKey, std::shared_ptr< Eigen::MatrixXf > mappingMat, const QVector< int > &pick)
void setMegFieldMapOnHead(bool onHead)
void setEegSurface(const QString &surfaceKey, const Eigen::MatrixX3f &vertices)
void setBadChannels(const QStringList &bads)
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static FiffCoordTrans combine(int from, int to, const FiffCoordTrans &t1, const FiffCoordTrans &t2)
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::Matrix< float, 4, 4, Eigen::DontAlign > trans
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
FiffCoordTrans dev_head_t
static Eigen::MatrixX3f compute_normals(const Eigen::MatrixX3f &rr, const Eigen::MatrixX3i &tris)
static FwdCoilSet::UPtr read_coil_defs(const QString &name)
static FwdCoilSet::UPtr create_eeg_els(const QList< FIFFLIB::FiffChInfo > &chs, int nch, const FIFFLIB::FiffCoordTrans &t=FIFFLIB::FiffCoordTrans())
static std::unique_ptr< Eigen::MatrixXf > computeEegMapping(const FwdCoilSet &coils, const Eigen::MatrixX3f &vertices, const Eigen::Vector3f &origin, float intrad=0.06f, float miss=1e-3f)
static std::unique_ptr< Eigen::MatrixXf > computeMegMapping(const FwdCoilSet &coils, const Eigen::MatrixX3f &vertices, const Eigen::MatrixX3f &normals, const Eigen::Vector3f &origin, float intrad=0.06f, float miss=1e-4f)