動画の一部分の切り抜きを大量に保存するソフト(スクショマシーン1号)

FUSION360の使い方とか作成風景の説明とかタイムラプス的なものを撮りたいな―と思いました。しかしながらいちいちスクショをしていては途中でやり直しが発生した際に、最終的な作品には関係のないスクショも保存されてしまう。すると完成したときには大量の本筋のスクショと失敗したスクショが混在するカオス状態になってしまうだろう。
これを解決するために考えたのが以下の手法になります。
1,とりあえずPCの画面を録画しておく
2,必要なコマのみ切り取る


至って普通の考えでした。

1に関してはWindowsにデフォルトでついているwindows+Gを押すことでできますが、その動画から切り取りをサクサク行うにはどうすればいいのか…
というわけでPythonでそんなことができるソフトを作ってみました。

やりたいこと


やりたいことをまとめると
1,動画をコマ送り(進む、戻る)し
2,動画を通して特定の範囲を
3,画像として保存する
ということです。下の通りです。

1,動画のコマ送りについて


まずは動画とはという所からなのですが、動画は上の図のように短い時間で画像をころころ変えることで動いているように見えるというものです。つまり1本の動画は大量の画像からなっているといってもいいでしょう。この画像の順番をフレーム番号と呼ぶことにすると動画を進めるよ言うことはフレーム番号を増やす、動画を巻き戻すということはフレーム番号を減らすということに置き換えることができます。
つまりフレーム番号を保存しておく変数を用意してあげ、何らかの方法でそのフレーム番号を増やしたり減らしたりすることで動画のコマ送りができるというわけです。

2,特定の範囲の指定


これは非常にシンプルです。画像はじっくり見ると多くの画素からなっています。例えば下の図のように数字と色を関係づけると

画像と行列を関係づけることができます。

模様を数値で表すことができる

画像はこのような2次元(カラーだと3次元)の数字の塊になっています。このため下図の赤枠の部分を切り取りたいと思ったら、図の9から19行目かつ13から24列目までを切り取れば済んでしまいます。

3,画像として保存する


これに関しては2よりもさらに簡単です。OpenCVには上記のような2次元、もしくは3次元の数列を画像として保存してくれる関数がデフォルトで入っています。使い方は「python opencv imwrite」とかで検索すれば無限に出てくると思います。

ソースは下の方に貼り付けます。

使い方


1,ファイルと同じディレクトリにmov.mp4と名前を変更した動画ファイルを保存します。

2,プログラムを実行すると動画の1フレームが再生されます。

3,切り抜きたい範囲の左上の部分をクリックしたのちに右下をクリックします。

4,マウスのスクロールで再生、逆再生ができますので、スクショを撮りたいフレームに合わせます。
5,右クリックするとimgフォルダ内に切り抜かれた画像が保存されます。


import cv2
import os
import shutil
import numpy as np


class calc:
    def __init__(self):
        self.x1=0
        self.x2=0
        self.y1=0
        self.y2=0
        self.speed=3
        self.num=0
        self.img=None
        self.fnum=1

        self.flg=0
        self.path = "mov.mp4"
        self.cap = cv2.VideoCapture(self.path)

        self.tot_frame=int(self.cap.get(cv2.CAP_PROP_FRAME_COUNT))

        #必要なフォルダを作成
        try:
            shutil.rmtree("./img")
        except:
            pass

        try:
            os.mkdir("./img")
        except:
            pass

    def onMouse(self,event, x, y, flags, params):
        if event == cv2.EVENT_LBUTTONDOWN:
            if self.flg==0:
                (self.x1,self.y1)=(x,y)
                print(x,y)
                self.flg=1
                print("切り取り範囲の右下側をクリック")
            elif self.flg==1:
                (self.x2,self.y2)=(x,y)
                self.flg=2
                print(x,y)
                print("スクロールでフレームを進めたり戻したりします \n右クリックで画像を保存します")

        if event == cv2.EVENT_RBUTTONDOWN and self.flg==2:
                img_output=self.img[self.y1 : self.y2 , self.x1 : self.x2]
                cv2.imwrite("./img/"+str(self.num).zfill(4)+".png",img_output)
                self.num=self.num+1

        if event == cv2.EVENT_MOUSEWHEEL:
                if flags<0:
                    if self.fnum+self.speed<self.tot_frame:
                        self.fnum+=self.speed
                    else:
                        print('これ以上再生できません')

                elif flags>0 :
                   if self.fnum-self.speed>0:
                        self.fnum-=self.speed
                   else:
                        print('これ以上巻き戻せません')

    def calc_main(self):

        print("切り取り範囲の左上の点クリックしてください")
        #切り抜き開始のフレームまで移動
        while True :
            self.cap.set(cv2.CAP_PROP_POS_FRAMES,self.fnum)
            #フレーム情報取得
            ret, self.img = self.cap.read()
    
            #動画が終われば処理終了
            if ret == False:
                break

            #動画表示
            cv2.imshow('Video', self.img)
            cv2.setMouseCallback('Video', self.onMouse)
            cv2.waitKey(1)

    
        self.cap.release()
        cv2.destroyAllWindows()


a=calc()

a.calc_main()

この記事が気に入ったらサポートをしてみませんか?