碎纸条拼接复原

有一份文字文件被纵向切割成为19条碎纸条,文字为中文,现通过计算机编程计算将其还原。图片切割如图所示:

1440598279_20150826204054

这个问题可以理解为碎纸条排列成一个序列,要使得碎纸条与碎纸条之间的差异最小。在这个问题中,可以将每张碎纸条理解为一个点,将两张纸条拼接起来时边缘的吻合程度为对应两个点的距离。由于文字文件两边都会有留白,所以最后一张碎纸条的右边与第一张碎纸条的左边都是留白,因此它们能很好吻合,这就使得碎纸条能够连接成为一个回路。这样子就可以将问题转化为求拜访每个地点后回到起点的最短路径的“旅行商问题”。“旅行商问题”常被称为“旅行推销员问题”,是指一名推销员要拜访多个地点时,如何找到在拜访每个地点一次后再回到起点的最短路径。

1 碎纸条吻合程度的量化

1.1 边缘吻合程度

图片在计算机中是以矩阵的形式表示的。例子中给出的是灰度图像,即图像没有色彩。灰度图像的像素数据就是一个矩阵,矩阵的行对应图像的高(单位为像素),矩阵的列对应图像的宽(单位为像素),矩阵的元素对应图像的像素,矩阵元素的值就是像素的灰度值。两张图片边缘的越能吻合,它们边缘对应像素的灰度值就越接近,所以在这里我们使用两张碎纸条边缘像素的灰度值的差的绝对值的和来描述两张碎纸条边缘的吻合程度。吻合度越小表示越吻合。即:

式中d表示吻合程度,akq表示碎纸条i第k行第q列的像素的灰度值,ak1表示碎纸条j第k行第一列的像素的灰度值。

1.2 邻接矩阵

我们用邻近矩阵的形式来表示任意两张碎纸条的吻合程度。我们规定,邻接矩阵中第i行第j列的元素dij表示将序号为j的碎纸条连接到序号为i的碎纸条的右边的吻合程度。因此,通过1.1中对碎纸条边缘吻合程度的量化,我们可以得到一个19*19的矩阵。

1.3 邻接矩阵的计算

邻接矩阵能够通过MATLAB编程计算出来,程序如下:


clear;clc;
%运行本程序时需要将 m 文件放到存储碎纸条的文件夹中
%并且将MATLAB的根目录 CD 到储存碎纸条的文件夹中
ljjz = ones(19,19);  %邻接矩阵
for i = 0:18
    %根据序号得到文件名
    if(i<10)
        imiph = ['00',num2str(i),'.bmp'];
    else
        imiph = ['0',num2str(i),'.bmp'];
    end
    for j = 0:18
        if(j<10)
            imjph = ['00',num2str(j),'.bmp'];
        else
            imjph = ['0',num2str(j),'.bmp'];
        end
        %读取图片,并且将图片矩阵转为double
        imi = im2double(imread(imiph));
        imj = im2double(imread(imjph));
        [imh,iml] = size(imi);
        %相减,求吻合度
        mysum = sum(abs(imi(:,iml) - imj(:,1)));
        ljjz(i+1,j+1) = mysum;
    end
end
 

 

 

运行这个程序就能得到邻接矩阵。

2 碎纸条拼接序列

2.1 旅行商模型

“旅行商问题”可以利用0-1规划求解。设xij = 1为将序号为j的碎纸条连接到序号为i的碎纸条的右边;dij为相应的吻合程度。对于任意一张碎纸条,只能有一张碎纸条连接在它的左边,也只能有一张碎纸条连接在它的右边,所以有以下两个约束条件。

由此可以得到以下0-1规划模型:

1440598293_20150826214258

 

2.2 LINGO求解

LINGO是一款用于求解线性或非线性规划模型的优秀的软件,我们将应用这款软件求解2.1总的模型。程序代码如下:

sets:          s/1..19/;          link(s,s):d,x;
endsets
Data:
d =  数据省略
enddata
min = @sum(s(i):@sum(s(j):x(i,j)*d(i,j)));
@for(s(i):@sum(s(j):x(i,j)) = 1);
@for(s(j):@sum(s(i):x(i,j)) = 1);
@for(s(i):@for(s(j):@bin(x(i,j))));

 

 

代码中的d为邻接矩阵,是1中MATLAB的计算结果。

3 拼接碎纸条

3.1 分析结果

通过2.2的计算,我们能够得到一个回路,但是这并不能很好的拼接碎纸条,因为我们并不知道从何开始拼接。由于文字文件的左右两端都会有留白,所以第一张碎纸条的左边应是有留白的,通过这个特点,我们能利用肉眼观察找到最左边的碎纸条。经过观察确定,编号为008的碎纸条为第一张碎纸条。这样子我们就能得到碎纸条的拼接顺序,如下:

8141215310216145913181171706

3.2 拼接碎纸条

利用3.1的分析结果,通过MATLAB编程,即可将碎纸条拼接成一张完整的文字文件。程序代码如下:


sets:
         s/1..19/;
         link(s,s):d,x;
endsets
Data:=  数据省略
enddata
min = @sum(s(i):@sum(s(j):x(i,j)*d(i,j)));
@for(s(i):@sum(s(j):x(i,j)) = 1);
@for(s(j):@sum(s(i):x(i,j)) = 1);
@for(s(i):@for(s(j):@bin(x(i,j))));
 

 

 

拼接结果如下:

1440598300_20150826220425

从结果上看,拼接效果很好。

本问题是2013年全国大学生数学建模大赛B题的第一问,这篇文章是作者参考竞赛论文《基于旅行商模型的文字碎纸片拼接复原》写成,作为练习之用。本文程序由作者自己编写,方法参考竞赛获奖论文。

posted @ 2016-03-28 18:21  钉钉铛铛  阅读(1761)  评论(0编辑  收藏  举报