Verfolgung der Farbtöne eines Bildes

17

Laden Sie ein Bild in dieses Stapel-Snippet und bewegen Sie die Maus darüber. Eine schwarze Kurve, die dem Farbtonwinkel ab dem Cursorpunkt folgt , wird gezeichnet:

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

Ich habe dieses Snippet nur in Google Chrome getestet.

Befindet sich der Cursor beispielsweise über Rot, weist die Kurve eine Neigung von 0 ° auf. Befindet er sich jedoch über Gelb, weist sie eine Neigung von 60 ° auf. Die Kurve wird für die angegebene Länge fortgesetzt, wobei die Steigung kontinuierlich geändert wird, um sie an den Farbton anzupassen.

Laden Sie dieses Bild und wenn Sie den Mauszeiger darüber bewegen, sollte die Linie um den Mauszeiger eine vollständige Drehung gegen den Uhrzeigersinn ausführen:

Farbtonwinkel

Dies und das sind andere nette Bilder zum Ausprobieren. (Sie müssen sie speichern und dann mit dem Snippet laden. Sie können aufgrund herkunftsübergreifender Einschränkungen nicht direkt verknüpft werden.)

Hier ist eine nicht verkleinerte Version des Snippets:

Herausforderung

Schreiben Sie ein Programm, das das tut, was das Snippet tut, nur nicht interaktiv. Nehmen Sie ein Bild und eine (x, y) Koordinate in den Bildgrenzen sowie eine maximale Kurvenlänge auf. Das gleiche Bild mit der hinzugefügten schwarzen Kurve ausgeben, die den Farbtonwinkeln ab (x, y) folgt und endet, wenn die maximale Länge erreicht ist oder eine Bildgrenze erreicht.

Beginnen Sie die Kurve bei (x, y) und messen Sie dort den Farbtonwinkel. Gehen Sie eine Einheit (ein Pixel breit) in diese Richtung und beachten Sie, dass Ihre neue Position höchstwahrscheinlich keine Ganzzahlkoordinate ist . Markieren Sie einen anderen Punkt auf der Kurve und bewegen Sie sich erneut mit dem Farbton vom nächsten Pixel (mit etwas wie flooroder round, ich werde dies nicht genau überprüfen). Fahren Sie so fort, bis die Kurve außerhalb der Grenzen liegt oder die maximale Länge überschreitet. Zeichnen Sie zum Abschluss alle Kurvenpunkte als einzelne schwarze Pixel (verwenden Sie wieder die nächsten Pixel), die über dem Bild liegen, und geben Sie dieses neue Bild aus.

Der "Farbtonwinkel" ist nur der Farbton :

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

Beachten Sie, dass für Graustufenwerte, die technisch gesehen keinen Farbton haben, 0 zurückgegeben wird, dies aber in Ordnung ist.

(Diese Formel verwendet atan2die meisten eingebauten Mathematikbibliotheken. R, G, B liegen zwischen 0 und 1, nicht zwischen 0 und 255.)

  • Sie können jedes gängige verlustfreie Bilddateiformat sowie alle Bildbibliotheken verwenden.
  • Nehmen Sie Eingaben von stdin oder der Befehlszeile entgegen oder schreiben Sie eine Funktion mit Argumenten für den Namen der Bilddatei, x und y und die maximale Länge.
  • Die maximale Länge und x und y sind immer nichtnegative ganze Zahlen. Sie können davon ausgehen, dass sich x und y im Bereich befinden.
  • Speichern Sie das Ausgabebild unter einem Namen Ihrer Wahl oder zeigen Sie es einfach an.
  • Ihre Implementierung muss nicht genau mit dem Snippet übereinstimmen. Ein paar Pixel an leicht unterschiedlichen Stellen aufgrund einer leicht unterschiedlichen Rundungs- / Berechnungsmethode sind in Ordnung. (In chaotischen Fällen kann dies dazu führen, dass Kurven sich stark voneinander unterscheiden. Solange sie jedoch optisch korrekt aussehen, ist dies in Ordnung.)

Wertung

Die kleinste Einsendung in Bytes gewinnt.

Calvins Hobbys
quelle
1
Das Snippet ist in Firefox völlig kaputt.
Ypnypn
Snippet funktioniert auch nicht in Safari. (Coole Herausforderung, +1)
Alex A.
@ Calvins Hobbys Dürfen wir uns unterhalten? chat.stackexchange.com/rooms/22029/…
BrainSteel

Antworten:

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

Kopierte den größten Teil des Setups und ähnliches von @sanchises, aber ich benutze streamlinees stattdessen , um den Pfad zu berechnen und zu zeichnen. Es wird jedoch ein Antialiasing für die Linie durchgeführt und die bilineare Interpolation verwendet, anstatt wie angegeben den nächsten Nachbarn.

AJMansfield
quelle
5

C mit SDL, 549 516 Bytes

Ich werde diese Party beginnen! Aus irgendeinem Grund hatte ich heute Abend Lust, Golf zu spielen. Was ihr tut, ist schwer ... Wenn es eine Sache gibt, die ich auf dieser Site nicht sehe, dann ist es SDL. Vielleicht habe ich gerade herausgefunden, warum. Dieses spezielle Snippet entspricht sowohl SDL2 als auch SDL1.2, ist aber grausam. Genannt wie f("imagename.bmp", xcoord, ycoord, max_length);. Es speichert eine Datei mit demselben Namen wie in den Argumenten angegeben. Die Ausgabe scheint dem Code-Snippet des OP sehr ähnlich zu sein, ist aber "unschärfer". Vielleicht versuche ich das etwas später zu beheben.

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

Hier ist alles enträtselt:

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

Ich sollte beachten, dass darauf geachtet wurde, es plattformübergreifend zu gestalten - im Interesse der Ehrlichkeit fühlte es sich nicht gut an, es für meine Maschine fest zu codieren, selbst wenn eine erhebliche Anzahl von Bytes abgeschnitten wurde. Trotzdem bin ich der Meinung, dass ein paar Dinge hier überflüssig sind, und ich werde es später noch einmal anschauen.

BEARBEITEN-------

Immerhin handelt es sich um eine grafische Ausgabe ... Ich werde diese regelmäßig mit Bildern aktualisieren, die ich interessant finde.

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

Beispielausgabe: Bildbeschreibung hier eingeben

Mit anrufen f("image.jpg", 400, 400, 300)

Ich habe viele Zeichen für den Import verschwendet, wenn jemand Vorschläge zur Verbesserung hat. Funktioniert möglicherweise nicht in 3.0

HainenNL
quelle
Von einem schnellen Blick: sqrt(3) -> 3**.5? Bei den Importen fällt mir allerdings nichts ein.
Sp3000,
Schön! Das wird in Zukunft nützlich sein.
HainenNL
3

MATLAB 186, 172

Das Spiel ist auf ! Rufen Sie als t('YourImage.ext',123,456,100')für ein Bild eines beliebigen Typs auf, das MATLAB unterstützt, beginnend mit (x, y) = (123,456) und maximaler Länge 100. Anfangspositionen können nicht genau am rechten und unteren Rand sein (das würde mich zwei Bytes kosten) fang am rand an, benutze so etwas wie x=799.99anfangen bei x=800. Die Indizierung beginnt bei 1 und nicht bei 0.

Code:

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)

Revisionen:

  • Geändert von der Zeile vom vorherigen zum nächsten Pixel, um nur einen Punkt auf einem Pixel zu platzieren, da die Zeile niemals länger als ein Pixel sein wird! Ich benutze immer noch, lineda dies der kürzeste Code ist, den ich zum Erzeugen eines Pixels kenne.
  • Änderte die Farbe von schwarz nach blau, indem Sie Coauf expandieren Color(was MATLAB automatisch macht)
  • Die Reihenfolge der Berechnung der nächsten Position und des Zeichnens eines Punkts wurde geändert, da ich dank @grovesNL feststellte, dass ich tatsächlich außerhalb der Grenzen zeichnete, da ich nach dem Ändern der Position die Grenzen überprüfte .
  • Geändert von Zeichnen von Linien, um die RGB-Matrix auf 0 zu setzen und danach anzuzeigen.

Schwarze Linie Süßigkeiten

Sanchises
quelle
Sind nicht x=0oder y=0möglicherweise gültige Positionen?
HainenNL
Und wie ist das mit 166 Bytes?
HainenNL
@grovesNL Entschuldigung, ich habe versehentlich meine Testversion ohne den functionHeader gezählt. Spec erforderte keine nullbasierte oder einsbasierte Indizierung, daher verwende ich MATLABs einsbasierte, also nein,x=0 oder y=0ist in diesem Programm nicht gültig.
Sanchises
Ah, ich habe vergessen, dass MATLAB einseitig ist (es ist schon eine Weile her). Aber ich vermute, dass das stattdessen machen x=Xund y=Ygültig sein kann?
HainenNL
1
Hah! Schlage dich in deinem eigenen Spiel, meine MATLAB-Lösung ist nur 135 Zeichen!
AJMansfield
1

Verarbeitung, 323 Zeichen

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

Mit Leerzeichen:

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

Farbton verfolgt Bild

Ich bin mir sicher, dass ich dies weiter verkürzen könnte, aber es funktioniert vorerst.

Kevin Workman
quelle
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
quelle