diff --git a/applications/rtkiterativefdk/rtkiterativefdk.py b/applications/rtkiterativefdk/rtkiterativefdk.py new file mode 100644 index 000000000..faea1bdfb --- /dev/null +++ b/applications/rtkiterativefdk/rtkiterativefdk.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +import argparse +import sys +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]." + ) + ) + + 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( + "--subsetsize", + help="Streaming option: number of projections processed at a time", + type=int, + default=16, + ) + parser.add_argument( + "--niterations", "-n", type=int, default=3, help="Number of iterations" + ) + parser.add_argument( + "--lambda", + "-l", + dest="lambda_", + type=float, + default=0.3, + help="Convergence factor", + ) + parser.add_argument( + "--positivity", + help="Enforces positivity during the reconstruction", + action="store_true", + ) + parser.add_argument( + "--nodisplaced", + help="Disable the displaced detector filter", + action="store_true", + ) + + # 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, + ) + + rtk.add_rtkprojectors_group(parser) + rtk.add_rtkinputprojections_group(parser) + rtk.add_rtk3Doutputimage_group(parser) + rtk.add_rtkiterations_group(parser) + + return parser + + +def process(args_info: argparse.Namespace): + OutputPixelType = itk.F + Dimension = 3 + OutputImageType = itk.Image[OutputPixelType, Dimension] + + # 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) + + # Projections reader + reader = rtk.ProjectionsReader[OutputImageType].New() + rtk.SetProjectionsReaderFromArgParse(reader, args_info) + + if args_info.verbose: + print("Reading... ") + reader.Update() + + # Geometry + if args_info.verbose: + print(f"Reading geometry information from {args_info.geometry}...") + geometry = rtk.read_geometry(args_info.geometry) + + # Create reconstructed image + constantImageSource = rtk.ConstantImageSource[OutputImageType].New() + rtk.SetConstantImageSourceFromArgParse(constantImageSource, args_info) + + enforcePositivity = bool(args_info.positivity) + + # Create Iterative FDK filter and connect it + if args_info.hardware == "cuda": + ifdk = rtk.CudaIterativeFDKConeBeamReconstructionFilter.New() + ifdk.SetInput(0, itk.cuda_image_from_image(constantImageSource.GetOutput())) + ifdk.SetInput(1, itk.cuda_image_from_image(reader.GetOutput())) + else: + IFDKCPUType = rtk.IterativeFDKConeBeamReconstructionFilter[ + OutputImageType, OutputImageType, itk.D + ] + ifdk = IFDKCPUType.New() + ifdk.SetInput(0, constantImageSource.GetOutput()) + ifdk.SetInput(1, reader.GetOutput()) + + # Common options + ifdk.SetGeometry(geometry) + rtk.SetForwardProjectionFromArgParse(args_info, ifdk) + ifdk.SetNumberOfIterations(args_info.niterations) + ifdk.SetTruncationCorrection(args_info.pad) + ifdk.SetHannCutFrequency(args_info.hann) + ifdk.SetHannCutFrequencyY(args_info.hannY) + ifdk.SetProjectionSubsetSize(args_info.subsetsize) + ifdk.SetLambda(args_info.lambda_) + ifdk.SetEnforcePositivity(enforcePositivity) + ifdk.SetDisableDisplacedDetectorFilter(args_info.nodisplaced) + + # Iterations reporting + rtk.SetIterationsReportFromArgParse(args_info, ifdk) + + # Run the filter and write + if args_info.verbose: + print("Reconstructing and writing... ") + + # Write + writer = itk.ImageFileWriter[OutputImageType].New() + writer.SetFileName(args_info.output) + writer.SetInput(ifdk.GetOutput()) + writer.Update() + + +def main(argv=None): + parser = build_parser() + args_info = parser.parse_args(argv) + process(args_info) + + +if __name__ == "__main__": + main() diff --git a/applications/rtkregularizedconjugategradient/rtkregularizedconjugategradient.py b/applications/rtkregularizedconjugategradient/rtkregularizedconjugategradient.py new file mode 100644 index 000000000..beb6b2cb2 --- /dev/null +++ b/applications/rtkregularizedconjugategradient/rtkregularizedconjugategradient.py @@ -0,0 +1,254 @@ +#!/usr/bin/env python +import argparse +import itk +from itk import RTK as rtk + + +def build_parser(): + parser = rtk.RTKArgumentParser( + description="Alternates between conjugate gradient reconstruction and regularization" + ) + + 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( + "--niter", + "-n", + help="Number of iterations", + type=int, + default=5, + ) + + parser.add_argument("--input", "-i", help="Input volume", type=str) + parser.add_argument( + "--weights", + "-w", + help="Weights file for Weighted Least Squares (WLS)", + type=str, + ) + + parser.add_argument( + "--gammalaplacian", + help="Laplacian regularization weight", + type=float, + default=0.0, + ) + parser.add_argument( + "--tikhonov", + help="Tikhonov regularization weight", + type=float, + default=0.0, + ) + parser.add_argument( + "--cgiter", + help="Number of conjugate gradient nested iterations", + type=int, + default=4, + ) + 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)", + type=str, + ) + parser.add_argument( + "--nodisplaced", + help="Disable the displaced detector filter", + action="store_true", + ) + + reg = parser.add_argument_group("Regularization") + reg.add_argument( + "--nopositivity", + help="Do not enforce positivity", + action="store_true", + ) + reg.add_argument( + "--tviter", + help="Total variation regularization: number of iterations", + type=int, + default=10, + ) + reg.add_argument( + "--gammatv", + help="Total variation spatial regularization parameter. The larger, the smoother", + type=float, + ) + reg.add_argument( + "--threshold", + help="Daubechies wavelets spatial regularization: soft threshold", + type=float, + ) + reg.add_argument( + "--order", + help="Daubechies wavelets spatial regularization: order of the wavelets", + type=int, + default=5, + ) + reg.add_argument( + "--levels", + help="Daubechies wavelets spatial regularization: number of decomposition levels", + type=int, + default=3, + ) + reg.add_argument( + "--soft", + help="Soft threshold for image domain sparsity enforcement", + type=float, + ) + + rtk.add_rtkinputprojections_group(parser) + rtk.add_rtk3Doutputimage_group(parser) + rtk.add_rtkprojectors_group(parser) + rtk.add_rtkiterations_group(parser) + + return parser + + +def process(args_info: argparse.Namespace) -> None: + 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) + + # Input volume: file or constant image + if args_info.input: + inputFilter = itk.ImageFileReader[OutputImageType].New() + inputFilter.SetFileName(args_info.input) + else: + inputFilter = rtk.ConstantImageSource[OutputImageType].New() + rtk.SetConstantImageSourceFromArgParse(inputFilter, args_info) + + inputFilter.Update() + + # Weights: file or constant 1 image matching projections + if args_info.weights: + weightsSource = itk.ImageFileReader[OutputImageType].New() + weightsSource.SetFileName(args_info.weights) + else: + weightsSource = rtk.ConstantImageSource[OutputImageType].New() + reader.UpdateOutputInformation() + weightsSource.SetInformationFromImage(reader.GetOutput()) + weightsSource.SetConstant(1.0) + + # Support mask + if args_info.mask: + supportmask = itk.imread(args_info.mask) + + # Set the forward and back projection filters to be used + if hasattr(itk, "CudaImage") and not args_info.nocudacg: + CudaImageType = itk.CudaImage[OutputPixelType, Dimension] + regularizedConjugateGradient = ( + rtk.RegularizedConjugateGradientConeBeamReconstructionFilter[ + CudaImageType + ].New() + ) + regularizedConjugateGradient.SetInputVolume( + itk.cuda_image_from_image(inputFilter.GetOutput()) + ) + regularizedConjugateGradient.SetInputProjectionStack( + itk.cuda_image_from_image(reader.GetOutput()) + ) + regularizedConjugateGradient.SetInputWeights( + itk.cuda_image_from_image(weightsSource.GetOutput()) + ) + if args_info.mask: + regularizedConjugateGradient.SetSupportMask( + itk.cuda_image_from_image(supportmask) + ) + + else: + regularizedConjugateGradient = ( + rtk.RegularizedConjugateGradientConeBeamReconstructionFilter[ + OutputImageType + ].New() + ) + + # Inputs + regularizedConjugateGradient.SetInputVolume(inputFilter.GetOutput()) + regularizedConjugateGradient.SetInputProjectionStack(reader.GetOutput()) + regularizedConjugateGradient.SetInputWeights(weightsSource.GetOutput()) + if args_info.mask: + regularizedConjugateGradient.SetSupportMask(supportmask) + + rtk.SetForwardProjectionFromArgParse(args_info, regularizedConjugateGradient) + rtk.SetBackProjectionFromArgParse(args_info, regularizedConjugateGradient) + + regularizedConjugateGradient.SetGeometry(geometry) + regularizedConjugateGradient.SetMainLoop_iterations(args_info.niter) + regularizedConjugateGradient.SetCG_iterations(args_info.cgiter) + regularizedConjugateGradient.SetCudaConjugateGradient(not args_info.nocudacg) + regularizedConjugateGradient.SetDisableDisplacedDetectorFilter( + args_info.nodisplaced + ) + regularizedConjugateGradient.SetPerformPositivity(not args_info.nopositivity) + regularizedConjugateGradient.SetGamma(args_info.gammalaplacian) + regularizedConjugateGradient.SetTikhonov(args_info.tikhonov) + + # TV + if args_info.gammatv is not None: + regularizedConjugateGradient.SetGammaTV(args_info.gammatv) + regularizedConjugateGradient.SetTV_iterations(args_info.tviter) + regularizedConjugateGradient.SetPerformTVSpatialDenoising(True) + else: + regularizedConjugateGradient.SetPerformTVSpatialDenoising(False) + + # Wavelets + if args_info.threshold is not None: + regularizedConjugateGradient.SetSoftThresholdWavelets(args_info.threshold) + regularizedConjugateGradient.SetOrder(args_info.order) + regularizedConjugateGradient.SetNumberOfLevels(args_info.levels) + regularizedConjugateGradient.SetPerformWaveletsSpatialDenoising(True) + else: + regularizedConjugateGradient.SetPerformWaveletsSpatialDenoising(False) + + # Image-domain soft threshold + if args_info.soft is not None: + regularizedConjugateGradient.SetSoftThresholdOnImage(args_info.soft) + regularizedConjugateGradient.SetPerformSoftThresholdOnImage(True) + else: + regularizedConjugateGradient.SetPerformSoftThresholdOnImage(False) + + # Iteration reporting + rtk.SetIterationsReportFromArgParse(args_info, regularizedConjugateGradient) + + if args_info.verbose: + print("Reconstructing...") + + regularizedConjugateGradient.Update() + + if args_info.verbose: + print(f"Writing output to {args_info.output}...") + + # Write + writer = itk.ImageFileWriter[OutputImageType].New() + writer.SetFileName(args_info.output) + writer.SetInput(regularizedConjugateGradient.GetOutput()) + writer.Update() + + +def main(argv=None): + parser = build_parser() + args_info = parser.parse_args(argv) + process(args_info) + + +if __name__ == "__main__": + main() diff --git a/applications/rtkscatterglarecorrection/rtkscatterglarecorrection.py b/applications/rtkscatterglarecorrection/rtkscatterglarecorrection.py new file mode 100644 index 000000000..a8199d4bd --- /dev/null +++ b/applications/rtkscatterglarecorrection/rtkscatterglarecorrection.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +import argparse +import sys +import itk +from itk import RTK as rtk + + +def build_parser(): + parser = rtk.RTKArgumentParser( + description="Reads projection images and correct them for scatter glare" + ) + + parser.add_argument("--output", "-o", help="Output filename", type=str) + parser.add_argument( + "--bufferSize", + help="Number of projections computed at the same time", + type=int, + default=4, + ) + parser.add_argument( + "--difference", + "-d", + help="Output the difference between input and corrected images", + action="store_true", + ) + + algorithm = parser.add_argument_group("Algorithm parameters") + algorithm.add_argument( + "--coefficients", + "-c", + help="Deconvolution kernel coefficients", + type=float, + nargs="+", + required=True, + ) + + rtk.add_rtkinputprojections_group(parser) + + return parser + + +def process(args_info: argparse.Namespace): + Dimension = 3 + InputImageType = itk.Image[itk.F, Dimension] + + if len(args_info.coefficients) != 2: + print("--coefficients requires exactly 2 coefficients") + sys.exit(1) + + reader = rtk.ProjectionsReader[InputImageType].New() + reader.SetFileNames(rtk.GetProjectionsFileNamesFromArgParse(args_info)) + reader.ComputeLineIntegralOff() + reader.UpdateOutputInformation() + + # Input projection parameters + sizeInput = list(reader.GetOutput().GetLargestPossibleRegion().GetSize()) + Nproj = int(sizeInput[2]) + + if hasattr(itk, "CudaImage"): + SFilter = rtk.CudaScatterGlareCorrectionImageFilter.New() + constantSource = rtk.ConstantImageSource[itk.CudaImage[itk.F, Dimension]].New() + else: + SFilter = rtk.ScatterGlareCorrectionImageFilter[ + InputImageType, InputImageType, itk.F + ].New() + constantSource = rtk.ConstantImageSource[InputImageType].New() + + SFilter.SetTruncationCorrection(0.0) + SFilter.SetCoefficients(args_info.coefficients) + + paste = itk.PasteImageFilter[InputImageType].New() + paste.SetSourceImage(SFilter.GetOutput()) + paste.SetDestinationImage(constantSource.GetOutput()) + + if args_info.verbose: + print("Starting processing") + projid = 0 + first = True + while projid < Nproj: + curBufferSize = min(args_info.bufferSize, Nproj - projid) + + sliceRegionA = reader.GetOutput().GetLargestPossibleRegion() + sizeA = list(sliceRegionA.GetSize()) + indexA = list(sliceRegionA.GetIndex()) + desiredRegionA = itk.ImageRegion[Dimension]() + desiredRegionA.SetSize([sizeA[0], sizeA[1], curBufferSize]) + desiredRegionA.SetIndex([indexA[0], indexA[1], projid]) + + extract = itk.ExtractImageFilter[InputImageType, InputImageType].New() + extract.SetDirectionCollapseToIdentity() + extract.SetExtractionRegion(desiredRegionA) + extract.SetInput(reader.GetOutput()) + extract.Update() + + image = extract.GetOutput() + image.DisconnectPipeline() + + if hasattr(itk, "CudaImage"): + SFilter.SetInput(itk.cuda_image_from_image(image)) + else: + SFilter.SetInput(image) + SFilter.GetOutput().SetRequestedRegion(image.GetRequestedRegion()) + SFilter.Update() + + procImage = SFilter.GetOutput() + procImage.DisconnectPipeline() + + if args_info.difference: + subtractFilter = itk.SubtractImageFilter[ + InputImageType, InputImageType, InputImageType + ].New() + subtractFilter.SetInput1(image) + subtractFilter.SetInput2(procImage) + subtractFilter.Update() + outImage = subtractFilter.GetOutput() + outImage.DisconnectPipeline() + else: + outImage = procImage + + current_idx = list(outImage.GetLargestPossibleRegion().GetIndex()) + current_idx[2] = projid + + if first: + sizeInput_local = list(outImage.GetLargestPossibleRegion().GetSize()) + sizeInput_local[2] = Nproj + + spacingInput = outImage.GetSpacing() + originInput = outImage.GetOrigin() + + imageDirection = itk.Matrix[itk.D, Dimension, Dimension]() + imageDirection.SetIdentity() + + # Initialization of the output volume + constantSource.SetOrigin(originInput) + constantSource.SetSpacing(spacingInput) + constantSource.SetDirection(imageDirection) + constantSource.SetSize(sizeInput_local) + constantSource.SetConstant(0.0) + first = False + else: + paste.SetDestinationImage(paste.GetOutput()) + + paste.SetSourceImage(outImage) + paste.SetSourceRegion(outImage.GetLargestPossibleRegion()) + paste.SetDestinationIndex(current_idx) + paste.Update() + + projid += curBufferSize + + if args_info.output: + itk.imwrite(paste.GetOutput(), args_info.output) + + +def main(argv=None): + parser = build_parser() + args_info = parser.parse_args(argv) + process(args_info) + + +if __name__ == "__main__": + main() diff --git a/applications/rtktotalnuclearvariationdenoising/rtktotalnuclearvariationdenoising.py b/applications/rtktotalnuclearvariationdenoising/rtktotalnuclearvariationdenoising.py new file mode 100644 index 000000000..d9ae83ed2 --- /dev/null +++ b/applications/rtktotalnuclearvariationdenoising/rtktotalnuclearvariationdenoising.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +import argparse +import itk +from itk import RTK as rtk + + +def build_parser(): + parser = rtk.RTKArgumentParser( + description="Performs total nuclear variation denoising of a 3D + channels image." + ) + parser.add_argument( + "--input", "-i", help="Input file name", type=str, required=True + ) + parser.add_argument( + "--output", "-o", help="Output file name", type=str, required=True + ) + parser.add_argument( + "--gamma", + "-g", + help="TV term's weighting parameter", + type=float, + default=1.0, + ) + parser.add_argument( + "--niter", + "-n", + help="Number of iterations", + type=int, + default=5, + ) + + return parser + + +def process(args_info: argparse.Namespace): + OutputPixelType = itk.F + Dimension = 4 # Number of dimensions of the input image + DimensionsProcessed = 3 # Number of dimensions along which the gradient is computed + + OutputImageType = itk.Image[OutputPixelType, Dimension] + GradientPixelType = itk.CovariantVector[OutputPixelType, DimensionsProcessed] + GradientImageType = itk.Image[GradientPixelType, Dimension] + + # Read input + input_image = itk.imread(args_info.input, pixel_type=OutputPixelType) + + # Apply total nuclear variation denoising + tv = rtk.TotalNuclearVariationDenoisingBPDQImageFilter[ + OutputImageType, + GradientImageType, + ].New() + tv.SetInput(input_image) + tv.SetGamma(args_info.gamma) + tv.SetNumberOfIterations(args_info.niter) + + # Write + itk.imwrite(tv.GetOutput(), args_info.output) + + +def main(argv=None): + parser = build_parser() + args_info = parser.parse_args(argv) + process(args_info) + + +if __name__ == "__main__": + main() diff --git a/include/rtkRegularizedConjugateGradientConeBeamReconstructionFilter.hxx b/include/rtkRegularizedConjugateGradientConeBeamReconstructionFilter.hxx index 4aa249257..cb9b546eb 100644 --- a/include/rtkRegularizedConjugateGradientConeBeamReconstructionFilter.hxx +++ b/include/rtkRegularizedConjugateGradientConeBeamReconstructionFilter.hxx @@ -135,12 +135,35 @@ template void RegularizedConjugateGradientConeBeamReconstructionFilter::GenerateInputRequestedRegion() { - // Call the superclass' implementation of this method - Superclass::GenerateInputRequestedRegion(); + // Input 0 is the volume we update + typename TImage::Pointer inputPtr0 = const_cast(this->GetInputVolume().GetPointer()); + if (!inputPtr0) + return; + inputPtr0->SetRequestedRegion(this->GetOutput()->GetRequestedRegion()); + + // Input 1 is the stack of projections to backproject + typename TImage::Pointer inputPtr1 = const_cast(this->GetInputProjectionStack().GetPointer()); + if (!inputPtr1) + return; + inputPtr1->SetRequestedRegion(inputPtr1->GetLargestPossibleRegion()); + + // Input "Weights" is the weights map on projections, either user-defined or filled with ones (default) + if (this->GetInputWeights().IsNotNull()) + { + typename TImage::Pointer inputWeights = const_cast(this->GetInputWeights().GetPointer()); + if (!inputWeights) + return; + inputWeights->SetRequestedRegion(inputWeights->GetLargestPossibleRegion()); + } - // Let the CG subfilters compute the requested regions for the projections - // stack and the input volume - m_CGFilter->PropagateRequestedRegion(m_CGFilter->GetOutput()); + // Input "SupportMask" is the support constraint mask on volume, if any + if (this->GetSupportMask().IsNotNull()) + { + typename TImage::Pointer inputSupportMaskPtr = const_cast(this->GetSupportMask().GetPointer()); + if (!inputSupportMaskPtr) + return; + inputSupportMaskPtr->SetRequestedRegion(this->GetOutput()->GetRequestedRegion()); + } } template diff --git a/pyproject.toml b/pyproject.toml index 304d5cfdd..98a06632a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,6 +60,7 @@ rtkfieldofview = "itk.rtkfieldofview:main" rtkforwardprojections = "itk.rtkforwardprojections:main" rtki0estimation = "itk.rtki0estimation:main" rtkimagexgeometry = "itk.rtkimagxgeometry:main" +rtkiterativefdk = "itk.rtkiterativefdk:main" rtkmaskcollimation = "itk.rtkmaskcollimation:main" rtkgaincorrection = "itk.rtkgaincorrection:main" rtkorageometry = "itk.rtkorageometry:main" @@ -67,11 +68,14 @@ rtkosem = "itk.rtkosem:main" rtkprojectgeometricphantom = "itk.rtkprojectgeometricphantom:main" rtkprojections = "itk.rtkprojections:main" rtkprojectshepploganphantom= "itk.rtkprojectshepploganphantom:main" +rtkregularizedconjugategradient = "itk.rtkregularizedconjugategradient:main" +rtkscatterglarecorrection = "itk.rtkscatterglarecorrection:main" rtkshowgeometry = "itk.rtkshowgeometry:main" rtksart = "itk.rtksart:main" rtksimulatedgeometry = "itk.rtksimulatedgeometry:main" rtksubselect = "itk.rtksubselect:main" rtktotalvariationdenoising = "itk.rtktotalvariationdenoising:main" +rtktotalnuclearvariationdenoising = "itk.rtktotalnuclearvariationdenoising:main" rtkvarianobigeometry = "itk.rtkvarianobigeometry:main" rtkwaveletsdenoising = "itk.rtkwaveletsdenoising:main" rtkxradgeometry = "itk.rtkxradgeometry:main" diff --git a/wrapping/__init_rtk__.py b/wrapping/__init_rtk__.py index 0f1fbf24c..aab29d067 100644 --- a/wrapping/__init_rtk__.py +++ b/wrapping/__init_rtk__.py @@ -42,6 +42,7 @@ "rtkforwardprojections", "rtki0estimation", "rtkimagxgeometry", + "rtkiterativefdk", "rtkmaskcollimation", "rtkgaincorrection", "rtkorageometry", @@ -49,10 +50,13 @@ "rtkprojectgeometricphantom", "rtkprojections", "rtkprojectshepploganphantom", + "rtkregularizedconjugategradient", + "rtkscatterglarecorrection", "rtkshowgeometry", "rtksart", "rtksimulatedgeometry", "rtksubselect", + "rtktotalnuclearvariationdenoising", "rtktotalvariationdenoising", "rtkvarianobigeometry", "rtkwaveletsdenoising", diff --git a/wrapping/rtkDenoisingBPDQImageFilter.wrap b/wrapping/rtkDenoisingBPDQImageFilter.wrap index 2ca699689..1ec4197ce 100644 --- a/wrapping/rtkDenoisingBPDQImageFilter.wrap +++ b/wrapping/rtkDenoisingBPDQImageFilter.wrap @@ -10,6 +10,12 @@ itk_wrap_class("rtk::DenoisingBPDQImageFilter" POINTER) "itk::Image<${ITKT_${t}}, ${d}>, itk::Image<${ITKT_${cvt}${d}}, ${d}>") endforeach() endforeach() + # Explicit float instantiation with gradient length (d-1) needed by TotalNuclearVariationDenoisingBPDQ + if(d GREATER 1) + math(EXPR gradDim "${d}-1") + itk_wrap_template("IF${d}ICVF${gradDim}${d}" + "itk::Image, itk::Image, ${d}>") + endif() endforeach() if(RTK_USE_CUDA) # CUDA instantiation required by TV denoising (base class specialization) diff --git a/wrapping/rtkTotalNuclearVariationDenoisingBPDQImageFilter.wrap b/wrapping/rtkTotalNuclearVariationDenoisingBPDQImageFilter.wrap new file mode 100644 index 000000000..9feb66f44 --- /dev/null +++ b/wrapping/rtkTotalNuclearVariationDenoisingBPDQImageFilter.wrap @@ -0,0 +1,9 @@ +itk_wrap_class("rtk::TotalNuclearVariationDenoisingBPDQImageFilter" POINTER) + foreach(d ${ITK_WRAP_IMAGE_DIMS}) + if(d GREATER 1) + math(EXPR gradDim "${d}-1") + itk_wrap_template("I${ITKM_F}${d}ICVF${gradDim}${d}" + "itk::Image<${ITKT_F}, ${d}>, itk::Image, ${d}>") + endif() + endforeach() +itk_end_wrap_class()