ウィンドウ指定で画面キャプチャを連続で取り続ける

# はじめに

## 画面をキャプチャしたいモチベーション
生成AIが猛威を奮っている中、口だけでなく手を出してほしいなぁと思った次第でございます。
そのためには、今の画面の内容を理解してもらわないといけない。

Macのデスクトップ版では画面を監視して口出しする機能がリリースされたようですがWindowsはまだちょっと先だし、できたとしても、なんでもアドバイスできる？いくらChatGPTって言っても、学んでないことだって世の中にはたくさんあるでしょう。

## 言ってしまえばマイクラです

抽象的なことを書いても伝わらないと思いますので、具体をカミングアウトすると、**ChatGPTにマイクラをやってほしい**んですよね！


今は、マイクラの画面をChatGPTに共有して「あのクモスポナーから糸生成器を作りたい」って言っても、具体的にどこをどう掘るとかは教えてくれない。ましてや実行はしてくれない。（結構、事細かに教えてはくれる）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/cc2478c4-1c0b-1725-5344-01b5c56ee6ac.png)

マイクラは、自分で課題を解決して仕組みを作るから面白いんだろうというご意見、ごもっともでございます。でもこちとらもう１つメタに行き、その仕組みを作る仕組みを作りたくなってしまったのでございます。エンジニアの性なのかなんなのか。

そのためには、学習データを作らないといけない。「トウヒの木があるよ」とか「クモスポナーとその周りに柵があるよ」とか「クリーパーが見てるよ」とか、さすがのChatGPTの画像解析機能のオブジェクト認識でも知らないでしょう。なので、その学習データ。

実際、画像認識まで作るかどうかはわからないけど、マイクラの画面キャプチャまでならできるのかな、できそうだよね、という技術メモです。夢は大きすぎ、現実は小さすぎる感がありますね🤣

## キャプチャの他の流用性
キャプチャする機能は、マイクラに限らず、使い道はたくさんあると思います。検索してここに辿り着くような人は、何かをやりたくて見たんでしょう。是非教えてほしいです。

ゲームで言うとLOLを自動でやらせたいのの足掛かりとか、リモート会議を動画だと重すぎるから画像で撮っておきたいとか、動画を・・とか。

法的、倫理的によくないこともあるんでしょう。ですが画面キャプチャの機能は、画面録画ができる機能をWindowsが搭載しているので、ここでは細かく言いません。私は責任を負いません。**法的、倫理的にOKな常識の範囲内で、ご利用ください**。

## 環境
Pythonと主なモジュール
- Python 3.12.3
    - PyGetWindow==0.0.9
    - mss==9.0.1


## ソース
https://github.com/yo16/take_screenshot_continuous

# 説明
手順は大きく２つです。
1. ウィンドウの位置を、ウィンドウ名から取得する
2. その位置をキャプチャして保存する

それ以外は、n秒待って１と２を呼ぶというだけなので、割愛します。ソース(`take_screenshots_continuous.py`あたり）をご覧ください。

## １．ウィンドウ位置を取得
```Python:take_screenshot.py
import pygetwindow as gw

from take_screenshot_by_mss import take_screenshot_by_mss


def take_screenshot(
    window_title: str,
    save_to_dir: str
):
    # ウィンドウを取得
    windows = gw.getWindowsWithTitle(window_title)  # ★大事★
    if not windows:
        print("No matching window found.")
        return 
    
    window = windows[0]
    left, top, width, height = window.left, window.top, window.width, window.height
    # 撮る
    take_screenshot_by_mss(
        left, top, width, height,
        save_to_dir
    )
```
短いので全文載せてしまいますが、★大事★のところだけですね。タイトル名からウィンドウの位置とサイズを取ります。

その後、キャプチャを取るという関数を呼びます。


## ２．画面キャプチャを１枚撮る
```Python:take_screenshot_by_mss.py
import mss
import mss.tools
import time
from pathlib import Path

def take_screenshot_by_mss(
    x: int,
    y: int,
    width: int,
    height: int,
    save_to: str
):
    print(f"x:{x}, y:{y}, w:{width}, h:{height}")
    
    # フォルダを作る
    dir_path = Path(save_to)
    dir_path.mkdir(parents=True, exist_ok=True)

    with mss.mss() as sct:
        # キャプチャ領域の定義
        monitor = {
            "top": y,
            "left": x,
            "width": width,
            "height": height
        }
        screenshot = sct.grab(monitor)   # ★大事１★
        
        # 現在の日時を取得し、ファイル名を生成
        current_time = time.localtime()
        time_str: str = time.strftime(
            "%Y%m%d_%H%M%S",
            current_time
        )
        file_path: Path = dir_path / f"{time_str}.png"
        
        # 画像を保存
        mss.tools.to_png(   # ★大事２★
            screenshot.rgb,
            screenshot.size,
            output=str(file_path)
        )
        print(f"Screenshot taken and saved as '{file_path}'")
```
またしても短いので全文載せてしまいますが、「★」をつけた２か所以外は説明不要でしょう。前後の処理確認程度にご覧ください。

★大事１★で、画面をキャプチャする関数を使ってキャプチャします。
★大事２★で、その結果を保存します。

書き方だけ知ればすぐですね。知っているかいないかだけの話でした。

１つ目の関数から２つ目の関数を呼ぶようにすると、強制的に連携されることになり、ウィンドウを移動させても安心が担保されています。バラしてもよかったです。

## 結果
下記はエクスプローラーに表示されたサムネイルですが、こんな感じでモリモリできあがります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/2e5333a6-703d-332d-86d4-255b081c3b3d.png)
- 真っ黒のpngはPyAutoGuiでの失敗作（後述）
- キャプチャしたいウィンドウの上に別のウィンドウが乗ると、しっかり別ウィンドウも写る（モニターでの見た目通り）
- ウィンドウが画面の外に出ると、外に出ている部分は黒くなる
- 最小化すると、横長の真っ黒画面が写る（ネザーか）
- 数ピクセル、ウィンドウの外も写る
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722610/27cdf48b-2c1e-10d6-c7a7-327f787f010b.png)
    - ケースバイケースかもしれないけど、私の場合は、キャプチャ領域が左右と下に7pxずつはみ出てました。ウィンドウの影の分かも。つまり7pxずつウィンドウの外も写っている。１ミリも自分の情報を出したくないんだという人は、`windows = gw.getWindowsWithTitle(window_title)`の結果から、調整するとよいです。


## 別の方法？PyAutoGui
mssというモジュールの代わりに、PyAutoGuiというモジュールでもやってみました。こちらは真っ黒な画面がキャプチャされてしまい、うまくいきませんでした。管理者権限がある環境でもダメっぽかった。

もしかしたらマイクラじゃなかったらうまくいくのかなぁ。
githubにはソースをアップしてあります。

# おわりに、いろいろ
実は、マイクラ以外にも、**YouTubeを一時停止してキャプチャ**して、インデックスを作りたいなーと思ってました。YouTubeのチャプターみたいなのを、自分用に作りたい。自分用メモを残して置いたりね。なので今回の機能は、そこで使うかもしれません。

画像の保存先は、ローカルに保存すると故障＆消失のリスクがあるから、**Google Driveへ保存**したくなりました。とするとGoogle Colabで実行すると相性いいなー環境作らなくていいし、という発想になるんですが、ブラウザで動作するGoogle Colabはローカルのウィンドウにアクセスできないからキャプチャできないという話に。
なのでやるとしたら、地道にPythonでGoogle Driveへファイル保存するという方法になりそうです。１度やれば大したことはなさそうですが。
YouTubeならいけるか・・？🤔

キャプチャタイミングを短くして、**前と同じ絵だったら捨てる**というアイデアは、有効なときがありそうです。
マイクラや会議録画の場合はぬるぬる動くからダメそうですが。

それにしても、マイクラをChatGPT君が自動で動かしてくれるとして、それを見て、面白いのかなぁ。果たして未来のゲームはそうなるんだろうか。我ながらどうかと思う。