Arcobaleni in bianco e nero


60

Data un'immagine che ha solo pixel bianchi e neri e una posizione (x, y) che è un pixel bianco, colora i pixel bianchi in base alla loro minima distanza di Manhattan da (x, y) in un percorso che prevede solo l'attraversamento di altri pixel bianchi.

La tonalità dei pixel colorati deve essere proporzionale alla loro distanza da (x, y), quindi il pixel a (x, y) avrà una tonalità di 0 ° (rosso puro) e i pixel più lontani da (x, y) avrà una tonalità di 360 ° (anche rossa), con le altre tonalità che si fondono in modo continuo e lineare. La saturazione e il valore devono essere entrambi al 100%.

Se un pixel bianco non è collegato a (x, y) attraverso altri pixel bianchi, deve rimanere bianco.

Dettagli

  • L'input consisterà nel nome del file dell'immagine o dei dati non elaborati dell'immagine, più i numeri interi xey.
  • L'immagine di output può essere salvata in un file o reindirizzata allo stdout in qualsiasi formato di file immagine comune o semplicemente visualizzata.
  • Il valore x è 0 sui pixel più a sinistra e aumenta andando a destra. Il valore y è 0 nei pixel più in alto e aumenta andando verso il basso. (x, y) sarà sempre nei limiti dell'immagine.
  • Sono ammessi programmi e funzioni completi.

Vince il codice più breve in byte.

Esempi

Tutte queste immagini sono state ridimensionate per risparmiare spazio. Fare clic per visualizzarli a dimensioni intere.

Immagine di input:

esempio 1 input

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

esempio 1 uscita A esempio 1 uscita B


Immagine di input e output con (x,y) = (0,0):

esempio 5 input esempio 5 input A


Immagine di input e output con (x,y) = (600,350):

esempio 2 input esempio 2 output


Immagine di input e output con (x,y) = (0,0):

esempio 3 input esempio 3 output


Immagine di input e output con (x,y) = (0,0):

esempio 4 input esempio 4 output


Bonus opzionale del 30%: usa la distanza euclidea. Un suggerimento per il tuo algoritmo è il seguente (schema generale):

  1. Avere un pixel iniziale.
  2. Riempi inondando da quel pixel.
  3. Per ogni pixel raggiunto nel riempimento dell'inondazione,
  4. Passa dal pixel iniziale a quel pixel a passi di mezza unità, in linea retta.
  5. Ad ogni passaggio, applicare int()alle coordinate xey. Se il pixel in queste coordinate è nero, fermati. Altrimenti, continua. (Questo è un metodo di linea di vista.)
  6. Qualsiasi pixel raggiunto che confina con un pixel bianco e / o un pixel precedentemente etichettato con una distanza significativamente più alta (ovvero, +10) diventa un pixel iniziale.

In un senso più meta, questo algoritmo si estende a tutti i pixel raggiungibili in linea retta da pixel iniziali / già colorati, quindi "pollici" attorno ai bordi. Il bit "distanza significativamente più alta" ha lo scopo di accelerare l'algoritmo. Onestamente, non importa davvero come si implementa la distanza euclidea, deve solo assomigliare a questo.

Ecco come appare il primo esempio con la distanza euclidea, usando l'algoritmo sopra:

Immagine di input e (x,y) = (165,155)

esempio 1 input inserisci qui la descrizione dell'immagine


Mille grazie a Calvin'sHobbies e trichoplax per aver contribuito a scrivere questa sfida! Divertiti!


7
Non ho intenzione di giocare a golf ma ho creato una versione Javascript in cui è possibile passare il mouse sull'immagine e i colori si aggiornano all'istante. Le immagini di prova qui sono troppo grandi per essere veloci, quindi ti consiglio di provare immagini più piccole come questa o questa .
Hobby di Calvin,

Questo e spettacolare! Ho il sospetto che sia troppo efficiente per essere una buona base per una versione
giocata

2
Le labirinti sono molto più facili da risolvere quando sono colorate in questo modo!
mbomb007,

L'ultimo esempio è davvero bellissimo. L'immagine in ingresso è solo rumore?
dylnan,

@dylnan: se stai parlando dell'esempio prima del bonus, in realtà è un labirinto. Puoi fare clic su di esso per vederlo a dimensione intera.
El'endia Starman,

Risposte:


33

Matlab, 255 245 231 byte

Questo si aspetta prima il nome dell'immagine, poi ye poi 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),[])

Ho implementato il riempimento di inondazioni (o 'dijkstra per 4 quartieri' se lo desideri) all'incirca creando prima una maschera in cui il pixel di seme è impostato su 1 e con un accumulatore di distanza (entrambe le dimensioni dell'immagine) e quindi ripetendo passaggi:

  • contorta la maschera con un kernel di vicinato 4 (questa è la parte molto inefficiente)
  • imposta tutti i pixel diversi da zero della maschera su 1
  • imposta tutti i pixel neri dell'immagine su zero
  • impostare tutti i valori nell'accumulatore in cui la maschera è stata modificata in questo passaggio su k
  • aumentare k
  • ripetere fino a quando non ci sono più modifiche nella maschera (in realtà non controllo questa condizione, ma uso solo il numero di pixel nell'immagine come limite superiore, che di solito è un limite superiore molto cattivo, ma questo è codegolf =)

Questo ci lascia con le distanze di manhattan di ogni pixel dal pixel di seme nell'accumulatore di distanza. Quindi creiamo una nuova immagine andando attraverso la gamma di colori specificata e mappando la "prima" tonalità al valore zero e l '"ultima" tonalità alla distanza massima.

Esempi

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Come bonus, ecco una bella immagine di come viene calcolata la distanza. più luminoso = più lontano.

inserisci qui la descrizione dell'immagine


3
Questo è il tipo di cose che vorrei stampare per mia figlia su cui attingere.
rayryeng - Ripristina Monica il

@rayryeng I modelli sono opera di El'endia Starman, non mia =)
flawr

Metti ancora colore nelle immagini: D. Hai fatto l'ultimo passo.
rayryeng - Ripristina Monica il

4
Sono impressionato.
Riuscivo a

Onestamente, quello per cui voglio usarlo è creare paesaggi.
corsiKa

3

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

Questa è l'implementazione di riferimento per l'algoritmo euclideo, golfizzato.

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

In realtà, odio quanto sia illeggibile rispetto all'originale. (Che è, per inciso, 5305 byte.) In realtà, potrei tagliare qualche altro byte usando nomi di variabili di un carattere per tutto, ma questo è già abbastanza ridicolo. E non vincerà presto. : P


2

C ++ / SFML: 1271 1235 1226 byte

-36 byte grazie all'utente202729 -9 byte grazie 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));}

Il sf::Imageparametro è anche l'output (verrà modificato). Puoi usarlo così:

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

r(img, 0, 0);

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

Il primo parametro è l'input (e l'output) dell'immagine, il secondo e il terzo parametro sono il parametro xe da ydove deve iniziare


Il caso switch sembra così dispendioso che probabilmente una definizione macro sarebbe utile ... Inoltre è `` at setPixel(j, i,hsv2e FI(xm,ym) (std::find_ifdavvero necessario?
user202729

È possibile rimuovere lo spazio tra G(d,a,b,c)e case d:. Inoltre, anche lo spazio tra case d:e return C(a,b,c)non è necessario. (b>m?b:m)non richiede le parentesi e (t/60)%6=> t/60%6per ordine delle operazioni.
Zacharý,

Probabilmente dovresti anche rinominare xme ymabbreviare i nomi delle variabili
Zacharý

Penso che sia possibile rimuovere lo spazio tra G(d,a,b,c)e case, FI, ti, e hsv2rgbpossono essere tutti sostituiti con un nome più breve.
Zacharý,

1

C ++, 979 969 898 859 848 byte

#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);}}}
  • Input: file di dati RGB (contenuto nel file: d)
  • Output: file di dati RGBA RGB (emesso nel file: d)
  • Esempio: convert -depth 8 -size "400x400" test.png d.rgb && mv -f d.rgb d && g ++ -o test main.c && ./test
  • NOTA: la dimensione e l'avvio dell'immagine sono controllati a livello di sorgente, se questo è un problema aggiungere 50 byte o qualcosa del genere - non mi importava di cambiarlo per essere onesti.

Non esattamente un "ungolf" diretto ma questo era un prototipo C che ho preso per primo:

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

Molti concetti rimangono simili, ma ci sono certamente una miriade di piccoli cambiamenti. Per compilarlo come C devi usare C11 (probabilmente C99 funzionerà, ma ho testato rigorosamente solo in C11).
Mi è piaciuta molto questa sfida, grazie per avermi dato l'idea di provare qualcosa di nuovo :).
Modifica: Golf sarebbe un po 'meglio.
Edit2: ho unito due strutture in modo che la mia struttura e coda di pixel siano uguali, un po 'più di abuso di macro e usi ridistribuiti di 255 in modo tale che possa essere definito come -1 durante la definizione di una serie di caratteri non firmati e infine rimosso una chiamata di funzione.
Edit3: riutilizzato alcune altre variabili, modifiche alla precedenza dell'operatore e output convertito in RGB salvando il canale alfa
Edit4: Penso di aver finito con questo ora, alcune modifiche aritmetiche del puntatore e lievi modifiche al flusso di controllo.


0

Python 3 e matplotlib, 251 byte

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')

L'input è un array numpy MxNx3 restituito dalla imshow()funzione matplotlib . L'input viene modificato dalla funzione, quindi dovrebbe essere copiato in anticipo. Visualizza automaticamente l'immagine se matplotlib è in modalità "interattiva"; in caso contrario, è show()necessario aggiungere una chiamata a per altri 7 byte.

L'output viene creato visualizzando prima l'immagine originale e quindi visualizzando l'immagine arcobaleno sopra di essa. Matplotlib tratta convenientemente inf e nan come trasparenti in modo da mostrare l'immagine in bianco e nero.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.