Arco-íris preto e branco

60

Dada uma imagem que possui apenas pixels preto e branco e um local (x, y) que é um pixel branco, pinte os pixels brancos com base na distância mínima de Manhattan de (x, y) em um caminho que envolva apenas o deslocamento de outros pixels brancos.

A tonalidade dos pixels coloridos deve ser proporcional à sua distância de (x, y), para que o pixel em (x, y) tenha uma tonalidade de 0 ° (vermelho puro) e os pixels mais distantes de (x, y) terá um matiz de 360 ​​° (também vermelho), com os outros matizes se mesclando de maneira uniforme e linear no meio. A saturação e o valor devem ser 100%.

Se um pixel branco não estiver conectado a (x, y) por outros pixels brancos, ele deverá permanecer em branco.

Detalhes

  • A entrada consistirá no nome do arquivo da imagem ou nos dados brutos da imagem, além dos números xey.
  • A imagem de saída pode ser salva em um arquivo ou em bruto para stdout em qualquer formato de arquivo de imagem comum ou simplesmente exibida.
  • O valor x é 0 nos pixels mais à esquerda e aumenta para a direita. O valor y é 0 nos pixels mais altos e aumenta a diminuir. (x, y) estará sempre nos limites da imagem.
  • Programas e funções completos são permitidos.

O código mais curto em bytes vence.

Exemplos

Todas essas imagens foram reduzidas para economizar espaço. Clique neles para ver em tamanho real.

Imagem de entrada:

exemplo 1 entrada

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

exemplo 1 saída A exemplo 1 saída B


Entrada de imagem e saída com (x,y) = (0,0):

exemplo 5 de entrada exemplo 5 entrada A


Entrada de imagem e saída com (x,y) = (600,350):

exemplo 2 entrada exemplo 2 de saída


Entrada de imagem e saída com (x,y) = (0,0):

exemplo 3 de entrada saída exemplo 3


Entrada de imagem e saída com (x,y) = (0,0):

exemplo 4 entrada exemplo 4 output


Bônus opcional de -30%: use a distância euclidiana. Uma sugestão para o seu algoritmo é a seguinte (descrição geral):

  1. Tenha um pixel inicial.
  2. Preenchimento a partir desse pixel.
  3. Para cada pixel atingido no preenchimento de inundação,
  4. Mova do pixel inicial para o pixel em etapas de meia unidade, em uma linha reta.
  5. Em cada etapa, aplique int()às coordenadas x e y. Se o pixel nessas coordenadas for preto, pare. Caso contrário, continue. (Este é um método de linha de visão.)
  6. Qualquer pixel atingido que contorne um pixel branco e / ou um pixel que foi rotulado anteriormente com uma distância significativamente maior (ou seja, +10) se torna um pixel inicial.

Em um sentido mais meta, esse algoritmo se espalha para todos os pixels acessíveis em uma linha reta a partir de pixels iniciais / já coloridos, e então "polegadas" em torno das bordas. O bit "distância significativamente maior" destina-se a acelerar o algoritmo. Honestamente, realmente não importa como você implementa a distância euclidiana, apenas tem que se parecer com isso.

É assim que o primeiro exemplo se parece com a distância euclidiana, usando o algoritmo acima:

Insira a imagem e (x,y) = (165,155)

exemplo 1 entrada insira a descrição da imagem aqui


Muito obrigado ao Calvin'sHobbies e ao trichoplax por ajudar a escrever esse desafio! Diverta-se!

El'endia Starman
fonte
7
Não pretendo jogar golfe, mas criei uma versão Javascript onde você pode passar o mouse sobre a imagem e as cores atualizam instantaneamente. As imagens de teste aqui são grandes demais para serem executadas rapidamente; portanto, aconselho a tentar imagens menores como esta ou esta .
Calvin's Hobbies
Isso é incrível! Eu suspeito que é muito eficiente para ser uma boa base para uma versão golfed =)
flawr
2
Os labirintos são muito mais fáceis de resolver quando são coloridos assim!
mbomb007
O último exemplo é realmente bonito. A imagem de entrada é apenas ruído?
dylnan
@dylnan: Se você está falando sobre o exemplo antes do bônus, na verdade é um labirinto. Você pode clicar nele para vê-lo em tamanho real.
El'endia Starman

Respostas:

33

Matlab, 255 245 231 bytes

Isso espera o nome da imagem primeiro, depois ye depois 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),[])

Eu implementei o preenchimento de inundação (ou 'dijkstra para 4 vizinhanças', se você quiser), primeiro criando uma máscara em que o pixel inicial é definido como 1 e com um acumulador de distância (ambos do tamanho da imagem) e depois repetindo o seguinte passos:

  • enrole a máscara com um núcleo de 4 vizinhanças (esta é a parte muito ineficiente)
  • defina todos os pixels diferentes de zero da máscara para 1
  • defina todos os pixels pretos da imagem para zero
  • defina todos os valores no acumulador em que a máscara foi alterada nesta etapa para k
  • aumentar k
  • repita até que não haja mais alterações na máscara (na verdade, não checo essa condição, mas apenas uso o número de pixels na imagem como limite superior, que geralmente é um limite superior muito ruim, mas isso é codegolf =)

Isso nos deixa com as distâncias de manhattan de cada pixel até o pixel inicial no acumulador de distância. Em seguida, criamos uma nova imagem percorrendo o intervalo de cores especificado e mapeamos a "primeira" tonalidade para o valor zero e a "última" tonalidade para a distância máxima.

Exemplos

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Como um bônus, aqui está uma bela imagem de como a distância é calculada. mais brilhante = mais longe.

insira a descrição da imagem aqui

flawr
fonte
3
Esse é o tipo de coisa que eu gostaria de imprimir para minha filha desenhar.
rayryeng - Reinstala Monica
@rayryeng Os modelos são o trabalho de El'endia Starman, não meu =)
flawr
Você ainda coloca cores nas imagens: D. Você deu o último passo.
rayryeng - Restabelece Monica
4
Estou impressionado. Eu mal conseguia entender o desafio lol
zfrisch
Honestamente, o que eu quero usar é criar paisagens.
corsiKa
3

Blitz 2D / 3D , 3068 * 0,7 = 2147,6

Esta é a implementação de referência para o algoritmo euclidiano, golfed.

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

Na verdade, eu odeio o quão ilegível isso é comparado ao original. (Que são, aliás, 5305 bytes.) Na verdade, eu poderia cortar mais alguns bytes usando nomes de variáveis ​​de um caractere para tudo, mas isso já é bastante ridículo. E não está ganhando tão cedo. : P

El'endia Starman
fonte
2

C ++ / SFML: 1271 1235 1226 bytes

-36 bytes graças a user202729 -9 bytes graças 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));}

O sf::Imageparâmetro também é a saída (será modificado). Você pode usá-lo assim:

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

r(img, 0, 0);

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

O primeiro parâmetro é a entrada (e a saída) da imagem, o segundo e o terceiro parâmetros são o parâmetro xe yonde ele precisa iniciar

HatsuPointerKun
fonte
O caso do switch parece tão inútil que provavelmente uma definição de macro seria útil ... Também é o `` at setPixel(j, i,hsv2e FI(xm,ym) (std::find_ifrealmente necessário?
user202729
Você pode remover o espaço entre G(d,a,b,c)e case d:. Além disso, o espaço entre case d:e também não return C(a,b,c)é necessário. (b>m?b:m)não requer parênteses e (t/60)%6=> t/60%6por ordem das operações.
Zacharý
Você provavelmente deve também mudar o nome xme yma nomes de variáveis mais curtos
Zachary
Eu acho que é possível remover o espaço entre G(d,a,b,c)e case, FI, ti, e hsv2rgbcada um pode ser substituído por um nome mais curto.
Zacharý
1

C ++, 97996989885298 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: arquivo de dados RGB (contido no arquivo: d)
  • Saída: arquivo de dados RGBA RGB (produzido no arquivo: d)
  • Exemplo: converter-profundidade 8 "tamanho" 400x400 "test.png d.rgb && mv -f d.rgb d && g ++ -o teste main.c && ./test
  • OBSERVAÇÃO: o tamanho e o início da imagem são controlados no nível da fonte; se esse for um problema, adicione 50 bytes ou algo assim - simplesmente não me importei de alterá-lo para ser honesto.

Não é exatamente um "ungolf" direto, mas esse foi um protótipo C que eu zombei primeiro:

#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);
        }
    }
}

Muitos conceitos permanecem semelhantes, mas há certamente uma miríade de pequenas mudanças. Para compilar que, como C, você precisa usar o C11 (o C99 provavelmente funcionará, mas eu apenas testei estritamente no C11).
Gostei muito desse desafio, obrigado por me dar a ideia de experimentar algo novo :).
Edit: Golf'd um pouco melhor.
Edit2: Mesclou duas estruturas para que minha estrutura e fila de pixels sejam as mesmas, pouco mais abuso de macro e refluxou os usos de 255, de modo que pode ser definido como -1 ao definir uma série de caracteres não assinados e, por fim, removi uma chamada de função.
Edit3: Reutilizou mais algumas variáveis, ajustes de precedência do operador e saída convertida em RGB, salvando o canal alfa
Edit4: Acho que já acabei com isso agora, algumas alterações aritméticas dos ponteiros e pequenos ajustes no fluxo de controle.

p4plus2
fonte
0

Python 3 e 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')

A entrada é uma matriz numpy MxNx3, conforme retornada pela imshow()função matplotlib . A entrada é modificada pela função e, portanto, deve ser copiada previamente. Ele exibe a imagem automaticamente se o matplotlib estiver no modo "interativo"; caso contrário, uma chamada para show()deve ser adicionada por mais 7 bytes.

A saída é criada exibindo primeiro a imagem original e depois a imagem do arco-íris sobre ela. O Matplotlib trata convenientemente inf e nan como transparentes, para que a imagem em preto e branco seja exibida.

Ryan McCampbell
fonte