/*========================================================================= * * 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 * * https://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. * *=========================================================================*/ #ifndef itkBinaryMask3DMeshSource_h #define itkBinaryMask3DMeshSource_h #include "vnl/vnl_matrix_fixed.h" #include "itkMesh.h" #include "itkImageToMeshFilter.h" #include "itkTriangleCell.h" #include "itkCovariantVector.h" #include "itkDefaultStaticMeshTraits.h" #include "itkImageRegionConstIterator.h" namespace itk { /** \class BinaryMask3DMeshSource * * * \par * This class tries to construct a 3D mesh surface based on a binary mask. * It can be used to integrate a region-based segmentation method and a deformable * model into one hybrid framework. * * \par * To construct a mesh, we need to construct elements in a voxel and combine * those elements later to form the final mesh. Before go through every voxel * in the 3D volume, we first construct 2 look up tables. The index of these 2 * tables are the on-off combination of the 8 nodes that form the voxel. So * both of these tables has the size of \$2^8\$ bytes. According to previous * work, all those \$2^8\$ combination of the nodes can be grouped into 16 * final combinations. In the first table, we record the final combination that * can be transformed from the current combination. The entries of the second * table are made up of the transforming sequence that is necessary for the * current combination transform to one of the final combinations. * * \par * We then go through the 3D volume voxel by voxel, using those two tables we have defined * to construct elements within each voxel. We then merge all these mesh elements into * one 3D mesh. * * \par PARAMETERS * The ObjectValue parameter is used to identify the object. In most applications, * pixels in the object region are assigned to "1", so the default value of ObjectValue is * set to "1" * * \par REFERENCE * W. Lorensen and H. Cline, "Marching Cubes: A High Resolution 3D Surface Construction Algorithm", * Computer Graphics 21, pp. 163-169, 1987. * * \par INPUT * The input should be a 3D binary image. * * \ingroup ITKMesh */ template class ITK_TEMPLATE_EXPORT BinaryMask3DMeshSource : public ImageToMeshFilter { public: ITK_DISALLOW_COPY_AND_MOVE(BinaryMask3DMeshSource); /** Standard "Self" type alias. */ using Self = BinaryMask3DMeshSource; using Superclass = ImageToMeshFilter; using Pointer = SmartPointer; using ConstPointer = SmartPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** \see LightObject::GetNameOfClass() */ itkOverrideGetNameOfClassMacro(BinaryMask3DMeshSource); /** Hold on to the type information specified by the template parameters. */ using OutputMeshType = TOutputMesh; using OMeshTraits = typename OutputMeshType::MeshTraits; using OPointType = typename OutputMeshType::PointType; using OPixelType = typename OMeshTraits::PixelType; /** Some convenient type alias. */ using OutputMeshPointer = typename OutputMeshType::Pointer; using CellTraits = typename OutputMeshType::CellTraits; using PointsContainerPointer = typename OutputMeshType::PointsContainerPointer; using PointsContainer = typename OutputMeshType::PointsContainer; using CellsContainerPointer = typename OutputMeshType::CellsContainerPointer; using CellsContainer = typename OutputMeshType::CellsContainer; using doubleVector = CovariantVector; using intVector = CovariantVector; /** Define the triangular cell types which forms the surface of the model * and will be used in FEM application. */ using TCellInterface = CellInterface; using TriCell = TriangleCell; using TriCellAutoPointer = typename TriCell::SelfAutoPointer; /** Input Image Type Definition. */ using InputImageType = TInputImage; using InputImagePointer = typename InputImageType::Pointer; using InputImageConstPointer = typename InputImageType::ConstPointer; using InputPixelType = typename InputImageType::PixelType; using SpacingType = typename InputImageType::SpacingType; using OriginType = typename InputImageType::PointType; using RegionType = typename InputImageType::RegionType; using SizeType = typename InputImageType::SizeType; /** Type definition for the classified image index type. */ using InputImageIndexType = typename InputImageType::IndexType; using InputImageIterator = ImageRegionConstIterator; using IdentifierType = itk::IdentifierType; using SizeValueType = itk::SizeValueType; itkSetMacro(ObjectValue, InputPixelType); itkGetConstMacro(NumberOfNodes, SizeValueType); itkGetConstMacro(NumberOfCells, SizeValueType); /** accept the input image */ using Superclass::SetInput; virtual void SetInput(const InputImageType * image); void SetRegionOfInterest(const RegionType & iRegion) { if (iRegion != m_RegionOfInterest) { this->m_RegionOfInterest = iRegion; this->m_RegionOfInterestProvidedByUser = true; this->Modified(); } } itkGetConstReferenceMacro(RegionOfInterest, RegionType); protected: BinaryMask3DMeshSource(); ~BinaryMask3DMeshSource() override; void PrintSelf(std::ostream & os, Indent indent) const override; void GenerateData() override; bool m_RegionOfInterestProvidedByUser{ false }; RegionType m_RegionOfInterest{}; void GenerateOutputInformation() override {} // do nothing override private: using InputImageSizeType = typename InputImageType::SizeType; void CreateMesh(); void XFlip(unsigned char * x); // 7 kinds of transformation void YFlip(unsigned char * x); void ZFlip(unsigned char * x); void XRotation(unsigned char * x); void YRotation(unsigned char * x); void ZRotation(unsigned char * x); void inverse(unsigned char * x); void InitializeLUT(); // initialize the look up table before the mesh // construction void AddCells(unsigned char celltype, unsigned char celltran, int index); void AddNodes(int index, unsigned char * nodesid, IdentifierType * globalnodesid, IdentifierType ** currentrowtmp, IdentifierType ** currentframetmp); void CellTransfer(unsigned char * nodesid, unsigned char celltran); IdentifierType SearchThroughLastRow(int index, int start, int end); IdentifierType SearchThroughLastFrame(int index, int start, int end); unsigned char m_LUT[256][2]{}; // the two lookup tables IdentifierType m_LastVoxel[14]{}; IdentifierType m_CurrentVoxel[14]{}; IdentifierType ** m_LastRow{ nullptr }; IdentifierType ** m_LastFrame{ nullptr }; IdentifierType ** m_CurrentRow{ nullptr }; IdentifierType ** m_CurrentFrame{ nullptr }; int m_CurrentRowIndex{ 0 }; int m_CurrentFrameIndex{ 0 }; int m_LastRowNum{ 0 }; int m_LastFrameNum{ 0 }; int m_CurrentRowNum{ 200 }; int m_CurrentFrameNum{ 2000 }; unsigned char m_AvailableNodes[14]{}; double m_LocationOffset[14][3]{}; SizeValueType m_NumberOfNodes{ 0 }; SizeValueType m_NumberOfCells{ 0 }; int m_NodeLimit{ 2000 }; int m_CellLimit{ 4000 }; int m_ImageWidth{ 0 }; int m_ImageHeight{ 0 }; int m_ImageDepth{ 0 }; int m_ColFlag{ 0 }; int m_RowFlag{ 0 }; int m_FrameFlag{ 0 }; int m_LastRowIndex{ 0 }; int m_LastVoxelIndex{ 0 }; int m_LastFrameIndex{ 0 }; unsigned char m_PointFound{ 0 }; InputPixelType m_ObjectValue{}; /** temporary variables used in CreateMesh to avoid thousands of * calls to GetInput() and GetOutput() */ OutputMeshType * m_OutputMesh{}; const InputImageType * m_InputImage{}; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION # include "itkBinaryMask3DMeshSource.hxx" #endif #endif