Diff of /lib/RayMasking.cpp [000000] .. [dd2e6a]

Switch to side-by-side view

--- a
+++ b/lib/RayMasking.cpp
@@ -0,0 +1,267 @@
+#include "../includes/Masking/MaskingMethods/RayMasking.h"
+
+RayMasking::RayMasking(sitk::Image &image)
+: MaskingStrategy(image) {}
+
+void RayMasking::run()
+{
+    //image = gaussianFilter.Execute(image);
+
+    sitk::Image initialMask = binaryThresholdImageFilter.Execute(image, lowerThreshold, upperThreshold, 1, 0);
+    sitk::Image boneMask = binaryThresholdImageFilter.Execute(image, boneLowerThreshold, boneUpperThreshold, 1, 0);
+
+    mask = isolateIntracranialVoxels(initialMask, boneMask);
+    //mask = LCC(mask, 2); // along z-axis
+}
+
+void RayMasking::sitkToBinaryItk( const sitk::Image &image, MaskImageType::Pointer &outputImage )
+{
+    caster.SetOutputPixelType( sitk::sitkUInt8 );
+    sitk::Image castedImage = caster.Execute( image );
+
+    outputImage = dynamic_cast <MaskImageType*>( castedImage.GetITKBase() );
+    outputImage->DisconnectPipeline();
+    outputImage->SetBufferedRegion( outputImage->GetLargestPossibleRegion() );
+}
+
+sitk::Image RayMasking::isolateIntracranialVoxels(sitk::Image &initialMask, sitk::Image &boneMask)
+{
+    /* ------ Init Variables ------ */
+
+    // Cast sitk images to itk image
+    MaskImageType::Pointer itkInitialMask = MaskImageType::New();
+    sitkToBinaryItk( initialMask, itkInitialMask );
+
+    MaskImageType::Pointer itkBoneMask = MaskImageType::New();
+    sitkToBinaryItk( boneMask, itkBoneMask );
+
+    // Create output mask
+    MaskImageType::Pointer outputMask = MaskImageType::New();
+    MaskImageType::RegionType region;
+    region.SetSize( itkInitialMask->GetLargestPossibleRegion().GetSize() );
+    region.SetIndex( itkInitialMask->GetLargestPossibleRegion().GetIndex() );
+    outputMask->SetRegions( region );
+    outputMask->Allocate();
+    outputMask->FillBuffer(0);
+
+    // Init variables required for extract filter
+    MaskImageType::IndexType start = { 0,0,0 };
+    MaskImageType::RegionType inputRegion = itkInitialMask->GetLargestPossibleRegion();
+    MaskImageType::SizeType size = inputRegion.GetSize();
+    size[2] = 0; // extract along z direction
+
+    // Init extract filter for initial mask
+    // Extract filter is used to extract a 2D slice from a 3D image
+    // TODO: Wrap this in a function
+    ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
+    extractFilter->SetDirectionCollapseToSubmatrix();
+    extractFilter->SetInput( itkInitialMask );
+    MaskImageType::RegionType desiredRegion;
+    desiredRegion.SetSize(  size  );
+
+    // Init extract filter for bone mask
+    ExtractFilterType::Pointer extractFilterBone = ExtractFilterType::New();
+    extractFilterBone->SetDirectionCollapseToSubmatrix();
+    extractFilterBone->SetInput( itkBoneMask );
+    MaskImageType::RegionType desiredRegionBone;
+    desiredRegionBone.SetSize(  size  );
+
+    // Init extract filter for output
+    ExtractFilterType::Pointer extractFilterOutput = ExtractFilterType::New();
+    extractFilterOutput->SetDirectionCollapseToSubmatrix();
+    extractFilterOutput->SetInput( outputMask );
+    MaskImageType::RegionType desiredRegionOutput;
+    desiredRegionOutput.SetSize(  size  );
+    
+    int nSlices = inputRegion.GetSize()[2];
+    constexpr unsigned int intersectionTreshold = 6;
+
+    // Layout is used with tile mask filter which is used to stack 2d slices into a 3d image
+    itk::FixedArray< unsigned int, 3 > layout;
+    layout[0] = 1;
+    layout[1] = 1;
+    layout[2] = nSlices;
+
+    tileMaskFilter->SetLayout( layout );
+
+
+    MaskImage2DType::Pointer initialMaskSlice;
+    MaskImage2DType::Pointer boneMaskSlice;
+    MaskImage2DType::Pointer outputMaskSlice;
+
+    for (int sliceIdx = 0; sliceIdx < nSlices; ++sliceIdx)
+    {
+        std::cout << "Slice idx: " << sliceIdx << std::endl;
+
+        /*------ Extract 2d slice from 3d image ------*/
+
+        // Specify slice idx to be extracted from 3d image
+        start[2] = sliceIdx;
+        
+        // Extract 2d slice from initial mask
+        desiredRegion.SetIndex( start );
+        extractFilter->SetExtractionRegion( desiredRegion );
+        extractFilter->Update();
+        initialMaskSlice = extractFilter->GetOutput();
+
+        // Extract 2d slice from bone mask
+        desiredRegionBone.SetIndex( start );
+        extractFilterBone->SetExtractionRegion( desiredRegionBone );
+        extractFilterBone->Update();
+        boneMaskSlice = extractFilterBone->GetOutput();
+
+        // Extract 2d slice from output mask
+        desiredRegionOutput.SetIndex( start );
+        extractFilterOutput->SetExtractionRegion( desiredRegionOutput );
+        extractFilterOutput->Update();
+        outputMaskSlice = extractFilterOutput->GetOutput();
+
+        /*------ Init iterators ------*/
+
+        // Ray casting
+        // Init iterators
+        constIterType it(initialMaskSlice, initialMaskSlice->GetLargestPossibleRegion());
+        iterType itOutput(outputMaskSlice, outputMaskSlice->GetLargestPossibleRegion());
+
+        // Init chain code which will be used to encode ray directions
+        chainCodeType::Pointer chainCode = chainCodeType::New();
+        // Path iter will be used to iterate on image starting from a certain position and following the
+        // path specified in chainCode
+        pathConstIterType pathIter(boneMaskSlice, chainCode);
+
+        // Index at which the image iterator is currently
+        MaskImage2DType::IndexType currentIndex;
+        // Number of intersection that rays casted from a pixel made with bone pixels (skull)
+        unsigned int numberOfIntersections;
+        // Max number of steps that one can take along ray directions without going out of bounds
+        std::vector<long> numberOfSteps;
+
+        // Ray directions in freeman codes
+        std::vector<unsigned int> pathDirections = { 1,2,3,4,5,6,7,8 };
+
+        /* ----- Iterate over image ----- */
+
+        it.GoToBegin();
+        itOutput.GoToBegin();
+
+        while ( !it.IsAtEnd() )
+        {
+            // If current pixel on mask is 0 it is also 0 in outputmask, so continue
+            if (it.Get() == 0) { ++it; ++itOutput; continue; }
+            
+            numberOfIntersections = 0;
+            currentIndex = it.GetIndex();
+            // Init numberOfSteps for current pixel
+            numberOfSteps = {
+                                511 - currentIndex[1], 
+                                std::min( 511 - currentIndex[0], 511 - currentIndex[1]), 
+                                511 - currentIndex[0], 
+                                std::min( 511 - currentIndex[0], currentIndex[1]), 
+                                currentIndex[1], 
+                                std::min( currentIndex[0], currentIndex[1]),
+                                currentIndex[0],
+                                std::min( currentIndex[0], 511 - currentIndex[1])
+                            };
+
+            chainCode->SetStart( currentIndex );
+            for (unsigned int i : pathDirections)
+            {
+                // Fill the chain code vector with same chain code
+                chainCode->FillWithSteps(numberOfSteps[i-1], i); // Custom
+
+                // Iterate by following the path on the image
+                pathIter.GoToBegin();
+                while ( !pathIter.IsAtEnd() )
+                {
+                    // If coincides with bone pixel
+                    if ( pathIter.Get() == 1)
+                    {
+                        ++numberOfIntersections;
+                        break;
+                    }
+                    ++pathIter;
+                }
+                chainCode->Clear();
+            }
+
+            // If, for the current pixel, at least 7 rays intersected with bone pixels out of 8, then that pixel is interpreted as
+            // inside skull and is marked as 1 in output mask
+            if (numberOfIntersections > intersectionTreshold) { itOutput.Set(1); }
+            ++it; ++itOutput;
+        }
+        // To use the output mask slice independent from the filter that owns it, the mask slice should be disconnected from filter's pipeline
+        outputMaskSlice->DisconnectPipeline();
+        // Put 2d slice into the final 3d image
+        tileMaskFilter->SetInput( sliceIdx, outputMaskSlice );
+    }   
+
+    MaskImageType::Pointer output = tileMaskFilter->GetOutput();
+    sitk::Image outputImage = sitk::Image( output );
+    
+    return outputImage;
+}
+
+
+
+/*
+Extracts largest connected components for each slice along an axis
+ */
+sitk::Image RayMasking::LCC( sitk::Image inputMask, int axis=2)
+{
+    MaskImageType::Pointer itkMask = MaskImageType::New();
+    
+    // Cast input to itk image
+    caster.SetOutputPixelType( sitk::sitkUInt8 );
+    inputMask = caster.Execute( inputMask );
+    itkMask = dynamic_cast <itk::Image< uint8_t, 3 >*>( inputMask.GetITKBase() );
+    itkMask->SetBufferedRegion( itkMask->GetLargestPossibleRegion() );
+
+    // Extract filter for initial mask
+    ExtractFilterType::Pointer extractFilter = ExtractFilterType::New();
+    extractFilter->SetDirectionCollapseToSubmatrix();
+    extractFilter->SetInput( itkMask );
+    MaskImageType::RegionType inputRegion = itkMask->GetLargestPossibleRegion();
+    MaskImageType::SizeType size = inputRegion.GetSize();
+    size[axis] = 0; // we extract along an axis
+    MaskImageType::IndexType start = { 0,0,0 };
+    MaskImageType::RegionType desiredRegion;
+    desiredRegion.SetSize(  size  );
+
+    int nSlices = inputRegion.GetSize()[axis];
+    itk::FixedArray< unsigned int, 3 > layout;
+    layout.Fill(1);
+    layout[axis] = nSlices;
+
+    tileFilter->SetLayout( layout );
+
+    MaskImage2DType::Pointer maskSlice = MaskImage2DType::New();
+    InputImage2DType::Pointer outputSlice = InputImage2DType::New();
+
+    for ( int i=0; i<nSlices; ++i)
+    {
+        // Extract slices
+        start[axis] = i;
+        desiredRegion.SetIndex( start );
+        extractFilter->SetExtractionRegion( desiredRegion );
+        extractFilter->Update();
+        maskSlice = extractFilter->GetOutput();
+
+        connCompFilter->SetInput( maskSlice );
+        connCompFilter->Update();
+
+        labelShapeKeepNObjectsImageFilter->SetInput( connCompFilter->GetOutput() );
+        labelShapeKeepNObjectsImageFilter->SetBackgroundValue( 0 );
+        labelShapeKeepNObjectsImageFilter->SetNumberOfObjects( 1 );
+        labelShapeKeepNObjectsImageFilter->SetAttribute( LabelShapeKeepNObjectsImageFilterType::LabelObjectType::NUMBER_OF_PIXELS);
+        outputSlice = labelShapeKeepNObjectsImageFilter->GetOutput();
+        //sitk::Show( sitk::Image( outputSlice ) );
+        outputSlice->DisconnectPipeline();
+        tileFilter->SetInput(i, outputSlice);
+    }
+
+    InputImageType::Pointer itkOutput = tileFilter->GetOutput();
+    itkOutput->DisconnectPipeline();
+    sitk::Image output = sitk::Image( itkOutput );
+    sitk::Show(output);
+    return output;
+}
\ No newline at end of file