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.