700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > python实现图片拼接长图_用Python实现长截图拼接

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

时间:2022-09-21 22:18:51

相关推荐

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()可以得到像素信息

from PIL import Image #引入PIL

from datetime import datetime #后续用于测试运行时间

class sewImage(object): #拼接图片的类

def __init__(self,imagePath=[]):

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

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

self.width = -1 #截图宽度

self.height = -1 #截图高度

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

#打开图片

def openImages(self):

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

raise ValueError("图片数量小于2张")

for path in self.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.width or h != self.height):

raise ValueError("图片尺寸不一致")

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

寻找相同的头部

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

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

代码如下

#寻找相同的头部

def findHead(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]

for h in range(head):

for w in range(width):#比对一行

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

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

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

equalPixel +=1

if(equalPixel

head = h

break

equalPixel=0

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

return head

拼接图片

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

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

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

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

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

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

#拼接两张图

def getNewImgData(self):

newHeight = self.height

newImgData = list(self.imgdatas[0])

width = self.width

height = self.height

for i in range(len(self.imgdatas)-1):

equalPixel=0

tail = newHeight

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

head = self.findHead()

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

for h in range(newHeight-offsetLine)[::-1]:

for w in range(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==r2 and g1==g2 and b1==b2 and r3==r4 and g3==g4 and b3==b4):

equalPixel+=1

if(h < newHeight-height): #没有找到相同行

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函数组织起来前面的函数,拼图并保存

def sew(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))

优化

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

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

识别拼接尾部的优化

在拼接尾部的时候我们遍历了每个像素,实际上我们只是需要判断两行是否完全一致,因此我们可以切片出每行的内容,直接判断即可,这样则可以去掉一个for循环,效率会提高不少。

我们可以将原来的修改成下面这样

#拼接两张图

def __getNewImgData(self):

newHeight = self.__height

newImgData = list(self.__imgdatas[0])

width = self.__width

height = self.__height

for i in range(len(self.__imgdatas)-1):

tail = newHeight

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

head = self.__findHead()

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

#####改动部分#####

for h in range(newHeight-offsetLine)[::-1]:

line1 = imgdata2[width*head:width*(head+1)]

line2 = newImgData[width*h:width*(h+1)]

line3 = imgdata2[width*(head+offsetLine):width*(head+offsetLine+1)]

line4 = newImgData[width*(h+offsetLine):width*(h+offsetLine+1)]

if(h < newHeight-height): #没有找到相同行

break

if(line1==line2 and line3 == line4):

tail = h

break

####改动部分结束####

newImgData = newImgData[:width*tail]

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

newHeight = tail + (height - head)

return (newImgData,newHeight)

改动后拼接3张图快了有半秒

效果

变成这个

new.png

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。