Rastreando los tonos de una imagen

17

Cargue una imagen en este fragmento de pila y mueva el mouse sobre él. Se dibujará una curva negra que sigue el ángulo de tono , comenzando en el punto del cursor:

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

Solo probé este fragmento en Google Chrome.

Por ejemplo, cuando el cursor está por encima de rojo, la curva tiene una pendiente de 0 °, pero cuando está por encima de amarillo, tiene una pendiente de 60 °. La curva continúa durante la longitud especificada, cambiando continuamente su pendiente para que coincida con el tono.

Cargue esta imagen y cuando desplace el cursor sobre ella, la línea justo alrededor del cursor debe hacer un giro completo en sentido antihorario:

ángulos de tono

Esta y estas son otras imágenes interesantes para probar. (Deberá guardarlos y luego cargarlos con el fragmento. No se pueden vincular directamente debido a restricciones de origen cruzado).

Aquí hay una versión no minificada del fragmento:

Desafío

Escriba un programa que haga lo que está haciendo el fragmento, pero no de forma interactiva. Tome una imagen y una coordenada (x, y) en los límites de la imagen, y una longitud máxima de la curva. Imprima la misma imagen con la curva negra agregada que sigue los ángulos de tono que comienzan en (x, y) y finaliza cuando alcanza la longitud máxima o alcanza un límite de imagen.

Específicamente, comience la curva en (x, y) y mida el ángulo del tono allí. Vaya una unidad (el ancho de un píxel) en esa dirección, observando que su nueva posición probablemente no sea una coordenada entera . Marque otro punto en la curva y muévase nuevamente, usando el tono del píxel más cercano (usando algo como flooro round, no lo comprobaré con precisión). Continúe así hasta que la curva salga de los límites o exceda la longitud máxima. Para finalizar, trace todos los puntos de la curva como píxeles negros individuales (nuevamente, use los píxeles más cercanos) superpuestos en la imagen y envíe esta nueva imagen.

El "ángulo de matiz" es solo el matiz :

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

Tenga en cuenta que para los valores de escala de grises que técnicamente no tienen un tono, esto devuelve 0, pero está bien.

(Esta fórmula utiliza atan2la mayoría de las bibliotecas matemáticas incorporadas. R, G, B son de 0 a 1, no de 0 a 255).

  • Puede utilizar cualquier formato de archivo de imagen sin pérdida común, así como cualquier biblioteca de imágenes.
  • Tome la entrada de stdin o la línea de comando, o escriba una función con argumentos para el nombre del archivo de imagen, x e y, y la longitud máxima.
  • La longitud máxima y x e y son siempre enteros no negativos. Puede suponer que x e y están dentro del rango.
  • Guarde la imagen de salida con un nombre de su elección o simplemente muéstrela.
  • Su implementación no tiene que coincidir exactamente con el fragmento. Unos pocos píxeles en lugares ligeramente diferentes debido a un método de redondeo / cálculo ligeramente diferente está bien. (En casos caóticos, esto podría conducir a curvas que terminan siendo muy diferentes, pero siempre y cuando se vean correctamente visualmente, está bien).

Puntuación

La presentación más pequeña en bytes gana.

Pasatiempos de Calvin
fuente
1
El fragmento está completamente roto en Firefox.
Ypnypn
Snippet tampoco funciona en Safari. (Genial desafío, sin embargo, +1)
Alex A.
@ Pasatiempos de Calvin ¿Podemos chatear? chat.stackexchange.com/rooms/22029/…
BrainSteel

Respuestas:

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

Copié la mayor parte de la configuración y similares de @sanchises, pero en su lugar uso streamlinepara calcular y dibujar la ruta. Sin embargo, hace antialias en la línea y usa interpolación bilineal, en lugar de la vecina más cercana como se especifica.

AJMansfield
fuente
5

C con SDL, 549 516 bytes

¡Comenzaré esta fiesta! Por alguna razón, tuve ganas de probar suerte en el golf esta noche. Lo que ustedes hacen es difícil ... Si hay algo que no veo en este sitio, es SDL. Puede que haya descubierto por qué. Este fragmento en particular cumple con SDL2 y SDL1.2, pero es atroz. Llamado así f("imagename.bmp", xcoord, ycoord, max_length);. Guarda un archivo con el mismo nombre que se da en los argumentos. La salida parece ser muy similar al fragmento de código del OP, pero "más difusa". Puedo tratar de arreglar esto un poco más tarde.

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

Aquí está todo desvelado:

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

Debo tener en cuenta que se tomó la precaución de hacerlo multiplataforma: en aras de la honestidad, no me sentí bien codificarlo para mi máquina, incluso si cortaba una cantidad sustancial de bytes. Aún así, siento que un par de cosas son superfluas aquí, y lo volveré a ver más tarde.

EDITAR-------

Esta es una salida gráfica, después de todo ... actualizaré con imágenes que encuentro interesantes periódicamente.

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

HueTest1.bmp

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

HueTest2.bmp

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

HueTest3.bmp

BrainSteel
fuente
3

Pitón, 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)

Salida de muestra: ingrese la descripción de la imagen aquí

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

He desperdiciado muchos caracteres para la importación, si alguien tiene sugerencias para mejorarlo. Puede no funcionar en 3.0

arboledasNL
fuente
De un vistazo rápido sqrt(3) -> 3**.5:? Sin embargo, no puedo pensar en nada para las importaciones.
Sp3000
¡Buena esa! Eso será útil en el futuro.
grovesNL
3

MATLAB, 186 172

El juego está en marcha! Llame como t('YourImage.ext',123,456,100'), para obtener una imagen de cualquier tipo que MATLAB admita, comenzando en (x, y) = (123,456) y longitud máxima 100. Las posiciones iniciales no pueden ser exactamente en los bordes derecho e inferior (eso me costaría dos bytes), por lo que comenzar en el borde, usar algo como x=799.99comenzar en x=800. La indexación comienza en 1, no en 0.

Código:

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)

Revisiones

  • ¡Cambió de la línea del píxel anterior al siguiente a solo poner un punto en un píxel, ya que la línea nunca será más larga que un píxel! Todavía lo uso lineporque ese es el código más corto que conozco para producir un píxel.
  • Cambió el color de negro a azul, usando Copara expandir a Color(lo que MATLAB hace automáticamente)
  • Se cambió el orden de cálculo de la siguiente posición y se dibujó un punto, ya que gracias a @grovesNL me di cuenta de que en realidad estaba saliendo de los límites ya que estaba revisando los límites después de cambiar la posición.
  • Cambió de dibujar líneas a establecer matriz rgb a 0 y mostrar después.

Línea negra de caramelo

Sanchises
fuente
¿No son x=0o son y=0posiciones potencialmente válidas?
grovesNL
Además, ¿cómo es esto 166 bytes?
grovesNL
@grovesNL Lo siento, accidentalmente conté mi versión de prueba sin el functionencabezado. Las especificaciones no requerían una indexación basada en cero o en una, por lo que estoy usando MATLAB de una base, por lo que no, x=0o y=0no es válido en este programa.
Sanchises
Ah, olvidé que MATLAB estaba basado en uno (ha pasado un tiempo). Pero supongo que puede hacer x=Xy y=Yválido en su lugar?
grovesNL
1
Ja! ¡Derrota en tu propio juego, mi solución MATLAB es solo 135 caracteres!
AJMansfield
1

Procesamiento, 323 caracteres

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 espacios en blanco:

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

imagen trazada del tono

Estoy seguro de que podría acortar esto aún más, pero funciona por ahora.

Kevin Workman
fuente
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}
martillo de lobo
fuente