v2.0.0
Loading...
Searching...
No Matches
polhemus_coregistration.h
Go to the documentation of this file.
1//=============================================================================================================
40
41#ifndef UTILS_POLHEMUS_COREGISTRATION_H
42#define UTILS_POLHEMUS_COREGISTRATION_H
43
44//=============================================================================================================
45// INCLUDES
46//=============================================================================================================
47
48#include "../utils_global.h"
49#include "acquired_points.h"
50#include "polhemus_connection.h"
51
52//=============================================================================================================
53// QT INCLUDES
54//=============================================================================================================
55
56#include <QMatrix4x4>
57#include <QObject>
58#include <QQuaternion>
59#include <QSettings>
60#include <QVector3D>
61
62#include <vector>
63
64//=============================================================================================================
65// DEFINE NAMESPACE UTILSLIB
66//=============================================================================================================
67
68namespace UTILSLIB
69{
70
71//=============================================================================================================
83{
84 Q_OBJECT
85
86public:
87 explicit PolhemusCoregistration(QObject* parent = nullptr);
88 ~PolhemusCoregistration() override = default;
89
90 //=========================================================================================================
91 // Station assignment
92 //=========================================================================================================
93
94 void setTrackerStation(int station);
95 void setPenStation(int station);
96 void setProbeStation(int station);
97
98 int trackerStation() const { return m_trackerStation; }
99 int penStation() const { return m_penStation; }
100 int probeStation() const { return m_probeStation; }
101
102 //=========================================================================================================
103 // Pen tip offset (sensor body frame → tip)
104 //=========================================================================================================
105
106 void setPenTipOffset(const QVector3D& offset) { m_penTipOffset = offset; }
107 QVector3D penTipOffset() const { return m_penTipOffset; }
108 void setTipOffsetEnabled(bool on) { m_tipOffsetEnabled = on; }
109 bool tipOffsetEnabled() const { return m_tipOffsetEnabled; }
110
111 //=========================================================================================================
112 // Axis mirroring — compensate for transmitter placement
113 //=========================================================================================================
114
124 void setAxisMirror(bool mirrorX, bool mirrorY) { m_mirrorX = mirrorX; m_mirrorY = mirrorY; }
125 bool mirrorX() const { return m_mirrorX; }
126 bool mirrorY() const { return m_mirrorY; }
127
128 //=========================================================================================================
129 // Pivot calibration — determine pen tip offset by pivoting pen around its tip
130 //=========================================================================================================
131
132 enum class PivotState { Idle, WaitingForStart, Collecting, Done };
133
134 void startPivotCalibration();
135 void cancelPivotCalibration();
136 PivotState pivotState() const { return m_pivotState; }
137 int pivotSampleCount() const { return static_cast<int>(m_pivotPositions.size()); }
138 float pivotResidualMm() const { return m_pivotResidualMm; }
139
140 //=========================================================================================================
141 // Tracker-to-device calibration offset
142 //=========================================================================================================
143
151 void setTrackerToDeviceOffset(const QVector3D& translation,
152 const QQuaternion& rotation);
153
154 QVector3D trackerToDeviceTranslation() const { return m_offsetTranslation; }
155 QQuaternion trackerToDeviceRotation() const { return m_offsetRotation; }
156
157 //=========================================================================================================
158 // Optical path calibration — determine the microscope optical axis
159 // relative to the tracker sensor.
160 //
161 // Workflow:
162 // 1. Move the OPMI to a position, aim at a point on the head surface,
163 // mark the focus point with the stylus pen → captureOpticalCalibSample().
164 // 2. Repeat at 2–3 different positions / focal distances.
165 // 3. Call solveOpticalCalibration() to fit a 3D line through the focus
166 // points in the tracker's local frame, yielding the optical axis
167 // direction and the optical center offset from the tracker.
168 //=========================================================================================================
169
171 QVector3D trackerPos;
172 QQuaternion trackerOri;
173 QVector3D focusPoint;
174 };
175
181
183 int opticalCalibSampleCount() const { return static_cast<int>(m_opticalCalibSamples.size()); }
184
186 void clearOpticalCalibSamples();
187
198 bool captureObjectiveCenter();
199
201 bool hasObjectiveCenter() const { return m_hasObjectiveCenter; }
202
204 void clearObjectiveCenter();
205
207 QVector3D objectiveCenterLocal() const { return m_objectiveCenterLocal; }
208
217 bool solveOpticalCalibration();
218
227 void setKnownTrackerToObjectiveDistance(float metres) { m_knownTrackerToObjectiveDist = metres; }
228 float knownTrackerToObjectiveDistance() const { return m_knownTrackerToObjectiveDist; }
229
231 bool opticalCalibrationValid() const { return m_opticalCalibValid; }
232
234 QVector3D opticalAxisLocal() const { return m_opticalAxisLocal; }
235
237 QVector3D opticalCenterLocal() const { return m_opticalCenterLocal; }
238
240 float opticalCalibResidualMm() const { return m_opticalCalibResidualMm; }
241
243 float opticalCalibDepthSpreadMm() const { return m_opticalCalibDepthSpreadMm; }
244
258 bool applyOpticalAxisFineAdjust(const QVector3D& targetWorldPos,
259 float& correctionDeg);
260
262 bool opticalFineAdjustApplied() const { return m_opticalFineAdjustApplied; }
263
265 float opticalFineAdjustDeg() const { return m_opticalFineAdjustDeg; }
266
268 void clearOpticalFineAdjust();
269
277 bool opticalRayInWorld(QVector3D& origin, QVector3D& direction) const;
278
288 bool opticalUpInWorld(QVector3D& up) const;
289
290 //=========================================================================================================
291 // Digitizer connection
292 //=========================================================================================================
293
294 void setConnection(PolhemusConnection* conn);
295 PolhemusConnection* connection() const { return m_pConn; }
296
297 //=========================================================================================================
298 // Digitized-point store
299 //=========================================================================================================
300
301 AcquiredPoints* acquiredPoints() const { return m_pPoints; }
302
303 //=========================================================================================================
304 // Explicit capture API
305 //=========================================================================================================
306
307 bool captureCurrentPenPositionAsFiducial(FiducialId id);
308 bool captureCurrentPenPositionAsHeadShape();
309
310 //=========================================================================================================
311 // Model (BEM / FIFF) fiducials — target landmarks for SVD coregistration
312 //=========================================================================================================
313
314 void setModelFiducial(FiducialId id, const QVector3D& posInModel);
315 bool hasModelFiducial(FiducialId id) const;
316 bool hasAllModelFiducials() const;
317 QVector3D modelFiducial(FiducialId id) const;
318
319 bool hasAllPenFiducials() const;
320
321 //=========================================================================================================
322 // Vertex (CZ / top of head) — 4th reference point for orientation validation
323 //=========================================================================================================
324
325 void setModelVertex(const QVector3D& pos) { m_modelVertex = pos; m_hasModelVertex = true; }
326 bool hasModelVertex() const { return m_hasModelVertex; }
327 QVector3D modelVertex() const { return m_modelVertex; }
328
329 bool captureCurrentPenPositionAsVertex();
330 bool hasPenVertex() const { return m_hasPenVertex; }
331 QVector3D penVertex() const { return m_penVertex; }
332
333 //=========================================================================================================
334 // Registration
335 //=========================================================================================================
336
344 bool computeRegistration();
345
350 void resetRegistration();
351
352 //=========================================================================================================
353 // Session persistence — save/restore registration state across app restarts
354 //=========================================================================================================
355
356 void saveSessionState(QSettings &settings, const QString &prefix = QStringLiteral("polhemus")) const;
357 bool restoreSessionState(QSettings &settings, const QString &prefix = QStringLiteral("polhemus"));
358
359 bool registrationValid() const { return m_registrationValid; }
360
361 //=========================================================================================================
362 // Current state accessors
363 //=========================================================================================================
364
365 QMatrix4x4 deviceToWorld() const { return m_deviceToWorld; }
366 QMatrix4x4 headToWorld() const { return m_headToWorld; }
367 QMatrix4x4 headToDevice() const { return m_headToDevice; }
368 QMatrix4x4 worldToModel() const { return m_worldToModel; }
369 QVector3D penPosition() const { return m_penPosition; }
370 QQuaternion penOrientation() const { return m_penOrientation; }
371 bool haveLivePenPosition() const { return m_havePenPos; }
372
373 QVector3D probePosition() const { return m_probePosition; }
374 QQuaternion probeOrientation() const { return m_probeOrientation; }
375 bool haveLiveProbePosition() const { return m_haveProbePos; }
376
377signals:
378 void devicePoseChanged(const QMatrix4x4& deviceToWorld);
379 void penPoseChanged(const QVector3D& position, const QQuaternion& orientation);
380 void probePoseChanged(const QVector3D& position, const QQuaternion& orientation);
381 void penButtonPressed(const QVector3D& position, const QQuaternion& orientation);
384 void pivotCalibrationDone(const QVector3D& offset, float residualMm);
385 void pivotSampleCollected(int sampleCount, float angularSpanDeg);
387
388private slots:
389 void onPointReceived(int station, const QVector3D& position, const QQuaternion& orientation);
390 void onPenButtonPressedFromConn(int station, const QVector3D& position, const QQuaternion& orientation);
391
392private:
393 QMatrix4x4 buildDevicePose(const QVector3D& trackerPos,
394 const QQuaternion& trackerOri) const;
395 QMatrix4x4 buildHeadFrame() const;
396
397 int m_trackerStation = 2;
398 int m_penStation = 1;
399 int m_probeStation = 3;
400
401 QVector3D m_offsetTranslation;
402 QQuaternion m_offsetRotation;
403 QVector3D m_penTipOffset; // tip offset in sensor body frame (metres)
404 bool m_tipOffsetEnabled = false;
405 bool m_mirrorX = false; // negate X of incoming Polhemus positions
406 bool m_mirrorY = false; // negate Y of incoming Polhemus positions
407
408 PolhemusConnection* m_pConn = nullptr;
409 AcquiredPoints* m_pPoints = nullptr;
410
411 QVector3D m_penPosition;
412 QQuaternion m_penOrientation;
413 bool m_havePenPos = false;
414
415 QVector3D m_probePosition;
416 QQuaternion m_probeOrientation;
417 bool m_haveProbePos = false;
418
419 QMatrix4x4 m_deviceToWorld;
420 QMatrix4x4 m_headToWorld;
421 QMatrix4x4 m_headToDevice;
422 QMatrix4x4 m_worldToModel;
423 bool m_registrationValid = false;
424
425 // Pen fiducials (captured from stylus in Polhemus world frame)
426 QVector3D m_penFid[4]; // indexed by FiducialId (1..3)
427 bool m_hasPenFid[4] = {};
428
429 // Model fiducials (from FIFF / BEM, in MRI/surface-RAS frame)
430 QVector3D m_modelFid[4]; // indexed by FiducialId (1..3)
431 bool m_hasModelFid[4] = {};
432
433 // Vertex / CZ (top of head) for orientation validation
434 QVector3D m_modelVertex;
435 bool m_hasModelVertex = false;
436 QVector3D m_penVertex;
437 bool m_hasPenVertex = false;
438
439 // Pivot calibration state
440 PivotState m_pivotState = PivotState::Idle;
441 std::vector<QVector3D> m_pivotPositions;
442 std::vector<QQuaternion> m_pivotOrientations;
443 float m_pivotResidualMm = 0.0f;
444
445 // Optical path calibration state
446 std::vector<OpticalCalibSample> m_opticalCalibSamples;
447 QVector3D m_opticalAxisLocal; // optical axis direction in tracker body frame
448 QVector3D m_opticalCenterLocal; // optical center position in tracker body frame
449 float m_opticalCalibResidualMm = 0.0f;
450 float m_opticalCalibDepthSpreadMm = 0.0f;
451 bool m_opticalCalibValid = false;
452
453 // Directly captured objective center (in tracker-local frame, metres).
454 // When available, the solver uses this instead of the distance constraint.
455 QVector3D m_objectiveCenterLocal; // objective center in tracker body frame
456 bool m_hasObjectiveCenter = false;
457
458 // Fine adjustment — small correction rotation applied on top of the
459 // solved optical axis to compensate for residual misalignment between
460 // the tracked probe position and its appearance in the video.
461 QVector3D m_opticalAxisPreFineAdjust; // axis before fine-adjust (for undo)
462 float m_opticalFineAdjustDeg = 0.0f;
463 bool m_opticalFineAdjustApplied = false;
464
465 // Known tracker-to-objective distance (metres) — fallback constraint when
466 // no direct objective center capture is available.
467 float m_knownTrackerToObjectiveDist = 0.200f; // metres
468
469 bool solvePivotCalibration();
470};
471
472} // namespace UTILSLIB
473
474#endif // UTILS_POLHEMUS_COREGISTRATION_H
Public umbrella header for UTILSLIB exposing the UTILSSHARED_EXPORT macro and build-info accessors.
#define UTILSSHARED_EXPORT
Polhemus digitizer connection abstraction.
Shared digitised-point store for Polhemus digitizer sessions.
Shared utilities (I/O helpers, spectral analysis, layout management, warp algorithms).
FiducialId
Identifier for the three cardinal fiducials.
In-memory store of all points captured during a session.
Polhemus digitizer connection (mock + serial-port backends).
~PolhemusCoregistration() override=default
void penPoseChanged(const QVector3D &position, const QQuaternion &orientation)
PolhemusConnection * connection() const
void penButtonPressed(const QVector3D &position, const QQuaternion &orientation)
void devicePoseChanged(const QMatrix4x4 &deviceToWorld)
void setKnownTrackerToObjectiveDistance(float metres)
Set the known perpendicular distance from tracker sensor to the objective center (metres).
void setModelVertex(const QVector3D &pos)
void pivotCalibrationDone(const QVector3D &offset, float residualMm)
void pivotStateChanged(PolhemusCoregistration::PivotState state)
bool captureOpticalCalibSample()
Record one calibration sample (current tracker pose + current pen position).
void setPenTipOffset(const QVector3D &offset)
PolhemusCoregistration(QObject *parent=nullptr)
void setAxisMirror(bool mirrorX, bool mirrorY)
Negate X and/or Y of incoming Polhemus positions.
void probePoseChanged(const QVector3D &position, const QQuaternion &orientation)
void pivotSampleCollected(int sampleCount, float angularSpanDeg)
QVector3D focusPoint
Stylus focus point in world frame (metres).
QVector3D trackerPos
Tracker position in world frame (metres).
QQuaternion trackerOri
Tracker orientation in world frame.