Nếu bạn có nhiều solved
hình ảnh mà bạn có thể tạo ra RGB đồ thị âm mưu
để vẽ đồ thị 3D nơi x,y
là vị trí pixel và z
được kiểm tra kênh màu (R, G hoặc B). Từ đó bạn có thể xác định một số thuộc tính của gradient. Nếu cốt truyện là một mặt phẳng hơn tất cả những gì bạn cần chỉ là bình thường (lấy từ 3 ô đã biết). Nếu nó là bề mặt cong tùy thuộc vào bao nhiêu điểm inflex nó có bạn có thể xác định như thế nào đa thức lớn đã được sử dụng cho nó. Từ tất cả những điều này bạn có thể bắt đầu giải quyết điều này.
tôi sẽ bắt đầu với một cái gì đó đơn giản (giả sử không khoảng cách quá lớn hoặc đa thức ưa thích):
Xử lý mỗi kênh màu riêng biệt. Tôi sẽ chỉ sử dụng các ô tĩnh và nội suy các màu lưới chỉ từ chúng.Một cái gì đó tương tự như:
Nếu không nhìn thấy các R, G, B đồ thị tôi không thể ước tính mà loại suy mà bạn cần. Nếu đồ thị là tuyến tính sử dụng nội suy tuyến tính hoặc tuyến tính. Nếu không sử dụng đa thức độ cao hơn.
Vì vậy, hãy điền vào bất kỳ ô lưới nào mà bạn có thể (có hàng xóm có màu đã biết). Sau đó, tìm ô có thể di chuyển gần nhất tới màu được tính toán (nếu ô có tất cả 3 kênh được nội suy) và đặt chúng (và đặt là tĩnh).
Bây giờ, chỉ cần lặp lại quy trình cho đến khi tất cả các ô được tính toán.
[Edit1 ngày 14 tháng 12 năm 2017] một số ghi chú thêm và các công cụ
Đã tò mò và có một số thời gian hiện nay vì vậy tôi đã cho nó một shot. Trước tiên, tôi tạo trò chơi trong C++/VCL để lấy hình ảnh của bạn làm đầu vào (được cắt và thay đổi kích cỡ). Sau đó, tôi được sắp xếp gạch bằng tay và vẽ đồ thị:
Các chấm trắng có nghĩa là gạch được đặt đúng (phù hợp với màu nội suy). Các vòng tròn màu xung quanh các chấm là các màu được nội suy (để so sánh trực quan, bạn cần thu phóng để xem chúng).
Như bạn có thể thấy các ô R, G, B 3D có nội suy tuyến tính do đó (bi) nên đủ.
Nếu tôi chỉ cố gắng nội suy tuyến tính cho các hàng, chỉ người giải quyết giải quyết câu đố ngay lập tức. Tuy nhiên Khi tôi mã hóa tương tự cho các cột (nhiều ô không xác định hơn giữa các cột đã biết), người giải quyết bắt đầu thực hiện vài lần đặt không chính xác (làm mất hiệu lực toàn bộ nội dung do đó các chấm trắng sai).
tôi cũng cố gắng HSL nhưng sau một thời gian tôi vứt nó đi do chạm vào tường vì Huế có thể vượt qua mức độ 0
và 360
tại bất kỳ điểm nào mà không phải là phân biệt với trường hợp đã làm không vượt qua. Cho rằng nó sẽ cần một số heuristics hoặc tương quan chéo từ các khu vực lân cận giải quyết và đó sẽ là quá nhiều mã cho hương vị của tôi. Không có kết quả ở đó thậm chí còn tệ hơn khi sử dụng RGB.
Vì vậy, bây giờ tôi đang suy nghĩ về cách sử dụng Bilinear suy hoặc giải quyết interpolations khoảng cách ngắn nhất và chỉ sau đó giải quyết phần còn lại ...
[Edit2 14 Tháng 12 2017] Bilinear suy
Hình như bilinear RGB nội suy giải quyết tất cả các vấn đề. Vì vậy, nếu hội đồng quản trị của bạn được đính kèm với các tế bào cố định nó sẽ làm việc. Nếu không, bạn cần phải giải quyết các hội đồng lặp đi lặp lại và sau đó sử dụng các ô mới được giải quyết như là ràng buộc mới cho các khu vực chưa được giải quyết. Ngoài ra tôi nhận ra tôi đã RGB đảo ngược vì vậy tôi cũng sửa chữa đó :).
Đây C++/VCL nguồn cho các trò chơi (Nó không phải là tối ưu hóa ở tất cả):
//$$---- 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
}
//---------------------------------------------------------------------------
Nó là một mẫu App duy nhất trong BDS2006 với single 40ms hẹn giờ trên đó. Vì vậy, chỉ cần thêm các sự kiện ... Bạn có thể bỏ qua các công cụ hiển thị và cửa sổ VCL. Điều quan trọng là lớp và hàm solve()
trong đó. Nó được sử dụng cho cả hai kiểm tra đặt chính xác và để giải quyết (tùy thuộc vào bool _solve
). Đây là hình ảnh đầu vào map.bmp
Tôi không mã hóa thích hợp lưu/chức năng nhà nước tải thay vào đó tôi đã chọn để sử dụng bitmap bản thân trực tiếp (lãng phí không gian nhưng hầu như không có nỗ lực code).
Bản đồ chính nó là 2D 32bit DWORD
mảng với hình thức SSBBGGRR hex
nơi SS
là lá cờ của tế bào (cố định/giải quyết/chưa được giải quyết).
đây biên soạn bản demo với mã nguồn
Đọc readme.txt
để biết thêm. Dưới đây là kết quả sau khi giải quyết (nhấn [S]):
Như bạn có thể (không) thấy các vòng tròn tan biến như màu bilinearly suy phù hợp chặt chẽ hơn đầu vào của bạn.
Chương trình đang mong đợi lưới có kích thước 7x9 độ phân giải của hình ảnh không quan trọng. Màu sắc được lấy mẫu từ điểm giữa của tế bào (chấm đen) và hơi bên phải (màu gạch)
Để thực hiện hiệu quả này, bạn có thể làm 2 điều:
add/danh sách sử dụng có chứa ô chưa được giải quyết
thay vì di chuột qua toàn bộ bản đồ chỉ lặp qua danh sách ô chưa được giải quyết.
convert T(N^2)
tìm kiếm để T((N^2)/2)
bằng cách tìm kiếm tam giác
này vẫn là tuy nhiên O(N^2)
nhưng hằng số thời gian là nhỏ hơn.
sử dụng 3D RGB LUT bảng
dùng cho lưới lớn bạn có thể tạo 32K mục 3D LUT bảng để tìm ra tế bào phù hợp với tìm kiếm trong O(1)
.Chỉ cần chuyển đổi màu RGB thành 15 bit và sử dụng
DWORD LUT[32][32][32];
Nơi mà bạn sẽ biết vị trí của từng màu. Tất cả các màu chưa sử dụng được đặt thành 0xFFFFFFFF
. Dưới đây là một ví dụ của việc sử dụng kỹ thuật này cho mục đích tương tự:
Hãy tìm recolor[32][32][32]
trong mã ... Trong màu 15bit thô có thể không đủ cho mục đích này rất có thể bạn sẽ cần nhiều bit hơn như 18bit dẫn đến 256K mục vẫn có thể quản lý được.
Tạo điều này LUT sẽ mất O(N)
thời gian nhưng chỉ sử dụng và duy trì thời gian là O(1)
.
ok Tôi đã chơi trò chơi nhưng từ bìa trước của trò chơi có vẻ như có 4 hướng có thể có màu sắc sẽ cho kết quả mịn ... chỉ cần tự hỏi nếu trò chơi chấp nhận tất cả 4 kết hợp. Hay luôn có một số khối cố định để đảm bảo chỉ có một giải pháp tồn tại? –
@TheoWalton luôn có một số khối cố định để đảm bảo chỉ tồn tại một giải pháp. –
Hình ảnh bạn đã tham chiếu trông giống như sự pha trộn giữa màu sắc và độ bão hòa khác nhau. Độ bão hòa màu tăng từ trung tâm hình ảnh xuyên qua biên giới hình ảnh. Màu sắc thay đổi theo góc xung quanh trung tâm hình ảnh. Bằng cách này bạn có 2 chiều trong các tọa độ cực. Các bản vá lỗi màu đen chấm hiện nay khai báo các điểm điều khiển cho một phép nội suy trên một lưới bất thường. Tuy nhiên, không nhìn thấy bất kỳ hình ảnh mẫu nào khác từ trò chơi đó, nó vẫn mở, cho dù các gradient màu luôn luôn theo các tọa độ cực tôi đã đề cập ở trên. –