【Python】VidStabで手ぶれ補正【動画編集への道#2】

Pythonで動画の手ぶれ補正をするライブラリ「VidStab」を試した記録です。OpenCVベースの補正を、短いコードで動かせるのが便利なところです。 この記事では、VidStabの基本的な使い方、補正後に出る黒いボーダーを reflect で目立たなくする方法、逆に補正が向かない動画の例をまとめます。Pythonやシェルを使って動画編集を自動化していく企画の第二弾です。 VidStab(Python Video Stabilization) VidStabモジュールは、コアにOpenCVを使用した動画の手振れを修正できるプログラムになります。OpenCVへの理解がなくても、VidStabを利用すれば手っ取り早く手ぶれ補正ができますのでご安心ください。 vidstab と OpenCV のインストール Python3.xで動作させていきます。vidstabをpipでインストールします。 $ pip install vidstab vidstabを動かすにはOpenCVも必要になりますので合わせてインストールしてください。 $ pip install opencv-python $ pip install opencv-contrib-python VidStab で手ぶれ補正する それではさっそく、VidStab で手ぶれ補正してみましょう。自転車に乗りながらスマホをチェストマウントして撮影した動画になります。 元動画(左)ではブレブレだった映像も、手ぶれ補正後はだいぶマシになったように感じます。 プログラムは次のとおりです。たったの数行で手ぶれ補正ができちゃいます。 from vidstab import VidStab stabilizer = VidStab(kp_method='ORB') stabilizer.stabilize(input_path='../build/in.mp4', output_path='../build/out_orb.avi') VidStabを使えば、驚くおほど簡単に手ぶれ補正した動画が作れちゃいますね! ただし注意点としまして、avi 形式で保存しないと機能しませんでした。 黒いボーダーを reflect(反射)で埋める 手ぶれ補正すると、端の部分に黒い隙間ができてしまいます。黒い部分を含めないように MoviePyなどでCrop処理しても良いのですが、VidStabのreflect(反射)機能を使うことで目立たなくさせることもできます。 from moviepy.editor import * from moviepy.video.tools.segmenting import findObjects import moviepy.video.fx.all as vfx import os def crop(clip, region, margin = 50): w,h = clip.size # clip = vfx.crop(clip, x1=(w/4), width=(w/2)) clip = clip.set_mask(region.mask).set_pos(region.screenpos) clip = vfx.crop(clip, x1=(margin), width=(w - margin * 2), y1=(margin), height=(h - margin * 2) ) return clip.resize(region.size) if __name__ == "__main__": # im = ImageClip("splitter_720x1280.png") im = ImageClip("../assets/splitter_800x900.png") # Loacate the regions, return a list of ImageClips regions = findObjects(im) # region ( リージョン): 範囲、領域、分野、(身体の)部位の意味 print(len(regions)) # ←これが0なら、マスク画像の作成で何か間違ってる。(黒枠でちゃんと囲えてないとか、透過だとダメで白黒でやる) clip1 = crop(VideoFileClip("../build/out_orb.avi", audio=False), regions[0]) clip2 = crop(VideoFileClip("../build/out_orb_reflect.avi", audio=False), regions[1]) final_clip = CompositeVideoClip([clip1, clip2], im.size).subclip(0.5, clip2.duration - 0.5) out_path = "../build/pip_orb_reflect.mp4" final_clip.write_videofile(out_path) os.system('open ' + out_path) 他にもkp_method には replicate(複製)が指定できますので各自で試してみてください。 ...

公開: 2022年7月25日 · 更新: 2026年5月2日 · Toshihiko Arai

【Python】MoviePyで動画編集の自動化【動画編集への道#1】

この記事は、FFmpegコマンドを直接書くのに疲れた人が、PythonでサクッとカットやテロップやBGMミックスまで自動化するための入門メモです。 MoviePyはコアにFFmpegを使いつつ、Pythonらしいオブジェクト指向で VideoFileClip(...).subclip(...).write_videofile(...) のようにつなげて書けるので、動画編集ソフトを開かずに同じ加工を繰り返したいときに向いています。 まず手を動かすなら、後述の 指定時間でカット からどうぞ。1分尺の動画から数秒だけ切り出して書き出すだけなら、10行ほどで済みます。 準備 Pythonやシェルを使った動画編集の第一弾としまして、MoviePyでの動画編集をご紹介していきます。MoviePyの全体像や最新情報は 公式ドキュメント にまとまっていますので、ご覧ください。 次のような動画フォーマットを元に、MoviePyで動画を編集していきますね。 項目 値 サイズ 1280 × 720 エンコード H.264、 AAC MoviePyのインストール pipでMoviePyをインストールしましょう。Pythonは3.x系を使っていきます。 本記事のサンプルは from moviepy.editor import * や subclip を前提とした 1.x 系で書いています。2.x 系ではインポートパスや一部APIが変わっておりサンプルがそのままでは動かないため、バージョンを固定してインストールしてください。 $ pip install "moviepy<2" なお、MoviePyのコアでは ffmpeg のバイナリを呼び出します。OS側に ffmpeg が入っていない場合は事前にインストールしてください(macOSなら brew install ffmpeg、Ubuntuなら sudo apt install ffmpeg)。 それではMoviePyの使い方、プログラミング例をご紹介していきます。 指定時間でカット from moviepy.editor import * start = "00:00:03" # 開始時刻 end = "00:00:06" # 終了時刻 final_clip = VideoFileClip("in.mp4").subclip(start, end) final_clip.write_videofile( "split.mp4", codec='libx264', audio_codec='aac', temp_audiofile='temp-audio.m4a', remove_temp=True ) 音が出ない時の対処 write_videofile に何も指定しないと、環境によっては音が落ちて書き出されることがあります。MP4 で配るなら audio_codec='aac' と一時ファイル名 temp-audio.m4a を明示しておくと安定します。書き出し後はQuickTime / VLCなどで実際に音が出るかも軽く確認しておくのがおすすめです。 ...

公開: 2022年7月22日 · 更新: 2026年6月1日 · Toshihiko Arai

伝達関数とインパルス応答(Python)

はじめに インパルス応答は身近なところで日常的に使っている。電車のレールや機械をハンマーで叩いて音の反響を聞き取る打音検査、これはインパルス応答で異常がないか調べているわけだ。そしてこのインパルス応答をラプラス変換したものが伝達関数である。この記事ではそんな伝達関数とインパルス応答について詳しく解説していく。 伝達関数とは 伝達関数とは、複素数領域sにおいてシステムに(X(s))を入力したときの出力(Y(s))との比である。つまり、伝達関数を(G(s))とすれば次の式で表される。 $$G(s)=\frac{Y(s)}{X(s)}$$ 具体的にRCローパスフィルタ回路の伝達関数をラプラス変換を使って導き出してみよう。 RCローパスフィルタの伝達関数 時間領域tにおいては、回路図より次の式が成り立つ。 $$v_{in}(t)=Ri(i)+v_{out}(t),~~~i(t)=C\frac{dv_{out}(t)}{dt}$$ よって $$v_{in}(t)=RC\frac{dv_{out}(t)}{dt}+V{out(t)}$$ である。これをラプラス変換して複素数領域sへ書き換えると $$V_{in}(s)=RC(sV_{out} - v_{out}(0)) + V_{out}(s)$$ (t=0) のとき (v_{out}=0) とすれば、 $$V_{in}(s)=RCsV_{out} + V_{out}(s)$$ よって、RCローパスフィルタの伝達関数 (G(s)) は $$G(s)=\frac{V_{out}(s)}{V_{in}(s)}=\frac{1}{1+RCs}$$ である。 ここで(V_{in}=1)となる入力を与えたときの過渡応答が伝達関数(G(s))である。複素数領域sで1となる関数はインパルス関数 (δ(t)) である。つまりインパルス応答をラプラス変換したものが伝達関数である。 $$G(s)=\mathcal{L}[δ(t)]$$ インパルス関数とは インパルス関数とは、時間幅が無限小で高さが無限大のパルス信号である。またすべての時間領域において、積分値が1となる。数学ではディラックのデルタ関数、制御工学ではインパルス関数と呼ばれる。 $$δ(t)=\begin{cases}\infty~(t=0)\0~~~(t\neq0)\end{cases}$$ 実は、インパルス関数はさまざまな周波数の余弦波を足し合わせることによって作り出すことができる。つまり、すべての周波数成分を含んだ信号と考えることができるのだ。 Pythonでインパルス関数を作ってみよう! 図の左側が、1Hzkから11Hzまでの余弦波を1Hz間隔で足し合わせたものである。右側が1Hzkから10001Hzまでの余弦波を1Hz間隔で足し合わせたもである。 import matplotlib.pyplot as plt import numpy as np fig, ax = plt.subplots() ax.grid(ls=':') if __name__ == '__main__': F1 = np.arange(1, 11, 1) F2 = np.arange(1, 10001, 1) T = np.arange(-1, 1, 0.001) Y1 = [0]*len(T) Y2 = [0]*len(T) for f in F1: for i in range(len(T)): Y1[i] += np.cos(2 * np.pi * f * T[i]) for f in F2: for i in range(len(T)): Y2[i] += np.cos(2 * np.pi * f * T[i]) plt.plot(T, Y1, label='{} times'.format(len(F1))) #plt.plot(T, Y2, label='{} times'.format(len(F2))) plt.legend() plt.show() ...

公開: 2021年1月21日 · 更新: 2026年3月25日 · Toshihiko Arai