
Euclidean Distance Transform

sbossuyt
This function takes an image mask M_fg and returns a matrix M_DistanceTransform of the same size, containing the Euclidean distance from each foreground pixel to its nearest background pixel. The matrix M_DistanceTransform is created in the current folder, overwriting any existing wave with that name (much like what happens with many forms of the ImageTransform operation). The image mask may be a binary image, or a grayscale image with an optional threshold parameter to specify the background level. The other optional parameters specify the use of periodic boundary conditions or a skeletonization of the EDT, which returns only the values of the distance for those pixels where moving away from the nearest edge pixel changes which is the nearest edge pixel. Typically, the skeletonization gives a centerline of the object with side branches at sharp corners of the object. You can pass a negative number for the skeletonization parameter to attempt to detect only the centerline.
function/WAVE make_FastEDT(M_fg, [bg, wrap,skel]) WAVE M_fg // foreground mask indicating pixels to calculate Euclidean distance from Variable bg // background level, below which pixels are considered background Variable wrap // flag to use periodic boundary conditions Variable skel // flag to do skeletonization (0 to skip, >0 for skeletonization, <0 for centerline only) bg= ParamIsDefault(bg)? 0 : bg wrap= ParamIsDefault(wrap)? 0 : wrap skel= ParamIsDefault(skel)? 0 : skel DFREF saveDFR= getDataFolderDFR() SetDataFolder GetWavesDataFolderDFR(M_fg) String dfName= UniqueName("EDT_",11,0) NewDataFolder/O/S $dfName Variable dX= DimDelta(M_fg,0) Variable nX= DimSize(M_fg,0) Variable dY= DimDelta(M_fg,1) Variable nY= DimSize(M_fg,1) Variable bigNum= (nX*dX)^2 + (nY*dY)^2 Make/O/N=(nX+2,nY+2) F=bigNum, Rx=0 , Ry=0, C = NaN F[1,nX][1,nY]= (M_fg[p-1][q-1] <= bg )? 0 : bigNum if (wrap) F[1,nX][0]= F[p][nY] F[1,nX][nY+1]= F[p][1] F[0][]= F[nX][q] F[nX+1][]= F[1][q] endif Variable iX,iY // indices of current pixel Variable pF,qF // current value and test value of squared distance Variable pRx,pRy // current value of relative location to nearest bg pixel for ( iY=1 ; iY<=nY ; iY+=1 ) for ( iX=1 ; iX<=nX ; iX+=1 ) pF= F[iX][iY] // immediately skip masked pixel if ( pF<=bg ) Rx[iX][iY]= 0 Ry[iX][iY]= 0 C[iX][iY]= 0 Continue endif // upper neighbour qF= F[iX][iY-1] - 2*Ry[iX][iY-1] + dY if ( pF > qF ) pF= qF pRx= Rx[iX][iY-1] pRy= Ry[iX][iY-1]-dY endif // left neighbour qF= F[iX-1][iY] - 2*Rx[iX-1][iY] + dX if ( pF > qF ) pF= qF pRx= Rx[iX-1][iY]-dX pRy= Ry[iX-1][iY] endif // upper-left neighbour qF= F[iX-1][iY-1] - 2*Rx[iX-1][iY-1] + dX - 2*Ry[iX-1][iY-1] + dY if ( pF > qF ) pF= qF pRx= Rx[iX-1][iY-1]-dX pRy= Ry[iX-1][iY-1]-dY endif // upper-right neighbour qF= F[iX+1][iY-1] + 2*Rx[iX+1][iY-1] + dX - 2*Ry[iX+1][iY-1] + dY if ( pF > qF ) pF= qF pRx= Rx[iX+1][iY-1]+dX pRy= Ry[iX+1][iY-1]-dY endif F[iX][iY]= pF Rx[iX][iY]= pRx Ry[iX][iY]= pRy endfor endfor for ( iY=nY ; iY>=1 ; iY-=1 ) for ( iX=nX ; iX>=1 ; iX-=1 ) pF= F[iX][iY] // immediately skip masked pixel if ( pF==0 ) Continue endif pRx= Rx[iX][iY] pRy= Ry[iX][iY] // lower neighbour qF= F[iX][iY+1] + 2*Ry[iX][iY+1] + dY if ( pF >= qF ) pF= qF pRx= Rx[iX][iY+1] pRy= Ry[iX][iY+1]+dY endif // right neighbour qF= F[iX+1][iY] + 2*Rx[iX+1][iY] + dX if ( pF > qF ) pF= qF pRx= Rx[iX+1][iY]+dX pRy= Ry[iX+1][iY] endif // lower-right neighbour qF= F[iX+1][iY+1] + 2*Rx[iX+1][iY+1] + dX + 2*Ry[iX+1][iY+1] + dY if ( pF > qF ) pF= qF pRx= Rx[iX+1][iY+1]+dX pRy= Ry[iX+1][iY+1]+dY endif // lower-left neighbour qF= F[iX-1][iY+1] - 2*Rx[iX-1][iY+1] + dX + 2*Ry[iX-1][iY+1] + dY if ( pF > qF ) pF= qF pRx= Rx[iX-1][iY+1]-dX pRy= Ry[iX-1][iY+1]+dY endif F[iX][iY]= pF Rx[iX][iY]= pRx Ry[iX][iY]= pRy endfor endfor Variable sX,sY // location of pixel to check for skeletonization if ( skel ) // check whether the next pixel away from nearest edge pixel uses different edge for ( iY=1 ; iY<=nY ; iY+=1 ) for ( iX=1 ; iX<=nX ; iX+=1 ) pF= F[iX][iY] if ( pF == 0 ) Continue endif pRx= Rx[iX][iY] pRy= Ry[iX][iY] if ( abs(pRx) >= abs(pRy) ) sX = iX - sign(pRx) sY = iY else sX = iX sY = iY - sign(pRy) endif if ( skel > 0 && (pRx-Rx[sX][sY])^2+(pRy-Ry[sX][sY])^2 > skel^2) // keep values where nearest edge pixels is are not near to each other C[iX][iY] = F elseif ( skel < 0 &&(pRx)^2+(pRy)^2 > skel^2 && (pRx+Rx[sX][sY])^2+(pRy+Ry[sX][sY])^2 < skel^2 *pF) // only keep values where nearest edge pixel is (near) diametrically opposed C[iX][iY] = F endif endfor endfor MatrixOP/O M_DistanceTransform= sqrt(C) else MatrixOP/O M_DistanceTransform= sqrt(F) endif Duplicate/O/R=[1,nX][1,nY] M_DistanceTransform ::M_DistanceTransform WAVE wResult= ::M_DistanceTransform CopyScales M_fg wResult KillDataFolder : SetDataFolder saveDFR Return wResult end
This algorithm assumes that pixelated Voronoi neighborhoods are 8-connected. This is not always true, e.g., for a pixel whose nearest background pixel is at offset (-4,1) with other background pixels at (-3,3) and (-5,0). However, the discrepancy with the true EDT is never more than a small fraction of a pixel.

Forum

Support

Gallery
Igor Pro 9
Learn More
Igor XOP Toolkit
Learn More
Igor NIDAQ Tools MX
Learn More