Square, Circle, Triangle, ... Gear?


69

Usando Algodoo e Paint ho realizzato queste sei immagini monocromatiche 300 × 300 di quattro forme convenienti:

Immagine 1 Immagine 2 Immagine 3 Immagine 4 Immagine 5 Immagine 6

Questa classe di immagini ha le seguenti proprietà:

  • Sono sempre 300 × 300 pixel, monocromatici (solo bianco e nero) e hanno esattamente quattro regioni bianche che corrispondono a un quadrato, un cerchio, un triangolo e un ingranaggio.
  • Le forme non si sovrappongono e non si toccano, né toccano il bordo dell'immagine né vanno oltre i limiti.
  • Le forme hanno sempre le stesse dimensioni, ma possono essere ruotate e posizionate in qualsiasi modo.

(Le forme hanno anche aree uguali, anche se quando rasterizzate in questo modo è improbabile che il loro numero di pixel sia esattamente equivalente.)

Sfida

Scrivi il programma o la funzione più breve possibile che contiene il nome file di tale immagine e trasforma tutti i pixel bianchi ...

  • rosso (255, 0, 0)se sono nella piazza.
  • blu (0, 0, 255)se sono nel cerchio.
  • verde (0, 255, 0)se sono nel triangolo.
  • giallo (255, 255, 0)se sono nella marcia.

per esempio

Immagine 1 colorata in

Dettagli

Il programma dovrebbe funzionare in modo efficace per tutte le possibili immagini di input. (Verranno immesse solo immagini monocromatiche valide 300 × 300). Le sei immagini che ho fornito sono solo esempi, è possibile che non sia possibile codificare il loro output nel programma.

Non è possibile utilizzare librerie o funzioni di computer vision, integrate o esterne. Il punto è farlo usando le tue operazioni a livello di pixel. È possibile utilizzare librerie di immagini che consentono semplicemente di aprire e modificare le immagini (ad esempio PIL per Python).

È possibile utilizzare qualsiasi formato di file immagine senza perdita comune per l'input e l'output purché si aderisca alla combinazione di colori.

È possibile includere il nome file dell'immagine come argomento di funzione, da stdin o dalla riga di comando. L'immagine di output può essere salvata in un nuovo file, nello stesso file o semplicemente visualizzata.

punteggio

Vince l'invio con il minor numero di byte . Posso testare gli invii con immagini aggiuntive per determinarne la validità.


Possiamo supporre che l'input sia in bianco e nero senza anti-aliasing? In caso contrario, possiamo rimuovere l'antialiasing dagli input antialiasing?
John Dvorak,

@JanDvorak Sì. Per monocromatico intendo solo bianco e nero, quindi non può esserci l'antialiasing.
Calvin's Hobbies,

1
Potremmo richiedere un formato di input specifico più precisamente di una semplice estensione di file? Vale a dire, mi piacerebbe un input PBM ASCII senza commenti all'interno.
John Dvorak,

12
Quindi ... stavo cercando di risolvere questo, e ho finito con questa immagine . Non so davvero come, ma ehi, sembra elegante. : P
Maniglia della porta

2
Non voglio pubblicare la mia soluzione perché è la stessa idea di Ell ma peggio. Ma voglio solo dire che è stata una piccola sfida divertente da fare :)
Chris Burt-Brown,

Risposte:


8

J - 246,224 185 byte

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

Questo è stato divertente!

Ho riutilizzato la parte dei componenti collegati che ho usato per la sfida "Sono nella stanza più grande" e ho usato il rapporto tra la distanza media e massima di tutti i punti dal centro di ciascun componente. Ho optato per questo, poiché è invariante sia in scala che in rotazione, e apparentemente abbastanza buono da distinguere tra le forme date. La classificazione di questo valore dal più basso al più alto mi dà il cerchio dell'ordine, l'ingranaggio, il quadrato e il triangolo, usati per permutare la mappa dei colori.

Visualizza il risultato usando l'addon viewmap. Nessun toolbox utilizzato tranne la lettura e l'output dei file.

La robustezza non sembra essere un requisito, questo toglie 18 byte. Altri 2 spazi non necessari, sostituiti &.>da &>in ratioe &.:da &:in dcent per altri 2 byte.

Enorme guadagno in termini di brevità e prestazioni compdell'uso del cambio invece di cut( ;.). In questo modo l'immagine viene replicata e spostata in tutte e 8 le direzioni invece di scansionarla con una finestra 3x3.

La idfunzione era ridicolmente complessa per ciò che doveva fare. Ora assegna gli ID ai pixel negli oggetti moltiplicando l'immagine con una matrice di numeri univoci, quindi impostando la BG su zero.

Codice un po 'più spiegato:

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

Questo è un po 'lungo da spiegare in dettaglio, ma lo farà se c'è interesse.


Il pixel in alto a destra è garantito per essere bg. Secondo l'OP "Le forme non si sovrappongono né si toccano, né toccano il bordo dell'immagine né vanno oltre i limiti".
Dr. belisarius,

Grazie, è utile. (in realtà intendevo pixel superiore sinistro, il primo nel ravel). Ciò elimina il rilevamento dello sfondo (22 byte).
jpjacobs,

Lunghezza drasticamente ridotta e aumento delle prestazioni :)
jpjacobs,

29

Mathematica, 459 392 byte

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]]]
) &

Potrei risparmiare 6 byte in più trasformandomi m=1.Mean@a;m=#-m&/@a;in m=#-Mean@a&/@a;, ma ciò fa esplodere in modo significativo i tempi di esecuzione, il che è fastidioso per i test. (Si noti che si tratta di due ottimizzazioni: estrarre il calcolo di Mean@aout of loop e utilizzare tipi simbolici esatti invece di numeri in virgola mobile. È interessante notare che l'uso di tipi esatti è molto più significativo rispetto al calcolo della media in ogni iterazione.)

Quindi questo è l'approccio numero tre:

  • Rileva aree riempiendo le inondazioni.
  • Trova il centro approssimativo di ciascuna area facendo la media di tutte le coordinate dei pixel.
  • Ora per tutti i pixel nella forma tracciamo la distanza dall'angolo vs al centro:

    inserisci qui la descrizione dell'immagine

    Il triangolo ha 3 massimi chiari, il quadrato 4, l'ingranaggio 16 e il cerchio ha tonnellate, a causa delle fluttuazioni di aliasing intorno al raggio costante.

  • Troviamo il numero di massimi osservando le sezioni di 300 pixel (ordinate per angolo) e contiamo le sezioni in cui il pixel nella posizione 150è il massimo.
  • Quindi coloriamo semplicemente tutti i pixel a seconda del numero di picchi (il cerchio è superiore a 16 e di solito produce circa 20 picchi, a causa della dimensione delle sezioni).

Solo per la cronaca, se uso l'idea di Ell e semplicemente ordino le regioni in base alla distanza maggiore tra qualsiasi pixel e centro, posso farlo in 342 byte:

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]]])&

Ma non intendo competere con quello, purché tutti gli altri utilizzino i propri algoritmi originali, invece di giocare a golf con quelli degli altri.


La soluzione più interessante!
CSharpie,

25

Java, 1204 1132 1087 1076

Solo per dimostrare a me stesso che io possa fare questo.

Ho incluso le importazioni proprio accanto alle dichiarazioni delle funzioni; questi dovrebbero essere al di fuori della classe perché questo funzioni:

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 (e runnable; ovvero boilerplate aggiunto):

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

Questo funziona ripetendo ogni pixel dell'immagine e riempiendo ogni volta che raggiungiamo un "buco". Aggiungiamo ogni risultato di riempimento dell'inondazione come Set<Point>a a Set. Quindi determiniamo quale forma è quale. Questo viene fatto osservando il numero di pixel al contorno della forma. Ho definito il confine come un cavaliere che si allontana da una tessera nera, poiché rimarrebbe più costante tra le rotazioni e simili. Quando lo facciamo, diventa chiaro che le forme possono essere ordinate per quel valore: Cerchio, Quadrato, Triangolo, Ingranaggio. Quindi ordino e imposto tutti i pixel di quella forma sul colore corretto.

Nota che l'immagine a cui sto scrivendo non è presa direttamente dal file, perché se dovessi farlo, Java tratterà l'immagine come in bianco e nero e il riempimento con i colori non funzionerebbe. Quindi devo creare la mia immagine con TYPE_INT_RGB(che è 1). Nota anche che l'immagine su cui sto lavorando è 302di 302; questo è così che l'algoritmo della distanza del Cavaliere non deve preoccuparsi di tentare di leggere fuori campo sull'immagine. Risolvo questa discrepanza nelle dimensioni chiamando i.getSubImage(1,1,300,300). Nota: potrei aver dimenticato di risolvere questo problema quando ho caricato le immagini, nel qual caso le immagini sono troppo larghe di 2 pixel, ma a parte questo, dovrebbero essere corrette

La funzione sovrascriverà il file il cui percorso è passato. Output:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


Può salvare alcuni caratteri abbreviando il nome della classe e args nel metodo principale in "a" o simile.
Ryan,

@Ryan Quelli non sono conteggiati nel conteggio. Conto solo le importazioni + le funzioni, come consentito dalla domanda.
Giustino,

Penso che potrei essere in grado di ottenere questo sotto 1000 byte. Devo lavorarci più tardi quando c'è tempo per provare.
Justin

20

Pitone, 571 567 528 byte

Analogamente alla soluzione di Quincunx, inizia riempiendo ogni forma con un indice compreso tra 1 e 4. Determina quindi l'identità delle forme mediante il raggio del loro cerchio di delimitazione. Una tavolozza di colori viene costruita di conseguenza e l'immagine viene salvata come immagine di colore indicizzato.

EDIT: mancato il fatto che le forme sono garantite per non toccare il bordo dell'immagine. Quindi è più corto!

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

Prende un nome file di input dalla riga di comando e scrive l'output in o.png.


2
Argh, è molto più semplice di quello che sto cercando di fare. +1
Martin Ender,

7

Mathematica 225


Aggiornamento :

L'OP ha deciso che questo approccio utilizza le funzioni di visione artificiale, quindi non è più in esecuzione. Lascerò comunque pubblicato. Forse qualcuno potrebbe trovarlo di interesse.


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 restituisce l'immagine come una matrice di 0 e 1.

Flatten converte quella matrice in un elenco.

Morphological Componentstrova i 4 cluster di pixel e assegna un intero distinto, 1, 2, 3, 4 a ciascun pixel in base al cluster. 0 è riservato per lo sfondo (nero).

ComponentMeasurements verifica la circolarità dei cluster.

Dal più al meno circolare sarà sempre: il cerchio, il quadrato, il triangolo e l'ingranaggio.

ReplacePart sostituisce ogni intero componente con il rispettivo colore RGB, usando l'ordinamento di circolarità.

Partition...Dimensions[m][[2]] prende l'elenco dei colori dei pixel e restituisce una matrice delle stesse dimensioni dell'immagine di input.

Image converte la matrice dei colori dei pixel in un'immagine colorata.

ingressi

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

uscite


147 caratteri:f@i_:=Image[#/.Append[Thread[Ordering[Last/@ComponentMeasurements[#,"Circularity"]]->{Yellow,Green,Red,Blue}],0->Black]]&@MorphologicalComponents@i
alephalpha

Punto minore: i tuoi colori non hanno i valori rgb corretti. Punto importante: non sono sicuro che lo considererei come non usare librerie o funzioni di computer vision.
Hobby di Calvin il

La "circolarità" è probabilmente visiva; Vedrò cos'altro posso fare. I colori sono, tuttavia, morti {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}:, dove 1 corrisponde a 255. Non sono state utilizzate librerie.
DavidC

@ Calvin'sHobbies Il problema sembra dipendere dal fatto che MorphologicalComponentssoddisfi o violi le tue regole. Una volta che si sa a quale cluster appartiene ciascun pixel, ci sono molti modi, incluso un conteggio grezzo di pixel, per determinare quale figura è quale.
DavidC,

Sto per dire che viola le regole, in quanto è molto probabilmente una funzione di visione artificiale e dà a Mathematica un vantaggio ingiusto. Sono d'accordo che i colori dovrebbero essere corretti, ma appaiono chiaramente nella tua immagine (il rosso è (255,0,22)quando controllo in Paint). Non ho Mathematica, quindi non posso correre per esserne sicuro.
Hobby di Calvin il

7

Mathematica, 354 345 314 291 288

Sempre a golf, potrebbe essere ridotto di qualche altro carattere, ma le prestazioni diventano insopportabili. Utilizza la varianza per identificare le forme:

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])])&

Con spaziatura:

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])]) &

test:

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

Grafica Mathematica

Qui è completamente non golfato. Aggiungeremo spiegazioni in seguito:

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

2

Python, 579 577 554 514 502 501 byte

Per ogni forma, la riempie, quindi calcola la distanza tra il centroide e il punto più lontano.

quindi la superficie reale della forma viene confrontata con la superficie di un triangolo, quadrato, disco o ruota che avrebbe le stesse dimensioni.

importazione matematica ; da PIL . Importazione di immagini *; A , R , _ , I = abs , range ( 300 ), 255 , open ( sys . Argv [ 1 ]). convert ( 'P' ); Q = I . load () per j in R : per i in R : se Q [ 

 
  i , j ] == _ : 
   X , Y , s , z , p = 0 , 0 , 0 , [], [( i , j )] mentre p : 
    a , b = n = p . pop () in caso contrario ( Q [ n ]! = _ oppure n in z ): 
     X + = a ; Y + =
   
     b ; z + = [ n ]; p + = [( a , b - 1 ), ( a + 1 , b ), ( a , b + 1 ), ( a - 1 , b )]; s + = 1 
   r = max ([ matematica . hypot ( X / s - x , Y / s - y ) per x , y in z ]); C = { 1 : A ( 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 )} per p in z
   : 
    Q [ p ] = min ( C , chiave = C . Ottenere ) 
I . putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ]) 
I . mostra ()

1

C # 1086 byte

Ancora un'altra soluzione di alluvione, solo per la cronaca poiché non esiste una versione C # qui. Come Quincunx volevo dimostrare a me stesso che posso farlo e non c'è molta differenza nel suo approccio in Java.

  • Questa soluzione non utilizza alcuna ricorsione (ma uno stack) perché ho continuato a correre in StackOverflows.
  • Il rilevamento di borderpixels è semplificato osservando i 4 pixel successivi, se uno di questi è nero, la corrente è un pixel di bordo.

Accetta tutti i formati di immagine.

  • Parametro 1 = InputPath
  • Parametro 2 = OutputPath

Probabilmente può essere ridotto alcuni caratteri rimuovendo tutto il materiale statico e creando un'istanza di Program.

Versione leggibile:

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

golfed:

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();}}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.