Repository: SimonRit/RTK Branch: main Commit: 4d124fc7fa19 Files: 1343 Total size: 6.8 MB Directory structure: gitextract_m7pjdl55/ ├── .clang-format ├── .gitattributes ├── .github/ │ └── workflows/ │ ├── build-test-cxx-cuda.yml │ ├── build-test-package-python-cuda.yml │ ├── build-test-package.yml │ └── clang-format-linter.yml ├── .gitignore ├── .readthedocs.yml ├── CMakeLists.txt ├── COPYRIGHT.TXT ├── CTestConfig.cmake ├── CodeContribution.md ├── GettingStarted.md ├── HEADER.TXT ├── INSTALLATION.md ├── ITKKWStyleFiles.txt.in ├── LICENSE.TXT ├── ORGANIZATION.TXT ├── README.md ├── applications/ │ ├── CMakeLists.txt │ ├── README.md │ ├── rtk3Doutputimage_group.py │ ├── rtk3Doutputimage_section.ggo │ ├── rtk4Doutputimage_section.ggo │ ├── rtkVersion.ggo.in │ ├── rtkadmmtotalvariation/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── TotalVariationRegularizedReconstruction.sh │ │ ├── rtkadmmtotalvariation.cxx │ │ ├── rtkadmmtotalvariation.ggo │ │ └── rtkadmmtotalvariation.py │ ├── rtkadmmwavelets/ │ │ ├── CMakeLists.txt │ │ ├── DaubechiesWavelets.sh │ │ ├── README.md │ │ ├── rtkadmmwavelets.cxx │ │ └── rtkadmmwavelets.ggo │ ├── rtkamsterdamshroud/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── rtkamsterdamshroud.cxx │ │ ├── rtkamsterdamshroud.ggo │ │ └── rtkamsterdamshroud.py │ ├── rtkargumentparser.py │ ├── rtkbackprojections/ │ │ ├── CMakeLists.txt │ │ ├── rtkbackprojections.cxx │ │ ├── rtkbackprojections.ggo │ │ └── rtkbackprojections.py │ ├── rtkbioscangeometry/ │ │ ├── CMakeLists.txt │ │ ├── rtkbioscangeometry.cxx │ │ ├── rtkbioscangeometry.ggo │ │ └── rtkbioscangeometry.py │ ├── rtkcheckimagequality/ │ │ ├── CMakeLists.txt │ │ ├── rtkcheckimagequality.cxx │ │ ├── rtkcheckimagequality.ggo │ │ └── rtkcheckimagequality.py │ ├── rtkconjugategradient/ │ │ ├── CMakeLists.txt │ │ ├── ConjugateGradient2D.sh │ │ ├── ConjugateGradient3D.sh │ │ ├── NoisyConjugateGradient.sh │ │ ├── README.md │ │ ├── rtkconjugategradient.cxx │ │ ├── rtkconjugategradient.ggo │ │ └── rtkconjugategradient.py │ ├── rtkdigisensgeometry/ │ │ ├── CMakeLists.txt │ │ ├── rtkdigisensgeometry.cxx │ │ ├── rtkdigisensgeometry.ggo │ │ └── rtkdigisensgeometry.py │ ├── rtkdrawgeometricphantom/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── rtkdrawgeometricphantom.cxx │ │ ├── rtkdrawgeometricphantom.ggo │ │ └── rtkdrawgeometricphantom.py │ ├── rtkdrawshepploganphantom/ │ │ ├── CMakeLists.txt │ │ ├── rtkdrawshepploganphantom.cxx │ │ ├── rtkdrawshepploganphantom.ggo │ │ └── rtkdrawshepploganphantom.py │ ├── rtkdualenergyforwardmodel/ │ │ ├── CMakeLists.txt │ │ ├── rtkdualenergyforwardmodel.cxx │ │ ├── rtkdualenergyforwardmodel.ggo │ │ └── rtkdualenergyforwardmodel.py │ ├── rtkdualenergysimplexdecomposition/ │ │ ├── CMakeLists.txt │ │ ├── rtkdualenergysimplexdecomposition.cxx │ │ ├── rtkdualenergysimplexdecomposition.ggo │ │ └── rtkdualenergysimplexdecomposition.py │ ├── rtkelektasynergygeometry/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── rtkelektasynergygeometry.cxx │ │ ├── rtkelektasynergygeometry.ggo │ │ └── rtkelektasynergygeometry.py │ ├── rtkextractphasesignal/ │ │ ├── CMakeLists.txt │ │ ├── rtkextractphasesignal.cxx │ │ ├── rtkextractphasesignal.ggo │ │ └── rtkextractphasesignal.py │ ├── rtkextractshroudsignal/ │ │ ├── CMakeLists.txt │ │ ├── rtkextractshroudsignal.cxx │ │ └── rtkextractshroudsignal.ggo │ ├── rtkfdk/ │ │ ├── CMakeLists.txt │ │ ├── FDK2D.sh │ │ ├── FDK3D.sh │ │ ├── README.md │ │ ├── rtkfdk.cxx │ │ ├── rtkfdk.ggo │ │ └── rtkfdk.py │ ├── rtkfieldofview/ │ │ ├── CMakeLists.txt │ │ ├── rtkfieldofview.cxx │ │ ├── rtkfieldofview.ggo │ │ └── rtkfieldofview.py │ ├── rtkforwardprojections/ │ │ ├── CMakeLists.txt │ │ ├── ForwardProjection.sh │ │ ├── README.md │ │ ├── rtkforwardprojections.cxx │ │ ├── rtkforwardprojections.ggo │ │ └── rtkforwardprojections.py │ ├── rtkfourdconjugategradient/ │ │ ├── CMakeLists.txt │ │ ├── rtkfourdconjugategradient.cxx │ │ └── rtkfourdconjugategradient.ggo │ ├── rtkfourdfdk/ │ │ ├── CMakeLists.txt │ │ ├── rtkfourdfdk.cxx │ │ └── rtkfourdfdk.ggo │ ├── rtkfourdrooster/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── rtkfourdrooster.cxx │ │ └── rtkfourdrooster.ggo │ ├── rtkfourdsart/ │ │ ├── CMakeLists.txt │ │ ├── rtkfourdsart.cxx │ │ └── rtkfourdsart.ggo │ ├── rtkgaincorrection/ │ │ ├── CMakeLists.txt │ │ ├── rtkgaincorrection.cxx │ │ └── rtkgaincorrection.ggo │ ├── rtki0estimation/ │ │ ├── CMakeLists.txt │ │ ├── rtki0estimation.cxx │ │ └── rtki0estimation.ggo │ ├── rtkimagxgeometry/ │ │ ├── CMakeLists.txt │ │ ├── rtkimagxgeometry.cxx │ │ └── rtkimagxgeometry.ggo │ ├── rtkinputprojections_group.py │ ├── rtkinputprojections_section.ggo │ ├── rtkiterations_group.py │ ├── rtkiterations_section.ggo │ ├── rtkiterativefdk/ │ │ ├── CMakeLists.txt │ │ ├── rtkiterativefdk.cxx │ │ └── rtkiterativefdk.ggo │ ├── rtklagcorrection/ │ │ ├── CMakeLists.txt │ │ ├── rtklagcorrection.cxx │ │ └── rtklagcorrection.ggo │ ├── rtkmaskcollimation/ │ │ ├── CMakeLists.txt │ │ ├── rtkmaskcollimation.cxx │ │ └── rtkmaskcollimation.ggo │ ├── rtkmcrooster/ │ │ ├── CMakeLists.txt │ │ ├── rtkmcrooster.cxx │ │ └── rtkmcrooster.ggo │ ├── rtkmotioncompensatedfourdconjugategradient/ │ │ ├── CMakeLists.txt │ │ ├── rtkmotioncompensatedfourdconjugategradient.cxx │ │ └── rtkmotioncompensatedfourdconjugategradient.ggo │ ├── rtkorageometry/ │ │ ├── CMakeLists.txt │ │ ├── rtkorageometry.cxx │ │ ├── rtkorageometry.ggo │ │ └── rtkorageometry.py │ ├── rtkosem/ │ │ ├── CMakeLists.txt │ │ ├── rtkosem.cxx │ │ └── rtkosem.ggo │ ├── rtkoverlayphaseandshroud/ │ │ ├── CMakeLists.txt │ │ ├── rtkoverlayphaseandshroud.cxx │ │ └── rtkoverlayphaseandshroud.ggo │ ├── rtkprojectgeometricphantom/ │ │ ├── CMakeLists.txt │ │ ├── rtkprojectgeometricphantom.cxx │ │ ├── rtkprojectgeometricphantom.ggo │ │ └── rtkprojectgeometricphantom.py │ ├── rtkprojectionmatrix/ │ │ ├── CMakeLists.txt │ │ ├── rtkMatlabSparseMatrix.h │ │ ├── rtkMatlabSparseMatrix.hxx │ │ ├── rtkprojectionmatrix.cxx │ │ └── rtkprojectionmatrix.ggo │ ├── rtkprojections/ │ │ ├── CMakeLists.txt │ │ ├── rtkprojections.cxx │ │ ├── rtkprojections.ggo │ │ └── rtkprojections.py │ ├── rtkprojectors_group.py │ ├── rtkprojectors_section.ggo │ ├── rtkprojectshepploganphantom/ │ │ ├── CMakeLists.txt │ │ ├── rtkprojectshepploganphantom.cxx │ │ ├── rtkprojectshepploganphantom.ggo │ │ └── rtkprojectshepploganphantom.py │ ├── rtkregularizedconjugategradient/ │ │ ├── CMakeLists.txt │ │ ├── rtkregularizedconjugategradient.cxx │ │ └── rtkregularizedconjugategradient.ggo │ ├── rtksart/ │ │ ├── CMakeLists.txt │ │ ├── rtksart.cxx │ │ └── rtksart.ggo │ ├── rtkscatterglarecorrection/ │ │ ├── CMakeLists.txt │ │ ├── rtkscatterglarecorrection.cxx │ │ └── rtkscatterglarecorrection.ggo │ ├── rtkshowgeometry/ │ │ ├── README.md │ │ ├── rtkshowgeometry.py │ │ └── showgeometry.sh │ ├── rtksimulatedgeometry/ │ │ ├── CMakeLists.txt │ │ ├── rtksimulatedgeometry.cxx │ │ ├── rtksimulatedgeometry.ggo │ │ └── rtksimulatedgeometry.py │ ├── rtkspectraldenoiseprojections/ │ │ ├── CMakeLists.txt │ │ ├── rtkspectraldenoiseprojections.cxx │ │ └── rtkspectraldenoiseprojections.ggo │ ├── rtkspectralforwardmodel/ │ │ ├── CMakeLists.txt │ │ ├── rtkspectralforwardmodel.cxx │ │ └── rtkspectralforwardmodel.ggo │ ├── rtkspectralonestep/ │ │ ├── CMakeLists.txt │ │ ├── rtkspectralonestep.cxx │ │ └── rtkspectralonestep.ggo │ ├── rtkspectralrooster/ │ │ ├── CMakeLists.txt │ │ ├── rtkspectralrooster.cxx │ │ └── rtkspectralrooster.ggo │ ├── rtkspectralsimplexdecomposition/ │ │ ├── CMakeLists.txt │ │ ├── rtkspectralsimplexdecomposition.cxx │ │ └── rtkspectralsimplexdecomposition.ggo │ ├── rtksubselect/ │ │ ├── CMakeLists.txt │ │ ├── rtksubselect.cxx │ │ └── rtksubselect.ggo │ ├── rtktotalnuclearvariationdenoising/ │ │ ├── CMakeLists.txt │ │ ├── rtktotalnuclearvariationdenoising.cxx │ │ └── rtktotalnuclearvariationdenoising.ggo │ ├── rtktotalvariationdenoising/ │ │ ├── CMakeLists.txt │ │ ├── rtktotalvariationdenoising.cxx │ │ └── rtktotalvariationdenoising.ggo │ ├── rtktutorialapplication/ │ │ ├── CMakeLists.txt │ │ ├── rtktutorialapplication.cxx │ │ └── rtktutorialapplication.ggo │ ├── rtkvarianobigeometry/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── rtkvarianobigeometry.cxx │ │ ├── rtkvarianobigeometry.ggo │ │ └── rtkvarianobigeometry.py │ ├── rtkvarianprobeamgeometry/ │ │ ├── CMakeLists.txt │ │ ├── rtkvarianprobeamgeometry.cxx │ │ └── rtkvarianprobeamgeometry.ggo │ ├── rtkvectorconjugategradient/ │ │ ├── CMakeLists.txt │ │ ├── rtkvectorconjugategradient.cxx │ │ └── rtkvectorconjugategradient.ggo │ ├── rtkversion.py.in │ ├── rtkwaveletsdenoising/ │ │ ├── CMakeLists.txt │ │ ├── rtkwaveletsdenoising.cxx │ │ └── rtkwaveletsdenoising.ggo │ └── rtkxradgeometry/ │ ├── CMakeLists.txt │ ├── rtkxradgeometry.cxx │ └── rtkxradgeometry.ggo ├── cmake/ │ ├── FindGengetopt.cmake │ ├── GetGitRevisionDescription.cmake │ ├── GetGitRevisionDescription.cmake.in │ ├── Hooks/ │ │ ├── pre-commit │ │ └── pre-commit-style.bash │ ├── KWStyle/ │ │ ├── RTK.kws.xml │ │ ├── RTKHeader.h │ │ └── RTKOverwrite.txt │ └── rtkCompilerFlags.cmake ├── conf.py ├── documentation/ │ ├── Doxygen/ │ │ ├── CMakeLists.txt │ │ ├── DoxygenFooter.html │ │ ├── DoxygenHeader.html │ │ ├── DoxygenStyle.css │ │ ├── MainPage.dox │ │ ├── Modules.dox │ │ ├── doxygen.config.in │ │ ├── itkdoxygen.pl.in │ │ ├── itkgroup.pl │ │ ├── rtkThreeDCirculationProjectionGeometry.dox │ │ └── vxl_doxy.pl │ └── docs/ │ ├── CMakeLists.txt │ ├── ExternalData/ │ │ ├── AddNoise.png.sha512 │ │ ├── AdmmWavelets.png.sha512 │ │ ├── Admmtv.png.sha512 │ │ ├── Amsterdam.png.sha512 │ │ ├── Blurred.jpg.sha512 │ │ ├── Blurred_vs_mc.gif.sha512 │ │ ├── ConjugateGradient-2D.png.sha512 │ │ ├── ConjugateGradient-3D.png.sha512 │ │ ├── ConjugateGradient-Sinogram-2D.png.sha512 │ │ ├── Elekta.png.sha512 │ │ ├── Fdk-2D.png.sha512 │ │ ├── Fdk-3D.png.sha512 │ │ ├── GammexPhantom.png.sha512 │ │ ├── MotionMask.jpg.sha512 │ │ ├── Moving-Phantom-Sinogram.png.sha512 │ │ ├── POPI-Reconstruction.png.sha512 │ │ ├── POPI-Sinogram.png.sha512 │ │ ├── SheppLogan-Sinogram-2D.png.sha512 │ │ ├── SheppLogan-Sinogram-3D.png.sha512 │ │ ├── ShowGeometry.png.sha512 │ │ ├── Signal.jpg.sha512 │ │ ├── Thorax-Sinogram.png.sha512 │ │ ├── Thorax-fdk.png.sha512 │ │ ├── Thorax-visualisation.png.sha512 │ │ ├── Varian.png.sha512 │ │ ├── VarianProBeam.png.sha512 │ │ └── VectorField.gif.sha512 │ ├── Geometry.md │ ├── Phantom.md │ ├── Projectors.md │ ├── README.md │ ├── Release.md │ ├── copy_and_fetch_sphinx_doc_files.cmake │ ├── requirements.txt │ └── rtk_3_migration_guide.md ├── examples/ │ ├── AddNoise/ │ │ ├── AddNoise.cxx │ │ ├── AddNoise.py │ │ ├── CMakeLists.txt │ │ └── README.md │ ├── ConjugateGradient/ │ │ ├── CMakeLists.txt │ │ ├── ConjugateGradient.cxx │ │ ├── ConjugateGradient.py │ │ └── README.md │ ├── FirstReconstruction/ │ │ ├── CMakeLists.txt │ │ ├── FirstCudaReconstruction.cxx │ │ ├── FirstCudaReconstruction.py │ │ ├── FirstReconstruction.cxx │ │ ├── FirstReconstruction.py │ │ └── README.md │ ├── GeometricPhantom/ │ │ ├── CMakeLists.txt │ │ ├── GeometricPhantom.cxx │ │ ├── GeometricPhantom.py │ │ └── README.md │ ├── InlineReconstruction/ │ │ ├── CMakeLists.txt │ │ ├── InlineReconstruction.cxx │ │ ├── InlineReconstruction.py │ │ └── README.md │ ├── README.md │ └── WaterPreCorrection/ │ ├── Profile.png.sha512 │ ├── README.md │ └── WaterPreCorrection.py ├── include/ │ ├── rtkADMMTotalVariationConeBeamReconstructionFilter.h │ ├── rtkADMMTotalVariationConeBeamReconstructionFilter.hxx │ ├── rtkADMMTotalVariationConjugateGradientOperator.h │ ├── rtkADMMTotalVariationConjugateGradientOperator.hxx │ ├── rtkADMMWaveletsConeBeamReconstructionFilter.h │ ├── rtkADMMWaveletsConeBeamReconstructionFilter.hxx │ ├── rtkADMMWaveletsConjugateGradientOperator.h │ ├── rtkADMMWaveletsConjugateGradientOperator.hxx │ ├── rtkAddMatrixAndDiagonalImageFilter.h │ ├── rtkAddMatrixAndDiagonalImageFilter.hxx │ ├── rtkAdditiveGaussianNoiseImageFilter.h │ ├── rtkAdditiveGaussianNoiseImageFilter.hxx │ ├── rtkAmsterdamShroudImageFilter.h │ ├── rtkAmsterdamShroudImageFilter.hxx │ ├── rtkAverageOutOfROIImageFilter.h │ ├── rtkAverageOutOfROIImageFilter.hxx │ ├── rtkBackProjectionImageFilter.h │ ├── rtkBackProjectionImageFilter.hxx │ ├── rtkBackwardDifferenceDivergenceImageFilter.h │ ├── rtkBackwardDifferenceDivergenceImageFilter.hxx │ ├── rtkBioscanGeometryReader.h │ ├── rtkBlockDiagonalMatrixVectorMultiplyImageFilter.h │ ├── rtkBlockDiagonalMatrixVectorMultiplyImageFilter.hxx │ ├── rtkBoellaardScatterCorrectionImageFilter.h │ ├── rtkBoellaardScatterCorrectionImageFilter.hxx │ ├── rtkBoxShape.h │ ├── rtkConditionalMedianImageFilter.h │ ├── rtkConditionalMedianImageFilter.hxx │ ├── rtkConjugateGradientConeBeamReconstructionFilter.h │ ├── rtkConjugateGradientConeBeamReconstructionFilter.hxx │ ├── rtkConjugateGradientGetP_kPlusOneImageFilter.h │ ├── rtkConjugateGradientGetP_kPlusOneImageFilter.hxx │ ├── rtkConjugateGradientGetR_kPlusOneImageFilter.h │ ├── rtkConjugateGradientGetR_kPlusOneImageFilter.hxx │ ├── rtkConjugateGradientGetX_kPlusOneImageFilter.h │ ├── rtkConjugateGradientGetX_kPlusOneImageFilter.hxx │ ├── rtkConjugateGradientImageFilter.h │ ├── rtkConjugateGradientImageFilter.hxx │ ├── rtkConjugateGradientOperator.h │ ├── rtkConjugateGradientOperator.hxx │ ├── rtkConstantImageSource.h │ ├── rtkConstantImageSource.hxx │ ├── rtkConvexShape.h │ ├── rtkCudaAverageOutOfROIImageFilter.h │ ├── rtkCudaAverageOutOfROIImageFilter.hcu │ ├── rtkCudaBackProjectionImageFilter.h │ ├── rtkCudaBackProjectionImageFilter.hcu │ ├── rtkCudaBackProjectionImageFilter.hxx │ ├── rtkCudaConjugateGradientImageFilter.h │ ├── rtkCudaConjugateGradientImageFilter.hcu │ ├── rtkCudaConjugateGradientImageFilter.hxx │ ├── rtkCudaConstantVolumeSeriesSource.h │ ├── rtkCudaConstantVolumeSeriesSource.hcu │ ├── rtkCudaConstantVolumeSource.h │ ├── rtkCudaConstantVolumeSource.hcu │ ├── rtkCudaCropImageFilter.h │ ├── rtkCudaCropImageFilter.hcu │ ├── rtkCudaCyclicDeformationImageFilter.h │ ├── rtkCudaCyclicDeformationImageFilter.hcu │ ├── rtkCudaDisplacedDetectorImageFilter.h │ ├── rtkCudaDisplacedDetectorImageFilter.hcu │ ├── rtkCudaFDKBackProjectionImageFilter.h │ ├── rtkCudaFDKBackProjectionImageFilter.hcu │ ├── rtkCudaFDKConeBeamReconstructionFilter.h │ ├── rtkCudaFDKWeightProjectionFilter.h │ ├── rtkCudaFDKWeightProjectionFilter.hcu │ ├── rtkCudaFFTProjectionsConvolutionImageFilter.h │ ├── rtkCudaFFTProjectionsConvolutionImageFilter.hcu │ ├── rtkCudaFFTProjectionsConvolutionImageFilter.hxx │ ├── rtkCudaFFTRampImageFilter.h │ ├── rtkCudaFirstOrderKernels.hcu │ ├── rtkCudaForwardProjectionImageFilter.h │ ├── rtkCudaForwardProjectionImageFilter.hcu │ ├── rtkCudaForwardProjectionImageFilter.hxx │ ├── rtkCudaForwardWarpImageFilter.h │ ├── rtkCudaForwardWarpImageFilter.hcu │ ├── rtkCudaInterpolateImageFilter.h │ ├── rtkCudaInterpolateImageFilter.hcu │ ├── rtkCudaIntersectBox.hcu │ ├── rtkCudaIterativeFDKConeBeamReconstructionFilter.h │ ├── rtkCudaLagCorrectionImageFilter.h │ ├── rtkCudaLagCorrectionImageFilter.hcu │ ├── rtkCudaLaplacianImageFilter.h │ ├── rtkCudaLaplacianImageFilter.hcu │ ├── rtkCudaLastDimensionTVDenoisingImageFilter.h │ ├── rtkCudaLastDimensionTVDenoisingImageFilter.hcu │ ├── rtkCudaParkerShortScanImageFilter.h │ ├── rtkCudaParkerShortScanImageFilter.hcu │ ├── rtkCudaPolynomialGainCorrectionImageFilter.h │ ├── rtkCudaPolynomialGainCorrectionImageFilter.hcu │ ├── rtkCudaRayCastBackProjectionImageFilter.h │ ├── rtkCudaRayCastBackProjectionImageFilter.hcu │ ├── rtkCudaScatterGlareCorrectionImageFilter.h │ ├── rtkCudaSplatImageFilter.h │ ├── rtkCudaSplatImageFilter.hcu │ ├── rtkCudaTotalVariationDenoisingBPDQImageFilter.h │ ├── rtkCudaTotalVariationDenoisingBPDQImageFilter.hcu │ ├── rtkCudaUtilities.hcu │ ├── rtkCudaWarpBackProjectionImageFilter.h │ ├── rtkCudaWarpBackProjectionImageFilter.hcu │ ├── rtkCudaWarpForwardProjectionImageFilter.h │ ├── rtkCudaWarpForwardProjectionImageFilter.hcu │ ├── rtkCudaWarpImageFilter.h │ ├── rtkCudaWarpImageFilter.hcu │ ├── rtkCudaWeidingerForwardModelImageFilter.h │ ├── rtkCudaWeidingerForwardModelImageFilter.hcu │ ├── rtkCudaWeidingerForwardModelImageFilter.hxx │ ├── rtkCyclicDeformationImageFilter.h │ ├── rtkCyclicDeformationImageFilter.hxx │ ├── rtkDCMImagXImageIO.h │ ├── rtkDCMImagXImageIOFactory.h │ ├── rtkDPExtractShroudSignalImageFilter.h │ ├── rtkDPExtractShroudSignalImageFilter.hxx │ ├── rtkDaubechiesWaveletsConvolutionImageFilter.h │ ├── rtkDaubechiesWaveletsConvolutionImageFilter.hxx │ ├── rtkDaubechiesWaveletsDenoiseSequenceImageFilter.h │ ├── rtkDaubechiesWaveletsDenoiseSequenceImageFilter.hxx │ ├── rtkDbf.h │ ├── rtkDePierroRegularizationImageFilter.h │ ├── rtkDePierroRegularizationImageFilter.hxx │ ├── rtkDeconstructImageFilter.h │ ├── rtkDeconstructImageFilter.hxx │ ├── rtkDeconstructSoftThresholdReconstructImageFilter.h │ ├── rtkDeconstructSoftThresholdReconstructImageFilter.hxx │ ├── rtkDenoisingBPDQImageFilter.h │ ├── rtkDenoisingBPDQImageFilter.hxx │ ├── rtkDigisensGeometryReader.h │ ├── rtkDigisensGeometryXMLFileReader.h │ ├── rtkDisplacedDetectorForOffsetFieldOfViewImageFilter.h │ ├── rtkDisplacedDetectorForOffsetFieldOfViewImageFilter.hxx │ ├── rtkDisplacedDetectorImageFilter.h │ ├── rtkDisplacedDetectorImageFilter.hxx │ ├── rtkDivergenceOfGradientConjugateGradientOperator.h │ ├── rtkDivergenceOfGradientConjugateGradientOperator.hxx │ ├── rtkDownsampleImageFilter.h │ ├── rtkDownsampleImageFilter.hxx │ ├── rtkDrawBoxImageFilter.h │ ├── rtkDrawBoxImageFilter.hxx │ ├── rtkDrawConeImageFilter.h │ ├── rtkDrawConeImageFilter.hxx │ ├── rtkDrawConvexImageFilter.h │ ├── rtkDrawConvexImageFilter.hxx │ ├── rtkDrawCubeImageFilter.h │ ├── rtkDrawCylinderImageFilter.h │ ├── rtkDrawCylinderImageFilter.hxx │ ├── rtkDrawEllipsoidImageFilter.h │ ├── rtkDrawEllipsoidImageFilter.hxx │ ├── rtkDrawGeometricPhantomImageFilter.h │ ├── rtkDrawGeometricPhantomImageFilter.hxx │ ├── rtkDrawQuadricImageFilter.h │ ├── rtkDrawQuadricImageFilter.hxx │ ├── rtkDrawSheppLoganFilter.h │ ├── rtkDrawSheppLoganFilter.hxx │ ├── rtkDualEnergyNegativeLogLikelihood.h │ ├── rtkEdfImageIO.h │ ├── rtkEdfImageIOFactory.h │ ├── rtkEdfRawToAttenuationImageFilter.h │ ├── rtkEdfRawToAttenuationImageFilter.hxx │ ├── rtkElektaSynergyGeometryReader.h │ ├── rtkElektaSynergyLookupTableImageFilter.h │ ├── rtkElektaSynergyLookupTableImageFilter.hxx │ ├── rtkElektaSynergyRawLookupTableImageFilter.h │ ├── rtkElektaSynergyRawLookupTableImageFilter.hxx │ ├── rtkElektaXVI5GeometryXMLFileReader.h │ ├── rtkExtractPhaseImageFilter.h │ ├── rtkExtractPhaseImageFilter.hxx │ ├── rtkFDKBackProjectionImageFilter.h │ ├── rtkFDKBackProjectionImageFilter.hxx │ ├── rtkFDKConeBeamReconstructionFilter.h │ ├── rtkFDKConeBeamReconstructionFilter.hxx │ ├── rtkFDKVarianceReconstructionFilter.h │ ├── rtkFDKVarianceReconstructionFilter.hxx │ ├── rtkFDKWarpBackProjectionImageFilter.h │ ├── rtkFDKWarpBackProjectionImageFilter.hxx │ ├── rtkFDKWeightProjectionFilter.h │ ├── rtkFDKWeightProjectionFilter.hxx │ ├── rtkFFTHilbertImageFilter.h │ ├── rtkFFTHilbertImageFilter.hxx │ ├── rtkFFTProjectionsConvolutionImageFilter.h │ ├── rtkFFTProjectionsConvolutionImageFilter.hxx │ ├── rtkFFTRampImageFilter.h │ ├── rtkFFTRampImageFilter.hxx │ ├── rtkFFTVarianceRampImageFilter.h │ ├── rtkFFTVarianceRampImageFilter.hxx │ ├── rtkFieldOfViewImageFilter.h │ ├── rtkFieldOfViewImageFilter.hxx │ ├── rtkForbildPhantomFileReader.h │ ├── rtkForwardDifferenceGradientImageFilter.h │ ├── rtkForwardDifferenceGradientImageFilter.hxx │ ├── rtkForwardProjectionImageFilter.h │ ├── rtkForwardProjectionImageFilter.hxx │ ├── rtkForwardWarpImageFilter.h │ ├── rtkForwardWarpImageFilter.hxx │ ├── rtkFourDConjugateGradientConeBeamReconstructionFilter.h │ ├── rtkFourDConjugateGradientConeBeamReconstructionFilter.hxx │ ├── rtkFourDROOSTERConeBeamReconstructionFilter.h │ ├── rtkFourDROOSTERConeBeamReconstructionFilter.hxx │ ├── rtkFourDReconstructionConjugateGradientOperator.h │ ├── rtkFourDReconstructionConjugateGradientOperator.hxx │ ├── rtkFourDSARTConeBeamReconstructionFilter.h │ ├── rtkFourDSARTConeBeamReconstructionFilter.hxx │ ├── rtkFourDToProjectionStackImageFilter.h │ ├── rtkFourDToProjectionStackImageFilter.hxx │ ├── rtkGeneralPurposeFunctions.h │ ├── rtkGeometricPhantom.h │ ├── rtkGetNewtonUpdateImageFilter.h │ ├── rtkGetNewtonUpdateImageFilter.hxx │ ├── rtkGgoArgsInfoManager.h │ ├── rtkGgoFunctions.h │ ├── rtkGlobalResourceProbe.h │ ├── rtkHilbertImageFilter.h │ ├── rtkHilbertImageFilter.hxx │ ├── rtkHisImageIO.h │ ├── rtkHisImageIOFactory.h │ ├── rtkHncImageIO.h │ ├── rtkHncImageIOFactory.h │ ├── rtkHndImageIO.h │ ├── rtkHndImageIOFactory.h │ ├── rtkHomogeneousMatrix.h │ ├── rtkI0EstimationProjectionFilter.h │ ├── rtkI0EstimationProjectionFilter.hxx │ ├── rtkIOFactories.h │ ├── rtkImagXGeometryReader.h │ ├── rtkImagXGeometryReader.hxx │ ├── rtkImagXImageIO.h │ ├── rtkImagXImageIOFactory.h │ ├── rtkImagXXMLFileReader.h │ ├── rtkImageToVectorImageFilter.h │ ├── rtkImageToVectorImageFilter.hxx │ ├── rtkImportImageFilter.h │ ├── rtkImportImageFilter.hxx │ ├── rtkInterpolatorWithKnownWeightsImageFilter.h │ ├── rtkInterpolatorWithKnownWeightsImageFilter.hxx │ ├── rtkIntersectionOfConvexShapes.h │ ├── rtkIterationCommands.h │ ├── rtkIterativeConeBeamReconstructionFilter.h │ ├── rtkIterativeConeBeamReconstructionFilter.hxx │ ├── rtkIterativeFDKConeBeamReconstructionFilter.h │ ├── rtkIterativeFDKConeBeamReconstructionFilter.hxx │ ├── rtkJosephBackAttenuatedProjectionImageFilter.h │ ├── rtkJosephBackAttenuatedProjectionImageFilter.hxx │ ├── rtkJosephBackProjectionImageFilter.h │ ├── rtkJosephBackProjectionImageFilter.hxx │ ├── rtkJosephForwardAttenuatedProjectionImageFilter.h │ ├── rtkJosephForwardAttenuatedProjectionImageFilter.hxx │ ├── rtkJosephForwardProjectionImageFilter.h │ ├── rtkJosephForwardProjectionImageFilter.hxx │ ├── rtkLUTbasedVariableI0RawToAttenuationImageFilter.h │ ├── rtkLUTbasedVariableI0RawToAttenuationImageFilter.hxx │ ├── rtkLagCorrectionImageFilter.h │ ├── rtkLagCorrectionImageFilter.hxx │ ├── rtkLaplacianImageFilter.h │ ├── rtkLaplacianImageFilter.hxx │ ├── rtkLastDimensionL0GradientDenoisingImageFilter.h │ ├── rtkLastDimensionL0GradientDenoisingImageFilter.hxx │ ├── rtkLookupTableImageFilter.h │ ├── rtkLookupTableImageFilter.hxx │ ├── rtkMacro.h │ ├── rtkMagnitudeThresholdImageFilter.h │ ├── rtkMagnitudeThresholdImageFilter.hxx │ ├── rtkMaskCollimationImageFilter.h │ ├── rtkMaskCollimationImageFilter.hxx │ ├── rtkMaximumIntensityProjectionImageFilter.h │ ├── rtkMechlemOneStepSpectralReconstructionFilter.h │ ├── rtkMechlemOneStepSpectralReconstructionFilter.hxx │ ├── rtkMotionCompensatedFourDConjugateGradientConeBeamReconstructionFilter.h │ ├── rtkMotionCompensatedFourDConjugateGradientConeBeamReconstructionFilter.hxx │ ├── rtkMotionCompensatedFourDROOSTERConeBeamReconstructionFilter.h │ ├── rtkMotionCompensatedFourDROOSTERConeBeamReconstructionFilter.hxx │ ├── rtkMotionCompensatedFourDReconstructionConjugateGradientOperator.h │ ├── rtkMotionCompensatedFourDReconstructionConjugateGradientOperator.hxx │ ├── rtkMultiplyByVectorImageFilter.h │ ├── rtkMultiplyByVectorImageFilter.hxx │ ├── rtkNesterovUpdateImageFilter.h │ ├── rtkNesterovUpdateImageFilter.hxx │ ├── rtkOSEMConeBeamReconstructionFilter.h │ ├── rtkOSEMConeBeamReconstructionFilter.hxx │ ├── rtkOraGeometryReader.h │ ├── rtkOraImageIO.h │ ├── rtkOraImageIOFactory.h │ ├── rtkOraLookupTableImageFilter.h │ ├── rtkOraLookupTableImageFilter.hxx │ ├── rtkOraXMLFileReader.h │ ├── rtkParkerShortScanImageFilter.h │ ├── rtkParkerShortScanImageFilter.hxx │ ├── rtkPhaseGatingImageFilter.h │ ├── rtkPhaseGatingImageFilter.hxx │ ├── rtkPhaseReader.h │ ├── rtkPhasesToInterpolationWeights.h │ ├── rtkPolynomialGainCorrectionImageFilter.h │ ├── rtkPolynomialGainCorrectionImageFilter.hxx │ ├── rtkProgressCommands.h │ ├── rtkProjectGeometricPhantomImageFilter.h │ ├── rtkProjectGeometricPhantomImageFilter.hxx │ ├── rtkProjectionGeometry.h │ ├── rtkProjectionGeometry.hxx │ ├── rtkProjectionStackToFourDImageFilter.h │ ├── rtkProjectionStackToFourDImageFilter.hxx │ ├── rtkProjectionsDecompositionNegativeLogLikelihood.h │ ├── rtkProjectionsReader.h │ ├── rtkProjectionsReader.hxx │ ├── rtkProjectionsRegionConstIteratorRayBased.h │ ├── rtkProjectionsRegionConstIteratorRayBased.hxx │ ├── rtkProjectionsRegionConstIteratorRayBasedParallel.h │ ├── rtkProjectionsRegionConstIteratorRayBasedParallel.hxx │ ├── rtkProjectionsRegionConstIteratorRayBasedWithCylindricalPanel.h │ ├── rtkProjectionsRegionConstIteratorRayBasedWithCylindricalPanel.hxx │ ├── rtkProjectionsRegionConstIteratorRayBasedWithFlatPanel.h │ ├── rtkProjectionsRegionConstIteratorRayBasedWithFlatPanel.hxx │ ├── rtkQuadricShape.h │ ├── rtkRayBoxIntersectionImageFilter.h │ ├── rtkRayBoxIntersectionImageFilter.hxx │ ├── rtkRayConvexIntersectionImageFilter.h │ ├── rtkRayConvexIntersectionImageFilter.hxx │ ├── rtkRayEllipsoidIntersectionImageFilter.h │ ├── rtkRayEllipsoidIntersectionImageFilter.hxx │ ├── rtkRayQuadricIntersectionImageFilter.h │ ├── rtkRayQuadricIntersectionImageFilter.hxx │ ├── rtkReconstructImageFilter.h │ ├── rtkReconstructImageFilter.hxx │ ├── rtkReconstructionConjugateGradientOperator.h │ ├── rtkReconstructionConjugateGradientOperator.hxx │ ├── rtkReg1DExtractShroudSignalImageFilter.h │ ├── rtkReg1DExtractShroudSignalImageFilter.hxx │ ├── rtkReg23ProjectionGeometry.h │ ├── rtkRegularizedConjugateGradientConeBeamReconstructionFilter.h │ ├── rtkRegularizedConjugateGradientConeBeamReconstructionFilter.hxx │ ├── rtkReorderProjectionsImageFilter.h │ ├── rtkReorderProjectionsImageFilter.hxx │ ├── rtkResourceProbesCollector.h │ ├── rtkSARTConeBeamReconstructionFilter.h │ ├── rtkSARTConeBeamReconstructionFilter.hxx │ ├── rtkScatterGlareCorrectionImageFilter.h │ ├── rtkScatterGlareCorrectionImageFilter.hxx │ ├── rtkSchlomka2008NegativeLogLikelihood.h │ ├── rtkSelectOneProjectionPerCycleImageFilter.h │ ├── rtkSelectOneProjectionPerCycleImageFilter.hxx │ ├── rtkSeparableQuadraticSurrogateRegularizationImageFilter.h │ ├── rtkSeparableQuadraticSurrogateRegularizationImageFilter.hxx │ ├── rtkSheppLoganPhantom.h │ ├── rtkSheppLoganPhantomFilter.h │ ├── rtkSheppLoganPhantomFilter.hxx │ ├── rtkSignalToInterpolationWeights.h │ ├── rtkSimplexSpectralProjectionsDecompositionImageFilter.h │ ├── rtkSimplexSpectralProjectionsDecompositionImageFilter.hxx │ ├── rtkSingularValueThresholdImageFilter.h │ ├── rtkSingularValueThresholdImageFilter.hxx │ ├── rtkSoftThresholdImageFilter.h │ ├── rtkSoftThresholdImageFilter.hxx │ ├── rtkSoftThresholdTVImageFilter.h │ ├── rtkSoftThresholdTVImageFilter.hxx │ ├── rtkSpectralForwardModelImageFilter.h │ ├── rtkSpectralForwardModelImageFilter.hxx │ ├── rtkSplatWithKnownWeightsImageFilter.h │ ├── rtkSplatWithKnownWeightsImageFilter.hxx │ ├── rtkSubSelectFromListImageFilter.h │ ├── rtkSubSelectFromListImageFilter.hxx │ ├── rtkSubSelectImageFilter.h │ ├── rtkSubSelectImageFilter.hxx │ ├── rtkSumOfSquaresImageFilter.h │ ├── rtkSumOfSquaresImageFilter.hxx │ ├── rtkThreeDCircularProjectionGeometry.h │ ├── rtkThreeDCircularProjectionGeometryXMLFile.h │ ├── rtkThreeDCircularProjectionGeometryXMLFileReader.h │ ├── rtkThreeDCircularProjectionGeometryXMLFileWriter.h │ ├── rtkTotalNuclearVariationDenoisingBPDQImageFilter.h │ ├── rtkTotalNuclearVariationDenoisingBPDQImageFilter.hxx │ ├── rtkTotalVariationDenoiseSequenceImageFilter.h │ ├── rtkTotalVariationDenoiseSequenceImageFilter.hxx │ ├── rtkTotalVariationDenoisingBPDQImageFilter.h │ ├── rtkTotalVariationDenoisingBPDQImageFilter.hxx │ ├── rtkTotalVariationImageFilter.h │ ├── rtkTotalVariationImageFilter.hxx │ ├── rtkUnwarpSequenceConjugateGradientOperator.h │ ├── rtkUnwarpSequenceConjugateGradientOperator.hxx │ ├── rtkUnwarpSequenceImageFilter.h │ ├── rtkUnwarpSequenceImageFilter.hxx │ ├── rtkUpsampleImageFilter.h │ ├── rtkUpsampleImageFilter.hxx │ ├── rtkVarianObiGeometryReader.h │ ├── rtkVarianObiRawImageFilter.h │ ├── rtkVarianObiRawImageFilter.hxx │ ├── rtkVarianObiXMLFileReader.h │ ├── rtkVarianProBeamGeometryReader.h │ ├── rtkVarianProBeamXMLFileReader.h │ ├── rtkVectorImageToImageFilter.h │ ├── rtkVectorImageToImageFilter.hxx │ ├── rtkWarpFourDToProjectionStackImageFilter.h │ ├── rtkWarpFourDToProjectionStackImageFilter.hxx │ ├── rtkWarpProjectionStackToFourDImageFilter.h │ ├── rtkWarpProjectionStackToFourDImageFilter.hxx │ ├── rtkWarpSequenceImageFilter.h │ ├── rtkWarpSequenceImageFilter.hxx │ ├── rtkWatcherForResourceProbe.h │ ├── rtkWaterPrecorrectionImageFilter.h │ ├── rtkWaterPrecorrectionImageFilter.hxx │ ├── rtkWeidingerForwardModelImageFilter.h │ ├── rtkWeidingerForwardModelImageFilter.hxx │ ├── rtkXRadGeometryReader.h │ ├── rtkXRadImageIO.h │ ├── rtkXRadImageIOFactory.h │ ├── rtkXRadRawToAttenuationImageFilter.h │ ├── rtkXRadRawToAttenuationImageFilter.hxx │ ├── rtkXimImageIO.h │ ├── rtkXimImageIOFactory.h │ ├── rtkZengBackProjectionImageFilter.h │ ├── rtkZengBackProjectionImageFilter.hxx │ ├── rtkZengForwardProjectionImageFilter.h │ └── rtkZengForwardProjectionImageFilter.hxx ├── index.md ├── itk-module-init.cmake ├── itk-module.cmake ├── pyproject.toml ├── rtkConfiguration.h.in ├── src/ │ ├── CMakeLists.txt │ ├── rtkBioscanGeometryReader.cxx │ ├── rtkBoxShape.cxx │ ├── rtkConditionalMedianImageFilter.cxx │ ├── rtkConvexShape.cxx │ ├── rtkCudaAverageOutOfROIImageFilter.cu │ ├── rtkCudaAverageOutOfROIImageFilter.cxx │ ├── rtkCudaBackProjectionImageFilter.cu │ ├── rtkCudaConjugateGradientImageFilter.cu │ ├── rtkCudaConstantVolumeSeriesSource.cu │ ├── rtkCudaConstantVolumeSeriesSource.cxx │ ├── rtkCudaConstantVolumeSource.cu │ ├── rtkCudaConstantVolumeSource.cxx │ ├── rtkCudaCropImageFilter.cu │ ├── rtkCudaCropImageFilter.cxx │ ├── rtkCudaCyclicDeformationImageFilter.cu │ ├── rtkCudaCyclicDeformationImageFilter.cxx │ ├── rtkCudaDisplacedDetectorImageFilter.cu │ ├── rtkCudaDisplacedDetectorImageFilter.cxx │ ├── rtkCudaFDKBackProjectionImageFilter.cu │ ├── rtkCudaFDKBackProjectionImageFilter.cxx │ ├── rtkCudaFDKConeBeamReconstructionFilter.cxx │ ├── rtkCudaFDKWeightProjectionFilter.cu │ ├── rtkCudaFDKWeightProjectionFilter.cxx │ ├── rtkCudaFFTProjectionsConvolutionImageFilter.cu │ ├── rtkCudaFirstOrderKernels.cu │ ├── rtkCudaForwardProjectionImageFilter.cu │ ├── rtkCudaForwardWarpImageFilter.cu │ ├── rtkCudaForwardWarpImageFilter.cxx │ ├── rtkCudaInterpolateImageFilter.cu │ ├── rtkCudaInterpolateImageFilter.cxx │ ├── rtkCudaIterativeFDKConeBeamReconstructionFilter.cxx │ ├── rtkCudaLagCorrectionImageFilter.cu │ ├── rtkCudaLagCorrectionImageFilter.cxx │ ├── rtkCudaLaplacianImageFilter.cu │ ├── rtkCudaLaplacianImageFilter.cxx │ ├── rtkCudaLastDimensionTVDenoisingImageFilter.cu │ ├── rtkCudaLastDimensionTVDenoisingImageFilter.cxx │ ├── rtkCudaParkerShortScanImageFilter.cu │ ├── rtkCudaParkerShortScanImageFilter.cxx │ ├── rtkCudaPolynomialGainCorrectionImageFilter.cu │ ├── rtkCudaPolynomialGainCorrectionImageFilter.cxx │ ├── rtkCudaRayCastBackProjectionImageFilter.cu │ ├── rtkCudaRayCastBackProjectionImageFilter.cxx │ ├── rtkCudaSplatImageFilter.cu │ ├── rtkCudaSplatImageFilter.cxx │ ├── rtkCudaTotalVariationDenoisingBPDQImageFilter.cu │ ├── rtkCudaTotalVariationDenoisingBPDQImageFilter.cxx │ ├── rtkCudaUtilities.cu │ ├── rtkCudaWarpBackProjectionImageFilter.cu │ ├── rtkCudaWarpBackProjectionImageFilter.cxx │ ├── rtkCudaWarpForwardProjectionImageFilter.cu │ ├── rtkCudaWarpForwardProjectionImageFilter.cxx │ ├── rtkCudaWarpImageFilter.cu │ ├── rtkCudaWarpImageFilter.cxx │ ├── rtkCudaWeidingerForwardModelImageFilter.cu │ ├── rtkDCMImagXImageIO.cxx │ ├── rtkDCMImagXImageIOFactory.cxx │ ├── rtkDbf.cxx │ ├── rtkDigisensGeometryReader.cxx │ ├── rtkDigisensGeometryXMLFileReader.cxx │ ├── rtkEdfImageIO.cxx │ ├── rtkEdfImageIOFactory.cxx │ ├── rtkElektaSynergyGeometryReader.cxx │ ├── rtkElektaXVI5GeometryXMLFileReader.cxx │ ├── rtkForbildPhantomFileReader.cxx │ ├── rtkGeometricPhantom.cxx │ ├── rtkGlobalResourceProbe.cxx │ ├── rtkHisImageIO.cxx │ ├── rtkHisImageIOFactory.cxx │ ├── rtkHncImageIO.cxx │ ├── rtkHncImageIOFactory.cxx │ ├── rtkHndImageIO.cxx │ ├── rtkHndImageIOFactory.cxx │ ├── rtkIOFactories.cxx │ ├── rtkImagXImageIO.cxx │ ├── rtkImagXImageIOFactory.cxx │ ├── rtkImagXXMLFileReader.cxx │ ├── rtkIntersectionOfConvexShapes.cxx │ ├── rtkOraGeometryReader.cxx │ ├── rtkOraImageIO.cxx │ ├── rtkOraImageIOFactory.cxx │ ├── rtkOraXMLFileReader.cxx │ ├── rtkPhaseReader.cxx │ ├── rtkPhasesToInterpolationWeights.cxx │ ├── rtkQuadricShape.cxx │ ├── rtkReg23ProjectionGeometry.cxx │ ├── rtkResourceProbesCollector.cxx │ ├── rtkSheppLoganPhantom.cxx │ ├── rtkSignalToInterpolationWeights.cxx │ ├── rtkThreeDCircularProjectionGeometry.cxx │ ├── rtkThreeDCircularProjectionGeometryXMLFileReader.cxx │ ├── rtkThreeDCircularProjectionGeometryXMLFileWriter.cxx │ ├── rtkVarianObiGeometryReader.cxx │ ├── rtkVarianObiXMLFileReader.cxx │ ├── rtkVarianProBeamGeometryReader.cxx │ ├── rtkVarianProBeamXMLFileReader.cxx │ ├── rtkWatcherForResourceProbe.cxx │ ├── rtkXRadGeometryReader.cxx │ ├── rtkXRadImageIO.cxx │ ├── rtkXRadImageIOFactory.cxx │ ├── rtkXimImageIO.cxx │ └── rtkXimImageIOFactory.cxx ├── test/ │ ├── Baseline/ │ │ ├── AmsterdamShroud/ │ │ │ ├── Amsterdam.mha.sha512 │ │ │ └── Amsterdam_crop.mha.sha512 │ │ ├── Bioscan/ │ │ │ └── geometry.xml.sha512 │ │ ├── Digisens/ │ │ │ ├── attenuation.mha.sha512 │ │ │ └── geometry.xml.sha512 │ │ ├── ESRF/ │ │ │ └── attenuation.mha.sha512 │ │ ├── Elekta/ │ │ │ ├── attenuation.mha.sha512 │ │ │ ├── geometry.xml.sha512 │ │ │ └── geometry5.xml.sha512 │ │ ├── ImagX/ │ │ │ ├── attenuation.mha.sha512 │ │ │ ├── attenuationDCM.mha.sha512 │ │ │ └── geo.xml.sha512 │ │ ├── Laplacian/ │ │ │ └── Laplacian.mha.sha512 │ │ ├── Ora/ │ │ │ ├── attenuation.mha.sha512 │ │ │ ├── geometry.xml.sha512 │ │ │ ├── geometry_optitrack.xml.sha512 │ │ │ └── geometry_yawtilt.xml.sha512 │ │ ├── Spectral/ │ │ │ └── OneStep/ │ │ │ ├── newtonUpdate.mha.sha512 │ │ │ ├── out1.mha.sha512 │ │ │ └── out2.mha.sha512 │ │ ├── Varian/ │ │ │ ├── attenuation.mha.sha512 │ │ │ ├── attenuationHnc.mha.sha512 │ │ │ ├── attenuationProBeam.mha.sha512 │ │ │ ├── geometry.xml.sha512 │ │ │ ├── geometryHnc.xml.sha512 │ │ │ └── geometryProBeam.xml.sha512 │ │ └── XRad/ │ │ ├── attenuation.mha.sha512 │ │ └── geometry.xml.sha512 │ ├── CMakeLists.txt │ ├── Input/ │ │ ├── Bioscan/ │ │ │ └── bioscan.dcm.sha512 │ │ ├── Digisens/ │ │ │ ├── calibration.cal.sha512 │ │ │ └── ima0010.tif.sha512 │ │ ├── ESRF/ │ │ │ ├── dark.edf.sha512 │ │ │ ├── raw.edf.sha512 │ │ │ └── refHST0000.edf.sha512 │ │ ├── Elekta/ │ │ │ ├── FRAME.DBF.sha512 │ │ │ ├── IMAGE.DBF.sha512 │ │ │ ├── _Frames.xml.sha512 │ │ │ └── raw.his.sha512 │ │ ├── Forbild/ │ │ │ └── Thorax.sha512 │ │ ├── GeometricPhantom/ │ │ │ ├── Geometries.txt.sha512 │ │ │ ├── Geometries_Forbild.txt.sha512 │ │ │ ├── SheppLogan.txt.sha512 │ │ │ └── SheppLogan_forbild.txt.sha512 │ │ ├── ImagX/ │ │ │ ├── 1.dcm.sha512 │ │ │ ├── calibration.xml.sha512 │ │ │ ├── raw.raw.sha512 │ │ │ ├── raw.xml.sha512 │ │ │ └── room.xml.sha512 │ │ ├── Ora/ │ │ │ ├── 084183_20211217170607335.mhd.sha512 │ │ │ ├── 084183_20211217170607335.ora.xml.sha512 │ │ │ ├── 0_afterLog.mhd.sha512 │ │ │ ├── 0_afterLog.ora.xml.sha512 │ │ │ ├── 0_afterLog.raw.sha512 │ │ │ ├── 2006137_20220918183246810.mhd.sha512 │ │ │ └── 2006137_20220918183246810.ora.xml.sha512 │ │ ├── Phases/ │ │ │ ├── phases.txt.sha512 │ │ │ ├── phases_3projs.txt.sha512 │ │ │ └── phases_slow.txt.sha512 │ │ ├── Spectral/ │ │ │ ├── OneStep/ │ │ │ │ ├── binnedDetectorResponse.csv.sha512 │ │ │ │ ├── gradient.mha.sha512 │ │ │ │ ├── hessian.mha.sha512 │ │ │ │ ├── incident_spectrum_64_rows.mha.sha512 │ │ │ │ ├── materialAttenuations.csv.sha512 │ │ │ │ ├── materialProjections.mha.sha512 │ │ │ │ ├── no_vector_incident_spectrum_64_rows.mha.sha512 │ │ │ │ ├── photonCounts.mha.sha512 │ │ │ │ ├── projOfOnes.mha.sha512 │ │ │ │ └── spectrum.mha.sha512 │ │ │ ├── detector_response.mha.sha512 │ │ │ ├── incident_spectrum.mha.sha512 │ │ │ ├── material_attenuations.mha.sha512 │ │ │ └── no_vector_incident_spectrum.mha.sha512 │ │ ├── Varian/ │ │ │ ├── Proj_00000.xim.sha512 │ │ │ ├── acqui.xml.sha512 │ │ │ ├── acqui_probeam.xml.sha512 │ │ │ ├── image_00052.hnc.sha512 │ │ │ └── raw.hnd.sha512 │ │ └── XRad/ │ │ ├── SolidWater_HiGain1x1.header.sha512 │ │ ├── SolidWater_HiGain1x1_firstProj.header.sha512 │ │ ├── SolidWater_HiGain1x1_firstProj.img.sha512 │ │ ├── dark.header.sha512 │ │ ├── dark.img.sha512 │ │ ├── flat.header.sha512 │ │ └── flat.img.sha512 │ ├── rtkI0estimationtest.cxx │ ├── rtkMaximumIntensity.py │ ├── rtkOutputArgumentWrapping.py │ ├── rtkTest.h │ ├── rtkTestConfiguration.h.in │ ├── rtkTestReg23ProjectionGeometry.cxx │ ├── rtkadjointoperatorstest.cxx │ ├── rtkadmmtotalvariationtest.cxx │ ├── rtkadmmwaveletstest.cxx │ ├── rtkamsterdamshroudtest.cxx │ ├── rtkapplicationtest.py │ ├── rtkargsinfomanagertest.cxx │ ├── rtkbinningtest.cxx │ ├── rtkbioscantest.cxx │ ├── rtkconjugategradientreconstructiontest.cxx │ ├── rtkconjugategradienttest.cxx │ ├── rtkcroptest.cxx │ ├── rtkcudaraycastadjointoperatorstest.cxx │ ├── rtkcyclicdeformationtest.cxx │ ├── rtkcylindricaldetectorreconstructiontest.cxx │ ├── rtkdecomposespectralprojectionstest.cxx │ ├── rtkdigisenstest.cxx │ ├── rtkdisplaceddetectorcompcudatest.cxx │ ├── rtkdisplaceddetectorcompoffsettest.cxx │ ├── rtkdisplaceddetectortest.cxx │ ├── rtkdivergencetest.cxx │ ├── rtkdrawgeometricphantomtest.cxx │ ├── rtkedftest.cxx │ ├── rtkelektatest.cxx │ ├── rtkfbpparalleltest.cxx │ ├── rtkfdkprojweightcompcudatest.cxx │ ├── rtkfdktest.cxx │ ├── rtkforbildtest.cxx │ ├── rtkforwardattenuatedprojectiontest.cxx │ ├── rtkforwardprojectiontest.cxx │ ├── rtkfourdadjointoperatorstest.cxx │ ├── rtkfourdconjugategradienttest.cxx │ ├── rtkfourdroostertest.cxx │ ├── rtkfourdsarttest.cxx │ ├── rtkfovtest.cxx │ ├── rtkgaincorrectiontest.cxx │ ├── rtkgeometryclonetest.cxx │ ├── rtkgeometryfiletest.cxx │ ├── rtkgeometryfrommatrixtest.cxx │ ├── rtkgradienttest.cxx │ ├── rtkheadertestfooter.cxx │ ├── rtkhilbertfiltertest.cxx │ ├── rtkimagxtest.cxx │ ├── rtkimporttest.cxx │ ├── rtkinterpolatesplatadjointtest.cxx │ ├── rtkiterativefdktest.cxx │ ├── rtkl0gradientnormtest.cxx │ ├── rtklagcorrectiontest.cxx │ ├── rtklaplaciantest.cxx │ ├── rtklutbasedvarI0rawtoatttest.cxx │ ├── rtkluttest.cxx │ ├── rtkmaximumintensityprojectiontest.cxx │ ├── rtkmotioncompensatedfdktest.cxx │ ├── rtknewtonupdatetest.cxx │ ├── rtkoratest.cxx │ ├── rtkosemtest.cxx │ ├── rtkparallelgeometryfrommatrixtest.cxx │ ├── rtkprojectgeometricphantomtest.cxx │ ├── rtkrampfiltertest.cxx │ ├── rtkrampfiltertest2.cxx │ ├── rtkregularizedconjugategradienttest.cxx │ ├── rtksarttest.cxx │ ├── rtkscatterglarefiltertest.cxx │ ├── rtkselectoneprojpercycletest.cxx │ ├── rtkshortscancompcudatest.cxx │ ├── rtkshortscantest.cxx │ ├── rtkspectralonesteptest.cxx │ ├── rtktestexamples.py │ ├── rtktotalvariationtest.cxx │ ├── rtkvariancereconstructiontest.cxx │ ├── rtkvariantest.cxx │ ├── rtkvectorimageconverterstest.cxx │ ├── rtkwarpfourdtoprojectionstacktest.cxx │ ├── rtkwarpprojectionstacktofourdtest.cxx │ ├── rtkwarptest.cxx │ ├── rtkwaterprecorrectiontest.cxx │ ├── rtkwaveletstest.cxx │ ├── rtkweidingerforwardmodeltest.cxx │ ├── rtkxradtest.cxx │ └── rtkzengforwardprojectiontest.cxx ├── utilities/ │ ├── SetupForDevelopment.sh │ ├── UploadTestData.py │ ├── clang-format.bash │ ├── gengetopt/ │ │ ├── CMakeLists.txt │ │ ├── Makefile.am │ │ ├── acceptedvalues.cpp │ │ ├── acceptedvalues.h │ │ ├── argsdef.c │ │ ├── argsdef.h │ │ ├── cmdline.c │ │ ├── cmdline.ggo │ │ ├── cmdline.h │ │ ├── errorcodes.h │ │ ├── fileutils.cpp │ │ ├── fileutils.h │ │ ├── gengetopt.cc │ │ ├── gengetopt.h │ │ ├── getopt.c │ │ ├── getopt.h │ │ ├── getopt1.c │ │ ├── ggo_options.h │ │ ├── ggos.cpp │ │ ├── ggos.h │ │ ├── global_opts.h │ │ ├── globals.cpp │ │ ├── globals.h │ │ ├── gm.cc │ │ ├── gm.h │ │ ├── gm_utils.cpp │ │ ├── gm_utils.h │ │ ├── groups.h │ │ ├── my_map.h │ │ ├── my_sstream.h │ │ ├── my_string.h │ │ ├── parser.cc │ │ ├── parser.h │ │ ├── parser.yy │ │ ├── scanner.cc │ │ ├── skels/ │ │ │ ├── Makefile.am │ │ │ ├── c_source.cc │ │ │ ├── c_source.h │ │ │ ├── check_modes.cc │ │ │ ├── check_modes.h │ │ │ ├── clear_arg.cc │ │ │ ├── clear_arg.h │ │ │ ├── clear_given.cc │ │ │ ├── clear_given.h │ │ │ ├── copyright.cc │ │ │ ├── copyright.h │ │ │ ├── custom_getopt_gen.cc │ │ │ ├── custom_getopt_gen.h │ │ │ ├── dependant_option.cc │ │ │ ├── dependant_option.h │ │ │ ├── enum_decl.cc │ │ │ ├── enum_decl.h │ │ │ ├── exit_failure.cc │ │ │ ├── exit_failure.h │ │ │ ├── file_save.cc │ │ │ ├── file_save.h │ │ │ ├── file_save_multiple.cc │ │ │ ├── file_save_multiple.h │ │ │ ├── free_list.cc │ │ │ ├── free_list.h │ │ │ ├── free_multiple.cc │ │ │ ├── free_multiple.h │ │ │ ├── free_string.cc │ │ │ ├── free_string.h │ │ │ ├── generic_option.cc │ │ │ ├── generic_option.h │ │ │ ├── given_field.cc │ │ │ ├── given_field.h │ │ │ ├── group_counter.cc │ │ │ ├── group_counter.h │ │ │ ├── group_option.cc │ │ │ ├── group_option.h │ │ │ ├── handle_help.cc │ │ │ ├── handle_help.h │ │ │ ├── handle_version.cc │ │ │ ├── handle_version.h │ │ │ ├── header.cc │ │ │ ├── header.h │ │ │ ├── init_args_info.cc │ │ │ ├── init_args_info.h │ │ │ ├── multiple_fill_array.cc │ │ │ ├── multiple_fill_array.h │ │ │ ├── multiple_opt_list.cc │ │ │ ├── multiple_opt_list.h │ │ │ ├── option_arg.cc │ │ │ ├── option_arg.h │ │ │ ├── print_help_string.cc │ │ │ ├── print_help_string.h │ │ │ ├── required_option.cc │ │ │ ├── required_option.h │ │ │ ├── reset_group.cc │ │ │ ├── reset_group.h │ │ │ ├── update_given.cc │ │ │ └── update_given.h │ │ ├── yyerror.cc │ │ ├── yyerror.h │ │ └── yywrap.c │ └── lp_solve/ │ ├── CMakeLists.txt │ ├── README.txt │ ├── bfp/ │ │ ├── bfp_LUSOL/ │ │ │ ├── LUSOL/ │ │ │ │ ├── LUSOL_LGPL.txt │ │ │ │ ├── LUSOL_Overview.txt │ │ │ │ ├── LUSOL_README.txt │ │ │ │ ├── lusol.c │ │ │ │ ├── lusol.h │ │ │ │ ├── lusol1.c │ │ │ │ ├── lusol2.c │ │ │ │ ├── lusol6a.c │ │ │ │ ├── lusol6l0.c │ │ │ │ ├── lusol6u.c │ │ │ │ ├── lusol7a.c │ │ │ │ └── lusol8a.c │ │ │ ├── lp_LUSOL.c │ │ │ └── lp_LUSOL.h │ │ ├── lp_BFP.h │ │ ├── lp_BFP1.c │ │ └── lp_BFP2.c │ ├── colamd/ │ │ ├── colamd.c │ │ └── colamd.h │ ├── fortify.c │ ├── ini.c │ ├── ini.h │ ├── lp_Hash.c │ ├── lp_Hash.h │ ├── lp_MDO.c │ ├── lp_MDO.h │ ├── lp_MPS.c │ ├── lp_MPS.h │ ├── lp_SOS.c │ ├── lp_SOS.h │ ├── lp_bit.h │ ├── lp_crash.c │ ├── lp_crash.h │ ├── lp_explicit.h │ ├── lp_fortify.h │ ├── lp_lib.c │ ├── lp_lib.h │ ├── lp_matrix.c │ ├── lp_matrix.h │ ├── lp_mipbb.c │ ├── lp_mipbb.h │ ├── lp_params.c │ ├── lp_presolve.c │ ├── lp_presolve.h │ ├── lp_price.c │ ├── lp_price.h │ ├── lp_pricePSE.c │ ├── lp_pricePSE.h │ ├── lp_report.c │ ├── lp_report.h │ ├── lp_rlp.c │ ├── lp_rlp.h │ ├── lp_scale.c │ ├── lp_scale.h │ ├── lp_simplex.c │ ├── lp_simplex.h │ ├── lp_solveDLL.h │ ├── lp_types.h │ ├── lp_utils.c │ ├── lp_utils.h │ ├── lp_wlp.c │ ├── lp_wlp.h │ ├── lpkit.h │ ├── shared/ │ │ ├── commonlib.c │ │ ├── commonlib.h │ │ ├── mmio.c │ │ ├── mmio.h │ │ ├── myblas.c │ │ └── myblas.h │ ├── yacc_read.c │ └── yacc_read.h └── wrapping/ ├── CMakeLists.txt ├── ConvexShape.i ├── FieldOfViewImageFilter.i ├── __init_rtk__.py ├── itkContinuousIndexRTK.wrap ├── itkCovariantVectorRTK.wrap ├── itkCudaImageDataManagerRTK.wrap ├── itkCudaImageRTK.wrap ├── itkCudaImageToImageFilterRTK.wrap ├── itkCudaInPlaceImageFilterRTK.wrap ├── itkFixedArrayRTK.wrap ├── itkImageBaseRTK.wrap ├── itkImageDuplicatorRTK.wrap ├── itkImageFileReaderRTK.wrap ├── itkImageFileWriterRTK.wrap ├── itkImageRTK.wrap ├── itkImageRegionRTK.wrap ├── itkImageSourceRTK.wrap ├── itkImageToImageFilterRTK.wrap ├── itkInPlaceImageFilterRTK.wrap ├── itkMatrixRTK.wrap ├── itkPointRTK.wrap ├── itkPyBufferRTK.wrap ├── itkVariableLengthVectorRTK.wrap ├── itkVectorImageRTK.wrap ├── itkVectorRTK.wrap ├── itkWarpImageFilterRTK.wrap ├── rtkADMMTotalVariationConeBeamReconstructionFilter.wrap ├── rtkADMMWaveletsConeBeamReconstructionFilter.wrap ├── rtkAmsterdamShroudImageFilter.wrap ├── rtkBackProjectionImageFilter.wrap ├── rtkBioscanGeometryReader.wrap ├── rtkBoellaardScatterCorrectionImageFilter.wrap ├── rtkBoxShape.wrap ├── rtkConditionalMedianImageFilter.wrap ├── rtkConjugateGradientConeBeamReconstructionFilter.wrap ├── rtkConstantImageSource.wrap ├── rtkConvexShape.wrap ├── rtkCudaBackProjectionImageFilter.wrap ├── rtkCudaConstantVolumeSource.wrap ├── rtkCudaDisplacedDetectorImageFilter.wrap ├── rtkCudaFDKBackProjectionImageFilter.wrap ├── rtkCudaFDKConeBeamReconstructionFilter.wrap ├── rtkCudaFFTProjectionsConvolutionImageFilter.wrap ├── rtkCudaFFTRampImageFilter.wrap ├── rtkCudaForwardProjectionImageFilter.wrap ├── rtkCudaIterativeFDKConeBeamReconstructionFilter.wrap ├── rtkCudaParkerShortScanImageFilter.wrap ├── rtkCudaRayCastBackProjectionImageFilter.wrap ├── rtkCudaScatterGlareCorrectionImageFilter.wrap ├── rtkCudaSplatImageFilter.wrap ├── rtkCudaWarpForwardProjectionImageFilter.wrap ├── rtkCyclicDeformationImageFilter.wrap ├── rtkDCMImagXImageIO.wrap ├── rtkDPExtractShroudSignalImageFilter.notwrap ├── rtkDaubechiesWaveletsDenoiseSequenceImageFilter.wrap ├── rtkDeconstructSoftThresholdReconstructImageFilter.wrap ├── rtkDenoisingBPDQImageFilter.wrap ├── rtkDigisensGeometryReader.wrap ├── rtkDisplacedDetectorForOffsetFieldOfViewImageFilter.wrap ├── rtkDisplacedDetectorImageFilter.wrap ├── rtkDrawConvexImageFilter.wrap ├── rtkDrawEllipsoidImageFilter.wrap ├── rtkDrawGeometricPhantomImageFilter.wrap ├── rtkDrawQuadricImageFilter.wrap ├── rtkDrawSheppLoganFilter.wrap ├── rtkEdfImageIO.wrap ├── rtkEdfRawToAttenuationImageFilter.wrap ├── rtkElektaSynergyGeometryReader.wrap ├── rtkElektaSynergyRawLookupTableImageFilter.wrap ├── rtkElektaXVI5GeometryXMLFileReader.wrap ├── rtkExtractPhaseImageFilter.wrap ├── rtkExtras.py ├── rtkFDKBackProjectionImageFilter.wrap ├── rtkFDKConeBeamReconstructionFilter.wrap ├── rtkFDKVarianceReconstructionFilter.wrap ├── rtkFDKWarpBackProjectionImageFilter.wrap ├── rtkFDKWeightProjectionFilter.wrap ├── rtkFFTHilbertImageFilter.wrap ├── rtkFFTProjectionsConvolutionImageFilter.wrap ├── rtkFFTRampImageFilter.wrap ├── rtkFFTVarianceRampImageFilter.wrap ├── rtkFieldOfViewImageFilter.wrap ├── rtkForwardProjectionImageFilter.wrap ├── rtkForwardWarpImageFilter.wrap ├── rtkFourDConjugateGradientConeBeamReconstructionFilter.wrap ├── rtkFourDROOSTERConeBeamReconstructionFilter.wrap ├── rtkFourDSARTConeBeamReconstructionFilter.wrap ├── rtkFourDToProjectionStackImageFilter.wrap ├── rtkGeometricPhantom.wrap ├── rtkGlobalResourceProbe.wrap ├── rtkHilbertImageFilter.notwrap ├── rtkHisImageIO.wrap ├── rtkHncImageIO.wrap ├── rtkHndImageIO.wrap ├── rtkI0EstimationProjectionFilter.wrap ├── rtkImagXImageIO.wrap ├── rtkImageToVectorImageFilter.wrap ├── rtkIntersectionOfConvexShapes.wrap ├── rtkIterativeConeBeamReconstructionFilter.wrap ├── rtkIterativeFDKConeBeamReconstructionFilter.wrap ├── rtkJosephBackAttenuatedProjectionImageFilter.wrap ├── rtkJosephBackProjectionImageFilter.wrap ├── rtkJosephForwardAttenuatedProjectionImageFilter.wrap ├── rtkJosephForwardProjectionImageFilter.wrap ├── rtkLUTbasedVariableI0RawToAttenuationImageFilter.wrap ├── rtkLagCorrectionImageFilter.wrap ├── rtkLastDimensionL0GradientDenoisingImageFilter.wrap ├── rtkLookupTableImageFilter.wrap ├── rtkMaskCollimationImageFilter.wrap ├── rtkMaximumIntensityProjectionImageFilter.wrap ├── rtkMechlemOneStepSpectralReconstructionFilter.wrap ├── rtkMotionCompensatedFourDConjugateGradientConeBeamReconstructionFilter.wrap ├── rtkOSEMConeBeamReconstructionFilter.wrap ├── rtkOraGeometryReader.wrap ├── rtkOraImageIO.wrap ├── rtkParkerShortScanImageFilter.wrap ├── rtkPhaseGatingImageFilter.wrap ├── rtkPolynomialGainCorrectionImageFilter.wrap ├── rtkProjectGeometricPhantomImageFilter.wrap ├── rtkProjectionGeometry.wrap ├── rtkProjectionStackToFourDImageFilter.wrap ├── rtkProjectionsReader.wrap ├── rtkQuadricShape.wrap ├── rtkRayBoxIntersectionImageFilter.wrap ├── rtkRayConvexIntersectionImageFilter.wrap ├── rtkRayEllipsoidIntersectionImageFilter.wrap ├── rtkRayQuadricIntersectionImageFilter.wrap ├── rtkReg1DExtractShroudSignalImageFilter.notwrap ├── rtkRegularizedConjugateGradientConeBeamReconstructionFilter.wrap ├── rtkReorderProjectionsImageFilter.wrap ├── rtkSARTConeBeamReconstructionFilter.wrap ├── rtkScatterGlareCorrectionImageFilter.wrap ├── rtkSelectOneProjectionPerCycleImageFilter.wrap ├── rtkSheppLoganPhantomFilter.wrap ├── rtkSimplexSpectralProjectionsDecompositionImageFilter.wrap ├── rtkSoftThresholdImageFilter.wrap ├── rtkSpectralForwardModelImageFilter.wrap ├── rtkSplatWithKnownWeightsImageFilter.wrap ├── rtkSubSelectFromListImageFilter.wrap ├── rtkSubSelectImageFilter.wrap ├── rtkThreeDCircularProjectionGeometry.wrap ├── rtkThreeDCircularProjectionGeometryXMLFileReader.wrap ├── rtkThreeDCircularProjectionGeometryXMLFileWriter.wrap ├── rtkTotalVariationDenoisingBPDQImageFilter.wrap ├── rtkTotalVariationImageFilter.wrap ├── rtkVarianObiGeometryReader.wrap ├── rtkVarianObiRawImageFilter.wrap ├── rtkVarianProBeamGeometryReader.wrap ├── rtkVectorImageToImageFilter.wrap ├── rtkWaterPrecorrectionImageFilter.wrap ├── rtkXRadGeometryReader.wrap ├── rtkXRadImageIO.wrap ├── rtkXRadRawToAttenuationImageFilter.wrap ├── rtkXimImageIO.wrap ├── rtkZengBackProjectionImageFilter.wrap └── rtkZengForwardProjectionImageFilter.wrap ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-format ================================================ ## This config file is only relevant for clang-format version 19.1.4 ## ## Examples of each format style can be found on the in the clang-format documentation ## See: https://clang.llvm.org/docs/ClangFormatStyleOptions.html for details of each option ## ## The clang-format binaries can be downloaded as part of the clang binary distributions ## from https://releases.llvm.org/download.html ## ## Use the script Utilities/Maintenance/clang-format.bash to faciliate ## maintaining a consistent code style. ## ## EXAMPLE apply code style enforcement before commit: # Utilities/Maintenance/clang-format.bash --clang ${PATH_TO_CLANG_FORMAT_19.1.4} --modified ## EXAMPLE apply code style enforcement after commit: # Utilities/Maintenance/clang-format.bash --clang ${PATH_TO_CLANG_FORMAT_19.1.4} --last --- # This configuration requires clang-format version 19.1.4 exactly. Language: Cpp AccessModifierOffset: -2 AlignAfterOpenBracket: Align AlignArrayOfStructures: None AlignConsecutiveAssignments: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: true AlignConsecutiveBitFields: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: false AlignConsecutiveDeclarations: Enabled: true AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: true AlignConsecutiveMacros: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: false AlignConsecutiveShortCaseStatements: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCaseArrows: false AlignCaseColons: false AlignConsecutiveTableGenBreakingDAGArgColons: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: false AlignConsecutiveTableGenCondOperatorColons: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: false AlignConsecutiveTableGenDefinitionColons: Enabled: false AcrossEmptyLines: false AcrossComments: false AlignCompound: false AlignFunctionPointers: false PadOperators: false AlignEscapedNewlines: Left AlignOperands: Align AlignTrailingComments: Kind: Always OverEmptyLines: 0 AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false AllowBreakBeforeNoexceptSpecifier: Never AllowShortBlocksOnASingleLine: Never AllowShortCaseExpressionOnASingleLine: true AllowShortCaseLabelsOnASingleLine: false AllowShortCompoundRequirementOnASingleLine: true AllowShortEnumsOnASingleLine: true #AllowShortFunctionsOnASingleLine: Inline Only merge functions defined inside a class. Implies empty. #AllowShortFunctionsOnASingleLine: None (in configuration: None) Never merge functions into a single line. AllowShortFunctionsOnASingleLine: All AllowShortIfStatementsOnASingleLine: Never AllowShortLambdasOnASingleLine: All AllowShortLoopsOnASingleLine: false AlwaysBreakAfterDefinitionReturnType: None AlwaysBreakBeforeMultilineStrings: false AttributeMacros: - __capability BinPackArguments: false BinPackParameters: false BitFieldColonSpacing: Both BraceWrapping: AfterCaseLabel: true AfterClass: true AfterControlStatement: Always AfterEnum: true AfterExternBlock: true AfterFunction: true AfterNamespace: true AfterObjCDeclaration: true AfterStruct: true AfterUnion: true BeforeCatch: true BeforeElse: true BeforeLambdaBody: false BeforeWhile: false IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false BreakAdjacentStringLiterals: true BreakAfterAttributes: Leave BreakAfterJavaFieldAnnotations: false BreakAfterReturnType: All BreakArrays: true BreakBeforeBinaryOperators: None BreakBeforeConceptDeclarations: Always BreakBeforeBraces: Custom BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: true BreakConstructorInitializers: BeforeComma BreakFunctionDefinitionParameters: false BreakInheritanceList: BeforeComma BreakStringLiterals: true BreakTemplateDeclarations: Yes ## The following line allows larger lines in non-documentation code ColumnLimit: 120 CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerIndentWidth: 2 ContinuationIndentWidth: 2 Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false EmptyLineAfterAccessModifier: Never EmptyLineBeforeAccessModifier: LogicalBlock ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH - BOOST_FOREACH IfMacros: - KJ_IF_MAYBE IncludeBlocks: Preserve IncludeCategories: - Regex: '^"(llvm|llvm-c|clang|clang-c)/' Priority: 2 SortPriority: 0 CaseSensitive: false - Regex: '^(<|"(gtest|gmock|isl|json)/)' Priority: 3 SortPriority: 0 CaseSensitive: false - Regex: '.*' Priority: 1 SortPriority: 0 CaseSensitive: false IncludeIsMainRegex: '(Test)?$' IncludeIsMainSourceRegex: '' IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: true IndentExternBlock: AfterExternBlock IndentGotoLabels: true IndentPPDirectives: AfterHash IndentRequiresClause: true IndentWidth: 2 IndentWrappedFunctionNames: false InsertBraces: false InsertNewlineAtEOF: false InsertTrailingCommas: None IntegerLiteralSeparator: Binary: 0 BinaryMinDigits: 0 Decimal: 0 DecimalMinDigits: 0 Hex: 0 HexMinDigits: 0 JavaScriptQuotes: Leave JavaScriptWrapImports: true KeepEmptyLines: AtEndOfFile: false AtStartOfBlock: true AtStartOfFile: true LambdaBodyIndentation: Signature LineEnding: DeriveLF MacroBlockBegin: '' MacroBlockEnd: '' MainIncludeChar: Quote MaxEmptyLinesToKeep: 2 NamespaceIndentation: None ObjCBinPackProtocolList: Auto ObjCBlockIndentWidth: 2 ObjCBreakBeforeNestedBlockParam: true ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: false PackConstructorInitializers: BinPack PenaltyBreakAssignment: 2 PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 ## The following line allows larger lines in non-documentation code PenaltyBreakFirstLessLess: 120 PenaltyBreakOpenParenthesis: 0 PenaltyBreakScopeResolution: 500 PenaltyBreakString: 1000 PenaltyBreakTemplateDeclaration: 10 PenaltyExcessCharacter: 1000000 PenaltyIndentedWhitespace: 0 PenaltyReturnTypeOnItsOwnLine: 200 PointerAlignment: Middle PPIndentWidth: -1 QualifierAlignment: Custom QualifierOrder: - friend - static - inline - constexpr - const - type ReferenceAlignment: Pointer ReflowComments: true RemoveBracesLLVM: false RemoveParentheses: Leave RemoveSemicolon: false RequiresClausePosition: OwnLine RequiresExpressionIndentation: OuterScope SeparateDefinitionBlocks: Leave ShortNamespaceLines: 1 SkipMacroDefinitionBody: false # We may want to sort the includes as a separate pass SortIncludes: Never SortJavaStaticImport: Before # We may want to revisit this later SortUsingDeclarations: Never SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: true SpaceAroundPointerQualifiers: Default SpaceBeforeAssignmentOperators: true SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeJsonColon: false SpaceBeforeParens: ControlStatements SpaceBeforeParensOptions: AfterControlStatements: true AfterForeachMacros: true AfterFunctionDefinitionName: false AfterFunctionDeclarationName: false AfterIfMacros: true AfterOverloadedOperator: false AfterPlacementOperator: true AfterRequiresInClause: false AfterRequiresInExpression: false BeforeNonEmptyParentheses: false SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: false SpacesBeforeTrailingComments: 1 SpacesInAngles: Never SpacesInContainerLiterals: false SpacesInLineCommentPrefix: Minimum: 1 Maximum: -1 SpacesInParens: Never SpacesInParensOptions: ExceptDoubleParentheses: false InCStyleCasts: false InConditionalStatements: false InEmptyParentheses: false Other: false SpacesInSquareBrackets: false Standard: Latest StatementAttributeLikeMacros: - Q_EMIT StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION - ITK_GCC_PRAGMA_PUSH - ITK_GCC_PRAGMA_POP - ITK_GCC_SUPPRESS_Wfloat_equal - ITK_GCC_SUPPRESS_Wformat_nonliteral - ITK_GCC_SUPPRESS_Warray_bounds - ITK_CLANG_PRAGMA_PUSH - ITK_CLANG_PRAGMA_POP - ITK_CLANG_SUPPRESS_Wzero_as_null_pointer_constant - CLANG_PRAGMA_PUSH - CLANG_PRAGMA_POP - CLANG_SUPPRESS_Wfloat_equal - INTEL_PRAGMA_WARN_PUSH - INTEL_PRAGMA_WARN_POP - INTEL_SUPPRESS_warning_1292 - itkTemplateFloatingToIntegerMacro - itkLegacyMacro TableGenBreakInsideDAGArg: DontBreak TabWidth: 2 UseTab: Never VerilogBreakBetweenInstancePorts: true WhitespaceSensitiveMacros: - BOOST_PP_STRINGIZE - CF_SWIFT_NAME - NS_SWIFT_NAME - PP_STRINGIZE - STRINGIZE ... ================================================ FILE: .gitattributes ================================================ .git* export-ignore # Custom attribute to mark sources as using our C++/C code style. [attr]our-c-style whitespace=tab-in-indent,no-lf-at-eof hooks.style=KWStyle,clangformat,uncrustify cmake/*.cxx our-c-style include/*.h* our-c-style src/*.c* our-c-style applications/**/*.h our-c-style applications/**/*.cxx our-c-style applications/**/*.hxx our-c-style examples/**/*.cxx our-c-style utilities/ITKCudaCommon/include/*.h* our-c-style utilities/ITKCudaCommon/src/*.cxx our-c-style test/*h our-c-style test/*cxx our-c-style *.txt whitespace=tab-in-indent,no-lf-at-eof *.cmake whitespace=tab-in-indent,no-lf-at-eof # ExternalData content links must have LF newlines *.md5 crlf=input ================================================ FILE: .github/workflows/build-test-cxx-cuda.yml ================================================ name: 'Build, Test RTK with CUDA' on: [push,pull_request] env: itk-git-tag: "v5.4.3" itk-module-deps: "CudaCommon@8baef219d19d8f320898c3c649539bbb5ba3f509" concurrency: group: '${{ github.workflow }}@${{ github.head_ref || github.run_id }}' cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build-test-cxx: runs-on: ${{ matrix.os }} strategy: max-parallel: 3 matrix: os: [self-hosted-linux, self-hosted-windows] include: - os: self-hosted-linux c-compiler: "gcc" cxx-compiler: "g++" cmake-build-type: "MinSizeRel" - os: self-hosted-windows c-compiler: "cl.exe" cxx-compiler: "cl.exe" cmake-build-type: "Release" steps: - uses: actions/checkout@v4 - name: Install build dependencies run: | python -m pip install --upgrade pip python -m pip install ninja - name: self-hosted cleanup shell: bash run: | rm -fr ../ITK rm -fr ../ITK-build rm -fr ../build - name: Download ITK run: | cd .. git clone https://github.com/InsightSoftwareConsortium/ITK.git cd ITK git checkout ${{ env.itk-git-tag }} - name: Build ITK if: matrix.os != 'self-hosted-windows' shell: bash run: | cd .. mkdir ITK-build cd ITK-build MODULE_ARGS="" MODULE_DEPS=${{ env.itk-module-deps }} for MODULE_INFO in ${MODULE_DEPS//:/ }; do MODULE_NAME=`(echo ${MODULE_INFO} | cut -d'@' -f 1)` MODULE_ARGS="${MODULE_ARGS} -DModule_${MODULE_NAME}:BOOL=ON" MODULE_TAG=`(echo ${MODULE_INFO} | cut -d'@' -f 2)` if [[ -n ${MODULE_TAG} ]]; then MODULE_ARGS+=" -DModule_${MODULE_NAME}_GIT_TAG:STRING=${MODULE_TAG}" fi done if [[ -n $MODULE_ARGS ]]; then echo "Building with modules: $MODULE_ARGS" fi cmake -DCMAKE_C_COMPILER:FILEPATH="${{ matrix.c-compiler }}" -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" -DCMAKE_BUILD_TYPE:STRING=${{ matrix.cmake-build-type }} -DBUILD_TESTING:BOOL=OFF ${{ env.itk-cmake-options }} ${MODULE_ARGS} -GNinja ../ITK ninja - name: Build ITK if: matrix.os == 'self-hosted-windows' shell: pwsh run: | Set-PSDebug -Trace 1 cd .. & "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -SkipAutomaticLocation mkdir ITK-build cd ITK-build $MODULE_ARGS="" $MODULE_DEPS="${{ env.itk-module-deps }}" $MODULES_LIST = $MODULE_DEPS.split(":") foreach($MODULE_INFO in $MODULES_LIST) { if($MODULE_ARGS) { $MODULE_ARGS += " " } $MODULE_NAME = $MODULE_INFO.split("@")[0] $MODULE_ARGS += "-DModule_$MODULE_NAME`:BOOL=ON" $MODULE_TAG = $MODULE_INFO.split("@")[1] if($MODULE_TAG) { $MODULE_ARGS += " -DModule_$MODULE_NAME`_GIT_TAG:STRING=$MODULE_TAG" } } if($MODULE_ARGS) { echo "Building with parameters: ${{ env.itk-cmake-options }} $MODULE_ARGS" } cmake -DCMAKE_C_COMPILER:FILEPATH="${{ matrix.c-compiler }}" -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_CXX_COMPILER="${{ matrix.cxx-compiler }}" -DCMAKE_BUILD_TYPE:STRING=${{ matrix.cmake-build-type }} -DBUILD_TESTING:BOOL=OFF ${{ env.itk-cmake-options }} $MODULE_ARGS.split(" ") -GNinja ../ITK ninja - name: Fetch CTest driver script run: | curl -L https://raw.githubusercontent.com/InsightSoftwareConsortium/ITK/dashboard/itk_common.cmake -O - name: Configure CTest script shell: bash run: | operating_system="${{ matrix.os }}" cat > dashboard.cmake << EOF set(CTEST_SITE "GitHubActions") file(TO_CMAKE_PATH "\$ENV{GITHUB_WORKSPACE}/.." CTEST_DASHBOARD_ROOT) file(TO_CMAKE_PATH "\$ENV{GITHUB_WORKSPACE}/" CTEST_SOURCE_DIRECTORY) file(TO_CMAKE_PATH "\$ENV{GITHUB_WORKSPACE}/../build" CTEST_BINARY_DIRECTORY) set(dashboard_source_name "${GITHUB_REPOSITORY}") if(ENV{GITHUB_REF_NAME} MATCHES "main") set(branch "-main") set(dashboard_model "Continuous") else() set(branch "-${GITHUB_REF}") set(dashboard_model "Experimental") endif() set(CTEST_BUILD_NAME "${GITHUB_REPOSITORY}-${operating_system}-\${branch}") set(CTEST_UPDATE_VERSION_ONLY 1) set(CTEST_TEST_ARGS \${CTEST_TEST_ARGS} PARALLEL_LEVEL \${PARALLEL_LEVEL}) set(CTEST_BUILD_CONFIGURATION "Release") set(CTEST_CMAKE_GENERATOR "Ninja") set(CTEST_CUSTOM_WARNING_EXCEPTION \${CTEST_CUSTOM_WARNING_EXCEPTION} # macOS Azure VM Warning "ld: warning: text-based stub file" ${{ env.warnings-to-ignore }} ) set(dashboard_no_clean 1) set(ENV{CC} ${{ matrix.c-compiler }}) set(ENV{CXX} ${{ matrix.cxx-compiler }}) if(WIN32) set(ENV{PATH} "\${CTEST_DASHBOARD_ROOT}/ITK-build/bin;\$ENV{PATH}") endif() set(dashboard_cache " ITK_DIR:PATH=\${CTEST_DASHBOARD_ROOT}/ITK-build BUILD_TESTING:BOOL=ON ${{ env.cmake-options }} ") string(TIMESTAMP build_date "%Y-%m-%d") message("CDash Build Identifier: \${build_date} \${CTEST_BUILD_NAME}") message("CTEST_SITE = \${CTEST_SITE}") include(\${CTEST_SCRIPT_DIRECTORY}/itk_common.cmake) EOF cat dashboard.cmake - name: Build and test if: matrix.os != 'self-hosted-windows' run: | ctest --output-on-failure -j 2 -VV -S dashboard.cmake ${{ env.ctest-options }} - name: Build and test if: matrix.os == 'self-hosted-windows' shell: pwsh run: | & "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -SkipAutomaticLocation ctest --output-on-failure -j 2 -VV -S dashboard.cmake ${{ env.ctest-options }} ================================================ FILE: .github/workflows/build-test-package-python-cuda.yml ================================================ name: 'Package RTK with CUDA' on: [push,pull_request] env: itk-wheel-tag: 'v5.4.3' itk-python-package-tag: 'release' itk-python-package-org: 'InsightSoftwareConsortium' itk-module-deps: "RTKConsortium/ITKCudaCommon@8baef219d19d8f320898c3c649539bbb5ba3f509" concurrency: group: '${{ github.workflow }}@${{ github.head_ref || github.run_id }}' cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: build-linux-cuda-python-packages: runs-on: self-hosted-linux strategy: max-parallel: 2 matrix: python3-minor-version: ${{ github.event_name == 'pull_request' && fromJSON('["11"]') || fromJSON('["9","10","11"]') }} manylinux-platform: ["_2_28-x64","2014-x64"] cuda-version: ["116","121","124"] steps: - uses: actions/checkout@v4 - name: 'Fetch build script' run: | IPP_DOWNLOAD_GIT_TAG=${{ env.itk-python-package-tag }} IPP_DOWNLOAD_ORG=${{ env.itk-python-package-org }} curl -L https://raw.githubusercontent.com/${IPP_DOWNLOAD_ORG:=InsightSoftwareConsortium}/ITKPythonPackage/${IPP_DOWNLOAD_GIT_TAG:=master}/scripts/dockcross-manylinux-download-cache-and-build-module-wheels.sh -O chmod u+x dockcross-manylinux-download-cache-and-build-module-wheels.sh - name: 'Build 🐍 Python 📦 package' shell: bash run: | rm -rf dist export ITK_PACKAGE_VERSION=${{ env.itk-wheel-tag }} export ITKPYTHONPACKAGE_TAG=${{ env.itk-python-package-tag }} export ITKPYTHONPACKAGE_ORG=${{ env.itk-python-package-org }} export ITK_MODULE_PREQ=${{ env.itk-module-deps }} export CUDA_VERSION=${{ matrix.cuda-version }} export CUDA_VERSION_MAJOR=`(echo ${CUDA_VERSION} | cut -b1-2)` export CUDA_VERSION_MINOR=`(echo ${CUDA_VERSION} | cut -b3-)` CMAKE_OPTIONS="-DRTK_CUDA_VERSION=${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}" CMAKE_OPTIONS="${CMAKE_OPTIONS} -DRTK_BUILD_APPLICATIONS:BOOL=OFF" CMAKE_OPTIONS="${CMAKE_OPTIONS} -DCUDAToolkit_ROOT=/usr/lib64/cuda${CUDA_VERSION}" CMAKE_OPTIONS="${CMAKE_OPTIONS} -DCMAKE_CUDA_COMPILER=/usr/lib64/cuda${CUDA_VERSION}/bin/nvcc" CMAKE_OPTIONS="${CMAKE_OPTIONS} --config-setting=build.tool-args=-j16" # The first two are not library paths but are included to be mounted in the # docker by dockcross-manylinux-build-module-wheels.sh export LD_LIBRARY_PATH="/home/srit/Downloads/cuda${CUDA_VERSION}" export LD_LIBRARY_PATH="/usr/lib64/libcuda.so.1:$LD_LIBRARY_PATH" export LD_LIBRARY_PATH="/home/srit/Downloads/cuda${CUDA_VERSION}/targets/x86_64-linux/lib:$LD_LIBRARY_PATH" if test -e ../../ITKPythonBuilds-linux-manylinux2014.tar.zst ; then mv ../../*zst . fi MANYLINUX_PLATFORM=${{ matrix.manylinux-platform }} if test ${MANYLINUX_PLATFORM} == "_2_28-x64" && test ${CUDA_VERSION} -lt 120; then export IMAGE_TAG=20230106-1aeaea0 CMAKE_OPTIONS="${CMAKE_OPTIONS} -DITK_USE_PYTHON_LIMITED_API=FALSE" fi CMAKE_OPTIONS=(--cmake_options "${CMAKE_OPTIONS}") echo "Manylinux platform ${MANYLINUX_PLATFORM}" rm -rf ITKPythonPackage export MANYLINUX_VERSION=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 1)` export TARGET_ARCH=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 2)` export ITK_MODULE_NO_CLEANUP=TRUE echo "Building for manylinux specialization ${MANYLINUX_VERSION} and target architecture ${TARGET_ARCH}" LIBCUDART=`basename /home/srit/Downloads/cuda${CUDA_VERSION}/targets/x86_64-linux/lib/libcudart.so.${CUDA_VERSION_MAJOR}*` LIBCUBLAS=`basename /home/srit/Downloads/cuda${CUDA_VERSION}/targets/x86_64-linux/lib/libcublas.so.${CUDA_VERSION_MAJOR}*` LIBCUBLASLT=`basename /home/srit/Downloads/cuda${CUDA_VERSION}/targets/x86_64-linux/lib/libcublasLt.so.${CUDA_VERSION_MAJOR}*` LIBCUFFT=`basename /home/srit/Downloads/cuda${CUDA_VERSION}/targets/x86_64-linux/lib/libcufft.so.$((${CUDA_VERSION_MAJOR}-1))*` sed -i "s/dependencies = \[/dependencies = [ \"itk-cudacommon-cuda${CUDA_VERSION} == 1.1.*\",/g" pyproject.toml sed -i "s/itk-rtk/itk-rtk-cuda${CUDA_VERSION}/g" pyproject.toml ./dockcross-manylinux-download-cache-and-build-module-wheels.sh "${CMAKE_OPTIONS[@]}" -x "libcuda.so.1;${LIBCUDART};${LIBCUBLAS};${LIBCUBLASLT};${LIBCUFFT}" cp3${{ matrix.python3-minor-version }} mv *zst ../.. - name: Validate build output shell: bash run: | python -m pip install twine ls dist/ MANYLINUX_PLATFORM=${{ matrix.manylinux-platform }} MANYLINUX_VERSION=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 1)` TARGET_ARCH_NAME=`(echo ${MANYLINUX_PLATFORM} | cut -d '-' -f 2)` if [[ ${TARGET_ARCH_NAME} == "x64" ]]; then TARGET_ARCH_NAME="x86_64" # Match auditwheel naming convention fi WHEEL_PATTERN="dist/itk_*cp3${{ matrix.python3-minor-version }}*manylinux${MANYLINUX_VERSION}*${TARGET_ARCH_NAME}.whl" echo "Searching for wheels matching pattern ${WHEEL_PATTERN}" python -m twine check ${WHEEL_PATTERN} - name: Publish Python package as GitHub Artifact uses: actions/upload-artifact@v4 with: name: LinuxWheel3${{ matrix.python3-minor-version }}${{ matrix.manylinux-platform }}-cuda${{ matrix.cuda-version }} path: dist/*.whl build-windows-cuda-python-packages: runs-on: self-hosted-windows strategy: max-parallel: 2 matrix: python3-minor-version: ${{ github.event_name == 'pull_request' && fromJSON('["11"]') || fromJSON('["9","10","11"]') }} cuda-version: ["124"] steps: - uses: actions/checkout@v4 with: path: "im" - name: 'Reduce source path length' shell: bash run: | # Move ITKPythonBuilds archive to the checked-out source if test -f ../../im/ITKPythonBuilds-windows.zip; then mv ../../im/*.zip im fi rm -fr ../../im # Move checked-out source to a shorter path to avoid Windows path length issues mv im ../../ - name: 'Fetch build script' shell: pwsh run: | cd ../../im $ITKPYTHONPACKAGE_TAG = "${{ env.itk-python-package-tag }}" $ITKPYTHONPACKAGE_ORG = "${{ env.itk-python-package-org }}" $SCRIPT_UPSTREAM = "https://raw.githubusercontent.com/$ITKPYTHONPACKAGE_ORG/ITKPythonPackage/$ITKPYTHONPACKAGE_TAG/scripts/windows-download-cache-and-build-module-wheels.ps1" echo "Fetching $SCRIPT_UPSTREAM" (new-object net.webclient).DownloadString($SCRIPT_UPSTREAM) > windows-download-cache-and-build-module-wheels.ps1 - name: 'Build 🐍 Python 📦 package' shell: pwsh run: | if (Test-Path dist) { rm dist -r -fo } cd ../../im & "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -SkipAutomaticLocation $env:CC="cl.exe" $env:CXX="cl.exe" $env:ITK_PACKAGE_VERSION = "${{ env.itk-wheel-tag }}" $env:ITKPYTHONPACKAGE_TAG = "${{ env.itk-python-package-tag }}" $env:ITKPYTHONPACKAGE_ORG = "${{ env.itk-python-package-org }}" $env:ITK_MODULE_PREQ = "${{ env.itk-module-deps }}" $CUDA_VERSION = "${{ matrix.cuda-version }}" $CUDA_VERSION_MAJOR=$CUDA_VERSION.substring(0,2) $CUDA_VERSION_MINOR=$CUDA_VERSION.substring(2,$CUDA_VERSION.Length-2) $env:CUDA_PATH = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}" $env:CUDACXX = "$env:CUDA_PATH\bin\nvcc.exe" $env:Path = "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}\bin;" + $env:Path $LIBCUDART= (Get-Item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}\bin\cudart64*dll" ).Name $LIBCUBLAS= (Get-Item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}\bin\cublas64*dll" ).Name $LIBCUBLASLT= (Get-Item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}\bin\cublasLt64*dll" ).Name $LIBCUFFT= (Get-Item "C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}\bin\cufft64*dll" ).Name ((Get-Content -Path pyproject.toml) -replace "itk-rtk","itk-rtk-cuda${CUDA_VERSION}") | Set-Content -Path pyproject.toml ((Get-Content -Path pyproject.toml) -replace "dependencies = \[","dependencies = [""itk-cudacommon-cuda${CUDA_VERSION} == 1.1.*"",") | Set-Content -Path pyproject.toml ./windows-download-cache-and-build-module-wheels.ps1 "${{ matrix.python3-minor-version }}" -setup_options "--lib-paths ""C:/Program\ Files/NVIDIA\ GPU\ Computing\ Toolkit/CUDA/v${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}/bin"" --exclude-libs ""nvcuda.dll;concrt140.dll;${LIBCUDART};${LIBCUBLAS};${LIBCUBLASLT};${LIBCUFFT}""" -cmake_options """-DRTK_CUDA_VERSION=${CUDA_VERSION_MAJOR}.${CUDA_VERSION_MINOR}"" ""-DCMAKE_CUDA_COMPILER=$env:CUDACXX"" ""-DRTK_BUILD_APPLICATIONS:BOOL=OFF""" mkdir -p '${{ github.workspace }}\dist' cp 'dist\*.whl' '${{ github.workspace }}\dist' - name: Validate build output shell: pwsh run: | python -m pip install twine ls dist/ $WHEEL_PATTERN = "dist/itk_*cp3${{ matrix.python3-minor-version }}*win*.whl" echo "Searching for wheels matching pattern ${WHEEL_PATTERN}" python -m twine check ${WHEEL_PATTERN} - name: Publish Python package as GitHub Artifact uses: actions/upload-artifact@v4 with: name: WindowsWheel3${{ matrix.python3-minor-version }}-cuda${{ matrix.cuda-version }} path: dist/*.whl publish-python-packages-to-pypi: needs: - build-linux-cuda-python-packages - build-windows-cuda-python-packages runs-on: ubuntu-22.04 steps: - name: Download Python Packages uses: actions/download-artifact@v4 - name: Prepare packages for upload run: | ls -R for d in */; do mv ${d}/*.whl . done mkdir dist mv *.whl dist/ ls dist - name: Publish 🐍 Python 📦 package to PyPI if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') uses: pypa/gh-action-pypi-publish@v1.5.1 with: skip_existing: true user: __token__ password: ${{ secrets.pypi_password }} test-python-wheel: needs: - build-linux-cuda-python-packages runs-on: self-hosted-linux steps: - uses: actions/checkout@v4 - name: Download all wheel artifacts uses: actions/download-artifact@v4 with: path: wheels - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: '3.11' - name: Test python wheel run: | # Find the CUDA 124 wheel for Python 3.11 dynamically wheel=$(find wheels -name "*cuda124*cp311*manylinux_2_28_x86_64.whl" -type f | head -1) pip uninstall -y $(pip list | grep -E '^itk-') echo "Installing wheel: $wheel" pip install $wheel pip install pytest matplotlib pytest $GITHUB_WORKSPACE/test/*.py -vv -W "ignore:builtin type swig" ================================================ FILE: .github/workflows/build-test-package.yml ================================================ name: Build, test, package on: [push,pull_request] concurrency: group: '${{ github.workflow }}@${{ github.head_ref || github.run_id }}' cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: cxx-build-workflow: uses: InsightSoftwareConsortium/ITKRemoteModuleBuildTestPackageAction/.github/workflows/build-test-cxx.yml@v5.4.3 python-build-workflow: uses: InsightSoftwareConsortium/ITKRemoteModuleBuildTestPackageAction/.github/workflows/build-test-package-python.yml@v5.4.3 with: cmake-options: '-DRTK_BUILD_APPLICATIONS:BOOL=OFF' python3-minor-versions: ${{ github.event_name == 'pull_request' && '["11"]' || '["9","10","11"]' }} itk-python-package-tag: 'release' secrets: pypi_password: ${{ secrets.pypi_password }} test-python-wheel: needs: - python-build-workflow runs-on: self-hosted-linux steps: - uses: actions/checkout@v4 - name: Download all wheel artifacts uses: actions/download-artifact@v4 with: path: wheels - name: Set up Python 3.11 uses: actions/setup-python@v4 with: python-version: '3.11' - name: Test python wheel run: | # Find wheel for Python 3.11 dynamically wheel=$(find wheels -name "*cp311*manylinux_2_28_x86_64.whl" -type f | head -1) pip uninstall -y $(pip list | grep -E '^itk-') echo "Installing wheel: $wheel" pip install $wheel pip install pytest matplotlib pytest $GITHUB_WORKSPACE/test/*.py -vv -W "ignore:builtin type swig" ================================================ FILE: .github/workflows/clang-format-linter.yml ================================================ name: clang-format linter on: [push,pull_request] jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: InsightSoftwareConsortium/ITKClangFormatLinterAction@master with: itk-branch: main ================================================ FILE: .gitignore ================================================ # Do not add ExternalData module staging files .ExternalData* # back-up files *~ *.bak # vim swp files *.swp ## Ignore files that are used for auto_completion with clang *.clang_complete ## YouCompleteMe vim plugin configuration file .ycm_extra_conf.py # KWStyle hook output *.kws # compiled python files *.pyc # qtcreator CMakeLists.txt.user* # kdevelop *.kdev* .kdev* # back-up files when conflicts occur *.orig # Clion editor internal project information .idea # Mac System File .DS_Store ================================================ FILE: .readthedocs.yml ================================================ # .readthedocs.yml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 build: os: ubuntu-22.04 tools: python: "3.11" apt_packages: - fonts-roboto - graphviz sphinx: configuration: conf.py python: install: - requirements: documentation/docs/requirements.txt ================================================ FILE: CMakeLists.txt ================================================ #========================================================= # RTK : Reconstruction Toolkit #========================================================= # Respect the CMAKE_CXX_STANDARD flags when building for # ITKv5 or C++11. cmake_minimum_required(VERSION 3.16.3 FATAL_ERROR) ## Only policies introduced after the cmake_minimum_required ## version need to explicitly be set to NEW. ## Refer to https://cmake.org/cmake/help/v3.11/manual/cmake-policies.7.html set(CMAKE_POLICIES CMP0135) foreach(p ${CMAKE_POLICIES}) if(POLICY ${p}) cmake_policy(SET ${p} NEW) endif() endforeach() #========================================================= # Help function to debug CMake macro (DD in) message(${in}=[${${in}}]) endmacro() #========================================================= project(RTK) ## RTK Version set(RTK_VERSION_MAJOR "2") set(RTK_VERSION_MINOR "7") set(RTK_VERSION_PATCH "0") set(RTK_VERSION_STRING "${RTK_VERSION_MAJOR}.${RTK_VERSION_MINOR}") set(RTK_LIBRARIES RTK) #========================================================= # Installation variables #========================================================= if(NOT CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR lib) endif() if(NOT RTK_INSTALL_RUNTIME_DIR) set(RTK_INSTALL_RUNTIME_DIR bin) endif() if(NOT RTK_INSTALL_LIB_DIR) set(RTK_INSTALL_LIB_DIR ${CMAKE_INSTALL_LIBDIR}) endif() if(NOT RTK_INSTALL_ARCHIVE_DIR) set(RTK_INSTALL_ARCHIVE_DIR ${CMAKE_INSTALL_LIBDIR}) endif() if(NOT RTK_INSTALL_INCLUDE_DIR) set(RTK_INSTALL_INCLUDE_DIR include/RTK) endif() if(NOT RTK_INSTALL_PACKAGE_DIR) set(RTK_INSTALL_PACKAGE_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/RTK") endif() #Set position independent code for Unix (-fPIC) set(CMAKE_POSITION_INDEPENDENT_CODE ON) #========================================================= # Remove some MS Visual c++ flags if(MSVC) add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -D_SCL_SECURE_NO_WARNINGS) endif() #========================================================= # Remove some Intel compiler warnings if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") string(REPLACE "-Wno-unused-parameter" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") if(WIN32) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Qwd1268") else() set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd1268") endif() endif() include(cmake/rtkCompilerFlags.cmake) # -------------------------------------------------------- # Find ITK (required) if(NOT ITK_SOURCE_DIR) find_package(ITK 5.1 REQUIRED) endif() # -------------------------------------------------------- # Shared libraries option if(NOT ITK_SOURCE_DIR) set(RTK_BUILD_SHARED_LIBS ${ITK_BUILD_SHARED}) else() set(RTK_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) endif() # ---------------------------------------------------------------------------- # Set RTK_DATA_ROOT # Internally used by XRadRawToAttenuationImageFilter to set the path for its # flat and dark headers. if(NOT ITK_SOURCE_DIR) set(RTK_DATA_ROOT ${CMAKE_BINARY_DIR}/ExternalData/test CACHE PATH "Path of the data root" FORCE) else() file(RELATIVE_PATH RTK_RELATIVE_PATH ${ITK_SOURCE_DIR} ${RTK_SOURCE_DIR}) set(RTK_DATA_ROOT ${ExternalData_BINARY_ROOT}/${RTK_RELATIVE_PATH}/test CACHE PATH "Path of the data root" FORCE) endif() mark_as_advanced(RTK_DATA_ROOT) # -------------------------------------------------------- # Initialization if(NOT ITK_SOURCE_DIR) include(itk-module-init.cmake) if(RTK_BUILD_SHARED_LIBS) set(ITK_LIBRARY_BUILD_TYPE "SHARED") else() set(ITK_LIBRARY_BUILD_TYPE "STATIC") endif() endif() #========================================================= # If choose to build documentation, then search for Doxygen/Sphinx executables. option(RTK_BUILD_DOXYGEN "Build Doxygen Documentation" OFF) if(RTK_BUILD_DOXYGEN) add_subdirectory(documentation/Doxygen) endif() option(RTK_BUILD_SPHINX "Build Sphinx Documentation" OFF) if(RTK_BUILD_SPHINX) add_subdirectory(documentation/docs) endif() # Setup build locations. if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${RTK_BINARY_DIR}/bin) endif() if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${RTK_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) endif() if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY) # Python builds CI workflows expects archives to be placed in the ITK lib # directory. This makes sure that other remote modules that depends on RTK # can link against the RTK libraries. if (WRAP_ITK_INSTALL_COMPONENT_IDENTIFIER STREQUAL "PythonWheel") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ITK_DIR}/${CMAKE_INSTALL_LIBDIR}) else() set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${RTK_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) endif() endif() #========================================================= # lp_solve library #========================================================= add_subdirectory(utilities/lp_solve) set(LPSOLVE_INCLUDE_DIRS ${PROJECT_SOURCE_DIR}/utilities/lp_solve ${PROJECT_SOURCE_DIR}/utilities/lp_solve/shared ${PROJECT_SOURCE_DIR}/utilities/lp_solve/bfp ${PROJECT_SOURCE_DIR}/utilities/lp_solve/bfp/bfp_LUSOL ${PROJECT_SOURCE_DIR}/utilities/lp_solve/bfp/bfp_LUSOL/LUSOL ${PROJECT_SOURCE_DIR}/utilities/lp_solve/colamd ) list(APPEND RTK_INCLUDE_DIRS "${LPSOLVE_INCLUDE_DIRS}") #========================================================= # Include directories #========================================================= list(APPEND RTK_INCLUDE_DIRS ${RTK_BINARY_DIR}/include) #========================================================= # Generate RTKConfig.cmake for the build tree. set(RTK_MODULE_PATH_CONFIG ${CMAKE_MODULE_PATH}) # Add option to control the generation of RTK applications option(RTK_BUILD_APPLICATIONS "Build RTK applications" ON) #----------------------------------------------------------------------------- # Common revision info between applications include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) if(GIT_SHA1 MATCHES ".*NOTFOUND") set(RTK_VERSION_HASH "") else() set(RTK_VERSION_HASH ", git hash ${GIT_SHA1}") endif() set(RTK_EXPORT_CODE_BUILD " # The RTK version number set(RTK_VERSION_MAJOR ${RTK_VERSION_MAJOR}) set(RTK_VERSION_MINOR ${RTK_VERSION_MINOR}) set(RTK_VERSION_PATCH ${RTK_VERSION_PATCH}) # Whether the compiled version of RTK uses CUDA set(RTK_USE_CUDA ${RTK_USE_CUDA}) # Whether RTK applications were built set(RTK_BUILD_APPLICATIONS ${RTK_BUILD_APPLICATIONS}) if(RTK_USE_CUDA) find_package(CUDAToolkit) set(RTK_CUDA_PROJECTIONS_SLAB_SIZE \"16\" CACHE STRING \"Number of projections processed simultaneously in CUDA forward and back projections\") endif() ") set(RTK_EXPORT_CODE_INSTALL " # The RTK version number set(RTK_VERSION_MAJOR ${RTK_VERSION_MAJOR}) set(RTK_VERSION_MINOR ${RTK_VERSION_MINOR}) set(RTK_VERSION_PATCH ${RTK_VERSION_PATCH}) # Whether the compiled version of RTK uses CUDA set(RTK_USE_CUDA ${RTK_USE_CUDA}) # Whether RTK applications were built set(RTK_BUILD_APPLICATIONS ${RTK_BUILD_APPLICATIONS}) if(RTK_USE_CUDA) find_package(CUDAToolkit) set(RTK_CUDA_PROJECTIONS_SLAB_SIZE \"16\" CACHE STRING \"Number of projections processed simultaneously in CUDA forward and back projections\") endif() ") #========================================================= # Configure and build ITK external module #========================================================= if(NOT ITK_SOURCE_DIR) if(NOT EXISTS ${ITK_CMAKE_DIR}/ITKModuleMacros.cmake) message(FATAL_ERROR "Modules can only be built against an ITK build tree; they cannot be built against an ITK install tree.") endif() list(APPEND CMAKE_MODULE_PATH ${ITK_CMAKE_DIR}) include(ITKModuleExternal) # Add third party to RTK build targets. # This must be done after RTK has been loaded by ITK to make sure # ${itk-module} variables are defined for RTK. itk_module_target(lpsolve55) if(${ITK_VERSION} VERSION_LESS 5.3) ## Set the default target properties for RTK if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) # Supported values are ``11``, ``14``, and ``17``. endif() if(NOT CMAKE_CXX_STANDARD_REQUIRED) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() if(NOT CMAKE_CXX_EXTENSIONS) set(CMAKE_CXX_EXTENSIONS OFF) endif() ## Default to release if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() endif() else() itk_module_impl() endif() # Propagate cmake options in a header file # Must be done after the external module configuration to make sure CudaCommon_VERSION is defined configure_file(${RTK_SOURCE_DIR}/rtkConfiguration.h.in ${RTK_BINARY_DIR}/include/rtkConfiguration.h) # Install lpsolve headers install(FILES ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_bit.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_crash.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_explicit.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_fortify.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_Hash.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_lib.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_matrix.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_MDO.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_mipbb.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_MPS.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_presolve.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_price.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_pricePSE.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_report.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_rlp.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_scale.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_simplex.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_solveDLL.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_SOS.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_types.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_utils.h ${RTK_SOURCE_DIR}/utilities/lp_solve/lp_wlp.h DESTINATION ${RTK_INSTALL_INCLUDE_DIR}/lpsolve COMPONENT Development) target_include_directories(lpsolve55 PUBLIC $) # Install configuration file install(FILES ${RTK_BINARY_DIR}/include/rtkConfiguration.h DESTINATION ${RTK_INSTALL_INCLUDE_DIR}) install(FILES ${RTK_SOURCE_DIR}/cmake/FindGengetopt.cmake ${RTK_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake ${RTK_SOURCE_DIR}/cmake/GetGitRevisionDescription.cmake.in DESTINATION ${ITK_INSTALL_PACKAGE_DIR}) if(NOT ITK_SOURCE_DIR) install(CODE "MESSAGE(FATAL_ERROR \"Cannot install, RTK is compiled separately from ITK. Installation is only functional if RTK is compiled within ITK.\")") endif() #========================================================= # Build applications #========================================================= option(RTK_PROBE_EACH_FILTER "Probe each RTK filter in a global object and report times and memory used in RTK applications" OFF) if(RTK_BUILD_APPLICATIONS) add_subdirectory(applications) endif() # -------------------------------------------------------- # Setup KWStyle from ITK if(ITK_USE_KWSTYLE) find_package( Git ) if(GIT_FOUND AND EXISTS "${RTK_SOURCE_DIR}/.git/config") execute_process( COMMAND ${GIT_EXECUTABLE} config hooks.KWStyle.path "${KWSTYLE_EXECUTABLE}" WORKING_DIRECTORY ${RTK_SOURCE_DIR} ) endif() endif() # -------------------------------------------------------- # Setup ClangFormat from ITK if(ITK_USE_CLANGFORMAT) find_package( Git ) if(GIT_FOUND AND EXISTS "${RTK_SOURCE_DIR}/.git/config") execute_process(COMMAND ${GIT_EXECUTABLE} config clangFormat.binary "${CLANG_FORMAT_EXECUTABLE}" WORKING_DIRECTORY ${RTK_SOURCE_DIR}) endif() endif() #========================================================= # Install pre-commit hook #========================================================= if(EXISTS "${RTK_SOURCE_DIR}/.git/config" AND NOT EXISTS "${RTK_SOURCE_DIR}/.git/hooks/pre-commit") # Silently ignore the error if the hooks directory is read-only. execute_process( COMMAND ${CMAKE_COMMAND} -E copy ${RTK_SOURCE_DIR}/cmake/Hooks/pre-commit ${RTK_SOURCE_DIR}/.git/hooks/pre-commit OUTPUT_VARIABLE _output ERROR_VARIABLE _output RESULT_VARIABLE _result ) if(_result AND NOT "${_output}" MATCHES "Error copying file") message("${_output}") endif() endif() ================================================ FILE: COPYRIGHT.TXT ================================================ RTK is copyrighted software. Copyright (c) 2010-2012 RTK consortium All rights reserved You can freely use and distribute RTK under an Apache 2.0 license. See LICENSE.TXT for details ------------------------------------------------------------------------ Some code is modified from plastimatch.org: - hnd_io.c in itkHndImageIO.cxx - fdk_cuda.cu in itkCudaFDKBackProjectionImageFilter.cu Plastimatch Software License ("Software License") Version 1.0 This Software License covers downloads from the Plastimatch project ("Plastimatch") maintained by The General Hospital Corporation Inc. ("MGH"). Your downloading, copying, modifying, displaying, distributing or use of any software and/or data from Plastimatch (collectively, the "Software") constitutes acceptance of all of the terms and conditions of this Software License. If you do not agree to such terms and conditions, you have no right to download, copy, modify, display, distribute or use the Software. 1. As used in this Software License, "you" means the individual downloading and/or using, reproducing, modifying, displaying and/or distributing the Software and the institution or entity which employs or is otherwise affiliated with such individual in connection therewith. The MGH hereby grants you, with right to sublicense, with respect to MGH's rights in the software, and data, if any, which is the subject of this Software License (collectively, the "Software"), a royalty-free, non-exclusive license to use, reproduce, make derivative works of, display and distribute the Software, provided that: (a) you accept and adhere to all of the terms and conditions of this Software License; (b) in connection with any copy of or sublicense of all or any portion of the Software, all of the terms and conditions in this Software License shall appear in and shall apply to such copy and such sublicense, including without limitation all source and executable forms and on any user documentation, prefaced with the following words: "All or portions of this licensed product (such portions are the "Software") have been obtained under license from MGH and are subject to the following terms and conditions:" (c) you preserve and maintain all applicable attributions, copyright notices and licenses included in or applicable to the Software; (d) modified versions of the Software must be clearly identified and marked as such, and must not be misrepresented as being the original Software; and (e) you consider making, but are under no obligation to make, the source code of any of your modifications to the Software freely available to others on an open source basis. 2. The license granted under this Software License includes without limitation the right to (i) incorporate the Software into proprietary programs (subject to any restrictions applicable to such programs), (ii) add your own copyright statement to your modifications of the Software, and (iii) provide additional or different license terms and conditions in your sublicenses of modifications of the Software; provided that in each case your use, reproduction or distribution of such modifications otherwise complies with the conditions stated in this Software License. 3. This Software License does not grant any rights with respect to third party software, except those rights that MGH has been authorized by a third party to grant to you, and accordingly you are solely responsible for (i) obtaining any permissions from third parties that you need to use, reproduce, make derivative works of, display and distribute the Software, and (ii) informing your sublicensees, including without limitation your end-users, of their obligations to secure any such required permissions. 4. The Software has been designed for research purposes only and has not been reviewed or approved by the Food and Drug Administration or by any other agency. YOU ACKNOWLEDGE AND AGREE THAT CLINICAL APPLICATIONS ARE NEITHER RECOMMENDED NOR ADVISED. Any commercialization of the Software is at the sole risk of the party or parties engaged in such commercialization. You further agree to use, reproduce, make derivative works of, display and distribute the Software in compliance with all applicable governmental laws, regulations and orders, including without limitation those relating to export and import control. 5. The Software is provided "AS IS" and neither MGH nor any contributor to the software (each a "Contributor") shall have any obligation to provide maintenance, support, updates, enhancements or modifications thereto. MGH AND ALL CONTRIBUTORS SPECIFICALLY DISCLAIM ALL EXPRESS AND IMPLIED WARRANTIES OF ANY KIND INCLUDING, BUT NOT LIMITED TO, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL MGH OR ANY CONTRIBUTOR BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ARISING IN ANY WAY RELATED TO THE SOFTWARE, EVEN IF MGH OR ANY CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. TO THE MAXIMUM EXTENT NOT PROHIBITED BY LAW OR REGULATION, YOU FURTHER ASSUME ALL LIABILITY FOR YOUR USE, REPRODUCTION, MAKING OF DERIVATIVE WORKS, DISPLAY, LICENSE OR DISTRIBUTION OF THE SOFTWARE AND AGREE TO INDEMNIFY AND HOLD HARMLESS MGH AND ALL CONTRIBUTORS FROM AND AGAINST ANY AND ALL CLAIMS, SUITS, ACTIONS, DEMANDS AND JUDGMENTS ARISING THEREFROM. 6. None of the names, logos or trademarks of MGH or any of MGH's affiliates or any of the Contributors, or any funding agency, may be used to endorse or promote products produced in whole or in part by operation of the Software or derived from or based on the Software without specific prior written permission from the applicable party. 7. Any use, reproduction or distribution of the Software which is not in accordance with this Software License shall automatically revoke all rights granted to you under this Software License and render Paragraphs 1 and 2 of this Software License null and void. 8. This Software License does not grant any rights in or to any intellectual property owned by MGH or any Contributor except those rights expressly granted hereunder. ------------------------------------------------------------------------ gengetopt code has been taken from git://git.sv.gnu.org/gengetopt.git tag rel_2_22_4 gengetopt - generate a C function that uses getopt_long to parse command line arguments Copyright (C) 1999, 2011, 2012, 2013-2007, Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Use of gengetopt does not impose any particular license on the generated code. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The included file 'COPYING' is a copy of the GNU General Public License. ------------------------------------------------------------------------ itkBinShrinkImageFilter has been taken from https://hdl.handle.net/10380/3450 It uses the same Apache 2.0 license as RTK ------------------------------------------------------------------------ itkImageScanlineConstIterator has been taken from ITK 4.5.0 It uses the same Apache 2.0 license as RTK ------------------------------------------------------------------------ Some code is modified (a lot) from GIFT, the Generalized Image Fusion Toolkit (https://hdl.handle.net/1926/216) - rtkDaubechiesWaveletsKernelSource.h - rtkDaubechiesWaveletsKernelSource.hxx - rtkDeconstructImageFilter.h - rtkDeconstructImageFilter.hxx - rtkReconstructImageFilter.h - rtkReconstructImageFilter.hxx - rtkDownsampleImageFilter.h - rtkDownsampleImageFilter.hxx - rtkUpsampleImageFilter.h - rtkUpsampleImageFilter.hxx ------------------------------------------------------------------------ Some code is modified from https://sourceforge.net/projects/niftyrec/: - tt_project_ray_gpu_kernels.cu in rtkCudaForwardProjectionImageFilter.h NIFTYREC TOMOGRAPHY TOOLBOX Copyright (c) 2009-2013, University College London, United-Kingdom. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the University College London nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------ utilities/lpsolve has been taken from https://sourceforge.net/projects/lpsolve/files/lpsolve/5.5.2.0/lp_solve_5.5.2.0_source.tar.gz/download and is distributed under the following license GNU lesser general public license. ------------------------------------------------------------------------ The CMakeLists.txt file for lpsolve has been taken from https://github.com/PADrend/ThirdParty. There is no license specified on this repository, https://github.com/PADrend/Rendering is distributed under the Mozilla Public License Version 2.0. ------------------------------------------------------------------------ GetGitRevisionDescription.cmake and GetGitRevisionDescription.cmake.in have been taken from https://github.com/rpavlik/cmake-modules with the following license: Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: CTestConfig.cmake ================================================ set(CTEST_PROJECT_NAME "RTK") set(CTEST_NIGHTLY_START_TIME "1:00:00 UTC") set(CTEST_DROP_METHOD "https") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=RTK") set(CTEST_DROP_SITE_CDASH TRUE) ================================================ FILE: CodeContribution.md ================================================ # Code contribution ## Coding style RTK is based on ITK and aims at following its coding conventions. Any developer should follow these conventions when submitting new code or contributions to the existing one. We strongly recommend you to read thoroughly [ITK's style guide](http://www.itk.org/Wiki/ITK/Coding_Style_Guide). ## Testing This section describes how to add/edit datasets for testing purposes for RTK. Datasets are not stored in the GIT repository for efficiency and also to avoid having large history due to binary files. Instead the files are stored on a [Girder](http://data.kitware.com) instance. Here's the recipe to add new datasets: 1. Register/Login to Girder hosted at Kitware: [http://data.kitware.com](http://data.kitware.com) 2. Locate the RTK collection: [https://data.kitware.com/#collection/5a7706878d777f0649e04776](https://data.kitware.com/#collection/5a7706878d777f0649e04776) 3. Upload the new datasets in the appropriate folder. If you don't have the necessary privileges please email the mailing list 4. In the GIT repository, add in testing/Data a file with the exact filename of the original file **but with the .md5 extension**. Inside that file put the md5sum of the file on Girder. 5. When adding a test use the new macro 'RTK_ADD_TEST' instead of 'ADD_TEST' and specify the datasets you want CTest to download by appending the data to 'DATA{}'. For example: ``` RTK_ADD_TEST(NAME rtkimagxtest COMMAND ${EXECUTABLE\_OUTPUT\_PATH}/rtkimagxtest DATA{Data/Input/ImagX/raw.xml,raw.raw} DATA{Data/Baseline/ImagX/attenuation.mha}) ``` ## Dashboard * The RTK dashboard is available at [RTK Dashboard](http://my.cdash.org/index.php?project=RTK) ================================================ FILE: GettingStarted.md ================================================ Getting started =============== Here are suggested steps for the RTK beginner. 1. Install the software following [INSTALLATION.md](INSTALLATION.md). 2. If you are not already familiar with ITK, [get started with ITK](https://github.com/InsightSoftwareConsortium/ITK/blob/master/GettingStarted.md). 3. Check out the [First reconstruction](examples/FirstReconstruction/README.md) example (both the [CMake](https://github.com/RTKConsortium/RTK/blob/master/examples/FirstReconstruction/CMakeLists.txt) and the [C++](https://github.com/RTKConsortium/RTK/blob/master/examples/FirstReconstruction/FirstReconstruction.cxx) codes). Many other examples are in the [documentation](https://docs.openrtk.org/). 4. Ask questions to the [user mailing list](https://www.creatis.insa-lyon.fr/mailman/listinfo/rtk-users). ================================================ FILE: HEADER.TXT ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ ================================================ FILE: INSTALLATION.md ================================================ RTK installation ================ Configuration, compilation and installation with ITK ---------------------------------------------------- RTK is a module of [ITK](https://www.itk.org), the Insight Toolkit. Follow the instructions of the [ITK software guide](https://itk.org/ITKSoftwareGuide/html) ([chapter 2](https://itk.org/ITKSoftwareGuide/html/Book1/ITKSoftwareGuide-Book1ch2.html) mainly) for configuring and compiling ITK. The following CMake options are RTK specific: * `Module_RTK`: Activates RTK download and compilation. Default is `OFF`. Turn it `ON` to activate RTK or compile RTK independently (see below). * `Module_RTK_GIT_TAG`: Git tag for the RTK download. By default, the RTK version which is downloaded and compiled is the one given in the [RTK.remote.cmake](https://github.com/InsightSoftwareConsortium/ITK/blob/main/Modules/Remote/RTK.remote.cmake). Change this option to build another version. For example, you can change it to `main` to build the latest RTK version. RTK is only maintained to be backward compatible with the latest ITK release and ITK main branch. * `RTK_BUILD_APPLICATIONS`: Activates the compilation of RTK's command line tools. Although RTK is mainly a toolkit, we also provide several command line tools for doing most of the available processing. These command line tools use [gengetopt](https://www.gnu.org/software/gengetopt/gengetopt.html). Several examples are available in the [documentation](http://docs.openrtk.org). * `RTK_USE_CUDA`: Activates CUDA computation. Default is `ON` if CMake has automatically found the CUDA package and a CUDA-compatible GPU, and `OFF` otherwise. * `RTK_CUDA_VERSION`: Specifies an exact version of the CUDA toolkit which must be used. If unspecified, RTK only checks if the found version is recent enough. * `RTK_CUDA_PROJECTIONS_SLAB_SIZE`: Set the number of projections processed at once in CUDA processing. Default is 16. * `RTK_PROBE_EACH_FILTER`: Activates the timing, CPU and CUDA memory consumption of each filter. Defaults is `OFF`. When activated, each filter processing is probed and a summary can be displayed. All command line applications display the result with `--verbose`. RTK will automatically be installed when installing ITK. Independent configuration and compilation ----------------------------------------- For RTK developpers, it may be useful to compile RTK independently from ITK. This is possible, simply: * Compile ITK with `Module_RTK=OFF`. * If you want to use CUDA, also activate `Module_CudaCommon` or compile it separately as RTK in the following two bullet points (cloning its [GitHub repository](https://github.com/RTKConsortium/ITKCudaCommon) or downloading it as a [zip package](https://codeload.github.com/RTKConsortium/ITKCudaCommon/zip/main)). * Manually download RTK's source repository from [GitHub](https://github.com/RTKConsortium/RTK) with `git` (recommended) or as a [zip package](https://codeload.github.com/RTKConsortium/RTK/zip/main). * Configure the project with CMake pointing to RTK's source directory and setting the CMake option `ITK_DIR` to ITK's compilation directory. All CMake options above can be set except `Module_RTK`. Installation is currently not supported for independent RTK compilations. Pre-compiled binaries --------------------- We only provide pre-compiled binaries for the Python package which depends on ITK. Use the following commands to install the RTK module with `pip`. ``` python -m pip install --upgrade pip python -m pip install itk-rtk ``` The same operating systems and Python versions are supported as ITK's packages, see the list on [Pypi](https://pypi.org/project/itk-rtk). We also provide pre-compiled [CUDA](https://developer.nvidia.com/cuda-toolkit) packages for Windows and Linux. They require an installed version of CUDA compatible with the package. Currently, RTK is available for CUDA 12.4 on Windows and Linux via: ``` python -m pip install itk-rtk-cuda124 ``` ``` python -m pip install itk-rtk-cuda124 ``` Getting started --------------- See [GettingStarted.md](GettingStarted.md). Your `CMakeLists.txt` can now use RTK when importing ITK as shown in the [FirstReconstruction's CMakeLists.txt](https://github.com/RTKConsortium/RTK/blob/main/examples/FirstReconstruction/CMakeLists.txt#L7). ================================================ FILE: ITKKWStyleFiles.txt.in ================================================ 200 0,1,2 [A-Z] m_[A-Z] 0 1 1 3 /**, *, */,true rtk [NameOfClass],itk [NameOfClass]_[Extension] 2 1,1
@RTK_SOURCE_DIR@/cmake/KWStyle/RTKHeader.h,false,true
================================================ FILE: LICENSE.TXT ================================================ Apache License Version 2.0, January 2004 https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [2010-2012] [RTK Consortium] The RTK consortium comprises: - CREATIS CNRS laboratory https://www.creatis.insa-lyon.fr/ - Massachusetts General Hospital https://www.mgh.harvard.edu/ - Université Catholique de Louvain https://www.uclouvain.be/ - IBA https://www.iba-protontherapy.com/ - Kitware https://www.kitware.com/ - medPhoton https://www.medphoton.at/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: ORGANIZATION.TXT ================================================ The software system known as the Reconstruction Toolkit, hereafter referred to as RTK, is an open-source platform for tomographic reconstruction. RTK consists of source code, data, tests, examples and documentation. Members of the RTK collaboration are bound by a gentleman's agreement. In this agreement, all partners agree to work together to the development, validation and support of RTK. A member can either be a person or an institute. Each RTK member provides an on-going contribution to the development, validation and support of RTK as approved by the Steering Committee of the collaboration. Examples of contributions are the design of the software architecture, the development of new features, the writing of the documentation, and so on. Each RTK member also agrees upon a common review process of any presentation and publication related to the development of new RTK features. Any RTK presentation and publication must circulate through the mailing list of the steering committee for approval. Every member should identify one technical contact person the technical coordinator of the RTK collaboration can address specific requests to. An elected technical coordinator organizes technical meetings, and attends the meetings of the Steering Committee. The technical coordinator is in charge of the smoothness of the collaborative developments, i.e., good communication between contributors to ensure that RTK is consistent. Every member must also be represented by one representative in the Steering Committee of the collaboration at least once a year. This Steering Committee decides the development strategy of RTK. The elected spokesman of the collaboration chairs this Steering Committee. The elected spokesperson and the elected technical coordinator will be confirmed in their functions or elected every other year by a simple majority of votes cast by the members of the Steering Committee. The RTK collaboration is open to new memberships. If you wish to join the collaboration, please contact Simon RIT, CREATIS CNRS UMR 5220 - INSERM U1044 - Universit Lyon 1 - INSA Lyon, France. Membership is confirmed or granted by the Steering Committe every other year by a simple majority of votes cast by the members of the Steering Committee. ================================================ FILE: README.md ================================================ RTK: The Reconstruction ToolKit =============================== [![GitHub release](https://img.shields.io/github/release/RTKConsortium/RTK.svg)](https://github.com/RTKConsortium/RTK/releases/latest) [![PyPI](https://img.shields.io/pypi/v/itk-rtk.svg)](https://pypi.python.org/pypi/itk-rtk) [![Wheels](https://img.shields.io/pypi/wheel/itk-rtk.svg)](https://pypi.org/project/itk-rtk) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/RTKConsortium/RTK/blob/master/LICENSE.TXT) [![][gha-img]][gha-link] [gha-img]: https://github.com/RTKConsortium/RTK/actions/workflows/build-test-package.yml/badge.svg [gha-link]: https://github.com/RTKConsortium/RTK/actions/workflows/build-test-package.yml Links ----- * [Homepage](https://www.openrtk.org) * [Download](https://www.openrtk.org/RTK/resources/software.html) * [Mailing List](https://www.creatis.insa-lyon.fr/mailman/listinfo/rtk-users) * [Getting Started](GettingStarted.md) * [Help](https://docs.openrtk.org) * [Issue tracking](https://github.com/RTKConsortium/RTK/issues) Copyright RTK Consortium ------------------------ Licensed under the Apache License, Version 2.0 (the "[License](https://www.apache.org/licenses/LICENSE-2.0.txt)"); you may not use this file except in compliance with the [License](https://www.apache.org/licenses/LICENSE-2.0.txt). Unless required by applicable law or agreed to in writing, software distributed under the [License](https://www.apache.org/licenses/LICENSE-2.0.txt) is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the [License](https://www.apache.org/licenses/LICENSE-2.0.txt) for the specific language governing permissions and limitations under the [License](https://www.apache.org/licenses/LICENSE-2.0.txt). ================================================ FILE: applications/CMakeLists.txt ================================================ find_package(Gengetopt) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/rtkVersion.ggo.in ${RTK_BINARY_DIR}/rtkVersion.ggo) #----------------------------------------------------------------------------- # Setup RPATH : # # add the automatically determined parts of the RPATH # which point to directories outside the build tree to the install RPATH set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) # add path to RTK libraries to the install path set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${RTK_INSTALL_LIB_DIR}") #----------------------------------------------------------------------------- # Find ITK. # Required to include ITK_USE_FILE in order to Register IO factories # Force requested modules to be RTK dependencies only, otherwise all # available factories will try to register themselves. if (NOT ITK_DIR) set(ITK_DIR ${ITK_BINARY_DIR}/CMakeTmp) endif() find_package(ITK REQUIRED COMPONENTS ${ITK_MODULE_RTK_DEPENDS}) include(${ITK_USE_FILE}) #----------------------------------------------------------------------------- # Executables add_subdirectory(rtkamsterdamshroud) add_subdirectory(rtkbackprojections) add_subdirectory(rtkfdk) add_subdirectory(rtkfieldofview) add_subdirectory(rtkforwardprojections) add_subdirectory(rtkfourdfdk) add_subdirectory(rtkfourdsart) add_subdirectory(rtkiterativefdk) add_subdirectory(rtkosem) add_subdirectory(rtkprojectgeometricphantom) add_subdirectory(rtkprojections) add_subdirectory(rtkprojectshepploganphantom) add_subdirectory(rtksart) add_subdirectory(rtksubselect) add_subdirectory(rtkdrawgeometricphantom) add_subdirectory(rtkdrawshepploganphantom) add_subdirectory(rtkextractshroudsignal) add_subdirectory(rtkextractphasesignal) add_subdirectory(rtkoverlayphaseandshroud) add_subdirectory(rtktotalvariationdenoising) add_subdirectory(rtktotalnuclearvariationdenoising) add_subdirectory(rtkadmmwavelets) add_subdirectory(rtkadmmtotalvariation) add_subdirectory(rtkfourdrooster) add_subdirectory(rtkmcrooster) add_subdirectory(rtkfourdconjugategradient) add_subdirectory(rtkspectralonestep) add_subdirectory(rtkconjugategradient) add_subdirectory(rtkregularizedconjugategradient) add_subdirectory(rtkwaveletsdenoising) add_subdirectory(rtki0estimation) add_subdirectory(rtktutorialapplication) add_subdirectory(rtkscatterglarecorrection) add_subdirectory(rtkmotioncompensatedfourdconjugategradient) add_subdirectory(rtklagcorrection) add_subdirectory(rtkgaincorrection) add_subdirectory(rtkspectralsimplexdecomposition) add_subdirectory(rtkdualenergysimplexdecomposition) add_subdirectory(rtkspectralrooster) add_subdirectory(rtkspectralforwardmodel) add_subdirectory(rtkmaskcollimation) add_subdirectory(rtkspectraldenoiseprojections) add_subdirectory(rtkprojectionmatrix) add_subdirectory(rtkvectorconjugategradient) add_subdirectory(rtkdualenergyforwardmodel) add_subdirectory(rtkcheckimagequality) #All the executables below are meant to create RTK ThreeDCircularProjectionGeometry files add_subdirectory(rtkvarianobigeometry) add_subdirectory(rtkvarianprobeamgeometry) add_subdirectory(rtksimulatedgeometry) add_subdirectory(rtkelektasynergygeometry) add_subdirectory(rtkdigisensgeometry) add_subdirectory(rtkxradgeometry) add_subdirectory(rtkimagxgeometry) add_subdirectory(rtkorageometry) add_subdirectory(rtkbioscangeometry) #========================================================= #----------------------------------------------------------------------------- # Testing of the applications if(BUILD_TESTING) # Reference data (geometry, reference Shepp Logan and corresponding projections) add_test(rtkappsimulatedgeometrytest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtksimulatedgeometry -n 45 --sid 1000 --sdd 1500 -o geo) add_test(rtkappdrawshepploganphantomtest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkdrawshepploganphantom -o reference.mha --size 21 --phantomscale 10) add_test(rtkappprojectshepploganphantomtest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkprojectshepploganphantom -o sheppy.mha -g geo --phantomscale 10 --size 32) set_tests_properties(rtkappprojectshepploganphantomtest PROPERTIES DEPENDS rtkappsimulatedgeometrytest) # FDK test if(RTK_USE_CUDA) add_test(rtkappfdktest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkfdk -g geo -p . -r sheppy.mha -o fdk.mha --hardware cuda --size 21) else() add_test(rtkappfdktest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkfdk -g geo -p . -r sheppy.mha -o fdk.mha --size 21) endif() set_tests_properties(rtkappfdktest PROPERTIES DEPENDS rtkappprojectshepploganphantomtest) add_test(rtkappfdkchecktest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkcheckimagequality -i reference.mha -j fdk.mha -t 400) set_tests_properties(rtkappfdkchecktest PROPERTIES DEPENDS "rtkappdrawshepploganphantomtest;rtkappfdktest") # Iteration reporting testing add_test(rtkapposemtest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkosem -g geo -p . -r sheppy.mha -o osem.mha -n 3 --size 21 --output-every 1 --nprojpersubset 15 --iteration-file-name osem%d.mha) set_tests_properties(rtkapposemtest PROPERTIES DEPENDS rtkappprojectshepploganphantomtest) add_test(rtkapposemchecktest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/rtkcheckimagequality -i reference.mha -j osem1.mha,osem2.mha,osem3.mha -t 800,550,455) set_tests_properties(rtkapposemchecktest PROPERTIES DEPENDS "rtkapposemtest;rtkappdrawshepploganphantomtest") endif() ================================================ FILE: applications/README.md ================================================ # Command-line applications RTK provides command line applications which can be built from the C++ code by turning `ON` the `RTK_BUILD_APPLICATIONS` CMake option. A few of these applications have also been translated to Python and integrated in the [Pypi package](https://pypi.org/project/itk-rtk/). The options of each command line application can be listed with the `--help option`. The following are examples using RTK applications: ```{toctree} :maxdepth: 1 ./rtkfdk/README.md ./rtkconjugategradient/README.md ./rtkforwardprojections/README.md ./rtkdrawgeometricphantom/README.md ./rtkamsterdamshroud/README.md ./rtkelektasynergygeometry/README.md ./rtkvarianobigeometry/README.md ./rtkadmmtotalvariation/README.md ./rtkadmmwavelets/README.md ./rtkfourdrooster/README.md ./rtkshowgeometry/README.md ``` In [applications/rtktutorialapplication/](https://github.com/RTKConsortium/RTK/blob/main/applications/rtktutorialapplication), you will find a very basic RTK application that can be used as a starting point for building your own new application. ## Using RTK applications in Python RTK applications which have been translated to Python can also be called from Python in addition to the command line, thus avoiding reloading the RTK package (which is slow) and enabling the flexibility of Python scripting. The Python interface dynamically maps the RTK applications to Python functions. You can use `help(rtk.)` to see all required and optional arguments. Note that only file paths are supported as input, ITK images or RTK geometry objects cannot be passed directly. Each Python application accepts either: 1. **A single positional string argument**: This mimics the command-line usage. 2. **A list of keyword arguments**: This provides a more Pythonic interface. ```python rtk.() ``` For example, ```bash rtkfdk -p . -r projections.mha -o fdk.mha -g geometry.xml --spacing 2 --dimension 256 ``` is equivalent to execute from Python ```python from itk import RTK as rtk rtk.rtkfdk( "-p . -r projections.mha -o fdk.mha -g geometry.xml --spacing 2 --dimension 256" ) ``` or ```python from itk import RTK as rtk rtk.rtkfdk( path=".", regexp="projections.mha", output="fdk.mha", geometry="geometry.xml", spacing="2,2,2", dimension=[256,256,256] # You can use a string or a list ) ``` ================================================ FILE: applications/rtk3Doutputimage_group.py ================================================ import itk from itk import RTK as rtk __all__ = [ "add_rtk3Doutputimage_group", "SetConstantImageSourceFromArgParse", ] # Mimicks rtk3Doutputimage_section.ggo def add_rtk3Doutputimage_group(parser): rtk3Doutputimage_group = parser.add_argument_group("Output 3D image properties") rtk3Doutputimage_group.add_argument( "--origin", help="Origin (default=centered)", type=float, nargs='+', ) rtk3Doutputimage_group.add_argument( "--size", help="Size", type=int, nargs='+', default=[256], ) rtk3Doutputimage_group.add_argument( "--dimension", help="Dimension", type=int, nargs='+', ) rtk3Doutputimage_group.add_argument( "--spacing", help="Spacing", type=float, nargs='+', default=[1] ) rtk3Doutputimage_group.add_argument( "--direction", help="Direction", type=float, nargs='+' ) rtk3Doutputimage_group.add_argument( "--like", help="Copy information from this image (origin, size, spacing, direction)", ) # Mimicks SetConstantImageSourceFromGgo def SetConstantImageSourceFromArgParse(source, args_info): ImageType = type(source.GetOutput()) # Handle deprecated --dimension argument if args_info.dimension is not None: print( "Warning: '--dimension' is deprecated and will be removed in a future release. " "Please use '--size' instead." ) args_info.size = args_info.dimension Dimension = ImageType.GetImageDimension() imageDimension = itk.Size[Dimension]() imageDimension.Fill(args_info.size[0]) for i in range(min(len(args_info.size), Dimension)): imageDimension[i] = args_info.size[i] imageSpacing = itk.Vector[itk.D, Dimension]() imageSpacing.Fill(args_info.spacing[0]) for i in range(min(len(args_info.spacing), Dimension)): imageSpacing[i] = args_info.spacing[i] imageOrigin = itk.Point[itk.D, Dimension]() for i in range(Dimension): imageOrigin[i] = imageSpacing[i] * (imageDimension[i] - 1) * -0.5 if args_info.origin is not None: for i in range(min(len(args_info.origin), Dimension)): imageOrigin[i] = args_info.origin[i] imageDirection = source.GetOutput().GetDirection() if args_info.direction is not None: for i in range(Dimension): for j in range(Dimension): imageDirection[i][j] = args_info.direction[i * Dimension + j] source.SetOrigin(imageOrigin) source.SetSpacing(imageSpacing) source.SetDirection(imageDirection) source.SetSize(imageDimension) source.SetConstant(0.0) # Copy output image information from an existing file, if requested # Overwrites parameters given in command line, if any if args_info.like is not None: LikeReaderType = itk.ImageFileReader[ImageType] likeReader = LikeReaderType.New() likeReader.SetFileName(args_info.like) likeReader.UpdateOutputInformation() source.SetInformationFromImage(likeReader.GetOutput()) source.UpdateOutputInformation() ================================================ FILE: applications/rtk3Doutputimage_section.ggo ================================================ section "Output 3D image properties" option "origin" - "Origin (default=centered)" double multiple no option "dimension" - "Dimension(Deprecated) Use --size instead." int multiple no default="256" option "size" - "Size" int multiple no default="256" option "spacing" - "Spacing" double multiple no default="1" option "direction" - "Direction" double multiple no option "like" - "Copy information from this image (origin, size, spacing, direction)" string no ================================================ FILE: applications/rtk4Doutputimage_section.ggo ================================================ section "Output 4D image properties" option "origin" - "Origin (default=centered)" double multiple no option "dimension" - "Dimension(Deprecated) Use --size instead." int multiple no default="256" option "size" - "Size" int multiple no default="256" option "spacing" - "Spacing" double multiple no default="1" option "direction" - "Direction" double multiple no option "frames" - "Number of time frames in the 4D reconstruction" int no default="10" option "like" - "Copy information from this image (origin, size, spacing, direction, frames)" string no ================================================ FILE: applications/rtkVersion.ggo.in ================================================ version "@RTK_VERSION_MAJOR@.@RTK_VERSION_MINOR@.@RTK_VERSION_PATCH@@RTK_VERSION_HASH@" ================================================ FILE: applications/rtkadmmtotalvariation/CMakeLists.txt ================================================ WRAP_GGO(rtkadmmtotalvariation_GGO_C rtkadmmtotalvariation.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkadmmtotalvariation rtkadmmtotalvariation.cxx ${rtkadmmtotalvariation_GGO_C}) target_link_libraries(rtkadmmtotalvariation RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkadmmtotalvariation) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkadmmtotalvariation/README.md ================================================ # Total Variation Regularized Reconstruction ![sin](../../documentation/docs/ExternalData/SheppLogan-Sinogram-3D.png){w=300px alt="SheppLogan sinogram 3D"} ![img](../../documentation/docs/ExternalData/Admmtv.png){w=300px alt="Admmtv reconstruction"} This script uses the SheppLogan phantom ```{literalinclude} TotalVariationRegularizedReconstruction.sh ``` ================================================ FILE: applications/rtkadmmtotalvariation/TotalVariationRegularizedReconstruction.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # You may add "--arc 200" to make the scan short or "--proj_iso_x 200" to offset the detector # Create projections of the phantom file rtkprojectshepploganphantom -g geometry.xml -o projections.mha --spacing 2 --size 256 # Reconstruct rtkadmmtotalvariation -p . -r projections.mha -o admmtv.mha -g geometry.xml --spacing 2 --size 256 --alpha 1 --beta 1000 -n 3 # Create a reference volume for comparison rtkdrawshepploganphantom --spacing 2 --size 256 -o ref.mha ================================================ FILE: applications/rtkadmmtotalvariation/rtkadmmtotalvariation.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkadmmtotalvariation_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include #include #include "rtkProjectionsReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkConstantImageSource.h" #include "rtkADMMTotalVariationConeBeamReconstructionFilter.h" #include "rtkPhaseGatingImageFilter.h" int main(int argc, char * argv[]) { GGO(rtkadmmtotalvariation, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif ////////////////////////////////////////////////////////////////////////////////////////// // Read all the inputs ////////////////////////////////////////////////////////////////////////////////////////// // Projections reader using projectionsReaderType = rtk::ProjectionsReader; auto projectionsReader = projectionsReaderType::New(); rtk::SetProjectionsReaderFromGgo(projectionsReader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Phase gating weights reader auto phaseGating = rtk::PhaseGatingImageFilter::New(); if (args_info.phases_given) { phaseGating->SetPhasesFileName(args_info.phases_arg); phaseGating->SetGatingWindowWidth(args_info.windowwidth_arg); phaseGating->SetGatingWindowCenter(args_info.windowcenter_arg); phaseGating->SetGatingWindowShape(args_info.windowshape_arg); phaseGating->SetInputProjectionStack(projectionsReader->GetOutput()); phaseGating->SetInputGeometry(geometry); TRY_AND_EXIT_ON_ITK_EXCEPTION(phaseGating->Update()) } // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); inputFilter = constantImageSource; } ////////////////////////////////////////////////////////////////////////////////////////// // Setup the ADMM filter and run it ////////////////////////////////////////////////////////////////////////////////////////// // Set the reconstruction filter using ADMM_TV_FilterType = rtk::ADMMTotalVariationConeBeamReconstructionFilter; auto admmFilter = ADMM_TV_FilterType::New(); // Set the forward and back projection filters to be used inside admmFilter SetForwardProjectionFromGgo(args_info, admmFilter.GetPointer()); SetBackProjectionFromGgo(args_info, admmFilter.GetPointer()); // Set all four numerical parameters admmFilter->SetCG_iterations(args_info.CGiter_arg); admmFilter->SetAL_iterations(args_info.niterations_arg); admmFilter->SetAlpha(args_info.alpha_arg); admmFilter->SetBeta(args_info.beta_arg); // Set the inputs of the ADMM filter admmFilter->SetInput(0, inputFilter->GetOutput()); if (args_info.phases_given) { admmFilter->SetInput(1, phaseGating->GetOutput()); admmFilter->SetGeometry(phaseGating->GetOutputGeometry()); admmFilter->SetGatingWeights(phaseGating->GetGatingWeightsOnSelectedProjections()); } else { admmFilter->SetInput(1, projectionsReader->GetOutput()); admmFilter->SetGeometry(geometry); } admmFilter->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); REPORT_ITERATIONS(admmFilter, ADMM_TV_FilterType, OutputImageType) TRY_AND_EXIT_ON_ITK_EXCEPTION(admmFilter->Update()) // Write the output TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(admmFilter->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkadmmtotalvariation/rtkadmmtotalvariation.ggo ================================================ purpose "Performs an iterative 3D reconstruction with 3D TV regularization" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="1" option "alpha" - "Regularization parameter" float no default="0.1" option "beta" - "Augmented Lagrangian constraint multiplier" float no default="1" option "CGiter" - "Number of nested iterations of conjugate gradient" int no default="5" option "input" i "Input volume" string no option "nodisplaced" - "Disable the displaced detector filter" flag off section "Phase gating" option "phases" - "File containing the phase of each projection" string no option "windowcenter" c "Target reconstruction phase" float no default="0" option "windowwidth" w "Tolerance around the target phase to determine in-phase and out-of-phase projections" float no default="1" option "windowshape" s "Shape of the gating window" values="Rectangular","Triangular" enum no default="Rectangular" ================================================ FILE: applications/rtkadmmtotalvariation/rtkadmmtotalvariation.py ================================================ #!/usr/bin/env python import argparse import sys import itk from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Perform ADMM total variation reconstruction on cone-beam projections." ) # General options parser.add_argument( "-v", "--verbose", help="Verbose execution", action="store_true" ) parser.add_argument( "-g", "--geometry", help="XML geometry file name", type=str, required=True ) parser.add_argument( "-o", "--output", help="Output file name", type=str, required=True ) parser.add_argument( "-n", "--niterations", help="Number of iterations", type=int, default=1 ) parser.add_argument( "--alpha", help="Regularization parameter", type=float, default=0.1 ) parser.add_argument( "--beta", help="Augmented Lagrangian constraint multiplier", type=float, default=1.0, ) parser.add_argument( "--CGiter", help="Number of nested iterations of conjugate gradient", type=int, default=5, ) parser.add_argument("-i", "--input", help="Input volume", type=str) parser.add_argument( "--nodisplaced", help="Disable the displaced detector filter", action="store_true", ) # Phase gating options parser.add_argument( "--phases", help="File containing the phase of each projection", type=str ) parser.add_argument( "-c", "--windowcenter", help="Target reconstruction phase", type=float, default=0.0, ) parser.add_argument( "-w", "--windowwidth", help="Tolerance around the target phase", type=float, default=1.0, ) parser.add_argument( "-s", "--windowshape", help="Shape of the gating window", type=str, choices=["Rectangular", "Triangular"], default="Rectangular", ) # RTK specific groups rtk.add_rtkprojectors_group(parser) rtk.add_rtkiterations_group(parser) rtk.add_rtkinputprojections_group(parser) rtk.add_rtk3Doutputimage_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): # Define output pixel type and dimension OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Projections reader projectionsReader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(projectionsReader, args_info) # Geometry if args_info.verbose: print(f"Reading geometry from {args_info.geometry}...") geometry = rtk.read_geometry(args_info.geometry) # Phase gating weights reader phaseGating = rtk.PhaseGatingImageFilter[OutputImageType].New() if args_info.phases: phaseGating.SetPhasesFileName(args_info.phases) phaseGating.SetGatingWindowWidth(args_info.windowwidth) phaseGating.SetGatingWindowCenter(args_info.windowcenter) # Rectangular=0, Triangular=1 if args_info.windowshape == "Triangular": phaseGating.SetGatingWindowShape(1) else: phaseGating.SetGatingWindowShape(0) phaseGating.SetInputProjectionStack(projectionsReader.GetOutput()) phaseGating.SetInputGeometry(geometry) phaseGating.Update() # Create input: either an existing volume read from a file or a blank image if args_info.input: # Read an existing image to initialize the volume inputFilter = itk.imread(args_info.input) else: # Create new empty volume constantImageSource = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) constantImageSource.Update() inputFilter = constantImageSource # Set the reconstruction filter if hasattr(itk, "CudaImage"): CudaOutputImageType = itk.CudaImage[OutputPixelType, Dimension] admmFilter = rtk.ADMMTotalVariationConeBeamReconstructionFilter[CudaOutputImageType].New() admmFilter.SetInput(0, itk.cuda_image_from_image(inputFilter.GetOutput())) if args_info.phases: admmFilter.SetInput(1, itk.cuda_image_from_image(phaseGating.GetOutput())) else: admmFilter.SetInput( 1, itk.cuda_image_from_image(projectionsReader.GetOutput()) ) else: admmFilter = rtk.ADMMTotalVariationConeBeamReconstructionFilter[ OutputImageType ].New() admmFilter.SetInput(0, inputFilter.GetOutput()) if args_info.phases: admmFilter.SetInput(1, phaseGating.GetOutput()) else: admmFilter.SetInput(1, projectionsReader.GetOutput()) # Set the forward and back projection filters to be used inside admmFilter rtk.SetForwardProjectionFromArgParse(args_info, admmFilter) rtk.SetBackProjectionFromArgParse(args_info, admmFilter) # Set all four numerical parameters admmFilter.SetCG_iterations(args_info.CGiter) admmFilter.SetAL_iterations(args_info.niterations) admmFilter.SetAlpha(args_info.alpha) admmFilter.SetBeta(args_info.beta) admmFilter.SetDisableDisplacedDetectorFilter(args_info.nodisplaced) # Set the inputs of the ADMM filter if args_info.phases: admmFilter.SetGeometry(phaseGating.GetOutputGeometry()) admmFilter.SetGatingWeights(phaseGating.GetGatingWeightsOnSelectedProjections()) else: admmFilter.SetGeometry(geometry) rtk.SetIterationsReportFromArgParse(args_info, admmFilter) admmFilter.Update() writer = itk.ImageFileWriter[OutputImageType].New() writer.SetFileName(args_info.output) writer.SetInput(admmFilter.GetOutput()) writer.Update() def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkadmmwavelets/CMakeLists.txt ================================================ WRAP_GGO(rtkadmmwavelets_GGO_C rtkadmmwavelets.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkadmmwavelets rtkadmmwavelets.cxx ${rtkadmmwavelets_GGO_C}) target_link_libraries(rtkadmmwavelets RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkadmmwavelets) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkadmmwavelets/DaubechiesWavelets.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # You may add "--arc 200" to make the scan short or "--proj_iso_x 200" to offset the detector # Create projections of the phantom file rtkprojectshepploganphantom -g geometry.xml -o projections.mha --spacing 2 --size 256 # Reconstruct rtkadmmwavelets -p . -r projections.mha -o admmwavelets.mha -g geometry.xml --spacing 2 --size 256 --alpha 1 --beta 1000 -n 3 # Create a reference volume for comparison rtkdrawshepploganphantom --spacing 2 --size 256 -o ref.mha ================================================ FILE: applications/rtkadmmwavelets/README.md ================================================ # Daubechies Wavelets Regularized Reconstruction ![sin](../../documentation/docs/ExternalData/SheppLogan-Sinogram-3D.png){w=300px alt="SheppLogan sinogram 3D"} ![img](../../documentation/docs/ExternalData/AdmmWavelets.png){w=300px alt="AdmmWavelets reconstruction"} This script uses the SheppLogan phantom ```{literalinclude} DaubechiesWavelets.sh ``` ================================================ FILE: applications/rtkadmmwavelets/rtkadmmwavelets.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkadmmwavelets_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include #include #include "rtkProjectionsReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkConstantImageSource.h" #include "rtkDisplacedDetectorImageFilter.h" #include "rtkADMMWaveletsConeBeamReconstructionFilter.h" int main(int argc, char * argv[]) { GGO(rtkadmmwavelets, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif ////////////////////////////////////////////////////////////////////////////////////////// // Read all the inputs ////////////////////////////////////////////////////////////////////////////////////////// // Projections reader using projectionsReaderType = rtk::ProjectionsReader; auto projectionsReader = projectionsReaderType::New(); rtk::SetProjectionsReaderFromGgo(projectionsReader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Displaced detector weighting auto ddf = rtk::DisplacedDetectorImageFilter::New(); ddf->SetInput(projectionsReader->GetOutput()); ddf->SetGeometry(geometry); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); inputFilter = constantImageSource; } ////////////////////////////////////////////////////////////////////////////////////////// // Setup the ADMM filter and run it ////////////////////////////////////////////////////////////////////////////////////////// // Set the reconstruction filter using ADMM_Wavelets_FilterType = rtk::ADMMWaveletsConeBeamReconstructionFilter; auto admmFilter = ADMM_Wavelets_FilterType::New(); // Set the forward and back projection filters to be used inside admmFilter SetForwardProjectionFromGgo(args_info, admmFilter.GetPointer()); SetBackProjectionFromGgo(args_info, admmFilter.GetPointer()); // Set the geometry and interpolation weights admmFilter->SetGeometry(geometry); // Set all numerical parameters admmFilter->SetCG_iterations(args_info.CGiter_arg); admmFilter->SetAL_iterations(args_info.niterations_arg); admmFilter->SetAlpha(args_info.alpha_arg); admmFilter->SetBeta(args_info.beta_arg); admmFilter->SetNumberOfLevels(args_info.levels_arg); admmFilter->SetOrder(args_info.order_arg); // Set the inputs of the ADMM filter admmFilter->SetInput(0, inputFilter->GetOutput()); admmFilter->SetInput(1, projectionsReader->GetOutput()); admmFilter->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); REPORT_ITERATIONS(admmFilter, ADMM_Wavelets_FilterType, OutputImageType); TRY_AND_EXIT_ON_ITK_EXCEPTION(admmFilter->Update()) // Write the output TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(admmFilter->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkadmmwavelets/rtkadmmwavelets.ggo ================================================ purpose "Performs an iterative 3D reconstruction with Daubechies wavelets regularization" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="1" option "alpha" - "Regularization parameter" float no default="0.1" option "beta" - "Augmented Lagrangian constraint multiplier" float no default="1" option "CGiter" - "Number of nested iterations of conjugate gradient" int no default="5" option "order" - "The order of the Daubechies wavelets" int no default="3" option "levels" - "The number of decomposition levels in the wavelets transform" int no default="5" option "input" i "Input volume" string no option "nodisplaced" - "Disable the displaced detector filter" flag off ================================================ FILE: applications/rtkamsterdamshroud/CMakeLists.txt ================================================ WRAP_GGO(rtkamsterdamshroud_GGO_C rtkamsterdamshroud.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkamsterdamshroud rtkamsterdamshroud.cxx ${rtkamsterdamshroud_GGO_C}) target_link_libraries(rtkamsterdamshroud RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkamsterdamshroud) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkamsterdamshroud/README.md ================================================ # Amsterdam Shroud ![sin](../../documentation/docs/ExternalData/Moving-Phantom-Sinogram.png){w=400px alt="Moving phantom sinogram"} ![img](../../documentation/docs/ExternalData/Amsterdam.png){w=400px alt="Amsterdam image"} Picture 1 shows the sinogram of the input and picture 2 the shroud image that was created using the command line below. The script uses the file [movingPhantom.mha](https://data.kitware.com/api/v1/file/5be99c428d777f2179a2e537/download) as input: ``` # Creating an Amsterdam Shroud image from a set of projections rtkamsterdamshroud -p . -r movingPhantom.mha -o Amsterdam.mha ``` ================================================ FILE: applications/rtkamsterdamshroud/rtkamsterdamshroud.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkamsterdamshroud_ggo.h" #include "rtkMacro.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkAmsterdamShroudImageFilter.h" #include "rtkGgoFunctions.h" #include int main(int argc, char * argv[]) { GGO(rtkamsterdamshroud, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Amsterdam shroud using ShroudFilterType = rtk::AmsterdamShroudImageFilter; auto shroudFilter = ShroudFilterType::New(); shroudFilter->SetInput(reader->GetOutput()); shroudFilter->SetUnsharpMaskSize(args_info.unsharp_arg); // Corners (if given) if (args_info.clipbox_given) { if (args_info.clipbox_given != 6) { std::cerr << "--clipbox requires 6 values, only" << args_info.clipbox_given << " given." << std::endl; return EXIT_FAILURE; } if (!args_info.geometry_given) { std::cerr << "You must provide the geometry to use --clipbox." << std::endl; return EXIT_FAILURE; } ShroudFilterType::PointType c1, c2; for (int i = 0; i < 3; i++) { c1[i] = args_info.clipbox_arg[i * 2]; c2[i] = args_info.clipbox_arg[i * 2 + 1]; } shroudFilter->SetCorner1(c1); shroudFilter->SetCorner2(c2); // Geometry rtk::ThreeDCircularProjectionGeometry::Pointer geometry; if (args_info.geometry_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); shroudFilter->SetGeometry(geometry); } } TRY_AND_EXIT_ON_ITK_EXCEPTION(shroudFilter->UpdateOutputInformation()) // Write auto writer = itk::ImageFileWriter::New(); writer->SetFileName(args_info.output_arg); writer->SetInput(shroudFilter->GetOutput()); writer->SetNumberOfStreamDivisions(shroudFilter->GetOutput()->GetLargestPossibleRegion().GetSize(1)); TRY_AND_EXIT_ON_ITK_EXCEPTION(writer->Update()) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkamsterdamshroud/rtkamsterdamshroud.ggo ================================================ purpose "Creates an Amsterdam Shroud image from a sequence of projections [Zijp et al, ICCR, 2004]." option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes option "unsharp" u "Unsharp mask size" int no default="17" option "clipbox" c "3D clipbox for cropping projections (x1,x2,y1,y2,z1,z2) mm" double multiple no option "geometry" g "XML geometry file name" string no ================================================ FILE: applications/rtkamsterdamshroud/rtkamsterdamshroud.py ================================================ #!/usr/bin/env python import sys import argparse import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Creates an Amsterdam Shroud image from a sequence of projections [Zijp et al, ICCR, 2004]." ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--output", "-o", help="Output file name", type=str, required=True ) parser.add_argument( "--unsharp", "-u", help="Unsharp mask size", type=int, default=17 ) parser.add_argument( "--clipbox", "-c", help="3D clipbox for cropping projections (x1, x2, y1, y2, z1, z2) in mm", type=float, nargs="+" ) parser.add_argument("--geometry", "-g", help="XML geometry file name", type=str) rtk.add_rtkinputprojections_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): if args_info.verbose: print("Running Amsterdam Shroud computation...") # Define output image type OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Projections reader reader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) # Amsterdam Shroud shroudFilter = rtk.AmsterdamShroudImageFilter[OutputImageType].New() shroudFilter.SetInput(reader.GetOutput()) shroudFilter.SetUnsharpMaskSize(args_info.unsharp) # Corners (if given) if args_info.clipbox: if len(args_info.clipbox) != 6: print( "--clipbox requires exactly 6 values, only ", args_info.clipbox, " given.", ) sys.exit(1) if not args_info.geometry: print("You must provide the geometry to use --clipbox.") sys.exit(1) c1 = itk.Point[itk.F, Dimension](args_info.clipbox[0:6:2]) c2 = itk.Point[itk.F, Dimension](args_info.clipbox[1:6:2]) shroudFilter.SetCorner1(c1) shroudFilter.SetCorner2(c2) # Geometry if args_info.geometry: geometry = rtk.read_geometry(args_info.geometry) shroudFilter.SetGeometry(geometry) shroudFilter.UpdateOutputInformation() # Write output writer = itk.ImageFileWriter[itk.Image[itk.D, 2]].New() writer.SetFileName(args_info.output) writer.SetInput(shroudFilter.GetOutput()) writer.SetNumberOfStreamDivisions( shroudFilter.GetOutput().GetLargestPossibleRegion().GetSize()[1] ) writer.Update() def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkargumentparser.py ================================================ import re import argparse from itk import RTK as rtk import difflib import inspect __all__ = [ "RTKArgumentParser" ] class RTKHelpFormatter(argparse.ArgumentDefaultsHelpFormatter): def _format_usage(self, usage, actions, groups, prefix=None): if prefix is None: prefix = rtk.version() + "\n\nusage: " return super()._format_usage(usage, actions, groups, prefix) class RTKArgumentParser(argparse.ArgumentParser): def __init__(self, description=None, **kwargs): super().__init__(description=description, **kwargs) self.formatter_class = RTKHelpFormatter # allow negative numeric tokens to be treated as values, not options. This mirrors CPython behavior in python 3.14 self._negative_number_matcher = re.compile(r'-\.?\d') self.add_argument('-V', '--version', action='version', version=rtk.version()) def build_signature(self) -> inspect.Signature: """Build a compact Python signature: only required kwargs + **kwargs.""" required_params = [] for action in self._actions: name = getattr(action, 'dest', None) if not name or name in ("help", "version"): continue if getattr(action, 'required', False): required_params.append( inspect.Parameter(name=name, kind=inspect.Parameter.KEYWORD_ONLY) ) # Add catch-all for the many optional CLI options to keep help inline var_kw = inspect.Parameter('kwargs', kind=inspect.Parameter.VAR_KEYWORD) return inspect.Signature(required_params + [var_kw]) def build_usage_examples(self, app_name: str | None = None) -> str: """Return a Usage examples block for Python help().""" name = app_name or self.prog # Collect required destinations req = [a.dest for a in self._actions if getattr(a, 'required', False) and a.dest and a.dest not in ("help", "version")] shell = f"{name}(\"{' '.join([f'--{d} {d.upper()}' for d in req])}\")" py = f"{name}({', '.join([f'{d}={d.upper()}' for d in req])})" return ( "Usage:\n" f" • Shell-style: {shell}\n" f" • Python API: {py}\n\n" ) def apply_signature(self, func): """Apply the built signature to a callable and return it.""" func.__signature__ = self.build_signature() return func def parse_args(self, args=None, namespace=None): """Parse args with optional single-token comma list support for multi-value options. Supported forms: --opt A B C (space separated) --opt A,B,C (single token, comma separated) """ neutralized = {} for action in self._actions: dest = getattr(action, 'dest', None) if not dest or dest in ("help", "version"): continue nargs = getattr(action, 'nargs', None) if nargs != '+': continue t = getattr(action, 'type', None) if t and t is not str: neutralized[dest] = t action.type = str namespace = super().parse_args(args, namespace) for action in self._actions: dest = getattr(action, 'dest', None) if dest not in neutralized: continue caster = neutralized[dest] val = getattr(namespace, dest, None) if isinstance(val, list): # Case 1: user supplied a single token containing commas (e.g. "1,2,3"). # Split on commas, strip whitespace, drop empty pieces, then cast each element. if len(val) == 1 and isinstance(val[0], str) and ',' in val[0]: pieces = [s for s in (p.strip() for p in val[0].split(',')) if s] setattr(namespace, dest, [caster(p) for p in pieces]) else: # Case 2: normal space-separated form (e.g. "1 2 3"). Just cast every token. setattr(namespace, dest, [caster(tk) for tk in val]) action.type = neutralized[dest] return namespace def parse_kwargs(self, func_name: str | None = None, **kwargs): """Convert Python kwargs to argv and parse them. Lists/tuples for multi-value options are serialized as a single comma token.""" actions = {a.dest: a for a in self._actions if a.dest and a.dest not in ("help", "version")} for key in kwargs: if key not in actions: matches = difflib.get_close_matches(key, actions.keys(), n=3, cutoff=0.5) name = func_name or self.prog or "function" msg = f"{name}() got an unexpected keyword argument '{key}'" if matches: msg += f"\nDid you mean: {', '.join(matches)}?" else: msg += f"\nValid arguments are: {', '.join(sorted(actions.keys()))}" raise TypeError(msg) argv = [] for key, val in kwargs.items(): action = actions[key] opt_strings = list(action.option_strings) flag = next((o for o in opt_strings if o.startswith('--')), opt_strings[0]) if isinstance(val, bool): if val: argv.append(flag) elif isinstance(val, (list, tuple)): argv += [flag, ",".join(map(str, val))] else: argv += [flag, str(val)] return self.parse_args(argv) ================================================ FILE: applications/rtkbackprojections/CMakeLists.txt ================================================ WRAP_GGO(rtkbackprojections_GGO_C rtkbackprojections.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkbackprojections rtkbackprojections.cxx ${rtkbackprojections_GGO_C}) target_link_libraries(rtkbackprojections RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkbackprojections) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkbackprojections/rtkbackprojections.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkbackprojections_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkFDKBackProjectionImageFilter.h" #include "rtkFDKWarpBackProjectionImageFilter.h" #include "rtkJosephBackProjectionImageFilter.h" #include "rtkJosephBackAttenuatedProjectionImageFilter.h" #include "rtkZengBackProjectionImageFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaFDKBackProjectionImageFilter.h" # include "rtkCudaBackProjectionImageFilter.h" # include "rtkCudaRayCastBackProjectionImageFilter.h" #endif #include "rtkCyclicDeformationImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtkbackprojections, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::flush; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); if (args_info.verbose_flag) std::cout << " done." << std::endl; // Create an empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->Update()) OutputImageType::Pointer attenuationMap; if (args_info.attenuationmap_given) { if (args_info.verbose_flag) std::cout << "Reading attenuation map " << args_info.attenuationmap_arg << "..." << std::endl; // Read an existing image to initialize the attenuation map attenuationMap = itk::ReadImage(args_info.attenuationmap_arg); } // Create back projection image filter if (args_info.verbose_flag) std::cout << "Backprojecting volume..." << std::endl; rtk::BackProjectionImageFilter::Pointer bp; // In case warp backprojection is used, we create a deformation using DVFPixelType = itk::Vector; using DeformationType = rtk::CyclicDeformationImageFilter, itk::Image>; auto def = DeformationType::New(); switch (args_info.bp_arg) { case (bp_arg_VoxelBasedBackProjection): bp = rtk::BackProjectionImageFilter::New(); break; case (bp_arg_FDKBackProjection): bp = rtk::FDKBackProjectionImageFilter::New(); break; case (bp_arg_FDKWarpBackProjection): if (!args_info.signal_given || !args_info.dvf_given) { std::cerr << "FDKWarpBackProjection requires input 4D deformation " << "vector field and signal file names" << std::endl; return EXIT_FAILURE; } def->SetInput(itk::ReadImage(args_info.dvf_arg)); bp = rtk::FDKWarpBackProjectionImageFilter::New(); def->SetSignalFilename(args_info.signal_arg); dynamic_cast *>( bp.GetPointer()) ->SetDeformation(def); break; case (bp_arg_Joseph): bp = rtk::JosephBackProjectionImageFilter::New(); break; case (bp_arg_JosephAttenuated): bp = rtk::JosephBackAttenuatedProjectionImageFilter::New(); break; case (bp_arg_Zeng): bp = rtk::ZengBackProjectionImageFilter::New(); break; case (bp_arg_CudaFDKBackProjection): #ifdef RTK_USE_CUDA bp = rtk::CudaFDKBackProjectionImageFilter::New(); #else std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; #endif break; case (bp_arg_CudaBackProjection): #ifdef RTK_USE_CUDA bp = rtk::CudaBackProjectionImageFilter::New(); #else std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; #endif break; case (bp_arg_CudaRayCast): #ifdef RTK_USE_CUDA bp = rtk::CudaRayCastBackProjectionImageFilter::New(); #else std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; #endif break; default: std::cerr << "Unhandled --method value." << std::endl; return EXIT_FAILURE; } bp->SetInput(constantImageSource->GetOutput()); bp->SetInput(1, reader->GetOutput()); if (args_info.attenuationmap_given) bp->SetInput(2, attenuationMap); if (args_info.sigmazero_given && args_info.bp_arg == bp_arg_Zeng) dynamic_cast *>(bp.GetPointer()) ->SetSigmaZero(args_info.sigmazero_arg); if (args_info.alphapsf_given && args_info.bp_arg == bp_arg_Zeng) dynamic_cast *>(bp.GetPointer()) ->SetAlpha(args_info.alphapsf_arg); bp->SetGeometry(geometry); TRY_AND_EXIT_ON_ITK_EXCEPTION(bp->Update()) // Write if (args_info.verbose_flag) std::cout << "Writing... " << std::endl; TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(bp->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkbackprojections/rtkbackprojections.ggo ================================================ purpose "Backprojects a volume according to a geometry file." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output volume file name" string yes section "Projectors" option "bp" - "Backprojection method" values="VoxelBasedBackProjection","FDKBackProjection","FDKWarpBackProjection","Joseph","JosephAttenuated", "Zeng", "CudaFDKBackProjection","CudaBackProjection","CudaRayCast" enum no default="VoxelBasedBackProjection" option "attenuationmap" - "Attenuation map relative to the volume to perfom the attenuation correction" string no option "sigmazero" - "PSF value at a distance of 0 meter of the detector" double no option "alphapsf" - "Slope of the PSF against the detector distance" double no section "Warped backprojection" option "signal" - "Signal file name" string no option "dvf" - "Input 4D DVF" string no ================================================ FILE: applications/rtkbackprojections/rtkbackprojections.py ================================================ #!/usr/bin/env python import sys import argparse import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Backprojects a volume according to a geometry file." ) # General arguments parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--geometry", "-g", help="XML geometry file name", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output volume file name", type=str, required=True ) # Projectors group rtkprojectors_group = parser.add_argument_group("Projectors") rtkprojectors_group.add_argument( "--bp", help="Backprojection method", type=str, choices=[ "VoxelBasedBackProjection", "FDKBackProjection", "FDKWarpBackProjection", "Joseph", "JosephAttenuated", "Zeng", "CudaFDKBackProjection", "CudaBackProjection", "CudaRayCast", ], default="VoxelBasedBackProjection", ) rtkprojectors_group.add_argument( "--attenuationmap", help="Attenuation map for attenuation correction", type=str ) rtkprojectors_group.add_argument( "--sigmazero", help="PSF value at a distance of 0 meter of the detector", type=float, ) rtkprojectors_group.add_argument( "--alphapsf", help="Slope of the PSF against the detector distance", type=float ) # Warped backprojections group Warped_backprojection_group = parser.add_argument_group( "Warped backprojections group" ) Warped_backprojection_group.add_argument( "--signal", help="Signal file name", type=str ) Warped_backprojection_group.add_argument("--dvf", help="Input 4D DVF", type=str) rtk.add_rtk3Doutputimage_group(parser) rtk.add_rtkinputprojections_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): # Define output pixel type and dimension OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] if hasattr(itk, "CudaImage"): OutputCudaImageType = itk.CudaImage[OutputPixelType, Dimension] # Geometry if args_info.verbose: print(f"Reading geometry from {args_info.geometry}...") geometry = rtk.read_geometry(args_info.geometry) if args_info.verbose: print(f"done.") # Create empty volume constantImageSource = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) # Projections reader reader = rtk.ProjectionsReader[itk.Image[itk.F, 3]].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) reader.Update() attenuation_map = None if args_info.attenuationmap: if args_info.verbose: print(f"Reading attenuation map from {args_info.attenuationmap}...") # Read an existing image to initialize the attenuation map attenuation_map = itk.imread(args_info.attenuationmap) # In case warp backprojection is used, we create a deformation DVFPixelType = itk.Vector[itk.F, 3] DVFImageSequenceType = itk.Image[DVFPixelType, 4] DVFImageType = itk.Image[DVFPixelType, 3] DeformationType = rtk.CyclicDeformationImageFilter[ DVFImageSequenceType, DVFImageType ] bp = None if args_info.bp == "VoxelBasedBackProjection": bp = rtk.BackProjectionImageFilter[OutputImageType, OutputImageType].New() elif args_info.bp == "FDKBackProjection": bp = rtk.FDKBackProjectionImageFilter[OutputImageType, OutputImageType].New() elif args_info.bp == "FDKWarpBackProjection": if not args_info.dvf or not args_info.signal: raise ValueError( "FDKWarpBackProjection requires input 4D deformation vector field and signal file names" ) deformation = rtk.CyclicDeformationImageFilter[ DVFImageSequenceType, DVFImageType ].New() deformation.SetInput(itk.imread(args_info.dvf)) bp = rtk.FDKWarpBackProjectionImageFilter[ OutputImageType, OutputImageType, DeformationType ].New() deformation.SetSignalFilename(args_info.signal) bp.SetDeformation(deformation) elif args_info.bp == "Joseph": bp = rtk.JosephBackProjectionImageFilter[OutputImageType, OutputImageType].New() elif args_info.bp == "JosephAttenuated": bp = rtk.JosephBackAttenuatedProjectionImageFilter[ OutputImageType, OutputImageType ].New() elif args_info.bp == "Zeng": bp = rtk.ZengBackProjectionImageFilter[OutputImageType, OutputImageType].New() if args_info.sigmazero: bp.SetSigmaZero(args_info.sigmazero) if args_info.alphapsf: bp.SetAlpha(args_info.alphapsf) elif args_info.bp == "CudaFDKBackProjection": if hasattr(itk, "CudaImage"): bp = rtk.CudaFDKBackProjectionImageFilter.New() else: print("The program has not been compiled with cuda option") sys.exit(1) elif args_info.bp == "CudaBackProjection": if hasattr(itk, "CudaImage"): bp = rtk.CudaBackProjectionImageFilter[OutputCudaImageType].New() else: print("The program has not been compiled with cuda option") sys.exit(1) elif args_info.bp == "CudaRayCast": if hasattr(itk, "CudaImage"): bp = rtk.CudaRayCastBackProjectionImageFilter.New() else: print("The program has not been compiled with cuda option") sys.exit(1) if args_info.bp in ["CudaFDKBackProjection", "CudaBackProjection", "CudaRayCast"]: bp.SetInput(itk.cuda_image_from_image(constantImageSource.GetOutput())) bp.SetInput(1, itk.cuda_image_from_image(reader.GetOutput())) if attenuation_map: bp.SetInput(2, itk.cuda_image_from_image(attenuation_map)) else: bp.SetInput(constantImageSource.GetOutput()) bp.SetInput(1, reader.GetOutput()) if attenuation_map: bp.SetInput(2, attenuation_map) bp.SetGeometry(geometry) bp.Update() # Write if args_info.verbose: print(f"Writing output to {args_info.output}...") writter = itk.ImageFileWriter[OutputImageType].New() writter.SetFileName(args_info.output) writter.SetInput(bp.GetOutput()) writter.Update() if args_info.verbose: print("Processing completed successfully.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkbioscangeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkbioscangeometry_GGO_C rtkbioscangeometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkbioscangeometry rtkbioscangeometry.cxx ${rtkbioscangeometry_GGO_C}) target_link_libraries(rtkbioscangeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkbioscangeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkbioscangeometry/rtkbioscangeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkbioscangeometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkBioscanGeometryReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" int main(int argc, char * argv[]) { GGO(rtkbioscangeometry, args_info); // Create geometry reader auto bioscanReader = rtk::BioscanGeometryReader::New(); bioscanReader->SetProjectionsFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); TRY_AND_EXIT_ON_ITK_EXCEPTION(bioscanReader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry( const_cast(bioscanReader->GetGeometry()), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkbioscangeometry/rtkbioscangeometry.ggo ================================================ purpose "Creates an RTK geometry file from a sequence of x-ray projections of a Bioscan NanoSPECT/CT scanner." option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes section "Projections" option "path" p "Path containing projections" string yes option "regexp" r "Regular expression to select projection files in path" string yes option "nsort" - "Numeric sort for regular expression matches" flag off option "submatch" - "Index of the submatch that will be used to sort matches" int no default="0" ================================================ FILE: applications/rtkbioscangeometry/rtkbioscangeometry.py ================================================ import argparse import sys from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Read Bioscan geometry and write RTK geometry XML file." ) parser.add_argument( "--output", "-o", required=True, help="Output geometry XML file" ) parser.add_argument( "--verbose", "-v", action="store_true", help="Enable verbose output" ) rtk.add_rtkinputprojections_group(parser) return parser def process(args_info: argparse.Namespace): # Create geometry reader bioscanReader = rtk.BioscanGeometryReader.New() bioscanReader.SetProjectionsFileNames(rtk.GetProjectionsFileNamesFromArgParse(args_info)) if args_info.verbose: print("Reading Bioscan geometry...") bioscanReader.UpdateOutputData() # Write geometry if args_info.verbose: print(f"Writing geometry to {args_info.output}...") rtk.write_geometry(bioscanReader.GetGeometry(), args_info.output) if args_info.verbose: print("Done.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkcheckimagequality/CMakeLists.txt ================================================ WRAP_GGO(rtkcheckimagequality_GGO_C rtkcheckimagequality.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkcheckimagequality rtkcheckimagequality.cxx ${rtkcheckimagequality_GGO_C}) target_link_libraries(rtkcheckimagequality RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkcheckimagequality) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkcheckimagequality/rtkcheckimagequality.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkcheckimagequality_ggo.h" #include "rtkConfiguration.h" #include "rtkMacro.h" #include "itkImage.h" #include "itkImageFileReader.h" #include "itkImageRegionConstIterator.h" namespace rtk { /** * \file rtkcheckimagequality.cxx * * \brief Checks that an image has a satisfactory MSE against a reference. * * \author Aurélien Coussat */ template double MSE(typename TImage::Pointer reference, typename TImage::Pointer reconstruction) { using ImageIteratorType = itk::ImageRegionConstIterator; ImageIteratorType itTest(reconstruction, reconstruction->GetBufferedRegion()); ImageIteratorType itRef(reference, reference->GetBufferedRegion()); using ErrorType = double; ErrorType EnerError = 0.; itTest.GoToBegin(); itRef.GoToBegin(); while (!itRef.IsAtEnd()) { typename TImage::PixelType TestVal = itTest.Get(); typename TImage::PixelType RefVal = itRef.Get(); EnerError += std::pow(ErrorType(RefVal - TestVal), 2.); ++itTest; ++itRef; } return EnerError; } } // namespace rtk int main(int argc, char ** argv) { GGO(rtkcheckimagequality, args_info); constexpr unsigned int Dimension = 3; using PixelType = float; using ImageType = itk::Image; // Maximum number of comparisons to perform (depends on the number of inputs) unsigned int n_max = std::max({ args_info.reference_given, args_info.reconstruction_given, args_info.threshold_given }); for (unsigned int i = 0; i < n_max; i++) { unsigned int reference_index = std::min(args_info.reference_given - 1, i); unsigned int reconstruction_index = std::min(args_info.reconstruction_given - 1, i); unsigned int threshold_index = std::min(args_info.threshold_given - 1, i); ImageType::Pointer reference, reconstruction; try { reference = itk::ReadImage(args_info.reference_arg[reference_index]); } catch (::itk::ExceptionObject & e) { std::cerr << e.GetDescription(); return EXIT_FAILURE; } try { reconstruction = itk::ReadImage(args_info.reconstruction_arg[reconstruction_index]); } catch (::itk::ExceptionObject & e) { std::cerr << e.GetDescription(); return EXIT_FAILURE; } double mse = rtk::MSE(reference, reconstruction); if (mse > args_info.threshold_arg[threshold_index]) { std::cerr << "Error comparing " << args_info.reference_arg[reference_index] << " and " << args_info.reconstruction_arg[reconstruction_index] << ":" << std::endl << "MSE " << mse << " above given threshold " << args_info.threshold_arg[threshold_index] << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkcheckimagequality/rtkcheckimagequality.ggo ================================================ purpose "Checks the MSE of a reconstructed image against a reference." option "verbose" v "Verbose execution" flag off option "reference" i "Reference volume(s)" string multiple yes option "reconstruction" j "Reconstructed volume(s)" string multiple yes option "threshold" t "MSE threshold(s)" int multiple yes ================================================ FILE: applications/rtkcheckimagequality/rtkcheckimagequality.py ================================================ import argparse import sys import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Checks the MSE of a reconstructed image against a reference." ) parser.add_argument( "--reference", "-i", required=True, type=str, nargs="+", help="Reference volume(s)", ) parser.add_argument( "--reconstruction", "-j", required=True, type=str, nargs="+", help="Reconstructed volume(s)", ) parser.add_argument( "--threshold", "-t", required=True, type=float, nargs="+", help="MSE threshold(s)", ) parser.add_argument( "--verbose", "-v", action="store_true", help="Enable verbose output" ) return parser def mse(reference, reconstruction): arr_ref = itk.array_view_from_image(reference) arr_rec = itk.array_view_from_image(reconstruction) return float(((arr_ref - arr_rec) ** 2).sum()) def process(args_info: argparse.Namespace): # Maximum number of comparisons to perform (depends on the number of inputs) n_max = max( len(args_info.reference), len(args_info.reconstruction), len(args_info.threshold), ) for i in range(n_max): reference_index = min(len(args_info.reference) - 1, i) reconstruction_index = min(len(args_info.reconstruction) - 1, i) threshold_index = min(len(args_info.threshold) - 1, i) if args_info.verbose: print(f"Reading reference image: {args_info.reference[reference_index]}") reference = itk.imread(args_info.reference[reference_index]) if args_info.verbose: print(f"Reading reconstruction image: {args_info.reconstruction[reconstruction_index]}") reconstruction = itk.imread(args_info.reconstruction[reconstruction_index]) mse_val = mse(reference, reconstruction) if args_info.verbose: print(f"MSE: {mse_val} (threshold: {args_info.threshold[threshold_index]})") if mse_val > args_info.threshold[threshold_index]: print( f"Error comparing {args_info.reference[reference_index]} and {args_info.reconstruction[reconstruction_index]}:\n" f"MSE {mse_val} above given threshold {args_info.threshold[threshold_index]}", file=sys.stderr, ) sys.exit(1) if args_info.verbose: print("All comparisons passed.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkconjugategradient/CMakeLists.txt ================================================ WRAP_GGO(rtkconjugategradient_GGO_C rtkconjugategradient.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkconjugategradient rtkconjugategradient.cxx ${rtkconjugategradient_GGO_C}) target_link_libraries(rtkconjugategradient RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkconjugategradient) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkconjugategradient/ConjugateGradient2D.sh ================================================ # Generate geometry for fan-beam setup rtksimulatedgeometry -n 720 -o geometry.xml --arc 360 # Create projections of the phantom file # Note the sinogram being 3 pixels wide in the y direction to allow back-projection interpolation in a 2D image rtkprojectgeometricphantom -g geometry.xml -o projections.mha --spacing 2 --size=512,3,512 --phantomfile SheppLogan-2d.txt --phantomscale=256,1,256 # Perform Conjugate Gradient reconstruction rtkconjugategradient -p . -r projections.mha -o cg.mha -g geometry.xml --spacing 2 --size 256 1 256 -n 10 # Create a reference volume for comparison rtkdrawgeometricphantom --spacing 2 --size=256,1,256 --phantomfile SheppLogan-2d.txt -o ref.mha --phantomscale=256,1,256 ================================================ FILE: applications/rtkconjugategradient/ConjugateGradient3D.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # You may add "--arc 200" to make the scan short or "--proj_iso_x 200" to offset the detector # Create projections of the phantom file rtkprojectshepploganphantom -g geometry.xml -o projections.mha --spacing 2 --size 256 # Reconstruct rtkconjugategradient -p . -r projections.mha -o 3dcg.mha -g geometry.xml --spacing 2 --size 256 -n 10 # Create a reference volume for comparison rtkdrawshepploganphantom --spacing 2 --size 256 -o ref.mha ================================================ FILE: applications/rtkconjugategradient/NoisyConjugateGradient.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # You may add "--arc 200" to make the scan short or "--proj_iso_x 200" to offset the detector # Create projections of the phantom file rtkprojectshepploganphantom -g geometry.xml -o projections.mha --spacing 2 --size 256 # Perform least squares reconstruction rtkconjugategradient -p . -r noisyLineIntegrals.mha -o LeastSquares.mha -g geom.xml -n 20 # Perform weighted least squares reconstruction rtkconjugategradient -p . -r noisyLineIntegrals.mha -o WeightedLeastSquares.mha -g geom.xml -w weightsmap.mha -n 10 # Create a reference volume for comparison rtkdrawshepploganphantom --spacing 2 --size 256 -o ref.mha ================================================ FILE: applications/rtkconjugategradient/README.md ================================================ # Conjugate gradient `````{tab-set} ````{tab-item} 3D ## 3D ![sin](../../documentation/docs/ExternalData/SheppLogan-Sinogram-3D.png){w=200px alt="SheppLogan sinogram 3D"} ![img](../../documentation/docs/ExternalData/ConjugateGradient-3D.png){w=200px alt="ConjugateGradient reconstruction"} This script uses the SheppLogan phantom. ```{literalinclude} ConjugateGradient3D.sh ``` ```` ````{tab-item} 2D ## 2D ![sin](../../documentation/docs/ExternalData/ConjugateGradient-Sinogram-2D.png){w=200px alt="SHeppLogan sinogram 2D"} ![img](../../documentation/docs/ExternalData/ConjugateGradient-2D.png){w=200px alt="ConjugateGradient reconstruction"} The same reconstruction can be performed using the original 2D Shepp-Logan phantom. RTK can perform 2D reconstructions through images wide of 1 pixel in the y direction. The following script performs the same reconstruction as above in a 2D environment and use the Shepp–Logan phantom (https://data.kitware.com/api/v1/file/67d1ff45c6dec2fc9c534d0d/download). ```{literalinclude} ConjugateGradient2D.sh ``` ```` ````{tab-item} Noisy Reconstruction ## Noisy Reconstruction In the presence of noise, all projection data may not be equally reliable. The conjugate gradient algorithm can be modified to take this into account, and each pixel of the projections can be associated with a weight. The higher the weight, the more reliable the pixel data. Download [noisy projections](https://data.kitware.com/api/v1/item/5be99cdf8d777f2179a2e63d/download) and [the associated weights](https://data.kitware.com/api/v1/item/5be99d268d777f2179a2e6f8/download), as well as [the geometry](https://data.kitware.com/api/v1/item/5be99d268d777f2179a2e700/download), and run the following to compare the regular least squares reconstruction (without weights) and the weighted least squares reconstruction. ```{literalinclude} NoisyConjugateGradient.sh ``` ```` ````` ================================================ FILE: applications/rtkconjugategradient/rtkconjugategradient.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkconjugategradient_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkConjugateGradientConeBeamReconstructionFilter.h" #include "rtkIterationCommands.h" #include #include #include #ifdef RTK_USE_CUDA # include #endif #include int main(int argc, char * argv[]) { GGO(rtkconjugategradient, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); inputFilter = constantImageSource; } // Read weights if given OutputImageType::Pointer inputWeights; if (args_info.weights_given) { using WeightsReaderType = itk::ImageFileReader; auto weightsReader = WeightsReaderType::New(); weightsReader->SetFileName(args_info.weights_arg); inputWeights = weightsReader->GetOutput(); TRY_AND_EXIT_ON_ITK_EXCEPTION(inputWeights->Update()) } // Read regularization weights if given OutputImageType::Pointer localRegWeights; if (args_info.regweights_given) { using WeightsReaderType = itk::ImageFileReader; auto localRegWeightsReader = WeightsReaderType::New(); localRegWeightsReader->SetFileName(args_info.regweights_arg); localRegWeights = localRegWeightsReader->GetOutput(); localRegWeights->Update(); } // Read Support Mask if given OutputImageType::Pointer supportmaskSource; if (args_info.mask_given) { supportmaskSource = itk::ReadImage(args_info.mask_arg); } // Set the forward and back projection filters to be used auto conjugategradient = rtk::ConjugateGradientConeBeamReconstructionFilter::New(); SetForwardProjectionFromGgo(args_info, conjugategradient.GetPointer()); SetBackProjectionFromGgo(args_info, conjugategradient.GetPointer()); conjugategradient->SetInputVolume(inputFilter->GetOutput()); conjugategradient->SetInputProjectionStack(reader->GetOutput()); conjugategradient->SetInputWeights(inputWeights); conjugategradient->SetLocalRegularizationWeights(localRegWeights); conjugategradient->SetCudaConjugateGradient(!args_info.nocudacg_flag); if (args_info.mask_given) { conjugategradient->SetSupportMask(supportmaskSource); } if (args_info.gamma_given) conjugategradient->SetGamma(args_info.gamma_arg); if (args_info.tikhonov_given) conjugategradient->SetTikhonov(args_info.tikhonov_arg); conjugategradient->SetGeometry(geometry); conjugategradient->SetNumberOfIterations(args_info.niterations_arg); conjugategradient->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); REPORT_ITERATIONS( conjugategradient, rtk::ConjugateGradientConeBeamReconstructionFilter, OutputImageType) itk::TimeProbe readerProbe; if (args_info.time_flag) { std::cout << "Recording elapsed time... " << std::flush; readerProbe.Start(); } TRY_AND_EXIT_ON_ITK_EXCEPTION(conjugategradient->Update()) if (args_info.time_flag) { // conjugategradient->PrintTiming(std::cout); readerProbe.Stop(); std::cout << "It took... " << readerProbe.GetMean() << ' ' << readerProbe.GetUnit() << std::endl; } // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(conjugategradient->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkconjugategradient/rtkconjugategradient.ggo ================================================ purpose "Reconstructs a 3D volume from a sequence of projections with a conjugate gradient technique" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="5" option "time" t "Records elapsed time during the process" flag off option "input" i "Input volume" string no option "weights" w "Weights file for Weighted Least Squares (WLS)" string no option "regweights" - "Local regularization weights file" string no option "gamma" - "Laplacian regularization weight" float no default="0" option "tikhonov" - "Tikhonov regularization weight" float no default="0" option "nocudacg" - "Do not perform conjugate gradient calculations on GPU" flag off option "mask" m "Apply a support binary mask: reconstruction kept null outside the mask)" string no option "nodisplaced" - "Disable the displaced detector filter" flag off ================================================ FILE: applications/rtkconjugategradient/rtkconjugategradient.py ================================================ #!/usr/bin/env python import argparse import itk from itk import RTK as rtk def build_parser(): # argument parsing parser = rtk.RTKArgumentParser( description="Reconstructs a 3D volume from a sequence of projections with a conjugate gradient technique" ) parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument("--geometry", "-g", help="Geometry file name", required=True) parser.add_argument("--output", "-o", help="Output file name", required=True) parser.add_argument( "--niterations", "-n", type=int, default=5, help="Number of iterations" ) parser.add_argument("--input", "-i", help="Input volume") parser.add_argument( "--weights", "-w", help="Weights file for Weighted Least Squares (WLS)" ) parser.add_argument("--gamma", help="Laplacian regularization weight") parser.add_argument("--tikhonov", help="Tikhonov regularization weight") parser.add_argument( "--nocudacg", help="Do not perform conjugate gradient calculations on GPU", action="store_true", ) parser.add_argument( "--mask", "-m", help="Apply a support binary mask: reconstruction kept null outside the mask)", ) parser.add_argument( "--nodisplaced", help="Disable the displaced detector filter", action="store_true", ) rtk.add_rtkinputprojections_group(parser) rtk.add_rtk3Doutputimage_group(parser) rtk.add_rtkprojectors_group(parser) rtk.add_rtkiterations_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Projections reader reader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) # Geometry if args_info.verbose: print(f"Reading geometry information from {args_info.geometry}") geometry = rtk.read_geometry(args_info.geometry) # Create input: either an existing volume read from a file or a blank image if args_info.input is not None: # Read an existing image to initialize the volume InputReaderType = itk.ImageFileReader[OutputImageType] inputReader = InputReaderType.New() inputReader.SetFileName(args_info.input) inputFilter = inputReader else: # Create new empty volume ConstantImageSourceType = rtk.ConstantImageSource[OutputImageType] constantImageSource = ConstantImageSourceType.New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) inputFilter = constantImageSource # Read weights if given, otherwise default to weights all equal to one if args_info.weights is not None: WeightsReaderType = itk.ImageFileReader[OutputImageType] weightsReader = WeightsReaderType.New() weightsReader.SetFileName(args_info.weights) weightsSource = weightsReader else: ConstantWeightsSourceType = rtk.ConstantImageSource[OutputImageType] constantWeightsSource = ConstantWeightsSourceType.New() # Set the weights to be like the projections reader.UpdateOutputInformation() constantWeightsSource.SetInformationFromImage(reader.GetOutput()) constantWeightsSource.SetConstant(1.0) weightsSource = constantWeightsSource # Read Support Mask if given if args_info.mask is not None: supportmask = itk.imread(args_info.mask) # Set the forward and back projection filters to be used if hasattr(itk, "CudaImage"): OutputCudaImageType = itk.CudaImage[OutputPixelType, Dimension] ConjugateGradientFilterType = rtk.ConjugateGradientConeBeamReconstructionFilter[ OutputCudaImageType ] conjugategradient = ConjugateGradientFilterType.New() conjugategradient.SetInput(itk.cuda_image_from_image(inputFilter.GetOutput())) conjugategradient.SetInput(1, itk.cuda_image_from_image(reader.GetOutput())) conjugategradient.SetInput( 2, itk.cuda_image_from_image(weightsSource.GetOutput()) ) conjugategradient.SetCudaConjugateGradient(not args_info.nocudacg) if args_info.mask is not None: conjugategradient.SetSupportMask(itk.cuda_image_from_image(supportmask)) else: ConjugateGradientFilterType = rtk.ConjugateGradientConeBeamReconstructionFilter[ OutputImageType ] conjugategradient = ConjugateGradientFilterType.New() conjugategradient.SetInput(inputFilter.GetOutput()) conjugategradient.SetInput(1, reader.GetOutput()) conjugategradient.SetInput(2, weightsSource.GetOutput()) if args_info.mask is not None: conjugategradient.SetSupportMask(supportmask) rtk.SetForwardProjectionFromArgParse(args_info, conjugategradient) rtk.SetBackProjectionFromArgParse(args_info, conjugategradient) rtk.SetIterationsReportFromArgParse(args_info, conjugategradient) if args_info.gamma is not None: conjugategradient.SetGamma(args_info.gamma) if args_info.tikhonov is not None: conjugategradient.SetTikhonov(args_info.tikhonov) conjugategradient.SetGeometry(geometry) conjugategradient.SetNumberOfIterations(args_info.niterations) conjugategradient.SetDisableDisplacedDetectorFilter(args_info.nodisplaced) rtk.SetIterationsReportFromArgParse(args_info, conjugategradient) conjugategradient.Update() # Write writer = itk.ImageFileWriter[OutputImageType].New() writer.SetFileName(args_info.output) writer.SetInput(conjugategradient.GetOutput()) writer.Update() def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkdigisensgeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkdigisensgeometry_GGO_C rtkdigisensgeometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkdigisensgeometry rtkdigisensgeometry.cxx ${rtkdigisensgeometry_GGO_C}) target_link_libraries(rtkdigisensgeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkdigisensgeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkdigisensgeometry/rtkdigisensgeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkdigisensgeometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkDigisensGeometryReader.h" int main(int argc, char * argv[]) { GGO(rtkdigisensgeometry, args_info); // Create geometry reader auto reader = rtk::DigisensGeometryReader::New(); reader->SetXMLFileName(args_info.xml_file_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(reader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkdigisensgeometry/rtkdigisensgeometry.ggo ================================================ purpose "Creates an RTK geometry file from a Digisens geometry calibration." option "verbose" v "Verbose execution" flag off option "xml_file" x "Digisens XML information file" string yes option "output" o "Output file name" string yes ================================================ FILE: applications/rtkdigisensgeometry/rtkdigisensgeometry.py ================================================ import argparse from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Read Digisens geometry XML and write RTK geometry XML file." ) parser.add_argument( "--xml_file", "-x", required=True, help="Input Digisens XML calibration file" ) parser.add_argument( "--output", "-o", required=True, help="Output geometry XML file" ) parser.add_argument( "--verbose", "-v", action="store_true", help="Enable verbose output" ) return parser def process(args_info: argparse.Namespace): # Create geometry reader reader = rtk.DigisensGeometryReader.New() reader.SetXMLFileName(args_info.xml_file) if args_info.verbose: print(f"Reading Digisens XML: {args_info.xml_file}") reader.UpdateOutputData() # Write geometry if args_info.verbose: print(f"Writing geometry to {args_info.output}") rtk.write_geometry(reader.GetGeometry(), args_info.output) if args_info.verbose: print("Done.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkdrawgeometricphantom/CMakeLists.txt ================================================ WRAP_GGO(rtkdrawgeometricphantom_GGO_C rtkdrawgeometricphantom.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkdrawgeometricphantom rtkdrawgeometricphantom.cxx ${rtkdrawgeometricphantom_GGO_C}) target_link_libraries(rtkdrawgeometricphantom RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkdrawgeometricphantom) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkdrawgeometricphantom/README.md ================================================ # Create gammex phantom ![img](../../documentation/docs/ExternalData/GammexPhantom.png){w=400px alt="Gammex"} This script uses the file [Gammex.txt](https://data.kitware.com/api/v1/file/6762da8a290777363f95c293/download) as configuration file which creates a Gammex phantom. ``` # Create a 3D Gammex phantom rtkdrawgeometricphantom --phantomfile Gammex.txt -o gammex.mha ``` ================================================ FILE: applications/rtkdrawgeometricphantom/rtkdrawgeometricphantom.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkdrawgeometricphantom_ggo.h" #include "rtkGgoFunctions.h" #include "rtkDrawGeometricPhantomImageFilter.h" #include "itkImageFileWriter.h" #include "rtkAdditiveGaussianNoiseImageFilter.h" int main(int argc, char * argv[]) { GGO(rtkdrawgeometricphantom, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Empty volume image using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); using DQType = rtk::DrawGeometricPhantomImageFilter; // Offset, scale, rotation DQType::VectorType offset(0.); if (args_info.offset_given) { if (args_info.offset_given > 3) { std::cerr << "--offset needs up to 3 values" << std::endl; exit(EXIT_FAILURE); } offset[0] = args_info.offset_arg[0]; offset[1] = args_info.offset_arg[1]; offset[2] = args_info.offset_arg[2]; } DQType::VectorType scale; scale.Fill(args_info.phantomscale_arg[0]); if (args_info.phantomscale_given) { if (args_info.phantomscale_given > 3) { std::cerr << "--phantomscale needs up to 3 values" << std::endl; exit(EXIT_FAILURE); } for (unsigned int i = 0; i < std::min(args_info.phantomscale_given, Dimension); i++) scale[i] = args_info.phantomscale_arg[i]; } DQType::RotationMatrixType rot; rot.SetIdentity(); if (args_info.rotation_given) { if (args_info.rotation_given != 9) { std::cerr << "--rotation needs exactly 9 values" << std::endl; exit(EXIT_FAILURE); } for (unsigned int i = 0; i < Dimension; i++) for (unsigned int j = 0; j < Dimension; j++) rot[i][j] = args_info.rotation_arg[i * Dimension + j]; } // Reference if (args_info.verbose_flag) std::cout << "Creating reference... " << std::flush; auto dq = DQType::New(); dq->SetInput(constantImageSource->GetOutput()); dq->SetPhantomScale(scale); dq->SetOriginOffset(offset); dq->SetRotationMatrix(rot); dq->SetConfigFile(args_info.phantomfile_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(dq->Update()) // Add noise OutputImageType::Pointer output = dq->GetOutput(); if (args_info.noise_given) { auto noisy = rtk::AdditiveGaussianNoiseImageFilter::New(); noisy->SetInput(output); noisy->SetMean(0.0); noisy->SetStandardDeviation(args_info.noise_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(noisy->Update()) output = noisy->GetOutput(); } // Write if (args_info.verbose_flag) std::cout << "Writing reference... " << std::flush; TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(output, args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkdrawgeometricphantom/rtkdrawgeometricphantom.ggo ================================================ purpose "Computes a 3D voxelized phantom from a phantom description file" option "verbose" v "Verbose execution" flag off option "output" o "Output projections file name" string yes option "phantomfile" - "Parameters of the phantom reference" string yes option "noise" - "Gaussian noise parameter (SD)" double no option "phantomscale" - "Scaling factor for the phantom dimensions" double multiple no default="1." option "offset" - "3D spatial offset of the phantom center" double multiple no option "rotation" - "Rotation matrix for the phantom" double multiple no ================================================ FILE: applications/rtkdrawgeometricphantom/rtkdrawgeometricphantom.py ================================================ #!/usr/bin/env python import argparse import sys import itk from itk import RTK as rtk import numpy as np def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Computes a 3D voxelized phantom from a phantom description file." ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--output", "-o", help="Output projections file name", type=str, required=True ) parser.add_argument( "--phantomfile", help="Parameters of the phantom reference", type=str, required=True, ) parser.add_argument("--noise", help="Gaussian noise parameter (SD)", type=float) parser.add_argument( "--phantomscale", help="Scaling factor for the phantom dimensions", type=float, nargs="+", default=[1.0], ) parser.add_argument( "--offset", help="3D spatial offset of the phantom center", type=float, nargs="+", ) parser.add_argument( "--rotation", help="Rotation matrix for the phantom", type=float, nargs="+", ) rtk.add_rtk3Doutputimage_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): # Define output pixel type and dimension OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Empty volume image constant_image_source = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constant_image_source, args_info) # Offset, scale, rotation offset = [0.0] * 3 if args_info.offset: if len(args_info.offset) > 3: print("--offset needs up to 3 values", file=sys.stderr) sys.exit(1) offset = args_info.offset scale = [args_info.phantomscale[0]] * Dimension if args_info.phantomscale: if len(args_info.phantomscale) > 3: print("--phantomscale needs up to 3 values", file=sys.stderr) sys.exit(1) for i in range(len(args_info.phantomscale)): scale[i] = args_info.phantomscale[i] rot = itk.Matrix[itk.D, Dimension, Dimension]() rot.SetIdentity() if args_info.rotation: if len(args_info.rotation) != 9: print("--rotation needs exactly 9 values", file=sys.stderr) sys.exit(1) rot = itk.matrix_from_array(np.array(args_info.rotation).reshape(3, 3)) # Reference if args_info.verbose: print("Creating reference... ", flush=True) # DrawGeometricPhantomImageFilter dq = rtk.DrawGeometricPhantomImageFilter[OutputImageType, OutputImageType].New() dq.SetInput(constant_image_source.GetOutput()) dq.SetPhantomScale(scale) dq.SetOriginOffset(offset) dq.SetRotationMatrix(rot) dq.SetConfigFile(args_info.phantomfile) dq.Update() # Add noise output = dq.GetOutput() if args_info.noise: noisy = rtk.AdditiveGaussianNoiseImageFilter[OutputImageType].New() noisy.SetInput(output) noisy.SetMean(0.0) noisy.SetStandardDeviation(args_info.noise) noisy.Update() output = noisy.GetOutput() # Write if args_info.verbose: print("Writing reference... ", flush=True) itk.imwrite(output, args_info.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkdrawshepploganphantom/CMakeLists.txt ================================================ WRAP_GGO(rtkdrawshepploganphantom_GGO_C rtkdrawshepploganphantom.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkdrawshepploganphantom rtkdrawshepploganphantom.cxx ${rtkdrawshepploganphantom_GGO_C}) target_link_libraries(rtkdrawshepploganphantom RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkdrawshepploganphantom) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkdrawshepploganphantom/rtkdrawshepploganphantom.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkdrawshepploganphantom_ggo.h" #include "rtkGgoFunctions.h" #include "rtkSheppLoganPhantomFilter.h" #include "rtkDrawSheppLoganFilter.h" #include "rtkConstantImageSource.h" #include "rtkAdditiveGaussianNoiseImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtkdrawshepploganphantom, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Create a stack of empty projection images using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // Create a reference object (in this case a 3D phantom reference). using DSLType = rtk::DrawSheppLoganFilter; DSLType::VectorType offset(0.); DSLType::VectorType scale; for (unsigned int i = 0; i < std::min(args_info.offset_given, Dimension); i++) offset[i] = args_info.offset_arg[i]; scale.Fill(args_info.phantomscale_arg[0]); for (unsigned int i = 0; i < std::min(args_info.phantomscale_given, Dimension); i++) scale[i] = args_info.phantomscale_arg[i]; auto dsl = DSLType::New(); dsl->SetPhantomScale(scale); dsl->SetInput(constantImageSource->GetOutput()); dsl->SetOriginOffset(offset); TRY_AND_EXIT_ON_ITK_EXCEPTION(dsl->Update()) // Add noise OutputImageType::Pointer output = dsl->GetOutput(); if (args_info.noise_given) { auto noisy = rtk::AdditiveGaussianNoiseImageFilter::New(); noisy->SetInput(output); noisy->SetMean(0.0); noisy->SetStandardDeviation(args_info.noise_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(noisy->Update()) output = noisy->GetOutput(); } // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(output, args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkdrawshepploganphantom/rtkdrawshepploganphantom.ggo ================================================ purpose "Computes a 3D voxelized Shepp & Logan phantom with noise [https://www.slaney.org/pct/pct-errata.html]" option "verbose" v "Verbose execution" flag off option "output" o "Output projections file name" string yes option "phantomscale" - "Scaling factor for the phantom dimensions" double multiple no default="128" option "noise" - "Gaussian noise parameter (SD)" double no option "offset" - "3D spatial offset of the phantom center" double multiple no ================================================ FILE: applications/rtkdrawshepploganphantom/rtkdrawshepploganphantom.py ================================================ #!/usr/bin/env python import argparse import sys import itk from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Computes a 3D voxelized Shepp & Logan phantom with noise [https://www.slaney.org/pct/pct-errata.html]" ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--output", "-o", help="Output projections file name", type=str, required=True ) parser.add_argument( "--phantomscale", help="Scaling factor for the phantom dimensions", type=float, nargs="+", default=[128], ) parser.add_argument("--noise", help="Gaussian noise parameter (SD)", type=float) parser.add_argument( "--offset", help="3D spatial offset of the phantom center", type=float, nargs="+", ) rtk.add_rtk3Doutputimage_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): # Define output pixel type and dimension OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Empty volume image constant_image_source = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constant_image_source, args_info) # Offset, scale, rotation offset = [0.0] * 3 if args_info.offset: if len(args_info.offset) > 3: print("--offset needs up to 3 values", file=sys.stderr) sys.exit(1) offset = args_info.offset scale = [args_info.phantomscale[0]] * Dimension if args_info.phantomscale: if len(args_info.phantomscale) > 3: print("--phantomscale needs up to 3 values", file=sys.stderr) sys.exit(1) for i in range(len(args_info.phantomscale)): scale[i] = args_info.phantomscale[i] # Reference if args_info.verbose: print("Creating reference... ", flush=True) # DrawSheppLoganPhantomImageFilter dsl = rtk.DrawSheppLoganFilter[OutputImageType, OutputImageType].New() dsl.SetPhantomScale(scale) dsl.SetInput(constant_image_source.GetOutput()) dsl.SetOriginOffset(offset) dsl.Update() # Add noise output = dsl.GetOutput() if args_info.noise: noisy = rtk.AdditiveGaussianNoiseImageFilter[OutputImageType].New() noisy.SetInput(output) noisy.SetMean(0.0) noisy.SetStandardDeviation(args_info.noise) noisy.Update() output = noisy.GetOutput() # Write if args_info.verbose: print("Writing reference... ", flush=True) itk.imwrite(output, args_info.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkdualenergyforwardmodel/CMakeLists.txt ================================================ WRAP_GGO(rtkdualenergyforwardmodel_GGO_C rtkdualenergyforwardmodel.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkdualenergyforwardmodel rtkdualenergyforwardmodel.cxx ${rtkdualenergyforwardmodel_GGO_C}) target_link_libraries(rtkdualenergyforwardmodel RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkdualenergyforwardmodel) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkdualenergyforwardmodel/rtkdualenergyforwardmodel.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkdualenergyforwardmodel_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkMacro.h" #include "rtkSpectralForwardModelImageFilter.h" #include "rtkConstantImageSource.h" #include #include int main(int argc, char * argv[]) { GGO(rtkdualenergyforwardmodel, args_info); using PixelValueType = float; constexpr unsigned int Dimension = 3; using DecomposedProjectionType = itk::VectorImage; using DualEnergyProjectionsType = itk::VectorImage; using IncidentSpectrumReaderType = itk::ImageFileReader>; using DetectorResponseImageType = itk::Image; // Read all inputs auto decomposedProjectionReader = itk::ImageFileReader::New(); decomposedProjectionReader->SetFileName(args_info.input_arg); decomposedProjectionReader->Update(); auto incidentSpectrumReaderHighEnergy = IncidentSpectrumReaderType::New(); incidentSpectrumReaderHighEnergy->SetFileName(args_info.high_arg); incidentSpectrumReaderHighEnergy->Update(); auto incidentSpectrumReaderLowEnergy = IncidentSpectrumReaderType::New(); incidentSpectrumReaderLowEnergy->SetFileName(args_info.low_arg); incidentSpectrumReaderLowEnergy->Update(); auto materialAttenuationsReader = itk::ImageFileReader>::New(); materialAttenuationsReader->SetFileName(args_info.attenuations_arg); materialAttenuationsReader->Update(); // Get parameters from the images const unsigned int MaximumEnergy = incidentSpectrumReaderHighEnergy->GetOutput()->GetLargestPossibleRegion().GetSize(0); // If the detector response is given by the user, use it. Otherwise, assume it is included in the // incident spectrum, and fill the response with ones auto detectorResponseReader = itk::ImageFileReader::New(); DetectorResponseImageType::Pointer detectorImage; if (args_info.detector_given) { detectorResponseReader->SetFileName(args_info.detector_arg); detectorResponseReader->Update(); detectorImage = detectorResponseReader->GetOutput(); } else { auto detectorSource = rtk::ConstantImageSource::New(); detectorSource->SetSize(itk::MakeSize(1, MaximumEnergy)); detectorSource->SetConstant(1.0); detectorSource->Update(); detectorImage = detectorSource->GetOutput(); } // Generate a set of zero-filled intensity projections auto dualEnergyProjections = DualEnergyProjectionsType::New(); dualEnergyProjections->CopyInformation(decomposedProjectionReader->GetOutput()); dualEnergyProjections->SetVectorLength(2); dualEnergyProjections->Allocate(); // Check that the inputs have the expected size DecomposedProjectionType::IndexType indexDecomp; indexDecomp.Fill(0); if (decomposedProjectionReader->GetOutput()->GetVectorLength() != 2) itkGenericExceptionMacro(<< "Decomposed projections (i.e. initialization data) image has vector length " << decomposedProjectionReader->GetOutput()->GetVectorLength() << ", should be 2"); if (materialAttenuationsReader->GetOutput()->GetLargestPossibleRegion().GetSize()[1] != MaximumEnergy) itkGenericExceptionMacro(<< "Material attenuations image has " << materialAttenuationsReader->GetOutput()->GetLargestPossibleRegion().GetSize()[1] << "energies, should have " << MaximumEnergy); // Create and set the filter auto forward = rtk::SpectralForwardModelImageFilter::New(); forward->SetInputDecomposedProjections(decomposedProjectionReader->GetOutput()); forward->SetInputMeasuredProjections(dualEnergyProjections); forward->SetInputIncidentSpectrum(incidentSpectrumReaderHighEnergy->GetOutput()); forward->SetInputSecondIncidentSpectrum(incidentSpectrumReaderLowEnergy->GetOutput()); forward->SetDetectorResponse(detectorImage); forward->SetMaterialAttenuations(materialAttenuationsReader->GetOutput()); forward->SetIsSpectralCT(false); forward->SetComputeVariances(args_info.variances_given); TRY_AND_EXIT_ON_ITK_EXCEPTION(forward->Update()) // Write output auto writer = itk::ImageFileWriter::New(); writer->SetInput(forward->GetOutput()); writer->SetFileName(args_info.output_arg); writer->Update(); // If requested, write the variances if (args_info.variances_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(forward->GetOutputVariances(), args_info.variances_arg)) } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkdualenergyforwardmodel/rtkdualenergyforwardmodel.ggo ================================================ purpose "Computes expected photon counts from incident spectrum, material attenuations, detector response and material-decomposed projections" option "verbose" v "Verbose execution" flag off option "output" o "Output file name (high and low energy projections)" string yes option "input" i "Material-decomposed projections" string yes option "high" - "Incident spectrum image, high energy" string yes option "low" - "Incident spectrum image, low energy" string yes option "detector" d "Detector response file" string no option "attenuations" a "Material attenuations file" string yes option "variances" - "Output variances of measured energies, file name" string no ================================================ FILE: applications/rtkdualenergyforwardmodel/rtkdualenergyforwardmodel.py ================================================ #!/usr/bin/env python import argparse import sys import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Computes expected photon counts from incident spectrum, material attenuations, detector response and material-decomposed projections" ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--output", "-o", help="Output file name (high and low energy projections)", type=str, required=True ) parser.add_argument( "--input", "-i", help="Material-decomposed projections", type=str, required=True ) parser.add_argument( "--high", help="Incident spectrum image, high energy", type=str, required=True ) parser.add_argument( "--low", help="Incident spectrum image, low energy", type=str, required=True ) parser.add_argument( "--detector", "-d", help="Detector response file", type=str ) parser.add_argument( "--attenuations", "-a", help="Material attenuations file", type=str, required=True ) parser.add_argument( "--variances", help="Output variances of measured energies, file name", type=str ) return parser def process(args_info: argparse.Namespace): # Define pixel type and dimension PixelValueType = itk.F Dimension = 3 DecomposedProjectionType = itk.VectorImage[PixelValueType, Dimension] DualEnergyProjectionsType = itk.VectorImage[PixelValueType, Dimension] IncidentSpectrumImageType = itk.Image[PixelValueType, Dimension] DetectorResponseImageType = itk.Image[PixelValueType, Dimension - 1] MaterialAttenuationsImageType = itk.Image[PixelValueType, Dimension - 1] # Read all inputs if args_info.verbose: print("Reading material-decomposed projections...") decomposed_projection_reader = itk.ImageFileReader[DecomposedProjectionType].New() decomposed_projection_reader.SetFileName(args_info.input) decomposed_projection_reader.Update() if args_info.verbose: print("Reading incident spectrum (high energy)...") incident_spectrum_reader_high_energy = itk.ImageFileReader[IncidentSpectrumImageType].New() incident_spectrum_reader_high_energy.SetFileName(args_info.high) incident_spectrum_reader_high_energy.Update() if args_info.verbose: print("Reading incident spectrum (low energy)...") incident_spectrum_reader_low_energy = itk.ImageFileReader[IncidentSpectrumImageType].New() incident_spectrum_reader_low_energy.SetFileName(args_info.low) incident_spectrum_reader_low_energy.Update() if args_info.verbose: print("Reading material attenuations...") material_attenuations_reader = itk.ImageFileReader[MaterialAttenuationsImageType].New() material_attenuations_reader.SetFileName(args_info.attenuations) material_attenuations_reader.Update() # Get parameters from the images maximum_energy = incident_spectrum_reader_high_energy.GetOutput().GetLargestPossibleRegion().GetSize(0) # If the detector response is given by the user, use it. Otherwise, assume it is included in the # incident spectrum, and fill the response with ones if args_info.detector: if args_info.verbose: print("Reading detector response...") detector_response_reader = itk.ImageFileReader[DetectorResponseImageType].New() detector_response_reader.SetFileName(args_info.detector) detector_response_reader.Update() detector_image = detector_response_reader.GetOutput() else: if args_info.verbose: print("No detector response provided, using default (ones)...") detector_source = rtk.ConstantImageSource[DetectorResponseImageType].New() detector_source.SetSize([1, maximum_energy]) detector_source.SetConstant(1.0) detector_source.Update() detector_image = detector_source.GetOutput() # Generate a set of zero-filled intensity projections dual_energy_projections = DualEnergyProjectionsType.New() dual_energy_projections.CopyInformation(decomposed_projection_reader.GetOutput()) dual_energy_projections.SetVectorLength(2) dual_energy_projections.Allocate() # Check that the inputs have the expected size if decomposed_projection_reader.GetOutput().GetVectorLength() != 2: print(f"Error: Decomposed projections image has vector length {decomposed_projection_reader.GetOutput().GetVectorLength()}, should be 2") sys.exit(1) if material_attenuations_reader.GetOutput().GetLargestPossibleRegion().GetSize()[1] != maximum_energy: print(f"Error: Material attenuations image has {material_attenuations_reader.GetOutput().GetLargestPossibleRegion().GetSize()[1]} energies, should have {maximum_energy}") sys.exit(1) # Create and set the filter if args_info.verbose: print("Setting up spectral forward model filter...") forward = rtk.SpectralForwardModelImageFilter[DecomposedProjectionType, DualEnergyProjectionsType].New() forward.SetInputDecomposedProjections(decomposed_projection_reader.GetOutput()) forward.SetInputMeasuredProjections(dual_energy_projections) forward.SetInputIncidentSpectrum(incident_spectrum_reader_high_energy.GetOutput()) forward.SetInputSecondIncidentSpectrum(incident_spectrum_reader_low_energy.GetOutput()) forward.SetDetectorResponse(detector_image) forward.SetMaterialAttenuations(material_attenuations_reader.GetOutput()) forward.SetIsSpectralCT(False) forward.SetComputeVariances(args_info.variances is not None) forward.Update() # Write output if args_info.verbose: print(f"Writing output to {args_info.output}...") writer = itk.ImageFileWriter[DualEnergyProjectionsType].New() writer.SetInput(forward.GetOutput()) writer.SetFileName(args_info.output) writer.Update() # If requested, write the variances if args_info.variances: if args_info.verbose: print(f"Writing variances to {args_info.variances}...") itk.imwrite(forward.GetOutputVariances(), args_info.variances) if args_info.verbose: print("Processing completed successfully.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkdualenergysimplexdecomposition/CMakeLists.txt ================================================ WRAP_GGO(rtkdualenergysimplexdecomposition_GGO_C rtkdualenergysimplexdecomposition.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkdualenergysimplexdecomposition rtkdualenergysimplexdecomposition.cxx ${rtkdualenergysimplexdecomposition_GGO_C}) target_link_libraries(rtkdualenergysimplexdecomposition RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkdualenergysimplexdecomposition) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkdualenergysimplexdecomposition/rtkdualenergysimplexdecomposition.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkdualenergysimplexdecomposition_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkMacro.h" #include "rtkSimplexSpectralProjectionsDecompositionImageFilter.h" #include #include #include int main(int argc, char * argv[]) { GGO(rtkdualenergysimplexdecomposition, args_info); using PixelValueType = float; constexpr unsigned int Dimension = 3; using DecomposedProjectionType = itk::VectorImage; using DualEnergyProjectionsType = itk::VectorImage; using IncidentSpectrumImageType = itk::Image; using IncidentSpectrumReaderType = itk::ImageFileReader; using DetectorResponseImageType = itk::Image; using MaterialAttenuationsImageType = itk::Image; // Read all inputs auto decomposedProjectionReader = itk::ImageFileReader::New(); decomposedProjectionReader->SetFileName(args_info.input_arg); decomposedProjectionReader->Update(); auto dualEnergyProjectionReader = itk::ImageFileReader::New(); dualEnergyProjectionReader->SetFileName(args_info.dual_arg); dualEnergyProjectionReader->Update(); auto incidentSpectrumReaderHighEnergy = IncidentSpectrumReaderType::New(); incidentSpectrumReaderHighEnergy->SetFileName(args_info.high_arg); incidentSpectrumReaderHighEnergy->Update(); auto incidentSpectrumReaderLowEnergy = IncidentSpectrumReaderType::New(); incidentSpectrumReaderLowEnergy->SetFileName(args_info.low_arg); incidentSpectrumReaderLowEnergy->Update(); auto materialAttenuationsReader = itk::ImageFileReader::New(); materialAttenuationsReader->SetFileName(args_info.attenuations_arg); materialAttenuationsReader->Update(); // Get parameters from the images const unsigned int MaximumEnergy = incidentSpectrumReaderHighEnergy->GetOutput()->GetLargestPossibleRegion().GetSize(0); // If the detector response is given by the user, use it. Otherwise, assume it is included in the // incident spectrum, and fill the response with ones auto detectorResponseReader = itk::ImageFileReader::New(); DetectorResponseImageType::Pointer detectorImage; if (args_info.detector_given) { detectorResponseReader->SetFileName(args_info.detector_arg); detectorResponseReader->Update(); detectorImage = detectorResponseReader->GetOutput(); } else { auto detectorSource = rtk::ConstantImageSource::New(); detectorSource->SetSize(itk::MakeSize(1, MaximumEnergy)); detectorSource->SetConstant(1.0); detectorSource->Update(); detectorImage = detectorSource->GetOutput(); } if (detectorImage->GetLargestPossibleRegion().GetSize()[1] != MaximumEnergy) itkGenericExceptionMacro(<< "Detector response image has " << detectorImage->GetLargestPossibleRegion().GetSize()[1] << "energies, should have " << MaximumEnergy); // Create and set the filter auto simplex = rtk::SimplexSpectralProjectionsDecompositionImageFilter::New(); simplex->SetInputDecomposedProjections(decomposedProjectionReader->GetOutput()); simplex->SetGuessInitialization(args_info.guess_flag); simplex->SetInputMeasuredProjections(dualEnergyProjectionReader->GetOutput()); simplex->SetInputIncidentSpectrum(incidentSpectrumReaderHighEnergy->GetOutput()); simplex->SetInputSecondIncidentSpectrum(incidentSpectrumReaderLowEnergy->GetOutput()); simplex->SetDetectorResponse(detectorImage); simplex->SetMaterialAttenuations(materialAttenuationsReader->GetOutput()); simplex->SetNumberOfIterations(args_info.niterations_arg); simplex->SetOptimizeWithRestarts(args_info.restarts_flag); simplex->SetIsSpectralCT(false); TRY_AND_EXIT_ON_ITK_EXCEPTION(simplex->Update()) // Write outputs auto writer = itk::ImageFileWriter::New(); writer->SetInput(simplex->GetOutput(0)); writer->SetFileName(args_info.output_arg); writer->Update(); return EXIT_SUCCESS; } ================================================ FILE: applications/rtkdualenergysimplexdecomposition/rtkdualenergysimplexdecomposition.ggo ================================================ purpose "Decomposes dual energy projections into materials" option "verbose" v "Verbose execution" flag off option "input" i "Initial solution for material decomposition, file name" string yes option "output" o "Output file name (decomposed projections)" string yes option "dual" - "Projections to be decomposed, VectorImage file name" string yes option "detector" d "Detector response file" string no option "high" - "High energy incident spectrum file" string yes option "low" - "Low energy incident spectrum file" string yes option "attenuations" a "Material attenuations file" string yes option "niterations" n "Number of iterations" int no default="300" option "weightsmap" w "File name for the output weights map (inverse noise variance)" string no option "restarts" r "Allow random restarts during optimization" flag off option "guess" g "Ignore values in input and initialize the simplex with a simple heuristic instead" flag off ================================================ FILE: applications/rtkdualenergysimplexdecomposition/rtkdualenergysimplexdecomposition.py ================================================ #!/usr/bin/env python import argparse import sys import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Decomposes dual energy projections into materials" ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--input", "-i", help="Initial solution for material decomposition, file name", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output file name (decomposed projections)", type=str, required=True ) parser.add_argument( "--dual", help="Projections to be decomposed, VectorImage file name", type=str, required=True ) parser.add_argument( "--detector", "-d", help="Detector response file", type=str ) parser.add_argument( "--high", help="High energy incident spectrum file", type=str, required=True ) parser.add_argument( "--low", help="Low energy incident spectrum file", type=str, required=True ) parser.add_argument( "--attenuations", "-a", help="Material attenuations file", type=str, required=True ) parser.add_argument( "--niterations", "-n", help="Number of iterations", type=int, default=300 ) parser.add_argument( "--weightsmap", "-w", help="File name for the output weights map (inverse noise variance)", type=str ) parser.add_argument( "--restarts", "-r", help="Allow random restarts during optimization", action="store_true" ) parser.add_argument( "--guess", "-g", help="Ignore values in input and initialize the simplex with a simple heuristic instead", action="store_true" ) return parser def process(args_info: argparse.Namespace): # Define pixel type and dimension PixelValueType = itk.F Dimension = 3 DecomposedProjectionType = itk.VectorImage[PixelValueType, Dimension] DualEnergyProjectionsType = itk.VectorImage[PixelValueType, Dimension] IncidentSpectrumImageType = itk.Image[PixelValueType, Dimension] DetectorResponseImageType = itk.Image[PixelValueType, Dimension - 1] MaterialAttenuationsImageType = itk.Image[PixelValueType, Dimension - 1] # Read all inputs if args_info.verbose: print("Reading initial solution for material decomposition...") decomposed_projection_reader = itk.ImageFileReader[DecomposedProjectionType].New() decomposed_projection_reader.SetFileName(args_info.input) decomposed_projection_reader.Update() if args_info.verbose: print("Reading dual energy projections...") dual_energy_projection_reader = itk.ImageFileReader[DualEnergyProjectionsType].New() dual_energy_projection_reader.SetFileName(args_info.dual) dual_energy_projection_reader.Update() if args_info.verbose: print("Reading incident spectrum (high energy)...") incident_spectrum_reader_high_energy = itk.ImageFileReader[IncidentSpectrumImageType].New() incident_spectrum_reader_high_energy.SetFileName(args_info.high) incident_spectrum_reader_high_energy.Update() if args_info.verbose: print("Reading incident spectrum (low energy)...") incident_spectrum_reader_low_energy = itk.ImageFileReader[IncidentSpectrumImageType].New() incident_spectrum_reader_low_energy.SetFileName(args_info.low) incident_spectrum_reader_low_energy.Update() if args_info.verbose: print("Reading material attenuations...") material_attenuations_reader = itk.ImageFileReader[MaterialAttenuationsImageType].New() material_attenuations_reader.SetFileName(args_info.attenuations) material_attenuations_reader.Update() # Get parameters from the images maximum_energy = incident_spectrum_reader_high_energy.GetOutput().GetLargestPossibleRegion().GetSize(0) # If the detector response is given by the user, use it. Otherwise, assume it is included in the # incident spectrum, and fill the response with ones if args_info.detector: if args_info.verbose: print("Reading detector response...") detector_response_reader = itk.ImageFileReader[DetectorResponseImageType].New() detector_response_reader.SetFileName(args_info.detector) detector_response_reader.Update() detector_image = detector_response_reader.GetOutput() else: if args_info.verbose: print("No detector response provided, using default (ones)...") detector_source = rtk.ConstantImageSource[DetectorResponseImageType].New() detector_source.SetSize([1, maximum_energy]) detector_source.SetConstant(1.0) detector_source.Update() detector_image = detector_source.GetOutput() # Validate detector response size if detector_image.GetLargestPossibleRegion().GetSize()[1] != maximum_energy: print(f"Error: Detector response image has {detector_image.GetLargestPossibleRegion().GetSize()[1]} energies, should have {maximum_energy}") sys.exit(1) # Create and set the filter if args_info.verbose: print("Setting up simplex spectral projections decomposition filter...") simplex = rtk.SimplexSpectralProjectionsDecompositionImageFilter[ DecomposedProjectionType, DualEnergyProjectionsType, IncidentSpectrumImageType, DetectorResponseImageType, MaterialAttenuationsImageType ].New() simplex.SetInputDecomposedProjections(decomposed_projection_reader.GetOutput()) simplex.SetGuessInitialization(args_info.guess) simplex.SetInputMeasuredProjections(dual_energy_projection_reader.GetOutput()) simplex.SetInputIncidentSpectrum(incident_spectrum_reader_high_energy.GetOutput()) simplex.SetInputSecondIncidentSpectrum(incident_spectrum_reader_low_energy.GetOutput()) simplex.SetDetectorResponse(detector_image) simplex.SetMaterialAttenuations(material_attenuations_reader.GetOutput()) simplex.SetNumberOfIterations(args_info.niterations) simplex.SetOptimizeWithRestarts(args_info.restarts) simplex.SetIsSpectralCT(False) simplex.Update() # Write output if args_info.verbose: print(f"Writing decomposed projections to {args_info.output}...") writer = itk.ImageFileWriter[DecomposedProjectionType].New() writer.SetInput(simplex.GetOutput(0)) writer.SetFileName(args_info.output) writer.Update() # If requested, write the weights map if args_info.weightsmap: if args_info.verbose: print(f"Writing weights map to {args_info.weightsmap}...") # Note: The weights map output might need to be accessed differently # depending on the actual filter implementation weights_writer = itk.ImageFileWriter[DecomposedProjectionType].New() weights_writer.SetInput(simplex.GetOutputWeights()) weights_writer.SetFileName(args_info.weightsmap) weights_writer.Update() if args_info.verbose: print("Decomposition completed successfully.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkelektasynergygeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkelektasynergygeometry_GGO_C rtkelektasynergygeometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkelektasynergygeometry rtkelektasynergygeometry.cxx ${rtkelektasynergygeometry_GGO_C}) target_link_libraries(rtkelektasynergygeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkelektasynergygeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkelektasynergygeometry/README.md ================================================ # Elekta Reconstruction Elekta provides easy access to raw data. The data and projection images are stored in a single directory which is user-configurable. The default location is `D:\db`. In this folder, there is a database in DBase format. Each table is contained in a `.DBF` file. RTK needs the `IMAGE.DBF` and `FRAME.DBF` tables. Patient data are stored in individual folders. By default, the name of each patient folder is `patient_ID` where `ID` is the patient ID. In these folders, one can access the planning CT in the `CT_SET` subfolder and the cone-beam projections in `IMAGES/img_DICOM_UID` subfolders, where `DICOM_UID` is the DICOM UID of the acquisition. The projection images are `.his` files. The reconstructed images are the `IMAGES/img_DICOM_UID/Reconstruction/*SCAN` files. ## Reconstruction Steps The first step before proceeding with the reconstruction is to convert Elekta's database information into an RTK geometry file using a command-line tool. Follow these steps: ### 1. Download Elekta Dataset Download the dataset from [Elekta-data](https://data.kitware.com/api/v1/item/5be973478d777f2179a26e1c/download). ### 2. Convert Geometry Run the application to convert Elekta's geometry into RTK's format (DICOM_UID is contained in the subfolder name of the `.his` files): ```bash rtkelektasynergygeometry \ --image_db IMAGE.DBF \ --frame_db FRAME.DBF \ --dicom_uid 1.3.46.423632.135428.1351013645.166 \ -o elektaGeometry ``` Since XVI v5, the geometry is contained in a separate `_Frames.xml` file, which can be used as follows: ```bash rtkelektasynergygeometry \ --xml _Frames.xml \ -o elektaGeometry ``` An example of such a file is available in the test data [here](https://data.kitware.com/api/v1/item/5b179c898d777f15ebe201fd/download). ### 3. Reconstruct Using RTK Applications Use the `rtkfdk` algorithm to reconstruct a single axial slice (e.g., slice 29.5) of the volume: ```bash rtkfdk \ --lowmem \ --geometry elektaGeometry \ --path img_1.3.46.423632.135428.1351013645.166/ \ --regexp '.*.his' \ --output slice29.5.mha \ --verbose \ --spacing 0.25,0.25,0.25 \ --size 1024,1,1024 \ --origin -127.875,29.5,-127.875 ``` ### 4. Apply the FOV Filter Apply the field-of-view (FOV) filter to mask out everything outside the FOV: ```bash rtkfieldofview \ --geometry elektaGeometry \ --path img_1.3.46.423632.135428.1351013645.166/ \ --regexp '.*.his' \ --reconstruction slice29.5.mha \ --output slice29.5.mha \ --verbose ``` ### 5. Visualize the Result You can visualize the result using a viewer (e.g., VV). The resulting image should look like the following: ![Elekta.jpg](../../documentation/docs/ExternalData/Elekta.png){w=400px alt="Elekta snapshot"} ================================================ FILE: applications/rtkelektasynergygeometry/rtkelektasynergygeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkelektasynergygeometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkElektaSynergyGeometryReader.h" #include "rtkElektaXVI5GeometryXMLFileReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" int main(int argc, char * argv[]) { GGO(rtkelektasynergygeometry, args_info); if (args_info.image_db_given && args_info.frame_db_given && args_info.dicom_uid_given && !args_info.xml_given) { // Create geometry reader auto reader = rtk::ElektaSynergyGeometryReader::New(); reader->SetDicomUID(args_info.dicom_uid_arg); reader->SetImageDbfFileName(args_info.image_db_arg); reader->SetFrameDbfFileName(args_info.frame_db_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(reader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } else if (!args_info.image_db_given && !args_info.frame_db_given && !args_info.dicom_uid_given && args_info.xml_given) { // Create geometry reader rtk::ElektaXVI5GeometryXMLFileReader::Pointer geometryReader; geometryReader = rtk::ElektaXVI5GeometryXMLFileReader::New(); geometryReader->SetFilename(args_info.xml_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(geometryReader->GenerateOutputInformation()); // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(geometryReader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } std::cerr << "You must either provide image_db, frame_db and dicom_uid" << "for versions up to v4 or xml starting with v5." << std::endl; return EXIT_FAILURE; } ================================================ FILE: applications/rtkelektasynergygeometry/rtkelektasynergygeometry.ggo ================================================ purpose "Creates an RTK geometry file from an Elekta Synergy acquisition." option "verbose" v "Verbose execution" flag off option "image_db" i "Image table filename (prior to XVI5)" string no option "frame_db" f "Frame table filename (prior to XVI5)" string no option "xml" x "XML file name (starting with XVI5)" string no option "dicom_uid" u "Dicom uid of the acquisition" string no option "output" o "Output file name" string yes ================================================ FILE: applications/rtkelektasynergygeometry/rtkelektasynergygeometry.py ================================================ #!/usr/bin/env python import argparse from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Creates an RTK geometry file from an Elekta Synergy acquisition." ) parser.add_argument("--verbose", "-v", help="Verbose execution", type=bool) parser.add_argument( "--xml", "-x", help="XML file name (starting with XVI5)", required=True ) parser.add_argument("--output", "-o", help="Output file name", required=True) return parser def process(args_info: argparse.Namespace): reader = rtk.ElektaXVI5GeometryXMLFileReader.New() reader.SetFilename(args_info.xml) reader.GenerateOutputInformation() rtk.write_geometry(reader.GetGeometry(), args_info.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkextractphasesignal/CMakeLists.txt ================================================ WRAP_GGO(rtkextractphasesignal_GGO_C rtkextractphasesignal.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkextractphasesignal rtkextractphasesignal.cxx ${rtkextractphasesignal_GGO_C}) target_link_libraries(rtkextractphasesignal RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkextractphasesignal) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkextractphasesignal/rtkextractphasesignal.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkextractphasesignal_ggo.h" #include "rtkMacro.h" #include "rtkExtractPhaseImageFilter.h" #include #include namespace rtk { template void WriteSignalToTextFile(TSignalType * sig, const std::string & fileName) { std::ofstream ofs(fileName.c_str()); itk::ImageRegionConstIterator it(sig, sig->GetLargestPossibleRegion()); for (it.GoToBegin(); !it.IsAtEnd(); ++it) { ofs << it.Get() << std::endl; } ofs.close(); } } // namespace rtk int main(int argc, char * argv[]) { GGO(rtkextractphasesignal, args_info); constexpr unsigned int Dimension = 1; using ImageType = itk::Image; // Read ImageType::Pointer signal; signal = itk::ReadImage(args_info.input_arg); // Process phase signal if required using PhaseFilter = rtk::ExtractPhaseImageFilter; auto phase = PhaseFilter::New(); phase->SetInput(signal); phase->SetMovingAverageSize(args_info.movavg_arg); phase->SetUnsharpMaskSize(args_info.unsharp_arg); phase->SetModel((PhaseFilter::ModelType)args_info.model_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(phase->Update()) // Write output phase rtk::WriteSignalToTextFile(phase->GetOutput(), args_info.output_arg); return EXIT_SUCCESS; } ================================================ FILE: applications/rtkextractphasesignal/rtkextractphasesignal.ggo ================================================ purpose "Extracts the phase from a signal." option "verbose" v "Verbose execution" flag off option "input" i "Input signal" string yes option "output" o "Output file name for the Hilbert phase signal" string yes option "movavg" - "Moving average size applied before phase extraction" int default="1" no option "unsharp" - "Unsharp mask size applied before phase extraction" int default="55" no option "model" - "Phase model" values="LOCAL_PHASE","LINEAR_BETWEEN_MINIMA","LINEAR_BETWEEN_MAXIMA" enum no default="LINEAR_BETWEEN_MINIMA" ================================================ FILE: applications/rtkextractphasesignal/rtkextractphasesignal.py ================================================ #!/usr/bin/env python import argparse import itk from itk import RTK as rtk def write_signal_to_text_file(signal_image, filename): # Convert ITK image to numpy array signal_array = itk.array_from_image(signal_image) # Write to text file with open(filename, 'w') as f: for value in signal_array.flatten(): f.write(f"{value}\n") def build_parser(): parser = rtk.RTKArgumentParser( description="Extracts the phase from a signal." ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--input", "-i", help="Input signal", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output file name for the Hilbert phase signal", type=str, required=True ) parser.add_argument( "--movavg", help="Moving average size applied before phase extraction", type=int, default=1 ) parser.add_argument( "--unsharp", help="Unsharp mask size applied before phase extraction", type=int, default=55 ) parser.add_argument( "--model", help="Phase model", choices=["LOCAL_PHASE", "LINEAR_BETWEEN_MINIMA", "LINEAR_BETWEEN_MAXIMA"], default="LINEAR_BETWEEN_MINIMA" ) return parser def process(args_info: argparse.Namespace): # Define pixel type and dimension PixelType = itk.D # double Dimension = 1 ImageType = itk.Image[PixelType, Dimension] # Read input signal if args_info.verbose: print(f"Reading input signal from {args_info.input}...") reader = itk.ImageFileReader[ImageType].New() reader.SetFileName(args_info.input) reader.Update() signal = reader.GetOutput() # Process phase signal if required PhaseFilter = rtk.ExtractPhaseImageFilter[ImageType] phase = PhaseFilter.New() phase.SetInput(signal) phase.SetMovingAverageSize(args_info.movavg) phase.SetUnsharpMaskSize(args_info.unsharp) # Map string to enum integer value model_values = { "LOCAL_PHASE": 0, "LINEAR_BETWEEN_MINIMA": 1, "LINEAR_BETWEEN_MAXIMA": 2 } phase.SetModel(model_values[args_info.model]) # Write output phase signal if args_info.verbose: print(f"Writing phase signal to {args_info.output}...") write_signal_to_text_file(phase.GetOutput(), args_info.output) if args_info.verbose: print("Phase extraction completed successfully.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkextractshroudsignal/CMakeLists.txt ================================================ WRAP_GGO(rtkextractshroudsignal_GGO_C rtkextractshroudsignal.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkextractshroudsignal rtkextractshroudsignal.cxx ${rtkextractshroudsignal_GGO_C}) target_link_libraries(rtkextractshroudsignal RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkextractshroudsignal) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkextractshroudsignal/rtkextractshroudsignal.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkextractshroudsignal_ggo.h" #include "rtkMacro.h" #include "rtkDPExtractShroudSignalImageFilter.h" #include "rtkReg1DExtractShroudSignalImageFilter.h" #include "rtkExtractPhaseImageFilter.h" #include #include namespace rtk { template void WriteSignalToTextFile(TSignalType * sig, const std::string & fileName) { std::ofstream ofs(fileName.c_str()); itk::ImageRegionConstIterator it(sig, sig->GetLargestPossibleRegion()); for (it.GoToBegin(); !it.IsAtEnd(); ++it) { ofs << it.Get() << std::endl; } ofs.close(); } } // namespace rtk int main(int argc, char * argv[]) { GGO(rtkextractshroudsignal, args_info); using InputPixelType = double; using OutputPixelType = double; constexpr unsigned int Dimension = 2; using InputImageType = itk::Image; using OutputImageType = itk::Image; // Extract shroud signal OutputImageType::Pointer shroudSignal; if (std::string(args_info.method_arg) == "DynamicProgramming") { if (!args_info.amplitude_given) { std::cerr << "You must supply a maximum amplitude to look for." << std::endl; return 1; } using shroudFilterType = rtk::DPExtractShroudSignalImageFilter; auto shroudFilter = shroudFilterType::New(); shroudFilter->SetInput(itk::ReadImage(args_info.input_arg)); shroudFilter->SetAmplitude(args_info.amplitude_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(shroudFilter->Update()) shroudSignal = shroudFilter->GetOutput(); } else if (std::string(args_info.method_arg) == "Reg1D") { using shroudFilterType = rtk::Reg1DExtractShroudSignalImageFilter; auto shroudFilter = shroudFilterType::New(); shroudFilter->SetInput(itk::ReadImage(args_info.input_arg)); TRY_AND_EXIT_ON_ITK_EXCEPTION(shroudFilter->Update()) shroudSignal = shroudFilter->GetOutput(); } else { std::cerr << "The specified method does not exist." << std::endl; return 1; } // Write output signal rtk::WriteSignalToTextFile(shroudSignal.GetPointer(), args_info.output_arg); // Process phase signal if required if (args_info.phase_given) { using PhaseFilter = rtk::ExtractPhaseImageFilter; auto phase = PhaseFilter::New(); phase->SetInput(shroudSignal); phase->SetMovingAverageSize(args_info.movavg_arg); phase->SetUnsharpMaskSize(args_info.unsharp_arg); phase->SetModel((PhaseFilter::ModelType)args_info.model_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(phase->Update()) rtk::WriteSignalToTextFile(phase->GetOutput(), args_info.phase_arg); } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkextractshroudsignal/rtkextractshroudsignal.ggo ================================================ purpose "Extracts the breathing signal from a shroud image." option "verbose" v "Verbose execution" flag off option "input" i "Input shroud image file name" string yes option "amplitude" a "Maximum breathing amplitude explored in mm" double no option "output" o "Output file name" string yes option "method" m "Method to use (Reg1D or DynamicProgramming)" string default="Reg1D" no section "Phase extraction" option "phase" p "Output file name for the Hilbert phase signal" string no option "movavg" - "Moving average size applied before phase extraction" int default="1" no option "unsharp" - "Unsharp mask size applied before phase extraction" int default="55" no option "model" - "Phase model" values="LOCAL_PHASE","LINEAR_BETWEEN_MINIMA","LINEAR_BETWEEN_MAXIMA" enum no default="LINEAR_BETWEEN_MINIMA" ================================================ FILE: applications/rtkfdk/CMakeLists.txt ================================================ WRAP_GGO(rtkfdk_GGO_C rtkfdk.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkfdk rtkfdk.cxx ${rtkfdk_GGO_C}) target_link_libraries(rtkfdk RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkfdk) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkfdk/FDK2D.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # Create projections of the phantom file # Note the sinogram being 3 pixels wide in the y direction to allow back-projection interpolation in a 2D image rtkprojectgeometricphantom -g geometry.xml -o projections.mha --spacing 2 --size=512,3,512 --phantomfile SheppLogan-2d.txt --phantomscale=256,1,256 # Reconstruct rtkfdk -p . -r projections.mha -o fdk.mha -g geometry.xml --spacing 2 --size 256,1,256 # Create a reference volume for comparison rtkdrawgeometricphantom --spacing 2 --size=256,1,256 --phantomfile SheppLogan-2d.txt -o ref.mha --phantomscale=256,1,256 ================================================ FILE: applications/rtkfdk/FDK3D.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # You may add "--arc 200" to make the scan short or "--proj_iso_x 200" to offset the detector # Create projections of the phantom file rtkprojectshepploganphantom -g geometry.xml -o projections.mha --spacing 2 --size 200 # Reconstruct rtkfdk -p . -r projections.mha -o fdk.mha -g geometry.xml --spacing 2 --size 128 # Create a reference volume for comparison rtkdrawshepploganphantom --like fdk.mha -o ref.mha ================================================ FILE: applications/rtkfdk/README.md ================================================ # 3D, 2D and motion-compensated FDK The following exampels illustrates the command line application `rtkfdk` by reconstructing a Shepp Logan phantom with Feldkamp, David and Kress algorithm in 3D (cone-beam) and 2D (fan-beam). `````{tab-set} ````{tab-item} 3D ## 3D ![sin_3D](../../documentation/docs/ExternalData/SheppLogan-Sinogram-3D.png){w=200px alt="SheppLogan sinogram 3D "} ![img_3D](../../documentation/docs/ExternalData/Fdk-3D.png){w=200px alt="Fdk reconstruction 3D"} This script uses the SheppLogan phantom. ```{literalinclude} FDK3D.sh ``` ```` ````{tab-item} 2D ## 2D ![sin_2D](../../documentation/docs/ExternalData/SheppLogan-Sinogram-2D.png){w=200px alt="SheppLogan sinogram 2D"} ![img_2D](../../documentation/docs/ExternalData/Fdk-2D.png){w=200px alt="Fdk reconstruction 2D"} The same reconstruction can be performed using the original 2D Shepp-Logan phantom. RTK can perform 2D reconstructions through images wide of 1 pixel in the y direction. The following script performs the same reconstruction as above in a 2D environment and uses the [2D Shepp-Logan](https://data.kitware.com/api/v1/file/67d1ff45c6dec2fc9c534d0d/download) phantom as input. ```{literalinclude} FDK2D.sh ``` ```` ````{tab-item} Motion compensated ## Motion-compensated reconstruction RTK provides the necessary tools to reconstruct an image with motion compensation. The implementation is based on two articles that we have published ([article 1](https://hal.archives-ouvertes.fr/hal-00443440) and [article 2](https://hal.archives-ouvertes.fr/hal-01967313)) but only the FDK-based motion-compensated CBCT reconstruction (analytic algorithm in article 1) and without optimization (very slow reconstruction compared to article 2). You should read the articles to understand the basics of the algorithm before trying to use the software. The algorithm requires a set of projection images with the associated RTK geometry, the respiratory phase of each projection image and the 4D motion vector field over a respiratory cycle in the cone-beam coordinate system. Each piece of data is described in more details below and can be downloaded using [Girder](https://data.kitware.com/#collection/5a7706878d777f0649e04776). It is assumed that we have a breathing motion that is cyclic and similar to that described by the vector field. Note that you could modify the code and create your own motion model if you want to, in which case you should probably [contact us](http://www.openrtk.org/RTK/project/contactus.html). ### Projection images This example is illustrated with a set of projection images of the [POPI patient](https://github.com/open-vv/popi-model/blob/master/popi-model.md). This dataset has been used in the first previously-mentioned article. You can [download the projections](https://data.kitware.com/api/v1/item/5be99af88d777f2179a2e144/download) and the required tables of the Elekta database, [FRAME.DBF](https://data.kitware.com/api/v1/item/5be99a068d777f2179a2cf4f/download) and [IMAGE.DBF](https://data.kitware.com/api/v1/item/5be99a078d777f2179a2cf65/download). The dataset is first used to reconstruct a blurry image: ```bash # Convert Elekta database to RTK geometry rtkelektasynergygeometry \ -o geometry.rtk \ -f FRAME.DBF \ -i IMAGE.DBF \ -u 1.3.46.423632.141000.1169042526.68 # Reconstruct from all projection images without any motion compensation rtkfdk \ -p . \ -r .*.his \ -o fdk.mha \ -g geometry.rtk \ --hann 0.5 \ --pad 1.0 # Keep only the field-of-view of the image rtkfieldofview \ --reconstruction fdk.mha \ --output fdk.mha \ --geometry geometry.rtk \ --path . \ --regexp '.*.his' ``` You should obtain something like that with [VV](http://vv.creatis.insa-lyon.fr/): ![Blurred](../../documentation/docs/ExternalData/Blurred.jpg){w=600px alt="Blurred image"} ### Deformation vector field The next piece of data is a 4D deformation vector field that describes a respiratory cycle. Typically, it can be obtained from the 4D planning CT with deformable image registration. Here, I have used [Elastix](http://elastix.lumc.nl/) with the [sliding module](http://elastix.lumc.nl/modelzoo/par0016) developed by Vivien Delmon. The registration uses a [patient mask](https://data.kitware.com/api/v1/item/5be99a408d777f2179a2dde8/download) (red+green) and a [motion mask](https://data.kitware.com/api/v1/item/5be99a088d777f2179a2cf6f/download) (red) as described in [Jef's publication](http://www.creatis.insa-lyon.fr/site/fr/publications/VAND-12): ![Mm](../../documentation/docs/ExternalData/MotionMask.jpg){w=400px alt="Motion mask"} The registration can easily be scripted, here with bash, where each phase image of the POPI 4D CT has been stored in files 00.mhd to 50.mhd: ```bash for i in $(seq -w 0 10 90) do mkdir $i elastix -f 50.mhd \ -m $i.mhd \ -out $i \ -labels mm_50.mha \ -fMask patient_50.mha \ -p Par0016.multibsplines.lung.sliding.txt done ``` Deformable Image Registration is a complex and long process so you will have to be patient here. Note that the reference frame is phase 50% and it is registered to each phase from 0% to 90%. One subtle step is that the vector field is a displacement vector field, i.e., each vector is the local displacement of the point at its location. Since I ran the registration on the 4D planning CT, the coordinate system is not that of the cone-beam CT. In order to produce the vector field in the cone-beam coordinate system, I have used the following bash script that combines transformix and several "clitk" tools that are provided along with VV: ```bash # Create 4x4 matrix that describes the CT to CBCT change of coordinate system. # This matrix is a combination of the knowledge of the isocenter position / axes orientation # and a rigid alignment that has been performed with Elastix echo "-0.0220916855767852 0.9996655273534405 -0.0134458487848415 -83.6625731437426197" >CT_CBCT.mat echo " 0.0150924269790251 -0.0131141301144939 -0.9998000991394341 -4.0763571826687057" >>CT_CBCT.mat echo " 0.9996420239647088 0.0222901999207823 0.0147976657359281 77.8903364738220034" >>CT_CBCT.mat echo " 0.0000000000000000 0.0000000000000000 0.0000000000000000 1.0000000000000000" >>CT_CBCT.mat # Transform 4x4 matrix that describes the transformation # from planning CT to CBCT to a vector field clitkMatrixTransformToVF --like 50.mhd \ --matrix CT_CBCT.mat \ --output CT_CBCT.mha # Inverse transformation. Also remove upper slices that are outside the # planning CT CBCT_CT.mat is the inverse of CT_CBCT.mha clitkMatrixInverse -i CT_CBCT.mat \ -o CBCT_CT.mat clitkMatrixTransformToVF --origin -127.5,-107.5,-127.5 \ --spacing 1,1,1 \ --size 256,236,256 \ --matrix CBCT_CT.mat \ --output CBCT_CT.mha # Go over each elastix output file, generate the vector field with # transformix and compose with the two rigid vector fields for i in $(seq -w 0 10 90) do transformix -in 50.mhd \ -out $i \ -tp $i/TransformParameters.0.txt \ -def all -threads 16 clitkComposeVF --input1 CBCT_CT.mha \ --input2 $i/deformationField.mhd \ --output $i/deformationField.mhd clitkComposeVF --input1 $i/deformationField.mhd \ --input2 CT_CBCT.mha \ --output $i/deformationField.mhd done ``` This is a bit complicated and there are probably other ways of doing this. For example, Vivien has resampled the planning CT frames on the CBCT coordinate system before doing the registrations, in which case you do not need to do all this. Just pick one of your choice but motion-compensated CBCT reconstruction requires a 4D vector field that is nicely displayed on top of a CBCT image, for example the fdk.mha that has been produced in the first step (the vector field is downsampled and displayed with VV): ![Vf](../../documentation/docs/ExternalData/VectorField.gif){w=400px alt="Vector field"} The elastix output files and the transformed 4D DVF are available [here](https://data.kitware.com/api/v1/item/5be99a058d777f2179a2cf42/download). ### Respiratory signal The motion model requires that we associate each projection image with one frame of the 4D vector field. We used the Amsterdam shroud solution of Lambert Zijp (described [here](http://www.creatis.insa-lyon.fr/site/fr/publications/RIT-12a)) which is implemented in RTK ```bash rtkamsterdamshroud --path . \ --regexp '.*.his' \ --output shroud.mha \ --unsharp 650 rtkextractshroudsignal --input shroud.mha \ --output signal.txt ``` Post-process with Matlab to obtain the phase signal, ensuring the phase ranges from 0 to 1 (e.g., 0.3 corresponds to 30% of the respiratory cycle). The resulting phase is visualized [here](https://data.kitware.com/api/v1/item/5be99af98d777f2179a2e160/download): ![Signal](../../documentation/docs/ExternalData/Signal.jpg){w=800px alt="Phase signal"} --- ### Motion-compensated cone-beam CT reconstruction Gather all the pieces to perform motion-compensated reconstruction. Use the following commands: Reconstruct with Motion Compensation : ```bash rtkfdk \ -p . \ -r .*.his \ -o fdk.mha \ -g geometry.rtk \ --hann 0.5 \ --pad 1.0 \ --signal sphase.txt \ --dvf deformationField_4D.mhd ``` Apply the Field-of-View Filter : ```bash rtkfieldofview \ --reconstruction fdk.mha \ --output fdk.mha \ --geometry geometry.rtk \ --path . \ --regexp '.*.his' ``` Toggle between uncorrected and motion-compensated reconstruction to appreciate the improvement: ![Blurred vs mc.gif](../../documentation/docs/ExternalData/Blurred_vs_mc.gif){w=400 alt="blurred vs motion compensation image"} The 4D vector field is constructed with phase 50% as a reference. Modify the reference image to reconstruct other phases, such as the time-average position. ```` ````` ================================================ FILE: applications/rtkfdk/rtkfdk.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkfdk_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkDisplacedDetectorForOffsetFieldOfViewImageFilter.h" #include "rtkParkerShortScanImageFilter.h" #include "rtkFDKConeBeamReconstructionFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaDisplacedDetectorImageFilter.h" // TODO # include "rtkCudaDisplacedDetectorForOffsetFieldOfViewImageFilter.h" # include "rtkCudaParkerShortScanImageFilter.h" # include "rtkCudaFDKConeBeamReconstructionFilter.h" #endif #include "rtkFDKWarpBackProjectionImageFilter.h" #include "rtkCyclicDeformationImageFilter.h" #include "rtkProgressCommands.h" #include #include #include int main(int argc, char * argv[]) { GGO(rtkfdk, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; using CPUOutputImageType = itk::Image; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = CPUOutputImageType; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); if (!args_info.lowmem_flag) { if (args_info.verbose_flag) std::cout << "Reading... " << std::endl; TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->Update()) } // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Check on hardware parameter #ifndef RTK_USE_CUDA if (!strcmp(args_info.hardware_arg, "cuda")) { std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; } #endif // Displaced detector weighting #ifdef RTK_USE_CUDA using DDFType = rtk::CudaDisplacedDetectorImageFilter; #else using DDFType = rtk::DisplacedDetectorForOffsetFieldOfViewImageFilter; #endif rtk::DisplacedDetectorImageFilter::Pointer ddf; if (!strcmp(args_info.hardware_arg, "cuda")) ddf = DDFType::New(); else ddf = rtk::DisplacedDetectorForOffsetFieldOfViewImageFilter::New(); ddf->SetInput(reader->GetOutput()); ddf->SetGeometry(geometry); ddf->SetDisable(args_info.nodisplaced_flag); // Short scan image filter using PSSFCPUType = rtk::ParkerShortScanImageFilter; #ifdef RTK_USE_CUDA using PSSFType = rtk::CudaParkerShortScanImageFilter; #else using PSSFType = rtk::ParkerShortScanImageFilter; #endif PSSFCPUType::Pointer pssf; if (!strcmp(args_info.hardware_arg, "cuda")) pssf = PSSFType::New(); else pssf = PSSFCPUType::New(); pssf->SetInput(ddf->GetOutput()); pssf->SetGeometry(geometry); pssf->InPlaceOff(); pssf->SetAngularGapThreshold(args_info.short_arg * itk::Math::pi / 180.); // Create reconstructed image using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // Motion-compensated objects for the compensation of a cyclic deformation. // Although these will only be used if the command line options for motion // compensation are set, we still create the object before hand to avoid auto // destruction. using DVFPixelType = itk::Vector; using DeformationType = rtk::CyclicDeformationImageFilter, itk::Image>; auto dvfReader = itk::ImageFileReader::New(); auto def = DeformationType::New(); def->SetInput(dvfReader->GetOutput()); auto bp = rtk::FDKWarpBackProjectionImageFilter::New(); bp->SetDeformation(def); bp->SetGeometry(geometry); // This macro sets options for fdk filter which I can not see how to do better // because TFFTPrecision is not the same, e.g. for CPU and CUDA (SR) #define SET_FELDKAMP_OPTIONS(f) \ f->SetInput(0, constantImageSource->GetOutput()); \ f->SetInput(1, pssf->GetOutput()); \ f->SetGeometry(geometry); \ f->GetRampFilter()->SetTruncationCorrection(args_info.pad_arg); \ f->GetRampFilter()->SetHannCutFrequency(args_info.hann_arg); \ f->GetRampFilter()->SetHannCutFrequencyY(args_info.hannY_arg); \ f->SetProjectionSubsetSize(args_info.subsetsize_arg); \ if (args_info.verbose_flag) \ { \ f->AddObserver(itk::AnyEvent(), progressCommand); \ } // FDK reconstruction filtering using FDKCPUType = rtk::FDKConeBeamReconstructionFilter; FDKCPUType::Pointer feldkamp; #ifdef RTK_USE_CUDA using FDKCUDAType = rtk::CudaFDKConeBeamReconstructionFilter; FDKCUDAType::Pointer feldkampCUDA; #endif itk::Image * pfeldkamp = nullptr; if (!strcmp(args_info.hardware_arg, "cpu")) { // Progress reporting using PercentageProgressCommandType = rtk::PercentageProgressCommand; auto progressCommand = PercentageProgressCommandType::New(); feldkamp = FDKCPUType::New(); SET_FELDKAMP_OPTIONS(feldkamp); // Motion compensated CBCT settings if (args_info.signal_given && args_info.dvf_given) { dvfReader->SetFileName(args_info.dvf_arg); def->SetSignalFilename(args_info.signal_arg); feldkamp->SetBackProjectionFilter(bp.GetPointer()); } pfeldkamp = feldkamp->GetOutput(); } #ifdef RTK_USE_CUDA else if (!strcmp(args_info.hardware_arg, "cuda")) { // Motion compensation not supported in cuda if (args_info.signal_given && args_info.dvf_given) { std::cerr << "Motion compensation is not supported in CUDA. Aborting" << std::endl; return EXIT_FAILURE; } // Progress reporting using PercentageProgressCommandType = rtk::PercentageProgressCommand; auto progressCommand = PercentageProgressCommandType::New(); feldkampCUDA = FDKCUDAType::New(); SET_FELDKAMP_OPTIONS(feldkampCUDA); pfeldkamp = feldkampCUDA->GetOutput(); } #endif // Streaming depending on streaming capability of writer auto streamerBP = itk::StreamingImageFilter::New(); streamerBP->SetInput(pfeldkamp); streamerBP->SetNumberOfStreamDivisions(args_info.divisions_arg); auto splitter = itk::ImageRegionSplitterDirection::New(); splitter->SetDirection(2); // Prevent splitting along z axis. As a result, splitting will be performed along y axis streamerBP->SetRegionSplitter(splitter); // Write if (args_info.verbose_flag) std::cout << "Reconstructing and writing... " << std::endl; TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(streamerBP->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkfdk/rtkfdk.ggo ================================================ purpose "Reconstructs a 3D volume from a sequence of projections [Feldkamp, David, Kress, 1984]." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "hardware" - "Hardware used for computation" values="cpu","cuda" no default="cpu" option "lowmem" l "Load only one projection per thread in memory" flag off option "divisions" d "Streaming option: number of stream divisions of the CT" int no default="1" option "subsetsize" - "Streaming option: number of projections processed at a time" int no default="16" option "nodisplaced" - "Disable the displaced detector filter" flag off option "short" - "Minimum angular gap to detect a short scan (in degree)." double no default="20" section "Ramp filter" option "pad" - "Data padding parameter to correct for truncation" double no default="0.0" option "hann" - "Cut frequency for hann window in ]0,1] (0.0 disables it)" double no default="0.0" option "hannY" - "Cut frequency for hann window in ]0,1] (0.0 disables it)" double no default="0.0" section "Motion-compensation described in [Rit et al, TMI, 2009] and [Rit et al, Med Phys, 2009]" option "signal" - "Signal file name" string no option "dvf" - "Input 4D DVF" string no ================================================ FILE: applications/rtkfdk/rtkfdk.py ================================================ #!/usr/bin/env python import argparse import sys import math import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Reconstructs a 3D volume from a sequence of projections [Feldkamp, David, Kress, 1984]." ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--geometry", "-g", help="XML geometry file name", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output file name", type=str, required=True ) parser.add_argument( "--hardware", help="Hardware used for computation", choices=["cpu", "cuda"], default="cpu", ) parser.add_argument( "--lowmem", "-l", help="Load only one projection per thread in memory", action="store_true", ) parser.add_argument( "--divisions", "-d", help="Streaming option: number of stream divisions of the CT", type=int, default=1, ) parser.add_argument( "--subsetsize", help="Streaming option: number of projections processed at a time", type=int, default=16, ) parser.add_argument( "--nodisplaced", help="Disable the displaced detector filter", action="store_true", ) parser.add_argument( "--short", help="Minimum angular gap to detect a short scan (converted to radians)", type=float, default=20.0, ) # Ramp filter options ramp_group = parser.add_argument_group("Ramp filter") ramp_group.add_argument( "--pad", help="Data padding parameter to correct for truncation", type=float, default=0.0, ) ramp_group.add_argument( "--hann", help="Cut frequency for Hann window in ]0,1] (0.0 disables it)", type=float, default=0.0, ) ramp_group.add_argument( "--hannY", help="Cut frequency for Hann window in ]0,1] (0.0 disables it)", type=float, default=0.0, ) # Motion compensation options motion_group = parser.add_argument_group( "Motion compensation described in [Rit et al, TMI, 2009] and [Rit et al, Med Phys, 2009]" ) motion_group.add_argument("--signal", help="Signal file name", type=str) motion_group.add_argument("--dvf", help="Input 4D DVF", type=str) # RTK specific groups rtk.add_rtkinputprojections_group(parser) rtk.add_rtk3Doutputimage_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): # Define pixel type and dimension OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Projections reader reader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) if not args_info.lowmem: if args_info.verbose: print("Reading projections...") reader.Update() # Geometry if args_info.verbose: print(f"Reading geometry information from {args_info.geometry}...") geometry = rtk.read_geometry(args_info.geometry) # Check on hardware parameter if not hasattr(itk, "CudaImage") and args_info.hardware == "cuda": print("The program has not been compiled with CUDA option.") sys.exit(1) # CUDA classes are non-templated, so they do not require a type specification. if args_info.hardware == "cuda": ddf = rtk.CudaDisplacedDetectorImageFilter.New() ddf.SetInput(itk.cuda_image_from_image(reader.GetOutput())) pssf = rtk.CudaParkerShortScanImageFilter.New() else: ddf = rtk.DisplacedDetectorForOffsetFieldOfViewImageFilter[ OutputImageType ].New() ddf.SetInput(reader.GetOutput()) pssf = rtk.ParkerShortScanImageFilter[OutputImageType].New() # Displaced detector weighting ddf.SetGeometry(geometry) ddf.SetDisable(args_info.nodisplaced) # Short scan image filter pssf.SetInput(ddf.GetOutput()) pssf.SetGeometry(geometry) pssf.InPlaceOff() pssf.SetAngularGapThreshold(args_info.short * math.pi / 180.0) # Create reconstructed image constantImageSource = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) # Motion-compensated objects for the compensation of a cyclic deformation. # Although these will only be used if the command line options for motion # compensation are set, we still create the object beforehand to avoid auto # destruction. DVFPixelType = itk.Vector[itk.F, 3] DVFImageType = itk.Image[DVFPixelType, 3] DVFImageSequenceType = itk.Image[DVFPixelType, 4] # Create the deformation filter and reader for DVF deformation = rtk.CyclicDeformationImageFilter[ DVFImageSequenceType, DVFImageType ].New() dvfReader = itk.ImageFileReader[DVFImageSequenceType].New() deformation.SetInput(dvfReader.GetOutput()) # Set up the back projection filter for motion compensation bp = rtk.FDKWarpBackProjectionImageFilter[ OutputImageType, OutputImageType, type(deformation) ].New() bp.SetDeformation(deformation) bp.SetGeometry(geometry) # FDK reconstruction filtering if args_info.hardware == "cuda": feldkamp = rtk.CudaFDKConeBeamReconstructionFilter.New() feldkamp.SetInput(0, itk.cuda_image_from_image(constantImageSource.GetOutput())) else: feldkamp = rtk.FDKConeBeamReconstructionFilter[OutputImageType].New() feldkamp.SetInput(0, constantImageSource.GetOutput()) # Set inputs and options for the FDK filter feldkamp.SetInput(1, pssf.GetOutput()) feldkamp.SetGeometry(geometry) feldkamp.GetRampFilter().SetTruncationCorrection(args_info.pad) feldkamp.GetRampFilter().SetHannCutFrequency(args_info.hann) feldkamp.GetRampFilter().SetHannCutFrequencyY(args_info.hannY) feldkamp.SetProjectionSubsetSize(args_info.subsetsize) # Progress reporting if args_info.verbose: progressCommand = rtk.PercentageProgressCommand(feldkamp) feldkamp.AddObserver( itk.ProgressEvent(), progressCommand.callback ) # Register the callback for progress feldkamp.AddObserver( itk.EndEvent(), progressCommand.End ) # Register end notification # Motion compensated CBCT settings if args_info.signal and args_info.dvf: if args_info.hardware == "cuda": print("Motion compensation is not supported in CUDA. Aborting") sys.exit(1) # Exit if CUDA is selected with motion compensation dvfReader.SetFileName(args_info.dvf) deformation.SetSignalFilename(args_info.signal) feldkamp.SetBackProjectionFilter(bp) # Streaming depending on streaming capability of writer streamerBP = itk.StreamingImageFilter[OutputImageType, OutputImageType].New() streamerBP.SetInput(feldkamp.GetOutput()) streamerBP.SetNumberOfStreamDivisions(args_info.divisions) # Create a splitter to control how the image region is divided during streaming splitter = itk.ImageRegionSplitterDirection.New() splitter.SetDirection(2) streamerBP.SetRegionSplitter(splitter) if args_info.verbose: print("Reconstructing and writing...") itk.imwrite(streamerBP.GetOutput(), args_info.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkfieldofview/CMakeLists.txt ================================================ WRAP_GGO(rtkfieldofview_GGO_C rtkfieldofview.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkfieldofview rtkfieldofview.cxx ${rtkfieldofview_GGO_C}) target_link_libraries(rtkfieldofview RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkfieldofview) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkfieldofview/rtkfieldofview.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkfieldofview_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkRayEllipsoidIntersectionImageFilter.h" #include "rtkFieldOfViewImageFilter.h" #include "rtkConstantImageSource.h" #include "rtkBackProjectionImageFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaBackProjectionImageFilter.h" #endif #include #include #include #include #include int main(int argc, char * argv[]) { GGO(rtkfieldofview, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; // Check on hardware parameter #ifndef RTK_USE_CUDA if (!strcmp(args_info.hardware_arg, "cuda")) { std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; } #endif #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Reconstruction reader OutputImageType::Pointer unmasked_reconstruction; TRY_AND_EXIT_ON_ITK_EXCEPTION(unmasked_reconstruction = itk::ReadImage(args_info.reconstruction_arg)) if (!args_info.bp_flag) { // FOV filter auto fieldofview = rtk::FieldOfViewImageFilter::New(); fieldofview->SetMask(args_info.mask_flag); fieldofview->SetInput(0, unmasked_reconstruction); fieldofview->SetProjectionsStack(reader->GetOutput()); fieldofview->SetGeometry(geometry); fieldofview->SetDisplacedDetector(args_info.displaced_flag); TRY_AND_EXIT_ON_ITK_EXCEPTION(fieldofview->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(fieldofview->GetOutput(), args_info.output_arg)) } else { if (args_info.displaced_flag) { std::cerr << "Options --displaced and --bp are not compatible (yet)." << std::endl; return EXIT_FAILURE; } TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputInformation()) #ifdef RTK_USE_CUDA using MaskImgType = itk::CudaImage; #else using MaskImgType = itk::Image; #endif using ConstantType = rtk::ConstantImageSource; auto ones = ConstantType::New(); ones->SetConstant(1); ones->SetInformationFromImage(reader->GetOutput()); auto zeroVol = ConstantType::New(); zeroVol->SetConstant(0.); zeroVol->SetInformationFromImage(unmasked_reconstruction); auto bp = rtk::BackProjectionImageFilter::New(); #ifdef RTK_USE_CUDA if (!strcmp(args_info.hardware_arg, "cuda")) bp = rtk::CudaBackProjectionImageFilter::New(); #endif bp->SetInput(zeroVol->GetOutput()); bp->SetInput(1, ones->GetOutput()); bp->SetGeometry(geometry); auto thresh = itk::ThresholdImageFilter::New(); thresh->SetInput(bp->GetOutput()); thresh->ThresholdBelow(geometry->GetGantryAngles().size() - 1); thresh->SetOutsideValue(0.); if (args_info.mask_flag) { auto div = itk::DivideImageFilter::New(); div->SetInput(thresh->GetOutput()); div->SetConstant2(geometry->GetGantryAngles().size()); TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(div->GetOutput(), args_info.output_arg)) } else { std::cerr << "Option --bp without --mask is not implemented (yet)." << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkfieldofview/rtkfieldofview.ggo ================================================ purpose "Computes the field of view of a reconstruction." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output projections file name" string yes option "reconstruction" - "Reconstruction file unmasked" string yes option "mask" m "Output a binary mask instead of a masked image" flag off option "displaced" d "Assume that a displaced detector has been used" flag off option "bp" b "Slow alternative for non cylindrical FOVs:\ backproject projections filled with ones and threshold result." flag off option "hardware" - "Hardware used for computation (with --bp only)" values="cpu","cuda" no default="cpu" ================================================ FILE: applications/rtkfieldofview/rtkfieldofview.py ================================================ #!/usr/bin/env python import argparse import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Computes the field of view of a reconstruction." ) parser.add_argument("--verbose", "-v", help="Verbose execution", action="store_true") parser.add_argument("--geometry", "-g", help="XML geometry file name", type=str, required=True) parser.add_argument("--output", "-o", help="Output projections file name", type=str, required=True) parser.add_argument("--reconstruction", help="Reconstruction file unmasked", type=str, required=True) parser.add_argument("--mask", "-m", help="Output a binary mask instead of a masked image", action="store_true") parser.add_argument("--displaced", "-d", help="Assume that a displaced detector has been used", action="store_true") parser.add_argument("--bp", "-b", help="Slow alternative for non cylindrical FOVs: backproject projections filled with ones and threshold result.", action="store_true") parser.add_argument("--hardware", help="Hardware used for computation (with --bp only)", choices=["cpu", "cuda"], default="cpu") rtk.add_rtkinputprojections_group(parser) return parser def process(args_info: argparse.Namespace): OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] use_cuda = args_info.hardware == "cuda" if use_cuda: if not hasattr(itk, "CudaImage"): raise RuntimeError("The program has not been compiled with CUDA option.") # Projections reader reader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) # Geometry if args_info.verbose: print(f"Reading geometry information from {args_info.geometry}...") geometry = rtk.read_geometry(args_info.geometry) # Reconstruction reader if args_info.verbose: print(f"Reading reconstruction from {args_info.reconstruction}...") unmasked_reconstruction = itk.imread(args_info.reconstruction) if not args_info.bp: # FOV filter fieldofview = rtk.FieldOfViewImageFilter[OutputImageType, OutputImageType].New() fieldofview.SetMask(args_info.mask) fieldofview.SetInput(0, unmasked_reconstruction) fieldofview.SetProjectionsStack(reader.GetOutput()) fieldofview.SetGeometry(geometry) fieldofview.SetDisplacedDetector(args_info.displaced) fieldofview.Update() if args_info.verbose: print(f"Writing output to {args_info.output}...") itk.imwrite(fieldofview.GetOutput(), args_info.output) else: if args_info.displaced: raise RuntimeError("Options --displaced and --bp are not compatible (yet).") MaskImgType = itk.image[itk.US, Dimension] reader.UpdateOutputInformation() ones = rtk.ConstantImageSource[MaskImgType].New() ones.SetConstant(1) ones.SetInformationFromImage(reader.GetOutput()) zeroVol = rtk.ConstantImageSource[MaskImgType].New() zeroVol.SetConstant(0.) zeroVol.SetInformationFromImage(unmasked_reconstruction) if use_cuda: CudaMaskImgType = itk.CudaImage[OutputPixelType, Dimension] bp = rtk.CudaBackProjectionImageFilter[CudaMaskImgType].New() bp.SetInput(itk.cuda_image_from_image(zeroVol.GetOutput())) bp.SetInput(1, itk.cuda_image_from_image(ones.GetOutput())) else: bp = rtk.BackProjectionImageFilter[MaskImgType, MaskImgType].New() bp.SetInput(zeroVol.GetOutput()) bp.SetInput(1, ones.GetOutput()) bp.SetGeometry(geometry) thresh = itk.ThresholdImageFilter[MaskImgType].New() thresh.SetInput(bp.GetOutput()) thresh.ThresholdBelow(len(geometry.GetGantryAngles()) - 1) thresh.SetOutsideValue(0.) if args_info.mask: div = itk.DivideImageFilter[MaskImgType, MaskImgType, MaskImgType].New() div.SetInput(thresh.GetOutput()) div.SetConstant2(len(geometry.GetGantryAngles())) if args_info.verbose: print(f"Writing mask output to {args_info.output}...") itk.imwrite(div.GetOutput(), args_info.output) else: raise NotImplementedError("Option --bp without --mask is not implemented (yet).") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkforwardprojections/CMakeLists.txt ================================================ WRAP_GGO(rtkforwardprojections_GGO_C rtkforwardprojections.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkforwardprojections rtkforwardprojections.cxx ${rtkforwardprojections_GGO_C}) target_link_libraries(rtkforwardprojections RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkforwardprojections) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkforwardprojections/ForwardProjection.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # Forward project rtkforwardprojections -g geometry.xml -o projections.mha -i 00.mhd --spacing 2 --size 512 # Reconstruct in the same resolution as the original rtkfdk -p . -r projections.mha -o fdk.mha -g geometry.xml --spacing=0.976562,0.976562,2 --origin=-250,-250,-164.5 --size=512,512,141 ================================================ FILE: applications/rtkforwardprojections/README.md ================================================ # Forward Projection ![sin](../../documentation/docs/ExternalData/POPI-Sinogram.png){w=400px alt="POPI sinogram"} ![img](../../documentation/docs/ExternalData/POPI-Reconstruction.png){w=400px alt="POPI reconstruction"} This script uses the files [00.mhd](http://www.creatis.insa-lyon.fr/~srit/POPI/MedPhys11/bl/mhd/00.mhd) and [00.raw](http://www.creatis.insa-lyon.fr/~srit/POPI/MedPhys11/bl/mhd/00.raw) of the [POPI](https://github.com/open-vv/popi-model/blob/master/popi-model.md) as input. ```{literalinclude} ForwardProjection.sh ``` Note that the original file is in Hounsfield units which explains the negative values in the projection images since, e.g., the attenuation of air is -1000 HU. It is also worth of note that the file is oriented in the DICOM coordinate system although RTK uses the IEC 61217 which results in a rotation around the antero-posterior axis of the patient. This can be easily changed by modifying the TransformMatrix in the 00.mhd file. ================================================ FILE: applications/rtkforwardprojections/rtkforwardprojections.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkforwardprojections_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkJosephForwardProjectionImageFilter.h" #include "rtkMaximumIntensityProjectionImageFilter.h" #include "rtkJosephForwardAttenuatedProjectionImageFilter.h" #include "rtkZengForwardProjectionImageFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaForwardProjectionImageFilter.h" #endif #include #include int main(int argc, char * argv[]) { GGO(rtkforwardprojections, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::flush; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); if (args_info.verbose_flag) std::cout << " done." << std::endl; // Create a stack of empty projection images using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // Adjust size according to geometry auto sizeOutput = itk::MakeSize( constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], geometry->GetGantryAngles().size()); constantImageSource->SetSize(sizeOutput); // Input reader if (args_info.verbose_flag) std::cout << "Reading input volume " << args_info.input_arg << "..." << std::endl; OutputImageType::Pointer inputVolume; TRY_AND_EXIT_ON_ITK_EXCEPTION(inputVolume = itk::ReadImage(args_info.input_arg)) OutputImageType::Pointer attenuationMap; if (args_info.attenuationmap_given) { if (args_info.verbose_flag) std::cout << "Reading attenuation map " << args_info.attenuationmap_arg << "..." << std::endl; // Read an existing image to initialize the attenuation map attenuationMap = itk::ReadImage(args_info.attenuationmap_arg); } using ClipImageType = itk::Image; ClipImageType::Pointer inferiorClipImage, superiorClipImage; if (args_info.inferiorclipimage_given) { if (args_info.verbose_flag) std::cout << "Reading inferior clip image " << args_info.inferiorclipimage_arg << "..." << std::endl; // Read an existing image to initialize the attenuation map inferiorClipImage = itk::ReadImage(args_info.inferiorclipimage_arg); } if (args_info.superiorclipimage_given) { if (args_info.verbose_flag) std::cout << "Reading superior clip image " << args_info.superiorclipimage_arg << "..." << std::endl; // Read an existing image to initialize the attenuation map superiorClipImage = itk::ReadImage(args_info.superiorclipimage_arg); } // Create forward projection image filter if (args_info.verbose_flag) std::cout << "Projecting volume..." << std::endl; rtk::ForwardProjectionImageFilter::Pointer forwardProjection; switch (args_info.fp_arg) { case (fp_arg_Joseph): forwardProjection = rtk::JosephForwardProjectionImageFilter::New(); break; case (fp_arg_JosephAttenuated): forwardProjection = rtk::JosephForwardAttenuatedProjectionImageFilter::New(); break; case (fp_arg_Zeng): forwardProjection = rtk::ZengForwardProjectionImageFilter::New(); break; case (fp_arg_MIP): forwardProjection = rtk::MaximumIntensityProjectionImageFilter::New(); break; case (fp_arg_CudaRayCast): #ifdef RTK_USE_CUDA forwardProjection = rtk::CudaForwardProjectionImageFilter::New(); dynamic_cast *>( forwardProjection.GetPointer()) ->SetStepSize(args_info.step_arg); #else std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; #endif break; default: std::cerr << "Unhandled --method value." << std::endl; return EXIT_FAILURE; } forwardProjection->SetInput(constantImageSource->GetOutput()); forwardProjection->SetInput(1, inputVolume); if (args_info.attenuationmap_given) forwardProjection->SetInput(2, attenuationMap); if (args_info.inferiorclipimage_given) { if (args_info.fp_arg == fp_arg_Joseph) { dynamic_cast *>( forwardProjection.GetPointer()) ->SetInferiorClipImage(inferiorClipImage); } else if (args_info.fp_arg == fp_arg_JosephAttenuated) { dynamic_cast *>( forwardProjection.GetPointer()) ->SetInferiorClipImage(inferiorClipImage); } else if (args_info.fp_arg == fp_arg_MIP) { dynamic_cast *>( forwardProjection.GetPointer()) ->SetInferiorClipImage(inferiorClipImage); } } if (args_info.superiorclipimage_given) { if (args_info.fp_arg == fp_arg_Joseph) { dynamic_cast *>( forwardProjection.GetPointer()) ->SetSuperiorClipImage(superiorClipImage); } else if (args_info.fp_arg == fp_arg_JosephAttenuated) { dynamic_cast *>( forwardProjection.GetPointer()) ->SetSuperiorClipImage(superiorClipImage); } else if (args_info.fp_arg == fp_arg_MIP) { dynamic_cast *>( forwardProjection.GetPointer()) ->SetSuperiorClipImage(superiorClipImage); } } if (args_info.sigmazero_given && args_info.fp_arg == fp_arg_Zeng) dynamic_cast *>( forwardProjection.GetPointer()) ->SetSigmaZero(args_info.sigmazero_arg); if (args_info.alphapsf_given && args_info.fp_arg == fp_arg_Zeng) dynamic_cast *>( forwardProjection.GetPointer()) ->SetAlpha(args_info.alphapsf_arg); forwardProjection->SetGeometry(geometry); if (!args_info.lowmem_flag) { TRY_AND_EXIT_ON_ITK_EXCEPTION(forwardProjection->Update()) } // Write if (args_info.verbose_flag) std::cout << "Writing... " << std::endl; auto writer = itk::ImageFileWriter::New(); writer->SetFileName(args_info.output_arg); writer->SetInput(forwardProjection->GetOutput()); if (args_info.lowmem_flag) { writer->SetNumberOfStreamDivisions(sizeOutput[2]); } TRY_AND_EXIT_ON_ITK_EXCEPTION(writer->Update()) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkforwardprojections/rtkforwardprojections.ggo ================================================ purpose "Projects a volume according to a geometry file." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "input" i "Input volume file name" string yes option "output" o "Output projections file name" string yes option "step" s "Step size along ray (for CudaRayCast only)" double no default="1" option "lowmem" l "Compute only one projection at a time" flag off section "Projectors" option "fp" f "Forward projection method" values="Joseph","JosephAttenuated","CudaRayCast","Zeng","MIP" enum no default="Joseph" option "attenuationmap" - "Attenuation map relative to the volume to perfom the attenuation correction" string no option "sigmazero" - "PSF value at a distance of 0 meter of the detector" double no option "alphapsf" - "Slope of the PSF against the detector distance" double no option "inferiorclipimage" - "Value of the inferior clip of the ray for each pixel of the projections (only with Joseph-based projector)" string no option "superiorclipimage" - "Value of the superior clip of the ray for each pixel of the projections (only with Joseph-based projector)" string no ================================================ FILE: applications/rtkforwardprojections/rtkforwardprojections.py ================================================ #!/usr/bin/env python import sys import argparse import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Projects a volume according to a geometry file." ) parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--geometry", "-g", help="XML geometry file name", type=str, required=True ) parser.add_argument( "--input", "-i", help="Input volume file name", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output projections file name", type=str, required=True ) parser.add_argument( "--step", "-s", help="Step size along ray (for CudaRayCast only)", type=float, default=1, ) parser.add_argument( "--lowmem", "-l", help="Compute only one projection at a time", action="store_true", ) # Projectors rtkprojectors_group = parser.add_argument_group("Projectors") rtkprojectors_group.add_argument( "--fp", "-f", help="Forward projection method", choices=[ "Joseph", "JosephAttenuated", "CudaRayCast", "Zeng", "MIP", "CudaWrapRayCast", ], default="Joseph", ) rtkprojectors_group.add_argument( "--attenuationmap", help="Attenuation map relative to the volume to perfom the attenuation correction", type=str, ) rtkprojectors_group.add_argument( "--sigmazero", help="PSF value at a distance of 0 meter of the detector", type=float, ) rtkprojectors_group.add_argument( "--alphapsf", help="Slope of the PSF against the detector distance", type=float ) rtkprojectors_group.add_argument( "--inferiorclipimage", help="Value of the inferior clip of the ray for each pixel of the projections (only with Joseph-based projector)", type=str, ) rtkprojectors_group.add_argument( "--superiorclipimage", help="Value of the superior clip of the ray for each pixel of the projections (only with Joseph-based projector)", type=str, ) # Motion compensation options warp_forwardprojection_group = parser.add_argument_group( "Motion compensation described in [Rit et al, TMI, 2009]" ) warp_forwardprojection_group.add_argument( "--signal", help="Signal file name", type=str ) warp_forwardprojection_group.add_argument("--dvf", help="Input 4D DVF", type=str) rtk.add_rtk3Doutputimage_group(parser) return parser def process(args_info): OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] if hasattr(itk, "CudaImage"): OutputCudaImageType = itk.CudaImage[OutputPixelType, Dimension] # Geometry if args_info.verbose: print( f"Reading geometry information from {args_info.geometry}...", end="", flush=True, ) geometry = rtk.read_geometry(args_info.geometry) if args_info.verbose: print(" done.") # Create a stack of empty projection images ConstantImageSourceType = rtk.ConstantImageSource[OutputImageType] constantImageSource = ConstantImageSourceType.New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) # Adjust size according to geometry sizeOutput = list(constantImageSource.GetSize()) sizeOutput[2] = len(geometry.GetGantryAngles()) constantImageSource.SetSize(sizeOutput) # Input reader if args_info.verbose: print(f"Reading input volume {args_info.input}...") inputVolume = itk.imread(args_info.input) attenuation_map = None if args_info.attenuationmap: if args_info.verbose: print(f"Reading attenuation map {args_info.attenuationmap}...") attenuation_map = itk.imread(args_info.attenuationmap) inferiorClipImage = None superiorClipImage = None if args_info.inferiorclipimage: if args_info.verbose: print(f"Reading inferior clip image {args_info.inferiorclipimage}...") inferiorClipImage = itk.imread(args_info.inferiorclipimage) if args_info.superiorclipimage: if args_info.verbose: print(f"Reading superior clip image {args_info.superiorclipimage}...") superiorClipImage = itk.imread(args_info.superiorclipimage) # Create forward projection image filter if args_info.verbose: print("Projecting volume...") # Select the forward projector if args_info.fp == "Joseph": forwardProjection = rtk.JosephForwardProjectionImageFilter[ OutputImageType, OutputImageType ].New() elif args_info.fp == "JosephAttenuated": forwardProjection = rtk.JosephForwardAttenuatedProjectionImageFilter[ OutputImageType, OutputImageType ].New() if attenuation_map is None: print( "JosephAttenuatedForwardProjection requires an attenuation map. " "Please provide it using --attenuationmap." ) sys.exit(1) elif args_info.fp == "Zeng": forwardProjection = rtk.ZengForwardProjectionImageFilter[ OutputImageType, OutputImageType ].New() elif args_info.fp == "MIP": forwardProjection = rtk.MaximumIntensityProjectionImageFilter[ OutputImageType, OutputImageType ].New() elif args_info.fp == "CudaRayCast": if hasattr(itk, "CudaImage"): forwardProjection = rtk.CudaForwardProjectionImageFilter[ OutputCudaImageType ].New() forwardProjection.SetStepSize(args_info.step) else: print("The program has not been compiled with cuda option") sys.exit(1) elif args_info.fp == "CudaWrapRayCast": if hasattr(itk, "CudaImage"): if not args_info.dvf or not args_info.signal: print("CudaWrapRayCast requires --dvf and --signal arguments.") sys.exit(1) forwardProjection = rtk.CudaWarpForwardProjectionImageFilter[ OutputCudaImageType ].New() forwardProjection.SetStepSize(args_info.step) dvf_image = itk.imread(args_info.dvf) forwardProjection.SetDisplacementField(dvf_image) forwardProjection.SetSignalFilename(args_info.signal) else: print("The program has not been compiled with cuda option") sys.exit(1) if args_info.fp in ["CudaWrapRayCast", "CudaRayCast"]: forwardProjection.SetInput( itk.cuda_image_from_image(constantImageSource.GetOutput()) ) forwardProjection.SetInput(1, itk.cuda_image_from_image(inputVolume)) if attenuation_map: forwardProjection.SetInput(2, itk.cuda_image_from_image(attenuation_map)) else: forwardProjection.SetInput(constantImageSource.GetOutput()) forwardProjection.SetInput(1, inputVolume) if attenuation_map: forwardProjection.SetInput(2, attenuation_map) if inferiorClipImage is not None: if args_info.fp == "Joseph": forwardProjection.SetInferiorClipImage(inferiorClipImage) elif args_info.fp == "JosephAttenuated": forwardProjection.SetInferiorClipImage(inferiorClipImage) elif args_info.fp == "MIP": forwardProjection.SetInferiorClipImage(inferiorClipImage) if superiorClipImage is not None: if args_info.fp == "Joseph": forwardProjection.SetSuperiorClipImage(superiorClipImage) elif args_info.fp == "JosephAttenuated": forwardProjection.SetSuperiorClipImage(superiorClipImage) elif args_info.fp == "MIP": forwardProjection.SetSuperiorClipImage(superiorClipImage) if args_info.sigmazero and args_info.fp == "Zeng": forwardProjection.SetSigmaZero(args_info.sigmazero) if args_info.alphapsf and args_info.fp == "Zeng": forwardProjection.SetAlpha(args_info.alphapsf) forwardProjection.SetGeometry(geometry) if not args_info.lowmem: forwardProjection.Update() # Write if args_info.verbose: print("Writing...") writer = itk.ImageFileWriter[OutputImageType].New() writer.SetFileName(args_info.output) writer.SetInput(forwardProjection.GetOutput()) writer.Update() if args_info.verbose: print("Processing completed successfully.") def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkfourdconjugategradient/CMakeLists.txt ================================================ WRAP_GGO(rtkfourdconjugategradient_GGO_C rtkfourdconjugategradient.ggo ../rtkinputprojections_section.ggo ../rtk4Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkfourdconjugategradient rtkfourdconjugategradient.cxx ${rtkfourdconjugategradient_GGO_C}) target_link_libraries(rtkfourdconjugategradient RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkfourdconjugategradient) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkfourdconjugategradient/rtkfourdconjugategradient.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkfourdconjugategradient_ggo.h" #include "rtkGgoFunctions.h" #include "rtkFourDConjugateGradientConeBeamReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkSignalToInterpolationWeights.h" #include "rtkReorderProjectionsImageFilter.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" # include "rtkCudaConstantVolumeSeriesSource.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkfourdconjugategradient, args_info); using OutputPixelType = float; #ifdef RTK_USE_CUDA using VolumeSeriesType = itk::CudaImage; using ProjectionStackType = itk::CudaImage; #else using VolumeSeriesType = itk::Image; using ProjectionStackType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateLargestPossibleRegion()) // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo( constantImageSource, args_info); // GenGetOpt can't handle default arguments for multiple arguments like size or spacing. // The only default it accepts is to set all components of a multiple argument to the same value. // Default size is 256^4, ie the number of reconstructed instants is 256. It has to be set to a more reasonable // value which is why a "frames" argument is introduced auto inputSize = itk::MakeSize(constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], constantImageSource->GetSize()[2], args_info.frames_arg); constantImageSource->SetSize(inputSize); inputFilter = constantImageSource; } TRY_AND_EXIT_ON_ITK_EXCEPTION(inputFilter->Update()) inputFilter->ReleaseDataFlagOn(); // Re-order geometry and projections // In the new order, projections with identical phases are packed together std::vector signal = rtk::ReadSignalFile(args_info.signal_arg); auto reorder = rtk::ReorderProjectionsImageFilter::New(); reorder->SetInput(reader->GetOutput()); reorder->SetInputGeometry(geometry); reorder->SetInputSignal(signal); TRY_AND_EXIT_ON_ITK_EXCEPTION(reorder->Update()) // Release the memory holding the stack of original projections reader->GetOutput()->ReleaseData(); // Compute the interpolation weights auto signalToInterpolationWeights = rtk::SignalToInterpolationWeights::New(); signalToInterpolationWeights->SetSignal(reorder->GetOutputSignal()); signalToInterpolationWeights->SetNumberOfReconstructedFrames( inputFilter->GetOutput()->GetLargestPossibleRegion().GetSize(3)); TRY_AND_EXIT_ON_ITK_EXCEPTION(signalToInterpolationWeights->Update()) // Set the forward and back projection filters to be used using ConjugateGradientFilterType = rtk::FourDConjugateGradientConeBeamReconstructionFilter; auto conjugategradient = ConjugateGradientFilterType::New(); SetForwardProjectionFromGgo(args_info, conjugategradient.GetPointer()); SetBackProjectionFromGgo(args_info, conjugategradient.GetPointer()); conjugategradient->SetInputVolumeSeries(inputFilter->GetOutput()); conjugategradient->SetNumberOfIterations(args_info.niterations_arg); conjugategradient->SetCudaConjugateGradient(args_info.cudacg_flag); conjugategradient->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); // Set the newly ordered arguments conjugategradient->SetInputProjectionStack(reorder->GetOutput()); conjugategradient->SetGeometry(reorder->GetOutputGeometry()); conjugategradient->SetWeights(signalToInterpolationWeights->GetOutput()); conjugategradient->SetSignal(reorder->GetOutputSignal()); REPORT_ITERATIONS(conjugategradient, ConjugateGradientFilterType, VolumeSeriesType); TRY_AND_EXIT_ON_ITK_EXCEPTION(conjugategradient->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(conjugategradient->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkfourdconjugategradient/rtkfourdconjugategradient.ggo ================================================ purpose "Reconstructs a 3D + time sequence of volumes from a projection stack and a respiratory/cardiac signal, with a conjugate gradient technique" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="5" option "cudacg" - "Perform conjugate gradient calculations on GPU" flag off option "input" i "Input volume" string no option "nodisplaced" - "Disable the displaced detector filter" flag off section "Phase gating" option "signal" - "File containing the phase of each projection" string yes ================================================ FILE: applications/rtkfourdfdk/CMakeLists.txt ================================================ WRAP_GGO(rtkfourdfdk_GGO_C rtkfourdfdk.ggo ../rtkinputprojections_section.ggo ../rtk4Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkfourdfdk rtkfourdfdk.cxx ${rtkfourdfdk_GGO_C}) target_link_libraries(rtkfourdfdk RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkfourdfdk) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkfourdfdk/rtkfourdfdk.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkfourdfdk_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkDisplacedDetectorForOffsetFieldOfViewImageFilter.h" #include "rtkParkerShortScanImageFilter.h" #include "rtkFDKConeBeamReconstructionFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaDisplacedDetectorImageFilter.h" // TODO # include "rtkCudaDisplacedDetectorForOffsetFieldOfViewImageFilter.h" # include "rtkCudaParkerShortScanImageFilter.h" # include "rtkCudaFDKConeBeamReconstructionFilter.h" #endif #include "rtkSelectOneProjectionPerCycleImageFilter.h" #include #include #include int main(int argc, char * argv[]) { GGO(rtkfourdfdk, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; using CPUOutputImageType = itk::Image; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = CPUOutputImageType; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Part specific to 4D auto selector = rtk::SelectOneProjectionPerCycleImageFilter::New(); selector->SetInput(reader->GetOutput()); selector->SetInputGeometry(geometry); selector->SetSignalFilename(args_info.signal_arg); // Check on hardware parameter #ifndef RTK_USE_CUDA if (!strcmp(args_info.hardware_arg, "cuda")) { std::cerr << "The program has not been compiled with cuda option" << std::endl; return EXIT_FAILURE; } #endif // Displaced detector weighting #ifdef RTK_USE_CUDA using DDFType = rtk::CudaDisplacedDetectorImageFilter; #else using DDFType = rtk::DisplacedDetectorForOffsetFieldOfViewImageFilter; #endif rtk::DisplacedDetectorImageFilter::Pointer ddf; if (!strcmp(args_info.hardware_arg, "cuda")) ddf = DDFType::New(); else ddf = rtk::DisplacedDetectorForOffsetFieldOfViewImageFilter::New(); ddf->SetInput(selector->GetOutput()); ddf->SetGeometry(selector->GetOutputGeometry()); // Short scan image filter using PSSFCPUType = rtk::ParkerShortScanImageFilter; #ifdef RTK_USE_CUDA using PSSFType = rtk::CudaParkerShortScanImageFilter; #else using PSSFType = rtk::ParkerShortScanImageFilter; #endif PSSFCPUType::Pointer pssf; if (!strcmp(args_info.hardware_arg, "cuda")) pssf = PSSFType::New(); else pssf = PSSFCPUType::New(); pssf->SetInput(ddf->GetOutput()); pssf->SetGeometry(selector->GetOutputGeometry()); pssf->InPlaceOff(); // Create one frame of the reconstructed image using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(constantImageSource->Update()) // This macro sets options for fdk filter which I can not see how to do better // because TFFTPrecision is not the same, e.g. for CPU and CUDA (SR) #define SET_FELDKAMP_OPTIONS(f) \ f->SetInput(0, constantImageSource->GetOutput()); \ f->SetInput(1, pssf->GetOutput()); \ f->SetGeometry(selector->GetOutputGeometry()); \ f->GetRampFilter()->SetTruncationCorrection(args_info.pad_arg); \ f->GetRampFilter()->SetHannCutFrequency(args_info.hann_arg); \ f->GetRampFilter()->SetHannCutFrequencyY(args_info.hannY_arg); \ f->SetProjectionSubsetSize(args_info.subsetsize_arg) // FDK reconstruction filtering using FDKCPUType = rtk::FDKConeBeamReconstructionFilter; FDKCPUType::Pointer feldkamp; #ifdef RTK_USE_CUDA using FDKCUDAType = rtk::CudaFDKConeBeamReconstructionFilter; FDKCUDAType::Pointer feldkampCUDA; #endif itk::Image * pfeldkamp = nullptr; if (!strcmp(args_info.hardware_arg, "cpu")) { feldkamp = FDKCPUType::New(); SET_FELDKAMP_OPTIONS(feldkamp); pfeldkamp = feldkamp->GetOutput(); } #ifdef RTK_USE_CUDA else if (!strcmp(args_info.hardware_arg, "cuda")) { feldkampCUDA = FDKCUDAType::New(); SET_FELDKAMP_OPTIONS(feldkampCUDA); pfeldkamp = feldkampCUDA->GetOutput(); } #endif // Streaming depending on streaming capability of writer auto streamerBP = itk::StreamingImageFilter::New(); streamerBP->SetInput(pfeldkamp); streamerBP->SetNumberOfStreamDivisions(args_info.divisions_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(streamerBP->UpdateOutputInformation()) // Create empty 4D image using FourDOutputImageType = itk::Image; using FourDConstantImageSourceType = rtk::ConstantImageSource; auto fourDConstantImageSource = FourDConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(fourDConstantImageSource, args_info); // GenGetOpt can't handle default arguments for multiple arguments like size or spacing. // The only default it accepts is to set all components of a multiple argument to the same value. // Default size is 256^4, ie the number of reconstructed instants is 256. It has to be set to a more reasonable // value which is why a "frames" argument is introduced FourDConstantImageSourceType::SizeType fourDInputSize(fourDConstantImageSource->GetSize()); fourDInputSize[3] = args_info.frames_arg; fourDConstantImageSource->SetSize(fourDInputSize); TRY_AND_EXIT_ON_ITK_EXCEPTION(fourDConstantImageSource->Update()) // Go over each frame, reconstruct 3D frame and paste with iterators in 4D image for (int f = 0; f < args_info.frames_arg; f++) { if (args_info.verbose_flag) std::cout << "Reconstructing frame #" << f << "..." << std::endl; selector->SetPhase(f / (double)args_info.frames_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(streamerBP->UpdateLargestPossibleRegion()) FourDConstantImageSourceType::OutputImageRegionType region; region = fourDConstantImageSource->GetOutput()->GetLargestPossibleRegion(); region.SetIndex(3, f); region.SetSize(3, 1); itk::ImageRegionIterator it4D(fourDConstantImageSource->GetOutput(), region); itk::ImageRegionIterator it3D(streamerBP->GetOutput(), streamerBP->GetOutput()->GetLargestPossibleRegion()); while (!it3D.IsAtEnd()) { it4D.Set(it3D.Get()); ++it4D; ++it3D; } } // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(fourDConstantImageSource->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkfourdfdk/rtkfourdfdk.ggo ================================================ purpose "Reconstructs a 4D volume from a sequence of projections using FDK with one projection per respiratory cycle in each frame." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "hardware" - "Hardware used for computation" values="cpu","cuda" no default="cpu" option "lowmem" l "Load only one projection per thread in memory" flag off option "divisions" d "Streaming option: number of stream divisions of the CT" int no default="1" option "subsetsize" - "Streaming option: number of projections processed at a time" int no default="16" section "Ramp filter" option "pad" - "Data padding parameter to correct for truncation" double no default="0.0" option "hann" - "Cut frequency for hann window in ]0,1] (0.0 disables it)" double no default="0.0" option "hannY" - "Cut frequency for hann window in ]0,1] (0.0 disables it)" double no default="0.0" section "Phase gating" option "signal" - "File containing the phase of each projection" string yes ================================================ FILE: applications/rtkfourdrooster/CMakeLists.txt ================================================ WRAP_GGO(rtkfourdrooster_GGO_C rtkfourdrooster.ggo ../rtkinputprojections_section.ggo ../rtk4Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkfourdrooster rtkfourdrooster.cxx ${rtkfourdrooster_GGO_C}) target_link_libraries(rtkfourdrooster RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkfourdrooster) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkfourdrooster/README.md ================================================ # 4DROOSTER: Total variation-regularized 3D + time reconstruction RTK provides a tool to reconstruct a 3D + time image which does not require explicit motion estimation. Instead, it uses total variation regularization along both space and time. The implementation is based on a paper that we have published ([article](http://www.creatis.insa-lyon.fr/site/fr/publications/MORY-14)). You should read the article to understand the basics of the algorithm before trying to use the software. The algorithm requires a set of projection images with the associated RTK geometry, the respiratory phase of each projection image and a motion mask in which the region of the space where movement is expected is set to 1 and the rest is set to 0. Each piece of data is described in more details below and can be downloaded using [Girder](https://data.kitware.com/#collection/5a7706878d777f0649e04776). It is assumed that the breathing motion is periodic, which implies that the mechanical state of the chest depends only on the respiratory phase. ## Projection images This example is illustrated with a set of projection images of the [POPI patient](https://github.com/open-vv/popi-model/blob/master/popi-model.md). You can [download the projections](https://data.kitware.com/api/v1/item/5be99af88d777f2179a2e144/download) and the required tables of the Elekta database, [FRAME.DBF](https://data.kitware.com/api/v1/item/5be99a068d777f2179a2cf4f/download) and [IMAGE.DBF](https://data.kitware.com/api/v1/item/5be99a078d777f2179a2cf65/download). The dataset is first used to reconstruct a blurry image: ``` # Convert Elekta database to RTK geometry rtkelektasynergygeometry \ -o geometry.rtk \ -f FRAME.DBF \ -i IMAGE.DBF \ -u 1.3.46.423632.141000.1169042526.68 # Reconstruct a 3D volume from all projection images. # We implicitly assume that the patient's chest is static, which is wrong. # Therefore the reconstruction will be blurry. This blurry reconstruction is # not required for 4D ROOSTER, but there is usually no other way to generate # a motion mask, which is required. Additionally, the blurry reconstruction # can be used to initialize the 4D ROOSTER reconstruction. rtkfdk \ -p . \ -r .*.his \ -o fdk.mha \ -g geometry.rtk \ --hann 0.5 \ --pad 1.0 \ --size 160 \ --spacing 2 ``` You should obtain something like that with [VV](http://vv.creatis.insa-lyon.fr/): ![Blurred](../../documentation/docs/ExternalData/Blurred.jpg){w=600px alt="Blurred image"} ## Motion mask The next piece of data is a 3D motion mask: a volume that contains only zeros, where no movement is expected to occur, and ones, where movement is expected. Typically, during breathing, the whole chest moves, so it is safe to use the [patient mask](https://data.kitware.com/api/v1/item/5be99a418d777f2179a2ddf4/download) (red+green). However, restricting the movement to a smaller region, e.g. the rib cage, can help reconstruct this region more accurately and mitigate artifacts, so we might want to use the [rib cage mask](https://data.kitware.com/api/v1/item/5be99a2c8d777f2179a2dc4f/download) (red): ![Mm](../../documentation/docs/ExternalData/MotionMask.jpg){w=400px alt="Motion mask"} ## Respiratory signal The 4D ROOSTER algorithm requires that we associate each projection image with the instant of the respiratory cycle at which it has been acquired. We used the Amsterdam shroud solution of Lambert Zijp (described [here](http://www.creatis.insa-lyon.fr/site/fr/publications/RIT-12a)) which is implemented in RTK ``` rtkamsterdamshroud --path . \ --regexp '.*.his' \ --output shroud.mha \ --unsharp 650 rtkextractshroudsignal --input shroud.mha \ --output signal.txt \ --phase sphase.txt ``` to get the phase signal. Note that the phase must go from 0 to 1 where 0.3 corresponds to 30% in the respiratory cycle, i.e., frame 3 if you have a 10-frames 4D reconstruction or frame 6 if you have a 20-frames 4D reconstruction. The [resulting phase](https://data.kitware.com/api/v1/item/5be99af98d777f2179a2e160/download) is in green on top of the blue respiratory signal and the detected end-exhale peaks: ![Signal](../../documentation/docs/ExternalData/Signal.jpg){w=800px alt="Phase signal"} ## ROOSTER for conebeam CT reconstruction We now have all the pieces to perform a 3D + time reconstruction. The algorithm will perform "niter" iterations of the main loop, and run three inner loops at each iteration: * conjugate gradient reconstruction with "cgiter" iterations * total-variation regularization in space, with parameter "gamma_space" (the higher, the more regularized) and "tviter" iterations * total-variation regularization in time, with parameter "gamma_time" (the higher, the more regularized) and "tviter" iterations The number of iterations suggested here should work in most situations. The parameters gamma_space and gamma_time, on the other hand, must be adjusted carefully for each type of datasets (and, unfortunately, for each resolution). Unlike in analytical reconstruction methods like FDK, with 4D ROOSTER it is also very important that the reconstruction volume contains the whole patient's chest. You should therefore adapt the "size", "spacing" and "origin" parameters carefully, based on what you have observed on the blurry FDK reconstruction. ``` # Reconstruct from all projection images with 4D ROOSTER rtkfourdrooster \ -p . \ -r .*.his \ -o rooster.mha \ -g geometry.rtk \ --signal sphase.txt \ --motionmask MotionMask.mha \ --gamma_time 0.0001 \ --gamma_space 0.0001 \ --niter 30 \ --cgiter 4 \ --tviter 10 \ --spacing 2 \ --size 160 \ --frames 5 ``` Depending on the resolution you choose, and even on powerful computers, the reconstruction time can range from a few minutes (very low resolution, typically 32³ * 5, only for tests) to many hours (standard resolution, typically 256³ * 10). To speed things up, it is recommended to use the CUDA version of the forward and back projectors by running this command instead: ``` # Reconstruct from all projection images with 4D ROOSTER, using CUDA forward and back projectors rtkfourdrooster \ -p . \ -r .*.his \ -o rooster.mha \ -g geometry.rtk \ --signal sphase.txt \ --motionmask MotionMask.mha \ --fp CudaRayCast \ --bp CudaVoxelBased \ --gamma_time 0.0001 \ --gamma_space 0.0001 \ --niter 30 \ --cgiter 4 \ --tviter 10 \ --spacing 2 \ --size 160 \ --frames 5 ``` With a recent GPU, this should allow you to perform a standard resolution reconstruction in less than one hour. Note that the reconstructed volume in this example does not fully contain the attenuating object, causing hyper-attenuation artifacts on the borders of the result. To avoid these artifacts, reconstruct a larger volume (--size 256) should be fine. Note that you will have to resize your motion mask as well, as 3D the motion mask is expected to have the same size, spacing and origin as the first 3 dimensions of the 4D output. ## Motion-Aware 4D Rooster 4D ROOSTER doesn't require explicit motion information, but can take advantage of it to guide TV-regularization if motion information is available. Please refer to the tutorial on Motion-Compensated FDK to learn how to obtain a valid 4D Displacement Vector Field (DVF). Once you have it, simply adding the option --dvf "4D_DVF_Filename" to the list of rtkfourdrooster arguments will run MA-ROOSTER instead of 4D ROOSTER. ``` # Reconstruct from all projection images with MA ROOSTER rtkfourdrooster \ -p . \ -r .*.his \ -o rooster.mha \ -g geometry.rtk \ --signal sphase.txt \ --motionmask MotionMask.mha \ --gamma_time 0.0001 \ --gamma_space 0.0001 \ --niter 30 \ --cgiter 4 \ --tviter 10 \ --spacing 2 \ --size 160 \ --frames 5 \ --dvf deformationField_4D.mhd ``` Making use of the motion information adds to computation time. If you have compiled RTK with RTK_USE_CUDA = ON and have a working and CUDA-enabled nVidia GPU, it will automatically be used to speed up that part of the process. The article in which the theoretical foundations of MA-ROOSTER are presented (link to be added) contains results obtained on two patients. If you wish to reproduce these results, you can download all the necessary data here: * Original projections, log-transformed projections with the table removed, motion mask, respiratory signal, and transform matrices to change from CT to CBCT coordinates and back * [Patient 1](https://data.kitware.com/api/v1/item/5be97d688d777f2179a28e39/download) * [Patient 2](https://data.kitware.com/api/v1/item/5be99df68d777f2179a2e904/download) * Inverse-consistent 4D Displacement Vector Fields, to the end-exhale phase and from the end-exhale phase * [Patient 1](https://data.kitware.com/api/v1/item/5be989e68d777f2179a29e95/download) * [Patient 2](https://data.kitware.com/api/v1/item/5be9a1388d777f2179a2f44d/download) Extract the data of each patient in a separate folder. From the folder containing the data of patient 1, run the following command line: ``` # Reconstruct patient 1 with MA ROOSTER rtkfourdrooster \ -p . \ -r correctedProjs.mha \ -o marooster.mha \ -g geom.xml \ --signal cphase.txt \ --motionmask dilated_resampled_mm.mhd \ --gamma_time 0.0002 \ --gamma_space 0.00005 \ --niter 10 \ --cgiter 4 \ --tviter 10 \ --spacing "1, 1, 1, 1" \ --size "220, 280, 370, 10" \ --origin "-140, -140, -75, 0" \ --frames 10 \ --dvf toPhase50_4D.mhd \ --idvf fromPhase50_4D.mhd ``` From the folder containing the data of patient 2, run the following command line (only the size and origin parameters are different): ``` # Reconstruct patient 2 with MA ROOSTER rtkfourdrooster \ -p . \ -r correctedProjs.mha \ -o marooster.mha \ -g geom.xml \ --signal cphase.txt \ --motionmask dilated_resampled_mm.mhd \ --gamma_time 0.0002 \ --gamma_space 0.00005 \ --niter 10 \ --cgiter 4 \ --tviter 10 \ --spacing "1, 1, 1, 1" \ --size "285, 270, 307, 10" \ --origin "-167.5, -135, -205, 0" \ --frames 10 \ --dvf toPhase50_4D.mhd \ --idvf fromPhase50_4D.mhd \ ``` Note the option "--idvf", which allows to provide the inverse DVF. It is used to inverse warp the 4D reconstruction after the temporal regularization. MA-ROOSTER will work with and without the inverse DVF, and yield almost the same results in both cases. Not using the inverse DVF is approximately two times slower, as it requires MA-ROOSTER to perform the inverse warping by an iterative method. Again, if you have a CUDA-enabled GPU (in this case with at least 3 GB of VRAM), and have compiled RTK with RTK_USE_CUDA = ON, you can add the "--bp CudaVoxelBased" and "--fp CudaRayCast" to speed up the computation by performing the forward and back projections on the GPU. You do not need the 4D planning CT data to perform the MA-ROOSTER reconstructions. It is only required to compute the DVFs, which can be downloaded above. We do provide it anyway, in case you want to use your own method, or the one described in Motion-Compensated FDK, to extract a DVF from it: * 4D planning CT * [Patient 1](https://data.kitware.com/api/v1/item/5be98bd28d777f2179a2a279/download) * [Patient 2](https://data.kitware.com/api/v1/item/5be9a1918d777f2179a2f568/download) ================================================ FILE: applications/rtkfourdrooster/rtkfourdrooster.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkfourdrooster_ggo.h" #include "rtkGgoFunctions.h" #include "rtkFourDROOSTERConeBeamReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkSignalToInterpolationWeights.h" #include "rtkReorderProjectionsImageFilter.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkfourdrooster, args_info); using OutputPixelType = float; using DVFVectorType = itk::CovariantVector; #ifdef RTK_USE_CUDA using VolumeSeriesType = itk::CudaImage; using ProjectionStackType = itk::CudaImage; using DVFSequenceImageType = itk::CudaImage; #else using VolumeSeriesType = itk::Image; using ProjectionStackType = itk::Image; using DVFSequenceImageType = itk::Image; #endif using VolumeType = ProjectionStackType; // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // GenGetOpt can't handle default arguments for multiple arguments like size or spacing. // The only default it accepts is to set all components of a multiple argument to the same value. // Default size is 256^4, ie the number of reconstructed instants is 256. It has to be set to a more reasonable // value which is why a "frames" argument is introduced auto inputSize = itk::MakeSize(constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], constantImageSource->GetSize()[2], args_info.frames_arg); constantImageSource->SetSize(inputSize); inputFilter = constantImageSource; } TRY_AND_EXIT_ON_ITK_EXCEPTION(inputFilter->Update()) inputFilter->ReleaseDataFlagOn(); // Re-order geometry and projections // In the new order, projections with identical phases are packed together std::vector signal = rtk::ReadSignalFile(args_info.signal_arg); auto reorder = rtk::ReorderProjectionsImageFilter::New(); reorder->SetInput(reader->GetOutput()); reorder->SetInputGeometry(geometry); reorder->SetInputSignal(signal); TRY_AND_EXIT_ON_ITK_EXCEPTION(reorder->Update()) // Release the memory holding the stack of original projections reader->GetOutput()->ReleaseData(); // Compute the interpolation weights auto signalToInterpolationWeights = rtk::SignalToInterpolationWeights::New(); signalToInterpolationWeights->SetSignal(reorder->GetOutputSignal()); signalToInterpolationWeights->SetNumberOfReconstructedFrames( inputFilter->GetOutput()->GetLargestPossibleRegion().GetSize(3)); TRY_AND_EXIT_ON_ITK_EXCEPTION(signalToInterpolationWeights->Update()) // Create the 4DROOSTER filter, connect the basic inputs, and set the basic parameters // Also set the forward and back projection filters to be used using ROOSTERFilterType = rtk::FourDROOSTERConeBeamReconstructionFilter; auto rooster = ROOSTERFilterType::New(); SetForwardProjectionFromGgo(args_info, rooster.GetPointer()); SetBackProjectionFromGgo(args_info, rooster.GetPointer()); rooster->SetInputVolumeSeries(inputFilter->GetOutput()); rooster->SetCG_iterations(args_info.cgiter_arg); rooster->SetMainLoop_iterations(args_info.niter_arg); rooster->SetPhaseShift(args_info.shift_arg); rooster->SetCudaConjugateGradient(args_info.cudacg_flag); rooster->SetUseCudaCyclicDeformation(args_info.cudadvfinterpolation_flag); rooster->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); REPORT_ITERATIONS(rooster, ROOSTERFilterType, VolumeSeriesType) // Set the newly ordered arguments rooster->SetInputProjectionStack(reorder->GetOutput()); rooster->SetGeometry(reorder->GetOutputGeometry()); rooster->SetWeights(signalToInterpolationWeights->GetOutput()); rooster->SetSignal(reorder->GetOutputSignal()); // For each optional regularization step, set whether or not // it should be performed, and provide the necessary inputs // Positivity if (args_info.nopositivity_flag) rooster->SetPerformPositivity(false); else rooster->SetPerformPositivity(true); // Motion mask VolumeType::Pointer motionMask; if (args_info.motionmask_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(motionMask = itk::ReadImage(args_info.motionmask_arg)) rooster->SetMotionMask(motionMask); rooster->SetPerformMotionMask(true); } else rooster->SetPerformMotionMask(false); // Spatial TV if (args_info.gamma_space_given) { rooster->SetGammaTVSpace(args_info.gamma_space_arg); rooster->SetTV_iterations(args_info.tviter_arg); rooster->SetPerformTVSpatialDenoising(true); } else rooster->SetPerformTVSpatialDenoising(false); // Spatial wavelets if (args_info.threshold_given) { rooster->SetSoftThresholdWavelets(args_info.threshold_arg); rooster->SetOrder(args_info.order_arg); rooster->SetNumberOfLevels(args_info.levels_arg); rooster->SetPerformWaveletsSpatialDenoising(true); } else rooster->SetPerformWaveletsSpatialDenoising(false); // Temporal TV if (args_info.gamma_time_given) { rooster->SetGammaTVTime(args_info.gamma_time_arg); rooster->SetTV_iterations(args_info.tviter_arg); rooster->SetPerformTVTemporalDenoising(true); } else rooster->SetPerformTVTemporalDenoising(false); // Temporal L0 if (args_info.lambda_time_given) { rooster->SetLambdaL0Time(args_info.lambda_time_arg); rooster->SetL0_iterations(args_info.l0iter_arg); rooster->SetPerformL0TemporalDenoising(true); } else rooster->SetPerformL0TemporalDenoising(false); // Total nuclear variation if (args_info.gamma_tnv_given) { rooster->SetGammaTNV(args_info.gamma_tnv_arg); rooster->SetTV_iterations(args_info.tviter_arg); rooster->SetPerformTNVDenoising(true); } else rooster->SetPerformTNVDenoising(false); // Warping if (args_info.dvf_given) { rooster->SetPerformWarping(true); if (args_info.nn_flag) rooster->SetUseNearestNeighborInterpolationInWarping(true); // Read DVF DVFSequenceImageType::Pointer dvf; TRY_AND_EXIT_ON_ITK_EXCEPTION(dvf = itk::ReadImage(args_info.dvf_arg)) rooster->SetDisplacementField(dvf); if (args_info.idvf_given) { rooster->SetComputeInverseWarpingByConjugateGradient(false); // Read inverse DVF if provided DVFSequenceImageType::Pointer idvf; TRY_AND_EXIT_ON_ITK_EXCEPTION(idvf = itk::ReadImage(args_info.idvf_arg)) rooster->SetInverseDisplacementField(idvf); } } TRY_AND_EXIT_ON_ITK_EXCEPTION(rooster->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(rooster->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkfourdrooster/rtkfourdrooster.ggo ================================================ purpose "Reconstructs a 3D + time sequence of volumes from a projection stack and a respiratory/cardiac signal, applying TV regularization in space and time, and restricting motion to a region of interest" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "input" i "Input volume" string no option "output" o "Output file name" string yes option "niter" n "Number of main loop iterations" int no default="5" option "cgiter" - "Number of conjugate gradient nested iterations" int no default="4" option "cudacg" - "Perform conjugate gradient calculations on GPU" flag off option "cudadvfinterpolation" - "Perform DVF interpolation calculations on GPU" flag off option "nodisplaced" - "Disable the displaced detector filter" flag off section "Phase gating" option "signal" - "File containing the phase of each projection" string yes option "shift" - "Phase shift applied on the DVFs to simulate phase estimation errors" float no default="0" section "Regularization" option "nopositivity" - "Do not enforce positivity" flag off option "motionmask" - "Motion mask file: binary image with ones where movement can occur and zeros elsewhere" string no option "tviter" - "Total variation (spatial, temporal and nuclear) regularization: number of iterations" int no default="10" option "gamma_space" - "Total variation spatial regularization parameter. The larger, the smoother" double no option "threshold" - "Daubechies wavelets spatial regularization: soft threshold" float no option "order" - "Daubechies wavelets spatial regularization: order of the wavelets" int no default="5" option "levels" - "Daubechies wavelets spatial regularization: number of decomposition levels" int no default="3" option "gamma_time" - "Total variation temporal regularization parameter. The larger, the smoother" double no option "lambda_time" - "Temporal gradient's L0 norm regularization parameter. The larger, the stronger" double no option "l0iter" - "Temporal gradient's L0 norm regularization: number of iterations" int no default="5" option "gamma_tnv" - "Total nuclear variation regularization parameter. The larger, the smoother" double no section "Motion-compensation described in [ToBeWritten]" option "dvf" - "Input 4D DVF" string no option "idvf" - "Input 4D inverse DVF. Inverse transform computed by conjugate gradient if not provided" string no option "nn" - "Nearest neighbor interpolation (default is trilinear)" flag off ================================================ FILE: applications/rtkfourdsart/CMakeLists.txt ================================================ WRAP_GGO(rtkfourdsart_GGO_C rtkfourdsart.ggo ../rtkinputprojections_section.ggo ../rtk4Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkfourdsart rtkfourdsart.cxx ${rtkfourdsart_GGO_C}) target_link_libraries(rtkfourdsart RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkfourdsart) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkfourdsart/rtkfourdsart.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkfourdsart_ggo.h" #include "rtkGgoFunctions.h" #include "rtkGeneralPurposeFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkFourDSARTConeBeamReconstructionFilter.h" #include "rtkPhasesToInterpolationWeights.h" #include "rtkDisplacedDetectorImageFilter.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" # include "rtkCudaConstantVolumeSeriesSource.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkfourdsart, args_info); using OutputPixelType = float; #ifdef RTK_USE_CUDA using VolumeSeriesType = itk::CudaImage; using ProjectionStackType = itk::CudaImage; #else using VolumeSeriesType = itk::Image; using ProjectionStackType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // GenGetOpt can't handle default arguments for multiple arguments like size or spacing. // The only default it accepts is to set all components of a multiple argument to the same value. // Default size is 256^4, ie the number of reconstructed instants is 256. It has to be set to a more reasonable // value which is why a "frames" argument is introduced auto inputSize = itk::MakeSize(constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], constantImageSource->GetSize()[2], args_info.frames_arg); constantImageSource->SetSize(inputSize); inputFilter = constantImageSource; } TRY_AND_EXIT_ON_ITK_EXCEPTION(inputFilter->Update()) inputFilter->ReleaseDataFlagOn(); // Read the phases file auto phaseReader = rtk::PhasesToInterpolationWeights::New(); phaseReader->SetFileName(args_info.signal_arg); phaseReader->SetNumberOfReconstructedFrames(inputFilter->GetOutput()->GetLargestPossibleRegion().GetSize(3)); TRY_AND_EXIT_ON_ITK_EXCEPTION(phaseReader->Update()) // 4D SART reconstruction filter using FourDSARTConeBeamReconstructionFilterType = rtk::FourDSARTConeBeamReconstructionFilter; auto fourdsart = FourDSARTConeBeamReconstructionFilterType::New(); // Set the forward and back projection filters SetForwardProjectionFromGgo(args_info, fourdsart.GetPointer()); SetBackProjectionFromGgo(args_info, fourdsart.GetPointer()); fourdsart->SetInputVolumeSeries(inputFilter->GetOutput()); fourdsart->SetInputProjectionStack(reader->GetOutput()); fourdsart->SetGeometry(geometry); fourdsart->SetNumberOfIterations(args_info.niterations_arg); fourdsart->SetNumberOfProjectionsPerSubset(args_info.nprojpersubset_arg); fourdsart->SetWeights(phaseReader->GetOutput()); fourdsart->SetSignal(rtk::ReadSignalFile(args_info.signal_arg)); fourdsart->SetLambda(args_info.lambda_arg); fourdsart->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); if (args_info.positivity_flag) { fourdsart->SetEnforcePositivity(true); } REPORT_ITERATIONS(fourdsart, FourDSARTConeBeamReconstructionFilterType, VolumeSeriesType) TRY_AND_EXIT_ON_ITK_EXCEPTION(fourdsart->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(fourdsart->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkfourdsart/rtkfourdsart.ggo ================================================ purpose "Reconstructs a 4D sequence of volumes from a sequence of projections with a 4D version of the Simulatenous Algebraic Reconstruction Technique [Andersen, 1984]." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="5" option "lambda" l "Convergence factor" double no default="0.3" option "positivity" - "Enforces positivity during the reconstruction" flag off option "input" i "Input volume" string no option "nprojpersubset" - "Number of projections processed between each update of the reconstructed volume (1 for SART, several for OSSART, all for SIRT)" int no default="1" option "nodisplaced" - "Disable the displaced detector filter" flag off section "Phase gating" option "signal" - "File containing the phase of each projection" string no ================================================ FILE: applications/rtkgaincorrection/CMakeLists.txt ================================================ WRAP_GGO(rtkgaincorrection_GGO_C rtkgaincorrection.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkgaincorrection rtkgaincorrection.cxx ${rtkgaincorrection_GGO_C}) target_link_libraries(rtkgaincorrection RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkgaincorrection) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkgaincorrection/rtkgaincorrection.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkgaincorrection_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkConstantImageSource.h" #include "rtkPolynomialGainCorrectionImageFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaPolynomialGainCorrectionImageFilter.h" #endif #include #include #include #include int main(int argc, char * argv[]) { GGO(rtkgaincorrection, args_info); constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using InputImageType = itk::CudaImage; using OutputImageType = itk::CudaImage; #else using InputImageType = itk::Image; using OutputImageType = itk::Image; #endif using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); reader->ComputeLineIntegralOff(); // Don't want to preprocess data reader->SetFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputInformation()) // Input projection parameters int Nprojections = reader->GetOutput()->GetLargestPossibleRegion().GetSize(2); if (!Nprojections) { std::cout << "No DR found to process!" << std::endl; return EXIT_FAILURE; } // Load dark image std::string darkFile(args_info.calibDir_arg); darkFile.append("\\"); darkFile.append(args_info.Dark_arg); InputImageType::Pointer darkImage; auto readerDark = itk::ImageFileReader::New(); readerDark->SetFileName(darkFile); TRY_AND_EXIT_ON_ITK_EXCEPTION(readerDark->Update()) darkImage = readerDark->GetOutput(); darkImage->DisconnectPipeline(); // Get gain image std::string gainFile(args_info.calibDir_arg); gainFile.append("\\"); gainFile.append(args_info.Gain_arg); OutputImageType::Pointer gainImage; auto readerGain = itk::ImageFileReader::New(); readerGain->SetFileName(gainFile); TRY_AND_EXIT_ON_ITK_EXCEPTION(readerGain->Update()) gainImage = readerGain->GetOutput(); gainImage->DisconnectPipeline(); #ifdef RTK_USE_CUDA using GainType = rtk::CudaPolynomialGainCorrectionImageFilter; #else using GainType = rtk::PolynomialGainCorrectionImageFilter; #endif auto gainfilter = GainType::New(); gainfilter->SetDarkImage(darkImage); gainfilter->SetGainCoefficients(gainImage); gainfilter->SetK(args_info.K_arg); // Create empty volume for storing processed images auto constantSource = rtk::ConstantImageSource::New(); auto pasteFilter = itk::PasteImageFilter::New(); pasteFilter->SetDestinationImage(constantSource->GetOutput()); int bufferSize = args_info.bufferSize_arg; int Nbuffers = static_cast(std::ceil(static_cast(Nprojections) / static_cast(bufferSize))); bool first = true; for (int bid = 0; bid < Nbuffers; ++bid) { int bufferIdx = bid * bufferSize; int currentBufferSize = std::min(Nprojections - bufferIdx, bufferSize); std::cout << "Processing buffer no " << bid << " starting at image " << bufferIdx << " of size " << currentBufferSize << std::endl; InputImageType::RegionType sliceRegion = reader->GetOutput()->GetLargestPossibleRegion(); InputImageType::RegionType desiredRegion; desiredRegion.SetSize(itk::MakeSize(sliceRegion.GetSize()[0], sliceRegion.GetSize()[1], currentBufferSize)); desiredRegion.SetIndex(itk::MakeIndex(sliceRegion.GetIndex()[0], sliceRegion.GetIndex()[1], bufferIdx)); auto extract = itk::ExtractImageFilter::New(); extract->SetDirectionCollapseToIdentity(); extract->SetExtractionRegion(desiredRegion); extract->SetInput(reader->GetOutput()); TRY_AND_EXIT_ON_ITK_EXCEPTION(extract->Update()) InputImageType::Pointer buffer = extract->GetOutput(); buffer->DisconnectPipeline(); gainfilter->SetInput(extract->GetOutput()); TRY_AND_EXIT_ON_ITK_EXCEPTION(gainfilter->Update()) if (first) { // Initialization of the output volume OutputImageType::SizeType sizeInput = gainfilter->GetOutput()->GetLargestPossibleRegion().GetSize(); sizeInput[2] = Nprojections; OutputImageType::SpacingType spacingInput = gainfilter->GetOutput()->GetSpacing(); OutputImageType::PointType originInput = gainfilter->GetOutput()->GetOrigin(); OutputImageType::DirectionType imageDirection; imageDirection.SetIdentity(); constantSource->SetOrigin(originInput); constantSource->SetSpacing(spacingInput); constantSource->SetDirection(imageDirection); constantSource->SetSize(sizeInput); constantSource->SetConstant(0.); } OutputImageType::Pointer procBuffer = gainfilter->GetOutput(); procBuffer->DisconnectPipeline(); OutputImageType::IndexType current_idx = procBuffer->GetLargestPossibleRegion().GetIndex(); current_idx[2] = bufferIdx; if (first) { first = false; } else { pasteFilter->SetDestinationImage(pasteFilter->GetOutput()); } pasteFilter->SetSourceImage(procBuffer); pasteFilter->SetSourceRegion(procBuffer->GetLargestPossibleRegion()); pasteFilter->SetDestinationIndex(current_idx); TRY_AND_EXIT_ON_ITK_EXCEPTION(pasteFilter->Update()) } TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(pasteFilter->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkgaincorrection/rtkgaincorrection.ggo ================================================ purpose "Polynomial gain correction projections" option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes section "Filter parameters" option "calibDir" c "Directory containing the calibration files" string yes option "Gain" - "Gain maps filename" string yes option "Dark" - "Offset map filename" string yes option "K" - "Normalization coefficient" float yes default="1.0" option "bufferSize" - "Number of projections computed at the same time" int no default="4" ================================================ FILE: applications/rtki0estimation/CMakeLists.txt ================================================ WRAP_GGO(rtki0estimation_GGO_C rtki0estimation.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtki0estimation rtki0estimation.cxx ${rtki0estimation_GGO_C}) target_link_libraries(rtki0estimation RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtki0estimation) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtki0estimation/rtki0estimation.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtki0estimation_ggo.h" #include "rtkMacro.h" #include "rtkGgoFunctions.h" #include #include "rtkI0EstimationProjectionFilter.h" #include "rtkProjectionsReader.h" #include #include #include int main(int argc, char * argv[]) { GGO(rtki0estimation, args_info); constexpr unsigned int Dimension = 3; using InputImageType = itk::Image; auto reader = rtk::ProjectionsReader::New(); reader->SetFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputInformation()) using ExtractFilterType = itk::ExtractImageFilter; auto extract = ExtractFilterType::New(); extract->InPlaceOff(); extract->SetDirectionCollapseToSubmatrix(); extract->SetInput(reader->GetOutput()); ExtractFilterType::InputImageRegionType subsetRegion = reader->GetOutput()->GetLargestPossibleRegion(); subsetRegion = reader->GetOutput()->GetLargestPossibleRegion(); extract->SetInput(reader->GetOutput()); InputImageType::SizeType extractSize = subsetRegion.GetSize(); extractSize[2] = 1; InputImageType::IndexType start = subsetRegion.GetIndex(); std::vector I0buffer; int istep = 1; unsigned int imin = 1; auto imax = (unsigned int)subsetRegion.GetSize()[2]; if (args_info.range_given) { if ((args_info.range_arg[0] <= args_info.range_arg[2]) && (istep <= (args_info.range_arg[2] - args_info.range_arg[0]))) { imin = args_info.range_arg[0]; istep = args_info.range_arg[1]; imax = std::min(unsigned(args_info.range_arg[2]), imax); } } auto i0est = rtk::I0EstimationProjectionFilter::New(); if (args_info.lambda_given) { i0est->SetLambda(args_info.lambda_arg); } if (args_info.expected_arg != 65535) { i0est->SetExpectedI0(args_info.expected_arg); } i0est->SaveHistogramsOn(); for (unsigned int i = imin; i < imax; i += istep) { i0est->SetInput(extract->GetOutput()); start[2] = i; InputImageType::RegionType desiredRegion(start, extractSize); extract->SetExtractionRegion(desiredRegion); try { TRY_AND_EXIT_ON_ITK_EXCEPTION(i0est->UpdateLargestPossibleRegion()) } catch (itk::ExceptionObject & err) { std::cerr << "ExceptionObject caught !" << std::endl; std::cerr << err << std::endl; return EXIT_FAILURE; } I0buffer.push_back(i0est->GetI0()); I0buffer.push_back(i0est->GetI0rls()); I0buffer.push_back(i0est->GetI0fwhm()); } if (args_info.debug_given) { std::ofstream paramFile; paramFile.open(args_info.debug_arg); std::vector::const_iterator it = I0buffer.begin(); for (; it != I0buffer.end(); ++it) { paramFile << *it << ","; } paramFile.close(); } return EXIT_SUCCESS; } ================================================ FILE: applications/rtki0estimation/rtki0estimation.ggo ================================================ purpose "Reads projection images and estimates the I0 value for each of them" option "verbose" v "Verbose execution" flag off option "output" o "Output filename - not implemented" string no option "debug" d "Debug mode: output CSV file name with I0 estimates" string no option "range" - "Range of projection to analyse min,step,max" int multiple no section "Algorithm parameters" option "lambda" l "RLS estimate coefficient" double no default="0.8" option "expected" e "Expected I0 value" int no default="65535" ================================================ FILE: applications/rtkimagxgeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkimagxgeometry_GGO_C rtkimagxgeometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkimagxgeometry rtkimagxgeometry.cxx ${rtkimagxgeometry_GGO_C}) target_link_libraries(rtkimagxgeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkimagxgeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkimagxgeometry/rtkimagxgeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkimagxgeometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkImagXGeometryReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" int main(int argc, char * argv[]) { GGO(rtkimagxgeometry, args_info); // Image Type constexpr unsigned int Dimension = 3; // Create geometry reader auto imagxReader = rtk::ImagXGeometryReader>::New(); imagxReader->SetProjectionsFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); if (args_info.calibration_given) { imagxReader->SetCalibrationXMLFileName(args_info.calibration_arg); } if (args_info.room_setup_given) { imagxReader->SetRoomXMLFileName(args_info.room_setup_arg); } TRY_AND_EXIT_ON_ITK_EXCEPTION(imagxReader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(imagxReader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkimagxgeometry/rtkimagxgeometry.ggo ================================================ purpose "Creates an RTK geometry file from an iMagX acquisition." option "calibration" c "iMagX Calibration file" string no option "room_setup" s "iMagX room setup file" string no option "output" o "Output file name" string yes option "verbose" v "Verbose execution" flag off section "Projections" option "path" p "Path containing projections" string yes option "regexp" r "Regular expression to select projection files in path" string yes option "nsort" - "Numeric sort for regular expression matches" flag off option "submatch" - "Index of the submatch that will be used to sort matches" int no default="0" ================================================ FILE: applications/rtkinputprojections_group.py ================================================ import itk from itk import RTK as rtk import numpy as np __all__ = [ "add_rtkinputprojections_group", "GetProjectionsFileNamesFromArgParse", ] # Mimicks rtkinputprojections_section.ggo def add_rtkinputprojections_group(parser): rtkinputprojections_group = parser.add_argument_group( "Input projections and their pre-processing" ) rtkinputprojections_group.add_argument( "--path", "-p", help="Path containing projections", required=True ) rtkinputprojections_group.add_argument( "--regexp", "-r", help="Regular expression to select projection files in path", required=True, ) rtkinputprojections_group.add_argument( "--nsort", help="Numeric sort for regular expression matches", action="store_true", ) rtkinputprojections_group.add_argument( "--submatch", help="Index of the submatch that will be used to sort matches", type=int, default=0, ) rtkinputprojections_group.add_argument( "--nolineint", help="Disable raw to line integral conversion, just casts to float", type=bool, default=False, ) rtkinputprojections_group.add_argument( "--newdirection", help="New value of input projections (before pre-processing)", type=float, nargs="+", ) rtkinputprojections_group.add_argument( "--neworigin", help="New origin of input projections (before pre-processing)", type=float, nargs="+", ) rtkinputprojections_group.add_argument( "--newspacing", help="New spacing of input projections (before pre-processing)", type=float, nargs="+", ) rtkinputprojections_group.add_argument( "--lowercrop", help="Lower boundary crop size", type=int, nargs="+", default=[0], ) rtkinputprojections_group.add_argument( "--uppercrop", help="Upper boundary crop size", type=int, nargs="+", default=[0], ) rtkinputprojections_group.add_argument( "--binning", help="Shrink / Binning factos in each direction", type=int, nargs="+", default=[1], ) rtkinputprojections_group.add_argument( "--wpc", help="Water precorrection coefficients (default is no correction)", type=float, nargs="+", ) rtkinputprojections_group.add_argument( "--spr", help="Boellaard scatter correction: scatter-to-primary ratio", type=float, default=0, ) rtkinputprojections_group.add_argument( "--nonneg", help="Boellaard scatter correction: non-negativity threshold", type=float, ) rtkinputprojections_group.add_argument( "--airthres", help="Boellaard scatter correction: air threshold", type=float ) rtkinputprojections_group.add_argument( "--i0", help="I0 value (when assumed constant per projection), 0 means auto", type=float, ) rtkinputprojections_group.add_argument( "--idark", help="IDark value, i.e., value when beam is off", type=float, default=0, ) rtkinputprojections_group.add_argument( "--component", help="Vector component to extract, for multi-material projections", type=int, default=0, ) rtkinputprojections_group.add_argument( "--radius", help="Radius of neighborhood for conditional median filtering", type=int, nargs="+", default=[0], ) rtkinputprojections_group.add_argument( "--multiplier", help="Threshold multiplier for conditional median filtering", type=float, default=0, ) # Mimicks GetProjectionsFileNamesFromGgo def GetProjectionsFileNamesFromArgParse(args_info): # Generate file names names = itk.RegularExpressionSeriesFileNames.New() names.SetDirectory(args_info.path) names.SetNumericSort(args_info.nsort) names.SetRegularExpression(args_info.regexp) names.SetSubMatch(args_info.submatch) if args_info.verbose: print(f"Regular expression matches {len(names.GetFileNames())} file(s)...") # Check submatch in file names TODO # rtk.RegisterIOFactories() TODO fileNames = [] for fn in names.GetFileNames(): imageio = itk.ImageIOFactory.CreateImageIO(fn, itk.CommonEnums.IOFileMode_ReadMode) if imageio is None: print(f"Ignoring file: {fn}") continue fileNames.append(fn) return fileNames # Mimicks SetProjectionsReaderFromGgo def SetProjectionsReaderFromArgParse(reader, args_info): fileNames = GetProjectionsFileNamesFromArgParse(args_info) # Vector component extraction if args_info.component is not None: reader.SetVectorComponent(args_info.component) # Change image information Dimension = reader.GetOutput().GetImageDimension() if args_info.newdirection is not None: direction = [args_info.newdirection[0]] * 9 for i in range(min(9, len(args_info.newdirection))): direction[i] = args_info.newdirection[i] direction = np.array(direction).reshape((3, 3)) reader.SetDirection(itk.matrix_from_array(direction)) if args_info.newspacing is not None: spacing = itk.Vector[itk.D, Dimension]() spacing.Fill(args_info.newspacing[0]) for i in range(len(args_info.newspacing)): spacing[i] = args_info.newspacing[i] reader.SetSpacing(spacing) if args_info.neworigin is not None: origin = itk.Point[itk.D, Dimension]() origin.Fill(args_info.neworigin[0]) for i in range(len(args_info.neworigin)): origin[i] = args_info.neworigin[i] reader.SetOrigin(origin) # Crop boundaries upperCrop = [0] * Dimension lowerCrop = [0] * Dimension if args_info.lowercrop is not None: for i in range(len(args_info.lowercrop)): lowerCrop[i] = args_info.lowercrop[i] reader.SetLowerBoundaryCropSize(lowerCrop) if args_info.uppercrop is not None: for i in range(len(args_info.uppercrop)): upperCrop[i] = args_info.uppercrop[i] reader.SetUpperBoundaryCropSize(upperCrop) # Conditional median medianRadius = reader.GetMedianRadius() if args_info.radius is not None: for i in range(len(args_info.radius)): medianRadius[i] = args_info.radius[i] reader.SetMedianRadius(medianRadius) if args_info.multiplier is not None: reader.SetConditionalMedianThresholdMultiplier(args_info.multiplier) # Shrink / Binning binFactors = reader.GetShrinkFactors() if args_info.binning is not None: for i in range(len(args_info.binning)): binFactors[i] = args_info.binning[i] reader.SetShrinkFactors(binFactors) # Boellaard scatter correction if args_info.spr is not None: reader.SetScatterToPrimaryRatio(args_info.spr) if args_info.nonneg is not None: reader.SetNonNegativityConstraintThreshold(args_info.nonneg) if args_info.airthres is not None: reader.SetAirThreshold(args_info.airthres) # I0 and IDark if args_info.i0 is not None: reader.SetI0(args_info.i0) reader.SetIDark(args_info.idark) # Line integral flag if args_info.nolineint: reader.ComputeLineIntegralOff() # Water precorrection if args_info.wpc is not None: reader.SetWaterPrecorrectionCoefficients(args_info.wpc) # Pass list to projections reader reader.SetFileNames(fileNames) reader.UpdateOutputInformation() ================================================ FILE: applications/rtkinputprojections_section.ggo ================================================ section "Input projections and their pre-processing" option "path" p "Path containing projections" string yes option "regexp" r "Regular expression to select projection files in path" string yes option "nsort" - "Numeric sort for regular expression matches" flag off option "submatch" - "Index of the submatch that will be used to sort matches" int no default="0" option "nolineint" - "Disable raw to line integral conversion, just casts to float" flag off option "newdirection" - "New value of input projections (before pre-processing)" double multiple no option "neworigin" - "New origin of input projections (before pre-processing)" double multiple no option "newspacing" - "New spacing of input projections (before pre-processing)" double multiple no option "lowercrop" - "Lower boundary crop size" int multiple no default="0" option "uppercrop" - "Upper boundary crop size" int multiple no default="0" option "binning" - "Shrink / Binning factos in each direction" int multiple no default="1" option "wpc" - "Water precorrection coefficients (default is no correction)" double multiple no option "spr" - "Boellaard scatter correction: scatter-to-primary ratio" double no default="0" option "nonneg" - "Boellaard scatter correction: non-negativity threshold" double no option "airthres" - "Boellaard scatter correction: air threshold" double no option "i0" - "I0 value (when assumed constant per projection), 0 means auto" double no option "idark" - "IDark value, i.e., value when beam is off" double no default="0" option "component" - "Vector component to extract, for multi-material projections" int no default="0" option "radius" - "Radius of neighborhood for conditional median filtering" int multiple no default="0" option "multiplier" - "Threshold multiplier for conditional median filtering" double no default="0" ================================================ FILE: applications/rtkiterations_group.py ================================================ import itk __all__ = [ "add_rtkiterations_group", "SetIterationsReportFromArgParse", ] # Mimicks rtkiterations_section.ggo def add_rtkiterations_group(parser): rtkiterations_group = parser.add_argument_group("Iteration reporting") rtkiterations_group.add_argument( "--outputevery", help="Output intermediate reconstruction after some iterations", type=int, ) rtkiterations_group.add_argument( "--iterationfilename", help="File name to output intermediate iterations with {i} as a placeholder for iteration number", ) # Mimicks VerboseIterationCommand class VerboseIterationCommand: def __init__(self): self.count = 0 def callback(self): self.count = self.count + 1 print(f"Iteration {self.count}", end="\r") class VerboseEndCommand: def callback(self): print("") # Mimicks OutputIterationCommand class OutputIterationCommand: def __init__(self, reconstruction_filter, outputevery, iterationfilename): self.count = 0 self.reconstruction_filter = reconstruction_filter self.outputevery = outputevery self.iterationfilename = iterationfilename def callback(self): self.count = self.count + 1 if self.count % self.outputevery == 0: output = self.reconstruction_filter.GetOutput() OutputImageType = type(output) if hasattr(output, "GetCudaDataManager"): OutputImageType = OutputImageType.__bases__[0] writer = itk.ImageFileWriter[OutputImageType].New() writer.SetInput(output) writer.SetFileName(self.iterationfilename.format(i=self.count)) writer.Update() # Mimicks macro REPORT_ITERATIONS def SetIterationsReportFromArgParse(args_info, filt): if args_info.verbose: cmd = VerboseIterationCommand() filt.AddObserver(itk.IterationEvent(), cmd.callback) cmd = VerboseEndCommand() filt.AddObserver(itk.EndEvent(), cmd.callback) if args_info.outputevery is not None: cmd = OutputIterationCommand( filt, args_info.outputevery, args_info.iterationfilename ) filt.AddObserver(itk.IterationEvent(), cmd.callback) ================================================ FILE: applications/rtkiterations_section.ggo ================================================ section "Iteration reporting" option "output-every" - "Output intermediate reconstruction after some iterations" int no option "iteration-file-name" - "File name to output intermediate iterations with %d as a placeholder for iteration number" string no ================================================ FILE: applications/rtkiterativefdk/CMakeLists.txt ================================================ WRAP_GGO(rtkiterativefdk_GGO_C rtkiterativefdk.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkiterativefdk rtkiterativefdk.cxx ${rtkiterativefdk_GGO_C}) target_link_libraries(rtkiterativefdk RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkiterativefdk) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkiterativefdk/rtkiterativefdk.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkiterativefdk_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkIterativeFDKConeBeamReconstructionFilter.h" #ifdef RTK_USE_CUDA # include "rtkCudaIterativeFDKConeBeamReconstructionFilter.h" #endif #include #include int main(int argc, char * argv[]) { GGO(rtkiterativefdk, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); if (args_info.verbose_flag) std::cout << "Reading... " << std::endl; TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->Update()) // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create reconstructed image using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); bool enforcePositivity = false; if (args_info.positivity_flag) enforcePositivity = true; else enforcePositivity = false; // Since the last template argument for IterativeFDKConeBeamReconstructionFilter is // double for the CPU version, and float for the CUDA one, we cannot have a single // pointer for both possibilities. In order to set the options only once, // we therefore create a macro, exactly as in rtkfdk.cxx #define SET_IFDK_OPTIONS(f) \ f->SetInput(0, constantImageSource->GetOutput()); \ f->SetInput(1, reader->GetOutput()); \ f->SetGeometry(geometry); \ SetForwardProjectionFromGgo(args_info, f.GetPointer()); \ f->SetNumberOfIterations(args_info.niterations_arg); \ f->SetTruncationCorrection(args_info.pad_arg); \ f->SetHannCutFrequency(args_info.hann_arg); \ f->SetHannCutFrequencyY(args_info.hannY_arg); \ f->SetProjectionSubsetSize(args_info.subsetsize_arg); \ f->SetLambda(args_info.lambda_arg); \ f->SetEnforcePositivity(enforcePositivity); \ f->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); // Create Iterative FDK filter and connect it using IFDKCPUType = rtk::IterativeFDKConeBeamReconstructionFilter; IFDKCPUType::Pointer ifdk; #ifdef RTK_USE_CUDA using IFDKCUDAType = rtk::CudaIterativeFDKConeBeamReconstructionFilter; IFDKCUDAType::Pointer ifdkCUDA; #endif OutputImageType::Pointer IFDKOutputPointer; if (!strcmp(args_info.hardware_arg, "cpu")) { ifdk = IFDKCPUType::New(); REPORT_ITERATIONS(ifdk, IFDKCPUType, OutputImageType) SET_IFDK_OPTIONS(ifdk); IFDKOutputPointer = ifdk->GetOutput(); } #ifdef RTK_USE_CUDA else if (!strcmp(args_info.hardware_arg, "cuda")) { ifdkCUDA = IFDKCUDAType::New(); REPORT_ITERATIONS(ifdkCUDA, IFDKCPUType, OutputImageType) SET_IFDK_OPTIONS(ifdkCUDA); IFDKOutputPointer = ifdkCUDA->GetOutput(); } #endif // Write if (args_info.verbose_flag) std::cout << "Reconstructing and writing... " << std::endl; TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(IFDKOutputPointer, args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkiterativefdk/rtkiterativefdk.ggo ================================================ purpose "Reconstructs a 3D volume from a sequence of projections [Feldkamp, David, Kress, 1984]." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "hardware" - "Hardware used for computation" values="cpu","cuda" no default="cpu" option "subsetsize" - "Streaming option: number of projections processed at a time" int no default="16" option "niterations" n "Number of iterations" int no default="3" option "lambda" - "Convergence factor" double no default="0.3" option "positivity" - "Enforces positivity during the reconstruction" flag off option "nodisplaced" - "Disable the displaced detector filter" flag off section "Ramp filter" option "pad" - "Data padding parameter to correct for truncation" double no default="0.0" option "hann" - "Cut frequency for hann window in ]0,1] (0.0 disables it)" double no default="0.0" option "hannY" - "Cut frequency for hann window in ]0,1] (0.0 disables it)" double no default="0.0" section "Projectors" option "fp" f "Forward projection method" values="Joseph","CudaRayCast","JosephAttenuated","Zeng" enum no default="Joseph" option "attenuationmap" - "Attenuation map relative to the volume to perfom the attenuation correction" string no option "sigmazero" - "PSF value at a distance of 0 meter of the detector" double no option "alphapsf" - "Slope of the PSF against the detector distance" double no option "inferiorclipimage" - "Value of the inferior clip of the ray for each pixel of the projections (only with Joseph-based projector)" string no option "superiorclipimage" - "Value of the superior clip of the ray for each pixel of the projections (only with Joseph-based projector)" string no option "step" - "Step size along ray (for CudaRayCast only)" double no default="1" ================================================ FILE: applications/rtklagcorrection/CMakeLists.txt ================================================ WRAP_GGO(rtklagcorrection_GGO_C rtklagcorrection.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtklagcorrection rtklagcorrection.cxx ${rtklagcorrection_GGO_C}) target_link_libraries(rtklagcorrection RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtklagcorrection) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtklagcorrection/rtklagcorrection.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtklagcorrection_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #ifdef RTK_USE_CUDA # include "rtkCudaLagCorrectionImageFilter.h" # include #else # include "rtkLagCorrectionImageFilter.h" #endif using namespace rtk; #include const unsigned VModelOrder = 4; int main(int argc, char * argv[]) { GGO(rtklagcorrection, args_info); constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif using VectorType = itk::Vector; // Parameter type always float/double // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); reader->ComputeLineIntegralOff(); // Don't want to preprocess data reader->SetFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->Update()) if ((args_info.coefficients_given != VModelOrder) && (args_info.rates_given != VModelOrder)) { std::cerr << "Expecting 4 lags rates and coefficients values" << std::endl; return EXIT_FAILURE; } VectorType a, b; for (unsigned int i = 0; i < VModelOrder; ++i) { a[i] = args_info.rates_arg[i]; b[i] = args_info.coefficients_arg[i]; } #ifdef RTK_USE_CUDA using LagType = rtk::CudaLagCorrectionImageFilter; #else using LagType = rtk::LagCorrectionImageFilter; #endif auto lagfilter = LagType::New(); lagfilter->SetInput(reader->GetOutput()); lagfilter->SetCoefficients(a, b); lagfilter->InPlaceOff(); TRY_AND_EXIT_ON_ITK_EXCEPTION(lagfilter->Update()) // Streaming filter auto streamer = itk::StreamingImageFilter::New(); streamer->SetInput(lagfilter->GetOutput()); streamer->SetNumberOfStreamDivisions(100); // Save corrected projections TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(streamer->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtklagcorrection/rtklagcorrection.ggo ================================================ purpose "4th order LTI Lag correction" option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes option "rates" a "Lag rates (a0, a1, a2, a3)" float multiple yes option "coefficients" c "Lag coefficients (b0, b1, b2, b3)" float multiple yes ================================================ FILE: applications/rtkmaskcollimation/CMakeLists.txt ================================================ WRAP_GGO(rtkmaskcollimation_GGO_C rtkmaskcollimation.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkmaskcollimation rtkmaskcollimation.cxx ${rtkmaskcollimation_GGO_C}) target_link_libraries(rtkmaskcollimation RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkmaskcollimation) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkmaskcollimation/rtkmaskcollimation.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkmaskcollimation_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkMaskCollimationImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtkmaskcollimation, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Create projection image filter auto ofm = rtk::MaskCollimationImageFilter::New(); ofm->SetInput(reader->GetOutput()); ofm->SetGeometry(geometry); // Write auto writer = itk::ImageFileWriter::New(); writer->SetFileName(args_info.output_arg); writer->SetInput(ofm->GetOutput()); if (args_info.verbose_flag) std::cout << "Projecting and writing... " << std::flush; TRY_AND_EXIT_ON_ITK_EXCEPTION(writer->UpdateOutputInformation()) writer->SetNumberOfStreamDivisions(reader->GetOutput()->GetLargestPossibleRegion().GetSize(2)); TRY_AND_EXIT_ON_ITK_EXCEPTION(writer->Update()) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkmaskcollimation/rtkmaskcollimation.ggo ================================================ purpose "Masks out the collimator from the projections" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output projections file name" string yes ================================================ FILE: applications/rtkmcrooster/CMakeLists.txt ================================================ WRAP_GGO(rtkmcrooster_GGO_C rtkmcrooster.ggo ../rtkinputprojections_section.ggo ../rtk4Doutputimage_section.ggo ../rtkprojectors_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkmcrooster rtkmcrooster.cxx ${rtkmcrooster_GGO_C}) target_link_libraries(rtkmcrooster RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkmcrooster) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkmcrooster/rtkmcrooster.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkmcrooster_ggo.h" #include "rtkGgoFunctions.h" #include "rtkGeneralPurposeFunctions.h" #include "rtkMotionCompensatedFourDROOSTERConeBeamReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkSignalToInterpolationWeights.h" #include "rtkReorderProjectionsImageFilter.h" #include "rtkWarpSequenceImageFilter.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkmcrooster, args_info); using OutputPixelType = float; using DVFVectorType = itk::CovariantVector; #ifdef RTK_USE_CUDA using VolumeSeriesType = itk::CudaImage; using ProjectionStackType = itk::CudaImage; using DVFSequenceImageType = itk::CudaImage; using DVFImageType = itk::CudaImage; #else using VolumeSeriesType = itk::Image; using ProjectionStackType = itk::Image; using DVFSequenceImageType = itk::Image; using DVFImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume using InputReaderType = itk::ImageFileReader; auto inputReader = InputReaderType::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // GenGetOpt can't handle default arguments for multiple arguments like size or spacing. // The only default it accepts is to set all components of a multiple argument to the same value. // Default size is 256^4, ie the number of reconstructed instants is 256. It has to be set to a more reasonable // value which is why a "frames" argument is introduced auto inputSize = itk::MakeSize(constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], constantImageSource->GetSize()[2], args_info.frames_arg); constantImageSource->SetSize(inputSize); inputFilter = constantImageSource; } TRY_AND_EXIT_ON_ITK_EXCEPTION(inputFilter->Update()) inputFilter->ReleaseDataFlagOn(); // Re-order geometry and projections // In the new order, projections with identical phases are packed together std::vector signal = rtk::ReadSignalFile(args_info.signal_arg); auto reorder = rtk::ReorderProjectionsImageFilter::New(); reorder->SetInput(reader->GetOutput()); reorder->SetInputGeometry(geometry); reorder->SetInputSignal(signal); TRY_AND_EXIT_ON_ITK_EXCEPTION(reorder->Update()) // Release the memory holding the stack of original projections reader->GetOutput()->ReleaseData(); // Compute the interpolation weights auto signalToInterpolationWeights = rtk::SignalToInterpolationWeights::New(); signalToInterpolationWeights->SetSignal(reorder->GetOutputSignal()); signalToInterpolationWeights->SetNumberOfReconstructedFrames( inputFilter->GetOutput()->GetLargestPossibleRegion().GetSize(3)); TRY_AND_EXIT_ON_ITK_EXCEPTION(signalToInterpolationWeights->Update()) // Create the 4DROOSTER filter, connect the basic inputs, and set the basic parameters auto mcrooster = rtk::MotionCompensatedFourDROOSTERConeBeamReconstructionFilter::New(); SetForwardProjectionFromGgo(args_info, mcrooster.GetPointer()); SetBackProjectionFromGgo(args_info, mcrooster.GetPointer()); mcrooster->SetInputVolumeSeries(inputFilter->GetOutput()); mcrooster->SetCG_iterations(args_info.cgiter_arg); mcrooster->SetMainLoop_iterations(args_info.niter_arg); mcrooster->SetCudaConjugateGradient(args_info.cudacg_flag); mcrooster->SetUseCudaCyclicDeformation(args_info.cudadvfinterpolation_flag); mcrooster->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); // Set the newly ordered arguments mcrooster->SetInputProjectionStack(reorder->GetOutput()); mcrooster->SetGeometry(reorder->GetOutputGeometry()); mcrooster->SetWeights(signalToInterpolationWeights->GetOutput()); mcrooster->SetSignal(reorder->GetOutputSignal()); // For each optional regularization step, set whether or not // it should be performed, and provide the necessary inputs // Positivity if (args_info.nopositivity_flag) mcrooster->SetPerformPositivity(false); else mcrooster->SetPerformPositivity(true); // Motion mask using InputReaderType = itk::ImageFileReader; if (args_info.motionmask_given) { auto motionMaskReader = InputReaderType::New(); motionMaskReader->SetFileName(args_info.motionmask_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(motionMaskReader->Update()) mcrooster->SetMotionMask(motionMaskReader->GetOutput()); mcrooster->SetPerformMotionMask(true); } else mcrooster->SetPerformMotionMask(false); // Spatial TV if (args_info.gamma_space_given) { mcrooster->SetGammaTVSpace(args_info.gamma_space_arg); mcrooster->SetTV_iterations(args_info.tviter_arg); mcrooster->SetPerformTVSpatialDenoising(true); } else mcrooster->SetPerformTVSpatialDenoising(false); // Spatial wavelets if (args_info.threshold_given) { mcrooster->SetSoftThresholdWavelets(args_info.threshold_arg); mcrooster->SetOrder(args_info.order_arg); mcrooster->SetNumberOfLevels(args_info.levels_arg); mcrooster->SetPerformWaveletsSpatialDenoising(true); } else mcrooster->SetPerformWaveletsSpatialDenoising(false); // Temporal TV if (args_info.gamma_time_given) { mcrooster->SetGammaTVTime(args_info.gamma_time_arg); mcrooster->SetTV_iterations(args_info.tviter_arg); mcrooster->SetPerformTVTemporalDenoising(true); } else mcrooster->SetPerformTVTemporalDenoising(false); // Temporal L0 if (args_info.lambda_time_arg) { mcrooster->SetLambdaL0Time(args_info.lambda_time_arg); mcrooster->SetL0_iterations(args_info.l0iter_arg); mcrooster->SetPerformL0TemporalDenoising(true); } else mcrooster->SetPerformL0TemporalDenoising(false); // Read DVF DVFSequenceImageType::Pointer dvf; TRY_AND_EXIT_ON_ITK_EXCEPTION(dvf = itk::ReadImage(args_info.dvf_arg)) mcrooster->SetDisplacementField(dvf); // Read inverse DVF if provided DVFSequenceImageType::Pointer idvf; TRY_AND_EXIT_ON_ITK_EXCEPTION(idvf = itk::ReadImage(args_info.idvf_arg)) mcrooster->SetInverseDisplacementField(idvf); TRY_AND_EXIT_ON_ITK_EXCEPTION(mcrooster->Update()) auto warp = rtk::WarpSequenceImageFilter::New(); if (args_info.nofinalwarp_flag) { // MCROOSTER outputs a motion-compensated reconstruction TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(mcrooster->GetOutput(), args_info.output_arg)) } else { // Warp the output of MCROOSTER with the inverse field so // that it is similar to that of rtkfourdrooster warp->SetInput(mcrooster->GetOutput()); warp->SetDisplacementField(idvf); TRY_AND_EXIT_ON_ITK_EXCEPTION(warp->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(warp->GetOutput(), args_info.output_arg)) } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkmcrooster/rtkmcrooster.ggo ================================================ purpose "Reconstructs a 3D + time sequence of volumes from a projection stack and a respiratory/cardiac signal, applying TV regularization in space and time, and restricting motion to a region of interest" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "input" i "Input volume" string no option "output" o "Output file name" string yes option "niter" n "Number of main loop iterations" int no default="5" option "cgiter" - "Number of conjugate gradient nested iterations" int no default="4" option "cudacg" - "Perform conjugate gradient calculations on GPU" flag off option "cudadvfinterpolation" - "Perform DVF interpolation calculations on GPU" flag off option "nodisplaced" - "Disable the displaced detector filter" flag off section "Phase gating" option "signal" - "File containing the phase of each projection" string yes section "Regularization" option "nopositivity" - "Do not enforce positivity" flag off option "motionmask" - "Motion mask file: binary image with ones where movement can occur and zeros elsewhere" string no option "tviter" - "Total variation (spatial and temporal) regularization: number of iterations" int no default="10" option "gamma_space" - "Total variation spatial regularization parameter. The larger, the smoother" double no option "threshold" - "Daubechies wavelets spatial regularization: soft threshold" float no option "order" - "Daubechies wavelets spatial regularization: order of the wavelets" int no default="5" option "levels" - "Daubechies wavelets spatial regularization: number of decomposition levels" int no default="3" option "gamma_time" - "Total variation temporal regularization parameter. The larger, the smoother" double no option "lambda_time" - "Temporal gradient's L0 norm regularization parameter. The larger, the stronger" double no option "l0iter" - "Temporal gradient's L0 norm regularization: number of iterations" int no default="5" section "Motion-compensation" option "dvf" - "Input 4D DVF" string yes option "idvf" - "Input 4D inverse DVF" string yes option "nofinalwarp" - "Outputs the motion-compensated sequence, without warping it" flag off ================================================ FILE: applications/rtkmotioncompensatedfourdconjugategradient/CMakeLists.txt ================================================ WRAP_GGO(rtkmotioncompensatedfourdconjugategradient_GGO_C rtkmotioncompensatedfourdconjugategradient.ggo ../rtkinputprojections_section.ggo ../rtk4Doutputimage_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkmotioncompensatedfourdconjugategradient rtkmotioncompensatedfourdconjugategradient.cxx ${rtkmotioncompensatedfourdconjugategradient_GGO_C}) target_link_libraries(rtkmotioncompensatedfourdconjugategradient RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkmotioncompensatedfourdconjugategradient) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkmotioncompensatedfourdconjugategradient/rtkmotioncompensatedfourdconjugategradient.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkmotioncompensatedfourdconjugategradient_ggo.h" #include "rtkGgoFunctions.h" #include "rtkMotionCompensatedFourDConjugateGradientConeBeamReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkPhasesToInterpolationWeights.h" #include "rtkWarpSequenceImageFilter.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkmotioncompensatedfourdconjugategradient, args_info); using OutputPixelType = float; using DVFVectorType = itk::CovariantVector; #ifdef RTK_USE_CUDA using VolumeSeriesType = itk::CudaImage; using ProjectionStackType = itk::CudaImage; using DVFSequenceImageType = itk::CudaImage; using DVFImageType = itk::CudaImage; #else using VolumeSeriesType = itk::Image; using ProjectionStackType = itk::Image; using DVFSequenceImageType = itk::Image; using DVFImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo( constantImageSource, args_info); // GenGetOpt can't handle default arguments for multiple arguments like size or spacing. // The only default it accepts is to set all components of a multiple argument to the same value. // Default size is 256^4, ie the number of reconstructed instants is 256. It has to be set to a more reasonable // value which is why a "frames" argument is introduced constantImageSource->SetSize(itk::MakeSize(constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], constantImageSource->GetSize()[2], args_info.frames_arg)); inputFilter = constantImageSource; } TRY_AND_EXIT_ON_ITK_EXCEPTION(inputFilter->Update()) inputFilter->ReleaseDataFlagOn(); // Read the phases file auto phaseReader = rtk::PhasesToInterpolationWeights::New(); phaseReader->SetFileName(args_info.signal_arg); phaseReader->SetNumberOfReconstructedFrames(inputFilter->GetOutput()->GetLargestPossibleRegion().GetSize(3)); TRY_AND_EXIT_ON_ITK_EXCEPTION(phaseReader->Update()) // Create the mcfourdcg filter, connect the basic inputs, and set the basic parameters using MCFourDCGFilterType = rtk::MotionCompensatedFourDConjugateGradientConeBeamReconstructionFilter; auto mcfourdcg = MCFourDCGFilterType::New(); mcfourdcg->SetInputVolumeSeries(inputFilter->GetOutput()); mcfourdcg->SetInputProjectionStack(reader->GetOutput()); mcfourdcg->SetGeometry(geometry); mcfourdcg->SetWeights(phaseReader->GetOutput()); mcfourdcg->SetNumberOfIterations(args_info.niter_arg); mcfourdcg->SetCudaConjugateGradient(args_info.cudacg_flag); mcfourdcg->SetSignal(rtk::ReadSignalFile(args_info.signal_arg)); mcfourdcg->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); REPORT_ITERATIONS(mcfourdcg, MCFourDCGFilterType, VolumeSeriesType) // Read DVF DVFSequenceImageType::Pointer dvf; TRY_AND_EXIT_ON_ITK_EXCEPTION(dvf = itk::ReadImage(args_info.dvf_arg)) mcfourdcg->SetDisplacementField(dvf); // Read inverse DVF if provided DVFSequenceImageType::Pointer idvf; TRY_AND_EXIT_ON_ITK_EXCEPTION(idvf = itk::ReadImage(args_info.idvf_arg)) mcfourdcg->SetInverseDisplacementField(idvf); TRY_AND_EXIT_ON_ITK_EXCEPTION(mcfourdcg->Update()) // The mcfourdcg filter reconstructs a static 4D volume (if the DVFs perfectly model the actual motion) // Warp this sequence with the inverse DVF so as to obtain a result similar to the classical 4D CG filter auto warp = rtk::WarpSequenceImageFilter::New(); warp->SetInput(mcfourdcg->GetOutput()); warp->SetDisplacementField(idvf); TRY_AND_EXIT_ON_ITK_EXCEPTION(warp->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(warp->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkmotioncompensatedfourdconjugategradient/rtkmotioncompensatedfourdconjugategradient.ggo ================================================ purpose "Performs a motion-compensated 4D reconstruction by the conjugate gradient method" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "input" i "Input volume" string no option "output" o "Output file name" string yes option "niter" n "Number of main loop iterations" int no default="5" option "cudacg" - "Perform conjugate gradient calculations on GPU" flag off option "nodisplaced" - "Disable the displaced detector filter" flag off section "Phase gating" option "signal" - "File containing the phase of each projection" string yes section "Motion-compensation described in [ToBeWritten]" option "dvf" - "Input 4D DVF" string no option "idvf" - "Input 4D inverse DVF. Inverse transform computed by conjugate gradient if not provided" string no ================================================ FILE: applications/rtkorageometry/CMakeLists.txt ================================================ WRAP_GGO(rtkorageometry_GGO_C rtkorageometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkorageometry rtkorageometry.cxx ${rtkorageometry_GGO_C}) target_link_libraries(rtkorageometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkorageometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkorageometry/rtkorageometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkorageometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkOraGeometryReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" int main(int argc, char * argv[]) { GGO(rtkorageometry, args_info); rtk::OraGeometryReader::MarginVectorType margin; margin.Fill(args_info.margin_arg[0]); for (unsigned int i = 0; i < std::min(args_info.margin_given, margin.GetVectorDimension()); i++) margin[i] = args_info.margin_arg[i]; // Create geometry reader auto oraReader = rtk::OraGeometryReader::New(); oraReader->SetProjectionsFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); oraReader->SetCollimationMargin(margin); oraReader->SetOptiTrackObjectID(args_info.optitrack_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(oraReader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry( const_cast(oraReader->GetGeometry()), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkorageometry/rtkorageometry.ggo ================================================ purpose "Creates an RTK geometry file from a sequence of ora.xml files (radART / medPhoton file format)." option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes option "margin" m "Collimation margin (uinf, usup, vinf, vsup)" double no multiple default="0." option "optitrack" - "OptiTrack object ID (unused by default)" int no default="-1" section "Projections" option "path" p "Path containing projections" string yes option "regexp" r "Regular expression to select projection files in path" string yes option "nsort" - "Numeric sort for regular expression matches" flag off option "submatch" - "Index of the submatch that will be used to sort matches" int no default="0" ================================================ FILE: applications/rtkorageometry/rtkorageometry.py ================================================ #!/usr/bin/env python import argparse import itk from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Creates an RTK geometry file from a Varian OBI acquisition." ) parser.add_argument("--verbose", "-v", help="Verbose execution", type=bool) parser.add_argument( "--xml_file", "-x", help="Varian OBI XML information file on projections" ) parser.add_argument("--output", "-o", help="Output file name", required=True) parser.add_argument( "--path", "-p", help="Path containing projections", required=True ) parser.add_argument( "--regexp", "-r", help="Regular expression to select projection files in path" ) parser.add_argument( "--margin", "-m", help="Collimation margin (uinf, usup, vinf, vsup)", type=float, nargs="+", default=0.0, ) # Parse the command line arguments return parser def process(args: argparse.Namespace): margin = args.margin if type(margin) is float: margin = [margin] for i in range(len(margin), 4, 1): margin.append(margin[0]) print(margin) names = itk.RegularExpressionSeriesFileNames.New() names.SetDirectory(args.path) names.SetRegularExpression(args.regexp) reader = rtk.OraGeometryReader.New() reader.SetProjectionsFileNames(names.GetFileNames()) reader.SetCollimationMargin(margin) reader.UpdateOutputData() rtk.write_geometry(reader.GetGeometry(), args.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkosem/CMakeLists.txt ================================================ WRAP_GGO(rtkosem_GGO_C rtkosem.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkosem rtkosem.cxx ${rtkosem_GGO_C}) target_link_libraries(rtkosem RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkosem) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkosem/rtkosem.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkosem_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkOSEMConeBeamReconstructionFilter.h" #include "rtkPhaseGatingImageFilter.h" #include "rtkIterationCommands.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkosem, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); constantImageSource->SetConstant(1.); inputFilter = constantImageSource; } // OSEM reconstruction filter auto osem = rtk::OSEMConeBeamReconstructionFilter::New(); // Set the forward and back projection filters SetForwardProjectionFromGgo(args_info, osem.GetPointer()); SetBackProjectionFromGgo(args_info, osem.GetPointer()); osem->SetInput(inputFilter->GetOutput()); osem->SetInput(1, reader->GetOutput()); osem->SetGeometry(geometry); if (args_info.betaregularization_given) osem->SetBetaRegularization(args_info.betaregularization_arg); osem->SetNumberOfIterations(args_info.niterations_arg); osem->SetNumberOfProjectionsPerSubset(args_info.nprojpersubset_arg); osem->SetStoreNormalizationImages(!args_info.nostorenormalizationimages_flag); REPORT_ITERATIONS(osem, rtk::OSEMConeBeamReconstructionFilter, OutputImageType) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(osem->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkosem/rtkosem.ggo ================================================ purpose "Reconstructs a 3D volume from a sequence of projections with Ordered subset expectation maximization." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="5" option "input" i "Input volume" string no option "nprojpersubset" - "Number of projections processed between each update of the reconstructed volume (several for OSEM, all for MLEM)" int no default="1" option "betaregularization" - "Hyperparameter for the regularization" float no default="0" option "nostorenormalizationimages" - "Do not store the normalization images during the reconstruction" flag off ================================================ FILE: applications/rtkoverlayphaseandshroud/CMakeLists.txt ================================================ WRAP_GGO(rtkoverlayphaseandshroud_GGO_C rtkoverlayphaseandshroud.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkoverlayphaseandshroud rtkoverlayphaseandshroud.cxx ${rtkoverlayphaseandshroud_GGO_C}) target_link_libraries(rtkoverlayphaseandshroud RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkoverlayphaseandshroud) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkoverlayphaseandshroud/rtkoverlayphaseandshroud.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkoverlayphaseandshroud_ggo.h" #include "rtkMacro.h" #include #include #include #include #include #include #include #include int main(int argc, char * argv[]) { GGO(rtkoverlayphaseandshroud, args_info); using RGBPixelType = itk::RGBPixel; constexpr unsigned int Dimension = 2; using InputImageType = itk::Image; using OutputImageType = itk::Image; // Read InputImageType::Pointer readImage; TRY_AND_EXIT_ON_ITK_EXCEPTION(readImage = itk::ReadImage(args_info.input_arg)) // Read signal file auto signalReader = itk::CSVArray2DFileReader::New(); signalReader->SetFileName(args_info.signal_arg); signalReader->SetFieldDelimiterCharacter(';'); signalReader->HasRowHeadersOff(); signalReader->HasColumnHeadersOff(); TRY_AND_EXIT_ON_ITK_EXCEPTION(signalReader->Update()) std::vector signal = signalReader->GetArray2DDataObject()->GetColumn(0); // Locate the minima in the signal std::vector minima; minima.push_back(false); for (unsigned int i = 1; i < signal.size(); i++) { minima.push_back((signal[i] < signal[i - 1])); } // Create output RGB image auto RGBout = OutputImageType::New(); RGBout->SetRegions(readImage->GetLargestPossibleRegion()); RGBout->Allocate(); // Compute min and max of shroud to scale output itk::ImageRegionConstIterator itIn(readImage, readImage->GetLargestPossibleRegion()); itk::ImageRegionIteratorWithIndex itOut(RGBout, RGBout->GetLargestPossibleRegion()); double min = itk::NumericTraits::max(); double max = -itk::NumericTraits::max(); while (!itIn.IsAtEnd()) { double currentPixel = itIn.Get(); if (currentPixel < min) min = currentPixel; if (currentPixel > max) max = currentPixel; ++itIn; } // Fill the output itIn.GoToBegin(); itOut.GoToBegin(); while (!itOut.IsAtEnd()) { RGBPixelType pix; if (minima[itOut.GetIndex()[1]]) // If it is a minimum, draw a red pixel { pix.Fill(0); pix.SetRed(255); } else // Otherwise, copy the input image, scaled to [0 - 255], in all channels { pix.Fill(floor((itIn.Get() - min) * 255.0 / (max - min))); } itOut.Set(pix); ++itIn; ++itOut; } TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(RGBout, args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkoverlayphaseandshroud/rtkoverlayphaseandshroud.ggo ================================================ purpose "Generates an RGB image showing the phase peaks on top of the shroud" option "verbose" v "Verbose execution" flag off option "input" i "Input shroud image file name" string yes option "output" o "Output RGB image file name (.png, .jpg, ...)" string yes option "signal" - "File containing the phase of each projection" string yes ================================================ FILE: applications/rtkprojectgeometricphantom/CMakeLists.txt ================================================ WRAP_GGO(rtkprojectgeometricphantom_GGO_C rtkprojectgeometricphantom.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkprojectgeometricphantom rtkprojectgeometricphantom.cxx ${rtkprojectgeometricphantom_GGO_C}) target_link_libraries(rtkprojectgeometricphantom RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkprojectgeometricphantom) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkprojectgeometricphantom/rtkprojectgeometricphantom.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkprojectgeometricphantom_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkRayEllipsoidIntersectionImageFilter.h" #include "rtkProjectGeometricPhantomImageFilter.h" #include int main(int argc, char * argv[]) { GGO(rtkprojectgeometricphantom, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create a stack of empty projection images using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); // Adjust size according to geometry constantImageSource->SetSize(itk::MakeSize( constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], geometry->GetGantryAngles().size())); using PPCType = rtk::ProjectGeometricPhantomImageFilter; // Offset, scale, rotation PPCType::VectorType offset(0.); if (args_info.offset_given) { if (args_info.offset_given > 3) { std::cerr << "--offset needs up to 3 values" << std::endl; exit(EXIT_FAILURE); } offset[0] = args_info.offset_arg[0]; offset[1] = args_info.offset_arg[1]; offset[2] = args_info.offset_arg[2]; } PPCType::VectorType scale; scale.Fill(args_info.phantomscale_arg[0]); if (args_info.phantomscale_given) { if (args_info.phantomscale_given > 3) { std::cerr << "--phantomscale needs up to 3 values" << std::endl; exit(EXIT_FAILURE); } for (unsigned int i = 0; i < std::min(args_info.phantomscale_given, Dimension); i++) scale[i] = args_info.phantomscale_arg[i]; } PPCType::RotationMatrixType rot; rot.SetIdentity(); if (args_info.rotation_given) { if (args_info.rotation_given != 9) { std::cerr << "--rotation needs exactly 9 values" << std::endl; exit(EXIT_FAILURE); } for (unsigned int i = 0; i < Dimension; i++) for (unsigned int j = 0; j < Dimension; j++) rot[i][j] = args_info.rotation_arg[i * Dimension + j]; } auto ppc = PPCType::New(); ppc->SetInput(constantImageSource->GetOutput()); ppc->SetGeometry(geometry); ppc->SetPhantomScale(scale); ppc->SetOriginOffset(offset); ppc->SetRotationMatrix(rot); ppc->SetConfigFile(args_info.phantomfile_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(ppc->Update()) // Write if (args_info.verbose_flag) std::cout << "Projecting and writing... " << std::flush; TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(ppc->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkprojectgeometricphantom/rtkprojectgeometricphantom.ggo ================================================ purpose "Computes projections through a 3D phantom described by a file, according to a geometry" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output projections file name" string yes option "phantomfile" - "Configuration parameteres for the phantom" string yes option "phantomscale" - "Scaling factor for the phantom dimensions" double multiple no default="1." option "offset" - "3D spatial offset of the phantom center" double multiple no option "rotation" - "Rotation matrix for the phantom" double multiple no ================================================ FILE: applications/rtkprojectgeometricphantom/rtkprojectgeometricphantom.py ================================================ import argparse import sys import itk from itk import RTK as rtk import numpy as np def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Computes projections through a 3D phantom described by a file, according to a geometry" ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--geometry", "-g", help="XML geometry file name", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output projections file name", type=str, required=True ) parser.add_argument( "--phantomfile", help="Configuration parameters for the phantom", type=str, required=True, ) parser.add_argument( "--phantomscale", help="Scaling factor for the phantom dimensions", type=float, nargs="+", default=[1.0], ) parser.add_argument( "--offset", help="3D spatial offset of the phantom center", type=float, nargs="+", ) parser.add_argument( "--rotation", help="Rotation matrix for the phantom", type=float, nargs="+", ) rtk.add_rtk3Doutputimage_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Geometry if args_info.verbose: print(f"Reading geometry information from {args_info.geometry}...") geometry = rtk.read_geometry(args_info.geometry) # Create a stack of empty projection images constantImageSource = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) # Adjust size according to geometry sizeOutput = constantImageSource.GetSize() sizeOutput[2] = len(geometry.GetGantryAngles()) constantImageSource.SetSize(sizeOutput) # Create SheppLogan Phantom offset = [0] * Dimension if args_info.offset is not None: if len(args_info.offset) > 3: print("--offset needs up to 3 values") sys.exit(1) for i in range(len(args_info.offset)): offset[i] = args_info.offset[i] scale = [args_info.phantomscale[0]] * 3 if len(args_info.phantomscale) > 3: print("--phantomscale needs up to 3 values") sys.exit(1) for i in range(len(args_info.phantomscale)): scale[i] = args_info.phantomscale[i] rot = itk.Matrix[itk.D, Dimension, Dimension]() rot.SetIdentity() if args_info.rotation is not None: if len(args_info.rotation) != 9: print("--rotation needs exactly 9 values") sys.exit(1) rot = itk.matrix_from_array(np.array(args_info.rotation).reshape(3, 3)) ppc = rtk.ProjectGeometricPhantomImageFilter[OutputImageType, OutputImageType].New() ppc.SetInput(constantImageSource.GetOutput()) ppc.SetGeometry(geometry) ppc.SetPhantomScale(scale) ppc.SetOriginOffset(offset) ppc.SetRotationMatrix(rot) ppc.SetConfigFile(args_info.phantomfile) ppc.Update() # Write if args_info.verbose: print("Projecting and writing... ") itk.imwrite(ppc.GetOutput(), args_info.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkprojectionmatrix/CMakeLists.txt ================================================ WRAP_GGO(rtkprojectionmatrix_GGO_C rtkprojectionmatrix.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkprojectionmatrix rtkprojectionmatrix.cxx ${rtkprojectionmatrix_GGO_C}) target_link_libraries(rtkprojectionmatrix RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkprojectionmatrix) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkprojectionmatrix/rtkMatlabSparseMatrix.h ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef rtkMatlabSparseMatrix_h #define rtkMatlabSparseMatrix_h #include namespace rtk { /** \class MatlabSparseMatrix * * sparse matrix in Matlab format * Initilaize it with vnl_sparse_matrix * Save it into .mat format * */ class MatlabSparseMatrix { public: template MatlabSparseMatrix(const vnl_sparse_matrix & sparseMatrix, TOutputImage * output); void Save(std::ostream & out); void Print(); struct MatlabSparseMatrixStruct { unsigned char s_headerMatlab[116]; unsigned char s_headerOffset[8]; unsigned short int s_headerVersion; unsigned char s_headerEndian[2]; unsigned long int s_mainTag; unsigned long int s_dataLength; unsigned long int s_arrayTag; unsigned long int s_arrayLength; unsigned short int s_arrayUndefined; unsigned char s_arrayFlags; unsigned char s_arrayClass; unsigned long int s_arrayNzmax; unsigned long int s_dimensionTag; unsigned long int s_dimensionLength; unsigned long int s_dimensionNbRow; unsigned long int s_dimensionNbColumn; unsigned short int s_nameLength; unsigned short int s_nameTag; unsigned char s_nameChar; unsigned char s_namePadding[3]; unsigned long int s_rowIndexTag; unsigned long int s_rowIndexLength; unsigned long int * s_rowIndex; unsigned long int s_rowIndexPadding; unsigned long int s_columnIndexTag; unsigned long int s_columnIndexLength; unsigned long int * s_columnIndex; unsigned long int s_columnIndexPadding; unsigned long int s_valueTag; unsigned long int s_valueLength; double * s_value; }; protected: MatlabSparseMatrixStruct m_MatlabSparseMatrix; }; } // namespace rtk #ifndef ITK_MANUAL_INSTANTIATION # include "rtkMatlabSparseMatrix.hxx" #endif #endif ================================================ FILE: applications/rtkprojectionmatrix/rtkMatlabSparseMatrix.hxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include #include //==================================================================== template rtk::MatlabSparseMatrix::MatlabSparseMatrix(const vnl_sparse_matrix & sparseMatrix, TOutputImage * output) { unsigned int nbColumn = output->GetLargestPossibleRegion().GetSize()[0] * output->GetLargestPossibleRegion().GetSize()[2]; // it's not sparseMatrix.columns() // Take the number of non-zero elements: // Compute the column index // Store elements in std::vector and sort them according 1\ index of column and 2\ index of row unsigned int nonZeroElement(0); using sparseMatrixColumn = std::vector>; auto * columnsVector = new sparseMatrixColumn[nbColumn]; sparseMatrix.reset(); while (sparseMatrix.next()) { typename TOutputImage::IndexType idx = output->ComputeIndex(sparseMatrix.getcolumn()); if (idx[1] != 1) continue; unsigned int indexColumn = idx[0] + idx[2] * output->GetLargestPossibleRegion().GetSize()[2]; auto it = columnsVector[indexColumn].begin(); while (it != columnsVector[indexColumn].end()) { if ((unsigned int)sparseMatrix.getrow() < it->first) break; ++it; } columnsVector[indexColumn].insert(it, std::make_pair(sparseMatrix.getrow(), sparseMatrix.value())); ++nonZeroElement; } // Store the sparse matrix into a matlab structure std::string headerMatlab("MATLAB 5.0 MAT-file, Platform: GLNXA64, Created on: Fri May 18 14:11:56 2018 " " "); for (unsigned int i = 0; i < 116; ++i) m_MatlabSparseMatrix.s_headerMatlab[i] = headerMatlab[i]; for (unsigned char & i : m_MatlabSparseMatrix.s_headerOffset) i = 0; m_MatlabSparseMatrix.s_headerVersion = 0x0100; m_MatlabSparseMatrix.s_headerEndian[0] = 'I'; m_MatlabSparseMatrix.s_headerEndian[1] = 'M'; m_MatlabSparseMatrix.s_mainTag = 14; m_MatlabSparseMatrix.s_arrayTag = 6; m_MatlabSparseMatrix.s_arrayLength = 8; m_MatlabSparseMatrix.s_arrayFlags = 5; m_MatlabSparseMatrix.s_arrayClass = 16; m_MatlabSparseMatrix.s_arrayUndefined = 0; m_MatlabSparseMatrix.s_arrayNzmax = nonZeroElement; m_MatlabSparseMatrix.s_dimensionTag = 5; m_MatlabSparseMatrix.s_dimensionLength = 8; m_MatlabSparseMatrix.s_dimensionNbRow = sparseMatrix.rows(); m_MatlabSparseMatrix.s_dimensionNbColumn = nbColumn; m_MatlabSparseMatrix.s_nameLength = 1; m_MatlabSparseMatrix.s_nameTag = 1; m_MatlabSparseMatrix.s_nameChar = 'A'; for (unsigned char & i : m_MatlabSparseMatrix.s_namePadding) i = 0; m_MatlabSparseMatrix.s_dataLength = 64 + nonZeroElement * 8 + 4 * m_MatlabSparseMatrix.s_arrayNzmax + 4 * (m_MatlabSparseMatrix.s_dimensionNbColumn + 1); if (m_MatlabSparseMatrix.s_arrayNzmax * 4 % 8 != 0) m_MatlabSparseMatrix.s_dataLength += 4; if ((m_MatlabSparseMatrix.s_dimensionNbColumn + 1) * 4 % 8 != 0) m_MatlabSparseMatrix.s_dataLength += 4; m_MatlabSparseMatrix.s_rowIndexTag = 5; m_MatlabSparseMatrix.s_rowIndexLength = 4 * m_MatlabSparseMatrix.s_arrayNzmax; // put it in hexa m_MatlabSparseMatrix.s_rowIndex = new unsigned long int[m_MatlabSparseMatrix.s_arrayNzmax]; m_MatlabSparseMatrix.s_rowIndexPadding = 0; m_MatlabSparseMatrix.s_columnIndexTag = 5; m_MatlabSparseMatrix.s_columnIndexLength = 4 * (m_MatlabSparseMatrix.s_dimensionNbColumn + 1); m_MatlabSparseMatrix.s_columnIndex = new unsigned long int[m_MatlabSparseMatrix.s_dimensionNbColumn + 1]; m_MatlabSparseMatrix.s_columnIndexPadding = 0; m_MatlabSparseMatrix.s_valueTag = 9; m_MatlabSparseMatrix.s_valueLength = 8 * m_MatlabSparseMatrix.s_arrayNzmax; m_MatlabSparseMatrix.s_value = new double[m_MatlabSparseMatrix.s_arrayNzmax]; // Copy data unsigned int elementIndex(0); for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_dimensionNbColumn; ++i) { m_MatlabSparseMatrix.s_columnIndex[i] = elementIndex; for (const auto & it : columnsVector[i]) { m_MatlabSparseMatrix.s_rowIndex[elementIndex] = it.first; m_MatlabSparseMatrix.s_value[elementIndex] = it.second; ++elementIndex; } } m_MatlabSparseMatrix.s_columnIndex[m_MatlabSparseMatrix.s_dimensionNbColumn] = m_MatlabSparseMatrix.s_arrayNzmax; } void rtk::MatlabSparseMatrix::Save(std::ostream & out) { // Write data out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_headerMatlab), 116); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_headerOffset), 8); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_headerVersion), 2); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_headerEndian), 2); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_mainTag), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_dataLength), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_arrayTag), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_arrayLength), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_arrayFlags), 1); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_arrayClass), 1); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_arrayUndefined), 2); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_arrayNzmax), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_dimensionTag), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_dimensionLength), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_dimensionNbRow), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_dimensionNbColumn), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_nameLength), 2); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_nameTag), 2); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_nameChar), 1); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_namePadding), 3); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_rowIndexTag), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_rowIndexLength), 4); for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_arrayNzmax; ++i) out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_rowIndex[i]), 4); if (m_MatlabSparseMatrix.s_arrayNzmax * 4 % 8 != 0) out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_rowIndexPadding), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_columnIndexTag), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_columnIndexLength), 4); for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_dimensionNbColumn + 1; ++i) out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_columnIndex[i]), 4); if ((m_MatlabSparseMatrix.s_dimensionNbColumn + 1) * 4 % 8 != 0) out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_columnIndexPadding), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_valueTag), 4); out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_valueLength), 4); for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_arrayNzmax; ++i) out.write(reinterpret_cast(&m_MatlabSparseMatrix.s_value[i]), 8); } void rtk::MatlabSparseMatrix::Print() { std::cout << "m_MatlabSparseMatrix.s_headerMatlab : \"" << m_MatlabSparseMatrix.s_headerMatlab << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_headerOffset : \"" << m_MatlabSparseMatrix.s_headerOffset << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_headerVersion : \"" << std::hex << +m_MatlabSparseMatrix.s_headerVersion << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_headerEndian : \"" << m_MatlabSparseMatrix.s_headerEndian << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_mainTag : \"" << m_MatlabSparseMatrix.s_mainTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_dataLength : \"" << m_MatlabSparseMatrix.s_dataLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_arrayTag : \"" << m_MatlabSparseMatrix.s_arrayTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_arrayLength : \"" << m_MatlabSparseMatrix.s_arrayLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_arrayFlags : \"" << (unsigned int)m_MatlabSparseMatrix.s_arrayFlags << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_arrayClass : \"" << (unsigned int)m_MatlabSparseMatrix.s_arrayClass << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_arrayUndefined : \"" << m_MatlabSparseMatrix.s_arrayUndefined << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_arrayNzmax : \"" << m_MatlabSparseMatrix.s_arrayNzmax << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_dimensionTag : \"" << m_MatlabSparseMatrix.s_dimensionTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_dimensionLength : \"" << m_MatlabSparseMatrix.s_dimensionLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_dimensionNbRow : \"" << m_MatlabSparseMatrix.s_dimensionNbRow << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_dimensionNbColumn : \"" << m_MatlabSparseMatrix.s_dimensionNbColumn << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_nameLength : \"" << m_MatlabSparseMatrix.s_nameLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_nameTag : \"" << m_MatlabSparseMatrix.s_nameTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_nameChar : \"" << m_MatlabSparseMatrix.s_nameChar << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_namePadding : \"" << m_MatlabSparseMatrix.s_namePadding << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_rowIndexTag : \"" << m_MatlabSparseMatrix.s_rowIndexTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_rowIndexLength : \"" << m_MatlabSparseMatrix.s_rowIndexLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_rowIndex : \""; for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_arrayNzmax; ++i) std::cout << m_MatlabSparseMatrix.s_rowIndex[i] << " "; std::cout << "\"" << std::endl; if (m_MatlabSparseMatrix.s_arrayNzmax * 4 % 8 != 0) std::cout << "m_MatlabSparseMatrix.s_rowIndexPadding : \"" << m_MatlabSparseMatrix.s_rowIndexPadding << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_columnIndexTag : \"" << m_MatlabSparseMatrix.s_columnIndexTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_columnIndexLength : \"" << m_MatlabSparseMatrix.s_columnIndexLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_columnIndex : \""; for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_dimensionNbColumn + 1; ++i) std::cout << m_MatlabSparseMatrix.s_columnIndex[i] << " "; std::cout << "\"" << std::endl; if ((m_MatlabSparseMatrix.s_dimensionNbColumn + 1) * 4 % 8 != 0) std::cout << "m_MatlabSparseMatrix.s_columnIndexPadding : \"" << m_MatlabSparseMatrix.s_columnIndexPadding << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_valueTag : \"" << m_MatlabSparseMatrix.s_valueTag << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_valueLength : \"" << m_MatlabSparseMatrix.s_valueLength << "\"" << std::endl; std::cout << "m_MatlabSparseMatrix.s_value : \""; for (unsigned int i = 0; i < m_MatlabSparseMatrix.s_arrayNzmax; ++i) std::cout << m_MatlabSparseMatrix.s_value[i] << " "; std::cout << "\"" << std::endl; } /* std::istream& operator>>(std::istream& in) { char temp1(0); unsigned short int temp2(0); unsigned long int temp4(0); double temp8(0); //header for (unsigned int i=0; i<116; ++i) { in.read(reinterpret_cast(&temp1),1); m_MatlabSparseMatrix.s_headerMatlab[i] = temp1; } std::cout << "m_MatlabSparseMatrix.s_headerMatlab : \"" << m_MatlabSparseMatrix.s_headerMatlab << "\"" << std::endl; for (unsigned int i=0; i<8; ++i) { in.read(reinterpret_cast(&temp1),1); m_MatlabSparseMatrix.s_headerOffset[i] = temp1; } std::cout << "m_MatlabSparseMatrix.s_headerOffset : \"" << m_MatlabSparseMatrix.s_headerOffset << "\"" << std::endl; in.read(reinterpret_cast(&temp2), 2); m_MatlabSparseMatrix.s_headerVersion = temp2; std::cout << "m_MatlabSparseMatrix.s_headerVersion : \"" << std::hex << +m_MatlabSparseMatrix.s_headerVersion << "\"" << std::endl; for (unsigned int i=0; i<2; ++i) { in.read(reinterpret_cast(&temp1),1); m_MatlabSparseMatrix.s_headerEndian[i] = temp1; } std::cout << "m_MatlabSparseMatrix.s_headerEndian : \"" << m_MatlabSparseMatrix.s_headerEndian << "\"" << std::endl; //data in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_mainTag = temp4; std::cout << "m_MatlabSparseMatrix.s_mainTag : \"" << m_MatlabSparseMatrix.s_mainTag << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_dataLength = temp4; std::cout << "m_MatlabSparseMatrix.s_dataLength : \"" << m_MatlabSparseMatrix.s_dataLength << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_arrayTag = temp4; std::cout << "m_MatlabSparseMatrix.s_arrayTag : \"" << m_MatlabSparseMatrix.s_arrayTag << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_arrayLength = temp4; std::cout << "m_MatlabSparseMatrix.s_arrayLength : \"" << m_MatlabSparseMatrix.s_arrayLength << "\"" << std::endl; in.read(reinterpret_cast(&temp1), 1); m_MatlabSparseMatrix.s_arrayFlags = temp1; std::cout << "m_MatlabSparseMatrix.s_arrayFlags : \"" << (unsigned int)m_MatlabSparseMatrix.s_arrayFlags << "\"" << std::endl; in.read(reinterpret_cast(&temp1),1); m_MatlabSparseMatrix.s_arrayClass = temp1; std::cout << "m_MatlabSparseMatrix.s_arrayClass : \"" << (unsigned int)m_MatlabSparseMatrix.s_arrayClass << "\"" << std::endl; in.read(reinterpret_cast(&temp2), 2); m_MatlabSparseMatrix.s_arrayUndefined = temp2; std::cout << "m_MatlabSparseMatrix.s_arrayUndefined : \"" << m_MatlabSparseMatrix.s_arrayUndefined << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_arrayNzmax = temp4; std::cout << "m_MatlabSparseMatrix.s_arrayNzmax : \"" << m_MatlabSparseMatrix.s_arrayNzmax << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_dimensionTag = temp4; std::cout << "m_MatlabSparseMatrix.s_dimensionTag : \"" << m_MatlabSparseMatrix.s_dimensionTag << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_dimensionLength = temp4; std::cout << "m_MatlabSparseMatrix.s_dimensionLength : \"" << m_MatlabSparseMatrix.s_dimensionLength << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_dimensionNbRow = temp4; std::cout << "m_MatlabSparseMatrix.s_dimensionNbRow : \"" << m_MatlabSparseMatrix.s_dimensionNbRow << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_dimensionNbColumn = temp4; std::cout << "m_MatlabSparseMatrix.s_dimensionNbColumn : \"" << m_MatlabSparseMatrix.s_dimensionNbColumn << "\"" << std::endl; in.read(reinterpret_cast(&temp2), 2); m_MatlabSparseMatrix.s_nameLength = temp2; std::cout << "m_MatlabSparseMatrix.s_nameLength : \"" << m_MatlabSparseMatrix.s_nameLength << "\"" << std::endl; in.read(reinterpret_cast(&temp2), 2); m_MatlabSparseMatrix.s_nameTag = temp2; std::cout << "m_MatlabSparseMatrix.s_nameTag : \"" << m_MatlabSparseMatrix.s_nameTag << "\"" << std::endl; in.read(reinterpret_cast(&temp1),1); m_MatlabSparseMatrix.s_nameChar = temp1; std::cout << "m_MatlabSparseMatrix.s_nameChar : \"" << m_MatlabSparseMatrix.s_nameChar << "\"" << std::endl; for (unsigned int i=0; i<3; ++i) { in.read(reinterpret_cast(&temp1),1); m_MatlabSparseMatrix.s_namePadding[i] = temp1; } std::cout << "m_MatlabSparseMatrix.s_namePadding : \"" << m_MatlabSparseMatrix.s_namePadding << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_rowIndexTag = temp4; std::cout << "m_MatlabSparseMatrix.s_rowIndexTag : \"" << m_MatlabSparseMatrix.s_rowIndexTag << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_rowIndexLength = temp4; std::cout << "m_MatlabSparseMatrix.s_rowIndexLength : \"" << m_MatlabSparseMatrix.s_rowIndexLength << "\"" << std::endl; m_MatlabSparseMatrix.s_rowIndex = new unsigned long int[m_MatlabSparseMatrix.s_arrayNzmax]; std::cout << "m_MatlabSparseMatrix.s_rowIndex : \""; for (unsigned int i=0; i(&temp4),4); m_MatlabSparseMatrix.s_rowIndex[i] = temp4; std::cout << m_MatlabSparseMatrix.s_rowIndex[i] << " "; } std::cout << "\"" << std::endl; if (m_MatlabSparseMatrix.s_arrayNzmax*4%8 != 0) { in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_rowIndexPadding = temp4; std::cout << "m_MatlabSparseMatrix.s_rowIndexPadding : \"" << m_MatlabSparseMatrix.s_rowIndexPadding << "\"" << std::endl; } in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_columnIndexTag = temp4; std::cout << "m_MatlabSparseMatrix.s_columnIndexTag : \"" << m_MatlabSparseMatrix.s_columnIndexTag << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_columnIndexLength = temp4; std::cout << "m_MatlabSparseMatrix.s_columnIndexLength : \"" << m_MatlabSparseMatrix.s_columnIndexLength << "\"" << std::endl; m_MatlabSparseMatrix.s_columnIndex = new unsigned long int[m_MatlabSparseMatrix.s_dimensionNbColumn+1]; std::cout << "m_MatlabSparseMatrix.s_columnIndex : \""; for (unsigned int i=0; i(&temp4),4); m_MatlabSparseMatrix.s_columnIndex[i] = temp4; std::cout << m_MatlabSparseMatrix.s_columnIndex[i] << " "; } std::cout << "\"" << std::endl; if ((m_MatlabSparseMatrix.s_dimensionNbColumn+1)*4%8 != 0) { in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_columnIndexPadding = temp4; std::cout << "m_MatlabSparseMatrix.s_columnIndexPadding : \"" << m_MatlabSparseMatrix.s_columnIndexPadding << "\"" << std::endl; } in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_valueTag = temp4; std::cout << "m_MatlabSparseMatrix.s_valueTag : \"" << m_MatlabSparseMatrix.s_valueTag << "\"" << std::endl; in.read(reinterpret_cast(&temp4), 4); m_MatlabSparseMatrix.s_valueLength = temp4; std::cout << "m_MatlabSparseMatrix.s_valueLength : \"" << m_MatlabSparseMatrix.s_valueLength << "\"" << std::endl; m_MatlabSparseMatrix.s_value = new double[m_MatlabSparseMatrix.s_arrayNzmax]; std::cout << "m_MatlabSparseMatrix.s_value : \""; for (unsigned int i=0; i(&temp8),8); m_MatlabSparseMatrix.s_value[i] = temp8; std::cout << m_MatlabSparseMatrix.s_value[i] << " "; } std::cout << "\"" << std::endl; return in; } */ ================================================ FILE: applications/rtkprojectionmatrix/rtkprojectionmatrix.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include #include #include "rtkprojectionmatrix_ggo.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkGgoFunctions.h" #include "rtkJosephBackProjectionImageFilter.h" #include "rtkConfiguration.h" #include "rtkMatlabSparseMatrix.h" namespace rtk { namespace Functor { template class StoreSparseMatrixSplatWeightMultiplication { public: StoreSparseMatrixSplatWeightMultiplication() = default; ~StoreSparseMatrixSplatWeightMultiplication() = default; bool operator!=(const StoreSparseMatrixSplatWeightMultiplication &) const { return false; } bool operator==(const StoreSparseMatrixSplatWeightMultiplication & other) const { return !(*this != other); } inline void operator()(const TInput & rayValue, TOutput & output, const double stepLengthInVoxel, const double voxelSize, const TCoordinateType weight) { // One row of the matrix is one ray, it should be thread safe m_SystemMatrix.put( &rayValue - m_ProjectionsBuffer, &output - m_VolumeBuffer, weight * voxelSize * stepLengthInVoxel); } vnl_sparse_matrix & GetVnlSparseMatrix() { return m_SystemMatrix; } void SetProjectionsBuffer(TInput * pb) { m_ProjectionsBuffer = pb; } void SetVolumeBuffer(TOutput * vb) { m_VolumeBuffer = vb; } private: vnl_sparse_matrix m_SystemMatrix; TInput * m_ProjectionsBuffer; TOutput * m_VolumeBuffer; }; } // namespace Functor } // namespace rtk std::ostream & operator<<(std::ostream & out, rtk::MatlabSparseMatrix & matlabSparseMatrix) { matlabSparseMatrix.Save(out); return out; } int main(int argc, char * argv[]) { GGO(rtkprojectionmatrix, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->Update()) // Create back projection image filter if (reader->GetOutput()->GetLargestPossibleRegion().GetSize()[1] != 1) { std::cerr << "This tool has been designed for 2D, i.e., with one row in the sinogram only." << std::endl; return EXIT_FAILURE; } const OutputImageType::DirectionType dir = reader->GetOutput()->GetDirection(); if (itk::Math::abs(dir[0][0]) != 1. || itk::Math::abs(dir[1][1]) != 1.) { std::cerr << "Projections with non-diagonal Direction is not handled." << std::endl; return EXIT_FAILURE; } // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); if (args_info.verbose_flag) std::cout << " done." << std::endl; // Create reconstructed image using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(constantImageSource->Update()) if (constantImageSource->GetOutput()->GetLargestPossibleRegion().GetSize()[1] != 3) { std::cerr << "This tool has been designed for 2D with Joseph project. " << "Joseph requires at least 2 slices in the y direction for bilinear interpolation. " << "To have one slice exactly in front of the row, use 3 slices in the volume " << "with the central slice in front of the single projection row." << std::endl; return EXIT_FAILURE; } if (!constantImageSource->GetOutput()->GetDirection().GetVnlMatrix().is_identity()) { std::cerr << "Volume with non-identity Direction is not handled." << std::endl; return EXIT_FAILURE; } // Adjust size according to geometry if (reader->GetOutput()->GetLargestPossibleRegion().GetSize()[2] != geometry->GetGantryAngles().size()) { std::cerr << "Number of projections in the geometry and in the stack do not match." << std::endl; return EXIT_FAILURE; } // Create back projection image filter if (args_info.verbose_flag) std::cout << "Backprojecting volume and recording matrix values..." << std::endl; auto backProjection = rtk::JosephBackProjectionImageFilter< OutputImageType, OutputImageType, rtk::Functor::InterpolationWeightMultiplicationBackProjection, rtk::Functor::StoreSparseMatrixSplatWeightMultiplication>::New(); backProjection->SetInput(constantImageSource->GetOutput()); backProjection->SetInput(1, reader->GetOutput()); backProjection->SetGeometry(geometry); backProjection->GetSplatWeightMultiplication().SetProjectionsBuffer(reader->GetOutput()->GetBufferPointer()); backProjection->GetSplatWeightMultiplication().SetVolumeBuffer(constantImageSource->GetOutput()->GetBufferPointer()); backProjection->GetSplatWeightMultiplication().GetVnlSparseMatrix().resize( reader->GetOutput()->GetLargestPossibleRegion().GetNumberOfPixels(), constantImageSource->GetOutput()->GetLargestPossibleRegion().GetNumberOfPixels()); TRY_AND_EXIT_ON_ITK_EXCEPTION(backProjection->Update()) // Write matrix to disk if (args_info.verbose_flag) std::cout << "Writing matrix to disk..." << std::endl; std::ofstream ofs(args_info.output_arg, std::ofstream::binary); if (!ofs) { std::cerr << "Failed to open " << args_info.output_arg << std::endl; return EXIT_FAILURE; } rtk::MatlabSparseMatrix matlabSparseMatrix(backProjection->GetSplatWeightMultiplication().GetVnlSparseMatrix(), backProjection->GetOutput()); ofs << matlabSparseMatrix; ofs.close(); return EXIT_SUCCESS; } ================================================ FILE: applications/rtkprojectionmatrix/rtkprojectionmatrix.ggo ================================================ purpose "Saves the sparse system matrix of rtk::JosephForwardProjectionImageFilter in a file. Only works in 2D." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name for the sparse matrix" string yes ================================================ FILE: applications/rtkprojections/CMakeLists.txt ================================================ WRAP_GGO(rtkprojections_GGO_C rtkprojections.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkprojections rtkprojections.cxx ${rtkprojections_GGO_C}) target_link_libraries(rtkprojections RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkprojections) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkprojections/rtkprojections.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkprojections_ggo.h" #include "rtkMacro.h" #include "rtkGgoFunctions.h" #include int main(int argc, char * argv[]) { GGO(rtkprojections, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Write auto writer = itk::ImageFileWriter::New(); writer->SetFileName(args_info.output_arg); writer->SetInput(reader->GetOutput()); TRY_AND_EXIT_ON_ITK_EXCEPTION(writer->UpdateOutputInformation()) writer->SetNumberOfStreamDivisions(1 + reader->GetOutput()->GetLargestPossibleRegion().GetNumberOfPixels() / (1024 * 1024 * 4)); TRY_AND_EXIT_ON_ITK_EXCEPTION(writer->Update()) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkprojections/rtkprojections.ggo ================================================ purpose "Reads raw projection images, converts them to attenuation and stacks them into a single output image file" option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes ================================================ FILE: applications/rtkprojections/rtkprojections.py ================================================ import argparse import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description="Reads raw projection images, converts them to attenuation and stacks them into a single output image file" ) parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--output", "-o", help="Output file name", type=str, required=True ) rtk.add_rtkinputprojections_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Projections reader reader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) if args_info.verbose: print(f"Reading projections...") # Write writer = itk.ImageFileWriter[OutputImageType].New() writer.SetFileName(args_info.output) writer.SetInput(reader.GetOutput()) if args_info.verbose: print(f"Writing output to: {args_info.output}") writer.Update() def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkprojectors_group.py ================================================ __all__ = [ "add_rtkprojectors_group", "SetForwardProjectionFromArgParse", "SetBackProjectionFromArgParse", ] # Mimicks rtkprojectors_section.ggo def add_rtkprojectors_group(parser): rtkprojectors_group = parser.add_argument_group("Projectors") rtkprojectors_group.add_argument( "--fp", "-f", help="Forward projection method", choices=["Joseph", "CudaRayCast", "JosephAttenuated", "Zeng"], default="Joseph", ) rtkprojectors_group.add_argument( "--bp", "-b", help="Back projection method", choices=[ "VoxelBasedBackProjection", "Joseph", "CudaVoxelBased", "CudaRayCast", "JosephAttenuated", "Zeng", ], default="VoxelBasedBackProjection", ) rtkprojectors_group.add_argument( "--attenuationmap", help="Attenuation map relative to the volume to perfom the attenuation correction (JosephAttenuated and Zeng)", ) rtkprojectors_group.add_argument( "--sigmazero", help="PSF value at a distance of 0 meter of the detector (Zeng only)", type=float, ) rtkprojectors_group.add_argument( "--alphapsf", help="Slope of the PSF against the detector distance (Zeng only)", type=float, ) rtkprojectors_group.add_argument( "--inferiorclipimage", help="Inferior clip of the ray for each pixel of the projections (Joseph only)", ) rtkprojectors_group.add_argument( "--superiorclipimage", help="Superior clip of the ray for each pixel of the projections (Joseph only)", ) # Mimicks SetBackProjectionFromGgo def SetBackProjectionFromArgParse(args_info, recon): if args_info.attenuationmap is not None: attenuation_map = itk.imread(args_info.attenuationmap) if args_info.inferiorclipimage is not None: inferior_clip_image = itk.imread(args_info.inferiorclipimage) if args_info.superiorclipimage is not None: superior_clip_image = itk.imread(args_info.superiorclipimage) ReconType = type(recon) if args_info.bp == "VoxelBasedBackProjection": # bp_arg_VoxelBasedBackProjection recon.SetBackProjectionFilter(ReconType.BackProjectionType_BP_VOXELBASED) elif args_info.bp == "Joseph": # bp_arg_Joseph recon.SetBackProjectionFilter(ReconType.BackProjectionType_BP_JOSEPH) elif args_info.bp == "CudaVoxelBased": # bp_arg_CudaVoxelBased recon.SetBackProjectionFilter(ReconType.BackProjectionType_BP_CUDAVOXELBASED) elif args_info.bp == "CudaRayCast": # bp_arg_CudaRayCast recon.SetBackProjectionFilter(ReconType.BackProjectionType_BP_CUDARAYCAST) elif args_info.bp == "JosephAttenuated": # bp_arg_JosephAttenuated recon.SetBackProjectionFilter(ReconType.BackProjectionType_BP_JOSEPHATTENUATED) if args_info.inferiorclipimage is not None: recon.SetInferiorClipImage(inferior_clip_image) if args_info.superiorclipimage is not None: recon.SetSuperiorClipImage(superior_clip_image) if args_info.attenuationmap is not None: recon.SetAttenuationMap(attenuation_map) elif args_info.bp == "Zeng": # bp_arg_RotationBased recon.SetBackProjectionFilter(ReconType.BackProjectionType_BP_ZENG) if args_info.sigmazero is not None: recon.SetSigmaZero(args_info.sigmazero) if args_info.alphapsf is not None: recon.SetAlphaPSF(args_info.alphapsf) if args_info.attenuationmap is not None: recon.SetAttenuationMap(args_info.attenuationMap) # Mimicks SetForwardProjectionFromGgo def SetForwardProjectionFromArgParse(args_info, recon): if args_info.attenuationmap is not None: attenuation_map = itk.imread(args_info.attenuationmap) ReconType = type(recon) if args_info.fp == "Joseph": # fp_arg_Joseph recon.SetForwardProjectionFilter(ReconType.ForwardProjectionType_FP_JOSEPH) elif args_info.fp == "CudaRayCast": # fp_arg_CudaRayCast recon.SetForwardProjectionFilter(ReconType.ForwardProjectionType_FP_CUDARAYCAST) elif args_info.fp == "JosephAttenuated": # fp_arg_JosephAttenuated recon.SetForwardProjectionFilter( ReconType.ForwardProjectionType_FP_JOSEPHATTENUATED ) if args_info.attenuationmap is not None: recon.SetAttenuationMap(attenuation_map) elif args_info.fp == "Zeng": # fp_arg_RotationBased recon.SetForwardProjectionFilter(ReconType.ForwardProjectionType_FP_ZENG) if args_info.sigmazero is not None: recon.SetSigmaZero(args_info.sigmazero) if args_info.alphapsf is not None: recon.SetAlphaPSF(args_info.alphapsf) if args_info.attenuationmap is not None: recon.SetAttenuationMap(args_info.attenuationMap) ================================================ FILE: applications/rtkprojectors_section.ggo ================================================ section "Projectors" option "fp" f "Forward projection method" values="Joseph","CudaRayCast","JosephAttenuated","Zeng" enum no default="Joseph" option "bp" b "Back projection method" values="VoxelBasedBackProjection","Joseph","CudaVoxelBased","CudaRayCast","JosephAttenuated", "Zeng" enum no default="VoxelBasedBackProjection" option "step" - "Step size along ray (for CudaRayCast only)" double no default="1" option "attenuationmap" - "Attenuation map relative to the volume to perfom the attenuation correction (JosephAttenuated and Zeng)" string no option "sigmazero" - "PSF value at a distance of 0 meter of the detector (Zeng only)" double no option "alphapsf" - "Slope of the PSF against the detector distance (Zeng only)" double no option "inferiorclipimage" - "Inferior clip of the ray for each pixel of the projections (Joseph only)" string no option "superiorclipimage" - "Superior clip of the ray for each pixel of the projections (Joseph only)" string no ================================================ FILE: applications/rtkprojectshepploganphantom/CMakeLists.txt ================================================ WRAP_GGO(rtkprojectshepploganphantom_GGO_C rtkprojectshepploganphantom.ggo ../rtk3Doutputimage_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkprojectshepploganphantom rtkprojectshepploganphantom.cxx ${rtkprojectshepploganphantom_GGO_C}) target_link_libraries(rtkprojectshepploganphantom RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkprojectshepploganphantom) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkprojectshepploganphantom/rtkprojectshepploganphantom.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkprojectshepploganphantom_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkRayEllipsoidIntersectionImageFilter.h" #include "rtkSheppLoganPhantomFilter.h" #include "rtkAdditiveGaussianNoiseImageFilter.h" #include int main(int argc, char * argv[]) { GGO(rtkprojectshepploganphantom, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create a stack of empty projection images using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo( constantImageSource, args_info); // Adjust size according to geometry constantImageSource->SetSize(itk::MakeSize( constantImageSource->GetSize()[0], constantImageSource->GetSize()[1], geometry->GetGantryAngles().size())); using SLPType = rtk::SheppLoganPhantomFilter; auto slp = SLPType::New(); SLPType::VectorType offset(0.); SLPType::VectorType scale; if (args_info.offset_given) { offset[0] = args_info.offset_arg[0]; offset[1] = args_info.offset_arg[1]; offset[2] = args_info.offset_arg[2]; } scale.Fill(args_info.phantomscale_arg[0]); if (args_info.phantomscale_given) { for (unsigned int i = 0; i < std::min(args_info.phantomscale_given, Dimension); i++) scale[i] = args_info.phantomscale_arg[i]; } slp->SetInput(constantImageSource->GetOutput()); slp->SetGeometry(geometry); slp->SetPhantomScale(scale); slp->SetOriginOffset(offset); TRY_AND_EXIT_ON_ITK_EXCEPTION(slp->Update()) // Add noise OutputImageType::Pointer output = slp->GetOutput(); if (args_info.noise_given) { auto noisy = rtk::AdditiveGaussianNoiseImageFilter::New(); noisy->SetInput(slp->GetOutput()); noisy->SetMean(0.0); noisy->SetStandardDeviation(args_info.noise_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(noisy->Update()) output = noisy->GetOutput(); } // Write if (args_info.verbose_flag) std::cout << "Projecting and writing... " << std::flush; TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(output, args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkprojectshepploganphantom/rtkprojectshepploganphantom.ggo ================================================ purpose "Computes projections through a 3D Shepp & Logan phantom, according to a geometry" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output projections file name" string yes option "phantomscale" - "Scaling factor for the phantom dimensions" double multiple no default="128" option "noise" - "Gaussian noise parameter (SD)" double no option "offset" - "3D spatial offset of the phantom center" double multiple no ================================================ FILE: applications/rtkprojectshepploganphantom/rtkprojectshepploganphantom.py ================================================ import argparse import itk from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Computes projections through a 3D Shepp & Logan phantom, according to a geometry" ) # General options parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument( "--geometry", "-g", help="XML geometry file name", type=str, required=True ) parser.add_argument( "--output", "-o", help="Output projections file name", type=str, required=True ) parser.add_argument( "--phantomscale", help="Scaling factor for the phantom dimensions", type=float, nargs="+", default=[128], ) parser.add_argument("--noise", help="Gaussian noise parameter (SD)", type=float) parser.add_argument( "--offset", help="3D spatial offset of the phantom center", type=float, nargs="+", ) rtk.add_rtk3Doutputimage_group(parser) # Parse the command line arguments return parser def process(args_info: argparse.Namespace): OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] # Geometry if args_info.verbose: print(f"Reading geometry information from {args_info.geometry}") geometry = rtk.read_geometry(args_info.geometry) # Create a stack of empty projection images constantImageSource = rtk.ConstantImageSource[OutputImageType].New() rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) # Adjust size according to geometry sizeOutput = constantImageSource.GetSize() sizeOutput[2] = len(geometry.GetGantryAngles()) constantImageSource.SetSize(sizeOutput) # Create SheppLogan Phantom offset = [0] * Dimension if args_info.offset is not None: for i in range((min(len(args_info.offset), Dimension))): offset[i] = args_info.offset[i] scale = [args_info.phantomscale[0]] * 3 for i in range(min(len(args_info.phantomscale), Dimension)): scale[i] = args_info.phantomscale[i] slp = rtk.SheppLoganPhantomFilter[OutputImageType, OutputImageType].New() slp.SetInput(constantImageSource.GetOutput()) slp.SetGeometry(geometry) slp.SetPhantomScale(scale) slp.SetOriginOffset(offset) slp.Update() output = slp.GetOutput() # Add noise if args_info.noise: noisy = rtk.AdditiveGaussianNoiseImageFilter[ OutputImageType, OutputImageType ].New() noisy.SetInput(slp.GetOutput()) noisy.SetMean(0.0) noisy.SetStandardDeviation(args_info.noise) noisy.Update() output = noisy.GetOutput() # Write if args_info.verbose: print("Projecting and writing... ") itk.imwrite(output, args_info.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkregularizedconjugategradient/CMakeLists.txt ================================================ WRAP_GGO(rtkregularizedconjugategradient_GGO_C rtkregularizedconjugategradient.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkregularizedconjugategradient rtkregularizedconjugategradient.cxx ${rtkregularizedconjugategradient_GGO_C}) target_link_libraries(rtkregularizedconjugategradient RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkregularizedconjugategradient) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkregularizedconjugategradient/rtkregularizedconjugategradient.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkregularizedconjugategradient_ggo.h" #include "rtkGgoFunctions.h" #include "rtkRegularizedConjugateGradientConeBeamReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" # include "rtkCudaConstantVolumeSource.h" #endif #include int main(int argc, char * argv[]) { GGO(rtkregularizedconjugategradient, args_info); using OutputPixelType = float; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo( constantImageSource, args_info); inputFilter = constantImageSource; } TRY_AND_EXIT_ON_ITK_EXCEPTION(inputFilter->Update()) inputFilter->ReleaseDataFlagOn(); // Read weights if given, otherwise default to weights all equal to one itk::ImageSource::Pointer weightsSource; if (args_info.weights_given) { auto weightsReader = itk::ImageFileReader::New(); weightsReader->SetFileName(args_info.weights_arg); weightsSource = weightsReader; } else { auto constantWeightsSource = rtk::ConstantImageSource::New(); // Set the weights to be like the projections TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputInformation()) constantWeightsSource->SetInformationFromImage(reader->GetOutput()); constantWeightsSource->SetConstant(1.0); weightsSource = constantWeightsSource; } // Read Support Mask if given OutputImageType::Pointer supportmaskSource; if (args_info.mask_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(supportmaskSource = itk::ReadImage(args_info.mask_arg)) } // Set the forward and back projection filters to be used using ConjugateGradientFilterType = rtk::RegularizedConjugateGradientConeBeamReconstructionFilter; auto regularizedConjugateGradient = ConjugateGradientFilterType::New(); SetForwardProjectionFromGgo(args_info, regularizedConjugateGradient.GetPointer()); SetBackProjectionFromGgo(args_info, regularizedConjugateGradient.GetPointer()); regularizedConjugateGradient->SetInputVolume(inputFilter->GetOutput()); regularizedConjugateGradient->SetInputProjectionStack(reader->GetOutput()); regularizedConjugateGradient->SetInputWeights(weightsSource->GetOutput()); regularizedConjugateGradient->SetGeometry(geometry); regularizedConjugateGradient->SetMainLoop_iterations(args_info.niter_arg); regularizedConjugateGradient->SetCudaConjugateGradient(!args_info.nocudacg_flag); regularizedConjugateGradient->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); if (args_info.mask_given) { regularizedConjugateGradient->SetSupportMask(supportmaskSource); } // Positivity if (args_info.nopositivity_flag) regularizedConjugateGradient->SetPerformPositivity(false); else regularizedConjugateGradient->SetPerformPositivity(true); if (args_info.gammalaplacian_given) regularizedConjugateGradient->SetGamma(args_info.gammalaplacian_arg); if (args_info.tikhonov_given) regularizedConjugateGradient->SetTikhonov(args_info.tikhonov_arg); // Spatial TV if (args_info.gammatv_given) { regularizedConjugateGradient->SetGammaTV(args_info.gammatv_arg); regularizedConjugateGradient->SetTV_iterations(args_info.tviter_arg); regularizedConjugateGradient->SetPerformTVSpatialDenoising(true); } else regularizedConjugateGradient->SetPerformTVSpatialDenoising(false); // Spatial wavelets if (args_info.threshold_given) { regularizedConjugateGradient->SetSoftThresholdWavelets(args_info.threshold_arg); regularizedConjugateGradient->SetOrder(args_info.order_arg); regularizedConjugateGradient->SetNumberOfLevels(args_info.levels_arg); regularizedConjugateGradient->SetPerformWaveletsSpatialDenoising(true); } else regularizedConjugateGradient->SetPerformWaveletsSpatialDenoising(false); // Sparsity in image domain if (args_info.soft_given) { regularizedConjugateGradient->SetSoftThresholdOnImage(args_info.soft_arg); regularizedConjugateGradient->SetPerformSoftThresholdOnImage(true); } else regularizedConjugateGradient->SetSoftThresholdOnImage(false); REPORT_ITERATIONS(regularizedConjugateGradient, ConjugateGradientFilterType, OutputImageType) TRY_AND_EXIT_ON_ITK_EXCEPTION(regularizedConjugateGradient->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(regularizedConjugateGradient->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkregularizedconjugategradient/rtkregularizedconjugategradient.ggo ================================================ purpose "Alternates between conjugate gradient reconstruction and regularization" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niter" n "Number of iterations" int no default="5" option "input" i "Input volume" string no option "weights" w "Weights file for Weighted Least Squares (WLS)" string no option "gammalaplacian" - "Laplacian regularization weight" float no default="0" option "tikhonov" - "Tikhonov regularization weight" float no default="0" option "cgiter" - "Number of conjugate gradient nested iterations" int no default="4" option "nocudacg" - "Do not perform conjugate gradient calculations on GPU" flag off option "mask" m "Apply a support binary mask: reconstruction kept null outside the mask)" string no option "nodisplaced" - "Disable the displaced detector filter" flag off section "Regularization" option "nopositivity" - "Do not enforce positivity" flag off option "tviter" - "Total variation regularization: number of iterations" int no default="10" option "gammatv" - "Total variation spatial regularization parameter. The larger, the smoother" double no option "threshold" - "Daubechies wavelets spatial regularization: soft threshold" float no option "order" - "Daubechies wavelets spatial regularization: order of the wavelets" int no default="5" option "levels" - "Daubechies wavelets spatial regularization: number of decomposition levels" int no default="3" option "soft" - "Soft threshold for image domain sparsity enforcement" float no ================================================ FILE: applications/rtksart/CMakeLists.txt ================================================ WRAP_GGO(rtksart_GGO_C rtksart.ggo ../rtkinputprojections_section.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtksart rtksart.cxx ${rtksart_GGO_C}) target_link_libraries(rtksart RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtksart) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtksart/rtksart.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtksart_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkSARTConeBeamReconstructionFilter.h" #include "rtkPhaseGatingImageFilter.h" #include "rtkIterationCommands.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" #endif #include int main(int argc, char * argv[]) { GGO(rtksart, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; #else using OutputImageType = itk::Image; #endif // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Phase gating weights reader auto phaseGating = rtk::PhaseGatingImageFilter::New(); if (args_info.signal_given) { phaseGating->SetPhasesFileName(args_info.signal_arg); phaseGating->SetGatingWindowWidth(args_info.windowwidth_arg); phaseGating->SetGatingWindowCenter(args_info.windowcenter_arg); phaseGating->SetGatingWindowShape(args_info.windowshape_arg); phaseGating->SetInputProjectionStack(reader->GetOutput()); phaseGating->SetInputGeometry(geometry); TRY_AND_EXIT_ON_ITK_EXCEPTION(phaseGating->Update()) } // Create input: either an existing volume read from a file or a blank image itk::ImageSource::Pointer inputFilter; if (args_info.input_given) { // Read an existing image to initialize the volume auto inputReader = itk::ImageFileReader::New(); inputReader->SetFileName(args_info.input_arg); inputFilter = inputReader; } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); inputFilter = constantImageSource; } // SART reconstruction filter auto sart = rtk::SARTConeBeamReconstructionFilter::New(); // Set the forward and back projection filters SetForwardProjectionFromGgo(args_info, sart.GetPointer()); SetBackProjectionFromGgo(args_info, sart.GetPointer()); sart->SetInput(inputFilter->GetOutput()); if (args_info.signal_given) { sart->SetInput(1, phaseGating->GetOutput()); sart->SetGeometry(phaseGating->GetOutputGeometry()); sart->SetGatingWeights(phaseGating->GetGatingWeightsOnSelectedProjections()); } else { sart->SetInput(1, reader->GetOutput()); sart->SetGeometry(geometry); } sart->SetNumberOfIterations(args_info.niterations_arg); sart->SetNumberOfProjectionsPerSubset(args_info.nprojpersubset_arg); sart->SetLambda(args_info.lambda_arg); sart->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); sart->SetResetNesterovEvery(args_info.reset_nesterov_arg); if (args_info.positivity_flag) { sart->SetEnforcePositivity(true); } if (args_info.divisionthreshold_given) { sart->SetDivisionThreshold(args_info.divisionthreshold_arg); } REPORT_ITERATIONS(sart, rtk::SARTConeBeamReconstructionFilter, OutputImageType) TRY_AND_EXIT_ON_ITK_EXCEPTION(sart->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(sart->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtksart/rtksart.ggo ================================================ purpose "Reconstructs a 3D volume from a sequence of projections with Simulatenous Algebraic Reconstruction Technique [Andersen, 1984]." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="5" option "lambda" l "Convergence factor" double no default="0.3" option "positivity" - "Enforces positivity during the reconstruction" flag off option "input" i "Input volume" string no option "nprojpersubset" - "Number of projections processed between each update of the reconstructed volume (1 for SART, several for OSSART, all for SIRT)" int no default="1" option "nodisplaced" - "Disable the displaced detector filter" flag off option "divisionthreshold" - "Denominator threshold below which denominator pixels are zero" double no option "reset_nesterov" - "Reset Nesterov after a number of subset (1 means no momentum)" int no default="1" section "Phase gating" option "signal" - "File containing the phase of each projection" string no option "windowcenter" c "Target reconstruction phase" float no default="0" option "windowwidth" w "Tolerance around the target phase to determine in-phase and out-of-phase projections" float no default="1" option "windowshape" s "Shape of the gating window" values="Rectangular","Triangular" enum no default="Rectangular" ================================================ FILE: applications/rtkscatterglarecorrection/CMakeLists.txt ================================================ WRAP_GGO(rtkscatterglarecorrection_GGO_C rtkscatterglarecorrection.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkscatterglarecorrection rtkscatterglarecorrection.cxx ${rtkscatterglarecorrection_GGO_C}) target_link_libraries(rtkscatterglarecorrection RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkscatterglarecorrection) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkscatterglarecorrection/rtkscatterglarecorrection.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkscatterglarecorrection_ggo.h" #include "rtkMacro.h" #include "rtkGgoFunctions.h" #include #include #ifdef RTK_USE_CUDA # include "rtkCudaScatterGlareCorrectionImageFilter.h" #else # include "rtkScatterGlareCorrectionImageFilter.h" #endif #include "rtkProjectionsReader.h" #include #include #include #include #include #include int main(int argc, char * argv[]) { GGO(rtkscatterglarecorrection, args_info); using InputPixelType = float; constexpr unsigned int Dimension = 3; #ifdef RTK_USE_CUDA using InputImageType = itk::CudaImage; #else using InputImageType = itk::Image; #endif using ReaderType = rtk::ProjectionsReader; // Warning: preprocess images auto reader = ReaderType::New(); reader->SetFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); reader->ComputeLineIntegralOff(); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputInformation()) // Input projection parameters InputImageType::SizeType sizeInput = reader->GetOutput()->GetLargestPossibleRegion().GetSize(); int Nproj = sizeInput[2]; std::vector coef; if (args_info.coefficients_given == 2) { coef.push_back(args_info.coefficients_arg[0]); coef.push_back(args_info.coefficients_arg[1]); } else { std::cerr << "--coefficients requires exactly 2 coefficients" << std::endl; return EXIT_FAILURE; } #ifdef RTK_USE_CUDA using ScatterCorrectionType = rtk::CudaScatterGlareCorrectionImageFilter; #else using ScatterCorrectionType = rtk::ScatterGlareCorrectionImageFilter; #endif auto SFilter = ScatterCorrectionType::New(); SFilter->SetTruncationCorrection(0.0); SFilter->SetCoefficients(coef); auto constantSource = rtk::ConstantImageSource::New(); auto paste = itk::PasteImageFilter::New(); paste->SetSourceImage(SFilter->GetOutput()); paste->SetDestinationImage(constantSource->GetOutput()); std::cout << "Starting processing" << std::endl; int projid = 0; bool first = true; while (projid < Nproj) { int curBufferSize = std::min(args_info.bufferSize_arg, Nproj - projid); InputImageType::RegionType sliceRegionA = reader->GetOutput()->GetLargestPossibleRegion(); InputImageType::RegionType desiredRegionA; desiredRegionA.SetSize(itk::MakeSize(sliceRegionA.GetSize()[0], sliceRegionA.GetSize()[1], curBufferSize)); desiredRegionA.SetIndex(itk::MakeIndex(sliceRegionA.GetIndex()[0], sliceRegionA.GetIndex()[1], projid)); auto extract = itk::ExtractImageFilter::New(); extract->SetDirectionCollapseToIdentity(); extract->SetExtractionRegion(desiredRegionA); extract->SetInput(reader->GetOutput()); TRY_AND_EXIT_ON_ITK_EXCEPTION(extract->Update()) InputImageType::Pointer image = extract->GetOutput(); image->DisconnectPipeline(); SFilter->SetInput(image); SFilter->GetOutput()->SetRequestedRegion(image->GetRequestedRegion()); TRY_AND_EXIT_ON_ITK_EXCEPTION(SFilter->Update()) InputImageType::Pointer procImage = SFilter->GetOutput(); procImage->DisconnectPipeline(); InputImageType::Pointer outImage; if (args_info.difference_flag) { auto subtractFilter = itk::SubtractImageFilter::New(); subtractFilter->SetInput1(image); subtractFilter->SetInput2(procImage); TRY_AND_EXIT_ON_ITK_EXCEPTION(subtractFilter->Update()) outImage = subtractFilter->GetOutput(); outImage->DisconnectPipeline(); } else { outImage = procImage; } InputImageType::IndexType current_idx = outImage->GetLargestPossibleRegion().GetIndex(); current_idx[2] = projid; if (first) { // Initialization of the output volume InputImageType::SizeType sizeInput_local = outImage->GetLargestPossibleRegion().GetSize(); sizeInput_local[2] = Nproj; InputImageType::SpacingType spacingInput = outImage->GetSpacing(); InputImageType::PointType originInput = outImage->GetOrigin(); InputImageType::DirectionType imageDirection; imageDirection.SetIdentity(); constantSource->SetOrigin(originInput); constantSource->SetSpacing(spacingInput); constantSource->SetDirection(imageDirection); constantSource->SetSize(sizeInput_local); constantSource->SetConstant(0.); first = false; } else { paste->SetDestinationImage(paste->GetOutput()); } paste->SetSourceImage(outImage); paste->SetSourceRegion(outImage->GetLargestPossibleRegion()); paste->SetDestinationIndex(current_idx); TRY_AND_EXIT_ON_ITK_EXCEPTION(paste->Update()) projid += curBufferSize; } if (args_info.output_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(paste->GetOutput(), args_info.output_arg)) } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkscatterglarecorrection/rtkscatterglarecorrection.ggo ================================================ purpose "Reads projection images and correct them for scatter glare" option "verbose" v "Verbose execution" flag off option "output" o "Output filename" string no option "bufferSize" - "Number of projections computed at the same time" int no default="4" option "difference" d "Output the difference between input and corrected images" flag off section "Algorithm parameters" option "coefficients" c "Deconvolution kernel coefficients" float multiple yes ================================================ FILE: applications/rtkshowgeometry/README.md ================================================ # Geometry viewer `rtkshowgeometry` is a Python-only command line tool which provides an **interactive three-dimensional (3D) visualization** of RTK geometry files, projections and volumes using [`pyvista`](https://pyvista.org). It is useful for visually assessing a geometry, i.e., how it relates the CT image with the projection images by displaying the source positions and the projections positions and orientations in a 3D scene. ![geom](../../documentation/docs/ExternalData/ShowGeometry.png){w=800 alt="Show Geometry"} All geometries described in the [documentation](../../documentation/docs/Geometry.md) are supported: cone-beam and parallel (`SDD = 0`), flat and cylindrical (`RadiusCylindricalDetector > 0`) detectors. If no projections are given, the detector size defaults to 40% of the Source-to-Isocenter Distance (SID) and is centered around point `(u,v)=(0,0)`. ```{literalinclude} showgeometry.sh ``` ================================================ FILE: applications/rtkshowgeometry/rtkshowgeometry.py ================================================ #!/usr/bin/env python import argparse import numpy as np import itk from itk import RTK as rtk def build_parser(): parser = rtk.RTKArgumentParser( description=" Create an interactive 3D viewer for the given geometry and projections.", ) parser.add_argument( "--verbose", "-v", help="Verbose execution", action="store_true" ) parser.add_argument("--geometry", "-g", help="Geometry file name", required=True) parser.add_argument( "--show_trajectory", "-t", help="Show source trajectory", action="store_true" ) parser.add_argument("--input", "-i", help="Input volume") parser.add_argument("--hide_volume", help="hide input volume", action="store_true") rtk.add_rtkinputprojections_group(parser) # Remove the required flag for '--path' and '--regexp' arguments for action in parser._actions: if action.dest == "path": action.required = False if action.dest == "regexp": action.required = False # Parse the command line arguments return parser def process(args_info: argparse.Namespace): try: import pyvista as pv except ImportError: raise ImportError( "rtkshowgeometry requires the 'pyvista' package. " "Please install it with 'pip install pyvista' to use this feature." ) OutputPixelType = itk.F Dimension = 3 OutputImageType = itk.Image[OutputPixelType, Dimension] if args_info.regexp: reader = rtk.ProjectionsReader[OutputImageType].New() rtk.SetProjectionsReaderFromArgParse(reader, args_info) reader.Update() projections = reader.GetOutput() else: projections = None if args_info.geometry: geometry = rtk.read_geometry(args_info.geometry) if args_info.input: volume = itk.imread(args_info.input) plotter = pv.Plotter() all_points_list = [] source_positions = [] # Iterate through all frames to calculate bounds and positions for frame in range(len(geometry.GetGantryAngles())): source_pos = np.asarray(geometry.GetSourcePosition(frame), dtype=float)[:3] source_positions.append(source_pos) SDD = geometry.GetSourceToDetectorDistances()[frame] SID = geometry.GetSourceToIsocenterDistances()[frame] m = geometry.GetProjectionCoordinatesToFixedSystemMatrix(frame) m_np = np.asarray(m) if projections is not None: corners = [] idx = list(projections.GetLargestPossibleRegion().GetIndex()) size = list(projections.GetLargestPossibleRegion().GetSize()) # Calculate detector corners from projection indices for i in range(4): corner_pt = projections.TransformIndexToPhysicalPoint(idx) corner = np.asarray(corner_pt, dtype=float) corner = np.append(corner, 1.0) corner = m_np.dot(corner) corners.append(corner[:3]) if i == 0: idx[0] += size[0] - 1 elif i == 1: idx[1] += size[1] - 1 elif i == 2: idx[0] -= size[0] - 1 else: # Simulate detector corners for cases without projections detector_size = max(abs(source_pos)) * 0.4 half_size = detector_size / 2 local_corners = [ [-half_size, -half_size, 0], [half_size, -half_size, 0], [half_size, half_size, 0], [-half_size, half_size, 0], ] for corner in local_corners: corners = [m_np.dot(np.append(corner, 1.0))[:3]] # Handle parallel geometry (SDD = 0) if SDD == 0: rot_matrix = np.asarray(geometry.GetRotationMatrix(frame)) direction = np.array( [-rot_matrix[2][0], -rot_matrix[2][1], -rot_matrix[2][2]] ) direction *= 2.0 * SID source_corners = [corner - direction for corner in corners] frame_points = np.vstack([source_pos] + corners + source_corners) else: frame_points = np.vstack([source_pos] + corners) all_points_list.append(frame_points) if args_info.show_trajectory: source_positions = np.array(source_positions) source_trajectory = pv.PolyData(source_positions) plotter.add_mesh(source_trajectory, color="red", opacity=0.3) # Calculate bounds for the entire geometry all_points = np.vstack(all_points_list) max_value = np.max(all_points) min_value = np.min(all_points) # Compute "nice" tick spacing for grid raw_spacing = (abs(max_value) + abs(min_value)) / 4.0 exp_val = np.floor(np.log10(raw_spacing)) f = raw_spacing / (10**exp_val) if f < 1.5: nf = 1.0 elif f < 3.0: nf = 2.0 elif f < 7.0: nf = 5.0 else: nf = 10.0 nice_tick = nf * (10**exp_val) # Adjust bounds to align with tick spacing min_bounds = np.floor(min_value / nice_tick) * nice_tick max_bounds = np.ceil(max_value / nice_tick) * nice_tick # Add bounding box box = pv.Cube( bounds=[min_bounds, max_bounds, min_bounds, max_bounds, min_bounds, max_bounds] ) plotter.add_mesh(box, style="wireframe", opacity=0) # Configure plotter view plotter.show_grid(minor_ticks=True) plotter.renderer.SetGradientBackground(True) plotter.enable_parallel_projection() actors = {} if not args_info.hide_volume: # Handle volume visualization if provided if args_info.input: volume_array = itk.GetArrayFromImage(volume) spacing = volume.GetSpacing() origin = volume.GetOrigin() direction = volume.GetDirection() # Adjust volume array for correct orientation volume_array = np.transpose(volume_array, (2, 1, 0)) dims = volume_array.shape grid = pv.ImageData(dimensions=dims, spacing=spacing, origin=origin) grid.point_data["values"] = volume_array.ravel(order="F") # Apply direction matrix if needed if not np.allclose(direction, np.eye(3)): transform = pv.transform_matrix(direction.flatten()) grid.transform(transform) center = grid.center slice_x = grid.slice(normal="x", origin=center) slice_y = grid.slice(normal="y", origin=center) slice_z = grid.slice(normal="z", origin=center) # Add slices with opacity plotter.add_mesh(slice_x, cmap="gray", show_scalar_bar=False, opacity=0.5) plotter.add_mesh(slice_y, cmap="gray", show_scalar_bar=False, opacity=0.5) plotter.add_mesh(slice_z, cmap="gray", show_scalar_bar=False, opacity=0.5) else: # Add a placeholder sphere if no volume is provided volume_sphere = pv.Sphere( radius=0.1 * geometry.GetSourceToIsocenterDistances()[0], center=[0, 0, 0], ) plotter.add_mesh(volume_sphere, color="blue", opacity=0.5) def update(frame): frame = int(frame) # Update geometry parameters for the current frame gantry_angle = np.degrees(geometry.GetGantryAngles()[frame]) out_of_plane_angle = np.degrees(geometry.GetOutOfPlaneAngles()[frame]) in_plane_angle = np.degrees(geometry.GetInPlaneAngles()[frame]) SID = geometry.GetSourceToIsocenterDistances()[frame] offset_x = geometry.GetSourceOffsetsX()[frame] offset_y = geometry.GetSourceOffsetsY()[frame] SDD = geometry.GetSourceToDetectorDistances()[frame] proj_offset_x = geometry.GetProjectionOffsetsX()[frame] proj_offset_y = geometry.GetProjectionOffsetsY()[frame] info_text = ( f"Frame index {frame}\n" f"Gantry: {gantry_angle:.2f}°\n" f"Out-of-Plane: {out_of_plane_angle:.2f}°\n" f"In-Plane: {in_plane_angle:.2f}°\n" f"SID: {SID:.2f} mm\n" f"Offset X: {offset_x:.2f} mm\n" f"Offset Y: {offset_y:.2f} mm\n" f"SDD: {SDD:.2f} mm\n" f"Proj Offset X: {proj_offset_x:.2f} mm\n" f"Proj Offset Y: {proj_offset_y:.2f} mm" ) m = geometry.GetProjectionCoordinatesToFixedSystemMatrix(frame) m_np = np.asarray(m) detector_position = m_np[:3, 3] source_pos = np.asarray(geometry.GetSourcePosition(frame), dtype=float)[:3] # Detector setup if projections is not None: corners = [] idx = list(projections.GetLargestPossibleRegion().GetIndex()) size = list(projections.GetLargestPossibleRegion().GetSize()) for i in range(4): corner_pt = projections.TransformIndexToPhysicalPoint(idx) corner = np.asarray(corner_pt, dtype=float) corner = np.append(corner, 1.0) corner = m_np.dot(corner) corners.append(corner[:3]) if i == 0: idx[0] += size[0] - 1 elif i == 1: idx[1] += size[1] - 1 elif i == 2: idx[0] -= size[0] - 1 proj_array = np.asarray(itk.GetArrayFromImage(projections)) proj_slice = proj_array[frame].astype(np.float32) proj_slice -= proj_slice.min() proj_slice /= proj_slice.max() + 1e-8 texture_data = (proj_slice * 255).astype(np.uint8) else: detector_size = SID * 0.4 half_size = detector_size / 2 local_corners = [ [-half_size, -half_size, 0], [half_size, -half_size, 0], [half_size, half_size, 0], [-half_size, half_size, 0], ] corners = [] for corner in local_corners: corner = np.append(corner, 1.0) corner = m_np.dot(corner) corners.append(corner[:3]) texture_data = np.zeros((2, 2), dtype=np.uint8) corners_array = np.asarray(corners, dtype=float) # Handle cylindrical detector if radius is non-zero radius = geometry.GetRadiusCylindricalDetector() if radius > 0: detector_width = np.linalg.norm(corners[1] - corners[0]) detector_height = np.linalg.norm(corners[3] - corners[0]) # Create grid points n_points = 100 u = np.linspace(-detector_width / 2, detector_width / 2, n_points) v = np.linspace(-detector_height / 2, detector_height / 2, n_points) U, V = np.meshgrid(u, v) # Calculate cylindrical coordinates Theta = U / radius detector_center = np.mean(corners_array, axis=0) u_dir = (corners[1] - corners[0]) / np.linalg.norm(corners[1] - corners[0]) v_dir = (corners[3] - corners[0]) / np.linalg.norm(corners[3] - corners[0]) n_dir = np.cross(u_dir, v_dir) # Create rotation matrix for detector orientation rot_matrix = np.column_stack([u_dir, v_dir, n_dir]) # Calculate curved surface points X = radius * np.sin(Theta) Z = radius * (1 - np.cos(Theta)) Y = V points = np.column_stack([X.ravel(), Y.ravel(), Z.ravel()]) points = points @ rot_matrix.T + detector_center # Create faces for the curved surface curved_faces = [] for i in range(n_points - 1): for j in range(n_points - 1): idx00 = i * n_points + j idx01 = i * n_points + (j + 1) idx10 = (i + 1) * n_points + j idx11 = (i + 1) * n_points + (j + 1) curved_faces.extend([3, idx00, idx01, idx11]) curved_faces.extend([3, idx00, idx11, idx10]) # Create texture coordinates tcoords = np.zeros((n_points * n_points, 2), dtype=np.float32) u_coords, v_coords = np.meshgrid( np.linspace(0, 1, n_points), np.linspace(1, 0, n_points), ) tcoords[:, 0] = u_coords.ravel() tcoords[:, 1] = v_coords.ravel() # Get the corner points of the curved surface curved_corners = [ points[0], # Bottom left points[n_points - 1], # Bottom right points[-1], # Top right points[-n_points], # Top left ] corners_array = points faces = np.array(curved_faces) detector_poly = pv.PolyData(corners_array, faces) detector_poly.active_texture_coordinates = tcoords corners = curved_corners else: faces = np.hstack([[4], np.arange(4)]) detector_poly = pv.PolyData(corners_array, faces) tcoords = np.asarray([[0, 1], [1, 1], [1, 0], [0, 0]], dtype=np.float32) detector_poly.active_texture_coordinates = tcoords texture = pv.Texture(texture_data) u_vec = (corners[1] - corners[0]) * 0.5 v_vec = (corners[3] - corners[0]) * 0.5 arrow_params = { "tip_length": 0.1, "tip_radius": 0.04, "shaft_radius": 0.02, "scale": "auto", } # Create arrows from center u_arrow = pv.Arrow(start=detector_position, direction=u_vec, **arrow_params) v_arrow = pv.Arrow(start=detector_position, direction=v_vec, **arrow_params) # Update label positions to be near arrow tips u_label_pos = detector_position + u_vec v_label_pos = detector_position + v_vec if SDD == 0: rot_matrix = np.asarray(geometry.GetRotationMatrix(frame)) direction = ( np.array([-rot_matrix[2][0], -rot_matrix[2][1], -rot_matrix[2][2]]) * 2.0 * SID ) source = detector_position - direction central_line = pv.Line(source, detector_position) else: central_line = pv.Line(source_pos, detector_position) # Update or create visualization actors if not actors: actors["detector"] = plotter.add_mesh( detector_poly, texture=texture, opacity=0.5 ) actors["central_line"] = plotter.add_mesh( central_line, color="black", line_width=2 ) actors["text"] = plotter.add_text( info_text, position="upper_right", font_size=10, color="white" ) actors["u_dir"] = plotter.add_mesh(u_arrow, color="green") actors["v_dir"] = plotter.add_mesh(v_arrow, color="green") else: actors["detector"].GetMapper().GetInput().points = corners_array actors["detector"].texture = texture actors["central_line"].GetMapper().GetInput().points = central_line.points actors["text"].SetText(3, info_text) actors["u_dir"].GetMapper().GetInput().points = u_arrow.points actors["v_dir"].GetMapper().GetInput().points = v_arrow.points # Handle parallel geometry (SDD = 0) if SDD == 0: rot_matrix = np.asarray(geometry.GetRotationMatrix(frame)) direction = np.array( [-rot_matrix[2][0], -rot_matrix[2][1], -rot_matrix[2][2]] ) direction *= 2.0 * SID source_corners = [corner - direction for corner in corners] source_corners_array = np.asarray(source_corners, dtype=float) source_faces = np.hstack([[4], np.arange(4)]) source_poly = pv.PolyData(source_corners_array, source_faces) if "source_plane" not in actors: actors["source_plane"] = plotter.add_mesh( source_poly, color="red", opacity=0.3 ) else: actors["source_plane"].GetMapper().GetInput().points = ( source_corners_array ) if "lines" not in actors: actors["lines"] = [] for src_corner, det_corner in zip(source_corners, corners): line = pv.Line(src_corner, det_corner) actors["lines"].append( plotter.add_mesh(line, color="black", line_width=1) ) else: for i, (src_corner, det_corner) in enumerate( zip(source_corners, corners) ): line = pv.Line(src_corner, det_corner) actors["lines"][i].GetMapper().GetInput().points = line.points else: if "lines" not in actors: actors["lines"] = [] for corner in corners: line = pv.Line(source_pos, corner) actors["lines"].append( plotter.add_mesh(line, color="black", line_width=1) ) else: for i, corner in enumerate(corners): line = pv.Line(source_pos, corner) actors["lines"][i].GetMapper().GetInput().points = line.points if "labels" in actors: plotter.remove_actor(actors["labels"]) actors["labels"] = plotter.add_point_labels( points=[u_label_pos, v_label_pos], labels=["u", "v"], text_color="green", font_size=24, shape_opacity=0, always_visible=True, show_points=False, ) plotter.camera.zoom(0.8) plotter.add_slider_widget( callback=update, rng=[0, len(geometry.GetGantryAngles()) - 1], value=0, title="Frame", style="modern", pointa=(0.02, 0.07), pointb=(0.98, 0.07), color="lightgrey", fmt="%.0f", interaction_event="always", ) update(0) plotter.show_axes() plotter.show() def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkshowgeometry/showgeometry.sh ================================================ # Create a simulated geometry rtksimulatedgeometry -n 180 -o geometry.xml # Create projections of the phantom file rtkprojectshepploganphantom -g geometry.xml -o projections.mha --spacing 2 --size 256 # Reconstruct rtkfdk -p . -r projections.mha -o fdk.mha -g geometry.xml --spacing 2 --size 256 # Show geometry rtkshowgeometry -p . -r projections.mha --geometry geometry.xml --input fdk.mha --show_trajectory ================================================ FILE: applications/rtksimulatedgeometry/CMakeLists.txt ================================================ WRAP_GGO(rtksimulatedgeometry_GGO_C rtksimulatedgeometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtksimulatedgeometry rtksimulatedgeometry.cxx ${rtksimulatedgeometry_GGO_C}) target_link_libraries(rtksimulatedgeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtksimulatedgeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtksimulatedgeometry/rtksimulatedgeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtksimulatedgeometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" int main(int argc, char * argv[]) { GGO(rtksimulatedgeometry, args_info); // RTK geometry object auto geometry = rtk::ThreeDCircularProjectionGeometry::New(); // Projection matrices for (int noProj = 0; noProj < args_info.nproj_arg; noProj++) { double angle = args_info.first_angle_arg + noProj * args_info.arc_arg / args_info.nproj_arg; geometry->AddProjection(args_info.sid_arg, args_info.sdd_arg, angle, args_info.proj_iso_x_arg, args_info.proj_iso_y_arg, args_info.out_angle_arg, args_info.in_angle_arg, args_info.source_x_arg, args_info.source_y_arg); } // Set cylindrical detector radius if (args_info.rad_cyl_given) geometry->SetRadiusCylindricalDetector(args_info.rad_cyl_arg); // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(geometry, args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtksimulatedgeometry/rtksimulatedgeometry.ggo ================================================ purpose "Creates an RTK geometry file from simulated/regular trajectory. See https://docs.openrtk.org/en/latest/documentation/docs/Geometry.html for more information." option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes option "first_angle" f "First angle in degrees" double no default="0" option "nproj" n "Number of projections" int yes option "arc" a "Angular arc covevered by the acquisition in degrees" double no default="360" option "sdd" - "Source to detector distance (mm)" double no default="1536" option "sid" - "Source to isocenter distance (mm)" double no default="1000" option "proj_iso_x" - "X coordinate of detector point (0,0) mm in rotated coordinate system" double no default="0" option "proj_iso_y" - "Y coordinate of detector point (0,0) mm in rotated coordinate system" double no default="0" option "source_x" - "X coordinate of source in rotated coordinate system" double no default="0" option "source_y" - "Y coordinate of source in rotated coordinate system" double no default="0" option "out_angle" - "Out of plane angle" double no default="0" option "in_angle" - "In plane angle" double no default="0" option "rad_cyl" - "Radius cylinder of cylindrical detector" double no default="0" ================================================ FILE: applications/rtksimulatedgeometry/rtksimulatedgeometry.py ================================================ #!/usr/bin/env python import argparse from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Creates an RTK geometry file from simulated/regular trajectory. See https://docs.openrtk.org/en/latest/documentation/docs/Geometry.html for more information." ) parser.add_argument( "--verbose", "-v", type=bool, default=False, help="Verbose execution" ) parser.add_argument( "--nproj", "-n", type=int, help="Number of projections", required=True ) parser.add_argument("--output", "-o", help="Output file name", required=True) parser.add_argument( "--first_angle", "-f", type=float, default=0, help="First angle in degrees" ) parser.add_argument( "--arc", "-a", type=float, default=360, help="Angular arc covevered by the acquisition in degrees", ) parser.add_argument( "--sdd", type=float, default=1536, help="Source to detector distance (mm)" ) parser.add_argument( "--sid", type=float, default=1000, help="Source to isocenter distance (mm)" ) parser.add_argument( "--proj_iso_x", type=float, default=0, help="X coordinate of detector point (0,0) mm in rotated coordinate system", ) parser.add_argument( "--proj_iso_y", type=float, default=0, help="Y coordinate of detector point (0,0) mm in rotated coordinate system", ) parser.add_argument( "--source_x", type=float, default=0, help="X coordinate of source in rotated coordinate system", ) parser.add_argument( "--source_y", type=float, default=0, help="Y coordinate of source in rotated coordinate system", ) parser.add_argument("--out_angle", type=float, default=0, help="Out of plane angle") parser.add_argument("--in_angle", type=float, default=0, help="In plane angle") parser.add_argument( "--rad_cyl", type=float, default=0, help="Radius cylinder of cylindrical detector", ) # Parse the command line arguments return parser def process(args: argparse.Namespace): # Simulated Geometry GeometryType = rtk.ThreeDCircularProjectionGeometry geometry = GeometryType.New() for noProj in range(0, args.nproj): angle = args.first_angle + noProj * args.arc / args.nproj geometry.AddProjection( args.sid, args.sdd, angle, args.proj_iso_x, args.proj_iso_y, args.out_angle, args.in_angle, args.source_x, args.source_y, ) geometry.SetRadiusCylindricalDetector(args.rad_cyl) rtk.write_geometry(geometry, args.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkspectraldenoiseprojections/CMakeLists.txt ================================================ WRAP_GGO(rtkspectraldenoiseprojections_GGO_C rtkspectraldenoiseprojections.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkspectraldenoiseprojections rtkspectraldenoiseprojections.cxx ${rtkspectraldenoiseprojections_GGO_C}) target_link_libraries(rtkspectraldenoiseprojections RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkspectraldenoiseprojections) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkspectraldenoiseprojections/rtkspectraldenoiseprojections.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkspectraldenoiseprojections_ggo.h" #include "rtkMacro.h" #include "rtkGgoFunctions.h" #include "rtkConditionalMedianImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtkspectraldenoiseprojections, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::VectorImage; // Reader OutputImageType::Pointer input; TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) // Remove aberrant pixels using MedianType = rtk::ConditionalMedianImageFilter; auto median = MedianType::New(); median->SetThresholdMultiplier(args_info.multiplier_arg); MedianType::MedianRadiusType radius; if (args_info.radius_given) { radius.Fill(args_info.radius_arg[0]); for (unsigned int i = 0; i < args_info.radius_given; i++) radius[i] = args_info.radius_arg[i]; } median->SetRadius(radius); median->SetInput(input); // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(median->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkspectraldenoiseprojections/rtkspectraldenoiseprojections.ggo ================================================ purpose "Replaces aberrant pixels by the median in a small neighborhood around them. Pixels are aberrant if the difference between their value and the median is larger that threshold multiplier * the standard deviation in the neighborhood" option "verbose" v "Verbose execution" flag off option "input" i "Input file name" string yes option "output" o "Output file name" string yes option "multiplier" m "Threshold multiplier (actual threshold is obtained by multiplying by standard dev. of neighborhood)" double no default="1" option "radius" r "Radius of neighborhood in each direction (actual radius is 2r+1)" int multiple no default="1" ================================================ FILE: applications/rtkspectralforwardmodel/CMakeLists.txt ================================================ WRAP_GGO(rtkspectralforwardmodel_GGO_C rtkspectralforwardmodel.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkspectralforwardmodel rtkspectralforwardmodel.cxx ${rtkspectralforwardmodel_GGO_C}) target_link_libraries(rtkspectralforwardmodel RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkspectralforwardmodel) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkspectralforwardmodel/rtkspectralforwardmodel.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkspectralforwardmodel_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkMacro.h" #include "rtkSpectralForwardModelImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtkspectralforwardmodel, args_info); using PixelValueType = float; constexpr unsigned int Dimension = 3; using DecomposedProjectionType = itk::VectorImage; using MeasuredProjectionsType = itk::VectorImage; using IncidentSpectrumImageType = itk::Image; using DetectorResponseImageType = itk::Image; using MaterialAttenuationsImageType = itk::Image; // Read all inputs DecomposedProjectionType::Pointer decomposedProjection; TRY_AND_EXIT_ON_ITK_EXCEPTION(decomposedProjection = itk::ReadImage(args_info.input_arg)) IncidentSpectrumImageType::Pointer incidentSpectrum; TRY_AND_EXIT_ON_ITK_EXCEPTION(incidentSpectrum = itk::ReadImage(args_info.incident_arg)) DetectorResponseImageType::Pointer detectorResponse; TRY_AND_EXIT_ON_ITK_EXCEPTION(detectorResponse = itk::ReadImage(args_info.detector_arg)) MaterialAttenuationsImageType::Pointer materialAttenuations; TRY_AND_EXIT_ON_ITK_EXCEPTION(materialAttenuations = itk::ReadImage(args_info.attenuations_arg)) // Get parameters from the images const unsigned int NumberOfMaterials = materialAttenuations->GetLargestPossibleRegion().GetSize()[0]; const unsigned int NumberOfSpectralBins = args_info.thresholds_given; const unsigned int MaximumEnergy = incidentSpectrum->GetLargestPossibleRegion().GetSize()[0]; // Generate a set of zero-filled photon count projections auto measuredProjections = MeasuredProjectionsType::New(); measuredProjections->CopyInformation(decomposedProjection); measuredProjections->SetVectorLength(NumberOfSpectralBins); measuredProjections->Allocate(); // Read the thresholds on command line itk::VariableLengthVector thresholds; thresholds.SetSize(NumberOfSpectralBins + 1); for (unsigned int i = 0; i < NumberOfSpectralBins; i++) thresholds[i] = args_info.thresholds_arg[i]; // Add the maximum pulse height at the end unsigned int MaximumPulseHeight = detectorResponse->GetLargestPossibleRegion().GetSize()[1]; thresholds[NumberOfSpectralBins] = MaximumPulseHeight; // Check that the inputs have the expected size DecomposedProjectionType::IndexType indexDecomp; indexDecomp.Fill(0); if (decomposedProjection->GetPixel(indexDecomp).Size() != NumberOfMaterials) itkGenericExceptionMacro(<< "Decomposed projections (i.e. initialization data) image has vector size " << decomposedProjection->GetPixel(indexDecomp).Size() << ", should be " << NumberOfMaterials); MeasuredProjectionsType::IndexType indexSpect; indexSpect.Fill(0); if (measuredProjections->GetPixel(indexSpect).Size() != NumberOfSpectralBins) itkGenericExceptionMacro(<< "Spectral projections (i.e. photon count data) image has vector size " << measuredProjections->GetPixel(indexSpect).Size() << ", should be " << NumberOfSpectralBins); if (detectorResponse->GetLargestPossibleRegion().GetSize()[0] != MaximumEnergy) itkGenericExceptionMacro(<< "Detector response image has " << detectorResponse->GetLargestPossibleRegion().GetSize()[0] << "energies, should have " << MaximumEnergy); // Create and set the filter auto forward = rtk::SpectralForwardModelImageFilter::New(); forward->SetInputDecomposedProjections(decomposedProjection); forward->SetInputMeasuredProjections(measuredProjections); forward->SetInputIncidentSpectrum(incidentSpectrum); forward->SetDetectorResponse(detectorResponse); forward->SetMaterialAttenuations(materialAttenuations); forward->SetThresholds(thresholds); forward->SetIsSpectralCT(true); if (args_info.cramer_rao_given) forward->SetComputeCramerRaoLowerBound(true); if (args_info.variances_given) forward->SetComputeVariances(true); TRY_AND_EXIT_ON_ITK_EXCEPTION(forward->Update()) // Write output TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(forward->GetOutput(), args_info.output_arg)) // If requested, write the Cramer-Rao lower bound if (args_info.cramer_rao_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(forward->GetOutputCramerRaoLowerBound(), args_info.cramer_rao_arg)) } // If requested, write the variance if (args_info.variances_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(forward->GetOutputVariances(), args_info.variances_arg)) } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkspectralforwardmodel/rtkspectralforwardmodel.ggo ================================================ purpose "Computes expected photon counts from incident spectrum, material attenuations, detector response and material-decomposed projections" option "verbose" v "Verbose execution" flag off option "output" o "Output file name (photon counts)" string yes option "input" i "Material-decomposed projections" string yes option "detector" d "Detector response file" string yes option "incident" - "Incident spectrum file" string yes option "attenuations" a "Material attenuations file" string yes option "thresholds" t "Lower threshold of bins, expressed in pulse height" double yes multiple option "cramer_rao" - "File name for the output Cramer Rao Lower Bound (estimation of the variance in the material decomposed images)" string no option "variances" - "Output variances of photon counts, file name" string no ================================================ FILE: applications/rtkspectralonestep/CMakeLists.txt ================================================ WRAP_GGO(rtkspectralonestep_GGO_C rtkspectralonestep.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ../rtkiterations_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkspectralonestep rtkspectralonestep.cxx ${rtkspectralonestep_GGO_C}) target_link_libraries(rtkspectralonestep RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkspectralonestep) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkspectralonestep/rtkspectralonestep.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkspectralonestep_ggo.h" #include "rtkGgoFunctions.h" #include "rtkMechlemOneStepSpectralReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkReorderProjectionsImageFilter.h" #include "rtkSpectralForwardModelImageFilter.h" #include // std::shuffle #include // std::vector #include // std::default_random_engine #include #include #include itk::ImageIOBase::Pointer GetFileHeader(const std::string & filename) { itk::ImageIOBase::Pointer reader = itk::ImageIOFactory::CreateImageIO(filename.c_str(), itk::ImageIOFactory::IOFileModeEnum::ReadMode); if (!reader) { itkGenericExceptionMacro(<< "Could not read " << filename); } reader->SetFileName(filename); reader->ReadImageInformation(); return reader; } namespace rtk { template void rtkspectralonestep(const args_info_rtkspectralonestep & args_info) { using dataType = float; constexpr unsigned int Dimension = 3; // Define types for the input images #ifdef RTK_USE_CUDA using MaterialVolumesType = typename itk::CudaImage, Dimension>; using MeasuredProjectionsType = typename itk::CudaImage, Dimension>; using IncidentSpectrumType = itk::CudaImage; using DetectorResponseType = itk::CudaImage; using MaterialAttenuationsType = itk::CudaImage; #else using MaterialVolumesType = typename itk::Image, Dimension>; using MeasuredProjectionsType = typename itk::Image, Dimension>; using IncidentSpectrumType = itk::Image; using DetectorResponseType = itk::Image; using MaterialAttenuationsType = itk::Image; #endif // Instantiate and update the readers typename MeasuredProjectionsType::Pointer mea; TRY_AND_EXIT_ON_ITK_EXCEPTION(mea = itk::ReadImage(args_info.spectral_arg)) IncidentSpectrumType::Pointer incidentSpectrum; TRY_AND_EXIT_ON_ITK_EXCEPTION(incidentSpectrum = itk::ReadImage(args_info.incident_arg)) DetectorResponseType::Pointer detectorResponse; TRY_AND_EXIT_ON_ITK_EXCEPTION(detectorResponse = itk::ReadImage(args_info.detector_arg)) MaterialAttenuationsType::Pointer materialAttenuations; TRY_AND_EXIT_ON_ITK_EXCEPTION(materialAttenuations = itk::ReadImage(args_info.attenuations_arg)) // Read Support Mask if given IncidentSpectrumType::Pointer supportmask; if (args_info.mask_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(supportmask = itk::ReadImage(args_info.mask_arg)) } // Read spatial regularization weights if given IncidentSpectrumType::Pointer spatialRegulWeighs; if (args_info.regul_spatial_weights_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(spatialRegulWeighs = itk::ReadImage(args_info.regul_spatial_weights_arg)) } // Read projections weights if given IncidentSpectrumType::Pointer projectionWeights; if (args_info.projection_weights_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(projectionWeights = itk::ReadImage(args_info.projection_weights_arg)) } // Create input: either an existing volume read from a file or a blank image typename MaterialVolumesType::Pointer input; if (args_info.input_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) } else { // Create new empty volume using ConstantImageSourceType = typename rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo(constantImageSource, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(constantImageSource->Update()) input = constantImageSource->GetOutput(); } // Read the material attenuations image as a matrix MaterialAttenuationsType::IndexType indexMat; unsigned int nEnergies = materialAttenuations->GetLargestPossibleRegion().GetSize()[1]; vnl_matrix materialAttenuationsMatrix(nEnergies, VNumberOfMaterials); for (unsigned int energy = 0; energy < nEnergies; energy++) { indexMat[1] = energy; for (unsigned int material = 0; material < VNumberOfMaterials; material++) { indexMat[0] = material; materialAttenuationsMatrix[energy][material] = materialAttenuations->GetPixel(indexMat); } } // Read the thresholds on command line and check their number itk::VariableLengthVector thresholds; thresholds.SetSize(VNumberOfBins + 1); if (args_info.thresholds_given == VNumberOfBins) { for (unsigned int bin = 0; bin < VNumberOfBins; bin++) thresholds[bin] = args_info.thresholds_arg[bin]; // Add the maximum pulse height at the end double MaximumPulseHeight = detectorResponse->GetLargestPossibleRegion().GetSize()[1]; thresholds[VNumberOfBins] = MaximumPulseHeight; } else { itkGenericExceptionMacro(<< "Number of thresholds " << args_info.thresholds_given << " does not match the number of bins " << VNumberOfBins); } // Read the detector response image as a matrix, and bin it vnl_matrix drm = rtk::SpectralBinDetectorResponse(detectorResponse.GetPointer(), thresholds, nEnergies); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Read the regularization parameters typename MaterialVolumesType::RegionType::SizeType regulRadius; if (args_info.regul_radius_given) for (unsigned int i = 0; i < Dimension; i++) regulRadius[i] = args_info.regul_radius_arg[std::min(i, args_info.regul_radius_given - 1)]; else regulRadius.Fill(0); typename MaterialVolumesType::PixelType regulWeights; if (args_info.regul_weights_given) for (unsigned int i = 0; i < VNumberOfMaterials; i++) regulWeights[i] = args_info.regul_weights_arg[std::min(i, args_info.regul_weights_given - 1)]; else regulWeights.Fill(0); // Set the forward and back projection filters to be used using MechlemFilterType = typename rtk:: MechlemOneStepSpectralReconstructionFilter; auto mechlemOneStep = MechlemFilterType::New(); SetForwardProjectionFromGgo(args_info, mechlemOneStep.GetPointer()); SetBackProjectionFromGgo(args_info, mechlemOneStep.GetPointer()); mechlemOneStep->SetInputMaterialVolumes(input); mechlemOneStep->SetInputIncidentSpectrum(incidentSpectrum); mechlemOneStep->SetBinnedDetectorResponse(drm); mechlemOneStep->SetMaterialAttenuations(materialAttenuationsMatrix); mechlemOneStep->SetNumberOfIterations(args_info.niterations_arg); mechlemOneStep->SetNumberOfSubsets(args_info.subsets_arg); mechlemOneStep->SetRegularizationRadius(regulRadius); mechlemOneStep->SetRegularizationWeights(regulWeights); if (args_info.reset_nesterov_given) mechlemOneStep->SetResetNesterovEvery(args_info.reset_nesterov_arg); if (args_info.mask_given) mechlemOneStep->SetSupportMask(supportmask); if (args_info.regul_spatial_weights_given) mechlemOneStep->SetSpatialRegularizationWeights(spatialRegulWeighs); mechlemOneStep->SetInputMeasuredProjections(mea); mechlemOneStep->SetGeometry(geometry); if (args_info.projection_weights_given) mechlemOneStep->SetProjectionWeights(projectionWeights); REPORT_ITERATIONS(mechlemOneStep, MechlemFilterType, MaterialVolumesType); TRY_AND_EXIT_ON_ITK_EXCEPTION(mechlemOneStep->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(mechlemOneStep->GetOutput(), args_info.output_arg)) } } // namespace rtk int main(int argc, char * argv[]) { GGO(rtkspectralonestep, args_info); try { itk::ImageIOBase::Pointer headerInputMeasuredProjections = GetFileHeader(args_info.spectral_arg); unsigned int nBins = headerInputMeasuredProjections->GetNumberOfComponents(); itk::ImageIOBase::Pointer headerAttenuations = GetFileHeader(args_info.attenuations_arg); unsigned int nMaterials = headerAttenuations->GetDimensions(0); if (nMaterials == 3 && nBins == 1) rtk::rtkspectralonestep<1, 3>(args_info); else if (nMaterials == 2 && nBins == 1) rtk::rtkspectralonestep<1, 2>(args_info); else if (nMaterials == 2 && nBins == 2) rtk::rtkspectralonestep<2, 2>(args_info); else if (nMaterials == 2 && nBins == 5) rtk::rtkspectralonestep<5, 2>(args_info); else if (nMaterials == 3 && nBins == 5) rtk::rtkspectralonestep<5, 3>(args_info); else { std::cerr << nMaterials << " materials and " << nBins << " bins is not handled" << std::endl; return EXIT_FAILURE; } } catch (itk::ExceptionObject & err) { std::cerr << "ExceptionObject caught in rtkspectraleonestep." << std::endl; std::cerr << err << std::endl; exit(EXIT_FAILURE); } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkspectralonestep/rtkspectralonestep.ggo ================================================ purpose "Reconstructs a set of 3D volumes from a set of photon counts projections, using the method described by Mechlem et al. in IEEE TMI in 2017" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niterations" n "Number of iterations" int no default="5" option "input" i "Material volumes initial guess" string no option "spectral" s "Spectral projections, i.e. photon counts" string yes option "detector" d "Detector response file" string yes option "incident" - "Incident spectrum file (mhd image)" string yes option "attenuations" a "Material attenuations file" string yes option "mask" m "Apply a support binary mask: reconstruction kept null outside" string no option "regul_spatial_weights" - "One-component image of spatial regularization weights" string no option "projection_weights" - "One-component image of projection weights (size of photon counts)" string no option "thresholds" t "Lower threshold of bins, expressed in pulse height" double yes multiple option "subsets" - "Number of subsets of projections (should not exceed 6)" int no default="4" option "regul_weights" - "Regularization parameters for each material" double multiple no option "regul_radius" - "Radius of the neighborhood for regularization" int multiple no option "reset_nesterov" - "Reset Nesterov after a number of subsets" int no ================================================ FILE: applications/rtkspectralrooster/CMakeLists.txt ================================================ WRAP_GGO(rtkspectralrooster_GGO_C rtkspectralrooster.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkspectralrooster rtkspectralrooster.cxx ${rtkspectralrooster_GGO_C}) target_link_libraries(rtkspectralrooster RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkspectralrooster) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkspectralrooster/rtkspectralrooster.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkspectralrooster_ggo.h" #include "rtkGgoFunctions.h" #include "rtkFourDROOSTERConeBeamReconstructionFilter.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkSignalToInterpolationWeights.h" #include "rtkVectorImageToImageFilter.h" #include "rtkImageToVectorImageFilter.h" #ifdef RTK_USE_CUDA # include "itkCudaImage.h" # include "rtkCudaConstantVolumeSeriesSource.h" #endif #include #include #include #include int main(int argc, char * argv[]) { GGO(rtkspectralrooster, args_info); using PixelValueType = float; constexpr unsigned int Dimension = 3; using DecomposedProjectionType = itk::VectorImage; using MaterialsVolumeType = itk::VectorImage; #ifdef RTK_USE_CUDA using VolumeSeriesType = itk::CudaImage; using ProjectionStackType = itk::CudaImage; #else using VolumeSeriesType = itk::Image; using ProjectionStackType = itk::Image; #endif // Projections reader DecomposedProjectionType::Pointer decomposedProjection; TRY_AND_EXIT_ON_ITK_EXCEPTION(decomposedProjection = itk::ReadImage(args_info.projection_arg)) const unsigned int NumberOfMaterials = decomposedProjection->GetVectorLength(); // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create 4D input. Fill it either with an existing materials volume read from a file or a blank image VolumeSeriesType::Pointer input; auto vecVol2VolSeries = rtk::VectorImageToImageFilter::New(); if (args_info.input_given) { // Using std::cout because itkWarningMacro cannot be used outside a class if (args_info.like_given) std::cout << "WARNING: Option --like ignored, since option --input was passed" << std::endl; MaterialsVolumeType::Pointer reference; TRY_AND_EXIT_ON_ITK_EXCEPTION(reference = itk::ReadImage(args_info.input_arg)) vecVol2VolSeries->SetInput(reference); vecVol2VolSeries->Update(); input = vecVol2VolSeries->GetOutput(); } else if (args_info.like_given) { MaterialsVolumeType::Pointer reference; TRY_AND_EXIT_ON_ITK_EXCEPTION(reference = itk::ReadImage(args_info.like_arg)) vecVol2VolSeries->SetInput(reference); vecVol2VolSeries->UpdateOutputInformation(); using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); constantImageSource->SetInformationFromImage(vecVol2VolSeries->GetOutput()); constantImageSource->Update(); input = constantImageSource->GetOutput(); } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); VolumeSeriesType::SizeType inputSize; VolumeSeriesType::SpacingType inputSpacing; VolumeSeriesType::PointType inputOrigin; VolumeSeriesType::DirectionType inputDirection; inputSize[Dimension] = decomposedProjection->GetVectorLength(); inputSpacing[Dimension] = 1; inputOrigin[Dimension] = 0; inputDirection.SetIdentity(); for (unsigned int i = 0; i < std::min(args_info.size_given, Dimension); i++) inputSize[i] = args_info.size_arg[i]; inputSpacing.Fill(args_info.spacing_arg[0]); for (unsigned int i = 0; i < std::min(args_info.spacing_given, Dimension); i++) inputSpacing[i] = args_info.spacing_arg[i]; for (unsigned int i = 0; i < Dimension; i++) inputOrigin[i] = inputSpacing[i] * (inputSize[i] - 1) * -0.5; for (unsigned int i = 0; i < std::min(args_info.origin_given, Dimension); i++) inputOrigin[i] = args_info.origin_arg[i]; if (args_info.direction_given) for (unsigned int i = 0; i < Dimension; i++) for (unsigned int j = 0; j < Dimension; j++) inputDirection[i][j] = args_info.direction_arg[i * Dimension + j]; else inputDirection.SetIdentity(); constantImageSource->SetOrigin(inputOrigin); constantImageSource->SetSpacing(inputSpacing); constantImageSource->SetDirection(inputDirection); constantImageSource->SetSize(inputSize); constantImageSource->SetConstant(0.); TRY_AND_EXIT_ON_ITK_EXCEPTION(constantImageSource->Update()); input = constantImageSource->GetOutput(); } // Duplicate geometry and transform the N M-vector projections into N*M scalar projections // Each material will occupy one frame of the 4D reconstruction, therefore all projections // of one material need to have the same phase. // Note : the 4D CG filter is optimized when projections with identical phases are packed together // Geometry unsigned int initialNumberOfProjections = decomposedProjection->GetLargestPossibleRegion().GetSize()[Dimension - 1]; for (unsigned int material = 1; material < NumberOfMaterials; material++) { for (unsigned int proj = 0; proj < initialNumberOfProjections; proj++) { geometry->AddProjectionInRadians(geometry->GetSourceToIsocenterDistances()[proj], geometry->GetSourceToDetectorDistances()[proj], geometry->GetGantryAngles()[proj], geometry->GetProjectionOffsetsX()[proj], geometry->GetProjectionOffsetsY()[proj], geometry->GetOutOfPlaneAngles()[proj], geometry->GetInPlaneAngles()[proj], geometry->GetSourceOffsetsX()[proj], geometry->GetSourceOffsetsY()[proj]); geometry->SetCollimationOfLastProjection(geometry->GetCollimationUInf()[proj], geometry->GetCollimationUSup()[proj], geometry->GetCollimationVInf()[proj], geometry->GetCollimationVSup()[proj]); } } // Signal std::vector fakeSignal; for (unsigned int material = 0; material < NumberOfMaterials; material++) { for (unsigned int proj = 0; proj < initialNumberOfProjections; proj++) { fakeSignal.push_back(itk::Math::Round((double)material / (double)NumberOfMaterials * 1000) / 1000); } } // Projections auto vproj2proj = rtk::VectorImageToImageFilter::New(); vproj2proj->SetInput(decomposedProjection); TRY_AND_EXIT_ON_ITK_EXCEPTION(vproj2proj->Update()) // Release the memory holding the stack of original projections decomposedProjection->ReleaseData(); // Compute the interpolation weights auto signalToInterpolationWeights = rtk::SignalToInterpolationWeights::New(); signalToInterpolationWeights->SetSignal(fakeSignal); signalToInterpolationWeights->SetNumberOfReconstructedFrames(NumberOfMaterials); TRY_AND_EXIT_ON_ITK_EXCEPTION(signalToInterpolationWeights->Update()) // Set the forward and back projection filters to be used auto rooster = rtk::FourDROOSTERConeBeamReconstructionFilter::New(); SetForwardProjectionFromGgo(args_info, rooster.GetPointer()); SetBackProjectionFromGgo(args_info, rooster.GetPointer()); rooster->SetInputVolumeSeries(input); rooster->SetCG_iterations(args_info.cgiter_arg); rooster->SetMainLoop_iterations(args_info.niter_arg); rooster->SetCudaConjugateGradient(args_info.cudacg_flag); rooster->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); // Set the newly ordered arguments rooster->SetInputProjectionStack(vproj2proj->GetOutput()); rooster->SetGeometry(geometry); rooster->SetWeights(signalToInterpolationWeights->GetOutput()); rooster->SetSignal(fakeSignal); // For each optional regularization step, set whether or not // it should be performed, and provide the necessary inputs // Positivity if (args_info.nopositivity_flag) rooster->SetPerformPositivity(false); else rooster->SetPerformPositivity(true); // No motion mask is used, since there is no motion rooster->SetPerformMotionMask(false); // Spatial TV if (args_info.gamma_space_given) { rooster->SetGammaTVSpace(args_info.gamma_space_arg); rooster->SetTV_iterations(args_info.tviter_arg); rooster->SetPerformTVSpatialDenoising(true); } else rooster->SetPerformTVSpatialDenoising(false); // Spatial wavelets if (args_info.threshold_given) { rooster->SetSoftThresholdWavelets(args_info.threshold_arg); rooster->SetOrder(args_info.order_arg); rooster->SetNumberOfLevels(args_info.levels_arg); rooster->SetPerformWaveletsSpatialDenoising(true); } else rooster->SetPerformWaveletsSpatialDenoising(false); // Temporal TV if (args_info.gamma_time_given) { rooster->SetGammaTVTime(args_info.gamma_time_arg); rooster->SetTV_iterations(args_info.tviter_arg); rooster->SetPerformTVTemporalDenoising(true); } else rooster->SetPerformTVTemporalDenoising(false); // Temporal L0 if (args_info.lambda_time_arg) { rooster->SetLambdaL0Time(args_info.lambda_time_arg); rooster->SetL0_iterations(args_info.l0iter_arg); rooster->SetPerformL0TemporalDenoising(true); } else rooster->SetPerformL0TemporalDenoising(false); // Total nuclear variation if (args_info.gamma_tnv_given) { rooster->SetGammaTNV(args_info.gamma_tnv_arg); rooster->SetTV_iterations(args_info.tviter_arg); rooster->SetPerformTNVDenoising(true); } else rooster->SetPerformTNVDenoising(false); TRY_AND_EXIT_ON_ITK_EXCEPTION(rooster->Update()) // Convert to result to a vector image auto volSeries2VecVol = rtk::ImageToVectorImageFilter::New(); volSeries2VecVol->SetInput(rooster->GetOutput()); TRY_AND_EXIT_ON_ITK_EXCEPTION(volSeries2VecVol->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(volSeries2VecVol->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkspectralrooster/rtkspectralrooster.ggo ================================================ purpose "Reconstructs a 3D + material vector volume from a vector projection stack, alternating between conjugate gradient optimization and regularization, including between materials" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "niter" n "Number of main loop iterations" int no default="5" option "cgiter" - "Number of conjugate gradient nested iterations" int no default="4" option "cudacg" - "Perform conjugate gradient calculations on GPU" flag off option "input" i "Input volume" string no option "projection" p "Vector projections file" string yes option "nodisplaced" - "Disable the displaced detector filter" flag off section "Regularization" option "nopositivity" - "Do not enforce positivity" flag off option "tviter" - "Total variation (spatial, temporal and nuclear) regularization: number of iterations" int no default="10" option "gamma_space" - "Total variation spatial regularization parameter. The larger, the smoother" double no option "threshold" - "Daubechies wavelets spatial regularization: soft threshold" float no option "order" - "Daubechies wavelets spatial regularization: order of the wavelets" int no default="5" option "levels" - "Daubechies wavelets spatial regularization: number of decomposition levels" int no default="3" option "gamma_time" - "Total variation temporal regularization parameter. The larger, the smoother" double no option "lambda_time" - "Temporal gradient's L0 norm regularization parameter. The larger, the stronger" double no option "l0iter" - "Temporal gradient's L0 norm regularization: number of iterations" int no default="5" option "gamma_tnv" - "Total nuclear variation regularization parameter. The larger, the smoother" double no ================================================ FILE: applications/rtkspectralsimplexdecomposition/CMakeLists.txt ================================================ WRAP_GGO(rtkspectralsimplexdecomposition_GGO_C rtkspectralsimplexdecomposition.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkspectralsimplexdecomposition rtkspectralsimplexdecomposition.cxx ${rtkspectralsimplexdecomposition_GGO_C}) target_link_libraries(rtkspectralsimplexdecomposition RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkspectralsimplexdecomposition) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkspectralsimplexdecomposition/rtkspectralsimplexdecomposition.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkspectralsimplexdecomposition_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkMacro.h" #include "rtkSimplexSpectralProjectionsDecompositionImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtkspectralsimplexdecomposition, args_info); using PixelValueType = float; constexpr unsigned int Dimension = 3; using DecomposedProjectionType = itk::VectorImage; using SpectralProjectionsType = itk::VectorImage; using IncidentSpectrumImageType = itk::Image; using DetectorResponseImageType = itk::Image; using MaterialAttenuationsImageType = itk::Image; // Read all inputs DecomposedProjectionType::Pointer decomposedProjection; TRY_AND_EXIT_ON_ITK_EXCEPTION(decomposedProjection = itk::ReadImage(args_info.input_arg)) SpectralProjectionsType::Pointer spectralProjection; TRY_AND_EXIT_ON_ITK_EXCEPTION(spectralProjection = itk::ReadImage(args_info.spectral_arg)) IncidentSpectrumImageType::Pointer incidentSpectrum; TRY_AND_EXIT_ON_ITK_EXCEPTION(incidentSpectrum = itk::ReadImage(args_info.incident_arg)) DetectorResponseImageType::Pointer detectorResponse; TRY_AND_EXIT_ON_ITK_EXCEPTION(detectorResponse = itk::ReadImage(args_info.detector_arg)) MaterialAttenuationsImageType::Pointer materialAttenuations; TRY_AND_EXIT_ON_ITK_EXCEPTION(materialAttenuations = itk::ReadImage(args_info.attenuations_arg)) // Get parameters from the images const unsigned int NumberOfMaterials = materialAttenuations->GetLargestPossibleRegion().GetSize()[0]; const unsigned int NumberOfSpectralBins = spectralProjection->GetVectorLength(); const unsigned int MaximumEnergy = incidentSpectrum->GetLargestPossibleRegion().GetSize()[0]; // Read the thresholds on command line and check their number itk::VariableLengthVector thresholds; thresholds.SetSize(NumberOfSpectralBins + 1); if (args_info.thresholds_given == NumberOfSpectralBins) { for (unsigned int i = 0; i < NumberOfSpectralBins; i++) thresholds[i] = args_info.thresholds_arg[i]; // Add the maximum pulse height at the end unsigned int MaximumPulseHeight = detectorResponse->GetLargestPossibleRegion().GetSize()[1]; thresholds[NumberOfSpectralBins] = MaximumPulseHeight; } else itkGenericExceptionMacro(<< "Number of thresholds " << args_info.thresholds_given << " does not match the number of bins " << NumberOfSpectralBins); // Check that the inputs have the expected size DecomposedProjectionType::IndexType indexDecomp; indexDecomp.Fill(0); if (decomposedProjection->GetPixel(indexDecomp).Size() != NumberOfMaterials) itkGenericExceptionMacro(<< "Decomposed projections (i.e. initialization data) image has vector size " << decomposedProjection->GetPixel(indexDecomp).Size() << ", should be " << NumberOfMaterials); SpectralProjectionsType::IndexType indexSpect; indexSpect.Fill(0); if (spectralProjection->GetPixel(indexSpect).Size() != NumberOfSpectralBins) itkGenericExceptionMacro(<< "Spectral projections (i.e. photon count data) image has vector size " << spectralProjection->GetPixel(indexSpect).Size() << ", should be " << NumberOfSpectralBins); if (detectorResponse->GetLargestPossibleRegion().GetSize()[0] != MaximumEnergy) itkGenericExceptionMacro(<< "Detector response image has " << detectorResponse->GetLargestPossibleRegion().GetSize()[0] << "energies, should have " << MaximumEnergy); // Create and set the filter auto simplex = rtk::SimplexSpectralProjectionsDecompositionImageFilter::New(); simplex->SetInputDecomposedProjections(decomposedProjection); simplex->SetGuessInitialization(args_info.guess_flag); simplex->SetInputMeasuredProjections(spectralProjection); simplex->SetInputIncidentSpectrum(incidentSpectrum); simplex->SetDetectorResponse(detectorResponse); simplex->SetMaterialAttenuations(materialAttenuations); simplex->SetThresholds(thresholds); simplex->SetNumberOfIterations(args_info.niterations_arg); simplex->SetOptimizeWithRestarts(args_info.restarts_flag); simplex->SetLogTransformEachBin(args_info.log_flag); simplex->SetIsSpectralCT(true); // Note: The simplex filter is set to perform several searches for each pixel, // with different initializations, and keep the best one (SetOptimizeWithRestart(true)). // While it may yield better results, these initializations are partially random, // which makes the output non-reproducible. // The default behavior, used for example in the tests, is not to use this feature // (SetOptimizeWithRestart(false)), which makes the output reproducible. if (args_info.weightsmap_given) simplex->SetOutputInverseCramerRaoLowerBound(true); if (args_info.fischer_given) simplex->SetOutputFischerMatrix(true); TRY_AND_EXIT_ON_ITK_EXCEPTION(simplex->Update()) // Write output TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(simplex->GetOutput(0), args_info.output_arg)) // If requested, write the weightsmap if (args_info.weightsmap_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(simplex->GetOutput(1), args_info.weightsmap_arg)) } // If requested, write the fisher information matrix if (args_info.fischer_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(simplex->GetOutput(2), args_info.fischer_arg)) } return EXIT_SUCCESS; } ================================================ FILE: applications/rtkspectralsimplexdecomposition/rtkspectralsimplexdecomposition.ggo ================================================ purpose "Decomposes spectral projections into materials" option "verbose" v "Verbose execution" flag off option "output" o "Output file name (decomposed projections)" string yes option "input" i "Decomposed projections for initialization file name" string no option "spectral" s "Spectral projections to be decomposed" string yes option "detector" d "Detector response file" string yes option "incident" - "Incident spectrum file" string yes option "attenuations" a "Material attenuations file" string yes option "niterations" n "Number of iterations" int no default="300" option "thresholds" t "Lower threshold of bins, expressed in pulse height" double yes multiple option "weightsmap" w "File name for the output weights map (inverse noise variance)" string no option "restarts" r "Allow random restarts during optimization" flag off option "fischer" f "File name for the Fischer information matrix" string no option "log" l "Log transform each bin, and concatenate the projections with the decomposed ones" flag off option "guess" g "Ignore values in input and initialize the simplex with a simple heuristic instead" flag off ================================================ FILE: applications/rtksubselect/CMakeLists.txt ================================================ WRAP_GGO(rtksubselect_GGO_C rtksubselect.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtksubselect rtksubselect.cxx ${rtksubselect_GGO_C}) target_link_libraries(rtksubselect RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtksubselect) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtksubselect/rtksubselect.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtksubselect_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkProjectionsReader.h" #include "rtkConstantImageSource.h" #include #include #include int main(int argc, char * argv[]) { GGO(rtksubselect, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Projections reader using ReaderType = rtk::ProjectionsReader; auto reader = ReaderType::New(); rtk::SetProjectionsReaderFromGgo(reader, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->Update()) // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Compute the indices of the selected projections std::vector indices; int n = geometry->GetGantryAngles().size(); if (args_info.last_given) n = std::min(args_info.last_arg, n); if (args_info.list_given) for (unsigned int i = 0; i < args_info.list_given; i++) { indices.push_back(args_info.list_arg[i]); } else for (int noProj = args_info.first_arg; noProj < n; noProj += args_info.step_arg) { indices.push_back(noProj); } // Output RTK geometry object auto outputGeometry = rtk::ThreeDCircularProjectionGeometry::New(); // Output projections object auto source = rtk::ConstantImageSource::New(); source->SetInformationFromImage(reader->GetOutput()); OutputImageType::SizeType outputSize = reader->GetOutput()->GetLargestPossibleRegion().GetSize(); outputSize[Dimension - 1] = indices.size(); source->SetSize(outputSize); source->SetConstant(0); TRY_AND_EXIT_ON_ITK_EXCEPTION(source->Update()) // Fill in the outputGeometry and the output projections auto paste = itk::PasteImageFilter::New(); paste->SetSourceImage(reader->GetOutput()); paste->SetDestinationImage(source->GetOutput()); OutputImageType::RegionType sourceRegion; OutputImageType::IndexType destinationIndex; for (unsigned int i = 0; i < indices.size(); i++) { // If it is not the first projection, we need to use the output of // the paste filter as input if (i) { OutputImageType::Pointer pimg = paste->GetOutput(); pimg->DisconnectPipeline(); paste->SetDestinationImage(pimg); } sourceRegion = reader->GetOutput()->GetLargestPossibleRegion(); sourceRegion.SetIndex(Dimension - 1, indices[i]); sourceRegion.SetSize(Dimension - 1, 1); paste->SetSourceRegion(sourceRegion); destinationIndex = reader->GetOutput()->GetLargestPossibleRegion().GetIndex(); destinationIndex[Dimension - 1] = i; paste->SetDestinationIndex(destinationIndex); TRY_AND_EXIT_ON_ITK_EXCEPTION(paste->Update()) // Fill in the output geometry object outputGeometry->SetRadiusCylindricalDetector(geometry->GetRadiusCylindricalDetector()); outputGeometry->AddProjectionInRadians(geometry->GetSourceToIsocenterDistances()[indices[i]], geometry->GetSourceToDetectorDistances()[indices[i]], geometry->GetGantryAngles()[indices[i]], geometry->GetProjectionOffsetsX()[indices[i]], geometry->GetProjectionOffsetsY()[indices[i]], geometry->GetOutOfPlaneAngles()[indices[i]], geometry->GetInPlaneAngles()[indices[i]], geometry->GetSourceOffsetsX()[indices[i]], geometry->GetSourceOffsetsY()[indices[i]]); outputGeometry->SetCollimationOfLastProjection(geometry->GetCollimationUInf()[indices[i]], geometry->GetCollimationUSup()[indices[i]], geometry->GetCollimationVInf()[indices[i]], geometry->GetCollimationVSup()[indices[i]]); } // Geometry writer if (args_info.verbose_flag) std::cout << "Writing geometry information in " << args_info.out_geometry_arg << "..." << std::endl; TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(outputGeometry, args_info.out_geometry_arg)) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(paste->GetOutput(), args_info.out_proj_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtksubselect/rtksubselect.ggo ================================================ purpose "Subselects a few projections from a list of projection files and a geometry file." option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "out_geometry" - "Output geometry file name" string yes option "out_proj" - "Output projections stack file name" string yes option "first" f "First projection index" int no default="0" option "last" l "Last projection index" int no option "step" s "Step between projections" int no default="1" option "list" - "List of projection indices to keep (0-based)" int multiple no ================================================ FILE: applications/rtktotalnuclearvariationdenoising/CMakeLists.txt ================================================ WRAP_GGO(rtktotalnuclearvariationdenoising_GGO_C rtktotalnuclearvariationdenoising.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtktotalnuclearvariationdenoising rtktotalnuclearvariationdenoising.cxx ${rtktotalnuclearvariationdenoising_GGO_C}) target_link_libraries(rtktotalnuclearvariationdenoising RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtktotalnuclearvariationdenoising) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtktotalnuclearvariationdenoising/rtktotalnuclearvariationdenoising.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtktotalnuclearvariationdenoising_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #include "rtkTotalNuclearVariationDenoisingBPDQImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtktotalnuclearvariationdenoising, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 4; // Number of dimensions of the input image constexpr unsigned int DimensionsProcessed = 3; // Number of dimensions along which the gradient is computed using OutputImageType = itk::Image; // Read input OutputImageType::Pointer input; TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) // Apply total nuclear variation denoising auto tv = rtk::TotalNuclearVariationDenoisingBPDQImageFilter< OutputImageType, itk::Image, Dimension>>::New(); tv->SetInput(input); tv->SetGamma(args_info.gamma_arg); tv->SetNumberOfIterations(args_info.niter_arg); // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(tv->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtktotalnuclearvariationdenoising/rtktotalnuclearvariationdenoising.ggo ================================================ purpose "Performs total nuclear variation denoising of a 3D + channels image." option "verbose" v "Verbose execution" flag off option "input" i "Input file name" string yes option "output" o "Output file name" string yes option "gamma" g "TV term's weighting parameter" double no default="1.0" option "niter" n "Number of iterations" int no default="5" ================================================ FILE: applications/rtktotalvariationdenoising/CMakeLists.txt ================================================ WRAP_GGO(rtktotalvariationdenoising_GGO_C rtktotalvariationdenoising.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtktotalvariationdenoising rtktotalvariationdenoising.cxx ${rtktotalvariationdenoising_GGO_C}) target_link_libraries(rtktotalvariationdenoising RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtktotalvariationdenoising) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtktotalvariationdenoising/rtktotalvariationdenoising.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtktotalvariationdenoising_ggo.h" #include "rtkGgoFunctions.h" #include "rtkConfiguration.h" #ifdef RTK_USE_CUDA # include "rtkCudaTotalVariationDenoisingBPDQImageFilter.h" #else # include "rtkTotalVariationDenoisingBPDQImageFilter.h" #endif #include "rtkTotalVariationImageFilter.h" #include #include int main(int argc, char * argv[]) { GGO(rtktotalvariationdenoising, args_info); using OutputPixelType = float; constexpr unsigned int Dimension = 3; // Number of dimensions of the input image #ifdef RTK_USE_CUDA using OutputImageType = itk::CudaImage; using TVDenoisingFilterType = rtk::CudaTotalVariationDenoisingBPDQImageFilter; #else using OutputImageType = itk::Image; using TVDenoisingFilterType = rtk::TotalVariationDenoisingBPDQImageFilter< OutputImageType, itk::Image, Dimension>>; #endif // Read input OutputImageType::Pointer input; TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) // Compute total variation before denoising auto tv = rtk::TotalVariationImageFilter::New(); tv->SetInput(input); if (args_info.verbose_flag) { tv->Update(); std::cout << "TV before denoising = " << tv->GetTotalVariation() << std::endl; } // Apply total variation denoising auto tvdenoising = TVDenoisingFilterType::New(); tvdenoising->SetInput(input); tvdenoising->SetGamma(args_info.gamma_arg); tvdenoising->SetNumberOfIterations(args_info.niter_arg); // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(tvdenoising->GetOutput(), args_info.output_arg)) // Compute total variation after denoising if (args_info.verbose_flag) { tv->SetInput(tvdenoising->GetOutput()); tv->Update(); std::cout << "TV after denoising = " << tv->GetTotalVariation() << std::endl; } return EXIT_SUCCESS; } ================================================ FILE: applications/rtktotalvariationdenoising/rtktotalvariationdenoising.ggo ================================================ purpose "Performs total variation denoising along the specified dimensions of a 3D image." option "verbose" v "Verbose execution" flag off option "input" i "Input file name" string yes option "output" o "Output file name" string yes option "gamma" g "TV term's weighting parameter" double no default="1.0" option "niter" n "Number of iterations" int no default="5" ================================================ FILE: applications/rtktutorialapplication/CMakeLists.txt ================================================ WRAP_GGO(rtktutorialapplication_GGO_C rtktutorialapplication.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtktutorialapplication rtktutorialapplication.cxx ${rtktutorialapplication_GGO_C}) target_link_libraries(rtktutorialapplication RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtktutorialapplication) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtktutorialapplication/rtktutorialapplication.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtktutorialapplication_ggo.h" #include "rtkGgoFunctions.h" #include #include #include int main(int argc, char * argv[]) { GGO(rtktutorialapplication, args_info); // This small application can be used by RTK beginners // as a starting point. It reads a volume and a scalar, // adds the scalar to all voxels of the volume, and // writes the result // Below is a list of a few possible challenges for beginners: // Copy/paste the folder of this application to a new one, named // "rtkMyApp", and modify the following files // -> applications/CMakeLists.txt // -> applications/rtkMyApp/rtkMyApp.cxx // -> applications/rtkMyApp/rtkMyApp.ggo // -> applications/rtkMyApp/CMakeLists.txt // so that rtkMyApp compiles and runs fine. // Modify the following files // -> applications/rtkMyApp/rtkMyApp.cxx // -> applications/rtkMyApp/rtkMyApp.ggo // so that rtkMyApp takes two volumes in input, adds them, // and writes the result in output // Modify the following files // -> applications/rtkMyApp/rtkMyApp.cxx // -> applications/rtkMyApp/rtkMyApp.ggo // so that rtkMyApp takes a volume in input, adds a scalar to // all voxels, multiplies the output by an other scalar, and // writes the result in output // !! HARDER !! // Modify the following files // -> applications/rtkMyApp/rtkMyApp.cxx // so that rtkMyApp computes the n-th element of the // Collatz sequence of each voxel, using only one // itk::AddImageFilter, one itk::MultiplyImageFilter, // and one itk::DivideImageFilter // https://en.wikipedia.org/wiki/Collatz_conjecture // // You will need to use the DisconnectPipeline() // function. You can see how it is used in // rtkSARTConeBeamReconstructionFilter.hxx constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Read the input volume OutputImageType::Pointer input; TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) // Create the Add filter auto add = itk::AddImageFilter::New(); add->SetInput1(input); add->SetConstant2(args_info.constant_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(add->Update()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(add->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtktutorialapplication/rtktutorialapplication.ggo ================================================ purpose "A simple application you can use to train yourself to writing rtk programs" option "verbose" v "Verbose execution" flag off option "output" o "Output file name" string yes option "input" i "Input volume" string yes option "constant" c "Scalar added to input" float no default="1" ================================================ FILE: applications/rtkvarianobigeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkvarianobigeometry_GGO_C rtkvarianobigeometry.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkvarianobigeometry rtkvarianobigeometry.cxx ${rtkvarianobigeometry_GGO_C}) target_link_libraries(rtkvarianobigeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkvarianobigeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkvarianobigeometry/README.md ================================================ # Varian Reconstruction `````{tab-set} ````{tab-item} OBI ## Varian OBI Reconstruction The first step before proceeding with reconstruction is to convert Varian's geometry into RTK's format using a command-line tool. Follow these simple steps: ### 1. Download Varian Dataset Download the dataset from [Varian-data](https://data.kitware.com/api/v1/item/5be94de88d777f2179a24de0/download). ### 2. Convert Geometry Run the application to convert Varian's geometry into RTK's format: ```bash rtkvarianobigeometry \ --xml_file ProjectionInfo.xml \ --path Scan0/ \ --regexp Proj_.*.hnd \ -o geometry.xml ``` ### 3. Reconstruct Using RTK Applications Reconstruct a slice (e.g., slice 30) of the volume using the `rtkfdk` algorithm: ```bash rtkfdk \ --geometry geometry.xml \ --regexp .*\.hnd \ --path Scan0 \ --output slice30.mha \ --verbose \ --spacing 0.25,0.25,0.25 \ --size 1024,1,1024 \ --origin -127.875,30,-127.875 ``` ### 4. Apply the FOV Filter Apply the field-of-view (FOV) filter to discard everything outside the FOV: ```bash rtkfieldofview \ --geometry geometry.xml \ --regexp .*\.hnd \ --path Scan0 \ --reconstruction slice30.mha \ --output slice30.mha \ --verbose ``` ### 5. Visualize the Result You can visualize the result using a viewer (e.g., VV). The resulting image should look like this: ![../../documentation/docs/ExternalData/Varian](../../documentation/docs/ExternalData/Varian.png){w=400px alt="Varian snapshot"} ```` ````{tab-item} ProBeam ## Varian ProBeam Reconstruction Follow these steps for the Varian ProBeam format: ### 1. Download Dataset Download the dataset from [Varian-ProBeam-data](https://data.kitware.com/api/v1/item/5be94bef8d777f2179a24ae1/download). ### 2. Convert Geometry Run the application to convert Varian ProBeam's geometry into RTK's format: ```bash rtkvarianprobeamgeometry \ --xml_file Scan.xml \ --path Acquisitions/733061622 \ --regexp Proj_.*.xim \ -o geometry.xml ``` ### 3. Reconstruct Using RTK Applications Reconstruct a slice (e.g., slice 58) of the volume using the `rtkfdk` algorithm: ```bash rtkfdk \ --geometry geometry.xml \ --regexp .*\.xim \ --path Acquisitions/733061622 \ --output slice58.mha \ --verbose \ --spacing 0.25,0.25,0.25 \ --size 1024,1,1024 \ --origin -127.875,-58,-127.875 ``` ### 4. Apply the FOV Filter Apply the field-of-view (FOV) filter to discard everything outside the FOV: ```bash rtkfieldofview \ --geometry geometry.xml \ --regexp .*\.xim \ --path Acquisitions/733061622 \ --reconstruction slice58.mha \ --output slice58.mha \ --verbose ``` ### 5. Visualize the Result You can visualize the result using a viewer (e.g., VV). The resulting image should look like this: ![../../documentation/docs/ExternalData/VarianProBeam](../../documentation/docs/ExternalData/VarianProBeam.png){w=400px alt="VarianProBeam snapshot"} ````` ```` ================================================ FILE: applications/rtkvarianobigeometry/rtkvarianobigeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkvarianobigeometry_ggo.h" #include "rtkMacro.h" #include "rtkVarianObiGeometryReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkGgoFunctions.h" #include int main(int argc, char * argv[]) { GGO(rtkvarianobigeometry, args_info); // Create geometry reader rtk::VarianObiGeometryReader::Pointer reader; reader = rtk::VarianObiGeometryReader::New(); reader->SetXMLFileName(args_info.xml_file_arg); reader->SetProjectionsFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(reader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkvarianobigeometry/rtkvarianobigeometry.ggo ================================================ purpose "Creates an RTK geometry file from a Varian OBI acquisition." option "verbose" v "Verbose execution" flag off option "xml_file" x "Varian OBI XML information file on projections" string yes option "output" o "Output file name" string yes ================================================ FILE: applications/rtkvarianobigeometry/rtkvarianobigeometry.py ================================================ #!/usr/bin/env python import argparse import itk from itk import RTK as rtk def build_parser(): # Argument parsing parser = rtk.RTKArgumentParser( description="Creates an RTK geometry file from a Varian OBI acquisition." ) parser.add_argument("--verbose", "-v", help="Verbose execution", type=bool) parser.add_argument( "--xml_file", "-x", help="Varian OBI XML information file on projections", required=True, ) parser.add_argument("--output", "-o", help="Output file name", required=True) parser.add_argument( "--path", "-p", help="Path containing projections", required=True ) parser.add_argument( "--regexp", "-r", help="Regular expression to select projection files in path", required=True, ) # Parse the command line arguments return parser def process(args: argparse.Namespace): names = itk.RegularExpressionSeriesFileNames.New() names.SetDirectory(args.path) names.SetRegularExpression(args.regexp) reader = rtk.VarianObiGeometryReader.New() reader.SetXMLFileName(args.xml_file) reader.SetProjectionsFileNames(names.GetFileNames()) reader.UpdateOutputData() rtk.write_geometry(reader.GetGeometry(), args.output) def main(argv=None): parser = build_parser() args_info = parser.parse_args(argv) process(args_info) if __name__ == "__main__": main() ================================================ FILE: applications/rtkvarianprobeamgeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkvarianprobeamgeometry_GGO_C rtkvarianprobeamgeometry.ggo ../rtkinputprojections_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkvarianprobeamgeometry rtkvarianprobeamgeometry.cxx ${rtkvarianprobeamgeometry_GGO_C}) target_link_libraries(rtkvarianprobeamgeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkvarianprobeamgeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkvarianprobeamgeometry/rtkvarianprobeamgeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkvarianprobeamgeometry_ggo.h" #include "rtkMacro.h" #include "rtkVarianProBeamGeometryReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkGgoFunctions.h" int main(int argc, char * argv[]) { GGO(rtkvarianprobeamgeometry, args_info); // Create geometry reader rtk::VarianProBeamGeometryReader::Pointer reader; reader = rtk::VarianProBeamGeometryReader::New(); reader->SetXMLFileName(args_info.xml_file_arg); reader->SetProjectionsFileNames(rtk::GetProjectionsFileNamesFromGgo(args_info)); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(reader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkvarianprobeamgeometry/rtkvarianprobeamgeometry.ggo ================================================ purpose "Creates an RTK geometry file from a Varian ProBeam acquisition." option "verbose" v "Verbose execution" flag off option "xml_file" x "Varian ProBeam XML information file on projections" string yes option "output" o "Output file name" string yes ================================================ FILE: applications/rtkvectorconjugategradient/CMakeLists.txt ================================================ WRAP_GGO(rtkvectorconjugategradient_GGO_C rtkvectorconjugategradient.ggo ../rtk3Doutputimage_section.ggo ../rtkprojectors_section.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkvectorconjugategradient rtkvectorconjugategradient.cxx ${rtkvectorconjugategradient_GGO_C}) target_link_libraries(rtkvectorconjugategradient RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkvectorconjugategradient) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkvectorconjugategradient/rtkvectorconjugategradient.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkvectorconjugategradient_ggo.h" #include "rtkGgoFunctions.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" #include "rtkConjugateGradientConeBeamReconstructionFilter.h" #include #include #include #ifdef RTK_USE_CUDA # include #endif #include int main(int argc, char * argv[]) { GGO(rtkvectorconjugategradient, args_info); constexpr unsigned int Dimension = 3; constexpr unsigned int nMaterials = 3; using DataType = float; using PixelType = itk::Vector; using WeightsType = itk::Vector; #ifdef RTK_USE_CUDA using SingleComponentImageType = itk::CudaImage; using OutputImageType = itk::CudaImage; using WeightsImageType = itk::CudaImage; #else using SingleComponentImageType = itk::Image; using OutputImageType = itk::Image; using WeightsImageType = itk::Image; #endif // Projections reader OutputImageType::Pointer projections; TRY_AND_EXIT_ON_ITK_EXCEPTION(projections = itk::ReadImage(args_info.projections_arg)) // Geometry if (args_info.verbose_flag) std::cout << "Reading geometry information from " << args_info.geometry_arg << "..." << std::endl; rtk::ThreeDCircularProjectionGeometry::Pointer geometry; TRY_AND_EXIT_ON_ITK_EXCEPTION(geometry = rtk::ReadGeometry(args_info.geometry_arg)); // Create input: either an existing volume read from a file or a blank image OutputImageType::Pointer input; if (args_info.input_given) { // Read an existing image to initialize the volume TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) } else { // Create new empty volume using ConstantImageSourceType = rtk::ConstantImageSource; auto constantImageSource = ConstantImageSourceType::New(); rtk::SetConstantImageSourceFromGgo( constantImageSource, args_info); TRY_AND_EXIT_ON_ITK_EXCEPTION(constantImageSource->Update()) input = constantImageSource->GetOutput(); } // Read weights if given WeightsImageType::Pointer inputWeights; if (args_info.weights_given) { using WeightsReaderType = itk::ImageFileReader; auto weightsReader = WeightsReaderType::New(); weightsReader->SetFileName(args_info.weights_arg); inputWeights = weightsReader->GetOutput(); TRY_AND_EXIT_ON_ITK_EXCEPTION(inputWeights->Update()) } // Read regularization weights if given SingleComponentImageType::Pointer localRegWeights; if (args_info.regweights_given) { using WeightsReaderType = itk::ImageFileReader; auto localRegWeightsReader = WeightsReaderType::New(); localRegWeightsReader->SetFileName(args_info.regweights_arg); localRegWeights = localRegWeightsReader->GetOutput(); localRegWeights->Update(); } // Read Support Mask if given SingleComponentImageType::Pointer supportmask; if (args_info.mask_given) { TRY_AND_EXIT_ON_ITK_EXCEPTION(supportmask = itk::ReadImage(args_info.mask_arg)) } // Set the forward and back projection filters to be used auto conjugategradient = rtk:: ConjugateGradientConeBeamReconstructionFilter::New(); SetForwardProjectionFromGgo(args_info, conjugategradient.GetPointer()); SetBackProjectionFromGgo(args_info, conjugategradient.GetPointer()); conjugategradient->SetInputVolume(input); conjugategradient->SetInputProjectionStack(projections); conjugategradient->SetInputWeights(inputWeights); conjugategradient->SetLocalRegularizationWeights(localRegWeights); conjugategradient->SetCudaConjugateGradient(!args_info.nocudacg_flag); if (args_info.mask_given) { conjugategradient->SetSupportMask(supportmask); } if (args_info.gamma_given) conjugategradient->SetGamma(args_info.gamma_arg); if (args_info.tikhonov_given) conjugategradient->SetTikhonov(args_info.tikhonov_arg); conjugategradient->SetGeometry(geometry); conjugategradient->SetNumberOfIterations(args_info.niterations_arg); conjugategradient->SetDisableDisplacedDetectorFilter(args_info.nodisplaced_flag); itk::TimeProbe readerProbe; if (args_info.time_flag) { std::cout << "Recording elapsed time... " << std::flush; readerProbe.Start(); } TRY_AND_EXIT_ON_ITK_EXCEPTION(conjugategradient->Update()) if (args_info.time_flag) { // conjugategradient->PrintTiming(std::cout); readerProbe.Stop(); std::cout << "It took... " << readerProbe.GetMean() << ' ' << readerProbe.GetUnit() << std::endl; } // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(conjugategradient->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkvectorconjugategradient/rtkvectorconjugategradient.ggo ================================================ purpose "Reconstructs a 3D volume from a sequence of projections with a conjugate gradient technique" option "verbose" v "Verbose execution" flag off option "geometry" g "XML geometry file name" string yes option "output" o "Output file name" string yes option "projections" - "Projections file name" string yes option "niterations" n "Number of iterations" int no default="5" option "time" t "Records elapsed time during the process" flag off option "input" i "Input volume" string no option "weights" w "Weights file for Weighted Least Squares (WLS)" string no option "regweights" - "Local regularization weights file" string no option "gamma" - "Laplacian regularization weight" float no default="0" option "tikhonov" - "Tikhonov regularization weight" float no default="0" option "nocudacg" - "Do not perform conjugate gradient calculations on GPU" flag off option "mask" m "Apply a support binary mask: reconstruction kept null outside the mask)" string no option "nodisplaced" - "Disable the displaced detector filter" flag off option "targetSDD" - "Target sum of squared difference between consecutive iterates, as stopping criterion" double no default="0" ================================================ FILE: applications/rtkversion.py.in ================================================ __all__ = [ "version" ] RTK_VERSION_MAJOR = "@RTK_VERSION_MAJOR@" RTK_VERSION_MINOR = "@RTK_VERSION_MINOR@" RTK_VERSION_PATCH = "@RTK_VERSION_PATCH@" RTK_VERSION_HASH = "@RTK_VERSION_HASH@" def version(): return f"RTK {RTK_VERSION_MAJOR}.{RTK_VERSION_MINOR}.{RTK_VERSION_PATCH}{RTK_VERSION_HASH}" ================================================ FILE: applications/rtkwaveletsdenoising/CMakeLists.txt ================================================ WRAP_GGO(rtkwaveletsdenoising_GGO_C rtkwaveletsdenoising.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkwaveletsdenoising rtkwaveletsdenoising.cxx ${rtkwaveletsdenoising_GGO_C}) target_link_libraries(rtkwaveletsdenoising RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkwaveletsdenoising) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkwaveletsdenoising/rtkwaveletsdenoising.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkwaveletsdenoising_ggo.h" #include "rtkGgoFunctions.h" #include "rtkDeconstructSoftThresholdReconstructImageFilter.h" #include #include #include #include int main(int argc, char * argv[]) { GGO(rtkwaveletsdenoising, args_info); constexpr unsigned int Dimension = 3; using OutputImageType = itk::Image; // Read the input image OutputImageType::Pointer input; TRY_AND_EXIT_ON_ITK_EXCEPTION(input = itk::ReadImage(args_info.input_arg)) // Create the denoising filter auto wst = rtk::DeconstructSoftThresholdReconstructImageFilter::New(); wst->SetInput(input); wst->SetOrder(args_info.order_arg); wst->SetThreshold(args_info.threshold_arg); wst->SetNumberOfLevels(args_info.level_arg); // Write reconstruction TRY_AND_EXIT_ON_ITK_EXCEPTION(itk::WriteImage(wst->GetOutput(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkwaveletsdenoising/rtkwaveletsdenoising.ggo ================================================ purpose "Denoises a volume using Daubechies wavelets soft thresholding" option "verbose" v "Verbose execution" flag off option "input" i "Input file name" string yes option "output" o "Input file name" string yes option "order" - "Order of the Daubechies wavelets" int yes option "level" l "Number of deconstruction levels" int yes option "threshold" t "Threshold used in soft thresholding of the wavelets coefficients" float yes ================================================ FILE: applications/rtkxradgeometry/CMakeLists.txt ================================================ WRAP_GGO(rtkxradgeometry_GGO_C rtkxradgeometry.ggo ${RTK_BINARY_DIR}/rtkVersion.ggo) add_executable(rtkxradgeometry rtkxradgeometry.cxx ${rtkxradgeometry_GGO_C}) target_link_libraries(rtkxradgeometry RTK) # Installation code if(NOT RTK_INSTALL_NO_EXECUTABLES) foreach(EXE_NAME rtkxradgeometry) install(TARGETS ${EXE_NAME} RUNTIME DESTINATION ${RTK_INSTALL_RUNTIME_DIR} COMPONENT Runtime LIBRARY DESTINATION ${RTK_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries ARCHIVE DESTINATION ${RTK_INSTALL_ARCHIVE_DIR} COMPONENT Development) endforeach() endif() ================================================ FILE: applications/rtkxradgeometry/rtkxradgeometry.cxx ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #include "rtkxradgeometry_ggo.h" #include "rtkGgoFunctions.h" #include "rtkXRadGeometryReader.h" #include "rtkThreeDCircularProjectionGeometryXMLFile.h" int main(int argc, char * argv[]) { GGO(rtkxradgeometry, args_info); // Create geometry reader auto reader = rtk::XRadGeometryReader::New(); reader->SetImageFileName(args_info.input_arg); TRY_AND_EXIT_ON_ITK_EXCEPTION(reader->UpdateOutputData()) // Write TRY_AND_EXIT_ON_ITK_EXCEPTION(rtk::WriteGeometry(reader->GetGeometry(), args_info.output_arg)) return EXIT_SUCCESS; } ================================================ FILE: applications/rtkxradgeometry/rtkxradgeometry.ggo ================================================ purpose "Creates an RTK geometry file from an acquisition exported on the XRad system." option "verbose" v "Verbose execution" flag off option "input" i "Input sinogram header file" string yes option "output" o "Output file name" string yes ================================================ FILE: cmake/FindGengetopt.cmake ================================================ # Attempt to find gengetopt. If not found, compile it. if (NOT TARGET gengetopt) find_program(GENGETOPT gengetopt) if ((GENGETOPT STREQUAL "GENGETOPT-NOTFOUND") OR (GENGETOPT STREQUAL "")) get_filename_component(CLITK_CMAKE_DIR ${CMAKE_CURRENT_LIST_FILE} PATH) add_subdirectory(${CLITK_CMAKE_DIR}/../utilities/gengetopt ${CMAKE_CURRENT_BINARY_DIR}/gengetopt) else() if(EXISTS ${GENGETOPT}) add_executable(gengetopt IMPORTED) set_property(TARGET gengetopt PROPERTY IMPORTED_LOCATION ${GENGETOPT}) else() set(GENGETOPT "GENGETOPT-NOTFOUND" CACHE FILEPATH "Path to a program." FORCE) message(FATAL_ERROR "No gengetopt executable found at the specified location") endif() endif() endif() # Create a cmake script to cat a list of files file(WRITE "${CMAKE_BINARY_DIR}/cat.cmake" " file(WRITE \"\${OUTPUT}\" \"\") foreach(INPUT \${INPUTS}) string(REPLACE \";\" \" \" INPUT \"\${INPUT}\") file(READ \"\${INPUT}\" CONTENT) file(APPEND \"\${OUTPUT}\" \"\${CONTENT}\") endforeach() ") macro (WRAP_GGO GGO_SRCS) # Set current list of files to zero for a new target set(GGO_FILES_ABS "") # Convert list of a file in a list with absolute file names foreach(GGO_FILE ${ARGN}) get_filename_component(GGO_FILE_ABS ${GGO_FILE} ABSOLUTE) list(APPEND GGO_FILES_ABS "${GGO_FILE_ABS}") endforeach() # Append to a new ggo file containing all files list(GET GGO_FILES_ABS 0 FIRST_GGO_FILE) get_filename_component(FIRST_GGO_BASEFILENAME ${FIRST_GGO_FILE} NAME) separate_arguments(GGO_FILES_ABS_LIST NATIVE_COMMAND "${GGO_FILES_ABS}") add_custom_command( OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${FIRST_GGO_BASEFILENAME}" COMMAND ${CMAKE_COMMAND} -D INPUTS="${GGO_FILES_ABS_LIST}" -D OUTPUT=${CMAKE_CURRENT_BINARY_DIR}/${FIRST_GGO_BASEFILENAME} -P "${CMAKE_BINARY_DIR}/cat.cmake" DEPENDS ${GGO_FILES_ABS} ) set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${FIRST_GGO_BASEFILENAME} PROPERTIES GENERATED TRUE) # Now add ggo command get_filename_component(GGO_BASEFILENAME ${FIRST_GGO_FILE} NAME_WE) set(GGO_H ${GGO_BASEFILENAME}_ggo.h) set(GGO_C ${GGO_BASEFILENAME}_ggo.c) set(GGO_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${GGO_H} ${CMAKE_CURRENT_BINARY_DIR}/${GGO_C}) add_custom_command(OUTPUT ${GGO_OUTPUT} COMMAND gengetopt ARGS --input=${CMAKE_CURRENT_BINARY_DIR}/${FIRST_GGO_BASEFILENAME} --output-dir=${CMAKE_CURRENT_BINARY_DIR} --arg-struct-name=args_info_${GGO_BASEFILENAME} --func-name=cmdline_parser_${GGO_BASEFILENAME} --file-name=${GGO_BASEFILENAME}_ggo --unamed-opts --conf-parser --include-getopt --set-package ${CMAKE_PROJECT_NAME} DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FIRST_GGO_BASEFILENAME} ) set(${GGO_SRCS} ${${GGO_SRCS}} ${GGO_OUTPUT}) include_directories("${CMAKE_CURRENT_BINARY_DIR}") set_source_files_properties(${${GGO_SRCS}} PROPERTIES GENERATED TRUE) if(CMAKE_COMPILER_IS_GNUCXX) set_source_files_properties(${${GGO_SRCS}} PROPERTIES COMPILE_FLAGS "-Wno-unused-but-set-variable") endif() if(MSVC) # Disable double to float truncation warning as gengetopt cannot append "f" # to force default numeric float values in the .ggo config file set_source_files_properties(${${GGO_SRCS}} PROPERTIES COMPILE_FLAGS "/wd4305") endif() endmacro () ================================================ FILE: cmake/GetGitRevisionDescription.cmake ================================================ # - Returns a version string from Git # # These functions force a re-configure on each git commit so that you can # trust the values of the variables in your build system. # # get_git_head_revision( [ ...]) # # Returns the refspec and sha hash of the current head revision # # git_describe( [ ...]) # # Returns the results of git describe on the source tree, and adjusting # the output so that it tests false if an error occurs. # # git_get_exact_tag( [ ...]) # # Returns the results of git describe --exact-match on the source tree, # and adjusting the output so that it tests false if there was no exact # matching tag. # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # https://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) return() endif() set(__get_git_revision_description YES) # We must run the following at "include" time, not at function call time, # to find the path to this module rather than the path to a calling list file get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") set(GIT_DIR "${GIT_PARENT_DIR}/.git") while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) # We have reached the root directory, we are not in git set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) return() endif() set(GIT_DIR "${GIT_PARENT_DIR}/.git") endwhile() # check if this is a submodule if(NOT IS_DIRECTORY ${GIT_DIR}) file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) endif() set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") if(NOT EXISTS "${GIT_DATA}") file(MAKE_DIRECTORY "${GIT_DATA}") endif() if(NOT EXISTS "${GIT_DIR}/HEAD") return() endif() set(HEAD_FILE "${GIT_DATA}/HEAD") configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" "${GIT_DATA}/grabRef.cmake" @ONLY) include("${GIT_DATA}/grabRef.cmake") set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) endfunction() function(git_describe _var) if(NOT GIT_FOUND) find_package(Git QUIET) endif() get_git_head_revision(refspec hash) if(NOT GIT_FOUND) set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) return() endif() if(NOT hash) set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) return() endif() # TODO sanitize #if((${ARGN}" MATCHES "&&") OR # (ARGN MATCHES "||") OR # (ARGN MATCHES "\\;")) # message("Please report the following error to the project!") # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") #endif() #message(STATUS "Arguments to execute_process: ${ARGN}") execute_process(COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT res EQUAL 0) set(out "${out}-${res}-NOTFOUND") endif() set(${_var} "${out}" PARENT_SCOPE) endfunction() function(git_get_exact_tag _var) git_describe(out --exact-match ${ARGN}) set(${_var} "${out}" PARENT_SCOPE) endfunction() ================================================ FILE: cmake/GetGitRevisionDescription.cmake.in ================================================ # # Internal file for GetGitRevisionDescription.cmake # # Requires CMake 2.6 or newer (uses the 'function' command) # # Original Author: # 2009-2010 Ryan Pavlik # http://academic.cleardefinition.com # Iowa State University HCI Graduate Program/VRAC # # Copyright Iowa State University 2009-2010. # Distributed under the Boost Software License, Version 1.0. # (See accompanying file LICENSE_1_0.txt or copy at # https://www.boost.org/LICENSE_1_0.txt) set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) set(HEAD_HASH "${HEAD_REF}") endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() ================================================ FILE: cmake/Hooks/pre-commit ================================================ #!/bin/sh echo 'Your work tree has not been configured for RTK development. Paste the following commands into a shell: ./utilities/SetupForDevelopment.sh' exit 1 ================================================ FILE: cmake/Hooks/pre-commit-style.bash ================================================ #============================================================================= # Copyright 2010-2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= # Run clangformat and KWStyle pre-commit hooks. # # 'git config' is used to enable the hooks and set their configuration files. # The repository .gitattributes must also enable the hooks on the targeted # files. die() { echo 'pre-commit hook failure' 1>&2 echo '-----------------------' 1>&2 echo '' 1>&2 echo "$@" 1>&2 exit 1 } do_KWStyle=$(git config --bool hooks.KWStyle) || do_KWStyle=true do_clangformat=$(git config --bool hooks.clangformat) || do_clangformat=true #----------------------------------------------------------------------------- # Check if we want to run the style on a given file. Uses git attributes. If # the hook.style attribute is set, then all styles are executed. If the # hook.style attribute is set to a value, only the values given are executed. # Also, do not run the style check if there are unstaged changes in the file. # The first positional parameter is the file to check. # The second positional parameter is the style to check. # Returns 0 for execute, 1 for don't execute. run_style_on_file() { # Do not run on submodule changes. if git diff-index --cached HEAD -- "$1" | grep -q '^:...... 160000'; then return 1 fi style=$(git check-attr hooks.style -- "$1" | sed 's/^[^:]*: hooks.style: //') has_style_attr=1 case "$style" in 'unset') has_style_attr=1 ;; 'set') has_style_attr=0 ;; 'unspecified') has_style_attr=1 ;; *) echo ",$style," | grep -iq ",$2," && has_style_attr=0 ;; esac if ! git diff-files --quiet -- "$1" && test $has_style_attr -eq 0; then # A way to always allow skipping. skip_unstaged=$(git config --bool hooks.styleSkipUnstaged) || skip_unstaged=false file_sha=$(git diff-index --cached --abbrev=7 HEAD -- "$1" | \ awk '{print substr($3,1,9) substr($4,1,7)}') if file_skip_unstaged=$(git config "hooks.$1.styleSkipUnstaged"); then if test ",$file_skip_unstaged," = ",$file_sha," -o \ ",$file_skip_unstaged," = ",true,"; then skip_unstaged=true fi fi if $skip_unstaged; then echo "The file '$1' contains unstaged stages. Skipping style \ check '$2'." else die "Style check '$2' cannot run on '$1' with unstaged stages. Allow skipping the style check for this commit with git config \"hooks.$1.styleSkipUnstaged\" $file_sha" fi return 1 fi return $has_style_attr } #----------------------------------------------------------------------------- # KWStyle. check_for_KWStyle() { KWStyle_path=$(git config hooks.KWStyle.path) || KWStyle_path=$(which KWStyle) if [ $? != 0 ] ; then echo "KWStyle executable was not found. No style verification will be performed with KWStyle! A KWStyle executable will be built and configured when ITK is built with the BUILD_TESTING CMake configuration option enabled. Alternatively, set the KWStyle executable location with git config hooks.KWStyle.path /path/to/KWStyle See https://kitware.github.io/KWStyle/ " >&2 return 1 fi KWStyle_conf=$(git config hooks.KWStyle.conf) if ! test -f "$KWStyle_conf"; then die "The file '$KWStyle_conf' does not exist. Please run git config hooks.KWStyle.conf path/to/KWStyle.conf.xml" fi KWStyle_overWriteRulesConf=$(git config hooks.KWStyle.overwriteRulesConf) if test $? -eq 0 && ! test -f "$KWStyle_overWriteRulesConf"; then die "The hooks.KWStyle.overwriteRulesConf file '$KWStyle_overWriteRulesConf' does not exist." fi } run_KWStyle_on_file() { local_KWStyle_overWriteRulesConf="`pwd`/${1%/*}/../ITKKWStyleOverwrite.txt" if test -f "$local_KWStyle_overWriteRulesConf"; then "$KWStyle_path" -gcc -xml "$KWStyle_conf" -o "$local_KWStyle_overWriteRulesConf" "$1" elif test -z "$KWStyle_overWriteRulesConf"; then "$KWStyle_path" -gcc -xml "$KWStyle_conf" "$1" else echo "$KWStyle_overWriteRulesConf" "$KWStyle_path" -gcc -xml "$KWStyle_conf" -o "$KWStyle_overWriteRulesConf" "$1" fi if test $? -ne 0; then cp -- "$1"{,.kws} die "KWStyle check failed. Line numbers in the errors shown refer to the file: ${1}.kws" fi return 0 } run_KWStyle() { git diff-index --cached --diff-filter=ACMR --name-only HEAD -- | while read f; do if run_style_on_file "$f" KWStyle; then run_KWStyle_on_file "$f" fi || return done } #----------------------------------------------------------------------------- # clangformat. check_for_clangformat() { clangformat_required_version=19.1 system_tools=" clang-format-$clangformat_required_version clang-format " for tool in $system_tools; do if type -p "$tool" >/dev/null; then system_clang_format="$tool" break fi done clangformat_path=$(git config clangFormat.binary) || clangformat_path=$(type -p "$system_clang_format" >/dev/null) || die "clang-format executable was not found. A clang-format binary will be downloaded and configured when ITK is built with the BUILD_TESTING CMake configuration option enabled. Alternatively, install clang-format version $clangformat_required_version or set the executable location with git config clangFormat.binary /path/to/clang-format " if ! "$clangformat_path" --version | grep "clang-format version $clangformat_required_version" >/dev/null 2>/dev/null; then die "clang-format version $clangformat_required_version is required Set the path the clang-format $clangformat_required_version executable with git config clangFormat.binary /path/to/clang-format or disable the clang-format pre-commit hook with git config hooks.clangformat false " fi } run_clang_format_check_attr() { IN=$1 OUT=$2 ERR=$3 "${clangformat_path}" -style=file "$IN" > "$OUT" 2> "$ERR" return $? } run_clangformat_on_file() { MERGED="$1" if run_style_on_file "$MERGED" "clangformat"; then ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')" BACKUP="./$MERGED.BACKUP.$ext" LOCAL="./$MERGED.STAGED.$ext" REMOTE="./$MERGED.CLANGFORMAT.$ext" NEW_MERGED="./$MERGED.NEW.$ext" ERROR_LOG="./$MERGED.$ext.log" OLD_MERGED="$MERGED" mv -- "$MERGED" "$BACKUP" # We temporarily change MERGED because the file might already be open, and # the text editor may complain. MERGED="$NEW_MERGED" cp -- "$BACKUP" "$LOCAL" run_clang_format_check_attr "$LOCAL" "$REMOTE" "$ERROR_LOG" clang_format_status=$? if [ $clang_format_status -ne 0 ]; then mv -- "$BACKUP" "$OLD_MERGED" if $merge_keep_temporaries; then rm -f -- "$LOCAL" "$REMOTE" "$BACKUP" fi die "error when running clang-format on $OLD_MERGED" fi cp -- "$REMOTE" "$MERGED" if test $(git hash-object -- "$LOCAL") != $(git hash-object -- "$REMOTE"); then if [ "$(uname)" == "Darwin" ]; then run_merge_tool "$merge_tool" "false" For more information, see git help mergetool" merge_keep_backup="$(git config --bool mergetool.keepBackup || echo true)" merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)" git diff-index --cached --diff-filter=ACMR --name-only HEAD -- | while read MERGED; do run_clangformat_on_file "$MERGED" || return done # end for changed files } # Do not run during merge commits for now. if test -f "$GIT_DIR/MERGE_HEAD"; then : elif $do_clangformat; then # We use git-mergetool settings to review the clangformat changes. TOOL_MODE=merge . "$(git --exec-path)/git-mergetool--lib" # Redefine check_unchanged because we do not need to check if the merge was # successful. check_unchanged() { status=0 } check_for_clangformat run_clangformat || exit 1 # do_clangformat will run KWStyle on the files incrementally so excessive # clangformat merges do not have to occur. elif $do_KWStyle; then if check_for_KWStyle; then run_KWStyle || exit 1 fi fi # vim: set fenc=utf-8 ff=unix sw=8 tw=0 : ================================================ FILE: cmake/KWStyle/RTK.kws.xml ================================================ 200 0,1,2 [A-Z] m_[A-Z] 0 1 1 3 /**, *, */,true rtk [NameOfClass],rtk [NameOfClass]_[Extension] 2 1,1
cmake/KWStyle/RTKHeader.h,false,true
================================================ FILE: cmake/KWStyle/RTKHeader.h ================================================ /*========================================================================= * * Copyright RTK Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ ================================================ FILE: cmake/KWStyle/RTKOverwrite.txt ================================================ test/.*\.cxx Namespace Disable test/.*\.cxx Header Disable examples/.*\.cxx Namespace Disable examples/.*\.cxx Header Disable ================================================ FILE: cmake/rtkCompilerFlags.cmake ================================================ macro(rtk_module_warnings_disable) # Remove compiler warnings flags for the languages sent as argument. # # Mirrors itk_module_warnings_disable to avoid inclusion of ITKModuleMacros # where the macro is defined. ITKModuleMacros has the side effect of adding # KWStyle/ClangFormat external projects so it should only be included once # per ITK External/Remote module through the inclusion of ITKModuleExternal. foreach(lang ${ARGN}) if(MSVC) string(REGEX REPLACE "(^| )[/-]W[0-4]( |$)" " " CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS}") set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} /W0") elseif(BORLAND) set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -w-") else() set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} -w") endif() endforeach() endmacro() ================================================ FILE: conf.py ================================================ # Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html from datetime import date import subprocess import os # -- Build setup ------------------------------------------------------------- def setup(app): # Fetch documentation images cwd = os.getcwd() subprocess.check_call( f"cmake -DRTK_SOURCE_DIR:PATH={cwd}" f" -DRTK_DOC_OUTPUT_DIR:PATH={cwd}" " -P documentation/docs/copy_and_fetch_sphinx_doc_files.cmake", stderr=subprocess.STDOUT, shell=True, ) # -- Project information ----------------------------------------------------- project = "RTK" copyright = f"{date.today().year}, RTK Consortium" author = "RTK Consortium" # The full version, including alpha/beta/rc tags # release = '2.6.0' # -- General configuration --------------------------------------------------- extensions = [ "myst_parser", "sphinx.ext.autodoc", "sphinx_copybutton", "sphinx_design", "sphinx.ext.graphviz", ] myst_enable_extensions = [ "attrs_inline", # inline image attributes "colon_fence", "dollarmath", # Support syntax for inline and block math using `$...$` and `$$...$$` # (see https://myst-parser.readthedocs.io/en/latest/syntax/optional.html#dollar-delimited-math) "fieldlist", "linkify", # convert bare links to hyperlinks ] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The output format for Graphviz when building HTML files. This must be either 'png' or 'svg'; the default is 'png'. graphviz_output_format = "svg" # -- Options for HTML output ------------------------------------------------- html_theme = "furo" # Furo options html_theme_options = { "top_of_page_button": "edit", "source_repository": "https://github.com/RTKConsortium/RTK/", "source_branch": "main", "source_directory": "", } # Add any paths that contain custom static files (such as style sheets or icons) # here, relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] html_logo = "https://www.openrtk.org/opensourcelogos/rtk75.png" html_title = f"{project}'s documentation" html_favicon = "https://www.openrtk.org/RTK/img/rtk_favicon.ico" # -- Master document ------------------------------------------------- master_doc = "index" ================================================ FILE: documentation/Doxygen/CMakeLists.txt ================================================ if(RTK_BUILD_DOXYGEN) find_package(UnixCommands) find_package(Doxygen) find_package(Gnuplot) find_package(HTMLHelp) find_package(Perl) find_package(Wget) # # Configure the script and the doxyfile, then add target # # Information on how to retrieve the ITK documentation tag file set(ITK_DOXYGEN_TAG_LOCATION "https://itk.org/files/NightlyDoxygen/InsightDoxygenDocTag.gz") set(ITK_DOXYGEN_COMPRESSED_TAG_FILE ${PROJECT_BINARY_DIR}/Doxygen/InsightDoxygen.tag.gz) set(ITK_DOXYGEN_TAG_FILE ${PROJECT_BINARY_DIR}/Doxygen/InsightDoxygen.tag) # Get the ITK documentation tag file if(NOT EXISTS ${ITK_DOXYGEN_COMPRESSED_TAG_FILE}) file( DOWNLOAD ${ITK_DOXYGEN_TAG_LOCATION} ${ITK_DOXYGEN_COMPRESSED_TAG_FILE} TIMEOUT 60 STATUS statusITKDoxygenTagFile SHOW_PROGRESS ) list(GET statusITKDoxygenTagFile 0 statusITKDoxygenTagFile) if(statusITKDoxygenTagFile) file(REMOVE ${ITK_DOXYGEN_COMPRESSED_TAG_FILE}) endif() endif() if(EXISTS ${ITK_DOXYGEN_COMPRESSED_TAG_FILE}) find_program(GZIP_TOOL NAMES gzip) if(GZIP_TOOL) execute_process(COMMAND ${GZIP_TOOL} -dkf ${ITK_DOXYGEN_COMPRESSED_TAG_FILE}) set(DOXYGEN_TAGFILES_PARAMETER "${ITK_DOXYGEN_TAG_FILE}=https://www.itk.org/Doxygen/html/") endif() else() set(DOXYGEN_TAGFILES_PARAMETER "") endif() configure_file(${PROJECT_SOURCE_DIR}/documentation/Doxygen/doxygen.config.in ${PROJECT_BINARY_DIR}/Doxygen/doxygen.config) configure_file(${PROJECT_SOURCE_DIR}/documentation/Doxygen/itkdoxygen.pl.in ${PROJECT_BINARY_DIR}/Doxygen/itkdoxygen.pl) add_custom_target(Documentation COMMAND ${DOXYGEN_EXECUTABLE} ${PROJECT_BINARY_DIR}/Doxygen/doxygen.config MAIN_DEPENDENCY ${PROJECT_BINARY_DIR}/Doxygen/doxygen.config DEPENDS ${PROJECT_BINARY_DIR}/Doxygen/itkdoxygen.pl ${LATEXTARGETS} WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/Doxygen ) endif () ================================================ FILE: documentation/Doxygen/DoxygenFooter.html ================================================ ================================================ FILE: documentation/Doxygen/DoxygenHeader.html ================================================ $projectname: $title $title $treeview $search $mathjax $extrastylesheet
$projectname  $projectnumber
$projectbrief
$projectbrief
$searchbox
================================================ FILE: documentation/Doxygen/DoxygenStyle.css ================================================ /* The standard CSS for doxygen 1.8.15 */ body, table, div, p, dl { font: 400 14px/22px Roboto,sans-serif; } p.reference, p.definition { font: 400 14px/22px Roboto,sans-serif; } /* @group Heading Levels */ h1.groupheader { font-size: 150%; } .title { font: 400 14px/28px Roboto,sans-serif; font-size: 150%; font-weight: bold; margin: 10px 2px; } h2.groupheader { border-bottom: 1px solid #879ECB; color: #354C7B; font-size: 150%; font-weight: normal; margin-top: 1.75em; padding-top: 8px; padding-bottom: 4px; width: 100%; } h3.groupheader { font-size: 100%; } h1, h2, h3, h4, h5, h6 { -webkit-transition: text-shadow 0.5s linear; -moz-transition: text-shadow 0.5s linear; -ms-transition: text-shadow 0.5s linear; -o-transition: text-shadow 0.5s linear; transition: text-shadow 0.5s linear; margin-right: 15px; } h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow { text-shadow: 0 0 15px cyan; } dt { font-weight: bold; } div.multicol { -moz-column-gap: 1em; -webkit-column-gap: 1em; -moz-column-count: 3; -webkit-column-count: 3; } p.startli, p.startdd { margin-top: 2px; } p.starttd { margin-top: 0px; } p.endli { margin-bottom: 0px; } p.enddd { margin-bottom: 4px; } p.endtd { margin-bottom: 2px; } p.interli { } p.interdd { } p.intertd { } /* @end */ caption { font-weight: bold; } span.legend { font-size: 70%; text-align: center; } h3.version { font-size: 90%; text-align: center; } div.qindex, div.navtab{ background-color: #EBEFF6; border: 1px solid #A3B4D7; text-align: center; } div.qindex, div.navpath { width: 100%; line-height: 140%; } div.navtab { margin-right: 15px; } /* @group Link Styling */ a { color: #3D578C; font-weight: normal; text-decoration: none; } .contents a:visited { color: #4665A2; } a:hover { text-decoration: underline; } a.qindex { font-weight: bold; } a.qindexHL { font-weight: bold; background-color: #9CAFD4; color: #FFFFFF; border: 1px double #869DCA; } .contents a.qindexHL:visited { color: #FFFFFF; } a.el { font-weight: bold; } a.elRef { } a.code, a.code:visited, a.line, a.line:visited { color: #4665A2; } a.codeRef, a.codeRef:visited, a.lineRef, a.lineRef:visited { color: #4665A2; } /* @end */ dl.el { margin-left: -1cm; } ul { overflow: hidden; /*Fixed: list item bullets overlap floating elements*/ } #side-nav ul { overflow: visible; /* reset ul rule for scroll bar in GENERATE_TREEVIEW window */ } #main-nav ul { overflow: visible; /* reset ul rule for the navigation bar drop down lists */ } .fragment { text-align: left; direction: ltr; overflow-x: auto; /*Fixed: fragment lines overlap floating elements*/ overflow-y: hidden; } pre.fragment { border: 1px solid #C4CFE5; background-color: #FBFCFD; padding: 4px 6px; margin: 4px 8px 4px 2px; overflow: auto; word-wrap: break-word; font-size: 9pt; line-height: 125%; font-family: monospace, fixed; font-size: 105%; } div.fragment { padding: 0 0 1px 0; /*Fixed: last line underline overlap border*/ margin: 4px 8px 4px 2px; background-color: #FBFCFD; border: 1px solid #C4CFE5; } div.line { font-family: monospace, fixed; font-size: 13px; min-height: 13px; line-height: 1.0; text-wrap: unrestricted; white-space: -moz-pre-wrap; /* Moz */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ white-space: pre-wrap; /* CSS3 */ word-wrap: break-word; /* IE 5.5+ */ text-indent: -53px; padding-left: 53px; padding-bottom: 0px; margin: 0px; -webkit-transition-property: background-color, box-shadow; -webkit-transition-duration: 0.5s; -moz-transition-property: background-color, box-shadow; -moz-transition-duration: 0.5s; -ms-transition-property: background-color, box-shadow; -ms-transition-duration: 0.5s; -o-transition-property: background-color, box-shadow; -o-transition-duration: 0.5s; transition-property: background-color, box-shadow; transition-duration: 0.5s; } div.line:after { content:"\000A"; white-space: pre; } div.line.glow { background-color: cyan; box-shadow: 0 0 10px cyan; } span.lineno { padding-right: 4px; text-align: right; border-right: 2px solid #0F0; background-color: #E8E8E8; white-space: pre; } span.lineno a { background-color: #D8D8D8; } span.lineno a:hover { background-color: #C8C8C8; } .lineno { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } div.ah, span.ah { background-color: black; font-weight: bold; color: #FFFFFF; margin-bottom: 3px; margin-top: 3px; padding: 0.2em; border: solid thin #333; border-radius: 0.5em; -webkit-border-radius: .5em; -moz-border-radius: .5em; box-shadow: 2px 2px 3px #999; -webkit-box-shadow: 2px 2px 3px #999; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444)); background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000 110%); } div.classindex ul { list-style: none; padding-left: 0; } div.classindex span.ai { display: inline-block; } div.groupHeader { margin-left: 16px; margin-top: 12px; font-weight: bold; } div.groupText { margin-left: 16px; font-style: italic; } body { background-color: white; color: black; margin: 0; } div.contents { margin-top: 10px; margin-left: 12px; margin-right: 8px; } td.indexkey { background-color: #EBEFF6; font-weight: bold; border: 1px solid #C4CFE5; margin: 2px 0px 2px 0; padding: 2px 10px; white-space: nowrap; vertical-align: top; } td.indexvalue { background-color: #EBEFF6; border: 1px solid #C4CFE5; padding: 2px 10px; margin: 2px 0px; } tr.memlist { background-color: #EEF1F7; } p.formulaDsp { text-align: center; } img.formulaDsp { } img.formulaInl, img.inline { vertical-align: middle; } div.center { text-align: center; margin-top: 0px; margin-bottom: 0px; padding: 0px; } div.center img { border: 0px; } address.footer { text-align: right; padding-right: 12px; } img.footer { border: 0px; vertical-align: middle; } /* @group Code Colorization */ span.keyword { color: #008000 } span.keywordtype { color: #604020 } span.keywordflow { color: #e08000 } span.comment { color: #800000 } span.preprocessor { color: #806020 } span.stringliteral { color: #002080 } span.charliteral { color: #008080 } span.vhdldigit { color: #ff00ff } span.vhdlchar { color: #000000 } span.vhdlkeyword { color: #700070 } span.vhdllogic { color: #ff0000 } blockquote { background-color: #F7F8FB; border-left: 2px solid #9CAFD4; margin: 0 24px 0 4px; padding: 0 12px 0 16px; } blockquote.DocNodeRTL { border-left: 0; border-right: 2px solid #9CAFD4; margin: 0 4px 0 24px; padding: 0 16px 0 12px; } /* @end */ /* .search { color: #003399; font-weight: bold; } form.search { margin-bottom: 0px; margin-top: 0px; } input.search { font-size: 75%; color: #000080; font-weight: normal; background-color: #e8eef2; } */ td.tiny { font-size: 75%; } .dirtab { padding: 4px; border-collapse: collapse; border: 1px solid #A3B4D7; } th.dirtab { background: #EBEFF6; font-weight: bold; } hr { height: 0px; border: none; border-top: 1px solid #4A6AAA; } hr.footer { height: 1px; } /* @group Member Descriptions */ table.memberdecls { border-spacing: 0px; padding: 0px; } .memberdecls td, .fieldtable tr { -webkit-transition-property: background-color, box-shadow; -webkit-transition-duration: 0.5s; -moz-transition-property: background-color, box-shadow; -moz-transition-duration: 0.5s; -ms-transition-property: background-color, box-shadow; -ms-transition-duration: 0.5s; -o-transition-property: background-color, box-shadow; -o-transition-duration: 0.5s; transition-property: background-color, box-shadow; transition-duration: 0.5s; } .memberdecls td.glow, .fieldtable tr.glow { background-color: cyan; box-shadow: 0 0 15px cyan; } .mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams { background-color: #F9FAFC; border: none; margin: 4px; padding: 1px 0 0 8px; } .mdescLeft, .mdescRight { padding: 0px 8px 4px 8px; color: #555; } .memSeparator { border-bottom: 1px solid #DEE4F0; line-height: 1px; margin: 0px; padding: 0px; } .memItemLeft, .memTemplItemLeft { white-space: nowrap; } .memItemRight { width: 100%; } .memTemplParams { color: #4665A2; white-space: nowrap; font-size: 80%; } /* @end */ /* @group Member Details */ /* Styles for detailed member documentation */ .memtitle { padding: 8px; border-top: 1px solid #A8B8D9; border-left: 1px solid #A8B8D9; border-right: 1px solid #A8B8D9; border-top-right-radius: 4px; border-top-left-radius: 4px; margin-bottom: -1px; background-image: url('nav_f.png'); background-repeat: repeat-x; background-color: #E2E8F2; line-height: 1.25; font-weight: 300; float:left; } .permalink { font-size: 65%; display: inline-block; vertical-align: middle; } .memtemplate { font-size: 80%; color: #4665A2; font-weight: normal; margin-left: 9px; } .memnav { background-color: #EBEFF6; border: 1px solid #A3B4D7; text-align: center; margin: 2px; margin-right: 15px; padding: 2px; } .mempage { width: 100%; } .memitem { padding: 0; margin-bottom: 10px; margin-right: 5px; -webkit-transition: box-shadow 0.5s linear; -moz-transition: box-shadow 0.5s linear; -ms-transition: box-shadow 0.5s linear; -o-transition: box-shadow 0.5s linear; transition: box-shadow 0.5s linear; display: table !important; width: 100%; } .memitem.glow { box-shadow: 0 0 15px cyan; } .memname { font-weight: 400; margin-left: 6px; } .memname td { vertical-align: bottom; } .memproto, dl.reflist dt { border-top: 1px solid #A8B8D9; border-left: 1px solid #A8B8D9; border-right: 1px solid #A8B8D9; padding: 6px 0px 6px 0px; color: #253555; font-weight: bold; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); background-color: #DFE5F1; /* opera specific markup */ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); border-top-right-radius: 4px; /* firefox specific markup */ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; -moz-border-radius-topright: 4px; /* webkit specific markup */ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); -webkit-border-top-right-radius: 4px; } .overload { font-family: "courier new",courier,monospace; font-size: 65%; } .memdoc, dl.reflist dd { border-bottom: 1px solid #A8B8D9; border-left: 1px solid #A8B8D9; border-right: 1px solid #A8B8D9; padding: 6px 10px 2px 10px; background-color: #FBFCFD; border-top-width: 0; background-image:url('nav_g.png'); background-repeat:repeat-x; background-color: #FFFFFF; /* opera specific markup */ border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); /* firefox specific markup */ -moz-border-radius-bottomleft: 4px; -moz-border-radius-bottomright: 4px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px; /* webkit specific markup */ -webkit-border-bottom-left-radius: 4px; -webkit-border-bottom-right-radius: 4px; -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15); } dl.reflist dt { padding: 5px; } dl.reflist dd { margin: 0px 0px 10px 0px; padding: 5px; } .paramkey { text-align: right; } .paramtype { white-space: nowrap; } .paramname { color: #602020; white-space: nowrap; } .paramname em { font-style: normal; } .paramname code { line-height: 14px; } .params, .retval, .exception, .tparams { margin-left: 0px; padding-left: 0px; } .params .paramname, .retval .paramname, .tparams .paramname { font-weight: bold; vertical-align: top; } .params .paramtype, .tparams .paramtype { font-style: italic; vertical-align: top; } .params .paramdir, .tparams .paramdir { font-family: "courier new",courier,monospace; vertical-align: top; } table.mlabels { border-spacing: 0px; } td.mlabels-left { width: 100%; padding: 0px; } td.mlabels-right { vertical-align: bottom; padding: 0px; white-space: nowrap; } span.mlabels { margin-left: 8px; } span.mlabel { background-color: #728DC1; border-top:1px solid #5373B4; border-left:1px solid #5373B4; border-right:1px solid #C4CFE5; border-bottom:1px solid #C4CFE5; text-shadow: none; color: white; margin-right: 4px; padding: 2px 3px; border-radius: 3px; font-size: 7pt; white-space: nowrap; vertical-align: middle; } /* @end */ /* these are for tree view inside a (index) page */ div.directory { margin: 10px 0px; border-top: 1px solid #9CAFD4; border-bottom: 1px solid #9CAFD4; width: 100%; } .directory table { border-collapse:collapse; } .directory td { margin: 0px; padding: 0px; vertical-align: top; } .directory td.entry { white-space: nowrap; padding-right: 6px; padding-top: 3px; } .directory td.entry a { outline:none; } .directory td.entry a img { border: none; } .directory td.desc { width: 100%; padding-left: 6px; padding-right: 6px; padding-top: 3px; border-left: 1px solid rgba(0,0,0,0.05); } .directory tr.even { padding-left: 6px; background-color: #F7F8FB; } .directory img { vertical-align: -30%; } .directory .levels { white-space: nowrap; width: 100%; text-align: right; font-size: 9pt; } .directory .levels span { cursor: pointer; padding-left: 2px; padding-right: 2px; color: #3D578C; } .arrow { color: #9CAFD4; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: pointer; font-size: 80%; display: inline-block; width: 16px; height: 22px; } .icon { font-family: Arial, Helvetica; font-weight: bold; font-size: 12px; height: 14px; width: 16px; display: inline-block; background-color: #728DC1; color: white; text-align: center; border-radius: 4px; margin-left: 2px; margin-right: 2px; } .icona { width: 24px; height: 22px; display: inline-block; } .iconfopen { width: 24px; height: 18px; margin-bottom: 4px; background-image:url('folderopen.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } .iconfclosed { width: 24px; height: 18px; margin-bottom: 4px; background-image:url('folderclosed.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } .icondoc { width: 24px; height: 18px; margin-bottom: 4px; background-image:url('doc.png'); background-position: 0px -4px; background-repeat: repeat-y; vertical-align:top; display: inline-block; } table.directory { font: 400 14px Roboto,sans-serif; } /* @end */ div.dynheader { margin-top: 8px; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } address { font-style: normal; color: #2A3D61; } table.doxtable caption { caption-side: top; } table.doxtable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.doxtable td, table.doxtable th { border: 1px solid #2D4068; padding: 3px 7px 2px; } table.doxtable th { background-color: #374F7F; color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; } table.fieldtable { /*width: 100%;*/ margin-bottom: 10px; border: 1px solid #A8B8D9; border-spacing: 0px; -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px; -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15); } .fieldtable td, .fieldtable th { padding: 3px 7px 2px; } .fieldtable td.fieldtype, .fieldtable td.fieldname { white-space: nowrap; border-right: 1px solid #A8B8D9; border-bottom: 1px solid #A8B8D9; vertical-align: top; } .fieldtable td.fieldname { padding-top: 3px; } .fieldtable td.fielddoc { border-bottom: 1px solid #A8B8D9; /*width: 100%;*/ } .fieldtable td.fielddoc p:first-child { margin-top: 0px; } .fieldtable td.fielddoc p:last-child { margin-bottom: 2px; } .fieldtable tr:last-child td { border-bottom: none; } .fieldtable th { background-image:url('nav_f.png'); background-repeat:repeat-x; background-color: #E2E8F2; font-size: 90%; color: #253555; padding-bottom: 4px; padding-top: 5px; text-align:left; font-weight: 400; -moz-border-radius-topleft: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-left-radius: 4px; -webkit-border-top-right-radius: 4px; border-top-left-radius: 4px; border-top-right-radius: 4px; border-bottom: 1px solid #A8B8D9; } .tabsearch { top: 0px; left: 10px; height: 36px; background-image: url('tab_b.png'); z-index: 101; overflow: hidden; font-size: 13px; } .navpath ul { font-size: 11px; background-image:url('tab_b.png'); background-repeat:repeat-x; background-position: 0 -5px; height:30px; line-height:30px; color:#8AA0CC; border:solid 1px #C2CDE4; overflow:hidden; margin:0px; padding:0px; } .navpath li { list-style-type:none; float:left; padding-left:10px; padding-right:15px; background-image:url('bc_s.png'); background-repeat:no-repeat; background-position:right; color:#364D7C; } .navpath li.navelem a { height:32px; display:block; text-decoration: none; outline: none; color: #283A5D; font-family: 'Lucida Grande',Geneva,Helvetica,Arial,sans-serif; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); text-decoration: none; } .navpath li.navelem a:hover { color:#6884BD; } .navpath li.footer { list-style-type:none; float:right; padding-left:10px; padding-right:15px; background-image:none; background-repeat:no-repeat; background-position:right; color:#364D7C; font-size: 8pt; } div.summary { float: right; font-size: 8pt; padding-right: 5px; width: 50%; text-align: right; } div.summary a { white-space: nowrap; } table.classindex { margin: 10px; white-space: nowrap; margin-left: 3%; margin-right: 3%; width: 94%; border: 0; border-spacing: 0; padding: 0; } div.ingroups { font-size: 8pt; width: 50%; text-align: left; } div.ingroups a { white-space: nowrap; } div.header { background-image:url('nav_h.png'); background-repeat:repeat-x; background-color: #F9FAFC; margin: 0px; border-bottom: 1px solid #C4CFE5; } div.headertitle { padding: 5px 5px 5px 10px; } .PageDocRTL-title div.headertitle { text-align: right; direction: rtl; } dl { padding: 0 0 0 0; } /* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug, dl.examples */ dl.section { margin-left: 0px; padding-left: 0px; } dl.section.DocNodeRTL { margin-right: 0px; padding-right: 0px; } dl.note { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #D0C000; } dl.note.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #D0C000; } dl.warning, dl.attention { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #FF0000; } dl.warning.DocNodeRTL, dl.attention.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #FF0000; } dl.pre, dl.post, dl.invariant { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #00D000; } dl.pre.DocNodeRTL, dl.post.DocNodeRTL, dl.invariant.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #00D000; } dl.deprecated { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #505050; } dl.deprecated.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #505050; } dl.todo { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #00C0E0; } dl.todo.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #00C0E0; } dl.test { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #3030E0; } dl.test.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #3030E0; } dl.bug { margin-left: -7px; padding-left: 3px; border-left: 4px solid; border-color: #C08050; } dl.bug.DocNodeRTL { margin-left: 0; padding-left: 0; border-left: 0; margin-right: -7px; padding-right: 3px; border-right: 4px solid; border-color: #C08050; } dl.section dd { margin-bottom: 6px; } #projectlogo { text-align: center; vertical-align: bottom; border-collapse: separate; } #projectlogo img { border: 0px none; } #projectalign { vertical-align: middle; } #projectname { font: 300% Tahoma, Arial,sans-serif; margin: 0px; padding: 2px 0px; } #projectbrief { font: 120% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #projectnumber { font: 50% Tahoma, Arial,sans-serif; margin: 0px; padding: 0px; } #titlearea { padding: 0px; margin: 0px; width: 100%; border-bottom: 1px solid #5373B4; } .image { text-align: center; } .dotgraph { text-align: center; } .mscgraph { text-align: center; } .plantumlgraph { text-align: center; } .diagraph { text-align: center; } .caption { font-weight: bold; } div.zoom { border: 1px solid #90A5CE; } dl.citelist { margin-bottom:50px; } dl.citelist dt { color:#334975; float:left; font-weight:bold; margin-right:10px; padding:5px; } dl.citelist dd { margin:2px 0; padding:5px 0; } div.toc { padding: 14px 25px; background-color: #F4F6FA; border: 1px solid #D8DFEE; border-radius: 7px 7px 7px 7px; float: right; height: auto; margin: 0 8px 10px 10px; width: 200px; } .PageDocRTL-title div.toc { float: left !important; text-align: right; } div.toc li { background: url("bdwn.png") no-repeat scroll 0 5px transparent; font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif; margin-top: 5px; padding-left: 10px; padding-top: 2px; } .PageDocRTL-title div.toc li { background-position-x: right !important; padding-left: 0 !important; padding-right: 10px; } div.toc h3 { font: bold 12px/1.2 Arial,FreeSans,sans-serif; color: #4665A2; border-bottom: 0 none; margin: 0; } div.toc ul { list-style: none outside none; border: medium none; padding: 0px; } div.toc li.level1 { margin-left: 0px; } div.toc li.level2 { margin-left: 15px; } div.toc li.level3 { margin-left: 30px; } div.toc li.level4 { margin-left: 45px; } .PageDocRTL-title div.toc li.level1 { margin-left: 0 !important; margin-right: 0; } .PageDocRTL-title div.toc li.level2 { margin-left: 0 !important; margin-right: 15px; } .PageDocRTL-title div.toc li.level3 { margin-left: 0 !important; margin-right: 30px; } .PageDocRTL-title div.toc li.level4 { margin-left: 0 !important; margin-right: 45px; } .inherit_header { font-weight: bold; color: gray; cursor: pointer; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .inherit_header td { padding: 6px 0px 2px 5px; } .inherit { display: none; } tr.heading h2 { margin-top: 12px; margin-bottom: 4px; } /* tooltip related style info */ .ttc { position: absolute; display: none; } #powerTip { cursor: default; white-space: nowrap; background-color: white; border: 1px solid gray; border-radius: 4px 4px 4px 4px; box-shadow: 1px 1px 7px gray; display: none; font-size: smaller; max-width: 80%; opacity: 0.9; padding: 1ex 1em 1em; position: absolute; z-index: 2147483647; } #powerTip div.ttdoc { color: grey; font-style: italic; } #powerTip div.ttname a { font-weight: bold; } #powerTip div.ttname { font-weight: bold; } #powerTip div.ttdeci { color: #006318; } #powerTip div { margin: 0px; padding: 0px; font: 12px/16px Roboto,sans-serif; } #powerTip:before, #powerTip:after { content: ""; position: absolute; margin: 0px; } #powerTip.n:after, #powerTip.n:before, #powerTip.s:after, #powerTip.s:before, #powerTip.w:after, #powerTip.w:before, #powerTip.e:after, #powerTip.e:before, #powerTip.ne:after, #powerTip.ne:before, #powerTip.se:after, #powerTip.se:before, #powerTip.nw:after, #powerTip.nw:before, #powerTip.sw:after, #powerTip.sw:before { border: solid transparent; content: " "; height: 0; width: 0; position: absolute; } #powerTip.n:after, #powerTip.s:after, #powerTip.w:after, #powerTip.e:after, #powerTip.nw:after, #powerTip.ne:after, #powerTip.sw:after, #powerTip.se:after { border-color: rgba(255, 255, 255, 0); } #powerTip.n:before, #powerTip.s:before, #powerTip.w:before, #powerTip.e:before, #powerTip.nw:before, #powerTip.ne:before, #powerTip.sw:before, #powerTip.se:before { border-color: rgba(128, 128, 128, 0); } #powerTip.n:after, #powerTip.n:before, #powerTip.ne:after, #powerTip.ne:before, #powerTip.nw:after, #powerTip.nw:before { top: 100%; } #powerTip.n:after, #powerTip.ne:after, #powerTip.nw:after { border-top-color: #FFFFFF; border-width: 10px; margin: 0px -10px; } #powerTip.n:before { border-top-color: #808080; border-width: 11px; margin: 0px -11px; } #powerTip.n:after, #powerTip.n:before { left: 50%; } #powerTip.nw:after, #powerTip.nw:before { right: 14px; } #powerTip.ne:after, #powerTip.ne:before { left: 14px; } #powerTip.s:after, #powerTip.s:before, #powerTip.se:after, #powerTip.se:before, #powerTip.sw:after, #powerTip.sw:before { bottom: 100%; } #powerTip.s:after, #powerTip.se:after, #powerTip.sw:after { border-bottom-color: #FFFFFF; border-width: 10px; margin: 0px -10px; } #powerTip.s:before, #powerTip.se:before, #powerTip.sw:before { border-bottom-color: #808080; border-width: 11px; margin: 0px -11px; } #powerTip.s:after, #powerTip.s:before { left: 50%; } #powerTip.sw:after, #powerTip.sw:before { right: 14px; } #powerTip.se:after, #powerTip.se:before { left: 14px; } #powerTip.e:after, #powerTip.e:before { left: 100%; } #powerTip.e:after { border-left-color: #FFFFFF; border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.e:before { border-left-color: #808080; border-width: 11px; top: 50%; margin-top: -11px; } #powerTip.w:after, #powerTip.w:before { right: 100%; } #powerTip.w:after { border-right-color: #FFFFFF; border-width: 10px; top: 50%; margin-top: -10px; } #powerTip.w:before { border-right-color: #808080; border-width: 11px; top: 50%; margin-top: -11px; } @media print { #top { display: none; } #side-nav { display: none; } #nav-path { display: none; } body { overflow:visible; } h1, h2, h3, h4, h5, h6 { page-break-after: avoid; } .summary { display: none; } .memitem { page-break-inside: avoid; } #doc-content { margin-left:0 !important; height:auto !important; width:auto !important; overflow:inherit; display:inline; } } /* @group Markdown */ /* table.markdownTable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.markdownTable td, table.markdownTable th { border: 1px solid #2D4068; padding: 3px 7px 2px; } table.markdownTableHead tr { } table.markdownTableBodyLeft td, table.markdownTable th { border: 1px solid #2D4068; padding: 3px 7px 2px; } th.markdownTableHeadLeft th.markdownTableHeadRight th.markdownTableHeadCenter th.markdownTableHeadNone { background-color: #374F7F; color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; } th.markdownTableHeadLeft { text-align: left } th.markdownTableHeadRight { text-align: right } th.markdownTableHeadCenter { text-align: center } */ table.markdownTable { border-collapse:collapse; margin-top: 4px; margin-bottom: 4px; } table.markdownTable td, table.markdownTable th { border: 1px solid #2D4068; padding: 3px 7px 2px; } table.markdownTable tr { } th.markdownTableHeadLeft, th.markdownTableHeadRight, th.markdownTableHeadCenter, th.markdownTableHeadNone { background-color: #374F7F; color: #FFFFFF; font-size: 110%; padding-bottom: 4px; padding-top: 5px; } th.markdownTableHeadLeft, td.markdownTableBodyLeft { text-align: left } th.markdownTableHeadRight, td.markdownTableBodyRight { text-align: right } th.markdownTableHeadCenter, td.markdownTableBodyCenter { text-align: center } .DocNodeRTL { text-align: right; direction: rtl; } .DocNodeLTR { text-align: left; direction: ltr; } table.DocNodeRTL { width: auto; margin-right: 0; margin-left: auto; } table.DocNodeLTR { width: auto; margin-right: auto; margin-left: 0; } tt, code, kbd, samp { display: inline-block; direction:ltr; } /* @end */ u { text-decoration: underline; } ================================================ FILE: documentation/Doxygen/MainPage.dox ================================================ /** * * \mainpage Reconstruction Toolkit * * \image html https://www.openrtk.org/opensourcelogos/rtk100.png width=200 * * Welcome to the RTK project! The objective of this project is to develop * open-source software for fast tomographic reconstruction compatible with * ITK. The Home Page of the Reconstruction toolkit can be found at : * * https://www.openrtk.org/ * * This documentation describes the API of the Toolkit. The Modules * link presents a hierarchy of classes organized according to their * functionality. * */ ================================================ FILE: documentation/Doxygen/Modules.dox ================================================ /** \defgroup Projector Objects related to forward- / back-projection. */ /** \defgroup ReconstructionAlgorithm Objects to reconstruct a tomography. */ /** \defgroup CudaImageToImageFilter Filters using the GPU with CUDA. */ /** \defgroup Macro Useful macros for RTK development. */ /** \defgroup Geometry Geometrical computations. */ ================================================ FILE: documentation/Doxygen/doxygen.config.in ================================================ # Doxyfile 1.8.15 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = RTK # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @RTK_VERSION_MAJOR@.@RTK_VERSION_MINOR@.@RTK_VERSION_PATCH@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "Reconstruction Toolkit" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = https://www.openrtk.org/opensourcelogos/rtk100.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/Doxygen # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = NO # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = NO # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = @PROJECT_BINARY_DIR@/ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 2 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) # Follow example from https://www.doxygen.nl/manual/custcmd.html # Use ^^ instead of \n in aliases https://sourceforge.net/p/doxygen/mailman/message/36215997/ ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files), VHDL, tcl. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is # Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 0. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 0 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = YES # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 2 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = YES # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = "@RTK_SOURCE_DIR@/documentation/Doxygen/DoxygenLayout.xml" # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = "@PROJECT_SOURCE_DIR@" \ "@PROJECT_SOURCE_DIR@/documentation/Doxygen" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, # *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.h \ *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = @PROJECT_SOURCE_DIR@/utilities # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = "*_ggo.h" # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = "itk::*" # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = *.cxx # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = YES # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = "perl @PROJECT_BINARY_DIR@/Doxygen/itkdoxygen.pl" # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = YES # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 1 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = "@PROJECT_SOURCE_DIR@/documentation/Doxygen/DoxygenHeader.html" # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = "@PROJECT_SOURCE_DIR@/documentation/Doxygen/DoxygenFooter.html" # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = "@PROJECT_SOURCE_DIR@/documentation/Doxygen/DoxygenStyle.css" # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via Javascript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have Javascript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = YES # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: https://developer.apple.com/xcode/), introduced with OSX # 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "RTK Doxygen generated documentation" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.rtk.RTK # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.rtk.RTKConsortium # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = RTKConsortium # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = @ITK_DOXYGEN_GEN_ECLIPSEHELP@ # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = NO # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 1 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # https://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: https://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /