Pythonでタイムラプス その1(連番JPEGから)

python

 Python初心者のための目的志向のプログラミング。Pythonのうねりがやってきた。いや数年前からやってきていたのを目をつぶっていた。最近の若い子はPythonで会話する。クラシックなCしかしゃべれない身としては、まるで外人との会話。

 独学を試みるも、何か目的がないとやる気が起こらない。Hello World ! とか言われても、モチベーションが上がらない。というわけで、Python初心者が以前紹介した、キャンプ場でのキャンプ場でのタイムラプスの効用で撮影したデジカメで撮影したJPEG画像群をタイムラプス動画に仕立てるまでのメモを残しておこうと思います。(結果から言うと、50行のコードでシンプルな処理は記述できてしまった。)

やりたいこと

 上の図のイメージ。

 適当に設置したカメラでインターバル撮影した複数のJPEGから、タイムラプス動画を作る。適当に設置したので、画角はずれており、残したい画像が必ずしも中心にないため、画像のトリミングを行う。また、三脚が傾いていたため、その補正(回転)も行う。(キャンプ場で設置時フレーミングしないです。ズームも未調整で設置をした前提の画像)

環境づくり

 自分が選んだ環境はAnacondaからのspyder。理由はうちの若いのが使ってたから…。これはあまり丁寧には説明しません。ほかのページに任せようと思います。Anacondaのインストールには、特段はまらなかったので。

OpenCVの追加

 画像処理の定番ライブラリ。これも詳細は割愛します。追加の方法もGUIを使って、簡単にできてしまいました。OpenCV自体はCで使ったことがあったので、抵抗はなかったです。環境構築で特に躓かなかったです。

方針

1.特定のフォルダにあるJPEG画像を順に同じ処理を繰り返す。
2.画像の特定の場所から画像を切り出し、動画のフレームサイズに倍率変更
3.特定のフォルダに動画として保存

2の箇所を関数にしました。

コード

 肝心のコード。これをコピペして定義の場所を書き換えるだけで動くと思います。拍子抜けするぐらい簡単でした。

import os
import cv2

ROOT_PATH = 'E:\\tmp\\photo4'       #ソース画像が置いてあるパス
SAVE_FILE = 'E:\\tmp\\photo4.mp4'   #出力動画
WIDTH = 1920
HEIGHT = 1080
FPS = 10.0

###############################
# imgをrotateだけ回転しshiftだけずらす
def convert(img, shift,angle,scale):
    # img    ソース画像
    # shift  シフト量(shift[0] x方向,shift[1] y方向)
    # rotate 回転角度(度)
    
    height, width = img.shape[:2]
    center = (int(width / 2), int(height / 2))
    trans = cv2.getRotationMatrix2D(center, angle, scale)
    trans[0, 2] -= int(shift[0])
    trans[1, 2] -= int(shift[1])

    return cv2.warpAffine(img, trans, (WIDTH, HEIGHT), cv2.INTER_CUBIC)

###############################
def main():

    #ファイル取得
    files = os.listdir(ROOT_PATH)

    shift = [100,100]
    angle = 45
    scale = 0.5

    # 画像入力
    fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
    video = cv2.VideoWriter(SAVE_FILE, fourcc, FPS, (WIDTH, HEIGHT))

    for file in files:
        img = cv2.imread(ROOT_PATH + "\\" + file)
        print(ROOT_PATH + "\\" + file)
        frame = convert(img,shift,angle,scale)
        video.write(frame)

    video.release()

if __name__ == '__main__':
    main()

解説

 50行のコードに解説は不要かもしれませんが…。

 先頭で定数を記述しています。気持ち的には#defineなり、constなりを付けたいところですが、この先の変更がないことは(自分で)保証することにします。

ROOT_PATH = 'E:\tmp\photo4'       #ソース画像が置いてあるパス
SAVE_FILE = 'E:\tmp\photo4.mp4'   #出力動画
WIDTH = 1920
HEIGHT = 1080
FPS = 10.0

 先頭から画像が置いてあるパス、出力動画ファイル名(コメント通り)WIDTH、HEIGHTは出力の動画のサイズFPSは動画のフレームレートです。

 肝心の処理です。もののサイトにはmainは不要とありましたが、個人的にはmain関数がないと不安なのでmainは作っちゃいました。意図はないです。main()から処理がスタートします。(細かい関数の仕様は別途Webサイトを確認してください。)ぼーっと眺めるだけでも何してるか直感で分かりそうですが。

まず

#ファイル取得
files = os.listdir(ROOT_PATH)

これで、files(これはリスト)に特定のフォルダ以下のファイル名がずらっと
入っていきます。これをループで回せばよいわけです。次の

shift = [100,100]
angle = 45
scale = 0.5

 が切り出し開始の座標(x,y)(shift)、回転角度(angle)、倍率(scale)を設定している箇所。全フレーム固定です。このパラメータを決定するには、別途画像ビューアーが必要そうです。これ専用のビューアーも今後Pythonで記述を試みます。また将来的にはこれをフレーム毎に少しずつ動的に制御してパンとかズームとかさせたいです。(なのでコレらは定数ではなく、変数の位置に置いときます。)

# 画像入力
fourcc = cv2.VideoWriter_fourcc('m','p','4','v')
video = cv2.VideoWriter(SAVE_FILE, fourcc, FPS, (WIDTH, HEIGHT))

これは半分おまじない。mp4で保存するためのvideoオブジェクトを作ってるところ。ビットレートはいじれない模様。

for file in files:

これがfor文。fileの中にファイル名の文字列が入ってきます。

    img = cv2.imread(ROOT_PATH + "\\" + file)
    print(ROOT_PATH + "\\" + file)
    frame = convert(img,shift,angle,scale)
    video.write(frame)

これ(imread)で1枚ずつ画像を読み込んで、convert関数でトリミング。返ってきた画像をビデオフレームとして保存しています。流れはこれで終了。このconvert関数ですが、OpenCVの力を使って、指定のパラメータでAffine変換しているだけです。
getRotationMatrix2D

warpAffine
を調べるだけで思った記述はできると思います。画像の中心から回転させる行列をgetRotationMatrix2Dで求めて、その後シフトさせているだけです。warpAffineの第四引数に補完方法を指定します。なければ線形変換です。今回はせっかくなのでCubicにしました。

まとめ

 ものの50行でシンプルなタイムラプス動画を作ることができてしまいました。Cしかしゃべれない人間からすると、あまりにシンプルすぎて不安になります。

なんというか、Cで
「私はあなたのその青い服が素敵だと思います」
くらい書かないといけないところ
「その服いいね」
くらいで済んでいる感じ。

「家を出て、最初の角を右に曲がって、50m先の角を左に曲がって、左手の黄色いMの看板の店」

「近所のマック」
くらい。
マックをモスに変更する際の記述量も少なく、バグが入り込む余地が少ない。

 やりたいことはあらかた満たせています。ただ少し味気ない動画なので、次にフェードイン、フェードアウトのエフェクトくらい入れようと思います。

python画像処理
スポンサーリンク
キャンプ工学

コメント

タイトルとURLをコピーしました