当前位置:   article > 正文

python3:android手机截长图的小工具_python 长截图

python 长截图

这个工具写下来遇到了不少坑,直到现在还没有完全解决,先记录下来吧,后面有机会再修改,或是有心的同学帮忙分析一下为什么?

 

主要实现以下功能:

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))

        这是截取是相同的两张图的同一个像素点,有可能是有差异的. 

  1. for i in range(int(img1_cropfile.size[0]*3/4)):
  2. # a/b/c为img1的RGB value
  3. # x/y/z为img2的RGB value
  4. a0, b0, c0 = img1_cropfile_pixel_list0[i]
  5. a1, b1, c1 = img1_cropfile_pixel_list1[i]
  6. a2, b2, c2 = img1_cropfile_pixel_list2[i]
  7. x0, y0, z0 = img2_cropfile_pixel_list0[i]
  8. x1, y1, z1 = img2_cropfile_pixel_list1[i]
  9. x2, y2, z2 = img2_cropfile_pixel_list2[i]
  10. if abs(a0 - x0) < 20 and abs(b0 - y0) < 20 and abs(c0 - z0) < 20:
  11. line0sum += 1
  12. if abs(a1 - x1) < 20 and abs(b1 - y1) < 20 and abs(c1 - z1) < 20:
  13. line1sum += 1
  14. if abs(a2 - x2) < 20 and abs(b2 - y2) < 20 and abs(c2 - z2) < 20:
  15. line2sum += 1
  16. # print(f'line0sum:{line0sum} line1sum:{line1sum} line2sum:{line2sum}')
  17. if line0sum == line1sum == line2sum == int(img1_cropfile.size[0]*3/4):
  18. print(f'h:{h}')
  19. # img1_cropfile_box = (0, 0, img1_cropfile.size[0], h)
  20. return h

   b)比对图片像素找相同的行时,只对比一行是不行的, 要同时比对多行才可以。如同时比对0, 100, 200行,这样才行之有效.

  1. img2_cropfile_pixel_list0 = list()
  2. img2_cropfile_pixel_list1 = list()
  3. img2_cropfile_pixel_list2 = list()
  4. for w in range(int(img2_cropfile.size[0]*3/4)):
  5. img2_cropfile_pixel_list0.append(img2_cropfile.getpixel((w, 0)))
  6. img2_cropfile_pixel_list1.append(img2_cropfile.getpixel((w, 100)))
  7. img2_cropfile_pixel_list2.append(img2_cropfile.getpixel((w, 200)))

   c)右侧通常有流动条或一些不变的信息时,比如很多联系人右侧的字母(用于快速定位联系人)会异常图片的像素比对,这时在对比时应该不比对右侧的信息,排除干扰

  1. # 只比对3/4行的内容即可,不用全部对比
  2. for w in range(int(img2_cropfile.size[0]*3/4))   

  3. 当前只能拼接两张图片,如多张则拼接异常,因为多张图片时找不到相同的行,原因是以下这句传入的img1每次都是一样的,一直找不到原因,希望有心人看到帮忙留言.

  1. # 将所有截图拼接成一个长图
  2. def sewImg(self):
  3. if len(self.imgPathList) == 1:
  4. shutil.move(self.imgPathList[0], getDesktopPath())
  5. return
  6. print('等待长图生成至桌面ing... ...')
  7. self.openImg()
  8. self.findHeadOverlap()
  9. self.findTailOverlap()
  10. img1 = self.imgInfoList[0]
  11. if self.tailoverlapBox == ():
  12. self.iscropTailoverlap = False
  13. for i in range(1, len(self.imgInfoList)):
  14. img2 = self.imgInfoList[i]
  15. # 对于img2是否为最后一张的处理是不一样的,因为当有时底部有重叠部时对于最后一张的处理会比较特殊
  16. if i == len(self.imgInfoList) - 1:
  17. # 感觉没有写错,但是每次传入img1都是self.imgInfoList[0]
  18. img1 = self.newImg(img1, img2, True)
  19. temp = img1
  20. print(f'img1.size: {img1.size}')
  21. else:
  22. img1 = self.newImg(img1, img2, False)
  23. temp = img1
  24. print(f'img1.size: {img1.size}')
  25. img1.save(getDesktopPath() + 'longImage.jpg')

实验图片如下:

完整的脚本如下: 

  1. from PIL import Image
  2. import os
  3. import subprocess
  4. import time
  5. import shutil
  6. import random
  7. # 得到PC桌面路径
  8. def getDesktopPath() -> str:
  9. return 'C:\\Users\\' + os.getlogin() + '\\Desktop\\'
  10. class Screenshot(object):
  11. def __init__(self, cmd='1'):
  12. # 用于存储各个截图的位置
  13. self.imgPathList = list()
  14. # 用于存储Image.open()打开截图后的数据
  15. self.imgInfoList = list()
  16. # 用于存储各个截图的像素信息
  17. self.imgDataList = list()
  18. # 要执行的截图类型
  19. self.cmd = cmd
  20. # 图片保存位置的flag
  21. self.save2destFlag = True
  22. # 头部相同区域的坐标info tuple(left,upper,right,lower)
  23. self.headoverlapBox = ()
  24. # 尾部相同区域的坐标info tuple(left,upper,right,lower)
  25. self.tailoverlapBox = ()
  26. # 尾部是否有重叠部分(注:头部一般是有重叠部分的)
  27. self.iscropTailoverlap = True
  28. # 在C根目录下新建test_screenshot目录
  29. if os.path.exists('c:\\test_screenshot') == False:
  30. os.mkdir('c:\\test_screenshot')
  31. # 执行截图动作
  32. def screenshot(self):
  33. imgname = str(time.time()).split('.')[0] + '.jpg'
  34. # 截图的cmd
  35. cmd = 'adb shell /system/bin/screencap -p /sdcard/' + imgname
  36. if subprocess.run(cmd, shell=True).returncode == 0:
  37. # 将截图文件copy至桌面的cmd
  38. if self.save2destFlag:
  39. cmd = 'adb pull /sdcard/' + imgname + ' ' + getDesktopPath() + imgname
  40. else:
  41. cmd = 'adb pull /sdcard/' + imgname + ' c:\\test_screenshot\\' + imgname
  42. if subprocess.run(cmd, shell=True).returncode == 0:
  43. self.imgPathList.append(imgname)
  44. if subprocess.run('adb shell rm /sdcard/' + imgname, shell=True).returncode == 0:
  45. print('本次截图成功!')
  46. # 从self.imgList中取出图片并将各个图片的像素info存在imgDataList中
  47. def openImg(self):
  48. for img in self.imgPathList:
  49. imgInfo = Image.open(img)
  50. imgData = imgInfo.getdata()
  51. self.imgInfoList.append(imgInfo)
  52. self.imgDataList.append(imgData)
  53. # 查找图1、图2头部的相同区域
  54. def findHeadOverlap(self, ratio=0.95):
  55. imgdata1, imgdata2 = self.imgDataList[0], self.imgDataList[1]
  56. img_width, img_height = self.imgDataList[0].size
  57. # print(img_width, img_height)
  58. for h in range(img_height): # 比较每一行
  59. totalForSame = 0
  60. totalForSame8 = 0
  61. for w in range(img_width): # 比较两张图片的每一个像素点info是否相同
  62. a, b, c = imgdata1.getpixel((w, h))
  63. x, y, z = imgdata2.getpixel((w, h))
  64. a8, b8, c8 = imgdata1.getpixel((w, h + 5))
  65. x8, y8, z8 = imgdata2.getpixel((w, h + 5))
  66. if abs(a - x) < 20 and abs(b - y) < 20 and abs(c - z) < 20:
  67. totalForSame += 1
  68. if abs(a8 - x8) < 20 and abs(b8 - y8) < 20 and abs(c8 - z8) < 20:
  69. totalForSame8 += 1
  70. # 每一行比较完成后,如果相同率小于0.85, 则会找到了相同的区域
  71. if totalForSame / img_width < ratio and totalForSame8 / img_width < ratio:
  72. self.headoverlapBox = (0, 0, img_width, h)
  73. print(f'找到啦:{self.headoverlapBox}')
  74. xImg = self.imgInfoList[0].crop(self.headoverlapBox)
  75. xImg.save(getDesktopPath() + 'head.jpg')
  76. break
  77. # 查找图1、图2尾部的相同区域
  78. def findTailOverlap(self, ratio=1):
  79. imgdata1, imgdata2 = self.imgDataList[0], self.imgDataList[1]
  80. img_width, img_height = self.imgDataList[0].size
  81. # print(img_width, img_height)
  82. # 从图像下面开始向上进行比较
  83. for h in range(img_height - 1, -1, -1):
  84. totalForSame = 0
  85. for w in range(img_width): # 比较两张图片的每一个像素点info是否相同
  86. a, b, c = imgdata1.getpixel((w, h))
  87. x, y, z = imgdata2.getpixel((w, h))
  88. if abs(a - x) < 20 and abs(b - y) < 20 and abs(c - z) < 20:
  89. totalForSame += 1
  90. # 每一行比较完成后,如果相同率小于0.95, 则会找到了相同的区域
  91. if totalForSame / img_width < ratio:
  92. # if h == img_height - 1:
  93. # break
  94. self.tailoverlapBox = (0, h, img_width, img_height)
  95. print(f'找到啦:{self.tailoverlapBox}')
  96. xImg = self.imgInfoList[0].crop(self.tailoverlapBox)
  97. xImg.save(getDesktopPath() + 'tail.jpg')
  98. break
  99. # 将两张图片拼接成一个新图片
  100. def newImg(self, img1, img2, isEnd: bool):
  101. if self.iscropTailoverlap is False:
  102. if self.headoverlapBox == ():
  103. return img1
  104. img2_cropbox_rm_head = (0,
  105. self.headoverlapBox[3]-1,
  106. img2.size[0],
  107. img2.size[1])
  108. img1_cropfile = img1
  109. img1_cropfilename = 'img1' + str(random.randint(1, 10000)) + '.jpg'
  110. img1_cropfile.save(getDesktopPath() + img1_cropfilename)
  111. img2_cropfile = img2.crop(img2_cropbox_rm_head)
  112. img2_cropfilename = 'img2' + str(random.randint(1, 10000)) + '.jpg'
  113. img2_cropfile.save(getDesktopPath() + img2_cropfilename)
  114. img1_cropfile_box = ()
  115. def findoverlapline1():
  116. # 查找重叠的行
  117. # 1, 分别取img1的lin0, line100, line200行的RGB值进行比对
  118. # 2, 如果三个值都相同则找到
  119. img2_cropfile_pixel_list0 = list()
  120. img2_cropfile_pixel_list1 = list()
  121. img2_cropfile_pixel_list2 = list()
  122. # 只取宽度的3/4是因为很多时候页面右侧会有滚动条或一直不变的字线序列(如联系人的ABCD),去掉这部分就可以正常查找了
  123. for w in range(int(img2_cropfile.size[0]*3/4)):
  124. img2_cropfile_pixel_list0.append(img2_cropfile.getpixel((w, 0)))
  125. img2_cropfile_pixel_list1.append(img2_cropfile.getpixel((w, 100)))
  126. img2_cropfile_pixel_list2.append(img2_cropfile.getpixel((w, 200)))
  127. # 在图片img1中自上而下的查找重叠的行
  128. for h in range(img1_cropfile.size[1] - 200):
  129. line0sum, line1sum, line2sum = 0, 0, 0
  130. img1_cropfile_pixel_list0 = list()
  131. img1_cropfile_pixel_list1 = list()
  132. img1_cropfile_pixel_list2 = list()
  133. for w in range(int(img1_cropfile.size[0]*3/4)):
  134. img1_cropfile_pixel_list0.append(img1_cropfile.getpixel((w, h)))
  135. img1_cropfile_pixel_list1.append(img1_cropfile.getpixel((w, h+100)))
  136. img1_cropfile_pixel_list2.append(img1_cropfile.getpixel((w, h+200)))
  137. for i in range(int(img1_cropfile.size[0]*3/4)):
  138. # a/b/c为img1的RGB value
  139. # x/y/z为img2的RGB value
  140. a0, b0, c0 = img1_cropfile_pixel_list0[i]
  141. a1, b1, c1 = img1_cropfile_pixel_list1[i]
  142. a2, b2, c2 = img1_cropfile_pixel_list2[i]
  143. x0, y0, z0 = img2_cropfile_pixel_list0[i]
  144. x1, y1, z1 = img2_cropfile_pixel_list1[i]
  145. x2, y2, z2 = img2_cropfile_pixel_list2[i]
  146. if abs(a0 - x0) < 30 and abs(b0 - y0) < 30 and abs(c0 - z0) < 30:
  147. line0sum += 1
  148. if abs(a1 - x1) < 30 and abs(b1 - y1) < 30 and abs(c1 - z1) < 30:
  149. line1sum += 1
  150. if abs(a2 - x2) < 30 and abs(b2 - y2) < 30 and abs(c2 - z2) < 30:
  151. line2sum += 1
  152. # print(f'line0sum:{line0sum} line1sum:{line1sum} line2sum:{line2sum}')
  153. if line0sum == line1sum == line2sum == img1_cropfile.size[0]:
  154. print(f'h:{h}')
  155. return h
  156. return False
  157. hFlag = findoverlapline1()
  158. if hFlag == False:
  159. pass
  160. else:
  161. img1_cropfile_box = (0, 0, img1_cropfile.size[0], hFlag)
  162. # 拼接图片
  163. if img1_cropfile_box == ():
  164. print('没有找到重叠的行')
  165. else:
  166. img1_cropfile = img1_cropfile.crop(img1_cropfile_box)
  167. img1_cropfile.save(getDesktopPath() + 'img1_cropfile.jpg')
  168. newimgfile = Image.new('RGB', (img1_cropfile.size[0], img1_cropfile.size[1] + img2_cropfile.size[1]))
  169. newimgfile.paste(img1_cropfile, (0, 0))
  170. newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
  171. newimgfile.save(getDesktopPath() + newimgfilename)
  172. newimgfile.paste(img2_cropfile, (0, img1_cropfile.size[1]))
  173. newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
  174. newimgfile.save(getDesktopPath() + newimgfilename)
  175. # return newimgfile
  176. if self.iscropTailoverlap is True:
  177. img1_cropbox_rm_tail = (0,
  178. 0,
  179. img1.size[0],
  180. self.tailoverlapBox[1])
  181. # 如果img2为最后一张图片时,img2不用去掉tail部分
  182. if isEnd:
  183. img2_cropbox_rm_head_tail = (0,
  184. self.headoverlapBox[3],
  185. img2.size[0],
  186. img2.size[1]
  187. )
  188. else:
  189. img2_cropbox_rm_head_tail = (0,
  190. self.headoverlapBox[3],
  191. img2.size[0],
  192. self.tailoverlapBox[1]
  193. )
  194. img1_cropfile = img1.crop(img1_cropbox_rm_tail)
  195. img1_cropfilename = 'img1' + str(random.randint(1, 10000)) + '.jpg'
  196. img1_cropfile.save(getDesktopPath() + img1_cropfilename)
  197. img2_cropfile = img2.crop(img2_cropbox_rm_head_tail)
  198. img2_cropfilename = 'img2' + str(random.randint(1, 10000)) + '.jpg'
  199. img2_cropfile.save(getDesktopPath() + img2_cropfilename)
  200. img1_cropfile_box = ()
  201. def findoverlapline2():
  202. # global img1_cropfile_box
  203. # 查找重叠的行
  204. # 1, 分别取img1的lin0, line15, line100行的RGB值进行比对
  205. # 2, 如果三个值都相同则找到
  206. img2_cropfile_pixel_list0 = list()
  207. img2_cropfile_pixel_list1 = list()
  208. img2_cropfile_pixel_list2 = list()
  209. for w in range(int(img2_cropfile.size[0]*3/4)):
  210. img2_cropfile_pixel_list0.append(img2_cropfile.getpixel((w, 0)))
  211. img2_cropfile_pixel_list1.append(img2_cropfile.getpixel((w, 100)))
  212. img2_cropfile_pixel_list2.append(img2_cropfile.getpixel((w, 200)))
  213. # 在图片img1中自上而下的查找重叠的行
  214. for h in range(img1_cropfile.size[1] - 200):
  215. line0sum, line1sum, line2sum = 0, 0, 0
  216. img1_cropfile_pixel_list0 = list()
  217. img1_cropfile_pixel_list1 = list()
  218. img1_cropfile_pixel_list2 = list()
  219. for w in range(int(img1_cropfile.size[0]*3/4)):
  220. img1_cropfile_pixel_list0.append(img1_cropfile.getpixel((w, h)))
  221. img1_cropfile_pixel_list1.append(img1_cropfile.getpixel((w, h+100)))
  222. img1_cropfile_pixel_list2.append(img1_cropfile.getpixel((w, h+200)))
  223. for i in range(int(img1_cropfile.size[0]*3/4)):
  224. # a/b/c为img1的RGB value
  225. # x/y/z为img2的RGB value
  226. a0, b0, c0 = img1_cropfile_pixel_list0[i]
  227. a1, b1, c1 = img1_cropfile_pixel_list1[i]
  228. a2, b2, c2 = img1_cropfile_pixel_list2[i]
  229. x0, y0, z0 = img2_cropfile_pixel_list0[i]
  230. x1, y1, z1 = img2_cropfile_pixel_list1[i]
  231. x2, y2, z2 = img2_cropfile_pixel_list2[i]
  232. if abs(a0 - x0) < 20 and abs(b0 - y0) < 20 and abs(c0 - z0) < 20:
  233. line0sum += 1
  234. if abs(a1 - x1) < 20 and abs(b1 - y1) < 20 and abs(c1 - z1) < 20:
  235. line1sum += 1
  236. if abs(a2 - x2) < 20 and abs(b2 - y2) < 20 and abs(c2 - z2) < 20:
  237. line2sum += 1
  238. # print(f'line0sum:{line0sum} line1sum:{line1sum} line2sum:{line2sum}')
  239. if line0sum == line1sum == line2sum == int(img1_cropfile.size[0]*3/4):
  240. print(f'h:{h}')
  241. # img1_cropfile_box = (0, 0, img1_cropfile.size[0], h)
  242. return h
  243. return False
  244. hFlag = findoverlapline2()
  245. if hFlag == False:
  246. pass
  247. else:
  248. img1_cropfile_box = (0, 0, img1_cropfile.size[0], hFlag)
  249. # 拼接图片
  250. findoverlapline2()
  251. if img1_cropfile_box == ():
  252. print('没有找到重叠的行')
  253. else:
  254. img1_cropfile = img1_cropfile.crop(img1_cropfile_box)
  255. newimgfile = Image.new('RGB', (img1.size[0], img1_cropfile.size[1] + img2_cropfile.size[1]))
  256. newimgfile.paste(img1_cropfile, (0, 0))
  257. newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
  258. newimgfile.save(getDesktopPath() + newimgfilename)
  259. newimgfile.paste(img2_cropfile, (0, img1_cropfile.size[1]))
  260. newimgfilename = 'newimgfile' + str(random.randint(1, 10000)) + '.jpg'
  261. newimgfile.save(getDesktopPath() + newimgfilename)
  262. return newimgfile
  263. # 将所有截图拼接成一个长图
  264. def sewImg(self):
  265. if len(self.imgPathList) == 1:
  266. shutil.move(self.imgPathList[0], getDesktopPath())
  267. return
  268. print('等待长图生成至桌面ing... ...')
  269. self.openImg()
  270. self.findHeadOverlap()
  271. self.findTailOverlap()
  272. img1 = self.imgInfoList[0]
  273. if self.tailoverlapBox == ():
  274. self.iscropTailoverlap = False
  275. for i in range(1, len(self.imgInfoList)):
  276. img2 = self.imgInfoList[i]
  277. # 对于img2是否为最后一张的处理是不一样的,因为当有时底部有重叠部时对于最后一张的处理会比较特殊
  278. if i == len(self.imgInfoList) - 1:
  279. # 感觉没有写错,但是每次传入img1都是self.imgInfoList[0]
  280. img1 = self.newImg(img1, img2, True)
  281. temp = img1
  282. print(f'img1.size: {img1.size}')
  283. else:
  284. img1 = self.newImg(img1, img2, False)
  285. temp = img1
  286. print(f'img1.size: {img1.size}')
  287. img1.save(getDesktopPath() + 'longImage.jpg')
  288. def run(self):
  289. # 只截一张图的case
  290. subprocess.run('adb wait-for-device', shell=True)
  291. if self.cmd == '1':
  292. self.screenshot()
  293. # 需要截长图的case
  294. if self.cmd in ('1,', '1L', '1Long', '1long', '11'):
  295. # 修改工作目录
  296. os.chdir('c:\\test_screenshot')
  297. # 修改save2destFlag,保证截长图时保存至c:\\test_screenshot
  298. self.save2destFlag = False
  299. # 截图过程
  300. self.screenshot()
  301. w, h = Image.open(self.imgPathList[0]).size
  302. cmd = 'adb shell input swipe ' + str(int(w / 2)) + ' ' + str(int(h * 2.5 / 4)) + ' ' + str(
  303. int(w / 2)) + ' ' + str(int(h / 4))
  304. self.swipe_distance = int(h * 3 / 4) - int(h / 4)
  305. enter = input('输入回车键继续截图,q结束长截图:').strip().lower()
  306. while True:
  307. if enter == 'q':
  308. print(f'当前共截图{len(self.imgPathList)}张.')
  309. break
  310. elif enter == '':
  311. subprocess.run(cmd, shell=True, encoding='utf-8', capture_output=True)
  312. self.screenshot()
  313. enter = input('输入回车键继续截图,q结束长截图:').strip().lower()
  314. else:
  315. enter = input('输入回车键继续截图,q结束长截图:').strip().lower()
  316. # 长图拼接过程
  317. start = time.time()
  318. self.sewImg()
  319. end = time.time()
  320. print(f'共耗时:{end - start:>0.2f}秒')
  321. if __name__ == '__main__':
  322. key = input('(截图请输入1, 长截图请输入1,):')
  323. if key.strip() in ('1', '1,', '11'):
  324. Screenshot(key).run()
  325. else:
  326. print('输入有误!!!')

 

  

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/474938
推荐阅读
相关标签
  

闽ICP备14008679号