管理人Kのひとりごと

デジモノレビューやプログラミングや写真など

Exifの撮影日時からフォルダを作成して画像をコピーする

画像管理はWindows10の「フォト」アプリを利用しています。
フォトアプリの画像管理のベースは撮影日時らしく、インポート機能を利用すると、撮影日ごとにフォルダが作られ、画像が振り分けられます。
インポート機能では、リムーバブルメディア以外のインポートは、フォルダへの参照が作られるだけの様で、管理フォルダ配下に集約したい自分の思っていた挙動ではなく...既にPC内に存在する画像もフォトアプリの管理ができるよう、Powershellで撮影日ベースの仕分けスクリプトを書いてみました。

方針

  • 元画像フルパス(a)、コピー先の親フォルダパス(b)をインプットにする
  • (a)からExif情報を基に撮影日時を取得する。Exifの撮影日時について☟

  • 撮影日時を基に、「yyyy-mm-dd」形式のフォルダ名(c)を作成する
  • (b)に、(c)フォルダが存在するかチェックし、存在しなければ作成
  • (a)を、(b)の(c)フォルダにコピーする

コード

フォルダ作成要否チェック用のパラメータをつけてみた

param(
[string]$filename    # 処理対象ファイル名(フルパス)
,[string]$targetDir  # 移動先親ディレクトリパス
,[switch]$test       # このパラメータがついていたら、ディレクトリ作成、コピーはしない
)

# エラーがあった時点で処理終了
$ErrorActionPreference = "stop"
[void][System.Reflection.Assembly]::LoadWithPartialName("System.Drawing")

# 撮影日取得処理
# 引数1 :Bitmapオブジェクト
# 戻り値:なし
function GetSatsueiDate($sourceImage){
    $propDate=""
    
    # Exif情報を取得
    $private:properties = $sourceImage.PropertyItems
    foreach($property in $properties){
      if($property.Id -eq 36867){ #0x9003 PropertyTagExifDTOrig
        $propDate = [System.Text.Encoding]::ASCII.GetString($property.Value)
        break
      }
    }

    return $propDate
}

function script:makeFolderName($originalSatsueiDate){
    $folderName = ""

    if($originalSatsueiDate -eq $null -or $originalSatsueiDate.length -lt 1){
        return $folderName
    }

    # yyyy.mm.ddを抽出しyyyy-mm-ddに変換
    if($originalSatsueiDate -match "(?<SatsueiDate>\d\d\d\d.\d\d.\d\d?)"){
        $folderName = $Matches["SatsueiDate"].replace(":","-")
        return $folderName
    }else{
        return $folderName
    }
}


# メイン処理
# 引数1 :元画像ファイルのフルパス
# 戻り値:なし
function script:Main($originalFileName){
  $private:sourceImage = $null

  try{
      # 元画像取得(Bitmap)
      $sourceImage = New-Object System.Drawing.Bitmap($originalFileName)

      # 撮影日取得
      $satsueiDate = GetSatsueiDate $sourceImage

      # 元Bitmapを閉じる
      $sourceImage.Dispose()

      # 撮影日付をyyyy-mm-dd形式に変換し、フォルダ名を作成
      $folderName = makeFolderName $satsueiDate

      $joinedPath = Join-Path $targetDir $folderName

      if (Test-Path $joinedPath){
        echo "存在します:$joinedPath"
      }else{
        echo "作成します(テスト):$joinedPath"
        if($test -eq $false){
            echo "作成します:$joinedPath"
            mkdir $joinedPath >$null 2>&1
        }
      }

      if($test -eq $false){
          cp $originalFileName $joinedPath
      }
  }catch{
    echo "例外が発生しました:$error"
  }finally{
    try{
      if($null -ne $sourceImage){
        $sourceImage.Dispose()
      }
    }catch{
      echo "画像クローズ時の例外:$error"
    }
  }
}

# 撮影日取得処理の実行
Main $filename

実行結果

# 実際にフォルダ作成、コピーを実行する場合
PS D:\Documents\scripts> .\撮影日で写真を仕分け.ps1 -filename "C:\Users\hoge\Pictures\2015\2015-01-02\IMG_20150102_180534.jpg" -targetDir D:\
作成します:D:\2015-01-02

# 実際にフォルダ作成、コピーを実行しない場合
PS D:\Documents\scripts> .\撮影日で写真を仕分け.ps1 -filename "C:\Users\hoge\Pictures\2015\2015-01-02\IMG_20150102_180534.jpg" -targetDir D:\ -test
作成します(テスト):D:\2015-01-02