56QVector<QPair<int, int>> findContiguousSegments(
const VectorXi& mask)
58 QVector<QPair<int, int>> segs;
59 const int n =
static_cast<int>(mask.size());
64 while (i < n && mask(i))
66 segs.append({start, i - 1});
78void removeShortSegments(QVector<QPair<int, int>>& segs,
int minSamples)
82 QVector<QPair<int, int>> filtered;
83 for (
const auto& seg : segs) {
84 if (seg.second - seg.first + 1 >= minSamples)
96 int best = std::numeric_limits<int>::max();
97 for (
long long p5 = 1; p5 < 2LL * n + 1; p5 *= 5) {
98 for (
long long p35 = p5; p35 < 2LL * n + 1; p35 *= 3) {
102 best =
static_cast<int>(std::min<long long>(best, v));
115 const MatrixXd& data,
123 const Index nTimes = data.cols();
124 if (data.rows() == 0 || nTimes == 0)
127 RowVectorXi picks = info.
pick_types(QStringLiteral(
"mag"),
false,
false);
128 if (picks.size() == 0)
129 picks = info.
pick_types(QStringLiteral(
"grad"),
false,
false);
130 if (picks.size() == 0)
132 if (picks.size() == 0) {
133 qWarning(
"annotateMusclZscore: no MEG or EEG channels found");
136 MatrixXd sub(picks.size(), nTimes);
137 for (Index i = 0; i < picks.size(); ++i)
138 sub.row(i) = data.row(picks(i));
141 const int nFft = nextFastLen(
static_cast<int>(nTimes));
142 RowVectorXd score = RowVectorXd::Zero(nTimes);
143 for (Index i = 0; i < band.rows(); ++i) {
145 const double mean = env.mean();
146 const double sd = std::sqrt((env.array() - mean).square().mean());
148 score += (env.array() - mean).matrix() / sd;
150 score /= std::sqrt(
static_cast<double>(band.rows()));
155 VectorXi mask(nTimes);
156 for (Index i = 0; i < nTimes; ++i)
157 mask(i) = score(i) > params.
dThreshold ? 1 : 0;
160 for (
const auto& seg : findContiguousSegments((1 - mask.array()).matrix())) {
161 if (seg.second - seg.first + 1 < minGood)
162 mask.segment(seg.first, seg.second - seg.first + 1).setOnes();
164 for (
const auto& seg : findContiguousSegments(mask)) {
165 const int last = std::min(seg.second + 1,
static_cast<int>(nTimes) - 1);
166 annot.
append(seg.first / sfreq, (last - seg.first) / sfreq, QStringLiteral(
"BAD_muscle"));
174 const MatrixXd& data,
183 const Eigen::Index nTimes = data.cols();
184 if (data.rows() == 0 || nTimes < 2 || (!params.
peak && !params.
flat))
187 const int minSamples =
static_cast<int>(std::round(params.
dMinDuration * sfreq));
188 VectorXi anyFlat = VectorXi::Zero(nTimes - 1);
189 VectorXi anyPeak = VectorXi::Zero(nTimes - 1);
192 if (ch >= data.rows())
194 const ArrayXd diff = (data.row(ch).tail(nTimes - 1) - data.row(ch).head(nTimes - 1)).array().abs().transpose();
196 for (
const auto& [threshold, isPeak] : {std::pair{params.
flat,
false}, std::pair{params.
peak,
true}}) {
199 const VectorXi mask = isPeak ? (diff >= *threshold).cast<int>().matrix().eval() : (diff <= *threshold).cast<int>().matrix().eval();
201 QVector<QPair<int, int>> segs = findContiguousSegments(mask);
202 removeShortSegments(segs, minSamples);
204 for (
const auto& seg : segs)
205 count += seg.second - seg.first + 1;
207 const double percent = (count > 0 ? count + 1 : 0) * 100.0 /
static_cast<double>(nTimes);
210 }
else if (percent > 0.0) {
211 VectorXi& any = isPeak ? anyPeak : anyFlat;
212 for (
const auto& seg : segs)
213 any.segment(seg.first, seg.second - seg.first + 1).setOnes();
220 for (
const auto& [any, description] : {std::pair{&anyFlat,
"BAD_flat"}, std::pair{&anyPeak,
"BAD_peak"}}) {
221 for (
const auto& seg : findContiguousSegments(*any))
222 annot.
append(seg.first / sfreq, (seg.second - seg.first + 1) / sfreq, QString::fromLatin1(description));
Symbolic FIFF tag, block, value, unit and channel-type constants shared across FIFFLIB.
Full FIFF measurement metadata: everything from FIFFB_MEAS / FIFFB_MEAS_INFO needed to interpret a re...
Continuous-data annotation of muscle and amplitude artefacts.
Discoverable design / apply façade over the UTILSLIB::FilterKernel FIR engine.
ConnectivityAec — Amplitude Envelope Correlation connectivity metric.
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 FIFFLIB::FiffAnnotations annotateMusclZscore(const Eigen::MatrixXd &data, const FIFFLIB::FiffInfo &info, double sfreq, const AnnotateMusclParams ¶ms=AnnotateMusclParams(), Eigen::RowVectorXd *scores=nullptr)
Detect muscle artifacts via high-frequency z-score and annotate bad segments.
DSPSHARED_EXPORT FIFFLIB::FiffAnnotations annotateAmplitude(const Eigen::MatrixXd &data, const FIFFLIB::FiffInfo &info, double sfreq, const AnnotateAmplitudeParams ¶ms, QStringList *bads=nullptr)
Annotate spans where consecutive samples jump or stay flat, as mne.preprocessing.annotate_amplitude.
Parameters for muscle artifact annotation.
Parameters for amplitude-based annotation.
std::optional< double > flat
std::optional< double > peak
static Eigen::VectorXd hilbertEnvelope(const Eigen::VectorXd &signal, int nFft=0)
static Eigen::MatrixXd filterData(const Eigen::MatrixXd &matData, double dSFreq, double dLFreq, double dHFreq)
Container for FiffAnnotation entries with FIFF, JSON and CSV serializers.
void append(const FiffAnnotation &annotation)
Full FIFF measurement info: per-channel descriptors, sampling and filter setup, projectors,...
Eigen::RowVectorXi pick_types(const QString meg, bool eeg=false, bool stim=false, const QStringList &include=defaultQStringList, const QStringList &exclude=defaultQStringList) const
Eigen::RowVectorXi pick_data_channels(const QStringList &exclude=defaultQStringList) const