From ca4a1426e84f47e8d25c400388491a57a59589f6 Mon Sep 17 00:00:00 2001 From: Axel Garcia Date: Tue, 8 Sep 2026 13:15:29 +0200 Subject: [PATCH 1/2] BUG: Fix black frames with --cudacg and spatial TV Fixes #888. The total variation denoise sequence filter reused the paste output as the destination for every frame following the first. On the ROOSTER main loop iterations, that accumulator could alias the fed-back CG volume through the CUDA buffer pool, so downstream spatial TV zeroed every frame but the first. Accumulate all frames in place into a single, freshly allocated destination buffer, which can never alias an upstream buffer. --- ...otalVariationDenoiseSequenceImageFilter.hxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/rtkTotalVariationDenoiseSequenceImageFilter.hxx b/include/rtkTotalVariationDenoiseSequenceImageFilter.hxx index b005d7b59..809dbea81 100644 --- a/include/rtkTotalVariationDenoiseSequenceImageFilter.hxx +++ b/include/rtkTotalVariationDenoiseSequenceImageFilter.hxx @@ -124,18 +124,18 @@ TotalVariationDenoiseSequenceImageFilter::GenerateData() { int Dimension = TImageSequence::ImageDimension; - // Declare an image pointer to disconnect the output of paste - typename TImageSequence::Pointer pimg; + // Accumulate all frames in place into a single, freshly allocated buffer so + // that the accumulator never aliases an upstream buffer. + typename TImageSequence::Pointer pimg = TImageSequence::New(); + pimg->CopyInformation(this->GetOutput()); + pimg->SetRegions(this->GetOutput()->GetLargestPossibleRegion()); + pimg->Allocate(); + pimg->FillBuffer(0); + m_PasteFilter->SetInPlace(true); + m_PasteFilter->SetDestinationImage(pimg); for (unsigned int frame = 0; frame < this->GetInput(0)->GetLargestPossibleRegion().GetSize(Dimension - 1); frame++) { - if (frame > 0) // After the first frame, use the output of paste as input - { - pimg = m_PasteFilter->GetOutput(); - pimg->DisconnectPipeline(); - m_PasteFilter->SetDestinationImage(pimg); - } - m_ExtractAndPasteRegion.SetIndex(Dimension - 1, frame); m_ExtractFilter->SetExtractionRegion(m_ExtractAndPasteRegion); From 35b572f734dd5a4ff7d6c12f10c6faff79dba353 Mon Sep 17 00:00:00 2001 From: Axel Garcia Date: Tue, 8 Sep 2026 13:15:29 +0200 Subject: [PATCH 2/2] TEST: Add a new test in rtkfourdroostertest The test check for black frames with --cudacg and spatial TV --- test/rtkfourdroostertest.cxx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/rtkfourdroostertest.cxx b/test/rtkfourdroostertest.cxx index e3070553c..77fe93550 100644 --- a/test/rtkfourdroostertest.cxx +++ b/test/rtkfourdroostertest.cxx @@ -401,6 +401,31 @@ rtkfourdroostertest(int, char *[]) CheckImageQuality(rooster->GetOutput(), join->GetOutput(), 0.25, 15, 2.0); std::cout << "\n\nTest PASSED! " << std::endl; + + std::cout << "\n\n****** Case 5: CUDA forward and back projectors, CUDA conjugate gradient, " + "spatial TV denoising (regression test for black frames) ******" + << std::endl; + + rooster->SetBackProjectionFilter(ROOSTERFilterType::BP_CUDAVOXELBASED); // Cuda voxel based + rooster->SetForwardProjectionFilter(ROOSTERFilterType::FP_CUDARAYCAST); // Cuda ray cast + + rooster->SetPerformPositivity(false); + rooster->SetPerformMotionMask(false); + rooster->SetPerformTVSpatialDenoising(true); + rooster->SetPerformWaveletsSpatialDenoising(false); + rooster->SetPerformTVTemporalDenoising(false); + rooster->SetPerformL0TemporalDenoising(false); + rooster->SetPerformWarping(false); + rooster->SetComputeInverseWarpingByConjugateGradient(false); + rooster->SetUseNearestNeighborInterpolationInWarping(false); + + // With --cudacg and spatial TV, every frame but the first used to come out + // black (all zeros). The spatial TV produces a slightly different result than + // other regularization modes, so use a looser tolerance here. + rooster->SetCudaConjugateGradient(true); + TRY_AND_EXIT_ON_ITK_EXCEPTION(rooster->Update()); + CheckImageQuality(rooster->GetOutput(), join->GetOutput(), 0.3, 14, 2.0); + std::cout << "\n\nTest PASSED! " << std::endl; #endif itksys::SystemTools::RemoveFile(signalFileName);