Python library to extract, read, modify, and write photo and video metadata (EXIF, IPTC, XMP) using ExifTool. Supports JPEG, RAW, and video files.
🗒️ このREADMEは 日本語と英語の両方 を含みます。 📄 This README includes both English and Japanese versions.
📘 English section is available below: Go to English version
📕 日本語 セクションはこちらからどうぞ: 日本語版へ移動
photo-metadata is a Python library for extracting, manipulating, and writing metadata from photo and video files. It uses ExifTool as a backend and supports a wide range of image and video formats. Full support for Japanese tags is also provided.
- Extract metadata from photos and videos
- Read, write, and delete metadata
- Convenient methods for various metadata operations
- Compare two
Metadataobjects - Filter multiple files by metadata
- Rename multiple files based on capture date or other metadata
- Windows
- Linux
pip install photo-metadata- [ExifTool] (needs to be installed separately; either add to PATH or provide full path)
- [tqdm] (automatically installed via pip; used for progress display)
- [charset-normalizer] (automatically installed via pip; used for encoding detection)
import photo_metadata
# Set the path to ExifTool
photo_metadata.set_exiftool_path(exiftool_path)The default exiftool_path is "exiftool". If ExifTool is already in your PATH, calling set_exiftool_path is not required.
The Metadata class is the core class for working with metadata.
from photo_metadata import Metadatametadata = Metadata(file_path="path/to/your/image.jpg")file_path(str): Path to the image file
Metadata can be accessed like a dictionary.
Access using English tags:
date_time = metadata["EXIF:DateTimeOriginal"]
print(date_time)Access using Japanese tags:
date_time = metadata[photo_metadata.key_ja_to_en("EXIF:撮影日時")]
print(date_time)You can modify metadata like a dictionary:
metadata["EXIF:DateTimeOriginal"] = "2024:02:17 12:34:56"metadata.write_metadata_to_file()Metadata can be deleted using the del statement:
del metadata["EXIF:DateTimeOriginal"]Two Metadata objects can be compared using == and !=:
metadata1 = Metadata("image1.jpg")
metadata2 = Metadata("image2.jpg")
if metadata1 == metadata2:
print("Metadata is identical")
else:
print("Metadata is different")MetadataBatchProcess allows you to process metadata for multiple files.
from photo_metadata import MetadataBatchProcessmbp = MetadataBatchProcess(file_path_list)mbp.filter_by_metadata(
keyword_list=["NEX-5R", 2012],
exact_match=True,
all_keys_match=True,
search_by="value"
)
for file, md in mbp.metadata_objects.items():
print(f"{os.path.basename(file)}")This example keeps files whose metadata values include both "NEX-5R" and 2012.
mbp.filter_by_custom_condition(
lambda md: md[photo_metadata.key_ja_to_en("EXIF:F値")] >= 4.0
and md[photo_metadata.key_ja_to_en("EXIF:モデル")] == 'NEX-5R'
)
for file, md in mbp.metadata_objects.items():
print(f"{os.path.basename(file)}")This example keeps files where the EXIF F-number is ≥ 4.0 and the camera model is 'NEX-5R'.
import os
from tkinter import filedialog
from photo_metadata import MetadataBatchProcess, Metadata
def date(md: Metadata):
date = md.get_date('%Y-%m-%d_%H.%M.%S', default_time_zone="+00:00")
if date == md.error_string:
raise Exception("Not Found")
return f"{date}-{MetadataBatchProcess.DUP_SEQ_1_DIGIT}" # This is a duplicate sequence. It increments if duplicates exist, starting from 0. Must be included in the format.
file_path_list = list(map(os.path.normpath, filedialog.askopenfilenames()))
mbp = MetadataBatchProcess(file_path_list)
# Prepare rename creates new_name_dict for preview
mbp.prepare_rename(format_func=date)
print("new_name_dict")
for file, new_name in mbp.new_name_dict.items():
print(f"{file}\n{new_name}")
print("\nerror_dist")
for file, new_name in mbp.error_files.items():
print(f"{file}\n{new_name}")
input("Press Enter to rename files")
mbp.rename_files()get_key_map() -> dict: Returns a dictionary for Japanese key conversion.set_exiftool_path(exiftool_path: str | Path) -> None: Sets the path to exiftool.get_exiftool_path() -> Path: Returns the configured path to exiftool.set_jp_tags_json_path(jp_tags_json_path: str | Path) -> None: Sets the path to the Japanese tags JSON file.get_jp_tags_json_path() -> Path: Returns the configured path to the Japanese tags JSON file.key_en_to_ja(key_en: str) -> str: Converts an English key to its Japanese equivalent.key_ja_to_en(key_ja: str) -> str: Converts a Japanese key to its English equivalent.
-
__init__(self, file_path: str | Path): Constructor. -
display_japanese(self, return_type: Literal["str", "print", "dict"] = "print") -> str: Displays metadata using Japanese keys. -
write_metadata_to_file(self, file_path: str = None) -> None: Writes metadata to a file. -
get_metadata_dict(self) -> dict: Returns the metadata as a dictionary. -
export_metadata(self, output_path: str = None, format: Literal["json", "csv"] = 'json', lang_ja_metadata: bool = False) -> None: Exports metadata to a file. -
keys(self) -> list[str]: Returns a list of metadata keys. -
values(self) -> list[Any]: Returns a list of metadata values. -
items(self) -> list[tuple[str, Any]]: Returns a list of key-value pairs for metadata. -
get_gps_coordinates(self) -> str: Returns GPS coordinates. -
export_gps_to_google_maps(self) -> str: Converts GPS information to a Google Maps URL. -
get_date(self, format: str = '%Y:%m:%d %H:%M:%S', default_time_zone: str = '+00:00') -> str: Returns the capture date (customizable date format). -
get_image_dimensions(self) -> str: Returns image dimensions. -
get_file_size(self) -> tuple[str, int]: Returns the file size. -
get_model_name(self) -> str: Returns the camera model name. -
get_lens_name(self) -> str: Returns the lens name. -
get_focal_length(self) -> dict: Returns focal length information. -
show(self) -> None: Displays the file. -
get_main_metadata(self) -> dict: Returns major metadata fields. -
contains_key(self, key, exact_match: bool = True): Checks whether the specified key exists. -
contains_value(self, value, exact_match: bool = True): Checks whether the specified value exists. -
copy(self) -> "Metadata": Copies the instance of the Metadata class. -
@classmethod def load_all_metadata(cls, file_path_list: list[str], progress_func: Callable[[int], None] | None = None, max_workers: int = 40) -> dict[str, "Metadata"]: Efficiently loads metadata from multiple files in parallel.
__init__(self, file_list: list[str], progress_func: Callable[[int], None] | None = None, max_workers: int = 40): Constructor.filter_by_custom_condition(self, condition_func: Callable[[Metadata], bool]) -> None: Filters metadata using a custom condition function.filter_by_metadata(self, keyword_list: list[str], exact_match: bool, all_keys_match: bool, search_by: Literal["either", "value", "key"]) -> None: Finds files containing specific values, keys, or either in their metadata.prepare_rename(self, format_func: Callable[[Metadata], str]) -> None: Prepares files for renaming.rename_files(self) -> str: Renames the files.copy(self) -> "MetadataBatchProcess": Copies the instance of the MetadataBatchProcess class.
- PyPI:
https://pypi.org/project/photo-metadata/ - GitHub:
https://github.com/kingyo1205/photo-metadata
ExifTool is required. This library uses ExifTool as an external command to process image and video metadata.
Some parts of the code in this repository were generated or assisted by AI tools such as ChatGPT and Gemini CLI. No generated content that cannot be used in open-source projects like LMArena is included.
ExifTool must be installed on your system. Download it from the official website.
This library is distributed under the MIT License.
However, ExifTool itself is distributed under the Artistic License 2.0.
If you use ExifTool, please make sure to comply with its license terms.
(Verified in 2025 / Based on information listed on PyPI)
| Library | License |
|---|---|
| charset_normalizer | MIT |
| tqdm | MIT |
photo-metadataは、���真や動画ファイルからメタデータを抽出、操作、書き込みを行うためのPythonライブラリです。exiftoolをバックエンドで使用し、幅広い画像、動画フォーマットに対応しています。日本語タグのサポートも特徴です。
- 写真や動画ファイルのメタデータの抽出
- メタデータの読み取り、書き込み、削除
- さまざまなメタデータ操作のための便利なメソッド
- 2つのMetadataオブジェクトの比較
- 複数のファイルをメタデータでフィルター
- 複数のファイルを撮影日時などでリネーム
- Windows
- Linux
pip install photo-metadata
- [exiftool] (別途インストールが必要です。 パスを通すかフルパスを指定してください)
- [tqdm] (pipで自動でインストールされます。進捗表示用です)
- [charset-normalizer] (pipで自動でインストールされます。 エンコーディング解析用です)
import photo_metadata
# exiftoolのパスを設定
photo_metadata.set_exiftool_path(exiftool_path)Metadataクラスは、メタデータ操作の中心となるクラスです。
from photo_metadata import Metadatametadata = Metadata(file_path="path/to/your/image.jpg")file_path(str): 画像ファイルのパス
メタデータは、辞書のようにアクセスできます。
英語のタグでアクセス
date_time = metadata["EXIF:DateTimeOriginal"]
print(date_time)日本語のタグでアクセス
date_time = metadata[photo_metadata.key_ja_to_en("EXIF:撮影日時")]
print(date_time)メタデータは、辞書のように変更できます。
metadata["EXIF:DateTimeOriginal"] = "2024:02:17 12:34:56"metadata.write_metadata_to_file()メタデータは、delステートメントで削除できます。
del metadata["EXIF:DateTimeOriginal"]==と!=演算子を使用して、2つのMetadataオブジェクトを比較できます。
metadata1 = Metadata("image1.jpg")
metadata2 = Metadata("image2.jpg")
if metadata1 == metadata2:
print("メタデータは同じです")
else:
print("メタデータは異なります")MetadataBatchProcessは複数ファイルのメタデータを処理するためのクラスです。
from photo_metadata import MetadataBatchProcessmbp = MetadataBatchProcess(file_path_list)def __init__(self, file_list: list[str],
progress_func: Callable[[int], None] | None = None,
max_workers: int = 40):mbp.filter_by_metadata(keyword_list=["NEX-5R", 2012],
exact_match=True,
all_keys_match=True,
search_by="value")
for file, md in mbp.metadata_objects.items():
print(f"{os.path.basename(file)}")この場合はメタデータの値に"NEX-5R", 2012が両方とも、存在したファイルが残る
mbp.filter_by_custom_condition(lambda md: md[photo_metadata.key_ja_to_en("EXIF:F値")] >= 4.0 and md[photo_metadata.key_ja_to_en("EXIF:モデル")] == 'NEX-5R')
for file, md in mbp.metadata_objects.items():
print(f"{os.path.basename(file)}")この場合はメタデータのEXIF:F値が4.0以上かつ、EXIF:モデルが'NEX-5R'のファイル��残る
import os
from tkinter import filedialog
from photo_metadata import MetadataBatchProcess, Metadata
def date(md: Metadata):
date = md.get_date('%Y年%m月%d日-%H.%M.%S', default_time_zone="+09:00")
if date == md.error_string:
raise Exception("Not Found")
return f"{date}-{MetadataBatchProcess.DUP_SEQ_1_DIGIT}" これは重複連番です。重複したときに数字が増えます。基本は0になります。フォーマットに必ず含めてください。
file_path_list = list(map(os.path.normpath, filedialog.askopenfilenames()))
mbp = MetadataBatchProcess(file_path_list)
# prepare_rename を実行すると new_name_dict が作成され、
# ファイル名のリネームプレビューが可能になります。
mbp.prepare_rename(format_func=date)
print("new_name_dict")
for file, new_name in mbp.new_name_dict.items():
print(f"{file}\n{new_name}")
print("\nerror_dist")
for file, new_name in mbp.error_files.items():
print(f"{file}\n{new_name}")
input("リネームするなら enter キーを押してください")
mbp.rename_files()この場合は日付でリネームします。 photo_metadata.MetadataBatchProcess.DUP_SEQ_1_DIGIT これは重複連番です。重複したときに数字が増えます。基本は0になります。フォーマットに必ず含めてください。
if date == md.error_string:
raise Exception("Not Found")日付が取得できない際はエラーを出してください。
get_key_map() -> dict: 日本語キー変換用の辞書を取得できますset_exiftool_path(exiftool_path: str | Path) -> None: exiftoolのパスを設定できますget_exiftool_path() -> Path: 設定されたexiftoolのパスを取得できますset_jp_tags_json_path(jp_tags_json_path: str | Path) -> None: 日本語タグのJSONファイルのパスを設定できますget_jp_tags_json_path() -> Path: 設定された日本語タグのJSONファイルのパスを取得できます`key_en_to_ja(key_en: str) -> str: 英語のキーを日本語に変換しますkey_ja_to_en(key_ja: str) -> str: 日本語のキーを英語に変換します
__init__(self, file_path: str | Path): コンストラクタdisplay_japanese(self, return_type: Literal["str", "print", "dict"] = "print") -> str: メタデータを日本語のキーで表示できますwrite_metadata_to_file(self, file_path: str = None) -> None: メタデータをファイルに書き込むget_metadata_dict(self) -> dict: メタデータの辞書を取得しますexport_metadata(self, output_path: str = None, format: Literal["json", "csv"] = 'json', lang_ja_metadata: bool = False) -> None: メタデータをファイルにエクスポートkeys(self) -> list[str]: メタデータのキーのリストを取得しますvalues(self) -> list[Any]: メタデータの値のリストを取得しますitems(self) -> list[tuple[str, Any]]: メタデータのキーと値のペアのリストを取得しますget_gps_coordinates(self) -> str: GPS座標を取得export_gps_to_google_maps(self) -> str: GPS情報をGoogleマップのURLに変換get_date(self, format: str = '%Y:%m:%d %H:%M:%S', default_time_zone: str = '+00:00') -> str: 撮影日時を取得 (日付フォーマットを指定できます)get_image_dimensions(self) -> str: 画像の寸法を取得get_file_size(self) -> tuple[str, int]: ファイルサイズを取得get_model_name(self) -> str: カメラの機種名を取得get_lens_name(self) -> str: レンズ名を取得get_focal_length(self) -> dict: 焦点距離を取得show(self) -> None: ファイルを表示get_main_metadata(self) -> dict: 主要なメタデータを取得contains_key(self, key, exact_match: bool = True): キーが存在するか確認しますcontains_value(self, value, exact_match: bool = True): 値が存在するか確認しますcopy(self) -> "Metadata": Metadataクラスのインスタンスをコピーします@classmethod def load_all_metadata(cls, file_path_list: list[str], progress_func: Callable[[int], None] | None = None, max_workers: int = 40) -> dict[str, "Metadata"]: 複数のファイルのメタデータを並列処理で高速に取得します。
__init__(self, file_list: list[str], progress_func: Callable[[int], None] | None = None, max_workers: int = 40): コンストラクタfilter_by_custom_condition(self, condition_func: Callable[[Metadata], bool]) -> None: メタデータを任意の関数 (条件) でフィルターしますfilter_by_metadata(self, keyword_list: list[str], exact_match: bool, all_keys_match: bool, search_by: Literal["either", "value", "key"]) -> None: メタデータに特定の値またはキーまたはキーと値どちらかに存在するファイルを見つけるprepare_rename(self, format_func: Callable[[Metadata], str]) -> None: リネームの準備をしますrename_files(self) -> str: ファイルをリネームしますcopy(self) -> "MetadataBatchProcess": MetadataBatchProcessクラスのインスタンスをコピーします
https://pypi.org/project/photo-metadata/
https://github.com/kingyo1205/photo-metadata
exiftoolが必ず必要です。
このライブラリは、画像やメタデータを処理する際にExifToolを外部コマンドとして使用しています。
本リポジトリの一部コードは ChatGPT, Gemini CLIなど用いて生成・補助しました。 LMArena 等の OSS にできない可能性がある生成物は一切含まれていません。
このライブラリを使用するには、ExifToolがシステムにインストールされている必要があります。ExifToolは公式サイトからダウンロードしてインストールしてください。
このライブラリはMITライセンスの下で配布されています。ただし、ExifTool自体はArtistic License 2.0の下で配布されています。ExifToolを利用する場合は、そのライセンス条件を遵守してください。
(2025年確認 / PyPI 記載情報)
| ライブラリ | ライセンス |
|---|---|
| charset_normalizer | MIT |
| tqdm | MIT |