9
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Pythonで指定した範囲のスクリーンショットを撮る

Last updated at Posted at 2022-12-21

概要

みんな大好き大乱闘スマッシュブラザーズ(以下スマブラ)!!
私事ながら,スマブラ配信をしています.チャンネル登録お願いします!
スマブラ×機械学習でなんかできないかなー,と思っていたところこんな記事を発見しました.
画像認識でスマブラの戦績を自動で作成するツールを作ろう

最終的に何を作るかは置いといて,面白そうだしとりあえずやってみよう!
配信画面からスマブラのスクリーンショットを撮る→画像認識でなんかする
ざっくりこんな感じで進めます.
今回の目標は範囲を指定してスクリーンショットを撮ることです.
配信画面全体ではなく,
screenshot.png
ゲーム画面のみを指定してスクリーンショットを撮りたい!
screenshot1.png

スクリーンショットを撮る

pyautoguiってやつを使えばスクリーンショットを撮れるらしい.

$ pip install pyautogui

どうやらスクリーンショット以外にも,マウスやキーボードの操作も行えるみたい.気になる方はドキュメント読んでみてください.
pyautoguiのドキュメント

今回はスクリーンショットを撮って画像認識をしたいだけなので,スクリーンショットを撮る方法だけ紹介します.

import pyautogui
# 全画面のスクリーンショットを撮る場合
img1 = pyautogui.screenshot('my_screenshot_all.png')
# 範囲を指定する場合
img2 = pyautogui.screenshot('my_screenshot_region.png', region=(left,top,width,height))

はい,これだけで全画面のスクリーンショットが指定したファイル名で保存されます.
超簡単!!だけど,全画面のスクリーンショットを一枚撮って保存するだけで私の環境だと0.26秒ぐらいかかりました.うーん,遅いけどまぁいいか.

途中アクセスを要求されたら,
セキュリティとプライバシー→画面収録
からターミナルに画面収録の許可を与えてください.与えないとデスクトップしか撮れません.
スクリーンショット 2022-12-21 2.50.19.png

スクリーンショットを撮る範囲を指定する

本来はゲーム画面を自動で検出したかったけど,配信レイアウトは人それぞれなので今回は汎用性を上げるためにクリックで指定できるようにします.
マウスのクリックを検知して位置を取得できれば,スクリーンショットを撮る範囲の左上と右下を指定できるはず!
pynputってのでクリックを検知できるらしい.まずはインストール!

pip install pynput

とりあえず,公式のサンプルコードのクリックに関する部分を動かしてみる.

from pynput import mouse

def on_click(x, y, button, pressed):
    print('{0} at {1}'.format(
        'Pressed' if pressed else 'Released',
        (x, y)))
    if not pressed:
        # Stop listener
        return False

# Collect events until released
with mouse.Listener(on_click=on_click) as listener:
    listener.join()

今度は入力監視のアクセスを要求されたので,ターミナルに許可のチェックを入れます.
スクリーンショット 2022-12-22 1.53.25.png

下記の実行結果から見てわかるように,クリックをすると位置が表示されて離すと終了するって感じですね.

Pressed at (1038.02734375, 455.2265625)
Released at (1038.02734375, 455.2265625)

コードをちょっと変えて範囲指定できるようにしました.

from pynput import mouse

left_up_x, left_up_y, right_down_x, right_down_y = [0,0,0,0]
num_press = 0

def main():
    with mouse.Listener(on_click=on_click) as listener:
        listener.join()
    # 範囲の確認
    print(f"left_up_x: {left_up_x}")
    print(f"left_up_y: {left_up_y}")
    print(f"right_down_x: {right_down_x}")
    print(f"right_down_y: {right_down_y}")
    
def on_click(x, y, button, pressed):
    global left_up_x, left_up_y, right_down_x, right_down_y, num_press
    if pressed:
        # 1回目のクリックはウィンドウの切り替え用で何もしない
        if num_press == 0:
            num_press = 1
        # 2回目のクリックはスクリーンショットを撮る範囲の左上を指定
        elif num_press == 1:
            num_press = 2
            left_up_x, left_up_y = x, y
        # 3回目のクリックはスクリーンショットを撮る範囲の右下を指定して終了
        else:
            right_down_x, right_down_y = x, y
            return False

if __name__ == "__main__":
    main()
left_up_x: 948.53125
left_up_y: 479.04296875
right_down_x: 1190.75
right_down_y: 637.22265625

範囲指定は問題なくできてそうですね.
これであとはスクリーンショット撮るだけや!
と思ったら次でしっかり沼りました.

指定した範囲のスクリーンショットを撮る

まずはpyautogui.screenshotのregionで指定してスクリーンショットを撮ってみる.

import pyautogui

def main():
    with mouse.Listener(on_click=on_click) as listener:
        listener.join()
    # 範囲の指定が終わったらスクリーンショットを撮る
#     print(f"left_up_x: {left_up_x}")
#     print(f"left_up_y: {left_up_y}")
#     print(f"right_down_x: {right_down_x}")
#     print(f"right_down_y: {right_down_y}")
    im = pyautogui.screenshot("screenshot.png", region=(left_up_x, left_up_y, right_down_x-left_up_x, right_down_y-left_up_y))    

これだとなぜか指定した範囲とは違う範囲のスクリーンショットが撮られました.
結論から言うとx座標,y座標を全て2倍するとうまくいったのですが,なぜそうなったのかは未だに分かっていません.

im = pyautogui.screenshot("screenshot.png", region=(left_up_x*2, left_up_y*2, (right_down_x-left_up_x)*2, (right_down_y-left_up_y)*2))

これでうまくいきました.
pyautoguiで取得している解像度と,実際の解像度に差があるのかも?と思って調べてみました.
まず,pyautoguiから

print(pyautogui.size())
Size(width=1470, height=956)

次はMacbook Air
スクリーンショット 2022-12-22 3.59.02.png
うーん,どちらも1470×956ですね.
スクリーンショット範囲の左と上の端をどちらも0にして,幅2940,高さ1912にするとちょうど全画面のスクリーンショットが撮れるのですが,,,なんでですかね?分かる方いたら是非教えてください.

終わりに

範囲指定してスクリーンショット撮るだけでも結構時間がかかってしまった.
次回は,対戦開始・終了タイミング取得の前準備として,スクリーンショットからエッジ検出,直線検出をしたいと思います!

9
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?