模型建立
问题1
边缘相似度
问题一给出的是一单页页面仅纵切出的 19 条碎纸片,确定碎纸片排列的顺序,就能很好的还原出原来的完整纸片。因为碎纸片的形状规则且大小相等,因此不能够通过碎纸片边缘形状来确定相邻纸片,只能通过分析边缘文字特征进行拼接。可以看出,碎纸片之间的切口都是整齐的直线,碎纸片边缘保留了字符信息。通过比较两个纸片边缘相似度,可以确定两张纸片是否相邻,从而得到正确的拼接序列。
一般来讲,从图像的连通性考虑,相邻的两个碎纸片的边缘像素应当是相似、甚至是相同的。这样我们可以将两个边缘的匹配指标用两个向量的距离来衡量:
其中,x和y表示两张碎纸图片的像素矩阵的边缘列向量。如果两个由像素组成的向量间的距离为0,则可以肯定两张碎纸片肯定相邻,但这是理想的情况,如图2所示,碎纸图片不是矢量图形,在显示点阵字体时,会出现锯齿现象,文字边缘由里向外,会有从黑到白之间的过渡,所以两个相邻像素向量间的距离一般较小,但不一定为0。虽然像素分开了,由于汉字字体或者英文字体大部分是连体的,所以这些碎片的图像灰度信息会有一定的相关性或者说是连续性。我们就能利用这些碎片之间的相关性还原出完整纸片。
贪心算法
由于存在锯齿现象,两个向量间距离不一定为零,一个碎纸图片可能与多张其他碎纸片有相关性。所以,我们选择主要的步骤是找出一个起始碎片作为起点,然后把这个碎片像素矩阵的最右边缘向量跟其他碎片的最左边缘向量进行比较,选择匹配度最高的碎片进行匹配,然后以新匹配到的碎片为开始,再对余下碎片进行最优匹配,不断循环,直到所有碎纸片都匹配结束。如图3所示,由于选择的起始碎片对算法影响较大,为拼接出正确的纸片,我们需要找出原纸片中最左边的碎纸片编号。通过观察,我们看出文字与页面之间存在内边距,左侧留白最多的碎纸片就是原纸片中的最左边。通过比较留白大小找出起始碎片后,即可拼接出完整中文纸片。英文字符的碎纸片同样有相同的规律,使用相同的算法处理,就能得到英文的完整纸片。
问题求解
附件一和附件二
在处理附件一和附件二时,不需要进行人工干预,运行算法就能得到拼接顺序,最后利用python的PIL库完成碎片的 拼接。
最终附件一的拼接顺序为:
[8, 14, 12, 15, 3, 10, 2, 16, 1, 4, 5, 9, 13, 18, 11, 7, 17, 0, 6]
附件二的拼接顺序为:
[3, 6, 2, 7, 15, 18, 11, 0, 5, 1, 9, 13, 10, 8, 12, 14, 17, 16, 4]
图4为附件一拼接完成后的效果,图5为附件二拼接完成的效果
代码
附件一和二代码:
import glob
import numpy as np
from PIL import Image
class PaperRecovery:
IMAGE_ABS_PATH = 'F:\code\py\paper-recovery\碎纸片1\*.bmp' #图片存在该目录下
img = {}
finded = set()
def read_img(self):
"""
读取指定目录下的碎纸图片,存入字典中,方便使用
"""
img_name_list = glob.glob(self.IMAGE_ABS_PATH)
for img_name in img_name_list:
# k = img_name.replace('.bmp','')[-3:]
# 使用文件路径作为字典的键,方便找出正确图片顺序后,拼接还原图片
k = img_name
self.img[k] = Image.open(img_name)
def __init__(self):
self.read_img()
def find_match_paper(self,paper_img,direction):
"""
根据图片边缘的相似度,找出给定碎纸片的相邻纸片
"""
self_img_array = np.array(self.img[paper_img])
sh, sw = self_img_array.shape
if direction == "r":
self_edge = self_img_array[0:sh,sw-1:sw]
elif direction == 'l':
self_edge = self_img_array[0:sh,0:1]
min_var = -1
for k, v in self.img.items():
if k == paper_img:
continue
if k in self.finded:
continue
other_img_array = np.array(v)
h, w = other_img_array.shape
if direction == "r":
other_edge = other_img_array[0:h,0:1]
elif direction == 'l':
other_edge = other_img_array[0:h,w-1:w]
else:
pass
var = np.sum(abs(self_edge-other_edge))
if min_var == -1:
similar_img = k
min_var = var
if var < min_var:
similar_img = k
min_var = var
self.finded.add(paper_img)
return similar_img
def find_edge_paper(self):
"""
函数找出原图片中的最左边和最右边的碎纸图片
观察碎纸图片后,可以看出,在竖切时
在完整纸片的最左端和最右端的碎纸片,有一定的留白
"""
def is_all_white(edge_array):
"""
判断一列像素值是否全为255,即全白
"""
WHITE_RGB = 255
for k in edge_array:
if k != WHITE_RGB:
return False
return True
for k, v in self.img.items():
img_array = np.array(v)
h, w = img_array.shape
right_edge = img_array[0:h,w-1:w]
left_edge = img_array[0:h,0:1]
if is_all_white(right_edge):
right_img_name = k
elif is_all_white(left_edge):
left_img_name = k
return (left_img_name,right_img_name)
def find_correct_order(self):
correct_order = []
left_side_img, right_side_img = self.find_edge_paper()
correct_order.append(left_side_img)
paper_list = list(self.img.keys())
paper_list.remove(left_side_img)
paper_list.remove(right_side_img)
while paper_list:
now_paper = correct_order[len(correct_order)-1]
next_paper = self.find_match_paper(now_paper,direction="r")
paper_list.remove(next_paper)
correct_order.append(next_paper)
correct_order.append(right_side_img)
return correct_order
def combining(self,order):
"""
复原竖切的碎纸片
按照正确的图片顺序(从左到右),将碎纸片拼接成完整的纸片
"""
UNIT_W, UNIT_H = self.img[order[0]].size
TARGET_WIDTH = len(order) * UNIT_W
target = Image.new('RGB', (TARGET_WIDTH, UNIT_H))
x = 0
for img_name in order:
img = self.img[img_name]
target.paste(img,(x,0))
x += UNIT_W
return target
if __name__ == "__main__":
rp = PaperRecovery()
correct_order = rp.find_correct_order()
whole_img = rp.combining(correct_order)
whole_img.save('whole.bmp')
idx_list = []
for order in correct_order:
idx = order[-7:-3]
idx_list.append(idx)
print(idx_list)