本文原理解释和很多图片结构来自和参考了知乎大佬大灰熊
的这篇:
https://zhuanlan.zhihu.com/p/542605872
该算法叫做二维波函数坍缩算法,虽然名听着很玄乎但其实和数独区别也不大,都是一些图决定周围图能取哪些:
在网站
(http://cr31.co.uk/stagecast/wang/tiles_e.html)可以获取一系列的素材图:
这张图每32个像素就是一个图块,整张图能划分为4x4个图块
% 图片切分
imgList{N*N}=[];
pixNum=size(img,1)/N;
for i=1:N
for j=1:N
imgList{sub2ind([N,N],i,j)}=...
img(((i-1)*pixNum+1):(i*pixNum),...
((j-1)*pixNum+1):(j*pixNum),:);
end
end
比如编号为10的图像右侧只有几张图可以与之相邻:
可以通过计算边界的均方根误差,若是误差小于一定值就认为可以相连(就有点像某年数学建模国赛碎纸片拼接第一问):
% 构建联通表
imgAdjList(N*N).r=[];
imgAdjList(N*N).l=[];
imgAdjList(N*N).u=[];
imgAdjList(N*N).d=[];
for i=1:N*N
imgC=imgList{i};
imgAdjList(i).r=[];
imgAdjList(i).l=[];
imgAdjList(i).u=[];
imgAdjList(i).d=[];
for j=1:N*N
imgS=imgList{j};
% 以下是均方根误差检测部分
% 检测是否右联通 -----------------------------------------------------------
rImgC=imgC(:,end,:);rImgC=rImgC(:);
rImgS=imgS(:,1,:);rImgS=rImgS(:);
rErr=sqrt(mean((rImgC-rImgS).^2));
if rErr<err
imgAdjList(i).r=[imgAdjList(i).r,j];
end
% 检测是否左联通 -----------------------------------------------------------
lImgC=imgC(:,1,:);lImgC=lImgC(:);
lImgS=imgS(:,end,:);lImgS=lImgS(:);
lErr=sqrt(mean((lImgC-lImgS).^2));
if lErr<err
imgAdjList(i).l=[imgAdjList(i).l,j];
end
% 检测是否上联通 -----------------------------------------------------------
uImgC=imgC(1,:,:);uImgC=uImgC(:);
uImgS=imgS(end,:,:);uImgS=uImgS(:);
uErr=sqrt(mean((uImgC-uImgS).^2));
if uErr<err
imgAdjList(i).u=[imgAdjList(i).u,j];
end
% 检测是否下联通 -----------------------------------------------------------
dImgC=imgC(end,:,:);dImgC=dImgC(:);
dImgS=imgS(1,:,:);dImgS=dImgS(:);
dErr=sqrt(mean((dImgC-dImgS).^2));
if dErr<err
imgAdjList(i).d=[imgAdjList(i).d,j];
end
end
end
这里err值设置为40,可根据需要调整,进行完上述步骤后获得联通表,举例一下编号为10的图可与之相邻的图:
imgAdjList(10)
ans =
包含以下字段的 struct:
r: [9 10 11 12 13 14 15 16]
l: [5 6 7 8 9 10 11 12]
u: [1 2 5 6 9 10 13 14]
d: [2 3 6 7 10 11 14 15]
要生成新图片的编号矩阵,首先生成全0矩阵,之后就需要不断地随机产生随机数往0的位置填充,填充的随机数需要能与当前上下左右的图相邻:
每当有一部分图片被确定,就有一部分位置只能填入特殊的图片。
% 构建序号矩阵
% 不想进行太多次回退,回退五次直接重新生成
indMat=zeros(rows,cols);
while any(any(indMat==0))
try
indMat=zeros(rows,cols);
for j=1:cols
for i=1:rows
k=sub2ind([rows,cols],i,j);
for kk=1:5% 回退五次
[ti,tj]=ind2sub([rows,cols],k);
fullList=fillij(indMat,imgAdjList,N,ti,tj);
if ~isempty(fullList)
indMat(ti,tj)=fullList(randi([1,length(fullList)],[1,1]));
k=k+1;
if k>sub2ind([rows,cols],i,j)
break
end
else
indMat(ti,tj)=0;
k=k-1;
end
end
end
end
catch
end
end
function fList=fillij(indMat,imgAdjList,N,i,j)
fList=1:N*N;
% 右侧图的左侧可行图 --------------------------------------------------------
if j+1>N,rInd=0;else,rInd=indMat(i,j+1);end
if rInd==0,rIndList=1:N*N;else,rIndList=imgAdjList(rInd).l;end
fList=intersect(fList,rIndList);
% 左侧图的右侧可行图 --------------------------------------------------------
if j-1<1,lInd=0;else,lInd=indMat(i,j-1);end
if lInd==0,lIndList=1:N*N;else,lIndList=imgAdjList(lInd).r;end
fList=intersect(fList,lIndList);
% 上侧图的下侧可行图 --------------------------------------------------------
if i-1<1,uInd=0;else,uInd=indMat(i-1,j);end
if uInd==0,uIndList=1:N*N;else,uIndList=imgAdjList(uInd).d;end
fList=intersect(fList,uIndList);
% 下侧图的上侧可行图 --------------------------------------------------------
if i+1>N,dInd=0;else,dInd=indMat(i+1,j);end
if dInd==0,dIndList=1:N*N;else,dIndList=imgAdjList(dInd).u;end
fList=intersect(fList,dIndList);
end
function newImg=waveCollapse(img,N,rows,cols)
% @author : slandarer
% gzh : slandarer随笔
img=double(img);err=40;
% =========================================================================
% 图片切分
imgList{N*N}=[];
pixNum=size(img,1)/N;
for i=1:N
for j=1:N
imgList{sub2ind([N,N],i,j)}=...
img(((i-1)*pixNum+1):(i*pixNum),...
((j-1)*pixNum+1):(j*pixNum),:);
end
end
% =========================================================================
% 构建联通表
imgAdjList(N*N).r=[];
imgAdjList(N*N).l=[];
imgAdjList(N*N).u=[];
imgAdjList(N*N).d=[];
for i=1:N*N
imgC=imgList{i};
imgAdjList(i).r=[];
imgAdjList(i).l=[];
imgAdjList(i).u=[];
imgAdjList(i).d=[];
for j=1:N*N
imgS=imgList{j};
% 以下是均方根误差检测部分
% 检测是否右联通 -----------------------------------------------------------
rImgC=imgC(:,end,:);rImgC=rImgC(:);
rImgS=imgS(:,1,:);rImgS=rImgS(:);
rErr=sqrt(mean((rImgC-rImgS).^2));
if rErr<err
imgAdjList(i).r=[imgAdjList(i).r,j];
end
% 检测是否左联通 -----------------------------------------------------------
lImgC=imgC(:,1,:);lImgC=lImgC(:);
lImgS=imgS(:,end,:);lImgS=lImgS(:);
lErr=sqrt(mean((lImgC-lImgS).^2));
if lErr<err
imgAdjList(i).l=[imgAdjList(i).l,j];
end
% 检测是否上联通 -----------------------------------------------------------
uImgC=imgC(1,:,:);uImgC=uImgC(:);
uImgS=imgS(end,:,:);uImgS=uImgS(:);
uErr=sqrt(mean((uImgC-uImgS).^2));
if uErr<err
imgAdjList(i).u=[imgAdjList(i).u,j];
end
% 检测是否下联通 -----------------------------------------------------------
dImgC=imgC(end,:,:);dImgC=dImgC(:);
dImgS=imgS(1,:,:);dImgS=dImgS(:);
dErr=sqrt(mean((dImgC-dImgS).^2));
if dErr<err
imgAdjList(i).d=[imgAdjList(i).d,j];
end
end
end
imgAdjList(10)
% =========================================================================
% 构建序号矩阵
% 不想进行太多次回退,回退五次直接重新生成
indMat=zeros(rows,cols);
while any(any(indMat==0))
try
indMat=zeros(rows,cols);
for j=1:cols
for i=1:rows
k=sub2ind([rows,cols],i,j);
for kk=1:5% 回退五次
[ti,tj]=ind2sub([rows,cols],k);
fullList=fillij(indMat,imgAdjList,N,ti,tj);
if ~isempty(fullList)
indMat(ti,tj)=fullList(randi([1,length(fullList)],[1,1]));
k=k+1;
if k>sub2ind([rows,cols],i,j)
break
end
else
indMat(ti,tj)=0;
k=k-1;
end
end
end
end
catch
end
end
function fList=fillij(indMat,imgAdjList,N,i,j)
fList=1:N*N;
% 右侧图的左侧可行图 --------------------------------------------------------
if j+1>N,rInd=0;else,rInd=indMat(i,j+1);end
if rInd==0,rIndList=1:N*N;else,rIndList=imgAdjList(rInd).l;end
fList=intersect(fList,rIndList);
% 左侧图的右侧可行图 --------------------------------------------------------
if j-1<1,lInd=0;else,lInd=indMat(i,j-1);end
if lInd==0,lIndList=1:N*N;else,lIndList=imgAdjList(lInd).r;end
fList=intersect(fList,lIndList);
% 上侧图的下侧可行图 --------------------------------------------------------
if i-1<1,uInd=0;else,uInd=indMat(i-1,j);end
if uInd==0,uIndList=1:N*N;else,uIndList=imgAdjList(uInd).d;end
fList=intersect(fList,uIndList);
% 下侧图的上侧可行图 --------------------------------------------------------
if i+1>N,dInd=0;else,dInd=indMat(i+1,j);end
if dInd==0,dIndList=1:N*N;else,dIndList=imgAdjList(dInd).u;end
fList=intersect(fList,dIndList);
end
% =========================================================================
% 填充图像
newImg=zeros([rows*pixNum,cols*pixNum,size(img,3)]);
for i=1:rows
for j=1:cols
newImg(((i-1)*pixNum+1):(i*pixNum),((j-1)*pixNum+1):(j*pixNum),:)=...
imgList{indMat(i,j)};
end
end
newImg=uint8(newImg);
% @author : slandarer
% gzh : slandarer随笔
end
以下解释各个参数是啥
对于4x4的素材图:
fileName='trench.png';
sz=[9,9];
oriImg=imread(fileName);
newImg=waveCollapse(oriImg,4,sz(1),sz(2));
imshow(newImg)
imwrite(newImg,[fileName(1:end-4),'_',num2str(sz(1)),'_',num2str(sz(2)),'.png'])
对于9x9的素材图:
fileName='celtic3e.png';
sz=[15,15];
oriImg=imread(fileName);
newImg=waveCollapse(oriImg,9,sz(1),sz(2));
imshow(newImg)
imwrite(newImg,[fileName(1:end-4),'_',num2str(sz(1)),'_',num2str(sz(2)),'.png'])
其他素材图:
注:目前检测图片是否相邻的算法对于横平竖直的图像检测比较好用,但对于一些有倾斜角度的图片不咋好用,可以根据自己需要指定更加准确的联通准则。
再注:本文原理解释和很多图片结构来自和参考了知乎大佬大灰熊
的这篇:
https://zhuanlan.zhihu.com/p/542605872
完整代码及文件:
链接:https://pan.baidu.com/s/1kAY3c9J3KZOcC7_jJ3D93A?pwd=slan
提取码:slan