Redraw an image with just one closed curve Inspired by vi.sualize.us

Goal

The input is a grayscale image and the output is a black and white image. The output image consists of just one closed curve (loop) that is not allowed to intersect with itself or touch itself. The width of the line shall be constant throughout the whole image. The challenge here is finding an algorithm for doing so. The output just has to represent the input image, but with any artistic freedom. The resolution is not so important but the aspect ratio should stay about the same.

Example




More test images
Python: Hilbert curve (373 361)

I decided to draw a Hilbert curve with variable granularity depending on the image intensity:

import pylab as pl
from scipy.misc import imresize, imfilter
import turtle

# load image
img = pl.flipud(pl.imread("face.png"))

# setup turtle
levels = 8
size = 2**levels
turtle.setup(img.shape[1] * 4.2, img.shape[0] * 4.2)
turtle.setworldcoordinates(0, 0, size, -size)
turtle.tracer(1000, 0)

# resize and blur image
img = imfilter(imresize(img, (size, size)), 'blur')

# define recursive hilbert curve
def hilbert(level, angle = 90):
    if level == 0:
        return
    if level == 1 and img[-turtle.pos()[1], turtle.pos()[0]] &gt; 128:
        turtle.forward(2**level - 1)
    else:
        turtle.right(angle)
        hilbert(level - 1, -angle)
        turtle.forward(1)
        turtle.left(angle)
        hilbert(level - 1, angle)
        turtle.forward(1)
        hilbert(level - 1, angle)
        turtle.left(angle)
        turtle.forward(1)
        hilbert(level - 1, -angle)
        turtle.right(angle)

# draw hilbert curve
hilbert(levels)
turtle.update()


Actually I planned to make decisions on different levels of detail, like "This spot is so bright, I'll stop the recursion and move to the next block!". But evaluating image intensity locally leading to large movements is very inaccurate and looks ugly. So I ended up with only deciding whether to skip level 1 or to draw another Hilbert loop.

Here is the result on the first test image:



Thanks to @githubphagocyte the rendering is pretty fast (using turtle.tracer). Thus I don't have to wait all night for a result and can go to my well-deserved bed. :)



Some code golf

@flawr: "short program"? You haven't seen the golfed version! ;)

So just for fun:

from pylab import*;from scipy.misc import*;from turtle import*
i=imread("f.p")[::-1];s=256;h=i.shape;i=imfilter(imresize(i,(s,s)),'blur')
setup(h[1]*4.2,h[0]*4.2);setworldcoordinates(0,0,s,-s);f=forward;r=right
def h(l,a=90):
 x,y=pos()
 if l==1and i[-y,x]&gt;128:f(2**l-1)
 else:
  if l:l-=1;r(a);h(l,-a);f(1);r(-a);h(l,a);f(1);h(l,a);r(-a);f(1);h(l,-a);r(a)
h(8)


(373 361 characters. But it will take forever since I remove the turte.tracer(...) command!)



Animation by flawr

flawr: My algorithm is slightly modified to what @DenDenDo told me: I had to delete some points in every iteration because the convergence would slow down drastically. That's why the curve will intersect itself.