MATLAB | 二维波函数坍缩算法随机地图生成

本文原理解释和很多图片结构来自和参考了知乎大佬大灰熊的这篇:

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

使用方式及效果

以下解释各个参数是啥

  • waveCollapse(img,N,rows,cols)
    • img : 素材图
    • N : 素材图划分数
    • rows : 新图行数
    • cols : 新图列数

对于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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 3

打赏作者

slandarer

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值