InvHpiFit
Namespace: INVERSELIB · Library: Inverse Library
mne.chpi in MNE-Python.
#include <inv/inv_hpi_fit.h>
class INVLIB::InvHpiFit
Drives one HPI fit: per-coil magnetic-dipole localisation, frequency matching against the digitised coil layout and Procrustes solve for the dewar-to-head transform.
The class is reusable across consecutive measurement blocks and caches the sensor geometry to avoid recomputing it on every call.
Drives one HPI fit (per-coil dipole localisation, coil ordering, dewar-to-head transform).
Public Methods
InvHpiFit()
Default constructor.
InvHpiFit(sensorSet)
Constructs the HPI from a InvSensorSet.
Parameters:
- InvSensorSet The MEG sensorSet used for the hpi fitting.
checkForUpdate(sensorSet)
Checks if InvSensorSet has changed and updates member InvSensorSet.
Parameters:
- InvSensorSet The MEG sensorSet used for the hpi fitting.
fit(matProjectedData, matProjectors, hpiModelParameters, matCoilsHead, hpiFitResult)
Perform one single HPI fit.
Parameters:
-
matProjectedData : const Eigen::MatrixXd & Data to estimate the HPI positions from. Projectars should be already applied.
-
matProjectors : const Eigen::MatrixXd & The projectors to apply.
-
hpiModelParameters : const InvHpiModelParameters & The model parameters to use for the Hpi Fitting, especially to compute the coil amplitudes.
-
matCoilsHead : const Eigen::MatrixXd & The hpi coil locations in head space.
-
bOrderFrequencies Order Hpi coils yes/no.
-
hpiFitResult : HpiFitResult & The fitting results.
fit(matProjectedData, matProjectors, hpiModelParameters, matCoilsHead, bOrderFrequencies, hpiFitResult)
Static Methods
storeHeadPosition(fTime, matTransDevHead, matPosition, vecGoF, vecError)
Store results from dev_Head_t as quaternions in position matrix.
The format is the same as you get from Neuromag's MaxFilter.
Parameters:
-
fTime : float The corresponding time in the measurement for the fit.
-
matTransDevHead : const Eigen::MatrixXf & The device->head transformation matrix.
-
matPosition : Eigen::MatrixXd & The matrix to store the results.
-
vecGoF : const Eigen::VectorXd & The goodness of fit per coil.
-
vecError : const QVector< double > & The Hpi estimation Error per coil.
compareTransformation(mDevHeadT, mDevHeadDest, fThreshRot, fThreshTrans)
Compares two 4x4 transformation matrices by evaluating their rotation and translation differences against given thresholds.
Parameters:
-
mDevHeadT : const Eigen::MatrixX4f & The first transformation matrix.
-
mDevHeadDest : const Eigen::MatrixX4f & The second (destination) transformation matrix.
-
fThreshRot : const float & The rotation threshold in degrees.
-
fThreshTrans : const float & The translation threshold in meters.
Returns:
- bool — True if the movement exceeds either threshold.
Example
Source: src/examples/ex_hpiFit/main.cpp
#include <iostream>
#include <vector>
#include <fiff/fiff.h>
#include <fiff/fiff_info.h>
#include <fiff/fiff_dig_point_set.h>
#include <inv/hpi/inv_hpi_fit.h>
#include <inv/hpi/inv_hpi_data_updater.h>
#include <utils/ioutils.h>
#include <utils/generics/mne_logger.h>
#include <fwd/fwd_coil_set.h>
//=============================================================================================================
// Qt INCLUDES
//=============================================================================================================
#include <QtCore/QCoreApplication>
#include <QFile>
#include <QCommandLineParser>
#include <QDebug>
#include <QGenericMatrix>
#include <QElapsedTimer>
//=============================================================================================================
// USED NAMESPACES
//=============================================================================================================
using namespace INVLIB;
using namespace FIFFLIB;
using namespace UTILSLIB;
using namespace Eigen;
//=============================================================================================================
// MAIN
//=============================================================================================================
//=============================================================================================================
/**
* Hpi fitting example to facilitade the use of the HPI fitting class. This example can further be used as CL tool
* to get an impression of head movements and the quality of the fitting.
*
* Example:
*
* ex_hpiFit --fileIn C:/Git/mne-cpp/bin/MNE-sample-data/chpi/raw/phantom/2khz_3.fif --freqs 293,307,314,321 --verbose 1 --fileOut 2k_3 --buffer 600 --save 1
*
* By default, the example uses the resources/data/mne-cpp-test-data set.
*
*/
int main(int argc, char *argv[])
{
qInstallMessageHandler(MNELogger::customLogWriter);
QElapsedTimer timer;
QCoreApplication a(argc, argv);
// Command Line Parser
QCommandLineParser parser;
parser.setApplicationDescription("hpiFit Example");
parser.addHelpOption();
qInfo() << "Please download the mne-cpp-test-data folder from Github (mne-tools) into mne-cpp/resources/data.";
QCommandLineOption inFile("fileIn", "The input file.", "in", QCoreApplication::applicationDirPath() + "../resources/data/mne-cpp-test-data/MEG/sample/test_hpiFit_raw.fif");
QCommandLineOption inWindow("window", "The window size for the HPI fit in ms.", "in","400");
QCommandLineOption inStep("step", "The step size in ms.", "in","10");
QCommandLineOption inFreqs("freqs", "The frequencies used.", "in","154,158,161,166");
QCommandLineOption inSave("save", "Store the fitting results [0,1].", "in","0");
QCommandLineOption inVerbose("verbose", "Print to command line [0,1].", "in","1");
QCommandLineOption inFast("fast", "Do fast fits [0,1].", "in","0");
QCommandLineOption outName("fileOut", "The output file name for movement data.", "out","position.txt");
parser.addOption(inFile);
parser.addOption(inWindow);
parser.addOption(inStep);
parser.addOption(inFreqs);
parser.addOption(inSave);
parser.addOption(inVerbose);
parser.addOption(inFast);
parser.addOption(outName);
parser.process(a);
float fWindow = parser.value(inWindow).toFloat()/1000.0; // convert to seconds
if(fWindow <= 0.0) {
// check for proper buffer size
qWarning() << "Window <= 0. Window was set to 200 ms.";
fWindow = 0.2f;
}
float fStep = parser.value(inStep).toFloat()/1000; // convert to seconds
if(fStep <= 0.0) {
// check for proper step size
qWarning() << "Step <= 0. Step size was set to 0.1 seconds.";
fStep = 0.1f;
}
QStringList lFreqs = parser.value(inFreqs).split(",");
QFile t_fileIn(parser.value(inFile));
bool bSave = parser.value(inSave).toInt();
bool bVerbose = parser.value(inVerbose).toInt();
bool bFast = parser.value(inFast).toInt();
QString sNameOut(parser.value(outName));
// Init data loading and writing
FiffRawData raw(t_fileIn);
QSharedPointer<FiffInfo> pFiffInfo = QSharedPointer<FiffInfo>::create(raw.info);
// Setup comparison of transformation matrices
FiffCoordTrans transDevHead = pFiffInfo->dev_head_t; // transformation that only updates after big head movements
float fThreshRot = 5.0f; // in degree
float fThreshTrans = 0.005f; // in m
// Set up the reading parameters
RowVectorXi vecPicks = pFiffInfo->pick_types(true, false, false);
MatrixXd matData, matTimes;
fiff_int_t from, to;
fiff_int_t first = raw.first_samp;
fiff_int_t last = raw.last_samp;
// create time vector that specifies when to fit
int iQuantum = floor(static_cast<double>(fWindow)*pFiffInfo->sfreq); // window size
int iQuantumT = floor(static_cast<double>(fStep)*pFiffInfo->sfreq); // samples between fits
int iN = floor((last-first)/iQuantumT)-floor(iQuantum/iQuantumT);
RowVectorXf vecTime = RowVectorXf::LinSpaced(iN, 0, iN-1);
// matPosition matrix to save hpi fit results
MatrixXd matPosition;
// setup informations for HPI fit (VectorView)
QVector<int> vecFreqs(lFreqs.size());
for(int i = 0; i < lFreqs.size(); i++) {
vecFreqs[i] = lFreqs[i].toInt();
}
QVector<double> vecError(lFreqs.size());
VectorXd vecGoF;
FiffDigPointSet fittedPointSet;
// setup Projectors
// Use SSP + SGM + calibration
MatrixXd matProjectors = MatrixXd::Identity(pFiffInfo->chs.size(), pFiffInfo->chs.size());
//Do a copy here because we are going to change the activity flags of the SSP's
FiffInfo infoTemp = *(pFiffInfo.data());
//Turn on all SSP
for(int i = 0; i < infoTemp.projs.size(); ++i) {
infoTemp.projs[i].active = true;
}
//Create the projector for all SSP's on
infoTemp.make_projector(matProjectors);
//set columns of matrix to zero depending on bad channels indexes
for(qint32 j = 0; j < infoTemp.bads.size(); ++j) {
matProjectors.col(infoTemp.ch_names.indexOf(infoTemp.bads.at(j))).setZero();
}
// if debugging files are necessary set bDoDebug = true;
QString sHPIResourceDir = QCoreApplication::applicationDirPath() + "/HPIFittingDebug";
InvHpiDataUpdater hpiDataUpdater = InvHpiDataUpdater(pFiffInfo);
InvHpiFit HPI = InvHpiFit(hpiDataUpdater.getSensors());
MatrixXd matAmplitudes;
MatrixXd matCoilLoc(4,3);
// ordering of frequencies
from = first + vecTime(0);
to = from + iQuantum;
if(!raw.read_raw_segment(matData, matTimes, from, to)) {
qCritical("error during read_raw_segment");
return -1;
}
// order frequencies
timer.start();
hpiDataUpdater.prepareDataAndProjectors(matData,matProjectors);
const auto& matProjectedData = hpiDataUpdater.getProjectedData();
const auto& matPreparedProjectors = hpiDataUpdater.getProjectors();
const auto& matCoilsHead = hpiDataUpdater.getHpiDigitizer();
InvHpiModelParameters hpiModelParameters(vecFreqs,
pFiffInfo->sfreq,
pFiffInfo->linefreq,
bFast);
HpiFitResult hpiFitResult;
HPI.fit(matProjectedData,matPreparedProjectors,hpiModelParameters,matCoilsHead,true,hpiFitResult);
hpiModelParameters = InvHpiModelParameters(hpiFitResult.hpiFreqs,
pFiffInfo->sfreq,
pFiffInfo->linefreq,
bFast);
float fTimer = 0.0;
// read and fit
for(int i = 0; i < vecTime.size(); i++) {
from = first + vecTime(i)*iQuantumT;
to = from + iQuantum;
if (to > last) {
to = last;
qWarning() << "Block size < iQuantum " << iQuantum;
}
// Reading
if(!raw.read_raw_segment(matData, matTimes, from, to)) {
qCritical("error during read_raw_segment");
return -1;
}
timer.start();
hpiDataUpdater.prepareDataAndProjectors(matData,matProjectors);
const auto& matProjectedData = hpiDataUpdater.getProjectedData();
const auto& matPreparedProjectors = hpiDataUpdater.getProjectors();
HPI.fit(matProjectedData,
matPreparedProjectors,
hpiModelParameters,
matCoilsHead,
hpiFitResult);
fTimer = timer.elapsed();
InvHpiFit::storeHeadPosition(vecTime(i), hpiFitResult.devHeadTrans.trans, matPosition, hpiFitResult.GoF, hpiFitResult.errorDistances);
matPosition(i,9) = fTimer;
// if big head displacement occures, update debHeadTrans
if(InvHpiFit::compareTransformation(transDevHead.trans, hpiFitResult.devHeadTrans.trans, fThreshRot, fThreshTrans)) {
transDevHead = hpiFitResult.devHeadTrans;
qInfo() << "dev_head_t has been updated.";
}
if(bVerbose) {
qInfo() << "Iteration" << i << "Of" << vecTime.size()
<< " Duration " << fTimer << "ms"
<< " Error" << hpiFitResult.errorDistances[0]*1000 << hpiFitResult.errorDistances[1]*1000 << hpiFitResult.errorDistances[2]*1000 << hpiFitResult.errorDistances[3]*1000
<< " GoF" << hpiFitResult.GoF[0] << hpiFitResult.GoF[1] << hpiFitResult.GoF[2] << hpiFitResult.GoF[3];
}
}
qInfo() << "Iterations:" << vecTime.size()
<< "Average GoF:" << matPosition.col(7).mean() * 100 << "%"
<< "Average Error:" << matPosition.col(8).mean() * 1000 << "mm"
<< "Average Duration:" << matPosition.col(9).mean() << "ms";
if(bSave) {
IOUtils::write_eigen_matrix(matPosition, QString(QCoreApplication::applicationDirPath() + "/MNE-sample-data/" + sNameOut));
}
}
Authors of this file
- Christoph Dinh <christoph.dinh@mne-cpp.org>