79RowVectorXd ArtifactDetect::bandpassFilter(
const RowVectorXd& vecSignal,
85 QVector<IirBiquad> sos;
99QVector<int> ArtifactDetect::findPeaks(
const RowVectorXd& vecSignal,
104 const int N =
static_cast<int>(vecSignal.size());
105 if (N < 3)
return peaks;
107 int lastPeak = -iMinDist - 1;
109 for (
int i = 1; i < N - 1; ++i) {
110 double v = vecSignal(i);
111 if (v < dThreshold)
continue;
114 if (v > vecSignal(i - 1) && v >= vecSignal(i + 1)) {
116 if (i - lastPeak >= iMinDist) {
119 }
else if (!peaks.isEmpty() && vecSignal(peaks.last()) < v) {
141 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
148 RowVectorXd ecgSignal;
152 ecgSignal = matData.row(ecgIdx).cast<
double>();
158 qWarning() <<
"ArtifactDetect::detectEcg: No ECG channel found. "
159 "Synthesising ECG proxy from MEG magnetometers.";
162 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
168 if (fiffInfo.
chs[i].unit == 112) {
174 if (magIdx.isEmpty()) {
176 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
183 if (magIdx.isEmpty()) {
184 qWarning() <<
"ArtifactDetect::detectEcg: No MEG channels found. Cannot detect ECG.";
188 const int nSamp =
static_cast<int>(matData.cols());
189 ecgSignal = RowVectorXd::Zero(nSamp);
190 for (
int idx : magIdx) {
191 ecgSignal += matData.row(idx).cwiseAbs().cast<
double>();
193 ecgSignal /=
static_cast<double>(magIdx.size());
197 RowVectorXd filtered = bandpassFilter(ecgSignal, dSFreq,
202 double sigMin = filtered.minCoeff();
203 double sigMax = filtered.maxCoeff();
204 double threshold = params.
dThreshFactor * (sigMax - sigMin) + sigMin;
207 int iMinDist =
static_cast<int>(std::round(params.
dMinRRSec * dSFreq));
208 iMinDist = std::max(iMinDist, 1);
210 return findPeaks(filtered, threshold, iMinDist);
222 for (
int i = 0; i < fiffInfo.
nchan; ++i) {
228 if (eogIdx.isEmpty()) {
229 qWarning() <<
"ArtifactDetect::detectEog: No EOG channel found.";
234 int bestIdx = eogIdx[0];
235 double bestPtp = 0.0;
236 for (
int idx : eogIdx) {
237 RowVectorXd row = matData.row(idx).cast<
double>();
238 double ptp = row.maxCoeff() - row.minCoeff();
245 RowVectorXd eogSignal = matData.row(bestIdx).cast<
double>();
248 RowVectorXd filtered = bandpassFilter(eogSignal, dSFreq,
255 RowVectorXd absFiltered = filtered.cwiseAbs();
257 int iMinDist =
static_cast<int>(std::round(params.
dMinGapSec * dSFreq));
258 iMinDist = std::max(iMinDist, 1);
260 return findPeaks(absFiltered, params.
dThresholdV, iMinDist);
FiffChInfo class declaration.
Declaration of IirFilter — Butterworth IIR filter design and application using numerically stable sec...
Declaration of ArtifactDetect — ECG and EOG physiological artifact event detection.
FIFF file I/O and data structures (raw, epochs, evoked, covariance, forward).
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)
FIFF measurement file information.