├── test ├── Input │ ├── PacMan.png.md5 │ ├── Cos3D_Noisy.vtk.md5 │ ├── FingerPrint.png.md5 │ ├── Lena_Detail.png.md5 │ ├── Triangle.png.md5 │ ├── mrbrain_noisy.vtk.md5 │ ├── Oscillations_Noisy1.png.md5 │ └── VectorField_CircleOpposites.vtk.md5 ├── Baseline │ ├── PacMan_I.png.md5 │ ├── Cos3D_cCED.vtk.md5 │ ├── PacMan_cCED.png.md5 │ ├── PacMan_cEED.png.md5 │ ├── Triangle_EED.png.md5 │ ├── Triangle_cEED.png.md5 │ ├── mrbrain_cEED.vtk.md5 │ ├── FingerPrint_I_20.png.md5 │ ├── FingerPrint_cCED_20.png.md5 │ ├── FingerPrint_cEED_20.png.md5 │ ├── Lena_Detail_I_2.png.md5 │ ├── Lena_Detail_cCED_2.png.md5 │ ├── Lena_Detail_cEED_2.png.md5 │ ├── Oscillations1_CED.png.md5 │ ├── Oscillations1_cCED.png.md5 │ └── VectorField_Circle_cEED.vtk.md5 ├── CoherenceEnhancingDiffusionTest.cxx └── CMakeLists.txt ├── examples ├── Data │ └── PacMan.png ├── CMakeLists.txt └── CoherenceEnhancingDiffusion.cxx ├── wrapping ├── CMakeLists.txt ├── itkAnisotropicDiffusionLBRImageFilter.wrap └── itkCoherenceEnhancingDiffusionImageFilter.wrap ├── ITKKWStyleOverwrite.txt ├── CTestConfig.cmake ├── .github └── workflows │ ├── clang-format-linter.yml │ ├── apply-clang-format.yml │ └── build-test-package.yml ├── CMakeLists.txt ├── .gitattributes ├── itk-module.cmake ├── Old ├── LinearAnisotropicDiffusionTest.cxx └── CMakeLists.txt ├── include ├── itkAnisotropicDiffusionLBRMacro.h ├── itkCoherenceEnhancingDiffusionImageFilter.hxx ├── itkUnaryFunctorWithIndexImageFilter.h ├── itkCoherenceEnhancingDiffusionImageFilter.h ├── itkStructureTensorImageFilter.h ├── itkAnisotropicDiffusionLBRImageFilter.h ├── itkLinearAnisotropicDiffusionLBRImageFilter.h ├── itkAnisotropicDiffusionLBRImageFilter.hxx ├── itkStructureTensorImageFilter.hxx ├── CoherenceEnhancingDiffusionCommandLine.h ├── LinearAnisotropicDiffusionCommandLine.h └── itkLinearAnisotropicDiffusionLBRImageFilter.hxx ├── README.rst ├── pyproject.toml ├── .clang-format └── LICENSE /test/Input/PacMan.png.md5: -------------------------------------------------------------------------------- 1 | d7955368c6f49cbb451d8901aa40add6 -------------------------------------------------------------------------------- /test/Baseline/PacMan_I.png.md5: -------------------------------------------------------------------------------- 1 | c5b358267defea8babcfebbc66c9fa8b -------------------------------------------------------------------------------- /test/Input/Cos3D_Noisy.vtk.md5: -------------------------------------------------------------------------------- 1 | 3a7d9131a732794fcb4100909cd3fd1c -------------------------------------------------------------------------------- /test/Input/FingerPrint.png.md5: -------------------------------------------------------------------------------- 1 | ed7342b4598d44574b2714834b705cad -------------------------------------------------------------------------------- /test/Input/Lena_Detail.png.md5: -------------------------------------------------------------------------------- 1 | 45a1845c6fa452c7465bebda5bbe9b0f -------------------------------------------------------------------------------- /test/Input/Triangle.png.md5: -------------------------------------------------------------------------------- 1 | bce40d3af4f491d728aaba8bb8c9ede9 -------------------------------------------------------------------------------- /test/Baseline/Cos3D_cCED.vtk.md5: -------------------------------------------------------------------------------- 1 | df95fdb0657f7f8472bdc16c73c5bed0 -------------------------------------------------------------------------------- /test/Baseline/PacMan_cCED.png.md5: -------------------------------------------------------------------------------- 1 | fd8d652016508d93ee861c1db83f3ed4 -------------------------------------------------------------------------------- /test/Baseline/PacMan_cEED.png.md5: -------------------------------------------------------------------------------- 1 | edf293e2cce2eae1df4f8598e8179641 -------------------------------------------------------------------------------- /test/Baseline/Triangle_EED.png.md5: -------------------------------------------------------------------------------- 1 | f823b62e9135a37c7438fa07a9e54096 -------------------------------------------------------------------------------- /test/Baseline/Triangle_cEED.png.md5: -------------------------------------------------------------------------------- 1 | 12473a0cb8d3afa0f8d7eb4f61e6216b -------------------------------------------------------------------------------- /test/Baseline/mrbrain_cEED.vtk.md5: -------------------------------------------------------------------------------- 1 | db41c262fba84a75eb399e1e154a5974 -------------------------------------------------------------------------------- /test/Input/mrbrain_noisy.vtk.md5: -------------------------------------------------------------------------------- 1 | ff88f04e75dc478b283ecdf39d8d7687 -------------------------------------------------------------------------------- /test/Baseline/FingerPrint_I_20.png.md5: -------------------------------------------------------------------------------- 1 | e4e5e233b434ea4c85059d7c62f15554 -------------------------------------------------------------------------------- /test/Baseline/FingerPrint_cCED_20.png.md5: -------------------------------------------------------------------------------- 1 | 07435f1d44aeb66fd98e642945437662 -------------------------------------------------------------------------------- /test/Baseline/FingerPrint_cEED_20.png.md5: -------------------------------------------------------------------------------- 1 | 5e752e3fa0e46bb530fedc94c7794c73 -------------------------------------------------------------------------------- /test/Baseline/Lena_Detail_I_2.png.md5: -------------------------------------------------------------------------------- 1 | ff36663855e6794712b081689aac70e5 -------------------------------------------------------------------------------- /test/Baseline/Lena_Detail_cCED_2.png.md5: -------------------------------------------------------------------------------- 1 | b741b80ce65e20c59f286244f621344a -------------------------------------------------------------------------------- /test/Baseline/Lena_Detail_cEED_2.png.md5: -------------------------------------------------------------------------------- 1 | c606fea9c82019d1b4e80d351b803d92 -------------------------------------------------------------------------------- /test/Baseline/Oscillations1_CED.png.md5: -------------------------------------------------------------------------------- 1 | 50da37ff706c93536c0f33390da4287c -------------------------------------------------------------------------------- /test/Baseline/Oscillations1_cCED.png.md5: -------------------------------------------------------------------------------- 1 | 21e83dc09f4c58a44eeb676e49ec3d99 -------------------------------------------------------------------------------- /test/Input/Oscillations_Noisy1.png.md5: -------------------------------------------------------------------------------- 1 | 75818e9d765fb6838a8cf5845ac19b9a -------------------------------------------------------------------------------- /test/Baseline/VectorField_Circle_cEED.vtk.md5: -------------------------------------------------------------------------------- 1 | 23c4495de1a746648418abc144972e92 -------------------------------------------------------------------------------- /test/Input/VectorField_CircleOpposites.vtk.md5: -------------------------------------------------------------------------------- 1 | 0a9e85b2b8dfadb4ab25b828a0f23852 -------------------------------------------------------------------------------- /examples/Data/PacMan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InsightSoftwareConsortium/ITKAnisotropicDiffusionLBR/HEAD/examples/Data/PacMan.png -------------------------------------------------------------------------------- /wrapping/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | itk_wrap_module(AnisotropicDiffusionLBR) 2 | set(WRAPPING_SUBMODULE_ORDER 3 | itkAnisotropicDiffusionLBRImageFilter 4 | ) 5 | itk_auto_load_and_end_wrap_submodules() 6 | -------------------------------------------------------------------------------- /wrapping/itkAnisotropicDiffusionLBRImageFilter.wrap: -------------------------------------------------------------------------------- 1 | itk_wrap_class("itk::AnisotropicDiffusionLBRImageFilter" POINTER) 2 | itk_wrap_image_filter("${WRAP_ITK_SCALAR}" 1 "2;3") 3 | itk_end_wrap_class() 4 | -------------------------------------------------------------------------------- /wrapping/itkCoherenceEnhancingDiffusionImageFilter.wrap: -------------------------------------------------------------------------------- 1 | itk_wrap_class("itk::CoherenceEnhancingDiffusionImageFilter" POINTER_WITH_SUPERCLASS) 2 | itk_wrap_image_filter("${WRAP_ITK_SCALAR}" 1 "2;3") 3 | itk_end_wrap_class() 4 | -------------------------------------------------------------------------------- /ITKKWStyleOverwrite.txt: -------------------------------------------------------------------------------- 1 | itkAnisotropicDiffusionLBRImageFilter\.h InternalVariables Disable 2 | itkLinearAnisotropicDiffusionLBRImageFilter\.h InternalVariables Disable 3 | itkLinearAnisotropicDiffusionLBRImageFilter\.hxx DeclarationOrder Disable 4 | -------------------------------------------------------------------------------- /CTestConfig.cmake: -------------------------------------------------------------------------------- 1 | set(CTEST_PROJECT_NAME "ITK") 2 | set(CTEST_NIGHTLY_START_TIME "1:00:00 UTC") 3 | 4 | set(CTEST_DROP_METHOD "https") 5 | set(CTEST_DROP_SITE "open.cdash.org") 6 | set(CTEST_DROP_LOCATION "/submit.php?project=Insight") 7 | set(CTEST_DROP_SITE_CDASH TRUE) 8 | -------------------------------------------------------------------------------- /.github/workflows/clang-format-linter.yml: -------------------------------------------------------------------------------- 1 | name: clang-format linter 2 | 3 | on: [push,pull_request] 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v5 11 | 12 | - uses: InsightSoftwareConsortium/ITKClangFormatLinterAction@main 13 | with: 14 | itk-branch: main 15 | -------------------------------------------------------------------------------- /.github/workflows/apply-clang-format.yml: -------------------------------------------------------------------------------- 1 | name: Apply clang-format to PR 2 | 3 | on: 4 | pull_request: 5 | types: [labeled] 6 | 7 | jobs: 8 | clang-format: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: InsightSoftwareConsortium/ITKApplyClangFormatAction@master 14 | with: 15 | github-token: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.3) 2 | 3 | project(AnisotropicDiffusionLBR 4 | VERSION 5.1.0 #Version should track with ITK 5 | LANGUAGES CXX) 6 | 7 | # Header only module, no libraries set(AnisotropicDiffusionLBR_LIBRARIES AnisotropicDiffusionLBR) 8 | 9 | if(NOT ITK_SOURCE_DIR) 10 | find_package(ITK 5.1 REQUIRED) 11 | list(APPEND CMAKE_MODULE_PATH ${ITK_CMAKE_DIR}) 12 | include(ITKModuleExternal) 13 | else() 14 | set(ITK_DIR ${CMAKE_BINARY_DIR}) 15 | itk_module_impl() 16 | endif() 17 | 18 | itk_module_examples() 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Custom attribute to mark sources as using our C++/C code style. 2 | [attr]our-c-style whitespace=tab-in-indent,no-lf-at-eof hooks.style=KWStyle,clangformat 3 | 4 | *.c our-c-style 5 | *.h our-c-style 6 | *.cxx our-c-style 7 | *.hxx our-c-style 8 | *.txx our-c-style 9 | *.txt whitespace=tab-in-indent,no-lf-at-eof 10 | *.cmake whitespace=tab-in-indent,no-lf-at-eof 11 | 12 | # ExternalData content links must have LF newlines 13 | *.md5 crlf=input 14 | *.sha512 crlf=input 15 | -------------------------------------------------------------------------------- /itk-module.cmake: -------------------------------------------------------------------------------- 1 | # the top-level README is used for describing this module, just 2 | # re-used it for documentation here 3 | get_filename_component(MY_CURRENT_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 4 | file(READ "${MY_CURRENT_DIR}/README.rst" DOCUMENTATION) 5 | 6 | # define the dependencies of the include module and the tests 7 | itk_module(AnisotropicDiffusionLBR 8 | DEPENDS 9 | ITKCommon 10 | ITKIOImageBase 11 | ITKIOSpatialObjects 12 | ITKMetaIO 13 | ITKImageGradient 14 | TEST_DEPENDS 15 | ITKTestKernel 16 | DESCRIPTION 17 | "${DOCUMENTATION}" 18 | EXCLUDE_FROM_DEFAULT 19 | # Header only library, no ENABLE_SHARED 20 | ) 21 | -------------------------------------------------------------------------------- /.github/workflows/build-test-package.yml: -------------------------------------------------------------------------------- 1 | 2 | name: Build, test, package 3 | 4 | 5 | on: 6 | push: 7 | branches: 8 | - main 9 | tags: 10 | - 'v*' 11 | pull_request: 12 | branches: 13 | - main 14 | 15 | jobs: 16 | cxx-build-workflow: 17 | uses: InsightSoftwareConsortium/ITKRemoteModuleBuildTestPackageAction/.github/workflows/build-test-cxx.yml@main 18 | with: 19 | warnings-to-ignore: "ld. warning. ignoring duplicate libraries. \'-lm\'" 20 | 21 | python-build-workflow: 22 | uses: InsightSoftwareConsortium/ITKRemoteModuleBuildTestPackageAction/.github/workflows/build-test-package-python.yml@main 23 | with: 24 | test-notebooks: false 25 | secrets: 26 | pypi_password: ${{ secrets.pypi_password }} 27 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10.2) 2 | 3 | project(AnisotropicDiffusionLBR_Examples) 4 | 5 | find_package(ITK REQUIRED 6 | COMPONENTS 7 | AnisotropicDiffusionLBR 8 | ITKIOImageBase 9 | ITKIOPNG 10 | ) 11 | include(${ITK_USE_FILE}) 12 | 13 | 14 | add_executable(CoherenceEnhancingDiffusion 15 | CoherenceEnhancingDiffusion.cxx) 16 | target_link_libraries(CoherenceEnhancingDiffusion 17 | ${ITK_LIBRARIES}) 18 | 19 | 20 | enable_testing() 21 | set(INPUT_DATA_DIR ${AnisotropicDiffusionLBR_SOURCE_DIR}/examples/Data) 22 | set(TEST_OUTPUT_DIR ${AnisotropicDiffusionLBR_BINARY_DIR}/Testing/Temporary) 23 | 24 | 25 | 26 | add_test(NAME CoherenceEnhancingDiffusionTest 27 | COMMAND CoherenceEnhancingDiffusion ${INPUT_DATA_DIR}/PacMan.png 28 | ${TEST_OUTPUT_DIR}/PacManFiltered.png 29 | 20 0.05 cCED 3) 30 | -------------------------------------------------------------------------------- /examples/CoherenceEnhancingDiffusion.cxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | #include 19 | #include 20 | #include "CoherenceEnhancingDiffusionCommandLine.h" 21 | 22 | int 23 | main(int argc, char ** argv) 24 | { 25 | try 26 | { 27 | return CoherenceEnhancingDiffusionCommandLine::Execute(argc, argv); 28 | } 29 | catch (itk::ExceptionObject & e) 30 | { 31 | std::cerr << "ITK Exception: " << e.GetDescription() << std::endl; 32 | return EXIT_FAILURE; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Old/LinearAnisotropicDiffusionTest.cxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | #include 19 | #include 20 | #include "LinearAnisotropicDiffusionCommandLine.h" 21 | 22 | int 23 | LinearAnisotropicDiffusionTest(int argc, char ** argv) 24 | { 25 | try 26 | { 27 | LinearAnisotropicDiffusionCommandLine::Execute(argc, argv); 28 | } 29 | catch (itk::ExceptionObject & e) 30 | { 31 | std::cerr << "ITK Exception : " << e.GetDescription() << std::endl; 32 | return EXIT_FAILURE; 33 | } 34 | return EXIT_SUCCESS; 35 | } 36 | -------------------------------------------------------------------------------- /test/CoherenceEnhancingDiffusionTest.cxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | #include 20 | #include 21 | #include "CoherenceEnhancingDiffusionCommandLine.h" 22 | 23 | int 24 | CoherenceEnhancingDiffusionTest(int argc, char * argv[]) 25 | { 26 | try 27 | { 28 | CoherenceEnhancingDiffusionCommandLine::Execute(argc, argv); 29 | } 30 | catch (itk::ExceptionObject & e) 31 | { 32 | std::cerr << "ITK Exception : " << e.GetDescription() << std::endl; 33 | return EXIT_FAILURE; 34 | } 35 | return EXIT_SUCCESS; 36 | } 37 | 38 | /* 39 | int LinearAnisotropicDiffusionTest(int argc, char **argv) 40 | { 41 | try { 42 | LinearAnisotropicDiffusionCommandLine::Execute(argc, argv); 43 | } catch (itk::ExceptionObject& e) { 44 | std::cerr << "ITK Exception : " << e.GetDescription() << std::endl; 45 | return EXIT_FAILURE; 46 | } 47 | return EXIT_SUCCESS; 48 | }*/ 49 | -------------------------------------------------------------------------------- /include/itkAnisotropicDiffusionLBRMacro.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 07/03/2014. 21 | // 22 | // 23 | 24 | #ifndef itkAnisotropicDiffusionLBRMacro_h 25 | #define itkAnisotropicDiffusionLBRMacro_h 26 | 27 | /** 28 | Getters and setters for functor types, inspired by UnaryFunctorImageFilter. 29 | No equality test performed for the setter. 30 | */ 31 | #define GetSetFunctorMacro(name, type) \ 32 | virtual type & Get##name() { return this->m_##name; } \ 33 | virtual const type & Get##name() const { return this->m_##name; } \ 34 | virtual void Set##name(const type & _arg) \ 35 | { \ 36 | m_##name = _arg; \ 37 | this->Modified(); \ 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ITKAnisotropicDiffusionLBR 2 | ========================== 3 | 4 | .. image:: https://github.com/InsightSoftwareConsortium/ITKAnisotropicDiffusionLBR/workflows/Build,%20test,%20package/badge.svg 5 | 6 | .. image:: https://img.shields.io/pypi/v/itk-anisotropicdiffusionlbr.svg 7 | :target: https://pypi.python.org/pypi/itk-anisotropicdiffusionlbr 8 | :alt: PyPI 9 | 10 | .. image:: https://img.shields.io/badge/License-Apache%202.0-blue.svg 11 | :target: https://github.com/InsightSoftwareConsortium/ITKAnisotropicDiffusionLBR/blob/master/LICENSE) 12 | :alt: License 13 | 14 | This `ITK `_ module implements Anisotropic Diffusion, using Lattice Basis Reduction. 15 | 16 | Documentation can be found in the `Insight Journal article `_:: 17 | 18 | Mirebeau J., Fehrenbach J., Risser L., Tobji S. 19 | "Anisotropic Diffusion in ITK", 20 | The Insight Journal, 21 | January-December, 2014. 22 | https://insight-journal.org/browse/publication/953 23 | https://hdl.handle.net/10380/3505 24 | 25 | To install the Python packages:: 26 | 27 | python -m pip install --upgrade pip 28 | python -m pip install itk-anisotropicdiffusionlbr 29 | 30 | Figures from this article can be `reproduced in your web browser 31 | `_. 32 | Sources for the interactive figures can be found in the `gh-pages branch 33 | `_ 34 | of this repository. 35 | 36 | Since ITK 4.9.0, this module is available in the ITK source tree as a Remote 37 | module. To enable it, set:: 38 | 39 | Module_AnisotropicDiffusionLBR:BOOL=ON 40 | 41 | in ITK's CMake build configuration. 42 | -------------------------------------------------------------------------------- /Old/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory("../src/Headers" "${CMAKE_CURRENT_BINARY_DIR}/src/Headers") 2 | 3 | include_directories(${PROJECT_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) 4 | 5 | itk_module_test() 6 | 7 | Set(ITK${itk-module}Tests 8 | CoherenceEnhancingDiffusionTest.cxx 9 | LinearAnisotropicDiffusionTest.cxx 10 | ) 11 | 12 | set(ITK_TEST_DRIVER itkTestDriver) 13 | 14 | CreateTestDriver(${itk-module} "${${itk-module}-Test_LIBRARIES}" "${ITK${itk-module}Tests}") 15 | 16 | itk_add_test(NAME Triangle_cEED_Test 17 | COMMAND ${itk-module}TestDriver 18 | --compare ${${itk-module}_SOURCE_DIR}/test/Baseline/Triangle_E.png 19 | ${ITK_TEST_OUTPUT_DIR}/Triangle_E_Test.png 20 | CoherenceEnhancingDiffusionTest ${${itk-module}_SOURCE_DIR}/test/Input/Triangle.png 21 | ${ITK_TEST_OUTPUT_DIR}/Triangle_E_Test.png 5 0.03 Edge 22 | ) 23 | 24 | itk_add_test(NAME Triangle_EED_Test 25 | COMMAND ${itk-module}TestDriver 26 | --compare ${${itk-module}_SOURCE_DIR}/test/Baseline/Triangle_EF.png 27 | ${ITK_TEST_OUTPUT_DIR}/Triangle_EF_Test.png 28 | CoherenceEnhancingDiffusionTest ${${itk-module}_SOURCE_DIR}/test/Input/Triangle.png 29 | ${ITK_TEST_OUTPUT_DIR}/Triangle_EF_Test.png 5 0.03 EdgeForced 30 | ) 31 | 32 | itk_add_test(NAME LAD_Test 33 | COMMAND ${itk-module}TestDriver 34 | --compare ${${itk-module}_SOURCE_DIR}/test/Baseline/VectorField_CircleOpposites_E.hdf5 35 | ${ITK_TEST_OUTPUT_DIR}/VectorField_CircleOpposites_E_Test.hdf5 36 | 37 | LinearAnisotropicDiffusionTest 38 | ${${itk-module}_SOURCE_DIR}/test/Input/VectorField_CircleOpposites.hdf5 39 | ${${itk-module}_SOURCE_DIR}/test/Input/TensorField_Circle_Anisotropic.hdf5 40 | 10 41 | ${ITK_TEST_OUTPUT_DIR}/VectorField_CircleOpposites_E_Test.hdf5 42 | ) 43 | 44 | -------------------------------------------------------------------------------- /include/itkCoherenceEnhancingDiffusionImageFilter.hxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | #ifndef itkCoherenceEnhancingDiffusionImageFilter_hxx 20 | #define itkCoherenceEnhancingDiffusionImageFilter_hxx 21 | 22 | 23 | namespace itk 24 | { 25 | 26 | template 27 | CoherenceEnhancingDiffusionImageFilter::CoherenceEnhancingDiffusionImageFilter() 28 | : m_Lambda(0.05) 29 | , m_Exponent(2) 30 | , m_Alpha(0.01) 31 | , m_Enhancement(CED) 32 | {} 33 | 34 | 35 | template 36 | typename CoherenceEnhancingDiffusionImageFilter::EigenValuesArrayType 37 | CoherenceEnhancingDiffusionImageFilter::EigenValuesTransform(const EigenValuesArrayType & ev0) const 38 | { 39 | const ScalarType evMin = ev0[0]; 40 | const ScalarType evMax = ev0[InputImageDimension - 1]; 41 | 42 | EigenValuesArrayType ev; 43 | switch (m_Enhancement) 44 | { 45 | // Weickert's filter. 46 | case CED: 47 | for (InputImageDimensionType i = 0; i < InputImageDimension; ++i) 48 | { 49 | ev[i] = g_CED(evMax - ev0[i]); 50 | } 51 | break; 52 | 53 | // A variance, requiring stronger coherence. 54 | case cCED: 55 | for (InputImageDimensionType i = 0; i < InputImageDimension; ++i) 56 | { 57 | ev[i] = g_CED((evMax - ev0[i]) / (1. + ev0[i] / m_Lambda)); 58 | } 59 | break; 60 | 61 | // Weickert's filter. 62 | case EED: 63 | for (InputImageDimensionType i = 0; i < InputImageDimension; ++i) 64 | { 65 | ev[i] = g_EED(ev0[i] - evMin); 66 | } 67 | break; 68 | 69 | // A variant, promoting diffusion in at least one direction at each point. 70 | case cEED: 71 | for (InputImageDimensionType i = 0; i < InputImageDimension; ++i) 72 | { 73 | ev[i] = g_EED(ev0[i]); 74 | } 75 | break; 76 | 77 | // Isotropic tensors, closely related to Perona-Malik's approach. 78 | case Isotropic: 79 | for (InputImageDimensionType i = 0; i < InputImageDimension; ++i) 80 | { 81 | ev[i] = g_EED(evMax); 82 | } 83 | break; 84 | 85 | default: 86 | itkExceptionMacro("Unsupported diffusion type"); 87 | } 88 | return ev; 89 | } 90 | 91 | } // end namespace itk 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /include/itkUnaryFunctorWithIndexImageFilter.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 07/03/2014. 21 | // 22 | // 23 | 24 | #ifndef itkUnaryFunctorWithIndexImageFilter_h 25 | #define itkUnaryFunctorWithIndexImageFilter_h 26 | 27 | #include "itkImageRegionConstIteratorWithIndex.h" 28 | #include "itkImageScanlineIterator.h" 29 | #include "itkAnisotropicDiffusionLBRMacro.h" 30 | #include "itkImageToImageFilter.h" 31 | 32 | namespace itk 33 | { 34 | 35 | /** \class UnaryFunctorWithIndexImageFilter 36 | * 37 | * \brief A (simplification of) UnaryFunctorImageFilter, which provides the pixel index to the functor. 38 | * 39 | * \ingroup AnisotropicDiffusionLBR 40 | */ 41 | template 42 | class UnaryFunctorWithIndexImageFilter : public ImageToImageFilter 43 | { 44 | public: 45 | ITK_DISALLOW_COPY_AND_MOVE(UnaryFunctorWithIndexImageFilter); 46 | 47 | using Self = UnaryFunctorWithIndexImageFilter; 48 | using Superclass = ImageToImageFilter; 49 | using Pointer = SmartPointer; 50 | using ConstPointer = SmartPointer; 51 | 52 | /** Method for creation through the object factory. */ 53 | itkNewMacro(Self); 54 | 55 | /** Run-time type information (and related methods). */ 56 | itkOverrideGetNameOfClassMacro(UnaryFunctorWithIndexImageFilter); 57 | 58 | using InputImageType = TInputImage; 59 | using OutputImageType = TOutputImage; 60 | using FunctorType = TFunctor; 61 | 62 | GetSetFunctorMacro(Functor, FunctorType); 63 | 64 | protected: 65 | UnaryFunctorWithIndexImageFilter() { this->DynamicMultiThreadingOn(); } 66 | 67 | FunctorType m_Functor; 68 | 69 | using InputRegionType = typename InputImageType::RegionType; 70 | using OutputRegionType = typename OutputImageType::RegionType; 71 | 72 | void 73 | DynamicThreadedGenerateData(const OutputRegionType & region) override 74 | { 75 | if (region.GetSize()[0] == 0) 76 | { 77 | return; 78 | } 79 | 80 | ImageRegionConstIteratorWithIndex inputIt(this->GetInput(), region); 81 | ImageScanlineIterator outputIt(this->GetOutput(), region); 82 | 83 | for (inputIt.GoToBegin(), outputIt.GoToBegin(); !outputIt.IsAtEnd();) 84 | { 85 | while (!outputIt.IsAtEndOfLine()) 86 | { 87 | outputIt.Set(m_Functor(inputIt.Value(), inputIt.GetIndex())); 88 | ++inputIt; 89 | ++outputIt; 90 | } 91 | outputIt.NextLine(); 92 | } 93 | } 94 | }; 95 | 96 | } // end namespace itk 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["scikit-build-core"] 3 | build-backend = "scikit_build_core.build" 4 | 5 | [project] 6 | name = "itk-anisotropicdiffusionlbr" 7 | version = "2.0.1" 8 | description = "Smooth images while preserving edges or coherent structures." 9 | readme = "README.rst" 10 | license = {file = "LICENSE"} 11 | authors = [ 12 | { name = "Insight Software Consortium", email = "community@itk.org" }, 13 | ] 14 | keywords = [ 15 | "itk", 16 | "InsightToolkit", 17 | ] 18 | classifiers = [ 19 | "Development Status :: 4 - Beta", 20 | "Intended Audience :: Developers", 21 | "Intended Audience :: Education", 22 | "Intended Audience :: Healthcare Industry", 23 | "Intended Audience :: Science/Research", 24 | "License :: OSI Approved :: Apache Software License", 25 | "Operating System :: Android", 26 | "Operating System :: MacOS", 27 | "Operating System :: Microsoft :: Windows", 28 | "Operating System :: POSIX", 29 | "Operating System :: Unix", 30 | "Programming Language :: C++", 31 | "Programming Language :: Python", 32 | "Topic :: Scientific/Engineering", 33 | "Topic :: Scientific/Engineering :: Information Analysis", 34 | "Topic :: Scientific/Engineering :: Medical Science Apps.", 35 | "Topic :: Software Development :: Libraries", 36 | ] 37 | requires-python = ">=3.9" 38 | dependencies = [ 39 | "itk == 5.4.*", 40 | ] 41 | 42 | [project.urls] 43 | Download = "https://github.com/InsightSoftwareConsortium/ITKAnisotropicDiffusionLBR" 44 | Homepage = "https://github.com/InsightSoftwareConsortium/ITKAnisotropicDiffusionLBR" 45 | 46 | [tool.scikit-build] 47 | # The versions of CMake to allow. If CMake is not present on the system or does 48 | # not pass this specifier, it will be downloaded via PyPI if possible. An empty 49 | # string will disable this check. 50 | cmake.version = ">=3.22.1" 51 | 52 | # A list of args to pass to CMake when configuring the project. Setting this in 53 | # config or envvar will override toml. See also ``cmake.define``. 54 | cmake.args = [] 55 | 56 | # A table of defines to pass to CMake when configuring the project. Additive. 57 | cmake.define = {} 58 | 59 | # Verbose printout when building. 60 | cmake.verbose = true 61 | 62 | # The build type to use when building the project. Valid options are: "Debug", 63 | # "Release", "RelWithDebInfo", "MinSizeRel", "", etc. 64 | cmake.build-type = "Release" 65 | 66 | # The source directory to use when building the project. Currently only affects 67 | # the native builder (not the setuptools plugin). 68 | cmake.source-dir = "." 69 | 70 | # The versions of Ninja to allow. If Ninja is not present on the system or does 71 | # not pass this specifier, it will be downloaded via PyPI if possible. An empty 72 | # string will disable this check. 73 | ninja.version = ">=1.11" 74 | 75 | # The logging level to display, "DEBUG", "INFO", "WARNING", and "ERROR" are 76 | # possible options. 77 | logging.level = "INFO" 78 | 79 | # Files to include in the SDist even if they are skipped by default. Supports 80 | # gitignore syntax. 81 | sdist.include = [] 82 | 83 | # Files to exclude from the SDist even if they are included by default. Supports 84 | # gitignore syntax. 85 | sdist.exclude = [] 86 | 87 | # A list of license files to include in the wheel. Supports glob patterns. 88 | wheel.license-files = ["LICEN[CS]E*",] 89 | 90 | # Target the platlib or the purelib. If not set, the default is to target the 91 | # platlib if wheel.cmake is true, and the purelib otherwise. 92 | wheel.platlib = "false" 93 | 94 | # If CMake is less than this value, backport a copy of FindPython. Set to 0 95 | # disable this, or the empty string. 96 | backport.find-python = "3.26.1" 97 | 98 | # Select the editable mode to use. Can be "redirect" (default) or "inplace". 99 | editable.mode = "redirect" 100 | 101 | # Rebuild the project when the package is imported. The build-directory must be 102 | # set. 103 | editable.rebuild = false 104 | 105 | # If set, this will provide a method for scikit-build-core backward compatibility. 106 | minimum-version = "0.8.2" 107 | 108 | # The build directory. Defaults to a temporary directory, but can be set. 109 | build-dir = "build/{wheel_tag}" 110 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | itk_module_test() 2 | 3 | set(AnisotropicDiffusionLBRTests 4 | CoherenceEnhancingDiffusionTest.cxx 5 | ) 6 | 7 | CreateTestDriver(AnisotropicDiffusionLBR "${AnisotropicDiffusionLBR-Test_LIBRARIES}" "${AnisotropicDiffusionLBRTests}") 8 | 9 | set(TestOutput ${ITK_TEST_OUTPUT_DIR}) 10 | 11 | ################ fig:PacMan ################# 12 | 13 | itk_add_test(NAME PacMan_cEED COMMAND AnisotropicDiffusionLBRTestDriver 14 | --compare DATA{Baseline/PacMan_cEED.png} ${TestOutput}/PacMan_cEED.png 15 | CoherenceEnhancingDiffusionTest DATA{Input/PacMan.png} ${TestOutput}/PacMan_cEED.png 20 0.05 cEED 3) 16 | 17 | itk_add_test(NAME PacMan_cCED COMMAND AnisotropicDiffusionLBRTestDriver 18 | --compare DATA{Baseline/PacMan_cCED.png} ${TestOutput}/PacMan_cCED.png 19 | CoherenceEnhancingDiffusionTest DATA{Input/PacMan.png} ${TestOutput}/PacMan_cCED.png 20 0.05 cCED 3) 20 | 21 | itk_add_test(NAME PacMan_I COMMAND AnisotropicDiffusionLBRTestDriver 22 | --compare DATA{Baseline/PacMan_I.png} ${TestOutput}/PacMan_I.png 23 | CoherenceEnhancingDiffusionTest DATA{Input/PacMan.png} ${TestOutput}/PacMan_I.png 20 0.05 Isotropic 3) 24 | 25 | ############# fig : Fingerprint ############# 26 | 27 | itk_add_test(NAME FingerPrint_cEED COMMAND AnisotropicDiffusionLBRTestDriver 28 | --compare DATA{Baseline/FingerPrint_cEED_20.png} ${TestOutput}/FingerPrint_cEED_20.png 29 | CoherenceEnhancingDiffusionTest DATA{Input/FingerPrint.png} ${TestOutput}/FingerPrint_cEED_20.png 20 0.02 cEED ) 30 | 31 | itk_add_test(NAME FingerPrint_cCED COMMAND AnisotropicDiffusionLBRTestDriver 32 | --compare DATA{Baseline/FingerPrint_cCED_20.png} ${TestOutput}/FingerPrint_cCED_20.png 33 | CoherenceEnhancingDiffusionTest DATA{Input/FingerPrint.png} ${TestOutput}/FingerPrint_cCED_20.png 20 0.02 cCED ) 34 | 35 | itk_add_test(NAME FingerPrint_I COMMAND AnisotropicDiffusionLBRTestDriver 36 | --compare DATA{Baseline/FingerPrint_I_20.png} ${TestOutput}/FingerPrint_I_20.png 37 | CoherenceEnhancingDiffusionTest DATA{Input/FingerPrint.png} ${TestOutput}/FingerPrint_I_20.png 20 0.02 Isotropic ) 38 | 39 | 40 | ################ fig : Cos3D ################ 41 | 42 | itk_add_test(NAME Cos3D_cCED COMMAND AnisotropicDiffusionLBRTestDriver 43 | --compare DATA{Baseline/Cos3D_cCED.vtk} ${TestOutput}/Cos3D_cCED.vtk 44 | CoherenceEnhancingDiffusionTest DATA{Input/Cos3D_Noisy.vtk} ${TestOutput}/Cos3D_cCED.vtk 10 0.02 cCED 4 10 ) 45 | 46 | 47 | ################ fig : Brain ################ 48 | 49 | itk_add_test(NAME mrbrain_cEED COMMAND AnisotropicDiffusionLBRTestDriver 50 | --compare DATA{Baseline/mrbrain_cEED.vtk} ${TestOutput}/mrbrain_cEED.vtk 51 | CoherenceEnhancingDiffusionTest DATA{Input/mrbrain_noisy.vtk} ${TestOutput}/mrbrain_cEED.vtk 5 0.003 cEED ) 52 | 53 | ################# fig : Lena ################ 54 | 55 | itk_add_test(NAME Lena_Detail_cCED_2 COMMAND AnisotropicDiffusionLBRTestDriver 56 | --compare DATA{Baseline/Lena_Detail_cCED_2.png} ${TestOutput}/Lena_Detail_cCED_2.png 57 | CoherenceEnhancingDiffusionTest DATA{Input/Lena_Detail.png} ${TestOutput}/Lena_Detail_cCED_2.png 2 0.003 cCED 0.5 2 4 ) 58 | 59 | itk_add_test(NAME Lena_Detail_cEED_2 COMMAND AnisotropicDiffusionLBRTestDriver 60 | --compare DATA{Baseline/Lena_Detail_cEED_2.png} ${TestOutput}/Lena_Detail_cEED_2.png 61 | CoherenceEnhancingDiffusionTest DATA{Input/Lena_Detail.png} ${TestOutput}/Lena_Detail_cEED_2.png 2 0.003 cEED 0.5 2 4 ) 62 | 63 | itk_add_test(NAME Lena_Detail_I_2 COMMAND AnisotropicDiffusionLBRTestDriver 64 | --compare DATA{Baseline/Lena_Detail_I_2.png} ${TestOutput}/Lena_Detail_I_2.png 65 | CoherenceEnhancingDiffusionTest DATA{Input/Lena_Detail.png} ${TestOutput}/Lena_Detail_I_2.png 2 0.003 Isotropic 0.5 2 4 ) 66 | 67 | 68 | ############## fig : Vector field ########### 69 | 70 | itk_add_test(NAME VectorField_Circle_cEED COMMAND AnisotropicDiffusionLBRTestDriver 71 | --compare DATA{Baseline/VectorField_Circle_cEED.vtk} ${TestOutput}/VectorField_Circle_cEED.vtk 72 | CoherenceEnhancingDiffusionTest DATA{Input/VectorField_CircleOpposites.vtk} ${TestOutput}/VectorField_Circle_cEED.vtk 10 0.05 cEED ) 73 | 74 | 75 | ############### fig : Triangle ############## 76 | 77 | 78 | itk_add_test(NAME Triangle_cEED COMMAND AnisotropicDiffusionLBRTestDriver 79 | --compare DATA{Baseline/Triangle_cEED.png} ${TestOutput}/Triangle_cEED.png 80 | CoherenceEnhancingDiffusionTest DATA{Input/Triangle.png} ${TestOutput}/Triangle_cEED.png 5 0.03 cEED ) 81 | 82 | itk_add_test(NAME Triangle_EED COMMAND AnisotropicDiffusionLBRTestDriver 83 | --compare DATA{Baseline/Triangle_EED.png} ${TestOutput}/Triangle_EED.png 84 | CoherenceEnhancingDiffusionTest DATA{Input/Triangle.png} ${TestOutput}/Triangle_EED.png 5 0.03 EED ) 85 | 86 | itk_add_test(NAME Oscillations1_cCED COMMAND AnisotropicDiffusionLBRTestDriver 87 | --compare DATA{Baseline/Oscillations1_cCED.png} ${TestOutput}/Oscillations1_cCED.png 88 | CoherenceEnhancingDiffusionTest DATA{Input/Oscillations_Noisy1.png} ${TestOutput}/Oscillations1_cCED.png 20 0.05 cCED ) 89 | 90 | itk_add_test(NAME Oscillations1_CED COMMAND AnisotropicDiffusionLBRTestDriver 91 | --compare DATA{Baseline/Oscillations1_CED.png} ${TestOutput}/Oscillations1_CED.png 92 | CoherenceEnhancingDiffusionTest DATA{Input/Oscillations_Noisy1.png} ${TestOutput}/Oscillations1_CED.png 20 0.05 CED ) 93 | -------------------------------------------------------------------------------- /include/itkCoherenceEnhancingDiffusionImageFilter.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 06/03/2014. 21 | // 22 | // 23 | 24 | #ifndef itkCoherenceEnhancingDiffusionImageFilter_h 25 | #define itkCoherenceEnhancingDiffusionImageFilter_h 26 | 27 | #include "itkAnisotropicDiffusionLBRImageFilter.h" 28 | 29 | 30 | namespace itk 31 | { 32 | /** 33 | * \class CoherenceEnhancingDiffusionImageFilter 34 | * 35 | * \brief Coherence enhancing diffusion and edge enhancing diffusion. 36 | * 37 | * Implementation of Coherence Enhancing Diffusion (CED), and 38 | * Edge Enhancing Diffusion (EED), as described by Weickert. 39 | * 40 | * CED heuristically smoothes everywhere except accross image contours, 41 | * while EED smoothes nowhere but tangentially to image contours. 42 | * 43 | * The non-linear diffusion tensor is defined in terms of the structure tensor. 44 | * 45 | * Denote by \f$\mu_i\f$ the structure tensor eigenvalues, at a given point \f$x\f$, 46 | * with \f$0\leq i < d\f$. Let also \f$\mu_{\rm min}\f$ and \f$\mu_{\rm max}\f$, 47 | * be the smallest and largest eigenvalues respectively. The diffusion tensor is 48 | * defined by the same eigenvectors, but with modified with eigenvalues \f$\lambda_i\f$. 49 | * 50 | * Edge Enhancing Diffusion: 51 | * 52 | * \f$\lambda_i := g(\mu_i - \mu_{\rm min})\f$, where \f$g(s) = 1 - (1-\alpha)*exp(-(\lambda/s)^m)\f$ 53 | * 54 | * Note the limit values \f$g(0) = 1\f$, \f$g(\infty) = \alpha\f$. 55 | * 56 | * Coherence Enhancing Diffusion: 57 | * 58 | * \f$\lambda_i := g(\mu_{\rm max} - \mu_i)\f$, where \f$g(s) = \alpha + (1-\alpha)*exp(-(\lambda/s)^m)\f$ 59 | * 60 | * Note the limit values \f$g(0) = \alpha\f$, \f$g(\infty) = 1\f$. 61 | * 62 | * \ingroup AnisotropicDiffusionLBR 63 | */ 64 | template ::RealType> 65 | class CoherenceEnhancingDiffusionImageFilter : public AnisotropicDiffusionLBRImageFilter 66 | { 67 | public: 68 | ITK_DISALLOW_COPY_AND_MOVE(CoherenceEnhancingDiffusionImageFilter); 69 | 70 | using Self = CoherenceEnhancingDiffusionImageFilter; 71 | using Superclass = AnisotropicDiffusionLBRImageFilter; 72 | using Pointer = SmartPointer; 73 | using ConstPointer = SmartPointer; 74 | 75 | /** Method for creation through the object factory. */ 76 | itkNewMacro(Self); 77 | 78 | /** Run-time type information (and related methods). */ 79 | itkOverrideGetNameOfClassMacro(CoherenceEnhancingDiffusionImageFilter); 80 | 81 | using InputImageDimensionType = typename Superclass::InputImageType::ImageDimensionType; 82 | static constexpr InputImageDimensionType InputImageDimension = Superclass::InputImageType::ImageDimension; 83 | 84 | using EigenValuesArrayType = typename Superclass::EigenValuesArrayType; 85 | EigenValuesArrayType 86 | EigenValuesTransform(const EigenValuesArrayType &) const override; 87 | 88 | using ScalarType = typename Superclass::ScalarType; 89 | /** Exponent m involved in the function g defining eigenvalues. */ 90 | itkSetMacro(Exponent, ScalarType); 91 | itkSetMacro(Lambda, ScalarType); 92 | itkSetMacro(Alpha, ScalarType); 93 | 94 | itkGetMacro(Exponent, ScalarType); 95 | itkGetMacro(Lambda, ScalarType); 96 | itkGetMacro(Alpha, ScalarType); 97 | 98 | enum EnhancementType 99 | { 100 | CED, 101 | cCED, 102 | EED, 103 | cEED, 104 | Isotropic 105 | }; 106 | /// Switch between CED, EED, and variants. 107 | itkSetEnumMacro(Enhancement, EnhancementType); 108 | itkGetEnumMacro(Enhancement, EnhancementType); 109 | 110 | protected: 111 | ScalarType m_Lambda; 112 | ScalarType m_Exponent; 113 | ScalarType m_Alpha; 114 | EnhancementType m_Enhancement; 115 | 116 | ScalarType 117 | g_CED(ScalarType s) const 118 | { 119 | return s <= 0 ? m_Alpha : m_Alpha + (1 - m_Alpha) * exp(-pow(m_Lambda / s, m_Exponent)); 120 | } 121 | ScalarType 122 | g_EED(ScalarType s) const 123 | { 124 | return s <= 0 ? 1 : 1 - (1 - m_Alpha) * exp(-pow(m_Lambda / s, m_Exponent)); 125 | } 126 | 127 | CoherenceEnhancingDiffusionImageFilter(); 128 | ~CoherenceEnhancingDiffusionImageFilter() override = default; 129 | }; 130 | 131 | } // end namespace itk 132 | 133 | #ifndef ITK_MANUAL_INSTANTIATION 134 | # include "itkCoherenceEnhancingDiffusionImageFilter.hxx" 135 | #endif 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /include/itkStructureTensorImageFilter.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 05/03/2014. 21 | // 22 | // 23 | 24 | #ifndef itkStructureTensorImageFilter_h 25 | #define itkStructureTensorImageFilter_h 26 | 27 | #include "itkCastImageFilter.h" 28 | #include "itkGradientRecursiveGaussianImageFilter.h" 29 | #include "itkAddImageFilter.h" 30 | #include "itkVectorIndexSelectionCastImageFilter.h" 31 | #include "itkGradientImageFilter.h" 32 | #include "itkSymmetricSecondRankTensor.h" 33 | 34 | namespace itk 35 | { 36 | /** 37 | * \class StructureTensorImageFilter 38 | * 39 | * \brief Computes the structure tensor. 40 | * 41 | * Implementation of the structure tensor, defined by 42 | * 43 | * \f[K_\rho (\nabla u_\sigma \otimes \nabla u_\sigma),\f] 44 | * 45 | * where \f$K_\rho\f$ denotes the gaussian kernel of standard deviation \f$\rho\f$, 46 | * and \f$u_\sigma := K_\sigma * u\f$. 47 | * 48 | * \ingroup AnisotropicDiffusionLBR 49 | */ 50 | template , 52 | TImage::ImageDimension>> 53 | class StructureTensorImageFilter : public ImageToImageFilter 54 | { 55 | public: 56 | ITK_DISALLOW_COPY_AND_MOVE(StructureTensorImageFilter); 57 | 58 | using Self = StructureTensorImageFilter; 59 | using Superclass = ImageToImageFilter; 60 | using Pointer = SmartPointer; 61 | using ConstPointer = SmartPointer; 62 | 63 | /// Method for creation through the object factory. 64 | itkNewMacro(Self); 65 | /// Run-time type information (and related methods). 66 | itkOverrideGetNameOfClassMacro(StructureTensorImageFilter); 67 | 68 | using InputImageDimensionType = typename Superclass::InputImageType::ImageDimensionType; 69 | static constexpr InputImageDimensionType InputImageDimension = Superclass::InputImageType::ImageDimension; 70 | 71 | using ImageType = TImage; 72 | using PixelType = typename ImageType::PixelType; 73 | using TensorImageType = TTensorImage; 74 | using TensorType = typename TensorImageType::PixelType; 75 | using ScalarType = typename TensorType::ComponentType; 76 | using ScalarImageType = Image; 77 | 78 | /// Parameter \f$\sigma\f$ of the structure tensor definition. 79 | itkSetMacro(NoiseScale, ScalarType); 80 | /// Parameter \f$\rho\f$ of the structure tensor definition. 81 | itkSetMacro(FeatureScale, ScalarType); 82 | /// Rescales all structure tensors by a common factor, so that the maximum trace is 1. 83 | itkSetMacro(RescaleForUnitMaximumTrace, bool); 84 | 85 | itkGetConstMacro(NoiseScale, ScalarType); 86 | itkGetConstMacro(FeatureScale, ScalarType); 87 | itkGetConstMacro(RescaleForUnitMaximumTrace, bool); 88 | itkGetConstMacro(PostRescaling, ScalarType); /// Global rescaling constant used. 89 | 90 | protected: 91 | void 92 | GenerateData() override; 93 | 94 | ScalarType m_FeatureScale; 95 | ScalarType m_NoiseScale; 96 | bool m_RescaleForUnitMaximumTrace{ false }; 97 | ScalarType m_PostRescaling; 98 | bool m_UseGradientRecursiveGaussianImageFilter{ true }; 99 | 100 | struct DispatchBase 101 | {}; 102 | template 103 | struct Dispatch : public DispatchBase 104 | {}; 105 | 106 | void 107 | IntermediateFilter(const Dispatch &); 108 | void 109 | IntermediateFilter(const Dispatch &); 110 | typename TensorImageType::Pointer m_IntermediateResult; 111 | 112 | using CovariantVectorType = CovariantVector; 113 | using CovariantImageType = Image; 114 | 115 | struct OuterFunctor 116 | { 117 | TensorType 118 | operator()(const CovariantVectorType & u) const 119 | { 120 | TensorType m; 121 | for (InputImageDimensionType i = 0; i < InputImageDimension; ++i) 122 | { 123 | for (InputImageDimensionType j = i; j < InputImageDimension; ++j) 124 | { 125 | m(i, j) = u[i] * u[j]; 126 | } 127 | } 128 | return m; 129 | } 130 | }; 131 | struct TraceFunctor 132 | { 133 | ScalarType 134 | operator()(const TensorType & t) const 135 | { 136 | return t.GetTrace(); 137 | } 138 | }; 139 | struct ScaleFunctor 140 | { 141 | ScalarType scaling; 142 | TensorType 143 | operator()(const TensorType & t) const 144 | { 145 | return t * scaling; 146 | } 147 | }; 148 | 149 | StructureTensorImageFilter(); 150 | }; 151 | 152 | } // end namespace itk 153 | 154 | #ifndef ITK_MANUAL_INSTANTIATION 155 | # include "itkStructureTensorImageFilter.hxx" 156 | #endif 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /include/itkAnisotropicDiffusionLBRImageFilter.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | 20 | // 21 | // Created by Jean-Marie Mirebeau on 28/02/2014. 22 | // 23 | // 24 | 25 | #ifndef itkAnisotropicDiffusionLBRImageFilter_h 26 | #define itkAnisotropicDiffusionLBRImageFilter_h 27 | 28 | #include "itkLinearAnisotropicDiffusionLBRImageFilter.h" 29 | #include "itkStructureTensorImageFilter.h" 30 | 31 | namespace itk 32 | { 33 | 34 | /** \class AnisotropicDiffusionLBRImageFilter 35 | * 36 | * \brief Non-linear anisotropic diffusion using lattice basis reduction. 37 | * 38 | * This class repeatedly calls the LinearAnisotropicDiffusionLBRImageFilter, 39 | * with non-linear diffusion tensors built on the fly. These tensors are 40 | * obtained by computing the image structure tensors, and appropriately 41 | * modifying their eigenvalues with the method EigenValuesTransform. The 42 | * latter method is not implemented, and needs to be provided in a subclass, 43 | * such as CoherenceEnhancingDiffusionImageFilter. 44 | * 45 | * \ingroup AnisotropicDiffusionLBR 46 | */ 47 | template ::RealType> 48 | class AnisotropicDiffusionLBRImageFilter : public ImageToImageFilter 49 | { 50 | public: 51 | ITK_DISALLOW_COPY_AND_MOVE(AnisotropicDiffusionLBRImageFilter); 52 | 53 | using Self = AnisotropicDiffusionLBRImageFilter; 54 | using Superclass = ImageToImageFilter; 55 | using Pointer = SmartPointer; 56 | using ConstPointer = SmartPointer; 57 | 58 | /// Method for creation through the object factory. 59 | itkNewMacro(Self); 60 | /// Run-time type information (and related methods). 61 | itkOverrideGetNameOfClassMacro(AnisotropicDiffusionLBRImageFilter); 62 | 63 | using ImageType = TImage; 64 | using PixelType = typename ImageType::PixelType; 65 | using ScalarType = TScalar; 66 | 67 | using ImageDimensionType = typename ImageType::ImageDimensionType; 68 | static constexpr ImageDimensionType ImageDimension = ImageType::ImageDimension; 69 | 70 | using TensorType = SymmetricSecondRankTensor; 71 | using TensorImageType = Image; 72 | 73 | using StructureTensorFilterType = StructureTensorImageFilter; 74 | using LinearDiffusionFilterType = LinearAnisotropicDiffusionLBRImageFilter; 75 | 76 | /** Passed to a StructureTensorImageFilter. */ 77 | itkSetMacro(NoiseScale, ScalarType); 78 | itkGetConstMacro(NoiseScale, ScalarType); 79 | itkSetMacro(FeatureScale, ScalarType); 80 | itkGetConstMacro(FeatureScale, ScalarType); 81 | 82 | /** Passed to a LinearAnisotropicDiffusion Filter. */ 83 | itkSetMacro(RatioToMaxStableTimeStep, ScalarType); 84 | itkGetConstMacro(RatioToMaxStableTimeStep, ScalarType); 85 | itkSetMacro(MaxTimeStepsBetweenTensorUpdates, int); 86 | itkGetConstMacro(MaxTimeStepsBetweenTensorUpdates, int); 87 | 88 | itkSetMacro(DiffusionTime, ScalarType); 89 | itkGetConstMacro(DiffusionTime, ScalarType); 90 | 91 | /** If true, uses unit pixel spacing, and rescales structure 92 | * tensors for uni maximum trace. */ 93 | itkSetMacro(Adimensionize, bool); 94 | itkGetConstMacro(Adimensionize, bool); 95 | 96 | using EigenValuesArrayType = typename TensorType::EigenValuesArrayType; 97 | /** Transformation of the Structure tensor eigenvalues into the diffusion 98 | * tensor eigenvalues. Needs to be overloaded in a subclass. 99 | * (Structure tensor eigenvalues are sorted by increasing order for convenience). */ 100 | virtual EigenValuesArrayType 101 | EigenValuesTransform(const EigenValuesArrayType &) const 102 | { 103 | itkExceptionMacro("Undefined tensor eigenvalues transform"); 104 | } 105 | 106 | virtual typename TensorImageType::Pointer 107 | GetLastTensorImage() 108 | { 109 | return m_TensorImage; 110 | } 111 | using EffectiveTimesAndIterationsType = std::vector>; 112 | itkGetConstReferenceMacro(LinearFilterEffectiveTimesAndIterations, EffectiveTimesAndIterationsType); 113 | 114 | protected: 115 | ScalarType m_NoiseScale; 116 | ScalarType m_FeatureScale; 117 | 118 | ScalarType m_RatioToMaxStableTimeStep; 119 | int m_MaxTimeStepsBetweenTensorUpdates{ 5 }; 120 | 121 | AnisotropicDiffusionLBRImageFilter(); 122 | ~AnisotropicDiffusionLBRImageFilter() override = default; 123 | 124 | typename TensorImageType::Pointer m_TensorImage; 125 | 126 | virtual void 127 | ComputeDiffusionTensors(ImageType *); 128 | 129 | ScalarType m_DiffusionTime; 130 | bool m_Adimensionize{ true }; 131 | 132 | void 133 | GenerateData() override; 134 | 135 | EffectiveTimesAndIterationsType m_LinearFilterEffectiveTimesAndIterations; 136 | 137 | struct DiffusionTensorFunctor; 138 | }; 139 | 140 | } // end namespace itk 141 | 142 | #ifndef ITK_MANUAL_INSTANTIATION 143 | # include "itkAnisotropicDiffusionLBRImageFilter.hxx" 144 | #endif 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /include/itkLinearAnisotropicDiffusionLBRImageFilter.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 28/02/2014. 21 | // 22 | // 23 | 24 | #ifndef itkLinearAnisotropicDiffusionLBRImageFilter_h 25 | #define itkLinearAnisotropicDiffusionLBRImageFilter_h 26 | 27 | 28 | #include "itkImageToImageFilter.h" 29 | #include "itkSymmetricSecondRankTensor.h" 30 | 31 | namespace itk 32 | { 33 | /** 34 | * \class LinearAnisotropicDiffusionLBRImageFilter 35 | * 36 | * \brief Anisotropic diffusion using lattice basis reduction. 37 | * 38 | * \f[\partial_t u = {\rm div} (D \nabla u),\f] 39 | * 40 | * with Neumann boundary conditions. The numerical scheme is stable and 41 | * satisfies the maximum principle, even for strongly anisotropic tensors, 42 | * thanks to an adaptive discretization using arithmetic techniques 43 | * (Lattice Basis Reduction, LBR). 44 | * 45 | * \ingroup AnisotropicDiffusionLBR 46 | */ 47 | template ::RealType> 48 | class LinearAnisotropicDiffusionLBRImageFilter : public ImageToImageFilter 49 | { 50 | public: 51 | ITK_DISALLOW_COPY_AND_MOVE(LinearAnisotropicDiffusionLBRImageFilter); 52 | 53 | /** Standard class type alias. */ 54 | using Self = LinearAnisotropicDiffusionLBRImageFilter; 55 | using Superclass = ImageToImageFilter; 56 | using Pointer = SmartPointer; 57 | using ConstPointer = SmartPointer; 58 | 59 | /** Method for creation through the object factory. */ 60 | itkNewMacro(Self); 61 | 62 | /** Run-time type information (and related methods). */ 63 | itkOverrideGetNameOfClassMacro(LinearAnisotropicDiffusionLBRImageFilter); 64 | 65 | using ImageType = TImage; 66 | using PixelType = typename ImageType::PixelType; 67 | 68 | using ImageDimensionType = typename ImageType::ImageDimensionType; 69 | static constexpr ImageDimensionType ImageDimension = ImageType::ImageDimension; 70 | 71 | using ScalarType = TScalar; 72 | using TensorType = SymmetricSecondRankTensor; 73 | using TensorImageType = Image; 74 | using RegionType = ImageRegion; 75 | 76 | void 77 | SetInputImage(const ImageType * image); 78 | void 79 | SetInputTensor(const TensorImageType * tensorImage); 80 | 81 | void 82 | SetMaxDiffusionTime(ScalarType time); 83 | itkGetConstMacro(DiffusionTime, ScalarType); 84 | 85 | void 86 | SetMaxNumberOfTimeSteps(int n); 87 | itkGetConstMacro(MaxNumberOfTimeSteps, int); 88 | 89 | void 90 | SetRatioToMaxStableTimeStep(ScalarType ratio); 91 | itkGetConstMacro(RatioToMaxStableTimeStep, ScalarType); 92 | 93 | itkGetConstMacro(EffectiveDiffusionTime, ScalarType); 94 | itkGetConstMacro(EffectiveNumberOfTimeSteps, int); 95 | 96 | protected: 97 | LinearAnisotropicDiffusionLBRImageFilter(); 98 | ~LinearAnisotropicDiffusionLBRImageFilter() override = default; 99 | 100 | typename ImageType::ConstPointer 101 | GetInputImage(); 102 | typename TensorImageType::ConstPointer 103 | GetInputTensor(); 104 | 105 | using IndexType = Index; 106 | 107 | // ******* Containers for the stencils used in the discretization 108 | static const unsigned int HalfStencilSize = (ImageDimension == 2) ? 3 : 6; 109 | static const unsigned int StencilSize = 2 * HalfStencilSize; 110 | 111 | using StencilCoefficientsType = Vector; 112 | using OffsetType = Offset; 113 | using StencilOffsetsType = Vector; 114 | 115 | using InternalSizeT = int; 116 | using StencilBufferIndicesType = Vector; 117 | 118 | 119 | // *************** Computation ***************** 120 | void 121 | GenerateData() override; 122 | virtual void 123 | GenerateStencils(); /// Automatically called by GenerateData 124 | virtual void 125 | ImageUpdateLoop(); /// Automatically called by GenerateData 126 | 127 | using StencilType = std::pair; 128 | using StencilImageType = Image; 129 | typename StencilImageType::Pointer m_StencilImage; 130 | 131 | using ScalarImageType = Image; 132 | typename ScalarImageType::Pointer m_DiagonalCoefficients; 133 | 134 | virtual ScalarType 135 | MaxStableTimeStep(); 136 | 137 | ScalarType m_DiffusionTime; 138 | ScalarType m_RatioToMaxStableTimeStep; 139 | int m_MaxNumberOfTimeSteps{ 10 }; 140 | 141 | ScalarType m_EffectiveDiffusionTime; 142 | int m_EffectiveNumberOfTimeSteps{ 0 }; 143 | 144 | virtual void 145 | ImageUpdate(ScalarType delta); 146 | typename ImageType::Pointer m_PreviousImage; 147 | typename ImageType::Pointer m_NextImage; 148 | 149 | virtual RegionType 150 | GetRequestedRegion() 151 | { 152 | return GetInputImage()->GetRequestedRegion(); 153 | } 154 | 155 | InternalSizeT 156 | OutsideBufferIndex() const 157 | { 158 | return NumericTraits::max(); 159 | } 160 | 161 | struct StencilFunctor; 162 | struct FunctorType; 163 | 164 | using VectorType = Vector; 165 | static ScalarType 166 | ScalarProduct(const TensorType &, const VectorType &, const VectorType &); 167 | }; 168 | } // end namespace itk 169 | 170 | 171 | #ifndef ITK_MANUAL_INSTANTIATION 172 | # include "itkLinearAnisotropicDiffusionLBRImageFilter.hxx" 173 | #endif 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /include/itkAnisotropicDiffusionLBRImageFilter.hxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 28/02/2014. 21 | // 22 | // 23 | 24 | #ifndef itkAnisotropicDiffusionLBRImageFilter_hxx 25 | #define itkAnisotropicDiffusionLBRImageFilter_hxx 26 | 27 | 28 | namespace itk 29 | { 30 | 31 | template 32 | AnisotropicDiffusionLBRImageFilter::AnisotropicDiffusionLBRImageFilter() 33 | : m_NoiseScale(0.5) 34 | , m_FeatureScale(2) 35 | , m_RatioToMaxStableTimeStep(0.7) 36 | , 37 | 38 | m_DiffusionTime(1) 39 | 40 | {} 41 | 42 | 43 | template 44 | void 45 | AnisotropicDiffusionLBRImageFilter::GenerateData() 46 | { 47 | typename ImageType::Pointer inputImage = const_cast(this->GetInput()); 48 | typename ImageType::Pointer image = inputImage; 49 | 50 | using SpacingType = typename ImageType::SpacingType; 51 | const SpacingType referenceSpacing = inputImage->GetSpacing(); 52 | 53 | // const SpacingType unitSpacing(1); // Better below for non-uniform spacing. 54 | double minSpacing = referenceSpacing[0]; 55 | for (ImageDimensionType i = 1; i < ImageDimension; ++i) 56 | { 57 | minSpacing = std::min(minSpacing, referenceSpacing[i]); 58 | } 59 | const SpacingType unitSpacing = referenceSpacing / minSpacing; 60 | 61 | if (m_Adimensionize) 62 | { 63 | inputImage->SetSpacing(unitSpacing); 64 | } 65 | 66 | ScalarType remainingTime = m_DiffusionTime; 67 | 68 | while (remainingTime > 0) 69 | { 70 | ComputeDiffusionTensors(image); 71 | typename LinearDiffusionFilterType::Pointer linearDiffusionFilter = LinearDiffusionFilterType::New(); 72 | linearDiffusionFilter->SetMaxNumberOfTimeSteps(m_MaxTimeStepsBetweenTensorUpdates); 73 | linearDiffusionFilter->SetRatioToMaxStableTimeStep(m_RatioToMaxStableTimeStep); 74 | 75 | linearDiffusionFilter->SetInputImage(image); 76 | linearDiffusionFilter->SetInputTensor(m_TensorImage); 77 | linearDiffusionFilter->SetMaxDiffusionTime(remainingTime); 78 | linearDiffusionFilter->Update(); 79 | image = linearDiffusionFilter->GetOutput(); 80 | remainingTime -= linearDiffusionFilter->GetEffectiveDiffusionTime(); 81 | 82 | m_LinearFilterEffectiveTimesAndIterations.push_back(std::pair( 83 | linearDiffusionFilter->GetEffectiveDiffusionTime(), linearDiffusionFilter->GetEffectiveNumberOfTimeSteps())); 84 | 85 | this->UpdateProgress(1. - remainingTime / m_DiffusionTime); 86 | } 87 | 88 | if (m_Adimensionize) 89 | { 90 | inputImage->SetSpacing(referenceSpacing); 91 | image->SetSpacing(referenceSpacing); 92 | } 93 | this->GraftOutput(image); 94 | } 95 | 96 | 97 | template 98 | struct AnisotropicDiffusionLBRImageFilter::DiffusionTensorFunctor 99 | { 100 | Self * eigenValuesFunctor; 101 | struct OrderingType; 102 | TensorType 103 | operator()(const TensorType & S) 104 | { 105 | EigenValuesArrayType eigenValues; 106 | typename TensorType::EigenVectorsMatrixType eigenVectors; 107 | S.ComputeEigenAnalysis(eigenValues, eigenVectors); 108 | 109 | // For convenience, eigenvalues are sorted by increasing order 110 | Vector order; 111 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 112 | order[i] = i; 113 | 114 | OrderingType ordering(eigenValues); 115 | 116 | std::sort(order.Begin(), order.End(), ordering); 117 | 118 | std::sort(eigenValues.Begin(), eigenValues.End()); 119 | EigenValuesArrayType ev = this->eigenValuesFunctor->EigenValuesTransform(eigenValues); 120 | 121 | TensorType DiffusionTensor; 122 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 123 | { 124 | DiffusionTensor(order[i], order[i]) = ev[i]; 125 | for (ImageDimensionType j = 0; j < i; ++j) 126 | DiffusionTensor(i, j) = 0.; 127 | } 128 | return DiffusionTensor.Rotate(eigenVectors.GetTranspose()); 129 | } 130 | }; 131 | 132 | 133 | // c++ 11 would be : [& eigenValues](int i, int j)->bool {return eigenValues[i] 135 | struct AnisotropicDiffusionLBRImageFilter::DiffusionTensorFunctor ::OrderingType 136 | { 137 | bool 138 | operator()(int i, int j) const 139 | { 140 | return this->e[i] < this->e[j]; 141 | } 142 | const EigenValuesArrayType & e; 143 | OrderingType(const EigenValuesArrayType & e_) 144 | : e(e_) {}; 145 | }; 146 | 147 | template 148 | void 149 | AnisotropicDiffusionLBRImageFilter::ComputeDiffusionTensors(ImageType * image) 150 | { 151 | typename StructureTensorFilterType::Pointer structureTensorFilter = StructureTensorFilterType::New(); 152 | 153 | structureTensorFilter->SetNoiseScale(m_NoiseScale); 154 | structureTensorFilter->SetFeatureScale(m_FeatureScale); 155 | structureTensorFilter->SetRescaleForUnitMaximumTrace(m_Adimensionize); 156 | structureTensorFilter->SetInput(image); 157 | 158 | using ImageFunctorType = UnaryFunctorImageFilter; 159 | typename ImageFunctorType::Pointer imageFunctor = ImageFunctorType::New(); 160 | imageFunctor->GetFunctor().eigenValuesFunctor = this; 161 | imageFunctor->SetInput(structureTensorFilter->GetOutput()); 162 | 163 | imageFunctor->Update(); 164 | m_TensorImage = imageFunctor->GetOutput(); 165 | } 166 | 167 | } // end namespace itk 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /include/itkStructureTensorImageFilter.hxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 21/11/2014. 21 | // 22 | // 23 | 24 | #ifndef itkStructureTensorImageFilter_hxx 25 | #define itkStructureTensorImageFilter_hxx 26 | 27 | #include "itkMinimumMaximumImageCalculator.h" 28 | 29 | namespace itk 30 | { 31 | 32 | template 33 | StructureTensorImageFilter::StructureTensorImageFilter() 34 | : m_FeatureScale(2) 35 | , m_NoiseScale(1) 36 | 37 | {} 38 | 39 | 40 | template 41 | void 42 | StructureTensorImageFilter::IntermediateFilter(const Dispatch &) 43 | { 44 | using GradientFilterType = GradientRecursiveGaussianImageFilter; 45 | typename GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); 46 | gradientFilter->SetInput(this->GetInput()); 47 | gradientFilter->SetSigma(this->m_NoiseScale); 48 | 49 | using OuterFilterType = UnaryFunctorImageFilter; 50 | typename OuterFilterType::Pointer outerFilter = OuterFilterType::New(); 51 | outerFilter->SetInput(gradientFilter->GetOutput()); 52 | 53 | outerFilter->Update(); 54 | this->m_IntermediateResult = outerFilter->GetOutput(); 55 | } 56 | 57 | 58 | template 59 | void 60 | StructureTensorImageFilter::IntermediateFilter(const Dispatch &) 61 | { 62 | typename Self::ImageType::ConstPointer input = this->GetInput(); 63 | typename Self::TensorImageType::Pointer output = Self::TensorImageType::New(); 64 | output->CopyInformation(input); 65 | output->SetRegions(input->GetRequestedRegion()); 66 | output->Allocate(); 67 | output->FillBuffer(Self::TensorType(0.)); 68 | 69 | for (unsigned int index = 0; index < Self::PixelType::Dimension; ++index) 70 | { 71 | using SelectionFilterType = VectorIndexSelectionCastImageFilter; 72 | typename SelectionFilterType::Pointer selectionFilter = SelectionFilterType::New(); 73 | selectionFilter->SetIndex(index); 74 | selectionFilter->SetInput(input); 75 | 76 | using GaussianFilterType = RecursiveGaussianImageFilter; 77 | using GradientFilterType = 78 | GradientImageFilter; 79 | using GradientGaussianFilterType = 80 | GradientRecursiveGaussianImageFilter; 81 | 82 | typename GaussianFilterType::Pointer gaussianFilter = GaussianFilterType::New(); 83 | typename GradientFilterType::Pointer gradientFilter = GradientFilterType::New(); 84 | typename GradientGaussianFilterType::Pointer gradientGaussianFilter = GradientGaussianFilterType::New(); 85 | 86 | gaussianFilter->SetSigma(this->m_NoiseScale); 87 | gradientGaussianFilter->SetSigma(this->m_NoiseScale); 88 | 89 | using OuterFilterType = 90 | UnaryFunctorImageFilter; 91 | typename OuterFilterType::Pointer outerFilter = OuterFilterType::New(); 92 | 93 | if (this->m_UseGradientRecursiveGaussianImageFilter) 94 | { 95 | gradientGaussianFilter->SetInput(selectionFilter->GetOutput()); 96 | outerFilter->SetInput(gradientGaussianFilter->GetOutput()); 97 | } 98 | else 99 | { 100 | gaussianFilter->SetInput(selectionFilter->GetOutput()); 101 | gradientFilter->SetInput(gaussianFilter->GetOutput()); 102 | outerFilter->SetInput(gradientFilter->GetOutput()); 103 | } 104 | 105 | using AddFilterType = AddImageFilter; 106 | typename AddFilterType::Pointer addFilter = AddFilterType::New(); 107 | addFilter->InPlaceOn(); 108 | addFilter->SetInput1(output); 109 | addFilter->SetInput2(outerFilter->GetOutput()); 110 | addFilter->Update(); 111 | output = addFilter->GetOutput(); 112 | 113 | this->UpdateProgress(index / float(Self::PixelType::Dimension + 1)); 114 | } 115 | this->m_IntermediateResult = output; 116 | } 117 | 118 | 119 | template 120 | void 121 | StructureTensorImageFilter::GenerateData() 122 | { 123 | this->IntermediateFilter(Dispatch::is_specialized>()); 124 | 125 | using GaussianFilterType = RecursiveGaussianImageFilter; 126 | typename GaussianFilterType::Pointer gaussianFilter = GaussianFilterType::New(); 127 | gaussianFilter->SetInput(m_IntermediateResult); 128 | gaussianFilter->SetSigma(m_FeatureScale); 129 | 130 | if (!m_RescaleForUnitMaximumTrace) 131 | { 132 | m_PostRescaling = 1.; 133 | gaussianFilter->Update(); 134 | this->GraftOutput(gaussianFilter->GetOutput()); 135 | return; 136 | } 137 | 138 | // *** Rescaling for normalization of largest trace *** 139 | 140 | using TraceFilterType = UnaryFunctorImageFilter; 141 | typename TraceFilterType::Pointer traceFilter = TraceFilterType::New(); 142 | traceFilter->SetInput(gaussianFilter->GetOutput()); 143 | 144 | using MaximumCalculatorType = MinimumMaximumImageCalculator; 145 | typename MaximumCalculatorType::Pointer maximumCalculator = MaximumCalculatorType::New(); 146 | maximumCalculator->SetImage(traceFilter->GetOutput()); 147 | 148 | using ScaleFilterType = UnaryFunctorImageFilter; 149 | typename ScaleFilterType::Pointer scaleFilter = ScaleFilterType::New(); 150 | scaleFilter->SetInput(gaussianFilter->GetOutput()); 151 | 152 | traceFilter->Update(); 153 | maximumCalculator->ComputeMaximum(); 154 | m_PostRescaling = 1. / maximumCalculator->GetMaximum(); 155 | scaleFilter->GetFunctor().scaling = m_PostRescaling; 156 | scaleFilter->Update(); 157 | this->GraftOutput(scaleFilter->GetOutput()); 158 | } 159 | 160 | } // end namespace itk 161 | 162 | #endif 163 | -------------------------------------------------------------------------------- /include/CoherenceEnhancingDiffusionCommandLine.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | // 19 | // 20 | // Created by Jean-Marie Mirebeau on 20/11/2014. 21 | // 22 | // 23 | 24 | #ifndef itkDiffusion_CoherenceEnhancingDiffusionCommandLine_h 25 | #define itkDiffusion_CoherenceEnhancingDiffusionCommandLine_h 26 | 27 | #include "itkImageFileReader.h" 28 | #include "itkImageFileWriter.h" 29 | #include "itkCoherenceEnhancingDiffusionImageFilter.h" 30 | #include "LinearAnisotropicDiffusionCommandLine.h" 31 | #include "itkTimeProbe.h" 32 | 33 | namespace CoherenceEnhancingDiffusionCommandLine 34 | { 35 | 36 | void 37 | Usage() 38 | { 39 | std::cerr << "Input image filename. 2D and 3D images supported. Required.\n" 40 | << "Output image filename. Required.\n" 41 | << "Diffusion time. Suggested range: 0.5-5, up to 50 for strong noise. Default: 2.\n" 42 | << "Lambda. Small values detect more edges. Suggested range: 0.05, 0.0001. Default: 0.01\n" 43 | << "Weickert diffusion type. Accepted values: CED, cCED, EED, cEED, Isotropic. Default: CED.\n" 44 | << "Noise scale. Suggested range: 0.5 - 4. Default 1.\n" 45 | << "Feature scale. Suggested range: 2-6. Default 2.\n" 46 | << "\n"; 47 | } 48 | 49 | using namespace itk; 50 | 51 | int 52 | Execute(int argc, char * argv[]); 53 | 54 | template 55 | int 56 | Execute(int argc, char * argv[], itk::ImageIOBase::IOComponentEnum, int nComponents); 57 | 58 | template 59 | int 60 | Execute(int argc, char * argv[], int nComponents); 61 | 62 | template 63 | int 64 | Execute(int argc, char * argv[]); 65 | 66 | 67 | using ReportProgressToCOutType = LinearAnisotropicDiffusionCommandLine::ReportProgressToCOutType; 68 | 69 | int 70 | Execute(int argc, char * argv[]) 71 | { 72 | using std::cerr; 73 | using std::endl; 74 | using namespace itk; 75 | 76 | if (argc < 2 + 1) 77 | { 78 | Usage(); 79 | return EXIT_SUCCESS; 80 | } 81 | 82 | const char * imageFileName = argv[1]; 83 | 84 | itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(imageFileName, itk::IOFileModeEnum::ReadMode); 85 | if (imageIO.IsNull()) 86 | { 87 | std::cerr << "Could not create ImageIO" << std::endl; 88 | return EXIT_FAILURE; 89 | } 90 | imageIO->SetFileName(imageFileName); 91 | imageIO->ReadImageInformation(); 92 | 93 | const unsigned int imageDimension = imageIO->GetNumberOfDimensions(); 94 | const itk::ImageIOBase::IOComponentEnum componentType = imageIO->GetComponentType(); 95 | const unsigned int nComponents = imageIO->GetNumberOfComponents(); 96 | 97 | switch (imageDimension) 98 | { 99 | case 2: 100 | return Execute<2>(argc, argv, componentType, nComponents); 101 | case 3: 102 | return Execute<3>(argc, argv, componentType, nComponents); 103 | default: 104 | itkGenericExceptionMacro("Sorry, unsupported image dimension."); 105 | } 106 | } 107 | 108 | template 109 | int 110 | Execute(int argc, char * argv[], itk::IOComponentEnum componentType, int nComponents) 111 | { 112 | switch (componentType) 113 | { 114 | case itk::IOComponentEnum::UCHAR: 115 | return Execute(argc, argv, nComponents); 116 | case itk::IOComponentEnum::FLOAT: 117 | return Execute(argc, argv, nComponents); 118 | case itk::IOComponentEnum::DOUBLE: 119 | return Execute(argc, argv, nComponents); 120 | default: 121 | itkGenericExceptionMacro("Sorry, unsupported component type"); 122 | } 123 | } 124 | 125 | template 126 | int 127 | Execute(int argc, char * argv[], int nComponents) 128 | { 129 | switch (nComponents) 130 | { 131 | case 1: 132 | return Execute(argc, argv); 133 | case 2: 134 | return Execute, Vector>(argc, argv); 135 | case 3: 136 | return Execute, Vector>(argc, argv); 137 | default: 138 | itkGenericExceptionMacro("Sorry, unsupported number of components"); 139 | } 140 | } 141 | 142 | template 143 | int 144 | Execute(int argc, char * argv[]) 145 | { 146 | using ImageType = Image; 147 | 148 | using ReaderType = ImageFileReader; 149 | typename ReaderType::Pointer reader = ReaderType::New(); 150 | 151 | const char * imageFileName = argv[1]; 152 | const char * outputFileName = argv[2]; 153 | reader->SetFileName(imageFileName); 154 | 155 | using DiffusionFilterType = CoherenceEnhancingDiffusionImageFilter; 156 | typename DiffusionFilterType::Pointer diffusionFilter = DiffusionFilterType::New(); 157 | diffusionFilter->SetInput(reader->GetOutput()); 158 | 159 | ReportProgressToCOutType::Pointer reportDiffusionProgress = ReportProgressToCOutType::New(); 160 | diffusionFilter->AddObserver(ProgressEvent(), reportDiffusionProgress); 161 | 162 | int argIndex = 3; 163 | if (argIndex < argc) 164 | { 165 | const double diffusionTime = std::stod(argv[argIndex++]); 166 | if (diffusionTime == 0) 167 | itkGenericExceptionMacro("Error: Unrecognized diffusion time (third argument).\n"); 168 | diffusionFilter->SetDiffusionTime(diffusionTime); 169 | } 170 | 171 | if (argIndex < argc) 172 | { 173 | const double lambda = std::stod(argv[argIndex++]); 174 | if (lambda == 0.) 175 | itkGenericExceptionMacro("Error: Unrecognized lambda (fourth argument).\n"); 176 | diffusionFilter->SetLambda(lambda); 177 | } 178 | 179 | if (argIndex < argc) 180 | { 181 | const char * enhancement = argv[argIndex++]; 182 | if (!strcmp(enhancement, "EED")) 183 | diffusionFilter->SetEnhancement(DiffusionFilterType::EED); // Weickert's exponent : 4. 184 | else if (!strcmp(enhancement, "cEED")) 185 | diffusionFilter->SetEnhancement(DiffusionFilterType::cEED); // Weickert's exponent : 4. 186 | else if (!strcmp(enhancement, "CED")) 187 | diffusionFilter->SetEnhancement(DiffusionFilterType::CED); // Weickert's exponent : 2. 188 | else if (!strcmp(enhancement, "cCED")) 189 | diffusionFilter->SetEnhancement(DiffusionFilterType::cCED); // Weickert's exponent : 2. 190 | else if (!strcmp(enhancement, "Isotropic")) 191 | diffusionFilter->SetEnhancement(DiffusionFilterType::Isotropic); // Perona-Mali's exponent: 2. 192 | else 193 | itkGenericExceptionMacro("Error: Unrecognized enhancement (fifth argument).\n"); 194 | } 195 | 196 | if (argIndex < argc) 197 | { 198 | const double noiseScale = std::stod(argv[argIndex++]); 199 | if (noiseScale == 0.) 200 | itkGenericExceptionMacro("Error: Unrecognized noiseScale (sixth argument).\n"); 201 | diffusionFilter->SetNoiseScale(noiseScale); 202 | } 203 | 204 | if (argIndex < argc) 205 | { 206 | const double featureScale = std::stod(argv[argIndex++]); 207 | if (featureScale == 0.) 208 | itkGenericExceptionMacro("Error: Unrecognized featureScale (seventh argument).\n"); 209 | diffusionFilter->SetFeatureScale(featureScale); 210 | } 211 | 212 | if (argIndex < argc) 213 | { 214 | const double exponent = std::stod(argv[argIndex++]); 215 | if (exponent == 0.) 216 | itkGenericExceptionMacro("Error: Unrecognized exponent (eighth argument).\n"); 217 | diffusionFilter->SetExponent(exponent); 218 | } 219 | 220 | if (argIndex < argc) 221 | { 222 | itkGenericExceptionMacro("Error: excessive number of arguments"); 223 | } 224 | 225 | /*{ 226 | std::cerr << 227 | "T: " << diffusionFilter->GetDiffusionTime() << "\n" << 228 | "Lambda: " << diffusionFilter->GetLambda() << "\n" << 229 | "argc: " << argc << "\n"; 230 | 231 | diffusionFilter->Update(); 232 | auto image = diffusionFilter->GetOutput(); 233 | std::cerr << 234 | image->GetBufferedRegion() << "\n\n" << 235 | "pixel export size: " << sizeof(ExportPixelType) << "\n"; 236 | ; 237 | 238 | }*/ 239 | 240 | using ExportImageType = Image; 241 | using CasterType = CastImageFilter; 242 | typename CasterType::Pointer caster = CasterType::New(); 243 | caster->SetInput(diffusionFilter->GetOutput()); 244 | 245 | // using ScalarImageType = typename DiffusionFilterType::ScalarImageType; 246 | using WriterType = ImageFileWriter; 247 | typename WriterType::Pointer writer = WriterType::New(); 248 | writer->SetInput(caster->GetOutput()); 249 | writer->SetFileName(outputFileName); 250 | 251 | itk::TimeProbe clock; 252 | clock.Start(); 253 | writer->Update(); 254 | clock.Stop(); 255 | std::cout << "Took: " << clock.GetMean() << " seconds\n"; 256 | 257 | return EXIT_SUCCESS; 258 | } 259 | 260 | } // end namespace CoherenceEnhancingDiffusionCommandLine 261 | 262 | #endif 263 | -------------------------------------------------------------------------------- /include/LinearAnisotropicDiffusionCommandLine.h: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 01/12/2014. 21 | // 22 | // 23 | 24 | #ifndef itkLinearAnisotropicDiffusionCommandLine_h 25 | #define itkLinearAnisotropicDiffusionCommandLine_h 26 | 27 | #include "itkImageFileReader.h" 28 | #include "itkImageFileWriter.h" 29 | #include "itkCoherenceEnhancingDiffusionImageFilter.h" 30 | 31 | namespace LinearAnisotropicDiffusionCommandLine 32 | { 33 | 34 | void 35 | Usage() 36 | { 37 | std::cerr << "Input image filename. 2D and 3D images supported. Required.\n" 38 | << "Output tensor field filename. Required.\n" 39 | << "Diffusion time. Required.\n" 40 | << "Output image filename. Required.\n" 41 | << "RatioToMaxStableStep. Range: ]0,1]. Default: 0.7. Optionnal.\n" 42 | << "MaxNumberOfIterations. Range: 1...Infinity. Default: 200. Optionnal.\n" 43 | << "\n"; 44 | } 45 | 46 | using namespace itk; 47 | 48 | int 49 | Execute(int argc, char * argv[]); 50 | 51 | template 52 | int 53 | Execute(int argc, char * argv[], itk::ImageIOBase::IOComponentEnum, int nComponents); 54 | 55 | template 56 | int 57 | Execute(int argc, char * argv[], int nComponents); 58 | 59 | template 60 | int 61 | Execute(int argc, char * argv[]); 62 | 63 | 64 | struct ReportProgressToCOutType : public itk::Command 65 | { 66 | itkNewMacro(ReportProgressToCOutType); 67 | void 68 | Execute(itk::Object * caller, const itk::EventObject & event) override 69 | { 70 | Execute((const itk::Object *)caller, event); 71 | } 72 | 73 | void 74 | Execute(const itk::Object * object, const itk::EventObject &) override 75 | { 76 | std::cout << object->GetNameOfClass() 77 | << " has completed: " << int(100 * dynamic_cast(object)->GetProgress()) << "%" 78 | << std::endl; 79 | } 80 | }; 81 | 82 | 83 | int 84 | Execute(int argc, char * argv[]) 85 | { 86 | using std::cerr; 87 | using std::endl; 88 | using namespace itk; 89 | 90 | if (argc < 4 + 1) 91 | { 92 | Usage(); 93 | return EXIT_SUCCESS; 94 | } 95 | 96 | const char * imageFileName = argv[0 + 1]; 97 | using ReaderType = ImageFileReader>; 98 | ReaderType::Pointer reader = ReaderType::New(); 99 | reader->SetFileName(imageFileName); 100 | 101 | reader->UpdateOutputInformation(); 102 | 103 | const ImageIOBase * io = reader->GetImageIO(); 104 | const int ImageDimension = io->GetNumberOfDimensions(); 105 | const itk::ImageIOBase::IOComponentEnum componentType = io->GetComponentType(); 106 | const int nComponents = io->GetNumberOfComponents(); 107 | 108 | { 109 | const char * tensorFileName = argv[1 + 1]; 110 | ReaderType::Pointer reader2 = ReaderType::New(); 111 | reader2->SetFileName(tensorFileName); 112 | reader2->UpdateOutputInformation(); 113 | const ImageIOBase * io2 = reader2->GetImageIO(); 114 | if (io2->GetComponentType() != componentType) 115 | std::cerr << "Warning: image and tensors have distinct component types.\n"; 116 | if (ImageDimension != (int)io2->GetNumberOfDimensions()) 117 | itkGenericExceptionMacro("Error: image and tensor image have distinct dimension"); 118 | const int TensorDimension = (ImageDimension * (ImageDimension + 1)) / 2; 119 | if (TensorDimension != (int)io2->GetNumberOfComponents()) 120 | itkGenericExceptionMacro("Error: wrong tensor dimension, should be n*(n+1)/2 where n=ImageDimension."); 121 | if (io2->GetPixelType() == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) 122 | std::cerr << "Warning: tensor image pixel type not marked as Symmetric Second Rank Tensor.\n"; 123 | } 124 | 125 | switch (ImageDimension) 126 | { 127 | case 2: 128 | return Execute<2>(argc, argv, componentType, nComponents); 129 | // case 3: return Execute<3>(argc,argv,componentType,nComponents); 130 | default: 131 | itkGenericExceptionMacro("Sorry, unsupported image dimension."); 132 | } 133 | } 134 | 135 | template 136 | int 137 | Execute(int argc, char * argv[], itk::ImageIOBase::IOComponentEnum componentType, int nComponents) 138 | { 139 | switch (componentType) 140 | { 141 | case itk::IOComponentEnum::UCHAR: 142 | return Execute(argc, argv, nComponents); 143 | case itk::IOComponentEnum::FLOAT: 144 | return Execute(argc, argv, nComponents); 145 | case itk::IOComponentEnum::DOUBLE: 146 | return Execute(argc, argv, nComponents); 147 | default: 148 | itkGenericExceptionMacro("Sorry, unsupported component type"); 149 | } 150 | } 151 | 152 | template 153 | int 154 | Execute(int argc, char * argv[], int nComponents) 155 | { 156 | switch (nComponents) 157 | { 158 | case 1: 159 | return Execute(argc, argv); 160 | case 2: 161 | return Execute, Vector>(argc, argv); 162 | case 3: 163 | return Execute, Vector>(argc, argv); 164 | default: 165 | itkGenericExceptionMacro("Sorry, unsupported number of components"); 166 | } 167 | } 168 | 169 | template 170 | int 171 | Execute(int argc, char * argv[]) 172 | { 173 | 174 | // Import image 175 | using ImageType = Image; 176 | using ReaderType = ImageFileReader; 177 | typename ReaderType::Pointer reader = ReaderType::New(); 178 | const char * imageFileName = argv[0 + 1]; 179 | reader->SetFileName(imageFileName); 180 | 181 | // Import tensors 182 | using TensorType = SymmetricSecondRankTensor; 183 | using TensorImageType = Image; 184 | using TensorReaderType = ImageFileReader; 185 | typename TensorReaderType::Pointer tensorReader = TensorReaderType::New(); 186 | const char * tensorImageFileName = argv[1 + 1]; 187 | tensorReader->SetFileName(tensorImageFileName); 188 | 189 | // Import diffusion time 190 | const double diffusionTime = std::stod(argv[2 + 1]); 191 | if (diffusionTime == 0) 192 | itkGenericExceptionMacro("Error: Unrecognized diffusion time (third argument).\n"); 193 | 194 | // Import output image filename 195 | const char * outputFileName = argv[3 + 1]; 196 | 197 | // Setup diffusion filter 198 | using DiffusionFilterType = LinearAnisotropicDiffusionLBRImageFilter; 199 | typename DiffusionFilterType::Pointer diffusionFilter = DiffusionFilterType::New(); 200 | diffusionFilter->SetInputImage(reader->GetOutput()); 201 | diffusionFilter->SetInputTensor(tensorReader->GetOutput()); 202 | diffusionFilter->SetMaxDiffusionTime(diffusionTime); 203 | 204 | int argIndex = 4 + 1; 205 | if (argIndex < argc) 206 | { 207 | const double ratioToMaxStableTimeStep = std::stod(argv[argIndex++]); 208 | if (ratioToMaxStableTimeStep == 0) 209 | itkGenericExceptionMacro("Error: Unrecognized RatioToMaxStableTimeStep (fourth argument).\n"); 210 | diffusionFilter->SetRatioToMaxStableTimeStep(ratioToMaxStableTimeStep); 211 | } 212 | 213 | if (argIndex < argc) 214 | { 215 | const int maxNumberOfTimeSteps = std::stoi(argv[argIndex++]); 216 | if (maxNumberOfTimeSteps == 0) 217 | itkGenericExceptionMacro("Error: Unrecognized maxNumberOfTimeSteps (fifth argument).\n"); 218 | diffusionFilter->SetMaxNumberOfTimeSteps(maxNumberOfTimeSteps); 219 | } 220 | else 221 | diffusionFilter->SetMaxNumberOfTimeSteps(200); 222 | 223 | ReportProgressToCOutType::Pointer reportDiffusionProgress = ReportProgressToCOutType::New(); 224 | diffusionFilter->AddObserver(ProgressEvent(), reportDiffusionProgress); 225 | 226 | using ExportImageType = Image; 227 | using CasterType = CastImageFilter; 228 | typename CasterType::Pointer caster = CasterType::New(); 229 | caster->SetInput(diffusionFilter->GetOutput()); 230 | 231 | // using ScalarImageType = typename DiffusionFilterType::ScalarImageType; 232 | using WriterType = ImageFileWriter; 233 | typename WriterType::Pointer writer = WriterType::New(); 234 | writer->SetInput(caster->GetOutput()); 235 | writer->SetFileName(outputFileName); 236 | writer->Update(); 237 | 238 | const ScalarType effectiveDiffusionTime = diffusionFilter->GetEffectiveDiffusionTime(); 239 | if (effectiveDiffusionTime < 0.99 * diffusionTime) 240 | { 241 | std::cerr << "Warning: early abort at effective diffusion time: " << effectiveDiffusionTime 242 | << ", you may want to increase the max number of time steps: " 243 | << diffusionFilter->GetMaxNumberOfTimeSteps() << "\n"; 244 | Usage(); 245 | } 246 | 247 | return EXIT_SUCCESS; 248 | } 249 | 250 | } // end namespace LinearAnisotropicDiffusionCommandLine 251 | 252 | #endif 253 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | ## This config file is only relevant for clang-format version 19.1.7 2 | ## 3 | ## Examples of each format style can be found on the in the clang-format documentation 4 | ## See: https://clang.llvm.org/docs/ClangFormatStyleOptions.html for details of each option 5 | ## 6 | ## The clang-format binaries can be downloaded as part of the clang binary distributions 7 | ## from https://releases.llvm.org/download.html 8 | ## 9 | ## Use the script Utilities/Maintenance/clang-format.bash to faciliate 10 | ## maintaining a consistent code style. 11 | ## 12 | ## EXAMPLE apply code style enforcement before commit: 13 | # Utilities/Maintenance/clang-format.bash --clang ${PATH_TO_CLANG_FORMAT_19.1.7} --modified 14 | ## EXAMPLE apply code style enforcement after commit: 15 | # Utilities/Maintenance/clang-format.bash --clang ${PATH_TO_CLANG_FORMAT_19.1.7} --last 16 | --- 17 | # This configuration requires clang-format version 19.1.7 exactly. 18 | Language: Cpp 19 | AccessModifierOffset: -2 20 | AlignAfterOpenBracket: Align 21 | AlignArrayOfStructures: None 22 | AlignConsecutiveAssignments: 23 | Enabled: false 24 | AcrossEmptyLines: false 25 | AcrossComments: false 26 | AlignCompound: false 27 | AlignFunctionPointers: false 28 | PadOperators: true 29 | AlignConsecutiveBitFields: 30 | Enabled: false 31 | AcrossEmptyLines: false 32 | AcrossComments: false 33 | AlignCompound: false 34 | AlignFunctionPointers: false 35 | PadOperators: false 36 | AlignConsecutiveDeclarations: 37 | Enabled: true 38 | AcrossEmptyLines: false 39 | AcrossComments: false 40 | AlignCompound: false 41 | AlignFunctionPointers: false 42 | PadOperators: true 43 | AlignConsecutiveMacros: 44 | Enabled: false 45 | AcrossEmptyLines: false 46 | AcrossComments: false 47 | AlignCompound: false 48 | AlignFunctionPointers: false 49 | PadOperators: false 50 | AlignConsecutiveShortCaseStatements: 51 | Enabled: false 52 | AcrossEmptyLines: false 53 | AcrossComments: false 54 | AlignCaseArrows: false 55 | AlignCaseColons: false 56 | AlignConsecutiveTableGenBreakingDAGArgColons: 57 | Enabled: false 58 | AcrossEmptyLines: false 59 | AcrossComments: false 60 | AlignCompound: false 61 | AlignFunctionPointers: false 62 | PadOperators: false 63 | AlignConsecutiveTableGenCondOperatorColons: 64 | Enabled: false 65 | AcrossEmptyLines: false 66 | AcrossComments: false 67 | AlignCompound: false 68 | AlignFunctionPointers: false 69 | PadOperators: false 70 | AlignConsecutiveTableGenDefinitionColons: 71 | Enabled: false 72 | AcrossEmptyLines: false 73 | AcrossComments: false 74 | AlignCompound: false 75 | AlignFunctionPointers: false 76 | PadOperators: false 77 | AlignEscapedNewlines: Left 78 | AlignOperands: Align 79 | AlignTrailingComments: 80 | Kind: Always 81 | OverEmptyLines: 0 82 | AllowAllArgumentsOnNextLine: true 83 | AllowAllParametersOfDeclarationOnNextLine: false 84 | AllowBreakBeforeNoexceptSpecifier: Never 85 | AllowShortBlocksOnASingleLine: Never 86 | AllowShortCaseExpressionOnASingleLine: true 87 | AllowShortCaseLabelsOnASingleLine: false 88 | AllowShortCompoundRequirementOnASingleLine: true 89 | AllowShortEnumsOnASingleLine: true 90 | #AllowShortFunctionsOnASingleLine: Inline Only merge functions defined inside a class. Implies empty. 91 | #AllowShortFunctionsOnASingleLine: None (in configuration: None) Never merge functions into a single line. 92 | AllowShortFunctionsOnASingleLine: All 93 | AllowShortIfStatementsOnASingleLine: Never 94 | AllowShortLambdasOnASingleLine: All 95 | AllowShortLoopsOnASingleLine: false 96 | AlwaysBreakAfterDefinitionReturnType: None 97 | AlwaysBreakBeforeMultilineStrings: false 98 | AttributeMacros: 99 | - __capability 100 | BinPackArguments: false 101 | BinPackParameters: false 102 | BitFieldColonSpacing: Both 103 | BraceWrapping: 104 | AfterCaseLabel: true 105 | AfterClass: true 106 | AfterControlStatement: Always 107 | AfterEnum: true 108 | AfterExternBlock: true 109 | AfterFunction: true 110 | AfterNamespace: true 111 | AfterObjCDeclaration: true 112 | AfterStruct: true 113 | AfterUnion: true 114 | BeforeCatch: true 115 | BeforeElse: true 116 | BeforeLambdaBody: false 117 | BeforeWhile: false 118 | IndentBraces: false 119 | SplitEmptyFunction: false 120 | SplitEmptyRecord: false 121 | SplitEmptyNamespace: false 122 | BreakAdjacentStringLiterals: true 123 | BreakAfterAttributes: Leave 124 | BreakAfterJavaFieldAnnotations: false 125 | BreakAfterReturnType: All 126 | BreakArrays: true 127 | BreakBeforeBinaryOperators: None 128 | BreakBeforeConceptDeclarations: Always 129 | BreakBeforeBraces: Custom 130 | BreakBeforeInlineASMColon: OnlyMultiline 131 | BreakBeforeTernaryOperators: true 132 | BreakConstructorInitializers: BeforeComma 133 | BreakFunctionDefinitionParameters: false 134 | BreakInheritanceList: BeforeComma 135 | BreakStringLiterals: true 136 | BreakTemplateDeclarations: Yes 137 | ## The following line allows larger lines in non-documentation code 138 | ColumnLimit: 120 139 | CommentPragmas: '^ IWYU pragma:' 140 | CompactNamespaces: false 141 | ConstructorInitializerIndentWidth: 2 142 | ContinuationIndentWidth: 2 143 | Cpp11BracedListStyle: false 144 | DerivePointerAlignment: false 145 | DisableFormat: false 146 | EmptyLineAfterAccessModifier: Never 147 | EmptyLineBeforeAccessModifier: LogicalBlock 148 | ExperimentalAutoDetectBinPacking: false 149 | FixNamespaceComments: true 150 | ForEachMacros: 151 | - foreach 152 | - Q_FOREACH 153 | - BOOST_FOREACH 154 | IfMacros: 155 | - KJ_IF_MAYBE 156 | IncludeBlocks: Preserve 157 | IncludeCategories: 158 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 159 | Priority: 2 160 | SortPriority: 0 161 | CaseSensitive: false 162 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 163 | Priority: 3 164 | SortPriority: 0 165 | CaseSensitive: false 166 | - Regex: '.*' 167 | Priority: 1 168 | SortPriority: 0 169 | CaseSensitive: false 170 | IncludeIsMainRegex: '(Test)?$' 171 | IncludeIsMainSourceRegex: '' 172 | IndentAccessModifiers: false 173 | IndentCaseBlocks: false 174 | IndentCaseLabels: true 175 | IndentExternBlock: AfterExternBlock 176 | IndentGotoLabels: true 177 | IndentPPDirectives: AfterHash 178 | IndentRequiresClause: true 179 | IndentWidth: 2 180 | IndentWrappedFunctionNames: false 181 | InsertBraces: false 182 | InsertNewlineAtEOF: false 183 | InsertTrailingCommas: None 184 | IntegerLiteralSeparator: 185 | Binary: 0 186 | BinaryMinDigits: 0 187 | Decimal: 0 188 | DecimalMinDigits: 0 189 | Hex: 0 190 | HexMinDigits: 0 191 | JavaScriptQuotes: Leave 192 | JavaScriptWrapImports: true 193 | KeepEmptyLines: 194 | AtEndOfFile: false 195 | AtStartOfBlock: true 196 | AtStartOfFile: true 197 | LambdaBodyIndentation: Signature 198 | LineEnding: DeriveLF 199 | MacroBlockBegin: '' 200 | MacroBlockEnd: '' 201 | MainIncludeChar: Quote 202 | MaxEmptyLinesToKeep: 2 203 | NamespaceIndentation: None 204 | ObjCBinPackProtocolList: Auto 205 | ObjCBlockIndentWidth: 2 206 | ObjCBreakBeforeNestedBlockParam: true 207 | ObjCSpaceAfterProperty: true 208 | ObjCSpaceBeforeProtocolList: false 209 | PackConstructorInitializers: BinPack 210 | PenaltyBreakAssignment: 2 211 | PenaltyBreakBeforeFirstCallParameter: 19 212 | PenaltyBreakComment: 300 213 | ## The following line allows larger lines in non-documentation code 214 | PenaltyBreakFirstLessLess: 120 215 | PenaltyBreakOpenParenthesis: 0 216 | PenaltyBreakScopeResolution: 500 217 | PenaltyBreakString: 1000 218 | PenaltyBreakTemplateDeclaration: 10 219 | PenaltyExcessCharacter: 1000000 220 | PenaltyIndentedWhitespace: 0 221 | PenaltyReturnTypeOnItsOwnLine: 200 222 | PointerAlignment: Middle 223 | PPIndentWidth: -1 224 | QualifierAlignment: Custom 225 | QualifierOrder: 226 | - friend 227 | - static 228 | - inline 229 | - constexpr 230 | - const 231 | - type 232 | ReferenceAlignment: Pointer 233 | ReflowComments: true 234 | RemoveBracesLLVM: false 235 | RemoveParentheses: Leave 236 | RemoveSemicolon: false 237 | RequiresClausePosition: OwnLine 238 | RequiresExpressionIndentation: OuterScope 239 | SeparateDefinitionBlocks: Leave 240 | ShortNamespaceLines: 1 241 | SkipMacroDefinitionBody: false 242 | # We may want to sort the includes as a separate pass 243 | SortIncludes: Never 244 | SortJavaStaticImport: Before 245 | # We may want to revisit this later 246 | SortUsingDeclarations: Never 247 | SpaceAfterCStyleCast: false 248 | SpaceAfterLogicalNot: false 249 | SpaceAfterTemplateKeyword: true 250 | SpaceAroundPointerQualifiers: Default 251 | SpaceBeforeAssignmentOperators: true 252 | SpaceBeforeCaseColon: false 253 | SpaceBeforeCpp11BracedList: false 254 | SpaceBeforeCtorInitializerColon: true 255 | SpaceBeforeInheritanceColon: true 256 | SpaceBeforeJsonColon: false 257 | SpaceBeforeParens: ControlStatements 258 | SpaceBeforeParensOptions: 259 | AfterControlStatements: true 260 | AfterForeachMacros: true 261 | AfterFunctionDefinitionName: false 262 | AfterFunctionDeclarationName: false 263 | AfterIfMacros: true 264 | AfterOverloadedOperator: false 265 | AfterPlacementOperator: true 266 | AfterRequiresInClause: false 267 | AfterRequiresInExpression: false 268 | BeforeNonEmptyParentheses: false 269 | SpaceBeforeRangeBasedForLoopColon: true 270 | SpaceBeforeSquareBrackets: false 271 | SpaceInEmptyBlock: false 272 | SpacesBeforeTrailingComments: 1 273 | SpacesInAngles: Never 274 | SpacesInContainerLiterals: false 275 | SpacesInLineCommentPrefix: 276 | Minimum: 1 277 | Maximum: -1 278 | SpacesInParens: Never 279 | SpacesInParensOptions: 280 | ExceptDoubleParentheses: false 281 | InCStyleCasts: false 282 | InConditionalStatements: false 283 | InEmptyParentheses: false 284 | Other: false 285 | SpacesInSquareBrackets: false 286 | Standard: Latest 287 | StatementAttributeLikeMacros: 288 | - Q_EMIT 289 | StatementMacros: 290 | - Q_UNUSED 291 | - QT_REQUIRE_VERSION 292 | - ITK_GCC_PRAGMA_PUSH 293 | - ITK_GCC_PRAGMA_POP 294 | - ITK_GCC_SUPPRESS_Wfloat_equal 295 | - ITK_GCC_SUPPRESS_Wformat_nonliteral 296 | - ITK_GCC_SUPPRESS_Warray_bounds 297 | - ITK_CLANG_PRAGMA_PUSH 298 | - ITK_CLANG_PRAGMA_POP 299 | - ITK_CLANG_SUPPRESS_Wzero_as_null_pointer_constant 300 | - ITK_CLANG_SUPPRESS_Wduplicate_enum 301 | - CLANG_PRAGMA_PUSH 302 | - CLANG_PRAGMA_POP 303 | - CLANG_SUPPRESS_Wfloat_equal 304 | - INTEL_PRAGMA_WARN_PUSH 305 | - INTEL_PRAGMA_WARN_POP 306 | - INTEL_SUPPRESS_warning_1292 307 | - itkTemplateFloatingToIntegerMacro 308 | - itkLegacyMacro 309 | TableGenBreakInsideDAGArg: DontBreak 310 | TabWidth: 2 311 | UseTab: Never 312 | VerilogBreakBetweenInstancePorts: true 313 | WhitespaceSensitiveMacros: 314 | - BOOST_PP_STRINGIZE 315 | - CF_SWIFT_NAME 316 | - NS_SWIFT_NAME 317 | - PP_STRINGIZE 318 | - STRINGIZE 319 | ... 320 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | https://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /include/itkLinearAnisotropicDiffusionLBRImageFilter.hxx: -------------------------------------------------------------------------------- 1 | /*========================================================================= 2 | * 3 | * Copyright NumFOCUS 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * https://www.apache.org/licenses/LICENSE-2.0.txt 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | *=========================================================================*/ 18 | 19 | // 20 | // Created by Jean-Marie Mirebeau on 28/02/2014. 21 | // 22 | // 23 | 24 | #ifndef itkLinearAnisotropicDiffusionLBRImageFilter_hxx 25 | #define itkLinearAnisotropicDiffusionLBRImageFilter_hxx 26 | 27 | #include "itkUnaryFunctorImageFilter.h" 28 | #include "itkImageRegionIterator.h" 29 | #include "itkMinimumMaximumImageCalculator.h" 30 | #include "itkCastImageFilter.h" 31 | #include "itkExtractImageFilter.h" 32 | #include "itkUnaryFunctorWithIndexImageFilter.h" 33 | #include "itkTernaryFunctorImageFilter.h" 34 | 35 | namespace itk 36 | { 37 | 38 | template 39 | LinearAnisotropicDiffusionLBRImageFilter::LinearAnisotropicDiffusionLBRImageFilter() 40 | : m_DiffusionTime(1) 41 | , m_RatioToMaxStableTimeStep(0.7) 42 | , 43 | 44 | m_EffectiveDiffusionTime(0) 45 | 46 | { 47 | this->SetNumberOfRequiredInputs(2); 48 | } 49 | 50 | 51 | template 52 | void 53 | LinearAnisotropicDiffusionLBRImageFilter::SetInputImage(const ImageType * image) 54 | { 55 | this->SetNthInput(0, const_cast(image)); 56 | } 57 | 58 | 59 | template 60 | void 61 | LinearAnisotropicDiffusionLBRImageFilter::SetInputTensor(const TensorImageType * tensorImage) 62 | { 63 | this->SetNthInput(1, const_cast(tensorImage)); 64 | } 65 | 66 | 67 | template 68 | typename TImage::ConstPointer 69 | LinearAnisotropicDiffusionLBRImageFilter::GetInputImage() 70 | { 71 | return static_cast(this->ProcessObject::GetInput(0)); 72 | } 73 | 74 | 75 | template 76 | typename LinearAnisotropicDiffusionLBRImageFilter::TensorImageType::ConstPointer 77 | LinearAnisotropicDiffusionLBRImageFilter::GetInputTensor() 78 | { 79 | return static_cast(this->ProcessObject::GetInput(1)); 80 | } 81 | 82 | 83 | template 84 | void 85 | LinearAnisotropicDiffusionLBRImageFilter::GenerateData() 86 | { 87 | GenerateStencils(); 88 | this->UpdateProgress(0.5); 89 | 90 | this->ImageUpdateLoop(); 91 | } 92 | 93 | // **************************** Computation *********************** 94 | template 95 | struct LinearAnisotropicDiffusionLBRImageFilter::StencilFunctor 96 | { 97 | public: 98 | using SpacingType = typename TensorImageType::SpacingType; 99 | void 100 | Initialize(RegionType region_, SpacingType spacing) 101 | { 102 | region = region_; 103 | prod[0] = 1; 104 | for (ImageDimensionType i = 1; i < ImageDimension; ++i) 105 | { 106 | prod[i] = prod[i - 1] * region.GetSize()[i - 1]; 107 | } 108 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 109 | { 110 | invSpacing[i] = ScalarType(1) / spacing[i]; 111 | } 112 | } 113 | 114 | InternalSizeT 115 | BufferIndex(const IndexType & x) const 116 | { 117 | IndexValueType ans = 0; 118 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 119 | { 120 | ans += this->prod[i] * (x[i] - this->region.GetIndex()[i]); 121 | } 122 | return ans; 123 | } 124 | 125 | StencilType 126 | operator()(const TensorType & tensor, const IndexType & x) const 127 | { 128 | StencilType stencil; 129 | StencilOffsetsType offsets; 130 | 131 | // Diffusion tensors are homogeneous to the inverse of norms, and are thus rescaled with an inverse spacing. 132 | 133 | TensorType D; 134 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 135 | for (ImageDimensionType j = i; j < ImageDimension; ++j) 136 | D(i, j) = tensor(i, j) * this->invSpacing[i] * this->invSpacing[j]; 137 | this->Stencil(Dispatch(), D, offsets, stencil.second); 138 | 139 | InternalSizeT * yIndex = &stencil.first[0]; 140 | 141 | // Compute buffer offsets from geometrical offsets 142 | for (unsigned int i = 0; i < HalfStencilSize; ++i) 143 | { 144 | for (auto orientation = 0; orientation < 2; ++orientation, ++yIndex) 145 | { 146 | const IndexType y = orientation ? x - offsets[i] : x + offsets[i]; 147 | if (this->region.IsInside(y)) 148 | { 149 | *yIndex = this->BufferIndex(y); 150 | } 151 | else 152 | { 153 | // Neumann boundary conditions. 154 | *yIndex = this->OutsideBufferIndex(); 155 | } // if y 156 | } // for eps 157 | } // for i 158 | return stencil; 159 | } 160 | 161 | protected: 162 | struct DispatchBase 163 | {}; 164 | template 165 | struct Dispatch : public DispatchBase 166 | {}; 167 | 168 | static void 169 | Stencil(const Dispatch<2> &, 170 | const TensorType & D, 171 | StencilOffsetsType & offsets, 172 | StencilCoefficientsType & coefficients) 173 | { 174 | // Construct a superbase, and make it obtuse with Selling's algorithm 175 | VectorType sb[ImageDimension + 1]; // SuperBase 176 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 177 | { 178 | for (ImageDimensionType j = 0; j < ImageDimension; ++j) 179 | { 180 | sb[i][j] = (i == j); 181 | } 182 | } 183 | 184 | sb[ImageDimension] = -(sb[0] + sb[1]); 185 | constexpr int maxIter = 200; 186 | int iter = 0; 187 | for (; iter < maxIter; ++iter) 188 | { 189 | bool same = true; 190 | for (ImageDimensionType i = 1; i <= ImageDimension && same; ++i) 191 | { 192 | for (ImageDimensionType j = 0; j < i && same; ++j) 193 | { 194 | if (ScalarProduct(D, sb[i], sb[j]) > 0) 195 | { 196 | const VectorType u = sb[i], v = sb[j]; 197 | sb[0] = v - u; 198 | sb[1] = u; 199 | sb[2] = -v; 200 | same = false; 201 | } 202 | } 203 | } 204 | if (same) 205 | { 206 | break; 207 | } 208 | } 209 | if (iter == maxIter) 210 | { 211 | std::cerr << "Warning: Selling's algorithm not stabilized." << std::endl; 212 | } 213 | 214 | for (ImageDimensionType i = 0; i < 3; ++i) 215 | { 216 | coefficients[i] = (-0.5) * ScalarProduct(D, sb[(i + 1) % 3], sb[(i + 2) % 3]); 217 | assert(coefficients[i] >= 0); 218 | 219 | offsets[i][0] = static_cast(-sb[i][1]); 220 | offsets[i][1] = static_cast(sb[i][0]); 221 | } 222 | } 223 | static void 224 | Stencil(const Dispatch<3> &, 225 | const TensorType & D, 226 | StencilOffsetsType & offsets, 227 | StencilCoefficientsType & coefficients) 228 | { 229 | // Construct a superbase, and make it obtuse with Selling's algorithm 230 | VectorType sb[ImageDimension + 1]; 231 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 232 | { 233 | for (ImageDimensionType j = 0; j < ImageDimension; ++j) 234 | { 235 | sb[i][j] = (i == j); 236 | } 237 | } 238 | sb[ImageDimension] = -(sb[0] + sb[1] + sb[2]); 239 | 240 | constexpr int maxIter = 200; 241 | int iter = 0; 242 | for (; iter < maxIter; ++iter) 243 | { 244 | bool same = true; 245 | for (ImageDimensionType i = 1; i <= ImageDimension && same; ++i) 246 | for (ImageDimensionType j = 0; j < i && same; ++j) 247 | if (ScalarProduct(D, sb[i], sb[j]) > 0) 248 | { 249 | const VectorType u = sb[i], v = sb[j]; 250 | for (ImageDimensionType k = 0, l = 0; k <= ImageDimension; ++k) 251 | if (k != i && k != j) 252 | sb[l++] = sb[k] + u; 253 | sb[2] = -u; 254 | sb[3] = v; 255 | same = false; 256 | } 257 | if (same) 258 | break; 259 | } 260 | if (iter == maxIter) 261 | { 262 | std::cerr << "Warning: Selling's algorithm not stabilized." << std::endl; 263 | } 264 | 265 | // Computation of the weights 266 | SymmetricSecondRankTensor Weights; 267 | for (ImageDimensionType i = 1; i < ImageDimension + 1; ++i) 268 | { 269 | for (ImageDimensionType j = 0; j < i; ++j) 270 | { 271 | Weights(i, j) = (-0.5) * ScalarProduct(D, sb[i], sb[j]); 272 | } 273 | } 274 | 275 | // Now that the obtuse superbasis has been created, generate the stencil. 276 | // First get the dual basis. Obtained by computing the comatrix of Basis[1..ImageDimension]. 277 | 278 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 279 | { 280 | for (ImageDimensionType j = 0; j < ImageDimension; ++j) 281 | { 282 | offsets[i][j] = sb[(i + 1) % ImageDimension][(j + 1) % ImageDimension] * 283 | sb[(i + 2) % ImageDimension][(j + 2) % ImageDimension] - 284 | sb[(i + 2) % ImageDimension][(j + 1) % ImageDimension] * 285 | sb[(i + 1) % ImageDimension][(j + 2) % ImageDimension]; 286 | } 287 | } 288 | 289 | offsets[ImageDimension] = offsets[0] - offsets[1]; 290 | offsets[ImageDimension + 1] = offsets[0] - offsets[2]; 291 | offsets[ImageDimension + 2] = offsets[1] - offsets[2]; 292 | 293 | // The corresponding coefficients are given by the scalar products. 294 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 295 | { 296 | coefficients[i] = Weights(i, 3); 297 | } 298 | 299 | coefficients[ImageDimension] = Weights(0, 1); 300 | coefficients[ImageDimension + 1] = Weights(0, 2); 301 | coefficients[ImageDimension + 2] = Weights(1, 2); 302 | } 303 | 304 | RegionType region; 305 | IndexType prod; 306 | SpacingType invSpacing; 307 | InternalSizeT 308 | OutsideBufferIndex() const 309 | { 310 | return NumericTraits::max(); 311 | } 312 | }; 313 | 314 | 315 | template 316 | void 317 | LinearAnisotropicDiffusionLBRImageFilter::GenerateStencils() 318 | { 319 | // Stencil type is a pair type because itk::UnaryFunctorImage filter 320 | // only produces one output 321 | // using SpacingType = typename TensorImageType::SpacingType; 322 | const RegionType region = GetRequestedRegion(); 323 | 324 | using FunctorFilterType = UnaryFunctorWithIndexImageFilter; 325 | typename FunctorFilterType::Pointer filter = FunctorFilterType::New(); 326 | filter->SetInput(GetInputTensor()); 327 | filter->GetFunctor().Initialize(region, GetInputTensor()->GetSpacing()); 328 | filter->Update(); 329 | m_StencilImage = filter->GetOutput(); 330 | 331 | 332 | // setup diagonal coefficients. Cannot be parallelized due to non-local modifications of diagBuffer. 333 | 334 | m_DiagonalCoefficients = ScalarImageType::New(); 335 | m_DiagonalCoefficients->CopyInformation(GetInputTensor()); 336 | m_DiagonalCoefficients->SetRegions(GetRequestedRegion()); 337 | m_DiagonalCoefficients->Allocate(); 338 | m_DiagonalCoefficients->FillBuffer(ScalarType(0)); 339 | 340 | ImageRegionConstIterator stencilIt(m_StencilImage, region); 341 | ImageRegionIterator diagIt(m_DiagonalCoefficients, region); 342 | ScalarType * diagBuffer = m_DiagonalCoefficients->GetBufferPointer(); 343 | 344 | for (stencilIt.GoToBegin(), diagIt.GoToBegin(); !stencilIt.IsAtEnd(); ++stencilIt, ++diagIt) 345 | { 346 | for (unsigned int i = 0; i < StencilSize; ++i) 347 | { 348 | const InternalSizeT yIndex = stencilIt.Value().first[i]; 349 | if (yIndex != OutsideBufferIndex()) 350 | { 351 | const ScalarType coefficient = stencilIt.Value().second[i / 2]; 352 | diagIt.Value() += coefficient; 353 | diagBuffer[yIndex] += coefficient; 354 | } // if y 355 | } // for i 356 | } // for stencilIt, diagIt 357 | } 358 | 359 | 360 | template 361 | typename LinearAnisotropicDiffusionLBRImageFilter::ScalarType 362 | LinearAnisotropicDiffusionLBRImageFilter::MaxStableTimeStep() 363 | { 364 | using MaxCalculatorType = MinimumMaximumImageCalculator; 365 | typename MaxCalculatorType::Pointer maximumCalculator = MaxCalculatorType::New(); 366 | maximumCalculator->SetImage(m_DiagonalCoefficients); 367 | maximumCalculator->SetRegion(GetRequestedRegion()); 368 | maximumCalculator->ComputeMaximum(); 369 | return 1. / maximumCalculator->GetMaximum(); 370 | } 371 | 372 | 373 | template 374 | void 375 | LinearAnisotropicDiffusionLBRImageFilter::SetMaxDiffusionTime(ScalarType time) 376 | { 377 | if (time < 0) 378 | { 379 | itkExceptionMacro("diffusion time must be finite and positive"); 380 | } 381 | m_DiffusionTime = time; 382 | } 383 | 384 | 385 | template 386 | void 387 | LinearAnisotropicDiffusionLBRImageFilter::SetRatioToMaxStableTimeStep(ScalarType ratio) 388 | { 389 | if (ratio <= 0 || ratio > 1) 390 | { 391 | itkExceptionMacro("Ratio to max time step " << ratio << "should be within ]0,1]"); 392 | } 393 | m_RatioToMaxStableTimeStep = ratio; 394 | } 395 | 396 | 397 | template 398 | void 399 | LinearAnisotropicDiffusionLBRImageFilter::SetMaxNumberOfTimeSteps(int n) 400 | { 401 | if (n <= 0) 402 | { 403 | itkExceptionMacro("Max number of time steps must be positive"); 404 | } 405 | m_MaxNumberOfTimeSteps = n; 406 | } 407 | 408 | 409 | template 410 | void 411 | LinearAnisotropicDiffusionLBRImageFilter::ImageUpdateLoop() 412 | { 413 | ScalarType delta = MaxStableTimeStep() * m_RatioToMaxStableTimeStep; 414 | int n = ceil(m_DiffusionTime / delta); 415 | if (n > m_MaxNumberOfTimeSteps) 416 | { 417 | n = m_MaxNumberOfTimeSteps; 418 | m_EffectiveDiffusionTime = n * delta; 419 | } 420 | else 421 | { 422 | delta = m_DiffusionTime / n; 423 | m_EffectiveDiffusionTime = m_DiffusionTime; 424 | } 425 | m_EffectiveNumberOfTimeSteps = n; 426 | 427 | // Extraction of the region of interest is required for image buffer access. 428 | using InputCasterType = ExtractImageFilter; 429 | typename InputCasterType::Pointer inputCaster = InputCasterType::New(); 430 | inputCaster->SetInput(GetInputImage()); 431 | inputCaster->SetExtractionRegion(GetRequestedRegion()); 432 | inputCaster->SetDirectionCollapseToIdentity(); 433 | inputCaster->Update(); 434 | m_PreviousImage = inputCaster->GetOutput(); 435 | 436 | m_NextImage = ImageType::New(); 437 | m_NextImage->CopyInformation(m_PreviousImage); 438 | m_NextImage->SetRegions(m_PreviousImage->GetBufferedRegion()); 439 | m_NextImage->Allocate(); 440 | 441 | for (auto k = 0; k < n; ++k) 442 | { 443 | ImageUpdate(delta); 444 | std::swap(m_PreviousImage, m_NextImage); 445 | this->UpdateProgress(0.5 + 0.5 * k / float(n)); 446 | } 447 | this->GraftOutput(m_PreviousImage); 448 | } 449 | 450 | 451 | template 452 | struct LinearAnisotropicDiffusionLBRImageFilter::FunctorType 453 | { 454 | ScalarType delta; 455 | PixelType 456 | operator()(PixelType output, PixelType input, ScalarType diag) 457 | { 458 | return output * this->delta + input * (ScalarType(1) - this->delta * diag); 459 | } 460 | }; 461 | 462 | 463 | template 464 | void 465 | LinearAnisotropicDiffusionLBRImageFilter::ImageUpdate(ScalarType delta) 466 | { 467 | // Setting up iterators 468 | ImageRegion region = GetRequestedRegion(); 469 | 470 | ImageRegionConstIterator inputIt(m_PreviousImage, region); 471 | ImageRegionIterator outputIt(m_NextImage, region); 472 | 473 | const PixelType * inputBuffer = m_PreviousImage->GetBufferPointer(); 474 | PixelType * outputBuffer = m_NextImage->GetBufferPointer(); 475 | 476 | ImageRegionConstIterator diagIt(m_DiagonalCoefficients, region); 477 | ImageRegionConstIterator stencilIt(m_StencilImage, region); 478 | 479 | // Rest of function is a hand-made (sparse matrix)*vector product. 480 | m_NextImage->FillBuffer({}); 481 | 482 | // Taking care of Off-Diagonal matrix elements. Cannot be parallelized due to non-local modifications of outputBuffer 483 | for (inputIt.GoToBegin(), outputIt.GoToBegin(), stencilIt.GoToBegin(); !inputIt.IsAtEnd(); 484 | ++inputIt, ++outputIt, ++stencilIt) 485 | { 486 | for (unsigned int i = 0; i < StencilSize; ++i) 487 | { 488 | const InternalSizeT yIndex = stencilIt.Value().first[i]; 489 | if (yIndex != OutsideBufferIndex()) 490 | { 491 | const ScalarType coefficient = stencilIt.Value().second[i / 2]; 492 | outputIt.Value() += coefficient * (inputBuffer[yIndex]); 493 | outputBuffer[yIndex] += coefficient * inputIt.Value(); 494 | } 495 | } 496 | } 497 | 498 | using ImageFunctorType = TernaryFunctorImageFilter; 499 | typename ImageFunctorType::Pointer imageFunctor = ImageFunctorType::New(); 500 | imageFunctor->SetInput1(m_NextImage); 501 | imageFunctor->SetInput2(m_PreviousImage); 502 | imageFunctor->SetInput3(m_DiagonalCoefficients); 503 | imageFunctor->GetFunctor().delta = delta; 504 | 505 | assert(imageFunctor->CanRunInPlace()); 506 | imageFunctor->InPlaceOn(); 507 | imageFunctor->Update(); 508 | m_NextImage = imageFunctor->GetOutput(); 509 | 510 | /* 511 | // Old Serial version for diagonal elements 512 | for(inputIt.GoToBegin(), outputIt.GoToBegin(), diagIt.GoToBegin(); 513 | !inputIt.IsAtEnd(); 514 | ++inputIt, ++outputIt, ++diagIt) 515 | outputIt.Value() = delta*outputIt.Value() + (1-delta*diagIt.Value())*inputIt.Value(); 516 | */ 517 | } 518 | 519 | // **************************** subclass SSRT_Traits call method ************************** 520 | 521 | template 522 | TScalar 523 | LinearAnisotropicDiffusionLBRImageFilter::ScalarProduct(const TensorType & m, 524 | const VectorType & u, 525 | const VectorType & v) 526 | { 527 | ScalarType result(0); 528 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 529 | { 530 | result += m(i, i) * u[i] * v[i]; 531 | } 532 | for (ImageDimensionType i = 0; i < ImageDimension; ++i) 533 | { 534 | for (ImageDimensionType j = i + 1; j < ImageDimension; ++j) 535 | { 536 | result += m(i, j) * (u[i] * v[j] + u[j] * v[i]); 537 | } 538 | } 539 | return result; 540 | } 541 | 542 | } // end namespace itk 543 | 544 | #endif 545 | --------------------------------------------------------------------------------