Skip to content

ENH: Apply the ITK history of CannyEdgeDetectionImageFilter to its recursive Gaussian copy - #64

Open
hjmjohnson wants to merge 13 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:canny-sync-itk-history
Open

hjmjohnson wants to merge 13 commits into
InsightSoftwareConsortium:mainfrom
hjmjohnson:canny-sync-itk-history

Conversation

@hjmjohnson

@hjmjohnson hjmjohnson commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

CannyEdgeDetectionRecursiveGaussianImageFilter is a 2009 copy of itk::CannyEdgeDetectionImageFilter that 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 with Co-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:

  • Fixes: edges no longer depend on the start index of the image region (ITK bug 8729), repeated and successive 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.
  • API removed: SetThreshold/GetThreshold and SetOutsideValue/GetOutsideValue, as ITK did in 2011. OutsideValue was 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.
  • Compare with ITK: side-by-side diff against ITK main of the two filter files (red is this repository, green is itk::CannyEdgeDetectionImageFilter at ITK 50c5b33430e with the class name replaced and this repository's .clang-format applied). The branch itk-main-reference-canny exists only for this comparison and is not part of the PR.
  • Every commit builds and passes the full test suite on ITK 6 and ITK 5.4.7, so git bisect works. Any merge method is fine; "Squash and merge" would drop the per-commit authorship.
Test results
Environment Result
macOS arm64, ITK 6 (5d31af3cfdf) all 13 commits build; 43/43 tests pass at commits 1 to 11, 51/51 at commits 12 and 13 (the new tests arrive in commit 12); rebuilt from scratch at the head, 51/51
macOS arm64, ITK 5.4.7 (release-5.4 build) the same: all 13 commits build; 43/43 and then 51/51; rebuilt from scratch at the head, 51/51
Linux x86_64, clang 18, ITK 6, no sanitizer at the head: Canny tests 1, 5, 6, 7, and 8 pass
Linux x86_64, clang 18, ITK v6.0b02 (the ITK 6 of the Python wheel jobs) full suite 51/51 at the head; the previous head, which guarded on ITK_VERSION_MAJOR, failed to compile there
Linux x86_64, clang 18, ITK 6, ASan and UBSan at the head: full suite 50/51, Canny tests 1 to 8 pass; the one failure is itkLandmarksReaderTest1, which never frees the list returned by GetChildren() and fails the same way on main (28f2cf5)
Linux x86_64, clang 18, ITK 6, ThreadSanitizer at the head: tests 1, 5, 6, 7, and 8 and the 2,100-case harness: 0 warnings
Linux x86_64, valgrind memcheck tests 1, 5, 6, 7, and 8: 0 errors, 0 definite or indirect leaks
GitHub Actions (ITK 5.4.7 and ITK 6 on Windows/macOS/Linux, plus Python wheels) at the head 31a2d1c: 27 checks pass, 2 skipped (the notebook jobs, also skipped on main), 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 rerun

The 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, GaussianImageFilterType made protected as in ITK, comments and tags, and the feature-guarded PrintSelf and iterator declarations), and the 2,100-case harness hashes are identical to main at 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 (itkCannyEdgesDistanceFeatureGeneratorTest1 and itkCannyEdgesDistanceAdvectionFieldFeatureGeneratorTest1) report a UBSan runtime error when it is made fatal (halt_on_error=1). The report is inside ITK's BinaryContourImageFilter (ScanlineFilterCommon::SetupLineOffsets, a null-pointer offset in ConstNeighborhoodIterator), not in this filter. Both tests pass otherwise.

The eight new itkCannyEdgeDetectionRecursiveGaussianImageFilterTest* tests mirror ITK's itkCannyEdgeDetectionImageFilterTest and Test2: a baseline comparison and an A/B re-execution check on cthead1.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 on main and 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 main and this branch:

  • on ITK 6 (5d31af3cfdf), 2,100 of 2,100 cases;
  • on ITK 5.4.7, 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 by Test2, Test5, Test7, and Test8, which fail on main and pass here.

The commits
# Commit Original authors
1 BUG non-zero start index (InBounds replaced by IsInside), ITK bug 8729 Bradley Lowekamp
2 BUG integer types on 64-bit Windows, ITK bug 9260 Luis Ibanez
3 BUG correct results when called multiple times (stale edge-linking node), ITK bug 9431 Bradley Lowekamp
4 BUG ITK_TEMPLATE_EXPORT on ListNode Mayeul Chassagnard
5 BUG isolate the internal filter from the external pipeline Bradley Lowekamp
6 ENH API cleanups: remove legacy code and Threshold/OutsideValue, const getters, size types 7 ITK commits
7 ENH ITK 5 threading: MultiThreaderBase, ParallelizeImageRegion, work units, ProgressTransformer 7 ITK commits
8 PERF hoist accessor calls out of inner loops 3 ITK commits
9 COMP C++ modernization 44 ITK commits
10 STYLE formatting, naming, and include conventions 17 ITK commits
11 DOC comments, license header, and documentation 18 ITK commits
12 ENH the eight new tests, fixture, and baseline this PR
13 ENH minimize the remaining differences from itk::CannyEdgeDetectionImageFilter this PR

Commits 1 to 5 are the bug fixes. Their dependencies are small, so they are applied first: commit 4 adds only the ListNode half 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-By trailer for every original ITK author. Together with the bug-fix commits, 101 ITK commits are listed.

Two ITK changes are handled differently, on purpose:

  • ITK's 2026 PrintNumericTrait adoption (2026-05-08) is applied only when the ITK being built against has it. ITK_VERSION_MAJOR cannot tell, because v6.0b02 and current main both report 6.0.0, so CMakeLists.txt compares ITK_VERSION_TWEAK (ITK's commit date) with the date the feature landed and defines LST_ITK_HAS_PRINT_NUMERIC_TRAIT. Without the macro, including for downstream code that does not use this CMake file, PrintSelf keeps the static_cast form (commit 13).
  • ITK's iterator class-template-argument-deduction commits (2026-02-27) do not compile on ITK 5.4 or v6.0b02, so commit 9 spells out ImageRegionIterator<TOutputImage> at four sites, and commit 13 restores ITK's ImageRegionIterator spelling behind LST_ITK_HAS_ITERATOR_CTAD, defined the same way from ITK_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 bd48a2188a8 and the implementation from 5f06f26925b, 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):

File Before After
itkCannyEdgeDetectionRecursiveGaussianImageFilter.h 265 65
itkCannyEdgeDetectionRecursiveGaussianImageFilter.hxx 528 83

What remains, including the lines of the feature guards, is the Sigma versus Variance and MaximumError smoothing parameters, the Sigma accessors, and doc comments. To compare:

N=s/CannyEdgeDetectionRecursiveGaussianImageFilter/CannyEdgeDetectionImageFilter/g
vimdiff <(sed "$N" include/itkCannyEdgeDetectionRecursiveGaussianImageFilter.hxx) \
        $ITK/Modules/Filtering/ImageFeature/include/itkCannyEdgeDetectionImageFilter.hxx
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.

@hjmjohnson

Copy link
Copy Markdown
Member Author

@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.

@hjmjohnson
hjmjohnson force-pushed the canny-sync-itk-history branch from 00a0f60 to 2de78c2 Compare October 14, 2026 13:59
blowekamp and others added 4 commits October 14, 2026 09:03
…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.
@hjmjohnson
hjmjohnson force-pushed the canny-sync-itk-history branch from 2de78c2 to 7825a8a Compare October 14, 2026 14:05
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.
@hjmjohnson
hjmjohnson removed the request for review from dzenanz October 14, 2026 14:20
@hjmjohnson
hjmjohnson force-pushed the canny-sync-itk-history branch from 7825a8a to 5953ace Compare October 14, 2026 14:26
hjmjohnson and others added 7 commits October 14, 2026 09:56
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.
@hjmjohnson
hjmjohnson force-pushed the canny-sync-itk-history branch 4 times, most recently from df5b534 to 7396329 Compare October 14, 2026 18:01
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.
@hjmjohnson
hjmjohnson force-pushed the canny-sync-itk-history branch from 7396329 to 31a2d1c Compare October 14, 2026 18:34
@hjmjohnson
hjmjohnson requested a review from dzenanz October 15, 2026 13:17
@hjmjohnson

Copy link
Copy Markdown
Member Author

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants