Квадрат, Круг, Треугольник,… Механизм?

69

Используя Algodoo и Paint, я сделал эти шесть 300 × 300 монохроматических изображений четырех удобных форм:

Изображение 1 Изображение 2 Изображение 3 Изображение 4 Изображение 5 Изображение 6

Этот класс изображений имеет следующие свойства:

  • Они всегда имеют размер 300 × 300 пикселей, монохроматический (только черно-белый) и имеют ровно четыре белых области, которые соответствуют квадрату, кругу, треугольнику и шестерне.
  • Формы никогда не пересекаются и не касаются друг друга, а также не касаются границы изображения и не выходят за границы.
  • Формы всегда имеют одинаковый размер, но их можно вращать и располагать любым способом.

(Фигуры также имеют равные области, хотя при растеризации таким образом их число пикселей вряд ли будет точно эквивалентным.)

Вызов

Напишите самую короткую возможную программу или функцию, которая берет в имени файла такого изображения и поворачивает все белые пиксели ...

  • красный, (255, 0, 0)если они на площади.
  • синий, (0, 0, 255)если они в кругу.
  • зеленый, (0, 255, 0)если они в треугольнике.
  • желтый, (255, 255, 0)если они в передаче.

например

Изображение 1 окрашено в

подробности

Ваша программа должна работать эффективно для всех возможных входных изображений. (Будут введены только действительные 300 × 300 монохроматические изображения.) Шесть изображений, которые я предоставил, являются лишь примерами, вы не можете жестко запрограммировать их вывод в вашу программу.

Вы не можете использовать библиотеки или функции компьютерного зрения, встроенные или внешние. Суть в том, чтобы сделать это, используя ваши собственные операции на уровне пикселей. Вы можете использовать библиотеки изображений, которые просто позволяют открывать и изменять изображения (например, PIL для Python).

Вы можете использовать любые распространенные форматы файлов изображений без потерь для ввода и вывода, если вы придерживаетесь цветовой схемы.

Вы можете взять имя файла изображения в качестве аргумента функции, из стандартного ввода или из командной строки. Выходное изображение может быть сохранено в новый файл, тот же файл или просто отображено.

счет

Представление с наименьшим количеством байтов выигрывает. Я могу протестировать материалы с дополнительными изображениями, чтобы определить их достоверность.

Кальвин Хобби
источник
Можем ли мы предположить, что вход черно-белый без сглаживания? Если нет, можем ли мы удалить сглаживание из сглаженных входов?
Джон Дворжак
@JanDvorak Да. Под монохромным я подразумеваю только черное и белое, поэтому не может быть сглаживания.
Увлечения Кэлвина
1
Можем ли мы указать конкретный формат ввода более точно, чем просто расширение файла? А именно, я хотел бы, чтобы я получил вход ASCII PBM без каких-либо комментариев внутри.
Джон Дворжак
12
Итак ... Я пытался решить это, и я получил этот образ . Не совсем уверен, как, но эй, это выглядит причудливо. : P
Дверная ручка
2
Я не хочу публиковать свое решение, так как это та же идея, что и у Элл, но хуже. Но я просто хочу сказать, что это было приятное небольшое испытание :)
Крис Бурт-Браун

Ответы:

8

J - 246 224 185 байтов

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

Это было весело!

Я повторно использовал часть связанных компонентов, которую использовал для задачи «Я в самой большой комнате» , и использовал соотношение между средним и максимальным расстоянием всех точек до центра каждого компонента. Я согласился с этим, так как он инвариантен как по масштабу, так и по вращению, и, по-видимому, достаточно хорош, чтобы различать фигуры как дано. Ранжирование этого значения от низкого до высокого дает мне порядок круга, снаряжение, квадрат и треугольник, используемые для перестановки цветовой карты.

Отображает результат, используя аддон viewmap. Не используются наборы инструментов, кроме чтения и вывода файлов.

Надежность, кажется, не является обязательным требованием, это занимает 18 байт. Еще 2 ненужных пробела, замененных &.>на &>in ratioи &.:на &:dcent еще на 2 байта.

Огромный выигрыш как в краткости, так и в производительности compиспользования переключения вместо cut( ;.). Таким образом, изображение копируется и сдвигается во всех 8 направлениях, а не сканируется с окном 3х3.

idФункция была до смешного сложной для того, что это нужно делать. Теперь он присваивает идентификаторы пикселям в объектах, умножая изображение на массив уникальных чисел, следовательно, устанавливая BG на ноль.

Код немного более объяснен:

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

Это немного долго, чтобы объяснить подробно, но подойдет, если есть интерес.

jpjacobs
источник
Верхний правый пиксель гарантированно будет bg. Согласно ОП «Формы никогда не пересекаются и не касаются друг друга, а также не касаются границы изображения и не выходят за границы».
Доктор Велизарий
Спасибо, это полезно. (на самом деле я имел в виду левый верхний пиксель, первый в равеле). Это сбивает обнаружение фона (22 байта).
jpjacobs
Резко уменьшена длина и увеличена производительность :)
jpjacobs
29

Mathematica, 459 392 байта

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

Я мог бы сэкономить еще 6 байтов, превращаясь m=1.Mean@a;m=#-m&/@a;в m=#-Mean@a&/@a;, но это значительно увеличивает время выполнения, что раздражает для тестирования. (Обратите внимание, что это две оптимизации: извлечение вычислений Mean@aвне цикла и использование точных символьных типов вместо чисел с плавающей запятой. Интересно, что использование точных типов намного важнее, чем вычисление среднего значения в каждой итерации.)

Так что это подход номер три:

  • Обнаружение областей по заливке.
  • Найти приблизительный центр каждой области, усредняя все координаты пикселей.
  • Теперь для всех пикселей в форме построим расстояние от угла до этого центра:

    введите описание изображения здесь

    Треугольник имеет 3 четких максимума, квадрат 4, шестерню 16, а круг имеет тонны из-за наложения колебаний вокруг постоянного радиуса.

  • Мы находим количество максимумов, глядя на срезы в 300 пикселей (упорядоченные по углу), и подсчитываем срезы, где пиксель в положении 150является максимумом.
  • Затем мы просто окрашиваем все пиксели в зависимости от количества пиков (круг больше 16, и обычно получается около 20 пиков из-за размера срезов).

Для справки: если я использую идею Элл и просто сортирую регионы по наибольшему расстоянию между любым пикселем и центром, я могу сделать это в 342 байта:

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

Но я не намерен конкурировать с этим, пока все остальные используют свои собственные оригинальные алгоритмы вместо того, чтобы играть в чужие алгоритмы.

Мартин Эндер
источник
Самое интересное решение!
CSharpie
25

Ява, 1204 1132 1087 1076

Просто чтобы доказать себе, что я могу это сделать.

Я включил импорт прямо рядом с объявлениями функций; это должно быть вне класса, чтобы это работало:

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

Неуправляемый (и пригодный для эксплуатации; т.е. добавленный шаблон):

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

Это работает, перебирая каждый пиксель изображения и заполняя каждый раз, когда мы достигаем «дыры». Мы добавляем каждый результат заливки как a Set<Point>к Set. Затем мы определяем, какая форма какая. Это делается путем просмотра количества граничных пикселей фигуры. Я определил границу как движение рыцаря от черной плитки, так как это будет более постоянным между поворотами и тому подобным. Когда мы делаем это, становится ясно, что формы могут быть отсортированы по этому значению: круг, квадрат, треугольник, шестеренка. Поэтому я сортирую и устанавливаю все пиксели этой формы на правильный цвет.

Обратите внимание, что изображение, к которому я пишу, напрямую не берется из файла, потому что, если бы я это сделал, Java воспринял бы изображение как черно-белое, и заполнение цветами не сработало бы. Поэтому я должен создать свой собственный образ с TYPE_INT_RGB(который есть 1). Также обратите внимание , что изображение , которое я делаю работу на это 302путем 302; это связано с тем, что алгоритму расстояния Найта не нужно беспокоиться о попытке считывания за пределами изображения. Я исправляю это несоответствие в размерах по телефону i.getSubImage(1,1,300,300). Примечание. Возможно, я забыл исправить это при загрузке изображений, в этом случае изображения имеют ширину 2 пикселя, но за исключением этого факта, они должны быть правильными

Функция перезапишет файл, путь которого передается. Выходы:

введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь введите описание изображения здесь

Джастин
источник
Можно сохранить несколько символов, сократив имя класса, а также аргументы в методе main до «a» или аналогичного.
Райан
@ Райан Они не учитываются. Я только считаю импорт + функции, как это разрешено вопросом.
Джастин
Я думаю, что смогу получить это под 1000 байтов. Надо поработать над этим позже, когда будет время попробовать.
Джастин
20

Python, 571 567 528 байт

Аналогично решению Quincunx, он начинается с заливки каждой фигуры индексом от 1 до 4. Затем он определяет идентичность фигур по радиусу их ограничивающей окружности. Цветовая палитра строится соответственно, и изображение сохраняется как изображение с индексированным цветом.

РЕДАКТИРОВАТЬ: пропустил тот факт, что формы гарантированно не касаются границы изображения. Короче, тогда!

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

Принимает имя файла ввода в командной строке и записывает вывод в o.png.

флигель
источник
2
Ага, это намного проще, чем то, что я пытаюсь сделать. +1
Мартин Эндер
7

Mathematica 225


Обновление :

ОП решил, что этот подход использует функции компьютерного зрения, поэтому он больше не работает. Я оставлю это отправленным однако. Возможно, кто-то может найти это интересным.


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 возвращает изображение в виде матрицы из 0 и 1.

Flatten преобразует эту матрицу в список.

Morphological Componentsнаходит 4 кластера пикселей и назначает отдельное целое число 1, 2, 3, 4 каждому пикселю в соответствии с кластером. 0 зарезервировано для (черного) фона.

ComponentMeasurements проверяет округлость кластеров.

От большинства к наименее круглым всегда будет: круг, квадрат, треугольник и шестеренка.

ReplacePart заменяет каждое целое число компонента соответствующим цветом RGB, используя сортировку по кругу.

Partition...Dimensions[m][[2]] берет список цветов пикселей и возвращает матрицу тех же размеров, что и входное изображение.

Image преобразует матрицу цветов пикселей в цветное изображение.

входные

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

выходы

DavidC
источник
147 символов:f@i_:=Image[#/.Append[Thread[Ordering[Last/@ComponentMeasurements[#,"Circularity"]]->{Yellow,Green,Red,Blue}],0->Black]]&@MorphologicalComponents@i
алефальфа
Незначительный момент: ваши цвета не имеют правильных значений RGB. Важный момент: я не уверен, что считаю это не использованием библиотек или функций компьютерного зрения.
Увлечения Кэлвина
«Круглость» возможно визуальная; Я посмотрю, что еще я могу сделать. Однако цвета не отображаются на:, {RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]}где 1 соответствует 255. Библиотеки не использовались.
DavidC
@ Calvin'sHobbies Проблема, похоже, сводится к тому, MorphologicalComponentsудовлетворяет или нарушает ваши правила. Как только кто-то знает, к какому кластеру принадлежит каждый пиксель, существует множество способов, включая общее количество пикселей, чтобы определить, какая фигура является какой.
DavidC
Я собираюсь сказать, что это нарушает правила, так как это очень вероятно функция компьютерного зрения, и это дает Mathematica несправедливое преимущество. Я согласен с тем, что цвета должны быть правильными, но они явно выглядят на вашем изображении (красный - (255,0,22)когда я проверяю в Paint). У меня нет Mathematica, поэтому я не могу бежать, чтобы убедиться.
Увлечения Кэлвина,
7

Mathematica, 354 345 314 291 288

Все еще игра в гольф, может быть сокращена еще на несколько символов, но производительность становится невыносимой. Использует дисперсию для определения форм:

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

С интервалом:

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

Тестирование:

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

Математическая графика

Здесь это совершенно безвкусно. Позже добавлю объяснения:

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 байт

Для каждой фигуры заполните ее, затем вычислите расстояние между центроидом и самой дальней точкой.

затем реальная поверхность формы сравнивается с поверхностью треугольника, квадрата, диска или колеса, которые имели бы одинаковый размер.

импорт математики ; из ПИЛ . Импорт изображений *; A , R , _ , I = abs , диапазон ( 300 ), 255 , открытый ( sys . Argv [ 1 ]). конвертировать ( 'P' ); Q = я . load () для j в R : для i в R : если Q [ 

 
  i , j ] == _ : 
   X , Y , s , z , p = 0 , 0 , 0 , [], [( i , j )], а p : 
    a , b = n = p . pop () если нет ( Q [ n ]! = _ или n в z ): 
     X + = a ; Y + =
   
     б ; z + = [ n ]; p + = [( a , b - 1 ), ( a + 1 , b ), ( a , b + 1 ), ( a - 1 , b )]; s + = 1 
   г = макс ([ математике . hypot ( Х / с - х , У / с - у ) для х , у в z ]); C = { 1 : A ( s - ( 1,4 * r ) ** 2 ), 2 : A ( s - r * r / 3 ), 3 : A ( s - математика . Pi * r * r ), 4 : A ( s - 2.5 * r * r )} для p в z
   : 
    Q [ р ] = мин ( С , ключ = С . Получаем ) 
I . putpalette ([ 0 , 0 , 0 , _ ] * 3 + [ _ , _ , 0 ]) 
Я . показать ()
Dieter
источник
1

C # 1086 байт

Еще одно решение для заполнения, просто для записи, поскольку здесь нет версии на C #. Как и Quincunx, я хотел доказать себе, что могу это сделать, и его подход в Java не сильно отличается.

  • Это решение не использует никакой рекурсии (кроме стека), потому что я продолжал работать в StackOverflows.
  • Обнаружение пограничных пикселей упрощается, если посмотреть на 4 следующих пикселя, если какой-либо из них является черным, текущий является пограничным пикселем.

Он принимает каждый формат изображения.

  • Параметр 1 = InputPath
  • Параметр 2 = Выходной путь

Вероятно, можно удалить несколько символов, удалив все статические элементы и создав экземпляр Program.

Читаемая версия:

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

Golfed:

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
источник