diff --git a/.github/workflows/itk_dict.txt b/.github/workflows/itk_dict.txt index 48d86e0ca31..a15fdaf508e 100644 --- a/.github/workflows/itk_dict.txt +++ b/.github/workflows/itk_dict.txt @@ -1,352 +1,237 @@ +abdo Abramowitz Abscissae +acq Acq +acqp +adj +adjNeigh +advective Aerosp +aff Agard Aghion +agui Ai -Alsabti -Armijo -AssembleFforTimeStep -AssembleKandM -Austern -Bankman -Beucher -Beucher's -BiDxz -Bigler -Bioimaging -Bioinformatics -Biomedicine -Blas -Bo -Borland -Bracewell -Brents -Burstein -Calc -Cardano's -Chrisochoides -Chu -CiDxy -Clatz -Clunie -Clunies -CommandIterationUpdatev -Conners -ConvertAnitkImageTovtkImageData -ConvertRGBvtkImageDataToAnitkImage -Cortesi's -Coverity -Creade -CreateListOfSamplesWithIDs -Crofton's -Cuadra -Cz -DDataFrameobject -Dasarathy -Dask -Deref -Dij -Dinstein -DownsamplerType -Dufour -Dxx -Dyx -Dyy -Dyz -Dz -Dzx -Dzy -Dzz -Eberhart -Eigen -Elmaghraby -Englewood -Eun -Evol -FEMExceptionItpackSparseMatrixSbagn -FEMExceptionItpackSparseMatrixSbsij -FEMSpatialOPbject -Fedorov -Fei -Fillhole -Fletch -Freesurfer -Frenet -GEAdw -GPUSuperclass's -Gao -Gibbsian -Gifti -Golby -Graphviz -Greenleaf -Guillen -Haar -Hackathon -Haralick -Haralick's -Hauke -Heapsort -Heibel -Hideaki -Hierarchique -Hilden -Hiraki -Hirschberg -Hostname -IOregion -ITKIOMeshGifti -ITKImageToIplImage -ITKOptimizersv -Intelli -Irfan -Irix -Isocenter -Jacobians -Jianfeng -Joly -Josuttis -Kalman -Kanungo -Ke -Kishore -KittlerIllingworth's -Kitware -LBFGSBOptimizerHelperv -LBFGSOptimizerBaseHelperv -LBFGSOptimizerBasev -Lagrange -Laurienti -LeBihan's -Levenberg -Lewy -Liao -Liu -LoadGrav -LoadGravConst -Lz -Magnotta -Malaterre -Mahalanobian -Mahalanobis -Marquardt -Mastronarde -Meijster -Methodv -Minc -MincTransformIO -Minkowski -Musser -NOLINTBEGIN -NOLINTEND -NVidia -Negahdaripour -Newmark -Nicolai -Nicolson -Niftilib -Nikos -Nocedal -Nonunit -NumbxHdrer -Octant -Olivo -Originv -Oxy -Oxyz -Oxz -Oy -Oyz -Pelleg -Pgrad -Pimpl -PimplGlobals -Pluggable -PointSetToPointSetMetricWithIndexv -Polak -Poynton's -Preallocate -PredType -Prefetch -Presaturation -Prev -Propag -Pyarali -Qa -Rajagopalan -RCSfile -RegistrationMethodv -Req -Resizeable -resizeable -Resizes -Ribiere -Rician -Rightarrow -Rosenbrock -SeQuence -Sehgal -SetBranchness -SetMedialness -SetRidgeness -Shanhbag -Shanmugam -Shinin -SinglephaseChanAndVeseSparseFieldLevelSetSegmentation -SingleValuedVnlCostFunctionAdaptorv -Smolikova -Sobel -Soille's -Sonka -Spall -Sprengel -Stegun -Stiehl -Synopsys -Syst -Tajbakhsh -Talos -Tannenbaum -Templateless -Tfar -Thiran -Thuente -Tnear -Tobias -Tra -Trivedi -Tunete -UIDs -Vec -Vectorpixel -Vicomtech -Vidya -Vitreo -Vkept -Vox -Wachowiak -Wanlin -Wdeprecated -Wi -Wtautological -Xu -Y'CbCr -Yager's -Yixun -Yhis -Yuanting -Zeiss -Zosso -Zurada -aPrioris -acq -acqp -advective aic algo alloc +Alsabti analyse anisotropies annuli anticausal antiraster +antonin ap +aparc apath +approx +apps +aPrioris archetypical +argc argidx +arithmetics +Armijo +arr ascii +AssembleFforTimeStep +AssembleKandM asymptote asymptotes atfork attenuator +Austern autocrop +autodetect +autogenerating +aux +avg +avi +axi +axii backend backends +Bankman bayes bb +bcc +BeginGeomDnext +BeginGeomLnext +beginthreadex bestdistortion +Beucher +Beucher's bezier +bg +bidirectionality +BiDxz +Bigler +bigtiff binarization binsize +bio +Bioimaging +Bioinformatics +Biomedicine bistoury -bg +bitmask +bitpix +Blas blockings +Bo +Borland +bot +bpm +bpp +Bracewell brainweb branchness +Brents bresenham bsplice btw buf bugfix +bugtracker +Burstein bw +bx +Bx +byteswap byu bzip calc +Calc +CalcDirnVector calloc callosum -centerline +Cardano's +cbmag +ccaster cdf cdfs +cellbuffersize +celldata +celllinks +centerline cert cfl cgi cha +chamferoutput +changemap chebycoefs chebyshev +checkboard +checksum +childrens chokkan +Chrisochoides chroma +Chu +chunked +chunking ci +CiDxy +cimage +cinfo +cit classvtk +Clatz +clEnqueueNDRangeKernel +Clerc +clib cline +Clunie +Clunies +clusterer +cmap cme cms +cnt codec +codestream coeff coeffP -composedness +CommandIterationUpdatev +commandline +comp complainty +composedness +Composedness computable +confocal connectedness +Conners +consec +ConvertAnitkImageTovtkImageData +ConvertRGBvtkImageDataToAnitkImage convolves -confocal cooccurrence +Cortesi's +costfunction costless +coverity +Coverity cp cpp cr cranio +Creade +CreateListOfSamplesWithIDs creativecommons +Crofton's +cronwake cryo cryosection cryosectional ctwd +Cuadra +cuboidal +cum +cumGaussianArrayCopySize +cumGaussianArraySize cumulate cumulated +curdist curvilinear +cuthill cv -cvCvtColor cvals +cvCvtColor cx cy +cyclicity +Cygwin +Cz d'Attente -dDwWmMyY da +Danielsson +dapi +Dasarathy dask +Dask dat -dcl -dcmimage -dcmtk datablock +datafile dataig dataobject datapipeline dataraces dbg +dblVec +dcl +dcmimage +dcmtk +dd +DDataFrameobject ddu +dDwWmMyY deallocated decompositions +decompressor decrementing decubitus defacto @@ -354,75 +239,123 @@ defe defem deformably deformer +deg degr +demangling demark denoiser denormal +Dept +Deref deriv +desc descr detections +devicetype +devs dgr +diast dic +dicoimg dicoopxt dicts +diff +Dij dilations dima dimb dimensionally dimensioned +Dinfo +Dinstein dirn dirns +div +Div +DivFloor +DivReal dl docstring dodecahedron +doesAttrExist dof +doitkEllipsoidInteriorExteriorSpatialFunctionTest +doitkSymmetricEllipsoidInteriorExteriorSpatialFunctionTest dom +doublereal downlist downlists -doublereal +DownsamplerType +dparameters drand +ds dseq dtnrm dtwd +Dufour duplications dwi dxx +Dxx dxy dxz +dyn +Dyx dyy +Dyy dyz +Dyz +dz +Dz +Dzx +Dzy dzz +Dzz e'e +eaa +Eberhart echall edgecell eg -eigenVecs +eigen +Eigen +eigenlib eigensolve eigenvec +eigenVecs +eigenvector +eigenvectors eix eiy eiz el -elt +Elmaghraby elmts +elt emph endlink +Englewood ent entropies eps epsmch epsmcj +eqi eqn +eqrt equalities equispaced -eqi -eqrt equs erf erfc +errid +est etd +Eun evaluable +Evol excitations +ExhaustiveOptimizedMetricV4 explicits extensionkey extradiagonal @@ -430,86 +363,177 @@ extrapolators extremum ey ez +faceGeometry factorable +FactorylessNewMacro fah failsafe +failsafes fastmarching favourite +fclose fcv fdinbuf fdistream fdostream fdstream +featureimage +fedisableexcept +Fedorov +feenableexcept +fegetenv +fegetexcept +Fei +fem +FEMExceptionItpackSparseMatrixSbagn +FEMExceptionItpackSparseMatrixSbsij femobject -fh +femparray +FEMSpatialOPbject +fenv +feraiseexcept +fesetenv +fexcept +ffmpeg fftinput fftoutput +fftpad fgm +fh fi fia fid fifo +filepath +fillch +fillhole +Fillhole +fixme +fixup +Fletch floatingfigure +floatRepresentationdx +floatRepresentationfx +fmag +fmri fn fnorm +foo +fopen formatter formatters fortran +FourCCtoEncoderType fourier +fov +fprintf +fps +fpu framebox framerate freesurfer +Freesurfer +Frenet +freq frobenius fsa fsb fstream ftol +fullx functors fw +fwrite fx fy ga galist gamold +Gao gauss gaussians +Gawedzki gb gc gdcm gdef +GEAdw geometrydata gerrit +GetQEGeom +getSint +Gibbsian gifti +Gifti gifticlib gii glehmann gnuplot +Golby +gotchas gp gpu +GPUSuperclass's +Grandin +Graphviz +Greenleaf +grep greyval -guid +gridsize +grindpeak gtol +guid +Guido +Guillen +gws gzip +Haar +Hackathon halfwidth +Hannu haralick +Haralick +Haralick's hardcoding +Hauke +hd hdfgroup +hdim +headerless +Heapsort +Heibel +Helminen +hermitian hexa hexahedral hh hhs +Hideaki +Hierarchique highrange +Hilden hipaa +Hiraki +Hirschberg +hist +hk +Homebrew hor +Hostname hough Hounsfield +hr hval hvector +hypercubic +hypercuboidal +hyperthreading ia ibmth icall icnt icosahedron +idet idtype idx'th iems @@ -519,172 +543,309 @@ ierr'th ifac ifstream ifti +Ignacy +ii +iix ijk +im +Im imagedimension +imagekey +imager +Imager +imgfile imit immunofluorescent +imread imsl imthd imthdd +incl includegraphics incx incy indct indir +informations inhomogeneous +initialisation +initialisations initialise initialised -initialisation +initialization initializations +initialize +initialized initnorm inpMax inpMin inputreader +inria +insideregion +Intelli intensityrescaler interactor interdependencies interop interslice +intmax intra invariance invariants +iobase +ioregion +IOregion +ip +iparm ipl -isValidJacobianCalcLocat +ipstr +Irfan +Irix +isk +Isocenter isocontouring isol -isk +isoshells +isValidJacobianCalcLocat +isw iter iterator's -itkGEAdwImageIO -itkFEMLoadGrav itkcaption +itkFEMLoadGrav +itkGEAdwImageIO +ITKImageToIplImage +ITKIOMeshGifti +ITKOptimizersv itmax itpack -ip -iparm -ipstr -isw itr +iv ival iwksp iwork iwskp +Ix ja jacobi jacobians +Jacobians jcd jcg jh +Jianfeng jj +Joly +Josuttis +jph +jpt jsi jvm k'th +Kalman +Kanungo +Ke +kg kHd -kwargs -kwstyle +kiran +Kishore +KittlerIllingworth's +Kitware kmeans kth +Ku +kwargs +kwstyle +kwsys labeler labelized +labeller +labeloutIt +Lagrange +lapl +Las lastx +Laurienti lbfgs lbfgsb +LBFGSBOptimizerHelperv +LBFGSOptimizerBaseHelperv +LBFGSOptimizerBasev lbg +LeBihan's leftarrow levell levenberg +Levenberg +Lewy +lh +Liao liblbfgs +libpngs +libsrc +libtiff lin lina lins +Liu lm lnt +LoadGrav +LoadGravConst loc localizer lockings logfixedImageMarginalPDFValue +longjmp +losslessly +lowercase lowpass lowrange +lpvReserved lradius lsw +lv lventricle +lws +Lz +macbook +macports +Macports +mag +Magnotta +Mahalanobian +Mahalanobis +Malaterre +mallinfo malloc mappable marquardt +Marquardt +Mastronarde +materiel mathrm matlab matricial +maxerr maxf +MaximumCityblockDistance maxm maxnorm maxs mebibytes medialness +Meijster +memcmp memcpy memmove memset merchantability +mersenne meshpixperelt metacharacters +metadatadictionary +metadataobject +metadatas +metadictionary +Methodv metricb metricx +micopy microgauss midslice migr minc +Minc +MincTransformIO +minf +mingw minimizer minimizers minimizes -minf -mingw +Minkowski minm minmod mins mis misalignments +misc +miset mmm +modulo +Moebius +morph +MorphFunction +mrf msec +msg mskIt +mtime +mtimes +mtype +mult multicolumn multicomponent multicomponents +multiframe multimetric multimodal multipage multiphase multivalued +multivisitor +Musser mutator mutexes +mutexing mv +MvtSunf +mx myhistogram +Myofibers +myoutput myreferenceimage -mx +natively nbOfDirections nbOfPixels +nc ncodewords ndarray ndof ndofpernode +Negahdaripour neighb +nel +nelements ness neuroimaging +newlines +Newmark nex +Nicolai +Nicolson niftilib +Niftilib +niix +Nikos nint nn nnb nocedal +Nocedal noded noface noindent +NOLINTBEGIN +NOLINTEND nonboundary nonterminal +Nonunit noreturn normalise normalised normalizations normalizer normalsize +nothrow nout noutt nr @@ -692,154 +853,291 @@ nreal nrl nrrdreader ns +Nslew +NumbxHdrer +numComp numerics numiter numnodesperelt +NVidia +nvox nw nx nxy -nxz nxyz +nxz ny nyz nz nzelts +objectif obo octahedron octant +Octant octants -oindex +offcentre ofstream +oindex +oitr oldnrm +Olivo +Onate +onedegree +op +opencl +opencvIO +openjpeg +oper +opj +optimisation +optimization optimizersv +orientable orienter +orig +Originv +Oring +Orings orthant orthantwise ortho +orthog orthogonalize orthonormality -opencl -openjpeg -oper -outJacobian's +ostringstream outdir +outJacobian's +outpixeltype +outputimage +outputpixeltype +outstream oversegmentation +Oxy +Oxyz +Oxz +Oy +Oyz padder pageref -pathologies palletized parallelizes +pathologies pc pca +pcoords pdata pde +Pelleg +Pgrad +Photometrics +pij +pijk +Pimpl +PimplGlobals pipelined pipelining piter +pixdim +PixelMed pixelspacing +pixeltypes +pkg plugable +Pluggable +pmap pmatrix +pointdata pointIDs pointItr pointsets +PointSetToPointSetMetricWithIndexv poisson's +Polak +polygondata polylines ponderation posn postcondition posteriori +Poynton's +pp +ppc +pq +pqr pragmas +prbndx +Preallocate preallocated +prec precalculate precalculating precompute preconditioner +predef +PredType +Prefetch prepend +Presaturation prescan -priorityqueue +prev +Prev printf printself +priorityqueue procs +Proj +Propag propagQueue +prvalue +prvalues +Prvalues psd +pset pshapeD pso pth pthread pthreads +pts +pw px pxp +Pyarali pythonprepend qa +Qa +QEcell +QEGeom qform -quantized +qfrom +qm +qr +qsort qto +QuadEdgeGeom +QuadEdgeMeshConstIteratorGeom +QuadEdgeMeshIteratorGeom +Quaing +quantized +rad radiographs radiological +Rajagopalan randgen +rasterizable rasterization -rasterized rasterize -rasterizable +rasterized rayleigh rcol -rgba +RCSfile reco reconstructer rectus recurse redeclared +redistributor +refactored +regiongrow regionprops regionsize +RegistrationMethodv +reinitializer reinitializes reinitializing +rel relabeler renderers renormalization renormalize renormalizing representable +Req +res resampler rescaler rescales rescaling +resizeable +Resizeable +Resizes +resLevel resubstitute +ret rethrow +rgba rhoold +Ribiere +Rician ridgeness +Rightarrow rightharpoonup +ritr rl rlp rof +Rosenbrock rotationally +rotplus +rowsperstrip rparm rr rrr rscg +RSGv rssi +rt rtc samedirection savemean savevar +saxi +sbend scalarndarr scaler scalingfactor scalings -scanMem scanf scanlines +scanlinesize +scanMem +scantype +scatt +scl scol +scomponents sct +sdk sdt sed +seedimage seek'd seekable +segfault +segfaults segmenter +Sehgal seimens seminumerical +SeQuence +serie +SerieHelper +SetBranchness +setjmp +setLibverBounds +SetMedialness +SetRidgeness +Sf sform -sholdImageCalculator +sforms +Sg +Shanhbag +Shanmugam shapeDgl +SharedMorphUtilities +Shinin +sholdImageCalculator shrinker shrinkings si @@ -848,41 +1146,78 @@ sigmad sigmoids signa signproduct +sim +SinglephaseChanAndVeseSparseFieldLevelSetSegmentation +SingleValuedVnlCostFunctionAdaptorv situ sizeable +sizez slicessorted smartpointers sme +Smolikova snprintf +Sobel +Soille's solmat +soln +Sonka sor sourceforge sourcelist +Spall spatialobject spdMatrix spdMatrixB +specificities +speedimage +sphereality splenium +splittable +splitted +Splus spr +Sprengel sptr +sshfs ssor ssorcg ssorsi +sstreamState stackrel +Starcam +startz +stat +statBuf +Stegun steplength +stepsize +Stepsize stereographic +Stiehl stod stoi streamable +stringstream +strncpy stype subarray subblocks subcube +subdomain +subdomains +Subdomains subdoxygen +subfile +subfiles subimages subindex subitems +submap +Submap submatch submatrix +subprocess subrefining subsampled subsampler @@ -890,62 +1225,121 @@ subsamplers subsamples substring substrings +subterraneous subtransform subtype summands superclass sym sympy +syn +Synopsys +sysroot +syst +Syst tagkey +tagkeys tagvalue +Tajbakhsh +Talos +Tannenbaum tardis +tbb +tcdf tcol +td +tdo +tech +teletype +Templateless +Testcode textheight textit texttt textwidth +Tfar +tfrequency tgz +thetamin +thetaplus thewtex +Thiran thisDic threadIDs threadsafe thresholder +thruview +Thuente thusly +tiffinfo +tiffiop +timestamp +timestamps tinuous -tfrequency tmpimage +tname +Tnear +Tobias +toffset tol +tolower tomerge +toplevel topologies +topoTest +topoTestpoints +toupper +tpdf tpl +tpo +Tra +transformIO tridimensional +Trivedi tunable +Tunete +twould txf +txx typedef'ed +typeless typemaps ub +UIDs +uiig +uk +umich +un unallocated -uncor +unattachable unbalancedness unbuffered +uncomment +Uncomment uncompiled +uncompress +uncor undeformed underbrace underflowed +Unencoded unfinalize unfinalizing uninitVar uninverted -unistd uniqLabel +unistd +unix unlink unmapped +unpacker unpadded unregister unrotated unselect unselected unsmoothed +Unsort unvisited upcast updatable @@ -956,59 +1350,120 @@ upsampler upwinding usec userdata +usleep +valgrind vall vals -valgrind +valuediff +valuev +vanal variates vd +ve +Vec +vecsize vectorial +Vectorpixel venc +ver +versa +vert +Vicomtech vidl +Vidya viewport +viewports visu vitrea +Vitreo +viz +Vkept +Vol +vop +vox +Vox voxelwise +vp +vr +vsize vv vxl vxl's +Wachowiak +waitid +walkthrough +Wanlin +warnable warper wb +wchar wctad +Wdeprecated weakgroup +webcam westin westinMEDIA +Wextra wfopen +Wheter +Wi winnls wisdoms +wk +wkjeong wksp wmatter +Wmaybe wolfe wopen workgroup worksize +Wrange +writability wstring +Wtautological +wunlink +Wunused wv xarray xfff xfrequency +xfull +xfullx +ximg xlen xlmda +xlv xspace +Xu +xyzt +Y'CbCr +Yager's yfrequency +Yhis +Yixun youngs yspace +Yuanting yx zagging zbrent +zeiss +Zeiss zero'th zerocrossings zeroflux zeroin -zig zfrequency -zmin +Zhao +zig +zlib zmax +zmin +Zosso +zspace +Zurada +Zurich zx zy zymatrix -zspace diff --git a/Modules/Segmentation/Voronoi/include/itkVoronoiDiagram2DGenerator.hxx b/Modules/Segmentation/Voronoi/include/itkVoronoiDiagram2DGenerator.hxx index c7600faad3e..32a40f70bba 100644 --- a/Modules/Segmentation/Voronoi/include/itkVoronoiDiagram2DGenerator.hxx +++ b/Modules/Segmentation/Voronoi/include/itkVoronoiDiagram2DGenerator.hxx @@ -272,8 +272,6 @@ VoronoiDiagram2DGenerator::ConstructDiagram() EdgeInfo curr1; EdgeInfo curr2; - unsigned char frontbnd; - unsigned char backbnd; std::vector cellPoints; for (unsigned int i = 0; i < m_NumberOfSeeds; ++i) { @@ -283,12 +281,56 @@ VoronoiDiagram2DGenerator::ConstructDiagram() buildEdges.push_back(curr); EdgeInfo front = curr; EdgeInfo back = curr; - while (!(rawEdges[i].empty())) + // Assemble raw edges into a connected chain for this Voronoi cell. + // Each iteration pops an edge from the deque and attempts to attach + // it to the front or back of the growing chain. Edges that cannot + // attach are pushed back for retry, because later attachments change + // the chain endpoints and may make previously unattachable edges + // attachable. + // + // A stall counter tracks progress: it resets whenever an edge + // attaches, and terminates the loop when a full pass through the + // deque makes no progress. Without this, certain degenerate seed + // configurations (near-collinear seeds, ITK issue #4386) cause an + // infinite loop because Fortune's algorithm produces near-zero-length + // edges whose endpoints differ by less than floating-point tolerance + // but have different vertex IDs. These degenerate edges cannot + // attach because: + // 1. Their endpoints don't match any chain vertex by ID. + // 2. The chain may already be closed (front[0] == back[1]). + // 3. The boundary-bridging logic doesn't apply when the chain + // endpoints are interior (not on the domain boundary). + // Such edges are safely dropped — they represent floating-point + // artifacts where two boundary intersection points should be + // identical in exact arithmetic. + auto remainingBeforeStall = rawEdges[i].size(); + while (!(rawEdges[i].empty()) && (remainingBeforeStall != 0)) { + --remainingBeforeStall; curr = rawEdges[i].front(); rawEdges[i].pop_front(); - frontbnd = Pointonbnd(front[0]); - backbnd = Pointonbnd(back[1]); + + // Check if this edge is a degenerate near-zero-length artifact. + // Fortune's algorithm can produce edges whose two endpoints map + // to the same geometric point (within DIFF_TOLERENCE) but have + // different vertex IDs. These carry no geometric information + // and can be safely discarded. + const PointType & edgeStart = m_OutputVD->GetVertex(curr[0]); + const PointType & edgeEnd = m_OutputVD->GetVertex(curr[1]); + if (!differentPoint(edgeStart, edgeEnd)) + { + itkDebugMacro("Dropping degenerate near-zero-length edge [" + << curr[0] << " (" << edgeStart[0] << "," << edgeStart[1] << ") -> " << curr[1] << " (" + << edgeEnd[0] << "," << edgeEnd[1] << ")]" + << " for cell " << i << ": endpoints within DIFF_TOLERENCE=" << DIFF_TOLERENCE); + // Count as progress — this edge is resolved (discarded). + remainingBeforeStall = rawEdges[i].size(); + continue; + } + + unsigned char frontbnd = Pointonbnd(front[0]); + unsigned char backbnd = Pointonbnd(back[1]); + bool edgeAttached = true; if (curr[0] == back[1]) { buildEdges.push_back(curr); @@ -357,20 +399,38 @@ VoronoiDiagram2DGenerator::ConstructDiagram() else { rawEdges[i].push_back(curr); + edgeAttached = false; } } else { rawEdges[i].push_back(curr); + edgeAttached = false; } + if (edgeAttached) + { + // Progress was made — chain endpoints changed, so previously + // unattachable edges may now be attachable. + remainingBeforeStall = rawEdges[i].size(); + } + } + // After assembly, all edges for this cell should have been either + // attached to the chain or identified as degenerate artifacts. + // Any remaining edges indicate an unexpected algorithmic failure. + if (!rawEdges[i].empty()) + { + itkExceptionMacro("VoronoiDiagram2DGenerator::ConstructDiagram: " + << rawEdges[i].size() << " non-degenerate edge(s) could not be " + << "assembled into cell " << i << " boundary chain. " + << "This indicates an unexpected geometric configuration."); } curr = buildEdges.front(); curr1 = buildEdges.back(); if (curr[0] != curr1[1]) { - frontbnd = Pointonbnd(curr[0]); - backbnd = Pointonbnd(curr1[1]); + unsigned char frontbnd = Pointonbnd(curr[0]); + unsigned char backbnd = Pointonbnd(curr1[1]); if ((frontbnd != 0) && (backbnd != 0)) { if (frontbnd == backbnd) diff --git a/Modules/Segmentation/Voronoi/test/CMakeLists.txt b/Modules/Segmentation/Voronoi/test/CMakeLists.txt index 944d3580a38..a3103f22309 100644 --- a/Modules/Segmentation/Voronoi/test/CMakeLists.txt +++ b/Modules/Segmentation/Voronoi/test/CMakeLists.txt @@ -3,10 +3,18 @@ set(ITKVoronoiTests itkVoronoiSegmentationImageFilterTest.cxx itkVoronoiSegmentationRGBImageFilterTest.cxx itkVoronoiDiagram2DTest.cxx + itkVoronoiDiagram2DInfiniteLoopTest.cxx itkVoronoiPartitioningImageFilterTest.cxx) createtestdriver(ITKVoronoi "${ITKVoronoi-Test_LIBRARIES}" "${ITKVoronoiTests}") +itk_add_test( + NAME + itkVoronoiDiagram2DInfiniteLoopTest + COMMAND + ITKVoronoiTestDriver + itkVoronoiDiagram2DInfiniteLoopTest) + itk_add_test( NAME itkVoronoiSegmentationImageFilterTest diff --git a/Modules/Segmentation/Voronoi/test/itkVoronoiDiagram2DInfiniteLoopTest.cxx b/Modules/Segmentation/Voronoi/test/itkVoronoiDiagram2DInfiniteLoopTest.cxx new file mode 100644 index 00000000000..754fc6db3f1 --- /dev/null +++ b/Modules/Segmentation/Voronoi/test/itkVoronoiDiagram2DInfiniteLoopTest.cxx @@ -0,0 +1,78 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include "itkVoronoiDiagram2DGenerator.h" +#include "itkTestingMacros.h" + +// Regression test for ITK issue #4386: near-collinear seed configurations +// caused an infinite loop in VoronoiDiagram2DGenerator::ConstructDiagram(). +// +// Root cause: Fortune's algorithm produces near-zero-length edges when +// boundary intersection points coincide within floating-point tolerance. +// These degenerate edges have different vertex IDs but geometrically +// identical endpoints (within DIFF_TOLERENCE = 0.001). They cannot be +// attached to the growing boundary chain because: +// 1. Their vertex IDs don't match any chain endpoint. +// 2. The chain may already be closed (front == back). +// 3. Boundary-bridging logic doesn't apply when chain endpoints are +// interior (not on the domain boundary). +// The fix explicitly detects and drops these degenerate edges using the +// existing differentPoint() tolerance check. +int +itkVoronoiDiagram2DInfiniteLoopTest(int argc, char * argv[]) +{ + if (argc != 1) + { + std::cerr << "Usage: " << itkNameOfTestExecutableMacro(argv) << std::endl; + return EXIT_FAILURE; + } + + using VoronoiDiagramType = itk::VoronoiDiagram2D; + using VoronoiGeneratorType = itk::VoronoiDiagram2DGenerator; + using PointType = VoronoiDiagramType::PointType; + + // Six near-collinear seeds (x in [-1.40, -1.21]) that produce a + // degenerate Voronoi edge on the left domain boundary where two + // intersection points are ~0.00003 apart. + auto vg = VoronoiGeneratorType::New(); + vg->SetOrigin(PointType{ { -1.61569, -1.76726 } }); + vg->SetBoundary(PointType{ { 1.60174, 1.76345 } }); + vg->AddOneSeed(PointType{ { -1.39649, 0.322212 } }); + vg->AddOneSeed(PointType{ { -1.30128, 0.231786 } }); + vg->AddOneSeed(PointType{ { -1.21509, 0.0515039 } }); + vg->AddOneSeed(PointType{ { -1.22364, -0.030281 } }); + vg->AddOneSeed(PointType{ { -1.22125, -0.120815 } }); + vg->AddOneSeed(PointType{ { -1.25159, -0.23593 } }); + + // Without the fix, this call loops forever. With the fix, the + // degenerate near-zero-length edge is detected and dropped, and + // the exception guard ensures no non-degenerate edges are lost. + ITK_TRY_EXPECT_NO_EXCEPTION(vg->Update()); + + // Verify all 6 cells were constructed with valid boundaries + auto vd = vg->GetOutput(); + for (unsigned int i = 0; i < 6; ++i) + { + VoronoiDiagramType::CellAutoPointer cellPtr; + vd->GetCellId(i, cellPtr); + ITK_TEST_EXPECT_TRUE(cellPtr->GetNumberOfPoints() >= 2); + } + + std::cout << "Test passed." << std::endl; + return EXIT_SUCCESS; +}