Square, Circle, Triangle,… Gear?

69

En utilisant Algodoo et Paint, j'ai créé ces six images monochromes 300 × 300 de quatre formes pratiques:

Image 1 Image 2 Image 3 Image 4 Image 5 Image 6

Cette classe d'images a les propriétés suivantes:

  • Ils ont toujours une résolution de 300 × 300 pixels, monochromes (noir et blanc uniquement) et ont exactement quatre régions blanches correspondant à un carré, un cercle, un triangle et un engrenage.
  • Les formes ne se chevauchent ni ne se touchent jamais, pas plus qu'elles ne touchent le bord de l'image ou ne sortent du cadre.
  • Les formes ont toujours la même taille, mais elles peuvent être pivotées et positionnées de n’importe quelle manière.

(Les formes ont également des surfaces égales, bien que, si elles sont tramées de la sorte, leur nombre de pixels ne sera probablement pas exactement équivalent.)

Défi

Ecrivez le programme ou la fonction la plus courte possible qui prend le nom de fichier d'une telle image et transforme tous les pixels blancs ...

  • rouge (255, 0, 0)si elles sont dans le carré.
  • bleu (0, 0, 255)s'ils sont dans le cercle.
  • vert (0, 255, 0)si elles sont dans le triangle.
  • jaune (255, 255, 0)s'ils sont dans l'engrenage.

par exemple

Image 1 en couleur

Détails

Votre programme devrait fonctionner efficacement pour toutes les images d'entrée possibles. (Seules les images monochromatiques 300 × 300 valides seront entrées.) Les six images que j'ai fournies ne sont que des exemples. Vous ne pouvez pas coder en dur leur sortie dans votre programme.

Vous ne pouvez pas utiliser de bibliothèques ou de fonctions de vision par ordinateur, intégrées ou externes. Le but est de faire cela en utilisant vos propres opérations au niveau des pixels. Vous pouvez utiliser des bibliothèques d’images qui vous permettent simplement d’ouvrir et de modifier des images (par exemple, PIL pour Python).

Vous pouvez utiliser n'importe quel format de fichier d'image commun sans perte pour les entrées et les sorties, tant que vous vous en tenez au schéma de couleurs.

Vous pouvez prendre l'image nomfichier en tant qu'argument de fonction, depuis stdin ou depuis la ligne de commande. L'image de sortie peut être enregistrée dans un nouveau fichier, le même fichier, ou simplement affichée.

Notation

La soumission avec le moins d'octets gagne. Je peux tester des soumissions avec des images supplémentaires pour déterminer leur validité.

Les passe-temps de Calvin
la source
Peut-on supposer que l'entrée est en noir et blanc sans anti-aliasing? Sinon, pouvons-nous supprimer l'anti-aliasing des entrées anti-aliasées?
John Dvorak
@ JanDvorak Oui. Par monochromatique, je parle uniquement de noir et blanc, il ne peut donc pas y avoir d'anti-aliasing.
Les passe-temps de Calvin
1
Peut-on exiger un format d'entrée spécifique plus précisément qu'une simple extension de fichier? Notamment, je voudrais une entrée ASCII PBM sans aucun commentaire à l'intérieur.
John Dvorak
12
Alors ... j'essayais de résoudre ça, et j'ai fini avec cette image . Je ne sais pas trop comment, mais bon, ça a l'air chic. : P
Doorknob
2
Je ne veux pas poster ma solution car c'est la même idée que celle d'Ell mais pire. Mais je veux juste dire que c'était un petit défi agréable à faire :)
Chris Burt-Brown

Réponses:

8

J - 246,224 185 octets

load'viewmat'
(viewmat~0,(255*4 3$_2|.#:3720)/:/:@(<([:}.(>./%+/%#)@:(+/&:*:@(-"1)+/%#)@(4$.$.@:=)&>)<"0@~.@,))@(~.@,i.])@(>./**@{.)@((0,(,-)#:>:i.3)&|.)^:_@(*i.@:$)@(0<readimg_jqtide_)

C'était amusant!

J'ai réutilisé la pièce de composants connectés que j'ai utilisée pour le défi "Suis-je dans la plus grande pièce" et ai utilisé le rapport entre la distance moyenne et la distance maximale de tous les points au centre de chaque composant. Je me suis décidé pour cela, car il est invariant à la fois en termes d'échelle et de rotation, et apparemment assez bon pour distinguer les formes données. Le classement de cette valeur de bas en haut me donne le cercle d'ordre, l'engrenage, le carré et le triangle utilisés pour permuter la carte de couleurs.

Affiche le résultat en utilisant l'addon viewmap. Aucune boîte à outils utilisée sauf pour la lecture et la sortie de fichier.

La robustesse ne semble pas être une exigence, cela prend 18 octets. 2 autres espaces inutiles, remplacés &.>par &>in ratioet &.:by &:dcent pour 2 autres octets.

Gain énorme à la fois de brièveté et de performance en computilisant shifting au lieu de cut( ;.). De cette façon, l'image est répliquée et décalée dans les 8 directions au lieu de la numériser avec une fenêtre 3x3.

La idfonction était ridiculement complexe pour ce qu'elle devait faire. Désormais, il attribue les identifiants aux pixels des objets en multipliant l'image par un tableau de nombres uniques, ce qui permet de définir la glycémie à zéro.

Code un peu plus expliqué:

load'viewmat'                                 NB. display only
imnames =: < ;. _2 (0 : 0)
C6IKR.png
DLM3y.png
F1ZDM.png
Oa2O1.png
YZfc6.png
chJFi.png
)

images =: (0<readimg_jqtide_) each imnames    NB. read all images in boxed array

id =: *i.@:$                                  NB. NB. assign one number to each non-background (non-zero) pixel
comp =: (>./ * *@{.)@shift^:_@id              NB. 8 connected neighbor using shift
  shift =: (>,{,~<0 _1 1)&|.                  NB. generate the original, and 8 shifted versions (automatically padding and cropping).
result =: comp each images                    NB. Execute comp verb for each image
col =: (~.@, i. ])                            NB. Color: give each component and BG a separate color.

NB. BG in 0, 0 Get all max distance to center % mean distance to center ratios
ratio  =: (< ([:}.rat@:dcent@getInd &>)  <"0@~.@,)
  getInd =: 4 $. $.@:=                        NB. get indices for component y in array x
  dcent  =: +/&.:*:@(-"1) +/%#                NB. distence from center each point
  rat    =: >./ % +/%#                        NB. ratio from distances

cm=: (255*4 3$_2|.#:3720)                     NB. colormap (except black).
(viewmat~ 0,cm /: /:@ratio )@col each result  NB. for each image, show the result, permuting the colormap according to ratio's

NB. almostgolf this
P1 =: (>./**@{.)@((0,(,-)#:>:i.3)&|.)^:_@(*i.@:$)@(0<readimg_jqtide_) NB. reading till components
P2 =: (<([:}.(>./%+/%#)@:(+/&:*:@(-"1)+/%#)@(4$.$.@:=)&>)<"0@~.@,) NB. recognition: get fraction mean vs max distance to center per component, toss BG.     
P3 =: (viewmat~0,(255*4 3$_2|.#:3720)/:/:@P2)@(~.@,i.])@P1    NB. piece together : permute colormap, display components

NB. seriousgolf
load'viewmat'
f =:(viewmat~0,(255*4 3$_2|.#:3720)/:/:@(<([:}.(>./%+/%#)@:(+/&:*:@(-"1)+/%#)@(4$.$.@:=)&>)<"0@~.@,))@(~.@,i.])@((>./**@{.)@shift^:_)@(*i.@:$)@(0<readimg_jqtide_)
NB. example usage:
f&> imnames NB. do for all images

Celui-ci est un peu long à expliquer en détail, mais fera s'il y a un intérêt.

jpjacobs
la source
Le pixel supérieur droit est garanti être bg. Selon le PO "Les formes ne se chevauchent ni ne se touchent jamais, pas plus qu'elles ne touchent la bordure de l'image ou ne sortent pas des limites."
Dr. belisarius
Merci, c'est utile. (En fait, je voulais dire pixel supérieur gauche, le premier dans le ravel). Cela élimine la détection d'arrière-plan (22 octets).
Jpjacobs
Longueur considérablement réduite et performances accrues :)
jpjacobs
29

Mathematica, 459 392 octets

f=(d=ImageData@Import@#/.{a_,_,_}:>a;(For[a={};b={#&@@d~Position~1},b!={},c=#&@@b;b=Rest@b;d[[##&@@c]]=0;a~AppendTo~c;If[Extract[d,c+#]==1,b=b⋃{c+#}]&/@{e={1,0},-e,e={0,1},-e}];m=1.Mean@a;m=#-m&/@a;n=Count[Partition[Norm/@SortBy[m,ArcTan@@#&],300,1,1],l_/;l[[150]]==Max@l];(d[[##&@@#]]=Round[n^.68])&/@a)&/@Range@4;Image[d/.n_Integer:>{{0,0,0},,{0,1,0},{1,0,0},,,,{1,1,0},{0,0,1}}[[n+1]]])&

Ungolfed:

f = (
 d = ImageData@Import@# /. {a_, _, _} :> a;
 (
    For[a = {}; b = {# & @@ d~Position~1},
     b != {},
     c = # & @@ b;
     b = Rest@b;
     d[[## & @@ c]] = 0;
     a~AppendTo~c;
     If[Extract[d, c + #] == 1, 
        b = b ⋃ {c + #}] & /@ {e = {1, 0}, -e, e = {0, 1}, -e}
     ];
    m = 1. Mean@a; m = # - m & /@ a;
    n = 
     Count[Partition[Norm /@ SortBy[m, ArcTan @@ # &], 300, 1, 1], 
      l_ /; l[[150]] == Max@l];
    (d[[## & @@ #]] = Round[n^.68]) & /@ a
    ) & /@ Range@4;
 Image[d /. 
   n_Integer :> {{0, 0, 0}, , {0, 1, 0}, {1, 0, 0}, , , , {1, 1, 
       0}, {0, 0, 1}}[[n + 1]]]
) &

Je pourrais économiser 6 octets de plus en me transformant m=1.Mean@a;m=#-m&/@a;en m=#-Mean@a&/@a;, mais cela accélère considérablement le temps d'exécution, ce qui est gênant pour les tests. (Notez qu'il s'agit de deux optimisations: extraire le calcul de la Mean@aboucle et utiliser des types symboliques exacts au lieu de nombres à virgule flottante. Il est intéressant de noter que l'utilisation de types exacts est beaucoup plus importante que le calcul de la moyenne à chaque itération.)

C'est donc l'approche numéro trois:

  • Détecter les zones par inondation.
  • Trouvez le centre approximatif de chaque zone en faisant la moyenne de toutes les coordonnées de pixels.
  • Maintenant, pour tous les pixels de la forme, traçons la distance de l'angle vs à ce centre:

    entrez la description de l'image ici

    Le triangle a 3 maxima clairs, le carré 4, l’engrenage 16 et le cercle des tonnes, en raison des fluctuations de repliement autour du rayon constant.

  • Nous trouvons le nombre de maxima en regardant des tranches de 300 pixels (ordonnées par angle) et comptons les tranches où le pixel en position 150est le maximum.
  • Ensuite, nous colorons tous les pixels en fonction du nombre de pics (le cercle est supérieur à 16, et donne généralement environ 20 pics, en raison de la taille des tranches).

Pour mémoire, si j'utilise l'idée d'Ell et que je trie simplement les régions en fonction de la distance la plus grande entre un pixel et le centre, je peux le faire en 342 octets:

f=(d=ImageData@Import@#/.{a_,_,_}:>a;MapIndexed[(d[[##&@@#]]=#&@@#2)&,SortBy[(For[a={};b={#&@@d~Position~1},b!={},c=#&@@b;b=Rest@b;d[[##&@@c]]=0;a~AppendTo~c;If[Extract[d,c+#]==1,b=b⋃{c+#}]&/@{e={1,0},-e,e={0,1},-e}];a)&/@Range@4,(m=Mean@#;Max[1.Norm[#-m]&/@#])&],{2}];Image[d/.n_Integer:>{{0,0,0},{0,0,1},{1,1,0},{1,0,0},{0,1,0}}[[n+1]]])&

Mais je n’ai pas l’intention de rivaliser avec cela, tant que tout le monde utilise ses propres algorithmes originaux, au lieu de jouer avec ceux des autres.

Martin Ender
la source
La solution la plus intéressante!
CSharpie
25

Java, 1204 1132 1087 1076

Juste pour me prouver que je peux le faire.

J'ai inclus les importations juste à côté des déclarations de fonction; ceux-ci devraient être en dehors de la classe pour que cela fonctionne:

import java.awt.*;import java.awt.image.*;import java.io.*;import java.util.*;import javax.imageio.*;

BufferedImage i;Set<Point>Q;void p(String a)throws Exception{i=new BufferedImage(302,302,1);i.getGraphics().drawImage(ImageIO.read(new File(a)),1,1,null);Set<Set<Point>>S=new HashSet<>();for(int y=0;y<300;y++){for(int x=0;x<300;x++){if(!G(x,y)){Point p=new Point(x,y);Q=new HashSet<>();if(!S.stream().anyMatch(s->s.contains(p)))S.add(f(x,y));}}}Object[]o=S.stream().sorted((p,P)->c(p)-c(P)).toArray();s(o[0],255);s(o[1],255<<16);s(o[2],0xFF00);s(o[3],0xFFFF00);ImageIO.write(i.getSubimage(1,1,300,300),"png",new File(a));}boolean G(int x,int y){return i.getRGB(x,y)!=-1;}Set<Point>f(int x,int y){Point p=new Point(x,y);if(!Q.contains(p)&&!G(x,y)){Q.add(p);f(x-1,y);f(x+1,y);f(x,y-1);f(x,y+1);}return Q;}int c(Set<Point>s){return(int)s.stream().filter(p->G(p.x-2,p.y-1)||G(p.x-2,p.y+1)||G(p.x+1,p.y-2)||G(p.x-1,p.y-2)||G(p.x+2,p.y-1)||G(p.x+2,p.y+1)||G(p.x+1,p.y+2)||G(p.x-1,p.y+2)).count();}void s(Object o,int c){((Set<Point>)o).stream().forEach(p->{i.setRGB(p.x,p.y,c);});}

Ungolfed (et runnable; c'est-à-dire ajouté)

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.HashSet;
import java.util.Set;
import javax.imageio.ImageIO;

public class SquareCircleTriangleGear {
    public static void main(String[]args){
        try {
            new SquareCircleTriangleGear().p("filepath");
        } catch (Exception ex) {
        }
    }
    BufferedImage i;
    Set<Point>Q;
    void p(String a)throws Exception{
        i = new BufferedImage(302,302,BufferedImage.TYPE_INT_RGB);
        i.getGraphics().drawImage(ImageIO.read(new File(a)),1,1,null);
        Set<Set<Point>>set=new HashSet<>();
        for(int y=0;y<300;y++){
            for(int x = 0;x<300;x++){
                if(i.getRGB(x,y)==-1){
                    Point p = new Point(x,y);
                    Q=new HashSet<>();
                    if(!set.stream().anyMatch((s)->s.contains(p))){
                        set.add(fill(x,y));
                    }
                }
            }
        }
        Object[]o=set.stream().sorted((p,P)->c(p)-c(P)).toArray();
        s(o[0],0x0000FF);
        s(o[1],0xFF0000);
        s(o[2],0x00FF00);
        s(o[3],0xFFFF00);
        ImageIO.write(i.getSubImage(1,1,300,300), "png", new File(a));
    }
    Set<Point>fill(int x, int y){
        Point p=new Point(x,y);
        if(!Q.contains(p)&&!i.getRGB(x,y)!=-1) {
        Q.add(p);
            fill(x-1,y);
            fill(x+1,y);
            fill(x,y-1);
            fill(x,y+1);
        }
        return Q;
    }
    int c(Set<Point>s){return (int)s.stream().filter(p->isBoundary(p.x,p.y)).count();}
    boolean isBoundary(int x, int y){
        return i.getRGB(x-2,y-1)!=-1||i.getRGB(x-2,y+1)!=-1||i.getRGB(x+1,y-2)!=-1||
               i.getRGB(x-1,y-2)!=-1||i.getRGB(x+2,y-1)!=-1||i.getRGB(x+2,y+1)!=-1||
               i.getRGB(x+1,y+2)!=-1||i.getRGB(x-1,y+2)!=-1;
    }
    void s(Object o,int c){
        ((Set<Point>)o).stream().forEach(p->{i.setRGB(p.x,p.y,c);});
    }
}

Cela fonctionne en itérant sur chaque pixel de l'image et en saturant chaque fois que nous atteignons un "trou". Nous ajoutons chaque résultat d'inondation en tant que Set<Point>a Set. Ensuite, nous déterminons quelle forme est laquelle. Ceci est fait en regardant le nombre de pixels limites de la forme. J'ai défini la limite comme un chevalier qui s'éloigne d'une tuile noire, car cela resterait plus constant entre les rotations et autres. Lorsque nous faisons cela, il devient clair que les formes peuvent être triées selon cette valeur: cercle, carré, triangle, engrenage. Donc, je trie et règle tous les pixels de cette forme à la bonne couleur.

Notez que l'image que je suis en train d'écrire n'est pas extraite directement du fichier, car si je le faisais, Java traiterait l'image en noir et blanc et le remplissage en couleurs ne fonctionnerait pas. Je dois donc créer ma propre image avec TYPE_INT_RGB(ce qui est 1). Notez également que l'image sur laquelle je travaille est 302par 302; ceci afin que l'algorithme de distance de Knight n'ait pas à s'inquiéter d'essayer de lire en dehors des limites de l'image. Je résous cette différence de taille en appelant i.getSubImage(1,1,300,300). Remarque: j'ai peut-être oublié de résoudre ce problème lorsque j'ai téléchargé les images. Dans ce cas, les images font 2 pixels de trop, mais à part cela, elles doivent être correctes.

La fonction écrasera le fichier dont le chemin est passé. Résultats:

entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici entrez la description de l'image ici

Justin
la source
Peut sauvegarder quelques caractères en raccourcissant le nom de la classe ainsi que les arguments de la méthode principale en "a" ou similaire.
Ryan
@Ryan Ceux-ci ne sont pas comptés dans le compte. Je ne compte que les importations + les fonctions, comme le permet la question.
Justin
Je pense que je pourrais peut-être obtenir ceci sous 1000 octets. Il faut y travailler plus tard, quand le temps sera venu.
Justin
20

Python, 571 567 528 octets

À l'instar de la solution de Quincunx, elle commence par remplir chaque forme d'indice compris entre 1 et 4. Elle détermine ensuite l'identité des formes par le rayon de leur cercle de délimitation. Une palette de couleurs est construite en conséquence et l'image est enregistrée en tant qu'image couleur indexée.

EDIT: Manqué le fait que les formes sont garantis pour ne pas toucher la bordure de l'image. C'est plus court alors!

from PIL.Image import*;from numpy import*
I=open(sys.argv[1]).convert("P")
D=list(I.getdata())
W=300;R=range(W*W);N=range(5)
O=[[0,i,array([0,0])]for i in N];n=0
for i in R:
 if D[i]>4:
    n+=1;S=[i]
    while S:
     j=S.pop()
     if D[j]>4:D[j]=n;O[n][0]+=1;O[n][2]+=j%W,j/W;S+=[j+1,j-1,j+W,j-W]
for o in O[1:]:o[2]/=o[0];o[0]=0
for i in R:
 if D[i]:o=O[D[i]];v=(i%W,i/W)-o[2];o[0]=max(o[0],dot(v,v))
O.sort()
C=[0]*5+[255]*3+[0,255,0,0]*2;P=C[:]
for i in N:j=3*O[i][1];P[j:j+3]=C[3*i:3*i+3]
I.putdata(D);I.putpalette(P);I.save("o.png")

Prend un nom de fichier d'entrée sur la ligne de commande et écrit la sortie dans o.png.

Aune
la source
2
Argh, c'est tellement plus simple que ce que j'essaie de faire. +1
Martin Ender
7

Mathematica 225


Mise à jour :

Le PO a décidé que cette approche utilisait des fonctions de vision par ordinateur, elle n'était donc plus dans la course. Je vais le laisser posté cependant. Peut-être que quelqu'un le trouvera intéressant.


f@i_ := (m = MorphologicalComponents[ImageData@i];
Image@Partition[Flatten[(m)] /. 
   Append[ ReplacePart[SortBy[ComponentMeasurements[m, "Circularity"], Last], 
   {{1, 2} -> Yellow, {2, 2} -> Green, {3, 2} -> Red, {4, 2} -> Blue}], 0 -> Black], 
Dimensions[m][[2]]])

ImageData renvoie l'image sous forme de matrice de 0 et de 1.

Flatten convertit cette matrice en liste.

Morphological Componentstrouve les 4 groupes de pixels et attribue un entier distinct, 1, 2, 3, 4 à chaque pixel en fonction du groupe. 0 est réservé pour le fond (noir).

ComponentMeasurements teste la circularité des grappes.

Du plus au moins circulaire sera toujours: le cercle, le carré, le triangle et la vitesse.

ReplacePart remplace chaque entier composant par la couleur RVB correspondante, en utilisant le tri de circularité.

Partition...Dimensions[m][[2]] prend la liste des couleurs de pixels et renvoie une matrice aux mêmes dimensions que l'image d'entrée.

Image convertit la matrice de couleurs de pixels en une image colorée.

contributions

{f[img1],f[img2],f[img3],f[img4]}

les sorties

DavidC
la source
147 caractères:f@i_:=Image[#/.Append[Thread[Ordering[Last/@ComponentMeasurements[#,"Circularity"]]->{Yellow,Green,Red,Blue}],0->Black]]&@MorphologicalComponents@i
Alephalpha
Point mineur: vos couleurs n'ont pas les bonnes valeurs de RVB. Point majeur: je ne suis pas sûr que je considérerais cela comme une non utilisation de bibliothèques ou de fonctions de vision par ordinateur.
Hobbies de Calvin le
La "circularité" est sans doute visuelle; Je verrai ce que je peux faire d'autre. Les couleurs sont toutefois {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}inactives:, où 1 correspond à 255. Aucune bibliothèque n'a été utilisée.
DavidC
@ Calvin'sHobbies La question semble être de savoir si vous MorphologicalComponentssatisfait ou non à vos règles. Une fois que l’on sait à quel groupe appartient chaque pixel, il existe de nombreuses façons, dont un nombre brut de pixels, de déterminer quel chiffre correspond à quel chiffre.
DavidC
Je vais dire que cela enfreint les règles, car il s'agit très probablement d'une fonction de vision par ordinateur et donne à Mathematica un avantage injuste. Je conviens que les couleurs doivent être correctes, mais elles ont clairement l'air dans votre image (le rouge est (255,0,22)lorsque je vérifie dans Paint). Je n'ai pas Mathematica, donc je ne peux pas courir pour m'en assurer.
Hobbies de Calvin le
7

Mathematica, 354 345 314 291 288

Toujours jouer au golf, pourrait être raccourci de quelques personnages supplémentaires, mais les performances deviennent insupportables. Utilise la variance pour identifier des formes:

f=(w=Position[z=ImageData@Import@#,1];r=Nearest;v@x_:=Variance@N[Norm[Mean@x-#]&/@x];Image[Plus@@(ReplacePart[0z/. 0->{0,0,0},#->r[{108,124,196,115}->List@@@{Blue,Red,Green,Yellow},v@#][[1]]]&/@Rest@NestList[(m=r[w=w~Complement~#];FixedPoint[Union@@(m[#,{8,2}]&/@#)&,{#&@@w}])&,{},4])])&

Avec espacement:

f = (w = Position[z = ImageData@Import@#, 1];
     r = Nearest; 
     v@x_ := Variance@N[Norm[Mean@x - #] & /@ x];
     Image[Plus @@ (ReplacePart[ 0 z /. 0 -> {0, 0, 0}, # -> r[{108, 124, 196, 115} -> 
                                              List @@@ {Blue, Red, Green, Yellow}, v@#][[1]]] & /@
     Rest@NestList[(m = r[w = w~ Complement~#];
                   FixedPoint[Union @@ (m[#, {8, 2}] & /@ #) &, {# & @@ w}]) &
                   , {}, 4])]) &

Essai:

s = {"http://i.stack.imgur.com/Oa2O1.png", "http://i.stack.imgur.com/C6IKR.png", 
     "http://i.stack.imgur.com/YZfc6.png", "http://i.stack.imgur.com/F1ZDM.png", 
     "http://i.stack.imgur.com/chJFi.png", "http://i.stack.imgur.com/DLM3y.png"};
Partition[f /@ s, 3] // Grid

Mathematica graphiques

Ici, c'est complètement non-golfé. Va ajouter des explications plus tard:

findOneZone[{universe_List, lastZone_List}] :=
 Module[{newUniverse, proximityFindFunc, seedElement},
  newUniverse = Complement[universe, lastZone];
  proximityFindFunc = Nearest@newUniverse;
  seedElement = {First@newUniverse};
  {newUniverse, FixedPoint[Union @@ (proximityFindFunc[#, {8, 2}] & /@ #) &, seedElement]}]

colorAssign[zone_List] :=
 Module[{
   vlist = {108, 124, 196, 115},
   cols = List @@@ {Blue, Red, Green, Yellow},
   centerVariance},
  centerVariance[x_List] := Variance@N[Norm[Mean@x - #] & /@ x];
  First@Nearest[vlist -> cols, centerVariance@zone]]

colorRules[zones_List] := (# -> colorAssign[#] & /@ zones)

main[urlName_String] := 
 Module[{pixels, FgPixelPositions, rawZones, zones},
  pixels = ImageData@Import@urlName;
  FgPixelPositions = Position[pixels, 1];
  (*fill and separate the regions*)
  rawZones = NestList[findOneZone[#] &, {FgPixelPositions, {}}, 4];
  zones = Rest[rawZones][[All, 2]];
  (*Identify,colorize and render*)
  Image@ReplacePart[ConstantArray[{0, 0, 0}, Dimensions@pixels], 
    colorRules[zones]]]

s = {"http://i.stack.imgur.com/Oa2O1.png"};
main /@ s
Dr. belisarius
la source
2

Python, 579 577 554 514 502 501 octets

Pour chaque forme, la remplit, puis calcule la distance entre le centre de gravité et le point le plus éloigné.

ensuite, la surface réelle de la forme est comparée à la surface d'un triangle, d'un carré, d'un disque ou d'une roue qui aurait la même taille.

importer des maths ; de i , j ] == _ : b ; z + = [ n ]; p dans z ]); C = { 1 : A PIL . Importation d' image *; A , R , _ , I = abs , plage ( 300 ), 255 , ouvert ( sys . Argv [ 1 ]). convertir ( 'P' ); Q = I . load () pour j dans R : pour i dans R : si Q [ 

 
   
   X , Y , s , z , p = 0 , 0 , 0 , [], [( i , j )] tandis que p : 
    a , b = n = p . pop () si non ( Q [ n ]! = _ ou n dans z ): 
     X + = a ; Y + =
   
      + = [( a , b - 1 ), ( a + 1 , b ), ( a , b + 1 ), ( a - 1 , b )]; s + = 1 
   r = max ([ math math . hypot ( X / s - x , Y / s - y ) pour x , y  ( s - ( 1.4 * r ) ** 2 ), 2 : A ( s - r * r / 3 ), 3 : A ( s - math . Pi * r * r ), 4 : A ( s - 2.5 * r * r )} pour p dans z
   : 
    Q [ p ] = min ( C , clé = C . Se ) 
I . putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ]) 
Je . show ()
dieter
la source
1

C # 1086 octets

Encore une autre solution, pour l’information car il n’existe pas de version C # ici. Comme Quincunx, je voulais prouver que je pouvais le faire et que son approche en Java n’était guère différente.

  • Cette solution n'utilise pas de récursivité (mais une pile) car je continuais à courir dans StackOverflows.
  • La détection des bordures de pixels est simplifiée en regardant les 4 pixels suivants. Si l’un de ces pixels est noir, le courant est un pixel de bordure.

Il accepte tous les formats d'image.

  • Paramètre 1 = InputPath
  • Paramètre 2 = OutputPath

On peut probablement réduire quelques caractères en supprimant tous les éléments statiques et en créant une instance de Program.

Version lisible:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;

class Program
{
    static Bitmap img;
    static int w, h;
    static ISet<Point> pointsDone = new HashSet<Point>();
    static void Main(string[] a)
    {
        img = new Bitmap(a[0]);
        w = img.Width;
        h = img.Height;
        Bitmap clone = new Bitmap(w,h, PixelFormat.Format32bppArgb);
        Graphics.FromImage(clone).DrawImage(img, 0, 0, w, h);
        img = clone;




        Color[] colors = new[] { Color.Blue, Color.Red, Color.Green, Color.Yellow };

        var shapes = new List<ISet<Tuple<bool, Point>>>();
        for(int x=0;x<w;x++)
            for (int y = 0; y < h; y++)
            {
                Point p = new Point(x, y);
                if (pointsDone.Add(p) && _isWhitePixel(p))
                    shapes.Add(_detectShape(p));
            }
        int index = 0;
        foreach (var shp in shapes.OrderBy(shp => shp.Count(item => item.Item1)))
        {
            foreach (var pixel in shp)
                img.SetPixel(pixel.Item2.X, pixel.Item2.Y, colors[index]);
            index++;
        }

        img.Save(a[1]);
    }

    private static ISet<Tuple<bool, Point>> _detectShape(Point p)
    {
        var todo = new Stack<Point>(new[] { p });
        var shape = new HashSet<Tuple<bool, Point>>();
        do
        {
            p = todo.Pop();
            var isBorderPixel = false;
            foreach (var n in new[] { new Point(p.X + 1, p.Y), new Point(p.X - 1, p.Y), new Point(p.X, p.Y + 1), new Point(p.X, p.Y - 1) })
                if (_isWhitePixel(n))
                {
                    if (pointsDone.Add(n))
                        todo.Push(n);
                }
                else isBorderPixel = true; // We know we are at the border of the shape
            shape.Add(Tuple.Create(isBorderPixel, p));

        } while (todo.Count > 0);
        return shape;
    }

    static bool _isWhitePixel(Point p)
    {
        return img.GetPixel(p.X, p.Y).ToArgb() == Color.White.ToArgb();
    }
}

Golfé:

using System;using System.Collections.Generic;using System.Drawing;using System.Drawing.Imaging;using System.Linq;class P{static Bitmap a;static int w,h;static ISet<Point> d=new HashSet<Point>();static void Main(string[] q){a=new Bitmap(q[0]);w=a.Width;h=a.Height;var c=new Bitmap(w,h,PixelFormat.Format32bppArgb);Graphics.FromImage(c).DrawImage(a,0,0,w,h);a=c;var e=new[]{Color.Blue,Color.Red,Color.Green,Color.Yellow};var f=new List<ISet<dynamic>>();for(int x=0;x<w;x++)for(int y=0;y<h;y++){Point p=new Point(x,y);if (d.Add(p)&&v(p))f.Add(u(p));}int i=0;foreach(var s in f.OrderBy(s=>s.Count(item=>item.b))){foreach(var x in s)a.SetPixel(x.p.X,x.p.Y,e[i]);i++;}a.Save(q[1]);}private static ISet<dynamic> u(Point p){var t=new Stack<Point>(new[]{p});var s=new HashSet<dynamic>();do{p=t.Pop();var b=false;foreach(var n in new[]{new Point(p.X+1,p.Y),new Point(p.X-1,p.Y),new Point(p.X,p.Y+1),new Point(p.X,p.Y-1)})if(v(n)){if (d.Add(n))t.Push(n);}else b=true;s.Add(new{b,p});}while (t.Count>0);return s;}static bool v(Point p){return a.GetPixel(p.X,p.Y).ToArgb()==Color.White.ToArgb();}}
CSharpie
la source