From 49774c0545cfc088e374926a7bf9fcba33dca668 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 22 Aug 2011 09:47:28 -0400 Subject: [PATCH 01/84] ENH: revision 3 --- Modules/Filtering/Cuberille/CMakeLists.txt | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Modules/Filtering/Cuberille/CMakeLists.txt diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt new file mode 100644 index 00000000000..15d4b6900ef --- /dev/null +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -0,0 +1,25 @@ +# This project is designed to be built outside the Insight source tree. +PROJECT(Cuberille) + +CMAKE_MINIMUM_REQUIRED(VERSION 2.4) + +# Find ITK. +FIND_PACKAGE(ITK REQUIRED) +INCLUDE(${ITK_USE_FILE}) + +SET(DATA_PATH ${Cuberille_SOURCE_DIR}/Data) +SET(SOURCE_PATH ${Cuberille_SOURCE_DIR}/Source) +SET(TESTING_PATH ${Cuberille_SOURCE_DIR}/Testing) + +INCLUDE_DIRECTORIES( + BEFORE + ${SOURCE_PATH} + ${TESTING_PATH} + ) + +ADD_SUBDIRECTORY(Source) +OPTION(BUILD_TESTING "Build the Testing directory." ON) +IF(BUILD_TESTING) + ENABLE_TESTING() + SUBDIRS(Testing) +ENDIF(BUILD_TESTING) From b935d6d3d106f7231b8d60c5100cd4ede1305444 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 14:17:48 -0400 Subject: [PATCH 02/84] BUG: Add missing LICENSE file. --- Modules/Filtering/Cuberille/LICENSE | 202 ++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 Modules/Filtering/Cuberille/LICENSE diff --git a/Modules/Filtering/Cuberille/LICENSE b/Modules/Filtering/Cuberille/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/Modules/Filtering/Cuberille/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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 + + 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. From 9adadd44f4d0589b360d091f37052cf8ef57926c Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 14:24:36 -0400 Subject: [PATCH 03/84] ENH: Add initial itk-module.cmake file. --- Modules/Filtering/Cuberille/itk-module.cmake | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Modules/Filtering/Cuberille/itk-module.cmake diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake new file mode 100644 index 00000000000..007d50526fa --- /dev/null +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -0,0 +1,31 @@ +set( + DOCUMENTATION + "This module implements cuberille implicit surface +polygonization for ITK. This method operates by diving the surface into a +number of small cubes called cuberilles. Each cuberille is centered at a +pixel lying on th eiso-surface and then quadrilaterals are generated for each +face. The original approach is improved by projecting the vertices of each +cuberille onto the implicit surface, smoothing the typical block-like +resultant mesh. + +A more detailed description can be found in the Insight Journal article: + + Mueller, D. \"Cuberille Implicit Surface Polygonization for ITK\" + http://hdl.handle.net/10380/3186 + http://www.insight-journal.org/browse/publication/740 + July 20, 2010. +" +) + +itk_module( + Cuberille + DEPENDS + ITKCommon + ITKImageFunction + ITKImageGradient + ITKMesh + TEST_DEPENDS + ITKTestKernel + EXCLUDE_FROM_DEFAULT + DESCRIPTION "${DOCUMENTATION}" +) From 1729732d94d3d0e16ec4f05d69fb25ae754ec022 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 14:36:26 -0400 Subject: [PATCH 04/84] ENH: Reorganize as an ITKv4 module. --- Modules/Filtering/Cuberille/CMakeLists.txt | 35 +- .../include/itkCuberilleImageToMeshFilter.h | 390 +++++++++++ .../include/itkCuberilleImageToMeshFilter.txx | 638 ++++++++++++++++++ .../Filtering/Cuberille/test/CMakeLists.txt | 366 ++++++++++ .../Cuberille/test/CuberilleTest01.cxx | 221 ++++++ .../Filtering/Cuberille/test/Input/blob0.mha | Bin 0 -> 410 bytes .../Filtering/Cuberille/test/Input/blob1.mha | Bin 0 -> 409 bytes .../Filtering/Cuberille/test/Input/blob2.mha | Bin 0 -> 409 bytes .../Filtering/Cuberille/test/Input/blob3.mha | Bin 0 -> 422 bytes .../Filtering/Cuberille/test/Input/blob4.mha | Bin 0 -> 844 bytes .../Filtering/Cuberille/test/Input/fuel.mha | Bin 0 -> 5642 bytes .../Cuberille/test/Input/hydrogenAtom.mha | Bin 0 -> 81007 bytes .../Cuberille/test/Input/marschnerlobb.mha | Bin 0 -> 34293 bytes .../Filtering/Cuberille/test/Input/neghip.mha | Bin 0 -> 81079 bytes .../Cuberille/test/Input/nucleon.mha | Bin 0 -> 18175 bytes .../Cuberille/test/Input/silicium.mha | Bin 0 -> 38286 bytes 16 files changed, 1625 insertions(+), 25 deletions(-) create mode 100644 Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h create mode 100644 Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx create mode 100644 Modules/Filtering/Cuberille/test/CMakeLists.txt create mode 100644 Modules/Filtering/Cuberille/test/CuberilleTest01.cxx create mode 100644 Modules/Filtering/Cuberille/test/Input/blob0.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/blob1.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/blob2.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/blob3.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/blob4.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/fuel.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/hydrogenAtom.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/marschnerlobb.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/neghip.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/nucleon.mha create mode 100644 Modules/Filtering/Cuberille/test/Input/silicium.mha diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt index 15d4b6900ef..02513d97920 100644 --- a/Modules/Filtering/Cuberille/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -1,25 +1,10 @@ -# This project is designed to be built outside the Insight source tree. -PROJECT(Cuberille) - -CMAKE_MINIMUM_REQUIRED(VERSION 2.4) - -# Find ITK. -FIND_PACKAGE(ITK REQUIRED) -INCLUDE(${ITK_USE_FILE}) - -SET(DATA_PATH ${Cuberille_SOURCE_DIR}/Data) -SET(SOURCE_PATH ${Cuberille_SOURCE_DIR}/Source) -SET(TESTING_PATH ${Cuberille_SOURCE_DIR}/Testing) - -INCLUDE_DIRECTORIES( - BEFORE - ${SOURCE_PATH} - ${TESTING_PATH} - ) - -ADD_SUBDIRECTORY(Source) -OPTION(BUILD_TESTING "Build the Testing directory." ON) -IF(BUILD_TESTING) - ENABLE_TESTING() - SUBDIRS(Testing) -ENDIF(BUILD_TESTING) +cmake_minimum_required(VERSION 2.9) +project(Cuberille) + +if(NOT ITK_SOURCE_DIR) + find_package(ITK REQUIRED) + list(APPEND CMAKE_MODULE_PATH ${ITK_CMAKE_DIR}) + include(ITKModuleExternal) +else() + itk_module_impl() +endif() diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h new file mode 100644 index 00000000000..6a8e8d23a6d --- /dev/null +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -0,0 +1,390 @@ +/*========================================================================= + + Program: Insight Segmentation & Registration Toolkit + Module: $RCSfile: itkCuberilleImageToMeshFilter.h,v $ + Language: C++ + Date: $Date$ + Version: $Revision$ + + Copyright (c) Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __itkCuberilleImageToMeshFilter_h +#define __itkCuberilleImageToMeshFilter_h + +#define DEBUG_PRINT 0 +#define USE_GRADIENT_RECURSIVE_GAUSSIAN 0 +#define USE_ADVANCED_PROJECTION 0 +#define USE_LINESEARCH_PROJECTION 0 + +#include "itkMacro.h" +#include "itkMesh.h" +#include "itkImageToMeshFilter.h" +#include "itkCellInterface.h" +#include "itkTriangleCell.h" +#include "itkQuadrilateralCell.h" +#include "itkDefaultStaticMeshTraits.h" +#include "itkConstShapedNeighborhoodIterator.h" +#include "itkLinearInterpolateImageFunction.h" +#include "itkGradientImageFilter.h" +#include "itkGradientRecursiveGaussianImageFilter.h" +#include "itkVectorLinearInterpolateImageFunction.h" + +namespace itk +{ + +/** \class CuberilleImageToMeshFilter + * + * This filter uses the 'cuberille' method to convert an implicit surface + * (image) to a mesh. + * + * The 'cuberille' model was proposed over 30 years ago [1,2]. + * A basic summary of the algorithm is as follows: + * step over all pixels, for each pixel determine if it lies on the surface, + * center a cube on the surface pixel, create quadrilateral faces aligned with + * the cube (taking care of neighbouring pixels also on the surface), use a + * gradient descent based method to project cube vertices to iso-surface [3]. + * + * \par Parmeters + * (Required) Input: specifies the input image containing the implicit surface + * for polygonization. Currently, the input must NOT have iso-surface pixels + * on the edge of the image. If this is the case, you MUST use + * itkConstantPadImageFilter to pad the edges by at least 1 pixel. + * + * (Required) IsoSurfaceValue: specifies the value of the iso-surface for which + * to generate the mesh. Pixels equal to or greater than this value are considered + * to lie on the surface or inside the resultant mesh. + * + * (Optional) GenerateTriangleFaces: specifies whether triangle or + * quadrilateral faces should be generated. The default is to generate + * triangle faces. + * + * (Optional) ProjectVerticesToIsoSurface: specifies whether the vertices + * should be projected onto the iso-surface. If projection is disabled, the + * resultant mesh exhibits the traditional blocky features. Projection takes + * roughly half of the algorithm time. The default is to project the vertices. + * + * (Optional) ProjectVertexSurfaceDistanceThreshold: specifies the threshold + * for the 'distance' from iso-surface during vertex projection. Note that the + * distance is actually measured in pixel value units (not space). + * The smaller this value, the closer the vertices will be to the iso-surface. + * Small values result in longer convergence time (i.e. slower). + * Values are clamped to the range [0.0, max pixel value]. + * The default value is 0.5. + * + * (Optional) ProjectVertexStepLength: specifies the threshold for the step + * length during vertex projection. + * The smaller this value, the more likely the vertices will end up closer to + * the surface. Small values cause the projection to take longer to converge. + * Values are clamped to the range [0.0, large]. + * The default value is max spacing * 0.25 (expressed in physical space). + * + * (Optional) ProjectVertexStepLengthRelaxationFactor: specifies the step + * length relaxation factor during vertex projection. The step length is + * multiplied by this factor each iteration to allow convergence. + * Values are clamped to the range [0.0, 1.0].The default value is 0.95. + * + * (Optional) ProjectVertexMaximumNumberOfSteps: specifies the maximum number + * of steps used during vertex projection. The default value is 50. + * + * \par References + * [1] G. Herman and H. Liu, "Three-dimensional Display of Human organs + * from Computed Tomograms", Computer Graphics and Images Processing, + * Volume 9, Issue 1, Pages 1-21, 1979. + * [2] D. Gordon and J.K. Udupa, "Fast surface tracking in Three-dimensional + Binary Images", Computer Vision, Graphics and Image Processing, + Volume 45, Pages 196-214, 1989. + * [3] http://www2.imm.dtu.dk/~jab/gallery/polygonization.html + * + * This implementation was taken from the Insight Journal: + * http://hdl.handle.net/10380/3186 + * + * \author Dan Mueller, Philips Healthcare, dan dot muel at gmail dot com + * + */ +template > +class ITK_EXPORT CuberilleImageToMeshFilter : public ImageToMeshFilter +{ +public: + /** Standard "Self" typedef. */ + typedef CuberilleImageToMeshFilter Self; + typedef ImageToMeshFilter Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(CuberilleImageToMeshFilter, ImageToMeshFilter); + + /** Some convenient typedefs. */ + typedef TOutputMesh OutputMeshType; + typedef typename OutputMeshType::Pointer OutputMeshPointer; + typedef typename OutputMeshType::MeshTraits OutputMeshTraits; + typedef typename OutputMeshType::PointType OutputPointType; + typedef typename OutputMeshTraits::PixelType OutputPixelType; + typedef typename OutputMeshType::CellTraits CellTraits; + typedef typename OutputMeshType::PointsContainerPointer PointsContainerPointer; + typedef typename OutputMeshType::PointsContainer PointsContainer; + typedef typename OutputMeshType::CellsContainerPointer CellsContainerPointer; + typedef typename OutputMeshType::CellsContainer CellsContainer; + typedef typename OutputMeshType::PointIdentifier PointIdentifier; + typedef typename OutputMeshType::CellIdentifier CellIdentifier; + typedef CellInterface CellInterfaceType; + typedef TriangleCell TriangleCellType; + typedef typename TriangleCellType::SelfAutoPointer TriangleAutoPointer; + typedef typename TriangleCellType::CellAutoPointer TriangleCellAutoPointer; + typedef QuadrilateralCell QuadrilateralCellType; + typedef typename QuadrilateralCellType::SelfAutoPointer QuadrilateralAutoPointer; + typedef typename QuadrilateralCellType::CellAutoPointer QuadrilateralCellAutoPointer; + + typedef TInputImage InputImageType; + typedef typename InputImageType::Pointer InputImagePointer; + typedef typename InputImageType::ConstPointer InputImageConstPointer; + typedef typename InputImageType::PixelType InputPixelType; + typedef typename InputImageType::SizeType SizeType; + typedef typename InputImageType::SpacingType SpacingType; + typedef typename InputImageType::SpacingValueType SpacingValueType; + typedef typename InputImageType::IndexType IndexType; + typedef typename OutputMeshType::PointType PointType; + + typedef TInterpolator InterpolatorType; + typedef typename InterpolatorType::Pointer InterpolatorPointer; + typedef typename InterpolatorType::OutputType InterpolatorOutputType; + + /** Other convenient typedefs. */ + typedef ConstShapedNeighborhoodIterator InputImageIteratorType; +#if USE_GRADIENT_RECURSIVE_GAUSSIAN + typedef GradientRecursiveGaussianImageFilter GradientFilterType; +#else + typedef GradientImageFilter GradientFilterType; +#endif + typedef typename GradientFilterType::Pointer GradientFilterPointer; + typedef typename GradientFilterType::OutputImageType GradientImageType; + typedef typename GradientImageType::Pointer GradientImagePointer; + typedef typename GradientFilterType::OutputPixelType GradientPixelType; + typedef itk::VectorLinearInterpolateImageFunction GradientInterpolatorType; + typedef typename GradientInterpolatorType::Pointer GradientInterpolatorPointer; + + /** Get/set the iso-surface value. + * This parameter specifies the value of the iso-surface for which to + * generate the mesh. Pixels equal to or less than this value are + * considered on the surface or inside the resultant mesh. + */ + itkGetMacro(IsoSurfaceValue, InputPixelType); + itkSetMacro(IsoSurfaceValue, InputPixelType); + + /** Accept the input image. */ + virtual void + SetInput(const InputImageType * inputImage); + + /** Get/set interpolate function. */ + itkGetObjectMacro(Interpolator, InterpolatorType); + itkSetObjectMacro(Interpolator, InterpolatorType); + + /** Get/set whether triangle or quadrilateral faces should be generated. + * True = triangle faces, False = quadrilateral faces. + Default = true (triangle faces). */ + itkGetMacro(GenerateTriangleFaces, bool); + itkSetMacro(GenerateTriangleFaces, bool); + itkBooleanMacro(GenerateTriangleFaces); + + /** Get/set whether the vertices should be project to the iso-surface. + Default = true. */ + itkGetMacro(ProjectVerticesToIsoSurface, bool); + itkSetMacro(ProjectVerticesToIsoSurface, bool); + itkBooleanMacro(ProjectVerticesToIsoSurface); + + /** Get/set the threshold for the "distance" from iso-surface during vertex projection. + Note that the distance is actually measured in pixel value units (not space). + The smaller this value, the closer the vertices will be to the iso-surface. + Small values result in longer convergence time (i.e. slower). + Values are clamped to the range [0.0, max pixel value]. + Default = 0.5. */ + itkGetMacro(ProjectVertexSurfaceDistanceThreshold, double); + itkSetClampMacro(ProjectVertexSurfaceDistanceThreshold, double, 0.0, NumericTraits::max()); + + /** Get/set the the initial step length for vertex projection. + Values are clamped to the range [0.0, large]. + Default = max spacing * 0.25 (expressed in physical space). */ + itkGetMacro(ProjectVertexStepLength, double); + itkSetClampMacro(ProjectVertexStepLength, double, 0.0, 100000.0); + + /** Get/set the step length relaxation factor during vertex projection. + The step length is multiplied by this factor each iteration to allow convergence. + Values are clamped to the range [0.0, 1.0]. + Default = 0.95. */ + itkGetMacro(ProjectVertexStepLengthRelaxationFactor, double); + itkSetClampMacro(ProjectVertexStepLengthRelaxationFactor, double, 0.0, 1.0); + + /** Get/set the maximum number of steps used during vertex projection. + Default = 50. */ + itkGetMacro(ProjectVertexMaximumNumberOfSteps, unsigned int); + itkSetMacro(ProjectVertexMaximumNumberOfSteps, unsigned int); + +protected: + CuberilleImageToMeshFilter(); + ~CuberilleImageToMeshFilter(); + void + PrintSelf(std::ostream & os, Indent indent) const; + + void + GenerateData(); + virtual void + GenerateOutputInformation() {}; // do nothing + +private: + CuberilleImageToMeshFilter(const Self &); // purposely not implemented + void + operator=(const Self &); // purposely not implemented + + /** A private class containing lookup details for vertices. */ + class VertexLookupNode + { + public: + /** Convenient typedefs */ + typedef VertexLookupNode Self; + + /** Constructors */ + VertexLookupNode() + : m_X(0) + , m_Y(0) + {} + VertexLookupNode(unsigned long x, unsigned long y) + : m_X(x) + , m_Y(y) + {} + + /** Parameters */ + unsigned long + GetX() + { + return m_X; + } + unsigned long + GetY() + { + return m_Y; + } + + /** Comparison operators for sorting */ + bool + operator>(const Self & node) const + { + return (m_Y > node.m_Y) || ((m_Y == node.m_Y) && (m_X > node.m_X)); + } + bool + operator>=(const Self & node) const + { + return (m_Y >= node.m_Y) || ((m_Y == node.m_Y) && (m_X >= node.m_X)); + } + bool + operator<(const Self & node) const + { + return (m_Y < node.m_Y) || ((m_Y == node.m_Y) && (m_X < node.m_X)); + } + bool + operator<=(const Self & node) const + { + return (m_Y <= node.m_Y) || ((m_Y == node.m_Y) && (m_X <= node.m_X)); + } + + private: + unsigned long m_X; + unsigned long m_Y; + }; + + /** A private class providing vertex lookup functionality. */ + template + class VertexLookupMap + { + public: + /** Convenient typedefs */ + typedef VertexLookupMap Self; + typedef typename TMeshType::PointIdentifier PointIdentifier; + typedef std::map MapType; + + /** Constructors */ + VertexLookupMap() {} + + /** Clear the lookup map. */ + void + Clear() + { + m_Map.clear(); + } + + /** Add the given vertex identifer to the given [x,y] position. */ + void + AddVertex(unsigned int x, unsigned int y, PointIdentifier id) + { + VertexLookupNode node(x, y); + m_Map.insert(typename MapType::value_type(node, id)); + } + + /** Get the vertex identifer for the given [x,y] position. + * Returns true if the vertex exists and id contains the identifer. + * Returns false if the vertex does not exist and id is undefined. */ + bool + GetVertex(unsigned int x, unsigned int y, PointIdentifier & id) + { + bool result = false; + VertexLookupNode node(x, y); + typename MapType::iterator it = m_Map.find(node); + if (it != m_Map.end()) + { + result = true; + id = it->second; + } + return result; + } + + private: + MapType m_Map; + }; + + /** Some convenient typedefs. */ + typedef VertexLookupMap VertexLookupMapType; + + /** Private functions to implement the algorithm. */ + inline void + ComputeGradientImage(); + inline void + SetVerticesFromFace(unsigned int face, bool * vertexHasQuad); + inline IndexType + GetVertexLookupIndex(unsigned int vertex, IndexType index); + inline void + ProjectVertexToIsoSurface(PointType & vertex); + inline void + AddVertex(PointIdentifier & id, IndexType index, const InputImageType * image, OutputMeshType * mesh); + inline void + AddQuadFace(CellIdentifier & id, PointIdentifier f[4], OutputMeshType * mesh); + + InputPixelType m_IsoSurfaceValue; + InterpolatorPointer m_Interpolator; + GradientInterpolatorPointer m_GradientInterpolator; + SpacingValueType m_MaxSpacing; + bool m_GenerateTriangleFaces; + bool m_ProjectVerticesToIsoSurface; + double m_ProjectVertexSurfaceDistanceThreshold; + double m_ProjectVertexStepLength; + double m_ProjectVertexStepLengthRelaxationFactor; + unsigned int m_ProjectVertexMaximumNumberOfSteps; +#if DEBUG_PRINT + unsigned int m_ProjectVertexTerminate[3]; +#endif +}; + +} // end namespace itk + +#ifndef ITK_MANUAL_INSTANTIATION +# include "itkCuberilleImageToMeshFilter.txx" +#endif + +#endif diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx new file mode 100644 index 00000000000..6e183ce8842 --- /dev/null +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx @@ -0,0 +1,638 @@ +/*========================================================================= + +Program: Insight Segmentation & Registration Toolkit +Module: $RCSfile: itkCuberilleImageToMeshFilter.txx,v $ +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) Insight Software Consortium. All rights reserved. +See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + +This software is distributed WITHOUT ANY WARRANTY; without even +the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __itkCuberilleImageToMeshFilter_txx +#define __itkCuberilleImageToMeshFilter_txx + +#define SWAP(x, y) \ + unsigned int t; \ + t = x; \ + x = y; \ + y = t; + +#include "itkCuberilleImageToMeshFilter.h" +#include "itkNumericTraits.h" +#include "itkConnectedComponentAlgorithm.h" + +namespace itk +{ + +template +CuberilleImageToMeshFilter::CuberilleImageToMeshFilter() +{ + this->SetNumberOfRequiredInputs(1); + m_IsoSurfaceValue = NumericTraits::One; + m_GenerateTriangleFaces = true; + m_ProjectVerticesToIsoSurface = true; + m_ProjectVertexSurfaceDistanceThreshold = 0.5; + m_ProjectVertexStepLength = -1.0; + m_ProjectVertexStepLengthRelaxationFactor = 0.95; + m_ProjectVertexMaximumNumberOfSteps = 50; +} + +template +CuberilleImageToMeshFilter::~CuberilleImageToMeshFilter() +{ + m_GradientInterpolator = NULL; +} + +template +void +CuberilleImageToMeshFilter::SetInput(const InputImageType * image) +{ + this->ProcessObject::SetNthInput(0, const_cast(image)); +} + +/** Generate data */ +template +void +CuberilleImageToMeshFilter::GenerateData() +{ +#if DEBUG_PRINT + m_ProjectVertexTerminate[0] = 0; + m_ProjectVertexTerminate[1] = 0; + m_ProjectVertexTerminate[2] = 0; +#endif + + // Get input/output + InputImageConstPointer image = Superclass::GetInput(0); + typename OutputMeshType::Pointer mesh = Superclass::GetOutput(); + + // Compute maximum spacing + m_MaxSpacing = image->GetSpacing()[0]; + for (int i = 1; i < InputImageType::ImageDimension; i++) + { + m_MaxSpacing = vnl_math_max(m_MaxSpacing, image->GetSpacing()[i]); + } + + // Set default step length + if (m_ProjectVertexStepLength < 0.0) + { + m_ProjectVertexStepLength = m_MaxSpacing * 0.25; + } + + // Create interpolator for pixel value + if (m_Interpolator.IsNull()) + { + m_Interpolator = InterpolatorType::New(); + } + m_Interpolator->SetInputImage(image); + + // Create interpolator for gradient image + ComputeGradientImage(); + + // Set up iterator + typename InputImageIteratorType::SizeType radius; + radius.Fill(1); + InputImageIteratorType it(radius, image, image->GetBufferedRegion()); + setConnectivity(&it, false); // Set face connectivity + + // TODO: Estimate number of vertices/faces for which to reserve + // TODO: There is an issue with quad edge mesh related to reserving, see + // http://www.itk.org/mailman/private/insight-developers/2010-June/014653.html + // SizeType size = image->GetLargestPossibleRegion().GetSize(); + // mesh->GetPoints()->Reserve( (size[0]*size[1]*size[2]) / 200 ); + // mesh->GetCells()->Reserve( (size[0]*size[1]*size[2]) / 100 ); + + // Set up helper structures + unsigned int i, look, look0, look1 = 0; + unsigned char numFaces = 0; + bool faceHasQuad[6]; + bool vertexHasQuad[8]; + PointIdentifier v[8]; + PointIdentifier f[4]; + PointIdentifier nextVertexId = 0; + CellIdentifier nextCellId = 0; + IndexType index; + typename IndexType::IndexValueType lastZ = -1; + InputPixelType center = 0; + typename InputImageIteratorType::OffsetType offset[6]; + offset[0][0] = -1; + offset[0][1] = +0; + offset[0][2] = +0; + offset[1][0] = +0; + offset[1][1] = -1; + offset[1][2] = +0; + offset[2][0] = +1; + offset[2][1] = +0; + offset[2][2] = +0; + offset[3][0] = +0; + offset[3][1] = +1; + offset[3][2] = +0; + offset[4][0] = +0; + offset[4][1] = +0; + offset[4][2] = -1; + offset[5][0] = +0; + offset[5][1] = +0; + offset[5][2] = +1; + VertexLookupMapType lookup[2]; + look0 = 1; + look1 = 0; + lookup[look0].Clear(); + lookup[look1].Clear(); + + // TODO: Handle voxels on the edge of the image + + // Iterate input image + for (it.GoToBegin(); !it.IsAtEnd(); ++it) + { + // Determine if current pixel is suitable + center = it.GetCenterPixel(); + if (center < m_IsoSurfaceValue) + continue; + + // Re-initialize for new pixel + numFaces = 0; + for (i = 0; i < 6; i++) + { + faceHasQuad[i] = false; + } + for (i = 0; i < 8; i++) + { + vertexHasQuad[i] = false; + } + + // Re-initialize for new z plane + index = it.GetIndex(); + if (index[2] != lastZ) + { + SWAP(look0, look1); + lookup[look1].Clear(); + lastZ = index[2]; + } + + // Compute which faces (if any) have quads + for (i = 0; i < 6; i++) + { + // NOTE: suitability check above means center <= m_IsoSurfaceValue + faceHasQuad[i] = (it.GetPixel(offset[i]) < m_IsoSurfaceValue); + if (faceHasQuad[i]) + { + numFaces++; + SetVerticesFromFace(i, &vertexHasQuad[0]); + } + } + + // Process each face + if (numFaces > 0) + { + // Create vertices + for (i = 0; i < 8; i++) + { + if (vertexHasQuad[i]) + { + // Use the vertex lookup to get the vertex for the correct slice + IndexType vindex = GetVertexLookupIndex(i, index); + look = (i < 4) ? look0 : look1; // First four are first slice + if (!lookup[look].GetVertex(vindex[0], vindex[1], v[i])) + { + // Vertex was not in lookup, create and add to lookup + v[i] = nextVertexId; + AddVertex(nextVertexId, vindex, image, mesh); + lookup[look].AddVertex(vindex[0], vindex[1], v[i]); + } + } // end if vertex has quad + } // end foreach vertex + + // Create faces + if (faceHasQuad[0]) + { + f[0] = v[0]; + f[1] = v[4]; + f[2] = v[7]; + f[3] = v[3]; + AddQuadFace(nextCellId, f, mesh); + } + if (faceHasQuad[1]) + { + f[0] = v[0]; + f[1] = v[1]; + f[2] = v[5]; + f[3] = v[4]; + AddQuadFace(nextCellId, f, mesh); + } + if (faceHasQuad[2]) + { + f[0] = v[1]; + f[1] = v[2]; + f[2] = v[6]; + f[3] = v[5]; + AddQuadFace(nextCellId, f, mesh); + } + if (faceHasQuad[3]) + { + f[0] = v[2]; + f[1] = v[3]; + f[2] = v[7]; + f[3] = v[6]; + AddQuadFace(nextCellId, f, mesh); + } + if (faceHasQuad[4]) + { + f[0] = v[0]; + f[1] = v[3]; + f[2] = v[2]; + f[3] = v[1]; + AddQuadFace(nextCellId, f, mesh); + } + if (faceHasQuad[5]) + { + f[0] = v[4]; + f[1] = v[5]; + f[2] = v[6]; + f[3] = v[7]; + AddQuadFace(nextCellId, f, mesh); + } + + } // end if num faces > 0 + } + +#if DEBUG_PRINT + std::cout << "Number of terminations due to surface distance threshold: " << m_ProjectVertexTerminate[0] << std::endl; + std::cout << "Number of terminations due to maximum number of steps: " << m_ProjectVertexTerminate[1] << std::endl; +# if USE_ADVANCED_PROJECTION + std::cout << "Number of terminations due to too many sign swaps: " << m_ProjectVertexTerminate[2] << std::endl; +# endif +#endif +} + +/** Set a flag activating each vertex for the given face. */ +template +void +CuberilleImageToMeshFilter::SetVerticesFromFace(unsigned int face, bool * v) +{ + switch (face) + { + case 0: + v[0] = true; + v[4] = true; + v[7] = true; + v[3] = true; + break; + case 1: + v[0] = true; + v[1] = true; + v[5] = true; + v[4] = true; + break; + case 2: + v[1] = true; + v[2] = true; + v[6] = true; + v[5] = true; + break; + case 3: + v[2] = true; + v[3] = true; + v[7] = true; + v[6] = true; + break; + case 4: + v[0] = true; + v[3] = true; + v[2] = true; + v[1] = true; + break; + case 5: + v[4] = true; + v[5] = true; + v[6] = true; + v[7] = true; + break; + } +} + +/** Get the vertex lookup index from the given index and vertex number. */ +template +typename TInputImage::IndexType +CuberilleImageToMeshFilter::GetVertexLookupIndex( + unsigned int vertex, + typename TInputImage::IndexType index) +{ + IndexType result(index); + switch (vertex) + { + case 0: + break; + case 1: + result[0] += 1; + break; + case 2: + result[0] += 1; + result[1] += 1; + break; + case 3: + result[1] += 1; + break; + case 4: + result[2] += 1; + break; + case 5: + result[0] += 1; + result[2] += 1; + break; + case 6: + result[0] += 1; + result[1] += 1; + result[2] += 1; + break; + case 7: + result[1] += 1; + result[2] += 1; + break; + } + return result; +} + +/** Add a vertex to the given mesh. Increments point identifier. */ +template +void +CuberilleImageToMeshFilter::AddVertex( + typename TOutputMesh::PointIdentifier & id, + typename TInputImage::IndexType index, + const TInputImage * image, + TOutputMesh * mesh) +{ + PointType vertex; + image->TransformIndexToPhysicalPoint(index, vertex); + SpacingType spacing = image->GetSpacing(); + vertex[0] -= (spacing[0] / 2.0); + vertex[1] -= (spacing[1] / 2.0); + vertex[2] -= (spacing[2] / 2.0); + if (m_ProjectVerticesToIsoSurface) + { + ProjectVertexToIsoSurface(vertex); + } + mesh->GetPoints()->InsertElement(id++, vertex); +} + +/** Add quadrilateral face to the given mesh. Increments cell identifier. */ +template +void +CuberilleImageToMeshFilter::AddQuadFace( + typename TOutputMesh::CellIdentifier & id, + typename TOutputMesh::PointIdentifier face[4], + TOutputMesh * mesh) +{ + if (m_GenerateTriangleFaces) + { + // Get vertices + PointType v[4]; + for (unsigned int i = 0; i < 4; i++) + { + v[i] = mesh->GetPoints()->GetElement(face[i]); + } + + // Split the quad along the longest edge to avoid skinny triangles + PointIdentifier face1[3]; + PointIdentifier face2[3]; + if (v[0].SquaredEuclideanDistanceTo(v[2]) >= v[1].SquaredEuclideanDistanceTo(v[3])) + { + face1[0] = face[0]; + face1[1] = face[1]; + face1[2] = face[3]; + face2[0] = face[1]; + face2[1] = face[2]; + face2[2] = face[3]; + } + else + { + face1[0] = face[0]; + face1[1] = face[1]; + face1[2] = face[2]; + face2[0] = face[0]; + face2[1] = face[2]; + face2[2] = face[3]; + } + + // Add triangle 1 cell + TriangleCellAutoPointer tri1; + tri1.TakeOwnership(new TriangleCellType); + tri1->SetPointIds(face1); + mesh->SetCell(id++, tri1); + // mesh->SetCellData( id, (OutputPixelType)0 ); + + // Add triangle 2 cell + TriangleCellAutoPointer tri2; + tri2.TakeOwnership(new TriangleCellType); + tri2->SetPointIds(face2); + mesh->SetCell(id++, tri2); + // mesh->SetCellData( id, (OutputPixelType)0 ); + } + else + { + // Add quateraleral cell + QuadrilateralCellAutoPointer quad1; + quad1.TakeOwnership(new QuadrilateralCellType); + quad1->SetPointIds(face); + mesh->SetCell(id++, quad1); + // mesh->SetCellData( id, (OutputPixelType)0 ); + } +} + +/** Project vertex to the iso-surface by stepping along normal. */ +template +void +CuberilleImageToMeshFilter::ProjectVertexToIsoSurface(PointType & vertex) +{ +#if USE_ADVANCED_PROJECTION + // Set up + bool done = false; + double step = m_ProjectVertexStepLength; + unsigned int numberOfSteps = 0; + PointType temp[2]; + PointType tempBest; + InterpolatorOutputType value[2]; + double diff[2]; + double diffBest = -1.0; + unsigned int i, swaps = 0; + int previousi = -1; + + while (!done) + { + // Compute normal vector + GradientPixelType normal = m_GradientInterpolator->Evaluate(vertex); + normal.Normalize(); + + // Step along both directions of normal + for (i = 0; i < InputImageType::ImageDimension; i++) + { + temp[0][i] = vertex[i] + (normal[i] * +1.0 * step); + temp[1][i] = vertex[i] + (normal[i] * -1.0 * step); + } + step *= m_ProjectVertexStepLengthRelaxationFactor; + + // Compute which direction moves vertex closer to iso-surface value + value[0] = m_Interpolator->Evaluate(temp[0]); + value[1] = m_Interpolator->Evaluate(temp[1]); + diff[0] = vnl_math_abs(value[0] - m_IsoSurfaceValue); + diff[1] = vnl_math_abs(value[1] - m_IsoSurfaceValue); + i = (diff[0] <= diff[1]) ? 0 : 1; + if (previousi < 0) + previousi = i; + swaps += (int)(previousi != i); + vertex = temp[i]; + + // Determine whether vertex is close enough to iso-surface value + done |= diff[i] < m_ProjectVertexSurfaceDistanceThreshold; +# if DEBUG_PRINT + if (done) + m_ProjectVertexTerminate[0]++; +# endif + if (done) + break; + + // Determine whether we have done enough steps + done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; +# if DEBUG_PRINT + if (done) + m_ProjectVertexTerminate[1]++; +# endif + if (done) + break; + + // Determine whether there has been too many sign swaps (oscillating) + done |= (swaps >= 5); +# if DEBUG_PRINT + if (done) + m_ProjectVertexTerminate[2]++; +# endif + if (done) + break; + } +#elif USE_LINESEARCH_PROJECTION + + // Set up + unsigned int i, j, k; + GradientPixelType normal; + InterpolatorOutputType value; + PointType temp, bestVertex; + double sign, d, metric, bestMetric = 10000; + + // Compute normal vector + normal = m_GradientInterpolator->Evaluate(vertex); + normal.Normalize(); + + // Search on both sides of the line + for (sign = -1.0; sign <= 1.0; sign += 2.0) + { + k = (sign == -1.0) ? 0 : 1; + for (j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; j++) + { + // Compute current location along line + d = (double)j / ((double)m_ProjectVertexMaximumNumberOfSteps / 2.0); + for (i = 0; i < InputImageType::ImageDimension; i++) + { + temp[i] = vertex[i] + (normal[i] * sign * m_ProjectVertexStepLength * d); + } + // Compute metric (combination of difference and distance) + value = m_Interpolator->Evaluate(temp); + metric = vnl_math_abs(value - m_IsoSurfaceValue); // Difference + // metric /= NumericTraits::max(); // Normalized difference + // metric /= d; // Distance + + // Determine if current position is the "best" + if (metric < bestMetric) + { + bestMetric = metric; + bestVertex = temp; + } + } + } + vertex = bestVertex; + +#else + // Set up + bool done = false; + double sign = 1.0; + double step = m_ProjectVertexStepLength; + unsigned int i, numberOfSteps = 0; + GradientPixelType normal; + InterpolatorOutputType value; + + while (!done) + { + // Compute normal vector + normal = m_GradientInterpolator->Evaluate(vertex); + normal.Normalize(); + + // Compute whether vertex is close enough to iso-surface value + value = m_Interpolator->Evaluate(vertex); + done |= vnl_math_abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; +# if DEBUG_PRINT + if (done) + m_ProjectVertexTerminate[0]++; +# endif + if (done) + break; + + // Step along the normal towards the iso-surface value + sign = (value < m_IsoSurfaceValue) ? +1.0 : -1.0; + for (i = 0; i < InputImageType::ImageDimension; i++) + { + vertex[i] += (normal[i] * sign * step); + } + step *= m_ProjectVertexStepLengthRelaxationFactor; + done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; +# if DEBUG_PRINT + if (done) + m_ProjectVertexTerminate[1]++; +# endif + } +#endif +} + +/** Compute gradient image. */ +template +void +CuberilleImageToMeshFilter::ComputeGradientImage() +{ + if (m_ProjectVerticesToIsoSurface && m_GradientInterpolator.IsNull()) + { + typename GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); + gradientFilter->SetInput(Superclass::GetInput(0)); +#if USE_GRADIENT_RECURSIVE_GAUSSIAN + gradientFilter->SetSigma(m_MaxSpacing * 1.0); + gradientFilter->SetNormalizeAcrossScale(true); +#endif + gradientFilter->Update(); + m_GradientInterpolator = GradientInterpolatorType::New(); + m_GradientInterpolator->SetInputImage(gradientFilter->GetOutput()); + gradientFilter->GetOutput()->DisconnectPipeline(); + gradientFilter = NULL; + } +} + +/** PrintSelf. */ +template +void +CuberilleImageToMeshFilter::PrintSelf(std::ostream & os, Indent indent) const +{ + Superclass::PrintSelf(os, indent); + + os << indent + << "IsoSurfaceValue: " << static_cast::PrintType>(m_IsoSurfaceValue) + << std::endl; + os << indent << "GenerateTriangleFaces: " << static_cast::PrintType>(m_GenerateTriangleFaces) + << std::endl; + os << indent + << "ProjectVerticesToIsoSurface: " << static_cast::PrintType>(m_ProjectVerticesToIsoSurface) + << std::endl; +} + +} // namespace itk + +#endif diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt new file mode 100644 index 00000000000..933a869d40b --- /dev/null +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -0,0 +1,366 @@ +#Set output path +SET(TESTING_OUTPUT_PATH "${Cuberille_BINARY_DIR}/Testing/Temporary") +MAKE_DIRECTORY(${TESTING_OUTPUT_PATH}) + +# Configure the Executables +ADD_EXECUTABLE( CuberilleTest01 CuberilleTest01.cxx ) +TARGET_LINK_LIBRARIES( CuberilleTest01 ${ITK_LIBRARIES} ITKQuadEdgeMesh ) + +# Configure the Tests +ADD_TEST( + Cuberille_Blob0_00 + CuberilleTest01 + Test01 + ${DATA_PATH}/blob0.mha + ${TESTING_OUTPUT_PATH}/blob0-01.vtk + 200 # Iso-surface value + 8 # Expected number of points + 6 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Blob1_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/blob1.mha + ${TESTING_OUTPUT_PATH}/blob1-01.vtk + 200 # Iso-surface value + 12 # Expected number of points + 10 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Blob2_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/blob2.mha + ${TESTING_OUTPUT_PATH}/blob2-01.vtk + 200 # Iso-surface value + 14 # Expected number of points + 12 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Blob3_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/blob3.mha + ${TESTING_OUTPUT_PATH}/blob3-01.vtk + 200 # Iso-surface value + 122 # Expected number of points + 180 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Blob4_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/blob4.mha + ${TESTING_OUTPUT_PATH}/blob4-01.vtk + 200 # Iso-surface value + 2124 # Expected number of points + 2122 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_MarschnerLobb_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/marschnerlobb.mha + ${TESTING_OUTPUT_PATH}/marschnerlobb-01.vtk + 55 # Iso-surface value + 20524 # Expected number of points + 22104 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 200 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Fuel_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/fuel.mha + ${TESTING_OUTPUT_PATH}/fuel-01.vtk + 15 # Iso-surface value + 5302 # Expected number of points + 5316 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Fuel_02 + CuberilleTest01 + Test01 + ${DATA_PATH}/fuel.mha + ${TESTING_OUTPUT_PATH}/fuel-02.vtk + 15 # Iso-surface value + 5302 # Expected number of points + 5316 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Fuel_03 + CuberilleTest01 + Test01 + ${DATA_PATH}/fuel.mha + ${TESTING_OUTPUT_PATH}/fuel-03.vtk + 15 # Iso-surface value + 5302 # Expected number of points + 10632 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_HydrogenAtom_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/hydrogenAtom.mha + ${TESTING_OUTPUT_PATH}/hydrogenAtom-01.vtk + 15 # Iso-surface value + 29880 # Expected number of points + 29874 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Neghip_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/neghip.mha + ${TESTING_OUTPUT_PATH}/neghip-01.vtk + 55 # Iso-surface value + 15146 # Expected number of points + 15136 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Neghip_02 + CuberilleTest01 + Test01 + ${DATA_PATH}/neghip.mha + ${TESTING_OUTPUT_PATH}/neghip-02.vtk + 55 # Iso-surface value + 15146 # Expected number of points + 15136 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Neghip_03 + CuberilleTest01 + Test01 + ${DATA_PATH}/neghip.mha + ${TESTING_OUTPUT_PATH}/neghip-03.vtk + 55 # Iso-surface value + 15146 # Expected number of points + 30272 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Nucleon_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/nucleon.mha + ${TESTING_OUTPUT_PATH}/nucleon-01.vtk + 140 # Iso-surface value + 3504 # Expected number of points + 3500 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Nucleon_02 + CuberilleTest01 + Test01 + ${DATA_PATH}/nucleon.mha + ${TESTING_OUTPUT_PATH}/nucleon-02.vtk + 140 # Iso-surface value + 3504 # Expected number of points + 3500 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Nucleon_03 + CuberilleTest01 + Test01 + ${DATA_PATH}/nucleon.mha + ${TESTING_OUTPUT_PATH}/nucleon-03.vtk + 140 # Iso-surface value + 3504 # Expected number of points + 7000 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Silicium_01 + CuberilleTest01 + Test01 + ${DATA_PATH}/silicium.mha + ${TESTING_OUTPUT_PATH}/silicium-01.vtk + 85 # Iso-surface value + 20036 # Expected number of points + 20024 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Silicium_02 + CuberilleTest01 + Test01 + ${DATA_PATH}/silicium.mha + ${TESTING_OUTPUT_PATH}/silicium-02.vtk + 85 # Iso-surface value + 20036 # Expected number of points + 20024 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +ADD_TEST( + Cuberille_Silicium_03 + CuberilleTest01 + Test01 + ${DATA_PATH}/silicium.mha + ${TESTING_OUTPUT_PATH}/silicium-03.vtk + 85 # Iso-surface value + 20036 # Expected number of points + 40048 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps +) + +#ADD_TEST( +# Cuberille_Engine_01 +# CuberilleTest01 +# Test01 +# ${DATA_PATH}/engine2.mha +# ${TESTING_OUTPUT_PATH}/engine2-01.vtk +# 115 # Iso-surface value +# 319918 # Expected number of points +# 640112 # Expected number of cells +# 0 # Generate triangle faces +# 1 # Project vertices to iso-surface +# 0.2 # Surface distance threshold +# 0.24 # Step length +# 0.95 # Step length relaxation factor +# 100 # Maximum number of steps +#) + +#ADD_TEST( +# Cuberille_Bunny_01 +# CuberilleTest01 +# Test01 +# ${DATA_PATH}/bunny3.mha +# ${TESTING_OUTPUT_PATH}/bunny3-01.vtk +# 155 # unsigned char +# #1550 # signed short +# 1021438 # Expected number of points +# 2042892 # Expected number of cells +# 1 # Generate triangle faces +# 1 # Project vertices to iso-surface +# 0.10 # Surface distance threshold +# 0.15 # Step length +# 0.85 # Step length relaxation factor +# 25 # Maximum number of steps +#) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx new file mode 100644 index 00000000000..4706baa221a --- /dev/null +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -0,0 +1,221 @@ +/*========================================================================= + +Program: Insight Segmentation & Registration Toolkit +Module: $RCSfile: CuberilleTest01.cxx,v $ +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) Insight Software Consortium. All rights reserved. +See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#if defined(_MSC_VER) +# pragma warning(disable : 4786) +#endif + +#define _SCL_SECURE_NO_WARNINGS + +#define USE_BSPLINE_INTERPOLATOR 0 +#define USE_MARCHING_CUBES 0 +#define USE_QUAD_EDGE_MESH 0 +#define USE_DECIMATION 0 + +#include +#include + +#ifndef NO_TESTING +# include "itkTestMain.h" +#endif + +#include "itkTimeProbe.h" +#include "itkImage.h" +#include "itkMesh.h" +#include "itkQuadEdgeMesh.h" +#include "itkCuberilleImageToMeshFilter.h" +#include "itkBinaryThresholdImageFilter.h" +#include "itkBinaryMask3DMeshSource.h" +#include "itkImageFileReader.h" +#include "itkImageFileWriter.h" +#include "itkVTKPolyDataWriter.h" +#include "itkLinearInterpolateImageFunction.h" +#include "itkBSplineInterpolateImageFunction.h" +#include "itkQuadEdgeMeshQuadricDecimation.h" +#include "itkQuadEdgeMeshDecimationCriteria.h" + +#ifndef NO_TESTING +void +RegisterTests() +{ + REGISTER_TEST(Test01); +} +#endif + +int +Test01(int argc, char * argv[]) +{ + try + { + // Typedefs + const unsigned int Dimension = 3; + typedef unsigned char PixelType; + // typedef signed short PixelType; + typedef itk::Image ImageType; +#if USE_QUAD_EDGE_MESH | USE_DECIMATION + typedef itk::QuadEdgeMesh MeshType; +#else + typedef itk::Mesh MeshType; +#endif + typedef itk::ImageFileReader ImageFileReaderType; + typedef itk::VTKPolyDataWriter MeshFileWriterType; +#if USE_BSPLINE_INTERPOLATOR + typedef itk::BSplineInterpolateImageFunction InterpolatorType; +#else + typedef itk::LinearInterpolateImageFunction InterpolatorType; +#endif + typedef itk::CuberilleImageToMeshFilter CuberilleType; + typedef itk::BinaryMask3DMeshSource MarchingCubesType; + typedef itk::BinaryThresholdImageFilter BinaryThresholdFilterType; + + // Read command-line parameters + if (argc < 6) + { + std::cout << "USAGE: " << argv[0]; + std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells"; + std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] "; + std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]"; + std::cout << std::endl; + return EXIT_FAILURE; + } + int arg = 1; + char * FilenameInputImage = argv[arg++]; + char * FilenameOutputMesh = argv[arg++]; + PixelType IsoSurfaceValue = atoi(argv[arg++]); + unsigned int ExpectedNumberOfPoints = atoi(argv[arg++]); + unsigned int ExpectedNumberOfCells = atoi(argv[arg++]); + bool GenerateTriangleFaces = true; + if (argc > arg) + GenerateTriangleFaces = atoi(argv[arg++]); + bool ProjectToIsoSurface = true; + if (argc > arg) + ProjectToIsoSurface = atoi(argv[arg++]); + double SurfaceDistanceThreshold = 0.5; + if (argc > arg) + SurfaceDistanceThreshold = atof(argv[arg++]); + double StepLength = 0.25; + if (argc > arg) + StepLength = atof(argv[arg++]); + double StepLengthRelax = 0.95; + if (argc > arg) + StepLengthRelax = atof(argv[arg++]); + unsigned int MaximumNumberOfSteps = 50; + if (argc > arg) + MaximumNumberOfSteps = atoi(argv[arg++]); + + // Read input image + std::cout << "Reading input image: " << FilenameInputImage << std::endl; + ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); + reader->SetFileName(FilenameInputImage); + reader->UpdateLargestPossibleRegion(); + ImageType::Pointer input = reader->GetOutput(); + input->DisconnectPipeline(); + + // Create output mesh + MeshType::Pointer outputMesh = NULL; + itk::TimeProbe time; +#if USE_MARCHING_CUBES + + // Create marching cubes + std::cout << "Creating marching cubes mesh..." << std::endl; + BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New(); + threshold->SetInput(input); + threshold->SetLowerThreshold(IsoSurfaceValue); + threshold->SetUpperThreshold(itk::NumericTraits::max()); + threshold->SetInsideValue(itk::NumericTraits::One); + threshold->SetOutsideValue(itk::NumericTraits::Zero); + threshold->UpdateLargestPossibleRegion(); + MarchingCubesType::Pointer marching = MarchingCubesType::New(); + marching->SetInput(threshold->GetOutput()); + time.Start(); + marching->Update(); + time.Stop(); + outputMesh = marching->GetOutput(); + outputMesh->DisconnectPipeline(); +#else + + // Create cuberille mesh filter + std::cout << "Creating cuberille mesh..." << std::endl; + CuberilleType::Pointer cuberille = CuberilleType::New(); + cuberille->SetInput(input); + cuberille->SetIsoSurfaceValue(IsoSurfaceValue); + InterpolatorType::Pointer interpolator = InterpolatorType::New(); +# if USE_BSPLINE_INTERPOLATOR + interpolator->SetSplineOrder(3); +# endif + cuberille->SetInterpolator(interpolator); + cuberille->SetGenerateTriangleFaces(GenerateTriangleFaces); + cuberille->SetProjectVerticesToIsoSurface(ProjectToIsoSurface); + cuberille->SetProjectVertexSurfaceDistanceThreshold(SurfaceDistanceThreshold); + cuberille->SetProjectVertexStepLength(StepLength); + cuberille->SetProjectVertexStepLengthRelaxationFactor(StepLengthRelax); + cuberille->SetProjectVertexMaximumNumberOfSteps(MaximumNumberOfSteps); + time.Start(); + cuberille->Update(); + time.Stop(); + outputMesh = cuberille->GetOutput(); + outputMesh->DisconnectPipeline(); +#endif + +#if USE_DECIMATION + // Decimation + typedef itk::NumberOfFacesCriterion DecimationCriterionType; + DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); + decimateCriterion->SetTopologicalChange(false); + decimateCriterion->SetNumberOfElements(2000); + typedef itk::QuadEdgeMeshQuadricDecimation DecimationType; + DecimationType::Pointer decimate = DecimationType::New(); + decimate->SetInput(outputMesh); + decimate->SetCriterion(decimateCriterion); + decimate->Update(); +#endif + + // Write mesh + std::cout << "Writing output mesh: " << FilenameOutputMesh << std::endl; + MeshFileWriterType::Pointer writer = MeshFileWriterType::New(); +#if USE_DECIMATION + writer->SetInput(decimate->GetOutput()); +#else + writer->SetInput(outputMesh); +#endif + writer->SetFileName(FilenameOutputMesh); + writer->Update(); + + // Assert number of points/cells + std::cout << "Polygonization took " << time.GetMeanTime() << " seconds" << std::endl; + std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices "; + std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl; + if (ExpectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != ExpectedNumberOfPoints) + { + std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfPoints << " points, but found " + << outputMesh->GetNumberOfPoints() << std::endl; + return EXIT_FAILURE; + } + if (ExpectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != ExpectedNumberOfCells) + { + std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfCells << " cells, but found " + << outputMesh->GetNumberOfCells() << std::endl; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; + } + catch (itk::ExceptionObject & err) + { + std::cerr << "ExceptionObject caught !" << std::endl; + std::cerr << err << std::endl; + return EXIT_FAILURE; + } +} diff --git a/Modules/Filtering/Cuberille/test/Input/blob0.mha b/Modules/Filtering/Cuberille/test/Input/blob0.mha new file mode 100644 index 0000000000000000000000000000000000000000..389a3d84475edcd8ef139ce175208dbe7c0325d2 GIT binary patch literal 410 zcmeZI%1TWx38^edRj^g?%uP&B(X~n4}Kq;^Umvd@fNotXQT2OvTVo7Fx9$dmPFR>&)H#0dg$G<2O zs0bz-LE literal 0 HcmV?d00001 diff --git a/Modules/Filtering/Cuberille/test/Input/blob1.mha b/Modules/Filtering/Cuberille/test/Input/blob1.mha new file mode 100644 index 0000000000000000000000000000000000000000..0d40cfc358c170580a884ba655c51b11a0826abc GIT binary patch literal 409 zcmeZI%1TWx38^edRj^g?%uP&B(X~n4}Kq;^Umvd@fNotXQT2OvTVo7Fx9$dmPFR>&)H#0dg$G<2O zs0bz-(X~n4}Kq;^Umvd@fNotXQT2OvTVo7Fx9$dmPFR>&)H#0dg$G<2O zs0bz-(X~n4}Kq;^Umvd@fNotXQT2OvTVo7Fx9$dmPFR>&)H#0dg$G<2O zs0bz-|+2`40=ES literal 0 HcmV?d00001 diff --git a/Modules/Filtering/Cuberille/test/Input/blob4.mha b/Modules/Filtering/Cuberille/test/Input/blob4.mha new file mode 100644 index 0000000000000000000000000000000000000000..adf54d97174707e94cc460919bfd128b1738e537 GIT binary patch literal 844 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úY6tPo&5 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 literal 0 HcmV?d00001 diff --git a/Modules/Filtering/Cuberille/test/Input/neghip.mha b/Modules/Filtering/Cuberille/test/Input/neghip.mha new file mode 100644 index 0000000000000000000000000000000000000000..f350b557bd0233aac6e4395d2b7e19cba1e83e6b GIT binary patch literal 81079 zcmb@sWl$Vp(>9vm?(P<3f#8eV;%~zo0Y~OWv_to8(im9!InLF6W*@8-#O3uN=$^u1M(#FB<{fY|(WaDV!>LY35 zZt{K!cJ;9Mza$_ZcMBC)a|>4`4bZz*+QiELKzX>{S0f{^!pBcSV$>z($%9vf}FhBma+G(l+-0u|QEpLR=BWJNL53E0b*1=R`2i zZHE6xF8z;OD|RAtV=M$o47w=ApbwdD^q(x%f|F$}bdxRC8(B8>YTW;GeobQe?$H_8 z{n&C-`&8>i_*7b1dtQ5eb{5go*xDybhn4H&a}E~xp?&idKu&&TEhD%wAMe>*^8er0 z%DsVhSe%-PTW>E=gqMtY;0gHm+luCIc0JAgUrbpAxcGl(^CrmiwgugK-t6_eWKXPq z?-irM`zA?Kq*_l*z9I^XT_nqrE(=kQmK}5Y-M`kT z3U57jgc7a~AOJ#OEZ}Gwz-6kHU_f{^KlX2;aI++4O_Dh2?P{b2aZZvN!`%=RA(mjB zGO(QoE42TE0E*@$KvmYA0sMbiA4p#^5}+jWJ@2E2sGnc57VlEGAPk9hQp6J|7w{%n zH0V^-toe7ls@auyhZuh+PpyNNFBP)TjD~Kn=X=XZ*~MDf{6?_1jh!WrU)`iTZ;LOa zmgtB~y=5EaxIFg(1<6tFlWCH%S+}MHob4FCJVc#3Lk?JG-U6oM>`NIfQ|?TpEbbm+A&l(Zrj&+2&? zU_9VJ(IB(>wLE33MH*RII*l_$t(>0chP}PeyCCAxV%B&heIR)w!bgW5YGkS6bgBO} z{QIm#b8|)^yxxWaxYp|nxC6GUdWAQ>TQDS*$nP~>6AtEMvW=8fP_Y#LK%Oc#l66*r zV4Bvll6@`=Ln6L+90+o^lYnHC;1Dad0fE6T6=K-Yc)qpG0(r_+S|E9?uY6l!XalTy z1Lm!nN?1+~aGUL#be!ROR?Ta9ju}Ii3st0PCU3aS#p8G+q2t$&@FC)T->WsoC3A-P zWTh)r&!&`(7;$2xLJU*1=0Av4qw$e+j2_)am_|zq9eADbiW0HVg>d#dvrwQSZUI~L zx5%;%qVWV`P^T38FPce!xw`_?TIbWiu4&VyiQ(z0Agr-e+ZOrtt?v3X$ztj2vj6p% zyE=lJ-pCuGOk1V!h9gIdufqr$)7J@^UwX^D;_sE)Ke-R~(SC@arm+q@la>sdbDQf% zfy{S)Q;v>@$|c&gaTV;P9WZzoJUCEjD2cwPIc1YN^`j3f1gsu#m7p~z59vU zJ(XgK+S-?Tl{Wyh~#r0{GW zPqi)CCX^!-=<52G<=tOChxuX*OstJB)#RM=OZ8vioo%%0PIJm73@o5Gw5 zc)&n=^f*2Ul)>G@M2SvAuwvIeJwN+wVHy}|_{lE51^LLgOEg`++*CYa`ooMm?GudE zn7dZS_p4?SUOqX}2-%|GwqWJodl9Hbx~>{8r% z+_CJQ{;Zdx(b6&9A+ur(gJnMO8l}%tgxJ{oom|sZsx+)sQF>a~J52cVd0IBgr9Y$o zK2;(SF?60OAzl>QsX<0a3qu1)c|_S1Nj3(zy%C+QpQ)EoY|YgBPC6)Kj@oXwj+->I zlcD~I+)t*5+T*vnTJ=g5^$dsw+y@_UW2De8N3+B#tHsca0a)_kX!aMp?L#_FHoioR zU20Xl{Zo?Uqp%(x-f5hmkg;Zjlw}MJlp4#Uk5#CfmHebXze1a(tA-9SBS7K>la33} zB1KsJZ;T)DIV8;yswPR5OPf0E0heys50#yqJYqR3=D(yDM)Lpu?XC6a+~dADrxCUycTXgoK1C|)lCdJl$guqW`jewgsyx*Z zfr;8GSKsP^p-bk6R%^}YSu`(tSYT8eP$tw{C`sQ6)8dt$ptYK1I@TQ$q72)3?(j%~ z%cPXAQ{?1sYg^rydAo_8LxGCiuyOg#Tmj0DT>+tPRLEz?7N>; zB3;9wq1DT3~Bz#zkw7lZpOF+R^zCdEL??R#ORpQgl67(n}Vi z%dW}M;Ux_jFP|ju&>wrC>Er?|IV=f$x9=9>FPFdIOa0?+xiBN6+HqR@PlH17H3@rgIC8c)2{@zGRhDXkSX1hmnjr^tZ~1UqQnhg zPduvBT?@MZ-pfxr3)UaDSfP<^4MzEsQ2#j_WQA&pc;Z=Gz!&iIHXkxS6SmqZCp(IZ zLxngaV=QSKv)kbexLQrpEv`fDAX3e>#n6H9#kq zW{Y38t0au%!%*vcSK#auq}65H@_+D2;B|>buLoi>*?M@Bou z(lIZL+u4W_MdF$28gd_C=+SX_CR_l@r9lgY=&*6o^_o#pk|2~>ZYU_ae`BiNUnvxE zI2}7(3RtAhPsE7_O$Fd>C(0gQaz?;tl8k)u0Mo@)f#JSDbeE)S@qP8O{aCm?imO0l z?I(I*YR>;`vn)X|%TG0T+kz6&>qw0@*Kw5Tc*0J}M_k0_|J^vbidiqwdLORK-!(gvEA3i~z>XHR=$ zqmbZAPK>Vuk?+zEb+kG9bO5(>7q20S?pniF@`W1cU;QGwp9rSvQi}$4V&PJ{=tniW?sr}m$Rdt@FM*+WG-1da`j^p z!X#kS7*3CypNe7U^R2{FfnYPIR%|DBJua|2@%RoNCvCle+;FSo-59vq z0Kqltwsw8b7(?`012v5xyO+nS?Y)0+>K!12*J+^KILPN*MaWR$llbQU0lyx_Qrycf z3~2gZNX}|8l>{V)^LE20A{i;mm{;Alpl}K}U2jxUrUZ45;p3K1`dzeH=#lEe|C%Ka zYboLvW$jYip5h3oEh~}2SuW@MAM+)`Nut2=Xt3yWR13{Pcf6`$3;nI3(!L3rRsBl% zc)`nd=3*9yG2zXbW|f6QNl7GpVvvvBBQ-Ri!rcqyX%k}=qy|+#D)dL3zkp|A+L7@Y zHMAi~kcc0 z>Yr#$KU7L1Gbxo4asEa~V+I3wSh#u{KE@7Qtl==qEtms`NQ_As!Ct+Y-O7?F0)-~* zy5yhRz8`vSOd%A8);REl=@iCQh|O3w3-o-{K^GzU+2uf=mt7BZB#sD;E%T^f=$$5y zD*H8}uFaGY`#YB)z<}6s&a%EUq(Ogn?)TEtZ&ei$3g6Z7K`*qKiOU-ld=>zb^`YBMPDf zY3eM7+$}%&)e7J4{UMCM$)cPJr&g;R{{xWC`Ox|TwvM~jd78>!d=kAIRG1ZZJv)7j zt|j75UJet-FGXL~);J|knmOySauWG7VOJ;Ob#b}Vu~S+gEl>4|Qy(XV>*+`I9_tA#&3^ z;$kO@CK__`YpfSh>gNSN#5e1jhZv{-R&jzPUN|h-+K$p?M};M&fw-^<#mQ6y0?4-^_>rKB!o6f!`pQFYg)KD3E3 zI#O&RWqct#D$ut*S>OTw7QmVDTjRtiS>zX~2ryVys&~Jcy(~jQDmnrN4D812&@5HN zk9MZTt{xOwTrRKph#%O35+GZlX#krTdnj#c&|$1cqPDMDrOMtsa8W>^=s*H+N`$&r zuw$E>^~Y;VNk}R651q@x?;thQ`d>Hl^)`#gFlC@@l5JF=T(GSf(bJ)yplJ6d zgbm93uA$MH-3rjrj6&|7bL;vy0QQ$M73AURW8=HGMDQ$^k+8dZ$Db2P8(6cOqpiu2 zi|k4A2J9Qw?X{Y)2__;5RyLTC1H;j3Oq$mWC<=COJOrzy;o2lavSUhqT6J(%hXOfM zro)@)4(AIqIFpOec{0!zeLfeXIgs?MUn7q+L9X-jYe518&bq{$TL8^MB?!-k(7t=a zJ6^K1v~8}-MC&lw_~O8Y5!yW8%h$4VfbvN|=0wY@uI9=xkMv4DlFBw^l@8qkQFncz zjAPAz{$T7Nzr<>`pzn39m-WzK{KLe0wL~g_#bGY?*vb4Tv zcrEa|*nZjx_%p6>F&UawIHW`R>XHHsW!`CX|Gh9Y+x_!VX2=iOO75tat3#-=dWUjq-X^Tm^KAi761Y$LP>yRg~GzShjG>`0K1y`|SkhkN5W?Zm?$;&=B+mA57mOAqc`3ysXctSIG^2#=}&+oB__?dt0jiv65S?{7pAA!7-- zSRIq}kW?jP$@LpiA3%$YDnw9yAQo48cd8n#NH85~vwBrYahc}PK_?AqRnVHVUX=Vh zWQj*os&%LB9{C<^T+O9q;9~8`L#pyQ+c5cIMBT*j^Io={VtC%F^fu@|?aNbk=m4#O zhAzOO6h#W}p-#V4Jq3!xSrsN*#E*YI4-zII(SQ~Q;$*)J$Sg%9#-`+FAPZsHz`eS( zpnv{~Mz!W7@q-$1w7M`bj!5%Ng)Fv@xepX&)n|GuF!hHH z@6(XV>v;yksfiMb?&`_fA>f_ zyfqk0uTFgo(%KX@f^=Qu<@+XYa`hx?a5=9oxuX#k5t_FFu!*7xC|>g^yHGvCdw$BZ zO_WTfOk-SU>__ulAosPD9{L=D1}pBmx|frRHU}(sMOfp_6KqYdco?J zT}9t9NCx&aAFu&ez&j$Ye(^k;y>OH9nJSt7mwX2*n z&1}|vTc0PlqVRnly7_wDMCo@ndbd39Uuqw>DH|@MM|_&suru<2%1hz>mgcn+xc~RC ziuSp15-d2>@S+Y^^+|`AmA}2&Zgco|FzI^$*q>W49__PPSfNg|y-G4w(Or|ti=>;bG>|%Ey z>kzjx@;WE_1s3oV-h7%g(f6f7EKCh*OPYuvNJ9hG3yJhf6diMzrRP+*6ghS5dPN+3 zOT5+9IiTH~9Jz6*2#DW3LCIVY*oskPZ!IM=hxe%CY@gzNbg6km`V21kOrKS0(Q|O8 z8?Zki3eo^}4`?4M@iYi(VnoX9Dd4M_T31lp1*yuZEs_n(ENbU1OT@z;qL9CBVDfUd zJT?ebtEZVtV!~OUnw)Y!V4=OYk2Fq@NB<@>nX(h1t3og|XaLD5GE^GFQF@tBd=G1^ zH(3Qi{z`EDyAOZF*X`Nv5gXQ&LNUd)WpvI!^7LBpeIlha%wGn?!h-yiY8pS&9l9kz z)TK0>^!kU9M#z2*3egC4c$9~0nMgRikewC9ier{Rdf{=DoFMBFKGVdv%j*A3=nFNz z>j0({w97S_f$G#=$@K`qbi7IJy(mUejV)D3bg<^2%he!xp5W4tb=jb4(h8)oWgB1a zuME+yq^6>QS@{|N)Tdh60KE5ScT69~!9`?caRKP3#>EoKhUII&Cna?u$uA8!713&S zJ8!N)4FWA=(aVOaCzp_m93|rB`7O+y2kY&i&oJyNmu*xDhvbt-9!FPzG%UK(dE#b0 zo~`*s5KlVBp!{Xz^3j-&MN7ZHvvfAvl75h?$6p(A;$GT%_^GSA_FQM!m@H&B;=yc| zkEpNI_`~b{V!1n1Q9-kZg~Yn7hvZlKfT8J$ic`o1=rcCWFhE$e&j06?scyx~+xZ)AyaduWSuA|Y>#M8ze zk?MIvP0WIxH#-eN!eDs>xrwrhbd<@3h*zRavcEbu<}ObL<=4HhOZ(d~w2-iZ-(&4e z9}#Ud&ZcU`ER6$i#wRg_UQTN!8J<`%j%go=bwAli0Ir=)>^B>#RJwnTlGX|NTn*Qr zIc`}teO!njr3sQ)$Rif7xzE8Ok-^W>LP@9W=J%E0}XhQ$PRWR}*L-is2CldQ-V^v1+RlMoGw>_-qM(Q{|5h5p_fQuDTyKR^7;R-5 zY*e31>2u=6rNDHy#UPJ3!xC`wxpb%kK~nq5pYE&*K<|HQCbL6hj(B>fLfchv3Q3x= z?IMl7btQ4Pl=jejhMgWOB*bZ2d&iXy@{t2&V_pV&W&6+jcgQ)#y__lTXtus2#8_{! zpNiM;L&S}VKYkQ%mI%@^v8E~w(}C4lM-i;VCWH~E8;XiUk|jVH#z&y7e1Z{=R|Joo z8{bOdFfkOFqYaz4>Qob~@m3Rz?IVvkze(gW*zQjb9w)Y;{H$HGXY_V#IeqFo{>Y2JZE@3Psc*ogUEOu7SO^hc!`fR zVj~qvD>%U(?B*zr_^x8sj2Jm1g|)Eba5%Os)%Kh#I+qw-)79E5AJJhr)RMr^)F1H5 zwaSJ+KEjk`h{&M9P?;52SG_-DnP6aXvM2;o&Pt5a7_(M;HA}ychB|FhCq3^c1l3~7 zvR)v4NdvpsuW%uJ!n_jQSgs5Qg8b#4;13#&$XKU;$w+LDD0-(@Z83vvTOg8Su?Ptwaj{C1rHew z5ySDuU{2iq{tOiEUNmRj2I}|gxpQj5DH01&z?+x{e*M73nE~rYC0|ZZXSOH7kG?@g zf%=iSZ+24BL4Q|VH!QXj>O5%4M~nz!?_M6REdhnIN~p`!)QWK%jFqKCOVVV0`%Px= zrhhy##yIxPKE#;3WPyZ5`K!>bs7Rj$PXQZVXOmh%Ni1~-xLr19{yOdi(Nb#_gHo`w z*XzQalI4$6b&BYO)P}pI75lPa&!5}vj5?z(f6stdcPILz##6@Ac}5$z$e%-BF!H({ zj(*LG`uy5?s4;`IuUb|Z+{P^~T6%`X911Gye?4jO+VT0bPHOci#+zLtz98Mk$-X`` z5}j90>3ueI)AO%En^N$rvrC}~FUbmav<0ANq<~yk*yZrKP4sT3t4`P@X|85;+nVlX zk@zBi^XrYoY~RZYDVvDS5lC>CS+Sou!;PkD<=t7 z3V9=nQ#ahJ9eO~N=nZSP&fK|*4HWB8W`2JEzOgT-ATgMp$&EiE@9mdqgbU~n3tiRl zm+8}6Z?x`ZdrPVWqtCjU(w}nP7zeCUs{yNv+gr}RVNMt{4@TEYF^XvK2~CNm_<%)2 zhZ*a5f8CXTG09s=P?xIBY?*ujFdegOT8kkGa;)R69Tl>oY!S(xO<}l@Q-`dZTL8ls zs+Lkw77Bk!@`cH|xg?9gJT`j2B5ABzYta*M5Ujv4G#Ih)Dd3>P-c+iIp`y;!>#-!V zpN%s}%B|ngTjsPeN=CFUIh#2K_aP%^;K&9~Gy1au1PT@4oH(#43xlX2ARDTspkVkg zd4U#~E2{CYacsk+uIF!)fJWcjHkUX8oBnts<;ZQ@2oLJY)&PHOQ0TDW_d)!7u_=O_ zp)=R6?az_RrD<*sVY zFJdDXzd~8lHp}YdLWhkvOB^IeE*fp^)G?E~%f2BrvKAja`Q%z^PJc0C)NrB1_V{** zk%2f>yXwFTu#I*{GI#b(LY4zqLg#*rMJqQi>iOvj!0plPbITsB8rMtUANqb=&E2l9 ziEHbZ8?;Gub&R(Gk>BVmM^5WRiv7G>RxohKH&5@xC-;1hc9rO5{mWHU-jM85FnFW))G;1XHRWrRuCA4Agz1S;YfXiilJKTpmacvP^O z2rrFF%q4`r$mw)rz;b-_3ZPHZ401ZxtDr{KK zjn+4RD*2c&dFK8aVp5JjIagY9kX_r_Or24%jT@WM7%j_4g!UZ=`e8F}h03O>GlbF1 z?WtFhK8K9}Cl&!F&iB1n+{K2mZ5^qSFO16k50mgfL{M|t-fV%sSnf-wljrKZU)^@6 z&u#~8m*s1dNt@Izmdy|Yc`GM-S4cv{zaxq^N+6i64jt%PEREv+9H}Qj-6woOo${^+@QlvcDvTzx&+G7i)_5dQa9)4XIF)Zgq6mD$*Gj2 zAwX`Q$|Wq5gs+1u!8Q~hBb)^fNZbY0$Z+Q?(jQ{?8B9WHz}9L#`OpFzlJ0#onw z486)VYO+|~549b#V?X9rVc21ulk1n&b&J${FZ&-T(GdPP=Co=h@dT4yI_|?pa-UW0 zc_4{{IAf4XI|;sGk1e%Fb)c|*B`%F?zwhS|2cmA?5Nq;imdd01;xHd;cE_70BKZkk zrf;N+_kzWb)f>Ao7XGS_UD(ep+bl5IPNT+Q9IH3y-!N9G=8GS(hxgZd#+7z?F8#=^h*J^oLy2S;q$TV7YWpMY*MPLHe2*d^&!ssQqd>YdqC^L>YG-~-8R5h# zV3tMuJ@0NDu|PVhLZYM2h}#of%#V+z(X=F;1P%%zUZ5%a` zQJg?GpxA@5nRLwCCIfYK5y>*8QQP|VOyF2uovEo)qKF03{1tSNFY%7#G5oF&Aepnc z@|wOe+q%-``dI}qWe}xNDhFuPYGo1%JWcq_!Tn1MKt4huEHCt%TncX{3UGY+QL zW}y#xctr%?YZeMSG#rc#);{1`&E)bmy9Ksm{aTM}QPL(O$94{HF@HKbFXAG+>GHk1 zFLKZjK1-}J98x*KmwV!5-v}7r$;y+N-Tk*0`eek{>K8~wN!?vbpk|80xxfQ<`bF1S z?n&0;e7G;;XduwxWx`1Kz&!^|3EnX7Q)%K-Co|ypemtN4vN}7|U`}iXtr_$zVd5b$ zYP9lweR&_%-3a>_zmMwRe}wb(pF>j54l%tn2{G0K(d>Q0VvRT$AU9xTZ&Hr5qUYV&w!hXowGp_+vHiR20as;+-=L2 zOdIm>T;=^L6FP}{#Pok7Hij7HI>m`NYi|d{#U?nf@}<3yeG{SvE4V+stAt2p3uj;& z+el6U)-pNgMI8bfPHvjTQzXrwpmLn4-KbAo>-wKlzD54AtjCX};F-c9hNS_lO(C=` zaf1i3F<{fkROKW?4KudIjEnQd#7v2(0p0;sGLTr|Fs1<#CTt_BSaJEWA0hQ|rC+Hd zG{W0%`PkJ%=4i8ElA>f90stTENORn6osp|NmgrZl#k&0XOP0yk*UbLnsFXGFbK~!E ziVv+;ntQuV6G9*8m-5hZePH3sD#&?=k>6-2qRShfO`$qt5Ht3#%|8W8SFFR46YD35 z%X}>G(S*o8JM%S|1C7ql&jH68xdcRzfp2*dd&6ch68|EJB;-onMr!l(uV?&2+(cQI zb?0g)F%XBK2X~Rh z80;A1M?q44{nXU>(%bahY!o+N*h?h&D1%SHI@sU_o$|Nr4_fU^D}(8 zho}s7^tKWr%Z+`hMIfxKGW&Wi(2{hZ7-Jeo1*syAR;m&Ga%r4e3Z~F5-rfO%C=E}i zgh`*KT-9Es@jY|0(#FvgZxe>-=rFl|n(&ISfJo*qoAI&VYX&l`l9g_%$ksz_WWRTk zQPWneZ{BJHHW^$$yZ%wNd0n32!(KElpI7bG>yRw+Dwk;`7YTHRGRUfu)`{2*T}E)m z47CYJ64ZL%yye4oS;~J*ff0rS(m8;4QGVvh6@91!#V{w|P*O04T{(;Pa@d&N*A%Me zzzfZQ0qxJ+g&Em6+BW@o!C18z5hz^-Tmv5R^&mT^<{=vcidIH`SVG#7o&j`E71a@_7<$Qvh+50@-dROZvvvE-I!3$@=}n#h;%+V-%XO^5dC4~Hsg z^Jo&;6u7UdRx#vK=57bxu0b`vV_aua0zNUq>crJ*pt0$73}=s!&!A6T^SK{Pt*88V zxZG}M?SQWZPbP4;NFe*9>|h}1iW6tio>?mYxC%DunpZt2pE-&rh&XAjsi72U_nQQN z#rUa-4lHq~1{DF*R?g!xA}rI}uGSE$_CA3;T*zgd_7CU8NTAkHtq;Q^tdr^zMBzNu*FlrY8b9ma0wp24AWurTu#x0k--}I9qc* zp^x5NfV0^f%k=jenY4x(lR?pP;PI{7s66Lhtr>OaUa$g5-E|+{P)SGt+~B zu_)=Ai9@Jjt!mhO)|dx+{D89dknpqQ*o8ZP-dI_L=W&z*hrjlk!8>{NNIiV!ngVP4 zy}T=1;)Nlk2nd&)b7;#f96@(XMOHK2XC+XZ$nbqZK!`*@e9_-A=1#l0zPPzkj5HwV zk*wfIcnDP(J-dr6c`pqs!_lz@P+;Cv8Sx~E zxx#Sf0=PYpa=fX4<7vK#GNTOT^xq>JsxYRAn=*nHF*ziWUQSr(f8%m)Xo6f?Woa}{ z&nYPiva|BCh_qB&QoYd#W3~w+v`w(|g~JZ#uK9~$px_2SV|Q0eM@LW7M0z+k%D^e{ zN$;TsNgkLu*St%O-)>S2r9?MRbNFjbHiN3;c3@+2DIz&4Z z5RO!CrfPW?g33r1HbzLESTSg1q{hWJtW|Sm z=?neT#-YU=n|aDeRMe+Q?{fx=1qAcA0cSe0d;X4Jwo-slc#8-U#vv+1n;{WRQANBJ zxAb}mPsRJG;Zs!|xs8f2GbF>jRubqIsQ!6SY7Lw=!zVDCUwMgWQXVnq*05sF^y3wU z4#l#TgTY1u^2eB5h)dGp_L+9+wXx??o^29w9T zcE~#3K3(_zJ&!aJ=*t8q4eWVjxBEs*FMkIo=eMQG=+`cFHU286{CbvThN}j9H#)B{ zDqk*Im4*JtL=|BNSH6a4p|9D_g3(efs=u=(G-*KfIb55rcq_2<5bm$1WwC2KJdJbF z5G$}0e5}`wpBQ@_tAYJa8nxsF=n9LBxOyP254wqSQcFmS&kJ;HS*>!i89CpPK5McE z-`#|h&G2D~ROJ@YtbQS4WxYrOb}o@-pS92GE2~FR%AS*GSIn-v&-3y3j-!u%;2{a# zIKZ&B{SqTk`3ji3BF|l~_+FhZNBsv$+GN8@DPoA;f$lCdRVT*N64#`U4eQ${F2<`S zV4%qpg+;nfjJcnTRffE?F8oFFdfdiFRBYOlTnoCUjgqI)JH4o*z0>Ub`N5GG6LDC# zvevJ}OiONcGo^^l2m*w%^wz75mH;2$icD#X4cjU(73b905<(7d?iIPw8p>!RH;*h- z*49CO&5}De`Xh78`k&~9y`#aO>5%n?9WwevPL}|D0y|h#m$aQD6o>%gXj$0+O;cv9 z@71VpdFOe4zXOu0vOUf2^5*Jhno7hUWiI)aEj*ggVP)7EPeu%agK_J*I8qmTzy&Tt zqI{a(JIBOUmG9D0_)g8yl@hhJa8Lg%_S7Ou6b-l>5`_4uc&d<)+>M&!w1`}uT^l?H zLu+=wU?b7!`R0dYUTYFKXQwPFrrTe+al%73g(oAY315zYNQ0GJ7wH~ycv65G<)l_s z?Ao|>@=g#Xrz)@N-nVW`;WRE|3}21?zrl6u~%o|ZM1DNlz6 ztJr6@huSF>fz-x;p<7?hD^WzZNb1p3;0d#TA{NTfz<3%Me-Fb!J7U$rMT33bjPK`vw`w6)on7@l|*#A2t zNpwopYr$!%0hhKZ-;M&cDvMZ_%%wP5oiP6eUmtXSL7AfqXBW{CJy2_pw?Il*c`3D} zeq=ig;0$To(;`|=MaeVR7VZgv4exDuuIYNb-frn+-_e#G9%P44g^A`~qXWqEOcE`v zFN*SU3vqI0Uz26)uDhL%B}w>cl_7qMRz-8A0z=l68Hwfbh5(t5*k4qwZazrLFD@=} zW!3I?Tp`Bi33#~wTY1MCOE=#_qohC|DG$_t*O!_b62S&b+L-z}y4cvbaO46olhznP zsd1C2Z%Q?+3NI_N7r;h;(VsIeHRC?y&Q5Y+PM=Emjif($hZ$QbJwm7N`()jsiI`B1skvycbT>d_0BVqF80N+Ut$Bum5@t<_HkKlnl7bF&)E< zrFQytDm;T(jyy~z@#6OD<&i4UVy7L~8=m@$BwoFs=1BTQ4u)AHsr`VuuFUAj09AM| zqr!M34X8N=^ip52Indfc@L?CJ`+2p^DP8jg;l)qT4Y|T}fy*xy6cQ@-rM2m+yX9nj z0OBg2CiARk?@(;!q%n;33pX08fF?FS4AV?Mhd$-swBcpBTY^ z_qg{3L(hh#$w+>A1SRR~?waKcQD_tkzqckZoL#lc1lnm5udXjHm@E#(F?HP=^?k9D zk_K4pb+=nNd2{#G@5u4$iGj3}eeo(d^fu%gCqd{;u}@q-dfyvO(9g;ScDIKm`XDg5UwlCcw_ zOYCzct=wMUL}atkqnbH{9q{TywUwyQew9mzL*muKOY4xX(j`6wQ56+@TuxU zk@pQxPoROvNcaM{`&vA*qz%)9U@e`SxrzY_?X=|!x49WsD2mky4YuQgw7M?2^3tpX zyplDJovXKtPvz-oU z`)vOKbyiNWA1w(k)lcGipL{-&|MErvWZhva$L}AZB8OU zcP$9#=W^|7xdx;uZA>7SMhB(!);-%cEz;JkLer!+PAMw(dgWzgvl~?57gq|!p^w%R z9vioao=ZS*4UZ04IC%J?UdR5Hnn)(9`VmJ{tOS_dGF}8lFH7(|SsXXV5&CP)3|rGU zV?5zg5Y{r#rBTBx_@}{swO`gr#mh^DnmEjpN%tzgv)W&2A@W0Y-}H=_(55gSkb?ub zW*{Ff-2PFzM#6NDg9YCFsN&U{ZV_3_=dc9Up!wT7kzq3~BETG}#Rn$Z|EYhD-CGzU zCt!bd-K!sqRt!uTB9}iA7&wP!Mh^tyJ~$gJD~aoQRJvP&ud%xG)j1X@TY&;cOH#&i{Uh{$T^r@}B4Lwc zvvL7Q%r>3uB+50Wm*qJlUt&M=vC=f!zU{sj85hIkmgWa##KgxRQ(PJFxe35=Zk-}N zLlMj;Vv;!0@N5D;Dmrx>erZvOc)`UALv|!q>foT3hQ{ufPXz3&e{8yn_6scp{%mhW z%QtRDdBiNaY(AQS$Eic%C7@qlu|3D^5fUFt7`w9*Q9CDb^( z$%~W)6SSGeWPna^%0OQ%4F0SdQp%%JFQ~*M?s1vuNeeQwb%~5Zc(`Zs1sa_$?!_7sgLM;3 z_@pu&lQ^Aq!XF!{%ivgrn|`6jC`>~3pYy!71CB?h?S3R&>5`o(dJwp-yQ6$by$qA7 z)RdG`aa}%eRuJJ9X=-nD_&$@@<>{5cb|dO`HD6xf^EN7s*cR(i2HG~rgMdEgi3ug& zs^Rm~q_ilGNtY35d0uaSy`3wP$hy8u3;jXieR7;p5OBH^svr@b`PI0vudb~sQ>oZq zed;gR+#-$`BR5Hes1jkybxT+r?Mt>@`?i81?oA2N5nues6h6>PQSz;Vt{R>tr> z!Z#+%dknG0JkigcT{C&z^yc>H;PH>ltYN$B?sCj$Cq^~0e4}#&(S1g`ZC{9DS!TXb zCL*DVmk4nLz`2SUpFII77UJb?YxAQh<;k$;-^m~C{`|FlWC+fwj5~^}3!B4wBfId1 zwxZPAXTkFxDr&C}z3QfCpxHURb}?n6_P~o^(v0~HIhHzhVh3G=ZBE`DBY9YS^Q>ew z<8M^DvJiR#FA4#V{f(M0t8A*YX@spi2Cbnr_Pf0vCZ-=Me&=dgXT4J!QIa0wrX4oF zzHOyltk&g1oR;^B`1Q*2AYzfNsiAWqh)OwFpAmPiU0t~m z=x+6hm7v?;5<#807qb!wd7XPt4pH*r3+Yo{m@2IAiMz+{LeZ&R?~@K0SpgC0`S=hl z$j$|UmKHpL8&%f7jxY;>gr5!Mmdi^X%XvItK6(wkh+@PT!VZA5lGOTVNMprksW!0F zOdkQD!g?9bkr{Q0B8jD|{PD5+I5Z_C7WjPio}TUS(p~T}zPRn9bczj9HPbiUGilm> z3=wv>XMcKZSFMaV+>6UzRXE=e@}-mF!ML(zY#p))Xti4X>Jk`=lqxOgP&Y(uRvoJ( z(C|)mFW9u~S8`g87n_rM{#$ArI$~*6HpF>K&@f6|!6s5~n=6OvWR}D#*kgC&+J?Hz zT54U~fM7!#8r)ullibG{{$`tLPJ)Q zM)BvG9fwEx_}csf?gmwYC%jhwEUA=4An8XJC>loEjSeI*9x)y3y-w`<|B34J?>w8D z{T4td*U6j_D{)1<#6#+fC6OHz+N|kWPe5ALlFwaG)1<-*4j*Y!z z`}WjE8YTgXR%FY!vNVl$>!JJOOVwYiX&k`cktH`O8mj_ZH8l&DvH+u6?;{6vMR7Rt z*njO{yVg~MlEx+(rVWJ8g`80t=Pb$zAa#`pwQ?np1m}^ALPQU+#L%g^(?1xCPTWJQ zGo%r!G!QRu$5Q5YAKggkHDKOBVbOp_)1UE(xO53!j;v!a2TIPT+^H*AqABMHbI zwRek`2=&#eK6b~xjepy*{;$j~yd6W~ih9H&TZcUA?&q=M*y@uUI0y2zv*1PpA|5Q@ zjnq(r4pSwq(mNf&(Mp*ouqyD#Nh@mJr|eVjh}{#$I|W?VxN-9!Tp<9jv`eg|e@qru z=8hgs8x32%I6vtV(+<)O82&z}q53bF`d}Ijk%QAV&uZ=h3i^?}v71ntfbcoKrTvcj zZO*=kN>^(B>;*R1KV~T;xM#PpXWY^Pj8M1Tl8?~~_mh>h8ubPh&SbsXnNbW0OSx;) zTS%w@6!TjPi(p2cZZXRqm?M4^K`>a5H@-Y{s2!)ssQFGuvEPNc`2m8W(m$ZzKe?YQ z-XV>Yj$QtOWP;?g2Qo63kH3$kV(pT=cXDmy+A3Gj0QG7c+ zXqfV2&I%?Pt5E4~;ArNz?m2w=!nIouJ^auErIF=Bm!AIc=fCph&wccT`_>l+auMJ5 z?W4ekEI}e?@c=NGmo`2!FSbkt`-#w4M`t}E(e(LFm z?%p*HU2SXo*LH7PoEqxNCBl9*#RjdStP)QbpmA$u&yjQ2AAjN1H{WhUP}I)>VJu8a9G%^|@A$oZr0{COT>cdgD%mMx{D==FG{%d$vpslu{9l+SCd$J-aw5ad>)V-| z?|tGOo89G$WV*Z2cE)9q^WmdQcK-Z5Ck||z9V(@e2Mi($g10CyTIayT(w<`%AAa%e zkH7bcx6!q!HJG8{iB3X2d+G9p)9X9thD*ti2cfInE;NC(mCx zf9lZonSnygPf{Ql`LKu{Ch|R_^E>al_r?>?z3|L~=eShvI&<#qv9*=SzHG#cYmKn7 zMLYanw}c%%LWV;4|g-TMU<+%ssJ!~gi44tQr#J}U4klqwUxFR z;fk#sUqi)oD;mV>P<3iJM75fNFzTZTYVtbx zsT1Nlk-BzsSJIxH-SU%eVogZHL1RpOc_a>oRY#}14R3{>SShN5=GJHOtLR@d5D-bZuXoQkM517sb zmtT=^I1-J5|I@3aqB_gmvKg&R$)vDeoyFlTZ*n3wmJgXgvg3iO4YBlEeO^aD9|aC1YW~%L+@H z)nSKpzGj7y3>*?%%|}tS6!g%SY@kwQB*P1O3s{q&rr{1N`RG)0VmJ&)s2!LbrKmV) zBlD&a8L5~|)$KK-x&zH1GCajpe=O40r-{8VoMTk*N2#oSL)4OM90Nyk+P2r=SVgq!Y|hn!;Gw%1Z)At$S5y*6OR|<0i(;; zs_AB3n>s4Km@qO{2ge3|TU0>41-}sETnuYDOOn;~ZiAzdC^TSGK(ZuZdWnnJttyI? z*9(zCYqXNKhp>MfOx_DnPBL7_X+s^MTx){F-BxEx?BhKO4XTR;ekxCtG9n}XlQtJ zd}?-ab;r&fTc<|*yP$OoCKS>Ys&FqWHCZ#taK(QMN$~0!9G#q=otvLuT;95{^Wd@5 zXHFg2zioDGpr^B#&!!Wx5UM%^*XYntXdrnIc+2PyMG4ATSlPaF_wGG=_U>QWd;G%v z4?XhWwR`T`IX62#JkZzERm!L1u)dTXoPQ7&3)%Eoz-)$Nscf;UZ)gJ1t)tL(b>h_g zo|E@I@yeTTzy89*7Y?sZF3(L*j*kvOx)p<13Kcm6fmNI+tE<%0KMXb2j+{P!<=XY@ z_uV(KdgSs`kZyhP6CZf+?B2P79jhzLi*u7BebBrW^ug;0njf$V-v9Ovj!w<5>^gMv z{Pl;QeD=9#pYEI4efq&yKl_dE{?@mD>D^~89#|aAAK1UPXUEFi_&_I=@uRF=2Qf^5 z#ap`24YMq_>^yk#;sZ~A;H?jT0M_Y`OsIs^_~Cl&98p^nRB}* zyOM#cS1+AAeqh_|NOumV*`n?b21*5bXZnUG=eMn$xct}$Kk?;neCt1aGg%s4x$D|X zAOG|_FTD8b3)c@X^rb`YM;?3l#)aeimM8l13C#V>MlCfj7}t%>t*)KA@2R&x|7+j- z;SawT$@ENaKXLz)53NruojkX`*qih_tk1vn!jm`8t#6y|FC;=f=+vY3sU0p1Q+tP} zx9mH4?U|2!<=a2{=^y^k7fyAKF7H3K)@uz?Q9*Zi~Aba~`AARGQ z>!)_l4&-Cdjs`conJEf)9QC-M5~* z0aOr}swQpV!YlNSE$usV>y`I@zEbcvZ{h2pE@u^R~ z^(4ph^~&SVzwqRZyJ55YsHcLF5jPr`%zWR(%7L@jpLp?=7azNHph{%h$*T`Oc>lde zc1-tWP@A-hUIr-D(ucE@51zVs{l>L(>r0)LD${#UoV)kzkv)qer8sN{rj#+92~@_W z+aJwz4$W-cd-(W?qif4U@rqukp}vJ(2M+DuxisFB3A;(Y4E=9uM&=2|vt2_|u%OP? z`Qe<0y}Oq<(*rY0E6a0Zec)n&EeDxx6$m4P_|O+ZS-z9gQ)B)4kfp*#qxWZehDJvQ zyYq=4_2LCxT~pUr#9$c2?GVlbZdBT(JnAm|*{B(qT7R`QMfYcl}NicH$VKrOpURu;nQXHkZfaa#F7( z=I1(QO-j^P0X)PUCj6VmuKm{^IMPGBJ$$(q);*(|GgOf_x?e4AX+y6(L2&Q8fAimd z{Y-%TC?9q~#f~aY(uPzlXqV^&&kY?p%fYjMd;9k7Hxty)LOYtWY)7kf#$kV*)sBk2 zne^1NM>G#rUSwn*qm`Phx|xBv-0>OmIxBb!YZUZ}$pIC%Q3{vn5LYCAQoOF*Ocl0` zsRJf>jQtx!g2rM4(F$!&jIBB^Efrnl*qv=hkXp zOOBW_wbA8}MI^;B5syZrF;r$E=-0^V@KHMNlTIQ=|3vUs8bvA*b(*z`s&3!R;Ij}J zjkjPA0q_isWi#nm$makmbq|S!$ml#tnS$a{@H>Lr4s%EAt=w674C5_q0n@>%TY*pr z=DZ_BR&RU6V^9qquA_o^5=`j;XUOM;tK0-tq#hn3X0gEo@E zI>m6OWg#_XH@D$q!Y$%|t$gG+ssk$q;ai^LkH8^*fJt(XKS{jkziDGXd3SF%0{wgZDi#yi^NAJY6`WHUXYB>TMgzUGDCoC z<*P|ANs=SExyKY(N-`Lko?>LuhS?T68VRfeW+XN;MiF>cX3Yj6l4}gOiz8xWZiKmvUS@|%*c-J-##}n+~3>XRVw6QlS>cEBsCgQwP~zk0vpBzN;U0<2C$W# z`wksGa^&#(`r)JdPF%e4@S_i1zxU|w`GuLu@v)Ji{_Y}7DFo|02ME}1605lUkz}sB zr>A>tYHoS^zQZTaUAlVT)yo$zEbKdd{izSW{mxr2KX&EV&YA67w=6Hs&rHCaLWre& zUN7o*fK^aZ7j&{MuI$)%*Xc_)AA9D77oL0iiOC(uuRZ(W&;8Ob|Kg|Lc>Kb_<&i@N z4y^6n3F#I|C4~_pxnyBfDr;(fdE4#-$Ie}U?4`Fq{{E*v`O&xL*Umlm_Lsi(gWvo9 zuYKl~8^>11JMX#Y?$gH(?p&H21Tyhx7&r99DrhL43He0NGjhw zFgCrgvg_c9OAmkGlfV4EAOGo}{oxOW77t#2{?p(3@n8JqpZv}*z4gGcm65{BAAI%Y zr*GYRc>C;7S2htv)!)2sazqJO&I~&14xGCB)Q7(Oy+8ZMfBBdH_b>Zr_nv$5-LHTD z_kQ<#-~8-Lm-o;1rbD0p%zGbt;pW+Wi=*9y`+N@4`w6YZ~Xe#zwpk}m)2Ltx-+pLp}knehrMXSQ%k## zU3vDC-}>|a1yv%Mo{4QouRi(W^G`hT;HCA&{&di7|DE6a?QeeO18}ChQeos8H0sD+ z8`{4hX&aeYUBB?u$A9xLpvY}V+J+bQ96fXT*x|L6(R|otH5&f#Pyg_HUwQMv6Whj0 ziGUj?q0-Jclv7*m8(Z9a_K|nK@hATis{9Wip2+tMPfU&tb|$=LtqQF1{`9Z@><3@_ z&_gFyM+#A&1LeL^dkhQC@6D73rnViu{(&$3@c)4-e+LP$%??((Hls=cA8JKy^1N1wWP?{rt( zV@1v3>9+!@*bjl@!0hhRw_bVgtKaz5FTVX?)${d%*FW{e&wuQt8^>2i@?ob5XNFM! zlGNOC!m~26xOVpDv#)>Xt(P7+u~;Fpa^~TeUVrtuTX*l90|#U`upMA!A6YchGrqib z^3u%*Z(TmN+8?giW>jq%2n(mpabeqfD`j}3LF{l*Sl1jQY-z0xyiP*Gi$A@(W(1wR>TMGq2NT(i2YsQ-#_3 zB?7Zd-4+6oq@GNYHsJ_Wu!k>v)1tNtth^hl$1=QvAq@zC|CmKb-Kvwvj`Y);dVw6Q zCGg%2eXC>ayXa2Ms1K?S)|TzZxb!>D4mEt$4dqhDX&$W0ndLGpHZR zyftW*5`GyceSBxtaM0&sc2b6z2APz<1b!tp%XT#OMp_by8NL;99g&Ys`}KA&e)`@>>;9r&0B6? z0H&Fv6IOR(=#0(s>F1G(^3Y%mliFcse0|VX#u^L{82VlfOSs@06g=G2b$xMMjEt(? zr9yZg)}Tb6+3W83I2akJ+yx(A;N~)*8Y|LGBQnZJCP#_!q>N${h>S9T!TU>zOHek0 z$Y}o;WjbkkUru$?Yh`m!s6SoQsRcI0xR{#OAVO_M){aJk=`wHnf}snNiJG>N(MB@* zHPu|krkGB)B(KcPKVL|)C95OlrXQKG1HGD*BZG*H)V8lvhkzqQaFMq3>+R_(=Ar#73L%-t z3A4J;t_GI$l&i?MOrDZT2>>ghAlT0jj!jNaPftxwOiWJA%+AeDPmT`ub`=Y`Y$lzI zM*<$O#E0oEcyC5hO*C*1;fDiu+HJ5*_|_e}_w3oVW83QLww?P9A3t$yeec5b*zh2% zy44L+Ktr&}B_y17xShu;4sSXS4guyrCL&AQ_pYBfbI-ZEPoFq?*Im1hp1beX1Camh zTv?c#otZ%evr5@SGz1oXaM>4*rLamU(KkFkJu@>oHrTyy{lvZZJ^0vDPd)z7jjNY; zpSb$ii?6-$>hlj@Ji24=E*LwuE>4dC9Wdb$*oiZlA;qT>`QDkumDR0V7N(AzI)DG; zFTM5dd!PK+TOW9C+tDjez4`txeEAEXc=gfq2bWKrICc8;iM!UeFH8(Tw_OrMI+HJ9 zl}zX0arfm1pL_c=zx=K5e)l)O_PM!z_dN2(=YH+~`u=x*<^5M~oY+2b z|E&ifx^?xQBfFQNOcs7y&1CaMtdi{-np{}jwYGlj%*9*JzVi#;{>k6`!$17>Prkc! z=<>6l_|+f$(Vzb0d%yVhBWHF^^}PDl+poXy$fcvZ=0|%=IdCDt#-KTBECXOCzJ1Su zBWJEW`s!!D{b&Cj@|S-ZUS7ZY;-|m$ylG#WToWx3KNtJ&%6m zTmPA;6o(i0-}~6>?|uIB@4x-jMQE>!cp=ptN5NRv*3{EWU zI&tf*U;lfe0ukBXQ};dk^iz-AcXHQsPb%bbz`y_7-+1%tzNxN60Hx>Bqz&e}!F+0T#Lc{};M|El?*)!u_tcgr% zU}oo;CqDBBM8)L`#!>~;2Q?G%Kwq2|{rx}u<{OuGj%0&QGisbj?G8}?Qhz9x?i^lP zzy9WLkcs)vR#bcu?2O^+9lVvKa`g9q@yjpWvpSIUS`A<&EvsN;sH4Ngjx$ew>UaJN z{NaE89@#l=fM5O4bXCw1Biayy0}uKDiN!3_ zNah_raQ)?vzW45HkKMa(rYq)#$ve!qB}tq!+gyHdOw-#B-+kr&Yxf>r?)ESr%(~R{ z!3z&O_Q>_S_b&})gAQXwA!*t!GMrf(P2dV^L3?HLC^&SpFOtBo5>$1PUT#7O_L(mq5Nu~@p4>LqcFl4hCm)S&hKPy$R zF)kPmhx{HpM=l@=K5Y1@)pFR;Xcg23H5i2HQ<|FTjqpPxDUT-MRI$T6R>=IUdSx9( z#z%FjDLTSbQK5Npdb~cL$)Gi97>w-U+s{;uMi zznZR)L3P%|tg<~~QoL3f-sKCdd z6kbt$-=VMl%iq7Zo-Zbcie-lQ^0n7ry|%s6qm@=z z?jUO8CT%($PC$h-Z#L-kCRb`~_wf^Zrwaj#MqY9D(e{=}+RBB0;rC;Fd+=>GyDO0D zom^a+?#~2l`sy2txw{q89T60$6#~ALcmj#r;|nK?y@P|jdFZsQ62Us;PgK`XDa8}e z2O7Res`9~RM)_hPn+UqCvihs;au))WK}_oE3PduM-V%zyx?9np$D)~n}A87kRm)~hyHxYHS%e1?I7C%|nNEwJvE&0^RH zk##VfKMcHuon(-1Sx7zE4OFTT8G&SSLbvp~jWb%f5E&Vaf}&eWRP%Nb?Hc%z|H~qBZiBPg7DH*HPP^p%i z8jb)K6NAecR7lr^J6oa;6b&#bD#R)sDhrItm4Kb24I?t-`ES7fg@UgzSdotm)$8~B zeLgQr+Jm_e;PoaKH!?eNgTo(=LqwKDI0E|D!w3d=FeB5jba>nqcHNP5p$nohSVJC0 zv5#y$!bYJ8)?I;M(r(E6Ynu%PU(C9axx|7#SKE z!2Y(*d;BoZS3^g8ZC} z6hLmNlu!z!(nRQt@d!#}>Zqd1+WPwHviyvsu~X(QU%zeVj?HTp&73&4b?mGqtJkht zJfpw4q`DgT=Qr0^7GG|e^oA%?Y+@RM5}i|8+d8JJs{SD)WKFmdYispGqwEArBl8#HUv_jp-fe z?`dx+U$$Z2xwk&~{?C8<`opWotGZ{suwF>Sb*x`IQ|#{e9h|XD?p2_xyW*gsF}Heg)KMJ5OD|``(=^$2QOHuFTwX;sT;? zYbni6Nr;UGZ9x-~(LIVy8C6o<+}<-VY37nm$KL!AuX4e;=9clXX}&P_CclMMppRMnBNX9Z&IuAR|QW{F0}zdW&^WmIg0LIQnFq3;&Pw80cvQfgLVZQs)4wrz>eC~XCO6*jI} zIB9fgdQ1d5{^xUxTQZGG5ig7bG!*FTd|*Qop^J)1OfPPox%a*mMHgo&s2SZmZd`9$ zRjxToEkVcsadB~@B~lW`MEHHQh6l7Sl_@pasQApvf%Vs|D8EHS#iwTH7Z&7zzOGU} z2Oa zbEqM3zgp}Tiik~kLa90`rEFlsjbG8VZ?fTvF<8F?Tq%j@_y;@378s!n5a|?U$C1&Y z^finx(Zpmo&f0hHU+~HgcXwmO@j&0;7jPRH|KZ|__N+(&ofzm~n2A(mfs#U5WKvP* z;)8E}`_F&>>Ak}89 z>EuR>%#MS>3sRbAZ#r=Fz~%+Lm1#OLi|mAgHG!nFxMGzdDW|ffd!Vnq(h}~FwFW6H zbptc!&zam=ogJ$dfI=dNC$64PnlrL8%&|J95UFFe-zBSdiwzgt>rK(o#z+O% zwS#F0PZ?=|)o6&1o+HzSu{fa3g9^G@BeiNN*SREQ3mTwyRFk^>5IT!a!}R0M5`Rl|xizYTo@|K0A)(t06DsRPpnpCi~kl*8dL=_F8V zhcF!6hL6GBU~RJViN{oJP(q2J*!VgIH0dHC7Ld?A!JDLRJOzP#u_9OOTq0?u+U%J& zL|?~-&LQUX7DOO~up>(sp8N35{_)vLdVtfp_j1e-?3gw>J;YXVPNszNwV5q5S1z4c zldNPComE#D)rNess~&~s$F{e=p$v{t6Q5Jv+*F|R>< zu)~B(U3^+*MzT@G4|OHl9FkidC}=dLkW2V1`cRB7jAEt89Ec4sm}-N*=Wt73%23)$cy(*VrbB%Y=r0fV&f;-7 z;BtOU9$~H6bgVJ*@&y$xuy!jHrpSgy@lYKH8r8#&YB(>`^cY7+l`?H2Mq_#wr(iTCoVaHE4O)a}a<%dyS;~OX(@L2k-S< zyvRlR+@lMtzh4|vk3aw9-PaDSpEs#v;`~+XH?ChXcYIrUQ^(jD3zjUNJGr}| zG$%b3+B-`|)~LLa8U!V~p?mV&B`cOMo;_hq?ZFH8zJsd)|9JQ0=H_wpfjRtvoonaz zHx$fVvHjq&BYW1(?Qbl}Nl!~n1MLp-OKK66tkTYjvllL10!mHyoxO)Coqm0{V$Aea zyN;c^aOTMNWs}=VHXXnD;M0%ZI=yvnS9M+nyfMI3t{g$JYh34Upo8xwbx$RvwB*4S^C58!SL_SuA5k&pOzFKpOBQCV#!4NGA3mfRyMZx z3{03hclG|8|6@h@2}U`0_4<`p_NImU?aY>8$BB_=r|ucWfB zsjX|`!dx>O$FKC64n3NPBt<%UwpIOJ>pH-mc zgh1IeNMo>jDC&H~;R=<;QJtHt8}rpFkpy@(D`db7ka^FFa%EhWiUHkX;HM^bS0W+z z=};z=;3ZbfIEFE8mnJ3v^FO3b3WZ`l?%!-_H_PY*AII5VFzW10V}wZ!S#8U1!?n4C z_tsY`@Myh0^jxan_#p!FkF)cuV@1?Jr@R0aXFxeDOQ1<8?p^ir!yo?i#f?o}NepOd z5O^{M;5g~y58MY=K%YFSdrPFu6 z_U`?+&upGrn;Ooe5S-ouS#8g0Os*s%F1u>Xgc&o&x8&*^I5L{CeEjmQJGU&K+?*S$ z2%|b1N&8ui@>x8IIw~oNtdnqn!?wdT0`s~8ax{rb z7n5ws%CaPA`OX{}OKZ+8E`hGVD3y>+b8fcgXKihasL^8L<6{kKzAHyYm+E5T;!HZV z1ZnW#00Xxi1Fv7;{>VCm(P)TJ@TqPdq$G}5uJq9F6Y0JOM0&vZP$UAK3LKo%Z4pR8 zjHcKOM2=?n_7IxwfJT7O{>as0=!PNYv+1M|&odsNktZU32<-Aez;2O2&uEDf+F+GJ z3aec_f&jbKAR^JZZySOF3Vc;kCDK!Y8tkzjB?#YupkQZfYa5DCrc|jEVlIPZC zhwtq#bC&FZwx1Yb=Vgxq_DBHT2>`)CwNewzvJ236=T4i@5e2j~2*Mn)Z79gmrlhAO=%g(76q%PDM}~Al1rcEXqSlWH z-EdS9kr7G}o8tCjwrAR`?E@U^SMYv8R3KFr@S(#_9*Q;wVQsPL>zKXZeF-E`%^?(k zvp;mzryWP;L~MXYU}QRA%fUP8#}I8en~coK=HAC2u5g6%2)oYNp2Aag9Nq_UZ6-rs z-^Ws9o;Wg00|wZwVbtk(;K)!HFt{7Z)9SxKcp__>R~@9%+hCOpK3-(g8Cw zsYC)hY{4BjagHmFR zR%eJZ=wJm3Y$peb5}@5gP*jnq>vWPytAPGgOK0CeUuR2oVU{^Q+GsFwsEtUu6begl34>S zB~24n>^pb$^)q``OdefXn44QrTwh;P*VH~1K`HN_wQ|dzmtJ~t^YR%zx2&?Df1ln4 zZRN5JJ9cefzhr7>^|&>=?)-Uh@YmlTTH0S*I4UbUx1gk~vbMPzK`E@8G=J5`ty?#& zoIByv-G5rCjSuI6t=zHyrG490&FZboTm247zigjepJy?rq-A916_ixwBPdxVbsYnX zmM&d5f9ZxJ@7m~>U)DB_o%h1d0|)nSUp1?zw)Z@?k=JL{WyYIKG4V+$mdqT~Kq@(_ zu(H0jy|cS-;=yI{+7ivZ10RKQJ+YDudqQ!oorVG%q&kI`>r@$|hq}Y*I>UsyQJ> zAHK#)9S+``8yyPYI=!HW1V-jik^Xs93N@6kFWL4vw(`&O!*LqW08Fb| zNl(Ru2G$!WC_Ey=dNUG3W}bB#t<4?0(Idej<=FyWSkf#j8T!kC>Tt5l?H4Q42n-CO z@C+rZum1-*hFi-3MG4<_LvazEr!kbftMZj(muC}58j((ML1eZnwPnMVZ=nnHIDP?1(E?7LPyGZB2QLxSJ%MP4-_2nJ&M(0F{Xawiy zq%Z1R1PD8>G$JmexVE{eYLqd|aXn#5-MBdm=S*lVN{SGK5(9i4XA!>s7)izq6Urk@ zN$J_6GLv-z2cs-#HDPQ{WnEo)Zi-RKXOMvy%^t_sS`Nq+%G7|2O^i2%3tTud-!M%~ za!PVsRJe=}nh-dYHv&ZuMxDqtz_m=k2~`Pbt`Bx^8ea}lQfM9k_l6C$hw($06BaOg zQ>vjSE8ua02`(T=;DL7omBs|7e)h$=C>Inalxb8EKz=C~vWFn1K$~kSod$a566{z= zf6xaISSBeI5eB0PC~sJlA*Kf2fkX<8PNk8F_WJ~Ph6`o$rD|2AF*-U*tKc(2hB9#S z4h#Y9a2%T=1Ry8^sYIKuD2+veDW#j%G^BNL`&Skcs4iz!!LO%PUnl>gInpTEVtS()C1PV`^UfVxqd`pgj z7wmj1@WS4rqidtmI4T{wbK1`#p_`V>XelTy%8ZsX2rlJvKgoLgxs?QZaPEL)^`}@s1psAKC%XF=N2{ z6X_f(ctyma1-ZYLZ73K;2F+6)g#w^8Je?KBg?;`Wmyp=W$Q+L#-WNQI>CgkuMs9ix zFHZsrJg_l=*{#P@WF9#(z~LfE(r}uGc_c^BPHiAnd%Ts6XMzPTlnij@H=i_6_b$I@ z(0}1^blH8Tta-T zF;We1!a(UL6S4b(@@|QU$D}yx0zi;}Lk|P^DiQp_+rGfu;?mNh97{q}IPBX5cY6eu zVQOePY|uqO*#;TZDw3O#l*&#^bx2UEe|#f+q7}@{P8U%>9N|C8@6uSxMF5^b#{VI z4g0eR1OnK$JGZpDwz{k!+Y%X_l%7*qQd*p!or4Q-WvRw+Kpq0_P@*=b7B}@yoIIhowYntVl2=~e(%#u;P}n|m&9#J+q+18g{K701$fz692*)d8tTG>s# z)8;Q-zI5J{?s~7d*>9s1!O?l;nW?Fnc_o##jjdx)ALf#V?xpLuzqot*y2a}c+Ob<- zc9L=Z(-$sZxorN_o`%4}gXs9b&dkY=)$2_OfX*tbYCupji>g~X`^FCR_YF*6f76CC z*zb?)nKEz5(nWJ7cGfa_tx~5;(+ZUWfkX`#f@S6)C<$q!3QH?0%1cYio2H+(Lz#fX zjUJdfd(OzvIP>E9IgA5tF=14PODK!q~T@QAp|iAhbyXTXl`k$ zEz8B#SqaX;%MA+n7yXDdCL8NL3cjnbVG)(WNNzn~Lji2*D8S<8W+aow+1RZ!6{uwo zlv#&_ph+C4Am^~?Bq*)%3m-M_bL-lG|4m3vNiiqH74ysV5;D^0vpun{r z!Ehe#7u|XsTlov1ZUk;qU~lE~Y}>Lm+l|R~Fu0`>&}3ejyXYfy<#T!PQIpGoMV)fm zw;0GDms;4aD1h9u7Sa>{bdb-1a>J@`o4OBAnf|afn|K=2hSmB8aV<{o<=PFTFTrlszrrXX-fyGveQ# znq8o#xZc2hFrQLyA3p+3pwDU?J8$uv-okJPjv_2`{LU-ye{lWqqLwrz-BoFUz4!_Y z3<{yLC6P&a)y*w6IR;0D!b_e#X6~BxD`vD8#L8Jj7b5{5r0X>-0t*u7wT%;-lp+3n1{C}W_FOwSL zC;-FxV&W~nM5#t>|2gy!HyoKi0|>W*ffk(UQnCqiWB^2}fgUn|43hFfJ&Lz{2qZcS ze$ivS*N3DJ=XeyC?paOj&1GIchCij<*Z8w$=1gCkU^^l`~)Y34Y+ zf=3JTJXP^0g>tyC%bI;_1=z@&$rY=@jR~pg=_zrMQntGukk$w!GdRHWn_}OK4mvEL znILqJG9{*3(vnOX0WA>QT`yL+LFruF7)S|au^7M>$)@&4QN$8WR9uodDMl+{24NsU zitN#2M`uZ$%Ts`rhFE;Hj{Uuk%AId^zxCKnu2lcm>cz21`*zNk){AViA1ow+WbjusV*)tPA6r!Udr&BbRR2U=ZhM(ktv|lr~M`CO(3%bQn{2L z8ti6%iZu>JQLGi4!A@4c-a#}X1=_%n0E%1E7;n$C+1}4Pu}@wBq);}G&tubwp6WFn zIWnZu*5yP5!7rMqp3TT?C^nAFc@^J4(BT%kMu+Yl0B^U)8Y8?9xR!{)LBptN8A3n= zyWGPm&ErvqqPP59#Ct=bxL<^pCO#9nh0$BjNN(BX)6aqeANgk|w=g5YX9F2=02_(_ zcjI|nyU#_9hR6C9KNAISb-eU`>iZN{_UZ5EB6%MEhWPkdt3I(d1Wyd&&2rPdh|-RBT+VF;XSr!>uO)P82*naHl(4u~z2HD5%;fNe zLOv&yMDPc1`*Mp)OY$?5qSU}Gh5~O33A}oisUr;reS}H^oXB9SBG`5fL5WQ-Zs;5s zJJ8)+mKhr%6L1D5Oc)qbS76r5c}&E%2)ugcN+Y6^Eg9*lNwGqiS|5{?mXVo}ni!3` zR8@7&UVq@!`LoA&t(eqYXf~?1?%K6^*_4*NSS6QEA_5r+G$Rh>t79{YtLthji}NC5 zQgX`b+s1UZH&+*;?p=M0cAdZb*|*>P?#-7s&FUz%MBjPv@SO|Wr`MWQY_N}TV^0EA zpi8grnlydN*p7zEjNKe`EXRYoC7m>Fp!aJL(#HrY=~y_Jvi8X7o=)md{*&Jw?8a^kK5?3?oOBn^GTj}3+QY9aB1DR@`A3(bC<4MxoqB)UX*F;Ub^Gt z)ptI4|LrTsw=FtvtCCy?dT_RO4~*^aYJq<8Ejfmazq76*Ga=DZFnj*|IpZcyYeG=! zyH{*G^vd~jrw(piw(024R+PQK-D@xOw}wO`WZyJ2%)!oHHTtw)CUhBEguppBAKWg8fKbl_?ptOhwO8N_J62O>K2q zetNPwcf#vdl-uAw&@j5QyR)qh`Y97L^Z(amn3nRP3Dky|ge0`?6LbT#q$S5j%D5bL z{zfaxeXy{iuBo}P2Grk9v@YmG(|#cTREMMd3g{H3*Q!L!ARLY$YqyrN{tjMRTvlFM zn1jOw+o~{Yv9litSa#GKjSn1__$(6qjrWeTl4KvkOEa>vvnZ2Qk(gp%N~VLY2`7u zr@EsLz{AfjiDG2TyZ+nY;AdM4?FB|&*_;1{fP6GB$z3fH$g%7mG@+*YB}Xow-O>}| zaJ!EfclH+m^4)>X7O21QI^(`>A)-U2Ucv~xo&1lmNty&Mv{fK(gRu=js))PmXV}&D%ivY ztJ166yW3$u=}=;TbIOD`*C70sDqI@@y!PoNz6;A3A~E880)i=^a~p@^)EpD`JOypt zI0C6QDke52Dnc$`k_oOGO@MP_3Nwr^6!1b39P0)F+v^ZOz$hYPl2X%B5{w!Vi|oN= z$SZ)%gssi^Ovj$AfFq-Xa)gSAXmiG>oUGJXt(fIeyyYEC=Yon%5syK&@23hoa5H%l zr6DmRuc#<5BR)b%5AygL&WFSj$`o?oKW^XK)t?Ydrn3cdjmex{2tA?GV^v|KAto5U z6plzLgHRaImpH%x+CU}0#!C?)6rn;B4WSeljWX-SG&_pJ&&3?VkIE5>!1aKTnf53G zh1!^uk&`zn)g))y?g_#CaJ`!2#GCk2SX|JGlj4LzWAOz_U2Jk%TCz#WwtWqQc>2_= zRDGB;3^|m^q*KUnF)M}wm^Q9hqSl*YqIGhP?M0vuRT`6-ml-2su(~=Ehit~B*B?Jc(gY5Y_{J5&~V- z%8|LG9bUkDg-j-Tv=PI*aAa;$DY(KVc<5K;=r38jTDqgRVCr-@4$DKOhM>gJe^|wh zL)F-Mq1C{{L-D+w-tI`nCk%w%dPZ^!J!sFznEu&$sS$E(Bu3(yK$dHGm*wx_Nig`Q`axtgt=!IR#^eFA+jonp1cOQo`i=_|%Myv}A!) zt&g|ll~z?(6z8PIAuF^^Uw`cNH{W{m#)W;$2CB2-v|D%W+_0dpJW*~hq3{mjMkM7` z*4NjRMMS6O*NmAkYr(>~Q+u0Ak>xYDoO=KY!2a{Y-4kmj)aNAJee~N8clLFq%N)|R z0IsoM%F5}}#&|UW zNaGKJ((XmpqQBD_PUq6M_{DFa zKlS0hsnxmtvsZ23f8@wZJJ!zYN0!dreB$P#Z-4sfn+Mm9Zg}Ah4CUQ8Clv24*f04{ zd)t$sTx!9Z%{z8%S-WU?E3&j>@z$f4ZohNq*2N>6OU7Hv7=KYap#*P7*@|oi4o4D4 zXD?sg)i-v+RAgyIOYf9j2ag_oX=g#2I%~mSF&IpUgqF?XH0Y95Lii(H8JAgDUR{SW zkQoIPb&ZWR#TGpu`WxA=VJPUqEw4bm<;I}n(^1qQend>NB?IMDqZ3opGtv?w1#n`7 zACIA6ds2x0dlelYjV}oXQJ@E)<1IOQ(cNGFeq)NvVNg96{o)^TOV@#Cu_KDAe%b!L3(L3;gQC`BFh};@ z0(Xiw7{&p`N0ipk*Hax!byz7VW7^RVzWw6H=F!paZ0k|CZ|kR^u&~IK?94c^6SpAF z8M|WZ=EdE)u`V8C@b7tJedhuQM5S7#knq{GIJJ`(6po-wuj%jybxj;D*ENwunwJ3~ z4O62tnvD8z8J|fGaw%{Ok%F9-!sH6Y5|O~EttBj=0LmbaKpUNyl4?#chKt!AoVdLS z&|^ueR4HX5nq!ruFCyOJO0+R4S-JT+=?QunhvGqH;updcD#Eo|jY1R(xBSFlKxa5g zXqZ@SGLI^*s4UG(jgqrNJc+jgXnchh^zxMPL&?E5lWf1hU=ocbP(;RO7FE|bR2QZh zB<`)z;0R(#BMec72!)7EgPSWL!~O&!Ig}%k8YJOY^OGaE#33daAzWx<^jf7j z4A~fT8<8DSL_sKZjkP7|Mpx>`AxJz$cm#A{!r(cDuOo^+F{`MevLrJ`PPZo~{9No@ z@no(P(0=StM-;F}Qf+K%c3y6JjDlfzlD$J1>`-Su2~UFVlziBJ&JHCQFl|DWJ~qjm zXjCxmPY;346@*cpbxvMHx=k8~3?Y|G7#9?e>cS-qhjUDzaky+(wUifuL_wOVV|*R> z`jBW$1~8Bpb7_tjiZ78$qqr$3^a=FB{?~(-*Nm@&$(pYZwkw?vptNlEt`jHru9;EQ zJ!j+5*Y7=g^xpN8+ZG}Vx4nv?{Pyt7hNYwa!xFROa}<#%o#TQyMKzhLW$tM?v0 zdhf=molB5~8&AE5?$Hl-j;}p{79f7py%{zB)T|B&b z0kUw}zRM4O{MX>%uit-gad+L~yXc0MyP)7^{T&)V)ux6H*s`sMUb%4L^x^F*P=aaf z+5;Ege)!p^58gb#k0CGGi=tRuQLcdTBL?K0Y}mPH@6L_O=FUf!)pt(XapcTJ01Q4> zpeR}w5Gg#q9sLxnN-3&sZfoyAKIW9xG_^KY=SHJU?S2G>>w>Zj9$$og^x^A(00MPb zi!-NZW~7*`zRoNJ#r6AwjQ3hU;~7$IR5V(#sn$YY4!NCv0$JPTav>q(Rkjg4nIn`~ z`@@QbTzhNiI(Y3&XOs;HN`c)FK8Qj`X>dA&L36a4f!A(wM){{J3dnHLeF!ExD`8>D zSho`h)?(A8As!0naz7mh)^2xNEel1dc8}uaGS5$bFgW;y^V*ev84|@c)QfMtwKdym zumoX4e^=`ESKNfScFv-%7^f{xe*qM7{<0^>{qML2FQuY7Tj4aQ>)3wt;oT!s%x;Q4 zT|y$k;aL&!@ml9=mucykw|e=ciYOYJWD}{7KMdMWzIJ#Ux~Tg5BA3v ziN!)!5O0(uqp0=K@d@$KI+ch;337SE6D-2{1d@TUO07}JMS*?}3I))P4OVYyj7gU4 zQCVs6I$0Ritt1=oPhtv{T0<0|g2?vejX;eS6hdY4RL11&qVkH;yfl+CjN;z=z$++J zsELY=k26Iog@9|bCi37BOkr@Py2PxK+UDl^vaA>do9JPB;7#VKO^If6f+<4AhG48h z5-5F#j+szpN-L~s>+Ec)$TUf49;+mM=~8_{YFcuvUMXNwh=Dex6OkOsm1tr!N*l-Y zbT$?xhjWH7{`Y4oP06X|ID<;WhW7~>2J9AtCkv0uEN$!<(_E4g$#)et*+l?bWlBhh zfzBj{4xHmKln{WTiLn&bG&Pi_MDW}xCG8@RqtY7<5pn_24b^U2xDsU)bc)J~ERnp& zUg89fRHaskxxmHF4uwqT2w{PGy4fIfE0%YHVeo;!J0faBh7heES0E2Jn4%(t&cZXd z5kO%u88p~@&WZxp=Cb(`xm*%9j4gqGiAW-cguvya$N&(4g^+19CY!^iJD(k2FyKa2 ziSq^ChVWDt{fu&CWC|t3g~swB5JQN8?!FDE#hR6A!#J`aHy*v-0YO0o_b-Ami#7Dm z`J(Lz-Bk{HLHp~eE)gyKME@M(N9OBRTZQ-WbAF9tw-9e*+ECAa4@n1f3mbym!rOCX zL(oRxZw!@-U?@YI`QV3{sST~c7%rpqn6*Y|sgW3ok@&wlo(FsWJj?;HE*#IIcePE) zpB~3*zW`%AWgV(-0O}_J9~LjPa^@R+8S2L}gl zTR)JQVMqf@+hhUuBc{-3WY1+8P!wGKq|>@w6%nD9@f@vu@qWQH7EdVTIX7cRgCkN! zz$_b4#3lzp^B6*ArX^7?V>{O5cn33}bVRS$27|^;u&4}_mPt`*N`BMCIk4G%{rHX& zvtBL;8`IuUoZ>Q#4`eA!=FIFYzEopMD{dYD-uF|w>rl30;<{Jvynkld;^S{zJ+P>& zBqd6zzSsH?i)X%D1%>L+B~)C zqhAIG|8!+`qRRmcNa}mxz`^ayCbmvpcl@odpw)i;?%wGw$ilrhtp#mgU)etU9Bfha z*?fr$3NB*op7W=+&S=b;wdv)zzx>;O2Y>nY-dU6z+HvU<)TRBOkIro#x94m4=Xq|< zCUf$|yBC(%M1@h-9lm=1o1gyl?V}sVH=@Vo$n7s+R^;!WT|dZ=8-SHKgVWuh;8y(M zZ?Cq)$7{#Qt9S3;zjytWUCWV0(>5Kye*eqwzyIQct4E-Z^o20WfIE~sgM$~)>toxY z(-$tBKC)xwGURh>|MY#YT)uYg(y6`30F2V%2E-p4KZ||v1Ddwp{yvna%CBf>ZELEq z$g$-;0Hx0j%5G@>Vl`% z>U0_r#cA!|c|dvJet^t{aw_CUo0~GQj(ZgN zsGoBj^9w}3vw=_ob$U`V=#uPd;v#il)|1tRAi*>_Ck)&kDIkQ{zoY@C299@t* zoU9%8#`ESz=;h996Le+$(J=|({Nc~WL(;){KCA< zB!fJR;(T z%rTL22H@H-7$5&YKq)DWsrl8dV>(-_M#Ug1H6lpUi0@P?cxh^g>Gb2Ia`M}zMXdo@|bTrOfdGnjZE*IOYFwE@q1N{EeV13I!0GM&!ka9A!H zIM7wa9iikEh*-Q^nKoZPj3XllyI%JmP!NkXJ2jdQ++;H-nBZ2UR#+2rnNlee zu*i-t2z>|?m?QImz?Fta!V@vPm`fvqR%y0|42w7UxHg$eI_@QKCcX^sYDGY0>FUvBCoZm0|&}F97?A#oIJTf7tzJKPNnX?xz zoYq;1ESo?CaWn`__?Vz2#{}^}@qjW~Yc<=6Gav+O+1;qjib7jgwzEcKzK4 zj~={zcKcjp;f4#pM`@{lKRCX;x59Gc?VBf8G-zE;D=lgCf=xTOtjuqovf<2qSc3A; zPcQF87Vdq+>W%;X>)U3uTzc^BS8r^pbW^aZX!XIukV_>`rH?TgJ17Vb8{XD6{`;)Tr;*jb;ZHksDb&9?;J;#?Y@HT(SJUO9GAB2|U`MbKuxJSu%N5J8+n~>rN) z3k`}X)#!46QP$2|JGb1e`pY@w3ZO!xDYY^eSx;C}Wpi~37y5!oT)8d#(CDI!Q98Ah z7s_L}+;)l5=wvd3Cjo9_8fV2D4sk~VS{fbj0833YYQ*k5!0_Oa#1^X!F>!IxdJWt! z(e4`Y@&Upt8e5-`o>x>{n4N5pbI5KLVE!bIOdp>HeS4Ek;jqlf%8_}Y^j2g-W^qkp zQ(Z}Bv?m1^K8PhZrsftE7i5`D8W!N%uq+(fl3-|m)zOyX`i`FNwyNxCDcyqt%$p)I zq!m`xR25|<>g6mdk$@?{d;_4};fS?~IaTcgqQgaCCR5}Do_!t?^UzL!W92+U;!_qh_3J}UGpbH^0%>eC?#9cKp7SyshUtf;K zV9+W7(`G|~IWi95$V@uPuoPefwpb<=hB0WQVC?An!J;-6M<9_&d2af2xQjpI|e$NYmk2@UTX2(TiygJn=caE zY_tMpV!JhXvIsq#T2o|vdPq+ zlY%R3ow~3OnzMViPU$L7i&BchD$DYc!(Cp;dearL8TmzFLWSO3+&+0~y(zYB{(|wS zztrRnXWo5ydHvcm@7z4Jw5Kf1sGhQY`Pe9DUmP5cr0JNmwzoGbrMP3>?#uV?-G1}_ zqc@JOLKdz&`w7Y@{quvPOMA-qoIHDS>!h487Zhr8=knbz?b%Q=X8zvWsA}x5@1pG1 zi#LD5*sX6aZ<)4q&xKo84@``40fDmE*0o1Y?prf$;tQug!kYa3{#9hzzFSr`+BdK7 zDD7Q+`tExty4;FFiq^h-V$Ta<3?)G^p!i1>2Am^?;)fmV?K3P`2F3J zQDnHbmm6FM<@X1_cp{YQB`vOgkC}%)e9^UH*K_#fc9golgvgk^VY0@}+Ma@zn3v+B z@(PHl>#PcQx4KIp92p}bI>P0S>8CJfo8{i9)@+D?IWkbNPnU3A$ObQlLQ7=}6%jgv zUh8Z?2(JJe4JdVpPkxkI$RxQ@Q+xRZ(|HPAOrkkC!K9T3`1v>q$$(Btp-)K9Eh^5> zOw>u3ZtVuV33P!bCT&z+UUpiXPVV3&hW7@XQYiEV&4O+cjWtDr7fLfn#!``B7`g|5mSKq!J8t}oAc{B2YRYv`3&k1 z3b0@*+fdZj*;<*C7%8#irJ$n%sw55Og1U~*TG!Hc*HD$VG*#pz>ZIIIaTsyR&L);s6=%k2B>*MR4uvZYk4eib$p@~En_7lInFDSPnvC46WTRRb zMz^5=$5jqrp^G)AB`WYZ^Ra>si!;U}~;B8iYS)T>Qfcu_cfK9@zKhFCRiFwIP&(iltzWw=(f zUcpp4ono&>L;6~R=1-oyzTCkC1QX$gpZ@k78ECNR|0D(^z#9#5FhM?hV`lqL*i*|< zjfOdiJt@5f`!HL*(S|Y-dmN69BYOtpc)Sxw_B0siFJnJEEyPHS#7K<9NI1s-9UR$n zBFXG2vZs0O;k|vFDYD_zS~_CDe3<>?ADW>f>_eX=!AqI0yLYX^AWEamOcWVOXf9>UogKd)>J=ra(l5^ zuMmb63o~4IwzH?^n`pXaBJ+lE>xZ>!$sMGt8xA)E;Q?g;t zOS_kMCR1Hd!g3~UJ$d%zp7NfhM?XNlnFc>W%fPn0ifOn0`_btaCQe(k@8prStuFRb zP{d3;aP`)OJxix;x%>@Ai2a0WpSEBA9MiOYd~TDadh(_dFRv|f1A-%Wop|r=>D9fZ zJ6^Y<{CEplw(0cye?~_Bc=rU(o0ZhRzCZLHTbiRT}o4Fdl2=_O>* z^8K$p{Nuj|2mkfMhX{j5?btfbEeMWw>i35bm!=z^!_MRX{PH%+9WGt_(yMnKeg4_Q zw=Q67ce$tQaJZKrVczLiKm79h@4vc#{rG0&bI*k7JC6dw>+lZi+A3E7IBuWBe%gEL z^0n)iU)etk?e|$yRaakIVo}+D(Z=B1$Do~bDM+7F-PGKGx>m)TQ&SQm9VwAvv0J`k zquF9fMLsAr8kLAX#M&Omy;(Ai7G*)091g=-1muZ2a^lXsDQqsf69gjB#j3~?iZgfS z6+}Y6Z+JH)Fdk}iziM(G!TS!8nGFtp=Vp}ksi$-2KE4>dFLxbnc)9IKK)5e{c*w== z4HwX|WZ0HwAT*OV%yw;Az$@*@eUj8NysEP<#w9D}r{pCLC9tstQfnzUXPXr+2One3 z6bd)T%r4Ygy+IeD5VEOS1;gb*7{ry(xe|3G(3-hp|G)#(FF>v}B_x{@V{{50J=jeH z9Pa=UQ>czgNJ&dgiqR>7k*A|08PI^qb#ZCgd3mGKVl)D}I|C-1FOezHC1&Oq7v-iW z=oM7BeWO)K2Gm>hFl}6VL3wp`Sx#aEpX`=X!Uu**qAW!yn z*30bJEre;4>JoD*np#Rd-Er+hTie-DS70{Ccnk{0OCkKDL>re`TvZ0#2fW=sjF5o& zGfRDAMNX1V$^&JR$T-Yyg$dQhGvxB&L~TbqYSm#{jfS2h&>l88ghHXysqS3$-3I23W>cg2z=mYO z!Ng$qE_`moF9@_T4<=aEXo!;-=+gKklWXV;Od!~KqoHp5kFU^h3@?OO!_;W7lh{*O zsyKG)d};+4+LjUh@YIQmy&CNqDYECojg2sDBQX*qF%oDzmmZtXrP$I|Bl*aW~U*5AGx@|7TgOr=tY4jdWYCoqJ{ zV6o`73H9Tukw8f$i$ZY7%e(@~ECDFO#0DrUe!1CZd&$WaDRoA@S{bg9gh3OvprGKW zs7R&2nIrS107v4K6hdHNAd$uu!yD=!iRs8PD?6txTsV8?oSFUgISG++K~8RVs!2w7 zP7#6Iep!7}m{4g*EAO5NJ3`gZpNginopZOJefz_8lE&B4Q~E8M+ePTTqQ;NTbMR?ppa-HP(_ zo5->iFJ1ctrv3i%(QA8gew^gKZO7M`-GJbjpi1o1t*z#57eDzAwntZxW%IVYeEZ`+ z{QUEGkI*Vf>EvVU+^c1P6aTAaBpmL@?Jr?(rhk9)7FzeYY|Xagm*2R1_x2@}G{Y&6 z9dQc+9QOth3g-BYhu{44)3+bpdU-3dsCVq7sk;vyJ9coFb?uuTF6}o42mg-#c;NJv zn>SxS^U|#G$minns;aWw1bcTk_eNsySI1Ul#*8m+Xl-pob!2f#$%)Y#mebmvQ0zX1 zDC5#HGEk08rc}uV)FIaPI8NXywUMZ#gARS)gI(74P$uc9Ir686@zIfhpddF+VjfVO z2KQ_$-_46hl1FD@&2WmI_F_>dbQ;-eOv5J9{ zGRZ|W8zR#x!5#2%9GOBBp$%6_cnorKwA6*l@ni5pc~Z5`6GsM}FHmi%(MQL{#TeAU zj?Bk33G;y-Nn(vLF%{;>Kw9r>Pm+OGOhF`ixG4#gQO4_K9FjYKD6arAM{Y>YE-Ws} zOHa_VVIOzv`z@(52DYRc+lp9reYQXtliu7_3HP@KmPM{L1=<%A7bQ z!{b#!2-BvNwDgZ3Xa^jb*si`2*&k7OOnPB;Lrtzx=-Kukx*)mosGQbuNH1UBu3(YNBplc?ME?&_Ro9_hL_d$AAJTtu~7lq ze#g$S-yd#J{X`hvus^>a_Sd1!0xu;=WRinb>U0l^9TX!7){zDI+upR{eFKP820JW_ zX`_g4dvtz+g9&qFv|xMd2rvH-22ZArh_Jph_U`=Ts$Dq(hc8uW^?F^nT)?7|h{3_Y zm|rPm1Up*x6WKC-q9rqv5D*X$L}rWNNijV_g{pJR`E}h>X3v{5ZJ@b0&7_g?v$8YI zCWZ4`_drf~N^wK`m@t7{Z!YN=4>KrZ=1got7B`Grvg_29n>SuPvT=52c}8qRV{=29 zMay(Pugs{T?zt=1s-jX#x)vR{`o{6a%g(%i=aqHH((x+}y#DYHfBE^F_b=>R&{LH) zuyNbQg`F8LRZN_$Df{2}Pc9<#c6-_Z+~#LJ;!1G>2D6i@ef}A z?C-FS?2Fqd5mvWs<&Go3fd9}oWPpERaPW>hlmQrJA<$MGx%$Bu-+%w*{p-h39y6#efjzJr}v-i}cYu7HnvTu4n^0~02q&Pp_WGf@aU4+)%?FOX} zfb?LWJEw7q zNTbuC92qr~L31o|dmF6%t1Ai^cF2;6EWQvu625_fP6_n6LH8TuI!YkZ>8|S`u@6Qu zxQu_}itSJEXbbcLjN+1HLYPo8`>HGrKm; zj~qTq-NpY7{^RUu=K@l0;g}kC!c+G_&RuZy!ok@&&PSgWm!0CK$A_B$e*B^F^%JJ| zm+74Ie6n00!5v1erC6?1DkOY1jaZOjVmmJ-GWZOx1oia?1J0=$=*R3AC=UmA{2D2j zPW1P}xqPdAfgPD9D&Cx8PB3Xna7#bChc_NE+*d|M$0sGm8dQ9GkXy(8KuVb0kd&EM zm_I5lE|N_r2NP_$JW_1QkVnRVuC?Z9HIL%Kv7Z#CjL9gjuCJ>A^b3y(oBvz41khVz zjVU=ZuOKHaTIoqfh9@u;v7@S5JIAzE=cgK#2-SuxL)B=q$T&-GQBjV02pO3dNfeb? zIl6yrPfK}LT)5b-N)p*0o@wZ@MBTyQXX0Ku2|MQY7HWFkPt+Y)Hma z=;AG-ax4bkP~KfQUph}|uIcJ+ugOc&N$of?U)Ye0E7L|NrJ9X`$IwC0+)R*O*Va^? zoe(MEgX3#M0qmAahj@TJb{a@Bu92sD@LMSw57@yA}4~NR}XRD*40Y}ER=g1HR7%<@{x&d)xbsNYO$>manBeSxh zNJ~m0G1y(_NxZL{l0|112~03W1GGBCick(gT)oL?}RLZ&!!WJ4*y@b0@n z`UDUN!9fHF1pOX)4b@5#{1LY^cjz_{5a91`$B_-G0Q2P8?}a-3+p}9k`0{%;^LOUR zJYwL6sDDHgpP_ztSn-@VvJo${kr;`Q`2Rni!+i*lIL~gk5A#V+Z8nKvcw_OjIC!)H zvkzjz`GoJ6Pz)5n2YHJ75Am|gb0BZ3z$W;hs`4Pv&iaYA$2Tx27`8bDo(IrI!Ge1V zl}05E?^PmZTR0*RqK z;J)J{Q%g}7p4ha4nzru#-j0TnjJQaJhzBJmDKQ$pqw{|tLmr!3+uqy9;Y%apa~u2n z3q{h3sndJV!lsgz2@5yu-oJ1A>RCNCxk-k~x`z64SLc7KDx+!I%FSCh>XQmuXKp)t z>&AskZ{NFmU@5YA#nhF1PG5WTjVmX&FCN!ekkT`8+vcTXiz1wIqySaR+Do7Q{6F8B zi$>4hbq)2&|I?jA$l^5%x1GHH-lIqNUO&2VPIuLeHG5B-*fqOM>U_|Z?FV5_>#ud= z*S!4TUnslvDW*nSvh&o9_Z~gGclE^91^v|ntB&1#=koe$7ds|O`!)0zr*1g&@HY$v zbv0YKVDsUNx9;4zdH&#rx$5}JIj27V$B$=QU9Ybuv`q7}18a}pK|5;x>Ah3P!im#Y zY(ISZ!i853ZCwWKzPjxcwDj+@TtIN;=%2s4xNi5w55E6(aPVJ0KDdlFE^MAZciG0B zd-v?zfE;ea5wPkNR}>41vbRXi*mwTT2cLfS@a>BSP(QM~j`p_Z+M*N{rexR-uj_Jj zaat4w=Ci=&Up#YU$D~daB|k4GBT2`%rqGMvb(guIa1jvHQjHy*Tix8+RD()|qN1at zv{HucUe-J4o>5{Ukw$z?}1v4r}6Bq zZ{1itRQ;Gk?i`xh^yJ3TjIx)#l;9oiz_7DV%uBX=L(+}`0VM^j{1SEP9AxZ)Tb zHZ7`R((={wIxNlvI!hZLAHl-oB#)!pgkrIX$EK4Ae&uF~Geb#{NkhX#3XL{0LhbGL z6&H|V2a4n>jan(;F@a;h$VIFa5JX{#RJ!Q+g!pK^8a7e#_q8kR^@6?f#Y(L{%Ai#W znIyN)p1uTXm`oR+mNhEdl4R6yU|%Rd>#YD3TZYht%1BdOd~B3P80zBChpZMr2~(QV z@=7bpi?WlAD!`EiS-(Ai-U^|yWf7(Xvl(=Kgi=xNw;rF9psI zgd&hf7>s(AbMI>B2;oJflr}Wg6j@?4042nR0?H)0Vue~I2@U3SOLBCCRLG%|ZF>Z)#Gb$dj4X_ZG8w|<0xpwk<;ak> zt|1|0(r^`Eq%etGF5$Cbj?9VzPek1-z`Vf*2Rknarn5P0CXEI-GK^D&C$t_W#a@BL z5F){udBz8ZP%w@R-G0m`-|cq??++YBT=4PGc_qRe8CJ15qyh}@8xR=arXm~q+5|vh zb2S@U0p<-Q0q&SQyf<_ld~L2~Lz7FOQ>WXnIM!m*#*ul%0DseCQOgr>WcVQ)XAawd zd1y*-*g556)*E5kMq(sJ;wj@fxOZW<)@LENQ1eRoPXF{cNb5%=o{rqYY`K;rFf=6yKv051 z$W%Im5lRgnHfI+=qJ=U-X-K*NHU{o�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 F{{RPcsxklo literal 0 HcmV?d00001 diff --git a/Modules/Filtering/Cuberille/test/Input/nucleon.mha b/Modules/Filtering/Cuberille/test/Input/nucleon.mha new file mode 100644 index 0000000000000000000000000000000000000000..52cce95dfa744aa4a22bfd001b58a77eaf90d857 GIT binary patch literal 18175 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 XsTTzw$YSfU{lESX-{1T|;X*Y49dw+$ literal 0 HcmV?d00001 diff --git a/Modules/Filtering/Cuberille/test/Input/silicium.mha b/Modules/Filtering/Cuberille/test/Input/silicium.mha new file mode 100644 index 0000000000000000000000000000000000000000..d33093e0d5220552f816fc387bcedc298a668ba0 GIT binary patch literal 38286 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 Pe~;#Zx1j#uTB82|H-MpJ literal 0 HcmV?d00001 From 522c87a6f3825550ed1ef7c95b92700d88bc78fc Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:13:14 -0400 Subject: [PATCH 05/84] ENH: Update test CMake configuration for modularization. --- Modules/Filtering/Cuberille/itk-module.cmake | 4 + .../Filtering/Cuberille/test/CMakeLists.txt | 553 ++++++++---------- .../Cuberille/test/CuberilleTest01.cxx | 20 +- 3 files changed, 263 insertions(+), 314 deletions(-) diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake index 007d50526fa..9ee90960798 100644 --- a/Modules/Filtering/Cuberille/itk-module.cmake +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -26,6 +26,10 @@ itk_module( ITKMesh TEST_DEPENDS ITKTestKernel + ITKQuadEdgeMesh + ITKQuadEdgeMeshFiltering + ITKThresholding + ITKIOImageBase EXCLUDE_FROM_DEFAULT DESCRIPTION "${DOCUMENTATION}" ) diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index 933a869d40b..dd2be321fac 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -1,340 +1,303 @@ -#Set output path -SET(TESTING_OUTPUT_PATH "${Cuberille_BINARY_DIR}/Testing/Temporary") -MAKE_DIRECTORY(${TESTING_OUTPUT_PATH}) +itk_module_test() -# Configure the Executables -ADD_EXECUTABLE( CuberilleTest01 CuberilleTest01.cxx ) -TARGET_LINK_LIBRARIES( CuberilleTest01 ${ITK_LIBRARIES} ITKQuadEdgeMesh ) +set(TESTING_OUTPUT_PATH "${Cuberille_BINARY_DIR}/Testing/Temporary") +set(DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Input) + +set(CuberilleTests CuberilleTest01.cxx) + +createtestdriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}") # Configure the Tests -ADD_TEST( - Cuberille_Blob0_00 - CuberilleTest01 - Test01 - ${DATA_PATH}/blob0.mha - ${TESTING_OUTPUT_PATH}/blob0-01.vtk - 200 # Iso-surface value - 8 # Expected number of points - 6 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Blob0_00 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob0.mha + ${TESTING_OUTPUT_PATH}/blob0-01.vtk 200 # Iso-surface value + 8 # Expected number of points + 6 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Blob1_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/blob1.mha - ${TESTING_OUTPUT_PATH}/blob1-01.vtk - 200 # Iso-surface value - 12 # Expected number of points - 10 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Blob1_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob1.mha + ${TESTING_OUTPUT_PATH}/blob1-01.vtk 200 # Iso-surface value + 12 # Expected number of points + 10 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Blob2_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/blob2.mha - ${TESTING_OUTPUT_PATH}/blob2-01.vtk - 200 # Iso-surface value - 14 # Expected number of points - 12 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Blob2_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob2.mha + ${TESTING_OUTPUT_PATH}/blob2-01.vtk 200 # Iso-surface value + 14 # Expected number of points + 12 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Blob3_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/blob3.mha - ${TESTING_OUTPUT_PATH}/blob3-01.vtk - 200 # Iso-surface value - 122 # Expected number of points - 180 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Blob3_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob3.mha + ${TESTING_OUTPUT_PATH}/blob3-01.vtk 200 # Iso-surface value + 122 # Expected number of points + 180 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Blob4_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/blob4.mha - ${TESTING_OUTPUT_PATH}/blob4-01.vtk - 200 # Iso-surface value - 2124 # Expected number of points - 2122 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Blob4_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob4.mha + ${TESTING_OUTPUT_PATH}/blob4-01.vtk 200 # Iso-surface value + 2124 # Expected number of points + 2122 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_MarschnerLobb_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/marschnerlobb.mha - ${TESTING_OUTPUT_PATH}/marschnerlobb-01.vtk - 55 # Iso-surface value - 20524 # Expected number of points - 22104 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 200 # Maximum number of steps +add_test( + NAME Cuberille_MarschnerLobb_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/marschnerlobb.mha + ${TESTING_OUTPUT_PATH}/marschnerlobb-01.vtk 55 # Iso-surface value + 20524 # Expected number of points + 22104 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 200 # Maximum number of steps ) -ADD_TEST( - Cuberille_Fuel_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/fuel.mha - ${TESTING_OUTPUT_PATH}/fuel-01.vtk - 15 # Iso-surface value - 5302 # Expected number of points - 5316 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Fuel_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha + ${TESTING_OUTPUT_PATH}/fuel-01.vtk 15 # Iso-surface value + 5302 # Expected number of points + 5316 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Fuel_02 - CuberilleTest01 - Test01 - ${DATA_PATH}/fuel.mha - ${TESTING_OUTPUT_PATH}/fuel-02.vtk - 15 # Iso-surface value - 5302 # Expected number of points - 5316 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Fuel_02 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha + ${TESTING_OUTPUT_PATH}/fuel-02.vtk 15 # Iso-surface value + 5302 # Expected number of points + 5316 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Fuel_03 - CuberilleTest01 - Test01 - ${DATA_PATH}/fuel.mha - ${TESTING_OUTPUT_PATH}/fuel-03.vtk - 15 # Iso-surface value - 5302 # Expected number of points - 10632 # Expected number of cells - 1 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Fuel_03 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha + ${TESTING_OUTPUT_PATH}/fuel-03.vtk 15 # Iso-surface value + 5302 # Expected number of points + 10632 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_HydrogenAtom_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/hydrogenAtom.mha - ${TESTING_OUTPUT_PATH}/hydrogenAtom-01.vtk - 15 # Iso-surface value - 29880 # Expected number of points - 29874 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_HydrogenAtom_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/hydrogenAtom.mha + ${TESTING_OUTPUT_PATH}/hydrogenAtom-01.vtk 15 # Iso-surface value + 29880 # Expected number of points + 29874 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Neghip_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/neghip.mha - ${TESTING_OUTPUT_PATH}/neghip-01.vtk - 55 # Iso-surface value - 15146 # Expected number of points - 15136 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Neghip_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha + ${TESTING_OUTPUT_PATH}/neghip-01.vtk 55 # Iso-surface value + 15146 # Expected number of points + 15136 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Neghip_02 - CuberilleTest01 - Test01 - ${DATA_PATH}/neghip.mha - ${TESTING_OUTPUT_PATH}/neghip-02.vtk - 55 # Iso-surface value - 15146 # Expected number of points - 15136 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Neghip_02 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha + ${TESTING_OUTPUT_PATH}/neghip-02.vtk 55 # Iso-surface value + 15146 # Expected number of points + 15136 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Neghip_03 - CuberilleTest01 - Test01 - ${DATA_PATH}/neghip.mha - ${TESTING_OUTPUT_PATH}/neghip-03.vtk - 55 # Iso-surface value - 15146 # Expected number of points - 30272 # Expected number of cells - 1 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Neghip_03 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha + ${TESTING_OUTPUT_PATH}/neghip-03.vtk 55 # Iso-surface value + 15146 # Expected number of points + 30272 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Nucleon_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/nucleon.mha - ${TESTING_OUTPUT_PATH}/nucleon-01.vtk - 140 # Iso-surface value - 3504 # Expected number of points - 3500 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Nucleon_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha + ${TESTING_OUTPUT_PATH}/nucleon-01.vtk 140 # Iso-surface value + 3504 # Expected number of points + 3500 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Nucleon_02 - CuberilleTest01 - Test01 - ${DATA_PATH}/nucleon.mha - ${TESTING_OUTPUT_PATH}/nucleon-02.vtk - 140 # Iso-surface value - 3504 # Expected number of points - 3500 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Nucleon_02 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha + ${TESTING_OUTPUT_PATH}/nucleon-02.vtk 140 # Iso-surface value + 3504 # Expected number of points + 3500 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Nucleon_03 - CuberilleTest01 - Test01 - ${DATA_PATH}/nucleon.mha - ${TESTING_OUTPUT_PATH}/nucleon-03.vtk - 140 # Iso-surface value - 3504 # Expected number of points - 7000 # Expected number of cells - 1 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Nucleon_03 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha + ${TESTING_OUTPUT_PATH}/nucleon-03.vtk 140 # Iso-surface value + 3504 # Expected number of points + 7000 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Silicium_01 - CuberilleTest01 - Test01 - ${DATA_PATH}/silicium.mha - ${TESTING_OUTPUT_PATH}/silicium-01.vtk - 85 # Iso-surface value - 20036 # Expected number of points - 20024 # Expected number of cells - 0 # Generate triangle faces - 0 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Silicium_01 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha + ${TESTING_OUTPUT_PATH}/silicium-01.vtk 85 # Iso-surface value + 20036 # Expected number of points + 20024 # Expected number of cells + 0 # Generate triangle faces + 0 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Silicium_02 - CuberilleTest01 - Test01 - ${DATA_PATH}/silicium.mha - ${TESTING_OUTPUT_PATH}/silicium-02.vtk - 85 # Iso-surface value - 20036 # Expected number of points - 20024 # Expected number of cells - 0 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Silicium_02 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha + ${TESTING_OUTPUT_PATH}/silicium-02.vtk 85 # Iso-surface value + 20036 # Expected number of points + 20024 # Expected number of cells + 0 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -ADD_TEST( - Cuberille_Silicium_03 - CuberilleTest01 - Test01 - ${DATA_PATH}/silicium.mha - ${TESTING_OUTPUT_PATH}/silicium-03.vtk - 85 # Iso-surface value - 20036 # Expected number of points - 40048 # Expected number of cells - 1 # Generate triangle faces - 1 # Project vertices to iso-surface - 0.2 # Surface distance threshold - 0.24 # Step length - 0.95 # Step length relaxation factor - 100 # Maximum number of steps +add_test( + NAME Cuberille_Silicium_03 + COMMAND + CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha + ${TESTING_OUTPUT_PATH}/silicium-03.vtk 85 # Iso-surface value + 20036 # Expected number of points + 40048 # Expected number of cells + 1 # Generate triangle faces + 1 # Project vertices to iso-surface + 0.2 # Surface distance threshold + 0.24 # Step length + 0.95 # Step length relaxation factor + 100 # Maximum number of steps ) -#ADD_TEST( +#add_test( # Cuberille_Engine_01 # CuberilleTest01 # Test01 -# ${DATA_PATH}/engine2.mha +# ${DATA_DIR}/engine2.mha # ${TESTING_OUTPUT_PATH}/engine2-01.vtk # 115 # Iso-surface value # 319918 # Expected number of points @@ -347,11 +310,11 @@ ADD_TEST( # 100 # Maximum number of steps #) -#ADD_TEST( +#add_test( # Cuberille_Bunny_01 # CuberilleTest01 # Test01 -# ${DATA_PATH}/bunny3.mha +# ${DATA_DIR}/bunny3.mha # ${TESTING_OUTPUT_PATH}/bunny3-01.vtk # 155 # unsigned char # #1550 # signed short diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 4706baa221a..429a3bb8003 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -14,12 +14,6 @@ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. PURPOSE. See the above copyright notices for more information. =========================================================================*/ -#if defined(_MSC_VER) -# pragma warning(disable : 4786) -#endif - -#define _SCL_SECURE_NO_WARNINGS - #define USE_BSPLINE_INTERPOLATOR 0 #define USE_MARCHING_CUBES 0 #define USE_QUAD_EDGE_MESH 0 @@ -28,10 +22,6 @@ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. #include #include -#ifndef NO_TESTING -# include "itkTestMain.h" -#endif - #include "itkTimeProbe.h" #include "itkImage.h" #include "itkMesh.h" @@ -47,16 +37,8 @@ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. #include "itkQuadEdgeMeshQuadricDecimation.h" #include "itkQuadEdgeMeshDecimationCriteria.h" -#ifndef NO_TESTING -void -RegisterTests() -{ - REGISTER_TEST(Test01); -} -#endif - int -Test01(int argc, char * argv[]) +CuberilleTest01(int argc, char * argv[]) { try { From 996bdfb2856a1ddd4dee3a39d64a702606775c93 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:14:31 -0400 Subject: [PATCH 06/84] COMP: QuadEdgeMeshQuadricDecimation renamed to QuadricDecimationQuadEdgeMeshFilter. --- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 429a3bb8003..90a662376c3 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -34,7 +34,7 @@ See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. #include "itkVTKPolyDataWriter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkBSplineInterpolateImageFunction.h" -#include "itkQuadEdgeMeshQuadricDecimation.h" +#include "itkQuadricDecimationQuadEdgeMeshFilter.h" #include "itkQuadEdgeMeshDecimationCriteria.h" int @@ -158,7 +158,7 @@ CuberilleTest01(int argc, char * argv[]) DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); decimateCriterion->SetTopologicalChange(false); decimateCriterion->SetNumberOfElements(2000); - typedef itk::QuadEdgeMeshQuadricDecimation DecimationType; + typedef itk::QuadricDecimationQuadEdgeMeshFilter DecimationType; DecimationType::Pointer decimate = DecimationType::New(); decimate->SetInput(outputMesh); decimate->SetCriterion(decimateCriterion); From c6d8260c95a20169ba858d61d2f313ceeb7422b6 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:16:11 -0400 Subject: [PATCH 07/84] COMP: TimeProbe::GetMeanTime renamed to TimeProbe::GetMean. --- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 90a662376c3..3b4d6d9d85a 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -177,7 +177,7 @@ CuberilleTest01(int argc, char * argv[]) writer->Update(); // Assert number of points/cells - std::cout << "Polygonization took " << time.GetMeanTime() << " seconds" << std::endl; + std::cout << "Polygonization took " << time.GetMean() << " seconds" << std::endl; std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices "; std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl; if (ExpectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != ExpectedNumberOfPoints) From d024e2cb2e7a751206446a41e3999e1d09469a81 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:19:04 -0400 Subject: [PATCH 08/84] BUG: Update copyright headers. --- .../include/itkCuberilleImageToMeshFilter.h | 31 ++++++++++--------- .../include/itkCuberilleImageToMeshFilter.txx | 31 ++++++++++--------- .../Cuberille/test/CuberilleTest01.cxx | 31 ++++++++++--------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 6a8e8d23a6d..8367a2f6ebd 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -1,19 +1,20 @@ /*========================================================================= - - Program: Insight Segmentation & Registration Toolkit - Module: $RCSfile: itkCuberilleImageToMeshFilter.h,v $ - Language: C++ - Date: $Date$ - Version: $Revision$ - - Copyright (c) Insight Software Consortium. All rights reserved. - See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ + * + * Copyright Insight Software Consortium + * + * 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. + * + *=========================================================================*/ #ifndef __itkCuberilleImageToMeshFilter_h #define __itkCuberilleImageToMeshFilter_h diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx index 6e183ce8842..f9cf1f9af4f 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx @@ -1,19 +1,20 @@ /*========================================================================= - -Program: Insight Segmentation & Registration Toolkit -Module: $RCSfile: itkCuberilleImageToMeshFilter.txx,v $ -Language: C++ -Date: $Date$ -Version: $Revision$ - -Copyright (c) Insight Software Consortium. All rights reserved. -See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. - -This software is distributed WITHOUT ANY WARRANTY; without even -the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ + * + * Copyright Insight Software Consortium + * + * 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. + * + *=========================================================================*/ #ifndef __itkCuberilleImageToMeshFilter_txx #define __itkCuberilleImageToMeshFilter_txx diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 3b4d6d9d85a..5a8eb09fb5c 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -1,19 +1,20 @@ /*========================================================================= - -Program: Insight Segmentation & Registration Toolkit -Module: $RCSfile: CuberilleTest01.cxx,v $ -Language: C++ -Date: $Date$ -Version: $Revision$ - -Copyright (c) Insight Software Consortium. All rights reserved. -See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the above copyright notices for more information. - -=========================================================================*/ + * + * Copyright Insight Software Consortium + * + * 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. + * + *=========================================================================*/ #define USE_BSPLINE_INTERPOLATOR 0 #define USE_MARCHING_CUBES 0 #define USE_QUAD_EDGE_MESH 0 From 21eb1c8532a750d87123864b44ed1627215220f9 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:20:24 -0400 Subject: [PATCH 09/84] STYLE: Remove unnecessary include's. --- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 5a8eb09fb5c..03ed8c33369 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -20,9 +20,6 @@ #define USE_QUAD_EDGE_MESH 0 #define USE_DECIMATION 0 -#include -#include - #include "itkTimeProbe.h" #include "itkImage.h" #include "itkMesh.h" From 69128aae0fb60602dbcf46bf9ebf420bdb76fbcc Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:22:25 -0400 Subject: [PATCH 10/84] STYLE: Update header names for ITKv4. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 6 +++--- ...geToMeshFilter.txx => itkCuberilleImageToMeshFilter.hxx} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename Modules/Filtering/Cuberille/include/{itkCuberilleImageToMeshFilter.txx => itkCuberilleImageToMeshFilter.hxx} (99%) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 8367a2f6ebd..e7263baa3a0 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -15,8 +15,8 @@ * limitations under the License. * *=========================================================================*/ -#ifndef __itkCuberilleImageToMeshFilter_h -#define __itkCuberilleImageToMeshFilter_h +#ifndef itkCuberilleImageToMeshFilter_h +#define itkCuberilleImageToMeshFilter_h #define DEBUG_PRINT 0 #define USE_GRADIENT_RECURSIVE_GAUSSIAN 0 @@ -109,7 +109,7 @@ namespace itk * */ template > -class ITK_EXPORT CuberilleImageToMeshFilter : public ImageToMeshFilter +class CuberilleImageToMeshFilter : public ImageToMeshFilter { public: /** Standard "Self" typedef. */ diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx similarity index 99% rename from Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx rename to Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index f9cf1f9af4f..9371575a6b2 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.txx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -15,8 +15,8 @@ * limitations under the License. * *=========================================================================*/ -#ifndef __itkCuberilleImageToMeshFilter_txx -#define __itkCuberilleImageToMeshFilter_txx +#ifndef itkCuberilleImageToMeshFilter_hxx +#define itkCuberilleImageToMeshFilter_hxx #define SWAP(x, y) \ unsigned int t; \ From 3d83af3ccd75508a7fb643c3ff0b31280ac0c832 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:23:36 -0400 Subject: [PATCH 11/84] BUG: Fix CuberilleInDoxygenGroup test. --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index e7263baa3a0..f5504010c03 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -107,6 +107,8 @@ namespace itk * * \author Dan Mueller, Philips Healthcare, dan dot muel at gmail dot com * + * \ingroup Cuberille + * */ template > class CuberilleImageToMeshFilter : public ImageToMeshFilter From 5cdad19b03bc67546ce87b23060ccfcd78b5ab1b Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 14 Oct 2015 15:50:28 -0400 Subject: [PATCH 12/84] COMP: Fix .txx/.hxx #include. --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index f5504010c03..ccaed3f3a5b 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -387,7 +387,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Thu, 15 Oct 2015 17:24:53 -0400 Subject: [PATCH 13/84] ENH: Add wrapping. --- Modules/Filtering/Cuberille/wrapping/CMakeLists.txt | 3 +++ .../Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 Modules/Filtering/Cuberille/wrapping/CMakeLists.txt create mode 100644 Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap diff --git a/Modules/Filtering/Cuberille/wrapping/CMakeLists.txt b/Modules/Filtering/Cuberille/wrapping/CMakeLists.txt new file mode 100644 index 00000000000..23dd508f7a5 --- /dev/null +++ b/Modules/Filtering/Cuberille/wrapping/CMakeLists.txt @@ -0,0 +1,3 @@ +itk_wrap_module(Cuberille) +itk_auto_load_submodules() +itk_end_wrap_module() diff --git a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap new file mode 100644 index 00000000000..e2218e69ed2 --- /dev/null +++ b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap @@ -0,0 +1,7 @@ +itk_wrap_class("itk::CuberilleImageToMeshFilter" POINTER) +foreach(d ${ITK_WRAP_IMAGE_DIMS}) + foreach(t ${ITK_WRAP_SCALAR}) + itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_D}${d}$" "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_D}, ${d} >") + endforeach() +endforeach() +itk_end_wrap_class() From f2c43ef183be49fc1354b0e2a60db7322bdc78f4 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 16 Oct 2015 17:21:06 -0400 Subject: [PATCH 14/84] COMP: Remove unused typedefs in the test. --- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 03ed8c33369..f9707a8cff9 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -58,8 +58,6 @@ CuberilleTest01(int argc, char * argv[]) typedef itk::LinearInterpolateImageFunction InterpolatorType; #endif typedef itk::CuberilleImageToMeshFilter CuberilleType; - typedef itk::BinaryMask3DMeshSource MarchingCubesType; - typedef itk::BinaryThresholdImageFilter BinaryThresholdFilterType; // Read command-line parameters if (argc < 6) From ffadf71d72575a327be0da891e92a090658a800e Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 16 Oct 2015 17:32:29 -0400 Subject: [PATCH 15/84] COMP: Address SetInput override virtual warning. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses: /home/matt/src/ITK/Modules/Remote/Cuberille/include/itkCuberilleImageToMeshFilter.h: In instantiation of ‘class itk::CuberilleImageToMeshFilter, itk::Mesh, itk::LinearInterpolateImageFunction > >’: /home/matt/src/ITK/Modules/Remote/Cuberille/test/CuberilleTest01.cxx:123:16: required from here /home/matt/src/ITK/Modules/Core/Common/include/itkProcessObject.h:522:16: warning: ‘virtual void itk::ProcessObject::SetInput(const DataObjectIdentifierType&, itk::DataObject*)’ was hidden [-Woverloaded-virtual] virtual void SetInput(const DataObjectIdentifierType & key, DataObject *input); ^ In file included from /home/matt/src/ITK/Modules/Remote/Cuberille/include/itkCuberilleImageToMeshFilter.h:347:0, from /home/matt/src/ITK/Modules/Remote/Cuberille/test/CuberilleTest01.cxx:27: /home/matt/src/ITK/Modules/Remote/Cuberille/include/itkCuberilleImageToMeshFilter.hxx:53:1: warning: by ‘void itk::CuberilleImageToMeshFilter::SetInput(const InputImageType*) [with TInputImage = itk::Image; TOutputMesh = itk::Mesh; TInterpolator = itk::LinearInterpolateImageFunction >; itk::CuberilleImageToMeshFilter::InputImageType = itk::Image]’ [-Woverloaded-virtual] CuberilleImageToMeshFilter ^ --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index ccaed3f3a5b..20bc6f196df 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -184,6 +184,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Fri, 16 Oct 2015 17:35:26 -0400 Subject: [PATCH 16/84] DOC: Fix description typo. --- Modules/Filtering/Cuberille/itk-module.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake index 9ee90960798..bbe1865e333 100644 --- a/Modules/Filtering/Cuberille/itk-module.cmake +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -3,7 +3,7 @@ set( "This module implements cuberille implicit surface polygonization for ITK. This method operates by diving the surface into a number of small cubes called cuberilles. Each cuberille is centered at a -pixel lying on th eiso-surface and then quadrilaterals are generated for each +pixel lying on the iso-surface and then quadrilaterals are generated for each face. The original approach is improved by projecting the vertices of each cuberille onto the implicit surface, smoothing the typical block-like resultant mesh. From 90645dc0a13de25ddebf10e35e069a38cb900d93 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Mon, 19 Oct 2015 13:38:28 -0400 Subject: [PATCH 17/84] BUG: Address KWStyle tests. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 20bc6f196df..223e7758d4d 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -248,7 +248,8 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter class VertexLookupMap { From 0056f7006eb3e08fe3fee957b43a774021e6d11d Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Mon, 19 Oct 2015 13:43:22 -0400 Subject: [PATCH 18/84] BUG: Fix test temporary output dir. The defined directory is not correct when built as a Remote module. --- .../Filtering/Cuberille/test/CMakeLists.txt | 43 +++++++++---------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index dd2be321fac..c19f7f4bba3 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -1,6 +1,5 @@ itk_module_test() -set(TESTING_OUTPUT_PATH "${Cuberille_BINARY_DIR}/Testing/Temporary") set(DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Input) set(CuberilleTests CuberilleTest01.cxx) @@ -12,7 +11,7 @@ add_test( NAME Cuberille_Blob0_00 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob0.mha - ${TESTING_OUTPUT_PATH}/blob0-01.vtk 200 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/blob0-01.vtk 200 # Iso-surface value 8 # Expected number of points 6 # Expected number of cells 0 # Generate triangle faces @@ -27,7 +26,7 @@ add_test( NAME Cuberille_Blob1_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob1.mha - ${TESTING_OUTPUT_PATH}/blob1-01.vtk 200 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/blob1-01.vtk 200 # Iso-surface value 12 # Expected number of points 10 # Expected number of cells 0 # Generate triangle faces @@ -42,7 +41,7 @@ add_test( NAME Cuberille_Blob2_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob2.mha - ${TESTING_OUTPUT_PATH}/blob2-01.vtk 200 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/blob2-01.vtk 200 # Iso-surface value 14 # Expected number of points 12 # Expected number of cells 0 # Generate triangle faces @@ -57,7 +56,7 @@ add_test( NAME Cuberille_Blob3_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob3.mha - ${TESTING_OUTPUT_PATH}/blob3-01.vtk 200 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/blob3-01.vtk 200 # Iso-surface value 122 # Expected number of points 180 # Expected number of cells 0 # Generate triangle faces @@ -72,7 +71,7 @@ add_test( NAME Cuberille_Blob4_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob4.mha - ${TESTING_OUTPUT_PATH}/blob4-01.vtk 200 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/blob4-01.vtk 200 # Iso-surface value 2124 # Expected number of points 2122 # Expected number of cells 0 # Generate triangle faces @@ -87,7 +86,7 @@ add_test( NAME Cuberille_MarschnerLobb_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/marschnerlobb.mha - ${TESTING_OUTPUT_PATH}/marschnerlobb-01.vtk 55 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/marschnerlobb-01.vtk 55 # Iso-surface value 20524 # Expected number of points 22104 # Expected number of cells 0 # Generate triangle faces @@ -102,7 +101,7 @@ add_test( NAME Cuberille_Fuel_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha - ${TESTING_OUTPUT_PATH}/fuel-01.vtk 15 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/fuel-01.vtk 15 # Iso-surface value 5302 # Expected number of points 5316 # Expected number of cells 0 # Generate triangle faces @@ -117,7 +116,7 @@ add_test( NAME Cuberille_Fuel_02 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha - ${TESTING_OUTPUT_PATH}/fuel-02.vtk 15 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/fuel-02.vtk 15 # Iso-surface value 5302 # Expected number of points 5316 # Expected number of cells 0 # Generate triangle faces @@ -132,7 +131,7 @@ add_test( NAME Cuberille_Fuel_03 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha - ${TESTING_OUTPUT_PATH}/fuel-03.vtk 15 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/fuel-03.vtk 15 # Iso-surface value 5302 # Expected number of points 10632 # Expected number of cells 1 # Generate triangle faces @@ -147,7 +146,7 @@ add_test( NAME Cuberille_HydrogenAtom_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/hydrogenAtom.mha - ${TESTING_OUTPUT_PATH}/hydrogenAtom-01.vtk 15 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom-01.vtk 15 # Iso-surface value 29880 # Expected number of points 29874 # Expected number of cells 0 # Generate triangle faces @@ -162,7 +161,7 @@ add_test( NAME Cuberille_Neghip_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha - ${TESTING_OUTPUT_PATH}/neghip-01.vtk 55 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/neghip-01.vtk 55 # Iso-surface value 15146 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces @@ -177,7 +176,7 @@ add_test( NAME Cuberille_Neghip_02 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha - ${TESTING_OUTPUT_PATH}/neghip-02.vtk 55 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/neghip-02.vtk 55 # Iso-surface value 15146 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces @@ -192,7 +191,7 @@ add_test( NAME Cuberille_Neghip_03 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha - ${TESTING_OUTPUT_PATH}/neghip-03.vtk 55 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/neghip-03.vtk 55 # Iso-surface value 15146 # Expected number of points 30272 # Expected number of cells 1 # Generate triangle faces @@ -207,7 +206,7 @@ add_test( NAME Cuberille_Nucleon_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha - ${TESTING_OUTPUT_PATH}/nucleon-01.vtk 140 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/nucleon-01.vtk 140 # Iso-surface value 3504 # Expected number of points 3500 # Expected number of cells 0 # Generate triangle faces @@ -222,7 +221,7 @@ add_test( NAME Cuberille_Nucleon_02 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha - ${TESTING_OUTPUT_PATH}/nucleon-02.vtk 140 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/nucleon-02.vtk 140 # Iso-surface value 3504 # Expected number of points 3500 # Expected number of cells 0 # Generate triangle faces @@ -237,7 +236,7 @@ add_test( NAME Cuberille_Nucleon_03 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha - ${TESTING_OUTPUT_PATH}/nucleon-03.vtk 140 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/nucleon-03.vtk 140 # Iso-surface value 3504 # Expected number of points 7000 # Expected number of cells 1 # Generate triangle faces @@ -252,7 +251,7 @@ add_test( NAME Cuberille_Silicium_01 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha - ${TESTING_OUTPUT_PATH}/silicium-01.vtk 85 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/silicium-01.vtk 85 # Iso-surface value 20036 # Expected number of points 20024 # Expected number of cells 0 # Generate triangle faces @@ -267,7 +266,7 @@ add_test( NAME Cuberille_Silicium_02 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha - ${TESTING_OUTPUT_PATH}/silicium-02.vtk 85 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/silicium-02.vtk 85 # Iso-surface value 20036 # Expected number of points 20024 # Expected number of cells 0 # Generate triangle faces @@ -282,7 +281,7 @@ add_test( NAME Cuberille_Silicium_03 COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha - ${TESTING_OUTPUT_PATH}/silicium-03.vtk 85 # Iso-surface value + ${ITK_TEST_OUTPUT_DIR}/silicium-03.vtk 85 # Iso-surface value 20036 # Expected number of points 40048 # Expected number of cells 1 # Generate triangle faces @@ -298,7 +297,7 @@ add_test( # CuberilleTest01 # Test01 # ${DATA_DIR}/engine2.mha -# ${TESTING_OUTPUT_PATH}/engine2-01.vtk +# ${ITK_TEST_OUTPUT_DIR}/engine2-01.vtk # 115 # Iso-surface value # 319918 # Expected number of points # 640112 # Expected number of cells @@ -315,7 +314,7 @@ add_test( # CuberilleTest01 # Test01 # ${DATA_DIR}/bunny3.mha -# ${TESTING_OUTPUT_PATH}/bunny3-01.vtk +# ${ITK_TEST_OUTPUT_DIR}/bunny3-01.vtk # 155 # unsigned char # #1550 # signed short # 1021438 # Expected number of points From 0d1b3a382a3b34e23e23536d1b3895f128d5504d Mon Sep 17 00:00:00 2001 From: Davis Vigneault Date: Fri, 5 Feb 2016 14:15:39 +0000 Subject: [PATCH 19/84] COMP: Wsign-compare warning --- .../include/itkCuberilleImageToMeshFilter.hxx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 9371575a6b2..d01f04e5e9c 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -74,7 +74,7 @@ CuberilleImageToMeshFilter::GenerateDat // Compute maximum spacing m_MaxSpacing = image->GetSpacing()[0]; - for (int i = 1; i < InputImageType::ImageDimension; i++) + for (unsigned int i = 1; i < InputImageType::ImageDimension; ++i) { m_MaxSpacing = vnl_math_max(m_MaxSpacing, image->GetSpacing()[i]); } @@ -157,11 +157,11 @@ CuberilleImageToMeshFilter::GenerateDat // Re-initialize for new pixel numFaces = 0; - for (i = 0; i < 6; i++) + for (i = 0; i < 6; ++i) { faceHasQuad[i] = false; } - for (i = 0; i < 8; i++) + for (i = 0; i < 8; ++i) { vertexHasQuad[i] = false; } @@ -176,7 +176,7 @@ CuberilleImageToMeshFilter::GenerateDat } // Compute which faces (if any) have quads - for (i = 0; i < 6; i++) + for (i = 0; i < 6; ++i) { // NOTE: suitability check above means center <= m_IsoSurfaceValue faceHasQuad[i] = (it.GetPixel(offset[i]) < m_IsoSurfaceValue); @@ -191,7 +191,7 @@ CuberilleImageToMeshFilter::GenerateDat if (numFaces > 0) { // Create vertices - for (i = 0; i < 8; i++) + for (i = 0; i < 8; ++i) { if (vertexHasQuad[i]) { @@ -392,7 +392,7 @@ CuberilleImageToMeshFilter::AddQuadFace { // Get vertices PointType v[4]; - for (unsigned int i = 0; i < 4; i++) + for (unsigned int i = 0; i < 4; ++i) { v[i] = mesh->GetPoints()->GetElement(face[i]); } @@ -469,7 +469,7 @@ CuberilleImageToMeshFilter::ProjectVert normal.Normalize(); // Step along both directions of normal - for (i = 0; i < InputImageType::ImageDimension; i++) + for (i = 0; i < InputImageType::ImageDimension; ++i) { temp[0][i] = vertex[i] + (normal[i] * +1.0 * step); temp[1][i] = vertex[i] + (normal[i] * -1.0 * step); @@ -531,11 +531,11 @@ CuberilleImageToMeshFilter::ProjectVert for (sign = -1.0; sign <= 1.0; sign += 2.0) { k = (sign == -1.0) ? 0 : 1; - for (j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; j++) + for (j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; ++j) { // Compute current location along line d = (double)j / ((double)m_ProjectVertexMaximumNumberOfSteps / 2.0); - for (i = 0; i < InputImageType::ImageDimension; i++) + for (i = 0; i < InputImageType::ImageDimension; ++i) { temp[i] = vertex[i] + (normal[i] * sign * m_ProjectVertexStepLength * d); } @@ -582,7 +582,7 @@ CuberilleImageToMeshFilter::ProjectVert // Step along the normal towards the iso-surface value sign = (value < m_IsoSurfaceValue) ? +1.0 : -1.0; - for (i = 0; i < InputImageType::ImageDimension; i++) + for (i = 0; i < InputImageType::ImageDimension; ++i) { vertex[i] += (normal[i] * sign * step); } From e37827b9ce3478194051fc15564f4d685d621905 Mon Sep 17 00:00:00 2001 From: Davis Vigneault Date: Fri, 5 Feb 2016 14:46:39 +0000 Subject: [PATCH 20/84] STYLE: Null to ITK_NULLPTR --- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 4 ++-- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 9371575a6b2..240b1683753 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -47,7 +47,7 @@ CuberilleImageToMeshFilter::CuberilleIm template CuberilleImageToMeshFilter::~CuberilleImageToMeshFilter() { - m_GradientInterpolator = NULL; + m_GradientInterpolator = ITK_NULLPTR; } template @@ -613,7 +613,7 @@ CuberilleImageToMeshFilter::ComputeGrad m_GradientInterpolator = GradientInterpolatorType::New(); m_GradientInterpolator->SetInputImage(gradientFilter->GetOutput()); gradientFilter->GetOutput()->DisconnectPipeline(); - gradientFilter = NULL; + gradientFilter = ITK_NULLPTR; } } diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index f9707a8cff9..19dc350747c 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -103,7 +103,7 @@ CuberilleTest01(int argc, char * argv[]) input->DisconnectPipeline(); // Create output mesh - MeshType::Pointer outputMesh = NULL; + MeshType::Pointer outputMesh = ITK_NULLPTR; itk::TimeProbe time; #if USE_MARCHING_CUBES From 2d177e830eddf80d1d07db725f272d5ef0ea82b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Tue, 26 Apr 2016 09:47:29 -0400 Subject: [PATCH 21/84] BUG: Fixing appearance of Not-A-Number values when gradient is 0 --- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 163e5618626..1aeaefde7fe 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -568,6 +568,10 @@ CuberilleImageToMeshFilter::ProjectVert { // Compute normal vector normal = m_GradientInterpolator->Evaluate(vertex); + if (normal.GetSquaredNorm() == 0) + { + break; + } normal.Normalize(); // Compute whether vertex is close enough to iso-surface value From dbc80440e2c976fc18d6aee5f8ce9681411a0989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Tue, 26 Apr 2016 16:33:12 -0400 Subject: [PATCH 22/84] ENH: Taking advantage of covariant vector's normalize returning the norm after today's patch to ITK --- .../include/itkCuberilleImageToMeshFilter.hxx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 1aeaefde7fe..a025cb59d8f 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -466,7 +466,10 @@ CuberilleImageToMeshFilter::ProjectVert { // Compute normal vector GradientPixelType normal = m_GradientInterpolator->Evaluate(vertex); - normal.Normalize(); + if (normal.Normalize() == 0.0) // old norm was zero + { + break; + } // Step along both directions of normal for (i = 0; i < InputImageType::ImageDimension; ++i) @@ -525,7 +528,10 @@ CuberilleImageToMeshFilter::ProjectVert // Compute normal vector normal = m_GradientInterpolator->Evaluate(vertex); - normal.Normalize(); + if (normal.Normalize() == 0.0) // old norm was zero + { + break; + } // Search on both sides of the line for (sign = -1.0; sign <= 1.0; sign += 2.0) @@ -568,11 +574,10 @@ CuberilleImageToMeshFilter::ProjectVert { // Compute normal vector normal = m_GradientInterpolator->Evaluate(vertex); - if (normal.GetSquaredNorm() == 0) + if (normal.Normalize() == 0.0) // old norm was zero { break; } - normal.Normalize(); // Compute whether vertex is close enough to iso-surface value value = m_Interpolator->Evaluate(vertex); From a7b101bfe126d1fd9cb9482c5f38a2134173a4ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Thu, 28 Apr 2016 10:06:17 -0400 Subject: [PATCH 23/84] ENH: Explicitly requesting ITK version 4.10 --- Modules/Filtering/Cuberille/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt index 02513d97920..ef3e2e393a8 100644 --- a/Modules/Filtering/Cuberille/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.9) project(Cuberille) if(NOT ITK_SOURCE_DIR) - find_package(ITK REQUIRED) + find_package(ITK 4.10 REQUIRED) list(APPEND CMAKE_MODULE_PATH ${ITK_CMAKE_DIR}) include(ITKModuleExternal) else() From e74af7ba35420a9fbb6ef7770072f4ca4d2723e5 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 25 May 2016 00:13:35 -0400 Subject: [PATCH 24/84] BUG: Fix CMake version check: 2.9 should be 2.8.9. --- Modules/Filtering/Cuberille/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt index ef3e2e393a8..ffd30dc4959 100644 --- a/Modules/Filtering/Cuberille/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.9) +cmake_minimum_required(VERSION 2.8.9) project(Cuberille) if(NOT ITK_SOURCE_DIR) From cd483e7572f12a05fa57ba9045e51ff34d44ea7b Mon Sep 17 00:00:00 2001 From: Zach Williamson Date: Fri, 24 Jun 2016 10:28:44 -0500 Subject: [PATCH 25/84] STYLE: Use Macro for Function Deletion --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 223e7758d4d..aa4308c7374 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -244,9 +244,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Mon, 1 Aug 2016 16:38:15 -0400 Subject: [PATCH 26/84] BUG: Instatiate itkCuberilleImageToMeshFilter --- .../wrapping/itkCuberilleImageToMeshFilter.wrap | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap index e2218e69ed2..23835531783 100644 --- a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap +++ b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap @@ -1,7 +1,10 @@ -itk_wrap_class("itk::CuberilleImageToMeshFilter" POINTER) +itk_wrap_class("itk::CuberilleImageToMeshFilter" POINTER_WITH_2_SUPERCLASSES) +unique(types "${WRAP_ITK_REAL};D") foreach(d ${ITK_WRAP_IMAGE_DIMS}) - foreach(t ${ITK_WRAP_SCALAR}) - itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_D}${d}$" "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_D}, ${d} >") + foreach(t ${types}) + itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}DSM${ITKM_${t}}${d}${d}${ITKM_F}${ITKM_F}" + "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d},itk::DefaultStaticMeshTraits<${ITKT_${t}},${d},${d},${ITKT_F},${ITKT_F} > >" + ) endforeach() endforeach() itk_end_wrap_class() From a526118e5ccdfca359b07387387a51916b181ff3 Mon Sep 17 00:00:00 2001 From: Jon Haitz Legarreta Date: Sun, 19 Feb 2017 10:26:25 +0100 Subject: [PATCH 27/84] ENH: Improve the itk::CuberilleImageToMeshFilter coverage. Exercise basic object methods. Exercise all Get methods using the TEST_SET_GET_VALUE macro. Style changes to the test in order to improve the readability. --- .../Cuberille/test/CuberilleTest01.cxx | 317 ++++++++++-------- 1 file changed, 184 insertions(+), 133 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 19dc350747c..0f6dcf4a7e8 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -34,166 +34,217 @@ #include "itkBSplineInterpolateImageFunction.h" #include "itkQuadricDecimationQuadEdgeMeshFilter.h" #include "itkQuadEdgeMeshDecimationCriteria.h" +#include "itkTestingMacros.h" + int CuberilleTest01(int argc, char * argv[]) { - try + if (argc < 6) { - // Typedefs - const unsigned int Dimension = 3; - typedef unsigned char PixelType; - // typedef signed short PixelType; - typedef itk::Image ImageType; + std::cout << "Usage: " << argv[0]; + std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells"; + std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] "; + std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]"; + std::cout << std::endl; + return EXIT_FAILURE; + } + + // Typedefs + const unsigned int Dimension = 3; + typedef unsigned char PixelType; + // typedef signed short PixelType; + typedef itk::Image ImageType; + #if USE_QUAD_EDGE_MESH | USE_DECIMATION - typedef itk::QuadEdgeMesh MeshType; + typedef itk::QuadEdgeMesh MeshType; #else - typedef itk::Mesh MeshType; + typedef itk::Mesh MeshType; #endif - typedef itk::ImageFileReader ImageFileReaderType; - typedef itk::VTKPolyDataWriter MeshFileWriterType; + typedef itk::ImageFileReader ImageFileReaderType; + typedef itk::VTKPolyDataWriter MeshFileWriterType; #if USE_BSPLINE_INTERPOLATOR - typedef itk::BSplineInterpolateImageFunction InterpolatorType; + typedef itk::BSplineInterpolateImageFunction InterpolatorType; #else - typedef itk::LinearInterpolateImageFunction InterpolatorType; + typedef itk::LinearInterpolateImageFunction InterpolatorType; #endif - typedef itk::CuberilleImageToMeshFilter CuberilleType; - - // Read command-line parameters - if (argc < 6) - { - std::cout << "USAGE: " << argv[0]; - std::cout << "InputImage OutputMesh IsoSurfaceValue ExpectedNumberOfPoints ExpectedNumberOfCells"; - std::cout << "[GenerateTriangleFaces] [ProjectToIsoSurface] "; - std::cout << "[SurfaceDistanceThreshold] [StepLength] [StepLengthRelax] [MaximumNumberOfSteps]"; - std::cout << std::endl; - return EXIT_FAILURE; - } - int arg = 1; - char * FilenameInputImage = argv[arg++]; - char * FilenameOutputMesh = argv[arg++]; - PixelType IsoSurfaceValue = atoi(argv[arg++]); - unsigned int ExpectedNumberOfPoints = atoi(argv[arg++]); - unsigned int ExpectedNumberOfCells = atoi(argv[arg++]); - bool GenerateTriangleFaces = true; - if (argc > arg) - GenerateTriangleFaces = atoi(argv[arg++]); - bool ProjectToIsoSurface = true; - if (argc > arg) - ProjectToIsoSurface = atoi(argv[arg++]); - double SurfaceDistanceThreshold = 0.5; - if (argc > arg) - SurfaceDistanceThreshold = atof(argv[arg++]); - double StepLength = 0.25; - if (argc > arg) - StepLength = atof(argv[arg++]); - double StepLengthRelax = 0.95; - if (argc > arg) - StepLengthRelax = atof(argv[arg++]); - unsigned int MaximumNumberOfSteps = 50; - if (argc > arg) - MaximumNumberOfSteps = atoi(argv[arg++]); - - // Read input image - std::cout << "Reading input image: " << FilenameInputImage << std::endl; - ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); - reader->SetFileName(FilenameInputImage); - reader->UpdateLargestPossibleRegion(); - ImageType::Pointer input = reader->GetOutput(); - input->DisconnectPipeline(); - - // Create output mesh - MeshType::Pointer outputMesh = ITK_NULLPTR; - itk::TimeProbe time; + typedef itk::CuberilleImageToMeshFilter CuberilleType; + + + // Read command-line parameters + int arg = 1; + char * filenameInputImage = argv[arg++]; + char * filenameOutputMesh = argv[arg++]; + PixelType isoSurfaceValue = atoi(argv[arg++]); + unsigned int expectedNumberOfPoints = atoi(argv[arg++]); + unsigned int expectedNumberOfCells = atoi(argv[arg++]); + + bool generateTriangleFaces = true; + if (argc > arg) + { + generateTriangleFaces = atoi(argv[arg++]); + } + + bool projectToIsoSurface = true; + if (argc > arg) + { + projectToIsoSurface = atoi(argv[arg++]); + } + + double surfaceDistanceThreshold = 0.5; + if (argc > arg) + { + surfaceDistanceThreshold = atof(argv[arg++]); + } + + double stepLength = 0.25; + if (argc > arg) + { + stepLength = atof(argv[arg++]); + } + + double stepLengthRelax = 0.95; + if (argc > arg) + { + stepLengthRelax = atof(argv[arg++]); + } + + unsigned int maximumNumberOfSteps = 50; + if (argc > arg) + { + maximumNumberOfSteps = atoi(argv[arg++]); + } + + // Read input image + ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); + reader->SetFileName(filenameInputImage); + + TRY_EXPECT_NO_EXCEPTION(reader->UpdateLargestPossibleRegion()); + + ImageType::Pointer input = reader->GetOutput(); + input->DisconnectPipeline(); + + // Create output mesh + MeshType::Pointer outputMesh = ITK_NULLPTR; + itk::TimeProbe time; #if USE_MARCHING_CUBES - // Create marching cubes - std::cout << "Creating marching cubes mesh..." << std::endl; - BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New(); - threshold->SetInput(input); - threshold->SetLowerThreshold(IsoSurfaceValue); - threshold->SetUpperThreshold(itk::NumericTraits::max()); - threshold->SetInsideValue(itk::NumericTraits::One); - threshold->SetOutsideValue(itk::NumericTraits::Zero); - threshold->UpdateLargestPossibleRegion(); - MarchingCubesType::Pointer marching = MarchingCubesType::New(); - marching->SetInput(threshold->GetOutput()); - time.Start(); - marching->Update(); - time.Stop(); - outputMesh = marching->GetOutput(); - outputMesh->DisconnectPipeline(); + // Create marching cubes mesh + BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New(); + threshold->SetInput(input); + threshold->SetLowerThreshold(IsoSurfaceValue); + threshold->SetUpperThreshold(itk::NumericTraits::max()); + threshold->SetInsideValue(itk::NumericTraits::One); + threshold->SetOutsideValue(itk::NumericTraits::Zero); + threshold->UpdateLargestPossibleRegion(); + + MarchingCubesType::Pointer marching = MarchingCubesType::New(); + marching->SetInput(threshold->GetOutput()); + + time.Start(); + + TRY_EXPECT_NO_EXCEPTION(marching->Update()); + + time.Stop(); + + outputMesh = marching->GetOutput(); + outputMesh->DisconnectPipeline(); #else - // Create cuberille mesh filter - std::cout << "Creating cuberille mesh..." << std::endl; - CuberilleType::Pointer cuberille = CuberilleType::New(); - cuberille->SetInput(input); - cuberille->SetIsoSurfaceValue(IsoSurfaceValue); - InterpolatorType::Pointer interpolator = InterpolatorType::New(); + // Create cuberille mesh filter + CuberilleType::Pointer cuberille = CuberilleType::New(); + + EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); + + cuberille->SetInput(input); + + cuberille->SetIsoSurfaceValue(isoSurfaceValue); + TEST_SET_GET_VALUE(isoSurfaceValue, cuberille->GetIsoSurfaceValue()); + + InterpolatorType::Pointer interpolator = InterpolatorType::New(); # if USE_BSPLINE_INTERPOLATOR - interpolator->SetSplineOrder(3); + unsigned int splineOrder = 3; + interpolator->SetSplineOrder(splineOrder); # endif - cuberille->SetInterpolator(interpolator); - cuberille->SetGenerateTriangleFaces(GenerateTriangleFaces); - cuberille->SetProjectVerticesToIsoSurface(ProjectToIsoSurface); - cuberille->SetProjectVertexSurfaceDistanceThreshold(SurfaceDistanceThreshold); - cuberille->SetProjectVertexStepLength(StepLength); - cuberille->SetProjectVertexStepLengthRelaxationFactor(StepLengthRelax); - cuberille->SetProjectVertexMaximumNumberOfSteps(MaximumNumberOfSteps); - time.Start(); - cuberille->Update(); - time.Stop(); - outputMesh = cuberille->GetOutput(); - outputMesh->DisconnectPipeline(); + cuberille->SetInterpolator(interpolator); + TEST_SET_GET_VALUE(interpolator, cuberille->GetInterpolator()); + + cuberille->SetGenerateTriangleFaces(generateTriangleFaces); + TEST_SET_GET_VALUE(generateTriangleFaces, cuberille->GetGenerateTriangleFaces()); + + cuberille->SetProjectVerticesToIsoSurface(projectToIsoSurface); + TEST_SET_GET_VALUE(projectToIsoSurface, cuberille->GetProjectVerticesToIsoSurface()); + + cuberille->SetProjectVertexSurfaceDistanceThreshold(surfaceDistanceThreshold); + TEST_SET_GET_VALUE(surfaceDistanceThreshold, cuberille->GetProjectVertexSurfaceDistanceThreshold()); + + cuberille->SetProjectVertexStepLength(stepLength); + TEST_SET_GET_VALUE(stepLength, cuberille->GetProjectVertexStepLength()); + + cuberille->SetProjectVertexStepLengthRelaxationFactor(stepLengthRelax); + TEST_SET_GET_VALUE(stepLengthRelax, cuberille->GetProjectVertexStepLengthRelaxationFactor()); + + cuberille->SetProjectVertexMaximumNumberOfSteps(maximumNumberOfSteps); + TEST_SET_GET_VALUE(maximumNumberOfSteps, cuberille->GetProjectVertexMaximumNumberOfSteps()); + + time.Start(); + + TRY_EXPECT_NO_EXCEPTION(cuberille->Update()); + + time.Stop(); + + outputMesh = cuberille->GetOutput(); + + outputMesh->DisconnectPipeline(); + #endif #if USE_DECIMATION - // Decimation - typedef itk::NumberOfFacesCriterion DecimationCriterionType; - DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); - decimateCriterion->SetTopologicalChange(false); - decimateCriterion->SetNumberOfElements(2000); - typedef itk::QuadricDecimationQuadEdgeMeshFilter DecimationType; - DecimationType::Pointer decimate = DecimationType::New(); - decimate->SetInput(outputMesh); - decimate->SetCriterion(decimateCriterion); - decimate->Update(); + // Decimation + typedef itk::NumberOfFacesCriterion DecimationCriterionType; + DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); + decimateCriterion->SetTopologicalChange(false); + decimateCriterion->SetNumberOfElements(2000); + + typedef itk::QuadricDecimationQuadEdgeMeshFilter DecimationType; + DecimationType::Pointer decimate = DecimationType::New(); + decimate->SetCriterion(decimateCriterion); + + decimate->SetInput(outputMesh); + + TRY_EXPECT_NO_EXCEPTION(decimate->Update()); #endif - // Write mesh - std::cout << "Writing output mesh: " << FilenameOutputMesh << std::endl; - MeshFileWriterType::Pointer writer = MeshFileWriterType::New(); + // Write mesh + MeshFileWriterType::Pointer writer = MeshFileWriterType::New(); #if USE_DECIMATION - writer->SetInput(decimate->GetOutput()); + writer->SetInput(decimate->GetOutput()); #else - writer->SetInput(outputMesh); + writer->SetInput(outputMesh); #endif - writer->SetFileName(FilenameOutputMesh); - writer->Update(); - - // Assert number of points/cells - std::cout << "Polygonization took " << time.GetMean() << " seconds" << std::endl; - std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices "; - std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl; - if (ExpectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != ExpectedNumberOfPoints) - { - std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfPoints << " points, but found " - << outputMesh->GetNumberOfPoints() << std::endl; - return EXIT_FAILURE; - } - if (ExpectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != ExpectedNumberOfCells) - { - std::cerr << "ERROR: Expected mesh with " << ExpectedNumberOfCells << " cells, but found " - << outputMesh->GetNumberOfCells() << std::endl; - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + writer->SetFileName(filenameOutputMesh); + + TRY_EXPECT_NO_EXCEPTION(writer->Update()); + + // Assert number of points/cells + std::cout << "Polygonization took " << time.GetMean() << " seconds" << std::endl; + std::cout << "Mesh has " << outputMesh->GetNumberOfPoints() << " vertices "; + std::cout << "and " << outputMesh->GetNumberOfCells() << " cells" << std::endl; + if (expectedNumberOfPoints > 0 && outputMesh->GetNumberOfPoints() != expectedNumberOfPoints) + { + std::cerr << "Test failed!" << std::endl; + std::cerr << "Error: Expected mesh with " << expectedNumberOfPoints << " points, but found " + << outputMesh->GetNumberOfPoints() << std::endl; + return EXIT_FAILURE; } - catch (itk::ExceptionObject & err) + if (expectedNumberOfCells > 0 && outputMesh->GetNumberOfCells() != expectedNumberOfCells) { - std::cerr << "ExceptionObject caught !" << std::endl; - std::cerr << err << std::endl; + std::cerr << "Test failed!" << std::endl; + std::cerr << "Error: Expected mesh with " << expectedNumberOfCells << " cells, but found " + << outputMesh->GetNumberOfCells() << std::endl; return EXIT_FAILURE; } + + std::cout << "Test finished" << std::endl; + return EXIT_SUCCESS; } From 5160e21e2cc693bca471fe6026da2e9694ded836 Mon Sep 17 00:00:00 2001 From: Jon Haitz Legarreta Date: Sun, 19 Feb 2017 10:58:16 +0100 Subject: [PATCH 28/84] STYLE: Improve the itk::CuberilleImageToMeshFilter style. Use initialization lists. Remove the out-of-method-boduy, Doxygen method documentation from the implementation file and put it to the method declarations in the header file. Print all member variables. Make loop counter variables have a local scope. Use brackets for one-liner control sentences. Some other cosmetics: white spaces between variables and operators, etc. --- .../include/itkCuberilleImageToMeshFilter.h | 28 +++++-- .../include/itkCuberilleImageToMeshFilter.hxx | 75 ++++++++++++------- 2 files changed, 67 insertions(+), 36 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index aa4308c7374..9b8109358b2 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -175,7 +175,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter GradientInterpolatorType; typedef typename GradientInterpolatorType::Pointer GradientInterpolatorPointer; - /** Get/set the iso-surface value. + /** Get/Set the iso-surface value. * This parameter specifies the value of the iso-surface for which to * generate the mesh. Pixels equal to or less than this value are * considered on the surface or inside the resultant mesh. @@ -188,24 +188,24 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter::max()); - /** Get/set the the initial step length for vertex projection. + /** Get/Set the the initial step length for vertex projection. Values are clamped to the range [0.0, large]. Default = max spacing * 0.25 (expressed in physical space). */ itkGetMacro(ProjectVertexStepLength, double); itkSetClampMacro(ProjectVertexStepLength, double, 0.0, 100000.0); - /** Get/set the step length relaxation factor during vertex projection. + /** Get/Set the step length relaxation factor during vertex projection. The step length is multiplied by this factor each iteration to allow convergence. Values are clamped to the range [0.0, 1.0]. Default = 0.95. */ itkGetMacro(ProjectVertexStepLengthRelaxationFactor, double); itkSetClampMacro(ProjectVertexStepLengthRelaxationFactor, double, 0.0, 1.0); - /** Get/set the maximum number of steps used during vertex projection. + /** Get/Set the maximum number of steps used during vertex projection. Default = 50. */ itkGetMacro(ProjectVertexMaximumNumberOfSteps, unsigned int); itkSetMacro(ProjectVertexMaximumNumberOfSteps, unsigned int); @@ -357,16 +357,28 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter VertexLookupMapType; /** Private functions to implement the algorithm. */ + + /** Compute gradient image. */ inline void ComputeGradientImage(); + + /** Set a flag activating each vertex for the given face. */ inline void SetVerticesFromFace(unsigned int face, bool * vertexHasQuad); + + /** Get the vertex lookup index from the given index and vertex number. */ inline IndexType GetVertexLookupIndex(unsigned int vertex, IndexType index); + + /** Project vertex to the iso-surface by stepping along normal. */ inline void ProjectVertexToIsoSurface(PointType & vertex); + + /** Add a vertex to the given mesh. Increments point identifier. */ inline void AddVertex(PointIdentifier & id, IndexType index, const InputImageType * image, OutputMeshType * mesh); + + /** Add quadrilateral face to the given mesh. Increments cell identifier. */ inline void AddQuadFace(CellIdentifier & id, PointIdentifier f[4], OutputMeshType * mesh); diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index a025cb59d8f..68b5531c4f4 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -33,15 +33,16 @@ namespace itk template CuberilleImageToMeshFilter::CuberilleImageToMeshFilter() + : m_IsoSurfaceValue(NumericTraits::One) + , m_MaxSpacing(NumericTraits::One) + , m_GenerateTriangleFaces(true) + , m_ProjectVerticesToIsoSurface(true) + , m_ProjectVertexSurfaceDistanceThreshold(0.5) + , m_ProjectVertexStepLength(-1.0) + , m_ProjectVertexStepLengthRelaxationFactor(0.95) + , m_ProjectVertexMaximumNumberOfSteps(50) { this->SetNumberOfRequiredInputs(1); - m_IsoSurfaceValue = NumericTraits::One; - m_GenerateTriangleFaces = true; - m_ProjectVerticesToIsoSurface = true; - m_ProjectVertexSurfaceDistanceThreshold = 0.5; - m_ProjectVertexStepLength = -1.0; - m_ProjectVertexStepLengthRelaxationFactor = 0.95; - m_ProjectVertexMaximumNumberOfSteps = 50; } template @@ -57,7 +58,6 @@ CuberilleImageToMeshFilter::SetInput(co this->ProcessObject::SetNthInput(0, const_cast(image)); } -/** Generate data */ template void CuberilleImageToMeshFilter::GenerateData() @@ -109,7 +109,7 @@ CuberilleImageToMeshFilter::GenerateDat // mesh->GetCells()->Reserve( (size[0]*size[1]*size[2]) / 100 ); // Set up helper structures - unsigned int i, look, look0, look1 = 0; + unsigned int look, look0, look1 = 0; unsigned char numFaces = 0; bool faceHasQuad[6]; bool vertexHasQuad[8]; @@ -153,15 +153,17 @@ CuberilleImageToMeshFilter::GenerateDat // Determine if current pixel is suitable center = it.GetCenterPixel(); if (center < m_IsoSurfaceValue) + { continue; + } // Re-initialize for new pixel numFaces = 0; - for (i = 0; i < 6; ++i) + for (unsigned int i = 0; i < 6; ++i) { faceHasQuad[i] = false; } - for (i = 0; i < 8; ++i) + for (unsigned int i = 0; i < 8; ++i) { vertexHasQuad[i] = false; } @@ -176,7 +178,7 @@ CuberilleImageToMeshFilter::GenerateDat } // Compute which faces (if any) have quads - for (i = 0; i < 6; ++i) + for (unsigned int i = 0; i < 6; ++i) { // NOTE: suitability check above means center <= m_IsoSurfaceValue faceHasQuad[i] = (it.GetPixel(offset[i]) < m_IsoSurfaceValue); @@ -191,7 +193,7 @@ CuberilleImageToMeshFilter::GenerateDat if (numFaces > 0) { // Create vertices - for (i = 0; i < 8; ++i) + for (unsigned int i = 0; i < 8; ++i) { if (vertexHasQuad[i]) { @@ -270,7 +272,6 @@ CuberilleImageToMeshFilter::GenerateDat #endif } -/** Set a flag activating each vertex for the given face. */ template void CuberilleImageToMeshFilter::SetVerticesFromFace(unsigned int face, bool * v) @@ -316,7 +317,6 @@ CuberilleImageToMeshFilter::SetVertices } } -/** Get the vertex lookup index from the given index and vertex number. */ template typename TInputImage::IndexType CuberilleImageToMeshFilter::GetVertexLookupIndex( @@ -358,7 +358,6 @@ CuberilleImageToMeshFilter::GetVertexLo return result; } -/** Add a vertex to the given mesh. Increments point identifier. */ template void CuberilleImageToMeshFilter::AddVertex( @@ -380,7 +379,6 @@ CuberilleImageToMeshFilter::AddVertex( mesh->GetPoints()->InsertElement(id++, vertex); } -/** Add quadrilateral face to the given mesh. Increments cell identifier. */ template void CuberilleImageToMeshFilter::AddQuadFace( @@ -444,7 +442,6 @@ CuberilleImageToMeshFilter::AddQuadFace } } -/** Project vertex to the iso-surface by stepping along normal. */ template void CuberilleImageToMeshFilter::ProjectVertexToIsoSurface(PointType & vertex) @@ -486,7 +483,9 @@ CuberilleImageToMeshFilter::ProjectVert diff[1] = vnl_math_abs(value[1] - m_IsoSurfaceValue); i = (diff[0] <= diff[1]) ? 0 : 1; if (previousi < 0) + { previousi = i; + } swaps += (int)(previousi != i); vertex = temp[i]; @@ -494,33 +493,45 @@ CuberilleImageToMeshFilter::ProjectVert done |= diff[i] < m_ProjectVertexSurfaceDistanceThreshold; # if DEBUG_PRINT if (done) + { m_ProjectVertexTerminate[0]++; + } # endif if (done) + { break; + } // Determine whether we have done enough steps done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; # if DEBUG_PRINT if (done) + { m_ProjectVertexTerminate[1]++; + } # endif if (done) + { break; + } // Determine whether there has been too many sign swaps (oscillating) done |= (swaps >= 5); # if DEBUG_PRINT if (done) + { m_ProjectVertexTerminate[2]++; + } # endif if (done) + { break; + } } #elif USE_LINESEARCH_PROJECTION // Set up - unsigned int i, j, k; + unsigned int k; GradientPixelType normal; InterpolatorOutputType value; PointType temp, bestVertex; @@ -537,11 +548,11 @@ CuberilleImageToMeshFilter::ProjectVert for (sign = -1.0; sign <= 1.0; sign += 2.0) { k = (sign == -1.0) ? 0 : 1; - for (j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; ++j) + for (unsigned int j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; ++j) { // Compute current location along line d = (double)j / ((double)m_ProjectVertexMaximumNumberOfSteps / 2.0); - for (i = 0; i < InputImageType::ImageDimension; ++i) + for (unsigned int i = 0; i < InputImageType::ImageDimension; ++i) { temp[i] = vertex[i] + (normal[i] * sign * m_ProjectVertexStepLength * d); } @@ -566,7 +577,7 @@ CuberilleImageToMeshFilter::ProjectVert bool done = false; double sign = 1.0; double step = m_ProjectVertexStepLength; - unsigned int i, numberOfSteps = 0; + unsigned int numberOfSteps = 0; GradientPixelType normal; InterpolatorOutputType value; @@ -584,14 +595,18 @@ CuberilleImageToMeshFilter::ProjectVert done |= vnl_math_abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; # if DEBUG_PRINT if (done) + { m_ProjectVertexTerminate[0]++; + } # endif if (done) + { break; + } // Step along the normal towards the iso-surface value sign = (value < m_IsoSurfaceValue) ? +1.0 : -1.0; - for (i = 0; i < InputImageType::ImageDimension; ++i) + for (unsigned int i = 0; i < InputImageType::ImageDimension; ++i) { vertex[i] += (normal[i] * sign * step); } @@ -599,13 +614,14 @@ CuberilleImageToMeshFilter::ProjectVert done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; # if DEBUG_PRINT if (done) + { m_ProjectVertexTerminate[1]++; + } # endif } #endif } -/** Compute gradient image. */ template void CuberilleImageToMeshFilter::ComputeGradientImage() @@ -626,7 +642,6 @@ CuberilleImageToMeshFilter::ComputeGrad } } -/** PrintSelf. */ template void CuberilleImageToMeshFilter::PrintSelf(std::ostream & os, Indent indent) const @@ -636,13 +651,17 @@ CuberilleImageToMeshFilter::PrintSelf(s os << indent << "IsoSurfaceValue: " << static_cast::PrintType>(m_IsoSurfaceValue) << std::endl; + os << indent << "MaxSpacing: " << static_cast::PrintType>(m_MaxSpacing) + << std::endl; os << indent << "GenerateTriangleFaces: " << static_cast::PrintType>(m_GenerateTriangleFaces) << std::endl; os << indent << "ProjectVerticesToIsoSurface: " << static_cast::PrintType>(m_ProjectVerticesToIsoSurface) << std::endl; + os << indent << "ProjectVertexSurfaceDistanceThreshold: " << m_ProjectVertexSurfaceDistanceThreshold << std::endl; + os << indent << "ProjectVertexStepLength: " << m_ProjectVertexStepLength << std::endl; + os << indent << "ProjectVertexStepLengthRelaxationFactor: " << m_ProjectVertexStepLengthRelaxationFactor << std::endl; + os << indent << "ProjectVertexMaximumNumberOfSteps: " << m_ProjectVertexMaximumNumberOfSteps << std::endl; } - -} // namespace itk - +} // end namespace itk #endif From 5ab2665f4e15b507a4d1c721b5a7c97e68b4f5dd Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Tue, 17 Oct 2017 17:57:06 -0400 Subject: [PATCH 29/84] COMP: Fix mesh traits wrapping types To address: itkCuberilleImageToMeshFilter: warning(4): ITK type not wrapped, or currently not known: itk::Mesh< double, 2, itk::DefaultStaticMeshTraits< double, 2, 2, float, float, double > > itkCuberilleImageToMeshFilter: warning(4): ITK type not wrapped, or currently not known: itk::Mesh< double, 3, itk::DefaultStaticMeshTraits< double, 3, 3, float, float, double > > --- .../Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap index 23835531783..d974dc447fd 100644 --- a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap +++ b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap @@ -2,8 +2,8 @@ itk_wrap_class("itk::CuberilleImageToMeshFilter" POINTER_WITH_2_SUPERCLASSES) unique(types "${WRAP_ITK_REAL};D") foreach(d ${ITK_WRAP_IMAGE_DIMS}) foreach(t ${types}) - itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}DSM${ITKM_${t}}${d}${d}${ITKM_F}${ITKM_F}" - "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d},itk::DefaultStaticMeshTraits<${ITKT_${t}},${d},${d},${ITKT_F},${ITKT_F} > >" + itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}DSM${ITKM_${t}}${d}${d}${ITKM_${t}}${ITKM_${t}}" + "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d},itk::DefaultStaticMeshTraits<${ITKT_${t}},${d},${d},${ITKT_${t}},${ITKT_${t}} > >" ) endforeach() endforeach() From fb6249bf763f595b97b4278b648e5799e26ca185 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sun, 17 Dec 2017 14:29:15 -0600 Subject: [PATCH 30/84] COMP: Deprecate use itkGetObjectMacro git grep -l "itkGetObjectMacro" | fgrep -v itk_compiler_detection.h | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/itkGetObjectMacro/itkGetConstObjectMacro/g" For a full description of the rational for this change see: commit 08df6afeb32e363104bf1e7a043f4b38d524c364 Author: Hans Johnson Date: Sun Dec 2 14:07:01 2012 -0600 ENH: Get function accessible from const objects --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 9b8109358b2..a970ab0c160 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -189,7 +189,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sun, 17 Dec 2017 14:40:20 -0600 Subject: [PATCH 31/84] COMP: Remove compiler warning regarding override Functions need to be labeled as ITK_OVERRIDE to suppress compiler warnings. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index a970ab0c160..53bd04ea5a1 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -236,12 +236,12 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Tue, 16 Jan 2018 16:44:18 -0800 Subject: [PATCH 32/84] COMP: Prefer std and itk::Math to vnl_math --- .../include/itkCuberilleImageToMeshFilter.hxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 9371575a6b2..701ddad9700 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -24,6 +24,7 @@ x = y; \ y = t; +#include "itkMath.h" #include "itkCuberilleImageToMeshFilter.h" #include "itkNumericTraits.h" #include "itkConnectedComponentAlgorithm.h" @@ -76,7 +77,7 @@ CuberilleImageToMeshFilter::GenerateDat m_MaxSpacing = image->GetSpacing()[0]; for (int i = 1; i < InputImageType::ImageDimension; i++) { - m_MaxSpacing = vnl_math_max(m_MaxSpacing, image->GetSpacing()[i]); + m_MaxSpacing = std::max(m_MaxSpacing, image->GetSpacing()[i]); } // Set default step length @@ -479,8 +480,8 @@ CuberilleImageToMeshFilter::ProjectVert // Compute which direction moves vertex closer to iso-surface value value[0] = m_Interpolator->Evaluate(temp[0]); value[1] = m_Interpolator->Evaluate(temp[1]); - diff[0] = vnl_math_abs(value[0] - m_IsoSurfaceValue); - diff[1] = vnl_math_abs(value[1] - m_IsoSurfaceValue); + diff[0] = itk::Math::abs(value[0] - m_IsoSurfaceValue); + diff[1] = itk::Math::abs(value[1] - m_IsoSurfaceValue); i = (diff[0] <= diff[1]) ? 0 : 1; if (previousi < 0) previousi = i; @@ -541,7 +542,7 @@ CuberilleImageToMeshFilter::ProjectVert } // Compute metric (combination of difference and distance) value = m_Interpolator->Evaluate(temp); - metric = vnl_math_abs(value - m_IsoSurfaceValue); // Difference + metric = itk::Math::abs(value - m_IsoSurfaceValue); // Difference // metric /= NumericTraits::max(); // Normalized difference // metric /= d; // Distance @@ -572,7 +573,7 @@ CuberilleImageToMeshFilter::ProjectVert // Compute whether vertex is close enough to iso-surface value value = m_Interpolator->Evaluate(vertex); - done |= vnl_math_abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; + done |= itk::Math::abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; # if DEBUG_PRINT if (done) m_ProjectVertexTerminate[0]++; From a310313ae7406f3cb4df2dacd396c0342a59e67c Mon Sep 17 00:00:00 2001 From: Jon Haitz Legarreta Date: Wed, 17 Jan 2018 17:37:08 +0100 Subject: [PATCH 33/84] ENH: Require cmake minimum version to be 3.9.5. Require CMake minimum version to be 3.9.5 following ITKv5 requiring C++11: https://discourse.itk.org/t/minimum-cmake-version-update/585 --- Modules/Filtering/Cuberille/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt index ffd30dc4959..9948184c41c 100644 --- a/Modules/Filtering/Cuberille/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.9) +cmake_minimum_required(VERSION 3.9.5) project(Cuberille) if(NOT ITK_SOURCE_DIR) From 486055df1071ffe3867788dca12eda228ea2d2e9 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sat, 16 Dec 2017 14:49:53 -0600 Subject: [PATCH 34/84] ENH: Initial ITKv5 conversions. Provide initial conversion of features to ITKv5. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 53bd04ea5a1..812237dff28 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -234,14 +234,14 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 16 Dec 2017 19:16:55 -0600 Subject: [PATCH 35/84] ENH: ITKv5 override consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide remove virtual and override Use clang-tidy to add ITK_OVERRIDE, and to remove redundant virtual on functions. cd ../ITK; clang-tidy -p ITK-clangtidy $find Modules/[A-J]* -name *.cxx |fgrep -v ThirdParty) -checks=-*,modernize-use-override -header-filter=.* -fix clang-tidy -p ITK-clangtidy $(find Modules/[K-Z]* -name *.cxx |fgrep -v ThirdParty) -checks=-*,modernize-use-override -header-filter=.* -fix https://stackoverflow.com/questions/39932391/virtual-override-or-both-c When you override a function you don't technically need to write either virtual or override. The original base class declaration needs the keyword virtual to mark it as virtual. In the derived class the function is virtual by way of having the ¹same type as the base class function. However, an override can help avoid bugs by producing a compilation error when the intended override isn't technically an override. E.g. that the function type isn't exactly like the base class function. Or that a maintenance of the base class changes that function's type, e.g. adding a defaulted argument. In the same way, a virtual keyword in the derived class can make such a bug more subtle, by ensuring that the function is still is virtual in further derived classes. So the general advice is, virtual for the base class function declaration. This is technically necessary. Use override (only) for a derived class' override. This helps with maintenance. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 812237dff28..b9d8b433369 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -234,14 +234,14 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 16 Dec 2017 19:36:24 -0600 Subject: [PATCH 36/84] COMP: Use C++11 override directly git grep -l "ITK_OVERRIDE" | fgrep -v itk_compiler_detection.h | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/ITK_OVERRIDE/override/g" --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index b9d8b433369..812237dff28 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -234,14 +234,14 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 16 Dec 2017 20:10:44 -0600 Subject: [PATCH 37/84] COMP: Use C++11 nullptr directly git grep -l "ITK_NULLPTR" | fgrep -v itk_compiler_detection.h | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/ITK_NULLPTR/nullptr/g" --- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 4 ++-- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index e28bb88fcbe..2b37c193b59 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -49,7 +49,7 @@ CuberilleImageToMeshFilter::CuberilleIm template CuberilleImageToMeshFilter::~CuberilleImageToMeshFilter() { - m_GradientInterpolator = ITK_NULLPTR; + m_GradientInterpolator = nullptr; } template @@ -639,7 +639,7 @@ CuberilleImageToMeshFilter::ComputeGrad m_GradientInterpolator = GradientInterpolatorType::New(); m_GradientInterpolator->SetInputImage(gradientFilter->GetOutput()); gradientFilter->GetOutput()->DisconnectPipeline(); - gradientFilter = ITK_NULLPTR; + gradientFilter = nullptr; } } diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 0f6dcf4a7e8..6ac5527e5a1 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -125,7 +125,7 @@ CuberilleTest01(int argc, char * argv[]) input->DisconnectPipeline(); // Create output mesh - MeshType::Pointer outputMesh = ITK_NULLPTR; + MeshType::Pointer outputMesh = nullptr; itk::TimeProbe time; #if USE_MARCHING_CUBES From 50a43700056b1b3ccffa3422461e6db6a6fd22ac Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sat, 16 Dec 2017 14:49:53 -0600 Subject: [PATCH 38/84] ENH: Initial ITKv5 conversions. Provide initial conversion of features to ITKv5. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 53bd04ea5a1..812237dff28 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -234,14 +234,14 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 16 Dec 2017 19:16:55 -0600 Subject: [PATCH 39/84] ENH: ITKv5 override consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide remove virtual and override Use clang-tidy to add ITK_OVERRIDE, and to remove redundant virtual on functions. cd ../ITK; clang-tidy -p ITK-clangtidy $find Modules/[A-J]* -name *.cxx |fgrep -v ThirdParty) -checks=-*,modernize-use-override -header-filter=.* -fix clang-tidy -p ITK-clangtidy $(find Modules/[K-Z]* -name *.cxx |fgrep -v ThirdParty) -checks=-*,modernize-use-override -header-filter=.* -fix https://stackoverflow.com/questions/39932391/virtual-override-or-both-c When you override a function you don't technically need to write either virtual or override. The original base class declaration needs the keyword virtual to mark it as virtual. In the derived class the function is virtual by way of having the ¹same type as the base class function. However, an override can help avoid bugs by producing a compilation error when the intended override isn't technically an override. E.g. that the function type isn't exactly like the base class function. Or that a maintenance of the base class changes that function's type, e.g. adding a defaulted argument. In the same way, a virtual keyword in the derived class can make such a bug more subtle, by ensuring that the function is still is virtual in further derived classes. So the general advice is, virtual for the base class function declaration. This is technically necessary. Use override (only) for a derived class' override. This helps with maintenance. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 812237dff28..b9d8b433369 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -234,14 +234,14 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 16 Dec 2017 19:36:24 -0600 Subject: [PATCH 40/84] COMP: Use C++11 override directly git grep -l "ITK_OVERRIDE" | fgrep -v itk_compiler_detection.h | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/ITK_OVERRIDE/override/g" --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index b9d8b433369..812237dff28 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -234,14 +234,14 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 16 Dec 2017 20:10:44 -0600 Subject: [PATCH 41/84] COMP: Use C++11 nullptr directly git grep -l "ITK_NULLPTR" | fgrep -v itk_compiler_detection.h | fgrep -v CMakeLists.txt |fgrep -v .cmake | xargs sed -i '' -e "s/ITK_NULLPTR/nullptr/g" --- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 4 ++-- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index e28bb88fcbe..2b37c193b59 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -49,7 +49,7 @@ CuberilleImageToMeshFilter::CuberilleIm template CuberilleImageToMeshFilter::~CuberilleImageToMeshFilter() { - m_GradientInterpolator = ITK_NULLPTR; + m_GradientInterpolator = nullptr; } template @@ -639,7 +639,7 @@ CuberilleImageToMeshFilter::ComputeGrad m_GradientInterpolator = GradientInterpolatorType::New(); m_GradientInterpolator->SetInputImage(gradientFilter->GetOutput()); gradientFilter->GetOutput()->DisconnectPipeline(); - gradientFilter = ITK_NULLPTR; + gradientFilter = nullptr; } } diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 0f6dcf4a7e8..6ac5527e5a1 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -125,7 +125,7 @@ CuberilleTest01(int argc, char * argv[]) input->DisconnectPipeline(); // Create output mesh - MeshType::Pointer outputMesh = ITK_NULLPTR; + MeshType::Pointer outputMesh = nullptr; itk::TimeProbe time; #if USE_MARCHING_CUBES From 7fd6ea43cf0ebaf5470ad403697f1dd8cef0b768 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 12 Feb 2018 15:20:16 -0600 Subject: [PATCH 42/84] STYLE: Modernize to C++11 conventions STYLE: Use auto for variable creation This check is responsible for using the auto type specifier for variable declarations to improve code readability and maintainability. The auto type specifier will only be introduced in situations where the variable type matches the type of the initializer expression. In other words auto should deduce the same type that was originally spelled in the source cd /Users/johnsonhj/Dashboard/src/ITK-clangtidy/ run-clang-tidy.py -checks=-*,modernize-use-auto -header-filter=.* -fix use auto when declaring iterators use auto when initializing with a cast to avoid duplicating the type name use auto when initializing with a template cast to avoid duplicating the type name use auto when initializing with new to avoid duplicating the type name SRCDIR=/Users/johnsonhj/Dashboard/src/ITK #My local SRC BLDDIR=/Users/johnsonhj/Dashboard/src/ITK-clangtidy/ #My local BLD PERF: Replace explicit return calls of constructor Replaces explicit calls to the constructor in a return with a braced initializer list. This way the return type is not needlessly duplicated in the function definition and the return statement. SRCDIR=/Users/johnsonhj/Dashboard/src/ITK #My local SRC BLDDIR=/Users/johnsonhj/Dashboard/src/ITK-clangtidy/ #My local BLD cd /Users/johnsonhj/Dashboard/src/ITK-clangtidy/ run-clang-tidy.py -checks=-*,modernize-return-braced-init-list -header-filter=.* -fix PERF: Allow compiler to choose best way to construct a copy With move semantics added to the language and the standard library updated with move constructors added for many types it is now interesting to take an argument directly by value, instead of by const-reference, and then copy. This check allows the compiler to take care of choosing the best way to construct the copy. The transformation is usually beneficial when the calling code passes an rvalue and assumes the move construction is a cheap operation. This short example illustrates how the construction of the value happens: class Foo { public: - Foo(const std::string &Copied, const std::string &ReadOnly) - : Copied(Copied), ReadOnly(ReadOnly) {} + Foo(std::string Moved, const std::string &ReadOnly) + : Copied(std::move(Moved)), ReadOnly(ReadOnly) {} private: private: std::string Copied; const std::string &ReadOnly; }; SRCDIR=/Users/johnsonhj/Dashboard/src/ITK #My local SRC BLDDIR=/Users/johnsonhj/Dashboard/src/ITK-clangtidy/ #My local BLD cd /Users/johnsonhj/Dashboard/src/ITK-clangtidy/ run-clang-tidy.py -checks=-*,modernize-pass-by-value -header-filter=.* -fix STYLE: Use range-based loops from C++11 Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container, in the forward direction. ==== Range based loopes are more explicit for only computing the end location once for containers. for ( ImageIORegion::IndexType::const_iterator i = this->GetIndex().begin(); i != this->GetIndex().end(); //<- NOTE: Compute end every loop iteration ++i ) for (long i : this->GetIndex()) //<- NOTE: Implicitly only compute end once ==== Explicitly reduce the amount of index computations: (The compiler probably does this too) for(int i = 0; i < 11; i++) { pos[0] = testPoints[i][0]; pos[1] = testPoints[i][1]; ^^^^ for(auto & testPoint : testPoints) { pos[0] = testPoint[0]; pos[1] = testPoint[1]; ==== SRCDIR=/Users/johnsonhj/Dashboard/src/ITK #My local SRC BLDDIR=/Users/johnsonhj/Dashboard/src/ITK-clangtidy/ #My local BLD cd /Users/johnsonhj/Dashboard/src/ITK-clangtidy/ run-clang-tidy.py -checks=-*,modernize-loop-convert -header-filter=.* -fix --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 6 +++--- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 812237dff28..868d9875ec2 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -338,9 +338,9 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter::GenerateDat // Re-initialize for new pixel numFaces = 0; - for (unsigned int i = 0; i < 6; ++i) + for (bool & i : faceHasQuad) { - faceHasQuad[i] = false; + i = false; } - for (unsigned int i = 0; i < 8; ++i) + for (bool & i : vertexHasQuad) { - vertexHasQuad[i] = false; + i = false; } // Re-initialize for new z plane From 69b523f640761a5d5af64fd0db05f116aa3cfae0 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 12 Feb 2018 23:29:01 -0600 Subject: [PATCH 43/84] STYLE: Prefer C++11 type alias over typedef == http://en.cppreference.com/w/cpp/language/type_alias == Type alias is a name that refers to a previously defined type (similar to typedef). A type alias declaration introduces a name which can be used as a synonym for the type denoted by type-id. It does not introduce a new type and it cannot change the meaning of an existing type name. There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope. == https://www.quora.com/Is-using-typedef-in-C++-considered-a-bad-practice == While typedef is still available for backward compatibility, the new Type Alias syntax 'using Alias = ExistingLongName;' is more consistent with the flow of C++ than the old typedef syntax 'typedef ExistingLongName Alias;', and it also works for templates (Type alias, alias template (since C++11)), so leftover 'typedef' aliases will differ in style from any alias templates. --- .../include/itkCuberilleImageToMeshFilter.h | 116 +++++++++--------- .../Cuberille/test/CuberilleTest01.cxx | 28 ++--- 2 files changed, 72 insertions(+), 72 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 868d9875ec2..c40600ff351 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -114,11 +114,11 @@ template { public: - /** Standard "Self" typedef. */ - typedef CuberilleImageToMeshFilter Self; - typedef ImageToMeshFilter Superclass; - typedef SmartPointer Pointer; - typedef SmartPointer ConstPointer; + /** Standard "Self" type alias. */ + using Self = CuberilleImageToMeshFilter; + using Superclass = ImageToMeshFilter; + using Pointer = SmartPointer; + using ConstPointer = SmartPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); @@ -126,54 +126,54 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter CellInterfaceType; - typedef TriangleCell TriangleCellType; - typedef typename TriangleCellType::SelfAutoPointer TriangleAutoPointer; - typedef typename TriangleCellType::CellAutoPointer TriangleCellAutoPointer; - typedef QuadrilateralCell QuadrilateralCellType; - typedef typename QuadrilateralCellType::SelfAutoPointer QuadrilateralAutoPointer; - typedef typename QuadrilateralCellType::CellAutoPointer QuadrilateralCellAutoPointer; - - typedef TInputImage InputImageType; - typedef typename InputImageType::Pointer InputImagePointer; - typedef typename InputImageType::ConstPointer InputImageConstPointer; - typedef typename InputImageType::PixelType InputPixelType; - typedef typename InputImageType::SizeType SizeType; - typedef typename InputImageType::SpacingType SpacingType; - typedef typename InputImageType::SpacingValueType SpacingValueType; - typedef typename InputImageType::IndexType IndexType; - typedef typename OutputMeshType::PointType PointType; - - typedef TInterpolator InterpolatorType; - typedef typename InterpolatorType::Pointer InterpolatorPointer; - typedef typename InterpolatorType::OutputType InterpolatorOutputType; - - /** Other convenient typedefs. */ - typedef ConstShapedNeighborhoodIterator InputImageIteratorType; + /** Some convenient type alias. */ + using OutputMeshType = TOutputMesh; + using OutputMeshPointer = typename OutputMeshType::Pointer; + using OutputMeshTraits = typename OutputMeshType::MeshTraits; + using OutputPointType = typename OutputMeshType::PointType; + using OutputPixelType = typename OutputMeshTraits::PixelType; + 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 PointIdentifier = typename OutputMeshType::PointIdentifier; + using CellIdentifier = typename OutputMeshType::CellIdentifier; + using CellInterfaceType = CellInterface; + using TriangleCellType = TriangleCell; + using TriangleAutoPointer = typename TriangleCellType::SelfAutoPointer; + using TriangleCellAutoPointer = typename TriangleCellType::CellAutoPointer; + using QuadrilateralCellType = QuadrilateralCell; + using QuadrilateralAutoPointer = typename QuadrilateralCellType::SelfAutoPointer; + using QuadrilateralCellAutoPointer = typename QuadrilateralCellType::CellAutoPointer; + + using InputImageType = TInputImage; + using InputImagePointer = typename InputImageType::Pointer; + using InputImageConstPointer = typename InputImageType::ConstPointer; + using InputPixelType = typename InputImageType::PixelType; + using SizeType = typename InputImageType::SizeType; + using SpacingType = typename InputImageType::SpacingType; + using SpacingValueType = typename InputImageType::SpacingValueType; + using IndexType = typename InputImageType::IndexType; + using PointType = typename OutputMeshType::PointType; + + using InterpolatorType = TInterpolator; + using InterpolatorPointer = typename InterpolatorType::Pointer; + using InterpolatorOutputType = typename InterpolatorType::OutputType; + + /** Other convenient type alias. */ + using InputImageIteratorType = ConstShapedNeighborhoodIterator; #if USE_GRADIENT_RECURSIVE_GAUSSIAN - typedef GradientRecursiveGaussianImageFilter GradientFilterType; + using GradientFilterType = GradientRecursiveGaussianImageFilter; #else - typedef GradientImageFilter GradientFilterType; + using GradientFilterType = GradientImageFilter; #endif - typedef typename GradientFilterType::Pointer GradientFilterPointer; - typedef typename GradientFilterType::OutputImageType GradientImageType; - typedef typename GradientImageType::Pointer GradientImagePointer; - typedef typename GradientFilterType::OutputPixelType GradientPixelType; - typedef itk::VectorLinearInterpolateImageFunction GradientInterpolatorType; - typedef typename GradientInterpolatorType::Pointer GradientInterpolatorPointer; + using GradientFilterPointer = typename GradientFilterType::Pointer; + using GradientImageType = typename GradientFilterType::OutputImageType; + using GradientImagePointer = typename GradientImageType::Pointer; + using GradientPixelType = typename GradientFilterType::OutputPixelType; + using GradientInterpolatorType = itk::VectorLinearInterpolateImageFunction; + using GradientInterpolatorPointer = typename GradientInterpolatorType::Pointer; /** Get/Set the iso-surface value. * This parameter specifies the value of the iso-surface for which to @@ -251,8 +251,8 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter MapType; + /** Convenient type alias */ + using Self = VertexLookupMap; + using PointIdentifier = typename TMeshType::PointIdentifier; + using MapType = std::map; /** Constructors */ VertexLookupMap() {} @@ -353,8 +353,8 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter VertexLookupMapType; + /** Some convenient type alias. */ + using VertexLookupMapType = VertexLookupMap; /** Private functions to implement the algorithm. */ diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 6ac5527e5a1..06ec2c8058b 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -51,24 +51,24 @@ CuberilleTest01(int argc, char * argv[]) } // Typedefs - const unsigned int Dimension = 3; - typedef unsigned char PixelType; - // typedef signed short PixelType; - typedef itk::Image ImageType; + const unsigned int Dimension = 3; + using PixelType = unsigned char; + // using PixelType = signed short; + using ImageType = itk::Image; #if USE_QUAD_EDGE_MESH | USE_DECIMATION - typedef itk::QuadEdgeMesh MeshType; + using MeshType = itk::QuadEdgeMesh; #else - typedef itk::Mesh MeshType; + using MeshType = itk::Mesh; #endif - typedef itk::ImageFileReader ImageFileReaderType; - typedef itk::VTKPolyDataWriter MeshFileWriterType; + using ImageFileReaderType = itk::ImageFileReader; + using MeshFileWriterType = itk::VTKPolyDataWriter; #if USE_BSPLINE_INTERPOLATOR - typedef itk::BSplineInterpolateImageFunction InterpolatorType; + using InterpolatorType = itk::BSplineInterpolateImageFunction; #else - typedef itk::LinearInterpolateImageFunction InterpolatorType; + using InterpolatorType = itk::LinearInterpolateImageFunction; #endif - typedef itk::CuberilleImageToMeshFilter CuberilleType; + using CuberilleType = itk::CuberilleImageToMeshFilter; // Read command-line parameters @@ -201,12 +201,12 @@ CuberilleTest01(int argc, char * argv[]) #if USE_DECIMATION // Decimation - typedef itk::NumberOfFacesCriterion DecimationCriterionType; - DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); + using DecimationCriterionType = itk::NumberOfFacesCriterion; + DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); decimateCriterion->SetTopologicalChange(false); decimateCriterion->SetNumberOfElements(2000); - typedef itk::QuadricDecimationQuadEdgeMeshFilter DecimationType; + using DecimationType = itk::QuadricDecimationQuadEdgeMeshFilter; DecimationType::Pointer decimate = DecimationType::New(); decimate->SetCriterion(decimateCriterion); From 8de168fe15d174b2574b4997dde8a6de561d41ed Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Tue, 13 Feb 2018 08:23:55 -0600 Subject: [PATCH 44/84] STYLE: Prefer constexpr for const numeric literals Use constexpr for constant numeric literals. --- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 06ec2c8058b..081d2753e6a 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -51,7 +51,7 @@ CuberilleTest01(int argc, char * argv[]) } // Typedefs - const unsigned int Dimension = 3; + constexpr unsigned int Dimension = 3; using PixelType = unsigned char; // using PixelType = signed short; using ImageType = itk::Image; From 6e7849c69ec6adde112659d9818fa0ae0e6a2b1e Mon Sep 17 00:00:00 2001 From: Jon Haitz Legarreta Date: Sat, 14 Apr 2018 19:23:16 +0200 Subject: [PATCH 45/84] COMP: Move ITK_DISALLOW_COPY_AND_ASSIGN calls to public section. Move `ITK_DISALLOW_COPY_AND_ASSIGN` calls to public section following the discussion in https://discourse.itk.org/t/noncopyable If legacy (pre-macro) copy and assing methods existed, subsitute them for the `ITK_DISALLOW_COPY_AND_ASSIGN` macro. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index c40600ff351..653633ec8a6 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -114,6 +114,8 @@ template { public: + ITK_DISALLOW_COPY_AND_ASSIGN(CuberilleImageToMeshFilter); + /** Standard "Self" type alias. */ using Self = CuberilleImageToMeshFilter; using Superclass = ImageToMeshFilter; @@ -244,8 +246,6 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Sat, 5 May 2018 12:18:49 +0200 Subject: [PATCH 46/84] COMP: Set the minimum required CMake version to 3.10.2. As agreed in: https://discourse.itk.org/t/cmake-update/870/ Set the `cmake_minimum_required` to version **3.10.2**. --- Modules/Filtering/Cuberille/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt index 9948184c41c..ae07bec173d 100644 --- a/Modules/Filtering/Cuberille/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9.5) +cmake_minimum_required(VERSION 3.10.2) project(Cuberille) if(NOT ITK_SOURCE_DIR) From 715121cf090d5873de729f8714873db8abdf2f69 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Sun, 17 Feb 2019 23:27:53 -0500 Subject: [PATCH 47/84] COMP: Wrap simplified Mesh types Available in ITK 5.0 RC 1. --- .../Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap index d974dc447fd..7be7018e63a 100644 --- a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap +++ b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap @@ -2,8 +2,8 @@ itk_wrap_class("itk::CuberilleImageToMeshFilter" POINTER_WITH_2_SUPERCLASSES) unique(types "${WRAP_ITK_REAL};D") foreach(d ${ITK_WRAP_IMAGE_DIMS}) foreach(t ${types}) - itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}DSM${ITKM_${t}}${d}${d}${ITKM_${t}}${ITKM_${t}}" - "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d},itk::DefaultStaticMeshTraits<${ITKT_${t}},${d},${d},${ITKT_${t}},${ITKT_${t}} > >" + itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}" + "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d} >" ) endforeach() endforeach() From 126ba778b7122771d2625679e3d05e21a62d798d Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 6 Nov 2019 14:03:44 -0500 Subject: [PATCH 48/84] ENH: Wrap for all scalar pixel types --- .../Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap index 7be7018e63a..eb6ec8ae2de 100644 --- a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap +++ b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap @@ -1,5 +1,5 @@ itk_wrap_class("itk::CuberilleImageToMeshFilter" POINTER_WITH_2_SUPERCLASSES) -unique(types "${WRAP_ITK_REAL};D") +unique(types "${WRAP_ITK_SCALAR}") foreach(d ${ITK_WRAP_IMAGE_DIMS}) foreach(t ${types}) itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}" From 5de242b0def2fd9032fb21a3909dc80f1cfb3716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20Haitz=20Legarreta=20Gorro=C3=B1o?= Date: Fri, 14 Jun 2019 21:30:14 -0400 Subject: [PATCH 49/84] STYLE: Add ITK prefix to testing macros --- .../Cuberille/test/CuberilleTest01.cxx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 081d2753e6a..d549e8a5a82 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -119,7 +119,7 @@ CuberilleTest01(int argc, char * argv[]) ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); reader->SetFileName(filenameInputImage); - TRY_EXPECT_NO_EXCEPTION(reader->UpdateLargestPossibleRegion()); + ITK_TRY_EXPECT_NO_EXCEPTION(reader->UpdateLargestPossibleRegion()); ImageType::Pointer input = reader->GetOutput(); input->DisconnectPipeline(); @@ -143,7 +143,7 @@ CuberilleTest01(int argc, char * argv[]) time.Start(); - TRY_EXPECT_NO_EXCEPTION(marching->Update()); + ITK_TRY_EXPECT_NO_EXCEPTION(marching->Update()); time.Stop(); @@ -154,12 +154,12 @@ CuberilleTest01(int argc, char * argv[]) // Create cuberille mesh filter CuberilleType::Pointer cuberille = CuberilleType::New(); - EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); + ITK_EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); cuberille->SetInput(input); cuberille->SetIsoSurfaceValue(isoSurfaceValue); - TEST_SET_GET_VALUE(isoSurfaceValue, cuberille->GetIsoSurfaceValue()); + ITK_TEST_SET_GET_VALUE(isoSurfaceValue, cuberille->GetIsoSurfaceValue()); InterpolatorType::Pointer interpolator = InterpolatorType::New(); # if USE_BSPLINE_INTERPOLATOR @@ -167,29 +167,29 @@ CuberilleTest01(int argc, char * argv[]) interpolator->SetSplineOrder(splineOrder); # endif cuberille->SetInterpolator(interpolator); - TEST_SET_GET_VALUE(interpolator, cuberille->GetInterpolator()); + ITK_TEST_SET_GET_VALUE(interpolator, cuberille->GetInterpolator()); cuberille->SetGenerateTriangleFaces(generateTriangleFaces); - TEST_SET_GET_VALUE(generateTriangleFaces, cuberille->GetGenerateTriangleFaces()); + ITK_TEST_SET_GET_VALUE(generateTriangleFaces, cuberille->GetGenerateTriangleFaces()); cuberille->SetProjectVerticesToIsoSurface(projectToIsoSurface); - TEST_SET_GET_VALUE(projectToIsoSurface, cuberille->GetProjectVerticesToIsoSurface()); + ITK_TEST_SET_GET_VALUE(projectToIsoSurface, cuberille->GetProjectVerticesToIsoSurface()); cuberille->SetProjectVertexSurfaceDistanceThreshold(surfaceDistanceThreshold); - TEST_SET_GET_VALUE(surfaceDistanceThreshold, cuberille->GetProjectVertexSurfaceDistanceThreshold()); + ITK_TEST_SET_GET_VALUE(surfaceDistanceThreshold, cuberille->GetProjectVertexSurfaceDistanceThreshold()); cuberille->SetProjectVertexStepLength(stepLength); - TEST_SET_GET_VALUE(stepLength, cuberille->GetProjectVertexStepLength()); + ITK_TEST_SET_GET_VALUE(stepLength, cuberille->GetProjectVertexStepLength()); cuberille->SetProjectVertexStepLengthRelaxationFactor(stepLengthRelax); - TEST_SET_GET_VALUE(stepLengthRelax, cuberille->GetProjectVertexStepLengthRelaxationFactor()); + ITK_TEST_SET_GET_VALUE(stepLengthRelax, cuberille->GetProjectVertexStepLengthRelaxationFactor()); cuberille->SetProjectVertexMaximumNumberOfSteps(maximumNumberOfSteps); - TEST_SET_GET_VALUE(maximumNumberOfSteps, cuberille->GetProjectVertexMaximumNumberOfSteps()); + ITK_TEST_SET_GET_VALUE(maximumNumberOfSteps, cuberille->GetProjectVertexMaximumNumberOfSteps()); time.Start(); - TRY_EXPECT_NO_EXCEPTION(cuberille->Update()); + ITK_TRY_EXPECT_NO_EXCEPTION(cuberille->Update()); time.Stop(); @@ -212,7 +212,7 @@ CuberilleTest01(int argc, char * argv[]) decimate->SetInput(outputMesh); - TRY_EXPECT_NO_EXCEPTION(decimate->Update()); + ITK_TRY_EXPECT_NO_EXCEPTION(decimate->Update()); #endif // Write mesh @@ -224,7 +224,7 @@ CuberilleTest01(int argc, char * argv[]) #endif writer->SetFileName(filenameOutputMesh); - TRY_EXPECT_NO_EXCEPTION(writer->Update()); + ITK_TRY_EXPECT_NO_EXCEPTION(writer->Update()); // Assert number of points/cells std::cout << "Polygonization took " << time.GetMean() << " seconds" << std::endl; From e3858e69853482cf809ec4c9c8d39b18c069a54a Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 8 Nov 2019 17:24:26 -0500 Subject: [PATCH 50/84] ENH: Enable the use of a different interpolator in wrapping BSplineInterpolateImageFunction, for example. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 2 +- .../Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index cea511aaa87..d8c1f1fcb33 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -89,7 +89,7 @@ CuberilleImageToMeshFilter::GenerateDat // Create interpolator for pixel value if (m_Interpolator.IsNull()) { - m_Interpolator = InterpolatorType::New(); + m_Interpolator = LinearInterpolateImageFunction::New(); } m_Interpolator->SetInputImage(image); diff --git a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap index eb6ec8ae2de..cedbcbc1ea1 100644 --- a/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap +++ b/Modules/Filtering/Cuberille/wrapping/itkCuberilleImageToMeshFilter.wrap @@ -3,7 +3,7 @@ unique(types "${WRAP_ITK_SCALAR}") foreach(d ${ITK_WRAP_IMAGE_DIMS}) foreach(t ${types}) itk_wrap_template("${ITKM_I${t}${d}}M${ITKM_${t}}${d}" - "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d} >" + "${ITKT_I${t}${d}}, itk::Mesh< ${ITKT_${t}},${d} >, itk::InterpolateImageFunction< ${ITKT_I${t}${d}}, ${ITKT_D} >" ) endforeach() endforeach() From d1d79391ddf8eb917ad62ed88f92775e3013df99 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Fri, 8 Nov 2019 17:29:43 -0500 Subject: [PATCH 51/84] STYLE: class -> typename --- .../include/itkCuberilleImageToMeshFilter.h | 6 +++-- .../include/itkCuberilleImageToMeshFilter.hxx | 22 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 653633ec8a6..836c57655b6 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -110,7 +110,9 @@ namespace itk * \ingroup Cuberille * */ -template > +template > class CuberilleImageToMeshFilter : public ImageToMeshFilter { public: @@ -305,7 +307,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter + template class VertexLookupMap { public: diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index d8c1f1fcb33..30b38b3e569 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -32,7 +32,7 @@ namespace itk { -template +template CuberilleImageToMeshFilter::CuberilleImageToMeshFilter() : m_IsoSurfaceValue(NumericTraits::One) , m_MaxSpacing(NumericTraits::One) @@ -46,20 +46,20 @@ CuberilleImageToMeshFilter::CuberilleIm this->SetNumberOfRequiredInputs(1); } -template +template CuberilleImageToMeshFilter::~CuberilleImageToMeshFilter() { m_GradientInterpolator = nullptr; } -template +template void CuberilleImageToMeshFilter::SetInput(const InputImageType * image) { this->ProcessObject::SetNthInput(0, const_cast(image)); } -template +template void CuberilleImageToMeshFilter::GenerateData() { @@ -273,7 +273,7 @@ CuberilleImageToMeshFilter::GenerateDat #endif } -template +template void CuberilleImageToMeshFilter::SetVerticesFromFace(unsigned int face, bool * v) { @@ -318,7 +318,7 @@ CuberilleImageToMeshFilter::SetVertices } } -template +template typename TInputImage::IndexType CuberilleImageToMeshFilter::GetVertexLookupIndex( unsigned int vertex, @@ -359,7 +359,7 @@ CuberilleImageToMeshFilter::GetVertexLo return result; } -template +template void CuberilleImageToMeshFilter::AddVertex( typename TOutputMesh::PointIdentifier & id, @@ -380,7 +380,7 @@ CuberilleImageToMeshFilter::AddVertex( mesh->GetPoints()->InsertElement(id++, vertex); } -template +template void CuberilleImageToMeshFilter::AddQuadFace( typename TOutputMesh::CellIdentifier & id, @@ -443,7 +443,7 @@ CuberilleImageToMeshFilter::AddQuadFace } } -template +template void CuberilleImageToMeshFilter::ProjectVertexToIsoSurface(PointType & vertex) { @@ -623,7 +623,7 @@ CuberilleImageToMeshFilter::ProjectVert #endif } -template +template void CuberilleImageToMeshFilter::ComputeGradientImage() { @@ -643,7 +643,7 @@ CuberilleImageToMeshFilter::ComputeGrad } } -template +template void CuberilleImageToMeshFilter::PrintSelf(std::ostream & os, Indent indent) const { From 5352ee9583f72de2f722b93f6a72a36812f4ea6e Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Thu, 12 Dec 2019 17:50:17 -0500 Subject: [PATCH 52/84] BUG: Use Direction When Computing Offsets Currently, the direction matrix of the input image is ignored when computing the half-pixel offset in the AddVertex method. When the direction matrix is identity, the result is correct; in all other cases, the resulting mesh is shifted relative to the underlying image data. This small patch accounts for the direction matrix in calculating the half-pixel offset, and additionally adds a simple test for cases where the direction matrix is non-identity. --- .../include/itkCuberilleImageToMeshFilter.hxx | 8 +- .../Filtering/Cuberille/test/CMakeLists.txt | 13 +- .../Cuberille/test/CuberilleTest02.cxx | 125 ++++++++++++++++++ 3 files changed, 141 insertions(+), 5 deletions(-) create mode 100644 Modules/Filtering/Cuberille/test/CuberilleTest02.cxx diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 30b38b3e569..671b6e19c17 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -369,10 +369,10 @@ CuberilleImageToMeshFilter::AddVertex( { PointType vertex; image->TransformIndexToPhysicalPoint(index, vertex); - SpacingType spacing = image->GetSpacing(); - vertex[0] -= (spacing[0] / 2.0); - vertex[1] -= (spacing[1] / 2.0); - vertex[2] -= (spacing[2] / 2.0); + const auto spacing = image->GetSpacing(); + const auto direction = image->GetDirection(); + const auto offset = direction * spacing * 0.5; + vertex -= offset; if (m_ProjectVerticesToIsoSurface) { ProjectVertexToIsoSurface(vertex); diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index c19f7f4bba3..800e6d3b885 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -2,7 +2,11 @@ itk_module_test() set(DATA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Input) -set(CuberilleTests CuberilleTest01.cxx) +set( + CuberilleTests + CuberilleTest01.cxx + CuberilleTest02.cxx +) createtestdriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}") @@ -326,3 +330,10 @@ add_test( # 0.85 # Step length relaxation factor # 25 # Maximum number of steps #) + +itk_add_test( + NAME CuberilleTestDirectionMatrix + COMMAND + ${itk-module}TestDriver + CuberilleTest02 +) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx new file mode 100644 index 00000000000..0ec32e932f3 --- /dev/null +++ b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx @@ -0,0 +1,125 @@ +/*========================================================================= + * + * Copyright Insight Software Consortium + * + * 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. + * + *=========================================================================*/ + +// STD +#include + +// ITK +#include "itkTestingMacros.h" +#include "itkImage.h" +#include "itkMesh.h" +#include "itkCuberilleImageToMeshFilter.h" +#include "itkNearestNeighborInterpolateImageFunction.h" + +const unsigned int Dimension = 3; +using TPixel = unsigned char; +using TImage = itk::Image; +using TMesh = itk::Mesh; +using TImageToMesh = itk::CuberilleImageToMeshFilter; +using TInterp = itk::NearestNeighborInterpolateImageFunction; + +TImage::Pointer +CuberilleTestCreateImage(const std::array & flips) +{ + const auto image = TImage::New(); + + TImage::IndexType start; + start.Fill(0); + + TImage::SizeType size; + size.Fill(3); + + TImage::RegionType region; + region.SetSize(size); + region.SetIndex(start); + + image->SetRegions(region); + image->Allocate(); + image->FillBuffer(0); + + TImage::IndexType index; + index.Fill(1); + image->SetPixel(index, 1); + + TImage::DirectionType direction; + direction.SetIdentity(); + + for (size_t i = 0; i < flips.size(); ++i) + { + if (flips[i]) + { + direction(i, i) *= -1; + } + } + + image->SetDirection(direction); + + return image; +} + +void +CuberilleTestHelper(TImage::Pointer input) +{ + + const auto image_to_mesh = TImageToMesh::New(); + image_to_mesh->SetInput(input); + image_to_mesh->ProjectVerticesToIsoSurfaceOff(); + image_to_mesh->Update(); + + const auto mesh = TMesh::New(); + mesh->Graft(image_to_mesh->GetOutput()); + mesh->DisconnectPipeline(); + + TMesh::PointType center; + center.Fill(0.0); + + for (auto it = mesh->GetPoints()->Begin(); it != mesh->GetPoints()->End(); ++it) + { + center[0] += it.Value()[0]; + center[1] += it.Value()[1]; + center[2] += it.Value()[2]; + } + + center[0] /= 8.0; + center[1] /= 8.0; + center[2] /= 8.0; + + const auto interp = TInterp::New(); + interp->SetInputImage(input); + + if (1 != interp->Evaluate(center)) + { + throw 0; + } +} + +int +CuberilleTest02(int itkNotUsed(argc), char * itkNotUsed(argv)[]) +{ + + CuberilleTestHelper(CuberilleTestCreateImage({ { false, false, false } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { false, false, true } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { false, true, false } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { false, true, true } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { true, false, false } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { true, false, true } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { true, true, false } })); + CuberilleTestHelper(CuberilleTestCreateImage({ { true, true, true } })); + + return EXIT_SUCCESS; +} From 7d917394bca589f1222e80ef7cd1a9be949ea139 Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Fri, 20 Dec 2019 13:58:01 -0500 Subject: [PATCH 53/84] ENH: Save Adjacent Pixel Value as Cell Data When a mesh is derived from a segmentation image, it is frequently useful to know, for each cell in the mesh, the value of the adjacent pixel in the segmentation image. In this way, it is possible to extract a labeled mesh from a multi-label segmentation. This patch adds an option (off by default) which allows the value of the adjacent pixel to be saved as cell data. --- .../include/itkCuberilleImageToMeshFilter.h | 9 +- .../include/itkCuberilleImageToMeshFilter.hxx | 37 ++-- .../Filtering/Cuberille/test/CMakeLists.txt | 8 + .../Cuberille/test/CuberilleTest03.cxx | 176 ++++++++++++++++++ 4 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 Modules/Filtering/Cuberille/test/CuberilleTest03.cxx diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 836c57655b6..4b35bffa04c 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -209,6 +209,12 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter::CuberilleIm , m_MaxSpacing(NumericTraits::One) , m_GenerateTriangleFaces(true) , m_ProjectVerticesToIsoSurface(true) + , m_SavePixelAsCellData(false) , m_ProjectVertexSurfaceDistanceThreshold(0.5) , m_ProjectVertexStepLength(-1.0) , m_ProjectVertexStepLengthRelaxationFactor(0.95) @@ -74,11 +75,7 @@ CuberilleImageToMeshFilter::GenerateDat typename OutputMeshType::Pointer mesh = Superclass::GetOutput(); // Compute maximum spacing - m_MaxSpacing = image->GetSpacing()[0]; - for (unsigned int i = 1; i < InputImageType::ImageDimension; ++i) - { - m_MaxSpacing = std::max(m_MaxSpacing, image->GetSpacing()[i]); - } + m_MaxSpacing = image->GetSpacing().GetVnlVector().max_value(); // Set default step length if (m_ProjectVertexStepLength < 0.0) @@ -218,7 +215,7 @@ CuberilleImageToMeshFilter::GenerateDat f[1] = v[4]; f[2] = v[7]; f[3] = v[3]; - AddQuadFace(nextCellId, f, mesh); + AddQuadFace(nextCellId, f, mesh, center); } if (faceHasQuad[1]) { @@ -226,7 +223,7 @@ CuberilleImageToMeshFilter::GenerateDat f[1] = v[1]; f[2] = v[5]; f[3] = v[4]; - AddQuadFace(nextCellId, f, mesh); + AddQuadFace(nextCellId, f, mesh, center); } if (faceHasQuad[2]) { @@ -234,7 +231,7 @@ CuberilleImageToMeshFilter::GenerateDat f[1] = v[2]; f[2] = v[6]; f[3] = v[5]; - AddQuadFace(nextCellId, f, mesh); + AddQuadFace(nextCellId, f, mesh, center); } if (faceHasQuad[3]) { @@ -242,7 +239,7 @@ CuberilleImageToMeshFilter::GenerateDat f[1] = v[3]; f[2] = v[7]; f[3] = v[6]; - AddQuadFace(nextCellId, f, mesh); + AddQuadFace(nextCellId, f, mesh, center); } if (faceHasQuad[4]) { @@ -250,7 +247,7 @@ CuberilleImageToMeshFilter::GenerateDat f[1] = v[3]; f[2] = v[2]; f[3] = v[1]; - AddQuadFace(nextCellId, f, mesh); + AddQuadFace(nextCellId, f, mesh, center); } if (faceHasQuad[5]) { @@ -258,7 +255,7 @@ CuberilleImageToMeshFilter::GenerateDat f[1] = v[5]; f[2] = v[6]; f[3] = v[7]; - AddQuadFace(nextCellId, f, mesh); + AddQuadFace(nextCellId, f, mesh, center); } } // end if num faces > 0 @@ -385,7 +382,8 @@ void CuberilleImageToMeshFilter::AddQuadFace( typename TOutputMesh::CellIdentifier & id, typename TOutputMesh::PointIdentifier face[4], - TOutputMesh * mesh) + TOutputMesh * mesh, + const InputPixelType & pixel) { if (m_GenerateTriangleFaces) { @@ -423,14 +421,20 @@ CuberilleImageToMeshFilter::AddQuadFace tri1.TakeOwnership(new TriangleCellType); tri1->SetPointIds(face1); mesh->SetCell(id++, tri1); - // mesh->SetCellData( id, (OutputPixelType)0 ); + if (this->m_SavePixelAsCellData) + { + mesh->SetCellData((id - 1), pixel); + } // Add triangle 2 cell TriangleCellAutoPointer tri2; tri2.TakeOwnership(new TriangleCellType); tri2->SetPointIds(face2); mesh->SetCell(id++, tri2); - // mesh->SetCellData( id, (OutputPixelType)0 ); + if (this->m_SavePixelAsCellData) + { + mesh->SetCellData((id - 1), pixel); + } } else { @@ -439,7 +443,10 @@ CuberilleImageToMeshFilter::AddQuadFace quad1.TakeOwnership(new QuadrilateralCellType); quad1->SetPointIds(face); mesh->SetCell(id++, quad1); - // mesh->SetCellData( id, (OutputPixelType)0 ); + if (this->m_SavePixelAsCellData) + { + mesh->SetCellData((id - 1), pixel); + } } } diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index 800e6d3b885..fbf307e837e 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -6,6 +6,7 @@ set( CuberilleTests CuberilleTest01.cxx CuberilleTest02.cxx + CuberilleTest03.cxx ) createtestdriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}") @@ -337,3 +338,10 @@ itk_add_test( ${itk-module}TestDriver CuberilleTest02 ) + +itk_add_test( + NAME CuberilleTestCellData + COMMAND + ${itk-module}TestDriver + CuberilleTest03 +) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx new file mode 100644 index 00000000000..08655ff6b13 --- /dev/null +++ b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx @@ -0,0 +1,176 @@ +/*========================================================================= + * + * Copyright Insight Software Consortium + * + * 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. + * + *=========================================================================*/ + +// STD +#include + +// ITK +#include "itkTestingMacros.h" +#include "itkImage.h" +#include "itkMesh.h" +#include "itkCuberilleImageToMeshFilter.h" +#include "itkNearestNeighborInterpolateImageFunction.h" +#include "itkTriangleHelper.h" + +const unsigned int Dimension = 3; +using TPixel = unsigned char; +using TImage = itk::Image; +using TMesh = itk::Mesh; +using TImageToMesh = itk::CuberilleImageToMeshFilter; +using TInterp = itk::NearestNeighborInterpolateImageFunction; +using TTriangleHelper = itk::TriangleHelper; + +TImage::Pointer +CuberilleTest03CreateImage() +{ + const auto image = TImage::New(); + + TImage::IndexType start; + start.Fill(0); + + TImage::SizeType size; + size[0] = 3; + size[1] = 5; + size[2] = 3; + + TImage::RegionType region; + region.SetSize(size); + region.SetIndex(start); + + image->SetRegions(region); + image->Allocate(); + image->FillBuffer(0); + + TImage::IndexType index; + index.Fill(1); + image->SetPixel(index, 1); + index[1] = 2; + image->SetPixel(index, 2); + index[1] = 3; + image->SetPixel(index, 3); + + return image; +} + +void +CuberilleTest03Helper(TImage::Pointer image) +{ + + const auto image_to_mesh = TImageToMesh::New(); + image_to_mesh->SetInput(image); + image_to_mesh->SavePixelAsCellDataOn(); + image_to_mesh->GenerateTriangleFacesOff(); + image_to_mesh->ProjectVerticesToIsoSurfaceOff(); + image_to_mesh->Update(); + + const auto mesh = TMesh::New(); + mesh->Graft(image_to_mesh->GetOutput()); + mesh->DisconnectPipeline(); + + const auto interp = TInterp::New(); + interp->SetInputImage(image); + + const auto half_spacing = image->GetSpacing() * 0.5; + + for (auto it = mesh->GetCells()->Begin(); it != mesh->GetCells()->End(); ++it) + { + const auto cell = it.Value(); + + typename TImage::PointType centroid; + centroid.SetToMidPoint(mesh->GetPoint(cell->GetPointIds()[0]), mesh->GetPoint(cell->GetPointIds()[2])); + + auto normal = TTriangleHelper::ComputeNormal(mesh->GetPoint(cell->GetPointIds()[0]), + mesh->GetPoint(cell->GetPointIds()[1]), + mesh->GetPoint(cell->GetPointIds()[2])); + + normal.Normalize(); + + for (size_t i = 0; i < 3; ++i) + { + normal[i] *= half_spacing[i]; + } + + const auto resample = centroid + -1.0f * normal; + const auto label = interp->Evaluate(resample); + + const auto data = mesh->GetCellData()->ElementAt(it.Index()); + + if (label != data) + { + std::cerr << "Calculated Pixel (" << label << ") != Cell Data (" << data << ").\n"; + throw 0; + } + } +} + +int +CuberilleTest03Parameters(const bool triangles, const bool project) +{ + + const auto image = CuberilleTest03CreateImage(); + const auto image_to_mesh = TImageToMesh::New(); + image_to_mesh->SetInput(image); + image_to_mesh->SavePixelAsCellDataOn(); + image_to_mesh->SetGenerateTriangleFaces(triangles); + image_to_mesh->SetProjectVerticesToIsoSurface(project); + ITK_TRY_EXPECT_NO_EXCEPTION(image_to_mesh->Update()); + ITK_TEST_EXPECT_EQUAL(image_to_mesh->GetOutput()->GetCells()->Size(), + image_to_mesh->GetOutput()->GetCellData()->Size()); + + return EXIT_SUCCESS; +} + +// - Create a test image, 3x3x5, with zeros along the edges and [1 2 3] +// in the interior. +// - Extract a Cuberille mesh calling SavePixelAsCellDataOn(). +// - For each cell in the mesh, calculate the centroid and normal. +// - Walk one-half pixel width from the centroid along the normal into the mesh. +// - Sample that point in the input segmentation image. +// - Assert that that cell data is equal to the adjacent pixel value. +int +CuberilleTest03(int itkNotUsed(argc), char * itkNotUsed(argv)[]) +{ + + { // Test Set/Get Methods + const auto image_to_mesh = TImageToMesh::New(); + bool save = true; + ITK_TEST_SET_GET_BOOLEAN(image_to_mesh, SavePixelAsCellData, save); + } + + if (EXIT_FAILURE == CuberilleTest03Parameters(false, false)) + { + throw 0; + } + if (EXIT_FAILURE == CuberilleTest03Parameters(false, true)) + { + throw 0; + } + if (EXIT_FAILURE == CuberilleTest03Parameters(true, false)) + { + throw 0; + } + if (EXIT_FAILURE == CuberilleTest03Parameters(true, true)) + { + throw 0; + } + + const auto image = CuberilleTest03CreateImage(); + CuberilleTest03Helper(image); + + return EXIT_SUCCESS; +} From 6a0f4117fa5acb25b89ea8234bfd51a1898588d7 Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Fri, 17 Jan 2020 16:49:35 -0800 Subject: [PATCH 54/84] STYLE: Prefer std::array to c-style arrays This patch makes a number of small modernizations which should marginally increase maintainability without impacting the logic of the code. In particular, std::array is prefered to c-style arrays, and std::swap is preferred to a custom pre-processing macro. --- .../include/itkCuberilleImageToMeshFilter.h | 7 +- .../include/itkCuberilleImageToMeshFilter.hxx | 72 ++++++++----------- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 4b35bffa04c..318f7114ac5 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -372,7 +372,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter & vertexHasQuad); /** Get the vertex lookup index from the given index and vertex number. */ inline IndexType @@ -388,7 +388,10 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter f, + OutputMeshType * mesh, + const InputPixelType & pixel); InputPixelType m_IsoSurfaceValue; InterpolatorPointer m_Interpolator; diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index e53818f7be2..300d6e5cbd7 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -18,16 +18,12 @@ #ifndef itkCuberilleImageToMeshFilter_hxx #define itkCuberilleImageToMeshFilter_hxx -#define SWAP(x, y) \ - unsigned int t; \ - t = x; \ - x = y; \ - y = t; - #include "itkMath.h" #include "itkCuberilleImageToMeshFilter.h" #include "itkNumericTraits.h" #include "itkConnectedComponentAlgorithm.h" +#include +#include namespace itk { @@ -107,39 +103,38 @@ CuberilleImageToMeshFilter::GenerateDat // mesh->GetCells()->Reserve( (size[0]*size[1]*size[2]) / 100 ); // Set up helper structures - unsigned int look, look0, look1 = 0; - unsigned char numFaces = 0; - bool faceHasQuad[6]; - bool vertexHasQuad[8]; - PointIdentifier v[8]; - PointIdentifier f[4]; - PointIdentifier nextVertexId = 0; - CellIdentifier nextCellId = 0; - IndexType index; - typename IndexType::IndexValueType lastZ = -1; - InputPixelType center = 0; - typename InputImageIteratorType::OffsetType offset[6]; + unsigned int look0 = 1; + unsigned int look1 = 0; + unsigned char numFaces = 0; + std::array faceHasQuad; + std::array vertexHasQuad; + std::array v; + std::array f; + PointIdentifier nextVertexId = 0; + CellIdentifier nextCellId = 0; + IndexType index; + typename IndexType::IndexValueType lastZ = -1; + InputPixelType center = 0; + std::array offset; offset[0][0] = -1; offset[0][1] = +0; - offset[0][2] = +0; + offset[0][2] = +0; // -X offset[1][0] = +0; offset[1][1] = -1; - offset[1][2] = +0; + offset[1][2] = +0; // -Y offset[2][0] = +1; offset[2][1] = +0; - offset[2][2] = +0; + offset[2][2] = +0; // +X offset[3][0] = +0; offset[3][1] = +1; - offset[3][2] = +0; + offset[3][2] = +0; // +Y offset[4][0] = +0; offset[4][1] = +0; - offset[4][2] = -1; + offset[4][2] = -1; // -Z offset[5][0] = +0; offset[5][1] = +0; - offset[5][2] = +1; + offset[5][2] = +1; // +Z VertexLookupMapType lookup[2]; - look0 = 1; - look1 = 0; lookup[look0].Clear(); lookup[look1].Clear(); @@ -157,20 +152,14 @@ CuberilleImageToMeshFilter::GenerateDat // Re-initialize for new pixel numFaces = 0; - for (bool & i : faceHasQuad) - { - i = false; - } - for (bool & i : vertexHasQuad) - { - i = false; - } + faceHasQuad.fill(false); + vertexHasQuad.fill(false); // Re-initialize for new z plane index = it.GetIndex(); if (index[2] != lastZ) { - SWAP(look0, look1); + std::swap(look0, look1); lookup[look1].Clear(); lastZ = index[2]; } @@ -183,7 +172,7 @@ CuberilleImageToMeshFilter::GenerateDat if (faceHasQuad[i]) { numFaces++; - SetVerticesFromFace(i, &vertexHasQuad[0]); + SetVerticesFromFace(i, vertexHasQuad); } } @@ -196,8 +185,8 @@ CuberilleImageToMeshFilter::GenerateDat if (vertexHasQuad[i]) { // Use the vertex lookup to get the vertex for the correct slice - IndexType vindex = GetVertexLookupIndex(i, index); - look = (i < 4) ? look0 : look1; // First four are first slice + IndexType vindex = GetVertexLookupIndex(i, index); + unsigned int look = (i < 4) ? look0 : look1; // First four are first slice if (!lookup[look].GetVertex(vindex[0], vindex[1], v[i])) { // Vertex was not in lookup, create and add to lookup @@ -272,7 +261,8 @@ CuberilleImageToMeshFilter::GenerateDat template void -CuberilleImageToMeshFilter::SetVerticesFromFace(unsigned int face, bool * v) +CuberilleImageToMeshFilter::SetVerticesFromFace(unsigned int face, + std::array & v) { switch (face) { @@ -381,7 +371,7 @@ template void CuberilleImageToMeshFilter::AddQuadFace( typename TOutputMesh::CellIdentifier & id, - typename TOutputMesh::PointIdentifier face[4], + std::array face, TOutputMesh * mesh, const InputPixelType & pixel) { @@ -441,7 +431,7 @@ CuberilleImageToMeshFilter::AddQuadFace // Add quateraleral cell QuadrilateralCellAutoPointer quad1; quad1.TakeOwnership(new QuadrilateralCellType); - quad1->SetPointIds(face); + quad1->SetPointIds(face.data()); mesh->SetCell(id++, quad1); if (this->m_SavePixelAsCellData) { From fd8b3ab1f7256734864c334088c08bd317d02ebc Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Tue, 4 Feb 2020 17:29:08 -0500 Subject: [PATCH 55/84] BUG: Prevent Non-Manifold Geometry This patch addresses a long-standing issue in the Cuberille module which frequently prevented its use with itk::QuadEdgeMesh, due a number of cases in which this filter produces non-manifold geometry. This patch determines, for each 2x2x2 neighborhood surrounding a vertex, how many face-connected components N are present, and replicates the relevant vertex N times. Then, in the vertex map, the bitmask and the position of the pixel index is used to determine which vertex to select in the construction of each face. --- .../include/itkCuberilleImageToMeshFilter.h | 34 +- .../include/itkCuberilleImageToMeshFilter.hxx | 407 +++++++++++++++++- Modules/Filtering/Cuberille/itk-module.cmake | 1 + .../Filtering/Cuberille/test/CMakeLists.txt | 12 +- .../Cuberille/test/CuberilleTest01.cxx | 10 + 5 files changed, 434 insertions(+), 30 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 318f7114ac5..bafd83ad07e 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -142,6 +142,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter; using CellIdentifier = typename OutputMeshType::CellIdentifier; using CellInterfaceType = CellInterface; using TriangleCellType = TriangleCell; @@ -242,6 +243,10 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter; + using MapType = std::map; /** Constructors */ VertexLookupMap() {} @@ -334,17 +338,17 @@ class CuberilleImageToMeshFilter : public ImageToMeshFiltersecond; + id = it->second.at(component); } return result; } @@ -383,8 +387,12 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter; + using TLabelsArray = std::array; + + TLabelsArray m_LabelsArray; + InputPixelType m_IsoSurfaceValue; InterpolatorPointer m_Interpolator; GradientInterpolatorPointer m_GradientInterpolator; diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 300d6e5cbd7..0f6a83b78d1 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -21,9 +21,10 @@ #include "itkMath.h" #include "itkCuberilleImageToMeshFilter.h" #include "itkNumericTraits.h" -#include "itkConnectedComponentAlgorithm.h" +#include #include #include +#include namespace itk { @@ -41,6 +42,7 @@ CuberilleImageToMeshFilter::CuberilleIm , m_ProjectVertexMaximumNumberOfSteps(50) { this->SetNumberOfRequiredInputs(1); + this->CalculateLabelsArray(); } template @@ -182,19 +184,28 @@ CuberilleImageToMeshFilter::GenerateDat // Create vertices for (unsigned int i = 0; i < 8; ++i) { - if (vertexHasQuad[i]) + if (!vertexHasQuad[i]) { - // Use the vertex lookup to get the vertex for the correct slice - IndexType vindex = GetVertexLookupIndex(i, index); - unsigned int look = (i < 4) ? look0 : look1; // First four are first slice - if (!lookup[look].GetVertex(vindex[0], vindex[1], v[i])) - { - // Vertex was not in lookup, create and add to lookup - v[i] = nextVertexId; - AddVertex(nextVertexId, vindex, image, mesh); - lookup[look].AddVertex(vindex[0], vindex[1], v[i]); - } - } // end if vertex has quad + continue; + } + + // Use the vertex lookup to get the vertex for the correct slice + IndexType vindex = GetVertexLookupIndex(i, index); + const auto bitmaskID = this->CalculateBitmaskIDForVertexIndex(vindex); + const auto diff = vindex - index; + size_t offsetID = 7 - (diff[0] + diff[1] * 2 + diff[2] * 4); + const auto components = this->m_LabelsArray.at(bitmaskID); + const auto numComponents = (*std::max_element(components.begin(), components.end())) + 1; + const auto component = components.at(offsetID); + unsigned int look = (i < 4) ? look0 : look1; // First four are first slice + if (!lookup[look].GetVertex(vindex[0], vindex[1], component, v[i])) + { + // Vertex was not in lookup, create and add to lookup + v[i] = nextVertexId; + const auto pv = AddVertex(nextVertexId, vindex, image, mesh, numComponents); + lookup[look].AddVertex(vindex[0], vindex[1], pv); + } + } // end foreach vertex // Create faces @@ -347,12 +358,13 @@ CuberilleImageToMeshFilter::GetVertexLo } template -void +typename CuberilleImageToMeshFilter::PointVectorType CuberilleImageToMeshFilter::AddVertex( typename TOutputMesh::PointIdentifier & id, typename TInputImage::IndexType index, const TInputImage * image, - TOutputMesh * mesh) + TOutputMesh * mesh, + const size_t numComponents) { PointType vertex; image->TransformIndexToPhysicalPoint(index, vertex); @@ -364,7 +376,13 @@ CuberilleImageToMeshFilter::AddVertex( { ProjectVertexToIsoSurface(vertex); } - mesh->GetPoints()->InsertElement(id++, vertex); + PointVectorType pointIDVector; + for (size_t i = 0; i < numComponents; ++i) + { + pointIDVector.push_back(id); + mesh->GetPoints()->InsertElement(id++, vertex); + } + return pointIDVector; } template @@ -640,6 +658,363 @@ CuberilleImageToMeshFilter::ComputeGrad } } +template +size_t +CuberilleImageToMeshFilter::CalculateBitmaskIDForVertexIndex( + const IndexType & vindex) +{ + typename IndexType::OffsetType ones; + ones.Fill(1); + const IndexType localorigin = vindex - ones; + std::bitset<8> bitmask; + for (size_t i = 0; i < 8; ++i) + { + std::bitset<3> bits(i); + typename IndexType::OffsetType offset; + offset[0] = bits[0]; + offset[1] = bits[1]; + offset[2] = bits[2]; + const auto index = localorigin + offset; + const auto pixel = this->GetInput()->GetPixel(index); + bitmask[i] = (pixel >= this->m_IsoSurfaceValue); + } + return static_cast(bitmask.to_ulong()); +} + +template +void +CuberilleImageToMeshFilter::CalculateLabelsArray() +{ + + // The commented code below iterates through all possible binary 2x2x2 regions, + // runs connected components on the result. The total number of foreground + // connected components is equal to the number of vertices which must be + // replicated--these vertices are stored in the VertexMap. One less than the + // number of the connected component is equal to the index of the corresponding + // vertex in the VertexMap. While it would be easily possible to generate this at + // runtime, it could theoretically cause a significant performance hit in a + // case where many instances of this class must be instantiated. Therefore, + // the values are hardcoded below. The commented code was used to generate + // the hardcoded values. + // + // using TImage = itk::Image; + // using TConnected = itk::ConnectedComponentImageFilter< TImage, TImage >; + // + // for (size_t mask = 0; mask < pow(2, 8); ++mask) { + // + // std::bitset<8> bitmask(mask); + // + // const auto image = TImage::New(); + // TImage::SizeType size; + // size.Fill( 2 ); + // + // TImage::IndexType origin; + // origin.Fill( 0 ); + // + // TImage::RegionType region(origin, size); + // + // image->SetRegions( region ); + // image->Allocate(); + // image->FillBuffer( 0 ); + // + // for (size_t index = 0; index < pow(2, 3); ++index) { + // std::bitset<3> bitindex(index); + // image->SetPixel( {{bitindex[0], bitindex[1], bitindex[2]}}, bitmask[index] ); + // } + // + // const auto connected = TConnected::New(); + // connected->SetInput( image ); + // connected->FullyConnectedOff(); + // connected->Update(); + // + // TLabels labels; + // + // for (size_t index = 0; index < pow(2, 3); ++index) { + // std::bitset<3> bitindex(index); + // const auto component = connected->GetOutput()->GetPixel({{ + // bitindex[0], + // bitindex[1], + // bitindex[2] + // }}); + // labels[index] = component - 1; + // } + // + // this->m_LabelsArray[mask] = labels; + // + // } + // + // for (size_t i = 0; i < pow(2,8); ++i) { + // std::cout << "this->m_LabelsArray[" << i << "] "; + // if (i < 10) std::cout << ' '; + // if (i < 100) std::cout << ' '; + // std::cout << "= {"; + // for (size_t j = 0; j < pow(2,3); ++j) { + // const auto k = static_cast(this->m_LabelsArray[i][j]); + // if (k >= 0) std::cout << ' '; + // std::cout << k; + // if (j != 7) std::cout << ','; + // } + // std::cout << "};\n"; + // } + + this->m_LabelsArray[0] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[1] = { 0, -1, -1, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[2] = { -1, 0, -1, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[3] = { 0, 0, -1, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[4] = { -1, -1, 0, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[5] = { 0, -1, 0, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[6] = { -1, 0, 1, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[7] = { 0, 0, 0, -1, -1, -1, -1, -1 }; + this->m_LabelsArray[8] = { -1, -1, -1, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[9] = { 0, -1, -1, 1, -1, -1, -1, -1 }; + this->m_LabelsArray[10] = { -1, 0, -1, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[11] = { 0, 0, -1, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[12] = { -1, -1, 0, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[13] = { 0, -1, 0, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[14] = { -1, 0, 0, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[15] = { 0, 0, 0, 0, -1, -1, -1, -1 }; + this->m_LabelsArray[16] = { -1, -1, -1, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[17] = { 0, -1, -1, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[18] = { -1, 0, -1, -1, 1, -1, -1, -1 }; + this->m_LabelsArray[19] = { 0, 0, -1, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[20] = { -1, -1, 0, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[21] = { 0, -1, 0, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[22] = { -1, 0, 1, -1, 1, -1, -1, -1 }; + this->m_LabelsArray[23] = { 0, 0, 0, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[24] = { -1, -1, -1, 0, 1, -1, -1, -1 }; + this->m_LabelsArray[25] = { 0, -1, -1, 1, 0, -1, -1, -1 }; + this->m_LabelsArray[26] = { -1, 0, -1, 0, 1, -1, -1, -1 }; + this->m_LabelsArray[27] = { 0, 0, -1, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[28] = { -1, -1, 0, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[29] = { 0, -1, 0, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[30] = { -1, 0, 0, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[31] = { 0, 0, 0, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[32] = { -1, -1, -1, -1, -1, 0, -1, -1 }; + this->m_LabelsArray[33] = { 0, -1, -1, -1, -1, 1, -1, -1 }; + this->m_LabelsArray[34] = { -1, 0, -1, -1, -1, 0, -1, -1 }; + this->m_LabelsArray[35] = { 0, 0, -1, -1, -1, 0, -1, -1 }; + this->m_LabelsArray[36] = { -1, -1, 0, -1, -1, 1, -1, -1 }; + this->m_LabelsArray[37] = { 0, -1, 0, -1, -1, 1, -1, -1 }; + this->m_LabelsArray[38] = { -1, 0, 1, -1, -1, 0, -1, -1 }; + this->m_LabelsArray[39] = { 0, 0, 0, -1, -1, 0, -1, -1 }; + this->m_LabelsArray[40] = { -1, -1, -1, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[41] = { 0, -1, -1, 1, -1, 1, -1, -1 }; + this->m_LabelsArray[42] = { -1, 0, -1, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[43] = { 0, 0, -1, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[44] = { -1, -1, 0, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[45] = { 0, -1, 0, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[46] = { -1, 0, 0, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[47] = { 0, 0, 0, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[48] = { -1, -1, -1, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[49] = { 0, -1, -1, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[50] = { -1, 0, -1, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[51] = { 0, 0, -1, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[52] = { -1, -1, 0, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[53] = { 0, -1, 0, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[54] = { -1, 0, 0, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[55] = { 0, 0, 0, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[56] = { -1, -1, -1, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[57] = { 0, -1, -1, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[58] = { -1, 0, -1, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[59] = { 0, 0, -1, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[60] = { -1, -1, 0, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[61] = { 0, -1, 0, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[62] = { -1, 0, 0, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[63] = { 0, 0, 0, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[64] = { -1, -1, -1, -1, -1, -1, 0, -1 }; + this->m_LabelsArray[65] = { 0, -1, -1, -1, -1, -1, 1, -1 }; + this->m_LabelsArray[66] = { -1, 0, -1, -1, -1, -1, 1, -1 }; + this->m_LabelsArray[67] = { 0, 0, -1, -1, -1, -1, 1, -1 }; + this->m_LabelsArray[68] = { -1, -1, 0, -1, -1, -1, 0, -1 }; + this->m_LabelsArray[69] = { 0, -1, 0, -1, -1, -1, 0, -1 }; + this->m_LabelsArray[70] = { -1, 0, 1, -1, -1, -1, 1, -1 }; + this->m_LabelsArray[71] = { 0, 0, 0, -1, -1, -1, 0, -1 }; + this->m_LabelsArray[72] = { -1, -1, -1, 0, -1, -1, 1, -1 }; + this->m_LabelsArray[73] = { 0, -1, -1, 1, -1, -1, 2, -1 }; + this->m_LabelsArray[74] = { -1, 0, -1, 0, -1, -1, 1, -1 }; + this->m_LabelsArray[75] = { 0, 0, -1, 0, -1, -1, 1, -1 }; + this->m_LabelsArray[76] = { -1, -1, 0, 0, -1, -1, 0, -1 }; + this->m_LabelsArray[77] = { 0, -1, 0, 0, -1, -1, 0, -1 }; + this->m_LabelsArray[78] = { -1, 0, 0, 0, -1, -1, 0, -1 }; + this->m_LabelsArray[79] = { 0, 0, 0, 0, -1, -1, 0, -1 }; + this->m_LabelsArray[80] = { -1, -1, -1, -1, 0, -1, 0, -1 }; + this->m_LabelsArray[81] = { 0, -1, -1, -1, 0, -1, 0, -1 }; + this->m_LabelsArray[82] = { -1, 0, -1, -1, 1, -1, 1, -1 }; + this->m_LabelsArray[83] = { 0, 0, -1, -1, 0, -1, 0, -1 }; + this->m_LabelsArray[84] = { -1, -1, 0, -1, 0, -1, 0, -1 }; + this->m_LabelsArray[85] = { 0, -1, 0, -1, 0, -1, 0, -1 }; + this->m_LabelsArray[86] = { -1, 0, 1, -1, 1, -1, 1, -1 }; + this->m_LabelsArray[87] = { 0, 0, 0, -1, 0, -1, 0, -1 }; + this->m_LabelsArray[88] = { -1, -1, -1, 0, 1, -1, 1, -1 }; + this->m_LabelsArray[89] = { 0, -1, -1, 1, 0, -1, 0, -1 }; + this->m_LabelsArray[90] = { -1, 0, -1, 0, 1, -1, 1, -1 }; + this->m_LabelsArray[91] = { 0, 0, -1, 0, 0, -1, 0, -1 }; + this->m_LabelsArray[92] = { -1, -1, 0, 0, 0, -1, 0, -1 }; + this->m_LabelsArray[93] = { 0, -1, 0, 0, 0, -1, 0, -1 }; + this->m_LabelsArray[94] = { -1, 0, 0, 0, 0, -1, 0, -1 }; + this->m_LabelsArray[95] = { 0, 0, 0, 0, 0, -1, 0, -1 }; + this->m_LabelsArray[96] = { -1, -1, -1, -1, -1, 0, 1, -1 }; + this->m_LabelsArray[97] = { 0, -1, -1, -1, -1, 1, 2, -1 }; + this->m_LabelsArray[98] = { -1, 0, -1, -1, -1, 0, 1, -1 }; + this->m_LabelsArray[99] = { 0, 0, -1, -1, -1, 0, 1, -1 }; + this->m_LabelsArray[100] = { -1, -1, 0, -1, -1, 1, 0, -1 }; + this->m_LabelsArray[101] = { 0, -1, 0, -1, -1, 1, 0, -1 }; + this->m_LabelsArray[102] = { -1, 0, 1, -1, -1, 0, 1, -1 }; + this->m_LabelsArray[103] = { 0, 0, 0, -1, -1, 0, 0, -1 }; + this->m_LabelsArray[104] = { -1, -1, -1, 0, -1, 0, 1, -1 }; + this->m_LabelsArray[105] = { 0, -1, -1, 1, -1, 1, 2, -1 }; + this->m_LabelsArray[106] = { -1, 0, -1, 0, -1, 0, 1, -1 }; + this->m_LabelsArray[107] = { 0, 0, -1, 0, -1, 0, 1, -1 }; + this->m_LabelsArray[108] = { -1, -1, 0, 0, -1, 0, 0, -1 }; + this->m_LabelsArray[109] = { 0, -1, 0, 0, -1, 0, 0, -1 }; + this->m_LabelsArray[110] = { -1, 0, 0, 0, -1, 0, 0, -1 }; + this->m_LabelsArray[111] = { 0, 0, 0, 0, -1, 0, 0, -1 }; + this->m_LabelsArray[112] = { -1, -1, -1, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[113] = { 0, -1, -1, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[114] = { -1, 0, -1, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[115] = { 0, 0, -1, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[116] = { -1, -1, 0, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[117] = { 0, -1, 0, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[118] = { -1, 0, 0, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[119] = { 0, 0, 0, -1, 0, 0, 0, -1 }; + this->m_LabelsArray[120] = { -1, -1, -1, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[121] = { 0, -1, -1, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[122] = { -1, 0, -1, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[123] = { 0, 0, -1, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[124] = { -1, -1, 0, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[125] = { 0, -1, 0, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[126] = { -1, 0, 0, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[127] = { 0, 0, 0, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[128] = { -1, -1, -1, -1, -1, -1, -1, 0 }; + this->m_LabelsArray[129] = { 0, -1, -1, -1, -1, -1, -1, 1 }; + this->m_LabelsArray[130] = { -1, 0, -1, -1, -1, -1, -1, 1 }; + this->m_LabelsArray[131] = { 0, 0, -1, -1, -1, -1, -1, 1 }; + this->m_LabelsArray[132] = { -1, -1, 0, -1, -1, -1, -1, 1 }; + this->m_LabelsArray[133] = { 0, -1, 0, -1, -1, -1, -1, 1 }; + this->m_LabelsArray[134] = { -1, 0, 1, -1, -1, -1, -1, 2 }; + this->m_LabelsArray[135] = { 0, 0, 0, -1, -1, -1, -1, 1 }; + this->m_LabelsArray[136] = { -1, -1, -1, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[137] = { 0, -1, -1, 1, -1, -1, -1, 1 }; + this->m_LabelsArray[138] = { -1, 0, -1, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[139] = { 0, 0, -1, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[140] = { -1, -1, 0, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[141] = { 0, -1, 0, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[142] = { -1, 0, 0, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[143] = { 0, 0, 0, 0, -1, -1, -1, 0 }; + this->m_LabelsArray[144] = { -1, -1, -1, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[145] = { 0, -1, -1, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[146] = { -1, 0, -1, -1, 1, -1, -1, 2 }; + this->m_LabelsArray[147] = { 0, 0, -1, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[148] = { -1, -1, 0, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[149] = { 0, -1, 0, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[150] = { -1, 0, 1, -1, 1, -1, -1, 2 }; + this->m_LabelsArray[151] = { 0, 0, 0, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[152] = { -1, -1, -1, 0, 1, -1, -1, 0 }; + this->m_LabelsArray[153] = { 0, -1, -1, 1, 0, -1, -1, 1 }; + this->m_LabelsArray[154] = { -1, 0, -1, 0, 1, -1, -1, 0 }; + this->m_LabelsArray[155] = { 0, 0, -1, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[156] = { -1, -1, 0, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[157] = { 0, -1, 0, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[158] = { -1, 0, 0, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[159] = { 0, 0, 0, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[160] = { -1, -1, -1, -1, -1, 0, -1, 0 }; + this->m_LabelsArray[161] = { 0, -1, -1, -1, -1, 1, -1, 1 }; + this->m_LabelsArray[162] = { -1, 0, -1, -1, -1, 0, -1, 0 }; + this->m_LabelsArray[163] = { 0, 0, -1, -1, -1, 0, -1, 0 }; + this->m_LabelsArray[164] = { -1, -1, 0, -1, -1, 1, -1, 1 }; + this->m_LabelsArray[165] = { 0, -1, 0, -1, -1, 1, -1, 1 }; + this->m_LabelsArray[166] = { -1, 0, 1, -1, -1, 0, -1, 0 }; + this->m_LabelsArray[167] = { 0, 0, 0, -1, -1, 0, -1, 0 }; + this->m_LabelsArray[168] = { -1, -1, -1, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[169] = { 0, -1, -1, 1, -1, 1, -1, 1 }; + this->m_LabelsArray[170] = { -1, 0, -1, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[171] = { 0, 0, -1, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[172] = { -1, -1, 0, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[173] = { 0, -1, 0, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[174] = { -1, 0, 0, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[175] = { 0, 0, 0, 0, -1, 0, -1, 0 }; + this->m_LabelsArray[176] = { -1, -1, -1, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[177] = { 0, -1, -1, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[178] = { -1, 0, -1, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[179] = { 0, 0, -1, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[180] = { -1, -1, 0, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[181] = { 0, -1, 0, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[182] = { -1, 0, 0, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[183] = { 0, 0, 0, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[184] = { -1, -1, -1, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[185] = { 0, -1, -1, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[186] = { -1, 0, -1, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[187] = { 0, 0, -1, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[188] = { -1, -1, 0, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[189] = { 0, -1, 0, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[190] = { -1, 0, 0, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[191] = { 0, 0, 0, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[192] = { -1, -1, -1, -1, -1, -1, 0, 0 }; + this->m_LabelsArray[193] = { 0, -1, -1, -1, -1, -1, 1, 1 }; + this->m_LabelsArray[194] = { -1, 0, -1, -1, -1, -1, 1, 1 }; + this->m_LabelsArray[195] = { 0, 0, -1, -1, -1, -1, 1, 1 }; + this->m_LabelsArray[196] = { -1, -1, 0, -1, -1, -1, 0, 0 }; + this->m_LabelsArray[197] = { 0, -1, 0, -1, -1, -1, 0, 0 }; + this->m_LabelsArray[198] = { -1, 0, 1, -1, -1, -1, 1, 1 }; + this->m_LabelsArray[199] = { 0, 0, 0, -1, -1, -1, 0, 0 }; + this->m_LabelsArray[200] = { -1, -1, -1, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[201] = { 0, -1, -1, 1, -1, -1, 1, 1 }; + this->m_LabelsArray[202] = { -1, 0, -1, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[203] = { 0, 0, -1, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[204] = { -1, -1, 0, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[205] = { 0, -1, 0, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[206] = { -1, 0, 0, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[207] = { 0, 0, 0, 0, -1, -1, 0, 0 }; + this->m_LabelsArray[208] = { -1, -1, -1, -1, 0, -1, 0, 0 }; + this->m_LabelsArray[209] = { 0, -1, -1, -1, 0, -1, 0, 0 }; + this->m_LabelsArray[210] = { -1, 0, -1, -1, 1, -1, 1, 1 }; + this->m_LabelsArray[211] = { 0, 0, -1, -1, 0, -1, 0, 0 }; + this->m_LabelsArray[212] = { -1, -1, 0, -1, 0, -1, 0, 0 }; + this->m_LabelsArray[213] = { 0, -1, 0, -1, 0, -1, 0, 0 }; + this->m_LabelsArray[214] = { -1, 0, 1, -1, 1, -1, 1, 1 }; + this->m_LabelsArray[215] = { 0, 0, 0, -1, 0, -1, 0, 0 }; + this->m_LabelsArray[216] = { -1, -1, -1, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[217] = { 0, -1, -1, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[218] = { -1, 0, -1, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[219] = { 0, 0, -1, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[220] = { -1, -1, 0, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[221] = { 0, -1, 0, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[222] = { -1, 0, 0, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[223] = { 0, 0, 0, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[224] = { -1, -1, -1, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[225] = { 0, -1, -1, -1, -1, 1, 1, 1 }; + this->m_LabelsArray[226] = { -1, 0, -1, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[227] = { 0, 0, -1, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[228] = { -1, -1, 0, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[229] = { 0, -1, 0, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[230] = { -1, 0, 0, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[231] = { 0, 0, 0, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[232] = { -1, -1, -1, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[233] = { 0, -1, -1, 1, -1, 1, 1, 1 }; + this->m_LabelsArray[234] = { -1, 0, -1, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[235] = { 0, 0, -1, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[236] = { -1, -1, 0, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[237] = { 0, -1, 0, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[238] = { -1, 0, 0, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[239] = { 0, 0, 0, 0, -1, 0, 0, 0 }; + this->m_LabelsArray[240] = { -1, -1, -1, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[241] = { 0, -1, -1, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[242] = { -1, 0, -1, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[243] = { 0, 0, -1, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[244] = { -1, -1, 0, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[245] = { 0, -1, 0, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[246] = { -1, 0, 0, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[247] = { 0, 0, 0, -1, 0, 0, 0, 0 }; + this->m_LabelsArray[248] = { -1, -1, -1, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[249] = { 0, -1, -1, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[250] = { -1, 0, -1, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[251] = { 0, 0, -1, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[252] = { -1, -1, 0, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[253] = { 0, -1, 0, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[254] = { -1, 0, 0, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[255] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +} + template void CuberilleImageToMeshFilter::PrintSelf(std::ostream & os, Indent indent) const diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake index bbe1865e333..7c90959498a 100644 --- a/Modules/Filtering/Cuberille/itk-module.cmake +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -24,6 +24,7 @@ itk_module( ITKImageFunction ITKImageGradient ITKMesh + ITKConnectedComponents TEST_DEPENDS ITKTestKernel ITKQuadEdgeMesh diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index fbf307e837e..b9b91b3b76f 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -47,7 +47,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob2.mha ${ITK_TEST_OUTPUT_DIR}/blob2-01.vtk 200 # Iso-surface value - 14 # Expected number of points + 16 # Expected number of points 12 # Expected number of cells 0 # Generate triangle faces 0 # Project vertices to iso-surface @@ -62,7 +62,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob3.mha ${ITK_TEST_OUTPUT_DIR}/blob3-01.vtk 200 # Iso-surface value - 122 # Expected number of points + 216 # Expected number of points 180 # Expected number of cells 0 # Generate triangle faces 0 # Project vertices to iso-surface @@ -92,7 +92,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/marschnerlobb.mha ${ITK_TEST_OUTPUT_DIR}/marschnerlobb-01.vtk 55 # Iso-surface value - 20524 # Expected number of points + 22106 # Expected number of points 22104 # Expected number of cells 0 # Generate triangle faces 1 # Project vertices to iso-surface @@ -167,7 +167,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha ${ITK_TEST_OUTPUT_DIR}/neghip-01.vtk 55 # Iso-surface value - 15146 # Expected number of points + 15162 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces 0 # Project vertices to iso-surface @@ -182,7 +182,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha ${ITK_TEST_OUTPUT_DIR}/neghip-02.vtk 55 # Iso-surface value - 15146 # Expected number of points + 15162 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces 1 # Project vertices to iso-surface @@ -197,7 +197,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha ${ITK_TEST_OUTPUT_DIR}/neghip-03.vtk 55 # Iso-surface value - 15146 # Expected number of points + 15162 # Expected number of points 30272 # Expected number of cells 1 # Generate triangle faces 1 # Project vertices to iso-surface diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index d549e8a5a82..25ed6457a1f 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -156,6 +156,16 @@ CuberilleTest01(int argc, char * argv[]) ITK_EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); + // How long does it take to pre-calculate the array labels array? + + itk::TimeProbe labelsArrayTimer; + + labelsArrayTimer.Start(); + cuberille->CalculateLabelsArray(); + labelsArrayTimer.Stop(); + + std::cout << "Time to calculate labels array: " << labelsArrayTimer.GetMean() << std::endl; + cuberille->SetInput(input); cuberille->SetIsoSurfaceValue(isoSurfaceValue); From 82b888457755e4a83b57dab4f9b44b2602c46956 Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Thu, 6 Feb 2020 22:56:26 -0500 Subject: [PATCH 56/84] BUG: Regenerate Hardcoded Array with CC Bugfix There was a recent bugfix to the ITK connected components filter, which is useed to generate the hardcoded connected components array. Unfortunately, the array was generated with the filter prior to applying the most recent ITK patch. This filter regenerates the array. Additionally, it updates to the correct number of points for the test cases. Finally, it enables itk::QuadEdgeMesh in the test suite, verifying that the filter now works for this data type on all the tested inputs. --- .../include/itkCuberilleImageToMeshFilter.hxx | 59 +++++++++++-------- .../Filtering/Cuberille/test/CMakeLists.txt | 8 +-- .../Cuberille/test/CuberilleTest01.cxx | 2 +- 3 files changed, 38 insertions(+), 31 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 0f6a83b78d1..28953c41abb 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -697,6 +697,13 @@ CuberilleImageToMeshFilter::CalculateLa // the values are hardcoded below. The commented code was used to generate // the hardcoded values. // + // NOTE: There was previously a bug in itk::ConnectedComponentImageFilter which + // caused the hardcoded values below to be incorrect, though this has since been + // fixed. Therefore, the commented code below will fail if built prior to the + // following patch: + // + // Git Hash: c32a7846cb6502eaa79780de3ff7b0ce81597118 + // // using TImage = itk::Image; // using TConnected = itk::ConnectedComponentImageFilter< TImage, TImage >; // @@ -757,7 +764,7 @@ CuberilleImageToMeshFilter::CalculateLa // std::cout << "};\n"; // } - this->m_LabelsArray[0] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[0] = { -1, -1, -1, -1, -1, -1, -1, -1 }; this->m_LabelsArray[1] = { 0, -1, -1, -1, -1, -1, -1, -1 }; this->m_LabelsArray[2] = { -1, 0, -1, -1, -1, -1, -1, -1 }; this->m_LabelsArray[3] = { 0, 0, -1, -1, -1, -1, -1, -1 }; @@ -777,17 +784,17 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[17] = { 0, -1, -1, -1, 0, -1, -1, -1 }; this->m_LabelsArray[18] = { -1, 0, -1, -1, 1, -1, -1, -1 }; this->m_LabelsArray[19] = { 0, 0, -1, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[20] = { -1, -1, 0, -1, 0, -1, -1, -1 }; + this->m_LabelsArray[20] = { -1, -1, 0, -1, 1, -1, -1, -1 }; this->m_LabelsArray[21] = { 0, -1, 0, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[22] = { -1, 0, 1, -1, 1, -1, -1, -1 }; + this->m_LabelsArray[22] = { -1, 0, 1, -1, 2, -1, -1, -1 }; this->m_LabelsArray[23] = { 0, 0, 0, -1, 0, -1, -1, -1 }; this->m_LabelsArray[24] = { -1, -1, -1, 0, 1, -1, -1, -1 }; this->m_LabelsArray[25] = { 0, -1, -1, 1, 0, -1, -1, -1 }; this->m_LabelsArray[26] = { -1, 0, -1, 0, 1, -1, -1, -1 }; this->m_LabelsArray[27] = { 0, 0, -1, 0, 0, -1, -1, -1 }; - this->m_LabelsArray[28] = { -1, -1, 0, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[28] = { -1, -1, 0, 0, 1, -1, -1, -1 }; this->m_LabelsArray[29] = { 0, -1, 0, 0, 0, -1, -1, -1 }; - this->m_LabelsArray[30] = { -1, 0, 0, 0, 0, -1, -1, -1 }; + this->m_LabelsArray[30] = { -1, 0, 0, 0, 1, -1, -1, -1 }; this->m_LabelsArray[31] = { 0, 0, 0, 0, 0, -1, -1, -1 }; this->m_LabelsArray[32] = { -1, -1, -1, -1, -1, 0, -1, -1 }; this->m_LabelsArray[33] = { 0, -1, -1, -1, -1, 1, -1, -1 }; @@ -797,27 +804,27 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[37] = { 0, -1, 0, -1, -1, 1, -1, -1 }; this->m_LabelsArray[38] = { -1, 0, 1, -1, -1, 0, -1, -1 }; this->m_LabelsArray[39] = { 0, 0, 0, -1, -1, 0, -1, -1 }; - this->m_LabelsArray[40] = { -1, -1, -1, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[41] = { 0, -1, -1, 1, -1, 1, -1, -1 }; + this->m_LabelsArray[40] = { -1, -1, -1, 0, -1, 1, -1, -1 }; + this->m_LabelsArray[41] = { 0, -1, -1, 1, -1, 2, -1, -1 }; this->m_LabelsArray[42] = { -1, 0, -1, 0, -1, 0, -1, -1 }; this->m_LabelsArray[43] = { 0, 0, -1, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[44] = { -1, -1, 0, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[45] = { 0, -1, 0, 0, -1, 0, -1, -1 }; + this->m_LabelsArray[44] = { -1, -1, 0, 0, -1, 1, -1, -1 }; + this->m_LabelsArray[45] = { 0, -1, 0, 0, -1, 1, -1, -1 }; this->m_LabelsArray[46] = { -1, 0, 0, 0, -1, 0, -1, -1 }; this->m_LabelsArray[47] = { 0, 0, 0, 0, -1, 0, -1, -1 }; this->m_LabelsArray[48] = { -1, -1, -1, -1, 0, 0, -1, -1 }; this->m_LabelsArray[49] = { 0, -1, -1, -1, 0, 0, -1, -1 }; this->m_LabelsArray[50] = { -1, 0, -1, -1, 0, 0, -1, -1 }; this->m_LabelsArray[51] = { 0, 0, -1, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[52] = { -1, -1, 0, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[52] = { -1, -1, 0, -1, 1, 1, -1, -1 }; this->m_LabelsArray[53] = { 0, -1, 0, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[54] = { -1, 0, 0, -1, 0, 0, -1, -1 }; + this->m_LabelsArray[54] = { -1, 0, 1, -1, 0, 0, -1, -1 }; this->m_LabelsArray[55] = { 0, 0, 0, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[56] = { -1, -1, -1, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[57] = { 0, -1, -1, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[56] = { -1, -1, -1, 0, 1, 1, -1, -1 }; + this->m_LabelsArray[57] = { 0, -1, -1, 1, 0, 0, -1, -1 }; this->m_LabelsArray[58] = { -1, 0, -1, 0, 0, 0, -1, -1 }; this->m_LabelsArray[59] = { 0, 0, -1, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[60] = { -1, -1, 0, 0, 0, 0, -1, -1 }; + this->m_LabelsArray[60] = { -1, -1, 0, 0, 1, 1, -1, -1 }; this->m_LabelsArray[61] = { 0, -1, 0, 0, 0, 0, -1, -1 }; this->m_LabelsArray[62] = { -1, 0, 0, 0, 0, 0, -1, -1 }; this->m_LabelsArray[63] = { 0, 0, 0, 0, 0, 0, -1, -1 }; @@ -861,12 +868,12 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[101] = { 0, -1, 0, -1, -1, 1, 0, -1 }; this->m_LabelsArray[102] = { -1, 0, 1, -1, -1, 0, 1, -1 }; this->m_LabelsArray[103] = { 0, 0, 0, -1, -1, 0, 0, -1 }; - this->m_LabelsArray[104] = { -1, -1, -1, 0, -1, 0, 1, -1 }; - this->m_LabelsArray[105] = { 0, -1, -1, 1, -1, 1, 2, -1 }; + this->m_LabelsArray[104] = { -1, -1, -1, 0, -1, 1, 2, -1 }; + this->m_LabelsArray[105] = { 0, -1, -1, 1, -1, 2, 3, -1 }; this->m_LabelsArray[106] = { -1, 0, -1, 0, -1, 0, 1, -1 }; this->m_LabelsArray[107] = { 0, 0, -1, 0, -1, 0, 1, -1 }; - this->m_LabelsArray[108] = { -1, -1, 0, 0, -1, 0, 0, -1 }; - this->m_LabelsArray[109] = { 0, -1, 0, 0, -1, 0, 0, -1 }; + this->m_LabelsArray[108] = { -1, -1, 0, 0, -1, 1, 0, -1 }; + this->m_LabelsArray[109] = { 0, -1, 0, 0, -1, 1, 0, -1 }; this->m_LabelsArray[110] = { -1, 0, 0, 0, -1, 0, 0, -1 }; this->m_LabelsArray[111] = { 0, 0, 0, 0, -1, 0, 0, -1 }; this->m_LabelsArray[112] = { -1, -1, -1, -1, 0, 0, 0, -1 }; @@ -877,8 +884,8 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[117] = { 0, -1, 0, -1, 0, 0, 0, -1 }; this->m_LabelsArray[118] = { -1, 0, 0, -1, 0, 0, 0, -1 }; this->m_LabelsArray[119] = { 0, 0, 0, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[120] = { -1, -1, -1, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[121] = { 0, -1, -1, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[120] = { -1, -1, -1, 0, 1, 1, 1, -1 }; + this->m_LabelsArray[121] = { 0, -1, -1, 1, 0, 0, 0, -1 }; this->m_LabelsArray[122] = { -1, 0, -1, 0, 0, 0, 0, -1 }; this->m_LabelsArray[123] = { 0, 0, -1, 0, 0, 0, 0, -1 }; this->m_LabelsArray[124] = { -1, -1, 0, 0, 0, 0, 0, -1 }; @@ -905,17 +912,17 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[145] = { 0, -1, -1, -1, 0, -1, -1, 1 }; this->m_LabelsArray[146] = { -1, 0, -1, -1, 1, -1, -1, 2 }; this->m_LabelsArray[147] = { 0, 0, -1, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[148] = { -1, -1, 0, -1, 0, -1, -1, 1 }; + this->m_LabelsArray[148] = { -1, -1, 0, -1, 1, -1, -1, 2 }; this->m_LabelsArray[149] = { 0, -1, 0, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[150] = { -1, 0, 1, -1, 1, -1, -1, 2 }; + this->m_LabelsArray[150] = { -1, 0, 1, -1, 2, -1, -1, 3 }; this->m_LabelsArray[151] = { 0, 0, 0, -1, 0, -1, -1, 1 }; this->m_LabelsArray[152] = { -1, -1, -1, 0, 1, -1, -1, 0 }; this->m_LabelsArray[153] = { 0, -1, -1, 1, 0, -1, -1, 1 }; this->m_LabelsArray[154] = { -1, 0, -1, 0, 1, -1, -1, 0 }; this->m_LabelsArray[155] = { 0, 0, -1, 0, 0, -1, -1, 0 }; - this->m_LabelsArray[156] = { -1, -1, 0, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[156] = { -1, -1, 0, 0, 1, -1, -1, 0 }; this->m_LabelsArray[157] = { 0, -1, 0, 0, 0, -1, -1, 0 }; - this->m_LabelsArray[158] = { -1, 0, 0, 0, 0, -1, -1, 0 }; + this->m_LabelsArray[158] = { -1, 0, 0, 0, 1, -1, -1, 0 }; this->m_LabelsArray[159] = { 0, 0, 0, 0, 0, -1, -1, 0 }; this->m_LabelsArray[160] = { -1, -1, -1, -1, -1, 0, -1, 0 }; this->m_LabelsArray[161] = { 0, -1, -1, -1, -1, 1, -1, 1 }; @@ -937,9 +944,9 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[177] = { 0, -1, -1, -1, 0, 0, -1, 0 }; this->m_LabelsArray[178] = { -1, 0, -1, -1, 0, 0, -1, 0 }; this->m_LabelsArray[179] = { 0, 0, -1, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[180] = { -1, -1, 0, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[180] = { -1, -1, 0, -1, 1, 1, -1, 1 }; this->m_LabelsArray[181] = { 0, -1, 0, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[182] = { -1, 0, 0, -1, 0, 0, -1, 0 }; + this->m_LabelsArray[182] = { -1, 0, 1, -1, 0, 0, -1, 0 }; this->m_LabelsArray[183] = { 0, 0, 0, -1, 0, 0, -1, 0 }; this->m_LabelsArray[184] = { -1, -1, -1, 0, 0, 0, -1, 0 }; this->m_LabelsArray[185] = { 0, -1, -1, 0, 0, 0, -1, 0 }; diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index b9b91b3b76f..1dac7e887ea 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -62,7 +62,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob3.mha ${ITK_TEST_OUTPUT_DIR}/blob3-01.vtk 200 # Iso-surface value - 216 # Expected number of points + 240 # Expected number of points 180 # Expected number of cells 0 # Generate triangle faces 0 # Project vertices to iso-surface @@ -167,7 +167,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha ${ITK_TEST_OUTPUT_DIR}/neghip-01.vtk 55 # Iso-surface value - 15162 # Expected number of points + 15164 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces 0 # Project vertices to iso-surface @@ -182,7 +182,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha ${ITK_TEST_OUTPUT_DIR}/neghip-02.vtk 55 # Iso-surface value - 15162 # Expected number of points + 15164 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces 1 # Project vertices to iso-surface @@ -197,7 +197,7 @@ add_test( COMMAND CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha ${ITK_TEST_OUTPUT_DIR}/neghip-03.vtk 55 # Iso-surface value - 15162 # Expected number of points + 15164 # Expected number of points 30272 # Expected number of cells 1 # Generate triangle faces 1 # Project vertices to iso-surface diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 25ed6457a1f..2c8095d0f70 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -17,7 +17,7 @@ *=========================================================================*/ #define USE_BSPLINE_INTERPOLATOR 0 #define USE_MARCHING_CUBES 0 -#define USE_QUAD_EDGE_MESH 0 +#define USE_QUAD_EDGE_MESH 1 #define USE_DECIMATION 0 #include "itkTimeProbe.h" From 82c8430e67a0e1b04e9436f1e5d7f51b22564f64 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 17 Feb 2020 14:30:03 -0600 Subject: [PATCH 57/84] STYLE: Prefer = default to explicitly trivial implementations This check replaces default bodies of special member functions with = default;. The explicitly defaulted function declarations enable more opportunities in optimization, because the compiler might treat explicitly defaulted functions as trivial. Additionally, the C++11 use of = default more clearly expreses the intent for the special member functions. --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index bafd83ad07e..085e5ad7bf5 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -327,7 +327,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter; /** Constructors */ - VertexLookupMap() {} + VertexLookupMap() = default; /** Clear the lookup map. */ void From 7485979b03adddfa9ea7c44b044a583d57efdba7 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 17 Feb 2020 17:52:19 -0600 Subject: [PATCH 58/84] STYLE: Use default member initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Converts a default constructor’s member initializers into the new default member initializers in C++11. Other member initializers that match the default member initializer are removed. This can reduce repeated code or allow use of ‘= default’. --- .../include/itkCuberilleImageToMeshFilter.h | 23 ++++++++----------- .../include/itkCuberilleImageToMeshFilter.hxx | 8 +------ 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 085e5ad7bf5..e424f03c292 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -268,10 +268,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter CuberilleImageToMeshFilter::CuberilleImageToMeshFilter() : m_IsoSurfaceValue(NumericTraits::One) , m_MaxSpacing(NumericTraits::One) - , m_GenerateTriangleFaces(true) - , m_ProjectVerticesToIsoSurface(true) - , m_SavePixelAsCellData(false) - , m_ProjectVertexSurfaceDistanceThreshold(0.5) - , m_ProjectVertexStepLength(-1.0) - , m_ProjectVertexStepLengthRelaxationFactor(0.95) - , m_ProjectVertexMaximumNumberOfSteps(50) + { this->SetNumberOfRequiredInputs(1); this->CalculateLabelsArray(); From c9e77140fbd7e04053dd6b14ec595baeee023792 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 17 Feb 2020 18:09:39 -0600 Subject: [PATCH 59/84] STYLE: Prefer = default to explicitly trivial implementations This check replaces default bodies of special member functions with = default;. The explicitly defaulted function declarations enable more opportunities in optimization, because the compiler might treat explicitly defaulted functions as trivial. Additionally, the C++11 use of = default more clearly expreses the intent for the special member functions. --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index e424f03c292..9b3e023db68 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -268,7 +268,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Wed, 19 Feb 2020 10:45:25 -0600 Subject: [PATCH 60/84] DOC: Update copyright assignment to NumFOCUS The mission of NumFOCUS is to promote open practices in research, data, and scientific computing. https://numfocus.org --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 +- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 2 +- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- Modules/Filtering/Cuberille/test/CuberilleTest02.cxx | 2 +- Modules/Filtering/Cuberille/test/CuberilleTest03.cxx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 9b3e023db68..56571bdcb50 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -1,6 +1,6 @@ /*========================================================================= * - * Copyright Insight Software Consortium + * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index de4f14df7bb..42fdc9c2a37 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -1,6 +1,6 @@ /*========================================================================= * - * Copyright Insight Software Consortium + * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 2c8095d0f70..4b0511b2e38 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -1,6 +1,6 @@ /*========================================================================= * - * Copyright Insight Software Consortium + * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx index 0ec32e932f3..45584d66dc6 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx @@ -1,6 +1,6 @@ /*========================================================================= * - * Copyright Insight Software Consortium + * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx index 08655ff6b13..dacbdb286e9 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx @@ -1,6 +1,6 @@ /*========================================================================= * - * Copyright Insight Software Consortium + * Copyright NumFOCUS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 7c7f61c7cc16ec0373d9e7b83dd851cfdf959865 Mon Sep 17 00:00:00 2001 From: Mathew Seng Date: Tue, 18 Feb 2020 07:18:11 -0600 Subject: [PATCH 61/84] BUG: Fix failing kwstyle test. Header mismatch. --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 1 + .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 1 + Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 1 + 3 files changed, 3 insertions(+) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 56571bdcb50..6219935044d 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -15,6 +15,7 @@ * limitations under the License. * *=========================================================================*/ + #ifndef itkCuberilleImageToMeshFilter_h #define itkCuberilleImageToMeshFilter_h diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 42fdc9c2a37..ec7a6695d96 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -15,6 +15,7 @@ * limitations under the License. * *=========================================================================*/ + #ifndef itkCuberilleImageToMeshFilter_hxx #define itkCuberilleImageToMeshFilter_hxx diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 4b0511b2e38..4e93c441c5c 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -15,6 +15,7 @@ * limitations under the License. * *=========================================================================*/ + #define USE_BSPLINE_INTERPOLATOR 0 #define USE_MARCHING_CUBES 0 #define USE_QUAD_EDGE_MESH 1 From bcaacc8f69c91b72594bf599de7293802d7c9325 Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Sat, 29 Feb 2020 21:45:31 -0600 Subject: [PATCH 62/84] ENH: Make similar to template from ITKModuleTemplate --- Modules/Filtering/Cuberille/itk-module.cmake | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake index 7c90959498a..25ea607f28e 100644 --- a/Modules/Filtering/Cuberille/itk-module.cmake +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -31,6 +31,7 @@ itk_module( ITKQuadEdgeMeshFiltering ITKThresholding ITKIOImageBase - EXCLUDE_FROM_DEFAULT DESCRIPTION "${DOCUMENTATION}" + EXCLUDE_FROM_DEFAULT + ENABLE_SHARED ) From 74599bd0ba5857513d87d44a3beb4bd22cae6fec Mon Sep 17 00:00:00 2001 From: Davis Vigneault Date: Sun, 24 May 2020 23:37:47 -0700 Subject: [PATCH 63/84] ENH: Improve Code Coverage This patch templatizes the primary test over the image and mesh type, so that it can be run on both Mesh and QuadEdgeMesh. Additionally, ITK_TEST_SET_GET_BOOLEAN is preferred over ITK_TEST_SET_GET_VALUE where appropriate. Finally, consistent pixel and coordinate types are used throughout. Line coverage is 99.8%, unchanged from prior to the patch. Function coverage is 98.0%, improved from 71.4% prior to the patch. --- .../Cuberille/test/CuberilleTest01.cxx | 83 ++++++++++++------- .../Cuberille/test/CuberilleTest02.cxx | 3 +- .../Cuberille/test/CuberilleTest03.cxx | 3 +- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 4e93c441c5c..9f85425d74d 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -18,7 +18,6 @@ #define USE_BSPLINE_INTERPOLATOR 0 #define USE_MARCHING_CUBES 0 -#define USE_QUAD_EDGE_MESH 1 #define USE_DECIMATION 0 #include "itkTimeProbe.h" @@ -37,10 +36,11 @@ #include "itkQuadEdgeMeshDecimationCriteria.h" #include "itkTestingMacros.h" - +template int -CuberilleTest01(int argc, char * argv[]) +CuberilleTest01Helper(int argc, char * argv[]) { + if (argc < 6) { std::cout << "Usage: " << argv[0]; @@ -51,17 +51,7 @@ CuberilleTest01(int argc, char * argv[]) return EXIT_FAILURE; } - // Typedefs - constexpr unsigned int Dimension = 3; - using PixelType = unsigned char; - // using PixelType = signed short; - using ImageType = itk::Image; - -#if USE_QUAD_EDGE_MESH | USE_DECIMATION - using MeshType = itk::QuadEdgeMesh; -#else - using MeshType = itk::Mesh; -#endif + using PixelType = typename ImageType::PixelType; using ImageFileReaderType = itk::ImageFileReader; using MeshFileWriterType = itk::VTKPolyDataWriter; #if USE_BSPLINE_INTERPOLATOR @@ -117,21 +107,21 @@ CuberilleTest01(int argc, char * argv[]) } // Read input image - ImageFileReaderType::Pointer reader = ImageFileReaderType::New(); + const auto reader = ImageFileReaderType::New(); reader->SetFileName(filenameInputImage); ITK_TRY_EXPECT_NO_EXCEPTION(reader->UpdateLargestPossibleRegion()); - ImageType::Pointer input = reader->GetOutput(); + typename ImageType::Pointer input = reader->GetOutput(); input->DisconnectPipeline(); // Create output mesh - MeshType::Pointer outputMesh = nullptr; - itk::TimeProbe time; + typename MeshType::Pointer outputMesh = nullptr; + itk::TimeProbe time; #if USE_MARCHING_CUBES // Create marching cubes mesh - BinaryThresholdFilterType::Pointer threshold = BinaryThresholdFilterType::New(); + const auto threshold = BinaryThresholdFilterType::New(); threshold->SetInput(input); threshold->SetLowerThreshold(IsoSurfaceValue); threshold->SetUpperThreshold(itk::NumericTraits::max()); @@ -139,7 +129,7 @@ CuberilleTest01(int argc, char * argv[]) threshold->SetOutsideValue(itk::NumericTraits::Zero); threshold->UpdateLargestPossibleRegion(); - MarchingCubesType::Pointer marching = MarchingCubesType::New(); + const auto marching = MarchingCubesType::New(); marching->SetInput(threshold->GetOutput()); time.Start(); @@ -153,9 +143,7 @@ CuberilleTest01(int argc, char * argv[]) #else // Create cuberille mesh filter - CuberilleType::Pointer cuberille = CuberilleType::New(); - - ITK_EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); + const auto cuberille = CuberilleType::New(); // How long does it take to pre-calculate the array labels array? @@ -172,7 +160,7 @@ CuberilleTest01(int argc, char * argv[]) cuberille->SetIsoSurfaceValue(isoSurfaceValue); ITK_TEST_SET_GET_VALUE(isoSurfaceValue, cuberille->GetIsoSurfaceValue()); - InterpolatorType::Pointer interpolator = InterpolatorType::New(); + const auto interpolator = InterpolatorType::New(); # if USE_BSPLINE_INTERPOLATOR unsigned int splineOrder = 3; interpolator->SetSplineOrder(splineOrder); @@ -181,10 +169,10 @@ CuberilleTest01(int argc, char * argv[]) ITK_TEST_SET_GET_VALUE(interpolator, cuberille->GetInterpolator()); cuberille->SetGenerateTriangleFaces(generateTriangleFaces); - ITK_TEST_SET_GET_VALUE(generateTriangleFaces, cuberille->GetGenerateTriangleFaces()); + ITK_TEST_SET_GET_BOOLEAN(cuberille, GenerateTriangleFaces, generateTriangleFaces); cuberille->SetProjectVerticesToIsoSurface(projectToIsoSurface); - ITK_TEST_SET_GET_VALUE(projectToIsoSurface, cuberille->GetProjectVerticesToIsoSurface()); + ITK_TEST_SET_GET_BOOLEAN(cuberille, ProjectVerticesToIsoSurface, projectToIsoSurface); cuberille->SetProjectVertexSurfaceDistanceThreshold(surfaceDistanceThreshold); ITK_TEST_SET_GET_VALUE(surfaceDistanceThreshold, cuberille->GetProjectVertexSurfaceDistanceThreshold()); @@ -198,6 +186,8 @@ CuberilleTest01(int argc, char * argv[]) cuberille->SetProjectVertexMaximumNumberOfSteps(maximumNumberOfSteps); ITK_TEST_SET_GET_VALUE(maximumNumberOfSteps, cuberille->GetProjectVertexMaximumNumberOfSteps()); + ITK_TEST_SET_GET_BOOLEAN(cuberille, SavePixelAsCellData, false); + time.Start(); ITK_TRY_EXPECT_NO_EXCEPTION(cuberille->Update()); @@ -213,12 +203,12 @@ CuberilleTest01(int argc, char * argv[]) #if USE_DECIMATION // Decimation using DecimationCriterionType = itk::NumberOfFacesCriterion; - DecimationCriterionType::Pointer decimateCriterion = DecimationCriterionType::New(); + const auto decimateCriterion = DecimationCriterionType::New(); decimateCriterion->SetTopologicalChange(false); decimateCriterion->SetNumberOfElements(2000); using DecimationType = itk::QuadricDecimationQuadEdgeMeshFilter; - DecimationType::Pointer decimate = DecimationType::New(); + const auto decimate = DecimationType::New(); decimate->SetCriterion(decimateCriterion); decimate->SetInput(outputMesh); @@ -227,7 +217,7 @@ CuberilleTest01(int argc, char * argv[]) #endif // Write mesh - MeshFileWriterType::Pointer writer = MeshFileWriterType::New(); + const auto writer = MeshFileWriterType::New(); #if USE_DECIMATION writer->SetInput(decimate->GetOutput()); #else @@ -259,3 +249,38 @@ CuberilleTest01(int argc, char * argv[]) std::cout << "Test finished" << std::endl; return EXIT_SUCCESS; } + +int +CuberilleTest01(int argc, char * argv[]) +{ + + constexpr unsigned int Dimension = 3; + using PixelType = unsigned char; + using CoordinateType = double; + using QEMeshType = itk::QuadEdgeMesh; + using MeshType = itk::Mesh; + using ImageType = itk::Image; + + { + using CuberilleType = itk::CuberilleImageToMeshFilter; + const auto cuberille = CuberilleType::New(); + ITK_EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); + } + + { + using CuberilleType = itk::CuberilleImageToMeshFilter; + const auto cuberille = CuberilleType::New(); + ITK_EXERCISE_BASIC_OBJECT_METHODS(cuberille, CuberilleImageToMeshFilter, ImageToMeshFilter); + } + + if (EXIT_FAILURE == CuberilleTest01Helper(argc, argv)) + { + return EXIT_FAILURE; + } + if (EXIT_FAILURE == CuberilleTest01Helper(argc, argv)) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx index 45584d66dc6..c5566aab0a7 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx @@ -28,8 +28,9 @@ const unsigned int Dimension = 3; using TPixel = unsigned char; +using TCoordinate = double; using TImage = itk::Image; -using TMesh = itk::Mesh; +using TMesh = itk::Mesh; using TImageToMesh = itk::CuberilleImageToMeshFilter; using TInterp = itk::NearestNeighborInterpolateImageFunction; diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx index dacbdb286e9..38a7ce78476 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx @@ -29,8 +29,9 @@ const unsigned int Dimension = 3; using TPixel = unsigned char; +using TCoordinate = double; using TImage = itk::Image; -using TMesh = itk::Mesh; +using TMesh = itk::Mesh; using TImageToMesh = itk::CuberilleImageToMeshFilter; using TInterp = itk::NearestNeighborInterpolateImageFunction; using TTriangleHelper = itk::TriangleHelper; From 8b7fbf4547b13936e13a41dd361fc15b05f89b7b Mon Sep 17 00:00:00 2001 From: Davis Vigneault Date: Sat, 30 May 2020 00:13:55 -0700 Subject: [PATCH 64/84] BUG: Handle Non-Manifold Geometry itk::QuadEdgeMesh enforces manifold geometry, and therefore cases where vertices appear at the same physical location must be handled carefully. A bugfix was recently applied which handles the vast majority (252/256) of cases. However, in the method fails in the four possible cases where two background pixels appear at opposite corners of a 2x2x2 region, with foreground pixels elsewhere. This patch handles these four cases manually. Testing and documentation are updated as appropriate. --- .../include/itkCuberilleImageToMeshFilter.hxx | 51 ++++++---- .../Filtering/Cuberille/test/CMakeLists.txt | 8 ++ .../Cuberille/test/CuberilleTest04.cxx | 97 +++++++++++++++++++ 3 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 Modules/Filtering/Cuberille/test/CuberilleTest04.cxx diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index ec7a6695d96..1d6f5da2431 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -682,15 +682,22 @@ CuberilleImageToMeshFilter::CalculateLa { // The commented code below iterates through all possible binary 2x2x2 regions, - // runs connected components on the result. The total number of foreground + // and runs connected components on the result. The total number of foreground // connected components is equal to the number of vertices which must be // replicated--these vertices are stored in the VertexMap. One less than the // number of the connected component is equal to the index of the corresponding - // vertex in the VertexMap. While it would be easily possible to generate this at - // runtime, it could theoretically cause a significant performance hit in a - // case where many instances of this class must be instantiated. Therefore, - // the values are hardcoded below. The commented code was used to generate - // the hardcoded values. + // vertex in the VertexMap. + // + // This method works for all but four of the 256 possible cases. The failing + // cases occur when there are two background pixels at opposite corners of the + // 2x2x2 region, and all remaining pixels are foreground. In these cases, + // there is only one connected component, but two vertices must be added. These + // cases are handled separately. + // + // While it would be possible to generate this at runtime, it could theoretically + // cause a significant performance hit in a case where many instances of this class + // must be instantiated. Therefore, the values are hardcoded below. The commented + // code was used to generate the hardcoded values. // // NOTE: There was previously a bug in itk::ConnectedComponentImageFilter which // caused the hardcoded values below to be incorrect, though this has since been @@ -702,7 +709,7 @@ CuberilleImageToMeshFilter::CalculateLa // using TImage = itk::Image; // using TConnected = itk::ConnectedComponentImageFilter< TImage, TImage >; // - // for (size_t mask = 0; mask < pow(2, 8); ++mask) { + // for (size_t mask = 0; mask < std::pow(2, 8); ++mask) { // // std::bitset<8> bitmask(mask); // @@ -719,7 +726,7 @@ CuberilleImageToMeshFilter::CalculateLa // image->Allocate(); // image->FillBuffer( 0 ); // - // for (size_t index = 0; index < pow(2, 3); ++index) { + // for (size_t index = 0; index < std::pow(2, 3); ++index) { // std::bitset<3> bitindex(index); // image->SetPixel( {{bitindex[0], bitindex[1], bitindex[2]}}, bitmask[index] ); // } @@ -731,7 +738,7 @@ CuberilleImageToMeshFilter::CalculateLa // // TLabels labels; // - // for (size_t index = 0; index < pow(2, 3); ++index) { + // for (size_t index = 0; index < std::pow(2, 3); ++index) { // std::bitset<3> bitindex(index); // const auto component = connected->GetOutput()->GetPixel({{ // bitindex[0], @@ -745,18 +752,22 @@ CuberilleImageToMeshFilter::CalculateLa // // } // - // for (size_t i = 0; i < pow(2,8); ++i) { + // // Manually handle the corner cases, discussed above. + // + // this->m_LabelsArray[126] = { -1, 0, 0, 1, 0, 1, 1, -1 }; + // this->m_LabelsArray[189] = { 0, -1, 1, 0, 1, 0, -1, 1 }; + // this->m_LabelsArray[219] = { 0, 1, -1, 0, 1, -1, 0, 1 }; + // this->m_LabelsArray[231] = { 1, 0, 0, -1, -1, 1, 1, 0 }; + // + // for (size_t i = 0; i < std::pow(2,8); ++i) { // std::cout << "this->m_LabelsArray[" << i << "] "; - // if (i < 10) std::cout << ' '; - // if (i < 100) std::cout << ' '; // std::cout << "= {"; - // for (size_t j = 0; j < pow(2,3); ++j) { + // for (size_t j = 0; j < std::pow(2,3); ++j) { // const auto k = static_cast(this->m_LabelsArray[i][j]); - // if (k >= 0) std::cout << ' '; - // std::cout << k; + // std::cout << ' ' << k; // if (j != 7) std::cout << ','; // } - // std::cout << "};\n"; + // std::cout << " };\n"; // } this->m_LabelsArray[0] = { -1, -1, -1, -1, -1, -1, -1, -1 }; @@ -885,7 +896,7 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[123] = { 0, 0, -1, 0, 0, 0, 0, -1 }; this->m_LabelsArray[124] = { -1, -1, 0, 0, 0, 0, 0, -1 }; this->m_LabelsArray[125] = { 0, -1, 0, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[126] = { -1, 0, 0, 0, 0, 0, 0, -1 }; + this->m_LabelsArray[126] = { -1, 0, 0, 1, 0, 1, 1, -1 }; this->m_LabelsArray[127] = { 0, 0, 0, 0, 0, 0, 0, -1 }; this->m_LabelsArray[128] = { -1, -1, -1, -1, -1, -1, -1, 0 }; this->m_LabelsArray[129] = { 0, -1, -1, -1, -1, -1, -1, 1 }; @@ -948,7 +959,7 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[186] = { -1, 0, -1, 0, 0, 0, -1, 0 }; this->m_LabelsArray[187] = { 0, 0, -1, 0, 0, 0, -1, 0 }; this->m_LabelsArray[188] = { -1, -1, 0, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[189] = { 0, -1, 0, 0, 0, 0, -1, 0 }; + this->m_LabelsArray[189] = { 0, -1, 1, 0, 1, 0, -1, 1 }; this->m_LabelsArray[190] = { -1, 0, 0, 0, 0, 0, -1, 0 }; this->m_LabelsArray[191] = { 0, 0, 0, 0, 0, 0, -1, 0 }; this->m_LabelsArray[192] = { -1, -1, -1, -1, -1, -1, 0, 0 }; @@ -978,7 +989,7 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[216] = { -1, -1, -1, 0, 0, -1, 0, 0 }; this->m_LabelsArray[217] = { 0, -1, -1, 0, 0, -1, 0, 0 }; this->m_LabelsArray[218] = { -1, 0, -1, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[219] = { 0, 0, -1, 0, 0, -1, 0, 0 }; + this->m_LabelsArray[219] = { 0, 1, -1, 0, 1, -1, 0, 1 }; this->m_LabelsArray[220] = { -1, -1, 0, 0, 0, -1, 0, 0 }; this->m_LabelsArray[221] = { 0, -1, 0, 0, 0, -1, 0, 0 }; this->m_LabelsArray[222] = { -1, 0, 0, 0, 0, -1, 0, 0 }; @@ -990,7 +1001,7 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[228] = { -1, -1, 0, -1, -1, 0, 0, 0 }; this->m_LabelsArray[229] = { 0, -1, 0, -1, -1, 0, 0, 0 }; this->m_LabelsArray[230] = { -1, 0, 0, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[231] = { 0, 0, 0, -1, -1, 0, 0, 0 }; + this->m_LabelsArray[231] = { 1, 0, 0, -1, -1, 1, 1, 0 }; this->m_LabelsArray[232] = { -1, -1, -1, 0, -1, 0, 0, 0 }; this->m_LabelsArray[233] = { 0, -1, -1, 1, -1, 1, 1, 1 }; this->m_LabelsArray[234] = { -1, 0, -1, 0, -1, 0, 0, 0 }; diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index 1dac7e887ea..865adeeaf85 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -7,6 +7,7 @@ set( CuberilleTest01.cxx CuberilleTest02.cxx CuberilleTest03.cxx + CuberilleTest04.cxx ) createtestdriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}") @@ -345,3 +346,10 @@ itk_add_test( ${itk-module}TestDriver CuberilleTest03 ) + +itk_add_test( + NAME CuberilleTestNonManifoldGeometry + COMMAND + ${itk-module}TestDriver + CuberilleTest04 +) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx new file mode 100644 index 00000000000..64c392f1e78 --- /dev/null +++ b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx @@ -0,0 +1,97 @@ +/*========================================================================= + * + * 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. + * + *=========================================================================*/ + +#include +#include +#include +#include +#include + +// In the case where a 2x2x2 region contains two background pixels at +// opposite corners, and foreground pixels elsewhere, it is necessary +// that there be two vertices at the center. Previously, this was +// handled incorrectly with a single vertex, causing non-manifold +// geometry. This test covers the four cases in which this occurs. + +int +CuberilleTest04(int itkNotUsed(argc), char * itkNotUsed(argv)[]) +{ + + using TPixel = unsigned char; + using TCoordinate = double; + const unsigned int Dimension = 3; + using TImage = itk::Image; + using TMesh = itk::QuadEdgeMesh; + using TPad = itk::ConstantPadImageFilter; + using TExtract = itk::CuberilleImageToMeshFilter; + + // 126 01111110 + // 189 10111101 + // 219 11011011 + // 231 11100111 + + std::array masks{ 126, 189, 219, 231 }; + + for (const auto & mask : masks) + { + + std::bitset<8> bitmask(mask); + + std::cout << mask << ' ' << bitmask << std::endl; + + const auto image = TImage::New(); + TImage::SizeType size; + size.Fill(2); + + TImage::IndexType origin; + origin.Fill(0); + + TImage::RegionType region(origin, size); + + image->SetRegions(region); + image->Allocate(); + image->FillBuffer(0); + + for (size_t index = 0; index < std::pow(2, 3); ++index) + { + std::bitset<3> bitindex(index); + image->SetPixel({ { bitindex[0], bitindex[1], bitindex[2] } }, bitmask[index]); + } + + typename TImage::SizeType padding; + padding.Fill(1); + + const auto pad = TPad::New(); + pad->SetInput(image); + pad->SetPadUpperBound(padding); + pad->SetPadLowerBound(padding); + pad->SetConstant(static_cast(0)); + + const auto extract = TExtract::New(); + extract->SetInput(pad->GetOutput()); + extract->GenerateTriangleFacesOn(); + extract->ProjectVerticesToIsoSurfaceOff(); + extract->SavePixelAsCellDataOn(); + extract->Update(); + + ITK_TEST_EXPECT_EQUAL(extract->GetOutput()->GetNumberOfPoints(), 26); + ITK_TEST_EXPECT_EQUAL(extract->GetOutput()->GetNumberOfCells(), 48); + } + + return EXIT_SUCCESS; +} From 22fa64cb4137bcc75f98c780dd5c479a8c0a8b1f Mon Sep 17 00:00:00 2001 From: Davis Vigneault Date: Mon, 15 Jun 2020 16:42:26 -0700 Subject: [PATCH 65/84] COMP: Address Wmissing-braces Warnings Address warnings of the form: warning: suggest braces around initialization of subobject [-Wmissing-braces] --- .../include/itkCuberilleImageToMeshFilter.hxx | 524 +++++++++--------- .../Cuberille/test/CuberilleTest04.cxx | 2 +- 2 files changed, 263 insertions(+), 263 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 1d6f5da2431..27f1af4ac76 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -754,278 +754,278 @@ CuberilleImageToMeshFilter::CalculateLa // // // Manually handle the corner cases, discussed above. // - // this->m_LabelsArray[126] = { -1, 0, 0, 1, 0, 1, 1, -1 }; - // this->m_LabelsArray[189] = { 0, -1, 1, 0, 1, 0, -1, 1 }; - // this->m_LabelsArray[219] = { 0, 1, -1, 0, 1, -1, 0, 1 }; - // this->m_LabelsArray[231] = { 1, 0, 0, -1, -1, 1, 1, 0 }; + // this->m_LabelsArray[126] = { { -1, 0, 0, 1, 0, 1, 1, -1 } }; + // this->m_LabelsArray[189] = { { 0, -1, 1, 0, 1, 0, -1, 1 } }; + // this->m_LabelsArray[219] = { { 0, 1, -1, 0, 1, -1, 0, 1 } }; + // this->m_LabelsArray[231] = { { 1, 0, 0, -1, -1, 1, 1, 0 } }; // // for (size_t i = 0; i < std::pow(2,8); ++i) { // std::cout << "this->m_LabelsArray[" << i << "] "; - // std::cout << "= {"; + // std::cout << "= { {"; // for (size_t j = 0; j < std::pow(2,3); ++j) { // const auto k = static_cast(this->m_LabelsArray[i][j]); // std::cout << ' ' << k; // if (j != 7) std::cout << ','; // } - // std::cout << " };\n"; + // std::cout << " } };\n"; // } - this->m_LabelsArray[0] = { -1, -1, -1, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[1] = { 0, -1, -1, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[2] = { -1, 0, -1, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[3] = { 0, 0, -1, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[4] = { -1, -1, 0, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[5] = { 0, -1, 0, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[6] = { -1, 0, 1, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[7] = { 0, 0, 0, -1, -1, -1, -1, -1 }; - this->m_LabelsArray[8] = { -1, -1, -1, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[9] = { 0, -1, -1, 1, -1, -1, -1, -1 }; - this->m_LabelsArray[10] = { -1, 0, -1, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[11] = { 0, 0, -1, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[12] = { -1, -1, 0, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[13] = { 0, -1, 0, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[14] = { -1, 0, 0, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[15] = { 0, 0, 0, 0, -1, -1, -1, -1 }; - this->m_LabelsArray[16] = { -1, -1, -1, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[17] = { 0, -1, -1, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[18] = { -1, 0, -1, -1, 1, -1, -1, -1 }; - this->m_LabelsArray[19] = { 0, 0, -1, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[20] = { -1, -1, 0, -1, 1, -1, -1, -1 }; - this->m_LabelsArray[21] = { 0, -1, 0, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[22] = { -1, 0, 1, -1, 2, -1, -1, -1 }; - this->m_LabelsArray[23] = { 0, 0, 0, -1, 0, -1, -1, -1 }; - this->m_LabelsArray[24] = { -1, -1, -1, 0, 1, -1, -1, -1 }; - this->m_LabelsArray[25] = { 0, -1, -1, 1, 0, -1, -1, -1 }; - this->m_LabelsArray[26] = { -1, 0, -1, 0, 1, -1, -1, -1 }; - this->m_LabelsArray[27] = { 0, 0, -1, 0, 0, -1, -1, -1 }; - this->m_LabelsArray[28] = { -1, -1, 0, 0, 1, -1, -1, -1 }; - this->m_LabelsArray[29] = { 0, -1, 0, 0, 0, -1, -1, -1 }; - this->m_LabelsArray[30] = { -1, 0, 0, 0, 1, -1, -1, -1 }; - this->m_LabelsArray[31] = { 0, 0, 0, 0, 0, -1, -1, -1 }; - this->m_LabelsArray[32] = { -1, -1, -1, -1, -1, 0, -1, -1 }; - this->m_LabelsArray[33] = { 0, -1, -1, -1, -1, 1, -1, -1 }; - this->m_LabelsArray[34] = { -1, 0, -1, -1, -1, 0, -1, -1 }; - this->m_LabelsArray[35] = { 0, 0, -1, -1, -1, 0, -1, -1 }; - this->m_LabelsArray[36] = { -1, -1, 0, -1, -1, 1, -1, -1 }; - this->m_LabelsArray[37] = { 0, -1, 0, -1, -1, 1, -1, -1 }; - this->m_LabelsArray[38] = { -1, 0, 1, -1, -1, 0, -1, -1 }; - this->m_LabelsArray[39] = { 0, 0, 0, -1, -1, 0, -1, -1 }; - this->m_LabelsArray[40] = { -1, -1, -1, 0, -1, 1, -1, -1 }; - this->m_LabelsArray[41] = { 0, -1, -1, 1, -1, 2, -1, -1 }; - this->m_LabelsArray[42] = { -1, 0, -1, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[43] = { 0, 0, -1, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[44] = { -1, -1, 0, 0, -1, 1, -1, -1 }; - this->m_LabelsArray[45] = { 0, -1, 0, 0, -1, 1, -1, -1 }; - this->m_LabelsArray[46] = { -1, 0, 0, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[47] = { 0, 0, 0, 0, -1, 0, -1, -1 }; - this->m_LabelsArray[48] = { -1, -1, -1, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[49] = { 0, -1, -1, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[50] = { -1, 0, -1, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[51] = { 0, 0, -1, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[52] = { -1, -1, 0, -1, 1, 1, -1, -1 }; - this->m_LabelsArray[53] = { 0, -1, 0, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[54] = { -1, 0, 1, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[55] = { 0, 0, 0, -1, 0, 0, -1, -1 }; - this->m_LabelsArray[56] = { -1, -1, -1, 0, 1, 1, -1, -1 }; - this->m_LabelsArray[57] = { 0, -1, -1, 1, 0, 0, -1, -1 }; - this->m_LabelsArray[58] = { -1, 0, -1, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[59] = { 0, 0, -1, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[60] = { -1, -1, 0, 0, 1, 1, -1, -1 }; - this->m_LabelsArray[61] = { 0, -1, 0, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[62] = { -1, 0, 0, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[63] = { 0, 0, 0, 0, 0, 0, -1, -1 }; - this->m_LabelsArray[64] = { -1, -1, -1, -1, -1, -1, 0, -1 }; - this->m_LabelsArray[65] = { 0, -1, -1, -1, -1, -1, 1, -1 }; - this->m_LabelsArray[66] = { -1, 0, -1, -1, -1, -1, 1, -1 }; - this->m_LabelsArray[67] = { 0, 0, -1, -1, -1, -1, 1, -1 }; - this->m_LabelsArray[68] = { -1, -1, 0, -1, -1, -1, 0, -1 }; - this->m_LabelsArray[69] = { 0, -1, 0, -1, -1, -1, 0, -1 }; - this->m_LabelsArray[70] = { -1, 0, 1, -1, -1, -1, 1, -1 }; - this->m_LabelsArray[71] = { 0, 0, 0, -1, -1, -1, 0, -1 }; - this->m_LabelsArray[72] = { -1, -1, -1, 0, -1, -1, 1, -1 }; - this->m_LabelsArray[73] = { 0, -1, -1, 1, -1, -1, 2, -1 }; - this->m_LabelsArray[74] = { -1, 0, -1, 0, -1, -1, 1, -1 }; - this->m_LabelsArray[75] = { 0, 0, -1, 0, -1, -1, 1, -1 }; - this->m_LabelsArray[76] = { -1, -1, 0, 0, -1, -1, 0, -1 }; - this->m_LabelsArray[77] = { 0, -1, 0, 0, -1, -1, 0, -1 }; - this->m_LabelsArray[78] = { -1, 0, 0, 0, -1, -1, 0, -1 }; - this->m_LabelsArray[79] = { 0, 0, 0, 0, -1, -1, 0, -1 }; - this->m_LabelsArray[80] = { -1, -1, -1, -1, 0, -1, 0, -1 }; - this->m_LabelsArray[81] = { 0, -1, -1, -1, 0, -1, 0, -1 }; - this->m_LabelsArray[82] = { -1, 0, -1, -1, 1, -1, 1, -1 }; - this->m_LabelsArray[83] = { 0, 0, -1, -1, 0, -1, 0, -1 }; - this->m_LabelsArray[84] = { -1, -1, 0, -1, 0, -1, 0, -1 }; - this->m_LabelsArray[85] = { 0, -1, 0, -1, 0, -1, 0, -1 }; - this->m_LabelsArray[86] = { -1, 0, 1, -1, 1, -1, 1, -1 }; - this->m_LabelsArray[87] = { 0, 0, 0, -1, 0, -1, 0, -1 }; - this->m_LabelsArray[88] = { -1, -1, -1, 0, 1, -1, 1, -1 }; - this->m_LabelsArray[89] = { 0, -1, -1, 1, 0, -1, 0, -1 }; - this->m_LabelsArray[90] = { -1, 0, -1, 0, 1, -1, 1, -1 }; - this->m_LabelsArray[91] = { 0, 0, -1, 0, 0, -1, 0, -1 }; - this->m_LabelsArray[92] = { -1, -1, 0, 0, 0, -1, 0, -1 }; - this->m_LabelsArray[93] = { 0, -1, 0, 0, 0, -1, 0, -1 }; - this->m_LabelsArray[94] = { -1, 0, 0, 0, 0, -1, 0, -1 }; - this->m_LabelsArray[95] = { 0, 0, 0, 0, 0, -1, 0, -1 }; - this->m_LabelsArray[96] = { -1, -1, -1, -1, -1, 0, 1, -1 }; - this->m_LabelsArray[97] = { 0, -1, -1, -1, -1, 1, 2, -1 }; - this->m_LabelsArray[98] = { -1, 0, -1, -1, -1, 0, 1, -1 }; - this->m_LabelsArray[99] = { 0, 0, -1, -1, -1, 0, 1, -1 }; - this->m_LabelsArray[100] = { -1, -1, 0, -1, -1, 1, 0, -1 }; - this->m_LabelsArray[101] = { 0, -1, 0, -1, -1, 1, 0, -1 }; - this->m_LabelsArray[102] = { -1, 0, 1, -1, -1, 0, 1, -1 }; - this->m_LabelsArray[103] = { 0, 0, 0, -1, -1, 0, 0, -1 }; - this->m_LabelsArray[104] = { -1, -1, -1, 0, -1, 1, 2, -1 }; - this->m_LabelsArray[105] = { 0, -1, -1, 1, -1, 2, 3, -1 }; - this->m_LabelsArray[106] = { -1, 0, -1, 0, -1, 0, 1, -1 }; - this->m_LabelsArray[107] = { 0, 0, -1, 0, -1, 0, 1, -1 }; - this->m_LabelsArray[108] = { -1, -1, 0, 0, -1, 1, 0, -1 }; - this->m_LabelsArray[109] = { 0, -1, 0, 0, -1, 1, 0, -1 }; - this->m_LabelsArray[110] = { -1, 0, 0, 0, -1, 0, 0, -1 }; - this->m_LabelsArray[111] = { 0, 0, 0, 0, -1, 0, 0, -1 }; - this->m_LabelsArray[112] = { -1, -1, -1, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[113] = { 0, -1, -1, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[114] = { -1, 0, -1, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[115] = { 0, 0, -1, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[116] = { -1, -1, 0, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[117] = { 0, -1, 0, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[118] = { -1, 0, 0, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[119] = { 0, 0, 0, -1, 0, 0, 0, -1 }; - this->m_LabelsArray[120] = { -1, -1, -1, 0, 1, 1, 1, -1 }; - this->m_LabelsArray[121] = { 0, -1, -1, 1, 0, 0, 0, -1 }; - this->m_LabelsArray[122] = { -1, 0, -1, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[123] = { 0, 0, -1, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[124] = { -1, -1, 0, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[125] = { 0, -1, 0, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[126] = { -1, 0, 0, 1, 0, 1, 1, -1 }; - this->m_LabelsArray[127] = { 0, 0, 0, 0, 0, 0, 0, -1 }; - this->m_LabelsArray[128] = { -1, -1, -1, -1, -1, -1, -1, 0 }; - this->m_LabelsArray[129] = { 0, -1, -1, -1, -1, -1, -1, 1 }; - this->m_LabelsArray[130] = { -1, 0, -1, -1, -1, -1, -1, 1 }; - this->m_LabelsArray[131] = { 0, 0, -1, -1, -1, -1, -1, 1 }; - this->m_LabelsArray[132] = { -1, -1, 0, -1, -1, -1, -1, 1 }; - this->m_LabelsArray[133] = { 0, -1, 0, -1, -1, -1, -1, 1 }; - this->m_LabelsArray[134] = { -1, 0, 1, -1, -1, -1, -1, 2 }; - this->m_LabelsArray[135] = { 0, 0, 0, -1, -1, -1, -1, 1 }; - this->m_LabelsArray[136] = { -1, -1, -1, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[137] = { 0, -1, -1, 1, -1, -1, -1, 1 }; - this->m_LabelsArray[138] = { -1, 0, -1, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[139] = { 0, 0, -1, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[140] = { -1, -1, 0, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[141] = { 0, -1, 0, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[142] = { -1, 0, 0, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[143] = { 0, 0, 0, 0, -1, -1, -1, 0 }; - this->m_LabelsArray[144] = { -1, -1, -1, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[145] = { 0, -1, -1, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[146] = { -1, 0, -1, -1, 1, -1, -1, 2 }; - this->m_LabelsArray[147] = { 0, 0, -1, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[148] = { -1, -1, 0, -1, 1, -1, -1, 2 }; - this->m_LabelsArray[149] = { 0, -1, 0, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[150] = { -1, 0, 1, -1, 2, -1, -1, 3 }; - this->m_LabelsArray[151] = { 0, 0, 0, -1, 0, -1, -1, 1 }; - this->m_LabelsArray[152] = { -1, -1, -1, 0, 1, -1, -1, 0 }; - this->m_LabelsArray[153] = { 0, -1, -1, 1, 0, -1, -1, 1 }; - this->m_LabelsArray[154] = { -1, 0, -1, 0, 1, -1, -1, 0 }; - this->m_LabelsArray[155] = { 0, 0, -1, 0, 0, -1, -1, 0 }; - this->m_LabelsArray[156] = { -1, -1, 0, 0, 1, -1, -1, 0 }; - this->m_LabelsArray[157] = { 0, -1, 0, 0, 0, -1, -1, 0 }; - this->m_LabelsArray[158] = { -1, 0, 0, 0, 1, -1, -1, 0 }; - this->m_LabelsArray[159] = { 0, 0, 0, 0, 0, -1, -1, 0 }; - this->m_LabelsArray[160] = { -1, -1, -1, -1, -1, 0, -1, 0 }; - this->m_LabelsArray[161] = { 0, -1, -1, -1, -1, 1, -1, 1 }; - this->m_LabelsArray[162] = { -1, 0, -1, -1, -1, 0, -1, 0 }; - this->m_LabelsArray[163] = { 0, 0, -1, -1, -1, 0, -1, 0 }; - this->m_LabelsArray[164] = { -1, -1, 0, -1, -1, 1, -1, 1 }; - this->m_LabelsArray[165] = { 0, -1, 0, -1, -1, 1, -1, 1 }; - this->m_LabelsArray[166] = { -1, 0, 1, -1, -1, 0, -1, 0 }; - this->m_LabelsArray[167] = { 0, 0, 0, -1, -1, 0, -1, 0 }; - this->m_LabelsArray[168] = { -1, -1, -1, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[169] = { 0, -1, -1, 1, -1, 1, -1, 1 }; - this->m_LabelsArray[170] = { -1, 0, -1, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[171] = { 0, 0, -1, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[172] = { -1, -1, 0, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[173] = { 0, -1, 0, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[174] = { -1, 0, 0, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[175] = { 0, 0, 0, 0, -1, 0, -1, 0 }; - this->m_LabelsArray[176] = { -1, -1, -1, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[177] = { 0, -1, -1, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[178] = { -1, 0, -1, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[179] = { 0, 0, -1, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[180] = { -1, -1, 0, -1, 1, 1, -1, 1 }; - this->m_LabelsArray[181] = { 0, -1, 0, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[182] = { -1, 0, 1, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[183] = { 0, 0, 0, -1, 0, 0, -1, 0 }; - this->m_LabelsArray[184] = { -1, -1, -1, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[185] = { 0, -1, -1, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[186] = { -1, 0, -1, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[187] = { 0, 0, -1, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[188] = { -1, -1, 0, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[189] = { 0, -1, 1, 0, 1, 0, -1, 1 }; - this->m_LabelsArray[190] = { -1, 0, 0, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[191] = { 0, 0, 0, 0, 0, 0, -1, 0 }; - this->m_LabelsArray[192] = { -1, -1, -1, -1, -1, -1, 0, 0 }; - this->m_LabelsArray[193] = { 0, -1, -1, -1, -1, -1, 1, 1 }; - this->m_LabelsArray[194] = { -1, 0, -1, -1, -1, -1, 1, 1 }; - this->m_LabelsArray[195] = { 0, 0, -1, -1, -1, -1, 1, 1 }; - this->m_LabelsArray[196] = { -1, -1, 0, -1, -1, -1, 0, 0 }; - this->m_LabelsArray[197] = { 0, -1, 0, -1, -1, -1, 0, 0 }; - this->m_LabelsArray[198] = { -1, 0, 1, -1, -1, -1, 1, 1 }; - this->m_LabelsArray[199] = { 0, 0, 0, -1, -1, -1, 0, 0 }; - this->m_LabelsArray[200] = { -1, -1, -1, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[201] = { 0, -1, -1, 1, -1, -1, 1, 1 }; - this->m_LabelsArray[202] = { -1, 0, -1, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[203] = { 0, 0, -1, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[204] = { -1, -1, 0, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[205] = { 0, -1, 0, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[206] = { -1, 0, 0, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[207] = { 0, 0, 0, 0, -1, -1, 0, 0 }; - this->m_LabelsArray[208] = { -1, -1, -1, -1, 0, -1, 0, 0 }; - this->m_LabelsArray[209] = { 0, -1, -1, -1, 0, -1, 0, 0 }; - this->m_LabelsArray[210] = { -1, 0, -1, -1, 1, -1, 1, 1 }; - this->m_LabelsArray[211] = { 0, 0, -1, -1, 0, -1, 0, 0 }; - this->m_LabelsArray[212] = { -1, -1, 0, -1, 0, -1, 0, 0 }; - this->m_LabelsArray[213] = { 0, -1, 0, -1, 0, -1, 0, 0 }; - this->m_LabelsArray[214] = { -1, 0, 1, -1, 1, -1, 1, 1 }; - this->m_LabelsArray[215] = { 0, 0, 0, -1, 0, -1, 0, 0 }; - this->m_LabelsArray[216] = { -1, -1, -1, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[217] = { 0, -1, -1, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[218] = { -1, 0, -1, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[219] = { 0, 1, -1, 0, 1, -1, 0, 1 }; - this->m_LabelsArray[220] = { -1, -1, 0, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[221] = { 0, -1, 0, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[222] = { -1, 0, 0, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[223] = { 0, 0, 0, 0, 0, -1, 0, 0 }; - this->m_LabelsArray[224] = { -1, -1, -1, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[225] = { 0, -1, -1, -1, -1, 1, 1, 1 }; - this->m_LabelsArray[226] = { -1, 0, -1, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[227] = { 0, 0, -1, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[228] = { -1, -1, 0, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[229] = { 0, -1, 0, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[230] = { -1, 0, 0, -1, -1, 0, 0, 0 }; - this->m_LabelsArray[231] = { 1, 0, 0, -1, -1, 1, 1, 0 }; - this->m_LabelsArray[232] = { -1, -1, -1, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[233] = { 0, -1, -1, 1, -1, 1, 1, 1 }; - this->m_LabelsArray[234] = { -1, 0, -1, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[235] = { 0, 0, -1, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[236] = { -1, -1, 0, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[237] = { 0, -1, 0, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[238] = { -1, 0, 0, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[239] = { 0, 0, 0, 0, -1, 0, 0, 0 }; - this->m_LabelsArray[240] = { -1, -1, -1, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[241] = { 0, -1, -1, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[242] = { -1, 0, -1, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[243] = { 0, 0, -1, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[244] = { -1, -1, 0, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[245] = { 0, -1, 0, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[246] = { -1, 0, 0, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[247] = { 0, 0, 0, -1, 0, 0, 0, 0 }; - this->m_LabelsArray[248] = { -1, -1, -1, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[249] = { 0, -1, -1, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[250] = { -1, 0, -1, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[251] = { 0, 0, -1, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[252] = { -1, -1, 0, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[253] = { 0, -1, 0, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[254] = { -1, 0, 0, 0, 0, 0, 0, 0 }; - this->m_LabelsArray[255] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + this->m_LabelsArray[0] = { { -1, -1, -1, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[1] = { { 0, -1, -1, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[2] = { { -1, 0, -1, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[3] = { { 0, 0, -1, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[4] = { { -1, -1, 0, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[5] = { { 0, -1, 0, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[6] = { { -1, 0, 1, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[7] = { { 0, 0, 0, -1, -1, -1, -1, -1 } }; + this->m_LabelsArray[8] = { { -1, -1, -1, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[9] = { { 0, -1, -1, 1, -1, -1, -1, -1 } }; + this->m_LabelsArray[10] = { { -1, 0, -1, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[11] = { { 0, 0, -1, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[12] = { { -1, -1, 0, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[13] = { { 0, -1, 0, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[14] = { { -1, 0, 0, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[15] = { { 0, 0, 0, 0, -1, -1, -1, -1 } }; + this->m_LabelsArray[16] = { { -1, -1, -1, -1, 0, -1, -1, -1 } }; + this->m_LabelsArray[17] = { { 0, -1, -1, -1, 0, -1, -1, -1 } }; + this->m_LabelsArray[18] = { { -1, 0, -1, -1, 1, -1, -1, -1 } }; + this->m_LabelsArray[19] = { { 0, 0, -1, -1, 0, -1, -1, -1 } }; + this->m_LabelsArray[20] = { { -1, -1, 0, -1, 1, -1, -1, -1 } }; + this->m_LabelsArray[21] = { { 0, -1, 0, -1, 0, -1, -1, -1 } }; + this->m_LabelsArray[22] = { { -1, 0, 1, -1, 2, -1, -1, -1 } }; + this->m_LabelsArray[23] = { { 0, 0, 0, -1, 0, -1, -1, -1 } }; + this->m_LabelsArray[24] = { { -1, -1, -1, 0, 1, -1, -1, -1 } }; + this->m_LabelsArray[25] = { { 0, -1, -1, 1, 0, -1, -1, -1 } }; + this->m_LabelsArray[26] = { { -1, 0, -1, 0, 1, -1, -1, -1 } }; + this->m_LabelsArray[27] = { { 0, 0, -1, 0, 0, -1, -1, -1 } }; + this->m_LabelsArray[28] = { { -1, -1, 0, 0, 1, -1, -1, -1 } }; + this->m_LabelsArray[29] = { { 0, -1, 0, 0, 0, -1, -1, -1 } }; + this->m_LabelsArray[30] = { { -1, 0, 0, 0, 1, -1, -1, -1 } }; + this->m_LabelsArray[31] = { { 0, 0, 0, 0, 0, -1, -1, -1 } }; + this->m_LabelsArray[32] = { { -1, -1, -1, -1, -1, 0, -1, -1 } }; + this->m_LabelsArray[33] = { { 0, -1, -1, -1, -1, 1, -1, -1 } }; + this->m_LabelsArray[34] = { { -1, 0, -1, -1, -1, 0, -1, -1 } }; + this->m_LabelsArray[35] = { { 0, 0, -1, -1, -1, 0, -1, -1 } }; + this->m_LabelsArray[36] = { { -1, -1, 0, -1, -1, 1, -1, -1 } }; + this->m_LabelsArray[37] = { { 0, -1, 0, -1, -1, 1, -1, -1 } }; + this->m_LabelsArray[38] = { { -1, 0, 1, -1, -1, 0, -1, -1 } }; + this->m_LabelsArray[39] = { { 0, 0, 0, -1, -1, 0, -1, -1 } }; + this->m_LabelsArray[40] = { { -1, -1, -1, 0, -1, 1, -1, -1 } }; + this->m_LabelsArray[41] = { { 0, -1, -1, 1, -1, 2, -1, -1 } }; + this->m_LabelsArray[42] = { { -1, 0, -1, 0, -1, 0, -1, -1 } }; + this->m_LabelsArray[43] = { { 0, 0, -1, 0, -1, 0, -1, -1 } }; + this->m_LabelsArray[44] = { { -1, -1, 0, 0, -1, 1, -1, -1 } }; + this->m_LabelsArray[45] = { { 0, -1, 0, 0, -1, 1, -1, -1 } }; + this->m_LabelsArray[46] = { { -1, 0, 0, 0, -1, 0, -1, -1 } }; + this->m_LabelsArray[47] = { { 0, 0, 0, 0, -1, 0, -1, -1 } }; + this->m_LabelsArray[48] = { { -1, -1, -1, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[49] = { { 0, -1, -1, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[50] = { { -1, 0, -1, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[51] = { { 0, 0, -1, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[52] = { { -1, -1, 0, -1, 1, 1, -1, -1 } }; + this->m_LabelsArray[53] = { { 0, -1, 0, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[54] = { { -1, 0, 1, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[55] = { { 0, 0, 0, -1, 0, 0, -1, -1 } }; + this->m_LabelsArray[56] = { { -1, -1, -1, 0, 1, 1, -1, -1 } }; + this->m_LabelsArray[57] = { { 0, -1, -1, 1, 0, 0, -1, -1 } }; + this->m_LabelsArray[58] = { { -1, 0, -1, 0, 0, 0, -1, -1 } }; + this->m_LabelsArray[59] = { { 0, 0, -1, 0, 0, 0, -1, -1 } }; + this->m_LabelsArray[60] = { { -1, -1, 0, 0, 1, 1, -1, -1 } }; + this->m_LabelsArray[61] = { { 0, -1, 0, 0, 0, 0, -1, -1 } }; + this->m_LabelsArray[62] = { { -1, 0, 0, 0, 0, 0, -1, -1 } }; + this->m_LabelsArray[63] = { { 0, 0, 0, 0, 0, 0, -1, -1 } }; + this->m_LabelsArray[64] = { { -1, -1, -1, -1, -1, -1, 0, -1 } }; + this->m_LabelsArray[65] = { { 0, -1, -1, -1, -1, -1, 1, -1 } }; + this->m_LabelsArray[66] = { { -1, 0, -1, -1, -1, -1, 1, -1 } }; + this->m_LabelsArray[67] = { { 0, 0, -1, -1, -1, -1, 1, -1 } }; + this->m_LabelsArray[68] = { { -1, -1, 0, -1, -1, -1, 0, -1 } }; + this->m_LabelsArray[69] = { { 0, -1, 0, -1, -1, -1, 0, -1 } }; + this->m_LabelsArray[70] = { { -1, 0, 1, -1, -1, -1, 1, -1 } }; + this->m_LabelsArray[71] = { { 0, 0, 0, -1, -1, -1, 0, -1 } }; + this->m_LabelsArray[72] = { { -1, -1, -1, 0, -1, -1, 1, -1 } }; + this->m_LabelsArray[73] = { { 0, -1, -1, 1, -1, -1, 2, -1 } }; + this->m_LabelsArray[74] = { { -1, 0, -1, 0, -1, -1, 1, -1 } }; + this->m_LabelsArray[75] = { { 0, 0, -1, 0, -1, -1, 1, -1 } }; + this->m_LabelsArray[76] = { { -1, -1, 0, 0, -1, -1, 0, -1 } }; + this->m_LabelsArray[77] = { { 0, -1, 0, 0, -1, -1, 0, -1 } }; + this->m_LabelsArray[78] = { { -1, 0, 0, 0, -1, -1, 0, -1 } }; + this->m_LabelsArray[79] = { { 0, 0, 0, 0, -1, -1, 0, -1 } }; + this->m_LabelsArray[80] = { { -1, -1, -1, -1, 0, -1, 0, -1 } }; + this->m_LabelsArray[81] = { { 0, -1, -1, -1, 0, -1, 0, -1 } }; + this->m_LabelsArray[82] = { { -1, 0, -1, -1, 1, -1, 1, -1 } }; + this->m_LabelsArray[83] = { { 0, 0, -1, -1, 0, -1, 0, -1 } }; + this->m_LabelsArray[84] = { { -1, -1, 0, -1, 0, -1, 0, -1 } }; + this->m_LabelsArray[85] = { { 0, -1, 0, -1, 0, -1, 0, -1 } }; + this->m_LabelsArray[86] = { { -1, 0, 1, -1, 1, -1, 1, -1 } }; + this->m_LabelsArray[87] = { { 0, 0, 0, -1, 0, -1, 0, -1 } }; + this->m_LabelsArray[88] = { { -1, -1, -1, 0, 1, -1, 1, -1 } }; + this->m_LabelsArray[89] = { { 0, -1, -1, 1, 0, -1, 0, -1 } }; + this->m_LabelsArray[90] = { { -1, 0, -1, 0, 1, -1, 1, -1 } }; + this->m_LabelsArray[91] = { { 0, 0, -1, 0, 0, -1, 0, -1 } }; + this->m_LabelsArray[92] = { { -1, -1, 0, 0, 0, -1, 0, -1 } }; + this->m_LabelsArray[93] = { { 0, -1, 0, 0, 0, -1, 0, -1 } }; + this->m_LabelsArray[94] = { { -1, 0, 0, 0, 0, -1, 0, -1 } }; + this->m_LabelsArray[95] = { { 0, 0, 0, 0, 0, -1, 0, -1 } }; + this->m_LabelsArray[96] = { { -1, -1, -1, -1, -1, 0, 1, -1 } }; + this->m_LabelsArray[97] = { { 0, -1, -1, -1, -1, 1, 2, -1 } }; + this->m_LabelsArray[98] = { { -1, 0, -1, -1, -1, 0, 1, -1 } }; + this->m_LabelsArray[99] = { { 0, 0, -1, -1, -1, 0, 1, -1 } }; + this->m_LabelsArray[100] = { { -1, -1, 0, -1, -1, 1, 0, -1 } }; + this->m_LabelsArray[101] = { { 0, -1, 0, -1, -1, 1, 0, -1 } }; + this->m_LabelsArray[102] = { { -1, 0, 1, -1, -1, 0, 1, -1 } }; + this->m_LabelsArray[103] = { { 0, 0, 0, -1, -1, 0, 0, -1 } }; + this->m_LabelsArray[104] = { { -1, -1, -1, 0, -1, 1, 2, -1 } }; + this->m_LabelsArray[105] = { { 0, -1, -1, 1, -1, 2, 3, -1 } }; + this->m_LabelsArray[106] = { { -1, 0, -1, 0, -1, 0, 1, -1 } }; + this->m_LabelsArray[107] = { { 0, 0, -1, 0, -1, 0, 1, -1 } }; + this->m_LabelsArray[108] = { { -1, -1, 0, 0, -1, 1, 0, -1 } }; + this->m_LabelsArray[109] = { { 0, -1, 0, 0, -1, 1, 0, -1 } }; + this->m_LabelsArray[110] = { { -1, 0, 0, 0, -1, 0, 0, -1 } }; + this->m_LabelsArray[111] = { { 0, 0, 0, 0, -1, 0, 0, -1 } }; + this->m_LabelsArray[112] = { { -1, -1, -1, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[113] = { { 0, -1, -1, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[114] = { { -1, 0, -1, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[115] = { { 0, 0, -1, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[116] = { { -1, -1, 0, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[117] = { { 0, -1, 0, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[118] = { { -1, 0, 0, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[119] = { { 0, 0, 0, -1, 0, 0, 0, -1 } }; + this->m_LabelsArray[120] = { { -1, -1, -1, 0, 1, 1, 1, -1 } }; + this->m_LabelsArray[121] = { { 0, -1, -1, 1, 0, 0, 0, -1 } }; + this->m_LabelsArray[122] = { { -1, 0, -1, 0, 0, 0, 0, -1 } }; + this->m_LabelsArray[123] = { { 0, 0, -1, 0, 0, 0, 0, -1 } }; + this->m_LabelsArray[124] = { { -1, -1, 0, 0, 0, 0, 0, -1 } }; + this->m_LabelsArray[125] = { { 0, -1, 0, 0, 0, 0, 0, -1 } }; + this->m_LabelsArray[126] = { { -1, 0, 0, 1, 0, 1, 1, -1 } }; + this->m_LabelsArray[127] = { { 0, 0, 0, 0, 0, 0, 0, -1 } }; + this->m_LabelsArray[128] = { { -1, -1, -1, -1, -1, -1, -1, 0 } }; + this->m_LabelsArray[129] = { { 0, -1, -1, -1, -1, -1, -1, 1 } }; + this->m_LabelsArray[130] = { { -1, 0, -1, -1, -1, -1, -1, 1 } }; + this->m_LabelsArray[131] = { { 0, 0, -1, -1, -1, -1, -1, 1 } }; + this->m_LabelsArray[132] = { { -1, -1, 0, -1, -1, -1, -1, 1 } }; + this->m_LabelsArray[133] = { { 0, -1, 0, -1, -1, -1, -1, 1 } }; + this->m_LabelsArray[134] = { { -1, 0, 1, -1, -1, -1, -1, 2 } }; + this->m_LabelsArray[135] = { { 0, 0, 0, -1, -1, -1, -1, 1 } }; + this->m_LabelsArray[136] = { { -1, -1, -1, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[137] = { { 0, -1, -1, 1, -1, -1, -1, 1 } }; + this->m_LabelsArray[138] = { { -1, 0, -1, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[139] = { { 0, 0, -1, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[140] = { { -1, -1, 0, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[141] = { { 0, -1, 0, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[142] = { { -1, 0, 0, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[143] = { { 0, 0, 0, 0, -1, -1, -1, 0 } }; + this->m_LabelsArray[144] = { { -1, -1, -1, -1, 0, -1, -1, 1 } }; + this->m_LabelsArray[145] = { { 0, -1, -1, -1, 0, -1, -1, 1 } }; + this->m_LabelsArray[146] = { { -1, 0, -1, -1, 1, -1, -1, 2 } }; + this->m_LabelsArray[147] = { { 0, 0, -1, -1, 0, -1, -1, 1 } }; + this->m_LabelsArray[148] = { { -1, -1, 0, -1, 1, -1, -1, 2 } }; + this->m_LabelsArray[149] = { { 0, -1, 0, -1, 0, -1, -1, 1 } }; + this->m_LabelsArray[150] = { { -1, 0, 1, -1, 2, -1, -1, 3 } }; + this->m_LabelsArray[151] = { { 0, 0, 0, -1, 0, -1, -1, 1 } }; + this->m_LabelsArray[152] = { { -1, -1, -1, 0, 1, -1, -1, 0 } }; + this->m_LabelsArray[153] = { { 0, -1, -1, 1, 0, -1, -1, 1 } }; + this->m_LabelsArray[154] = { { -1, 0, -1, 0, 1, -1, -1, 0 } }; + this->m_LabelsArray[155] = { { 0, 0, -1, 0, 0, -1, -1, 0 } }; + this->m_LabelsArray[156] = { { -1, -1, 0, 0, 1, -1, -1, 0 } }; + this->m_LabelsArray[157] = { { 0, -1, 0, 0, 0, -1, -1, 0 } }; + this->m_LabelsArray[158] = { { -1, 0, 0, 0, 1, -1, -1, 0 } }; + this->m_LabelsArray[159] = { { 0, 0, 0, 0, 0, -1, -1, 0 } }; + this->m_LabelsArray[160] = { { -1, -1, -1, -1, -1, 0, -1, 0 } }; + this->m_LabelsArray[161] = { { 0, -1, -1, -1, -1, 1, -1, 1 } }; + this->m_LabelsArray[162] = { { -1, 0, -1, -1, -1, 0, -1, 0 } }; + this->m_LabelsArray[163] = { { 0, 0, -1, -1, -1, 0, -1, 0 } }; + this->m_LabelsArray[164] = { { -1, -1, 0, -1, -1, 1, -1, 1 } }; + this->m_LabelsArray[165] = { { 0, -1, 0, -1, -1, 1, -1, 1 } }; + this->m_LabelsArray[166] = { { -1, 0, 1, -1, -1, 0, -1, 0 } }; + this->m_LabelsArray[167] = { { 0, 0, 0, -1, -1, 0, -1, 0 } }; + this->m_LabelsArray[168] = { { -1, -1, -1, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[169] = { { 0, -1, -1, 1, -1, 1, -1, 1 } }; + this->m_LabelsArray[170] = { { -1, 0, -1, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[171] = { { 0, 0, -1, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[172] = { { -1, -1, 0, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[173] = { { 0, -1, 0, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[174] = { { -1, 0, 0, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[175] = { { 0, 0, 0, 0, -1, 0, -1, 0 } }; + this->m_LabelsArray[176] = { { -1, -1, -1, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[177] = { { 0, -1, -1, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[178] = { { -1, 0, -1, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[179] = { { 0, 0, -1, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[180] = { { -1, -1, 0, -1, 1, 1, -1, 1 } }; + this->m_LabelsArray[181] = { { 0, -1, 0, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[182] = { { -1, 0, 1, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[183] = { { 0, 0, 0, -1, 0, 0, -1, 0 } }; + this->m_LabelsArray[184] = { { -1, -1, -1, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[185] = { { 0, -1, -1, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[186] = { { -1, 0, -1, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[187] = { { 0, 0, -1, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[188] = { { -1, -1, 0, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[189] = { { 0, -1, 1, 0, 1, 0, -1, 1 } }; + this->m_LabelsArray[190] = { { -1, 0, 0, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[191] = { { 0, 0, 0, 0, 0, 0, -1, 0 } }; + this->m_LabelsArray[192] = { { -1, -1, -1, -1, -1, -1, 0, 0 } }; + this->m_LabelsArray[193] = { { 0, -1, -1, -1, -1, -1, 1, 1 } }; + this->m_LabelsArray[194] = { { -1, 0, -1, -1, -1, -1, 1, 1 } }; + this->m_LabelsArray[195] = { { 0, 0, -1, -1, -1, -1, 1, 1 } }; + this->m_LabelsArray[196] = { { -1, -1, 0, -1, -1, -1, 0, 0 } }; + this->m_LabelsArray[197] = { { 0, -1, 0, -1, -1, -1, 0, 0 } }; + this->m_LabelsArray[198] = { { -1, 0, 1, -1, -1, -1, 1, 1 } }; + this->m_LabelsArray[199] = { { 0, 0, 0, -1, -1, -1, 0, 0 } }; + this->m_LabelsArray[200] = { { -1, -1, -1, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[201] = { { 0, -1, -1, 1, -1, -1, 1, 1 } }; + this->m_LabelsArray[202] = { { -1, 0, -1, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[203] = { { 0, 0, -1, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[204] = { { -1, -1, 0, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[205] = { { 0, -1, 0, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[206] = { { -1, 0, 0, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[207] = { { 0, 0, 0, 0, -1, -1, 0, 0 } }; + this->m_LabelsArray[208] = { { -1, -1, -1, -1, 0, -1, 0, 0 } }; + this->m_LabelsArray[209] = { { 0, -1, -1, -1, 0, -1, 0, 0 } }; + this->m_LabelsArray[210] = { { -1, 0, -1, -1, 1, -1, 1, 1 } }; + this->m_LabelsArray[211] = { { 0, 0, -1, -1, 0, -1, 0, 0 } }; + this->m_LabelsArray[212] = { { -1, -1, 0, -1, 0, -1, 0, 0 } }; + this->m_LabelsArray[213] = { { 0, -1, 0, -1, 0, -1, 0, 0 } }; + this->m_LabelsArray[214] = { { -1, 0, 1, -1, 1, -1, 1, 1 } }; + this->m_LabelsArray[215] = { { 0, 0, 0, -1, 0, -1, 0, 0 } }; + this->m_LabelsArray[216] = { { -1, -1, -1, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[217] = { { 0, -1, -1, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[218] = { { -1, 0, -1, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[219] = { { 0, 1, -1, 0, 1, -1, 0, 1 } }; + this->m_LabelsArray[220] = { { -1, -1, 0, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[221] = { { 0, -1, 0, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[222] = { { -1, 0, 0, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[223] = { { 0, 0, 0, 0, 0, -1, 0, 0 } }; + this->m_LabelsArray[224] = { { -1, -1, -1, -1, -1, 0, 0, 0 } }; + this->m_LabelsArray[225] = { { 0, -1, -1, -1, -1, 1, 1, 1 } }; + this->m_LabelsArray[226] = { { -1, 0, -1, -1, -1, 0, 0, 0 } }; + this->m_LabelsArray[227] = { { 0, 0, -1, -1, -1, 0, 0, 0 } }; + this->m_LabelsArray[228] = { { -1, -1, 0, -1, -1, 0, 0, 0 } }; + this->m_LabelsArray[229] = { { 0, -1, 0, -1, -1, 0, 0, 0 } }; + this->m_LabelsArray[230] = { { -1, 0, 0, -1, -1, 0, 0, 0 } }; + this->m_LabelsArray[231] = { { 1, 0, 0, -1, -1, 1, 1, 0 } }; + this->m_LabelsArray[232] = { { -1, -1, -1, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[233] = { { 0, -1, -1, 1, -1, 1, 1, 1 } }; + this->m_LabelsArray[234] = { { -1, 0, -1, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[235] = { { 0, 0, -1, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[236] = { { -1, -1, 0, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[237] = { { 0, -1, 0, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[238] = { { -1, 0, 0, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[239] = { { 0, 0, 0, 0, -1, 0, 0, 0 } }; + this->m_LabelsArray[240] = { { -1, -1, -1, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[241] = { { 0, -1, -1, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[242] = { { -1, 0, -1, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[243] = { { 0, 0, -1, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[244] = { { -1, -1, 0, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[245] = { { 0, -1, 0, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[246] = { { -1, 0, 0, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[247] = { { 0, 0, 0, -1, 0, 0, 0, 0 } }; + this->m_LabelsArray[248] = { { -1, -1, -1, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[249] = { { 0, -1, -1, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[250] = { { -1, 0, -1, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[251] = { { 0, 0, -1, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[252] = { { -1, -1, 0, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[253] = { { 0, -1, 0, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[254] = { { -1, 0, 0, 0, 0, 0, 0, 0 } }; + this->m_LabelsArray[255] = { { 0, 0, 0, 0, 0, 0, 0, 0 } }; } template diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx index 64c392f1e78..c966b8d78d8 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx @@ -45,7 +45,7 @@ CuberilleTest04(int itkNotUsed(argc), char * itkNotUsed(argv)[]) // 219 11011011 // 231 11100111 - std::array masks{ 126, 189, 219, 231 }; + std::array masks{ { 126, 189, 219, 231 } }; for (const auto & mask : masks) { From 1e4b4574db00194e8053a1382e40d4d53e7fc316 Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Sun, 25 Oct 2020 18:30:33 -0700 Subject: [PATCH 66/84] BUG: Fix Incorrect Indexing in Case 231 This patch fixes an indexing error in case 231 (one of the four "3D checkerboard" cases), which previously caused vertices to be incorrectly linked. The patch also expands the testing suite to perform basic checks on all configurations around a vertex. --- .../include/itkCuberilleImageToMeshFilter.hxx | 6 ++-- .../Cuberille/test/CuberilleTest04.cxx | 31 +++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 27f1af4ac76..92d928cbca6 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -190,13 +190,13 @@ CuberilleImageToMeshFilter::GenerateDat const auto diff = vindex - index; size_t offsetID = 7 - (diff[0] + diff[1] * 2 + diff[2] * 4); const auto components = this->m_LabelsArray.at(bitmaskID); - const auto numComponents = (*std::max_element(components.begin(), components.end())) + 1; const auto component = components.at(offsetID); unsigned int look = (i < 4) ? look0 : look1; // First four are first slice if (!lookup[look].GetVertex(vindex[0], vindex[1], component, v[i])) { // Vertex was not in lookup, create and add to lookup v[i] = nextVertexId; + const auto numComponents = (*std::max_element(components.begin(), components.end())) + 1; const auto pv = AddVertex(nextVertexId, vindex, image, mesh, numComponents); lookup[look].AddVertex(vindex[0], vindex[1], pv); } @@ -757,7 +757,7 @@ CuberilleImageToMeshFilter::CalculateLa // this->m_LabelsArray[126] = { { -1, 0, 0, 1, 0, 1, 1, -1 } }; // this->m_LabelsArray[189] = { { 0, -1, 1, 0, 1, 0, -1, 1 } }; // this->m_LabelsArray[219] = { { 0, 1, -1, 0, 1, -1, 0, 1 } }; - // this->m_LabelsArray[231] = { { 1, 0, 0, -1, -1, 1, 1, 0 } }; + // this->m_LabelsArray[231] = { { 0, 1, 1, -1, -1, 0, 0, 1 } }; // // for (size_t i = 0; i < std::pow(2,8); ++i) { // std::cout << "this->m_LabelsArray[" << i << "] "; @@ -1001,7 +1001,7 @@ CuberilleImageToMeshFilter::CalculateLa this->m_LabelsArray[228] = { { -1, -1, 0, -1, -1, 0, 0, 0 } }; this->m_LabelsArray[229] = { { 0, -1, 0, -1, -1, 0, 0, 0 } }; this->m_LabelsArray[230] = { { -1, 0, 0, -1, -1, 0, 0, 0 } }; - this->m_LabelsArray[231] = { { 1, 0, 0, -1, -1, 1, 1, 0 } }; + this->m_LabelsArray[231] = { { 0, 1, 1, -1, -1, 0, 0, 1 } }; this->m_LabelsArray[232] = { { -1, -1, -1, 0, -1, 0, 0, 0 } }; this->m_LabelsArray[233] = { { 0, -1, -1, 1, -1, 1, 1, 1 } }; this->m_LabelsArray[234] = { { -1, 0, -1, 0, -1, 0, 0, 0 } }; diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx index c966b8d78d8..90df0c6f5b9 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx @@ -22,12 +22,6 @@ #include #include -// In the case where a 2x2x2 region contains two background pixels at -// opposite corners, and foreground pixels elsewhere, it is necessary -// that there be two vertices at the center. Previously, this was -// handled incorrectly with a single vertex, causing non-manifold -// geometry. This test covers the four cases in which this occurs. - int CuberilleTest04(int itkNotUsed(argc), char * itkNotUsed(argv)[]) { @@ -40,14 +34,7 @@ CuberilleTest04(int itkNotUsed(argc), char * itkNotUsed(argv)[]) using TPad = itk::ConstantPadImageFilter; using TExtract = itk::CuberilleImageToMeshFilter; - // 126 01111110 - // 189 10111101 - // 219 11011011 - // 231 11100111 - - std::array masks{ { 126, 189, 219, 231 } }; - - for (const auto & mask : masks) + for (size_t mask = 1; mask < std::pow(2, 8); ++mask) { std::bitset<8> bitmask(mask); @@ -87,10 +74,20 @@ CuberilleTest04(int itkNotUsed(argc), char * itkNotUsed(argv)[]) extract->GenerateTriangleFacesOn(); extract->ProjectVerticesToIsoSurfaceOff(); extract->SavePixelAsCellDataOn(); - extract->Update(); + ITK_TRY_EXPECT_NO_EXCEPTION(extract->Update()); + + const auto out = TMesh::New(); + out->Graft(extract->GetOutput()); - ITK_TEST_EXPECT_EQUAL(extract->GetOutput()->GetNumberOfPoints(), 26); - ITK_TEST_EXPECT_EQUAL(extract->GetOutput()->GetNumberOfCells(), 48); + ITK_TEST_EXPECT_TRUE(out->GetNumberOfFaces() == out->GetCellData()->Size()); + + for (auto it = out->GetEdgeCells()->Begin(); it != out->GetEdgeCells()->End(); ++it) + { + using TEdge = TMesh::EdgeCellType; + const auto qe = dynamic_cast(it.Value())->GetQEGeom(); + ITK_TEST_EXPECT_TRUE(qe->IsLeftSet()); + ITK_TEST_EXPECT_TRUE(qe->IsRightSet()); + } } return EXIT_SUCCESS; From 7cbad1fe1ae925618b6cb1d7d0bfc2ff8f89703a Mon Sep 17 00:00:00 2001 From: Mathew Seng Date: Wed, 14 Oct 2020 10:58:38 -0500 Subject: [PATCH 67/84] STYLE: Rename ITK_DISALLOW_COPY_AND_ASSIGN to ITK_DISALLOW_COPY_AND_MOVE Fixes changes made in #2053. ITK_DISALLOW_COPY_AND_ASSIGN will be used if ITK_FUTURE_LEGACY_REMOVE=OFF. --- .../Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 6219935044d..7efb80ba9d4 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -117,7 +117,7 @@ template { public: - ITK_DISALLOW_COPY_AND_ASSIGN(CuberilleImageToMeshFilter); + ITK_DISALLOW_COPY_AND_MOVE(CuberilleImageToMeshFilter); /** Standard "Self" type alias. */ using Self = CuberilleImageToMeshFilter; From 122de0d63152e9dc522047d4b8934dffb09f621f Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Fri, 17 Dec 2021 09:34:47 -0600 Subject: [PATCH 68/84] COMP: Remove inclusion of .hxx files as headers The ability to include either .h or .hxx files as header files required recursively reading the .h files twice. The added complexity is unnecessary, costly, and can confuse static analysis tools that monitor header guardes (due to reaching the maximum depth of recursion limits for nested #ifdefs in checking). --- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 92d928cbca6..f486c26254a 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -20,7 +20,6 @@ #define itkCuberilleImageToMeshFilter_hxx #include "itkMath.h" -#include "itkCuberilleImageToMeshFilter.h" #include "itkNumericTraits.h" #include #include From f03481f74dfb016a21c99db0a64688eaa17efe47 Mon Sep 17 00:00:00 2001 From: Tom Birdsong Date: Tue, 31 May 2022 11:26:48 -0400 Subject: [PATCH 69/84] ENH: Bump ITK and replace http with https using script --- Modules/Filtering/Cuberille/LICENSE | 4 ++-- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 6 +++--- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 4 ++-- Modules/Filtering/Cuberille/itk-module.cmake | 4 ++-- Modules/Filtering/Cuberille/test/CuberilleTest01.cxx | 2 +- Modules/Filtering/Cuberille/test/CuberilleTest02.cxx | 2 +- Modules/Filtering/Cuberille/test/CuberilleTest03.cxx | 2 +- Modules/Filtering/Cuberille/test/CuberilleTest04.cxx | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Modules/Filtering/Cuberille/LICENSE b/Modules/Filtering/Cuberille/LICENSE index d6456956733..62589edd12a 100644 --- a/Modules/Filtering/Cuberille/LICENSE +++ b/Modules/Filtering/Cuberille/LICENSE @@ -1,7 +1,7 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -193,7 +193,7 @@ 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 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 7efb80ba9d4..964df996307 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -6,7 +6,7 @@ * 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 + * 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, @@ -101,10 +101,10 @@ namespace itk * [2] D. Gordon and J.K. Udupa, "Fast surface tracking in Three-dimensional Binary Images", Computer Vision, Graphics and Image Processing, Volume 45, Pages 196-214, 1989. - * [3] http://www2.imm.dtu.dk/~jab/gallery/polygonization.html + * [3] https://www2.imm.dtu.dk/~jab/gallery/polygonization.html * * This implementation was taken from the Insight Journal: - * http://hdl.handle.net/10380/3186 + * https://hdl.handle.net/10380/3186 * * \author Dan Mueller, Philips Healthcare, dan dot muel at gmail dot com * diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index f486c26254a..66a2b9bc9ef 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -6,7 +6,7 @@ * 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 + * 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, @@ -93,7 +93,7 @@ CuberilleImageToMeshFilter::GenerateDat // TODO: Estimate number of vertices/faces for which to reserve // TODO: There is an issue with quad edge mesh related to reserving, see - // http://www.itk.org/mailman/private/insight-developers/2010-June/014653.html + // https://www.itk.org/mailman/private/insight-developers/2010-June/014653.html // SizeType size = image->GetLargestPossibleRegion().GetSize(); // mesh->GetPoints()->Reserve( (size[0]*size[1]*size[2]) / 200 ); // mesh->GetCells()->Reserve( (size[0]*size[1]*size[2]) / 100 ); diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake index 25ea607f28e..96bb41fb8eb 100644 --- a/Modules/Filtering/Cuberille/itk-module.cmake +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -11,8 +11,8 @@ resultant mesh. A more detailed description can be found in the Insight Journal article: Mueller, D. \"Cuberille Implicit Surface Polygonization for ITK\" - http://hdl.handle.net/10380/3186 - http://www.insight-journal.org/browse/publication/740 + https://hdl.handle.net/10380/3186 + https://www.insight-journal.org/browse/publication/740 July 20, 2010. " ) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx index 9f85425d74d..78253d3cc38 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest01.cxx @@ -6,7 +6,7 @@ * 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 + * 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, diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx index c5566aab0a7..b2f76f64d16 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest02.cxx @@ -6,7 +6,7 @@ * 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 + * 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, diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx index 38a7ce78476..0283983f5a1 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest03.cxx @@ -6,7 +6,7 @@ * 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 + * 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, diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx index 90df0c6f5b9..9ec59091e72 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest04.cxx @@ -6,7 +6,7 @@ * 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 + * 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, From 4905ade14efc9296afd6c1e981de62f675ecc295 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Wed, 5 Jun 2024 15:46:27 -0400 Subject: [PATCH 70/84] COMP: Update cmake_minimum_required to 3.16.3 CI Windows test failures. CMake Warning at D:/a/ITKCuberille/ITK/CMake/ITKModuleExternal.cmake:15 (message): -- This is needed to allow proper setting of CMAKE_MSVC_RUNTIME_LIBRARY. -- Do not be surprised if you run into link errors of the style: LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_Static' doesn't match value 'MDd_Dynamic' in module.obj cmake_minimum_required must be at least 3.16.3 Call Stack (most recent call first): CMakeLists.txt:7 (include) --- Modules/Filtering/Cuberille/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/CMakeLists.txt b/Modules/Filtering/Cuberille/CMakeLists.txt index ae07bec173d..e17b3840ee6 100644 --- a/Modules/Filtering/Cuberille/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10.2) +cmake_minimum_required(VERSION 3.16.3) project(Cuberille) if(NOT ITK_SOURCE_DIR) From 75ee38c8270ea9848518a5f7c3db93bbddadf5de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Wed, 27 Nov 2024 11:48:47 -0500 Subject: [PATCH 71/84] ENH: Add Python tests for existing input images --- Modules/Filtering/Cuberille/itk-module.cmake | 4 ++ .../Cuberille/wrapping/test/CMakeLists.txt | 41 +++++++++++++++++++ .../Cuberille/wrapping/test/cuberille_test.py | 30 ++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt create mode 100644 Modules/Filtering/Cuberille/wrapping/test/cuberille_test.py diff --git a/Modules/Filtering/Cuberille/itk-module.cmake b/Modules/Filtering/Cuberille/itk-module.cmake index 96bb41fb8eb..56a9b1eb2b9 100644 --- a/Modules/Filtering/Cuberille/itk-module.cmake +++ b/Modules/Filtering/Cuberille/itk-module.cmake @@ -31,6 +31,10 @@ itk_module( ITKQuadEdgeMeshFiltering ITKThresholding ITKIOImageBase + ITKIOMeta + ITKIONRRD + ITKIOMeshBase + ITKIOVTK DESCRIPTION "${DOCUMENTATION}" EXCLUDE_FROM_DEFAULT ENABLE_SHARED diff --git a/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt b/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt new file mode 100644 index 00000000000..026381ebcdc --- /dev/null +++ b/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt @@ -0,0 +1,41 @@ +itk_python_expression_add_test( + NAME itkCuberilleImageToMeshFilterPythonTest + EXPRESSION "filter = itk.CuberilleImageToMeshFilter.New()" +) + +set(test_input_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../test/Input) +# cmake_path(CONVERT ${test_input_dir} TO_CMAKE_PATH_LIST test_input_dir NORMALIZE) # CMake 3.20+ + +list(FIND ITK_WRAP_IMAGE_DIMS 3 wrap_3_index) +if(wrap_3_index GREATER -1) + itk_python_add_test(NAME Cuberille_blob0_python + TEST_DRIVER_ARGS + COMMAND cuberille_test.py + DATA{${test_input_dir}/blob0.mha} + ${ITK_TEST_OUTPUT_DIR}/blob0.vtk + ) + + itk_python_add_test(NAME Cuberille_blob0_100_python + TEST_DRIVER_ARGS + COMMAND cuberille_test.py + DATA{${test_input_dir}/blob0.mha} + ${ITK_TEST_OUTPUT_DIR}/blob0_100.vtk + 100 # iso-surface value + ) + + itk_python_add_test(NAME Cuberille_hydrogenAtom_35_python + TEST_DRIVER_ARGS + COMMAND cuberille_test.py + DATA{${test_input_dir}/hydrogenAtom.mha} + ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom35.vtk + 35 # iso-surface value + ) + + itk_python_add_test(NAME Cuberille_hydrogenAtom_50_python + TEST_DRIVER_ARGS + COMMAND cuberille_test.py + DATA{${test_input_dir}/hydrogenAtom.mha} + ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom50.vtk + 50 # iso-surface value + ) +endif() diff --git a/Modules/Filtering/Cuberille/wrapping/test/cuberille_test.py b/Modules/Filtering/Cuberille/wrapping/test/cuberille_test.py new file mode 100644 index 00000000000..ee90a25daa7 --- /dev/null +++ b/Modules/Filtering/Cuberille/wrapping/test/cuberille_test.py @@ -0,0 +1,30 @@ +import sys + +import itk + +image = itk.imread(sys.argv[1]) + +if len(sys.argv) > 3: + isosurface = sys.argv[3] +else: + calculator = itk.MinimumMaximumImageCalculator[type(image)].New() + calculator.SetImage(image) + calculator.Compute() + isosurface = (calculator.GetMaximum() + calculator.GetMinimum()) / 2 + +# convert number type +(input_image_template, (input_pixel_type, input_image_dimension)) = itk.template(image) +if input_pixel_type == itk.F or input_pixel_type == itk.D: + isosurface = float(isosurface) +else: + isosurface = int(isosurface) + +mesh = itk.cuberille_image_to_mesh_filter( + image, + generate_triangle_faces=True, + iso_surface_value=isosurface, + project_vertices_to_iso_surface=True, + project_vertex_surface_distance_threshold=0.05, +) + +itk.meshwrite(mesh, sys.argv[2]) From 8848c9d864227a161f9b112530d1947db9b3aad6 Mon Sep 17 00:00:00 2001 From: Davis Marc Vigneault Date: Mon, 24 Nov 2025 12:15:47 -0500 Subject: [PATCH 72/84] ENH: Add test case from GitHub issue #66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Dženan Zukić --- .../Filtering/Cuberille/test/CMakeLists.txt | 7 ++ .../Cuberille/test/CuberilleTest_Issue66.cxx | 113 ++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index 865adeeaf85..4c5f1a2ca5a 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -8,6 +8,7 @@ set( CuberilleTest02.cxx CuberilleTest03.cxx CuberilleTest04.cxx + CuberilleTest_Issue66.cxx ) createtestdriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}") @@ -333,6 +334,12 @@ add_test( # 25 # Maximum number of steps #) +add_test( + NAME CuberilleIssue66 + COMMAND + ${itk-module}TestDriver CuberilleTest_Issue66 +) + itk_add_test( NAME CuberilleTestDirectionMatrix COMMAND diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx new file mode 100644 index 00000000000..408d0b7565f --- /dev/null +++ b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx @@ -0,0 +1,113 @@ +/*========================================================================= + * + * 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. + * + *=========================================================================*/ + +#include +#include +#include +#include +#include + +const unsigned int Dimension = 3; +using TPixel = unsigned char; +using TImage = itk::Image; +// using TMesh = itk::Mesh; +using TMesh = itk::QuadEdgeMesh; +using TExtract = itk::CuberilleImageToMeshFilter; +using TMeshWriter = itk::MeshFileWriter; + +/* This is description from https://github.com/InsightSoftwareConsortium/ITKCuberille/issues/66 + +Imagine an image with only two foreground pixels, offset by {1, 1, 0}. When extracting the surface of this image as a +mesh, it is necessary to duplicate the coincident edge, creating two disconnected cubes, in order to ensure that the +result is manifold. A slice through the z plane would show: + +*---*---* +| A | | +*---*---* +| | B | +*---*---* +Now imagine that these foreground pixels are still flanked by background pixels in the z plain, but are "connected" into +the same connected component by foreground pixels in the two adjacent z plains: + + Z-1 Z Z+1 +*---*---* *---*---* *---*---* +| X | X | | A | | | X | X | +*---*---* *---*---* *---*---* +| | X | | | B | | | X | +*---*---* *---*---* *---*---* +Here, the coincident edge of pixels A and B must again be duplicated; but unlike the first example, the two distinct +edges actually connect the same two vertices. Quite reasonably, when asked to create a face which would require an edge +between two vertices, itk::QuadEdgeMesh checks: +https://github.com/InsightSoftwareConsortium/ITK/blob/v5.4.5/Modules/Core/QuadEdgeMesh/include/itkQuadEdgeMesh.hxx#L1212-L1221 +to see whether there is an existing edge, and refuses to create a new +edge if one already exists. + +Most of the time, this is what we want--but in the case described above, it acts as a bug. It's not immediately obvious +to me how to detect when this check is and is not necessary; as I work through this problem, I thought I would post it +as an issue in case anyone else had any insight--pun intended. ;-D + +*/ + +int +CuberilleTest_Issue66(int argc, char * argv[]) +{ + const auto image = TImage::New(); + TImage::RegionType region({ { 0, 0, 0 } }, { { 5, 4, 4 } }); + image->SetBufferedRegion(region); + image->Allocate(); + image->FillBuffer(0); + + image->SetPixel({ { 1, 1, 1 } }, 1); // A + image->SetPixel({ { 2, 1, 1 } }, 1); // B + + image->SetPixel({ { 3, 1, 1 } }, 1); // C + image->SetPixel({ { 1, 2, 1 } }, 1); // D + image->SetPixel({ { 3, 2, 1 } }, 1); // E + + image->SetPixel({ { 1, 2, 2 } }, 1); // F + image->SetPixel({ { 2, 2, 2 } }, 1); // G + image->SetPixel({ { 3, 2, 2 } }, 1); // H + + itk::WriteImage(image, "input_issue66.nrrd"); + + const auto extract = TExtract::New(); + extract->SetInput(image); + extract->SavePixelAsCellDataOn(); + extract->GenerateTriangleFacesOff(); + extract->ProjectVerticesToIsoSurfaceOff(); + + const auto m_writer = TMeshWriter::New(); + m_writer->SetInput(extract->GetOutput()); + m_writer->SetFileName("mesh.obj"); + m_writer->Update(); + + const auto n_cell = extract->GetOutput()->GetNumberOfCells(); + const auto n_data = extract->GetOutput()->GetCellData()->Size(); + + if (n_cell != n_data) + { + std::cout << "This fails if using itk::QuadEdgeMesh." << std::endl; + std::cout << "n_cell: " << n_cell << std::endl; + std::cout << "n_data: " << n_data << std::endl; + return EXIT_FAILURE; + } + + std::cout << "This succeeds if using itk::Mesh." << std::endl; + + return EXIT_SUCCESS; +} From 2fc9ebdf155cb6c628f945838612a0243f8f7f89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Mon, 24 Nov 2025 12:32:59 -0500 Subject: [PATCH 73/84] ENH: Split the test into two cases The one which was described, and the other which was created using code. --- .../Cuberille/test/CuberilleTest_Issue66.cxx | 87 +++++++++++++------ 1 file changed, 62 insertions(+), 25 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx index 408d0b7565f..9977508b137 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx @@ -63,14 +63,35 @@ as an issue in case anyone else had any insight--pun intended. ;-D */ -int -CuberilleTest_Issue66(int argc, char * argv[]) +TImage::Pointer +case1() { const auto image = TImage::New(); TImage::RegionType region({ { 0, 0, 0 } }, { { 5, 4, 4 } }); image->SetBufferedRegion(region); - image->Allocate(); - image->FillBuffer(0); + image->AllocateInitialized(); // zero filled image + + image->SetPixel({ { 1, 1, 1 } }, 1); // A + image->SetPixel({ { 2, 2, 1 } }, 1); // B + + image->SetPixel({ { 1, 1, 0 } }, 1); // C + image->SetPixel({ { 1, 2, 0 } }, 1); // D + image->SetPixel({ { 2, 2, 0 } }, 1); // E + + image->SetPixel({ { 1, 1, 2 } }, 1); // F + image->SetPixel({ { 1, 2, 2 } }, 1); // G + image->SetPixel({ { 2, 2, 2 } }, 1); // H + + return image; +} + +TImage::Pointer +case2() +{ + const auto image = TImage::New(); + TImage::RegionType region({ { 0, 0, 0 } }, { { 5, 4, 4 } }); + image->SetBufferedRegion(region); + image->AllocateInitialized(); // zero filled image image->SetPixel({ { 1, 1, 1 } }, 1); // A image->SetPixel({ { 2, 1, 1 } }, 1); // B @@ -83,31 +104,47 @@ CuberilleTest_Issue66(int argc, char * argv[]) image->SetPixel({ { 2, 2, 2 } }, 1); // G image->SetPixel({ { 3, 2, 2 } }, 1); // H - itk::WriteImage(image, "input_issue66.nrrd"); + return image; +} - const auto extract = TExtract::New(); - extract->SetInput(image); - extract->SavePixelAsCellDataOn(); - extract->GenerateTriangleFacesOff(); - extract->ProjectVerticesToIsoSurfaceOff(); - const auto m_writer = TMeshWriter::New(); - m_writer->SetInput(extract->GetOutput()); - m_writer->SetFileName("mesh.obj"); - m_writer->Update(); +int +CuberilleTest_Issue66(int argc, char * argv[]) +{ + int countFailed = 0; + for (const auto image : { case1(), case2() }) + { - const auto n_cell = extract->GetOutput()->GetNumberOfCells(); - const auto n_data = extract->GetOutput()->GetCellData()->Size(); + const auto extract = TExtract::New(); + extract->SetInput(image); + extract->SavePixelAsCellDataOn(); + extract->GenerateTriangleFacesOff(); + extract->ProjectVerticesToIsoSurfaceOff(); + + const auto m_writer = TMeshWriter::New(); + m_writer->SetInput(extract->GetOutput()); + m_writer->SetFileName("mesh.obj"); + m_writer->Update(); + + const auto n_cell = extract->GetOutput()->GetNumberOfCells(); + const auto n_data = extract->GetOutput()->GetCellData()->Size(); + + if (n_cell != n_data) + { + std::cout << "This fails if using itk::QuadEdgeMesh." << std::endl; + std::cout << "n_cell: " << n_cell << std::endl; + std::cout << "n_data: " << n_data << std::endl; + ++countFailed; + } + else + { + std::cout << "This succeeds if using itk::Mesh." << std::endl; + } + } - if (n_cell != n_data) + if (countFailed == 0) { - std::cout << "This fails if using itk::QuadEdgeMesh." << std::endl; - std::cout << "n_cell: " << n_cell << std::endl; - std::cout << "n_data: " << n_data << std::endl; - return EXIT_FAILURE; + return EXIT_SUCCESS; } - - std::cout << "This succeeds if using itk::Mesh." << std::endl; - - return EXIT_SUCCESS; + return EXIT_FAILURE; } From d42eaf60922e359f5bd48b1911fc18092316192f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Mon, 24 Nov 2025 12:32:59 -0500 Subject: [PATCH 74/84] ENH: Use itk::Mesh so the test passes --- Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx index 9977508b137..14dd0283ca2 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx @@ -25,8 +25,8 @@ const unsigned int Dimension = 3; using TPixel = unsigned char; using TImage = itk::Image; -// using TMesh = itk::Mesh; -using TMesh = itk::QuadEdgeMesh; +using TMesh = itk::Mesh; +// using TMesh = itk::QuadEdgeMesh; using TExtract = itk::CuberilleImageToMeshFilter; using TMeshWriter = itk::MeshFileWriter; From 55d75c8cb4194bde14c85d48aa7742d840d3582d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C5=BEenan=20Zuki=C4=87?= Date: Mon, 16 Mar 2026 14:13:28 -0400 Subject: [PATCH 75/84] COMP: Fix warning: unused parameter 'argv' ITK/Modules/Remote/Cuberille/test/CuberilleTest_Issue66.cxx:112:40: warning: unused parameter 'argv' [-Wunused-parameter] --- Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx index 14dd0283ca2..51ba04fa5d2 100644 --- a/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx +++ b/Modules/Filtering/Cuberille/test/CuberilleTest_Issue66.cxx @@ -109,7 +109,7 @@ case2() int -CuberilleTest_Issue66(int argc, char * argv[]) +CuberilleTest_Issue66(int, char *[]) { int countFailed = 0; for (const auto image : { case1(), case2() }) From 8df9883e850ffd37d25ed02d1daf38eac7f5295b Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Mon, 4 May 2026 17:30:45 -0500 Subject: [PATCH 76/84] STYLE: Apply gersemi 0.19.3 to Cuberille wrapping test CMakeLists --- .../Cuberille/wrapping/test/CMakeLists.txt | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt b/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt index 026381ebcdc..e61e8418fe7 100644 --- a/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/wrapping/test/CMakeLists.txt @@ -8,34 +8,42 @@ set(test_input_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../test/Input) list(FIND ITK_WRAP_IMAGE_DIMS 3 wrap_3_index) if(wrap_3_index GREATER -1) - itk_python_add_test(NAME Cuberille_blob0_python + itk_python_add_test( + NAME Cuberille_blob0_python TEST_DRIVER_ARGS - COMMAND cuberille_test.py + COMMAND + cuberille_test.py DATA{${test_input_dir}/blob0.mha} ${ITK_TEST_OUTPUT_DIR}/blob0.vtk - ) + ) - itk_python_add_test(NAME Cuberille_blob0_100_python + itk_python_add_test( + NAME Cuberille_blob0_100_python TEST_DRIVER_ARGS - COMMAND cuberille_test.py + COMMAND + cuberille_test.py DATA{${test_input_dir}/blob0.mha} ${ITK_TEST_OUTPUT_DIR}/blob0_100.vtk - 100 # iso-surface value - ) + 100 # iso-surface value + ) - itk_python_add_test(NAME Cuberille_hydrogenAtom_35_python + itk_python_add_test( + NAME Cuberille_hydrogenAtom_35_python TEST_DRIVER_ARGS - COMMAND cuberille_test.py + COMMAND + cuberille_test.py DATA{${test_input_dir}/hydrogenAtom.mha} ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom35.vtk - 35 # iso-surface value - ) + 35 # iso-surface value + ) - itk_python_add_test(NAME Cuberille_hydrogenAtom_50_python + itk_python_add_test( + NAME Cuberille_hydrogenAtom_50_python TEST_DRIVER_ARGS - COMMAND cuberille_test.py + COMMAND + cuberille_test.py DATA{${test_input_dir}/hydrogenAtom.mha} ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom50.vtk - 50 # iso-surface value - ) + 50 # iso-surface value + ) endif() From 30c3dbe6402c75e06dfb1b56597b415785d1ab3b Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Mon, 4 May 2026 17:30:54 -0500 Subject: [PATCH 77/84] DOC: Add Cuberille module README pointing at archived upstream --- Modules/Filtering/Cuberille/README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 Modules/Filtering/Cuberille/README.md diff --git a/Modules/Filtering/Cuberille/README.md b/Modules/Filtering/Cuberille/README.md new file mode 100644 index 00000000000..2a208fcc632 --- /dev/null +++ b/Modules/Filtering/Cuberille/README.md @@ -0,0 +1,22 @@ +# Cuberille + +In-tree ITK module providing the cuberille implicit-surface +extraction algorithm: given a binary or scalar volume and an +iso-value, produce a quad mesh approximating the iso-surface, with +optional vertex normal interpolation and project-to-iso-surface +refinement. + +The flagship class is `itk::CuberilleImageToMeshFilter`. + +## Origin + +Ingested from the standalone remote module +[**InsightSoftwareConsortium/ITKCuberille**](https://github.com/InsightSoftwareConsortium/ITKCuberille) +on 2026-05-04 via the v4 ingestion pipeline. The upstream repository +will be archived read-only after this PR merges; it remains +reachable at the URL above for historical reference. + +## References + +- Herman G.T., Liu H.K. *Three-dimensional display of human organs from computed tomograms.* Computer Graphics and Image Processing. 1979. +- Mueller D. *Cuberille implicit surface polygonization for ITK.* The Insight Journal. 2010. From e0fbc8eaf24bb365807ccaa51daf5881533d7a73 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Mon, 4 May 2026 17:30:58 -0500 Subject: [PATCH 78/84] COMP: Remove Cuberille remote-fetch cmake (now in-tree) --- Modules/Remote/Cuberille.remote.cmake | 72 --------------------------- 1 file changed, 72 deletions(-) delete mode 100644 Modules/Remote/Cuberille.remote.cmake diff --git a/Modules/Remote/Cuberille.remote.cmake b/Modules/Remote/Cuberille.remote.cmake deleted file mode 100644 index 78046b2839e..00000000000 --- a/Modules/Remote/Cuberille.remote.cmake +++ /dev/null @@ -1,72 +0,0 @@ -#-- # Grading Level Criteria Report -#-- EVALUATION DATE: 2020-03-24 -#-- EVALUATORS: [Dženan Zukić, Davis Vigneault] -#-- -#-- ## Compliance level 5 star (AKA ITK main modules, or remote modules that could become core modules) -#-- - [ ] Widespread community dependance -#-- - [X] Above 90% code coverage -#-- - [ ] CI dashboards and testing monitored rigorously -#-- - [X] Key API features are exposed in wrapping interface -#-- - [ ] All requirements of Levels 4,3,2,1 -#-- -#-- ## Compliance Level 4 star (Very high-quality code, perhaps small community dependance) -#-- - [X] Meets all ITK code style standards -#-- - [X] No external requirements beyond those needed by ITK proper -#-- - [ ] Builds and passes tests on all supported platforms within 1 month of each core tagged release -#-- - [ ] Windows Shared Library Build with Visual Studio -#-- - [ ] Mac with clang compiller -#-- - [ ] Linux with gcc compiler -#-- - [X] Active developer community dedicated to maintaining code-base -#-- - [X] 75% code coverage demonstrated for testing suite -#-- - [X] Continuous integration testing performed -#-- - [X] All requirements of Levels 3,2,1 -#-- -#-- ## Compliance Level 3 star (Quality beta code) -#-- - [X] API | executable interface is considered mostly stable and feature complete -#-- - [X] 10% C0-code coverage demonstrated for testing suite -#-- - [X] Some tests exist and pass on at least some platform -#-- - [X] All requirements of Levels 2,1 -#-- -#-- ## Compliance Level 2 star (Alpha code feature API development or niche community/execution environment dependance ) -#-- - [X] Compiles for at least 1 niche set of execution envirionments, and perhaps others -#-- (may depend on specific external tools like a java environment, or specific external libraries to work ) -#-- - [X] All requirements of Levels 1 -#-- -#-- ## Compliance Level 1 star (Pre-alpha features under development and code of unknown quality) -#-- - [X] Code complies on at least 1 platform -#-- -#-- ## Compliance Level 0 star ( Code/Feature of known poor-quality or deprecated status ) -#-- - [ ] Code reviewed and explicitly identified as not recommended for use -#-- -#-- ### Please document here any justification for the criteria above -# Code style enforced by clang-format on 2020-02-19, and clang-tidy modernizations completed -# 2020-05-25 Continuous integration and clang format linting now performed by Github Actions -# 2020-05-25 Line coverage is 99.8%, function coverage is 98.0% - -# Contact: Matt McCormick -itk_fetch_module( - Cuberille - "This module implements cuberille implicit surface -polygonization for ITK. This method operates by diving the surface into a -number of small cubes called cuberilles. Each cuberille is centered at a -pixel lying on the iso-surface and then quadrilaterals are generated for each -face. The original approach is improved by projecting the vertices of each -cuberille onto the implicit surface, smoothing the typical block-like -resultant mesh. - -A more detailed description can be found in the Insight Journal article: - - Mueller, D. \"Cuberille Implicit Surface Polygonization for ITK\" - https://doi.org/10.54294/df9mgw - July-December, 2010. - -And the related: - - Mueller, D. \"Fast Marching Minimal Path Extraction in ITK\" - https://doi.org/10.54294/z5zwhh - January-June, 2008. -" - MODULE_COMPLIANCE_LEVEL 3 - GIT_REPOSITORY https://github.com/InsightSoftwareConsortium/ITKCuberille.git - GIT_TAG a152f89e5d45f7262523446555e1f4da86071a71 - ) From f6a46d2adb50f33b68a6190682714a05673bbd2f Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Mon, 4 May 2026 17:31:00 -0500 Subject: [PATCH 79/84] ENH: Enable Module_Cuberille in configure-ci pixi task --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d75121de752..52c56027848 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ cmd = '''cmake -DCMAKE_C_COMPILER_LAUNCHER:STRING=ccache -DCMAKE_CXX_COMPILER_LAUNCHER:STRING=ccache -DModule_AnisotropicDiffusionLBR:BOOL=ON + -DModule_Cuberille:BOOL=ON -DModule_Montage:BOOL=ON -DModule_FastBilateral:BOOL=ON -DModule_GenericLabelInterpolator:BOOL=ON From 69ec0498852baa4fa4fc72799235d1b904ed67bc Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 4 May 2026 14:57:35 -0500 Subject: [PATCH 80/84] BUG: Move Cuberille debug macros out of public header The four implementation-control macros (DEBUG_PRINT, USE_GRADIENT_RECURSIVE_GAUSSIAN, USE_ADVANCED_PROJECTION, USE_LINESEARCH_PROJECTION) were defined unconditionally at the top of itkCuberilleImageToMeshFilter.h. Any translation unit including the public header silently inherited DEBUG_PRINT=0, masking a same-named macro any consumer might want to test with #ifdef. Move the four #define lines to itkCuberilleImageToMeshFilter.hxx so they remain visible to the template implementation that depends on them via #if, but are no longer emitted into consumer translation units that only include the .h. Greptile P1 on PR #6205. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 5 ----- .../Cuberille/include/itkCuberilleImageToMeshFilter.hxx | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 964df996307..ba2b0ca89b7 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -19,11 +19,6 @@ #ifndef itkCuberilleImageToMeshFilter_h #define itkCuberilleImageToMeshFilter_h -#define DEBUG_PRINT 0 -#define USE_GRADIENT_RECURSIVE_GAUSSIAN 0 -#define USE_ADVANCED_PROJECTION 0 -#define USE_LINESEARCH_PROJECTION 0 - #include "itkMacro.h" #include "itkMesh.h" #include "itkImageToMeshFilter.h" diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 66a2b9bc9ef..58620919774 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -19,6 +19,11 @@ #ifndef itkCuberilleImageToMeshFilter_hxx #define itkCuberilleImageToMeshFilter_hxx +#define DEBUG_PRINT 0 +#define USE_GRADIENT_RECURSIVE_GAUSSIAN 0 +#define USE_ADVANCED_PROJECTION 0 +#define USE_LINESEARCH_PROJECTION 0 + #include "itkMath.h" #include "itkNumericTraits.h" #include From 58c3d1b68884f92f61243c3ecb2306bfb4d00dbc Mon Sep 17 00:00:00 2001 From: Hans Johnson Date: Mon, 4 May 2026 14:57:36 -0500 Subject: [PATCH 81/84] BUG: Fix VertexLookupNode operator>= and operator<= equal-Y branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both operators applied >= / <= on m_Y in their first branch, which short-circuited to true/false whenever the Y values were equal — before the X tiebreaker could be evaluated. E.g. {Y=1,X=0} >= {Y=1,X=5} resolved (1 >= 1) == true and returned true even though the lhs is strictly less than the rhs. Replace the first-branch comparison with strict > / < so equal-Y cases fall through to the X tiebreaker, mirroring the existing operator> and operator< definitions. std::map calls only operator<, so existing callers do not observe the bug, but the broken operators are class-level API and would silently misbehave under direct use. Greptile P1 on PR #6205. --- .../Cuberille/include/itkCuberilleImageToMeshFilter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index ba2b0ca89b7..00e75006dbb 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -291,7 +291,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter=(const Self & node) const { - return (m_Y >= node.m_Y) || ((m_Y == node.m_Y) && (m_X >= node.m_X)); + return (m_Y > node.m_Y) || ((m_Y == node.m_Y) && (m_X >= node.m_X)); } bool operator<(const Self & node) const @@ -301,7 +301,7 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter Date: Mon, 4 May 2026 14:57:45 -0500 Subject: [PATCH 82/84] STYLE: Use itk_add_test for Cuberille CTest registrations Convert the 20 Cuberille tests registered via plain add_test() to itk_add_test() so they pick up the standard ITK CTest labels and properties (matching the three already-itk_add_test'd entries below them). Tests with bare add_test are invisible to dashboard-specific test selectors that filter on those labels. GTest conversion of these tests is a separate follow-up. Greptile P2 on PR #6205. --- .../Filtering/Cuberille/test/CMakeLists.txt | 176 ++++++++++++------ 1 file changed, 117 insertions(+), 59 deletions(-) diff --git a/Modules/Filtering/Cuberille/test/CMakeLists.txt b/Modules/Filtering/Cuberille/test/CMakeLists.txt index 4c5f1a2ca5a..a8373c9f747 100644 --- a/Modules/Filtering/Cuberille/test/CMakeLists.txt +++ b/Modules/Filtering/Cuberille/test/CMakeLists.txt @@ -14,11 +14,14 @@ set( createtestdriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}") # Configure the Tests -add_test( +itk_add_test( NAME Cuberille_Blob0_00 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob0.mha - ${ITK_TEST_OUTPUT_DIR}/blob0-01.vtk 200 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/blob0.mha + ${ITK_TEST_OUTPUT_DIR}/blob0-01.vtk + 200 # Iso-surface value 8 # Expected number of points 6 # Expected number of cells 0 # Generate triangle faces @@ -29,11 +32,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Blob1_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob1.mha - ${ITK_TEST_OUTPUT_DIR}/blob1-01.vtk 200 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/blob1.mha + ${ITK_TEST_OUTPUT_DIR}/blob1-01.vtk + 200 # Iso-surface value 12 # Expected number of points 10 # Expected number of cells 0 # Generate triangle faces @@ -44,11 +50,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Blob2_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob2.mha - ${ITK_TEST_OUTPUT_DIR}/blob2-01.vtk 200 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/blob2.mha + ${ITK_TEST_OUTPUT_DIR}/blob2-01.vtk + 200 # Iso-surface value 16 # Expected number of points 12 # Expected number of cells 0 # Generate triangle faces @@ -59,11 +68,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Blob3_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob3.mha - ${ITK_TEST_OUTPUT_DIR}/blob3-01.vtk 200 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/blob3.mha + ${ITK_TEST_OUTPUT_DIR}/blob3-01.vtk + 200 # Iso-surface value 240 # Expected number of points 180 # Expected number of cells 0 # Generate triangle faces @@ -74,11 +86,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Blob4_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/blob4.mha - ${ITK_TEST_OUTPUT_DIR}/blob4-01.vtk 200 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/blob4.mha + ${ITK_TEST_OUTPUT_DIR}/blob4-01.vtk + 200 # Iso-surface value 2124 # Expected number of points 2122 # Expected number of cells 0 # Generate triangle faces @@ -89,11 +104,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_MarschnerLobb_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/marschnerlobb.mha - ${ITK_TEST_OUTPUT_DIR}/marschnerlobb-01.vtk 55 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/marschnerlobb.mha + ${ITK_TEST_OUTPUT_DIR}/marschnerlobb-01.vtk + 55 # Iso-surface value 22106 # Expected number of points 22104 # Expected number of cells 0 # Generate triangle faces @@ -104,11 +122,14 @@ add_test( 200 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Fuel_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha - ${ITK_TEST_OUTPUT_DIR}/fuel-01.vtk 15 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/fuel.mha + ${ITK_TEST_OUTPUT_DIR}/fuel-01.vtk + 15 # Iso-surface value 5302 # Expected number of points 5316 # Expected number of cells 0 # Generate triangle faces @@ -119,11 +140,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Fuel_02 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha - ${ITK_TEST_OUTPUT_DIR}/fuel-02.vtk 15 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/fuel.mha + ${ITK_TEST_OUTPUT_DIR}/fuel-02.vtk + 15 # Iso-surface value 5302 # Expected number of points 5316 # Expected number of cells 0 # Generate triangle faces @@ -134,11 +158,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Fuel_03 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/fuel.mha - ${ITK_TEST_OUTPUT_DIR}/fuel-03.vtk 15 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/fuel.mha + ${ITK_TEST_OUTPUT_DIR}/fuel-03.vtk + 15 # Iso-surface value 5302 # Expected number of points 10632 # Expected number of cells 1 # Generate triangle faces @@ -149,11 +176,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_HydrogenAtom_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/hydrogenAtom.mha - ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom-01.vtk 15 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/hydrogenAtom.mha + ${ITK_TEST_OUTPUT_DIR}/hydrogenAtom-01.vtk + 15 # Iso-surface value 29880 # Expected number of points 29874 # Expected number of cells 0 # Generate triangle faces @@ -164,11 +194,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Neghip_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha - ${ITK_TEST_OUTPUT_DIR}/neghip-01.vtk 55 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/neghip.mha + ${ITK_TEST_OUTPUT_DIR}/neghip-01.vtk + 55 # Iso-surface value 15164 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces @@ -179,11 +212,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Neghip_02 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha - ${ITK_TEST_OUTPUT_DIR}/neghip-02.vtk 55 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/neghip.mha + ${ITK_TEST_OUTPUT_DIR}/neghip-02.vtk + 55 # Iso-surface value 15164 # Expected number of points 15136 # Expected number of cells 0 # Generate triangle faces @@ -194,11 +230,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Neghip_03 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/neghip.mha - ${ITK_TEST_OUTPUT_DIR}/neghip-03.vtk 55 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/neghip.mha + ${ITK_TEST_OUTPUT_DIR}/neghip-03.vtk + 55 # Iso-surface value 15164 # Expected number of points 30272 # Expected number of cells 1 # Generate triangle faces @@ -209,11 +248,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Nucleon_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha - ${ITK_TEST_OUTPUT_DIR}/nucleon-01.vtk 140 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/nucleon.mha + ${ITK_TEST_OUTPUT_DIR}/nucleon-01.vtk + 140 # Iso-surface value 3504 # Expected number of points 3500 # Expected number of cells 0 # Generate triangle faces @@ -224,11 +266,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Nucleon_02 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha - ${ITK_TEST_OUTPUT_DIR}/nucleon-02.vtk 140 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/nucleon.mha + ${ITK_TEST_OUTPUT_DIR}/nucleon-02.vtk + 140 # Iso-surface value 3504 # Expected number of points 3500 # Expected number of cells 0 # Generate triangle faces @@ -239,11 +284,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Nucleon_03 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/nucleon.mha - ${ITK_TEST_OUTPUT_DIR}/nucleon-03.vtk 140 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/nucleon.mha + ${ITK_TEST_OUTPUT_DIR}/nucleon-03.vtk + 140 # Iso-surface value 3504 # Expected number of points 7000 # Expected number of cells 1 # Generate triangle faces @@ -254,11 +302,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Silicium_01 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha - ${ITK_TEST_OUTPUT_DIR}/silicium-01.vtk 85 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/silicium.mha + ${ITK_TEST_OUTPUT_DIR}/silicium-01.vtk + 85 # Iso-surface value 20036 # Expected number of points 20024 # Expected number of cells 0 # Generate triangle faces @@ -269,11 +320,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Silicium_02 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha - ${ITK_TEST_OUTPUT_DIR}/silicium-02.vtk 85 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/silicium.mha + ${ITK_TEST_OUTPUT_DIR}/silicium-02.vtk + 85 # Iso-surface value 20036 # Expected number of points 20024 # Expected number of cells 0 # Generate triangle faces @@ -284,11 +338,14 @@ add_test( 100 # Maximum number of steps ) -add_test( +itk_add_test( NAME Cuberille_Silicium_03 COMMAND - CuberilleTestDriver CuberilleTest01 ${DATA_DIR}/silicium.mha - ${ITK_TEST_OUTPUT_DIR}/silicium-03.vtk 85 # Iso-surface value + CuberilleTestDriver + CuberilleTest01 + ${DATA_DIR}/silicium.mha + ${ITK_TEST_OUTPUT_DIR}/silicium-03.vtk + 85 # Iso-surface value 20036 # Expected number of points 40048 # Expected number of cells 1 # Generate triangle faces @@ -334,10 +391,11 @@ add_test( # 25 # Maximum number of steps #) -add_test( +itk_add_test( NAME CuberilleIssue66 COMMAND - ${itk-module}TestDriver CuberilleTest_Issue66 + ${itk-module}TestDriver + CuberilleTest_Issue66 ) itk_add_test( From 9ad68e003d4805f837e3c99de66852f7d84d41a4 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Mon, 4 May 2026 17:51:15 -0500 Subject: [PATCH 83/84] BUG: Remove DEBUG_PRINT macro and all guarded debug-tracing blocks DEBUG_PRINT was a debug-tracing toggle defined to 0 (compile-time disabled) since the module's first commit; the eight #if DEBUG_PRINT...#endif blocks they guarded contained std::cout traces from algorithm bring-up that were never re-enabled. Removing the macro and the dead code per the reviewer's preference for cleaner module headers. Behavior is unchanged because all guarded blocks were #if 0. --- .../include/itkCuberilleImageToMeshFilter.h | 3 -- .../include/itkCuberilleImageToMeshFilter.hxx | 44 ------------------- 2 files changed, 47 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 00e75006dbb..6b12ec94811 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -415,9 +415,6 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter void CuberilleImageToMeshFilter::GenerateData() { -#if DEBUG_PRINT - m_ProjectVertexTerminate[0] = 0; - m_ProjectVertexTerminate[1] = 0; - m_ProjectVertexTerminate[2] = 0; -#endif // Get input/output InputImageConstPointer image = Superclass::GetInput(0); @@ -259,14 +253,6 @@ CuberilleImageToMeshFilter::GenerateDat } // end if num faces > 0 } - -#if DEBUG_PRINT - std::cout << "Number of terminations due to surface distance threshold: " << m_ProjectVertexTerminate[0] << std::endl; - std::cout << "Number of terminations due to maximum number of steps: " << m_ProjectVertexTerminate[1] << std::endl; -# if USE_ADVANCED_PROJECTION - std::cout << "Number of terminations due to too many sign swaps: " << m_ProjectVertexTerminate[2] << std::endl; -# endif -#endif } template @@ -506,12 +492,6 @@ CuberilleImageToMeshFilter::ProjectVert // Determine whether vertex is close enough to iso-surface value done |= diff[i] < m_ProjectVertexSurfaceDistanceThreshold; -# if DEBUG_PRINT - if (done) - { - m_ProjectVertexTerminate[0]++; - } -# endif if (done) { break; @@ -519,12 +499,6 @@ CuberilleImageToMeshFilter::ProjectVert // Determine whether we have done enough steps done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; -# if DEBUG_PRINT - if (done) - { - m_ProjectVertexTerminate[1]++; - } -# endif if (done) { break; @@ -532,12 +506,6 @@ CuberilleImageToMeshFilter::ProjectVert // Determine whether there has been too many sign swaps (oscillating) done |= (swaps >= 5); -# if DEBUG_PRINT - if (done) - { - m_ProjectVertexTerminate[2]++; - } -# endif if (done) { break; @@ -608,12 +576,6 @@ CuberilleImageToMeshFilter::ProjectVert // Compute whether vertex is close enough to iso-surface value value = m_Interpolator->Evaluate(vertex); done |= itk::Math::abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; -# if DEBUG_PRINT - if (done) - { - m_ProjectVertexTerminate[0]++; - } -# endif if (done) { break; @@ -627,12 +589,6 @@ CuberilleImageToMeshFilter::ProjectVert } step *= m_ProjectVertexStepLengthRelaxationFactor; done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; -# if DEBUG_PRINT - if (done) - { - m_ProjectVertexTerminate[1]++; - } -# endif } #endif } From 65bf4daabb58fc076ebc8c2a1d96ba9b2eded5d3 Mon Sep 17 00:00:00 2001 From: "Hans J. Johnson" Date: Mon, 4 May 2026 17:57:51 -0500 Subject: [PATCH 84/84] ENH: Convert Cuberille USE_* macros to class-level static constexpr bool Replaces three preprocessor toggles USE_GRADIENT_RECURSIVE_GAUSSIAN USE_ADVANCED_PROJECTION USE_LINESEARCH_PROJECTION with class-level `static constexpr bool` members of the same intent. Preprocessor `#if FLAG` becomes `if constexpr (Flag)` and a `#if/#else` selecting between two GradientFilterType type aliases becomes `std::conditional_t<...>`. Eliminates global-namespace preprocessor pollution flagged in the Greptile draft review (P1) and matches dzenanz's reviewer preference for class-level configuration over file-level macros. Default values remain false (matching the prior `#define X 0`) so behavior is unchanged. The dead linesearch branch had a stray top-level `break;` outside any loop (a long-standing bit-rot in the disabled-since-2014 alternative path). Replaced with `return;` (the void function's natural early-out) so the branch parses cleanly under `if constexpr` instantiation rules. --- .../include/itkCuberilleImageToMeshFilter.h | 20 +- .../include/itkCuberilleImageToMeshFilter.hxx | 253 +++++++++--------- 2 files changed, 139 insertions(+), 134 deletions(-) diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h index 6b12ec94811..57a5ed4923c 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.h @@ -32,6 +32,8 @@ #include "itkGradientRecursiveGaussianImageFilter.h" #include "itkVectorLinearInterpolateImageFunction.h" +#include + namespace itk { @@ -126,6 +128,16 @@ class CuberilleImageToMeshFilter : public ImageToMeshFilter; -#if USE_GRADIENT_RECURSIVE_GAUSSIAN - using GradientFilterType = GradientRecursiveGaussianImageFilter; -#else - using GradientFilterType = GradientImageFilter; -#endif + using GradientFilterType = std::conditional_t, + GradientImageFilter>; using GradientFilterPointer = typename GradientFilterType::Pointer; using GradientImageType = typename GradientFilterType::OutputImageType; using GradientImagePointer = typename GradientImageType::Pointer; diff --git a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx index 5ffad0ee84c..e95cbde8bad 100644 --- a/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx +++ b/Modules/Filtering/Cuberille/include/itkCuberilleImageToMeshFilter.hxx @@ -19,10 +19,6 @@ #ifndef itkCuberilleImageToMeshFilter_hxx #define itkCuberilleImageToMeshFilter_hxx -#define USE_GRADIENT_RECURSIVE_GAUSSIAN 0 -#define USE_ADVANCED_PROJECTION 0 -#define USE_LINESEARCH_PROJECTION 0 - #include "itkMath.h" #include "itkNumericTraits.h" #include @@ -447,150 +443,148 @@ template void CuberilleImageToMeshFilter::ProjectVertexToIsoSurface(PointType & vertex) { -#if USE_ADVANCED_PROJECTION - // Set up - bool done = false; - double step = m_ProjectVertexStepLength; - unsigned int numberOfSteps = 0; - PointType temp[2]; - PointType tempBest; - InterpolatorOutputType value[2]; - double diff[2]; - double diffBest = -1.0; - unsigned int i, swaps = 0; - int previousi = -1; - - while (!done) + if constexpr (UseAdvancedProjection) { - // Compute normal vector - GradientPixelType normal = m_GradientInterpolator->Evaluate(vertex); - if (normal.Normalize() == 0.0) // old norm was zero - { - break; - } - - // Step along both directions of normal - for (i = 0; i < InputImageType::ImageDimension; ++i) - { - temp[0][i] = vertex[i] + (normal[i] * +1.0 * step); - temp[1][i] = vertex[i] + (normal[i] * -1.0 * step); - } - step *= m_ProjectVertexStepLengthRelaxationFactor; - - // Compute which direction moves vertex closer to iso-surface value - value[0] = m_Interpolator->Evaluate(temp[0]); - value[1] = m_Interpolator->Evaluate(temp[1]); - diff[0] = itk::Math::abs(value[0] - m_IsoSurfaceValue); - diff[1] = itk::Math::abs(value[1] - m_IsoSurfaceValue); - i = (diff[0] <= diff[1]) ? 0 : 1; - if (previousi < 0) - { - previousi = i; - } - swaps += (int)(previousi != i); - vertex = temp[i]; - - // Determine whether vertex is close enough to iso-surface value - done |= diff[i] < m_ProjectVertexSurfaceDistanceThreshold; - if (done) + // Set up + bool done = false; + double step = m_ProjectVertexStepLength; + unsigned int numberOfSteps = 0; + PointType temp[2]; + InterpolatorOutputType value[2]; + double diff[2]; + unsigned int i, swaps = 0; + int previousi = -1; + + while (!done) { - break; - } + // Compute normal vector + GradientPixelType normal = m_GradientInterpolator->Evaluate(vertex); + if (normal.Normalize() == 0.0) // old norm was zero + { + break; + } - // Determine whether we have done enough steps - done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; - if (done) - { - break; - } + // Step along both directions of normal + for (i = 0; i < InputImageType::ImageDimension; ++i) + { + temp[0][i] = vertex[i] + (normal[i] * +1.0 * step); + temp[1][i] = vertex[i] + (normal[i] * -1.0 * step); + } + step *= m_ProjectVertexStepLengthRelaxationFactor; + + // Compute which direction moves vertex closer to iso-surface value + value[0] = m_Interpolator->Evaluate(temp[0]); + value[1] = m_Interpolator->Evaluate(temp[1]); + diff[0] = itk::Math::abs(value[0] - m_IsoSurfaceValue); + diff[1] = itk::Math::abs(value[1] - m_IsoSurfaceValue); + i = (diff[0] <= diff[1]) ? 0 : 1; + if (previousi < 0) + { + previousi = i; + } + swaps += (int)(previousi != i); + vertex = temp[i]; - // Determine whether there has been too many sign swaps (oscillating) - done |= (swaps >= 5); - if (done) - { - break; - } - } -#elif USE_LINESEARCH_PROJECTION - - // Set up - unsigned int k; - GradientPixelType normal; - InterpolatorOutputType value; - PointType temp, bestVertex; - double sign, d, metric, bestMetric = 10000; - - // Compute normal vector - normal = m_GradientInterpolator->Evaluate(vertex); - if (normal.Normalize() == 0.0) // old norm was zero - { - break; - } + // Determine whether vertex is close enough to iso-surface value + done |= diff[i] < m_ProjectVertexSurfaceDistanceThreshold; + if (done) + { + break; + } - // Search on both sides of the line - for (sign = -1.0; sign <= 1.0; sign += 2.0) - { - k = (sign == -1.0) ? 0 : 1; - for (unsigned int j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; ++j) - { - // Compute current location along line - d = (double)j / ((double)m_ProjectVertexMaximumNumberOfSteps / 2.0); - for (unsigned int i = 0; i < InputImageType::ImageDimension; ++i) + // Determine whether we have done enough steps + done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; + if (done) { - temp[i] = vertex[i] + (normal[i] * sign * m_ProjectVertexStepLength * d); + break; } - // Compute metric (combination of difference and distance) - value = m_Interpolator->Evaluate(temp); - metric = itk::Math::abs(value - m_IsoSurfaceValue); // Difference - // metric /= NumericTraits::max(); // Normalized difference - // metric /= d; // Distance - - // Determine if current position is the "best" - if (metric < bestMetric) + + // Determine whether there has been too many sign swaps (oscillating) + done |= (swaps >= 5); + if (done) { - bestMetric = metric; - bestVertex = temp; + break; } } } - vertex = bestVertex; - -#else - // Set up - bool done = false; - double sign = 1.0; - double step = m_ProjectVertexStepLength; - unsigned int numberOfSteps = 0; - GradientPixelType normal; - InterpolatorOutputType value; - - while (!done) + else if constexpr (UseLineSearchProjection) { + + // Set up + GradientPixelType normal; + InterpolatorOutputType value; + PointType temp, bestVertex; + double sign, d, metric, bestMetric = 10000; + // Compute normal vector normal = m_GradientInterpolator->Evaluate(vertex); if (normal.Normalize() == 0.0) // old norm was zero { - break; + return; } - // Compute whether vertex is close enough to iso-surface value - value = m_Interpolator->Evaluate(vertex); - done |= itk::Math::abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; - if (done) + // Search on both sides of the line + for (sign = -1.0; sign <= 1.0; sign += 2.0) { - break; - } + for (unsigned int j = 1; j < m_ProjectVertexMaximumNumberOfSteps / 2; ++j) + { + // Compute current location along line + d = (double)j / ((double)m_ProjectVertexMaximumNumberOfSteps / 2.0); + for (unsigned int i = 0; i < InputImageType::ImageDimension; ++i) + { + temp[i] = vertex[i] + (normal[i] * sign * m_ProjectVertexStepLength * d); + } + // Compute metric (combination of difference and distance) + value = m_Interpolator->Evaluate(temp); + metric = itk::Math::abs(value - m_IsoSurfaceValue); // Difference - // Step along the normal towards the iso-surface value - sign = (value < m_IsoSurfaceValue) ? +1.0 : -1.0; - for (unsigned int i = 0; i < InputImageType::ImageDimension; ++i) + // Determine if current position is the "best" + if (metric < bestMetric) + { + bestMetric = metric; + bestVertex = temp; + } + } + } + vertex = bestVertex; + } + else + { + // Set up + bool done = false; + double sign = 1.0; + double step = m_ProjectVertexStepLength; + unsigned int numberOfSteps = 0; + GradientPixelType normal; + InterpolatorOutputType value; + + while (!done) { - vertex[i] += (normal[i] * sign * step); + // Compute normal vector + normal = m_GradientInterpolator->Evaluate(vertex); + if (normal.Normalize() == 0.0) // old norm was zero + { + break; + } + + // Compute whether vertex is close enough to iso-surface value + value = m_Interpolator->Evaluate(vertex); + done |= itk::Math::abs(value - m_IsoSurfaceValue) < m_ProjectVertexSurfaceDistanceThreshold; + if (done) + { + break; + } + + // Step along the normal towards the iso-surface value + sign = (value < m_IsoSurfaceValue) ? +1.0 : -1.0; + for (unsigned int i = 0; i < InputImageType::ImageDimension; ++i) + { + vertex[i] += (normal[i] * sign * step); + } + step *= m_ProjectVertexStepLengthRelaxationFactor; + done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; } - step *= m_ProjectVertexStepLengthRelaxationFactor; - done |= numberOfSteps++ > m_ProjectVertexMaximumNumberOfSteps; } -#endif } template @@ -601,10 +595,11 @@ CuberilleImageToMeshFilter::ComputeGrad { typename GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); gradientFilter->SetInput(Superclass::GetInput(0)); -#if USE_GRADIENT_RECURSIVE_GAUSSIAN - gradientFilter->SetSigma(m_MaxSpacing * 1.0); - gradientFilter->SetNormalizeAcrossScale(true); -#endif + if constexpr (UseGradientRecursiveGaussian) + { + gradientFilter->SetSigma(m_MaxSpacing * 1.0); + gradientFilter->SetNormalizeAcrossScale(true); + } gradientFilter->Update(); m_GradientInterpolator = GradientInterpolatorType::New(); m_GradientInterpolator->SetInputImage(gradientFilter->GetOutput());