v2.0.0
Loading...
Searching...
No Matches
bad_channels_lof.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "bad_channels_lof.h"
18
19#include <fiff/fiff_info.h>
20#include <fiff/fiff_ch_info.h>
21#include <fiff/fiff_constants.h>
22
23//=============================================================================================================
24// SKIGEN INCLUDES
25//=============================================================================================================
26
27#include <Skigen/Neighbors>
28
29//=============================================================================================================
30// QT INCLUDES
31//=============================================================================================================
32
33#include <QDebug>
34
35//=============================================================================================================
36// STL INCLUDES
37//=============================================================================================================
38
39#include <algorithm>
40#include <cmath>
41
42//=============================================================================================================
43// USED NAMESPACES
44//=============================================================================================================
45
46using namespace UTILSLIB;
47using namespace FIFFLIB;
48using namespace Eigen;
49
50//=============================================================================================================
51// DEFINE FUNCTIONS
52//=============================================================================================================
53
54VectorXd UTILSLIB::computeLofScores(const MatrixXd& features, int k)
55{
56 const int n = static_cast<int>(features.rows());
57
58 if (n <= k) {
59 return VectorXd::Ones(n);
60 }
61
62 // features is (n_samples × n_features), matches skigen convention
63 Skigen::LocalOutlierFactor<double> lof(k);
64 lof.fit(features);
65 return lof.lof_scores();
66}
67
68//=============================================================================================================
69
70QStringList UTILSLIB::findBadChannelsLof(const MatrixXd& data,
71 const FiffInfo& info,
72 const LofBadChannelParams& params)
73{
74 // Select channel indices based on type filter
75 QList<int> chIdx;
76 for (int i = 0; i < info.chs.size(); ++i) {
77 if (params.bMegOnly && info.chs[i].kind != FIFFV_MEG_CH)
78 continue;
79 if (params.bEegOnly && info.chs[i].kind != FIFFV_EEG_CH)
80 continue;
81 if (!params.bMegOnly && !params.bEegOnly) {
82 if (info.chs[i].kind != FIFFV_MEG_CH && info.chs[i].kind != FIFFV_EEG_CH)
83 continue;
84 }
85 chIdx.append(i);
86 }
87
88 const int nCh = chIdx.size();
89 if (nCh < 3) {
90 return QStringList();
91 }
92
93 // Compute features: [std_dev, kurtosis, max_abs]
94 const int nFeatures = 3;
95 MatrixXd features(nCh, nFeatures);
96
97 for (int i = 0; i < nCh; ++i) {
98 const RowVectorXd row = data.row(chIdx[i]);
99 const int nTimes = static_cast<int>(row.size());
100 const double mean = row.mean();
101
102 // Standard deviation
103 double variance = (row.array() - mean).square().sum() / static_cast<double>(nTimes);
104 double stdDev = std::sqrt(variance);
105 features(i, 0) = stdDev;
106
107 // Kurtosis (excess)
108 double m4 = (row.array() - mean).pow(4.0).sum() / static_cast<double>(nTimes);
109 double kurt = (variance > 1e-30) ? m4 / (variance * variance) - 3.0 : 0.0;
110 features(i, 1) = kurt;
111
112 // Max absolute value
113 features(i, 2) = row.array().abs().maxCoeff();
114 }
115
116 // Normalise features to zero mean, unit variance
117 for (int f = 0; f < nFeatures; ++f) {
118 double fMean = features.col(f).mean();
119 double fStd = std::sqrt((features.col(f).array() - fMean).square().mean());
120 if (fStd > 1e-30) {
121 features.col(f) = (features.col(f).array() - fMean) / fStd;
122 }
123 }
124
125 // Compute LOF scores
126 int k = std::min(params.iNNeighbors, nCh - 1);
127 VectorXd lof = computeLofScores(features, k);
128
129 // Flag channels above threshold
130 QStringList badChannels;
131 for (int i = 0; i < nCh; ++i) {
132 if (lof(i) > params.dThreshold) {
133 badChannels.append(info.ch_names[chIdx[i]]);
134 }
135 }
136
137 return badChannels;
138}
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
#define FIFFV_EEG_CH
#define FIFFV_MEG_CH
FIFF channel descriptor record (FIFF_CH_INFO): per-channel logical/scanner numbers,...
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
Bad-channel detection via the Local Outlier Factor (LOF) algorithm.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
DSPSHARED_EXPORT Eigen::VectorXd computeLofScores(const Eigen::MatrixXd &features, int k)
Compute LOF scores for a feature matrix.
DSPSHARED_EXPORT QStringList findBadChannelsLof(const Eigen::MatrixXd &data, const FIFFLIB::FiffInfo &info, const LofBadChannelParams &params=LofBadChannelParams())
Detect bad channels using Local Outlier Factor (LOF).
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Definition fiff_info.h:88
QList< FiffChInfo > chs