Tracer les teintes d'une image

17

Chargez une image dans cet extrait de pile et déplacez votre souris dessus. Une courbe noire qui suit l' angle de teinte , en commençant au point du curseur, sera dessinée:

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

Je n'ai testé cet extrait que dans Google Chrome.

Par exemple, lorsque le curseur est au-dessus du rouge, la courbe a une pente de 0 °, mais lorsqu'elle est au-dessus du jaune, elle a une pente de 60 °. La courbe continue sur la longueur spécifiée, changeant continuellement sa pente pour correspondre à la teinte.

Chargez cette image et lorsque vous déplacez le curseur dessus, la ligne juste autour du curseur doit faire un tour complet dans le sens antihoraire:

angles de teinte

Ceci et ce sont d'autres images soignées à essayer. (Vous devrez les enregistrer, puis les charger avec l'extrait de code. Ils ne peuvent pas être directement liés en raison de contraintes d'origine croisée.)

Voici une version non réduite de l'extrait:

Défi

Écrivez un programme qui fait ce que l'extrait fait, mais pas de manière interactive. Prenez une image et une coordonnée (x, y) dans les limites de l'image et une longueur de courbe maximale. Sortez la même image avec la courbe noire ajoutée qui suit les angles de teinte commençant à (x, y) et se termine lorsqu'elle a atteint la longueur maximale ou atteint une limite d'image.

Plus précisément, commencez la courbe à (x, y) et mesurez l'angle de teinte à cet endroit. Allez d'une unité (largeur d'un pixel) dans cette direction, en notant que votre nouvelle position n'est probablement pas une coordonnée entière . Marquez un autre point sur la courbe et déplacez-vous à nouveau, en utilisant la teinte du pixel le plus proche (en utilisant quelque chose comme floorou round, je ne vérifierai pas cela précisément). Continuez comme ceci jusqu'à ce que la courbe sorte des limites ou dépasse la longueur maximale. Pour terminer, tracez tous les points de la courbe sous la forme de pixels noirs uniques (à nouveau, utilisez les pixels les plus proches) superposés sur l'image, et sortez cette nouvelle image.

"L'angle de teinte" est juste la teinte :

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

Notez que pour les valeurs de niveaux de gris qui n'ont techniquement pas de teinte, cela renvoie 0, mais c'est très bien.

(Cette formule utilise la atan2plupart des bibliothèques mathématiques intégrées. R, G, B sont de 0 à 1, pas de 0 à 255.)

  • Vous pouvez utiliser n'importe quel format de fichier d'image sans perte commun ainsi que toutes les bibliothèques d'images.
  • Prenez l'entrée de stdin ou de la ligne de commande, ou écrivez une fonction avec des arguments pour le nom du fichier image, x et y, et la longueur maximale.
  • La longueur maximale et x et y sont toujours des entiers non négatifs. Vous pouvez supposer que x et y sont dans la plage.
  • Enregistrez l'image de sortie avec un nom de votre choix ou affichez-la simplement.
  • Votre implémentation ne doit pas nécessairement correspondre exactement à l'extrait de code. Quelques pixels à des endroits légèrement différents en raison d'une méthode d'arrondi / de calcul légèrement différente conviennent. (Dans les cas chaotiques, cela pourrait conduire à des courbes qui finissent par être très différentes, mais tant qu'elles semblent correctes visuellement, c'est bien.)

Notation

La plus petite soumission en octets gagne.

Loisirs de Calvin
la source
1
L'extrait est complètement cassé dans Firefox.
Ypnypn
L'extrait ne fonctionne pas non plus dans Safari. (Cool challenge cependant, +1)
Alex A.
@ Loisirs Calvin Pouvons-nous discuter? chat.stackexchange.com/rooms/22029/…
BrainSteel

Réponses:

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 plupart de la configuration et autres de @sanchises, mais j'utilise à la place streamlinepour calculer et tracer le chemin. Cependant, il anticrénalise la ligne et utilise une interpolation bilinéaire, plutôt que le plus proche voisin comme spécifié.

AJMansfield
la source
5

C avec SDL, 549 516 octets

Je vais commencer cette fête! Pour une raison quelconque, j'avais envie de m'essayer au golf ce soir. Ce que vous faites est difficile ... S'il y a une chose que je ne vois pas sur ce site, c'est SDL. Je viens peut-être de découvrir pourquoi. Cet extrait particulier est conforme à la fois à SDL2 et SDL1.2, mais est atroce. Appelé comme f("imagename.bmp", xcoord, ycoord, max_length);. Il enregistre un fichier avec le même nom que celui indiqué dans les arguments. La sortie semble être très similaire à l'extrait de code de l'OP, mais "plus floue". Je vais peut-être essayer de résoudre ce problème un peu plus tard.

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

Ici, tout est démêlé:

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

Je dois noter que le soin a été pris pour le rendre multiplateforme - dans un souci d'honnêteté, il n'était pas bon de le coder en dur pour ma machine, même s'il coupait un nombre substantiel d'octets. Pourtant, j'ai l'impression que certaines choses sont superflues ici, et je vais y revenir plus tard.

ÉDITER-------

Cette sortie graphique EST, après tout ... Je vais mettre à jour avec des images que je trouve périodiquement intéressantes.

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
la source
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)

Exemple de sortie: entrez la description de l'image ici

Appeler avec f("image.jpg", 400, 400, 300)

J'ai perdu beaucoup de caractères pour l'importation, si quelqu'un a des suggestions pour l'améliorer. Peut ne pas fonctionner en 3.0

grovesNL
la source
D'un rapide coup d' œil: sqrt(3) -> 3**.5? Je ne peux penser à rien pour les importations cependant.
Sp3000
Joli! Cela sera utile à l'avenir.
grovesNL
3

MATLAB, 186 172

Le jeu est lancé ! Appelez as t('YourImage.ext',123,456,100'), pour une image de tout type prise en charge par MATLAB, à partir de (x, y) = (123,456) et de la longueur maximale 100. Les positions initiales ne peuvent pas être exactement sur les bords droit et inférieur (cela me coûterait deux octets), donc pour commencer sur le bord, utiliser quelque chose comme x=799.99pour commencer x=800. L'indexation commence à 1, pas à 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)

Révisions:

  • Passé de la ligne du pixel précédent au pixel suivant en plaçant simplement un point sur un pixel, car la ligne ne sera jamais plus longue qu'un pixel! J'utilise toujours linecar c'est le code le plus court que je connaisse pour produire un pixel.
  • Changement de la couleur du noir au bleu, en utilisant Copour développer Color(ce que MATLAB fait automatiquement)
  • Modification de l'ordre de calcul de la position suivante et de dessin d'un point, car j'ai réalisé grâce à @grovesNL que je dessinais réellement hors des limites car je vérifiais les limites après avoir changé la position.
  • Passé du tracé des lignes à la définition de la matrice rgb à 0 et à l'affichage après.

Bonbons ligne noire

Sanchises
la source
Les postes ne sont-ils pas x=0ou y=0potentiellement valides?
grovesNL
De plus, comment sont ces 166 octets?
grovesNL
@grovesNL Désolé, j'ai accidentellement compté ma version de test sans en- functiontête. Spec ne nécessitait pas d'indexation à base zéro ou à base unique, j'utilise donc MATLAB à base unique, donc non, x=0ou y=0n'est pas valide dans ce programme.
Sanchises
Ah, j'ai oublié que MATLAB était à base unique (cela fait longtemps). Mais je suppose que cela peut faire x=Xet y=Yvalable à la place?
grovesNL
1
Hah! Vous battre à votre propre jeu, ma solution MATLAB est à seulement 135 caractères!
AJMansfield
1

Traitement, 323 caractères

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

Avec espace:

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

image tracée par teinte

Je suis sûr que je pourrais encore raccourcir cela, mais cela fonctionne pour l'instant.

Kevin Workman
la source
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}
marteau-de-loup
la source