Quadrado, Círculo, Triângulo, ... Equipamento?


69

Usando Algodoo e Paint, fiz essas seis imagens monocromáticas 300 × 300 de quatro formas convenientes:

Imagem 1 Imagem 2 Imagem 3 Imagem 4 Imagem 5 Imagem 6

Esta classe de imagens possui as seguintes propriedades:

  • Eles sempre são 300 × 300 pixels, monocromáticos (somente preto e branco) e têm exatamente quatro regiões brancas que correspondem a um quadrado, um círculo, um triângulo e uma engrenagem.
  • As formas nunca se sobrepõem ou se tocam, nem tocam a borda da imagem ou ficam fora dos limites.
  • As formas sempre têm o mesmo tamanho, mas podem ser giradas e posicionadas de qualquer maneira.

(As formas também têm áreas iguais, embora, quando rasterizadas dessa maneira, é improvável que a contagem de pixels seja exatamente equivalente.)

Desafio

Escreva o programa ou a função mais curta possível que capte o nome do arquivo dessa imagem e gire todos os pixels brancos ...

  • vermelho (255, 0, 0)se estiverem na praça.
  • azul (0, 0, 255)se estiverem no círculo.
  • verde (0, 255, 0)se estiverem no triângulo.
  • amarelo (255, 255, 0)se estiverem na engrenagem.

por exemplo

Imagem 1 colorida em

Detalhes

Seu programa deve funcionar para efetivamente todas as imagens de entrada possíveis. (Somente imagens monocromáticas válidas de 300 × 300 serão inseridas.) As seis imagens que forneci são meramente exemplos. Você pode não codificar sua saída em seu programa.

Você não pode usar bibliotecas ou funções de visão computacional, internas ou externas. O objetivo é fazer isso usando suas próprias operações em nível de pixel. Você pode usar bibliotecas de imagens que simplesmente permitem abrir e modificar imagens (por exemplo, PIL para Python).

Você pode usar qualquer formato de arquivo de imagem sem perda comum para entrada e saída, desde que se atenha ao esquema de cores.

Você pode observar o nome do arquivo da imagem como argumento de função, de stdin ou da linha de comando. A imagem de saída pode ser salva em um novo arquivo, no mesmo arquivo ou simplesmente exibida.

Pontuação

O envio com o menor número de bytes vence. Posso testar envios com imagens adicionais para determinar sua validade.


Podemos assumir que a entrada é em preto e branco sem anti-aliasing? Caso contrário, podemos remover o anti-aliasing de entradas com anti-alias?
John Dvorak

@JanDvorak Yes. Por monocromático, quero dizer apenas preto e branco, então não pode haver anti-aliasing.
Calvin's Hobbies

11
Podemos exigir um formato de entrada específico com mais precisão do que apenas uma extensão de arquivo? Ou seja, eu gostaria de uma entrada PBM ASCII sem nenhum comentário dentro.
John Dvorak #

12
Então ... eu estava tentando resolver isso e acabei com esta imagem . Não tenho muita certeza de como, mas olha, parece chique. : P
Maçaneta da porta

2
Não quero postar minha solução, pois é a mesma ideia que a de Ell, mas pior. Mas eu só quero dizer que este foi um pequeno desafio agradável para fazer :)
Chris Burt-Brown

Respostas:


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

Essa foi engraçada!

Reutilizei a parte de componentes conectados que usei para o desafio "Estou na sala maior" e usei a razão entre a distância média e máxima de todos os pontos do centro de cada componente. Eu me conformei com isso, já que a escala e a rotação são invariantes, e aparentemente boas o suficiente para distinguir as formas dadas. Classificar esse valor de baixo para alto me fornece o círculo, a engrenagem, o quadrado e o triângulo da ordem, usados ​​para permutar o mapa de cores.

Exibe o resultado usando o complemento viewmap. Nenhuma caixa de ferramentas usada, exceto para leitura e saída de arquivos.

A robustez não parece ser um requisito, isso tira 18 bytes. Mais 2 espaços desnecessários, substituídos &.>por &>in ratioe &.:por &:no dcent por mais 2 bytes.

Ganho enorme na falta e no desempenho de compusar shift em vez de cut( ;.). Dessa forma, a imagem é replicada e deslocada nas 8 direções, em vez de digitalizá-la com uma janela 3x3.

A idfunção era ridiculamente complexa para o que precisava fazer. Agora, ele atribui IDs a pixels em objetos, multiplicando a imagem com uma matriz de números únicos, configurando o BG para zero.

Código um pouco mais explicado:

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

Este é um pouco longo para explicar em detalhes, mas será útil se houver interesse.


O pixel superior direito é garantido como BG. Conforme o OP "As formas nunca se sobrepõem ou se tocam, nem tocam na borda da imagem ou ficam fora dos limites".
amigos estão dizendo sobre belisarius

Obrigado, isso é útil. (na verdade, eu quis dizer pixel superior esquerdo, o primeiro no desfile). Isso remove a detecção de segundo plano (22 bytes).
Jpjacobs # 6/14

Drasticamente reduzido comprimento, e aumento da performance :)
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]]]
) &

Eu poderia economizar mais 6 bytes transformando m=1.Mean@a;m=#-m&/@a;em m=#-Mean@a&/@a;, mas isso aumenta significativamente o tempo de execução, o que é irritante para o teste. (Observe que são duas otimizações: retirar o cálculo de Mean@afora de loop e usar tipos simbólicos exatos em vez de números de ponto flutuante. Curiosamente, o uso de tipos exatos é muito mais significativo do que calcular a média em todas as iterações.)

Portanto, esta é a abordagem número três:

  • Detecte áreas preenchendo-as.
  • Encontre o centro aproximado de cada área calculando a média de todas as coordenadas de pixel.
  • Agora, para todos os pixels na forma, vamos traçar a distância do ângulo x ao centro:

    insira a descrição da imagem aqui

    O triângulo tem 3 máximos nítidos, o quadrado 4, a engrenagem 16 e o ​​círculo possui toneladas, devido a flutuações de aliasing sobre o raio constante.

  • Encontramos o número de máximos observando fatias de 300 pixels (ordenadas por ângulo) e contamos as fatias em que o pixel na posição 150é o máximo.
  • Em seguida, apenas colorimos todos os pixels, dependendo do número de picos (o círculo tem mais de 16 e geralmente produz cerca de 20 picos, devido ao tamanho das fatias).

Só para constar, se eu usar a idéia de Ell e simplesmente classificar as regiões pela maior distância entre qualquer pixel e centro, posso fazer isso em 342 bytes:

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

Mas não pretendo competir com isso, desde que todos os outros usem seus próprios algoritmos originais, em vez de usar os dos outros.


A solução mais interessante!
CSharpie

25

Java, 1204 1132 1087 1076

Só para provar a mim mesmo que eu posso fazer isso.

Incluí importações ao lado das declarações de função; estes teriam que estar fora da classe para que isso funcionasse:

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 executável; ou seja, clichê adicionado):

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

Isso funciona iterando sobre cada pixel da imagem e preenchendo cada inundação cada vez que atingimos um "buraco". Adicionamos cada resultado de preenchimento como a Set<Point>a a Set. Então determinamos qual forma é qual. Isso é feito observando o número de pixels de limite da forma. Eu defini limite como o afastamento de um cavaleiro de um ladrilho preto, pois isso permaneceria mais constante entre as rotações e tal. Quando fazemos isso, fica claro que as formas podem ser classificadas por esse valor: Círculo, Quadrado, Triângulo, Engrenagem. Então, ordeno e defino todos os pixels dessa forma para a cor correta.

Observe que a imagem para a qual estou escrevendo não é extraída diretamente do arquivo, porque se eu fizesse isso, Java trataria a imagem como preto e branco e o preenchimento de cores não funcionaria. Então eu tenho que criar minha própria imagem com TYPE_INT_RGB(o que é 1). Observe também que a imagem na qual estou trabalhando é feita 302por 302; isso é para que o algoritmo de distância de Knight não precise se preocupar em tentar ler fora dos limites da imagem. Corrijo essa discrepância de tamanho ligando i.getSubImage(1,1,300,300). Nota: Talvez eu tenha esquecido de corrigir isso quando carreguei as imagens. Nesse caso, as imagens têm 2 pixels de largura demais, mas, exceto por esse fato, elas devem estar corretas

A função substituirá o arquivo cujo caminho é passado. Saídas:

insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui insira a descrição da imagem aqui


Pode salvar alguns caracteres, encurtando o nome da classe e os argumentos no método principal para "a" ou similar.
Ryan

@ Ryan Esses não são contados na contagem. Eu apenas conto as importações + as funções, conforme permitido pela pergunta.
Justin

Eu acho que posso conseguir isso com menos de 1000 bytes. Deve trabalhar nisso mais tarde, quando houver tempo para tentar.
Justin

20

Pitão, 571 567 528 bytes

Da mesma forma que a solução de Quincunx, ela começa preenchendo cada forma com um índice de 1 a 4. Em seguida, determina a identidade das formas pelo raio do seu círculo delimitador. Uma paleta de cores é construída de acordo e a imagem é salva como uma imagem de cores indexadas.

Edição: Perdeu o fato de que as formas são garantidas para não tocar na borda da imagem. É mais curto, então!

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

Pega um nome de arquivo de entrada na linha de comando e grava a saída em o.png.


2
Argh, isso é muito mais simples do que estou tentando fazer. +1
Martin Ender

7

Mathematica 225


Atualização :

O OP decidiu que essa abordagem usa funções de visão computacional, portanto não está mais em execução. Vou deixá-lo publicado no entanto. Talvez alguém possa achar interessante.


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 retorna a imagem como uma matriz de 0 e 1.

Flatten converte essa matriz em uma lista.

Morphological Componentslocaliza os 4 grupos de pixels e atribui um número inteiro distinto, 1, 2, 3, 4 a cada pixel, de acordo com o cluster. 0 é reservado para o plano de fundo (preto).

ComponentMeasurements testa a circularidade dos clusters.

Do mais para o menos circular sempre será: o círculo, o quadrado, o triângulo e a engrenagem.

ReplacePart substitui cada número inteiro do componente pela respectiva cor RGB, usando a classificação de circularidade.

Partition...Dimensions[m][[2]] pega a lista de cores de pixel e retorna uma matriz com as mesmas dimensões da imagem de entrada.

Image converte a matriz de cores de pixel em uma imagem colorida.

entradas

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

saídas


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

Ponto secundário: suas cores não têm os valores rgb corretos. Ponto principal: não tenho certeza se consideraria isso como não usando bibliotecas ou funções de visão computacional.
Calvin's Hobbies

"Circularidade" é sem dúvida visual; Vou ver o que mais posso fazer. As cores estão, no entanto, apagadas:, em {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}que 1 corresponde a 255. Nenhuma biblioteca foi usada.
DavidC

@ Calvin'sHobbies A questão parece se resumir a se MorphologicalComponentssatisfaz ou viola suas regras. Quando se sabe a qual cluster cada pixel pertence, existem várias maneiras, incluindo uma contagem bruta de pixels, para determinar qual número é qual.
DavidC

Vou dizer que isso viola as regras, pois é muito discutível uma função de visão computacional, e dá ao Mathematica uma vantagem injusta. Concordo que as cores devem estar corretas, mas elas parecem claramente na sua imagem (o vermelho é (255,0,22)quando eu verifico no Paint). Eu não tenho o Mathematica, então não posso correr para ter certeza.
Hobbies de Calvin

7

Mathematica, 354 345 314 291 288

Ainda jogando golfe, poderia ser reduzido por mais alguns caracteres, mas o desempenho se torna insuportável. Usa a variação para identificar formas:

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

Com espaçamento:

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

Testando:

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

Gráficos do Mathematica

Aqui é completamente não-destruído. Adicionará explicações mais tarde:

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 bytes

Para cada forma, preencha-a e calcule a distância entre o centróide e o ponto mais distante.

então a superfície real da forma é comparada à superfície de um triângulo, quadrado, disco ou roda que teria o mesmo tamanho.

import math; from PIL.Image import *; A, R, _, I = abs, intervalo (300), 255, aberto (sys.argv [1]). convert ('P'); Q = I.load ( )matemática ; do PIL . Imagem de importação *; A , R , _ , I = abs , intervalo ( 300 ), 255 , aberto ( sys . Argv [ 1 ]). converter ( 'P' ); Q = eu . load () 
para j em R:para j em R :
 para i em R:para i em R :
  se Q [i, j] == _:se Q [ i , j ] == _ :
   X, Y, s, z, p = 0,0,0, [], [(i, j)], Y , s , z , p = 0 , 0 , 0 , [], [( i , j )]
   enquanto p:enquanto p :
    a, b = n = p.pop (), b = n = p . pop ()
    caso contrário (Q [n]! = _ ou n em z):caso contrário ( Q [ n ]! = _ ou n em z ): 
     X + = a; Y + = b; z + = [n]; p + = [(a, b-1), (a + 1, b), (a, b + 1), (a-1, b)]; s + = 1+=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 / sx, Y / sy) para x, y em z]); C = {1: A (s- (1,4 * r) ** 2), 2: A (sr * r / 3), 3: A (s-math.pi * r * r), 4: A (s-2,5 * r * r)}=max([math.hypot(X/s-x,Y/s-y) for 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 )}
   para p em z:para p em z :
    Q [p] = min (C, tecla = C.get)[ P ] = min ( C , chave = C . Conseguir )
I.putpalette ([0,0,0, _] * 3 + [_, _, 0]). putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ])
Eu mostro(). show ()

1

Bytes em C # 1086

Mais uma solução de inundação, apenas para constar, já que não há versão C # aqui. Como Quincunx, eu queria provar a mim mesmo que posso fazer isso e não há muita diferença em sua abordagem em Java.

  • Esta solução não usa nenhuma recursão (mas uma pilha) porque eu continuava executando o StackOverflows.
  • A detecção de pixels de borda é simplificada observando os 4 pixels seguintes; se algum deles for preto, a corrente será um pixel de borda.

Aceita todos os formatos de imagem.

  • Parâmetro 1 = InputPath
  • Parâmetro 2 = Caminho de Saída

Provavelmente, pode ser retirado alguns caracteres, removendo todo o material estático e criando uma instância do Program.

Versão legível:

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

Golfe:

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();}}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.