Python 3, puntaje = 1.57
Primero, nuestra serpiente recorre la imagen creando líneas verticales con la misma distancia entre sí.
Podemos extender esta serpiente tomando dos puntos uno al lado del otro en una línea vertical y creando un bucle cuyos puntos finales son ellos.
| |
| => +----+
| +----+
| |
Organizamos los puntos en pares y para cada par almacenamos el tamaño y el valor de brillo promedio del bucle que da el mayor brillo promedio.
En cada paso que elegimos, el par con el valor más alto extiende su bucle para lograr el brillo promedio máximo en la extensión y calculamos el nuevo tamaño de bucle óptimo y el valor de brillo óptimo para el par.
Almacenamos los tripletes (value, size, point_pair) en una estructura de montón ordenada por valor para que podamos eliminar el elemento más grande (en O (1)) y agregar el nuevo modificado (en O (log n)) de manera eficiente.
Nos detenemos cuando alcanzamos el límite de recuento de píxeles y esa serpiente será la serpiente final.
La distancia entre las líneas verticales tiene muy poco efecto en los resultados, por lo que se eligió una constante de 40 píxeles.
Resultados
swirl 1.33084397946
chaos 1.76585674741
fractal 1.49085737611
bridge 1.42603926741
balls 1.92235115238
scream 1.48603818637
----------------------
average 1.57033111819
Nota: la imagen original de "The Scream" no estaba disponible, así que utilicé otra imagen de "The Scream" con una resolución similar.
Gif que muestra el proceso de extensión de la serpiente en la imagen "remolino":
El código toma uno o más nombres de archivo (separados por espacios) de stdin y escribe las imágenes de serpiente resultantes en archivos png e imprime las puntuaciones en stdout.
from PIL import Image
import numpy as np
import heapq as hq
def upd_sp(p,st):
vs,c=0,0
mv,mp=-1,0
for i in range(st,gap):
if p[1]+i<h:
vs+=v[p[0],p[1]+i]+v[p[0]+1,p[1]+i]
c+=2
if vs/c>mv:
mv=vs/c
mp=i
return (-mv,mp)
mrl=[]
bf=input().split()
for bfe in bf:
mr,mg=0,0
for gap in range(40,90,1500):
im=Image.open(bfe)
im_d=np.asarray(im).astype(int)
v=im_d[:,:,0]+im_d[:,:,1]+im_d[:,:,2]
w,h=v.shape
fp=[]
sp=[]
x,y=0,0
d=1
go=True
while go:
if 0<=x+2*d<w:
fp+=[(x,y)]
fp+=[(x+d,y)]
sp+=[(x-(d<0),y)]
x+=2*d
continue
if y+gap<h:
for k in range(gap):
fp+=[(x,y+k)]
y+=gap
d=-d
continue
go=False
sh=[]
px=im.load()
pl=[]
for p in fp:
pl+=[v[p[0],p[1]]]
px[p[1],p[0]]=(0,127,0)
for p in sp:
mv,mp=upd_sp(p,1)
if mv<=0:
hq.heappush(sh,(mv,1,mp+1,p))
empty=False
pleft=h*w//3
pleft-=len(fp)
while pleft>gap*2 and not empty:
if len(sh)>0:
es,eb,ee,p=hq.heappop(sh)
else:
empty=True
pleft-=(ee-eb)*2
mv,mp=upd_sp(p,ee)
if mv<=0:
hq.heappush(sh,(mv,ee,mp+1,p))
for o in range(eb,ee):
pl+=[v[p[0],p[1]+o]]
pl+=[v[p[0]+1,p[1]+o]]
px[p[1]+o,p[0]]=(0,127,0)
px[p[1]+o,p[0]+1]=(0,127,0)
pl+=[0]*pleft
sb=sum(pl)/len(pl)
ob=np.sum(v)/(h*w)
im.save(bfe[:-4]+'snaked.png')
if sb/ob>mr:
mr=sb/ob
mg=gap
print(bfe,mr)
mrl+=[mr]
print(sum(mrl)/len(mrl))
[![image description](SE URL for downsized image)](URL for original image)
.