v2.0.0
Loading...
Searching...
No Matches
mne_bem.cpp
Go to the documentation of this file.
1//=============================================================================================================
22
23//=============================================================================================================
24// INCLUDES
25//=============================================================================================================
26
27#include "mne_bem.h"
28
29#include <math/warp.h>
30#include <fs/fs_label.h>
31
32//=============================================================================================================
33// QT INCLUDES
34//=============================================================================================================
35
36#include <QFile>
37
38#include <stdexcept>
39//=============================================================================================================
40// USED NAMESPACES
41//=============================================================================================================
42
43using namespace UTILSLIB;
44using namespace FSLIB;
45using namespace MNELIB;
46using namespace FIFFLIB;
47using namespace Eigen;
48
49//=============================================================================================================
50// DEFINE MEMBER METHODS
51//=============================================================================================================
52
56
57//=============================================================================================================
58
59MNEBem::MNEBem(QIODevice& p_IODevice) //const MNEBem &p_MNEBem
60//: m_qListBemSurface()
61{
62 FiffStream::SPtr t_pStream(new FiffStream(&p_IODevice));
63
64 if (!MNEBem::readFromStream(t_pStream, true, *this)) {
65 t_pStream->close();
66 throw std::runtime_error("Could not read the bem surfaces\n");
67 //ToDo error(me,'Could not read the bem surfaces (%s)',mne_omit_first_line(lasterr));
68 // return false;
69 }
70
71 // bool testStream =t_pStream->device()->isOpen();
72}
73
74//=============================================================================================================
75
79
80//=============================================================================================================
81
83{
84 m_qListBemSurface.clear();
85}
86
87//=============================================================================================================
88
89bool MNEBem::readFromStream(FiffStream::SPtr& p_pStream, bool add_geom, MNEBem& p_Bem)
90{
91 //
92 // Open the file, create directory
93 //
94 bool open_here = false;
95 QFile t_file; //ToDo TCPSocket;
96
97 if (!p_pStream->device()->isOpen()) {
98 QString t_sFileName = p_pStream->streamName();
99
100 t_file.setFileName(t_sFileName);
101 p_pStream = FiffStream::SPtr(new FiffStream(&t_file));
102 if (!p_pStream->open()) {
103 return false;
104 }
105 open_here = true;
106 // if(t_pDir)
107 // delete t_pDir;
108 }
109
110 //
111 // Find all BEM surfaces
112 //
113
114 QList<FiffDirNode::SPtr> bem = p_pStream->dirtree()->dir_tree_find(FIFFB_BEM);
115 if (bem.isEmpty()) {
116 qCritical() << "No BEM block found!";
117 if (open_here) {
118 p_pStream->close();
119 }
120 return false;
121 }
122
123 QList<FiffDirNode::SPtr> bemsurf = p_pStream->dirtree()->dir_tree_find(FIFFB_BEM_SURF);
124 if (bemsurf.isEmpty()) {
125 qCritical() << "No BEM surfaces found!";
126 if (open_here) {
127 p_pStream->close();
128 }
129 return false;
130 }
131
132 QList<MNEBemSurface> surfaces;
133 for (int k = 0; k < bemsurf.size(); ++k) {
134 MNEBemSurface p_BemSurface;
135 qInfo("\tReading a BEM surface...");
136 if (!MNEBem::readBemSurface(p_pStream, bemsurf[k], p_BemSurface)) {
137 if (open_here) {
138 p_pStream->close();
139 }
140 return false;
141 }
142 p_BemSurface.addTriangleData();
143 if (add_geom || p_BemSurface.nn.rows() == 0) {
144 p_BemSurface.addVertexNormals();
145 }
146 qInfo("\t[done]\n");
147
148 surfaces.append(p_BemSurface);
149 }
150 p_Bem.m_qListBemSurface.append(surfaces);
151
152 qInfo("\t%lld bem surfaces read\n", static_cast<long long>(bemsurf.size()));
153
154 if (open_here) {
155 p_pStream->close();
156 }
157 return true;
158}
159
160//=============================================================================================================
161
162bool MNEBem::readBemSurface(FiffStream::SPtr& p_pStream, const FiffDirNode::SPtr& p_Tree, MNEBemSurface& p_BemSurface)
163{
164 p_BemSurface.clear();
165
166 FiffTag::UPtr t_pTag;
167
168 //=====================================================================
169 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_ID, t_pTag)) {
170 p_BemSurface.id = FIFFV_BEM_SURF_ID_UNKNOWN;
171 } else {
172 p_BemSurface.id = *t_pTag->toInt();
173 }
174
175 // qDebug() << "Read BemSurface ID; type:" << t_pTag->getType() << "value:" << *t_pTag->toInt();
176
177 //=====================================================================
178 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SIGMA, t_pTag)) {
179 p_BemSurface.sigma = 1.0;
180 } else {
181 p_BemSurface.sigma = *t_pTag->toFloat();
182 }
183
184 // qDebug() <<
185
186 //=====================================================================
187 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NNODE, t_pTag)) {
188 qWarning() << "np not found!";
189 return false;
190 } else {
191 p_BemSurface.np = *t_pTag->toInt();
192 }
193
194 // qDebug() <<
195
196 //=====================================================================
197 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NTRI, t_pTag)) {
198 qWarning() << "ntri not found!";
199 return false;
200 } else {
201 p_BemSurface.ntri = *t_pTag->toInt();
202 }
203
204 // qDebug() <<
205
206 //=====================================================================
207 if (!p_Tree->find_tag(p_pStream, FIFF_MNE_COORD_FRAME, t_pTag)) {
208 qWarning() << "FIFF_MNE_COORD_FRAME not found, trying FIFF_BEM_COORD_FRAME.";
209 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_COORD_FRAME, t_pTag)) {
210 qWarning() << "Coordinate frame information not found.";
211 return false;
212 } else {
213 p_BemSurface.coord_frame = *t_pTag->toInt();
214 }
215 } else {
216 p_BemSurface.coord_frame = *t_pTag->toInt();
217 }
218
219 // qDebug() <<
220
221 //=====================================================================
222 //
223 // Vertices, normals, and triangles
224 //
225 //=====================================================================
226 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NODES, t_pTag)) {
227 qWarning() << "Vertex data not found.";
228 return false;
229 }
230
231 p_BemSurface.rr = t_pTag->toFloatMatrix().transpose();
232 qint32 rows_rr = p_BemSurface.rr.rows();
233
234 if (rows_rr != p_BemSurface.np) {
235 qWarning() << "Vertex information is incorrect.";
236 return false;
237 }
238
239 // qDebug() << "Surf Nodes; type:" << t_pTag->getType();
240
241 //=====================================================================
242 // Stored normals are optional (mne-python, MNE-C); readFromStream computes missing ones.
243 if (p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_NORMALS, t_pTag) || p_Tree->find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_NORMALS, t_pTag)) {
244 p_BemSurface.nn = t_pTag->toFloatMatrix().transpose();
245 if (p_BemSurface.nn.rows() != p_BemSurface.np) {
246 qWarning() << "Vertex normal information is incorrect.";
247 return false;
248 }
249 } else {
250 p_BemSurface.nn.resize(0, 3);
251 }
252
253 // qDebug() << "Bem Vertex Normals; type:" << t_pTag->getType();
254
255 //=====================================================================
256 if (p_BemSurface.ntri > 0) {
257 if (!p_Tree->find_tag(p_pStream, FIFF_BEM_SURF_TRIANGLES, t_pTag)) {
258 if (!p_Tree->find_tag(p_pStream, FIFF_MNE_SOURCE_SPACE_TRIANGLES, t_pTag)) {
259 qWarning() << "Triangulation not found.";
260 return false;
261 } else {
262 p_BemSurface.itris = t_pTag->toIntMatrix().transpose();
263 p_BemSurface.itris -= MatrixXi::Constant(p_BemSurface.itris.rows(), 3, 1); //0 based indizes
264 }
265 } else {
266 p_BemSurface.itris = t_pTag->toIntMatrix().transpose();
267 p_BemSurface.itris -= MatrixXi::Constant(p_BemSurface.itris.rows(), 3, 1); //0 based indizes
268 }
269
270 if (p_BemSurface.itris.rows() != p_BemSurface.ntri) {
271 qWarning() << "Triangulation information is incorrect.";
272 return false;
273 }
274 } else {
275 p_BemSurface.itris.resize(0, 3);
276 }
277
278 return true;
279}
280
281//=============================================================================================================
282
283bool MNEBem::write(QIODevice& p_IODevice)
284{
285 FiffStream::SPtr t_pStream = FiffStream::start_file(p_IODevice);
286 if (!t_pStream) {
287 return false;
288 }
289 qInfo("Write BEM surface in %s...\n", t_pStream->streamName().toUtf8().constData());
290 this->writeToStream(t_pStream.data());
291 t_pStream->end_file();
292 return true;
293}
294
295//=============================================================================================================
296
298{
299 p_pStream->start_block(FIFFB_BEM);
300 for (qint32 h = 0; h < m_qListBemSurface.size(); ++h) {
301 qInfo("\tWrite a bem surface... ");
302 p_pStream->start_block(FIFFB_BEM_SURF);
303 m_qListBemSurface[h].writeToStream(p_pStream);
304 p_pStream->end_block(FIFFB_BEM_SURF);
305 qInfo("[done]\n");
306 }
307 qInfo("\t%lld bem surfaces written\n", static_cast<long long>(m_qListBemSurface.size()));
308 p_pStream->end_block(FIFFB_BEM);
309}
310
311//=============================================================================================================
312
313const MNEBemSurface& MNEBem::operator[](qint32 idx) const
314{
315 //
316 // Falling back to surface 0 is no help when there are no surfaces at all:
317 // indexing an empty QList asserts in a debug build and is undefined
318 // otherwise. Hand back a default surface instead, which callers can spot
319 // through its empty vertex list.
320 //
321 if (m_qListBemSurface.isEmpty()) {
322 qWarning("Warning: No BEM surfaces available! Returning an empty surface.");
323
324 static const MNEBemSurface defaultSurface;
325
326 return defaultSurface;
327 }
328
329 if (idx < 0 || idx >= m_qListBemSurface.length()) {
330 qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
331 idx = 0;
332 }
333 return m_qListBemSurface[idx];
334}
335
336//=============================================================================================================
337
339{
340 //
341 // See the const overload: an empty list has no surface 0 to fall back to,
342 // so keep one default surface around rather than indexing out of bounds.
343 //
344 if (m_qListBemSurface.isEmpty()) {
345 qWarning("Warning: No BEM surfaces available! Returning an empty surface.");
346
347 static MNEBemSurface defaultSurface;
348 defaultSurface = MNEBemSurface();
349
350 return defaultSurface;
351 }
352
353 if (idx < 0 || idx >= m_qListBemSurface.length()) {
354 qWarning("Warning: Required surface doesn't exist! Returning surface '0'.");
355 idx = 0;
356 }
357 return m_qListBemSurface[idx];
358}
359
360//=============================================================================================================
361
363{
364 this->m_qListBemSurface.append(surf);
365 return *this;
366}
367
368//=============================================================================================================
369
371{
372 this->m_qListBemSurface.append(*surf);
373 return *this;
374}
375
376//=============================================================================================================
377
378void MNEBem::warp(const MatrixXf& sLm, const MatrixXf& dLm)
379{
380 Warp help;
381 QList<MatrixXf> vertList;
382 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
383 vertList.append(this->m_qListBemSurface[i].rr);
384 }
385
386 help.calculate(sLm, dLm, vertList);
387
388 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
389 this->m_qListBemSurface[i].rr = vertList.at(i);
390 }
391 return;
392}
393
394//=============================================================================================================
395
397{
398 MatrixX3f vert;
399 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
400 vert = this->m_qListBemSurface[i].rr;
401 vert = trans.apply_trans(vert);
402 this->m_qListBemSurface[i].rr = vert;
403 }
404 return;
405}
406
407//=============================================================================================================
408
410{
411 MatrixX3f vert;
412 for (int i = 0; i < this->m_qListBemSurface.size(); i++) {
413 vert = this->m_qListBemSurface[i].rr;
414 vert = trans.apply_inverse_trans(vert);
415 this->m_qListBemSurface[i].rr = vert;
416 }
417 return;
418}
constexpr auto FIFF_MNE_SOURCE_SPACE_NORMALS
constexpr auto FIFF_MNE_COORD_FRAME
constexpr auto FIFF_MNE_SOURCE_SPACE_TRIANGLES
constexpr auto FIFF_BEM_SURF_TRIANGLES
Definition fiff_file.h:727
constexpr auto FIFFB_BEM
Definition fiff_file.h:394
constexpr auto FIFFV_BEM_SURF_ID_UNKNOWN
Definition fiff_file.h:739
constexpr auto FIFF_BEM_SURF_NNODE
Definition fiff_file.h:724
constexpr auto FIFF_BEM_SURF_NORMALS
Definition fiff_file.h:728
constexpr auto FIFF_BEM_SIGMA
Definition fiff_file.h:735
constexpr auto FIFF_BEM_SURF_NODES
Definition fiff_file.h:726
constexpr auto FIFF_BEM_SURF_ID
Definition fiff_file.h:722
constexpr auto FIFFB_BEM_SURF
Definition fiff_file.h:395
constexpr auto FIFF_BEM_SURF_NTRI
Definition fiff_file.h:725
constexpr auto FIFF_BEM_COORD_FRAME
Definition fiff_file.h:734
Thin-plate-spline 3-D warp from landmark correspondences.
Reader and in-memory representation of a FreeSurfer/MNE surface label (.label).
Boundary element model bundle (inner skull, outer skull, outer skin) loaded from -bem....
Core MNE data structures (source spaces, source estimates, hemispheres).
FreeSurfer surface, annotation and parcellation I/O for mne-cpp.
FIFF file I/O, in-memory data structures and high-level readers/writers.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
Eigen::MatrixX3f apply_inverse_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
Eigen::MatrixX3f apply_trans(const Eigen::MatrixX3f &rr, bool do_move=true) const
QSharedPointer< FiffDirNode > SPtr
FIFF tag-stream reader/writer: wraps a QIODevice and exposes typed read_* / write_* methods for every...
fiff_long_t start_block(fiff_int_t kind)
QSharedPointer< FiffStream > SPtr
static FiffStream::SPtr start_file(QIODevice &p_IODevice)
fiff_long_t end_block(fiff_int_t kind, fiff_int_t next=FIFFV_NEXT_SEQ)
std::unique_ptr< FiffTag > UPtr
Definition fiff_tag.h:165
Thin-plate-spline 3-D warp fitted from landmark correspondences.
Definition warp.h:78
Eigen::MatrixXf calculate(const Eigen::MatrixXf &sLm, const Eigen::MatrixXf &dLm, const Eigen::MatrixXf &sVert)
bool write(QIODevice &p_IODevice)
Definition mne_bem.cpp:283
void invtransform(const FIFFLIB::FiffCoordTrans &trans)
Definition mne_bem.cpp:409
void transform(const FIFFLIB::FiffCoordTrans &trans)
Definition mne_bem.cpp:396
static bool readBemSurface(FIFFLIB::FiffStream::SPtr &p_pStream, const FIFFLIB::FiffDirNode::SPtr &p_Tree, MNEBemSurface &p_BemSurface)
Definition mne_bem.cpp:162
void writeToStream(FIFFLIB::FiffStream *p_pStream)
Definition mne_bem.cpp:297
void warp(const Eigen::MatrixXf &sLm, const Eigen::MatrixXf &dLm)
Definition mne_bem.cpp:378
static bool readFromStream(FIFFLIB::FiffStream::SPtr &p_pStream, bool add_geom, MNEBem &p_Bem)
Definition mne_bem.cpp:89
const MNEBemSurface & operator[](qint32 idx) const
Definition mne_bem.cpp:313
MNEBem & operator<<(const MNEBemSurface &surf)
Definition mne_bem.cpp:362
BEM surface provides geometry information.