🚀

adb経由でスクショをPythonに高速取り込み

2021/11/04に公開

遅延が気になってきたので見直しました。

今まで使っていた方法

adb exec-out screencap -pで出力されるpngバイト列をホストにリダイレクトして保存し、PILやcv2で読み込んでいました。

import cv2

def screencap(fname="tmp.png"):
    subprocess.run(f"adb exec-out screencap -p > {fname}", shell=True)
    return cv2.imread(fname)

バイト列をメモリ上で変換して読み込む(ファイルを介さない)

https://stackoverflow.com/questions/48304210/how-to-show-adb-screenshot-directly-in-python

https://pupli.net/2020/09/read-adb-screencap-raw-image-using-python/

np.fromstringはwarningが出るので、np.frombufferに変えました。
cv2の方はpngに変換する処理をなんとかホスト側に持っていけないでしょうか?
画像のフォーマットとかよくわからないのでコピペですみません。

import cv2
import numpy as np
from PIL import Image

def screencap2cv2():
    pipe = subprocess.Popen(
        "adb shell screencap -p",
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE, shell=True)
    image_bytes = pipe.stdout.read()
    return cv2.imdecode(np.frombuffer(image_bytes, np.uint8), cv2.IMREAD_COLOR)
    
def screencap2pil(width: int, height: int):
    pipe = subprocess.Popen(
        "adb shell screencap",
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE, shell=True)
    img_bytes = pipe.stdout.read()
    return Image.frombuffer('RGBA', (width, height), img_bytes[12:], 'raw', 'RGBX', 0, 1)

scrcpyを経由する

少し方向性が変わってしまうのですが、adbを経由しているのは嘘ではないので載せておきます。
scrcpyのpythonクライアントとして、比較的にメンテされているleng-yue/py-scrcpy-clientを使いました。
https://github.com/leng-yue/py-scrcpy-client

import scrcpy
import numpy as np

client = scrcpy.Client(device="YOUR_ANDROID_DEVICE_ID", max_fps=5)
client.start(threaded=True)
while True:
    if isinstance(client.last_frame, np.ndarray):
        # client.last_frame(cv2のRGB順序)をここで調理する
	...
    if flag_to_stop:
        client.stop()
        break

比較

最速は当然ですがscrcpyです。ただ、ホストに負荷をかけないために実機を使っていることを踏まえると、scrcpyは重量級すぎる気がします。

私の実機環境で上の2つを比べると、必要な時間は約66%早くなりました。お試しあれ。

Discussion