From cf000cf340d79e641288b9e85e6c3a0be3205488 Mon Sep 17 00:00:00 2001 From: FLORENT LERAY Date: Thu, 8 Jun 2023 10:44:50 +0200 Subject: [PATCH 01/11] - Add an ODF average tool (Goh et al. 2011) - Add a Track Orientation Distribution (TOD) image estimator (Dhollander et al. 2014). --- Anima/diffusion/odf/CMakeLists.txt | 4 +- .../diffusion/odf/odf_average/CMakeLists.txt | 43 + .../odf/odf_average/animaODFAverage.cxx | 194 ++++ .../animaODFAverageImageFilter.cxx | 448 +++++++++ .../odf_average/animaODFAverageImageFilter.h | 131 +++ .../odf/tod_estimator/CMakeLists.txt | 40 + .../odf/tod_estimator/animaTODEstimator.cxx | 70 ++ .../animaTODEstimatorImageFilter.h | 138 +++ .../animaTODEstimatorImageFilter.hxx | 850 ++++++++++++++++++ 9 files changed, 1917 insertions(+), 1 deletion(-) create mode 100644 Anima/diffusion/odf/odf_average/CMakeLists.txt create mode 100644 Anima/diffusion/odf/odf_average/animaODFAverage.cxx create mode 100644 Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx create mode 100644 Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h create mode 100644 Anima/diffusion/odf/tod_estimator/CMakeLists.txt create mode 100644 Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx create mode 100644 Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h create mode 100644 Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx diff --git a/Anima/diffusion/odf/CMakeLists.txt b/Anima/diffusion/odf/CMakeLists.txt index 3551f66fc..9a7212709 100644 --- a/Anima/diffusion/odf/CMakeLists.txt +++ b/Anima/diffusion/odf/CMakeLists.txt @@ -1,2 +1,4 @@ add_subdirectory(generalized_fa) -add_subdirectory(odf_estimator) \ No newline at end of file +add_subdirectory(odf_estimator) +add_subdirectory(tod_estimator) +add_subdirectory(odf_average) \ No newline at end of file diff --git a/Anima/diffusion/odf/odf_average/CMakeLists.txt b/Anima/diffusion/odf/odf_average/CMakeLists.txt new file mode 100644 index 000000000..f3c7c5121 --- /dev/null +++ b/Anima/diffusion/odf/odf_average/CMakeLists.txt @@ -0,0 +1,43 @@ +if(BUILD_TOOLS) + +project(animaODFAverage) + +## ############################################################################# +## List Sources +## ############################################################################# + +list_source_files(${PROJECT_NAME} + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +## ############################################################################# +## add executable +## ############################################################################# + +add_executable(${PROJECT_NAME} + ${${PROJECT_NAME}_CFILES} + ) + + +## ############################################################################# +## Link +## ############################################################################# + +target_link_libraries(${PROJECT_NAME} + ${ITKIO_LIBRARIES} + AnimaSHTools + AnimaDataIO + ITKCommon + ${VTK_PREFIX}CommonCore + ${VTKSYS_LIBRARY} + ${ITKIO_LIBRARIES} + + ) + +## ############################################################################# +## install +## ############################################################################# + +set_exe_install_rules(${PROJECT_NAME}) + +endif() diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx new file mode 100644 index 000000000..931806891 --- /dev/null +++ b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx @@ -0,0 +1,194 @@ +#include + +#include +#include + +#include + +#include + +//Update progression of the process +void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData) +{ + itk::ProcessObject * processObject = (itk::ProcessObject*) caller; + std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"< inArg("i","input-odf","ODF images list as a text file",true,"","ODF images list",cmd); + TCLAP::ValueArg maskArg("m","masks","Masks images list as a text file",true,"","Masks images list",cmd); + + TCLAP::ValueArg resArg("o","output","Result average ODF",true,"","result ODF image",cmd); + TCLAP::ValueArg resMaskArg("M","resMask","Result average mask",false,"","average mask",cmd); + + TCLAP::ValueArg resWeightArg("w","resWeight","Flag for iterative barycenter atlasing - Result number of average per pixel",false,"","number average image",cmd); + TCLAP::ValueArg resPondArg("P","resPond","Resulting ponderation map, in case of two images averaging",false,"","ponderation map",cmd); + + TCLAP::ValueArg aicImageArg("a","aic","Input AIC image",false,"","aic image",cmd); + + TCLAP::ValueArg weightArg("W","weight","In the case of 2 image averaging, weight of the first image ",false, 0.0 ,"first image weight",cmd); + TCLAP::ValueArg testArg("t","test","test value",false, 0.0 ,"first image weight",cmd); + + TCLAP::SwitchArg gfaArg("", "gfa", "Use GFA of the atlas (first image) to ponderate",cmd,false); +// TCLAP::SwitchArg baseArg("t", "tournier", "Use the Tournier SH base (Mrtrix). If not set, use the Descoteaux set",cmd,false); + + TCLAP::ValueArg nbpArg("p","numberofthreads","Number of threads to run on (default: all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd); + + try + { + cmd.parse(argc,argv); + } + catch (TCLAP::ArgException& e) + { + std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; + return EXIT_FAILURE; + } + itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); + callback->SetCallback(eventCallback); + + std::ifstream odfFile(inArg.getValue().c_str()); + if (!odfFile.is_open()) + { + std::cerr << "Please provide usable file with input ODFs" << std::endl; + return EXIT_FAILURE; + } + + std::ifstream maskFile(maskArg.getValue().c_str()); + if (!maskFile.is_open()) + { + std::cerr << "Please provide usable file with input Masks" << std::endl; + return EXIT_FAILURE; + } + + typedef anima::ODFAverageImageFilter FilterType; + typedef FilterType::InputImageType InputImageType; + typedef FilterType::OutputImageType OutputImageType; + typedef FilterType::MaskImageType MaskImageType; + typedef FilterType::DoubleImageType DoubleImageType; + FilterType::Pointer mainFilter = FilterType::New(); + + std::vector inputFiles; + std::vector maskFiles; + unsigned int numInput = 0; + while (!odfFile.eof()) + { + char tmpStr[2048], maskStr[2048]; + odfFile.getline(tmpStr,2048); + maskFile.getline(maskStr, 2048); + + if (strcmp(tmpStr,"") == 0) + continue; + + // std::cout << "Loading image " << numInput << ": " << tmpStr << std::endl; + // std::cout << "Loading Mask " << numInput << ": " << maskStr << std::endl; + + inputFiles.push_back(tmpStr); + maskFiles.push_back(maskStr); + // mainFilter->SetInput(numInput, anima::readImage(tmpStr)); + // mainFilter->SetMask(numInput, anima::readImage(maskStr)); + numInput++; + } + odfFile.close(); + maskFile.close(); + + if(weightArg.getValue() != 0.0) + mainFilter->Setweight(weightArg.getValue()); + if(gfaArg.getValue()) + mainFilter->SetflagGFA(true); + +// if(baseArg.getValue()) +// mainFilter->SetTournier(true); + + if(aicImageArg.getValue() != "") + mainFilter->SetAicImage(anima::readImage(aicImageArg.getValue())); + mainFilter->SettestCombi(testArg.getValue()); + + MaskImageType::Pointer geomImage = anima::readImage(maskFiles[0]); + + std::cout << "Processing image : " << inputFiles[1] << std::endl; + + if(resWeightArg.getValue() != "") + { + MaskImageType::Pointer weightImage = MaskImageType::New(); + weightImage->Initialize(); + weightImage->SetDirection(geomImage->GetDirection()); + weightImage->SetSpacing(geomImage->GetSpacing()); + weightImage->SetOrigin(geomImage->GetOrigin()); + MaskImageType::RegionType region = geomImage->GetLargestPossibleRegion(); + weightImage->SetRegions(region); + weightImage->Allocate(); + weightImage->FillBuffer(0); + mainFilter->SetWeightImage(weightImage); + } + mainFilter->SetInput(0, anima::readImage(inputFiles[0])); + mainFilter->SetInput(1, anima::readImage(inputFiles[1])); + + mainFilter->SetMask(0, anima::readImage(maskFiles[0])); + mainFilter->SetMask(1, anima::readImage(maskFiles[1])); + + mainFilter->AddObserver(itk::ProgressEvent(), callback); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + itk::TimeProbe tmpTimer; + + tmpTimer.Start(); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + + for(int i = 2; i < numInput; i++) + { + + std::cout << std::endl << "Processing image : " << inputFiles[i] << std::endl; + mainFilter->SetWeightImage(mainFilter->getWeightImage()); + + mainFilter->SetInput(0, mainFilter->GetOutput()); + mainFilter->SetInput(1, anima::readImage(inputFiles[i])); + + mainFilter->SetMask(0, mainFilter->getMaskAverage()); + mainFilter->SetMask(1, anima::readImage(maskFiles[i])); + + mainFilter->AddObserver(itk::ProgressEvent(), callback); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + } + + tmpTimer.Stop(); + + std::cout << "\nAveraging done in " << tmpTimer.GetTotal() << " s" << std::endl; + + anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); + + if(resWeightArg.getValue() != "") + anima::writeImage(resWeightArg.getValue(), mainFilter->getWeightImage()); + + if(resMaskArg.getValue() != "") + { + MaskImageType::Pointer maskOut = mainFilter->getMaskAverage(); + anima::writeImage(resMaskArg.getValue(), maskOut.GetPointer()); + } + + if(resPondArg.getValue() != "") + anima::writeImage(resPondArg.getValue(), mainFilter->getPondImage()); + + return EXIT_SUCCESS; +} diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx new file mode 100644 index 000000000..fe086caba --- /dev/null +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx @@ -0,0 +1,448 @@ +#include "animaODFAverageImageFilter.h" + +#include +#include "animaLogExpMapsUnitSphere.h" + +#include +#include + +namespace anima +{ + +void ODFAverageImageFilter::SetMask(unsigned int i, MaskImagePointer mask) +{ + if (i == m_Masks.size()) + m_Masks.push_back(mask); + else if (i > m_Masks.size()) + { + itkExceptionMacro("Trying to add a non contiguous mask... Add mask images contiguously (0,1,2,3,...)..."); + } + else + m_Masks[i] = mask; +} + + +void ODFAverageImageFilter::BeforeThreadedGenerateData() +{ + Superclass::BeforeThreadedGenerateData(); + + m_PondImage = DoubleImageType::New(); + m_PondImage->Initialize(); + m_PondImage->SetDirection(m_Masks[0]->GetDirection()); + m_PondImage->SetSpacing(m_Masks[0]->GetSpacing()); + m_PondImage->SetOrigin(m_Masks[0]->GetOrigin()); + MaskImageType::RegionType region = m_Masks[0]->GetLargestPossibleRegion(); + m_PondImage->SetRegions(region); + m_PondImage->Allocate(); + m_PondImage->FillBuffer(0); + + // if(m_AicImage) + // { + // itk::ImageRegionIterator aicIt(m_AicImage, m_AicImage->GetLargestPossibleRegion()); + // m_minAic = 1e10; + + // while (!aicIt.IsAtEnd()) + // { + // double tmpVal = aicIt.Get(); + // if(tmpVal < m_minAic && tmpVal > 1000) + // m_minAic = tmpVal; + // ++aicIt; + // } + // } + + m_minAic = 10; + + m_histoODFs.resize(this->GetNumberOfIndexedInputs()); + m_SQRTHistoODFs.resize(this->GetNumberOfIndexedInputs()); + + for(int i = 0; i < this->GetNumberOfIndexedInputs(); i++) + { + m_histoODFs[i].resize(m_nbSamplesPhi * m_nbSamplesTheta); + m_SQRTHistoODFs[i].resize(m_nbSamplesPhi * m_nbSamplesTheta); + } + + m_ODFSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8 * this->GetInput(0)->GetVectorLength() + 1)); + m_vectorLength = (m_ODFSHOrder + 1)*(m_ODFSHOrder + 2)/2; + + m_spherHarm.set_size(m_nbSamplesPhi * m_nbSamplesTheta, m_vectorLength); + m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_ODFSHOrder); + + this->discretizeSH(); + +} +void ODFAverageImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) +{ + typedef itk::ImageRegionConstIterator ImageIteratorType; + typedef itk::ImageRegionConstIterator MaskIteratorType; + typedef itk::ImageRegionConstIterator DoubleIteratorType; + + static double progress = 0; + double weight0 = 0.5, weight1 = 0.5; + + unsigned int numInputs = this->GetNumberOfIndexedInputs(); + std::vector inIterators(numInputs); + std::vector maskIterators(numInputs); + for (unsigned int i = 0;i < numInputs;++i) + { + inIterators[i] = ImageIteratorType(this->GetInput(i),outputRegionForThread); + maskIterators[i] = MaskIteratorType(m_Masks[i], outputRegionForThread); + } + typedef itk::ImageRegionIterator OutImageIteratorType; + OutImageIteratorType outIterator(this->GetOutput(),outputRegionForThread); + + itk::ImageRegionIterator weightIt; + if(m_WeightImage) + weightIt = itk::ImageRegionIterator(m_WeightImage, outputRegionForThread); + + itk::ImageRegionIterator aicIt; + if(m_AicImage) + aicIt = itk::ImageRegionIterator (m_AicImage, outputRegionForThread); + + itk::ImageRegionIterator pondIt(m_PondImage, outputRegionForThread); + + std::vector arrayCoef(numInputs), arraySQRTCoef(numInputs); + + VectorType nullVector(m_vectorLength); + nullVector.Fill(0.0); + + while(!outIterator.IsAtEnd()) + { + if(maskIterators[0].Get() == 0 && maskIterators[1].Get() == 0) + outIterator.Set(nullVector); + + else if(maskIterators[0].Get() == 1 && maskIterators[1].Get() == 0) + { + outIterator.Set(inIterators[0].Get()); + } + else if(maskIterators[0].Get() == 0 && maskIterators[1].Get() == 1) + { + outIterator.Set(inIterators[1].Get()); + } + else + { + for(int i = 0; i < numInputs; i++) + { + arrayCoef[i] = inIterators[i].Get(); + this->discretizeODF(arrayCoef[i], m_histoODFs[i]); + arraySQRTCoef[i] = this->getSquareRootODFCoef(m_histoODFs[i]); + } + + if(m_WeightImage) + { + double k = weightIt.Get(); + weight1 = (k+1) / (k+2); + weight0 = 1 - weight1; + + weightIt.Set(weightIt.Get() + 1); + } + + else if(m_weight != 0.0) + { + weight0 = m_weight; + weight1 = 1 - weight1; + } + + else if(m_flagGFA && m_AicImage) + { + double tmpAic = aicIt.Get(); + double aic; + if(tmpAic == 0) + aic = 0.0; + else + aic = std::exp((m_minAic - tmpAic) / 2000); + double gfa = 0.8*this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); + + // weight0 = (m_testCombi*gfa + (1-m_testCombi)*aic) / (gfa+aic); + weight0 = std::exp((m_testCombi*gfa + (1-m_testCombi)*aic) - (m_testCombi + (1-m_testCombi))); + weight1 = 1 - weight0; + + pondIt.Set(weight0); + } + + else if(m_flagGFA) + { + double gfa = this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); + weight0 = 1*(1-gfa); + weight1 = 1 - weight0; + pondIt.Set(weight0); + if(weight0 == 0) + auto test = outIterator.GetIndex(); + } + + else if(m_AicImage) + { + double aic = aicIt.Get(); + if(aic == 0) + weight1 = 1; + else + weight1 = std::exp((m_minAic - aic) / 1); + + weight0 = 1 - weight1; + pondIt.Set(weight0); + if(weight0 == 0) + { + auto test = outIterator.GetIndex(); + } + } + + VectorType averageSQRTCoef(m_vectorLength); + this->getAverageHisto(arraySQRTCoef, averageSQRTCoef, weight0, weight1); + + std::vector averageSQRTHisto(m_nbSamplesPhi*m_nbSamplesTheta); + this->discretizeODF(averageSQRTCoef, averageSQRTHisto); + + VectorType averageCoef(m_vectorLength); + averageCoef = this->getSquareODFCoef(averageSQRTHisto); + + outIterator.Set(averageCoef); + } + + for (unsigned int i = 0;i < numInputs;++i) + { + ++inIterators[i]; + ++maskIterators[i]; + } + + if(m_WeightImage) + ++weightIt; + + if(m_AicImage) + ++aicIt; + + ++outIterator; + ++pondIt; + this->IncrementNumberOfProcessedPoints(); + } +} + + +void ODFAverageImageFilter::AfterThreadedGenerateData() +{ + delete m_ODFSHBasis; +} + +void ODFAverageImageFilter::discretizeSH() +{ + double sqrt2 = std::sqrt(2); + double theta, phi; + double deltaPhi = 2*M_PI/(double)(m_nbSamplesPhi - 1); + double deltaTheta = M_PI/(double)(m_nbSamplesTheta - 1); + + int k = 0; + for(int i = 0; i < m_nbSamplesTheta; i++) + { + theta = (double)i * deltaTheta; + + for(int j = 0; j < m_nbSamplesPhi; j++) + { + phi = (double)j*deltaPhi; + int c = 0; + for(double l = 0; l <= m_ODFSHOrder; l+=2) + { + for(double m = -l; m <= l; m++) + { + // if(m_Tournier) + // m_spherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPositionTournier(l, m, theta, phi); + // else + m_spherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi); + } + } + k++; + } + } +} + +void ODFAverageImageFilter::discretizeODF(VectorType &modelValue, std::vector &odf) +{ + int flag = 0, k = 0; + double theta, phi; + double resVal2 = 0; + + double deltaPhi = 2*M_PI/(double)(m_nbSamplesPhi - 1); + double deltaTheta = M_PI/(double)(m_nbSamplesTheta - 1); + + for(int i = 0; i < m_nbSamplesTheta; ++i) + { + for(int j = 0; j < m_nbSamplesPhi; j++) + { + + phi = (double)j * deltaPhi; + theta = (double)i * deltaTheta; + + // if(m_Tournier) + // resVal2 = m_ODFSHBasis->getValueAtPositionTournier(modelValue, theta, phi); + // else + resVal2 = m_ODFSHBasis->getValueAtPosition(modelValue, theta, phi); + + if(resVal2 < 0) + flag++; + + odf[k] = resVal2; + k++; + } + } +} + +ODFAverageImageFilter::VectorType ODFAverageImageFilter::getSquareRootODFCoef(std::vector &odf) +{ + vnl_matrix squareRootOdf(m_nbSamplesTheta * m_nbSamplesPhi, 1); + vnl_matrix squareRootCoef(m_vectorLength, 1); + + for(int i = 0; i < m_nbSamplesTheta * m_nbSamplesPhi; ++i) + { + if(odf[i] < 0) + odf[i] = 0; + squareRootOdf(i, 0) = std::sqrt(odf[i]); + } + squareRootCoef = vnl_matrix_inverse(m_spherHarm.transpose() * m_spherHarm).as_matrix() * m_spherHarm.transpose()*squareRootOdf; + + VectorType squareRootModelValue(m_vectorLength); + std::vector test(m_vectorLength); + for(int i = 0; i < m_vectorLength; ++i) + { + squareRootModelValue[i] = squareRootCoef(i, 0); + test[i] = squareRootCoef(i, 0); + } + return squareRootModelValue; +} + +ODFAverageImageFilter::VectorType ODFAverageImageFilter::getSquareODFCoef(std::vector &odf) +{ + vnl_matrix squareOdf(m_nbSamplesTheta * m_nbSamplesPhi, 1); + vnl_matrix squareCoef(m_vectorLength, 1); + + for(int i = 0; i < m_nbSamplesTheta * m_nbSamplesPhi; ++i) + squareOdf(i, 0) = std::pow(odf[i], 2); + + squareCoef = vnl_matrix_inverse(m_spherHarm.transpose() * m_spherHarm).as_matrix() * m_spherHarm.transpose()*squareOdf; + + VectorType squareModelValue(m_vectorLength); + std::vector test(m_vectorLength); + for(int i = 0; i < m_vectorLength; ++i) + { + squareModelValue[i] = squareCoef(i, 0); + } + +// long double integralODF = 0; + +// for (unsigned int i = 0;i < m_spherHarm.rows(); ++i) +// for (unsigned int j = 0;j < m_vectorLength;++j) +// integralODF += m_spherHarm[i][j] * squareModelValue[j]; + +// for (unsigned int i = 0;i < m_vectorLength;++i) +// squareModelValue[i] /= integralODF; + + squareModelValue[0] = 1/(2*sqrt(M_PI)); + return squareModelValue; +} + +void ODFAverageImageFilter::getAverageHisto(std::vector &coefs, ODFAverageImageFilter::VectorType &resCoef, double weight0, double weight1) +{ + int numImage = this->GetNumberOfIndexedInputs(); + + std::vector> arrayCoef(numImage), arrayLogMap(numImage); + std::vector expMap(m_vectorLength), mean(m_vectorLength), nextMean(m_vectorLength), tangent(m_vectorLength); + + const int T = 100; + const double eps = 0.000035; + + for(int i = 0; i < numImage; i++) + { + arrayCoef[i].resize(m_vectorLength); + arrayLogMap[i].resize(m_vectorLength); + + for(int n = 0; n < m_vectorLength; n++) + { + arrayCoef[i][n] = coefs[i][n]; + // nextMean[n] += 1/(double)numImage * arrayCoef[i][n]; + } + } + + nextMean = arrayCoef[0]; + + int t = 0; + double normTan = 1; + while(t < T && normTan > eps) + { + mean = nextMean; + + for(int i = 0; i < numImage; i++) + anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); + + std::fill(tangent.begin(), tangent.end(), 0); + for(int n = 0; n < m_vectorLength; n++) + tangent[n] += weight1 * arrayLogMap[1][n] + weight0 * arrayLogMap[0][n]; + + normTan = anima::ComputeNorm(tangent); + anima::sphere_exp_map(tangent, mean, nextMean); + + t++; + } + + // nextMean[0] = 2.82094792e-01; + for(int i = 0; i < m_vectorLength; ++i) + resCoef[i] = nextMean[i]; +} + +void ODFAverageImageFilter::getAverageODF(std::vector> &histos, ODFAverageImageFilter::VectorType &resCoef, double smallWeight, double bigWeight) +{ + vnl_matrix meanODF(m_nbSamplesTheta * m_nbSamplesPhi, 1); + vnl_matrix meanCoef(m_vectorLength, 1); + + for(int i = 0; i < m_nbSamplesTheta * m_nbSamplesPhi; ++i) + meanODF(i, 0) = (histos[0][i] + histos[1][i]) / 2.0; + + meanCoef = vnl_matrix_inverse(m_spherHarm.transpose() * m_spherHarm).as_matrix() * m_spherHarm.transpose()*meanODF; + + std::vector test(m_vectorLength); + meanCoef(0, 0) = 2.82094792e-01; + for(int i = 0; i < m_vectorLength; ++i) + { + resCoef[i] = meanCoef(i, 0); + } + +} + +ODFAverageImageFilter::MaskImagePointer ODFAverageImageFilter::getMaskAverage() +{ + MaskImageType::Pointer maskAverage = MaskImageType::New(); + maskAverage->Initialize(); + maskAverage->SetDirection(m_Masks[0]->GetDirection()); + maskAverage->SetSpacing(m_Masks[0]->GetSpacing()); + maskAverage->SetOrigin(m_Masks[0]->GetOrigin()); + MaskImageType::RegionType region = m_Masks[0]->GetLargestPossibleRegion(); + maskAverage->SetRegions(region); + maskAverage->Allocate(); + maskAverage->FillBuffer(0); + + typedef itk::ImageRegionConstIterator MaskIteratorType; + + itk::ImageRegionIterator averageIt(maskAverage, maskAverage->GetLargestPossibleRegion()); + MaskIteratorType mask0It(m_Masks[0], m_Masks[0]->GetLargestPossibleRegion()); + MaskIteratorType mask1It(m_Masks[1], m_Masks[1]->GetLargestPossibleRegion()); + + while(!averageIt.IsAtEnd()) + { + averageIt.Set(mask0It.Get() || mask1It.Get()); + + ++averageIt; + ++mask0It; + ++mask1It; + } + + m_Masks.clear(); + return maskAverage; +} + +double ODFAverageImageFilter::GetGeneralizedFractionalAnisotropy(ODFAverageImageFilter::VectorType &modelValue) +{ + double sumSquares = 0; + for (unsigned int i = 0;i < this->m_vectorLength;++i) + sumSquares += modelValue[i]*modelValue[i]; + + return std::sqrt(1.0 - modelValue[0]*modelValue[0]/sumSquares); + +} + + +} // end namespace anima diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h new file mode 100644 index 000000000..38f3b76b5 --- /dev/null +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + + +namespace anima +{ +class ODFAverageImageFilter : +public anima::NumberedThreadImageToImageFilter < itk::VectorImage, itk::VectorImage > +{ +public: + typedef ODFAverageImageFilter Self; + typedef double PixelScalarType; + typedef unsigned int PixelMaskType; + typedef itk::VectorImage InputImageType; + typedef itk::VectorImage OutputImageType; + typedef itk::Image DoubleImageType; + typedef itk::Image MaskImageType; + typedef anima::NumberedThreadImageToImageFilter Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self) + + /** Run-time type information (and related methods) */ + itkTypeMacro(ODFAverageImageFilter, anima::NumberedThreadImageToImageFilter) + + /** Image typedef support */ + typedef typename InputImageType::Pointer InputImagePointer; + typedef typename OutputImageType::Pointer OutputImagePointer; + typedef typename MaskImageType::Pointer MaskImagePointer; + typedef typename OutputImageType::PixelType OutputPixelType; + typedef typename DoubleImageType::Pointer DoubleImagePointer; + + /** Superclass typedefs. */ + typedef typename Superclass::InputImageRegionType InputImageRegionType; + typedef typename Superclass::OutputImageRegionType OutputImageRegionType; + + typedef itk::VariableLengthVector VectorType; + typedef std::vector> HistoArrayType; + + void SetMask(unsigned int i, MaskImagePointer mask); + + void SetWeightImage(MaskImageType *weightImage) {m_WeightImage = weightImage;} + MaskImageType *getWeightImage() {return m_WeightImage;} + DoubleImageType *getPondImage() {return m_PondImage;} + MaskImagePointer getMaskAverage(); + + itkSetMacro(weight, double) + itkSetMacro(testCombi, double) + itkSetMacro(flagGFA, bool) + itkSetMacro(Tournier, bool) + itkSetMacro(AicImage, DoubleImagePointer) + +protected: + ODFAverageImageFilter() + : Superclass() + { + m_nbSamplesTheta = 10; + m_nbSamplesPhi = 2 * m_nbSamplesTheta; + m_weight = 0.0; + + m_flagGFA = false; + } + + virtual ~ODFAverageImageFilter() + { + } + + void BeforeThreadedGenerateData() ITK_OVERRIDE; + void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; + void AfterThreadedGenerateData() ITK_OVERRIDE; + + void discretizeSH(); + void discretizeODF(VectorType &Coef, std::vector &resHisto); + void getAverageHisto(std::vector &coefs, VectorType &resCoef, double smallWeight, double bigWeight); + void getAverageODF(std::vector> &histos, VectorType &resCoef, double smallWeight, double bigWeight); + VectorType getSquareRootODFCoef(std::vector &histo); + VectorType getSquareODFCoef(std::vector &histo); + + double GetGeneralizedFractionalAnisotropy(VectorType &modelValue); + + + +private: + ITK_DISALLOW_COPY_AND_ASSIGN(ODFAverageImageFilter); + + int m_nbSamplesPhi; + int m_nbSamplesTheta; + int m_vectorLength; + + double m_ODFSHOrder; + double m_smallWeight; + double m_bigWeight; + double m_weight; + double m_minAic; + double m_testCombi; + + bool m_flagGFA; + bool m_Tournier; + + MaskImagePointer m_WeightImage; + DoubleImagePointer m_PondImage; + DoubleImagePointer m_AicImage; + + std::vector m_Masks; + + vnl_matrix m_spherHarm; + + anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; + + HistoArrayType m_histoODFs, m_SQRTHistoODFs; + + + +}; +} // end of namespace anima + + diff --git a/Anima/diffusion/odf/tod_estimator/CMakeLists.txt b/Anima/diffusion/odf/tod_estimator/CMakeLists.txt new file mode 100644 index 000000000..8cbbaa67c --- /dev/null +++ b/Anima/diffusion/odf/tod_estimator/CMakeLists.txt @@ -0,0 +1,40 @@ +if(BUILD_TOOLS AND USE_NLOPT) + +project(animaTODEstimator) + +## ############################################################################# +## List Sources +## ############################################################################# + +list_source_files(${PROJECT_NAME} + ${CMAKE_CURRENT_SOURCE_DIR} + ) + + +## ############################################################################# +## add executable +## ############################################################################# + +add_executable(${PROJECT_NAME} + ${${PROJECT_NAME}_CFILES} + ) + + +## ############################################################################# +## Link +## ############################################################################# + +target_link_libraries(${PROJECT_NAME} + ${VTK_LIBRARIES} + ${ITKIO_LIBRARIES} + AnimaSHTools + AnimaDataIO + ) + +## ############################################################################# +## install +## ############################################################################# + +set_exe_install_rules(${PROJECT_NAME}) + +endif() diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx new file mode 100644 index 000000000..c868a2189 --- /dev/null +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx @@ -0,0 +1,70 @@ +#include +#include +#include + +#include + +void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData) +{ + itk::ProcessObject * processObject = (itk::ProcessObject*) caller; + std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"< inArg("i","input","Input tractography image (.vtk or .vtp)",true,"","input tractography image",cmd); + TCLAP::ValueArg resArg("o","outputfile","Result TOD image",true,"","result TOD image",cmd); + TCLAP::ValueArg refArg("g","geometry","Output image geometry",true,"","output geometry",cmd); + + TCLAP::SwitchArg normArg("N", "Normalize", "Normalize TOD", cmd, false); + + TCLAP::ValueArg orderArg("k","order","Order of spherical harmonics basis (default 4)",false,4,"Order of SH basis",cmd); + + TCLAP::ValueArg nbpArg("p","numberofthreads","Number of threads to run on (default: all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd); + + try + { + cmd.parse(argc,argv); + } + catch (TCLAP::ArgException& e) + { + std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; + return EXIT_FAILURE; + } + + typedef anima::TODEstimatorImageFilter FilterType; + + FilterType::Pointer mainFilter = FilterType::New(); + +// if (orderArg.getValue() % 2 == 0) +// mainFilter->SetLOrder(orderArg.getValue()); +// else +// mainFilter->SetLOrder(orderArg.getValue() - 1); + typedef FilterType::InputImageType InputImageType; + mainFilter->SetInput(anima::readImage(refArg.getValue())); + + mainFilter->SetLOrder(orderArg.getValue()); + mainFilter->SetInputFileName(inArg.getValue()); + mainFilter->SetRefFileName(refArg.getValue()); + mainFilter->SetNormalize(normArg.getValue()); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); + callback->SetCallback(eventCallback); + mainFilter->AddObserver(itk::ProgressEvent(), callback); + + itk::TimeProbe tmpTime; + tmpTime.Start(); + + mainFilter->Update(); + + tmpTime.Stop(); + + std::cout << std::endl << "Execution Time: " << tmpTime.GetTotal() << std::endl; + + anima::writeImage (resArg.getValue(),mainFilter->GetOutput()); + + return EXIT_SUCCESS; +} diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h new file mode 100644 index 000000000..29abdc6f4 --- /dev/null +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h @@ -0,0 +1,138 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace anima +{ + +//template +class TODEstimatorImageFilter : +public anima::NumberedThreadImageToImageFilter < itk::Image, itk::VectorImage > +{ +public: + + typedef TODEstimatorImageFilter Self; +// typedef itk::Vector pointType; + typedef itk::Point PointType; +// typedef PointType DirType; + typedef itk::Vector DirType; + typedef std::vector DirVectorType; + typedef std::vector FiberType; + + typedef double MathScalarType; + + typedef itk::Matrix Matrix3DType; + typedef itk::Vector Vector3DType; + typedef itk::VariableLengthVector VectorType; + + typedef anima::ODFSphericalHarmonicBasis baseSH; + typedef std::complex complexType; + + typedef itk::VectorImage TOutputImage; + typedef itk::Image TRefImage; + + typedef anima::NumberedThreadImageToImageFilter Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; + + + itkNewMacro(Self) + + itkTypeMacro(TODEstimatorImageFilter, anima::NumberedThreadImageToImageFilter); + + typedef typename TOutputImage::Pointer OutputImagePointer; + + typedef typename Superclass::InputImageRegionType InputImageRegionType; + typedef typename Superclass::OutputImageRegionType OutputImageRegionType; + + itkSetMacro(InputFileName,std::string); + itkSetMacro(RefFileName,std::string); + itkSetMacro(LOrder,unsigned int); + itkSetMacro(Normalize, bool); + + +protected: + TODEstimatorImageFilter() + : Superclass() + { + } + + virtual ~TODEstimatorImageFilter() + { + + } + +// void GenerateData() ITK_OVERRIDE; + + void BeforeThreadedGenerateData() ITK_OVERRIDE; + void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; + void AfterThreadedGenerateData() ITK_OVERRIDE; + + + FiberType readFiber(vtkIdType numberOfPoints, const vtkIdType *indices, vtkPoints *points); + PointType getCenterVoxel(int index, FiberType &fiber); + DirType getFiberDirection(int index, FiberType &fiber); + void getSHCoefs(DirType dir, VectorType &resSH, baseSH &basis); + void ComputeCoefs(); + void processFiber(FiberType &fiber, baseSH &basis); + + void getMainDirections(DirVectorType inDirs, DirVectorType &mainDirs); + double getEuclideanDistance(DirType dir1, DirType dir2); + DirType getNewClusterAverage(int numCluster, DirVectorType &dirs, std::vector &cluster); + + void getSHCoef(DirType dir, VectorType &coefs); + + void precomputeSH(); + void discretizeODF(VectorType ODFCoefs, std::vector &ODFDiscret); + VectorType getSquareRootODF(std::vector ODFDiscret); + VectorType getSquareODF(std::vector ODFDiscret); + void getAverageCoefs(std::vector &vecCoefs, VectorType &avgCoef); + + void averageODFs(std::vector &vecCoefs, VectorType &resOdf); + + vnl_matrix GetRotationMatrix(DirType dir1, DirType dir2); + +// void GenerateOutputInformation() ITK_OVERRIDE; + +private: + ITK_DISALLOW_COPY_AND_ASSIGN(TODEstimatorImageFilter); + + std::string m_InputFileName; + std::string m_RefFileName; + + DirType m_CstDir; + + unsigned int m_LOrder; + int m_VectorLength; + + double m_NbSample; + + bool m_Normalize; + + + VectorType m_GaussCoefs; + + std::vector> m_SphereSampl; + vnl_matrix m_SpherHarm; + + anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; + std::vector> m_ImgDir; + + itk::Image::Pointer test; + +}; +} // end namespace anima + +#include "animaTODEstimatorImageFilter.hxx" diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx new file mode 100644 index 000000000..c1c58227d --- /dev/null +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx @@ -0,0 +1,850 @@ +#pragma once + +#include "animaTODEstimatorImageFilter.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "animaLogExpMapsUnitSphere.h" + +#include + +namespace anima +{ + +void TODEstimatorImageFilter::BeforeThreadedGenerateData() +{ + Superclass::BeforeThreadedGenerateData(); + + TRefImage::Pointer refImg = anima::readImage(m_RefFileName); + + unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2; + m_VectorLength = vectorLength; + TOutputImage *output = this->GetOutput(); + output->SetVectorLength(vectorLength); + output->SetRegions(refImg->GetLargestPossibleRegion()); + output->SetSpacing(refImg->GetSpacing()); + output->SetDirection(refImg->GetDirection()); + output->SetOrigin(refImg->GetOrigin()); + output->Allocate(); + + VectorType tmp; + tmp.SetSize((m_LOrder+1)*(m_LOrder+2)/2); + tmp.Fill(0); + output->FillBuffer(tmp); + + test = itk::Image::New(); + test->SetRegions(refImg->GetLargestPossibleRegion()); + test->SetSpacing(refImg->GetSpacing()); + test->SetDirection(refImg->GetDirection()); + test->SetOrigin(refImg->GetOrigin()); + test->Allocate(); + + m_CstDir[0] = 0; + m_CstDir[1] = 0; + m_CstDir[2] = 1; + + m_NbSample = 200; + anima::GetSphereEvenSampling(m_SphereSampl, m_NbSample); + + m_SpherHarm.set_size(m_NbSample, vectorLength); + + m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_LOrder); + this->precomputeSH(); + + const int Xmax = output->GetLargestPossibleRegion().GetSize()[0]; + const int Ymax = output->GetLargestPossibleRegion().GetSize()[1]; + const int Zmax = output->GetLargestPossibleRegion().GetSize()[2]; + + const int Xmin = output->GetLargestPossibleRegion().GetIndex()[0]; + const int Ymin = output->GetLargestPossibleRegion().GetIndex()[1]; + const int Zmin = output->GetLargestPossibleRegion().GetIndex()[2]; + + const int dirX = output->GetDirection()[0][0]; + const int dirY = output->GetDirection()[1][1]; + const int dirZ = output->GetDirection()[2][2]; + + anima::ShapesReader trackReader; + trackReader.SetFileName(m_InputFileName); + trackReader.Update(); + + this->ComputeCoefs(); + + m_ImgDir.resize(Xmax * Ymax * Zmax); + + vtkSmartPointer tracks = trackReader.GetOutput(); + vtkSmartPointer cells = tracks->GetLines(); + vtkSmartPointer points = tracks->GetPoints(); + + const vtkIdType *indices; + vtkIdType numberOfPoints; + + float nbLines = tracks->GetNumberOfLines(); + float lineCount = 0; + + std::cout << "Number of fibers + test : " << nbLines << std::endl; + + anima::ODFSphericalHarmonicBasis basis(m_LOrder); + + auto iter = vtk::TakeSmartPointer(cells->NewIterator()); + for (iter->GoToFirstCell(); !iter->IsDoneWithTraversal(); iter->GoToNextCell()) + { + iter->GetCurrentCell(numberOfPoints, indices); + // std::cout << "Progress : " << (int) (((float)lineCount / nbLines)*100) << "%\r"; + // fflush (stdout); + lineCount++; + + FiberType fiber = readFiber(numberOfPoints, indices, points); + int nbPoints = fiber.size(); + + + for(int i = 0; i < nbPoints; i++) + { + DirType dir = getFiberDirection(i, fiber); + // OutputImagePointer::DirectionType refDir = output->GetDirection(); + + // if(output->GetDirection()[0][0] > 0) + // dir[0] *= -1; + // if(output->GetDirection()[1][1] < 0) + // dir[1] *= -1; + // if(output->GetDirection()[2][2] < 0) + // dir[2] *= -1; + // dir[0] *= -1; + // dir[1] *= -1; + // dir[2] *= -1; + + // anima::Normalize(dir, dir); + PointType point = getCenterVoxel(i, fiber); + itk::Index<3> index, index2; + + output->TransformPhysicalPointToIndex(point, index); + + index[0] *= dirX; + index[1] *= dirY; + index[2] *= dirZ; + + index2[0] = std::floor(point[0]); + index2[1] = std::floor(point[0]); + index2[2] = std::floor(point[0]); + + m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].push_back(dir); + + // // itk::ImageRegionIterator > testItr(test, test->GetLargestPossibleRegion()); + // // while(!testItr.IsAtEnd()) + // // { + // // itk::Image::IndexType index; + // // index = testItr.GetIndex(); + // // testItr.Set(imgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size()); + // // ++testItr; + // // } + // // anima::writeImage>("./test.nii.gz", test); + + + } + } +} + +void TODEstimatorImageFilter::ComputeCoefs() +{ + m_GaussCoefs.SetSize(91); + m_GaussCoefs.Fill(0.0); + + double l1 = 1.71e-4; + double l2 = 2e-5; + double d = l1 - l2; + double spi = sqrt(M_PI); + double pi = M_PI; + double at = M_PI+2*atan((d-l2)/(2*sqrt(d*l2))); + + double c0 = 1/(2*sqrt(pi)); + double c1 = - sqrt(5) * (3*sqrt(l2)*(d+l2)*(pi + atan((d-l2)/(2*sqrt(l2*d)))) - 4*sqrt(d)*(2*d+3*l2)) / (16*sqrt(pi)*pow(d,1.5)); + double c2 = 3 * (4*sqrt(d)*(16*pow(d,2)+115*d*l2+105*pow(l2,2)) - 15*sqrt(l2)*(3*pow(d,2)+10*d*l2+7*pow(l2,2))*(pi+2*atan((d-l2)/(2*sqrt(d*l2))))) / (128*spi*pow(d,2.5)); + double c3 = -105*sqrt(13)*(at*sqrt(l2)*(5*pow(d,3)+35*l2*pow(d,2)+63*pow(l2,2)*d+33*pow(l2,3)) - 4*sqrt(d)*(33*pow(l2,3)+52*d*pow(l2,2)+103*pow(d,2)*l2/5+128*pow(d,3)/105)) / (1024*spi*pow(d,3.5)); + double c4 = -315*sqrt(17) * (at*sqrt(l2)*(35*pow(d,4)+420*pow(d,3)*l2+1386*pow(d,2)*pow(l2,2)+1716*pow(l2,3)*d+715*pow(l2,4)) - 4*sqrt(d)*(715*pow(l2,4)+4433*pow(l2,3)*d/3+957*pow(l2,2)*pow(d,2)+6967*l2*pow(d,3)/35+2048*pow(d,4)/315)) / (16384 * spi * pow(d,4.5)); + double c5 = -24255*sqrt(21) * (at * sqrt(l2)*(9*pow(d,5)+165*pow(d,4)*l2+858*pow(d,3)*pow(l2,2)+12870*pow(d,2)*pow(l2,3)/7+12155*d*pow(l2,4)/7+4199*pow(l2,5)/7) - 4*sqrt(d)*(4199*pow(l2,5)/7+32266*pow(l2,4)*d/21+20691*pow(l2,3)*pow(d,2)/15+24830*pow(l2,2)*pow(d,3)/49+28799*l2*pow(d,4)/441+32768*pow(d,5)/24255)) / (262144*pow(d,5.5)*spi); + + + // m_GaussCoefs[0] = this->m_ODFSHBasis->getNthSHValueAtPosition(0, 0, 0, 0); + // std::vector Dir(3), sphDir(2); + // Dir[0] = 0; + // Dir[1] = 0; + // Dir[2] = 1; + // anima::TransformCartesianToSphericalCoordinates(Dir, sphDir); + // int k = 0; + // for(int l = 0; l <= m_LOrder; l+=2) + // { + // for(int m = -l; m <= l; m++) + // { + // m_GaussCoefs[k] = this->m_ODFSHBasis->getNthSHValueAtPosition(l, m, sphDir[0], sphDir[1]); + // k++; + // } + // } + + m_GaussCoefs[0] = c0; + m_GaussCoefs[3] = c1; + m_GaussCoefs[10] = c2; + m_GaussCoefs[21] = c3; + m_GaussCoefs[36] = c4; + m_GaussCoefs[55] = c5; +} + +void TODEstimatorImageFilter::DynamicThreadedGenerateData(const TODEstimatorImageFilter::OutputImageRegionType &outputRegionForThread) +{ + TOutputImage *output = this->GetOutput(); + itk::ImageRegionIterator outItr(output, outputRegionForThread); + typename TOutputImage::IndexType index, index_out; + + VectorType nullVector; + nullVector.SetSize((m_LOrder+1)*(m_LOrder+2)/2); + nullVector.Fill(0); + + const int Xmax = output->GetLargestPossibleRegion().GetSize()[0]; + const int Ymax = output->GetLargestPossibleRegion().GetSize()[1]; + const int Zmax = output->GetLargestPossibleRegion().GetSize()[2]; + const int dirX = output->GetDirection()[0][0]; + const int dirY = output->GetDirection()[1][1]; + const int dirZ = output->GetDirection()[2][2]; + + + while(!outItr.IsAtEnd()) + { + + + index = outItr.GetIndex(); + + // index[0] *= -1; + // index[1] *= -1; + // index[2] *= -1; + + + // index[0] *= dirX; + // index[1] *= dirY; + // index[2] *= dirZ; + + std::vector vecCoefs; + if(!(m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size()) == 0) + { + int tmpSize = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size(); + DirVectorType mainDirs; + DirVectorType localDir(tmpSize); + localDir = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])]; + this->getMainDirections(localDir, mainDirs); + + // std::cout << "----------------------------------------" << std::endl << std::endl; + // for(int i = 0; i < tmpSize; i++) + // std::cout << localDir[i] << ", " << std::endl; + // std::cout << std::endl << std::endl; + // for(int i = 0; i < mainDirs.size(); i++) + // std::cout << mainDirs[i] << ", " << std::endl; + + // std::cout << std::endl << std::endl; + + VectorType tmpCoefs(m_VectorLength); + tmpCoefs.Fill(0); + for(int i = 0; i < mainDirs.size(); i++) + { + if(!isnan(mainDirs[i][0])) + { + this->getSHCoef(mainDirs[i], tmpCoefs); + // std::vector Dir(3), sphDir(2); + // for(int k = 0; k < 3; k++) + // Dir[k] = mainDirs[i][k]; + // anima::TransformCartesianToSphericalCoordinates(Dir, sphDir); + // int j = 0; + // for(int l = 0; l <= m_LOrder; l+=2) + // { + // for(int m = -l; m <= l; m++) + // { + // tmpCoefs[j] = this->m_ODFSHBasis->getNthSHValueAtPosition(l, m, sphDir[0], sphDir[1]); + // j++; + // } + // } + + vecCoefs.push_back(tmpCoefs); + } + } + + VectorType resCoefs(m_VectorLength); + resCoefs.Fill(0); + if(vecCoefs.size() != 0) + { + // this->averageODFs(vecCoefs, resCoefs); + // for(int k = 0; k < vecCoefs.size(); k++) + // { + // for(int i = 0; i < m_VectorLength; i++) + // resCoefs[i] += (vecCoefs[k][i] / vecCoefs.size()); + // } + for(int k = 0; k < vecCoefs.size(); k++) + { + for(int i = 0; i < m_VectorLength; i++) + resCoefs[i] += vecCoefs[k][i]; + } + if(m_Normalize) + { + for(int i = 0; i < m_VectorLength; i++) + resCoefs[i] /= vecCoefs.size(); + } + outItr.Set(resCoefs); + } + + else + outItr.Set(nullVector); + + std::vector testSh(m_VectorLength); + for(int i = 0; i < m_VectorLength; i++) + { + testSh[i] = resCoefs[i]; + } + localDir.clear(); + localDir.shrink_to_fit(); + } + else + outItr.Set(nullVector); + + ++outItr; + + this->IncrementNumberOfProcessedPoints(); + } + +} + +void TODEstimatorImageFilter::AfterThreadedGenerateData() +{ + +} + +//void TODEstimatorImageFilter::GenerateData() +//{ + + + + + + +//} + + + + +void TODEstimatorImageFilter::getMainDirections(DirVectorType inDirs, DirVectorType &mainDirs) +{ + + if(inDirs.size() == 0) + { + mainDirs.resize(0); + return; + } + const int numIt = 200; + int numDirs = inDirs.size(), crit = 0, numClusters = 0; + + if(numDirs < 4) + { + numClusters = numDirs; + mainDirs.resize(numClusters); + for(int i = 0; i < numClusters; i++) + { + mainDirs[i] = inDirs[i]; + } + return; + } + else + numClusters = 4; + + DirVectorType clustersAverage(numClusters); + std::vector clusters(numDirs); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distrib(0, numDirs); + + + for(int i = 0; i < numClusters; i++) + { + int k = distrib(gen); + clustersAverage[i] = inDirs[k]; + } + + int It = 0; + bool stop = false; + double dist = 0; + while(It < numIt && !stop) + { + std::vector clustersOld(numDirs); + for(int i = 0; i < numDirs; i++) + clustersOld[i] = clusters[i]; + + for(int i = 0; i < numDirs; i++) + { + double minDist = 1e10; + for(int j = 0; j < numClusters; j++) + { + // double dist = getEuclideanDistance(inDirs[i], clustersAverage[j]); + // dist = std::sqrt(std::pow((clustersAverage[j][0] - inDirs[i][0]),2) + std::pow((clustersAverage[j][1] - inDirs[i][1]),2) + std::pow((clustersAverage[j][2] - inDirs[i][2]),2)); + dist = std::sqrt((clustersAverage[j][0] - inDirs[i][0]) * (clustersAverage[j][0] - inDirs[i][0]) + (clustersAverage[j][1] - inDirs[i][1]) * (clustersAverage[j][1] - inDirs[i][1]) + (clustersAverage[j][2] - inDirs[i][2]) * (clustersAverage[j][2] - inDirs[i][2])); + if(dist < minDist) + { + minDist = dist; + clusters[i] = j; + } + } + } + + crit = 0; + for(int i = 0; i < numClusters; i++) + { + clustersAverage[i] = getNewClusterAverage(i, inDirs, clusters); + } + + for(int i = 0; i < numDirs; i++) + crit += clusters[i] - clustersOld[i]; + + if(crit == 0) + stop = true; + + ++It; + } + + mainDirs.resize(numClusters); + for(int i = 0; i < numClusters; i++) + mainDirs[i] = clustersAverage[i]; + + +} + + +typename TODEstimatorImageFilter::DirType TODEstimatorImageFilter ::getNewClusterAverage(int numCluster, DirVectorType &indirs, std::vector &cluster) +{ + DirType sum, sumNorm; + DirVectorType dirs; + dirs = indirs; + for(int j = 0; j < 3; j++) + sum[j] = 0; + + int size = 0; + for(int i = 0 ; i < dirs.size(); i++) + { + if(cluster[i] == numCluster) + { + for(int j = 0; j < 3; j++) + sum[j] = sum[j] + dirs[i][j]; + ++size; + } + } + for(int j = 0; j < 3; j++) + sum[j] = sum[j]/size; + + + anima::Normalize(sum, sumNorm); + return sumNorm; +} + + +double TODEstimatorImageFilter::getEuclideanDistance(DirType dir1, DirType dir2) +{ + return std::sqrt(std::pow((dir2[0] - dir1[0]),2) + std::pow((dir2[1] - dir1[1]),2) + std::pow((dir2[2] - dir1[2]),2)); +} + + +void TODEstimatorImageFilter::getSHCoef(DirType tmpDir, VectorType &coefs) +{ + int pos = 0, T = this->GetOutput()->GetVectorLength(); + VectorType tmp, resSH, rotatedModel; + tmp.SetSize(T); + DirType dir; + this->GetOutput()->TransformLocalVectorToPhysicalVector(tmpDir, dir); + // this->GetOutput()->TransformPhysicalVectorToLocalVector(tmpDir, dir); + // dir[0] *= -1; + // dir[1] *= -1; + // dir[2] *= -1; + + for(int k = 0; k <= m_LOrder; k+=2) + { + for(int m = -k; m <= k; m ++) + { + tmp[pos] = m_GaussCoefs[pos]; + pos++; + } + } + + // if(this->GetOutput()->GetDirection()[0][0] > 0) + // dir[0] *= -1; + // if(this->GetOutput()->GetDirection()[1][1] < 0) + // dir[1] *= -1; + // if(this->GetOutput()->GetDirection()[2][2] < 0) + // dir[2] *= -1; + // itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(m_CstDir, dir); + itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(dir, m_CstDir); + + vnl_matrix rotationMatrix; + rotationMatrix.set_size(3,3); + for(int x = 0; x < 3; ++x) + for(int y = 0; y < 3; ++y) + rotationMatrix.put(x, y, itkRotationMatrix[y][x]); + + // vnl_matrix rotationMatrix = this->GetRotationMatrix(CstDir, dir); + std::vector eulerAngles; + anima::GetEulerAnglesFromRotationMatrix(rotationMatrix, eulerAngles); + + rotatedModel = tmp; + + vnl_matrix ODFRotationMatrix; + for (unsigned int l = 0;l <= m_LOrder;l += 2) + { + anima::EstimateLocalODFRotationMatrix(ODFRotationMatrix, l, eulerAngles[0],eulerAngles[1],eulerAngles[2]); + + unsigned int mBaseInd = (l*l + l + 2)/2 - l - 1; + for (unsigned int m = 0;m <= 2*l;++m) + { + rotatedModel[mBaseInd + m] = 0; + + for (unsigned int mp = 0;mp <= 2*l;++mp) + rotatedModel[mBaseInd + m] += ODFRotationMatrix(m,mp)*tmp[mBaseInd + mp]; + } + } + coefs = rotatedModel; + +} + +void TODEstimatorImageFilter::averageODFs(std::vector &vecCoefs, VectorType &resOdf) +{ + int numImages = vecCoefs.size(); + + std::vector> odfHistos(numImages); + std::vector vecSqrtCoefs(numImages); + std::vector avgSqrtHisto(m_NbSample); + VectorType avgSqrtCoefs(m_VectorLength); + + for(int i = 0; i < numImages; i++) + { + odfHistos[i].resize(m_NbSample); + + this->discretizeODF(vecCoefs[i], odfHistos[i]); + vecSqrtCoefs[i] = getSquareRootODF(odfHistos[i]); + } + + this->getAverageCoefs(vecSqrtCoefs, avgSqrtCoefs); + this->discretizeODF(avgSqrtCoefs, avgSqrtHisto); + resOdf = this->getSquareODF(avgSqrtHisto); +} + +void TODEstimatorImageFilter::precomputeSH() +{ + int k = 0; + + std::vector tmpDir(2), revDir(3); + for(int i = 0; i < m_NbSample; i++) + { + anima::TransformCartesianToSphericalCoordinates(m_SphereSampl[i], tmpDir); + int c = 0; + for(double l = 0; l <= m_LOrder; l+=2) + { + for(double m = -l; m <= l; m++) + { + m_SpherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, tmpDir[0], tmpDir[1]); + } + } + k++; + } +} + +void TODEstimatorImageFilter::discretizeODF(VectorType ODFCoefs, std::vector &ODFDiscret) +{ + int k = 0; + double resVal2 = 0; + + + std::vector tmpDir(2), revDir(3); + for(int i = 0; i < m_NbSample; i++) + { + anima::TransformCartesianToSphericalCoordinates(m_SphereSampl[i], tmpDir); + resVal2 = m_ODFSHBasis->getValueAtPosition(ODFCoefs, tmpDir[0], tmpDir[1]); + + ODFDiscret[i] = resVal2; + k++; + } +} + + + +typename TODEstimatorImageFilter::VectorType TODEstimatorImageFilter::getSquareRootODF(std::vector ODFDiscret) +{ + vnl_matrix Odf(m_NbSample, 1); + vnl_matrix Coef(m_VectorLength, 1); + + for(int i = 0; i < m_NbSample; ++i) + Odf(i, 0) = std::sqrt(ODFDiscret[i]); + + Coef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose()*Odf; + + VectorType ModelValue(m_VectorLength); + for(int i = 0; i < m_VectorLength; ++i) + { + ModelValue[i] = Coef(i, 0); + } + return ModelValue; +} + + +typename TODEstimatorImageFilter::VectorType +TODEstimatorImageFilter::getSquareODF(std::vector ODFDiscret) +{ + vnl_matrix Odf(m_NbSample, 1); + vnl_matrix Coef(m_VectorLength, 1); + + for(int i = 0; i < m_NbSample; ++i) + Odf(i, 0) = ODFDiscret[i] * ODFDiscret[i]; + + Coef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose()*Odf; + + VectorType ModelValue(m_VectorLength); + for(int i = 0; i < m_VectorLength; ++i) + { + ModelValue[i] = Coef(i, 0); + } + + ModelValue[0] = 1/(2*sqrt(M_PI)); + return ModelValue; +} + + +void +TODEstimatorImageFilter::getAverageCoefs(std::vector &vecCoefs, VectorType &avgCoef) +{ + int numImage = vecCoefs.size(); + + std::vector> arrayCoef(numImage), arrayLogMap(numImage); + std::vector expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); + + const int T = 100; + + for(int i = 0; i < numImage; i++) + { + arrayCoef[i].resize(m_VectorLength); + arrayLogMap[i].resize(m_VectorLength); + + for(int n = 0; n < m_VectorLength; n++) + { + arrayCoef[i][n] = vecCoefs[i][n]; + // nextMean[n] += 1/(double)numImage * arrayCoef[i][n]; + } + } + + nextMean = arrayCoef[0]; + + for(int t = 0; t < T; t++) + { + mean = nextMean; + + for(int i = 0; i < numImage; i++) + anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); + + std::fill(tangent.begin(), tangent.end(), 0); + for(int n = 0; n < m_VectorLength; n++) + for(int i = 0; i < numImage; i++) + tangent[n] += 1/(double)numImage * arrayLogMap[i][n]; + + anima::sphere_exp_map(tangent, mean, nextMean); + } + + // nextMean[0] = 2.82094792e-01; + for(int i = 0; i < m_VectorLength; ++i) + avgCoef[i] = nextMean[i]; +} + + + +void TODEstimatorImageFilter::processFiber(FiberType &fiber, baseSH &basis) +{ + const int T = (m_LOrder+1)*(m_LOrder+2)/2; + VectorType tmp, resSH, rotatedModel; + tmp.SetSize(T); + resSH.SetSize(T); + rotatedModel.SetSize(T); + // itk::Vector tmp, resSH; + DirType CstDir; + CstDir[0] = 0; + CstDir[1] = 0; + CstDir[2] = 1; + + + TOutputImage *output = this->GetOutput(); + int nbPoints = fiber.size(); + + for(int i = 0; i < nbPoints; i++) + { + DirType dir = getFiberDirection(i, fiber); + // OutputImagePointer::DirectionType refDir = output->GetDirection(); + + if(output->GetDirection()[0][0] > 0) + dir[0] *= -1; + if(output->GetDirection()[1][1] < 0) + dir[1] *= -1; + if(output->GetDirection()[2][2] < 0) + dir[2] *= -1; + + + + int pos = 0; + for(int k = 0; k <= m_LOrder; k+=2) + { + for(int m = -k; m <= k; m ++) + { + tmp[pos] = m_GaussCoefs[pos]; + pos++; + } + } + + itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(CstDir, dir); + vnl_matrix rotationMatrix; + rotationMatrix.set_size(3,3); + for(int x = 0; x < 3; ++x) + for(int y = 0; y < 3; ++y) + rotationMatrix.put(x, y, itkRotationMatrix[y][x]); + + // vnl_matrix rotationMatrix = this->GetRotationMatrix(CstDir, dir); + std::vector eulerAngles; + anima::GetEulerAnglesFromRotationMatrix(rotationMatrix, eulerAngles); + + rotatedModel = tmp; + + vnl_matrix ODFRotationMatrix; + for (unsigned int l = 0;l <= m_LOrder;l += 2) + { + anima::EstimateLocalODFRotationMatrix(ODFRotationMatrix, l, eulerAngles[0],eulerAngles[1],eulerAngles[2]); + + unsigned int mBaseInd = (l*l + l + 2)/2 - l - 1; + for (unsigned int m = 0;m <= 2*l;++m) + { + rotatedModel[mBaseInd + m] = 0; + + for (unsigned int mp = 0;mp <= 2*l;++mp) + rotatedModel[mBaseInd + m] += ODFRotationMatrix(m,mp)*tmp[mBaseInd + mp]; + } + } + PointType point = getCenterVoxel(i, fiber); + itk::Index<3> index; + + output->TransformPhysicalPointToIndex(point, index); + // index[0] *= -1; + // index[1] *= -1; + // resSH = (output->GetPixel(index) + tmp); + // resSH /= resSH[0]; + output->SetPixel(index, rotatedModel); + + resSH.Fill(0); + tmp.Fill(0); + rotatedModel.Fill(0); + + } +} + + +typename TODEstimatorImageFilter::DirType +TODEstimatorImageFilter::getFiberDirection(int index, FiberType &fiber) +{ + DirType resDir; + if(index == 0) + { + for(int i = 0; i < 3; i++) + resDir[i] = fiber[index+1][i] - fiber[index][i]; + } + else if(index == fiber.size() - 1) + { + for(int i = 0; i < 3; i++) + resDir[i] = fiber[index][i] - fiber[index-1][i]; + } + else + { + for(int i = 0; i < 3; i++) + resDir[i] = fiber[index-1][i] - fiber[index+1][i]; + } + + return resDir; +} + + +typename TODEstimatorImageFilter::PointType +TODEstimatorImageFilter::getCenterVoxel(int index, FiberType &fiber) +{ + PointType resPoint; + for(int i = 0; i < 3; i++) + { + resPoint[i] = fiber[index][i]; + } + + return resPoint; +} + + +typename TODEstimatorImageFilter::FiberType TODEstimatorImageFilter::readFiber(vtkIdType numberOfPoints, const vtkIdType *indices, vtkPoints *points) +{ + FiberType fiber; + // fiber.resize(numberOfPoints); + for (vtkIdType i = 0; i < numberOfPoints; i++) + { + double tmpPoint[3]; + points->GetPoint(indices[i], tmpPoint); + PointType point; + for(int i = 0; i < 3; i++) + point[i] = tmpPoint[i]; + + fiber.push_back(tmpPoint); + } + + return fiber; +} + + +vnl_matrix TODEstimatorImageFilter::GetRotationMatrix(DirType dir1, DirType dir2) +{ + DirType nu; + anima::ComputeCrossProduct(dir2, dir1, nu); + + double s = anima::ComputeNorm(nu); + double c = anima::ComputeScalarProduct(dir2, dir1); + + vnl_matrix R; + vnl_matrix MatNu; + vnl_matrix SqrMatNu; + MatNu.set_size(3,3); + MatNu.fill(0.0); + + MatNu.put(0,1,-nu[2]); + MatNu.put(0,2, nu[1]); + MatNu.put(1,0, nu[2]); + MatNu.put(1,2,-nu[0]); + MatNu.put(2,0,-nu[1]); + MatNu.put(2,1, nu[0]); + + + // MatNu[1][0] = -nu[2]; + // MatNu[2][0] = nu[1]; + // MatNu[0][1] = nu[2]; + // MatNu[2][1] = -nu[0]; + // MatNu[0][2] = -nu[1]; + // MatNu[1][2] = nu[0]; + + SqrMatNu = MatNu*MatNu; + + R.set_size(3,3); + R.set_identity(); + R = R + MatNu + SqrMatNu*(1-c)/(s*s); + // R = R + MatNu + SqrMatNu*(1/1+c); + + return R; + +} + +} // end namespace anima + From 86a1306317888e9233c393c28742ea22fb8794c6 Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 5 Oct 2023 11:33:50 +0200 Subject: [PATCH 02/11] Replace ANIMA_PRIVATE_VERSION with ANIMA_VERSION following fusion. --- Anima/diffusion/odf/odf_average/animaODFAverage.cxx | 2 +- Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx index 931806891..59f0e072e 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx +++ b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx @@ -16,7 +16,7 @@ void eventCallback (itk::Object* caller, const itk::EventObject& event, void* cl int main(int argc, char **argv) { - TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_PRIVATE_VERSION); + TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_VERSION); TCLAP::ValueArg inArg("i","input-odf","ODF images list as a text file",true,"","ODF images list",cmd); TCLAP::ValueArg maskArg("m","masks","Masks images list as a text file",true,"","Masks images list",cmd); diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx index c868a2189..2f991ddb7 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx @@ -12,9 +12,9 @@ void eventCallback (itk::Object* caller, const itk::EventObject& event, void* cl int main(int argc, char **argv) { - TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_PRIVATE_VERSION); + TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_VERSION); - TCLAP::ValueArg inArg("i","input","Input tractography image (.vtk or .vtp)",true,"","input tractography image",cmd); + TCLAP::ValueArg inArg("i","input","Input tractography image (.vtk or .vtp)",true,"","input tractography image",cmd); TCLAP::ValueArg resArg("o","outputfile","Result TOD image",true,"","result TOD image",cmd); TCLAP::ValueArg refArg("g","geometry","Output image geometry",true,"","output geometry",cmd); From 7dac8afb354576c03b1f39dcee65f1cd3d92339e Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 5 Oct 2023 11:35:41 +0200 Subject: [PATCH 03/11] Clean up CMakeLists files to avoid duplicate library linkage. --- Anima/diffusion/odf/odf_average/CMakeLists.txt | 3 --- Anima/diffusion/odf/tod_estimator/CMakeLists.txt | 1 - 2 files changed, 4 deletions(-) diff --git a/Anima/diffusion/odf/odf_average/CMakeLists.txt b/Anima/diffusion/odf/odf_average/CMakeLists.txt index f3c7c5121..8dae3df5d 100644 --- a/Anima/diffusion/odf/odf_average/CMakeLists.txt +++ b/Anima/diffusion/odf/odf_average/CMakeLists.txt @@ -18,7 +18,6 @@ add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_CFILES} ) - ## ############################################################################# ## Link ## ############################################################################# @@ -30,8 +29,6 @@ target_link_libraries(${PROJECT_NAME} ITKCommon ${VTK_PREFIX}CommonCore ${VTKSYS_LIBRARY} - ${ITKIO_LIBRARIES} - ) ## ############################################################################# diff --git a/Anima/diffusion/odf/tod_estimator/CMakeLists.txt b/Anima/diffusion/odf/tod_estimator/CMakeLists.txt index 8cbbaa67c..5b8e31076 100644 --- a/Anima/diffusion/odf/tod_estimator/CMakeLists.txt +++ b/Anima/diffusion/odf/tod_estimator/CMakeLists.txt @@ -19,7 +19,6 @@ add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_CFILES} ) - ## ############################################################################# ## Link ## ############################################################################# From 3864fe7ded1e301d63276fbd1907b39dc6b39fcb Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 5 Oct 2023 11:37:11 +0200 Subject: [PATCH 04/11] Fix bug using not logical operation in wrong way. --- .../odf/tod_estimator/animaTODEstimatorImageFilter.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx index c1c58227d..e83a81817 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx @@ -233,9 +233,9 @@ void TODEstimatorImageFilter::DynamicThreadedGenerateData(const TODEstimatorImag // index[2] *= dirZ; std::vector vecCoefs; - if(!(m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size()) == 0) + unsigned int tmpSize = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size(); + if (tmpSize != 0) { - int tmpSize = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size(); DirVectorType mainDirs; DirVectorType localDir(tmpSize); localDir = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])]; From a7e707e3128b43ac8107d466f6f0acc98307ac43 Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 5 Oct 2023 21:10:08 +0200 Subject: [PATCH 05/11] Remove useless library linkage and use naming convention from anima in odf averager. --- Anima/diffusion/odf/CMakeLists.txt | 2 +- .../odf/odf_average/animaODFAverage.cxx | 194 -------- .../animaODFAverageImageFilter.cxx | 448 ------------------ .../CMakeLists.txt | 6 +- .../animaODFAverageImages.cxx | 202 ++++++++ .../animaODFAverageImagesImageFilter.cxx | 395 +++++++++++++++ .../animaODFAverageImagesImageFilter.h} | 80 ++-- 7 files changed, 633 insertions(+), 694 deletions(-) delete mode 100644 Anima/diffusion/odf/odf_average/animaODFAverage.cxx delete mode 100644 Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx rename Anima/diffusion/odf/{odf_average => odf_average_images}/CMakeLists.txt (91%) create mode 100644 Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx create mode 100644 Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx rename Anima/diffusion/odf/{odf_average/animaODFAverageImageFilter.h => odf_average_images/animaODFAverageImagesImageFilter.h} (64%) diff --git a/Anima/diffusion/odf/CMakeLists.txt b/Anima/diffusion/odf/CMakeLists.txt index 9a7212709..d0cf7948b 100644 --- a/Anima/diffusion/odf/CMakeLists.txt +++ b/Anima/diffusion/odf/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(generalized_fa) add_subdirectory(odf_estimator) add_subdirectory(tod_estimator) -add_subdirectory(odf_average) \ No newline at end of file +add_subdirectory(odf_average_images) \ No newline at end of file diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx deleted file mode 100644 index 59f0e072e..000000000 --- a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx +++ /dev/null @@ -1,194 +0,0 @@ -#include - -#include -#include - -#include - -#include - -//Update progression of the process -void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData) -{ - itk::ProcessObject * processObject = (itk::ProcessObject*) caller; - std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"< inArg("i","input-odf","ODF images list as a text file",true,"","ODF images list",cmd); - TCLAP::ValueArg maskArg("m","masks","Masks images list as a text file",true,"","Masks images list",cmd); - - TCLAP::ValueArg resArg("o","output","Result average ODF",true,"","result ODF image",cmd); - TCLAP::ValueArg resMaskArg("M","resMask","Result average mask",false,"","average mask",cmd); - - TCLAP::ValueArg resWeightArg("w","resWeight","Flag for iterative barycenter atlasing - Result number of average per pixel",false,"","number average image",cmd); - TCLAP::ValueArg resPondArg("P","resPond","Resulting ponderation map, in case of two images averaging",false,"","ponderation map",cmd); - - TCLAP::ValueArg aicImageArg("a","aic","Input AIC image",false,"","aic image",cmd); - - TCLAP::ValueArg weightArg("W","weight","In the case of 2 image averaging, weight of the first image ",false, 0.0 ,"first image weight",cmd); - TCLAP::ValueArg testArg("t","test","test value",false, 0.0 ,"first image weight",cmd); - - TCLAP::SwitchArg gfaArg("", "gfa", "Use GFA of the atlas (first image) to ponderate",cmd,false); -// TCLAP::SwitchArg baseArg("t", "tournier", "Use the Tournier SH base (Mrtrix). If not set, use the Descoteaux set",cmd,false); - - TCLAP::ValueArg nbpArg("p","numberofthreads","Number of threads to run on (default: all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd); - - try - { - cmd.parse(argc,argv); - } - catch (TCLAP::ArgException& e) - { - std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; - return EXIT_FAILURE; - } - itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); - callback->SetCallback(eventCallback); - - std::ifstream odfFile(inArg.getValue().c_str()); - if (!odfFile.is_open()) - { - std::cerr << "Please provide usable file with input ODFs" << std::endl; - return EXIT_FAILURE; - } - - std::ifstream maskFile(maskArg.getValue().c_str()); - if (!maskFile.is_open()) - { - std::cerr << "Please provide usable file with input Masks" << std::endl; - return EXIT_FAILURE; - } - - typedef anima::ODFAverageImageFilter FilterType; - typedef FilterType::InputImageType InputImageType; - typedef FilterType::OutputImageType OutputImageType; - typedef FilterType::MaskImageType MaskImageType; - typedef FilterType::DoubleImageType DoubleImageType; - FilterType::Pointer mainFilter = FilterType::New(); - - std::vector inputFiles; - std::vector maskFiles; - unsigned int numInput = 0; - while (!odfFile.eof()) - { - char tmpStr[2048], maskStr[2048]; - odfFile.getline(tmpStr,2048); - maskFile.getline(maskStr, 2048); - - if (strcmp(tmpStr,"") == 0) - continue; - - // std::cout << "Loading image " << numInput << ": " << tmpStr << std::endl; - // std::cout << "Loading Mask " << numInput << ": " << maskStr << std::endl; - - inputFiles.push_back(tmpStr); - maskFiles.push_back(maskStr); - // mainFilter->SetInput(numInput, anima::readImage(tmpStr)); - // mainFilter->SetMask(numInput, anima::readImage(maskStr)); - numInput++; - } - odfFile.close(); - maskFile.close(); - - if(weightArg.getValue() != 0.0) - mainFilter->Setweight(weightArg.getValue()); - if(gfaArg.getValue()) - mainFilter->SetflagGFA(true); - -// if(baseArg.getValue()) -// mainFilter->SetTournier(true); - - if(aicImageArg.getValue() != "") - mainFilter->SetAicImage(anima::readImage(aicImageArg.getValue())); - mainFilter->SettestCombi(testArg.getValue()); - - MaskImageType::Pointer geomImage = anima::readImage(maskFiles[0]); - - std::cout << "Processing image : " << inputFiles[1] << std::endl; - - if(resWeightArg.getValue() != "") - { - MaskImageType::Pointer weightImage = MaskImageType::New(); - weightImage->Initialize(); - weightImage->SetDirection(geomImage->GetDirection()); - weightImage->SetSpacing(geomImage->GetSpacing()); - weightImage->SetOrigin(geomImage->GetOrigin()); - MaskImageType::RegionType region = geomImage->GetLargestPossibleRegion(); - weightImage->SetRegions(region); - weightImage->Allocate(); - weightImage->FillBuffer(0); - mainFilter->SetWeightImage(weightImage); - } - mainFilter->SetInput(0, anima::readImage(inputFiles[0])); - mainFilter->SetInput(1, anima::readImage(inputFiles[1])); - - mainFilter->SetMask(0, anima::readImage(maskFiles[0])); - mainFilter->SetMask(1, anima::readImage(maskFiles[1])); - - mainFilter->AddObserver(itk::ProgressEvent(), callback); - mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); - - itk::TimeProbe tmpTimer; - - tmpTimer.Start(); - - try - { - mainFilter->Update(); - } - catch (itk::ExceptionObject &e) - { - std::cerr << e << std::endl; - return EXIT_FAILURE; - } - - for(int i = 2; i < numInput; i++) - { - - std::cout << std::endl << "Processing image : " << inputFiles[i] << std::endl; - mainFilter->SetWeightImage(mainFilter->getWeightImage()); - - mainFilter->SetInput(0, mainFilter->GetOutput()); - mainFilter->SetInput(1, anima::readImage(inputFiles[i])); - - mainFilter->SetMask(0, mainFilter->getMaskAverage()); - mainFilter->SetMask(1, anima::readImage(maskFiles[i])); - - mainFilter->AddObserver(itk::ProgressEvent(), callback); - mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); - - try - { - mainFilter->Update(); - } - catch (itk::ExceptionObject &e) - { - std::cerr << e << std::endl; - return EXIT_FAILURE; - } - } - - tmpTimer.Stop(); - - std::cout << "\nAveraging done in " << tmpTimer.GetTotal() << " s" << std::endl; - - anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); - - if(resWeightArg.getValue() != "") - anima::writeImage(resWeightArg.getValue(), mainFilter->getWeightImage()); - - if(resMaskArg.getValue() != "") - { - MaskImageType::Pointer maskOut = mainFilter->getMaskAverage(); - anima::writeImage(resMaskArg.getValue(), maskOut.GetPointer()); - } - - if(resPondArg.getValue() != "") - anima::writeImage(resPondArg.getValue(), mainFilter->getPondImage()); - - return EXIT_SUCCESS; -} diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx deleted file mode 100644 index fe086caba..000000000 --- a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx +++ /dev/null @@ -1,448 +0,0 @@ -#include "animaODFAverageImageFilter.h" - -#include -#include "animaLogExpMapsUnitSphere.h" - -#include -#include - -namespace anima -{ - -void ODFAverageImageFilter::SetMask(unsigned int i, MaskImagePointer mask) -{ - if (i == m_Masks.size()) - m_Masks.push_back(mask); - else if (i > m_Masks.size()) - { - itkExceptionMacro("Trying to add a non contiguous mask... Add mask images contiguously (0,1,2,3,...)..."); - } - else - m_Masks[i] = mask; -} - - -void ODFAverageImageFilter::BeforeThreadedGenerateData() -{ - Superclass::BeforeThreadedGenerateData(); - - m_PondImage = DoubleImageType::New(); - m_PondImage->Initialize(); - m_PondImage->SetDirection(m_Masks[0]->GetDirection()); - m_PondImage->SetSpacing(m_Masks[0]->GetSpacing()); - m_PondImage->SetOrigin(m_Masks[0]->GetOrigin()); - MaskImageType::RegionType region = m_Masks[0]->GetLargestPossibleRegion(); - m_PondImage->SetRegions(region); - m_PondImage->Allocate(); - m_PondImage->FillBuffer(0); - - // if(m_AicImage) - // { - // itk::ImageRegionIterator aicIt(m_AicImage, m_AicImage->GetLargestPossibleRegion()); - // m_minAic = 1e10; - - // while (!aicIt.IsAtEnd()) - // { - // double tmpVal = aicIt.Get(); - // if(tmpVal < m_minAic && tmpVal > 1000) - // m_minAic = tmpVal; - // ++aicIt; - // } - // } - - m_minAic = 10; - - m_histoODFs.resize(this->GetNumberOfIndexedInputs()); - m_SQRTHistoODFs.resize(this->GetNumberOfIndexedInputs()); - - for(int i = 0; i < this->GetNumberOfIndexedInputs(); i++) - { - m_histoODFs[i].resize(m_nbSamplesPhi * m_nbSamplesTheta); - m_SQRTHistoODFs[i].resize(m_nbSamplesPhi * m_nbSamplesTheta); - } - - m_ODFSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8 * this->GetInput(0)->GetVectorLength() + 1)); - m_vectorLength = (m_ODFSHOrder + 1)*(m_ODFSHOrder + 2)/2; - - m_spherHarm.set_size(m_nbSamplesPhi * m_nbSamplesTheta, m_vectorLength); - m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_ODFSHOrder); - - this->discretizeSH(); - -} -void ODFAverageImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) -{ - typedef itk::ImageRegionConstIterator ImageIteratorType; - typedef itk::ImageRegionConstIterator MaskIteratorType; - typedef itk::ImageRegionConstIterator DoubleIteratorType; - - static double progress = 0; - double weight0 = 0.5, weight1 = 0.5; - - unsigned int numInputs = this->GetNumberOfIndexedInputs(); - std::vector inIterators(numInputs); - std::vector maskIterators(numInputs); - for (unsigned int i = 0;i < numInputs;++i) - { - inIterators[i] = ImageIteratorType(this->GetInput(i),outputRegionForThread); - maskIterators[i] = MaskIteratorType(m_Masks[i], outputRegionForThread); - } - typedef itk::ImageRegionIterator OutImageIteratorType; - OutImageIteratorType outIterator(this->GetOutput(),outputRegionForThread); - - itk::ImageRegionIterator weightIt; - if(m_WeightImage) - weightIt = itk::ImageRegionIterator(m_WeightImage, outputRegionForThread); - - itk::ImageRegionIterator aicIt; - if(m_AicImage) - aicIt = itk::ImageRegionIterator (m_AicImage, outputRegionForThread); - - itk::ImageRegionIterator pondIt(m_PondImage, outputRegionForThread); - - std::vector arrayCoef(numInputs), arraySQRTCoef(numInputs); - - VectorType nullVector(m_vectorLength); - nullVector.Fill(0.0); - - while(!outIterator.IsAtEnd()) - { - if(maskIterators[0].Get() == 0 && maskIterators[1].Get() == 0) - outIterator.Set(nullVector); - - else if(maskIterators[0].Get() == 1 && maskIterators[1].Get() == 0) - { - outIterator.Set(inIterators[0].Get()); - } - else if(maskIterators[0].Get() == 0 && maskIterators[1].Get() == 1) - { - outIterator.Set(inIterators[1].Get()); - } - else - { - for(int i = 0; i < numInputs; i++) - { - arrayCoef[i] = inIterators[i].Get(); - this->discretizeODF(arrayCoef[i], m_histoODFs[i]); - arraySQRTCoef[i] = this->getSquareRootODFCoef(m_histoODFs[i]); - } - - if(m_WeightImage) - { - double k = weightIt.Get(); - weight1 = (k+1) / (k+2); - weight0 = 1 - weight1; - - weightIt.Set(weightIt.Get() + 1); - } - - else if(m_weight != 0.0) - { - weight0 = m_weight; - weight1 = 1 - weight1; - } - - else if(m_flagGFA && m_AicImage) - { - double tmpAic = aicIt.Get(); - double aic; - if(tmpAic == 0) - aic = 0.0; - else - aic = std::exp((m_minAic - tmpAic) / 2000); - double gfa = 0.8*this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); - - // weight0 = (m_testCombi*gfa + (1-m_testCombi)*aic) / (gfa+aic); - weight0 = std::exp((m_testCombi*gfa + (1-m_testCombi)*aic) - (m_testCombi + (1-m_testCombi))); - weight1 = 1 - weight0; - - pondIt.Set(weight0); - } - - else if(m_flagGFA) - { - double gfa = this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); - weight0 = 1*(1-gfa); - weight1 = 1 - weight0; - pondIt.Set(weight0); - if(weight0 == 0) - auto test = outIterator.GetIndex(); - } - - else if(m_AicImage) - { - double aic = aicIt.Get(); - if(aic == 0) - weight1 = 1; - else - weight1 = std::exp((m_minAic - aic) / 1); - - weight0 = 1 - weight1; - pondIt.Set(weight0); - if(weight0 == 0) - { - auto test = outIterator.GetIndex(); - } - } - - VectorType averageSQRTCoef(m_vectorLength); - this->getAverageHisto(arraySQRTCoef, averageSQRTCoef, weight0, weight1); - - std::vector averageSQRTHisto(m_nbSamplesPhi*m_nbSamplesTheta); - this->discretizeODF(averageSQRTCoef, averageSQRTHisto); - - VectorType averageCoef(m_vectorLength); - averageCoef = this->getSquareODFCoef(averageSQRTHisto); - - outIterator.Set(averageCoef); - } - - for (unsigned int i = 0;i < numInputs;++i) - { - ++inIterators[i]; - ++maskIterators[i]; - } - - if(m_WeightImage) - ++weightIt; - - if(m_AicImage) - ++aicIt; - - ++outIterator; - ++pondIt; - this->IncrementNumberOfProcessedPoints(); - } -} - - -void ODFAverageImageFilter::AfterThreadedGenerateData() -{ - delete m_ODFSHBasis; -} - -void ODFAverageImageFilter::discretizeSH() -{ - double sqrt2 = std::sqrt(2); - double theta, phi; - double deltaPhi = 2*M_PI/(double)(m_nbSamplesPhi - 1); - double deltaTheta = M_PI/(double)(m_nbSamplesTheta - 1); - - int k = 0; - for(int i = 0; i < m_nbSamplesTheta; i++) - { - theta = (double)i * deltaTheta; - - for(int j = 0; j < m_nbSamplesPhi; j++) - { - phi = (double)j*deltaPhi; - int c = 0; - for(double l = 0; l <= m_ODFSHOrder; l+=2) - { - for(double m = -l; m <= l; m++) - { - // if(m_Tournier) - // m_spherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPositionTournier(l, m, theta, phi); - // else - m_spherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi); - } - } - k++; - } - } -} - -void ODFAverageImageFilter::discretizeODF(VectorType &modelValue, std::vector &odf) -{ - int flag = 0, k = 0; - double theta, phi; - double resVal2 = 0; - - double deltaPhi = 2*M_PI/(double)(m_nbSamplesPhi - 1); - double deltaTheta = M_PI/(double)(m_nbSamplesTheta - 1); - - for(int i = 0; i < m_nbSamplesTheta; ++i) - { - for(int j = 0; j < m_nbSamplesPhi; j++) - { - - phi = (double)j * deltaPhi; - theta = (double)i * deltaTheta; - - // if(m_Tournier) - // resVal2 = m_ODFSHBasis->getValueAtPositionTournier(modelValue, theta, phi); - // else - resVal2 = m_ODFSHBasis->getValueAtPosition(modelValue, theta, phi); - - if(resVal2 < 0) - flag++; - - odf[k] = resVal2; - k++; - } - } -} - -ODFAverageImageFilter::VectorType ODFAverageImageFilter::getSquareRootODFCoef(std::vector &odf) -{ - vnl_matrix squareRootOdf(m_nbSamplesTheta * m_nbSamplesPhi, 1); - vnl_matrix squareRootCoef(m_vectorLength, 1); - - for(int i = 0; i < m_nbSamplesTheta * m_nbSamplesPhi; ++i) - { - if(odf[i] < 0) - odf[i] = 0; - squareRootOdf(i, 0) = std::sqrt(odf[i]); - } - squareRootCoef = vnl_matrix_inverse(m_spherHarm.transpose() * m_spherHarm).as_matrix() * m_spherHarm.transpose()*squareRootOdf; - - VectorType squareRootModelValue(m_vectorLength); - std::vector test(m_vectorLength); - for(int i = 0; i < m_vectorLength; ++i) - { - squareRootModelValue[i] = squareRootCoef(i, 0); - test[i] = squareRootCoef(i, 0); - } - return squareRootModelValue; -} - -ODFAverageImageFilter::VectorType ODFAverageImageFilter::getSquareODFCoef(std::vector &odf) -{ - vnl_matrix squareOdf(m_nbSamplesTheta * m_nbSamplesPhi, 1); - vnl_matrix squareCoef(m_vectorLength, 1); - - for(int i = 0; i < m_nbSamplesTheta * m_nbSamplesPhi; ++i) - squareOdf(i, 0) = std::pow(odf[i], 2); - - squareCoef = vnl_matrix_inverse(m_spherHarm.transpose() * m_spherHarm).as_matrix() * m_spherHarm.transpose()*squareOdf; - - VectorType squareModelValue(m_vectorLength); - std::vector test(m_vectorLength); - for(int i = 0; i < m_vectorLength; ++i) - { - squareModelValue[i] = squareCoef(i, 0); - } - -// long double integralODF = 0; - -// for (unsigned int i = 0;i < m_spherHarm.rows(); ++i) -// for (unsigned int j = 0;j < m_vectorLength;++j) -// integralODF += m_spherHarm[i][j] * squareModelValue[j]; - -// for (unsigned int i = 0;i < m_vectorLength;++i) -// squareModelValue[i] /= integralODF; - - squareModelValue[0] = 1/(2*sqrt(M_PI)); - return squareModelValue; -} - -void ODFAverageImageFilter::getAverageHisto(std::vector &coefs, ODFAverageImageFilter::VectorType &resCoef, double weight0, double weight1) -{ - int numImage = this->GetNumberOfIndexedInputs(); - - std::vector> arrayCoef(numImage), arrayLogMap(numImage); - std::vector expMap(m_vectorLength), mean(m_vectorLength), nextMean(m_vectorLength), tangent(m_vectorLength); - - const int T = 100; - const double eps = 0.000035; - - for(int i = 0; i < numImage; i++) - { - arrayCoef[i].resize(m_vectorLength); - arrayLogMap[i].resize(m_vectorLength); - - for(int n = 0; n < m_vectorLength; n++) - { - arrayCoef[i][n] = coefs[i][n]; - // nextMean[n] += 1/(double)numImage * arrayCoef[i][n]; - } - } - - nextMean = arrayCoef[0]; - - int t = 0; - double normTan = 1; - while(t < T && normTan > eps) - { - mean = nextMean; - - for(int i = 0; i < numImage; i++) - anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); - - std::fill(tangent.begin(), tangent.end(), 0); - for(int n = 0; n < m_vectorLength; n++) - tangent[n] += weight1 * arrayLogMap[1][n] + weight0 * arrayLogMap[0][n]; - - normTan = anima::ComputeNorm(tangent); - anima::sphere_exp_map(tangent, mean, nextMean); - - t++; - } - - // nextMean[0] = 2.82094792e-01; - for(int i = 0; i < m_vectorLength; ++i) - resCoef[i] = nextMean[i]; -} - -void ODFAverageImageFilter::getAverageODF(std::vector> &histos, ODFAverageImageFilter::VectorType &resCoef, double smallWeight, double bigWeight) -{ - vnl_matrix meanODF(m_nbSamplesTheta * m_nbSamplesPhi, 1); - vnl_matrix meanCoef(m_vectorLength, 1); - - for(int i = 0; i < m_nbSamplesTheta * m_nbSamplesPhi; ++i) - meanODF(i, 0) = (histos[0][i] + histos[1][i]) / 2.0; - - meanCoef = vnl_matrix_inverse(m_spherHarm.transpose() * m_spherHarm).as_matrix() * m_spherHarm.transpose()*meanODF; - - std::vector test(m_vectorLength); - meanCoef(0, 0) = 2.82094792e-01; - for(int i = 0; i < m_vectorLength; ++i) - { - resCoef[i] = meanCoef(i, 0); - } - -} - -ODFAverageImageFilter::MaskImagePointer ODFAverageImageFilter::getMaskAverage() -{ - MaskImageType::Pointer maskAverage = MaskImageType::New(); - maskAverage->Initialize(); - maskAverage->SetDirection(m_Masks[0]->GetDirection()); - maskAverage->SetSpacing(m_Masks[0]->GetSpacing()); - maskAverage->SetOrigin(m_Masks[0]->GetOrigin()); - MaskImageType::RegionType region = m_Masks[0]->GetLargestPossibleRegion(); - maskAverage->SetRegions(region); - maskAverage->Allocate(); - maskAverage->FillBuffer(0); - - typedef itk::ImageRegionConstIterator MaskIteratorType; - - itk::ImageRegionIterator averageIt(maskAverage, maskAverage->GetLargestPossibleRegion()); - MaskIteratorType mask0It(m_Masks[0], m_Masks[0]->GetLargestPossibleRegion()); - MaskIteratorType mask1It(m_Masks[1], m_Masks[1]->GetLargestPossibleRegion()); - - while(!averageIt.IsAtEnd()) - { - averageIt.Set(mask0It.Get() || mask1It.Get()); - - ++averageIt; - ++mask0It; - ++mask1It; - } - - m_Masks.clear(); - return maskAverage; -} - -double ODFAverageImageFilter::GetGeneralizedFractionalAnisotropy(ODFAverageImageFilter::VectorType &modelValue) -{ - double sumSquares = 0; - for (unsigned int i = 0;i < this->m_vectorLength;++i) - sumSquares += modelValue[i]*modelValue[i]; - - return std::sqrt(1.0 - modelValue[0]*modelValue[0]/sumSquares); - -} - - -} // end namespace anima diff --git a/Anima/diffusion/odf/odf_average/CMakeLists.txt b/Anima/diffusion/odf/odf_average_images/CMakeLists.txt similarity index 91% rename from Anima/diffusion/odf/odf_average/CMakeLists.txt rename to Anima/diffusion/odf/odf_average_images/CMakeLists.txt index 8dae3df5d..14ad1dae1 100644 --- a/Anima/diffusion/odf/odf_average/CMakeLists.txt +++ b/Anima/diffusion/odf/odf_average_images/CMakeLists.txt @@ -1,6 +1,6 @@ if(BUILD_TOOLS) -project(animaODFAverage) +project(animaODFAverageImages) ## ############################################################################# ## List Sources @@ -25,10 +25,6 @@ add_executable(${PROJECT_NAME} target_link_libraries(${PROJECT_NAME} ${ITKIO_LIBRARIES} AnimaSHTools - AnimaDataIO - ITKCommon - ${VTK_PREFIX}CommonCore - ${VTKSYS_LIBRARY} ) ## ############################################################################# diff --git a/Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx b/Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx new file mode 100644 index 000000000..e0a708df5 --- /dev/null +++ b/Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx @@ -0,0 +1,202 @@ +#include + +#include +#include + +#include + +#include + +//Update progression of the process +void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData) +{ + itk::ProcessObject * processObject = (itk::ProcessObject*) caller; + std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"< inArg("i", "input-odfs", + "A text file listing the names of the ODF images.", + true, "", "ODF image list", cmd); + TCLAP::ValueArg maskArg("m", "input-masks", + "A text file listing the names of the mask images.", + true, "", "Mask image list", cmd); + TCLAP::ValueArg resArg("o", "output-average-odf", + "The name of the file where the average ODF image will be written.", + true, "", "average ODF image", cmd); + TCLAP::ValueArg resMaskArg("", "output-average-mask", + "The name of the file where the average mask image will be written.", + false, "", "average mask image", cmd); + TCLAP::ValueArg resWeightArg("", "output-weight", + "The name of the file where the weight image will be written.", + false, "", "weight image", cmd); + TCLAP::ValueArg resPondArg("", "output-ponderation", + "The name of the file where the ponderation image will be written.", + false, "", "ponderation image", cmd); + TCLAP::ValueArg aicImageArg("", "input-aic-image", + "The name of the input AIC image.", + false, "", "aic image", cmd); + TCLAP::ValueArg weightArg("", "input-weight-image", + "The name of the inpuy first image weight.", + false, 0.0, "first image weight as image",cmd); + TCLAP::ValueArg testArg("", "input-weight-value", + "A number specifying a global weight for the first image.", + false, 0.0, "first image weight as scalar", cmd); + TCLAP::SwitchArg gfaArg("", "use-gfa", + "Activates the use of GFA of the atlas (first image) to ponderate", + cmd, false); + TCLAP::ValueArg nbpArg("", "nthreads", + "An integer specifying the number of threads to run on (default: all cores).", + false, itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(), "number of threads", cmd); + + try + { + cmd.parse(argc,argv); + } + catch (TCLAP::ArgException& e) + { + std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; + return EXIT_FAILURE; + } + + itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); + callback->SetCallback(eventCallback); + + std::ifstream odfFile(inArg.getValue().c_str()); + if (!odfFile.is_open()) + { + std::cerr << "Please provide usable file with input ODFs" << std::endl; + return EXIT_FAILURE; + } + + std::ifstream maskFile(maskArg.getValue().c_str()); + if (!maskFile.is_open()) + { + std::cerr << "Please provide usable file with input Masks" << std::endl; + return EXIT_FAILURE; + } + + using FilterType = anima::ODFAverageImageFilter; + using InputImageType = FilterType::InputImageType; + using OutputImageType = FilterType::OutputImageType; + using MaskImageType = FilterType::MaskImageType; + using DoubleImageType = FilterType::DoubleImageType; + + FilterType::Pointer mainFilter = FilterType::New(); + + std::vector inputFiles; + std::vector maskFiles; + unsigned int numInput = 0; + while (!odfFile.eof()) + { + char tmpStr[2048], maskStr[2048]; + odfFile.getline(tmpStr,2048); + maskFile.getline(maskStr, 2048); + + if (strcmp(tmpStr,"") == 0) + continue; + + inputFiles.push_back(tmpStr); + maskFiles.push_back(maskStr); + numInput++; + } + odfFile.close(); + maskFile.close(); + + if (weightArg.getValue() != 0.0) + mainFilter->SetWeight(weightArg.getValue()); + + mainFilter->SetUseGFA(gfaArg.isSet()); + + if(aicImageArg.getValue() != "") + mainFilter->SetAICImage(anima::readImage(aicImageArg.getValue())); + mainFilter->SetTestCombi(testArg.getValue()); + + MaskImageType::Pointer geomImage = anima::readImage(maskFiles[0]); + + std::cout << "Processing image : " << inputFiles[1] << std::endl; + + if (resWeightArg.getValue() != "") + { + MaskImageType::Pointer weightImage = MaskImageType::New(); + weightImage->Initialize(); + weightImage->SetDirection(geomImage->GetDirection()); + weightImage->SetSpacing(geomImage->GetSpacing()); + weightImage->SetOrigin(geomImage->GetOrigin()); + MaskImageType::RegionType region = geomImage->GetLargestPossibleRegion(); + weightImage->SetRegions(region); + weightImage->Allocate(); + weightImage->FillBuffer(0); + mainFilter->SetWeightImage(weightImage); + } + mainFilter->SetInput(0, anima::readImage(inputFiles[0])); + mainFilter->SetInput(1, anima::readImage(inputFiles[1])); + + mainFilter->SetMaskImage(0, anima::readImage(maskFiles[0])); + mainFilter->SetMaskImage(1, anima::readImage(maskFiles[1])); + + mainFilter->AddObserver(itk::ProgressEvent(), callback); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + itk::TimeProbe tmpTimer; + + tmpTimer.Start(); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + + for (unsigned int i = 2;i < numInput;++i) + { + std::cout << std::endl << "Processing image : " << inputFiles[i] << std::endl; + mainFilter->SetWeightImage(mainFilter->GetWeightImage()); + + mainFilter->SetInput(0, mainFilter->GetOutput()); + mainFilter->SetInput(1, anima::readImage(inputFiles[i])); + + mainFilter->SetMaskImage(0, mainFilter->GetAverageMaskImage()); + mainFilter->SetMaskImage(1, anima::readImage(maskFiles[i])); + + mainFilter->AddObserver(itk::ProgressEvent(), callback); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + } + + tmpTimer.Stop(); + + std::cout << "\nAveraging done in " << tmpTimer.GetTotal() << " s" << std::endl; + + anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); + + if (resWeightArg.getValue() != "") + anima::writeImage(resWeightArg.getValue(), mainFilter->GetWeightImage()); + + if (resMaskArg.getValue() != "") + { + MaskImageType::Pointer maskOut = mainFilter->GetAverageMaskImage(); + anima::writeImage(resMaskArg.getValue(), maskOut.GetPointer()); + } + + if (resPondArg.getValue() != "") + anima::writeImage(resPondArg.getValue(), mainFilter->GetPonderationImage()); + + return EXIT_SUCCESS; +} diff --git a/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx b/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx new file mode 100644 index 000000000..da67e5413 --- /dev/null +++ b/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx @@ -0,0 +1,395 @@ +#include "animaODFAverageImagesImageFilter.h" + +#include +#include "animaLogExpMapsUnitSphere.h" + +#include +#include + +namespace anima +{ + +void ODFAverageImageFilter::SetMaskImage(unsigned int i, MaskImagePointer mask) +{ + if (i == m_MaskImages.size()) + m_MaskImages.push_back(mask); + else if (i > m_MaskImages.size()) + { + itkExceptionMacro("Trying to add a non contiguous mask... Add mask images contiguously (0,1,2,3,...)..."); + } + else + m_MaskImages[i] = mask; +} + + +void ODFAverageImageFilter::BeforeThreadedGenerateData() +{ + Superclass::BeforeThreadedGenerateData(); + + m_PonderationImage = DoubleImageType::New(); + m_PonderationImage->Initialize(); + m_PonderationImage->SetDirection(m_MaskImages[0]->GetDirection()); + m_PonderationImage->SetSpacing(m_MaskImages[0]->GetSpacing()); + m_PonderationImage->SetOrigin(m_MaskImages[0]->GetOrigin()); + MaskImageType::RegionType region = m_MaskImages[0]->GetLargestPossibleRegion(); + m_PonderationImage->SetRegions(region); + m_PonderationImage->Allocate(); + m_PonderationImage->FillBuffer(0); + + m_MinAICValue = 10.0; + + m_HistoODFs.resize(this->GetNumberOfIndexedInputs()); + m_SqrtHistoODFs.resize(this->GetNumberOfIndexedInputs()); + + for (unsigned int i = 0;i < this->GetNumberOfIndexedInputs();++i) + { + m_HistoODFs[i].resize(m_PhiGridSize * m_ThetaGridSize); + m_SqrtHistoODFs[i].resize(m_PhiGridSize * m_ThetaGridSize); + } + + m_ODFSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8 * this->GetInput(0)->GetVectorLength() + 1)); + m_VectorLength = (m_ODFSHOrder + 1) * (m_ODFSHOrder + 2) / 2; + + m_SHValues.set_size(m_PhiGridSize * m_ThetaGridSize, m_VectorLength); + m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_ODFSHOrder); + + this->DiscretizeSH(); + +} +void ODFAverageImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) +{ + typedef itk::ImageRegionConstIterator ImageIteratorType; + typedef itk::ImageRegionConstIterator MaskIteratorType; + typedef itk::ImageRegionConstIterator DoubleIteratorType; + + static double progress = 0; + double weight0 = 0.5, weight1 = 0.5; + + unsigned int numInputs = this->GetNumberOfIndexedInputs(); + std::vector inIterators(numInputs); + std::vector maskIterators(numInputs); + for (unsigned int i = 0;i < numInputs;++i) + { + inIterators[i] = ImageIteratorType(this->GetInput(i),outputRegionForThread); + maskIterators[i] = MaskIteratorType(m_MaskImages[i], outputRegionForThread); + } + typedef itk::ImageRegionIterator OutImageIteratorType; + OutImageIteratorType outIterator(this->GetOutput(),outputRegionForThread); + + itk::ImageRegionIterator weightIt; + if (m_WeightImage) + weightIt = itk::ImageRegionIterator(m_WeightImage, outputRegionForThread); + + itk::ImageRegionIterator aicIt; + if (m_AICImage) + aicIt = itk::ImageRegionIterator (m_AICImage, outputRegionForThread); + + itk::ImageRegionIterator pondIt(m_PonderationImage, outputRegionForThread); + + std::vector arrayCoef(numInputs), arraySQRTCoef(numInputs); + + VectorType nullVector(m_VectorLength); + nullVector.Fill(0.0); + + while(!outIterator.IsAtEnd()) + { + if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 0) + outIterator.Set(nullVector); + else if (maskIterators[0].Get() == 1 && maskIterators[1].Get() == 0) + outIterator.Set(inIterators[0].Get()); + else if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 1) + outIterator.Set(inIterators[1].Get()); + else + { + for (unsigned int i = 0;i < numInputs;++i) + { + arrayCoef[i] = inIterators[i].Get(); + this->DiscretizeODF(arrayCoef[i], m_HistoODFs[i]); + arraySQRTCoef[i] = this->GetSquareRootODFCoef(m_HistoODFs[i]); + } + + if (m_WeightImage) + { + double k = weightIt.Get(); + weight1 = (k + 1.0) / (k + 2.0); + weight0 = 1.0 - weight1; + weightIt.Set(weightIt.Get() + 1.0); + } + else if (m_Weight != 0.0) + { + weight0 = m_Weight; + weight1 = 1 - weight1; // AST: weight0 ? + } + else if (m_UseGFA && m_AICImage) + { + double tmpAic = aicIt.Get(); + double aic; + if(tmpAic == 0.0) + aic = 0.0; + else + aic = std::exp((m_MinAICValue - tmpAic) / 2000.0); + double gfa = 0.8*this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); + + weight0 = std::exp((m_TestCombi * gfa + (1.0 - m_TestCombi) * aic) - (m_TestCombi + (1.0 - m_TestCombi))); + weight1 = 1.0 - weight0; + + pondIt.Set(weight0); + } + else if (m_UseGFA) + { + double gfa = this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); + weight0 = 1.0 - gfa; + weight1 = 1.0 - weight0; + pondIt.Set(weight0); + if (weight0 == 0.0) + auto test = outIterator.GetIndex(); + } + else if (m_AICImage) + { + double aic = aicIt.Get(); + if(aic == 0) + weight1 = 1.0; + else + weight1 = std::exp((m_MinAICValue - aic) / 1); + + weight0 = 1.0 - weight1; + pondIt.Set(weight0); + if (weight0 == 0.0) + auto test = outIterator.GetIndex(); + } + + VectorType averageSQRTCoef(m_VectorLength); + this->GetAverageHisto(arraySQRTCoef, averageSQRTCoef, weight0, weight1); + + std::vector averageSQRTHisto(m_PhiGridSize * m_ThetaGridSize); + this->DiscretizeODF(averageSQRTCoef, averageSQRTHisto); + + VectorType averageCoef(m_VectorLength); + averageCoef = this->GetSquareODFCoef(averageSQRTHisto); + + outIterator.Set(averageCoef); + } + + for (unsigned int i = 0;i < numInputs;++i) + { + ++inIterators[i]; + ++maskIterators[i]; + } + + if (m_WeightImage) + ++weightIt; + + if (m_AICImage) + ++aicIt; + + ++outIterator; + ++pondIt; + + this->IncrementNumberOfProcessedPoints(); + } +} + + +void ODFAverageImageFilter::AfterThreadedGenerateData() +{ + delete m_ODFSHBasis; +} + +void ODFAverageImageFilter::DiscretizeSH() +{ + double sqrt2 = std::sqrt(2); + double theta, phi; + double deltaPhi = 2.0 * M_PI / static_cast(m_PhiGridSize - 1.0); + double deltaTheta = M_PI / static_cast(m_ThetaGridSize - 1.0); + + unsigned int k = 0; + for (unsigned int i = 0;i < m_ThetaGridSize;++i) + { + theta = static_cast(i) * deltaTheta; + + for (unsigned int j = 0;j < m_PhiGridSize;++j) + { + phi = static_cast(j) * deltaPhi; + unsigned int c = 0; + for (int l = 0;l <= m_ODFSHOrder;l+=2) + { + for (int m = -l;m <= l;++m) + m_SHValues(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi); + } + k++; + } + } +} + +void ODFAverageImageFilter::DiscretizeODF(VectorType &modelValue, std::vector &odf) +{ + unsigned int flag = 0, k = 0; + double theta, phi; + double resVal2 = 0.0; + + double deltaPhi = 2.0 * M_PI / static_cast(m_PhiGridSize - 1.0); + double deltaTheta = M_PI / static_cast(m_ThetaGridSize - 1.0); + + for (unsigned int i = 0;i < m_ThetaGridSize;++i) + { + for (unsigned int j = 0;j < m_PhiGridSize;++j) + { + phi = static_cast(j) * deltaPhi; + theta = static_cast(i) * deltaTheta; + resVal2 = m_ODFSHBasis->getValueAtPosition(modelValue, theta, phi); + + if (resVal2 < 0) + flag++; + + odf[k] = resVal2; + k++; + } + } +} + +ODFAverageImageFilter::VectorType ODFAverageImageFilter::GetSquareRootODFCoef(std::vector &odf) +{ + vnl_matrix squareRootOdf(m_ThetaGridSize * m_PhiGridSize, 1.0); + vnl_matrix squareRootCoef(m_VectorLength, 1.0); + + for (unsigned int i = 0;i < m_ThetaGridSize * m_PhiGridSize;++i) + { + if (odf[i] < 0) + odf[i] = 0; + squareRootOdf(i, 0) = std::sqrt(odf[i]); + } + squareRootCoef = vnl_matrix_inverse(m_SHValues.transpose() * m_SHValues).as_matrix() * m_SHValues.transpose() * squareRootOdf; + + VectorType squareRootModelValue(m_VectorLength); + std::vector test(m_VectorLength); + for (unsigned int i = 0;i < m_VectorLength;++i) + { + squareRootModelValue[i] = squareRootCoef(i, 0); + test[i] = squareRootCoef(i, 0); + } + + return squareRootModelValue; +} + +ODFAverageImageFilter::VectorType ODFAverageImageFilter::GetSquareODFCoef(std::vector &odf) +{ + vnl_matrix squareOdf(m_ThetaGridSize * m_PhiGridSize, 1.0); + vnl_matrix squareCoef(m_VectorLength, 1.0); + + for (unsigned int i = 0;i < m_ThetaGridSize * m_PhiGridSize;++i) + squareOdf(i, 0) = std::pow(odf[i], 2.0); + + squareCoef = vnl_matrix_inverse(m_SHValues.transpose() * m_SHValues).as_matrix() * m_SHValues.transpose() * squareOdf; + + VectorType squareModelValue(m_VectorLength); + std::vector test(m_VectorLength); + for (unsigned int i = 0;i < m_VectorLength;++i) + squareModelValue[i] = squareCoef(i, 0); + + squareModelValue[0] = 1.0 / (2.0 * sqrt(M_PI)); + + return squareModelValue; +} + +void ODFAverageImageFilter::GetAverageHisto(std::vector &coefs, ODFAverageImageFilter::VectorType &resCoef, double weight0, double weight1) +{ + unsigned int numImage = this->GetNumberOfIndexedInputs(); + + std::vector> arrayCoef(numImage), arrayLogMap(numImage); + std::vector expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); + + const int T = 100; + const double eps = 0.000035; + + for (unsigned int i = 0;i < numImage;++i) + { + arrayCoef[i].resize(m_VectorLength); + arrayLogMap[i].resize(m_VectorLength); + + for (unsigned int n = 0;n < m_VectorLength;++n) + arrayCoef[i][n] = coefs[i][n]; + } + + nextMean = arrayCoef[0]; + + unsigned int t = 0; + double normTan = 1.0; + while(t < T && normTan > eps) + { + mean = nextMean; + + for (unsigned int i = 0;i < numImage;++i) + anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); + + std::fill(tangent.begin(), tangent.end(), 0.0); + for (unsigned int n = 0;n < m_VectorLength;++n) + tangent[n] += weight1 * arrayLogMap[1][n] + weight0 * arrayLogMap[0][n]; + + normTan = anima::ComputeNorm(tangent); + anima::sphere_exp_map(tangent, mean, nextMean); + + t++; + } + + for (unsigned int i = 0;i < m_VectorLength;++i) + resCoef[i] = nextMean[i]; +} + +void ODFAverageImageFilter::GetAverageODF(std::vector> &histos, ODFAverageImageFilter::VectorType &resCoef, double smallWeight, double bigWeight) +{ + vnl_matrix meanODF(m_ThetaGridSize * m_PhiGridSize, 1.0); + vnl_matrix meanCoef(m_VectorLength, 1.0); + + for (unsigned int i = 0;i < m_ThetaGridSize * m_PhiGridSize;++i) + meanODF(i, 0) = (histos[0][i] + histos[1][i]) / 2.0; + + meanCoef = vnl_matrix_inverse(m_SHValues.transpose() * m_SHValues).as_matrix() * m_SHValues.transpose() * meanODF; + + std::vector test(m_VectorLength); + meanCoef(0, 0) = 2.82094792e-01; + for (unsigned int i = 0;i < m_VectorLength;++i) + resCoef[i] = meanCoef(i, 0); +} + +ODFAverageImageFilter::MaskImagePointer ODFAverageImageFilter::GetAverageMaskImage() +{ + MaskImageType::Pointer maskAverage = MaskImageType::New(); + maskAverage->Initialize(); + maskAverage->SetDirection(m_MaskImages[0]->GetDirection()); + maskAverage->SetSpacing(m_MaskImages[0]->GetSpacing()); + maskAverage->SetOrigin(m_MaskImages[0]->GetOrigin()); + MaskImageType::RegionType region = m_MaskImages[0]->GetLargestPossibleRegion(); + maskAverage->SetRegions(region); + maskAverage->Allocate(); + maskAverage->FillBuffer(0); + + typedef itk::ImageRegionConstIterator MaskIteratorType; + + itk::ImageRegionIterator averageIt(maskAverage, maskAverage->GetLargestPossibleRegion()); + MaskIteratorType mask0It(m_MaskImages[0], m_MaskImages[0]->GetLargestPossibleRegion()); + MaskIteratorType mask1It(m_MaskImages[1], m_MaskImages[1]->GetLargestPossibleRegion()); + + while(!averageIt.IsAtEnd()) + { + averageIt.Set(mask0It.Get() || mask1It.Get()); + + ++averageIt; + ++mask0It; + ++mask1It; + } + + m_MaskImages.clear(); + return maskAverage; +} + +double ODFAverageImageFilter::GetGeneralizedFractionalAnisotropy(ODFAverageImageFilter::VectorType &modelValue) +{ + double sumSquares = 0; + for (unsigned int i = 0;i < this->m_VectorLength;++i) + sumSquares += modelValue[i] * modelValue[i]; + + return std::sqrt(1.0 - modelValue[0] * modelValue[0] / sumSquares); + +} + +} // end namespace anima diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h b/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h similarity index 64% rename from Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h rename to Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h index 38f3b76b5..469147de7 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h +++ b/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h @@ -13,7 +13,6 @@ #include - namespace anima { class ODFAverageImageFilter : @@ -51,80 +50,69 @@ public anima::NumberedThreadImageToImageFilter < itk::VectorImage, itk typedef itk::VariableLengthVector VectorType; typedef std::vector> HistoArrayType; - void SetMask(unsigned int i, MaskImagePointer mask); + void SetMaskImage(unsigned int i, MaskImagePointer mask); - void SetWeightImage(MaskImageType *weightImage) {m_WeightImage = weightImage;} - MaskImageType *getWeightImage() {return m_WeightImage;} - DoubleImageType *getPondImage() {return m_PondImage;} - MaskImagePointer getMaskAverage(); + void SetWeightImage(const MaskImagePointer weightImage) {m_WeightImage = weightImage;} + MaskImageType *GetWeightImage() {return m_WeightImage;} + DoubleImageType *GetPonderationImage() {return m_PonderationImage;} + MaskImagePointer GetAverageMaskImage(); - itkSetMacro(weight, double) - itkSetMacro(testCombi, double) - itkSetMacro(flagGFA, bool) - itkSetMacro(Tournier, bool) - itkSetMacro(AicImage, DoubleImagePointer) + itkSetMacro(Weight, double) + itkSetMacro(TestCombi, double) + itkSetMacro(UseGFA, bool) + itkSetMacro(AICImage, DoubleImagePointer) protected: ODFAverageImageFilter() : Superclass() { - m_nbSamplesTheta = 10; - m_nbSamplesPhi = 2 * m_nbSamplesTheta; - m_weight = 0.0; - - m_flagGFA = false; + m_ThetaGridSize = 10; + m_PhiGridSize = 2 * m_ThetaGridSize; + m_Weight = 0.0; + m_UseGFA = false; } - virtual ~ODFAverageImageFilter() - { - } + virtual ~ODFAverageImageFilter() {} void BeforeThreadedGenerateData() ITK_OVERRIDE; void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; void AfterThreadedGenerateData() ITK_OVERRIDE; - void discretizeSH(); - void discretizeODF(VectorType &Coef, std::vector &resHisto); - void getAverageHisto(std::vector &coefs, VectorType &resCoef, double smallWeight, double bigWeight); - void getAverageODF(std::vector> &histos, VectorType &resCoef, double smallWeight, double bigWeight); - VectorType getSquareRootODFCoef(std::vector &histo); - VectorType getSquareODFCoef(std::vector &histo); + void DiscretizeSH(); + void DiscretizeODF(VectorType &Coef, std::vector &resHisto); + void GetAverageHisto(std::vector &coefs, VectorType &resCoef, double smallWeight, double bigWeight); + void GetAverageODF(std::vector> &histos, VectorType &resCoef, double smallWeight, double bigWeight); + VectorType GetSquareRootODFCoef(std::vector &histo); + VectorType GetSquareODFCoef(std::vector &histo); double GetGeneralizedFractionalAnisotropy(VectorType &modelValue); - - private: ITK_DISALLOW_COPY_AND_ASSIGN(ODFAverageImageFilter); - int m_nbSamplesPhi; - int m_nbSamplesTheta; - int m_vectorLength; + unsigned int m_PhiGridSize; + unsigned int m_ThetaGridSize; + unsigned int m_VectorLength; double m_ODFSHOrder; - double m_smallWeight; - double m_bigWeight; - double m_weight; - double m_minAic; - double m_testCombi; + double m_SmallWeight; + double m_BigWeight; + double m_Weight; + double m_MinAICValue; + double m_TestCombi; - bool m_flagGFA; - bool m_Tournier; + bool m_UseGFA; MaskImagePointer m_WeightImage; - DoubleImagePointer m_PondImage; - DoubleImagePointer m_AicImage; + DoubleImagePointer m_PonderationImage; + DoubleImagePointer m_AICImage; - std::vector m_Masks; + std::vector m_MaskImages; - vnl_matrix m_spherHarm; + vnl_matrix m_SHValues; anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; - - HistoArrayType m_histoODFs, m_SQRTHistoODFs; - - - + HistoArrayType m_HistoODFs, m_SqrtHistoODFs; }; } // end of namespace anima From d1a361a0e7d412ff1a3f0e44b8d745685e0393fa Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Wed, 15 Nov 2023 23:04:15 +0100 Subject: [PATCH 06/11] Clean up TOD estimator. --- .../odf/tod_estimator/CMakeLists.txt | 45 +- .../odf/tod_estimator/animaTODEstimator.cxx | 71 +- .../animaTODEstimatorImageFilter.h | 176 ++- .../animaTODEstimatorImageFilter.hxx | 1114 ++++++----------- 4 files changed, 547 insertions(+), 859 deletions(-) diff --git a/Anima/diffusion/odf/tod_estimator/CMakeLists.txt b/Anima/diffusion/odf/tod_estimator/CMakeLists.txt index 5b8e31076..3effe2ce5 100644 --- a/Anima/diffusion/odf/tod_estimator/CMakeLists.txt +++ b/Anima/diffusion/odf/tod_estimator/CMakeLists.txt @@ -1,39 +1,30 @@ if(BUILD_TOOLS AND USE_NLOPT) -project(animaTODEstimator) + project(animaTODEstimator) -## ############################################################################# -## List Sources -## ############################################################################# + # ############################################################################ + # List Sources + # ############################################################################ -list_source_files(${PROJECT_NAME} - ${CMAKE_CURRENT_SOURCE_DIR} - ) + list_source_files(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}) + # ############################################################################ + # add executable + # ############################################################################ -## ############################################################################# -## add executable -## ############################################################################# + add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_CFILES}) -add_executable(${PROJECT_NAME} - ${${PROJECT_NAME}_CFILES} - ) + # ############################################################################ + # Link + # ############################################################################ -## ############################################################################# -## Link -## ############################################################################# + target_link_libraries(${PROJECT_NAME} ${ITKIO_LIBRARIES} AnimaDataIO + AnimaSHTools) -target_link_libraries(${PROJECT_NAME} - ${VTK_LIBRARIES} - ${ITKIO_LIBRARIES} - AnimaSHTools - AnimaDataIO - ) + # ############################################################################ + # install + # ############################################################################ -## ############################################################################# -## install -## ############################################################################# - -set_exe_install_rules(${PROJECT_NAME}) + set_exe_install_rules(${PROJECT_NAME}) endif() diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx index 2f991ddb7..4adcb43b1 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx @@ -1,54 +1,61 @@ +#include #include + #include -#include -#include +#include -void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData) +void eventCallback(itk::Object *caller, const itk::EventObject &event, void *clientData) { - itk::ProcessObject * processObject = (itk::ProcessObject*) caller; - std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"<GetProgress() * 100) << "%" << std::flush; } int main(int argc, char **argv) { - TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_VERSION); - - TCLAP::ValueArg inArg("i","input","Input tractography image (.vtk or .vtp)",true,"","input tractography image",cmd); - TCLAP::ValueArg resArg("o","outputfile","Result TOD image",true,"","result TOD image",cmd); - TCLAP::ValueArg refArg("g","geometry","Output image geometry",true,"","output geometry",cmd); - - TCLAP::SwitchArg normArg("N", "Normalize", "Normalize TOD", cmd, false); - - TCLAP::ValueArg orderArg("k","order","Order of spherical harmonics basis (default 4)",false,4,"Order of SH basis",cmd); - - TCLAP::ValueArg nbpArg("p","numberofthreads","Number of threads to run on (default: all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd); + TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ', ANIMA_VERSION); + + TCLAP::ValueArg inArg( + "i", "input-file", + "A string specifying the name of a file storing the input tractography image. Supported formats are `.vtk`, `.vtp` or `.fds`.", + true, "", "input tractography image", cmd); + TCLAP::ValueArg outArg( + "o", "output-file", + "A string specifying the name of a file storing the output TOD image.", + true, "", "output TOD image", cmd); + TCLAP::ValueArg refArg( + "g", "geometry-file", + "A string specifying the name of a file storing the reference geometry image.", + true, "", "reference geometry image", cmd); + + TCLAP::SwitchArg normArg( + "N", "normalize-tod", + "A switch to turn on TOD normalization.", + cmd, false); + + TCLAP::ValueArg nbpArg( + "T", "nb-threads", + "An integer value specifying the number of threads to run on (default: all cores).", + false, itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(), "number of threads", cmd); try { - cmd.parse(argc,argv); + cmd.parse(argc, argv); } - catch (TCLAP::ArgException& e) + catch (TCLAP::ArgException &e) { std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; return EXIT_FAILURE; } - typedef anima::TODEstimatorImageFilter FilterType; + using FilterType = anima::TODEstimatorImageFilter; + using InputImageType = FilterType::InputImageType; FilterType::Pointer mainFilter = FilterType::New(); - -// if (orderArg.getValue() % 2 == 0) -// mainFilter->SetLOrder(orderArg.getValue()); -// else -// mainFilter->SetLOrder(orderArg.getValue() - 1); - typedef FilterType::InputImageType InputImageType; mainFilter->SetInput(anima::readImage(refArg.getValue())); - - mainFilter->SetLOrder(orderArg.getValue()); mainFilter->SetInputFileName(inArg.getValue()); - mainFilter->SetRefFileName(refArg.getValue()); - mainFilter->SetNormalize(normArg.getValue()); + mainFilter->SetReferenceFileName(refArg.getValue()); + mainFilter->SetUseNormalization(normArg.getValue()); mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); @@ -57,14 +64,12 @@ int main(int argc, char **argv) itk::TimeProbe tmpTime; tmpTime.Start(); - mainFilter->Update(); - tmpTime.Stop(); - std::cout << std::endl << "Execution Time: " << tmpTime.GetTotal() << std::endl; + std::cout << "\nExecution Time: " << tmpTime.GetTotal() << "s" << std::endl; - anima::writeImage (resArg.getValue(),mainFilter->GetOutput()); + anima::writeImage(outArg.getValue(), mainFilter->GetOutput()); return EXIT_SUCCESS; } diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h index 29abdc6f4..d84896712 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.h @@ -1,138 +1,112 @@ #pragma once -#include -#include -#include -#include -#include - #include #include -#include -#include #include -#include namespace anima { -//template -class TODEstimatorImageFilter : -public anima::NumberedThreadImageToImageFilter < itk::Image, itk::VectorImage > -{ -public: - - typedef TODEstimatorImageFilter Self; -// typedef itk::Vector pointType; - typedef itk::Point PointType; -// typedef PointType DirType; - typedef itk::Vector DirType; - typedef std::vector DirVectorType; - typedef std::vector FiberType; - - typedef double MathScalarType; - - typedef itk::Matrix Matrix3DType; - typedef itk::Vector Vector3DType; - typedef itk::VariableLengthVector VectorType; - - typedef anima::ODFSphericalHarmonicBasis baseSH; - typedef std::complex complexType; - - typedef itk::VectorImage TOutputImage; - typedef itk::Image TRefImage; - - typedef anima::NumberedThreadImageToImageFilter Superclass; - typedef itk::SmartPointer Pointer; - typedef itk::SmartPointer ConstPointer; - - - itkNewMacro(Self) - - itkTypeMacro(TODEstimatorImageFilter, anima::NumberedThreadImageToImageFilter); - - typedef typename TOutputImage::Pointer OutputImagePointer; - - typedef typename Superclass::InputImageRegionType InputImageRegionType; - typedef typename Superclass::OutputImageRegionType OutputImageRegionType; - - itkSetMacro(InputFileName,std::string); - itkSetMacro(RefFileName,std::string); - itkSetMacro(LOrder,unsigned int); - itkSetMacro(Normalize, bool); - - -protected: - TODEstimatorImageFilter() - : Superclass() + template + class TODEstimatorImageFilter : public anima::NumberedThreadImageToImageFilter, itk::VectorImage> { - } + public: + /** Standard class typedefs. */ + using Self = TODEstimatorImageFilter; + using InputImageType = itk::Image; + using OutputImageType = itk::VectorImage; + using ReferenceImageType = itk::Image; + using Superclass = anima::NumberedThreadImageToImageFilter; + using Pointer = itk::SmartPointer; + using ConstPointer = itk::SmartPointer; - virtual ~TODEstimatorImageFilter() - { + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods) */ + itkTypeMacro(TODEstimatorImageFilter, anima::NumberedThreadImageToImageFilter); - } + /** Superclass typedefs. */ + using InputImageRegionType = typename Superclass::InputImageRegionType; + using OutputImageRegionType = typename Superclass::OutputImageRegionType; -// void GenerateData() ITK_OVERRIDE; + using InputImagePointerType = typename InputImageType::Pointer; + using OutputImagePointerType = typename OutputImageType::Pointer; + using ReferenceImagePointerType = typename ReferenceImageType::Pointer; + using InputImagePixelType = typename InputImageType::PixelType; + using OutputImagePixelType = typename OutputImageType::PixelType; + using ReferenceImagePixelType = ReferenceImageType::PixelType; - void BeforeThreadedGenerateData() ITK_OVERRIDE; - void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; - void AfterThreadedGenerateData() ITK_OVERRIDE; + using PointType = itk::Point; + using DirType = itk::Vector; + using DirVectorType = std::vector; + using FiberType = std::vector; + using Matrix3DType = itk::Matrix; + using Vector3DType = itk::Vector; + using MatrixType = vnl_matrix; + using BasisType = anima::ODFSphericalHarmonicBasis; + using ComplexType = std::complex; + itkSetMacro(InputFileName, std::string); + itkSetMacro(ReferenceFileName, std::string); + itkSetMacro(LOrder, unsigned int); + itkSetMacro(UseNormalization, bool); - FiberType readFiber(vtkIdType numberOfPoints, const vtkIdType *indices, vtkPoints *points); - PointType getCenterVoxel(int index, FiberType &fiber); - DirType getFiberDirection(int index, FiberType &fiber); - void getSHCoefs(DirType dir, VectorType &resSH, baseSH &basis); - void ComputeCoefs(); - void processFiber(FiberType &fiber, baseSH &basis); + protected: + TODEstimatorImageFilter() {} - void getMainDirections(DirVectorType inDirs, DirVectorType &mainDirs); - double getEuclideanDistance(DirType dir1, DirType dir2); - DirType getNewClusterAverage(int numCluster, DirVectorType &dirs, std::vector &cluster); + virtual ~TODEstimatorImageFilter() {} - void getSHCoef(DirType dir, VectorType &coefs); + void BeforeThreadedGenerateData() ITK_OVERRIDE; + void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; - void precomputeSH(); - void discretizeODF(VectorType ODFCoefs, std::vector &ODFDiscret); - VectorType getSquareRootODF(std::vector ODFDiscret); - VectorType getSquareODF(std::vector ODFDiscret); - void getAverageCoefs(std::vector &vecCoefs, VectorType &avgCoef); - - void averageODFs(std::vector &vecCoefs, VectorType &resOdf); + FiberType ReadFiber(vtkIdType numberOfPoints, const vtkIdType *indices, vtkPoints *points); + PointType GetCenterVoxel(int index, FiberType &fiber); + DirType GetFiberDirection(int index, FiberType &fiber); + void GetSHCoefs(DirType dir, OutputImagePixelType &resSH, BasisType &basis); + void ComputeCoefs(); + void ProcessFiber(FiberType &fiber, BasisType &basis); - vnl_matrix GetRotationMatrix(DirType dir1, DirType dir2); + void GetMainDirections(DirVectorType inDirs, DirVectorType &mainDirs); + double GetEuclideanDistance(DirType dir1, DirType dir2); + DirType GetNewClusterAverage(int numCluster, DirVectorType &dirs, std::vector &cluster); -// void GenerateOutputInformation() ITK_OVERRIDE; + void GetSHCoef(DirType dir, OutputImagePixelType &coefs); -private: - ITK_DISALLOW_COPY_AND_ASSIGN(TODEstimatorImageFilter); + void PrecomputeSH(); + void DiscretizeODF(OutputImagePixelType ODFCoefs, std::vector &ODFDiscret); + OutputImagePixelType GetSquareRootODF(std::vector ODFDiscret); + OutputImagePixelType GetSquareODF(std::vector ODFDiscret); + void GetAverageCoefs(std::vector &vecCoefs, OutputImagePixelType &avgCoef); - std::string m_InputFileName; - std::string m_RefFileName; + void AverageODFs(std::vector &vecCoefs, OutputImagePixelType &resOdf); - DirType m_CstDir; + MatrixType GetRotationMatrix(DirType dir1, DirType dir2); - unsigned int m_LOrder; - int m_VectorLength; + private: + ITK_DISALLOW_COPY_AND_ASSIGN(TODEstimatorImageFilter); - double m_NbSample; + std::string m_InputFileName; + std::string m_ReferenceFileName; - bool m_Normalize; + DirType m_CstDir; + unsigned int m_LOrder; + int m_VectorLength; - VectorType m_GaussCoefs; + double m_NbSample; - std::vector> m_SphereSampl; - vnl_matrix m_SpherHarm; + bool m_UseNormalization; - anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; - std::vector> m_ImgDir; + OutputImagePixelType m_GaussCoefs; - itk::Image::Pointer test; + std::vector> m_SphereSampl; + MatrixType m_SpherHarm; -}; + anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; + std::vector m_ImgDir; + }; } // end namespace anima #include "animaTODEstimatorImageFilter.hxx" diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx index e83a81817..e06d13200 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx @@ -1,850 +1,568 @@ #pragma once #include "animaTODEstimatorImageFilter.h" - -#include -#include -#include -#include -#include -#include - -#include +#include +#include #include -#include #include -#include -#include #include -#include "animaLogExpMapsUnitSphere.h" + +#include #include namespace anima { + template + void + TODEstimatorImageFilter::BeforeThreadedGenerateData() + { + Superclass::BeforeThreadedGenerateData(); + + ReferenceImagePointerType refImg = anima::readImage(m_ReferenceFileName); + m_LOrder = 8; + unsigned int vectorLength = (m_LOrder + 1) * (m_LOrder + 2) / 2; + m_VectorLength = vectorLength; + OutputImageType *output = this->GetOutput(); + output->SetVectorLength(vectorLength); + output->SetRegions(refImg->GetLargestPossibleRegion()); + output->SetSpacing(refImg->GetSpacing()); + output->SetDirection(refImg->GetDirection()); + output->SetOrigin(refImg->GetOrigin()); + output->Allocate(); + + OutputImagePixelType tmp; + tmp.SetSize((m_LOrder + 1) * (m_LOrder + 2) / 2); + tmp.Fill(0); + output->FillBuffer(tmp); -void TODEstimatorImageFilter::BeforeThreadedGenerateData() -{ - Superclass::BeforeThreadedGenerateData(); - - TRefImage::Pointer refImg = anima::readImage(m_RefFileName); - - unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2; - m_VectorLength = vectorLength; - TOutputImage *output = this->GetOutput(); - output->SetVectorLength(vectorLength); - output->SetRegions(refImg->GetLargestPossibleRegion()); - output->SetSpacing(refImg->GetSpacing()); - output->SetDirection(refImg->GetDirection()); - output->SetOrigin(refImg->GetOrigin()); - output->Allocate(); - - VectorType tmp; - tmp.SetSize((m_LOrder+1)*(m_LOrder+2)/2); - tmp.Fill(0); - output->FillBuffer(tmp); - - test = itk::Image::New(); - test->SetRegions(refImg->GetLargestPossibleRegion()); - test->SetSpacing(refImg->GetSpacing()); - test->SetDirection(refImg->GetDirection()); - test->SetOrigin(refImg->GetOrigin()); - test->Allocate(); - - m_CstDir[0] = 0; - m_CstDir[1] = 0; - m_CstDir[2] = 1; - - m_NbSample = 200; - anima::GetSphereEvenSampling(m_SphereSampl, m_NbSample); - - m_SpherHarm.set_size(m_NbSample, vectorLength); - - m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_LOrder); - this->precomputeSH(); - - const int Xmax = output->GetLargestPossibleRegion().GetSize()[0]; - const int Ymax = output->GetLargestPossibleRegion().GetSize()[1]; - const int Zmax = output->GetLargestPossibleRegion().GetSize()[2]; - - const int Xmin = output->GetLargestPossibleRegion().GetIndex()[0]; - const int Ymin = output->GetLargestPossibleRegion().GetIndex()[1]; - const int Zmin = output->GetLargestPossibleRegion().GetIndex()[2]; + m_CstDir[0] = 0; + m_CstDir[1] = 0; + m_CstDir[2] = 1; - const int dirX = output->GetDirection()[0][0]; - const int dirY = output->GetDirection()[1][1]; - const int dirZ = output->GetDirection()[2][2]; + m_NbSample = 200; + anima::GetSphereEvenSampling(m_SphereSampl, m_NbSample); - anima::ShapesReader trackReader; - trackReader.SetFileName(m_InputFileName); - trackReader.Update(); + m_SpherHarm.set_size(m_NbSample, vectorLength); - this->ComputeCoefs(); + m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_LOrder); + this->PrecomputeSH(); + this->ComputeCoefs(); - m_ImgDir.resize(Xmax * Ymax * Zmax); + const int Xmax = output->GetLargestPossibleRegion().GetSize()[0]; + const int Ymax = output->GetLargestPossibleRegion().GetSize()[1]; + const int Zmax = output->GetLargestPossibleRegion().GetSize()[2]; + m_ImgDir.resize(Xmax * Ymax * Zmax); - vtkSmartPointer tracks = trackReader.GetOutput(); - vtkSmartPointer cells = tracks->GetLines(); - vtkSmartPointer points = tracks->GetPoints(); + anima::ShapesReader trackReader; + trackReader.SetFileName(m_InputFileName); + trackReader.Update(); - const vtkIdType *indices; - vtkIdType numberOfPoints; + vtkSmartPointer tracks = trackReader.GetOutput(); + vtkSmartPointer cells = tracks->GetLines(); + vtkSmartPointer points = tracks->GetPoints(); - float nbLines = tracks->GetNumberOfLines(); - float lineCount = 0; + const vtkIdType *indices; + vtkIdType numberOfPoints; - std::cout << "Number of fibers + test : " << nbLines << std::endl; + float nbLines = tracks->GetNumberOfLines(); + float lineCount = 0; - anima::ODFSphericalHarmonicBasis basis(m_LOrder); + std::cout << "Number of fibers + test : " << nbLines << std::endl; - auto iter = vtk::TakeSmartPointer(cells->NewIterator()); - for (iter->GoToFirstCell(); !iter->IsDoneWithTraversal(); iter->GoToNextCell()) - { - iter->GetCurrentCell(numberOfPoints, indices); - // std::cout << "Progress : " << (int) (((float)lineCount / nbLines)*100) << "%\r"; - // fflush (stdout); - lineCount++; + anima::ODFSphericalHarmonicBasis basis(m_LOrder); - FiberType fiber = readFiber(numberOfPoints, indices, points); - int nbPoints = fiber.size(); + auto iter = vtk::TakeSmartPointer(cells->NewIterator()); + for (iter->GoToFirstCell(); !iter->IsDoneWithTraversal(); iter->GoToNextCell()) + { + iter->GetCurrentCell(numberOfPoints, indices); + lineCount++; + FiberType fiber = ReadFiber(numberOfPoints, indices, points); + int nbPoints = fiber.size(); - for(int i = 0; i < nbPoints; i++) - { - DirType dir = getFiberDirection(i, fiber); - // OutputImagePointer::DirectionType refDir = output->GetDirection(); - - // if(output->GetDirection()[0][0] > 0) - // dir[0] *= -1; - // if(output->GetDirection()[1][1] < 0) - // dir[1] *= -1; - // if(output->GetDirection()[2][2] < 0) - // dir[2] *= -1; - // dir[0] *= -1; - // dir[1] *= -1; - // dir[2] *= -1; - - // anima::Normalize(dir, dir); - PointType point = getCenterVoxel(i, fiber); - itk::Index<3> index, index2; - - output->TransformPhysicalPointToIndex(point, index); - - index[0] *= dirX; - index[1] *= dirY; - index[2] *= dirZ; - - index2[0] = std::floor(point[0]); - index2[1] = std::floor(point[0]); - index2[2] = std::floor(point[0]); - - m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].push_back(dir); - - // // itk::ImageRegionIterator > testItr(test, test->GetLargestPossibleRegion()); - // // while(!testItr.IsAtEnd()) - // // { - // // itk::Image::IndexType index; - // // index = testItr.GetIndex(); - // // testItr.Set(imgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size()); - // // ++testItr; - // // } - // // anima::writeImage>("./test.nii.gz", test); + for (int i = 0; i < nbPoints; i++) + { + DirType dir = GetFiberDirection(i, fiber); + PointType point = GetCenterVoxel(i, fiber); + itk::Index<3> index; + output->TransformPhysicalPointToIndex(point, index); + m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].push_back(dir); + } } } -} - -void TODEstimatorImageFilter::ComputeCoefs() -{ - m_GaussCoefs.SetSize(91); - m_GaussCoefs.Fill(0.0); - - double l1 = 1.71e-4; - double l2 = 2e-5; - double d = l1 - l2; - double spi = sqrt(M_PI); - double pi = M_PI; - double at = M_PI+2*atan((d-l2)/(2*sqrt(d*l2))); - - double c0 = 1/(2*sqrt(pi)); - double c1 = - sqrt(5) * (3*sqrt(l2)*(d+l2)*(pi + atan((d-l2)/(2*sqrt(l2*d)))) - 4*sqrt(d)*(2*d+3*l2)) / (16*sqrt(pi)*pow(d,1.5)); - double c2 = 3 * (4*sqrt(d)*(16*pow(d,2)+115*d*l2+105*pow(l2,2)) - 15*sqrt(l2)*(3*pow(d,2)+10*d*l2+7*pow(l2,2))*(pi+2*atan((d-l2)/(2*sqrt(d*l2))))) / (128*spi*pow(d,2.5)); - double c3 = -105*sqrt(13)*(at*sqrt(l2)*(5*pow(d,3)+35*l2*pow(d,2)+63*pow(l2,2)*d+33*pow(l2,3)) - 4*sqrt(d)*(33*pow(l2,3)+52*d*pow(l2,2)+103*pow(d,2)*l2/5+128*pow(d,3)/105)) / (1024*spi*pow(d,3.5)); - double c4 = -315*sqrt(17) * (at*sqrt(l2)*(35*pow(d,4)+420*pow(d,3)*l2+1386*pow(d,2)*pow(l2,2)+1716*pow(l2,3)*d+715*pow(l2,4)) - 4*sqrt(d)*(715*pow(l2,4)+4433*pow(l2,3)*d/3+957*pow(l2,2)*pow(d,2)+6967*l2*pow(d,3)/35+2048*pow(d,4)/315)) / (16384 * spi * pow(d,4.5)); - double c5 = -24255*sqrt(21) * (at * sqrt(l2)*(9*pow(d,5)+165*pow(d,4)*l2+858*pow(d,3)*pow(l2,2)+12870*pow(d,2)*pow(l2,3)/7+12155*d*pow(l2,4)/7+4199*pow(l2,5)/7) - 4*sqrt(d)*(4199*pow(l2,5)/7+32266*pow(l2,4)*d/21+20691*pow(l2,3)*pow(d,2)/15+24830*pow(l2,2)*pow(d,3)/49+28799*l2*pow(d,4)/441+32768*pow(d,5)/24255)) / (262144*pow(d,5.5)*spi); - - - // m_GaussCoefs[0] = this->m_ODFSHBasis->getNthSHValueAtPosition(0, 0, 0, 0); - // std::vector Dir(3), sphDir(2); - // Dir[0] = 0; - // Dir[1] = 0; - // Dir[2] = 1; - // anima::TransformCartesianToSphericalCoordinates(Dir, sphDir); - // int k = 0; - // for(int l = 0; l <= m_LOrder; l+=2) - // { - // for(int m = -l; m <= l; m++) - // { - // m_GaussCoefs[k] = this->m_ODFSHBasis->getNthSHValueAtPosition(l, m, sphDir[0], sphDir[1]); - // k++; - // } - // } - - m_GaussCoefs[0] = c0; - m_GaussCoefs[3] = c1; - m_GaussCoefs[10] = c2; - m_GaussCoefs[21] = c3; - m_GaussCoefs[36] = c4; - m_GaussCoefs[55] = c5; -} - -void TODEstimatorImageFilter::DynamicThreadedGenerateData(const TODEstimatorImageFilter::OutputImageRegionType &outputRegionForThread) -{ - TOutputImage *output = this->GetOutput(); - itk::ImageRegionIterator outItr(output, outputRegionForThread); - typename TOutputImage::IndexType index, index_out; - - VectorType nullVector; - nullVector.SetSize((m_LOrder+1)*(m_LOrder+2)/2); - nullVector.Fill(0); - - const int Xmax = output->GetLargestPossibleRegion().GetSize()[0]; - const int Ymax = output->GetLargestPossibleRegion().GetSize()[1]; - const int Zmax = output->GetLargestPossibleRegion().GetSize()[2]; - const int dirX = output->GetDirection()[0][0]; - const int dirY = output->GetDirection()[1][1]; - const int dirZ = output->GetDirection()[2][2]; - - while(!outItr.IsAtEnd()) + template + void + TODEstimatorImageFilter::ComputeCoefs() { + m_GaussCoefs.SetSize(91); + m_GaussCoefs.Fill(0.0); + + double l1 = 1.71e-4; + double l2 = 2e-5; + double d = l1 - l2; + double spi = sqrt(M_PI); + double pi = M_PI; + double at = M_PI + 2 * atan((d - l2) / (2 * sqrt(d * l2))); + + double c0 = 1 / (2 * sqrt(pi)); + double c1 = -sqrt(5) * (3 * sqrt(l2) * (d + l2) * (pi + atan((d - l2) / (2 * sqrt(l2 * d)))) - 4 * sqrt(d) * (2 * d + 3 * l2)) / (16 * sqrt(pi) * pow(d, 1.5)); + double c2 = 3 * (4 * sqrt(d) * (16 * pow(d, 2) + 115 * d * l2 + 105 * pow(l2, 2)) - 15 * sqrt(l2) * (3 * pow(d, 2) + 10 * d * l2 + 7 * pow(l2, 2)) * (pi + 2 * atan((d - l2) / (2 * sqrt(d * l2))))) / (128 * spi * pow(d, 2.5)); + double c3 = -105 * sqrt(13) * (at * sqrt(l2) * (5 * pow(d, 3) + 35 * l2 * pow(d, 2) + 63 * pow(l2, 2) * d + 33 * pow(l2, 3)) - 4 * sqrt(d) * (33 * pow(l2, 3) + 52 * d * pow(l2, 2) + 103 * pow(d, 2) * l2 / 5 + 128 * pow(d, 3) / 105)) / (1024 * spi * pow(d, 3.5)); + double c4 = -315 * sqrt(17) * (at * sqrt(l2) * (35 * pow(d, 4) + 420 * pow(d, 3) * l2 + 1386 * pow(d, 2) * pow(l2, 2) + 1716 * pow(l2, 3) * d + 715 * pow(l2, 4)) - 4 * sqrt(d) * (715 * pow(l2, 4) + 4433 * pow(l2, 3) * d / 3 + 957 * pow(l2, 2) * pow(d, 2) + 6967 * l2 * pow(d, 3) / 35 + 2048 * pow(d, 4) / 315)) / (16384 * spi * pow(d, 4.5)); + double c5 = -24255 * sqrt(21) * (at * sqrt(l2) * (9 * pow(d, 5) + 165 * pow(d, 4) * l2 + 858 * pow(d, 3) * pow(l2, 2) + 12870 * pow(d, 2) * pow(l2, 3) / 7 + 12155 * d * pow(l2, 4) / 7 + 4199 * pow(l2, 5) / 7) - 4 * sqrt(d) * (4199 * pow(l2, 5) / 7 + 32266 * pow(l2, 4) * d / 21 + 20691 * pow(l2, 3) * pow(d, 2) / 15 + 24830 * pow(l2, 2) * pow(d, 3) / 49 + 28799 * l2 * pow(d, 4) / 441 + 32768 * pow(d, 5) / 24255)) / (262144 * pow(d, 5.5) * spi); + + m_GaussCoefs[0] = c0; + m_GaussCoefs[3] = c1; + m_GaussCoefs[10] = c2; + m_GaussCoefs[21] = c3; + m_GaussCoefs[36] = c4; + m_GaussCoefs[55] = c5; + } + template + void + TODEstimatorImageFilter::DynamicThreadedGenerateData(const TODEstimatorImageFilter::OutputImageRegionType &outputRegionForThread) + { + OutputImageType *output = this->GetOutput(); + itk::ImageRegionIterator outItr(output, outputRegionForThread); + typename OutputImageType::IndexType index, index_out; - index = outItr.GetIndex(); + OutputImagePixelType nullVector; + nullVector.SetSize((m_LOrder + 1) * (m_LOrder + 2) / 2); + nullVector.Fill(0); - // index[0] *= -1; - // index[1] *= -1; - // index[2] *= -1; + const int Xmax = output->GetLargestPossibleRegion().GetSize()[0]; + const int Ymax = output->GetLargestPossibleRegion().GetSize()[1]; + const int Zmax = output->GetLargestPossibleRegion().GetSize()[2]; + while (!outItr.IsAtEnd()) + { - // index[0] *= dirX; - // index[1] *= dirY; - // index[2] *= dirZ; + index = outItr.GetIndex(); - std::vector vecCoefs; - unsigned int tmpSize = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size(); - if (tmpSize != 0) - { - DirVectorType mainDirs; - DirVectorType localDir(tmpSize); - localDir = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])]; - this->getMainDirections(localDir, mainDirs); - - // std::cout << "----------------------------------------" << std::endl << std::endl; - // for(int i = 0; i < tmpSize; i++) - // std::cout << localDir[i] << ", " << std::endl; - // std::cout << std::endl << std::endl; - // for(int i = 0; i < mainDirs.size(); i++) - // std::cout << mainDirs[i] << ", " << std::endl; - - // std::cout << std::endl << std::endl; - - VectorType tmpCoefs(m_VectorLength); - tmpCoefs.Fill(0); - for(int i = 0; i < mainDirs.size(); i++) + std::vector vecCoefs; + int tmpSize = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])].size(); + if (tmpSize != 0) { - if(!isnan(mainDirs[i][0])) + DirVectorType mainDirs; + DirVectorType localDir(tmpSize); + localDir = m_ImgDir[index[0] + Xmax * (index[1] + Ymax * index[2])]; + this->GetMainDirections(localDir, mainDirs); + + OutputImagePixelType tmpCoefs(m_VectorLength); + tmpCoefs.Fill(0); + for (int i = 0; i < mainDirs.size(); i++) { - this->getSHCoef(mainDirs[i], tmpCoefs); - // std::vector Dir(3), sphDir(2); - // for(int k = 0; k < 3; k++) - // Dir[k] = mainDirs[i][k]; - // anima::TransformCartesianToSphericalCoordinates(Dir, sphDir); - // int j = 0; - // for(int l = 0; l <= m_LOrder; l+=2) - // { - // for(int m = -l; m <= l; m++) - // { - // tmpCoefs[j] = this->m_ODFSHBasis->getNthSHValueAtPosition(l, m, sphDir[0], sphDir[1]); - // j++; - // } - // } - - vecCoefs.push_back(tmpCoefs); + if (!isnan(mainDirs[i][0])) + { + this->GetSHCoef(mainDirs[i], tmpCoefs); + + vecCoefs.push_back(tmpCoefs); + } } - } - VectorType resCoefs(m_VectorLength); - resCoefs.Fill(0); - if(vecCoefs.size() != 0) - { - // this->averageODFs(vecCoefs, resCoefs); - // for(int k = 0; k < vecCoefs.size(); k++) - // { - // for(int i = 0; i < m_VectorLength; i++) - // resCoefs[i] += (vecCoefs[k][i] / vecCoefs.size()); - // } - for(int k = 0; k < vecCoefs.size(); k++) + OutputImagePixelType resCoefs(m_VectorLength); + resCoefs.Fill(0); + if (vecCoefs.size() != 0) { - for(int i = 0; i < m_VectorLength; i++) - resCoefs[i] += vecCoefs[k][i]; + resCoefs = vecCoefs[0]; + this->AverageODFs(vecCoefs, resCoefs); + if (m_UseNormalization) + { + for (int i = 0; i < m_VectorLength; i++) + resCoefs[i] /= vecCoefs.size(); + } + outItr.Set(resCoefs); } - if(m_Normalize) + + else + outItr.Set(nullVector); + + std::vector testSh(m_VectorLength); + for (int i = 0; i < m_VectorLength; i++) { - for(int i = 0; i < m_VectorLength; i++) - resCoefs[i] /= vecCoefs.size(); + testSh[i] = resCoefs[i]; } - outItr.Set(resCoefs); + localDir.clear(); + localDir.shrink_to_fit(); } - else outItr.Set(nullVector); - std::vector testSh(m_VectorLength); - for(int i = 0; i < m_VectorLength; i++) - { - testSh[i] = resCoefs[i]; - } - localDir.clear(); - localDir.shrink_to_fit(); - } - else - outItr.Set(nullVector); + ++outItr; - ++outItr; - - this->IncrementNumberOfProcessedPoints(); + this->IncrementNumberOfProcessedPoints(); + } } -} - -void TODEstimatorImageFilter::AfterThreadedGenerateData() -{ - -} + template + void + TODEstimatorImageFilter::GetMainDirections(DirVectorType inDirs, DirVectorType &mainDirs) + { -//void TODEstimatorImageFilter::GenerateData() -//{ + if (inDirs.size() == 0) + { + mainDirs.resize(0); + return; + } + const int numIt = 200; + int numDirs = inDirs.size(), crit = 0, numClusters = 0; + if (numDirs < 4) + { + numClusters = numDirs; + mainDirs.resize(numClusters); + for (int i = 0; i < numClusters; i++) + { + mainDirs[i] = inDirs[i]; + } + return; + } + else + numClusters = 4; + DirVectorType clustersAverage(numClusters); + std::vector clusters(numDirs); + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distrib(0, numDirs); + for (int i = 0; i < numClusters; i++) + { + int k = distrib(gen); + clustersAverage[i] = inDirs[k]; + } + int It = 0; + bool stop = false; + double dist = 0; + while (It < numIt && !stop) + { + std::vector clustersOld(numDirs); + for (int i = 0; i < numDirs; i++) + clustersOld[i] = clusters[i]; -//} + for (int i = 0; i < numDirs; i++) + { + double minDist = 1e10; + for (int j = 0; j < numClusters; j++) + { + dist = std::sqrt((clustersAverage[j][0] - inDirs[i][0]) * (clustersAverage[j][0] - inDirs[i][0]) + (clustersAverage[j][1] - inDirs[i][1]) * (clustersAverage[j][1] - inDirs[i][1]) + (clustersAverage[j][2] - inDirs[i][2]) * (clustersAverage[j][2] - inDirs[i][2])); + if (dist < minDist) + { + minDist = dist; + clusters[i] = j; + } + } + } + crit = 0; + for (int i = 0; i < numClusters; i++) + { + clustersAverage[i] = GetNewClusterAverage(i, inDirs, clusters); + } + for (int i = 0; i < numDirs; i++) + crit += clusters[i] - clustersOld[i]; + if (crit == 0) + stop = true; -void TODEstimatorImageFilter::getMainDirections(DirVectorType inDirs, DirVectorType &mainDirs) -{ + ++It; + } - if(inDirs.size() == 0) - { - mainDirs.resize(0); - return; + mainDirs.resize(numClusters); + for (int i = 0; i < numClusters; i++) + mainDirs[i] = clustersAverage[i]; } - const int numIt = 200; - int numDirs = inDirs.size(), crit = 0, numClusters = 0; - if(numDirs < 4) + template + typename TODEstimatorImageFilter::DirType + TODEstimatorImageFilter::GetNewClusterAverage(int numCluster, DirVectorType &indirs, std::vector &cluster) { - numClusters = numDirs; - mainDirs.resize(numClusters); - for(int i = 0; i < numClusters; i++) + DirType sum, sumNorm; + DirVectorType dirs; + dirs = indirs; + for (int j = 0; j < 3; j++) + sum[j] = 0; + + int size = 0; + for (int i = 0; i < dirs.size(); i++) { - mainDirs[i] = inDirs[i]; + if (cluster[i] == numCluster) + { + for (int j = 0; j < 3; j++) + sum[j] = sum[j] + dirs[i][j]; + ++size; + } } - return; - } - else - numClusters = 4; - - DirVectorType clustersAverage(numClusters); - std::vector clusters(numDirs); - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> distrib(0, numDirs); + for (int j = 0; j < 3; j++) + sum[j] = sum[j] / size; + anima::Normalize(sum, sumNorm); + return sumNorm; + } - for(int i = 0; i < numClusters; i++) + template + double + TODEstimatorImageFilter::GetEuclideanDistance(DirType dir1, DirType dir2) { - int k = distrib(gen); - clustersAverage[i] = inDirs[k]; + return std::sqrt(std::pow((dir2[0] - dir1[0]), 2) + std::pow((dir2[1] - dir1[1]), 2) + std::pow((dir2[2] - dir1[2]), 2)); } - int It = 0; - bool stop = false; - double dist = 0; - while(It < numIt && !stop) + template + void + TODEstimatorImageFilter::GetSHCoef(DirType tmpDir, OutputImagePixelType &coefs) { - std::vector clustersOld(numDirs); - for(int i = 0; i < numDirs; i++) - clustersOld[i] = clusters[i]; - - for(int i = 0; i < numDirs; i++) + int pos = 0, T = this->GetOutput()->GetVectorLength(); + OutputImagePixelType tmp, resSH, rotatedModel(m_VectorLength); + tmp.SetSize(T); + DirType dir; + this->GetOutput()->TransformLocalVectorToPhysicalVector(tmpDir, dir); + for (int k = 0; k <= m_LOrder; k += 2) { - double minDist = 1e10; - for(int j = 0; j < numClusters; j++) + for (int m = -k; m <= k; m++) { - // double dist = getEuclideanDistance(inDirs[i], clustersAverage[j]); - // dist = std::sqrt(std::pow((clustersAverage[j][0] - inDirs[i][0]),2) + std::pow((clustersAverage[j][1] - inDirs[i][1]),2) + std::pow((clustersAverage[j][2] - inDirs[i][2]),2)); - dist = std::sqrt((clustersAverage[j][0] - inDirs[i][0]) * (clustersAverage[j][0] - inDirs[i][0]) + (clustersAverage[j][1] - inDirs[i][1]) * (clustersAverage[j][1] - inDirs[i][1]) + (clustersAverage[j][2] - inDirs[i][2]) * (clustersAverage[j][2] - inDirs[i][2])); - if(dist < minDist) - { - minDist = dist; - clusters[i] = j; - } + tmp[pos] = m_GaussCoefs[pos]; + pos++; } } - crit = 0; - for(int i = 0; i < numClusters; i++) - { - clustersAverage[i] = getNewClusterAverage(i, inDirs, clusters); - } - - for(int i = 0; i < numDirs; i++) - crit += clusters[i] - clustersOld[i]; - - if(crit == 0) - stop = true; - - ++It; - } - - mainDirs.resize(numClusters); - for(int i = 0; i < numClusters; i++) - mainDirs[i] = clustersAverage[i]; - + itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(m_CstDir, dir); + vnl_matrix rotationMatrix; + rotationMatrix.set_size(3, 3); + for (int x = 0; x < 3; ++x) + for (int y = 0; y < 3; ++y) + rotationMatrix.put(x, y, itkRotationMatrix[y][x]); -} + std::vector eulerAngles; + anima::GetEulerAnglesFromRotationMatrix(rotationMatrix, eulerAngles); + double a = rotationMatrix(2, 2); + rotatedModel = tmp; -typename TODEstimatorImageFilter::DirType TODEstimatorImageFilter ::getNewClusterAverage(int numCluster, DirVectorType &indirs, std::vector &cluster) -{ - DirType sum, sumNorm; - DirVectorType dirs; - dirs = indirs; - for(int j = 0; j < 3; j++) - sum[j] = 0; - - int size = 0; - for(int i = 0 ; i < dirs.size(); i++) - { - if(cluster[i] == numCluster) + vnl_matrix ODFRotationMatrix; + for (unsigned int l = 0; l <= m_LOrder; l += 2) { - for(int j = 0; j < 3; j++) - sum[j] = sum[j] + dirs[i][j]; - ++size; - } - } - for(int j = 0; j < 3; j++) - sum[j] = sum[j]/size; - - - anima::Normalize(sum, sumNorm); - return sumNorm; -} - - -double TODEstimatorImageFilter::getEuclideanDistance(DirType dir1, DirType dir2) -{ - return std::sqrt(std::pow((dir2[0] - dir1[0]),2) + std::pow((dir2[1] - dir1[1]),2) + std::pow((dir2[2] - dir1[2]),2)); -} + anima::EstimateLocalODFRotationMatrix(ODFRotationMatrix, l, eulerAngles[0], eulerAngles[1], eulerAngles[2]); + unsigned int mBaseInd = (l * l + l + 2) / 2 - l - 1; + for (unsigned int m = 0; m <= 2 * l; ++m) + { + rotatedModel[mBaseInd + m] = 0; -void TODEstimatorImageFilter::getSHCoef(DirType tmpDir, VectorType &coefs) -{ - int pos = 0, T = this->GetOutput()->GetVectorLength(); - VectorType tmp, resSH, rotatedModel; - tmp.SetSize(T); - DirType dir; - this->GetOutput()->TransformLocalVectorToPhysicalVector(tmpDir, dir); - // this->GetOutput()->TransformPhysicalVectorToLocalVector(tmpDir, dir); - // dir[0] *= -1; - // dir[1] *= -1; - // dir[2] *= -1; - - for(int k = 0; k <= m_LOrder; k+=2) - { - for(int m = -k; m <= k; m ++) - { - tmp[pos] = m_GaussCoefs[pos]; - pos++; + for (unsigned int mp = 0; mp <= 2 * l; ++mp) + rotatedModel[mBaseInd + m] += ODFRotationMatrix(m, mp) * tmp[mBaseInd + mp]; + } } + coefs = rotatedModel; } - // if(this->GetOutput()->GetDirection()[0][0] > 0) - // dir[0] *= -1; - // if(this->GetOutput()->GetDirection()[1][1] < 0) - // dir[1] *= -1; - // if(this->GetOutput()->GetDirection()[2][2] < 0) - // dir[2] *= -1; - // itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(m_CstDir, dir); - itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(dir, m_CstDir); - - vnl_matrix rotationMatrix; - rotationMatrix.set_size(3,3); - for(int x = 0; x < 3; ++x) - for(int y = 0; y < 3; ++y) - rotationMatrix.put(x, y, itkRotationMatrix[y][x]); - - // vnl_matrix rotationMatrix = this->GetRotationMatrix(CstDir, dir); - std::vector eulerAngles; - anima::GetEulerAnglesFromRotationMatrix(rotationMatrix, eulerAngles); - - rotatedModel = tmp; - - vnl_matrix ODFRotationMatrix; - for (unsigned int l = 0;l <= m_LOrder;l += 2) + template + void + TODEstimatorImageFilter::AverageODFs(std::vector &vecCoefs, OutputImagePixelType &resOdf) { - anima::EstimateLocalODFRotationMatrix(ODFRotationMatrix, l, eulerAngles[0],eulerAngles[1],eulerAngles[2]); + int numImages = vecCoefs.size(); + + std::vector> odfHistos(numImages); + std::vector vecSqrtCoefs(numImages); + std::vector avgSqrtHisto(m_NbSample); + OutputImagePixelType avgSqrtCoefs(m_VectorLength); - unsigned int mBaseInd = (l*l + l + 2)/2 - l - 1; - for (unsigned int m = 0;m <= 2*l;++m) + for (int i = 0; i < numImages; i++) { - rotatedModel[mBaseInd + m] = 0; + odfHistos[i].resize(m_NbSample); - for (unsigned int mp = 0;mp <= 2*l;++mp) - rotatedModel[mBaseInd + m] += ODFRotationMatrix(m,mp)*tmp[mBaseInd + mp]; + this->DiscretizeODF(vecCoefs[i], odfHistos[i]); + vecSqrtCoefs[i] = GetSquareRootODF(odfHistos[i]); } - } - coefs = rotatedModel; -} - -void TODEstimatorImageFilter::averageODFs(std::vector &vecCoefs, VectorType &resOdf) -{ - int numImages = vecCoefs.size(); - - std::vector> odfHistos(numImages); - std::vector vecSqrtCoefs(numImages); - std::vector avgSqrtHisto(m_NbSample); - VectorType avgSqrtCoefs(m_VectorLength); - - for(int i = 0; i < numImages; i++) - { - odfHistos[i].resize(m_NbSample); - - this->discretizeODF(vecCoefs[i], odfHistos[i]); - vecSqrtCoefs[i] = getSquareRootODF(odfHistos[i]); + this->GetAverageCoefs(vecSqrtCoefs, avgSqrtCoefs); + this->DiscretizeODF(avgSqrtCoefs, avgSqrtHisto); + resOdf = this->GetSquareODF(avgSqrtHisto); } - this->getAverageCoefs(vecSqrtCoefs, avgSqrtCoefs); - this->discretizeODF(avgSqrtCoefs, avgSqrtHisto); - resOdf = this->getSquareODF(avgSqrtHisto); -} - -void TODEstimatorImageFilter::precomputeSH() -{ - int k = 0; - - std::vector tmpDir(2), revDir(3); - for(int i = 0; i < m_NbSample; i++) + template + void + TODEstimatorImageFilter::PrecomputeSH() { - anima::TransformCartesianToSphericalCoordinates(m_SphereSampl[i], tmpDir); - int c = 0; - for(double l = 0; l <= m_LOrder; l+=2) + int k = 0; + + std::vector tmpDir(2), revDir(3); + for (int i = 0; i < m_NbSample; i++) { - for(double m = -l; m <= l; m++) + anima::TransformCartesianToSphericalCoordinates(m_SphereSampl[i], tmpDir); + int c = 0; + for (double l = 0; l <= m_LOrder; l += 2) { - m_SpherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, tmpDir[0], tmpDir[1]); + for (double m = -l; m <= l; m++) + { + m_SpherHarm(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, tmpDir[0], tmpDir[1]); + } } + k++; } - k++; } -} - -void TODEstimatorImageFilter::discretizeODF(VectorType ODFCoefs, std::vector &ODFDiscret) -{ - int k = 0; - double resVal2 = 0; - - std::vector tmpDir(2), revDir(3); - for(int i = 0; i < m_NbSample; i++) + template + void + TODEstimatorImageFilter::DiscretizeODF(OutputImagePixelType ODFCoefs, std::vector &ODFDiscret) { - anima::TransformCartesianToSphericalCoordinates(m_SphereSampl[i], tmpDir); - resVal2 = m_ODFSHBasis->getValueAtPosition(ODFCoefs, tmpDir[0], tmpDir[1]); - - ODFDiscret[i] = resVal2; - k++; - } -} - - - -typename TODEstimatorImageFilter::VectorType TODEstimatorImageFilter::getSquareRootODF(std::vector ODFDiscret) -{ - vnl_matrix Odf(m_NbSample, 1); - vnl_matrix Coef(m_VectorLength, 1); - - for(int i = 0; i < m_NbSample; ++i) - Odf(i, 0) = std::sqrt(ODFDiscret[i]); + int k = 0; + double resVal2 = 0; - Coef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose()*Odf; + std::vector tmpDir(2), revDir(3); + for (int i = 0; i < m_NbSample; i++) + { + anima::TransformCartesianToSphericalCoordinates(m_SphereSampl[i], tmpDir); + resVal2 = m_ODFSHBasis->getValueAtPosition(ODFCoefs, tmpDir[0], tmpDir[1]); - VectorType ModelValue(m_VectorLength); - for(int i = 0; i < m_VectorLength; ++i) - { - ModelValue[i] = Coef(i, 0); + ODFDiscret[i] = resVal2; + k++; + } } - return ModelValue; -} - - -typename TODEstimatorImageFilter::VectorType -TODEstimatorImageFilter::getSquareODF(std::vector ODFDiscret) -{ - vnl_matrix Odf(m_NbSample, 1); - vnl_matrix Coef(m_VectorLength, 1); - - for(int i = 0; i < m_NbSample; ++i) - Odf(i, 0) = ODFDiscret[i] * ODFDiscret[i]; - Coef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose()*Odf; - - VectorType ModelValue(m_VectorLength); - for(int i = 0; i < m_VectorLength; ++i) + template + typename TODEstimatorImageFilter::OutputImagePixelType + TODEstimatorImageFilter::GetSquareRootODF(std::vector ODFDiscret) { - ModelValue[i] = Coef(i, 0); - } + vnl_matrix Odf(m_NbSample, 1); + vnl_matrix Coef(m_VectorLength, 1); - ModelValue[0] = 1/(2*sqrt(M_PI)); - return ModelValue; -} - - -void -TODEstimatorImageFilter::getAverageCoefs(std::vector &vecCoefs, VectorType &avgCoef) -{ - int numImage = vecCoefs.size(); + for (int i = 0; i < m_NbSample; ++i) + Odf(i, 0) = std::sqrt(ODFDiscret[i]); - std::vector> arrayCoef(numImage), arrayLogMap(numImage); - std::vector expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); + Coef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose() * Odf; - const int T = 100; - - for(int i = 0; i < numImage; i++) - { - arrayCoef[i].resize(m_VectorLength); - arrayLogMap[i].resize(m_VectorLength); - - for(int n = 0; n < m_VectorLength; n++) + OutputImagePixelType ModelValue(m_VectorLength); + for (int i = 0; i < m_VectorLength; ++i) { - arrayCoef[i][n] = vecCoefs[i][n]; - // nextMean[n] += 1/(double)numImage * arrayCoef[i][n]; + ModelValue[i] = Coef(i, 0); } + return ModelValue; } - nextMean = arrayCoef[0]; - - for(int t = 0; t < T; t++) + template + typename TODEstimatorImageFilter::OutputImagePixelType + TODEstimatorImageFilter::GetSquareODF(std::vector ODFDiscret) { - mean = nextMean; + vnl_matrix Odf(m_NbSample, 1); + vnl_matrix Coef(m_VectorLength, 1); - for(int i = 0; i < numImage; i++) - anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); - - std::fill(tangent.begin(), tangent.end(), 0); - for(int n = 0; n < m_VectorLength; n++) - for(int i = 0; i < numImage; i++) - tangent[n] += 1/(double)numImage * arrayLogMap[i][n]; - - anima::sphere_exp_map(tangent, mean, nextMean); - } + for (int i = 0; i < m_NbSample; ++i) + Odf(i, 0) = ODFDiscret[i] * ODFDiscret[i]; - // nextMean[0] = 2.82094792e-01; - for(int i = 0; i < m_VectorLength; ++i) - avgCoef[i] = nextMean[i]; -} + Coef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose() * Odf; + OutputImagePixelType ModelValue(m_VectorLength); + for (int i = 0; i < m_VectorLength; ++i) + { + ModelValue[i] = Coef(i, 0); + } + ModelValue[0] = 1 / (2 * sqrt(M_PI)); + return ModelValue; + } -void TODEstimatorImageFilter::processFiber(FiberType &fiber, baseSH &basis) -{ - const int T = (m_LOrder+1)*(m_LOrder+2)/2; - VectorType tmp, resSH, rotatedModel; - tmp.SetSize(T); - resSH.SetSize(T); - rotatedModel.SetSize(T); - // itk::Vector tmp, resSH; - DirType CstDir; - CstDir[0] = 0; - CstDir[1] = 0; - CstDir[2] = 1; - - - TOutputImage *output = this->GetOutput(); - int nbPoints = fiber.size(); - - for(int i = 0; i < nbPoints; i++) + template + void + TODEstimatorImageFilter::GetAverageCoefs(std::vector &vecCoefs, OutputImagePixelType &avgCoef) { - DirType dir = getFiberDirection(i, fiber); - // OutputImagePointer::DirectionType refDir = output->GetDirection(); - - if(output->GetDirection()[0][0] > 0) - dir[0] *= -1; - if(output->GetDirection()[1][1] < 0) - dir[1] *= -1; - if(output->GetDirection()[2][2] < 0) - dir[2] *= -1; + int numImage = vecCoefs.size(); + std::vector> arrayCoef(numImage), arrayLogMap(numImage); + std::vector expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); + const int T = 100; - int pos = 0; - for(int k = 0; k <= m_LOrder; k+=2) + for (int i = 0; i < numImage; i++) { - for(int m = -k; m <= k; m ++) + arrayCoef[i].resize(m_VectorLength); + arrayLogMap[i].resize(m_VectorLength); + + for (int n = 0; n < m_VectorLength; n++) { - tmp[pos] = m_GaussCoefs[pos]; - pos++; + arrayCoef[i][n] = vecCoefs[i][n]; } } - itk::Matrix itkRotationMatrix = anima::GetRotationMatrixFromVectors(CstDir, dir); - vnl_matrix rotationMatrix; - rotationMatrix.set_size(3,3); - for(int x = 0; x < 3; ++x) - for(int y = 0; y < 3; ++y) - rotationMatrix.put(x, y, itkRotationMatrix[y][x]); - - // vnl_matrix rotationMatrix = this->GetRotationMatrix(CstDir, dir); - std::vector eulerAngles; - anima::GetEulerAnglesFromRotationMatrix(rotationMatrix, eulerAngles); - - rotatedModel = tmp; + nextMean = arrayCoef[0]; - vnl_matrix ODFRotationMatrix; - for (unsigned int l = 0;l <= m_LOrder;l += 2) + for (int t = 0; t < T; t++) { - anima::EstimateLocalODFRotationMatrix(ODFRotationMatrix, l, eulerAngles[0],eulerAngles[1],eulerAngles[2]); + mean = nextMean; - unsigned int mBaseInd = (l*l + l + 2)/2 - l - 1; - for (unsigned int m = 0;m <= 2*l;++m) - { - rotatedModel[mBaseInd + m] = 0; + for (int i = 0; i < numImage; i++) + anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); - for (unsigned int mp = 0;mp <= 2*l;++mp) - rotatedModel[mBaseInd + m] += ODFRotationMatrix(m,mp)*tmp[mBaseInd + mp]; - } - } - PointType point = getCenterVoxel(i, fiber); - itk::Index<3> index; + std::fill(tangent.begin(), tangent.end(), 0); + for (int n = 0; n < m_VectorLength; n++) + for (int i = 0; i < numImage; i++) + tangent[n] += 1 / (double)numImage * arrayLogMap[i][n]; - output->TransformPhysicalPointToIndex(point, index); - // index[0] *= -1; - // index[1] *= -1; - // resSH = (output->GetPixel(index) + tmp); - // resSH /= resSH[0]; - output->SetPixel(index, rotatedModel); - - resSH.Fill(0); - tmp.Fill(0); - rotatedModel.Fill(0); + anima::sphere_exp_map(tangent, mean, nextMean); + } + // nextMean[0] = 2.82094792e-01; + for (int i = 0; i < m_VectorLength; ++i) + avgCoef[i] = nextMean[i]; } -} - -typename TODEstimatorImageFilter::DirType -TODEstimatorImageFilter::getFiberDirection(int index, FiberType &fiber) -{ - DirType resDir; - if(index == 0) - { - for(int i = 0; i < 3; i++) - resDir[i] = fiber[index+1][i] - fiber[index][i]; - } - else if(index == fiber.size() - 1) + template + typename TODEstimatorImageFilter::DirType + TODEstimatorImageFilter::GetFiberDirection(int index, FiberType &fiber) { - for(int i = 0; i < 3; i++) - resDir[i] = fiber[index][i] - fiber[index-1][i]; - } - else - { - for(int i = 0; i < 3; i++) - resDir[i] = fiber[index-1][i] - fiber[index+1][i]; - } - - return resDir; -} - + DirType resDir; + if (index == fiber.size() - 1) + { + for (int i = 0; i < 3; i++) + resDir[i] = fiber[index][i] - fiber[index - 1][i]; + } + else + { + for (int i = 0; i < 3; i++) + resDir[i] = fiber[index + 1][i] - fiber[index][i]; + } -typename TODEstimatorImageFilter::PointType -TODEstimatorImageFilter::getCenterVoxel(int index, FiberType &fiber) -{ - PointType resPoint; - for(int i = 0; i < 3; i++) - { - resPoint[i] = fiber[index][i]; + anima::Normalize(resDir, resDir); + return resDir; } - return resPoint; -} - - -typename TODEstimatorImageFilter::FiberType TODEstimatorImageFilter::readFiber(vtkIdType numberOfPoints, const vtkIdType *indices, vtkPoints *points) -{ - FiberType fiber; - // fiber.resize(numberOfPoints); - for (vtkIdType i = 0; i < numberOfPoints; i++) + template + typename TODEstimatorImageFilter::PointType + TODEstimatorImageFilter::GetCenterVoxel(int index, FiberType &fiber) { - double tmpPoint[3]; - points->GetPoint(indices[i], tmpPoint); - PointType point; - for(int i = 0; i < 3; i++) - point[i] = tmpPoint[i]; + PointType resPoint; + for (int i = 0; i < 3; i++) + { + resPoint[i] = fiber[index][i]; + } - fiber.push_back(tmpPoint); + return resPoint; } - return fiber; -} - - -vnl_matrix TODEstimatorImageFilter::GetRotationMatrix(DirType dir1, DirType dir2) -{ - DirType nu; - anima::ComputeCrossProduct(dir2, dir1, nu); - - double s = anima::ComputeNorm(nu); - double c = anima::ComputeScalarProduct(dir2, dir1); - - vnl_matrix R; - vnl_matrix MatNu; - vnl_matrix SqrMatNu; - MatNu.set_size(3,3); - MatNu.fill(0.0); - - MatNu.put(0,1,-nu[2]); - MatNu.put(0,2, nu[1]); - MatNu.put(1,0, nu[2]); - MatNu.put(1,2,-nu[0]); - MatNu.put(2,0,-nu[1]); - MatNu.put(2,1, nu[0]); - - - // MatNu[1][0] = -nu[2]; - // MatNu[2][0] = nu[1]; - // MatNu[0][1] = nu[2]; - // MatNu[2][1] = -nu[0]; - // MatNu[0][2] = -nu[1]; - // MatNu[1][2] = nu[0]; - - SqrMatNu = MatNu*MatNu; - - R.set_size(3,3); - R.set_identity(); - R = R + MatNu + SqrMatNu*(1-c)/(s*s); - // R = R + MatNu + SqrMatNu*(1/1+c); + template + typename TODEstimatorImageFilter::FiberType + TODEstimatorImageFilter::ReadFiber(vtkIdType numberOfPoints, const vtkIdType *indices, vtkPoints *points) + { + FiberType fiber; + for (vtkIdType i = 0; i < numberOfPoints; i++) + { + double tmpPoint[3]; + points->GetPoint(indices[i], tmpPoint); + PointType point; + for (int i = 0; i < 3; i++) + point[i] = tmpPoint[i]; - return R; + fiber.push_back(tmpPoint); + } -} + return fiber; + } } // end namespace anima - From b790fd73c1c0083bd08f2d30f13b47faf71541a2 Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 16 Nov 2023 15:16:26 +0100 Subject: [PATCH 07/11] Clean ODF average tool. --- Anima/diffusion/odf/CMakeLists.txt | 2 +- .../diffusion/odf/odf_average/CMakeLists.txt | 29 + .../odf/odf_average/animaODFAverage.cxx | 199 ++++++ .../animaODFAverageImageFilter.cxx | 302 +++++++++ .../odf_average/animaODFAverageImageFilter.h | 99 +++ .../odf/odf_average_images/CMakeLists.txt | 36 - .../animaODFAverageImages.cxx | 202 ------ .../animaODFAverageImagesImageFilter.cxx | 395 ----------- .../animaODFAverageImagesImageFilter.h | 119 ---- .../odf/odf_estimator/animaODFEstimator.cxx | 193 ++++-- .../animaODFEstimatorImageFilter.h | 193 +++--- .../animaODFEstimatorImageFilter.hxx | 619 +++++++++--------- .../odf/tod_estimator/animaTODEstimator.cxx | 12 +- .../animaTODEstimatorImageFilter.hxx | 1 - 14 files changed, 1169 insertions(+), 1232 deletions(-) create mode 100644 Anima/diffusion/odf/odf_average/CMakeLists.txt create mode 100644 Anima/diffusion/odf/odf_average/animaODFAverage.cxx create mode 100644 Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx create mode 100644 Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h delete mode 100644 Anima/diffusion/odf/odf_average_images/CMakeLists.txt delete mode 100644 Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx delete mode 100644 Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx delete mode 100644 Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h diff --git a/Anima/diffusion/odf/CMakeLists.txt b/Anima/diffusion/odf/CMakeLists.txt index d0cf7948b..9a7212709 100644 --- a/Anima/diffusion/odf/CMakeLists.txt +++ b/Anima/diffusion/odf/CMakeLists.txt @@ -1,4 +1,4 @@ add_subdirectory(generalized_fa) add_subdirectory(odf_estimator) add_subdirectory(tod_estimator) -add_subdirectory(odf_average_images) \ No newline at end of file +add_subdirectory(odf_average) \ No newline at end of file diff --git a/Anima/diffusion/odf/odf_average/CMakeLists.txt b/Anima/diffusion/odf/odf_average/CMakeLists.txt new file mode 100644 index 000000000..5bbdce61a --- /dev/null +++ b/Anima/diffusion/odf/odf_average/CMakeLists.txt @@ -0,0 +1,29 @@ +if(BUILD_TOOLS) + + project(animaODFAverage) + + # ############################################################################ + # List Sources + # ############################################################################ + + list_source_files(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}) + + # ############################################################################ + # add executable + # ############################################################################ + + add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_CFILES}) + + # ############################################################################ + # Link + # ############################################################################ + + target_link_libraries(${PROJECT_NAME} ${ITKIO_LIBRARIES} AnimaSHTools) + + # ############################################################################ + # install + # ############################################################################ + + set_exe_install_rules(${PROJECT_NAME}) + +endif() diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx new file mode 100644 index 000000000..ea03caa3d --- /dev/null +++ b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx @@ -0,0 +1,199 @@ +#include +#include + +#include + +#include + +#include + +// Update progression of the process +void eventCallback(itk::Object *caller, const itk::EventObject &event, void *clientData) +{ + itk::ProcessObject *processObject = (itk::ProcessObject *)caller; + std::cout << "\033[K\rProgression: " << (int)(processObject->GetProgress() * 100) << "%" << std::flush; +} + +int main(int argc, char **argv) +{ + TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ', ANIMA_VERSION); + + TCLAP::ValueArg inArg( + "i", "input-odf", + "ODF images list as a text file", + true, "", "ODF images list", cmd); + TCLAP::ValueArg maskArg( + "m", "masks", + "Masks images list as a text file", + true, "", "Masks images list", cmd); + + TCLAP::ValueArg resArg( + "o", "output", + "Result average ODF", + true, "", "result ODF image", cmd); + TCLAP::ValueArg resMaskArg( + "M", "resMask", + "Result average mask", + false, "", "average mask", cmd); + + TCLAP::ValueArg barycenterWeightArg( + "b", "barycenterWeight", + "Flag for iterative barycenter atlasing - Result number of average per pixel", + false, "", "number average image", cmd); + TCLAP::ValueArg weightImageArg( + "W", "weightImage", + "Image containing the first image weight for each voxel", + false, "", "weight Image", cmd); + + TCLAP::ValueArg weightArg( + "w", "weight", + "In the case of 2 image averaging, scalar weight of the first image ", + false, 0.0, "first image weight", cmd); + + TCLAP::ValueArg nbpArg( + "T", "nb-threads", + "An integer value specifying the number of threads to run on (default: all cores).", + false, itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(), "number of threads", cmd); + + try + { + cmd.parse(argc, argv); + } + catch (TCLAP::ArgException &e) + { + std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; + return EXIT_FAILURE; + } + + itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); + callback->SetCallback(eventCallback); + + std::ifstream odfFile(inArg.getValue().c_str()); + if (!odfFile.is_open()) + { + std::cerr << "Please provide usable file with input ODFs" << std::endl; + return EXIT_FAILURE; + } + + std::ifstream maskFile(maskArg.getValue().c_str()); + if (!maskFile.is_open()) + { + std::cerr << "Please provide usable file with input Masks" << std::endl; + return EXIT_FAILURE; + } + + using FilterType = anima::ODFAverageImageFilter; + using InputImageType = FilterType::InputImageType; + using OutputImageType = FilterType::OutputImageType; + using MaskImageType = FilterType::MaskImageType; + using DoubleImageType = FilterType::DoubleImageType; + + FilterType::Pointer mainFilter = FilterType::New(); + + std::vector inputFiles; + std::vector maskFiles; + unsigned int numInput = 0; + while (!odfFile.eof()) + { + char tmpStr[2048], maskStr[2048]; + odfFile.getline(tmpStr, 2048); + maskFile.getline(maskStr, 2048); + + if (strcmp(tmpStr, "") == 0) + continue; + + inputFiles.push_back(tmpStr); + maskFiles.push_back(maskStr); + numInput++; + } + odfFile.close(); + maskFile.close(); + + if (weightArg.getValue() != 0.0) + mainFilter->SetWeightValue(weightArg.getValue()); + + MaskImageType::Pointer geomImage = anima::readImage(maskFiles[0]); + + std::cout << "Processing image : " << inputFiles[1] << std::endl; + + if (weightImageArg.getValue() != "") + mainFilter->SetWeightImage(anima::readImage(weightImageArg.getValue())); + + if (barycenterWeightArg.getValue() != "") + { + MaskImageType::Pointer barycenterWeightImage = MaskImageType::New(); + barycenterWeightImage->Initialize(); + barycenterWeightImage->SetDirection(geomImage->GetDirection()); + barycenterWeightImage->SetSpacing(geomImage->GetSpacing()); + barycenterWeightImage->SetOrigin(geomImage->GetOrigin()); + MaskImageType::RegionType region = geomImage->GetLargestPossibleRegion(); + barycenterWeightImage->SetRegions(region); + barycenterWeightImage->Allocate(); + barycenterWeightImage->FillBuffer(0); + mainFilter->SetBarycenterWeightImage(barycenterWeightImage); + } + + mainFilter->SetInput(0, anima::readImage(inputFiles[0])); + mainFilter->SetInput(1, anima::readImage(inputFiles[1])); + + mainFilter->AddMaskImage(0, anima::readImage(maskFiles[0])); + mainFilter->AddMaskImage(1, anima::readImage(maskFiles[1])); + + mainFilter->AddObserver(itk::ProgressEvent(), callback); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + itk::TimeProbe tmpTimer; + + tmpTimer.Start(); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + + for (int i = 2; i < numInput; i++) + { + + std::cout << std::endl + << "Processing image : " << inputFiles[i] << std::endl; + mainFilter->SetBarycenterWeightImage(mainFilter->GetBarycenterWeightImage()); + + mainFilter->SetInput(0, mainFilter->GetOutput()); + mainFilter->SetInput(1, anima::readImage(inputFiles[i])); + + mainFilter->AddMaskImage(0, mainFilter->GetMaskAverage()); + mainFilter->AddMaskImage(1, anima::readImage(maskFiles[i])); + + mainFilter->AddObserver(itk::ProgressEvent(), callback); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + } + + tmpTimer.Stop(); + + std::cout << "\nAveraging done in " << tmpTimer.GetTotal() << "s" << std::endl; + + anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); + + if (barycenterWeightArg.getValue() != "") + anima::writeImage(barycenterWeightArg.getValue(), mainFilter->GetBarycenterWeightImage()); + + if (resMaskArg.getValue() != "") + anima::writeImage(resMaskArg.getValue(), mainFilter->GetMaskAverage()); + + return EXIT_SUCCESS; +} diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx new file mode 100644 index 000000000..606c84cd1 --- /dev/null +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx @@ -0,0 +1,302 @@ +#include "animaODFAverageImageFilter.h" +#include +#include + +#include + +namespace anima +{ + + void ODFAverageImageFilter::AddMaskImage(const unsigned int i, const MaskImagePointer &mask) + { + if (i == m_MaskImages.size()) + { + m_MaskImages.push_back(mask); + return; + } + + if (i > m_MaskImages.size()) + itkExceptionMacro("Trying to add a non contiguous mask... Add mask images contiguously (0,1,2,3,...)..."); + + m_MaskImages[i] = mask; + } + + void ODFAverageImageFilter::BeforeThreadedGenerateData() + { + Superclass::BeforeThreadedGenerateData(); + + m_HistoODFs.resize(this->GetNumberOfIndexedInputs()); + for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i) + m_HistoODFs[i].resize(m_NbSamplesPhi * m_NbSamplesTheta); + + m_ODFSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8 * this->GetInput(0)->GetVectorLength() + 1)); + m_VectorLength = (m_ODFSHOrder + 1) * (m_ODFSHOrder + 2) / 2; + + m_SpherHarm.set_size(m_NbSamplesPhi * m_NbSamplesTheta, m_VectorLength); + m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_ODFSHOrder); + + this->DiscretizeSH(); + } + + void ODFAverageImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) + { + using InputIteratorType = itk::ImageRegionConstIterator; + using InputMaskIteratorType = itk::ImageRegionConstIterator; + using DoubleIteratorType = itk::ImageRegionConstIterator; + using OutputIteratorType = itk::ImageRegionIterator; + using OutputMaskIteratorType = itk::ImageRegionIterator; + using OutputDoubleIteratorType = itk::ImageRegionIterator; + + double weight0 = 0.5, weight1 = 0.5; + + unsigned int numInputs = this->GetNumberOfIndexedInputs(); + std::vector inIterators(numInputs); + std::vector maskIterators(numInputs); + for (unsigned int i = 0; i < numInputs; ++i) + { + inIterators[i] = InputIteratorType(this->GetInput(i), outputRegionForThread); + maskIterators[i] = InputMaskIteratorType(m_MaskImages[i], outputRegionForThread); + } + OutputIteratorType outIterator(this->GetOutput(), outputRegionForThread); + + OutputMaskIteratorType barycenterWeightItr; + if (m_BarycenterWeightImage) + barycenterWeightItr = OutputMaskIteratorType(m_BarycenterWeightImage, outputRegionForThread); + + OutputDoubleIteratorType weightImgIt; + if (m_WeightImage) + weightImgIt = OutputDoubleIteratorType(m_WeightImage, outputRegionForThread); + + std::vector arrayCoef(numInputs), arraySQRTCoef(numInputs); + + IOVectorType nullVector(m_VectorLength); + nullVector.Fill(0.0); + + while (!outIterator.IsAtEnd()) + { + if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 0) + outIterator.Set(nullVector); + else if (maskIterators[0].Get() == 1 && maskIterators[1].Get() == 0) + { + outIterator.Set(inIterators[0].Get()); + } + else if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 1) + { + outIterator.Set(inIterators[1].Get()); + } + else + { + for (unsigned int i = 0; i < numInputs; ++i) + { + arrayCoef[i] = inIterators[i].Get(); + this->DiscretizeODF(arrayCoef[i], m_HistoODFs[i]); + arraySQRTCoef[i] = this->GetSquareRootODFCoef(m_HistoODFs[i]); + } + + if (m_BarycenterWeightImage) + { + double k = barycenterWeightItr.Get(); + weight1 = (k + 1.0) / (k + 2.0); + weight0 = 1.0 - weight1; + + barycenterWeightItr.Set(barycenterWeightItr.Get() + 1.0); + } + else if (m_WeightImage) + { + weight0 = weightImgIt.Get(); + weight1 = 1.0 - weight0; + } + else if (m_WeightValue != 0.0) + { + weight0 = m_WeightValue; + weight1 = 1.0 - weight1; + } + + IOVectorType averageSQRTCoef(m_VectorLength); + this->GetAverageHisto(arraySQRTCoef, averageSQRTCoef, weight0, weight1); + + VectorType averageSQRTHisto(m_NbSamplesPhi * m_NbSamplesTheta); + this->DiscretizeODF(averageSQRTCoef, averageSQRTHisto); + + IOVectorType averageCoef(m_VectorLength); + averageCoef = this->GetSquareODFCoef(averageSQRTHisto); + + outIterator.Set(averageCoef); + } + + for (unsigned int i = 0; i < numInputs; ++i) + { + ++inIterators[i]; + ++maskIterators[i]; + } + + if (m_BarycenterWeightImage) + ++barycenterWeightItr; + if (m_WeightImage) + ++weightImgIt; + + ++outIterator; + + this->IncrementNumberOfProcessedPoints(); + } + } + + void ODFAverageImageFilter::AfterThreadedGenerateData() + { + delete m_ODFSHBasis; + } + + void ODFAverageImageFilter::DiscretizeSH() + { + double sqrt2 = std::sqrt(2); + double deltaPhi = 2.0 * M_PI / (static_cast(m_NbSamplesPhi) - 1.0); + double deltaTheta = M_PI / (static_cast(m_NbSamplesTheta) - 1.0); + + unsigned int k = 0; + for (unsigned int i = 0; i < m_NbSamplesTheta; ++i) + { + double theta = static_cast(i) * deltaTheta; + + for (unsigned int j = 0; j < m_NbSamplesPhi; ++j) + { + double phi = static_cast(j) * deltaPhi; + unsigned int c = 0; + for (double l = 0; l <= m_ODFSHOrder; l += 2) + for (double m = -l; m <= l; m++) + m_SpherHarm.put(k, c++, m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi)); + k++; + } + } + } + + void ODFAverageImageFilter::DiscretizeODF(const IOVectorType &modelValue, VectorType &odf) + { + unsigned int k = 0; + double deltaPhi = 2.0 * M_PI / (static_cast(m_NbSamplesPhi) - 1.0); + double deltaTheta = M_PI / (static_cast(m_NbSamplesTheta) - 1.0); + + for (unsigned int i = 0; i < m_NbSamplesTheta; ++i) + { + double theta = static_cast(i) * deltaTheta; + for (unsigned int j = 0; j < m_NbSamplesPhi; ++j) + { + double phi = static_cast(j) * deltaPhi; + odf[k] = m_ODFSHBasis->getValueAtPosition(modelValue, theta, phi); + k++; + } + } + } + + ODFAverageImageFilter::IOVectorType ODFAverageImageFilter::GetSquareRootODFCoef(const VectorType &odf) + { + MatrixType squareRootOdf(m_NbSamplesTheta * m_NbSamplesPhi, 1); + MatrixType squareRootCoef(m_VectorLength, 1); + + for (unsigned int i = 0; i < m_NbSamplesTheta * m_NbSamplesPhi; ++i) + squareRootOdf(i, 0) = std::sqrt(std::max(0.0, odf[i])); + + squareRootCoef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose() * squareRootOdf; + + IOVectorType squareRootModelValue(m_VectorLength); + for (unsigned int i = 0; i < m_VectorLength; ++i) + squareRootModelValue[i] = squareRootCoef(i, 0); + + return squareRootModelValue; + } + + ODFAverageImageFilter::IOVectorType ODFAverageImageFilter::GetSquareODFCoef(const VectorType &odf) + { + MatrixType squareOdf(m_NbSamplesTheta * m_NbSamplesPhi, 1); + MatrixType squareCoef(m_VectorLength, 1); + + for (unsigned int i = 0; i < m_NbSamplesTheta * m_NbSamplesPhi; ++i) + squareOdf(i, 0) = std::pow(odf[i], 2); + + squareCoef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose() * squareOdf; + + IOVectorType squareModelValue(m_VectorLength); + for (unsigned int i = 0; i < m_VectorLength; ++i) + squareModelValue[i] = squareCoef(i, 0); + + squareModelValue[0] = 1.0 / (2.0 * std::sqrt(M_PI)); + + return squareModelValue; + } + + void ODFAverageImageFilter::GetAverageHisto(const std::vector &coefs, IOVectorType &resCoef, double weight0, double weight1) + { + unsigned int numImage = this->GetNumberOfIndexedInputs(); + + HistoArrayType arrayCoef(numImage), arrayLogMap(numImage); + VectorType expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); + + const unsigned int T = 100; + const double eps = 0.000035; + + for (unsigned int i = 0; i < numImage; ++i) + { + arrayCoef[i].resize(m_VectorLength); + arrayLogMap[i].resize(m_VectorLength); + + for (unsigned int n = 0; n < m_VectorLength; ++n) + arrayCoef[i][n] = coefs[i][n]; + } + + nextMean = arrayCoef[0]; + + unsigned int t = 0; + double normTan = 1.0; + while (t < T && normTan > eps) + { + mean = nextMean; + + for (unsigned int i = 0; i < numImage; ++i) + anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); + + std::fill(tangent.begin(), tangent.end(), 0); + for (unsigned int n = 0; n < m_VectorLength; ++n) + tangent[n] += weight1 * arrayLogMap[1][n] + weight0 * arrayLogMap[0][n]; + + normTan = anima::ComputeNorm(tangent); + anima::sphere_exp_map(tangent, mean, nextMean); + + t++; + } + + for (unsigned int i = 0; i < m_VectorLength; ++i) + resCoef[i] = nextMean[i]; + } + + ODFAverageImageFilter::MaskImageType *ODFAverageImageFilter::GetMaskAverage() + { + MaskImageType::Pointer maskAverage = MaskImageType::New(); + maskAverage->Initialize(); + maskAverage->SetDirection(m_MaskImages[0]->GetDirection()); + maskAverage->SetSpacing(m_MaskImages[0]->GetSpacing()); + maskAverage->SetOrigin(m_MaskImages[0]->GetOrigin()); + MaskImageType::RegionType region = m_MaskImages[0]->GetLargestPossibleRegion(); + maskAverage->SetRegions(region); + maskAverage->Allocate(); + maskAverage->FillBuffer(0); + + using InputMaskIteratorType = itk::ImageRegionConstIterator; + using OutputMaskIteratorType = itk::ImageRegionIterator; + + OutputMaskIteratorType averageIt(maskAverage, maskAverage->GetLargestPossibleRegion()); + InputMaskIteratorType mask0It(m_MaskImages[0], m_MaskImages[0]->GetLargestPossibleRegion()); + InputMaskIteratorType mask1It(m_MaskImages[1], m_MaskImages[1]->GetLargestPossibleRegion()); + + while (!averageIt.IsAtEnd()) + { + averageIt.Set(mask0It.Get() || mask1It.Get()); + + ++averageIt; + ++mask0It; + ++mask1It; + } + + m_MaskImages.clear(); + return maskAverage; + } + +} // end namespace anima diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h new file mode 100644 index 000000000..38c831e1e --- /dev/null +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h @@ -0,0 +1,99 @@ +#pragma once + +#include +#include + +#include + +namespace anima +{ + class ODFAverageImageFilter : public anima::NumberedThreadImageToImageFilter, itk::VectorImage> + { + public: + using Self = ODFAverageImageFilter; + using ScalarPixelType = double; + using PixelMaskType = unsigned int; + using InputImageType = itk::VectorImage; + using OutputImageType = itk::VectorImage; + using DoubleImageType = itk::Image; + using MaskImageType = itk::Image; + using Superclass = anima::NumberedThreadImageToImageFilter; + using Pointer = itk::SmartPointer; + using ConstPointer = itk::SmartPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods) */ + itkTypeMacro(ODFAverageImageFilter, anima::NumberedThreadImageToImageFilter); + + /** Image typedef support */ + using InputImagePointer = InputImageType::Pointer; + using OutputImagePointer = OutputImageType::Pointer; + using MaskImagePointer = MaskImageType::Pointer; + using OutputPixelType = OutputImageType::PixelType; + using DoubleImagePointer = DoubleImageType::Pointer; + + /** Superclass typedefs. */ + using InputImageRegionType = Superclass::InputImageRegionType; + using OutputImageRegionType = Superclass::OutputImageRegionType; + + using IOVectorType = OutputImageType::PixelType; + using VectorType = std::vector; + using HistoArrayType = std::vector; + using MatrixType = vnl_matrix; + + void AddMaskImage(const unsigned int i, const MaskImagePointer &maskImage); + + void SetBarycenterWeightImage(const MaskImagePointer &weightImage) { m_BarycenterWeightImage = weightImage; } + void SetWeightImage(const DoubleImagePointer &img) { m_WeightImage = img; } + MaskImageType *GetBarycenterWeightImage() { return m_BarycenterWeightImage; } + MaskImageType *GetMaskAverage(); + + itkSetMacro(WeightValue, double); + + protected: + ODFAverageImageFilter() : Superclass() + { + m_NbSamplesTheta = 10; + m_NbSamplesPhi = 2 * m_NbSamplesTheta; + m_WeightValue = 0.0; + m_MaskImages.clear(); + m_HistoODFs.clear(); + m_SpherHarm.clear(); + } + + virtual ~ODFAverageImageFilter() {} + + void BeforeThreadedGenerateData() ITK_OVERRIDE; + void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; + void AfterThreadedGenerateData() ITK_OVERRIDE; + + void DiscretizeSH(); + void DiscretizeODF(const IOVectorType &Coef, VectorType &resHisto); + void GetAverageHisto(const std::vector &coefs, IOVectorType &resCoef, double smallWeight, double bigWeight); + IOVectorType GetSquareRootODFCoef(const VectorType &histo); + IOVectorType GetSquareODFCoef(const VectorType &histo); + + private: + ITK_DISALLOW_COPY_AND_ASSIGN(ODFAverageImageFilter); + + unsigned int m_NbSamplesPhi; + unsigned int m_NbSamplesTheta; + unsigned int m_VectorLength; + + double m_ODFSHOrder; + double m_WeightValue; + + MaskImagePointer m_BarycenterWeightImage; + DoubleImagePointer m_WeightImage; + + std::vector m_MaskImages; + + MatrixType m_SpherHarm; + + anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; + + HistoArrayType m_HistoODFs; + }; +} // end of namespace anima diff --git a/Anima/diffusion/odf/odf_average_images/CMakeLists.txt b/Anima/diffusion/odf/odf_average_images/CMakeLists.txt deleted file mode 100644 index 14ad1dae1..000000000 --- a/Anima/diffusion/odf/odf_average_images/CMakeLists.txt +++ /dev/null @@ -1,36 +0,0 @@ -if(BUILD_TOOLS) - -project(animaODFAverageImages) - -## ############################################################################# -## List Sources -## ############################################################################# - -list_source_files(${PROJECT_NAME} - ${CMAKE_CURRENT_SOURCE_DIR} - ) - -## ############################################################################# -## add executable -## ############################################################################# - -add_executable(${PROJECT_NAME} - ${${PROJECT_NAME}_CFILES} - ) - -## ############################################################################# -## Link -## ############################################################################# - -target_link_libraries(${PROJECT_NAME} - ${ITKIO_LIBRARIES} - AnimaSHTools - ) - -## ############################################################################# -## install -## ############################################################################# - -set_exe_install_rules(${PROJECT_NAME}) - -endif() diff --git a/Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx b/Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx deleted file mode 100644 index e0a708df5..000000000 --- a/Anima/diffusion/odf/odf_average_images/animaODFAverageImages.cxx +++ /dev/null @@ -1,202 +0,0 @@ -#include - -#include -#include - -#include - -#include - -//Update progression of the process -void eventCallback (itk::Object* caller, const itk::EventObject& event, void* clientData) -{ - itk::ProcessObject * processObject = (itk::ProcessObject*) caller; - std::cout<<"\033[K\rProgression: "<<(int)(processObject->GetProgress() * 100)<<"%"< inArg("i", "input-odfs", - "A text file listing the names of the ODF images.", - true, "", "ODF image list", cmd); - TCLAP::ValueArg maskArg("m", "input-masks", - "A text file listing the names of the mask images.", - true, "", "Mask image list", cmd); - TCLAP::ValueArg resArg("o", "output-average-odf", - "The name of the file where the average ODF image will be written.", - true, "", "average ODF image", cmd); - TCLAP::ValueArg resMaskArg("", "output-average-mask", - "The name of the file where the average mask image will be written.", - false, "", "average mask image", cmd); - TCLAP::ValueArg resWeightArg("", "output-weight", - "The name of the file where the weight image will be written.", - false, "", "weight image", cmd); - TCLAP::ValueArg resPondArg("", "output-ponderation", - "The name of the file where the ponderation image will be written.", - false, "", "ponderation image", cmd); - TCLAP::ValueArg aicImageArg("", "input-aic-image", - "The name of the input AIC image.", - false, "", "aic image", cmd); - TCLAP::ValueArg weightArg("", "input-weight-image", - "The name of the inpuy first image weight.", - false, 0.0, "first image weight as image",cmd); - TCLAP::ValueArg testArg("", "input-weight-value", - "A number specifying a global weight for the first image.", - false, 0.0, "first image weight as scalar", cmd); - TCLAP::SwitchArg gfaArg("", "use-gfa", - "Activates the use of GFA of the atlas (first image) to ponderate", - cmd, false); - TCLAP::ValueArg nbpArg("", "nthreads", - "An integer specifying the number of threads to run on (default: all cores).", - false, itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(), "number of threads", cmd); - - try - { - cmd.parse(argc,argv); - } - catch (TCLAP::ArgException& e) - { - std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; - return EXIT_FAILURE; - } - - itk::CStyleCommand::Pointer callback = itk::CStyleCommand::New(); - callback->SetCallback(eventCallback); - - std::ifstream odfFile(inArg.getValue().c_str()); - if (!odfFile.is_open()) - { - std::cerr << "Please provide usable file with input ODFs" << std::endl; - return EXIT_FAILURE; - } - - std::ifstream maskFile(maskArg.getValue().c_str()); - if (!maskFile.is_open()) - { - std::cerr << "Please provide usable file with input Masks" << std::endl; - return EXIT_FAILURE; - } - - using FilterType = anima::ODFAverageImageFilter; - using InputImageType = FilterType::InputImageType; - using OutputImageType = FilterType::OutputImageType; - using MaskImageType = FilterType::MaskImageType; - using DoubleImageType = FilterType::DoubleImageType; - - FilterType::Pointer mainFilter = FilterType::New(); - - std::vector inputFiles; - std::vector maskFiles; - unsigned int numInput = 0; - while (!odfFile.eof()) - { - char tmpStr[2048], maskStr[2048]; - odfFile.getline(tmpStr,2048); - maskFile.getline(maskStr, 2048); - - if (strcmp(tmpStr,"") == 0) - continue; - - inputFiles.push_back(tmpStr); - maskFiles.push_back(maskStr); - numInput++; - } - odfFile.close(); - maskFile.close(); - - if (weightArg.getValue() != 0.0) - mainFilter->SetWeight(weightArg.getValue()); - - mainFilter->SetUseGFA(gfaArg.isSet()); - - if(aicImageArg.getValue() != "") - mainFilter->SetAICImage(anima::readImage(aicImageArg.getValue())); - mainFilter->SetTestCombi(testArg.getValue()); - - MaskImageType::Pointer geomImage = anima::readImage(maskFiles[0]); - - std::cout << "Processing image : " << inputFiles[1] << std::endl; - - if (resWeightArg.getValue() != "") - { - MaskImageType::Pointer weightImage = MaskImageType::New(); - weightImage->Initialize(); - weightImage->SetDirection(geomImage->GetDirection()); - weightImage->SetSpacing(geomImage->GetSpacing()); - weightImage->SetOrigin(geomImage->GetOrigin()); - MaskImageType::RegionType region = geomImage->GetLargestPossibleRegion(); - weightImage->SetRegions(region); - weightImage->Allocate(); - weightImage->FillBuffer(0); - mainFilter->SetWeightImage(weightImage); - } - mainFilter->SetInput(0, anima::readImage(inputFiles[0])); - mainFilter->SetInput(1, anima::readImage(inputFiles[1])); - - mainFilter->SetMaskImage(0, anima::readImage(maskFiles[0])); - mainFilter->SetMaskImage(1, anima::readImage(maskFiles[1])); - - mainFilter->AddObserver(itk::ProgressEvent(), callback); - mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); - - itk::TimeProbe tmpTimer; - - tmpTimer.Start(); - - try - { - mainFilter->Update(); - } - catch (itk::ExceptionObject &e) - { - std::cerr << e << std::endl; - return EXIT_FAILURE; - } - - for (unsigned int i = 2;i < numInput;++i) - { - std::cout << std::endl << "Processing image : " << inputFiles[i] << std::endl; - mainFilter->SetWeightImage(mainFilter->GetWeightImage()); - - mainFilter->SetInput(0, mainFilter->GetOutput()); - mainFilter->SetInput(1, anima::readImage(inputFiles[i])); - - mainFilter->SetMaskImage(0, mainFilter->GetAverageMaskImage()); - mainFilter->SetMaskImage(1, anima::readImage(maskFiles[i])); - - mainFilter->AddObserver(itk::ProgressEvent(), callback); - mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); - - try - { - mainFilter->Update(); - } - catch (itk::ExceptionObject &e) - { - std::cerr << e << std::endl; - return EXIT_FAILURE; - } - } - - tmpTimer.Stop(); - - std::cout << "\nAveraging done in " << tmpTimer.GetTotal() << " s" << std::endl; - - anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); - - if (resWeightArg.getValue() != "") - anima::writeImage(resWeightArg.getValue(), mainFilter->GetWeightImage()); - - if (resMaskArg.getValue() != "") - { - MaskImageType::Pointer maskOut = mainFilter->GetAverageMaskImage(); - anima::writeImage(resMaskArg.getValue(), maskOut.GetPointer()); - } - - if (resPondArg.getValue() != "") - anima::writeImage(resPondArg.getValue(), mainFilter->GetPonderationImage()); - - return EXIT_SUCCESS; -} diff --git a/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx b/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx deleted file mode 100644 index da67e5413..000000000 --- a/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.cxx +++ /dev/null @@ -1,395 +0,0 @@ -#include "animaODFAverageImagesImageFilter.h" - -#include -#include "animaLogExpMapsUnitSphere.h" - -#include -#include - -namespace anima -{ - -void ODFAverageImageFilter::SetMaskImage(unsigned int i, MaskImagePointer mask) -{ - if (i == m_MaskImages.size()) - m_MaskImages.push_back(mask); - else if (i > m_MaskImages.size()) - { - itkExceptionMacro("Trying to add a non contiguous mask... Add mask images contiguously (0,1,2,3,...)..."); - } - else - m_MaskImages[i] = mask; -} - - -void ODFAverageImageFilter::BeforeThreadedGenerateData() -{ - Superclass::BeforeThreadedGenerateData(); - - m_PonderationImage = DoubleImageType::New(); - m_PonderationImage->Initialize(); - m_PonderationImage->SetDirection(m_MaskImages[0]->GetDirection()); - m_PonderationImage->SetSpacing(m_MaskImages[0]->GetSpacing()); - m_PonderationImage->SetOrigin(m_MaskImages[0]->GetOrigin()); - MaskImageType::RegionType region = m_MaskImages[0]->GetLargestPossibleRegion(); - m_PonderationImage->SetRegions(region); - m_PonderationImage->Allocate(); - m_PonderationImage->FillBuffer(0); - - m_MinAICValue = 10.0; - - m_HistoODFs.resize(this->GetNumberOfIndexedInputs()); - m_SqrtHistoODFs.resize(this->GetNumberOfIndexedInputs()); - - for (unsigned int i = 0;i < this->GetNumberOfIndexedInputs();++i) - { - m_HistoODFs[i].resize(m_PhiGridSize * m_ThetaGridSize); - m_SqrtHistoODFs[i].resize(m_PhiGridSize * m_ThetaGridSize); - } - - m_ODFSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8 * this->GetInput(0)->GetVectorLength() + 1)); - m_VectorLength = (m_ODFSHOrder + 1) * (m_ODFSHOrder + 2) / 2; - - m_SHValues.set_size(m_PhiGridSize * m_ThetaGridSize, m_VectorLength); - m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_ODFSHOrder); - - this->DiscretizeSH(); - -} -void ODFAverageImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) -{ - typedef itk::ImageRegionConstIterator ImageIteratorType; - typedef itk::ImageRegionConstIterator MaskIteratorType; - typedef itk::ImageRegionConstIterator DoubleIteratorType; - - static double progress = 0; - double weight0 = 0.5, weight1 = 0.5; - - unsigned int numInputs = this->GetNumberOfIndexedInputs(); - std::vector inIterators(numInputs); - std::vector maskIterators(numInputs); - for (unsigned int i = 0;i < numInputs;++i) - { - inIterators[i] = ImageIteratorType(this->GetInput(i),outputRegionForThread); - maskIterators[i] = MaskIteratorType(m_MaskImages[i], outputRegionForThread); - } - typedef itk::ImageRegionIterator OutImageIteratorType; - OutImageIteratorType outIterator(this->GetOutput(),outputRegionForThread); - - itk::ImageRegionIterator weightIt; - if (m_WeightImage) - weightIt = itk::ImageRegionIterator(m_WeightImage, outputRegionForThread); - - itk::ImageRegionIterator aicIt; - if (m_AICImage) - aicIt = itk::ImageRegionIterator (m_AICImage, outputRegionForThread); - - itk::ImageRegionIterator pondIt(m_PonderationImage, outputRegionForThread); - - std::vector arrayCoef(numInputs), arraySQRTCoef(numInputs); - - VectorType nullVector(m_VectorLength); - nullVector.Fill(0.0); - - while(!outIterator.IsAtEnd()) - { - if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 0) - outIterator.Set(nullVector); - else if (maskIterators[0].Get() == 1 && maskIterators[1].Get() == 0) - outIterator.Set(inIterators[0].Get()); - else if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 1) - outIterator.Set(inIterators[1].Get()); - else - { - for (unsigned int i = 0;i < numInputs;++i) - { - arrayCoef[i] = inIterators[i].Get(); - this->DiscretizeODF(arrayCoef[i], m_HistoODFs[i]); - arraySQRTCoef[i] = this->GetSquareRootODFCoef(m_HistoODFs[i]); - } - - if (m_WeightImage) - { - double k = weightIt.Get(); - weight1 = (k + 1.0) / (k + 2.0); - weight0 = 1.0 - weight1; - weightIt.Set(weightIt.Get() + 1.0); - } - else if (m_Weight != 0.0) - { - weight0 = m_Weight; - weight1 = 1 - weight1; // AST: weight0 ? - } - else if (m_UseGFA && m_AICImage) - { - double tmpAic = aicIt.Get(); - double aic; - if(tmpAic == 0.0) - aic = 0.0; - else - aic = std::exp((m_MinAICValue - tmpAic) / 2000.0); - double gfa = 0.8*this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); - - weight0 = std::exp((m_TestCombi * gfa + (1.0 - m_TestCombi) * aic) - (m_TestCombi + (1.0 - m_TestCombi))); - weight1 = 1.0 - weight0; - - pondIt.Set(weight0); - } - else if (m_UseGFA) - { - double gfa = this->GetGeneralizedFractionalAnisotropy(arrayCoef[0]); - weight0 = 1.0 - gfa; - weight1 = 1.0 - weight0; - pondIt.Set(weight0); - if (weight0 == 0.0) - auto test = outIterator.GetIndex(); - } - else if (m_AICImage) - { - double aic = aicIt.Get(); - if(aic == 0) - weight1 = 1.0; - else - weight1 = std::exp((m_MinAICValue - aic) / 1); - - weight0 = 1.0 - weight1; - pondIt.Set(weight0); - if (weight0 == 0.0) - auto test = outIterator.GetIndex(); - } - - VectorType averageSQRTCoef(m_VectorLength); - this->GetAverageHisto(arraySQRTCoef, averageSQRTCoef, weight0, weight1); - - std::vector averageSQRTHisto(m_PhiGridSize * m_ThetaGridSize); - this->DiscretizeODF(averageSQRTCoef, averageSQRTHisto); - - VectorType averageCoef(m_VectorLength); - averageCoef = this->GetSquareODFCoef(averageSQRTHisto); - - outIterator.Set(averageCoef); - } - - for (unsigned int i = 0;i < numInputs;++i) - { - ++inIterators[i]; - ++maskIterators[i]; - } - - if (m_WeightImage) - ++weightIt; - - if (m_AICImage) - ++aicIt; - - ++outIterator; - ++pondIt; - - this->IncrementNumberOfProcessedPoints(); - } -} - - -void ODFAverageImageFilter::AfterThreadedGenerateData() -{ - delete m_ODFSHBasis; -} - -void ODFAverageImageFilter::DiscretizeSH() -{ - double sqrt2 = std::sqrt(2); - double theta, phi; - double deltaPhi = 2.0 * M_PI / static_cast(m_PhiGridSize - 1.0); - double deltaTheta = M_PI / static_cast(m_ThetaGridSize - 1.0); - - unsigned int k = 0; - for (unsigned int i = 0;i < m_ThetaGridSize;++i) - { - theta = static_cast(i) * deltaTheta; - - for (unsigned int j = 0;j < m_PhiGridSize;++j) - { - phi = static_cast(j) * deltaPhi; - unsigned int c = 0; - for (int l = 0;l <= m_ODFSHOrder;l+=2) - { - for (int m = -l;m <= l;++m) - m_SHValues(k, c++) = m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi); - } - k++; - } - } -} - -void ODFAverageImageFilter::DiscretizeODF(VectorType &modelValue, std::vector &odf) -{ - unsigned int flag = 0, k = 0; - double theta, phi; - double resVal2 = 0.0; - - double deltaPhi = 2.0 * M_PI / static_cast(m_PhiGridSize - 1.0); - double deltaTheta = M_PI / static_cast(m_ThetaGridSize - 1.0); - - for (unsigned int i = 0;i < m_ThetaGridSize;++i) - { - for (unsigned int j = 0;j < m_PhiGridSize;++j) - { - phi = static_cast(j) * deltaPhi; - theta = static_cast(i) * deltaTheta; - resVal2 = m_ODFSHBasis->getValueAtPosition(modelValue, theta, phi); - - if (resVal2 < 0) - flag++; - - odf[k] = resVal2; - k++; - } - } -} - -ODFAverageImageFilter::VectorType ODFAverageImageFilter::GetSquareRootODFCoef(std::vector &odf) -{ - vnl_matrix squareRootOdf(m_ThetaGridSize * m_PhiGridSize, 1.0); - vnl_matrix squareRootCoef(m_VectorLength, 1.0); - - for (unsigned int i = 0;i < m_ThetaGridSize * m_PhiGridSize;++i) - { - if (odf[i] < 0) - odf[i] = 0; - squareRootOdf(i, 0) = std::sqrt(odf[i]); - } - squareRootCoef = vnl_matrix_inverse(m_SHValues.transpose() * m_SHValues).as_matrix() * m_SHValues.transpose() * squareRootOdf; - - VectorType squareRootModelValue(m_VectorLength); - std::vector test(m_VectorLength); - for (unsigned int i = 0;i < m_VectorLength;++i) - { - squareRootModelValue[i] = squareRootCoef(i, 0); - test[i] = squareRootCoef(i, 0); - } - - return squareRootModelValue; -} - -ODFAverageImageFilter::VectorType ODFAverageImageFilter::GetSquareODFCoef(std::vector &odf) -{ - vnl_matrix squareOdf(m_ThetaGridSize * m_PhiGridSize, 1.0); - vnl_matrix squareCoef(m_VectorLength, 1.0); - - for (unsigned int i = 0;i < m_ThetaGridSize * m_PhiGridSize;++i) - squareOdf(i, 0) = std::pow(odf[i], 2.0); - - squareCoef = vnl_matrix_inverse(m_SHValues.transpose() * m_SHValues).as_matrix() * m_SHValues.transpose() * squareOdf; - - VectorType squareModelValue(m_VectorLength); - std::vector test(m_VectorLength); - for (unsigned int i = 0;i < m_VectorLength;++i) - squareModelValue[i] = squareCoef(i, 0); - - squareModelValue[0] = 1.0 / (2.0 * sqrt(M_PI)); - - return squareModelValue; -} - -void ODFAverageImageFilter::GetAverageHisto(std::vector &coefs, ODFAverageImageFilter::VectorType &resCoef, double weight0, double weight1) -{ - unsigned int numImage = this->GetNumberOfIndexedInputs(); - - std::vector> arrayCoef(numImage), arrayLogMap(numImage); - std::vector expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); - - const int T = 100; - const double eps = 0.000035; - - for (unsigned int i = 0;i < numImage;++i) - { - arrayCoef[i].resize(m_VectorLength); - arrayLogMap[i].resize(m_VectorLength); - - for (unsigned int n = 0;n < m_VectorLength;++n) - arrayCoef[i][n] = coefs[i][n]; - } - - nextMean = arrayCoef[0]; - - unsigned int t = 0; - double normTan = 1.0; - while(t < T && normTan > eps) - { - mean = nextMean; - - for (unsigned int i = 0;i < numImage;++i) - anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); - - std::fill(tangent.begin(), tangent.end(), 0.0); - for (unsigned int n = 0;n < m_VectorLength;++n) - tangent[n] += weight1 * arrayLogMap[1][n] + weight0 * arrayLogMap[0][n]; - - normTan = anima::ComputeNorm(tangent); - anima::sphere_exp_map(tangent, mean, nextMean); - - t++; - } - - for (unsigned int i = 0;i < m_VectorLength;++i) - resCoef[i] = nextMean[i]; -} - -void ODFAverageImageFilter::GetAverageODF(std::vector> &histos, ODFAverageImageFilter::VectorType &resCoef, double smallWeight, double bigWeight) -{ - vnl_matrix meanODF(m_ThetaGridSize * m_PhiGridSize, 1.0); - vnl_matrix meanCoef(m_VectorLength, 1.0); - - for (unsigned int i = 0;i < m_ThetaGridSize * m_PhiGridSize;++i) - meanODF(i, 0) = (histos[0][i] + histos[1][i]) / 2.0; - - meanCoef = vnl_matrix_inverse(m_SHValues.transpose() * m_SHValues).as_matrix() * m_SHValues.transpose() * meanODF; - - std::vector test(m_VectorLength); - meanCoef(0, 0) = 2.82094792e-01; - for (unsigned int i = 0;i < m_VectorLength;++i) - resCoef[i] = meanCoef(i, 0); -} - -ODFAverageImageFilter::MaskImagePointer ODFAverageImageFilter::GetAverageMaskImage() -{ - MaskImageType::Pointer maskAverage = MaskImageType::New(); - maskAverage->Initialize(); - maskAverage->SetDirection(m_MaskImages[0]->GetDirection()); - maskAverage->SetSpacing(m_MaskImages[0]->GetSpacing()); - maskAverage->SetOrigin(m_MaskImages[0]->GetOrigin()); - MaskImageType::RegionType region = m_MaskImages[0]->GetLargestPossibleRegion(); - maskAverage->SetRegions(region); - maskAverage->Allocate(); - maskAverage->FillBuffer(0); - - typedef itk::ImageRegionConstIterator MaskIteratorType; - - itk::ImageRegionIterator averageIt(maskAverage, maskAverage->GetLargestPossibleRegion()); - MaskIteratorType mask0It(m_MaskImages[0], m_MaskImages[0]->GetLargestPossibleRegion()); - MaskIteratorType mask1It(m_MaskImages[1], m_MaskImages[1]->GetLargestPossibleRegion()); - - while(!averageIt.IsAtEnd()) - { - averageIt.Set(mask0It.Get() || mask1It.Get()); - - ++averageIt; - ++mask0It; - ++mask1It; - } - - m_MaskImages.clear(); - return maskAverage; -} - -double ODFAverageImageFilter::GetGeneralizedFractionalAnisotropy(ODFAverageImageFilter::VectorType &modelValue) -{ - double sumSquares = 0; - for (unsigned int i = 0;i < this->m_VectorLength;++i) - sumSquares += modelValue[i] * modelValue[i]; - - return std::sqrt(1.0 - modelValue[0] * modelValue[0] / sumSquares); - -} - -} // end namespace anima diff --git a/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h b/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h deleted file mode 100644 index 469147de7..000000000 --- a/Anima/diffusion/odf/odf_average_images/animaODFAverageImagesImageFilter.h +++ /dev/null @@ -1,119 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include - -#include -#include - -#include - -namespace anima -{ -class ODFAverageImageFilter : -public anima::NumberedThreadImageToImageFilter < itk::VectorImage, itk::VectorImage > -{ -public: - typedef ODFAverageImageFilter Self; - typedef double PixelScalarType; - typedef unsigned int PixelMaskType; - typedef itk::VectorImage InputImageType; - typedef itk::VectorImage OutputImageType; - typedef itk::Image DoubleImageType; - typedef itk::Image MaskImageType; - typedef anima::NumberedThreadImageToImageFilter Superclass; - typedef itk::SmartPointer Pointer; - typedef itk::SmartPointer ConstPointer; - - /** Method for creation through the object factory. */ - itkNewMacro(Self) - - /** Run-time type information (and related methods) */ - itkTypeMacro(ODFAverageImageFilter, anima::NumberedThreadImageToImageFilter) - - /** Image typedef support */ - typedef typename InputImageType::Pointer InputImagePointer; - typedef typename OutputImageType::Pointer OutputImagePointer; - typedef typename MaskImageType::Pointer MaskImagePointer; - typedef typename OutputImageType::PixelType OutputPixelType; - typedef typename DoubleImageType::Pointer DoubleImagePointer; - - /** Superclass typedefs. */ - typedef typename Superclass::InputImageRegionType InputImageRegionType; - typedef typename Superclass::OutputImageRegionType OutputImageRegionType; - - typedef itk::VariableLengthVector VectorType; - typedef std::vector> HistoArrayType; - - void SetMaskImage(unsigned int i, MaskImagePointer mask); - - void SetWeightImage(const MaskImagePointer weightImage) {m_WeightImage = weightImage;} - MaskImageType *GetWeightImage() {return m_WeightImage;} - DoubleImageType *GetPonderationImage() {return m_PonderationImage;} - MaskImagePointer GetAverageMaskImage(); - - itkSetMacro(Weight, double) - itkSetMacro(TestCombi, double) - itkSetMacro(UseGFA, bool) - itkSetMacro(AICImage, DoubleImagePointer) - -protected: - ODFAverageImageFilter() - : Superclass() - { - m_ThetaGridSize = 10; - m_PhiGridSize = 2 * m_ThetaGridSize; - m_Weight = 0.0; - m_UseGFA = false; - } - - virtual ~ODFAverageImageFilter() {} - - void BeforeThreadedGenerateData() ITK_OVERRIDE; - void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; - void AfterThreadedGenerateData() ITK_OVERRIDE; - - void DiscretizeSH(); - void DiscretizeODF(VectorType &Coef, std::vector &resHisto); - void GetAverageHisto(std::vector &coefs, VectorType &resCoef, double smallWeight, double bigWeight); - void GetAverageODF(std::vector> &histos, VectorType &resCoef, double smallWeight, double bigWeight); - VectorType GetSquareRootODFCoef(std::vector &histo); - VectorType GetSquareODFCoef(std::vector &histo); - - double GetGeneralizedFractionalAnisotropy(VectorType &modelValue); - -private: - ITK_DISALLOW_COPY_AND_ASSIGN(ODFAverageImageFilter); - - unsigned int m_PhiGridSize; - unsigned int m_ThetaGridSize; - unsigned int m_VectorLength; - - double m_ODFSHOrder; - double m_SmallWeight; - double m_BigWeight; - double m_Weight; - double m_MinAICValue; - double m_TestCombi; - - bool m_UseGFA; - - MaskImagePointer m_WeightImage; - DoubleImagePointer m_PonderationImage; - DoubleImagePointer m_AICImage; - - std::vector m_MaskImages; - - vnl_matrix m_SHValues; - - anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; - HistoArrayType m_HistoODFs, m_SqrtHistoODFs; -}; -} // end of namespace anima - - diff --git a/Anima/diffusion/odf/odf_estimator/animaODFEstimator.cxx b/Anima/diffusion/odf/odf_estimator/animaODFEstimator.cxx index c8edfeecd..d0b150556 100644 --- a/Anima/diffusion/odf/odf_estimator/animaODFEstimator.cxx +++ b/Anima/diffusion/odf/odf_estimator/animaODFEstimator.cxx @@ -1,76 +1,127 @@ +#include #include +#include + #include -#include -#include -#include +#include int main(int argc, char **argv) { - TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ',ANIMA_VERSION); - - TCLAP::ValueArg inArg("i","input","List of diffusion weighted images or 4D volume",true,"","input diffusion images",cmd); - TCLAP::ValueArg resArg("o","outputfile","Result ODF image",true,"","result ODF image",cmd); - TCLAP::ValueArg b0OutArg("O","output-b0","output_b0",false,"","result B0 image",cmd); - TCLAP::ValueArg varOutArg("V","output-variance","output_variance",false,"","result noise variance image",cmd); - - TCLAP::ValueArg gradArg("g","gradientlist","List of gradients (text file)",true,"","list of gradients",cmd); - TCLAP::ValueArg bvalArg("b","bval","input b-values (for checking single shell)",true,"","Input b-values",cmd); - TCLAP::ValueArg refB0Arg("r","ref-b0","Externally estimated B0 (otherwise mean of B0s is used)",false,"","External B0 image",cmd); - TCLAP::SwitchArg bvalueScaleArg("B","b-no-scale","Do not scale b-values according to gradient norm",cmd); - TCLAP::ValueArg selectedBvalArg("v","select-bval","B-value shell used to estimate ODFs (default: first one in data volume above 10)",false,-1,"b-value shell selection",cmd); - - TCLAP::ValueArg lambdaArg("l","lambda","Lambda regularization parameter (see Descoteaux MRM 2007)",false,0.006,"lambda for regularization",cmd); - TCLAP::ValueArg orderArg("k","order","Order of spherical harmonics basis",false,4,"Order of SH basis",cmd); - - TCLAP::ValueArg sharpFactorArg("s","sharpenratio","Ratio for sharpening ODFs (see Descoteaux TMI 2009, default : 0.255)",false,0.255,"sharpening ratio",cmd); - TCLAP::SwitchArg sharpenArg("S","sharpenodf","Sharpen ODF ? (default: no)",cmd,false); - - TCLAP::ValueArg normSphereArg("n","normalizefile","Sphere tesselation for normalization",false,"","Normalization sphere file",cmd); - TCLAP::SwitchArg normalizeArg("N","normalize","Normalize ODF ? (default: no)",cmd,false); - - TCLAP::SwitchArg radialArg("R","radialestimation","Use radial estimation (see Aganj et al) ? (default: no)",cmd,false); - TCLAP::ValueArg aganjRegFactorArg("d","adr","Delta threshold for signal regularization, only use if R option activated (see Aganj et al, default : 0.001)",false,0.001,"delta signal regularization",cmd); - - TCLAP::ValueArg nbpArg("p","numberofthreads","Number of threads to run on (default: all cores)",false,itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(),"number of threads",cmd); - + TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ', ANIMA_VERSION); + + TCLAP::ValueArg inArg( + "i", "input", + "List of diffusion weighted images or 4D volume", + true, "", "input diffusion images", cmd); + TCLAP::ValueArg resArg( + "o", "outputfile", + "Result ODF image", + true, "", "result ODF image", cmd); + TCLAP::ValueArg b0OutArg( + "O", "output-b0", + "output_b0", + false, "", "result B0 image", cmd); + TCLAP::ValueArg varOutArg( + "V", "output-variance", + "output_variance", + false, "", "result noise variance image", cmd); + + TCLAP::ValueArg gradArg( + "g", "gradientlist", + "List of gradients (text file)", + true, "", "list of gradients", cmd); + TCLAP::ValueArg bvalArg( + "b", "bval", + "input b-values (for checking single shell)", + true, "", "Input b-values", cmd); + TCLAP::ValueArg refB0Arg( + "r", "ref-b0", + "Externally estimated B0 (otherwise mean of B0s is used)", + false, "", "External B0 image", cmd); + TCLAP::SwitchArg bvalueScaleArg( + "B", "b-no-scale", + "Do not scale b-values according to gradient norm", + cmd); + TCLAP::ValueArg selectedBvalArg("v", "select-bval", "B-value shell used to estimate ODFs (default: first one in data volume above 10)", false, -1, "b-value shell selection", cmd); + + TCLAP::ValueArg lambdaArg( + "l", "lambda", + "Lambda regularization parameter (see Descoteaux MRM 2007)", + false, 0.006, "lambda for regularization", cmd); + TCLAP::ValueArg orderArg( + "k", "order", + "Order of spherical harmonics basis", + false, 4, "Order of SH basis", cmd); + + TCLAP::ValueArg sharpFactorArg( + "s", "sharpenratio", + "Ratio for sharpening ODFs (see Descoteaux TMI 2009, default : 0.255)", + false, 0.255, "sharpening ratio", cmd); + TCLAP::SwitchArg sharpenArg( + "S", "sharpenodf", + "Sharpen ODF ? (default: no)", cmd, false); + + TCLAP::ValueArg normSphereArg( + "n", "normalizefile", + "Sphere tesselation for normalization", + false, "", "Normalization sphere file", cmd); + TCLAP::SwitchArg normalizeArg( + "N", "normalize", + "Normalize ODF ? (default: no)", + cmd, false); + + TCLAP::SwitchArg radialArg( + "R", "radialestimation", + "Use radial estimation (see Aganj et al) ? (default: no)", + cmd, false); + TCLAP::ValueArg aganjRegFactorArg( + "d", "adr", + "Delta threshold for signal regularization, only use if R option activated (see Aganj et al, default : 0.001)", + false, 0.001, "delta signal regularization", cmd); + + TCLAP::ValueArg nbpArg( + "T", "nb-threads", + "An integer value specifying the number of threads to run on (default: all cores).", + false, itk::MultiThreaderBase::GetGlobalDefaultNumberOfThreads(), "number of threads", cmd); + try { - cmd.parse(argc,argv); + cmd.parse(argc, argv); } - catch (TCLAP::ArgException& e) + catch (TCLAP::ArgException &e) { std::cerr << "Error: " << e.error() << "for argument " << e.argId() << std::endl; return EXIT_FAILURE; } - - typedef anima::ODFEstimatorImageFilter MainFilterType; - typedef MainFilterType::TInputImage InputImageType; - - MainFilterType::Pointer mainFilter = MainFilterType::New(); - mainFilter->SetLambda(lambdaArg.getValue()); - if (orderArg.getValue() % 2 == 0) - mainFilter->SetLOrder(orderArg.getValue()); - else - mainFilter->SetLOrder(orderArg.getValue() - 1); - - mainFilter->SetSharpen(sharpenArg.isSet()); - if (sharpenArg.isSet()) - mainFilter->SetSharpnessRatio(sharpFactorArg.getValue()); - - mainFilter->SetUseAganjEstimation(radialArg.isSet()); + + using MainFilterType = anima::ODFEstimatorImageFilter; + using InputImageType = MainFilterType::TInputImage; + + MainFilterType::Pointer mainFilter = MainFilterType::New(); + mainFilter->SetLambda(lambdaArg.getValue()); + if (orderArg.getValue() % 2 == 0) + mainFilter->SetLOrder(orderArg.getValue()); + else + mainFilter->SetLOrder(orderArg.getValue() - 1); + + mainFilter->SetSharpen(sharpenArg.isSet()); + if (sharpenArg.isSet()) + mainFilter->SetSharpnessRatio(sharpFactorArg.getValue()); + + mainFilter->SetUseAganjEstimation(radialArg.isSet()); mainFilter->SetDeltaAganjRegularization(aganjRegFactorArg.getValue()); - + mainFilter->SetNormalize(normalizeArg.isSet()); if (normalizeArg.isSet()) mainFilter->SetFileNameSphereTesselation(normSphereArg.getValue()); - - anima::setMultipleImageFilterInputsFromFileName(inArg.getValue(), mainFilter); - + + anima::setMultipleImageFilterInputsFromFileName(inArg.getValue(), mainFilter); + if (refB0Arg.getValue() != "") - mainFilter->SetReferenceB0Image(anima::readImage (refB0Arg.getValue())); + mainFilter->SetReferenceB0Image(anima::readImage(refB0Arg.getValue())); - typedef anima::GradientFileReader < std::vector < double >, double > GFReaderType; + using GFReaderType = anima::GradientFileReader, double>; GFReaderType gfReader; gfReader.SetGradientFileName(gradArg.getValue()); gfReader.SetBValueBaseString(bvalArg.getValue()); @@ -78,33 +129,31 @@ int main(int argc, char **argv) gfReader.SetB0ValueThreshold(10); gfReader.Update(); - + GFReaderType::GradientVectorType directions = gfReader.GetGradients(); GFReaderType::BValueVectorType mb = gfReader.GetBValues(); - for(unsigned int i = 0;i < directions.size();++i) - mainFilter->AddGradientDirection(i,directions[i]); + for (unsigned int i = 0; i < directions.size(); ++i) + mainFilter->AddGradientDirection(i, directions[i]); mainFilter->SetBValuesList(mb); mainFilter->SetBValueShellSelected(selectedBvalArg.getValue()); - - itk::TimeProbe tmpTime; - tmpTime.Start(); - - mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); - mainFilter->Update(); - - tmpTime.Stop(); - - std::cout << "Execution Time: " << tmpTime.GetTotal() << std::endl; - - anima::writeImage (resArg.getValue(),mainFilter->GetOutput()); + mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); + + itk::TimeProbe tmpTime; + tmpTime.Start(); + mainFilter->Update(); + tmpTime.Stop(); + + std::cout << "\nExecution Time: " << tmpTime.GetTotal() << "s" << std::endl; + + anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); if (b0OutArg.getValue() != "") - anima::writeImage (b0OutArg.getValue(),mainFilter->GetEstimatedB0Image()); + anima::writeImage(b0OutArg.getValue(), mainFilter->GetEstimatedB0Image()); if (varOutArg.getValue() != "") - anima::writeImage (varOutArg.getValue(),mainFilter->GetEstimatedVarianceImage()); + anima::writeImage(varOutArg.getValue(), mainFilter->GetEstimatedVarianceImage()); - return EXIT_SUCCESS; + return EXIT_SUCCESS; } diff --git a/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.h b/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.h index 028fe52dd..de78590cc 100644 --- a/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.h +++ b/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.h @@ -1,140 +1,143 @@ #pragma once -#include +#include #include #include -#include + +#include #include namespace anima { -template -class ODFEstimatorImageFilter : - public itk::ImageToImageFilter< itk::Image , itk::VectorImage > -{ -public: - /** Standard class typedefs. */ - typedef ODFEstimatorImageFilter Self; - typedef itk::Image TInputImage; - typedef itk::Image Image4DType; - typedef itk::Image OutputScalarImageType; - typedef itk::VectorImage TOutputImage; - typedef itk::ImageToImageFilter< TInputImage, TOutputImage > Superclass; - typedef itk::SmartPointer Pointer; - typedef itk::SmartPointer ConstPointer; + template + class ODFEstimatorImageFilter : public itk::ImageToImageFilter, itk::VectorImage> + { + public: + /** Standard class typedefs. */ + typedef ODFEstimatorImageFilter Self; + typedef itk::Image TInputImage; + typedef itk::Image Image4DType; + typedef itk::Image OutputScalarImageType; + typedef itk::VectorImage TOutputImage; + typedef itk::ImageToImageFilter Superclass; + typedef itk::SmartPointer Pointer; + typedef itk::SmartPointer ConstPointer; - /** Method for creation through the object factory. */ - itkNewMacro(Self); + /** Method for creation through the object factory. */ + itkNewMacro(Self); - /** Run-time type information (and related methods) */ - itkTypeMacro(ODFEstimatorImageFilter, ImageToImageFilter); + /** Run-time type information (and related methods) */ + itkTypeMacro(ODFEstimatorImageFilter, ImageToImageFilter); - typedef typename TInputImage::Pointer InputImagePointer; - typedef typename TOutputImage::Pointer OutputImagePointer; - typedef typename OutputScalarImageType::Pointer OutputScalarImagePointer; + typedef typename TInputImage::Pointer InputImagePointer; + typedef typename TOutputImage::Pointer OutputImagePointer; + typedef typename OutputScalarImageType::Pointer OutputScalarImagePointer; - /** Superclass typedefs. */ - typedef typename Superclass::OutputImageRegionType OutputImageRegionType; + /** Superclass typedefs. */ + typedef typename Superclass::OutputImageRegionType OutputImageRegionType; - void AddGradientDirection(unsigned int i, std::vector &grad); - void SetBValuesList(std::vector bValuesList) {m_BValuesList = bValuesList;} - itkSetMacro(BValueShellSelected, int) + void AddGradientDirection(unsigned int i, std::vector &grad); + void SetBValuesList(std::vector bValuesList) { m_BValuesList = bValuesList; } + itkSetMacro(BValueShellSelected, int); - itkSetMacro(Lambda,double); - itkSetMacro(LOrder,unsigned int); + itkSetMacro(Lambda, double); + itkSetMacro(LOrder, unsigned int); - itkSetMacro(SharpnessRatio,double); - itkSetMacro(Sharpen,bool); + itkSetMacro(SharpnessRatio, double); + itkSetMacro(Sharpen, bool); - itkSetMacro(Normalize,bool); - itkSetMacro(FileNameSphereTesselation,std::string); + itkSetMacro(Normalize, bool); + itkSetMacro(FileNameSphereTesselation, std::string); - itkSetMacro(UseAganjEstimation,bool); - itkSetMacro(DeltaAganjRegularization, double); + itkSetMacro(UseAganjEstimation, bool); + itkSetMacro(DeltaAganjRegularization, double); - itkGetMacro(EstimatedB0Image, OutputScalarImageType *) - itkGetMacro(EstimatedVarianceImage, OutputScalarImageType *) + itkGetMacro(EstimatedB0Image, OutputScalarImageType *); + itkGetMacro(EstimatedVarianceImage, OutputScalarImageType *); - void SetReferenceB0Image(TInputImage *refB0) {m_ReferenceB0Image = refB0;} + void SetReferenceB0Image(TInputImage *refB0) + { + m_ReferenceB0Image = refB0; + } -protected: - ODFEstimatorImageFilter() - { - m_GradientDirections.clear(); - m_PVector.clear(); - m_ReferenceB0Image = nullptr; + protected: + ODFEstimatorImageFilter() + { + m_GradientDirections.clear(); + m_PVector.clear(); + m_ReferenceB0Image = nullptr; - m_BValueShellSelected = -1; - m_BValueShellTolerance = 20; + m_BValueShellSelected = -1; + m_BValueShellTolerance = 20; - m_Lambda = 0.006; - m_DeltaAganjRegularization = 0.001; + m_Lambda = 0.006; + m_DeltaAganjRegularization = 0.001; - m_LOrder = 4; + m_LOrder = 4; - m_Sharpen = false; - m_SharpnessRatio = 0.255; + m_Sharpen = false; + m_SharpnessRatio = 0.255; - m_Normalize = false; - m_SphereSHSampling.clear(); + m_Normalize = false; + m_SphereSHSampling.clear(); - m_UseAganjEstimation = false; - } + m_UseAganjEstimation = false; + } - virtual ~ODFEstimatorImageFilter() {} + virtual ~ODFEstimatorImageFilter() {} - void GenerateOutputInformation() ITK_OVERRIDE; - void BeforeThreadedGenerateData() ITK_OVERRIDE; - void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; + void GenerateOutputInformation() ITK_OVERRIDE; + void BeforeThreadedGenerateData() ITK_OVERRIDE; + void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; -private: - ITK_DISALLOW_COPY_AND_ASSIGN(ODFEstimatorImageFilter); + private: + ITK_DISALLOW_COPY_AND_ASSIGN(ODFEstimatorImageFilter); - bool isZero(std::vector &testVal) - { - bool resVal = true; - for (unsigned int i = 0;i < testVal.size();++i) + bool isZero(std::vector &testVal) { - if (testVal[i] != 0) + bool resVal = true; + for (unsigned int i = 0; i < testVal.size(); ++i) { - resVal = false; - break; + if (testVal[i] != 0) + { + resVal = false; + break; + } } - } - return resVal; - } + return resVal; + } - std::vector < std::vector > m_GradientDirections; - std::vector m_BValuesList; - InputImagePointer m_ReferenceB0Image; + std::vector> m_GradientDirections; + std::vector m_BValuesList; + InputImagePointer m_ReferenceB0Image; - OutputScalarImagePointer m_EstimatedVarianceImage; - OutputScalarImagePointer m_EstimatedB0Image; + OutputScalarImagePointer m_EstimatedVarianceImage; + OutputScalarImagePointer m_EstimatedB0Image; - int m_BValueShellSelected; - double m_BValueShellTolerance; - std::vector m_SelectedDWIIndexes; + int m_BValueShellSelected; + double m_BValueShellTolerance; + std::vector m_SelectedDWIIndexes; - vnl_matrix m_TMatrix; // evaluation matrix computed once and for all before threaded generate data - vnl_matrix m_BMatrix; - std::vector m_DeconvolutionVector; - std::vector m_PVector; + vnl_matrix m_TMatrix; // evaluation matrix computed once and for all before threaded generate data + vnl_matrix m_BMatrix; + std::vector m_DeconvolutionVector; + std::vector m_PVector; - std::vector m_B0Indexes, m_GradientIndexes; + std::vector m_B0Indexes, m_GradientIndexes; - bool m_Normalize; - std::string m_FileNameSphereTesselation; - std::vector < std::vector > m_SphereSHSampling; + bool m_Normalize; + std::string m_FileNameSphereTesselation; + std::vector> m_SphereSHSampling; - double m_Lambda; - double m_SharpnessRatio; // See Descoteaux et al. TMI 2009, article plus appendix - bool m_Sharpen; - bool m_UseAganjEstimation; - double m_DeltaAganjRegularization; - unsigned int m_LOrder; -}; + double m_Lambda; + double m_SharpnessRatio; // See Descoteaux et al. TMI 2009, article plus appendix + bool m_Sharpen; + bool m_UseAganjEstimation; + double m_DeltaAganjRegularization; + unsigned int m_LOrder; + }; } // end of namespace anima diff --git a/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.hxx b/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.hxx index 18325b1d2..e4ec182fd 100644 --- a/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.hxx +++ b/Anima/diffusion/odf/odf_estimator/animaODFEstimatorImageFilter.hxx @@ -1,413 +1,412 @@ #pragma once -#include + +#include #include "animaODFEstimatorImageFilter.h" #include + #include #include + #include -#include -#include +#include +#include namespace anima { -template -void -ODFEstimatorImageFilter -::AddGradientDirection(unsigned int i, std::vector &grad) -{ - if (isZero(grad)) + template + void + ODFEstimatorImageFilter::AddGradientDirection(unsigned int i, std::vector &grad) { - m_B0Indexes.push_back(i); - return; - } - - m_GradientIndexes.push_back(i); + if (isZero(grad)) + { + m_B0Indexes.push_back(i); + return; + } - std::vector sphericalCoords; - anima::TransformCartesianToSphericalCoordinates(grad,sphericalCoords); - m_GradientDirections.push_back(sphericalCoords); -} + m_GradientIndexes.push_back(i); -template -void -ODFEstimatorImageFilter -::GenerateOutputInformation() -{ - // Override the method in itkImageSource, so we can set the vector length of - // the output itk::VectorImage + std::vector sphericalCoords; + anima::TransformCartesianToSphericalCoordinates(grad, sphericalCoords); + m_GradientDirections.push_back(sphericalCoords); + } - this->Superclass::GenerateOutputInformation(); + template + void + ODFEstimatorImageFilter::GenerateOutputInformation() + { + // Override the method in itkImageSource, so we can set the vector length of + // the output itk::VectorImage - unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2; - TOutputImage *output = this->GetOutput(); - output->SetVectorLength(vectorLength); -} + this->Superclass::GenerateOutputInformation(); -template -void -ODFEstimatorImageFilter -::BeforeThreadedGenerateData() -{ - unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2; - unsigned int numGrads = m_GradientDirections.size(); - - if ((m_GradientIndexes.size() + m_B0Indexes.size()) != this->GetNumberOfIndexedInputs()) - throw itk::ExceptionObject(__FILE__, __LINE__,"Number of gradient directions different from number of inputs",ITK_LOCATION); + unsigned int vectorLength = (m_LOrder + 1) * (m_LOrder + 2) / 2; + TOutputImage *output = this->GetOutput(); + output->SetVectorLength(vectorLength); + } - if (m_UseAganjEstimation) + template + void + ODFEstimatorImageFilter::BeforeThreadedGenerateData() { - m_Normalize = false; - m_Sharpen = false; - } + unsigned int vectorLength = (m_LOrder + 1) * (m_LOrder + 2) / 2; + unsigned int numGrads = m_GradientDirections.size(); - if (m_BValueShellSelected < 0) - m_BValueShellSelected = m_BValuesList[m_GradientIndexes[0]]; + if ((m_GradientIndexes.size() + m_B0Indexes.size()) != this->GetNumberOfIndexedInputs()) + throw itk::ExceptionObject(__FILE__, __LINE__, "Number of gradient directions different from number of inputs", ITK_LOCATION); - std::vector bvalKeptIndexes; - std::vector < std::vector > keptGradients; - // First filter out unwanted b-values (keeping only b = m_BValueShellSelected) - for (unsigned int i = 0;i < numGrads;++i) - { - if ((m_BValuesList[m_GradientIndexes[i]] >= m_BValueShellSelected - m_BValueShellTolerance) && - (m_BValuesList[m_GradientIndexes[i]] <= m_BValueShellSelected + m_BValueShellTolerance)) + if (m_UseAganjEstimation) { - bvalKeptIndexes.push_back(m_GradientIndexes[i]); - keptGradients.push_back(m_GradientDirections[i]); + m_Normalize = false; + m_Sharpen = false; } - } - m_GradientIndexes = bvalKeptIndexes; - m_GradientDirections = keptGradients; - numGrads = m_GradientIndexes.size(); + if (m_BValueShellSelected < 0) + m_BValueShellSelected = m_BValuesList[m_GradientIndexes[0]]; - std::cout << "Running ODF estimation using " << m_B0Indexes.size() << " B0 images and " << numGrads << " gradient images with b-value at " << m_BValueShellSelected << "s.mm^-2" << std::endl; - - m_EstimatedVarianceImage = OutputScalarImageType::New(); - m_EstimatedVarianceImage->Initialize(); - m_EstimatedVarianceImage->SetOrigin(this->GetOutput()->GetOrigin()); - m_EstimatedVarianceImage->SetSpacing(this->GetOutput()->GetSpacing()); - m_EstimatedVarianceImage->SetDirection(this->GetOutput()->GetDirection()); - m_EstimatedVarianceImage->SetRegions(this->GetOutput()->GetLargestPossibleRegion()); + std::vector bvalKeptIndexes; + std::vector> keptGradients; + // First filter out unwanted b-values (keeping only b = m_BValueShellSelected) + for (unsigned int i = 0; i < numGrads; ++i) + { + if ((m_BValuesList[m_GradientIndexes[i]] >= m_BValueShellSelected - m_BValueShellTolerance) && + (m_BValuesList[m_GradientIndexes[i]] <= m_BValueShellSelected + m_BValueShellTolerance)) + { + bvalKeptIndexes.push_back(m_GradientIndexes[i]); + keptGradients.push_back(m_GradientDirections[i]); + } + } - m_EstimatedVarianceImage->Allocate(); - m_EstimatedVarianceImage->FillBuffer(0.0); + m_GradientIndexes = bvalKeptIndexes; + m_GradientDirections = keptGradients; + numGrads = m_GradientIndexes.size(); - m_EstimatedB0Image = OutputScalarImageType::New(); - m_EstimatedB0Image->Initialize(); - m_EstimatedB0Image->SetOrigin(this->GetOutput()->GetOrigin()); - m_EstimatedB0Image->SetSpacing(this->GetOutput()->GetSpacing()); - m_EstimatedB0Image->SetDirection(this->GetOutput()->GetDirection()); - m_EstimatedB0Image->SetRegions(this->GetOutput()->GetLargestPossibleRegion()); + std::cout << "Running ODF estimation using " << m_B0Indexes.size() << " B0 images and " << numGrads << " gradient images with b-value at " << m_BValueShellSelected << "s.mm^-2" << std::endl; - m_EstimatedB0Image->Allocate(); - m_EstimatedB0Image->FillBuffer(0.0); + m_EstimatedVarianceImage = OutputScalarImageType::New(); + m_EstimatedVarianceImage->Initialize(); + m_EstimatedVarianceImage->SetOrigin(this->GetOutput()->GetOrigin()); + m_EstimatedVarianceImage->SetSpacing(this->GetOutput()->GetSpacing()); + m_EstimatedVarianceImage->SetDirection(this->GetOutput()->GetDirection()); + m_EstimatedVarianceImage->SetRegions(this->GetOutput()->GetLargestPossibleRegion()); - // Compute TMatrix as expressed in Descoteaux MRM 2007 - unsigned int posValue = 0; - m_BMatrix.set_size(numGrads,vectorLength); + m_EstimatedVarianceImage->Allocate(); + m_EstimatedVarianceImage->FillBuffer(0.0); - anima::ODFSphericalHarmonicBasis tmpBasis(m_LOrder); + m_EstimatedB0Image = OutputScalarImageType::New(); + m_EstimatedB0Image->Initialize(); + m_EstimatedB0Image->SetOrigin(this->GetOutput()->GetOrigin()); + m_EstimatedB0Image->SetSpacing(this->GetOutput()->GetSpacing()); + m_EstimatedB0Image->SetDirection(this->GetOutput()->GetDirection()); + m_EstimatedB0Image->SetRegions(this->GetOutput()->GetLargestPossibleRegion()); - for (unsigned int i = 0;i < numGrads;++i) - { - posValue = 0; - for (int k = 0;k <= (int)m_LOrder;k += 2) - for (int m = -k;m <= k;++m) - { - m_BMatrix(i,posValue) = tmpBasis.getNthSHValueAtPosition(k,m,m_GradientDirections[i][0],m_GradientDirections[i][1]); - ++posValue; - } - } + m_EstimatedB0Image->Allocate(); + m_EstimatedB0Image->FillBuffer(0.0); - std::vector LVector(vectorLength,0); - m_PVector.resize(vectorLength); + // Compute TMatrix as expressed in Descoteaux MRM 2007 + unsigned int posValue = 0; + m_BMatrix.set_size(numGrads, vectorLength); - posValue = 0; - for (unsigned int k = 0;k <= m_LOrder;k += 2) - { - double ljVal = k*k*(k + 1)*(k + 1); - double pjValNum = 1, pjValDenom = 1, pjVal; + anima::ODFSphericalHarmonicBasis tmpBasis(m_LOrder); - for (unsigned int l = 2;l <= k; l += 2) + for (unsigned int i = 0; i < numGrads; ++i) { - pjValNum *= (l-1); - pjValDenom *= l; + posValue = 0; + for (int k = 0; k <= (int)m_LOrder; k += 2) + for (int m = -k; m <= k; ++m) + { + m_BMatrix(i, posValue) = tmpBasis.getNthSHValueAtPosition(k, m, m_GradientDirections[i][0], m_GradientDirections[i][1]); + ++posValue; + } } - pjVal = pjValNum/pjValDenom; - if (k/2 % 2 != 0) - pjVal *= -1; + std::vector LVector(vectorLength, 0); + m_PVector.resize(vectorLength); - for (int m = -k;m <= (int)k;++m) + posValue = 0; + for (unsigned int k = 0; k <= m_LOrder; k += 2) { - LVector[posValue] = ljVal; + double ljVal = k * k * (k + 1) * (k + 1); + double pjValNum = 1, pjValDenom = 1, pjVal; - if (!m_UseAganjEstimation) - m_PVector[posValue] = 2*M_PI*pjVal; - else - m_PVector[posValue] = k*(k+1.0)*pjVal/(-8.0*M_PI); + for (unsigned int l = 2; l <= k; l += 2) + { + pjValNum *= (l - 1); + pjValDenom *= l; + } - ++posValue; - } - } + pjVal = pjValNum / pjValDenom; + if (k / 2 % 2 != 0) + pjVal *= -1; - vnl_matrix tmpMat = m_BMatrix.transpose() * m_BMatrix; - for (unsigned int i = 0;i < vectorLength;++i) - tmpMat(i,i) += m_Lambda*LVector[i]; + for (int m = -k; m <= (int)k; ++m) + { + LVector[posValue] = ljVal; - vnl_matrix_inverse tmpInv(tmpMat); + if (!m_UseAganjEstimation) + m_PVector[posValue] = 2 * M_PI * pjVal; + else + m_PVector[posValue] = k * (k + 1.0) * pjVal / (-8.0 * M_PI); - m_TMatrix = tmpInv.inverse() * m_BMatrix.transpose(); + ++posValue; + } + } - if (m_Sharpen) - { - m_DeconvolutionVector.clear(); + vnl_matrix tmpMat = m_BMatrix.transpose() * m_BMatrix; + for (unsigned int i = 0; i < vectorLength; ++i) + tmpMat(i, i) += m_Lambda * LVector[i]; + + vnl_matrix_inverse tmpInv(tmpMat); - for (unsigned int k = 0;k <= m_LOrder;k += 2) + m_TMatrix = tmpInv.inverse() * m_BMatrix.transpose(); + + if (m_Sharpen) { - double Zfactor = 0; - double lambdaL = 0; - double delta = 0.001; + m_DeconvolutionVector.clear(); - for (double t = -1;t <= 1;t += delta) + for (unsigned int k = 0; k <= m_LOrder; k += 2) { - double tmpVal = sqrt(1.0/((m_SharpnessRatio - 1)*t*t + 1)); - if ((t == -1) || (t == 1)) - { - lambdaL += tmpVal*boost::math::legendre_p(k,t); - Zfactor += tmpVal; - } - else + double Zfactor = 0; + double lambdaL = 0; + double delta = 0.001; + + for (double t = -1; t <= 1; t += delta) { - lambdaL += 2*tmpVal*boost::math::legendre_p(k,t); - Zfactor += 2*tmpVal; + double tmpVal = sqrt(1.0 / ((m_SharpnessRatio - 1) * t * t + 1)); + if ((t == -1) || (t == 1)) + { + lambdaL += tmpVal * boost::math::legendre_p(k, t); + Zfactor += tmpVal; + } + else + { + lambdaL += 2 * tmpVal * boost::math::legendre_p(k, t); + Zfactor += 2 * tmpVal; + } } - } - for (int m = -k;m <= (int)k;++m) - m_DeconvolutionVector.push_back(lambdaL*2*M_PI/Zfactor); - } + for (int m = -k; m <= (int)k; ++m) + m_DeconvolutionVector.push_back(lambdaL * 2 * M_PI / Zfactor); + } - unsigned int startI = 0; - if (m_UseAganjEstimation) - startI = 1; + unsigned int startI = 0; + if (m_UseAganjEstimation) + startI = 1; - for (unsigned int i = startI;i < vectorLength;++i) - for (unsigned int j = 0;j < numGrads;++j) - m_TMatrix(i,j) *= m_PVector[i]/m_DeconvolutionVector[i]; - } - else - { - unsigned int startI = 0; - if (m_UseAganjEstimation) - startI = 1; + for (unsigned int i = startI; i < vectorLength; ++i) + for (unsigned int j = 0; j < numGrads; ++j) + m_TMatrix(i, j) *= m_PVector[i] / m_DeconvolutionVector[i]; + } + else + { + unsigned int startI = 0; + if (m_UseAganjEstimation) + startI = 1; - for (unsigned int i = startI;i < vectorLength;++i) - for (unsigned int j = 0;j < numGrads;++j) - m_TMatrix(i,j) *= m_PVector[i]; - } + for (unsigned int i = startI; i < vectorLength; ++i) + for (unsigned int j = 0; j < numGrads; ++j) + m_TMatrix(i, j) *= m_PVector[i]; + } - if ((m_Normalize)&&(m_FileNameSphereTesselation != "")) - { - std::ifstream sphereIn(m_FileNameSphereTesselation.c_str()); + if ((m_Normalize) && (m_FileNameSphereTesselation != "")) + { + std::ifstream sphereIn(m_FileNameSphereTesselation.c_str()); - m_SphereSHSampling.clear(); + m_SphereSHSampling.clear(); - std::vector dirTmp(3,0); - std::vector sphericalCoords; - std::vector shData; + std::vector dirTmp(3, 0); + std::vector sphericalCoords; + std::vector shData; - while (!sphereIn.eof()) - { - char tmpStr[2048]; - sphereIn.getline(tmpStr,2048); + while (!sphereIn.eof()) + { + char tmpStr[2048]; + sphereIn.getline(tmpStr, 2048); - if (strcmp(tmpStr,"") == 0) - continue; + if (strcmp(tmpStr, "") == 0) + continue; - std::stringstream tmpStrStream(tmpStr); - tmpStrStream >> dirTmp[0] >> dirTmp[1] >> dirTmp[2]; + std::stringstream tmpStrStream(tmpStr); + tmpStrStream >> dirTmp[0] >> dirTmp[1] >> dirTmp[2]; - anima::TransformCartesianToSphericalCoordinates(dirTmp,sphericalCoords); - shData.clear(); + anima::TransformCartesianToSphericalCoordinates(dirTmp, sphericalCoords); + shData.clear(); - for (int k = 0;k <= (int)m_LOrder;k += 2) - for (int m = -k;m <= k;++m) - shData.push_back(tmpBasis.getNthSHValueAtPosition(k,m,sphericalCoords[0],sphericalCoords[1])); + for (int k = 0; k <= (int)m_LOrder; k += 2) + for (int m = -k; m <= k; ++m) + shData.push_back(tmpBasis.getNthSHValueAtPosition(k, m, sphericalCoords[0], sphericalCoords[1])); - m_SphereSHSampling.push_back(shData); + m_SphereSHSampling.push_back(shData); + } + sphereIn.close(); } - sphereIn.close(); + else + m_Normalize = false; } - else - m_Normalize = false; -} - -template -void -ODFEstimatorImageFilter -::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) -{ - typedef itk::ImageRegionConstIterator InputIteratorType; - typedef itk::ImageRegionIterator OutputIteratorType; - typedef itk::ImageRegionIterator OutputScalarIteratorType; - - unsigned int vectorLength = (m_LOrder + 1)*(m_LOrder + 2)/2; - unsigned int numGrads = m_GradientIndexes.size(); - unsigned int numB0 = m_B0Indexes.size(); - OutputIteratorType resIt(this->GetOutput(),outputRegionForThread); + template + void + ODFEstimatorImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) + { + typedef itk::ImageRegionConstIterator InputIteratorType; + typedef itk::ImageRegionIterator OutputIteratorType; + typedef itk::ImageRegionIterator OutputScalarIteratorType; - std::vector diffusionIts(numGrads); - std::vector b0Its(numGrads); - for (unsigned int i = 0;i < numGrads;++i) - diffusionIts[i] = InputIteratorType(this->GetInput(m_GradientIndexes[i]),outputRegionForThread); - for (unsigned int i = 0;i < numB0;++i) - b0Its[i] = InputIteratorType(this->GetInput(m_B0Indexes[i]),outputRegionForThread); + unsigned int vectorLength = (m_LOrder + 1) * (m_LOrder + 2) / 2; + unsigned int numGrads = m_GradientIndexes.size(); + unsigned int numB0 = m_B0Indexes.size(); - InputIteratorType refB0Itr; - if (m_ReferenceB0Image.IsNotNull()) - refB0Itr = InputIteratorType(m_ReferenceB0Image, outputRegionForThread); + OutputIteratorType resIt(this->GetOutput(), outputRegionForThread); - OutputScalarIteratorType varItr(m_EstimatedVarianceImage, outputRegionForThread); - OutputScalarIteratorType outB0Itr(m_EstimatedB0Image, outputRegionForThread); + std::vector diffusionIts(numGrads); + std::vector b0Its(numGrads); + for (unsigned int i = 0; i < numGrads; ++i) + diffusionIts[i] = InputIteratorType(this->GetInput(m_GradientIndexes[i]), outputRegionForThread); + for (unsigned int i = 0; i < numB0; ++i) + b0Its[i] = InputIteratorType(this->GetInput(m_B0Indexes[i]), outputRegionForThread); - itk::VariableLengthVector outputData(vectorLength); - std::vector tmpData(numGrads,0); - while (!diffusionIts[0].IsAtEnd()) - { - double b0Value = 0; + InputIteratorType refB0Itr; if (m_ReferenceB0Image.IsNotNull()) - b0Value = refB0Itr.Get(); - else - { - for (unsigned int i = 0;i < numB0;++i) - b0Value += b0Its[i].Get(); + refB0Itr = InputIteratorType(m_ReferenceB0Image, outputRegionForThread); - b0Value /= numB0; - } + OutputScalarIteratorType varItr(m_EstimatedVarianceImage, outputRegionForThread); + OutputScalarIteratorType outB0Itr(m_EstimatedB0Image, outputRegionForThread); - for (unsigned int i = 0;i < numGrads;++i) - tmpData[i] = diffusionIts[i].Get(); - - for (unsigned int i = 0;i < vectorLength;++i) - outputData[i] = 0; - - if ((isZero(tmpData))||(b0Value <= 0)) + itk::VariableLengthVector outputData(vectorLength); + std::vector tmpData(numGrads, 0); + while (!diffusionIts[0].IsAtEnd()) { - resIt.Set(outputData); - outB0Itr.Set(0.0); - varItr.Set(0.0); - ++resIt; - ++outB0Itr; - ++varItr; + double b0Value = 0; + if (m_ReferenceB0Image.IsNotNull()) + b0Value = refB0Itr.Get(); + else + { + for (unsigned int i = 0; i < numB0; ++i) + b0Value += b0Its[i].Get(); - for (unsigned int i = 0;i < numGrads;++i) - ++diffusionIts[i]; + b0Value /= numB0; + } - for (unsigned int i = 0;i < numB0;++i) - ++b0Its[i]; + for (unsigned int i = 0; i < numGrads; ++i) + tmpData[i] = diffusionIts[i].Get(); - continue; - } + for (unsigned int i = 0; i < vectorLength; ++i) + outputData[i] = 0; - if (m_UseAganjEstimation) - { - for (unsigned int i = 0;i < numGrads;++i) + if ((isZero(tmpData)) || (b0Value <= 0)) { - double e = tmpData[i] / b0Value; - - if (e < 0) - tmpData[i] = m_DeltaAganjRegularization / 2.0; - else if (e < m_DeltaAganjRegularization) - tmpData[i] = m_DeltaAganjRegularization / 2.0 + e * e / (2.0 * m_DeltaAganjRegularization); - else if (e < 1.0 - m_DeltaAganjRegularization) - tmpData[i] = e; - else if (e < 1) - tmpData[i] = 1.0 - m_DeltaAganjRegularization / 2.0 - (1.0 - e) * (1.0 - e) / (2.0 * m_DeltaAganjRegularization); - else - tmpData[i] = 1.0 - m_DeltaAganjRegularization / 2.0; + resIt.Set(outputData); + outB0Itr.Set(0.0); + varItr.Set(0.0); + ++resIt; + ++outB0Itr; + ++varItr; - tmpData[i] = std::log(-std::log(tmpData[i])); - } - } + for (unsigned int i = 0; i < numGrads; ++i) + ++diffusionIts[i]; - double zeroValue = 1.0; - if (b0Value > 0) - { - for (unsigned int i = 0;i < vectorLength;++i) - for (unsigned int j = 0;j < numGrads;++j) - outputData[i] += m_TMatrix(i,j)*tmpData[j]; + for (unsigned int i = 0; i < numB0; ++i) + ++b0Its[i]; - if (!m_UseAganjEstimation) - { - for (unsigned int i = 0;i < vectorLength;++i) - outputData[i] /= b0Value; + continue; } - else + + if (m_UseAganjEstimation) { - zeroValue = outputData[0]; - outputData[0] = 1/(2*sqrt(M_PI)); + for (unsigned int i = 0; i < numGrads; ++i) + { + double e = tmpData[i] / b0Value; + + if (e < 0) + tmpData[i] = m_DeltaAganjRegularization / 2.0; + else if (e < m_DeltaAganjRegularization) + tmpData[i] = m_DeltaAganjRegularization / 2.0 + e * e / (2.0 * m_DeltaAganjRegularization); + else if (e < 1.0 - m_DeltaAganjRegularization) + tmpData[i] = e; + else if (e < 1) + tmpData[i] = 1.0 - m_DeltaAganjRegularization / 2.0 - (1.0 - e) * (1.0 - e) / (2.0 * m_DeltaAganjRegularization); + else + tmpData[i] = 1.0 - m_DeltaAganjRegularization / 2.0; + + tmpData[i] = std::log(-std::log(tmpData[i])); + } } - } - double noiseVariance = 0.0; - for (unsigned int i = 0;i < numGrads;++i) - { - double signalSim = 0.0; - for (unsigned int j = 0;j < vectorLength;++j) + double zeroValue = 1.0; + if (b0Value > 0) { - if ((j == 0) && (m_UseAganjEstimation)) - signalSim += m_BMatrix(i,j) * zeroValue; + for (unsigned int i = 0; i < vectorLength; ++i) + for (unsigned int j = 0; j < numGrads; ++j) + outputData[i] += m_TMatrix(i, j) * tmpData[j]; + + if (!m_UseAganjEstimation) + { + for (unsigned int i = 0; i < vectorLength; ++i) + outputData[i] /= b0Value; + } else - signalSim += m_BMatrix(i,j) * outputData[j] / m_PVector[j]; + { + zeroValue = outputData[0]; + outputData[0] = 1 / (2 * sqrt(M_PI)); + } } - if (!m_UseAganjEstimation) - signalSim *= b0Value; - else - signalSim = b0Value * std::exp(- std::exp(signalSim)); + double noiseVariance = 0.0; + for (unsigned int i = 0; i < numGrads; ++i) + { + double signalSim = 0.0; + for (unsigned int j = 0; j < vectorLength; ++j) + { + if ((j == 0) && (m_UseAganjEstimation)) + signalSim += m_BMatrix(i, j) * zeroValue; + else + signalSim += m_BMatrix(i, j) * outputData[j] / m_PVector[j]; + } - double signalValue = diffusionIts[i].Get(); - noiseVariance += (signalSim - signalValue) * (signalSim - signalValue); - } + if (!m_UseAganjEstimation) + signalSim *= b0Value; + else + signalSim = b0Value * std::exp(-std::exp(signalSim)); - for (unsigned int i = 0;i < numB0;++i) - { - double b0Signal = b0Its[i].Get(); - noiseVariance += (b0Value - b0Signal) * (b0Value - b0Signal); - } + double signalValue = diffusionIts[i].Get(); + noiseVariance += (signalSim - signalValue) * (signalSim - signalValue); + } - noiseVariance /= (numGrads + numB0); - varItr.Set(noiseVariance); + for (unsigned int i = 0; i < numB0; ++i) + { + double b0Signal = b0Its[i].Get(); + noiseVariance += (b0Value - b0Signal) * (b0Value - b0Signal); + } - outB0Itr.Set(b0Value); + noiseVariance /= (numGrads + numB0); + varItr.Set(noiseVariance); - if (m_Normalize) - { - long double integralODF = 0; - for (unsigned int i = 0;i < m_SphereSHSampling.size();++i) - for (unsigned int j = 0;j < vectorLength;++j) - integralODF += m_SphereSHSampling[i][j]*outputData[j]; + outB0Itr.Set(b0Value); - for (unsigned int i = 0;i < vectorLength;++i) - outputData[i] /= integralODF; - } + if (m_Normalize) + { + long double integralODF = 0; + for (unsigned int i = 0; i < m_SphereSHSampling.size(); ++i) + for (unsigned int j = 0; j < vectorLength; ++j) + integralODF += m_SphereSHSampling[i][j] * outputData[j]; - resIt.Set(outputData); - ++resIt; - ++outB0Itr; - ++varItr; + for (unsigned int i = 0; i < vectorLength; ++i) + outputData[i] /= integralODF; + } - for (unsigned int i = 0;i < numGrads;++i) - ++diffusionIts[i]; + resIt.Set(outputData); + ++resIt; + ++outB0Itr; + ++varItr; + + for (unsigned int i = 0; i < numGrads; ++i) + ++diffusionIts[i]; - for (unsigned int i = 0;i < numB0;++i) - ++b0Its[i]; + for (unsigned int i = 0; i < numB0; ++i) + ++b0Its[i]; + } } -} - + } // end of namespace anima diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx index 4adcb43b1..0631bb02f 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimator.cxx @@ -64,7 +64,17 @@ int main(int argc, char **argv) itk::TimeProbe tmpTime; tmpTime.Start(); - mainFilter->Update(); + + try + { + mainFilter->Update(); + } + catch (itk::ExceptionObject &e) + { + std::cerr << e << std::endl; + return EXIT_FAILURE; + } + tmpTime.Stop(); std::cout << "\nExecution Time: " << tmpTime.GetTotal() << "s" << std::endl; diff --git a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx index e06d13200..edfd168f6 100644 --- a/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx +++ b/Anima/diffusion/odf/tod_estimator/animaTODEstimatorImageFilter.hxx @@ -141,7 +141,6 @@ namespace anima while (!outItr.IsAtEnd()) { - index = outItr.GetIndex(); std::vector vecCoefs; From be8f47824b67d3b310d6b318345ec8bdaf30ce1b Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 16 Nov 2023 20:38:45 +0100 Subject: [PATCH 08/11] Done simplifying ODF average tool. --- .../odf/odf_average/animaODFAverage.cxx | 109 +------ .../animaODFAverageImageFilter.cxx | 285 +++++++----------- .../odf_average/animaODFAverageImageFilter.h | 53 ++-- 3 files changed, 144 insertions(+), 303 deletions(-) diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx index ea03caa3d..c46340814 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx +++ b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx @@ -22,33 +22,14 @@ int main(int argc, char **argv) "i", "input-odf", "ODF images list as a text file", true, "", "ODF images list", cmd); - TCLAP::ValueArg maskArg( + TCLAP::ValueArg weightArg( "m", "masks", "Masks images list as a text file", true, "", "Masks images list", cmd); - - TCLAP::ValueArg resArg( + TCLAP::ValueArg outArg( "o", "output", "Result average ODF", true, "", "result ODF image", cmd); - TCLAP::ValueArg resMaskArg( - "M", "resMask", - "Result average mask", - false, "", "average mask", cmd); - - TCLAP::ValueArg barycenterWeightArg( - "b", "barycenterWeight", - "Flag for iterative barycenter atlasing - Result number of average per pixel", - false, "", "number average image", cmd); - TCLAP::ValueArg weightImageArg( - "W", "weightImage", - "Image containing the first image weight for each voxel", - false, "", "weight Image", cmd); - - TCLAP::ValueArg weightArg( - "w", "weight", - "In the case of 2 image averaging, scalar weight of the first image ", - false, 0.0, "first image weight", cmd); TCLAP::ValueArg nbpArg( "T", "nb-threads", @@ -75,70 +56,45 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - std::ifstream maskFile(maskArg.getValue().c_str()); - if (!maskFile.is_open()) + std::ifstream weightFile(weightArg.getValue().c_str()); + if (!weightFile.is_open()) { - std::cerr << "Please provide usable file with input Masks" << std::endl; + std::cerr << "Please provide usable file with input weight images" << std::endl; return EXIT_FAILURE; } using FilterType = anima::ODFAverageImageFilter; using InputImageType = FilterType::InputImageType; using OutputImageType = FilterType::OutputImageType; - using MaskImageType = FilterType::MaskImageType; - using DoubleImageType = FilterType::DoubleImageType; + using WeightImageType = FilterType::WeightImageType; FilterType::Pointer mainFilter = FilterType::New(); std::vector inputFiles; - std::vector maskFiles; - unsigned int numInput = 0; + std::vector weightFiles; + unsigned int numInputs = 0; while (!odfFile.eof()) { char tmpStr[2048], maskStr[2048]; odfFile.getline(tmpStr, 2048); - maskFile.getline(maskStr, 2048); + weightFile.getline(maskStr, 2048); if (strcmp(tmpStr, "") == 0) continue; inputFiles.push_back(tmpStr); - maskFiles.push_back(maskStr); - numInput++; + weightFiles.push_back(maskStr); + numInputs++; } odfFile.close(); - maskFile.close(); - - if (weightArg.getValue() != 0.0) - mainFilter->SetWeightValue(weightArg.getValue()); - - MaskImageType::Pointer geomImage = anima::readImage(maskFiles[0]); - - std::cout << "Processing image : " << inputFiles[1] << std::endl; - - if (weightImageArg.getValue() != "") - mainFilter->SetWeightImage(anima::readImage(weightImageArg.getValue())); + weightFile.close(); - if (barycenterWeightArg.getValue() != "") + for (unsigned int i = 0; i < numInputs; ++i) { - MaskImageType::Pointer barycenterWeightImage = MaskImageType::New(); - barycenterWeightImage->Initialize(); - barycenterWeightImage->SetDirection(geomImage->GetDirection()); - barycenterWeightImage->SetSpacing(geomImage->GetSpacing()); - barycenterWeightImage->SetOrigin(geomImage->GetOrigin()); - MaskImageType::RegionType region = geomImage->GetLargestPossibleRegion(); - barycenterWeightImage->SetRegions(region); - barycenterWeightImage->Allocate(); - barycenterWeightImage->FillBuffer(0); - mainFilter->SetBarycenterWeightImage(barycenterWeightImage); + mainFilter->SetInput(i, anima::readImage(inputFiles[i])); + mainFilter->AddWeightImage(i, anima::readImage(weightFiles[i])); } - mainFilter->SetInput(0, anima::readImage(inputFiles[0])); - mainFilter->SetInput(1, anima::readImage(inputFiles[1])); - - mainFilter->AddMaskImage(0, anima::readImage(maskFiles[0])); - mainFilter->AddMaskImage(1, anima::readImage(maskFiles[1])); - mainFilter->AddObserver(itk::ProgressEvent(), callback); mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); @@ -156,44 +112,11 @@ int main(int argc, char **argv) return EXIT_FAILURE; } - for (int i = 2; i < numInput; i++) - { - - std::cout << std::endl - << "Processing image : " << inputFiles[i] << std::endl; - mainFilter->SetBarycenterWeightImage(mainFilter->GetBarycenterWeightImage()); - - mainFilter->SetInput(0, mainFilter->GetOutput()); - mainFilter->SetInput(1, anima::readImage(inputFiles[i])); - - mainFilter->AddMaskImage(0, mainFilter->GetMaskAverage()); - mainFilter->AddMaskImage(1, anima::readImage(maskFiles[i])); - - mainFilter->AddObserver(itk::ProgressEvent(), callback); - mainFilter->SetNumberOfWorkUnits(nbpArg.getValue()); - - try - { - mainFilter->Update(); - } - catch (itk::ExceptionObject &e) - { - std::cerr << e << std::endl; - return EXIT_FAILURE; - } - } - tmpTimer.Stop(); std::cout << "\nAveraging done in " << tmpTimer.GetTotal() << "s" << std::endl; - anima::writeImage(resArg.getValue(), mainFilter->GetOutput()); - - if (barycenterWeightArg.getValue() != "") - anima::writeImage(barycenterWeightArg.getValue(), mainFilter->GetBarycenterWeightImage()); - - if (resMaskArg.getValue() != "") - anima::writeImage(resMaskArg.getValue(), mainFilter->GetMaskAverage()); + anima::writeImage(outArg.getValue(), mainFilter->GetOutput()); return EXIT_SUCCESS; } diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx index 606c84cd1..1c7e3316c 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx @@ -7,135 +7,128 @@ namespace anima { - void ODFAverageImageFilter::AddMaskImage(const unsigned int i, const MaskImagePointer &mask) + void ODFAverageImageFilter::AddWeightImage(const unsigned int i, const WeightImagePointer &weightImage) { - if (i == m_MaskImages.size()) + if (i == m_WeightImages.size()) { - m_MaskImages.push_back(mask); + m_WeightImages.push_back(weightImage); return; } - if (i > m_MaskImages.size()) - itkExceptionMacro("Trying to add a non contiguous mask... Add mask images contiguously (0,1,2,3,...)..."); + if (i > m_WeightImages.size()) + itkExceptionMacro("Weight images must be added contiguously."); - m_MaskImages[i] = mask; + m_WeightImages[i] = weightImage; } void ODFAverageImageFilter::BeforeThreadedGenerateData() { Superclass::BeforeThreadedGenerateData(); - m_HistoODFs.resize(this->GetNumberOfIndexedInputs()); - for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i) - m_HistoODFs[i].resize(m_NbSamplesPhi * m_NbSamplesTheta); - - m_ODFSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8 * this->GetInput(0)->GetVectorLength() + 1)); - m_VectorLength = (m_ODFSHOrder + 1) * (m_ODFSHOrder + 2) / 2; + unsigned int odfSHOrder = std::round(-1.5 + 0.5 * std::sqrt(8.0 * static_cast(this->GetInput(0)->GetVectorLength()) + 1.0)); + m_VectorLength = (odfSHOrder + 1) * (odfSHOrder + 2) / 2; m_SpherHarm.set_size(m_NbSamplesPhi * m_NbSamplesTheta, m_VectorLength); - m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(m_ODFSHOrder); + m_ODFSHBasis = new anima::ODFSphericalHarmonicBasis(odfSHOrder); + + // Discretize SH + double sqrt2 = std::sqrt(2.0); + double deltaPhi = 2.0 * M_PI / (static_cast(m_NbSamplesPhi) - 1.0); + double deltaTheta = M_PI / (static_cast(m_NbSamplesTheta) - 1.0); + + unsigned int k = 0; + for (unsigned int i = 0; i < m_NbSamplesTheta; ++i) + { + double theta = static_cast(i) * deltaTheta; + + for (unsigned int j = 0; j < m_NbSamplesPhi; ++j) + { + double phi = static_cast(j) * deltaPhi; + unsigned int c = 0; + for (double l = 0; l <= odfSHOrder; l += 2) + for (double m = -l; m <= l; m++) + m_SpherHarm.put(k, c++, m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi)); + k++; + } + } - this->DiscretizeSH(); + m_SolveSHMatrix = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose(); } void ODFAverageImageFilter::DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) { - using InputIteratorType = itk::ImageRegionConstIterator; - using InputMaskIteratorType = itk::ImageRegionConstIterator; - using DoubleIteratorType = itk::ImageRegionConstIterator; - using OutputIteratorType = itk::ImageRegionIterator; - using OutputMaskIteratorType = itk::ImageRegionIterator; - using OutputDoubleIteratorType = itk::ImageRegionIterator; - - double weight0 = 0.5, weight1 = 0.5; + using InputImageIteratorType = itk::ImageRegionConstIterator; + using InputWeightIteratorType = itk::ImageRegionConstIterator; + using OutputImageIteratorType = itk::ImageRegionIterator; unsigned int numInputs = this->GetNumberOfIndexedInputs(); - std::vector inIterators(numInputs); - std::vector maskIterators(numInputs); + + std::vector inItrs(numInputs); + std::vector weightItrs(numInputs); for (unsigned int i = 0; i < numInputs; ++i) { - inIterators[i] = InputIteratorType(this->GetInput(i), outputRegionForThread); - maskIterators[i] = InputMaskIteratorType(m_MaskImages[i], outputRegionForThread); + inItrs[i] = InputImageIteratorType(this->GetInput(i), outputRegionForThread); + weightItrs[i] = InputWeightIteratorType(m_WeightImages[i], outputRegionForThread); } - OutputIteratorType outIterator(this->GetOutput(), outputRegionForThread); - OutputMaskIteratorType barycenterWeightItr; - if (m_BarycenterWeightImage) - barycenterWeightItr = OutputMaskIteratorType(m_BarycenterWeightImage, outputRegionForThread); + OutputImageIteratorType outItr(this->GetOutput(), outputRegionForThread); - OutputDoubleIteratorType weightImgIt; - if (m_WeightImage) - weightImgIt = OutputDoubleIteratorType(m_WeightImage, outputRegionForThread); + VectorType weightValues(numInputs); + VectorType workValue(m_NbSamplesPhi * m_NbSamplesTheta); + HistoArrayType workValues(numInputs); - std::vector arrayCoef(numInputs), arraySQRTCoef(numInputs); + InputPixelType inputValue(m_VectorLength); + OutputPixelType outputValue(m_VectorLength); - IOVectorType nullVector(m_VectorLength); - nullVector.Fill(0.0); + double epsValue = std::sqrt(std::numeric_limits::epsilon()); - while (!outIterator.IsAtEnd()) + while (!outItr.IsAtEnd()) { - if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 0) - outIterator.Set(nullVector); - else if (maskIterators[0].Get() == 1 && maskIterators[1].Get() == 0) - { - outIterator.Set(inIterators[0].Get()); - } - else if (maskIterators[0].Get() == 0 && maskIterators[1].Get() == 1) + double weightSum = 0.0; + for (unsigned int i = 0; i < numInputs; ++i) { - outIterator.Set(inIterators[1].Get()); + double weightValue = weightItrs[i].Get(); + weightSum += weightValue; + weightValues[i] = weightValue; } - else + + if (weightSum < epsValue) { + outputValue.Fill(0.0); + outItr.Set(outputValue); + for (unsigned int i = 0; i < numInputs; ++i) { - arrayCoef[i] = inIterators[i].Get(); - this->DiscretizeODF(arrayCoef[i], m_HistoODFs[i]); - arraySQRTCoef[i] = this->GetSquareRootODFCoef(m_HistoODFs[i]); + ++inItrs[i]; + ++weightItrs[i]; } - if (m_BarycenterWeightImage) - { - double k = barycenterWeightItr.Get(); - weight1 = (k + 1.0) / (k + 2.0); - weight0 = 1.0 - weight1; + ++outItr; - barycenterWeightItr.Set(barycenterWeightItr.Get() + 1.0); - } - else if (m_WeightImage) - { - weight0 = weightImgIt.Get(); - weight1 = 1.0 - weight0; - } - else if (m_WeightValue != 0.0) - { - weight0 = m_WeightValue; - weight1 = 1.0 - weight1; - } - - IOVectorType averageSQRTCoef(m_VectorLength); - this->GetAverageHisto(arraySQRTCoef, averageSQRTCoef, weight0, weight1); + this->IncrementNumberOfProcessedPoints(); + continue; + } - VectorType averageSQRTHisto(m_NbSamplesPhi * m_NbSamplesTheta); - this->DiscretizeODF(averageSQRTCoef, averageSQRTHisto); + for (unsigned int i = 0; i < numInputs; ++i) + { + inputValue = inItrs[i].Get(); + this->DiscretizeODF(inputValue, workValue); + workValues[i] = this->GetSquareRootODFCoef(workValue); + } - IOVectorType averageCoef(m_VectorLength); - averageCoef = this->GetSquareODFCoef(averageSQRTHisto); + this->GetAverageHisto(workValues, weightValues, outputValue); + this->DiscretizeODF(outputValue, workValue); + outputValue = this->GetSquareODFCoef(workValue); - outIterator.Set(averageCoef); - } + outItr.Set(outputValue); for (unsigned int i = 0; i < numInputs; ++i) { - ++inIterators[i]; - ++maskIterators[i]; + ++inItrs[i]; + ++weightItrs[i]; } - if (m_BarycenterWeightImage) - ++barycenterWeightItr; - if (m_WeightImage) - ++weightImgIt; - - ++outIterator; + ++outItr; this->IncrementNumberOfProcessedPoints(); } @@ -146,30 +139,7 @@ namespace anima delete m_ODFSHBasis; } - void ODFAverageImageFilter::DiscretizeSH() - { - double sqrt2 = std::sqrt(2); - double deltaPhi = 2.0 * M_PI / (static_cast(m_NbSamplesPhi) - 1.0); - double deltaTheta = M_PI / (static_cast(m_NbSamplesTheta) - 1.0); - - unsigned int k = 0; - for (unsigned int i = 0; i < m_NbSamplesTheta; ++i) - { - double theta = static_cast(i) * deltaTheta; - - for (unsigned int j = 0; j < m_NbSamplesPhi; ++j) - { - double phi = static_cast(j) * deltaPhi; - unsigned int c = 0; - for (double l = 0; l <= m_ODFSHOrder; l += 2) - for (double m = -l; m <= l; m++) - m_SpherHarm.put(k, c++, m_ODFSHBasis->getNthSHValueAtPosition(l, m, theta, phi)); - k++; - } - } - } - - void ODFAverageImageFilter::DiscretizeODF(const IOVectorType &modelValue, VectorType &odf) + void ODFAverageImageFilter::DiscretizeODF(const InputPixelType &modelValue, VectorType &odf) { unsigned int k = 0; double deltaPhi = 2.0 * M_PI / (static_cast(m_NbSamplesPhi) - 1.0); @@ -187,116 +157,83 @@ namespace anima } } - ODFAverageImageFilter::IOVectorType ODFAverageImageFilter::GetSquareRootODFCoef(const VectorType &odf) + ODFAverageImageFilter::VectorType ODFAverageImageFilter::GetSquareRootODFCoef(const VectorType &odf) { MatrixType squareRootOdf(m_NbSamplesTheta * m_NbSamplesPhi, 1); MatrixType squareRootCoef(m_VectorLength, 1); for (unsigned int i = 0; i < m_NbSamplesTheta * m_NbSamplesPhi; ++i) - squareRootOdf(i, 0) = std::sqrt(std::max(0.0, odf[i])); + squareRootOdf.put(i, 0, std::sqrt(std::max(0.0, odf[i]))); - squareRootCoef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose() * squareRootOdf; + squareRootCoef = m_SolveSHMatrix * squareRootOdf; - IOVectorType squareRootModelValue(m_VectorLength); + VectorType squareRootModelValue(m_VectorLength); for (unsigned int i = 0; i < m_VectorLength; ++i) - squareRootModelValue[i] = squareRootCoef(i, 0); + squareRootModelValue[i] = squareRootCoef.get(i, 0); return squareRootModelValue; } - ODFAverageImageFilter::IOVectorType ODFAverageImageFilter::GetSquareODFCoef(const VectorType &odf) + ODFAverageImageFilter::OutputPixelType ODFAverageImageFilter::GetSquareODFCoef(const VectorType &odf) { MatrixType squareOdf(m_NbSamplesTheta * m_NbSamplesPhi, 1); MatrixType squareCoef(m_VectorLength, 1); for (unsigned int i = 0; i < m_NbSamplesTheta * m_NbSamplesPhi; ++i) - squareOdf(i, 0) = std::pow(odf[i], 2); + squareOdf.put(i, 0, std::pow(odf[i], 2.0)); - squareCoef = vnl_matrix_inverse(m_SpherHarm.transpose() * m_SpherHarm).as_matrix() * m_SpherHarm.transpose() * squareOdf; + squareCoef = m_SolveSHMatrix * squareOdf; - IOVectorType squareModelValue(m_VectorLength); + OutputPixelType squareModelValue(m_VectorLength); for (unsigned int i = 0; i < m_VectorLength; ++i) - squareModelValue[i] = squareCoef(i, 0); + squareModelValue[i] = squareCoef.get(i, 0); squareModelValue[0] = 1.0 / (2.0 * std::sqrt(M_PI)); return squareModelValue; } - void ODFAverageImageFilter::GetAverageHisto(const std::vector &coefs, IOVectorType &resCoef, double weight0, double weight1) + void ODFAverageImageFilter::GetAverageHisto(const HistoArrayType &coefs, const VectorType &weightValues, OutputPixelType &resCoef) { - unsigned int numImage = this->GetNumberOfIndexedInputs(); + unsigned int numImages = this->GetNumberOfIndexedInputs(); - HistoArrayType arrayCoef(numImage), arrayLogMap(numImage); - VectorType expMap(m_VectorLength), mean(m_VectorLength), nextMean(m_VectorLength), tangent(m_VectorLength); + VectorType mean, nextMean = coefs[0]; + VectorType tangent(m_VectorLength), workValue(m_VectorLength); - const unsigned int T = 100; - const double eps = 0.000035; + const unsigned int maxIter = 100; + const double epsValue = 0.000035; - for (unsigned int i = 0; i < numImage; ++i) - { - arrayCoef[i].resize(m_VectorLength); - arrayLogMap[i].resize(m_VectorLength); - - for (unsigned int n = 0; n < m_VectorLength; ++n) - arrayCoef[i][n] = coefs[i][n]; - } - - nextMean = arrayCoef[0]; - - unsigned int t = 0; + unsigned int nIter = 0; double normTan = 1.0; - while (t < T && normTan > eps) + + while (nIter < maxIter && normTan > epsValue) { mean = nextMean; - for (unsigned int i = 0; i < numImage; ++i) - anima::sphere_log_map(arrayCoef[i], mean, arrayLogMap[i]); + std::fill(tangent.begin(), tangent.end(), 0.0); + double weightSum = 0.0; + for (unsigned int i = 0; i < numImages; ++i) + { + weightSum += weightValues[i]; + std::fill(workValue.begin(), workValue.end(), 0.0); + anima::sphere_log_map(coefs[i], mean, workValue); + for (unsigned int j = 0; j < m_VectorLength; ++j) + tangent[j] += weightValues[i] * workValue[j]; + } - std::fill(tangent.begin(), tangent.end(), 0); - for (unsigned int n = 0; n < m_VectorLength; ++n) - tangent[n] += weight1 * arrayLogMap[1][n] + weight0 * arrayLogMap[0][n]; + for (unsigned int j = 0; j < m_VectorLength; ++j) + tangent[j] /= weightSum; normTan = anima::ComputeNorm(tangent); + + std::fill(nextMean.begin(), nextMean.end(), 0.0); anima::sphere_exp_map(tangent, mean, nextMean); - t++; + nIter++; } for (unsigned int i = 0; i < m_VectorLength; ++i) resCoef[i] = nextMean[i]; } - ODFAverageImageFilter::MaskImageType *ODFAverageImageFilter::GetMaskAverage() - { - MaskImageType::Pointer maskAverage = MaskImageType::New(); - maskAverage->Initialize(); - maskAverage->SetDirection(m_MaskImages[0]->GetDirection()); - maskAverage->SetSpacing(m_MaskImages[0]->GetSpacing()); - maskAverage->SetOrigin(m_MaskImages[0]->GetOrigin()); - MaskImageType::RegionType region = m_MaskImages[0]->GetLargestPossibleRegion(); - maskAverage->SetRegions(region); - maskAverage->Allocate(); - maskAverage->FillBuffer(0); - - using InputMaskIteratorType = itk::ImageRegionConstIterator; - using OutputMaskIteratorType = itk::ImageRegionIterator; - - OutputMaskIteratorType averageIt(maskAverage, maskAverage->GetLargestPossibleRegion()); - InputMaskIteratorType mask0It(m_MaskImages[0], m_MaskImages[0]->GetLargestPossibleRegion()); - InputMaskIteratorType mask1It(m_MaskImages[1], m_MaskImages[1]->GetLargestPossibleRegion()); - - while (!averageIt.IsAtEnd()) - { - averageIt.Set(mask0It.Get() || mask1It.Get()); - - ++averageIt; - ++mask0It; - ++mask1It; - } - - m_MaskImages.clear(); - return maskAverage; - } - } // end namespace anima diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h index 38c831e1e..3e272b51e 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.h @@ -7,16 +7,13 @@ namespace anima { - class ODFAverageImageFilter : public anima::NumberedThreadImageToImageFilter, itk::VectorImage> + class ODFAverageImageFilter : public anima::NumberedThreadImageToImageFilter, itk::VectorImage> { public: using Self = ODFAverageImageFilter; - using ScalarPixelType = double; - using PixelMaskType = unsigned int; - using InputImageType = itk::VectorImage; - using OutputImageType = itk::VectorImage; - using DoubleImageType = itk::Image; - using MaskImageType = itk::Image; + using InputImageType = itk::VectorImage; + using OutputImageType = itk::VectorImage; + using WeightImageType = itk::Image; using Superclass = anima::NumberedThreadImageToImageFilter; using Pointer = itk::SmartPointer; using ConstPointer = itk::SmartPointer; @@ -30,36 +27,28 @@ namespace anima /** Image typedef support */ using InputImagePointer = InputImageType::Pointer; using OutputImagePointer = OutputImageType::Pointer; - using MaskImagePointer = MaskImageType::Pointer; - using OutputPixelType = OutputImageType::PixelType; - using DoubleImagePointer = DoubleImageType::Pointer; + using WeightImagePointer = WeightImageType::Pointer; /** Superclass typedefs. */ using InputImageRegionType = Superclass::InputImageRegionType; using OutputImageRegionType = Superclass::OutputImageRegionType; + using InputPixelType = InputImageType::PixelType; + using OutputPixelType = OutputImageType::PixelType; - using IOVectorType = OutputImageType::PixelType; + /** Typedefs for computations. */ using VectorType = std::vector; using HistoArrayType = std::vector; using MatrixType = vnl_matrix; - void AddMaskImage(const unsigned int i, const MaskImagePointer &maskImage); - - void SetBarycenterWeightImage(const MaskImagePointer &weightImage) { m_BarycenterWeightImage = weightImage; } - void SetWeightImage(const DoubleImagePointer &img) { m_WeightImage = img; } - MaskImageType *GetBarycenterWeightImage() { return m_BarycenterWeightImage; } - MaskImageType *GetMaskAverage(); - - itkSetMacro(WeightValue, double); + void AddWeightImage(const unsigned int i, const WeightImagePointer &weightImage); protected: ODFAverageImageFilter() : Superclass() { m_NbSamplesTheta = 10; m_NbSamplesPhi = 2 * m_NbSamplesTheta; - m_WeightValue = 0.0; - m_MaskImages.clear(); - m_HistoODFs.clear(); + m_WeightImages.clear(); + m_SolveSHMatrix.clear(); m_SpherHarm.clear(); } @@ -69,11 +58,10 @@ namespace anima void DynamicThreadedGenerateData(const OutputImageRegionType &outputRegionForThread) ITK_OVERRIDE; void AfterThreadedGenerateData() ITK_OVERRIDE; - void DiscretizeSH(); - void DiscretizeODF(const IOVectorType &Coef, VectorType &resHisto); - void GetAverageHisto(const std::vector &coefs, IOVectorType &resCoef, double smallWeight, double bigWeight); - IOVectorType GetSquareRootODFCoef(const VectorType &histo); - IOVectorType GetSquareODFCoef(const VectorType &histo); + void DiscretizeODF(const InputPixelType &Coef, VectorType &resHisto); + void GetAverageHisto(const HistoArrayType &coefs, const VectorType &weightValues, OutputPixelType &resCoef); + VectorType GetSquareRootODFCoef(const VectorType &histo); + OutputPixelType GetSquareODFCoef(const VectorType &histo); private: ITK_DISALLOW_COPY_AND_ASSIGN(ODFAverageImageFilter); @@ -82,18 +70,11 @@ namespace anima unsigned int m_NbSamplesTheta; unsigned int m_VectorLength; - double m_ODFSHOrder; - double m_WeightValue; - - MaskImagePointer m_BarycenterWeightImage; - DoubleImagePointer m_WeightImage; - - std::vector m_MaskImages; + std::vector m_WeightImages; MatrixType m_SpherHarm; + MatrixType m_SolveSHMatrix; anima::ODFSphericalHarmonicBasis *m_ODFSHBasis; - - HistoArrayType m_HistoODFs; }; } // end of namespace anima From 1879d6eb5c94f4b6bb1ad72160e62b7e5191cda5 Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Thu, 16 Nov 2023 20:46:01 +0100 Subject: [PATCH 09/11] Better document arguments in ODF average tool. --- .../odf/odf_average/animaODFAverage.cxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx index c46340814..4bfb6012d 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx +++ b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx @@ -19,17 +19,17 @@ int main(int argc, char **argv) TCLAP::CmdLine cmd("INRIA / IRISA - VisAGeS/Empenn Team", ' ', ANIMA_VERSION); TCLAP::ValueArg inArg( - "i", "input-odf", - "ODF images list as a text file", - true, "", "ODF images list", cmd); + "i", "input-odf-file", + "A string specifying the name of a text file in which the ODF images are listed.", + true, "", "odf image list", cmd); TCLAP::ValueArg weightArg( - "m", "masks", - "Masks images list as a text file", - true, "", "Masks images list", cmd); + "w", "input-weight-file", + "A string specifying the name of a text file in which the weight images are listed.", + true, "", "weight image list", cmd); TCLAP::ValueArg outArg( - "o", "output", - "Result average ODF", - true, "", "result ODF image", cmd); + "o", "output-file", + "A string specifying the name of the output average ODF image.", + true, "", "output odf image", cmd); TCLAP::ValueArg nbpArg( "T", "nb-threads", From d98f163f6c5ffcc0b8003a8a882581c48aa4656c Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Tue, 21 Nov 2023 21:57:08 +0100 Subject: [PATCH 10/11] Suppress odd line of code. --- Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx index 1c7e3316c..43829ed67 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx +++ b/Anima/diffusion/odf/odf_average/animaODFAverageImageFilter.cxx @@ -188,8 +188,6 @@ namespace anima for (unsigned int i = 0; i < m_VectorLength; ++i) squareModelValue[i] = squareCoef.get(i, 0); - squareModelValue[0] = 1.0 / (2.0 * std::sqrt(M_PI)); - return squareModelValue; } From 35df1d1438d935e55e01567a9145c10c1e320146 Mon Sep 17 00:00:00 2001 From: Aymeric Stamm Date: Wed, 22 Nov 2023 14:28:27 +0100 Subject: [PATCH 11/11] Fix Flo comments. --- .../odf/odf_average/animaODFAverage.cxx | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx index 4bfb6012d..339d16f00 100644 --- a/Anima/diffusion/odf/odf_average/animaODFAverage.cxx +++ b/Anima/diffusion/odf/odf_average/animaODFAverage.cxx @@ -20,15 +20,15 @@ int main(int argc, char **argv) TCLAP::ValueArg inArg( "i", "input-odf-file", - "A string specifying the name of a text file in which the ODF images are listed.", + "A path or file name specifying the text file in which the ODF images are listed.", true, "", "odf image list", cmd); TCLAP::ValueArg weightArg( "w", "input-weight-file", - "A string specifying the name of a text file in which the weight images are listed.", + "A path or file name specifying the text file in which the weight images are listed.", true, "", "weight image list", cmd); TCLAP::ValueArg outArg( "o", "output-file", - "A string specifying the name of the output average ODF image.", + "A path or file name specifying the output average ODF image.", true, "", "output odf image", cmd); TCLAP::ValueArg nbpArg( @@ -72,23 +72,37 @@ int main(int argc, char **argv) std::vector inputFiles; std::vector weightFiles; - unsigned int numInputs = 0; + while (!odfFile.eof()) { - char tmpStr[2048], maskStr[2048]; - odfFile.getline(tmpStr, 2048); - weightFile.getline(maskStr, 2048); + char tmpStr[2048], weightStr[2048]; - if (strcmp(tmpStr, "") == 0) + odfFile.getline(tmpStr, 2048); + if (odfFile.fail()) + { + std::cerr << "Error: Failed to read an ODF image." << std::endl; + return EXIT_FAILURE; + } + + weightFile.getline(weightStr, 2048); + if (weightFile.fail()) + { + std::cerr << "Error: Failed to read a weight image." << std::endl; + return EXIT_FAILURE; + } + + if (strcmp(tmpStr, "") == 0 || strcmp(weightStr, "") == 0) continue; inputFiles.push_back(tmpStr); - weightFiles.push_back(maskStr); - numInputs++; + weightFiles.push_back(weightStr); } + odfFile.close(); weightFile.close(); + unsigned int numInputs = weightFiles.size(); + for (unsigned int i = 0; i < numInputs; ++i) { mainFilter->SetInput(i, anima::readImage(inputFiles[i]));