大量のiPhone写真をネットプリントするために、HEIC→JPG変換を自動化した話
はじめに
先日、溜まりに溜まった家族写真を「そろそろ印刷しないと…」と思い立ちました。
対象は iPhone とデジカメ で撮影した写真で、合計すると数百枚あります。
せっかくなのでネットプリントで一括発注しようとしたところ、
iPhoneの写真がHEIC形式のため、そのままでは印刷できない という問題にぶつかりました。
ネットプリントは、ほとんどの業者が JPG形式のみ対応 しており、
HEIC形式の写真は事前に変換する必要があります。
変換サービスや専用ソフトもいくつかありますが、
- 枚数に上限がある
- 有料プランが前提
- 写真を外部サービスにアップロードする点が気になる
といった理由から、今回は使いませんでした。
そこで本記事では、
HEIC形式の写真をJPGに一括変換する処理を、自力で自動化したときの記録をまとめます。
まずは写真を一箇所に集約する
大量の写真を扱う前に、まずやったのは整理です。
- Googleフォトを有料契約(2TB)
- iPhoneの写真を丸ごとバックアップ
- デジカメ(Z50)の写真もPC経由でGoogleフォトへ追加
- 印刷対象が分かりやすいよう 「印刷用アルバム」 を作成
- そのアルバムを妻とファミリー共有
- 「印刷したい写真はここに入れてね」という運用に
結果、最終的に 約500枚 になりました。
実店舗プリントは高い、ネットプリントは安い
これまでは、
- 近所のFUJIFILM
- カメラのキタムラ
などで印刷していましたが、
枚数が増えるとそれなりにお値段が張ります。
今回はコストを抑えたかったので、
ネットプリント を使うことにしました。
ネットプリントの比較サイト等色々ありますが、今回はDPE宅配便という業者に依頼することにしました。
使ってみた感想
- ブラウザ経由でアップロード可能
- 500枚まとめてアップしても途中で落ちない
- 動作はかなり軽快
ただし、一点だけ問題がありました。
iPhoneの写真、HEIC問題
ネットプリントのアップロード形式は JPGのみ。
iPhoneで撮った写真は HEIC形式 なので、そのままではアップロードできません。
選択肢として考えたこと
- HEIC → JPG 変換ソフト
→ だいたいシェアウェア。最初の数枚だけ無料というものが多く、今回は枚数的に現実的ではありませんでした。 - HEIC → JPG 変換してくれるサイト
→ プライバシーとかガバガバで怖い。不安。口コミ関係なく却下。 - 自作スクリプトで変換する
→ どうやらWindowsストアに「HEIF画像表示オプション」なる拡張機能があり、これでHEIC形式が読めるようになるらしい。これでいく。
実際にやってみたが…
HEIC形式の画像を1枚ずつ、
- 画像を開く
- 「名前をつけて保存」
- JPG形式を選んで保存
という手順が必要でした。
当然ながら、これを数百枚やるのは現実的ではありません。
ということで自動化する
結論として、
スクリプトを書いて一括変換することにしました。
- フォルダを指定
- HEICをまとめてJPGに変換
- 別フォルダに吐き出す
という単純なものです。
スクリプト
※ 細かいエラーハンドリングなどはしていない「自分用の雑スクリプト」です。
# HEIC → JPG 一括変換スクリプト
param(
[Parameter(Mandatory=$true)][string]$InDir,
[Parameter(Mandatory=$true)][string]$OutDir,
[int]$Quality = 92,
[switch]$Recurse
)
# WPFを使う(HEICデコードはWindowsのコーデックに依存)
Add-Type -AssemblyName PresentationCore
if (!(Test-Path $InDir)) { throw "入力フォルダが存在しません: $InDir" }
if (!(Test-Path $OutDir)) { New-Item -ItemType Directory -Path $OutDir | Out-Null }
# PowerShell 5.1互換:三項演算子を使わない
if ($Recurse) {
$files = Get-ChildItem -Path $InDir -Filter *.heic -File -Recurse
} else {
$files = Get-ChildItem -Path $InDir -Filter *.heic -File
}
$failed = New-Object System.Collections.ArrayList
$done = 0
$total = $files.Count
foreach ($f in $files) {
try {
$inPath = $f.FullName
# サブフォルダ構成を保ちたい場合(Recurse時)は相対パスを作る
if ($Recurse) {
$rel = $f.FullName.Substring($InDir.Length).TrimStart('\')
$subDir = Split-Path $rel -Parent
$outDir2 = if ([string]::IsNullOrEmpty($subDir)) { $OutDir } else { Join-Path $OutDir $subDir }
if (!(Test-Path $outDir2)) { New-Item -ItemType Directory -Path $outDir2 -Force | Out-Null }
$outPath = Join-Path $outDir2 ($f.BaseName + ".jpg")
} else {
$outPath = Join-Path $OutDir ($f.BaseName + ".jpg")
}
# 画像読み込み(HEIF拡張が入っていればここで読める)
$stream = [System.IO.File]::OpenRead($inPath)
try {
$bmp = New-Object System.Windows.Media.Imaging.BitmapImage
$bmp.BeginInit()
$bmp.CacheOption = [System.Windows.Media.Imaging.BitmapCacheOption]::OnLoad
$bmp.StreamSource = $stream
$bmp.EndInit()
$bmp.Freeze()
} finally {
$stream.Dispose()
}
# JPEGエンコード
$encoder = New-Object System.Windows.Media.Imaging.JpegBitmapEncoder
$encoder.QualityLevel = [Math]::Max(1, [Math]::Min(100, $Quality))
$encoder.Frames.Add([System.Windows.Media.Imaging.BitmapFrame]::Create($bmp))
$outStream = [System.IO.File]::Open($outPath, [System.IO.FileMode]::Create)
try {
$encoder.Save($outStream)
} finally {
$outStream.Dispose()
}
# タイムスタンプ合わせ(任意)
(Get-Item $outPath).CreationTime = $f.CreationTime
(Get-Item $outPath).LastWriteTime = $f.LastWriteTime
$done++
if ($done % 10 -eq 0 -or $done -eq $total) {
Write-Host ("{0}/{1} 完了" -f $done, $total)
}
}
catch {
[void]$failed.Add($f.FullName + " / " + $_.Exception.Message)
Write-Warning ("NG: {0} / {1}" -f $f.FullName, $_.Exception.Message)
}
}
if ($failed.Count -gt 0) {
Write-Host "`n失敗一覧:" -ForegroundColor Yellow
$failed | ForEach-Object { Write-Host $_ }
exit 1
} else {
Write-Host "`n全部完了" -ForegroundColor Green
}
動かしてみる。
Powershellを開き、以下のコマンドを実行する
powershell -ExecutionPolicy Bypass -File .\heicconv.ps1 `
-InDir "C:\~略~\heic" `
-OutDir "C:\~略~\heic\conv"

できた。
おわりに
技術的には大したことはしていませんが、
こういうちょっとした 「面倒くさい」を自力で潰すのは気持ちがいいですね。
同じように
「HEICが印刷できない…」
となっている人の参考になれば幸いです。
Discussion