Hi,
I am trying to reconstruct a CBCT raw data using RTK. I was able to execute the code and generate output, but unfortunately I am facing issues with the output generated. I don’t know if the issue is due to the geometry or any other parameters. I will write my code below and will also attach my xml file and output here. I humbly for request any advices and solutions. Thanks in advance.
#include <iostream>
#include <fstream>
#include <itkCudaImage.h>
#include <rtkConstantImageSource.h>
#include <rtkThreeDCircularProjectionGeometryXMLFileWriter.h>
#include <rtkCudaFDKConeBeamReconstructionFilter.h>
#include <rtkFieldOfViewImageFilter.h>
#include <itkImageFileReader.h>
#include <itkTimeProbe.h>
#include <rtkThreeDCircularProjectionGeometryXMLFileReader.h>
#include <itkCastImageFilter.h>
#include <itkMetaImageIO.h>
#include <cmath>
#include <cstdint>
#define M_PI 3.14159265358979323846
//// Function to convert degrees to radians
//double degreesToRadians(double degrees) {
//    return degrees * M_PI / 180.0;
//}
int main(int argc, char **argv)
{
//    if (argc < 4)
//        {
//            std::cout << "Usage: CbctReconstruction <inputmhdfile> <outputimage> <outputgeometry>" << std::endl;
//            return EXIT_FAILURE;
//        }
    // Create a time probe
    itk::TimeProbe clock;
    // Define the image type
    using ImageType = itk::CudaImage<uint16_t, 3>;
    // Define the RTK geometry object
    using GeometryType = rtk::ThreeDCircularProjectionGeometry;
    GeometryType::Pointer geometry = GeometryType::New();
    unsigned int numberOfProjections = 351; // Adjusted number of projections
    // System geometry parameters
    const double DSD = 1800.0; // Distance Source Detector (mm)
    const double DSO = 921.0; // Distance Source Origin (mm)
//   // const double detectorPixelPitch = 0.139; // Detector pixel pitch (mm)
//    double                firstAngle = 0;
//     double                angularArc = 351;
//     for (unsigned int noProj = 0; noProj < numberOfProjections; noProj++)
//      {
//        double angle = firstAngle + noProj * angularArc / numberOfProjections;
//    //    double angleRadians = degreesToRadians(angle); // Convert angle to radians
//                           geometry->AddProjection(DSO, DSD, angle);
       // geometry->AddProjection(DSO, DSD, angle);
 //     }
    // Assuming the file contains angles separated by newlines
    std::ifstream angleFile("D:/Navami/TIGRE_OUT_NEW/Data/CBCT/angles/angle.txt");
    if (!angleFile.is_open()) {
        std::cerr << "Error opening angle file." << std::endl;
        return EXIT_FAILURE;
    }
////////-----TO READ ANGLES IN RADIANS--------////////
    // Create the geometry
    for (unsigned int noProj = 1; noProj <= numberOfProjections; ++noProj)
    {
        double angle;
           angleFile >> angle;
       //   double angleRadians = degreesToRadians(angle); // Convert angle to radians
                   geometry->AddProjection(DSO, DSD, angle);
       // std::cout << "Angle " << noProj << ": " << angle << std::endl;
    }
    // Read the input image from MetaImage file
        using ReaderType = itk::ImageFileReader<ImageType>;
        ReaderType::Pointer reader = ReaderType::New();
        reader->SetFileName("D:/Navami/TIGRE_OUT_NEW/CBCT_Projections/combined_projections_meta.mhd");
        // Get the MetaDataDictionary from the ImageIO
        itk::MetaDataDictionary &metadata = reader->GetMetaDataDictionary();
        // Set detector row, column, and pixel pitch
        itk::EncapsulateMetaData<std::string>(metadata, "detector_row", "1536");
        itk::EncapsulateMetaData<std::string>(metadata, "detector_column", "1536");
        itk::EncapsulateMetaData<std::string>(metadata, "detector_pixel_pitch", "0.139");
        try {
            reader->Update();
        } catch (itk::ExceptionObject &excp) {
            std::cerr << "Exception thrown while reading the image file " << argv[1] << std::endl;
            std::cerr << excp << std::endl;
            return EXIT_FAILURE;
        }
            // Define the image type
               using InputImageType = itk::CudaImage<uint16_t, 3>;
               using OutputImageType = itk::CudaImage<float, 3>;
            // Cast the image type from uint16_t to float
                using CastFilterType = itk::CastImageFilter<InputImageType, OutputImageType>;
                CastFilterType::Pointer caster = CastFilterType::New();
                caster->SetInput(reader->GetOutput());
                caster->Update();
    // Write the geometry to disk
    rtk::ThreeDCircularProjectionGeometryXMLFileWriter::Pointer xmlWriter;
    xmlWriter = rtk::ThreeDCircularProjectionGeometryXMLFileWriter::New();
    xmlWriter->SetFilename("D:/Navami/TIGRE_OUT_NEW/CBCT_Out_Geo/CBCT_GeoOut_13.03.24/2.xml");
    xmlWriter->SetObject(geometry);
    xmlWriter->WriteFile();
    // Create a constant image source for reconstruction
    using ConstantImageSourceType = rtk::ConstantImageSource<ImageType>;
    ConstantImageSourceType::Pointer constantImageSource = ConstantImageSourceType::New();
    // Set image origin, spacing, and size based on provided parameters
    ImageType::PointType origin;
    origin.Fill(0.5 * (512 - 1) * -0.5);
    constantImageSource->SetOrigin(origin);
    ImageType::SpacingType spacing;
    spacing.Fill(0.5); // Adjusted spacing between slices
    constantImageSource->SetSpacing(spacing);
    ImageType::SizeType sizeOutput;
    sizeOutput[0] = 512; // Image Columns
    sizeOutput[1] = 512; // Image Rows
    sizeOutput[2] = 512; // Slices
    constantImageSource->SetSize(sizeOutput);
    // Set the constant value for the image source
    constantImageSource->SetConstant(0.0);
    constantImageSource->Update();
    // Perform casting from unsigned 16-bit integer to float
    using CastFilterType = itk::CastImageFilter<InputImageType, OutputImageType>;
    CastFilterType::Pointer castconstantImageSource = CastFilterType::New();
    castconstantImageSource->SetInput(constantImageSource->GetOutput());
    castconstantImageSource->Update();
    // Perform the reconstruction
    std::cout << "Reconstructing..." << std::endl;
    clock.Start();
    // FDK reconstruction
    //  std::cout << "Reconstructing..." << std::endl;
      using FDKGPUType = rtk::CudaFDKConeBeamReconstructionFilter;
      FDKGPUType::Pointer feldkamp = FDKGPUType::New();
      feldkamp->SetInput(0, castconstantImageSource->GetOutput());
      feldkamp->SetInput(1, caster->GetOutput());
      feldkamp->SetGeometry(geometry);
      feldkamp->GetRampFilter()->SetTruncationCorrection(0.);
      feldkamp->GetRampFilter()->SetHannCutFrequency(0.0);
    clock.Stop();
    std::cout << "Time taken for reconstruction: " << clock.GetTotal() << " seconds." << std::endl;
     using OutputImageTypeUnsignedShort = itk::CudaImage<uint16_t, 3>;
    // Cast the output of the FDK reconstruction filter to unsigned short
       using CastFilterTypeUnsignedShort = itk::CastImageFilter<OutputImageType, OutputImageTypeUnsignedShort>;
       CastFilterTypeUnsignedShort::Pointer casterUnsignedShort = CastFilterTypeUnsignedShort::New();
       casterUnsignedShort->SetInput(feldkamp->GetOutput());
       casterUnsignedShort->Update();
       // Field-of-view masking
       using FOVFilterType = rtk::FieldOfViewImageFilter<ImageType, ImageType>;
       FOVFilterType::Pointer fieldofview = FOVFilterType::New();
       fieldofview->SetInput(0, casterUnsignedShort->GetOutput());
       fieldofview->SetProjectionsStack(reader->GetOutput());
       fieldofview->SetGeometry(geometry);
       fieldofview->Update();
    // Writer
    std::cout << "Writing output image..." << std::endl;
    using WriterType = itk::ImageFileWriter<ImageType>;
    WriterType::Pointer writer = WriterType::New();
    writer->SetFileName("D:/Navami/TIGRE_OUT_NEW/CBCT_Out_Geo/CBCT_GeoOut_13.03.24/4.mhd");
    writer->SetInput(casterUnsignedShort->GetOutput());
    writer->Update();
    std::cout << "Reconstruction done." << std::endl;
    return EXIT_SUCCESS;
}
2.mhd (323 Bytes)
2.xml (124.0 KB)
combined_projections_meta.mhd (278 Bytes)



