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> 41 typedef unsigned char uchar;
54 void correlate(
double* x,
double* y,
double* corr,
int maxdelay,
int n)
57 double mx, my, sx, sy, sxy, denom, r;
63 for (i = 0; i < n; i++)
74 for (i = 0; i < n; i++)
76 sx += (x[i] - mx) * (x[i] - mx);
77 sy += (y[i] - my) * (y[i] - my);
79 denom = sqrt(sx * sy);
82 for (delay = -maxdelay; delay < maxdelay; delay++)
85 for (i = 0; i < n; i++)
91 sxy += (x[i] - mx) * (y[j] - my);
94 corr[delay+maxdelay] = r;
104 mAddRawToDebug =
false;
110 mFilename = filename;
113 if (!QFileInfo(filename).exists())
125 report(
"Temporal Calibration: Successfully loaded data from " + filename);
130 mDebugFolder = filename;
133 void TemporalCalibration::saveDebugFile()
135 if (mDebugFolder.isEmpty())
138 QString dbFilename = mDebugFolder +
"/"+ QFileInfo(mFilename).baseName() +
"_temporal_calib.txt";
139 QFile file(dbFilename);
142 if (!file.open(QIODevice::ReadWrite))
144 reportError(
"Failed to write file " + file.fileName() +
".");
148 file.write(
qstring_cast(mDebugStream.str()).toLatin1());
149 report(
"Saved temporal calibration details to " + file.fileName());
170 mDebugStream.str(
"");
171 mDebugStream.clear();
185 mDebugStream <<
"Loaded data: " << mFilename << std::endl;
186 mDebugStream <<
"=======================================" << std::endl;
188 mProcessedFrames = mFileData.
mUsRaw->initializeFrames(std::vector<bool>(1,
false)).front();
190 std::vector<double> frameMovement = this->computeProbeMovement();
192 if (!this->checkFrameMovementQuality(frameMovement))
194 reportError(
"Failed to detect movement in images. Make sure that the first image is clear and visible.");
200 std::vector<double> trackingMovement = this->computeTrackingMovement();
203 double resolution = 5;
205 double offset = mFileData.
mFrames.front().mTime - mFileData.
mPositions.front().mTime;
206 std::vector<double> frameMovementRegular = this->resample(frameMovement, mFileData.
mFrames, resolution);
207 std::vector<double> trackingMovementRegular = this->resample(trackingMovement, mFileData.
mPositions, resolution);
209 double shift = this->findLSShift(frameMovementRegular, trackingMovementRegular, resolution);
211 double totalShift = offset + shift;
213 mDebugStream <<
"=======================================" << std::endl;
214 mDebugStream <<
"Performed temporal calibration:" << std::endl;
215 mDebugStream <<
"offset = " << offset <<
", shift = " << shift << std::endl;
216 mDebugStream <<
"Total temporal shift tf-tt = " << offset+shift <<
" ms" << std::endl;
217 mDebugStream <<
"=======================================" << std::endl;
219 double startTime = mFileData.
mPositions.front().mTime;
220 this->writePositions(
"Tracking", trackingMovement, mFileData.
mPositions, startTime);
221 this->writePositions(
"Frames", frameMovement, mFileData.
mFrames, startTime);
222 this->writePositions(
"Shifted Frames", frameMovement, mFileData.
mFrames, startTime + totalShift);
225 this->saveDebugFile();
230 bool TemporalCalibration::checkFrameMovementQuality(std::vector<double> pos)
233 for (
unsigned i=0; i<pos.size(); ++i)
238 double error = double(count)/pos.size();
240 reportWarning(QString(
"Found %1 % zeros in frame movement").arg(error*100));
247 double TemporalCalibration::findLeastSquares(std::vector<double> frames, std::vector<double> tracking,
int shift)
const 250 size_t r1 = frames.size();
252 r0 = std::max<int>(r0, - shift);
253 r1 = std::min<int>(r1, tracking.size() - shift);
257 for (
int i=r0; i<r1; ++i)
259 double core = pow(frames[i] - tracking[i+shift], 2.0);
272 double TemporalCalibration::findLSShift(std::vector<double> frames, std::vector<double> tracking,
double resolution)
const 274 double maxShift = 1000;
275 size_t N = std::min(tracking.size(), frames.size());
276 N = std::min<int>(N, 2*maxShift/resolution);
277 std::vector<double> result(N, 0);
280 for (
int i=-W; i<W; ++i)
282 double rms = this->findLeastSquares(frames, tracking, i);
286 int top = std::distance(result.begin(), std::min_element(result.begin(), result.end()));
287 double shift = (W-top) * resolution;
289 mDebugStream <<
"=======================================" << std::endl;
290 mDebugStream <<
"tracking vs frames fit using least squares:" << std::endl;
291 mDebugStream <<
"Temporal resolution " << resolution <<
" ms" << std::endl;
292 mDebugStream <<
"Max shift " << maxShift <<
" ms" << std::endl;
293 mDebugStream <<
"#frames=" << frames.size() <<
", #tracks=" << tracking.size() << std::endl;
294 mDebugStream << std::endl;
295 mDebugStream <<
"Frame pos" <<
"\t" <<
"Track pos" <<
"\t" <<
"RMS(center=" << W <<
")" << std::endl;
296 for (
size_t x = 0; x < std::min<int>(tracking.size(), frames.size()); ++x)
298 mDebugStream << frames[x] <<
"\t" << tracking[x];
300 mDebugStream <<
"\t" << result[x];
301 mDebugStream << std::endl;
304 mDebugStream << std::endl;
305 mDebugStream <<
"minimal index: " << top <<
", = shift in ms: " << shift << std::endl;
306 mDebugStream <<
"=======================================" << std::endl;
316 double TemporalCalibration::findCorrelationShift(std::vector<double> frames, std::vector<double> tracking,
double resolution)
const 318 size_t N = std::min(tracking.size(), frames.size());
319 std::vector<double> result(N, 0);
321 correlate(&*frames.begin(), &*tracking.begin(), &*result.begin(), N / 2, N);
323 int top = std::distance(result.begin(), std::max_element(result.begin(), result.end()));
324 double shift = (N/2-top) * resolution;
326 mDebugStream <<
"=======================================" << std::endl;
327 mDebugStream <<
"tracking vs frames correlation:" << std::endl;
328 mDebugStream <<
"Temporal resolution " << resolution <<
" ms" << std::endl;
329 mDebugStream <<
"#frames=" << frames.size() <<
", #tracks=" << tracking.size() << std::endl;
330 mDebugStream << std::endl;
331 mDebugStream <<
"Frame pos" <<
"\t" <<
"Track pos" <<
"\t" <<
"correlation" << std::endl;
332 for (
int x = 0; x < N; ++x)
334 mDebugStream << frames[x] <<
"\t" << tracking[x] <<
"\t" << result[x] << std::endl;
337 mDebugStream << std::endl;
338 mDebugStream <<
"corr top: " << top <<
", = shift in ms: " << shift << std::endl;
339 mDebugStream <<
"=======================================" << std::endl;
345 void TemporalCalibration::writePositions(QString title, std::vector<double> pos, std::vector<TimedPosition> time,
double shift)
347 if (pos.size()!=time.size())
349 std::cout <<
"size mismatch" << std::endl;
353 mDebugStream << title << std::endl;
354 mDebugStream <<
"time\t" <<
"pos\t" << std::endl;
355 for (
unsigned i=0; i<time.size(); ++i)
357 mDebugStream << time[i].mTime - shift <<
"\t" << pos[i] << std::endl;
364 std::vector<double> TemporalCalibration::resample(std::vector<double> shift, std::vector<TimedPosition> time,
double resolution)
366 if (shift.size()!=time.size())
368 reportError(
"Assert failure, shift and time different sizes");
372 for (
unsigned i=0; i<shift.size(); ++i)
374 frames->AddPoint(time[i].mTime, shift[i]);
377 frames->GetRange(r0, r1);
378 double range = r1-r0;
379 int N_r = range/resolution;
381 std::vector<double> framesRegular;
382 for (
int i=0; i<N_r; ++i)
383 framesRegular.push_back(frames->GetValue(r0 + i*resolution));
385 return framesRegular;
388 std::vector<double> TemporalCalibration::computeTrackingMovement()
390 std::vector<double> retval;
397 for (
unsigned i=0; i<mFileData.
mPositions.size(); ++i)
402 double val =
dot(ez_pr, p_pr);
407 retval.push_back(val-zero);
412 mDebugStream <<
"=======================================" << std::endl;
413 mDebugStream <<
"tracking raw data:" << std::endl;
414 mDebugStream << std::endl;
415 mDebugStream <<
"timestamp" <<
"\t" <<
"pos" << std::endl;
416 for (
unsigned x = 0; x < mFileData.
mPositions.size(); ++x)
418 mDebugStream << mFileData.
mPositions[x].mTime <<
"\t" << retval[x] << std::endl;
420 mDebugStream << std::endl;
421 mDebugStream <<
"=======================================" << std::endl;
430 std::vector<double> TemporalCalibration::computeProbeMovement()
432 int N_frames = mFileData.
mUsRaw->getDimensions()[2];
434 std::vector<double> retval;
436 double maxSingleStep = 5;
441 for (
int i=0; i<N_frames; ++i)
443 double val = this->findCorrelation(mFileData.
mUsRaw, 0, i, maxSingleStep, lastVal);
446 retval.push_back(val);
451 mDebugStream <<
"=======================================" << std::endl;
452 mDebugStream <<
"frames raw data:" << std::endl;
453 mDebugStream << std::endl;
454 mDebugStream <<
"timestamp" <<
"\t" <<
"shift" << std::endl;
455 for (
int x = 0; x < N_frames; ++x)
457 mDebugStream << mFileData.
mFrames[x].mTime <<
"\t" << retval[x] << std::endl;
459 mDebugStream << std::endl;
460 mDebugStream <<
"=======================================" << std::endl;
466 double TemporalCalibration::findCorrelation(
USFrameDataPtr data,
int frame_a,
int frame_b,
double maxShift,
double lastVal)
468 int maxShift_pix = maxShift / mFileData.
mUsRaw->getSpacing()[1];
469 int lastVal_pix = lastVal / mFileData.
mUsRaw->getSpacing()[1];
473 int dimY = mFileData.
mUsRaw->getDimensions()[1];
479 std::vector<double> result(N, 0);
480 double* line_a =
static_cast<double*
>(line1->GetScalarPointer());
481 double* line_b =
static_cast<double*
>(line2->GetScalarPointer());
482 double* line_c = &*result.begin();
484 correlate(line_a, line_b, line_c, N/2, dimY);
487 int lastTop = N/2 - lastVal_pix;
488 std::pair<int,int> range(lastTop - maxShift_pix, lastTop+maxShift_pix);
489 range.first = std::max(0, range.first);
490 range.second = std::min(N, range.second);
493 int top = std::distance(result.begin(), std::max_element(result.begin()+range.first, result.begin()+range.second));
495 double hit = (N/2-top) * mFileData.
mUsRaw->getSpacing()[1];
505 int dimX = data->getDimensions()[0];
506 int dimY = data->getDimensions()[1];
514 maskFilter->SetMaskInputData(mMask);
515 maskFilter->SetImageInputData(base);
516 maskFilter->SetMaskedOutputValue(0.0);
517 maskFilter->Update();
518 uchar* source =
static_cast<uchar*
>(maskFilter->GetOutput()->GetScalarPointer());
520 double* dest =
static_cast<double*
>(retval->GetScalarPointer());
522 for (
int y=0; y<dimY; ++y)
524 dest[y] = source[y*dimX + line_index_x];
QString qstring_cast(const T &val)
boost::shared_ptr< class FileManagerService > FileManagerServicePtr
Reader class for the US Acquisition files.
vtkSmartPointer< vtkImageCorrelation > vtkImageCorrelationPtr
void reportError(QString msg)
void selectData(QString filename, FileManagerServicePtr filemanager)
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)
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.