Initial commit.
Digitally Reconstructed Topogram library and simple test application.
This commit is contained in:
40
reg23Topograms/itkDTRrecon/CMakeLists.txt
Normal file
40
reg23Topograms/itkDTRrecon/CMakeLists.txt
Normal file
@ -0,0 +1,40 @@
|
||||
cmake_minimum_required(VERSION 2.8.8)
|
||||
|
||||
SET(LIB_NAME itkDTRrecon)
|
||||
|
||||
find_package(ITK REQUIRED)
|
||||
include(${ITK_USE_FILE})
|
||||
INCLUDE_DIRECTORIES(${ITK_INCLUDE_DIRS})
|
||||
|
||||
find_package(VTK REQUIRED)
|
||||
INCLUDE_DIRECTORIES(${VTK_INCLUDE_DIRS})
|
||||
|
||||
FIND_PACKAGE(GDCM REQUIRED)
|
||||
include(${GDCM_USE_FILE})
|
||||
INCLUDE_DIRECTORIES(${GDCM_INCLUDE_DIRS})
|
||||
|
||||
SET(SRCS
|
||||
itkImageProcessor.cpp
|
||||
)
|
||||
|
||||
SET(HDR
|
||||
itkImageProcessor.h itkgSiddonJacobsRayCastInterpolateImageFunction.h itkgSiddonJacobsRayCastInterpolateImageFunction.hxx
|
||||
)
|
||||
|
||||
ADD_LIBRARY(${LIB_NAME} ${SRCS} ${HDR})
|
||||
|
||||
SET(LINK_LIBS
|
||||
${LIB_NAME}
|
||||
${VTK_LIBRARIES}
|
||||
${ITK_LIBRARIES}
|
||||
${GDCM_LIBRARIES}
|
||||
)
|
||||
|
||||
TARGET_LINK_LIBRARIES(
|
||||
${LINK_LIBS}
|
||||
)
|
||||
|
||||
|
||||
target_include_directories (${LIB_NAME}
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
1933
reg23Topograms/itkDTRrecon/itkImageProcessor.cpp
Normal file
1933
reg23Topograms/itkDTRrecon/itkImageProcessor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
273
reg23Topograms/itkDTRrecon/itkImageProcessor.h
Normal file
273
reg23Topograms/itkDTRrecon/itkImageProcessor.h
Normal file
@ -0,0 +1,273 @@
|
||||
|
||||
/*
|
||||
|
||||
Calculate Ditigally Reconstructed Topogram from a CT dataset using a modified
|
||||
version of incremental ray-tracing algorithm
|
||||
|
||||
gfattori 08.11.2021
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ITKIMAGEPROCESSOR_H
|
||||
#define ITKIMAGEPROCESSOR_H
|
||||
|
||||
|
||||
#include "itkCommand.h"
|
||||
|
||||
#include "itkPowellOptimizer.h"
|
||||
#include "itkgSiddonJacobsRayCastInterpolateImageFunction.h"
|
||||
#include "itkEuler3DTransform.h"
|
||||
#include "itkTwoProjectionImageRegistrationMethod.h"
|
||||
#include "itkResampleImageFilter.h"
|
||||
#include "itkCastImageFilter.h"
|
||||
#include "itkRescaleIntensityImageFilter.h"
|
||||
#include "itkFlipImageFilter.h"
|
||||
#include "itkChangeInformationImageFilter.h"
|
||||
#include "itkTimeProbesCollectorBase.h"
|
||||
#include "itkNormalizedCorrelationTwoImageToOneImageMetric.h"
|
||||
#include "itkGDCMImageIO.h"
|
||||
#include "itkMetaDataObject.h"
|
||||
|
||||
#include "gdcmGlobal.h"
|
||||
|
||||
#include "itkImageToVTKImageFilter.h"
|
||||
|
||||
#include "itkImageFileReader.h"
|
||||
#include "itkImageFileWriter.h"
|
||||
#include "itkImageSeriesReader.h"
|
||||
|
||||
|
||||
namespace itk
|
||||
{
|
||||
|
||||
|
||||
class CommandIterationUpdate : public itk::Command
|
||||
{
|
||||
public:
|
||||
using Self = CommandIterationUpdate;
|
||||
using Superclass = itk::Command;
|
||||
using Pointer = itk::SmartPointer<Self>;
|
||||
itkNewMacro(Self);
|
||||
|
||||
protected:
|
||||
CommandIterationUpdate() = default;
|
||||
|
||||
public:
|
||||
using OptimizerType = itk::PowellOptimizer;
|
||||
using OptimizerPointer = const OptimizerType *;
|
||||
|
||||
void
|
||||
Execute(itk::Object * caller, const itk::EventObject & event) override
|
||||
{
|
||||
Execute((const itk::Object *)caller, event);
|
||||
}
|
||||
|
||||
void
|
||||
Execute(const itk::Object * object, const itk::EventObject & event) override
|
||||
{
|
||||
auto optimizer = dynamic_cast<OptimizerPointer>(object);
|
||||
if (typeid(event) != typeid(itk::IterationEvent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::cout << "Iteration: " << optimizer->GetCurrentIteration() << std::endl;
|
||||
std::cout << "Similarity: " << optimizer->GetValue() << std::endl;
|
||||
std::cout << "Position: " << optimizer->GetCurrentPosition() << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/* reference string required for comparison with tag values */
|
||||
static const char *ImageOrientationStrings[] = {
|
||||
"HFS",
|
||||
"FFS"
|
||||
};
|
||||
|
||||
class ITK_EXPORT itkImageProcessor : public Object
|
||||
{
|
||||
|
||||
constexpr static unsigned int Dimension = 3;
|
||||
|
||||
public:
|
||||
/** Standard "Self" typedef. */
|
||||
typedef itkImageProcessor Self;
|
||||
/** Standard "Superclass" typedef. */
|
||||
typedef Object Superclass;
|
||||
/** Smart pointer typedef support */
|
||||
typedef SmartPointer<Self> Pointer;
|
||||
typedef SmartPointer<const Self> ConstPointer;
|
||||
/** Method of creation through the object factory. */
|
||||
itkNewMacro(Self);
|
||||
/** Run-time type information (and related methods). */
|
||||
itkTypeMacro(itkImageProcessor, Object);
|
||||
|
||||
/** Input data load methods*/
|
||||
void load3DVolume(const char *);
|
||||
int load3DSerie(const char* );
|
||||
int load2D(const char *);
|
||||
|
||||
/** Projection angles - Gantry angle */
|
||||
void SetProjectionAngle1(double);
|
||||
void SetProjectionAngle2(double);
|
||||
|
||||
/** Source to axis distance - SAD */
|
||||
void SetSCD(double);
|
||||
|
||||
/** Intensity threshold used for image projection,
|
||||
* any voxel below threshold is ignored */
|
||||
void SetIntensityThreshold(double);
|
||||
|
||||
/** Custom settings of the projection images */
|
||||
void SetCustom_Isocenter(double,double,double);
|
||||
void SetCustom_2Dres(double,double,double,double);
|
||||
void SetCustom_2Dsize(int,int,int,int);
|
||||
void SetCustom_2Dcenter(double dX1, double dY1, double dX2, double dY2);
|
||||
|
||||
/** Set transform parameters for 3D Volume */
|
||||
void SetInitialTranslations(double, double, double);
|
||||
void SetInitialRotations(double, double, double);
|
||||
|
||||
/** Get the Localizer intensity window and center for rendering */
|
||||
double* GetImageIntensityWindow(int );
|
||||
|
||||
/** 2D-3D registration routines */
|
||||
// int InitializeRegistration();
|
||||
void InitializeProjector();
|
||||
// void RunRegistration();
|
||||
|
||||
/** Compute Digital Localizers */
|
||||
void GetProjectionImages();
|
||||
|
||||
/** Conveniency methods to get VTK images for rendering */
|
||||
vtkImageData* GetProjection1VTK();
|
||||
vtkImageData* GetProjection2VTK();
|
||||
|
||||
vtkImageData* GetLocalizer1VTK();
|
||||
vtkImageData* GetLocalizer2VTK();
|
||||
|
||||
/** Debug writers */
|
||||
void WriteProjectionImages();
|
||||
void Write2DImages();
|
||||
|
||||
protected:
|
||||
itkImageProcessor();
|
||||
virtual ~itkImageProcessor();
|
||||
void PrintSelf(std::ostream& os, Indent indent) const;
|
||||
|
||||
|
||||
private:
|
||||
itkImageProcessor(const Self&); //purposely not implemented
|
||||
void operator=(const Self&); //purposely not implemented
|
||||
|
||||
|
||||
void Finalize();
|
||||
|
||||
/** Various pixel types */
|
||||
using InternalPixelType = float;
|
||||
using PixelType2D = short;
|
||||
using PixelType3D = short;
|
||||
using OutputPixelType = unsigned char;
|
||||
|
||||
/** Image types */
|
||||
using ImageType2D = itk::Image<PixelType3D, Dimension>;
|
||||
using ImageType3D = itk::Image<PixelType3D, Dimension>;
|
||||
using OutputImageType = itk::Image<OutputPixelType, Dimension>;
|
||||
|
||||
/** The following lines define each of the components used in the
|
||||
registration: The transform, optimizer, metric, interpolator and
|
||||
the registration method itself. */
|
||||
using InternalImageType = itk::Image<InternalPixelType, Dimension>;
|
||||
using TransformType = itk::Euler3DTransform<double>;
|
||||
using OptimizerType = itk::PowellOptimizer;
|
||||
using MetricType = itk::NormalizedCorrelationTwoImageToOneImageMetric<InternalImageType, InternalImageType>;
|
||||
using InterpolatorType = itk::gSiddonJacobsRayCastInterpolateImageFunction<InternalImageType, double>;
|
||||
using RegistrationType = itk::TwoProjectionImageRegistrationMethod<InternalImageType, InternalImageType>;
|
||||
using ParametersType = RegistrationType::ParametersType;
|
||||
|
||||
/** Image reader types */
|
||||
using ImageReaderType2D = itk::ImageFileReader<ImageType2D>;
|
||||
using ImageReaderType3D = itk::ImageFileReader<ImageType3D>;
|
||||
using ImageSeriesReaderType3D = itk::ImageSeriesReader<ImageType3D>;
|
||||
using ImageIOType = itk::GDCMImageIO;
|
||||
|
||||
/** widely used filters: flip, cast, rescale, ... */
|
||||
using FlipFilterType = itk::FlipImageFilter<InternalImageType>;
|
||||
using Input2DRescaleFilterType = itk::RescaleIntensityImageFilter<InternalImageType, InternalImageType>;
|
||||
using CastFilterType3D = itk::CastImageFilter<ImageType3D, InternalImageType>;
|
||||
using ChangeInformationFilterType = itk::ChangeInformationImageFilter<InternalImageType>;
|
||||
using ChangeInformationFilterInputType = itk::ChangeInformationImageFilter<ImageType3D>;
|
||||
|
||||
/** ITK to VTK filter */
|
||||
using ITKtoVTKFilterType = itk::ImageToVTKImageFilter<OutputImageType>;
|
||||
|
||||
/** Enum type for Orientation */
|
||||
enum eImageOrientationType { HFS = 0, FFS = 1};
|
||||
|
||||
|
||||
MetricType::Pointer metric;
|
||||
TransformType::Pointer transform;
|
||||
OptimizerType::Pointer optimizer;
|
||||
InterpolatorType::Pointer interpolator1;
|
||||
InterpolatorType::Pointer interpolator2;
|
||||
RegistrationType::Pointer registration;
|
||||
|
||||
ImageReaderType2D::Pointer imageReader2D;
|
||||
ImageReaderType3D::Pointer imageReader3D;
|
||||
ImageSeriesReaderType3D::Pointer imageSeriesReader3D;
|
||||
|
||||
CommandIterationUpdate::Pointer observer;
|
||||
|
||||
FlipFilterType::Pointer flipFilter1;
|
||||
FlipFilterType::Pointer flipFilter2;
|
||||
FlipFilterType::Pointer flipFilterLocalizer1;
|
||||
FlipFilterType::Pointer flipFilterLocalizer2;
|
||||
|
||||
CastFilterType3D::Pointer caster3D;
|
||||
CastFilterType3D::Pointer caster2D1;
|
||||
CastFilterType3D::Pointer caster2D2;
|
||||
CastFilterType3D::Pointer caster2DInput;
|
||||
|
||||
TransformType::InputPointType isocenter3D;
|
||||
|
||||
ITKtoVTKFilterType::Pointer toVTK2D1;
|
||||
ITKtoVTKFilterType::Pointer toVTK2D2;
|
||||
ITKtoVTKFilterType::Pointer toVTKLocalizer1;
|
||||
ITKtoVTKFilterType::Pointer toVTKLocalizer2;
|
||||
|
||||
ImageType3D::Pointer image3DIn;
|
||||
InternalImageType::Pointer image2D1In;
|
||||
InternalImageType::Pointer image2D2In;
|
||||
|
||||
Input2DRescaleFilterType::Pointer image2D1Rescaler;
|
||||
Input2DRescaleFilterType::Pointer image2D2Rescaler;
|
||||
|
||||
InternalImageType::Pointer TMPImg1;
|
||||
InternalImageType::Pointer TMPImg2;
|
||||
|
||||
|
||||
ChangeInformationFilterInputType::Pointer image3DIECFilt;
|
||||
|
||||
double image1res[2], image2res[2];
|
||||
double image1center[2], image2center[2];
|
||||
int image1Size[2], image2Size[2];
|
||||
double isocenter[3];
|
||||
double scd;
|
||||
double threshold;
|
||||
double projAngle1, projAngle2;
|
||||
|
||||
double TZero[3], RZero[3];
|
||||
double image3DCOV[3];
|
||||
|
||||
bool customized_iso,customized_2DCX,customized_2DRES, customized_2DSIZE;
|
||||
bool image2D1Loaded, image2D2Loaded, image3DLoaded;
|
||||
|
||||
double image1IntensityWindow[2], image2IntensityWindow[2];
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -0,0 +1,248 @@
|
||||
|
||||
/*
|
||||
|
||||
Calculate Ditigally Reconstructed Topogram from a CT dataset using a modified
|
||||
version of incremental ray-tracing algorithm
|
||||
|
||||
gfattori 08.11.2021
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
*
|
||||
* Copyright NumFOCUS
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
*=========================================================================*/
|
||||
/*=========================================================================
|
||||
Calculate DRR from a CT dataset using incremental ray-tracing algorithm
|
||||
The algorithm was initially proposed by Robert Siddon and improved by
|
||||
Filip Jacobs etc.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
References:
|
||||
|
||||
R. L. Siddon, "Fast calculation of the exact radiological path for a
|
||||
threedimensional CT array," Medical Physics 12, 252-55 (1985).
|
||||
|
||||
F. Jacobs, E. Sundermann, B. De Sutter, M. Christiaens, and I. Lemahieu,
|
||||
"A fast algorithm to calculate the exact radiological path through a pixel
|
||||
or voxel space," Journal of Computing and Information Technology ?
|
||||
CIT 6, 89-94 (1998).
|
||||
|
||||
=========================================================================*/
|
||||
#ifndef itkgSiddonJacobsRayCastInterpolateImageFunction_h
|
||||
#define itkgSiddonJacobsRayCastInterpolateImageFunction_h
|
||||
|
||||
#include "itkInterpolateImageFunction.h"
|
||||
#include "itkTransform.h"
|
||||
#include "itkVector.h"
|
||||
#include "itkEuler3DTransform.h"
|
||||
|
||||
namespace itk
|
||||
{
|
||||
|
||||
/** \class gSiddonJacobsRayCastInterpolateImageFunction
|
||||
* \brief Projective interpolation of an image at specified positions.
|
||||
*
|
||||
* SiddonJacobsRayCastInterpolateImageFunction casts rays through a 3-dimensional
|
||||
* image
|
||||
* \warning This interpolator works for 3-dimensional images only.
|
||||
*
|
||||
* \ingroup ImageFunctions
|
||||
* \ingroup TwoProjectionRegistration
|
||||
*/
|
||||
template <typename TInputImage, typename TCoordRep = float>
|
||||
class gSiddonJacobsRayCastInterpolateImageFunction : public InterpolateImageFunction<TInputImage, TCoordRep>
|
||||
{
|
||||
public:
|
||||
ITK_DISALLOW_COPY_AND_ASSIGN(gSiddonJacobsRayCastInterpolateImageFunction);
|
||||
|
||||
/** Standard class type alias. */
|
||||
using Self = gSiddonJacobsRayCastInterpolateImageFunction;
|
||||
using Superclass = InterpolateImageFunction<TInputImage, TCoordRep>;
|
||||
using Pointer = SmartPointer<Self>;
|
||||
using ConstPointer = SmartPointer<const Self>;
|
||||
|
||||
/** Constants for the image dimensions */
|
||||
static constexpr unsigned int InputImageDimension = TInputImage::ImageDimension;
|
||||
|
||||
|
||||
using TransformType = Euler3DTransform<TCoordRep>;
|
||||
|
||||
using TransformPointer = typename TransformType::Pointer;
|
||||
using InputPointType = typename TransformType::InputPointType;
|
||||
using OutputPointType = typename TransformType::OutputPointType;
|
||||
using TransformParametersType = typename TransformType::ParametersType;
|
||||
using TransformJacobianType = typename TransformType::JacobianType;
|
||||
|
||||
using PixelType = typename Superclass::InputPixelType;
|
||||
|
||||
using SizeType = typename TInputImage::SizeType;
|
||||
|
||||
using DirectionType = Vector<TCoordRep, 3>;
|
||||
|
||||
/** Type of the Interpolator Base class */
|
||||
using InterpolatorType = InterpolateImageFunction<TInputImage, TCoordRep>;
|
||||
|
||||
using InterpolatorPointer = typename InterpolatorType::Pointer;
|
||||
|
||||
|
||||
/** Run-time type information (and related methods). */
|
||||
itkTypeMacro(gSiddonJacobsRayCastInterpolateImageFunction, InterpolateImageFunction);
|
||||
|
||||
/** Method for creation through the object factory. */
|
||||
itkNewMacro(Self);
|
||||
|
||||
/** OutputType type alias support. */
|
||||
using OutputType = typename Superclass::OutputType;
|
||||
|
||||
/** InputImageType type alias support. */
|
||||
using InputImageType = typename Superclass::InputImageType;
|
||||
|
||||
/** InputImageConstPointer type alias support. */
|
||||
using InputImageConstPointer = typename Superclass::InputImageConstPointer;
|
||||
|
||||
/** RealType type alias support. */
|
||||
using RealType = typename Superclass::RealType;
|
||||
|
||||
/** Dimension underlying input image. */
|
||||
static constexpr unsigned int ImageDimension = Superclass::ImageDimension;
|
||||
|
||||
/** Point type alias support. */
|
||||
using PointType = typename Superclass::PointType;
|
||||
|
||||
/** Index type alias support. */
|
||||
using IndexType = typename Superclass::IndexType;
|
||||
|
||||
/** ContinuousIndex type alias support. */
|
||||
using ContinuousIndexType = typename Superclass::ContinuousIndexType;
|
||||
|
||||
|
||||
/** \brief
|
||||
* Interpolate the image at a point position.
|
||||
*
|
||||
* Returns the interpolated image intensity at a
|
||||
* specified point position. No bounds checking is done.
|
||||
* The point is assume to lie within the image buffer.
|
||||
*
|
||||
* ImageFunction::IsInsideBuffer() can be used to check bounds before
|
||||
* calling the method.
|
||||
*/
|
||||
OutputType
|
||||
Evaluate(const PointType & point) const override;
|
||||
|
||||
/** Interpolate the image at a continuous index position
|
||||
*
|
||||
* Returns the interpolated image intensity at a
|
||||
* specified index position. No bounds checking is done.
|
||||
* The point is assume to lie within the image buffer.
|
||||
*
|
||||
* Subclasses must override this method.
|
||||
*
|
||||
* ImageFunction::IsInsideBuffer() can be used to check bounds before
|
||||
* calling the method.
|
||||
*/
|
||||
OutputType
|
||||
EvaluateAtContinuousIndex(const ContinuousIndexType & index) const override;
|
||||
|
||||
virtual void
|
||||
Initialize();
|
||||
|
||||
/** Connect the Transform. */
|
||||
itkSetObjectMacro(Transform, TransformType);
|
||||
/** Get a pointer to the Transform. */
|
||||
itkGetConstObjectMacro(Transform, TransformType);
|
||||
|
||||
/** Set and get the focal point to isocenter distance in mm */
|
||||
itkSetMacro(FocalPointToIsocenterDistance, double);
|
||||
itkGetMacro(FocalPointToIsocenterDistance, double);
|
||||
|
||||
/** Set and get the Lianc grantry rotation angle in radians */
|
||||
itkSetMacro(ProjectionAngle, double);
|
||||
itkGetMacro(ProjectionAngle, double);
|
||||
|
||||
/** Set and get the Threshold */
|
||||
itkSetMacro(Threshold, double);
|
||||
itkGetMacro(Threshold, double);
|
||||
|
||||
/** Check if a point is inside the image buffer.
|
||||
* \warning For efficiency, no validity checking of
|
||||
* the input image pointer is done. */
|
||||
inline bool
|
||||
IsInsideBuffer(const PointType &) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
IsInsideBuffer(const ContinuousIndexType &) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool
|
||||
IsInsideBuffer(const IndexType &) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !defined(ITKV4_COMPATIBILITY)
|
||||
SizeType
|
||||
GetRadius() const override
|
||||
{
|
||||
const InputImageType * input = this->GetInputImage();
|
||||
if (!input)
|
||||
{
|
||||
itkExceptionMacro("Input image required!");
|
||||
}
|
||||
return input->GetLargestPossibleRegion().GetSize();
|
||||
}
|
||||
#endif
|
||||
|
||||
protected:
|
||||
gSiddonJacobsRayCastInterpolateImageFunction();
|
||||
|
||||
~gSiddonJacobsRayCastInterpolateImageFunction() override = default;
|
||||
|
||||
void
|
||||
PrintSelf(std::ostream & os, Indent indent) const override;
|
||||
|
||||
/// Transformation used to calculate the new focal point position
|
||||
TransformPointer m_Transform; // Displacement of the volume
|
||||
// Overall inverse transform used to calculate the ray position in the input space
|
||||
TransformPointer m_InverseTransform;
|
||||
|
||||
// The threshold above which voxels along the ray path are integrated
|
||||
double m_Threshold;
|
||||
double m_FocalPointToIsocenterDistance; // Focal point to isocenter distance
|
||||
double m_ProjectionAngle; // Linac gantry rotation angle in radians
|
||||
|
||||
private:
|
||||
void
|
||||
ComputeInverseTransform() const;
|
||||
TransformPointer m_GantryRotTransform; // Gantry rotation transform
|
||||
TransformPointer m_CamShiftTransform; // Camera shift transform camRotTransform
|
||||
TransformPointer m_CamRotTransform; // Camera rotation transform
|
||||
TransformPointer m_ComposedTransform; // Composed transform
|
||||
PointType m_SourcePoint; // Coordinate of the source in the standard Z projection geometry
|
||||
PointType m_SourceWorld; // Coordinate of the source in the world coordinate system
|
||||
};
|
||||
|
||||
} // namespace itk
|
||||
|
||||
#ifndef ITK_MANUAL_INSTANTIATION
|
||||
# include "itkgSiddonJacobsRayCastInterpolateImageFunction.hxx"
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,488 @@
|
||||
|
||||
/*
|
||||
|
||||
Calculate Ditigally Reconstructed Topogram from a CT dataset using a modified
|
||||
version of incremental ray-tracing algorithm
|
||||
|
||||
gfattori 08.11.2021
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*=========================================================================
|
||||
*
|
||||
* Copyright NumFOCUS
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* http://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.
|
||||
*
|
||||
*=========================================================================*/
|
||||
/*=========================================================================
|
||||
The algorithm was initially proposed by Robert Siddon and improved by
|
||||
Filip Jacobs etc.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
References:
|
||||
|
||||
R. L. Siddon, "Fast calculation of the exact radiological path for a
|
||||
threedimensional CT array," Medical Physics 12, 252-55 (1985).
|
||||
|
||||
F. Jacobs, E. Sundermann, B. De Sutter, M. Christiaens, and I. Lemahieu,
|
||||
"A fast algorithm to calculate the exact radiological path through a pixel
|
||||
or voxel space," Journal of Computing and Information Technology ?
|
||||
CIT 6, 89-94 (1998).
|
||||
|
||||
=========================================================================*/
|
||||
|
||||
#ifndef itkgSiddonJacobsRayCastInterpolateImageFunction_hxx
|
||||
#define itkgSiddonJacobsRayCastInterpolateImageFunction_hxx
|
||||
|
||||
#include "itkgSiddonJacobsRayCastInterpolateImageFunction.h"
|
||||
|
||||
#include "itkMath.h"
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace itk
|
||||
{
|
||||
|
||||
template <typename TInputImage, typename TCoordRep>
|
||||
gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::gSiddonJacobsRayCastInterpolateImageFunction()
|
||||
{
|
||||
m_FocalPointToIsocenterDistance = 1000.; // Focal point to isocenter distance in mm.
|
||||
m_ProjectionAngle = 0.; // Angle in radians betweeen projection central axis and reference axis
|
||||
m_Threshold = 0.; // Intensity threshold, below which is ignored.
|
||||
|
||||
m_SourcePoint[0] = 0.;
|
||||
m_SourcePoint[1] = 0.;
|
||||
m_SourcePoint[2] = 0.;
|
||||
|
||||
m_InverseTransform = TransformType::New();
|
||||
m_InverseTransform->SetComputeZYX(true);
|
||||
|
||||
m_ComposedTransform = TransformType::New();
|
||||
m_ComposedTransform->SetComputeZYX(true);
|
||||
|
||||
m_GantryRotTransform = TransformType::New();
|
||||
m_GantryRotTransform->SetComputeZYX(true);
|
||||
m_GantryRotTransform->SetIdentity();
|
||||
|
||||
m_CamShiftTransform = TransformType::New();
|
||||
m_CamShiftTransform->SetComputeZYX(true);
|
||||
m_CamShiftTransform->SetIdentity();
|
||||
|
||||
m_CamRotTransform = TransformType::New();
|
||||
m_CamRotTransform->SetComputeZYX(true);
|
||||
m_CamRotTransform->SetIdentity();
|
||||
// constant for converting degrees into radians
|
||||
const float dtr = (atan(1.0) * 4.0) / 180.0;
|
||||
m_CamRotTransform->SetRotation(dtr * (-90.0), 0.0, 0.0);
|
||||
|
||||
m_Threshold = 0;
|
||||
}
|
||||
|
||||
|
||||
template <typename TInputImage, typename TCoordRep>
|
||||
void
|
||||
gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::PrintSelf(std::ostream & os, Indent indent) const
|
||||
{
|
||||
this->Superclass::PrintSelf(os, indent);
|
||||
|
||||
os << indent << "Threshold: " << m_Threshold << std::endl;
|
||||
os << indent << "Transform: " << m_Transform.GetPointer() << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template <typename TInputImage, typename TCoordRep>
|
||||
typename gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::OutputType
|
||||
gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::Evaluate(const PointType & point) const
|
||||
{
|
||||
float rayVector[3];
|
||||
IndexType cIndex;
|
||||
|
||||
PointType drrPixelWorld; // Coordinate of a DRR pixel in the world coordinate system
|
||||
OutputType pixval;
|
||||
|
||||
|
||||
float firstIntersection[3];
|
||||
float alphaX1, alphaXN, alphaXmin, alphaXmax;
|
||||
float alphaY1, alphaYN, alphaYmin, alphaYmax;
|
||||
float alphaZ1, alphaZN, alphaZmin, alphaZmax;
|
||||
float alphaMin, alphaMax;
|
||||
float alphaX, alphaY, alphaZ, alphaCmin, alphaCminPrev;
|
||||
float alphaUx, alphaUy, alphaUz;
|
||||
float alphaIntersectionUp[3], alphaIntersectionDown[3];
|
||||
float d12, value;
|
||||
float firstIntersectionIndex[3];
|
||||
int firstIntersectionIndexUp[3], firstIntersectionIndexDown[3];
|
||||
int iU, jU, kU;
|
||||
|
||||
|
||||
// Min/max values of the output pixel type AND these values
|
||||
// represented as the output type of the interpolator
|
||||
const OutputType minOutputValue = itk::NumericTraits<OutputType>::NonpositiveMin();
|
||||
const OutputType maxOutputValue = itk::NumericTraits<OutputType>::max();
|
||||
|
||||
// If the volume was shifted, recalculate the overall inverse transform
|
||||
unsigned long int interpMTime = this->GetMTime();
|
||||
unsigned long int vTransformMTime = m_Transform->GetMTime();
|
||||
|
||||
if (interpMTime < vTransformMTime)
|
||||
{
|
||||
this->ComputeInverseTransform();
|
||||
// The m_SourceWorld should be computed here to avoid the repeatedly calculation
|
||||
// for each projection ray. However, we are in a const function, which prohibits
|
||||
// the modification of class member variables. So the world coordiate of the source
|
||||
// point is calculated for each ray as below. Performance improvement may be made
|
||||
// by using a static variable?
|
||||
// m_SourceWorld = m_InverseTransform->TransformPoint(m_SourcePoint);
|
||||
}
|
||||
|
||||
//PointType m_SourcePointSliding = m_SourcePoint;
|
||||
//m_SourcePointSliding[2] = point[2];
|
||||
PointType SourceWorld = m_InverseTransform->TransformPoint(m_SourcePoint);
|
||||
|
||||
// Get ths input pointers
|
||||
InputImageConstPointer inputPtr = this->GetInputImage();
|
||||
|
||||
typename InputImageType::SizeType sizeCT;
|
||||
typename InputImageType::RegionType regionCT;
|
||||
typename InputImageType::SpacingType ctPixelSpacing;
|
||||
typename InputImageType::PointType ctOrigin;
|
||||
|
||||
ctPixelSpacing = inputPtr->GetSpacing();
|
||||
ctOrigin = inputPtr->GetOrigin();
|
||||
regionCT = inputPtr->GetLargestPossibleRegion();
|
||||
sizeCT = regionCT.GetSize();
|
||||
|
||||
drrPixelWorld = m_InverseTransform->TransformPoint(point);
|
||||
|
||||
//slide source along z axis...
|
||||
SourceWorld[2] = drrPixelWorld[2];
|
||||
|
||||
// calculate the detector position for this pixel center by moving
|
||||
// 2*m_FocalPointToIsocenterDistance from the source in the pixel
|
||||
// directions
|
||||
double p1[2], p2[2];
|
||||
|
||||
p1[0] = SourceWorld[0];
|
||||
p1[1] = SourceWorld[1];
|
||||
|
||||
p2[0] = drrPixelWorld[0];
|
||||
p2[1] = drrPixelWorld[1];
|
||||
|
||||
double P12D = sqrt( (p2[0]-p1[0]) * (p2[0]-p1[0]) + (p2[1]-p1[1]) * (p2[1]-p1[1]) );
|
||||
double p12V[2];
|
||||
p12V[0] = p2[0] - p1[0];
|
||||
p12V[1] = p2[1] - p1[1];
|
||||
|
||||
double p12v [2];
|
||||
p12v[0] = p12V[0]/P12D;
|
||||
p12v[1] = p12V[1]/P12D;
|
||||
|
||||
double pDest [2];
|
||||
pDest[0] = p1[0] + m_FocalPointToIsocenterDistance * 2 * p12v[0];
|
||||
pDest[1] = p1[1] + m_FocalPointToIsocenterDistance * 2 * p12v[1];
|
||||
|
||||
|
||||
// The following is the Siddon-Jacob fast ray-tracing algorithm
|
||||
//rayVector[0] = drrPixelWorld[0] - SourceWorld[0];
|
||||
//rayVector[1] = drrPixelWorld[1] - SourceWorld[1];
|
||||
|
||||
// correct ray to reach detector
|
||||
rayVector[0] = pDest[0] - SourceWorld[0];
|
||||
rayVector[1] = pDest[1] - SourceWorld[1];
|
||||
rayVector[2] = drrPixelWorld[2] - SourceWorld[2];
|
||||
|
||||
|
||||
/* Calculate the parametric values of the first and the last
|
||||
intersection points of the ray with the X, Y, and Z-planes that
|
||||
define the CT volume. */
|
||||
if (rayVector[0] != 0)
|
||||
{
|
||||
alphaX1 = (0.0 - SourceWorld[0]) / rayVector[0];
|
||||
alphaXN = (sizeCT[0] * ctPixelSpacing[0] - SourceWorld[0]) / rayVector[0];
|
||||
alphaXmin = std::min(alphaX1, alphaXN);
|
||||
alphaXmax = std::max(alphaX1, alphaXN);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaXmin = -2;
|
||||
alphaXmax = 2;
|
||||
}
|
||||
|
||||
if (rayVector[1] != 0)
|
||||
{
|
||||
alphaY1 = (0.0 - SourceWorld[1]) / rayVector[1];
|
||||
alphaYN = (sizeCT[1] * ctPixelSpacing[1] - SourceWorld[1]) / rayVector[1];
|
||||
alphaYmin = std::min(alphaY1, alphaYN);
|
||||
alphaYmax = std::max(alphaY1, alphaYN);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaYmin = -2;
|
||||
alphaYmax = 2;
|
||||
}
|
||||
|
||||
if (rayVector[2] != 0)
|
||||
{
|
||||
alphaZ1 = (0.0 - SourceWorld[2]) / rayVector[2];
|
||||
alphaZN = (sizeCT[2] * ctPixelSpacing[2] - SourceWorld[2]) / rayVector[2];
|
||||
alphaZmin = std::min(alphaZ1, alphaZN);
|
||||
alphaZmax = std::max(alphaZ1, alphaZN);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaZmin = -2;
|
||||
alphaZmax = 2;
|
||||
}
|
||||
|
||||
/* Get the very first and the last alpha values when the ray
|
||||
intersects with the CT volume. */
|
||||
alphaMin = std::max(std::max(alphaXmin, alphaYmin), alphaZmin);
|
||||
alphaMax = std::min(std::min(alphaXmax, alphaYmax), alphaZmax);
|
||||
|
||||
/* Calculate the parametric values of the first intersection point
|
||||
of the ray with the X, Y, and Z-planes after the ray entered the
|
||||
CT volume. */
|
||||
|
||||
firstIntersection[0] = SourceWorld[0] + alphaMin * rayVector[0];
|
||||
firstIntersection[1] = SourceWorld[1] + alphaMin * rayVector[1];
|
||||
firstIntersection[2] = SourceWorld[2] + alphaMin * rayVector[2];
|
||||
|
||||
/* Transform world coordinate to the continuous index of the CT volume*/
|
||||
firstIntersectionIndex[0] = firstIntersection[0] / ctPixelSpacing[0];
|
||||
firstIntersectionIndex[1] = firstIntersection[1] / ctPixelSpacing[1];
|
||||
firstIntersectionIndex[2] = firstIntersection[2] / ctPixelSpacing[2];
|
||||
|
||||
firstIntersectionIndexUp[0] = (int)ceil(firstIntersectionIndex[0]);
|
||||
firstIntersectionIndexUp[1] = (int)ceil(firstIntersectionIndex[1]);
|
||||
firstIntersectionIndexUp[2] = (int)ceil(firstIntersectionIndex[2]);
|
||||
|
||||
firstIntersectionIndexDown[0] = (int)floor(firstIntersectionIndex[0]);
|
||||
firstIntersectionIndexDown[1] = (int)floor(firstIntersectionIndex[1]);
|
||||
firstIntersectionIndexDown[2] = (int)floor(firstIntersectionIndex[2]);
|
||||
|
||||
|
||||
if (rayVector[0] == 0)
|
||||
{
|
||||
alphaX = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaIntersectionUp[0] = (firstIntersectionIndexUp[0] * ctPixelSpacing[0] - SourceWorld[0]) / rayVector[0];
|
||||
alphaIntersectionDown[0] = (firstIntersectionIndexDown[0] * ctPixelSpacing[0] - SourceWorld[0]) / rayVector[0];
|
||||
alphaX = std::max(alphaIntersectionUp[0], alphaIntersectionDown[0]);
|
||||
}
|
||||
|
||||
if (rayVector[1] == 0)
|
||||
{
|
||||
alphaY = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaIntersectionUp[1] = (firstIntersectionIndexUp[1] * ctPixelSpacing[1] - SourceWorld[1]) / rayVector[1];
|
||||
alphaIntersectionDown[1] = (firstIntersectionIndexDown[1] * ctPixelSpacing[1] - SourceWorld[1]) / rayVector[1];
|
||||
alphaY = std::max(alphaIntersectionUp[1], alphaIntersectionDown[1]);
|
||||
}
|
||||
|
||||
if (rayVector[2] == 0)
|
||||
{
|
||||
alphaZ = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaIntersectionUp[2] = (firstIntersectionIndexUp[2] * ctPixelSpacing[2] - SourceWorld[2]) / rayVector[2];
|
||||
alphaIntersectionDown[2] = (firstIntersectionIndexDown[2] * ctPixelSpacing[2] - SourceWorld[2]) / rayVector[2];
|
||||
alphaZ = std::max(alphaIntersectionUp[2], alphaIntersectionDown[2]);
|
||||
}
|
||||
|
||||
/* Calculate alpha incremental values when the ray intercepts with x, y, and z-planes */
|
||||
if (rayVector[0] != 0)
|
||||
{
|
||||
alphaUx = ctPixelSpacing[0] / fabs(rayVector[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaUx = 999;
|
||||
}
|
||||
if (rayVector[1] != 0)
|
||||
{
|
||||
alphaUy = ctPixelSpacing[1] / fabs(rayVector[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaUy = 999;
|
||||
}
|
||||
if (rayVector[2] != 0)
|
||||
{
|
||||
alphaUz = ctPixelSpacing[2] / fabs(rayVector[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
alphaUz = 999;
|
||||
}
|
||||
/* Calculate voxel index incremental values along the ray path. */
|
||||
if (SourceWorld[0] < drrPixelWorld[0])
|
||||
{
|
||||
iU = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iU = -1;
|
||||
}
|
||||
if (SourceWorld[1] < drrPixelWorld[1])
|
||||
{
|
||||
jU = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
jU = -1;
|
||||
}
|
||||
|
||||
if (SourceWorld[2] < drrPixelWorld[2])
|
||||
{
|
||||
kU = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
kU = -1;
|
||||
}
|
||||
|
||||
d12 = 0.0; /* Initialize the sum of the voxel intensities along the ray path to zero. */
|
||||
|
||||
|
||||
/* Initialize the current ray position. */
|
||||
alphaCmin = std::min(std::min(alphaX, alphaY), alphaZ);
|
||||
|
||||
/* Initialize the current voxel index. */
|
||||
cIndex[0] = firstIntersectionIndexDown[0];
|
||||
cIndex[1] = firstIntersectionIndexDown[1];
|
||||
cIndex[2] = firstIntersectionIndexDown[2];
|
||||
|
||||
while (alphaCmin < alphaMax) /* Check if the ray is still in the CT volume */
|
||||
{
|
||||
/* Store the current ray position */
|
||||
alphaCminPrev = alphaCmin;
|
||||
|
||||
if ((alphaX <= alphaY) && (alphaX <= alphaZ))
|
||||
{
|
||||
/* Current ray front intercepts with x-plane. Update alphaX. */
|
||||
alphaCmin = alphaX;
|
||||
cIndex[0] = cIndex[0] + iU;
|
||||
alphaX = alphaX + alphaUx;
|
||||
}
|
||||
else if ((alphaY <= alphaX) && (alphaY <= alphaZ))
|
||||
{
|
||||
/* Current ray front intercepts with y-plane. Update alphaY. */
|
||||
alphaCmin = alphaY;
|
||||
cIndex[1] = cIndex[1] + jU;
|
||||
alphaY = alphaY + alphaUy;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Current ray front intercepts with z-plane. Update alphaZ. */
|
||||
alphaCmin = alphaZ;
|
||||
cIndex[2] = cIndex[2] + kU;
|
||||
alphaZ = alphaZ + alphaUz;
|
||||
}
|
||||
|
||||
if ((cIndex[0] >= 0) && (cIndex[0] < static_cast<IndexValueType>(sizeCT[0])) && (cIndex[1] >= 0) &&
|
||||
(cIndex[1] < static_cast<IndexValueType>(sizeCT[1])) && (cIndex[2] >= 0) &&
|
||||
(cIndex[2] < static_cast<IndexValueType>(sizeCT[2])))
|
||||
{
|
||||
/* If it is a valid index, get the voxel intensity. */
|
||||
value = static_cast<float>(inputPtr->GetPixel(cIndex));
|
||||
if (value > m_Threshold) /* Ignore voxels whose intensities are below the threshold. */
|
||||
{
|
||||
d12 += (alphaCmin - alphaCminPrev) * (value - m_Threshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (d12 < minOutputValue)
|
||||
{
|
||||
pixval = minOutputValue;
|
||||
}
|
||||
else if (d12 > maxOutputValue)
|
||||
{
|
||||
pixval = maxOutputValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixval = static_cast<OutputType>(d12);
|
||||
}
|
||||
return (pixval);
|
||||
}
|
||||
|
||||
|
||||
template <typename TInputImage, typename TCoordRep>
|
||||
typename gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::OutputType
|
||||
gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::EvaluateAtContinuousIndex(
|
||||
const ContinuousIndexType & index) const
|
||||
{
|
||||
OutputPointType point;
|
||||
this->m_Image->TransformContinuousIndexToPhysicalPoint(index, point);
|
||||
|
||||
return this->Evaluate(point);
|
||||
}
|
||||
|
||||
|
||||
template <typename TInputImage, typename TCoordRep>
|
||||
void
|
||||
gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::ComputeInverseTransform() const
|
||||
{
|
||||
m_ComposedTransform->SetIdentity();
|
||||
m_ComposedTransform->Compose(m_Transform, 0);
|
||||
|
||||
typename TransformType::InputPointType isocenter;
|
||||
isocenter = m_Transform->GetCenter();
|
||||
// An Euler 3D transform is used to rotate the volume to simulate the roation of the linac gantry.
|
||||
// The rotation is about z-axis. After the transform, a AP projection geometry (projecting
|
||||
// towards positive y direction) is established.
|
||||
m_GantryRotTransform->SetRotation(0.0, 0.0, -m_ProjectionAngle);
|
||||
m_GantryRotTransform->SetCenter(isocenter);
|
||||
m_ComposedTransform->Compose(m_GantryRotTransform, 0);
|
||||
|
||||
// An Euler 3D transfrom is used to shift the source to the origin.
|
||||
typename TransformType::OutputVectorType focalpointtranslation;
|
||||
focalpointtranslation[0] = -isocenter[0];
|
||||
focalpointtranslation[1] = m_FocalPointToIsocenterDistance - isocenter[1];
|
||||
focalpointtranslation[2] = -isocenter[2];
|
||||
m_CamShiftTransform->SetTranslation(focalpointtranslation);
|
||||
m_ComposedTransform->Compose(m_CamShiftTransform, 0);
|
||||
|
||||
// A Euler 3D transform is used to establish the standard negative z-axis projection geometry. (By
|
||||
// default, the camera is situated at the origin, points down the negative z-axis, and has an up-
|
||||
// vector of (0, 1, 0).)
|
||||
|
||||
m_ComposedTransform->Compose(m_CamRotTransform, 0);
|
||||
|
||||
// The overall inverse transform is computed. The inverse transform will be used by the interpolation
|
||||
// procedure.
|
||||
m_ComposedTransform->GetInverse(m_InverseTransform);
|
||||
this->Modified();
|
||||
}
|
||||
|
||||
|
||||
template <typename TInputImage, typename TCoordRep>
|
||||
void
|
||||
gSiddonJacobsRayCastInterpolateImageFunction<TInputImage, TCoordRep>::Initialize()
|
||||
{
|
||||
this->ComputeInverseTransform();
|
||||
m_SourceWorld = m_InverseTransform->TransformPoint(m_SourcePoint);
|
||||
}
|
||||
|
||||
} // namespace itk
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user