バッチファイル作成で振り返る:超解像処理動画の自動化手順

以前のブログで【便利】コマンドラインツールで超解像処理!静止画も動画も高画質にという内容で書いていました。

uepon.hatenadiary.com

PowerShellやcmdの補完機能があるとはいえ、コマンドラインでの実行では打ち込みが多いので結構大変です。こんなときにはバッチファイル(.bat)などを使用することで入力の負担は軽減できるのですが…自分にはバッチファイルを作る知識があまりないので、このときの内容をバッチファイル化を通して作り方を学び直したいと思います。

元になる処理

今回は、以下のような処理を考えます。この処理では低画質mp4動画ファイルをフレーム分割、高画質化の処理を行い、それを結合しつつ、元の動画ファイルの音声を結合するといった処理になっています。

PS> mkdir img
PS> mkdir hires
PS> .\ffmpeg.exe -i .\640×480_サンプル動画.mp4 -q:v 1 .\img\%06d.png
PS> .\realesrgan-ncnn-vulkan.exe -i img -o hires -n realesrgan-x4plus
PS> .\ffmpeg.exe -i .\hires\%06d.png -i .\640×480_サンプル動画.mp4 .\output.mp4

このときは各処理の実行ファイルパス上で作業を行っていたのですが、できれば環境変数PATHに実行ファイルのパスを追加しておくことで、.\ffmpeg.exeというような表記をffmpeg.exeにする省力化もできると思います。今回はPATHに追加したという前提で行います。

大まかな処理は先程説明しましたが、もう少し処理を詳細にしてみます。

  1. 低画質動画ファイルをフレームごとに分割するためのフォルダを作成します。(フォルダ名は指定する)
  2. 高画質化したフレーム画像を保存するフォルダを作成します。(フォルダ名は指定する)
  3. ffmpeg.exeを使用して指定した動画ファイルからフレーム毎の静止画(.pngファイル)を先程のフォルダへ保存を行う(動画ファイルは指定する)
  4. realesrgan-ncnn-vulkan.exeを使用して、先程の静止画ファイルをそれぞれ高画質化する。
  5. ffmpeg.exeを使用して、高画質化したフレーム画像使用して動画を構成する。(出力ファイル名は指定する)

※realesrgan-ncnn-vulkan.exeは作業ファイルをフォルダ指定できるのでかなり簡単に書くことができます。

この処理からバッチファイルで引数として指定する必要があるのは以下の項目となります。

  • 入力する動画ファイル名(パス)
  • 入力動画のフレーム画像を格納するフォルダ名(パス)
  • 高画質処理を行ったフレーム画像を格納するフォルダ名(パス)
  • 出力する動画ファイル名(パス)

続いて必要になる機能を考えてみます。

  • バッチファイルに与えるコマンドライン引数をチェック・取得する。
  • コマンドライン引数の値を変数に格納する。
  • フォルダがない場合のみフォルダの作成する。
  • 各処理で変数値を参照して実行を行う。

こんなところでしょうか。

加えて基本的な部分となりますが以下も必要になってきます。

  • ディスプレイ出力時のechoを非表示にする
  • コメント記述する

実行は以下のような想定になるかなと思います。バッチファイルの名前はprocess_video.batとしています。

PS> process_video.bat "input.mp4" "./img" "./hires" "output.mp4"

注意点ですが、引数で与える値をダブルクォーテーション(")で囲んでいますが、これはPowerShellでもcmdでも動作するようにするためです。ダブルクォーテーションを使用することで、パスの区切り文字を"/"、"\"(¥)のどちらでも使用することができるようになります。また、空白文字を含むパスがあっても対応ができる点でこちらを採用しています。

基本的な記述

ディスプレイ出力時のechoを非表示にする

このあたりは基本ですね。よく出てくるので自分も知っています。ファイルに以下の様にいれれば それ以降でechoを使用して文字列表示を行う場合にコマンド実行としてのechoが画面表示されなくなります。

文字列表記時にechoを出さない

@echo off
echo テスト

実行

>sample.bat
テスト

コメント記述

バッチファイル内のコメント表示といえばremになるのですが、実はその他の記載方法もあります。それが::(コロン2つ)となります。自分もこのことは今回調べて初めてわかりました。remはパット見わかりにくいとは思っていましたが、::をしようすることでかなり可読性が良くなったように感じます。ただ、バッチファイルのラベルの記述がが`:(コロン1つ)なのでifやfor内で使用するとバグになる可能性がありそうです。このあたりは注意かもしれません。

REM これはコメントです
:: これもコメントです

メインの処理部分

基本部分はわかってきたので(このあたりはよく知られてますよね~)続いてはメインの処理部分をおさえていくことにします。

バッチファイルに与えるコマンドライン引数をチェック・取得する。

バッチファイルでのコマンドライン引数は以下の様に実行を行うと

ファイル名.bat 引数1 引数2 引数3 ……

この場合、バッチファイル内では%1%2%3、……の記述で参照できます。バッチファイル側で%に続く数値が何番目かを表します。ちなみに引数は単純に使用する場合には10個(%9)までの使用になります。(SHIFTを使用すれば使用できるみたいですが、現実的じゃないかも?)

引数の個数はこの何番目の引数の値が空か否かを判別することになるかなと思います。(C言語などにあるargvっぽいものはなさそうでした)

また、後述する変数を使用するのですが、その格納時にダブルクォート(")も同時に格納されると面倒なことになりそうなのでその解決策はないのかなと調べてみると…%~1%~2%~3、……の記述でダブルクオーテーションなしの値を参照することができるようです。以下の様にコードをつくって実行すると

@echo off
:: %1の参照テスト
echo %1

:: %~1の参照テスト
echo %~1

実行結果は以下のようになり、~があると引数に与えたダブルクオーテーションが削除されて参照がされます。厳密に言えばパスであれば混在していてもWindows側がよしなに処理を行ってくれるのですが、他の処理を行う際は気にしておいたほうがいいかもしれません。今、気がついたのですが、PowerShellでは%1も%~1の実行結果は同じになるようです。

cmdでの実行

>sample.bat "img"
"img"
img

PowerShellでの実行

PS>.\sample.bat "img"
img
img

では元の処理に戻って、今回は4つの引数があるので4つあるかを調べてみる部分を作成してみます。%~4の文字列が空か調べてます。もし空ならエラー出力をしてプログラムを終了させます。エラー出力では使い方を出力するようにします。

@echo off

:: 引数の確認
if "%~4"=="" (
    echo 使用法: %0 [入力動画パス] [imgフォルダパス] [hiresフォルダパス] [出力動画パス]
    goto :eof
)

実行結果

# 引数が4つある場合
>sample.bat arg1 arg2 arg3 arg4

# 引数が4つない場合
>sample.bat arg1 arg2 arg3
使用法: sample.bat [入力動画パス] [imgフォルダパス] [hiresフォルダパス] [出力動画パス]

コマンドライン引数の値を変数に格納する

続いてはコマンドライン引数の値を変数に入れておきます。今回はあまり必要ではないのですが、遅延環境変数展開を入れておこうと思います。変数をセットあとすぐにifやfor内で変数参照を行う際に予想通りの動きになります。このあたりは説明が難しいので以下をご参照ください。使用したら最後にendlocalを入れておくのも忘れずに。

qiita.com

今回のバッチファイルでは関係ないですけどね。念の為入れておいたほうがいいかなとは思います。 では、これも含めて変数に入れていきます。

変数の格納方法

set "inputVideo=%~1"

変数の参照方法

echo inputVideo=%inputVideo%

変数の参照時には変数を%で囲みます。

@echo off
setlocal enabledelayedexpansion

:: 引数の確認
if "%~4"=="" (
    echo 使用法: %0 [入力動画パス] [imgフォルダパス] [hiresフォルダパス] [出力動画パス]
    goto :eof
)

:: 引数を変数に格納
set "inputVideo=%~1"
set "imgFolder=%~2"
set "hiresFolder=%~3"
set "outputVideo=%~4"

:: 変数の参照
echo inputVideo=%inputVideo%
echo imgFolder=%imgFolder%
echo hiresFolder=%hiresFolder%
echo outputVideo=%outputVideo%

endlocal

実行結果

> sample.bat arg1 arg2 arg3 arg4
inputVideo=arg1
imgFolder=arg2
hiresFolder=arg3
outputVideo=arg4

フォルダがない場合のみフォルダの作成する

これは引数に格納したファイルが存在するか否かを確認して、ない場合にはmkdirをすることになるので以下のような処理を追加していきます。ここまで来るとそこまで難しくはないですね。if not exist フォルダ名でフォルダがないことを確認できています。

:: フォルダの準備
if not exist "%imgFolder%" mkdir "%imgFolder%"
if not exist "%hiresFolder%" mkdir "%hiresFolder%"

各処理で変数値を参照して実行を行う

あとは各プログラムの引数に変数を参照させて行きます。ファイル名も変えておきます。 ffmpegの引数に%06d.pngと入れている部分は、バッチファイルでは%エスケープ文字が必要となるため%%とする必要があります。

RealESRGANとffmpegを使用して超解像化した動画を作成するバッチファイル

これで作成しようと思っていたバッチファイルが完成しました!

cmdでの実行例

> process_video.bat "input.mp4" "./img" "./hires" "output.mp4"

PowerShellでの実行例

PS> ./process_video.bat "input.mp4" "./img" "./hires" "output.mp4"

おわりに

これまではコマンドラインからの実行で済ませていましたが、改めてバッチファイルの作り方や、昔の知識がアップデートできたような気がします。特にコメントが::で記述できたり、引数のダブルクオーテーションの削除方法が%~1でできるというのは今後もちょっと使えそうだなとも思いました。

普段はshbashなどを使用することも多いのですが、AI関連の作業となるとWindowsを使用することも多いのでバッチファイルの書き方とても勉強になりました。

/* -----codeの行番号----- */