Cuadrado, Círculo, Triángulo, ... ¿Equipo?

69

Usando Algodoo y Paint hice estas seis imágenes monocromáticas de 300 × 300 de cuatro formas convenientes:

Imagen 1 Imagen 2 Imagen 3 Imagen 4 Imagen 5 Imagen 6

Esta clase de imágenes tiene las siguientes propiedades:

  • Siempre son 300 × 300 píxeles, monocromáticos (solo en blanco y negro), y tienen exactamente cuatro regiones blancas que corresponden a un cuadrado, un círculo, un triángulo y un engranaje.
  • Las formas nunca se superponen o se tocan entre sí, ni tocan el borde de la imagen ni se salen de los límites.
  • Las formas siempre tienen el mismo tamaño, pero pueden rotarse y colocarse de cualquier manera.

(Las formas también tienen áreas iguales, aunque cuando se trazan así, es poco probable que sus recuentos de píxeles sean exactamente equivalentes).

Desafío

Escriba el programa o función más breve posible que tome el nombre de archivo de dicha imagen y convierta todos los píxeles blancos ...

  • rojo (255, 0, 0)si están en la plaza.
  • azul (0, 0, 255)si están en el círculo.
  • verde (0, 255, 0)si están en el triángulo.
  • amarillo (255, 255, 0)si están en la marcha.

p.ej

Imagen 1 coloreada

Detalles

Su programa debería funcionar para todas las imágenes de entrada posibles. (Solo se ingresarán imágenes monocromáticas válidas de 300 × 300). Las seis imágenes que he proporcionado son meramente ejemplos, no puede codificar su salida en su programa.

No puede utilizar bibliotecas o funciones de visión artificial, integradas o externas. El punto es hacer esto usando sus propias operaciones a nivel de píxel. Puede usar bibliotecas de imágenes que simplemente le permiten abrir y modificar imágenes (por ejemplo, PIL para Python).

Puede utilizar cualquier formato de archivo de imagen sin pérdida común para entrada y salida, siempre y cuando se adhiera al esquema de color.

Puede tomar el nombre de archivo de la imagen como argumento de función, desde stdin o desde la línea de comando. La imagen de salida puede guardarse en un nuevo archivo, el mismo archivo, o simplemente mostrarse.

Puntuación

El envío con la menor cantidad de bytes gana. Puedo probar los envíos con imágenes adicionales para determinar su validez.

Pasatiempos de Calvin
fuente
¿Podemos suponer que la entrada es en blanco y negro sin suavizado? Si no es así, ¿podemos eliminar el suavizado de las entradas suavizadas?
John Dvorak el
@ JanDvorak Sí. Por monocromático me refiero solo a blanco y negro, por lo que no puede haber suavizado.
Aficiones de Calvin el
1
¿Podemos requerir un formato de entrada específico con mayor precisión que solo una extensión de archivo? Es decir, me gustaría una entrada ASCII PBM sin ningún comentario dentro.
John Dvorak
12
Entonces ... estaba tratando de resolver esto, y terminé con esta imagen . No estoy seguro de cómo, pero bueno, parece elegante. : P
Pomo de la puerta
2
No quiero publicar mi solución, ya que es la misma idea que la de Ell, pero peor. Pero solo quiero decir que fue un pequeño desafío agradable para hacer :)
Chris Burt-Brown

Respuestas:

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

¡Esto fue divertido!

Reutilicé la parte de componentes conectados que usé para el desafío "Estoy en la habitación más grande" , y usé la relación entre la distancia promedio y máxima de todos los puntos al centro de cada componente. Me decidí por esto, ya que tanto la escala como la rotación son invariantes, y aparentemente son lo suficientemente buenas como para distinguir entre las formas que se dan. Clasificar este valor de menor a mayor me da el círculo de orden, el engranaje, el cuadrado y el triángulo, utilizados para permutar el mapa de colores.

Muestra el resultado usando el complemento viewmap. No se utilizan cajas de herramientas, excepto para la lectura y salida de archivos.

La robustez no parece ser un requisito, esto quita 18 bytes. 2 espacios innecesarios más, reemplazados &.>por &>in ratioy &.:por &:incent para otros 2 bytes.

Enorme ganancia en la brevedad y el rendimiento del compuso de cambios en lugar de cut( ;.). De esta forma, la imagen se replica y se desplaza en las 8 direcciones en lugar de escanearla con una ventana de 3x3.

La idfunción era ridículamente compleja para lo que tenía que hacer. Ahora asigna las identificaciones a los píxeles en los objetos al multiplicar la imagen con una matriz de números únicos y, por lo tanto, establecer el BG en cero.

Código un poco más 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 es un poco largo para explicar en detalle, pero servirá si hay interés.

jpjacobs
fuente
El píxel superior derecho está garantizado para ser bg. Según el OP "Las formas nunca se superponen o se tocan entre sí, ni tocan el borde de la imagen ni se salen de los límites".
Dr. belisario el
Gracias, eso es útil. (En realidad, me refería al píxel superior izquierdo, el primero en el enmarañamiento). Esto elimina la detección de fondo (22 bytes).
jpjacobs
Reducción drástica de la longitud y aumento del rendimiento :)
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]]])&

Sin golf:

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

Podría ahorrar 6 bytes más convirtiéndome m=1.Mean@a;m=#-m&/@a;en m=#-Mean@a&/@a;, pero eso explota significativamente el tiempo de ejecución, lo cual es molesto para las pruebas. (Tenga en cuenta que se trata de dos optimizaciones: extraer el cálculo de Mean@afuera de bucle y usar tipos simbólicos exactos en lugar de números de punto flotante. Curiosamente, el uso de tipos exactos es mucho más significativo que calcular la media en cada iteración).

Entonces este es el enfoque número tres:

  • Detectar áreas por inundación.
  • Encuentre el centro aproximado de cada área promediando todas las coordenadas de píxeles.
  • Ahora, para todos los píxeles en la forma, tracemos la distancia del ángulo vs al centro:

    ingrese la descripción de la imagen aquí

    El triángulo tiene 3 máximos claros, el cuadrado 4, el engranaje 16 y el círculo tiene toneladas, debido a las fluctuaciones de alias sobre el radio constante.

  • Encontramos el número de máximos observando cortes de 300 píxeles (ordenados por ángulo) y contamos los cortes donde el píxel en la posición 150es el máximo.
  • Luego, simplemente coloreamos todos los píxeles dependiendo del número de picos (el círculo tiene más de 16, y generalmente produce alrededor de 20 picos, debido al tamaño de los cortes).

Solo para el registro, si uso la idea de Ell, y simplemente clasifico las regiones por la mayor distancia entre cualquier píxel y centro, puedo hacer esto en 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]]])&

Pero no tengo la intención de competir con eso, siempre y cuando todos los demás estén usando sus propios algoritmos originales, en lugar de jugar golf con los de los demás.

Martin Ender
fuente
¡La solución más interesante!
CSharpie
25

Java, 1204 1132 1087 1076

Sólo para demostrar a mí mismo que yo puedo hacer esto.

Incluí importaciones justo al lado de las declaraciones de funciones; estos tendrían que estar fuera de la clase para que esto funcione:

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

Sin golf (y ejecutable; es decir, repetitivo agregado):

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

Esto funciona iterando sobre cada píxel de la imagen y llenando cada vez que llegamos a un "agujero". Agregamos cada resultado de inundación como Set<Point>a a Set. Luego determinamos qué forma es cuál. Esto se hace mirando el número de píxeles de límite de la forma. Definí el límite como el movimiento de un caballero lejos de un azulejo negro, ya que eso se mantendría más constante entre rotaciones y tal. Cuando hacemos esto, queda claro que las formas se pueden ordenar por ese valor: Círculo, Cuadrado, Triángulo, Engranaje. Así que clasifico y configuro todos los píxeles de esa forma con el color correcto.

Tenga en cuenta que la imagen en la que estoy escribiendo no se toma directamente del archivo, porque si tuviera que hacer eso, Java trataría la imagen como blanco y negro y el relleno con colores no funcionaría. Entonces tengo que crear mi propia imagen con TYPE_INT_RGB(que es 1). También tenga en cuenta que la imagen en la que estoy trabajando es 302por 302; Esto es para que el algoritmo de distancia del Caballero no tenga que preocuparse por intentar leer fuera de los límites de la imagen. Arreglo esta discrepancia de tamaño llamando i.getSubImage(1,1,300,300). Nota: Puede que haya olvidado arreglar esto cuando cargué las imágenes, en cuyo caso las imágenes tienen 2 píxeles de ancho, pero a excepción de este hecho, deberían ser correctas

La función sobrescribirá el archivo cuya ruta se pasa. Salidas:

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

Justin
fuente
Puede guardar algunos caracteres acortando el nombre de la clase y los argumentos en el método principal a "a" o similar.
Ryan
@Ryan Esos no se cuentan en el recuento. Solo cuento las importaciones + las funciones, según lo permitido por la pregunta.
Justin
Creo que puedo obtener esto por debajo de 1000 bytes. Debe trabajar en esto más tarde cuando haya tiempo para intentarlo.
Justin
20

Pitón, 571 567 528 bytes

De manera similar a la solución de Quincunx, comienza inundando cada forma con un índice del 1 al 4. Luego determina la identidad de las formas por el radio de su círculo delimitador. Se construye una paleta de colores en consecuencia y la imagen se guarda como una imagen de color indexado.

EDITAR: se perdió el hecho de que se garantiza que las formas no toquen el borde de la imagen. Más corto es, entonces!

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

Toma un nombre de archivo de entrada en la línea de comando y escribe la salida en o.png.

Ana
fuente
2
Argh, eso es mucho más simple de lo que estoy tratando de hacer. +1
Martin Ender
7

Mathematica 225


Actualización :

El OP decidió que este enfoque utiliza funciones de visión por computadora, por lo que ya no está en funcionamiento. Sin embargo, lo dejaré publicado. Quizás alguien pueda encontrarlo de interés.


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 devuelve la imagen como una matriz de 0 y 1.

Flatten convierte esa matriz en una lista.

Morphological Componentsencuentra los 4 grupos de píxeles y asigna un número entero distinto, 1, 2, 3, 4 a cada píxel de acuerdo con el grupo. 0 está reservado para el fondo (negro).

ComponentMeasurements prueba la circularidad de los grupos.

De mayor a menor circular siempre será: el círculo, el cuadrado, el triángulo y el engranaje.

ReplacePart reemplaza cada número entero componente con el color RGB respectivo, usando la clasificación de circularidad.

Partition...Dimensions[m][[2]] toma la lista de colores de píxeles y devuelve una matriz de las mismas dimensiones que la imagen de entrada.

Image Convierte la matriz de colores de píxeles en una imagen coloreada.

entradas

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

salidas

DavidC
fuente
147 caracteres:f@i_:=Image[#/.Append[Thread[Ordering[Last/@ComponentMeasurements[#,"Circularity"]]->{Yellow,Green,Red,Blue}],0->Black]]&@MorphologicalComponents@i
alephalpha
Punto menor: sus colores no tienen los valores rgb correctos. Punto principal: no estoy seguro de contar esto como no usar bibliotecas o funciones de visión por computadora.
Aficiones de Calvin
"Circularidad" es posiblemente visual; Veré qué más puedo hacer. Sin embargo, los colores están muertos: {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}1, donde 1 corresponde a 255. No se utilizaron bibliotecas.
DavidC
@ Calvin'sHobbies El problema parece reducirse a si MorphologicalComponentssatisface o viola sus reglas. Una vez que se sabe a qué grupo pertenece cada píxel, hay muchas formas, incluido un recuento de píxeles sin procesar, para determinar qué figura es cuál.
DavidC
Voy a decir que viola las reglas, ya que es muy posiblemente una función de visión por computadora, y le da a Mathematica una ventaja injusta. Estoy de acuerdo en que los colores deben ser correctos, pero se ven claramente en su imagen (el rojo es (255,0,22)cuando verifico en Paint). No tengo Mathematica, así que no puedo correr para asegurarme.
Aficiones de Calvin
7

Mathematica, 354 345 314 291 288

Aún jugando al golf, podría acortarse con algunos caracteres más, pero el rendimiento se vuelve insoportable. Utiliza la varianza 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])])&

Con espaciado:

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

Pruebas:

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 de Mathematica

Aquí está completamente sin golf. Agregará explicaciones más 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
Dr. belisario
fuente
2

Python, 579 577 554 514 502 501 bytes

Para cada forma, la inundación la llena, luego calcula la distancia entre el centroide y el punto más lejano.

entonces la superficie real de la forma se compara con la superficie de un triángulo, cuadrado, disco o rueda que tendría el mismo tamaño.

importar matemática ; de PIL . Importación de imagen *; A , R , _ , I = abs , rango ( 300 ), 255 , abierto ( sys . Argv [ 1 ]). convertir ( 'P' ); Q = I . load () para j en R : para i en R : si Q [ 

 
  i , j ] == _ : 
   X , Y , s , z , p = 0 , 0 , 0 , [], [( i , j )] mientras p : 
    a , b = n = p . pop () si no ( Q [ n ]! = _ o n en z ): 
     X + = a ; Y + =
   
     b ; z + = [ n ]; p + = [( a , b - 1 ), ( a + 1 , b ), ( a , b + 1 ), ( a - 1 , b )]; s + = 1 
   r = max ([ matemáticas . hypot ( X / s - x , Y / s - y ) para x , y en z ]); C = { 1 : A ( s - ( 1.4 * r ) ** 2 ), 2 : A ( s - r * r / 3 ), 3 : A ( s - matemáticas . Pi * r * r ), 4 : A ( s - 2.5 * r * r )} para p en z
   : 
    Q [ p ] = min ( C , clave = C . Obtener ) 
I . putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ]) 
I . show ()
dieter
fuente
1

C # 1086 bytes

Sin embargo, otra solución de inundación, solo para el registro ya que no hay una versión de C # aquí. Al igual que Quincunx, quería demostrarme a mí mismo que puedo hacerlo y no hay mucha diferencia en su enfoque en Java.

  • Esta solución no utiliza ninguna recursividad (sino una pila) porque seguí corriendo en StackOverflows.
  • La detección de píxeles de borde se simplifica al observar los 4 píxeles siguientes, si alguno de ellos es negro, la corriente es un píxel de borde.

Acepta cada formato de imagen.

  • Parámetro 1 = InputPath
  • Parámetro 2 = OutputPath

Probablemente se puede quitar algunos caracteres eliminando todas las cosas estáticas y creando una instancia de Programa.

Versión legible:

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

Golfizado:

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
fuente