16 #include <QVBoxLayout> 17 #include "boost/bind.hpp" 19 #include <vtkPiecewiseFunction.h> 20 #include <vtkPointData.h> 21 #include <vtkImageData.h> 26 #include "vtkImageCorrelation.h" 32 #include <vtkImageMask.h> 40 typedef unsigned char uchar;
53 void correlate(
double* x,
double* y,
double* corr,
int maxdelay,
int n)
56 double mx, my, sx, sy, sxy, denom, r;
62 for (i = 0; i < n; i++)
73 for (i = 0; i < n; i++)
75 sx += (x[i] - mx) * (x[i] - mx);
76 sy += (y[i] - my) * (y[i] - my);
78 denom = sqrt(sx * sy);
81 for (delay = -maxdelay; delay < maxdelay; delay++)
84 for (i = 0; i < n; i++)
90 sxy += (x[i] - mx) * (y[j] - my);
93 corr[delay+maxdelay] = r;
103 mAddRawToDebug =
false;
109 mFilename = filename;
112 if (!QFileInfo(filename).exists())
124 report(
"Temporal Calibration: Successfully loaded data from " + filename);
129 mDebugFolder = filename;
132 void TemporalCalibration::saveDebugFile()
134 if (mDebugFolder.isEmpty())
137 QString dbFilename = mDebugFolder +
"/"+ QFileInfo(mFilename).baseName() +
"_temporal_calib.txt";
138 QFile file(dbFilename);
141 if (!file.open(QIODevice::ReadWrite))
143 reportError(
"Failed to write file " + file.fileName() +
".");
147 file.write(
qstring_cast(mDebugStream.str()).toLatin1());
148 report(
"Saved temporal calibration details to " + file.fileName());
169 mDebugStream.str(
"");
170 mDebugStream.clear();
184 mDebugStream <<
"Loaded data: " << mFilename << std::endl;
185 mDebugStream <<
"=======================================" << std::endl;
187 mProcessedFrames = mFileData.
mUsRaw->initializeFrames(std::vector<bool>(1,
false)).front();
189 std::vector<double> frameMovement = this->computeProbeMovement();
191 if (!this->checkFrameMovementQuality(frameMovement))
193 reportError(
"Failed to detect movement in images. Make sure that the first image is clear and visible.");
199 std::vector<double> trackingMovement = this->computeTrackingMovement();
202 double resolution = 5;
204 double offset = mFileData.
mFrames.front().mTime - mFileData.
mPositions.front().mTime;
205 std::vector<double> frameMovementRegular = this->resample(frameMovement, mFileData.
mFrames, resolution);
206 std::vector<double> trackingMovementRegular = this->resample(trackingMovement, mFileData.
mPositions, resolution);
208 double shift = this->findLSShift(frameMovementRegular, trackingMovementRegular, resolution);
210 double totalShift = offset + shift;
212 mDebugStream <<
"=======================================" << std::endl;
213 mDebugStream <<
"Performed temporal calibration:" << std::endl;
214 mDebugStream <<
"offset = " << offset <<
", shift = " << shift << std::endl;
215 mDebugStream <<
"Total temporal shift tf-tt = " << offset+shift <<
" ms" << std::endl;
216 mDebugStream <<
"=======================================" << std::endl;
218 double startTime = mFileData.
mPositions.front().mTime;
219 this->writePositions(
"Tracking", trackingMovement, mFileData.
mPositions, startTime);
220 this->writePositions(
"Frames", frameMovement, mFileData.
mFrames, startTime);
221 this->writePositions(
"Shifted Frames", frameMovement, mFileData.
mFrames, startTime + totalShift);
224 this->saveDebugFile();
229 bool TemporalCalibration::checkFrameMovementQuality(std::vector<double> pos)
232 for (
unsigned i=0; i<pos.size(); ++i)
237 double error = double(count)/pos.size();
239 reportWarning(QString(
"Found %1 % zeros in frame movement").arg(error*100));
246 double TemporalCalibration::findLeastSquares(std::vector<double> frames, std::vector<double> tracking,
int shift)
const 249 size_t r1 = frames.size();
251 r0 = std::max<int>(r0, - shift);
252 r1 = std::min<int>(r1, tracking.size() - shift);
256 for (
int i=r0; i<r1; ++i)
258 double core = pow(frames[i] - tracking[i+shift], 2.0);
271 double TemporalCalibration::findLSShift(std::vector<double> frames, std::vector<double> tracking,
double resolution)
const 273 double maxShift = 1000;
274 size_t N = std::min(tracking.size(), frames.size());
275 N = std::min<int>(N, 2*maxShift/resolution);
276 std::vector<double> result(N, 0);
279 for (
int i=-W; i<W; ++i)
281 double rms = this->findLeastSquares(frames, tracking, i);
285 int top = std::distance(result.begin(), std::min_element(result.begin(), result.end()));
286 double shift = (W-top) * resolution;
288 mDebugStream <<
"=======================================" << std::endl;
289 mDebugStream <<
"tracking vs frames fit using least squares:" << std::endl;
290 mDebugStream <<
"Temporal resolution " << resolution <<
" ms" << std::endl;
291 mDebugStream <<
"Max shift " << maxShift <<
" ms" << std::endl;
292 mDebugStream <<
"#frames=" << frames.size() <<
", #tracks=" << tracking.size() << std::endl;
293 mDebugStream << std::endl;
294 mDebugStream <<
"Frame pos" <<
"\t" <<
"Track pos" <<
"\t" <<
"RMS(center=" << W <<
")" << std::endl;
295 for (
size_t x = 0; x < std::min<int>(tracking.size(), frames.size()); ++x)
297 mDebugStream << frames[x] <<
"\t" << tracking[x];
299 mDebugStream <<
"\t" << result[x];
300 mDebugStream << std::endl;
303 mDebugStream << std::endl;
304 mDebugStream <<
"minimal index: " << top <<
", = shift in ms: " << shift << std::endl;
305 mDebugStream <<
"=======================================" << std::endl;
315 double TemporalCalibration::findCorrelationShift(std::vector<double> frames, std::vector<double> tracking,
double resolution)
const 317 size_t N = std::min(tracking.size(), frames.size());
318 std::vector<double> result(N, 0);
320 correlate(&*frames.begin(), &*tracking.begin(), &*result.begin(), N / 2, N);
322 int top = std::distance(result.begin(), std::max_element(result.begin(), result.end()));
323 double shift = (N/2-top) * resolution;
325 mDebugStream <<
"=======================================" << std::endl;
326 mDebugStream <<
"tracking vs frames correlation:" << std::endl;
327 mDebugStream <<
"Temporal resolution " << resolution <<
" ms" << std::endl;
328 mDebugStream <<
"#frames=" << frames.size() <<
", #tracks=" << tracking.size() << std::endl;
329 mDebugStream << std::endl;
330 mDebugStream <<
"Frame pos" <<
"\t" <<
"Track pos" <<
"\t" <<
"correlation" << std::endl;
331 for (
int x = 0; x < N; ++x)
333 mDebugStream << frames[x] <<
"\t" << tracking[x] <<
"\t" << result[x] << std::endl;
336 mDebugStream << std::endl;
337 mDebugStream <<
"corr top: " << top <<
", = shift in ms: " << shift << std::endl;
338 mDebugStream <<
"=======================================" << std::endl;
344 void TemporalCalibration::writePositions(QString title, std::vector<double> pos, std::vector<TimedPosition> time,
double shift)
346 if (pos.size()!=time.size())
348 std::cout <<
"size mismatch" << std::endl;
352 mDebugStream << title << std::endl;
353 mDebugStream <<
"time\t" <<
"pos\t" << std::endl;
354 for (
unsigned i=0; i<time.size(); ++i)
356 mDebugStream << time[i].mTime - shift <<
"\t" << pos[i] << std::endl;
363 std::vector<double> TemporalCalibration::resample(std::vector<double> shift, std::vector<TimedPosition> time,
double resolution)
365 if (shift.size()!=time.size())
367 reportError(
"Assert failure, shift and time different sizes");
371 for (
unsigned i=0; i<shift.size(); ++i)
373 frames->AddPoint(time[i].mTime, shift[i]);
376 frames->GetRange(r0, r1);
377 double range = r1-r0;
378 int N_r = range/resolution;
380 std::vector<double> framesRegular;
381 for (
int i=0; i<N_r; ++i)
382 framesRegular.push_back(frames->GetValue(r0 + i*resolution));
384 return framesRegular;
387 std::vector<double> TemporalCalibration::computeTrackingMovement()
389 std::vector<double> retval;
396 for (
unsigned i=0; i<mFileData.
mPositions.size(); ++i)
401 double val =
dot(ez_pr, p_pr);
406 retval.push_back(val-zero);
411 mDebugStream <<
"=======================================" << std::endl;
412 mDebugStream <<
"tracking raw data:" << std::endl;
413 mDebugStream << std::endl;
414 mDebugStream <<
"timestamp" <<
"\t" <<
"pos" << std::endl;
415 for (
unsigned x = 0; x < mFileData.
mPositions.size(); ++x)
417 mDebugStream << mFileData.
mPositions[x].mTime <<
"\t" << retval[x] << std::endl;
419 mDebugStream << std::endl;
420 mDebugStream <<
"=======================================" << std::endl;
429 std::vector<double> TemporalCalibration::computeProbeMovement()
431 int N_frames = mFileData.
mUsRaw->getDimensions()[2];
433 std::vector<double> retval;
435 double maxSingleStep = 5;
440 for (
int i=0; i<N_frames; ++i)
442 double val = this->findCorrelation(mFileData.
mUsRaw, 0, i, maxSingleStep, lastVal);
445 retval.push_back(val);
450 mDebugStream <<
"=======================================" << std::endl;
451 mDebugStream <<
"frames raw data:" << std::endl;
452 mDebugStream << std::endl;
453 mDebugStream <<
"timestamp" <<
"\t" <<
"shift" << std::endl;
454 for (
int x = 0; x < N_frames; ++x)
456 mDebugStream << mFileData.
mFrames[x].mTime <<
"\t" << retval[x] << std::endl;
458 mDebugStream << std::endl;
459 mDebugStream <<
"=======================================" << std::endl;
465 double TemporalCalibration::findCorrelation(
USFrameDataPtr data,
int frame_a,
int frame_b,
double maxShift,
double lastVal)
467 int maxShift_pix = maxShift / mFileData.
mUsRaw->getSpacing()[1];
468 int lastVal_pix = lastVal / mFileData.
mUsRaw->getSpacing()[1];
472 int dimY = mFileData.
mUsRaw->getDimensions()[1];
478 std::vector<double> result(N, 0);
479 double* line_a =
static_cast<double*
>(line1->GetScalarPointer());
480 double* line_b =
static_cast<double*
>(line2->GetScalarPointer());
481 double* line_c = &*result.begin();
483 correlate(line_a, line_b, line_c, N/2, dimY);
486 int lastTop = N/2 - lastVal_pix;
487 std::pair<int,int> range(lastTop - maxShift_pix, lastTop+maxShift_pix);
488 range.first = std::max(0, range.first);
489 range.second = std::min(N, range.second);
492 int top = std::distance(result.begin(), std::max_element(result.begin()+range.first, result.begin()+range.second));
494 double hit = (N/2-top) * mFileData.
mUsRaw->getSpacing()[1];
504 int dimX = data->getDimensions()[0];
505 int dimY = data->getDimensions()[1];
513 maskFilter->SetMaskInputData(mMask);
514 maskFilter->SetImageInputData(base);
515 maskFilter->SetMaskedOutputValue(0.0);
516 maskFilter->Update();
517 uchar* source =
static_cast<uchar*
>(maskFilter->GetOutput()->GetScalarPointer());
519 double* dest =
static_cast<double*
>(retval->GetScalarPointer());
521 for (
int y=0; y<dimY; ++y)
523 dest[y] = source[y*dimX + line_index_x];
QString qstring_cast(const T &val)
Reader class for the US Acquisition files.
vtkSmartPointer< vtkImageCorrelation > vtkImageCorrelationPtr
void reportError(QString msg)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
double calibrate(bool *success)
boost::shared_ptr< class USFrameData > USFrameDataPtr
QString timestampSecondsFormatNice()
vtkSmartPointer< class vtkPiecewiseFunction > vtkPiecewiseFunctionPtr
Vector3D getOrigin_p() const
void correlate(double *x, double *y, double *corr, int maxdelay, int n)
USReconstructInputData readAllFiles(QString fileName, QString calFilesPath="")
void reportWarning(QString msg)
void selectData(QString filename)
double dot(const Vector3D &a, const Vector3D &b)
compute inner product (or dot product) of a and b.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
void setDebugFolder(QString path)
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
vtkSmartPointer< vtkImageMask > vtkImageMaskPtr
vtkImageDataPtr generateVtkImageDataDouble(Eigen::Array3i dim, Vector3D spacing, double initValue)
Namespace for all CustusX production code.