2022년 4월 10일 일요일

uiautomator2 로 screenshot 비교하기

uiautomator2로 android에서 자동화를 해보기위해서 이것저것 해보고 있습니다. 도움이 될만한 내용들을 정리해 보았습니다.


1. 현재앱 알아내기

print(d.app_current())

출력 형태

{'package': 'com.android.settings', 'activity': '.homepage.SettingsHomepageActivity', 'pid': 14781}


2. 앱 실행키시기

app_start로 실행합니다. 이때인자는 패키지 이름입니다. 앱을 실행 시킨 후 app_wait로 pid를 얻어 올 수 있습니다.

		d.app_start(test_package)
		pid = d.app_wait(test_package, timeout=20.0)
		if not pid:
			print(f"{test_package} is not running")
		else:
			print(f"{test_package} pid is {pid}")


3. 화면 구성값 가져오기

xml 형태로 저장됩니다.

		xml = d.dump_hierarchy()
		print("saving xml")
		write_file("hierarchy.xml",str(xml))

형태가 복잡해서 전체 예제를 실행시키면 저장하도록 해놨습니다. 파일을 열어보시길 추천합니다.


4. 스크린샷 찍어 저장하기

확장자에 따라서 png, jpg 형태로 저장이 가능합니다.

		image = d.screenshot()
		if not exists(test_image_name):
			image.save(test_image_name)
			print("save screenshot")


5. 키보내기

여러 종류의 키를 보낼 수 있습니다.

d.press("home")

숫자값으로 보낼때는 여기 참고

https://developer.android.com/reference/android/view/KeyEvent

텍스트는 아래 참고

home

back

left

right

up

down

center

menu

search

enter

delete ( or del)

recent (recent apps)

volume_up

volume_down

volume_mute

camera

power


6. 종합 screenshot 찍어서 이전 저장된 사진과 비교하기

전체 소스

from os.path import exists
import uiautomator2 as u2
import adbutils
import time
from PIL import Image
from PIL import ImageChops
import math, operator

test_image_name = "testimage.png"
test_package = "com.android.settings"


def write_file(filename,str_data):
	with open(filename, 'w', encoding="utf-8") as fp:
		fp.write(str_data)
		fp.close()
		
def images_are_similar(img1, img2, error=90):
	print(img1.size[0],img1.size[1]) # xsize, ysize
	diff = ImageChops.difference(img1, img2).histogram()
	# [ r 256 , g 256 , b 256 ] = 768 list
	print(len(diff))
	print(diff)
	sq = (value * (i % 256) ** 2 for i, value in enumerate(diff))
	sum_squares = sum(sq)
	print(sum_squares)
	rms = math.sqrt(sum_squares / float(img1.size[0] * img1.size[1]))
	print(rms)
	return rms < error
	
if __name__ == "__main__":
	d = None
	for dev in adbutils.adb.device_list():
		print("Dev:", dev)
		d = u2.connect(dev.serial)

	if d==None:
		print("please check connected devices (adb devices)")
	else:
		d.screen_on()
		d.unlock()
		d.press("home")

		print(d.app_current())
		d.app_start(test_package)
		pid = d.app_wait(test_package, timeout=20.0)
		if not pid:
			print(f"{test_package} is not running")
		else:
			print(f"{test_package} pid is {pid}")

		xml = d.dump_hierarchy()
		print("saving xml")
		write_file("hierarchy.xml",str(xml))
		image = d.screenshot()
		if not exists(test_image_name):
			image.save(test_image_name)
			print("save screenshot")
		else:
			# compare
			image.save("new"+test_image_name)
			pimage = Image.open(test_image_name)
			#for test sdiff = images_are_similar(image,image)
			sdiff = images_are_similar(image,pimage)
			print(sdiff)
		print(d.app_current())
		d.press("home")


7.이미지 비교 함수 설명

images_are_similar

여기에 넘어가는 이미지는 PIL type입니다. size[0], size[1]은 각각 가로 세로 크기입니다.

기본 원리는 image의 diff를 구하는것입니다. 이것은 ImageChops.difference(img1, img2) 와 같은 형태로 구할 수 있습니다.

다음으로는 histogram()을 취하게 되는데 histogram은 R, G, B 각각 256개로 모두 768 개를 지닙니다. 

일반적으로 delta를 수치로 표현할때 표준편차를 구해서 비교하는데 여기에서는 이미지 histogram의 표준 편차를 구해서 특정값보다 크게 되면 차이가 난다고 판단합니다. 임의의 기준을 여기에서는 90으로 정했으며 조정 가능한 값입니다.

같은 이미지로 시험하면 아래와 같은 histogram을 얻게 됩니다. 여기에 들어가 있는 2592000 는 가로*세로 즉 점의 갯수가 됩니다. 1080 2400 화면에서는 아래와 같습니다.

[2592000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2592000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2592000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

그리고 첫번째는 R의 0번값이며 2592000 수치를 나타낸다는 의미는 점을 빼줬기 때문에 0을 가진다는 의미입니다.

만약 다른 이미지로 테스트했다면 아래와 같은 형태가 될것입니다.


중간이미지가 diff(차)연산으로 나온 이미지인데 이것을 histogram을 해서 표준 편차를 구한다는 의미입니다.  

여기에서는 이미지가 같게 되면 rms 수치가 작아지게 되고 images_are_similar True값이 리턴됩니다.



댓글 없음:

댓글 쓰기