v2.0.0
Loading...
Searching...
No Matches
inv_source_estimate.cpp
Go to the documentation of this file.
1//=============================================================================================================
20
21//=============================================================================================================
22// INCLUDES
23//=============================================================================================================
24
25#include "inv_source_estimate.h"
26
27//=============================================================================================================
28// QT INCLUDES
29//=============================================================================================================
30
31#include <QFile>
32#include <QDataStream>
33#include <QSharedPointer>
34#include <QDebug>
35
36#include <stdexcept>
37#include <QtEndian>
38//=============================================================================================================
39// USED NAMESPACES
40//=============================================================================================================
41
42using namespace INVLIB;
43using namespace FSLIB;
44using namespace Eigen;
45
46//=============================================================================================================
47// DEFINE MEMBER METHODS
48//=============================================================================================================
49
59
60//=============================================================================================================
61
62InvSourceEstimate::InvSourceEstimate(const MatrixXd& p_sol, const VectorXi& p_vertices, float p_tmin, float p_tstep)
63: data(p_sol)
64, vertices(p_vertices)
65, tmin(p_tmin)
66, tstep(p_tstep)
67, nVerticesLh(-1)
69, sourceSpaceType(InvSourceSpaceType::Unknown)
70, orientationType(InvOrientationType::Unknown)
71{
72 this->update_times();
73}
74
75//=============================================================================================================
76
78: data(p_SourceEstimate.data)
79, vertices(p_SourceEstimate.vertices)
80, times(p_SourceEstimate.times)
81, tmin(p_SourceEstimate.tmin)
82, tstep(p_SourceEstimate.tstep)
83, nVerticesLh(p_SourceEstimate.nVerticesLh)
84, method(p_SourceEstimate.method)
85, sourceSpaceType(p_SourceEstimate.sourceSpaceType)
86, orientationType(p_SourceEstimate.orientationType)
87, positions(p_SourceEstimate.positions)
88, couplings(p_SourceEstimate.couplings)
89, focalDipoles(p_SourceEstimate.focalDipoles)
90, connectivity(p_SourceEstimate.connectivity)
91{
92}
93
94//=============================================================================================================
95
97: tmin(0)
98, tstep(-1)
99, nVerticesLh(-1)
103{
104 if (!read(p_IODevice, *this)) {
105 throw std::runtime_error("Source estimation not found");
106 }
107}
108
109//=============================================================================================================
110
112{
113 data = MatrixXd();
114 vertices = VectorXi();
115 times = RowVectorXf();
116 tmin = 0;
117 tstep = 0;
118 nVerticesLh = -1;
122 positions = MatrixX3f();
123 couplings.clear();
124 focalDipoles.clear();
125 connectivity.clear();
126}
127
128//=============================================================================================================
129
131{
132 InvSourceEstimate p_sourceEstimateReduced;
133
134 qint32 rows = this->data.rows();
135
136 p_sourceEstimateReduced.data = MatrixXd::Zero(rows, n);
137 p_sourceEstimateReduced.data = this->data.block(0, start, rows, n);
138 p_sourceEstimateReduced.vertices = this->vertices;
139 p_sourceEstimateReduced.times = RowVectorXf::Zero(n);
140 p_sourceEstimateReduced.times = this->times.block(0, start, 1, n);
141 p_sourceEstimateReduced.tmin = p_sourceEstimateReduced.times(0);
142 p_sourceEstimateReduced.tstep = this->tstep;
143 p_sourceEstimateReduced.method = this->method;
144 p_sourceEstimateReduced.sourceSpaceType = this->sourceSpaceType;
145 p_sourceEstimateReduced.orientationType = this->orientationType;
146 p_sourceEstimateReduced.positions = this->positions;
147 p_sourceEstimateReduced.couplings = this->couplings;
148 p_sourceEstimateReduced.focalDipoles = this->focalDipoles;
149 p_sourceEstimateReduced.connectivity = this->connectivity;
150
151 return p_sourceEstimateReduced;
152}
153
154//=============================================================================================================
155
156bool InvSourceEstimate::read(QIODevice& p_IODevice, InvSourceEstimate& p_stc)
157{
158 QSharedPointer<QDataStream> t_pStream(new QDataStream(&p_IODevice));
159
160 t_pStream->setFloatingPointPrecision(QDataStream::SinglePrecision);
161 t_pStream->setByteOrder(QDataStream::BigEndian);
162 t_pStream->setVersion(QDataStream::Qt_5_0);
163
164 if (!t_pStream->device()->open(QIODevice::ReadOnly))
165 return false;
166
167 QFile* t_pFile = qobject_cast<QFile*>(&p_IODevice);
168 if (t_pFile)
169 qInfo("Reading source estimate from %s...", t_pFile->fileName().toUtf8().constData());
170 else
171 qInfo("Reading source estimate...");
172
173 // read start time in ms
174 *t_pStream >> p_stc.tmin;
175 p_stc.tmin /= 1000;
176 // read sampling rate in ms
177 *t_pStream >> p_stc.tstep;
178 p_stc.tstep /= 1000;
179 // read number of vertices
180 quint32 t_nVertices;
181 *t_pStream >> t_nVertices;
182 p_stc.vertices = VectorXi(t_nVertices);
183 // read the vertex indices
184 for (quint32 i = 0; i < t_nVertices; ++i)
185 *t_pStream >> p_stc.vertices[i];
186 // read the number of timepts
187 quint32 t_nTimePts;
188 *t_pStream >> t_nTimePts;
189 //
190 // read the data
191 //
192 p_stc.data = MatrixXd(t_nVertices, t_nTimePts);
193 for (qint32 i = 0; i < p_stc.data.array().size(); ++i) {
194 float value;
195 *t_pStream >> value;
196 p_stc.data.array()(i) = value;
197 }
198
199 //Update time vector
200 p_stc.update_times();
201
202 // close the file
203 t_pStream->device()->close();
204
205 qInfo("[done]");
206
207 return true;
208}
209
210//=============================================================================================================
211
212bool InvSourceEstimate::write(QIODevice& p_IODevice)
213{
214 // Create the file and save the essentials
215 QSharedPointer<QDataStream> t_pStream(new QDataStream(&p_IODevice));
216
217 t_pStream->setFloatingPointPrecision(QDataStream::SinglePrecision);
218 t_pStream->setByteOrder(QDataStream::BigEndian);
219 t_pStream->setVersion(QDataStream::Qt_5_0);
220
221 if (!t_pStream->device()->open(QIODevice::WriteOnly)) {
222 qWarning("Failed to write source estimate!");
223 return false;
224 }
225
226 QFile* t_pFile = qobject_cast<QFile*>(&p_IODevice);
227 if (t_pFile)
228 qInfo("Write source estimate to %s...", t_pFile->fileName().toUtf8().constData());
229 else
230 qInfo("Write source estimate...");
231
232 // write start time in ms
233 *t_pStream << static_cast<float>(1000 * this->tmin);
234 // write sampling rate in ms
235 *t_pStream << static_cast<float>(1000 * this->tstep);
236 // write number of vertices
237 *t_pStream << static_cast<quint32>(this->vertices.size());
238 // write the vertex indices
239 for (qint32 i = 0; i < this->vertices.size(); ++i)
240 *t_pStream << static_cast<quint32>(this->vertices[i]);
241 // write the number of timepts
242 *t_pStream << static_cast<quint32>(this->data.cols());
243 //
244 // write the data
245 //
246 for (qint32 i = 0; i < this->data.array().size(); ++i)
247 *t_pStream << static_cast<float>(this->data.array()(i));
248
249 // close the file
250 t_pStream->device()->close();
251
252 qInfo("[done]");
253 return true;
254}
255
256//=============================================================================================================
257
258bool InvSourceEstimate::writeHemispherePair(const QString& sBasePath)
259{
261 qWarning("InvSourceEstimate::writeHemispherePair - nVerticesLh is %d for %lld vertices."
262 " The hemisphere split point is unknown, cannot write an MNE compatible pair.",
263 nVerticesLh, static_cast<long long>(vertices.size()));
264 return false;
265 }
266
267 if (data.rows() != vertices.size()) {
268 qWarning("InvSourceEstimate::writeHemispherePair - data has %lld rows but there are"
269 " %lld vertices.",
270 static_cast<long long>(data.rows()), static_cast<long long>(vertices.size()));
271 return false;
272 }
273
274 // Accept a path that already carries one of the usual suffixes so callers
275 // can pass back a name they got from a file dialog.
276 QString sBase = sBasePath;
277 for (const QString& sSuffix : {QStringLiteral("-lh.stc"),
278 QStringLiteral("-rh.stc"),
279 QStringLiteral(".stc")}) {
280 if (sBase.endsWith(sSuffix, Qt::CaseInsensitive)) {
281 sBase.chop(sSuffix.size());
282 break;
283 }
284 }
285
286 const int iNumRh = static_cast<int>(vertices.size()) - nVerticesLh;
287
288 struct Hemisphere
289 {
290 QString sPath;
291 int iOffset;
292 int iCount;
293 };
294
295 const Hemisphere hemispheres[2] = {
296 {sBase + QStringLiteral("-lh.stc"), 0, nVerticesLh},
297 {sBase + QStringLiteral("-rh.stc"), nVerticesLh, iNumRh}};
298
299 for (const Hemisphere& hemi : hemispheres) {
300 InvSourceEstimate stcHemi;
301 stcHemi.tmin = this->tmin;
302 stcHemi.tstep = this->tstep;
303 stcHemi.times = this->times;
304 stcHemi.vertices = this->vertices.segment(hemi.iOffset, hemi.iCount);
305 stcHemi.data = this->data.block(hemi.iOffset, 0, hemi.iCount, this->data.cols());
306
307 QFile file(hemi.sPath);
308 if (!stcHemi.write(file)) {
309 qWarning("InvSourceEstimate::writeHemispherePair - Failed to write %s",
310 hemi.sPath.toUtf8().constData());
311 return false;
312 }
313 }
314
315 return true;
316}
317
318//=============================================================================================================
319
321{
322 QFile file(path);
323 if (!file.open(QIODevice::ReadOnly)) {
324 qWarning("InvSourceEstimate::read_w - Cannot open file %s", path.toUtf8().constData());
325 return InvSourceEstimate();
326 }
327
328 QDataStream stream(&file);
329 stream.setByteOrder(QDataStream::BigEndian);
330 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
331
332 // Skip 2-byte magic
333 quint16 magic;
334 stream >> magic;
335
336 // Read number of vertices (3-byte big-endian integer)
337 quint8 b0, b1, b2;
338 stream >> b0 >> b1 >> b2;
339 qint32 nVertices = (static_cast<qint32>(b0) << 16) | (static_cast<qint32>(b1) << 8) | static_cast<qint32>(b2);
340
341 VectorXi vertices(nVertices);
342 MatrixXd data(nVertices, 1);
343
344 for (qint32 i = 0; i < nVertices; ++i) {
345 // Read 3-byte vertex index
346 stream >> b0 >> b1 >> b2;
347 vertices[i] = (static_cast<qint32>(b0) << 16) | (static_cast<qint32>(b1) << 8) | static_cast<qint32>(b2);
348
349 // Read 4-byte big-endian float
350 float val;
351 stream >> val;
352 data(i, 0) = static_cast<double>(val);
353 }
354
355 file.close();
356
357 InvSourceEstimate stc(data, vertices, 0.0f, 0.0f);
358 return stc;
359}
360
361//=============================================================================================================
362
363void InvSourceEstimate::write_w(const QString& path) const
364{
365 if (isEmpty()) {
366 qWarning("InvSourceEstimate::write_w - Source estimate is empty");
367 return;
368 }
369
370 QFile file(path);
371 if (!file.open(QIODevice::WriteOnly)) {
372 qWarning("InvSourceEstimate::write_w - Cannot open file %s for writing", path.toUtf8().constData());
373 return;
374 }
375
376 QDataStream stream(&file);
377 stream.setByteOrder(QDataStream::BigEndian);
378 stream.setFloatingPointPrecision(QDataStream::SinglePrecision);
379
380 // Write 2-byte magic (zeros)
381 stream << static_cast<quint8>(0) << static_cast<quint8>(0);
382
383 // Write number of vertices as 3-byte big-endian integer
384 qint32 nVertices = static_cast<qint32>(vertices.size());
385 stream << static_cast<quint8>((nVertices >> 16) & 0xFF)
386 << static_cast<quint8>((nVertices >> 8) & 0xFF)
387 << static_cast<quint8>(nVertices & 0xFF);
388
389 // Write each vertex index (3 bytes) and value (4-byte float)
390 for (qint32 i = 0; i < nVertices; ++i) {
391 qint32 idx = vertices[i];
392 stream << static_cast<quint8>((idx >> 16) & 0xFF)
393 << static_cast<quint8>((idx >> 8) & 0xFF)
394 << static_cast<quint8>(idx & 0xFF);
395
396 stream << static_cast<float>(data(i, 0));
397 }
398
399 file.close();
400}
401
402//=============================================================================================================
403
404void InvSourceEstimate::update_times()
405{
406 if (data.cols() > 0) {
407 this->times = RowVectorXf(data.cols());
408 this->times[0] = this->tmin;
409 for (float i = 1; i < this->times.size(); ++i)
410 this->times[i] = this->times[i - 1] + this->tstep;
411 } else
412 this->times = RowVectorXf();
413}
414
415//=============================================================================================================
416
418{
419 if (this != &rhs) // protect against invalid self-assignment
420 {
421 data = rhs.data;
422 vertices = rhs.vertices;
423 times = rhs.times;
424 tmin = rhs.tmin;
425 tstep = rhs.tstep;
427 method = rhs.method;
430 positions = rhs.positions;
431 couplings = rhs.couplings;
434 }
435 // to support chained assignment operators (a=b=c), always return *this
436 return *this;
437}
438
439//=============================================================================================================
440
442{
443 return data.cols();
444}
445
446//=============================================================================================================
447
448VectorXi InvSourceEstimate::getIndicesByLabel(const QList<FsLabel>& lPickedLabels, bool bIsClustered) const
449{
450 VectorXi vIndexSourceLabels;
451
452 if (lPickedLabels.isEmpty()) {
453 qWarning() << "InvSourceEstimate::getIndicesByLabel - picked label list is empty. Returning.";
454 return vIndexSourceLabels;
455 }
456
457 if (bIsClustered) {
458 for (int i = 0; i < this->vertices.rows(); i++) {
459 for (int k = 0; k < lPickedLabels.size(); k++) {
460 if (this->vertices(i) == lPickedLabels.at(k).label_id) {
461 vIndexSourceLabels.conservativeResize(vIndexSourceLabels.rows() + 1, 1);
462 vIndexSourceLabels(vIndexSourceLabels.rows() - 1) = i;
463 break;
464 }
465 }
466 }
467 } else {
468 int hemi = 0;
469
470 for (int i = 0; i < this->vertices.rows(); i++) {
471 // Detect left right hemi separation
472 if (i > 0) {
473 if (this->vertices(i) < this->vertices(i - 1)) {
474 hemi = 1;
475 }
476 }
477
478 for (int k = 0; k < lPickedLabels.size(); k++) {
479 for (int l = 0; l < lPickedLabels.at(k).vertices.rows(); l++) {
480 if (this->vertices(i) == lPickedLabels.at(k).vertices(l) && lPickedLabels.at(k).hemi == hemi) {
481 vIndexSourceLabels.conservativeResize(vIndexSourceLabels.rows() + 1, 1);
482 vIndexSourceLabels(vIndexSourceLabels.rows() - 1) = i;
483 break;
484 }
485 }
486 }
487 }
488 }
489
490 return vIndexSourceLabels;
491}
InvSourceEstimate value type — central source-space data container produced by every INVLIB inverse s...
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
Inverse source estimation (MNE, dSPM, sLORETA, dipole fitting).
InvEstimateMethod
Definition inv_types.h:37
InvOrientationType
Definition inv_types.h:71
InvSourceSpaceType
Definition inv_types.h:58
std::vector< InvSourceCoupling > couplings
std::vector< InvFocalDipole > focalDipoles
Eigen::VectorXi getIndicesByLabel(const QList< FSLIB::FsLabel > &lPickedLabels, bool bIsClustered) const
static bool read(QIODevice &p_IODevice, InvSourceEstimate &p_stc)
InvSourceEstimate & operator=(const InvSourceEstimate &rhs)
static InvSourceEstimate read_w(const QString &path)
bool write(QIODevice &p_IODevice)
InvSourceSpaceType sourceSpaceType
InvOrientationType orientationType
bool writeHemispherePair(const QString &sBasePath)
std::vector< InvConnectivity > connectivity
InvSourceEstimate reduce(qint32 start, qint32 n)
void write_w(const QString &path) const