2017-12-11 22 views
5

저는 현재 인기있는 모바일 게임에서 퍼즐을 자동으로 풀기 위해 취미 프로젝트를 진행하고 있습니다. I Love Hue. 이 게임은 here입니다.색상을 2 차원으로 정렬하는 방법은 무엇입니까?

기본적으로 게임의 전제는 격자로 구성된 색이 지정된 직사각형 블록을 많이 제공한다는 것입니다. 검은 색 점으로 표시된 몇 개의 고정 된 블록을 제외하고 대부분의 블록을 바꿀 수 있습니다. 게임의 목적은 블럭을 서로 바꾸어 2 차원 스펙트럼의 색을 얻는 것입니다. 색상은 각 블록의 색상이 대략 주변 색상의 평균이되도록 정렬됩니다. (미안 해요, 난은 (는) 모든 색상이 이론을 모르지만 단어는 내가 찾고 무엇을 거기에 아마.) 다음과 같은 전형적인 퍼즐 모습입니다 : 이미 스크린 샷을 할 수 있었다

img

through adb를 사용하여 블록에서 RGB 행렬을 추출하고 "고정 된"블록을 표시하십시오. 이 문제의 실제 알고리즘 부분에 문제가 있습니다.

는 여기에 지금까지 한 일이다 :

  1. 는 한 차원 목록에 색상으로 색상을 HSV로 RGB 변환 및 정렬. 이것은 나에게 스펙트럼을 제공하지만,이 결과를 두 가지 차원으로 변환하는 방법을 모르겠습니다.
  2. RGB로 색을 그대로두고 단색으로 작업하려고합니다. 아마 여기서 다룰 수있는 다 변수 계산법이있을 수 있지만, 어려움은 일부 색상이 하나 이상의 RGB 값을 공유한다는 사실에 있습니다. 세 가지 색상 모두를 고려해야합니다.
  3. 유클리드 거리를 사용하여 각 색상 쌍 사이의 거리를 찾습니다. 최종 목표는이 거리를 인접한 색상 중에서 가장 작게 설정하는 것이지만 2 차원 격자는이 작업을 더 어렵게 만드는 것입니다.
  4. 유클리드 거리를 사용하여 인접한 블록 색상의 유클리드 거리를보고 특정 격자가 얼마나 이상적인지에 대한 척도를 개발했습니다. 그러나 이상적인 상태로 전환하는 데 필요한 스왑을 파악할 수있는 효율적인 알고리즘을 찾을 수 없습니다. 더 solved 이미지가 있다면
+0

ok 나는 게임을하지는 않았지만 게임의 앞 표지에서 부드러운 마무리가 될 수있는 색상의 방향이 4 가지 인 것처럼 보입니다 ... 게임에서 4 가지 조합을 모두 허용하는지 궁금합니다. 아니면 하나의 솔루션 만 존재하도록 항상 고정 된 블록이 있습니까? –

+0

@TheoWalton 항상 하나의 솔루션 만 존재하도록하는 고정 블록이 있습니다. –

+1

참조한 이미지는 다양한 색조와 채도가 혼합 된 것처럼 보입니다. 채도는 이미지 중심에서 방사상으로 이미지 테두리로 증가합니다. 색조는 이미지 중심 주위의 각도에 따라 다릅니다. 이 방법으로 극좌표에서 2 차원을 갖게됩니다. 검은 색 점이 찍힌 색상 패치는 이제 불규칙한 격자 위에 보간을위한 제어점을 선언합니다. 그러나, 그 게임에서 다른 샘플 이미지를 보지 않고도, 색 그라디언트가 항상 위에서 언급 한 극좌표를 따를 지 여부는 여전히 열려 있습니다. –

답변

4

그렇게 x,y는 화소 위치이며 z 컬러 채널 검사되는 3D 그래프 플롯 RGB 그래프 플롯

를 만들 수있다 (R, G 또는 B). 그것으로부터 그라디언트의 속성을 결정할 수 있습니다. 플롯이 필요한 평면보다 평면이면 3 개의 알려진 셀에서 가져온 것입니다. 그것이 얼마나 많은 inflex 포인트에 따라 곡면 일 경우, 당신은 그것을 위해 얼마나 큰 다항식이 사용되었는지를 결정할 수 있습니다. 이 모든 것을 통해이 문제를 해결할 수 있습니다.

나는 (너무 큰 격차 또는 공상 다항식 가정) 간단한 뭔가 시작할 것 :

별도로 각 색상 채널을 처리합니다. 정적 타일을 사용하고 격자 타일 색상 만 보간합니다.비슷한 :

나는 당신이 필요로 보간의 종류를 추정 할 수없는 R, G, B 그래프를 보지 않고. 그래프가 선형 인 경우 양방향 선형 또는 선형 보간을 사용합니다. 더 높은 다항식을 사용하지 않는 경우.

그래서 당신이 할 수있는 격자 셀을 채우십시오 (알려진 색상의 이웃을 가짐). 그런 다음 계산 된 색상에 가장 가까운 이동 가능한 타일을 찾습니다 (셀에 3 개의 채널이 모두 삽입 된 경우 셀을 정적으로 설정).

이제 모든 셀이 계산 될 때까지 프로세스를 반복하십시오.

[2017 EDIT1 년 12 월 14] 일부 추가 참고 사항 및 물건

호기심이었고, 그래서 나는 그것에게 주사를 놓았다 오늘 약간의 시간을 얻었다. 먼저 C++/VCL에서 이미지를 입력 (자른 후 크기 조정) 한 게임을 만듭니다. 그럼 난 그래프를 수동으로 타일을 정렬 플롯 :

RGB plots

화이트 도트 타일이 (보간 된 색상을 일치)이 올바르게 배치됩니다 것을 의미합니다. 도트 주위의 색상이있는 원은 보간 된 색상입니다 (시각적으로 비교하려면 확대/축소해야합니다).

그림과 같이 R, G, B 3D 플롯은 선형으로 보이므로 선형 보간이면 충분합니다.

행에 대한 선형 보간을 시도한 경우에만 해결사가 즉시 퍼즐을 해결합니다. 그러나 컬럼 (알려진 것들 사이에 더 많은 알려지지 않은 셀)에 대해 동일한 코드를 작성하면 솔버는 잘못된 배치를 만들기 시작합니다. 따라서 전체 배치가 잘못된 흰색 점으로 무효화됩니다.

column solver

나는 또한 HSL을 시도했지만 잠시 후에 나는 때문에 색조했던 경우에서 구별 할 수없는 어떤 지점에서 0360 학위를 통과 할 수 있기 때문에 벽을 치는 멀리 던져 십자가가 아닙니다. 이를 위해서는 이웃 해있는 영역에서 경험적으로 또는 상호 상관이 필요하며 이는 내 취향에 맞게 코딩하는 것입니다. 결과가 없으면 결과는 더욱 악화되어 RGB을 사용합니다. 그래서

는 지금은

의 모습을 선형 보간 ... 선형 보간법을 사용하거나 나머지를 해결에만 먼저 짧은 거리 보간을 해결하고 중 하나에 대해

[Edit2가 2017년 12월 14일]를 생각하고 bilinear 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에 따라 다름). 이 입력 이미지 I가 적절한 코드 부하 상태의 기능을 대신 내가 비트 맵을 직접 자체 (공간의 낭비하지만, 거의 코드 노력)를 사용하기로 결정했습니다/저장하지 않은 map.bmp

map.bmp

입니다.

맵 자체 SS 셀 (고정/풀/미해결)의 플래그이다 SSBBGGRR hex 형태와 2D 32 DWORD 배열이다. 소스 코드

여기에 컴파일 된 데모는 더 많은 정보를 위해 readme.txt을 읽어보십시오. 여기에 해결 한 후 결과 ([S] 키를 누르면) :

solved state

당신이 bilinearly 보간 색상이 더 밀접하게 귀하의 의견 일치로 원이 사라 볼 (안) 수 있듯이.

프로그램 크기 7x9의 그리드가 예상됩니다. 이미지의 해상도는 중요하지 않습니다. 포함

  1. 추가/사용 목록 : 색상은 당신이이 일을 할 수있는 권리 (타일 색상)

    이 효율적으로 만들려면 약간 세포 (검은 점)과의 중간 지점에서 샘플링 미해결 셀의 목록을 통해서만 전체 맵을 반복하는 대신, 미해결 된 셀

    이 있습니다. 삼각형 검색

    에 의해 T((N^2)/2)

  2. 변환 T(N^2) 검색이 여전히 그러나 O(N^2)하지만 일정 시간이 작다. 당신은 32K 항목 3D LUT 테이블을 만들 수 있습니다 대형 그리드

  3. 사용 3D RGB LUT 테이블

    O(1)에서 검색 일치하는 셀을 찾을 수 있습니다.간단하게 15 비트 색상을 RGB로 변환하고 사용

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

    곳에 각 색상 배치 할 위치를 알 수 LUT[r][g][b]=row+(column<<16); 아시고 방법. 사용하지 않은 모든 색상은 0xFFFFFFFF으로 설정됩니다. 유사한 목적을 위해이 기술을 사용하여 여기에 예제 : 그래서 당신이 필요가있을 수 있습니다 거친 15 비트 컬러의 코드에서 recolor[32][32][32]에 대한

    봐 ...이 목적을 위해 충분하지 될 수있다 18 비트 같은 더 많은 비트가 여전히 관리 할 수있는 256K 항목을 생성합니다.

    이것을 만들려면 LUTO(N) 시간이 걸리지 만 사용 및 유지 관리 시간은 O(1)입니다.

0

이 방법이 효과가 있다는 생각이 들지 않습니다. 나는 방금 재미로 그것을 썼고 그것에 진짜 테스트를 적용 할 수 없었다. 친절하게도 그것에 대한 시도와 의견을 주시면 감사하겠습니다.

 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는 비교를 위해서만 필요하므로 필요하지 않습니다.

"정렬"은 고정되지 않은 픽셀의 위치를 ​​변경하는 주요 기능입니다. 이 함수에 대한 입력은 픽셀의 행 - 열 배열입니다. "sort"함수는이 배열을 변경합니다.

+0

작동 방식에 대한 몇 가지 단어를 추가하십시오. 순수한 주석이 달린 코드는 비록 그 코드가 단순 해 보이더라도 다른 사람이 이해하기 어렵습니다. ... – Spektre

+0

@Spektre 그것에 대해 몇 가지 설명을 추가했습니다. 나는 그 충분을 바란다. 코드가 더 잘 설명됩니다. 나는이 문제에 대한 보간법을 다룰 필요가 없다고 생각한다. 그러나 나는 그것에 대해 확실하지 않다. 감사. – Koray

+1

약간 더 좋지만 다른 사람들에게 유용하게 사용할 수 있다고 생각합니다. 퍼즐의 입력과 출력이 저장되는 방식 (형식)은 주 변수와 의미를 추가해야합니다. – Spektre