Repository navigation
ENH: Apply the ITK history of CannyEdgeDetectionImageFilter to its recursive Gaussian copy - #64
hjmjohnson wants to merge 13 commits into
Conversation
fbf5f63 to
00a0f60
Compare
|
@dzenanz I was trying to preserve maximal attribution and credit for all the changes. But I now see that is not all that useful, and I will squash this into a few commits. These files are copy-and-paste files with algorithm components (smoothing components swapped) from 2009, so conceptually it's really not that much of a change, but it occurred over 17 years. |
00a0f60 to
2de78c2
Compare
…n. Removed private InBounds met Applies ITK commit 0a20088c62a (2009-04-08) to the copy of itk::CannyEdgeDetectionImageFilter.
… this, Traits have been in Applies ITK commit 7ab73279a11 (2009-07-12) to the copy of itk::CannyEdgeDetectionImageFilter.
…t results when called multiple Applies ITK commit c20831fdc0f (2009-08-17) to the copy of itk::CannyEdgeDetectionImageFilter.
Applies ITK commit be14aef1ec8 (2016-12-02) to the copy of itk::CannyEdgeDetectionImageFilter.
2de78c2 to
7825a8a
Compare
Applies ITK commit acf4da8b05f (2015-05-19) to the copy of itk::CannyEdgeDetectionImageFilter, together with the private output pointer from ITK commit 563210160bf (2012-07-20) that it builds on. GenerateData grafts the input and output into private images, so the sub-filters and the internal passes no longer modify the pipeline's own data objects, and the filter can be updated again on a new input.
7825a8a to
5953ace
Compare
Remove the legacy Threshold and OutsideValue parameters and the unreachable code in GenerateInputRequestedRegion, make the getters const, use SizeValueType for strides and offsets, replace floating-point equality checks, and drop the ITK_USE_CONCEPT_CHECKING guard, as ITK did. The feature generators and the example no longer call the removed methods. Applies these ITK commits to the copy of itk::CannyEdgeDetectionImageFilter: fede4ba39c4 (2009-04-25) Changed itkGetMacro to itkGetConstMacro. Now these Get methods can be used in other const metho e6a2204367c (2010-07-31) Removed all legacy code. 31f5b60c8e7 (2010-12-31) Add abstract integer types for use with image size c5e2f63b6f6 (2011-07-20) removed Threshold and OutsideVaule as parameters in Canny b6b6b1274d7 (2011-11-05) Removed unreachable code. 9ea5521e0fd (2015-08-14) Removes floating-point equality checks 9e582fd86c0 (2025-01-23) Remove the `ITK_USE_CONCEPT_CHECKING` option Co-Authored-By: Alexander Leinoff <alexander-leinoff@uiowa.edu> Co-Authored-By: Bill Lorensen <bill.lorensen@gmail.com> Co-Authored-By: Bradley Lowekamp <blowekamp@mail.nih.gov> Co-Authored-By: David Cole <david.cole@kitware.com> Co-Authored-By: Shelly Belsky <shelly.belsky@kitware.com>
Replace the SetSingleMethod callbacks, the thread-data struct, and the per-pixel ProgressReporter with MultiThreaderBase, ParallelizeImageRegion, NumberOfWorkUnits, and ProgressTransformer, as ITK did. Applies these ITK commits to the copy of itk::CannyEdgeDetectionImageFilter: 7acd5a7fc14 (2011-05-16) Changed number of threads type. c7014107365 (2011-06-11) ThreadIdType changes. a24d6204e0d (2018-01-11) Use MultiThreaderBase e086233e890 (2018-04-13) simplifying Canny filter by using new threading infrastructure 011266e5ea8 (2018-05-29) rename NumberOfThreads into NumberOfWorkUnits in filters ce15429a230 (2018-05-29) renaming NumberOfThreads into NumberOfWorkUnits in MultiThreaderBase dd0b0d128d6 (2018-08-29) using progress transformer in recently refactored classes Co-Authored-By: Bill Lorensen <bill.lorensen@gmail.com> Co-Authored-By: David Doria <daviddoria@gmail.com> Co-Authored-By: Dženan Zukić <dzenan.zukic@kitware.com>
Pass the multiply filter output to FollowEdge instead of fetching it per voxel, and cache the ComputeIndex() result of the hysteresis iterators, as ITK did. Applies these ITK commits to the copy of itk::CannyEdgeDetectionImageFilter: 563210160bf (2012-07-20) Pull expensive GetInput/GetOutput calls out of inner loops cc5b30193fa (2026-02-11) Replace GetIndex() calls on iterators with ComputeIndex() d086c8cf966 (2026-02-15) Locally cache ComputeIndex() result in filters Co-Authored-By: Kent Williams <norman-k-williams@uiowa.edu> Co-Authored-By: Niels Dekker <N.Dekker@lumc.nl>
Use override, nullptr, type aliases, auto, constexpr, defaulted and deleted
special members, in-class member initializers, range-based for loops,
trailing return types, strongly typed enums, and value initialization in
place of NumericTraits<T>::ZeroValue(), as ITK did. The iterator template
arguments stay spelled out because ITK 5.4 has no deduction guides for them.
Applies these ITK commits to the copy of
itk::CannyEdgeDetectionImageFilter:
90b8fb21cf1 (2011-09-26) fix warnings about unused variable and copy constructors
1b825a85101 (2014-01-16) Remove explicit typed exception specifications
0d61aa0798e (2014-05-07) Add ITK_NULLPTR supporting c++11 checks
3f712cd0955 (2014-10-31) Deprecate ::Zero and ::One
671d04333f5 (2014-12-13) Add c++11 override designation
b6b9a8e8849 (2015-10-29) C++11 allows for rigorous =delete
4a3257e293a (2016-06-10) Provide a well documented macro for repeated code
576893dba8d (2017-10-13) Added ITK_OVERRIDE to hundreds of destructor declarations.
520a2493068 (2017-12-16) Preparing for ITKv5 by adding override
3ceacc0ad4e (2017-12-16) Use C++11 override directly
188a1632079 (2017-12-16) Use C++11 ITK_NULLPTR directly
66e5d6b3bcc (2018-01-27) Prefer C++11 type alias over typedef
5c14741e1e0 (2018-02-02) Replace itkStaticConstMacro with static constexpr
de713e7ac52 (2018-02-05) Use auto for variable creation
d72d28f5a1f (2018-03-04) Do not use itkGetStaticConstMacro in ITK
b0f183b04fd (2018-03-25) Moved ITK_DISALLOW_COPY_AND_ASSIGN calls to public section
5142ed7bb5e (2018-09-23) Pefer = default to explicitly trivial implementations
0dd31a62f85 (2020-02-12) Consistently use strongly typed enums
4c0982ddec4 (2020-10-11) Call ITK_DISALLOW_COPY_AND_MOVE, not ITK_DISALLOW_COPY_AND_ASSIGN
828453d1bf6 (2021-10-19) Use `auto` for declaration of variables initialized by `New()`
2fa80e8bd32 (2022-03-30) Avoid old, C-style cast
2de7e24b0b5 (2022-12-01) Use range-based `for` loops to iterate over `faceList` variables
18a672368d7 (2022-12-02) Initialize `faceList` directly by ImageBoundaryFacesCalculator
5e2c49f9eb8 (2023-01-18) Add in-class `{}` member initializers to objects created by New()
8f233d177ab (2023-03-04) Add in-class `{}` member initializers having trailing comments
5a60ed00adb (2023-03-07) Remove unnecessary iterator `GoToBegin()` calls from Filtering
42b0bfc0771 (2023-03-09) Replace `T var = NumericTraits<T>::ZeroValue()` with `T var{}`
f7fa2693f61 (2023-04-26) Use trailing return types in multi-line function declarations
2c264ea2ef6 (2023-12-19) Replace itkTypeMacro calls with `itkOverrideGetNameOfClassMacro`
13ccff6af85 (2024-02-13) Replace `NumericTraits<T>::ZeroValue()` with `T{}`
7fd1e7341bd (2024-10-27) Replace `Size var; var.Fill` with `auto var = Size::Filled`
6cb6bf98234 (2024-10-31) Replace `T var; var.Fill(x)` with `auto var = MakeFilled<T>(x)`
fd144827976 (2024-11-04) Add constexpr to variables initialized by MakeFilled
c77f9f8da0d (2024-11-09) Add constexpr to variables initialized by T::Filled(int-literal)
677b76192a6 (2024-11-19) Replace T::Pointer with auto when initializing variables by New()
375ee8c47a0 (2024-11-27) Prefer explicit const designation
e1acaf5aa8d (2024-12-15) Variable can be made constexpr
1649dee8e42 (2025-04-17) Replace TInputImage::SizeValueType with itk::SizeValueType
caa61aa3cdf (2025-06-10) Assign initial values to variables
f46b4c93fec (2025-06-13) Prefer member initializer to assignment
50210af9d3b (2026-03-12) Use CTAD for iterators "with index", in hxx files
b6a38b188ca (2026-03-26) Use CTAD for `ImageRegionIterator` variables in hxx files
dbab3e0faba (2026-03-29) Use CTAD when assigning an Image Iterator, in hxx files
4394f5d0eab (2026-04-03) Move region iterator declarations into `for` loops over faces
Co-Authored-By: Bradley Lowekamp <blowekamp@mail.nih.gov>
Co-Authored-By: Gaëtan Lehmann <gaetan.lehmann@jouy.inra.fr>
Co-Authored-By: Lee Newberg <lee.newberg@kitware.com>
Co-Authored-By: Michka Popoff <michkapopoff@gmail.com>
Co-Authored-By: Niels Dekker <N.Dekker@lumc.nl>
Co-Authored-By: Sean McBride <sean@rogue-research.com>
…anny filter copy Remove trailing whitespace, rename module and template parameter names, update header guards, use typename, use the PrintSelf object macros and indentation, prefer prefix increments, and tidy includes, as ITK did. Applies these ITK commits to the copy of itk::CannyEdgeDetectionImageFilter: dc3ced8177a (2010-08-24) Uncrustify code to remove end of line spacing. dccd0de7b07 (2010-09-25) Remove redundant header includes. a1cb6f7621c (2011-03-18) let BinaryFunctorImageFilter take a constant as input 13f99f81510 (2011-07-15) Change for development tool compliance 540228ef42f (2011-07-15) Converted module names to use camel case f8e626290a8 (2011-08-13) Remove Win32 duplicate warning disable d1918d28f06 (2012-11-08) Update space before access specification a4603f252da (2013-08-06) Remove superfluous return statements at the end of void functions 86bbb9d092d (2013-09-14) Use "typename" for template parameters 26354b32667 (2013-12-13) Rename TValueType template parameters to TValue c57adf26874 (2014-06-16) Add itkPrintSelfObjectMacro to improve indenting dd673a7a7b3 (2014-12-15) Remove double underscore in header guards (.h & __itk*_h form) 422aa2da236 (2014-12-15) Remove double underscore in header guards (.hxx & __itk*_h form) 971aef211ca (2021-04-21) Replace postfix by prefix increment in `for` loops in ITK/Modules f84720ee082 (2021-12-13) Remove recursive inclusion of .h files ec510c17494 (2022-07-11) Replace postfix with prefix increment, decrement (`++`, `--`) 48c6ed76973 (2022-12-22) Add missing `<< indent` insertions to `PrintSelf` implementations Co-Authored-By: Bradley Lowekamp <blowekamp@mail.nih.gov> Co-Authored-By: Brian Helba <brian.helba@kitware.com> Co-Authored-By: Gaëtan Lehmann <gaetan.lehmann@jouy.inra.fr> Co-Authored-By: Niels Dekker <N.Dekker@lumc.nl> Co-Authored-By: Sean McBride <sean@rogue-research.com>
…with ITK Update the license header, Doxygen briefs and groups, spelling, https links, the copyright holder, in-code citations (now bibtex), and the migration notes, as ITK did. Applies these ITK commits to the copy of itk::CannyEdgeDetectionImageFilter: b738d019148 (2010-10-25) Headers changed after running Hans script. df54d1bb59e (2010-08-05) Python,Tcl,&Java examples used outdated methods dc85ead03f7 (2011-04-15) add module name in the doxygen groups 3397115b3b8 (2011-06-13) Code review of EdgeDetectionReview d7d38eede7f (2012-03-11) Spelling erros in comments 17030da3289 (2013-10-16) Prevent concept checking comments from being parsed by Doxygen e6908a9bfd9 (2014-02-21) Make comment the Doxygen brief aacfcc330b4 (2014-06-26) Add break in brief description of Canny edge filter 621f43c7223 (2017-01-17) Improve the itkCannyEdgeDetectionImageFilter style. 344e9bc58e6 (2020-02-07) Update copyright assignment to NumFOCUS 9cd0f2053f0 (2022-05-16) Use https instead of http when https works eede9c9f790 (2022-08-02) fix typos in Modules/Filtering 1608256c5a4 (2022-08-26) Remove outdated migration information 87a66198e11 (2022-09-25) Make Doxygen class command indent consistent 2513f8f62e0 (2023-03-08) Fixed spelling bugs in Modules/Filtering headers ed7ad92dd64 (2023-03-27) Spellcheck Filtering HXX and Video headers 4d750726d06 (2024-03-01) Update documentation of `GetNameOfClass` macro calls 097e2dc8788 (2025-01-23) Convert from raw in-code citations to bibtex (1/N) Co-Authored-By: Bill Lorensen <bill.lorensen@gmail.com> Co-Authored-By: Bradley Lowekamp <blowekamp@mail.nih.gov> Co-Authored-By: Brian Helba <brian.helba@kitware.com> Co-Authored-By: Dave Chen <dchen@mail.nih.gov> Co-Authored-By: Gaëtan Lehmann <gaetan.lehmann@jouy.inra.fr> Co-Authored-By: Jon Haitz Legarreta <jhlegarreta@vicomtech.org> Co-Authored-By: Jon Haitz Legarreta Gorroño <jon.haitz.legarreta@gmail.com> Co-Authored-By: Lee Newberg <lee.newberg@kitware.com> Co-Authored-By: Luis Ibanez <luis.ibanez@kitware.com> Co-Authored-By: Matt McCormick <matt.mccormick@kitware.com> Co-Authored-By: Niels Dekker <N.Dekker@lumc.nl> Co-Authored-By: Peter Lamb <Peter.Lamb@ge.com> Co-Authored-By: Ziv Yaniv <zivyaniv@nih.gov> Co-Authored-By: luz paz <luzpaz@pm.me>
The filter had no test of its own, only indirect use through the feature generators, so output values and re-execution were unchecked. Add eight tests that mirror ITK's CannyEdgeDetectionImageFilter tests: a baseline comparison and an A/B comparison after changing and restoring the thresholds on cthead1.png, the same A/B check on the 3D lesion volume, and tests of region start index, successive inputs of different sizes, parameters, hysteresis linking, error handling, and a downstream pipeline with the release-data flag on. The baseline was generated by the filter before the ITK history was applied.
df5b534 to
7396329
Compare
Remove the unused m_MaximumError member, give m_Sigma an in-class initializer, make GetNonMaximumSuppressionImage non-const, and place the \ingroup tags as ITK does, so the remaining differences from itk::CannyEdgeDetectionImageFilter are the smoothing parameters only. GaussianImageFilterType is protected as in ITK. ScalarRealType and SigmaArrayType stay public because the Sigma accessors use them. PrintSelf uses print_helper::PrintNumericTrait and the iterators use class template argument deduction when the ITK being built against has them. ITK_VERSION_MAJOR cannot tell, because v6.0b02 and current main both report 6.0.0, so CMake compares ITK_VERSION_TWEAK (the commit date) with the dates those features landed and defines LST_ITK_HAS_PRINT_NUMERIC_TRAIT and LST_ITK_HAS_ITERATOR_CTAD. Without the macros the code keeps the NumericTraits and explicit ImageRegionIterator<TOutputImage> forms, which build everywhere.
7396329 to
31a2d1c
Compare
|
@dzenanz The main goal of this was a learning experience for me trying some new approaches out. The goal was to minimize the difference between the local LSTK itkCannyEdgeDetectionRecursiveGaussianImageFilter.h and the original copied itk itkCannyEdgeDetectionImageFilter.h. The ITK itkCannyEdgeDetectionImageFilter.h has over 100 code updates, bug fixes, and style improvements; I was trying to preserve attribution for those who contributed to the fixes. In the end, many of the removals of code are provably no-ops that are copy-and-paste cruft, or building out of tests from ITK/CannyEdgeDetectionImageFilter to add coverage for the LSTK/CannyEdgeDetectionRecursiveGaussianImageFilter. |
CannyEdgeDetectionRecursiveGaussianImageFilteris a 2009 copy ofitk::CannyEdgeDetectionImageFilterthat never received ITK's later changes. This PR applies them: 5 bug-fix commits that keep their original ITK authors, then 7 themed commits (API, threading, performance, modernization, style, docs, tests) that credit the original authors withCo-Authored-By, and a final commit that trims the remaining differences. The two files now differ only in the smoothing filter. Part of #63.For reviewers:
Update()calls are correct, and a downstream consumer works when the release-data flag is on (the internal pipeline is isolated from the external one). Output is bit-identical to the current code on 2,100 random 2D and 3D images when none of those cases apply.SetThreshold/GetThresholdandSetOutsideValue/GetOutsideValue, as ITK did in 2011.OutsideValuewas stored but never used. The callers in this repository (three feature generators and one example) are updated. A search of the ITK forest testbed repositories and Slicer found no other users of the class.mainof the two filter files (red is this repository, green isitk::CannyEdgeDetectionImageFilterat ITK50c5b33430ewith the class name replaced and this repository's.clang-formatapplied). The branchitk-main-reference-cannyexists only for this comparison and is not part of the PR.git bisectworks. Any merge method is fine; "Squash and merge" would drop the per-commit authorship.Test results
5d31af3cfdf)v6.0b02(the ITK 6 of the Python wheel jobs)ITK_VERSION_MAJOR, failed to compile thereitkLandmarksReaderTest1, which never frees the list returned byGetChildren()and fails the same way onmain(28f2cf5)31a2d1c: 27 checks pass, 2 skipped (the notebook jobs, also skipped onmain), 0 failed; the two ARM ITK 5.4.7 Python jobs hit a runner DNS error (Could not resolve host: github.com) and passed on rerunThe 500-run ASan loop and the valgrind runs were made on the tree at commit 12; everything else in the table was rerun on the head. Commit 13 changes no behavior (an unused member removed, one getter made non-const,
GaussianImageFilterTypemade protected as in ITK, comments and tags, and the feature-guardedPrintSelfand iterator declarations), and the 2,100-case harness hashes are identical tomainat the head on ITK 6 and ITK 5.4.7 on macOS and on ITK 6 on Linux, and the full suite was rerun on it with ITK 6 and ITK 5.4.7, which compile the two branches of each version guard respectively.Two feature-generator tests (
itkCannyEdgesDistanceFeatureGeneratorTest1anditkCannyEdgesDistanceAdvectionFieldFeatureGeneratorTest1) report a UBSan runtime error when it is made fatal (halt_on_error=1). The report is inside ITK'sBinaryContourImageFilter(ScanlineFilterCommon::SetupLineOffsets, a null-pointer offset inConstNeighborhoodIterator), not in this filter. Both tests pass otherwise.The eight new
itkCannyEdgeDetectionRecursiveGaussianImageFilterTest*tests mirror ITK'sitkCannyEdgeDetectionImageFilterTestandTest2: a baseline comparison and an A/B re-execution check oncthead1.png(new 197 KB fixture), the same A/B check on the 3D lesion volume, and tests of region start index, successive inputs of different sizes, parameters, hysteresis linking, error handling, and a downstream pipeline with the release-data flag on (Test8, which throws a buffered-region exception onmainand passes here). The baseline PNG was generated by the filter before the ITK history was applied.Equivalence with the current code
A harness runs the filter on 2,100 random images: 300 2D images (4 to 73 pixels per side) and 120 3D images (4 to 31 pixels per side), each at 1, 2, 3, 7, and 16 work units, with random sigma and thresholds. 620 of the cases contain edges. The FNV-1a hash of the output buffer is identical between the code on
mainand this branch:5d31af3cfdf), 2,100 of 2,100 cases;The output does not depend on the number of work units: 420 image and seed groups, 0 mismatches.
The harness excludes the four situations this PR fixes (non-zero start index, repeated
Update(), successive inputs, pipelines with the release-data flag), where the old code was wrong. Those are covered byTest2,Test5,Test7, andTest8, which fail onmainand pass here.The commits
BUGnon-zero start index (InBoundsreplaced byIsInside), ITK bug 8729BUGinteger types on 64-bit Windows, ITK bug 9260BUGcorrect results when called multiple times (stale edge-linking node), ITK bug 9431BUGITK_TEMPLATE_EXPORTonListNodeBUGisolate the internal filter from the external pipelineENHAPI cleanups: remove legacy code andThreshold/OutsideValue, const getters, size typesENHITK 5 threading:MultiThreaderBase,ParallelizeImageRegion, work units,ProgressTransformerPERFhoist accessor calls out of inner loopsCOMPC++ modernizationSTYLEformatting, naming, and include conventionsDOCcomments, license header, and documentationENHthe eight new tests, fixture, and baselineENHminimize the remaining differences fromitk::CannyEdgeDetectionImageFilterCommits 1 to 5 are the bug fixes. Their dependencies are small, so they are applied first: commit 4 adds only the
ListNodehalf of ITK's 2016 fix (the filter class already had the keyword), and commit 5 includes the private output pointer from ITK's 2012 performance commit that the isolation fix builds on.Commits 6 to 11 each list the ITK commits they contain (hash, date, and subject) in the message, and carry a
Co-Authored-Bytrailer for every original ITK author. Together with the bug-fix commits, 101 ITK commits are listed.Two ITK changes are handled differently, on purpose:
PrintNumericTraitadoption (2026-05-08) is applied only when the ITK being built against has it.ITK_VERSION_MAJORcannot tell, becausev6.0b02and currentmainboth report 6.0.0, soCMakeLists.txtcomparesITK_VERSION_TWEAK(ITK's commit date) with the date the feature landed and definesLST_ITK_HAS_PRINT_NUMERIC_TRAIT. Without the macro, including for downstream code that does not use this CMake file,PrintSelfkeeps thestatic_castform (commit 13).v6.0b02, so commit 9 spells outImageRegionIterator<TOutputImage>at four sites, and commit 13 restores ITK'sImageRegionIteratorspelling behindLST_ITK_HAS_ITERATOR_CTAD, defined the same way fromITK_VERSION_TWEAK.How the history was produced
The first LST version equals ITK's revisions from late 2008 once the class name is normalized: the header from
bd48a2188a8and the implementation from5f06f26925b, with 0 differing lines. For each of the 125 later ITK commits that touch the header or implementation, the before and after versions were renamed, formatted with this repository's.clang-format, and merged into the LST file with a three-way merge, replaying ITK's history in order. 22 commits produced no change because LST had already made the same edit. Conflicts were resolved by keeping LST's smoothing code (Sigma,SmoothingRecursiveGaussianImageFilter) and taking ITK's change elsewhere, with a brace-depth check after every commit.The five bug fixes were then re-created as the first five commits. Commits 6 to 11 group the remaining ITK commits by theme: the intermediate trees are built by replaying each cumulative set of groups from commit 5 (resolving conflicts toward the commit's side), then repairing the few spots that did not compile. The tree at commit 12 equals the full, in-order replay byte for byte; commit 13 then removes the last avoidable differences. Every intermediate commit was built and tested as described above.
Distance to ITK's current files, after normalizing the class name and formatting (differing lines):
itkCannyEdgeDetectionRecursiveGaussianImageFilter.hitkCannyEdgeDetectionRecursiveGaussianImageFilter.hxxWhat remains, including the lines of the feature guards, is the
SigmaversusVarianceandMaximumErrorsmoothing parameters, theSigmaaccessors, and doc comments. To compare:Tooling and AI assistance
The replay was scripted and the conflicts were resolved with assistance from Claude Code (Anthropic). The author reviewed the results and is responsible for them. The scripts, harness, and result files are kept by the author and can be shared on request.