こんにちは、Tech Samuraiです!
最近、YOLOのカスタム学習のためにiPhoneで撮影した大量の写真をPCに取り込んだのですが、ある問題に直面しました。画像の拡張子が、すべて「.heic」なのです。
HEIC (High Efficiency Image Container) は、高画質のままファイルサイズを小さくできる優れた形式ですが、Windowsや一部のWebサービス、古いソフトウェアなどではまだ標準サポートされておらず、表示すらできないことがあります。YOLOの学習データ()として使うにも、汎用的なJPG形式の方がはるかに使い勝手が良いです。
そこで今回は、**「指定したフォルダ内のHEIC画像を、サブフォルダの構造を保ったまま、すべてJPGに一括変換する」**という、実用的なPythonスクリプトを開発しました。このツールの開発プロセスとコードの全貌を共有します!
ステップ1:プロジェクトの設計とライブラリ選定
今回のツールの主な要件は以下の通りです。
- 指定した入力フォルダ(サブフォルダを含む)を**再帰的に検索**したい。
- 見つけた
.heic/.HEICファイルをJPGに変換したい。 - 変換後のJPGは、元のフォルダ構造を維持したまま、別の出力フォルダに保存したい。
- JPGの品質を指定できるようにしたい。
この要件を実現するため、以下のライブラリを選定しました。
Pillow: Pythonの画像処理ライブラリの王様です。pillow-heif: Pillow本体だけではHEICを読めないため、PillowにHEICの読み書き能力を追加するための必須プラグインです。pathlib: モダンなPythonでファイルパスを扱うための標準ライブラリ。フォルダの再帰的検索(rglob)や拡張子の操作(with_suffix)が非常に直感的です。
ステップ2:HEICからJPGへ、変換のコアロジック
このスクリプトの心臓部は、pillow-heifを使ってHEIC画像を開き、Pillowの機能でJPGとして保存する部分です。
# スクリプトの冒頭で、pillow-heifをPillowに登録
try:
from pillow_heif import register_heif_opener
register_heif_opener()
except ImportError:
print("エラー: 'pillow_heif' がインストールされていません。")
sys.exit(1)
# ...
def convert_heic_to_jpg(input_dir: Path, output_dir: Path, quality: int):
# ...
for i, heic_path in enumerate(heic_files):
try:
# ... (出力パスの決定ロジック) ...
# 3. 画像を開いて変換・保存する
with Image.open(heic_path) as image:
# 【重要】HEICは透明情報(RGBA)を持つことがあるが、JPGは持たない(RGB)。
# .convert('RGB') を呼び出し、JPGで保存できる形式に変換する。
image.convert('RGB').save(jpg_path, format="JPEG", quality=quality)
success_count += 1
except Exception as e:
fail_count += 1
# ...
pillow_heif.register_heif_opener()`を実行するだけで、あとはImage.open(heic_path)`と書けば、PillowがHEICファイルを魔法のように開けるようになります。また、image.convert('RGB')`で透明情報を除去し、JPG形式で安全に保存できるようにするのも重要なポイントです。
ステップ3:`pathlib`によるフォルダの再帰的処理
このツールを実用的にするため、pathlib`のrglob`メソッドを使って、サブフォルダ内のファイルもすべて見つけ出します。さらに、relative_toやwith_suffixを使って、元のフォルダ構造を維持したまま出力パスを組み立てます。
# .heic と .HEIC の両方を "再帰的" に検索
heic_files = list(input_dir.rglob('*.heic')) + list(input_dir.rglob('*.HEIC'))
for heic_path in heic_files:
# 入力ディレクトリからの相対パスを取得
relative_path = heic_path.relative_to(input_dir)
# 出力ディレクトリに相対パスを組み合わせ、拡張子を .jpg に変更
jpg_path = output_dir / relative_path.with_suffix('.jpg')
# 出力先のサブディレクトリが存在しない場合は作成
jpg_path.parent.mkdir(parents=True, exist_ok=True)
# ... (変換処理) ...
このロジックにより、例えばINPUT_DIR/旅行/沖縄/IMG_001.HEIC`というファイルは、OUTPUT_DIR/旅行/沖縄/IMG_001.jpg`として完璧に保存されます。
完成したツールとコード
Gallery
これらのロジックを組み合わせて完成した、HEIC一括変換スクリプトの全コードは、以下のGitHubリポジトリで公開しています。使い方はREADME.md`に詳しく記載しています。
- GitHubリポジトリ: convert_heic
# 最終的なスクリプト (src/heic_2_jpg/convert_heic.py)
import sys
from pathlib import Path
from PIL import Image
# pillow-heif プラグインをPillowに登録します
try:
from pillow_heif import register_heif_opener
register_heif_opener()
except ImportError:
print("エラー: 'pillow_heif' がインストールされていません。")
print("Rye環境で 'rye add pillow-heif' を実行してください。")
sys.exit(1)
# --- 設定ここから ---
# 1. HEICファイルが保存されている親ディレクトリのパス
INPUT_DIR = Path("./src/heic_2_jpg/picture")
# 2. 変換後のJPGファイルを保存するディレクトリのパス
OUTPUT_DIR = Path("./src/heic_2_jpg/output_jpg_files")
# 3. 保存するJPGの品質 (1-100, デフォルトは95)
JPG_QUALITY = 95
# --- 設定ここまで ---
def convert_heic_to_jpg(input_dir: Path, output_dir: Path, quality: int):
# (関数の中身はステップ2, 3で解説した通り)
print(f"スキャンを開始します: {input_dir}")
print(f"保存先: {output_dir}")
print("-" * 30)
output_dir.mkdir(parents=True, exist_ok=True)
heic_files = list(input_dir.rglob('*.heic')) + list(input_dir.rglob('*.HEIC'))
if not heic_files:
print("変換対象のHEICファイルが見つかりませんでした。")
print(f"検索ディレクトリ: {input_dir.resolve()}")
return
print(f"{len(heic_files)} 件のHEICファイルが見つかりました。変換を開始します...")
success_count = 0
fail_count = 0
for i, heic_path in enumerate(heic_files):
try:
relative_path = heic_path.relative_to(input_dir)
jpg_path = output_dir / relative_path.with_suffix('.jpg')
jpg_path.parent.mkdir(parents=True, exist_ok=True)
with Image.open(heic_path) as image:
image.convert('RGB').save(jpg_path, format="JPEG", quality=quality)
print(f"[{i+1}/{len(heic_files)}] 成功: {heic_path.name} -> {jpg_path.name}")
success_count += 1
except Exception as e:
print(f"[{i+1}/{len(heic_files)}] ★失敗★: {heic_path.name} (エラー: {e})")
fail_count += 1
print("-" * 30)
print("変換処理が完了しました。")
print(f"成功: {success_count} 件")
print(f"失敗: {fail_count} 件")
if __name__ == "__main__":
if not INPUT_DIR.exists():
print(f"エラー: 入力ディレクトリが見つかりません: {INPUT_DIR.resolve()}")
print("スクリプト内の INPUT_DIR を正しいパスに変更してください。")
sys.exit(1)
convert_heic_to_jpg(INPUT_DIR, OUTPUT_DIR, JPG_QUALITY)
まとめ
iPhoneとWindows/Linux環境の橋渡しをする、実用的な画像変換ツールを開発しました。YOLOのデータセット準備()だけでなく、ブログに画像をアップロードする際など、様々な場面でこのツールは役立つはずです。ぜひ、あなたのPCにも導入してみてください!


コメント