58RowVectorXd ArtifactDetect::bandpassFilter(
const RowVectorXd& vecSignal,
64 QVector<IirBiquad> sos;
78QVector<int> ArtifactDetect::findPeaks(
const RowVectorXd& vecSignal,
83 const int N =
static_cast<int>(vecSignal.size());
87 int lastPeak = -iMinDist - 1;
89 for (
int i = 1; i < N - 1; ++i) {
90 double v = vecSignal(i);
95 if (v > vecSignal(i - 1) && v >= vecSignal(i + 1)) {
97 if (i - lastPeak >= iMinDist) {
100 }
else if (!peaks.isEmpty() && vecSignal(peaks.last()) < v) {
122 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
129 RowVectorXd ecgSignal;
133 ecgSignal = matData.row(ecgIdx).cast<
double>();
139 qWarning() <<
"ArtifactDetect::detectEcg: No ECG channel found. "
140 "Synthesising ECG proxy from MEG magnetometers.";
143 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
149 if (fiffInfo.
chs[i].unit == 112) {
155 if (magIdx.isEmpty()) {
157 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
164 if (magIdx.isEmpty()) {
165 qWarning() <<
"ArtifactDetect::detectEcg: No MEG channels found. Cannot detect ECG.";
169 const int nSamp =
static_cast<int>(matData.cols());
170 ecgSignal = RowVectorXd::Zero(nSamp);
171 for (
int idx : magIdx) {
172 ecgSignal += matData.row(idx).cwiseAbs().cast<
double>();
174 ecgSignal /=
static_cast<double>(magIdx.size());
178 RowVectorXd filtered = bandpassFilter(ecgSignal, dSFreq,
183 double sigMin = filtered.minCoeff();
184 double sigMax = filtered.maxCoeff();
185 double threshold = params.
dThreshFactor * (sigMax - sigMin) + sigMin;
188 int iMinDist =
static_cast<int>(std::round(params.
dMinRRSec * dSFreq));
189 iMinDist = std::max(iMinDist, 1);
191 return findPeaks(filtered, threshold, iMinDist);
203 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
209 if (eogIdx.isEmpty()) {
210 qWarning() <<
"ArtifactDetect::detectEog: No EOG channel found.";
215 int bestIdx = eogIdx[0];
216 double bestPtp = 0.0;
217 for (
int idx : eogIdx) {
218 RowVectorXd row = matData.row(idx).cast<
double>();
219 double ptp = row.maxCoeff() - row.minCoeff();
226 RowVectorXd eogSignal = matData.row(bestIdx).cast<
double>();
229 RowVectorXd filtered = bandpassFilter(eogSignal, dSFreq,
236 RowVectorXd absFiltered = filtered.cwiseAbs();
238 int iMinDist =
static_cast<int>(std::round(params.
dMinGapSec * dSFreq));
239 iMinDist = std::max(iMinDist, 1);
241 return findPeaks(absFiltered, params.
dThresholdV, iMinDist);
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.
Declaration of ArtifactDetect — ECG and EOG physiological artifact event detection.
Butterworth IIR filter design and application via numerically stable second-order sections.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
static QVector< int > detectEcg(const Eigen::MatrixXd &matData, const FIFFLIB::FiffInfo &fiffInfo, double dSFreq, const EcgParams ¶ms=EcgParams())
ArtifactDetectEogParams EogParams
ArtifactDetectEcgParams EcgParams
static QVector< int > detectEog(const Eigen::MatrixXd &matData, const FIFFLIB::FiffInfo &fiffInfo, double dSFreq, const EogParams ¶ms=EogParams())
static Eigen::RowVectorXd applyZeroPhase(const Eigen::RowVectorXd &vecData, const QVector< IirBiquad > &sos)
static QVector< IirBiquad > designButterworth(int iOrder, FilterType type, double dCutoffLow, double dCutoffHigh, double dSFreq)
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...