Kwadrat, koło, trójkąt… Sprzęt?


69

Za pomocą Algodoo i Paint wykonałem te sześć monochromatycznych obrazów o wymiarach 300 × 300 i czterech wygodnych kształtach:

Zdjęcie 1 Zdjęcie 2 Zdjęcie 3 Zdjęcie 4 Zdjęcie 5 Zdjęcie 6

Ta klasa obrazów ma następujące właściwości:

  • Są one zawsze w rozmiarze 300 × 300 pikseli, monochromatyczne (tylko czarno-białe) i mają dokładnie cztery białe obszary odpowiadające kwadratowi, okręgowi, trójkątowi i zębatce.
  • Kształty nigdy się nie nakładają ani nie dotykają się, ani nie dotykają obramowania obrazu ani nie wychodzą poza granice.
  • Kształty zawsze mają ten sam rozmiar, ale można je dowolnie obracać i ustawiać.

(Kształty mają również równe obszary, chociaż przy takim rastrowaniu ich liczba pikseli prawdopodobnie nie będzie dokładnie równa.)

Wyzwanie

Napisz najkrótszy możliwy program lub funkcję, która pobierze nazwę pliku takiego obrazu i zamieni wszystkie białe piksele ...

  • czerwony, (255, 0, 0)jeśli są na kwadracie.
  • niebieski, (0, 0, 255)jeśli są w kole.
  • zielony, (0, 255, 0)jeśli są w trójkącie.
  • żółty, (255, 255, 0)jeśli są na biegu.

na przykład

Obraz 1 w kolorze

Detale

Twój program powinien działać skutecznie dla wszystkich możliwych obrazów wejściowych. (Wprowadzone zostaną tylko prawidłowe obrazy monochromatyczne 300 × 300). Sześć obrazów, które przedstawiłem, są jedynie przykładami, możesz nie zakodować na stałe ich wyników w swoim programie.

Nie wolno używać bibliotek lub funkcji komputerowych, wbudowanych ani zewnętrznych. Chodzi o to, aby to zrobić za pomocą własnych operacji na poziomie pikseli. Możesz użyć bibliotek obrazów, które po prostu pozwalają otwierać i modyfikować obrazy (np. PIL dla Pythona).

Możesz używać dowolnych popularnych bezstratnych formatów plików obrazu do wprowadzania i wyprowadzania, o ile trzymasz się schematu kolorów.

Możesz pobrać nazwę pliku obrazu jako argument funkcji, ze standardowego wejścia lub z wiersza poleceń. Obraz wyjściowy można zapisać w nowym pliku, tym samym pliku lub po prostu wyświetlić.

Punktacja

Zgłoszenie z najmniejszą liczbą bajtów wygrywa. Mogę przetestować zgłoszenia za pomocą dodatkowych zdjęć, aby ustalić ich ważność.


Czy możemy założyć, że sygnał wejściowy jest czarno-biały bez wygładzania? Jeśli nie, to czy możemy usunąć wygładzanie z antyaliasingu?
— John Dvorak

@JanDvorak Tak. Przez monochromatyczny rozumiem tylko czarno-biały, więc nie może być wygładzania.
— Calvin's Hobbies

1
Czy możemy wymagać konkretnego formatu wejściowego bardziej precyzyjnie niż rozszerzenia plików? Mianowicie, chciałbym mieć wejście ASCII PBM bez żadnych komentarzy w środku.
— John Dvorak

12
Więc ... Próbowałem rozwiązać ten problem i skończyłem z tym obrazem . Nie bardzo wiem jak, ale hej, to wygląda fantastycznie. : P
— Klamka

2
Nie chcę publikować mojego rozwiązania, ponieważ jest to ten sam pomysł co Ell, ale gorzej. Ale chcę tylko powiedzieć, że było to przyjemne małe wyzwanie :)
— Chris Burt-Brown,

Odpowiedzi:


8

J - 246,224 185 bajtów

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

To była świetna zabawa!

Ponownie wykorzystałem część połączonych komponentów, której użyłem do wyzwania „Jestem w największym pokoju” , i zastosowałem stosunek między średnią a maksymalną odległością wszystkich punktów do centrum każdego komponentu. Postawiłem na to, ponieważ jest ona niezmienna zarówno pod względem skali, jak i rotacji, i najwyraźniej wystarczająco dobra, aby rozróżnić kształty, które zostały podane. Ranking tej wartości od niskiej do wysokiej daje mi koło zamówienia, koło zębate, kwadrat i trójkąt, używane do permutacji mapy kolorów.

Wyświetla wynik za pomocą dodatku viewmap. Nie użyto żadnych przyborników oprócz odczytu i wyjścia plików.

Wytrzymałość nie wydaje się być wymogiem, to usuwa 18 bajtów. 2 więcej niepotrzebne przestrzenie zastąpione &.>przez &>w ratioa &.:o &:w dcent przez kolejne 2 bajty.

Ogromny wzrost zarówno krótkości, jak i wydajności compdzięki zmianie biegów zamiast cut( ;.). W ten sposób obraz jest replikowany i przesuwany we wszystkich 8 kierunkach zamiast skanowania go za pomocą okna 3x3.

Ta idfunkcja była absurdalnie skomplikowana pod względem tego, co musiała zrobić. Teraz przypisuje identyfikatory do pikseli w obiektach poprzez pomnożenie obrazu przez tablicę unikalnych liczb, a tym samym ustawienie BG na zero.

Kod nieco bardziej wyjaśniony:

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

Ten jest nieco długi, aby szczegółowo wyjaśnić, ale zrobi to, jeśli będzie zainteresowanie.


Gwarantowany prawy górny piksel to bg. Zgodnie z OP „Kształty nigdy się nie nakładają ani nie dotykają się, ani nie dotykają krawędzi obrazu ani nie przekraczają granic”.
— Dr Belisarius,

Dzięki, to jest pomocne. (właściwie miałem na myśli lewy górny piksel, pierwszy w ravelu). Zmniejsza to wykrywanie tła (22 bajty).
— jpjacobs


29

Mathematica, 459 392 bajtów

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

Nie golfowany:

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

Mógłbym zaoszczędzić 6 bajtów, zamieniając się m=1.Mean@a;m=#-m&/@a;w m=#-Mean@a&/@a;, ale to znacznie skraca czas wykonania, co jest denerwujące dla testowania. (Zauważ, że są to dwie optymalizacje: wyciągnięcie obliczenia Mean@apoza pętlą i użycie dokładnych typów symbolicznych zamiast liczb zmiennoprzecinkowych. Co ciekawe, użycie dokładnych typów jest o wiele bardziej znaczące niż obliczanie średniej w każdej iteracji.)

To jest podejście numer trzy:

  • Wykryj obszary przez zalanie.
  • Znajdź przybliżone centrum każdego obszaru, uśredniając wszystkie współrzędne pikseli.
  • Teraz dla wszystkich pikseli w kształcie wykreślmy odległość od kąta względem kąta do tego środka:

    wprowadź opis zdjęcia tutaj

    Trójkąt ma 3 wyraźne maksima, kwadrat 4, koło zębate 16, a koło ma tony, ze względu na aliasing wahań wokół stałego promienia.

  • Znajdujemy liczbę maksimów, patrząc na plasterki o wielkości 300 pikseli (uporządkowane według kąta), i liczymy plasterki, w których piksel w pozycji 150jest maksymalny.
  • Następnie po prostu kolorujemy wszystkie piksele w zależności od liczby pików (okrąg ma wartość powyżej 16 i zwykle daje około 20 pików, ze względu na rozmiar plasterków).

Dla przypomnienia, jeśli użyję pomysłu Ella i po prostu posortuję regiony według największej odległości między dowolnym pikselem a środkiem, mogę to zrobić w 342 bajtach:

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

Ale nie zamierzam z tym konkurować, dopóki wszyscy inni używają swoich własnych oryginalnych algorytmów, zamiast grać w golfa algorytmami innych.


Najciekawsze rozwiązanie!
— CSharpie

25

Java, 1204 1132 1087 1076

Aby udowodnić sobie, że mogę to zrobić.

Dołączyłem import tuż obok deklaracji funkcji; aby to zadziałało, musiałyby znajdować się poza klasą:

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

Niegolfowane (i możliwe do uruchomienia; tj. Dodana płyta kotła)

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

Działa to poprzez iterowanie każdego piksela obrazu i wypełnianie zalewaniem za każdym razem, gdy docieramy do „dziury”. Każdy wynik wypełnienia dodajemy jako Set<Point>do Set. Następnie określamy, który kształt jest który. Odbywa się to poprzez sprawdzenie liczby pikseli brzegowych kształtu. Zdefiniowałem granicę jako ruch rycerza od czarnej płytki, ponieważ pozostałaby ona bardziej stała między rotacjami i tym podobne. Kiedy to robimy, staje się jasne, że kształty można sortować według tej wartości: koło, kwadrat, trójkąt, koło zębate. Więc sortuję i ustawiam wszystkie piksele tego kształtu na właściwy kolor.

Zauważ, że obraz, do którego piszę, nie jest pobierany bezpośrednio z pliku, ponieważ gdybym to zrobił, Java traktowałaby obraz jako czarno-biały, a wypełnienie kolorami nie działałoby. Więc muszę stworzyć własny obraz z TYPE_INT_RGB(który jest 1). Zauważ też, że obraz, nad którym pracuję, jest 302autorstwa 302; Dzieje się tak, aby algorytm odległości rycerza nie musiał się martwić próbą odczytania obrazu poza granicami. Naprawiam tę rozbieżność, dzwoniąc i.getSubImage(1,1,300,300). Uwaga: mogłem zapomnieć o rozwiązaniu tego problemu podczas przesyłania zdjęć, w których to przypadkach obrazy są o 2 piksele za szerokie, ale poza tym powinny być poprawne

Funkcja zastąpi plik, do którego ścieżka została przekazana. Dane wyjściowe:

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj


Można zapisać kilka znaków, skracając nazwę klasy, a także argumenty w metodzie głównej do „a” lub podobnego.
— Ryan

@ Ryan Te nie są liczone. Liczę tylko import + funkcje, zgodnie z pytaniem.
— Justin

Myślę, że uda mi się uzyskać mniej niż 1000 bajtów. Musisz nad tym popracować później, gdy będzie czas na próbę.
— Justin

20

Pyton, 571 567 528 bajtów

Podobnie jak w rozwiązaniu Quincunx, zaczyna się od wypełnienia każdego kształtu indeksem od 1 do 4. Następnie określa tożsamość kształtów na podstawie promienia ich ograniczającego okręgu. Paleta kolorów jest odpowiednio skonstruowana, a obraz jest zapisywany jako obraz w kolorze indeksowanym.

EDYCJA: Pominięto fakt, że kształty mają gwarancję, że nie dotykają ramki obrazu. Zatem jest krótszy!

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

Pobiera wejściową nazwę pliku w wierszu poleceń i zapisuje dane wyjściowe w o.png.


2
Argh, to o wiele prostsze niż to, co próbuję zrobić. +1
— Martin Ender

7

Mathematica 225


Aktualizacja :

PO zdecydował, że to podejście wykorzystuje funkcje widzenia komputerowego, więc nie jest już aktywne. Zostawię to jednak opublikowane. Być może ktoś może go zainteresować.


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 zwraca obraz jako macierz zer i jedynek.

Flatten konwertuje tę macierz na listę.

Morphological Componentsznajduje 4 klastry pikseli i przypisuje odrębną liczbę całkowitą 1, 2, 3, 4 do każdego piksela zgodnie z klastrem. 0 jest zarezerwowane dla (czarnego) tła.

ComponentMeasurements sprawdza kołowość klastrów.

Od najbardziej do najmniej okrągłego zawsze będzie: koło, kwadrat, trójkąt i koło zębate.

ReplacePart zastępuje każdą liczbę całkowitą składową odpowiednim kolorem RGB, stosując sortowanie według kołowości.

Partition...Dimensions[m][[2]] pobiera listę kolorów pikseli i zwraca macierz o takich samych wymiarach jak obraz wejściowy.

Image konwertuje matrycę kolorów pikseli na kolorowy obraz.

wejścia

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

wyjścia


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

Drobny punkt: twoje kolory nie mają prawidłowych wartości rgb. Ważny punkt: nie jestem pewien, czy policzyłbym to jako brak korzystania z bibliotek lub funkcji komputerowych.
— Calvin's Hobbies

„Okrągłość” jest prawdopodobnie wizualna; Zobaczę, co jeszcze mogę zrobić. Kolory są jednak martwe w:, {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}gdzie 1 odpowiada 255. Nie użyto bibliotek.
— DavidC

@ Calvin'sHobbies Wydaje się, że problem sprowadza się do tego, czy MorphologicalComponentsspełnia on lub narusza Twoje zasady. Kiedy już wiadomo, do którego klastra należy każdy piksel, istnieje wiele sposobów, w tym surowa liczba pikseli, w celu ustalenia, która figura jest która.
— DavidC

Powiem, że narusza to zasady, ponieważ jest to prawdopodobnie funkcja widzenia komputerowego i daje Mathematice nieuczciwą przewagę. Zgadzam się, że kolory powinny być prawidłowe, ale wyraźnie wyglądają na twoim obrazie (czerwony jest, (255,0,22)gdy sprawdzam w Paint). Nie mam Mathematica, więc nie mogę biegać, żeby się upewnić.
— Calvin's Hobbies

7

Mathematica, 354 345 314 291 288

Wciąż gra w golfa, może zostać skrócony o kilka znaków, ale wydajność staje się nie do zniesienia. Używa wariancji do identyfikacji kształtów:

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

Z odstępami:

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

Testowanie:

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

Grafika matematyczna

Tutaj jest całkowicie bez golfa. Dodają wyjaśnienia później:

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 bajtów

Dla każdego kształtu wypełnij go, a następnie oblicz odległość między środkiem ciężkości a najdalszym punktem.

wtedy rzeczywista powierzchnia kształtu jest porównywana z powierzchnią trójkąta, kwadratu, tarczy lub koła, które miałyby ten sam rozmiar.

import matematyki ; z PIL . Import obrazu *; A , R , _ , I = abs , zakres ( 300 ), 255 , otwarty ( sys . Argv [ 1 ]). konwersja ( „P” ); Q = ja . Obciążenie () dla j w R : dla i w R : jeśli Q [ 

 
  i , j ] == _ : 
   X , Y , s , z , p = 0 , 0 , 0 , [], [( i , j )] podczas gdy p : 
    a , b = n = p . Pop () czy nie ( P [ N !] = _ lub n w Z ) 
     X + = ; Y + =
   
     b ; z + = [ n ]; p + = [( a , b - 1 ), ( a + 1 , b ), ( a , b + 1 ), ( a - 1 , b )]; s + = 1 
   r = max ([ mat . hipot . ( X / s - x , Y / s - y ) dla x , y w z ]); C = { 1 : ( s - ( 1,4 * R ) ** 2 ), 2 : ( a - R * R / 3 ), 3 : ( y - Math . Pi * R * R ), 4 : ( s - 2,5 * r * r )} dla p w z
   : 
    P [ t ] = min ( C , klucz = C . Dostać ) 
I . putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ]) 
I . pokaż ()

1

C # 1086 bajtów

Jeszcze jedno rozwiązanie wypełniające, tylko dla przypomnienia, ponieważ nie ma tutaj wersji C #. Podobnie jak Quincunx, chciałem udowodnić, że potrafię to zrobić i jego podejście do Javy nie ma większych trudności.

  • To rozwiązanie nie korzysta z żadnej rekurencji (ale stosu), ponieważ ciągle napotykałem przepływy stosów.
  • Wykrywanie pikseli granicznych jest uproszczone poprzez spojrzenie na 4 kolejne piksele, jeśli którykolwiek z nich jest czarny, prąd jest pikselem granicznym.

Akceptuje każdy format obrazu.

  • Parametr 1 = InputPath
  • Parametr 2 = OutputPath

Prawdopodobnie można go rozebrać o kilka znaków, usuwając wszystkie elementy statyczne i tworząc instancję programu.

Wersja do odczytu:

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

Gra w golfa:

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();}}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.