54 QSharedPointer<FiffRawData> pFiffRawData,
62 const RowVectorXi& vecPicks,
66 dCenterfreq = dCenterfreq / (dSFreq / 2.0);
67 bandwidth = bandwidth / (dSFreq / 2.0);
68 dTransition = dTransition / (dSFreq / 2.0);
90 QSharedPointer<FiffRawData> pFiffRawData,
92 const RowVectorXi& vecPicks,
98 SparseMatrix<double> mult;
110 float fFactor = 2.0f;
111 int iSize = fFactor * iOrder;
112 int residual = (to - from) % iSize;
113 while (residual < iOrder) {
114 fFactor = fFactor - 0.1f;
115 iSize = fFactor * iOrder;
116 residual = (to - from) % iSize;
118 if ((iSize < iOrder)) {
119 qInfo() <<
"[Filter::filterData] Sliced data block size is too small. Filtering whole block at once.";
125 float quantum_sec = iSize / pFiffRawData->info.sfreq;
126 fiff_int_t quantum = ceil(
static_cast<double>(quantum_sec) * pFiffRawData->info.sfreq);
129 bool first_buffer =
true;
132 MatrixXd matData, matDataOverlap;
135 for (first = from; first < to; first += quantum) {
136 last = first + quantum - 1;
141 if (!pFiffRawData->read_raw_segment(matData, times, mult, first, last, sel)) {
142 qWarning(
"[Filter::filterData] Error during read_raw_segment\n");
146 qInfo() <<
"Filtering and writing block" << first <<
"to" << last;
152 first_buffer =
false;
161 const bool lastBlock = last == to;
162 const int writeFrom = first == from ? iOrder / 2 : 0;
163 const int writeTo = lastBlock ? matData.cols() - iOrder / 2 : matData.cols() - iOrder;
165 matData.block(0, 0, matData.rows(), iOrder) += matDataOverlap;
167 outfid->write_raw_buffer(matData.block(0, writeFrom, matData.rows(), writeTo - writeFrom), cals);
169 matDataOverlap = matData.block(0, matData.cols() - iOrder, matData.rows(), iOrder);
172 outfid->finish_writing_raw();
187 const RowVectorXi& vecPicks,
192 if (matData.cols() < iOrder) {
193 qWarning() << QString(
"[Filter::filterData] Filter length/order is bigger than data length. Returning.");
198 dCenterfreq = dCenterfreq / (dSFreq / 2.0);
199 bandwidth = bandwidth / (dSFreq / 2.0);
200 dTransition = dTransition / (dSFreq / 2.0);
223 const RowVectorXi& vecPicks,
230 if (matData.cols() < iOrder) {
231 qWarning() <<
"[Filter::filterData] Filter length/order is bigger than data length. Returning.";
236 MatrixXd matDataOut(matData.rows(), matData.cols() + iOrder);
237 matDataOut.setZero();
238 MatrixXd sliceFiltered;
241 float fFactor = 2.0f;
242 int iSize = fFactor * iOrder;
243 int residual = matData.cols() % iSize;
244 while (residual < iOrder) {
245 fFactor = fFactor - 0.1f;
246 iSize = fFactor * iOrder;
247 residual = matData.cols() % iSize;
249 if (iSize < iOrder) {
250 iSize = matData.cols();
255 if (matData.cols() > iSize) {
257 int numSlices = ceil(
static_cast<float>(matData.cols()) /
static_cast<float>(iSize));
259 for (
int i = 0; i < numSlices; i++) {
260 if (i == numSlices - 1) {
262 iSize = matData.cols() - (iSize * (numSlices - 1));
266 sliceFiltered =
filterDataBlock(matData.block(0, from, matData.rows(), iSize),
273 matDataOut.block(0, 0, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
275 matDataOut.block(0, from, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
290 return matDataOut.block(0, iOrder / 2, matDataOut.rows(), matData.cols());
297 const RowVectorXi& vecPicks,
304 if (matData.cols() < iOrder) {
305 qWarning() << QString(
"[Filter::filterDataBlock] Filter length/order is bigger than data length. Returning.");
314 RowVectorXi vecPicksNew = vecPicks;
315 if (vecPicksNew.cols() == 0) {
316 vecPicksNew = RowVectorXi::LinSpaced(matData.rows(), 0, matData.rows() - 1);
320 QList<FilterObject> timeData;
324 for (qint32 i = 0; i < vecPicksNew.cols(); ++i) {
326 data.
iRow = vecPicksNew[i];
327 data.
vecData = matData.row(vecPicksNew[i]);
328 timeData.append(data);
332 MatrixXd matDataOut(matData.rows(), matData.cols() + iOrder);
333 matDataOut.setZero();
334 matDataOut.block(0, iOrder / 2, matData.rows(), matData.cols()) = matData;
337 QFuture<void> future = QtConcurrent::map(timeData,
339 future.waitForFinished();
341 for (
int i = 0; i < timeData.size(); ++i) {
347 RowVectorXd tempData;
349 for (
int r = 0; r < timeData.size(); r++) {
351 matDataOut.row(timeData.at(r).iRow) = timeData.at(r).vecData;
377 const RowVectorXi& vecPicks,
383 if (matData.cols() < iOrder) {
384 qWarning() << QString(
"[Filter::filterData] Filter length/order is bigger than data length. Returning.");
389 dCenterfreq = dCenterfreq / (dSFreq / 2.0);
390 bandwidth = bandwidth / (dSFreq / 2.0);
391 dTransition = dTransition / (dSFreq / 2.0);
394 FilterKernel filter = FilterKernel(
"filter_kernel",
414 const FilterKernel& filterKernel,
415 const RowVectorXi& vecPicks,
423 if (matData.cols() < iOrder) {
424 qWarning() <<
"[Filter::filterData] Filter length/order is bigger than data length. Returning.";
429 if (m_matOverlapBack.cols() != iOrder || m_matOverlapBack.rows() < matData.rows()) {
430 m_matOverlapBack.resize(matData.rows(), iOrder);
431 m_matOverlapBack.setZero();
434 if (m_matOverlapFront.cols() != iOrder || m_matOverlapFront.rows() < matData.rows()) {
435 m_matOverlapFront.resize(matData.rows(), iOrder);
436 m_matOverlapFront.setZero();
440 MatrixXd matDataOut(matData.rows(), matData.cols() + iOrder);
441 matDataOut.setZero();
442 MatrixXd sliceFiltered;
445 float fFactor = 2.0f;
446 int iSize = fFactor * iOrder;
447 int residual = matData.cols() % iSize;
448 while (residual < iOrder) {
449 fFactor = fFactor - 0.1f;
450 iSize = fFactor * iOrder;
451 residual = matData.cols() % iSize;
453 if (iSize < iOrder) {
454 iSize = matData.cols();
459 if (matData.cols() > iSize) {
461 int numSlices = ceil(
static_cast<float>(matData.cols()) /
static_cast<float>(iSize));
463 for (
int i = 0; i < numSlices; i++) {
464 if (i == numSlices - 1) {
466 iSize = matData.cols() - (iSize * (numSlices - 1));
470 sliceFiltered =
filterDataBlock(matData.block(0, from, matData.rows(), iSize),
476 matDataOut.block(0, 0, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
478 matDataOut.block(0, from, matData.rows(), sliceFiltered.cols()) += sliceFiltered;
481 if (bFilterEnd && (i == 0)) {
482 matDataOut.block(0, 0, matDataOut.rows(), iOrder) += m_matOverlapBack;
483 }
else if (!bFilterEnd && (i == numSlices - 1)) {
484 matDataOut.block(0, matDataOut.cols() - iOrder, matDataOut.rows(), iOrder) += m_matOverlapFront;
496 matDataOut.block(0, 0, matDataOut.rows(), iOrder) += m_matOverlapBack;
498 matDataOut.block(0, matDataOut.cols() - iOrder, matDataOut.rows(), iOrder) += m_matOverlapFront;
503 m_matOverlapBack = matDataOut.block(0, matDataOut.cols() - iOrder, matDataOut.rows(), iOrder);
504 m_matOverlapFront = matDataOut.block(0, 0, matDataOut.rows(), iOrder);
509 return matDataOut.block(0, 0, matDataOut.rows(), matData.cols());
517 m_matOverlapBack.resize(0, 0);
518 m_matOverlapFront.resize(0, 0);
524 const MatrixXi& matEvents,
529 float fTBaselineFromS,
531 const QMap<QString, double>& mapReject,
533 const QStringList& lExcludeChs,
534 const RowVectorXi& picks)
541 MatrixXi selected = MatrixXi::Zero(1, matEvents.rows());
542 for (p = 0; p < matEvents.rows(); ++p) {
543 if (matEvents(p, 1) == 0 && matEvents(p, 2) == eventType) {
544 selected(0, count) = p;
548 selected.conservativeResize(1, count);
550 qInfo(
"[RTPROCESSINGLIB::computeFilteredAverage] %d matching events found", count);
554 RowVectorXi picksNew = picks;
555 if (picks.cols() <= 0) {
556 picksNew.resize(raw.
info.
chs.size());
557 for (
int i = 0; i < raw.
info.
chs.size(); ++i) {
567 std::unique_ptr<MNEEpochData> epoch;
570 for (p = 0; p < count; ++p) {
572 event_samp = matEvents(selected(p), 0);
577 epoch = std::make_unique<MNEEpochData>();
579 if (raw.
read_raw_segment(epoch->epoch, timesDummy, from - iFilterDelay, to + iFilterDelay, picksNew)) {
584 times.resize(1, to - from + 1);
585 for (qint32 i = 0; i < times.cols(); ++i)
586 times(0, i) = (
static_cast<float>(from - event_samp + i)) / raw.
info.
sfreq;
589 epoch->event = eventType;
590 epoch->eventSample = event_samp;
591 epoch->tmin = fTMinS;
592 epoch->tmax = fTMaxS;
599 if (epoch->bReject) {
604 if (!lstEpochDataList.isEmpty()) {
605 if (epoch->epoch.size() == lstEpochDataList.last()->epoch.size()) {
612 qWarning(
"[MNEEpochDataList::readEpochs] Can't read the event data segments.");
616 qInfo().noquote() <<
"[MNEEpochDataList::readEpochs] Read a total of" << lstEpochDataList.size() <<
"epochs of type" << eventType <<
"and marked" << dropCount <<
"for rejection.";
618 if (bApplyBaseline) {
619 QPair<float, float> baselinePair(fTBaselineFromS, fTBaselineToS);
623 if (!mapReject.isEmpty()) {
627 if (lstEpochDataList.isEmpty())
631 FiffEvoked evoked = lstEpochDataList.
average(raw.
info,
633 first +
static_cast<fiff_int_t>(lstEpochDataList.first()->epoch.cols()) - 1);
634 evoked.
baseline = bApplyBaseline ? QPair<float, float>(fTBaselineFromS, fTBaselineToS)
635 : QPair<float, float>(0.0f, 0.0f);
FIFF tag-kind, block-kind and type-code numerical definitions, authoritative for FIFFLIB.
constexpr auto 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.
QPair< float, float > baseline
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())