Arcoiris blanco y negro


60

Dada una imagen que solo tiene píxeles en blanco y negro y una ubicación (x, y) que es un píxel blanco, colorea los píxeles blancos según su distancia mínima de Manhattan desde (x, y) en una ruta que solo implica atravesar otros píxeles blancos.

El tono de los píxeles coloreados debe ser proporcional a su distancia desde (x, y), por lo que el píxel en (x, y) tendrá un tono de 0 ° (rojo puro) y los píxeles más alejados de (x, y) tendrá un tono de 360 ​​° (también rojo), y los otros tonos se mezclarán de manera transparente y lineal en el medio. La saturación y el valor deben ser ambos 100%.

Si un píxel blanco no está conectado a (x, y) a través de otros píxeles blancos, entonces debe permanecer blanco.

Detalles

  • La entrada consistirá en el nombre del archivo de la imagen o los datos de la imagen en bruto, más los enteros x e y.
  • La imagen de salida puede guardarse en un archivo o canalizarse sin formato a stdout en cualquier formato de archivo de imagen común, o simplemente mostrarse.
  • El valor x es 0 en los píxeles más a la izquierda y aumenta a la derecha. El valor y es 0 en los píxeles superiores y aumenta al descender. (x, y) siempre estará en los límites de la imagen.
  • Se permiten tanto programas completos como funciones.

El código más corto en bytes gana.

Ejemplos

Todas estas imágenes se han reducido para ahorrar espacio. Haz clic para verlos a tamaño completo.

Imagen de entrada:

ejemplo 1 entrada

(x,y) = (165,155) y (x,y) = (0,0)

ejemplo 1 salida A ejemplo 1 salida B


Imagen de entrada y salida con (x,y) = (0,0):

ejemplo 5 entrada ejemplo 5 entrada A


Imagen de entrada y salida con (x,y) = (600,350):

ejemplo 2 entrada ejemplo 2 salida


Imagen de entrada y salida con (x,y) = (0,0):

ejemplo 3 entrada ejemplo 3 salida


Imagen de entrada y salida con (x,y) = (0,0):

ejemplo 4 entrada ejemplo 4 salida


Opcional -30% de bonificación: use la distancia euclidiana. Una sugerencia para su algoritmo es la siguiente (esquema general):

  1. Tener un píxel de inicio.
  2. Relleno de inundación desde ese píxel.
  3. Por cada píxel alcanzado en el relleno de inundación,
  4. Pase del píxel inicial a ese píxel en pasos de media unidad, en línea recta.
  5. En cada paso, aplique int()a las coordenadas x e y. Si el píxel en estas coordenadas es negro, deténgase. De lo contrario, continúe. (Este es un método de línea de visión).
  6. Cualquier píxel alcanzado que bordee un píxel blanco y / o un píxel previamente etiquetado con una distancia significativamente mayor (es decir, +10) se convierte en un píxel inicial.

En un sentido más meta, este algoritmo se extiende a cada píxel accesible en línea recta desde los píxeles iniciales / ya coloreados, luego "pulgadas" alrededor de los bordes. El bit de "distancia significativamente mayor" está destinado a acelerar el algoritmo. Honestamente, realmente no importa cómo implemente la distancia euclidiana, solo tiene que parecerse a esto.

Así es como se ve el primer ejemplo con la distancia euclidiana, usando el algoritmo anterior:

Imagen de entrada y (x,y) = (165,155)

ejemplo 1 entrada ingrese la descripción de la imagen aquí


¡Muchas gracias a Calvin'sHobbies y trichoplax por ayudarnos a escribir este desafío! ¡Que te diviertas!


77
No planeo jugar golf, pero hice una versión de Javascript donde puedes pasar el mouse sobre la imagen y los colores se actualizan al instante. Las imágenes de prueba aquí son demasiado grandes para que se ejecuten rápidamente, por lo que aconsejaría probar imágenes más pequeñas como esta o esta .
Hobbies de Calvin

¡Esto es asombroso! Sospecho que es demasiado eficiente para ser una buena base para una versión de golf =)
error

2
¡Los laberintos son mucho más fáciles de resolver cuando tienen este color!
mbomb007

El último ejemplo es realmente hermoso. ¿La imagen de entrada es solo ruido?
dylnan

@dylnan: Si estás hablando del ejemplo justo antes de la bonificación, en realidad es un laberinto. Puede hacer clic en él para verlo a tamaño completo.
El'endia Starman

Respuestas:


33

Matlab, 255 245 231 bytes

Esto espera el nombre de la imagen primero, luego yy luego x.

I=@input;i=imread(I('','s'));[r,c]=size(i);m=zeros(r,c);m(I(''),I(''))=1;M=m;d=m;K=[1,2,1];for k=1:r*c;d(m&~M)=k;M=m;m=~~conv2(m,K'*K>1,'s');m(~i)=0;end;z=max(d(:));v=[1,1,3];imshow(ind2rgb(d,hsv(z)).*repmat(m,v)+repmat(~d&i,v),[])

Implementé el relleno de inundación (o 'dijkstra para 4 vecindarios' si lo desea) aproximadamente creando primero una máscara donde el píxel inicial se establece en 1 y con un acumulador de distancia (ambos del tamaño de la imagen) y luego repitiendo lo siguiente pasos:

  • convoluciona la máscara con un núcleo de 4 vecindades (esta es la parte muy ineficiente)
  • establecer todos los píxeles distintos de cero de la máscara a 1
  • establecer todos los píxeles negros de la imagen a cero
  • establecer todos los valores en el acumulador donde la máscara ha cambiado en este paso a k
  • incrementar k
  • repita hasta que no haya más cambios en la máscara (en realidad no verifico esta condición, pero solo uso el número de píxeles en la imagen como límite superior, que generalmente es un límite superior muy malo, pero esto es codegolf =)

Esto nos deja con las distancias de Manhattan de cada píxel al píxel inicial en el acumulador de distancia. Luego creamos una nueva imagen yendo a través del rango de colores dado y asignamos el "primer" tono al valor cero y el "último" tono a la distancia máxima.

Ejemplos

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Como beneficio adicional, aquí hay una bonita imagen de cómo se calcula la distancia. más brillante = más lejos.

ingrese la descripción de la imagen aquí


3
Este es el tipo de cosas que me gustaría imprimir para que mi hija las dibuje.
rayryeng - Restablecer Mónica

@rayryeng Las plantillas son obra de El'endia Starman, no mía =)
error

Todavía le pone color a las imágenes: D. Hiciste el último paso.
rayryeng - Restablecer Monica

44
Estoy impresionado. Apenas podía entender el desafío lol
zfrisch

Honestamente, para lo que quiero usarlo es para crear paisajes.
corsiKa

3

Blitz 2D / 3D , 3068 * 0.7 = 2147.6

Esta es la implementación de referencia para el algoritmo euclidiano, golfizado.

image=LoadImage("HueEverywhere_example1.png")
Graphics ImageWidth(image),ImageHeight(image)
image=LoadImage("HueEverywhere_example1.png")
x=0
y=0
w=ImageWidth(image)
h=ImageHeight(image)
Type start
Field x,y
Field dis#
Field nex.start
End Type
Type cell
Field x,y
Field dis#
End Type
Type oldCell
Field x,y
Field dis#
End Type
initCell.start=New start
initCell\x=x
initCell\y=y
initCell\dis=1
Dim array#(w,h)
imgBuff=ImageBuffer(image)
LockBuffer(imgBuff)
s.start=First start
colr=col(0,0,0)
colg=col(0,0,1)
colb=col(0,0,2)
newcol=colr*256*256+colg*256+colb
WritePixelFast(s\x,s\y,newcol,imgBuff)
While s<>Null
c.cell=New cell
c\x=s\x
c\y=s\y
c\dis=s\dis
While c<>Null
For dy=-1To 1
For dx=-1To 1
If dx*dy=0And dx+dy<>0
nx=c\x+dx
ny=c\y+dy
ndis#=s\dis+Sqr#((nx-s\x)*(nx-s\x)+(ny-s\y)*(ny-s\y))
If nx >= 0And nx<w And ny >= 0And ny<h
If KeyHit(1)End
pixcol=ReadPixelFast(nx,ny,imgBuff)
If pixcol<>-16777216
If array(nx,ny)=0Or ndis<array(nx,ny)
check=1
steps=Ceil(dis)*2
For k=0 To steps
r#=k*1./steps
offx#=Int(s\x+(c\x-s\x)*r)
offy#=Int(s\y+(c\y-s\y)*r)
pixcol2=ReadPixelFast(offx,offy,imgBuff)
If pixcol2=-16777216
check=0
Exit
EndIf
Next
If check
array(nx,ny)=ndis
newCell.cell=New cell
newCell\x=nx
newCell\y=ny
newCell\dis=ndis
EndIf
EndIf
EndIf
EndIf
EndIf
Next
Next
o.oldCell=New oldCell
o\x=c\x
o\y=c\y
o\dis=c\dis
Delete c
c=First cell
Wend
For o.oldCell=Each oldCell
bordersWhite=0
For dy=-1To 1
For dx=-1To 1
If dx<>0Or dy<>0
nx=o\x+dx
ny=o\y+dy
If nx>=0And nx<w And ny>=0And ny<h
pixcol=ReadPixelFast(nx,ny,imgBuff)
If (pixcol=-1And array(nx,ny)=0)Or array(nx,ny)>o\dis+9
bordersWhite=1
Exit
EndIf
EndIf
EndIf
Next
If bordersWhite Exit
Next
If bordersWhite
ns.start=New start
ns\x=o\x
ns\y=o\y
ns\dis=o\dis
s2.start=First start
While s2\nex<>Null
If ns\dis<s2\nex\dis
Exit
EndIf
s2=s2\nex
Wend
ns\nex=s2\nex
s2\nex=ns
EndIf
Delete o
Next
EndIf
s2=s
s=s\nex
Delete s2
Wend
maxDis=0
For j=0To h
For i=0To w
If array(i,j)>maxDis maxDis=array(i,j)
Next
Next
For j=0To h
For i=0To w
dis2#=array(i,j)*360./maxDis
If array(i,j) <> 0
colr=col(dis2,0,0)
colg=col(dis2,0,1)
colb=col(dis2,0,2)
newcol=colr*256*256+colg*256+colb
WritePixelFast(i,j,newcol,imgBuff)
EndIf
Next
Next
UnlockBuffer(imgBuff)
DrawImage image,0,0
Function col(ang1#,ang2#,kind)
While ang1>360
ang1=ang1-360
Wend
While ang1<0 
ang1=ang1+360
Wend
While ang2>180
ang2=ang2-360
Wend
While ang2<-180
ang2=ang2+360
Wend
a3#=ang2/180.
If ang1>300
diff#=(ang1-300)/60.
r=255
g=0
b=255*(1-diff)
ElseIf ang1>240
diff#=(ang1-240)/60.
r=255*diff
g=0
b=255
ElseIf ang1>180
diff#=(ang1-180)/60.
r=0
g=255*(1-diff)
b=255
ElseIf ang1>120
diff#=(ang1-120)/60.
r=0
g=255
b=255*diff
ElseIf ang1>60
diff#=(ang1-60)/60.
r=255*(1-diff)
g=255
b=0
Else
diff#=(ang1-00)/60.
r=255
g=255*diff
b=0
EndIf
If a3>0
r2=r+a3*(255-r)
g2=g+a3*(255-g)
b2=b+a3*(255-b)
Else
r2=r+a3*r
g2=g+a3*g
b2=b+a3*b
EndIf
If r2>255
r2=255
ElseIf r2<0
r2=0
EndIf
If g2>255
g2=255
ElseIf g2<0
g2=0
EndIf
If b2>255
b2=255
ElseIf b2<0
b2=0
EndIf
If kind=0
Return r2
ElseIf kind=1
Return g2
ElseIf kind=2
Return b2
Else
Return 0
EndIf
End Function

En realidad, detesto lo ilegible que se compara esto con el original. (Lo cual es, por cierto, 5305 bytes.) En realidad, podría cortar bastantes bytes más usando nombres de variables de un carácter para todo, pero esto ya es bastante ridículo. Y no está ganando en el corto plazo. :PAGS


2

C ++ / SFML: 1271 1235 1226 bytes

-36 bytes gracias a user202729 -9 bytes gracias a Zacharý

#include<SFML\Graphics.hpp>
#include<iostream>
#define V std::vector
#define P e.push_back
#define G(d,a,b,c) case d:return C(a,b,c);
#define FI(r,s)(std::find_if(e.begin(),e.end(),[&a](const T&b){return b==T{a.x+(r),a.y+(s),0};})!=e.end())
using namespace sf;using C=Color;struct T{int x,y,c;bool operator==(const T&a)const{return x==a.x&&y==a.y;}};int max(V<V<int>>&v){int m=INT_MIN;for(auto&a:v)for(auto&b:a)m=b>m?b:m;return m;}C hsv2rgb(int t){int ti=t/60%6;float f=t/60.f-ti,m=(1.f-f)*255,n=f*255;switch(ti){G(0,255,n,0)G(1,m,255,0)G(2,0,255,n)G(3,0,m,255)G(4,n,0,255)G(5,255,0,m)default:throw std::exception();}}void r(Image&a,int x,int y){auto d=a.getSize();V<V<int>>m(d.x,V<int>(d.y));int i=0,j,c=0,t;for(;i<d.y;++i)for(j=0;j<d.x;++j)m[j][i]=a.getPixel(j,i)==C::Black?-1:0;V<T>e{{x,y,1}};while(e.size()){V<T>t=std::move(e);for(auto&a:t){m[a.x][a.y]=a.c;if(a.x>0&&m[a.x-1][a.y]==0&&!FI(-1,0))P({a.x-1,a.y,a.c+1});if(a.y>0&&m[a.x][a.y-1]==0&&!FI(0,-1))P({a.x,a.y-1,a.c+1});if(a.x<m.size()-1&&m[a.x+1][a.y]==0&&!FI(1,0))P({a.x+1,a.y,a.c+1});if(a.y<m[0].size()-1&&m[a.x][a.y+1]==0&&!FI(0,1))P({a.x,a.y+1,a.c+1});}}c=max(m)-1;for(i=0,j;i<d.y;++i)for(j=0;j<d.x;++j)if(m[j][i]>0)a.setPixel(j,i,hsv2rgb(360.f*(m[j][i]-1)/c));}

El sf::Imageparámetro también es la salida (se modificará). Puedes usarlo así:

sf::Image img;
if (!img.loadFromFile(image_filename))
    return -1;

r(img, 0, 0);

if (!img.saveToFile(a_new_image_filename))
    return -2;

El primer parámetro es la entrada (y salida) de la imagen, el segundo y el tercer parámetro son el parámetro xy ydonde necesita comenzar


El caso del interruptor parece tan derrochador que probablemente sería útil una definición de macro ... ¿También es `` at setPixel(j, i,hsv2y FI(xm,ym) (std::find_ifrealmente necesario?
user202729

Puede eliminar el espacio entre G(d,a,b,c)y case d:. Además, el espacio entre case d:y también return C(a,b,c)es innecesario. (b>m?b:m)no requiere paréntesis, y (t/60)%6=> t/60%6por orden de operaciones.
Zacharý

Probablemente también debería cambiar el nombre xmy ymnombres de variable más cortos
Zacharý

Creo que es posible eliminar el espacio entre G(d,a,b,c)y case, FI, ti, y hsv2rgbcada uno puede ser reemplazado con un nombre más corto.
Zacharý

1

C ++, 979969898859848 bytes

#include<cstdio>
#include<cstdlib>
#define K 400
#define L 400
#define M (i*)malloc(sizeof(i))
#define a(C,X,Y)if(C&&b[Y][X].c){t->n=M;t=t->n;b[Y][X].d=d+1;t->n=0;t->c=X;t->d=Y;}
#define A(n,d)case n:d;break;
#define F fgetc(f)
#define W(A,B) for(A=0;A<B;A++){
struct i{int c;int d;int v;i*n;}b[L][K]={0},*h,*t;float m=0;int main(){FILE*f=fopen("d","r+b");int x,y,d=0;W(y,L)W(x,K)b[y][x].c=F<<16|F<<8|F;}}rewind(f);x=165,y=155;h=M;h->c=x;h->d=y;b[y][x].d=d;t=h;while(h){i*p=b[h->d]+h->c;if(p->v)h=h->n;else{p->v=1;x=h->c;y=h->d;d=p->d;m=d>m?d:m;a(x>0,x-1,y)a(x<K-1,x+1,y)a(y>0,x,y-1)a(y<L-1,x,y+1)}}W(y,L)W(x,K)i p=b[y][x];unsigned char n=-1,h=p.d/(m/n),R=h%43*6,Q=n*(n-(n*R>>8))>>8,t=n*(n-(n*(n-R)>>8))>>8,r,g,b;switch(h/43){A(0,n,t,0)A(1,Q,n,0)A(2,0,n,t)A(3,0,Q,n)A(4,t,0,n)A(5,n,0,Q)}d=h?r|g<<8|b<<16:p.c?-1:0;fwrite(&d,1,3,f);}}}
  • Entrada: archivo de datos RGB (contenido en el archivo: d)
  • Salida: archivo de datos RGBA RGB (salida en archivo: d)
  • Ejemplo: convert -depth 8 -size "400x400" test.png d.rgb && mv -f d.rgb d && g ++ -o test main.c && ./test
  • NOTA: el tamaño de la imagen y el inicio se controlan a nivel de origen, si se trata de un problema, agregue 50 bytes o algo así, simplemente no me importó cambiarlo para ser honesto.

No es exactamente un "ungolf" directo, pero este fue un prototipo C que me burlé primero:

#include "stdio.h"
#include "stdlib.h"

struct i{
    unsigned int c;
    int d;
    int v;
}b[400][400]={0};

typedef struct q{
    int x;
    int y;
    struct q *n;
}q;
q *qu;
q *t;
float m=0;
int get_dist(int x, int y)
{
    int d = 0;

}

void flood(int x,int y,int d){
    qu=malloc(sizeof(q));
    qu->x=x;qu->y=y;b[y][x].d=d;
    t=qu;
    while(qu){
        struct i *p = &b[qu->y][qu->x];
        if(p->v){qu=qu->n; continue;}
        p->v=1;x=qu->x;y=qu->y;d=p->d;
        #define a(C,X,Y) if(C&&b[Y][X].c){t->n=malloc(sizeof(q));t=t->n;b[Y][X].d=d+1;t->n=0;t->x=X;t->y=Y;}
        a(x>0,x-1,y);
        a(x<399,x+1,y);
        a(y>0,x,y-1);
        a(y<399,x,y+1);
        m=p->d>m?p->d:m;
    }
}

unsigned int C(int h)
{
    int r=0,g=0,b=0;
    int s=255,v=255;
    unsigned char R, qq, t;

    R = h%43*6; 

    qq = (v * (255 - ((s * R) >> 8))) >> 8;
    t = (v * (255 - ((s * (255 - R)) >> 8))) >> 8;

    switch (h / 43){
        case 0: r = v; g = t; break;
        case 1: r = qq; g = v; break;
        case 2: g = v; b = t; break;
        case 3: g = qq; b = v; break;
        case 4: r = t; b = v; break;
        case 5: r = v; b = qq; break;
    }

    return r|(g<<8)|(b<<16)|255<<24;
}

#define F fgetc(f)
int main()
{
    FILE *f=fopen("d", "r+b");
    for(int y=0; y<400; y++){
        for(int x=0; x<400; x++){
            b[y][x].c = (F<<24)|(F<<16)|(F<<8);
        }
    }
    rewind(f);
    flood(165,155,1);
    m/=255.f;
    for(int y=0; y<400; y++){
        for(int x=0; x<400; x++){
            struct i p = b[y][x];
            unsigned int h = C(p.d/m);
            int o = p.c?-1:255<<24;
            if(p.d)fwrite(&h,4,1,f);
            else fwrite(&o,4,1,f);
        }
    }
}

Muchos conceptos siguen siendo similares, pero ciertamente hay una miríada de pequeños cambios. Para compilar eso como C, debe usar C11 (C99 probablemente funcionará, pero solo lo probé estrictamente en C11).
Disfruté bastante este desafío, gracias por darme la idea de probar algo nuevo :).
Editar: Golf'd un poco mejor.
Edit2: fusioné dos estructuras para que mi estructura de píxeles y mi cola sean iguales, un poco más de abuso de macro y usos redistribuidos de 255, de modo que se pueda definir como -1 al definir una serie de caracteres sin firmar y, por último, eliminar una llamada de función.
Edit3: reutilizó algunas variables más, ajustes de precedencia de operadores y salida convertida a RGB guardando el canal alfa
Edit4: creo que ya terminé con esto, algunos cambios aritméticos de puntero y ligeros ajustes de flujo de control.


0

Python 3 y matplotlib, 251 bytes

from pylab import*
def f(i,p):
    h,w,_=i.shape;o=full((h,w),inf);q=[p+(0,)]
    while q:
        x,y,d=q.pop(0)
        if w>x>=0and h>y>=0and i[y,x,0]:o[y,x]=d;i[y,x]=0;d+=1;q+=[(x-1,y,d),(x+1,y,d),(x,y-1,d),(x,y+1,d)]
    imshow(i);imshow(o,'hsv')

La entrada es una matriz numpy MxNx3 tal como la devuelve la imshow()función matplotlib . La entrada modifica la entrada, por lo que debe copiarse de antemano. Muestra la imagen automáticamente si matplotlib está en modo "interactivo"; de lo contrario, se show()debe agregar una llamada a otros 7 bytes.

La salida se crea mostrando primero la imagen original y luego mostrando la imagen del arco iris sobre ella. Matplotlib trata convenientemente inf y nan como transparentes para que la imagen en blanco y negro se vea a través.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.