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());
84 if (N < 3)
return peaks;
86 int lastPeak = -iMinDist - 1;
88 for (
int i = 1; i < N - 1; ++i) {
89 double v = vecSignal(i);
90 if (v < dThreshold)
continue;
93 if (v > vecSignal(i - 1) && v >= vecSignal(i + 1)) {
95 if (i - lastPeak >= iMinDist) {
98 }
else if (!peaks.isEmpty() && vecSignal(peaks.last()) < v) {
120 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
127 RowVectorXd ecgSignal;
131 ecgSignal = matData.row(ecgIdx).cast<
double>();
137 qWarning() <<
"ArtifactDetect::detectEcg: No ECG channel found. "
138 "Synthesising ECG proxy from MEG magnetometers.";
141 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
147 if (fiffInfo.
chs[i].unit == 112) {
153 if (magIdx.isEmpty()) {
155 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
162 if (magIdx.isEmpty()) {
163 qWarning() <<
"ArtifactDetect::detectEcg: No MEG channels found. Cannot detect ECG.";
167 const int nSamp =
static_cast<int>(matData.cols());
168 ecgSignal = RowVectorXd::Zero(nSamp);
169 for (
int idx : magIdx) {
170 ecgSignal += matData.row(idx).cwiseAbs().cast<
double>();
172 ecgSignal /=
static_cast<double>(magIdx.size());
176 RowVectorXd filtered = bandpassFilter(ecgSignal, dSFreq,
181 double sigMin = filtered.minCoeff();
182 double sigMax = filtered.maxCoeff();
183 double threshold = params.
dThreshFactor * (sigMax - sigMin) + sigMin;
186 int iMinDist =
static_cast<int>(std::round(params.
dMinRRSec * dSFreq));
187 iMinDist = std::max(iMinDist, 1);
189 return findPeaks(filtered, threshold, iMinDist);
201 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
207 if (eogIdx.isEmpty()) {
208 qWarning() <<
"ArtifactDetect::detectEog: No EOG channel found.";
213 int bestIdx = eogIdx[0];
214 double bestPtp = 0.0;
215 for (
int idx : eogIdx) {
216 RowVectorXd row = matData.row(idx).cast<
double>();
217 double ptp = row.maxCoeff() - row.minCoeff();
224 RowVectorXd eogSignal = matData.row(bestIdx).cast<
double>();
227 RowVectorXd filtered = bandpassFilter(eogSignal, dSFreq,
234 RowVectorXd absFiltered = filtered.cwiseAbs();
236 int iMinDist =
static_cast<int>(std::round(params.
dMinGapSec * dSFreq));
237 iMinDist = std::max(iMinDist, 1);
239 return findPeaks(absFiltered, params.
dThresholdV, iMinDist);
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
Butterworth IIR filter design and application via numerically stable second-order sections.
Declaration of ArtifactDetect — ECG and EOG physiological artifact event detection.
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,...