ルモーリン

PowerShellで画像を扱う

投稿:2023-04-01

PowerShellでテキストからボイス作って一枚絵に加えて動画を作る処理を書きました。 ところが、一枚絵のファイル形式やサイズにより上手く処理できないケースが出てきました。 そこでできる限りオールマイティに処理できるよう、色々と工夫してみます。

  • JPEGかPNGを選択可
  • PNGをJPEGに変換
  • VoicePeakをコマンドラインで使用
  • JPEG変換でピクセルフォーマットをyuvj420p
  • 画像処理にライブラリ(?)のSystem.Drawing
  • 画像の解像度がフルHDより大きい場合は縦横比を変えずに収まるよう縮小
  • 解像度の縦または横が奇数ピクセル数の場合は強制的に1減らして偶数に(動画エンコードの都合)
  • モノラル音声を読み込む際の警告を抑止
# 【注意】powershellはBOM必要
# 静止画にボイスをつけてmp4を生成

add-type -AssemblyName System.Drawing

if (1 -ne $args.Length) {
	Write-Output "画像ファイルを1個だけ選択してください。"
	Pause
	Exit
}
	
$extension = (Get-Item -Path $args[0]).Extension
if (".jpg" -ne $extension -and ".png" -ne $extension) {
	Write-Output "JPEGかPNGを選択してください。"
	Pause
	Exit
}

$graph_path = $args[0]
Write-Output "画像ファイル $graph_path"
$wav_path = [System.IO.Path]::ChangeExtension($graph_path, ".wav")
Write-Output "WAVEファイル $wav_path"
$mp4_path = [System.IO.Path]::ChangeExtension($graph_path, ".mp4")
Write-Output "MP4ファイル $mp4_path"

if (".jpg" -eq $extension) {
	$jpg_path = $graph_path
} elseif (".png" -eq $extension) {
	$jpg_path = [System.IO.Path]::ChangeExtension($graph_path, ".jpg")
	Write-Output "PNGからJPEGファイルに変換"
	Write-Output "JPEGファイル $jpg_path"
	$ArgList =
		"-loglevel", "warning",
		"-y",
		"-i", $graph_path,
		"-update", 1,
		"-pix_fmt", "yuvj420p",
		$jpg_path
	Start-Process -Wait -NoNewWindow -FilePath "ffmpeg" -ArgumentList $ArgList
}

if (!(Test-Path -Path $jpg_path)) {
	Write-Output "JPEGファイルがありません。"
	Pause
	Exit
}

$text = Read-Host "ボイスを入力してください"
if (0 -lt $text.Length) {
	Write-Output "音声を生成"
	&"voicepeak" --say "$text" --out $wav_path --narrator "Japanese Male 3" --emotion happy=100,fun=100,angry=0,sad=0 | Out-Null
}

if (Test-Path -Path $wav_path) {
	Write-Output "動画を生成"
	$image = [System.Drawing.Image]::FromFile($jpg_path)
	$width = $image.Width
	$height = $image.Height
	Write-Output "縮小前サイズ $width x $height"
	if (1920 -lt $width) {
		$height *= (1920 / $width)
		$width = 1920
	}
	if (1080 -lt $height) {
		$width *= (1080 / $height)
		$height = 1080
	}
	$width = [Math]::Floor($width)
	if (1 -eq $width % 2) {
		$width--
	}
	$height = [Math]::Floor($height)
	if (1 -eq $height % 2) {
		$height--
	}
	Write-Output "縮小後サイズ $width x $height"

	$ArgList =
		"-loglevel", "warning",
		"-y",
		"-i", $jpg_path,
		"-guess_layout_max", "0",
		"-i", $wav_path,
		"-filter_complex", "[0:v]scale=width=${width}:height=${height}[video];[1:a]channelmap=0|0[audio]",
		"-map", "[video]",
		"-map", "[audio]",
		$mp4_path
	Start-Process -Wait -NoNewWindow -FilePath "ffmpeg" -ArgumentList $ArgList

	Write-Output "音声を削除"
	Remove-Item -Path $wav_path
}

if (Test-Path -Path $mp4_path) {
	Write-Output "動画を再生"
	Start-Process -FilePath $mp4_path
}

Pause