Śledzenie odcieni obrazu

17

Załaduj obraz do tego fragmentu stosu i przesuń nad nim kursor myszy. Narysowana zostanie czarna krzywa, która podąża za kątem barwy , zaczynając od punktu kursora:

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

Ten fragment kodu przetestowałem tylko w Google Chrome.

Na przykład, gdy kursor znajduje się powyżej czerwonego, krzywa ma nachylenie 0 °, ale gdy jest powyżej żółtego, ma nachylenie 60 °. Krzywa jest kontynuowana dla określonej długości, stale zmieniając jej nachylenie, aby dopasować do odcienia.

Załaduj ten obraz, a kiedy przesuniesz na nim kursor, linia wokół kursora powinna wykonać pełny obrót w lewo:

kąty odcienia

To i to inne fajne obrazy do wypróbowania. (Musisz je zapisać, a następnie załadować za pomocą fragmentu. Nie można ich bezpośrednio połączyć z powodu ograniczeń krzyżowych.)

Oto niezamknięta wersja fragmentu:

Wyzwanie

Napisz program, który robi to, co robi fragment kodu, ale nie interaktywnie. Zrób zdjęcie i współrzędne (x, y) w granicach obrazu oraz maksymalną długość krzywej. Wyprowadź ten sam obraz z dodaną czarną krzywą, która podąża za kątami odcienia, zaczynając od (x, y) i kończy się, gdy osiągnie maksymalną długość lub osiągnie granicę obrazu.

W szczególności rozpocznij krzywą od (x, y) i zmierz tam kąt barwy. Idź o jedną jednostkę (szerokość jednego piksela) w tym kierunku, zauważając, że twoja nowa pozycja najprawdopodobniej nie jest współrzędną całkowitą . Zaznacz kolejny punkt na krzywej i rusz się ponownie, używając odcienia z najbliższego piksela (używając czegoś w rodzaju floorlub round, nie będę tego dokładnie sprawdzać). Kontynuuj w ten sposób, aż krzywa wyjdzie poza granice lub przekroczy maksymalną długość. Aby zakończyć, wykreśl wszystkie punkty krzywej jako pojedyncze czarne piksele (ponownie użyj najbliższych pikseli) nałożone na obraz i wyślij ten nowy obraz.

„Kąt odcienia” to tylko odcień :

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

Zauważ, że dla wartości w skali szarości, które technicznie nie mają odcienia, zwraca 0, ale to jest w porządku.

(Ta formuła wykorzystuje atan2większość wbudowanych bibliotek matematycznych. R, G, B mają wartość od 0 do 1, a nie od 0 do 255.)

  • Możesz użyć dowolnego popularnego formatu plików bezstratnych, a także dowolnych bibliotek obrazów.
  • Weź dane wejściowe ze standardowego wiersza poleceń lub wiersza poleceń albo napisz funkcję z argumentami nazwy pliku obrazu, xiy oraz maksymalnej długości.
  • Maksymalna długość i xiy są zawsze liczbami całkowitymi nieujemnymi. Możesz założyć, że xiy są w zakresie.
  • Zapisz obraz wyjściowy z wybraną nazwą lub po prostu go wyświetl.
  • Twoja implementacja nie musi dokładnie pasować do fragmentu. Kilka pikseli w nieco różnych miejscach ze względu na nieco inną metodę zaokrąglania / obliczania jest w porządku. (W chaotycznych przypadkach może to prowadzić do krzywych, które kończą się zasadniczo inaczej, ale dopóki wyglądają poprawnie wizualnie, jest w porządku).

Punktacja

Najmniejsze przesłanie w bajtach wygrywa.

Hobby Calvina
źródło
1
Fragment ten jest całkowicie uszkodzony w przeglądarce Firefox.
Ypnypn
Urywek również nie działa w przeglądarce Safari. (Fajne wyzwanie, +1)
Alex A.,
@ Hobby Calvina Czy możemy rozmawiać? chat.stackexchange.com/rooms/22029/…
BrainSteel

Odpowiedzi:

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

Skopiowałem większość ustawień i takie z @sanchises, ale zamiast tego używam streamlinedo obliczania i rysowania ścieżki. Wykonuje jednak antyializację linii i używa interpolacji dwuliniowej, a nie określonego najbliższego sąsiada.

AJMansfield
źródło
5

C z SDL, 549 516 bajtów

Zacznę tę imprezę! Z jakiegoś powodu chciałem dziś wieczorem spróbować swoich sił w golfie. To, co robicie, jest trudne ... Jeśli jest jedna rzecz, której nie widzę na tej stronie, to SDL. Być może właśnie dowiedziałem się, dlaczego. Ten konkretny fragment kodu jest zgodny zarówno z SDL2, jak i SDL1.2, ale jest okropny. Nazywa się jak f("imagename.bmp", xcoord, ycoord, max_length);. Zapisuje plik o takiej samej nazwie jak podano w argumentach. Dane wyjściowe wydają się bardzo podobne do fragmentu kodu OP, ale „bardziej skomplikowane”. Może spróbuję to naprawić nieco później.

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

Tutaj wszystko się rozwiązuje:

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

Powinienem zauważyć, że zadbano o to, aby był on wieloplatformowy - w trosce o uczciwość nie było dobrze, aby kodować go na stałe dla mojego komputera, nawet jeśli odciął on znaczną liczbę bajtów. Mimo to wydaje mi się, że kilka rzeczy jest tutaj zbytecznych i przyjrzę się temu później.

EDYTOWAĆ-------

W końcu to wyjście graficzne IS ... Będę aktualizować obrazami, które uważam za interesujące okresowo.

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
źródło
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)

Przykładowe dane wyjściowe: wprowadź opis zdjęcia tutaj

Zadzwoń z f("image.jpg", 400, 400, 300)

Zmarnowałem wiele znaków na import, jeśli ktoś ma sugestie, aby to poprawić. Może nie działać w wersji 3.0

gajeNL
źródło
Z szybkim skrócie: sqrt(3) -> 3**.5? Nie mogę jednak wymyślić niczego dla importu.
Sp3000,
Niezłe! Przyda się to w przyszłości.
gajeNL
3

MATLAB, 186 172

Gra jest włączona ! Call as t('YourImage.ext',123,456,100'), dla obrazu dowolnego typu MATLAB-a, zaczynającego się od (x, y) = (123 456) i maksymalnej długości 100. Pozycje początkowe nie mogą znajdować się dokładnie na prawej i dolnej krawędzi (kosztowałoby mnie to dwa bajty), więc zacznij od krawędzi, użyj czegoś takiego jak x=799.99na początek x=800. Indeksowanie rozpoczyna się od 1, a nie od 0.

Kod:

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)

Rewizje:

  • Zmieniono z linii z poprzedniego na następny piksel na umieszczanie kropki na pikselu, ponieważ linia nigdy nie będzie dłuższa niż piksel! Nadal używam, lineponieważ jest to najkrótszy kod, jaki znam, aby wytworzyć piksel.
  • Zmieniłem kolor z czarnego na niebieski, używając Codo rozwinięcia Color(co MATLAB robi automatycznie)
  • Zmieniono kolejność obliczania następnej pozycji i rysowania punktu, ponieważ dzięki @grovesNL zdałem sobie sprawę, że tak naprawdę rysowałem poza granicami, ponieważ sprawdzałem granice po zmianie pozycji.
  • Zmieniono z rysowania linii na ustawianie macierzy rgb na 0 i wyświetlanie po.

Cukierki z czarnej linii

Sanchises
źródło
Nie są x=0lub y=0potencjalnie ważne pozycje?
gajeNL
Ponadto, jak to 166 bajtów?
gajeNL
@grovesNL Niestety, przypadkowo policzyłem moją wersję testową bez functionnagłówka. Specyfikacja nie wymagała indeksowania zera ani zera, więc używam MATLAB-a, więc nie x=0lub y=0nie jest poprawna w tym programie.
Sanchises
Ach, zapomniałem, że MATLAB był oparty na jednym (minęło trochę czasu). Ale myślę, że może to zrobić x=Xi y=Yzamiast tego ważne?
gajeNL
1
Hah! Pokonałem Cię we własnej grze, moje rozwiązanie MATLAB ma tylko 135 znaków!
AJMansfield
1

Przetwarzanie, 323 znaków

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

Z białymi znakami:

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

prześledzony obraz barwy

Jestem pewien, że mógłbym to jeszcze skrócić, ale na razie działa.

Kevin Workman
źródło
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}
wolfhammer
źródło