14 #include <QPushButton> 16 #include <vtkPolyData.h> 17 #include <vtkPoints.h> 51 mLastRegistration = Transform3D::Identity();
55 XmlOptionFile options =
profile()->getXmlSettings().descend(
"registration").descend(
"WirePhantomWidget");
60 mPipeline->initialize(filters);
62 mPipeline->getNodes()[0]->setValueName(
"US Image:");
63 mPipeline->getNodes()[0]->setHelp(
"Select an US volume acquired from the wire phantom.");
64 mPipeline->setOption(
"Color", QVariant(QColor(
"red")));
66 mLayout =
new QVBoxLayout(
this);
70 QPushButton* showCrossButton =
new QPushButton(
"Show Cross");
71 showCrossButton->setToolTip(
"Load the centerline description of the wire cross");
72 connect(showCrossButton, SIGNAL(clicked()),
this, SLOT(loadNominalCross()));
74 mMeasureButton =
new QPushButton(
"Execute");
75 mMeasureButton->setToolTip(
"Measure deviation of input volume from nominal wire cross.");
76 connect(mMeasureButton, SIGNAL(clicked()),
this, SLOT(measureSlot()));
78 mCalibrationButton =
new QPushButton(
"Probe calib");
79 mCalibrationButton->setToolTip(
"" 80 "Estimate probe calibration sMt\n" 81 "based on last accuracy test and\n" 82 "the current probe orientation");
83 connect(mCalibrationButton, SIGNAL(clicked()),
this, SLOT(generate_sMt()));
85 QLayout* buttonsLayout =
new QHBoxLayout;
86 buttonsLayout->addWidget(mMeasureButton);
87 buttonsLayout->addWidget(mCalibrationButton);
89 mResults =
new QTextEdit;
91 mLayout->addWidget(showCrossButton);
92 mLayout->addWidget(mPipelineWidget);
93 mLayout->addLayout(buttonsLayout);
94 mLayout->addWidget(mResults, 1);
105 "<h3>Measure US accuracy using the wire phantom.</h3>" 107 "Select a US recording of the wire phantom, then press" 108 "the register button to find deviation from the nominal" 109 "data. The output is given as a distance from measured" 115 MeshPtr WirePhantomWidget::loadNominalCross()
120 std::map<QString, MeshPtr> meshes =
mServices->patient()->getDataOfType<
Mesh>();
121 for (std::map<QString, MeshPtr>::iterator iter=meshes.begin(); iter!=meshes.end(); ++iter)
122 if (iter->first.contains(
"wire_phantom_cross_pts"))
123 retval = iter->second;
128 retval = boost::dynamic_pointer_cast<
Mesh>(
mServices->patient()->importData(nominalCrossFilename, infoText));
133 reportError(QString(
"failed to load %s.").arg(nominalCrossFilename));
136 retval->setColor(QColor(
"green"));
137 this->showData(retval);
142 void WirePhantomWidget::showData(
DataPtr data)
144 mServices->view()->getGroup(0)->addData(data->getUid());
147 void WirePhantomWidget::measureSlot()
149 if (mPipeline->getPipelineTimedAlgorithm()->isRunning())
151 connect(mPipeline->getPipelineTimedAlgorithm().get(), SIGNAL(finished()),
this, SLOT(registration()));
152 mPipeline->execute();
161 int N = points->GetNumberOfPoints();
166 for (
int i = 0; i < N; ++i)
167 acc +=
Vector3D(points->GetPoint(i));
171 void WirePhantomWidget::registration()
174 disconnect(mPipeline->getPipelineTimedAlgorithm().get(), SIGNAL(finished()),
this, SLOT(registration()));
188 DataPtr measuredCross = mPipeline->getNodes().back()->getData();
189 MeshPtr nominalCross = this->loadNominalCross();
190 if (!nominalCross || !measuredCross)
192 reportError(
"Missing fixed/moving data. WirePhantom measurement failed.");
196 mServices->registration()->setFixedData(nominalCross);
197 mServices->registration()->setMovingData(measuredCross);
199 this->showData(nominalCross);
200 this->showData(measuredCross);
206 QString logPath =
mServices->patient()->getActivePatientFolder() +
"/Logs/";
210 success = success && vesselReg.
execute();
218 std::cout <<
"v2v linear result:\n" << linearTransform << std::endl;
221 mLastRegistration = linearTransform;
228 mServices->registration()->setLastRegistrationTime(QDateTime::currentDateTime());
229 mServices->registration()->addImage2ImageRegistration(delta,
"Wire Phantom Measurement");
232 Vector3D t_delta = linearTransform.matrix().block<3, 1>(0, 3);
233 Eigen::AngleAxisd angleAxis = Eigen::AngleAxisd(linearTransform.matrix().block<3, 3>(0, 0));
234 double angle = angleAxis.angle();
239 Vector3D cross_r = this->findCentroid(nominalCross);
246 std::pair<QString, Transform3D> probePos = this->getLastProbePosition();
249 Vector3D cross_moving_r = linearTransform.coord(cross_r);
250 Vector3D diff_r = (cross_r - cross_moving_r);
251 Vector3D diff_tus = rMt_us.inv().vector(diff_r);
255 QString operator()(
double val)
257 return QString(
"%1").arg(val, 2,
'f', 2);
263 result += QString(
"Results for: %1\n").arg(
mServices->registration()->getMovingData()->getName());
264 result += QString(
"Shift vector (r): \t%1\t%2\t%3\n").arg(fmt(diff_r[0])).arg(fmt(diff_r[1])).arg(fmt(diff_r[2]));
265 if (!probePos.first.isEmpty())
266 result += QString(
"Shift vector (probe): \t%1\t%2\t%3\t(used tracking data from %4)\n").arg(fmt(diff_tus[0])).arg(fmt(diff_tus[1])).arg(fmt(diff_tus[2])).arg(probePos.first);
267 result += QString(
"Accuracy |v|: \t%1\tmm\n").arg(fmt(diff_r.length()));
268 result += QString(
"Angle: \t%1\t*\n").arg(fmt(angle /
M_PI * 180.0));
270 mResults->append(result);
271 report(
"Wire Phantom Test Results:\n"+result);
273 this->showDataMetrics(cross_r);
281 void WirePhantomWidget::showDataMetrics(
Vector3D cross_r)
287 Vector3D cross_us = usMnom.coord(cross_r);
294 p1->setSpace(
mServices->spaceProvider()->getD(
mServices->registration()->getFixedData()));
295 p1->setCoordinate(cross_r);
305 p2->setSpace(
mServices->spaceProvider()->getD(
mServices->registration()->getMovingData()));
306 p2->setCoordinate(cross_us);
316 d0->getArguments()->set(0, p1);
317 d0->getArguments()->set(1, p2);
329 std::pair<QString, Transform3D> WirePhantomWidget::getLastProbePosition()
335 return std::make_pair(
"", Transform3D::Identity());
337 return std::make_pair(usData.
mFilename, prMt_us);
340 void WirePhantomWidget::generate_sMt()
342 bool translateOnly =
true;
345 std::pair<QString, Transform3D> probePos = this->getLastProbePosition();
347 if (probePos.first.isEmpty())
349 reportWarning(
"Cannot find probe position from last recording, aborting calibration test.");
357 reportWarning(
"Cannot find probe, aborting calibration test.");
367 if (!
mServices->registration()->getMovingData())
369 reportWarning(
"Cannot find moving data, aborting calibration test.");
376 Vector3D cross_r = this->findCentroid(this->loadNominalCross());
386 Vector3D cross_moving_r = mLastRegistration.coord(cross_r);
387 Vector3D diff_r = (cross_r - cross_moving_r);
388 Vector3D diff_tus = rMt_us.inv().vector(diff_r);
396 "Calculated new calibration matrix\n" 397 "adding only translation " 398 "from last accuracy test\n" 417 sQt = usMs.inv() * nomMus * usMs * sMt;
420 "Calculated new calibration matrix\n" 421 "from last accuracy test\n" 423 "Old calibration matrix sMt:\n" 425 "New calibration matrix sQt:\n"
QString qstring_cast(const T &val)
cxResource_EXPORT ProfilePtr profile()
void reportError(QString msg)
boost::shared_ptr< class DistanceMetric > DistanceMetricPtr
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
vtkSmartPointer< vtkPoints > vtkPointsPtr
boost::shared_ptr< class Data > DataPtr
virtual RegistrationHistoryPtr get_rMd_History()
boost::shared_ptr< class Filter > FilterPtr
void reportWarning(QString msg)
boost::shared_ptr< FilterGroup > FilterGroupPtr
Transform3D createTransformTranslate(const Vector3D &translation)
Data class that represents a distance between two points, or a point and a plane. ...
Data class that represents a single point.
vtkSmartPointer< vtkPolyData > vtkPolyDataPtr
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
static QString getRootConfigPath()
return path to root config folder. May be replaced with getExistingConfigPath()
void checkQuality(Transform3D linearTransform)
boost::shared_ptr< class Mesh > MeshPtr
Transform3D getLinearResult(ContextPtr context=ContextPtr())
Helper class for xml files used to store ssc/cx data.
bool initialize(DataPtr source, DataPtr target, QString logPath)
Namespace for all CustusX production code.
boost::shared_ptr< class Tool > ToolPtr
boost::shared_ptr< class PointMetric > PointMetricPtr