Signed distance function from grayscale image

I have a grayscale image and I would like to convert it to a signed distance function.

Right now I'm using DistanceTransform but I do not like the result.

image = Import[FileNameJoin[{NotebookDirectory[], "harbor.png"}]];
levelset =
ImageData[DistanceTransform[ColorNegate[image]] ] -
ImageData[DistanceTransform[image] ];
ListPlot3D[levelset]

harbor.png output You can clearly see the outline on the 3d plot which is really noisy, this is probably from the fact that DistanceTransform works on black&white images not on grayscale images.

What I really need is correct gradients near boundary but right now they are complete mess. Is there an easy way how to achieve this?

• Can you explain what you mean by a "signed distance function"? BTW - DistanceTransform does not require a black and white image. – bill s Mar 27 '18 at 13:23

How exact do you need the result to be? You could just binarize an upscaled version of the image:

img = Import["https://i.stack.imgur.com/iMQGZ.png"];
b = Binarize[ImageResize[img, Scaled], .5];
d = DistanceTransform[b] - DistanceTransform[1 - Dilation[b, 1]];

This yields relatively clean-looking gradients:

ListContourPlot[ImageData[d][[;; ;; 8, ;; ;; 8]]] ListPlot3D[ImageData[d][[;; ;; 8, ;; ;; 8]]] • Good idea to upscale and then downscale! This should suffice for my purpose. – tom Mar 27 '18 at 15:12
• Just a small improvement. You probably want to divide the result by 8(or by whatever scaling factor you choose). At first I was a bit confused about the values I got. – tom Mar 27 '18 at 15:36