遠隔授業のスライドを自動でスクリーンショットする裏技

#はじめに

普段遠隔授業を受けている学生さん！！
授業のスライドを残してくれない先生いますよね？
後でスライドを見直したい場合，毎回手動でスクリーンショットを取らなくちゃいけなくて面倒です．

ということで，Pythonを使って画面の変化を検出しスライドを自動でスクショしてくれるプログラムを作っていきたいと思います．

#(new!)完成品をダウンロードしたい方
UI付きの完成品(exeファイル)を[github](https://github.com/link2004/AutoScreenshot)にアップロードしました．

#使用言語
* Python

#使用ライブラリ
* OpenCV (画像処理)
* PyAutoGUI (スクリーンショットの自動化)

標準ライブラリ：numpy，time

#仕組み
**①一定時間ごとに画面をスクリーンショット
②画面の変化量を調べ，一定の数値を越えたらその画像を保存**

#実装
##二つの画像の変化量を返す関数

```python:ImageChangeRate.py
import cv2
def ImageChangeRate(img1,img2,isShow=False):
    # 画像をグレースケールに変換
    img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
    img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

    # 2枚の画像の差分を求める
    mdframe = cv2.absdiff(img1,img2,0.5)
    # 白と黒の2色の画像にする
    thresh = cv2.threshold(mdframe, 10, 255, cv2.THRESH_BINARY)[1]

    #画像サイズ
    image_size = thresh.size
    #白のピクセル数／画像サイズ
    whitePixels = cv2.countNonZero(thresh)
    whiteAreaRatio = (whitePixels/image_size)

    # 変化量を1.0~0.0で出力
    return whiteAreaRatio
```
##スクリーンショットを取る関数
```python:ScreenShot.py
import numpy as np
import pyautogui as pag
def ScreenShot():
    #スクリーンショット
    img = pag.screenshot()
    #pyautoguiの画像をopencvの画像形式に変換
    img = np.array(img)
    img = img[:,:,::-1].copy()
    return img
```
画像形式の変換方法は下のページを参考にしました．

https://stackoverflow.com/questions/35958071/using-pyautogui-and-opencv-for-screenshot

##全体
```python:main.py
import numpy as np
import cv2
import pyautogui as pag
import time

def main():
    #最後に保存したフレーム
    old_img = ScreenShot()

    count = 0
    while True:
        #現在のフレーム
        img = ScreenShot()

        #変化量計算
        rate = ImageChangeRate(old_img,img)
        print(rate)

        #大きな変化があった場合
        if rate > 0.1:
            #画像を保存
            cv2.imwrite("imgs/"+str(count)+".jpg",img)
            count += 1

            #保存した画像をold_imgに代入
            old_img = img
            print("Screenshot!")

        time.sleep(0.5)


def ImageChangeRate(img1,img2):
    #省略…

def ScreenShot():
    #省略…

if __name__ == "__main__":
    main()
```

全体としてはこんな感じです．基準値は自分でカスタマイズしてください．
注意点は，前フレームと後フレームを比較するのではなく，最後に保存したフレームと現在のフレームを比較しているという点です．応用版にも対応するため，このような仕様にしています．

#応用
ここから先はこだわりたい人向けです．

**穴埋め形式のスライドや，アニメーションで変化するタイプのスライドにも対応します．**

このようなタイプは大きな変化と小さな変化が交互に起こるので少し複雑です．

基準値をうまく調節しても良いのですが，
低く設定すると，画像が増えすぎてしまう
高く設定すると，取り残しが生まれてしまう
という問題が発生します．

**穴埋め形式のように変化のあるスライドは，変化前と変化後だけが保存できるようにしたいです.**

画像で説明します．
![スクリーンショット 2021-10-19 175237.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192833/8c366eee-a222-314e-b8ca-989f0400ab81.jpeg)
**ポイントは，緑色の部分を確実に保存したいという点です．**

「大きな変化が起きたら前フレームと後フレーム両方を保存する」というプログラムにしても良いですが，静止画(変化のないスライド)があった場合同じ画像を二枚保存してしまいます．


そのために，tmp_imgという変数を追加します．

* old_img (最後に保存したフレーム)
* img (現在のフレーム)
* tmp_img (ひとつ前のフレーム)

変数名は適当です．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2192833/e7f7e69f-2101-4878-9ca6-d53638d6c676.png)

スライドが切り替わるタイミングで，変化のあるスライドだったかを判定し，そうであった場合は変化後のスライドも保存するようにします．

```python:main.py
import numpy as np
import cv2
import pyautogui as pag
import time

def main():
    #最後に保存したフレーム
    old_img = ScreenShot()

    count = 0

    #1つ前のフレーム
    tmp_img = ScreenShot()

    while True:
        #現在のフレーム
        img = ScreenShot()

        #変化量計算
        rate = ImageChangeRate(old_img,img)
        print(rate)

        #大きな変化があった場合
        if rate > 0.1:
            #静止画じゃない場合，ひとつ前のスライドも保存
            if ImageChangeRate(old_img,tmp_img) > 0.005:
                cv2.imwrite("imgs/"+str(count)+".jpg",tmp_img)
                count += 1

            #画像を保存
            cv2.imwrite("imgs/"+str(count)+".jpg",img)
            count += 1

            #保存した画像をold_imgに代入
            old_img = img
            print("Screenshot!")
        
        #現在のフレームを前フレームにする
        tmp_img = img

        time.sleep(0.5)


def ImageChangeRate(img1,img2,isShow=False):
    #省略…

def ScreenShot():
    #省略…

if __name__ == "__main__":
    main()
```

これで完成です．実際に動かして動作を確認してみてください．


#注意点と問題点
* 仕様上，授業のコメント欄を開いたり，画面を動かしたりするとその都度スクショしてしまうので気を付けてください．
* PyAutoGUIは仕様上メインモニターしかスクリーンショットができないっぽいです．対処法があれば教えてください．


#参考
https://ensekitt.hatenablog.com/entry/2018/06/11/200000

https://boook24.com/?p=358

https://techtech-sorae.com/pythonopencv%E3%81%A7%E4%BA%8C%E5%80%A4%E7%94%BB%E5%83%8F%E3%81%8B%E3%82%89%E7%99%BD%E3%81%A8%E9%BB%92%E3%81%AE%E9%9D%A2%E7%A9%8D%E6%AF%94%E3%82%92%E7%AE%97%E5%87%BA/

https://note.nkmk.me/python-opencv-imread-imwrite/

https://stackoverflow.com/questions/35958071/using-pyautogui-and-opencv-for-screenshot