screencap是Android的截屏工具。通过调用访问SurfaceFlinger服务或读取 /dev/graphics/fb0文件来实现屏幕截取。
import subprocess
import cv2
import numpy as np
def get_screenshot():
# 使用subprocess的Popen调用adb shell命令,并将结果保存到PIPE管道中
process = subprocess.Popen('adb shell screencap -p', shell=True, stdout=subprocess.PIPE)
# 读取管道中的数据
screenshot = process.stdout.read()
# 将读取的字节流数据的回车换行替换成'\n'
binary_screenshot = screenshot.replace(b'\r\n', b'\n')
# 使用numpy和imdecode将二进制数据转换成cv2的mat图片格式
img_screenshot = cv2.imdecode(np.frombuffer(binary_screenshot, np.uint8), cv2.IMREAD_COLOR)
return img_screenshot
import subprocess
import cv2
def adb(order):
return subprocess.Popen(order, shell=True, stdout=subprocess.PIPE, encoding='UTF-8').stdout
def get_screenshot():
adb('adb shell /system/bin/screencap -p /sdcard/screenshot.png') # 将截图保存到SDCard
adb('adb pull /sdcard/screenshot.png ./screenshot.png')# 将截图从手机拉取到电脑
adb('adb shell rm /sdcard/screenshot.png')# 删除手机端的截图
image = cv2.imread('./screenshot.png') # 使用imread读取截图
return image
appium 是一个自动化测试开源工具,支持 iOS 平台和 Android 平台上的原生应用,web应用和混合应用。
appium的安装就不在这里介绍了
关于appium安装配置时遇到的问题(传送门)
#-*- coding: UTF-8 -*-
import os
import time
import unittest
import base64
import numpy as np
import cv2
from appium import webdriver
# 用于路径的转换
PATH = lambda p: os.path.abspath(
os.path.join(os.path.dirname(__file__), p)
)
desired_caps={}
desired_caps['platformName']='Android'
desired_caps['version']='6.0.0'
desired_caps['deviceName']='MI MAX 2'#这是测试机的型号,可以查看手机的关于本机选项获得
# desired_caps['app'] = PATH('mobileqq.apk')#被测试的App在电脑上的位置
# print(desired_caps['app'])
desired_caps['appPackage'] = 'com.tencent.mobileqq'
desired_caps['appActivity'] = 'com.tencent.mobileqq.activity.SplashActivity'
driver=webdriver.Remote('http://127.0.0.1:4725/wd/hub',desired_caps) # 访问端口,用于获取数据
time.sleep(3)
# 方法一:
# 使用appium的get_screenshot_as_flie(image_save_path),直接将图片数据保存早电脑端
# 然后通过cv2读取
while True:
driver.get_screenshot_as_file(screen_save_path)
start = time.clock()
img = cv2.imread('./screenshot.png')
cv2.imshow('test', img)
cv2.waitKey(1)
# 方法二:
# 使用appium的get_screenshot_as_base64()获得截屏的数据流
# 然后通过base64解析数据格式
# 使用numpy再次转换成uint8格式
# 最后使用cv2自带的imdecode转换成mat格式
while(True):
base64data = driver.get_screenshot_as_base64()
imgData = base64.b64decode(base64data)
nparr = np.fromstring(imgData,np.uint8)
img = cv2.imdecode(nparr, 1)
cv2.imshow('test', img)
cv2.waitKey(1)
minicap是开源项目STF(Smartphone Test Farm)中的一个工具,负责屏幕显示。
minicap的配置也不介绍了,网上有很多
网上提供了很多种连续获取图片的方式,但是很多时候,在获取一张图片之后就马上需要对其进行处理,这样的情况连续获取图片的方式并不合适
但是由于minicap本身传输的设定,手机端的minicap会源源不断的想socket传输图片信息,导致再次通过socket获得图片时,并不一定是当前的图片信息
这个时候采用的是重新断开socket连接的方式来再次获得图片
# 连接端口
import socket
import cv2
import numpy as np
def get_image()
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect(('localhost', 7777))
readBannerBytes = 0
bannerLength = 24
readFrameBytes = 0
frameBodyLengthRemaining = 0
frameBody = ''
while True:
chunk = self.client_socket.recv(12000)
if len(chunk) == 0:
continue
cursor = 0
while cursor < len(chunk):
# 跳过前面24位baner信息
if (readBannerBytes < bannerLength):
cursor += 1
readBannerBytes += 1
elif readFrameBytes < 4: # 在一开始的24位baner信息之后就是4位图片长度的信息
frameBodyLengthRemaining += (int(hex(chunk[cursor]), 16) << (readFrameBytes * 8))
cursor += 1
readFrameBytes += 1
else:
# 如果当前剩下的数据长度超过了图片还未读取的长度,说明这里包含了下一张图片的信息(由于不是连续读取,所以不考虑这些信息)
if len(chunk) - cursor >= frameBodyLengthRemaining:
frameBody = frameBody + chunk[cursor:(cursor + frameBodyLengthRemaining)]
if hex(frameBody[0]) != '0xff' or hex(frameBody[1]) != '0xd8':
exit()
img = np.array(bytearray(frameBody))
img = cv2.imdecode(img, 1)
self.client_socket.close()
return img
else:
# 这次数据的读取还未达到图片的数据的长度
frameBody = bytes(list(frameBody) + list(chunk[cursor:len(chunk)]))
frameBodyLengthRemaining -= (len(chunk) - cursor)
readFrameBytes += len(chunk) - cursor
cursor = len(chunk)
if __name__ == '__main__':
image = get_image()