python实现长截图_用 Python 实现长截图拼接

用 Python 实现长截图拼接

可能很多安卓手机都会自带拼接长截图的功能, 可是对于 iOS 只能通过第三方的 app 拼接了于是我想将拼接的功能做成微信小程序, 这样会比较方便, 无奈实现过程中发现用 JavaScript 实现性能存在很大问题, 识别率也很低, 于是打算先用 python 实现, 以测试算法的正确性

我写得可能不是很好, 欢迎指正!

要实现长截图, 可能的情况很多, 先介绍一下最简单的情况

长截图算法

获取截图

我们按照手工拼接的想法来考虑这个问题

首先得到两张截图

寻找相同部分

然后我们首先要考虑两张图相同的部分, 也就是找到相同的头部

删除相同头部

接着把第二张图中与图 1 重复的头部删掉

移动并拼接

然后第二张图从底至顶移动, 找到重合的地方, 拼接

这就是我们正常的手工拼接流程因此可以把算法总结一下:

step 1: 获取两张截图

step 2: 寻找相同的头部

step 3: 删除第二张中相同的头部

step 4: 从第一张的底部至顶部比较, 找到重合的地方, 拼接

实现前要知道

通常的图片为 RGB 模式, 即图片的每个像素由红 (red) 绿(green)蓝 (blue) 三个颜色组成, 它们的取值范围是 [0,255], 即(0,0,0) 表示黑色,(255,255,255)表示白色

在处理图片时, 我们可以对图片中的每个像素操作, 获取到图片的每个像素的 RGB 值, 对于一张 2*3 的纯白图片, 我们可以得到它的像素内容:[

[(255,255,255),(255,255,255)],

[(255,255,255),(255,255,255)],

[(255,255,255),(255,255,255)]

]

共 6 个像素点, 每个 tuple 包含了一个像素点的 RGB 内容

因此, 我们在 寻找相同的头部 的时候, 可以从头开始比较其中每个像素的信息

拼接图片 , 也就是将这些图片的像素信息重新拼接起来, 再显示即可

用 Python 实现

在 python 中我们可以用 PIL 的 getdata()方法获得图片的像素内容, 用 putdata()将像素内容转变成图片

打开图片

通过

img = Image.open(path)

我们可以打开一张图片

再通过 img.getdata()可以得到像素信息fromPILimportImage#引入 PIL

fromdatetimeimportdatetime#后续用于测试运行时间

classsewImage(object):#拼接图片的类

def__init__(self,imagePath=[]):

self.imgPath=imagePath#imgPath 是储存图片路径的数组

self.imgdatas=[]#imgdatas 是储存每张图片的像素信息的数组

self.width=-1#截图宽度

self.height=-1#截图高度

self.curr=0#当前图片的位置

#打开图片

defopenImages(self):

if(len(self.imgPath)<2):

raiseValueError("图片数量小于 2 张")

forpathinself.imgPath:

img=Image.open(path)#打开图片

img_RGB=img.convert("RGB")#转换成 RGB 模式

imgdata=img_RGB.getdata()#获取图片的像素信息

#判断尺寸是否一致, 要求每张图片的宽高一致(高度其实可以不一致, 不过稍微麻烦点)

if(self.width==-1):

self.width,self.height=img_RGB.size

else:

w,h=img_RGB.size

if(w!=self.widthorh!=self.height):

raiseValueError("图片尺寸不一致")

self.imgdatas.append(imgdata)#将获取的像素信息加入 imgdatas 数组

寻找相同的头部

在 iOS 中的导航栏通常是磨砂半透明的, 所以实际上无论微信还是其他 app 的导航栏, 两张截图的颜色通常是有差别的, 如图所示, 因此我们再比较是否两个像素是否相同的时候, 应该允许一定的误差范围, 我试验后发现 RGB 分别相差在 25 以内可以认为是相同的 (这个可根据需求自行调整) 同时, 截图时状态栏的图标可能也会发生细微变化, 比如信号强度不同, 因此我们也得设置一定的误差允许范围, 在此我设置了一行中超过 90% 的像素是相同的, 即认为该行相同

导航栏的颜色可能不同, 状态栏可能有细微差别

代码如下# 寻找相同的头部

deffindHead(self,hitRate=0.9):#hitRate: 一行中超过 hitRate*width 个相同的像素即认为该行相同

imgdatas=self.imgdatas

curr=self.curr

width=self.width

if(curr>=len(imgdatas)-1):

return

equalPixel=0

head=self.height#相同头的位置, 默认为 height

imgdata1=imgdatas[curr]

imgdata2=imgdatas[curr+1]

forhinrange(head):

forwinrange(width):#比对一行

r1,g1,b1=imgdata1[width*h+w]

r2,g2,b2=imgdata2[width*h+w]

if(abs(r1-r2)<25andabs(g1-g2)<25andabs(b1-b2)<25):

equalPixel+=1

if(equalPixel

head=h

break

equalPixel=0

self.curr+=1#当前位置后移一位

returnhead

拼接图片

拼接部分是按这样的顺序, 两张两张拼接:

-> [ 图 1 ] [ 图 2 ] [ 图 3 ] [ 图 4 ]

-> [ 图 1 图 2 ] [ 图 3 ] [ 图 4 ]

-> [ 图 1 图 2 图 3 ] [ 图 4 ]

-> [ 图 1 图 2 图 3 图 4 ]

先调用上面的函数寻找图 1 和图 2 的相同的头部, 将图 2 的头部的那一行与图 1 从底至顶依次比较每一行, 为了提高准确率, 我再同时比较图 2 头部的后 15 行, 与图 1 的对应位置的行# 拼接两张图

defgetNewImgData(self):

newHeight=self.height

newImgData=list(self.imgdatas[0])

width=self.width

height=self.height

foriinrange(len(self.imgdatas)-1):

equalPixel=0

tail=newHeight

imgdata2=list(self.imgdatas[i+1])

head=self.findHead()

offsetLine=15#同时检查 offsetLine 行是否一致

forhinrange(newHeight-offsetLine)[::-1]:

forwinrange(width):

r1,g1,b1=imgdata2[w+width*head]

r2,g2,b2=newImgData[w+width*h]

r3,g3,b3=imgdata2[w+width*(head+offsetLine)]

r4,g4,b4=newImgData[w+width*(h+offsetLine)]

if(r1==r2andg1==g2andb1==b2andr3==r4andg3==g4andb3==b4):

equalPixel+=1

if(h

break

if(equalPixel==width):

tail=h

break

equalPixel=0

newImgData=newImgData[:width*tail]

newImgData.extend(imgdata2[width*head:])

newHeight=tail+(height-head)

return(newImgData,newHeight)

完成拼图

最后再定义一个 sew 函数组织起来前面的函数, 拼图并保存defsew(self,imagePath=[]):

if(imagePath!=[]):

self.imgPath=imagePath

self.curr=0

self.openImages()#加载图片

newImgData,newHeight=self.getNewImgData()

newImg=Image.new('RGB',(self.width,newHeight))

newImg.putdata(newImgData)

newImg.save('new.png')

print('拼图完成!')

测试的时候只需要这样即可sewImg=sewImage(["pic1.png","pic2.png","pic3.png"])

begin=datetime.now()

sewImg.sew()

end=datetime.now()

total=end.timestamp()-begin.timestamp()

print("共耗时:%sms"%(total*1000))

源代码放在了 GitHub 上: https://github.com/ZitionChan/sewImage

效果

变成这个

优化

显然, 逐个像素比较还是比较慢的, 可以看到拼接上面的三张图, 需要 1.8 秒

因此我们需要寻找更好的方法来优化一下

来源: http://www.jianshu.com/p/b345a3689e3c

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值