Traçando os matizes de uma imagem


17

Carregue uma imagem nesse snippet de pilha e mova o mouse sobre ela. Uma curva preta que segue o ângulo da tonalidade , começando no ponto do cursor, será desenhada:

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

Eu só testei esse snippet no Google Chrome.

Por exemplo, quando o cursor está acima do vermelho, a curva tem uma inclinação de 0 °, mas quando está acima do amarelo, tem uma inclinação de 60 °. A curva continua pelo comprimento especificado, alterando continuamente sua inclinação para corresponder à tonalidade.

Carregue esta imagem e quando você mover o cursor sobre ela, a linha ao redor do cursor deverá fazer uma volta completa no sentido anti-horário:

ângulos de matiz

Este e este são outras imagens nítidas para tentar. (Você precisará salvá-los e carregá-los com o snippet. Eles não podem ser vinculados diretamente devido a restrições de origem cruzada.)

Aqui está uma versão não minificada do snippet:

Desafio

Escreva um programa que faça o que o snippet está fazendo, mas não de maneira interativa. Tire uma imagem e uma coordenada (x, y) nos limites da imagem e um comprimento máximo da curva. Emita a mesma imagem com a curva preta adicionada que segue os ângulos de matiz começando em (x, y) e termina quando atinge o comprimento máximo ou atinge o limite de uma imagem.

Especificamente, inicie a curva em (x, y) e meça o ângulo da tonalidade lá. Vá uma unidade (largura de um pixel) nessa direção, observando que sua nova posição provavelmente não é uma coordenada inteira . Marque outro ponto na curva e mova-se novamente, usando a tonalidade do pixel mais próximo (usando algo como floorou round, não vou verificar isso com precisão). Continue assim até que a curva saia dos limites ou exceda o comprimento máximo. Para finalizar, plote todos os pontos da curva como pixels pretos únicos (novamente, use os pixels mais próximos) sobrepostos na imagem e produza essa nova imagem.

O "ângulo de matiz" é apenas o matiz :

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

Observe que, para valores em escala de cinza que tecnicamente não possuem matiz, isso retorna 0, mas tudo bem.

(Essa fórmula usa a atan2maioria das bibliotecas matemáticas internas. R, G, B são de 0 a 1, não de 0 a 255.)

  • Você pode usar qualquer formato de arquivo de imagem sem perdas comum, bem como quaisquer bibliotecas de imagens.
  • Pegue a entrada do stdin ou da linha de comando ou escreva uma função com argumentos para o nome do arquivo de imagem, xey, e o comprimento máximo.
  • O comprimento máximo e x e y são sempre números inteiros não negativos. Você pode assumir que xey estão dentro do alcance.
  • Salve a imagem de saída com um nome de sua escolha ou simplesmente exiba-a.
  • Sua implementação não precisa corresponder exatamente ao snippet. Alguns pixels em locais ligeiramente diferentes devido a um método de arredondamento / cálculo um pouco diferente são bons. (Em casos caóticos, isso pode levar a curvas muito diferentes, mas, desde que pareçam corretas visualmente, tudo bem.)

Pontuação

O menor envio em bytes vence.


11
O trecho está completamente quebrado no Firefox.
Ypnypn

O snippet também não funciona no Safari. (Desafio legal, porém, +1) #
586 Alex A.

@ Hobbies de Calvin Podemos conversar? chat.stackexchange.com/rooms/22029/...
BrainSteel

Respostas:


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

Copiei a maior parte da configuração e coisas do @sanchises, mas, em vez disso, uso streamlinepara calcular e desenhar o caminho. No entanto, antialias a linha e usa interpolação bilinear, em vez do vizinho mais próximo, conforme especificado.


5

C com SDL, 549 516 bytes

Eu vou começar essa festa! Por alguma razão, tive vontade de tentar jogar golfe hoje à noite. O que vocês fazem é difícil ... Se há uma coisa que eu não vejo neste site, é SDL. Eu posso ter acabado de descobrir o porquê. Esse snippet específico está em conformidade com SDL2 e SDL1.2, mas é atroz. Chamado comof("imagename.bmp", xcoord, ycoord, max_length); . Ele salva um arquivo com o mesmo nome dado nos argumentos. A saída parece ser muito semelhante ao trecho de código do OP, mas "mais confusa". Eu posso tentar consertar isso um pouco mais 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);}

Aqui está tudo desvendado:

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

Devo observar que foi tomado cuidado para torná-lo entre plataformas - por uma questão de honestidade, não foi bom codificá-lo para minha máquina, mesmo que ele corte um número substancial de bytes. Ainda assim, sinto que algumas coisas são supérfluas aqui, e analisarei novamente mais tarde.

EDITAR-------

Afinal, isso é saída gráfica ... vou atualizar com imagens que acho interessantes periodicamente.

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)

Saída de amostra: insira a descrição da imagem aqui

Ligue com f("image.jpg", 400, 400, 300)

Eu desperdicei muitos caracteres para a importação, se alguém tiver sugestões para melhorá-la. Pode não funcionar no 3.0


De uma rápida olhada sqrt(3) -> 3**.5:? Não consigo pensar em nada para as importações.
Sp3000 29/03/2015

Agradável! Isso será útil no futuro.
grovesNL

3

MATLAB, 186 172

O jogo começou ! Chame como t('YourImage.ext',123,456,100'), para uma imagem de qualquer tipo que o MATLAB suporte, iniciando em (x, y) = (123.456) e comprimento máximo 100. As posições iniciais não podem estar exatamente nas bordas direita e inferior (isso me custaria dois bytes), para comece no limite, use algo como x=799.99para começar x=800. A indexação começa em 1, não em 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)

Revisões:

  • Mudou da linha do pixel anterior para o próximo para apenas colocar um ponto em um pixel, pois a linha nunca será maior que um pixel! Ainda usando, linepois esse é o código mais curto que conheço para produzir um pixel.
  • Mudou a cor de preto para azul, usando Copara expandir para Color(o que MATLAB faz automaticamente)
  • Alterei a ordem de calcular a próxima posição e desenhar um ponto, desde que percebi, graças ao @grovesNL, que estava realmente fora dos limites desde que estava verificando os limites depois alterar a posição.
  • Alterado de desenhar linhas para definir matriz rgb como 0 e exibir depois.

Doce de linha preta


Não são x=0ou são y=0posições potencialmente válidas?
grovesNL

Além disso, como são esses 166 bytes?
grovesNL

@grovesNL Desculpe, contei acidentalmente minha versão de teste sem o functioncabeçalho. O Spec não exigiu indexação com base em zero ou em uma base; portanto, eu estou usando a base de MATLAB, então não, x=0ou y=0não é válida neste programa.
Sanchises 29/03

Ah, eu esqueci que o MATLAB era baseado em uma coisa (já faz um tempo). Mas acho que isso pode ser válido x=Xe y=Yválido?
grovesNL

11
Hah! Vencê-lo no seu próprio jogo, minha solução MATLAB é de apenas 135 caracteres!
AJMansfield

1

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

Com espaço em branco:

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

imagem rastreada de tonalidade

Tenho certeza de que poderia diminuir ainda mais isso, mas funciona por enquanto.


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}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.