Tracciare le tonalità di un'immagine


17

Carica un'immagine in questo frammento di stack e sposta il mouse su di essa. Verrà disegnata una curva nera che segue l' angolo di tonalità , a partire dal punto del cursore:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><style>canvas{border:1px solid black;}</style>Load an image: <input type='file' onchange='load(this)'><br><br>Max length <input id='length' type='text' value='300'><br><br><div id='coords'></div><br><canvas id='c' width='100' height='100'>Your browser doesn't support the HTML5 canvas tag.</canvas><script>function load(t){if(t.files&&t.files[0]){var e=new FileReader;e.onload=setupImage,e.readAsDataURL(t.files[0])}}function setupImage(t){function e(t){t.attr("width",img.width),t.attr("height",img.height);var e=t[0].getContext("2d");return e.drawImage(img,0,0),e}img=$("<img>").attr("src",t.target.result)[0],ctx=e($("#c")),ctxRead=e($("<canvas>"))}function findPos(t){var e=0,a=0;if(t.offsetParent){do e+=t.offsetLeft,a+=t.offsetTop;while(t=t.offsetParent);return{x:e,y:a}}return void 0}$("#c").mousemove(function(t){function e(t,e){var a=ctxRead.getImageData(t,e,1,1).data,i=a[0]/255,r=a[1]/255,o=a[2]/255;return Math.atan2(Math.sqrt(3)*(r-o),2*i-r-o)}if("undefined"!=typeof img){var a=findPos(this),i=t.pageX-a.x,r=t.pageY-a.y;$("#coords").html("x = "+i.toString()+", y = "+r.toString());var o=parseInt($("#length").val());if(isNaN(o))return void alert("Bad max length!");for(var n=[i],f=[r],h=0;n[h]>=0&&n[h]<this.width&&f[h]>=0&&f[h]<this.height&&o>h;)n.push(n[h]+Math.cos(e(n[h],f[h]))),f.push(f[h]-Math.sin(e(n[h],f[h]))),h++;ctx.clearRect(0,0,this.width,this.height),ctx.drawImage(img,0,0);for(var h=0;h<n.length;h++)ctx.fillRect(Math.floor(n[h]),Math.floor(f[h]),1,1)}});</script>

Ho testato questo frammento solo su Google Chrome.

Ad esempio, quando il cursore si trova sopra il rosso, la curva ha una pendenza di 0 °, ma quando è sopra il giallo, ha una pendenza di 60 °. La curva continua per la lunghezza specificata, cambiando continuamente la sua pendenza per adattarla alla tonalità.

Carica questa immagine e quando fai scorrere il cursore su di essa, la linea proprio attorno al cursore dovrebbe compiere una svolta completa in senso antiorario:

angoli di tonalità

Questa e questa sono altre immagini pulite da provare. (Dovrai salvarli e caricarli con lo snippet. Non possono essere collegati direttamente a causa di vincoli di origine incrociata.)

Ecco una versione non ridotta dello snippet:

Sfida

Scrivi un programma che fa ciò che fa lo snippet, ma non in modo interattivo. Prendi un'immagine e una coordinata (x, y) nei limiti dell'immagine e una lunghezza massima della curva. Stampa la stessa immagine con la curva nera aggiunta che segue gli angoli di tonalità a partire da (x, y) e termina quando ha raggiunto la lunghezza massima o raggiunge un limite dell'immagine.

In particolare, inizia la curva su (x, y) e misura l'angolo di tonalità lì. Vai di un'unità (una larghezza di un pixel) in quella direzione, notando che la tua nuova posizione probabilmente non è una coordinata intera . Segna un altro punto sulla curva e spostati di nuovo, usando la tonalità del pixel più vicino (usando qualcosa come flooro round, non lo verificherò con precisione). Continua così fino a quando la curva non supera i limiti o supera la lunghezza massima. Per finire, traccia tutti i punti della curva come singoli pixel neri (di nuovo, usa i pixel più vicini) sovrapposti sull'immagine e genera questa nuova immagine.

L '"angolo di tonalità" è solo la tonalità :

hue = atan2(sqrt(3) * (G - B), 2 * R - G - B)

Si noti che per i valori in scala di grigi che tecnicamente non hanno una tonalità, questo restituisce 0, ma va bene.

(Questa formula utilizza la atan2maggior parte delle librerie matematiche integrate. R, G, B sono compresi tra 0 e 1, non tra 0 e 255.)

  • È possibile utilizzare qualsiasi formato di file immagine lossless comune nonché librerie di immagini.
  • Prendi l'input da stdin o dalla riga di comando o scrivi una funzione con argomenti per il nome del file di immagine, x e ye la lunghezza massima.
  • La lunghezza massima e xey sono sempre numeri interi non negativi. Puoi supporre che x e y siano nell'intervallo.
  • Salva l'immagine di output con un nome a tua scelta o semplicemente visualizzala.
  • L'implementazione non deve corrispondere esattamente allo snippet. Alcuni pixel in posizioni leggermente diverse a causa di un metodo di arrotondamento / calcolo leggermente diverso vanno bene. (In casi caotici questo potrebbe portare a curve che finiscono per essere sostanzialmente diverse, ma purché appaiano visivamente corrette, va bene.)

punteggio

Vince il più piccolo invio in byte .


1
Lo snippet è completamente rotto in Firefox.
Ypnypn,

Lo snippet inoltre non funziona in Safari. (Bella sfida però, +1)
Alex A.

@ Hobby di Calvin Possiamo chattare? chat.stackexchange.com/rooms/22029/…
BrainSteel

Risposte:


2

MATLAB, 136

function t(g,x,y,l)
m=imread(g);imshow(m); h=2*pi*rgb2hsv(m);h=h(:,:,1);s=streamline(cos(h),-sin(h),x,y,[1,l]);set(s,'LineW',1,'Co','k');

Ho copiato gran parte dell'installazione e simili da @sanchises, ma invece utilizzo streamlineper calcolare e tracciare il percorso. Fa antialias la linea però e usa l'interpolazione bilineare, piuttosto che il vicino più vicino come specificato.


5

C con SDL, 549 516 byte

Inizierò questa festa! Per qualche motivo, stasera mi è venuta voglia di cimentarmi con il golf. Quello che fate ragazzi è difficile ... Se c'è una cosa che non vedo in questo sito, è SDL. Potrei aver appena scoperto il perché. Questo particolare frammento è conforme sia a SDL2 che a SDL1.2, ma è atroce. Chiamato come f("imagename.bmp", xcoord, ycoord, max_length);. Salva un file con lo stesso nome indicato negli argomenti. L'output sembra essere molto simile allo snippet di codice del PO, ma "fuzzier". Potrei provare a risolverlo un po 'più tardi.

#include"SDL.h"
f(char*C,x,y,m){SDL_Surface*P=SDL_LoadBMP(C);int p=P->pitch,i=P->format->BytesPerPixel,q=0;double X=x,Y=y,f=255,R,G,B,h;Uint8*Q,d=1,r,g,b,k;while(d){Q=P->pixels+y*p+i*x;SDL_GetRGB(i==4?*(Uint32*)Q:i==3?SDL_BYTEORDER==4321?*Q<<16|Q[1]<<8|Q[2]:*Q|Q[1]<<8|Q[2]<<16:i==2?*(Uint16*)Q:*Q,P->format,&r,&g,&b);R=r/f;G=g/f;B=b/f;h=atan2(sqrt(3)*(G-B),2*R-G-B);for(k=0;k<i;k++)Q[k]=0;X+=cos(h);Y-=sin(h);if((int)X-x|(int)Y-y)q++;x=X;y=Y;d=x<0|x>=P->w|y<0|y>=P->h|q>=m?0:1;}SDL_SaveBMP(P,C);SDL_FreeSurface(P);}

Qui è tutto svelato:

#include"SDL.h"
f(char*C,x,y,m){
    SDL_Surface*P=SDL_LoadBMP(C);
    int p=P->pitch,i=P->format->BytesPerPixel,q=0;
    double X=x,Y=y,f=255,R,G,B,h;
    Uint8*Q,d=1,r,g,b,k;
    while(d){
        Q=P->pixels+y*p+i*x;
        SDL_GetRGB(i==4?*(Uint32*)Q:i==3?SDL_BYTEORDER==4321?*Q<<16|Q[1]<<8|Q[2]:*Q|Q[1]<<8|Q[2]<<16:i==2?*(Uint16*)Q:*Q,P->format,&r,&g,&b);
        R=r/f;
        G=g/f;
        B=b/f;
        h=atan2(sqrt(3)*(G-B),2*R-G-B);
        for(k=0;k<i;k++)Q[k]=0;
        X+=cos(h);
        Y-=sin(h);
        if((int)X-x|(int)Y-y)q++;
        x=X;y=Y;
        d=x<0|x>=P->w|y<0|y>=P->h|q>=m?0:1;
    }
    SDL_SaveBMP(P,C);
    SDL_FreeSurface(P);
}

Dovrei notare che è stata presa cura di renderlo multipiattaforma - nell'interesse dell'onestà, non era bello codificarlo per il mio computer, anche se tagliava un numero considerevole di byte. Tuttavia, mi sento come se un paio di cose fossero superflue qui, e lo rivedrò più tardi.

MODIFICARE-------

Questo output grafico IS, dopo tutto ... lo aggiorno con immagini che trovo periodicamente interessanti.

f("HueTest1.bmp", 270, 40, 200);

HueTest1.bmp

f("HueTest2.bmp", 50, 50, 200);

HueTest2.bmp

f("HueTest3.bmp", 400, 400, 300);

HueTest3.bmp


3

Python, 203 172

from scipy.misc import*
def f(i,x,y,l):
 a=imread(i);Y,X,_=a.shape;o=a.copy()
 while(X>x>0<y<Y<[]>l>0):r,g,b=a[y,x]/255.;o[y,x]=0;l-=1;x+=2*r-g-b;y-=3**.5*(g-b);imsave(i,o)

Uscita campione: inserisci qui la descrizione dell'immagine

Chiama con f("image.jpg", 400, 400, 300)

Ho perso molti personaggi per l'importazione, se qualcuno ha suggerimenti per migliorarlo. Potrebbe non funzionare in 3.0


Da una rapida occhiata sqrt(3) -> 3**.5:? Non riesco a pensare a nulla per le importazioni però.
Sp3000,

Ben fatto! Sarà utile in futuro.
GroveNL

3

MATLAB, 186 172

Il gioco è acceso ! Chiama come t('YourImage.ext',123,456,100'), per un'immagine di qualsiasi tipo supportata da MATLAB, a partire da (x, y) = (123.456) e lunghezza massima 100. Le posizioni iniziali non possono essere esattamente ai bordi destro e inferiore (che mi costerebbero due byte), quindi per iniziare dal bordo, usare qualcosa di simile x=799.99a iniziare da x=800. L'indicizzazione inizia da 1, non da 0.

Codice:

function t(g,x,y,l)
m=imread(g);[Y,X,~]=size(m);h=2*pi*rgb2hsv(m);while(x>0&x<X&y>0&y<Y&l)
p=ceil(x);q=ceil(y);m(q,p,:)=0;a=h(q,p);x=x+cos(a);y=y-sin(a);l=l-1;end
imshow(m)

revisioni:

  • Passato dalla linea dal pixel precedente al successivo, basta inserire un punto in un pixel, poiché la linea non sarà mai più lunga di un pixel! Continuo a usare linepoiché questo è il codice più corto che conosco per produrre un pixel.
  • Cambiato il colore dal nero al blu, usando Coper espandere a Color(che MATLAB fa automaticamente)
  • Modificato l'ordine di calcolo della posizione successiva e del disegno di un punto, dato che grazie a @grovesNL mi sono reso conto che in realtà stavo disegnando dei limiti poiché stavo controllando i limiti dopo aver cambiato la posizione.
  • Modificato da disegnare linee per impostare la matrice rgb su 0 e visualizzare dopo.

Caramella linea nera


Non sono x=0o y=0posizioni potenzialmente valide?
GroveNL

Inoltre, come sono 166 byte?
GroveNL

@grovesNL Siamo spiacenti, ho contato per errore la mia versione di prova senza l' functionintestazione. Le specifiche non richiedevano l'indicizzazione in base zero o in base a una, quindi sto usando l'una-base di MATLAB, quindi no, x=0o y=0non è valida in questo programma.
Sanchises,

Ah, ho dimenticato che MATLAB era basato su uno (è stato un po '). Ma suppongo che potrebbe rendere x=Xe y=Yvalido invece?
GroveNL

1
Hah! Batti al tuo stesso gioco, la mia soluzione MATLAB è di soli 135 caratteri!
AJMansfield

1

Elaborazione, 323 caratteri

void h(String f,float x,float y,float m){PImage i=loadImage(f);PImage i2=loadImage(f);while(m>0){color c=i.get((int)x,(int)y);float r=red(c)/255;float g=green(c)/255;float b=blue(c)/255;float h=atan2(sqrt(3)*(g-b),2*r-g-b);float dx=cos(h);float dy=-sin(h);m-=1;x+=dx;y+=dy;i2.set((int)x,(int)y,color(0));}i2.save("o.png");}

Con spazi bianchi:

void h(String f, float x, float y, float m) {
  PImage i = loadImage(f);
  PImage i2 = loadImage(f);

  while (m > 0) {

    color c = i.get((int)x, (int)y);
    float r = red(c)/255;
    float g = green(c)/255;
    float b = blue(c)/255;
    float h = atan2(sqrt(3) * (g - b), 2 * r - g - b);

    float dx = cos(h);
    float dy = -sin(h);

    m-= 1;
    x += dx;
    y += dy;

    i2.set((int)x, (int)y, color(0));
  }

  i2.save("o.png");
}

immagine tracciata tonalità

Sono sicuro che potrei accorciarlo ulteriormente, ma per ora funziona.


0

JavaScript 414

function P(G,x,y,l){
I=new Image()
I.onload=function(){d=document,M=Math,C=d.createElement('canvas')
d.body.appendChild(C)
w=C.width=I.width,h=C.height=I.height,X=C.getContext('2d')
X.drawImage(I,0,0)
D=X.getImageData(0,0,w,h),d=D.data,m=255,i=0
for(;l--;i=(w*~~y+~~x)*4,r=d[i]/m,g=d[i+1]/m,b=d[i+2]/m,H=M.atan2(M.sqrt(3)*(g-b),2*r-g-b),d[i]=d[i+1]=d[i+2]=0,x+=M.cos(H),y-=M.sin(H))
X.putImageData(D,0,0)}
I.src=G}
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.