34#include <Eigen/Geometry>
36#define _USE_MATH_DEFINES
88 Eigen::Vector3f& scales)
91 using FidMatrix = Eigen::Matrix<float, 3, 3, Eigen::RowMajor>;
92 FidMatrix head_fid, mri_fid;
93 bool head_fid_found[3] = {
false,
false,
false};
94 bool mri_fid_found[3] = {
false,
false,
false};
97 float nasion_weight = 5.0;
99 for (j = 0; j < 2; j++) {
101 FidMatrix& fid = (j == 0) ? head_fid : mri_fid;
102 bool* found = (j == 0) ? head_fid_found : mri_fid_found;
104 for (k = 0; k < d.
npoint; k++) {
108 fid.row(0) = Eigen::Map<const Eigen::RowVector3f>(d.
points[k].r);
112 fid.row(1) = Eigen::Map<const Eigen::RowVector3f>(d.
points[k].r);
116 fid.row(2) = Eigen::Map<const Eigen::RowVector3f>(d.
points[k].r);
123 for (k = 0; k < 3; k++) {
124 if (!head_fid_found[k]) {
125 qCritical(
"Some of the MEG fiducials were missing");
129 if (!mri_fid_found[k]) {
130 qCritical(
"Some of the MRI fiducials were missing");
137 qInfo(
"xscale = %.3f yscale = %.3f zscale = %.3f\n",scales[0],scales[1],scales[2]);
139 for (j = 0; j < 3; j++)
140 for (k = 0; k < 3; k++)
141 mri_fid(j,k) = mri_fid(j,k)*scales[k];
148 mri_fid.row(0).data(),mri_fid.row(1).data(),mri_fid.row(2).data()));
154 for (k = 0; k < head_dig.
nfids(); k++)
155 Eigen::Map<Eigen::Vector3f>(head_dig.
mri_fids[k].r) = mri_fid.row(k).transpose();
157 qInfo(
"After simple alignment : \n");
164 for (k = 0; k < niter; k++) {
165 if (
iterate_alignment_once(head_dig,nasion_weight,Eigen::Vector3f(mri_fid.row(1).transpose()),k == niter-1 && niter > 1) ==
FAIL)
169 qInfo(
"%d / %d iterations done. RMS dist = %7.1f mm\n",k,niter,
171 qInfo(
"After refinement :\n");
182 const Eigen::Matrix<float, 3, 3, Eigen::RowMajor>& mri_fid,
183 Eigen::Vector3f& scales)
186 float simplex_size = 2e-2;
187 Eigen::VectorXf r0(3);
190 scales[0] = scales[1] = scales[2] = 1.0;
192 Eigen::MatrixXf dig_rr(dig.
npoint, 3);
193 Eigen::MatrixXf head_rr(
np, 3);
196 for (k = 0, ndig = 0; k < dig.
npoint; k++) {
197 if (dig.
points[k].r[2] > 0) {
198 dig_rr.row(ndig++) = Eigen::Map<const Eigen::RowVector3f>(dig.
points[k].r);
206 qInfo(
"Polhemus : (%.1f %.1f %.1f) mm R = %.1f mm\n",1000*r0[0],1000*r0[1],1000*r0[2],1000*Rdig);
209 Eigen::Vector3f LR = mri_fid.row(2).transpose() - mri_fid.row(0).transpose();
210 Eigen::Vector3f LN = mri_fid.row(1).transpose() - mri_fid.row(0).transpose();
211 Eigen::Vector3f norm = LR.cross(LN);
214 for (k = 0, nhead = 0; k <
np; k++) {
215 Eigen::Vector3f diff_vec =
rr.row(k).transpose() - mri_fid.row(0).transpose();
216 if (diff_vec.dot(norm) > 0) {
217 head_rr.row(nhead++) =
rr.row(k);
225 qInfo(
"Scalp : (%.1f %.1f %.1f) mm R = %.1f mm\n",1000*r0[0],1000*r0[1],1000*r0[2],1000*Rscalp);
227 scales[0] = scales[1] = scales[2] = Rdig/Rscalp;
241 d.dist_valid =
false;
243 for (k = 0; k < d.npoint; k++) {
248 if (std::fabs(d.dist(k)) > maxdist &&
255 qInfo(
"%d points discarded (maxdist = %6.1f mm).\n",discarded,1000*maxdist);
263 bool do_all,
bool do_approx)
const
270 Q_ASSERT(dig.head_mri_t);
271 const FiffCoordTrans& t = (dig.head_mri_t_adj && !dig.head_mri_t_adj->isEmpty()) ? *dig.head_mri_t_adj : *dig.head_mri_t;
279 dig.dist.conservativeResize(dig.npoint);
280 if (dig.closest.size() == 0) {
284 dig.closest = Eigen::VectorXi::Constant(dig.npoint, -1);
287 dig.closest_point.setZero(dig.npoint,3);
288 Eigen::VectorXi closest(dig.npoint);
289 Eigen::VectorXf dists(dig.npoint);
291 for (k = 0, nactive = 0; k < dig.npoint; k++) {
292 if ((dig.active[k] && !dig.discard[k]) || do_all) {
293 point = dig.points.at(k);
294 rr.row(nactive) = Eigen::Map<const Eigen::RowVector3f>(
point.r);
297 closest[nactive] = dig.closest(k);
298 if (closest[nactive] < 0)
302 closest[nactive] = -1;
312 qInfo(
"Inside or outside for %d points...",nactive);
313 for (k = 0, nactive = 0; k < dig.npoint; k++) {
314 if ((dig.active[k] && !dig.discard[k]) || do_all) {
315 dig.dist(k) = dists[nactive];
316 dig.closest(k) = closest[nactive];
318 Eigen::Vector3f pt = Eigen::Map<const Eigen::Vector3f>(
rr.row(nactive).data());
320 dig.closest_point.row(k) = proj.transpose();
326 if (!do_approx &&
false) {
327 Eigen::Vector3f pt = Eigen::Map<const Eigen::Vector3f>(
rr.row(nactive).data());
329 dig.dist(k) = - std::fabs(dig.dist(k));
331 dig.dist(k) = std::fabs(dig.dist(k));
340 dig.dist_valid =
true;
349 const std::optional<Eigen::Vector3f>& nasion_mri,
355 Eigen::MatrixXf rr_head(dig.npoint, 3);
356 Eigen::MatrixXf rr_mri(dig.npoint, 3);
357 Eigen::VectorXf w = Eigen::VectorXf::Zero(dig.npoint);
361 float max_diff = 40e-3;
363 if (!dig.head_mri_t_adj) {
364 qCritical()<<
"Not adjusting the transformation";
372 for (k = 0, nactive = 0; k < dig.npoint; k++) {
373 if (dig.active[k] && !dig.discard[k]) {
374 point = dig.points.at(k);
375 rr_head.row(nactive) = Eigen::Map<const Eigen::RowVector3f>(
point.r);
376 rr_mri.row(nactive) = dig.closest_point.row(k);
382 w[nactive] = nasion_weight;
384 rr_mri.row(nactive) = nasion_mri->transpose();
385 rr_head.row(nactive) = nasion_mri->transpose();
386 Q_ASSERT(dig.head_mri_t || dig.head_mri_t_adj);
388 dig.head_mri_t_adj ? *dig.head_mri_t_adj : *dig.head_mri_t,
398 qCritical() <<
"Not enough points to do the alignment";
402 rr_head.topRows(nactive),
403 rr_mri.topRows(nactive),
405 max_diff)).isEmpty())
408 if (dig.head_mri_t_adj)
409 dig.head_mri_t_adj = std::make_unique<FiffCoordTrans>(t);
413 dig.dist_valid =
false;
427 for (k = 0, rms = 0.0, nactive = 0; k < dig.
npoint; k++)
429 rms = rms + dig.
dist(k)*dig.
dist(k);
433 rms = rms/(nactive-1);
446 for (k = 0; k < 3; k++) {
450 for (j = 0; j <
np; j++)
451 for (k = 0; k < 3; k++)
452 rr(j,k) =
rr(j,k)*scales[k];
460 Eigen::Vector3f mn =
rr.row(0).transpose();
461 Eigen::Vector3f mx = mn;
463 for (
int k = 1; k <
np; k++) {
464 Eigen::Vector3f r =
rr.row(k).transpose();
470 qInfo(
"%s:\n",tag.toUtf8().constData());
471 qInfo(
"\tx = %f ... %f mm\n",1000*mn[0],1000*mx[0]);
472 qInfo(
"\ty = %f ... %f mm\n",1000*mn[1],1000*mx[1]);
473 qInfo(
"\tz = %f ... %f mm\n",1000*mn[2],1000*mx[2]);
476 fov = std::max(mn.cwiseAbs().maxCoeff(), mx.cwiseAbs().maxCoeff());
487 if (name.startsWith(
"inflated") || name.startsWith(
"sphere") || name.startsWith(
"white"))
502 const int totalSize = ncolor *
np;
507 float curv_sum = 0.0f;
509 for (
int k = 0; k <
np; k++) {
510 const int base = k * ncolor;
511 curv_sum += std::fabs(
curv[k]);
513 for (
int j = 0; j < 3; j++)
520 for (
int k = 0; k <
np; k++) {
521 const int base = k * ncolor;
522 curv_sum += std::fabs(
curv[k]);
523 for (
int j = 0; j < 3; j++)
530 qInfo(
"Average curvature : %f\n",curv_sum/
np);
Lightweight triangulated surface (vertices, triangles, normals) used by surface-based routines.
Sparse linear operator that morphs a source estimate between two subjects.
One renderable surface (cortex / pial / inflated / BEM) inside an MSH display set.
constexpr float NEG_CURV_COLOR
constexpr int SHOW_CURVATURE_OVERLAY
constexpr float POS_CURV_COLOR
constexpr int SHOW_CURVATURE_NONE
constexpr int SHOW_OVERLAY_HEAT
constexpr float EVEN_CURV_COLOR
Mesh-display colour scale definition (FreeSurfer mri_glm style).
Picked vertices / triangles on a mesh display surface.
#define FIFFV_POINT_CARDINAL
#define FIFFV_POINT_NASION
High-level digitization data: dig points plus the device→head transform and fitting metadata that tog...
4x4 affine FIFF coordinate transform (FIFF_COORD_TRANS) annotated with source/destination coordinate-...
FIFF binary tag-stream layer: wraps a QIODevice to read and write FIFF tags, directories,...
Single digitization point (FIFF_DIG_POINT) with kind (cardinal/HPI/EEG/extra), identifier and 3D coor...
Best-fit sphere from a 3-D point cloud with closed-form and Nelder–Mead solvers.
Core MNE data structures (source spaces, source estimates, hemispheres).
FIFF file I/O, in-memory data structures and high-level readers/writers.
Labelled 4x4 FIFF affine: source frame, destination frame, rotation, translation and cached inverse.
static FiffCoordTrans procrustesAlign(int from_frame, int to_frame, const Eigen::MatrixXf &fromp, const Eigen::MatrixXf &top, const Eigen::VectorXf &w, float max_diff)
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
static FiffCoordTrans fromCardinalPoints(int from, int to, const float *rL, const float *rN, const float *rR)
One digitizer point: kind (cardinal/HPI/EEG/extra), ident, 3D position in FIFFV_COORD_HEAD.
Registration-ready digitization data: dig points, device→head transform and HPI fit metadata.
QList< FIFFLIB::FiffDigPoint > points
std::unique_ptr< FiffCoordTrans > head_mri_t_adj
QList< FIFFLIB::FiffDigPoint > mri_fids
void pickCardinalFiducials()
static bool fit_sphere_to_points(const Eigen::MatrixXf &rr, float simplex_size, Eigen::VectorXf &r0, float &R)
void get_head_scale(FIFFLIB::FiffDigitizerData &dig, const Eigen::Matrix< float, 3, 3, Eigen::RowMajor > &mri_fid, Eigen::Vector3f &scales)
float rms_digitizer_distance(FIFFLIB::FiffDigitizerData &dig) const
void decide_surface_extent(const QString &tag)
mneUserFreeFunc user_data_free
void scale(const Eigen::Vector3f &scales)
void calculate_digitizer_distances(FIFFLIB::FiffDigitizerData &dig, bool do_all, bool do_approx) const
int align_fiducials(FIFFLIB::FiffDigitizerData &head_dig, const FIFFLIB::FiffDigitizerData &mri_dig, int niter, int scale_head, float omit_dist, Eigen::Vector3f &scales)
void setup_curvature_colors()
Eigen::VectorXf vertex_colors
int discard_outlier_digitizer_points(FIFFLIB::FiffDigitizerData &d, float maxdist) const
int iterate_alignment_once(FIFFLIB::FiffDigitizerData &dig, int nasion_weight, const std::optional< Eigen::Vector3f > &nasion_mri, int last_step) const
void decide_curv_display(const QString &name)
double sum_solids(const Eigen::Vector3f &from) const
void find_closest_on_surface_approx(const PointsT &r, int np, Eigen::VectorXi &nearest_tri, Eigen::VectorXf &dist, int nstep) const
Eigen::Vector3f project_to_triangle(int tri, float p, float q) const
Eigen::Matrix< float, Eigen::Dynamic, 3, Eigen::RowMajor > PointsT
Eigen::Map< const Eigen::Vector3f > point(int k) const