複数の画像ファイルを縦または横方向に連結する PowerShell モジュール

  • 2
    いいね
  • 2
    コメント

ImageMagick とかをインストールするのが面倒だったので書きました。

開発/動作環境

  • Windows 10
  • PowerShell 5.1.14393.576

導入の仕方

$env:USERPROFILE\Documents\WindowsPowerShell\ModulesCombineImage フォルダを作り、その中にモジュールファイル(CombineImage.psm1)を入れます。

使い方

構文

Save-CombinedImage [-Src] <String[]> [[-Dst] <String>] [-Horizontal]

使用例をいくつか

# 2つの画像を縦に連結して `3.png` に保存
Save-CombinedImage -Src 1.png,2.png -Dst 3.png
# -Src は省略可
Save-CombinedImage 1.png,2.png -Dst 3.png
# -Dst を省略すると out.png に保存
Save-CombinedImage 1.png,2.png
# -Horizontal で横方向に連結
Save-CombinedImage a.png,b.png,c.png -Horizontal
# 連結するファイルをパイプで渡せる
Get-ChildItem image*.png | Save-CombinedImage -Dst combined.png
# 画像形式を指定できる(拡張子から判断)
Get-ChildItem image*.png | Save-CombinedImage -Dst new.bmp  # BMPで保存

ソースコード

Gist に置いています。
:page_facing_up: CombineImage.psm1

CombineImage.psm1
<#
.SYNOPSIS
画像ファイルを連結します。

.DESCRIPTION
複数の画像ファイルを縦または横方向に連結し、保存します。
保存される画像形式は、保存ファイル名の拡張子から判断されます。

.PARAMETER Src
連結するファイル名をコンマ区切りまたはパイプラインから指定します。

.PARAMETER Dst
出力するファイル名を指定します。このパラメータを省略した際の出力先は "out.png" になります。

.PARAMETER Horizontal
画像を横方向に連結します。

.EXAMPLE
    > Save-CombinedImage -Src image1.png,image2.png -Dst new.png

.EXAMPLE
    > Get-ChildItem -Path image*.png | Save-CombinedImage -Dst new.png -Horizontal
#>

function Save-CombinedImage {
    param(
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
        [string[]]
        $Src,

        [string]
        $Dst = "out.png",

        [switch]
        $Horizontal
    )

    begin
    {
        $src_ = New-Object System.Collections.Generic.List[string]
    }

    process
    {
        $Src | ForEach-Object { $src_.Add($_) }
    }

    end
    {
        Add-Type -AssemblyName System.Drawing

        # カレントディレクトリを $PWD に設定
        $pcd = [System.IO.Directory]::GetCurrentDirectory()
        [System.IO.Directory]::SetCurrentDirectory($PWD)

        # 指定された画像から Bitmap を作成
        $bmp = $src_ | ForEach-Object { New-Object System.Drawing.Bitmap($_) }

        # 連結後のサイズ
        $measureW = $bmp | ForEach-Object { $_.Width } | Measure-Object -Sum -Maximum
        $measureH = $bmp | ForEach-Object { $_.Height } | Measure-Object -Sum -Maximum
        [int] $w = $measureW.Maximum
        [int] $h = $measureH.Sum
        if ($Horizontal)
        {
            $w = $measureW.Sum
            $h = $measureH.Maximum
        }

        # 連結画像を作成
        $bmpCanvas = New-Object System.Drawing.Bitmap($w, $h)
        $gfx = [System.Drawing.Graphics]::FromImage($bmpCanvas)
        $gfx.FillRectangle([System.Drawing.Brushes]::Transparent, 0, 0, $w, $h)
        $bmp | ForEach-Object { $x=0; $y=0 } {
            $gfx.DrawImage($_, $x, $y);
            if ($Horizontal) { $x += $_.Width } else { $y += $_.Height }
        }

        # 画像形式
        $format = switch -Regex ([System.IO.Path]::GetExtension($Dst))
        {
            "\.bmp"   { "Bmp" }
            "\.jpe?g" { "Jpeg" }
            "\.gif"   { "Gif" }
            "\.png"   { "Png" }
            default   { "Png" }
        }

        # 保存
        $bmpCanvas.Save($Dst, $format)

        # 破棄
        $gfx.Dispose()
        $bmpCanvas.Dispose()
        $bmp | ForEach-Object { $_.Dispose() }

        [System.IO.Directory]::SetCurrentDirectory($pcd)
    }
}

Export-ModuleMember -Function Save-CombinedImage

コードを書いていて気になった点

  • Set-Location で カレントディレクトリを変えても [IO.Directory]::GetCurrentDirectory() の結果が変わっていない。そのまま New-Object System.Drawing.Bitmap() に相対パスを渡すとファイルが見つからず失敗する。SetCurrentDirectory($PWD) しておく必要がある

参考にした記事