赞
踩
这个工具写下来遇到了不少坑,直到现在还没有完全解决,先记录下来吧,后面有机会再修改,或是有心的同学帮忙分析一下为什么?
主要实现以下功能:
1. 在手机上截一张图至桌面.
2. 在手机在连接截多张图片拼接成一张图(按回车后手机屏幕会自动滚动1/2屏,q退出截图并拼接图片.
主要遇到的坑:
1. 多重for循环发现用break只能退出一层for循环(后用函数返回值解决)
2. 图片处理用的是PIL库, 在将两张图进行拼接时,必须先找到两张图相同的行才可以完美拼接。这个过程走了很多路。
a)截的两张图片,我们视觉上看到相同图像的同一行像素点RGB(x, y, z)其实是有一些偏差的(>30均可认为其像素点是相同的), 如果不明白这一点,估计在找原因debug时会很郁闷。
如a, b, c = img1data.getpixel((25, 25))
x, y, z = img2data.getpixel((25,25))
这是截取是相同的两张图的同一个像素点,有可能是有差异的.
- for i in range(int(img1_cropfile.size[0]*3/4)):
- # a/b/c为img1的RGB value
- # x/y/z为img2的RGB value
- a0, b0, c0 = img1_cropfile_pixel_list0[i]
- a1, b1, c1 = img1_cropfile_pixel_list1[i]
- a2, b2, c2 = img1_cropfile_pixel_list2[i]
- x0, y0, z0 = img2_cropfile_pixel_list0[i]
- x1, y1, z1 = img2_cropfile_pixel_list1[i]
- x2, y2, z2 = img2_cropfile_pixel_list2[i]
- if abs(a0 - x0) < 20 and abs(b0 - y0) < 20 and abs(c0 - z0) < 20:
- line0sum += 1
- if abs(a1 - x1) < 20 and abs(b1 - y1) < 20 and abs(c1 - z1) < 20:
- line1sum += 1
- if abs(a2 - x2) < 20 and abs(b2 - y2) < 20 and abs(c2 - z2) < 20:
- line2sum += 1
- # print(f'line0sum:{line0sum} line1sum:{line1sum} line2sum:{line2sum}')
- if line0sum == line1sum == line2sum == int(img1_cropfile.size[0]*3/4):
- print(f'h:{h}')
- # img1_cropfile_box = (0, 0, img1_cropfile.size[0], h)
- return h
b)比对图片像素找相同的行时,只对比一行是不行的, 要同时比对多行才可以。如同时比对0, 100, 200行,这样才行之有效.
- img2_cropfile_pixel_list0 = list()
- img2_cropfile_pixel_list1 = list()
- img2_cropfile_pixel_list2 = list()
- for w in range(int(img2_cropfile.size[0]*3/4)):
- img2_cropfile_pixel_list0.append(img2_cropfile.getpixel((w, 0)))
- img2_cropfile_pixel_list1.append(img2_cropfile.getpixel((w, 100)))
- img2_cropfile_pixel_list2.append(img2_cropfile.getpixel((w, 200)))
c)右侧通常有流动条或一些不变的信息时,比如很多联系人右侧的字母(用于快速定位联系人)会异常图片的像素比对,这时在对比时应该不比对右侧的信息,排除干扰
- # 只比对3/4行的内容即可,不用全部对比
- for w in range(int(img2_cropfile.size[0]*3/4))
3. 当前只能拼接两张图片,如多张则拼接异常,因为多张图片时找不到相同的行,原因是以下这句传入的img1每次都是一样的,一直找不到原因,希望有心人看到帮忙留言.
- # 将所有截图拼接成一个长图
- def sewImg(self):
- if len(self.imgPathList) == 1:
- shutil.move(self.imgPathList[0], getDesktopPath())
- return
-
- print('等待长图生成至桌面ing... ...')
- self.openImg()
- self.findHeadOverlap()
- self.findTailOverlap()
- img1 = self.imgInfoList[0]
-
- if self.tailoverlapBox == ():
- self.iscropTailoverlap = False
- for i in range(1, len(self.imgInfoList)):
- img2 = self.imgInfoList[i]
- # 对于img2是否为最后一张的处理是不一样的,因为当有时底部有重叠部时对于最后一张的处理会比较特殊
- if i == len(self.imgInfoList) - 1:
- # 感觉没有写错,但是每次传入img1都是self.imgInfoList[0]
- img1 = self.newImg(img1, img2, True)
- temp = img1
- print(f'img1.size: {img1.size}')
- else:
- img1 = self.newImg(img1, img2, False)
- temp = img1
- print(f'img1.size: {img1.size}')
- img1.save(getDesktopPath() + 'longImage.jpg')
实验图片如下:
完整的脚本如下:
- from PIL import Image
- import os
- import subprocess
- import time
- import shutil
- import random
-
-
- # 得到PC桌面路径
- def getDesktopPath() -> str:
- return 'C:\\Users\\' + os.getlogin() + '\\Desktop\\'
-
-
- class Screenshot(object):
- def __init__(self, cmd='1'):
- # 用于存储各个截图的位置
- self.imgPathList = list()
- # 用于存储Image.open()打开截图后的数据
- self.imgInfoList = list()
- # 用于存储各个截图的像素信息
- self.imgDataList = list()
-
- # 要执行的截图类型
- self.cmd = cmd
- # 图片保存位置的flag
- self.save2destFlag = True
-
- # 头部相同区域的坐标info tuple(left,upper,right,lower)
- self.headoverlapBox = ()
- # 尾部相同区域的坐标info tuple(left,upper,right,lower)
- self.tailoverlapBox = ()
-
- # 尾部是否有重叠部分(注:头部一般是有重叠部分的)
- self.iscropTailoverlap = True
-
- # 在C根目录下新建test_screenshot目录
- if os.path.exists('c:\\test_screenshot') == False:
- os.mkdir('c:\\test_screenshot')
-
- # 执行截图动作
- def screenshot(self):
- imgname = str(time.time()).split('.')[0] + '.jpg'
- # 截图的cmd
- cmd = 'adb shell /system/bin/screencap -p /sdcard/' + imgname
- if subprocess.run(cmd, shell=True).returncode == 0:
- # 将截图文件copy至桌面的cmd
- if self.save2destFlag:
- cmd = 'adb pull /sdcard/' + imgname + ' ' + getDesktopPath() + imgname
- else:
- cmd = 'adb pull /sdcard/' + imgname + ' c:\\test_screenshot\\' + imgname
- if subprocess.run(cmd, shell=True).returncode == 0:
- self.imgPathList.append(imgname)
- if subprocess.run('adb shell rm /sdcard/' + imgname, shell=True).returncode == 0:
- print('本次截图成功!')
-
- # 从self.imgList中取出图片并将各个图片的像素info存在imgDataList中
- def openImg(self):
- for img in self.imgPathList:
- imgInfo = Image.open(img)
- imgData = imgInfo.getdata()
- self.imgInfoList.append(imgInfo)
- self.imgDataList.append(imgData)
-
- # 查找图1、图2头部的相同区域
- def findHeadOverlap(self, ratio=0.95):
- imgdata1, imgdata2 = self.imgDataList[0], self.imgDataList[1]
- img_width, img_height = self.imgDataList[0].size
- # print(img_width, img_height)
- for h in range(img_height): # 比较每一行
- totalForSame = 0
- totalForSame8 = 0
- for w in range(img_width): # 比较两张图片的每一个像素点info是否相同
- a, b, c = imgdata1.getpixel((w, h))
- x, y, z = imgdata2.getpixel((w, h))
- a8, b8, c8 = imgdata1.getpixel((w, h + 5))
- x8, y8, z8 = imgdata2.getpixel((w, h + 5))
- if abs(a - x) < 20 and abs(b - y) < 20 and abs(c - z) < 20:
- totalForSame += 1
- if abs(a8 - x8) < 20 and abs(b8 - y8) < 20 and abs(c8 - z8) < 20:
- totalForSame8 += 1
- # 每一行比较完成后,如果相同率小于0.85, 则会找到了相同的区域
- if totalForSame / img_width < ratio and totalForSame8 / img_width < ratio:
- self.headoverlapBox = (0, 0, img_width, h)
- print(f'找到啦:{self.headoverlapBox}')
- xImg = self.imgInfoList[0].crop(self.headoverlapBox)
- xImg.save(getDesktopPath() + 'head.jpg')
- break
-
- # 查找图1、图2尾部的相同区域
- def findTailOverlap(self, ratio=1):
- imgdata1, imgdata2 = self.imgDataList[0], self.imgDataList[1]
- img_width, img_height = self.imgDataList[0].size
- # print(img_width, img_height)
- # 从图像下面开始向上进行比较
- for h in range(img_height - 1, -1, -1):
- totalForSame = 0
- for w in range(img_width): # 比较两张图片的每一个像素点info是否相同
- a, b, c = imgdata1.getpixel((w, h))
- x, y, z = imgdata2.getpixel((w, h))
- if abs(a - x) < 20 and abs(b - y) < 20 and abs(c - z) < 20:
- totalForSame += 1
- # 每一行比较完成后,如果相同率小于0.95, 则会找到了相同的区域
- if totalForSame / img_width < ratio:
- # if h == img_height - 1:
- # break
- self.tailoverlapBox = (0, h, img_width, img_height)
- print(f'找到啦:{self.tailoverlapBox}')
- xImg = self.imgInfoList[0].crop(self.tailoverlapBox)
- xImg.save(getDesktopPath() + 'tail.jpg')
- break
-
- # 将两张图片拼接成一个新图片
- def newImg(self, img1, img2, isEnd: bool):
- if self.iscropTailoverlap is False:
- if self.headoverlapBox == ():
- return img1
- img2_cropbox_rm_head = (0,
- self.headoverlapBox[3]-1,
- img2.size[0],
- img2.size[1])
- img1_cropfile = img1
- img1_cropfilename = 'img1' + str(random.randint(1, 10000)) + '.jpg'
- img1_cropfile.save(getDesktopPath() + img1_cropfilename)
- img2_cropfile = img2.crop(img2_cropbox_rm_head)
- img2_cropfilename = 'img2' + str(random.randint(1, 10000)) + '.jpg'
- img2_cropfile.save(getDesktopPath() + img2_cropfilename)
-
- img1_cropfile_box = ()
-
- def findoverlapline1():
- # 查找重叠的行
- # 1, 分别取img1的lin0, line100, line200行的RGB值进行比对
- # 2, 如果三个值都相同则找到
- img2_cropfile_pixel_list0 = list()
- img2_cropfile_pixel_list1 = list()
- img2_cropfile_pixel_list2 = list()
- # 只取宽度的3/4是因为很多时候页面右侧会有滚动条或一直不变的字线序列(如联系人的ABCD),去掉这部分就可以正常查找了
- for w in range(int(img2_cropfile.size[0]*3/4)):
- img2_cropfile_pixel_list0.append(img2_cropfile.getpixel((w, 0)))
- img2_cropfile_pixel_list1.append(img2_cropfile.getpixel((w, 100)))
- img2_cropfile_pixel_list2.append(img2_cropfile.getpixel((w, 200)))
-
- # 在图片img1中自上而下的查找重叠的行
- for h in range(img1_cropfile.size[1] - 200):
- line0sum, line1sum, line2sum = 0, 0, 0
- img1_cropfile_pixel_list0 = list()
- img1_cropfile_pixel_list1 = list()
- img1_cropfile_pixel_list2 = list()
-
- for w in range(int(img1_cropfile.size[0]*3/4)):
- img1_cropfile_pixel_list0.append(img1_cropfile.getpixel((w, h)))
- img1_cropfile_pixel_list1.append(img1_cropfile.getpixel((w, h+100)))
- img1_cropfile_pixel_list2.append(img1_cropfile.getpixel((w, h+200)))
-
- for i in range(int(img1_cropfile.size[0]*3/4)):
- # a/b/c为img1的RGB value
- # x/y/z为img2的RGB value
- a0, b0, c0 = img1_cropfile_pixel_list0[i]
- a1, b1, c1 = img1_cropfile_pixel_list1[i]
- a2, b2, c2 = img1_cropfile_pixel_list2[i]
- x0, y0, z0 = img2_cropfile_pixel_list0[i]
- x1, y1, z1 = img2_cropfile_pixel_list1[i]
- x2, y2, z2 = img2_cropfile_pixel_list2[i]
- if abs(a0 - x0) < 30 and abs(b0 - y0) < 30 and abs(c0 - z0) < 30:
- line0sum += 1
- if abs(a1 - x1) < 30 and abs(b1 - y1) < 30 and abs(c1 - z1) < 30:
- line1sum += 1
- if abs(a2 - x2) < 30 and abs(b2 - y2) < 30 and abs(c2 - z2) < 30:
- line2sum += 1
- # print(f'line0sum:{line0sum} line1sum:{line1sum} line2sum:{line2sum}')
- if line0sum == line1sum == line2sum == img1_cropfile.size[0]:
- print(f'h:{h}')
- return h
- return False
- hFlag = findoverlapline1()
- if hFlag == False:
- pass
- else:
- img1_cropfile_box = (0, 0, img1_cropfile.size[0], hFlag)
- # 拼接图片
- if img1_cropfile_box == ():
- print('没有找到重叠的行')
- else:
- img1_cropfile = img1_cropfile.crop(img1_cropfile_box)
- img1_cropfile.save(getDesktopPath() + 'img1_cropfile.jpg')
- newimgfile = Image.new('RGB', (img1_cropfile.size[0], img1_cropfile.size[1] + img2_cropfile.size[1]))
- newimgfile.paste(img1_cropfile, (0, 0))
- newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
- newimgfile.save(getDesktopPath() + newimgfilename)
- newimgfile.paste(img2_cropfile, (0, img1_cropfile.size[1]))
- newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
- newimgfile.save(getDesktopPath() + newimgfilename)
- # return newimgfile
- if self.iscropTailoverlap is True:
- img1_cropbox_rm_tail = (0,
- 0,
- img1.size[0],
- self.tailoverlapBox[1])
- # 如果img2为最后一张图片时,img2不用去掉tail部分
- if isEnd:
- img2_cropbox_rm_head_tail = (0,
- self.headoverlapBox[3],
- img2.size[0],
- img2.size[1]
- )
- else:
- img2_cropbox_rm_head_tail = (0,
- self.headoverlapBox[3],
- img2.size[0],
- self.tailoverlapBox[1]
- )
-
- img1_cropfile = img1.crop(img1_cropbox_rm_tail)
- img1_cropfilename = 'img1' + str(random.randint(1, 10000)) + '.jpg'
- img1_cropfile.save(getDesktopPath() + img1_cropfilename)
- img2_cropfile = img2.crop(img2_cropbox_rm_head_tail)
- img2_cropfilename = 'img2' + str(random.randint(1, 10000)) + '.jpg'
- img2_cropfile.save(getDesktopPath() + img2_cropfilename)
-
- img1_cropfile_box = ()
-
- def findoverlapline2():
- # global img1_cropfile_box
- # 查找重叠的行
- # 1, 分别取img1的lin0, line15, line100行的RGB值进行比对
- # 2, 如果三个值都相同则找到
- img2_cropfile_pixel_list0 = list()
- img2_cropfile_pixel_list1 = list()
- img2_cropfile_pixel_list2 = list()
- for w in range(int(img2_cropfile.size[0]*3/4)):
- img2_cropfile_pixel_list0.append(img2_cropfile.getpixel((w, 0)))
- img2_cropfile_pixel_list1.append(img2_cropfile.getpixel((w, 100)))
- img2_cropfile_pixel_list2.append(img2_cropfile.getpixel((w, 200)))
-
- # 在图片img1中自上而下的查找重叠的行
- for h in range(img1_cropfile.size[1] - 200):
- line0sum, line1sum, line2sum = 0, 0, 0
- img1_cropfile_pixel_list0 = list()
- img1_cropfile_pixel_list1 = list()
- img1_cropfile_pixel_list2 = list()
- for w in range(int(img1_cropfile.size[0]*3/4)):
- img1_cropfile_pixel_list0.append(img1_cropfile.getpixel((w, h)))
- img1_cropfile_pixel_list1.append(img1_cropfile.getpixel((w, h+100)))
- img1_cropfile_pixel_list2.append(img1_cropfile.getpixel((w, h+200)))
-
- for i in range(int(img1_cropfile.size[0]*3/4)):
- # a/b/c为img1的RGB value
- # x/y/z为img2的RGB value
- a0, b0, c0 = img1_cropfile_pixel_list0[i]
- a1, b1, c1 = img1_cropfile_pixel_list1[i]
- a2, b2, c2 = img1_cropfile_pixel_list2[i]
- x0, y0, z0 = img2_cropfile_pixel_list0[i]
- x1, y1, z1 = img2_cropfile_pixel_list1[i]
- x2, y2, z2 = img2_cropfile_pixel_list2[i]
- if abs(a0 - x0) < 20 and abs(b0 - y0) < 20 and abs(c0 - z0) < 20:
- line0sum += 1
- if abs(a1 - x1) < 20 and abs(b1 - y1) < 20 and abs(c1 - z1) < 20:
- line1sum += 1
- if abs(a2 - x2) < 20 and abs(b2 - y2) < 20 and abs(c2 - z2) < 20:
- line2sum += 1
- # print(f'line0sum:{line0sum} line1sum:{line1sum} line2sum:{line2sum}')
- if line0sum == line1sum == line2sum == int(img1_cropfile.size[0]*3/4):
- print(f'h:{h}')
- # img1_cropfile_box = (0, 0, img1_cropfile.size[0], h)
- return h
- return False
-
- hFlag = findoverlapline2()
- if hFlag == False:
- pass
- else:
- img1_cropfile_box = (0, 0, img1_cropfile.size[0], hFlag)
-
- # 拼接图片
- findoverlapline2()
- if img1_cropfile_box == ():
- print('没有找到重叠的行')
- else:
- img1_cropfile = img1_cropfile.crop(img1_cropfile_box)
-
- newimgfile = Image.new('RGB', (img1.size[0], img1_cropfile.size[1] + img2_cropfile.size[1]))
- newimgfile.paste(img1_cropfile, (0, 0))
- newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
- newimgfile.save(getDesktopPath() + newimgfilename)
- newimgfile.paste(img2_cropfile, (0, img1_cropfile.size[1]))
- newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
- newimgfile.save(getDesktopPath() + newimgfilename)
- return newimgfile
-
- # 将所有截图拼接成一个长图
- def sewImg(self):
- if len(self.imgPathList) == 1:
- shutil.move(self.imgPathList[0], getDesktopPath())
- return
-
- print('等待长图生成至桌面ing... ...')
- self.openImg()
- self.findHeadOverlap()
- self.findTailOverlap()
- img1 = self.imgInfoList[0]
-
- if self.tailoverlapBox == ():
- self.iscropTailoverlap = False
- for i in range(1, len(self.imgInfoList)):
- img2 = self.imgInfoList[i]
- # 对于img2是否为最后一张的处理是不一样的,因为当有时底部有重叠部时对于最后一张的处理会比较特殊
- if i == len(self.imgInfoList) - 1:
- # 感觉没有写错,但是每次传入img1都是self.imgInfoList[0]
- img1 = self.newImg(img1, img2, True)
- temp = img1
- print(f'img1.size: {img1.size}')
- else:
- img1 = self.newImg(img1, img2, False)
- temp = img1
- print(f'img1.size: {img1.size}')
- img1.save(getDesktopPath() + 'longImage.jpg')
-
- def run(self):
- # 只截一张图的case
- subprocess.run('adb wait-for-device', shell=True)
- if self.cmd == '1':
- self.screenshot()
- # 需要截长图的case
- if self.cmd in ('1,', '1L', '1Long', '1long', '11'):
- # 修改工作目录
- os.chdir('c:\\test_screenshot')
- # 修改save2destFlag,保证截长图时保存至c:\\test_screenshot
- self.save2destFlag = False
- # 截图过程
- self.screenshot()
- w, h = Image.open(self.imgPathList[0]).size
- cmd = 'adb shell input swipe ' + str(int(w / 2)) + ' ' + str(int(h * 2.5 / 4)) + ' ' + str(
- int(w / 2)) + ' ' + str(int(h / 4))
- self.swipe_distance = int(h * 3 / 4) - int(h / 4)
- enter = input('输入回车键继续截图,q结束长截图:').strip().lower()
- while True:
- if enter == 'q':
- print(f'当前共截图{len(self.imgPathList)}张.')
- break
- elif enter == '':
- subprocess.run(cmd, shell=True, encoding='utf-8', capture_output=True)
- self.screenshot()
- enter = input('输入回车键继续截图,q结束长截图:').strip().lower()
- else:
- enter = input('输入回车键继续截图,q结束长截图:').strip().lower()
- # 长图拼接过程
- start = time.time()
- self.sewImg()
- end = time.time()
- print(f'共耗时:{end - start:>0.2f}秒')
-
-
- if __name__ == '__main__':
- key = input('(截图请输入1, 长截图请输入1,):')
- if key.strip() in ('1', '1,', '11'):
- Screenshot(key).run()
- else:
- print('输入有误!!!')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。