Quadrat, Kreis, Dreieck,… Zahnrad?

69

Mit Algodoo und Paint habe ich diese sechs monochromen 300 × 300-Bilder in vier praktischen Formen erstellt:

Bild 1 Bild 2 Bild 3 Bild 4 Bild 5 Bild 6

Diese Bildklasse weist die folgenden Eigenschaften auf:

  • Sie sind immer 300 × 300 Pixel, einfarbig (nur Schwarzweiß) und haben genau vier weiße Bereiche, die einem Quadrat, einem Kreis, einem Dreieck und einem Zahnrad entsprechen.
  • Die Formen überlappen sich nie und berühren sich auch nicht. Sie berühren auch nicht den Bildrand oder überschreiten Grenzen.
  • Die Formen haben immer die gleiche Größe, können jedoch gedreht und beliebig positioniert werden.

(Die Formen haben auch gleiche Flächen, wenn sie jedoch so gerastert werden, ist es unwahrscheinlich, dass ihre Pixelanzahl genau gleich ist.)

Herausforderung

Schreiben Sie das kürzeste Programm oder die kürzeste Funktion, die den Dateinamen eines solchen Bildes aufnimmt und alle weißen Pixel umdreht ...

  • rot, (255, 0, 0)wenn sie auf dem Platz sind.
  • blau, (0, 0, 255)wenn sie im Kreis sind.
  • grün, (0, 255, 0)wenn sie im Dreieck sind.
  • gelb, (255, 255, 0)wenn sie im Gang sind.

z.B

Bild 1 eingefärbt

Einzelheiten

Ihr Programm sollte für alle möglichen Eingabebilder effektiv funktionieren. (Es werden nur gültige einfarbige 300 × 300-Bilder eingegeben.) Die sechs von mir bereitgestellten Bilder sind lediglich Beispiele. Sie können die Ausgabe möglicherweise nicht fest in Ihr Programm einprogrammieren.

Sie dürfen keine integrierten oder externen Computer Vision-Bibliotheken oder -Funktionen verwenden. Der Punkt ist, dies unter Verwendung Ihrer eigenen Operationen auf Pixelebene zu tun. Sie können Bildbibliotheken verwenden, mit denen Sie einfach Bilder öffnen und ändern können (z. B. PIL für Python).

Sie können alle gängigen verlustfreien Bilddateiformate für die Eingabe und Ausgabe verwenden, solange Sie sich an das Farbschema halten.

Sie können den Bilddateinamen als Funktionsargument von stdin oder über die Befehlszeile eingeben. Das Ausgabebild kann in einer neuen Datei oder derselben Datei gespeichert oder einfach angezeigt werden.

Wertung

Die Einsendung mit den wenigsten Bytes gewinnt. Ich kann Einsendungen mit zusätzlichen Bildern testen, um ihre Gültigkeit zu bestimmen.

Calvins Hobbys
quelle
Dürfen wir annehmen, dass die Eingabe in Schwarzweiß ohne Antialiasing erfolgt? Wenn nicht, können wir Anti-Aliasing von Anti-Aliasing-Eingaben entfernen?
John Dvorak
@ JanDvorak Ja. Mit Monochrom meine ich nur Schwarzweiß, daher kann es kein Anti-Aliasing geben.
Calvins Hobbys
1
Können wir ein bestimmtes Eingabeformat genauer als nur eine Dateierweiterung benötigen? Ich möchte mir nämlich einen ASCII- PBM- Eingang ohne irgendwelche Kommentare reinschauen.
John Dvorak
12
Also ... ich habe versucht, dieses Problem zu lösen und bin zu diesem Bild gekommen . Ich weiß nicht genau wie, aber hey, es sieht schick aus. : P
Türklinke
2
Ich möchte meine Lösung nicht veröffentlichen, da es die gleiche Idee wie Ell ist, aber schlimmer. Aber ich möchte nur sagen, dass dies eine unterhaltsame kleine Herausforderung war :)
Chris Burt-Brown

Antworten:

8

J - 246,224 185 Bytes

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

Das war ein Spaß!

Ich habe den Teil für verbundene Komponenten, den ich für die Herausforderung "Bin ich im größten Raum" verwendet habe , erneut verwendet und das Verhältnis zwischen durchschnittlichem und maximalem Abstand aller Punkte zum Mittelpunkt jeder Komponente verwendet. Ich habe mich damit abgefunden, da es sowohl skalierungs- als auch rotationsunabhängig ist und anscheinend gut genug ist, um zwischen den angegebenen Formen zu unterscheiden. Wenn ich diesen Wert von niedrig nach hoch ordne, erhalte ich den Ordnungskreis, das Zahnrad, das Quadrat und das Dreieck, mit denen die Farbkarte permutiert wird.

Zeigt das Ergebnis mit dem viewmap-Addon an. Keine Toolboxen außer zum Lesen und Ausgeben von Dateien.

Robustheit scheint keine Voraussetzung zu sein, dafür werden 18 Bytes benötigt. 2 weitere unnötige Bereiche sind, ersetzt &.>durch &>in ratiound &.:durch &:in dcent für weitere 2 Byte.

Enormer Gewinn an Kürze und Leistung bei der compVerwendung von Shifting anstelle von cut( ;.). Auf diese Weise wird das Bild repliziert und in alle 8 Richtungen verschoben, anstatt es mit einem 3x3-Fenster zu scannen.

Die idFunktion war für das, was sie tun musste, lächerlich komplex. Jetzt werden den Pixeln in Objekten IDs zugewiesen, indem das Bild mit einer Reihe eindeutiger Zahlen multipliziert wird, wodurch der Hintergrund auf Null gesetzt wird.

Code etwas genauer erklärt:

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

Dieser ist ein bisschen lang, um im Detail zu erklären, wird aber tun, wenn es Interesse gibt.

jpjacobs
quelle
Das obere rechte Pixel ist garantiert bg. Laut OP "Die Formen überlappen oder berühren sich nie. Sie berühren auch nicht den Bildrand oder überschreiten die Grenzen."
Dr. Belisarius
Danke, das ist hilfreich. (Eigentlich meinte ich das linke obere Pixel, das erste im Ravel). Dies verhindert die Hintergrunderkennung (22 Byte).
Jpjacobs
Dramatisch verkürzte Länge und erhöhte Leistung :)
jpjacobs
29

Mathematica, 459 392 Bytes

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

Ich konnte 6 weiteres Bytes speichern , indem m=1.Mean@a;m=#-m&/@a;in m=#-Mean@a&/@a;, aber das sprengt deutlich Ausführungszeit auf, die für die Prüfung ärgerlich. (Beachten Sie, dass dies zwei Optimierungen sind: Herausziehen der Berechnung Mean@aaus der Schleife und Verwenden exakter Symboltypen anstelle von Gleitkommazahlen. Interessanterweise ist die Verwendung exakter Typen viel wichtiger als die Berechnung des Mittelwerts in jeder Iteration.)

Das ist also Ansatz Nummer drei:

  • Bereiche durch Überfluten erkennen.
  • Finden Sie die ungefähre Mitte jedes Bereichs durch Mitteln aller Pixelkoordinaten.
  • Zeichnen wir nun für alle Pixel in der Form den Abstand von vs Winkel zu diesem Mittelpunkt:

    Bildbeschreibung hier eingeben

    Das Dreieck hat 3 klare Maxima, das Quadrat 4, das Zahnrad 16 und der Kreis hat Tonnen aufgrund von Aliasing-Schwankungen um den konstanten Radius.

  • Wir ermitteln die Anzahl der Maxima, indem wir Scheiben mit 300 Pixeln (sortiert nach Winkel) betrachten und die Scheiben zählen, bei denen das Pixel an der Position 150das Maximum ist.
  • Dann färben wir einfach alle Pixel in Abhängigkeit von der Anzahl der Peaks ein (der Kreis ist größer als 16 und ergibt aufgrund der Größe der Slices normalerweise etwa 20 Peaks).

Nur zur Veranschaulichung, wenn ich Ell's Idee verwende und die Regionen einfach nach dem größten Abstand zwischen einem Pixel und der Mitte sortiere, kann ich dies in 342 Bytes tun:

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

Aber ich habe nicht vor, damit zu konkurrieren, solange alle anderen ihre eigenen Originalalgorithmen verwenden, anstatt die der anderen herunterzuspielen.

Martin Ender
quelle
Die interessanteste Lösung!
CSharpie
25

Java, 1204 1132 1087 1076

Nur um mir selbst zu beweisen, dass ich das kann.

Ich habe Importe direkt neben den Funktionsdeklarationen eingefügt. Diese müssten außerhalb der Klasse sein, damit dies funktioniert:

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 (und runnable; dh Boilerplate hinzugefügt):

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

Dies funktioniert, indem jedes Pixel des Bildes durchlaufen wird und jedes Mal, wenn wir ein "Loch" erreichen, eine Überflutung erfolgt. Wir addieren jedes Füllergebnis als a Set<Point>zu a Set. Dann bestimmen wir, welche Form welche ist. Dies erfolgt durch Betrachten der Anzahl der Grenzpixel der Form. Ich definierte die Grenze als eine Bewegung eines Ritters weg von einem schwarzen Plättchen, da dies zwischen Rotationen und dergleichen konstanter bleiben würde. Wenn wir dies tun, wird klar, dass die Formen nach diesem Wert sortiert werden können: Kreis, Quadrat, Dreieck, Zahnrad. Also sortiere und setze ich alle Pixel dieser Form auf die richtige Farbe.

Beachten Sie, dass das Bild, auf das ich schreibe, nicht direkt aus der Datei stammt. In diesem Fall würde Java das Bild als schwarzweiß behandeln und das Füllen mit Farben würde nicht funktionieren. Also muss ich mit TYPE_INT_RGB(was ist 1) mein eigenes Image erstellen . Beachten Sie auch, dass das Bild, an dem ich arbeite, 302von stammt 302. Auf diese Weise muss sich der Abstandsalgorithmus des Ritters keine Gedanken darüber machen, ob das Bild außerhalb der Grenzen gelesen werden soll. Diese Größenabweichung behebe ich durch einen Anruf i.getSubImage(1,1,300,300). Hinweis: Ich habe möglicherweise vergessen, dies zu beheben, als ich die Bilder hochgeladen habe. In diesem Fall sind die Bilder 2 Pixel zu breit, aber bis auf diese Tatsache sollten sie korrekt sein

Die Funktion überschreibt die Datei, deren Pfad übergeben wird.

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Justin
quelle
Kann ein paar Zeichen speichern, indem der Klassenname sowie die Argumente in der Hauptmethode auf "a" oder ähnliches gekürzt werden.
Ryan
@ Ryan Diese werden nicht mitgezählt. Ich zähle nur die Importe + die Funktionen, wie von der Frage erlaubt.
Justin
Ich denke, dass ich in der Lage sein kann, dieses unter 1000 Bytes zu erhalten. Muss später daran arbeiten, wenn Zeit zum Ausprobieren ist.
Justin
20

Python, 571 567 528 Bytes

Ähnlich wie bei der Lösung von Quincunx wird zunächst jede Form mit einem Index von 1 bis 4 gefüllt. Anschließend wird die Identität der Formen anhand des Radius ihres Begrenzungskreises bestimmt. Entsprechend wird eine Farbpalette erstellt und das Bild als indiziertes Farbbild gespeichert.

BEARBEITEN: Die Tatsache, dass die Formen den Bildrand garantiert nicht berühren, wurde übersehen. Kürzer ist es dann!

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

Nimmt einen Eingabedateinamen in die Befehlszeile und schreibt die Ausgabe in o.png.

Ell
quelle
2
Das ist so viel einfacher als das, was ich versuche. +1
Martin Ender
7

Mathematica 225


Update :

Das OP entschied, dass dieser Ansatz Computer-Vision-Funktionen verwendet, sodass er nicht mehr läuft. Ich lasse es aber gepostet. Vielleicht findet es jemand von 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 Gibt das Bild als Matrix aus Nullen und Einsen zurück.

Flatten wandelt diese Matrix in eine Liste um.

Morphological ComponentsFindet die 4 Cluster von Pixeln und weist jedem Pixel je nach Cluster eine bestimmte Ganzzahl, 1, 2, 3, 4, zu. 0 ist für den (schwarzen) Hintergrund reserviert.

ComponentMeasurements testet die Zirkularität der Cluster.

Am wenigsten kreisförmig wird es immer sein: der Kreis, das Quadrat, das Dreieck und das Zahnrad.

ReplacePart Ersetzt jede ganzzahlige Komponente durch die jeweilige RGB-Farbe unter Verwendung der Zirkularitätssortierung.

Partition...Dimensions[m][[2]] Nimmt die Liste der Pixelfarben und gibt eine Matrix mit den gleichen Abmessungen wie das Eingabebild zurück.

Image wandelt die Matrix der Pixelfarben in ein Farbbild um.

Eingänge

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

Ausgänge

DavidC
quelle
147 Zeichen:f@i_:=Image[#/.Append[Thread[Ordering[Last/@ComponentMeasurements[#,"Circularity"]]->{Yellow,Green,Red,Blue}],0->Black]]&@MorphologicalComponents@i
alephalpha
Kleiner Punkt: Ihre Farben haben nicht die richtigen RGB-Werte. Wichtigster Punkt: Ich bin mir nicht sicher, ob ich Computer Vision-Bibliotheken oder -Funktionen nicht verwenden würde.
Calvins Hobbys
"Zirkularität" ist wohl visuell; Ich werde sehen, was ich sonst noch tun kann. Die Farben sind jedoch tot auf {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}:, wobei 1 255 entspricht. Es wurden keine Bibliotheken verwendet.
DavidC
@ Calvin'sHobbies Das Problem scheint darin zu bestehen, ob es MorphologicalComponentsIhre Regeln erfüllt oder verletzt. Sobald man weiß, zu welchem ​​Cluster jedes Pixel gehört, gibt es viele Möglichkeiten, einschließlich einer unformatierten Anzahl von Pixeln, um zu bestimmen, welche Figur welche ist.
DavidC
Ich werde sagen, dass es gegen die Regeln verstößt, da es sehr wohl eine Computer-Vision-Funktion ist und Mathematica einen unfairen Vorteil verschafft. Ich bin damit einverstanden, dass die Farben korrekt sein sollten, aber sie sehen auf Ihrem Bild deutlich anders aus (rot ist, (255,0,22)wenn ich Paint einchecke). Ich habe kein Mathematica, kann also nicht rennen, um sicherzugehen.
Calvins Hobbys
7

Mathematica, 354 345 314 291 288

Noch Golfspielen, könnte durch ein paar Zeichen mehr verkürzt werden, aber die Leistung wird unerträglich. Verwendet die Varianz, um Formen zu identifizieren:

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

Mit Abstand:

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

Testen:

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

Hier ist es völlig ungolfed. Füge später Erklärungen hinzu:

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

Python, 579 577 554 514 502 501 Bytes

Füllen Sie jede Form mit einer Überflutung und berechnen Sie dann den Abstand zwischen dem Schwerpunkt und dem entferntesten Punkt.

dann wird die reale Oberfläche der Form mit der Oberfläche eines Dreiecks, Quadrats, einer Scheibe oder eines Rads verglichen, die die gleiche Größe haben würden.

Mathe importieren ; von PIL . Bild Import *; A , R , _ , I = abs , Bereich ( 300 ), 255 , offen ( sys . Argv [ 1 ]). convert ( 'P' ); Q = ich . load () für j in R : für i in R : wenn Q [ 

 
  i , j ] == _ : 
   X , Y , n , z , p = 0 , 0 , 0 , [], [( i , j )] wobei p : 
    a , b = n = p . pop () falls nicht ( Q [ n ]! = _ oder 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 ([ math . hypot ( X / s - x , Y / s - y )) für 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 )} für p in z
   : 
    Q [ p ] = min ( C , Schlüssel = C . Erhalten ) 
I . putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ]) 
I . show ()
Dieter
quelle
1

C # 1086 Bytes

Noch eine Floodfill-Lösung, nur zur Veranschaulichung, da es hier keine C # -Version gibt. Wie Quincunx wollte ich mich beweisen, dass ich es kann und es gibt keinen großen Unterschied zu seiner Vorgehensweise in Java.

  • Diese Lösung verwendet keine Rekursion (sondern einen Stack), da ich immer wieder auf StackOverflows gestoßen bin.
  • Die Erkennung von Randpixeln wird durch Betrachten der 4 nächsten Pixel vereinfacht. Wenn eines dieser Pixel schwarz ist, handelt es sich bei dem aktuellen Pixel um einen Randpixel.

Es akzeptiert jedes Bildformat.

  • Parameter 1 = Eingabepfad
  • Parameter 2 = Ausgabepfad

Es kann wahrscheinlich ein paar Zeichen entfernt werden, indem das gesamte statische Material entfernt und eine Instanz von Program erstellt wird.

Lesbare Version:

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

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
quelle