52 QSharedPointer<FiffRawData> pFiffRawData,
60 const RowVectorXi& vecPicks,
64 dCenterfreq = dCenterfreq / (dSFreq / 2.0);
65 bandwidth = bandwidth / (dSFreq / 2.0);
66 dTransition = dTransition / (dSFreq / 2.0);
88 QSharedPointer<FiffRawData> pFiffRawData,
90 const RowVectorXi& vecPicks,
96 SparseMatrix<double> mult;
108 float fFactor = 2.0f;
109 int iSize = fFactor * iOrder;
110 int residual = (to - from) % iSize;
111 while (residual < iOrder) {
112 fFactor = fFactor - 0.1f;
113 iSize = fFactor * iOrder;
114 residual = (to - from) % iSize;
116 if ((iSize < iOrder)) {
117 qInfo() <<
"[Filter::filterData] Sliced data block size is too small. Filtering whole block at once.";
123 float quantum_sec = iSize / pFiffRawData->info.sfreq;
124 fiff_int_t quantum = ceil(
static_cast<double>(quantum_sec) * pFiffRawData->info.sfreq);
127 bool first_buffer =
true;
130 MatrixXd matData, matDataOverlap;
133 for (first = from; first < to; first += quantum) {
134 last = first + quantum - 1;
139 if (!pFiffRawData->read_raw_segment(matData, times, mult, first, last, sel)) {
140 qWarning(
"[Filter::filterData] Error during read_raw_segment\n");
144 qInfo() <<
"Filtering and writing block" << first <<
"to" << last;
150 first_buffer =
false;
159 outfid->write_raw_buffer(matData.block(0, iOrder / 2, matData.rows(), matData.cols() - iOrder), cals);
160 }
else if (first + quantum >= to) {
161 matData.block(0, 0, matData.rows(), iOrder) += matDataOverlap;
162 outfid->write_raw_buffer(matData.block(0, 0, matData.rows(), matData.cols() - iOrder), cals);
164 matData.block(0, 0, matData.rows(), iOrder) += matDataOverlap;
165 outfid->write_raw_buffer(matData.block(0, 0, matData.rows(), matData.cols() - iOrder), cals);
168 matDataOverlap = matData.block(0, matData.cols() - iOrder, matData.rows(), iOrder);
171 outfid->finish_writing_raw();
186 const RowVectorXi& vecPicks,
191 if (matData.cols() < iOrder) {
192 qWarning() << QString(
"[Filter::filterData] Filter length/order is bigger than data length. Returning.");
197 dCenterfreq = dCenterfreq / (dSFreq / 2.0);
198 bandwidth = bandwidth / (dSFreq / 2.0);
199 dTransition = dTransition / (dSFreq / 2.0);
222 const RowVectorXi& vecPicks,
229 if (matData.cols() < iOrder) {
230 qWarning() <<
"[Filter::filterData] Filter length/order is bigger than data length. Returning.";
235 MatrixXd matDataOut(matData.rows(), matData.cols() + iOrder);
236 matDataOut.setZero();
237 MatrixXd sliceFiltered;
240 float fFactor = 2.0f;
241 int iSize = fFactor * iOrder;
242 int residual = matData.cols() % iSize;
243 while (residual < iOrder) {
244 fFactor = fFactor - 0.1f;
245 iSize = fFactor * iOrder;
246 residual = matData.cols() % iSize;
248 if (iSize < iOrder) {
249 iSize = matData.cols();
254 if (matData.cols() > iSize) {
256 int numSlices = ceil(
float(matData.cols()) /
float(iSize));
258 for (
int i = 0; i < numSlices; i++) {
259 if (i == numSlices - 1) {
261 iSize = matData.cols() - (iSize * (numSlices - 1));
265 sliceFiltered =
filterDataBlock(matData.block(0, from, matData.rows(), iSize),
272 matDataOut.block(0, 0, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
274 matDataOut.block(0, from, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
289 return matDataOut.block(0, iOrder / 2, matDataOut.rows(), matData.cols());
296 const RowVectorXi& vecPicks,
303 if (matData.cols() < iOrder) {
304 qWarning() << QString(
"[Filter::filterDataBlock] Filter length/order is bigger than data length. Returning.");
313 RowVectorXi vecPicksNew = vecPicks;
314 if (vecPicksNew.cols() == 0) {
315 vecPicksNew = RowVectorXi::LinSpaced(matData.rows(), 0, matData.rows() - 1);
319 QList<FilterObject> timeData;
323 for (qint32 i = 0; i < vecPicksNew.cols(); ++i) {
325 data.
iRow = vecPicksNew[i];
326 data.
vecData = matData.row(vecPicksNew[i]);
327 timeData.append(data);
331 MatrixXd matDataOut(matData.rows(), matData.cols() + iOrder);
332 matDataOut.setZero();
333 matDataOut.block(0, iOrder / 2, matData.rows(), matData.cols()) = matData;
336 QFuture<void> future = QtConcurrent::map(timeData,
338 future.waitForFinished();
340 for (
int i = 0; i < timeData.size(); ++i) {
346 RowVectorXd tempData;
348 for (
int r = 0; r < timeData.size(); r++) {
350 matDataOut.row(timeData.at(r).iRow) = timeData.at(r).vecData;
376 const RowVectorXi& vecPicks,
382 if (matData.cols() < iOrder) {
383 qWarning() << QString(
"[Filter::filterData] Filter length/order is bigger than data length. Returning.");
388 dCenterfreq = dCenterfreq / (dSFreq / 2.0);
389 bandwidth = bandwidth / (dSFreq / 2.0);
390 dTransition = dTransition / (dSFreq / 2.0);
393 FilterKernel filter = FilterKernel(
"filter_kernel",
413 const FilterKernel& filterKernel,
414 const RowVectorXi& vecPicks,
422 if (matData.cols() < iOrder) {
423 qWarning() <<
"[Filter::filterData] Filter length/order is bigger than data length. Returning.";
428 if (m_matOverlapBack.cols() != iOrder || m_matOverlapBack.rows() < matData.rows()) {
429 m_matOverlapBack.resize(matData.rows(), iOrder);
430 m_matOverlapBack.setZero();
433 if (m_matOverlapFront.cols() != iOrder || m_matOverlapFront.rows() < matData.rows()) {
434 m_matOverlapFront.resize(matData.rows(), iOrder);
435 m_matOverlapFront.setZero();
439 MatrixXd matDataOut(matData.rows(), matData.cols() + iOrder);
440 matDataOut.setZero();
441 MatrixXd sliceFiltered;
444 float fFactor = 2.0f;
445 int iSize = fFactor * iOrder;
446 int residual = matData.cols() % iSize;
447 while (residual < iOrder) {
448 fFactor = fFactor - 0.1f;
449 iSize = fFactor * iOrder;
450 residual = matData.cols() % iSize;
452 if (iSize < iOrder) {
453 iSize = matData.cols();
458 if (matData.cols() > iSize) {
460 int numSlices = ceil(
float(matData.cols()) /
float(iSize));
462 for (
int i = 0; i < numSlices; i++) {
463 if (i == numSlices - 1) {
465 iSize = matData.cols() - (iSize * (numSlices - 1));
469 sliceFiltered =
filterDataBlock(matData.block(0, from, matData.rows(), iSize),
475 matDataOut.block(0, 0, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
477 matDataOut.block(0, from, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
480 if (bFilterEnd && (i == 0)) {
481 matDataOut.block(0, 0, matDataOut.rows(), iOrder) += m_matOverlapBack;
482 }
else if (!bFilterEnd && (i == numSlices - 1)) {
483 matDataOut.block(0, matDataOut.cols() - iOrder, matDataOut.rows(), iOrder) += m_matOverlapFront;
495 matDataOut.block(0, 0, matDataOut.rows(), iOrder) += m_matOverlapBack;
497 matDataOut.block(0, matDataOut.cols() - iOrder, matDataOut.rows(), iOrder) += m_matOverlapFront;
502 m_matOverlapBack = matDataOut.block(0, matDataOut.cols() - iOrder, matDataOut.rows(), iOrder);
503 m_matOverlapFront = matDataOut.block(0, 0, matDataOut.rows(), iOrder);
508 return matDataOut.block(0, 0, matDataOut.rows(), matData.cols());
516 m_matOverlapBack.resize(0, 0);
517 m_matOverlapFront.resize(0, 0);
523 const MatrixXi& matEvents,
528 float fTBaselineFromS,
530 const QMap<QString, double>& mapReject,
532 const QStringList& lExcludeChs,
533 const RowVectorXi& picks)
540 MatrixXi selected = MatrixXi::Zero(1, matEvents.rows());
541 for (p = 0; p < matEvents.rows(); ++p) {
542 if (matEvents(p, 1) == 0 && matEvents(p, 2) == eventType) {
543 selected(0, count) = p;
547 selected.conservativeResize(1, count);
549 qInfo(
"[RTPROCESSINGLIB::computeFilteredAverage] %d matching events found", count);
553 RowVectorXi picksNew = picks;
554 if (picks.cols() <= 0) {
555 picksNew.resize(raw.
info.
chs.size());
556 for (
int i = 0; i < raw.
info.
chs.size(); ++i) {
566 std::unique_ptr<MNEEpochData> epoch;
569 for (p = 0; p < count; ++p) {
571 event_samp = matEvents(selected(p), 0);
572 from = event_samp + fTMinS * raw.
info.
sfreq;
573 to = event_samp + floor(fTMaxS * raw.
info.
sfreq + 0.5);
575 epoch = std::make_unique<MNEEpochData>();
577 if (raw.
read_raw_segment(epoch->epoch, timesDummy, from - iFilterDelay, to + iFilterDelay, picksNew)) {
582 times.resize(1, to - from + 1);
583 for (qint32 i = 0; i < times.cols(); ++i)
584 times(0, i) = ((float)(from - event_samp + i)) / raw.
info.
sfreq;
587 epoch->event = eventType;
588 epoch->tmin = fTMinS;
589 epoch->tmax = fTMaxS;
596 if (epoch->bReject) {
601 if (!lstEpochDataList.isEmpty()) {
602 if (epoch->epoch.size() == lstEpochDataList.last()->epoch.size()) {
609 qWarning(
"[MNEEpochDataList::readEpochs] Can't read the event data segments.");
613 qInfo().noquote() <<
"[MNEEpochDataList::readEpochs] Read a total of" << lstEpochDataList.size() <<
"epochs of type" << eventType <<
"and marked" << dropCount <<
"for rejection.";
615 if (bApplyBaseline) {
616 QPair<float, float> baselinePair(fTBaselineFromS, fTBaselineToS);
620 if (!mapReject.isEmpty()) {
626 lstEpochDataList.first()->epoch.cols());
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
#define FIFF_FIRST_SAMPLE
FIFF continuous raw recording: FiffInfo plus a directory of FIFF_DATA_BUFFER tags for random-access s...
Real-time FIR / IIR filtering of streaming MEG / EEG data blocks.
Single epoch (one trial slice of preprocessed sensor data) with timing and rejection metadata.
Ordered list of MNELIB::MNEEpochData objects sharing a common FIFFLIB::FiffInfo.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
DSPSHARED_EXPORT FIFFLIB::FiffEvoked computeFilteredAverage(const FIFFLIB::FiffRawData &raw, const Eigen::MatrixXi &matEvents, float fTMinS, float fTMaxS, qint32 eventType, bool bApplyBaseline, float fTBaselineFromS, float fTBaselineToS, const QMap< QString, double > &mapReject, const UTILSLIB::FilterKernel &filterKernel, const QStringList &lExcludeChs=QStringList(), const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi())
DSPSHARED_EXPORT Eigen::MatrixXd filterData(const Eigen::MatrixXd &matData, int type, double dCenterfreq, double dBandwidth, double dTransition, double dSFreq, int iOrder=1024, int designMethod=UTILSLIB::FilterKernel::m_designMethods.indexOf(UTILSLIB::FilterParameter("Cosine")), const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi(), bool bUseThreads=true, bool bKeepOverhead=false)
DSPSHARED_EXPORT Eigen::MatrixXd filterDataBlock(const Eigen::MatrixXd &matData, const Eigen::RowVectorXi &vecPicks, const UTILSLIB::FilterKernel &filterKernel, bool bUseThreads=true)
DSPSHARED_EXPORT void filterChannel(FilterObject &channelDataTime)
DSPSHARED_EXPORT bool filterFile(QIODevice &pIODevice, QSharedPointer< FIFFLIB::FiffRawData > pFiffRawData, int type, double dCenterfreq, double dBandwidth, double dTransition, double dSFreq, int iOrder=4096, int designMethod=UTILSLIB::FilterKernel::m_designMethods.indexOf(UTILSLIB::FilterParameter("Cosine")), const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi(), bool bUseThreads=true)
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
The FilterKernel class provides methods to create/design a FIR filter kernel.
void applyFftFilter(Eigen::RowVectorXd &vecData, bool bKeepOverhead=false)
void prepareFilter(int iDataSize)
int getFilterOrder() const
Lightweight filter configuration holding kernel coefficients and overlap-add state for one channel.
UTILSLIB::FilterKernel filterKernel
Eigen::RowVectorXd vecData
Eigen::MatrixXd calculate(const Eigen::MatrixXd &matData, int type, double dCenterfreq, double dBandwidth, double dTransition, double dSFreq, int iOrder=1024, int designMethod=UTILSLIB::FilterKernel::m_designMethods.indexOf(UTILSLIB::FilterParameter("Cosine")), const Eigen::RowVectorXi &vecPicks=Eigen::RowVectorXi(), bool bFilterEnd=true, bool bUseThreads=true, bool bKeepOverhead=false)
Single averaged evoked response: time axis, data, baseline, channel info and averaging metadata.
Continuous FIFF raw recording: FiffInfo plus a random-access directory of FIFF_DATA_BUFFER tags.
bool read_raw_segment(Eigen::MatrixXd &data, Eigen::MatrixXd ×, fiff_int_t from=-1, fiff_int_t to=-1, const Eigen::RowVectorXi &sel=defaultRowVectorXi, bool do_debug=false) const
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_writing_raw(QIODevice &p_IODevice, const FiffInfo &info, Eigen::RowVectorXd &cals, Eigen::MatrixXi sel=defaultMatrixXi, bool bResetRange=true)
QSharedPointer< MNEEpochData > SPtr
Ordered list of MNEEpochData objects sharing a common measurement info.
FIFFLIB::FiffEvoked average(const FIFFLIB::FiffInfo &p_info, FIFFLIB::fiff_int_t first, FIFFLIB::fiff_int_t last, Eigen::VectorXi sel=FIFFLIB::defaultVectorXi, bool proj=false) const
void applyBaselineCorrection(const QPair< float, float > &baseline)
static bool checkForArtifact(const Eigen::MatrixXd &data, const FIFFLIB::FiffInfo &pFiffInfo, const QMap< QString, double > &mapReject, const QStringList &lExcludeChs=QStringList())