【问题标题】:How to sort colors in two dimensions?如何在二维中对颜色进行排序?
【发布时间】:2017-12-11 20:09:27
【问题描述】:

我目前正在从事一个爱好项目,以自动解决流行手机游戏 I Love Hue 中的谜题。游戏可用here

基本上,游戏的整个前提是,你会得到一堆按网格组织的彩色矩形块。您可以交换大多数块,除了一些用黑点标记的固定块。游戏的目的是交换方块,这样你就可以得到二维的色谱。颜色被排序,使得每个块的颜色大约是它周围颜色的平均值。 (抱歉,我不知道任何颜色理论,但我可能正在寻找一个词。)这是一个典型的谜题:

我已经能够通过 adb 截取屏幕截图,从块中提取 RGB 矩阵并标记哪些块是“固定的”。我在这个问题的实际算法部分遇到了麻烦。

这是我到目前为止所做的:

  1. 将 RGB 转换为 HSV 并在一维列表中按色调对颜色进行排序。这给了我一个光谱,但我不知道如何将此结果转换为二维。
  2. 保留 RGB 中的颜色并尝试使用单一颜色。我可能在这里可以做一些多元微积分,但困难在于某些颜色共享它们的一个或多个 RGB 值。有必要考虑所有三种颜色。
  3. 使用欧几里得距离求每对颜色之间的距离。我知道最终目标是使这个距离在相邻颜色中最小,但是二维网格使这变得更加困难。
  4. 使用欧几里得距离,我通过查看相邻块颜色的欧几里得距离,开发了一个衡量某个网格的理想程度的度量标准。但是,我找不到一种有效的算法来计算达到理想状态所需的交换。

【问题讨论】:

  • 好吧,我没玩过游戏,但从游戏的封面看,似乎有 4 种可能的颜色方向可以实现平滑的完成......只是想知道游戏是否接受所有4种组合。还是总是有一些固定的块来确保只存在一种解决方案?
  • @TheoWalton 总是有一些固定的块来确保只存在一个解决方案。
  • 您引用的图像看起来像是混合了不同的色调和饱和度。颜色饱和度从图像中心径向向图像边界增加。色调随图像中心周围的角度而变化。这样,您在极坐标中就有了 2 个维度。黑点色块现在声明了在不规则网格上进行插值的控制点。但是,在没有看到该游戏的任何其他示例图像的情况下,它仍然是开放的,无论颜色渐变是否始终遵循我上面提到的极坐标。
  • 看起来它们确实在改变色调和饱和度以获得颜色。所以 HSV 确实是正确的起点。
  • 您有没有上图中的数据集示例?我对此很着迷。我认为在两个不同的矩阵中按每个轴上的距离对颜色进行排序是这里的方法,并找到每个轴上每个端点的平均距离。

标签: arrays algorithm sorting colors calculus


【解决方案1】:

如果您有更多 solved 图像,您可以创建 RGB 图形图

所以绘制 3D 图,其中x,y 是像素位置,z 是检查颜色通道(R、G 或 B)。从中您可以确定渐变的一些属性。如果绘图是一个平面,那么您所需要的只是正常的(取自 3 个已知单元格)。如果它是曲面,取决于它得到多少拐点,你可以确定它使用了多大的多项式。从这一切你可以开始解决这个问题。

我会从一些简单的事情开始(假设没有太大的差距或花哨的多项式):

分别处理每个颜色通道。我将只使用静态图块并仅从它们中插入网格颜色。类似于:

没有看到 R,G,B 图表,我无法估计您需要哪种插值。如果图表是线性的,请使用双线性或线性插值。如果不使用更高次的多项式。

因此,您可以填写任何网格单元(具有已知颜色的邻居)。在此之后找到最接近计算颜色的可移动图块(如果单元格已插入所有 3 个通道)并将它们放置(并设置为静态)。

现在只需重复该过程,直到计算完所有单元格。

[Edit1 Dec 14 2017] 一些额外的注释和资料

很好奇,今天有时间,所以我试了一下。首先,我在 C++/VCL 中创建游戏,它将您的图像作为输入(裁剪和调整大小)。然后我手动对瓷砖进行排序并绘制图表:

白点表示瓷砖放置正确(匹配插值颜色)。圆点周围的彩色圆圈是插值后的颜色(为了进行视觉比较,您需要放大才能看到它们)。

如您所见,R、G、B 3D 图看起来是线性的,因此(双)线性插值应该足够了。

如果我只尝试对行进行线性插值,则只有求解器会立即解决难题。但是,当我为列(已知单元之间有更多未知单元)编写相同的代码时,求解器开始进行一些不正确的放置(使整个内容无效,因此错误的白点)。

我也尝试过 HSL,但过了一会儿我因为撞墙而将其扔掉,因为 Hue 可以随时跨越0360 度数这与没有交叉的情况无法区分。为此,它需要一些来自相邻解决区域的启发式或互相关,这对于我的口味来说编码太多了。没有它,结果会比使用 RGB 更糟糕。

所以现在我正在考虑要么使用双线性插值,要么先解决短距离插值,然后再解决其余部分......

[Edit2 Dec 14 2017] 双线性插值

看起来双线性 RGB 插值解决了所有问题。因此,如果您的电路板装有固定单元,它应该可以工作。如果不是,您需要迭代地求解电路板,然后使用新求解的单元格作为未求解区域的新边界。我也意识到我的 RGB 颠倒了,所以我也修复了它:)。

这里是游戏的C++/VCL源代码(根本没有优化):

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
//---------------------------------------------------------------------------
TForm1 *Form1;
bool _update=false;
//---------------------------------------------------------------------------
const _ILoveHue_state_fixed   =255<<24;
const _ILoveHue_state_unsolved=  0<<24;
const _ILoveHue_state_solved  =  1<<24;
const _ILoveHue_render_board=0;
const _ILoveHue_render_graph=1;
//---------------------------------------------------------------------------
int rgbdist(DWORD c0,DWORD c1)  // AABBGGRR
    {
    int r0,g0,b0,r1,g1,b1;
    r0=( c0     &255); r1=( c1     &255);
    g0=((c0>> 8)&255); g1=((c1>> 8)&255);
    b0=((c0>>16)&255); b1=((c1>>16)&255);
    r0-=r1; g0-=g1; b0-=b1;
    return (r0*r0)+(g0*g0)+(b0*b0);
    }
//---------------------------------------------------------------------------
class ILoveHue
    {
public:
    // variables
    bool _redraw;               // redraw needed?
    Graphics::TBitmap *bmp;     // screen buffer
    int sxs,sys,mxs,mys,gxs,gys;// screen,map,grid cell resolution
    DWORD **map,**imap;         // map[y][x] actual and interpolated
    int mx,my,mx0,my0;          // mouse position state actual and last
    TShiftState sh,sh0;         // mouse buttons and spec keys state actual and last
    int render_mode;
    // class constructors and destructors
    ILoveHue()  { bmp=new Graphics::TBitmap; bmp_resize(1,1); map=NULL; imap=NULL; mxs=0; mys=0; mx=-1; my=-1; mx0=-1; my0=-1; gxs=1; gys=1; render_mode=_ILoveHue_render_board; }
    ~ILoveHue() { map_free(); if (bmp) delete bmp; }
    ILoveHue(ILoveHue& a)   { *this=a; }
    ILoveHue* operator = (const ILoveHue *a) { *this=*a; return this; }
    //ILoveHue* operator = (const ILoveHue &a) { ...copy... return this; }

    // game/Window API and stuff
    void map_free()                             // relese map
        {
        if ( map) { if ( map[0]) delete[]  map[0]; delete[]  map; }  map=NULL; mxs=0; mys=0;
        if (imap) { if (imap[0]) delete[] imap[0]; delete[] imap; } imap=NULL;
        }
    void map_resize(int x,int y)                // resize/allocate map
        {
        _redraw=true;
        if ((x==mxs)&&(y==mys)) return; map_free();
         map=new DWORD*[y]; if ( map==NULL) return;  map[0]=new DWORD[x*y]; if ( map[0]==NULL) return;
        imap=new DWORD*[y]; if (imap==NULL) return; imap[0]=new DWORD[x*y]; if (imap[0]==NULL) return;
        mxs=x; mys=y; for (x=mxs,y=1;y<mys;y++,x+=mxs) { map[y]=map[0]+x; imap[y]=imap[0]+x; }
        if (mxs) gxs=sxs/mxs; else gxs=1;
        if (mys) gys=sys/mys; else gys=1;
        }
    void bmp_resize(int x=-1,int y=-1)          // resize bmp
        {
        _redraw=true;
        if ((x>=0)&&(y>=0)) bmp->SetSize(x,y);
        bmp->HandleType=bmDIB;
        bmp->PixelFormat=pf32bit;
        sxs=bmp->Width;
        sys=bmp->Height;
        if (mxs) gxs=sxs/mxs; else gxs=1;
        if (mys) gys=sys/mys; else gys=1;
        }
    void bmp_load(AnsiString file)              // init game from image (map must be resized already)
        {
        _redraw=true;
        // load file
        bmp->LoadFromFile(file);
        bmp_resize();
        // convert to map
        int x,y;
        DWORD *p,c;
        for (y=0;y<mys;y++)
         for (p=(DWORD*)bmp->ScanLine[(y*gys)+(gys>>1)],x=0;x<mxs;x++)
            {
            c=p[(x*gxs)+(gxs>>1)+4]&0x00FFFFFF;         // near mid point (0<<24 is unsolved state)
            c=((c>>16)&0x000000FF)                      // RGB -> BGR (file has reverse RGB order than bmp)
             |((c<<16)&0x00FF0000)
             |( c     &0x0000FF00);
            map[y][x]=c;
            c=p[(x*gxs)+(gxs>>1)]&0x00FFFFFF;           // mid point
            if ((((c)|(c>>8)|(c>>16))&255)<64)          // ~max(R,G,B)<32
             map[y][x]|=_ILoveHue_state_fixed;
            }
        }
    void mouse(int x,int y,TShiftState s)       // handle mouse
        {
        _redraw=true;
        mx=x/gxs;
        my=y/gys;
        sh0=sh; sh=s;
        bool q0=sh0.Contains(ssLeft);
        bool q1=sh .Contains(ssLeft);
        if ((!q0)&&( q1)){ mx0=mx; my0=my; }    // mouse left button down
        if (( q0)&&(!q1))                       // mouse left button up (swap)
            {
            // swap if valid coordinates
            if ((mx0>=0)&&(mx0<mxs)&&(my0>=0)&&(my0<mys)) if (DWORD(map[my0][mx0]&0xFF000000)!=_ILoveHue_state_fixed)
             if ((mx >=0)&&(mx <mxs)&&(my >=0)&&(my <mys)) if (DWORD(map[my ][mx ]&0xFF000000)!=_ILoveHue_state_fixed)
                {
                DWORD c=map[my0][mx0]; map[my0][mx0]=map[my][mx]; map[my][mx]=c;    // swap cells
                map[my0][mx0]&=0x00FFFFFF; map[my0][mx0]|=_ILoveHue_state_unsolved; // set them as unsolved
                map[my ][mx ]&=0x00FFFFFF; map[my ][mx ]|=_ILoveHue_state_unsolved;
                map_solve(false);                                                   // check for solved state
                }
            // clear selection
            mx0=-1; my0=-1;
            }
        }
    void draw()                                 // render game
        {
        _redraw=false;
        int x,y,z,x0,x1,x2,y0,y1,y2,r;
        DWORD c;
        if (render_mode==_ILoveHue_render_board)
            {
            for (y0=0,y1=gys,y2=gys>>1,y=0;y<mys;y++,y0+=gys,y1+=gys,y2+=gys)
             for (x0=0,x1=gxs,x2=gxs>>1,x=0;x<mxs;x++,x0+=gxs,x1+=gxs,x2+=gxs)
                {
                c=map[y][x];
                bmp->Canvas->Pen->Color=TColor(c&0x00FFFFFF);
                if ((x==mx )&&(y==my )) bmp->Canvas->Pen->Color=clYellow;
                if ((x==mx0)&&(y==my0)) bmp->Canvas->Pen->Color=clGreen;
                bmp->Canvas->Brush->Color=TColor(c&0x00FFFFFF);
                bmp->Canvas->Rectangle(x0,y0,x1,y1);

                if (DWORD(c&0xFF000000)!=_ILoveHue_state_fixed)
                    {
                    r=10;
                    bmp->Canvas->Pen->Color=imap[y][x]&0x00FFFFFF;
                    bmp->Canvas->Brush->Style=bsClear;
                    bmp->Canvas->Ellipse(x2-r,y2-r,x2+r,y2+r);
                    bmp->Canvas->Brush->Style=bsSolid;
                    }

                if (DWORD(c&0xFF000000)!=_ILoveHue_state_unsolved)
                    {
                    if (DWORD(c&0xFF000000)==_ILoveHue_state_fixed ) c=clBlack;
                    if (DWORD(c&0xFF000000)==_ILoveHue_state_solved) c=clWhite;
                    r=4;
                    bmp->Canvas->Pen->Color=c;
                    bmp->Canvas->Brush->Color=c;
                    bmp->Canvas->Ellipse(x2-r,y2-r,x2+r,y2+r);
                    }
                }
            }
        if (render_mode==_ILoveHue_render_graph)
            {
            bmp->Canvas->Pen->Color=clBlack;
            bmp->Canvas->Brush->Color=clBlack;
            bmp->Canvas->Rectangle(0,0,sxs,sys);
            r=13; x0=15; y0=sys-15;
            int c=r*double(256.0*cos(55.0*M_PI/180.0));
            int s=r*double(256.0*sin(55.0*M_PI/180.0));
            bmp->Canvas->Pen->Color=clRed;
            for (y=0;y<mys;y++)
             for (x=0;x<mxs;x++)
                {
                z=(map[y][x])&255;
                x1=x0+(x*r)+((y*c)>>8);
                y1=y0      -((y*s)>>8);
                bmp->Canvas->MoveTo(x1,y1);
                bmp->Canvas->LineTo(x1,y1-z);
                } x0=x1+5;
            bmp->Canvas->Pen->Color=clGreen;
            for (y=0;y<mys;y++)
             for (x=0;x<mxs;x++)
                {
                z=(map[y][x]>>8)&255;
                x1=x0+(x*r)+((y*c)>>8);
                y1=y0      -((y*s)>>8);
                bmp->Canvas->MoveTo(x1,y1);
                bmp->Canvas->LineTo(x1,y1-z);
                } x0=x1+5;
            bmp->Canvas->Pen->Color=clBlue;
            for (y=0;y<mys;y++)
             for (x=0;x<mxs;x++)
                {
                z=(map[y][x]>>16)&255;
                x1=x0+(x*r)+((y*c)>>8);
                y1=y0      -((y*s)>>8);
                bmp->Canvas->MoveTo(x1,y1);
                bmp->Canvas->LineTo(x1,y1-z);
                }

            }
        }
    // Solver
    void map_solve(bool _solve) // check for solved state and try to solve if _solve is true
        {
        _redraw=true;
        const int _thr=10;  // color comparison threshold
        int x,y,x0,x1,y0,y1,xx,yy;
        int r0,g0,b0,r,g,b;
        int r1,g1,b1;
        int r2,g2,b2;
        int r3,g3,b3;
        DWORD c;

        // compute interpolated colors to imap (wanted solution)
        for (x=0;x<mxs;x++)
         for (y=0;y<mys;y++)
          if (DWORD(map[y][x]&0xFF000000)!=_ILoveHue_state_fixed)
            {
            for (x0=-1,xx=x;xx>= 0;xx--) if (DWORD(map[y][xx]&0xFF000000)==_ILoveHue_state_fixed){ x0=xx; break; }
            for (x1=-1,xx=x;xx<mxs;xx++) if (DWORD(map[y][xx]&0xFF000000)==_ILoveHue_state_fixed){ x1=xx; break; }
            for (y0=-1,yy=y;yy>= 0;yy--) if (DWORD(map[yy][x]&0xFF000000)==_ILoveHue_state_fixed){ y0=yy; break; }
            for (y1=-1,yy=y;yy<mys;yy++) if (DWORD(map[yy][x]&0xFF000000)==_ILoveHue_state_fixed){ y1=yy; break; }
            c=0;
            if (int(x0|x1|y0|y1)>=0)
                {
                // bilinear interpolation
                c=map[y0][x0]; r0=c&255; g0=(c>>8)&255; b0=(c>>16)&255;
                c=map[y0][x1]; r1=c&255; g1=(c>>8)&255; b1=(c>>16)&255;
                c=map[y1][x0]; r2=c&255; g2=(c>>8)&255; b2=(c>>16)&255;
                c=map[y1][x1]; r3=c&255; g3=(c>>8)&255; b3=(c>>16)&255;
                r0=r0+(r1-r0)*(x-x0)/(x1-x0);
                g0=g0+(g1-g0)*(x-x0)/(x1-x0);
                b0=b0+(b1-b0)*(x-x0)/(x1-x0);
                r1=r2+(r3-r2)*(x-x0)/(x1-x0);
                g1=g2+(g3-g2)*(x-x0)/(x1-x0);
                b1=b2+(b3-b2)*(x-x0)/(x1-x0);
                r =r0+(r1-r0)*(y-y0)/(y1-y0);
                g =g0+(g1-g0)*(y-y0)/(y1-y0);
                b =b0+(b1-b0)*(y-y0)/(y1-y0);
                c=(r)+(g<<8)+(b<<16);
                }
            imap[y][x]=c;
            }

        // compute solved state
        for (x=0;x<mxs;x++)
         for (y=0;y<mys;y++)
          if (DWORD(map[y][x]&0xFF000000)!=_ILoveHue_state_fixed)
            {
            map[y][x]&=0x00FFFFFF;
            if (rgbdist(map[y][x],imap[y][x])<_thr) map[y][x]|=_ILoveHue_state_solved;
             else                                   map[y][x]|=_ILoveHue_state_unsolved;
            }

        // solver/checker
        if (_solve)
            {
            // process all unsolved cells
            for (x=0;x<mxs;x++)
             for (y=0;y<mys;y++)
              if (DWORD(map[y][x]&0xFF000000)==_ILoveHue_state_unsolved)
               // find match in unsolved cells
               for (xx=0;xx<mxs;xx++)
                for (yy=0;yy<mys;yy++)
                 if (DWORD(map[yy][xx]&0xFF000000)==_ILoveHue_state_unsolved)
                  if (rgbdist(map[yy][xx],imap[y][x])<_thr)
                    {
                    // swap if found
                    c=map[yy][xx];
                    map[yy][xx]=map[y][x];
                    map[y][x]=(c&0x00FFFFFF)|_ILoveHue_state_solved;
                    }
            }
        }
    } gam;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
    {
    gam.map_resize(7,9);
    gam.bmp_load("map.bmp");
    gam.map_solve(false);
    _update=true;
    ClientWidth=gam.sxs;
    ClientHeight=gam.sys;
    _update=false;
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
    {
    gam.render_mode=_ILoveHue_render_board;
    gam.draw();
    gam.bmp->SaveToFile("map.bmp");
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender){ gam.draw(); Canvas->Draw(0,0,gam.bmp); }
void __fastcall TForm1::FormResize(TObject *Sender){ if (_update) return; gam.bmp_resize(ClientWidth,ClientHeight); }
void __fastcall TForm1::Timer1Timer(TObject *Sender){ if (gam._redraw) FormPaint(Sender); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y){ gam.mouse(X,Y,Shift); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y){ gam.mouse(X,Y,Shift); }
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y){ gam.mouse(X,Y,Shift); }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
    {
    if (Key=='S') gam.map_solve(true);                      // try to solve
    if (Key=='M') { gam.render_mode^=1; gam._redraw=true; } // swap render modes
    if (Key==115) gam.bmp->SaveToFile("screenshot.bmp");    // [F4] screenshot
    }
//---------------------------------------------------------------------------

它是 BDS2006 中的单个表单应用程序,上面有单个 40ms 计时器。所以只需添加事件......你可以忽略 VCL 渲染和窗口的东西。重要的是类和其中的solve() 函数。它用于正确放置检查和求解(取决于_solve bool)。这是输入图像 ma​​p.bmp

我没有编写适当的保存/加载状态函数,而是选择直接使用位图本身(浪费空间但几乎没有代码工作)。

地图本身是 2D 32 位 DWORD 数组,形式为 SSBBGGRR hex,其中 SS 是单元格的标志(固定/已解决/未解决)。

这里用源码编译demo

阅读readme.txt 了解更多信息。求解后的结果如下(按[S]):

您可以(看不到)看到圆圈消失,因为双线性插值颜色更接近您的输入。

程序期望大小为 7x9 的网格,图像的分辨率并不重要。颜色从单元格的中点(黑点)稍微向右(平铺颜色)采样

为了提高效率,你可以做两件事:

  1. 添加/使用包含未解决单元格的列表

    而不是遍历整个地图,只遍历未解决的单元格列表。

  2. 通过三角形搜索将T(N^2)搜索转换为T((N^2)/2)

    这仍然是O(N^2),但是常数时间更小。

  3. 使用 3D RGB LUT 表

    对于大型网格,您可以创建 32K 条目 3D LUT 表以在 O(1) 中查找搜索到的匹配单元格。只需将 RGB 转换为 15 位颜色并使用

    DWORD LUT[32][32][32];
    

    LUT[r][g][b]=row+(column&lt;&lt;16); 这样您就可以知道每种颜色的位置。所有未使用的颜色设置为0xFFFFFFFF。这里是使用这种技术实现类似目的的示例:

    在代码中查找recolor[32][32][32]...粗略的 15 位颜色可能不足以达到此目的,因此您可能需要更多位,例如 18 位,从而导致 256K 条目仍然可以管理。

    创建这个 LUT 需要 O(N) 时间,但使用和维护它只需 O(1) 时间。

【讨论】:

    【解决方案2】:

    我不知道这是否可行。我只是为了好玩而写的,无法对其进行真正的测试。如果您试一试并对此发表评论,我们将不胜感激。

            struct pixel
            {
                public int R;
                public int G;
                public int B;
                public bool Fixed;
                public pixel(int r, int g, int b, bool _fixed)
                {
                    this.R = r; this.G = g; this.B = b; this.Fixed = _fixed;
                }
                public int DistanceSQ(pixel px)
                {
                    int r = this.R - px.R;
                    int g = this.G - px.G;
                    int b = this.B - px.B;
                    return r * r + g * g + b * b;
                }
                public override string ToString()
                {
                    return string.Format("{0} {1} {2} {3}", this.R, this.G, this.B, this.Fixed);
                }
                public override int GetHashCode()
                {
                    return this.R.GetHashCode() ^ this.G.GetHashCode() ^ this.B.GetHashCode();
                }
                public override bool Equals(object obj)
                {
                    pixel px = (pixel)obj;
                    return this.R == px.R && this.G == px.G && this.B == px.B;
                }
            }
            static void sort(pixel[,] img)
            {            
                List<pixel> lst = new List<pixel>();
                foreach (pixel px in img)
                    if (!px.Fixed)
                        lst.Add(px);
    
                int rows = img.GetLength(0);
                int cols = img.GetLength(1);
                while (lst.Count > 0)
                    for (int row = 0; row < rows; row++)
                        for (int col = 0; col < cols; col++)
                            if (!img[row, col].Fixed)
                            {
                                pixel[] neighbors = getFixedNeighbors(img, row, col, rows, cols).ToArray();
                                int min = int.MaxValue;
                                pixel nearest = new pixel();
                                foreach (pixel n in lst)
                                {
                                    int dist = neighbors.Select((a) => a.DistanceSQ(n)).Sum();
                                    if (dist < min)
                                    {
                                        min = dist;
                                        nearest = n;
                                    }
                                }
                                nearest.Fixed = true;
                                img[row, col] = nearest;
                                lst.Remove(nearest);
                                if (lst.Count == 0)
                                    return;
                            }
            }
            private static IEnumerable<pixel> getFixedNeighbors(pixel[,] img, int row, int col, int rows, int cols)
            {
                for (int r = Math.Max(0, row - 1); r < Math.Min(row + 2, rows); r++)
                    for (int c = Math.Max(0, col - 1); c < Math.Min(col + 2, cols); c++)
                        if (img[r, c].Fixed)
                            yield return img[r, c];
    
            }
    
    
    
            //test
            {
                bool b0 = false; bool b1 = true;//for easy editing 
                {
                    pixel[,] img = new pixel[3, 4];
                    img[0, 0] = new pixel(0, 0, 0, b1); img[1, 0] = new pixel(0, 1, 0, b0); img[2, 0] = new pixel(0, 2, 0, b1);
                    img[0, 1] = new pixel(1, 0, 0, b0); img[1, 1] = new pixel(1, 1, 0, b0); img[2, 1] = new pixel(1, 2, 0, b0);
                    img[0, 2] = new pixel(2, 0, 0, b0); img[1, 2] = new pixel(2, 1, 0, b0); img[2, 2] = new pixel(2, 2, 0, b0);
                    img[0, 3] = new pixel(3, 0, 0, b1); img[1, 3] = new pixel(3, 1, 0, b0); img[2, 3] = new pixel(3, 2, 0, b1);
                    sort(img);
                }
                {
                    pixel[,] img = new pixel[3, 4];
                    img[0, 0] = new pixel(0, 0, 0, b1); img[1, 0] = new pixel(0, 1, 0, b0); img[2, 0] = new pixel(0, 2, 0, b1);
                    img[0, 1] = new pixel(2, 0, 0, b0); img[1, 2] = new pixel(2, 1, 0, b0); img[2, 2] = new pixel(2, 2, 0, b0);
                    img[0, 2] = new pixel(1, 0, 0, b0); img[1, 1] = new pixel(1, 1, 0, b0); img[2, 1] = new pixel(1, 2, 0, b0);
                    img[0, 3] = new pixel(3, 0, 0, b1); img[1, 3] = new pixel(3, 1, 0, b0); img[2, 3] = new pixel(3, 2, 0, b1);
                    sort(img);
                }
            }
    

    代码很简单。它将未评级的保留在列表中,并在找到位置时将其删除。为了决定应该为一个位置选择哪种颜色,选择具有最小平方距离和的颜色。 Sqrt 不是必需的,因为我们只需要它进行比较。

    “排序”是改变不固定像素位置的主要功能。此函数的输入是像素的行列数组。 “排序”函数对此数组进行更改。

    【讨论】:

    • 添加一些关于它如何工作的文字。纯未注释的代码对于除其作者之外的任何人来说都很难理解,即使它看起来很简单......
    • @Spektre 我已经添加了一些关于它的解释。我希望它足够了。该代码更好地解释了自己。我认为我们不需要为这个问题处理插值;但我不太确定。谢谢。
    • 稍微好一点,但我觉得让它对其他人有用。您至少应该添加主要变量是什么和含义以及拼图的输入和输出的存储方式(格式)。跨度>
    • 我不擅长解释事情。我希望它现在更好。
    • 我在我的 C++ 解决方案尝试中添加了 edit2(看起来它正在工作)并链接到我的答案的 win32 演示。看看solve() 函数。如您所见,插值并没有那么多代码。另一方面,创建交换的组合或轮换(就像你提议的那样)可能会变得可怕并且可能会慢得多......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-19
    • 1970-01-01
    • 2020-06-26
    • 2014-11-07
    • 2013-09-13
    • 2013-07-03
    相关资源
    最近更新 更多