/*========================================================================= medInria Copyright (c) INRIA 2013. All rights reserved. See LICENSE.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. =========================================================================*/ #include "LCCLogDemons.h" #include // ///////////////////////////////////////////////////////////////// // // ///////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include // ///////////////////////////////////////////////////////////////// // LCCLogDemonsPrivate // ///////////////////////////////////////////////////////////////// typedef itk::Image< float, 3 > RegImageType; typedef double TransformScalarType; class LCCLogDemonsPrivate { public: LCCLogDemons * proc; template int update(); rpi::LCClogDemons< RegImageType, RegImageType, double > * registrationMethod; rpi::LCClogDemons< RegImageType, RegImageType, double >::UpdateRule updateRule; rpi::LCClogDemons< RegImageType, RegImageType, double >::GradientType gradientType; std::vector iterations; double maxStepLength, similarityCriteriaSigma, sigmaI, updateFieldSigma, velocityFieldSigma; unsigned int BCHExpansion; bool verbose, boundaryCheck, useHistogramMatching, useMask; int interpolatorType; }; // ///////////////////////////////////////////////////////////////// // LCCLogDemons // ///////////////////////////////////////////////////////////////// LCCLogDemons::LCCLogDemons() : itkProcessRegistration(), d(new LCCLogDemonsPrivate) { d->proc = this; d->registrationMethod = nullptr; // Gives the exported file type for medRegistrationSelectorToolBox this->setProperty("outputFileType", "notText"); } LCCLogDemons::~LCCLogDemons() { d->proc = nullptr; if (d->registrationMethod) { delete d->registrationMethod; } d->registrationMethod = nullptr; delete d; d = nullptr; } bool LCCLogDemons::registered() { return dtkAbstractProcessFactory::instance()->registerProcessType("LCCLogDemons", createLCCLogDemons); } void LCCLogDemons::setUpdateRule(int rule) { d->updateRule = (rpi::LCClogDemons< RegImageType, RegImageType, double >::UpdateRule)rule; } void LCCLogDemons::setVerbosity(bool verbose) { d->verbose = verbose; } void LCCLogDemons::setNumberOfIterations(std::vector iterations) { d->iterations = iterations; } void LCCLogDemons::setMaximumUpdateStepLength(double value) { d->maxStepLength = value; } void LCCLogDemons::setGradientType(int value) { d->gradientType = (rpi::LCClogDemons< RegImageType, RegImageType, double >::GradientType)value; } void LCCLogDemons::setUseHistogramMatching(bool flag) { d->useHistogramMatching = flag; } void LCCLogDemons::setBoundaryCheck(bool flag) { d->boundaryCheck = flag; } void LCCLogDemons::setSigmaI(double sigmaI) { d->sigmaI = sigmaI; } void LCCLogDemons::setSimilarityCriteriaSigma(double std) { d->similarityCriteriaSigma = std; } void LCCLogDemons::setUpdateFieldSigma(double sigma) { d->updateFieldSigma = sigma; } void LCCLogDemons::setVelocityFieldSigma(double sigma) { d->velocityFieldSigma = sigma; } void LCCLogDemons::setNumberOfTermsBCHExpansion(unsigned int number) { d->BCHExpansion = number; } void LCCLogDemons::useMask (bool flag) { d->useMask = flag; } void LCCLogDemons::setInterpolatorType(int value) { d->interpolatorType = value;; } QString LCCLogDemons::description() const { return "LCCLogDemons"; } // ///////////////////////////////////////////////////////////////// // Templated Version of update // ///////////////////////////////////////////////////////////////// template int LCCLogDemonsPrivate::update() { RegImageType *inputFixed = static_cast(proc->fixedImage().GetPointer()); RegImageType *inputMoving = static_cast(proc->movingImages()[0].GetPointer()); // Cast spacing, origin, direction and size from the moving data to the fixed one typedef itk::ResampleImageFilter ResampleFilterType; typename ResampleFilterType::Pointer resampleFilter = ResampleFilterType::New(); resampleFilter->SetOutputSpacing(inputFixed->GetSpacing()); resampleFilter->SetOutputOrigin(inputFixed->GetOrigin()); resampleFilter->SetOutputDirection(inputFixed->GetDirection()); resampleFilter->SetSize(inputFixed->GetLargestPossibleRegion().GetSize()); resampleFilter->SetInput(inputMoving); resampleFilter->Update(); inputMoving = resampleFilter->GetOutput(); // Register the fixed and moving data registrationMethod = new rpi::LCClogDemons (); registrationMethod->SetFixedImage(inputFixed); registrationMethod->SetMovingImage(inputMoving); registrationMethod->SetUpdateRule(updateRule); registrationMethod->SetVerbosity(verbose); //Log registrationMethod->SetMaximumUpdateStepLength(maxStepLength); registrationMethod->SetGradientType(gradientType); registrationMethod->SetUseHistogramMatching(useHistogramMatching); registrationMethod->SetNumberOfIterations(iterations); registrationMethod->SetBoundaryCheck(boundaryCheck); registrationMethod->SetSimilarityCriteriaStandardDeviation(similarityCriteriaSigma); registrationMethod->SetSigmaI(sigmaI); registrationMethod->SetUpdateFieldStandardDeviation(updateFieldSigma); registrationMethod->SetStationaryVelocityFieldStandardDeviation(velocityFieldSigma); registrationMethod->SetNumberOfTermsBCHExpansion(BCHExpansion); registrationMethod->UseMask(useMask); // Print method parameters QString methodParameters = proc->getTitleAndParameters(); qDebug() << "METHOD PARAMETERS"; qDebug() << methodParameters; // Run the registration time_t t1 = clock(); try { registrationMethod->StartRegistration(); } catch( std::exception & err ) { qDebug() << "ExceptionObject caught (startRegistration): " << err.what(); return medAbstractProcessLegacy::FAILURE; } time_t t2 = clock(); qDebug() << "Elasped time: " << static_cast(t2-t1)/static_cast(CLOCKS_PER_SEC); typedef itk::ResampleImageFilter< RegImageType,RegImageType, double> ResampleFilterType; typename ResampleFilterType::Pointer resampler = ResampleFilterType::New(); resampler->SetTransform(registrationMethod->GetDisplacementFieldTransformation()); resampler->SetInput(inputMoving); resampler->SetDefaultPixelValue(0); // Set the image interpolator switch(interpolatorType) { case rpi::INTERPOLATOR_NEAREST_NEIGHBOR: { resampler->SetInterpolator(itk::NearestNeighborInterpolateImageFunction::New()); break; } case rpi::INTERPOLATOR_LINEAR: { // Nothing to do ; linear interpolator by default break; } case rpi::INTERPOLATOR_BSLPINE: { resampler->SetInterpolator(itk::BSplineInterpolateImageFunction::New()); break; } case rpi::INTERPOLATOR_SINUS_CARDINAL: { resampler->SetInterpolator(itk::WindowedSincInterpolateImageFunction< RegImageType, RegImageType::ImageDimension, itk::Function::HammingWindowFunction, itk::ConstantBoundaryCondition, double >::New()); break; } } try { resampler->Update(); } catch (itk::ExceptionObject & err) { qDebug() << "ExceptionObject caught (resampler): " << err.GetDescription(); return medAbstractProcessLegacy::FAILURE; } if (proc->output()) { proc->output()->setData(resampler->GetOutput()); } return medAbstractProcessLegacy::SUCCESS; } int LCCLogDemons::update(itkProcessRegistration::ImageType imgType) { // Cast has been done in itkProcessRegistration if (imgType == itkProcessRegistration::FLOAT) { return d->update(); } return medAbstractProcessLegacy::FAILURE; } bool LCCLogDemons::writeTransform(const QString& file) { try { if (auto transform = getTransform()) { rpi::writeDisplacementFieldTransformation(transform, file.toStdString()); } } catch (std::exception& err) { qDebug() << "ExceptionObject caught (writeTransform): " << err.what(); return false; } return true; } itk::Transform::Pointer LCCLogDemons::getTransform() { if (d->registrationMethod) { return d->registrationMethod->GetDisplacementFieldTransformation().GetPointer(); } return nullptr; } QString LCCLogDemons::getTitleAndParameters() { auto registration = d->registrationMethod; QString titleAndParameters; titleAndParameters += "LCC Log Demons\n"; titleAndParameters += " Max number of iterations: " + QString::fromStdString(rpi::VectorToString(registration->GetNumberOfIterations())) + "\n"; switch (registration->GetUpdateRule()) { case 0: { titleAndParameters += " Update rule: SSD Non Symmetric Log Domain\n"; break; } case 1: { titleAndParameters += " Update rule: SSD Symmetric Log Domain\n"; break; } case 2: { titleAndParameters += " Update rule: LCC\n"; break; } default: { titleAndParameters += " Update rule: Unknown\n"; } } titleAndParameters += " Update Field Sigma: " + QString::number(d->updateFieldSigma) + "\n"; titleAndParameters += " Velocity Field Sigma: " + QString::number(d->velocityFieldSigma) + "\n"; titleAndParameters += " BCH Expansion: " + QString::number(d->BCHExpansion) + "\n"; switch(d->interpolatorType) { case rpi::INTERPOLATOR_NEAREST_NEIGHBOR: { titleAndParameters += " Interpolator type: Nearest Neighbor\n"; break; } case rpi::INTERPOLATOR_LINEAR: { titleAndParameters += " Interpolator type: Linear\n"; break; } case rpi::INTERPOLATOR_BSLPINE: { titleAndParameters += " Interpolator type: B-Spline\n"; break; } case rpi::INTERPOLATOR_SINUS_CARDINAL: { titleAndParameters += " Interpolator type: Sinus Cardinal\n"; break; } default: { titleAndParameters += " Interpolator type: Unknown\n"; } } return titleAndParameters; } // ///////////////////////////////////////////////////////////////// // Type instanciation // ///////////////////////////////////////////////////////////////// dtkAbstractProcess *createLCCLogDemons() { return new LCCLogDemons; }