多くのお客様は、検索を容易にするために、AIを利用して画像の説明をサポートしています。AIは、画像に含まれる物体、人物、テキストを識別するのに非常に優れています。しかし、お客様から、以下のようないわゆる技術的メタデータに基づいて検索やフィルタを行いたいという要望が寄せられることもあります。
- EXIF (Exchangeable Image File Format)
- ICC (International Color Consortium) プロファイル
- JFIF (JPEG File Interchange Format)
- MPF (Multi-Picture Format)
ソースやその作成方法によっては、画像にこのメタデータの一部またはすべてが埋め込まれます。この情報はデジタル資産管理 (DAM) のユースケースにおいて重要です。他のファイル形式には、他の種類の技術的メタデータが埋め込まれることになります。
現在、Boxではこの重要な情報すべてを抽出できます。
今回の技術的な記事では、Boxがどのようにこの情報を抽出するか、また、Box APIとメタデータを使用して、その情報をBox Appsダッシュボードで検索および利用できるようにする方法について説明します。
重要なポイント:
- プレビュー可能なすべてのBox形式で、埋め込みメタデータが利用可能になりました。
- 埋め込みメタデータはファイルのレプリゼンテーションとして提供されます。これは、サムネイル、PDF、テキスト、マークダウン、技術データなど、別の形式でファイル内のデータを表現したものです。
- 埋め込みメタデータを使用するには、メタデータテンプレート、スクリプト、Boxアプリが必要です。
埋め込みメタデータとは
埋め込みメタデータとは、Boxがファイルに対して生成できる新しいレプリゼンテーションです。これは、すでにプレビュー可能であればどの形式でも利用可能です。コンテンツの種類に応じて、異なるメタデータが抽出されます。以下の例を参考にしてください。

すべての画像にこれほど多くの属性があるわけではありません。ツールによってはこのメタデータの一部が削除される場合があり、すべてのソフトウェアがすべての属性を追加するわけでもありません。
{
"BoxNormalized": 1 attribute,
"File": 9 attributes,
"JFIF": 4 attributes,
"EXIF": 49 attributes,
"MakerNotes": 19 attributes,
"MPF": 9 attributes,
"ICC_Profile": 17 attributes,
"JPEG": 1 attribute
}
Sample quicktime video file
{
"BoxNormalized": 1 attribute,
"File": 3 attributes,
"QuickTime": 52 attributes
}埋め込みメタデータの取得
前述のとおり、埋め込みメタデータは、ファイルのレプリゼンテーションとして提供されます。Boxにおけるレプリゼンテーションとは、サムネイル、PDF、テキスト、マークダウンなどの異なる形式 (今回のケースでは技術的メタデータ) でファイル内のデータを表現したものです。
DOCX、PDF、抽出されたテキスト、大半のサムネイルなど、ファイルのアップロード時に自動的に生成されるレプリゼンテーションもあります。これらのレプリゼンテーションは、プレビュー、サムネイル、ファイルコンテンツ検索などに使用されます。この記事の焦点であるembedded_metadataは、オンデマンドのレプリゼンテーションです。ここでは、これが何を意味するのか説明します。
まず、ダッシュボードにEXIFメタデータとともに表示したい画像をいくつかアップロードしました。
画像のレプリゼンテーションを取得するには、/filesエンドポイントを使用して、representationsフィールドをリクエストする必要があります。以下に、利用可能なレプリゼンテーションを取得して一覧表示するPythonの例を示します。
def list_representations(client: BoxClient, file_id: str) -> None:
"""Fetch and print all available representations for a file."""
file = client.files.get_file_by_id(
file_id,
fields=["representations"],
)
entries = (
file.representations.entries
)
print("Representations for file", file_id)
print("-" * 50)
for rep in entries:
dims = rep.properties.dimensions if rep.properties and rep.properties.dimensions else ""
dims_str = f" | dimensions: {dims}" if dims else ""
print(f" type: {rep.representation}{dims_str}")
Representations for file 2163735367009
--------------------------------------------------
type: jpg | dimensions: 32x32
type: jpg | dimensions: 94x94
type: jpg | dimensions: 160x160
type: jpg | dimensions: 320x320
type: jpg | dimensions: 1024x1024
type: png | dimensions: 1024x1024
type: png | dimensions: 2048x2048
type: jpg | dimensions: 2048x2048
type: 3d
type: embedded_metadata特定のレプリゼンテーションを取得するには、リクエストにx-rep-hintを追加する必要があります。ここで [embedded_metadata] を追加します。必要に応じて1回のコールで複数のレプリゼンテーションをリクエストできるように、ヘッダーは配列を受け取ります。
def fetch_representation(
client: BoxClient,
file_id: str,
rep_hint: str,
token: str,
asset_path: str = "",
max_wait_seconds: int = 30,
poll_interval: int = 3,
) -> bytes | None:
file = client.files.get_file_by_id(
file_id,
fields=["representations"],
x_rep_hints=rep_hint,
)
entries = (
file.representations.entries
)
rep = entries[0]
state = rep.status.state
state_str = state.value if state else "unknown"
url_template = rep.content.url_template
print(f"state: {state_str} | url_template: {url_template}")
state: none | url_template: https://dl.boxcloud.com/api/2.0/internal_files/2163735367009/versions/2392182789409/representations/embedded_metadata/content/{+asset_path}
状態がnoneの場合、オンデマンドのレプリゼンテーションであるため、レプリゼンテーションがまだ生成されていないことを意味します。これは、初めてダウンロードしようとしたときに、Boxによって作成されます。つまり、ダウンロードのリクエストによって202 Acceptedが返され、ステータスはpendingに変更されます。最初のコール後、実際の埋め込みメタデータのJSONを取得するまで少し待つ必要があります。通常、レプリゼンテーションの生成には1〜2秒かかります。
if state == FileFullRepresentationsEntriesStatusStateField.NONE:
print("State is 'none' - triggering generation via info URL...")
if rep.info and rep.info.url:
requests.get(rep.info.url, headers={"Authorization": f"Bearer {token}"})
state = FileFullRepresentationsEntriesStatusStateField.PENDING
# Poll until ready or timed out
elapsed = 0
while state == FileFullRepresentationsEntriesStatusStateField.PENDING:
if elapsed >= max_wait_seconds:
print(f"Timed out after {max_wait_seconds}s waiting for representation.")
return None
print(f" State is 'pending' - waiting {poll_interval}s... (elapsed: {elapsed}s)")
time.sleep(poll_interval)
elapsed += poll_interval
file = client.files.get_file_by_id(
file_id,
fields=["representations"],
x_rep_hints=rep_hint,
)
entries = (
file.representations.entries
)
rep = entries[0]
state = rep.status.state if rep.status else None
ready_states = {
FileFullRepresentationsEntriesStatusStateField.SUCCESS,
FileFullRepresentationsEntriesStatusStateField.VIEWABLE,
}
if state not in ready_states:
print(f"Representation ended in unexpected state: {state}")
return None
url_template = rep.content.url_template
download_url = url_template.replace("{+asset_path}", asset_path)
response = requests.get(
download_url,
headers={"Authorization": f"Bearer {token}"},
allow_redirects=True,
)
response.raise_for_status()
return response.content
[
{
"BoxNormalized": {
"PageCount": null
},
"File": {
"FileType": "JPEG",
"FileTypeExtension": "jpg",
"MIMEType": "image/jpeg",
…truncated 5 fields
},
"JFIF": {
"JFIFVersion": 1.01,
"ResolutionUnit": "inches",
"XResolution": 300,
"YResolution": 300
},
"EXIF": {
"Make": "Apple",
"Model": "iPhone 14",
"Orientation": "Horizontal (normal)",
…truncated 48 fields
},
"MakerNotes": {
"MakerNoteVersion": 15,
"RunTimeFlags": "Valid",
"RunTimeValue": 6241807022333,
…truncated 15 fields
},
"MPF": {
"MPFVersion": "0100",
"NumberOfImages": 2,
"MPImageFlags": "(none)",
…truncated 6 fields
},
"ICC_Profile": {
"ProfileCMMType": "Apple Computer Inc.",
"ProfileVersion": "4.0.0",
…truncated 19 fields
},
"JPEG": {
"HDRGainCurve": "(Binary data 1392 bytes, use -b option to extract)"
}
}
]
結果として、画像メタデータから114のデータポイントを取得できました。なお、すべての画像にすべてのデータポイント、さらにすべてのカテゴリのメタデータが含まれるわけではありません。また、実際のデータは形式や種類によって異なります。
埋め込みメタデータの使用
次の疑問は、当然のことながら、Boxアプリではそのデータをどのように使用できるのかという点です。
そのためには、以下の3点が必要になります。
- アプリに必要なフィールドを含むメタデータテンプレート
- 埋め込みメタデータのレプリゼンテーションから値を抽出し、それをメタデータに適用するスクリプト
- 画像とメタデータを表示できるダッシュボード (またはBox Content Explorer UI Elementで利用可能なメタデータビューを使用したカスタムページ) を備えたBoxアプリ
テンプレートはBox内で直接作成できます。今回のテンプレートには9個のデータポイントを追加しました。

次は、埋め込みメタデータからこれらの各データポイントを読み取り、画像にメタデータとして適用するスクリプトです。埋め込みメタデータのレプリゼンテーションを取得し、上記のメタデータテンプレートと、そのレプリゼンテーション内のJSONパスの間のマッピングを追加しました。
"DateTimeOriginal": ("[0].EXIF.DateTimeOriginal", "date"),
"Make": ("[0].EXIF.Make", "text"),
"ImageWidth": ("[0].File.ImageWidth", "number"),
"ImageHeight": ("[0].File.ImageHeight", "number"),
"ISO": ("[0].EXIF.ISO", "number"),
"ProfileDescription": ("[0].ICC_Profile.ProfileDescription", "text"),
"Rights": ("[0].XMP.Rights", "text"),
"Creator": ("[0].XMP.Creator", "text"),
}
embedded_metadata = json.loads(embedded_metadata_bytes.decode("utf-8"))
fields = {}
for template_field, (path, field_type) in EMBEDDED_METADATA_FIELD_MAP.items():
value = _resolve_path(embedded_metadata, path)
if value is not None:
fields[template_field] = _coerce(value, field_type)
print(f"Extracted {len(fields)} of {len(EMBEDDED_METADATA_FIELD_MAP)} fields:")
for k, v in fields.items():
print(f" {k}: {v!r} ({type(v).__name__})")
sdk_scope = (
CreateFileMetadataByIdScope.ENTERPRISE
)
try:
client.file_metadata.create_file_metadata_by_id(
file_id=file_id,
scope=sdk_scope,
template_key=template_key,
request_body=fields,
)
print(f"Metadata created: {template_key} on file {file_id}")
except Exception as e:
print(f"Metadata write failed: {e}")
フォルダ内のすべてのファイルに対してこの処理を行います。
次に、作成したテンプレートを使用してBox Appsのダッシュボードを追加できます。これで、抽出された技術的メタデータに基づいて画像を表示したり、画像にフィルタをかけたりできるようになります。

また、明らかな改善点として、画像の要約を生成し、それをメタデータの一部として追加することで、技術的メタデータと実際の画像コンテンツ (物体、場所、人物、テキストなど) の両方を基に簡単に検索できるようにすることも挙げられます。以下のような検索が画像全体に対して機能します。
上記の生成に使用されたPythonスクリプトの完全版は、こちらのリンクで確認できます。
※このブログは Box, Inc 公式ブログ(https://blog.box.com/)2026年5月13日(日本時間5月14日)付投稿の翻訳です。
著者:Peter Christensen, Senior Staff Platform Solutions Engineer at Box
原文リンク:https://blog.box.com/lets-talk-about-image-metadata-and-dams
- トピックス:
- 開発者