【PowerShell】画像ファイルを一括リネームして、Exif情報も反映する
はじまり




重宝していたアプリが消えた・・・
つい最近までこんなリネームアプリを使っていたのですが、機種変更して OS バージョンが上がったら使えなくなってしまいました・・・


なので、そろそろこのりネーム機能を作りますか・・・
ファイルのリネームの基本
PowerShell でファイルをリネームする基本コマンドは Rename-Item です。このコマンドは -Path パラメータで指定されたファイルの名前を -NewName パラメータで指定された新しい名前に変更します。
画像ファイルを一括でリネームする
画像ファイルの一括リネームは、デジタルアセットの整理、アップロード前の準備、またはバックアッププロセスの一環として非常に便利です。
基本コマンド
Get-ChildItem -Path "C:\Images\" -Filter *.jpg | Rename-Item -NewName { $_.Name -replace '旧文字列','新文字列' }このコマンドは、指定したディレクトリ(この例では C:\Images\)内のすべての jpg ファイルを対象に、ファイル名内の「旧文字列」を「新文字列」に置換します。
このコマンドの詳細
少し噛み砕きます。
ファイルの選択
Get-ChildItem -Path "対象ディレクトリ" -Filter *対象ファイル形式*このコマンドは、指定したディレクトリ内の特定のファイル形式(例:.jpg、.png)を検索します。
リネームの実行
リネームプロセスでは、新しいファイル名の形式を定義する必要があります。これは、ファイルの作成日、撮影日、またはシーケンス番号を含むことができます。
| Rename-Item -NewName { 新しいファイル名の定義 }このパイプライン (|) コマンドは、直前のコマンドレットで選択したファイルを次のコマンドレットに渡します。
今回はRename-Itemに渡してリネーム操作を適用します。
ファイルの最終更新日からリネームする
この例では、ファイルの最終書き込み時刻を使用して、画像ファイルを年月日時分秒.jpgの形式でリネームします。$_.Extensionを忘れないようにですね。
Get-ChildItem -Path "C:\Images\" -Filter *.jpg | Rename-Item -NewName { $_.LastWriteTime.ToString('yyyyMMddHHmmss') + $_.Extension }で、ここまで書いたのですが、「ファイルの最終更新日」でリネームしてしまうと、スマホから撮った写真を PC に「コピーした時の日時」でリネームてしまいます。
なので、ここからはもう少し深い部分に入ります。
Exif情報からリネームする
そこで、別の情報からファイルのリネームを掛けていきたいと思います。
画像ファイルは、「Exif」という情報を持っています。(Exchangeable image file format というらしいです。)
カメラの ISO だったり、 F 値だったりその時に撮影したカメラの情報とかが画像ファイルの中に色々と入っているわけです。
そして、その Exif の情報の中には「ファイルの作成日時」が存在します。 PowerShell 標準のCreationTimeでも出来そうなもんですが、今回はそのカメラの情報も欲しいので、 Exif という角度から切り込みます。
Exif取得に使うツール
今回、 Exif 取得に使うツールは、 Phil Harvey さんが開発している「ExifTool」というものです。この方面では一般的なツールみたいですね。
このサイトから exe をダウンロードしていきましょう。
ExifToolを使って、ファイル作成日時からリネームする
使っていきます。以下のコマンドを実行する際には、実行ディレクトリにexiftool.exeを配置して使っていきます。
詳しい使い方・仕様は、ダウンロードサイトにありますが、実際に実行してみたいコマンドを以下で紹介します。
ファイルのCreateDateからリネームする
まずは、ファイルの作成日時からリネームしていきましょう。
流れとしては、
Start-Processで実行する処理のプロセスを設定。(PassThruを入れないとプロセスの戻り値を取れません。waitでプロセス終了まで待ちます。)"${folderDir}\exiftool"でディレクトリ内にあるexiftool.exeを指定します。exiftoolに使う引数は、ArgumentListの引数の中に配置します。ArgumentListの引数の中には、"-FileName<CreateDate"を指定して、ファイルの作成日時からファイル名を変えるようにします。フォーマットは、"%Y%m%d%H%M%S.%%e"として名付けます。
$username = (Get-ChildItem Env:\USERNAME).Value;$folderDir = "C:\Users\${username}\Downloads\picture_backup";cd $folderDir;$proc = Start-Process -FilePath "${folderDir}\exiftool" -ArgumentList "-FileName<CreateDate","-d","%Y%m%d%H%M%S.%%e",$folderDir -NoNewWindow -PassThru -wait;Write-Host $proc.ExitCode;CreateDateフィールドを作る(画像ファイル版)
しかし、ファイルによっては、CreateDateのフィールドがない画像ファイルがあったりもします。
その場合に、CreateDateフィールドを作ってあげましょう。
次は、"-CreateDate<FileCreateDate"と引数に指定して、
cd 1-2_create_date_setting;$username = (Get-ChildItem Env:\USERNAME).Value;$toCreateDateDir = "1-2_create_date_setting";$folderDir = "C:\Users\${username}\Downloads\picture_backup\${toCreateDateDir}";$proc = Start-Process -FilePath "${folderDir}\exiftool" -ArgumentList "-CreateDate<FileCreateDate","-d","%Y:%m:%d:%H:%M:%S",$folderDir -NoNewWindow -PassThru -wait;Write-Host $proc.ExitCode;CreateDateフィールドを作る(動画ファイル版)
しかしながら、画像ファイルと違って、僕が撮影した動画ファイルのCreateDateには、実際に撮影した日時とは別の情報が入っていました。
そこで次は、"-CreateDate<FileModifyDate"と引数に指定して、 Exif 内にある更新日時からCreateDateの情報を作成します。
cd 1-1_movie_escaping;$username = (Get-ChildItem Env:\USERNAME).Value;$toCreateDateDir = "1-1_movie_escaping";$folderDir = "C:\Users\${username}\Downloads\picture_backup\${toCreateDateDir}";$proc = Start-Process -FilePath "${folderDir}\exiftool" -ArgumentList "-CreateDate<FileModifyDate","-d","%Y:%m:%d:%H:%M:%S",$folderDir -NoNewWindow -PassThru -wait;Write-Host $proc.ExitCode;しかし、もうファイル作成日時の情報が一切無い場合もある。
しかしながら、もう作成日時の情報がファイルの中に一切無い場合もあります。(LINE からダウンロードする時とか。)
なので、そういうイレギュラー画像ファイルには、もう自分が日時を指定してリネームしてあげるしかありません!
PowerShell 上でプロンプトしてリネームしましょう。
$number = 1$date_formatted = Get-Date -Format "yyyyMMddHH"$date_formatted = Get-Date -Format "yyyyMMddH"$year_this = Get-Date -UFormat "%Y"$year_last = Get-Date (Get-Date).AddYears(-1) -UFormat "%Y"$iscorrect = $falsewhile($iscorrect -eq $false){ $date_formatted = Read-Host "Input prefix of pictures (yyyyMMddHH)" if($date_formatted.length -eq 10 -and $date_formatted -like "${year_this}*" -or $date_formatted -like "${year_last}*"){ $iscorrect = $true }}Get-ChildItem -Path . -File | ForEach-Object { if($_ -notlike "${year_this}*" -and $_ -notlike "${year_last}*" -and $_ -notlike "*.exe" -and $_ -isnot [System.IO.DirectoryInfo]){ Write-Host $_.GetType() "true" $_.Name $number_zeropadded = "{0:0000}" -f $number $ext = [System.IO.Path]::GetExtension($_) $newName = "${date_formatted}${number_zeropadded}${ext}" Write-Host $newName Rename-Item -Path ".\${_}" -NewName $newName $number++ }else{ Write-Host $_.GetType() "false" $_.Name }}そして、加工した画像ファイルに、 Exif 情報を与える
しかし、ここまででは筆者は終わりたくありません。
最近のフラッグシップスマホのカメラで撮る画像ファイルって、容量がデカくありませんか? 5 MB なんて優に超えますよね。
なので、僕は画像ファイルを jpg → webp に変換して容量を下げて、その webp に対して Exif 情報を付与し直すこともしてみました。
Exif 情報を元 JPG ファイルから与える
デカすぎる画像ファイルを webp に加工した後、その元 jpg ファイルが立ち並ぶディレクトリの中に webp ファイルを格納します。
そうしたら、このコマンドで webp に Exif 情報を与えます。
Get-ChildItem *.webp | ForEach-Object { $jpg = $_.BaseName + ".jpg"; if (Test-Path $jpg) { $webpFilePath = $_.FullName; Start-Process -FilePath exiftool.exe -ArgumentList "-tagsFromFile",$jpg,"-exif:all",$webpFilePath -NoNewWindow -Wait; }}Exif情報を元JPGファイルから与える(DOS版)
僕は当初、 DOS から全てのコマンドを実行したかったので、このコマンドの DOS 実行バージョンもあります。
STARTでプロセスを実行、/WAITオプションで PowerShell の処理終了まで PowerShell を開きっぱなしにします。Commandの中に、 PowerShell で実行するコマンドを書きます。- そのコマンドの中の、ダブルクォーテーションはバックスラッシュ(円マーク)でエスケープしてあげましょう。
START /WAIT powershell -Command "Get-ChildItem *.webp | ForEach-Object {$jpg = $_.BaseName + \".jpg\"; if (Test-Path $jpg) {$webpFilePath = $_.FullName; Start-Process -FilePath exiftool.exe -ArgumentList \"-tagsFromFile\",$jpg,\"-exif:all\",$webpFilePath -NoNewWindow -Wait}}"こうすると、Googleフォトにて・・・
上記の処理をしてあげることで、 Google フォトにアップロードすると、
①容量が減った webp の状態で、②撮影日時を保持した状態で、③カメラの情報も保持した、写真が出来上がります!

まとめ
本記事のまとめです。
- PowerShell から、ファイルの
LastWriteTimeを見てファイルをリネーム出来る。 - しかし、もう少しちゃんとリネームしたかったら、「ExifTool」を使うのがオススメ。
- 「ExifTool」では、
CreateDateフィールドを使って撮影日時でリネーム出来る。 CreateDateフィールドが無い場合は、画像ではFileCreateDate、動画ではFileModifyDateフィールドから編集できる。- ファイルの Exif 情報に全く情報がない場合は、自動採番したり、別の画像ファイルから Exif 情報を引っ張れる。
PowerShell は最近使ってなかったので、改めて便利だなと感じました。
おしまい


以上になります!
記事を共有
この記事が役に立ったなら、ぜひ他の人と共有してください!