正方形,圆形,三角形……齿轮?


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单色图像。)我提供的六幅图像仅是示例,您可能无法将其输出硬编码到程序中。

您不得使用内置或外部的计算机视觉库或功能。关键是要使用您自己的像素级操作来执行此操作。您可以使用仅允许您打开和修改图像的图像库(例如,用于Python的PIL)。

您可以使用任何常见的无损图像文件格式进行输入和输出,只要您遵守配色方案即可。

您可以从stdin或命令行中将图像文件名作为函数参数。可以将输出图像保存到新文件,相同文件或简单显示。

计分

字节最少的提交将获胜。我可能会用其他图像测试提交的内容,以确定其有效性。


我们是否可以假设输入为黑白且没有抗锯齿?如果没有,是否可以从抗锯齿输入中删除抗锯齿?
约翰·德沃夏克

@JanDvorak是的。单色是指黑白,因此不能进行抗锯齿。
卡尔文的爱好

1
除了文件扩展名,我们是否可以要求更精确的特定输入格式?也就是说,我想要一个不带任何注释的ASCII PBM输入。
约翰·德沃夏克

12
所以...我试图解决这个问题,最终得到了这张图片。不确定如何操作,但嘿,看起来不错。:P
门把手

2
我不想发布我的解决方案,因为它与Ell的想法相同,但更糟。但是我只想说这是一件令人愉快的小挑战:)
克里斯·伯特·布朗

Answers:


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个不必要的空格,代替&.>通过&>ratio&.:通过&:在dcent另外2个字节。

comp使用移位代替cut;.)可以大大缩短和缩短性能。这样,就可以复制图像并在所有8个方向上移动,而不是使用3x3窗口进行扫描。

id功能对于需要执行的操作非常复杂。现在,它通过将图像与唯一数字数组相乘,将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

这一点需要详细解释,但是会很有趣。


右上方像素保证为bg。按照OP的规定,“形状永远不会重叠或彼此接触,也不会接触图像边界或边界。”
belisarius博士2014年

谢谢,这很有帮助。(实际上,我的意思是左上像素,在渐变中的第一个)。这样可以消除后台检测(22字节)。
jpjacobs 2014年

大大减少了长度,提高了性能:)
jpjacobs 2014年

29

Mathematica,459392字节

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

取消高尔夫:

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=1.Mean@a;m=#-m&/@a;转换为m=#-Mean@a&/@a;,我可以再节省6个字节,但这会极大地浪费执行时间,这对于测试很烦人。(请注意,这是二的优化:拉出的计算Mean@a出循环的。使用精确符号类型,而不是浮点数有趣的是,使用确切的类型是很多不是在每个迭代计算的平均值更显著。)

这是方法三:

  • 通过洪水填充来检测区域。
  • 通过平均所有像素坐标来找到每个区域的近似中心。
  • 现在,对于形状中的所有像素,让我们绘制从vs角度到该中心的距离:

    在此处输入图片说明

    由于围绕恒定半径的混叠波动,三角形具有3个清晰的最大值,正方形4个,齿轮16个,圆形具有吨数。

  • 我们通过查看300个像素的切片(按角度排序)来找到最大值,并计算位置上像素150最大的切片。
  • 然后,我们仅根据峰的数量对所有像素进行着色(圆圈的大小超过16,并且由于切片的大小,通常会产生20个左右的峰)。

仅作记录,如果我使用Ell的想法,并仅按任何像素与中心之间的最大距离对区域进行排序,则可以以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 2014年

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

这是通过迭代图像的每个像素并在每次到达“空洞”时进行泛洪填充而起作用的。我们添加每个洪水填写结果作为Set<Point>一个Set。然后我们确定哪种形状。通过查看形状的边界像素数可以完成此操作。我将边界定义为骑士远离黑色瓷砖的举动,因为这样在旋转等之间会保持更恒定。当我们这样做时,很明显可以按照该值对形状进行排序:圆形,正方形,三角形,齿轮。因此,我将所有形状的像素排序并设置为正确的颜色。

请注意,我要写入的图像不是直接从文件中获取的,因为如果这样做,Java会将图像视为黑白图像,并且用颜色填充将不起作用。因此,我必须使用创建自己的映像TYPE_INT_RGB(为1)。另请注意,我正在处理的图像是302by 302;这样一来,奈特的距离算法就不必担心尝试读取图像上的边界。我通过致电来解决大小上的差异i.getSubImage(1,1,300,300)注意:上传图片时,我可能忘记了修复此问题,在这种情况下,图片的宽度过2像素,但除此以外,它们应该是正确的

该函数将覆盖传递路径的文件。输出:

在此处输入图片说明 在此处输入图片说明 在此处输入图片说明 在此处输入图片说明


通过将类名以及main方法中的arg缩短为“ a”或类似名称,可以节省一些字符。
Ryan 2014年

@Ryan那些不算在内。我只考虑问题允许的导入+函数。
贾斯汀

我想我也许可以在1000字节以下获得它。有时间尝试时,必须稍后进行处理。
贾斯汀2014年

20

蟒蛇, 571567 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
马丁·恩德2014年

7

Mathematica 225


更新

OP决定该方法使用计算机视觉功能,因此不再运行。但是,我将其保留。也许有人会发现它有趣。


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]}

输出


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

重点:您的颜色没有正确的rgb值。要点:我不确定我是否认为没有使用计算机视觉库或函数。
加尔文的爱好2014年

“圆度”可以说是视觉上的;我会看看我还能做什么。但是,这些颜色会停在以下位置:{RGBColor[1, 0, 0], RGBColor[0, 1, 0], RGBColor[0, 0, 1], RGBColor[1, 1, 0]},其中1对应于255。未使用任何库。
DavidC 2014年

@ Calvin'sHobbies问题似乎归结为是否MorphologicalComponents满足或违反您的规则。一旦知道了每个像素属于哪个群集,就可以通过多种方法(包括原始像素计数)来确定哪个数字是哪个。
DavidC 2014年

我要说的是,它确实违反了规则,因为它可以说是计算机视觉功能,并且为Mathematica提供了不公平的优势。我同意颜色应该正确,但是在您的图像中颜色看起来很明显(红色是(255,0,22)我在“画图”中签入的时间)。我没有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

Mathematica图形

在这里,它是完全没有意义的。稍后将添加说明:

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字节

对于每种形状,将其充满,然后计算质心和最远点之间的距离。

然后将形状的真实表面与具有相同大小的三角形,正方形,圆盘或轮子的表面进行比较。

导入数学; 来自PIL 图片导入*;- [R _ = ABS 范围300 ),255 开放SYS 的argv [ 1 ])。转换'P' ); Q = 负载()Ĵ ř 用于ř 如果Q [ 

 
  Ĵ ] == _ 
   X ÿ 小号Ž p = 0 0 0 ,[],[(Ĵ )] p 
    一个b = Ñ = p 弹出()如果不是Q [ Ñ !] = _ ñ Ž ):
     X + = 一个; Y + =
   
     b ; z + = [ n ]; p + = [(一个b - 1 ),(一个+ 1 b ),(b + 1 ),(- 1 b )]; 小号+ = 1 
   - [R = 最大值([ 数学hypot将X / 小号- X ÿ / 小号- Ý X ÿ z ])中;c ^ = { 1 小号- (1.4 * - [R )** 2 ),2 小号- - [R * - [R / 3 ),3 小号- 数学PI * - [R * - [R ),4 小号- 2.5 * - [R * - [R )} p ž
   
    Q [ p ] = 分钟Ç = Ç 得到putpalette ([ 0 0 0 _ ] * 3 + [ _ _ 0 ])显示()

1

C#1086字节

由于此处没有C#版本,因此还有一个泛滥的解决方案,仅供参考。像Quincunx一样,我想向自己证明自己可以做到,并且他在Java中的方法没有太大差异。

  • 此解决方案不使用任何递归(而是堆栈),因为我一直遇到StackOverflows。
  • 通过查看接下来的4个像素简化了对边界像素的检测,如果其中任何一个为黑色,则当前为边界像素。

它接受每种图像格式。

  • 参数1 = InputPath
  • 参数2 = OutputPath

通过删除所有静态内容并创建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();
    }
}

打高尔夫球:

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();}}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.