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
總結
以上是生活随笔為你收集整理的python实现图片拼接长图_用Python实现长截图拼接的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 链表面试笔试题目总结
- 下一篇: phpStorm解释器与服务器配置(解决