17 #include <vtkImageData.h> 35 mPatientModelService(patientModelService)
50 std::vector<std::vector<vtkImageDataPtr> > frames = mFileData.
mUsRaw->initializeFrames(angio);
52 std::vector<ProcessedUSInputDataPtr> retval;
54 for (
unsigned i=0; i<angio.size(); ++i)
61 QFileInfo(mFileData.
mFilename).completeBaseName() ));
62 CX_ASSERT(Eigen::Array3i(frames[i][0]->GetDimensions()).isApprox(Eigen::Array3i(mFileData.
getMask()->GetDimensions())));
63 retval.push_back(input);
70 bool within(
int x,
int min,
int max)
72 return (x >= min) && (x <= max);
85 void ReconstructPreprocessor::calibrateTimeStamps(
double offset,
double scale)
87 for (
unsigned int i = 0; i < mFileData.
mPositions.size(); i++)
99 void ReconstructPreprocessor::alignTimeSeries()
101 report(
"Generate time calibration based on input time stamps.");
102 double framesSpan = mFileData.
mFrames.back().mTime - mFileData.
mFrames.front().mTime;
104 double scale = framesSpan / positionsSpan;
106 double offset = mFileData.
mFrames.front().mTime - scale * mFileData.
mPositions.front().mTime;
108 this->calibrateTimeStamps(offset, scale);
114 void ReconstructPreprocessor::cropInputData()
119 cropbox = this->reduceCropboxToImageSize(cropbox, sector.
getSize());
120 Eigen::Vector3i shift = cropbox.
corner(0,0,0).cast<
int>();
121 Eigen::Vector3i size = cropbox.range().cast<
int>() + Eigen::Vector3i(1,1,0);
122 mFileData.
mUsRaw->setCropBox(cropbox);
127 for (
unsigned i=0; i<3; ++i)
129 clipRect_p[2*i] -= shift[i];
130 clipRect_p[2*i+1] -= shift[i];
131 origin_p[i] -= shift[i];
136 sector.
setSize(QSize(size[0], size[1]));
142 cropbox[0] = std::max(cropbox[0], 0);
143 cropbox[2] = std::max(cropbox[2], 0);
144 cropbox[4] = std::max(cropbox[4], 0);
146 cropbox[1] = std::min(cropbox[1], size.width() - 1);
147 cropbox[3] = std::min(cropbox[3], size.height() - 1);
161 void ReconstructPreprocessor::applyTimeCalibration()
171 this->calibrateTimeStamps(timeshift, 1.0);
176 this->alignTimeSeries();
184 void add(
double _err) { ++count; err=((err<0)?_err:std::min(_err, err)); }
189 void ReconstructPreprocessor::filterPositions()
191 int filterStrength = mInput.mPosFilterStrength;
193 if (filterStrength > 0)
195 int filterLength(1+2*filterStrength);
196 int nPositions(mFileData.mPositions.size());
197 if (nPositions > filterLength)
200 Eigen::ArrayXXd qPosArray(7,(nPositions+(2*filterStrength)));
202 Eigen::Quaterniond qA;
204 unsigned int sourceIdx(0);
205 for (
unsigned int i = 0; i < (nPositions+(2*filterStrength)); i++)
209 sourceIdx = (i > filterStrength) ? (i-filterStrength) : 0;
210 sourceIdx = (sourceIdx < nPositions) ? sourceIdx : (nPositions-1);
212 localTx = mFileData.mPositions[sourceIdx].mPos;
214 qPosArray.block<3,1>(4,i) = localTx.matrix().block<3, 1>(0,3);
215 qA = Eigen::Quaterniond(localTx.matrix().block<3, 3>(0,0));
216 qPosArray.block<4,1>(0,i) = qA.coeffs();
221 Eigen::ArrayXXd qPosFiltered = Eigen::ArrayXXd::Zero(7,nPositions);
222 for (
unsigned int i = 0; i < (1+2*filterStrength); i++)
224 qPosFiltered = qPosFiltered + qPosArray.block(0,i,7,nPositions);
226 qPosFiltered = qPosFiltered / (1+2*filterStrength);
228 for (
unsigned int i = 0; i < mFileData.mPositions.size(); i++)
231 qA.coeffs() = qPosFiltered.block<4,1>(0,i);
232 localTx.matrix().block<3, 3>(0,0) = qA.toRotationMatrix();
233 localTx.matrix().block<3, 1>(0,3) = qPosFiltered.block<3,1>(4,i);
234 mFileData.mPositions[i].mPos = localTx;
241 void ReconstructPreprocessor::positionThinning()
255 void ReconstructPreprocessor::interpolatePositions()
257 mFileData.mUsRaw->resetRemovedFrames();
258 int startFrames = mFileData.mFrames.size();
260 std::map<int,RemoveDataType> removedData;
262 for (
unsigned i_frame = 0; i_frame < mFileData.mFrames.size();)
264 std::vector<TimedPosition>::iterator posIter;
265 posIter = lower_bound(mFileData.mPositions.begin(), mFileData.mPositions.end(), mFileData.mFrames[i_frame]);
267 unsigned i_pos = distance(mFileData.mPositions.begin(), posIter);
271 if (i_pos >= mFileData.mPositions.size() - 1)
272 i_pos = mFileData.mPositions.size() - 2;
276 double timeToPos1 = timeToPosition(i_frame, i_pos);
277 double timeToPos2 = timeToPosition(i_frame, i_pos+1);
278 if ((timeToPos1 > mMaxTimeDiff) || (timeToPos2 > mMaxTimeDiff))
280 removedData[i_frame].add(std::max(timeToPos1, timeToPos2));
282 mFileData.mFrames.erase(mFileData.mFrames.begin() + i_frame);
283 mFileData.mUsRaw->removeFrame(i_frame);
287 double t_delta_tracking = mFileData.mPositions[i_pos + 1].mTime - mFileData.mPositions[i_pos].mTime;
289 if (!
similar(t_delta_tracking, 0))
290 t = (mFileData.mFrames[i_frame].mTime - mFileData.mPositions[i_pos].mTime) / t_delta_tracking;
298 for (std::map<int,RemoveDataType>::iterator iter=removedData.begin(); iter!=removedData.end(); ++iter)
300 int first = iter->first+removeCount;
301 int last = first + iter->second.count-1;
302 report(QString(
"Removed input frame [%1-%2]. Time diff=%3").arg(first).arg(last).arg(iter->second.err, 0,
'f', 1));
303 removeCount += iter->second.count;
306 double removed = double(startFrames - mFileData.mFrames.size()) /
double(startFrames);
309 double percent = removed * 100;
316 report(
"Removed " + QString::number(percent,
'f', 1) +
"% of the " +
qstring_cast(startFrames) +
" frames.");
322 double ReconstructPreprocessor::timeToPosition(
unsigned i_frame,
unsigned i_pos)
324 return fabs(mFileData.mFrames[i_frame].mTime - mFileData.mPositions[i_pos].mTime);
330 std::vector<Vector3D> ReconstructPreprocessor::generateInputRectangle()
332 std::vector<Vector3D> retval(4);
336 reportError(
"Reconstructer::generateInputRectangle() + requires mask");
339 Eigen::Array3i dims = mFileData.mUsRaw->getDimensions();
340 Vector3D spacing = mFileData.mUsRaw->getSpacing();
342 Eigen::Array3i maskDims(mask->GetDimensions());
344 if (( maskDims[0]<dims[0] )||( maskDims[1]<dims[1] ))
345 reportError(QString(
"input data (%1) and mask (%2) dim mimatch")
349 int xmin = maskDims[0];
351 int ymin = maskDims[1];
354 unsigned char* ptr =
static_cast<unsigned char*
> (mask->GetScalarPointer());
355 for (
int x = 0; x < maskDims[0]; x++)
356 for (
int y = 0; y < maskDims[1]; y++)
358 unsigned char val = ptr[x + y * maskDims[0]];
361 xmin = std::min(xmin, x);
362 ymin = std::min(ymin, y);
363 xmax = std::max(xmax, x);
364 ymax = std::max(ymax, y);
370 double red = mInput.mMaskReduce;
371 int reduceX = (xmax - xmin) * (red / 100);
372 int reduceY = (ymax - ymin) * (red / 100);
379 retval[0] =
Vector3D(xmin * spacing[0], ymin * spacing[1], 0);
380 retval[1] =
Vector3D(xmax * spacing[0], ymin * spacing[1], 0);
381 retval[2] =
Vector3D(xmin * spacing[0], ymax * spacing[1], 0);
382 retval[3] =
Vector3D(xmax * spacing[0], ymax * spacing[1], 0);
393 Transform3D ReconstructPreprocessor::applyOutputOrientation()
397 if (mInput.mOrientation ==
"PatientReference")
401 else if (mInput.mOrientation ==
"MiddleFrame")
403 prMdd = mFileData.mFrames[mFileData.mFrames.size() / 2].mPos;
407 reportError(
"no orientation algorithm selected in reconstruction");
412 for (
unsigned i = 0; i < mFileData.mFrames.size(); i++)
415 mFileData.mFrames[i].mPos = ddMpr * mFileData.mFrames[i].mPos;
431 void ReconstructPreprocessor::findExtentAndOutputTransform()
433 if (mFileData.mFrames.empty())
436 Transform3D prMdd = this->applyOutputOrientation();
440 std::vector<Vector3D> inputRect = this->generateInputRectangle();
441 std::vector<Vector3D> outputRect;
442 for (
unsigned slice = 0; slice < mFileData.mFrames.size(); slice++)
445 for (
unsigned i = 0; i < inputRect.size(); i++)
447 outputRect.push_back(dMu.coord(inputRect[i]));
456 for (
unsigned i = 0; i < mFileData.mFrames.size(); i++)
458 mFileData.mFrames[i].mPos = T_origo.inv() * mFileData.mFrames[i].mPos;
462 double inputSpacing = std::min(mFileData.mUsRaw->getSpacing()[0], mFileData.mUsRaw->getSpacing()[1]);
463 mOutputVolumeParams =
OutputVolumeParams(extent, inputSpacing, mInput.mMaxOutputVolumeSize);
465 mOutputVolumeParams.set_rMd((mPatientModelService->get_rMpr()) * prMd);
471 void ReconstructPreprocessor::updateFromOriginalFileData()
474 this->cropInputData();
482 this->applyTimeCalibration();
487 this->filterPositions();
492 this->interpolatePositions();
495 if (mFileData.mFrames.empty())
498 this->findExtentAndOutputTransform();
505 mFileData = fileData;
506 this->updateFromOriginalFileData();
QString qstring_cast(const T &val)
void reportError(QString msg)
Vector3D corner(int x, int y, int z) const
#define CX_ASSERT(statement)
Transform3D Transform3D
Transform3D is a representation of an affine 3D transform.
Helper struct for sending and controlling output volume properties.
static DoubleBoundingBox3D fromCloud(std::vector< Vector3D > cloud)
Vector3D getOrigin_p() const
void setOrigin_p(Vector3D origin_p)
ReconstructPreprocessor(PatientModelServicePtr patientModelService)
void reportWarning(QString msg)
void setClipRect_p(DoubleBoundingBox3D clipRect_p)
boost::shared_ptr< class PatientModelService > PatientModelServicePtr
virtual ~ReconstructPreprocessor()
Transform3D createTransformTranslate(const Vector3D &translation)
std::vector< ProcessedUSInputDataPtr > createProcessedInput(std::vector< bool > angio)
Representation of an integer bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Representation of a floating-point bounding box in 3D. The data are stored as {xmin,xmax,ymin,ymax,zmin,zmax}, in order to simplify communication with vtk.
Eigen::Vector3d Vector3D
Vector3D is a representation of a point or vector in 3D.
Definition of characteristics for an Ultrasound Probe Sector.
Eigen::Vector3i corner(int x, int y, int z) const
bool similar(const CameraInfo &lhs, const CameraInfo &rhs, double tol)
DoubleBoundingBox3D getClipRect_p() const
boost::shared_ptr< class ProcessedUSInputData > ProcessedUSInputDataPtr
void initialize(ReconstructCore::InputParams input, USReconstructInputData fileData)
Algorithm part of reconstruction - no dependencies on parameter classes.
void setData(ProbeDefinition data)
Namespace for all CustusX production code.